diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..0d945dd697 --- /dev/null +++ b/.clang-format @@ -0,0 +1,61 @@ +# using [clang-formt-12 options](https://releases.llvm.org/12.0.0/tools/clang/docs/ClangFormatStyleOptions.html) +RawStringFormats: + - Language: Cpp + Delimiters: + - c + - C + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + - h + - hpp + CanonicalDelimiter: '' + BasedOnStyle: Mozilla + +Language: Cpp +BasedOnStyle: Mozilla +# 6.1 +IndentWidth: 4 +ContinuationIndentWidth: 4 +# 6.2 +TabWidth: 4 +UseTab: Never +# 6.3 +ColumnLimit: 80 +# 6.9 +AlignAfterOpenBracket: Align +BinPackArguments: true +BinPackParameters: true +# 6.10 +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: false + SplitEmptyNamespace: true +# 6.27 +BreakBeforeBinaryOperators: NonAssignment + +# additional +AlignEscapedNewlines: Left +AllowAllParametersOfDeclarationOnNextLine: false +AllowAllArgumentsOnNextLine: false +PointerAlignment: Right +SpaceAroundPointerQualifiers: After +SortIncludes: false diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000000..b5f3da69d5 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,49 @@ +# refer to https://clang.llvm.org/extra/clang-tidy/checks/list.html +# +# Configure clang-tidy for this project. + +# Here is an explanation for why some of the checks are disabled: +# + +Checks: > + -*, + bugprone-*, + cert-*, + clang-analyzer-*, + concurrency-*, + misc-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -bugprone-easily-swappable-parameters, + -bugprone-macro-parentheses, + -misc-no-recursion, + -misc-unused-parameters, + -readability-braces-around-statements, + -readability-else-after-return, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-named-parameter, + -readability-non-const-parameter, + -readability-redundant-preprocessor, + -readability-suspicious-call-argument, + -readability-uppercase-literal-suffix + + +# Turn all the warnings from the checks above into errors. +WarningsAsErrors: "*" + +# headers in the following directories will be checked: +# - core/iwasm/ +# - core/shared/ +HeaderFilterRegex: '(core/iwasm/|core/shared/).*\\.h$' + +# goto .clang-format at root directory to see the format style +FormatStyle: file + +CheckOptions: + - { key: readability-identifier-naming.NamespaceCase, value: lower_case } + - { key: readability-function-cognitive-complexity.Threshold, value: 100 } diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..b81561a56f --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,42 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# hadolint global ignore=DL3008,DL3009 + +ARG VARIANT=debian-12 +FROM mcr.microsoft.com/devcontainers/cpp:${VARIANT} + +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Asia/Shanghai + +RUN apt-get update \ + && apt-get upgrade -y + +RUN apt-get update \ + && apt-get install -y apt-transport-https apt-utils build-essential \ + ca-certificates ccache clang-format-14 curl file g++-multilib git gnupg \ + libgcc-12-dev lib32gcc-12-dev libzstd-dev lsb-release \ + ninja-build ocaml ocamlbuild opam \ + python3-venv python3-pip \ + software-properties-common tree tzdata \ + unzip valgrind vim wget zip --no-install-recommends + +WORKDIR /opt + +ARG WASI_SDK_VER=25 +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-x86_64-linux.tar.gz -P /tmp \ + && tar xf /tmp/wasi-sdk-${WASI_SDK_VER}.0-x86_64-linux.tar.gz -C /opt \ + && ln -sf /opt/wasi-sdk-${WASI_SDK_VER}.0-x86_64-linux /opt/wasi-sdk + +ARG WABT_VER=1.0.37 +RUN wget -c --progress=dot:giga https://github.com/WebAssembly/wabt/releases/download/${WABT_VER}/wabt-${WABT_VER}-ubuntu-20.04.tar.gz -P /tmp \ + && tar xf /tmp/wabt-${WABT_VER}-ubuntu-20.04.tar.gz -C /opt \ + && ln -sf /opt/wabt-${WABT_VER} /opt/wabt + +# set path + +# clean up +RUN apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /tmp/* diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..b36c063091 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +{ + "name": "WAMR-Dev", + "build": { + "dockerfile": "Dockerfile", + "args": { + "VARIANT": "debian-12", + "WASI_SDK_VER": "25", + "WABT_VER": "1.0.37" + } + }, + "runArgs": [ + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined" + ], + "customizations": { + "vscode": { + "settings": {}, + "extensions": [ + "dtsvet.vscode-wasm", + "ms-python.python", + "ms-python.vscode-pylance", + "ms-vscode.cmake-tools" + ] + } + }, + "postCreateCommand": "bash .devcontainer/finalize.sh", + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.devcontainer/finalize.sh b/.devcontainer/finalize.sh new file mode 100644 index 0000000000..a7d877b82e --- /dev/null +++ b/.devcontainer/finalize.sh @@ -0,0 +1,18 @@ +echo "Running finalize script..." + +# +# Python Package Installation +# +echo "--- Installing Python Dependencies\n" + +# Upgrade pip first +python3 -m pip install --no-cache-dir --break-system-packages --upgrade pip +# Install required packages +pip3 install --no-cache-dir --break-system-packages -r .devcontainer/requirements.txt + +echo "--- Installing Ocaml stuff\n" +opam init --yes --shell-setup +eval $(opam env --switch=default) +opam install --yes dune menhir + +echo "Finalize script completed. ✅" diff --git a/.devcontainer/requirements.txt b/.devcontainer/requirements.txt new file mode 100644 index 0000000000..a8ed4b65d6 --- /dev/null +++ b/.devcontainer/requirements.txt @@ -0,0 +1,5 @@ +black +nose +pycparser +pylint +requests diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..f322b3c1e1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +# for now, it is used to speed up wasi-nn tests only. +# you shall adapt below rules to incoming requirements + +**/build +**/tmp.* +.* +**/*.gguf + +/core/deps/ +!/core/deps/tensorflow-src +/samples +/tests diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..9fa3fad944 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,41 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "Add a placeholder for issue title. ex: [BUG]" +labels: bug +assignees: "" +--- + +**Is it a security vulnerability?** +If it results in a crash or hang, please refer to [a quick checklist](../../doc/security_need_to_know.md#is-this-bug-considered-a-security-vulnerability) to determine if it is a security vulnerability. If you are still unsure, please report it through [a security advisor](https://github.com/bytecodealliance/wasm-micro-runtime/security/advisories) and allow the maintainer to make a decision. Thank you. + +**Describe the bug** +A clear and concise description of what the bug is. + +**Version** +Information like tags, release version, commits. + +**To Reproduce** +Steps to reproduce the behavior: + +1. Compile iwasm with flags like '...' +2. (Optional) Compile wamrc with flags like '....' +3. (Optional) Run wamrc with CLI options like '...' to generate .aot +4. Run iwasm with CLI options like '...' +5. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Actual Result** +What you've got. + +**Desktop (please complete the following information):** + +- Arch [e.g. x86_64, arm64, 32bit] +- Board [e.g. STM32F407] +- OS [e.g. Linux, Windows, macOS, FreeRTOS] +- Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..0086358db1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..f20f45edee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: 'Add a placeholder for issue title. ex: [RFC]' +labels: help wanted +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/actions/install-linux-sgx/action.yml b/.github/actions/install-linux-sgx/action.yml new file mode 100644 index 0000000000..17757573d6 --- /dev/null +++ b/.github/actions/install-linux-sgx/action.yml @@ -0,0 +1,47 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Always follow https://download.01.org/intel-sgx/latest/linux-latest/docs/ + +name: "Install Intel SGX SDK" +description: "Installs the Intel SGX SDK and necessary libraries for Ubuntu." +author: "Intel Corporation" +inputs: + os: + description: "Operating system to install on (ubuntu-22.04)" + required: true + +runs: + using: "composite" + steps: + - name: Check Runner OS + if: ${{ inputs.os != 'ubuntu-22.04' }} + shell: bash + run: | + echo "::error title=⛔ error hint::Only support ubuntu-22.04 for now" + exit 1 + + - name: Create installation directory + shell: bash + run: sudo mkdir -p /opt/intel + + - name: Download and install SGX SDK on ubuntu-22.04 + if: ${{ inputs.os == 'ubuntu-22.04' }} + shell: bash + run: | + sudo wget -O sgx_linux_x64_sdk.bin https://download.01.org/intel-sgx/sgx-linux/2.25/distro/ubuntu22.04-server/sgx_linux_x64_sdk_2.25.100.3.bin + sudo chmod +x sgx_linux_x64_sdk.bin + echo 'yes' | sudo ./sgx_linux_x64_sdk.bin + working-directory: /opt/intel + + - name: Add SGX repository and install libraries + shell: bash + run: | + echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/intel-sgx.list + wget -qO - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add - + sudo apt update + sudo apt install -y libsgx-launch libsgx-urts + + - name: Source SGX SDK environment + shell: bash + run: source /opt/intel/sgxsdk/environment diff --git a/.github/actions/install-wasi-sdk-wabt/action.yml b/.github/actions/install-wasi-sdk-wabt/action.yml new file mode 100644 index 0000000000..31d22c18d5 --- /dev/null +++ b/.github/actions/install-wasi-sdk-wabt/action.yml @@ -0,0 +1,125 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Get URLs from: +# - https://github.com/WebAssembly/wasi-sdk/releases +# - https://github.com/WebAssembly/wabt/releases + +# Install WASI-SDK and WABT at /opt +# /opt is the assumed location widely used in the project +name: Install WASI-SDK and WABT + +description: A composite action to download and install wasi-sdk and wabt on Ubuntu, macOS. + +inputs: + os: + description: "Operating system to install on (ubuntu, macos)" + required: true + +runs: + using: "composite" + steps: + - name: Check Runner OS + if: ${{ !startsWith(inputs.os, 'ubuntu') && !startsWith(inputs.os, 'windows') && !startsWith(inputs.os, 'macos') }} + shell: bash + run: | + echo "::error title=⛔ error hint::Support Ubuntu, Windows, and macOS Only" + exit 1 + + - name: Set up wasi-sdk and wabt on Ubuntu + if: ${{ startsWith(inputs.os, 'ubuntu') }} + shell: bash + run: | + echo "Downloading wasi-sdk for Ubuntu..." + sudo wget -O wasi-sdk.tar.gz --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz + + echo "Extracting wasi-sdk..." + sudo tar -xf wasi-sdk.tar.gz + sudo ln -sf wasi-sdk-25.0-x86_64-linux/ wasi-sdk + + echo "Downloading wabt for Ubuntu..." + sudo wget -O wabt.tar.gz --progress=dot:giga https://github.com/WebAssembly/wabt/releases/download/1.0.37/wabt-1.0.37-ubuntu-20.04.tar.gz + + echo "Extracting wabt..." + sudo tar -xf wabt.tar.gz + sudo ln -sf wabt-1.0.37 wabt + + /opt/wasi-sdk/bin/clang --version + /opt/wabt/bin/wasm-interp --version + + echo "::notice::wasi-sdk-25 and wabt-1.0.37 installed on ubuntu" + working-directory: /opt + + - name: Set up wasi-sdk and wabt on macOS on Intel + if: ${{ inputs.os == 'macos-15-intel' }} + shell: bash + run: | + echo "Downloading wasi-sdk for macOS on Intel..." + sudo wget -O wasi-sdk.tar.gz --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-macos.tar.gz + + echo "Extracting wasi-sdk..." + sudo tar -xf wasi-sdk.tar.gz + sudo ln -sf wasi-sdk-25.0-x86_64-macos wasi-sdk + + echo "Downloading wabt for macOS on Intel..." + sudo wget -O wabt.tar.gz --progress=dot:giga https://github.com/WebAssembly/wabt/releases/download/1.0.36/wabt-1.0.36-macos-12.tar.gz + + echo "Extracting wabt..." + sudo tar -xf wabt.tar.gz + sudo ln -sf wabt-1.0.36 wabt + + /opt/wasi-sdk/bin/clang --version + /opt/wabt/bin/wasm-interp --version + + echo "::notice::wasi-sdk-25 and wabt-1.0.36 installed on ${{ inputs.os }}" + working-directory: /opt + + - name: Set up wasi-sdk and wabt on macOS on ARM + if: ${{ inputs.os == 'macos-15' }} + shell: bash + run: | + echo "Downloading wasi-sdk for macOS on ARM..." + sudo wget -O wasi-sdk.tar.gz --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-arm64-macos.tar.gz + + echo "Extracting wasi-sdk..." + sudo tar -xf wasi-sdk.tar.gz + sudo ln -sf wasi-sdk-25.0-arm64-macos wasi-sdk + + echo "Downloading wabt for macOS on ARM..." + sudo wget -O wabt.tar.gz --progress=dot:giga https://github.com/WebAssembly/wabt/releases/download/1.0.37/wabt-1.0.37-macos-14.tar.gz + + echo "Extracting wabt..." + sudo tar -xf wabt.tar.gz + sudo ln -sf wabt-1.0.37 wabt + + /opt/wasi-sdk/bin/clang --version + /opt/wabt/bin/wasm-interp --version + + echo "::notice::wasi-sdk-25 and wabt-1.0.37 installed on ${{ inputs.os }}" + working-directory: /opt + + - name: Set up wasi-sdk and wabt on Windows + if: ${{ startsWith(inputs.os, 'windows') }} + shell: bash + run: | + choco install -y wget + + mkdir -p /opt/wasi-sdk + mkdir -p /opt/wabt + + echo "Downloading wasi-sdk for Windows..." + wget -O wasi-sdk.tar.gz --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-windows.tar.gz + + echo "Extracting wasi-sdk..." + tar --strip-components=1 -xf wasi-sdk.tar.gz -C /opt/wasi-sdk + + echo "Downloading wabt for Windows..." + wget -O wabt.tar.gz --progress=dot:giga https://github.com/WebAssembly/wabt/releases/download/1.0.37/wabt-1.0.37-windows.tar.gz + + echo "Extracting wabt..." + tar --strip-components=1 -xf wabt.tar.gz -C /opt/wabt + + /opt/wasi-sdk/bin/clang --version + /opt/wabt/bin/wasm-interp --version + + echo "::notice::wasi-sdk-25 and wabt-1.0.37 installed on Windows" diff --git a/.github/codeql/codeql_config.yml b/.github/codeql/codeql_config.yml new file mode 100644 index 0000000000..3da712c645 --- /dev/null +++ b/.github/codeql/codeql_config.yml @@ -0,0 +1,46 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +paths: + - .github + - core/iwasm + - core/shared/platform/common/ + - core/shared/platform/include/ + - core/shared/platform/linux/ + - product-mini/platforms/common/ + - product-mini/platforms/linux/ + # TODO: add other platforms back if able to do cross-compilation + # - product-mini/platforms/ + # TODO: add samples back after buildscript modification + # - need to ignore workloads and wasm-apps + # - samples + - wamr-compiler/ +paths-ignore: + # always ignore build + - '**/build/**' + - '**/test*/**' + - '**/wasm-app*/**' + - core/deps/ + # platform specific + - core/iwasm/aot/arch/aot_reloc_aarch64.c + - core/iwasm/aot/arch/aot_reloc_arc.c + - core/iwasm/aot/arch/aot_reloc_arm.c + - core/iwasm/aot/arch/aot_reloc_dummy.c + - core/iwasm/aot/arch/aot_reloc_mips.c + - core/iwasm/aot/arch/aot_reloc_riscv.c + - core/iwasm/aot/arch/aot_reloc_thumb.c + - core/iwasm/aot/arch/aot_reloc_xtensa.c + - core/iwasm/libraries/lib-rats/ + - core/iwasm/libraries/lib-socket/ + - core/iwasm/libraries/lib-wasi-threads/*-test/ + - core/shared/platform/common/freertos/ + - core/shared/platform/common/math/ + #TODO: add me back if lldb libraries installed + - core/iwasm/compilation/debug/ + # spend disk space and slow + - core/iwasm/libraries/wasi-nn/src/wasi_nn_tflite* + #TODO: add me back if openvino installed + - core/iwasm/libraries/wasi-nn/src/wasi_nn_openvino* + # for wasm + - core/iwasm/libraries/wasi-nn/include/wasi_nn.h + # reference + - core/iwasm/common/arch/invokeNative_general.c diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..0676cf7417 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +version: 2 +updates: + +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + +- package-ecosystem: "docker" + directory: "/.devcontainer" + schedule: + interval: "weekly" + +- package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: "weekly" + +- package-ecosystem: "pip" + directory: "/build-scripts" + schedule: + interval: "weekly" + +- package-ecosystem: "pip" + directory: "/language-bindings/python/wasm-c-api" + schedule: + interval: "weekly" + +- package-ecosystem: "pip" + directory: "/language-bindings/python/wamr-api" + schedule: + interval: "weekly" diff --git a/.github/scripts/codeql_buildscript.sh b/.github/scripts/codeql_buildscript.sh new file mode 100755 index 0000000000..99060af8f4 --- /dev/null +++ b/.github/scripts/codeql_buildscript.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +# This script is used to build the WAMR project for CodeQL analysis. + +# Pre-requisites +sudo apt -qq update +sudo apt install -y -qq build-essential cmake g++-multilib libgcc-12-dev lib32gcc-12-dev ccache ninja-build + +LLVM_VER=18.1.8 +pushd /opt +sudo wget --progress=dot:giga -O clang+llvm-x86_64-linux-gnu.tar.xz https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VER}/clang+llvm-${LLVM_VER}-x86_64-linux-gnu-ubuntu-18.04.tar.xz \ + && tar -xf clang+llvm-x86_64-linux-gnu.tar.xz \ + && mv clang+llvm-${LLVM_VER}-x86_64-linux-gnu-ubuntu-18.04 llvm-${LLVM_VER} +popd + +# libtinfo.so.5 for /opt/llvm-18.1.8/lib/libomptarget.rtl.amdgpu.so.18.1 +sudo apt -qq update +wget http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.1_amd64.deb +sudo apt install -y -qq ./libtinfo5_6.3-2ubuntu0.1_amd64.deb + +# Start the build process +WAMR_DIR=${PWD} +LLVM_DIR=/opt/llvm-${LLVM_VER}/lib/cmake/llvm + +# Function to build wamrc +build_wamrc() { + local options="$1" + echo "Building wamrc with options: $options" + + pushd ${WAMR_DIR}/wamr-compiler + rm -rf build + cmake -S . -B build \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DWAMR_BUILD_WITH_CUSTOM_LLVM=1 -DLLVM_DIR=${LLVM_DIR} \ + $options + cmake --build build --target wamrc --parallel + if [[ $? != 0 ]]; then + echo "Failed to build wamrc with options: $options" + exit 1 + fi + popd +} + +# Function to build iwasm +build_iwasm() { + local options="$1" + echo "Building iwasm with options: $options" + + pushd ${WAMR_DIR}/product-mini/platforms/linux + rm -rf build + cmake -S . -B build \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Debug \ + -DLLVM_DIR=${LLVM_DIR} \ + $options + cmake --build build --target iwasm --parallel + if [[ $? != 0 ]]; then + echo "Failed to build iwasm with options: $options" + exit 1 + fi + popd +} + +# List of compilation options for wamrc +wamrc_options_list=( + #default + "" +) + +# List of compilation options for iwasm +iwasm_options_list=( + #default + "" + # +classic interp + "-DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_SIMD=0" + # fast jit + "-DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_FAST_JIT_DUMP=1 -DWAMR_BUILD_SIMD=0" + # +llvm jit + "-DWAMR_BUILD_JIT=1" + # + "-DWAMR_BUILD_TARGET=X86_32" + # + # libraries + "-DWAMR_BUILD_LIBC_BUILTIN=0 -DWAMR_BUILD_LIBC_UVWASI=1 -DWAMR_BUILD_LIBC_EMCC=1" + "-DWAMR_BUILD_THREAD_MGR=1 -DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_LIB_PTHREAD_SEMAPHORE=1" + "-DWAMR_BUILD_THREAD_MGR=1 -DWAMR_BUILD_LIB_WASI_THREADS=1 -DWAMR_BUILD_LIB_PTHREAD_SEMAPHORE=1" + "-DWAMR_BUILD_WASI_NN=1 -DWAMR_BUILD_WASI_NN_LLAMACPP=1" + # + # Wasm specs + "-DWAMR_BUILD_GC=1 -DWAMR_BUILD_STRINGREF=1 -DWAMR_STRINGREF_IMPL_SOURCE=STUB" + "-DWAMR_BUILD_EXCE_HANDLING=1 -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_SIMD=0" + "-DWAMR_BUILD_MEMORY64=1 -DWAMR_BUILD_MULTI_MEMORY=1 -DWAMR_BUILD_SHARED_MEMORY=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_SIMD=0 -DWAMR_BUILD_AOT=0" + # + # WARM features + "-DWAMR_BUILD_MULTI_MODULE=1 -DWAMR_BUILD_MINI_LOADER=1 -DWAMR_BUILD_SHARED_HEAP=1" + "-DWAMR_DISABLE_HW_BOUND_CHECK=1" + "-DWAMR_CONFIGURABLE_BOUNDS_CHECKS=1" + "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1" + # - Debug + "-DWAMR_BUILD_DEBUG_INTERP=1 -DWAMR_BUILD_DEBUG_AOT=1 -DWAMR_BUILD_DYNAMIC_AOT_DEBUG=1" + # - developer options + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1 -DWAMR_BUILD_LOAD_CUSTOM_SECTION=1 -DWAMR_BUILD_DUMP_CALL_STACK=1 -DWAMR_BUILD_LINUX_PERF=1 -DWAMR_BUILD_AOT_VALIDATOR=1 -DWAMR_BUILD_MEMORY_PROFILING=1 -DWAMR_BUILD_PERF_PROFILING=1" + # - global heap + "-DWAMR_BUILD_ALLOC_WITH_USER_DATA=1 -DWAMR_BUILD_GLOBAL_HEAP_POOL=1 -DWAMR_BUILD_GLOBAL_HEAP_SIZE=131072" + "-DWAMR_BUILD_QUICK_AOT_ENTRY=0 -DWAMR_DISABLE_WAKEUP_BLOCKING_OP=1 -DWAMR_BUILD_MODULE_INST_CONTEXT=0" + # - pgo + "-DWAMR_BUILD_STATIC_PGO=1" + # TODO: SGX specifics. +) + +# Loop through all iwasm options and build +for options in "${iwasm_options_list[@]}"; do + build_iwasm "$options" +done + +# Loop through all wamrc options and build +for options in "${wamrc_options_list[@]}"; do + build_wamrc "$options" +done diff --git a/.github/scripts/codeql_fail_on_error.py b/.github/scripts/codeql_fail_on_error.py new file mode 100755 index 0000000000..f150c38a25 --- /dev/null +++ b/.github/scripts/codeql_fail_on_error.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import json +import sys +import os +import requests + + +def fetch_dismissed_alerts(repo_name, github_token): + headers = { + "Authorization": f"token {github_token}", + "Accept": "application/vnd.github.v3+json", + } + url = ( + f"https://api.github.com/repos/{repo_name}/code-scanning/alerts?state=dismissed" + ) + response = requests.get(url, headers=headers) + return response.json() # This assumes a successful API call + + +def parse_location(location): + path = location.get("physicalLocation", {}).get("artifactLocation", {}).get("uri") + start_line = location.get("physicalLocation", {}).get("region", {}).get("startLine") + column_range = ( + location.get("physicalLocation", {}).get("region", {}).get("startColumn"), + location.get("physicalLocation", {}).get("region", {}).get("endColumn"), + ) + return (path, start_line, column_range) + + +def is_dismissed(rule_id, path, start_line, column_range, dismissed_alerts): + for alert in dismissed_alerts: + alert_rule_id = alert.get("rule", {}).get("id") + alert_path = alert.get("location", {}).get("path") + alert_start_line = alert.get("location", {}).get("start_line") + alert_column_range = ( + alert.get("location", {}).get("start_column"), + alert.get("location", {}).get("end_column"), + ) + + if ( + rule_id == alert_rule_id + and path == alert_path + and start_line == alert_start_line + and column_range == alert_column_range + ): + return True + return False + + +# Return whether SARIF file contains error-level results +def codeql_sarif_contain_error(filename, dismissed_alerts): + has_error = False + + with open(filename, "r") as f: + s = json.load(f) + + for run in s.get("runs", []): + rules_metadata = run["tool"]["driver"]["rules"] + if not rules_metadata: + rules_metadata = run["tool"]["extensions"][0]["rules"] + + for res in run.get("results", []): + if "ruleIndex" in res: + rule_index = res["ruleIndex"] + elif "rule" in res and "index" in res["rule"]: + rule_index = res["rule"]["index"] + else: + continue + + # check whether it's dismissed before + rule_id = res["ruleId"] + path, start_line, column_range = parse_location(res["locations"][0]) + # the source code is from dependencies + if "_deps" in path: + continue + if is_dismissed(rule_id, path, start_line, column_range, dismissed_alerts): + print( + f"====== Finding a dismissed entry: {rule_id} at {path}:{start_line} is dismissed.======" + ) + print(res) + continue + + try: + rule_level = rules_metadata[rule_index]["defaultConfiguration"]["level"] + except IndexError as e: + print(e, rule_index, len(rules_metadata)) + else: + if rule_level == "error": + # very likely to be an actual error + if rules_metadata[rule_index]["properties"].get("precision") in [ + "high", + "very-high", + ]: + # the security severity is above medium(Common Vulnerability Scoring System (CVSS) >= 4.0) + if "security-severity" in rules_metadata[rule_index][ + "properties" + ] and ( + float( + rules_metadata[rule_index]["properties"][ + "security-severity" + ] + ) + > 4.0 + ): + print("====== Finding a likely error. ======") + print(res) + has_error = True + + return has_error + + +if __name__ == "__main__": + GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") + GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY") + dismissed_alerts = fetch_dismissed_alerts(GITHUB_REPOSITORY, GITHUB_TOKEN) + + if codeql_sarif_contain_error(sys.argv[1], dismissed_alerts): + sys.exit(1) diff --git a/.github/scripts/extract_from_release_notes.py b/.github/scripts/extract_from_release_notes.py new file mode 100644 index 0000000000..3802d92113 --- /dev/null +++ b/.github/scripts/extract_from_release_notes.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +""" +Extract the latest release notes content from RELEASE_NOTES.md +""" + +import argparse +import os +import sys +import traceback + + +def latest_content(release_notes_path): + """ + can't change the format of the original content + """ + content = "" + start_extract = False + with open(release_notes_path, encoding="utf-8") as f: + for line in f: + if line.startswith("## "): + if start_extract: + break + + start_extract = True + continue + + # hit a separated line + if line.startswith("---"): + break + + content += line + + content += os.linesep + return content + + +def main(): + """ + GO!GO!!GO!!! + """ + parser = argparse.ArgumentParser(description="run the sample and examine outputs") + parser.add_argument("release_notes_path", type=str) + args = parser.parse_args() + + ret = 1 + try: + print(latest_content(args.release_notes_path)) + ret = 0 + except AssertionError: + traceback.print_exc() + return ret + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/scripts/fetch_and_compare_version.py b/.github/scripts/fetch_and_compare_version.py new file mode 100644 index 0000000000..ad9e53a0a5 --- /dev/null +++ b/.github/scripts/fetch_and_compare_version.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import re +import shlex +import subprocess +import sys + + +def fetch_version_from_code(): + """ + search the semantic version definition in core/version.h + """ + major, minor, patch = "", "", "" + with open("core/version.h", encoding="utf-8") as f: + for line in f: + if "WAMR_VERSION" not in line: + continue + + major_match = re.search(r"WAMR_VERSION_MAJOR (\d+)", line) + if major_match is not None: + major = major_match.groups()[0] + continue + + minor_match = re.search(r"WAMR_VERSION_MINOR (\d+)", line) + if minor_match is not None: + minor = minor_match.groups()[0] + continue + + patch_match = re.search(r"WAMR_VERSION_PATCH (\d+)", line) + if patch_match is not None: + patch = patch_match.groups()[0] + + if len(major) == 0 or len(minor) == 0 or len(patch) == 0: + raise Exception( + "can't find the semantic version definition likes WAMR_VERSION_*" + ) + return f"WAMR-{major}.{minor}.{patch}" + + +def fetch_latest_git_tag(): + """ + Get the most recent tag from the HEAD, + if it's main branch, it should be the latest release tag. + if it's release/x.x.x branch, it should be the latest release tag of the branch. + """ + list_tag_cmd = "git describe --tags --abbrev=0 HEAD" + p = subprocess.run(shlex.split(list_tag_cmd), capture_output=True, check=True) + + all_tags = p.stdout.decode().strip() + latest_tag = all_tags.split("\n")[-1] + return latest_tag + + +def match_version_pattern(v): + pattern = r"WAMR-\d+\.\d+\.\d+" + m = re.match(pattern, v) + return m is not None + + +def split_version_string(v): + """ + return the semantic version as an integer list + """ + pattern = r"WAMR-(\d+)\.(\d+)\.(\d+)" + m = re.match(pattern, v) + return [int(x) for x in m.groups()] + + +def compare_version_string(v1, v2): + """ + return value: + - 1. if v1 > v2 + - -1. if v1 < v2 + - 0. if v1 == v2 + """ + if not match_version_pattern(v1): + raise Exception(f"{v1} doesn't match the version pattern") + + if not match_version_pattern(v2): + raise Exception(f"{v2} doesn't match the version pattern") + + v1_sem_ver = split_version_string(v1) + v2_sem_ver = split_version_string(v2) + + return 0 if v1_sem_ver == v2_sem_ver else (1 if v1_sem_ver > v2_sem_ver else -1) + + +def is_major_or_minor_changed(v1, v2): + """ + return true if change either major of v2 or minor of v2 + return false or else + """ + if not match_version_pattern(v1): + raise Exception(f"{v1} doesn't match the version pattern") + + if not match_version_pattern(v2): + raise Exception(f"{v2} doesn't match the version pattern") + + v1_major, v1_minor, _ = split_version_string(v1) + v2_major, v2_minor, _ = split_version_string(v2) + + return v2_major != v1_major or v2_minor != v1_minor + + +def next_version(): + definition = fetch_version_from_code() + tag = fetch_latest_git_tag() + + new_version = "" + minor_changed = False + if compare_version_string(definition, tag) == 1: + new_version = definition.split("-")[-1] + + if is_major_or_minor_changed(tag, definition): + minor_changed = True + + return new_version, "major_minor_change" if minor_changed else "patch_change" + + +if __name__ == "__main__": + print(f"{next_version()[0]},{next_version()[1]}") + sys.exit(0) diff --git a/.github/scripts/install_qemu_xtensa.sh b/.github/scripts/install_qemu_xtensa.sh new file mode 100755 index 0000000000..4a0e0fe5c0 --- /dev/null +++ b/.github/scripts/install_qemu_xtensa.sh @@ -0,0 +1,10 @@ +#! /bin/sh + +set -e + +URL=https://github.com/espressif/qemu/releases/download/esp-develop-9.0.0-20240606/qemu-xtensa-softmmu-esp_develop_9.0.0_20240606-x86_64-linux-gnu.tar.xz + +DIR=$(mktemp -d) +cd ${DIR} +curl -fLsS "${URL}" | xzcat | tar -x +ln -s ${DIR}/qemu/bin/qemu-system-xtensa /usr/local/bin/qemu-system-xtensa diff --git a/.github/scripts/reuse_latest_release_binaries.py b/.github/scripts/reuse_latest_release_binaries.py new file mode 100644 index 0000000000..0fe2d9766c --- /dev/null +++ b/.github/scripts/reuse_latest_release_binaries.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import argparse +import json +import os +import shlex +import subprocess +import sys +from urllib.error import HTTPError, URLError +import urllib.request + + +def get_last_commit(target_path, cwd): + last_commit_cmd = f"git log -n 1 --pretty=format:%H -- {target_path}" + p = subprocess.run( + shlex.split(last_commit_cmd), capture_output=True, check=True, cwd=cwd + ) + return p.stdout.decode().strip() + + +def fetch_git_tags(): + list_tag_cmd = ( + 'git tag --list WAMR-*.*.* --sort=committerdate --format="%(refname:short)"' + ) + p = subprocess.run(shlex.split(list_tag_cmd), capture_output=True, check=True) + + all_tags = p.stdout.decode().strip() + return all_tags.split("\n") + + +def download_binaries(binary_name_stem, cwd): + """ + 1. find the latest release name + 2. form assets download url: + """ + try: + all_tags = fetch_git_tags() + # *release_process.yml* will create a tag and release at first + second_last_tag = all_tags[-2] + latest_tag = all_tags[-1] + + latest_url = "https://api.github.com/repos/bytecodealliance/wasm-micro-runtime/releases/latest" + print(f"::notice::query the latest release with {latest_url}...") + with urllib.request.urlopen(latest_url) as response: + body = response.read() + + release_name = json.loads(body)["name"] + + # WAMR-X.Y.Z -> X.Y.Z + second_last_sem_ver = second_last_tag[5:] + latest_sem_ver = latest_tag[5:] + assert latest_sem_ver in binary_name_stem + name_stem_in_release = binary_name_stem.replace( + latest_sem_ver, second_last_sem_ver + ) + + # download and rename + for file_ext in (".zip", ".tar.gz"): + assets_url = f"https://github.com/bytecodealliance/wasm-micro-runtime/releases/download/{release_name}/{name_stem_in_release}{file_ext}" + local_path = f"{binary_name_stem}{file_ext}" + print(f"::notice::download from {assets_url} and save as {local_path}...") + urllib.request.urlretrieve(assets_url, local_path) + return True + except HTTPError as error: + print(error.status, error.reason) + except URLError as error: + print(error.reason) + except TimeoutError: + print("Request timeout") + + return False + + +def main(): + parser = argparse.ArgumentParser( + description="Reuse binaries of the latest release if no more modification on the_path since last_commit" + ) + parser.add_argument("working_directory", type=str) + parser.add_argument("--binary_name_stem", type=str) + parser.add_argument("--last_commit", type=str) + parser.add_argument("--the_path", type=str) + args = parser.parse_args() + + last_commit = get_last_commit(args.the_path, args.working_directory) + if last_commit == args.last_commit: + return download_binaries(args.binary_name_stem, args.working_directory) + else: + return False + + +if __name__ == "__main__": + # use output to indicate results + # echo "result=${result}" >> "$GITHUB_OUTPUT" + with open(os.environ.get("GITHUB_OUTPUT"), 'a') as output_file: + output_file.write("result=hit\n" if main() else "result=not-hit\n") + + # always return 0 + sys.exit(0) diff --git a/.github/scripts/run_qemu_arc.sh b/.github/scripts/run_qemu_arc.sh new file mode 100755 index 0000000000..ab226521b2 --- /dev/null +++ b/.github/scripts/run_qemu_arc.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# THIS SCRIPT IS USED TO RUN QEMU ARC EMULATOR DIRECTLY ON CI ONLY. +# USUALLY, you SHOULD NOT RUN IT ON YOUR LOCAL MACHINE. +# INSTEAD, YOU SHOULD USE `west build -t run` COMMAND. + +# Two arguments. first is the path to the zephyr-sdk, second is the path to the zephyr elf. +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +ZEPHYR_SDK_PATH=$1 +ZEPHYR_ELF_PATH=$2 + +if [ ! -d "$ZEPHYR_SDK_PATH" ]; then + echo "Error: Zephyr SDK path does not exist: $ZEPHYR_SDK_PATH" + exit 1 +fi +if [ ! -f "$ZEPHYR_ELF_PATH" ]; then + echo "Error: Zephyr ELF file does not exist: $ZEPHYR_ELF_PATH" + exit 1 +fi + +# this command is copied from the content of build.ninja which is generated by west build. +# please do not modify it unless synchronizing with the build.ninja file. +$ZEPHYR_SDK_PATH/sysroots/x86_64-pokysdk-linux/usr/bin/qemu-system-arc \ + -cpu arcem -m 8M \ + -nographic -no-reboot -monitor none \ + -global cpu.firq=false \ + -global cpu.num-irqlevels=15 \ + -global cpu.num-irq=25 \ + -global cpu.ext-irq=20 \ + -global cpu.freq_hz=10000000 \ + -global cpu.timer0=true \ + -global cpu.timer1=true \ + -global cpu.has-mpu=true \ + -global cpu.mpu-numreg=16 \ + -net none \ + -pidfile qemu.pid \ + -chardev stdio,id=con,mux=on \ + -serial chardev:con \ + -mon chardev=con,mode=readline \ + -icount shift=6,align=off,sleep=off \ + -rtc clock=vm \ + -kernel $ZEPHYR_ELF_PATH diff --git a/.github/workflows/build_docker_images.yml b/.github/workflows/build_docker_images.yml new file mode 100644 index 0000000000..9c4371b4e7 --- /dev/null +++ b/.github/workflows/build_docker_images.yml @@ -0,0 +1,94 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: Create and publish Docker images + +on: + workflow_call: + inputs: + upload_url: + description: upload binary assets to the URL of release + type: string + required: true + ver_num: + description: a semantic version number. + type: string + required: true + +permissions: + contents: read + +jobs: + build-and-push-images: + runs-on: ubuntu-22.04 + permissions: + contents: write # for uploading release artifacts + + steps: + - name: Checkout repository + uses: actions/checkout@v6.0.2 + + - name: Build and save Docker image(wasm-debug-server:${{ inputs.ver_num }}) to tar file + run: | + docker build -t wasm-debug-server:${{ inputs.ver_num }} . + docker save -o wasm-debug-server.tar wasm-debug-server:${{ inputs.ver_num }} + working-directory: test-tools/wamr-ide/WASM-Debug-Server/Docker + + - name: compress the tar file + run: | + tar czf wasm-debug-server-${{ inputs.ver_num }}.tar.gz wasm-debug-server.tar + zip wasm-debug-server-${{ inputs.ver_num }}.zip wasm-debug-server.tar + working-directory: test-tools/wamr-ide/WASM-Debug-Server/Docker + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Debug-Server/Docker/wasm-debug-server-${{ inputs.ver_num }}.tar.gz + asset_name: wasm-debug-server-${{ inputs.ver_num }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Debug-Server/Docker/wasm-debug-server-${{ inputs.ver_num }}.zip + asset_name: wasm-debug-server-${{ inputs.ver_num }}.zip + asset_content_type: application/zip + + - name: Build and save Docker image(wasm-toolchain:${{ inputs.ver_num }}) to tar file + run: | + docker build -t wasm-toolchain:${{ inputs.ver_num }} . + docker save -o wasm-toolchain.tar wasm-toolchain:${{ inputs.ver_num }} + working-directory: test-tools/wamr-ide/WASM-Toolchain/Docker + + - name: compress the tar file + run: | + tar czf wasm-toolchain-${{ inputs.ver_num }}.tar.gz wasm-toolchain.tar + zip wasm-toolchain-${{ inputs.ver_num }}.zip wasm-toolchain.tar + working-directory: test-tools/wamr-ide/WASM-Toolchain/Docker + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Toolchain/Docker/wasm-toolchain-${{ inputs.ver_num }}.tar.gz + asset_name: wasm-toolchain-${{ inputs.ver_num }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/WASM-Toolchain/Docker/wasm-toolchain-${{ inputs.ver_num }}.zip + asset_name: wasm-toolchain-${{ inputs.ver_num }}.zip + asset_content_type: application/zip + diff --git a/.github/workflows/build_iwasm_release.yml b/.github/workflows/build_iwasm_release.yml new file mode 100644 index 0000000000..ab84c4f43f --- /dev/null +++ b/.github/workflows/build_iwasm_release.yml @@ -0,0 +1,187 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: build iwasm release + +on: + workflow_call: + inputs: + arch: + description: arch of the release + type: string + required: false + default: x86_64 + cwd: + description: working directory + type: string + required: true + llvm_cache_key: + description: the cache key of llvm libraries + type: string + required: true + runner: + description: OS of compilation + type: string + required: true + upload_url: + description: upload binary assets to the URL of release + type: string + required: false + ver_num: + description: a semantic version number. it is required when `release` is true. + type: string + required: false + +env: + DEFAULT_BUILD_OPTIONS: + "-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 \ + -DWAMR_BUILD_DUMP_CALL_STACK=0 \ + -DWAMR_BUILD_LIBC_UVWASI=0 \ + -DWAMR_BUILD_LIBC_EMCC=0 \ + -DWAMR_BUILD_LIB_RATS=0 \ + -DWAMR_BUILD_LOAD_CUSTOM_SECTION=0 \ + -DWAMR_BUILD_MEMORY_PROFILING=0 \ + -DWAMR_BUILD_MINI_LOADER=0 \ + -DWAMR_BUILD_MULTI_MODULE=0 \ + -DWAMR_BUILD_PERF_PROFILING=0 \ + -DWAMR_BUILD_SPEC_TEST=0 \ + -DWAMR_BUILD_BULK_MEMORY=1 \ + -DWAMR_BUILD_LIB_PTHREAD=1 \ + -DWAMR_BUILD_LIB_PTHREAD_SEMAPHORE=1 \ + -DWAMR_BUILD_LIB_WASI_THREADS=1 \ + -DWAMR_BUILD_LIBC_BUILTIN=1 \ + -DWAMR_BUILD_LIBC_WASI=1 \ + -DWAMR_BUILD_REF_TYPES=1 \ + -DWAMR_BUILD_SIMD=1 \ + -DWAMR_BUILD_SHARED_MEMORY=1 \ + -DWAMR_BUILD_TAIL_CALL=1 \ + -DWAMR_BUILD_THREAD_MGR=1" + GC_EH_BUILD_OPTIONS: + "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 \ + -DWAMR_BUILD_CUSTOM_NAME_SECTION=0 \ + -DWAMR_BUILD_DEBUG_INTERP=0 \ + -DWAMR_BUILD_DEBUG_AOT=0 \ + -DWAMR_BUILD_DUMP_CALL_STACK=0 \ + -DWAMR_BUILD_LIBC_UVWASI=0 \ + -DWAMR_BUILD_LIBC_EMCC=0 \ + -DWAMR_BUILD_LIB_RATS=0 \ + -DWAMR_BUILD_LOAD_CUSTOM_SECTION=0 \ + -DWAMR_BUILD_MEMORY_PROFILING=0 \ + -DWAMR_BUILD_MINI_LOADER=0 \ + -DWAMR_BUILD_MULTI_MODULE=0 \ + -DWAMR_BUILD_PERF_PROFILING=0 \ + -DWAMR_BUILD_SPEC_TEST=0 \ + -DWAMR_BUILD_BULK_MEMORY=1 \ + -DWAMR_BUILD_LIB_PTHREAD=1 \ + -DWAMR_BUILD_LIB_PTHREAD_SEMAPHORE=1 \ + -DWAMR_BUILD_LIB_WASI_THREADS=1 \ + -DWAMR_BUILD_LIBC_BUILTIN=1 \ + -DWAMR_BUILD_LIBC_WASI=1 \ + -DWAMR_BUILD_REF_TYPES=1 \ + -DWAMR_BUILD_SIMD=1 \ + -DWAMR_BUILD_SHARED_MEMORY=1 \ + -DWAMR_BUILD_TAIL_CALL=1 \ + -DWAMR_BUILD_THREAD_MGR=1 \ + -DWAMR_BUILD_EXCE_HANDLING=1 \ + -DWAMR_BUILD_GC=1" + +permissions: + contents: read + +jobs: + build: + runs-on: ${{ inputs.runner }} + strategy: + matrix: + include: + - build_options: $DEFAULT_BUILD_OPTIONS + suffix: "" + - build_options: $GC_EH_BUILD_OPTIONS + suffix: "-gc-eh" + permissions: + contents: write # for uploading release artifacts + + steps: + - uses: actions/checkout@v6.0.2 + + - name: get cached LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ inputs.llvm_cache_key }} + fail-on-cache-miss: true + + - name: generate iwasm binary release + shell: bash + run: | + cmake -S . -B build ${{ matrix.build_options }} + cmake --build build --config Release --parallel 4 + working-directory: ${{ inputs.cwd }} + + - name: smoke test on non-Windows + if: ${{ !startsWith(inputs.runner, 'windows') }} + shell: bash + run: | + if [[ ! -f build/iwasm ]]; then + echo "iwasm binary is not found in the expected location." + exit 1 + fi + + build/iwasm --version + working-directory: ${{ inputs.cwd }} + + - name: smoke test on Windows + if: ${{ startsWith(inputs.runner, 'windows') }} + shell: bash + run: | + if [[ ! -f build/Release/iwasm ]]; then + echo "iwasm binary is not found in the expected location." + exit 1 + fi + + build/Release/iwasm --version + working-directory: ${{ inputs.cwd }} + + - name: Compress the binary on Windows + if: inputs.runner == 'windows-2022' + run: | + tar -czf iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz iwasm.exe + Compress-Archive -Path iwasm.exe -DestinationPath iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.zip + mv iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.* ../ + working-directory: ${{ inputs.cwd }}/build/Release + + - name: compress the binary on non-Windows + if: inputs.runner != 'windows-2022' + run: | + # Follow the symlink to the actual binary file + tar --dereference -czf iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz iwasm + zip iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.zip iwasm + working-directory: ${{ inputs.cwd }}/build + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: ${{ inputs.cwd }}/build/iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz + asset_name: iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: ${{ inputs.cwd }}/build/iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.runner }}.zip + asset_name: iwasm${{ matrix.suffix }}-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/build_llvm_libraries.yml b/.github/workflows/build_llvm_libraries.yml new file mode 100644 index 0000000000..b661131f2c --- /dev/null +++ b/.github/workflows/build_llvm_libraries.yml @@ -0,0 +1,131 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: Reusable workflow-build_llvm_libraries + +on: + workflow_call: + inputs: + os: + required: true + type: string + arch: + required: true + type: string + container_image: + required: false + type: string + extra_build_llvm_options: + required: false + type: string + default: "" + cache_key_suffix: + required: false + type: string + default: "" + outputs: + cache_key: + description: "A cached key of LLVM libraries" + value: ${{ jobs.build_llvm_libraries.outputs.key}} + +permissions: + contents: read + +jobs: + build_llvm_libraries: + runs-on: ${{ inputs.os }} + # Using given container image if it is specified. + # Otherwise, it will be ignored by the runner. + container: + image: ${{ inputs.container_image }} + outputs: + key: ${{ steps.create_lib_cache_key.outputs.key}} + permissions: + contents: read + actions: write # for uploading cached artifact + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install dependencies for non macos + if: ${{ !startsWith(inputs.os, 'macos') }} + shell: bash + run: /usr/bin/env python3 -m pip install -r requirements.txt + working-directory: build-scripts + + - name: install dependencies for macos + if: startsWith(inputs.os, 'macos') + run: /usr/bin/env python3 -m pip install -r requirements.txt --break-system-packages + working-directory: build-scripts + + - name: Retrieve the last commit ID + id: get_last_commit + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + echo "last_commit=$(GH_TOKEN=${{ secrets.GITHUB_TOKEN }} /usr/bin/env python3 ./build_llvm.py ${{ inputs.extra_build_llvm_options }} --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 + shell: bash + run: | + echo "key=0-llvm-libraries-${{ inputs.os }}-${{ inputs.arch }}-${{ steps.get_last_commit.outputs.last_commit }}${{ inputs.cache_key_suffix }}" >> $GITHUB_OUTPUT + + - name: Cache LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ steps.create_lib_cache_key.outputs.key}} + + - uses: actions/cache@v5 + 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' + + # Don't install dependencies if the cache is hit or running in docker container + - run: sudo apt install -y ccache ninja-build + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && startsWith(inputs.os, 'ubuntu') && inputs.container_image == '' + + - uses: actions/cache@v5 + 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') + + - uses: actions/cache@v5 + 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 == 'windows-2022' + + # Install tools on Windows + - run: choco install -y ccache ninja + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' && inputs.os == 'windows-2022' + + - name: Build LLVM libraries + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + shell: bash + run: /usr/bin/env python3 ./build_llvm.py ${{ inputs.extra_build_llvm_options }} --arch ${{ inputs.arch }} + working-directory: build-scripts diff --git a/.github/workflows/build_wamr_lldb.yml b/.github/workflows/build_wamr_lldb.yml new file mode 100644 index 0000000000..98a15e653b --- /dev/null +++ b/.github/workflows/build_wamr_lldb.yml @@ -0,0 +1,269 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: build wamr lldb + +on: + workflow_call: + inputs: + arch: + description: arch of the release + type: string + required: false + default: x86_64 + runner: + description: OS of compilation + type: string + required: true + upload_url: + description: upload binary assets to the URL of release + type: string + required: true + ver_num: + description: a semantic version number + type: string + required: true + wasi_sdk_url: + description: download WASI_SDK from this URL + type: string + required: false + default: "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-linux.tar.gz" + +permissions: + contents: read + +jobs: + try_reuse: + permissions: + contents: write # for uploading release artifacts + uses: ./.github/workflows/reuse_latest_release_binaries.yml + with: + binary_name_stem: "wamr-lldb-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}" + last_commit: "ea63ba4bd010c2285623ad4acc0262a4d63bcfea" + the_path: "./build-scripts/lldb_wasm.patch" + upload_url: ${{ inputs.upload_url }} + + build: + needs: try_reuse + if: needs.try_reuse.outputs.result != 'hit' + runs-on: ${{ inputs.runner }} + + env: + PYTHON_VERSION: '3.10' + PYTHON_UBUNTU_STANDALONE_BUILD: https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-x86_64-unknown-linux-gnu-install_only.tar.gz + PYTHON_MACOS_STANDALONE_BUILD: https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.10.11+20230507-x86_64-apple-darwin-install_only.tar.gz + permissions: + contents: write # for uploading release artifacts + + steps: + - uses: actions/checkout@v6.0.2 + + - name: download and install wasi-sdk + run: | + cd /opt + basename=$(basename ${{ inputs.wasi_sdk_url }}) + sudo wget --progress=dot:giga ${{ inputs.wasi_sdk_url }} + sudo tar -xzf ${basename} + sudo rm ${basename} + sudo mv wasi-sdk-* wasi-sdk + + - name: Cache build + id: lldb_build_cache + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm-project/build/bin + ./core/deps/llvm-project/build/include + ./core/deps/llvm-project/build/lib + ./core/deps/llvm-project/build/libexec + ./core/deps/llvm-project/build/share + ./core/deps/llvm-project/lldb/tools/ + ./core/deps/llvm-project/wamr-lldb/ + key: ${{inputs.arch}}-${{ inputs.runner }}-lldb_build + + - name: setup xcode macos + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + # Remove xCode command line tools, to prevent duplicate symbol compilation failures + - name: install utils macos + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') + run: | + brew install swig cmake ninja libedit + sudo rm -rf /Library/Developer/CommandLineTools + + - name: install utils ubuntu + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') + run: sudo apt update && sudo apt-get install -y lld ninja-build + + # `git clone` takes ~7m + - name: download llvm + if: steps.lldb_build_cache.outputs.cache-hit != 'true' + run: | + wget https://github.com/llvm/llvm-project/archive/1f27fe6128769f00197925c3b8f6abb9d0e5cd2e.zip + unzip -q 1f27fe6128769f00197925c3b8f6abb9d0e5cd2e.zip + mv llvm-project-1f27fe6128769f00197925c3b8f6abb9d0e5cd2e llvm-project + working-directory: core/deps/ + + - name: apply wamr patch + if: steps.lldb_build_cache.outputs.cache-hit != 'true' + run: | + git init + git config user.email "action@github.com" + git config user.name "github action" + git apply ../../../build-scripts/lldb_wasm.patch + working-directory: core/deps/llvm-project + + - name: get stand-alone python ubuntu + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') + run: | + wget ${{ env.PYTHON_UBUNTU_STANDALONE_BUILD }} -O python.tar.gz + tar -xvf python.tar.gz + working-directory: core/deps + + - name: get stand-alone python macos + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') + run: | + wget ${{ env.PYTHON_MACOS_STANDALONE_BUILD }} -O python.tar.gz + tar -xvf python.tar.gz + working-directory: core/deps + + - name: build lldb ubuntu + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') + run: | + echo "start to build lldb..." + mkdir -p wamr-lldb + cmake -S ./llvm -B build \ + -G Ninja \ + -DCMAKE_INSTALL_PREFIX=../wamr-lldb \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DLLVM_ENABLE_PROJECTS="clang;lldb" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;WebAssembly" \ + -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_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_ENABLE_BINDINGS:BOOL=OFF \ + -DLLVM_ENABLE_LIBXML2:BOOL=ON \ + -DLLVM_ENABLE_LLD:BOOL=ON \ + -DLLDB_ENABLE_PYTHON:BOOL=ON \ + -DLLDB_EMBED_PYTHON_HOME=ON \ + -DLLDB_PYTHON_HOME=.. \ + -DLLDB_PYTHON_RELATIVE_PATH=lib/lldb-python \ + -DPython3_EXECUTABLE="$(pwd)/../python/bin/python${{ env.PYTHON_VERSION }}" + cmake --build build --target lldb install --parallel $(nproc) + working-directory: core/deps/llvm-project + + - name: validate lldb ubuntu + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') + run: | + echo "start to validate lldb..." + mkdir -p wamr-debug + cmake -S product-mini/platforms/linux -B wamr-debug -DWAMR_BUILD_DEBUG_INTERP=1 + cmake --build wamr-debug --parallel $(nproc) + export LD_LIBRARY_PATH=$(pwd)/core/deps/python/lib:${LD_LIBRARY_PATH} + python3 ci/validate_lldb.py --port 1239 --lldb core/deps/wamr-lldb/bin/lldb --wamr wamr-debug/iwasm --verbose + working-directory: . + + # Define CMAKE_OSX_SYSROOT to avoid the error: + # no such file or directory: 'llvm-project/build/tools/lldb/tools/debugserver/source/mach_excServer.c' + # no such file or directory: 'llvm-project/build/tools/lldb/tools/debugserver/source/mach_excUser.c' + # + # This workaround should be removed when the issue is fixed in llvm-project: + # - https://github.com/llvm/llvm-project/pull/138020/ + - name: build lldb macos + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') + run: | + echo "start to build lldb..." + mkdir -p wamr-lldb + cmake -S ./llvm -B build \ + -G Ninja \ + -DCMAKE_OSX_SYSROOT=$(xcrun --show-sdk-path) \ + -DCMAKE_INSTALL_PREFIX=../wamr-lldb \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DLLVM_ENABLE_PROJECTS="clang;lldb" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;WebAssembly" \ + -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_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_ENABLE_BINDINGS:BOOL=OFF \ + -DLLVM_ENABLE_LIBXML2:BOOL=ON \ + -DLLDB_BUILD_FRAMEWORK:BOOL=OFF \ + -DLLDB_ENABLE_PYTHON:BOOL=ON \ + -DLLDB_EMBED_PYTHON_HOME=ON \ + -DLLDB_PYTHON_HOME=.. \ + -DLLDB_PYTHON_RELATIVE_PATH=lib/lldb-python \ + -DPython3_EXECUTABLE="$(pwd)/../python/bin/python${{ env.PYTHON_VERSION }}" + cmake --build build --target lldb install --parallel $(nproc) + working-directory: core/deps/llvm-project + + - name: pack a distribution + if: steps.lldb_build_cache.outputs.cache-hit != 'true' + run: | + mkdir -p wamr-lldb/bin + mkdir -p wamr-lldb/lib + cp build/bin/lldb* wamr-lldb/bin + cp lldb/tools/lldb-vscode/package.json wamr-lldb + cp -r lldb/tools/lldb-vscode/syntaxes/ wamr-lldb + working-directory: core/deps/llvm-project + + - name: pack ubuntu specific libraries + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'ubuntu') + run: | + cp build/lib/liblldb*.so wamr-lldb/lib + cp build/lib/liblldb*.so.* wamr-lldb/lib + cp -R build/lib/lldb-python wamr-lldb/lib + cp -R ../python/lib/python* wamr-lldb/lib + cp ../python/lib/libpython${{ env.PYTHON_VERSION }}.so.1.0 wamr-lldb/lib + working-directory: core/deps/llvm-project + + - name: pack macos specific libraries + if: steps.lldb_build_cache.outputs.cache-hit != 'true' && contains(inputs.runner, 'macos') + run: | + cp build/lib/liblldb*.dylib wamr-lldb/lib + cp -R build/lib/lldb-python wamr-lldb/lib + cp -R ../python/lib/python* wamr-lldb/lib + cp ../python/lib/libpython*.dylib wamr-lldb/lib + install_name_tool -change /install/lib/libpython${{ env.PYTHON_VERSION }}.dylib @rpath/libpython${{ env.PYTHON_VERSION }}.dylib wamr-lldb/lib/liblldb.*.dylib + # Patch path of python library -> https://github.com/indygreg/python-build-standalone/blob/85923ca3911784e6978b85d56e06e9ae75cb2dc4/docs/quirks.rst?plain=1#L412-L446 + working-directory: core/deps/llvm-project + + - name: compress the binary + run: | + tar czf wamr-lldb-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz wamr-lldb + zip -r wamr-lldb-${{ inputs.ver_num }}-${{ inputs.runner }}.zip wamr-lldb + working-directory: core/deps/llvm-project + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: core/deps/llvm-project/wamr-lldb-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz + asset_name: wamr-lldb-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: core/deps/llvm-project/wamr-lldb-${{ inputs.ver_num }}-${{ inputs.runner }}.zip + asset_name: wamr-lldb-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/build_wamr_sdk.yml b/.github/workflows/build_wamr_sdk.yml new file mode 100644 index 0000000000..df1f26c748 --- /dev/null +++ b/.github/workflows/build_wamr_sdk.yml @@ -0,0 +1,109 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: build wamr-sdk + +on: + workflow_call: + inputs: + arch: + description: arch of the release + type: string + required: false + default: x86_64 + config_file: + description: warm-sdk config file path + type: string + required: true + runner: + description: OS of compilation + type: string + required: true + upload_url: + description: upload binary assets to the URL of release + type: string + required: true + ver_num: + description: a semantic version number + type: string + required: true + wasi_sdk_url: + description: download WASI_SDK from this URL + type: string + required: true + wamr_app_framework_url: + description: download WAMR app framework to get wamr_sdk + type: string + required: true + +permissions: + contents: read + +jobs: + build: + runs-on: ${{ inputs.runner }} + permissions: + contents: write # for uploading release artifacts + + steps: + - uses: actions/checkout@v6.0.2 + + - name: download wamr-app-framework + run: | + git clone ${{ inputs.wamr_app_framework_url }} + cd wamr-app-framework + git submodule init + git submodule update + working-directory: wamr-sdk + + - name: download and install wasi-sdk + run: | + cd /opt + basename=$(basename ${{ inputs.wasi_sdk_url }}) + sudo wget --progress=dot:giga ${{ inputs.wasi_sdk_url }} + sudo tar -xzf ${basename} + sudo rm ${basename} + sudo mv wasi-sdk-* wasi-sdk + + - name: download dependencies + run: | + cd ./wamr-app-framework/deps + ./download.sh + working-directory: wamr-sdk + + - name: generate wamr-sdk release + run: | + cd ./wamr-app-framework/wamr-sdk + ./build_sdk.sh -n wamr-sdk -x $(pwd)/${{ inputs.config_file }} + working-directory: wamr-sdk + + - name: compress the binary + run: | + cd wamr-app-framework/wamr-sdk/out + tar czf wamr-sdk-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz wamr-sdk + zip -r wamr-sdk-${{ inputs.ver_num }}-${{ inputs.runner }}.zip wamr-sdk + working-directory: wamr-sdk + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: wamr-sdk/wamr-app-framework/wamr-sdk/out/wamr-sdk-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz + asset_name: wamr-sdk-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: wamr-sdk/wamr-app-framework/wamr-sdk/out/wamr-sdk-${{ inputs.ver_num }}-${{ inputs.runner }}.zip + asset_name: wamr-sdk-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.zip + asset_content_type: application/zip + + - name: delete wamr-app-framework + run: | + rm -rf wamr-app-framework + working-directory: wamr-sdk diff --git a/.github/workflows/build_wamr_vscode_ext.yml b/.github/workflows/build_wamr_vscode_ext.yml new file mode 100644 index 0000000000..7410fd0955 --- /dev/null +++ b/.github/workflows/build_wamr_vscode_ext.yml @@ -0,0 +1,79 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: build wamr-ide vscode extension + +on: + workflow_call: + inputs: + upload_url: + description: upload binary assets to the URL of release + type: string + required: true + ver_num: + description: a semantic version number. + type: string + required: true + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-22.04 + permissions: + contents: write # for uploading release artifacts + + steps: + - uses: actions/checkout@v6.0.2 + + - name: Use Node.js 18.x + uses: actions/setup-node@v6 + with: + node-version: 18.x + + - name: set vscode extension to correct version + run: | + npm install -g json + json -I -f package.json -e "this.version=\"${{ inputs.ver_num }}\"" + working-directory: test-tools/wamr-ide/VSCode-Extension + + - name: generate wamr ide vscode extension + run: | + npm install -g @vscode/vsce + rm -rf node_modules + npm install + vsce package + working-directory: test-tools/wamr-ide/VSCode-Extension + + - name: publish wamr ide vscode extension to the vsce marketplace + if: ${{ github.repository == 'bytecodealliance/wasm-micro-runtime' }} + run: | + vsce publish -p ${{ secrets.TOKEN }} + working-directory: test-tools/wamr-ide/VSCode-Extension + + - name: compress the vscode extension + run: | + mv wamride-*.vsix wamr-ide.vsix + tar czf wamr-ide-${{ inputs.ver_num }}.tar.gz wamr-ide.vsix + zip wamr-ide-${{ inputs.ver_num }}.zip wamr-ide.vsix + working-directory: test-tools/wamr-ide/VSCode-Extension + + - name: upload release tar.gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/VSCode-Extension/wamr-ide-${{ inputs.ver_num }}.tar.gz + asset_name: wamr-ide-${{ inputs.ver_num }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: test-tools/wamr-ide/VSCode-Extension/wamr-ide-${{ inputs.ver_num }}.zip + asset_name: wamr-ide-${{ inputs.ver_num }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/build_wamr_wasi_extensions.yml b/.github/workflows/build_wamr_wasi_extensions.yml new file mode 100644 index 0000000000..21a07a1cc1 --- /dev/null +++ b/.github/workflows/build_wamr_wasi_extensions.yml @@ -0,0 +1,57 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: build wamr_wasi_extensions release + +on: + workflow_call: + inputs: + upload_url: + description: upload binary assets to the URL of release + type: string + required: false + ver_num: + description: a semantic version number. it is required when `release` is true. + type: string + required: false + +permissions: + contents: read + +jobs: + build_wamr_wasi_extensions: + runs-on: ${{ matrix.os }} + permissions: + contents: write # for uploading release artifacts + strategy: + matrix: + os: [ubuntu-22.04] + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamr-wasi-extensions + run: | + mkdir dist + ./build_libs.sh $(pwd)/dist/wamr-wasi-extensions + working-directory: wamr-wasi-extensions + + - name: Compress the binary + run: | + zip -r wamr-wasi-extensions-${{ inputs.ver_num }}.zip wamr-wasi-extensions + working-directory: wamr-wasi-extensions/dist + + - name: Upload release zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: wamr-wasi-extensions/dist/wamr-wasi-extensions-${{ inputs.ver_num }}.zip + asset_name: wamr-wasi-extensions-${{ inputs.ver_num }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/build_wamrc.yml b/.github/workflows/build_wamrc.yml new file mode 100644 index 0000000000..9fbe38c0a0 --- /dev/null +++ b/.github/workflows/build_wamrc.yml @@ -0,0 +1,125 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: build wamrc + +on: + workflow_call: + inputs: + arch: + description: arch of the release + type: string + required: false + default: x86_64 + llvm_cache_key: + description: the cache key of llvm libraries + type: string + required: true + release: + description: it is a part of the release process + type: boolean + required: true + runner: + description: OS of compilation + type: string + required: true + upload_url: + description: upload binary assets to the URL of release + type: string + required: false + ver_num: + description: a semantic version number. it is required when `release` is true. + type: string + required: false + +permissions: + contents: read + +jobs: + build: + runs-on: ${{ inputs.runner }} + permissions: + contents: write # for uploading release artifacts + + steps: + - uses: actions/checkout@v6.0.2 + + - name: get cached LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ inputs.llvm_cache_key }} + fail-on-cache-miss: true + + - name: generate wamrc binary release + run: | + cmake -S . -B build + cmake --build build --config Release --parallel 4 + working-directory: wamr-compiler + + - name: smoke test on non-windows + if: ${{ !startsWith(inputs.runner, 'windows') }} + shell: bash + run: | + if [[ ! -f build/wamrc ]]; then + echo "wamrc binary is not found in the expected location." + exit 1 + fi + + build/wamrc --version + working-directory: wamr-compiler + + - name: smoke test on Windows + if: ${{ startsWith(inputs.runner, 'windows') }} + shell: bash + run: | + if [[ ! -f build/Release/wamrc ]]; then + echo "wamrc binary is not found in the expected location." + exit 1 + fi + + build/Release/wamrc --version + working-directory: wamr-compiler + + - name: Compress the binary on Windows + if: inputs.runner == 'windows-2022' && inputs.release + run: | + tar -czf wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz wamrc.exe + Compress-Archive -Path wamrc.exe -DestinationPath wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.zip + mv wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.* ../ + working-directory: wamr-compiler/build/Release + + - name: compress the binary on non-Windows + if: inputs.runner != 'windows-2022' && inputs.release + run: | + # Follow the symlink to the actual binary file + tar --dereference -czf wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz wamrc + zip wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.zip wamrc + working-directory: wamr-compiler/build + + - name: upload release tar.gz + if: inputs.release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: wamr-compiler/build/wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.tar.gz + asset_name: wamrc-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + if: inputs.release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: wamr-compiler/build/wamrc-${{ inputs.ver_num }}-${{ inputs.runner }}.zip + asset_name: wamrc-${{ inputs.ver_num }}-${{ inputs.arch }}-${{ inputs.runner }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/check_version_h.yml b/.github/workflows/check_version_h.yml new file mode 100644 index 0000000000..4ea4c1105e --- /dev/null +++ b/.github/workflows/check_version_h.yml @@ -0,0 +1,29 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: confirm version.h stay in sync + +on: + workflow_call: + +permissions: + contents: read + +jobs: + confirm_version: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: cmake execute to generate version.h + run: cmake -B build_version -S . + + - name: confirm version.h + run: | + if [ -z "$(git status --porcelain | grep version.h)" ]; then + echo "version.h is in sync" + else + echo "version.h is not in sync" + exit 1 + fi diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000..5f5eecbc3e --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,135 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: "CodeQL" + +on: + # run on every push to the feature-development branch + # the main branch is covered by below cron plan + push: + branches: + - dev/** + # midnight UTC on the latest commit on the main branch + schedule: + - cron: "0 0 * * *" + # allow to be triggered manually + workflow_dispatch: + +jobs: + analyze: + # only run this job if the repository is not a fork + # if want to run this job on a fork, please remove the if condition + if: github.repository == 'bytecodealliance/wasm-micro-runtime' + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + # But it is not free, so please be aware of the cost. + runs-on: ubuntu-22.04 + timeout-minutes: 360 + + strategy: + fail-fast: false + matrix: + #TODO: add actions + language: ["cpp"] + + permissions: + contents: read + actions: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6.0.2 + with: + submodules: recursive + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4.35.1 + with: + languages: ${{ matrix.language }} + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + queries: security-and-quality + config-file: ./.github/codeql/codeql_config.yml + + - run: | + ./.github/scripts/codeql_buildscript.sh || exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4.35.1 + with: + category: "/language:${{matrix.language}}" + upload: false + id: step1 + + # - cpp/alloca-in-loop is about touch_pages() which is intended to + # - cpp/command-line-injection is about bh_system() which is used to + # - cpp/path-injection is used in bh_read_file_to_buffer() to load a .wasm. + # or operate a stack usage file which is not sensitive or generate a .aot + # - cpp/suspicious-pointer-scaling + # - wasm_runtime_invoke_native() used to trivial registers + # - cpp/uncontrolled-process-operation is about dlopen() which is used by + # native libraries registrations. + # - cpp/world-writable-file-creation is about fopen() a temporary file + # for perf-PID.map or .aot(wamrc). The permission isn't sensitive. + # file. + # + # execute customized compiler + - name: Filter out unwanted errors and warnings + uses: advanced-security/filter-sarif@v1 + with: + patterns: | + ## Exclude files and directories + -**/build/** + -**/core/deps/** + -**/cmake*/Modules/** + -**/test*/** + -**/wasm-app*/** + ## Exclude rules 1. Related to formatting, style + -**:cpp/commented-out-code + -**:cpp/complex-condition + -**:cpp/empty-if + -**:cpp/fixme-comment + -**:cpp/include-non-header + -**:cpp/long-switch + -**:cpp/poorly-documented-function + -**:cpp/trivial-switch + -**:cpp/unused-local-variable + -**:cpp/unused-static-function + -**:cpp/unused-static-variable + -**:cpp/use-of-goto + ## Exclude rules 2. Related to special usage of APIs + -**:cpp/alloca-in-loop + -**:cpp/command-line-injection + -**:cpp/path-injection + -core/iwasm/common/wasm_runtime_common.c:cpp/suspicious-pointer-scaling + -**:cpp/uncontrolled-process-operation + -**:cpp/world-writable-file-creation + input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + + - name: Upload CodeQL results to code scanning + uses: github/codeql-action/upload-sarif@v4.35.1 + with: + sarif_file: ${{ steps.step1.outputs.sarif-output }} + category: "/language:${{matrix.language}}" + + - name: Upload CodeQL results as an artifact + uses: actions/upload-artifact@v7.0.1 + with: + name: codeql-results + path: ${{ steps.step1.outputs.sarif-output }} + retention-days: 10 + + - name: Fail if an error is found + run: | + ./.github/scripts/codeql_fail_on_error.py \ + ${{ steps.step1.outputs.sarif-output }}/cpp.sarif + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} diff --git a/.github/workflows/coding_guidelines.yml b/.github/workflows/coding_guidelines.yml new file mode 100644 index 0000000000..402c455d86 --- /dev/null +++ b/.github/workflows/coding_guidelines.yml @@ -0,0 +1,31 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: Coding Guidelines + +on: + # will be triggered on PR events + pull_request: + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + compliance_job: + runs-on: ubuntu-22.04 + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + with: + fetch-depth: 0 + + # github.event.pull_request.base.label = ${{github.repository}}/${{github.base_ref}} + - name: Run Coding Guidelines Checks + run: /usr/bin/env python3 ./ci/coding_guidelines_check.py --commits ${{ github.event.pull_request.base.sha }}..HEAD diff --git a/.github/workflows/compilation_on_android_ubuntu.yml b/.github/workflows/compilation_on_android_ubuntu.yml new file mode 100644 index 0000000000..e945887b2b --- /dev/null +++ b/.github/workflows/compilation_on_android_ubuntu.yml @@ -0,0 +1,779 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on android, ubuntu-22.04 + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_android_ubuntu.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "tests/unit/**" + - "wamr-compiler/**" + - "test-tools/wamr-ide/**" + # will be triggered on push events + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_android_ubuntu.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "tests/unit/**" + - "wamr-compiler/**" + - "test-tools/wamr-ide/**" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # For BUILD + AOT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + FAST_INTERP_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + 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" + 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" + WAMR_COMPILER_TEST_OPTIONS: "-s wamr_compiler -S -b -P" + GC_TEST_OPTIONS: "-s spec -G -b -P" + MEMORY64_TEST_OPTIONS: "-s spec -W -b -P" + MULTI_MEMORY_TEST_OPTIONS: "-s spec -E -b -P" + EXTENDED_CONST_EXPR_TEST_OPTIONS: "-s spec -N -b -P" + +permissions: + contents: read + +jobs: + check_version_h: + permissions: + contents: read + actions: write + uses: ./.github/workflows/check_version_h.yml + + build_llvm_libraries_on_ubuntu_2204: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "X86" + + build_wamrc: + needs: [build_llvm_libraries_on_ubuntu_2204] + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + # 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: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. -DCMAKE_C_FLAGS="-Werror" + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + build_iwasm: + needs: [build_llvm_libraries_on_ubuntu_2204] + runs-on: ${{ matrix.os }} + strategy: + matrix: + make_options_run_mode: [ + # Running mode + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + $LLVM_LAZY_JIT_BUILD_OPTIONS, + $LLVM_EAGER_JIT_BUILD_OPTIONS, + $MULTI_TIER_JIT_BUILD_OPTIONS, + ] + make_options_feature: [ + # Features + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1", + "-DWAMR_BUILD_DEBUG_AOT=1", + "-DWAMR_BUILD_DEBUG_INTERP=1", + "-DWAMR_BUILD_DUMP_CALL_STACK=1", + "-DWAMR_BUILD_LIB_PTHREAD=1", + "-DWAMR_BUILD_LIB_WASI_THREADS=1", + "-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1", + "-DWAMR_BUILD_MINI_LOADER=1", + "-DWAMR_BUILD_MEMORY_PROFILING=1", + "-DWAMR_BUILD_MULTI_MODULE=1", + "-DWAMR_BUILD_PERF_PROFILING=1", + "-DWAMR_BUILD_LIB_SIMDE=1", + "-DWAMR_BUILD_TAIL_CALL=1", + "-DWAMR_DISABLE_HW_BOUND_CHECK=1", + "-DWAMR_BUILD_MEMORY64=1", + "-DWAMR_BUILD_MULTI_MEMORY=1", + "-DWAMR_BUILD_SHARED=1", + "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1", + "-DWAMR_BUILD_LIME1=1 -DWAMR_BUILD_BULK_MEMORY=0 -DWAMR_BUILD_REF_TYPES=0 -DWAMR_BUILD_SIMD=0", + ] + os: [ubuntu-22.04] + platform: [android, linux] + exclude: + # incompatible feature and platform and mode + # MULTI_MODULE only on INTERP mode and AOT mode + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MODULE=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + # DEBUG_INTERP only on CLASSIC INTERP mode + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + # TODO: DEBUG_AOT on JIT + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + # Memory64 only on CLASSIC INTERP and AOT mode, and only on 64-bit platform + - make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + platform: android + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + # Multi memory only on CLASSIC INTERP mode, and only on 64-bit platform + - make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + platform: android + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + # Fast-JIT and Multi-Tier-JIT mode don't support android + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + platform: android + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + platform: android + # LLVM JIT pre-built binary wasn't compiled by Android NDK + # and isn't available for android + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + platform: android + - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS + platform: android + # android does not support WAMR_BUILD_SHARED in its CMakeLists.txt. + - make_options_feature: "-DWAMR_BUILD_SHARED=1" + platform: android + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + # classic interp doesn't support SIMD + - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # fast jit doesn't support SIMD + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # multi-tier jit doesn't support SIMD + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + # only download llvm cache when needed + - name: Get LLVM libraries + id: retrieve_llvm_libs + if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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 for linux + if: matrix.platform == 'linux' + run: | + mkdir build && cd build + cmake .. -DCMAKE_C_FLAGS="-Werror" ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} ${{ matrix.extra_options}} + cmake --build . --config Release --parallel 4 + working-directory: product-mini/platforms/${{ matrix.platform }} + + - name: Build iwasm for android + if: matrix.platform == 'android' + run: | + mkdir build && cd build + cmake .. -DCMAKE_C_FLAGS="-Werror" ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} \ + -DWAMR_BUILD_TARGET=X86_64 + cmake --build . --config Release --parallel 4 + working-directory: product-mini/platforms/${{ matrix.platform }} + + build_unit_tests: + needs: [build_llvm_libraries_on_ubuntu_2204, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04] + build_target: ["X86_64", "X86_32"] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + with: + submodules: recursive + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: (steps.retrieve_llvm_libs.outputs.cache-hit != 'true') + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Install dependencies for X86_32 + if: matrix.build_target == 'X86_32' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y g++-multilib libzstd-dev:i386 zlib1g-dev:i386 + + - name: Build and run unit tests + run: | + mkdir build && cd build + cmake .. -DWAMR_BUILD_TARGET=${{ matrix.build_target }} + cmake --build . --parallel 4 + ctest --output-on-failure + working-directory: tests/unit + + build_regression_tests: + needs: [build_llvm_libraries_on_ubuntu_2204] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: (steps.retrieve_llvm_libs.outputs.cache-hit != 'true') + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build wamrc and iwasm + run: | + ./build_wamr.sh + working-directory: tests/regression/ba-issues + + - name: Run regression tests + run: | + python run.py + working-directory: tests/regression/ba-issues + + build_samples_wasm_c_api: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu_2204, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + make_options: [ + # Running mode + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + $LLVM_LAZY_JIT_BUILD_OPTIONS, + $LLVM_EAGER_JIT_BUILD_OPTIONS, + $MULTI_TIER_JIT_BUILD_OPTIONS, + ] + os: [ubuntu-22.04] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + # classic interp doesn't support SIMD + - make_options: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # fast jit doesn't support Multi-module and SIMD + - make_options: $FAST_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0 -DWAMR_BUILD_MULTI_MODULE=0" + # multi-tier jit doesn't support Multi-module and SIMD + - make_options: $MULTI_TIER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0 -DWAMR_BUILD_MULTI_MODULE=0" + # LLVM JIT doesn't support Multi-module + - make_options: $LLVM_LAZY_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_MULTI_MODULE=0" + - make_options: $LLVM_EAGER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_MULTI_MODULE=0" + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamrc + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Build Sample [wasm-c-api] + run: | + VERBOSE=1 + cmake -S . -B build ${{ matrix.make_options }} ${{ matrix.extra_options }} + cmake --build build --config Debug --parallel 4 + ctest --test-dir build --output-on-failure + working-directory: samples/wasm-c-api + + - name: Build Sample [printversion] + run: | + ./test.sh + working-directory: samples/printversion + + build_samples_others: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu_2204, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Build Sample [basic] + run: | + cd samples/basic + ./build.sh + ./run.sh + + - name: Build Sample [file] + run: | + cd samples/file + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./src/iwasm -f wasm-app/file.wasm -d . + + - name: Build Sample [multi-thread] + run: | + cd samples/multi-thread + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./iwasm wasm-apps/test.wasm + + - name: Build Sample [multi-module] + run: | + cd samples/multi-module + mkdir build && cd build + cmake .. -DWAMR_BUILD_AOT=1 + cmake --build . --config Debug --parallel 4 + ./multi_module mC.wasm + ./multi_module mC.aot + + - name: Build Sample [spawn-thread] + run: | + cd samples/spawn-thread + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./spawn_thread + + - name: Build Sample [ref-types] + run: | + cd samples/ref-types + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./hello + + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm + + - name: Build Sample [shared-module] + run: | + cd samples/shared-module + ./build.sh + ./run.sh + + - name: Build Sample [terminate] + run: | + cd samples/terminate + ./build.sh + ./run.sh + + - name: Build Sample [debug-tools] + run: | + cd samples/debug-tools + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./iwasm wasm-apps/trap.wasm | grep "#" > call_stack.txt + ./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt + bash -x ../symbolicate.sh + + - name: Build Sample [native-stack-overflow] + run: | + cd samples/native-stack-overflow + ./build.sh + ./run.sh test1 + ./run.sh test2 + + - name: Build Sample [shared-heap] + run: | + cd samples/shared-heap + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./shared_heap_test + ./shared_heap_test --aot + + - name: Build Sample [import-func-callback] + run: | + cd samples/import-func-callback + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./import-func-callback + + - name: Build Sample [custom_section] + run: | + cd samples/custom-section + ./build.sh + ./run.sh + ./build.sh --aot + ./run.sh --aot + + test: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu_2204, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04] + running_mode: + [ + "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, + $GC_TEST_OPTIONS, + $MEMORY64_TEST_OPTIONS, + $MULTI_MEMORY_TEST_OPTIONS, + $EXTENDED_CONST_EXPR_TEST_OPTIONS, + ] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + ubuntu_version: "22.04" + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + running_mode: aot + test_option: $WAMR_COMPILER_TEST_OPTIONS + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Set-up OCaml + uses: ocaml/setup-ocaml@v3 + if: matrix.test_option == '$GC_TEST_OPTIONS' + with: + ocaml-compiler: 4.13 + + - name: Set-up Ocamlbuild + if: matrix.test_option == '$GC_TEST_OPTIONS' + run: opam install ocamlbuild dune menhir + + - name: download and install wasi-sdk + if: matrix.test_option == '$WASI_TEST_OPTIONS' + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: set env variable(if llvm are used) + 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.test_option == '$WASI_TEST_OPTIONS' || matrix.test_option == '$GC_TEST_OPTIONS') + && matrix.test_option != '$MEMORY64_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: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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: 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: 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: bash build.sh + working-directory: ./core/iwasm/libraries/lib-socket/test/ + + - name: run tests + timeout-minutes: 30 + if: matrix.test_option != '$GC_TEST_OPTIONS' + run: ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }} + working-directory: ./tests/wamr-test-suites + + - name: run gc tests + timeout-minutes: 20 + if: matrix.test_option == '$GC_TEST_OPTIONS' + run: | + eval $(opam env) + ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }} + working-directory: ./tests/wamr-test-suites + + #only install x32 support libraries when to run x86_32 cases + - name: install x32 support libraries + if: env.TEST_ON_X86_32 == 'true' + run: + # Add another apt repository as some packages cannot + # be downloaded with the github default repository + sudo curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc && + sudo apt-add-repository https://packages.microsoft.com/ubuntu/${{ matrix.ubuntu_version }}/prod && + sudo apt-get update && + sudo apt install -y g++-multilib lib32gcc-9-dev + + - name: run tests x86_32 + timeout-minutes: 30 + if: env.TEST_ON_X86_32 == 'true' && matrix.test_option != '$GC_TEST_OPTIONS' + run: ./test_wamr.sh ${{ env.X86_32_TARGET_TEST_OPTIONS }} ${{ matrix.test_option }} -t ${{ matrix.running_mode }} + working-directory: ./tests/wamr-test-suites + + - name: run gc tests x86_32 + timeout-minutes: 20 + if: env.TEST_ON_X86_32 == 'true' && matrix.test_option == '$GC_TEST_OPTIONS' + run: | + eval $(opam env) + ./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 new file mode 100644 index 0000000000..30c9b0565b --- /dev/null +++ b/.github/workflows/compilation_on_macos.yml @@ -0,0 +1,460 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on macos + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_macos.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # will be triggered on push events + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_macos.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # For BUILD + AOT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + FAST_INTERP_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + 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" + 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" + +permissions: + contents: read + +jobs: + build_llvm_libraries_on_intel_macos: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "macos-15-intel" + arch: "X86" + build_llvm_libraries_on_arm_macos: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "macos-15" + arch: "AArch64 ARM" + + build_wamrc: + needs: [build_llvm_libraries_on_intel_macos, build_llvm_libraries_on_arm_macos] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-15, macos-15-intel] + include: + - os: macos-15-intel + llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} + - os: macos-15 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_arm_macos.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. -DCMAKE_C_FLAGS="-Werror" + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + build_iwasm: + needs: [build_llvm_libraries_on_intel_macos, build_llvm_libraries_on_arm_macos] + runs-on: ${{ matrix.os }} + strategy: + matrix: + make_options_run_mode: [ + # Running mode + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $LLVM_LAZY_JIT_BUILD_OPTIONS, + $LLVM_EAGER_JIT_BUILD_OPTIONS, + ] + make_options_feature: [ + # Features + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1", + # doesn't support + #"-DWAMR_BUILD_DEBUG_AOT=1", + "-DWAMR_BUILD_DEBUG_INTERP=1", + "-DWAMR_BUILD_DUMP_CALL_STACK=1", + "-DWAMR_BUILD_LIB_PTHREAD=1", + "-DWAMR_BUILD_LIB_WASI_THREADS=1", + "-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1", + "-DWAMR_BUILD_MINI_LOADER=1", + "-DWAMR_BUILD_MEMORY_PROFILING=1", + "-DWAMR_BUILD_MULTI_MODULE=1", + "-DWAMR_BUILD_PERF_PROFILING=1", + "-DWAMR_BUILD_TAIL_CALL=1", + "-DWAMR_DISABLE_HW_BOUND_CHECK=1", + "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1", + ] + os: [macos-15, macos-15-intel] + platform: [darwin] + exclude: + # incompatible feature and platform + # incompatible mode and feature + # MULTI_MODULE only on INTERP mode and AOT mode + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + # DEBUG_INTERP only on CLASSIC INTERP mode + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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: $FAST_INTERP_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" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + # TODO: DEBUG_AOT on JIT + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + # MINI_LOADER only on INTERP mode + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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-15 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_arm_macos.outputs.cache_key }} + - os: macos-15-intel + llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} + # classic interp doesn't support SIMD + - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + # only download llvm cache when needed + - name: Get LLVM libraries + id: retrieve_llvm_libs + if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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 + run: | + mkdir build && cd build + cmake .. -DCMAKE_C_FLAGS="-Werror" ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} ${{ matrix.extra_options }} + cmake --build . --config Release --parallel 4 + working-directory: product-mini/platforms/${{ matrix.platform }} + + build_samples_wasm_c_api: + needs: + [ + build_iwasm, + build_llvm_libraries_on_intel_macos, + build_llvm_libraries_on_arm_macos, + build_wamrc, + ] + runs-on: ${{ matrix.os }} + strategy: + matrix: + make_options: [ + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $LLVM_LAZY_JIT_BUILD_OPTIONS, + $LLVM_EAGER_JIT_BUILD_OPTIONS, + ] + os: [macos-15, macos-15-intel] + include: + - os: macos-15 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_arm_macos.outputs.cache_key }} + - os: macos-15-intel + llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} + # classic interp doesn't support SIMD + - make_options: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # LLVM JIT doesn't support Multi-module + - make_options: $LLVM_LAZY_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_MULTI_MODULE=0" + - make_options: $LLVM_EAGER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_MULTI_MODULE=0" + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamrc + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Build Sample [wasm-c-api] + run: | + VERBOSE=1 + cmake -S . -B build ${{ matrix.make_options }} ${{ matrix.extra_options }} + cmake --build build --config Debug --parallel 4 + ctest --test-dir build --output-on-failure + working-directory: samples/wasm-c-api + + - name: Build Sample [printversion] + run: | + ./test.sh + working-directory: samples/printversion + + build_samples_others: + needs: [build_iwasm, build_wamrc, build_llvm_libraries_on_intel_macos, build_llvm_libraries_on_arm_macos] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-15-intel, macos-15] + include: + - os: macos-15-intel + llvm_cache_key: ${{ needs.build_llvm_libraries_on_intel_macos.outputs.cache_key }} + - os: macos-15 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_arm_macos.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build Sample [basic] + run: | + cd samples/basic + ./build.sh + ./run.sh + + - name: Build Sample [file] + run: | + cd samples/file + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./src/iwasm -f wasm-app/file.wasm -d . + + - name: Build Sample [multi-thread] + run: | + cd samples/multi-thread + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./iwasm wasm-apps/test.wasm + + - name: Build Sample [multi-module] + run: | + cd samples/multi-module + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./multi_module mC.wasm + + - name: Build Sample [spawn-thread] + run: | + cd samples/spawn-thread + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./spawn_thread + + - name: Build Sample [ref-types] + run: | + cd samples/ref-types + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./hello + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + # cmake --build . --config Debug --parallel 4 + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake .. + cmake --build . --config Debug --verbose + ./iwasm wasm-apps/no_pthread.wasm + + ../../../wamr-compiler/build/wamrc --size-level=0 --enable-multi-thread -o wasm-apps/no_pthread.aot wasm-apps/no_pthread.wasm + ./iwasm wasm-apps/no_pthread.aot + + - name: Build Sample [shared-module] + run: | + cd samples/shared-module + ./build.sh + ./run.sh + + - name: Build Sample [terminate] + run: | + cd samples/terminate + ./build.sh + ./run.sh + + - name: Build Sample [debug-tools] + run: | + cd samples/debug-tools + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./iwasm wasm-apps/trap.wasm | grep "#" > call_stack.txt + ./iwasm wasm-apps/trap.aot | grep "#" > call_stack_aot.txt + bash -x ../symbolicate.sh + + - name: Build Sample [native-stack-overflow] + run: | + cd samples/native-stack-overflow + ./build.sh + ./run.sh test1 + ./run.sh test2 + + - name: Build Sample [shared-heap] + run: | + cd samples/shared-heap + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./shared_heap_test + ./shared_heap_test --aot + + - name: Build Sample [import-func-callback] + run: | + cd samples/import-func-callback + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./import-func-callback + + - name: Build Sample [custom_section] + run: | + cd samples/custom-section + ./build.sh + ./run.sh + ./build.sh --aot + ./run.sh --aot + + - name: Test x18 register reservation (macOS ARM64 only) + if: matrix.os == 'macos-15' + run: | + cd product-mini/platforms/darwin + mkdir -p build && cd build + cmake .. -DWAMR_BUILD_AOT=1 + cmake --build . --config Release --parallel 4 + cd ../../../../tests/standalone/test-aot-x18-reserve + ./run.sh --aot diff --git a/.github/workflows/compilation_on_nuttx.yml b/.github/workflows/compilation_on_nuttx.yml new file mode 100644 index 0000000000..1ad28f3bb8 --- /dev/null +++ b/.github/workflows/compilation_on_nuttx.yml @@ -0,0 +1,150 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on nuttx + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/compilation_on_nuttx.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # will be triggered on push events + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/compilation_on_nuttx.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + WASI_SDK_PATH: "/opt/wasi-sdk" + +permissions: + contents: read + +jobs: + build_iwasm_on_nuttx: + runs-on: ubuntu-latest + container: + image: ghcr.io/apache/nuttx/apache-nuttx-ci-linux@sha256:d9261eacf6c6ebe656c571757751c803e8f04c3ae9b820320a5ea5dd57b7205a + + strategy: + matrix: + nuttx_board_config: [ + # x64 + "boards/sim/sim/sim/configs/nsh", + # cortex-m0 + "boards/arm/rp2040/raspberrypi-pico/configs/nsh", + # cortex-m7 + "boards/arm/stm32h7/nucleo-h743zi/configs/nsh", + # cortex-m33 + "boards/arm/rp23xx/raspberrypi-pico-2/configs/nsh", + # riscv32gc + "boards/risc-v/qemu-rv/rv-virt/configs/nsh", + # riscv64gc + "boards/risc-v/qemu-rv/rv-virt/configs/nsh64", + # arm64 + "boards/arm64/qemu/qemu-armv8a/configs/nsh", + ] + + wamr_config_option: + - "CONFIG_INTERPRETERS_WAMR_AOT" + - "CONFIG_INTERPRETERS_WAMR_FAST" + - "CONFIG_INTERPRETERS_WAMR_CLASSIC" + - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_FAST" + - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_FAST CONFIG_INTERPRETERS_WAMR_LIBC_WASI" + - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_FAST CONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN" + - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_CLASSIC" + - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_CLASSIC CONFIG_INTERPRETERS_WAMR_LIBC_WASI" + - "CONFIG_INTERPRETERS_WAMR_AOT CONFIG_INTERPRETERS_WAMR_CLASSIC CONFIG_INTERPRETERS_WAMR_LIBC_WASI" + + steps: + - name: Checkout NuttX + uses: actions/checkout@v6.0.2 + with: + repository: apache/nuttx + ref: 09a71ec7c16c43398d5acbdcbeee7b08736c3170 + path: nuttx + + - name: Checkout NuttX Apps + uses: actions/checkout@v6.0.2 + with: + repository: apache/nuttx-apps + ref: 6bd593459c4af3cef325c3d22bccd5537a8ed755 + path: apps + + - name: Checkout WAMR + uses: actions/checkout@v6.0.2 + with: + repository: ${{ github.repository }} + path: apps/interpreters/wamr/wamr + + - name: Configure WAMR + working-directory: nuttx + run: | + tools/configure.sh ${{ matrix.nuttx_board_config }} + kconfig-tweak --disable CONFIG_RP2040_UF2_BINARY + kconfig-tweak --disable CONFIG_RP23XX_UF2_BINARY + kconfig-tweak --enable CONFIG_PSEUDOFS_SOFTLINKS + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR + kconfig-tweak --enable CONFIG_INTERPRETERS_IWASM_TASK + kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_PRIORITY 100 + kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_STACKSIZE 8192 + for x in ${{ matrix.wamr_config_option }}; do + kconfig-tweak --enable $x + done + + - name: Build + working-directory: nuttx + run: make -j$(nproc) EXTRAFLAGS=-Werror + + - name: Checkout Bloaty + uses: actions/checkout@v6.0.2 + with: + repository: google/bloaty + submodules: recursive + path: bloaty + ref: 34f4a66559ad4938c1e629e9b5f54630b2b4d7b0 + + - name: Build Bloaty + run: | + cmake -Bbuild -GNinja bloaty + cmake --build build + + - name: Size Report + run: | + echo "Build target: ${{ matrix.nuttx_board_config }}" + echo "WAMR build config: ${{ matrix.wamr_config_option }}" + echo "WAMR size:" + build/bloaty -d compileunits --source-filter wamr nuttx/nuttx + echo "libc-builtin size (if enabled):" + build/bloaty -d compileunits --source-filter libc-builtin nuttx/nuttx + echo "libc-wasi size (if enabled):" + build/bloaty -d compileunits --source-filter libc-wasi nuttx/nuttx diff --git a/.github/workflows/compilation_on_sgx.yml b/.github/workflows/compilation_on_sgx.yml new file mode 100644 index 0000000000..6791d31aaf --- /dev/null +++ b/.github/workflows/compilation_on_sgx.yml @@ -0,0 +1,303 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on SGX + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_sgx.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # will be triggered on push events + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/build_llvm_libraries.yml" + - ".github/workflows/compilation_on_sgx.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # ref types enabled in wamrc by default, so we need to enable it for iwasm in AOT mode + AOT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0 -DWAMR_BUILD_REF_TYPES=1" + CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + 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 -DWAMR_BUILD_SIMD=0" + FAST_JIT_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=1" + 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" + # For Spec Test + DEFAULT_TEST_OPTIONS: "-s spec -x -p -b" + SIMD_TEST_OPTIONS: "-s spec -x -p -b -S" + XIP_TEST_OPTIONS: "-s spec -x -p -b -X" + +permissions: + contents: read + +jobs: + build_llvm_libraries: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: ubuntu-22.04 + arch: "X86" + + build_iwasm: + runs-on: ${{ matrix.os }} + strategy: + matrix: + make_options_run_mode: [ + # Running modes supported + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + # Running modes unsupported + #$LLVM_LAZY_JIT_BUILD_OPTIONS, + #$LLVM_EAGER_JIT_BUILD_OPTIONS, + ] + make_options_feature: [ + # Features + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1", + # doesn't support + # "-DWAMR_BUILD_DEBUG_AOT=1", + # "-DWAMR_BUILD_DEBUG_INTERP=1", + "-DWAMR_BUILD_DUMP_CALL_STACK=1", + "-DWAMR_BUILD_LIB_PTHREAD=1", + "-DWAMR_BUILD_LIB_WASI_THREADS=1", + "-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1", + "-DWAMR_BUILD_MINI_LOADER=1", + "-DWAMR_BUILD_MEMORY_PROFILING=1", + "-DWAMR_BUILD_MULTI_MODULE=1", + "-DWAMR_BUILD_PERF_PROFILING=1", + "-DWAMR_BUILD_REF_TYPES=1", + "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1", + # doesn't support + "-DWAMR_BUILD_SIMD=0", + "-DWAMR_BUILD_TAIL_CALL=1", + "-DWAMR_DISABLE_HW_BOUND_CHECK=1", + "-DWAMR_BUILD_SGX_IPFS=1", + ] + os: [ubuntu-22.04] + platform: [linux-sgx] + exclude: + # incompatible mode and feature + # MINI_LOADER only on INTERP mode + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + # FAST_JIT doesn't support MULTI_MODULE + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MODULE=1" + include: + # classic interp mode doesn't support SIMD + - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_SIMD=0" + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install SGX SDK and necessary libraries + uses: ./.github/actions/install-linux-sgx + with: + os: ${{ matrix.os }} + + - name: Build iwasm + run: | + mkdir build && cd build + cmake .. ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} + cmake --build . --config Release --parallel 4 + cd ../enclave-sample + make + working-directory: product-mini/platforms/${{ matrix.platform }} + + run_samples_file: + needs: [build_iwasm, build_llvm_libraries] + runs-on: ${{ matrix.os }} + strategy: + matrix: + iwasm_make_options_run_mode: [ + # Running modes supported + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + # Running modes unsupported + #$LLVM_LAZY_JIT_BUILD_OPTIONS, + #$LLVM_EAGER_JIT_BUILD_OPTIONS, + ] + os: [ubuntu-22.04] + iwasm_make_options_feature: [ + # Features to be tested: IPFS + "-DWAMR_BUILD_SGX_IPFS=1", + ] + platform: [linux-sgx] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries.outputs.cache_key }} + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: install SGX SDK and necessary libraries + uses: ./.github/actions/install-linux-sgx + with: + os: ${{ matrix.os }} + + - name: Build iwasm for testing samples + run: | + mkdir build && cd build + cmake .. ${{ matrix.iwasm_make_options_run_mode }} ${{ matrix.iwasm_make_options_feature }} + cmake --build . --config Release --parallel 4 + cd ../enclave-sample + make + working-directory: product-mini/platforms/${{ matrix.platform }} + + - name: Get LLVM libraries + if: matrix.iwasm_make_options_run_mode == '$AOT_BUILD_OPTIONS' + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + fail-on-cache-miss: true + + - name: Build wamrc only for testing samples in aot mode + if: matrix.iwasm_make_options_run_mode == '$AOT_BUILD_OPTIONS' + run: | + cmake -S . -B build + cmake --build build --config Release --parallel 4 + cp build/wamrc ../product-mini/platforms/${{ matrix.platform }}/enclave-sample + working-directory: wamr-compiler + + - name: Build Sample [file] + run: | + cmake -S . -B build + cmake --build build --config Debug --parallel 4 + cp build/wasm-app/file.wasm ../../product-mini/platforms/${{ matrix.platform }}/enclave-sample + working-directory: samples/file + + - name: Test Sample [file] in non-aot mode + if: matrix.iwasm_make_options_run_mode != '$AOT_BUILD_OPTIONS' + run: | + source /opt/intel/sgxsdk/environment + ./iwasm --dir=. ./file.wasm + working-directory: product-mini/platforms/${{ matrix.platform }}/enclave-sample + + - name: Test Sample [file] in aot mode + if: matrix.iwasm_make_options_run_mode == '$AOT_BUILD_OPTIONS' + run: | + source /opt/intel/sgxsdk/environment + ./wamrc -sgx -o ./file.aot ./file.wasm + ./iwasm --dir=. ./file.aot + working-directory: product-mini/platforms/${{ matrix.platform }}/enclave-sample + + spec_test_default: + needs: [build_iwasm, build_llvm_libraries] + runs-on: ${{ matrix.os }} + strategy: + matrix: + #(workaround) disable "fast-interp" because of SIMDE + running_mode: ["classic-interp", "aot", "fast-jit"] + test_option: + [$DEFAULT_TEST_OPTIONS, $SIMD_TEST_OPTIONS, $XIP_TEST_OPTIONS] + os: [ubuntu-22.04] + exclude: + # classic-interp, fast-interp and fast-jit don't support simd + - running_mode: "classic-interp" + test_option: $SIMD_TEST_OPTIONS + - running_mode: "fast-interp" + test_option: $SIMD_TEST_OPTIONS + - running_mode: "fast-jit" + test_option: $SIMD_TEST_OPTIONS + # classic-interp, fast-interp and fast jit don't support XIP + - running_mode: "classic-interp" + test_option: $XIP_TEST_OPTIONS + - running_mode: "fast-interp" + test_option: $XIP_TEST_OPTIONS + - running_mode: "fast-jit" + test_option: $XIP_TEST_OPTIONS + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries.outputs.cache_key }} + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + if: matrix.running_mode == 'aot' + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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 + uses: ./.github/actions/install-linux-sgx + with: + os: ${{ matrix.os }} + + #workaround about a https://github.com/actions/runner-images/issues/6680#issuecomment-2640923706 + - name: Increase swapfile + run: | + sudo swapoff -a + sudo fallocate -l 15G /swapfile + sudo chmod 600 /swapfile + sudo mkswap /swapfile + sudo swapon /swapfile + sudo swapon --show + + - name: run spec tests + run: | + source /opt/intel/sgxsdk/environment + ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }} + working-directory: ./tests/wamr-test-suites diff --git a/.github/workflows/compilation_on_windows.yml b/.github/workflows/compilation_on_windows.yml new file mode 100644 index 0000000000..56adbe7ef6 --- /dev/null +++ b/.github/workflows/compilation_on_windows.yml @@ -0,0 +1,182 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on windows-2022 + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/compilation_on_windows.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # will be triggered on push events + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/compilation_on_windows.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # allow to be triggered manually + workflow_dispatch: + +env: + # For Spec Test + DEFAULT_TEST_OPTIONS: "-s spec -b" + MULTI_MODULES_TEST_OPTIONS: "-s spec -b -M" + THREADS_TEST_OPTIONS: "-s spec -b -p" + WASI_TEST_OPTIONS: "-s wasi_certification -w" + WASI_TEST_FILTER: ${{ github.workspace }}/product-mini/platforms/windows/wasi_filtered_tests.json + # Used when building the WASI socket and thread tests + CC: ${{ github.workspace }}/wasi-sdk/bin/clang + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + build_llvm_libraries_on_windows: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "windows-2022" + arch: "AArch64 ARM Mips RISCV X86" + + build_iwasm: + runs-on: windows-2022 + strategy: + matrix: + build_options: + [ + "-DWAMR_BUILD_AOT=1 -DWAMR_BUILD_INTERP=0", + "-DWAMR_BUILD_AOT=0", + "-DWAMR_BUILD_TAIL_CALL=1", + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1", + "-DWAMR_DISABLE_HW_BOUND_CHECK=1", + "-DWAMR_BUILD_REF_TYPES=1", + "-DWAMR_BUILD_DEBUG_INTERP=1", + "-DWAMR_BUILD_LIB_PTHREAD=1", + "-DWAMR_BUILD_LIB_WASI_THREADS=1", + "-DWAMR_BUILD_LIBC_UVWASI=0 -DWAMR_BUILD_LIBC_WASI=1", + ] + steps: + - uses: actions/checkout@v6.0.2 + + - name: clone uvwasi library + if: ${{ !contains(matrix.build_options, '-DWAMR_BUILD_LIBC_UVWASI=0') }} + run: | + cd core/deps + git clone https://github.com/nodejs/uvwasi.git + - name: Build iwasm + run: | + cd product-mini/platforms/windows + mkdir build && cd build + cmake .. ${{ matrix.build_options }} + cmake --build . --config Release --parallel 4 + + build_wamrc: + needs: [build_llvm_libraries_on_windows] + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: windows-2022 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_windows.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + # 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: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build wamrc + run: | + cmake -S . -B build + cmake --build build --config Release --parallel 4 + working-directory: wamr-compiler + + test: + runs-on: windows-2022 + needs: [build_iwasm, build_wamrc] + strategy: + fail-fast: false + matrix: + running_mode: ["classic-interp", "fast-interp"] + test_option: + [ + $DEFAULT_TEST_OPTIONS, + $MULTI_MODULES_TEST_OPTIONS, + $THREADS_TEST_OPTIONS, + $WASI_TEST_OPTIONS, + ] + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: download and install wasi-sdk + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: | + curl "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0.m-mingw.tar.gz" -o wasi-sdk.tar.gz -L + mkdir wasi-sdk + tar -xzf wasi-sdk.tar.gz -C wasi-sdk --strip-components 1 + + - name: build socket api tests + shell: bash + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: ./build.sh + working-directory: ./core/iwasm/libraries/lib-socket/test/ + + - name: Build WASI thread tests + shell: bash + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: ./build.sh + working-directory: ./core/iwasm/libraries/lib-wasi-threads/test/ + + - name: install wget + shell: bash + run: choco install wget + + - name: run tests + shell: bash + timeout-minutes: 20 + run: ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }} + working-directory: ./tests/wamr-test-suites diff --git a/.github/workflows/compilation_on_zephyr.yml b/.github/workflows/compilation_on_zephyr.yml new file mode 100644 index 0000000000..a98b9a28dd --- /dev/null +++ b/.github/workflows/compilation_on_zephyr.yml @@ -0,0 +1,143 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: compilation on zephyr + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/compilation_on_zephyr.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/platforms/common/**" + - "product-mini/platforms/zephyr/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # will be triggered on push events + push: + branches: + - main + - "dev/**" + paths: + - ".github/workflows/compilation_on_zephyr.yml" + - "build-scripts/**" + - "core/**" + - "!core/deps/**" + - "product-mini/platforms/common/**" + - "product-mini/platforms/zephyr/**" + - "samples/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # FOR SETUP + ZEPHYR_SDK_VERSION: "0.16.9" + ZEPHYR_VERSION: "v3.7.0" + # FOR BUILD + +permissions: + contents: read + +jobs: + smoke_test: + runs-on: ubuntu-22.04 + container: + # For Zephyr 3.7 LTS, use the v0.26-branch or the latest v0.26.x release Docker image. + # ci require a larger runner to avoid "no space left on device" + image: ghcr.io/zephyrproject-rtos/ci-base:v0.26-branch + options: --user root + + steps: + # https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-workspace-application + # zephyrproject/ --> CI ROOT + # ├─── .west/ + # │ └─── config + # ├─── bootloader/ + # ├─── zephyr/ --> Zephyr source code + # ├─── zephyr-sdk/ + # ├─── modules/ + # │ |─── wasm-micro-runtime --> WAMR source code + # ├─── tools/ + # ├─── vendor/ + # └─── application/ --> DUMMY. keep west_lite.yml here + + - name: Checkout code + uses: actions/checkout@v6.0.2 + with: + path: modules/wasm-micro-runtime + + - name: Prepare Zephyr environment + shell: bash + run: | + mkdir -p application + cp modules/wasm-micro-runtime/product-mini/platforms/zephyr/simple/west_lite.yml application/west_lite.yml + + - name: Setup Zephyr project + uses: zephyrproject-rtos/action-zephyr-setup@v1 + with: + app-path: application + manifest-file-name: west_lite.yml + sdk-version: ${{ env.ZEPHYR_SDK_VERSION }} + toolchains: arc-zephyr-elf:arc64-zephyr-elf + + - name: Build a sample application(simple) + shell: bash + run: | + pushd product-mini/platforms/zephyr/simple + west build . -b qemu_arc/qemu_arc_hs -p always -- -DWAMR_BUILD_TARGET=ARC + popd + + # west build -t run will fork several processes, which will cause the job to hang. + # run in the background and kill it after 5 seconds + .github/scripts/run_qemu_arc.sh \ + ../../zephyr-sdk \ + product-mini/platforms/zephyr/simple/build/zephyr/zephyr.elf & + sleep 5 + pkill qemu-system-arc + working-directory: modules/wasm-micro-runtime + + - name: Build a sample application(user-mode) + shell: bash + run: | + pushd product-mini/platforms/zephyr/user-mode + west build . -b qemu_arc/qemu_arc_hs -p always -- -DWAMR_BUILD_TARGET=ARC + popd + + # west build -t run will fork several processes, which will cause the job to hang. + # run in the background and kill it after 5 seconds + .github/scripts/run_qemu_arc.sh \ + ../../zephyr-sdk \ + product-mini/platforms/zephyr/user-mode/build/zephyr/zephyr.elf & + sleep 5 + pkill qemu-system-arc + working-directory: modules/wasm-micro-runtime + + - name: Build a sample application(user-mode, prebuilt library approach) + shell: bash + run: | + pushd product-mini/platforms/zephyr/user-mode + west build . -b qemu_arc/qemu_arc_hs -p always -- -DWAMR_BUILD_TARGET=ARC -DWAMR_USE_PREBUILT_LIB=1 + popd + + .github/scripts/run_qemu_arc.sh \ + ../../zephyr-sdk \ + product-mini/platforms/zephyr/user-mode/build/zephyr/zephyr.elf & + sleep 5 + pkill qemu-system-arc + working-directory: modules/wasm-micro-runtime diff --git a/.github/workflows/create_tag.yml b/.github/workflows/create_tag.yml new file mode 100644 index 0000000000..2ca4e8283a --- /dev/null +++ b/.github/workflows/create_tag.yml @@ -0,0 +1,87 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: create a tag + +on: + workflow_call: + outputs: + minor_version: + description: "the new version is a minor version or a major version" + value: ${{ jobs.create_tag.outputs.minor_version}} + new_ver: + description: "the new version" + value: ${{ jobs.create_tag.outputs.new_ver}} + new_tag: + description: "the new tag just created" + value: ${{ jobs.create_tag.outputs.new_tag}} + +permissions: + contents: read + +jobs: + create_tag: + runs-on: ubuntu-latest + outputs: + minor_version: ${{ steps.preparation.outputs.minor_version }} + new_ver: ${{ steps.preparation.outputs.new_ver }} + new_tag: ${{ steps.preparation.outputs.new_tag }} + permissions: + contents: write # create and push tags + + steps: + - uses: actions/checkout@v6.0.2 + # Full git history is needed to get a proper list of commits and tags + with: + fetch-depth: 0 + + - name: prepare + id: preparation + run: | + # show latest 3 versions on the branch that create release + # Set the initial commit to the head of the branch + commit="HEAD" + # + # Loop to get the three most recent tags + for i in {1..3} + do + # Get the most recent tag reachable from the current commit + tag=$(git describe --tags --abbrev=0 $commit) + + # Print the tag + echo "$tag" + + # Move to the commit before the found tag to find the next tag in the next iteration + commit=$(git rev-list -n 1 $tag^) + done + # compare latest git tag and semantic version definition + result=$(python3 ./.github/scripts/fetch_and_compare_version.py) + echo "script result is ${result}" + # + # return in a form like "WAMR-X.Y.Z,major_minor_change" or ",patch_change" + new_ver=$(echo "${result}" | awk -F',' '{print $1}') + diff_versioning=$(echo "${result}" | awk -F',' '{print $2}') + echo "next version is ${new_ver}, it ${diff_versioning}" + # + # set output + if [[ ${diff_versioning} == 'major_minor_change' ]];then + echo "minor_version=true" >> "$GITHUB_OUTPUT" + else + echo "minor_version=false" >> "$GITHUB_OUTPUT" + fi + # + # + if [[ -z ${new_ver} ]]; then + echo "::error::please indicate the right semantic version in core/version.h" + echo "new_ver=''" >> "$GITHUB_OUTPUT" + echo "new_tag=''" >> "$GITHUB_OUTPUT" + exit 1 + else + echo "new_ver=${new_ver}" >> "$GITHUB_OUTPUT" + echo "new_tag=WAMR-${new_ver}" >> "$GITHUB_OUTPUT" + fi + + - name: push tag + if: steps.preparation.outputs.new_tag != '' + run: | + git tag ${{ steps.preparation.outputs.new_tag }} + git push origin --force --tags diff --git a/.github/workflows/hadolint_dockerfiles.yml b/.github/workflows/hadolint_dockerfiles.yml new file mode 100644 index 0000000000..8f4051f86b --- /dev/null +++ b/.github/workflows/hadolint_dockerfiles.yml @@ -0,0 +1,50 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: hadolint dockerfiles + +on: + # will be triggered on PR events + pull_request: + types: + - opened + - synchronize + paths: + - "**/Dockerfile*" + - ".github/workflows/hadolint_dockerfiles.yml" + push: + branches: + - main + - "dev/**" + paths: + - "**/Dockerfile*" + - ".github/workflows/hadolint_dockerfiles.yml" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + run-hadolint-on-dockerfiles: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v6.0.2 + + # on default, hadolint will fail on warnings and errors + - name: Run hadolint on dockerfiles + run: | + docker pull hadolint/hadolint:latest-debian + find . -name "*Dockerfile*" | while read dockerfile; do + echo "run hadolint on $dockerfile:" + docker run --rm -i hadolint/hadolint:latest-debian hadolint - <"$dockerfile" + echo "successful" + done \ No newline at end of file diff --git a/.github/workflows/nightly_run.yml b/.github/workflows/nightly_run.yml new file mode 100644 index 0000000000..15b58f7d5d --- /dev/null +++ b/.github/workflows/nightly_run.yml @@ -0,0 +1,813 @@ +# Copyright (C) 2023 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: nightly_run + +on: + pull_request: + types: + - opened + - synchronize + # running nightly pipeline if you're changing it + # stress tests are run only in nightly at the moment, so running them in they are changed + paths: + - ".github/workflows/nightly_run.yml" + - "core/iwasm/libraries/lib-wasi-threads/stress-test/**" + + # midnight UTC + schedule: + - cron: "0 0 * * *" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + # For BUILD + AOT_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + CLASSIC_INTERP_BUILD_OPTIONS: "-DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=0 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + FAST_INTERP_BUILD_OPTIONS: " -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_JIT=0 -DWAMR_BUILD_JIT=0 -DWAMR_BUILD_LAZY_JIT=0" + 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" + 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" + EXTENDED_CONST_EXPR_TEST_OPTIONS: "-s spec -b -P -N" + MULTI_MODULES_TEST_OPTIONS: "-s spec -b -P -M" + SIMD_TEST_OPTIONS: "-s spec -b -P -S" + THREADS_TEST_OPTIONS: "-s spec -b -P -p" + X86_32_TARGET_TEST_OPTIONS: "-m x86_32" + WASI_TEST_OPTIONS: "-s wasi_certification -w" + +permissions: + contents: read + +jobs: + build_llvm_libraries_on_ubuntu: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "X86" + + build_wamrc: + needs: build_llvm_libraries_on_ubuntu + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + # 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: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + build_iwasm: + needs: build_llvm_libraries_on_ubuntu + runs-on: ${{ matrix.os }} + strategy: + matrix: + make_options_run_mode: [ + # Running mode + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + $LLVM_LAZY_JIT_BUILD_OPTIONS, + $LLVM_EAGER_JIT_BUILD_OPTIONS, + $MULTI_TIER_JIT_BUILD_OPTIONS, + ] + make_options_feature: [ + # Features + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1", + "-DWAMR_BUILD_DEBUG_AOT=1", + "-DWAMR_BUILD_DEBUG_INTERP=1", + "-DWAMR_BUILD_DUMP_CALL_STACK=1", + "-DWAMR_BUILD_LIB_PTHREAD=1", + "-DWAMR_BUILD_LIB_WASI_THREADS=1", + "-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1", + "-DWAMR_BUILD_MINI_LOADER=1", + "-DWAMR_BUILD_MEMORY_PROFILING=1", + "-DWAMR_BUILD_MULTI_MODULE=1", + "-DWAMR_BUILD_PERF_PROFILING=1", + "-DWAMR_BUILD_TAIL_CALL=1", + "-DWAMR_DISABLE_HW_BOUND_CHECK=1", + "-DWAMR_BUILD_MEMORY64=1", + "-DWAMR_BUILD_MULTI_MEMORY=1", + "-DWAMR_BUILD_SHARED=1", + "-DWAMR_BUILD_EXTENDED_CONST_EXPR=1", + ] + os: [ubuntu-22.04] + platform: [android, linux] + exclude: + # incompatible feature and platform + # incompatible mode and feature + # MULTI_MODULE only on INTERP mode and AOT mode + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MODULE=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + # DEBUG_INTERP only on CLASSIC INTERP mode + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + # TODO: DEBUG_AOT on JIT + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + 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" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + # Memory64 only on CLASSIC INTERP and AOT mode, and only on 64-bit platform + - make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + platform: android + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + # Multi memory only on CLASSIC INTERP mode, and only on 64-bit platform + - make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + platform: android + - make_options_run_mode: $AOT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + # Fast-JIT and Multi-Tier-JIT mode don't support android + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + platform: android + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + platform: android + # LLVM JIT pre-built binary wasn't compiled by Android NDK + # and isn't available for android + - make_options_run_mode: $LLVM_LAZY_JIT_BUILD_OPTIONS + platform: android + - make_options_run_mode: $LLVM_EAGER_JIT_BUILD_OPTIONS + platform: android + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu.outputs.cache_key }} + # classic interp doesn't support SIMD + - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # fast jit doesn't support SIMD + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # multi-tier jit doesn't support SIMD + - make_options_run_mode: $MULTI_TIER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + # only download llvm cache when needed + - name: Get LLVM libraries + id: retrieve_llvm_libs + if: endsWith(matrix.make_options_run_mode, '_JIT_BUILD_OPTIONS') + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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 for linux + if: matrix.platform == 'linux' + run: | + mkdir build && cd build + cmake .. ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} ${{ matrix.extra_options }} + cmake --build . --config Release --parallel 4 + working-directory: product-mini/platforms/${{ matrix.platform }} + + - name: Build iwasm for android + if: matrix.platform == 'android' + run: | + mkdir build && cd build + cmake .. ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} ${{ matrix.extra_options }} \ + -DWAMR_BUILD_TARGET=X86_64 + cmake --build . --config Release --parallel 4 + working-directory: product-mini/platforms/${{ matrix.platform }} + + build_unit_tests: + needs: [build_llvm_libraries_on_ubuntu, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04] + build_target: ["X86_64", "X86_32"] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu.outputs.cache_key }} + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + with: + submodules: recursive + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + if: (steps.retrieve_llvm_libs.outputs.cache-hit != 'true') + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamrc + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Install dependencies for X86_32 + if: matrix.build_target == 'X86_32' + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y g++-multilib libzstd-dev:i386 zlib1g-dev:i386 + + - name: Build and run unit tests + run: | + mkdir build && cd build + cmake .. -DWAMR_BUILD_TARGET=${{ matrix.build_target }} -DFULL_TEST=ON + cmake --build . --parallel 4 + ctest --output-on-failure + working-directory: tests/unit + + build_iwasm_linux_gcc4_8: + runs-on: ubuntu-latest + container: + image: ubuntu:14.04 + strategy: + matrix: + make_options_run_mode: [ + # Running mode + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + ] + make_options_feature: [ + # Features + "-DWAMR_BUILD_CUSTOM_NAME_SECTION=1", + "-DWAMR_BUILD_DEBUG_AOT=1", + "-DWAMR_BUILD_DEBUG_INTERP=1", + "-DWAMR_BUILD_DUMP_CALL_STACK=1", + "-DWAMR_BUILD_LIB_PTHREAD=1", + "-DWAMR_BUILD_LIB_WASI_THREADS=1", + "-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1", + "-DWAMR_BUILD_MINI_LOADER=1", + "-DWAMR_BUILD_MEMORY_PROFILING=1", + "-DWAMR_BUILD_MULTI_MODULE=1", + "-DWAMR_BUILD_PERF_PROFILING=1", + "-DWAMR_BUILD_TAIL_CALL=1", + "-DWAMR_DISABLE_HW_BOUND_CHECK=1", + "-DWAMR_BUILD_MEMORY64=1", + "-DWAMR_BUILD_MULTI_MEMORY=1", + ] + exclude: + # incompatible feature and platform + # incompatible mode and feature + # MULTI_MODULE only on INTERP mode and AOT mode + - make_options_run_mode: $FAST_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" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_SIMD=1" + # DEBUG_INTERP only on CLASSIC INTERP mode + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_INTERP=1" + - make_options_run_mode: $FAST_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" + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + # TODO: DEBUG_AOT on JIT + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_DEBUG_AOT=1" + # MINI_LOADER only on INTERP mode + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MINI_LOADER=1" + # Memory64 only on CLASSIC INTERP mode + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MEMORY64=1" + # Memory64 only on CLASSIC INTERP mode + - make_options_run_mode: $FAST_INTERP_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + make_options_feature: "-DWAMR_BUILD_MULTI_MEMORY=1" + include: + # classic interp doesn't support SIMD + - make_options_run_mode: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # fast jit doesn't support SIMD + - make_options_run_mode: $FAST_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + steps: + - name: Install dependencies + run: | + apt update && apt install -y make g++-4.8 gcc-4.8 wget git + + - name: checkout + run: | + git clone https://github.com/${{ github.repository }} wamr + + - name: Install cmake + run: | + wget https://github.com/Kitware/CMake/releases/download/v3.26.1/cmake-3.26.1-linux-x86_64.tar.gz -O cmake.tar.gz + tar xzf cmake.tar.gz + cp cmake-3.26.1-linux-x86_64/bin/cmake /usr/local/bin + cp -r cmake-3.26.1-linux-x86_64/share/cmake-3.26/ /usr/local/share/ + - name: Build iwasm + run: | + mkdir build && cd build + cmake .. ${{ matrix.make_options_run_mode }} ${{ matrix.make_options_feature }} ${{ matrix.extra_options }} -DCMAKE_C_COMPILER=gcc-4.8 -DCMAKE_CXX_COMPILER=g++-4.8 + cmake --build . --config Release --parallel 4 + working-directory: wamr/product-mini/platforms/linux + + build_samples_wasm_c_api: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + sanitizer: ["", "ubsan", "asan"] + make_options: [ + # Running mode + $AOT_BUILD_OPTIONS, + $CLASSIC_INTERP_BUILD_OPTIONS, + $FAST_INTERP_BUILD_OPTIONS, + $FAST_JIT_BUILD_OPTIONS, + $LLVM_LAZY_JIT_BUILD_OPTIONS, + $LLVM_EAGER_JIT_BUILD_OPTIONS, + $MULTI_TIER_JIT_BUILD_OPTIONS, + ] + os: [ubuntu-22.04] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu.outputs.cache_key }} + # classic interp doesn't support SIMD + - make_options: $CLASSIC_INTERP_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0" + # fast jit doesn't support Multi-module and SIMD + - make_options: $FAST_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0 -DWAMR_BUILD_MULTI_MODULE=0" + # multi-tier jit doesn't support Multi-module and SIMD + - make_options: $MULTI_TIER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_SIMD=0 -DWAMR_BUILD_MULTI_MODULE=0" + # LLVM JIT doesn't support Multi-module + - make_options: $LLVM_LAZY_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_MULTI_MODULE=0" + - make_options: $LLVM_EAGER_JIT_BUILD_OPTIONS + extra_options: "-DWAMR_BUILD_MULTI_MODULE=0" + exclude: + - make_options: $MULTI_TIER_JIT_BUILD_OPTIONS + sanitizer: asan + + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: Get LLVM libraries + id: retrieve_llvm_libs + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamrc + if: (!endsWith(matrix.make_options, '_INTERP_BUILD_OPTIONS')) + run: | + mkdir build && cd build + cmake -D WAMR_BUILD_SANITIZER="${{matrix.sanitizer}}" .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Build Sample [wasm-c-api] + run: | + VERBOSE=1 + cmake -S . -B build ${{ matrix.make_options }} ${{ matrix.extra_options }} \ + -D WAMR_BUILD_SANITIZER="${{matrix.sanitizer}}" \ + -D WAMR_BUILD_QUICK_AOT_ENTRY=0 + cmake --build build --config Release --parallel 4 + ctest --test-dir build --output-on-failure + working-directory: samples/wasm-c-api + + build_samples_others: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu.outputs.cache_key }} + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Get LLVM libraries + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Build wamrc + run: | + mkdir build && cd build + cmake -D WAMR_BUILD_SANITIZER="${{matrix.sanitizer}}" .. + cmake --build . --config Release --parallel 4 + working-directory: wamr-compiler + + - name: Build Sample [basic] + run: | + cd samples/basic + ./build.sh + ./run.sh + - name: Build Sample [file] + run: | + cd samples/file + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./src/iwasm -f wasm-app/file.wasm -d . + - name: Build Sample [multi-thread] + run: | + cd samples/multi-thread + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/test.wasm + - name: Build Sample [multi-module] + run: | + cd samples/multi-module + mkdir build && cd build + cmake .. -DWAMR_BUILD_AOT=1 + cmake --build . --config Release --parallel 4 + ./multi_module mC.wasm + ./multi_module mC.aot + - name: Build Sample [spawn-thread] + run: | + cd samples/spawn-thread + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./spawn_thread + - name: Build Sample [ref-types] + run: | + cd samples/ref-types + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./hello + + - name: Build Sample [wasi-threads] + run: | + cd samples/wasi-threads + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./iwasm wasm-apps/no_pthread.wasm + + - name: Build Sample [shared-module] + run: | + cd samples/shared-module + ./build.sh + ./run.sh + + - name: Build Sample [terminate] + run: | + cd samples/terminate + ./build.sh + ./run.sh + + - name: Build Sample [native-stack-overflow] + run: | + cd samples/native-stack-overflow + ./build.sh + ./run.sh test1 + ./run.sh test2 + + - name: Build Sample [native-lib] + run: | + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./iwasm --native-lib=./libtest_add.so --native-lib=./libtest_sqrt.so --native-lib=./libtest_hello.so --native-lib=./libtest_hello2.so wasm-app/test.wasm + working-directory: ./samples/native-lib + + # FIXME: un-comment me after fix cmake minimum issue + # https://github.com/bytecodealliance/wamr-app-framework/pull/11 + # - name: checkout wamr-app-framework + # run: git clone https://github.com/bytecodealliance/wamr-app-framework.git + + # - name: download wamr-app-framework dependencies + # run: LVGL=0 LV_DRIVERS=0 ./download.sh + # working-directory: ./wamr-app-framework/deps + + # - name: Build Sample [simple] + # run: | + # ./build.sh -p host-interp + # python3 ./sample_test_run.py $(pwd)/out + # exit $? + # working-directory: ./wamr-app-framework/samples/simple + + - name: Build Sample [shared-heap] + run: | + cd samples/shared-heap + mkdir build && cd build + cmake .. + cmake --build . --config Debug --parallel 4 + ./shared_heap_test + ./shared_heap_test --aot + + - name: Build Sample [import-func-callback] + run: | + cd samples/import-func-callback + mkdir build && cd build + cmake .. + cmake --build . --config Release --parallel 4 + ./import-func-callback + + - name: Build Sample [custom_section] + run: | + cd samples/custom-section + ./build.sh + ./run.sh + ./build.sh --aot + ./run.sh --aot + + test: + needs: [build_iwasm, build_llvm_libraries_on_ubuntu, build_wamrc] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04] + sanitizer: ["", "ubsan", "asan", "tsan"] + running_mode: + [ + "classic-interp", + "fast-interp", + "jit", + "aot", + "fast-jit", + "multi-tier-jit", + ] + test_option: + [ + $DEFAULT_TEST_OPTIONS, + $MULTI_MODULES_TEST_OPTIONS, + $SIMD_TEST_OPTIONS, + $EXTENDED_CONST_EXPR_TEST_OPTIONS, + $THREADS_TEST_OPTIONS, + $WASI_TEST_OPTIONS, + ] + include: + - os: ubuntu-22.04 + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu.outputs.cache_key }} + ubuntu_version: "22.04" + + exclude: + # asan works only for aot now + - running_mode: "classic-interp" + sanitizer: asan + - running_mode: "fast-interp" + sanitizer: asan + - running_mode: "jit" + sanitizer: asan + - running_mode: "fast-jit" + sanitizer: asan + - running_mode: "multi-tier-jit" + sanitizer: asan + - running_mode: "classic-interp" + sanitizer: tsan + - running_mode: "jit" + sanitizer: tsan + - running_mode: "fast-jit" + sanitizer: tsan + - running_mode: "multi-tier-jit" + sanitizer: tsan + # simd128.h brings ubsan errors + # like: negation of XXXcannot be represented in type 'long int'; + # cast to an unsigned type to negate this value to itself + - running_mode: "fast-interp" + sanitizer: ubsan + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install-wasi-sdk-wabt + if: matrix.test_option == '$WASI_TEST_OPTIONS' + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: set env variable(if llvm are used) + 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.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 + + - name: set additional tsan options + run: | + echo "TSAN_OPTIONS=suppressions=$PWD/tsan_suppressions.txt" >> $GITHUB_ENV + sudo sysctl vm.mmap_rnd_bits=28 + working-directory: tests/wamr-test-suites + + #only download llvm libraries in jit and aot mode + - name: Get LLVM libraries + if: env.USE_LLVM == 'true' + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.llvm_cache_key }} + + - name: Quit if cache miss + 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: 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: install for wabt compilation + run: sudo apt update && sudo apt install -y ninja-build + + - name: Build WASI thread tests + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: bash build.sh + working-directory: ./core/iwasm/libraries/lib-wasi-threads/test/ + + - name: Build WASI thread stress tests + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: bash build.sh + working-directory: ./core/iwasm/libraries/lib-wasi-threads/stress-test/ + + - name: build socket api tests + if: matrix.test_option == '$WASI_TEST_OPTIONS' + run: bash build.sh + working-directory: ./core/iwasm/libraries/lib-socket/test/ + + - name: run tests + timeout-minutes: 40 + run: ./test_wamr.sh ${{ matrix.test_option }} -t ${{ matrix.running_mode }} -T "${{ matrix.sanitizer }}" + working-directory: ./tests/wamr-test-suites + + #only install x32 support libraries when to run x86_32 cases + - name: install x32 support libraries + if: env.TEST_ON_X86_32 == 'true' + run: + # Add another apt repository as some packages cannot + # be downloaded with the github default repository + sudo curl -sSL https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc && + sudo apt-add-repository https://packages.microsoft.com/ubuntu/${{ matrix.ubuntu_version }}/prod && + sudo apt-get update && + sudo apt install -y g++-multilib lib32gcc-9-dev + + - name: run tests x86_32 + timeout-minutes: 40 + 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/release_process.yml b/.github/workflows/release_process.yml new file mode 100644 index 0000000000..b7eebd1c52 --- /dev/null +++ b/.github/workflows/release_process.yml @@ -0,0 +1,279 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: the binary release processes + +on: + workflow_dispatch: + inputs: + require_confirmation: + description: "If the process requires a confirmation" + type: boolean + required: false + default: false + release_wamr_sdk: + description: "If the WAMR SDK in the release" + type: boolean + required: false + default: false + release_wamr_lldb: + description: "If the WAMR LLDB in the release" + type: boolean + required: false + default: false + release_wamr_ide_vscode_ext: + description: "If the WAMR VSCode extension in the release" + type: boolean + required: false + default: false + release_wamr_wasi_ext: + description: "If the WAMR WASI extensions in the release" + type: boolean + required: false + default: true + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + create_tag: + permissions: + contents: write # create and push tags + uses: ./.github/workflows/create_tag.yml + + create_release: + permissions: + contents: write # create release + needs: [create_tag] + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.create_release.outputs.upload_url }} + steps: + - uses: actions/checkout@v6.0.2 + + - name: prepare the release note + run: | + extract_result="$(python3 ./.github/scripts/extract_from_release_notes.py RELEASE_NOTES.md)" + echo "RELEASE_NOTE<> $GITHUB_ENV + echo "${extract_result}" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: create a release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ needs.create_tag.outputs.new_tag }} + release_name: ${{ needs.create_tag.outputs.new_tag }} + prerelease: ${{ inputs.require_confirmation || needs.create_tag.outputs.minor_version }} + draft: false + body: ${{ env.RELEASE_NOTE }} + + # + # LLVM_LIBRARIES + build_llvm_libraries_on_ubuntu_2204: + permissions: + contents: read + actions: write + needs: [create_tag, create_release] + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "AArch64 ARM Mips RISCV X86" + + #CLARIFY: Require to build LLVM libraries on ARM macOS? + build_llvm_libraries_on_macos: + permissions: + contents: read + actions: write + needs: [create_tag, create_release] + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "macos-15-intel" + arch: "AArch64 ARM Mips RISCV X86" + + build_llvm_libraries_on_windows: + permissions: + contents: read + actions: write + needs: [create_tag, create_release] + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "windows-2022" + arch: "AArch64 ARM Mips RISCV X86" + + # + # WAMRC + release_wamrc_on_ubuntu_2204: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release, build_llvm_libraries_on_ubuntu_2204] + uses: ./.github/workflows/build_wamrc.yml + with: + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + release: true + runner: ubuntu-22.04 + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver }} + + #CLARIFY: Require to release wamrc on ARM macOS? + release_wamrc_on_ubuntu_macos: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release, build_llvm_libraries_on_macos] + uses: ./.github/workflows/build_wamrc.yml + with: + llvm_cache_key: ${{ needs.build_llvm_libraries_on_macos.outputs.cache_key }} + release: true + runner: macos-15-intel + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver }} + + release_wamrc_on_windows: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release, build_llvm_libraries_on_windows] + uses: ./.github/workflows/build_wamrc.yml + with: + llvm_cache_key: ${{ needs.build_llvm_libraries_on_windows.outputs.cache_key }} + release: true + runner: windows-2022 + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver }} + + # + # IWASM + release_iwasm_on_ubuntu_2204: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release, build_llvm_libraries_on_ubuntu_2204] + uses: ./.github/workflows/build_iwasm_release.yml + with: + cwd: product-mini/platforms/linux + llvm_cache_key: ${{ needs.build_llvm_libraries_on_ubuntu_2204.outputs.cache_key }} + runner: ubuntu-22.04 + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver}} + + #CLARIFY: Require to release iwasm on ARM macOS? + release_iwasm_on_macos: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release, build_llvm_libraries_on_macos] + uses: ./.github/workflows/build_iwasm_release.yml + with: + cwd: product-mini/platforms/darwin + llvm_cache_key: ${{ needs.build_llvm_libraries_on_macos.outputs.cache_key }} + runner: macos-15-intel + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver}} + + release_iwasm_on_windows: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release, build_llvm_libraries_on_windows] + uses: ./.github/workflows/build_iwasm_release.yml + with: + cwd: product-mini/platforms/windows + llvm_cache_key: ${{ needs.build_llvm_libraries_on_windows.outputs.cache_key }} + runner: windows-2022 + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver}} + + # + # WAMR_SDK + release_wamr_sdk_on_ubuntu_2204: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_sdk }} + uses: ./.github/workflows/build_wamr_sdk.yml + with: + config_file: wamr_config_ubuntu_release.cmake + 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-19/wasi-sdk-19.0-linux.tar.gz + wamr_app_framework_url: https://github.com/bytecodealliance/wamr-app-framework.git + + #CLARIFY: Require to release WAMR SDK on ARM macOS? + release_wamr_sdk_on_macos: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_sdk }} + uses: ./.github/workflows/build_wamr_sdk.yml + with: + config_file: wamr_config_macos_release.cmake + runner: macos-15-intel + 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-19/wasi-sdk-19.0-macos.tar.gz + wamr_app_framework_url: https://github.com/bytecodealliance/wamr-app-framework.git + + # vscode extension cross-platform + release_wamr_ide_vscode_ext: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_ide_vscode_ext}} + 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 }} + + # + # vscode extension docker images package + release_wamr_ide_docker_images_package: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_ide_vscode_ext}} + uses: ./.github/workflows/build_docker_images.yml + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver }} + + # + # WAMR_LLDB + release_wamr_lldb_on_ubuntu_2204: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_lldb }} + uses: ./.github/workflows/build_wamr_lldb.yml + with: + runner: ubuntu-22.04 + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver}} + + #CLARIFY: Require to release WAMR LLDB on ARM macOS? + release_wamr_lldb_on_macos_universal: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_lldb }} + uses: ./.github/workflows/build_wamr_lldb.yml + with: + runner: macos-15-intel + arch: universal + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver}} + + release_wamr_wasi_extensions: + permissions: + contents: write # upload release artifact + needs: [create_tag, create_release] + if: ${{ inputs.release_wamr_wasi_ext }} + uses: ./.github/workflows/build_wamr_wasi_extensions.yml + with: + upload_url: ${{ needs.create_release.outputs.upload_url }} + ver_num: ${{ needs.create_tag.outputs.new_ver }} diff --git a/.github/workflows/reuse_latest_release_binaries.yml b/.github/workflows/reuse_latest_release_binaries.yml new file mode 100644 index 0000000000..3bbd4ac9bf --- /dev/null +++ b/.github/workflows/reuse_latest_release_binaries.yml @@ -0,0 +1,74 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +name: reuse binaries of the latest release if no more modification on the_path since last_commit + +on: + workflow_call: + inputs: + binary_name_stem: + type: string + required: true + last_commit: + type: string + required: true + the_path: + type: string + required: true + upload_url: + description: upload binary assets to the URL of release + type: string + required: true + outputs: + result: + value: ${{ jobs.build.outputs.result }} + +permissions: + contents: read + +jobs: + reuse: + runs-on: ubuntu-latest + outputs: + result: ${{ steps.try_reuse.outputs.result }} + permissions: + contents: write # for creating realease and uploading release artifacts + + steps: + - uses: actions/checkout@v6.0.2 + # Full git history is needed to get a proper list of commits and tags + with: + fetch-depth: 0 + + - name: try to reuse binaries + id: try_reuse + run: | + echo '::echo::on' + python3 ./.github/scripts/reuse_latest_release_binaries.py \ + --binary_name_stem ${{ inputs.binary_name_stem }} \ + --last_commit ${{ inputs.last_commit }} \ + --the_path ${{ inputs.the_path }} . + ls -lh . + + - run: echo ${{ steps.try_reuse.outputs.result }} + + - name: upload release tar.gz + if: steps.try_reuse.outputs.result == 'hit' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: ${{ inputs.binary_name_stem }}.tar.gz + asset_name: ${{ inputs.binary_name_stem }}.tar.gz + asset_content_type: application/x-gzip + + - name: upload release zip + if: steps.try_reuse.outputs.result == 'hit' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ inputs.upload_url }} + asset_path: ${{ inputs.binary_name_stem }}.zip + asset_name: ${{ inputs.binary_name_stem }}.zip + asset_content_type: application/zip diff --git a/.github/workflows/spec_test_on_nuttx.yml b/.github/workflows/spec_test_on_nuttx.yml new file mode 100644 index 0000000000..baf4fb3e11 --- /dev/null +++ b/.github/workflows/spec_test_on_nuttx.yml @@ -0,0 +1,335 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: spec test on nuttx + +on: + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/spec_test_on_nuttx.yml" + - "core/**" + - "!core/deps/**" + - "product-mini/**" + - "!samples/workload/**" + - "tests/wamr-test-suites/**" + - "wamr-compiler/**" + - "wamr-sdk/**" + schedule: + - cron: '0 0 * * *' + + workflow_dispatch: + +# Note on INTERPRETERS_WAMR_STACK_GUARD_SIZE: +# https://github.com/apache/nuttx-apps/pull/2241 is not included in +# releases/12.4 branch as of writing this. +env: + LLVM_CACHE_SUFFIX: "build-llvm_libraries_ex" + WASI_SDK_PATH: "/opt/wasi-sdk" + +permissions: + contents: read + +jobs: + build_llvm_libraries: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "ARM RISCV AArch64" + container_image: ghcr.io/no1wudi/nuttx/apache-nuttx-ci-linux@sha256:8c4e00b607d4d6d66ba8f51c4544819a616eac69d3a2ac669e2af2150e2eb0f9 + + build_llvm_libraries_xtensa: + permissions: + contents: read + actions: write + uses: ./.github/workflows/build_llvm_libraries.yml + with: + os: "ubuntu-22.04" + arch: "Xtensa" + extra_build_llvm_options: "--platform xtensa" + cache_key_suffix: "-xtensa" + container_image: ghcr.io/no1wudi/nuttx/apache-nuttx-ci-linux@sha256:8c4e00b607d4d6d66ba8f51c4544819a616eac69d3a2ac669e2af2150e2eb0f9 + + spec_test_on_qemu: + runs-on: ubuntu-latest + needs: [build_llvm_libraries, build_llvm_libraries_xtensa] + container: + image: ghcr.io/no1wudi/nuttx/apache-nuttx-ci-linux@sha256:8c4e00b607d4d6d66ba8f51c4544819a616eac69d3a2ac669e2af2150e2eb0f9 + strategy: + matrix: + target_config: [ + # { + # config: "boards/arm64/qemu/qemu-armv8a/configs/nsh", + # target: "aarch64_vfp", + # fpu_type: "fp" + # }, + # { + # config: "boards/arm/imx6/sabre-6quad/configs/nsh", + # target: "thumbv7", + # fpu_type: "none" + # }, + { + config: "boards/arm/imx6/sabre-6quad/configs/nsh", + target: "thumbv7_vfp", + fpu_type: "dp" + }, + { + config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh", + target: "riscv32", + fpu_type: "none" + }, + #{ + # config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh", + # target: "riscv32_ilp32f", + # fpu_type: "fp" + #}, + # { + # config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh", + # target: "riscv32_ilp32d", + # fpu_type: "dp" + # }, + { + config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh64", + target: "riscv64", + fpu_type: "none" + }, + { + config: "boards/xtensa/esp32s3/esp32s3-devkit/configs/qemu_debug", + target: "xtensa", + fpu_type: "none" + }, + ] + + wamr_test_option: [ + { + mode: "-t aot", + option: "CONFIG_INTERPRETERS_WAMR_AOT" + }, + { + mode: "-t aot -X", + option: "CONFIG_INTERPRETERS_WAMR_AOT" + }, + # { + # mode: "-t classic-interp", + # option: "CONFIG_INTERPRETERS_WAMR_CLASSIC" + # }, + # { + # mode: "-t fast-interp", + # option: "CONFIG_INTERPRETERS_WAMR_FAST" + # }, + ] + + wamr_feature_option: + # Empty option for default + - { option: "", mode: "" } + # need to install menhir + # - { option: "CONFIG_INTERPRETERS_WAMR_GC CONFIG_INTERPRETERS_WAMR_AOT_STACK_FRAME", mode: "-G" } + + exclude: + # XIP is not fully supported yet on RISCV64, some relocations can not be resolved + - target_config: { config: "boards/risc-v/qemu-rv/rv-virt/configs/nsh64" } + wamr_test_option: { mode: "-t aot -X" } + + # Our xtensa environment doesn't have enough memory + - target_config: { target: "xtensa" } + wamr_feature_option: { mode: "-G" } + + steps: + # Note: we use an unreleased version nuttx for xtensa because + # 12.4 doesn't contain necessary esp32s3 changes. + - name: Checkout NuttX + uses: actions/checkout@v6.0.2 + with: + repository: apache/nuttx + ref: ${{ matrix.target_config.target == 'xtensa' && '985d395b025cf2012b22f6bb4461959fa6d87645' || '09a71ec7c16c43398d5acbdcbeee7b08736c3170' }} + path: nuttx + + - name: Checkout NuttX Apps + uses: actions/checkout@v6.0.2 + with: + repository: apache/nuttx-apps + ref: ${{ matrix.target_config.target == 'xtensa' && '2ef3eb25c0cec944b13792185f7e5d5a05990d5f' || '6bd593459c4af3cef325c3d22bccd5537a8ed755' }} + path: apps + + - name: Checkout WAMR + uses: actions/checkout@v6.0.2 + with: + repository: ${{ github.repository }} + path: apps/interpreters/wamr/wamr + + - name: Get LLVM libraries + if: contains(matrix.wamr_test_option.mode, 'aot') + id: retrieve_llvm_libs + uses: actions/cache@v5 + with: + path: | + ./core/deps/llvm/build/bin + ./core/deps/llvm/build/include + ./core/deps/llvm/build/lib + ./core/deps/llvm/build/libexec + ./core/deps/llvm/build/share + key: ${{ matrix.target_config.target == 'xtensa' && needs.build_llvm_libraries_xtensa.outputs.cache_key || needs.build_llvm_libraries.outputs.cache_key }} + + - name: Quit if cache miss + if: contains(matrix.wamr_test_option.mode, 'aot') && steps.retrieve_llvm_libs.outputs.cache-hit != 'true' + run: echo "::error::can not get prebuilt llvm libraries" && exit 1 + + - name: Copy LLVM + if: contains(matrix.wamr_test_option.mode, 'aot') + run: cp -r core/deps/llvm apps/interpreters/wamr/wamr/core/deps/llvm + + - name: Build wamrc + if: contains(matrix.wamr_test_option.mode, 'aot') + working-directory: apps/interpreters/wamr/wamr/wamr-compiler + run: | + cmake -B build -DWAMR_BUILD_SHRUNK_MEMORY=0 -S . + cmake --build build + + # the nuttx version we use for xtensa requires esptool.py newer than + # what we have in our version of the apache-nuttx-ci-linux image. + - name: Install the latest esptool.py (xtensa) + if: matrix.target_config.target == 'xtensa' + run: | + pip3 install esptool==4.7.0 + esptool.py version + + - name: Configure NuttX + run: | + tools/configure.sh ${{ matrix.target_config.config }} + working-directory: nuttx + + # depending on configurations, the iwasm command line generated + # by spec-test-script can be longer than the default NSH_LINELEN, + # which is 64 or 80. + - name: Enable WAMR for NuttX + run: | + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_LOG + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_REF_TYPES + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_SHARED_MEMORY + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_BULK_MEMORY + kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE 1024 + kconfig-tweak --enable CONFIG_FS_HOSTFS + kconfig-tweak --enable CONFIG_ARM_SEMIHOSTING_HOSTFS + kconfig-tweak --enable CONFIG_ARM_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + kconfig-tweak --enable CONFIG_RISCV_SEMIHOSTING_HOSTFS + kconfig-tweak --enable CONFIG_RISCV_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + kconfig-tweak --enable CONFIG_XTENSA_SEMIHOSTING_HOSTFS + kconfig-tweak --enable CONFIG_XTENSA_SEMIHOSTING_HOSTFS_CACHE_COHERENCE + kconfig-tweak --enable CONFIG_LIBC_FLOATINGPOINT + kconfig-tweak --set-val CONFIG_NSH_LINELEN 255 + working-directory: nuttx + + - name: Set WAMR stack size for NuttX + if: matrix.target_config.target != 'xtensa' + run: | + kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_STACKSIZE 327680 + working-directory: nuttx + + # because qemu doesn't have a proper emulation of esp32s3 psram, + # we are limited to the internal ram, which is about 400KB. + - name: Set WAMR stack size for NuttX (xtensa) + if: matrix.target_config.target == 'xtensa' + run: | + kconfig-tweak --set-val CONFIG_INTERPRETERS_WAMR_STACKSIZE 25600 + working-directory: nuttx + + - name: Enable WAMR interpreter/aot runtime for NuttX + if: matrix.wamr_test_option.option != '' + run: | + for x in ${{ matrix.wamr_test_option.option }}; do + kconfig-tweak --enable $x + done + working-directory: nuttx + + - name: Enable WAMR Features for NuttX + if: matrix.wamr_feature_option.option != '' + run: | + for x in ${{ matrix.wamr_feature_option.option }}; do + kconfig-tweak --enable $x + done + working-directory: nuttx + + - name: Disable FPU for NuttX + if: matrix.target_config.fpu_type == 'none' + run: | + kconfig-tweak --disable CONFIG_ARCH_FPU + working-directory: nuttx + + - name: Disable DPFPU for NuttX + if: matrix.target_config.fpu_type == 'fp' + run: | + kconfig-tweak --disable CONFIG_ARCH_DPFPU + working-directory: nuttx + + # Note: while a real hardware would need + # INTERPRETERS_WAMR_MEM_DUAL_BUS_MIRROR=y, + # it doesn't work with xtensa qemu which we use on the CI because it + # doesn't have a proper emulation of I/D separate mappings. + # we work it around by using INTERPRETERS_WAMR_MEM_DUAL_BUS_MIRROR=n. + # this configuration won't work on a real hardware. + - name: Tweak NuttX config (xtensa) + if: matrix.target_config.target == 'xtensa' + run: | + kconfig-tweak --enable CONFIG_INTERPRETERS_WAMR_AOT_WORD_ALIGN_READ + kconfig-tweak --disable CONFIG_INTERPRETERS_WAMR_MEM_DUAL_BUS_MIRROR + working-directory: nuttx + + - name: Build NuttX + run: | + make olddefconfig + make -j$(nproc) + working-directory: nuttx + + # for xtensa, build a 8MB firmware image. + # simple boot is assumed. (thus the nuttx.bin offset in the image is 0) + # qemu will infer the flash size from the file size. + - name: Post build processing (xtensa) + if: matrix.target_config.target == 'xtensa' + run: | + cd nuttx + dd if=/dev/zero of=flash.img bs=1024 count=8192 + dd if=nuttx.bin of=flash.img conv=notrunc + mv flash.img nuttx + + - name: Build firmware path + id: build_firmware_path + run: | + echo "firmware=$PWD/nuttx/nuttx" >> $GITHUB_OUTPUT + + # for xtensa, use the espressif fork of qemu, which has esp32s3 support. + - name: Install QEMU (xtensa) + if: matrix.target_config.target == 'xtensa' + run: | + apt-get remove -y qemu-system-misc + apt-get update && apt-get install -y libsdl2-2.0-0 + ./.github/scripts/install_qemu_xtensa.sh + qemu-system-xtensa --version + working-directory: apps/interpreters/wamr/wamr + + - name: Test + run: | + cd apps/interpreters/wamr/wamr/tests/wamr-test-suites + ./test_wamr.sh -s spec ${{ matrix.wamr_test_option.mode }} -m ${{ matrix.target_config.target }} -b -Q -F ${{ steps.build_firmware_path.outputs.firmware }} ${{ matrix.wamr_feature_option.mode}} + + - name: pack the log + if: always() + run: | + mkdir log + cp $PWD/nuttx/.config log/dot-config + cp ${{ steps.build_firmware_path.outputs.firmware }} log + tar -C apps/interpreters/wamr/wamr/tests/wamr-test-suites/workspace -cvzf log/report.tgz report + + - name: upload the log + if: always() + uses: actions/upload-artifact@v7.0.1 + with: + name: spec-test-log-${{ github.run_id }}-${{ strategy.job-index }}-${{ matrix.target_config.target }} + path: log diff --git a/.github/workflows/supply_chain.yml b/.github/workflows/supply_chain.yml new file mode 100644 index 0000000000..3b481f7de8 --- /dev/null +++ b/.github/workflows/supply_chain.yml @@ -0,0 +1,65 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +# Check current WASM Micro Runtime results here: https://securityscorecards.dev/viewer/?uri=github.com/bytecodealliance/wasm-micro-runtime + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + # midnight UTC + schedule: + - cron: "0 0 * * *" + # allow to be triggered manually + workflow_dispatch: + +# Declare default permissions as read only. +permissions: + contents: read + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + if: github.repository == 'bytecodealliance/wasm-micro-runtime' + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: "Checkout code" + uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v3.1.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v3.1.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@a899987af240c0578ed84ce13c02319a693e168f + with: + sarif_file: results.sarif diff --git a/.github/workflows/wamr_wasi_extensions.yml b/.github/workflows/wamr_wasi_extensions.yml new file mode 100644 index 0000000000..0ef0b5b123 --- /dev/null +++ b/.github/workflows/wamr_wasi_extensions.yml @@ -0,0 +1,58 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: wamr_wasi_extensions + +on: + pull_request: + types: + - opened + - synchronize + paths: + - ".github/workflows/wamr_wasi_extensions.yml" + - "wamr_wasi_extensios/**" + - "core/iwasm/libraries/wasi-nn/include/**" + - "core/iwasm/libraries/lib-socket/**" + # allow to be triggered manually + workflow_dispatch: + +# Cancel any in-flight jobs for the same PR/branch so there's only one active +# at a time +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build_wamr_wasi_extensions: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04, macos-15-intel, macos-15] + steps: + - name: checkout + uses: actions/checkout@v6.0.2 + + - name: install-wasi-sdk-wabt + uses: ./.github/actions/install-wasi-sdk-wabt + with: + os: ${{ matrix.os }} + + - name: Build wamr-wasi-extensions + run: | + mkdir dist + ./build_libs.sh $(pwd)/dist/wamr-wasi-extensions + working-directory: wamr-wasi-extensions + + - name: Build wamr-wasi-extensions samples + run: | + ./build_samples.sh $(pwd)/dist/wamr-wasi-extensions + working-directory: wamr-wasi-extensions + + #CLARIFY: Require to upload artifact on ARM macOS? + - name: Upload artifacts + if: matrix.os == 'macos-15' + uses: actions/upload-artifact@v7.0.1 + with: + name: wamr-wasi-extensions + path: wamr-wasi-extensions/dist + retention-days: 10 diff --git a/.gitignore b/.gitignore index 0e82e0b5b0..92909c9f84 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,48 @@ +.* +!.gitignore + +.cache +.clangd +.vs .vscode -**/*build/ +.venv +/.idea +**/cmake-build-*/ +**/*build*/ +!/build-scripts +*.obj +*.a +*.so +.clangd +.DS_Store +*.o +.aider* + +core/deps/** +core/shared/mem-alloc/tlsf +core/iwasm/libraries/lib-wasi-threads/test/*.wasm +core/iwasm/libraries/lib-socket/test/*.wasm + +product-mini/app-samples/hello-world/test.wasm +product-mini/platforms/linux-sgx/enclave-sample/ +!product-mini/platforms/linux-sgx/enclave-sample/App/App.* +!product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.* + +build_out +tests/wamr-test-suites/workspace + +!/test-tools/wamr-ide/VSCode-Extension/.vscode + +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 + +tests/unit/runtime-common/wasm-apps/main.aot +tests/unit/aot-stack-frame/wasm-apps/test_aot.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..84099cb708 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "tests/unit/llm-enhanced-test"] + path = tests/unit/llm-enhanced-test + url = https://github.com/wasm-micro-runtime/llm-enhanced-test.git + branch = main diff --git a/ADOPTERS.md b/ADOPTERS.md new file mode 100644 index 0000000000..8cdd82d5c9 --- /dev/null +++ b/ADOPTERS.md @@ -0,0 +1,37 @@ +# WAMR adopters + +_If you are using WAMR in production/pre-production at your organization, please add your company name to this list. +The list is in alphabetical order._ + +| Organization | Contact | Status | Description of Use | +| -------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| [Alibaba](https://www.alibaba.com) | [@johnlanni](https://github.com/johnlanni) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Higress is a next-generation cloud-native gateway built on the core of open-source Istio + Envoy based on Alibaba's internal Envoy Gateway practice. | +| [Amazon](https://www.amazon.com) | [@loganek](https://github.com/loganek) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Prime Video is a global streaming service by Amazon that provides on-demand access to a vast library of movies, TV shows, and original programming, as well as live content. | +| [Ant Group](https://www.antgroup.com) | [@wei-tang](https://github.com/wei-tang) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | AntChain is a blockchain technology platform owned by Ant Group, a Chinese tech conglomerate that also runs Alipay, China's largest digital payment system. | +| [Bosch](https://www.bosch.com) | emily.ruppel@us.bosch.com | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | Silverline Cloud-Edge platform | +| [Disney](https://www.disney.com) | | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Disney+ Streaming | +| [Intel](https://www.intel.com) | [@wenyongh](https://github.com/wenyongh) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | Edge and the embedded environments | +| [Moonbit](https://www.moonbitlang.com) | [@peter-jerry-ye](https://github.com/peter-jerry-ye) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | MoonBit is an end-to-end programming language toolchain for cloud and edge computing using WebAssembly. | +| [Microsoft](https://www.microsoft.com) | [@Mossaka](https://github.com/Mossaka) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Hyperlight runs Wasm workloads in VMs without OS and kernel, it is a solution for improving the management and security of Wasm workloads on Azure. | +| [Midokura](https://www.midokura.com) | [@yamt](https://github.com/yamt) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | The next-generation Edge AI sensing platform | +| [Siemens](https://www.siemens.com) | [@ttrenner](https://github.com/ttrenner) | ![Undisclosed](https://img.shields.io/badge/-Undisclosed-orange?style=flat) | Industrial, IoT | +| [Sony Semiconductor Solutions](https://www.sony-semicon.com) | [@dongsheng28849455](https://github.com/dongsheng28849455) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | AI digital camera | +| [Xiaomi](https://www.mi.com) | [@no1wudi](https://github.com/no1wudi) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Xiaomi Vela is Xiaomi's IoT embedded software platform based on the open-source, real-time operating system NuttX. | +| [Xiaomi](https://www.mi.com) | [@no1wudi](https://github.com/no1wudi) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | TEE (trusted execution environment) app engine. | + +# Adopted in open-source projects + +_The list is in alphabetical order._ + +| Project | Reference | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| [Apache Teaclave](https://github.com/apache/incubator-teaclave) | https://github.com/apache/incubator-teaclave/blob/master/docs/executing-wasm.md | +| [Edge Virtualization Platform](https://github.com/SonySemiconductorSolutions/edge-virtualization-platform) | https://github.com/SonySemiconductorSolutions/edge-virtualization-platform | +| [Envoy](https://github.com/envoyproxy/envoy) | https://github.com/envoyproxy/envoy/blob/main/docs/root/configuration/other_features/wasm.rst | +| [faasm](https://github.com/faasm/faasm) | https://github.com/faasm/faasm/blob/main/docs/source/wamr.md | +| [fluent-bit](https://github.com/fluent/fluent-bit) | https://github.com/fluent/fluent-bit/tree/master/lib/wasm-micro-runtime-WAMR-1.3.0 | +| [harfbuzz](https://github.com/harfbuzz/harfbuzz) | https://github.com/harfbuzz/harfbuzz/blob/main/docs/wasm-shaper.md#enabling-the-wasm-shaper-when-building-harfbuzz | +| [inclave-containers](https://github.com/inclavare-containers/inclavare-containers) | https://github.com/inclavare-containers/inclavare-containers | +| [private-data-objects](https://github.com/hyperledger-labs/private-data-objects) | https://github.com/hyperledger-labs/private-data-objects/blob/main/common/interpreter/wawaka_wasm/README.md | +| [runwasi](https://github.com/containerd/runwasi) | https://github.com/containerd/runwasi/pull/508 (WIP) | +| [Wasmnizer-ts](https://github.com/web-devkits/Wasmnizer-ts) | https://github.com/web-devkits/Wasmnizer-ts | diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index 59239b72ab..ce0d4f8526 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -2,327 +2,104 @@ WebAssembly Micro Runtime Attributions ====================================== WAMR project reused some components from other open source project: -- **wasmtime**: for the wasi libc implementation -- **cJson**: used in the host_tool for remotely managing wasm applications +- **cJson**: in the repository [wamr-app-framework](https://github.com/bytecodealliance/wamr-app-framework/), used in the host_tool for remotely managing wasm applications - **contiki-ng**: for the coap protocol implementation - **freebsd libm**: used in core/shared/platform/alios/bh_math.c -- **littlevgl**: for the gui samples and wrapped the wasm graphic layer. +- **LVGL**: in the repository [wamr-app-framework](https://github.com/bytecodealliance/wamr-app-framework/), for the gui samples and wrapped the wasm graphic layer +- **llvm**: for the AOT/JIT compilation +- **wasm-c-api**: to implement the C-APIs of wasm. using headers and sameples +- **wasmtime**: for the wasi libc implementation +- **zephyr**: for several platform specific examples +- **WebAssembly debugging patch for LLDB**: for extending the ability of LLDB to support wasm debugging +- **libuv**: for the WASI Libc with uvwasi implementation +- **uvwasi**: for the WASI Libc with uvwasi implementation +- **asmjit**: for the Fast JIT x86-64 codegen implementation +- **zydis**: for the Fast JIT x86-64 codegen implementation +- **NuttX ELF headers**: used in core/iwasm/aot/debug/elf_parser.c +- **Dhrystone**: for the test benchmark dhrystone + +The WAMR fast interpreter is a clean room development. We would acknowledge the inspirations by [WASM3](https://github.com/wasm3/wasm3) open source project for the approach of pre-calculated operand stack location. + +| third party components | version number | latest release | vendor pages | CVE details | +| --- | --- | --- | --- | --- | +| cjson | 1.7.16 | 1.7.16 | https://github.com/DaveGamble/cJSON | https://www.cvedetails.com/vendor/19164/Cjson-Project.html | +| contiki-ng (er-coap) | unspecified | 3.0 | https://github.com/contiki-os/contiki | https://www.cvedetails.com/vendor/16528/Contiki-os.html | +| freebsd libm | unspecified | 13.0 | https://www.freebsd.org/ | https://www.cvedetails.com/vendor/6/Freebsd.html | +| LVGL | 6.0.1 | 7.11.0 | https://lvgl.io/ | | +| llvm | 11.0.1 | 12.0.0 | https://llvm.org | https://www.cvedetails.com/vendor/13260/Llvm.html | +| wasm-c-api | ac9b509f4df86e40e56e9b01f3f49afab0100037 | c9d31284651b975f05ac27cee0bab1377560b87e | https://github.com/WebAssembly/wasm-c-api | | +| wasmtime | unspecified | v0.26.0 | https://github.com/bytecodealliance/wasmtime | | +| zephyr | unspecified | v2.5.0 | https://www.zephyrproject.org/ | https://www.cvedetails.com/vendor/19255/Zephyrproject.html | +| WebAssembly debugging patch for LLDB | unspecified | unspecified | https://reviews.llvm.org/D78801 | | +| libuv | v1.46.0 | v1.46.0 | https://github.com/libuv/libuv | https://www.cvedetails.com/vendor/15402/Libuv-Project.html | +| uvwasi | unspecified | v0.0.12 | https://github.com/nodejs/uvwasi | | +| asmjit | unspecified | unspecified | https://github.com/asmjit/asmjit | | +| zydis | unspecified | e14a07895136182a5b53e181eec3b1c6e0b434de | https://github.com/zyantific/zydis | | +| NuttX ELF headers | 72313301e23f9c2de969fb64b9a0f67bb4c284df | 10.3.0 | https://github.com/apache/nuttx | | +| Dhrystone | 2.1 | 2.1 | https://fossies.org/linux/privat/old/ | | ## Licenses -### wasmtime - -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - ---- LLVM Exceptions to the Apache 2.0 License ---- - -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you -may redistribute such embedded portions in such Object form without complying -with the conditions of Sections 4(a), 4(b) and 4(d) of the License. - -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a -court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of -the License, but only in their entirety and only with respect to the Combined -Software. -``` - ### cJson -``` -Copyright (c) 2009-2017 Dave Gamble and cJSON contributors +[LICENSE](https://github.com/bytecodealliance/wamr-app-framework/blob/main/test-tools/host-tool/external/cJSON/LICENSE) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +### contiki-ng -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +[LICENSE](./core/shared/coap/er-coap/LICENSE.md) -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` +### freebsd libm -### contiki-ng +[COPYRIGHT](./core/shared/platform/common/math/COPYRIGHT) -``` +### LVGL +[LICENSE](https://github.com/bytecodealliance/wamr-app-framework/blob/main/samples/littlevgl/LICENCE.txt) -Copyright (c) (Year), (Name of copyright holder) All rights reserved. +[LICENSE](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/wgl/app/wa-inc/lvgl/LICENCE.txt) -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +### llvm - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +[LICENSE](./LICENSE) - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +### wasm-c-api - Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +[LICENSE](./samples/wasm-c-api/src/LICENSE) -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +### wasmtime -``` +[LICENSE](./core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/LICENSE) -### freebsd libm +[LICENSE](./core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/LICENSE) + +[LICENSE](./core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/LICENSE) -``` -Copyright 1992-2011 The FreeBSD Project. All rights reserved. +### zephyr - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: +[LICENSE](https://github.com/bytecodealliance/wamr-app-framework/blob/main/samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE) - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +### libuv - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +[LICENSE](./core/iwasm/libraries/libc-uvwasi/LICENSE_LIBUV) - The views and conclusions contained in the software and documentation - are those of the authors and should not be interpreted as representing - official policies, either expressed or implied, of the FreeBSD - Project. -``` +### uvwasi -### littlevgl +[LICENSE](./core/iwasm/libraries/libc-uvwasi/LICENSE_UVWASI) -``` -MIT licence -Copyright (c) 2016 Gábor Kiss-Vámosi +### asmjit -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +[LICENSE](./core/iwasm/fast-jit/cg/LICENSE_ASMJIT) -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +### zydis -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -``` +[LICENSE](./core/iwasm/fast-jit/cg/LICENSE_ZYDIS) +### NuttX ELF headers +[LICENSE](./core/iwasm/aot/debug/LICENSE_NUTTX) +[NOTICE](./core/iwasm/aot/debug/NOTICE_NUTTX) +### Dhrystone +[LICENSE](./tests/benchmarks/dhrystone/LICENSE) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..3872b2c9c7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,183 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) + +if(ESP_PLATFORM) + include (${COMPONENT_DIR}/build-scripts/esp-idf/wamr/CMakeLists.txt) + return() +endif() + +project (iwasm LANGUAGES C) + +set(CMAKE_CXX_STANDARD 17) + +set (CMAKE_VERBOSE_MAKEFILE OFF) + +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 "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (CMAKE_C_STANDARD 99) + +# 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_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple modules by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + 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_COPY_CALL_STACK) + # Disable copy callstack by default + set (WAMR_BUILD_COPY_CALL_STACK 0) +endif() + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + # Enable reference types by default + set (WAMR_BUILD_REF_TYPES 1) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +if (NOT WIN32) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security \ + -ffunction-sections -fdata-sections \ + -fvisibility=hidden") + # Remove the extra spaces for better make log + string (REGEX REPLACE " *" " " CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) +endif() + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#endif () +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_target_properties (vmlib PROPERTIES OUTPUT_NAME iwasm) +target_include_directories(vmlib INTERFACE + $ + $ +) + +target_link_libraries (vmlib PUBLIC ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl ${CMAKE_THREAD_LIBS_INIT}) +if (WAMR_BUILD_WASM_CACHE EQUAL 1) + target_link_libraries(vmlib INTERFACE boringssl_crypto) +endif () + +if (MINGW) + target_link_libraries(vmlib INTERFACE -lWs2_32 -lwsock32) + target_link_libraries(vmlib PRIVATE ws2_32) +endif () + +if (WIN32) + target_link_libraries(vmlib PRIVATE ntdll) +endif() + +set (WAMR_PUBLIC_HEADERS + ${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 +) +set_target_properties (vmlib PROPERTIES PUBLIC_HEADER "${WAMR_PUBLIC_HEADERS}") + +set_version_info (vmlib) + +install (TARGETS vmlib + EXPORT iwasmTargets + LIBRARY DESTINATION lib + PUBLIC_HEADER DESTINATION include +) + +install_iwasm_package () diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..b5103711ca --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,42 @@ +# In this project, we use CODEOWNERS to identify people who are likely to know +# who should review a pull request. +# +# People listed in this file are committing to respond in a timely fashion to +# PRs in the selected areas. However, that response doesn't have to be a full +# code review; it could also take any of these forms: +# +# - "I intend to review this but I can't yet. Please leave me a message if I +# haven't responded by (a specific date in the near future)." +# +# - "I think (a specific other contributor) should review this." (Note that the +# best reviewer for a PR may not necessarily be listed in this file.) +# +# People must only be added to this file if they've agreed to provide one of +# the above responses in a reasonable amount of time for every PR to which +# they're assigned. +# +# We only ask for this commitment from people who are employed full-time to +# work on this project. We gratefully welcome reviews from other contributors, +# but we don't believe it's fair to ask volunteers to respond quickly. + +# If none of the later patterns match, assign to anyone. This team is the +# parent of all the other teams and automatically includes everyone on those +# teams. +* @lum1n0us @TianlongLiang @yamt + +# Some parts of the project require more specialized knowledge. In those areas +# we designate smaller groups who are more likely to be aware of who's working +# in specific areas. + +/core/iwasm/core @loganek @lum1n0us @no1wudi @TianlongLiang @yamt +/core/iwasm/interpreter @loganek @lum1n0us @no1wudi @TianlongLiang @yamt +/core/iwasm/libraries/lib-socket @loganek @srberard +/core/iwasm/libraries/wasi-nn @lum1n0us @yamt +/core/iwasm @lum1n0us @no1wudi @TianlongLiang @yamt + +/docs/ @loganek @lum1n0us @no1wudi @TianlongLiang @yamt + +/product-mini/platforms/darwin/ @yamt +/product-mini/platforms/nuttx/ @no1wudi +/product-mini/platforms/windows/ @loganek +/product-mini/platforms/zephyr/ @srberard diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9210b3deb1..4382257321 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,8 +18,12 @@ Code changes =================== We Use Github Flow, So All Code Changes Happen Through Pull Requests. Pull requests are the best way to propose changes to the codebase. We actively welcome your pull requests: -- If you've added code that should be tested, add tests. Ensure the test suite passes. -- Avoid use macros for different platforms. Use seperate folder of source files to host diffeent platform logic. + - If you've added / modified code: + - Please provide tests to the test suite to validate the operation of your code, or point to existing test cases which do the same. + - Ensure that your new tests pass. This way we ensure that your contribution continues to work as you expected as future contributions are made by other contributors. + - Ensure all the existing tests in the test suite pass. This way we can verify that your contribution doesn’t accidentally impact other contributions. + - If your contribution is minor and you feel it does not need an additional test case, i.e. updating comments, formatting, simple refactoring, etc. then provide an explanation in your PR comment, i.e. “this is a minor change *explain the change*, and as such [ “is covered by” *list existing test cases* | “is except from addition test contribution”]. +- Avoid using macros for different platforms. Use separate folders for source files to collect together different host platform logic. - Put macro definitions inside share_lib/include/config.h if you have to use macro. - Make sure your code lints and compliant to our coding style. - Extend the application library is highly welcome. @@ -27,7 +31,7 @@ We Use Github Flow, So All Code Changes Happen Through Pull Requests. Pull reque Coding Style =============================== Please use [K&R](https://en.wikipedia.org/wiki/Indentation_style#K.26R) coding style, such as 4 spaces for indentation rather than tabs etc. -We suggest use Eclipse like IDE or stable coding format tools to make your code compliant to K&R format. +We suggest using VS Code like IDE or stable coding format tools, like clang-format, to make your code compliant to the customized format(in .clang-format). Report bugs =================== diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 565b740e6f..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -# Currently supports clang-8 compiler -# Using the "test.c" app from the README.md: -# clang-8 --target=wasm32 -O3 -Wl,--initial-memory=131072,--allow-undefined,--export=main,--no-threads,--strip-all,--no-entry -nostdlib -o test.wasm test.c -# Pay attention to spacing above! ^ -# iwasm test.wasm - -FROM ubuntu:latest - -RUN apt-get update && \ - apt-get -y upgrade && \ - apt-get install -y build-essential clang-8 cmake g++-multilib git lib32gcc-5-dev llvm-8 lld-8 nano - -WORKDIR /root - -RUN git clone https://github.com/intel/wasm-micro-runtime - -RUN cd wasm-micro-runtime/core/iwasm/products/linux/ && mkdir build && \ - cd build && cmake .. && make - -RUN cd /usr/bin && ln -s wasm-ld-8 wasm-ld -RUN cd /usr/bin && ln -s ~/wasm-micro-runtime/core/iwasm/products/linux/build/iwasm iwasm diff --git a/README.md b/README.md index 7db0c50ec6..9d79661c8f 100644 --- a/README.md +++ b/README.md @@ -1,187 +1,108 @@ -WebAssembly Micro Runtime -========================= -[Building WAMR VM core](./doc/build_wamr.md) | [Embedding WAMR VM core](./doc/embed_wamr.md) | [Building WASM applications](./doc/build_wasm_app.md) | [Samples and demos](https://github.com/bytecodealliance/wasm-micro-runtime#samples-and-demos) +# WebAssembly Micro Runtime + **A [Bytecode Alliance][BA] project** [BA]: https://bytecodealliance.org/ -WebAssembly Micro Runtime (WAMR) is a standalone WebAssembly (WASM) runtime with small footprint. It includes a few parts as below: -- The "iwasm" VM core, supporting WebAssembly interpreter, ahead of time compilation (AoT) and Just-in-Time compilation (JIT) - -- The application framework and the supporting API's for the WASM applications - -- The dynamic management of the WASM applications - - - -iwasm VM core -========================= - -### key features - -- Embeddable with the supporting C API's -- Small runtime binary size (85K for interpreter and 50K for AoT) and low memory usage -- Near to native speed by AoT -- AoT module loader works for both embedded OS and Linux system -- Choices of WASM application libc support: the built-in libc subset for embedded environment or [WASI](https://github.com/WebAssembly/WASI) for standard libc -- The mechanism for exporting native API's to WASM applications +**[Guide](https://wamr.gitbook.io/)**  **[Website](https://bytecodealliance.github.io/wamr.dev)**  **[Chat](https://bytecodealliance.zulipchat.com/#narrow/stream/290350-wamr)** + +[Build WAMR](./doc/build_wamr.md) | [Build AOT Compiler](./wamr-compiler/README.md) | [Embed WAMR](./doc/embed_wamr.md) | [Export Native API](./doc/export_native_api.md) | [Build Wasm Apps](./doc/build_wasm_app.md) | [Samples](./samples/README.md) + +WebAssembly Micro Runtime (WAMR) is a lightweight standalone WebAssembly (Wasm) runtime with small footprint, high performance and highly configurable features for applications cross from embedded, IoT, edge to Trusted Execution Environment (TEE), smart contract, cloud native and so on. It includes a few parts as below: +- [**VMcore**](./core/iwasm/): A set of runtime libraries for loading and running Wasm modules. It supports rich running modes including interpreter, Ahead-of-Time compilation(AoT) and Just-in-Time compilation (JIT). WAMR supports two JIT tiers - Fast JIT, LLVM JIT, and dynamic tier-up from Fast JIT to LLVM JIT. +- [**iwasm**](./product-mini/): The executable binary built with WAMR VMcore which supports WASI and command line interface. +- [**wamrc**](./wamr-compiler/): The AOT compiler to compile Wasm file into AOT file +- Useful components and tools for building real solutions with WAMR vmcore: + - [App-framework](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/README.md): A framework for supporting APIs for the Wasm applications + - [App-manager](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-mgr/README.md): A framework for dynamical loading the Wasm module remotely + - [WAMR-IDE](./test-tools/wamr-ide): An experimental VSCode extension for developping WebAssembly applications with C/C++ + + +### Key features +- Full compliant to the W3C Wasm MVP +- Small runtime binary size (core vmlib on cortex-m4f with tail-call/bulk memory/shared memory support, text size from bloaty) + * ~58.9K for fast interpreter + * ~56.3K for classic interpreter + * ~29.4K for aot runtime + * ~21.4K for libc-wasi library + * ~3.7K for libc-builtin library +- Near to native speed by AOT and JIT +- Self-implemented AOT module loader to enable AOT working on Linux, Windows, MacOS, Android, SGX and MCU systems +- Choices of Wasm application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for the standard libc +- [The simple C APIs to embed WAMR into host environment](./doc/embed_wamr.md), see [how to integrate WAMR](./doc/embed_wamr.md) and the [API list](./core/iwasm/include/wasm_export.h) +- [The mechanism to export native APIs to Wasm applications](./doc/export_native_api.md), see [how to register native APIs](./doc/export_native_api.md) +- [Multiple modules as dependencies](./doc/multi_module.md), ref to [document](./doc/multi_module.md) and [sample](samples/multi-module) +- [Multi-thread, pthread APIs and thread management](./doc/pthread_library.md), ref to [document](./doc/pthread_library.md) and [sample](samples/multi-thread) +- [wasi-threads](./doc/pthread_impls.md#wasi-threads-new), ref to [document](./doc/pthread_impls.md#wasi-threads-new) and [sample](samples/wasi-threads) +- [Linux SGX (Intel Software Guard Extension) support](./doc/linux_sgx.md), ref to [document](./doc/linux_sgx.md) +- [Source debugging support](./doc/source_debugging.md), ref to [document](./doc/source_debugging.md) +- [XIP (Execution In Place) support](./doc/xip.md), ref to [document](./doc/xip.md) +- [Berkeley/Posix Socket support](./doc/socket_api.md), ref to [document](./doc/socket_api.md) and [sample](./samples/socket-api) +- [Multi-tier JIT](./product-mini#linux) and [Running mode control](https://bytecodealliance.github.io/wamr.dev/blog/introduction-to-wamr-running-modes/) +- Language bindings: [Go](./language-bindings/go/README.md), [Python](./language-bindings/python/README.md), [Rust](./language-bindings/rust/README.md) + +### Wasm post-MVP features +- [wasm-c-api](https://github.com/WebAssembly/wasm-c-api), ref to [document](doc/wasm_c_api.md) and [sample](samples/wasm-c-api) +- [128-bit SIMD](https://github.com/WebAssembly/simd), ref to [samples/workload](samples/workload) +- [Reference Types](https://github.com/WebAssembly/reference-types), ref to [document](doc/ref_types.md) and [sample](samples/ref-types) +- [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations), [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory), [Memory64](https://github.com/WebAssembly/memory64) +- [Tail-call](https://github.com/WebAssembly/tail-call), [Garbage Collection](https://github.com/WebAssembly/gc), [Exception Handling](https://github.com/WebAssembly/exception-handling), [Branch Hinting](https://github.com/WebAssembly/branch-hinting) +- [Extended Constant Expressions](https://github.com/WebAssembly/extended-const) ### Supported architectures and platforms - -The iwasm supports following architectures: - +The WAMR VMcore supports the following architectures: - X86-64, X86-32 -- ARM, THUMB -- MIPS -- XTENSA - -Following platforms are supported: - -- [Linux](./doc/build_wamr.md#linux) -- [Zephyr](./doc/build_wamr.md#zephyr) -- [MacOS](./doc/build_wamr.md#macos) -- [VxWorks](./doc/build_wamr.md#vxworks) -- [AliOS-Things](./doc/build_wamr.md#alios-things) -- [Intel Software Guard Extention (Linux)](./doc/build_wamr.md#linux-sgx-intel-software-guard-extention) - -Refer to [WAMR porting guide](./doc/port_wamr.md) for how to port WAMR to a new platform. - -### Build wamrc AoT compiler - -Execute following commands to build **wamrc** compiler: - -```shell -cd wamr-compiler -./build_llvm.sh -mkdir build -cd build -cmake .. -make -``` - -After build is completed, create a symbolic link **/usr/bin/wamrc** to the generated wamrc. - -### Build the mini product - -WAMR supports building the iwasm VM core only (no app framework) to the mini product. The WAMR mini product takes the WASM application file name as input, and then executes it. For the detailed procedure, see **[build WAMR VM core](./doc/build_wamr.md)** and **[build and run WASM application](./doc/build_wasm_app.md)**. - -### Embed WAMR VM core - -WAMR provides a set of C API for loading the WASM module, instantiating the module and invoking a WASM function from a native call. For the details, see [embed WAMR VM core](./doc/embed_wamr.md). - - - -Application framework -=================================== - -By using the iwasm VM core, we are flexible to build different application frameworks for the specific domains, although it would take quite some efforts. - -The WAMR has offered a comprehensive framework for programming WASM applications for device and IoT usages. The framework supports running multiple applications, which are based on the event driven programming model. Here are the supporting API sets by the [WAMR application library](./doc/wamr_api.md) : - -- Timer -- Micro service (Request/Response) and Pub/Sub inter-app communication -- Sensor -- Connectivity and data transmission -- 2D graphic UI (based on littlevgl) - -Every subfolder under [WAMR application framework](./core/app-framework) folder is a compilation configurable component. The developers can copy the template folder to create new components to the application framework. If a component needs to export native functions to the WASM application, refer to the [export_native_api.md](./doc/export_native_api.md) . - - - -# Remote application management - -The WAMR application manager supports remote application management from host environment or the cloud through any physical communications such as TCP, UPD, UART, BLE, etc. Its modular design makes it able to support application management for different managed runtimes. - - - - - -The tool [host_tool](./test-tools/host-tool) communicates to the WAMR app manager for installing/uninstalling the WASM applications on companion chip from host system. And the [IoT App Store Demo](./test-tools/IoT-APP-Store-Demo/) shows the conception of remotely managing the device applications from cloud. - - - -WAMR SDK -========== - -The **wamr-sdk** tools build the WAMR to both **runtime SDK** for embedding by your native codes and **APP SDK** for developing the WASM applications. A SDK profile presents a configuration of build parameters for the selection of CPU arch, software platforms, execution mode, libc and application framework components. - -**Note**: [WASI-SDK](https://github.com/CraneStation/wasi-sdk/releases) version 7 and above should be installed before building the WAMR SDK. - -### Menu configuration for building SDK - -Menu configuration is supported for easy integration of runtime components and application libraries for the target architecture and platform. - -``` -cd wamr-sdk -./menuconfig.sh -``` - -wamr build menu configuration - -After the menu configuration is finished, the building process is automatically started. When the building gets successful, the SDK package is generated under folder $wamr-sdk/out/{profile}, and the header files of configured components were copied into the SDK package. - -The directory structure of a SDK package with profile name "simple": - -``` -simple/ -├── app-sdk -│   ├── libc-builtin-sysroot -│   │   ├── include -│   │   └── share -│   └── wamr-app-framework -│   ├── include -│   │   ├── bi-inc -│   │   └── wa-inc -│   ├── lib -│   └── share -└── runtime-sdk - ├── include - │   └── bi-inc - └── lib -``` - -The tool **build_sdk.sh** can be also directly executed by passing the configuration arguments, which is how each WAMR sample project builds the WAMR SDK for its own building profile. - -### Use Runtime SDK - -The folder "**runtime-sdk**" contains all the header files and library files for integration with project native code. - -### Build WASM applications with APP-SDK - -The folder “**app-sdk**” contains all the header files and WASM library for developing the WASM application. For C/C++ based WASM applications, the developers can use conventional cross-compilation procedure to build the WASM application. Refer to [build WASM applications](./doc/build_wasm_app.md) for the details. - - - - -Samples and demos -================= - -The WAMR samples integrate the iwasm VM core, application manager and selected application framework components. The samples are located in folder [samples](./samples): -- **[Simple](./samples/simple/README.md)**: The runtime is integrated with most of the WAMR APP libraries, and a few WASM applications are provided for testing the WAMR APP API set. It uses **built-in libc** and executes apps in **interpreter** mode by default. -- **[littlevgl](./samples/littlevgl/README.md)**: Demonstrating the graphic user interface application usage on WAMR. The whole [LittlevGL](https://github.com/littlevgl/) 2D user graphic library and the UI application is built into WASM application. It uses **WASI libc** and executes apps in **AoT mode** by default. -- **[gui](./samples/gui/README.md)**: Moved the [LittlevGL](https://github.com/littlevgl/) library into the runtime and defined a WASM application interface by wrapping the littlevgl API. It uses **WASI libc** and executes apps in **interpreter** mode by default. - - -The graphic user interface demo photo: - -![WAMR samples diagram](./doc/pics/vgl_demo.png "WAMR samples diagram") - - -Releases and acknowledgments -============================ - -WAMR is a community effort. Since Intel Corp contributed the first release of this open source project, this project has received many good contributions from the community. - -See the [major features releasing history and contributor names](./doc/release_ack.md) - - -Roadmap -======= - -See the [roadmap](./doc/roadmap.md) to understand what major features are planned or under development. - -Please submit issues for any new feature request, or your plan for contributing new features. +- ARM, THUMB (ARMV7 Cortex-M7 and Cortex-A15 are tested) +- AArch64 (Cortex-A57 and Cortex-A53 are tested) +- RISCV64, RISCV32 (RISC-V LP64 and RISC-V LP64D are tested) +- XTENSA, MIPS, ARC + +The following platforms are supported, click each link below for how to build iwasm on that platform. Refer to [WAMR porting guide](./doc/port_wamr.md) for how to port WAMR to a new platform. +- [Linux](./product-mini/README.md#linux), [Linux SGX (Intel Software Guard Extension)](./doc/linux_sgx.md), [MacOS](./product-mini/README.md#macos), [Android](./product-mini/README.md#android), [Windows](./product-mini/README.md#windows), [Windows (MinGW, MSVC)](./product-mini/README.md#mingw) +- [Zephyr](./product-mini/README.md#zephyr), [AliOS-Things](./product-mini/README.md#alios-things), [VxWorks](./product-mini/README.md#vxworks), [NuttX](./product-mini/README.md#nuttx), [RT-Thread](./product-mini/README.md#RT-Thread), [ESP-IDF(FreeRTOS)](./product-mini/README.md#esp-idf) + + +## Getting started +- [Build VM core](./doc/build_wamr.md) and [Build wamrc AOT compiler](./wamr-compiler/README.md) +- [Build iwasm (mini product)](./product-mini/README.md): [Linux](./product-mini/README.md#linux), [SGX](./doc/linux_sgx.md), [MacOS](./product-mini/README.md#macos) and [Windows](./product-mini/README.md#windows) +- [Embed into C/C++](./doc/embed_wamr.md), [Embed into Python](./language-bindings/python), [Embed into Go](./language-bindings/go), [Embed in Rust](./language-bindings/rust) +- [Register native APIs for Wasm applications](./doc/export_native_api.md) +- [Build wamrc AOT compiler](./wamr-compiler/README.md) +- [Build Wasm applications](./doc/build_wasm_app.md) +- [Port WAMR to a new platform](./doc/port_wamr.md) +- [VS Code development container](./doc/devcontainer.md) +- [Samples](./samples) and [Benchmarks](./tests/benchmarks) +- [End-user APIs documentation](https://bytecodealliance.github.io/wamr.dev/apis/) + + +### Performance and memory +- [Blog: The WAMR memory model](https://bytecodealliance.github.io/wamr.dev/blog/the-wamr-memory-model/) +- [Blog: Understand WAMR heaps](https://bytecodealliance.github.io/wamr.dev/blog/understand-the-wamr-heaps/) and [stacks](https://bytecodealliance.github.io/wamr.dev/blog/understand-the-wamr-stacks/) +- [Blog: Introduction to WAMR running modes](https://bytecodealliance.github.io/wamr.dev/blog/introduction-to-wamr-running-modes/) +- [Memory usage tuning](./doc/memory_tune.md): the memory model and how to tune the memory usage +- [Memory usage profiling](./doc/build_wamr.md#enable-memory-profiling-experiment): how to profile the memory usage +- [Performance tuning](./doc/perf_tune.md): how to tune the performance +- [Benchmarks](./tests/benchmarks): checkout these links for how to run the benchmarks: [PolyBench](./tests/benchmarks/polybench), [CoreMark](./tests/benchmarks/coremark), [Sightglass](./tests/benchmarks/sightglass), [JetStream2](./tests/benchmarks/jetstream) +- [Performance and footprint data](https://github.com/bytecodealliance/wasm-micro-runtime/wiki/Performance): the performance and footprint data + + +Project Technical Steering Committee +==================================== +The [WAMR PTSC Charter](./TSC_Charter.md) governs the operations of the project TSC. +The current TSC members: +- [dongsheng28849455](https://github.com/dongsheng28849455) - **Dongsheng Yan**, +- [loganek](https://github.com/loganek) - **Marcin Kolny**, +- [lum1n0us](https://github.com/lum1n0us) - **Liang He**, +- [no1wudi](https://github.com/no1wudi) **Qi Huang**, +- [qinxk-inter](https://github.com/qinxk-inter) - **Xiaokang Qin**, +- [ttrenner ](https://github.com/ttrenner) - **Trenner, Thomas**, +- [wei-tang](https://github.com/wei-tang) - **Wei Tang**, +- [wenyongh](https://github.com/wenyongh) - **Wenyong Huang**, +- [woodsmc](https://github.com/woodsmc) - **Woods, Chris**, +- [xujuntwt95329](https://github.com/xujuntwt95329) - **Jun Xu**, +- [xwang98](https://github.com/xwang98) - **Xin Wang**, (chair) +- [yamt](https://github.com/yamt) - **Takashi Yamamoto**, License @@ -191,10 +112,8 @@ exception. See the LICENSE file for details. This license allows you to freely use, modify, distribute and sell your own products based on WAMR. Any contributions you make will be under the same license. - -Submit issues and contact the maintainers -========================================= -[Click here to submit. Your feedback is always welcome!](https://github.com/intel/wasm-micro-runtime/issues/new) - - -Contact the maintainers: imrt-public@intel.com +# More resources +- [Who use WAMR?](https://github.com/bytecodealliance/wasm-micro-runtime/wiki) +- [WAMR Blogs](https://bytecodealliance.github.io/wamr.dev/blog/) +- [Community news and events](https://bytecodealliance.github.io/wamr.dev/events/) +- [WAMR TSC meetings](https://github.com/bytecodealliance/wasm-micro-runtime/wiki/TSC-meeting-notes) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 0000000000..56455b8742 --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,1740 @@ +## WAMR-2.4.3 + +### Breaking Changes + +### New Features + +### Bug Fixes + +- libc-wasi: add missing pointer validations to socket functions (#4611) (#4665) + +### Enhancements + +### Others + +--- + +## WAMR-2.4.2 + +### Breaking Changes + +### New Features + +### Bug Fixes + +- CVE-2025-58749. Fix a potential hang issue in LLVMJIT mode + +### Enhancements + +### Others + +--- + +## WAMR-2.4.1 + +### Breaking Changes + +- wasi_socket_ext.c: fix error reporting (#4476) +- lib-socket: make getaddrinfo return EAI\_ values (#4498) +- bump AOT_CURRENT_VERSION for extended-const (#4511) + +### New Features + +### Bug Fixes + +- modify macro related to simde when WASM_OP_SELECT_128 (#4461) +- posix os_socket_accept: stop assuming socklen_t is unsigned int (#4488) +- wasi_socket_ext.c: fix the number of getaddrinfo results (#4466) +- Fix typos (#4472) +- fix regression running_config.json (#4477) +- posix os_socket_addr_resolve: relax compatibility check (#4469) +- Add validation for recursive type count in loader (#4440) + +### Enhancements + +- Add CI on Zephyr (#4336) +- introduce wasm_runtime_instantiate_ex2 (#4444) +- Add a CLI option to specify shared heap size on Windows platform (#4503) +- wasi-nn: add a comment on load_by_name_with_config (#4492) +- nn-cli: add an option to use load_by_name (#4490) +- wamr-wasi-extensions: document (#4493) +- doc/socket_api.md: some historical notes (#4494) +- lib-socket: implement getsockopt(SOL_SOCKET,SO_TYPE) (#4458) + +### Others + +- build(deps): Bump github/codeql-action from 3.29.2 to 3.29.3 (#4507) + +## WAMR-2.4.0 + +### Breaking Changes + +- Refactor copy callstack feature (#4401) +- Enable WAMR_BUILD_WASI_EPHEMERAL_NN by default (#4381) +- Enable aot memory64 sw bounds checks by default (#4350) + +### New Features + +- Support extended constant expressions (#4432) +- Shared heap enhancements for Interpreter and AOT (#4400) + +### Bug Fixes + +- posix os_socket_addr_resolve: return the consistent max_info_size (#4467) +- fix a wamrc debug mode compile issue (#4470) +- wasi-nn: do not pretend to support legacy abi in openvino and llamacpp (#4468) +- appease a few compiler warnings (-Wstrict-prototypes) (#4465) +- enable aux stack frame for aot compiler fuzz test (#4462) +- improve logic of `heap_type` validation when `ref.null` (#4372) +- wasi_nn_llamacpp.c: explicitly reject unimplemented input index (#4446) +- wasi: avoid user-triggerable 0-sized allocations (#4452) +- Fix socket shutdown (#12) (#4449) +- wasi_nn_llamacpp.c: validate input tensor type/dimensions (#4442) +- wasi_nn_llamacpp.c: reject invalid graph and execution context (#4422) +- wasi_nn_openvino.c: avoid self-assignment warning (#4434) +- Fix potential integer overflow issues (#4429) +- Improve run.py of regression (#4417) +- wasi-nn: reduce code duplication a bit (#4433) +- Refactor AOTObjectData definition to use a forward declaration (#4428) +- CI: revert SGX retry attempts (#4421) +- loader: fix a potential overflow issue (#4427) +- wasi_nn_openvino.c: fix a debug build (#4416) +- Fix few shadow warnings (#4409) +- wasi_nn_llamacpp.c: remove an unused variable (#4415) +- wasi_nn_llamacpp.c: fix buffer overruns in set_input (#4420) +- wasi-nn: make the host use the wasi_ephemeral_nn version of tensor_data (#4411) +- Collective fix (#4413) +- fix bug in bh_vector when extending (#4414) +- wasi_nn_llamacpp.c: make this compilable (#4403) +- Fix handling of non-nullable global_type during global import (#4408) +- loader: add type index checking (#4402) +- wasi_nn_tensorflowlite.cpp: fix get_output return size (#4390) +- wasi-nn: fix context lifetime issues (#4396) +- CI: fix the description of upload_url (#4407) +- wamr-wasi-extensions/socket: disable reference-types (#4392) +- wasi_nn_openvino.c: implement multiple models per instance (#4380) +- Improve spec test execution by adding retry logic for transient errors (#4393) +- wasi-nn: add minimum serialization on WASINNContext (#4387) +- deprecate legacy WAMR-specific "wasi_nn" module (#4382) +- wasi-nn: fix tensor_data abi for wasi_ephemeral_nn (#4379) +- core/iwasm/libraries/wasi-nn/test: use the correct version of keras (#4383) +- Fix several issues related to night-run CI and test scripts. (#4385) +- wasi_nn_tensorflowlite.cpp: reject non-fp32 input earlier (#4388) +- core/iwasm/libraries/wasi-nn/test/build.sh: add a tip for intel mac (#4389) +- wasi-nn: don't try to deinit uninitialized backend (#4375) +- wasi-nn: apply the shared library hack to darwin as well (#4374) +- add nn-cli example (#4373) +- wasi_nn_openvino.c: remove pre/postprocessing and layout assumptions (#4361) +- send an empty/error reply from server (#4362) +- wasi_nn_openvino.c: add a missing buffer overflow check in get_output (#4353) +- wasi_ephemeral_nn.h: prefix identfiers to avoid too generic names (#4358) +- wamr-wasi-extensions: add lib-socket things (#4360) +- wasi_nn_openvino.c: remove broken xml check (#4365) +- add validation for array type in load_init_expr(GC only) (#4370) +- wasi-nn: fix backend leak on multiple loads (#4366) +- Collective fix for typos and minor bugs (#4369) +- Modify AOT static PGO to conform to llvm-18 and add a CI job to test static PGO on the coremark benchmark (#4345) +- Update WABT downloads URL (#4357) +- clean up incompatible running mode checks in test script and ci (#4342) +- Follow #4268 to deprecate wamr_ide-related components (#4341) +- Update type validation in load_table_import() and load_table() (#4296) +- wasi_nn_openvino.c: remove the tensor layout adjustment logic (#4308) +- add heap-type check for GC when ref.null (#4300) +- wasi_nn_types.h: remove a seemingly stale comment (#4348) +- wasi_socket_ext.c: avoid tls to make this library-friendly (#4338) +- wasi-nn: do not assign wasi_nn_ctx->backend multiple times (#4329) +- wasi_nn.h: make this compatible with wasi_ephemeral_nn (#4330) +- remove temporary wasi-libc build steps from CI workflows (#4343) +- wasi-nn: fix the size of tensor->type (#4333) +- wasi-nn: move some host-only things out of wasi_nn_types.h (#4334) +- Collective fix: fix some typos (#4337) +- Update binary compression steps to follow symlinks for actual files (#4321) +- Add wamrc compilation into Windows CI workflow (#4327) +- wasi-nn: remove unused wasi_nn_dump_tensor_dimension prototype (#4325) +- wasi_nn.h: add import_name attribute (#4328) +- wasi-nn: protect the backend lookup table with a lock (#4319) +- handle nullable heap reference types in import section (#4302) +- wasi_nn_openvino.c: make this buildable (#4305) +- wasi-nn: fix shared library filenames for macOS (#4306) +- fix wasi-nn abi definitions (#4307) +- wasi-nn: remove "backends" argument from detect_and_load_backend() (#4309) +- wasi_nn_openvino.c: fix a few printf formats (#4310) +- Bump uvwasi to latest commit #392e1f1 (#4312) + +### Enhancements + +- Add readme for extended const (#4471) +- Add security issue runbook (#4450) +- docs: fix cmake variable typo (#4441) +- CI: add wamr_wasi_extensions to the release assets (#4425) +- CI: build wamr-wasi-extensions (#4394) +- improve installation steps for wasi-sdk and wabt on Windows (#4359) +- wamr-wasi-extensions: add a cmake package to provide our wasi extension (#4344) +- Update Dockerfile for Zephyr SDK and Zephyr-project versioning (#4335) +- add load_by_name in wasi-nn (#4298) + +### Others + +- build(deps): Bump ossf/scorecard-action from 2.4.1 to 2.4.2 (#4315) +- build(deps): Bump github/codeql-action from 3.29.1 to 3.29.2 (#4459) +- build(deps): Bump github/codeql-action from 3.29.0 to 3.29.1 (#4436) +- build(deps): Bump github/codeql-action from 3.28.19 to 3.29.0 (#4371) +- build(deps): Bump github/codeql-action from 3.28.18 to 3.28.19 (#4346) +- build(deps): Bump requests from 2.32.3 to 2.32.4 in /build-scripts (#4349) + +--- + +## WAMR-2.3.1 + +### Breaking Changes + +- Revert the location to install public headers (#4295). This restores compatibility (of installed headers) with WAMR-2.2.0 and earlier. + +### New Features + +- feat: Add instruction metering for interpreter (#4122) + +### Bug Fixes + +- updating WASI stdio handle initialization and build options for UVWASI (#4260) +- Fix SIMD load lane to avoid incompatible pointer types (#4278) +- Fixed unit tests on X86_32 (#4279) +- Improve Embedding WAMR guideline (#4284) +- Fix Compiler Error C2491 (#4286) +- Enhance type checking for function types in loader and improve error handling (#4294) +- Dockerfile.vx-delegate build error fix (#4273) +- Enable runtime API exposure for MSVC builds (#4287) + +### Enhancements + +- feat(yml): Add ESP32-P4 and ESP32-C5 support (#4270) +- add a sample to use cmake package (#4291) + +### Others + +- build(deps): Bump github/codeql-action from 3.28.17 to 3.28.18 (#4285) + +--- + +## WAMR-2.3.0 + +### Breaking changes + +### New features + +- simd for fast-interp (#4131) +- copy call-stack (#4033) + +### Bug fixes + +- fix(ios): Remove `float-abi` flag (#3889) +- Fix out of bounds issues after memory.grow on non-aot non-threads builds (#3872) +- Fix out of bounds issue in is_native_addr_in_shared_heap function (#3886) +- Fix mmap flags for AOT loader on non-Linux SGX platforms (#3890) +- fix(uwp): Gate NTSTATUS definition behind WINAPI_PARTITION_DESKTOP for UWP builds (#3897) +- Fix linked global initialization in multimodule (#3905) +- Correct the table index calculation in aot_instantiation (#3903) +- Fix a leak in wasm_loader_emit_br_info (#3900) +- Check possible integer overflow in aot memory boundary check (#3920) +- Fix CI wamr-ide error (#3913) +- Fix WASI Path Mapping Processing (#3923) +- Use plain assignment rather than bh_memcpy_s (#3924) +- Fix loader small bug (#3928) +- don't return an uninitialized trap if argv_to_results fails (#3935) +- support WASM_FUNCREF return type in argv_to_results (#3936) +- Fix incorrect assignment in win_file.c (#3939) +- Fix aot table instantiate (#3946) +- set alignment 4 when loading multi return value (#3955) +- Only access Zephyr thread stats info when it's available (#3962) +- top-level cmakefile: fix macOS build (#3968) +- Handle a new scenario where an item is both exported and imported. (#3984) +- platform/nuttx: Flush icache/dcache properly (#4147) +- fix(runtest.py): A workaround to bypass errors that occur when deleting temporary files (#4093) +- Fix build issues when compiling WAMRC as a cross-compiler (#4112) +- include bh_platform.h (#4135) +- Fix iwasm build error when WAMR_BUILD_WASI_NN enabled (#4138) +- avoid Windows perform newline translation (#4128) +- fix: correct typos and improve comments across multiple files by codespell (#4116) +- fix: fix load aarch64 aot failed (#4114) +- wasm_loader allocates more spaces for elements (#4099) +- fix: add dispose of the debug information builder when destroying compilation context (#4105) +- prevent mmap size overflow on 32 bit platform for memory.grow (#4071) +- fix: when load aot init expr,no type_idx set. (#4094) +- fix(aot_emit_aot_file): prevent buffer emission for zero byte_count (#4095) +- fix(build_llvm_libraries.yml): Correct script path for build_llvm.py (#4089) +- fix(unit-test): libc_builtin_test issues (#4073) +- [gc] Subtyping fix (#4075) +- fix(build_llvm.py): clean up whitespace and formatting in build script (#4087) +- Unit test:type matching issue and code redundancy (#4079) +- fix(aot): ensure value_cmp does not exceed br_count in branch table compilation (#4065) +- In wasm32, fix potential conversion overflow when enlarging 65536 pages (#4064) +- Use wasm32-wasip1 instead of wasm32-wasi target for rust code (#4057) +- Update Rust target from 'wasm32-wasi' to 'wasm32-wasip1' in CI (#4050) +- Fix wasm loader check data segment count (#4039) +- Fix table index calculations in wasm_loader and wasm_mini_loader (#4004) +- Ensure **heap_base and **data_end global indices are validated against import count (#3996) +- fix format specifier warning on 32bit builds (#4177) +- Remove indirect-load for constants on Xtensa Target to improve performance (#4162) +- cmake: Enhance target selection for ARM architectures with FPU (#4185) +- Add import memory/table flag assert check for miniloader (#4179) +- Fix few integer overflowing (#4161) +- prevent frame_offset underflow in wasm_loader (#4165) +- fix: Remove unused variables in SIMD_v128_const case (#4197) +- fix false native stack overflow detections with HW_BOUND_CHECK (#4196) +- Keep fix the CMake compatibility issue (#4180) +- Fix the error of AOT mode on the "i386-windows-msvc" platform (#4183) +- debug-engine: fix a few type mismatches (#4189) +- Replace CMAKE_CURRENT_FUNCTION_LIST_DIR (#4200) +- fix potential memory leak (#4205) +- Add missing V128 handling in WASM_OP_BR (#4203) +- fix print_help when libc wasi is enabled (#4218) +- LLVM: don't verify instcombine fixpoint (#4219) +- LLVMCreateTargetMachineWithOpts: disable large data (#4220) +- set default value of `WAMR_BUILD_REF_TYPES` to 1 in standalone cases (#4227) +- platform/nuttx: Fix dcache operation in os_dcache_flush (#4225) +- fix return types of our 64-bit clz/ctz/popcount intrinsics (#4238) +- riscv: avoid llvm.cttz.i32/i64 for xip (#4248) +- Add overflow check for preserved local offset in preserve_referenced_local (#4211) +- aot_resolve_object_relocation_group: adapt to LLVM 16 (#4250) +- initialize WASI stdio handles to invalid for better error handling (#4092) +- Modifying build flags to ensure libiwasm.so is built (#4255) +- Stop pretending to support extended-const proposal (#4258) +- Improve readlinkat_dup() to handle symlink size correctly (#4229) +- fix: improve error handling of snprintf() in send_thread_stop_status() (#4234) +- Don't call os_thread_get_stack_boundary unless we actually use it (#4264) +- avoid access null pointer (#4262) +- disable compiler to prevent get_current_target() crash (#4251) +- product-mini/platforms/windows: set C++17 explicitly (#4269) +- fix buf checking in load_table_section (#4276) +- Set CMAKE_OSX_SYSROOT when building lldb (#4274) +- Add select 128 (#4236) + +### Enhancements + +- Refine looking up aot function with index (#3882) +- Wasm loader enhancement: check code size in code entry (#3892) +- Refactor AOT loader to support compatible versions (#3891) +- GlobalValueSet was moved to IRPartitionLayer recently, but we have a local definition anyway (#3899) +- Support external toolchain on Windows for aot compiler (#3911) +- Drop declarative elements on module instantiation (#3922) +- add testcases for shared heap and fix POP_MEM_OFFSET of memory64 (#3916) +- Enable ref types by default (#3894) +- Update README.md to clarify Windows toolchain support and ESP-IDF reference (#3917) +- add thread cpu time for zephyr (#3937) +- Improvements for platform thread APIs on Windows and Zephyr (#3941) +- Refactor SConscript and add file checks in iwasm.c (#3945) +- Consume the placeholders that were put when emitting table info (#3940) +- wasm_export.h: Use "default" visibility for gcc and clang (#3957) +- [fuzzing] Enable instantiation (#3958) +- use a random secret key (#3971) +- CMakeLists.txt: Do not require C++ (#3956) +- add reference type support by default for darwin to support WASI-SDK-25 (#3978) +- top-level cmake: link llvm libraries to our shared library (#3973) +- Set thread information earlier in exec_env creation (#3967) +- Break aot_create_comp_data into small functions (#3987) +- Optimize memory initialization handling in AOT loader (#3983) +- nuttx: remove the up_x API for kernel build (#4154) +- Expose WAMR_BUILD_GC_HEAP_SIZE_DEFAULT as a CMake option (#4124) +- Use log instead of using assertion in aot loader (#4119) +- feat: use C linkage in aot_comp_option.h for C++ embeding (#4106) +- Cmake improvements (#4076) +- feat: add support for EXTERNREF value type and enable AOT validator in fuzz tests (#4083) +- build_llvm.py: Allow to build xtensa target on non-xtensa host (#4086) +- Add a conditional check for the macro **STDC_VERSION** (#4080) +- [fuzzing] execute every exported function (#3959) +- Update memory allocation functions to use allocator user data (#4043) +- Add versioning support and update CMake configuration (#3933) +- Show wasm proposals status during compilation and execution (#3989) +- add a validator for aot module (#3995) +- Synchronize the GC spec tests to the commit from December 9. 2024. (#4022) +- Refine getting const offsets in wasm loader of fast-interp (#4012) +- fixes for compiling on windows (#4026) +- .github: Add shared lib builds (#3975) +- Error message improvement (#4000) +- Refine read leb int wasm loader of fast interpreter (#4017) +- Enable shrunk memory by default and add related configurations (#4008) +- Add documentation regarding security issues and the status of Wasm proposals (#3972) +- Improve stack consistency by ensuring sufficient space for dummy offsets (#4011) +- Check whether related table has funcref elem in opcode call_indirect (#3999) +- [fuzzing] Use software bound-check during fuzzing (#4003) +- Add an example of how to embed WAMR in Zephyr user mode (#3998) +- Update cmake min to 3.14 (#4175) +- aot: add new u64 intrinsics (#4168) +- Refactor Dockerfile and update .dockerignore for wasi-nn tests; adjust map-dir parameters in smoke test script (#4158) +- improve variable naming and code clarity in SIMD operations (#4157) +- Raise CI runner to ubuntu 22.04 (#4191) +- Remove the dlen to optimize it. (#4193) +- Add missing casts and improve error handling in performance map functions (#4202) +- Raise wasi-sdk to 25 and wabt to 1.0.37 (#4187) +- wamrc: add --disable-llvm-jump-tables option (#4224) +- feat(fuzz): add a new fuzzing target about aot compiler (#4121) +- bypass vptr santizier (#4231) +- use a selected llvm libs list to replace the full list (#4232) +- teach aot emitter/loader about .srodata and .srodata.cst\* sections (#4240) +- run_clang_format_diff: mention homebrew for clang-format installation (#4237) +- Use --target to pass a triple in wamrc (#4199) +- samples/wasm-c-api: skip aot compilation unless necessary (#4239) +- samples/wasm-c-api: remove unused valgrind detection (#4249) +- More detail to python setup, and fixed small typo (#4247) +- JIT: don't join worker threads twice (#4252) +- aot_resolve_object_relocation_group: adapt to LLVM 19 (#4254) +- build-scripts/build_llvm.py: bump to llvm 18 (#4259) +- CI: make macos' build_samples_wasm_c_api similar to ubuntu (#4253) +- Refactor fast-interpreter SIMD compilation flags (#4261) +- Bypass wamr_ide-related components from the release process. (#4268) +- Check for WASM_ENABLE_SIMDE in a couple more places (#4266) +- Add error handling for sgx ci (#4222) + +### Security Issues + +- Add validation for old_path in wasi_path_symlink (# CVE-2025-43853) + +### Others + +- Exclude fuzz test python and npm packages in scoreboard scan (#3871) +- Bump AOT_CURRENT_VERSION for WAMR 2.x (gc, memory64) (#3880) +- Add Tianlong into code owners (#3970) +- build(deps): Bump actions/upload-artifact from 4.4.3 to 4.5.0 (#3981) +- docs: Update build instructions suggestions for using Valgrind (#4164) +- test: temporarily skip 'skip-stack-guard-page' test case (#4163) +- build(deps): Bump actions/upload-artifact from 4.6.1 to 4.6.2 (#4159) +- Update NuttX and NuttX Apps references to releases/12.9 in workflow files (#4148) +- build(deps): Bump esbuild, @vitejs/plugin-react and vite (#4149) +- build(deps): Bump ossf/scorecard-action from 2.4.0 to 2.4.1 (#4109) +- build(deps): bump github/codeql-action from 3.26.13 to 3.28.1 (#3888) (#3902) +- build(deps): Bump github/codeql-action from 3.28.10 to 3.28.11 (#4132) +- build(deps): Bump github/codeql-action from 3.28.9 to 3.28.10 (#4108) +- build(deps): Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#4107) + +--- + +## WAMR-2.2.0 + +### Breaking changes + +### New features + +- Add support for multi-memory proposal in classic interpreter (#3742) +- wasi-nn: Add a new target for llama.cpp as a wasi-nn backend (#3709) +- Add memory instance support apis (#3786) +- Implement a first version of shared heap feature (#3789) +- Support dynamic aot debug (#3788) +- Implement shared heap for AOT (#3815) +- Support table64 extension in classic-interp and AOT running modes (#3811) + +### Bug fixes + +- Enable merged os_mmap for aot data sections (#3681) +- Fix arm64 issues on mac (#3688) +- aot loader: Call os_mmap with MMAP_MAP_32BIT only when target is x86-64 or riscv64 (#3755) +- Fix building iwasm_shared and iwasm_static libs on win32 (#3762) +- Fix compile error when multi-module and tags are enabled (#3781) +- Fix aot multi export memory support (#3791) +- Fix Windows compile error when uvwasi is enabled (#3810) +- Fix missing symbols when using aot mode on riscv platforms (#3812) +- Fix mac build of libc_emcc_wrapper.c (#3836) +- aot_comp_option.h: Add missing stdint.h header (#3834) +- Fix compilation error found in tflite test (#3820) +- Fix exec_env_tls assertion in module instantiation (#3844) +- Fix issues of destroy_shared_heaps (#3847) + +### Enhancements + +- aot loader: Refine os_mmap related code (#3711) +- Enable merged os_mmap for aot data sections and aot text (#3743) +- Improve posix mmap retry logic (#3714) +- Remove unnecessary code duplication in aot runtime (#3767) +- Add wamrc parameter to configure stack frame features (#3763) +- refactoring: Re-use commit IP functionality between exception handling and other cases (#3768) +- AOT call stack optimizations (#3773) +- Appease GCC strict prototypes warning (#3775) +- Appease GCC -Wformat (#3783) +- Fix compiler warnings (#3784) +- Implement option for skipping function index in the callstack (#3785) +- Fix a compile warning in aot_emit_function.c (#3793) +- Restore cmake hidden compile symbol visibility (#3796) +- Refactor shared heap feature for interpreter mode (#3794) +- Add no_resolve to LoadArgs and wasm_runtime_resolve_symbols (#3790) +- shared heap: Fix some issues and add basic unit test case (#3801) +- Add shared heap sample (#3806) +- Fix unused param warning when GC is enabled (#3814) +- Add scoreboard CI for supply-chain security (#3819) +- Emit load_addr and load_size if WAMR_ENABLE_COMPILER is set (#3835) +- libc-emcc: Use alternate method to check getrandom support (#3848) +- Enable libc-wasi for windows msvc build (#3852) +- Remove unused folder samples/gui and samples/littlevgl (#3853) +- Fix some compile warnings and typos (#3854) +- Allow to set native stack boundary to exec_env (#3862) +- Refine wasm/aot function instance lookup (#3865) +- Fix quadratic runtime for duplicate export name detection (#3861) + +### Others + +- Add a comment on AOT_SECTION_TYPE_SIGNATURE (#3746) +- CI: Freeze version of bloaty for NuttX compilation (#3756) +- aot compiler: Allow to control stack boundary check when boundary check is enabled (#3754) +- Update ref to the multi-memory tests (#3764) +- compilation_on_nuttx.yml: Update checkout action to suppress warnings (#3765) +- CI: Disable parallel test in spectest for NuttX (#3780) +- spec_test_on_nuttx.yml: Disable riscv32_ilp32f for now (#3777) +- Ignore temporary file from aider (#3787) +- Add CODEOWNERS (#3822) +- build(deps): bump github/codeql-action from 2.2.4 to 3.26.9 (#3826) +- build(deps): bump actions/upload-artifact from 3.1.0 to 4.4.0 (#3827) +- build(deps): bump ossf/scorecard-action from 2.3.1 to 2.4.0 (#3828) +- build(deps): bump github/codeql-action from 3.26.9 to 3.26.11 (#3843) +- build(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3 (#3855) +- build(deps): bump github/codeql-action from 3.26.11 to 3.26.12 (#3856) +- Add Windows wamrc and iwasm build in release CI (#3857) +- Fix syntax error in codeql_buildscript.sh (#3864) +- release CI: Add another iwasm binary that supports Garbage Collection and Exception Handling (#3866) +- Fix lookup function issue reported in nightly run (#3868) + +--- + +## WAMR-2.1.2 + +### Breaking Changes + +- wasi-nn: Apply new architecture (#3692) + +### New Features + +- [wasi-nn] Add a new wasi-nn backend openvino (#3603) +- Add APIs into wasm_c_api.h to summary wasm function execution duration (#3639) +- Add support for RISCV32 ILP32F (#3708) + +### Bug Fixes + +- libc-builtin: Fix function prototype for wasm_runtime_module_realloc (#3702) +- Fix potential memory leak in insert_native_symbol (#3712) +- aot compiler: Fix NaN handling for opcode f32/f64.const in XIP mode (#3721) +- Fix table idx resolving in op call_indirect/return_call_indirect (#3726) + +### Enhancements + +- Remove a few hardcoded spec test knowledge from the core library (#3648) +- Change log of import function to be consistent (#3656) +- libc-builtin: Fix a printf format (#3652) +- Set compile symbol visibility to hidden in cmake (#3655) +- wamrc: Add --mllvm= option (#3658) +- wamr-compiler: Avoid size-level tweak if target is specified (#3659) +- aot runtime: Add missing arm/thumb relocations (#3660) +- aot compiler: Enlarge AOTNativeSymbol->symbol (#3662) +- aot compiler: Bail out on too long native symbol names (#3663) +- Support more features for rt-thread (#3661) +- Zephyr User Mode Support (#3650) +- Set posix thread name for debug build (#3657) +- Add emscripten_sleep() wrapper to libc-emcc (#3669) +- Fix a compilation warning (#3682) +- wamrc: Add some help text for --size-level (#3689) +- Restore linux iwasm default visibility (#3691) +- posix_thread.c: Restore old signal alternate stack before thread exit (#3693) +- libc-wasi: Make rights of STDIN/STDOUT/STDERR fixed and overlook their access modes (#3694) +- [refactoring] Extract read leb to a separate file, share the code between loader and mini loader (#3701) +- debug-interp: Only add lock when signal_flag is SIG_SINGSTEP (#3704) +- Fix compilation warnings (#3707) +- Add missing headers in bh_atomic.h and aot_llvm_extra.cpp (#3715) +- Update std atomic check and simd compatibility check for arc compiler (#3716) +- aot compiler: Track non-0x00 tableindex as ref types use (#3695) +- compilation: Use the dedicated stack-sizes section only for AOT (#3732) +- riscv: Add missing relocation intrinsics for **fixdfsi/**ltdf2 (#3733) + +### Others + +- Fix night run CI (#3640) +- spec-test-script/runtest.py: Don't assume the tmp dir path (#3632) +- wamr-test-suites: Remove dead code (wasi_test) (#3634) +- wamr-test-suites/test_wamr.sh: Add an option to specify wamrc binary (#3635) +- CI: Build llvm for xtensa (#3637) +- spec-test-script/runtest.py: Avoid specifying -v=0 unnecessarily (#3642) +- spec-test-script: Add xtensa case (#3643) +- spec-test-script/runtest.py: Move "--size-level=1" to common place for RISCV64 (#3644) +- spec-test-script/runtest.py: Use a shorter timeout when expected to fail (#3647) +- spec-test-script: Make case_last_words larger (#3651) +- spec-test-script/runtest.py: Reduce stack size for aot w/o gc (#3653) +- spec-test-script: Skip a few tests for xtensa qemu (#3664) +- spec-test-script: Use -mtext-section-literals for xtensa xip (#3666) +- spec_test_on_nuttx.yml: Add xtensa (#3665) +- spec_test_on_nuttx.yml: Enable xip (#3671) +- spec_test_on_nuttx.yml: Record more logs (#3670) +- spec_test_on_nuttx.yml: Replace sed with kconfig-tweak (#3672) +- spec_test_on_nuttx.yml: Retire CONFIG_EOL_IS_LF (#3676) +- spec-test-script/runtest.py: Use wamrc --xip option for xip (#3683) +- CI: Bump NuttX version to 12.6 (#3684) +- wamr-test-suites: Clean up generated tmp files after spec test (#3700) +- test_wamr.sh: Fix build wabt tool (#3703) +- NuttX: Retire CONFIG_ARCH_RV32IM and CONFIG_ARCH_RV64GC (#3717) +- runtest.py: Normallize option handling for XIP mode (#3722) +- CI: Enable XIP spectest for RISCV32 ILP32F (#3727) +- CI: Unify configuration stage for NuttX (#3725) + +--- + +## WAMR-2.1.1 + +### Breaking Changes + +- Sync up with latest wasi-nn spec (#3530) + +### New Features + +- Add APIs to get package version (#3601) +- Export API wasm_runtime_enlarge_memory (#3569) +- Add table type API support (#3515) +- Add wasm_runtime_get_module_package_type() and wasm_runtime_get_file_package_type() (#3600) + +### Bug Fixes + +- wasm_application.c: Avoid null pointer dereference (#3620) +- EH: Use the consistent type for EH handlers (#3619) +- wasm loader: Fix several issues in GC and exception handling (#3586) +- wasm loader: Fix push_frame_offset when pushing v128 type (#3588) +- Add integer overflow check for some indices in wasm/aot loader (#3579) +- aot-analyzer: Fix a few printf formats (#3590) +- aot-analyzer: Fix macos build (#3589) +- Fix compilation errors in aot-analyzer tool (#3584) +- interp debugger: Fix setting invalid value to step_count (#3583) +- aot loader: Check import global value type before using (#3571) +- Fix missing stack frame alloc/free in AOT multi-module invoke (#3562) +- aot loader: Verify global value type (#3560) +- aot loader: Add more checks in load_native_symbol_section() (#3559) +- core/shared/platform: Zero memory returned by os_mmap in some platforms (#3551) +- dwarf_extractor.cpp: Fix buffer overruns (#3541) +- aot loader: Prevent loading multiple native symbol sections (#3538) +- Validate func type in aot loader (#3535) +- wamrc: Fix truncated DW_AT_producer (#3537) +- wasm loader: Fix pop invalid offset count when stack top is ANY (#3516) +- Fix two fuzz issues (#3529) +- Fix several issues reported by oss-fuzz (#3526) + +### Enhancements + +- Fix compile warnings/error reported in Windows (#3616) +- wasm loader: Reject v128 for interpreters (#3611) +- Fix typos in wamrc and wasm_export.h (#3609) +- Bump ocaml/setup-ocaml from 2 to 3 (#3604) +- CMakeLists.txt: Fix Android pthread linkage (#3591) +- Add more arm AOT reloc entries (#3587) +- wasi-nn: Use numpy v1 in wasi-nn test requirements.txt (#3582) +- Optimize for multi-module support in AOT mode (#3563) +- aot compiler: Propagate const-ness by ourselves (#3567) +- aot_resolve_target_info: Avoid in-place modification of e_type (#3564) +- Allow missing imports in wasm loader and report error in wasm instantiation instead (#3539) +- aot compiler: Use larger alignment for load/store when possible (#3552) +- Consistent const keyword position in wasm_export.h (#3558) +- wasm_memory.c: Fix typo: hasn't been initialize -> `hasn't been initialized` (#3547) +- dwarf_extractor.cpp: Try to preserve link name (#3542) +- dwarf_extractor.cpp: Enable limited support for C++ (#3540) +- Sync up with latest wasi-nn spec (#3530) +- Expose more functions related to emitting AOT files (#3520) +- Make wasi-nn backends as separated shared libraries (#3509) +- build_llvm.py: Speed up llvm build with multi procs on windows (#3512) +- Fix compilation warnings of wasi-nn (#3497) +- Add missing functions to make RIOT work with the 2.x.x version (#3508) + +### Others + +- Update devcontainer.md (#3628) +- Fix compile errors on workload bwa and benchmark jetstream (#3617) +- wasm-mutator-fuzz: Set compilers earlier (#3585) +- wasm-mutator-fuzz: Make compilers overridable (#3578) +- wasi-nn: Add wasmedge-wasinn-example as smoke test (#3554) +- Add standalone cases (#3536) +- wasm-mutator-fuzz: Fix build errors and warnings for macOS (#3519) +- wasm-mutator-fuzz: Use another variable to check if in oss-fuzz environment (#3518) +- Add wasi-nn example as smoke test case (#3501) + +--- + +## WAMR-2.1.0 + +### Breaking Changes + +### New Features + +- Add wasm_export.h APIs to expose memory type (#3496) +- Add api to get export global instance (#3452) +- Add wasm-mutator-fuzz test (#3420) +- Implement Memory64 support for AOT (#3362) +- Add wasm module global type information APIs (#3406) +- Add aot binary analysis tool aot-analyzer (#3379) +- Expose API to get import/export function's param/result valkind (#3363) +- Add WASI support for esp-idf platform (#3348) + +### Bug Fixes + +- Fix posix build when libc wasi is disabled and debug interp is enabled (#3503) +- Fix wasm_mini_loader.c build when jit or multi-module is enabled (#3502) +- Fix wasm loader check data segment count (#3492) +- Fix loader parse block type and calculate dynamic offset for loop args (#3482) +- Fix memory64 handling find_block_addr and execute_main (#3480) +- Fix two issues to make fuzzing test quit earlier (#3471) +- Fix test-wamr-ide CI failure (#3485) +- NuttX: Fix a dbus-related crash on esp32s3 (#3470) +- Clone data segments when specified with load args (#3463) +- Fix codeql compilation error (#3461) +- Fix several typos and fix bh_log calculate mills (#3441) +- ssp_config.h: Fix ifdef for android random api (#3444) +- libc-wasi: Fix a locking botch (#3437) +- Fix fast interp RECOVER_BR_INFO and local set/tee (#3434) +- aot compiler: Fix a type mismatch in compile_op_float_min_max (#3423) +- Correct Exception Handling tag type when GC is enabled (#3413) +- wasm loader: Fix handling if block without op else (#3404) +- ref-types: Correct default value for function local variables (#3397) +- aot compiler: Fix the length type passed to aot_memmove/aot_memset (#3378) +- Fix loader and mini-loader select potential error (#3374) +- Fix aot debugger compilation error on windows (#3370) +- A few native stack detection fixes for macOS/arm64 (#3368) +- Fix ESP32-S3 compiling error (#3359) +- Fix a few native stack address calculations (#3351) + +### Enhancements + +- Modify logging for windows exception handler and remove unused function (#3489) +- posix iwasm: Make the timeout logic a bit more robust (#3478) +- libc-builtin: Enhance buffered print for printf_wrapper (#3460) +- Enhance GC const initializer expression to support nested struct/array new (#3447) +- wasi: Tweak the configuration for nuttx and explain why (#3451) +- NuttX: Replace esp32s3 bits with the OS-provided APIs (#3439) +- Allow not copying the wasm binary in wasm-c-api and not referring to the binary in wasm/aot loader (#3389) +- aot: Make precheck functions use short-call for xtensa (#3418) +- Add wasm_runtime_detect_native_stack_overflow_size (#3355) +- Enhance wasm loader checks for opcode br_table (#3352) + +### Others + +- Bump requests from 2.32.2 to 2.32.3 in /build-scripts (#3494) +- Enable building static library on Android platform (#3488) +- wasm-mutator-fuzz: Generate more kinds of corpus (#3487) +- Correct nuttx repo names (#3484) +- Bump requests from 2.31.0 to 2.32.2 in /build-scripts (#3474) +- wasm-mutator-fuzz: Adapt to oss-fuzz compilation (#3464) +- Add regression tests of BA issue cases (#3462) +- Add malformed test cases (#3459) +- NuttX: Rename a few recently-added nuttx options (#3449) +- wamr-test-suites: Enable AOT multi-module spec tests (#3450) +- Remove install_wasi_sdk from workload preparation script (#3445) +- Add cmake static/shared library build settings (#3443) +- Update spec test to latest commit (#3293) +- Fix typo of WAMR_CONFIGUABLE_BOUNDS_CHECKS (#3424) +- ci/coding_guidelines_check.py: Allow some well-known file names to contain '-' (#3428) +- product-mini/platforms/posix/main.c: Adapt to WASM_MEM_DUAL_BUS_MIRROR (#3427) +- Add comments to global type function declarations (#3431) +- nuttx/esp32s3: Apply ibus/dbus adjustment to internal ram 1 as well (#3421) +- Change WASM_ANYREF to WASM_EXTERNREF (#3426) +- Remove unused macros which were moved to wamr-app-framework (#3425) +- Add WASM_V128 in wasm_valkind_enum (#3412) +- Fix basic example, parameter missmatch between host and wasm (#3415) +- Fix workspaces path in build_wamr.sh (#3414) +- core/iwasm/compilation: Remove stale function prototypes (#3408) +- Add test cases for the requirements of "gc-aot" feature (#3399) +- append_aot_to_wasm.py: Add --ver-str option to emit more info in custom section name (#3398) +- Fix clang compile warnings (#3396) +- Fix some more spelling issues (#3393) +- Fix some spelling issues (#3385) +- samples/native-stack-overflow: Examine native functions with signature (#3382) +- Add some more comments on WASM_STACK_GUARD_SIZE (#3380) +- Fix typo for 'native' in wasm_export.h (#3376) +- CI: Use macos-13 instead of macos-latest (#3366) +- Test more samples in nightly-run CI (#3358) +- Random improvements to samples/native-stack-overflow (#3353) +- Reduce WASM_STACK_GUARD_SIZE a bit for posix-like platforms (#3350) +- doc: Add ADOPTERS.md (#3324) +- Update binary size info in README.md (#3030) +- core/config.h: Bump the default WASM_STACK_GUARD_SIZE (#3344) +- Add unit test suites (#3490) +- Fix internal global getter types (#3495) +- Fix CI build and run unit tests (#3499) + +--- + +## WAMR-2.0.0 + +### Breaking Changes + +- The AOT ABI was changed after GC and memory64 features were introduced: + - Implement GC feature for interpreter, AOT and LLVM-JIT (#3125) + - Implement memory64 for classic interpreter (#3266) + - Always allocate linear memory using mmap (#3052) + - Refactor APIs and data structures as preliminary work for Memory64 (#3209) +- Remove unused argument in wasm_runtime_lookup_function (#3218) +- Separate app-manager and app-framework from WAMR (#3129) + +### New Features + +- Implement GC feature for interpreter, AOT and LLVM-JIT (#3125) +- Implement memory64 for classic interpreter (#3266) +- Add wasi_ephemeral_nn module support (#3241) + +### Bug Fixes + +- EH: Fix broken stack usage calculation (#3121) +- Fix loader check_wasi_abi_compatibility (#3126) +- Fix possible integer overflow in loader target block check (#3133) +- Fix locel.set in polymorphic stack (#3135) +- Fix threads opcodes' boundary check in classic-interp and fast-interp (#3136) +- fast-interp: Fix copy_stack_top_i64 overlap issue (#3146) +- Fix a ubsan complaint "applying zero offset to null pointer" (#3160) +- fast-interp: Fix GC opcode ref.as_non_null (#3156) +- Fix llvm jit push funcref/externref result type issue (#3169) +- Fix wasm loader handling opcode br_table (#3176) +- Fix ref.func opcode check when GC is enabled (#3181) +- lldb_function_to_function_dbi: Fix a null dereference (#3189) +- Fix compilation errors on MinGW (#3217) +- Fix compilation errors on esp-idf platform (#3224) +- Fix aot relocation symbols not found on windows 32-bit (#3231) +- posix_file.c: Correct the dirfd argument that passes to fstatat (#3244) +- Fix compilation errors on zephyr platform (#3255) +- Fix dynamic offset not updated in op_br for block with ret type (#3269) +- aot debug: Fix a NULL dereference (#3274) +- thread mgr: Free aux stack only when it was allocated (#3282) +- interp: Restore context from prev_frame after tail calling a native function (#3283) +- Sync simd opcode definitions spec (#3290) +- Fix posix_fadvise error handling (#3323) +- Fix windows relocation string parsing issue (#3333) + +### Enhancements + +- Zero the memory mapped from os_mmap in NuttX (#3132) +- Use logger for runtime error/debug prints (#3097) +- aot_compile_op_call: Stop setting calling convention explicitly (#3140) +- aot compiler: Place precheck wrapper before the corresponding wrapped function (#3141) +- Fix null pointer access in fast-interp when configurable soft bound check is enabled (#3150) +- Clarify how to verify SGX evidence without an Intel SGX-enabled platform (#3158) +- zephyr: Use zephyr sys_cache instead of CMSIS (#3162) +- VSCode IDE enhancement and readme update (#3172) +- Add vprintf override for android and esp-idf (#3174) +- zephyr: Include math only with minimal libc (#3177) +- zephyr: Implement Alloc_With_System_Allocator (#3179) +- Use indirect call in pre-checker function to avoid relocation in XIP mode (#3142) +- Implement the remaining Windows filesystem functions (#3166) +- Fix LLVM assertion failure and update CONTRIBUTING.md (#3197) +- Allow overriding max memory on module instantiation (#3198) +- Get location info from function indexes in addr2line script (#3206) +- Demangle function names in stack trace when using addr2line script (#3211) +- Refactor APIs and data structures as preliminary work for Memory64 (#3209) +- Allow converting the zero wasm address to native (#3215) +- Small refactor on WASMModuleInstance and fix Go/Python language bindings (#3227) +- Add esp32c6 support (#3234) +- Make android platform's cmake flags configurable (#3239) +- Go binding: Change C.long to C.int64_t when call wasm_runtime_set_wasi_args_ex (#3235) +- Implement apis to set and get the name of a wasm module (#3254) +- Append '\0' to every name string in aot name section (#3249) +- Add cmake flag to control aot intrinsics (#3261) +- Add lock and ref_count for runtime init (#3263) +- nuttx: Migrate NuttX CMake build for WAMR (#3256) +- LLVM 19: Switch to debug records (#3272) +- aot debug: Process lldb_function_to_function_dbi only for C (#3278) +- Fix warnings/issues reported in Windows and by CodeQL/Coverity (#3275) +- Enhance wasm loading with LoadArgs and support module names (#3265) +- Add wamr to esp-idf components registry (#3287) +- zephyr: Add missing pthread library functions (#3291) +- Add more checks in wasm loader (#3300) +- Log warning if growing table failed (#3310) +- Enhance GC subtyping checks (#3317) +- User defined memory allocator for different purposes (#3316) +- Add a comment on WASM_STACK_GUARD_SIZE (#3332) +- Allow executing malloc/free from native in memory64 mode (#3315) +- Add functions to expose module import/export info (#3330) + +### Others + +- Add ARM MacOS to the CI (#3120) +- Download jetstream src from github instead of browserbench.org (#3196) +- Update document to add wamr-rust-sdk introduction (#3204) +- Fix nightly run tsan ASLR issue (#3233) +- Add CodeQL Workflow for Code Security Analysis (#2812) +- Add issue templates (#3248) +- Fix CI error when install packages for macos-14 (#3270) +- Update document for GC, exception handling and memory64 features (#3284) +- Update release CI (#3295) +- Add native-stack-overflow sample (#3321) + +--- + +## WAMR-1.3.2 + +### Breaking Changes + +### New Features + +- Implement Exception Handling for classic interpreter (#3096) + - Use `cmake -DWAMR_BUILD_EXCE_HANDLING=1/0` option to enable/disable + the feature, and by default it is disabled + - It is still in highly experimental stage + +### Bug Fixes + +- Fix build errors when initializing wasm_val_t values with macros (#3007) +- fix(wasm-c-api): Do not clone stack frames if there's no trap (#3008) +- classic-interp: Handle SIMD opcode when JIT is enabled (#3046) +- fast-interp: Fix dynamic offset error issue in else branch (#3058) +- wasm_cluster_destroy_spawned_exec_env: Avoid "invalid exec env" trap (#3068) +- thread-mgr: Fix locking problems around aux stack allocation (#3073) +- cosmopolitan: Update compiler and update platform_internal.h (#3079) +- wasi: Apply wasm_runtime_begin_blocking_op to poll as well (#3080) +- Fix memory/table segment checks in memory.init/table.init (#3081) +- perf profiling: Adjust the calculation of execution time (#3089) +- aot: Fix LLVMSetTailCallKind check (#3099) +- fast-interp: Fix stack recovery for else branch (#3100) +- fast-interp: Fix frame_offset pop order (#3101) +- Fix AOT compilation on MacOS (#3102) +- fast-interp: Fix block with parameter in polymorphic stack issue (#3112) +- Fix read and validation of misc/simd/atomic sub opcodes (#3115) + +### Enhancements + +- Clear compilation warning and dead code (#3002) +- aot debug: Try to use a bit more appropriate file names (#3000) +- Increase default app thread stack size (#3010) +- Rename rwlock_init to avoid conflict (#3016) +- nuttx: Use larger alignment for os_mmap and comment why (#3017) +- Allow using mmap for shared memory if hw bound check is disabled (#3029) +- Don't redefine D_INO if already defined (#3036) +- Enhancements on wasm function execution time statistic (#2985) +- wamr-compiler: Fix non-x86{\_64} host builds (#3037) +- Disable quick aot entry for interp and fast-jit (#3039) +- nuttx: Add option to enable quick aot entry (#3040) +- Set CONFIG_HAS_CAP_ENTER to support posix file api for freertos (#3041) +- Revert "Enable MAP_32BIT for macOS (#2992)" (#3032) +- Enable quick aot entry when hw bound check is disabled (#3044) +- Do not inherit WASM_SUSPEND_FLAG_BLOCKING from the parent thread (#3051) +- wasm_runtime_begin_blocking_op: A comment about usage expectation (#3056) +- Check arguments before calling bh_hash_map_find (#3055) +- Fix aot large model (--size-level=0) with LLVM 18 (#3057) +- Add flag to control Winsocket initialization (#3060) +- nuttx: If STACK_GUARD_SIZE is not set, leave it to config.h (#2927) +- Enhance setting write gs base with cmake variable (#3066) +- aot_reloc_x86_64.c: Suggest to try --size-level=0 as well (#3067) +- Fix some issues reported by CodeQL (#3064) +- Remove a lot of "unused parameter" warnings (#3075) +- Forward log and log level to custom bh_log callback (#3070) +- Fix inconsistent code style in aot_loader.c (#3082) +- freertos: Thread exit more common (#3094) +- Fix windows build error and compilation warnings (#3095) + +### Others + +- Fix nightly-run CI failure (#3014) +- Build samples in debug mode (#3019) +- Remove deprecated tests in language-bindings python (#3018) +- Avoid unused thread_id warning and recompile multi-module sample (#3033) +- samples/terminate: Add a sample to demonstrate wasm_runtime_terminate (#3043) +- Bump NuttX version to 12.4.x in CI (#3047) +- perf_tune.md: Add refine the calling processes between host and wasm (#3065) +- build_wamr.md: Update the document (#3074) +- Fix download link for wasi-sdk (#3077) +- README.md: Fix typo tunning to tuning (#3078) +- Update outdated reference link in multi_module.md (#3092) +- Add comments to suppress warning from clang-tidy (#3088) +- CI: Update version of checkout to suppress warnings (#3093) +- test_wamr.sh: Allow using test script on different platforms (#3098) + +--- + +## WAMR-1.3.1 + +### Breaking Changes + +- In multi-threading, when an exception was thrown in wasm_func_call(), + the trap returned contains the stack frames of the thread where the + exception occurs, but not the stack frames of the main thread. +- Disable emitting custom name section to AOT file with + `wamrc --enable-dump-call-stack` option, instead, use + `wamrc --emit-custom-sections=name` to emit it and make it clear. + +### New Features + +- Enable AOT linux perf support (#2930) + +### Bug Fixes + +- Corrects Zephyr include files for current versions of Zephyr (#2881) +- Fix possible dead lock in wasm_cluster_spawn_exec_env (#2882) +- Handle ambiguous fstflags on fd_filestat_set_times (#2892) +- Fix memory size not updating after growing in interpreter (#2898) +- fixed(freertos): Fix crash when wasm app call pthread_exit(NULL) (#2970) +- fast-jit: Fix const shift and const i64 compare issues (#2969) +- Fix ref.is_null processing in fast-interp loader (#2971) +- simd-128: The input lanes of integer-to-integer narrowing ops should be interpreted as signed (#2850) +- Fix ref.func function declared check in wasm loader (#2972) +- Fix fast-interp polymorphic stack processing (#2974) +- Fix potential recursive lock in pthread_create_wrapper (#2980) +- Fix build failure on esp-idf platform (#2991) +- Return stack frames of crashed thread when using wasm-c-api (#2908) +- Fix compilation error on iOS due to macOS-specific API (#2995) +- Fix a bug when emit the custom name section to aot file (#2987) +- Fix linux-sgx build error when libc-wasi is disabled (#2997) + +### Enhancements + +- fix command-reactor: Look for \_initialize only if \_start not found (#2891) +- Refactor reloc symbols for riscv (#2894) +- Avoid memory import failure when wasi-threads is enabled (#2893) +- interpreter: Simplify memory.grow a bit (#2899) +- Avoid reporting timestamp if custom logger is used (#2905) +- Expose API to set log level in embedder (#2907) +- Add a script to translate jitted function names in flamegraph (#2906) +- Refine wasm-c-api wasm_func_call (#2922) +- Add VectorCombine pass for JIT and AOT (#2923) +- Enable wasm_runtime_terminate for single-threading (#2924) +- nuttx: Add CONFIG_INTERPRETERS_WAMR_DEBUG_AOT (#2929) +- Allow to control built-in libraries for wamrc from command line options (#2928) +- Fix a bug that appends '\_precheck' to aot_func (#2936) +- freertos: Add os_cond_broadcast for pthread wrapper (#2937) +- Append .aot to .wasm as a custom section named "aot" (#2933) +- fix(sgx-ra): Fix building when enclave is built without librats ahead (#2968) +- Refine LLVM JIT function call process (#2925) +- Refine AOT function call process (#2940) +- Allow to set segue flags for wasm-c-api JIT (#2926) +- freertos: Minor changes for freertos libc_wasi build adaption (#2973) +- freertos: Change ssp_config.h due to clock_nanosleep() not supported in freertos (#2979) +- aot compiler: Some updates for LLVM 18 (#2981) +- Enable MAP_32BIT for macOS (#2992) +- Register quick call entries to speedup the aot/jit func call process (#2978) +- Refine AOT/JIT code call wasm-c-api import process (#2982) + +### Others + +- compilation_on_nuttx.yml: Use docker image to simplify env setup (#2878) +- samples/spawn-thread: Disable libc and pthread (#2883) +- Add arm64 to nuttx compilation test (#2886) +- samples/spawn-thread: Tweak to expose a bug (#2888) +- Fix typo in CI config and suppress STORE_U8 in TSAN (#2802) +- Using docker image for nuttx spectest (#2887) +- doc: Separate source_debugging.md into two files (#2932) +- doc/build_wasm_app.md: Add a note about aot abi compatibility (#2993) + +--- + +## WAMR-1.3.0 + +### Breaking Changes + +- Abstract POSIX filesystem functions (#2585) + - Change API wasm_runtime_set_wasi_args_ex's arguments + `int stdinfd/stdoutfd/stderrfd` to `int64_t stdinfd/stdoutfd/stderrfd` +- core/iwasm: Support mapped file system access on non-libuv WASI (#2628) + - Enable mapping host directories to guest directories by parsing + the `map_dir_list` argument in API `wasm_runtime_init_wasi` for libc-wasi +- Support muti-module for AOT mode (#2482) + - Add argument `package_type_t module_type` for module_reader callback +- Generate jitdump to support linux perf for LLVM JIT (#2788) + - Add a field `bool linux_perf_support` in RuntimeInitArgs +- Remove provision of unnecessary fd rights (#2579) +- libc-wasi: Conditionally support SYNC flags (#2581) + +### New Features + +- Support muti-module for AOT mode (#2482) +- Implement libc-wasi for Windows platform (#2740) +- Implement module instance context APIs (#2436) +- Implement async termination of blocking thread (#2516) +- Generate jitdump to support linux perf for LLVM JIT (#2788) +- Add Cosmopolitan Libc Platform (#2598) + +### Bug Fixes + +- sgx-ra: Disable the building of samples (#2507) +- Handle a return from wasi \_start function correctly (#2529) +- fd_object_release: Preserve errno (#2535) +- Fix build error with ancient GCC (4.8) (#2553) +- Fix compiling error for RT-Thread (#2569) +- Fix potential unaligned store issue when extra return value is v128 (#2583) +- Fix loader push_pop_frame_ref_offset (#2590) +- Fix compilation error on Android platform (#2594) +- Ignore handling SIG_DFL/SIG_IGN for previous sig action (#2589) +- Fix nightly run sanitizer error in Fast JIT (#2601) +- Check ValueKind before extracting a constant int value (#2595) +- Patch implementations of vfbinop(min,max,pmin,pax) (#2584) +- Improve stack trace dump and fix coding guideline CI (#2599) +- aot_resolve_stack_sizes: Disable the size check for now (#2608) +- Remove module instance from hashmap in wasi_nn_destroy (#2613) +- Fix label index out-of-range references in op_br_table_cache (#2615) +- Fix compilation of shift opcodes on x86_64 and i386 architectures (#2619) +- Fix potential issue in aot compiler when translating block opcodes (#2622) +- Use another default pipeline when opt-level is 0 (#2624) +- Fix AOT shift operations for indirect constants (#2627) +- Fix fast-interp "pre-compiled label offset out of range" issue (#2659) +- Revert "Strip static and shared libraries of iwasm to reduce the binary size (#2431)" (#2669) +- Fix windows compilation on C++20 (#2670) +- Fix fast-jit f32/f64 truncate to i32/i64 (#2671) +- Fix use getrandom on cosmopolitan libc (#2674) +- Fix repeatedly initialize shared memory data and protect the memory's fields (#2673) +- Minor fixes for Go bindings (#2676) +- Fix issues reported by Coverity (#2681) +- Add more buffer boundary checks in wasm loader (#2734) +- Grab cluster->lock when modifying exec_env->module_inst (#2685) +- Fix CMSIS import with Zephyr 3.4+ (#2744) +- Fix log messages in Zephyr example (#2761) +- Fix fast-jit callnative translation (#2765) +- aot compiler: Disable musttail for thumb (#2771) +- Fix data/elem drop (#2747) +- Fix formatting in aot_dump_perf_profiling (#2796) +- Fix formatting in wasm_dump_perf_profiling (#2799) +- Fix memory.init opcode issue in fast-interp (#2798) +- aot compiler: Fix handle next reachable if block (#2793) +- Fix configurable bounds checks typo (#2809) +- Attestation: Free JSON from the Wasm module heap (#2803) +- Update Zephyr support to v3.5.0 and make instructions generic to boards (#2805) +- Return error when shutdown() fails (#2801) +- iwasm: Print help when meeting unknown cmd options (#2824) +- Fix fast-jit accessing shared memory's fields issue (#2841) +- Fix wasm loader handle op_br_table and op_drop (#2864) +- Fix block with type issue in fast interp (#2866) +- Fix float argument handling for riscv32 ilp32d (#2871) +- Portably handle fd_advise on directory fd (#2875) +- Fix sample basic intToStr was called with wrong length (#2876) + +### Enhancements + +- Implement strict validation of thread IDs according to the specification (#2521) +- Stop abusing shared memory lock to protect exception (#2509) +- Implement os_usleep for posix (#2517) +- set_exception_visitor: Remove the special case for wasi proc exit (#2525) +- Revert "Return error when exception was raised after main thread finishes" (#2524) +- libc-wasi: Remove unused code (#2528) +- Add callback to handle memory.grow failures (#2522) +- Add context to enlarge memory error callback (#2546) +- Add ARM aeabi symbol for clearing memory content in a specific range (#2531) +- Unifdef -U WASMTIME_SSP_STATIC_CURFDS (#2533) +- Fix typo for IP address buffer (#2532) +- Add an API to terminate instance (#2538) +- Add user to enlarge memory error callback (#2546) +- runtest.py: Show accurate case amount in summary (#2549) +- Allow using custom signal handler from non-main thread (#2551) +- Return \_\_WASI_EINVAL from fd_prestat_dir_name (#2580) +- Support AOT compiler with LLVM 17 (#2567) +- Add support for closing/renumbering preopen fds (#2578) +- Enable AOT usage on M1 mac (#2618) +- core/iwasm: Support mapped file system access on non-libuv WASI (#2628) +- Enable MASM automatically in runtime_lib.cmake (#2634) +- Abstract POSIX filesystem functions (#2585) +- Implement wasi clock_time/clock_res get (#2637) +- Fix several typo/warning/unused-code issues (#2655) +- Partial windows filesystem implementation (#2657) +- Apply no_sanitize_address for clang compiler in several places (#2663) +- Refactor clock functions to use WASI types (#2666) +- Refine lock/unlock shared memory (#2682) +- Fix several AOT compiler issues (#2697) +- Fix AOT compiler simd shift opcodes (#2715) +- Fix invalid use of jit_reg_is_const_val in fast-jit (#2718) +- Use user defined malloc/free functions for user defined memory allocator (#2717) +- Move WASI types into separate header (#2724) +- Provide default vprintf on UWP (#2725) +- Fix typo in Zephyr simple example (#2738) +- Fix switch-case fallthrough compilation warning (#2753) +- Add eabihf ABI support and set vendor-sys of bare-metal targets (#2745) +- Return uint32 from WASI functions (#2749) +- Add compilation flag to enable/disable heap corruption check (#2766) +- Extend os_mmap to support map file from fd (#2763) +- Fix printing ref.extern addresses in wasm_application.c (#2774) +- Remove unused JitBitmap (#2775) +- Use next generation crypto API on Windows (#2769) +- More precise help info of enabled targets for wamrc (#2783) +- Refine atomic operation flags in bh_atomic.h (#2780) +- Fix comment in WAMR_MEM_DUAL_BUS_MIRROR (#2791) +- Fix return type in wasm_loader_get_custom_section (#2794) +- Add support for custom sections in nuttx (#2795) +- Change is_shared_memory type from bool to uint8 (#2800) +- Fix typos in zephyr platform struct descriptions (#2818) +- Access linear memory size atomically (#2834) +- Output warning and quit if import/export name contains '\00' (#2806) +- Use wasm_config_t to pass private configuration to wasm_engine_new (#2837) +- core/iwasm/interpreter/wasm_loader.c: remove an extra validation (#2845) +- Don't add "+d" to riscv cpu features if already given (#2855) +- Fix compilation warnings on Windows (#2868) + +### Others + +- Add mutex stress test (#2472) +- Add unit tests for the tid allocator (#2519) +- Add support for running tests on apple M1 macs (#2554) +- export_native_api.md: Add a note about thread termination (#2572) +- test_wamr.sh: Print a bit more meaningful message (#2574) +- run_wasi_tests.sh: Provide stdin by ourselves (#2576) +- Fix a few issues in "run_wasi_tests.sh: provide stdin by ourselves" (#2582) +- Fix compile error of tsf benchmark (#2588) +- test_wamr.sh: Bump wasi-testsuite version (#2568) +- samples/inst-context-threads: Add a brief explanation (#2592) +- doc/memory_tune.md: "remove malloc" hack is not relevant to wasi-threads (#2603) +- Refactor stress tests to make them runnable in reactor mode (#2614) +- Run rust tests from wasi-testsuite (#2484) +- spec-test-script: Fix NaN comparision between v128 values (#2605) +- CI: Enable testing AOT multi-module feature (#2621) +- Vote for nomination of Woods, Chris and Trenner, Thomas as TSC members (#2638) +- Add tsan for fast interp and aot (#2679) +- Enable WASI tests on Windows CI (#2699) +- docs: Fix typo in export native APIs doc (#2750) +- Update RISC-V compilers in Nuttx compilation CI and spec test CI (#2756) +- Enable more LLVM backends for the release wamrc binary (#2778) +- Disable FPU in NuttX spec test (#2781) +- Fix broken links in app-mgr README.md (#2786) +- Fix build error of libsodium benchmark (#2792) +- Fix wamr-test-suites script for macos (#2819) +- Run spec test for classic/fast-interp in NuttX CI (#2817) +- test_wamr.sh: Don't bother to build shared library (#2844) +- doc/build_wamr.md: Fix links to RISC-V named ABIs (#2852) +- Fix typos of CIDR in docs and help text (#2851) +- Enable spectest on riscv64 (#2843) +- Update FPU configuration in spec_test_on_nuttx.yml (#2856) + +--- + +## WAMR-1.2.3 + +### Breaking Changes + +- Increase default native stack size (#2332) + +### New Features + +- Implement the segue optimization for LLVM AOT/JIT (#2230) +- Implement AOT static PGO (#2243) +- Enable static PGO for Linux SGX (#2270) +- Add Rust Formatters to Debugger (Vector, Map etc.) (#2219) + +### Bug Fixes + +- The Python language-binding needs python>=3.9 (#2228) +- aot_compile_op_call: Remove a wrong optimization (#2233) +- Fix typo in samples/ref-types (#2236) +- Update thread proposal ignore cases (#2246) +- Disable writting GS register on linux-sgx platform (#2255) +- Fix compile error of wamrc with llvm-13/llvm-14 (#2261) +- aot/jit: Set module layout (#2260) +- Fix build error with LLVM 16 (#2259) +- spec-test-script: Disable conversions.wast on i386 (#2269) +- Fix a heap corruption bug in ems realloc (#2279) +- Fix fast-interp issue of LAST_OP_OUTPUT_I32/64 check (#2295) +- Fix wamrc build issues with LLVM 13 and LLVM 16 (#2313) +- aot: Move stack_sizes table to a dedicated section (#2317) +- product-mini/platforms/linux: Mark vmlib POSITION_INDEPENDENT_CODE (#2323) +- aot: Avoid possible relocations around "stack_sizes" for XIP mode (#2322) +- Avoid switch lowering to lookup tables for XIP (#2339) +- Fix typo in zephyr's Dockerfile.old (#2354) +- Fix typo (dwarf) in the codebase (#2367) +- Implement suspend flags as atomic variable (#2361) +- Fix llvm jit failed to lookup aot_stack_sizes symbol issue (#2384) +- Fix some check issues on table operations (#2392) +- Fix ExpandMemoryOpPass doesn't work properly (#2399) +- Fix non-builtin BH_ATOMIC_32_FETCH_OR and BH_ATOMIC_32_FETCH_AND (#2400) +- Fix wasi-sockets tests (#2389) +- Fix result arity check on select_t opcode (#2406) +- Re-organize intrinsics in aot_reloc_riscv.c to fix some FPU issues (#2414) +- Fix lib-pthread issues (#2410) +- Fix typo in test_wamr.sh (#2421) +- Fix memory sharing (#2415) +- wasm_export.h: Fix struct wasm_val_t (#2435) +- Fix typos in wamrc print_help() (#2442) +- iwasm: Fix native lib cleanup after error occurs (#2443) +- Correct --heap-size option in messages (#2458) +- wasm_instantiate: Fix a potential integer overflow issue (#2459) +- Fix windows link error and clear windows warnings (#2463) +- aot: Disable musttail for mips (#2457) +- Fix opcode overwrite issue in fast interp (#2476) +- wamrc: Fix windows relocation to `aot_func_internal#n` (#2474) +- Fix windows AOT hw bound check (#2475) +- Fix typo in aot_emit_aot_file.c (#2478) + +### Enhancements + +- A few changes related to WAMRC_LLC_COMPILER (#2218) +- Enhance linux-sgx CI (#2102) +- Add asan and ubsan to WAMR CI (#2161) +- Update doc on WAMR_DISABLE_HW_BOUND_CHECK 32-bit (#2262) +- wamrc: Add an incompatibility note in the help message (#2276) +- Add cmake variable to disable writing gs register (#2284) +- Make hmu_tree_node 4 byte aligned to reduce compiler warning (#2268) +- Appease unused warning on min_uint64 (#2277) +- Fix format warning by PRIu32 in [wasm|aot] dump call stack (#2251) +- Fix a compile warning due to missing include (#2293) +- Fix dockerfile linter warnings (#2291) +- Enable windows x86-32 AOT relocations (#2285) +- wamr-ide: Add vscode extension tests (#2292) +- AOT/JIT native stack bound check improvement (#2244) +- Add retries to flaky step in nightly run CI (#2306) +- Use system libuv if available (#1861) +- wasi-nn: Simplify cmake and headers' location (#2308) +- wasi-nn: Improve tests paths for local dev (#2309) +- aot: Implement a few more relocation types for riscv (#2318) +- wasi-nn: Add support of wasi-nn as shared lib (#2310) +- Add a few more assertions on structures to which aot abi is sensitive (#2326) +- Fix sanitizer errors in posix socket (#2331) +- Add "--xip" option for wamrc (#2336) +- Add "--enable-llvm-passes=" option to wamrc (#2335) +- Make memory access boundary check behavior configurable (#2289) +- Migrate ExpandMemoryOpPass to llvm new pass manager (#2334) +- Allow defining hints without exact socket type or address family (#2337) +- wamrc: Warn on text relocations for XIP (#2340) +- Add scripts to validate lldb source debugger (#2150) +- Add docker file to fix Zephy ESP32 linking issue (#2314) +- Add "--native-lib=" option to wamrc (#2342) +- Fix unused warnings on disable_bounds_checks (#2347) +- Add "--enable-builtin-intrinsics=" option to wamrc (#2341) +- nuttx: Add a kconfig for wasi-threads (#2343) +- iwasm: Disable app heap by default if wasi is enabled (#2346) +- Fix some static scan issues (#2362) +- Bring up WAMR on esp32-s3 device (#2348) +- ESP-IDF platform supports to load AOT to PSRAM and run it (#2385) +- Add hadolint CI for Dockerfile linting (#2387) +- Move generic parts of wasm_suspend_flags.h to bh_atomic.h (#2393) +- bh_atomic.h: Add comments (#2398) +- bh_atomic.h: Add BH_ATOMIC_32_FETCH_ADD/BH_ATOMIC_32_FETCH_SUB (#2408) +- Update libuv version to v1.46.0 (#2405) +- Remove a few unused functions (#2409) +- Add initial stress test (#2364) +- Move wasm_runtime_destroy_wasi and wasi_nn_destroy calls together (#2418) +- embed_wamr.md: Improvements about threads (#2420) +- Add runtime inited checks in Enclave command handlings to improve security (#2416) +- Add some relocation symbols for xtensa target (#2422) +- Remove unnecessary and extra zero length check in mem functions' macro (#2428) +- Introduce WASMModuleInstanceExtraCommon (#2429) +- Strip static and shared libraries of iwasm to reduce the binary size (#2431) +- Auto-check wrgsbase in cmake script (#2437) +- iwasm: call native lib init/deinit if exists (#2439) +- wasi-nn: Support uint8 quantized networks (#2433) +- Implement `wasm_externref_objdel` and `wasm_externref_set_cleanup` (#2455) +- wasi-nn: Improve TPU support (#2447) +- wamr-python: Enable debugging WASM and grant dir access (#2449) +- Build wasi-libc from source in WAMR CI (#2465) +- wamrc: More friendly to print help info (#2451) +- Add another wamr test (#2411) +- Fix issues reported by Coverity and clear windows warnings (#2467) +- Clone the input binary during wasm_module_validate (#2483) + +### Others + +- Nuttx CI: Ignore the expired certificate for riscv gcc toolchain (#2222) +- core/iwasm/compilation: constify a bit (#2223) +- Bump requests from 2.28.2 to 2.31.0 in /build-scripts (#2229) +- dwarf_extractor: Constify a bit (#2278) +- AOTFuncContext: Remove a stale comment (#2283) +- Add performance tunning document (#2286) +- Reduce CI jobs number (#2296) +- CI: Update used node version to 16 (#2303) +- Update Docker image for latest version of external libraries & tools (#2374) +- Upgrade cJSON version to v1.7.16 (#2404) +- Upgrade XNNPACK workload (#2394) +- Build more benchmarks in workload XNNPACK (#2417) +- Upgrade SGX-RA integration for 0.1.2 and Ubuntu 20.04 (#2454) +- Add sample pre-commit hook (#2470) + +--- + +## WAMR-1.2.2 + +### Breaking Changes + +### New Features + +- Implement Fast JIT multi-threading feature (#2134) + +### Bug Fixes + +- Update request.ts wasm_response_send signature (#2122) +- Fix ems allocator unaligned memory access on riscv64 (#2140) +- libc_wasi_wrapper.c: Fix min func issue for size_t < 8 bytes on some platforms (#2152) +- Fix three multi-threading and wasm-c-api-imports issues (#2173) +- Fix build polybench benchmark error with wasi-sdk-19.0 (#2187) +- Fix wamr-ide debugger ignoring launch config (#2155) + +### Enhancements + +- Add test for validating linear memory size updates (#2078) +- Update Zephyr docs to remove unsupported west subcommand (#2128) +- Update messages/comments to refer the new place of the version definition (#2133) +- build_wamr_lldb.yml: sync lldb build options between ubuntu and macos (#2132) +- build_wamr_vscode_ext.yml: vsce publish only on the official repo (#2130) +- VSCode-Extension: Download lldb built for ubuntu 20.04 (#2139) +- Avoid re-installing if Tensorflow is already installed for WASI-NN (#2148) +- wamrc: Add --stack-usage option (#2158) +- Fix URL in language-bindings/python/README.md (#2166) +- Fix URL in embed_wamr.md (#2165) +- Fix URL in README.md (#2168) +- Return error when exception was raised after main thread finishes (#2169) +- wasi-nn: Add external delegation to support several NPU/GPU (#2162) +- Update document for iwasm/wamrc dependent packages (#2183) +- Use a manual flag to disable clock_nanosleep on the unsupported platforms (#2176) +- Fix compile warnings on windows platform (#2208) + +### Others + +- CI: Add ubsan checks to samples/wasm-c-api (#2147) +- CI: More precise trigger paths for github actions (#2157) + +--- + +## WAMR-1.2.1 + +### Breaking Changes + +### New Features + +### Bug Fixes + +- libc-wasi/posix.c: Fix POLL{RD,WR}NORM in uClibc (#2069) +- Fix bh_assert for 64-bit platforms (#2071) +- wamr-ide: Modify Dockerfile to update base image version and fix build issue (#2068) +- Fix module_malloc/module_free issues (#2072) +- Fix use after free when dumping call stack (#2084) +- Fix compilation errors of workload xnnpack and meshoptimizer (#2081) +- Fix typo in Fast JIT's BUILD_COND_BR Macro (#2092) +- Fix sanitizer pointer overflow warning when perform pointer arithmetic (#2098) +- Update sample workload tensorflow (#2101) +- Fix ref.func forward-declared function check (#2099) +- Fix interpreter read linear memory size for multi-threading (#2088) + +### Enhancements + +- Limit the minimal size of bh_hashmap (#2073) +- Bump tensorflow to 2.11.1 in /core/iwasm/libraries/wasi-nn/test (#2061) +- Bump tensorflow to 2.11.1 in install_tensorflow.sh (#2076) +- Add support for universal binaries on OSX (#2060) +- Update documents (#2100) + +### Others + +- spectest/nuttx: Increase stack size of iwasm task (#2082) +- ci: Refactor windows build definition (#2087) +- ci: Enable WASI threads in CI (#2086) +- Use wasi-sdk-20 to build wasi-threads cases in CI (#2095) + +--- + +## WAMR-1.2.0 + +### Breaking Changes + +### New Features + +- Implement two-level Multi-tier JIT engine: tier-up from Fast JIT to LLVM JIT to get quick cold startup and better performance +- Enable running mode control for runtime, wasm module instance and iwasm +- Implement wasi-threads feature +- Upgrade toolkits: upgrade to llvm-15.0, wasi-sdk-19.0, emsdk-3.1.28 and so on +- Port WAMR to the FreeBSD platform +- Refactor wasi-nn to simplify the support for multiple frameworks +- wasi-nn: Enable GPU support +- wasi-nn: Support multiple TFLite models +- Add WAMR API bindings in Python +- Add libsodium benchmark + +### Bug Fixes + +- Fix wasm-c-api import func link issue in wasm_instance_new +- Fix watchpoint segfault when using debug interp without server +- libc-wasi: Fix spurious poll timeout +- Fix typo verify_module in aot_compiler.c +- Fix failure about preopen of reactor modules +- Fix equal check in AOT XIP float cmp intrinsic +- Fix issue of resolving func name in custom name section +- Fix go language binding build on macos arm64 +- Prevent undefined behavior from c_api_func_imports == NULL +- Fix potential block issue in source debugger +- SGX IPFS: Fix a segfault and support seeking beyond the end of files while using SEEK_CUR/SEEK_END +- Fix undef error about WAMR_BUILD_MEMORY_PROFILING +- Fix jit memory overwritten after instance deinstantiate +- Fix stack alignment issue on ia32 +- Fix explicit casts and types in espidf_socket.c +- Fix potential integer overflow issue in wasm-c-api +- Fix libc-wasi build failure when using clang +- Fix wamrapi python binding for darwin +- Fix getting port issue in posix os_socket_bind +- Fix key error in build_llvm.py +- nuttx: Add missing pthread.h header +- Fix os_socket_addr_resolve() for IPv6 +- Enhance/Fix sample socket-api and workload +- Fix fast-jit build error +- Fix dead lock in source debugger +- fix debugger: Set termination flags also when in debug mode + +### Enhancements + +- Add WAMR-IDE vscode extension to the Visual Studio Marketplace +- Refine Windows thread waiting list operations +- Improve wasm-c-api instantiation-time linking +- Enable platform support for esp-idf v5.0.1 +- Readme refactoring +- Add architecture diagram for wasm function +- Add architecture document for wasm export +- Add architecture diagram for wasm globals and classic-interp stack frame +- Use boringssl instead of openssl to implement wasm cache loading +- Implement i32.rem_s and i32.rem_u intrinsic +- Perfect the codebase for wamr-ide +- Remove unnecessary ret value control when spec test is enabled +- Use float version library routine for XIP aot_intrinsic_xxx APIs +- Register missing symbols for f32 to 64 bit integer conversion +- Report error in instantiation when meeting unlinked import globals +- Add more types and APIs for attr_container +- Simplify fcmp intrinsic logic for AOT/XIP +- Add some missing macros for int literals in wamr-sdk libc-builtin-sysroot stdint.h +- nuttx: Mock socket APIs if NET is disabled +- Main thread spread exception when thread-mgr is enabled +- Implement opcode atomic.wait and atomic.notify for Fast JIT +- Add docker images auto check and setup support for WAMR-IDE +- Make memory profiling show native stack usage +- Enable gcc-4.8 compilation +- Enable specifying out-of-source platform configuration cmake file +- Add gh api call for fetching llvm version (#1942) Fixes +- Don't terminate other threads when create thread failed +- Modify poll_oneoff in libc-wasi to make it interruptible +- Expose wasm_runtime_call_indirect +- Make a workaround for EGO when fstat returns NOT_SUPPORT +- Re-org calling post instantiation functions +- Enable custom llvm build flags +- support SSH for git clone llvm +- Support dump call stack on exception and dump call stack on nuttx +- Update document for source debugging +- Document some info about estimating memory usage +- Document the summary of two pthread implementations +- Refine aot compiler check suspend_flags and fix issue of multi-tier jit + +### Others + +- Enable XIP in CI daily test +- Integrate wasi test suite to wamr-test-suites and CI +- Add CI for wasi-threads tests +- Update CIs and documents to make naming of generated binaries consist +- Enable CI wasi test suite for x86-32 classic/fast interpreter +- CI: Enable libc-wasi compilation test on NuttX +- CI: Enable Multi-tier JIT by default for released iwasm binary +- Enable CI build for gcc 4.8 on linux + +--- + +## WAMR-1.1.2 + +### Breaking Changes + +- Remove the LLVM MCJIT mode, replace it with LLVM ORC JIT eager mode +- Add option to pass user data to the allocator functions of RuntimeInitArgs +- Change how iwasm returns: + - return 1 if an exception was thrown, else + - return the wasi exit code if the wasm app is a wasi app, else + - keep the same behavior as before +- Enable bulk memory by default + +### New Features + +- Add control for the native stack check with hardware trap +- Add memory watchpoint support to debugger +- Add wasm_module_obtain() to clone wasm_module_t +- Implement Fast JIT dump call stack and perf profiling +- esp-idf: Add socket support for esp-idf platform + +### Bug Fixes + +- Fix XIP issue caused by rem_s on RISC-V +- Fix XIP issues of fp to int cast and int rem/div +- Fix missing float cmp for XIP +- Correct the arch name for armv7a on NuttX +- Fix issue of restoring wasm operand stack +- Fix issue of thumb relocation R_ARM_THM_MOVT_ABS +- Fix fast jit issue of translating opcode i32.rem_s/i64.rem_s +- Fix interp/fast-jit float min/max issues +- Fix missing intrinsics for risc-v which were reported by spec test +- wasm-c-api: Fix init/destroy thread env multiple times issue +- Fix wasm-c-api import func link issue in wasm_instance_new +- Fix sample ref-types/wasm-c-api build error with wat2wasm low version +- Fix zephyr sample build errors +- Fix source debugger error handling: continue executing when detached +- Fix scenario where the timeout for atomic wait is set to negative number +- Fix link cxx object file error when building wamrc for docker image +- Fix XIP issue of handling 64-bit const in 32-bit target + +### Enhancements + +- Refactor the layout of interpreter and AOT module instance +- Refactor LLVM JIT: remove mcjit and legacy pass manager, upgrade to ORCv2 JIT +- Refine Fast JIT call indirect and call native process +- Refine Fast JIT accessing memory/table instance and global data +- Refine AOT exception check when function return +- Enable source debugger reconnection +- Add wasm_runtime_get_wasi_exit_code +- linux-sgx: Use non-destructive modes for opening files using SGX IPFS +- Add wasm_runtime_unregister_natives +- Implement invokeNative asm code for MinGW +- Add wamr Blog link and Gitbook link to readme +- Remove unnecessary app heap memory clean operations to reduce process RSS +- Normalize how the global heap pool is configured across iwasm apps +- Refine the stack frame size check in interpreter +- Enlarge the default wasm operand stack size to 64KB +- Use cmake POSITION_INDEPENDENT_CODE instead of hardcoding -pie -fPIE +- Implement R*ARM_THM_MOVT*[ABS|REPL] for thumb +- Suppress the warnings when building with GCC11 +- samples/native-lib: Add a bit more complicated example +- Add mutex initializer for wasm-c-api engine operations +- XIP adaptation for xtensa platform +- Update libuv version number +- Remove an improper assumption when creating wasm_trap +- Avoid initialize LLVM repeatedly +- linux-sgx: Improve the remote attestation +- linux-sgx: Improve the documentation of SGX-RA sample +- linux-sgx: Allow to open files with arbitrary paths in the sandbox using IPFS +- Avoid raising exception when debugging with VSCode +- wamr-test-suites: Update runtest.py to support python3 +- Enable Nuttx spec test option and register aot symbols +- Use wabt binary instead of building from source in spec test +- nuttx: Enable ref types by Kconfig +- Update xtensa LLVM version to 15.x +- Add bh_print_proc_mem() to dump memory info of current process +- Create trap for error message when wasm_instance_new fails +- wamr-test-suites: Add support for ARM/RISCV by QEMU +- Enable to compile WAMR on platforms that don't support IPV6 +- Fix warnings in the posix socket implementation +- Update document for MacOS compilation +- Install patched LLDB on vscode extension activation +- Add ARM aeabi memcpy/memmove/memset symbols for AOT bulk memory ops +- Enable wasm cache loading in wasm-c-api + +### Others + +- Add CIs to release new version and publish binary files +- Add more compilation groups of fast jit into CI +- Enable spec test on nuttx and daily run it + +--- + +## WAMR-1.1.1 + +- Implement Linux SGX socket API getpeername, recvfrom and sendto +- Implement Linux SGX POSIX calls based on getsockname and set/getbool +- Integrate WASI-NN into WAMR: support TensorFlow/CPU/F32 in the first stage +- Add timeout send/recv and multicast client/server socket examples +- Support cross building and linking LLVM shared libs for wamrc +- Add darwin support for app_framework +- Add ios support for product-mini +- Update export_native_api.md: Relax the "ground rule" +- wasm_export.h: Add comments on wasm_runtime_register_natives +- Remove unused wasm_runtime_is_module_registered +- samples/multi-module: Examine module registration a bit +- samples/native-lib: Fix exec_env type +- Fix Linux SGX directional OCALL parameter for getsockname +- Fix threads issue to enable running threads spec proposal test cases +- Fix the "register native with iwasm" stuff for macOS +- Fix issues in assemblyscript lib +- Wrap wasi_socket_ext api with extern "C" to fix link failure with cxx project +- Fix invalid size of memory allocated in wasi init +- posix_thread.c: Avoid sem_getvalue deprecation warning on macOS + +--- + +## WAMR-1.1.0 + +- Extend support for Socket API: + - Implement IPv6 (along with IPv4) for all the socket-related operations + - Enable resolving host name IP address by adding a host call to WASI + - Implement a security feature for controlling what domains are allowed to be resolved + - Allow configuring socket options by adding host calls to WASI for setting and reading the options + - Enable connection-less communication between hosts by adding host calls to WASI for sending + - data directly to a given address and receiving messages from a specific address + - Fix verification of the address in the address pool + - Add more samples and update the documents + - Implement SGX IPFS as POSIX backend for file interaction for linux-sgx +- Integrates the Intel SGX feature called Intel Protection File System Library (IPFS) into the runtime + to create, operate and delete files inside the enclave, while guaranteeing the confidentiality and + integrity of the data persisted +- Make libc-builtin buffered printf be a common feature +- Enable passing through arguments for build_llvm.sh +- Update \_\_wasi_sock_accept signature to match wasi_snapshot_preview1 +- Enable build wasi_socket_ext.c with both clang and clang++ +- Add check for code section size, fix interpreter float operations +- Prevent an already detached thread from being detached again for thread manager +- Fix several issues related to AOT debug and update source_debugging.md +- Fix Windows/MSVC build issues and compile warnings +- Fix wasm loader: function sub local count can be 0 +- Fix crash in dumping call stack when the AOT file doesn't contain custom name section +- Fix Dockerfile lint errors and suppress hadolint warnings for pinning versions part +- Fix Fast JIT issues reported by instrument test +- Fix link error for ESP-IDF 4.4.2 +- Fix syntax errors and undefined names in Python code +- Fix issues reported by Coverity +- Fix Go binding build error +- Fix a wrongly named parameter and enhance the docs in bh_hashmap.h + +--- + +## WAMR-1.0.0 + +- Implement Python language binding +- Implement Go language binding +- Implement Fast JIT engine +- Implement hw bound check for interpreter and Fast JIT +- Enable the semantic version mechanism for WAMR +- Implement POSIX semaphore support for linux platform +- Implement SGX getrandom/getentropy without ocall +- Enable remote attestation by librats in SGX mode +- Upgrade WAMR-IDE and source debugging +- Support print exception info in source debugger +- Support emit specified custom sections into AoT file +- Refactor spec test script and CI workflows +- Support integrate 3rd-party toolchains into wamrc +- Enable dump call stack to a buffer +- Enable aot compiler with llvm-14/15 +- Don't suppress prev signal handler in hw bound check +- Remove unnecessary memset after mmap +- Refine wasm\*runtime_call_wasm_a/v +- Enable app management and thread support for esp32 arch +- Enable libc-wasi support for esp-idf arch +- Implement xtensa XIP +- Enable memory leak check +- Introduce basic CI for nuttx +- Update documents +- Fix module_realloc with NULL ptr issue +- Fix a typo of macro in wasm_application.c +- nuttx: add CONFIG_INTERPRETERS_WAMR_PERF_PROFILING +- aot_reloc_xtensa.c: define \_\_packed if not available +- Fix bh_vector extend_vector not locked issue +- Enable build libc-wasi for nuttx +- Fix typo in embed_wamr.md +- Fix drop opcode issue in fast interpreter +- Fix typos in wasm_mini_loader.c +- Fix issues reported by Coverity and Klocwork +- Add missing aot relocation symbols for xtensa target +- Add arc compiler-rt functions and reloc type for mwdt +- Fix get invokeNative float ret value issue with clang compiler +- Make robust on choosing target assumption for X86_32 support +- Fix an issue of wasm_cluster_spread_custom_data when called before exec +- Fix socket api verification of addresses in the address pool +- Add API wasm_runtime_set_module_inst +- Set noexecstack CXX link flags for wamrc +- Add import subtyping validation +- Fix libc-wasi/uvwasi poll/environ_get issues +- Add missing symbol for aot_reloc_arc.c +- Add a dev docker container for WAMR repo +- Fix dump call stack issue in interpreter +- Fix windows thread data issue and enhance windows os_mmap +- Support custom stack guard size +- Implement i64.div and i64.rem intrinsics +- Let iwasm return non-zero value when running failed +- Reserve one pointer size for fast-interp code_compiled_size +- Enable libc-wasi support for esp-idf +- Expose wasm_runtime_get_exec_env_singleton to the API users +- Normalize wasm types to refine interpreter call_indirect +- Remove unused wasm_runtime_create_exec_env_and_call_wasm +- Fix linear memory page count issues +- debug: Retire wasm_debug\*(get|set)\_engine_active mechanism +- wasm_application.c: Do not start debug instance automatically +- Fix typo in simd_conversions.c +- nuttx: Add CONFIG_INTERPRETERS_WAMR_DEBUG_INTERP +- Add a new API to get free memory in memory pool +- Fix multi-module and some other issues +- Fix build issue of the meshoptimizer workload +- Fix build error on alios platform + +--- + +## WAMR-X.Y.Z + +### Breaking Changes + +### New Features + +### Bug Fixes + +### Enhancements + +### Others + +--- diff --git a/SConscript b/SConscript new file mode 100644 index 0000000000..7db274184c --- /dev/null +++ b/SConscript @@ -0,0 +1,23 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +# for module compiling +import os + +from building import * + +objs = [] +cwd = GetCurrentDir() +list = os.listdir(cwd) + +if GetDepend(['PKG_USING_WAMR']): + wamr_entry_sconscript = os.path.join(cwd, "product-mini", "platforms", "rt-thread", 'SConscript') + wamr_runlib_sconscript = os.path.join(cwd, "build-scripts", 'SConscript') + + objs = objs + SConscript(wamr_entry_sconscript) + objs = objs + SConscript(wamr_runlib_sconscript) + +Return('objs') diff --git a/SECURITY.md b/SECURITY.md index 3513b9cb35..fa3398cdec 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,29 +1,3 @@ # Security Policy -Building secure foundations for software development is at the core of what we do in the Bytecode Alliance. Contributions of external security researchers are a vital part of that. - -## Scope - -If you believe you've found a security issue in any website, service, or software owned or operated by the Bytecode Alliance, we encourage you to notify us. - -## How to Submit a Report - -To submit a vulnerability report to the Bytecode Alliance, please contact us at [security@bytecodealliance.org](mailto:security@bytecodealliance.org). Your submission will be reviewed and validated by a member of our security team. - -## Safe Harbor - -The Bytecode Alliance supports safe harbor for security researchers who: - -* Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services. -* Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, do not proceed with access, and immediately purge any local information. -* Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party. - -We will consider activities conducted consistent with this policy to constitute "authorized" conduct and will not pursue civil action or initiate a complaint to law enforcement. We will help to the extent we can if legal action is initiated by a third party against you. - -Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. - -## Preferences - -* Please provide detailed reports with reproducible steps and a clearly defined impact. -* Submit one vulnerability per report. -* Social engineering (e.g. phishing, vishing, smishing) is prohibited. +Please refer to the [Bytecode Alliance security policy](https://bytecodealliance.org/security) for details on how to report security issues in WebAssembly Micro Runtime, our disclosure policy, and how to receive notifications about security issues. diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000000..ea0d9a3040 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,90 @@ +# Summary: structure of chapters and subchapters of the book + +* [WAMR Document Home Page](gitbook/home_page.md) + +## Basics + +* [Introduction](gitbook/basics/introduction/README.md) + * [WebAssembly](gitbook/basics/introduction/webassembly.md) + * [WAMR Project](gitbook/basics/introduction/wamr_project.md) + * [Security Feature](gitbook/basics/introduction/security_feature.md) + +* [Getting Started](gitbook/basics/getting-started/README.md) + * [Host Environment Preparation](gitbook/basics/getting-started/host_prerequsites.md) + * [Hello-world Program On Host](gitbook/basics/getting-started/on_host.md) + * [Docker Environment Preparation](doc/devcontainer.md) + * [Hello-world Program On Docker](gitbook/basics/getting-started/on_docker.md) + * [Build And Run WASM Application](doc/build_wasm_app.md) + * [More Tools To Create WASM Application](doc/other_wasm_compilers.md) + +## WAMR In Practice + +* [Tutorial](gitbook/tutorial/README.md) + * [WAMR Running Modes](gitbook/tutorial/running-modes/README.md) + * [Build Tutorial](gitbook/tutorial/build-tutorial/README.md) + * [Build iwasm](doc/build_wamr.md) + * [Build wamrc](gitbook/tutorial/build-tutorial/build_wamrc.md) + * [Language Embedding](gitbook/tutorial/language-embedding/README.md) + * [C/C++](doc/embed_wamr.md) + * [Python](language-bindings/python/README.md) + * [Go](language-bindings/go/README.md) + * [Debugging & IDE Support](gitbook/tutorial/debugging%26IDE-support/README.md) + * [WAMR Source Debugging With LLDB](doc/source_debugging.md) + * [VS Code Support](test-tools/wamr-ide/README.md) + * [Enable Debugging In VS Code](test-tools/wamr-ide/VSCode-Extension/README.md) + * [Move LLDB Binaries](test-tools/wamr-ide/VSCode-Extension/resource/debug/README.md) + +* [Advance Tutorial](gitbook/advance-tutorial/README.md) + * [Performance Test](gitbook/advance-tutorial/performance-benchmark/README.md) + * [PolyBench](tests/benchmarks/polybench/README.md) + * [CoreMark](tests/benchmarks/coremark/README.md) + * [Sightglass](tests/benchmarks/sightglass/README.md) + * [JetStream2](tests/benchmarks/jetstream/README.md) + * [Memory Usage Tunning](doc/memory_tune.md) + * [WAMR Porting Guide](doc/port_wamr.md) + +* [Features](gitbook/features/README.md) + * [Export Native APIs To WASM Applications](doc/export_native_api.md) + * [Example 1: Export C Functions to WASM](samples/basic/README.md) + * [Example 2: Using "native-lib"](samples/native-lib/README.md) + * [Multiple Modules As Dependencies](doc/multi_module.md) + * [Multi-modules Example](samples/multi-module/README.md) + * [Multi-thread, Pthread APIs And Thread Management](doc/pthread_library.md) + * [Multi-thread Example](samples/multi-thread/README.md) + * [Linux SGX(Intel Software Guard Extension) Support](doc/linux_sgx.md) + * [Linux SGX Remote Attestation](samples/sgx-ra/README.md) + * [XIP(Execution In Place) Support](doc/xip.md) + * [Socket Support](doc/socket_api.md) + * [Example: Use Socket Api in WAMR](samples/socket-api/README.md) + * [Post-MVP Features](gitbook/features/demo-examples/README.md) + * [WASM C API](samples/wasm-c-api/README.md) + * [128-bit SIMD](samples/workload/README.md) + * [Reference Types](samples/ref-types/README.md) + +* [More Examples](gitbook/examples/README.md) + * [File Interaction Of WASI](samples/file/README.md) + * [Same WASM Program Executing Concurrently](samples/spawn-thread/README.md) + * [Build And Run Workload](samples/workload/README.md) + +* [User Case](gitbook/features/user-case/README.md) + +## Programmer's Manual + +* [Programmer's Manual](gitbook/programmer's-manual/README.md) + * [C API Lists](gitbook/programmer's-manual/C_API_Lists.md) + +## Community + +* [How To Contribute](CONTRIBUTING.md) + +* [WAMR On Github](https://github.com/bytecodealliance/wasm-micro-runtime) + +* [WAMR Blogs](https://bytecodealliance.github.io/wamr.dev/) + +## Appendix + +* [Appendix A. Background Knowledge And Glossary Of Terms](gitbook/appendix/background_knowledge.md) + +* [Appendix B. WebAssembly Details](gitbook/appendix/webassembly_details.md) + +* [Appendix C. Complete WAMR Guide](README.md) diff --git a/TSC_Charter.md b/TSC_Charter.md new file mode 100644 index 0000000000..56c4a024b9 --- /dev/null +++ b/TSC_Charter.md @@ -0,0 +1,168 @@ +# Project Technical Steering Committee (PTSC) Charter + +## Section 1. Guiding Principle + +The WebAssembly Micro Runtime (WAMR) project is part of the +Bytecode Alliance (BA) which operates transparently, openly, +collaboratively, and ethically. Project proposals, timelines, and status +must not merely be open, but also easily visible to outsiders. + +## Section 2. Project Governance under Bytecode Alliance + +Technical leadership for the WAMR projects within the Bytecode Alliance +is delegated to the projects through the project charter. Though the BA TSC +will not interfere with day-to-day discussions, votes or meetings of the PTSC, +the BA TSC may request additional amendments to the PTSC charter when +there is misalignment between the project charter and the BA mission and values. + + + +The PTSC structure described in this document may be overhauled as part of +establishing a BA TSC in order to adhere to constraints or requirements that +TSC will impose on project-level governance. + +## Section 3. Establishment of the PTSC + +PTSC memberships are not time-limited. There is no maximum size of the PTSC. +The size is expected to vary in order to ensure adequate coverage of important +areas of expertise, balanced with the ability to make decisions efficiently. +The PTSC must have at least four members. + +There is no specific set of requirements or qualifications for PTSC +membership beyond these rules. The PTSC may add additional members to the +PTSC by a standard PTSC motion and vote. A PTSC member may be removed from the +PTSC by voluntary resignation, by a standard PTSC motion, or in accordance to the +participation rules described below. + +Changes to PTSC membership should be posted in the agenda, and may be suggested +as any other agenda item. + +The PTSC may, at its discretion, invite any number of non-voting observers to +participate in the public portion of PTSC discussions and meetings. + +The PTSC shall meet regularly using tools that enable participation by the +community (e.g. weekly on a Zulip channel, or through any other +appropriate means selected by the PTSC ). The meeting shall be directed by +the PTSC Chairperson. Responsibility for directing individual meetings may be +delegated by the PTSC Chairperson to any other PTSC member. Minutes or an +appropriate recording shall be taken and made available to the community +through accessible public postings. + +PTSC members are expected to regularly participate in PTSC activities. + +In the case where an individual PTSC member -- within any three month period -- +attends fewer than 25% of the regularly scheduled meetings, does not +participate in PTSC discussions, *and* does not participate in PTSC votes, the +member shall be automatically removed from the PTSC. The member may be invited +to continue attending PTSC meetings as an observer. + +## Section 4. Responsibilities of the PTSC + +Subject to such policies as may be set by the BA TSC, the WAMR PTSC is +responsible for all technical development within the WAMR project, +including: + +* Setting release dates. +* Release quality standards. +* Technical direction. +* Project governance and process. +* GitHub repository hosting. +* Conduct guidelines. +* Maintaining the list of additional Collaborators. +* Development process and any coding standards. +* Mediating technical conflicts between Collaborators or Foundation +projects. + +The PTSC will define WAMR project’s release vehicles. + +## Section 5. WAMR Project Operations + +The PTSC will establish and maintain a development process for the WAMR +project. The development process will establish guidelines +for how the developers and community will operate. It will, for example, +establish appropriate timelines for PTSC review (e.g. agenda items must be +published at least a certain number of hours in advance of a PTSC +meeting). + +The PTSC and entire technical community will follow any processes as may +be specified by the Bytecode Alliance Board relating to the intake and license compliance +review of contributions, including the Bytecode Alliance IP Policy. + +## Section 6. Elections + +Leadership roles in the WAMR project will be peer elected +representatives of the community. + +For election of persons (such as the PTSC Chairperson), a multiple-candidate +method should be used, such as: + +* [Condorcet][] or +* [Single Transferable Vote][] + +Multiple-candidate methods may be reduced to simple election by plurality +when there are only two candidates for one position to be filled. No +election is required if there is only one candidate and no objections to +the candidate's election. Elections shall be done within the projects by +the Collaborators active in the project. + +The PTSC will elect from amongst voting PTSC members a PTSC Chairperson to +work on building an agenda for PTSC meetings. The PTSC shall hold annual + +elections to select a PTSC Chairperson; there are no limits on the number +of terms a PTSC Chairperson may serve. + +## Section 7. Voting + +For internal project decisions, Collaborators shall operate under Lazy +Consensus. The PTSC shall establish appropriate guidelines for +implementing Lazy Consensus (e.g. expected notification and review time +periods) within the development process. + +The PTSC follows a [Consensus Seeking][] decision making model. When an agenda +item has appeared to reach a consensus the moderator will ask "Does anyone +object?" as a final call for dissent from the consensus. + +If an agenda item cannot reach a consensus a PTSC member can call for +either a closing vote or a vote to table the issue to the next meeting. +The call for a vote must be seconded by a majority of the PTSC or else the +discussion will continue. + +For all votes, a simple majority of all PTSC members for, or against, the issue +wins. A PTSC member may choose to participate in any vote through abstention. + +## Section 8. Project Roles + +The WAMR git repository is maintained by the PTSC and +additional Collaborators who are added by the PTSC on an ongoing basis. + +Individuals making significant and valuable contributions, +“Contributor(s)”, are made Collaborators and given commit-access to the +project. These individuals are identified by the PTSC and their addition +as Collaborators is discussed during a PTSC meeting. Modifications of the +contents of the git repository are made on a collaborative basis as defined in +the development process. + +Collaborators may opt to elevate significant or controversial +modifications, or modifications that have not found consensus to the PTSC +for discussion by assigning the `tsc-agenda` tag to a pull request or +issue. The PTSC should serve as the final arbiter where required. The PTSC +will maintain and publish a list of current Collaborators, as +well as a development process guide for Collaborators and Contributors +looking to participate in the development effort. + +## Section 9. Definitions + +* **Contributors**: contribute code or other artifacts, but do not have +the right to commit to the code base. Contributors work with the +project’s Collaborators to have code committed to the code base. A +Contributor may be promoted to a Collaborator by the PTSC. Contributors should +rarely be encumbered by the PTSC. + +* **Project**: a technical collaboration effort, e.g. a subsystem, that +is organized through the project creation process and approved by the +PTSC. + +[Consensus Seeking]: https://en.wikipedia.org/wiki/Consensus-seeking_decision-making +[Condorcet]: https://en.wikipedia.org/wiki/Condorcet_method +[Single Transferable Vote]: https://en.wikipedia.org/wiki/Single_transferable_vote + diff --git a/assembly-script/README.md b/assembly-script/README.md deleted file mode 100644 index a1324e9d7e..0000000000 --- a/assembly-script/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# AssemblyScript_on_WAMR -This project is based on [Wasm Micro Runtime](https://github.com/bytecodealliance/wasm-micro-runtime) (WAMR) and [AssemblyScript](https://github.com/AssemblyScript/assemblyscript). It implements some of the `wamr app framework` in *assemblyscript*, which allows you to write some applications in *assemblyscript* and dynamically installed on *WAMR Runtime* - -## Building -To build the samples in this repo, you need `npm` on your system -``` bash -sudo apt install npm -``` - -Then install all the dependencies under the repo's root dir -``` bash -cd $repo_root -npm install -``` - -Use the command to build all samples: -``` bash -npm run build:all -``` -or you can build every sample individually: -``` bash -npm run build:timer -npm run build:publisher -npm run build:subscriber -# ... -``` -You will get the compiled wasm file under `build` folder - -Please refer to [package.json](./package.json) for more commands. - -## Run -These applications require WAMR's application framework, you need to build WAMR first. - -``` bash -cd ${WAMR_ROOT}/samples/simple -./build.sh -``` - -You will get two executable files under `out` folder: - -`simple`: The wamr runtime with application framework - -`host_tool`: The tool used to dynamically install/uninstall applications - -1. Start the runtime: - ``` bash - ./simple -s - ``` - -2. Install the compiled wasm file using `host_tool`: - ``` bash - ./host_tool -i app_name -f your_compiled_wasm_file.wasm - ``` -You can also use the WAMR's AoT compiler `wamrc` to compile the wasm bytecode into native code before you run them. Please refer to this [guide](../README.md#build-wamrc-aot-compiler) to build and install `WAMR AoT compiler`. - -After installing `wamrc`, you can compile the wasm file using command: -``` bash -wamrc -o file_name.aot file_name.wasm -``` -and you can install the AoT file to the runtime: -``` bash -./host_tool -i app_name -f your_compiled_aot_file.aot -``` - -## Development -You can develop your own application based on the `wamr_app_lib` APIs. - -### Console APIs -``` typescript -function log(a: string): void; -function log_number(a: number): void; -``` - -### Timer APIs -``` typescript -function setTimeout(cb: () => void, timeout: i32): user_timer; -function setInterval(cb: () => void, timeout: i32): user_timer; -function timer_cancel(timer: user_timer): void; -function timer_restart(timer: user_timer, interval: number): void; -function now(): i32; - -// export to runtime -function on_timer_callback(on_timer_id: i32): void; -``` - -### Request APIs -``` typescript -// register handler -function register_resource_handler(url: string, - request_handle: request_handler_f): void; -// request -function post(url: string, payload: ArrayBuffer, payload_len: number, - tag: string, cb: (resp: wamr_response) => void): void; -function get(url: string, tag: string, - cb: (resp: wamr_response) => void): void; -function put(url: string, payload: ArrayBuffer, payload_len: number, tag: string, - cb: (resp: wamr_response) => void): void; -function del(url: string, tag: string, - cb: (resp: wamr_response) => void): void; - -// response -function make_response_for_request(req: wamr_request): wamr_response; -function api_response_send(resp: wamr_response): void; - -// event -function publish_event(url: string, fmt: number, - payload: ArrayBuffer, payload_len: number): void; -function subscribe_event(url: string, cb: request_handler_f): void; - -// export to runtime -function on_request(buffer_offset: i32, size: i32): void; -function on_response(buffer_offset : i32, size: i32): void; -``` - -You should export the `on_timer_callback`, `on_request` and `on_response` in your application entry file, refer to the samples for example. - -To build your application, you can use `asc`: -``` bash -asc app.ts -b build/app.wasm -t build/app.wat --sourceMap --validate --optimize -``` -or you can add a command into [package.json](./package.json): -``` json -"build:app": "asc app.ts -b build/app.wasm -t build/app.wat --sourceMap --validate --optimize", -``` diff --git a/assembly-script/package-lock.json b/assembly-script/package-lock.json deleted file mode 100644 index 97d4e290cf..0000000000 --- a/assembly-script/package-lock.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "assembly_script", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "assemblyscript": { - "version": "0.8.1", - "resolved": "https://registry.npm.taobao.org/assemblyscript/download/assemblyscript-0.8.1.tgz", - "integrity": "sha1-xcYnSSQG5th/QmiXs9kr0qUz9/4=", - "dev": true, - "requires": { - "binaryen": "89.0.0-nightly.20191113", - "long": "^4.0.0" - } - }, - "binaryen": { - "version": "89.0.0-nightly.20191113", - "resolved": "https://registry.npm.taobao.org/binaryen/download/binaryen-89.0.0-nightly.20191113.tgz", - "integrity": "sha1-oNORTzXJKXhzQeApELf/rrfYl6k=", - "dev": true - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz", - "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", - "dev": true - } - } -} diff --git a/assembly-script/package.json b/assembly-script/package.json deleted file mode 100644 index fa8343d8e3..0000000000 --- a/assembly-script/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "assembly_script", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "build:request_handler": "asc samples/request_handler.ts -b build/request_handler.wasm -t build/request_handler.wat --sourceMap --validate --optimize", - "build:request_sender": "asc samples/request_sender.ts -b build/request_sender.wasm -t build/request_sender.wat --sourceMap --validate --optimize", - "build:timer": "asc samples/timer.ts -b build/timer.wasm -t build/timer.wat --sourceMap --validate --optimize", - "build:publisher": "asc samples/event_publisher.ts -b build/event_publisher.wasm -t build/event_publisher.wat --sourceMap --validate --optimize", - "build:subscriber": "asc samples/event_subscriber.ts -b build/event_subscriber.wasm -t build/event_subscriber.wat --sourceMap --validate --optimize", - "build:all": "npm run build:request_handler; npm run build:request_sender; npm run build:timer; npm run build:subscriber; npm run build:publisher" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "assemblyscript": "^0.8.1" - } -} diff --git a/assembly-script/samples/event_publisher.ts b/assembly-script/samples/event_publisher.ts deleted file mode 100644 index 3ca133fdb4..0000000000 --- a/assembly-script/samples/event_publisher.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -// The entry file of your WebAssembly module. -import * as console from "../wamr_app_lib/console" -import * as timer from "../wamr_app_lib/timer" -import * as request from "../wamr_app_lib/request" - -function publish_overheat_event(): void { - var payload = String.UTF8.encode("warning: temperature is over high"); - request.publish_event("alert/overheat", 0, payload, payload.byteLength); -} - -export function on_init() : void { - timer.setInterval(publish_overheat_event, 2000); -} - -export function on_destroy() : void { - -} - - -/* Function below are requred by wamr runtime, don't remove or modify them */ -export function _on_timer_callback(on_timer_id: i32): void { - timer.on_timer_callback(on_timer_id); -} - -export function _on_request(buffer_offset: i32, size: i32): void { - request.on_request(buffer_offset, size); -} - -export function _on_response(buffer_offset : i32, size: i32): void { - request.on_response(buffer_offset, size); -} \ No newline at end of file diff --git a/assembly-script/samples/event_subscriber.ts b/assembly-script/samples/event_subscriber.ts deleted file mode 100644 index c9aa52a8e9..0000000000 --- a/assembly-script/samples/event_subscriber.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -// The entry file of your WebAssembly module. -import * as console from "../wamr_app_lib/console" -import * as timer from "../wamr_app_lib/timer" -import * as request from "../wamr_app_lib/request" - -export function on_init() : void { - request.subscribe_event("alert/overheat", (req) => { - console.log("### user over heat event handler called:"); - - console.log(""); - console.log(" " + String.UTF8.decode(req.payload) + "\n"); - }) -} - -export function on_destroy() : void { - -} - - -/* Function below are requred by wamr runtime, don't remove or modify them */ -export function _on_timer_callback(on_timer_id: i32): void { - timer.on_timer_callback(on_timer_id); -} - -export function _on_request(buffer_offset: i32, size: i32): void { - request.on_request(buffer_offset, size); -} - -export function _on_response(buffer_offset : i32, size: i32): void { - request.on_response(buffer_offset, size); -} \ No newline at end of file diff --git a/assembly-script/samples/request_handler.ts b/assembly-script/samples/request_handler.ts deleted file mode 100644 index ef9f58c587..0000000000 --- a/assembly-script/samples/request_handler.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - - // The entry file of your WebAssembly module. -import * as console from "../wamr_app_lib/console" -import * as timer from "../wamr_app_lib/timer" -import * as request from "../wamr_app_lib/request" - -export function on_init() : void { - request.register_resource_handler("/test", (req) => { - console.log("### Req: /test " + String.UTF8.decode(req.payload)); - - console.log(" request payload:"); - console.log(" " + String.UTF8.decode(req.payload) + "\n"); - - var resp = request.make_response_for_request(req); - resp.set_payload(String.UTF8.encode("Ok"), 2); - request.api_response_send(resp); - }); -} - -export function on_destroy() : void { - -} - - -/* Function below are requred by wamr runtime, don't remove or modify them */ -export function _on_timer_callback(on_timer_id: i32): void { - timer.on_timer_callback(on_timer_id); -} - -export function _on_request(buffer_offset: i32, size: i32): void { - request.on_request(buffer_offset, size); -} - -export function _on_response(buffer_offset : i32, size: i32): void { - request.on_response(buffer_offset, size); -} \ No newline at end of file diff --git a/assembly-script/samples/request_sender.ts b/assembly-script/samples/request_sender.ts deleted file mode 100644 index 5648985e76..0000000000 --- a/assembly-script/samples/request_sender.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -// The entry file of your WebAssembly module. -import * as console from "../wamr_app_lib/console" -import * as timer from "../wamr_app_lib/timer" -import * as request from "../wamr_app_lib/request" - -export function on_init() : void { - var payload = String.UTF8.encode("test message"); - request.post("/test", payload, payload.byteLength, "", (resp) => { - if (resp != null) { - console.log("Post Success"); - - if (resp.payload != null) { - console.log(" response payload:") - console.log(" " + String.UTF8.decode(resp.payload!) + "\n"); - } - } - else - console.log("Post Timeout"); - }); -} - -export function on_destroy() : void { - -} - - -/* Function below are requred by wamr runtime, don't remove or modify them */ -export function _on_timer_callback(on_timer_id: i32): void { - timer.on_timer_callback(on_timer_id); -} - -export function _on_request(buffer_offset: i32, size: i32): void { - request.on_request(buffer_offset, size); -} - -export function _on_response(buffer_offset : i32, size: i32): void { - request.on_response(buffer_offset, size); -} \ No newline at end of file diff --git a/assembly-script/samples/timer.ts b/assembly-script/samples/timer.ts deleted file mode 100644 index 2e3f69d29a..0000000000 --- a/assembly-script/samples/timer.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -// The entry file of your WebAssembly module. -import * as console from '../wamr_app_lib/console' -import * as timer from '../wamr_app_lib/timer' - -/* clousure is not implemented yet, we need to declare global variables - so that they can be accessed inside a callback function */ -var cnt = 0; -var my_timer: timer.user_timer; - -export function on_init(): void { - /* The callback function will be called every 2 second, - and will stop after 10 calls */ - my_timer = timer.setInterval(() => { - cnt ++; - console.log((cnt * 2).toString() + " seconds passed"); - - if (cnt >= 10) { - timer.timer_cancel(my_timer); - console.log("Stop Timer"); - } - }, 2000); -} - -export function on_destroy(): void { - -} - -/* Function below are requred by wamr runtime, don't remove or modify them */ -export function _on_timer_callback(on_timer_id: i32): void { - timer.on_timer_callback(on_timer_id); -} \ No newline at end of file diff --git a/assembly-script/samples/tsconfig.json b/assembly-script/samples/tsconfig.json deleted file mode 100644 index c614e5c8e0..0000000000 --- a/assembly-script/samples/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../node_modules/assemblyscript/std/assembly.json", - "include": [ - "./**/*.ts" - ] -} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/console.ts b/assembly-script/wamr_app_lib/console.ts deleted file mode 100644 index 0b06c07d76..0000000000 --- a/assembly-script/wamr_app_lib/console.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -@external("env", "printf") -declare function printf(a: ArrayBuffer): i32; - -export function log(a: string): void { - printf(String.UTF8.encode(a + '\n', true)); -} - -export function log_number(a: number): void { - printf(String.UTF8.encode(a.toString() + '\n')); -} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/request.ts b/assembly-script/wamr_app_lib/request.ts deleted file mode 100644 index 2125e045a9..0000000000 --- a/assembly-script/wamr_app_lib/request.ts +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -import * as console from './console' -import * as timer from './timer' - -@external("env", "wasm_response_send") -declare function wasm_response_send(buffer: ArrayBuffer, size: i32): void; - -@external("env", "wasm_register_resource") -declare function wasm_register_resource(url: ArrayBuffer): void; - -@external("env", "wasm_post_request") -declare function wasm_post_request(buffer: ArrayBuffer, size: i32): void; - -@external("env", "wasm_sub_event") -declare function wasm_sub_event(url: ArrayBuffer): void; - -var COAP_GET = 1; -var COAP_POST = 2; -var COAP_PUT = 3; -var COAP_DELETE = 4; -var COAP_EVENT = COAP_DELETE + 2; - -/* CoAP response codes */ -export enum CoAP_Status { - NO_ERROR = 0, - - CREATED_2_01 = 65, /* CREATED */ - DELETED_2_02 = 66, /* DELETED */ - VALID_2_03 = 67, /* NOT_MODIFIED */ - CHANGED_2_04 = 68, /* CHANGED */ - CONTENT_2_05 = 69, /* OK */ - CONTINUE_2_31 = 95, /* CONTINUE */ - - BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ - UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ - BAD_OPTION_4_02 = 130, /* BAD_OPTION */ - FORBIDDEN_4_03 = 131, /* FORBIDDEN */ - NOT_FOUND_4_04 = 132, /* NOT_FOUND */ - METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ - NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ - PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ - REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ - UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ - - INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ - NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ - BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ - SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ - GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ - PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ - - /* Erbium errors */ - MEMORY_ALLOCATION_ERROR = 192, PACKET_SERIALIZATION_ERROR, - - /* Erbium hooks */ - MANUAL_RESPONSE, PING_RESPONSE -}; - -var g_mid: i32 = 0; -class wamr_request { - mid: i32 = 0; - url: string = ""; - action: i32 = 0; - fmt: i32 = 0; - payload: ArrayBuffer; - payload_len: i32 = 0; - - sender: i32 = 0; - - constructor(mid: i32, url: string, action: i32, fmt: i32, - payload: ArrayBuffer, payload_len: number) { - this.mid = mid; - this.url = url; - this.action = action; - this.fmt = fmt; - this.payload = payload; - this.payload_len = i32(payload_len); - } -} - -class wamr_response { - mid: i32 = 0; - status: i32 = 0; - fmt: i32 = 0; - payload: ArrayBuffer | null; - payload_len: i32 = 0; - - receiver: i32 = 0; - - constructor(mid: i32, status: i32, fmt: i32, - payload: ArrayBuffer | null, payload_len: i32) { - this.mid = mid; - this.status = status; - this.fmt = fmt; - this.payload = payload; - this.payload_len = payload_len; - } - - set_status(status: number): void { - this.status = i32(status); - } - - set_payload(payload: ArrayBuffer, payload_len: number): void { - this.payload = payload; - this.payload_len = i32(payload_len); - } -} - -class wamr_resource { - url: string; - type: number; - cb: request_handler_f; - - constructor(url: string, type: number, cb: request_handler_f) { - this.url = url; - this.type = type; - this.cb = cb; - } -} - -function is_expire(trans: wamr_transaction, index: i32, array: Array): bool { - var now = timer.now(); - - var elapsed_ms = (now < trans.time) ? - (now + (0xFFFFFFFF - trans.time) + 1) : (now - trans.time); - - return elapsed_ms >= TRANSACTION_TIMEOUT_MS; -} - -function not_expire(trans: wamr_transaction, index: i32, array: Array): bool { - var now = timer.now(); - - var elapsed_ms = (now < trans.time) ? - (now + (0xFFFFFFFF - trans.time) + 1) : (now - trans.time); - - return elapsed_ms >= TRANSACTION_TIMEOUT_MS; -} - -function transaction_timeout_handler(): void { - var now = timer.now(); - - var expired = transaction_list.filter(is_expire); - transaction_list = transaction_list.filter(not_expire); - - expired.forEach(item => { - item.cb(null); - transaction_remove(item); - }) - - if (transaction_list.length > 0) { - var elpased_ms: number, ms_to_expiry: number; - now = timer.now(); - if (now < transaction_list[0].time) { - elpased_ms = now + (0xFFFFFFFF - transaction_list[0].time) + 1; - } else { - elpased_ms = now - transaction_list[0].time; - } - ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; - timer.timer_restart(g_trans_timer, ms_to_expiry); - } else { - timer.timer_cancel(g_trans_timer); - } -} - -function transaction_find(mid: number): wamr_transaction | null { - for (let i = 0; i < transaction_list.length; i++) { - if (transaction_list[i].mid == mid) - return transaction_list[i]; - } - return null; -} - -function transaction_add(trans: wamr_transaction): void { - transaction_list.push(trans); - - if (transaction_list.length == 1) { - g_trans_timer = timer.setTimeout( - transaction_timeout_handler, - TRANSACTION_TIMEOUT_MS - ); - } -} - -function transaction_remove(trans: wamr_transaction): void { - var index = transaction_list.indexOf(trans); - transaction_list.splice(index, 1); -} - -var transaction_list = new Array(); -class wamr_transaction { - mid: number; - time: number; - cb: (resp: wamr_response | null) => void; - - constructor(mid: number, time: number, cb: (resp: wamr_response) => void) { - this.mid = mid; - this.time = time; - this.cb = cb; - } -} - -var REQUEST_PACKET_FIX_PART_LEN = 18; -var RESPONSE_PACKET_FIX_PART_LEN = 16; -var TRANSACTION_TIMEOUT_MS = 5000; -var g_trans_timer: timer.user_timer; - -var Reg_Event = 0; -var Reg_Request = 1; - -function pack_request(req: wamr_request): DataView { - var url_len = req.url.length + 1; - var len = REQUEST_PACKET_FIX_PART_LEN + url_len + req.payload_len - var buf = new ArrayBuffer(len); - - var dataview = new DataView(buf, 0, len); - - dataview.setUint8(0, 1); - dataview.setUint8(1, u8(req.action)); - dataview.setUint16(2, u16(req.fmt)); - dataview.setUint32(4, req.mid); - dataview.setUint32(8, req.sender); - dataview.setUint16(12, u16(url_len)) - dataview.setUint32(14, req.payload_len); - - var i = 0; - for (i = 0; i < url_len - 1; i++) { - dataview.setUint8(i + 18, u8(req.url.codePointAt(i))); - } - dataview.setUint8(i + 18, 0); - - var payload_view = new DataView(req.payload); - for (i = 0; i < req.payload_len; i++) { - dataview.setUint8(i + 18 + url_len, u8(payload_view.getUint8(i))); - } - - return dataview; -} - -function unpack_request(packet: ArrayBuffer, size: i32): wamr_request { - var dataview = new DataView(packet, 0, size); - - if (dataview.getUint8(0) != 1) - throw new Error("packet version mismatch"); - - if (size < REQUEST_PACKET_FIX_PART_LEN) - throw new Error("packet size error"); - - var url_len = dataview.getUint16(12); - var payload_len = dataview.getUint32(14); - - if (size != (REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) - throw new Error("packet size error"); - - var action = dataview.getUint8(1); - var fmt = dataview.getUint16(2); - var mid = dataview.getUint32(4); - var sender = dataview.getUint32(8); - - var url = packet.slice(REQUEST_PACKET_FIX_PART_LEN, REQUEST_PACKET_FIX_PART_LEN + url_len - 1); - var payload = packet.slice(REQUEST_PACKET_FIX_PART_LEN + url_len, REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len); - - var req = new wamr_request(mid, String.UTF8.decode(url), action, fmt, payload, payload_len); - req.sender = sender; - - return req; -} - -function pack_response(resp: wamr_response): DataView { - var len = RESPONSE_PACKET_FIX_PART_LEN + resp.payload_len - var buf = new ArrayBuffer(len); - - var dataview = new DataView(buf, 0, len); - - dataview.setUint8(0, 1); - dataview.setUint8(1, u8(resp.status)); - dataview.setUint16(2, u16(resp.fmt)); - dataview.setUint32(4, resp.mid); - dataview.setUint32(8, resp.receiver); - dataview.setUint32(12, resp.payload_len) - - if (resp.payload != null) { - var payload_view = new DataView(resp.payload!); - for (let i = 0; i < resp.payload_len; i++) { - dataview.setUint8(i + 16, payload_view.getUint8(i)); - } - } - - return dataview; -} - -function unpack_response(packet: ArrayBuffer, size: i32): wamr_response { - var dataview = new DataView(packet, 0, size); - - if (dataview.getUint8(0) != 1) - throw new Error("packet version mismatch"); - - if (size < RESPONSE_PACKET_FIX_PART_LEN) - throw new Error("packet size error"); - - var payload_len = dataview.getUint32(12); - if (size != RESPONSE_PACKET_FIX_PART_LEN + payload_len) - throw new Error("packet size error"); - - var status = dataview.getUint8(1); - var fmt = dataview.getUint16(2); - var mid = dataview.getUint32(4); - var receiver = dataview.getUint32(8); - - var payload = packet.slice(RESPONSE_PACKET_FIX_PART_LEN); - - var resp = new wamr_response(mid, status, fmt, payload, payload_len); - resp.receiver = receiver; - - return resp; -} - -function do_request(req: wamr_request, cb: (resp: wamr_response) => void): void { - var trans = new wamr_transaction(req.mid, timer.now(), cb); - var msg = pack_request(req); - - transaction_add(trans); - - wasm_post_request(msg.buffer, msg.byteLength); -} - -function do_response(resp: wamr_response): void { - var msg = pack_response(resp); - - wasm_response_send(msg.buffer, msg.byteLength); -} - -var resource_list = new Array(); -type request_handler_f = (req: wamr_request) => void; - -function registe_url_handler(url: string, cb: request_handler_f, type: number): void { - for (let i = 0; i < resource_list.length; i++) { - if (resource_list[i].type == type && resource_list[i].url == url) { - resource_list[i].cb = cb; - return; - } - } - - var res = new wamr_resource(url, type, cb); - resource_list.push(res); - - if (type == Reg_Request) - wasm_register_resource(String.UTF8.encode(url)); - else - wasm_sub_event(String.UTF8.encode(url)); -} - -function is_event_type(req: wamr_request): bool { - return req.action == COAP_EVENT; -} - -function check_url_start(url: string, leading_str: string): bool { - return url.split('/')[0] == leading_str.split('/')[0]; -} - -/* User APIs below */ -export function post(url: string, payload: ArrayBuffer, payload_len: number, tag: string, - cb: (resp: wamr_response) => void): void { - var req = new wamr_request(g_mid++, url, COAP_POST, 0, payload, payload_len); - - do_request(req, cb); -} - -export function get(url: string, tag: string, - cb: (resp: wamr_response) => void): void { - var req = new wamr_request(g_mid++, url, COAP_GET, 0, new ArrayBuffer(0), 0); - - do_request(req, cb); -} - -export function put(url: string, payload: ArrayBuffer, payload_len: number, tag: string, - cb: (resp: wamr_response) => void): void { - var req = new wamr_request(g_mid++, url, COAP_PUT, 0, payload, payload_len); - - do_request(req, cb); -} - -export function del(url: string, tag: string, - cb: (resp: wamr_response) => void): void { - var req = new wamr_request(g_mid++, url, COAP_PUT, 0, new ArrayBuffer(0), 0); - - do_request(req, cb); -} - -export function make_response_for_request(req: wamr_request): wamr_response { - var resp = new wamr_response(req.mid, CoAP_Status.CONTENT_2_05, 0, null, 0); - resp.receiver = req.sender; - - return resp; -} - -export function api_response_send(resp: wamr_response): void { - do_response(resp); -} - -export function register_resource_handler(url: string, - request_handle: request_handler_f): void { - registe_url_handler(url, request_handle, Reg_Request); -} - -export function publish_event(url: string, fmt: number, - payload: ArrayBuffer, payload_len: number): void { - var req = new wamr_request(g_mid++, url, COAP_EVENT, i32(fmt), payload, payload_len); - - var msg = pack_request(req); - - wasm_post_request(msg.buffer, msg.byteLength); -} - -export function subscribe_event(url: string, cb: request_handler_f): void { - registe_url_handler(url, cb, Reg_Event); -} - - -/* These two APIs are required by wamr runtime, - use a wrapper to export them in the entry file - - e.g: - - import * as request from '.wamr_app_lib/request' - - // Your code here ... - - export function _on_request(buffer_offset: i32, size: i32): void { - on_request(buffer_offset, size); - } - - export function _on_response(buffer_offset: i32, size: i32): void { - on_response(buffer_offset, size); - } -*/ -export function on_request(buffer_offset: i32, size: i32): void { - var buffer = new ArrayBuffer(size); - var dataview = new DataView(buffer); - - for (let i = 0; i < size; i++) { - dataview.setUint8(i, load(buffer_offset + i, 0, 1)); - } - - var req = unpack_request(buffer, size); - - var is_event = is_event_type(req); - - for (let i = 0; i < resource_list.length; i++) { - if ((is_event && resource_list[i].type == Reg_Event) - || (!is_event && resource_list[i].type == Reg_Request)) { - if (check_url_start(req.url, resource_list[i].url)) { - resource_list[i].cb(req); - return; - } - } - } - - console.log("on_request: exit. no service handler."); -} - -export function on_response(buffer_offset: i32, size: i32): void { - var buffer = new ArrayBuffer(size); - var dataview = new DataView(buffer); - - for (let i = 0; i < size; i++) { - dataview.setUint8(i, load(buffer_offset + i, 0, 1)); - } - - var resp = unpack_response(buffer, size); - var trans = transaction_find(resp.mid); - - if (trans != null) { - if (transaction_list.indexOf(trans) == 0) { - if (transaction_list.length >= 2) { - var elpased_ms: number, ms_to_expiry: number; - var now = timer.now(); - if (now < transaction_list[1].time) { - elpased_ms = now + (0xFFFFFFFF - transaction_list[1].time) + 1; - } else { - elpased_ms = now - transaction_list[1].time; - } - ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; - timer.timer_restart(g_trans_timer, ms_to_expiry); - } else { - timer.timer_cancel(g_trans_timer); - } - } - - trans.cb(resp); - } -} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/timer.ts b/assembly-script/wamr_app_lib/timer.ts deleted file mode 100644 index ea8363e8c2..0000000000 --- a/assembly-script/wamr_app_lib/timer.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -@external("env", "wasm_create_timer") -declare function wasm_create_timer(a: i32, b: bool, c: bool): i32; - -@external("env", "wasm_timer_cancel") -declare function wasm_timer_cancel(a: i32): void; - -@external("env", "wasm_timer_restart") -declare function wasm_timer_restart(a: i32, b: i32): void; - -@external("env", "wasm_get_sys_tick_ms") -declare function wasm_get_sys_tick_ms(): i32; - -export var timer_list = new Array(); - -export class user_timer { - timer_id: i32 = 0; - timeout: i32; - period: bool = false; - cb: () => void; - - constructor(cb: () => void, timeout: i32, period: bool) { - this.cb = cb; - this.timeout = timeout; - this.period = period - this.timer_id = timer_create(this.timeout, this.period, true); - } -} - -export function timer_create(a: i32, b: bool, c: bool): i32 { - return wasm_create_timer(a, b, c); -} - -export function setTimeout(cb: () => void, timeout: i32): user_timer { - var timer = new user_timer(cb, timeout, false); - timer_list.push(timer); - - return timer; -} - -export function setInterval(cb: () => void, timeout: i32): user_timer { - var timer = new user_timer(cb, timeout, true); - timer_list.push(timer); - - return timer; -} - -export function timer_cancel(timer: user_timer): void { - wasm_timer_cancel(timer.timer_id); - - var i = 0; - for (i = 0; i < timer_list.length; i++) { - if (timer_list[i].timer_id == timer.timer_id) - break; - } - - timer_list.splice(i, 1); -} - -export function timer_restart(timer: user_timer, interval: number): void { - wasm_timer_restart(timer.timer_id, i32(interval)); -} - -export function now(): i32 { - return wasm_get_sys_tick_ms(); -} - -// This export function need to be copied to the top application file -// -export function on_timer_callback(on_timer_id: i32): void { - for (let i = 0; i < timer_list.length; i++) { - if (timer_list[i].timer_id == on_timer_id) { - timer_list[i].cb(); - } - } -} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/tsconfig.json b/assembly-script/wamr_app_lib/tsconfig.json deleted file mode 100644 index c614e5c8e0..0000000000 --- a/assembly-script/wamr_app_lib/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../node_modules/assemblyscript/std/assembly.json", - "include": [ - "./**/*.ts" - ] -} \ No newline at end of file diff --git a/build-scripts/SConscript b/build-scripts/SConscript new file mode 100644 index 0000000000..ccb1b19f42 --- /dev/null +++ b/build-scripts/SConscript @@ -0,0 +1,56 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] + +WAMR_ROOT_DIR = os.path.join(cwd, "..") +SHARED_DIR = os.path.join(WAMR_ROOT_DIR, 'core', 'shared') +IWASM_DIR = os.path.join(WAMR_ROOT_DIR, 'core', 'iwasm') +DEPS_DIR = os.path.join(WAMR_ROOT_DIR, 'core', 'deps') + +if GetDepend(['WAMR_BUILD_INTERP']): + script_path = os.path.join(IWASM_DIR, 'interpreter', 'SConscript') + objs += SConscript(script_path) + +if GetDepend(['WAMR_BUILD_AOT']): + script_path = os.path.join(IWASM_DIR, 'aot', 'SConscript') + objs += SConscript(script_path) + if GetDepend(['WAMR_BUILD_JIT']): + script_path = os.path.join(IWASM_DIR, 'compilation', 'SConscript') + objs += SConscript(script_path) + +if GetDepend(['WAMR_BUILD_LIBC_BUILTIN']): + objs += SConscript(os.path.join(IWASM_DIR, 'libraries', 'libc-builtin', 'SConscript')) + +if GetDepend(['WAMR_BUILD_LIBC_WASI']): + objs += SConscript(os.path.join(IWASM_DIR, 'libraries', 'libc-wasi', 'SConscript')) + objs += SConscript(os.path.join(SHARED_DIR, 'platform', 'common', 'posix', 'SConscript')) + objs += SConscript(os.path.join(SHARED_DIR, 'platform', 'common', 'libc-util', 'SConscript')) + +if GetDepend(['WAMR_BUILD_LIB_PTHREAD']): + objs += SConscript(os.path.join(IWASM_DIR, 'libraries', 'lib-pthread', 'SConscript')) + +if GetDepend(['WAMR_BUILD_THREAD_MGR']): + objs += SConscript(os.path.join(IWASM_DIR, 'libraries', 'thread-mgr', 'SConscript')) + +if GetDepend(['WAMR_BUILD_LIBC_EMCC']): + objs += SConscript(os.path.join(IWASM_DIR, 'libraries', 'libc-emmc', 'SConscript')) + +if GetDepend(['WAMR_BUILD_LIB_WASI_THREADS']): + objs += SConscript(os.path.join(IWASM_DIR, 'libraries', 'lib-wasi-threads', 'SConscript')) + +objs += SConscript(os.path.join(cwd, 'SConscript_config')); + +objs += SConscript(os.path.join(SHARED_DIR, 'platform', 'rt-thread', 'SConscript')) +objs += SConscript(os.path.join(SHARED_DIR, 'mem-alloc', 'SConscript')) +objs += SConscript(os.path.join(IWASM_DIR, 'common', 'SConscript')) +objs += SConscript(os.path.join(SHARED_DIR, 'utils', 'SConscript')) + +Return('objs') diff --git a/build-scripts/SConscript_config b/build-scripts/SConscript_config new file mode 100644 index 0000000000..c439d5c955 --- /dev/null +++ b/build-scripts/SConscript_config @@ -0,0 +1,142 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +import re + +from building import * + +Import('rtconfig') + +src = [] +objs = [] +cwd = GetCurrentDir() + +IWASM_INC_DIR = os.path.join(cwd, '..', 'core', 'iwasm', 'include') + +CPPPATH = [IWASM_INC_DIR] + +if rtconfig.BUILD == 'debug': + CPPDEFINES = ['BH_DEBUG=1'] +else: + CPPDEFINES = ['BH_DEBUG=0'] + +if rtconfig.ARCH == 'arm': + if re.match('^cortex-m.*', rtconfig.CPU): + print('[WAMR] using thumbv4t') + CPPDEFINES += ['BUILD_TARGET_THUMB'] + CPPDEFINES += ['RTT_WAMR_BUILD_TARGET_THUMB'] + elif re.match('^cortex-a.*', rtconfig.CPU): + print('[WAMR] using armv7') + CPPDEFINES += ['BUILD_TARGET_ARM'] + CPPDEFINES += ['RTT_WAMR_BUILD_TARGET_ARMV7'] + elif re.match('^cortex-r.*', rtconfig.CPU): + print('[WAMR] using armv7') + CPPDEFINES += ['BUILD_TARGET_ARM'] + CPPDEFINES += ['RTT_WAMR_BUILD_TARGET_ARMV7'] + elif rtconfig.CPU == 'armv6': + print('[WAMR] using armv6') + CPPDEFINES += ['BUILD_TARGET_ARM'] + CPPDEFINES += ['RTT_WAMR_BUILD_TARGET_ARMV6'] + elif re.match('^arm9*', rtconfig.CPU): + print('[WAMR] using armv4') + CPPDEFINES += ['BUILD_TARGET_ARM'] + CPPDEFINES += ['RTT_WAMR_BUILD_TARGET_ARMV4'] +elif rtconfig.ARCH == 'ia32': + CPPDEFINES += ['BUILD_TARGET_X86_32', 'RTT_WAMR_BUILD_TARGET_X86_32'] +else: + print("[WAMR] unknown arch", rtconfig.ARCH) + +if GetDepend(['WAMR_BUILD_INTERP']): + CPPDEFINES += ['WASM_ENABLE_INTERP=1'] + if GetDepend(['WAMR_BUILD_FAST_INTERP']): + CPPDEFINES += ['WASM_ENABLE_FAST_INTERP=1'] + print("[WAMR] fast interpreter was enabled") + else: + CPPDEFINES += ['WASM_ENABLE_FAST_INTERP=0'] + print("[WAMR] fast interpreter was disabled") +else: + CPPDEFINES += ['WASM_ENABLE_INTERP=0'] + +CPPDEFINES += ['WASM_ENABLE_JIT=0'] + +if GetDepend(['WAMR_BUILD_MULTI_MODULE']): + CPPDEFINES += ['WASM_ENABLE_MULTI_MODULE=1'] +else: + CPPDEFINES += ['WASM_ENABLE_MULTI_MODULE=0'] + +if GetDepend(['WAMR_BUILD_SPEC_TEST']): + CPPDEFINES += ['WASM_ENABLE_SPEC_TEST=1'] + print("[WAMR] spec test compatible mode was enabled") + +if GetDepend(['WAMR_BUILD_BULK_MEMORY']): + CPPDEFINES += ['WASM_ENABLE_BULK_MEMORY=1'] + print("[WAMR] Bulk memory feature was enabled") +else: + CPPDEFINES += ['WASM_ENABLE_BULK_MEMORY=0'] + +if GetDepend(['WAMR_BUILD_SHARED_MEMORY']): + CPPDEFINES += ['WASM_ENABLE_SHARED_MEMORY=1'] + print("[WAMR] Shared memory enabled") +else: + CPPDEFINES += ['WASM_ENABLE_SHARED_MEMORY=0'] + +if GetDepend(['WAMR_BUILD_MINI_LOADER']): + CPPDEFINES += ['WASM_ENABLE_MINI_LOADER=1'] + print("[WAMR] mini loader enabled") +else: + CPPDEFINES += ['WASM_ENABLE_MINI_LOADER=0'] + +if GetDepend(['WAMR_DISABLE_HW_BOUND_CHECK']): + CPPDEFINES += ['WASM_DISABLE_HW_BOUND_CHECK=1'] + CPPDEFINES += ['WASM_DISABLE_STACK_HW_BOUND_CHECK=1'] + print("[WAMR] Hardware boundary check disabled") + +if GetDepend(['WAMR_BUILD_SIMD']): + CPPDEFINES += ['WASM_ENABLE_SIMD=1'] + print('[WAMR] SIMD enabled') + +if GetDepend(['WAMR_BUILD_MEMORY_PROFILING']): + CPPDEFINES += ['WASM_ENABLE_MEMORY_PROFILING=1'] + print('[WAMR] Memory profiling enabled') + +if GetDepend(['WAMR_BUILD_MEMORY_TRACING']): + CPPDEFINES += ['WASM_ENABLE_MEMORY_TRACING=1'] + print('[WAMR] Memory tracing enabled') + +if GetDepend(['WAMR_BUILD_CUSTOM_NAME_SECTION']): + CPPDEFINES += ['WASM_ENABLE_CUSTOM_NAME_SECTION=1'] + print('[WAMR] Custom name section enabled') + +if GetDepend(['WAMR_BUILD_TAIL_CALL']): + CPPDEFINES += ['WASM_ENABLE_TAIL_CALL=1'] + print('[WAMR] Tail call enabled') + +if GetDepend(['WAMR_BUILD_THREAD_MGR']): + CPPDEFINES += ['WASM_ENABLE_THREAD_MGR=1'] + print('[WAMR] Thread manager enabled') + +if GetDepend(['WAMR_BUILD_LIBC_WASI']): + CPPDEFINES += ['WASM_ENABLE_LIBC_WASI=1'] + CPPDEFINES += ['WASM_ENABLE_MODULE_INST_CONTEXT=1'] + print('[WAMR] Libc wasi enabled') + +if GetDepend(['WAMR_BUILD_LIB_WASI_THREADS']): + CPPDEFINES += ['WASM_ENABLE_LIB_WASI_THREADS=1'] + print('[WAMR] Lib wasi threads enabled') + +if GetDepend(['WAMR_BUILD_REF_TYPES']): + CPPDEFINES += ['WASM_ENABLE_REF_TYPES=1'] + print('[WAMR] enable ref types') + +CPPDEFINES += ['BH_MALLOC=wasm_runtime_malloc'] +CPPDEFINES += ['BH_FREE=wasm_runtime_free'] + +LIBS = ['m'] + +group = DefineGroup('wamr', src, depend = ['PKG_USING_WAMR'], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES, LIBS = LIBS) + +Return('group') diff --git a/build-scripts/build_llvm.py b/build-scripts/build_llvm.py new file mode 100755 index 0000000000..e2221b8e54 --- /dev/null +++ b/build-scripts/build_llvm.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import argparse +import os +import pathlib +import requests +import shlex +import shutil +import subprocess +import sysconfig +import sys + + +def clone_llvm(dst_dir, llvm_repo, llvm_branch): + """ + any error will raise CallProcessError + """ + llvm_dir = dst_dir.joinpath("llvm").resolve() + + if not llvm_dir.exists(): + 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) + + return llvm_dir + + +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='', use_ccache=False): + LLVM_COMPILE_OPTIONS = [ + '-DCMAKE_BUILD_TYPE:STRING="Release"', + "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", + "-DLLVM_APPEND_VC_REV:BOOL=ON", + "-DLLVM_BUILD_EXAMPLES:BOOL=OFF", + "-DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF", + "-DLLVM_ENABLE_BINDINGS:BOOL=OFF", + "-DLLVM_ENABLE_IDE:BOOL=OFF", + "-DLLVM_ENABLE_LIBEDIT=OFF", + "-DLLVM_ENABLE_TERMINFO:BOOL=OFF", + "-DLLVM_ENABLE_ZLIB:BOOL=ON", + "-DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF", + "-DLLVM_INCLUDE_DOCS:BOOL=OFF", + "-DLLVM_INCLUDE_EXAMPLES:BOOL=OFF", + "-DLLVM_INCLUDE_UTILS:BOOL=OFF", + "-DLLVM_INCLUDE_TESTS:BOOL=OFF", + "-DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON", + ] + + # ccache is opt-in via --use-ccache flag + if not "windows" == platform and use_ccache: + LLVM_COMPILE_OPTIONS.append("-DLLVM_CCACHE_BUILD:BOOL=ON") + # perf support is available on Linux only + if "linux" == platform: + LLVM_COMPILE_OPTIONS.append("-DLLVM_USE_PERF: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"', + "-DLLVM_ENABLE_LIBICUUC:BOOL=OFF", + "-DLLVM_ENABLE_LIBICUDATA:BOOL=OFF", + ], + "xtensa": [ + '-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="Xtensa"', + ], + "windows": [ + "-DCMAKE_INSTALL_PREFIX=LLVM-install", + ], + "default": [], + } + + experimental_backends = ["ARC", "Xtensa"] + normal_backends = [s for s in backends if s not in experimental_backends] + + LLVM_TARGETS_TO_BUILD = [ + '-DLLVM_TARGETS_TO_BUILD:STRING="' + ";".join(normal_backends) + '"' + if normal_backends + else '-DLLVM_TARGETS_TO_BUILD:STRING="AArch64;ARM;Mips;RISCV;X86"' + ] + + # if not on ARC platform, but want to add expeirmental backend ARC as target + if platform != "ARC" and "ARC" in backends: + LLVM_TARGETS_TO_BUILD.extend( + LLVM_EXTRA_COMPILE_OPTIONS["arc"] + ) + + LLVM_PROJECTS_TO_BUILD = [ + '-DLLVM_ENABLE_PROJECTS:STRING="' + ";".join(projects) + '"' if projects else "" + ] + + # lldb project requires libxml2 + LLVM_LIBXML2_OPTION = [ + "-DLLVM_ENABLE_LIBXML2:BOOL=" + ("ON" if "lldb" in projects else "OFF") + ] + + # enabling LLVM_INCLUDE_TOOLS will increase ~300M to the final package + LLVM_INCLUDE_TOOLS_OPTION = [ + "-DLLVM_INCLUDE_TOOLS:BOOL=ON" if projects else "-DLLVM_INCLUDE_TOOLS:BOOL=OFF" + ] + + if not llvm_dir.exists(): + raise Exception(f"{llvm_dir} doesn't exist") + + build_dir = llvm_dir.joinpath("build").resolve() + build_dir.mkdir(exist_ok=True) + + lib_llvm_core_library = build_dir.joinpath("lib/libLLVMCore.a").resolve() + if lib_llvm_core_library.exists(): + 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 + + LLVM_LIBXML2_OPTION + + LLVM_EXTRA_COMPILE_OPTIONS.get( + platform, LLVM_EXTRA_COMPILE_OPTIONS["default"] + ) + + LLVM_TARGETS_TO_BUILD + + LLVM_PROJECTS_TO_BUILD + + LLVM_INCLUDE_TOOLS_OPTION + ) + + 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" + else: + CONFIG_CMD += " -G'Ninja'" + print(f"Config command: {CONFIG_CMD}") + subprocess.check_call(shlex.split(CONFIG_CMD), cwd=build_dir) + + BUILD_CMD = "cmake --build . --target package" + ( + " --config Release" if "windows" == platform else "" + ) + if "windows" == platform: + BUILD_CMD += " --parallel " + str(os.cpu_count()) + print(f"Build command: {BUILD_CMD}") + subprocess.check_call(shlex.split(BUILD_CMD), cwd=build_dir) + + return build_dir + + +def repackage_llvm(llvm_dir): + build_dir = llvm_dir.joinpath("./build").resolve() + + packs = [f for f in build_dir.glob("LLVM-*.tar.gz")] + if len(packs) > 1: + raise Exception("Find more than one LLVM-*.tar.gz") + + if not packs: + raise Exception("Didn't find any LLVM-* package") + return + + llvm_package = packs[0].name + # 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-*.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 repackage_llvm_windows(llvm_dir): + build_dir = llvm_dir.joinpath("./build").resolve() + + packs_path = [f for f in build_dir.glob("./_CPack_Packages/win64/NSIS/LLVM-*-win64")] + if len(packs_path) > 1: + raise Exception("Find more than one LLVM-* package") + + if not packs_path: + raise Exception("Didn't find any LLVM-* package") + return + + llvm_package_path = f"_CPack_Packages/win64/NSIS/{packs_path[0].name}" + windows_package_dir = build_dir.joinpath(llvm_package_path).resolve() + + # mv package dir outside of build + shutil.move(str(windows_package_dir), str(llvm_dir)) + # rm -r build + shutil.rmtree(str(build_dir)) + # mkdir build + build_dir.mkdir() + # move back all the subdiretories under cpack directory(bin/include/lib) to build dir + moved_package_dir = llvm_dir.joinpath(packs_path[0].name) + for sub_dir in moved_package_dir.iterdir(): + shutil.move(str(sub_dir), str(build_dir)) + moved_package_dir.rmdir() + +def main(): + parser = argparse.ArgumentParser(description="build necessary LLVM libraries") + parser.add_argument( + "--platform", + type=str, + choices=["android", "arc", "darwin", "linux", "windows", "xtensa"], + help="identify current platform", + ) + parser.add_argument( + "--arch", + nargs="+", + type=str, + choices=[ + "AArch64", + "ARC", + "ARM", + "Mips", + "RISCV", + "WebAssembly", + "X86", + "Xtensa", + ], + default=[], + help="identify LLVM supported backends, separate by space, like '--arch ARM Mips X86'", + ) + parser.add_argument( + "--project", + nargs="+", + type=str, + default="", + 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( + "--use-ccache", + action="store_true", + help="enable ccache for faster incremental LLVM builds (disabled by default to reduce CI storage consumption, recommended for local development)", + ) + parser.add_argument( + "--extra-cmake-flags", + type=str, + default="", + help="custom extra cmake flags", + ) + options = parser.parse_args() + + # if the "platform" is not identified in the command line option, + # detect it + if not options.platform: + if sys.platform.startswith("win32") or sys.platform.startswith("msys"): + platform = "windows" + elif sys.platform.startswith("darwin"): + platform = "darwin" + else: + platform = "linux" + else: + platform = options.platform + + llvm_repo_and_branch = { + "arc": { + "repo": "https://github.com/llvm/llvm-project.git", + "repo_ssh": "git@github.com:llvm/llvm-project.git", + "branch": "release/18.x", + }, + "xtensa": { + "repo": "https://github.com/espressif/llvm-project.git", + "repo_ssh": "git@github.com:espressif/llvm-project.git", + "branch": "xtensa_release_18.1.2", + }, + "default": { + "repo": "https://github.com/llvm/llvm-project.git", + "repo_ssh": "git@github.com:llvm/llvm-project.git", + "branch": "llvmorg-18.1.8", + }, + } + + # retrieve the real file + current_file = pathlib.Path(__file__) + if current_file.is_symlink(): + current_file = pathlib.Path(os.readlink(current_file)) + + current_dir = current_file.parent.resolve() + deps_dir = current_dir.joinpath("../core/deps").resolve() + + try: + llvm_info = llvm_repo_and_branch.get(platform, llvm_repo_and_branch["default"]) + + 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, options.use_ccache + ) + is not None + ): + # TODO: repackage process may change in the future, this work for LLVM 15.x + if "windows" == platform: + repackage_llvm_windows(llvm_dir) + else: + repackage_llvm(llvm_dir) + + return True + except subprocess.CalledProcessError: + return False + + +if __name__ == "__main__": + sys.exit(0 if main() else 1) diff --git a/build-scripts/code_coverage.cmake b/build-scripts/code_coverage.cmake new file mode 100644 index 0000000000..e122201df0 --- /dev/null +++ b/build-scripts/code_coverage.cmake @@ -0,0 +1,37 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +function(check_ubuntu_version) +# ubuntu 2204 is the recommended environment for collecting coverage data for now +# otherwise, there will be ERRORs, when using 2404, like below and +# +# geninfo: ERROR: ('mismatch') mismatched end line for _ZN63compilation_aot_emit_memory_test_aot_check_memory_overflow_Test8TestBodyEv at /workspaces/wasm-micro-runtime/tests/unit/compilation/aot_emit_memory_test.cc:96: 96 -> 106 +# (use "geninfo --ignore-errors mismatch,mismatch ..." to suppress this warning) +# geninfo: ERROR: ('negative') Unexpected negative count '-3' for /workspaces/wasm-micro-runtime/core/iwasm/interpreter/wasm_interp_classic.c:5473. +# Perhaps you need to compile with '-fprofile-update=atomic +# (use "geninfo --ignore-errors negative,negative ..." to suppress this warning) +# +# For sure, `--ignore-errors` can be used to ignore these errors, but better to use the recommended environment. + file(READ "/etc/os-release" OS_RELEASE_CONTENT) + string(REGEX MATCH "VERSION_ID=\"([0-9]+)\\.([0-9]+)\"" _ ${OS_RELEASE_CONTENT}) + if(NOT DEFINED CMAKE_MATCH_1 OR NOT DEFINED CMAKE_MATCH_2) + message(WARNING "Unable to detect Ubuntu version. Please ensure you are using Ubuntu 22.04.") + return() + endif() + + set(UBUNTU_MAJOR_VERSION ${CMAKE_MATCH_1}) + set(UBUNTU_MINOR_VERSION ${CMAKE_MATCH_2}) + + if(NOT (UBUNTU_MAJOR_VERSION EQUAL 22 AND UBUNTU_MINOR_VERSION EQUAL 04)) + message(WARNING "Ubuntu ${UBUNTU_MAJOR_VERSION}.${UBUNTU_MINOR_VERSION} detected. Ubuntu 22.04 is recommended for collecting coverage data.") + else() + message(STATUS "Ubuntu 22.04 detected. Proceeding with coverage data collection.") + endif() +endfunction() + +check_ubuntu_version() + +# add compile options for code coverage globally +add_compile_options(--coverage -O0 -g) +link_libraries(gcov) +add_definitions (-DCOLLECT_CODE_COVERAGE) diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 4c0a356c6d..ee00203b28 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -3,23 +3,6 @@ string(TOUPPER ${WAMR_BUILD_TARGET} WAMR_BUILD_TARGET) -# Add definitions for the build platform -if (WAMR_BUILD_PLATFORM STREQUAL "linux") - add_definitions(-DBH_PLATFORM_LINUX) -elseif (WAMR_BUILD_PLATFORM STREQUAL "linux-sgx") - add_definitions(-DBH_PLATFORM_LINUX_SGX) -elseif (WAMR_BUILD_PLATFORM STREQUAL "zephyr") - add_definitions(-DBH_PLATFORM_ZEPHYR) -elseif (WAMR_BUILD_PLATFORM STREQUAL "vxworks") - add_definitions(-DBH_PLATFORM_VXWORKS) -elseif (WAMR_BUILD_PLATFORM STREQUAL "darwin") - add_definitions(-DBH_PLATFORM_DARWIN) -elseif (WAMR_BUILD_PLATFORM STREQUAL "alios-things") - add_definitions(-DBH_PLATFORM_ALIOS_THINGS) -else () - message (WARNING "-- WAMR build platform isn't set") -endif () - # Add definitions for the build target if (WAMR_BUILD_TARGET STREQUAL "X86_64") add_definitions(-DBUILD_TARGET_X86_64) @@ -43,86 +26,795 @@ elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") add_definitions(-DBUILD_TARGET_THUMB) add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") endif () +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + add_definitions(-DBUILD_TARGET_AARCH64) + add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") elseif (WAMR_BUILD_TARGET STREQUAL "MIPS") add_definitions(-DBUILD_TARGET_MIPS) elseif (WAMR_BUILD_TARGET STREQUAL "XTENSA") add_definitions(-DBUILD_TARGET_XTENSA) +elseif (WAMR_BUILD_TARGET STREQUAL "RISCV64" OR WAMR_BUILD_TARGET STREQUAL "RISCV64_LP64D") + add_definitions(-DBUILD_TARGET_RISCV64_LP64D) +elseif (WAMR_BUILD_TARGET STREQUAL "RISCV64_LP64") + add_definitions(-DBUILD_TARGET_RISCV64_LP64) +elseif (WAMR_BUILD_TARGET STREQUAL "RISCV32" OR WAMR_BUILD_TARGET STREQUAL "RISCV32_ILP32D") + add_definitions(-DBUILD_TARGET_RISCV32_ILP32D) +elseif (WAMR_BUILD_TARGET STREQUAL "RISCV32_ILP32F") + add_definitions(-DBUILD_TARGET_RISCV32_ILP32F) +elseif (WAMR_BUILD_TARGET STREQUAL "RISCV32_ILP32") + add_definitions(-DBUILD_TARGET_RISCV32_ILP32) +elseif (WAMR_BUILD_TARGET STREQUAL "ARC") + add_definitions(-DBUILD_TARGET_ARC) else () - message (FATAL_ERROR "-- WAMR build target isn't set") + message (FATAL_ERROR "-- WAMR build target isn't set") +endif () + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DBH_DEBUG=1) endif () if (CMAKE_SIZEOF_VOID_P EQUAL 8) - if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") - # Add -fPIC flag if build as 64-bit - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64" + OR WAMR_BUILD_TARGET MATCHES "AARCH64.*" OR WAMR_BUILD_TARGET MATCHES "RISCV64.*") + if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + # Add -fPIC flag if build as 64-bit + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -fPIC") + endif () else () - add_definitions (-m32) - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") + include(CheckCCompilerFlag) + Check_C_Compiler_Flag(-m32 M32_OK) + if (M32_OK OR WAMR_BUILD_TARGET STREQUAL "X86_32") + add_definitions (-m32) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") + endif () endif () endif () if (WAMR_BUILD_TARGET MATCHES "ARM.*") - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm") elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb") - set (CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-mthumb") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb") + set (CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-mthumb") +elseif (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (CMAKE_C_COMPILER_ID MATCHES ".*GNU") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mindirect-branch-register") + endif () endif () + +include (${CMAKE_CURRENT_LIST_DIR}/warnings.cmake) + if (NOT WAMR_BUILD_INTERP EQUAL 1) if (NOT WAMR_BUILD_AOT EQUAL 1) message (FATAL_ERROR "-- WAMR Interpreter and AOT must be enabled at least one") endif () endif () +if (WAMR_BUILD_FAST_JIT EQUAL 1) + if (NOT WAMR_BUILD_LAZY_JIT EQUAL 0) + # Enable Lazy JIT by default + set (WAMR_BUILD_LAZY_JIT 1) + endif () +endif () + if (WAMR_BUILD_JIT EQUAL 1) - if (WAMR_BUILD_AOT EQUAL 1) - add_definitions("-DWASM_ENABLE_JIT=1") + if (NOT WAMR_BUILD_LAZY_JIT EQUAL 0) + # Enable Lazy JIT by default + set (WAMR_BUILD_LAZY_JIT 1) + endif () + + # In Debug mode, always use release builds of pre-built dependency libraries + if (WAMR_BUILD_PLATFORM STREQUAL "windows" AND MSVC) + add_compile_options($<$:/MD>) + endif() + + if (NOT DEFINED LLVM_DIR) set (LLVM_SRC_ROOT "${WAMR_ROOT_DIR}/core/deps/llvm") - if (NOT EXISTS "${LLVM_SRC_ROOT}/build") - message (FATAL_ERROR "Cannot find LLVM dir: ${LLVM_SRC_ROOT}/build") + set (LLVM_BUILD_ROOT "${LLVM_SRC_ROOT}/build") + if (NOT EXISTS "${LLVM_BUILD_ROOT}") + message (FATAL_ERROR "Cannot find LLVM dir: ${LLVM_BUILD_ROOT}") endif () - set (CMAKE_PREFIX_PATH "${LLVM_SRC_ROOT}/build;${CMAKE_PREFIX_PATH}") - find_package(LLVM REQUIRED CONFIG) - include_directories(${LLVM_INCLUDE_DIRS}) - add_definitions(${LLVM_DEFINITIONS}) - message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") - message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") - else () - set (WAMR_BUILD_JIT 0) - message ("-- WAMR JIT disabled due to WAMR AOT is disabled") - endif () + set (CMAKE_PREFIX_PATH "${LLVM_BUILD_ROOT};${CMAKE_PREFIX_PATH}") + set (LLVM_DIR ${LLVM_BUILD_ROOT}/lib/cmake/llvm) + endif () + find_package(LLVM REQUIRED CONFIG) + include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) + add_definitions(${LLVM_DEFINITIONS}) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + + # Disable -Wredundant-move when building LLVM JIT + include(CheckCXXCompilerFlag) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + check_cxx_compiler_flag("-Wredundant-move" CXX_SUPPORTS_REDUNDANT_MOVE_FLAG) + if (CXX_SUPPORTS_REDUNDANT_MOVE_FLAG) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-redundant-move") + endif () + # Enable exporting symbols after llvm-17, or LLVM JIT may run failed + # with `llvm_orc_registerEHFrameSectionWrapper` symbol not found error + if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL "17.0.0") + set (CMAKE_ENABLE_EXPORTS 1) + endif () + endif () else () unset (LLVM_AVAILABLE_LIBS) endif () +# Version +include (${WAMR_ROOT_DIR}/build-scripts/version.cmake) + +# Package +include (${WAMR_ROOT_DIR}/build-scripts/package.cmake) + +# Sanitizers + +if (NOT DEFINED WAMR_BUILD_SANITIZER) + set(WAMR_BUILD_SANITIZER "$ENV{WAMR_BUILD_SANITIZER}") +endif() + +if (NOT WAMR_BUILD_SANITIZER STREQUAL "") + set(SUPPORTED_SANITIZERS "ubsan;asan;tsan;posan") + string(REPLACE "," ";" SANITIZER_LIST "${WAMR_BUILD_SANITIZER}") + + # Check uncompabile sanitizers + if("tsan" IN_LIST SANITIZER_LIST AND "asan" IN_LIST SANITIZER_LIST) + message(FATAL_ERROR "ThreadSanitizer (tsan) and AddressSanitizer (asan) cannot be used together!") + endif() + + # Check every sanitizer in the list + set(INVALID_SANITIZERS "") + list(REMOVE_DUPLICATES SANITIZER_LIST) + set(SANITIZER_FLAGS) + set(NO_SANITIZER_FLAGS) + foreach(sanitizer ${SANITIZER_LIST}) + string(STRIP "${sanitizer}" sanitizer) + if(NOT sanitizer IN_LIST SUPPORTED_SANITIZERS) + list(APPEND INVALID_SANITIZERS "${sanitizer}") + elseif(sanitizer STREQUAL "ubsan") + list(APPEND SANITIZER_FLAGS "undefined") + list(APPEND NO_SANITIZER_FLAGS "alignment") + elseif(sanitizer STREQUAL "asan") + if (NOT WAMR_BUILD_JIT EQUAL 1) + set(ENV{ASAN_OPTIONS} "verbosity=2 debug=true") + list(APPEND SANITIZER_FLAGS "address") + else() + message(WARNING "AddressSanitizer is not supported in LLVM JIT mode, skip it") + endif() + elseif(sanitizer STREQUAL "tsan") + list(APPEND SANITIZER_FLAGS "thread") + elseif(sanitizer STREQUAL "posan") + list(APPEND SANITIZER_FLAGS "pointer-overflow") + endif() + endforeach() + + if(INVALID_SANITIZERS) + message(FATAL_ERROR "Unsupported sanitizers: ${INVALID_SANITIZERS}") + endif() + # common flags for all sanitizers + # clang: warning: the object size sanitizer has no effect at -O0, but is explicitly enabled ... [-Winvalid-command-line-argument] + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O1 -fno-omit-frame-pointer -fno-sanitize-recover=all -fno-sanitize=alignment") + if(CMAKE_C_COMPILER_ID MATCHES ".*Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=unsigned-integer-overflow") + endif() + if(SANITIZER_FLAGS) + string(REPLACE ";" "," SANITIZER_FLAGS_STR "${SANITIZER_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=${SANITIZER_FLAGS_STR}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${SANITIZER_FLAGS_STR}") + endif() + if(NO_SANITIZER_FLAGS) + string(REPLACE ";" "," NO_SANITIZER_FLAGS_STR "${NO_SANITIZER_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-sanitize=${NO_SANITIZER_FLAGS_STR}") + endif() +endif () + +if (WAMR_BUILD_LINUX_PERF EQUAL 1) + if (NOT WAMR_BUILD_JIT AND NOT WAMR_BUILD_AOT) + message(WARNING "only support perf in aot and llvm-jit") + set(WAMR_BUILD_LINUX_PERF 0) + endif () +endif () + +if (NOT DEFINED WAMR_BUILD_SHRUNK_MEMORY) + # Enable shrunk memory by default + set (WAMR_BUILD_SHRUNK_MEMORY 1) +endif () + +######################################## +# Default values +######################################## +if (NOT DEFINED WAMR_BUILD_BULK_MEMORY) + set (WAMR_BUILD_BULK_MEMORY 1) +endif () + +if (NOT DEFINED WAMR_BUILD_BULK_MEMORY_OPT) + set (WAMR_BUILD_BULK_MEMORY_OPT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_CALL_INDIRECT_OVERLONG) + set (WAMR_BUILD_CALL_INDIRECT_OVERLONG 0) +endif () + +if (NOT DEFINED WAMR_BUILD_EXCE_HANDLING) + set (WAMR_BUILD_EXCE_HANDLING 0) +endif () + +if (NOT DEFINED WAMR_BUILD_GC) + set (WAMR_BUILD_GC 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MEMORY64) + set (WAMR_BUILD_MEMORY64 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MEMORY) + set (WAMR_BUILD_MULTI_MEMORY 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SHARED_MEMORY) + set(WAMR_BUILD_SHARED_MEMORY 0) +endif () + +if (NOT DEFINED WAMR_BUILD_STRINGREF) + set(WAMR_BUILD_STRINGREF 0) +endif () + +if (NOT DEFINED WAMR_BUILD_TAIL_CALL) + set (WAMR_BUILD_TAIL_CALL 0) +endif () + +if (NOT DEFINED WAMR_BUILD_EXTENDED_CONST_EXPR) + set (WAMR_BUILD_EXTENDED_CONST_EXPR 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIME1) + set (WAMR_BUILD_LIME1 0) +endif () + +######################################## +# Compilation options to marco +######################################## + +if (WAMR_BUILD_LIME1 EQUAL 1) + set (WAMR_BUILD_BULK_MEMORY_OPT 1) + set (WAMR_BUILD_CALL_INDIRECT_OVERLONG 1) + set (WAMR_BUILD_EXTENDED_CONST_EXPR 1) +endif () + +if (WAMR_BUILD_BULK_MEMORY EQUAL 1) + set (WAMR_BUILD_BULK_MEMORY_OPT 1) +endif () +if (WAMR_BUILD_REF_TYPES EQUAL 1) + set (WAMR_BUILD_CALL_INDIRECT_OVERLONG 1) +endif () + +include(${CMAKE_CURRENT_LIST_DIR}/unsupported_combination.cmake) + message ("-- Build Configurations:") message (" Build as target ${WAMR_BUILD_TARGET}") +message (" Build for platform ${WAMR_BUILD_PLATFORM}") message (" CMAKE_BUILD_TYPE " ${CMAKE_BUILD_TYPE}) +message (" BUILD_SHARED_LIBS " ${BUILD_SHARED_LIBS}) +################## running mode ################## if (WAMR_BUILD_INTERP EQUAL 1) message (" WAMR Interpreter enabled") else () - message (" WAMR Interpreter disbled") + message (" WAMR Interpreter disabled") +endif () +if ((WAMR_BUILD_FAST_INTERP EQUAL 1) AND (WAMR_BUILD_INTERP EQUAL 1)) + add_definitions (-DWASM_ENABLE_FAST_INTERP=1) + message (" Fast interpreter enabled") +else () + add_definitions (-DWASM_ENABLE_FAST_INTERP=0) + message (" Fast interpreter disabled") endif () if (WAMR_BUILD_AOT EQUAL 1) message (" WAMR AOT enabled") else () - message (" WAMR AOT disbled") + message (" WAMR AOT disabled") +endif () +if (WAMR_BUILD_FAST_JIT EQUAL 1) + if (WAMR_BUILD_LAZY_JIT EQUAL 1) + add_definitions("-DWASM_ENABLE_LAZY_JIT=1") + message (" WAMR Fast JIT enabled with Lazy Compilation") + else () + message (" WAMR Fast JIT enabled with Eager Compilation") + endif () +else () + message (" WAMR Fast JIT disabled") endif () if (WAMR_BUILD_JIT EQUAL 1) - message (" WAMR JIT enabled") + add_definitions("-DWASM_ENABLE_JIT=1") + if (WAMR_BUILD_LAZY_JIT EQUAL 1) + add_definitions("-DWASM_ENABLE_LAZY_JIT=1") + message (" WAMR LLVM ORC JIT enabled with Lazy Compilation") + else () + message (" WAMR LLVM ORC JIT enabled with Eager Compilation") + endif () else () - message (" WAMR JIT disbled") + message (" WAMR LLVM ORC JIT disabled") +endif () +if (WAMR_BUILD_FAST_JIT EQUAL 1 AND WAMR_BUILD_JIT EQUAL 1 + AND WAMR_BUILD_LAZY_JIT EQUAL 1) + message (" Multi-tier JIT enabled") +endif () +################## test modes ################## +if (WAMR_BUILD_SPEC_TEST EQUAL 1) + add_definitions (-DWASM_ENABLE_SPEC_TEST=1) + message (" spec test compatible mode is on") endif () +if (WAMR_BUILD_WASI_TEST EQUAL 1) + add_definitions (-DWASM_ENABLE_WASI_TEST=1) + message (" wasi test compatible mode is on") +endif () +################## native ################## if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) message (" Libc builtin enabled") else () - message (" Libc builtin disbled") + message (" Libc builtin disabled") endif () -if (WAMR_BUILD_LIBC_WASI EQUAL 1) +if (WAMR_BUILD_LIBC_UVWASI EQUAL 1) + message (" Libc WASI enabled with uvwasi implementation\n" + " WANRING:: uvwasi does not currently provide the comprehensive\n" + " file system security properties provided by some WASI runtimes.\n" + " Full support for secure file system sandboxing may or may not\n" + " be implemented in future. In the mean time, DO NOT RELY ON IT\n" + " TO RUN UNTRUSTED CODE.") +elseif (WAMR_BUILD_LIBC_WASI EQUAL 1) message (" Libc WASI enabled") else () - message (" Libc WASI disbled") + message (" Libc WASI disabled") +endif () +if (WAMR_BUILD_THREAD_MGR EQUAL 1) + message (" Thread manager enabled") +endif () +if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) + message (" Lib pthread enabled") +endif () +if (WAMR_BUILD_LIB_PTHREAD_SEMAPHORE EQUAL 1) + message (" Lib pthread semaphore enabled") +endif () +if (WAMR_BUILD_LIB_WASI_THREADS EQUAL 1) + message (" Lib wasi-threads enabled") +endif () +if (WAMR_BUILD_LIBC_EMCC EQUAL 1) + message (" Libc emcc enabled") +endif () +if (WAMR_BUILD_LIB_RATS EQUAL 1) + message (" Lib rats enabled") +endif() +if ((WAMR_BUILD_LIB_SIMDE EQUAL 1)) + message (" Lib simde enabled") +endif() +################## WAMR features ################## +if (WAMR_BUILD_MULTI_MODULE EQUAL 1) + add_definitions (-DWASM_ENABLE_MULTI_MODULE=1) + message (" Multiple modules enabled") +else () + add_definitions (-DWASM_ENABLE_MULTI_MODULE=0) + message (" Multiple modules disabled") +endif () +if (WAMR_BUILD_BULK_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_BULK_MEMORY=1) +else () + add_definitions (-DWASM_ENABLE_BULK_MEMORY=0) +endif () +if (WAMR_BUILD_BULK_MEMORY_OPT EQUAL 1) + add_definitions (-DWASM_ENABLE_BULK_MEMORY_OPT=1) +else() + add_definitions (-DWASM_ENABLE_BULK_MEMORY_OPT=0) +endif () +if (WAMR_BUILD_SHARED_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_SHARED_MEMORY=1) + message (" Shared memory enabled") +else () + add_definitions (-DWASM_ENABLE_SHARED_MEMORY=0) endif () +if (WAMR_BUILD_SHARED_HEAP EQUAL 1) + add_definitions (-DWASM_ENABLE_SHARED_HEAP=1) + message (" Shared heap enabled") +endif() +if (WAMR_BUILD_COPY_CALL_STACK EQUAL 1) + add_definitions (-DWASM_ENABLE_COPY_CALL_STACK=1) + message(" Copy callstack enabled") +endif() +if (WAMR_BUILD_MEMORY64 EQUAL 1) + # if native is 32-bit or cross-compiled to 32-bit + if (NOT WAMR_BUILD_TARGET MATCHES ".*64.*") + message (FATAL_ERROR "-- Memory64 is only available on the 64-bit platform/target") + endif() + add_definitions (-DWASM_ENABLE_MEMORY64=1) + set (WAMR_DISABLE_HW_BOUND_CHECK 1) +endif () +if (WAMR_BUILD_MULTI_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_MULTI_MEMORY=1) + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () +if (WAMR_BUILD_MINI_LOADER EQUAL 1) + add_definitions (-DWASM_ENABLE_MINI_LOADER=1) + message (" WASM mini loader enabled") +else () + add_definitions (-DWASM_ENABLE_MINI_LOADER=0) +endif () +if (WAMR_DISABLE_HW_BOUND_CHECK EQUAL 1) + add_definitions (-DWASM_DISABLE_HW_BOUND_CHECK=1) + add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=1) + message (" Hardware boundary check disabled") +else () + add_definitions (-DWASM_DISABLE_HW_BOUND_CHECK=0) + if (WAMR_DISABLE_STACK_HW_BOUND_CHECK EQUAL 1) + add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=1) + message (" Hardware boundary check for native stack disabled") + else () + add_definitions (-DWASM_DISABLE_STACK_HW_BOUND_CHECK=0) + endif () +endif () +if (WAMR_DISABLE_WAKEUP_BLOCKING_OP EQUAL 1) + add_definitions (-DWASM_DISABLE_WAKEUP_BLOCKING_OP=1) + message (" Wakeup of blocking operations disabled") +else () + add_definitions (-DWASM_DISABLE_WAKEUP_BLOCKING_OP=0) + message (" Wakeup of blocking operations enabled") +endif () +if (WAMR_BUILD_SIMD EQUAL 1) + if (WAMR_BUILD_TARGET MATCHES "RISCV64.*") + set(SIMD_ENABLED 0) + message (" SIMD disabled due to not supported on target RISCV64") + else() + set(SIMD_ENABLED 1) + message (" SIMD enabled") + endif () + add_definitions(-DWASM_ENABLE_SIMD=${SIMD_ENABLED}) +endif () +if (WAMR_BUILD_AOT_STACK_FRAME EQUAL 1) + add_definitions (-DWASM_ENABLE_AOT_STACK_FRAME=1) + message (" AOT stack frame enabled") +endif () +if (WAMR_BUILD_MEMORY_PROFILING EQUAL 1) + add_definitions (-DWASM_ENABLE_MEMORY_PROFILING=1) + message (" Memory profiling enabled") +endif () +if (WAMR_BUILD_MEMORY_TRACING EQUAL 1) + add_definitions (-DWASM_ENABLE_MEMORY_TRACING=1) + message (" Memory tracing enabled") +endif () +if (WAMR_BUILD_PERF_PROFILING EQUAL 1) + add_definitions (-DWASM_ENABLE_PERF_PROFILING=1) + message (" Performance profiling enabled") +endif () +if (DEFINED WAMR_APP_THREAD_STACK_SIZE_MAX) + add_definitions (-DAPP_THREAD_STACK_SIZE_MAX=${WAMR_APP_THREAD_STACK_SIZE_MAX}) +endif () +if (WAMR_BUILD_CUSTOM_NAME_SECTION EQUAL 1) + add_definitions (-DWASM_ENABLE_CUSTOM_NAME_SECTION=1) + message (" Custom name section enabled") +endif () +if (WAMR_BUILD_DUMP_CALL_STACK EQUAL 1) + add_definitions (-DWASM_ENABLE_DUMP_CALL_STACK=1) + message (" Dump call stack enabled") +endif () +if (WAMR_BUILD_TAIL_CALL EQUAL 1) + add_definitions (-DWASM_ENABLE_TAIL_CALL=1) +endif () +if (WAMR_BUILD_REF_TYPES EQUAL 1) + add_definitions (-DWASM_ENABLE_REF_TYPES=1) +endif () +if (WAMR_BUILD_CALL_INDIRECT_OVERLONG EQUAL 1) + add_definitions (-DWASM_ENABLE_CALL_INDIRECT_OVERLONG=1) +else () + add_definitions(-DWASM_ENABLE_CALL_INDIRECT_OVERLONG=0) +endif () +if (WAMR_BUILD_GC EQUAL 1) + if (WAMR_TEST_GC EQUAL 1) + message(" GC testing enabled") + endif() +endif () +if (WAMR_BUILD_GC EQUAL 1 AND WAMR_BUILD_GC_PERF_PROFILING EQUAL 1) + add_definitions (-DWASM_ENABLE_GC_PERF_PROFILING=1) + message (" GC performance profiling enabled") +else () + message (" GC performance profiling disabled") +endif () +if (WAMR_BUILD_STRINGREF EQUAL 1) + if (NOT DEFINED WAMR_STRINGREF_IMPL_SOURCE) + message (" Using WAMR builtin implementation for stringref") + else () + message (" Using custom implementation for stringref") + endif() +endif () +if (WAMR_BUILD_PERF_PROFILING EQUAL 1 OR + WAMR_BUILD_DUMP_CALL_STACK EQUAL 1 OR + WAMR_BUILD_GC EQUAL 1) + # Enable AOT/JIT stack frame when perf-profiling, dump-call-stack + # or GC is enabled + if (WAMR_BUILD_AOT EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) + add_definitions (-DWASM_ENABLE_AOT_STACK_FRAME=1) + endif () +endif () +if (WAMR_BUILD_EXCE_HANDLING EQUAL 1) + add_definitions (-DWASM_ENABLE_EXCE_HANDLING=1) + add_definitions (-DWASM_ENABLE_TAGS=1) + message (" Exception Handling enabled") +endif () +if (DEFINED WAMR_BH_VPRINTF) + add_definitions (-DBH_VPRINTF=${WAMR_BH_VPRINTF}) +endif () +if (DEFINED WAMR_BH_LOG) + add_definitions (-DBH_LOG=${WAMR_BH_LOG}) +endif () +if (WAMR_DISABLE_APP_ENTRY EQUAL 1) + message (" WAMR application entry functions excluded") +endif () +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + message (" Debug Interpreter enabled") +endif () +if (WAMR_BUILD_DEBUG_AOT EQUAL 1) + message (" Debug AOT enabled") +endif () +if (WAMR_BUILD_DYNAMIC_AOT_DEBUG EQUAL 1) + add_definitions (-DWASM_ENABLE_DYNAMIC_AOT_DEBUG=1) + message (" Dynamic AOT debug enabled") +endif () +if (WAMR_BUILD_LOAD_CUSTOM_SECTION EQUAL 1) + add_definitions (-DWASM_ENABLE_LOAD_CUSTOM_SECTION=1) + message (" Load custom section enabled") +endif () +if (WAMR_BUILD_GLOBAL_HEAP_POOL EQUAL 1) + add_definitions(-DWASM_ENABLE_GLOBAL_HEAP_POOL=1) + message (" Global heap pool enabled") +endif () +if (WAMR_BUILD_GLOBAL_HEAP_SIZE GREATER 0) + add_definitions (-DWASM_GLOBAL_HEAP_SIZE=${WAMR_BUILD_GLOBAL_HEAP_SIZE}) + message (" Custom global heap size: " ${WAMR_BUILD_GLOBAL_HEAP_SIZE}) +else () + # Spec test requires more heap pool size + if (WAMR_BUILD_SPEC_TEST EQUAL 1) + if (WAMR_BUILD_PLATFORM STREQUAL "linux-sgx") + math(EXPR WAMR_BUILD_GLOBAL_HEAP_SIZE "100 * 1024 * 1024") + else () + math(EXPR WAMR_BUILD_GLOBAL_HEAP_SIZE "300 * 1024 * 1024") + endif () + add_definitions (-DWASM_GLOBAL_HEAP_SIZE=${WAMR_BUILD_GLOBAL_HEAP_SIZE}) + else () + # By default, the global heap size is of 10 MB + math(EXPR WAMR_BUILD_GLOBAL_HEAP_SIZE "10 * 1024 * 1024") + add_definitions (-DWASM_GLOBAL_HEAP_SIZE=${WAMR_BUILD_GLOBAL_HEAP_SIZE}) + endif () +endif () +if (WAMR_BUILD_STACK_GUARD_SIZE GREATER 0) + add_definitions (-DWASM_STACK_GUARD_SIZE=${WAMR_BUILD_STACK_GUARD_SIZE}) + message (" Custom stack guard size: " ${WAMR_BUILD_STACK_GUARD_SIZE}) +endif () +if (WAMR_BUILD_SGX_IPFS EQUAL 1) + add_definitions (-DWASM_ENABLE_SGX_IPFS=1) + message (" SGX IPFS enabled") +endif () +if (WAMR_BUILD_WASI_NN EQUAL 1) + message (" WASI-NN enabled") + add_definitions (-DWASM_ENABLE_WASI_NN=1) + # Variant backends + if (NOT WAMR_BUILD_WASI_NN_TFLITE EQUAL 1 AND + NOT WAMR_BUILD_WASI_NN_OPENVINO EQUAL 1 AND + NOT WAMR_BUILD_WASI_NN_LLAMACPP EQUAL 1 AND + NOT WAMR_BUILD_WASI_NN_ONNX EQUAL 1) + message (FATAL_ERROR " Need to select a backend for WASI-NN") + endif () + + if (WAMR_BUILD_WASI_NN_TFLITE EQUAL 1) + message (" WASI-NN: backend tflite enabled") + add_definitions (-DWASM_ENABLE_WASI_NN_TFLITE) + endif () + if (WAMR_BUILD_WASI_NN_OPENVINO EQUAL 1) + message (" WASI-NN: backend openvino enabled") + add_definitions (-DWASM_ENABLE_WASI_NN_OPENVINO) + endif () + if (WAMR_BUILD_WASI_NN_LLAMACPP EQUAL 1) + message (" WASI-NN: backend llamacpp enabled") + add_definitions (-DWASM_ENABLE_WASI_NN_LLAMACPP) + endif () + if (WAMR_BUILD_WASI_NN_ONNX EQUAL 1) + message (" WASI-NN: backend onnx enabled") + add_definitions (-DWASM_ENABLE_WASI_NN_ONNX) + endif () + # Variant devices + if (WAMR_BUILD_WASI_NN_ENABLE_GPU EQUAL 1) + message (" WASI-NN: GPU enabled") + add_definitions (-DWASM_ENABLE_WASI_NN_GPU=1) + endif () + if (WAMR_BUILD_WASI_NN_ENABLE_EXTERNAL_DELEGATE EQUAL 1) + message (" WASI-NN: External Delegation enabled") + add_definitions (-DWASM_ENABLE_WASI_NN_EXTERNAL_DELEGATE=1) + endif () + if (DEFINED WAMR_BUILD_WASI_NN_EXTERNAL_DELEGATE_PATH) + add_definitions (-DWASM_WASI_NN_EXTERNAL_DELEGATE_PATH="${WAMR_BUILD_WASI_NN_EXTERNAL_DELEGATE_PATH}") + endif () + if (NOT DEFINED WAMR_BUILD_WASI_EPHEMERAL_NN) + set(WAMR_BUILD_WASI_EPHEMERAL_NN 1) + endif() + if (WAMR_BUILD_WASI_EPHEMERAL_NN EQUAL 1) + message (" WASI-NN: use 'wasi_ephemeral_nn' instead of 'wasi-nn'") + add_definitions (-DWASM_ENABLE_WASI_EPHEMERAL_NN=1) + endif() +endif () +if (WAMR_BUILD_ALLOC_WITH_USER_DATA EQUAL 1) + add_definitions(-DWASM_MEM_ALLOC_WITH_USER_DATA=1) +endif() +if (WAMR_BUILD_WASM_CACHE EQUAL 1) + add_definitions (-DWASM_ENABLE_WASM_CACHE=1) + message (" Wasm files cache enabled") +endif () +if (WAMR_BUILD_MODULE_INST_CONTEXT EQUAL 1) + add_definitions (-DWASM_ENABLE_MODULE_INST_CONTEXT=1) + message (" Module instance context enabled") +endif () +if (WAMR_BUILD_GC_HEAP_VERIFY EQUAL 1) + add_definitions (-DBH_ENABLE_GC_VERIFY=1) + message (" GC heap verification enabled") +endif () +if ("$ENV{COLLECT_CODE_COVERAGE}" STREQUAL "1" OR COLLECT_CODE_COVERAGE EQUAL 1) + include(${CMAKE_CURRENT_LIST_DIR}/code_coverage.cmake) + message (" Collect code coverage enabled") +endif () +if (WAMR_BUILD_STATIC_PGO EQUAL 1) + add_definitions (-DWASM_ENABLE_STATIC_PGO=1) + message (" AOT static PGO enabled") +endif () +if (WAMR_BUILD_TARGET STREQUAL "X86_64" + AND WAMR_BUILD_PLATFORM STREQUAL "linux") + if (WAMR_DISABLE_WRITE_GS_BASE EQUAL 1) + # disabled by user + set (DISABLE_WRITE_GS_BASE 1) + elseif (WAMR_DISABLE_WRITE_GS_BASE EQUAL 0) + # enabled by user + set (DISABLE_WRITE_GS_BASE 0) + elseif (CMAKE_CROSSCOMPILING) + # disabled in cross compilation environment + set (DISABLE_WRITE_GS_BASE 1) + else () + # auto-detected by the compiler + set (TEST_WRGSBASE_SOURCE "${CMAKE_BINARY_DIR}/test_wrgsbase.c") + file (WRITE "${TEST_WRGSBASE_SOURCE}" " + #include + #include + int main() { + uint64_t value; + asm volatile (\"wrgsbase %0\" : : \"r\"(value)); + printf(\"WRGSBASE instruction is available.\\n\"); + return 0; + }") + # Try to compile and run the test program + try_run (TEST_WRGSBASE_RESULT + TEST_WRGSBASE_COMPILED + ${CMAKE_BINARY_DIR}/test_wrgsbase + SOURCES ${TEST_WRGSBASE_SOURCE} + CMAKE_FLAGS -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + ) + #message("${TEST_WRGSBASE_COMPILED}, ${TEST_WRGSBASE_RESULT}") + if (TEST_WRGSBASE_RESULT EQUAL 0) + set (DISABLE_WRITE_GS_BASE 0) + else () + set (DISABLE_WRITE_GS_BASE 1) + endif () + endif () + if (DISABLE_WRITE_GS_BASE EQUAL 1) + add_definitions (-DWASM_DISABLE_WRITE_GS_BASE=1) + message (" Write linear memory base addr to x86 GS register disabled") + else () + add_definitions (-DWASM_DISABLE_WRITE_GS_BASE=0) + message (" Write linear memory base addr to x86 GS register enabled") + endif () +endif () +if (WAMR_CONFIGURABLE_BOUNDS_CHECKS EQUAL 1) + add_definitions (-DWASM_CONFIGURABLE_BOUNDS_CHECKS=1) + message (" Configurable bounds checks enabled") +endif () +if (WAMR_BUILD_LINUX_PERF EQUAL 1) + add_definitions (-DWASM_ENABLE_LINUX_PERF=1) + message (" Linux perf support enabled") +endif () +if (WAMR_BUILD_AOT EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) + if (NOT DEFINED WAMR_BUILD_QUICK_AOT_ENTRY) + # Enable quick aot/jit entries by default + set (WAMR_BUILD_QUICK_AOT_ENTRY 1) + endif () + if (WAMR_BUILD_QUICK_AOT_ENTRY EQUAL 1) + add_definitions (-DWASM_ENABLE_QUICK_AOT_ENTRY=1) + message (" Quick AOT/JIT entries enabled") + else () + add_definitions (-DWASM_ENABLE_QUICK_AOT_ENTRY=0) + message (" Quick AOT/JIT entries disabled") + endif () +else () + # Disable quick aot/jit entries for interp and fast-jit + add_definitions (-DWASM_ENABLE_QUICK_AOT_ENTRY=0) +endif () +if (WAMR_BUILD_AOT EQUAL 1) + if (NOT DEFINED WAMR_BUILD_AOT_INTRINSICS) + # Enable aot intrinsics by default + set (WAMR_BUILD_AOT_INTRINSICS 1) + endif () + if (WAMR_BUILD_AOT_INTRINSICS EQUAL 1) + add_definitions (-DWASM_ENABLE_AOT_INTRINSICS=1) + message (" AOT intrinsics enabled") + else () + add_definitions (-DWASM_ENABLE_AOT_INTRINSICS=0) + message (" AOT intrinsics disabled") + endif () +else () + # Disable aot intrinsics for interp, fast-jit and llvm-jit + add_definitions (-DWASM_ENABLE_AOT_INTRINSICS=0) +endif () +if (WAMR_BUILD_ALLOC_WITH_USAGE EQUAL 1) + add_definitions(-DWASM_MEM_ALLOC_WITH_USAGE=1) +endif() +if (NOT WAMR_BUILD_SANITIZER STREQUAL "") + message (" Sanitizer ${WAMR_BUILD_SANITIZER} enabled") +endif () +if (WAMR_BUILD_SHRUNK_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_SHRUNK_MEMORY=1) + message (" Shrunk memory enabled") +else () + add_definitions (-DWASM_ENABLE_SHRUNK_MEMORY=0) + message (" Shrunk memory disabled") +endif() +if (WAMR_BUILD_AOT_VALIDATOR EQUAL 1) + message (" AOT validator enabled") + add_definitions (-DWASM_ENABLE_AOT_VALIDATOR=1) +endif () +if (WAMR_BUILD_INSTRUCTION_METERING EQUAL 1) + message (" Instruction metering enabled") + add_definitions (-DWASM_ENABLE_INSTRUCTION_METERING=1) +endif () +if (WAMR_BUILD_EXTENDED_CONST_EXPR EQUAL 1) + message (" Extended constant expression enabled") + add_definitions(-DWASM_ENABLE_EXTENDED_CONST_EXPR=1) +else() + message (" Extended constant expression disabled") + add_definitions(-DWASM_ENABLE_EXTENDED_CONST_EXPR=0) +endif () +if (WAMR_BUILD_LIME1 EQUAL 1) + message (" Lime1 enabled") +endif () +if (WAMR_BUILD_BRANCH_HINTS EQUAL 1) + message (" Branch hints enabled") + add_definitions(-DWASM_ENABLE_BRANCH_HINTS=1) +endif () + +######################################## +# Show Phase4 Wasm proposals status. +######################################## +message ( +"-- About Wasm Proposals:\n" +" Always-on:\n" +" \"Import/Export of Mutable Globals\"\n" +" \"Multi-value\"\n" +" \"Non-trapping float-to-int Conversions\"\n" +" \"Sign-extension Operators\"\n" +" \"WebAssembly C and C++ API\"\n" +" Configurable. 0 is OFF. 1 is ON:\n" +" \"Branch Hinting\" via WAMR_BUILD_BRANCH_HINTS: ${WAMR_BUILD_BRANCH_HINTS}\n" +" \"Bulk Memory Operation\" via WAMR_BUILD_BULK_MEMORY: ${WAMR_BUILD_BULK_MEMORY}\n" +" \"Bulk-memory-opt\" via WAMR_BUILD_BULK_MEMORY_OPT: ${WAMR_BUILD_BULK_MEMORY_OPT}\n" +" \"Call-indirect-overlong\" via WAMR_BUILD_CALL_INDIRECT_OVERLONG: ${WAMR_BUILD_CALL_INDIRECT_OVERLONG}\n" +" \"Extended Constant Expressions\" via WAMR_BUILD_EXTENDED_CONST_EXPR: ${WAMR_BUILD_EXTENDED_CONST_EXPR}\n" +" \"Fixed-width SIMD\" via WAMR_BUILD_SIMD: ${WAMR_BUILD_SIMD}\n" +" \"Garbage Collection\" via WAMR_BUILD_GC: ${WAMR_BUILD_GC}\n" +" \"Legacy Exception Handling\" via WAMR_BUILD_EXCE_HANDLING: ${WAMR_BUILD_EXCE_HANDLING}\n" +" \"Memory64\" via WAMR_BUILD_MEMORY64: ${WAMR_BUILD_MEMORY64}\n" +" \"Multiple Memories\" via WAMR_BUILD_MULTI_MEMORY: ${WAMR_BUILD_MULTI_MEMORY}\n" +" \"Reference Types\" via WAMR_BUILD_REF_TYPES: ${WAMR_BUILD_REF_TYPES}\n" +" \"Reference-Typed Strings\" via WAMR_BUILD_STRINGREF: ${WAMR_BUILD_STRINGREF}\n" +" \"Tail Call\" via WAMR_BUILD_TAIL_CALL: ${WAMR_BUILD_TAIL_CALL}\n" +" \"Threads\" via WAMR_BUILD_SHARED_MEMORY: ${WAMR_BUILD_SHARED_MEMORY}\n" +" \"Typed Function References\" via WAMR_BUILD_GC: ${WAMR_BUILD_GC}\n" +" Unsupported (>= Phase4):\n" +" \"Custom Annotation Syntax in the Text Format\"\n" +" \"Exception Handling\"\n" +" \"JS String Builtins\"\n" +" \"Relaxed SIMD\"\n" +) diff --git a/build-scripts/esp-idf/README.md b/build-scripts/esp-idf/README.md new file mode 100644 index 0000000000..25fe491ea6 --- /dev/null +++ b/build-scripts/esp-idf/README.md @@ -0,0 +1,31 @@ +# wasm-micro-runtime as ESP-IDF component + +You can build an ESP-IDF project with wasm-micro-runtime as a component: + +- Make sure you have the ESP-IDF properly installed and setup +- In particular have the following paths set: + - `WAMR_PATH` to point to your wasm-micro-runtime repository + - `IDF_PATH` to point to your ESP-IDF + - `source $IDF_PATH/export.sh` +- Create a new project, e.g.: `idf.py create-project wamr-hello` +- In the newly created project folder edit the `CMakeList.txt`: + + ``` + cmake_minimum_required(VERSION 3.14) + + include($ENV{IDF_PATH}/tools/cmake/project.cmake) + + set (COMPONENTS ${IDF_TARGET} main freertos esptool_py wamr) + + list(APPEND EXTRA_COMPONENT_DIRS "$ENV{WAMR_PATH}/build-scripts/esp-idf") + + project(wamr-hello) + ``` +- Develop your project in it's `main` component folder. + +You can find an example [here](../../product-mini/platforms/esp-idf). + +- Set target platform: `idf.py set-target esp32c3` +- Build: `idf.py build` +- Flash: `idf.py flash` +- Check the output: `idf.py monitor` \ No newline at end of file diff --git a/build-scripts/esp-idf/wamr/CMakeLists.txt b/build-scripts/esp-idf/wamr/CMakeLists.txt new file mode 100644 index 0000000000..d851ed4c75 --- /dev/null +++ b/build-scripts/esp-idf/wamr/CMakeLists.txt @@ -0,0 +1,115 @@ +# Copyright (C) 2021 Intel Corporation and others. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Set WAMR's build options +if (NOT CMAKE_BUILD_EARLY_EXPANSION) + + if (CONFIG_IDF_TARGET_ARCH_RISCV) + if (CONFIG_IDF_TARGET_ESP32P4) + set (WAMR_BUILD_TARGET "RISCV32_ILP32F") + else () + set (WAMR_BUILD_TARGET "RISCV32_ILP32") + endif () + elseif (CONFIG_IDF_TARGET_ARCH_XTENSA) + set (WAMR_BUILD_TARGET "XTENSA") + else () + message (FATAL_ERROR "Arch ${CONFIG_IDF_TARGET_ARCH} is not supported") + endif () + + set (WAMR_BUILD_PLATFORM "esp-idf") + + if (CONFIG_WAMR_BUILD_DEBUG) + set (CMAKE_BUILD_TYPE Debug) + else () + set (CMAKE_BUILD_TYPE Release) + endif () + + if (CONFIG_WAMR_ENABLE_INTERP) + set (WAMR_BUILD_INTERP 1) + endif () + + if (CONFIG_WAMR_INTERP_FAST) + set (WAMR_BUILD_FAST_INTERP 1) + endif () + + if (CONFIG_WAMR_ENABLE_AOT) + set (WAMR_BUILD_AOT 1) + endif () + + if (CONFIG_WAMR_ENABLE_LIBC_BUILTIN) + set (WAMR_BUILD_LIBC_BUILTIN 1) + endif () + + if (CONFIG_WAMR_INTERP_LOADER_MINI) + set (WAMR_BUILD_MINI_LOADER 1) + endif () + + if (CONFIG_WAMR_ENABLE_MULTI_MODULE) + set (WAMR_BUILD_MULTI_MODULE 1) + endif () + + if (CONFIG_WAMR_ENABLE_SHARED_MEMORY) + set (WAMR_BUILD_SHARED_MEMORY 1) + endif () + + if (CONFIG_WAMR_ENABLE_MEMORY_PROFILING) + set (WAMR_BUILD_MEMORY_PROFILING 1) + endif () + + if (CONFIG_WAMR_ENABLE_MEMORY_TRACING) + set (WAMR_BUILD_MEMORY_TRACING 1) + endif () + + if (CONFIG_WAMR_ENABLE_PERF_PROFILING) + set (WAMR_BUILD_PERF_PROFILING 1) + endif () + + if (CONFIG_WAMR_ENABLE_REF_TYPES) + set (WAMR_BUILD_REF_TYPES 1) + endif () + + if (CONFIG_WAMR_ENABLE_LIBC_WASI) + set (WAMR_BUILD_LIBC_WASI 1) + endif () + + if (CONFIG_WAMR_ENABLE_LIB_PTHREAD) + set (WAMR_BUILD_LIB_PTHREAD 1) + endif () + + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + + list (APPEND srcs "${WAMR_RUNTIME_LIB_SOURCE}" + "${PLATFORM_SHARED_SOURCE}") + + set (include_dirs "${IWASM_DIR}/include" + "${UTILS_SHARED_DIR}" + "${PLATFORM_SHARED_DIR}" + "${PLATFORM_SHARED_DIR}/../include" + "${IWASM_COMMON_DIR}") +endif () + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + REQUIRES pthread lwip esp_timer + KCONFIG ${CMAKE_CURRENT_LIST_DIR}/Kconfig) + +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") + +if (CONFIG_IDF_TARGET_ARCH_RISCV) + if (CONFIG_IDF_TARGET_ESP32P4) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DBUILD_TARGET_RISCV32_ILP32F=1) + else () + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DBUILD_TARGET_RISCV32_ILP32=1) + endif () +elseif (CONFIG_IDF_TARGET_ARCH_XTENSA) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DBUILD_TARGET_XTENSA=1) +endif () + +if (CONFIG_WAMR_ENABLE_AOT) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DWASM_ENABLE_AOT=1) +endif () + +if (CONFIG_WAMR_ENABLE_INTERP) + target_compile_definitions(${COMPONENT_LIB} PUBLIC -DWASM_ENABLE_INTERP=1) +endif () diff --git a/build-scripts/esp-idf/wamr/Kconfig b/build-scripts/esp-idf/wamr/Kconfig new file mode 100644 index 0000000000..5ed496255d --- /dev/null +++ b/build-scripts/esp-idf/wamr/Kconfig @@ -0,0 +1,77 @@ +menu "WASM Micro Runtime" + choice WAMR_BUILD_TYPE + prompt "Build type" + default WAMR_BUILD_RELEASE + + config WAMR_BUILD_RELEASE + bool "Release" + + config WAMR_BUILD_DEBUG + bool "Debug" + endchoice + + config WAMR_ENABLE_AOT + bool "AOT" + default y + + menuconfig WAMR_ENABLE_INTERP + bool "Interpreter" + default y + + if WAMR_ENABLE_INTERP + + choice WAMR_INTERP_MODE + prompt "Interpreter mode" + default WAMR_INTERP_FAST + + config WAMR_INTERP_CLASSIC + bool "Classic" + + config WAMR_INTERP_FAST + bool "Fast" + endchoice + + choice WAMR_INTERP_LOADER_MODE + prompt "Loader mode" + default WAMR_INTERP_LOADER_NORMAL + + config WAMR_INTERP_LOADER_NORMAL + bool "Normal" + + config WAMR_INTERP_LOADER_MINI + bool "Mini" + endchoice + endif + + config WAMR_ENABLE_LIB_PTHREAD + bool "Lib pthread" + default y + + config WAMR_ENABLE_LIBC_BUILTIN + bool "Libc builtin" + default y + + config WAMR_ENABLE_LIBC_WASI + bool "Libc WASI" + default y + + config WAMR_ENABLE_MEMORY_PROFILING + bool "Memory profiling" + default n + + config WAMR_ENABLE_MULTI_MODULE + bool "Multi module" + default n + + config WAMR_ENABLE_PERF_PROFILING + bool "Performance profiling" + default n + + config WAMR_ENABLE_REF_TYPES + bool "Reference types" + default n + + config WAMR_ENABLE_SHARED_MEMORY + bool "Shared memory" + default n +endmenu diff --git a/build-scripts/involve_boringssl.cmake b/build-scripts/involve_boringssl.cmake new file mode 100644 index 0000000000..a0e069ba88 --- /dev/null +++ b/build-scripts/involve_boringssl.cmake @@ -0,0 +1,41 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +message(STATUS "involving boringssl...") + +include(ExternalProject) + +ExternalProject_Add(boringssl + PREFIX external/boringssl + # follow envoy, which tracks BoringSSL, which tracks Chromium + # https://github.com/envoyproxy/envoy/blob/main/bazel/repository_locations.bzl#L112 + # chromium-105.0.5195.37 (linux/beta) + URL https://github.com/google/boringssl/archive/098695591f3a2665fccef83a3732ecfc99acdcdd.tar.gz + URL_HASH SHA256=e141448cf6f686b6e9695f6b6459293fd602c8d51efe118a83106752cf7e1280 + DOWNLOAD_EXTRACT_TIMESTAMP NEW + # SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../external/boringssl + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/src/boringssl-build/libssl.a + ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ + && ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/src/boringssl-build/libcrypto.a + ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ + && ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/src/boringssl/src/include/openssl + ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/openssl +) + +add_library(boringssl_ssl STATIC IMPORTED GLOBAL) +set_target_properties( + boringssl_ssl + PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/libssl.a + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ +) +add_dependencies(boringssl_ssl boringssl) + +add_library(boringssl_crypto STATIC IMPORTED GLOBAL) +set_target_properties( + boringssl_crypto + PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/libcrypto.a + INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/external/boringssl/ +) +add_dependencies(boringssl_crypto boringssl) \ No newline at end of file diff --git a/build-scripts/iwasmConfig.cmake.in b/build-scripts/iwasmConfig.cmake.in new file mode 100644 index 0000000000..05ec5a8cda --- /dev/null +++ b/build-scripts/iwasmConfig.cmake.in @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/iwasmTargets.cmake") diff --git a/build-scripts/lldb_wasm.patch b/build-scripts/lldb_wasm.patch new file mode 100644 index 0000000000..83221ef6ac --- /dev/null +++ b/build-scripts/lldb_wasm.patch @@ -0,0 +1,5906 @@ +diff --git a/lldb/bindings/CMakeLists.txt b/lldb/bindings/CMakeLists.txt +index 9759b069fdc4..25b427f8bcf2 100644 +--- a/lldb/bindings/CMakeLists.txt ++++ b/lldb/bindings/CMakeLists.txt +@@ -26,8 +26,6 @@ set(SWIG_COMMON_FLAGS + -features autodoc + -I${LLDB_SOURCE_DIR}/include + -I${CMAKE_CURRENT_SOURCE_DIR} +- -D__STDC_LIMIT_MACROS +- -D__STDC_CONSTANT_MACROS + ${DARWIN_EXTRAS} + ) + +diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig +index c9a6d0f06056..021c7683d170 100644 +--- a/lldb/bindings/interfaces.swig ++++ b/lldb/bindings/interfaces.swig +@@ -1,8 +1,5 @@ + /* Various liblldb typedefs that SWIG needs to know about. */ + #define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */ +-/* The ISO C99 standard specifies that in C++ implementations limit macros such +- as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ +-#define __STDC_LIMIT_MACROS + %include "stdint.i" + + %include "lldb/lldb-defines.h" +diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig +index b1ace4ff3b1e..5f8f4aa678c4 100644 +--- a/lldb/bindings/python/python-typemaps.swig ++++ b/lldb/bindings/python/python-typemaps.swig +@@ -439,7 +439,7 @@ bool SetNumberFromPyObject(double &number, PyObject *obj) { + + %typemap(out) lldb::FileSP { + $result = nullptr; +- lldb::FileSP &sp = $1; ++ const lldb::FileSP &sp = $1; + if (sp) { + PythonFile pyfile = unwrapOrSetPythonException(PythonFile::FromFile(*sp)); + if (!pyfile.IsValid()) +diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h +index f2e2a0d22784..426d1129bd10 100644 +--- a/lldb/include/lldb/Breakpoint/Breakpoint.h ++++ b/lldb/include/lldb/Breakpoint/Breakpoint.h +@@ -9,6 +9,7 @@ + #ifndef LLDB_BREAKPOINT_BREAKPOINT_H + #define LLDB_BREAKPOINT_BREAKPOINT_H + ++#include + #include + #include + #include +diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h +index dd7100c4616c..97d70daadbdc 100644 +--- a/lldb/include/lldb/Core/Module.h ++++ b/lldb/include/lldb/Core/Module.h +@@ -41,6 +41,7 @@ + + namespace lldb_private { + class CompilerDeclContext; ++class DWARFEvaluatorFactory; + class Function; + class Log; + class ObjectFile; +@@ -859,6 +860,8 @@ public: + /// Update the ArchSpec to a more specific variant. + bool MergeArchitecture(const ArchSpec &arch_spec); + ++ DWARFEvaluatorFactory *GetDWARFExpressionEvaluatorFactory(); ++ + /// \class LookupInfo Module.h "lldb/Core/Module.h" + /// A class that encapsulates name lookup information. + /// +@@ -985,6 +988,8 @@ protected: + m_first_file_changed_log : 1; /// See if the module was modified after it + /// was initially opened. + ++ std::unique_ptr m_dwarf_evaluator_factory; ++ + /// Resolve a file or load virtual address. + /// + /// Tries to resolve \a vm_addr as a file address (if \a +diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h +index be91929c62e1..8d876fc1fa2f 100644 +--- a/lldb/include/lldb/Core/PluginManager.h ++++ b/lldb/include/lldb/Core/PluginManager.h +@@ -508,6 +508,17 @@ public: + static bool CreateSettingForStructuredDataPlugin( + Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, + ConstString description, bool is_global_property); ++ ++ // DWARFEvaluatorFactory ++ static bool ++ RegisterPlugin(ConstString name, const char *description, ++ DWARFEvaluatorFactoryCreateInstance create_callback); ++ ++ static bool ++ UnregisterPlugin(DWARFEvaluatorFactoryCreateInstance create_callback); ++ ++ static DWARFEvaluatorFactoryCreateInstance ++ GetDWARFEvaluatorFactoryCreateCallbackAtIndex(uint32_t idx); + }; + + } // namespace lldb_private +diff --git a/lldb/include/lldb/Expression/DWARFEvaluator.h b/lldb/include/lldb/Expression/DWARFEvaluator.h +new file mode 100644 +index 000000000000..6811cbeae3d3 +--- /dev/null ++++ b/lldb/include/lldb/Expression/DWARFEvaluator.h +@@ -0,0 +1,110 @@ ++//===-- DWARFEvaluator.h ----------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLDB_EXPRESSION_DWARFEVALUATOR_H ++#define LLDB_EXPRESSION_DWARFEVALUATOR_H ++ ++#include "lldb/lldb-private.h" ++#include ++ ++namespace lldb_private { ++ ++class DWARFExpression; ++ ++/// \class DWARFEvaluator DWARFEvaluator.h ++/// "lldb/Expression/DWARFEvaluator.h" Evaluates DWARF opcodes. ++/// ++class DWARFEvaluator { ++public: ++ /// Crates a DWARF location expression evaluator ++ /// ++ /// \param[in] dwarf_expression ++ /// The DWARF expression to evaluate. ++ /// ++ /// \param[in] exe_ctx ++ /// The execution context in which to evaluate the location ++ /// expression. The location expression may access the target's ++ /// memory, especially if it comes from the expression parser. ++ /// ++ /// \param[in] reg_ctx ++ /// An optional parameter which provides a RegisterContext for use ++ /// when evaluating the expression (i.e. for fetching register values). ++ /// Normally this will come from the ExecutionContext's StackFrame but ++ /// in the case where an expression needs to be evaluated while building ++ /// the stack frame list, this short-cut is available. ++ /// ++ /// \param[in] initial_value_ptr ++ /// A value to put on top of the interpreter stack before evaluating ++ /// the expression, if the expression is parametrized. Can be NULL. ++ /// ++ /// \param[in] object_address_ptr ++ /// ++ DWARFEvaluator(const DWARFExpression &dwarf_expression, ++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx, ++ const Value *initial_value_ptr, ++ const Value *object_address_ptr); ++ ++ /// DWARFEvaluator protocol. ++ /// \{ ++ ++ /// Evaluate the DWARF location expression ++ /// ++ /// \param[in] result ++ /// A value into which the result of evaluating the expression is ++ /// to be placed. ++ /// ++ /// \param[in] error_ptr ++ /// If non-NULL, used to report errors in expression evaluation. ++ /// ++ /// \return ++ /// True on success; false otherwise. If error_ptr is non-NULL, ++ /// details of the failure are provided through it. ++ virtual bool Evaluate(Value &result, Status *error_ptr); ++ ++ /// Evaluate the DWARF location expression with the opcodes specified. ++ /// ++ /// \param[in] opcodes ++ /// The DWARF opcodes to evaluate. ++ /// ++ /// \param[in] result ++ /// A value into which the result of evaluating the expression is ++ /// to be placed. ++ /// ++ /// \param[in] error_ptr ++ /// If non-NULL, used to report errors in expression evaluation. ++ /// ++ /// \return ++ /// True on success; false otherwise. If error_ptr is non-NULL, ++ /// details of the failure are provided through it. ++ virtual bool Evaluate(const DataExtractor &opcodes, Value &result, ++ Status *error_ptr); ++ ++ /// Evaluates a specific DWARF opcode in the context of a DWARF expression ++ virtual bool Evaluate(const uint8_t op, Process *process, StackFrame *frame, ++ std::vector &stack, const DataExtractor &opcodes, ++ lldb::offset_t &offset, Value &pieces, ++ uint64_t &op_piece_offset, Log *log, Status *error_ptr); ++ ++ /// \} ++ ++protected: ++ const DWARFExpression &m_dwarf_expression; ++ ExecutionContext *m_exe_ctx; ++ RegisterContext *m_reg_ctx; ++ const Value *m_initial_value_ptr; ++ const Value *m_object_address_ptr; ++ ++private: ++ DWARFEvaluator(const DWARFEvaluator &); ++ const DWARFEvaluator &operator=(const DWARFEvaluator &) = delete; ++ ++}; ++ ++} // namespace lldb_private ++ ++#endif // LLDB_EXPRESSION_DWARFEVALUATOR_H +diff --git a/lldb/include/lldb/Expression/DWARFEvaluatorFactory.h b/lldb/include/lldb/Expression/DWARFEvaluatorFactory.h +new file mode 100644 +index 000000000000..f3b496c580e4 +--- /dev/null ++++ b/lldb/include/lldb/Expression/DWARFEvaluatorFactory.h +@@ -0,0 +1,56 @@ ++//===-- DWARFEvaluatorFactory.h ---------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLDB_EXPRESSION_DWARFEVALUATORFACTORY_H ++#define LLDB_EXPRESSION_DWARFEVALUATORFACTORY_H ++ ++#include "lldb/Core/PluginInterface.h" ++#include "lldb/Utility/ConstString.h" ++#include "lldb/lldb-private.h" ++ ++class DWARFUnit; ++ ++namespace lldb_private { ++ ++class DWARFEvaluator; ++class DWARFExpression; ++ ++/// \class DWARFEvaluatorFactory DWARFEvaluatorFactory.h ++/// "lldb/Expression/DWARFEvaluatorFactory.h" Factory class that allows the ++/// registration of platform-specific DWARF expression evaluators, used to ++/// handle platform-specific DWARF opcodes. ++class DWARFEvaluatorFactory : public PluginInterface { ++public: ++ static std::unique_ptr FindPlugin(Module *module); ++ ++ /// PluginInterface protocol. ++ /// \{ ++ ConstString GetPluginName() override; ++ ++ uint32_t GetPluginVersion() override { return 1; } ++ /// \} ++ ++ DWARFEvaluatorFactory() {} ++ ++ /// DWARFEvaluatorFactory protocol. ++ /// \{ ++ virtual std::unique_ptr ++ CreateDWARFEvaluator(const DWARFExpression &dwarf_expression, ++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx, ++ const Value *initial_value_ptr, ++ const Value *object_address_ptr); ++ /// \} ++ ++private: ++ DWARFEvaluatorFactory(const DWARFEvaluatorFactory &); ++ const DWARFEvaluatorFactory &operator=(const DWARFEvaluatorFactory &) = delete; ++}; ++ ++} // namespace lldb_private ++ ++#endif // LLDB_EXPRESSION_DWARFEVALUATORFACTORY_H +diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h +index 1490ac2d614a..35c741d4e6ba 100644 +--- a/lldb/include/lldb/Expression/DWARFExpression.h ++++ b/lldb/include/lldb/Expression/DWARFExpression.h +@@ -120,6 +120,10 @@ public: + + void SetModule(const lldb::ModuleSP &module) { m_module_wp = module; } + ++ lldb::ModuleSP GetModule() const { return m_module_wp.lock(); } ++ ++ const DWARFUnit *GetDWARFCompileUnit() const { return m_dwarf_cu; } ++ + bool ContainsThreadLocalStorage() const; + + bool LinkThreadLocalStorage( +@@ -140,7 +144,7 @@ public: + lldb::addr_t func_file_addr); + + /// Return the call-frame-info style register kind +- int GetRegisterKind(); ++ lldb::RegisterKind GetRegisterKind() const; + + /// Set the call-frame-info style register kind + /// +@@ -219,6 +223,9 @@ public: + + bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op); + ++ static lldb::addr_t ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, ++ uint32_t index); ++ + llvm::Optional + GetLocationExpression(lldb::addr_t load_function_start, + lldb::addr_t addr) const; +diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h +index aaa2470d2931..c15f2db52fbc 100644 +--- a/lldb/include/lldb/Target/Process.h ++++ b/lldb/include/lldb/Target/Process.h +@@ -1434,7 +1434,7 @@ public: + /// vm_addr, \a buf, and \a size updated appropriately. Zero is + /// returned in the case of an error. + virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, +- Status &error); ++ Status &error, ExecutionContext *exe_ctx = nullptr); + + /// Read of memory from a process. + /// +diff --git a/lldb/include/lldb/Target/ProcessTrace.h b/lldb/include/lldb/Target/ProcessTrace.h +index 7b9d6b13dd6f..9525fc9750fd 100644 +--- a/lldb/include/lldb/Target/ProcessTrace.h ++++ b/lldb/include/lldb/Target/ProcessTrace.h +@@ -59,7 +59,7 @@ public: + bool WarnBeforeDetach() const override { return false; } + + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, +- Status &error) override; ++ Status &error, ExecutionContext *exe_ctx = nullptr) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; +diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h +index ad5298151e4a..5a3c0b27a738 100644 +--- a/lldb/include/lldb/lldb-forward.h ++++ b/lldb/include/lldb/lldb-forward.h +@@ -74,6 +74,7 @@ class Disassembler; + class DumpValueObjectOptions; + class DynamicCheckerFunctions; + class DynamicLoader; ++class DWARFEvaluatorFactory; + class Editline; + class EmulateInstruction; + class Environment; +diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h +index 2ed083ec8ae9..f4d500d198e8 100644 +--- a/lldb/include/lldb/lldb-private-interfaces.h ++++ b/lldb/include/lldb/lldb-private-interfaces.h +@@ -113,6 +113,8 @@ typedef lldb::REPLSP (*REPLCreateInstance)(Status &error, + const char *repl_options); + typedef int (*ComparisonFunction)(const void *, const void *); + typedef void (*DebuggerInitializeCallback)(Debugger &debugger); ++typedef DWARFEvaluatorFactory *(*DWARFEvaluatorFactoryCreateInstance)( ++ Module *module); + /// Trace + /// \{ + typedef llvm::Expected (*TraceCreateInstanceForSessionFile)( +diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp +index 19c97be15066..1647f93ec4f3 100644 +--- a/lldb/source/Core/Module.cpp ++++ b/lldb/source/Core/Module.cpp +@@ -16,6 +16,7 @@ + #include "lldb/Core/ModuleSpec.h" + #include "lldb/Core/SearchFilter.h" + #include "lldb/Core/Section.h" ++#include "lldb/Expression/DWARFEvaluatorFactory.h" + #include "lldb/Host/FileSystem.h" + #include "lldb/Host/Host.h" + #include "lldb/Host/HostInfo.h" +@@ -1659,3 +1660,9 @@ bool Module::GetIsDynamicLinkEditor() { + + return false; + } ++ ++DWARFEvaluatorFactory *Module::GetDWARFExpressionEvaluatorFactory() { ++ if (!m_dwarf_evaluator_factory) ++ m_dwarf_evaluator_factory = DWARFEvaluatorFactory::FindPlugin(this); ++ return m_dwarf_evaluator_factory.get(); ++} +diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp +index fcaa868b083e..59a404d4a7e1 100644 +--- a/lldb/source/Core/PluginManager.cpp ++++ b/lldb/source/Core/PluginManager.cpp +@@ -1597,3 +1597,32 @@ bool PluginManager::CreateSettingForStructuredDataPlugin( + ConstString("Settings for structured data plug-ins"), properties_sp, + description, is_global_property); + } ++ ++#pragma mark DWARFEvaluator ++ ++typedef PluginInstance ++ DWARFEvaluatorFactoryInstance; ++typedef PluginInstances ++ DWARFEvaluatorFactoryInstances; ++ ++static DWARFEvaluatorFactoryInstances &GetDWARFEvaluatorFactoryInstances() { ++ static DWARFEvaluatorFactoryInstances g_instances; ++ return g_instances; ++} ++ ++bool PluginManager::RegisterPlugin( ++ ConstString name, const char *description, ++ DWARFEvaluatorFactoryCreateInstance create_callback) { ++ return GetDWARFEvaluatorFactoryInstances().RegisterPlugin(name, description, ++ create_callback); ++} ++ ++bool PluginManager::UnregisterPlugin( ++ DWARFEvaluatorFactoryCreateInstance create_callback) { ++ return GetDWARFEvaluatorFactoryInstances().UnregisterPlugin(create_callback); ++} ++ ++DWARFEvaluatorFactoryCreateInstance ++PluginManager::GetDWARFEvaluatorFactoryCreateCallbackAtIndex(uint32_t idx) { ++ return GetDWARFEvaluatorFactoryInstances().GetCallbackAtIndex(idx); ++} +diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp +index fb57c0fedf04..f92d6a54de94 100644 +--- a/lldb/source/Core/Value.cpp ++++ b/lldb/source/Core/Value.cpp +@@ -538,7 +538,7 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data, + + if (process) { + const size_t bytes_read = +- process->ReadMemory(address, dst, byte_size, error); ++ process->ReadMemory(address, dst, byte_size, error, exe_ctx); + if (bytes_read != byte_size) + error.SetErrorStringWithFormat( + "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)", +diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp +index 9c1ba99da1d0..b15b214b2a2f 100644 +--- a/lldb/source/Core/ValueObject.cpp ++++ b/lldb/source/Core/ValueObject.cpp +@@ -735,7 +735,7 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx, + if (process) { + heap_buf_ptr->SetByteSize(bytes); + size_t bytes_read = process->ReadMemory( +- addr + offset, heap_buf_ptr->GetBytes(), bytes, error); ++ addr + offset, heap_buf_ptr->GetBytes(), bytes, error, &exe_ctx); + if (error.Success() || bytes_read > 0) { + data.SetData(data_sp); + return bytes_read; +diff --git a/lldb/source/Expression/CMakeLists.txt b/lldb/source/Expression/CMakeLists.txt +index bf94361dd6c1..4e76d547aeaf 100644 +--- a/lldb/source/Expression/CMakeLists.txt ++++ b/lldb/source/Expression/CMakeLists.txt +@@ -1,5 +1,7 @@ + add_lldb_library(lldbExpression + DiagnosticManager.cpp ++ DWARFEvaluator.cpp ++ DWARFEvaluatorFactory.cpp + DWARFExpression.cpp + Expression.cpp + ExpressionVariable.cpp +diff --git a/lldb/source/Expression/DWARFEvaluator.cpp b/lldb/source/Expression/DWARFEvaluator.cpp +new file mode 100644 +index 000000000000..06107e136197 +--- /dev/null ++++ b/lldb/source/Expression/DWARFEvaluator.cpp +@@ -0,0 +1,1952 @@ ++//===-- DWARFEvaluator.cpp ------------ -----------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "lldb/Expression/DWARFEvaluator.h" ++#include "lldb/Expression/DWARFExpression.h" ++ ++#include "lldb/Core/Module.h" ++#include "lldb/Core/Value.h" ++#include "lldb/Core/dwarf.h" ++ ++#include "lldb/Utility/Log.h" ++#include "lldb/Utility/RegisterValue.h" ++ ++#include "lldb/Target/Process.h" ++#include "lldb/Target/RegisterContext.h" ++#include "lldb/Target/StackFrame.h" ++ ++#include "Plugins/SymbolFile/DWARF/DWARFUnit.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++ ++DWARFEvaluator::DWARFEvaluator(const DWARFExpression &dwarf_expression, ++ ExecutionContext *exe_ctx, ++ RegisterContext *reg_ctx, ++ const Value *initial_value_ptr, ++ const Value *object_address_ptr) ++ : m_dwarf_expression(dwarf_expression), m_exe_ctx(exe_ctx), ++ m_reg_ctx(reg_ctx), m_initial_value_ptr(initial_value_ptr), ++ m_object_address_ptr(object_address_ptr) {} ++ ++static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx, ++ lldb::RegisterKind reg_kind, ++ uint32_t reg_num, Status *error_ptr, ++ Value &value) { ++ if (reg_ctx == nullptr) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat("No register context in frame.\n"); ++ } else { ++ uint32_t native_reg = ++ reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); ++ if (native_reg == LLDB_INVALID_REGNUM) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat("Unable to convert register " ++ "kind=%u reg_num=%u to a native " ++ "register number.\n", ++ reg_kind, reg_num); ++ } else { ++ const RegisterInfo *reg_info = ++ reg_ctx->GetRegisterInfoAtIndex(native_reg); ++ RegisterValue reg_value; ++ if (reg_ctx->ReadRegister(reg_info, reg_value)) { ++ if (reg_value.GetScalarValue(value.GetScalar())) { ++ value.SetValueType(Value::ValueType::Scalar); ++ value.SetContext(Value::ContextType::RegisterInfo, ++ const_cast(reg_info)); ++ if (error_ptr) ++ error_ptr->Clear(); ++ return true; ++ } else { ++ // If we get this error, then we need to implement a value buffer in ++ // the dwarf expression evaluation function... ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "register %s can't be converted to a scalar value", ++ reg_info->name); ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat("register %s is not available", ++ reg_info->name); ++ } ++ } ++ } ++ return false; ++} ++ ++static bool Evaluate_DW_OP_entry_value(std::vector &stack, ++ ExecutionContext *exe_ctx, ++ RegisterContext *reg_ctx, ++ const DataExtractor &opcodes, ++ lldb::offset_t &opcode_offset, ++ Status *error_ptr, Log *log) { ++ // DW_OP_entry_value(sub-expr) describes the location a variable had upon ++ // function entry: this variable location is presumed to be optimized out at ++ // the current PC value. The caller of the function may have call site ++ // information that describes an alternate location for the variable (e.g. a ++ // constant literal, or a spilled stack value) in the parent frame. ++ // ++ // Example (this is pseudo-code & pseudo-DWARF, but hopefully illustrative): ++ // ++ // void child(int &sink, int x) { ++ // ... ++ // /* "x" gets optimized out. */ ++ // ++ // /* The location of "x" here is: DW_OP_entry_value($reg2). */ ++ // ++sink; ++ // } ++ // ++ // void parent() { ++ // int sink; ++ // ++ // /* ++ // * The callsite information emitted here is: ++ // * ++ // * DW_TAG_call_site ++ // * DW_AT_return_pc ... (for "child(sink, 123);") ++ // * DW_TAG_call_site_parameter (for "sink") ++ // * DW_AT_location ($reg1) ++ // * DW_AT_call_value ($SP - 8) ++ // * DW_TAG_call_site_parameter (for "x") ++ // * DW_AT_location ($reg2) ++ // * DW_AT_call_value ($literal 123) ++ // * ++ // * DW_TAG_call_site ++ // * DW_AT_return_pc ... (for "child(sink, 456);") ++ // * ... ++ // */ ++ // child(sink, 123); ++ // child(sink, 456); ++ // } ++ // ++ // When the program stops at "++sink" within `child`, the debugger determines ++ // the call site by analyzing the return address. Once the call site is found, ++ // the debugger determines which parameter is referenced by DW_OP_entry_value ++ // and evaluates the corresponding location for that parameter in `parent`. ++ ++ // 1. Find the function which pushed the current frame onto the stack. ++ if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no exe/reg context"); ++ return false; ++ } ++ ++ StackFrame *current_frame = exe_ctx->GetFramePtr(); ++ Thread *thread = exe_ctx->GetThreadPtr(); ++ if (!current_frame || !thread) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current frame/thread"); ++ return false; ++ } ++ ++ Target &target = exe_ctx->GetTargetRef(); ++ StackFrameSP parent_frame = nullptr; ++ addr_t return_pc = LLDB_INVALID_ADDRESS; ++ uint32_t current_frame_idx = current_frame->GetFrameIndex(); ++ uint32_t num_frames = thread->GetStackFrameCount(); ++ for (uint32_t parent_frame_idx = current_frame_idx + 1; ++ parent_frame_idx < num_frames; ++parent_frame_idx) { ++ parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx); ++ // Require a valid sequence of frames. ++ if (!parent_frame) ++ break; ++ ++ // Record the first valid return address, even if this is an inlined frame, ++ // in order to look up the associated call edge in the first non-inlined ++ // parent frame. ++ if (return_pc == LLDB_INVALID_ADDRESS) { ++ return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target); ++ LLDB_LOG(log, ++ "Evaluate_DW_OP_entry_value: immediate ancestor with pc = {0:x}", ++ return_pc); ++ } ++ ++ // If we've found an inlined frame, skip it (these have no call site ++ // parameters). ++ if (parent_frame->IsInlined()) ++ continue; ++ ++ // We've found the first non-inlined parent frame. ++ break; ++ } ++ if (!parent_frame || !parent_frame->GetRegisterContext()) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent frame with reg ctx"); ++ return false; ++ } ++ ++ Function *parent_func = ++ parent_frame->GetSymbolContext(eSymbolContextFunction).function; ++ if (!parent_func) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent function"); ++ return false; ++ } ++ ++ // 2. Find the call edge in the parent function responsible for creating the ++ // current activation. ++ Function *current_func = ++ current_frame->GetSymbolContext(eSymbolContextFunction).function; ++ if (!current_func) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current function"); ++ return false; ++ } ++ ++ CallEdge *call_edge = nullptr; ++ ModuleList &modlist = target.GetImages(); ++ ExecutionContext parent_exe_ctx = *exe_ctx; ++ parent_exe_ctx.SetFrameSP(parent_frame); ++ if (!parent_frame->IsArtificial()) { ++ // If the parent frame is not artificial, the current activation may be ++ // produced by an ambiguous tail call. In this case, refuse to proceed. ++ call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target); ++ if (!call_edge) { ++ LLDB_LOG(log, ++ "Evaluate_DW_OP_entry_value: no call edge for retn-pc = {0:x} " ++ "in parent frame {1}", ++ return_pc, parent_func->GetName()); ++ return false; ++ } ++ Function *callee_func = call_edge->GetCallee(modlist, parent_exe_ctx); ++ if (callee_func != current_func) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, " ++ "can't find real parent frame"); ++ return false; ++ } ++ } else { ++ // The StackFrameList solver machinery has deduced that an unambiguous tail ++ // call sequence that produced the current activation. The first edge in ++ // the parent that points to the current function must be valid. ++ for (auto &edge : parent_func->GetTailCallingEdges()) { ++ if (edge->GetCallee(modlist, parent_exe_ctx) == current_func) { ++ call_edge = edge.get(); ++ break; ++ } ++ } ++ } ++ if (!call_edge) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no unambiguous edge from parent " ++ "to current function"); ++ return false; ++ } ++ ++ // 3. Attempt to locate the DW_OP_entry_value expression in the set of ++ // available call site parameters. If found, evaluate the corresponding ++ // parameter in the context of the parent frame. ++ const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset); ++ const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len); ++ if (!subexpr_data) { ++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: subexpr could not be read"); ++ return false; ++ } ++ ++ const CallSiteParameter *matched_param = nullptr; ++ for (const CallSiteParameter ¶m : call_edge->GetCallSiteParameters()) { ++ DataExtractor param_subexpr_extractor; ++ if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor)) ++ continue; ++ lldb::offset_t param_subexpr_offset = 0; ++ const void *param_subexpr_data = ++ param_subexpr_extractor.GetData(¶m_subexpr_offset, subexpr_len); ++ if (!param_subexpr_data || ++ param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0) ++ continue; ++ ++ // At this point, the DW_OP_entry_value sub-expression and the callee-side ++ // expression in the call site parameter are known to have the same length. ++ // Check whether they are equal. ++ // ++ // Note that an equality check is sufficient: the contents of the ++ // DW_OP_entry_value subexpression are only used to identify the right call ++ // site parameter in the parent, and do not require any special handling. ++ if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) { ++ matched_param = ¶m; ++ break; ++ } ++ } ++ if (!matched_param) { ++ LLDB_LOG(log, ++ "Evaluate_DW_OP_entry_value: no matching call site param found"); ++ return false; ++ } ++ ++ // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value ++ // subexpresion whenever llvm does. ++ Value result; ++ const DWARFExpression ¶m_expr = matched_param->LocationInCaller; ++ if (!param_expr.Evaluate(&parent_exe_ctx, ++ parent_frame->GetRegisterContext().get(), ++ /*loclist_base_addr=*/LLDB_INVALID_ADDRESS, ++ /*initial_value_ptr=*/nullptr, ++ /*object_address_ptr=*/nullptr, result, error_ptr)) { ++ LLDB_LOG(log, ++ "Evaluate_DW_OP_entry_value: call site param evaluation failed"); ++ return false; ++ } ++ ++ stack.push_back(result); ++ return true; ++} ++ ++bool DWARFEvaluator::Evaluate(Value &result, Status *error_ptr) { ++ DataExtractor opcodes; ++ if (!m_dwarf_expression.GetExpressionData(opcodes)) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "no location, value may have been optimized out"); ++ return false; ++ } ++ return Evaluate(opcodes, result, error_ptr); ++} ++ ++bool DWARFEvaluator::Evaluate(const DataExtractor &opcodes, Value &result, ++ Status *error_ptr) { ++ if (opcodes.GetByteSize() == 0) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "no location, value may have been optimized out"); ++ return false; ++ } ++ std::vector stack; ++ ++ Process *process = nullptr; ++ StackFrame *frame = nullptr; ++ ++ if (m_exe_ctx) { ++ process = m_exe_ctx->GetProcessPtr(); ++ frame = m_exe_ctx->GetFramePtr(); ++ } ++ if (m_reg_ctx == nullptr && frame) ++ m_reg_ctx = frame->GetRegisterContext().get(); ++ ++ if (m_initial_value_ptr) ++ stack.push_back(*m_initial_value_ptr); ++ ++ lldb::offset_t offset = 0; ++ ++ /// Insertion point for evaluating multi-piece expression. ++ uint64_t op_piece_offset = 0; ++ Value pieces; // Used for DW_OP_piece ++ ++ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); ++ ++ uint8_t _opcode = 0; ++ ++ while (opcodes.ValidOffset(offset)) { ++ const lldb::offset_t op_offset = offset; ++ const uint8_t op = opcodes.GetU8(&offset); ++ _opcode = op; ++ ++ if (log && log->GetVerbose()) { ++ size_t count = stack.size(); ++ LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:", ++ (uint64_t)count); ++ for (size_t i = 0; i < count; ++i) { ++ StreamString new_value; ++ new_value.Printf("[%" PRIu64 "]", (uint64_t)i); ++ stack[i].Dump(&new_value); ++ LLDB_LOGF(log, " %s", new_value.GetData()); ++ } ++ LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset, ++ DW_OP_value_to_name(op)); ++ } ++ ++ if (!Evaluate(op, process, frame, stack, opcodes, offset, pieces, ++ op_piece_offset, log, error_ptr)) ++ return false; ++ } ++ ++ if (stack.empty()) { ++ // Nothing on the stack, check if we created a piece value from DW_OP_piece ++ // or DW_OP_bit_piece opcodes ++ if (pieces.GetBuffer().GetByteSize()) ++ result = pieces; ++ else { ++ if (error_ptr) ++ error_ptr->SetErrorString("Stack empty after evaluation."); ++ return false; ++ } ++ } else { ++ if (log && log->GetVerbose()) { ++ size_t count = stack.size(); ++ LLDB_LOGF(log, "Stack after operation has %" PRIu64 " values:", ++ (uint64_t)count); ++ for (size_t i = 0; i < count; ++i) { ++ StreamString new_value; ++ new_value.Printf("[%" PRIu64 "]", (uint64_t)i); ++ stack[i].Dump(&new_value); ++ LLDB_LOGF(log, " %s", new_value.GetData()); ++ } ++ } ++ result = stack.back(); ++ } ++ return true; // Return true on success ++} ++ ++bool DWARFEvaluator::Evaluate(const uint8_t op, Process *process, ++ StackFrame *frame, std::vector &stack, ++ const DataExtractor &opcodes, ++ lldb::offset_t &offset, Value &pieces, ++ uint64_t &op_piece_offset, Log *log, ++ Status *error_ptr) { ++ Value tmp; ++ uint32_t reg_num; ++ ++ lldb::ModuleSP module_sp = m_dwarf_expression.GetModule(); ++ const DWARFUnit *dwarf_cu = m_dwarf_expression.GetDWARFCompileUnit(); ++ const lldb::RegisterKind reg_kind = m_dwarf_expression.GetRegisterKind(); ++ ++ switch (op) { ++ // The DW_OP_addr operation has a single operand that encodes a machine ++ // address and whose size is the size of an address on the target machine. ++ case DW_OP_addr: ++ stack.push_back(Scalar(opcodes.GetAddress(&offset))); ++ stack.back().SetValueType(Value::ValueType::FileAddress); ++ // Convert the file address to a load address, so subsequent ++ // DWARF operators can operate on it. ++ if (frame) ++ stack.back().ConvertToLoadAddress(module_sp.get(), ++ frame->CalculateTarget().get()); ++ break; ++ ++ // The DW_OP_addr_sect_offset4 is used for any location expressions in ++ // shared libraries that have a location like: ++ // DW_OP_addr(0x1000) ++ // If this address resides in a shared library, then this virtual address ++ // won't make sense when it is evaluated in the context of a running ++ // process where shared libraries have been slid. To account for this, this ++ // new address type where we can store the section pointer and a 4 byte ++ // offset. ++ // case DW_OP_addr_sect_offset4: ++ // { ++ // result_type = eResultTypeFileAddress; ++ // lldb::Section *sect = (lldb::Section ++ // *)opcodes.GetMaxU64(&offset, sizeof(void *)); ++ // lldb::addr_t sect_offset = opcodes.GetU32(&offset); ++ // ++ // Address so_addr (sect, sect_offset); ++ // lldb::addr_t load_addr = so_addr.GetLoadAddress(); ++ // if (load_addr != LLDB_INVALID_ADDRESS) ++ // { ++ // // We successfully resolve a file address to a load ++ // // address. ++ // stack.push_back(load_addr); ++ // break; ++ // } ++ // else ++ // { ++ // // We were able ++ // if (error_ptr) ++ // error_ptr->SetErrorStringWithFormat ("Section %s in ++ // %s is not currently loaded.\n", ++ // sect->GetName().AsCString(), ++ // sect->GetModule()->GetFileSpec().GetFilename().AsCString()); ++ // return false; ++ // } ++ // } ++ // break; ++ ++ // OPCODE: DW_OP_deref ++ // OPERANDS: none ++ // DESCRIPTION: Pops the top stack entry and treats it as an address. ++ // The value retrieved from that address is pushed. The size of the data ++ // retrieved from the dereferenced address is the size of an address on the ++ // target machine. ++ case DW_OP_deref: { ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Expression stack empty for DW_OP_deref."); ++ return false; ++ } ++ Value::ValueType value_type = stack.back().GetValueType(); ++ switch (value_type) { ++ case Value::ValueType::HostAddress: { ++ void *src = (void *)stack.back().GetScalar().ULongLong(); ++ intptr_t ptr; ++ ::memcpy(&ptr, src, sizeof(void *)); ++ stack.back().GetScalar() = ptr; ++ stack.back().ClearContext(); ++ } break; ++ case Value::ValueType::FileAddress: { ++ auto file_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); ++ if (!module_sp) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "need module to resolve file address for DW_OP_deref"); ++ return false; ++ } ++ Address so_addr; ++ if (!module_sp->ResolveFileAddress(file_addr, so_addr)) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "failed to resolve file address in module"); ++ return false; ++ } ++ addr_t load_Addr = so_addr.GetLoadAddress(m_exe_ctx->GetTargetPtr()); ++ if (load_Addr == LLDB_INVALID_ADDRESS) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat("failed to resolve load address"); ++ return false; ++ } ++ stack.back().GetScalar() = load_Addr; ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ // Fall through to load address code below... ++ } ++ LLVM_FALLTHROUGH; ++ case Value::ValueType::LoadAddress: ++ if (m_exe_ctx) { ++ if (process) { ++ lldb::addr_t pointer_addr = ++ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); ++ Status error; ++ lldb::addr_t pointer_value = ++ process->ReadPointerFromMemory(pointer_addr, error); ++ if (pointer_value != LLDB_INVALID_ADDRESS) { ++ stack.back().GetScalar() = pointer_value; ++ stack.back().ClearContext(); ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "Failed to dereference pointer from 0x%" PRIx64 ++ " for DW_OP_deref: %s\n", ++ pointer_addr, error.AsCString()); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "NULL process for DW_OP_deref.\n"); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "NULL execution context for DW_OP_deref.\n"); ++ return false; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ } break; ++ ++ // OPCODE: DW_OP_deref_size ++ // OPERANDS: 1 ++ // 1 - uint8_t that specifies the size of the data to dereference. ++ // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top ++ // stack entry and treats it as an address. The value retrieved from that ++ // address is pushed. In the DW_OP_deref_size operation, however, the size ++ // in bytes of the data retrieved from the dereferenced address is ++ // specified by the single operand. This operand is a 1-byte unsigned ++ // integral constant whose value may not be larger than the size of an ++ // address on the target machine. The data retrieved is zero extended to ++ // the size of an address on the target machine before being pushed on the ++ // expression stack. ++ case DW_OP_deref_size: { ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack empty for DW_OP_deref_size."); ++ return false; ++ } ++ uint8_t size = opcodes.GetU8(&offset); ++ Value::ValueType value_type = stack.back().GetValueType(); ++ switch (value_type) { ++ case Value::ValueType::HostAddress: { ++ void *src = (void *)stack.back().GetScalar().ULongLong(); ++ intptr_t ptr; ++ ::memcpy(&ptr, src, sizeof(void *)); ++ // I can't decide whether the size operand should apply to the bytes in ++ // their ++ // lldb-host endianness or the target endianness.. I doubt this'll ever ++ // come up but I'll opt for assuming big endian regardless. ++ switch (size) { ++ case 1: ++ ptr = ptr & 0xff; ++ break; ++ case 2: ++ ptr = ptr & 0xffff; ++ break; ++ case 3: ++ ptr = ptr & 0xffffff; ++ break; ++ case 4: ++ ptr = ptr & 0xffffffff; ++ break; ++ // the casts are added to work around the case where intptr_t is a 32 ++ // bit quantity; ++ // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this ++ // program. ++ case 5: ++ ptr = (intptr_t)ptr & 0xffffffffffULL; ++ break; ++ case 6: ++ ptr = (intptr_t)ptr & 0xffffffffffffULL; ++ break; ++ case 7: ++ ptr = (intptr_t)ptr & 0xffffffffffffffULL; ++ break; ++ default: ++ break; ++ } ++ stack.back().GetScalar() = ptr; ++ stack.back().ClearContext(); ++ } break; ++ case Value::ValueType::LoadAddress: ++ if (m_exe_ctx) { ++ if (process) { ++ lldb::addr_t pointer_addr = ++ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); ++ uint8_t addr_bytes[sizeof(lldb::addr_t)]; ++ Status error; ++ if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == ++ size) { ++ DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), ++ process->GetByteOrder(), size); ++ lldb::offset_t addr_data_offset = 0; ++ switch (size) { ++ case 1: ++ stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); ++ break; ++ case 2: ++ stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); ++ break; ++ case 4: ++ stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); ++ break; ++ case 8: ++ stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); ++ break; ++ default: ++ stack.back().GetScalar() = ++ addr_data.GetAddress(&addr_data_offset); ++ } ++ stack.back().ClearContext(); ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "Failed to dereference pointer from 0x%" PRIx64 ++ " for DW_OP_deref: %s\n", ++ pointer_addr, error.AsCString()); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "NULL process for DW_OP_deref.\n"); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "NULL execution context for DW_OP_deref.\n"); ++ return false; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ } break; ++ ++ // OPCODE: DW_OP_xderef_size ++ // OPERANDS: 1 ++ // 1 - uint8_t that specifies the size of the data to dereference. ++ // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at ++ // the top of the stack is treated as an address. The second stack entry is ++ // treated as an "address space identifier" for those architectures that ++ // support multiple address spaces. The top two stack elements are popped, ++ // a data item is retrieved through an implementation-defined address ++ // calculation and pushed as the new stack top. In the DW_OP_xderef_size ++ // operation, however, the size in bytes of the data retrieved from the ++ // dereferenced address is specified by the single operand. This operand is ++ // a 1-byte unsigned integral constant whose value may not be larger than ++ // the size of an address on the target machine. The data retrieved is zero ++ // extended to the size of an address on the target machine before being ++ // pushed on the expression stack. ++ case DW_OP_xderef_size: ++ if (error_ptr) ++ error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); ++ return false; ++ // OPCODE: DW_OP_xderef ++ // OPERANDS: none ++ // DESCRIPTION: Provides an extended dereference mechanism. The entry at ++ // the top of the stack is treated as an address. The second stack entry is ++ // treated as an "address space identifier" for those architectures that ++ // support multiple address spaces. The top two stack elements are popped, ++ // a data item is retrieved through an implementation-defined address ++ // calculation and pushed as the new stack top. The size of the data ++ // retrieved from the dereferenced address is the size of an address on the ++ // target machine. ++ case DW_OP_xderef: ++ if (error_ptr) ++ error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); ++ return false; ++ ++ // All DW_OP_constXXX opcodes have a single operand as noted below: ++ // ++ // Opcode Operand 1 ++ // DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s ++ // 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer ++ // constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u ++ // 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer ++ // constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s ++ // 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer ++ // constant DW_OP_consts signed LEB128 integer constant ++ case DW_OP_const1u: ++ stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset))); ++ break; ++ case DW_OP_const1s: ++ stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset))); ++ break; ++ case DW_OP_const2u: ++ stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset))); ++ break; ++ case DW_OP_const2s: ++ stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset))); ++ break; ++ case DW_OP_const4u: ++ stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset))); ++ break; ++ case DW_OP_const4s: ++ stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset))); ++ break; ++ case DW_OP_const8u: ++ stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset))); ++ break; ++ case DW_OP_const8s: ++ stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset))); ++ break; ++ case DW_OP_constu: ++ stack.push_back(Scalar(opcodes.GetULEB128(&offset))); ++ break; ++ case DW_OP_consts: ++ stack.push_back(Scalar(opcodes.GetSLEB128(&offset))); ++ break; ++ ++ // OPCODE: DW_OP_dup ++ // OPERANDS: none ++ // DESCRIPTION: duplicates the value at the top of the stack ++ case DW_OP_dup: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); ++ return false; ++ } else ++ stack.push_back(stack.back()); ++ break; ++ ++ // OPCODE: DW_OP_drop ++ // OPERANDS: none ++ // DESCRIPTION: pops the value at the top of the stack ++ case DW_OP_drop: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); ++ return false; ++ } else ++ stack.pop_back(); ++ break; ++ ++ // OPCODE: DW_OP_over ++ // OPERANDS: none ++ // DESCRIPTION: Duplicates the entry currently second in the stack at ++ // the top of the stack. ++ case DW_OP_over: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_over."); ++ return false; ++ } else ++ stack.push_back(stack[stack.size() - 2]); ++ break; ++ ++ // OPCODE: DW_OP_pick ++ // OPERANDS: uint8_t index into the current stack ++ // DESCRIPTION: The stack entry with the specified index (0 through 255, ++ // inclusive) is pushed on the stack ++ case DW_OP_pick: { ++ uint8_t pick_idx = opcodes.GetU8(&offset); ++ if (pick_idx < stack.size()) ++ stack.push_back(stack[stack.size() - 1 - pick_idx]); ++ else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "Index %u out of range for DW_OP_pick.\n", pick_idx); ++ return false; ++ } ++ } break; ++ ++ // OPCODE: DW_OP_swap ++ // OPERANDS: none ++ // DESCRIPTION: swaps the top two stack entries. The entry at the top ++ // of the stack becomes the second stack entry, and the second entry ++ // becomes the top of the stack ++ case DW_OP_swap: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_swap."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.back() = stack[stack.size() - 2]; ++ stack[stack.size() - 2] = tmp; ++ } ++ break; ++ ++ // OPCODE: DW_OP_rot ++ // OPERANDS: none ++ // DESCRIPTION: Rotates the first three stack entries. The entry at ++ // the top of the stack becomes the third stack entry, the second entry ++ // becomes the top of the stack, and the third entry becomes the second ++ // entry. ++ case DW_OP_rot: ++ if (stack.size() < 3) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 3 items for DW_OP_rot."); ++ return false; ++ } else { ++ size_t last_idx = stack.size() - 1; ++ Value old_top = stack[last_idx]; ++ stack[last_idx] = stack[last_idx - 1]; ++ stack[last_idx - 1] = stack[last_idx - 2]; ++ stack[last_idx - 2] = old_top; ++ } ++ break; ++ ++ // OPCODE: DW_OP_abs ++ // OPERANDS: none ++ // DESCRIPTION: pops the top stack entry, interprets it as a signed ++ // value and pushes its absolute value. If the absolute value can not be ++ // represented, the result is undefined. ++ case DW_OP_abs: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_abs."); ++ return false; ++ } else if (!stack.back().ResolveValue(m_exe_ctx).AbsoluteValue()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Failed to take the absolute value of the first stack item."); ++ return false; ++ } ++ break; ++ ++ // OPCODE: DW_OP_and ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, performs a bitwise and ++ // operation on the two, and pushes the result. ++ case DW_OP_and: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_and."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) & tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_div ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, divides the former second ++ // entry by the former top of the stack using signed division, and pushes ++ // the result. ++ case DW_OP_div: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_div."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ if (tmp.ResolveValue(m_exe_ctx).IsZero()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Divide by zero."); ++ return false; ++ } else { ++ stack.pop_back(); ++ stack.back() = ++ stack.back().ResolveValue(m_exe_ctx) / tmp.ResolveValue(m_exe_ctx); ++ if (!stack.back().ResolveValue(m_exe_ctx).IsValid()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Divide failed."); ++ return false; ++ } ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_minus ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, subtracts the former top ++ // of the stack from the former second entry, and pushes the result. ++ case DW_OP_minus: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_minus."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) - tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_mod ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values and pushes the result of ++ // the calculation: former second stack entry modulo the former top of the ++ // stack. ++ case DW_OP_mod: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_mod."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) % tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_mul ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, multiplies them ++ // together, and pushes the result. ++ case DW_OP_mul: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_mul."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) * tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_neg ++ // OPERANDS: none ++ // DESCRIPTION: pops the top stack entry, and pushes its negation. ++ case DW_OP_neg: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_neg."); ++ return false; ++ } else { ++ if (!stack.back().ResolveValue(m_exe_ctx).UnaryNegate()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Unary negate failed."); ++ return false; ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_not ++ // OPERANDS: none ++ // DESCRIPTION: pops the top stack entry, and pushes its bitwise ++ // complement ++ case DW_OP_not: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_not."); ++ return false; ++ } else { ++ if (!stack.back().ResolveValue(m_exe_ctx).OnesComplement()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Logical NOT failed."); ++ return false; ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_or ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, performs a bitwise or ++ // operation on the two, and pushes the result. ++ case DW_OP_or: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_or."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) | tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_plus ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, adds them together, and ++ // pushes the result. ++ case DW_OP_plus: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_plus."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().GetScalar() += tmp.GetScalar(); ++ } ++ break; ++ ++ // OPCODE: DW_OP_plus_uconst ++ // OPERANDS: none ++ // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 ++ // constant operand and pushes the result. ++ case DW_OP_plus_uconst: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_plus_uconst."); ++ return false; ++ } else { ++ const uint64_t uconst_value = opcodes.GetULEB128(&offset); ++ // Implicit conversion from a UINT to a Scalar... ++ stack.back().GetScalar() += uconst_value; ++ if (!stack.back().GetScalar().IsValid()) { ++ if (error_ptr) ++ error_ptr->SetErrorString("DW_OP_plus_uconst failed."); ++ return false; ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_shl ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, shifts the former ++ // second entry left by the number of bits specified by the former top of ++ // the stack, and pushes the result. ++ case DW_OP_shl: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_shl."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) <<= tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_shr ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, shifts the former second ++ // entry right logically (filling with zero bits) by the number of bits ++ // specified by the former top of the stack, and pushes the result. ++ case DW_OP_shr: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_shr."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ if (!stack.back().ResolveValue(m_exe_ctx).ShiftRightLogical( ++ tmp.ResolveValue(m_exe_ctx))) { ++ if (error_ptr) ++ error_ptr->SetErrorString("DW_OP_shr failed."); ++ return false; ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_shra ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, shifts the former second ++ // entry right arithmetically (divide the magnitude by 2, keep the same ++ // sign for the result) by the number of bits specified by the former top ++ // of the stack, and pushes the result. ++ case DW_OP_shra: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_shra."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) >>= tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_xor ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack entries, performs the bitwise ++ // exclusive-or operation on the two, and pushes the result. ++ case DW_OP_xor: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_xor."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) ^ tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_skip ++ // OPERANDS: int16_t ++ // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte ++ // signed integer constant. The 2-byte constant is the number of bytes of ++ // the DWARF expression to skip forward or backward from the current ++ // operation, beginning after the 2-byte constant. ++ case DW_OP_skip: { ++ int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); ++ lldb::offset_t new_offset = offset + skip_offset; ++ if (opcodes.ValidOffset(new_offset)) ++ offset = new_offset; ++ else { ++ if (error_ptr) ++ error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); ++ return false; ++ } ++ } break; ++ ++ // OPCODE: DW_OP_bra ++ // OPERANDS: int16_t ++ // DESCRIPTION: A conditional branch. Its single operand is a 2-byte ++ // signed integer constant. This operation pops the top of stack. If the ++ // value popped is not the constant 0, the 2-byte constant operand is the ++ // number of bytes of the DWARF expression to skip forward or backward from ++ // the current operation, beginning after the 2-byte constant. ++ case DW_OP_bra: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_bra."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ int16_t bra_offset = (int16_t)opcodes.GetU16(&offset); ++ Scalar zero(0); ++ if (tmp.ResolveValue(m_exe_ctx) != zero) { ++ lldb::offset_t new_offset = offset + bra_offset; ++ if (opcodes.ValidOffset(new_offset)) ++ offset = new_offset; ++ else { ++ if (error_ptr) ++ error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); ++ return false; ++ } ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_eq ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, compares using the ++ // equals (==) operator. ++ // STACK RESULT: push the constant value 1 onto the stack if the result ++ // of the operation is true or the constant value 0 if the result of the ++ // operation is false. ++ case DW_OP_eq: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_eq."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) == tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_ge ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, compares using the ++ // greater than or equal to (>=) operator. ++ // STACK RESULT: push the constant value 1 onto the stack if the result ++ // of the operation is true or the constant value 0 if the result of the ++ // operation is false. ++ case DW_OP_ge: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_ge."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) >= tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_gt ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, compares using the ++ // greater than (>) operator. ++ // STACK RESULT: push the constant value 1 onto the stack if the result ++ // of the operation is true or the constant value 0 if the result of the ++ // operation is false. ++ case DW_OP_gt: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_gt."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) > tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_le ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, compares using the ++ // less than or equal to (<=) operator. ++ // STACK RESULT: push the constant value 1 onto the stack if the result ++ // of the operation is true or the constant value 0 if the result of the ++ // operation is false. ++ case DW_OP_le: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_le."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) <= tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_lt ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, compares using the ++ // less than (<) operator. ++ // STACK RESULT: push the constant value 1 onto the stack if the result ++ // of the operation is true or the constant value 0 if the result of the ++ // operation is false. ++ case DW_OP_lt: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_lt."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) < tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_ne ++ // OPERANDS: none ++ // DESCRIPTION: pops the top two stack values, compares using the ++ // not equal (!=) operator. ++ // STACK RESULT: push the constant value 1 onto the stack if the result ++ // of the operation is true or the constant value 0 if the result of the ++ // operation is false. ++ case DW_OP_ne: ++ if (stack.size() < 2) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 2 items for DW_OP_ne."); ++ return false; ++ } else { ++ tmp = stack.back(); ++ stack.pop_back(); ++ stack.back().ResolveValue(m_exe_ctx) = ++ stack.back().ResolveValue(m_exe_ctx) != tmp.ResolveValue(m_exe_ctx); ++ } ++ break; ++ ++ // OPCODE: DW_OP_litn ++ // OPERANDS: none ++ // DESCRIPTION: encode the unsigned literal values from 0 through 31. ++ // STACK RESULT: push the unsigned literal constant value onto the top ++ // of the stack. ++ case DW_OP_lit0: ++ case DW_OP_lit1: ++ case DW_OP_lit2: ++ case DW_OP_lit3: ++ case DW_OP_lit4: ++ case DW_OP_lit5: ++ case DW_OP_lit6: ++ case DW_OP_lit7: ++ case DW_OP_lit8: ++ case DW_OP_lit9: ++ case DW_OP_lit10: ++ case DW_OP_lit11: ++ case DW_OP_lit12: ++ case DW_OP_lit13: ++ case DW_OP_lit14: ++ case DW_OP_lit15: ++ case DW_OP_lit16: ++ case DW_OP_lit17: ++ case DW_OP_lit18: ++ case DW_OP_lit19: ++ case DW_OP_lit20: ++ case DW_OP_lit21: ++ case DW_OP_lit22: ++ case DW_OP_lit23: ++ case DW_OP_lit24: ++ case DW_OP_lit25: ++ case DW_OP_lit26: ++ case DW_OP_lit27: ++ case DW_OP_lit28: ++ case DW_OP_lit29: ++ case DW_OP_lit30: ++ case DW_OP_lit31: ++ stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0))); ++ break; ++ ++ // OPCODE: DW_OP_regN ++ // OPERANDS: none ++ // DESCRIPTION: Push the value in register n on the top of the stack. ++ case DW_OP_reg0: ++ case DW_OP_reg1: ++ case DW_OP_reg2: ++ case DW_OP_reg3: ++ case DW_OP_reg4: ++ case DW_OP_reg5: ++ case DW_OP_reg6: ++ case DW_OP_reg7: ++ case DW_OP_reg8: ++ case DW_OP_reg9: ++ case DW_OP_reg10: ++ case DW_OP_reg11: ++ case DW_OP_reg12: ++ case DW_OP_reg13: ++ case DW_OP_reg14: ++ case DW_OP_reg15: ++ case DW_OP_reg16: ++ case DW_OP_reg17: ++ case DW_OP_reg18: ++ case DW_OP_reg19: ++ case DW_OP_reg20: ++ case DW_OP_reg21: ++ case DW_OP_reg22: ++ case DW_OP_reg23: ++ case DW_OP_reg24: ++ case DW_OP_reg25: ++ case DW_OP_reg26: ++ case DW_OP_reg27: ++ case DW_OP_reg28: ++ case DW_OP_reg29: ++ case DW_OP_reg30: ++ case DW_OP_reg31: { ++ reg_num = op - DW_OP_reg0; ++ ++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr, tmp)) ++ stack.push_back(tmp); ++ else ++ return false; ++ } break; ++ // OPCODE: DW_OP_regx ++ // OPERANDS: ++ // ULEB128 literal operand that encodes the register. ++ // DESCRIPTION: Push the value in register on the top of the stack. ++ case DW_OP_regx: { ++ reg_num = opcodes.GetULEB128(&offset); ++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr, tmp)) ++ stack.push_back(tmp); ++ else ++ return false; ++ } break; ++ ++ // OPCODE: DW_OP_bregN ++ // OPERANDS: ++ // SLEB128 offset from register N ++ // DESCRIPTION: Value is in memory at the address specified by register ++ // N plus an offset. ++ case DW_OP_breg0: ++ case DW_OP_breg1: ++ case DW_OP_breg2: ++ case DW_OP_breg3: ++ case DW_OP_breg4: ++ case DW_OP_breg5: ++ case DW_OP_breg6: ++ case DW_OP_breg7: ++ case DW_OP_breg8: ++ case DW_OP_breg9: ++ case DW_OP_breg10: ++ case DW_OP_breg11: ++ case DW_OP_breg12: ++ case DW_OP_breg13: ++ case DW_OP_breg14: ++ case DW_OP_breg15: ++ case DW_OP_breg16: ++ case DW_OP_breg17: ++ case DW_OP_breg18: ++ case DW_OP_breg19: ++ case DW_OP_breg20: ++ case DW_OP_breg21: ++ case DW_OP_breg22: ++ case DW_OP_breg23: ++ case DW_OP_breg24: ++ case DW_OP_breg25: ++ case DW_OP_breg26: ++ case DW_OP_breg27: ++ case DW_OP_breg28: ++ case DW_OP_breg29: ++ case DW_OP_breg30: ++ case DW_OP_breg31: { ++ reg_num = op - DW_OP_breg0; ++ ++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr, ++ tmp)) { ++ int64_t breg_offset = opcodes.GetSLEB128(&offset); ++ tmp.ResolveValue(m_exe_ctx) += (uint64_t)breg_offset; ++ tmp.ClearContext(); ++ stack.push_back(tmp); ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } else ++ return false; ++ } break; ++ // OPCODE: DW_OP_bregx ++ // OPERANDS: 2 ++ // ULEB128 literal operand that encodes the register. ++ // SLEB128 offset from register N ++ // DESCRIPTION: Value is in memory at the address specified by register ++ // N plus an offset. ++ case DW_OP_bregx: { ++ reg_num = opcodes.GetULEB128(&offset); ++ ++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr, ++ tmp)) { ++ int64_t breg_offset = opcodes.GetSLEB128(&offset); ++ tmp.ResolveValue(m_exe_ctx) += (uint64_t)breg_offset; ++ tmp.ClearContext(); ++ stack.push_back(tmp); ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } else ++ return false; ++ } break; ++ ++ case DW_OP_fbreg: ++ if (m_exe_ctx) { ++ if (frame) { ++ Scalar value; ++ if (frame->GetFrameBaseValue(value, error_ptr)) { ++ int64_t fbreg_offset = opcodes.GetSLEB128(&offset); ++ value += fbreg_offset; ++ stack.push_back(value); ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } else ++ return false; ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Invalid stack frame in context for DW_OP_fbreg opcode."); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "NULL execution context for DW_OP_fbreg.\n"); ++ return false; ++ } ++ ++ break; ++ ++ // OPCODE: DW_OP_nop ++ // OPERANDS: none ++ // DESCRIPTION: A place holder. It has no effect on the location stack ++ // or any of its values. ++ case DW_OP_nop: ++ break; ++ ++ // OPCODE: DW_OP_piece ++ // OPERANDS: 1 ++ // ULEB128: byte size of the piece ++ // DESCRIPTION: The operand describes the size in bytes of the piece of ++ // the object referenced by the DWARF expression whose result is at the top ++ // of the stack. If the piece is located in a register, but does not occupy ++ // the entire register, the placement of the piece within that register is ++ // defined by the ABI. ++ // ++ // Many compilers store a single variable in sets of registers, or store a ++ // variable partially in memory and partially in registers. DW_OP_piece ++ // provides a way of describing how large a part of a variable a particular ++ // DWARF expression refers to. ++ case DW_OP_piece: { ++ const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); ++ ++ if (piece_byte_size > 0) { ++ Value curr_piece; ++ ++ if (stack.empty()) { ++ // In a multi-piece expression, this means that the current piece is ++ // not available. Fill with zeros for now by resizing the data and ++ // appending it ++ curr_piece.ResizeData(piece_byte_size); ++ // Note that "0" is not a correct value for the unknown bits. ++ // It would be better to also return a mask of valid bits together ++ // with the expression result, so the debugger can print missing ++ // members as "" or something. ++ ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size); ++ pieces.AppendDataToHostBuffer(curr_piece); ++ } else { ++ Status error; ++ // Extract the current piece into "curr_piece" ++ Value curr_piece_source_value(stack.back()); ++ stack.pop_back(); ++ ++ const Value::ValueType curr_piece_source_value_type = ++ curr_piece_source_value.GetValueType(); ++ switch (curr_piece_source_value_type) { ++ case Value::ValueType::LoadAddress: ++ if (process) { ++ if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { ++ lldb::addr_t load_addr = ++ curr_piece_source_value.GetScalar().ULongLong( ++ LLDB_INVALID_ADDRESS); ++ if (process->ReadMemory( ++ load_addr, curr_piece.GetBuffer().GetBytes(), ++ piece_byte_size, error) != piece_byte_size) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "failed to read memory DW_OP_piece(%" PRIu64 ++ ") from 0x%" PRIx64, ++ piece_byte_size, load_addr); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "failed to resize the piece memory buffer for " ++ "DW_OP_piece(%" PRIu64 ")", ++ piece_byte_size); ++ return false; ++ } ++ } ++ break; ++ ++ case Value::ValueType::FileAddress: ++ case Value::ValueType::HostAddress: ++ if (error_ptr) { ++ lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong( ++ LLDB_INVALID_ADDRESS); ++ error_ptr->SetErrorStringWithFormat( ++ "failed to read memory DW_OP_piece(%" PRIu64 ++ ") from %s address 0x%" PRIx64, ++ piece_byte_size, ++ curr_piece_source_value.GetValueType() == ++ Value::ValueType::FileAddress ++ ? "file" ++ : "host", ++ addr); ++ } ++ return false; ++ ++ case Value::ValueType::Scalar: { ++ uint32_t bit_size = piece_byte_size * 8; ++ uint32_t bit_offset = 0; ++ Scalar &scalar = curr_piece_source_value.GetScalar(); ++ if (!scalar.ExtractBitfield(bit_size, bit_offset)) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "unable to extract %" PRIu64 " bytes from a %" PRIu64 ++ " byte scalar value.", ++ piece_byte_size, ++ (uint64_t)curr_piece_source_value.GetScalar().GetByteSize()); ++ return false; ++ } ++ // Create curr_piece with bit_size. By default Scalar ++ // grows to the nearest host integer type. ++ llvm::APInt fail_value(1, 0, false); ++ llvm::APInt ap_int = scalar.UInt128(fail_value); ++ assert(ap_int.getBitWidth() >= bit_size); ++ llvm::ArrayRef buf{ap_int.getRawData(), ++ ap_int.getNumWords()}; ++ curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf)); ++ } break; ++ } ++ ++ // Check if this is the first piece? ++ if (op_piece_offset == 0) { ++ // This is the first piece, we should push it back onto the stack ++ // so subsequent pieces will be able to access this piece and add ++ // to it. ++ if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { ++ if (error_ptr) ++ error_ptr->SetErrorString("failed to append piece data"); ++ return false; ++ } ++ } else { ++ // If this is the second or later piece there should be a value on ++ // the stack. ++ if (pieces.GetBuffer().GetByteSize() != op_piece_offset) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "DW_OP_piece for offset %" PRIu64 ++ " but top of stack is of size %" PRIu64, ++ op_piece_offset, pieces.GetBuffer().GetByteSize()); ++ return false; ++ } ++ ++ if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { ++ if (error_ptr) ++ error_ptr->SetErrorString("failed to append piece data"); ++ return false; ++ } ++ } ++ } ++ op_piece_offset += piece_byte_size; ++ } ++ } break; ++ ++ case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); ++ if (stack.size() < 1) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_bit_piece."); ++ return false; ++ } else { ++ const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); ++ const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); ++ switch (stack.back().GetValueType()) { ++ case Value::ValueType::Scalar: { ++ if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, ++ piece_bit_offset)) { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "unable to extract %" PRIu64 " bit value with %" PRIu64 ++ " bit offset from a %" PRIu64 " bit scalar value.", ++ piece_bit_size, piece_bit_offset, ++ (uint64_t)(stack.back().GetScalar().GetByteSize() * 8)); ++ return false; ++ } ++ } break; ++ ++ case Value::ValueType::FileAddress: ++ case Value::ValueType::LoadAddress: ++ case Value::ValueType::HostAddress: ++ if (error_ptr) { ++ error_ptr->SetErrorStringWithFormat( ++ "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 ++ ", bit_offset = %" PRIu64 ") from an address value.", ++ piece_bit_size, piece_bit_offset); ++ } ++ return false; ++ } ++ } ++ break; ++ ++ // OPCODE: DW_OP_push_object_address ++ // OPERANDS: none ++ // DESCRIPTION: Pushes the address of the object currently being ++ // evaluated as part of evaluation of a user presented expression. This ++ // object may correspond to an independent variable described by its own ++ // DIE or it may be a component of an array, structure, or class whose ++ // address has been dynamically determined by an earlier step during user ++ // expression evaluation. ++ case DW_OP_push_object_address: ++ if (m_object_address_ptr) ++ stack.push_back(*m_object_address_ptr); ++ else { ++ if (error_ptr) ++ error_ptr->SetErrorString("DW_OP_push_object_address used without " ++ "specifying an object address"); ++ return false; ++ } ++ break; ++ ++ // OPCODE: DW_OP_call2 ++ // OPERANDS: ++ // uint16_t compile unit relative offset of a DIE ++ // DESCRIPTION: Performs subroutine calls during evaluation ++ // of a DWARF expression. The operand is the 2-byte unsigned offset of a ++ // debugging information entry in the current compilation unit. ++ // ++ // Operand interpretation is exactly like that for DW_FORM_ref2. ++ // ++ // This operation transfers control of DWARF expression evaluation to the ++ // DW_AT_location attribute of the referenced DIE. If there is no such ++ // attribute, then there is no effect. Execution of the DWARF expression of ++ // a DW_AT_location attribute may add to and/or remove from values on the ++ // stack. Execution returns to the point following the call when the end of ++ // the attribute is reached. Values on the stack at the time of the call ++ // may be used as parameters by the called expression and values left on ++ // the stack by the called expression may be used as return values by prior ++ // agreement between the calling and called expressions. ++ case DW_OP_call2: ++ if (error_ptr) ++ error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2."); ++ return false; ++ // OPCODE: DW_OP_call4 ++ // OPERANDS: 1 ++ // uint32_t compile unit relative offset of a DIE ++ // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF ++ // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of ++ // a debugging information entry in the current compilation unit. ++ // ++ // Operand interpretation DW_OP_call4 is exactly like that for ++ // DW_FORM_ref4. ++ // ++ // This operation transfers control of DWARF expression evaluation to the ++ // DW_AT_location attribute of the referenced DIE. If there is no such ++ // attribute, then there is no effect. Execution of the DWARF expression of ++ // a DW_AT_location attribute may add to and/or remove from values on the ++ // stack. Execution returns to the point following the call when the end of ++ // the attribute is reached. Values on the stack at the time of the call ++ // may be used as parameters by the called expression and values left on ++ // the stack by the called expression may be used as return values by prior ++ // agreement between the calling and called expressions. ++ case DW_OP_call4: ++ if (error_ptr) ++ error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4."); ++ return false; ++ ++ // OPCODE: DW_OP_stack_value ++ // OPERANDS: None ++ // DESCRIPTION: Specifies that the object does not exist in memory but ++ // rather is a constant value. The value from the top of the stack is the ++ // value to be used. This is the actual object value and not the location. ++ case DW_OP_stack_value: ++ if (stack.empty()) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_stack_value."); ++ return false; ++ } ++ stack.back().SetValueType(Value::ValueType::Scalar); ++ break; ++ ++ // OPCODE: DW_OP_convert ++ // OPERANDS: 1 ++ // A ULEB128 that is either a DIE offset of a ++ // DW_TAG_base_type or 0 for the generic (pointer-sized) type. ++ // ++ // DESCRIPTION: Pop the top stack element, convert it to a ++ // different type, and push the result. ++ case DW_OP_convert: { ++ if (stack.size() < 1) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Expression stack needs at least 1 item for DW_OP_convert."); ++ return false; ++ } ++ const uint64_t die_offset = opcodes.GetULEB128(&offset); ++ uint64_t bit_size; ++ bool sign; ++ if (die_offset == 0) { ++ // The generic type has the size of an address on the target ++ // machine and an unspecified signedness. Scalar has no ++ // "unspecified signedness", so we use unsigned types. ++ if (!module_sp) { ++ if (error_ptr) ++ error_ptr->SetErrorString("No module"); ++ return false; ++ } ++ sign = false; ++ bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8; ++ if (!bit_size) { ++ if (error_ptr) ++ error_ptr->SetErrorString("unspecified architecture"); ++ return false; ++ } ++ } else { ++ // Retrieve the type DIE that the value is being converted to. ++ // FIXME: the constness has annoying ripple effects. ++ DWARFDIE die = const_cast(dwarf_cu)->GetDIE(die_offset); ++ if (!die) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE"); ++ return false; ++ } ++ uint64_t encoding = ++ die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user); ++ bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; ++ if (!bit_size) ++ bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0); ++ if (!bit_size) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Unsupported type size in DW_OP_convert"); ++ return false; ++ } ++ switch (encoding) { ++ case DW_ATE_signed: ++ case DW_ATE_signed_char: ++ sign = true; ++ break; ++ case DW_ATE_unsigned: ++ case DW_ATE_unsigned_char: ++ sign = false; ++ break; ++ default: ++ if (error_ptr) ++ error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert"); ++ return false; ++ } ++ } ++ Scalar &top = stack.back().ResolveValue(m_exe_ctx); ++ top.TruncOrExtendTo(bit_size, sign); ++ break; ++ } ++ ++ // OPCODE: DW_OP_call_frame_cfa ++ // OPERANDS: None ++ // DESCRIPTION: Specifies a DWARF expression that pushes the value of ++ // the canonical frame address consistent with the call frame information ++ // located in .debug_frame (or in the FDEs of the eh_frame section). ++ case DW_OP_call_frame_cfa: ++ if (frame) { ++ // Note that we don't have to parse FDEs because this DWARF expression ++ // is commonly evaluated with a valid stack frame. ++ StackID id = frame->GetStackID(); ++ addr_t cfa = id.GetCallFrameAddress(); ++ if (cfa != LLDB_INVALID_ADDRESS) { ++ stack.push_back(Scalar(cfa)); ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } else if (error_ptr) ++ error_ptr->SetErrorString("Stack frame does not include a canonical " ++ "frame address for DW_OP_call_frame_cfa " ++ "opcode."); ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorString("Invalid stack frame in context for " ++ "DW_OP_call_frame_cfa opcode."); ++ return false; ++ } ++ break; ++ ++ // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension ++ // opcode, DW_OP_GNU_push_tls_address) ++ // OPERANDS: none ++ // DESCRIPTION: Pops a TLS offset from the stack, converts it to ++ // an address in the current thread's thread-local storage block, and ++ // pushes it on the stack. ++ case DW_OP_form_tls_address: ++ case DW_OP_GNU_push_tls_address: { ++ if (stack.size() < 1) { ++ if (error_ptr) { ++ if (op == DW_OP_form_tls_address) ++ error_ptr->SetErrorString( ++ "DW_OP_form_tls_address needs an argument."); ++ else ++ error_ptr->SetErrorString( ++ "DW_OP_GNU_push_tls_address needs an argument."); ++ } ++ return false; ++ } ++ ++ if (!m_exe_ctx || !module_sp) { ++ if (error_ptr) ++ error_ptr->SetErrorString("No context to evaluate TLS within."); ++ return false; ++ } ++ ++ Thread *thread = m_exe_ctx->GetThreadPtr(); ++ if (!thread) { ++ if (error_ptr) ++ error_ptr->SetErrorString("No thread to evaluate TLS within."); ++ return false; ++ } ++ ++ // Lookup the TLS block address for this thread and module. ++ const addr_t tls_file_addr = ++ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); ++ const addr_t tls_load_addr = ++ thread->GetThreadLocalData(module_sp, tls_file_addr); ++ ++ if (tls_load_addr == LLDB_INVALID_ADDRESS) { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "No TLS data currently exists for this thread."); ++ return false; ++ } ++ ++ stack.back().GetScalar() = tls_load_addr; ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } break; ++ ++ // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.) ++ // OPERANDS: 1 ++ // ULEB128: index to the .debug_addr section ++ // DESCRIPTION: Pushes an address to the stack from the .debug_addr ++ // section with the base address specified by the DW_AT_addr_base attribute ++ // and the 0 based index is the ULEB128 encoded index. ++ case DW_OP_addrx: ++ case DW_OP_GNU_addr_index: { ++ if (!dwarf_cu) { ++ if (error_ptr) ++ error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a " ++ "compile unit being specified"); ++ return false; ++ } ++ uint64_t index = opcodes.GetULEB128(&offset); ++ lldb::addr_t value = ++ DWARFExpression::ReadAddressFromDebugAddrSection(dwarf_cu, index); ++ stack.push_back(Scalar(value)); ++ stack.back().SetValueType(Value::ValueType::FileAddress); ++ } break; ++ ++ // OPCODE: DW_OP_GNU_const_index ++ // OPERANDS: 1 ++ // ULEB128: index to the .debug_addr section ++ // DESCRIPTION: Pushes an constant with the size of a machine address to ++ // the stack from the .debug_addr section with the base address specified ++ // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128 ++ // encoded index. ++ case DW_OP_GNU_const_index: { ++ if (!dwarf_cu) { ++ if (error_ptr) ++ error_ptr->SetErrorString("DW_OP_GNU_const_index found without a " ++ "compile unit being specified"); ++ return false; ++ } ++ uint64_t index = opcodes.GetULEB128(&offset); ++ lldb::addr_t value = ++ DWARFExpression::ReadAddressFromDebugAddrSection(dwarf_cu, index); ++ stack.push_back(Scalar(value)); ++ } break; ++ ++ case DW_OP_entry_value: { ++ if (!Evaluate_DW_OP_entry_value(stack, m_exe_ctx, m_reg_ctx, opcodes, ++ offset, error_ptr, log)) { ++ LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op)); ++ return false; ++ } ++ break; ++ } ++ ++ default: ++ LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.", ++ DW_OP_value_to_name(op)); ++ break; ++ } ++ ++ return true; ++} +diff --git a/lldb/source/Expression/DWARFEvaluatorFactory.cpp b/lldb/source/Expression/DWARFEvaluatorFactory.cpp +new file mode 100644 +index 000000000000..c0612641204a +--- /dev/null ++++ b/lldb/source/Expression/DWARFEvaluatorFactory.cpp +@@ -0,0 +1,57 @@ ++//===-- DWARFEvaluatorFactory.cpp -----------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "lldb/Expression/DWARFEvaluatorFactory.h" ++#include "lldb/Expression/DWARFEvaluator.h" ++ ++#include "lldb/Core/PluginManager.h" ++#include "lldb/Core/Value.h" ++#include "lldb/Target/RegisterContext.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++ ++// PluginInterface protocol ++lldb_private::ConstString DWARFEvaluatorFactory::GetPluginName() { ++ static ConstString g_name("vendor-default"); ++ return g_name; ++} ++ ++// FindPlugin ++// ++// Platforms can register a callback to use when creating DWARF expression ++// evaluators to allow handling platform-specific DWARF codes. ++std::unique_ptr ++DWARFEvaluatorFactory::FindPlugin(Module *module) { ++ std::unique_ptr instance_up; ++ DWARFEvaluatorFactoryCreateInstance create_callback; ++ ++ for (size_t idx = 0; ++ (create_callback = ++ PluginManager::GetDWARFEvaluatorFactoryCreateCallbackAtIndex( ++ idx)) != nullptr; ++ ++idx) { ++ instance_up.reset(create_callback(module)); ++ ++ if (instance_up) { ++ return instance_up; ++ } ++ } ++ ++ instance_up.reset(new DWARFEvaluatorFactory()); ++ return instance_up; ++} ++ ++std::unique_ptr DWARFEvaluatorFactory::CreateDWARFEvaluator( ++ const DWARFExpression &dwarf_expression, ExecutionContext *exe_ctx, ++ RegisterContext *reg_ctx, const Value *initial_value_ptr, ++ const Value *object_address_ptr) { ++ return std::make_unique(dwarf_expression, exe_ctx, reg_ctx, ++ initial_value_ptr, ++ object_address_ptr); ++} +diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp +index a10546c1deae..4d13e4642af3 100644 +--- a/lldb/source/Expression/DWARFExpression.cpp ++++ b/lldb/source/Expression/DWARFExpression.cpp +@@ -15,6 +15,8 @@ + #include "lldb/Core/Module.h" + #include "lldb/Core/Value.h" + #include "lldb/Core/dwarf.h" ++#include "lldb/Expression/DWARFEvaluator.h" ++#include "lldb/Expression/DWARFEvaluatorFactory.h" + #include "lldb/Utility/DataEncoder.h" + #include "lldb/Utility/Log.h" + #include "lldb/Utility/RegisterValue.h" +@@ -41,8 +43,8 @@ + using namespace lldb; + using namespace lldb_private; + +-static lldb::addr_t +-ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, ++lldb::addr_t ++DWARFExpression::ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu, + uint32_t index) { + uint32_t index_size = dwarf_cu->GetAddressByteSize(); + dw_offset_t addr_base = dwarf_cu->GetAddrBase(); +@@ -96,7 +98,7 @@ void DWARFExpression::SetLocationListAddresses(addr_t cu_file_addr, + m_loclist_addresses = LoclistAddresses{cu_file_addr, func_file_addr}; + } + +-int DWARFExpression::GetRegisterKind() { return m_reg_kind; } ++RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } + + void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) { + m_reg_kind = reg_kind; +@@ -150,52 +152,6 @@ void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level, + } + } + +-static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx, +- lldb::RegisterKind reg_kind, +- uint32_t reg_num, Status *error_ptr, +- Value &value) { +- if (reg_ctx == nullptr) { +- if (error_ptr) +- error_ptr->SetErrorString("No register context in frame.\n"); +- } else { +- uint32_t native_reg = +- reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); +- if (native_reg == LLDB_INVALID_REGNUM) { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat("Unable to convert register " +- "kind=%u reg_num=%u to a native " +- "register number.\n", +- reg_kind, reg_num); +- } else { +- const RegisterInfo *reg_info = +- reg_ctx->GetRegisterInfoAtIndex(native_reg); +- RegisterValue reg_value; +- if (reg_ctx->ReadRegister(reg_info, reg_value)) { +- if (reg_value.GetScalarValue(value.GetScalar())) { +- value.SetValueType(Value::ValueType::Scalar); +- value.SetContext(Value::ContextType::RegisterInfo, +- const_cast(reg_info)); +- if (error_ptr) +- error_ptr->Clear(); +- return true; +- } else { +- // If we get this error, then we need to implement a value buffer in +- // the dwarf expression evaluation function... +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "register %s can't be converted to a scalar value", +- reg_info->name); +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat("register %s is not available", +- reg_info->name); +- } +- } +- } +- return false; +-} +- + /// Return the length in bytes of the set of operands for \p op. No guarantees + /// are made on the state of \p data after this call. + static offset_t GetOpcodeDataSize(const DataExtractor &data, +@@ -955,1719 +911,17 @@ bool DWARFExpression::Evaluate( + const Value *initial_value_ptr, const Value *object_address_ptr, + Value &result, Status *error_ptr) { + +- if (opcodes.GetByteSize() == 0) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "no location, value may have been optimized out"); +- return false; +- } +- std::vector stack; +- +- Process *process = nullptr; +- StackFrame *frame = nullptr; +- +- if (exe_ctx) { +- process = exe_ctx->GetProcessPtr(); +- frame = exe_ctx->GetFramePtr(); +- } +- if (reg_ctx == nullptr && frame) +- reg_ctx = frame->GetRegisterContext().get(); +- +- if (initial_value_ptr) +- stack.push_back(*initial_value_ptr); +- +- lldb::offset_t offset = 0; +- Value tmp; +- uint32_t reg_num; +- +- /// Insertion point for evaluating multi-piece expression. +- uint64_t op_piece_offset = 0; +- Value pieces; // Used for DW_OP_piece +- +- Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); +- // A generic type is "an integral type that has the size of an address and an +- // unspecified signedness". For now, just use the signedness of the operand. +- // TODO: Implement a real typed stack, and store the genericness of the value +- // there. +- auto to_generic = [&](auto v) { +- bool is_signed = std::is_signed::value; +- return Scalar(llvm::APSInt( +- llvm::APInt(8 * opcodes.GetAddressByteSize(), v, is_signed), +- !is_signed)); +- }; +- +- // The default kind is a memory location. This is updated by any +- // operation that changes this, such as DW_OP_stack_value, and reset +- // by composition operations like DW_OP_piece. +- LocationDescriptionKind dwarf4_location_description_kind = Memory; +- +- while (opcodes.ValidOffset(offset)) { +- const lldb::offset_t op_offset = offset; +- const uint8_t op = opcodes.GetU8(&offset); +- +- if (log && log->GetVerbose()) { +- size_t count = stack.size(); +- LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:", +- (uint64_t)count); +- for (size_t i = 0; i < count; ++i) { +- StreamString new_value; +- new_value.Printf("[%" PRIu64 "]", (uint64_t)i); +- stack[i].Dump(&new_value); +- LLDB_LOGF(log, " %s", new_value.GetData()); +- } +- LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset, +- DW_OP_value_to_name(op)); +- } +- +- switch (op) { +- // The DW_OP_addr operation has a single operand that encodes a machine +- // address and whose size is the size of an address on the target machine. +- case DW_OP_addr: +- stack.push_back(Scalar(opcodes.GetAddress(&offset))); +- stack.back().SetValueType(Value::ValueType::FileAddress); +- // Convert the file address to a load address, so subsequent +- // DWARF operators can operate on it. +- if (frame) +- stack.back().ConvertToLoadAddress(module_sp.get(), +- frame->CalculateTarget().get()); +- break; +- +- // The DW_OP_addr_sect_offset4 is used for any location expressions in +- // shared libraries that have a location like: +- // DW_OP_addr(0x1000) +- // If this address resides in a shared library, then this virtual address +- // won't make sense when it is evaluated in the context of a running +- // process where shared libraries have been slid. To account for this, this +- // new address type where we can store the section pointer and a 4 byte +- // offset. +- // case DW_OP_addr_sect_offset4: +- // { +- // result_type = eResultTypeFileAddress; +- // lldb::Section *sect = (lldb::Section +- // *)opcodes.GetMaxU64(&offset, sizeof(void *)); +- // lldb::addr_t sect_offset = opcodes.GetU32(&offset); +- // +- // Address so_addr (sect, sect_offset); +- // lldb::addr_t load_addr = so_addr.GetLoadAddress(); +- // if (load_addr != LLDB_INVALID_ADDRESS) +- // { +- // // We successfully resolve a file address to a load +- // // address. +- // stack.push_back(load_addr); +- // break; +- // } +- // else +- // { +- // // We were able +- // if (error_ptr) +- // error_ptr->SetErrorStringWithFormat ("Section %s in +- // %s is not currently loaded.\n", +- // sect->GetName().AsCString(), +- // sect->GetModule()->GetFileSpec().GetFilename().AsCString()); +- // return false; +- // } +- // } +- // break; +- +- // OPCODE: DW_OP_deref +- // OPERANDS: none +- // DESCRIPTION: Pops the top stack entry and treats it as an address. +- // The value retrieved from that address is pushed. The size of the data +- // retrieved from the dereferenced address is the size of an address on the +- // target machine. +- case DW_OP_deref: { +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString("Expression stack empty for DW_OP_deref."); +- return false; +- } +- Value::ValueType value_type = stack.back().GetValueType(); +- switch (value_type) { +- case Value::ValueType::HostAddress: { +- void *src = (void *)stack.back().GetScalar().ULongLong(); +- intptr_t ptr; +- ::memcpy(&ptr, src, sizeof(void *)); +- stack.back().GetScalar() = ptr; +- stack.back().ClearContext(); +- } break; +- case Value::ValueType::FileAddress: { +- auto file_addr = stack.back().GetScalar().ULongLong( +- LLDB_INVALID_ADDRESS); +- if (!module_sp) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "need module to resolve file address for DW_OP_deref"); +- return false; +- } +- Address so_addr; +- if (!module_sp->ResolveFileAddress(file_addr, so_addr)) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "failed to resolve file address in module"); +- return false; +- } +- addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr()); +- if (load_Addr == LLDB_INVALID_ADDRESS) { +- if (error_ptr) +- error_ptr->SetErrorString("failed to resolve load address"); +- return false; +- } +- stack.back().GetScalar() = load_Addr; +- // Fall through to load address promotion code below. +- } LLVM_FALLTHROUGH; +- case Value::ValueType::Scalar: +- // Promote Scalar to LoadAddress and fall through. +- stack.back().SetValueType(Value::ValueType::LoadAddress); +- LLVM_FALLTHROUGH; +- case Value::ValueType::LoadAddress: +- if (exe_ctx) { +- if (process) { +- lldb::addr_t pointer_addr = +- stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); +- Status error; +- lldb::addr_t pointer_value = +- process->ReadPointerFromMemory(pointer_addr, error); +- if (pointer_value != LLDB_INVALID_ADDRESS) { +- if (ABISP abi_sp = process->GetABI()) +- pointer_value = abi_sp->FixCodeAddress(pointer_value); +- stack.back().GetScalar() = pointer_value; +- stack.back().ClearContext(); +- } else { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "Failed to dereference pointer from 0x%" PRIx64 +- " for DW_OP_deref: %s\n", +- pointer_addr, error.AsCString()); +- return false; +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorString("NULL process for DW_OP_deref.\n"); +- return false; +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorString( +- "NULL execution context for DW_OP_deref.\n"); +- return false; +- } +- break; +- +- case Value::ValueType::Invalid: +- if (error_ptr) +- error_ptr->SetErrorString("Invalid value type for DW_OP_deref.\n"); +- return false; +- } +- +- } break; +- +- // OPCODE: DW_OP_deref_size +- // OPERANDS: 1 +- // 1 - uint8_t that specifies the size of the data to dereference. +- // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top +- // stack entry and treats it as an address. The value retrieved from that +- // address is pushed. In the DW_OP_deref_size operation, however, the size +- // in bytes of the data retrieved from the dereferenced address is +- // specified by the single operand. This operand is a 1-byte unsigned +- // integral constant whose value may not be larger than the size of an +- // address on the target machine. The data retrieved is zero extended to +- // the size of an address on the target machine before being pushed on the +- // expression stack. +- case DW_OP_deref_size: { +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack empty for DW_OP_deref_size."); +- return false; +- } +- uint8_t size = opcodes.GetU8(&offset); +- Value::ValueType value_type = stack.back().GetValueType(); +- switch (value_type) { +- case Value::ValueType::HostAddress: { +- void *src = (void *)stack.back().GetScalar().ULongLong(); +- intptr_t ptr; +- ::memcpy(&ptr, src, sizeof(void *)); +- // I can't decide whether the size operand should apply to the bytes in +- // their +- // lldb-host endianness or the target endianness.. I doubt this'll ever +- // come up but I'll opt for assuming big endian regardless. +- switch (size) { +- case 1: +- ptr = ptr & 0xff; +- break; +- case 2: +- ptr = ptr & 0xffff; +- break; +- case 3: +- ptr = ptr & 0xffffff; +- break; +- case 4: +- ptr = ptr & 0xffffffff; +- break; +- // the casts are added to work around the case where intptr_t is a 32 +- // bit quantity; +- // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this +- // program. +- case 5: +- ptr = (intptr_t)ptr & 0xffffffffffULL; +- break; +- case 6: +- ptr = (intptr_t)ptr & 0xffffffffffffULL; +- break; +- case 7: +- ptr = (intptr_t)ptr & 0xffffffffffffffULL; +- break; +- default: +- break; +- } +- stack.back().GetScalar() = ptr; +- stack.back().ClearContext(); +- } break; +- case Value::ValueType::Scalar: +- case Value::ValueType::LoadAddress: +- if (exe_ctx) { +- if (process) { +- lldb::addr_t pointer_addr = +- stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); +- uint8_t addr_bytes[sizeof(lldb::addr_t)]; +- Status error; +- if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) == +- size) { +- DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), +- process->GetByteOrder(), size); +- lldb::offset_t addr_data_offset = 0; +- switch (size) { +- case 1: +- stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset); +- break; +- case 2: +- stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset); +- break; +- case 4: +- stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset); +- break; +- case 8: +- stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset); +- break; +- default: +- stack.back().GetScalar() = +- addr_data.GetAddress(&addr_data_offset); +- } +- stack.back().ClearContext(); +- } else { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "Failed to dereference pointer from 0x%" PRIx64 +- " for DW_OP_deref: %s\n", +- pointer_addr, error.AsCString()); +- return false; +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorString("NULL process for DW_OP_deref_size.\n"); +- return false; +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorString( +- "NULL execution context for DW_OP_deref_size.\n"); +- return false; +- } +- break; +- +- case Value::ValueType::FileAddress: +- case Value::ValueType::Invalid: +- if (error_ptr) +- error_ptr->SetErrorString("Invalid value for DW_OP_deref_size.\n"); +- return false; +- } +- +- } break; +- +- // OPCODE: DW_OP_xderef_size +- // OPERANDS: 1 +- // 1 - uint8_t that specifies the size of the data to dereference. +- // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at +- // the top of the stack is treated as an address. The second stack entry is +- // treated as an "address space identifier" for those architectures that +- // support multiple address spaces. The top two stack elements are popped, +- // a data item is retrieved through an implementation-defined address +- // calculation and pushed as the new stack top. In the DW_OP_xderef_size +- // operation, however, the size in bytes of the data retrieved from the +- // dereferenced address is specified by the single operand. This operand is +- // a 1-byte unsigned integral constant whose value may not be larger than +- // the size of an address on the target machine. The data retrieved is zero +- // extended to the size of an address on the target machine before being +- // pushed on the expression stack. +- case DW_OP_xderef_size: +- if (error_ptr) +- error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size."); +- return false; +- // OPCODE: DW_OP_xderef +- // OPERANDS: none +- // DESCRIPTION: Provides an extended dereference mechanism. The entry at +- // the top of the stack is treated as an address. The second stack entry is +- // treated as an "address space identifier" for those architectures that +- // support multiple address spaces. The top two stack elements are popped, +- // a data item is retrieved through an implementation-defined address +- // calculation and pushed as the new stack top. The size of the data +- // retrieved from the dereferenced address is the size of an address on the +- // target machine. +- case DW_OP_xderef: +- if (error_ptr) +- error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef."); +- return false; +- +- // All DW_OP_constXXX opcodes have a single operand as noted below: +- // +- // Opcode Operand 1 +- // DW_OP_const1u 1-byte unsigned integer constant +- // DW_OP_const1s 1-byte signed integer constant +- // DW_OP_const2u 2-byte unsigned integer constant +- // DW_OP_const2s 2-byte signed integer constant +- // DW_OP_const4u 4-byte unsigned integer constant +- // DW_OP_const4s 4-byte signed integer constant +- // DW_OP_const8u 8-byte unsigned integer constant +- // DW_OP_const8s 8-byte signed integer constant +- // DW_OP_constu unsigned LEB128 integer constant +- // DW_OP_consts signed LEB128 integer constant +- case DW_OP_const1u: +- stack.push_back(to_generic(opcodes.GetU8(&offset))); +- break; +- case DW_OP_const1s: +- stack.push_back(to_generic((int8_t)opcodes.GetU8(&offset))); +- break; +- case DW_OP_const2u: +- stack.push_back(to_generic(opcodes.GetU16(&offset))); +- break; +- case DW_OP_const2s: +- stack.push_back(to_generic((int16_t)opcodes.GetU16(&offset))); +- break; +- case DW_OP_const4u: +- stack.push_back(to_generic(opcodes.GetU32(&offset))); +- break; +- case DW_OP_const4s: +- stack.push_back(to_generic((int32_t)opcodes.GetU32(&offset))); +- break; +- case DW_OP_const8u: +- stack.push_back(to_generic(opcodes.GetU64(&offset))); +- break; +- case DW_OP_const8s: +- stack.push_back(to_generic((int64_t)opcodes.GetU64(&offset))); +- break; +- // These should also use to_generic, but we can't do that due to a +- // producer-side bug in llvm. See llvm.org/pr48087. +- case DW_OP_constu: +- stack.push_back(Scalar(opcodes.GetULEB128(&offset))); +- break; +- case DW_OP_consts: +- stack.push_back(Scalar(opcodes.GetSLEB128(&offset))); +- break; +- +- // OPCODE: DW_OP_dup +- // OPERANDS: none +- // DESCRIPTION: duplicates the value at the top of the stack +- case DW_OP_dup: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString("Expression stack empty for DW_OP_dup."); +- return false; +- } else +- stack.push_back(stack.back()); +- break; +- +- // OPCODE: DW_OP_drop +- // OPERANDS: none +- // DESCRIPTION: pops the value at the top of the stack +- case DW_OP_drop: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString("Expression stack empty for DW_OP_drop."); +- return false; +- } else +- stack.pop_back(); +- break; +- +- // OPCODE: DW_OP_over +- // OPERANDS: none +- // DESCRIPTION: Duplicates the entry currently second in the stack at +- // the top of the stack. +- case DW_OP_over: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_over."); +- return false; +- } else +- stack.push_back(stack[stack.size() - 2]); +- break; +- +- // OPCODE: DW_OP_pick +- // OPERANDS: uint8_t index into the current stack +- // DESCRIPTION: The stack entry with the specified index (0 through 255, +- // inclusive) is pushed on the stack +- case DW_OP_pick: { +- uint8_t pick_idx = opcodes.GetU8(&offset); +- if (pick_idx < stack.size()) +- stack.push_back(stack[stack.size() - 1 - pick_idx]); +- else { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "Index %u out of range for DW_OP_pick.\n", pick_idx); +- return false; +- } +- } break; +- +- // OPCODE: DW_OP_swap +- // OPERANDS: none +- // DESCRIPTION: swaps the top two stack entries. The entry at the top +- // of the stack becomes the second stack entry, and the second entry +- // becomes the top of the stack +- case DW_OP_swap: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_swap."); +- return false; +- } else { +- tmp = stack.back(); +- stack.back() = stack[stack.size() - 2]; +- stack[stack.size() - 2] = tmp; +- } +- break; +- +- // OPCODE: DW_OP_rot +- // OPERANDS: none +- // DESCRIPTION: Rotates the first three stack entries. The entry at +- // the top of the stack becomes the third stack entry, the second entry +- // becomes the top of the stack, and the third entry becomes the second +- // entry. +- case DW_OP_rot: +- if (stack.size() < 3) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 3 items for DW_OP_rot."); +- return false; +- } else { +- size_t last_idx = stack.size() - 1; +- Value old_top = stack[last_idx]; +- stack[last_idx] = stack[last_idx - 1]; +- stack[last_idx - 1] = stack[last_idx - 2]; +- stack[last_idx - 2] = old_top; +- } +- break; +- +- // OPCODE: DW_OP_abs +- // OPERANDS: none +- // DESCRIPTION: pops the top stack entry, interprets it as a signed +- // value and pushes its absolute value. If the absolute value can not be +- // represented, the result is undefined. +- case DW_OP_abs: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_abs."); +- return false; +- } else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Failed to take the absolute value of the first stack item."); +- return false; +- } +- break; +- +- // OPCODE: DW_OP_and +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, performs a bitwise and +- // operation on the two, and pushes the result. +- case DW_OP_and: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_and."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_div +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, divides the former second +- // entry by the former top of the stack using signed division, and pushes +- // the result. +- case DW_OP_div: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_div."); +- return false; +- } else { +- tmp = stack.back(); +- if (tmp.ResolveValue(exe_ctx).IsZero()) { +- if (error_ptr) +- error_ptr->SetErrorString("Divide by zero."); +- return false; +- } else { +- stack.pop_back(); +- stack.back() = +- stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx); +- if (!stack.back().ResolveValue(exe_ctx).IsValid()) { +- if (error_ptr) +- error_ptr->SetErrorString("Divide failed."); +- return false; +- } +- } +- } +- break; +- +- // OPCODE: DW_OP_minus +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, subtracts the former top +- // of the stack from the former second entry, and pushes the result. +- case DW_OP_minus: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_minus."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_mod +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values and pushes the result of +- // the calculation: former second stack entry modulo the former top of the +- // stack. +- case DW_OP_mod: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_mod."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_mul +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, multiplies them +- // together, and pushes the result. +- case DW_OP_mul: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_mul."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_neg +- // OPERANDS: none +- // DESCRIPTION: pops the top stack entry, and pushes its negation. +- case DW_OP_neg: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_neg."); +- return false; +- } else { +- if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) { +- if (error_ptr) +- error_ptr->SetErrorString("Unary negate failed."); +- return false; +- } +- } +- break; +- +- // OPCODE: DW_OP_not +- // OPERANDS: none +- // DESCRIPTION: pops the top stack entry, and pushes its bitwise +- // complement +- case DW_OP_not: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_not."); +- return false; +- } else { +- if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) { +- if (error_ptr) +- error_ptr->SetErrorString("Logical NOT failed."); +- return false; +- } +- } +- break; +- +- // OPCODE: DW_OP_or +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, performs a bitwise or +- // operation on the two, and pushes the result. +- case DW_OP_or: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_or."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_plus +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, adds them together, and +- // pushes the result. +- case DW_OP_plus: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_plus."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().GetScalar() += tmp.GetScalar(); +- } +- break; +- +- // OPCODE: DW_OP_plus_uconst +- // OPERANDS: none +- // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128 +- // constant operand and pushes the result. +- case DW_OP_plus_uconst: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_plus_uconst."); +- return false; +- } else { +- const uint64_t uconst_value = opcodes.GetULEB128(&offset); +- // Implicit conversion from a UINT to a Scalar... +- stack.back().GetScalar() += uconst_value; +- if (!stack.back().GetScalar().IsValid()) { +- if (error_ptr) +- error_ptr->SetErrorString("DW_OP_plus_uconst failed."); +- return false; +- } +- } +- break; +- +- // OPCODE: DW_OP_shl +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, shifts the former +- // second entry left by the number of bits specified by the former top of +- // the stack, and pushes the result. +- case DW_OP_shl: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_shl."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_shr +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, shifts the former second +- // entry right logically (filling with zero bits) by the number of bits +- // specified by the former top of the stack, and pushes the result. +- case DW_OP_shr: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_shr."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical( +- tmp.ResolveValue(exe_ctx))) { +- if (error_ptr) +- error_ptr->SetErrorString("DW_OP_shr failed."); +- return false; +- } +- } +- break; +- +- // OPCODE: DW_OP_shra +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, shifts the former second +- // entry right arithmetically (divide the magnitude by 2, keep the same +- // sign for the result) by the number of bits specified by the former top +- // of the stack, and pushes the result. +- case DW_OP_shra: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_shra."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_xor +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack entries, performs the bitwise +- // exclusive-or operation on the two, and pushes the result. +- case DW_OP_xor: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_xor."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_skip +- // OPERANDS: int16_t +- // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte +- // signed integer constant. The 2-byte constant is the number of bytes of +- // the DWARF expression to skip forward or backward from the current +- // operation, beginning after the 2-byte constant. +- case DW_OP_skip: { +- int16_t skip_offset = (int16_t)opcodes.GetU16(&offset); +- lldb::offset_t new_offset = offset + skip_offset; +- if (opcodes.ValidOffset(new_offset)) +- offset = new_offset; +- else { +- if (error_ptr) +- error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip."); +- return false; +- } +- } break; +- +- // OPCODE: DW_OP_bra +- // OPERANDS: int16_t +- // DESCRIPTION: A conditional branch. Its single operand is a 2-byte +- // signed integer constant. This operation pops the top of stack. If the +- // value popped is not the constant 0, the 2-byte constant operand is the +- // number of bytes of the DWARF expression to skip forward or backward from +- // the current operation, beginning after the 2-byte constant. +- case DW_OP_bra: +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_bra."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- int16_t bra_offset = (int16_t)opcodes.GetU16(&offset); +- Scalar zero(0); +- if (tmp.ResolveValue(exe_ctx) != zero) { +- lldb::offset_t new_offset = offset + bra_offset; +- if (opcodes.ValidOffset(new_offset)) +- offset = new_offset; +- else { +- if (error_ptr) +- error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra."); +- return false; +- } +- } +- } +- break; +- +- // OPCODE: DW_OP_eq +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, compares using the +- // equals (==) operator. +- // STACK RESULT: push the constant value 1 onto the stack if the result +- // of the operation is true or the constant value 0 if the result of the +- // operation is false. +- case DW_OP_eq: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_eq."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_ge +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, compares using the +- // greater than or equal to (>=) operator. +- // STACK RESULT: push the constant value 1 onto the stack if the result +- // of the operation is true or the constant value 0 if the result of the +- // operation is false. +- case DW_OP_ge: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_ge."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_gt +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, compares using the +- // greater than (>) operator. +- // STACK RESULT: push the constant value 1 onto the stack if the result +- // of the operation is true or the constant value 0 if the result of the +- // operation is false. +- case DW_OP_gt: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_gt."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_le +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, compares using the +- // less than or equal to (<=) operator. +- // STACK RESULT: push the constant value 1 onto the stack if the result +- // of the operation is true or the constant value 0 if the result of the +- // operation is false. +- case DW_OP_le: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_le."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_lt +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, compares using the +- // less than (<) operator. +- // STACK RESULT: push the constant value 1 onto the stack if the result +- // of the operation is true or the constant value 0 if the result of the +- // operation is false. +- case DW_OP_lt: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_lt."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_ne +- // OPERANDS: none +- // DESCRIPTION: pops the top two stack values, compares using the +- // not equal (!=) operator. +- // STACK RESULT: push the constant value 1 onto the stack if the result +- // of the operation is true or the constant value 0 if the result of the +- // operation is false. +- case DW_OP_ne: +- if (stack.size() < 2) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 2 items for DW_OP_ne."); +- return false; +- } else { +- tmp = stack.back(); +- stack.pop_back(); +- stack.back().ResolveValue(exe_ctx) = +- stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx); +- } +- break; +- +- // OPCODE: DW_OP_litn +- // OPERANDS: none +- // DESCRIPTION: encode the unsigned literal values from 0 through 31. +- // STACK RESULT: push the unsigned literal constant value onto the top +- // of the stack. +- case DW_OP_lit0: +- case DW_OP_lit1: +- case DW_OP_lit2: +- case DW_OP_lit3: +- case DW_OP_lit4: +- case DW_OP_lit5: +- case DW_OP_lit6: +- case DW_OP_lit7: +- case DW_OP_lit8: +- case DW_OP_lit9: +- case DW_OP_lit10: +- case DW_OP_lit11: +- case DW_OP_lit12: +- case DW_OP_lit13: +- case DW_OP_lit14: +- case DW_OP_lit15: +- case DW_OP_lit16: +- case DW_OP_lit17: +- case DW_OP_lit18: +- case DW_OP_lit19: +- case DW_OP_lit20: +- case DW_OP_lit21: +- case DW_OP_lit22: +- case DW_OP_lit23: +- case DW_OP_lit24: +- case DW_OP_lit25: +- case DW_OP_lit26: +- case DW_OP_lit27: +- case DW_OP_lit28: +- case DW_OP_lit29: +- case DW_OP_lit30: +- case DW_OP_lit31: +- stack.push_back(to_generic(op - DW_OP_lit0)); +- break; +- +- // OPCODE: DW_OP_regN +- // OPERANDS: none +- // DESCRIPTION: Push the value in register n on the top of the stack. +- case DW_OP_reg0: +- case DW_OP_reg1: +- case DW_OP_reg2: +- case DW_OP_reg3: +- case DW_OP_reg4: +- case DW_OP_reg5: +- case DW_OP_reg6: +- case DW_OP_reg7: +- case DW_OP_reg8: +- case DW_OP_reg9: +- case DW_OP_reg10: +- case DW_OP_reg11: +- case DW_OP_reg12: +- case DW_OP_reg13: +- case DW_OP_reg14: +- case DW_OP_reg15: +- case DW_OP_reg16: +- case DW_OP_reg17: +- case DW_OP_reg18: +- case DW_OP_reg19: +- case DW_OP_reg20: +- case DW_OP_reg21: +- case DW_OP_reg22: +- case DW_OP_reg23: +- case DW_OP_reg24: +- case DW_OP_reg25: +- case DW_OP_reg26: +- case DW_OP_reg27: +- case DW_OP_reg28: +- case DW_OP_reg29: +- case DW_OP_reg30: +- case DW_OP_reg31: { +- dwarf4_location_description_kind = Register; +- reg_num = op - DW_OP_reg0; +- +- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) +- stack.push_back(tmp); +- else +- return false; +- } break; +- // OPCODE: DW_OP_regx +- // OPERANDS: +- // ULEB128 literal operand that encodes the register. +- // DESCRIPTION: Push the value in register on the top of the stack. +- case DW_OP_regx: { +- dwarf4_location_description_kind = Register; +- reg_num = opcodes.GetULEB128(&offset); +- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp)) +- stack.push_back(tmp); +- else +- return false; +- } break; +- +- // OPCODE: DW_OP_bregN +- // OPERANDS: +- // SLEB128 offset from register N +- // DESCRIPTION: Value is in memory at the address specified by register +- // N plus an offset. +- case DW_OP_breg0: +- case DW_OP_breg1: +- case DW_OP_breg2: +- case DW_OP_breg3: +- case DW_OP_breg4: +- case DW_OP_breg5: +- case DW_OP_breg6: +- case DW_OP_breg7: +- case DW_OP_breg8: +- case DW_OP_breg9: +- case DW_OP_breg10: +- case DW_OP_breg11: +- case DW_OP_breg12: +- case DW_OP_breg13: +- case DW_OP_breg14: +- case DW_OP_breg15: +- case DW_OP_breg16: +- case DW_OP_breg17: +- case DW_OP_breg18: +- case DW_OP_breg19: +- case DW_OP_breg20: +- case DW_OP_breg21: +- case DW_OP_breg22: +- case DW_OP_breg23: +- case DW_OP_breg24: +- case DW_OP_breg25: +- case DW_OP_breg26: +- case DW_OP_breg27: +- case DW_OP_breg28: +- case DW_OP_breg29: +- case DW_OP_breg30: +- case DW_OP_breg31: { +- reg_num = op - DW_OP_breg0; +- +- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, +- tmp)) { +- int64_t breg_offset = opcodes.GetSLEB128(&offset); +- tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; +- tmp.ClearContext(); +- stack.push_back(tmp); +- stack.back().SetValueType(Value::ValueType::LoadAddress); +- } else +- return false; +- } break; +- // OPCODE: DW_OP_bregx +- // OPERANDS: 2 +- // ULEB128 literal operand that encodes the register. +- // SLEB128 offset from register N +- // DESCRIPTION: Value is in memory at the address specified by register +- // N plus an offset. +- case DW_OP_bregx: { +- reg_num = opcodes.GetULEB128(&offset); +- +- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, +- tmp)) { +- int64_t breg_offset = opcodes.GetSLEB128(&offset); +- tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset; +- tmp.ClearContext(); +- stack.push_back(tmp); +- stack.back().SetValueType(Value::ValueType::LoadAddress); +- } else +- return false; +- } break; +- +- case DW_OP_fbreg: +- if (exe_ctx) { +- if (frame) { +- Scalar value; +- if (frame->GetFrameBaseValue(value, error_ptr)) { +- int64_t fbreg_offset = opcodes.GetSLEB128(&offset); +- value += fbreg_offset; +- stack.push_back(value); +- stack.back().SetValueType(Value::ValueType::LoadAddress); +- } else +- return false; +- } else { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Invalid stack frame in context for DW_OP_fbreg opcode."); +- return false; +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorString( +- "NULL execution context for DW_OP_fbreg.\n"); +- return false; +- } +- +- break; +- +- // OPCODE: DW_OP_nop +- // OPERANDS: none +- // DESCRIPTION: A place holder. It has no effect on the location stack +- // or any of its values. +- case DW_OP_nop: +- break; +- +- // OPCODE: DW_OP_piece +- // OPERANDS: 1 +- // ULEB128: byte size of the piece +- // DESCRIPTION: The operand describes the size in bytes of the piece of +- // the object referenced by the DWARF expression whose result is at the top +- // of the stack. If the piece is located in a register, but does not occupy +- // the entire register, the placement of the piece within that register is +- // defined by the ABI. +- // +- // Many compilers store a single variable in sets of registers, or store a +- // variable partially in memory and partially in registers. DW_OP_piece +- // provides a way of describing how large a part of a variable a particular +- // DWARF expression refers to. +- case DW_OP_piece: { +- LocationDescriptionKind piece_locdesc = dwarf4_location_description_kind; +- // Reset for the next piece. +- dwarf4_location_description_kind = Memory; +- +- const uint64_t piece_byte_size = opcodes.GetULEB128(&offset); +- +- if (piece_byte_size > 0) { +- Value curr_piece; +- +- if (stack.empty()) { +- UpdateValueTypeFromLocationDescription( +- log, dwarf_cu, LocationDescriptionKind::Empty); +- // In a multi-piece expression, this means that the current piece is +- // not available. Fill with zeros for now by resizing the data and +- // appending it +- curr_piece.ResizeData(piece_byte_size); +- // Note that "0" is not a correct value for the unknown bits. +- // It would be better to also return a mask of valid bits together +- // with the expression result, so the debugger can print missing +- // members as "" or something. +- ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size); +- pieces.AppendDataToHostBuffer(curr_piece); +- } else { +- Status error; +- // Extract the current piece into "curr_piece" +- Value curr_piece_source_value(stack.back()); +- stack.pop_back(); +- UpdateValueTypeFromLocationDescription(log, dwarf_cu, piece_locdesc, +- &curr_piece_source_value); +- +- const Value::ValueType curr_piece_source_value_type = +- curr_piece_source_value.GetValueType(); +- switch (curr_piece_source_value_type) { +- case Value::ValueType::Invalid: +- return false; +- case Value::ValueType::LoadAddress: +- if (process) { +- if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) { +- lldb::addr_t load_addr = +- curr_piece_source_value.GetScalar().ULongLong( +- LLDB_INVALID_ADDRESS); +- if (process->ReadMemory( +- load_addr, curr_piece.GetBuffer().GetBytes(), +- piece_byte_size, error) != piece_byte_size) { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "failed to read memory DW_OP_piece(%" PRIu64 +- ") from 0x%" PRIx64, +- piece_byte_size, load_addr); +- return false; +- } +- } else { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "failed to resize the piece memory buffer for " +- "DW_OP_piece(%" PRIu64 ")", +- piece_byte_size); +- return false; +- } +- } +- break; +- +- case Value::ValueType::FileAddress: +- case Value::ValueType::HostAddress: +- if (error_ptr) { +- lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong( +- LLDB_INVALID_ADDRESS); +- error_ptr->SetErrorStringWithFormat( +- "failed to read memory DW_OP_piece(%" PRIu64 +- ") from %s address 0x%" PRIx64, +- piece_byte_size, curr_piece_source_value.GetValueType() == +- Value::ValueType::FileAddress +- ? "file" +- : "host", +- addr); +- } +- return false; +- +- case Value::ValueType::Scalar: { +- uint32_t bit_size = piece_byte_size * 8; +- uint32_t bit_offset = 0; +- Scalar &scalar = curr_piece_source_value.GetScalar(); +- if (!scalar.ExtractBitfield( +- bit_size, bit_offset)) { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "unable to extract %" PRIu64 " bytes from a %" PRIu64 +- " byte scalar value.", +- piece_byte_size, +- (uint64_t)curr_piece_source_value.GetScalar() +- .GetByteSize()); +- return false; +- } +- // Create curr_piece with bit_size. By default Scalar +- // grows to the nearest host integer type. +- llvm::APInt fail_value(1, 0, false); +- llvm::APInt ap_int = scalar.UInt128(fail_value); +- assert(ap_int.getBitWidth() >= bit_size); +- llvm::ArrayRef buf{ap_int.getRawData(), +- ap_int.getNumWords()}; +- curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf)); +- } break; +- } +- +- // Check if this is the first piece? +- if (op_piece_offset == 0) { +- // This is the first piece, we should push it back onto the stack +- // so subsequent pieces will be able to access this piece and add +- // to it. +- if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { +- if (error_ptr) +- error_ptr->SetErrorString("failed to append piece data"); +- return false; +- } +- } else { +- // If this is the second or later piece there should be a value on +- // the stack. +- if (pieces.GetBuffer().GetByteSize() != op_piece_offset) { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "DW_OP_piece for offset %" PRIu64 +- " but top of stack is of size %" PRIu64, +- op_piece_offset, pieces.GetBuffer().GetByteSize()); +- return false; +- } +- +- if (pieces.AppendDataToHostBuffer(curr_piece) == 0) { +- if (error_ptr) +- error_ptr->SetErrorString("failed to append piece data"); +- return false; +- } +- } +- } +- op_piece_offset += piece_byte_size; +- } +- } break; +- +- case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3); +- if (stack.size() < 1) { +- UpdateValueTypeFromLocationDescription(log, dwarf_cu, +- LocationDescriptionKind::Empty); +- // Reset for the next piece. +- dwarf4_location_description_kind = Memory; +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_bit_piece."); +- return false; +- } else { +- UpdateValueTypeFromLocationDescription( +- log, dwarf_cu, dwarf4_location_description_kind, &stack.back()); +- // Reset for the next piece. +- dwarf4_location_description_kind = Memory; +- const uint64_t piece_bit_size = opcodes.GetULEB128(&offset); +- const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset); +- switch (stack.back().GetValueType()) { +- case Value::ValueType::Invalid: +- return false; +- case Value::ValueType::Scalar: { +- if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size, +- piece_bit_offset)) { +- if (error_ptr) +- error_ptr->SetErrorStringWithFormat( +- "unable to extract %" PRIu64 " bit value with %" PRIu64 +- " bit offset from a %" PRIu64 " bit scalar value.", +- piece_bit_size, piece_bit_offset, +- (uint64_t)(stack.back().GetScalar().GetByteSize() * 8)); +- return false; +- } +- } break; +- +- case Value::ValueType::FileAddress: +- case Value::ValueType::LoadAddress: +- case Value::ValueType::HostAddress: +- if (error_ptr) { +- error_ptr->SetErrorStringWithFormat( +- "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64 +- ", bit_offset = %" PRIu64 ") from an address value.", +- piece_bit_size, piece_bit_offset); +- } +- return false; +- } +- } +- break; +- +- // OPCODE: DW_OP_implicit_value +- // OPERANDS: 2 +- // ULEB128 size of the value block in bytes +- // uint8_t* block bytes encoding value in target's memory +- // representation +- // DESCRIPTION: Value is immediately stored in block in the debug info with +- // the memory representation of the target. +- case DW_OP_implicit_value: { +- dwarf4_location_description_kind = Implicit; +- +- const uint32_t len = opcodes.GetULEB128(&offset); +- const void *data = opcodes.GetData(&offset, len); +- +- if (!data) { +- LLDB_LOG(log, "Evaluate_DW_OP_implicit_value: could not be read data"); +- LLDB_ERRORF(error_ptr, "Could not evaluate %s.", +- DW_OP_value_to_name(op)); +- return false; +- } +- +- Value result(data, len); +- stack.push_back(result); +- break; +- } +- +- case DW_OP_implicit_pointer: { +- dwarf4_location_description_kind = Implicit; +- LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op)); +- return false; +- } +- +- // OPCODE: DW_OP_push_object_address +- // OPERANDS: none +- // DESCRIPTION: Pushes the address of the object currently being +- // evaluated as part of evaluation of a user presented expression. This +- // object may correspond to an independent variable described by its own +- // DIE or it may be a component of an array, structure, or class whose +- // address has been dynamically determined by an earlier step during user +- // expression evaluation. +- case DW_OP_push_object_address: +- if (object_address_ptr) +- stack.push_back(*object_address_ptr); +- else { +- if (error_ptr) +- error_ptr->SetErrorString("DW_OP_push_object_address used without " +- "specifying an object address"); +- return false; +- } +- break; +- +- // OPCODE: DW_OP_call2 +- // OPERANDS: +- // uint16_t compile unit relative offset of a DIE +- // DESCRIPTION: Performs subroutine calls during evaluation +- // of a DWARF expression. The operand is the 2-byte unsigned offset of a +- // debugging information entry in the current compilation unit. +- // +- // Operand interpretation is exactly like that for DW_FORM_ref2. +- // +- // This operation transfers control of DWARF expression evaluation to the +- // DW_AT_location attribute of the referenced DIE. If there is no such +- // attribute, then there is no effect. Execution of the DWARF expression of +- // a DW_AT_location attribute may add to and/or remove from values on the +- // stack. Execution returns to the point following the call when the end of +- // the attribute is reached. Values on the stack at the time of the call +- // may be used as parameters by the called expression and values left on +- // the stack by the called expression may be used as return values by prior +- // agreement between the calling and called expressions. +- case DW_OP_call2: +- if (error_ptr) +- error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2."); +- return false; +- // OPCODE: DW_OP_call4 +- // OPERANDS: 1 +- // uint32_t compile unit relative offset of a DIE +- // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF +- // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of +- // a debugging information entry in the current compilation unit. +- // +- // Operand interpretation DW_OP_call4 is exactly like that for +- // DW_FORM_ref4. +- // +- // This operation transfers control of DWARF expression evaluation to the +- // DW_AT_location attribute of the referenced DIE. If there is no such +- // attribute, then there is no effect. Execution of the DWARF expression of +- // a DW_AT_location attribute may add to and/or remove from values on the +- // stack. Execution returns to the point following the call when the end of +- // the attribute is reached. Values on the stack at the time of the call +- // may be used as parameters by the called expression and values left on +- // the stack by the called expression may be used as return values by prior +- // agreement between the calling and called expressions. +- case DW_OP_call4: +- if (error_ptr) +- error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4."); +- return false; +- +- // OPCODE: DW_OP_stack_value +- // OPERANDS: None +- // DESCRIPTION: Specifies that the object does not exist in memory but +- // rather is a constant value. The value from the top of the stack is the +- // value to be used. This is the actual object value and not the location. +- case DW_OP_stack_value: +- dwarf4_location_description_kind = Implicit; +- if (stack.empty()) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_stack_value."); +- return false; +- } +- stack.back().SetValueType(Value::ValueType::Scalar); +- break; +- +- // OPCODE: DW_OP_convert +- // OPERANDS: 1 +- // A ULEB128 that is either a DIE offset of a +- // DW_TAG_base_type or 0 for the generic (pointer-sized) type. +- // +- // DESCRIPTION: Pop the top stack element, convert it to a +- // different type, and push the result. +- case DW_OP_convert: { +- if (stack.size() < 1) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "Expression stack needs at least 1 item for DW_OP_convert."); +- return false; +- } +- const uint64_t die_offset = opcodes.GetULEB128(&offset); +- uint64_t bit_size; +- bool sign; +- if (die_offset == 0) { +- // The generic type has the size of an address on the target +- // machine and an unspecified signedness. Scalar has no +- // "unspecified signedness", so we use unsigned types. +- if (!module_sp) { +- if (error_ptr) +- error_ptr->SetErrorString("No module"); +- return false; +- } +- sign = false; +- bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8; +- if (!bit_size) { +- if (error_ptr) +- error_ptr->SetErrorString("unspecified architecture"); +- return false; +- } +- } else { +- // Retrieve the type DIE that the value is being converted to. +- // FIXME: the constness has annoying ripple effects. +- DWARFDIE die = const_cast(dwarf_cu)->GetDIE(die_offset); +- if (!die) { +- if (error_ptr) +- error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE"); +- return false; +- } +- uint64_t encoding = +- die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user); +- bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8; +- if (!bit_size) +- bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0); +- if (!bit_size) { +- if (error_ptr) +- error_ptr->SetErrorString("Unsupported type size in DW_OP_convert"); +- return false; +- } +- switch (encoding) { +- case DW_ATE_signed: +- case DW_ATE_signed_char: +- sign = true; +- break; +- case DW_ATE_unsigned: +- case DW_ATE_unsigned_char: +- sign = false; +- break; +- default: +- if (error_ptr) +- error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert"); +- return false; +- } +- } +- Scalar &top = stack.back().ResolveValue(exe_ctx); +- top.TruncOrExtendTo(bit_size, sign); +- break; +- } +- +- // OPCODE: DW_OP_call_frame_cfa +- // OPERANDS: None +- // DESCRIPTION: Specifies a DWARF expression that pushes the value of +- // the canonical frame address consistent with the call frame information +- // located in .debug_frame (or in the FDEs of the eh_frame section). +- case DW_OP_call_frame_cfa: +- if (frame) { +- // Note that we don't have to parse FDEs because this DWARF expression +- // is commonly evaluated with a valid stack frame. +- StackID id = frame->GetStackID(); +- addr_t cfa = id.GetCallFrameAddress(); +- if (cfa != LLDB_INVALID_ADDRESS) { +- stack.push_back(Scalar(cfa)); +- stack.back().SetValueType(Value::ValueType::LoadAddress); +- } else if (error_ptr) +- error_ptr->SetErrorString("Stack frame does not include a canonical " +- "frame address for DW_OP_call_frame_cfa " +- "opcode."); +- } else { +- if (error_ptr) +- error_ptr->SetErrorString("Invalid stack frame in context for " +- "DW_OP_call_frame_cfa opcode."); +- return false; +- } +- break; +- +- // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension +- // opcode, DW_OP_GNU_push_tls_address) +- // OPERANDS: none +- // DESCRIPTION: Pops a TLS offset from the stack, converts it to +- // an address in the current thread's thread-local storage block, and +- // pushes it on the stack. +- case DW_OP_form_tls_address: +- case DW_OP_GNU_push_tls_address: { +- if (stack.size() < 1) { +- if (error_ptr) { +- if (op == DW_OP_form_tls_address) +- error_ptr->SetErrorString( +- "DW_OP_form_tls_address needs an argument."); +- else +- error_ptr->SetErrorString( +- "DW_OP_GNU_push_tls_address needs an argument."); +- } +- return false; +- } +- +- if (!exe_ctx || !module_sp) { +- if (error_ptr) +- error_ptr->SetErrorString("No context to evaluate TLS within."); +- return false; +- } +- +- Thread *thread = exe_ctx->GetThreadPtr(); +- if (!thread) { +- if (error_ptr) +- error_ptr->SetErrorString("No thread to evaluate TLS within."); +- return false; +- } +- +- // Lookup the TLS block address for this thread and module. +- const addr_t tls_file_addr = +- stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS); +- const addr_t tls_load_addr = +- thread->GetThreadLocalData(module_sp, tls_file_addr); +- +- if (tls_load_addr == LLDB_INVALID_ADDRESS) { +- if (error_ptr) +- error_ptr->SetErrorString( +- "No TLS data currently exists for this thread."); +- return false; +- } +- +- stack.back().GetScalar() = tls_load_addr; +- stack.back().SetValueType(Value::ValueType::LoadAddress); +- } break; +- +- // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.) +- // OPERANDS: 1 +- // ULEB128: index to the .debug_addr section +- // DESCRIPTION: Pushes an address to the stack from the .debug_addr +- // section with the base address specified by the DW_AT_addr_base attribute +- // and the 0 based index is the ULEB128 encoded index. +- case DW_OP_addrx: +- case DW_OP_GNU_addr_index: { +- if (!dwarf_cu) { +- if (error_ptr) +- error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a " +- "compile unit being specified"); +- return false; +- } +- uint64_t index = opcodes.GetULEB128(&offset); +- lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); +- stack.push_back(Scalar(value)); +- stack.back().SetValueType(Value::ValueType::FileAddress); +- } break; +- +- // OPCODE: DW_OP_GNU_const_index +- // OPERANDS: 1 +- // ULEB128: index to the .debug_addr section +- // DESCRIPTION: Pushes an constant with the size of a machine address to +- // the stack from the .debug_addr section with the base address specified +- // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128 +- // encoded index. +- case DW_OP_GNU_const_index: { +- if (!dwarf_cu) { +- if (error_ptr) +- error_ptr->SetErrorString("DW_OP_GNU_const_index found without a " +- "compile unit being specified"); +- return false; +- } +- uint64_t index = opcodes.GetULEB128(&offset); +- lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index); +- stack.push_back(Scalar(value)); +- } break; +- +- case DW_OP_GNU_entry_value: +- case DW_OP_entry_value: { +- if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset, +- error_ptr, log)) { +- LLDB_ERRORF(error_ptr, "Could not evaluate %s.", +- DW_OP_value_to_name(op)); +- return false; +- } +- break; +- } +- +- default: +- if (error_ptr) +- error_ptr->SetErrorStringWithFormatv( +- "Unhandled opcode {0} in DWARFExpression", LocationAtom(op)); +- return false; +- } +- } +- +- if (stack.empty()) { +- // Nothing on the stack, check if we created a piece value from DW_OP_piece +- // or DW_OP_bit_piece opcodes +- if (pieces.GetBuffer().GetByteSize()) { +- result = pieces; +- return true; +- } +- if (error_ptr) +- error_ptr->SetErrorString("Stack empty after evaluation."); +- return false; +- } +- +- UpdateValueTypeFromLocationDescription( +- log, dwarf_cu, dwarf4_location_description_kind, &stack.back()); +- +- if (log && log->GetVerbose()) { +- size_t count = stack.size(); +- LLDB_LOGF(log, +- "Stack after operation has %" PRIu64 " values:", (uint64_t)count); +- for (size_t i = 0; i < count; ++i) { +- StreamString new_value; +- new_value.Printf("[%" PRIu64 "]", (uint64_t)i); +- stack[i].Dump(&new_value); +- LLDB_LOGF(log, " %s", new_value.GetData()); +- } +- } +- result = stack.back(); +- return true; // Return true on success ++ DWARFExpression expr(module_sp, opcodes, dwarf_cu); ++ expr.SetRegisterKind(reg_kind); ++ ++ // Use the DWARF expression evaluator registered for this module (or ++ // DWARFEvaluator by default). ++ DWARFEvaluatorFactory *evaluator_factory = ++ module_sp->GetDWARFExpressionEvaluatorFactory(); ++ std::unique_ptr evaluator = ++ evaluator_factory->CreateDWARFEvaluator( ++ expr, exe_ctx, reg_ctx, initial_value_ptr, object_address_ptr); ++ return evaluator->Evaluate(result, error_ptr); + } + + static DataExtractor ToDataExtractor(const llvm::DWARFLocationExpression &loc, +diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp +index 00e9ccb762c3..2137a1ac8324 100644 +--- a/lldb/source/Interpreter/CommandInterpreter.cpp ++++ b/lldb/source/Interpreter/CommandInterpreter.cpp +@@ -759,6 +759,24 @@ void CommandInterpreter::LoadCommandDictionary() { + } + } + ++ std::unique_ptr connect_wasm_cmd_up( ++ new CommandObjectRegexCommand( ++ *this, "wasm", ++ "Connect to a WebAssembly process via remote GDB server. " ++ "If no host is specifed, localhost is assumed.", ++ "wasm [:]", 2, 0, false)); ++ if (connect_wasm_cmd_up) { ++ if (connect_wasm_cmd_up->AddRegexCommand( ++ "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$", ++ "process connect --plugin wasm connect://%1:%2") && ++ connect_wasm_cmd_up->AddRegexCommand( ++ "^([[:digit:]]+)$", ++ "process connect --plugin wasm connect://localhost:%1")) { ++ CommandObjectSP command_sp(connect_wasm_cmd_up.release()); ++ m_command_dict[std::string(command_sp->GetCommandName())] = command_sp; ++ } ++ } ++ + std::unique_ptr connect_kdp_remote_cmd_up( + new CommandObjectRegexCommand( + *this, "kdp-remote", +diff --git a/lldb/source/Plugins/CMakeLists.txt b/lldb/source/Plugins/CMakeLists.txt +index 9181a4e47675..2be6ec3657c0 100644 +--- a/lldb/source/Plugins/CMakeLists.txt ++++ b/lldb/source/Plugins/CMakeLists.txt +@@ -2,6 +2,7 @@ add_subdirectory(ABI) + add_subdirectory(Architecture) + add_subdirectory(Disassembler) + add_subdirectory(DynamicLoader) ++add_subdirectory(DWARFEvaluator) + add_subdirectory(ExpressionParser) + add_subdirectory(Instruction) + add_subdirectory(InstrumentationRuntime) +@@ -32,6 +33,7 @@ set(LLDB_ENUM_PLUGINS "") + # FIXME: ProcessWindowsCommon needs to be initialized after all other process + # plugins but before ProcessGDBRemote. + set(LLDB_PROCESS_WINDOWS_PLUGIN "") ++set(LLDB_PROCESS_WASM_PLUGIN "") + set(LLDB_PROCESS_GDB_PLUGIN "") + + foreach(p ${LLDB_ALL_PLUGINS}) +@@ -43,6 +45,8 @@ foreach(p ${LLDB_ALL_PLUGINS}) + set(LLDB_PROCESS_WINDOWS_PLUGIN "LLDB_PLUGIN(${pStripped})\n") + elseif(${pStripped} STREQUAL "ProcessGDBRemote") + set(LLDB_PROCESS_GDB_PLUGIN "LLDB_PLUGIN(${pStripped})\n") ++ elseif(${pStripped} STREQUAL "ProcessWasm") ++ set(LLDB_PROCESS_WASM_PLUGIN "LLDB_PLUGIN(${pStripped})\n") + else() + set(LLDB_ENUM_PLUGINS "${LLDB_ENUM_PLUGINS}LLDB_PLUGIN(${pStripped})\n") + endif() +diff --git a/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt b/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt +new file mode 100644 +index 000000000000..73fad41e1a72 +--- /dev/null ++++ b/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt +@@ -0,0 +1 @@ ++add_subdirectory(wasm) +diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt b/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt +new file mode 100644 +index 000000000000..e50b1bef7e69 +--- /dev/null ++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt +@@ -0,0 +1,10 @@ ++add_lldb_library(lldbPluginWasmDWARFEvaluatorFactory PLUGIN ++ WasmDWARFEvaluator.cpp ++ WasmDWARFEvaluatorFactory.cpp ++ ++ LINK_LIBS ++ lldbCore ++ lldbHost ++ lldbSymbol ++ lldbPluginObjectFileWasm ++ ) +diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp +new file mode 100644 +index 000000000000..fdda1991d19f +--- /dev/null ++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp +@@ -0,0 +1,126 @@ ++//===-- WasmDWARFEvaluator.cpp --------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "WasmDWARFEvaluator.h" ++ ++#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" ++#include "Plugins/Process/wasm/ProcessWasm.h" ++#include "lldb/Core/Module.h" ++#include "lldb/Core/PluginManager.h" ++#include "lldb/Core/Value.h" ++#include "lldb/Core/dwarf.h" ++#include "lldb/Expression/DWARFExpression.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++using namespace lldb_private::wasm; ++ ++bool WasmDWARFEvaluator::Evaluate(const uint8_t op, Process *process, ++ StackFrame *frame, std::vector &stack, ++ const DataExtractor &opcodes, ++ lldb::offset_t &offset, Value &pieces, ++ uint64_t &op_piece_offset, Log *log, ++ Status *error_ptr) { ++ lldb::ModuleSP module_sp = m_dwarf_expression.GetModule(); ++ ++ switch (op) { ++ case DW_OP_WASM_location: { ++ if (frame) { ++ const llvm::Triple::ArchType machine = ++ frame->CalculateTarget()->GetArchitecture().GetMachine(); ++ if (machine != llvm::Triple::wasm32) { ++ if (error_ptr) ++ error_ptr->SetErrorString("Invalid target architecture for " ++ "DW_OP_WASM_location opcode."); ++ return false; ++ } ++ ++ ProcessWasm *wasm_process = ++ static_cast(frame->CalculateProcess().get()); ++ int frame_index = frame->GetConcreteFrameIndex(); ++ uint64_t wasm_op = opcodes.GetULEB128(&offset); ++ uint64_t index = opcodes.GetULEB128(&offset); ++ uint8_t buf[16]; ++ size_t size = 0; ++ switch (wasm_op) { ++ case 0: // Local ++ if (!wasm_process->GetWasmLocal(frame_index, index, buf, 16, size)) { ++ return false; ++ } ++ break; ++ case 1: // Global ++ if (!wasm_process->GetWasmGlobal(frame_index, index, buf, 16, size)) { ++ return false; ++ } ++ break; ++ case 2: // Operand Stack ++ if (!wasm_process->GetWasmStackValue(frame_index, index, buf, 16, ++ size)) { ++ return false; ++ } ++ break; ++ default: ++ return false; ++ } ++ ++ if (size == sizeof(uint32_t)) { ++ uint32_t value; ++ memcpy(&value, buf, size); ++ stack.push_back(Scalar(value)); ++ } else if (size == sizeof(uint64_t)) { ++ uint64_t value; ++ memcpy(&value, buf, size); ++ stack.push_back(Scalar(value)); ++ } else ++ return false; ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorString("Invalid stack frame in context for " ++ "DW_OP_WASM_location opcode."); ++ return false; ++ } ++ } break; ++ ++ case DW_OP_addr: { ++ /// {addr} is an offset in the module Data section. ++ lldb::addr_t addr = opcodes.GetAddress(&offset); ++ stack.push_back(Scalar(addr)); ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } break; ++ ++ case DW_OP_fbreg: ++ if (m_exe_ctx) { ++ if (frame) { ++ Scalar value; ++ if (frame->GetFrameBaseValue(value, error_ptr)) { ++ // The value is an address in the Wasm Memory space. ++ int64_t fbreg_offset = opcodes.GetSLEB128(&offset); ++ stack.push_back(Scalar(value.ULong() + fbreg_offset)); ++ stack.back().SetValueType(Value::ValueType::LoadAddress); ++ } else ++ return false; ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorString( ++ "Invalid stack frame in context for DW_OP_fbreg opcode."); ++ return false; ++ } ++ } else { ++ if (error_ptr) ++ error_ptr->SetErrorStringWithFormat( ++ "NULL execution context for DW_OP_fbreg.\n"); ++ return false; ++ } ++ break; ++ ++ default: ++ return DWARFEvaluator::Evaluate(op, process, frame, stack, opcodes, offset, ++ pieces, op_piece_offset, log, error_ptr); ++ } ++ return true; ++} +diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h +new file mode 100644 +index 000000000000..a01159064a39 +--- /dev/null ++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h +@@ -0,0 +1,47 @@ ++//===-- WasmDWARFEvaluator.h ------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H ++#define LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H ++ ++#include "lldb/Expression/DWARFEvaluator.h" ++#include "lldb/lldb-private.h" ++ ++namespace lldb_private { ++namespace wasm { ++ ++/// \class WasmDWARFEvaluator evaluates DWARF expressions in the context of a ++/// WebAssembly process. ++/// ++class WasmDWARFEvaluator : public DWARFEvaluator { ++public: ++ WasmDWARFEvaluator(const DWARFExpression &dwarf_expression, ++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx, ++ const Value *initial_value_ptr, ++ const Value *object_address_ptr) ++ : DWARFEvaluator(dwarf_expression, exe_ctx, reg_ctx, initial_value_ptr, ++ object_address_ptr) {} ++ ++ /// DWARFEvaluator protocol. ++ /// \{ ++ bool Evaluate(const uint8_t op, Process *process, StackFrame *frame, ++ std::vector &stack, const DataExtractor &opcodes, ++ lldb::offset_t &offset, Value &pieces, ++ uint64_t &op_piece_offset, Log *log, ++ Status *error_ptr) override; ++ /// \} ++ ++private: ++ WasmDWARFEvaluator(const WasmDWARFEvaluator &); ++ const WasmDWARFEvaluator &operator=(const WasmDWARFEvaluator &) = delete; ++}; ++ ++} // namespace wasm ++} // namespace lldb_private ++ ++#endif // LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H +diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.cpp b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.cpp +new file mode 100644 +index 000000000000..d43e96a34d37 +--- /dev/null ++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.cpp +@@ -0,0 +1,64 @@ ++//===-- WasmDWARFEvaluatorFactory.cpp -------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "WasmDWARFEvaluatorFactory.h" ++#include "WasmDWARFEvaluator.h" ++ ++#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h" ++#include "lldb/Core/Module.h" ++#include "lldb/Core/PluginManager.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++using namespace lldb_private::wasm; ++ ++LLDB_PLUGIN_DEFINE(WasmDWARFEvaluatorFactory) ++ ++void WasmDWARFEvaluatorFactory::Initialize() { ++ PluginManager::RegisterPlugin(GetPluginNameStatic(), ++ GetPluginDescriptionStatic(), CreateInstance); ++} ++ ++void WasmDWARFEvaluatorFactory::Terminate() { ++ PluginManager::UnregisterPlugin(CreateInstance); ++} ++ ++lldb_private::ConstString WasmDWARFEvaluatorFactory::GetPluginNameStatic() { ++ static ConstString g_name("WASM"); ++ return g_name; ++} ++ ++const char *WasmDWARFEvaluatorFactory::GetPluginDescriptionStatic() { ++ return "DWARF expression evaluator factory for WASM."; ++} ++ ++// CreateInstance ++// ++// Platforms can register a callback to use when creating DWARF expression ++// evaluators to allow handling platform-specific DWARF codes. ++DWARFEvaluatorFactory * ++WasmDWARFEvaluatorFactory::CreateInstance(Module *module) { ++ if (!module) ++ return nullptr; ++ ++ ObjectFileWasm *obj_file = ++ llvm::dyn_cast_or_null(module->GetObjectFile()); ++ if (!obj_file) ++ return nullptr; ++ ++ return new WasmDWARFEvaluatorFactory(); ++} ++ ++std::unique_ptr WasmDWARFEvaluatorFactory::CreateDWARFEvaluator( ++ const DWARFExpression &dwarf_expression, ExecutionContext *exe_ctx, ++ RegisterContext *reg_ctx, const Value *initial_value_ptr, ++ const Value *object_address_ptr) { ++ return std::make_unique(dwarf_expression, exe_ctx, ++ reg_ctx, initial_value_ptr, ++ object_address_ptr); ++} +diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.h b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.h +new file mode 100644 +index 000000000000..8a946592a09a +--- /dev/null ++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.h +@@ -0,0 +1,55 @@ ++//===-- WasmDWARFEvaluatorFactory.h -----------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATORFACTORY_H ++#define LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATORFACTORY_H ++ ++#include "lldb/Expression/DWARFEvaluatorFactory.h" ++ ++namespace lldb_private { ++namespace wasm { ++ ++/// \class WasmDWARFEvaluatorFactory creates DWARF evaluators specialized to ++/// manage DWARF-specific opcodes. ++class WasmDWARFEvaluatorFactory : public DWARFEvaluatorFactory { ++public: ++ static void Initialize(); ++ static void Terminate(); ++ static lldb_private::ConstString GetPluginNameStatic(); ++ static const char *GetPluginDescriptionStatic(); ++ ++ static lldb_private::DWARFEvaluatorFactory *CreateInstance(Module *module); ++ ++ /// PluginInterface protocol. ++ /// \{ ++ lldb_private::ConstString GetPluginName() override { ++ return GetPluginNameStatic(); ++ } ++ uint32_t GetPluginVersion() override { return 1; } ++ /// \} ++ ++ WasmDWARFEvaluatorFactory() {} ++ ++ /// DWARFEvaluatorFactory protocol. ++ /// \{ ++ std::unique_ptr ++ CreateDWARFEvaluator(const DWARFExpression &dwarf_expression, ++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx, ++ const Value *initial_value_ptr, ++ const Value *object_address_ptr) override; ++ /// \} ++ ++private: ++ WasmDWARFEvaluatorFactory(const WasmDWARFEvaluatorFactory &); ++ const WasmDWARFEvaluatorFactory &operator=(const WasmDWARFEvaluatorFactory &) = delete; ++}; ++ ++} // namespace wasm ++} // namespace lldb_private ++ ++#endif // LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATORFACTORY_H +diff --git a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp +index ae7e011eaa52..24ea75d1971c 100644 +--- a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp ++++ b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp +@@ -62,6 +62,15 @@ void DynamicLoaderWasmDYLD::DidAttach() { + // Ask the process for the list of loaded WebAssembly modules. + auto error = m_process->LoadModules(); + LLDB_LOG_ERROR(log, std::move(error), "Couldn't load modules: {0}"); ++ ++ // TODO: multi-modules support ? ++ Target &target = m_process->GetTarget(); ++ const ModuleList &modules = target.GetImages(); ++ ModuleSP module_sp(modules.GetModuleAtIndex(0)); ++ // module_sp is nullptr if without libxml2 ++ if(module_sp) { ++ module_sp->PreloadSymbols(); ++ } + } + + ThreadPlanSP DynamicLoaderWasmDYLD::GetStepThroughTrampolinePlan(Thread &thread, +diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +index 5272da9ab33a..abc5523bfd70 100644 +--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp ++++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +@@ -23,6 +23,7 @@ + #include "llvm/BinaryFormat/Wasm.h" + #include "llvm/Support/Endian.h" + #include "llvm/Support/Format.h" ++#include "Plugins/Process/wasm/ProcessWasm.h" + + using namespace lldb; + using namespace lldb_private; +@@ -341,6 +342,8 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { + 0, // Alignment of the section + 0, // Flags for this section. + 1)); // Number of host bytes per target byte ++ if (section_type == eSectionTypeCode) ++ section_sp->SetPermissions(ePermissionsReadable|ePermissionsExecutable); + m_sections_up->AddSection(section_sp); + unified_section_list.AddSection(section_sp); + } +@@ -367,6 +370,7 @@ bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address, + assert(m_memory_addr == LLDB_INVALID_ADDRESS || + m_memory_addr == load_address); + ++ lldb::addr_t adjust_addr; + ModuleSP module_sp = GetModule(); + if (!module_sp) + return false; +@@ -381,8 +385,9 @@ bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address, + const size_t num_sections = section_list->GetSize(); + for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) { + SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx)); ++ adjust_addr = load_address; + if (target.SetSectionLoadAddress( +- section_sp, load_address | section_sp->GetFileOffset())) { ++ section_sp, adjust_addr | section_sp->GetFileOffset())) { + ++num_loaded_sections; + } + } +diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt +index 5f284e517dca..6084cbc9378d 100644 +--- a/lldb/source/Plugins/Platform/CMakeLists.txt ++++ b/lldb/source/Plugins/Platform/CMakeLists.txt +@@ -15,3 +15,4 @@ + add_subdirectory(POSIX) + add_subdirectory(gdb-server) + add_subdirectory(Android) ++add_subdirectory(wasm-remote) +diff --git a/lldb/source/Plugins/Platform/wasm-remote/CMakeLists.txt b/lldb/source/Plugins/Platform/wasm-remote/CMakeLists.txt +new file mode 100644 +index 000000000000..4a65765a5659 +--- /dev/null ++++ b/lldb/source/Plugins/Platform/wasm-remote/CMakeLists.txt +@@ -0,0 +1,10 @@ ++add_lldb_library(lldbPluginPlatformWasm PLUGIN ++ PlatformRemoteWasmServer.cpp ++ ++ LINK_LIBS ++ lldbBreakpoint ++ lldbCore ++ lldbHost ++ lldbTarget ++ lldbPluginProcessUtility ++ ) +diff --git a/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.cpp b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.cpp +new file mode 100644 +index 000000000000..f26d11f00e5c +--- /dev/null ++++ b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.cpp +@@ -0,0 +1,139 @@ ++#include "PlatformRemoteWasmServer.h" ++#include "lldb/Host/Config.h" ++ ++#include "lldb/Breakpoint/BreakpointLocation.h" ++#include "lldb/Core/Debugger.h" ++#include "lldb/Core/Module.h" ++#include "lldb/Core/ModuleList.h" ++#include "lldb/Core/ModuleSpec.h" ++#include "lldb/Core/PluginManager.h" ++#include "lldb/Core/StreamFile.h" ++#include "lldb/Host/ConnectionFileDescriptor.h" ++#include "lldb/Host/Host.h" ++#include "lldb/Host/HostInfo.h" ++#include "lldb/Host/PosixApi.h" ++#include "lldb/Target/Process.h" ++#include "lldb/Target/Target.h" ++#include "lldb/Utility/FileSpec.h" ++#include "lldb/Utility/Log.h" ++#include "lldb/Utility/ProcessInfo.h" ++#include "lldb/Utility/Status.h" ++#include "lldb/Utility/StreamString.h" ++#include "lldb/Utility/UriParser.h" ++ ++#include "Plugins/Process/Utility/GDBRemoteSignals.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++using namespace lldb_private::platform_wasm_server; ++ ++LLDB_PLUGIN_DEFINE_ADV(PlatformRemoteWASMServer, PlatformWasm) ++ ++static bool g_initialized = false; ++ ++void PlatformRemoteWASMServer::Initialize() { ++ Platform::Initialize(); ++ ++ if (!g_initialized) { ++ g_initialized = true; ++ PluginManager::RegisterPlugin( ++ PlatformRemoteWASMServer::GetPluginNameStatic(), ++ PlatformRemoteWASMServer::GetDescriptionStatic(), ++ PlatformRemoteWASMServer::CreateInstance); ++ } ++} ++ ++void PlatformRemoteWASMServer::Terminate() { ++ if (g_initialized) { ++ g_initialized = false; ++ PluginManager::UnregisterPlugin(PlatformRemoteWASMServer::CreateInstance); ++ } ++ ++ Platform::Terminate(); ++} ++ ++PlatformSP PlatformRemoteWASMServer::CreateInstance(bool force, ++ const ArchSpec *arch) { ++ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM)); ++ if (log) { ++ const char *arch_name; ++ if (arch && arch->GetArchitectureName()) ++ arch_name = arch->GetArchitectureName(); ++ else ++ arch_name = ""; ++ ++ const char *triple_cstr = ++ arch ? arch->GetTriple().getTriple().c_str() : ""; ++ ++ LLDB_LOGF(log, "PlatformRemoteWASMServer::%s(force=%s, arch={%s,%s})", ++ __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr); ++ } ++ ++ bool create = force; ++ if (!create && arch && arch->IsValid()) { ++ const llvm::Triple &triple = arch->GetTriple(); ++ if (arch->GetMachine() == llvm::Triple::wasm32 && ++ triple.getOS() == llvm::Triple::WASI) { ++ create = true; ++ } ++ } ++ ++ if (create) { ++ if (log) ++ LLDB_LOGF(log, "PlatformRemoteWASMServer::%s() creating platform", ++ __FUNCTION__); ++ return PlatformSP(new PlatformRemoteWASMServer()); ++ } ++ ++ if (log) ++ LLDB_LOGF(log, ++ "PlatformRemoteWASMServer::%s() aborting creation of platform", ++ __FUNCTION__); ++ return PlatformSP(); ++} ++ ++ConstString PlatformRemoteWASMServer::GetPluginNameStatic() { ++ static ConstString g_name("remote-wasm-server"); ++ return g_name; ++} ++ ++ConstString PlatformRemoteWASMServer::GetPluginName() { ++ return GetPluginNameStatic(); ++} ++ ++const char *PlatformRemoteWASMServer::GetDescriptionStatic() { ++ return "A platform that uses the GDB remote protocol as the communication " ++ "transport for Wasm Runtime"; ++} ++ ++size_t PlatformRemoteWASMServer::ConnectToWaitingProcesses(Debugger &debugger, ++ Status &error) { ++ std::vector connection_urls; ++ GetPendingGdbServerList(connection_urls); ++ ++ for (size_t i = 0; i < connection_urls.size(); ++i) { ++ ConnectProcess(connection_urls[i].c_str(), "wasm", debugger, nullptr, error); ++ if (error.Fail()) ++ return i; // We already connected to i process succsessfully ++ } ++ return connection_urls.size(); ++} ++ ++bool PlatformRemoteWASMServer::GetSupportedArchitectureAtIndex(uint32_t idx, ++ ArchSpec &arch) { ++ ArchSpec remote_arch = m_gdb_client.GetSystemArchitecture(); ++ if (idx == 0) { ++ arch = remote_arch; ++ return arch.IsValid(); ++ } else if (idx == 1 && remote_arch.IsValid() && ++ remote_arch.GetTriple().getOS() == llvm::Triple::WASI) { ++ return arch.IsValid(); ++ } ++ return false; ++} ++ ++/// Default Constructor ++PlatformRemoteWASMServer::PlatformRemoteWASMServer() ++ : PlatformRemoteGDBServer() ++ { ++ } +\ No newline at end of file +diff --git a/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.h b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.h +new file mode 100644 +index 000000000000..f306a79d3f4f +--- /dev/null ++++ b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.h +@@ -0,0 +1,37 @@ ++#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEWASMSERVER_H ++#define LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEWASMSERVER_H ++ ++#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h" ++#include "lldb/Target/Platform.h" ++ ++namespace lldb_private { ++namespace platform_wasm_server { ++ ++class PlatformRemoteWASMServer : public lldb_private::platform_gdb_server::PlatformRemoteGDBServer{ ++ ++public: ++ static void Initialize(); ++ ++ static void Terminate(); ++ ++ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch); ++ ++ static ConstString GetPluginNameStatic(); ++ ++ static const char *GetDescriptionStatic(); ++ ++ size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger, ++ lldb_private::Status &error) override; ++ ++ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override; ++ ++ ConstString GetPluginName() override; ++ ++ PlatformRemoteWASMServer(); ++ ++}; ++ ++} // namespace platform_wasm_server ++} // namespace lldb_private ++ ++#endif +\ No newline at end of file +diff --git a/lldb/source/Plugins/Plugins.def.in b/lldb/source/Plugins/Plugins.def.in +index bf54598fb2f3..b0bd7b9965fe 100644 +--- a/lldb/source/Plugins/Plugins.def.in ++++ b/lldb/source/Plugins/Plugins.def.in +@@ -31,6 +31,7 @@ + + @LLDB_ENUM_PLUGINS@ + @LLDB_PROCESS_WINDOWS_PLUGIN@ ++@LLDB_PROCESS_WASM_PLUGIN@ + @LLDB_PROCESS_GDB_PLUGIN@ + + #undef LLDB_PLUGIN +diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt +index bea5bac9eb21..7a0855e02ca2 100644 +--- a/lldb/source/Plugins/Process/CMakeLists.txt ++++ b/lldb/source/Plugins/Process/CMakeLists.txt +@@ -18,3 +18,4 @@ add_subdirectory(Utility) + add_subdirectory(elf-core) + add_subdirectory(mach-core) + add_subdirectory(minidump) ++add_subdirectory(wasm) +diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +index 12bc7390c729..707ab85e5615 100644 +--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp ++++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +@@ -285,7 +285,7 @@ bool ProcessElfCore::IsAlive() { return true; } + + // Process Memory + size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size, +- Status &error) { ++ Status &error, ExecutionContext *exe_ctx) { + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h +index d8e3cc9ae3e1..f0bf9c4d3b00 100644 +--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h ++++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h +@@ -84,7 +84,8 @@ public: + + // Process Memory + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, +- lldb_private::Status &error) override; ++ lldb_private::Status &error, ++ lldb_private::ExecutionContext *exe_ctx = nullptr) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; +diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +index 6914b37348ea..bb8a056049f3 100644 +--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp ++++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +@@ -334,6 +334,11 @@ ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); } + + uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; } + ++std::shared_ptr ++ProcessGDBRemote::CreateThread(lldb::tid_t tid) { ++ return std::make_shared(*this, tid); ++} ++ + bool ProcessGDBRemote::ParsePythonTargetDefinition( + const FileSpec &target_definition_fspec) { + ScriptInterpreter *interpreter = +@@ -1626,7 +1631,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list, + ThreadSP thread_sp( + old_thread_list_copy.RemoveThreadByProtocolID(tid, false)); + if (!thread_sp) { +- thread_sp = std::make_shared(*this, tid); ++ thread_sp = CreateThread(tid); + LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.", + thread_sp.get(), thread_sp->GetID()); + } else { +@@ -1742,7 +1747,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo( + + if (!thread_sp) { + // Create the thread if we need to +- thread_sp = std::make_shared(*this, tid); ++ thread_sp = CreateThread(tid); + m_thread_list_real.AddThread(thread_sp); + } + } +diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +index fe04cdddd0f5..e4a14c64579a 100644 +--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h ++++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +@@ -237,6 +237,8 @@ protected: + + bool SupportsMemoryTagging() override; + ++ virtual std::shared_ptr CreateThread(lldb::tid_t tid); ++ + /// Broadcaster event bits definitions. + enum { + eBroadcastBitAsyncContinue = (1 << 0), +diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +index 84548edb5caa..0ae6f7e4a177 100644 +--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp ++++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +@@ -596,7 +596,7 @@ bool ProcessMachCore::WarnBeforeDetach() const { return false; } + + // Process Memory + size_t ProcessMachCore::ReadMemory(addr_t addr, void *buf, size_t size, +- Status &error) { ++ Status &error, ExecutionContext *exe_ctx) { + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // in core files we have it all cached our our core file anyway. + return DoReadMemory(addr, buf, size, error); +diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h +index db77e96f1072..1c930896c743 100644 +--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h ++++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h +@@ -65,7 +65,8 @@ public: + + // Process Memory + size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, +- lldb_private::Status &error) override; ++ lldb_private::Status &error, ++ lldb_private::ExecutionContext *exe_ctx = nullptr) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + lldb_private::Status &error) override; +diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp +index 385557422758..d8bb21581086 100644 +--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp ++++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp +@@ -374,7 +374,7 @@ bool ProcessMinidump::IsAlive() { return true; } + bool ProcessMinidump::WarnBeforeDetach() const { return false; } + + size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size, +- Status &error) { ++ Status &error, ExecutionContext *exe_ctx) { + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // we have it all cached in our dump file anyway. + return DoReadMemory(addr, buf, size, error); +diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h +index 27b0da0047a5..e94ecab430c1 100644 +--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h ++++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h +@@ -69,8 +69,8 @@ public: + + bool WarnBeforeDetach() const override; + +- size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, +- Status &error) override; ++ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error, ++ ExecutionContext *exe_ctx = nullptr) override; + + size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, + Status &error) override; +diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt +new file mode 100644 +index 000000000000..61efb933fa62 +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt +@@ -0,0 +1,12 @@ ++ ++add_lldb_library(lldbPluginProcessWasm PLUGIN ++ ProcessWasm.cpp ++ ThreadWasm.cpp ++ UnwindWasm.cpp ++ ++ LINK_LIBS ++ lldbCore ++ ${LLDB_PLUGINS} ++ LINK_COMPONENTS ++ Support ++ ) +diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp +new file mode 100644 +index 000000000000..9c0fc7b7f270 +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp +@@ -0,0 +1,261 @@ ++//===-- ProcessWasm.cpp ---------------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "ProcessWasm.h" ++#include "ThreadWasm.h" ++#include "lldb/Core/Module.h" ++#include "lldb/Core/PluginManager.h" ++#include "lldb/Utility/DataBufferHeap.h" ++ ++#include "lldb/Target/UnixSignals.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++using namespace lldb_private::process_gdb_remote; ++using namespace lldb_private::wasm; ++ ++LLDB_PLUGIN_DEFINE(ProcessWasm) ++ ++// ProcessGDBRemote constructor ++ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp) ++ : ProcessGDBRemote(target_sp, listener_sp) { ++ /* always use linux signals for wasm process */ ++ m_unix_signals_sp = UnixSignals::Create(ArchSpec{"wasm32-Ant-wasi-wasm"}); ++} ++ ++void ProcessWasm::Initialize() { ++ static llvm::once_flag g_once_flag; ++ ++ llvm::call_once(g_once_flag, []() { ++ PluginManager::RegisterPlugin(GetPluginNameStatic(), ++ GetPluginDescriptionStatic(), CreateInstance, ++ DebuggerInitialize); ++ }); ++} ++ ++void ProcessWasm::DebuggerInitialize(Debugger &debugger) { ++ ProcessGDBRemote::DebuggerInitialize(debugger); ++} ++ ++// PluginInterface ++ConstString ProcessWasm::GetPluginName() { return GetPluginNameStatic(); } ++ ++uint32_t ProcessWasm::GetPluginVersion() { return 1; } ++ ++ConstString ProcessWasm::GetPluginNameStatic() { ++ static ConstString g_name("wasm"); ++ return g_name; ++} ++ ++const char *ProcessWasm::GetPluginDescriptionStatic() { ++ return "GDB Remote protocol based WebAssembly debugging plug-in."; ++} ++ ++void ProcessWasm::Terminate() { ++ PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance); ++} ++ ++lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp, ++ ListenerSP listener_sp, ++ const FileSpec *crash_file_path, ++ bool can_connect) { ++ lldb::ProcessSP process_sp; ++ if (crash_file_path == nullptr) ++ process_sp = std::make_shared(target_sp, listener_sp); ++ return process_sp; ++} ++ ++bool ProcessWasm::CanDebug(lldb::TargetSP target_sp, ++ bool plugin_specified_by_name) { ++ if (plugin_specified_by_name) ++ return true; ++ ++ Module *exe_module = target_sp->GetExecutableModulePointer(); ++ if (exe_module) { ++ ObjectFile *exe_objfile = exe_module->GetObjectFile(); ++ return exe_objfile->GetArchitecture().GetMachine() == llvm::Triple::wasm32; ++ } ++ // However, if there is no wasm module, we return false, otherwise, ++ // we might use ProcessWasm to attach gdb remote. ++ return false; ++} ++ ++ ++ ++std::shared_ptr ProcessWasm::CreateThread(lldb::tid_t tid) { ++ return std::make_shared(*this, tid); ++} ++ ++size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, ++ Status &error, ExecutionContext *exe_ctx) { ++ wasm_addr_t wasm_addr(vm_addr); ++ size_t nread = 0; ++ ++ switch (wasm_addr.GetType()) { ++ case WasmAddressType::Memory: ++ case WasmAddressType::Object: ++ return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); ++ case WasmAddressType::Invalid: ++ default: ++ error.SetErrorStringWithFormat( ++ "Wasm read failed for invalid address 0x%" PRIx64, vm_addr); ++ return 0; ++ } ++} ++ ++size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, ++ void *buf, size_t buffer_size) { ++ char packet[64]; ++ int packet_len = ++ ::snprintf(packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64, ++ wasm_module_id, static_cast(addr), ++ static_cast(buffer_size)); ++ assert(packet_len + 1 < (int)sizeof(packet)); ++ UNUSED_IF_ASSERT_DISABLED(packet_len); ++ StringExtractorGDBRemote response; ++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) == ++ GDBRemoteCommunication::PacketResult::Success) { ++ if (response.IsNormalResponse()) { ++ return response.GetHexBytes(llvm::MutableArrayRef( ++ static_cast(buf), buffer_size), ++ '\xdd'); ++ } ++ } ++ return 0; ++} ++ ++size_t ProcessWasm::WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr, ++ void *buf, size_t buffer_size) { ++ char packet[64]; ++ int packet_len = ++ ::snprintf(packet, sizeof(packet), "qWasmData:%d;%" PRIx64 ";%" PRIx64, ++ wasm_module_id, static_cast(addr), ++ static_cast(buffer_size)); ++ assert(packet_len + 1 < (int)sizeof(packet)); ++ UNUSED_IF_ASSERT_DISABLED(packet_len); ++ StringExtractorGDBRemote response; ++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) == ++ GDBRemoteCommunication::PacketResult::Success) { ++ if (response.IsNormalResponse()) { ++ return response.GetHexBytes(llvm::MutableArrayRef( ++ static_cast(buf), buffer_size), ++ '\xdd'); ++ } ++ } ++ return 0; ++} ++ ++bool ProcessWasm::GetWasmLocal(int frame_index, int index, void *buf, ++ size_t buffer_size, size_t &size) { ++ StreamString packet; ++ packet.Printf("qWasmLocal:"); ++ packet.Printf("%d;%d", frame_index, index); ++ StringExtractorGDBRemote response; ++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != ++ GDBRemoteCommunication::PacketResult::Success) { ++ return false; ++ } ++ ++ if (!response.IsNormalResponse()) { ++ return false; ++ } ++ ++ DataBufferSP buffer_sp( ++ new DataBufferHeap(response.GetStringRef().size() / 2, 0)); ++ response.GetHexBytes(buffer_sp->GetData(), '\xcc'); ++ size = buffer_sp->GetByteSize(); ++ if (size <= buffer_size) { ++ memcpy(buf, buffer_sp->GetBytes(), size); ++ return true; ++ } ++ ++ return false; ++} ++ ++bool ProcessWasm::GetWasmGlobal(int frame_index, int index, void *buf, ++ size_t buffer_size, size_t &size) { ++ StreamString packet; ++ packet.PutCString("qWasmGlobal:"); ++ packet.Printf("%d;%d", frame_index, index); ++ StringExtractorGDBRemote response; ++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != ++ GDBRemoteCommunication::PacketResult::Success) { ++ return false; ++ } ++ ++ if (!response.IsNormalResponse()) { ++ return false; ++ } ++ ++ DataBufferSP buffer_sp( ++ new DataBufferHeap(response.GetStringRef().size() / 2, 0)); ++ response.GetHexBytes(buffer_sp->GetData(), '\xcc'); ++ size = buffer_sp->GetByteSize(); ++ if (size <= buffer_size) { ++ memcpy(buf, buffer_sp->GetBytes(), size); ++ return true; ++ } ++ ++ return false; ++} ++ ++bool ProcessWasm::GetWasmStackValue(int frame_index, int index, void *buf, ++ size_t buffer_size, size_t &size) { ++ StreamString packet; ++ packet.PutCString("qWasmStackValue:"); ++ packet.Printf("%d;%d", frame_index, index); ++ StringExtractorGDBRemote response; ++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != ++ GDBRemoteCommunication::PacketResult::Success) { ++ return false; ++ } ++ ++ if (!response.IsNormalResponse()) { ++ return false; ++ } ++ ++ DataBufferSP buffer_sp( ++ new DataBufferHeap(response.GetStringRef().size() / 2, 0)); ++ response.GetHexBytes(buffer_sp->GetData(), '\xcc'); ++ size = buffer_sp->GetByteSize(); ++ if (size <= buffer_size) { ++ memcpy(buf, buffer_sp->GetBytes(), size); ++ return true; ++ } ++ ++ return false; ++} ++ ++bool ProcessWasm::GetWasmCallStack(lldb::tid_t tid, ++ std::vector &call_stack_pcs) { ++ call_stack_pcs.clear(); ++ StreamString packet; ++ packet.Printf("qWasmCallStack:"); ++ packet.Printf("%llx", tid); ++ StringExtractorGDBRemote response; ++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != ++ GDBRemoteCommunication::PacketResult::Success) { ++ return false; ++ } ++ ++ if (!response.IsNormalResponse()) { ++ return false; ++ } ++ ++ addr_t buf[1024 / sizeof(addr_t)]; ++ size_t bytes = response.GetHexBytes( ++ llvm::MutableArrayRef((uint8_t *)buf, sizeof(buf)), '\xdd'); ++ if (bytes == 0) { ++ return false; ++ } ++ ++ for (size_t i = 0; i < bytes / sizeof(addr_t); i++) { ++ call_stack_pcs.push_back(buf[i]); ++ } ++ return true; ++} +diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h +new file mode 100644 +index 000000000000..d3aece7a6554 +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h +@@ -0,0 +1,128 @@ ++//===-- ProcessWasm.h -------------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H ++#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H ++ ++#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" ++#include "lldb/Target/RegisterContext.h" ++ ++namespace lldb_private { ++namespace wasm { ++ ++// Each WebAssembly module has separated address spaces for Code and Memory. ++// A WebAssembly module also has a Data section which, when the module is ++// loaded, gets mapped into a region in the module Memory. ++// For the purpose of debugging, we can represent all these separated 32-bit ++// address spaces with a single virtual 64-bit address space. ++// ++// Struct wasm_addr_t provides this encoding using bitfields ++// ++enum WasmAddressType { ++ Memory = 0x00, ++ Object = 0x01, ++ Invalid = 0x03 ++}; ++struct wasm_addr_t { ++ uint64_t offset : 32; ++ uint64_t module_id : 30; ++ uint64_t type : 2; ++ ++ wasm_addr_t(lldb::addr_t addr) ++ : type(addr >> 62), module_id((addr & 0x00ffffff00000000) >> 32), ++ offset(addr & 0x00000000ffffffff) {} ++ ++ wasm_addr_t(WasmAddressType type_, uint32_t module_id_, uint32_t offset_) ++ : type(type_), module_id(module_id_), offset(offset_) {} ++ ++ WasmAddressType GetType() { return static_cast(type); } ++ operator lldb::addr_t() { return *(uint64_t *)this; } ++}; ++ ++/// ProcessWasm provides the access to the Wasm program state ++/// retrieved from the Wasm engine. ++class ProcessWasm : public process_gdb_remote::ProcessGDBRemote { ++public: ++ ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); ++ ~ProcessWasm() override = default; ++ ++ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, ++ lldb::ListenerSP listener_sp, ++ const FileSpec *crash_file_path, ++ bool can_connect); ++ ++ static void Initialize(); ++ static void DebuggerInitialize(Debugger &debugger); ++ static void Terminate(); ++ static ConstString GetPluginNameStatic(); ++ static const char *GetPluginDescriptionStatic(); ++ ++ /// PluginInterface protocol. ++ /// \{ ++ ConstString GetPluginName() override; ++ uint32_t GetPluginVersion() override; ++ /// \} ++ ++ /// Process protocol. ++ /// \{ ++ size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error, ++ ExecutionContext *exe_ctx = nullptr) override; ++ /// \} ++ ++ /// Query the value of a WebAssembly local variable from the WebAssembly ++ /// remote process. ++ bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size, ++ size_t &size); ++ ++ /// Query the value of a WebAssembly global variable from the WebAssembly ++ /// remote process. ++ bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size, ++ size_t &size); ++ ++ /// Query the value of an item in the WebAssembly operand stack from the ++ /// WebAssembly remote process. ++ bool GetWasmStackValue(int frame_index, int index, void *buf, ++ size_t buffer_size, size_t &size); ++ ++ /// Read from the WebAssembly Memory space. ++ size_t WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, void *buf, ++ size_t buffer_size); ++ ++ /// Read from the WebAssembly Data space. ++ size_t WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr, void *buf, ++ size_t buffer_size); ++ ++ /// Retrieve the current call stack from the WebAssembly remote process. ++ bool GetWasmCallStack(lldb::tid_t tid, ++ std::vector &call_stack_pcs); ++ ++ // Check if a given Process ++ bool CanDebug(lldb::TargetSP target_sp, ++ bool plugin_specified_by_name) override; ++ ++protected: ++ /// ProcessGDBRemote protocol. ++ /// \{ ++ std::shared_ptr ++ CreateThread(lldb::tid_t tid) override; ++ /// \} ++ ++private: ++ friend class UnwindWasm; ++ process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() { ++ return m_register_info_sp; ++ } ++ ++ ProcessWasm(const ProcessWasm &); ++ const ProcessWasm &operator=(const ProcessWasm &) = delete; ++}; ++ ++} // namespace wasm ++} // namespace lldb_private ++ ++#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H +diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp +new file mode 100644 +index 000000000000..fa02073e7a52 +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp +@@ -0,0 +1,35 @@ ++//===-- ThreadWasm.cpp ----------------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "ThreadWasm.h" ++ ++#include "ProcessWasm.h" ++#include "UnwindWasm.h" ++#include "lldb/Target/Target.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++using namespace lldb_private::wasm; ++ ++Unwind &ThreadWasm::GetUnwinder() { ++ if (!m_unwinder_up) { ++ assert(CalculateTarget()->GetArchitecture().GetMachine() == ++ llvm::Triple::wasm32); ++ m_unwinder_up.reset(new wasm::UnwindWasm(*this)); ++ } ++ return *m_unwinder_up; ++} ++ ++bool ThreadWasm::GetWasmCallStack(std::vector &call_stack_pcs) { ++ ProcessSP process_sp(GetProcess()); ++ if (process_sp) { ++ ProcessWasm *wasm_process = static_cast(process_sp.get()); ++ return wasm_process->GetWasmCallStack(GetID(), call_stack_pcs); ++ } ++ return false; ++} +diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h +new file mode 100644 +index 000000000000..0a33c07de994 +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h +@@ -0,0 +1,41 @@ ++//===-- ThreadWasm.h --------------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H ++#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H ++ ++#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" ++ ++namespace lldb_private { ++namespace wasm { ++ ++/// ProcessWasm provides the access to the Wasm program state ++/// retrieved from the Wasm engine. ++class ThreadWasm : public process_gdb_remote::ThreadGDBRemote { ++public: ++ ThreadWasm(Process &process, lldb::tid_t tid) ++ : process_gdb_remote::ThreadGDBRemote(process, tid) {} ++ ~ThreadWasm() override = default; ++ ++ /// Retrieve the current call stack from the WebAssembly remote process. ++ bool GetWasmCallStack(std::vector &call_stack_pcs); ++ ++protected: ++ /// Thread protocol. ++ /// \{ ++ Unwind &GetUnwinder() override; ++ /// \} ++ ++ ThreadWasm(const ThreadWasm &); ++ const ThreadWasm &operator=(const ThreadWasm &) = delete; ++}; ++ ++} // namespace wasm ++} // namespace lldb_private ++ ++#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H +diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp +new file mode 100644 +index 000000000000..1a195cb9361a +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp +@@ -0,0 +1,74 @@ ++//===-- UnwindWasm.cpp ----------------------------------------------------===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#include "UnwindWasm.h" ++#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" ++#include "Plugins/Process/wasm/ProcessWasm.h" ++#include "Plugins/Process/wasm/ThreadWasm.h" ++ ++using namespace lldb; ++using namespace lldb_private; ++using namespace process_gdb_remote; ++using namespace wasm; ++ ++class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext { ++public: ++ WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread, ++ uint32_t concrete_frame_idx, ++ GDBRemoteDynamicRegisterInfoSP ®_info_sp, ++ uint64_t pc) ++ : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false, ++ false) { ++ PrivateSetRegisterValue(0, pc); ++ } ++}; ++ ++lldb::RegisterContextSP ++UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) { ++ if (m_frames.size() <= frame->GetFrameIndex()) { ++ return lldb::RegisterContextSP(); ++ } ++ ++ ThreadSP thread = frame->GetThread(); ++ ThreadGDBRemote *gdb_thread = static_cast(thread.get()); ++ ProcessWasm *wasm_process = ++ static_cast(thread->GetProcess().get()); ++ std::shared_ptr reg_ctx_sp = ++ std::make_shared( ++ *gdb_thread, frame->GetConcreteFrameIndex(), ++ wasm_process->GetRegisterInfo(), m_frames[frame->GetFrameIndex()]); ++ return reg_ctx_sp; ++} ++ ++uint32_t UnwindWasm::DoGetFrameCount() { ++ if (!m_unwind_complete) { ++ m_unwind_complete = true; ++ m_frames.clear(); ++ ++ ThreadWasm &wasm_thread = static_cast(GetThread()); ++ if (!wasm_thread.GetWasmCallStack(m_frames)) ++ m_frames.clear(); ++ } ++ return m_frames.size(); ++} ++ ++bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, ++ lldb::addr_t &pc, ++ bool &behaves_like_zeroth_frame) { ++ if (m_frames.size() == 0) { ++ DoGetFrameCount(); ++ } ++ ++ if (frame_idx < m_frames.size()) { ++ behaves_like_zeroth_frame = (frame_idx == 0); ++ cfa = 0; ++ pc = m_frames[frame_idx]; ++ return true; ++ } ++ return false; ++} +\ No newline at end of file +diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h +new file mode 100644 +index 000000000000..9bd1dac9a98a +--- /dev/null ++++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h +@@ -0,0 +1,55 @@ ++//===-- UnwindWasm.h --------------------------------------------*- C++ -*-===// ++// ++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. ++// See https://llvm.org/LICENSE.txt for license information. ++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ++// ++//===----------------------------------------------------------------------===// ++ ++#ifndef lldb_UnwindWasm_h_ ++#define lldb_UnwindWasm_h_ ++ ++#include "lldb/Target/RegisterContext.h" ++#include "lldb/Target/Unwind.h" ++#include ++ ++namespace lldb_private { ++namespace wasm { ++ ++/// UnwindWasm manages stack unwinding for a WebAssembly process. ++class UnwindWasm : public lldb_private::Unwind { ++public: ++ UnwindWasm(lldb_private::Thread &thread) ++ : Unwind(thread), m_frames(), m_unwind_complete(false) {} ++ ~UnwindWasm() override = default; ++ ++protected: ++ /// Unwind protocol. ++ /// \{ ++ void DoClear() override { ++ m_frames.clear(); ++ m_unwind_complete = false; ++ } ++ ++ uint32_t DoGetFrameCount() override; ++ ++ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, ++ lldb::addr_t &pc, ++ bool &behaves_like_zeroth_frame) override; ++ ++ lldb::RegisterContextSP ++ DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override; ++ /// \} ++ ++private: ++ std::vector m_frames; ++ bool m_unwind_complete; ++ ++ UnwindWasm(const UnwindWasm &); ++ const UnwindWasm &operator=(const UnwindWasm &) = delete; ++}; ++ ++} // namespace wasm ++} // namespace lldb_private ++ ++#endif // lldb_UnwindWasm_h_ +diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +index ccaf31317d75..c3ef5aebd46d 100644 +--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp ++++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +@@ -3212,8 +3212,13 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, + GetDWARFDeclContext(die).GetQualifiedNameAsConstString().GetCString(); + } + +- if (tag == DW_TAG_formal_parameter) ++ if (tag == DW_TAG_formal_parameter) { + scope = eValueTypeVariableArgument; ++ // For Wasm dwarft, pamameter may don't have location attr, ++ // so set module here ++ if (!location.GetModule()) ++ location.SetModule(module); ++ } + else { + // DWARF doesn't specify if a DW_TAG_variable is a local, global + // or static variable, so we have to do a little digging: +diff --git a/lldb/source/Target/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp +index b660c310ef31..cd76421cec18 100644 +--- a/lldb/source/Target/PathMappingList.cpp ++++ b/lldb/source/Target/PathMappingList.cpp +@@ -218,7 +218,12 @@ bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) co + } + + llvm::Optional PathMappingList::FindFile(const FileSpec &orig_spec) const { +- if (auto remapped = RemapPath(orig_spec.GetPath(), /*only_if_exists=*/true)) ++ // We must normalize the orig_spec again using the host's path style, ++ // otherwise there will be mismatch between the host and remote platform ++ // if they use different path styles. ++ if (auto remapped = RemapPath( ++ NormalizePath(ConstString(orig_spec.GetCString())).GetStringRef(), ++ /*only_if_exists=*/true)) + return remapped; + + return {}; +diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp +index a77ecddfbab6..e257f93508f6 100644 +--- a/lldb/source/Target/Platform.cpp ++++ b/lldb/source/Target/Platform.cpp +@@ -1970,6 +1970,12 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target, + trap_opcode_size = sizeof(g_i386_opcode); + } break; + ++ case llvm::Triple::wasm32: { ++ static const uint8_t g_wasm_opcode[] = {0x00}; // unreachable ++ trap_opcode = g_wasm_opcode; ++ trap_opcode_size = sizeof(g_wasm_opcode); ++ } break; ++ + default: + return 0; + } +diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp +index 8ecc66b592ea..f148987915de 100644 +--- a/lldb/source/Target/Process.cpp ++++ b/lldb/source/Target/Process.cpp +@@ -1892,7 +1892,8 @@ Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) { + // code + //#define VERIFY_MEMORY_READS + +-size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) { ++size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error, ++ ExecutionContext *exe_ctx) { + error.Clear(); + if (!GetDisableMemoryCache()) { + #if defined(VERIFY_MEMORY_READS) +diff --git a/lldb/source/Target/ProcessTrace.cpp b/lldb/source/Target/ProcessTrace.cpp +index c878a2ac4eb9..ad5945b0ad1f 100644 +--- a/lldb/source/Target/ProcessTrace.cpp ++++ b/lldb/source/Target/ProcessTrace.cpp +@@ -88,7 +88,7 @@ void ProcessTrace::RefreshStateAfterStop() {} + Status ProcessTrace::DoDestroy() { return Status(); } + + size_t ProcessTrace::ReadMemory(addr_t addr, void *buf, size_t size, +- Status &error) { ++ Status &error, ExecutionContext *exe_ctx) { + // Don't allow the caching that lldb_private::Process::ReadMemory does since + // we have it all cached in the trace files. + return DoReadMemory(addr, buf, size, error); +diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp +index 896e647bbb52..f76307016102 100644 +--- a/lldb/source/Target/ThreadPlanStepRange.cpp ++++ b/lldb/source/Target/ThreadPlanStepRange.cpp +@@ -334,7 +334,10 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { + // If we didn't find a branch, run to the end of the range. + if (branch_index == UINT32_MAX) { + uint32_t last_index = instructions->GetSize() - 1; +- if (last_index - pc_index > 1) { ++ /* This line causes the "step over was treated as step in" issue, we ++ * modify it as a workaround */ ++ /* The origin line is: if (last_index - pc_index > 1) { */ ++ if (last_index - pc_index >= 1) { + InstructionSP last_inst = + instructions->GetInstructionAtIndex(last_index); + size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); +diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp +index 4ec2e25c7e3b..24c88fe9ae4f 100644 +--- a/lldb/source/Target/UnixSignals.cpp ++++ b/lldb/source/Target/UnixSignals.cpp +@@ -46,6 +46,8 @@ lldb::UnixSignalsSP UnixSignals::Create(const ArchSpec &arch) { + return std::make_shared(); + case llvm::Triple::NetBSD: + return std::make_shared(); ++ case llvm::Triple::WASI: ++ return std::make_shared(); + default: + return std::make_shared(); + } +diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h +index 4310ba9ce9e0..297b3387999d 100644 +--- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h ++++ b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h +@@ -13,6 +13,7 @@ + #ifndef LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H + #define LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H + ++#include "llvm/ExecutionEngine/Orc/Core.h" + #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" + #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h" + #include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h" +diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h +index 753b1998c40c..27370c62dd6e 100644 +--- a/llvm/include/llvm/Support/MathExtras.h ++++ b/llvm/include/llvm/Support/MathExtras.h +@@ -16,6 +16,7 @@ + #include "llvm/Support/Compiler.h" + #include + #include ++#include + #include + #include + #include diff --git a/build-scripts/package.cmake b/build-scripts/package.cmake new file mode 100644 index 0000000000..67cb8fc238 --- /dev/null +++ b/build-scripts/package.cmake @@ -0,0 +1,30 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set(_WAMR_BUILD_SCRIPTS_DIR "${CMAKE_CURRENT_LIST_DIR}") + +function(install_iwasm_package) + install (EXPORT iwasmTargets + FILE iwasmTargets.cmake + NAMESPACE iwasm:: + DESTINATION lib/cmake/iwasm + ) + + include (CMakePackageConfigHelpers) + configure_package_config_file (${_WAMR_BUILD_SCRIPTS_DIR}/iwasmConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/iwasmConfig.cmake" + INSTALL_DESTINATION lib/cmake/iwasm + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/iwasmConfigVersion.cmake" + VERSION ${WAMR_VERSION_MAJOR}.${WAMR_VERSION_MINOR}.${WAMR_VERSION_PATCH} + COMPATIBILITY SameMajorVersion + ) + + install (FILES + "${CMAKE_CURRENT_BINARY_DIR}/iwasmConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/iwasmConfigVersion.cmake" + DESTINATION lib/cmake/iwasm + ) +endfunction() diff --git a/build-scripts/requirements.txt b/build-scripts/requirements.txt new file mode 100644 index 0000000000..9b191ad7a2 --- /dev/null +++ b/build-scripts/requirements.txt @@ -0,0 +1 @@ +requests==2.33.1 \ No newline at end of file diff --git a/build-scripts/runtime_lib.cmake b/build-scripts/runtime_lib.cmake index e60a53b87a..bac81c4f2d 100644 --- a/build-scripts/runtime_lib.cmake +++ b/build-scripts/runtime_lib.cmake @@ -1,7 +1,6 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - if (NOT DEFINED WAMR_ROOT_DIR) set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../) endif () @@ -11,15 +10,14 @@ endif () if (NOT DEFINED IWASM_DIR) set (IWASM_DIR ${WAMR_ROOT_DIR}/core/iwasm) endif () -if (NOT DEFINED APP_MGR_DIR) - set (APP_MGR_DIR ${WAMR_ROOT_DIR}/core/app-mgr) -endif () -if (NOT DEFINED APP_FRAMEWORK_DIR) - set (APP_FRAMEWORK_DIR ${WAMR_ROOT_DIR}/core/app-framework) -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 variable + # 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} ") @@ -31,63 +29,178 @@ endif () # Set default options # Set WAMR_BUILD_TARGET, currently values supported: -# "X86_64", "AMD_64", "X86_32", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +# "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_SIZEOF_VOID_P EQUAL 8) + 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") - else () + 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 () ################ optional according to settings ################ -if (WAMR_BUILD_INTERP EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) +if (WAMR_BUILD_FAST_JIT EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) + # Enable classic interpreter if Fast JIT or LLVM JIT is enabled + set (WAMR_BUILD_INTERP 1) + set (WAMR_BUILD_FAST_INTERP 0) +endif () + +if (WAMR_BUILD_INTERP EQUAL 1) include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) endif () +if (WAMR_BUILD_FAST_JIT EQUAL 1) + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + message ("Fast JIT currently not supported on Windows") + set (WAMR_BUILD_FAST_JIT 0) + else () + include (${IWASM_DIR}/fast-jit/iwasm_fast_jit.cmake) + endif () +endif () + +if (WAMR_BUILD_JIT EQUAL 1) + # Enable AOT if LLVM JIT is enabled + set (WAMR_BUILD_AOT 1) + include (${IWASM_DIR}/compilation/iwasm_compl.cmake) +endif () + if (WAMR_BUILD_AOT EQUAL 1) include (${IWASM_DIR}/aot/iwasm_aot.cmake) - if (WAMR_BUILD_JIT EQUAL 1) - include (${IWASM_DIR}/compilation/iwasm_compl.cmake) - endif () endif () -if (WAMR_BUILD_APP_FRAMEWORK EQUAL 1) - include (${APP_FRAMEWORK_DIR}/app_framework.cmake) - include (${SHARED_DIR}/coap/lib_coap.cmake) - include (${APP_MGR_DIR}/app-manager/app_mgr.cmake) - include (${APP_MGR_DIR}/app-mgr-shared/app_mgr_shared.cmake) +if (WAMR_BUILD_STRINGREF EQUAL 1) + set (WAMR_BUILD_GC 1) +endif () + +if (WAMR_BUILD_GC EQUAL 1) + include (${IWASM_DIR}/common/gc/iwasm_gc.cmake) + # Enable the dependent feature if GC is enabled + set (WAMR_BUILD_REF_TYPES 1) endif () if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) include (${IWASM_DIR}/libraries/libc-builtin/libc_builtin.cmake) endif () -if (WAMR_BUILD_LIBC_WASI EQUAL 1) +if (WAMR_BUILD_LIBC_UVWASI EQUAL 1) + include (${IWASM_DIR}/libraries/libc-uvwasi/libc_uvwasi.cmake) + set (WAMR_BUILD_MODULE_INST_CONTEXT 1) +elseif (WAMR_BUILD_LIBC_WASI EQUAL 1) include (${IWASM_DIR}/libraries/libc-wasi/libc_wasi.cmake) + set (WAMR_BUILD_MODULE_INST_CONTEXT 1) +endif () + +if (WAMR_BUILD_LIB_PTHREAD_SEMAPHORE EQUAL 1) + # Enable the dependent feature if lib pthread semaphore is enabled + set (WAMR_BUILD_LIB_PTHREAD 1) +endif () + +if (WAMR_BUILD_WASI_NN EQUAL 1) + include (${IWASM_DIR}/libraries/wasi-nn/cmake/wasi_nn.cmake) + set (WAMR_BUILD_MODULE_INST_CONTEXT 1) +endif () + +if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + set (WAMR_BUILD_LIB_PTHREAD_SEMAPHORE 0) + message ("Lib pthread semaphore currently not supported on Windows") + endif () + include (${IWASM_DIR}/libraries/lib-pthread/lib_pthread.cmake) + # Enable the dependent feature if lib pthread is enabled + set (WAMR_BUILD_THREAD_MGR 1) + set (WAMR_BUILD_BULK_MEMORY 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_SHARED_HEAP EQUAL 1) + include (${IWASM_DIR}/libraries/shared-heap/shared_heap.cmake) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_THREAD_MGR 1) + include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake) + + if (WAMR_BUILD_FAST_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + message(STATUS + "Debugger doesn't work with fast interpreter, switch to classic interpreter") + endif () +endif () + +if (WAMR_BUILD_THREAD_MGR EQUAL 1) + include (${IWASM_DIR}/libraries/thread-mgr/thread_mgr.cmake) +endif () + +if (WAMR_BUILD_LIBC_EMCC EQUAL 1) + include (${IWASM_DIR}/libraries/libc-emcc/libc_emcc.cmake) +endif () + +if (WAMR_BUILD_LIB_RATS EQUAL 1) + include (${IWASM_DIR}/libraries/lib-rats/lib_rats.cmake) +endif () + +if (WAMR_BUILD_WASM_CACHE EQUAL 1) + include (${WAMR_ROOT_DIR}/build-scripts/involve_boringssl.cmake) endif () ####################### Common sources ####################### -set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -ffunction-sections -fdata-sections \ - -Wall -Wno-unused-parameter -Wno-pedantic") +if (NOT MSVC) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -ffunction-sections -fdata-sections") +endif () # include the build config template file include (${CMAKE_CURRENT_LIST_DIR}/config_common.cmake) -include_directories (${SHARED_DIR}/include - ${IWASM_DIR}/include) +if (WAMR_BUILD_SIMD EQUAL 1 AND WAMR_BUILD_FAST_INTERP EQUAL 1) + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + message(STATUS "SIMDe doesnt support platform " ${WAMR_BUILD_PLATFORM}) + set(WAMR_BUILD_SIMDE 0) + else() + include (${IWASM_DIR}/libraries/simde/simde.cmake) + set (WAMR_BUILD_SIMDE 1) + endif() +else() + set(WAMR_BUILD_SIMDE 0) +endif () + +include_directories (${IWASM_DIR}/include) file (GLOB header - ${SHARED_DIR}/include/*.h ${IWASM_DIR}/include/*.h ) LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) -enable_language (ASM) +if (WAMR_BUILD_PLATFORM STREQUAL "windows") + enable_language (ASM_MASM) +else() + enable_language (ASM) +endif() + +# it will expose the runtime APIs. +# you'll use the following command to check the exported APIs +# dumpbin.exe /EXPORTS xxx +if (MSVC) + add_compile_definitions(COMPILING_WASM_RUNTIME_API=1) +endif () -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) @@ -99,13 +212,20 @@ set (source_all ${UTILS_SHARED_SOURCE} ${LIBC_BUILTIN_SOURCE} ${LIBC_WASI_SOURCE} + ${WASI_NN_SOURCES} ${IWASM_COMMON_SOURCE} ${IWASM_INTERP_SOURCE} ${IWASM_AOT_SOURCE} ${IWASM_COMPL_SOURCE} - ${WASM_APP_LIB_SOURCE_ALL} - ${NATIVE_INTERFACE_SOURCE} - ${APP_MGR_SOURCE} + ${IWASM_FAST_JIT_SOURCE} + ${IWASM_GC_SOURCE} + ${LIB_WASI_THREADS_SOURCE} + ${LIB_PTHREAD_SOURCE} + ${THREAD_MGR_SOURCE} + ${LIBC_EMCC_SOURCE} + ${LIB_RATS_SOURCE} + ${DEBUG_ENGINE_SOURCE} + ${LIB_SHARED_HEAP_SOURCE} ) set (WAMR_RUNTIME_LIB_SOURCE ${source_all}) diff --git a/build-scripts/unsupported_combination.cmake b/build-scripts/unsupported_combination.cmake new file mode 100644 index 0000000000..1789c71b52 --- /dev/null +++ b/build-scripts/unsupported_combination.cmake @@ -0,0 +1,111 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(CMakePrintHelpers) + +# Define a function to check for unsupported combinations +function(check_aot_mode_error error_message) + if(WAMR_BUILD_AOT EQUAL 1) + message(FATAL_ERROR "${error_message}") + endif() +endfunction() + +# Define a function to check for unsupported combinations with CLASSIC_INTERP +function(check_classic_interp_error error_message) + # Usually, Enable INTERP to enable wasm loader for JIT + # WAMR_BUILD_JIT might be undefined, so check it first + if(WAMR_BUILD_JIT EQUAL 1) + return() + endif() + + if(WAMR_BUILD_FAST_JIT EQUAL 1) + return() + endif() + + if(WAMR_BUILD_INTERP EQUAL 1 AND WAMR_BUILD_FAST_INTERP EQUAL 0) + message(FATAL_ERROR "${error_message}") + endif() +endfunction() + +# Define a function to check for unsupported combinations with FAST_INTERP +function(check_fast_interp_error error_message) + # Usually, Enable INTERP to enable wasm loader for JIT + # WAMR_BUILD_JIT might be undefined, so check it first + if(WAMR_BUILD_JIT EQUAL 1) + return() + endif() + + if(WAMR_BUILD_FAST_JIT EQUAL 1) + return() + endif() + + if(WAMR_BUILD_INTERP EQUAL 1 AND WAMR_BUILD_FAST_INTERP EQUAL 1) + message(FATAL_ERROR "${error_message}") + endif() +endfunction() + +# Define a function to check for unsupported combinations with FAST_JIT +function(check_fast_jit_error error_message) + if(WAMR_BUILD_FAST_JIT EQUAL 1) + message(FATAL_ERROR "${error_message}") + endif() +endfunction() + +# Define a function to check for unsupported combinations with LLVM_JIT +function(check_llvm_jit_error error_message) + if(WAMR_BUILD_JIT EQUAL 1) + message(FATAL_ERROR "${error_message}") + endif() +endfunction() + +# Below are the unsupported combinations checks +# Please keep this list in sync with tests/unit/unsupported-features/CMakeLists.txt +# and tests/wamr-test-suites/test_wamr.sh + +if(WAMR_BUILD_EXCE_HANDLING EQUAL 1) + check_aot_mode_error("Unsupported build configuration: EXCE_HANDLING + AOT") + # FAST_INTERP + EXCE_HANDLING is supported for *throw-only* shapes: + # WASM modules that declare tags and execute throw / rethrow without + # ever entering a same-function try / catch handler. The throw + # propagates to the caller via the existing got_exception bailout + # path, exactly like any other trap. This covers Porffor (its + # JS-to-wasm compiler emits 0 try/catch handlers; every JS throw + # escapes to the host). Modules that contain WASM_OP_TRY / CATCH / + # CATCH_ALL / DELEGATE still load, but those handlers report + # "unsupported opcode" at runtime — see the WASM_OP_TRY handler in + # core/iwasm/interpreter/wasm_interp_fast.c. Full same-function + # try / catch lowering is the natural follow-up. + check_fast_jit_error("Unsupported build configuration: EXCE_HANDLING + FAST_JIT") + check_llvm_jit_error("Unsupported build configuration: EXCE_HANDLING + JIT") +endif() + +if(WAMR_BUILD_GC EQUAL 1) + check_fast_jit_error("Unsupported build configuration: GC + FAST_JIT") +endif() + +if(WAMR_BUILD_MEMORY64 EQUAL 1) + check_fast_interp_error("Unsupported build configuration: MEMORY64 + FAST_INTERP") + check_fast_jit_error("Unsupported build configuration: MEMORY64 + FAST_JIT") + check_llvm_jit_error("Unsupported build configuration: MEMORY64 + JIT") +endif() + +if(WAMR_BUILD_MULTI_MEMORY EQUAL 1) + check_aot_mode_error("Unsupported build configuration: MULTI_MEMORY + AOT") + check_fast_interp_error("Unsupported build configuration: MULTI_MEMORY + FAST_INTERP") + check_fast_jit_error("Unsupported build configuration: MULTI_MEMORY + FAST_JIT") + check_llvm_jit_error("Unsupported build configuration: MULTI_MEMORY + JIT") +endif() + +if(WAMR_BUILD_MULTI_MODULE EQUAL 1) + check_fast_jit_error("Unsupported build configuration: MULTI_MODULE + FAST_JIT") + check_llvm_jit_error("Unsupported build configuration: MULTI_MODULE + JIT") +endif() + +if(WAMR_BUILD_SHARED_HEAP EQUAL 1) + check_fast_jit_error("Unsupported build configuration: SHARED_HEAP + FAST_JIT") +endif() + +if(WAMR_BUILD_SIMD EQUAL 1) + check_classic_interp_error("Unsupported build configuration: SIMD + CLASSIC_INTERP") + check_fast_jit_error("Unsupported build configuration: SIMD + FAST_JIT") +endif() diff --git a/build-scripts/version.cmake b/build-scripts/version.cmake new file mode 100644 index 0000000000..accbe5fd5a --- /dev/null +++ b/build-scripts/version.cmake @@ -0,0 +1,28 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +if(NOT WAMR_ROOT_DIR) + # if from wamr-compiler + set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..) +endif() + +set(WAMR_VERSION_MAJOR 2) +set(WAMR_VERSION_MINOR 4) +set(WAMR_VERSION_PATCH 3) + +message("-- WAMR version: ${WAMR_VERSION_MAJOR}.${WAMR_VERSION_MINOR}.${WAMR_VERSION_PATCH}") + +# Configure the version header file +configure_file( + ${WAMR_ROOT_DIR}/core/version.h.in + ${WAMR_ROOT_DIR}/core/version.h +) + +# Set the library version and SOVERSION +function(set_version_info target) + set_target_properties(${target} + PROPERTIES + VERSION ${WAMR_VERSION_MAJOR}.${WAMR_VERSION_MINOR}.${WAMR_VERSION_PATCH} + SOVERSION ${WAMR_VERSION_MAJOR} +) +endfunction() diff --git a/build-scripts/warnings.cmake b/build-scripts/warnings.cmake new file mode 100644 index 0000000000..fbe6966ccc --- /dev/null +++ b/build-scripts/warnings.cmake @@ -0,0 +1,46 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# global additional warnings. +if (MSVC) + # warning level 4 + add_compile_options(/W4) +else () + # refer to https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html + add_compile_options( + -Wall -Wextra -Wformat -Wformat-security + $<$:-Wshadow> + ) + # -pedantic causes warnings like "ISO C forbids initialization between function pointer and ‘void *’" which + # is widely used in the codebase. + # + # -fpermissive causes warnings like "-fpermissive is valid for C++/ObjC++ but not for C" + # + # Reference: + # - gcc-4.8 https://gcc.gnu.org/onlinedocs/gcc-4.8.4/gcc/Warning-Options.html + # - gcc-11.5 https://gcc.gnu.org/onlinedocs/gcc-11.5.0/gcc/Warning-Options.html + add_compile_options ( + $<$:-Wimplicit-function-declaration> + ) + + # https://gcc.gnu.org/gcc-5/changes.html introduces incompatible-pointer-types + # https://releases.llvm.org/7.0.0/tools/clang/docs/DiagnosticsReference.html#wincompatible-pointer-types + # is the earliest version that supports this option I can found. + # Assume AppClang versioning is compatible with Clang. + if ((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "5.1") + OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0.0") + OR (CMAKE_C_COMPILER_ID STREQUAL "AppClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0.0")) + add_compile_options($<$:-Wincompatible-pointer-types>) + endif() + + # options benefit embedded system. + add_compile_options ( + -Wdouble-promotion + ) + + # waivers + add_compile_options ( + -Wno-unused + -Wno-unused-parameter + ) +endif () diff --git a/ci/build_wamr.sh b/ci/build_wamr.sh new file mode 100755 index 0000000000..1162cbf9ed --- /dev/null +++ b/ci/build_wamr.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +readonly CURRENT_PATH=$(dirname "$(realpath "$0")") +readonly ROOT=$(realpath "${CURRENT_PATH}/..") +readonly VARIANT=$(lsb_release -c | awk '{print $2}') + +docker build \ + --memory=4G --cpu-quota=50000 \ + -t wamr_dev_${VARIANT}:0.1 -f "${ROOT}"/.devcontainer/Dockerfile "${ROOT}"/.devcontainer \ + && docker run --rm -it \ + --cap-add=SYS_PTRACE \ + --cpus=".5" \ + --memory=4G \ + --mount type=bind,src="${ROOT}",dst=/workspaces \ + --name wamr_build_env \ + --security-opt=seccomp=unconfined \ + wamr_dev_${VARIANT}:0.1 \ + /bin/bash -c "\ + pwd \ + && pushd product-mini/platforms/linux \ + && rm -rf build \ + && mkdir build \ + && pushd build \ + && cmake .. \ + && make \ + && popd \ + && popd \ + && echo 'Copying the binary ...' \ + && rm -rf build_out \ + && mkdir build_out \ + && cp product-mini/platforms/linux/build/iwasm build_out/iwasm" diff --git a/ci/coding_guidelines_check.py b/ci/coding_guidelines_check.py new file mode 100644 index 0000000000..be1002c00f --- /dev/null +++ b/ci/coding_guidelines_check.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +import argparse +from pathlib import Path +import re +import shlex +import shutil +import subprocess +import sys +import unittest + +CLANG_FORMAT_CMD = "clang-format-14" +GIT_CLANG_FORMAT_CMD = "git-clang-format-14" + +# glob style patterns +EXCLUDE_PATHS = [ + "**/.git/*", + "**/.github/*", + "**/.vscode/*", + "**/build/*", + "**/build-scripts/*", + "**/ci/*", + "**/core/deps/*", + "**/doc/*", + "**/samples/wasm-c-api/src/*.*", + "**/samples/workload/*", + "**/test-tools/wasi-sdk/*", + "**/tests/wamr-test-suites/workspace/*", +] + +C_SUFFIXES = [".c", ".cc", ".cpp", ".h"] +INVALID_DIR_NAME_SEGMENT = r"([a-zA-Z0-9]+\_[a-zA-Z0-9]+)" +INVALID_FILE_NAME_SEGMENT = r"([a-zA-Z0-9]+\-[a-zA-Z0-9]+)" + + +def locate_command(command: str) -> bool: + if not shutil.which(command): + print(f"Command '{command}' not found") + return False + + return True + + +def is_excluded(path: str) -> bool: + path = Path(path).resolve() + for exclude_path in EXCLUDE_PATHS: + if path.match(exclude_path): + return True + return False + + +def pre_flight_check(root: Path) -> bool: + def check_aspell(root): + return True + + def check_clang_format(root: Path) -> bool: + if not locate_command(CLANG_FORMAT_CMD): + return False + + # Quick syntax check for .clang-format + try: + subprocess.check_output( + shlex.split(f"{CLANG_FORMAT_CMD} --dump-config"), cwd=root + ) + except subprocess.CalledProcessError: + print(f"Might have a typo in .clang-format") + return False + return True + + def check_git_clang_format() -> bool: + return locate_command(GIT_CLANG_FORMAT_CMD) + + return check_aspell(root) and check_clang_format(root) and check_git_clang_format() + + +def run_clang_format(file_path: Path, root: Path) -> bool: + try: + subprocess.check_call( + shlex.split( + f"{CLANG_FORMAT_CMD} --style=file --Werror --dry-run {file_path}" + ), + cwd=root, + ) + return True + except subprocess.CalledProcessError: + print(f"{file_path} failed the check of {CLANG_FORMAT_CMD}") + return False + + +def run_clang_format_diff(root: Path, commits: str) -> bool: + """ + Use `clang-format-14` or `git-clang-format-14` to check code format of + the PR, with a commit range specified. It is required to format the + code before committing the PR, or it might fail to pass the CI check: + + 1. Install clang-format-14.0.0 + + You can download the package from + https://github.com/llvm/llvm-project/releases + and install it. + + For Debian/Ubuntu, we can probably use + `sudo apt-get install clang-format-14`. + + Homebrew has it as a part of llvm@14. + ```shell + brew install llvm@14 + /usr/local/opt/llvm@14/bin/clang-format + ``` + + 2. Format the C/C++ source file + ``` shell + cd path/to/wamr/root + clang-format-14 --style file -i path/to/file + ``` + + The code wrapped by `/* clang-format off */` and `/* clang-format on */` + will not be formatted, you shall use them when the formatted code is not + readable or friendly: + + ``` cc + /* clang-format off */ + code snippets + /* clang-format on */ + ``` + + """ + try: + before, after = commits.split("..") + after = after if after else "HEAD" + COMMAND = ( + f"{GIT_CLANG_FORMAT_CMD} -v --binary " + f"{shutil.which(CLANG_FORMAT_CMD)} --style file " + f"--extensions c,cpp,h --diff {before} {after}" + ) + + p = subprocess.Popen( + shlex.split(COMMAND), + stdout=subprocess.PIPE, + stderr=None, + stdin=None, + universal_newlines=True, + ) + + stdout, _ = p.communicate() + if not stdout.startswith("diff --git"): + return True + + diff_content = stdout.split("\n") + found = False + for summary in [x for x in diff_content if x.startswith("diff --git")]: + # b/path/to/file -> path/to/file + with_invalid_format = re.split(r"\s+", summary)[-1][2:] + if not is_excluded(with_invalid_format): + print(f"--- {with_invalid_format} failed on code style checking.") + found = True + else: + return not found + except subprocess.CalledProcessError: + return False + + +def run_aspell(file_path: Path, root: Path) -> bool: + return True + + +def check_dir_name(path: Path, root: Path) -> bool: + m = re.search(INVALID_DIR_NAME_SEGMENT, str(path.relative_to(root).parent)) + if m: + print(f"--- found a character '_' in {m.groups()} in {path}") + + return not m + + +def check_file_name(path: Path) -> bool: + """ + file names should not contain any character '-' + + but some names are well known and use '-' as the separator, e.g.: + - docker-compose + - package-lock + - vite-env.d + - .clang-tidy (standard config file for Clang-Tidy) + """ + if path.stem in [ + "docker-compose", + "package-lock", + "vite-env.d", + "osv-scanner", + ".clang-tidy", + ]: + return True + + m = re.search(INVALID_FILE_NAME_SEGMENT, path.stem) + if m: + print(f"--- found a character '-' in {m.groups()} in {path}") + + return not m + + +def parse_commits_range(root: Path, commits: str) -> list: + GIT_LOG_CMD = f"git log --pretty='%H' {commits}" + try: + ret = subprocess.check_output( + shlex.split(GIT_LOG_CMD), cwd=root, universal_newlines=True + ) + return [x for x in ret.split("\n") if x] + except subprocess.CalledProcessError: + print(f"can not parse any commit from the range {commits}") + return [] + + +def analysis_new_item_name(root: Path, commit: str) -> bool: + """ + For any file name in the repo, it is required to use '_' to replace '-'. + + For any directory name in the repo, it is required to use '-' to replace '_'. + """ + GIT_SHOW_CMD = f"git show --oneline --name-status --diff-filter A {commit}" + try: + invalid_items = True + output = subprocess.check_output( + shlex.split(GIT_SHOW_CMD), cwd=root, universal_newlines=True + ) + if not output: + return True + + NEW_FILE_PATTERN = "^A\s+(\S+)" + for line_no, line in enumerate(output.split("\n")): + # bypass the first line, usually it is the commit description + if line_no == 0: + continue + + if not line: + continue + + match = re.match(NEW_FILE_PATTERN, line) + if not match: + continue + + new_item = match.group(1) + new_item = Path(new_item).resolve() + + if new_item.is_file(): + if not check_file_name(new_item): + invalid_items = False + continue + + new_item = new_item.parent + + if not check_dir_name(new_item, root): + invalid_items = False + continue + else: + return invalid_items + + except subprocess.CalledProcessError: + return False + + +def process_entire_pr(root: Path, commits: str) -> bool: + if not commits: + print("Please provide a commits range") + return False + + commit_list = parse_commits_range(root, commits) + if not commit_list: + print(f"Quit since there is no commit to check with") + return True + + print(f"there are {len(commit_list)} commits in the PR") + + found = False + if not analysis_new_item_name(root, commits): + print(f"{analysis_new_item_name.__doc__}") + found = True + + if not run_clang_format_diff(root, commits): + print(f"{run_clang_format_diff.__doc__}") + found = True + + return not found + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Check if change meets all coding guideline requirements" + ) + parser.add_argument( + "-c", "--commits", default=None, help="Commit range in the form: a..b" + ) + options = parser.parse_args() + + wamr_root = Path(__file__).parent.joinpath("..").resolve() + + if not pre_flight_check(wamr_root): + return False + + return process_entire_pr(wamr_root, options.commits) + + +# run with python3 -m unitest ci/coding_guidelines_check.py +class TestCheck(unittest.TestCase): + def test_check_dir_name_failed(self): + root = Path("/root/Workspace/") + new_file_path = root.joinpath("core/shared/platform/esp_idf/espid_memmap.c") + self.assertFalse(check_dir_name(new_file_path, root)) + + def test_check_dir_name_pass(self): + root = Path("/root/Workspace/") + new_file_path = root.joinpath("core/shared/platform/esp-idf/espid_memmap.c") + self.assertTrue(check_dir_name(new_file_path, root)) + + def test_check_file_name_failed(self): + new_file_path = Path( + "/root/Workspace/core/shared/platform/esp-idf/espid-memmap.c" + ) + self.assertFalse(check_file_name(new_file_path)) + + def test_check_file_name_pass(self): + new_file_path = Path( + "/root/Workspace/core/shared/platform/esp-idf/espid_memmap.c" + ) + self.assertTrue(check_file_name(new_file_path)) + + +if __name__ == "__main__": + sys.exit(0 if main() else 1) diff --git a/ci/install_sgx_for_applicaiton_developer.sh b/ci/install_sgx_for_applicaiton_developer.sh new file mode 100755 index 0000000000..f119f0438c --- /dev/null +++ b/ci/install_sgx_for_applicaiton_developer.sh @@ -0,0 +1,400 @@ +#!/bin/bash + +# Reference: +# https://cc-enabling.trustedservices.intel.com/intel-sgx-sw-installation-guide-linux/02/installation_instructions/#intel-sgx-application-developer + +#TODO: +# report error when curl fails to download files, e.g. due to network issues or incorrect URLs + +set -euo pipefail +if [ "${DEBUG:-0}" -eq 1 ]; then + set -o xtrace +fi + +# Error trap handler - logs failure details and calls cleanup before exit +error_handler() { + local exit_code=$? + local line_number=${1:-$LINENO} + local bash_lineno=${2:-$BASH_LINENO} + local last_command=${3:-$BASH_COMMAND} + local function_stack=${4:-${FUNCNAME[*]}} + + # Log error context to file + { + echo "=== ERROR OCCURRED ===" + echo "Exit Code: $exit_code" + echo "Line Number: $line_number" + echo "Bash Line: $bash_lineno" + echo "Failed Command: $last_command" + echo "Function Stack: $function_stack" + echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" + echo "======================" + } >> "${LOG_FILE:-/tmp/install_sgx.log}" 2>/dev/null || true + + # Print concise error to stderr + echo "ERROR: Script failed at line $line_number with exit code $exit_code" >&2 + echo "Failed command: $last_command" >&2 + echo "Check log file: ${LOG_FILE:-/tmp/install_sgx.log}" >&2 + + # Call cleanup function if it exists + if type cleanup >/dev/null 2>&1; then + cleanup || true + fi + + exit $exit_code +} + +# Set up error trap +trap 'error_handler $LINENO $BASH_LINENO "$BASH_COMMAND" "${FUNCNAME[*]}"' ERR + +# Platform will be detected dynamically by platform_detect() function +# Supported platforms: Debian12, Debian11, Ubuntu22.04-server, Ubuntu20.04-server +PLATFORM="" + +# Logging infrastructure +LOG_FILE="/tmp/install_sgx.log" + +# Initialize log file with timestamp +init_log() { + echo "=== Intel SGX Installation Log - $(date) ===" > "${LOG_FILE}" + echo "Platform: ${PLATFORM}" >> "${LOG_FILE}" + echo "Script: $0" >> "${LOG_FILE}" + echo "Started at: $(date '+%Y-%m-%d %H:%M:%S')" >> "${LOG_FILE}" + echo "" >> "${LOG_FILE}" +} + +# Log message with timestamp +log_info() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "${LOG_FILE}" +} + +# Execute command with output redirected to log +log_exec() { + log_info "Executing: $*" + "$@" >>"$LOG_FILE" 2>&1 +} + +# Print environment sourcing instructions +print_env_instructions() { + log_info "Printing environment setup instructions" + + echo "========================================================================" + echo " IMPORTANT: Before building or running SGX applications, you must run:" + echo " source /opt/intel/sgxsdk/environment" + echo " in your current shell to activate SGX SDK environment variables." + echo "========================================================================" + + log_info "Environment setup instructions displayed to user" +} + +check_sgx_packages() { + log_info "Checking for existing SGX packages..." + + local packages=("libsgx-quote-ex" "libsgx-dcap-ql" "libsgx-enclave-common-dev" "libsgx-dcap-ql-dev" "libsgx-dcap-default-qpl-dev" "tee-appraisal-tool") + local missing_packages=() + + for package in "${packages[@]}"; do + if ! dpkg -l "$package" >> "${LOG_FILE}" 2>&1; then + missing_packages+=("$package") + log_info "Package $package not installed" + else + log_info "Package $package already installed" + fi + done + + if [ ${#missing_packages[@]} -eq 0 ]; then + log_info "All SGX packages are already installed" + return 0 + else + log_info "Missing SGX packages: ${missing_packages[*]}" + return 1 + fi +} + +check_sgx_sdk() { + log_info "Checking for existing SGX SDK..." + + if [ -d "/opt/intel/sgxsdk" ] && [ -f "/opt/intel/sgxsdk/environment" ]; then + log_info "SGX SDK already installed at /opt/intel/sgxsdk" + + # Validate SDK installation by checking key components + if [ -f "/opt/intel/sgxsdk/bin/sgx-gdb" ] && [ -d "/opt/intel/sgxsdk/include" ]; then + log_info "SGX SDK installation appears complete" + return 0 + else + log_info "SGX SDK installation incomplete - missing key components" + return 1 + fi + else + log_info "SGX SDK not found" + return 1 + fi +} + +check_sgx_repo() { + log_info "Checking for existing SGX local repository..." + + if [ -d "/opt/intel/sgx_debian_local_repo" ] && [ -f "/etc/apt/sources.list.d/sgx_debian_local_repo.list" ]; then + log_info "SGX local repository already configured" + return 0 + else + log_info "SGX local repository not configured" + return 1 + fi +} + +# Modular installation functions + +# Platform detection and configuration +platform_detect() { + log_info "Entering platform_detect() function" + + if [ ! -f "/etc/os-release" ]; then + log_info "ERROR: /etc/os-release not found - cannot detect OS" + echo "ERROR: Cannot detect operating system. /etc/os-release not found." >&2 + log_info "Exiting platform_detect() function" + return 1 + fi + + # Parse OS information from /etc/os-release + local os_id=$(grep '^ID=' /etc/os-release | cut -d'=' -f2 | tr -d '"') + local version_id=$(grep '^VERSION_ID=' /etc/os-release | cut -d'=' -f2 | tr -d '"') + + log_info "Raw OS detection: ID=${os_id}, VERSION_ID=${version_id}" + + # Determine platform string based on OS and version + case "${os_id}" in + "ubuntu") + case "${version_id}" in + "20.04") + PLATFORM="Ubuntu20.04-server" + ;; + "22.04") + PLATFORM="Ubuntu22.04-server" + ;; + *) + log_info "ERROR: Unsupported Ubuntu version ${version_id}. Supported: 20.04, 22.04" + echo "ERROR: Unsupported Ubuntu version ${version_id}. This script supports Ubuntu 20.04 and 22.04 only." >&2 + log_info "Exiting platform_detect() function" + return 1 + ;; + esac + ;; + "debian") + case "${version_id}" in + "11") + PLATFORM="Debian11" + ;; + "12") + PLATFORM="Debian12" + ;; + *) + log_info "ERROR: Unsupported Debian version ${version_id}. Supported: 11, 12" + echo "ERROR: Unsupported Debian version ${version_id}. This script supports Debian 11 and 12 only." >&2 + log_info "Exiting platform_detect() function" + return 1 + ;; + esac + ;; + *) + log_info "ERROR: Unsupported OS ${os_id}. Supported: ubuntu, debian" + echo "ERROR: Unsupported operating system '${os_id}'. This script supports Ubuntu and Debian only." >&2 + log_info "Exiting platform_detect() function" + return 1 + ;; + esac + + log_info "Successfully detected platform: ${PLATFORM}" + echo "Detected platform: ${PLATFORM}" + + log_info "Exiting platform_detect() function" + return 0 +} + +# Install SGX packages and SDK +install_packages() { + log_info "Entering install_packages() function" + + # Skip repo setup if already configured + if ! check_sgx_repo; then + log_info "Setting up SGX local repository..." + + pushd /tmp >> "${LOG_FILE}" 2>&1 + log_exec curl -fsSLO \ + https://download.01.org/intel-sgx/latest/linux-latest/distro/${PLATFORM}/sgx_debian_local_repo.tgz + + local_sum=$(sha256sum sgx_debian_local_repo.tgz | awk '{print $1}') + remote_sum=$(curl -s https://download.01.org/intel-sgx/latest/dcap-latest/linux/SHA256SUM_dcap_1.24.cfg | grep "distro/${PLATFORM}/sgx_debian_local_repo.tgz" | awk '{print $1}') + if [[ "$local_sum" == "$remote_sum" ]]; then + log_info "Checksum matches" + else + log_info "Checksum mismatch!" + fi + + log_exec sudo mkdir -p /opt/intel + log_exec sudo tar xzf sgx_debian_local_repo.tgz -C /opt/intel + + echo 'deb [signed-by=/etc/apt/keyrings/intel-sgx-keyring.asc arch=amd64] file:///opt/intel/sgx_debian_local_repo bookworm main' \ + | sudo tee /etc/apt/sources.list.d/sgx_debian_local_repo.list | tee -a "${LOG_FILE}" > /dev/null + + log_exec sudo cp /opt/intel/sgx_debian_local_repo/keys/intel-sgx.key /etc/apt/keyrings/intel-sgx-keyring.asc + popd >> "${LOG_FILE}" 2>&1 + else + log_info "SGX repository already configured, skipping setup" + fi + + # Install SGX packages only if missing + if ! check_sgx_packages; then + log_info "Installing missing SGX packages..." + log_exec sudo apt-get update + log_exec sudo apt-get install -y libsgx-quote-ex libsgx-dcap-ql + else + log_info "SGX packages already installed, skipping" + fi + + # Install build dependencies + log_exec sudo apt-get update --quiet + log_exec sudo apt-get install --quiet -y build-essential python3 + log_exec sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 1 + + # Install Intel SGX SDK only if missing + if ! check_sgx_sdk; then + log_info "Installing Intel SGX SDK for Application Developer..." + + pushd /opt/intel >> "${LOG_FILE}" 2>&1 + log_exec sudo curl -fsSLo sgx_linux_x64_sdk.bin \ + https://download.01.org/intel-sgx/latest/linux-latest/distro/${PLATFORM}/sgx_linux_x64_sdk_2.27.100.1.bin + log_exec sudo chmod +x sgx_linux_x64_sdk.bin + log_exec sudo ./sgx_linux_x64_sdk.bin --prefix /opt/intel + + # Log environment setup instructions for user + log_info "SGX SDK installation completed successfully." + log_info "IMPORTANT: To use the SGX SDK in your development session:" + log_info " 1. Run: source /opt/intel/sgxsdk/environment" + log_info " 2. This must be done in each shell session where you use SGX" + log_info " 3. Environment variables are session-specific and cannot be exported by this script" + log_info " 4. Consider adding 'source /opt/intel/sgxsdk/environment' to your ~/.bashrc for automatic setup" + popd >> "${LOG_FILE}" 2>&1 + else + log_info "SGX SDK already installed, skipping" + fi + + # Install Developer packages for Intel SGX only if missing + if ! check_sgx_packages; then + log_info "Installing Intel SGX Developer packages..." + + log_exec sudo apt-get install -y libsgx-enclave-common-dev \ + libsgx-dcap-ql-dev \ + libsgx-dcap-default-qpl-dev \ + tee-appraisal-tool + else + log_info "SGX Developer packages already installed, skipping" + fi + + log_info "Exiting install_packages() function" + return 0 +} + +# Validate the installation was successful +validate_installation() { + log_info "Entering validate_installation() function" + + local validation_failed=0 + + # Re-check all components after installation + if ! check_sgx_packages; then + log_info "VALIDATION FAILED: SGX packages not properly installed" + validation_failed=1 + fi + + if ! check_sgx_sdk; then + log_info "VALIDATION FAILED: SGX SDK not properly installed" + validation_failed=1 + fi + + if ! check_sgx_repo; then + log_info "VALIDATION FAILED: SGX repository not properly configured" + validation_failed=1 + fi + + if [ $validation_failed -eq 0 ]; then + log_info "VALIDATION SUCCESS: All SGX components properly installed" + else + log_info "VALIDATION FAILED: Some SGX components failed installation" + log_info "Exiting validate_installation() function" + return 1 + fi + + log_info "Exiting validate_installation() function" + return 0 +} + +# Clean up temporary files +cleanup() { + log_info "Entering cleanup() function" + + # Clean up any temporary files in /tmp related to SGX installation + if [ -f "/tmp/sgx_debian_local_repo.tgz" ]; then + log_info "Removing temporary SGX repository archive" + rm -f /tmp/sgx_debian_local_repo.tgz + fi + + # Additional cleanup can be added here as needed + log_info "Temporary file cleanup completed" + + log_info "Exiting cleanup() function" + return 0 +} + +# Initialize logging +init_log + +log_info "Starting idempotency checks..." + +# Check if everything is already installed +if check_sgx_packages && check_sgx_sdk && check_sgx_repo; then + log_info "Complete SGX installation detected - no action needed" + echo "Intel SGX for Application Developer is already installed and configured." + print_env_instructions + exit 0 +fi + +log_info "Partial or missing SGX installation detected - proceeding with installation" + +# Main installation flow using modular functions + +log_info "Starting Intel SGX for Application Developer installation..." + +# Execute installation steps in modular fashion +platform_detect +if [ $? -ne 0 ]; then + log_info "Platform detection failed" + exit 1 +fi + +install_packages +if [ $? -ne 0 ]; then + log_info "Package installation failed" + exit 1 +fi + +validate_installation +if [ $? -ne 0 ]; then + log_info "Installation validation failed" + exit 1 +fi + +cleanup +if [ $? -ne 0 ]; then + log_info "Cleanup failed" + exit 1 +fi + +cleanup +if [ $? -ne 0 ]; then + log_info "Cleanup failed" + exit 1 +fi + +echo "Intel SGX for Application Developer installation completed." +print_env_instructions diff --git a/ci/pre_commit_hook_sample b/ci/pre_commit_hook_sample new file mode 100755 index 0000000000..682e789464 --- /dev/null +++ b/ci/pre_commit_hook_sample @@ -0,0 +1,32 @@ +#!/bin/bash + +# Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This is a sample of pre-commit hook that can be used to make your code fit the WAMR CI code style requirements. +# You need to have clang-format-12 installed to use this hook. +# To add this pre-commit hook, copy it to /.git/hooks/pre-commit +# (you don't need any extensions here) + +# Function to check if a file has a C or C++ extension + +is_c_or_cpp_file() { + file="$1" + if [[ "$filename" =~ \.(h|c|cpp)$ ]]; then + return 0 + else + return 1 + fi +} + +# Loop through staged files and apply command "abc" to C and C++ files +for staged_file in $(git diff --cached --name-only); do + if is_c_or_cpp_file "$staged_file"; then + clang-format-12 -Werror --style file --dry-run "$staged_file" 2>/dev/null + if [ $? -ne 0 ]; then + echo "Issues are found in $staged_file. Applying the fix" + clang-format-12 --style file -i "$staged_file" + fi + git add "$staged_file" # Add the modified file back to staging + fi +done diff --git a/ci/setup.sh b/ci/setup.sh new file mode 100755 index 0000000000..ebdf73af55 --- /dev/null +++ b/ci/setup.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This script executes some commands to make your onboarding with WAMR easier. +# For example, setting pre-commit hook that will make your code complaint with the +# code style requirements checked in WAMR CI + +echo "Copy the pre-commit hook to your hooks folder" +cp pre_commit_hook_sample ../.git/hooks/pre-commit + +# Feel free to propose your commands to this script to make developing WAMR easier + +echo "Setup is done" diff --git a/ci/validate_lldb.py b/ci/validate_lldb.py new file mode 100755 index 0000000000..1b431ada85 --- /dev/null +++ b/ci/validate_lldb.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import argparse +import time +from pathlib import Path +import subprocess, shlex + +SCRIPT_DIR = Path(__file__).parent.resolve() +REPO_ROOT_DIR = SCRIPT_DIR.parent +SAMPLE_CODE_FILE = REPO_ROOT_DIR / 'product-mini/app-samples/hello-world/main.c' +WASM_OUT_FILE = SCRIPT_DIR / 'out.wasm' + +parser = argparse.ArgumentParser( + description="Validate the customized lldb with sample code" +) +parser.add_argument( + "-l", "--lldb", dest='lldb', default='lldb', help="path to lldb executable" +) +parser.add_argument( + "-w", "--wamr", dest='wamr', default='iwasm', help="path to iwasm executable" +) +parser.add_argument( + "-p", "--port", dest='port', default='1234', help="debug server listen port" +) +parser.add_argument( + "-v", "--verbose", dest='verbose', action='store_true', default=False, help="display lldb stdout" +) + +options = parser.parse_args() + +lldb_command_epilogue = '-o q' + +test_cases = { + 'run_to_exit': '-o c', + 'func_breakpoint': '-o "b main" -o c -o c', + 'line_breakpoint': '-o "b main.c:12" -o c -o c', + 'break_on_unknown_func': '-o "b not_a_func" -o c', + 'watch_point': '-o "b main" -o c -o "watchpoint set variable buf" -o c -o "fr v buf" -o c', +} + +# Step1: Build wasm module with debug information +build_cmd = f'/opt/wasi-sdk/bin/clang -g -O0 -o {WASM_OUT_FILE} {SAMPLE_CODE_FILE}' +try: + print(f'building wasm module ...', end='', flush=True) + subprocess.check_call(shlex.split(build_cmd)) + print(f'\t OK') +except subprocess.CalledProcessError: + print("Failed to build wasm module with debug information") + exit(1) + +def print_process_output(p): + try: + outs, errs = p.communicate(timeout=2) + print("stdout:") + print(outs) + print("stderr:") + print(errs) + except subprocess.TimeoutExpired: + print("Failed to get process output") + +# Step2: Launch WAMR in debug mode and validate lldb commands + +iteration = 0 +for case, cmd in test_cases.items(): + lldb_command_prologue = f'{options.lldb} -o "process connect -p wasm connect://127.0.0.1:{int(options.port) + iteration}"' + wamr_cmd = f'{options.wamr} -g=127.0.0.1:{int(options.port) + iteration} {WASM_OUT_FILE}' + iteration += 1 + + has_error = False + print(f'validating case [{case}] ...', end='', flush=True) + lldb_cmd = f'{lldb_command_prologue} {cmd} {lldb_command_epilogue}' + + wamr_process = subprocess.Popen(shlex.split( + wamr_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + + time.sleep(0.1) + if (wamr_process.poll() != None): + print("\nWAMR doesn't wait for lldb connection") + print_process_output(wamr_process) + exit(1) + + lldb_process = subprocess.Popen(shlex.split( + lldb_cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + + if (options.verbose): + while (lldb_process.poll() is None): + print(lldb_process.stdout.read(), end='', flush=True) + + try: + if (lldb_process.wait(5) != 0): + print(f"\nFailed to validate case [{case}]") + print_process_output(lldb_process) + has_error = True + + if wamr_process.wait(2) != 0: + print("\nWAMR process doesn't exit normally") + print_process_output(wamr_process) + has_error = True + + except subprocess.TimeoutExpired: + print(f"\nFailed to validate case [{case}]") + print("wamr output:") + print_process_output(wamr_process) + print("lldb output:") + print_process_output(lldb_process) + has_error = True + finally: + if (lldb_process.poll() == None): + print(f'\nterminating lldb process [{lldb_process.pid}]') + lldb_process.kill() + if (wamr_process.poll() == None): + print(f'terminating wamr process [{wamr_process.pid}]') + wamr_process.kill() + + if (has_error): + exit(1) + + print(f'\t OK') + + # wait 100ms to ensure the socket is closed + time.sleep(0.1) + +print('Validate lldb success') +exit(0) diff --git a/core/app-framework/app-native-shared/README.md b/core/app-framework/app-native-shared/README.md deleted file mode 100644 index b166e0b3a9..0000000000 --- a/core/app-framework/app-native-shared/README.md +++ /dev/null @@ -1,11 +0,0 @@ - Notes: -======= -This folder is for the source files shared by both WASM APP and native runtime - -- The c files in this directory are compiled into both the WASM APP and runtime. -- The header files for distributing to SDK are placed in the "bi-inc" folder. - - - - - diff --git a/core/app-framework/app-native-shared/attr_container.c b/core/app-framework/app-native-shared/attr_container.c deleted file mode 100644 index 5145ac70b1..0000000000 --- a/core/app-framework/app-native-shared/attr_container.c +++ /dev/null @@ -1,830 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bi-inc/attr_container.h" - -typedef union jvalue { - bool z; - int8_t b; - uint16_t c; - int16_t s; - int32_t i; - int64_t j; - float f; - double d; -} jvalue; - -#define bh_memcpy_s(dest, dlen, src, slen) do { \ - int _ret = slen == 0 ? 0 : b_memcpy_s (dest, dlen, src, slen); \ - (void)_ret; \ - } while (0) - -static inline int16_t get_int16(const char *buf) -{ - int16_t ret; - bh_memcpy_s(&ret, sizeof(int16_t), buf, sizeof(int16_t)); - return ret; -} - -static inline uint16_t get_uint16(const char *buf) -{ - return get_int16(buf); -} - -static inline int32_t get_int32(const char *buf) -{ - int32_t ret; - bh_memcpy_s(&ret, sizeof(int32_t), buf, sizeof(int32_t)); - return ret; -} - -static inline uint32_t get_uint32(const char *buf) -{ - return get_int32(buf); -} - -static inline int64_t get_int64(const char *buf) -{ - int64_t ret; - bh_memcpy_s(&ret, sizeof(int64_t), buf, sizeof(int64_t)); - return ret; -} - -static inline uint64_t get_uint64(const char *buf) -{ - return get_int64(buf); -} - -static inline void set_int16(char *buf, int16_t v) -{ - bh_memcpy_s(buf, sizeof(int16_t), &v, sizeof(int16_t)); -} - -static inline void set_uint16(char *buf, uint16_t v) -{ - bh_memcpy_s(buf, sizeof(uint16_t), &v, sizeof(uint16_t)); -} - -static inline void set_int32(char *buf, int32_t v) -{ - bh_memcpy_s(buf, sizeof(int32_t), &v, sizeof(int32_t)); -} - -static inline void set_uint32(char *buf, uint32_t v) -{ - bh_memcpy_s(buf, sizeof(uint32_t), &v, sizeof(uint32_t)); -} - -static inline void set_int64(char *buf, int64_t v) -{ - bh_memcpy_s(buf, sizeof(int64_t), &v, sizeof(int64_t)); -} - -static inline void set_uint64(char *buf, uint64_t v) -{ - bh_memcpy_s(buf, sizeof(uint64_t), &v, sizeof(uint64_t)); -} - -char* -attr_container_get_attr_begin(const attr_container_t *attr_cont, - uint32_t *p_total_length, uint16_t *p_attr_num) -{ - char *p = (char*) attr_cont->buf; - uint16_t str_len, attr_num; - uint32_t total_length; - - /* skip total length */ - total_length = get_uint32(p); - p += sizeof(uint32_t); - if (!total_length) - return NULL; - - /* tag length */ - str_len = get_uint16(p); - p += sizeof(uint16_t); - if (!str_len) - return NULL; - - /* tag content */ - p += str_len; - if (p - attr_cont->buf >= total_length) - return NULL; - - /* attribute num */ - attr_num = get_uint16(p); - p += sizeof(uint16_t); - if (p - attr_cont->buf >= total_length) - return NULL; - - if (p_total_length) - *p_total_length = total_length; - - if (p_attr_num) - *p_attr_num = attr_num; - - /* first attribute */ - return p; -} - -static char* -attr_container_get_attr_next(const char *curr_attr) -{ - char *p = (char*) curr_attr; - uint8_t type; - - /* key length and key */ - p += sizeof(uint16_t) + get_uint16(p); - type = *p++; - - /* Short type to Boolean type */ - if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) { - p += 1 << (type & 3); - return p; - } - /* String type */ - else if (type == ATTR_TYPE_STRING) { - p += sizeof(uint16_t) + get_uint16(p); - return p; - } - /* ByteArray type */ - else if (type == ATTR_TYPE_BYTEARRAY) { - p += sizeof(uint32_t) + get_uint32(p); - return p; - } - - return NULL; -} - -static const char* -attr_container_find_attr(const attr_container_t *attr_cont, const char *key) -{ - uint32_t total_length; - uint16_t str_len, attr_num, i; - const char *p = attr_cont->buf; - - if (!key) - return NULL; - - if (!(p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num))) - return NULL; - - for (i = 0; i < attr_num; i++) { - /* key length */ - if (!(str_len = get_uint16(p))) - return NULL; - - if (str_len == strlen(key) + 1 - && memcmp(p + sizeof(uint16_t), key, str_len) == 0) { - if (p + sizeof(uint16_t) + str_len - attr_cont->buf >= total_length) - return NULL; - return p; - } - - if (!(p = attr_container_get_attr_next(p))) - return NULL; - } - - return NULL; -} - -char* -attr_container_get_attr_end(const attr_container_t *attr_cont) -{ - uint32_t total_length; - uint16_t attr_num, i; - char *p; - - if (!(p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num))) - return NULL; - - for (i = 0; i < attr_num; i++) - if (!(p = attr_container_get_attr_next(p))) - return NULL; - - return p; -} - -static char* -attr_container_get_msg_end(attr_container_t *attr_cont) -{ - char *p = attr_cont->buf; - return p + get_uint32(p); -} - -uint16_t attr_container_get_attr_num(const attr_container_t *attr_cont) -{ - uint16_t str_len; - /* skip total length */ - const char *p = attr_cont->buf + sizeof(uint32_t); - - str_len = get_uint16(p); - /* skip tag length and tag */ - p += sizeof(uint16_t) + str_len; - - /* attribute num */ - return get_uint16(p); -} - -static void attr_container_inc_attr_num(attr_container_t *attr_cont) -{ - uint16_t str_len, attr_num; - /* skip total length */ - char *p = attr_cont->buf + sizeof(uint32_t); - - str_len = get_uint16(p); - /* skip tag length and tag */ - p += sizeof(uint16_t) + str_len; - - /* attribute num */ - attr_num = get_uint16(p) + 1; - set_uint16(p, attr_num); -} - -attr_container_t * -attr_container_create(const char *tag) -{ - attr_container_t *attr_cont; - int length, tag_length; - char *p; - - tag_length = tag ? strlen(tag) + 1 : 1; - length = offsetof(attr_container_t, buf) + - /* total length + tag length + tag + reserved 100 bytes */ - sizeof(uint32_t) + sizeof(uint16_t) + tag_length + 100; - - if (!(attr_cont = attr_container_malloc(length))) { - attr_container_printf( - "Create attr_container failed: allocate memory failed.\r\n"); - return NULL; - } - - memset(attr_cont, 0, length); - p = attr_cont->buf; - - /* total length */ - set_uint32(p, length - offsetof(attr_container_t, buf)); - p += 4; - - /* tag length, tag */ - set_uint16(p, tag_length); - p += 2; - if (tag) - bh_memcpy_s(p, tag_length, tag, tag_length); - - return attr_cont; -} - -void attr_container_destroy(const attr_container_t *attr_cont) -{ - if (attr_cont) - attr_container_free((char*) attr_cont); -} - -static bool check_set_attr(attr_container_t **p_attr_cont, const char *key) -{ - uint32_t flags; - - if (!p_attr_cont || !*p_attr_cont || !key || strlen(key) == 0) { - attr_container_printf( - "Set attribute failed: invalid input arguments.\r\n"); - return false; - } - - flags = get_uint32((char*) *p_attr_cont); - if (flags & ATTR_CONT_READONLY_SHIFT) { - attr_container_printf( - "Set attribute failed: attribute container is readonly.\r\n"); - return false; - } - - return true; -} - -bool attr_container_set_attr(attr_container_t **p_attr_cont, const char *key, - int type, const void *value, int value_length) -{ - attr_container_t *attr_cont, *attr_cont1; - uint16_t str_len; - uint32_t total_length, attr_len; - char *p, *p1, *attr_end, *msg_end, *attr_buf; - - if (!check_set_attr(p_attr_cont, key)) { - return false; - } - - attr_cont = *p_attr_cont; - p = attr_cont->buf; - total_length = get_uint32(p); - - if (!(attr_end = attr_container_get_attr_end(attr_cont))) { - attr_container_printf("Set attr failed: get attr end failed.\r\n"); - return false; - } - - msg_end = attr_container_get_msg_end(attr_cont); - - /* key len + key + '\0' + type */ - attr_len = sizeof(uint16_t) + strlen(key) + 1 + 1; - if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) - attr_len += 1 << (type & 3); - else if (type == ATTR_TYPE_STRING) - attr_len += sizeof(uint16_t) + value_length; - else if (type == ATTR_TYPE_BYTEARRAY) - attr_len += sizeof(uint32_t) + value_length; - - if (!(p = attr_buf = attr_container_malloc(attr_len))) { - attr_container_printf("Set attr failed: allocate memory failed.\r\n"); - return false; - } - - /* Set the attr buf */ - str_len = strlen(key) + 1; - set_uint16(p, str_len); - p += sizeof(uint16_t); - bh_memcpy_s(p, str_len, key, str_len); - p += str_len; - - *p++ = type; - if (type >= ATTR_TYPE_SHORT && 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); - p += sizeof(uint16_t); - bh_memcpy_s(p, value_length, value, value_length); - } else if (type == ATTR_TYPE_BYTEARRAY) { - set_uint32(p, value_length); - p += sizeof(uint32_t); - bh_memcpy_s(p, value_length, value, value_length); - } - - if ((p = (char*) attr_container_find_attr(attr_cont, key))) { - /* key found */ - p1 = attr_container_get_attr_next(p); - - if (p1 - p == attr_len) { - bh_memcpy_s(p, attr_len, attr_buf, attr_len); - attr_container_free(attr_buf); - return true; - } - - if (p1 - p + msg_end - attr_end >= attr_len) { - memmove(p, p1, attr_end - p1); - bh_memcpy_s(p + (attr_end - p1), attr_len, attr_buf, attr_len); - attr_container_free(attr_buf); - return true; - } - - total_length += attr_len + 100; - if (!(attr_cont1 = attr_container_malloc( - offsetof(attr_container_t, buf) + total_length))) { - attr_container_printf( - "Set attr failed: allocate memory failed.\r\n"); - attr_container_free(attr_buf); - return false; - } - - bh_memcpy_s(attr_cont1, p - (char* )attr_cont, attr_cont, - p - (char* )attr_cont); - bh_memcpy_s((char* )attr_cont1 + (unsigned )(p - (char* )attr_cont), - attr_end - p1, p1, attr_end - p1); - bh_memcpy_s( - (char* )attr_cont1 + (unsigned )(p - (char* )attr_cont) - + (unsigned )(attr_end - p1), attr_len, attr_buf, - attr_len); - p = attr_cont1->buf; - set_uint32(p, total_length); - *p_attr_cont = attr_cont1; - /* Free original buffer */ - attr_container_free(attr_cont); - attr_container_free(attr_buf); - return true; - } else { - /* key not found */ - if (msg_end - attr_end >= attr_len) { - bh_memcpy_s(attr_end, msg_end - attr_end, attr_buf, attr_len); - attr_container_inc_attr_num(attr_cont); - attr_container_free(attr_buf); - return true; - } - - total_length += attr_len + 100; - if (!(attr_cont1 = attr_container_malloc( - offsetof(attr_container_t, buf) + total_length))) { - attr_container_printf( - "Set attr failed: allocate memory failed.\r\n"); - attr_container_free(attr_buf); - return false; - } - - bh_memcpy_s(attr_cont1, attr_end - (char* )attr_cont, attr_cont, - attr_end - (char* )attr_cont); - bh_memcpy_s( - (char* )attr_cont1 + (unsigned )(attr_end - (char* )attr_cont), - attr_len, attr_buf, attr_len); - attr_container_inc_attr_num(attr_cont1); - p = attr_cont1->buf; - set_uint32(p, total_length); - *p_attr_cont = attr_cont1; - /* Free original buffer */ - attr_container_free(attr_cont); - attr_container_free(attr_buf); - return true; - } - - return false; -} - -bool attr_container_set_short(attr_container_t **p_attr_cont, const char *key, - short value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_SHORT, &value, 2); -} - -bool attr_container_set_int(attr_container_t **p_attr_cont, const char *key, - int value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT, &value, 4); -} - -bool attr_container_set_int64(attr_container_t **p_attr_cont, const char *key, - int64_t value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT64, &value, 8); -} - -bool attr_container_set_byte(attr_container_t **p_attr_cont, const char *key, - int8_t value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BYTE, &value, 1); -} - -bool attr_container_set_uint16(attr_container_t **p_attr_cont, const char *key, - uint16_t value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_UINT16, &value, - 2); -} - -bool attr_container_set_float(attr_container_t **p_attr_cont, const char *key, - float value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_FLOAT, &value, 4); -} - -bool attr_container_set_double(attr_container_t **p_attr_cont, const char *key, - double value) -{ - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_DOUBLE, &value, - 8); -} - -bool attr_container_set_bool(attr_container_t **p_attr_cont, const char *key, - bool value) -{ - int8_t value1 = value ? 1 : 0; - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BOOLEAN, &value1, - 1); -} - -bool attr_container_set_string(attr_container_t **p_attr_cont, const char *key, - const char *value) -{ - if (!value) { - attr_container_printf("Set attr failed: invald input arguments.\r\n"); - return false; - } - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_STRING, value, - strlen(value) + 1);; -} - -bool attr_container_set_bytearray(attr_container_t **p_attr_cont, - const char *key, const int8_t *value, unsigned length) -{ - if (!value) { - attr_container_printf("Set attr failed: invald input arguments.\r\n"); - return false; - } - return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BYTEARRAY, value, - length);; -} - -static const char* -attr_container_get_attr(const attr_container_t *attr_cont, const char *key) -{ - const char *attr_addr; - - if (!attr_cont || !key) { - attr_container_printf( - "Get attribute failed: invalid input arguments.\r\n"); - return NULL; - } - - if (!(attr_addr = attr_container_find_attr(attr_cont, key))) { - attr_container_printf("Get attribute failed: lookup key failed.\r\n"); - return false; - } - - /* key len + key + '\0' */ - return attr_addr + 2 + strlen(key) + 1; -} - -#define TEMPLATE_ATTR_BUF_TO_VALUE(attr, key, var_name) do {\ - jvalue val; \ - const char *addr = attr_container_get_attr(attr, key); \ - uint8_t type; \ - if (!addr) \ - return 0; \ - val.j = 0; \ - type = *(uint8_t*)addr++; \ - switch (type) { \ - case ATTR_TYPE_SHORT: \ - case ATTR_TYPE_INT: \ - case ATTR_TYPE_INT64: \ - case ATTR_TYPE_BYTE: \ - case ATTR_TYPE_UINT16: \ - case ATTR_TYPE_FLOAT: \ - case ATTR_TYPE_DOUBLE: \ - case ATTR_TYPE_BOOLEAN: \ - bh_memcpy_s(&val, sizeof(val.var_name), addr, 1 << (type & 3)); \ - break; \ - case ATTR_TYPE_STRING: \ - { \ - unsigned len= get_uint16(addr); \ - addr += 2; \ - if (len > sizeof(val.var_name)) \ - len = sizeof(val.var_name); \ - bh_memcpy_s(&val.var_name, sizeof(val.var_name), addr, len); \ - break; \ - } \ - case ATTR_TYPE_BYTEARRAY: \ - { \ - unsigned len= get_uint32(addr); \ - addr += 4; \ - if (len > sizeof(val.var_name)) \ - len = sizeof(val.var_name); \ - bh_memcpy_s(&val.var_name, sizeof(val.var_name), addr, len); \ - break; \ - } \ - } \ - return val.var_name; \ - } while (0) - -short attr_container_get_as_short(const attr_container_t *attr_cont, - const char *key) -{ - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, s); -} - -int attr_container_get_as_int(const attr_container_t *attr_cont, - const char *key) -{ - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i); -} - -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); -} - -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); -} - -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); -} - -float attr_container_get_as_float(const attr_container_t *attr_cont, - const char *key) -{ - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, f); -} - -double attr_container_get_as_double(const attr_container_t *attr_cont, - const char *key) -{ - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, d); -} - -bool attr_container_get_as_bool(const attr_container_t *attr_cont, - const char *key) -{ - TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, z); -} - -const int8_t* -attr_container_get_as_bytearray(const attr_container_t *attr_cont, - const char *key, unsigned *array_length) -{ - const char *addr = attr_container_get_attr(attr_cont, key); - uint8_t type; - uint32_t length; - - if (!addr) - return NULL; - - if (!array_length) { - attr_container_printf("Get attribute failed: invalid input arguments."); - return NULL; - } - - type = *(uint8_t*) addr++; - switch (type) { - case ATTR_TYPE_SHORT: - case ATTR_TYPE_INT: - case ATTR_TYPE_INT64: - case ATTR_TYPE_BYTE: - case ATTR_TYPE_UINT16: - case ATTR_TYPE_FLOAT: - case ATTR_TYPE_DOUBLE: - case ATTR_TYPE_BOOLEAN: - length = 1 << (type & 3); - break; - case ATTR_TYPE_STRING: - length = get_uint16(addr); - addr += 2; - break; - case ATTR_TYPE_BYTEARRAY: - length = get_uint32(addr); - addr += 4; - break; - default: - return NULL; - } - - *array_length = length; - return (const int8_t*) addr; -} - -char* -attr_container_get_as_string(const attr_container_t *attr_cont, const char *key) -{ - unsigned array_length; - return (char*) attr_container_get_as_bytearray(attr_cont, key, - &array_length); -} - -const char* -attr_container_get_tag(const attr_container_t *attr_cont) -{ - return attr_cont ? - attr_cont->buf + sizeof(uint32_t) + sizeof(uint16_t) : NULL; -} - -bool attr_container_contain_key(const attr_container_t *attr_cont, - const char *key) -{ - if (!attr_cont || !key || !strlen(key)) { - attr_container_printf( - "Check contain key failed: invalid input arguments.\r\n"); - return false; - } - return attr_container_find_attr(attr_cont, key) ? true : false; -} - -unsigned int attr_container_get_serialize_length( - const attr_container_t *attr_cont) -{ - const char *p; - - if (!attr_cont) { - attr_container_printf( - "Get container serialize length failed: invalid input arguments.\r\n"); - return 0; - } - - p = attr_cont->buf; - return sizeof(uint16_t) + get_uint32(p); -} - -bool attr_container_serialize(char *buf, const attr_container_t *attr_cont) -{ - const char *p; - uint16_t flags; - uint32_t length; - - if (!buf || !attr_cont) { - attr_container_printf( - "Container serialize failed: invalid input arguments.\r\n"); - return false; - } - - p = attr_cont->buf; - length = sizeof(uint16_t) + get_uint32(p); - bh_memcpy_s(buf, length, attr_cont, length); - /* Set readonly */ - flags = get_uint16((const char*) attr_cont); - set_uint16(buf, flags | (1 << ATTR_CONT_READONLY_SHIFT)); - - return true; -} - -bool attr_container_is_constant(const attr_container_t* attr_cont) -{ - uint16_t flags; - - if (!attr_cont) { - attr_container_printf( - "Container check const: invalid input arguments.\r\n"); - return false; - } - - flags = get_uint16((const char*) attr_cont); - return (flags & (1 << ATTR_CONT_READONLY_SHIFT)) ? true : false; -} - -void attr_container_dump(const attr_container_t *attr_cont) -{ - uint32_t total_length; - uint16_t attr_num, i, type; - const char *p, *tag, *key; - jvalue value; - - if (!attr_cont) - return; - - tag = attr_container_get_tag(attr_cont); - if (!tag) - return; - - attr_container_printf("Attribute container dump:\n"); - attr_container_printf("Tag: %s\n", tag); - - p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num); - if (!p) - return; - - attr_container_printf("Attribute list:\n"); - for (i = 0; i < attr_num; i++) { - key = p + 2; - /* Skip key len and key */ - p += 2 + get_uint16(p); - type = *p++; - attr_container_printf(" key: %s", key); - - switch (type) { - case ATTR_TYPE_SHORT: - bh_memcpy_s(&value.s, sizeof(int16_t), p, sizeof(int16_t)); - attr_container_printf(", type: short, value: 0x%x\n", - value.s & 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); - p += 4; - break; - case ATTR_TYPE_INT64: - bh_memcpy_s(&value.j, sizeof(uint64_t), p, sizeof(uint64_t)); - attr_container_printf(", type: int64, value: 0x%llx\n", (long long unsigned int)(value.j)); - 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); - 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); - p += 2; - 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); - p += 4; - break; - case ATTR_TYPE_DOUBLE: - bh_memcpy_s(&value.d, sizeof(double), p, sizeof(double)); - attr_container_printf(", type: double, value: %f\n", value.d); - p += 8; - break; - case ATTR_TYPE_BOOLEAN: - bh_memcpy_s(&value.z, 1, p, 1); - attr_container_printf(", type: bool, value: 0x%x\n", value.z); - p++; - break; - case ATTR_TYPE_STRING: - attr_container_printf(", type: string, value: %s\n", - p + sizeof(uint16_t)); - p += sizeof(uint16_t) + get_uint16(p); - break; - case ATTR_TYPE_BYTEARRAY: - attr_container_printf(", type: byte array, length: %d\n", - get_uint32(p)); - p += sizeof(uint32_t) + get_uint32(p); - break; - } - } - - attr_container_printf("\n"); -} - 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 deleted file mode 100644 index 52e0ebcc03..0000000000 --- a/core/app-framework/app-native-shared/bi-inc/attr_container.h +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _ATTR_CONTAINER_H_ -#define _ATTR_CONTAINER_H_ - -#include -#include -#include -#include -#include -#include -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Attribute type */ -enum { - ATTR_TYPE_BEGIN = 1, - ATTR_TYPE_SHORT = ATTR_TYPE_BEGIN, - ATTR_TYPE_INT, - ATTR_TYPE_INT64, - ATTR_TYPE_BYTE, - ATTR_TYPE_UINT16, - ATTR_TYPE_FLOAT, - ATTR_TYPE_DOUBLE, - ATTR_TYPE_BOOLEAN, - ATTR_TYPE_STRING, - ATTR_TYPE_BYTEARRAY, - ATTR_TYPE_END = ATTR_TYPE_BYTEARRAY -}; - -#define ATTR_CONT_READONLY_SHIFT 2 - -typedef struct attr_container { - /* container flag: - * bit0, bit1 denote the implemenation algorithm, 00: buffer, 01: link list - * bit2 denotes the readonly flag: 1 is readonly and attr cannot be set - */ - char flags[2]; - /** - * Buffer format - * for buffer implementation: - * buf length (4 bytes) - * tag length (2 bytes) - * tag - * attr num (2bytes) - * attr[0..n-1]: - * attr key length (2 bytes) - * attr type (1byte) - * attr value (length depends on attr type) - */ - char buf[1]; -} attr_container_t; - -/** - * Create attribute container - * - * @param tag tag of current attribute container - * - * @return the created attribute container, NULL if failed - */ -attr_container_t * -attr_container_create(const char *tag); - -/** - * Destroy attribute container - * - * @param attr_cont the attribute container to destroy - */ -void -attr_container_destroy(const attr_container_t *attr_cont); - -/** - * Set short 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_short(attr_container_t **p_attr_cont, const char *key, - short value); - -/** - * Set int 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_int(attr_container_t **p_attr_cont, const char *key, - int value); - -/** - * Set int64 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_int64(attr_container_t **p_attr_cont, const char *key, - int64_t value); - -/** - * Set byte 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_byte(attr_container_t **p_attr_cont, const char *key, - int8_t value); - -/** - * Set uint16 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_uint16(attr_container_t **p_attr_cont, const char *key, - uint16_t value); - -/** - * Set float 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_float(attr_container_t **p_attr_cont, const char *key, - float value); - -/** - * Set double 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_double(attr_container_t **p_attr_cont, const char *key, - double value); - -/** - * Set bool 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_bool(attr_container_t **p_attr_cont, const char *key, - bool value); - -/** - * Set string 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_string(attr_container_t **p_attr_cont, const char *key, - const char *value); - -/** - * Set bytearray 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 bytearray buffer - * @param length the bytearray length - * - * @return true if success, false otherwise - */ -bool -attr_container_set_bytearray(attr_container_t **p_attr_cont, const char *key, - const int8_t *value, unsigned length); - -/** - * Get tag of current attribute container - * - * @param attr_cont the attribute container - * - * @return tag of current attribute container - */ -const char* -attr_container_get_tag(const attr_container_t *attr_cont); - -/** - * Get attribute number of current attribute container - * - * @param attr_cont the attribute container - * - * @return attribute number of current attribute container - */ -uint16_t -attr_container_get_attr_num(const attr_container_t *attr_cont); - -/** - * Whether the attribute container contains an attribute key. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return true if key is contained in message, false otherwise - */ -bool -attr_container_contain_key(const attr_container_t *attr_cont, const char *key); - -/** - * Get attribute from attribute container and return it as short 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 - */ -short -attr_container_get_as_short(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. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the int value of the attribute, 0 if key isn't found - */ -int -attr_container_get_as_int(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. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the long value of the attribute, 0 if key isn't found - */ -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 byte 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_byte(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. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the char value of the attribute, 0 if key isn't found - */ -uint16_t -attr_container_get_as_uint16(const attr_container_t *attr_cont, - const char *key); - -/** - * Get attribute from attribute container and return it as float value, - * return 0 if attribute isn't found in attribute container. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the float value of the attribute, 0 if key isn't found - */ -float -attr_container_get_as_float(const attr_container_t *attr_cont, const char *key); - -/** - * Get attribute from attribute container and return it as double value, - * return 0 if attribute isn't found in attribute container. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the double value of the attribute, 0 if key isn't found - */ -double -attr_container_get_as_double(const attr_container_t *attr_cont, - const char *key); - -/** - * Get attribute from attribute container and return it as bool value, - * return false if attribute isn't found in attribute container. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the bool value of the attribute, 0 if key isn't found - */ -bool -attr_container_get_as_bool(const attr_container_t *attr_cont, const char *key); - -/** - * Get attribute from attribute container and return it as string value, - * return NULL if attribute isn't found in attribute container. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the string value of the attribute, NULL if key isn't found - */ -char* -attr_container_get_as_string(const attr_container_t *attr_cont, - const char *key); - -/** - * Get attribute from attribute container and return it as bytearray value, - * return 0 if attribute isn't found in attribute container. - * - * @param attr_cont the attribute container - * @param key the attribute key - * - * @return the bytearray value of the attribute, NULL if key isn't found - */ -const int8_t* -attr_container_get_as_bytearray(const attr_container_t *attr_cont, - const char *key, unsigned *array_length); - -/** - * Get the buffer size of attribute container - * - * @param attr_cont the attribute container - * - * @return the buffer size of attribute container - */ -unsigned -attr_container_get_serialize_length(const attr_container_t *attr_cont); - -/** - * Serialize attribute container to a buffer - * - * @param buf the buffer to receive the serialized data - * @param attr_cont the attribute container to be serialized - * - * @return true if success, false otherwise - */ -bool -attr_container_serialize(char *buf, const attr_container_t *attr_cont); - -/** - * Whether the attribute container is const, or set attribute isn't supported - * - * @param attr_cont the attribute container - * - * @return true if const, false otherwise - */ -bool -attr_container_is_constant(const attr_container_t* attr_cont); - -void -attr_container_dump(const attr_container_t *attr_cont); - -#ifndef attr_container_malloc -#define attr_container_malloc wa_malloc -#endif - -#ifndef attr_container_free -#define attr_container_free wa_free -#endif - -#ifndef attr_container_printf -#define attr_container_printf printf -#endif - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* end of _ATTR_CONTAINER_H_ */ - diff --git a/core/app-framework/app-native-shared/bi-inc/shared_utils.h b/core/app-framework/app-native-shared/bi-inc/shared_utils.h deleted file mode 100644 index 2ce84a1f03..0000000000 --- a/core/app-framework/app-native-shared/bi-inc/shared_utils.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _SHARED_UTILS_H_ -#define _SHARED_UTILS_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define FMT_ATTR_CONTAINER 99 -#define FMT_APP_RAW_BINARY 98 - -/* the request structure */ -typedef struct request { - // message id - uint32 mid; - - // url of the request - char *url; - - // action of the request, can be PUT/GET/POST/DELETE - int action; - - // payload format, currently only support attr_container_t type - int fmt; - - // payload of the request, currently only support attr_container_t type - void *payload; - - //length in bytes of the payload - int payload_len; - - //sender of the request - unsigned long sender; -} request_t; - -/* the response structure */ -typedef struct response { - // message id - uint32 mid; - - // status of the response - int status; - - // payload format - int fmt; - - // payload of the response, - void *payload; - - //length in bytes of the payload - int payload_len; - - //receiver of the response - unsigned long reciever; -} response_t; - -int -check_url_start(const char* url, int url_len, const char * leading_str); - -bool -match_url(char * pattern, char * matched); - -char * -find_key_value(char * buffer, int buffer_len, char * key, char * value, - int value_len, char delimiter); - -request_t * -clone_request(request_t *request); - -void -request_cleaner(request_t *request); - -response_t * -clone_response(response_t * response); - -void -response_cleaner(response_t * response); - -/** - * @brief Set fields of response. - * - * @param response pointer of the response to be set - * @param status status of response - * @param fmt format of the response payload - * @param payload payload of the response - * @param payload_len length in bytes of the response payload - * - * @return pointer to the response - * - * @warning the response pointer MUST NOT be NULL - */ -response_t * -set_response(response_t * response, int status, int fmt, - const char *payload, int payload_len); - -/** - * @brief Make a response for a request. - * - * @param request pointer of the request - * @param response pointer of the response to be made - * - * @return pointer to the response - * - * @warning the request and response pointers MUST NOT be NULL - */ -response_t * -make_response_for_request(request_t * request, response_t * response); - -/** - * @brief Initialize a request. - * - * @param request pointer of the request to be initialized - * @param url url of the request - * @param action action of the request - * @param fmt format of the request payload - * @param payload payload of the request - * @param payload_len length in bytes of the request payload - * - * @return pointer to the request - * - * @warning the request pointer MUST NOT be NULL - */ -request_t * -init_request(request_t * request, char *url, int action, int fmt, - void *payload, int payload_len); - -char * -pack_request(request_t *request, int * size); - -request_t * -unpack_request(char * packet, int size, request_t * request); - -char * -pack_response(response_t *response, int * size); - -response_t * -unpack_response(char * packet, int size, response_t * response); - -void -free_req_resp_packet(char * packet); - - - -#ifdef __cplusplus -} -#endif - -#endif /* end of _SHARED_UTILS_H_ */ diff --git a/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h b/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h deleted file mode 100644 index 2fee5b38f3..0000000000 --- a/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_SHARED_UTILS_H -#define WAMR_GRAPHIC_LIBRARY_SHARED_UTILS_H - -#include "bh_platform.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include "lv_conf.h" - -typedef lv_coord_t wgl_coord_t; /* lv_coord_t is defined in lv_conf.h */ -typedef void * wgl_font_user_data_t; - -/** - * Represents a point on the screen. - */ -typedef struct -{ - lv_coord_t x; - lv_coord_t y; -} wgl_point_t; - -/** Represents an area of the screen. */ -typedef struct -{ - lv_coord_t x1; - lv_coord_t y1; - lv_coord_t x2; - lv_coord_t y2; -} wgl_area_t; - - -/** Describes the properties of a glyph. */ -typedef struct -{ - uint16_t adv_w; /**< The glyph needs this space. Draw the next glyph after this width. 8 bit integer, 4 bit fractional */ - uint8_t box_w; /**< Width of the glyph's bounding box*/ - uint8_t box_h; /**< Height of the glyph's bounding box*/ - int8_t ofs_x; /**< x offset of the bounding box*/ - int8_t ofs_y; /**< y offset of the bounding box*/ - uint8_t bpp; /**< Bit-per-pixel: 1, 2, 4, 8*/ -}wgl_font_glyph_dsc_t; - -/*Describe the properties of a font*/ -typedef struct _wgl_font_struct -{ - /** Get a glyph's descriptor from a font*/ - bool (*get_glyph_dsc)(const struct _wgl_font_struct *, wgl_font_glyph_dsc_t *, uint32_t letter, uint32_t letter_next); - - /** Get a glyph's bitmap from a font*/ - const uint8_t * (*get_glyph_bitmap)(const struct _wgl_font_struct *, uint32_t); - - /*Pointer to the font in a font pack (must have the same line height)*/ - uint8_t line_height; /**< The real line height where any text fits*/ - uint8_t base_line; /**< Base line measured from the top of the line_height*/ - void * dsc; /**< Store implementation specific data here*/ -#if LV_USE_USER_DATA - wgl_font_user_data_t user_data; /**< Custom user data for font. */ -#endif -} wgl_font_t; - -#if LV_COLOR_DEPTH == 1 -#define LV_COLOR_SIZE 8 -#elif LV_COLOR_DEPTH == 8 -#define LV_COLOR_SIZE 8 -#elif LV_COLOR_DEPTH == 16 -#define LV_COLOR_SIZE 16 -#elif LV_COLOR_DEPTH == 32 -#define LV_COLOR_SIZE 32 -#else -#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!" -#endif - -/********************** - * TYPEDEFS - **********************/ - -typedef union -{ - uint8_t blue : 1; - uint8_t green : 1; - uint8_t red : 1; - uint8_t full : 1; -} wgl_color1_t; - -typedef union -{ - struct - { - uint8_t blue : 2; - uint8_t green : 3; - uint8_t red : 3; - } ch; - uint8_t full; -} wgl_color8_t; - -typedef union -{ - struct - { -#if LV_COLOR_16_SWAP == 0 - uint16_t blue : 5; - uint16_t green : 6; - uint16_t red : 5; -#else - uint16_t green_h : 3; - uint16_t red : 5; - uint16_t blue : 5; - uint16_t green_l : 3; -#endif - } ch; - uint16_t full; -} wgl_color16_t; - -typedef union -{ - struct - { - uint8_t blue; - uint8_t green; - uint8_t red; - uint8_t alpha; - } ch; - uint32_t full; -} wgl_color32_t; - -#if LV_COLOR_DEPTH == 1 -typedef uint8_t wgl_color_int_t; -typedef wgl_color1_t wgl_color_t; -#elif LV_COLOR_DEPTH == 8 -typedef uint8_t wgl_color_int_t; -typedef wgl_color8_t wgl_color_t; -#elif LV_COLOR_DEPTH == 16 -typedef uint16_t wgl_color_int_t; -typedef wgl_color16_t wgl_color_t; -#elif LV_COLOR_DEPTH == 32 -typedef uint32_t wgl_color_int_t; -typedef wgl_color32_t wgl_color_t; -#else -#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!" -#endif - -typedef uint8_t wgl_opa_t; - - - -/*Border types (Use 'OR'ed values)*/ -enum { - WGL_BORDER_NONE = 0x00, - WGL_BORDER_BOTTOM = 0x01, - WGL_BORDER_TOP = 0x02, - WGL_BORDER_LEFT = 0x04, - WGL_BORDER_RIGHT = 0x08, - WGL_BORDER_FULL = 0x0F, - WGL_BORDER_INTERNAL = 0x10, /**< FOR matrix-like objects (e.g. Button matrix)*/ -}; -typedef uint8_t wgl_border_part_t; - -/*Shadow types*/ -enum { - WGL_SHADOW_BOTTOM = 0, /**< Only draw bottom shadow */ - WGL_SHADOW_FULL, /**< Draw shadow on all sides */ -}; -typedef uint8_t wgl_shadow_type_t; - -/** - * Objects in LittlevGL can be assigned a style - which holds information about - * how the object should be drawn. - * - * This allows for easy customization without having to modify the object's design - * function. - */ -typedef struct -{ - uint8_t glass : 1; /**< 1: Do not inherit this style*/ - - /** Object background. */ - struct - { - wgl_color_t main_color; /**< Object's main background color. */ - wgl_color_t grad_color; /**< Second color. If not equal to `main_color` a gradient will be drawn for the background. */ - wgl_coord_t radius; /**< Object's corner radius. You can use #WGL_RADIUS_CIRCLE if you want to draw a circle. */ - wgl_opa_t opa; /**< Object's opacity (0-255). */ - - struct - { - wgl_color_t color; /**< Border color */ - wgl_coord_t width; /**< Border width */ - wgl_border_part_t part; /**< Which borders to draw */ - wgl_opa_t opa; /**< Border opacity. */ - } border; - - - struct - { - wgl_color_t color; - wgl_coord_t width; - wgl_shadow_type_t type; /**< Which parts of the shadow to draw */ - } shadow; - - struct - { - wgl_coord_t top; - wgl_coord_t bottom; - wgl_coord_t left; - wgl_coord_t right; - wgl_coord_t inner; - } padding; - } body; - - /** Style for text drawn by this object. */ - struct - { - wgl_color_t color; /**< Text color */ - wgl_color_t sel_color; /**< Text selection background color. */ - const wgl_font_t * font; - wgl_coord_t letter_space; /**< Space between letters */ - wgl_coord_t line_space; /**< Space between lines (vertical) */ - wgl_opa_t opa; /**< Text opacity */ - } text; - - /**< Style of images. */ - struct - { - wgl_color_t color; /**< Color to recolor the image with */ - wgl_opa_t intense; /**< Opacity of recoloring (0 means no recoloring) */ - wgl_opa_t opa; /**< Opacity of whole image */ - } image; - - /**< Style of lines (not borders). */ - struct - { - wgl_color_t color; - wgl_coord_t width; - wgl_opa_t opa; - uint8_t rounded : 1; /**< 1: rounded line endings*/ - } line; -} wgl_style_t; - - - -/* Object native function IDs */ -enum { - OBJ_FUNC_ID_DEL, - OBJ_FUNC_ID_DEL_ASYNC, - OBJ_FUNC_ID_CLEAN, - OBJ_FUNC_ID_SET_EVT_CB, - OBJ_FUNC_ID_ALIGN, - - /* Number of functions */ - _OBJ_FUNC_ID_NUM, -}; - -/* Button native function IDs */ -enum { - BTN_FUNC_ID_CREATE, - BTN_FUNC_ID_SET_TOGGLE, - BTN_FUNC_ID_SET_STATE, - BTN_FUNC_ID_TOGGLE, - BTN_FUNC_ID_SET_INK_IN_TIME, - BTN_FUNC_ID_SET_INK_WAIT_TIME, - BTN_FUNC_ID_SET_INK_OUT_TIME, - BTN_FUNC_ID_GET_STATE, - BTN_FUNC_ID_GET_TOGGLE, - BTN_FUNC_ID_GET_INK_IN_TIME, - BTN_FUNC_ID_GET_INK_WAIT_TIME, - BTN_FUNC_ID_GET_INK_OUT_TIME, - /* Number of functions */ - _BTN_FUNC_ID_NUM, -}; - -/* Check box native function IDs */ -enum { - CB_FUNC_ID_CREATE, - CB_FUNC_ID_SET_TEXT, - CB_FUNC_ID_SET_STATIC_TEXT, - CB_FUNC_ID_GET_TEXT, - CB_FUNC_ID_GET_TEXT_LENGTH, - - /* Number of functions */ - _CB_FUNC_ID_NUM, -}; - -/* List native function IDs */ -enum { - LIST_FUNC_ID_CREATE, - LIST_FUNC_ID_ADD_BTN, - - /* Number of functions */ - _LIST_FUNC_ID_NUM, -}; - -/* Label native function IDs */ -enum { - LABEL_FUNC_ID_CREATE, - LABEL_FUNC_ID_SET_TEXT, - LABEL_FUNC_ID_SET_ARRAY_TEXT, - LABEL_FUNC_ID_SET_STATIC_TEXT, - LABEL_FUNC_ID_SET_LONG_MODE, - LABEL_FUNC_ID_SET_ALIGN, - LABEL_FUNC_ID_SET_RECOLOR, - LABEL_FUNC_ID_SET_BODY_DRAW, - LABEL_FUNC_ID_SET_ANIM_SPEED, - LABEL_FUNC_ID_SET_TEXT_SEL_START, - LABEL_FUNC_ID_SET_TEXT_SEL_END, - LABEL_FUNC_ID_GET_TEXT, - LABEL_FUNC_ID_GET_TEXT_LENGTH, - LABEL_FUNC_ID_GET_LONG_MODE, - LABEL_FUNC_ID_GET_ALIGN, - LABEL_FUNC_ID_GET_RECOLOR, - LABEL_FUNC_ID_GET_BODY_DRAW, - LABEL_FUNC_ID_GET_ANIM_SPEED, - LABEL_FUNC_ID_GET_LETTER_POS, - LABEL_FUNC_ID_GET_TEXT_SEL_START, - LABEL_FUNC_ID_GET_TEXT_SEL_END, - LABEL_FUNC_ID_INS_TEXT, - LABEL_FUNC_ID_CUT_TEXT, - /* Number of functions */ - _LABEL_FUNC_ID_NUM, -}; - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_SHARED_UTILS_H */ diff --git a/core/app-framework/app-native-shared/native_interface.cmake b/core/app-framework/app-native-shared/native_interface.cmake deleted file mode 100644 index 48ebe0a335..0000000000 --- a/core/app-framework/app-native-shared/native_interface.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (NATIVE_INTERFACE_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${NATIVE_INTERFACE_DIR}) - - -file (GLOB_RECURSE source_all ${NATIVE_INTERFACE_DIR}/*.c) - -set (NATIVE_INTERFACE_SOURCE ${source_all}) - -set (WASM_APP_BI_INC_DIR "${NATIVE_INTERFACE_DIR}/bi-inc") -LIST (APPEND RUNTIME_LIB_HEADER_LIST "${NATIVE_INTERFACE_DIR}/native_interface.h") - diff --git a/core/app-framework/app-native-shared/native_interface.h b/core/app-framework/app-native-shared/native_interface.h deleted file mode 100644 index cb7e7e9639..0000000000 --- a/core/app-framework/app-native-shared/native_interface.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _NATIVE_INTERFACE_H_ -#define _NATIVE_INTERFACE_H_ - -/* Note: the bh_plaform.h is the only head file separately - implemented by both [app] and [native] worlds */ -#include "bh_platform.h" -#include "wasm_export.h" - -#define get_module_inst(exec_env) \ - wasm_runtime_get_module_inst(exec_env) - -#define validate_app_addr(offset, size) \ - wasm_runtime_validate_app_addr(module_inst, offset, size) - -#define validate_app_str_addr(offset) \ - wasm_runtime_validate_app_str_addr(module_inst, offset) - -#define addr_app_to_native(offset) \ - wasm_runtime_addr_app_to_native(module_inst, offset) - -#define addr_native_to_app(ptr) \ - wasm_runtime_addr_native_to_app(module_inst, ptr) - -#define module_malloc(size) \ - wasm_runtime_module_malloc(module_inst, size) - -#define module_free(offset) \ - wasm_runtime_module_free(module_inst, offset) - -/*char *wa_strdup(const char *);*/ - -/* - * request/response interfaces - */ - -bool -wasm_response_send(wasm_exec_env_t exec_env, - int32 buffer_offset, int size); -void -wasm_register_resource(wasm_exec_env_t exec_env, - int32 url_offset); -void -wasm_post_request(wasm_exec_env_t exec_env, - int32 buffer_offset, int size); -void -wasm_sub_event(wasm_exec_env_t exec_env, - int32 url_offset); - -/* - * sensor interfaces - */ - -bool -wasm_sensor_config(wasm_exec_env_t exec_env, - uint32 sensor, int interval, int bit_cfg, int delay); -uint32 -wasm_sensor_open(wasm_exec_env_t exec_env, - int32 name_offset, int instance); -bool -wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, - uint32 sensor, - int32 buffer_offset, int len); -bool -wasm_sensor_close(wasm_exec_env_t exec_env, - uint32 sensor); - -/* - * timer interfaces - */ - -typedef unsigned int timer_id_t; - -timer_id_t -wasm_create_timer(wasm_exec_env_t exec_env, - int interval, bool is_period, bool auto_start); -void -wasm_timer_destroy(wasm_exec_env_t exec_env, timer_id_t timer_id); -void -wasm_timer_cancel(wasm_exec_env_t exec_env, timer_id_t timer_id); -void -wasm_timer_restart(wasm_exec_env_t exec_env, - timer_id_t timer_id, int interval); -uint32 -wasm_get_sys_tick_ms(wasm_exec_env_t exec_env); - -/* - * connection interfaces - */ - -uint32 -wasm_open_connection(wasm_exec_env_t exec_env, - int32 name_offset, int32 args_offset, uint32 len); -void -wasm_close_connection(wasm_exec_env_t exec_env, - uint32 handle); -int -wasm_send_on_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 data_offset, uint32 len); -bool -wasm_config_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 cfg_offset, uint32 len); - -/** - * gui interfaces - */ - -void -wasm_obj_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); - -void -wasm_btn_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); - -void -wasm_label_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); - -void -wasm_cb_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); - -void -wasm_list_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); - -#endif /* end of _NATIVE_INTERFACE_H */ - diff --git a/core/app-framework/app-native-shared/restful_utils.c b/core/app-framework/app-native-shared/restful_utils.c deleted file mode 100644 index 5edc470810..0000000000 --- a/core/app-framework/app-native-shared/restful_utils.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include -#include - -#include "bi-inc/shared_utils.h" - -/* Serialization of request and response message - * - * Choices: - * We considered a few options: - * 1. coap - * 2. flatbuffer - * 3. cbor - * 4. attr-containers of our own - * 5. customized serialization for request/response - * - * Now we choose the #5 mainly because we need to quickly get the URL for dispatching - * and sometimes we want to change the URL in the original packet. the request format: - * fixed part: version: (1 byte), code (1 byte), fmt(2 byte), mid (4 bytes), sender_id(4 bytes), url_len(2 bytes), payload_len(4bytes) - * dynamic part: url (bytes in url_len), payload - * - * response format: - * fixed part: (1 byte), code (1 byte), fmt(2 byte), mid (4 bytes), sender_id(4 bytes), payload_len(4bytes) - * dynamic part: payload - */ -#define REQUES_PACKET_VER 1 -#define REQUEST_PACKET_FIX_PART_LEN 18 -#define REQUEST_PACKET_URL_OFFSET REQUEST_PACKET_FIX_PART_LEN -#define REQUEST_PACKET_URL_LEN *((uint16*)( (char*) buffer + 12))) //!!! to ensure little endian -#define REQUEST_PACKET_PAYLOAD_LEN *((uint32*)( (char*) buffer + 14))) //!!! to ensure little endian -#define REQUEST_PACKET_URL(buffer) ((char*) buffer + REQUEST_PACKET_URL_OFFSET) -#define REQUEST_PACKET_PAYLOAD(buffer) ((char*) buffer + REQUEST_PACKET_URL_OFFSET + REQUEST_PACKET_URL_LEN(buffer)) - -#define RESPONSE_PACKET_FIX_PART_LEN 16 - -char * pack_request(request_t *request, int * size) -{ - int url_len = strlen(request->url) + 1; - int len = REQUEST_PACKET_FIX_PART_LEN + url_len + request->payload_len; - char * packet = (char*) wa_malloc(len); - if (packet == NULL) - return NULL; - - // TODO: ensure little endian for words and dwords - *packet = REQUES_PACKET_VER; - *((uint8*) (packet + 1)) = request->action; - *((uint16*) (packet + 2)) = htons(request->fmt); - *((uint32*) (packet + 4)) = htonl(request->mid); - *((uint32*) (packet + 8)) = htonl(request->sender); - *((uint16*) (packet + 12)) = htons(url_len); - *((uint32*) (packet + 14)) = htonl(request->payload_len); - strcpy(packet + REQUEST_PACKET_URL_OFFSET, request->url); - memcpy(packet + REQUEST_PACKET_URL_OFFSET + url_len, request->payload, - request->payload_len); - - *size = len; - return packet; -} - -void free_req_resp_packet(char * packet) -{ - wa_free(packet); -} - -request_t * unpack_request(char * packet, int size, request_t * request) -{ - if (*packet != REQUES_PACKET_VER) { - printf("version fail\n"); - return NULL; - } - if (size < REQUEST_PACKET_FIX_PART_LEN) { - printf("size error: %d\n", size); - return NULL; - } - uint16 url_len = ntohs(*((uint16*) (packet + 12))); - uint32 payload_len = ntohl(*((uint32*) (packet + 14))); - - if (size != ( REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) { - printf("size error: %d, expect: %d\n", size, - REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len); - return NULL; - } - if (*(packet + REQUEST_PACKET_FIX_PART_LEN + url_len - 1) != 0) { - printf("url not end with 0\n"); - return NULL; - } - - request->action = *((uint8*) (packet + 1)); - request->fmt = ntohs(*((uint16*) (packet + 2))); - request->mid = ntohl(*((uint32*) (packet + 4))); - request->sender = ntohl(*((uint32*) (packet + 8))); - request->payload_len = payload_len; - request->url = REQUEST_PACKET_URL(packet); - if (payload_len > 0) - request->payload = packet + REQUEST_PACKET_URL_OFFSET + url_len; - else - request->payload = NULL; - - return request; -} - -char * pack_response(response_t *response, int * size) -{ - int len = RESPONSE_PACKET_FIX_PART_LEN + response->payload_len; - char * packet = (char*) wa_malloc(len); - if (packet == NULL) - return NULL; - - // TODO: ensure little endian for words and dwords - *packet = REQUES_PACKET_VER; - *((uint8*) (packet + 1)) = response->status; - *((uint16*) (packet + 2)) = htons(response->fmt); - *((uint32*) (packet + 4)) = htonl(response->mid); - *((uint32*) (packet + 8)) = htonl(response->reciever); - *((uint32*) (packet + 12)) = htonl(response->payload_len); - memcpy(packet + RESPONSE_PACKET_FIX_PART_LEN, response->payload, - response->payload_len); - - *size = len; - return packet; -} - -response_t * unpack_response(char * packet, int size, response_t * response) -{ - if (*packet != REQUES_PACKET_VER) - return NULL; - if (size < RESPONSE_PACKET_FIX_PART_LEN) - return NULL; - uint32 payload_len = ntohl(*((uint32*) (packet + 12))); - if (size != ( RESPONSE_PACKET_FIX_PART_LEN + payload_len)) - return NULL; - - response->status = *((uint8*) (packet + 1)); - response->fmt = ntohs(*((uint16*) (packet + 2))); - response->mid = ntohl(*((uint32*) (packet + 4))); - response->reciever = ntohl(*((uint32*) (packet + 8))); - response->payload_len = payload_len; - if (payload_len > 0) - response->payload = packet + RESPONSE_PACKET_FIX_PART_LEN; - else - response->payload = NULL; - - return response; -} - -request_t *clone_request(request_t *request) -{ - /* deep clone */ - request_t *req = (request_t *) wa_malloc(sizeof(request_t)); - if (req == NULL) - return NULL; - - memset(req, 0, sizeof(*req)); - req->action = request->action; - req->fmt = request->fmt; - req->url = wa_strdup(request->url); - req->sender = request->sender; - req->mid = request->mid; - - if (req->url == NULL) - goto fail; - - req->payload_len = request->payload_len; - - if (request->payload_len) { - req->payload = (char *) wa_malloc(request->payload_len); - if (!req->payload) - goto fail; - memcpy(req->payload, request->payload, request->payload_len); - } else { - // when payload_len is 0, the payload may be used for carrying some handle or integer - req->payload = request->payload; - } - - return req; - -fail: - request_cleaner(req); - return NULL; -} - -void request_cleaner(request_t *request) -{ - if (request->url != NULL) - wa_free(request->url); - if (request->payload != NULL && request->payload_len > 0) - wa_free(request->payload); - - wa_free(request); -} - -void response_cleaner(response_t * response) -{ - if (response->payload != NULL && response->payload_len > 0) - wa_free(response->payload); - - wa_free(response); -} - -response_t * clone_response(response_t * response) -{ - response_t *clone = (response_t *) wa_malloc(sizeof(response_t)); - if (clone == NULL) - return NULL; - - memset(clone, 0, sizeof(*clone)); - clone->fmt = response->fmt; - clone->mid = response->mid; - clone->status = response->status; - clone->reciever = response->reciever; - clone->payload_len = response->payload_len; - if (clone->payload_len) { - clone->payload = (char *) wa_malloc(response->payload_len); - if (!clone->payload) - goto fail; - memcpy(clone->payload, response->payload, response->payload_len); - } else { - // when payload_len is 0, the payload may be used for carrying some handle or integer - clone->payload = response->payload; - } - return clone; - -fail: - response_cleaner(clone); - return NULL; -} - -response_t * set_response(response_t * response, int status, int fmt, - const char *payload, int payload_len) -{ - response->payload = (void *)payload; - response->payload_len = payload_len; - response->status = status; - response->fmt = fmt; - return response; -} - -response_t * make_response_for_request(request_t * request, - response_t * response) -{ - response->mid = request->mid; - response->reciever = request->sender; - - return response; -} - -request_t * init_request(request_t * request, char *url, int action, int fmt, - void *payload, int payload_len) -{ - static unsigned int mid = 0; - request->url = url; - request->action = action; - request->fmt = fmt; - request->payload = payload; - request->payload_len = payload_len; - request->mid = ++mid; - - return request; -} - -/* - check if the "url" is starting with "leading_str" - return: 0 - not match; >0 - the offset of matched url, include any "/" at the end - notes: - 1. it ensures the leading_str "/abc" can pass "/abc/cde" and "/abc/, but fail "/ab" and "/abcd". - leading_str "/abc/" can pass "/abc" - 2. it omit the '/' at the first char - 3. it ensure the leading_str "/abc" can pass "/abc?cde - */ - -int check_url_start(const char* url, int url_len, const char * leading_str) -{ - int offset = 0; - if (*leading_str == '/') - leading_str++; - if (url_len > 0 && *url == '/') { - url_len--; - url++; - offset++; - } - - int len = strlen(leading_str); - if (len == 0) - return 0; - - // ensure leading_str not end with "/" - if (leading_str[len - 1] == '/') { - len--; - if (len == 0) - return 0; - } - - // equal length - if (url_len == len) { - if (memcmp(url, leading_str, url_len) == 0) { - return (offset + len); - } else { - return 0; - } - } - - if (url_len < len) - return 0; - - else if (memcmp(url, leading_str, len) != 0) - return 0; - - else if (url[len] != '/' && url[len] != '?') - return 0; - else - return (offset + len + 1); -} - -// * @pattern: -// * sample 1: /abcd, match /abcd only -// * sample 2: /abcd/ match match "/abcd" and "/abcd/*" -// * sample 3: /abcd*, match any url started with "/abcd" -// * sample 4: /abcd/*, exclude "/abcd" - -bool match_url(char * pattern, char * matched) -{ - if (*pattern == '/') - pattern++; - if (*matched == '/') - matched++; - - int matched_len = strlen(matched); - if (matched_len == 0) - return false; - - if (matched[matched_len - 1] == '/') { - matched_len--; - if (matched_len == 0) - return false; - } - - int len = strlen(pattern); - if (len == 0) - return false; - - if (pattern[len - 1] == '/') { - len--; - if (strncmp(pattern, matched, len) != 0) - return false; - - if (len == matched_len) - return true; - - if (matched_len > len && matched[len] == '/') - return true; - - return false; - - } else if (pattern[len - 1] == '*') { - if (pattern[len - 2] == '/') { - if (strncmp(pattern, matched, len - 1) == 0) - return true; - - else - return false; - } else { - return (strncmp(pattern, matched, len - 1) == 0); - } - } else { - return (strcmp(pattern, matched) == 0); - } -} - -/* - * get the value of the key from following format buffer: - * key1=value1;key2=value2;key3=value3 - */ -char * find_key_value(char * buffer, int buffer_len, char * key, char * value, - int value_len, char delimiter) -{ - char * p = buffer; - int remaining = buffer_len; - int key_len = strlen(key); - - while (*p != 0 && remaining > 0) { - while (*p == ' ' || *p == delimiter) { - p++; - remaining--; - } - - if (remaining <= key_len) - return NULL; - - // find the key - if (0 == strncmp(p, key, key_len) && p[key_len] == '=') { - p += (key_len + 1); - remaining -= (key_len + 1); - char * v = value; - memset(value, 0, value_len); - value_len--; // ensure last char is 0 - while (*p != delimiter && remaining > 0 && value_len > 0) { - *v++ = *p++; - remaining--; - value_len--; - } - return value; - } - - // goto next key - while (*p != delimiter && remaining > 0) { - p++; - remaining--; - } - } - - return NULL; -} diff --git a/core/app-framework/app_framework.cmake b/core/app-framework/app_framework.cmake deleted file mode 100644 index 68439d8748..0000000000 --- a/core/app-framework/app_framework.cmake +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (APP_FRAMEWORK_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) - -# app-native-shared and base are required -include (${APP_FRAMEWORK_ROOT_DIR}/app-native-shared/native_interface.cmake) -LIST (APPEND WASM_APP_SOURCE_ALL ${NATIVE_INTERFACE_SOURCE}) - -MACRO(SUBDIRLIST result curdir) - FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) - SET(dirlist "") - FOREACH(child ${children}) - IF(IS_DIRECTORY ${curdir}/${child}) - LIST(APPEND dirlist ${child}) - ENDIF() - ENDFOREACH() - SET(${result} ${dirlist}) -ENDMACRO() - -function (add_module_native arg) - message ("Add native module ${ARGV0}") - include (${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/native/wasm_lib.cmake) - - file (GLOB header - ${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/native/*.h - ${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/native/*.inl - ) - LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) - set (RUNTIME_LIB_HEADER_LIST ${RUNTIME_LIB_HEADER_LIST} PARENT_SCOPE) - - LIST (APPEND WASM_APP_LIB_SOURCE_ALL ${WASM_APP_LIB_CURRENT_SOURCE}) - set (WASM_APP_LIB_SOURCE_ALL ${WASM_APP_LIB_SOURCE_ALL} PARENT_SCOPE) - - # VARIABLES in function are only used in this scope, - # set PARENT_SCOPE to pass to top CMakeLists - set (WASM_LIB_BASE_SOURCE ${WASM_LIB_BASE_SOURCE} PARENT_SCOPE) -endfunction () - -function (add_module_app arg) - message ("Add app module ${ARGV0}") - include (${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/app/wasm_app.cmake) - - LIST (APPEND WASM_APP_WA_INC_DIR_LIST "${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/app/wa-inc") - set (WASM_APP_WA_INC_DIR_LIST ${WASM_APP_WA_INC_DIR_LIST} PARENT_SCOPE) - - LIST (APPEND WASM_APP_NAME ${ARGV0}) - set (WASM_APP_NAME ${WASM_APP_NAME} PARENT_SCOPE) - - LIST (APPEND WASM_APP_SOURCE_ALL ${WASM_APP_CURRENT_SOURCE}) - set (WASM_APP_SOURCE_ALL ${WASM_APP_SOURCE_ALL} PARENT_SCOPE) -endfunction () - -if ("${WAMR_BUILD_APP_LIST}" STREQUAL "WAMR_APP_BUILD_ALL") - # add all modules under this folder - SUBDIRLIST(SUBDIRS ${APP_FRAMEWORK_ROOT_DIR}) - - FOREACH(subdir ${SUBDIRS}) - if ("${subdir}" STREQUAL "app-native-shared") - continue() - endif () - if ("${subdir}" STREQUAL "template") - continue() - endif () - - if ( NOT DEFINED APP_FRAMEWORK_INCLUDE_TYPE ) - add_module_native (${subdir}) - else () - add_module_app (${subdir}) - endif () - ENDFOREACH() - -else () - # add each module in the list - FOREACH (dir IN LISTS WAMR_BUILD_APP_LIST) - string(REPLACE "WAMR_APP_BUILD_" "" dir ${dir}) - string(TOLOWER ${dir} dir) - - if ( NOT DEFINED APP_FRAMEWORK_INCLUDE_TYPE ) - add_module_native (${dir}) - else () - add_module_app (${dir}) - endif () - ENDFOREACH (dir) - -endif() \ No newline at end of file diff --git a/core/app-framework/base/app/bh_platform.c b/core/app-framework/base/app/bh_platform.c deleted file mode 100644 index 37b828e0e3..0000000000 --- a/core/app-framework/base/app/bh_platform.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include -#include -#include - -/* - * - * - */ - -static bool is_little_endian() -{ - long i = 0x01020304; - unsigned char* c = (unsigned char*) &i; - return (*c == 0x04) ? true : false; -} - -static void swap32(uint8* pData) -{ - uint8 value = *pData; - *pData = *(pData + 3); - *(pData + 3) = value; - - value = *(pData + 1); - *(pData + 1) = *(pData + 2); - *(pData + 2) = value; -} - -static void swap16(uint8* pData) -{ - uint8 value = *pData; - *(pData) = *(pData + 1); - *(pData + 1) = value; -} - -uint32 htonl(uint32 value) -{ - uint32 ret; - if (is_little_endian()) { - ret = value; - swap32((uint8*) &ret); - return ret; - } - - return value; -} - -uint32 ntohl(uint32 value) -{ - return htonl(value); -} - -uint16 htons(uint16 value) -{ - uint16 ret; - if (is_little_endian()) { - ret = value; - swap16((uint8 *)&ret); - return ret; - } - - return value; -} - -uint16 ntohs(uint16 value) -{ - return htons(value); -} - -char *wa_strdup(const char *s) -{ - char *s1 = NULL; - if (s && (s1 = wa_malloc(strlen(s) + 1))) - memcpy(s1, s, strlen(s) + 1); - return s1; -} - -#define RSIZE_MAX 0x7FFFFFFF -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n) -{ - char *dest = (char*) s1; - char *src = (char*) s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} diff --git a/core/app-framework/base/app/bh_platform.h b/core/app-framework/base/app/bh_platform.h deleted file mode 100755 index d44522c023..0000000000 --- a/core/app-framework/base/app/bh_platform.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef DEPS_IWASM_APP_LIBS_BASE_BH_PLATFORM_H_ -#define DEPS_IWASM_APP_LIBS_BASE_BH_PLATFORM_H_ - -#include - -typedef unsigned char uint8; -typedef char int8; -typedef unsigned short uint16; -typedef short int16; -typedef unsigned int uint32; -typedef int int32; - -#ifndef NULL -# define NULL ((void*) 0) -#endif - -#ifndef __cplusplus -#define true 1 -#define false 0 -#define inline __inline -#endif - -// all wasm-app<->native shared source files should use wa_malloc/wa_free. -// they will be mapped to different implementations in each side -#ifndef wa_malloc -#define wa_malloc malloc -#endif - -#ifndef wa_free -#define wa_free free -#endif - -char *wa_strdup(const char *s); - -uint32 htonl(uint32 value); -uint32 ntohl(uint32 value); -uint16 htons(uint16 value); -uint16 ntohs(uint16 value); - -int -b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n); - -#endif /* DEPS_IWASM_APP_LIBS_BASE_BH_PLATFORM_H_ */ diff --git a/core/app-framework/base/app/req_resp_api.h b/core/app-framework/base/app/req_resp_api.h deleted file mode 100644 index cbddab5f7f..0000000000 --- a/core/app-framework/base/app/req_resp_api.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _REQ_RESP_API_H_ -#define _REQ_RESP_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -bool -wasm_response_send(const char *buf, int size); - -void -wasm_register_resource(const char *url); - -void -wasm_post_request(const char *buf, int size); - -void -wasm_sub_event(const char *url); - -#ifdef __cplusplus -} -#endif - -#endif /* end of _REQ_RESP_API_H_ */ - diff --git a/core/app-framework/base/app/request.c b/core/app-framework/base/app/request.c deleted file mode 100644 index 13fa22a8ff..0000000000 --- a/core/app-framework/base/app/request.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bi-inc/attr_container.h" -#include "wa-inc/request.h" -#include "wa-inc/timer_wasm_app.h" -#include "bi-inc/shared_utils.h" -#include "wasm_app.h" -#include "req_resp_api.h" -#include "timer_api.h" - -#define TRANSACTION_TIMEOUT_MS 5000 - -typedef enum { - Reg_Event, Reg_Request -} reg_type_t; - -typedef struct _res_register { - struct _res_register *next; - const char * url; - reg_type_t reg_type; - void (*request_handler)(request_t *); -} res_register_t; - -typedef struct transaction { - struct transaction *next; - int mid; - unsigned int time; /* start time */ - response_handler_f handler; - void *user_data; -} transaction_t; - -static res_register_t * g_resources = NULL; - -static transaction_t *g_transactions = NULL; - -static user_timer_t g_trans_timer = NULL; - -static transaction_t *transaction_find(int mid) -{ - transaction_t *t = g_transactions; - - while (t) { - if (t->mid == mid) - return t; - t = t->next; - } - - return NULL; -} - -/* - * new transaction is added to the tail of the list, so the list - * is sorted by expiry time naturally. - */ -static void transaction_add(transaction_t *trans) -{ - transaction_t *t; - - if (g_transactions == NULL) { - g_transactions = trans; - return; - } - - t = g_transactions; - while (t) { - if (t->next == NULL) { - t->next = trans; - return; - } - } -} - -static void transaction_remove(transaction_t *trans) -{ - transaction_t *prev = NULL, *current = g_transactions; - - while (current) { - if (current == trans) { - if (prev == NULL) { - g_transactions = current->next; - free(current); - return; - } - prev->next = current->next; - free(current); - return; - } - prev = current; - current = current->next; - } -} - -static bool is_event_type(request_t * req) -{ - return req->action == COAP_EVENT; -} - -static bool register_url_handler(const char *url, - request_handler_f request_handler, reg_type_t reg_type) -{ - res_register_t * r = g_resources; - - while (r) { - if (reg_type == r->reg_type && strcmp(r->url, url) == 0) { - r->request_handler = request_handler; - return true; - } - r = r->next; - } - - r = (res_register_t *) malloc(sizeof(res_register_t)); - if (r == NULL) - return false; - - memset(r, 0, sizeof(*r)); - - r->url = strdup(url); - if (!r->url) { - free(r); - return false; - } - - r->request_handler = request_handler; - r->reg_type = reg_type; - r->next = g_resources; - g_resources = r; - - // tell app mgr to route this url to me - if (reg_type == Reg_Request) - wasm_register_resource(url); - else - wasm_sub_event(url); - - return true; -} - -bool api_register_resource_handler(const char *url, - request_handler_f request_handler) -{ - return register_url_handler(url, request_handler, Reg_Request); -} - -static void transaction_timeout_handler(user_timer_t timer) -{ - transaction_t *cur, *expired = NULL; - unsigned int elpased_ms, now = wasm_get_sys_tick_ms(); - - /* - * Since he transaction list is sorted by expiry time naturally, - * we can easily get all expired transactions. - * */ - cur = g_transactions; - while (cur) { - if (now < cur->time) - elpased_ms = now + (0xFFFFFFFF - cur->time) + 1; - else - elpased_ms = now - cur->time; - - if (elpased_ms >= TRANSACTION_TIMEOUT_MS) { - g_transactions = cur->next; - cur->next = expired; - expired = cur; - cur = g_transactions; - } else { - break; - } - } - - /* call each transaction's handler with response set to NULL */ - cur = expired; - while (cur) { - transaction_t *tmp = cur; - cur->handler(NULL, cur->user_data); - cur = cur->next; - free(tmp); - } - - /* - * If the transaction list is not empty, restart the timer according - * to the first transaction. Otherwise, stop the timer. - */ - if (g_transactions != NULL) { - unsigned int elpased_ms, ms_to_expiry, now = wasm_get_sys_tick_ms(); - if (now < g_transactions->time) { - elpased_ms = now + (0xFFFFFFFF - g_transactions->time) + 1; - } else { - elpased_ms = now - g_transactions->time; - } - ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; - api_timer_restart(g_trans_timer, ms_to_expiry); - } else { - api_timer_cancel(g_trans_timer); - g_trans_timer = NULL; - } -} - -void api_send_request(request_t * request, response_handler_f response_handler, - void * user_data) -{ - int size; - char *buffer; - transaction_t *trans; - - if ((trans = (transaction_t *) malloc(sizeof(transaction_t))) == NULL) { - printf( - "send request: allocate memory for request transaction failed!\n"); - return; - } - - memset(trans, 0, sizeof(transaction_t)); - trans->handler = response_handler; - trans->mid = request->mid; - trans->time = wasm_get_sys_tick_ms(); - trans->user_data = user_data; - - if ((buffer = pack_request(request, &size)) == NULL) { - printf("send request: pack request failed!\n"); - free(trans); - return; - } - - transaction_add(trans); - - /* if the trans is the 1st one, start the timer */ - if (trans == g_transactions) { - /* assert(g_trans_timer == NULL); */ - if (g_trans_timer == NULL) { - g_trans_timer = api_timer_create(TRANSACTION_TIMEOUT_MS, - false, - true, transaction_timeout_handler); - } - } - - wasm_post_request(buffer, size); - - free_req_resp_packet(buffer); -} - -/* - * - * APIs for the native layers to callback for request/response arrived to this app - * - */ - -void on_response(char * buffer, int size) -{ - response_t response[1]; - transaction_t *trans; - - if (NULL == unpack_response(buffer, size, response)) { - printf("unpack response failed\n"); - return; - } - - if ((trans = transaction_find(response->mid)) == NULL) { - printf("cannot find the transaction\n"); - return; - } - - /* - * When the 1st transaction get response: - * 1. If the 2nd trans exist, restart the timer according to its expiry time; - * 2. Otherwise, stop the timer since there is no more transactions; - */ - if (trans == g_transactions) { - if (trans->next != NULL) { - unsigned int elpased_ms, ms_to_expiry, now = wasm_get_sys_tick_ms(); - if (now < trans->next->time) { - elpased_ms = now + (0xFFFFFFFF - trans->next->time) + 1; - } else { - elpased_ms = now - trans->next->time; - } - ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; - api_timer_restart(g_trans_timer, ms_to_expiry); - } else { - api_timer_cancel(g_trans_timer); - g_trans_timer = NULL; - } - } - - trans->handler(response, trans->user_data); - transaction_remove(trans); -} - -void on_request(char *buffer, int size) -{ - request_t request[1]; - bool is_event; - res_register_t *r = g_resources; - - if (NULL == unpack_request(buffer, size, request)) { - printf("unpack request failed\n"); - return; - } - - is_event = is_event_type(request); - - while (r) { - if ((is_event && r->reg_type == Reg_Event) - || (!is_event && r->reg_type == Reg_Request)) { - if (check_url_start(request->url, strlen(request->url), r->url) - > 0) { - r->request_handler(request); - return; - } - } - - r = r->next; - } - - printf("on_request: exit. no service handler\n"); -} - -void api_response_send(response_t *response) -{ - int size; - char * buffer = pack_response(response, &size); - if (buffer == NULL) - return; - - wasm_response_send(buffer, size); - free_req_resp_packet(buffer); -} - -/// event api - -bool api_publish_event(const char *url, int fmt, void *payload, int payload_len) -{ - int size; - request_t request[1]; - init_request(request, (char *)url, COAP_EVENT, fmt, payload, payload_len); - char * buffer = pack_request(request, &size); - if (buffer == NULL) - return false; - wasm_post_request(buffer, size); - - free_req_resp_packet(buffer); - - return true; -} - -bool api_subscribe_event(const char * url, request_handler_f handler) -{ - return register_url_handler(url, handler, Reg_Event); -} - diff --git a/core/app-framework/base/app/timer.c b/core/app-framework/base/app/timer.c deleted file mode 100644 index 41bd59f59b..0000000000 --- a/core/app-framework/base/app/timer.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include - -#include "wa-inc/timer_wasm_app.h" -#include "timer_api.h" - -#if 1 -#include -#else -#define printf (...) -#endif - -struct user_timer { - struct user_timer * next; - int timer_id; - void (*user_timer_callback)(user_timer_t); -}; - -struct user_timer * g_timers = NULL; - -user_timer_t api_timer_create(int interval, bool is_period, bool auto_start, - on_user_timer_update_f on_timer_update) -{ - - int timer_id = wasm_create_timer(interval, is_period, auto_start); - - //TODO - struct user_timer * timer = (struct user_timer *) malloc( - sizeof(struct user_timer)); - if (timer == NULL) { - // TODO: remove the timer_id - printf("### api_timer_create malloc faild!!! \n"); - return NULL; - } - - memset(timer, 0, sizeof(*timer)); - timer->timer_id = timer_id; - timer->user_timer_callback = on_timer_update; - - if (g_timers == NULL) - g_timers = timer; - else { - timer->next = g_timers; - g_timers = timer; - } - - return timer; -} - -void api_timer_cancel(user_timer_t timer) -{ - user_timer_t t = g_timers, prev = NULL; - - wasm_timer_cancel(timer->timer_id); - - while (t) { - if (t == timer) { - if (prev == NULL) { - g_timers = t->next; - free(t); - } else { - prev->next = t->next; - free(t); - } - return; - } else { - prev = t; - t = t->next; - } - } -} - -void api_timer_restart(user_timer_t timer, int interval) -{ - wasm_timer_restart(timer->timer_id, interval); -} - -void on_timer_callback(int timer_id) -{ - struct user_timer * t = g_timers; - - while (t) { - if (t->timer_id == timer_id) { - t->user_timer_callback(t); - break; - } - t = t->next; - } -} - diff --git a/core/app-framework/base/app/timer_api.h b/core/app-framework/base/app/timer_api.h deleted file mode 100644 index 0881bab95c..0000000000 --- a/core/app-framework/base/app/timer_api.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _TIMER_API_H_ -#define _TIMER_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned int timer_id_t; - -timer_id_t -wasm_create_timer(int interval, bool is_period, bool auto_start); - -void -wasm_timer_destroy(timer_id_t timer_id); - -void -wasm_timer_cancel(timer_id_t timer_id); - -void -wasm_timer_restart(timer_id_t timer_id, int interval); - -uint32 -wasm_get_sys_tick_ms(void); - -#ifdef __cplusplus -} -#endif - -#endif /* end of _TIMER_API_H_ */ - diff --git a/core/app-framework/base/app/wa-inc/request.h b/core/app-framework/base/app/wa-inc/request.h deleted file mode 100644 index c209fae7cc..0000000000 --- a/core/app-framework/base/app/wa-inc/request.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _AEE_REQUEST_H_ -#define _AEE_REQUEST_H_ - -#include "bi-inc/shared_utils.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/* CoAP request method codes */ -typedef enum { - COAP_GET = 1, - COAP_POST, - COAP_PUT, - COAP_DELETE, - COAP_EVENT = (COAP_DELETE + 2) -} coap_method_t; - -/* CoAP response codes */ -typedef enum { - NO_ERROR = 0, - - CREATED_2_01 = 65, /* CREATED */ - DELETED_2_02 = 66, /* DELETED */ - VALID_2_03 = 67, /* NOT_MODIFIED */ - CHANGED_2_04 = 68, /* CHANGED */ - CONTENT_2_05 = 69, /* OK */ - CONTINUE_2_31 = 95, /* CONTINUE */ - - BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ - UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ - BAD_OPTION_4_02 = 130, /* BAD_OPTION */ - FORBIDDEN_4_03 = 131, /* FORBIDDEN */ - NOT_FOUND_4_04 = 132, /* NOT_FOUND */ - METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ - NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ - PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ - REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ - UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ - - INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ - NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ - BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ - SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ - GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ - PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ - - /* Erbium errors */ - MEMORY_ALLOCATION_ERROR = 192, PACKET_SERIALIZATION_ERROR, - - /* Erbium hooks */ - MANUAL_RESPONSE, PING_RESPONSE -} coap_status_t; - - -/** - * @typedef request_handler_f - * - * @brief Define the signature of callback function for API - * api_register_resource_handler() to handle request or for API - * api_subscribe_event() to handle event. - * - * @param request pointer of the request to be handled - * - * @see api_register_resource_handler - * @see api_subscribe_event - */ -typedef void (*request_handler_f)(request_t *request); - -/** - * @typedef response_handler_f - * - * @brief Define the signature of callback function for API - * api_send_request() to handle response of a request. - * - * @param response pointer of the response to be handled - * @param user_data user data associated with the request which is set when - * calling api_send_request(). - * - * @see api_send_request - */ -typedef void (*response_handler_f)(response_t *response, void *user_data); - - -/* - ***************** - * Request APIs - ***************** - */ - -/** - * @brief Register resource. - * - * @param url url of the resource - * @param handler callback function to handle the request to the resource - * - * @return true if success, false otherwise - */ -bool api_register_resource_handler(const char *url, request_handler_f handler); - -/** - * @brief Send request asynchronously. - * - * @param request pointer of the request to be sent - * @param response_handler callback function to handle the response - * @param user_data user data - */ -void api_send_request(request_t * request, response_handler_f response_handler, - void * user_data); - -/** - * @brief Send response. - * - * @param response pointer of the response to be sent - * - * @par - * @code - * void res1_handler(request_t *request) - * { - * response_t response[1]; - * make_response_for_request(request, response); - * set_response(response, DELETED_2_02, 0, NULL, 0); - * api_response_send(response); - * } - * @endcode - */ -void api_response_send(response_t *response); - - -/* - ***************** - * Event APIs - ***************** - */ - -/** - * @brief Publish an event. - * - * @param url url of the event - * @param fmt format of the event payload - * @param payload payload of the event - * @param payload_len length in bytes of the event payload - * - * @return true if success, false otherwise - */ -bool api_publish_event(const char *url, int fmt, void *payload, - int payload_len); - - -/** - * @brief Subscribe an event. - * - * @param url url of the event - * @param handler callback function to handle the event. - * - * @return true if success, false otherwise - */ -bool api_subscribe_event(const char * url, request_handler_f handler); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/app-framework/base/app/wa-inc/timer_wasm_app.h b/core/app-framework/base/app/wa-inc/timer_wasm_app.h deleted file mode 100644 index 17e59bc036..0000000000 --- a/core/app-framework/base/app/wa-inc/timer_wasm_app.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _AEE_TIMER_H_ -#define _AEE_TIMER_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* board producer define user_timer */ -struct user_timer; -typedef struct user_timer * user_timer_t; - -/** - * @typedef on_user_timer_update_f - * - * @brief Define the signature of callback function for API api_timer_create(). - * - * @param timer the timer - * - * @see api_timer_create - */ -typedef void (*on_user_timer_update_f)(user_timer_t timer); - -/* - ***************** - * Timer APIs - ***************** - */ - -/** - * @brief Create timer. - * - * @param interval timer interval - * @param is_period whether the timer is periodic - * @param auto_start whether start the timer immediately after created - * @param on_timer_update callback function called when timer expired - * - * @return the timer created if success, NULL otherwise - */ -user_timer_t api_timer_create(int interval, bool is_period, bool auto_start, - on_user_timer_update_f on_timer_update); - -/** - * @brief Cancel timer. - * - * @param timer the timer to cancel - */ -void api_timer_cancel(user_timer_t timer); - -/** - * @brief Restart timer. - * - * @param timer the timer to cancel - * @param interval the timer interval - */ -void api_timer_restart(user_timer_t timer, int interval); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/app-framework/base/app/wasm_app.cmake b/core/app-framework/base/app/wasm_app.cmake deleted file mode 100644 index 2313df99d5..0000000000 --- a/core/app-framework/base/app/wasm_app.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_APP_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_APP_BASE_DIR}) - -add_definitions (-DWASM_ENABLE_BASE_LIB) - -file (GLOB_RECURSE source_all ${WASM_APP_BASE_DIR}/*.c) - -set (WASM_APP_CURRENT_SOURCE ${source_all}) -set (WASM_APP_BASE_DIR ${WASM_APP_BASE_DIR} PARENT_SCOPE) diff --git a/core/app-framework/base/app/wasm_app.h b/core/app-framework/base/app/wasm_app.h deleted file mode 100644 index a22e77ed45..0000000000 --- a/core/app-framework/base/app/wasm_app.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _LIB_AEE_H_ -#define _LIB_AEE_H_ - -#include "bi-inc/shared_utils.h" -#include "bi-inc/attr_container.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -#ifdef __cplusplus -} -#endif - -#endif /* end of _LIB_AEE_H_ */ diff --git a/core/app-framework/base/native/base_lib_export.c b/core/app-framework/base/native/base_lib_export.c deleted file mode 100644 index 04086ba55f..0000000000 --- a/core/app-framework/base/native/base_lib_export.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include -#include "lib_export.h" -#include "base_lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { - /* TODO: use macro EXPORT_WASM_API() or EXPORT_WASM_API2() to - add functions to register. */ - EXPORT_WASM_API(wasm_register_resource), - EXPORT_WASM_API(wasm_response_send), - EXPORT_WASM_API(wasm_post_request), - EXPORT_WASM_API(wasm_sub_event), - EXPORT_WASM_API(wasm_create_timer), - EXPORT_WASM_API(wasm_timer_destroy), - EXPORT_WASM_API(wasm_timer_cancel), - EXPORT_WASM_API(wasm_timer_restart), - EXPORT_WASM_API(wasm_get_sys_tick_ms), -}; - -int get_base_lib_export_apis(NativeSymbol **p_base_lib_apis) -{ - *p_base_lib_apis = extended_native_symbol_defs; - return sizeof(extended_native_symbol_defs) / sizeof(NativeSymbol); -} - diff --git a/core/app-framework/base/native/base_lib_export.h b/core/app-framework/base/native/base_lib_export.h deleted file mode 100644 index d5f9f62676..0000000000 --- a/core/app-framework/base/native/base_lib_export.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BASE_LIB_EXPORT_H_ -#define _BASE_LIB_EXPORT_H_ - -#include "bi-inc/attr_container.h" -#include "native_interface.h" - -#endif /* end of _BASE_LIB_EXPORT_H_ */ - diff --git a/core/app-framework/base/native/req_resp_api.h b/core/app-framework/base/native/req_resp_api.h deleted file mode 100644 index fffa5131d5..0000000000 --- a/core/app-framework/base/native/req_resp_api.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _REQ_RESP_API_H_ -#define _REQ_RESP_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -bool -wasm_response_send(int32 buf_offset, int size); - -void -wasm_register_resource(int32 url_offset); - -void -wasm_post_request(int32 buf_offset, int size); - -void -wasm_sub_event(int32 url_offset); - -#ifdef __cplusplus -} -#endif - -#endif /* end of _REQ_RESP_API_H_ */ - diff --git a/core/app-framework/base/native/request_response.c b/core/app-framework/base/native/request_response.c deleted file mode 100644 index 2d725d35df..0000000000 --- a/core/app-framework/base/native/request_response.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "app_manager_export.h" -#include "coap_ext.h" -#include "wasm_export.h" -#include "bh_assert.h" - -extern void module_request_handler(request_t *request, void *user_data); - -bool -wasm_response_send(wasm_exec_env_t exec_env, - int32 buffer_offset, int size) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - if (!validate_app_addr(buffer_offset, size)) - return false; - - buffer = addr_app_to_native(buffer_offset); - - if (buffer != NULL) { - response_t response[1]; - - if (NULL == unpack_response(buffer, size, response)) - return false; - - am_send_response(response); - - return true; - } - - return false; -} - -void -wasm_register_resource(wasm_exec_env_t exec_env, int32 url_offset) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *url = NULL; - - if (!validate_app_str_addr(url_offset)) - return; - - url = addr_app_to_native(url_offset); - - if (url != NULL) { - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(mod_id != ID_NONE); - am_register_resource(url, module_request_handler, mod_id); - } -} - -void -wasm_post_request(wasm_exec_env_t exec_env, - int32 buffer_offset, int size) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - if (!validate_app_addr(buffer_offset, size)) - return; - - buffer = addr_app_to_native(buffer_offset); - - if (buffer != NULL) { - request_t req[1]; - - if (!unpack_request(buffer, size, req)) - return; - - // TODO: add permission check, ensure app can't do harm - - // set sender to help dispatch the response to the sender ap - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(mod_id != ID_NONE); - req->sender = mod_id; - - if (req->action == COAP_EVENT) { - am_publish_event(req); - return; - } - - am_dispatch_request(req); - } -} - -void -wasm_sub_event(wasm_exec_env_t exec_env, int32 url_offset) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *url = NULL; - - if (!validate_app_str_addr(url_offset)) - return; - - url = addr_app_to_native(url_offset); - - if (url != NULL) { - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - - bh_assert(mod_id != ID_NONE); - am_register_event(url, mod_id); - } -} - diff --git a/core/app-framework/base/native/runtime_lib.h b/core/app-framework/base/native/runtime_lib.h deleted file mode 100644 index 8fb9dbb341..0000000000 --- a/core/app-framework/base/native/runtime_lib.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef LIB_BASE_RUNTIME_LIB_H_ -#define LIB_BASE_RUNTIME_LIB_H_ - - -#include "runtime_timer.h" - -void init_wasm_timer(); -void exit_wasm_timer(); -timer_ctx_t get_wasm_timer_ctx(); -timer_ctx_t create_wasm_timer_ctx(unsigned int module_id, int prealloc_num); -void destroy_module_timer_ctx(unsigned int module_id); - -#endif /* LIB_BASE_RUNTIME_LIB_H_ */ diff --git a/core/app-framework/base/native/timer_api.h b/core/app-framework/base/native/timer_api.h deleted file mode 100644 index 0881bab95c..0000000000 --- a/core/app-framework/base/native/timer_api.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _TIMER_API_H_ -#define _TIMER_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned int timer_id_t; - -timer_id_t -wasm_create_timer(int interval, bool is_period, bool auto_start); - -void -wasm_timer_destroy(timer_id_t timer_id); - -void -wasm_timer_cancel(timer_id_t timer_id); - -void -wasm_timer_restart(timer_id_t timer_id, int interval); - -uint32 -wasm_get_sys_tick_ms(void); - -#ifdef __cplusplus -} -#endif - -#endif /* end of _TIMER_API_H_ */ - diff --git a/core/app-framework/base/native/timer_wrapper.c b/core/app-framework/base/native/timer_wrapper.c deleted file mode 100644 index 9ba3628ed6..0000000000 --- a/core/app-framework/base/native/timer_wrapper.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "runtime_timer.h" -#include "app_manager_export.h" -#include "module_wasm_app.h" -#include "bh_list.h" -#include "bh_thread.h" -#include "bh_time.h" - -static bool timer_thread_run = true; - -bh_list g_timer_ctx_list; -korp_cond g_timer_ctx_list_cond; -korp_mutex g_timer_ctx_list_mutex; -typedef struct { - bh_list_link l; - timer_ctx_t timer_ctx; -} timer_ctx_node_t; - -void wasm_timer_callback(timer_id_t id, unsigned int mod_id) -{ - module_data* module = module_data_list_lookup_id(mod_id); - if (module == NULL) - return; - - // !!! the length parameter must be 0, so the receiver will - // not free the payload pointer. - bh_post_msg(module->queue, TIMER_EVENT_WASM, (char *)(uintptr_t)id, 0); -} - -/// -/// why we create a separate link for module timer contexts -/// rather than traverse the module list? -/// It helps to reduce the lock frequency for the module list. -/// Also when we lock the module list and then call the callback for -/// timer expire, the callback is request the list lock again for lookup -/// the module from module id. It is for avoiding that situation. - -void * thread_modulers_timer_check(void * arg) -{ - int ms_to_expiry; - - while (timer_thread_run) { - ms_to_expiry = -1; - vm_mutex_lock(&g_timer_ctx_list_mutex); - timer_ctx_node_t* elem = (timer_ctx_node_t*) - bh_list_first_elem(&g_timer_ctx_list); - while (elem) { - int next = check_app_timers(elem->timer_ctx); - if (next != -1) { - if (ms_to_expiry == -1 || ms_to_expiry > next) - ms_to_expiry = next; - } - - elem = (timer_ctx_node_t*) bh_list_elem_next(elem); - } - vm_mutex_unlock(&g_timer_ctx_list_mutex); - - if (ms_to_expiry == -1) - ms_to_expiry = 60 * 1000; - vm_mutex_lock(&g_timer_ctx_list_mutex); - vm_cond_reltimedwait(&g_timer_ctx_list_cond, &g_timer_ctx_list_mutex, - ms_to_expiry); - vm_mutex_unlock(&g_timer_ctx_list_mutex); - } - - return NULL; -} - -void wakeup_modules_timer_thread(timer_ctx_t ctx) -{ - vm_mutex_lock(&g_timer_ctx_list_mutex); - vm_cond_signal(&g_timer_ctx_list_cond); - vm_mutex_unlock(&g_timer_ctx_list_mutex); -} - -void init_wasm_timer() -{ - korp_tid tm_tid; - bh_list_init(&g_timer_ctx_list); - - vm_cond_init(&g_timer_ctx_list_cond); - /* temp solution for: thread_modulers_timer_check thread would recursive lock the mutex */ - vm_recursive_mutex_init(&g_timer_ctx_list_mutex); - - vm_thread_create(&tm_tid, thread_modulers_timer_check, - NULL, BH_APPLET_PRESERVED_STACK_SIZE); -} - -void exit_wasm_timer() -{ - timer_thread_run = false; -} - -timer_ctx_t create_wasm_timer_ctx(unsigned int module_id, int prealloc_num) -{ - timer_ctx_t ctx = create_timer_ctx(wasm_timer_callback, - wakeup_modules_timer_thread, - prealloc_num, - module_id); - - if (ctx == NULL) - return NULL; - - timer_ctx_node_t * node = (timer_ctx_node_t*) - bh_malloc(sizeof(timer_ctx_node_t)); - if (node == NULL) { - destroy_timer_ctx(ctx); - return NULL; - } - memset(node, 0, sizeof(*node)); - node->timer_ctx = ctx; - - vm_mutex_lock(&g_timer_ctx_list_mutex); - bh_list_insert(&g_timer_ctx_list, node); - vm_mutex_unlock(&g_timer_ctx_list_mutex); - - return ctx; -} - -void destroy_module_timer_ctx(unsigned int module_id) -{ - vm_mutex_lock(&g_timer_ctx_list_mutex); - timer_ctx_node_t* elem = (timer_ctx_node_t*) - bh_list_first_elem(&g_timer_ctx_list); - while (elem) { - if (timer_ctx_get_owner(elem->timer_ctx) == module_id) { - bh_list_remove(&g_timer_ctx_list, elem); - destroy_timer_ctx(elem->timer_ctx); - bh_free(elem); - break; - } - - elem = (timer_ctx_node_t*) bh_list_elem_next(elem); - } - vm_mutex_unlock(&g_timer_ctx_list_mutex); -} - -timer_ctx_t get_wasm_timer_ctx(wasm_module_inst_t module_inst) -{ - module_data * m = app_manager_get_module_data(Module_WASM_App, - module_inst); - if (m == NULL) - return NULL; - return m->timer_ctx; -} - -timer_id_t -wasm_create_timer(wasm_exec_env_t exec_env, - int interval, bool is_period, bool auto_start) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); - bh_assert(timer_ctx); - return sys_create_timer(timer_ctx, interval, is_period, auto_start); -} - -void -wasm_timer_destroy(wasm_exec_env_t exec_env, timer_id_t timer_id) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); - bh_assert(timer_ctx); - sys_timer_destroy(timer_ctx, timer_id); -} - -void -wasm_timer_cancel(wasm_exec_env_t exec_env, timer_id_t timer_id) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); - bh_assert(timer_ctx); - sys_timer_cancel(timer_ctx, timer_id); -} - -void -wasm_timer_restart(wasm_exec_env_t exec_env, - timer_id_t timer_id, int interval) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); - bh_assert(timer_ctx); - sys_timer_restart(timer_ctx, timer_id, interval); -} - -extern uint32 get_sys_tick_ms(); - -uint32 -wasm_get_sys_tick_ms(wasm_exec_env_t exec_env) -{ - return (uint32) bh_get_tick_ms(); -} - diff --git a/core/app-framework/base/native/wasm_lib.cmake b/core/app-framework/base/native/wasm_lib.cmake deleted file mode 100644 index 223320b323..0000000000 --- a/core/app-framework/base/native/wasm_lib.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) - -add_definitions (-DWASM_ENABLE_BASE_LIB) - -include_directories(${WASM_LIB_BASE_DIR}) - -file (GLOB_RECURSE source_all ${WASM_LIB_BASE_DIR}/*.c) - -set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) - diff --git a/core/app-framework/connection/app/connection.c b/core/app-framework/connection/app/connection.c deleted file mode 100644 index 775ee16f17..0000000000 --- a/core/app-framework/connection/app/connection.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wa-inc/connection.h" -#include "connection_api.h" - -/* Raw connection structure */ -typedef struct _connection { - /* Next connection */ - struct _connection *next; - - /* Handle of the connection */ - uint32 handle; - - /* Callback function called when event on this connection occurs */ - on_connection_event_f on_event; - - /* User data */ - void *user_data; -} connection_t; - -/* Raw connections list */ -static connection_t *g_conns = NULL; - -connection_t *api_open_connection(const char *name, - attr_container_t *args, - on_connection_event_f on_event, - void *user_data) -{ - connection_t *conn; - char *args_buffer = (char *)args; - uint32 handle, args_len = attr_container_get_serialize_length(args); - - handle = wasm_open_connection(name, args_buffer, args_len); - if (handle == -1) - return NULL; - - conn = (connection_t *)malloc(sizeof(*conn)); - if (conn == NULL) { - wasm_close_connection(handle); - return NULL; - } - - memset(conn, 0, sizeof(*conn)); - conn->handle = handle; - conn->on_event = on_event; - conn->user_data = user_data; - - if (g_conns != NULL) { - conn->next = g_conns; - g_conns = conn; - } else { - g_conns = conn; - } - - return conn; -} - -void api_close_connection(connection_t *c) -{ - connection_t *conn = g_conns, *prev = NULL; - - while (conn) { - if (conn == c) { - wasm_close_connection(c->handle); - if (prev != NULL) - prev->next = conn->next; - else - g_conns = conn->next; - free(conn); - return; - } else { - prev = conn; - conn = conn->next; - } - } -} - -int api_send_on_connection(connection_t *conn, const char *data, uint32 len) -{ - return wasm_send_on_connection(conn->handle, data, len); -} - -bool api_config_connection(connection_t *conn, attr_container_t *cfg) -{ - char *cfg_buffer = (char *)cfg; - uint32 cfg_len = attr_container_get_serialize_length(cfg); - - return wasm_config_connection(conn->handle, cfg_buffer, cfg_len); -} - -void on_connection_data(uint32 handle, char *buffer, uint32 len) -{ - connection_t *conn = g_conns; - - while (conn != NULL) { - if (conn->handle == handle) { - if (len == 0) { - conn->on_event(conn, - CONN_EVENT_TYPE_DISCONNECT, - NULL, - 0, - conn->user_data); - } else { - conn->on_event(conn, - CONN_EVENT_TYPE_DATA, - buffer, - len, - conn->user_data); - } - - return; - } - conn = conn->next; - } -} - diff --git a/core/app-framework/connection/app/connection_api.h b/core/app-framework/connection/app/connection_api.h deleted file mode 100644 index 5697c8361b..0000000000 --- a/core/app-framework/connection/app/connection_api.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef CONNECTION_API_H_ -#define CONNECTION_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -uint32 -wasm_open_connection(const char *name, char *args_buf, uint32 args_buf_len); - -void -wasm_close_connection(uint32 handle); - -int -wasm_send_on_connection(uint32 handle, const char *data, uint32 data_len); - -bool -wasm_config_connection(uint32 handle, const char *cfg_buf, uint32 cfg_buf_len); - -#ifdef __cplusplus -} -#endif - - -#endif /* end of CONNECTION_API_H_ */ diff --git a/core/app-framework/connection/app/wa-inc/connection.h b/core/app-framework/connection/app/wa-inc/connection.h deleted file mode 100644 index 675befa59a..0000000000 --- a/core/app-framework/connection/app/wa-inc/connection.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _CONNECTION_H_ -#define _CONNECTION_H_ - -#include "bi-inc/attr_container.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct _connection; -typedef struct _connection connection_t; - -/* Connection event type */ -typedef enum { - /* Data is received */ - CONN_EVENT_TYPE_DATA = 1, - /* Connection is disconnected */ - CONN_EVENT_TYPE_DISCONNECT -} conn_event_type_t; - -/* - * @typedef on_connection_event_f - * - * @param conn the connection that the event belongs to - * @param type event type - * @param data the data received for CONN_EVENT_TYPE_DATA event - * @param len length of the data in byte - * @param user_data user data - */ -typedef void (*on_connection_event_f)(connection_t *conn, - conn_event_type_t type, - const char *data, - uint32 len, - void *user_data); - -/* - ***************** - * Connection API's - ***************** - */ - -/* - * @brief Open a connection. - * - * @param name name of the connection, "TCP", "UDP" or "UART" - * @param args connection arguments, such as: ip:127.0.0.1, port:8888 - * @param on_event callback function called when event occurs - * @param user_data user data - * - * @return the connection or NULL means fail - */ -connection_t *api_open_connection(const char *name, - attr_container_t *args, - on_connection_event_f on_event, - void *user_data); - -/* - * @brief Close a connection. - * - * @param conn connection - */ -void api_close_connection(connection_t *conn); - -/* - * Send data to the connection in non-blocking manner which returns immediately - * - * @param conn the connection - * @param data data buffer to be sent - * @param len length of the data in byte - * - * @return actual length sent, or -1 if fail(maybe underlying buffer is full) - */ -int api_send_on_connection(connection_t *conn, const char *data, uint32 len); - -/* - * @brief Configure connection. - * - * @param conn the connection - * @param cfg configurations - * - * @return true if success, false otherwise - */ -bool api_config_connection(connection_t *conn, attr_container_t *cfg); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/app-framework/connection/app/wasm_app.cmake b/core/app-framework/connection/app/wasm_app.cmake deleted file mode 100644 index ca4e025998..0000000000 --- a/core/app-framework/connection/app/wasm_app.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_APP_CONN_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_APP_CONN_DIR}) - - -file (GLOB source_all ${WASM_APP_CONN_DIR}/*.c) - -set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/core/app-framework/connection/native/connection.inl b/core/app-framework/connection/native/connection.inl deleted file mode 100644 index 2956e2696c..0000000000 --- a/core/app-framework/connection/native/connection.inl +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -EXPORT_WASM_API(wasm_open_connection), -EXPORT_WASM_API(wasm_close_connection), -EXPORT_WASM_API(wasm_send_on_connection), -EXPORT_WASM_API(wasm_config_connection), diff --git a/core/app-framework/connection/native/connection_api.h b/core/app-framework/connection/native/connection_api.h deleted file mode 100644 index 7d8ef5986f..0000000000 --- a/core/app-framework/connection/native/connection_api.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef CONNECTION_API_H_ -#define CONNECTION_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -uint32 -wasm_open_connection(int32 name_offset, int32 args_buf_offset, uint32 args_buf_len); - -void -wasm_close_connection(uint32 handle); - -int -wasm_send_on_connection(uint32 handle, int32 data_offset, uint32 data_len); - -bool -wasm_config_connection(uint32 handle, int32 cfg_buf_offset, uint32 cfg_buf_len); - -#ifdef __cplusplus -} -#endif - - -#endif /* end of CONNECTION_API_H_ */ diff --git a/core/app-framework/connection/native/connection_lib.h b/core/app-framework/connection/native/connection_lib.h deleted file mode 100644 index 4a69f555ff..0000000000 --- a/core/app-framework/connection/native/connection_lib.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef CONNECTION_LIB_H_ -#define CONNECTION_LIB_H_ - -#include "bi-inc/attr_container.h" -#include "wasm_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - ***************** - * This file defines connection library which should be implemented by different platforms - ***************** - */ - -/* - * @brief Open a connection. - * - * @param name name of the connection, "TCP", "UDP" or "UART" - * @param args connection arguments, such as: ip:127.0.0.1, port:8888 - * - * @return 0~0xFFFFFFFE means id of the connection, otherwise(-1) means fail - */ -typedef uint32 (*connection_open_f)(wasm_module_inst_t module_inst, - const char *name, attr_container_t *args); - -/* - * @brief Close a connection. - * - * @param handle of the connection - */ -typedef void (*connection_close_f)(uint32 handle); - -/* - * @brief Send data to the connection in non-blocking manner. - * - * @param handle of the connection - * @param data data buffer to be sent - * @param len length of the data in byte - * - * @return actual length sent, -1 if fail - */ -typedef int (*connection_send_f)(uint32 handle, const char *data, int len); - -/* - * @brief Configure connection. - * - * @param handle of the connection - * @param cfg configurations - * - * @return true if success, false otherwise - */ -typedef bool (*connection_config_f)(uint32 handle, attr_container_t *cfg); - -/* Raw connection interface for platform to implement */ -typedef struct _connection_interface { - connection_open_f _open; - connection_close_f _close; - connection_send_f _send; - connection_config_f _config; -} connection_interface_t; - -/* Platform must define this interface */ -extern connection_interface_t connection_impl; - -#ifdef __cplusplus -} -#endif - - -#endif /* CONNECTION_LIB_H_ */ diff --git a/core/app-framework/connection/native/connection_wrapper.c b/core/app-framework/connection/native/connection_wrapper.c deleted file mode 100644 index d817bc4f19..0000000000 --- a/core/app-framework/connection/native/connection_wrapper.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "connection_lib.h" -#include "wasm_export.h" -#include "native_interface.h" - -/* Note: - * - * This file is the consumer of connection lib which is implemented by different platforms - */ - -uint32 -wasm_open_connection(wasm_exec_env_t exec_env, - int32 name_offset, int32 args_offset, uint32 len) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - attr_container_t *args; - char *name, *args_buf; - - if (!validate_app_str_addr(name_offset) || - !validate_app_addr(args_offset, len) || - !(name = addr_app_to_native(name_offset)) || - !(args_buf = addr_app_to_native(args_offset))) - return -1; - - args = (attr_container_t *)args_buf; - - if (connection_impl._open != NULL) - return connection_impl._open(module_inst, name, args); - - return -1; -} - -void -wasm_close_connection(wasm_exec_env_t exec_env, uint32 handle) -{ - if (connection_impl._close != NULL) - connection_impl._close(handle); -} - -int -wasm_send_on_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 data_offset, uint32 len) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *data; - - if (!validate_app_addr(data_offset, len) || - !(data = addr_app_to_native(data_offset))) - return -1; - - if (connection_impl._send != NULL) - return connection_impl._send(handle, data, len); - - return -1; -} - -bool -wasm_config_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 cfg_offset, uint32 len) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *cfg_buf; - attr_container_t *cfg; - - if (!validate_app_addr(cfg_offset, len) || - !(cfg_buf = addr_app_to_native(cfg_offset))) - return false; - - cfg = (attr_container_t *)cfg_buf; - - if (connection_impl._config != NULL) - return connection_impl._config(handle, cfg); - - return false; -} diff --git a/core/app-framework/connection/native/linux/conn_tcp.c b/core/app-framework/connection/native/linux/conn_tcp.c deleted file mode 100644 index e676e53a07..0000000000 --- a/core/app-framework/connection/native/linux/conn_tcp.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "conn_tcp.h" - -#include -#include -#include -#include -#include - -int tcp_open(char *address, uint16 port) -{ - int sock, ret; - struct sockaddr_in servaddr; - - memset(&servaddr, 0, sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(address); - servaddr.sin_port = htons(port); - - sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == -1) - return -1; - - ret = connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)); - if (ret == -1) { - close(sock); - return -1; - } - - /* Put the socket in non-blocking mode */ - if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0) { - close(sock); - return -1; - } - - return sock; -} - -int tcp_send(int sock, const char *data, int size) -{ - return send(sock, data, size, 0); -} - -int tcp_recv(int sock, char *buffer, int buf_size) -{ - return recv(sock, buffer, buf_size, 0); -} diff --git a/core/app-framework/connection/native/linux/conn_tcp.h b/core/app-framework/connection/native/linux/conn_tcp.h deleted file mode 100644 index c1b560e321..0000000000 --- a/core/app-framework/connection/native/linux/conn_tcp.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef CONN_LINUX_TCP_H_ -#define CONN_LINUX_TCP_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int tcp_open(char *address, uint16 port); - -int tcp_send(int sock, const char *data, int size); - -int tcp_recv(int sock, char *buffer, int buf_size); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/core/app-framework/connection/native/linux/conn_uart.c b/core/app-framework/connection/native/linux/conn_uart.c deleted file mode 100644 index 1f5c4e767c..0000000000 --- a/core/app-framework/connection/native/linux/conn_uart.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "conn_uart.h" - -#include -#include -#include - -static int parse_baudrate(int baud) -{ - switch (baud) { - case 9600: - return B9600; - case 19200: - return B19200; - case 38400: - return B38400; - case 57600: - return B57600; - case 115200: - return B115200; - case 230400: - return B230400; - case 460800: - return B460800; - case 500000: - return B500000; - case 576000: - return B576000; - case 921600: - return B921600; - case 1000000: - return B1000000; - case 1152000: - return B1152000; - case 1500000: - return B1500000; - case 2000000: - return B2000000; - case 2500000: - return B2500000; - case 3000000: - return B3000000; - case 3500000: - return B3500000; - case 4000000: - return B4000000; - default: - return -1; - } -} - -int uart_open(char* device, int baudrate) -{ - int uart_fd; - struct termios uart_term; - - uart_fd = open(device, O_RDWR | O_NOCTTY); - - if (uart_fd < 0) - return -1; - - memset(&uart_term, 0, sizeof(uart_term)); - uart_term.c_cflag = parse_baudrate(baudrate) | CS8 | CLOCAL | CREAD; - uart_term.c_iflag = IGNPAR; - uart_term.c_oflag = 0; - - /* set noncanonical mode */ - uart_term.c_lflag = 0; - uart_term.c_cc[VTIME] = 30; - uart_term.c_cc[VMIN] = 1; - tcflush(uart_fd, TCIFLUSH); - - if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { - close(uart_fd); - return -1; - } - - /* Put the fd in non-blocking mode */ - if (fcntl(uart_fd, F_SETFL, fcntl(uart_fd, F_GETFL) | O_NONBLOCK) < 0) { - close(uart_fd); - return -1; - } - - return uart_fd; -} - -int uart_send(int fd, const char *data, int size) -{ - return write(fd, data, size); -} - -int uart_recv(int fd, char *buffer, int buf_size) -{ - return read(fd, buffer, buf_size); -} diff --git a/core/app-framework/connection/native/linux/conn_uart.h b/core/app-framework/connection/native/linux/conn_uart.h deleted file mode 100644 index 9d40fb004d..0000000000 --- a/core/app-framework/connection/native/linux/conn_uart.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef CONN_LINUX_UART_H_ -#define CONN_LINUX_UART_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int uart_open(char* device, int baudrate); - -int uart_send(int fd, const char *data, int size); - -int uart_recv(int fd, char *buffer, int buf_size); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/core/app-framework/connection/native/linux/conn_udp.c b/core/app-framework/connection/native/linux/conn_udp.c deleted file mode 100644 index 9d370475ad..0000000000 --- a/core/app-framework/connection/native/linux/conn_udp.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "conn_udp.h" - -#include -#include -#include -#include -#include - -int udp_open(uint16 port) -{ - int sock, ret; - struct sockaddr_in addr; - - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock == -1) - return -1; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(port); - - ret = bind(sock, (struct sockaddr*)&addr, sizeof(addr)); - if (ret == -1) { - close(sock); - return -1; - } - - /* Put the socket in non-blocking mode */ - if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0) { - close(sock); - return -1; - } - - return sock; -} - -int udp_send(int sock, struct sockaddr *dest, const char *data, int size) -{ - return sendto(sock, data, size, MSG_CONFIRM, dest, sizeof(*dest)); -} - -int udp_recv(int sock, char *buffer, int buf_size) -{ - struct sockaddr_in remaddr; - socklen_t addrlen = sizeof(remaddr); - - return recvfrom(sock, - buffer, - buf_size, - 0, - (struct sockaddr *)&remaddr, - &addrlen); -} diff --git a/core/app-framework/connection/native/linux/conn_udp.h b/core/app-framework/connection/native/linux/conn_udp.h deleted file mode 100644 index 37b9db79a7..0000000000 --- a/core/app-framework/connection/native/linux/conn_udp.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef CONN_LINUX_UDP_H_ -#define CONN_LINUX_UDP_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int udp_open(uint16 port); - -int udp_send(int sock, struct sockaddr *dest, const char *data, int size); - -int udp_recv(int sock, char *buffer, int buf_size); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/core/app-framework/connection/native/linux/connection_mgr.c b/core/app-framework/connection/native/linux/connection_mgr.c deleted file mode 100644 index cd8431a351..0000000000 --- a/core/app-framework/connection/native/linux/connection_mgr.c +++ /dev/null @@ -1,587 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/* - * Note: - * This file implements the linux version connection library which is - * defined in connection_lib.h. - * It also provides a reference implementation of connections manager. - */ - -#include "connection_lib.h" -#include "bh_thread.h" -#include "app_manager_export.h" -#include "module_wasm_app.h" -#include "conn_tcp.h" -#include "conn_udp.h" -#include "conn_uart.h" -#include "bh_common.h" -#include "bh_assert.h" - -#include -#include -#include -#include -#include - -#define MAX_EVENTS 10 -#define IO_BUF_SIZE 256 - -static bool polling_thread_run = true; - -/* Connection type */ -typedef enum conn_type { - CONN_TYPE_TCP, - CONN_TYPE_UDP, - CONN_TYPE_UART, - CONN_TYPE_UNKNOWN -} conn_type_t; - -/* Sys connection */ -typedef struct sys_connection { - /* Next connection */ - struct sys_connection *next; - - /* Type */ - conn_type_t type; - - /* Handle to interact with wasm app */ - uint32 handle; - - /* Underlying connection ID, may be socket fd */ - int fd; - - /* Module id that the connection belongs to */ - uint32 module_id; - - /* Argument, such as dest addr for udp */ - void *arg; -} sys_connection_t; - -/* Epoll instance */ -static int epollfd; - -/* Connections list */ -static sys_connection_t *g_connections = NULL; - -/* Max handle */ -static uint32 g_handle_max = 0; - -/* Lock to protect g_connections and g_handle_max */ -static korp_mutex g_lock; - -/* Epoll events */ -static struct epoll_event epoll_events[MAX_EVENTS]; - -/* Buffer to receive data */ -static char io_buf[IO_BUF_SIZE]; - -static uint32 _conn_open(wasm_module_inst_t module_inst, - const char *name, attr_container_t *args); -static void _conn_close(uint32 handle); -static int _conn_send(uint32 handle, const char *data, int len); -static bool _conn_config(uint32 handle, attr_container_t *cfg); - -/* - * Platform implementation of connection library - */ -connection_interface_t connection_impl = { - ._open = _conn_open, - ._close = _conn_close, - ._send = _conn_send, - ._config = _conn_config -}; - -static void add_connection(sys_connection_t *conn) -{ - vm_mutex_lock(&g_lock); - - g_handle_max++; - if (g_handle_max == -1) - g_handle_max++; - conn->handle = g_handle_max; - - if (g_connections) { - conn->next = g_connections; - g_connections = conn; - } else { - g_connections = conn; - } - - vm_mutex_unlock(&g_lock); -} - -#define FREE_CONNECTION(conn) do { \ - if (conn->arg) \ - bh_free(conn->arg); \ - bh_free(conn); \ -} while (0) - -static int get_app_conns_num(uint32 module_id) -{ - sys_connection_t *conn; - int num = 0; - - vm_mutex_lock(&g_lock); - - conn = g_connections; - while (conn) { - if (conn->module_id == module_id) - num++; - conn = conn->next; - } - - vm_mutex_unlock(&g_lock); - - return num; -} - -static sys_connection_t *find_connection(uint32 handle, bool remove_found) -{ - sys_connection_t *conn, *prev = NULL; - - vm_mutex_lock(&g_lock); - - conn = g_connections; - while (conn) { - if (conn->handle == handle) { - if (remove_found) { - if (prev != NULL) { - prev->next = conn->next; - } else { - g_connections = conn->next; - } - } - vm_mutex_unlock(&g_lock); - return conn; - } else { - prev = conn; - conn = conn->next; - } - } - - vm_mutex_unlock(&g_lock); - - return NULL; -} - -static void cleanup_connections(uint32 module_id) -{ - sys_connection_t *conn, *prev = NULL; - - vm_mutex_lock(&g_lock); - - conn = g_connections; - while (conn) { - if (conn->module_id == module_id) { - epoll_ctl(epollfd, EPOLL_CTL_DEL, conn->fd, NULL); - close(conn->fd); - - if (prev != NULL) { - prev->next = conn->next; - FREE_CONNECTION(conn); - conn = prev->next; - } else { - g_connections = conn->next; - FREE_CONNECTION(conn); - conn = g_connections; - } - } else { - prev = conn; - conn = conn->next; - } - } - - vm_mutex_unlock(&g_lock); -} - -static conn_type_t get_conn_type(const char *name) -{ - if (strcmp(name, "TCP") == 0) - return CONN_TYPE_TCP; - if (strcmp(name, "UDP") == 0) - return CONN_TYPE_UDP; - if (strcmp(name, "UART") == 0) - return CONN_TYPE_UART; - - return CONN_TYPE_UNKNOWN; -} - -/* --- connection lib function --- */ -static uint32 _conn_open(wasm_module_inst_t module_inst, - const char *name, attr_container_t *args) -{ - int fd; - sys_connection_t *conn; - struct epoll_event ev; - uint32 module_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(module_id != ID_NONE); - - if (get_app_conns_num(module_id) >= MAX_CONNECTION_PER_APP) - return -1; - - conn = (sys_connection_t *)bh_malloc(sizeof(*conn)); - if (conn == NULL) - return -1; - - memset(conn, 0, sizeof(*conn)); - conn->module_id = module_id; - conn->type = get_conn_type(name); - - /* Generate a handle and add to list */ - add_connection(conn); - - if (conn->type == CONN_TYPE_TCP) { - char *address; - uint16 port; - - /* Check and parse connection parameters */ - if (!attr_container_contain_key(args, "address") || - !attr_container_contain_key(args, "port")) - goto fail; - - address = attr_container_get_as_string(args, "address"); - port = attr_container_get_as_uint16(args, "port"); - - /* Connect to TCP server */ - if (!address || (fd = tcp_open(address, port)) == -1) - goto fail; - - } else if (conn->type == CONN_TYPE_UDP) { - uint16 port; - - /* Check and parse connection parameters */ - if (!attr_container_contain_key(args, "bind port")) - goto fail; - port = attr_container_get_as_uint16(args, "bind port"); - - /* Bind port */ - if ((fd = udp_open(port)) == -1) - goto fail; - - } else if (conn->type == CONN_TYPE_UART) { - char *device; - int baud; - - /* Check and parse connection parameters */ - if (!attr_container_contain_key(args, "device") || - !attr_container_contain_key(args, "baudrate")) - goto fail; - device = attr_container_get_as_string(args, "device"); - baud = attr_container_get_as_int(args, "baudrate"); - - /* Open device */ - if (!device || (fd = uart_open(device, baud)) == -1) - goto fail; - } else { - goto fail; - } - - conn->fd = fd; - - /* Set current connection as event data */ - ev.events = EPOLLIN; - ev.data.ptr = conn; - - /* Monitor incoming data */ - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { - close(fd); - goto fail; - } - - return conn->handle; - -fail: - find_connection(conn->handle, true); - bh_free(conn); - return -1; -} - -/* --- connection lib function --- */ -static void _conn_close(uint32 handle) -{ - sys_connection_t *conn = find_connection(handle, true); - - if (conn != NULL) { - epoll_ctl(epollfd, EPOLL_CTL_DEL, conn->fd, NULL); - close(conn->fd); - FREE_CONNECTION(conn); - } -} - -/* --- connection lib function --- */ -static int _conn_send(uint32 handle, const char *data, int len) -{ - sys_connection_t *conn = find_connection(handle, false); - - if (conn == NULL) - return -1; - - if (conn->type == CONN_TYPE_TCP) - return tcp_send(conn->fd, data, len); - - if (conn->type == CONN_TYPE_UDP) { - struct sockaddr *addr = (struct sockaddr *)conn->arg; - return udp_send(conn->fd, addr, data, len); - } - - if (conn->type == CONN_TYPE_UART) - return uart_send(conn->fd, data, len); - - return -1; -} - -/* --- connection lib function --- */ -static bool _conn_config(uint32 handle, attr_container_t *cfg) -{ - sys_connection_t *conn = find_connection(handle, false); - - if (conn == NULL) - return false; - - if (conn->type == CONN_TYPE_UDP) { - char *address; - uint16_t port; - struct sockaddr_in *addr; - - /* Parse remote address/port */ - if (!attr_container_contain_key(cfg, "address") || - !attr_container_contain_key(cfg, "port")) - return false; - if (!(address = attr_container_get_as_string(cfg, "address"))) - return false; - port = attr_container_get_as_uint16(cfg, "port"); - - if (conn->arg == NULL) { - addr = (struct sockaddr_in *)bh_malloc(sizeof(*addr)); - if (addr == NULL) - return false; - - memset(addr, 0, sizeof(*addr)); - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = inet_addr(address); - addr->sin_port = htons(port); - - /* Set remote address as connection arg */ - conn->arg = addr; - } else { - addr = (struct sockaddr_in *)conn->arg; - addr->sin_addr.s_addr = inet_addr(address); - addr->sin_port = htons(port); - } - - return true; - } - - return false; -} - -/* --- connection manager reference implementation ---*/ - -typedef struct connection_event { - uint32 handle; - char *data; - uint32 len; -} connection_event_t; - -static void connection_event_cleaner(connection_event_t *conn_event) -{ - if (conn_event->data != NULL) - bh_free(conn_event->data); - bh_free(conn_event); -} - -static void post_msg_to_module(sys_connection_t *conn, - char *data, - uint32 len) -{ - module_data *module = module_data_list_lookup_id(conn->module_id); - char *data_copy = NULL; - connection_event_t *conn_data_event; - bh_message_t msg; - - if (module == NULL) - return; - - conn_data_event = (connection_event_t *)bh_malloc(sizeof(*conn_data_event)); - if (conn_data_event == NULL) - return; - - if (len > 0) { - data_copy = (char *)bh_malloc(len); - if (data_copy == NULL) { - bh_free(conn_data_event); - return; - } - bh_memcpy_s(data_copy, len, data, len); - } - - memset(conn_data_event, 0, sizeof(*conn_data_event)); - conn_data_event->handle = conn->handle; - conn_data_event->data = data_copy; - conn_data_event->len = len; - - msg = bh_new_msg(CONNECTION_EVENT_WASM, - conn_data_event, - sizeof(*conn_data_event), - connection_event_cleaner); - if (!msg) { - connection_event_cleaner(conn_data_event); - return; - } - - bh_post_msg2(module->queue, msg); -} - -static void* polling_thread_routine (void *arg) -{ - while (polling_thread_run) { - int i, n; - - n = epoll_wait(epollfd, epoll_events, MAX_EVENTS, -1); - - if (n == -1 && errno != EINTR) - continue; - - for (i = 0; i < n; i++) { - sys_connection_t *conn - = (sys_connection_t *)epoll_events[i].data.ptr; - - if (conn->type == CONN_TYPE_TCP) { - int count = tcp_recv(conn->fd, io_buf, IO_BUF_SIZE); - if (count <= 0) { - /* Connection is closed by peer */ - post_msg_to_module(conn, NULL, 0); - _conn_close(conn->handle); - } else { - /* Data is received */ - post_msg_to_module(conn, io_buf, count); - } - } else if (conn->type == CONN_TYPE_UDP) { - int count = udp_recv(conn->fd, io_buf, IO_BUF_SIZE); - if (count > 0) - post_msg_to_module(conn, io_buf, count); - } else if (conn->type == CONN_TYPE_UART) { - int count = uart_recv(conn->fd, io_buf, IO_BUF_SIZE); - if (count > 0) - post_msg_to_module(conn, io_buf, count); - } - } - } - - return NULL; -} - -void app_mgr_connection_event_callback(module_data *m_data, bh_message_t msg) -{ - uint32 argv[3]; - wasm_function_inst_t func_on_conn_data; - bh_assert(CONNECTION_EVENT_WASM == bh_message_type(msg)); - wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; - wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; - connection_event_t *conn_event - = (connection_event_t *)bh_message_payload(msg); - int32 data_offset; - - if (conn_event == NULL) - return; - - func_on_conn_data = wasm_runtime_lookup_function(inst, "_on_connection_data", - "(i32i32i32)"); - if (!func_on_conn_data) - func_on_conn_data = wasm_runtime_lookup_function(inst, "on_connection_data", - "(i32i32i32)"); - if (!func_on_conn_data) { - printf("Cannot find function on_connection_data\n"); - return; - } - - /* 0 len means connection closed */ - if (conn_event->len == 0) { - argv[0] = conn_event->handle; - argv[1] = 0; - argv[2] = 0; - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_on_conn_data, - 3, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - printf(":Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - return; - } - } else { - data_offset = wasm_runtime_module_dup_data(inst, - conn_event->data, - conn_event->len); - if (data_offset == 0) { - const char *exception = wasm_runtime_get_exception(inst); - if (exception) { - printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - } - return; - } - - argv[0] = conn_event->handle; - argv[1] = (uint32) data_offset; - argv[2] = conn_event->len; - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_on_conn_data, - 3, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - printf(":Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - wasm_runtime_module_free(inst, data_offset); - return; - } - wasm_runtime_module_free(inst, data_offset); - } -} - -bool init_connection_framework() -{ - korp_thread tid; - - epollfd = epoll_create(MAX_EVENTS); - if (epollfd == -1) - return false; - - if (vm_mutex_init(&g_lock) != 0) { - close(epollfd); - return false; - } - - if (!wasm_register_cleanup_callback(cleanup_connections)) { - goto fail; - } - - if (!wasm_register_msg_callback(CONNECTION_EVENT_WASM, - app_mgr_connection_event_callback)) { - goto fail; - } - - if (vm_thread_create(&tid, - polling_thread_routine, - NULL, - BH_APPLET_PRESERVED_STACK_SIZE) != 0) { - goto fail; - } - - return true; - -fail: - vm_mutex_destroy(&g_lock); - close(epollfd); - return false; -} - -void exit_connection_framework() -{ - polling_thread_run = false; -} diff --git a/core/app-framework/connection/native/linux/connection_mgr.cmake b/core/app-framework/connection/native/linux/connection_mgr.cmake deleted file mode 100644 index c8f2b487e7..0000000000 --- a/core/app-framework/connection/native/linux/connection_mgr.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_CONN_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_LIB_CONN_MGR_DIR}) - - -file (GLOB_RECURSE source_all ${WASM_LIB_CONN_MGR_DIR}/*.c) - -set (WASM_LIB_CONN_MGR_SOURCE ${source_all}) - - diff --git a/core/app-framework/connection/native/wasm_lib.cmake b/core/app-framework/connection/native/wasm_lib.cmake deleted file mode 100644 index d78763f749..0000000000 --- a/core/app-framework/connection/native/wasm_lib.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_CONN_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_LIB_CONN_DIR}) - -include (${CMAKE_CURRENT_LIST_DIR}/${WAMR_BUILD_PLATFORM}/connection_mgr.cmake) - -file (GLOB source_all - ${WASM_LIB_CONN_MGR_SOURCE} - ${WASM_LIB_CONN_DIR}/*.c -) - -set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) diff --git a/core/app-framework/connection/native/zephyr/connection_lib_impl.c b/core/app-framework/connection/native/zephyr/connection_lib_impl.c deleted file mode 100644 index 9c30edcaf6..0000000000 --- a/core/app-framework/connection/native/zephyr/connection_lib_impl.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/* - * Note: - * This file implements the linux version connection library which is - * defined in connection_lib.h. - * It also provides a reference impl of connections manager. - */ - -#include "connection_lib.h" - -/* - * Platform implementation of connection library - */ -connection_interface_t connection_impl = { - ._open = NULL, - ._close = NULL, - ._send = NULL, - ._config = NULL -}; diff --git a/core/app-framework/connection/native/zephyr/connection_mgr.cmake b/core/app-framework/connection/native/zephyr/connection_mgr.cmake deleted file mode 100644 index c8f2b487e7..0000000000 --- a/core/app-framework/connection/native/zephyr/connection_mgr.cmake +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_CONN_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_LIB_CONN_MGR_DIR}) - - -file (GLOB_RECURSE source_all ${WASM_LIB_CONN_MGR_DIR}/*.c) - -set (WASM_LIB_CONN_MGR_SOURCE ${source_all}) - - diff --git a/core/app-framework/sensor/app/sensor.c b/core/app-framework/sensor/app/sensor.c deleted file mode 100644 index 8427af4ef3..0000000000 --- a/core/app-framework/sensor/app/sensor.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wa-inc/sensor.h" -#include "sensor_api.h" - -typedef struct _sensor { - struct _sensor * next; - char *name; - uint32 handle; - void (*sensor_callback)(sensor_t, attr_container_t *, void *); - void *user_data; -} sensor; - -static sensor_t g_sensors = NULL; - -sensor_t sensor_open(const char* name, int index, - sensor_event_handler_f sensor_event_handler, - void *user_data) -{ - uint32 id = wasm_sensor_open(name, index); - if (id == -1) - return NULL; - - //create local node for holding the user callback - sensor_t sensor = (sensor_t) malloc(sizeof(struct _sensor)); - if (sensor == NULL) - return NULL; - - memset(sensor, 0, sizeof(struct _sensor)); - sensor->handle = id; - sensor->name = strdup(name); - sensor->user_data = user_data; - sensor->sensor_callback = sensor_event_handler; - - if (!sensor->name) { - free(sensor); - return NULL; - } - - if (g_sensors == NULL) { - g_sensors = sensor; - } else { - sensor->next = g_sensors; - g_sensors = sensor; - } - - return sensor; -} - -bool sensor_config_with_attr_container(sensor_t sensor, attr_container_t *cfg) -{ - char *buffer = (char *)cfg; - int len = attr_container_get_serialize_length(cfg); - - return wasm_sensor_config_with_attr_container(sensor->handle, buffer, len); -} - -bool sensor_config(sensor_t sensor, int interval, int bit_cfg, int delay) -{ - bool ret = wasm_sensor_config(sensor->handle, interval, bit_cfg, delay); - return ret; -} - -bool sensor_close(sensor_t sensor) -{ - wasm_sensor_close(sensor->handle); - - // remove local node - sensor_t s = g_sensors; - sensor_t prev = NULL; - while (s) { - if (s == sensor) { - if (prev == NULL) { - g_sensors = s->next; - } else { - prev->next = s->next; - } - free(s->name); - free(s); - return true; - } else { - prev = s; - s = s->next; - } - } - - return false; -} - -/* - * - * API for native layer to callback for sensor events - * - */ - -void on_sensor_event(uint32 sensor_id, char * buffer, int len) -{ - attr_container_t * sensor_data = (attr_container_t *) buffer; - - // lookup the sensor and call the handlers - sensor_t s = g_sensors; - sensor_t prev = NULL; - while (s) { - if (s->handle == sensor_id) { - s->sensor_callback(s, sensor_data, s->user_data); - break; - } - - s = s->next; - } -} diff --git a/core/app-framework/sensor/app/sensor_api.h b/core/app-framework/sensor/app/sensor_api.h deleted file mode 100644 index 9f67677026..0000000000 --- a/core/app-framework/sensor/app/sensor_api.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _SENSOR_API_H_ -#define _SENSOR_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -uint32 -wasm_sensor_open(const char* name, int instance); - -bool -wasm_sensor_config(uint32 sensor, int interval, int bit_cfg, int delay); - -bool -wasm_sensor_config_with_attr_container(uint32 sensor, char *buffer, int len); - -bool -wasm_sensor_close(uint32 sensor); - -#ifdef __cplusplus -} -#endif - -#endif /* end of _SENSOR_API_H_ */ - diff --git a/core/app-framework/sensor/app/wa-inc/sensor.h b/core/app-framework/sensor/app/wa-inc/sensor.h deleted file mode 100644 index 8abc1addff..0000000000 --- a/core/app-framework/sensor/app/wa-inc/sensor.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _AEE_SENSOR_H_ -#define _AEE_SENSOR_H_ - -#include "bi-inc/attr_container.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* board producer define sensor */ -struct _sensor; -typedef struct _sensor *sensor_t; - -/** - * @typedef sensor_event_handler_f - * - * @brief Define the signature of callback function for API - * sensor_open() to handle sensor event. - * - * @param sensor the sensor which the event belong to - * @param sensor_event the sensor event - * @param user_data user data associated with the sensor which is set when - * calling sensor_open(). - * - * @see sensor_open - */ -typedef void (*sensor_event_handler_f)(sensor_t sensor, - attr_container_t *sensor_event, - void *user_data); - -/* - ***************** - * Sensor APIs - ***************** - */ - -/** - * @brief Open sensor. - * - * @param name sensor name - * @param index sensor index - * @param handler callback function to handle the sensor event - * @param user_data user data - * - * @return the sensor opened if success, NULL otherwise - */ -sensor_t sensor_open(const char* name, - int index, - sensor_event_handler_f handler, - void *user_data); - -/** - * @brief Configure sensor with interval/bit_cfg/delay values. - * - * @param sensor the sensor to be configured - * @param interval sensor event interval - * @param bit_cfg sensor bit config - * @param delay sensor delay - * - * @return true if success, false otherwise - */ -bool sensor_config(sensor_t sensor, int interval, int bit_cfg, int delay); - -/** - * @brief Configure sensor with attr_container_t object. - * - * @param sensor the sensor to be configured - * @param cfg the configuration - * - * @return true if success, false otherwise - */ -bool sensor_config_with_attr_container(sensor_t sensor, attr_container_t *cfg); - -/** - * @brief Close sensor. - * - * @param sensor the sensor to be closed - * - * @return true if success, false otherwise - */ -bool sensor_close(sensor_t sensor); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/app-framework/sensor/app/wasm_app.cmake b/core/app-framework/sensor/app/wasm_app.cmake deleted file mode 100644 index 4b14a8bef9..0000000000 --- a/core/app-framework/sensor/app/wasm_app.cmake +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_APP_SENSOR_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_APP_SENSOR_DIR}) - - -file (GLOB_RECURSE source_all ${WASM_APP_SENSOR_DIR}/*.c) - -set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/core/app-framework/sensor/native/runtime_sensor.c b/core/app-framework/sensor/native/runtime_sensor.c deleted file mode 100644 index 5a10434cba..0000000000 --- a/core/app-framework/sensor/native/runtime_sensor.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "runtime_sensor.h" -#include "app_manager_export.h" -#include "module_wasm_app.h" -#include "bh_thread.h" -#include "bh_time.h" -#include "bh_common.h" -#include "bh_assert.h" - -static sys_sensor_t * g_sys_sensors = NULL; -static int g_sensor_id_max = 0; - -static sensor_client_t * -find_sensor_client(sys_sensor_t * sensor, - unsigned int client_id, bool remove_if_found); - -void (*rechedule_sensor_callback)() = NULL; - -/* - * API for the applications to call - don't call it from the runtime - * - */ - -static void -sensor_event_cleaner(sensor_event_data_t *sensor_event) -{ - if (sensor_event->data != NULL) { - if (sensor_event->data_fmt == FMT_ATTR_CONTAINER) - attr_container_destroy(sensor_event->data); - else - bh_free(sensor_event->data); - } - - bh_free(sensor_event); -} - -static void -wasm_sensor_callback(void *client, uint32 sensor_id, void *user_data) -{ - attr_container_t *sensor_data = (attr_container_t *) user_data; - attr_container_t *sensor_data_clone; - int sensor_data_len; - sensor_event_data_t *sensor_event; - bh_message_t msg; - sensor_client_t *c = (sensor_client_t *) client; - - module_data *module = module_data_list_lookup_id(c->client_id); - if (module == NULL) - return; - - if (sensor_data == NULL) - return; - - sensor_data_len = attr_container_get_serialize_length(sensor_data); - sensor_data_clone = (attr_container_t *)bh_malloc(sensor_data_len); - if (sensor_data_clone == NULL) - return; - - /* multiple sensor clients may use/free the sensor data, so make a copy */ - bh_memcpy_s(sensor_data_clone, sensor_data_len, - sensor_data, sensor_data_len); - - sensor_event = (sensor_event_data_t *)bh_malloc(sizeof(*sensor_event)); - if (sensor_event == NULL) { - bh_free(sensor_data_clone); - return; - } - - memset(sensor_event, 0, sizeof(*sensor_event)); - sensor_event->sensor_id = sensor_id; - sensor_event->data = sensor_data_clone; - sensor_event->data_fmt = FMT_ATTR_CONTAINER; - - msg = bh_new_msg(SENSOR_EVENT_WASM, - sensor_event, - sizeof(*sensor_event), - sensor_event_cleaner); - if (!msg) { - sensor_event_cleaner(sensor_event); - return; - } - - bh_post_msg2(module->queue, msg); -} - -bool -wasm_sensor_config(wasm_exec_env_t exec_env, - uint32 sensor, int interval, - int bit_cfg, int delay) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - attr_container_t * attr_cont; - sensor_client_t * c; - sensor_obj_t s = find_sys_sensor_id(sensor); - if (s == NULL) - return false; - - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(mod_id != ID_NONE); - - vm_mutex_lock(&s->lock); - - c = find_sensor_client(s, mod_id, false); - if (c == NULL) { - vm_mutex_unlock(&s->lock); - return false; - } - - c->interval = interval; - c->bit_cfg = bit_cfg; - c->delay = delay; - - vm_mutex_unlock(&s->lock); - - if (s->config != NULL) { - attr_cont = attr_container_create("config sensor"); - attr_container_set_int(&attr_cont, "interval", interval); - attr_container_set_int(&attr_cont, "bit_cfg", bit_cfg); - attr_container_set_int(&attr_cont, "delay", delay); - s->config(s, attr_cont); - attr_container_destroy(attr_cont); - } - - refresh_read_interval(s); - - reschedule_sensor_read(); - - return true; -} - -uint32 -wasm_sensor_open(wasm_exec_env_t exec_env, - int32 name_offset, int instance) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *name = NULL; - - if (!validate_app_str_addr(name_offset)) - return -1; - - name = addr_app_to_native(name_offset); - - if (name != NULL) { - sensor_client_t *c; - sys_sensor_t *s = find_sys_sensor(name, instance); - if (s == NULL) - return -1; - - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(mod_id != ID_NONE); - - vm_mutex_lock(&s->lock); - - c = find_sensor_client(s, mod_id, false); - if (c) { - // the app already opened this sensor - vm_mutex_unlock(&s->lock); - return -1; - } - - sensor_client_t * client = (sensor_client_t*) bh_malloc( - sizeof(sensor_client_t)); - if (client == NULL) { - vm_mutex_unlock(&s->lock); - return -1; - } - - memset(client, 0, sizeof(sensor_client_t)); - client->client_id = mod_id; - client->client_callback = (void *)wasm_sensor_callback; - client->interval = s->default_interval; - client->next = s->clients; - s->clients = client; - - vm_mutex_unlock(&s->lock); - - refresh_read_interval(s); - - reschedule_sensor_read(); - - return s->sensor_id; - } - - return -1; -} - -bool -wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, - uint32 sensor, int32 buffer_offset, - int len) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - if (!validate_app_addr(buffer_offset, len)) - return false; - - buffer = addr_app_to_native(buffer_offset); - - if (buffer != NULL) { - attr_container_t *cfg = (attr_container_t *)buffer; - sensor_obj_t s = find_sys_sensor_id(sensor); - if (s == NULL) - return false; - - if (s->config == NULL) - return false; - - return s->config(s, cfg); - } - - return false; -} - -bool -wasm_sensor_close(wasm_exec_env_t exec_env, uint32 sensor) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - unsigned int client_id = mod_id; - sensor_obj_t s = find_sys_sensor_id(sensor); - sensor_client_t *c; - - bh_assert(mod_id != ID_NONE); - - if (s == NULL) - return false; - - vm_mutex_lock(&s->lock); - if ((c = find_sensor_client(s, client_id, true)) != NULL) - bh_free(c); - vm_mutex_unlock(&s->lock); - - refresh_read_interval(s); - - reschedule_sensor_read(); - - return true; -} - -/* - * - * sensor framework API - don't expose to the applications - * - */ -void set_sensor_reshceduler(void (*callback)()) -{ - rechedule_sensor_callback = callback; -} - -// used for other threads to wakeup the sensor read thread -void reschedule_sensor_read() -{ - if (rechedule_sensor_callback) - rechedule_sensor_callback(); -} - -void refresh_read_interval(sensor_obj_t sensor) -{ - sensor_client_t *c; - uint32 interval = sensor->default_interval; - vm_mutex_lock(&sensor->lock); - - c = sensor->clients; - if (c) - interval = c->interval; - - while (c) { - if (c->interval < interval) - interval = c->interval; - c = c->next; - } - - vm_mutex_unlock(&sensor->lock); - - sensor->read_interval = interval; -} - -sensor_obj_t -add_sys_sensor(char * name, char * description, int instance, - uint32 default_interval, void * read_func, void * config_func) -{ - sys_sensor_t * s = (sys_sensor_t *) bh_malloc(sizeof(sys_sensor_t)); - if (s == NULL) - return NULL; - - memset(s, 0, sizeof(*s)); - s->name = bh_strdup(name); - s->sensor_instance = instance; - s->default_interval = default_interval; - - if (!s->name) { - bh_free(s); - return NULL; - } - - if (description) { - s->description = bh_strdup(description); - if (!s->description) { - bh_free(s->name); - bh_free(s); - return NULL; - } - } - - g_sensor_id_max++; - if (g_sensor_id_max == -1) - g_sensor_id_max++; - s->sensor_id = g_sensor_id_max; - - s->read = read_func; - s->config = config_func; - - if (g_sys_sensors == NULL) { - g_sys_sensors = s; - } else { - s->next = g_sys_sensors; - g_sys_sensors = s; - } - - vm_mutex_init(&s->lock); - - return s; -} - -sensor_obj_t find_sys_sensor(const char* name, int instance) -{ - sys_sensor_t * s = g_sys_sensors; - while (s) { - if (strcmp(s->name, name) == 0 && s->sensor_instance == instance) - return s; - - s = s->next; - } - return NULL; -} - -sensor_obj_t find_sys_sensor_id(uint32 sensor_id) -{ - sys_sensor_t * s = g_sys_sensors; - while (s) { - if (s->sensor_id == sensor_id) - return s; - - s = s->next; - } - return NULL; -} - -sensor_client_t *find_sensor_client(sys_sensor_t * sensor, - unsigned int client_id, bool remove_if_found) -{ - sensor_client_t *prev = NULL, *c = sensor->clients; - - while (c) { - sensor_client_t *next = c->next; - if (c->client_id == client_id) { - if (remove_if_found) { - if (prev) - prev->next = next; - else - sensor->clients = next; - } - return c; - } else { - c = c->next; - } - } - - return NULL; -} - -// return the milliseconds to next check -int check_sensor_timers() -{ - int ms_to_next_check = -1; - uint32 now = (uint32) bh_get_tick_ms(); - - sys_sensor_t * s = g_sys_sensors; - while (s) { - uint32 last_read = s->last_read; - uint32 elpased_ms = bh_get_elpased_ms(&last_read); - - if (s->read_interval <= 0 || s->clients == NULL) { - s = s->next; - continue; - } - - if (elpased_ms >= s->read_interval) { - attr_container_t * data = s->read(s); - if (data) { - sensor_client_t * client = s->clients; - while (client) { - client->client_callback(client, s->sensor_id, data); - client = client->next; - } - attr_container_destroy(data); - } - - s->last_read = now; - - if (ms_to_next_check == -1 || (ms_to_next_check < s->read_interval)) - ms_to_next_check = s->read_interval; - } else { - int remaining = s->read_interval - elpased_ms; - if (ms_to_next_check == -1 || (ms_to_next_check < remaining)) - ms_to_next_check = remaining; - - } - - s = s->next; - } - - return ms_to_next_check; -} - -void sensor_cleanup_callback(uint32 module_id) -{ - sys_sensor_t * s = g_sys_sensors; - - while (s) { - sensor_client_t *c; - vm_mutex_lock(&s->lock); - if ((c = find_sensor_client(s, module_id, true)) != NULL) { - bh_free(c); - } - vm_mutex_unlock(&s->lock); - s = s->next; - } -} diff --git a/core/app-framework/sensor/native/runtime_sensor.h b/core/app-framework/sensor/native/runtime_sensor.h deleted file mode 100644 index 03ce65c0f3..0000000000 --- a/core/app-framework/sensor/native/runtime_sensor.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef LIB_EXTENSION_RUNTIME_SENSOR_H_ -#define LIB_EXTENSION_RUNTIME_SENSOR_H_ - -#include "bh_platform.h" -#include "bi-inc/attr_container.h" -#include "wasm_export.h" - -struct _sys_sensor; -typedef struct _sys_sensor* sensor_obj_t; - -typedef struct _sensor_client { - struct _sensor_client * next; - unsigned int client_id; // the app id - int interval; - int bit_cfg; - int delay; - void (*client_callback)(void * client, uint32, attr_container_t *); -} sensor_client_t; - -typedef struct _sys_sensor { - struct _sys_sensor * next; - char * name; - int sensor_instance; - char * description; - uint32 sensor_id; - sensor_client_t * clients; - /* app, sensor mgr and app mgr may access the clients at the same time, - * so need a lock to protect the clients */ - korp_mutex lock; - uint32 last_read; - uint32 read_interval; - uint32 default_interval; - - attr_container_t * (*read)(void *); /* TODO: may support other type return value, such as 'cbor' */ - bool (*config)(void *, void *); - -} sys_sensor_t; - -sensor_obj_t add_sys_sensor(char * name, char * description, int instance, - uint32 default_interval, void * read_func, void * config_func); -sensor_obj_t find_sys_sensor(const char* name, int instance); -sensor_obj_t find_sys_sensor_id(uint32 sensor_id); -void refresh_read_interval(sensor_obj_t sensor); -void sensor_cleanup_callback(uint32 module_id); -int check_sensor_timers(); -void reschedule_sensor_read(); - -uint32 -wasm_sensor_open(wasm_exec_env_t exec_env, - int32 name_offset, int instance); - -bool -wasm_sensor_config(wasm_exec_env_t exec_env, - uint32 sensor, int interval, int bit_cfg, int delay); - -bool -wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, - uint32 sensor, int32 buffer_offset, - int len); - -bool -wasm_sensor_close(wasm_exec_env_t exec_env, uint32 sensor); - -#endif /* LIB_EXTENSION_RUNTIME_SENSOR_H_ */ diff --git a/core/app-framework/sensor/native/runtime_sensor.inl b/core/app-framework/sensor/native/runtime_sensor.inl deleted file mode 100644 index 1b7af765bf..0000000000 --- a/core/app-framework/sensor/native/runtime_sensor.inl +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -EXPORT_WASM_API(wasm_sensor_open), -EXPORT_WASM_API(wasm_sensor_config), -EXPORT_WASM_API(wasm_sensor_config_with_attr_container), -EXPORT_WASM_API(wasm_sensor_close), diff --git a/core/app-framework/sensor/native/sensor_api.h b/core/app-framework/sensor/native/sensor_api.h deleted file mode 100644 index 0546ecf497..0000000000 --- a/core/app-framework/sensor/native/sensor_api.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _SENSOR_API_H_ -#define _SENSOR_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -uint32 -wasm_sensor_open(int32 name_offset, int instance); - -bool -wasm_sensor_config(uint32 sensor, int interval, int bit_cfg, int delay); - -bool -wasm_sensor_config_with_attr_container(uint32 sensor, int32 buffer_offset, int len); - -bool -wasm_sensor_close(uint32 sensor); - -#ifdef __cplusplus -} -#endif - -#endif /* end of _SENSOR_API_H_ */ - diff --git a/core/app-framework/sensor/native/sensor_mgr_ref.c b/core/app-framework/sensor/native/sensor_mgr_ref.c deleted file mode 100644 index b31c696b2a..0000000000 --- a/core/app-framework/sensor/native/sensor_mgr_ref.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "runtime_sensor.h" -#include "bi-inc/attr_container.h" -#include "module_wasm_app.h" -#include "wasm_export.h" - -/* - * - * One reference implementation for sensor manager - * - * - */ -static korp_cond cond; -static korp_mutex mutex; -static bool sensor_check_thread_run = true; - -void app_mgr_sensor_event_callback(module_data *m_data, bh_message_t msg) -{ - uint32 argv[3]; - wasm_function_inst_t func_onSensorEvent; - - bh_assert(SENSOR_EVENT_WASM == bh_message_type(msg)); - wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; - wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; - - sensor_event_data_t *payload = (sensor_event_data_t*) - bh_message_payload(msg); - if (payload == NULL) - return; - - func_onSensorEvent = wasm_runtime_lookup_function(inst, "_on_sensor_event", - "(i32i32i32)"); - if (!func_onSensorEvent) - func_onSensorEvent = wasm_runtime_lookup_function(inst, "on_sensor_event", - "(i32i32i32)"); - if (!func_onSensorEvent) { - printf("Cannot find function on_sensor_event\n"); - } else { - int32 sensor_data_offset; - uint32 sensor_data_len; - - if (payload->data_fmt == FMT_ATTR_CONTAINER) { - sensor_data_len = attr_container_get_serialize_length(payload->data); - } else { - printf("Unsupported sensor data format: %d\n", payload->data_fmt); - return; - } - - sensor_data_offset = wasm_runtime_module_dup_data(inst, payload->data, - sensor_data_len); - if (sensor_data_offset == 0) { - const char *exception = wasm_runtime_get_exception(inst); - if (exception) { - printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - } - return; - } - - argv[0] = payload->sensor_id; - argv[1] = (uint32) sensor_data_offset; - argv[2] = sensor_data_len; - - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onSensorEvent, - 3, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - printf(":Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - wasm_runtime_module_free(inst, sensor_data_offset); - return; - } - - wasm_runtime_module_free(inst, sensor_data_offset); - } -} - -static attr_container_t * read_test_sensor(void * sensor) -{ - //luc: for test - attr_container_t *attr_obj = attr_container_create("read test sensor data"); - if (attr_obj) { - attr_container_set_string(&attr_obj, "name", "read test sensor"); - return attr_obj; - } - return NULL; -} - -static bool config_test_sensor(void * s, void * config) -{ - return false; -} - -static void thread_sensor_check(void * arg) -{ - while (sensor_check_thread_run) { - int ms_to_expiry = check_sensor_timers(); - if (ms_to_expiry == -1) - ms_to_expiry = 5000; - vm_mutex_lock(&mutex); - vm_cond_reltimedwait(&cond, &mutex, ms_to_expiry); - vm_mutex_unlock(&mutex); - } -} - -static void cb_wakeup_thread() -{ - vm_cond_signal(&cond); -} - -void set_sensor_reshceduler(void (*callback)()); - -void init_sensor_framework() -{ - // init the mutext and conditions - korp_thread tid; - vm_cond_init(&cond); - vm_mutex_init(&mutex); - - // add the sys sensor objects - add_sys_sensor("sensor_test", "This is a sensor for test", 0, 1000, - read_test_sensor, config_test_sensor); - - set_sensor_reshceduler(cb_wakeup_thread); - - wasm_register_msg_callback(SENSOR_EVENT_WASM, - app_mgr_sensor_event_callback); - - wasm_register_cleanup_callback(sensor_cleanup_callback); - - vm_thread_create(&tid, (void *)thread_sensor_check, NULL, - BH_APPLET_PRESERVED_STACK_SIZE); -} - -void exit_sensor_framework() -{ - sensor_check_thread_run = false; -} - diff --git a/core/app-framework/sensor/native/wasm_lib.cmake b/core/app-framework/sensor/native/wasm_lib.cmake deleted file mode 100644 index e233334c1f..0000000000 --- a/core/app-framework/sensor/native/wasm_lib.cmake +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_SENSOR_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${WASM_LIB_SENSOR_DIR}) - - -file (GLOB_RECURSE source_all ${WASM_LIB_SENSOR_DIR}/*.c) - -set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) - diff --git a/core/app-framework/template/app/wa-inc/app_xxx.h b/core/app-framework/template/app/wa-inc/app_xxx.h deleted file mode 100644 index ac30842f09..0000000000 --- a/core/app-framework/template/app/wa-inc/app_xxx.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/* - header file for wasm application -*/ \ No newline at end of file diff --git a/core/app-framework/template/app/wasm_app.cmake b/core/app-framework/template/app/wasm_app.cmake deleted file mode 100644 index 16ca237ae1..0000000000 --- a/core/app-framework/template/app/wasm_app.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_APP_CURRENT_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories( - ${WASM_APP_CURRENT_DIR} - # Add your include dir here -) - -file (GLOB_RECURSE source_all - ${WASM_APP_CURRENT_DIR}/*.c - # Add your source file here -) - -set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/core/app-framework/template/native/app_xxx.inl b/core/app-framework/template/native/app_xxx.inl deleted file mode 100644 index 2503fe454e..0000000000 --- a/core/app-framework/template/native/app_xxx.inl +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/* EXPORT_WASM_API(your_api_here), */ diff --git a/core/app-framework/template/native/wasm_lib.cmake b/core/app-framework/template/native/wasm_lib.cmake deleted file mode 100644 index 2601c1d271..0000000000 --- a/core/app-framework/template/native/wasm_lib.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_CURRENT_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories( - ${WASM_LIB_CURRENT_DIR} - # Add your include dir here -) - -file (GLOB_RECURSE source_all - ${WASM_LIB_CURRENT_DIR}/*.c - # Add your source file here -) - -set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) - diff --git a/core/app-framework/wgl/app/gui_api.h b/core/app-framework/wgl/app/gui_api.h deleted file mode 100644 index 991b079eeb..0000000000 --- a/core/app-framework/wgl/app/gui_api.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _GUI_API_H_ -#define _GUI_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void -wasm_obj_native_call(int32 func_id, uint32 *argv, uint32 argc); - -void -wasm_btn_native_call(int32 func_id, uint32 *argv, uint32 argc); - -void -wasm_label_native_call(int32 func_id, uint32 *argv, uint32 argc); - -void -wasm_cb_native_call(int32 func_id, uint32 *argv, uint32 argc); - -void -wasm_list_native_call(int32 func_id, uint32 *argv, uint32 argc); - - -#ifdef __cplusplus -} -#endif - - -#endif /* end of _GUI_API_H_ */ diff --git a/core/app-framework/wgl/app/src/wgl_btn.c b/core/app-framework/wgl/app/src/wgl_btn.c deleted file mode 100644 index 6f6b1b953c..0000000000 --- a/core/app-framework/wgl/app/src/wgl_btn.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wa-inc/wgl.h" -#include "bh_platform.h" -#include "gui_api.h" - -#define ARGC sizeof(argv)/sizeof(uint32) -#define CALL_BTN_NATIVE_FUNC(id) wasm_btn_native_call(id, argv, ARGC) - -wgl_obj_t wgl_btn_create(wgl_obj_t par, wgl_obj_t copy) -{ - uint32 argv[2] = {0}; - - argv[0] = (uint32)par; - argv[1] = (uint32)copy; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_CREATE); - return (wgl_obj_t)argv[0]; -} - -void wgl_btn_set_toggle(wgl_obj_t btn, bool tgl) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)btn; - argv[1] = tgl; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_TOGGLE); -} - -void wgl_btn_set_state(wgl_obj_t btn, wgl_btn_state_t state) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)btn; - argv[1] = state; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_STATE); -} - -void wgl_btn_toggle(wgl_obj_t btn) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)btn; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_TOGGLE); -} - -void wgl_btn_set_ink_in_time(wgl_obj_t btn, uint16_t time) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)btn; - argv[1] = time; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_INK_IN_TIME); -} - -void wgl_btn_set_ink_wait_time(wgl_obj_t btn, uint16_t time) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)btn; - argv[1] = time; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_INK_WAIT_TIME); -} - -void wgl_btn_set_ink_out_time(wgl_obj_t btn, uint16_t time) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)btn; - argv[1] = time; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_INK_OUT_TIME); -} - -//void wgl_btn_set_style(wgl_obj_t btn, wgl_btn_style_t type, const wgl_style_t * style) -//{ -// //TODO: pack style -// //wasm_btn_set_style(btn, type, style); -//} -// -wgl_btn_state_t wgl_btn_get_state(const wgl_obj_t btn) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)btn; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_STATE); - return (wgl_btn_state_t)argv[0]; -} - -bool wgl_btn_get_toggle(const wgl_obj_t btn) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)btn; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_TOGGLE); - return (bool)argv[0]; -} - -uint16_t wgl_btn_get_ink_in_time(const wgl_obj_t btn) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)btn; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_INK_IN_TIME); - return (uint16_t)argv[0]; -} - -uint16_t wgl_btn_get_ink_wait_time(const wgl_obj_t btn) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)btn; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_INK_WAIT_TIME); - return (uint16_t)argv[0]; -} - -uint16_t wgl_btn_get_ink_out_time(const wgl_obj_t btn) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)btn; - CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_INK_OUT_TIME); - return (uint16_t)argv[0]; -} -// -//const wgl_style_t * wgl_btn_get_style(const wgl_obj_t btn, wgl_btn_style_t type) -//{ -// //TODO: pack style -// //wasm_btn_get_style(btn, type); -// return NULL; -//} diff --git a/core/app-framework/wgl/app/src/wgl_cb.c b/core/app-framework/wgl/app/src/wgl_cb.c deleted file mode 100644 index 7ede61a103..0000000000 --- a/core/app-framework/wgl/app/src/wgl_cb.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wa-inc/wgl.h" -#include "gui_api.h" - -#include - -#define ARGC sizeof(argv)/sizeof(uint32) -#define CALL_CB_NATIVE_FUNC(id) wasm_cb_native_call(id, argv, ARGC) - -wgl_obj_t wgl_cb_create(wgl_obj_t par, const wgl_obj_t copy) -{ - uint32 argv[2] = {0}; - - argv[0] = (uint32)par; - argv[1] = (uint32)copy; - CALL_CB_NATIVE_FUNC(CB_FUNC_ID_CREATE); - return (wgl_obj_t)argv[0]; -} - -void wgl_cb_set_text(wgl_obj_t cb, const char * txt) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)cb; - argv[1] = (uint32)txt; - argv[2] = strlen(txt) + 1; - CALL_CB_NATIVE_FUNC(CB_FUNC_ID_SET_TEXT); -} - -void wgl_cb_set_static_text(wgl_obj_t cb, const char * txt) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)cb; - argv[1] = (uint32)txt; - argv[2] = strlen(txt) + 1; - CALL_CB_NATIVE_FUNC(CB_FUNC_ID_SET_STATIC_TEXT); -} - -//void wgl_cb_set_style(wgl_obj_t cb, wgl_cb_style_t type, const wgl_style_t * style) -//{ -// //TODO: -//} -// - -unsigned int wgl_cb_get_text_length(wgl_obj_t cb) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)cb; - CALL_CB_NATIVE_FUNC(CB_FUNC_ID_GET_TEXT_LENGTH); - return argv[0]; -} - -char *wgl_cb_get_text(wgl_obj_t cb, char *buffer, int buffer_len) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)cb; - argv[1] = (uint32)buffer; - argv[2] = buffer_len; - CALL_CB_NATIVE_FUNC(CB_FUNC_ID_GET_TEXT); - return (char *)argv[0]; -} - -//const wgl_style_t * wgl_cb_get_style(const wgl_obj_t cb, wgl_cb_style_t type) -//{ -// //TODO -// return NULL; -//} -// - - diff --git a/core/app-framework/wgl/app/src/wgl_label.c b/core/app-framework/wgl/app/src/wgl_label.c deleted file mode 100644 index f96508c331..0000000000 --- a/core/app-framework/wgl/app/src/wgl_label.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - - -#include "wa-inc/wgl.h" -#include "gui_api.h" -#include - - -#define ARGC sizeof(argv)/sizeof(uint32) -#define CALL_LABEL_NATIVE_FUNC(id) wasm_label_native_call(id, argv, ARGC) - -wgl_obj_t wgl_label_create(wgl_obj_t par, wgl_obj_t copy) -{ - uint32 argv[2] = {0}; - - argv[0] = (uint32)par; - argv[1] = (uint32)copy; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_CREATE); - return (wgl_obj_t)argv[0]; -} - -void wgl_label_set_text(wgl_obj_t label, const char * text) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = (uint32)text; - argv[2] = strlen(text) + 1; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_TEXT); -} - - -void wgl_label_set_array_text(wgl_obj_t label, const char * array, uint16_t size) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = (uint32)array; - argv[2] = size; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_ARRAY_TEXT); -} - - -void wgl_label_set_static_text(wgl_obj_t label, const char * text) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = (uint32)text; - argv[2] = strlen(text) + 1; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_STATIC_TEXT); -} - - -void wgl_label_set_long_mode(wgl_obj_t label, wgl_label_long_mode_t long_mode) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = long_mode; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_LONG_MODE); -} - - -void wgl_label_set_align(wgl_obj_t label, wgl_label_align_t align) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = align; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_ALIGN); -} - - -void wgl_label_set_recolor(wgl_obj_t label, bool en) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = en; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_RECOLOR); -} - - -void wgl_label_set_body_draw(wgl_obj_t label, bool en) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = en; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_BODY_DRAW); -} - - -void wgl_label_set_anim_speed(wgl_obj_t label, uint16_t anim_speed) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = anim_speed; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_ANIM_SPEED); -} - - -void wgl_label_set_text_sel_start(wgl_obj_t label, uint16_t index) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = index; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_TEXT_SEL_START); -} - - -void wgl_label_set_text_sel_end(wgl_obj_t label, uint16_t index) -{ - uint32 argv[2] = {0}; - argv[0] = (uint32)label; - argv[1] = index; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_TEXT_SEL_END); -} - -unsigned int wgl_label_get_text_length(wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT_LENGTH); - return argv[0]; -} - -char * wgl_label_get_text(wgl_obj_t label, char *buffer, int buffer_len) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = (uint32)buffer; - argv[2] = buffer_len; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT); - return (char *)argv[0]; -} - - -wgl_label_long_mode_t wgl_label_get_long_mode(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LONG_MODE); - return (wgl_label_long_mode_t)argv[0]; -} - - -wgl_label_align_t wgl_label_get_align(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_ALIGN); - return (wgl_label_align_t)argv[0]; -} - - -bool wgl_label_get_recolor(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_RECOLOR); - return (bool)argv[0]; -} - - -bool wgl_label_get_body_draw(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_BODY_DRAW); - return (bool)argv[0]; -} - - -uint16_t wgl_label_get_anim_speed(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_ANIM_SPEED); - return (uint16_t)argv[0]; -} - - -void wgl_label_get_letter_pos(const wgl_obj_t label, uint16_t index, wgl_point_t * pos) -{ - uint32 argv[4] = {0}; - argv[0] = (uint32)label; - argv[1] = index; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LETTER_POS); - pos->x = argv[2]; - pos->y = argv[3]; -} - - -uint16_t wgl_label_get_letter_on(const wgl_obj_t label, wgl_point_t * pos) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = pos->x; - argv[2] = pos->y; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LETTER_POS); - return (uint16_t)argv[0]; -} - - -bool wgl_label_is_char_under_pos(const wgl_obj_t label, wgl_point_t * pos) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = pos->x; - argv[2] = pos->y; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LETTER_POS); - return (bool)argv[0]; -} - - -uint16_t wgl_label_get_text_sel_start(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT_SEL_START); - return (uint16_t)argv[0]; -} - - -uint16_t wgl_label_get_text_sel_end(const wgl_obj_t label) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)label; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT_SEL_END); - return (uint16_t)argv[0]; -} - - -void wgl_label_ins_text(wgl_obj_t label, uint32_t pos, const char * txt) -{ - uint32 argv[4] = {0}; - argv[0] = (uint32)label; - argv[1] = pos; - argv[2] = (uint32)txt; - argv[3] = strlen(txt) + 1; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_INS_TEXT); -} - - -void wgl_label_cut_text(wgl_obj_t label, uint32_t pos, uint32_t cnt) -{ - uint32 argv[3] = {0}; - argv[0] = (uint32)label; - argv[1] = pos; - argv[2] = cnt; - CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_CUT_TEXT); -} - - diff --git a/core/app-framework/wgl/app/src/wgl_list.c b/core/app-framework/wgl/app/src/wgl_list.c deleted file mode 100644 index 55893ccd55..0000000000 --- a/core/app-framework/wgl/app/src/wgl_list.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wa-inc/wgl.h" -#include "gui_api.h" - -#include - -#define ARGC sizeof(argv)/sizeof(uint32) -#define CALL_LIST_NATIVE_FUNC(id) wasm_list_native_call(id, argv, ARGC) - - -wgl_obj_t wgl_list_create(wgl_obj_t par, const wgl_obj_t copy) -{ - uint32 argv[2] = {0}; - - argv[0] = (uint32)par; - argv[1] = (uint32)copy; - - CALL_LIST_NATIVE_FUNC(LIST_FUNC_ID_CREATE); - return (wgl_obj_t)argv[0]; -} -// -// -//void wgl_list_clean(wgl_obj_t obj) -//{ -// wasm_list_clean(obj); -//} -// - -wgl_obj_t wgl_list_add_btn(wgl_obj_t list, const void * img_src, const char * txt) -{ - uint32 argv[3] = {0}; - - (void)img_src; /* doesn't support img src currently */ - - argv[0] = (uint32)list; - argv[1] = (uint32)txt; - argv[2] = strlen(txt) + 1; - CALL_LIST_NATIVE_FUNC(LIST_FUNC_ID_ADD_BTN); - return (wgl_obj_t)argv[0]; -} -// -// -//bool wgl_list_remove(const wgl_obj_t list, uint16_t index) -//{ -// return wasm_list_remove(list, index); -//} -// -// -//void wgl_list_set_single_mode(wgl_obj_t list, bool mode) -//{ -// wasm_list_set_single_mode(list, mode); -//} -// -//#if LV_USE_GROUP -// -// -//void wgl_list_set_btn_selected(wgl_obj_t list, wgl_obj_t btn) -//{ -// wasm_list_set_btn_selected(list, btn); -//} -//#endif -// -// -//void wgl_list_set_style(wgl_obj_t list, wgl_list_style_t type, const wgl_style_t * style) -//{ -// //TODO -//} -// -// -//bool wgl_list_get_single_mode(wgl_obj_t list) -//{ -// return wasm_list_get_single_mode(list); -//} -// -// -//const char * wgl_list_get_btn_text(const wgl_obj_t btn) -//{ -// return wasm_list_get_btn_text(btn); -//} -// -//wgl_obj_t wgl_list_get_btn_label(const wgl_obj_t btn) -//{ -// return wasm_list_get_btn_label(btn); -//} -// -// -//wgl_obj_t wgl_list_get_btn_img(const wgl_obj_t btn) -//{ -// return wasm_list_get_btn_img(btn); -//} -// -// -//wgl_obj_t wgl_list_get_prev_btn(const wgl_obj_t list, wgl_obj_t prev_btn) -//{ -// return wasm_list_get_prev_btn(list, prev_btn); -//} -// -// -//wgl_obj_t wgl_list_get_next_btn(const wgl_obj_t list, wgl_obj_t prev_btn) -//{ -// return wasm_list_get_next_btn(list, prev_btn); -//} -// -// -//int32_t wgl_list_get_btn_index(const wgl_obj_t list, const wgl_obj_t btn) -//{ -// return wasm_list_get_btn_index(list, btn); -//} -// -// -//uint16_t wgl_list_get_size(const wgl_obj_t list) -//{ -// return wasm_list_get_size(list); -//} -// -//#if LV_USE_GROUP -// -//wgl_obj_t wgl_list_get_btn_selected(const wgl_obj_t list) -//{ -// return wasm_list_get_btn_selected(list); -//} -//#endif -// -// -// -//const wgl_style_t * wgl_list_get_style(const wgl_obj_t list, wgl_list_style_t type) -//{ -// //TODO -// return NULL; -//} -// -// -//void wgl_list_up(const wgl_obj_t list) -//{ -// wasm_list_up(list); -//} -// -//void wgl_list_down(const wgl_obj_t list) -//{ -// wasm_list_down(list); -//} -// -// -//void wgl_list_focus(const wgl_obj_t btn, wgl_anim_enable_t anim) -//{ -// wasm_list_focus(btn, anim); -//} -// - - diff --git a/core/app-framework/wgl/app/src/wgl_obj.c b/core/app-framework/wgl/app/src/wgl_obj.c deleted file mode 100644 index 6d94079088..0000000000 --- a/core/app-framework/wgl/app/src/wgl_obj.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wa-inc/wgl.h" -#include "gui_api.h" -#include -#include - -#define ARGC sizeof(argv)/sizeof(uint32) -#define CALL_OBJ_NATIVE_FUNC(id) wasm_obj_native_call(id, argv, ARGC) - -typedef struct _obj_evt_cb { - struct _obj_evt_cb *next; - - wgl_obj_t obj; - - wgl_event_cb_t event_cb; -} obj_evt_cb_t; - -static obj_evt_cb_t *g_obj_evt_cb_list = NULL; - -/* For lvgl compatible */ -char g_widget_text[100]; - -wgl_res_t wgl_obj_del(wgl_obj_t obj) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)obj; - CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_DEL); - return (wgl_res_t)argv[0]; -} - -void wgl_obj_del_async(wgl_obj_t obj) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)obj; - CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_DEL_ASYNC); -} - -void wgl_obj_clean(wgl_obj_t obj) -{ - uint32 argv[1] = {0}; - argv[0] = (uint32)obj; - CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_CLEAN); -} - -void wgl_obj_align(wgl_obj_t obj, const wgl_obj_t base, wgl_align_t align, wgl_coord_t x_mod, wgl_coord_t y_mod) -{ - uint32 argv[5] = {0}; - argv[0] = (uint32)obj; - argv[1] = (uint32)base; - argv[2] = align; - argv[3] = x_mod; - argv[4] = y_mod; - CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_ALIGN); -} - -wgl_event_cb_t wgl_obj_get_event_cb(const wgl_obj_t obj) -{ - obj_evt_cb_t *obj_evt_cb = g_obj_evt_cb_list; - while (obj_evt_cb != NULL) { - if (obj_evt_cb->obj == obj) { - return obj_evt_cb->event_cb; - } - obj_evt_cb = obj_evt_cb->next; - } - - return NULL; -} - -void wgl_obj_set_event_cb(wgl_obj_t obj, wgl_event_cb_t event_cb) -{ - obj_evt_cb_t *obj_evt_cb; - uint32 argv[1] = {0}; - - obj_evt_cb = g_obj_evt_cb_list; - while (obj_evt_cb) { - if (obj_evt_cb->obj == obj) { - obj_evt_cb->event_cb = event_cb; - return; - } - } - - obj_evt_cb = (obj_evt_cb_t *)malloc(sizeof(*obj_evt_cb)); - if (obj_evt_cb == NULL) - return; - - memset(obj_evt_cb, 0, sizeof(*obj_evt_cb)); - obj_evt_cb->obj = obj; - obj_evt_cb->event_cb = event_cb; - - if (g_obj_evt_cb_list != NULL) { - obj_evt_cb->next = g_obj_evt_cb_list; - g_obj_evt_cb_list = obj_evt_cb; - } else { - g_obj_evt_cb_list = obj_evt_cb; - } - - argv[0] = (uint32)obj; - CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_SET_EVT_CB); -} - -void on_widget_event(wgl_obj_t obj, wgl_event_t event) -{ - obj_evt_cb_t *obj_evt_cb = g_obj_evt_cb_list; - - while (obj_evt_cb != NULL) { - if (obj_evt_cb->obj == obj) { - obj_evt_cb->event_cb(obj, event); - return; - } - obj_evt_cb = obj_evt_cb->next; - } -} diff --git a/core/app-framework/wgl/app/wa-inc/inc/LICENCE.txt b/core/app-framework/wgl/app/wa-inc/inc/LICENCE.txt deleted file mode 100644 index beaef1d26e..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/LICENCE.txt +++ /dev/null @@ -1,8 +0,0 @@ -MIT licence -Copyright (c) 2016 Gábor Kiss-Vámosi - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/app-framework/wgl/app/wa-inc/inc/wgl_btn.h b/core/app-framework/wgl/app/wa-inc/inc/wgl_btn.h deleted file mode 100644 index bbf3644183..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/wgl_btn.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_BTN_H -#define WAMR_GRAPHIC_LIBRARY_BTN_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** Possible states of a button. - * It can be used not only by buttons but other button-like objects too*/ -enum { - /**Released*/ - WGL_BTN_STATE_REL, - - /**Pressed*/ - WGL_BTN_STATE_PR, - - /**Toggled released*/ - WGL_BTN_STATE_TGL_REL, - - /**Toggled pressed*/ - WGL_BTN_STATE_TGL_PR, - - /**Inactive*/ - WGL_BTN_STATE_INA, - - /**Number of states*/ - _WGL_BTN_STATE_NUM, -}; -typedef uint8_t wgl_btn_state_t; - -/**Styles*/ -enum { - /** Release style */ - WGL_BTN_STYLE_REL, - - /**Pressed style*/ - WGL_BTN_STYLE_PR, - - /** Toggle released style*/ - WGL_BTN_STYLE_TGL_REL, - - /** Toggle pressed style */ - WGL_BTN_STYLE_TGL_PR, - - /** Inactive style*/ - WGL_BTN_STYLE_INA, -}; -typedef uint8_t wgl_btn_style_t; - - -/* Create a button */ -wgl_obj_t wgl_btn_create(wgl_obj_t par, wgl_obj_t copy); - -/*===================== - * Setter functions - *====================*/ - -/** - * Enable the toggled states. On release the button will change from/to toggled state. - * @param btn pointer to a button object - * @param tgl true: enable toggled states, false: disable - */ -void wgl_btn_set_toggle(wgl_obj_t btn, bool tgl); - -/** - * Set the state of the button - * @param btn pointer to a button object - * @param state the new state of the button (from wgl_btn_state_t enum) - */ -void wgl_btn_set_state(wgl_obj_t btn, wgl_btn_state_t state); - -/** - * Toggle the state of the button (ON->OFF, OFF->ON) - * @param btn pointer to a button object - */ -void wgl_btn_toggle(wgl_obj_t btn); - -/** - * Set time of the ink effect (draw a circle on click to animate in the new state) - * @param btn pointer to a button object - * @param time the time of the ink animation - */ -void wgl_btn_set_ink_in_time(wgl_obj_t btn, uint16_t time); - -/** - * Set the wait time before the ink disappears - * @param btn pointer to a button object - * @param time the time of the ink animation - */ -void wgl_btn_set_ink_wait_time(wgl_obj_t btn, uint16_t time); - -/** - * Set time of the ink out effect (animate to the released state) - * @param btn pointer to a button object - * @param time the time of the ink animation - */ -void wgl_btn_set_ink_out_time(wgl_obj_t btn, uint16_t time); - -/** - * Set a style of a button. - * @param btn pointer to button object - * @param type which style should be set - * @param style pointer to a style - * */ -//void wgl_btn_set_style(wgl_obj_t btn, wgl_btn_style_t type, const wgl_style_t * style); - -/*===================== - * Getter functions - *====================*/ - -/** - * Get the current state of the button - * @param btn pointer to a button object - * @return the state of the button (from wgl_btn_state_t enum) - */ -wgl_btn_state_t wgl_btn_get_state(wgl_obj_t btn); - -/** - * Get the toggle enable attribute of the button - * @param btn pointer to a button object - * @return true: toggle enabled, false: disabled - */ -bool wgl_btn_get_toggle(wgl_obj_t btn); - -/** - * Get time of the ink in effect (draw a circle on click to animate in the new state) - * @param btn pointer to a button object - * @return the time of the ink animation - */ -uint16_t wgl_btn_get_ink_in_time(wgl_obj_t btn); - -/** - * Get the wait time before the ink disappears - * @param btn pointer to a button object - * @return the time of the ink animation - */ -uint16_t wgl_btn_get_ink_wait_time(wgl_obj_t btn); - -/** - * Get time of the ink out effect (animate to the releases state) - * @param btn pointer to a button object - * @return the time of the ink animation - */ -uint16_t wgl_btn_get_ink_out_time(wgl_obj_t btn); - -/** - * Get style of a button. - * @param btn pointer to button object - * @param type which style should be get - * @return style pointer to the style - * */ -//const wgl_style_t * wgl_btn_get_style(const wgl_obj_t btn, wgl_btn_style_t type); -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_BTN_H */ diff --git a/core/app-framework/wgl/app/wa-inc/inc/wgl_cb.h b/core/app-framework/wgl/app/wa-inc/inc/wgl_cb.h deleted file mode 100644 index 5ff271ee23..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/wgl_cb.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_CB_H -#define WAMR_GRAPHIC_LIBRARY_CB_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** Checkbox styles. */ -enum { - WGL_CB_STYLE_BG, /**< Style of object background. */ - WGL_CB_STYLE_BOX_REL, /**< Style of box (released). */ - WGL_CB_STYLE_BOX_PR, /**< Style of box (pressed). */ - WGL_CB_STYLE_BOX_TGL_REL, /**< Style of box (released but checked). */ - WGL_CB_STYLE_BOX_TGL_PR, /**< Style of box (pressed and checked). */ - WGL_CB_STYLE_BOX_INA, /**< Style of disabled box */ -}; -typedef uint8_t wgl_cb_style_t; - - -/** - * Create a check box objects - * @param par pointer to an object, it will be the parent of the new check box - * @param copy pointer to a check box object, if not NULL then the new object will be copied from it - * @return pointer to the created check box - */ -wgl_obj_t wgl_cb_create(wgl_obj_t par, const wgl_obj_t copy); - -/*===================== - * Setter functions - *====================*/ - -/** - * Set the text of a check box. `txt` will be copied and may be deallocated - * after this function returns. - * @param cb pointer to a check box - * @param txt the text of the check box. NULL to refresh with the current text. - */ -void wgl_cb_set_text(wgl_obj_t cb, const char * txt); - -/** - * Set the text of a check box. `txt` must not be deallocated during the life - * of this checkbox. - * @param cb pointer to a check box - * @param txt the text of the check box. NULL to refresh with the current text. - */ -void wgl_cb_set_static_text(wgl_obj_t cb, const char * txt); - - -/*===================== - * Getter functions - *====================*/ - - -/** - * Get the length of the text of a check box - * @param label the check box object - * @return the length of the text of the check box - */ -unsigned int wgl_cb_get_text_length(wgl_obj_t cb); - -/** - * Get the text of a check box - * @param label the check box object - * @param buffer buffer to save the text - * @param buffer_len length of the buffer - * @return the text of the check box, note that the text will be truncated if buffer is not long enough - */ -char *wgl_cb_get_text(wgl_obj_t cb, char *buffer, int buffer_len); - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_CB_H */ diff --git a/core/app-framework/wgl/app/wa-inc/inc/wgl_label.h b/core/app-framework/wgl/app/wa-inc/inc/wgl_label.h deleted file mode 100644 index 3c7f07a321..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/wgl_label.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_LABEL_H -#define WAMR_GRAPHIC_LIBRARY_LABEL_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** Long mode behaviors. Used in 'wgl_label_ext_t' */ -enum { - WGL_LABEL_LONG_EXPAND, /**< Expand the object size to the text size*/ - WGL_LABEL_LONG_BREAK, /**< Keep the object width, break the too long lines and expand the object - height*/ - WGL_LABEL_LONG_DOT, /**< Keep the size and write dots at the end if the text is too long*/ - WGL_LABEL_LONG_SROLL, /**< Keep the size and roll the text back and forth*/ - WGL_LABEL_LONG_SROLL_CIRC, /**< Keep the size and roll the text circularly*/ - WGL_LABEL_LONG_CROP, /**< Keep the size and crop the text out of it*/ -}; -typedef uint8_t wgl_label_long_mode_t; - -/** Label align policy*/ -enum { - WGL_LABEL_ALIGN_LEFT, /**< Align text to left */ - WGL_LABEL_ALIGN_CENTER, /**< Align text to center */ - WGL_LABEL_ALIGN_RIGHT, /**< Align text to right */ -}; -typedef uint8_t wgl_label_align_t; - -/** Label styles*/ -enum { - WGL_LABEL_STYLE_MAIN, -}; -typedef uint8_t wgl_label_style_t; - -/* Create a label */ -wgl_obj_t wgl_label_create(wgl_obj_t par, wgl_obj_t copy); - -/* Set text for the label */ -void wgl_label_set_text(wgl_obj_t label, const char * text); - -/** - * Get the length of the text of a label - * @param label the label object - * @return the length of the text of the label - */ -unsigned int wgl_label_get_text_length(wgl_obj_t label); - -/** - * Get the text of a label - * @param label the label object - * @param buffer buffer to save the text - * @param buffer_len length of the buffer - * @return the text of the label, note that the text will be truncated if buffer is not long enough - */ -char *wgl_label_get_text(wgl_obj_t label, char *buffer, int buffer_len); - - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_LABEL_H */ diff --git a/core/app-framework/wgl/app/wa-inc/inc/wgl_list.h b/core/app-framework/wgl/app/wa-inc/inc/wgl_list.h deleted file mode 100644 index fd83276ada..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/wgl_list.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_LIST_H -#define WAMR_GRAPHIC_LIBRARY_LIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** List styles. */ -enum { - WGL_LIST_STYLE_BG, /**< List background style */ - WGL_LIST_STYLE_SCRL, /**< List scrollable area style. */ - WGL_LIST_STYLE_SB, /**< List scrollbar style. */ - WGL_LIST_STYLE_EDGE_FLASH, /**< List edge flash style. */ - WGL_LIST_STYLE_BTN_REL, /**< Same meaning as the ordinary button styles. */ - WGL_LIST_STYLE_BTN_PR, - WGL_LIST_STYLE_BTN_TGL_REL, - WGL_LIST_STYLE_BTN_TGL_PR, - WGL_LIST_STYLE_BTN_INA, -}; -typedef uint8_t wgl_list_style_t; - - -/** - * Create a list objects - * @param par pointer to an object, it will be the parent of the new list - * @param copy pointer to a list object, if not NULL then the new object will be copied from it - * @return pointer to the created list - */ -wgl_obj_t wgl_list_create(wgl_obj_t par, wgl_obj_t copy); - - -/*====================== - * Add/remove functions - *=====================*/ - -/** - * Add a list element to the list - * @param list pointer to list object - * @param img_fn file name of an image before the text (NULL if unused) - * @param txt text of the list element (NULL if unused) - * @return pointer to the new list element which can be customized (a button) - */ -wgl_obj_t wgl_list_add_btn(wgl_obj_t list, const void * img_src, const char * txt); - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_LIST_H */ diff --git a/core/app-framework/wgl/app/wa-inc/inc/wgl_obj.h b/core/app-framework/wgl/app/wa-inc/inc/wgl_obj.h deleted file mode 100644 index 80d2cb09b9..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/wgl_obj.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_OBJ_H -#define WAMR_GRAPHIC_LIBRARY_OBJ_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void * wgl_obj_t; - -enum { - WGL_EVENT_PRESSED, /**< The object has been pressed*/ - WGL_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/ - WGL_EVENT_PRESS_LOST, /**< User is still pressing but slid cursor/finger off of the object */ - WGL_EVENT_SHORT_CLICKED, /**< User pressed object for a short period of time, then released it. Not called if dragged. */ - WGL_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `WGL_INDEV_LONG_PRESS_TIME`. Not called if dragged.*/ - WGL_EVENT_LONG_PRESSED_REPEAT, /**< Called after `WGL_INDEV_LONG_PRESS_TIME` in every - `WGL_INDEV_LONG_PRESS_REP_TIME` ms. Not called if dragged.*/ - WGL_EVENT_CLICKED, /**< Called on release if not dragged (regardless to long press)*/ - WGL_EVENT_RELEASED, /**< Called in every cases when the object has been released*/ - WGL_EVENT_DRAG_BEGIN, - WGL_EVENT_DRAG_END, - WGL_EVENT_DRAG_THROW_BEGIN, - WGL_EVENT_KEY, - WGL_EVENT_FOCUSED, - WGL_EVENT_DEFOCUSED, - WGL_EVENT_VALUE_CHANGED, /**< The object's value has changed (i.e. slider moved) */ - WGL_EVENT_INSERT, - WGL_EVENT_REFRESH, - WGL_EVENT_APPLY, /**< "Ok", "Apply" or similar specific button has clicked*/ - WGL_EVENT_CANCEL, /**< "Close", "Cancel" or similar specific button has clicked*/ - WGL_EVENT_DELETE, /**< Object is being deleted */ -}; -typedef uint8_t wgl_event_t; /**< Type of event being sent to the object. */ - - -/** Object alignment. */ -enum { - WGL_ALIGN_CENTER = 0, - WGL_ALIGN_IN_TOP_LEFT, - WGL_ALIGN_IN_TOP_MID, - WGL_ALIGN_IN_TOP_RIGHT, - WGL_ALIGN_IN_BOTTOM_LEFT, - WGL_ALIGN_IN_BOTTOM_MID, - WGL_ALIGN_IN_BOTTOM_RIGHT, - WGL_ALIGN_IN_LEFT_MID, - WGL_ALIGN_IN_RIGHT_MID, - WGL_ALIGN_OUT_TOP_LEFT, - WGL_ALIGN_OUT_TOP_MID, - WGL_ALIGN_OUT_TOP_RIGHT, - WGL_ALIGN_OUT_BOTTOM_LEFT, - WGL_ALIGN_OUT_BOTTOM_MID, - WGL_ALIGN_OUT_BOTTOM_RIGHT, - WGL_ALIGN_OUT_LEFT_TOP, - WGL_ALIGN_OUT_LEFT_MID, - WGL_ALIGN_OUT_LEFT_BOTTOM, - WGL_ALIGN_OUT_RIGHT_TOP, - WGL_ALIGN_OUT_RIGHT_MID, - WGL_ALIGN_OUT_RIGHT_BOTTOM, -}; -typedef uint8_t wgl_align_t; - - -enum { - WGL_DRAG_DIR_HOR = 0x1, /**< Object can be dragged horizontally. */ - WGL_DRAG_DIR_VER = 0x2, /**< Object can be dragged vertically. */ - WGL_DRAG_DIR_ALL = 0x3, /**< Object can be dragged in all directions. */ -}; - -typedef uint8_t wgl_drag_dir_t; - -typedef void (*wgl_event_cb_t)(wgl_obj_t obj, wgl_event_t event); - -void wgl_obj_align(wgl_obj_t obj, wgl_obj_t base, wgl_align_t align, wgl_coord_t x_mod, wgl_coord_t y_mod); -void wgl_obj_set_event_cb(wgl_obj_t obj, wgl_event_cb_t event_cb); -wgl_res_t wgl_obj_del(wgl_obj_t obj); -void wgl_obj_del_async(wgl_obj_t obj); -void wgl_obj_clean(wgl_obj_t obj); - - - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_OBJ_H */ diff --git a/core/app-framework/wgl/app/wa-inc/inc/wgl_types.h b/core/app-framework/wgl/app/wa-inc/inc/wgl_types.h deleted file mode 100644 index 8f69565265..0000000000 --- a/core/app-framework/wgl/app/wa-inc/inc/wgl_types.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_TYPES_H -#define WAMR_GRAPHIC_LIBRARY_TYPES_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** - * WGL error codes. - */ -enum { - WGL_RES_INV = 0, /*Typically indicates that the object is deleted (become invalid) in the action - function or an operation was failed*/ - WGL_RES_OK, /*The object is valid (no deleted) after the action*/ -}; -typedef uint8_t wgl_res_t; - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_TYPES_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/LICENCE.txt b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/LICENCE.txt deleted file mode 100644 index beaef1d26e..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/LICENCE.txt +++ /dev/null @@ -1,8 +0,0 @@ -MIT licence -Copyright (c) 2016 Gábor Kiss-Vámosi - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_btn.h b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_btn.h deleted file mode 100644 index 75337a4e7a..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_btn.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_BTN_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_BTN_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../inc/wgl_btn.h" - -/** Possible states of a button. - * It can be used not only by buttons but other button-like objects too*/ -enum { - /**Released*/ - LV_BTN_STATE_REL, - - /**Pressed*/ - LV_BTN_STATE_PR, - - /**Toggled released*/ - LV_BTN_STATE_TGL_REL, - - /**Toggled pressed*/ - LV_BTN_STATE_TGL_PR, - - /**Inactive*/ - LV_BTN_STATE_INA, - - /**Number of states*/ - _LV_BTN_STATE_NUM, -}; -typedef wgl_btn_state_t lv_btn_state_t; - -/**Styles*/ -enum { - /** Release style */ - LV_BTN_STYLE_REL, - - /**Pressed style*/ - LV_BTN_STYLE_PR, - - /** Toggle released style*/ - LV_BTN_STYLE_TGL_REL, - - /** Toggle pressed style */ - LV_BTN_STYLE_TGL_PR, - - /** Inactive style*/ - LV_BTN_STYLE_INA, -}; -typedef wgl_btn_style_t lv_btn_style_t; - - -#define lv_btn_create wgl_btn_create -#define lv_btn_set_toggle wgl_btn_set_toggle -#define lv_btn_set_state wgl_btn_set_state -#define lv_btn_toggle wgl_btn_toggle -#define lv_btn_set_ink_in_time wgl_btn_set_ink_in_time -#define lv_btn_set_ink_wait_time wgl_btn_set_ink_wait_time -#define lv_btn_set_ink_out_time wgl_btn_set_ink_out_time -#define lv_btn_get_state wgl_btn_get_state -#define lv_btn_get_toggle wgl_btn_get_toggle -#define lv_btn_get_ink_in_time wgl_btn_get_ink_in_time -#define lv_btn_get_ink_wait_time wgl_btn_get_ink_wait_time -#define lv_btn_get_ink_out_time wgl_btn_get_ink_out_time - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_BTN_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_cb.h b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_cb.h deleted file mode 100644 index edaa109bb9..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_cb.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_CB_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_CB_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../inc/wgl_cb.h" - -/** Checkbox styles. */ -enum { - LV_CB_STYLE_BG, /**< Style of object background. */ - LV_CB_STYLE_BOX_REL, /**< Style of box (released). */ - LV_CB_STYLE_BOX_PR, /**< Style of box (pressed). */ - LV_CB_STYLE_BOX_TGL_REL, /**< Style of box (released but checked). */ - LV_CB_STYLE_BOX_TGL_PR, /**< Style of box (pressed and checked). */ - LV_CB_STYLE_BOX_INA, /**< Style of disabled box */ -}; -typedef wgl_cb_style_t lv_cb_style_t; - - -#define lv_cb_create wgl_cb_create -#define lv_cb_set_text wgl_cb_set_text -#define lv_cb_set_static_text wgl_cb_set_static_text -#define lv_cb_get_text(cb) wgl_cb_get_text(cb, g_widget_text, 100) - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_CB_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_label.h b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_label.h deleted file mode 100644 index 264ae295d2..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_label.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_LABEL_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_LABEL_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../inc/wgl_label.h" - -/** Long mode behaviors. Used in 'lv_label_ext_t' */ -enum { - LV_LABEL_LONG_EXPAND, /**< Expand the object size to the text size*/ - LV_LABEL_LONG_BREAK, /**< Keep the object width, break the too long lines and expand the object - height*/ - LV_LABEL_LONG_DOT, /**< Keep the size and write dots at the end if the text is too long*/ - LV_LABEL_LONG_SROLL, /**< Keep the size and roll the text back and forth*/ - LV_LABEL_LONG_SROLL_CIRC, /**< Keep the size and roll the text circularly*/ - LV_LABEL_LONG_CROP, /**< Keep the size and crop the text out of it*/ -}; -typedef wgl_label_long_mode_t lv_label_long_mode_t; - -/** Label align policy*/ -enum { - LV_LABEL_ALIGN_LEFT, /**< Align text to left */ - LV_LABEL_ALIGN_CENTER, /**< Align text to center */ - LV_LABEL_ALIGN_RIGHT, /**< Align text to right */ -}; -typedef wgl_label_align_t lv_label_align_t; - -/** Label styles*/ -enum { - LV_LABEL_STYLE_MAIN, -}; -typedef wgl_label_style_t lv_label_style_t; - - -#define lv_label_create wgl_label_create -#define lv_label_set_text wgl_label_set_text -#define lv_label_get_text(label) wgl_label_get_text(label, g_widget_text, 100) - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_LABEL_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_list.h b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_list.h deleted file mode 100644 index cc9a35a88d..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_list.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_LIST_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_LIST_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../inc/wgl_list.h" - -/** List styles. */ -enum { - LV_LIST_STYLE_BG, /**< List background style */ - LV_LIST_STYLE_SCRL, /**< List scrollable area style. */ - LV_LIST_STYLE_SB, /**< List scrollbar style. */ - LV_LIST_STYLE_EDGE_FLASH, /**< List edge flash style. */ - LV_LIST_STYLE_BTN_REL, /**< Same meaning as the ordinary button styles. */ - LV_LIST_STYLE_BTN_PR, - LV_LIST_STYLE_BTN_TGL_REL, - LV_LIST_STYLE_BTN_TGL_PR, - LV_LIST_STYLE_BTN_INA, -}; -typedef wgl_list_style_t lv_list_style_t; - - -#define lv_list_create wgl_list_create -#define lv_list_add_btn wgl_list_add_btn - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_LIST_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_obj.h b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_obj.h deleted file mode 100644 index b79ffb7304..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_obj.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_OBJ_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_OBJ_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../inc/wgl_obj.h" - -typedef void lv_obj_t; - -enum { - LV_EVENT_PRESSED, - LV_EVENT_PRESSING, - LV_EVENT_PRESS_LOST, - LV_EVENT_SHORT_CLICKED, - LV_EVENT_LONG_PRESSED, - LV_EVENT_LONG_PRESSED_REPEAT, - LV_EVENT_CLICKED, - LV_EVENT_RELEASED, - LV_EVENT_DRAG_BEGIN, - LV_EVENT_DRAG_END, - LV_EVENT_DRAG_THROW_BEGIN, - LV_EVENT_KEY, - LV_EVENT_FOCUSED, - LV_EVENT_DEFOCUSED, - LV_EVENT_VALUE_CHANGED, - LV_EVENT_INSERT, - LV_EVENT_REFRESH, - LV_EVENT_APPLY, - LV_EVENT_CANCEL, - LV_EVENT_DELETE, -}; -typedef wgl_event_t lv_event_t; - - -/** Object alignment. */ -enum { - LV_ALIGN_CENTER, - LV_ALIGN_IN_TOP_LEFT, - LV_ALIGN_IN_TOP_MID, - LV_ALIGN_IN_TOP_RIGHT, - LV_ALIGN_IN_BOTTOM_LEFT, - LV_ALIGN_IN_BOTTOM_MID, - LV_ALIGN_IN_BOTTOM_RIGHT, - LV_ALIGN_IN_LEFT_MID, - LV_ALIGN_IN_RIGHT_MID, - LV_ALIGN_OUT_TOP_LEFT, - LV_ALIGN_OUT_TOP_MID, - LV_ALIGN_OUT_TOP_RIGHT, - LV_ALIGN_OUT_BOTTOM_LEFT, - LV_ALIGN_OUT_BOTTOM_MID, - LV_ALIGN_OUT_BOTTOM_RIGHT, - LV_ALIGN_OUT_LEFT_TOP, - LV_ALIGN_OUT_LEFT_MID, - LV_ALIGN_OUT_LEFT_BOTTOM, - LV_ALIGN_OUT_RIGHT_TOP, - LV_ALIGN_OUT_RIGHT_MID, - LV_ALIGN_OUT_RIGHT_BOTTOM, -}; -typedef wgl_align_t lv_align_t; - - -enum { - LV_DRAG_DIR_HOR, - LV_DRAG_DIR_VER, - LV_DRAG_DIR_ALL, -}; - -typedef wgl_drag_dir_t lv_drag_dir_t; - - -#define lv_obj_align wgl_obj_align -#define lv_obj_set_event_cb wgl_obj_set_event_cb -#define lv_obj_del wgl_obj_del -#define lv_obj_del_async wgl_obj_del_async -#define lv_obj_clean wgl_obj_clean - - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_OBJ_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_types.h b/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_types.h deleted file mode 100644 index c8954b720d..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl-compatible/lv_types.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_TYPES_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_TYPES_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "../inc/wgl_types.h" - -/** - * error codes. - */ -enum { - LV_RES_INV, - LV_RES_OK, -}; -typedef wgl_res_t lv_res_t; - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_TYPES_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/lvgl.h b/core/app-framework/wgl/app/wa-inc/lvgl.h deleted file mode 100644 index 93d6f186d2..0000000000 --- a/core/app-framework/wgl/app/wa-inc/lvgl.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_LVGL_COMPATIBLE_H -#define WAMR_GRAPHIC_LIBRARY_LVGL_COMPATIBLE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bi-inc/wgl_shared_utils.h" /* shared types between app and native */ -#include "lvgl-compatible/lv_types.h" -#include "lvgl-compatible/lv_obj.h" -#include "lvgl-compatible/lv_btn.h" -#include "lvgl-compatible/lv_cb.h" -#include "lvgl-compatible/lv_label.h" -#include "lvgl-compatible/lv_list.h" - - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_LVGL_COMPATIBLE_H */ diff --git a/core/app-framework/wgl/app/wa-inc/wgl.h b/core/app-framework/wgl/app/wa-inc/wgl.h deleted file mode 100644 index 05969b11aa..0000000000 --- a/core/app-framework/wgl/app/wa-inc/wgl.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_H -#define WAMR_GRAPHIC_LIBRARY_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bi-inc/wgl_shared_utils.h" /* shared types between app and native */ - -#include "inc/wgl_types.h" -#include "inc/wgl_obj.h" -#include "inc/wgl_btn.h" -#include "inc/wgl_cb.h" -#include "inc/wgl_label.h" -#include "inc/wgl_list.h" - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_H */ diff --git a/core/app-framework/wgl/app/wasm_app.cmake b/core/app-framework/wgl/app/wasm_app.cmake deleted file mode 100644 index e64566378a..0000000000 --- a/core/app-framework/wgl/app/wasm_app.cmake +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_APP_GUI_DIR ${CMAKE_CURRENT_LIST_DIR}) - -set (DEPS_DIR ${WASM_APP_GUI_DIR}/../../../deps) - -include_directories(${WASM_APP_GUI_DIR} - ${DEPS_DIR} - ${DEPS_DIR}/lvgl - ${DEPS_DIR}/lvgl/src) - -file (GLOB_RECURSE source_all ${WASM_APP_GUI_DIR}/src/*.c) - -set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/core/app-framework/wgl/native/gui_api.h b/core/app-framework/wgl/native/gui_api.h deleted file mode 100644 index 65ff587a2f..0000000000 --- a/core/app-framework/wgl/native/gui_api.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _GUI_API_H_ -#define _GUI_API_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void -wasm_obj_native_call(int32 func_id, int32 argv_offset, uint32 argc); - -void -wasm_btn_native_call(int32 func_id, int32 argv_offset, uint32 argc); - -void -wasm_label_native_call(int32 func_id, int32 argv_offset, uint32 argc); - -void -wasm_cb_native_call(int32 func_id, int32 argv_offset, uint32 argc); - -void -wasm_list_native_call(int32 func_id, int32 argv_offset, uint32 argc); - - -#ifdef __cplusplus -} -#endif - - -#endif /* end of _GUI_API_H_ */ diff --git a/core/app-framework/wgl/native/wamr_gui.inl b/core/app-framework/wgl/native/wamr_gui.inl deleted file mode 100644 index 908a191558..0000000000 --- a/core/app-framework/wgl/native/wamr_gui.inl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/* button */ -EXPORT_WASM_API(wasm_btn_native_call), - -/* obj */ -EXPORT_WASM_API(wasm_obj_native_call), - -/* label */ -EXPORT_WASM_API(wasm_label_native_call), - -/* cont */ -//EXPORT_WASM_API(wasm_cont_native_call), - -/* page */ -//EXPORT_WASM_API(wasm_page_native_call), - -/* list */ -EXPORT_WASM_API(wasm_list_native_call), - -/* drop down list */ -//EXPORT_WASM_API(wasm_ddlist_native_call), - -/* check box */ -EXPORT_WASM_API(wasm_cb_native_call), diff --git a/core/app-framework/wgl/native/wasm_lib.cmake b/core/app-framework/wgl/native/wasm_lib.cmake deleted file mode 100644 index d2ccf3c255..0000000000 --- a/core/app-framework/wgl/native/wasm_lib.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (WASM_LIB_GUI_DIR ${CMAKE_CURRENT_LIST_DIR}) - -set (DEPS_DIR ${WASM_LIB_GUI_DIR}/../../../deps) - -add_definitions(-DLV_CONF_INCLUDE_SIMPLE) - -include_directories(${WASM_LIB_GUI_DIR} - ${DEPS_DIR} - ${DEPS_DIR}/lvgl - ${DEPS_DIR}/lvgl/src) - -file (GLOB_RECURSE lvgl_source ${DEPS_DIR}/lvgl/*.c) -file (GLOB_RECURSE wrapper_source ${WASM_LIB_GUI_DIR}/*.c) - -set (WASM_APP_LIB_CURRENT_SOURCE ${wrapper_source} ${lvgl_source}) diff --git a/core/app-framework/wgl/native/wgl.h b/core/app-framework/wgl/native/wgl.h deleted file mode 100644 index dc01191c29..0000000000 --- a/core/app-framework/wgl/native/wgl.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_H -#define WAMR_GRAPHIC_LIBRARY_H - -#ifdef __cplusplus -extern "C" { -#endif - -void wgl_init(void); -void wgl_exit(void); - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_H */ diff --git a/core/app-framework/wgl/native/wgl_btn_wrapper.c b/core/app-framework/wgl/native/wgl_btn_wrapper.c deleted file mode 100644 index 7a08062e96..0000000000 --- a/core/app-framework/wgl/native/wgl_btn_wrapper.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "native_interface.h" -#include "lvgl.h" -#include "module_wasm_app.h" -#include "wgl_native_utils.h" - -/* ------------------------------------------------------------------------- - * Button widget native function wrappers - * -------------------------------------------------------------------------*/ -static int32 -lv_btn_create_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *par, lv_obj_t *copy) -{ - return wgl_native_wigdet_create(WIDGET_TYPE_BTN, par, copy, module_inst); -} - -static void -lv_btn_set_toggle_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn, bool tgl) -{ - (void)module_inst; - lv_btn_set_toggle(btn, tgl); -} - -static void -lv_btn_set_state_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn, lv_btn_state_t state) -{ - (void)module_inst; - lv_btn_set_state(btn, state); -} - -static void -lv_btn_set_ink_in_time_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn, uint16_t time) -{ - (void)module_inst; - lv_btn_set_ink_in_time(btn, time); -} - -static void -lv_btn_set_ink_out_time_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn, uint16_t time) -{ - (void)module_inst; - lv_btn_set_ink_out_time(btn, time); -} - -static void -lv_btn_set_ink_wait_time_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn, uint16_t time) -{ - (void)module_inst; - lv_btn_set_ink_wait_time(btn, time); -} - -static uint16_t -lv_btn_get_ink_in_time_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn) -{ - (void)module_inst; - return lv_btn_get_ink_in_time(btn); -} - -static uint16_t -lv_btn_get_ink_out_time_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn) -{ - (void)module_inst; - return lv_btn_get_ink_out_time(btn); -} - -static uint16_t -lv_btn_get_ink_wait_time_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn) -{ - (void)module_inst; - return lv_btn_get_ink_wait_time(btn); -} - -static lv_btn_state_t -lv_btn_get_state_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn) -{ - (void)module_inst; - return lv_btn_get_state(btn); -} - -static bool -lv_btn_get_toggle_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn) -{ - (void)module_inst; - return lv_btn_get_toggle(btn); -} - -static void -lv_btn_toggle_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * btn) -{ - (void)module_inst; - lv_btn_toggle(btn); -} - -static WGLNativeFuncDef btn_native_func_defs[] = { - { BTN_FUNC_ID_CREATE, lv_btn_create_wrapper, HAS_RET, 3, {1 | NULL_OK, 2 | NULL_OK, -1}, {-1} }, - { BTN_FUNC_ID_SET_TOGGLE, lv_btn_set_toggle_wrapper, NO_RET, 3, {1, -1}, {-1} }, - { BTN_FUNC_ID_SET_STATE, lv_btn_set_state_wrapper, NO_RET, 3, {1, -1}, {-1} }, -// { BTN_FUNC_ID_SET_STYLE, _btn_set_style, NO_RET, 2, {0, -1}, {-1} }, - { BTN_FUNC_ID_SET_INK_IN_TIME, lv_btn_set_ink_in_time_wrapper, NO_RET, 3, {1, -1}, {-1} }, - { BTN_FUNC_ID_SET_INK_OUT_TIME, lv_btn_set_ink_out_time_wrapper, NO_RET, 3, {1, -1}, {-1} }, - { BTN_FUNC_ID_SET_INK_WAIT_TIME, lv_btn_set_ink_wait_time_wrapper, NO_RET, 3, {1, -1}, {-1} }, - { BTN_FUNC_ID_GET_INK_IN_TIME, lv_btn_get_ink_in_time_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { BTN_FUNC_ID_GET_INK_OUT_TIME, lv_btn_get_ink_out_time_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { BTN_FUNC_ID_GET_INK_WAIT_TIME, lv_btn_get_ink_wait_time_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { BTN_FUNC_ID_GET_STATE, lv_btn_get_state_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { BTN_FUNC_ID_GET_TOGGLE, lv_btn_get_toggle_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { BTN_FUNC_ID_TOGGLE, lv_btn_toggle_wrapper, NO_RET, 2, {1, -1}, {-1} }, - -}; - -/*************** Native Interface to Wasm App ***********/ -void -wasm_btn_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - uint32 size = sizeof(btn_native_func_defs) / sizeof(WGLNativeFuncDef); - - wgl_native_func_call(module_inst, - btn_native_func_defs, - size, - func_id, - argv_offset, - argc); -} diff --git a/core/app-framework/wgl/native/wgl_cb_wrapper.c b/core/app-framework/wgl/native/wgl_cb_wrapper.c deleted file mode 100644 index d0609b402a..0000000000 --- a/core/app-framework/wgl/native/wgl_cb_wrapper.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lvgl.h" -#include "wasm_export.h" -#include "native_interface.h" -#include "module_wasm_app.h" -#include "wgl_native_utils.h" - -/* ------------------------------------------------------------------------- - * Label widget native function wrappers - * -------------------------------------------------------------------------*/ -static int32 -lv_cb_create_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *par, lv_obj_t *copy) -{ - return wgl_native_wigdet_create(WIDGET_TYPE_CB, par, copy, module_inst); -} - -static void -lv_cb_set_text_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * cb, const char * txt) -{ - (void)module_inst; - lv_cb_set_text(cb, txt); -} - -static void -lv_cb_set_static_text_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * cb, const char * txt) -{ - (void)module_inst; - lv_cb_set_static_text(cb, txt); -} - -static int32 -lv_cb_get_text_length_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *cb) -{ - const char *text = lv_cb_get_text(cb); - - if (text == NULL) - return 0; - - return strlen(text); -} - -static char * -lv_cb_get_text_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *cb, char *buffer, int buffer_len) -{ - const char *text = lv_cb_get_text(cb); - - if (text == NULL) - return 0; - - strncpy(buffer, text, buffer_len - 1); - buffer[buffer_len - 1] = '\0'; - - return buffer; -} - -static WGLNativeFuncDef cb_native_func_defs[] = { - { CB_FUNC_ID_CREATE, lv_cb_create_wrapper, HAS_RET, 3, {1 | NULL_OK, 2 | NULL_OK, -1}, {-1} }, - { CB_FUNC_ID_SET_TEXT, lv_cb_set_text_wrapper, NO_RET, 3, {1, -1}, {2, -1} }, - { CB_FUNC_ID_SET_STATIC_TEXT, lv_cb_set_static_text_wrapper, NO_RET, 3, {1, -1}, {2, -1} }, - { CB_FUNC_ID_GET_TEXT_LENGTH, lv_cb_get_text_length_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { CB_FUNC_ID_GET_TEXT, lv_cb_get_text_wrapper, RET_PTR, 4, {1, -1}, {2, -1} }, -}; - -/*************** Native Interface to Wasm App ***********/ -void -wasm_cb_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - uint32 size = sizeof(cb_native_func_defs) / sizeof(WGLNativeFuncDef); - - wgl_native_func_call(module_inst, - cb_native_func_defs, - size, - func_id, - argv_offset, - argc); -} diff --git a/core/app-framework/wgl/native/wgl_cont_wrapper.c b/core/app-framework/wgl/native/wgl_cont_wrapper.c deleted file mode 100644 index 70eeb0f487..0000000000 --- a/core/app-framework/wgl/native/wgl_cont_wrapper.c +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lvgl.h" -#include "module_wasm_app.h" diff --git a/core/app-framework/wgl/native/wgl_label_wrapper.c b/core/app-framework/wgl/native/wgl_label_wrapper.c deleted file mode 100644 index 2aaa38594e..0000000000 --- a/core/app-framework/wgl/native/wgl_label_wrapper.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lvgl.h" -#include "wasm_export.h" -#include "native_interface.h" -#include "module_wasm_app.h" -#include "wgl_native_utils.h" - -/* ------------------------------------------------------------------------- - * Label widget native function wrappers - * -------------------------------------------------------------------------*/ -static int32 -lv_label_create_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *par, lv_obj_t *copy) -{ - return wgl_native_wigdet_create(WIDGET_TYPE_LABEL, par, copy, module_inst); -} - -static void -lv_label_set_text_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * label, const char * text) -{ - (void)module_inst; - lv_label_set_text(label, text); -} - -static int32 -lv_label_get_text_length_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *label) -{ - char *text = lv_label_get_text(label); - - if (text == NULL) - return 0; - - return strlen(text); -} - -static char * -lv_label_get_text_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *label, char *buffer, int buffer_len) -{ - char *text = lv_label_get_text(label); - - if (text == NULL) - return 0; - - strncpy(buffer, text, buffer_len - 1); - buffer[buffer_len - 1] = '\0'; - - return buffer; -} - -static WGLNativeFuncDef label_native_func_defs[] = { - { LABEL_FUNC_ID_CREATE, lv_label_create_wrapper, HAS_RET, 3, {1 | NULL_OK, 2 | NULL_OK, -1}, {-1} }, - { LABEL_FUNC_ID_SET_TEXT, lv_label_set_text_wrapper, NO_RET, 3, {1, -1}, {2, -1} }, - { LABEL_FUNC_ID_GET_TEXT_LENGTH, lv_label_get_text_length_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { LABEL_FUNC_ID_GET_TEXT, lv_label_get_text_wrapper, RET_PTR, 4, {1, -1}, {2, -1} }, -}; - -/*************** Native Interface to Wasm App ***********/ -void -wasm_label_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - uint32 size = sizeof(label_native_func_defs) / sizeof(WGLNativeFuncDef); - - wgl_native_func_call(module_inst, - label_native_func_defs, - size, - func_id, - argv_offset, - argc); -} diff --git a/core/app-framework/wgl/native/wgl_list_wrapper.c b/core/app-framework/wgl/native/wgl_list_wrapper.c deleted file mode 100644 index 2db29481b4..0000000000 --- a/core/app-framework/wgl/native/wgl_list_wrapper.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "native_interface.h" -#include "lvgl.h" -#include "module_wasm_app.h" -#include "wgl_native_utils.h" -#include "bh_assert.h" - -/* ------------------------------------------------------------------------- - * List widget native function wrappers - * -------------------------------------------------------------------------*/ -static int32 -lv_list_create_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *par, lv_obj_t *copy) -{ - return wgl_native_wigdet_create(WIDGET_TYPE_LIST, par, copy, module_inst); -} - -static int32 -lv_list_add_btn_wrapper(wasm_module_inst_t module_inst, - lv_obj_t *list, const char *text) -{ - uint32 btn_obj_id; - lv_obj_t *btn; - uint32 mod_id; - - btn = lv_list_add_btn(list, NULL, text); - - if (btn == NULL) - return 0; - - mod_id = app_manager_get_module_id(Module_WASM_App, module_inst); - bh_assert(mod_id != ID_NONE); - - if (wgl_native_add_object(btn, mod_id, &btn_obj_id)) - return btn_obj_id; /* success return */ - - return 0; -} - -static WGLNativeFuncDef list_native_func_defs[] = { - { LIST_FUNC_ID_CREATE, lv_list_create_wrapper, HAS_RET, 3, {1 | NULL_OK, 2 | NULL_OK, -1}, {-1} }, - { LIST_FUNC_ID_ADD_BTN, lv_list_add_btn_wrapper, HAS_RET, 3, {1, -1}, {2, -1} }, -}; - -/*************** Native Interface to Wasm App ***********/ -void -wasm_list_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - uint32 size = sizeof(list_native_func_defs) / sizeof(WGLNativeFuncDef); - - wgl_native_func_call(module_inst, - list_native_func_defs, - size, - func_id, - argv_offset, - argc); -} diff --git a/core/app-framework/wgl/native/wgl_native_utils.c b/core/app-framework/wgl/native/wgl_native_utils.c deleted file mode 100644 index 5fdaffdd73..0000000000 --- a/core/app-framework/wgl/native/wgl_native_utils.c +++ /dev/null @@ -1,215 +0,0 @@ - - -#include "wgl_native_utils.h" -#include "lvgl.h" -#include "module_wasm_app.h" -#include "wasm_export.h" -#include "bh_assert.h" - -#include - -#define THROW_EXC(msg) wasm_runtime_set_exception(module_inst, msg); - -void -wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); - -uint32 wgl_native_wigdet_create(int8 widget_type, lv_obj_t *par, lv_obj_t *copy, - wasm_module_inst_t module_inst) -{ - uint32 obj_id; - lv_obj_t *wigdet = NULL; - uint32 mod_id; - - //TODO: limit total widget number - - if (par == NULL) - par = lv_disp_get_scr_act(NULL); - - if (widget_type == WIDGET_TYPE_BTN) - wigdet = lv_btn_create(par, copy); - else if (widget_type == WIDGET_TYPE_LABEL) - wigdet = lv_label_create(par, copy); - else if (widget_type == WIDGET_TYPE_CB) - wigdet = lv_cb_create(par, copy); - else if (widget_type == WIDGET_TYPE_LIST) - wigdet = lv_list_create(par, copy); - else if (widget_type == WIDGET_TYPE_DDLIST) - wigdet = lv_ddlist_create(par, copy); - - if (wigdet == NULL) - return 0; - - mod_id = app_manager_get_module_id(Module_WASM_App, module_inst); - bh_assert(mod_id != ID_NONE); - - if (wgl_native_add_object(wigdet, mod_id, &obj_id)) - return obj_id; /* success return */ - - return 0; -} - -static void invokeNative(intptr_t argv[], uint32 argc, void (*native_code)()) -{ - bh_assert(argc >= 1); - - switch(argc) { - case 1: - native_code(argv[0]); - break; - case 2: - native_code(argv[0], argv[1]); - break; - case 3: - native_code(argv[0], argv[1], argv[2]); - break; - case 4: - native_code(argv[0], argv[1], argv[2], argv[3]); - break; - case 5: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4]); - break; - case 6: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); - break; - case 7: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], - argv[6]); - break; - case 8: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], - argv[6], argv[7]); - break; - case 9: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], - argv[6], argv[7], argv[8]); - break; - case 10: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], - argv[6], argv[7], argv[8], argv[9]); - break; - - default: - { - /* FIXME: If this happen, add more cases. */ - wasm_module_inst_t module_inst = (wasm_module_inst_t)argv[0]; - THROW_EXC("the argument number of native function exceeds maximum"); - return; - } - } -} - -typedef void (*GenericFunctionPointer)(); -typedef int32 (*Int32FuncPtr)(intptr_t *, uint32, GenericFunctionPointer); -typedef void (*VoidFuncPtr)(intptr_t *, uint32, GenericFunctionPointer); - -static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)invokeNative; -static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; - -void wgl_native_func_call(wasm_module_inst_t module_inst, - WGLNativeFuncDef *funcs, - uint32 size, - int32 func_id, - uint32 argv_offset, - uint32 argc) -{ - WGLNativeFuncDef *func_def = funcs; - WGLNativeFuncDef *func_def_end = func_def + size; - uint32 *argv; - - if (!validate_app_addr(argv_offset, argc * sizeof(uint32))) - return; - - argv = addr_app_to_native(argv_offset); - - while (func_def < func_def_end) { - if (func_def->func_id == func_id) { - int i, obj_arg_num = 0, ptr_arg_num = 0, argc1 = 0; - intptr_t argv_copy_buf[16]; - intptr_t *argv_copy = argv_copy_buf; - - argc1++; /* module_inst */ - argc1 += func_def->arg_num; - if (argc1 > 16) { - argv_copy = (intptr_t *)bh_malloc(func_def->arg_num * - sizeof(intptr_t)); - if (argv_copy == NULL) - return; - } - - /* Init argv_copy */ - argv_copy[0] = (intptr_t)module_inst; - for (i = 0; i < func_def->arg_num; i++) - argv_copy[i + 1] = (intptr_t)argv[i]; - - /* Validate object arguments */ - i = 0; - for (; i < OBJ_ARG_NUM_MAX && func_def->obj_arg_indexes[i] != 0xff; - i++, obj_arg_num++) { - uint8 index = func_def->obj_arg_indexes[i]; - bool null_ok = index & NULL_OK; - - index = index & (~NULL_OK); - - /* Some API's allow to pass NULL obj, such as xxx_create() */ - if (argv_copy[index] == 0) { - if (!null_ok) { - THROW_EXC("the object id is 0 and invalid"); - goto fail; - } - /* Continue so that to pass null object validation */ - continue; - } - - if (!wgl_native_validate_object(argv_copy[index], - (lv_obj_t **)&argv_copy[index])) { - THROW_EXC("the object is invalid"); - goto fail; - } - } - - /* Validate address arguments */ - i = 0; - for (; i < PTR_ARG_NUM_MAX && func_def->ptr_arg_indexes[i] != 0xff; - i++, ptr_arg_num++) { - uint8 index = func_def->ptr_arg_indexes[i]; - - /* The index+1 arg is the data size to be validated */ - if (!validate_app_addr(argv_copy[index], argv_copy[index + 1])) - goto fail; - - /* Convert to native address before call lvgl function */ - argv_copy[index] = (intptr_t)addr_app_to_native(argv_copy[index]); - } - - if (func_def->has_ret == NO_RET) - invokeNative_Void(argv_copy, - argc1, - func_def->func_ptr); - else { - argv[0] = invokeNative_Int32(argv_copy, - argc1, - func_def->func_ptr); - /* Convert to app memory offset if return value is a - * native address pointer */ - if (func_def->has_ret == RET_PTR) - argv[0] = addr_native_to_app((char *)(intptr_t)argv[0]); - } - - if (argv_copy != argv_copy_buf) - bh_free(argv_copy); - - /* success return */ - return; - - fail: - if (argv_copy != argv_copy_buf) - bh_free(argv_copy); - return; - } - - func_def++; - } - - THROW_EXC("the native widget function is not found!"); -} - diff --git a/core/app-framework/wgl/native/wgl_native_utils.h b/core/app-framework/wgl/native/wgl_native_utils.h deleted file mode 100644 index cc8b2d215b..0000000000 --- a/core/app-framework/wgl/native/wgl_native_utils.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WAMR_GRAPHIC_LIBRARY_NATIVE_UTILS_H -#define WAMR_GRAPHIC_LIBRARY_NATIVE_UTILS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bh_platform.h" -#include "lvgl.h" -#include "wasm_export.h" -#include "bi-inc/wgl_shared_utils.h" - -#define OBJ_ARG_NUM_MAX 4 -#define PTR_ARG_NUM_MAX 4 - -#define NULL_OK 0x80 - -enum { - /* The function has a normal return value (not a pointer) */ - HAS_RET, - /* The function doesn't have return value */ - NO_RET, - /* The function's return value is a native address pointer */ - RET_PTR -}; - -enum { - WIDGET_TYPE_BTN, - WIDGET_TYPE_LABEL, - WIDGET_TYPE_CB, - WIDGET_TYPE_LIST, - WIDGET_TYPE_DDLIST, - - _WIDGET_TYPE_NUM, -}; - -typedef struct WGLNativeFuncDef { - /* Function id */ - int32 func_id; - - /* Native function pointer */ - void *func_ptr; - - /* whether has return value */ - uint8 has_ret; - - /* argument number */ - uint8 arg_num; - - /* low 7 bit: obj argument index - * highest 1 bit: allow obj be null or not - * -1 means the end of this array */ - uint8 obj_arg_indexes[OBJ_ARG_NUM_MAX]; - - /* pointer argument indexes, -1 means the end of this array */ - uint8 ptr_arg_indexes[PTR_ARG_NUM_MAX]; -} WGLNativeFuncDef; - -bool wgl_native_validate_object(int32 obj_id, lv_obj_t **obj); - -bool wgl_native_add_object(lv_obj_t *obj, uint32 module_id, uint32 *obj_id); - -uint32 wgl_native_wigdet_create(int8 widget_type, - lv_obj_t *par, - lv_obj_t *copy, - wasm_module_inst_t module_inst); - -void wgl_native_func_call(wasm_module_inst_t module_inst, - WGLNativeFuncDef *funcs, - uint32 size, - int32 func_id, - uint32 argv_offset, - uint32 argc); - -#ifdef __cplusplus -} -#endif - -#endif /* WAMR_GRAPHIC_LIBRARY_NATIVE_UTILS_H */ diff --git a/core/app-framework/wgl/native/wgl_obj_wrapper.c b/core/app-framework/wgl/native/wgl_obj_wrapper.c deleted file mode 100644 index 6cd7122e4c..0000000000 --- a/core/app-framework/wgl/native/wgl_obj_wrapper.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lvgl.h" -#include "app_manager_export.h" -#include "module_wasm_app.h" -#include "bh_list.h" -#include "bh_thread.h" -#include "wgl_native_utils.h" -#include "wgl.h" - - -typedef struct { - bh_list_link l; - - /* The object id. */ - uint32 obj_id; - - /* The lv object */ - lv_obj_t *obj; - - /* Module id that the obj belongs to */ - uint32 module_id; -} object_node_t; - -typedef struct { - int32 obj_id; - lv_event_t event; -} object_event_t; - -/* Max obj id */ -static uint32 g_obj_id_max = 0; - -static bh_list g_object_list; - -static korp_mutex g_object_list_mutex; - -static bool lv_task_handler_thread_run = true; - -static void app_mgr_object_event_callback(module_data *m_data, bh_message_t msg) -{ - uint32 argv[2]; - wasm_function_inst_t func_on_object_event; - bh_assert(WIDGET_EVENT_WASM == bh_message_type(msg)); - wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; - wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; - object_event_t *object_event - = (object_event_t *)bh_message_payload(msg); - - if (object_event == NULL) - return; - - func_on_object_event = wasm_runtime_lookup_function(inst, "_on_widget_event", - "(i32i32)"); - if (!func_on_object_event) - func_on_object_event = wasm_runtime_lookup_function(inst, "on_widget_event", - "(i32i32)"); - if (!func_on_object_event) { - printf("Cannot find function on_widget_event\n"); - return; - } - - argv[0] = object_event->obj_id; - argv[1] = object_event->event; - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_on_object_event, - 2, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - printf(":Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - return; - } -} - -static void cleanup_object_list(uint32 module_id) -{ - object_node_t *elem; - - vm_mutex_lock(&g_object_list_mutex); - - while (true) { - bool found = false; - elem = (object_node_t *)bh_list_first_elem(&g_object_list); - while (elem) { - /* delete the leaf node belongs to the module firstly */ - if (module_id == elem->module_id && - lv_obj_count_children(elem->obj) == 0) { - object_node_t *next = (object_node_t *)bh_list_elem_next(elem); - - found = true; - lv_obj_del(elem->obj); - bh_list_remove(&g_object_list, elem); - bh_free(elem); - elem = next; - } else { - elem = (object_node_t *)bh_list_elem_next(elem); - } - } - - if (!found) - break; - } - - vm_mutex_unlock(&g_object_list_mutex); -} - -static bool init_object_event_callback_framework() -{ - if (!wasm_register_cleanup_callback(cleanup_object_list)) { - goto fail; - } - - if (!wasm_register_msg_callback(WIDGET_EVENT_WASM, - app_mgr_object_event_callback)) { - goto fail; - } - - return true; - -fail: - return false; -} - -bool wgl_native_validate_object(int32 obj_id, lv_obj_t **obj) -{ - object_node_t *elem; - - vm_mutex_lock(&g_object_list_mutex); - - elem = (object_node_t *)bh_list_first_elem(&g_object_list); - while (elem) { - if (obj_id == elem->obj_id) { - if (obj != NULL) - *obj = elem->obj; - vm_mutex_unlock(&g_object_list_mutex); - return true; - } - elem = (object_node_t *) bh_list_elem_next(elem); - } - - vm_mutex_unlock(&g_object_list_mutex); - - return false; -} - -bool wgl_native_add_object(lv_obj_t *obj, uint32 module_id, uint32 *obj_id) -{ - object_node_t *node; - - node = (object_node_t *) bh_malloc(sizeof(object_node_t)); - - if (node == NULL) - return false; - - /* Generate an obj id */ - g_obj_id_max++; - if (g_obj_id_max == -1) - g_obj_id_max = 1; - - memset(node, 0, sizeof(*node)); - node->obj = obj; - node->obj_id = g_obj_id_max; - node->module_id = module_id; - - vm_mutex_lock(&g_object_list_mutex); - bh_list_insert(&g_object_list, node); - vm_mutex_unlock(&g_object_list_mutex); - - if (obj_id != NULL) - *obj_id = node->obj_id; - - return true; -} - -static void _obj_del_recursive(lv_obj_t *obj) -{ - object_node_t *elem; - lv_obj_t * i; - lv_obj_t * i_next; - - i = lv_ll_get_head(&(obj->child_ll)); - - while (i != NULL) { - /*Get the next object before delete this*/ - i_next = lv_ll_get_next(&(obj->child_ll), i); - - /*Call the recursive del to the child too*/ - _obj_del_recursive(i); - - /*Set i to the next node*/ - i = i_next; - } - - vm_mutex_lock(&g_object_list_mutex); - - elem = (object_node_t *)bh_list_first_elem(&g_object_list); - while (elem) { - if (obj == elem->obj) { - bh_list_remove(&g_object_list, elem); - bh_free(elem); - vm_mutex_unlock(&g_object_list_mutex); - return; - } - elem = (object_node_t *) bh_list_elem_next(elem); - } - - vm_mutex_unlock(&g_object_list_mutex); -} - -static void _obj_clean_recursive(lv_obj_t *obj) -{ - lv_obj_t * i; - lv_obj_t * i_next; - - i = lv_ll_get_head(&(obj->child_ll)); - - while (i != NULL) { - /*Get the next object before delete this*/ - i_next = lv_ll_get_next(&(obj->child_ll), i); - - /*Call the recursive del to the child too*/ - _obj_del_recursive(i); - - /*Set i to the next node*/ - i = i_next; - } -} - -static void post_widget_msg_to_module(object_node_t *object_node, lv_event_t event) -{ - module_data *module = module_data_list_lookup_id(object_node->module_id); - object_event_t *object_event; - - if (module == NULL) - return; - - object_event = (object_event_t *)bh_malloc(sizeof(*object_event)); - if (object_event == NULL) - return; - - memset(object_event, 0, sizeof(*object_event)); - object_event->obj_id = object_node->obj_id; - object_event->event = event; - - bh_post_msg(module->queue, - WIDGET_EVENT_WASM, - object_event, - sizeof(*object_event)); -} - -static void internal_lv_obj_event_cb(lv_obj_t *obj, lv_event_t event) -{ - object_node_t *elem; - - vm_mutex_lock(&g_object_list_mutex); - - elem = (object_node_t *)bh_list_first_elem(&g_object_list); - while (elem) { - if (obj == elem->obj) { - post_widget_msg_to_module(elem, event); - vm_mutex_unlock(&g_object_list_mutex); - return; - } - elem = (object_node_t *) bh_list_elem_next(elem); - } - - vm_mutex_unlock(&g_object_list_mutex); -} - -static void* lv_task_handler_thread_routine (void *arg) -{ - korp_sem sem; - - vm_sem_init(&sem, 0); - - while (lv_task_handler_thread_run) { - vm_sem_reltimedwait(&sem, 100); - lv_task_handler(); - } - - vm_sem_destroy(&sem); - - return NULL; -} - -void wgl_init(void) -{ - korp_thread tid; - - lv_init(); - - bh_list_init(&g_object_list); - vm_recursive_mutex_init(&g_object_list_mutex); - init_object_event_callback_framework(); - - /* new a thread, call lv_task_handler periodically */ - vm_thread_create(&tid, - lv_task_handler_thread_routine, - NULL, - BH_APPLET_PRESERVED_STACK_SIZE); -} - -void wgl_exit(void) -{ - lv_task_handler_thread_run = false; -} - -/* ------------------------------------------------------------------------- - * Obj native function wrappers - * -------------------------------------------------------------------------*/ -static lv_res_t -lv_obj_del_wrapper(wasm_module_inst_t module_inst, lv_obj_t *obj) -{ - (void)module_inst; - - /* Recursively delete object node in the list belong to this - * parent object including itself */ - _obj_del_recursive(obj); - - return lv_obj_del(obj); -} - -static void -lv_obj_del_async_wrapper(wasm_module_inst_t module_inst, lv_obj_t * obj) -{ - (void)module_inst; - lv_obj_del_async(obj); -} - -static void -lv_obj_clean_wrapper(wasm_module_inst_t module_inst, lv_obj_t *obj) -{ - (void)module_inst; - - /* Recursively delete child object node in the list belong to this - * parent object */ - _obj_clean_recursive(obj); - - /* Delete all of its children */ - lv_obj_clean(obj); -} - -static void -lv_obj_align_wrapper(wasm_module_inst_t module_inst, - lv_obj_t * obj, - const lv_obj_t * base, - lv_align_t align, - lv_coord_t x_mod, - lv_coord_t y_mod) -{ - (void)module_inst; - lv_obj_align(obj, base, align, x_mod, y_mod); -} - -static void -lv_obj_set_event_cb_wrapper(wasm_module_inst_t module_inst, lv_obj_t *obj) -{ - (void)module_inst; - lv_obj_set_event_cb(obj, internal_lv_obj_event_cb); -} -/* ------------------------------------------------------------------------- */ - - -static WGLNativeFuncDef obj_native_func_defs[] = { - { OBJ_FUNC_ID_DEL, lv_obj_del_wrapper, HAS_RET, 2, {1, -1}, {-1} }, - { OBJ_FUNC_ID_DEL_ASYNC, lv_obj_del_async_wrapper, NO_RET, 2, {1, -1}, {-1} }, - { OBJ_FUNC_ID_CLEAN, lv_obj_clean_wrapper, NO_RET, 2, {1, -1}, {-1} }, - { OBJ_FUNC_ID_ALIGN, lv_obj_align_wrapper, NO_RET, 6, {1, 2 | NULL_OK, -1}, {-1} }, - { OBJ_FUNC_ID_SET_EVT_CB, lv_obj_set_event_cb_wrapper, NO_RET, 2, {1, -1}, {-1} }, -}; - -/*************** Native Interface to Wasm App ***********/ -void -wasm_obj_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - uint32 size = sizeof(obj_native_func_defs) / sizeof(WGLNativeFuncDef); - - wgl_native_func_call(module_inst, - obj_native_func_defs, - size, - func_id, - argv_offset, - argc); -} diff --git a/core/app-mgr/app-manager/app_manager.c b/core/app-mgr/app-manager/app_manager.c deleted file mode 100644 index 7c8c8577a8..0000000000 --- a/core/app-mgr/app-manager/app_manager.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "app_manager.h" -#include "app_manager_host.h" -#include "bh_queue.h" -#include "bh_memory.h" -#include "bh_thread.h" -#include "bi-inc/attr_container.h" -#include "event.h" -#include "watchdog.h" -#include "coap_ext.h" - -/* Queue of app manager */ -static bh_queue *g_app_mgr_queue; - -void* -get_app_manager_queue() -{ - return g_app_mgr_queue; -} - -void app_manager_post_applets_update_event() -{ - module_data *m_data; - attr_container_t *attr_cont; - request_t msg; - int num = 0, i = 0; - char *url = "/applets"; - - if (!event_is_registered(url)) - return; - - if (!(attr_cont = attr_container_create("All Applets"))) { - app_manager_printf( - "Post applets update event failed: allocate memory failed."); - return; - } - - vm_mutex_lock(&module_data_list_lock); - - m_data = module_data_list; - while (m_data) { - num++; - m_data = m_data->next; - } - - if (!(attr_container_set_int(&attr_cont, "num", num))) { - app_manager_printf( - "Post applets update event failed: set attr container key failed."); - goto fail; - } - - m_data = module_data_list; - while (m_data) { - char buf[32]; - i++; - snprintf(buf, sizeof(buf), "%s%d", "applet", i); - if (!(attr_container_set_string(&attr_cont, buf, m_data->module_name))) { - app_manager_printf( - "Post applets update event failed: set attr applet name key failed."); - goto fail; - } - snprintf(buf, sizeof(buf), "%s%d", "heap", i); - if (!(attr_container_set_int(&attr_cont, buf, m_data->heap_size))) { - app_manager_printf( - "Post applets update event failed: set attr heap key failed."); - goto fail; - } - m_data = m_data->next; - } - - memset(&msg, 0, sizeof(msg)); - msg.url = url; - msg.action = COAP_EVENT; - msg.payload = (char*) attr_cont; - send_request_to_host(&msg); - - app_manager_printf("Post applets update event success!\n"); - attr_container_dump(attr_cont); - - fail: vm_mutex_unlock(&module_data_list_lock); - attr_container_destroy(attr_cont); -} - -static int get_applets_count() -{ - module_data *m_data; - int num = 0; - - vm_mutex_lock(&module_data_list_lock); - - m_data = module_data_list; - while (m_data) { - num++; - m_data = m_data->next; - } - - vm_mutex_unlock(&module_data_list_lock); - - return num; -} - -/* Query fw apps info if name = NULL, otherwise query specify app */ -static bool app_manager_query_applets(request_t *msg, const char *name) -{ - module_data *m_data; - attr_container_t *attr_cont; - int num = 0, i = 0, len; - bool ret = false, found = false; - response_t response[1] = { 0 }; - - attr_cont = attr_container_create("Applets Info"); - if (!attr_cont) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applets failed: allocate memory failed."); - return false; - } - - vm_mutex_lock(&module_data_list_lock); - - m_data = module_data_list; - while (m_data) { - num++; - m_data = m_data->next; - } - - if (name == NULL && !(attr_container_set_int(&attr_cont, "num", num))) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applets failed: set attr container key failed."); - goto fail; - } - - m_data = module_data_list; - while (m_data) { - char buf[32]; - - if (name == NULL) { - i++; - snprintf(buf, sizeof(buf), "%s%d", "applet", i); - if (!(attr_container_set_string(&attr_cont, buf, - m_data->module_name))) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applets failed: set attr container key failed."); - goto fail; - } - snprintf(buf, sizeof(buf), "%s%d", "heap", i); - if (!(attr_container_set_int(&attr_cont, buf, m_data->heap_size))) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applets failed: set attr container heap key failed."); - goto fail; - } - } else if (!strcmp(name, m_data->module_name)) { - found = true; - if (!(attr_container_set_string(&attr_cont, "name", - m_data->module_name))) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applet failed: set attr container key failed."); - goto fail; - } - if (!(attr_container_set_int(&attr_cont, "heap", m_data->heap_size))) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applet failed: set attr container heap key failed."); - goto fail; - } - } - - m_data = m_data->next; - } - - if (name != NULL && !found) { - SEND_ERR_RESPONSE(msg->mid, - "Query Applet failed: the app is not found."); - goto fail; - } - - len = attr_container_get_serialize_length(attr_cont); - - make_response_for_request(msg, response); - set_response(response, CONTENT_2_05, - FMT_ATTR_CONTAINER, (char*) attr_cont, len); - send_response_to_host(response); - - ret = true; - app_manager_printf("Query Applets success!\n"); - attr_container_dump(attr_cont); - - fail: vm_mutex_unlock(&module_data_list_lock); - attr_container_destroy(attr_cont); - return ret; -} - -void applet_mgt_reqeust_handler(request_t *request, void *unused) -{ - bh_message_t msg; - /* deep copy, but not use app self heap, but use global heap */ - request_t *req = clone_request(request); - - if (!req) - return; - - msg = bh_new_msg(RESTFUL_REQUEST, req, sizeof(*req), request_cleaner); - if (!msg) { - request_cleaner(req); - return; - } - - bh_post_msg2(get_app_manager_queue(), msg); -} - -/* return -1 for error */ -static int get_module_type(char *kv_str) -{ - int module_type = -1; - char type_str[16] = { 0 }; - - find_key_value(kv_str, strlen(kv_str), "type", type_str, - sizeof(type_str) - 1, '&'); - - if (strlen(type_str) == 0) - module_type = Module_WASM_App; - else if (strcmp(type_str, "jeff") == 0) - module_type = Module_Jeff; - else if (strcmp(type_str, "wasm") == 0) - module_type = Module_WASM_App; - else if (strcmp(type_str, "wasmlib") == 0) - module_type = Module_WASM_Lib; - - return module_type; -} - -#define APP_NAME_MAX_LEN 128 - -/* Queue callback of App Manager */ - -static void app_manager_queue_callback(void *message, void *arg) -{ - request_t *request = (request_t *) bh_message_payload((bh_message_t)message); - int mid = request->mid, module_type, offset; - - (void)arg; - - if ((offset = check_url_start(request->url, strlen(request->url), "/applet")) - > 0) { - module_type = get_module_type(request->url + offset); - - if (module_type == -1) { - SEND_ERR_RESPONSE(mid, - "Applet Management failed: invalid module type."); - goto fail; - } - - /* Install Applet */ - if (request->action == COAP_PUT) { - if (get_applets_count() >= MAX_APP_INSTALLATIONS) { - SEND_ERR_RESPONSE(mid, - "Install Applet failed: exceed max app installations."); - goto fail; - } - - if (!request->payload) { - SEND_ERR_RESPONSE(mid, - "Install Applet failed: invalid payload."); - goto fail; - } - if (g_module_interfaces[module_type] - && g_module_interfaces[module_type]->module_install) { - if (!g_module_interfaces[module_type]->module_install(request)) - goto fail; - } - } - /* Uninstall Applet */ - else if (request->action == COAP_DELETE) { - module_type = get_module_type(request->url + offset); - if (module_type == -1) { - SEND_ERR_RESPONSE(mid, - "Uninstall Applet failed: invalid module type."); - goto fail; - } - - if (g_module_interfaces[module_type] - && g_module_interfaces[module_type]->module_uninstall) { - if (!g_module_interfaces[module_type]->module_uninstall( - request)) - goto fail; - } - } - /* Query Applets installed */ - else if (request->action == COAP_GET) { - char name[APP_NAME_MAX_LEN] = { 0 }; - char *properties = request->url + offset; - find_key_value(properties, strlen(properties), "name", name, - sizeof(name) - 1, '&'); - if (strlen(name) > 0) - app_manager_query_applets(request, name); - else - app_manager_query_applets(request, NULL); - } else { - SEND_ERR_RESPONSE(mid, "Invalid request of applet: invalid action"); - } - } - /* Event Register/Unregister */ - else if ((offset = check_url_start(request->url, strlen(request->url), - "/event/")) > 0) { - char url_buf[256] = { 0 }; - - strncpy(url_buf, request->url + offset, sizeof(url_buf) - 1); - - if (!event_handle_event_request(request->action, url_buf, ID_HOST)) { - SEND_ERR_RESPONSE(mid, "Handle event request failed."); - goto fail; - } - send_error_response_to_host(mid, CONTENT_2_05, NULL); /* OK */ - } else { - int i; - for (i = 0; i < Module_Max; i++) { - if (g_module_interfaces[i] - && g_module_interfaces[i]->module_handle_host_url) { - if (g_module_interfaces[i]->module_handle_host_url(request)) - break; - } - } - - } - - fail: - - return; - -} - -static void module_interfaces_init() -{ - int i; - for (i = 0; i < Module_Max; i++) { - if (g_module_interfaces[i] && g_module_interfaces[i]->module_init) - g_module_interfaces[i]->module_init(); - } -} - -void app_manager_startup(host_interface *interface) -{ - module_interfaces_init(); - - /* Create queue of App Manager */ - g_app_mgr_queue = bh_queue_create(); - if (!g_app_mgr_queue) - return; - - if (!module_data_list_init()) - goto fail1; - - if (!watchdog_startup()) - goto fail2; - - /* Initialize Host */ - app_manager_host_init(interface); - - am_register_resource("/app/", targeted_app_request_handler, ID_APP_MGR); - - /*/app/ and /event/ are both processed by applet_mgt_reqeust_handler*/ - am_register_resource("/applet", applet_mgt_reqeust_handler, ID_APP_MGR); - am_register_resource("/event/", applet_mgt_reqeust_handler, ID_APP_MGR); - - app_manager_printf("App Manager started.\n"); - - /* Enter loop run */ - bh_queue_enter_loop_run(g_app_mgr_queue, app_manager_queue_callback, NULL); - - fail2: module_data_list_destroy(); - - fail1: bh_queue_destroy(g_app_mgr_queue); -} - -#include "module_config.h" - -module_interface *g_module_interfaces[Module_Max] = { -#if ENABLE_MODULE_JEFF != 0 - &jeff_module_interface, -#else - NULL, -#endif - -#if ENABLE_MODULE_WASM_APP != 0 - &wasm_app_module_interface, -#else - NULL, -#endif - -#if ENABLE_MODULE_WASM_LIB != 0 - &wasm_lib_module_interface -#else - NULL -#endif - }; diff --git a/core/app-mgr/app-manager/app_manager.h b/core/app-mgr/app-manager/app_manager.h deleted file mode 100644 index 188c685d1f..0000000000 --- a/core/app-mgr/app-manager/app_manager.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef APP_MANAGER_H -#define APP_MANAGER_H - -#include "bh_platform.h" -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_types.h" -#include "app_manager_export.h" -#include "native_interface.h" -#include "bi-inc/shared_utils.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* bh_printf is defined in each platform */ -#define app_manager_printf bh_printf - -#define SEND_ERR_RESPONSE(mid, err_msg) do { \ - app_manager_printf("%s\n", err_msg); \ - send_error_response_to_host(mid, INTERNAL_SERVER_ERROR_5_00, err_msg); \ -} while (0) - -extern module_interface *g_module_interfaces[Module_Max]; - -/* Lock of the module data list */ -extern korp_mutex module_data_list_lock; - -/* Module data list */ -extern module_data *module_data_list; - -void -app_manager_add_module_data(module_data *m_data); - -void -app_manager_del_module_data(module_data *m_data); - -bool -module_data_list_init(); - -void -module_data_list_destroy(); - -bool -app_manager_is_interrupting_module(uint32 module_type, void *module_inst); - -void release_module(module_data *m_data); - -void -module_data_list_remove(module_data *m_data); - -void* -app_manager_timer_create(void (*timer_callback)(void*), - watchdog_timer *wd_timer); - -void -app_manager_timer_destroy(void *timer); - -void -app_manager_timer_start(void *timer, int timeout); - -void -app_manager_timer_stop(void *timer); - -watchdog_timer* -app_manager_get_wd_timer_from_timer_handle(void *timer); - -int -app_manager_signature_verify(const uint8_t *file, unsigned int file_len, - const uint8_t *signature, unsigned int sig_size); - -void targeted_app_request_handler(request_t *request, void *unused); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif - diff --git a/core/app-mgr/app-manager/app_manager_host.c b/core/app-mgr/app-manager/app_manager_host.c deleted file mode 100644 index efd86e4867..0000000000 --- a/core/app-mgr/app-manager/app_manager_host.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_common.h" -#include "bh_types.h" -#include "app_manager_host.h" -#include "app_manager.h" -#include "app_manager_export.h" -#include "coap_ext.h" -#include "bh_memory.h" -#include "bh_thread.h" - -/* host communication interface */ -static host_interface host_commu; - -/* IMRTLink Two leading bytes */ -static unsigned char leadings[] = { (unsigned char) 0x12, (unsigned char) 0x34 }; - -/* IMRTLink Receiving Phase */ -typedef enum recv_phase_t { - Phase_Non_Start, Phase_Leading, Phase_Type, Phase_Size, Phase_Payload -} recv_phase_t; - -/* IMRTLink Receive Context */ -typedef struct recv_context_t { - recv_phase_t phase; - bh_link_msg_t message; - int size_in_phase; -} recv_context_t; - -/* Current IMRTLink receive context */ -static recv_context_t recv_ctx; - -/* Lock for device write */ -static korp_mutex host_lock; - -static bool enable_log = false; - -static bool is_little_endian() -{ - long i = 0x01020304; - unsigned char* c = (unsigned char*) &i; - return (*c == 0x04) ? true : false; -} - -static void exchange32(uint8* pData) -{ - uint8 value = *pData; - *pData = *(pData + 3); - *(pData + 3) = value; - - value = *(pData + 1); - *(pData + 1) = *(pData + 2); - *(pData + 2) = value; -} - -/* return: - * 1: complete message received - * 0: incomplete message received - */ -static int on_imrt_link_byte_arrive(unsigned char ch, recv_context_t *ctx) -{ - if (ctx->phase == Phase_Non_Start) { - ctx->message.payload_size = 0; - - if (ctx->message.payload) { - bh_free(ctx->message.payload); - ctx->message.payload = NULL; - } - - if (ch == leadings[0]) { - if (enable_log) - app_manager_printf("##On byte arrive: got leading 0\n"); - ctx->phase = Phase_Leading; - } - - return 0; - } else if (ctx->phase == Phase_Leading) { - if (ch == leadings[1]) { - if (enable_log) - app_manager_printf("##On byte arrive: got leading 1\n"); - ctx->phase = Phase_Type; - } else - ctx->phase = Phase_Non_Start; - - return 0; - } else if (ctx->phase == Phase_Type) { - if (ctx->size_in_phase++ == 0) { - if (enable_log) - app_manager_printf("##On byte arrive: got type 0\n"); - ctx->message.message_type = ch; - } else { - if (enable_log) - app_manager_printf("##On byte arrive: got type 1\n"); - ctx->message.message_type |= (ch << 8); - ctx->message.message_type = ntohs(ctx->message.message_type); - ctx->phase = Phase_Size; - ctx->size_in_phase = 0; - } - - return 0; - } else if (ctx->phase == Phase_Size) { - unsigned char *p = (unsigned char *) &ctx->message.payload_size; - - if (enable_log) - app_manager_printf("##On byte arrive: got payload_size, byte %d\n", - ctx->size_in_phase); - p[ctx->size_in_phase++] = ch; - - if (ctx->size_in_phase == sizeof(ctx->message.payload_size)) { - ctx->message.payload_size = ntohl(ctx->message.payload_size); - ctx->phase = Phase_Payload; - - if (enable_log) - app_manager_printf("##On byte arrive: payload_size: %d\n", - ctx->message.payload_size); - if (ctx->message.payload) { - bh_free(ctx->message.payload); - ctx->message.payload = NULL; - } - - /* message completion */ - if (ctx->message.payload_size == 0) { - ctx->phase = Phase_Non_Start; - if (enable_log) - app_manager_printf( - "##On byte arrive: receive end, payload_size is 0.\n"); - return 1; - } - - if (ctx->message.payload_size > 1024 * 1024) { - ctx->phase = Phase_Non_Start; - return 0; - } - - if (ctx->message.message_type != INSTALL_WASM_APP) { - ctx->message.payload = - (char *) bh_malloc(ctx->message.payload_size); - if (!ctx->message.payload) { - ctx->phase = Phase_Non_Start; - return 0; - } - } - - ctx->phase = Phase_Payload; - ctx->size_in_phase = 0; - } - - return 0; - } else if (ctx->phase == Phase_Payload) { - if (ctx->message.message_type == INSTALL_WASM_APP) { - int received_size; - module_on_install_request_byte_arrive_func module_on_install = - g_module_interfaces[Module_WASM_App]->module_on_install; - - ctx->size_in_phase++; - - if (module_on_install != NULL) { - if (module_on_install(ch, ctx->message.payload_size, - &received_size)) { - if (received_size == ctx->message.payload_size) { - /* whole wasm app received */ - ctx->phase = Phase_Non_Start; - return 1; - } - } else { - /* receive or handle fail */ - ctx->phase = Phase_Non_Start; - ctx->size_in_phase = 0; - return 0; - } - return 0; - } else { - ctx->phase = Phase_Non_Start; - ctx->size_in_phase = 0; - return 0; - } - } else { - ctx->message.payload[ctx->size_in_phase++] = ch; - - if (ctx->size_in_phase == ctx->message.payload_size) { - ctx->phase = Phase_Non_Start; - if (enable_log) - app_manager_printf("##On byte arrive: receive end, payload_size is %d.\n", - ctx->message.payload_size); - return 1; - } - return 0; - } - } - - return 0; -} - -int aee_host_msg_callback(void *msg, uint16_t msg_len) -{ - unsigned char *p = msg, *p_end = p + msg_len; - - /*app_manager_printf("App Manager receive %d bytes from Host\n", msg_len);*/ - - for (; p < p_end; p++) { - int ret = on_imrt_link_byte_arrive(*p, &recv_ctx); - - if (ret == 1) { - if (recv_ctx.message.payload) { - int msg_type = recv_ctx.message.message_type; - - if (msg_type == REQUEST_PACKET) { - request_t request; - memset(&request, 0, sizeof(request)); - - if (!unpack_request(recv_ctx.message.payload, - recv_ctx.message.payload_size, &request)) - continue; - - request.sender = ID_HOST; - - am_dispatch_request(&request); - } else { - printf("unexpected host msg type: %d\n", msg_type); - } - - bh_free(recv_ctx.message.payload); - recv_ctx.message.payload = NULL; - recv_ctx.message.payload_size = 0; - } - - memset(&recv_ctx, 0, sizeof(recv_ctx)); - } - } - - return 0; -} - -bool app_manager_host_init(host_interface *interface) -{ - vm_mutex_init(&host_lock); - memset(&recv_ctx, 0, sizeof(recv_ctx)); - - host_commu.init = interface->init; - host_commu.send = interface->send; - host_commu.destroy = interface->destroy; - - if (host_commu.init != NULL) - return host_commu.init(); - - return true; -} - -int app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size) -{ - /* send an IMRT LINK message contains the buf as payload */ - if (host_commu.send != NULL) { - int size_s = size, n; - char header[16]; - - vm_mutex_lock(&host_lock); - /* leading bytes */ - bh_memcpy_s(header, 2, leadings, 2); - - /* message type */ - // todo: check if use network byte order!!! - *((uint16*) (header + 2)) = htons(msg_type); - - /* payload length */ - if (is_little_endian()) - exchange32((uint8*) &size_s); - - bh_memcpy_s(header + 4, 4, &size_s, 4); - n = host_commu.send(NULL, header, 8); - if (n != 8) { - vm_mutex_unlock(&host_lock); - return 0; - } - - /* payload */ - n = host_commu.send(NULL, buf, size); - vm_mutex_unlock(&host_lock); - - printf("sent %d bytes to host\n", n); - return n; - } else { - printf("no send api provided\n"); - } - return 0; -} diff --git a/core/app-mgr/app-manager/app_manager_host.h b/core/app-mgr/app-manager/app_manager_host.h deleted file mode 100644 index cc663725be..0000000000 --- a/core/app-mgr/app-manager/app_manager_host.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _APP_MANAGER_HOST_H_ -#define _APP_MANAGER_HOST_H_ - -#include "wasm_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define HOST_MODE_AON 1 -#define HOST_MODE_UART 2 -#define HOST_MODE_TEST 3 - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif - diff --git a/core/app-mgr/app-manager/app_mgr.cmake b/core/app-mgr/app-manager/app_mgr.cmake deleted file mode 100644 index fd6e69098e..0000000000 --- a/core/app-mgr/app-manager/app_mgr.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (__APP_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${__APP_MGR_DIR}) - - -file (GLOB source_all ${__APP_MGR_DIR}/*.c ${__APP_MGR_DIR}/platform/${WAMR_BUILD_PLATFORM}/*.c) - -set (APP_MGR_SOURCE ${source_all}) - -file (GLOB header - ${__APP_MGR_DIR}/module_wasm_app.h -) -LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) - diff --git a/core/app-mgr/app-manager/ble_msg.c b/core/app-mgr/app-manager/ble_msg.c deleted file mode 100644 index 3794fa56b2..0000000000 --- a/core/app-mgr/app-manager/ble_msg.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#if 0 - -#define BLUETOOTH_INTERFACE_ADVERTISMENT_DATA_LENGTH 31 -/* ble_device_info */ -typedef struct ble_device_info { - - /* address type */ - uint8_t address_type; - /* MAC of Device */ - uint8_t mac[6]; - /* security level */ - uint8_t security_level; - /* signal strength */ - int8_t rssi; - /* uuid_16_type */ - int8_t uuid_16_type; - /* uuid_32_type */ - int8_t uuid_32_type; - /* uuid_128_type */ - int8_t uuid_128_type; - /* error code */ - uint8_t error_code; - /* scan response length*/ - uint16_t adv_data_len; - /* advertisement data */ - uint8_t *adv_data; - /* scan response length*/ - uint16_t scan_response_len; - /* scan response */ - uint8_t *scan_response; - /* next device */ - struct ble_device_info *next; - /* private data length */ - int private_data_length; - /* private data */ - uint8_t *private_data; - /* value handle*/ - uint16_t value_handle; - /* ccc handle*/ - uint16_t ccc_handle; - -}ble_device_info; - -/* BLE message sub type */ -typedef enum BLE_SUB_EVENT_TYPE { - BLE_SUB_EVENT_DISCOVERY, - BLE_SUB_EVENT_CONNECTED, - BLE_SUB_EVENT_DISCONNECTED, - BLE_SUB_EVENT_NOTIFICATION, - BLE_SUB_EVENT_INDICATION, - BLE_SUB_EVENT_PASSKEYENTRY, - BLE_SUB_EVENT_SECURITY_LEVEL_CHANGE -}BLE_SUB_EVENT_TYPE; - -/* Queue message, for BLE Event */ -typedef struct bh_queue_ble_sub_msg_t { - /* message type, should be one of QUEUE_MSG_TYPE */ - BLE_SUB_EVENT_TYPE type; - /* payload size */ - /*uint32_t payload_size;*/ - char payload[1]; -}bh_queue_ble_sub_msg_t; - -static void -app_instance_free_ble_msg(char *msg) -{ - bh_queue_ble_sub_msg_t *ble_msg = (bh_queue_ble_sub_msg_t *)msg; - ble_device_info *dev_info; - - dev_info = (ble_device_info *) ble_msg->payload; - - if (dev_info->scan_response != NULL) - bh_free(dev_info->scan_response); - - if (dev_info->private_data != NULL) - bh_free(dev_info->private_data); - - if (dev_info->adv_data != NULL) - bh_free(dev_info->adv_data); - - if (dev_info != NULL) - bh_free(dev_info); -} - -static void -app_instance_queue_free_callback(bh_message_t queue_msg) -{ - - char * payload = (char *)bh_message_payload(queue_msg); - if(payload == NULL) - return; - - switch (bh_message_type(queue_msg)) - { - /* - case SENSOR_EVENT: { - bh_sensor_event_t *sensor_event = (bh_sensor_event_t *) payload; - attr_container_t *event = sensor_event->event; - attr_container_destroy(event); - } - break; - */ - case BLE_EVENT: { - app_instance_free_ble_msg(payload); - break; - } - } -} - -#endif diff --git a/core/app-mgr/app-manager/coding_rule.txt b/core/app-mgr/app-manager/coding_rule.txt deleted file mode 100644 index 4598872a34..0000000000 --- a/core/app-mgr/app-manager/coding_rule.txt +++ /dev/null @@ -1,15 +0,0 @@ -Coding rules: - -1. module implementation can include the export head files of associated runtime - -2. app manager only call access the module implementation through the interface API - -3. module implementation can call the app manager API from following files: - - util.c - - message.c - -4. platform API: To define it - -5. Any platform dependent implementation of app manager should be implemented in the - platform specific source file, such as app_mgr_zephyr.c - diff --git a/core/app-mgr/app-manager/event.c b/core/app-mgr/app-manager/event.c deleted file mode 100644 index 67def1c78d..0000000000 --- a/core/app-mgr/app-manager/event.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include - -#include "event.h" - -#include "app_manager.h" -#include "bh_memory.h" -#include "coap_ext.h" - -typedef struct _subscribe { - struct _subscribe * next; - uint32 subscriber_id; -} subscribe_t; - -typedef struct _event { - struct _event *next; - int subscriber_size; - subscribe_t * subscribers; - char url[1]; /* event url */ -} event_reg_t; - -event_reg_t *g_events = NULL; - -static bool find_subscriber(event_reg_t * reg, uint32 id, bool remove_found) -{ - subscribe_t* c = reg->subscribers; - subscribe_t * prev = NULL; - while (c) { - subscribe_t * next = c->next; - if (c->subscriber_id == id) { - if (remove_found) { - if (prev) - prev->next = next; - else - reg->subscribers = next; - - bh_free(c); - } - - return true; - } else { - prev = c; - c = next; - } - } - - return false; -} - -static bool check_url(const char *url) -{ - if (*url == 0) - return false; - - return true; -} - -bool am_register_event(const char *url, uint32_t reg_client) -{ - event_reg_t *current = g_events; - - app_manager_printf("am_register_event adding url:(%s)\n", url); - - if (!check_url(url)) { - app_manager_printf("am_register_event: invaild url:(%s)\n", url); - return false; - } - while (current) { - if (strcmp(url, current->url) == 0) - break; - current = current->next; - } - - if (current == NULL) { - if (NULL - == (current = (event_reg_t *) bh_malloc( - offsetof(event_reg_t, url) + strlen(url) + 1))) { - app_manager_printf("am_register_event: malloc fail\n"); - return false; - } - - memset(current, 0, sizeof(event_reg_t)); - bh_strcpy_s(current->url, strlen(url) + 1, url); - current->next = g_events; - g_events = current; - } - - if (find_subscriber(current, reg_client, false)) { - return true; - } else { - subscribe_t * s = (subscribe_t*) bh_malloc(sizeof(subscribe_t)); - if (s == NULL) - return false; - - memset(s, 0, sizeof(subscribe_t)); - s->subscriber_id = reg_client; - s->next = current->subscribers; - current->subscribers = s; - app_manager_printf("client: %d registered event (%s)\n", reg_client, - url); - } - - return true; -} - -// @url: NULL means the client wants to unregister all its subscribed items -bool am_unregister_event(const char *url, uint32_t reg_client) -{ - event_reg_t *current = g_events, *pre = NULL; - - while (current != NULL) { - if (url == NULL || strcmp(current->url, url) == 0) { - event_reg_t * next = current->next; - if (find_subscriber(current, reg_client, true)) { - app_manager_printf("client: %d deregistered event (%s)\n", - reg_client, current->url); - } - - // remove the registration if no client subscribe it - if (current->subscribers == NULL) { - app_manager_printf("unregister for event deleted url:(%s)\n", - current->url); - if (pre) - pre->next = next; - else - g_events = next; - bh_free(current); - current = next; - continue; - } - } - pre = current; - current = current->next; - } - - return true; -} - -bool event_handle_event_request(uint8_t code, const char *event_url, - uint32_t reg_client) -{ - if (code == COAP_PUT) { /* register */ - return am_register_event(event_url, reg_client); - } else if (code == COAP_DELETE) { /* unregister */ - return am_unregister_event(event_url, reg_client); - } else { - /* invalid request */ - return false; - } -} - -void am_publish_event(request_t * event) -{ - bh_assert(event->action == COAP_EVENT); - - event_reg_t *current = g_events; - while (current) { - if (0 == strcmp(event->url, current->url)) { - subscribe_t* c = current->subscribers; - while (c) { - if (c->subscriber_id == ID_HOST) { - send_request_to_host(event); - } else { - module_request_handler - (event, (void *)(uintptr_t)c->subscriber_id); - } - c = c->next; - } - - return; - } - - current = current->next; - } -} - -bool event_is_registered(const char *event_url) -{ - event_reg_t *current = g_events; - - while (current != NULL) { - if (strcmp(current->url, event_url) == 0) { - return true; - } - current = current->next; - } - - return false; -} diff --git a/core/app-mgr/app-manager/event.h b/core/app-mgr/app-manager/event.h deleted file mode 100644 index a9c3de3b2f..0000000000 --- a/core/app-mgr/app-manager/event.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _EVENT_H_ -#define _EVENT_H_ - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Handle event request from host agent - * - * @param code the coap packet code - * @param event_url the event url - * - * @return true if success, false otherwise - */ -bool -event_handle_event_request(uint8_t code, const char *event_url, - uint32_t register); - -/** - * Test whether the event is registered - * - * @param event_url the event url - * - * @return true for registered, false for not registered - */ -bool -event_is_registered(const char *event_url); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _EVENT_H_ */ diff --git a/core/app-mgr/app-manager/message.c b/core/app-mgr/app-manager/message.c deleted file mode 100644 index 13ee4c04f8..0000000000 --- a/core/app-mgr/app-manager/message.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "app_manager.h" -#include "app_manager_host.h" -#include "event.h" -#include "bi-inc/attr_container.h" -#include "bh_memory.h" -#include "coap_ext.h" - -#if 0 -bool send_coap_packet_to_host(coap_packet_t * packet) -{ - int size; - uint8_t *buf; - - size = coap_serialize_message_tcp(&packet, &buf); - if (!buf || size == 0) - return false; - - app_manager_host_send_msg(buf, size); - bh_free(buf); - - return true; -} -#endif - -bool send_request_to_host(request_t *msg) -{ - if (COAP_EVENT == msg->action && !event_is_registered(msg->url)) { - app_manager_printf("Event is not registered\n"); - return false; - } - - int size; - char * packet = pack_request(msg, &size); - if (packet == NULL) - return false; - - app_manager_host_send_msg(REQUEST_PACKET, packet, size); - - free_req_resp_packet(packet); - - return true; -} - -bool send_response_to_host(response_t *response) -{ - int size; - char * packet = pack_response(response, &size); - if (packet == NULL) - return false; - - app_manager_host_send_msg(RESPONSE_PACKET, packet, size); - - free_req_resp_packet(packet); - - return true; -} - -bool send_error_response_to_host(int mid, int status, const char *msg) -{ - int payload_len = 0; - attr_container_t *payload = NULL; - response_t response[1] = { 0 }; - - if (msg) { - payload = attr_container_create(""); - if (payload) { - attr_container_set_string(&payload, "error message", msg); - payload_len = attr_container_get_serialize_length(payload); - } - } - - set_response(response, status, - FMT_ATTR_CONTAINER, (const char *)payload, payload_len); - response->mid = mid; - - send_response_to_host(response); - - if (payload) - attr_container_destroy(payload); - return true; -} - diff --git a/core/app-mgr/app-manager/module_config.h b/core/app-mgr/app-manager/module_config.h deleted file mode 100644 index b742fed3a4..0000000000 --- a/core/app-mgr/app-manager/module_config.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _MODULE_CONFIG_H_ -#define _MODULE_CONFIG_H_ - -#define ENABLE_MODULE_JEFF 0 -#define ENABLE_MODULE_WASM_APP 1 -#define ENABLE_MODULE_WASM_LIB 1 - -#ifdef ENABLE_MODULE_JEFF -#include "module_jeff.h" -#endif -#ifdef ENABLE_MODULE_WASM_APP -#include "module_wasm_app.h" -#endif -#ifdef ENABLE_MODULE_WASM_LIB -#include "module_wasm_lib.h" -#endif - -#endif /* _MODULE_CONFIG_H_ */ diff --git a/core/app-mgr/app-manager/module_jeff.c b/core/app-mgr/app-manager/module_jeff.c deleted file mode 100644 index af2fbaef09..0000000000 --- a/core/app-mgr/app-manager/module_jeff.c +++ /dev/null @@ -1,1733 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifdef ENABLE_JEFF - -#include "module_jeff.h" -#include "jeff_export.h" -#include "../vmcore_jeff/jeff-runtime.h" -#include "../vmcore_jeff/jeff-thread.h" -#include "../vmcore_jeff/jeff-buffer.h" -#include "../vmcore_jeff/jeff-tool.h" -#include "../vmcore_jeff/jeff-tool-priv.h" -#include "app_manager-host.h" -#include "bh_queue.h" -#include "attr-container.h" -#include "attr-container-util.h" -#include "bh_thread.h" -#include "ems_gc.h" -#include "coap_ext.h" -#include "libcore.h" -#include "event.h" -#include "watchdog.h" - -#define DEFAULT_APPLET_TIMEOUT (3 * 60 * 1000) -#define DEFAULT_APPLET_HEAP_SIZE (48 * 1024) -#define MIN_APPLET_HEAP_SIZE (2 * 1024) -#define MAX_APPLET_HEAP_SIZE (1024 * 1024) - -typedef struct jeff_applet_data { - /* Java Applet Object */ - JeffObjectRef applet_obj; - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - /* Whether the applet is in debug mode */ - bool debug_mode; - /* Queue of the tool agent */ - bh_queue *tool_agent_queue; -#endif - - /* VM instance */ - JeffInstanceLocalRoot *vm_instance; - /* Applet Main file */ - JeffFileHeaderLinked *main_file; - /* Permissions of the Java Applet */ - char *perms; -}jeff_applet_data; - -/* Jeff class com.intel.aee.AEEApplet */ -static JeffClassHeaderLinked *class_AEEApplet; -/* Jeff class com.intel.aee.Request */ -static JeffClassHeaderLinked *class_AEERequest; -/* Jeff class com.intel.aee.Timer */ -static JeffClassHeaderLinked *class_Timer; -/* Jeff class com.intel.aee.Sensor */ -static JeffClassHeaderLinked *class_Sensor; -/* Jeff class com.intel.aee.ble.BLEManager */ -static JeffClassHeaderLinked *class_BLEManager; -/* Jeff class com.intel.aee.ble.BLEDevice */ -static JeffClassHeaderLinked *class_BLEDevice; -/* Jeff class com.intel.aee.ble.BLEGattService */ -JeffClassHeaderLinked *class_BLEGattService; -/* Jeff class com.intel.aee.ble.BLEGattCharacteristic */ -JeffClassHeaderLinked *class_BLEGattCharacteristic; -/* Jeff class com.intel.aee.ble.BLEGattDescriptor */ -JeffClassHeaderLinked *class_BLEGattDescriptor; -/* Jeff class com.intel.aee.gpio.GPIOChannel */ -static JeffClassHeaderLinked *class_GPIOChannel; -/* Jeff method void com.intel.aee.AEEApplet.onInit() */ -static JeffMethodLinked *method_AEEApplet_onInit; -/* Jeff method void com.intel.aee.AEEApplet.onDestroy() */ -static JeffMethodLinked *method_AEEApplet_onDestroy; -/* Jeff method void com.intel.aee.AEEApplet.callOnRequest(Request request) */ -static JeffMethodLinked *method_AEEApplet_callOnRequest; -/* Jeff method void com.intel.aee.Timer.callOnTimer() */ -static JeffMethodLinked *method_callOnTimer; -/* Jeff method void com.intel.aee.Sensor.callOnSensorEvent() */ -static JeffMethodLinked *method_callOnSensorEvent; -/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEStartDiscovery() */ -static JeffMethodLinked *method_callOnBLEStartDiscovery; -/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEConnected() */ -static JeffMethodLinked *method_callOnBLEConnected; -/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEDisonnected() */ -static JeffMethodLinked *method_callOnBLEDisconnected; -/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLENotification() */ -static JeffMethodLinked *method_callOnBLENotification; -/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEIndication() */ -static JeffMethodLinked *method_callOnBLEIndication; -/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEPasskeyEntry() */ -static JeffMethodLinked *method_callOnBLEPasskeyEntry; -/* Jeff method void com.intel.aee.gpio.GPIOChannel.callOnGPIOInterrupt() */ -static JeffMethodLinked *method_callOnGPIOInterrupt; -/* Jeff method void com.intel.aee.ble.BLEManager.getBLEDevice() */ -static JeffMethodLinked *method_callOnBLEManagerGetBLEDevice; - -static jeff_applet_data * -app_manager_get_jeff_applet_data() -{ - module_data *m_data = app_manager_get_module_data(Module_Jeff); - return (jeff_applet_data *)m_data->internal_data; -} - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 -void * -app_manager_get_tool_agent_queue() -{ - return app_manager_get_jeff_applet_data()->tool_agent_queue; -} -#endif - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 -static bool -is_tool_agent_running(module_data *m_data) -{ - jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; - return (applet_data->debug_mode - && applet_data->tool_agent_queue - && applet_data->vm_instance->tool_agent); -} -#endif - -static char * -get_class_qname(const JeffString *pname, const JeffString *cname) -{ - unsigned int length = pname->length ? pname->length + 2 + cname->length - : cname->length + 1; - char *buf = bh_malloc(length), *p; - - if (!buf) - return NULL; - - p = buf; - if (pname->length) { - bh_memcpy_s(p, pname->length, pname->value, pname->length); - p += pname->length; - *p++ = '.'; - } - - bh_memcpy_s(p, cname->length, cname->value, cname->length); - p += cname->length; - *p = '\0'; - - return buf; -} - -static void -send_exception_event_to_host(const char *applet_name, const char *exc_name) -{ - attr_container_t *payload; - bh_request_msg_t msg; - char *url; - int url_len; - - payload = attr_container_create("exception detail"); - if (!payload) { - app_manager_printf("Send exception to host fail: allocate memory"); - return; - } - - if (!attr_container_set_string(&payload, "exception name", exc_name) - || !attr_container_set_string(&payload, "stack trace", "TODO") - || !attr_container_set_string(&payload, "applet name", applet_name)) { - app_manager_printf("Send exception to host fail: set attr"); - goto fail; - } - - url_len = strlen("/exception/") + strlen(applet_name); - url = bh_malloc(url_len + 1); - if (!url) { - app_manager_printf("Send exception to host fail: allocate memory"); - goto fail; - } - memset(url, 0, url_len + 1); - bh_strcpy_s(url, url_len + 1, "/exception/"); - bh_strcat_s(url, url_len + 1, applet_name); - - memset(&msg, 0, sizeof(msg)); - msg.url = url; - msg.action = COAP_PUT; - msg.payload = (char *)payload; - - app_send_request_msg_to_host(&msg); - - bh_free(url); - - fail: - attr_container_destroy(payload); -} - -static bool -check_exception() -{ - if (jeff_runtime_get_exception()) { - jeff_printf("V1.Exception thrown when running applet '%s':\n", - app_manager_get_module_name(Module_Jeff)); - jeff_runtime_print_exception(); - jeff_printf("\n"); - jeff_printf(NULL); - - if (!app_manager_is_interrupting_module(Module_Jeff)) { - attr_container_t *payload; - int payload_len; - JeffClassHeaderLinked *exc_class = jeff_object_class_pointer(jeff_runtime_get_exception()); - char *qname_buf = get_class_qname(jeff_get_class_pname(exc_class), - jeff_get_class_cname(exc_class)); - - /* Send exception event to host */ - if (qname_buf) { - send_exception_event_to_host(app_manager_get_module_name(Module_Jeff), qname_buf); - bh_free(qname_buf); - } - - /* Uninstall the applet */ - if ((payload = attr_container_create("uninstall myself"))) { - if (attr_container_set_string(&payload, "name", app_manager_get_module_name(Module_Jeff)) - /* Set special flag to prevent app manager making response since this is an internal message */ - && attr_container_set_bool(&payload, "do not reply me", true)) { - request_t request = {0}; - payload_len = attr_container_get_serialize_length(payload); - - init_request(request, "/applet", COAP_DELETE, (char *)payload, payload_len)); - app_mgr_lookup_resource(&request); - - // TODO: confirm this is right - attr_container_destroy(payload); - } - } - - jeff_runtime_set_exception(NULL); - return true; - } - - return false; - } - - static bool - app_manager_initialize_class(JeffClassHeaderLinked *c) - { - jeff_runtime_initialize_class(c); - return !check_exception(); - } - - static bool - app_manager_initialize_object(JeffObjectRef obj) - { - jeff_runtime_initialize_object(obj); - return !check_exception(); - } - - static bool - app_manager_call_java(JeffMethodLinked *method, - unsigned int argc, uint32 argv[], uint8 argt[]) - { - module_data *m_data = app_manager_get_module_data(Module_Jeff); - watchdog_timer *wd_timer = &m_data->wd_timer; - bool is_wd_started = false; - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - /* Only start watchdog when debugger is not running */ - if (!is_tool_agent_running(m_data)) { -#endif - watchdog_timer_start(wd_timer); - is_wd_started = true; -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - } -#endif - - jeff_runtime_call_java(method, argc, argv, argt); - - if (is_wd_started) { - vm_mutex_lock(&wd_timer->lock); - if (!wd_timer->is_interrupting) { - wd_timer->is_stopped = true; - watchdog_timer_stop(wd_timer); - } - vm_mutex_unlock(&wd_timer->lock); - } - - return !check_exception(); - } - - static AEEBLEDevice - create_object_BLEDevice(ble_device_info *dev_info) - { - JeffLocalObjectRef ref; - AEEBLEDevice dev_struct; - - jeff_runtime_push_local_object_ref(&ref); - - ref.val = jeff_runtime_new_object(class_BLEDevice); - - if (!ref.val) { - jeff_runtime_pop_local_object_ref(1); - return NULL; - } - - dev_struct = (AEEBLEDevice) (ref.val); - dev_struct->rssi = dev_info->rssi; - dev_struct->mac = (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->mac, 6); - - app_manager_printf("adv_data_len:%d,scan_response_len:%d\n", dev_info->adv_data_len, dev_info->scan_response_len); - - dev_struct->advData = (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->adv_data, dev_info->adv_data_len); - dev_struct->scanResponse = (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->scan_response, dev_info->scan_response_len); - dev_struct->addressType = dev_info->address_type; - jeff_runtime_initialize_object(ref.val); - jeff_runtime_pop_local_object_ref(1); - if ((dev_struct->mac == NULL) || (dev_struct->advData == NULL) || (dev_struct->scanResponse == NULL)) { - return NULL; - } - return (AEEBLEDevice) ref.val; - } - - static void - app_instance_process_ble_msg(char *msg) - { - bh_queue_ble_sub_msg_t *ble_msg = (bh_queue_ble_sub_msg_t *)msg; - unsigned int argv[5]; - uint8 argt[5]; - - ble_device_info *dev_info; - - dev_info = (ble_device_info *) ble_msg->payload; - AEEBLEDevice ble_dev; - - argv[0] = (unsigned int) (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->mac, 6); - argt[0] = 1; - if (!app_manager_call_java(method_callOnBLEManagerGetBLEDevice, 1, argv, argt)) { - app_manager_printf("app_manager_call_java BLEManagerGetBLEDevice fail error\n"); - goto fail; - } - ble_dev = (AEEBLEDevice) argv[0]; - if (ble_dev == NULL) { - ble_dev = create_object_BLEDevice(dev_info); - if (ble_dev == NULL) { - goto fail; - } - } - - switch (ble_msg->type) { - case BLE_SUB_EVENT_DISCOVERY: - { - argv[0] = (unsigned int) ble_dev; - argt[0] = 1; - ble_dev->rssi = dev_info->rssi; - if (!app_manager_call_java(method_callOnBLEStartDiscovery, 1, argv, argt)) { - app_manager_printf("app_manager_call_java method_callOnBLEStartDiscovery fail error\n"); - goto fail; - } - } - break; - - case BLE_SUB_EVENT_CONNECTED: - { - if (ble_dev) { - argv[0] = (unsigned int) ble_dev; - argv[1] = 0; - argt[0] = 1; - argt[1] = 1; - if (!app_manager_call_java(method_callOnBLEConnected, 2, argv, argt)) { - app_manager_printf("app_manager_call_java method_callOnBLEConnected fail error\n"); - goto fail; - } - } - } - break; - - case BLE_SUB_EVENT_DISCONNECTED: - { - app_manager_printf("app instance received disconnected\n"); - - if (ble_dev) { - argv[0] = (unsigned int) ble_dev; - argv[1] = 0; - argt[0] = 1; - argt[1] = 1; - ble_dev->rssi = dev_info->rssi; - if (!app_manager_call_java(method_callOnBLEDisconnected, 2, argv, argt)) { - app_manager_printf("app_manager_call_java method_callOnBLEDisconnected fail error\n"); - goto fail; - } - } - } - break; - - case BLE_SUB_EVENT_NOTIFICATION: - { - if (ble_dev) { - argv[0] = (unsigned int) ble_dev; - argv[1] = (unsigned int) (jbyteArray) jeff_runtime_create_byte_array( - (int8 *)dev_info->private_data, dev_info->private_data_length); - argv[2] = dev_info->value_handle; - argv[3] = dev_info->ccc_handle; - argt[1] = 1; - argt[2] = 0; - argt[3] = 0; - ble_dev->rssi = dev_info->rssi; - if (!app_manager_call_java(method_callOnBLENotification, 4, argv, argt)) { - app_manager_printf("app_manager_call_java method_callOnBLENotification fail error\n"); - goto fail; - } - } - } - break; - - case BLE_SUB_EVENT_INDICATION: - { - if (ble_dev) { - argv[0] = (unsigned int) ble_dev; - argv[1] = (unsigned int) (jbyteArray) jeff_runtime_create_byte_array( - (int8 *)dev_info->private_data, dev_info->private_data_length); - argv[2] = dev_info->value_handle; - argv[3] = dev_info->ccc_handle; - argt[0] = 1; - argt[1] = 1; - argt[2] = 0; - argt[3] = 0; - ble_dev->rssi = dev_info->rssi; - if (!app_manager_call_java(method_callOnBLEIndication, 4, argv, argt)) { - app_manager_printf("app_manager_call_java method_callOnBLEIndication fail error\n"); - goto fail; - } - } - } - break; - - case BLE_SUB_EVENT_PASSKEYENTRY: - { - - if (ble_dev) { - argv[0] = (unsigned int) ble_dev; - argt[0] = 1; - argt[1] = 1; - ble_dev->rssi = dev_info->rssi; - if (!app_manager_call_java(method_callOnBLEPasskeyEntry, 1, argv, argt)) { - app_manager_printf("app_manager_call_java method_callOnBLEPasskeyEntry fail error\n"); - goto fail; - } - } - } - break; - - case BLE_SUB_EVENT_SECURITY_LEVEL_CHANGE: - { - if (ble_dev) { - ble_dev->securityLevel = dev_info->security_level; - } - } - break; - - default: - break; - } - - fail: - if (dev_info->scan_response != NULL) { - bh_free(dev_info->scan_response); - } - if (dev_info->private_data != NULL) { - bh_free(dev_info->private_data); - } - - if (dev_info->adv_data != NULL) { - bh_free(dev_info->adv_data); - } - if (dev_info != NULL) { - bh_free(dev_info); - } - - } - - static void - app_instance_free_ble_msg(char *msg) - { - bh_queue_ble_sub_msg_t *ble_msg = (bh_queue_ble_sub_msg_t *)msg; - ble_device_info *dev_info; - - dev_info = (ble_device_info *) ble_msg->payload; - - if (dev_info->scan_response != NULL) - bh_free(dev_info->scan_response); - - if (dev_info->private_data != NULL) - bh_free(dev_info->private_data); - - if (dev_info->adv_data != NULL) - bh_free(dev_info->adv_data); - - if (dev_info != NULL) - bh_free(dev_info); - } - - static void - app_instance_queue_free_callback(void *queue_msg) - { - bh_queue_msg_t *msg = (bh_queue_msg_t *)queue_msg; - - switch (msg->message_type) { - case APPLET_REQUEST: - { - bh_request_msg_t *req_msg = (bh_request_msg_t *)msg->payload; - bh_free(req_msg); - break; - } - - case TIMER_EVENT: - { - break; - } - - case SENSOR_EVENT: - { - if (msg->payload) { - bh_sensor_event_t *sensor_event = (bh_sensor_event_t *)msg->payload; - attr_container_t *event = sensor_event->event; - - attr_container_destroy(event); - bh_free(sensor_event); - } - break; - } - - case BLE_EVENT: - { - if (msg->payload) { - app_instance_free_ble_msg(msg->payload); - bh_free(msg->payload); - } - break; - } - - case GPIO_INTERRUPT_EVENT: - { - break; - } - - default: - { - break; - } - } - - bh_free(msg); - } - - static void - app_instance_queue_callback(void *queue_msg) - { - bh_queue_msg_t *msg = (bh_queue_msg_t *)queue_msg; - unsigned int argv[5]; - uint8 argt[5]; - - if (app_manager_is_interrupting_module(Module_Jeff)) { - app_instance_queue_free_callback(queue_msg); - return; - } - - switch (msg->message_type) { - case APPLET_REQUEST: - { - JeffLocalObjectRef ref; - AEERequest req_obj; - bh_request_msg_t *req_msg = (bh_request_msg_t *)msg->payload; - attr_container_t *attr_cont = (attr_container_t *)req_msg->payload; - module_data *m_data = app_manager_get_module_data(Module_Jeff); - jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; - - app_manager_printf("Applet %s got request, url %s, action %d\n", - m_data->module_name, req_msg->url, req_msg->action); - - /* Create Request object */ - req_obj = (AEERequest)jeff_object_new(m_data->heap, class_AEERequest); - if (!req_obj) { - app_manager_printf("Applet process request failed: create request obj failed.\n"); - goto fail1; - } - - jeff_runtime_push_local_object_ref(&ref); - ref.val = (JeffObjectRef)req_obj; - - req_obj->mid = req_msg->mid; - req_obj->action = req_msg->action; - req_obj->fmt = req_msg->fmt; - - /* Create Java url string */ - if (req_msg->url) { - req_obj->url = (jstring)jeff_runtime_create_java_string(req_msg->url); - if (!req_obj->url) { - app_manager_printf("Applet process request failed: create url string failed.\n"); - goto fail2; - } - } - - /* Create Java AttributeObject payload */ - if (attr_cont - && !attr_container_to_attr_obj(attr_cont, &req_obj->payload)) { - app_manager_printf("Applet process request failed: convert payload failed.\n"); - goto fail2; - } - - /* Call AEEApplet.callOnRequest(Request request) method */ - argv[0] = (unsigned int)applet_data->applet_obj; - argv[1] = (unsigned int)req_obj; - argt[0] = argt[1] = 1; - app_manager_call_java(method_AEEApplet_callOnRequest, 2, argv, argt); - app_manager_printf("Applet process request success.\n"); - - fail2: - jeff_runtime_pop_local_object_ref(1); - fail1: - bh_free(req_msg); - break; - } - - case TIMER_EVENT: - { - if (msg->payload) { - /* Call Timer.callOnTimer() method */ - argv[0] = (unsigned int)msg->payload; - argt[0] = 1; - app_manager_call_java(method_callOnTimer, 1, argv, argt); - } - break; - } - - case SENSOR_EVENT: - { - if (msg->payload) { - bh_sensor_event_t *sensor_event = (bh_sensor_event_t *)msg->payload; - AEESensor sensor = sensor_event->sensor; - attr_container_t *event = sensor_event->event; - bool ret = attr_container_to_attr_obj(event, &sensor->event); - - attr_container_destroy(event); - bh_free(sensor_event); - - if (ret) { - /* Call Sensor.callOnSensorEvent() method */ - argv[0] = (unsigned int)sensor; - argt[0] = 1; - app_manager_call_java(method_callOnSensorEvent, 1, argv, argt); - } - } - break; - } - - case BLE_EVENT: - { - if (msg->payload) { - app_instance_process_ble_msg(msg->payload); - bh_free(msg->payload); - } - break; - } - - case GPIO_INTERRUPT_EVENT: - { - AEEGPIOChannel gpio_ch = (AEEGPIOChannel)msg->payload; - - if ((gpio_ch == NULL) || (gpio_ch->callback == 0) || (gpio_ch->listener == NULL)) { - break; - } - argv[0] = (unsigned int) gpio_ch; - argt[0] = 1; - bool ret_value = app_manager_call_java(method_callOnGPIOInterrupt, 1, argv, argt); - - if (!ret_value) { - app_manager_printf("app_manager_call_java method_method_callOnGPIOInterrupt return false\n"); - } - break; - } - - default: - { - app_manager_printf("Invalid message type of applet queue message.\n"); - break; - } - } - - bh_free(msg); - } - - static JeffClassHeaderLinked* - find_main_class(JeffFileHeaderLinked *main_file) - { - JeffClassHeaderLinked *c = NULL, *ci; - unsigned int i; - - for (i = 0; i < main_file->internal_class_count; i++) { - ci = main_file->class_header[i]; - - if (jeff_is_super_class(class_AEEApplet, ci) - && (ci->access_flag & JEFF_ACC_PUBLIC)) { - if (c) { - jeff_printe_more_than_one_main_class(); - return NULL; - } - - c = ci; - } - } - - if (!c) - jeff_printe_no_main_class(); - - return c; - } - - /* Java applet thread main routine */ - static void* - app_instance_main(void *arg) - { - module_data *m_data = (module_data *)arg; - jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; - JeffClassHeaderLinked *object_class; - JeffMethodLinked *m; - unsigned int argv[1]; - uint8 argt[1]; - - app_manager_printf("Java Applet '%s' started\n", m_data->module_name); - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - if (applet_data->debug_mode) - jeff_tool_suspend_self(); -#endif - - applet_data->vm_instance->applet_object = applet_data->applet_obj; - object_class = jeff_object_class_pointer(applet_data->applet_obj); - m = jeff_select_method_virtual(object_class, method_AEEApplet_onInit); - bh_assert(m != NULL); - /* Initialize applet class which call */ - if (!app_manager_initialize_class(object_class)) { - app_manager_printf("Call fail\n"); - goto fail; - } - - /* Initialize applet object which call */ - if (!app_manager_initialize_object(applet_data->applet_obj)) { - app_manager_printf("Call fail\n"); - goto fail; - } - - /* Call applet's onInit() method */ - argv[0] = (unsigned int)applet_data->applet_obj; - argt[0] = 1; - if (app_manager_call_java(m, 1, argv, argt)) - /* Enter queue loop run to receive and process applet queue message */ - bh_queue_enter_loop_run(m_data->queue, app_instance_queue_callback); - - fail: - applet_data->vm_instance->applet_object = applet_data->applet_obj; - object_class = jeff_object_class_pointer(applet_data->applet_obj); - m = jeff_select_method_virtual(object_class, method_AEEApplet_onDestroy); - bh_assert(m != NULL); - /* Call User Applet or AEEApplet onDestroy() method */ - app_manager_call_java(m, 1, argv, argt); - if (m != method_AEEApplet_onDestroy) { - /*If 'm' is user onDestroy, then Call AEEApplet.onDestroy() method*/ - app_manager_call_java(method_AEEApplet_onDestroy, 1, argv, argt); - } - app_manager_printf("Applet instance main thread exit.\n"); - return NULL; - } - - static bool - verify_signature(JeffFileHeader *file, unsigned size) - { - uint8 *sig; - unsigned sig_size; - -#if BEIHAI_ENABLE_NO_SIGNATURE != 0 - /* no signature */ - if (file->file_signature == 0) - return true; -#endif - - if (file->file_length != size -#if BEIHAI_ENABLE_NO_SIGNATURE == 0 - || file->file_signature == 0 -#endif - || file->file_signature >= file->file_length) - return false; - - sig = (uint8 *)file + file->file_signature; - sig_size = file->file_length - file->file_signature; - - if (0 == app_manager_signature_verify((uint8_t *)file, file->file_signature, - sig, sig_size)) - return false; - - return true; - } - - /* Install Java Applet */ - static bool - jeff_module_install(bh_request_msg_t *msg) - { - unsigned int size, bpk_file_len, main_file_len, heap_size, timeout; - uint8 *bpk_file; - JeffFileHeaderLinked *main_file; - JeffClassHeaderLinked *main_class; - module_data *m_data; - jeff_applet_data *applet_data; - char *applet_name, *applet_perm; - attr_container_t *attr_cont; - bool debug = false; - - /* Check url */ - if (!msg->url - || strcmp(msg->url, "/applet") != 0) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: invalid url."); - return false; - } - - /* Check payload */ - attr_cont = (attr_container_t *)msg->payload; - if (!attr_cont - || !(bpk_file = (uint8 *) - attr_container_get_as_bytearray(attr_cont, "bpk", &bpk_file_len))) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: invalid bpk file."); - return false; - } - - /* Check applet name */ - applet_name = attr_container_get_as_string(attr_cont, "name"); - - if (!applet_name || strlen(applet_name) == 0) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: invalid applet name."); - return false; - } - - if (app_manager_lookup_module_data(applet_name)) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: applet already installed."); - return false; - } - - /* TODO: convert bpk file to Jeff file */ - main_file_len = bpk_file_len; - main_file = bh_malloc(main_file_len); - if (!main_file) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: allocate memory failed."); - return false; - } - bh_memcpy_s(main_file, main_file_len, bpk_file, main_file_len); - - /* Verify signature */ - if (!verify_signature((JeffFileHeader *)main_file, main_file_len)) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: verify Jeff file signature failed."); - goto fail1; - } - - /* Load Jeff main file */ - if (!jeff_runtime_load(main_file, main_file_len, false, NULL, NULL)) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: load Jeff file failed."); - goto fail1; - } - - /* Find main class */ - main_class = find_main_class(main_file); - if (!main_class) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: find applet class failed."); - goto fail2; - } - - /* Create module data */ - size = offsetof(module_data, module_name) + strlen(applet_name) + 1; - size = align_uint(size, 4); - m_data = bh_malloc(size + sizeof(jeff_applet_data)); - if (!m_data) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: allocate memory failed."); - goto fail2; - } - - memset(m_data, 0, size + sizeof(jeff_applet_data)); - m_data->module_type = Module_Jeff; - m_data->internal_data = (uint8*)m_data + size; - applet_data = (jeff_applet_data*)m_data->internal_data; - bh_strcpy_s(m_data->module_name, strlen(applet_name) + 1, applet_name); - applet_data->main_file = main_file; - - /* Set applet execution timeout */ - timeout = DEFAULT_APPLET_TIMEOUT; - if (attr_container_contain_key(attr_cont, "execution timeout")) - timeout = attr_container_get_as_int(attr_cont, "execution timeout"); - m_data->timeout = timeout; - - /* Create applet permissions */ - applet_perm = attr_container_get_as_string(attr_cont, "perm"); - if (applet_perm != NULL) { - applet_data->perms = bh_malloc(strlen(applet_perm) + 1); - if (!applet_data->perms) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: allocate memory for applet permissions failed."); - goto fail3; - } - - bh_strcpy_s(applet_data->perms, strlen(applet_perm) + 1, applet_perm); - } - - /* Create applet queue */ - m_data->queue = bh_queue_create(); - if (!m_data->queue) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create applet queue failed."); - goto fail3_1; - } - - /* Set heap size */ - heap_size = DEFAULT_APPLET_HEAP_SIZE; - if (attr_container_contain_key(attr_cont, "heap size")) { - heap_size = attr_container_get_as_int(attr_cont, "heap size"); - if (heap_size < MIN_APPLET_HEAP_SIZE) - heap_size = MIN_APPLET_HEAP_SIZE; - else if (heap_size > MAX_APPLET_HEAP_SIZE) - heap_size = MAX_APPLET_HEAP_SIZE; - } - - m_data->heap_size = heap_size; - - /* Create applet heap */ - m_data->heap = gc_init_for_instance(heap_size); - if (!m_data->heap) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create heap failed."); - goto fail4; - } - - /* Create applet object */ - applet_data->applet_obj = jeff_object_new(m_data->heap, main_class); - if (!applet_data->applet_obj) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create applet object failed."); - goto fail5; - } - - /* Initialize watchdog timer */ - if (!watchdog_timer_init(m_data)) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create applet watchdog timer failed."); - goto fail5; - } - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - /* Check whether applet is debuggable */ - if (attr_container_contain_key(attr_cont, "debug")) - debug = attr_container_get_as_bool(attr_cont, "debug"); - - applet_data->debug_mode = debug; - - /* Create tool agent queue */ - if (debug && !(applet_data->tool_agent_queue = bh_queue_create())) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create tool agent queue failed."); - goto fail5_1; - } -#endif - - /* Create applet instance */ - applet_data->vm_instance = - jeff_runtime_create_instance(main_file, m_data->heap, 16, - app_instance_main, m_data, - NULL); - if (!applet_data->vm_instance) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create Java VM failed"); - goto fail6; - } - - /* Add applet data to applet data list */ - applet_data->vm_instance->applet_object = applet_data->applet_obj; - app_manager_add_module_data(m_data); - app_manager_post_applets_update_event(); - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - /* Start tool agent thread */ - if (debug && !jeff_tool_start_agent(applet_data->vm_instance, applet_data->tool_agent_queue)) { - SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: start tool agent failed"); - goto fail6; - } -#endif - - app_manager_printf("Install Applet success!\n"); - app_send_response_to_host(msg->mid, CREATED_2_01, NULL); /* CREATED */ - return true; - - fail6: -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - if (debug) - bh_queue_destroy(applet_data->tool_agent_queue); -#endif - - fail5_1: - watchdog_timer_destroy(&m_data->wd_timer); - - fail5: - gc_destroy_for_instance(m_data->heap); - - fail4: - bh_queue_destroy(m_data->queue, NULL); - - fail3_1: - bh_free(applet_data->perms); - - fail3: - bh_free(applet_data); - - fail2: - jeff_runtime_unload(main_file); - - fail1: - bh_free(main_file); - - return false; - } - - static void - cleanup_applet_resource(module_data *m_data) - { - jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; - - /* Unload Jeff main file and free it */ - jeff_runtime_unload(applet_data->main_file); - bh_free(applet_data->main_file); - - /* Destroy queue */ - bh_queue_destroy(m_data->queue, app_instance_queue_free_callback); - - /* Destroy heap */ - gc_destroy_for_instance(m_data->heap); - - /* Destroy watchdog timer */ - watchdog_timer_destroy(&m_data->wd_timer); - - /* Remove module data from module data list and free it */ - app_manager_del_module_data(m_data); - bh_free(applet_data->perms); - bh_free(m_data); - } - - /* Uninstall Java Applet */ - static bool - jeff_module_uninstall(bh_request_msg_t *msg) - { - module_data *m_data; - jeff_applet_data *applet_data; - attr_container_t *attr_cont; - char *applet_name; - bool do_not_reply = false; - - /* Check payload and applet name*/ - attr_cont = (attr_container_t *)msg->payload; - - /* Check whether need to reply this request */ - if (attr_container_contain_key(attr_cont, "do not reply me")) - do_not_reply = attr_container_get_as_bool(attr_cont, "do not reply me"); - - /* Check url */ - if (!msg->url - || strcmp(msg->url, "/applet") != 0) { - if (!do_not_reply) - SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: invalid url."); - else - app_manager_printf("Uninstall Applet failed: invalid url."); - return false; - } - - if (!attr_cont - || !(applet_name = attr_container_get_as_string(attr_cont, "name")) - || strlen(applet_name) == 0) { - if (!do_not_reply) - SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: invalid applet name."); - else - app_manager_printf("Uninstall Applet failed: invalid applet name."); - return false; - } - - m_data = app_manager_lookup_module_data(applet_name); - if (!m_data) { - if (!do_not_reply) - SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: no applet found."); - else - app_manager_printf("Uninstall Applet failed: no applet found."); - return false; - } - - if (m_data->module_type != Module_Jeff) { - if (!do_not_reply) - SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: invlaid module type."); - else - app_manager_printf("Uninstall Applet failed: invalid module type."); - return false; - } - - if (m_data->wd_timer.is_interrupting) { - if (!do_not_reply) - SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: applet is being interrupted by watchdog."); - else - app_manager_printf("Uninstall Applet failed: applet is being interrupted by watchdog."); - return false; - } - - /* Exit applet queue loop run */ - bh_queue_exit_loop_run(m_data->queue); - - applet_data = (jeff_applet_data*)m_data->internal_data; -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - /* Exit tool agent queue loop run */ - if (is_tool_agent_running(m_data)) { - bh_queue_exit_loop_run(applet_data->tool_agent_queue); - } -#endif - - /* Wait the end of the applet instance and then destroy it */ - if (applet_data->vm_instance->main_file) - jeff_runtime_wait_for_instance(applet_data->vm_instance, -1); - jeff_runtime_destroy_instance(applet_data->vm_instance); - - cleanup_applet_resource(m_data); - app_manager_post_applets_update_event(); - - app_manager_printf("Uninstall Applet success!\n"); - - if (!do_not_reply) - app_send_response_to_host(msg->mid, DELETED_2_02, NULL); /* DELETED */ - return true; - } - -#define PERM_PREFIX "AEE.permission." - - static bool - check_permission_format(const char *perm) - { - const char *prefix = PERM_PREFIX; - const char *p; - - if (perm == NULL || strncmp(perm, prefix, strlen(prefix)) != 0 - || *(p = perm + strlen(prefix)) == '\0') - return false; - - do { - if (!(*p == '.' || ('A' <= *p && *p <= 'Z') || ('a' <= *p && *p <= 'z'))) - return false; - }while (*++p != '\0'); - - return true; - } - - static bool - match(const char *haystack, const char *needle, char delim) - { - const char *p = needle; - - if (haystack == NULL || *haystack == '\0' - || needle == NULL || *needle == '\0') - return false; - - while (true) { - while (true) { - if ((*haystack == '\0' || *haystack == delim) && *p == '\0') { - return true; - } else if (*p == *haystack) { - ++p; - ++haystack; - } else { - break; - } - } - while (*haystack != '\0' && *haystack != delim) { - ++haystack; - } - if (*haystack == '\0') { - return false; - } else { - ++haystack; - p = needle; - } - } - } - - bool - bh_applet_check_permission(const char *perm) - { - return check_permission_format(perm) - && match(app_manager_get_jeff_applet_data()->perms, - perm + strlen(PERM_PREFIX), ' '); - } - - static bool - jeff_module_init() - { - JeffDescriptorFull d[] = { {JEFF_TYPE_VOID, 0, NULL}}; - JeffDescriptorFull d1[] = { - { JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, NULL}, - { JEFF_TYPE_VOID, 0, NULL} - }; - - /* Resolve class com.intel.aee.AEEApplet */ - class_AEEApplet = jeff_runtime_resolve_class_full_name("com.intel.aee.AEEApplet"); - if (!class_AEEApplet) { - app_manager_printf("App Manager start failed: resolve class AEEApplet failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.Request */ - class_AEERequest = jeff_runtime_resolve_class_full_name("com.intel.aee.Request"); - if (!class_AEERequest) { - app_manager_printf("App Manager start failed: resolve class Request failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.Timer */ - class_Timer = jeff_runtime_resolve_class_full_name("com.intel.aee.Timer"); - if (!class_Timer) { - app_manager_printf("App Manager start failed: resolve class Timer failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.Sensor */ - class_Sensor = jeff_runtime_resolve_class_full_name("com.intel.aee.Sensor"); - if (!class_Sensor) { - app_manager_printf("App Manager start failed: resolve class Sensor failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.ble.BLEManager */ - class_BLEManager = jeff_runtime_resolve_class_full_name( - "com.intel.aee.ble.BLEManager"); - if (!class_BLEManager) { - app_manager_printf( - "App Manager start failed: resolve class BLEManager failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.ble.BLEDevice */ - class_BLEDevice = jeff_runtime_resolve_class_full_name( - "com.intel.aee.ble.BLEDevice"); - if (!class_BLEDevice) { - app_manager_printf( - "App Manager start failed: resolve class BLEDevice failed.\n"); - return false; - } - /* Resolve class com.intel.aee.ble.BLEDevice */ - class_BLEGattService = jeff_runtime_resolve_class_full_name( - "com.intel.aee.ble.BLEGattService"); - if (!class_BLEGattService) { - app_manager_printf( - "App Manager start failed: resolve class BLEGattService failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.ble.BLEDevice */ - class_BLEGattCharacteristic = jeff_runtime_resolve_class_full_name( - "com.intel.aee.ble.BLEGattCharacteristic"); - if (!class_BLEGattCharacteristic) { - app_manager_printf( - "App Manager start failed: resolve class BLEGattCharacteristic failed.\n"); - return false; - } - - /* Resolve class com.intel.aee.ble.BLEDevice */ - class_BLEGattDescriptor = jeff_runtime_resolve_class_full_name( - "com.intel.aee.ble.BLEGattDescriptor"); - if (!class_BLEGattDescriptor) { - app_manager_printf( - "App Manager start failed: resolve class BLEGattDescriptor failed.\n"); - return false; - } - /* Resolve class com.intel.aee.gpio.GPIOChannel */ - class_GPIOChannel = jeff_runtime_resolve_class_full_name( - "com.intel.aee.gpio.GPIOChannel"); - if (!class_GPIOChannel) { - app_manager_printf( - "App Manager start failed: resolve class GPIOChannel failed.\n"); - return false; - } - - /* Resolve method com.intel.aee.AEEApplet.onInit() */ - method_AEEApplet_onInit = jeff_lookup_method(class_AEEApplet, "onInit", 0, d); - if (!method_AEEApplet_onInit) { - app_manager_printf("App Manager start failed: resolve method Applet.onInit() failed.\n"); - return false; - } - - /* Resolve method com.intel.aee.AEEApplet.onDestroy() */ - method_AEEApplet_onDestroy = jeff_lookup_method(class_AEEApplet, "onDestroy", 0, d); - if (!method_AEEApplet_onDestroy) { - app_manager_printf("App Manager start failed: resolve method AEEApplet.onDestroy() failed.\n"); - return false; - } - - /* Resolve method com.intel.aee.AEEApplet.callOnRequest(Request) */ - d1[0].class_header = class_AEERequest; - method_AEEApplet_callOnRequest = jeff_lookup_method(class_AEEApplet, "callOnRequest", 1, d1); - if (!method_AEEApplet_callOnRequest) { - app_manager_printf("App Manager start failed: resolve method AEEApplet.callOnRequest() failed.\n"); - return false; - } - - /* Resolve method com.intel.aee.Timer.callOnTimer() */ - method_callOnTimer = jeff_lookup_method(class_Timer, "callOnTimer", 0, d); - if (!method_callOnTimer) { - app_manager_printf("App Manager start failed: resolve method Timer.callOnTimer() failed.\n"); - return false; - } - - /* Resolve method com.intel.aee.Sensor.callOnSensorEvent() */ - method_callOnSensorEvent = jeff_lookup_method(class_Sensor, "callOnSensorEvent", 0, d); - if (!method_callOnSensorEvent) { - app_manager_printf("App Manager start failed: resolve method Sensor.callOnSensorEvent() failed.\n"); - return false; - } - - /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEStartDiscovery(BLEDevice) */ - d1[0].class_header = class_BLEDevice; - method_callOnBLEStartDiscovery = jeff_lookup_method(class_BLEManager, "callOnBLEStartDiscovery", 1, d1); - if (!method_callOnBLEStartDiscovery) { - app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEStartDiscovery() failed.\n"); - return false; - } - - /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice) */ - JeffDescriptorFull d2_1[] = { {JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, class_BLEDevice}, - { JEFF_TYPE_INT, 0, NULL}, - { JEFF_TYPE_VOID, 0, NULL}}; - method_callOnBLEConnected = jeff_lookup_method(class_BLEManager, "callOnBLEConnected", 2, d2_1); - if (!method_callOnBLEConnected) { - app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEConnected() failed.\n"); - return false; - } - - /* Resovle method com.intel.aee.ble.BLEManager.method_callOnBLENotification(BLEDevice,byte[]) */ - JeffDescriptorFull d2_2[] = { {JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, class_BLEDevice}, - { JEFF_TYPE_BYTE | JEFF_TYPE_REF | JEFF_TYPE_MONO, 1, NULL}, - { JEFF_TYPE_INT, 0, NULL}, - { JEFF_TYPE_INT, 0, NULL}, - { JEFF_TYPE_VOID, 0, NULL}}; - method_callOnBLENotification = jeff_lookup_method(class_BLEManager, "callOnBLENotification", 4, d2_2); - if (!method_callOnBLENotification) { - app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLENotification() failed.\n"); - return false; - } - - /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice,byte[]) */ - method_callOnBLEIndication = jeff_lookup_method(class_BLEManager, "callOnBLEIndication", 4, d2_2); - if (!method_callOnBLEIndication) { - app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEIndication() failed.\n"); - return false; - } - - /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice) */ - d1[0].class_header = class_BLEDevice; - method_callOnBLEDisconnected = jeff_lookup_method(class_BLEManager, "callOnBLEDisconnected", 1, d1); - if (!method_callOnBLEDisconnected) { - app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEDisconnected() failed.\n"); - return false; - } - - /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice) */ - method_callOnBLEPasskeyEntry = jeff_lookup_method(class_BLEManager, "callOnBLEPasskeyEntry", 1, d1); - if (!method_callOnBLEPasskeyEntry) { - app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEPasskeyEntry() failed.\n"); - return false; - } - /* Resovle method void com.intel.aee.gpio.GPIOChannel.callOnGPIOInterrupt() */ - method_callOnGPIOInterrupt = jeff_lookup_method(class_GPIOChannel, "callOnGPIOInterrupt", 0, d); - if (!method_callOnGPIOInterrupt) { - app_manager_printf("App Manager start failed: resolve method GPIOChannel.callOnGPIOInterrupt() failed.\n"); - return false; - } - - JeffDescriptorFull d2[] = { {JEFF_TYPE_BYTE | JEFF_TYPE_REF | JEFF_TYPE_MONO, 1, NULL}, - { JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, class_BLEDevice}}; - /* Resovle method com.intel.aee.ble.BLEManager.getBLEDevice(byte []) */ - method_callOnBLEManagerGetBLEDevice = jeff_lookup_method(class_BLEManager, - "getBLEDevice", 1, d2); - if (!method_callOnBLEManagerGetBLEDevice) { - app_manager_printf( - "App Manager start failed: resolve method BLEManager.getBLEDevice() failed.\n"); - return false; - } - - return true; - } - - static void - jeff_module_watchdog_kill(module_data *m_data) - { - jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; - - app_manager_printf("Watchdog interrupt the applet %s\n", m_data->module_name); - - jeff_runtime_interrupt_instance(applet_data->vm_instance, true); - - /* Exit applet queue loop run */ - bh_queue_exit_loop_run(m_data->queue); - - /* Wait the end of the applet instance. If timeout, it means applet - * is busy executing native code, then try to cancle the main thread. */ - if (applet_data->vm_instance->main_file) - jeff_runtime_wait_for_instance(applet_data->vm_instance, 3000); - - if (applet_data->vm_instance->main_file) { - app_manager_printf("Watchdog cancel applet main thread.\n"); - vm_thread_cancel(applet_data->vm_instance->main_tlr.handle); - /* k_thread_abort(applet_data->vm_instance->main_tlr.handle); */ - } - - send_exception_event_to_host(m_data->module_name, "java.lang.InterruptedException"); - cleanup_applet_resource(m_data); - app_manager_printf("Watchdog interrupt Jeff applet done.\n"); - } - - static bool - jeff_module_handle_host_url(void *queue_msg) - { -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - bh_queue_msg_t *msg = (bh_queue_msg_t *)queue_msg; - - if (msg->message_type == COAP_PARSED) { - coap_packet_t *packet = (coap_packet_t *)msg->payload; - attr_container_t *attr_cont = (attr_container_t *)packet->payload; - const char *url = NULL; - int url_len = 0, mid; - - bh_memcpy_s(&mid, sizeof(uint32), packet->token, sizeof(uint32)); - url_len = coap_get_header_uri_path(packet, &url); - - /* Send request to tool agent */ - if (url_len >= 12 && memcmp(url, "/tool_agent/", 12) == 0) { - module_data *m_data; - jeff_applet_data *applet_data; - unsigned attr_cont_len = 0, req_msg_len; - bh_queue_msg_t *tool_agent_msg; - bh_request_msg_t *req_msg; - char url_buf[256] = {0}, *p = url_buf; - char applet_name[128] = {0}; - - /* Resolve applet name */ - bh_memcpy_s(url_buf, sizeof(url_buf), url + 12, url_len - 12); - while (*p != '/' && *p != '\0') - p++; - - bh_memcpy_s(applet_name, sizeof(applet_name), url_buf, p - url_buf); - app_manager_printf("Send request to tool agent of applet: %s\n", applet_name); - - /* Check applet name */ - if (!(m_data = app_manager_lookup_module_data(applet_name))) { - SEND_ERR_RESPONSE(mid, "Send request to tool agent failed: invalid applet name"); - return false; - } - - applet_data = (jeff_applet_data*)m_data->internal_data; - /* Attach debug: start the tool agent firstly */ - if (packet->code == COAP_PUT) { - if (is_tool_agent_running(m_data)) { - SEND_ERR_RESPONSE(mid, "Attach debug failed: tool agent is already exist."); - return false; - } - - applet_data->debug_mode = true; - - /* Create tool agent queue */ - if (!(applet_data->tool_agent_queue = bh_queue_create())) { - SEND_ERR_RESPONSE(mid, "Attach debug failed: create tool agent queue failed."); - return false; - } - - /* Start tool agent thread */ - if (!jeff_tool_start_agent(applet_data->vm_instance, applet_data->tool_agent_queue)) { - bh_queue_destroy(applet_data->tool_agent_queue, NULL); - SEND_ERR_RESPONSE(mid, "Attach debug failed: start tool agent failed"); - return false; - } - - app_manager_printf("Attach debug: start tool agent of applet %s success.\n", applet_name); - app_send_response_to_host(mid, CREATED_2_01, NULL); /* OK */ - } else { - /* Check tool agent running */ - if (!is_tool_agent_running(m_data)) { - SEND_ERR_RESPONSE(mid, "Send request to tool agent failed: tool agent is not running"); - return false; - } - - /* Create queue message for tool agent */ - if (!(tool_agent_msg = bh_malloc(sizeof(bh_queue_msg_t)))) { - SEND_ERR_RESPONSE(mid, "Send request to tool agent failed: allocate memory failed"); - return false; - } - - if (attr_cont) - attr_cont_len = attr_container_get_serialize_length(attr_cont); - - req_msg_len = sizeof(bh_request_msg_t) + strlen(p) + 1 + attr_cont_len; - - /* Create request message */ - if (!(req_msg = bh_malloc(req_msg_len))) { - SEND_ERR_RESPONSE(mid, "Send request to applet failed: allocate memory failed"); - bh_free(tool_agent_msg); - return false; - } - - /* Set request message */ - memset(req_msg, 0, req_msg_len); - req_msg->mid = mid; - req_msg->url = (char*)req_msg + sizeof(bh_request_msg_t); - bh_strcpy_s(req_msg->url, strlen(p)+1, p); /* Actual url sent to tool agent */ - req_msg->action = packet->code; - req_msg->fmt = 0; - if (attr_cont) { - req_msg->payload = (char*)req_msg + sizeof(bh_request_msg_t) + - strlen(p) + 1; - attr_container_serialize(req_msg->payload, attr_cont); - } - - /* Set queue message and send to tool agent's queue */ - tool_agent_msg->message_type = JDWP_REQUEST; - tool_agent_msg->payload_size = req_msg_len; - tool_agent_msg->payload = (char*)req_msg; - if (!bh_queue_send_message(applet_data->tool_agent_queue, tool_agent_msg)) { - bh_free(req_msg); - bh_free(tool_agent_msg); - SEND_ERR_RESPONSE - (mid, "Send request to tool agent failed: send queue msg failed."); - return false; - } - - /* app_manager_printf("Send request to tool agent of applet %s success.\n", applet_name); */ - } - - return true; - } - } -#endif /* BEIHAI_ENABLE_TOOL_AGENT != 0 */ - return false; - } - - static module_data* - jeff_module_get_module_data(void) - { - JeffThreadLocalRoot *self = jeff_runtime_get_tlr(); - return (module_data *)self->il_root->start_routine_arg; - } - -#if BEIHAI_ENABLE_TOOL_AGENT != 0 - -#define JDWP_HANDSHAKE_MAGIC "JDWP-Handshake" -#define JDWP_HANDSHAKE_LEN (sizeof (JDWP_HANDSHAKE_MAGIC) - 1) - -#define JDWP_PAYLOAD_KEY "jdwp" - - static bool debug = true; - - static bool - send_msg_to_host (int mid, const char *url, int code, const uint8 *msg, unsigned size) - { - bool ret; - int payload_len = 0; - attr_container_t *payload = NULL; - - if (msg) { - if ((payload = attr_container_create(""))) { - attr_container_set_bytearray(&payload, JDWP_PAYLOAD_KEY, (const int8_t *)msg, size); - payload_len = attr_container_get_serialize_length(payload); - } - } - ret = app_send_msg_to_host(mid, url, code, (char*)payload, payload_len); - - if (payload) - attr_container_destroy(payload); - - return ret; - } - - static bool - send_response(int mid, int code, const uint8 *msg, unsigned size) - { - return send_msg_to_host(mid, NULL, code, msg, size); - } - - static bool - send_packet_response(int mid, int code, JeffBuffer *packet) - { - int size; - - if ((size = jeff_buffer_size(packet)) == 0) - /* No data need to be written, succeed. */ - return true; - - return send_msg_to_host(mid, NULL, code, jeff_buffer_at(packet, 0), size); - } - - void - jeff_tool_event_publish(uint8 *evtbuf, unsigned size) - { - char *prefix = "/jdwp/", *url = NULL; - int url_len; - - url_len = strlen(prefix) + strlen(app_manager_get_module_name(Module_Jeff)); - if (NULL == (url = jeff_runtime_malloc(url_len + 1))) - return; - - bh_strcpy_s(url,url_len + 1, prefix); - bh_strcat_s(url,url_len + 1, app_manager_get_module_name(Module_Jeff)); - - /* Event is sent as request so we set code as COAP_PUT */ - if (event_is_registered(url)) - send_msg_to_host(0, url, COAP_PUT, evtbuf, size); - - jeff_runtime_free(url); - } - -#define SEND_ERROR_RESPONSE(err_msg) do { \ - app_manager_printf("%s\n", err_msg); \ - send_response(req_msg->mid, INTERNAL_SERVER_ERROR_5_00,\ - (uint8 *)err_msg, strlen(err_msg) + 1); \ - } while (0) - - /* Queue callback of tool agent */ - void - tool_agent_queue_callback(void *arg) - { - bh_queue_msg_t *msg = (bh_queue_msg_t*)arg; - - if (msg->message_type == JDWP_REQUEST) { - bh_request_msg_t *req_msg = (bh_request_msg_t*)msg->payload; - attr_container_t *attr_cont = (attr_container_t*)req_msg->payload; - JeffThreadLocalRoot *self = jeff_runtime_get_tlr(); - JeffInstanceLocalRoot *cur_instance = self->il_root; - JeffToolAgent *agent = cur_instance->tool_agent; - bh_queue *queue = (bh_queue *)self->start_routine_arg; - - if (debug) - app_manager_printf("Tool Agent of applet %s got request, url %s, action %d\n", - app_manager_get_module_name(Module_Jeff), req_msg->url, req_msg->action); - - /* Handshake or Process Request */ - if (req_msg->action == COAP_GET) { - uint8 *buf; - unsigned buf_len; - - if (!attr_cont - || !(buf = (uint8*) - attr_container_get_as_bytearray(attr_cont, JDWP_PAYLOAD_KEY, &buf_len))) { - SEND_ERROR_RESPONSE("Tool Agent fail: invalid JDWP payload."); - goto fail; - } - - if (!agent->connected) { - if (buf_len != JDWP_HANDSHAKE_LEN - || memcmp (buf, JDWP_HANDSHAKE_MAGIC, JDWP_HANDSHAKE_LEN)) { - SEND_ERROR_RESPONSE("Tool Agent fail: handshake fail."); - goto fail; - } - - /* Handshake success and response */ - agent->connected = true; - send_response(req_msg->mid, CONTENT_2_05, buf, buf_len); - } else { - /* TODO: tool-agent thread should reuse the request/reply buffer to avoid allocating memory repeatedly */ - JeffBuffer request, reply; - - /* Initialize the package buffers. */ - jeff_buffer_init(&request); - jeff_buffer_init(&reply); - - if (!jeff_buffer_resize(&request, buf_len)) { - SEND_ERROR_RESPONSE("Tool Agent fail: resize buffer fail."); - jeff_buffer_destroy(&request); - jeff_buffer_destroy(&reply); - goto fail; - } - - /* Copy data from request to jeff buffer */ - bh_memcpy_s(jeff_buffer_at(&request, 0), jeff_buffer_size(&request), buf, buf_len); - - /* Handle JDWP request */ - if (!jeff_tool_handle_packet(agent, &request, &reply)) { - SEND_ERROR_RESPONSE("Tool agent fail: handle request fail."); - jeff_buffer_destroy(&request); - jeff_buffer_destroy(&reply); - goto fail; - } - - /* Response JDWP reply */ - send_packet_response(req_msg->mid, CONTENT_2_05, &reply); - - /* Destroy the package buffers. */ - jeff_buffer_destroy(&request); - jeff_buffer_destroy(&reply); - } - } - /* Debugger disconnect */ - else if (req_msg->action == COAP_DELETE) { - send_response(req_msg->mid, DELETED_2_02, NULL, 0); - bh_queue_exit_loop_run(queue); - } - else { - SEND_ERROR_RESPONSE("Tool agent fail: invalid request."); - goto fail; - } - - bh_free(req_msg); - bh_free(msg); - return; - - fail: - bh_queue_exit_loop_run(queue); - bh_free(req_msg); - } - - bh_free(msg); - } - - void - tool_agent_queue_free_callback(void *message) - { - bh_queue_msg_t *msg = (bh_queue_msg_t*)message; - - if (msg->message_type == JDWP_REQUEST) { - bh_request_msg_t *req_msg = (bh_request_msg_t*)msg->payload; - bh_free(req_msg); - } - - bh_free(msg); - } - -#endif /* BEIHAI_ENABLE_TOOL_AGENT != 0 */ - - module_interface jeff_module_interface = { - jeff_module_init, - jeff_module_install, - jeff_module_uninstall, - jeff_module_watchdog_kill, - jeff_module_handle_host_url, - jeff_module_get_module_data, - NULL - }; - -#endif diff --git a/core/app-mgr/app-manager/module_jeff.h b/core/app-mgr/app-manager/module_jeff.h deleted file mode 100644 index bb39f27e45..0000000000 --- a/core/app-mgr/app-manager/module_jeff.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _MODULE_JEFF_H_ -#define _MODULE_JEFF_H_ - -#include "app_manager.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern module_interface jeff_module_interface; - -/* sensor event */ -typedef struct bh_sensor_event_t { - /* Java sensor object */ - void *sensor; - /* event of attribute container from context core */ - void *event; -} bh_sensor_event_t; - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _MODULE_JEFF_H_ */ diff --git a/core/app-mgr/app-manager/module_utils.c b/core/app-mgr/app-manager/module_utils.c deleted file mode 100644 index a4e52cb45f..0000000000 --- a/core/app-mgr/app-manager/module_utils.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "app_manager.h" -#include "app_manager_host.h" -#include "bh_queue.h" -#include "bh_memory.h" -#include "bh_thread.h" -#include "bi-inc/attr_container.h" -#include "event.h" -#include "watchdog.h" -#include "coap_ext.h" - -/* Lock of the module data list */ -korp_mutex module_data_list_lock; - -/* Module data list */ -module_data *module_data_list; - -bool module_data_list_init() -{ - module_data_list = NULL; - return !vm_mutex_init(&module_data_list_lock) ? true : false; -} - -void module_data_list_destroy() -{ - - vm_mutex_lock(&module_data_list_lock); - if (module_data_list) { - while (module_data_list) { - module_data *p = module_data_list->next; - bh_free(module_data_list); - module_data_list = p; - } - } - vm_mutex_unlock(&module_data_list_lock); - vm_mutex_destroy(&module_data_list_lock); -} - -static void module_data_list_add(module_data *m_data) -{ - static uint32 module_id_max = 1; - vm_mutex_lock(&module_data_list_lock); - // reserve some special ID - // TODO: check the new id is not already occupied! - if (module_id_max == 0xFFFFFFF0) - module_id_max = 1; - m_data->id = module_id_max++; - if (!module_data_list) { - module_data_list = m_data; - } else { - /* Set as head */ - m_data->next = module_data_list; - module_data_list = m_data; - } - vm_mutex_unlock(&module_data_list_lock); -} - -void module_data_list_remove(module_data *m_data) -{ - vm_mutex_lock(&module_data_list_lock); - if (module_data_list) { - if (module_data_list == m_data) - module_data_list = module_data_list->next; - else { - /* Search and remove it */ - module_data *p = module_data_list; - - while (p && p->next != m_data) - p = p->next; - if (p && p->next == m_data) - p->next = p->next->next; - } - } - vm_mutex_unlock(&module_data_list_lock); -} - -module_data* -module_data_list_lookup(const char *module_name) -{ - vm_mutex_lock(&module_data_list_lock); - if (module_data_list) { - module_data *p = module_data_list; - - while (p) { - /* Search by module name */ - if (!strcmp(module_name, p->module_name)) { - vm_mutex_unlock(&module_data_list_lock); - return p; - } - p = p->next; - } - } - vm_mutex_unlock(&module_data_list_lock); - return NULL; -} - -module_data* -module_data_list_lookup_id(unsigned int module_id) -{ - vm_mutex_lock(&module_data_list_lock); - if (module_data_list) { - module_data *p = module_data_list; - - while (p) { - /* Search by module name */ - if (module_id == p->id) { - vm_mutex_unlock(&module_data_list_lock); - return p; - } - p = p->next; - } - } - vm_mutex_unlock(&module_data_list_lock); - return NULL; -} - -module_data * -app_manager_get_module_data(uint32 module_type, void *module_inst) -{ - if (module_type < Module_Max - && g_module_interfaces[module_type] - && g_module_interfaces[module_type]->module_get_module_data) - return g_module_interfaces[module_type]->module_get_module_data(module_inst); - return NULL; -} - -void* -app_manager_get_module_queue(uint32 module_type, void *module_inst) -{ - module_data *m_data = app_manager_get_module_data(module_type, module_inst); - return m_data ? m_data->queue : NULL; -} - -const char* -app_manager_get_module_name(uint32 module_type, void *module_inst) -{ - module_data *m_data = app_manager_get_module_data(module_type, module_inst); - return m_data ? m_data->module_name : NULL; -} - -unsigned int app_manager_get_module_id(uint32 module_type, void *module_inst) -{ - module_data *m_data = app_manager_get_module_data(module_type, module_inst); - return m_data ? m_data->id : ID_NONE; -} - -void* -app_manager_get_module_heap(uint32 module_type, void *module_inst) -{ - module_data *m_data = app_manager_get_module_data(module_type, module_inst); - return m_data ? m_data->heap : NULL; -} - -module_data* -app_manager_lookup_module_data(const char *name) -{ - return module_data_list_lookup(name); -} - -void app_manager_add_module_data(module_data *m_data) -{ - module_data_list_add(m_data); -} - -void app_manager_del_module_data(module_data *m_data) -{ - module_data_list_remove(m_data); - - release_module(m_data); -} - -bool app_manager_is_interrupting_module(uint32 module_type, void *module_inst) -{ - module_data *m_data = app_manager_get_module_data(module_type, module_inst); - return m_data ? m_data->wd_timer.is_interrupting : false; -} - -extern void destroy_module_timer_ctx(unsigned int module_id); - -void release_module(module_data *m_data) -{ - watchdog_timer_destroy(&m_data->wd_timer); - -#ifdef HEAP_ENABLED /* TODO */ - if(m_data->heap) - gc_destroy_for_instance(m_data->heap); -#endif - - if (m_data->queue) - bh_queue_destroy(m_data->queue); - - m_data->timer_ctx = NULL; - - destroy_module_timer_ctx(m_data->id); - - bh_free(m_data); -} - -int check_modules_timer_expiry() -{ - vm_mutex_lock(&module_data_list_lock); - module_data *p = module_data_list; - int ms_to_expiry = -1; - - while (p) { - - int next = get_expiry_ms(p->timer_ctx); - if (next != -1) { - if (ms_to_expiry == -1 || ms_to_expiry > next) - ms_to_expiry = next; - } - - p = p->next; - } - vm_mutex_unlock(&module_data_list_lock); - return ms_to_expiry; -} - diff --git a/core/app-mgr/app-manager/module_wasm_app.c b/core/app-mgr/app-manager/module_wasm_app.c deleted file mode 100644 index a5cd6b4d9c..0000000000 --- a/core/app-mgr/app-manager/module_wasm_app.c +++ /dev/null @@ -1,1630 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "module_wasm_app.h" - -#include "native_interface.h" /* for request_t type */ -#include "app_manager_host.h" -#include "bh_queue.h" -#include "bi-inc/attr_container.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "coap_ext.h" -#include "event.h" -#include "watchdog.h" -#include "runtime_lib.h" -#if WASM_ENABLE_AOT != 0 -#include "aot_export.h" -#endif - -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 -/* Wasm bytecode file 4 version bytes */ -static uint8 wasm_bytecode_version[] = { - (uint8) 0x01, - (uint8) 0x00, - (uint8) 0x00, - (uint8) 0x00 -}; -#endif - -#if WASM_ENABLE_AOT != 0 -/* Wasm aot file 4 version bytes */ -static uint8 wasm_aot_version[] = { - (uint8) 0x01, - (uint8) 0x00, - (uint8) 0x00, - (uint8) 0x00 -}; - -static union { - int a; - char b; -} __ue = { .a = 1 }; - -#define is_little_endian() (__ue.b == 1) -#endif - -/* Wasm App Install Request Receiving Phase */ -typedef enum wasm_app_install_req_recv_phase_t { - Phase_Req_Ver, - Phase_Req_Action, - Phase_Req_Fmt, - Phase_Req_Mid, - Phase_Req_Sender, - Phase_Req_Url_Len, - Phase_Req_Payload_Len, /* payload is wasm app binary */ - Phase_Req_Url, - - /* Magic phase */ - Phase_App_Magic, - -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - /* Phases of wasm bytecode file */ - Phase_Wasm_Version, - Phase_Wasm_Section_Type, - Phase_Wasm_Section_Size, - Phase_Wasm_Section_Content, -#endif - -#if WASM_ENABLE_AOT != 0 - /* Phases of wasm AOT file */ - Phase_AOT_Version, - Phase_AOT_Section_ID, - Phase_AOT_Section_Size, - Phase_AOT_Section_Content -#endif -} wasm_app_install_req_recv_phase_t; - -/* Message for insall wasm app */ -typedef struct install_wasm_app_msg_t { - uint8 request_version; - uint8 request_action; - uint16 request_fmt; - uint32 request_mid; - uint32 request_sender; - uint16 request_url_len; - uint32 wasm_app_size; /* payload size is just wasm app binary size */ - char *request_url; - wasm_app_file_t app_file; - int app_file_magic; -} install_wasm_app_msg_t; - -/* Wasm App Install Request Receive Context */ -typedef struct wasm_app_install_req_recv_ctx_t { - wasm_app_install_req_recv_phase_t phase; - int size_in_phase; - install_wasm_app_msg_t message; - int total_received_size; -} wasm_app_install_req_recv_ctx_t; - -/* Current wasm app install request receive context */ -static wasm_app_install_req_recv_ctx_t recv_ctx; - -static bool -wasm_app_module_init(void); - -static bool -wasm_app_module_install(request_t *msg); - -static bool -wasm_app_module_uninstall(request_t *msg); - -static void -wasm_app_module_watchdog_kill(module_data *module_data); - -static bool -wasm_app_module_handle_host_url(void *queue_msg); - -static module_data * -wasm_app_module_get_module_data(void *inst); - -static bool -wasm_app_module_on_install_request_byte_arrive(uint8 ch, int request_total_size, - int *received_size); - -static bool -module_wasm_app_handle_install_msg(install_wasm_app_msg_t *message); - -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 -static void -destroy_all_wasm_sections(wasm_section_list_t sections); - -static void -destroy_part_wasm_sections(wasm_section_list_t *p_sections, - uint8 *section_types, - int section_cnt); -#endif - -#if WASM_ENABLE_AOT != 0 -static void -destroy_all_aot_sections(aot_section_list_t sections); - -static void -destroy_part_aot_sections(aot_section_list_t *p_sections, - uint8 *section_types, - int section_cnt); -#endif - -#define Max_Msg_Callback 10 -int g_msg_type[Max_Msg_Callback] = { 0 }; -message_type_handler_t g_msg_callbacks[Max_Msg_Callback] = { 0 }; - -#define Max_Cleanup_Callback 10 -static resource_cleanup_handler_t -g_cleanup_callbacks[Max_Cleanup_Callback] = { 0 }; - -module_interface wasm_app_module_interface = { - wasm_app_module_init, - wasm_app_module_install, - wasm_app_module_uninstall, - wasm_app_module_watchdog_kill, - wasm_app_module_handle_host_url, - wasm_app_module_get_module_data, - wasm_app_module_on_install_request_byte_arrive -}; - -static unsigned -align_uint(unsigned v, unsigned b) -{ - unsigned m = b - 1; - return (v + m) & ~m; -} - -#if WASM_ENABLE_AOT != 0 -static void -exchange_uint32(uint8 *p_data) -{ - uint8 value = *p_data; - *p_data = *(p_data + 3); - *(p_data + 3) = value; - - value = *(p_data + 1); - *(p_data + 1) = *(p_data + 2); - *(p_data + 2) = value; -} -#endif - -static wasm_function_inst_t -app_manager_lookup_function(const wasm_module_inst_t module_inst, - const char *name, const char *signature) -{ - wasm_function_inst_t func; - - func = wasm_runtime_lookup_function(module_inst, name, signature); - if (!func && name[0] == '_') - func = wasm_runtime_lookup_function(module_inst, name + 1, signature); - return func; -} - - -static void -app_instance_queue_callback(void *queue_msg, void *arg) -{ - uint32 argv[2]; - wasm_function_inst_t func_onRequest, func_onTimer; - - wasm_module_inst_t inst = (wasm_module_inst_t)arg; - module_data *m_data = app_manager_get_module_data(Module_WASM_App, inst); - wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; - int message_type = bh_message_type(queue_msg); - - bh_assert(m_data); - - if (message_type < BASE_EVENT_MAX) { - switch (message_type) { - case RESTFUL_REQUEST: { - request_t *request = (request_t *)bh_message_payload(queue_msg); - int size; - char *buffer; - int32 buffer_offset; - - app_manager_printf("App %s got request, url %s, action %d\n", - m_data->module_name, - request->url, - request->action); - - func_onRequest = app_manager_lookup_function(inst, - "_on_request", - "(i32i32)"); - if (!func_onRequest) { - app_manager_printf("Cannot find function onRequest\n"); - break; - } - - buffer = pack_request(request, &size); - if (buffer == NULL) - break; - - buffer_offset = wasm_runtime_module_dup_data(inst, buffer, size); - if (buffer_offset == 0) { - const char *exception = wasm_runtime_get_exception(inst); - if (exception) { - app_manager_printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - } - free_req_resp_packet(buffer); - break; - } - - free_req_resp_packet(buffer); - - argv[0] = (uint32) buffer_offset; - argv[1] = (uint32) size; - - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onRequest, - 2, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - app_manager_printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - wasm_runtime_module_free(inst, buffer_offset); - break; - } - - wasm_runtime_module_free(inst, buffer_offset); - app_manager_printf("Wasm app process request success.\n"); - break; - } - case RESTFUL_RESPONSE: { - wasm_function_inst_t func_onResponse; - response_t *response = (response_t *) bh_message_payload(queue_msg); - int size; - char *buffer; - int32 buffer_offset; - - app_manager_printf("App %s got response_t,status %d\n", - m_data->module_name, response->status); - - func_onResponse = - app_manager_lookup_function(inst, "_on_response", "(i32i32)"); - if (!func_onResponse) { - app_manager_printf("Cannot find function on_response\n"); - break; - } - - buffer = pack_response(response, &size); - if (buffer == NULL) - break; - - buffer_offset = wasm_runtime_module_dup_data(inst, buffer, size); - if (buffer_offset == 0) { - const char *exception = wasm_runtime_get_exception(inst); - if (exception) { - app_manager_printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - } - free_req_resp_packet(buffer); - break; - } - - free_req_resp_packet(buffer); - - argv[0] = (uint32) buffer_offset; - argv[1] = (uint32) size; - - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onResponse, - 2, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - app_manager_printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - wasm_runtime_module_free(inst, buffer_offset); - break; - } - - wasm_runtime_module_free(inst, buffer_offset); - app_manager_printf("Wasm app process response success.\n"); - break; - } - default: { - for (int i = 0; i < Max_Msg_Callback; i++) { - if (g_msg_type[i] == message_type) { - g_msg_callbacks[i](m_data, queue_msg); - return; - } - } - app_manager_printf("Invalid message type of WASM app queue message.\n"); - break; - - } - } - } - else { - switch (message_type) { - case TIMER_EVENT_WASM: { - unsigned int timer_id; - if (bh_message_payload(queue_msg)) { - /* Call Timer.callOnTimer() method */ - func_onTimer = - app_manager_lookup_function(inst, - "_on_timer_callback", - "(i32)"); - - if (!func_onTimer) { - app_manager_printf("Cannot find function _on_timer_callback\n"); - break; - } - timer_id = - (unsigned int)(uintptr_t)bh_message_payload(queue_msg); - argv[0] = timer_id; - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onTimer, - 1, argv)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - app_manager_printf("Got exception running wasm code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - } - } - break; - } - default: { - for (int i = 0; i < Max_Msg_Callback; i++) { - if (g_msg_type[i] == message_type) { - g_msg_callbacks[i](m_data, queue_msg); - return; - } - } - app_manager_printf("Invalid message type of WASM app queue message.\n"); - break; - } - - } - } -} - -#if WASM_ENABLE_LIBC_WASI != 0 -static bool -wasm_app_prepare_wasi_dir(wasm_module_t module, const char *module_name, - char *wasi_dir_buf, uint32 buf_size) -{ - const char *wasi_root = wasm_get_wasi_root_dir(); - char *p = wasi_dir_buf; - uint32 module_name_len = strlen(module_name); - uint32 wasi_root_len = strlen(wasi_root); - uint32 total_size; - struct stat st = { 0 }; - - bh_assert(wasi_root); - - /* wasi_dir: wasi_root/module_name */ - total_size = wasi_root_len + 1 + module_name_len + 1; - if (total_size > buf_size) - return false; - memcpy(p, wasi_root, wasi_root_len); - p += wasi_root_len; - *p++ = '/'; - memcpy(p, module_name, module_name_len); - p += module_name_len; - *p++ = '\0'; - - /* Create a wasi dir for the module */ - if (stat(wasi_dir_buf, &st) == 0) { - /* exist, but is a regular file, not a dir */ - if (st.st_mode & S_IFREG) - return false; - } - else { - /* not exist, create it */ - if (mkdir(wasi_dir_buf, 0777) != 0) - return false; - } - - return true; -} -#endif - -/* WASM app thread main routine */ -static void* -wasm_app_routine(void *arg) -{ - wasm_function_inst_t func_onInit; - wasm_function_inst_t func_onDestroy; - - module_data *m_data = (module_data *) arg; - wasm_data *wasm_app_data = (wasm_data*) m_data->internal_data; - wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; - korp_tid thread = wasm_app_data->thread_id; - - /* Set m_data to the VM managed instance's custom data */ - wasm_runtime_set_custom_data(inst, m_data); - - app_manager_printf("WASM app '%s' started\n", m_data->module_name); - -#if WASM_ENABLE_LIBC_WASI != 0 - if (wasm_runtime_is_wasi_mode(inst)) { - wasm_function_inst_t func_start; - /* In wasi mode, we should call function named "_start" - which initializes the wasi envrionment. The "_start" function - will call "main" function */ - if ((func_start = wasm_runtime_lookup_wasi_start_function(inst))) { - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_start, - 0, NULL)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - printf("Got exception running wasi start function: %s\n", - exception); - wasm_runtime_clear_exception(inst); - goto fail1; - } - } - /* if no start function is found, we execute - the _on_init function as normal */ - } -#endif - - /* Call app's onInit() method */ - func_onInit = app_manager_lookup_function(inst, "_on_init", "()"); - if (!func_onInit) { - app_manager_printf("Cannot find function on_init().\n"); - goto fail1; - } - - if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onInit, - 0, NULL)) { - const char *exception = wasm_runtime_get_exception(inst); - bh_assert(exception); - printf("Got exception running WASM code: %s\n", - exception); - wasm_runtime_clear_exception(inst); - /* call on_destroy() in case some resources are opened in on_init() - * and then exception thrown */ - goto fail2; - } - - /* Enter queue loop run to receive and process applet queue message */ - bh_queue_enter_loop_run(m_data->queue, app_instance_queue_callback, inst); - - app_manager_printf("App instance main thread exit.\n"); - -fail2: - /* Call WASM app onDestroy() method if there is */ - func_onDestroy = app_manager_lookup_function(inst, "_on_destroy", "()"); - if (func_onDestroy) - wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onDestroy, 0, NULL); - -fail1: - vm_thread_detach(thread); - vm_thread_exit(NULL); - - return NULL; -} - -static void -cleanup_app_resource(module_data *m_data) -{ - int i; - wasm_data *wasm_app_data = (wasm_data*) m_data->internal_data; - bool is_bytecode = wasm_app_data->is_bytecode; - - am_cleanup_registeration(m_data->id); - - am_unregister_event(NULL, m_data->id); - - for (i = 0; i < Max_Cleanup_Callback; i++) { - if (g_cleanup_callbacks[i] != NULL) - g_cleanup_callbacks[i](m_data->id); - else - break; - } - - wasm_runtime_deinstantiate(wasm_app_data->wasm_module_inst); - - /* Destroy remain sections (i.e. data segment section for bytecode file - * or text section of aot file) from app file's section list. */ - if (is_bytecode) -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - destroy_all_wasm_sections((wasm_section_list_t)(wasm_app_data->sections)); -#else - bh_assert(0); -#endif - else -#if WASM_ENABLE_AOT != 0 - destroy_all_aot_sections((aot_section_list_t)(wasm_app_data->sections)); -#else - bh_assert(0); -#endif - - if (wasm_app_data->wasm_module) - wasm_runtime_unload(wasm_app_data->wasm_module); - - if (wasm_app_data->exec_env) - wasm_runtime_destroy_exec_env(wasm_app_data->exec_env); - - /* Destroy watchdog timer */ - watchdog_timer_destroy(&m_data->wd_timer); - - /* Remove module data from module data list and free it */ - app_manager_del_module_data(m_data); -} - -/************************************************************/ -/* Module specific functions implementation */ -/************************************************************/ - -static bool -wasm_app_module_init(void) -{ - /* Initialize WASM VM*/ - if (!wasm_runtime_init()) { - app_manager_printf("WASM runtime environment initialization failed.\n"); - return false; - } - - return true; -} - -#define APP_NAME_MAX_LEN 128 -#define MAX_INT_STR_LEN 11 - -static bool -wasm_app_module_install(request_t * msg) -{ - unsigned int m_data_size, heap_size; - unsigned int timeout, timers, err_size; - char *properties; - int properties_offset; - wasm_app_file_t *wasm_app_file; - wasm_data *wasm_app_data; - package_type_t package_type; - module_data *m_data; - wasm_module_t module = NULL; - wasm_module_inst_t inst = NULL; - wasm_exec_env_t exec_env = NULL; - char m_name[APP_NAME_MAX_LEN] = { 0 }; - char timeout_str[MAX_INT_STR_LEN] = { 0 }; - char heap_size_str[MAX_INT_STR_LEN] = { 0 }; - char timers_str[MAX_INT_STR_LEN] = { 0 }, err[256]; -#if WASM_ENABLE_LIBC_WASI != 0 - char wasi_dir_buf[PATH_MAX] = { 0 }; - const char *wasi_dir_list[] = { wasi_dir_buf }; -#endif - - err_size = sizeof(err); - - /* Check payload */ - if (!msg->payload || msg->payload_len == 0) { - SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: invalid wasm file."); - return false; - } - - /* Check app name */ - properties_offset = check_url_start(msg->url, strlen(msg->url), "/applet"); - bh_assert(properties_offset > 0); - if (properties_offset <= 0) - return false; - properties = msg->url + properties_offset; - find_key_value(properties, strlen(properties), "name", m_name, - sizeof(m_name) - 1, '&'); - - if (strlen(m_name) == 0) { - SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: invalid app name."); - return false; - } - - if (app_manager_lookup_module_data(m_name)) { - SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: app already installed."); - return false; - } - - /* Parse heap size */ - heap_size = APP_HEAP_SIZE_DEFAULT; - find_key_value(properties, strlen(properties), "heap", heap_size_str, - sizeof(heap_size_str) - 1, '&'); - if (strlen(heap_size_str) > 0) { - heap_size = atoi(heap_size_str); - if (heap_size < APP_HEAP_SIZE_MIN) - heap_size = APP_HEAP_SIZE_MIN; - else if (heap_size > APP_HEAP_SIZE_MAX) - heap_size = APP_HEAP_SIZE_MAX; - } - - /* Judge the app type is AOTed or not */ - package_type = get_package_type((uint8 *) msg->payload, msg->payload_len); - - /* Load WASM file and instantiate*/ - switch (package_type) { -#if WASM_ENABLE_AOT != 0 - case Wasm_Module_AoT: - { - wasm_aot_file_t *aot_file; - /* Sections to be released after loading */ - uint8 sections1[] = { - AOT_SECTION_TYPE_TARGET_INFO, - AOT_SECTION_TYPE_INIT_DATA, - AOT_SECTION_TYPE_FUNCTION, - AOT_SECTION_TYPE_EXPORT, - AOT_SECTION_TYPE_RELOCATION, - AOT_SECTION_TYPE_SIGANATURE - }; - - wasm_app_file = (wasm_app_file_t *) msg->payload; - bh_assert(wasm_app_file); - aot_file = &wasm_app_file->u.aot; - - /* Load AOT module from sections */ - module = wasm_runtime_load_from_sections(aot_file->sections, true, - err, err_size); - if (!module) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: load WASM file failed."); - printf("error: %s\n", err); - destroy_all_aot_sections(aot_file->sections); - return false; - } - - /* Destroy useless sections from list after load */ - destroy_part_aot_sections(&aot_file->sections, - sections1, - sizeof(sections1) / sizeof(uint8)); - -#if WASM_ENABLE_LIBC_WASI != 0 - if (!wasm_app_prepare_wasi_dir(module, m_name, - wasi_dir_buf, sizeof(wasi_dir_buf))) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: prepare wasi env failed."); - wasm_runtime_unload(module); - destroy_all_aot_sections(aot_file->sections); - return false; - } - wasm_runtime_set_wasi_args(module, - wasi_dir_list, 1, - NULL, 0, - NULL, 0, - NULL, 0); -#endif - - /* Instantiate the AOT module */ - inst = wasm_runtime_instantiate(module, 0, heap_size, err, err_size); - if (!inst) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: instantiate wasm runtime failed."); - printf("error: %s\n", err); - wasm_runtime_unload(module); - destroy_all_aot_sections(aot_file->sections); - return false; - } - break; - } -#endif /* endof WASM_ENABLE_AOT != 0 */ - -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - case Wasm_Module_Bytecode: - { - wasm_bytecode_file_t *bytecode_file; - /* Sections to be released after loading */ - uint8 sections1[] = { - SECTION_TYPE_USER, - SECTION_TYPE_TYPE, - SECTION_TYPE_IMPORT, - SECTION_TYPE_FUNC, - SECTION_TYPE_TABLE, - SECTION_TYPE_MEMORY, - SECTION_TYPE_GLOBAL, - SECTION_TYPE_EXPORT, - SECTION_TYPE_START, - SECTION_TYPE_ELEM - }; - /* Sections to be released after instantiating */ - uint8 sections2[] = { SECTION_TYPE_DATA }; - - wasm_app_file = (wasm_app_file_t *) msg->payload; - bh_assert(wasm_app_file); - bytecode_file = &wasm_app_file->u.bytecode; - - /* Load wasm module from sections */ - module = wasm_runtime_load_from_sections(bytecode_file->sections, false, - err, err_size); - if (!module) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: load WASM file failed."); - printf("error: %s\n", err); - destroy_all_wasm_sections(bytecode_file->sections); - return false; - } - - /* Destroy useless sections from list after load */ - destroy_part_wasm_sections(&bytecode_file->sections, - sections1, - sizeof(sections1) / sizeof(uint8)); - -#if WASM_ENABLE_LIBC_WASI != 0 - if (!wasm_app_prepare_wasi_dir(module, m_name, - wasi_dir_buf, sizeof(wasi_dir_buf))) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: prepare wasi env failed."); - wasm_runtime_unload(module); - destroy_all_wasm_sections(bytecode_file->sections); - return false; - } - wasm_runtime_set_wasi_args(module, - wasi_dir_list, 1, - NULL, 0, - NULL, 0, - NULL, 0); -#endif - - /* Instantiate the wasm module */ - inst = wasm_runtime_instantiate(module, 0, heap_size, err, err_size); - if (!inst) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: instantiate wasm runtime failed."); - printf("error: %s\n", err); - wasm_runtime_unload(module); - destroy_all_wasm_sections(bytecode_file->sections); - return false; - } - - /* Destroy useless sections from list after instantiate */ - destroy_part_wasm_sections(&bytecode_file->sections, - sections2, - sizeof(sections2) / sizeof(uint8)); - break; - } -#endif /* endof WASM_ENALBE_INTERP != 0 || WASM_ENABLE_JIT != 0 */ - default: - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: invalid wasm package type."); - return false; - } - - /* Create module data including the wasm_app_data as its internal_data*/ - m_data_size = offsetof(module_data, module_name) + strlen(m_name) + 1; - m_data_size = align_uint(m_data_size, 4); - m_data = bh_malloc(m_data_size + sizeof(wasm_data)); - if (!m_data) { - SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: allocate memory failed."); - goto fail; - } - memset(m_data, 0, m_data_size + sizeof(wasm_data)); - - m_data->module_type = Module_WASM_App; - m_data->internal_data = (uint8*) m_data + m_data_size; - wasm_app_data = (wasm_data*) m_data->internal_data; - wasm_app_data->wasm_module_inst = inst; - wasm_app_data->wasm_module = module; - wasm_app_data->m_data = m_data; - if (package_type == Wasm_Module_Bytecode) { - wasm_app_data->is_bytecode = true; - wasm_app_data->sections = wasm_app_file->u.bytecode.sections; - } - else { - wasm_app_data->is_bytecode = false; - wasm_app_data->sections = wasm_app_file->u.aot.sections; - } - - if (!(wasm_app_data->exec_env = exec_env = - wasm_runtime_create_exec_env(inst, DEFAULT_WASM_STACK_SIZE))) { - SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: create exec env failed."); - goto fail; - } - - /* Set module data - name and module type */ - bh_strcpy_s(m_data->module_name, strlen(m_name) + 1, m_name); - - /* Set module data - execution timeout */ - timeout = DEFAULT_WATCHDOG_INTERVAL; - find_key_value(properties, strlen(properties), "wd", timeout_str, - sizeof(timeout_str) - 1, '&'); - if (strlen(timeout_str) > 0) - timeout = atoi(timeout_str); - m_data->timeout = timeout; - - /* Set module data - create queue */ - m_data->queue = bh_queue_create(); - if (!m_data->queue) { - SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: create app queue failed."); - goto fail; - } - - /* Set heap size */ - m_data->heap_size = heap_size; - - /* Set module data - timers number */ - timers = DEFAULT_TIMERS_PER_APP; - find_key_value(properties, strlen(properties), "timers", timers_str, - sizeof(timers_str) - 1, '&'); - if (strlen(timers_str) > 0) { - timers = atoi(timers_str); - if (timers > MAX_TIMERS_PER_APP) - timers = MAX_TIMERS_PER_APP; - } - - /* Attention: must add the module before start the thread! */ - app_manager_add_module_data(m_data); - - m_data->timer_ctx = create_wasm_timer_ctx(m_data->id, timers); - if (!m_data->timer_ctx) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: create app timers failed."); - goto fail; - } - - /* Initialize watchdog timer */ - if (!watchdog_timer_init(m_data)) { - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: create app watchdog timer failed."); - goto fail; - } - - /* Create WASM app thread. */ - if (vm_thread_create(&wasm_app_data->thread_id, wasm_app_routine, - (void*) m_data, APP_THREAD_STACK_SIZE_DEFAULT) != 0) { - module_data_list_remove(m_data); - SEND_ERR_RESPONSE(msg->mid, - "Install WASM app failed: create app threadf failed."); - goto fail; - } - - /* only when thread is created it is the flag of installation success */ - app_manager_post_applets_update_event(); - - app_manager_printf("Install WASM app success!\n"); - send_error_response_to_host(msg->mid, CREATED_2_01, NULL); /* CREATED */ - - return true; - -fail: - if (m_data) - release_module(m_data); - wasm_runtime_deinstantiate(inst); - wasm_runtime_unload(module); - if (exec_env) - wasm_runtime_destroy_exec_env(exec_env); - - switch (package_type) { -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - case Wasm_Module_Bytecode: - destroy_all_wasm_sections(wasm_app_file->u.bytecode.sections); - break; -#endif -#if WASM_ENABLE_AOT != 0 - case Wasm_Module_AoT: - destroy_all_aot_sections(wasm_app_file->u.aot.sections); - break; -#endif - default: - break; - } - - return false; -} - -/* For internal use: if defined to 1, the process will - * exit when wasm app is uninstalled. Hence valgrind can - * print memory leak report. */ -#ifndef VALGRIND_CHECK -#define VALGRIND_CHECK 0 -#endif - -/* Uninstall WASM app */ -static bool -wasm_app_module_uninstall(request_t *msg) -{ - module_data *m_data; - wasm_data *wasm_app_data; - char m_name[APP_NAME_MAX_LEN] = { 0 }; - char *properties; - int properties_offset; - - properties_offset = check_url_start(msg->url, strlen(msg->url), "/applet"); - /* TODO: assert(properties_offset > 0) */ - if (properties_offset <= 0) - return false; - properties = msg->url + properties_offset; - find_key_value(properties, strlen(properties), "name", m_name, - sizeof(m_name) - 1, '&'); - - if (strlen(m_name) == 0) { - SEND_ERR_RESPONSE(msg->mid, "Uninstall WASM app failed: invalid app name."); - return false; - } - - m_data = app_manager_lookup_module_data(m_name); - if (!m_data) { - SEND_ERR_RESPONSE(msg->mid, "Uninstall WASM app failed: no app found."); - return false; - } - - if (m_data->module_type != Module_WASM_App) { - SEND_ERR_RESPONSE(msg->mid, "Uninstall WASM app failed: invalid module type."); - return false; - } - - if (m_data->wd_timer.is_interrupting) { - SEND_ERR_RESPONSE(msg->mid, - "Uninstall WASM app failed: app is being interrupted by watchdog."); - return false; - } - - /* Exit app queue loop run */ - bh_queue_exit_loop_run(m_data->queue); - - /* Wait for wasm app thread to exit */ - wasm_app_data = (wasm_data*) m_data->internal_data; - vm_thread_join(wasm_app_data->thread_id, NULL, -1); - - cleanup_app_resource(m_data); - - app_manager_post_applets_update_event(); - - app_manager_printf("Uninstall WASM app successful!\n"); - -#if VALGRIND_CHECK != 0 - bh_queue_exit_loop_run(get_app_manager_queue()); -#endif - - send_error_response_to_host(msg->mid, DELETED_2_02, NULL); /* DELETED */ - return true; -} - -static bool -wasm_app_module_handle_host_url(void *queue_msg) -{ - //todo: implement in future - app_manager_printf("App handles host url address %d\n", - (int)(uintptr_t)queue_msg); - return false; -} - -static module_data* -wasm_app_module_get_module_data(void *inst) -{ - wasm_module_inst_t module_inst = (wasm_module_inst_t)inst; - return (module_data *)wasm_runtime_get_custom_data(module_inst); -} - -static void -wasm_app_module_watchdog_kill(module_data *m_data) -{ - //todo: implement in future - app_manager_printf("Watchdog kills app: %s\n", m_data->module_name); - return; -} - -bool -wasm_register_msg_callback(int message_type, - message_type_handler_t message_handler) -{ - int i; - int freeslot = -1; - for (i = 0; i < Max_Msg_Callback; i++) { - // replace handler for the same event registered - if (g_msg_type[i] == message_type) - break; - - if (g_msg_callbacks[i] == NULL && freeslot == -1) - freeslot = i; - } - - if (i != Max_Msg_Callback) - g_msg_callbacks[i] = message_handler; - else if (freeslot != -1) { - g_msg_callbacks[freeslot] = message_handler; - g_msg_type[freeslot] = message_type; - } else - return false; - - return true; -} - -bool -wasm_register_cleanup_callback(resource_cleanup_handler_t handler) -{ - int i; - - for (i = 0; i < Max_Cleanup_Callback; i++) { - if (g_cleanup_callbacks[i] == NULL) { - g_cleanup_callbacks[i] = handler; - return true; - } - } - - return false; -} - -#define RECV_INTEGER(value, next_phase) do { \ - uint8 *p = (uint8 *)&value; \ - p[recv_ctx.size_in_phase++] = ch; \ - if (recv_ctx.size_in_phase == sizeof(value)) { \ - if (sizeof(value) == 4) \ - value = ntohl(value); \ - else if (sizeof(value) == 2) \ - value = ntohs(value); \ - recv_ctx.phase = next_phase; \ - recv_ctx.size_in_phase = 0; \ - } \ - } while(0) - -/* return: - * 1: whole wasm app arrived - * 0: one valid byte arrived - * -1: fail to process the byte arrived, e.g. allocate memory fail - */ -static bool -wasm_app_module_on_install_request_byte_arrive(uint8 ch, - int request_total_size, - int *received_size) -{ - uint8 *p; - package_type_t package_type = Package_Type_Unknown; - - if (recv_ctx.phase == Phase_Req_Ver) { - recv_ctx.phase = Phase_Req_Ver; - recv_ctx.size_in_phase = 0; - recv_ctx.total_received_size = 0; - } - - recv_ctx.total_received_size++; - *received_size = recv_ctx.total_received_size; - - if (recv_ctx.phase == Phase_Req_Ver) { - if (ch != 1 /* REQUES_PACKET_VER from restful_utils.c */) - return false; - recv_ctx.phase = Phase_Req_Action; - return true; - } - else if (recv_ctx.phase == Phase_Req_Action) { - recv_ctx.message.request_action = ch; - recv_ctx.phase = Phase_Req_Fmt; - recv_ctx.size_in_phase = 0; - return true; - } - else if (recv_ctx.phase == Phase_Req_Fmt) { - RECV_INTEGER(recv_ctx.message.request_fmt, Phase_Req_Mid); - return true; - } - else if (recv_ctx.phase == Phase_Req_Mid) { - RECV_INTEGER(recv_ctx.message.request_mid, Phase_Req_Sender); - return true; - } - else if (recv_ctx.phase == Phase_Req_Sender) { - RECV_INTEGER(recv_ctx.message.request_sender, Phase_Req_Url_Len); - return true; - } - else if (recv_ctx.phase == Phase_Req_Url_Len) { - p = (uint8*)&recv_ctx.message.request_url_len; - - p[recv_ctx.size_in_phase++] = ch; - if (recv_ctx.size_in_phase == - sizeof(recv_ctx.message.request_url_len)) { - recv_ctx.message.request_url_len = - ntohs(recv_ctx.message.request_url_len); - recv_ctx.message.request_url = - bh_malloc(recv_ctx.message.request_url_len + 1); - if (NULL == recv_ctx.message.request_url) { - app_manager_printf("Allocate memory failed!\n"); - goto fail; - } - memset(recv_ctx.message.request_url, 0, - recv_ctx.message.request_url_len + 1); - recv_ctx.phase = Phase_Req_Payload_Len; - recv_ctx.size_in_phase = 0; - } - return true; - } - else if (recv_ctx.phase == Phase_Req_Payload_Len) { - RECV_INTEGER(recv_ctx.message.wasm_app_size, Phase_Req_Url); - return true; - } - else if (recv_ctx.phase == Phase_Req_Url) { - recv_ctx.message.request_url[recv_ctx.size_in_phase++] = ch; - if (recv_ctx.size_in_phase == recv_ctx.message.request_url_len) { - recv_ctx.phase = Phase_App_Magic; - recv_ctx.size_in_phase = 0; - } - return true; - } - else if (recv_ctx.phase == Phase_App_Magic) { - /* start to receive wasm app magic: bytecode or aot */ - p = (uint8*)&recv_ctx.message.app_file_magic; - - p[recv_ctx.size_in_phase++] = ch; - - if (recv_ctx.size_in_phase == - sizeof(recv_ctx.message.app_file_magic)) { - int magic = recv_ctx.message.app_file_magic; - package_type = get_package_type((uint8 *)&magic, sizeof(magic) + 1); - switch (package_type) { -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - case Wasm_Module_Bytecode: - recv_ctx.message.app_file.u.bytecode.magic = - recv_ctx.message.app_file_magic; - recv_ctx.phase = Phase_Wasm_Version; - recv_ctx.size_in_phase = 0; - break; -#endif -#if WASM_ENABLE_AOT != 0 - case Wasm_Module_AoT: - recv_ctx.message.app_file.u.aot.magic = - recv_ctx.message.app_file_magic; - recv_ctx.phase = Phase_AOT_Version; - recv_ctx.size_in_phase = 0; - break; -#endif - default: - SEND_ERR_RESPONSE(recv_ctx.message.request_mid, - "Install WASM app failed: invalid file format."); - goto fail; - } - } - return true; - } -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - else if (recv_ctx.phase == Phase_Wasm_Version) { - p = (uint8*)&recv_ctx.message.app_file.u.bytecode.version; - - if (ch == wasm_bytecode_version[recv_ctx.size_in_phase]) - p[recv_ctx.size_in_phase++] = ch; - else { - app_manager_printf("Invalid WASM version!\n"); - goto fail; - } - - if (recv_ctx.size_in_phase == - sizeof(recv_ctx.message.app_file.u.bytecode.version)) { - recv_ctx.phase = Phase_Wasm_Section_Type; - recv_ctx.size_in_phase = 0; - } - return true; - } - else if (recv_ctx.phase == Phase_Wasm_Section_Type) { - uint8 section_type = ch; - if (section_type <= SECTION_TYPE_DATA) { - wasm_section_t *new_section; - if (!(new_section = (wasm_section_t *) bh_malloc(sizeof(wasm_section_t)))) { - app_manager_printf("Allocate memory failed!\n"); - goto fail; - } - memset(new_section, 0, sizeof(wasm_section_t)); - new_section->section_type = section_type; - new_section->next = NULL; - - /* add the section to tail of link list */ - if (NULL == recv_ctx.message.app_file.u.bytecode.sections) { - recv_ctx.message.app_file.u.bytecode.sections = new_section; - recv_ctx.message.app_file.u.bytecode.section_end = new_section; - } - else { - recv_ctx.message.app_file.u.bytecode.section_end->next = new_section; - recv_ctx.message.app_file.u.bytecode.section_end = new_section; - } - - recv_ctx.phase = Phase_Wasm_Section_Size; - recv_ctx.size_in_phase = 0; - - return true; - } - else { - app_manager_printf("Invalid wasm section type: %d\n", section_type); - goto fail; - } - } - else if (recv_ctx.phase == Phase_Wasm_Section_Size) { - /* the last section is the current receiving one */ - wasm_section_t *section = recv_ctx.message.app_file.u.bytecode.section_end; - uint32 byte; - - bh_assert(section); - - byte = ch; - - section->section_body_size |= - ((byte & 0x7f) << recv_ctx.size_in_phase * 7); - recv_ctx.size_in_phase++; - /* check leab128 overflow for uint32 value */ - if (recv_ctx.size_in_phase > - (sizeof(section->section_body_size) * 8 + 7 - 1) / 7) { - app_manager_printf(" LEB overflow when parsing section size\n"); - goto fail; - } - - if ((byte & 0x80) == 0) { - /* leb128 encoded section size parsed done */ - if (!(section->section_body = bh_malloc(section->section_body_size))) { - app_manager_printf("Allocate memory failed!\n"); - goto fail; - } - recv_ctx.phase = Phase_Wasm_Section_Content; - recv_ctx.size_in_phase = 0; - } - - return true; - } - else if (recv_ctx.phase == Phase_Wasm_Section_Content) { - /* the last section is the current receiving one */ - wasm_section_t *section = recv_ctx.message.app_file.u.bytecode.section_end; - - bh_assert(section); - - section->section_body[recv_ctx.size_in_phase++] = ch; - - if (recv_ctx.size_in_phase == section->section_body_size) { - if (recv_ctx.total_received_size == request_total_size) { - /* whole wasm app received */ - if (module_wasm_app_handle_install_msg(&recv_ctx.message)) { - bh_free(recv_ctx.message.request_url); - recv_ctx.message.request_url = NULL; - memset(&recv_ctx, 0, sizeof(recv_ctx)); - return true; - } - else { - app_manager_printf("Handle install message failed!\n"); - goto fail; - } - } - else { - recv_ctx.phase = Phase_Wasm_Section_Type; - recv_ctx.size_in_phase = 0; - return true; - } - } - - return true; - } -#endif /* end of WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 */ -#if WASM_ENABLE_AOT != 0 - else if (recv_ctx.phase == Phase_AOT_Version) { - p = (uint8*)&recv_ctx.message.app_file.u.aot.version; - - if (ch == wasm_aot_version[recv_ctx.size_in_phase]) - p[recv_ctx.size_in_phase++] = ch; - else { - app_manager_printf("Invalid WASM AOT version!\n"); - goto fail; - } - - if (recv_ctx.size_in_phase == - sizeof(recv_ctx.message.app_file.u.aot.version)) { - recv_ctx.phase = Phase_AOT_Section_ID; - recv_ctx.size_in_phase = 0; - } - return true; - } - else if (recv_ctx.phase == Phase_AOT_Section_ID) { - aot_section_t *cur_section; - uint32 aot_file_cur_offset = recv_ctx.total_received_size - 1 - - 18 /* Request fixed part */ - - recv_ctx.message.request_url_len; - - if (recv_ctx.size_in_phase == 0) { - /* Skip paddings */ - if (aot_file_cur_offset % 4) - return true; - - if (!(cur_section = (aot_section_t *) bh_malloc(sizeof(aot_section_t)))) { - app_manager_printf("Allocate memory failed!\n"); - goto fail; - } - memset(cur_section, 0, sizeof(aot_section_t)); - - /* add the section to tail of link list */ - if (NULL == recv_ctx.message.app_file.u.aot.sections) { - recv_ctx.message.app_file.u.aot.sections = cur_section; - recv_ctx.message.app_file.u.aot.section_end = cur_section; - } - else { - recv_ctx.message.app_file.u.aot.section_end->next = cur_section; - recv_ctx.message.app_file.u.aot.section_end = cur_section; - } - } else { - cur_section = recv_ctx.message.app_file.u.aot.section_end; - bh_assert(cur_section); - } - - p = (uint8 *)&cur_section->section_type; - p[recv_ctx.size_in_phase++] = ch; - if (recv_ctx.size_in_phase == sizeof(cur_section->section_type)) { - /* Notes: integers are always little endian encoded in AOT file */ - if (!is_little_endian()) - exchange_uint32(p); - if (cur_section->section_type < AOT_SECTION_TYPE_SIGANATURE) { - recv_ctx.phase = Phase_AOT_Section_Size; - recv_ctx.size_in_phase = 0; - } - else { - app_manager_printf("Invalid AOT section id: %d\n", - cur_section->section_type); - goto fail; - } - } - - return true; - } - else if (recv_ctx.phase == Phase_AOT_Section_Size) { - /* the last section is the current receiving one */ - aot_section_t *section = recv_ctx.message.app_file.u.aot.section_end; - bh_assert(section); - - p = (uint8*)§ion->section_body_size; - p[recv_ctx.size_in_phase++] = ch; - if (recv_ctx.size_in_phase == sizeof(section->section_body_size)) { - /* Notes: integers are always little endian encoded in AOT file */ - if (!is_little_endian()) - exchange_uint32(p); - /* Allocate memory for section body */ - if (section->section_body_size > 0) { - if (section->section_type == AOT_SECTION_TYPE_TEXT) { - int map_prot = - MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* aot code and data in x86_64 must be in range 0 to 2G due to - relocation for R_X86_64_32/32S/PC32 */ - int map_flags = MMAP_MAP_32BIT; -#else - int map_flags = MMAP_MAP_NONE; -#endif - uint64 total_size = (uint64)section->section_body_size - + aot_get_plt_table_size(); - total_size = (total_size + 3) & ~((uint64)3); - if (total_size >= UINT32_MAX - || !(section->section_body = - bh_mmap(NULL, (uint32)total_size, - map_prot, map_flags))) { - app_manager_printf("Allocate executable memory failed!\n"); - goto fail; - } -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* address must be in the first 2 Gigabytes of - the process address space */ - bh_assert((uintptr_t)section->section_body < INT32_MAX); -#endif - } - else { - if (!(section->section_body = - bh_malloc(section->section_body_size))) { - app_manager_printf("Allocate memory failed!\n"); - goto fail; - } - } - } - - recv_ctx.phase = Phase_AOT_Section_Content; - recv_ctx.size_in_phase = 0; - } - - return true; - } - else if (recv_ctx.phase == Phase_AOT_Section_Content) { - /* the last section is the current receiving one */ - aot_section_t *section = recv_ctx.message.app_file.u.aot.section_end; - bh_assert(section && section->section_body); - - section->section_body[recv_ctx.size_in_phase++] = ch; - - if (recv_ctx.size_in_phase == section->section_body_size) { - if (section->section_type == AOT_SECTION_TYPE_TEXT) { - uint32 total_size = section->section_body_size - + aot_get_plt_table_size(); - total_size = (total_size + 3) & ~3; - if (total_size > section->section_body_size) { - memset(section->section_body + section->section_body_size, - 0, total_size - section->section_body_size); - section->section_body_size = total_size; - } - } - if (recv_ctx.total_received_size == request_total_size) { - /* whole aot file received */ - if (module_wasm_app_handle_install_msg(&recv_ctx.message)) { - bh_free(recv_ctx.message.request_url); - recv_ctx.message.request_url = NULL; - memset(&recv_ctx, 0, sizeof(recv_ctx)); - return true; - } - else { - app_manager_printf("Handle install message failed!\n"); - goto fail; - } - } - else { - recv_ctx.phase = Phase_AOT_Section_ID; - recv_ctx.size_in_phase = 0; - return true; - } - } - - return true; - } -#endif /* end of WASM_ENABLE_AOT != 0 */ - -fail: - switch (package_type) { -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - case Wasm_Module_Bytecode: - destroy_all_wasm_sections(recv_ctx.message.app_file.u.bytecode.sections); - break; -#endif -#if WASM_ENABLE_AOT != 0 - case Wasm_Module_AoT: - destroy_all_aot_sections(recv_ctx.message.app_file.u.aot.sections); - break; -#endif - default: - break; - } - - if (recv_ctx.message.request_url != NULL) { - bh_free(recv_ctx.message.request_url); - recv_ctx.message.request_url = NULL; - } - - recv_ctx.phase = Phase_Req_Ver; - recv_ctx.size_in_phase = 0; - recv_ctx.total_received_size = 0; - - return false; -} - -static bool -module_wasm_app_handle_install_msg(install_wasm_app_msg_t *message) -{ - request_t *request = NULL; - bh_message_t msg; - - request = (request_t *) bh_malloc(sizeof(request_t)); - if (request == NULL) - return false; - - memset(request, 0, sizeof(*request)); - request->action = message->request_action; - request->fmt = message->request_fmt; - request->url = bh_strdup(message->request_url); - request->sender = ID_HOST; - request->mid = message->request_mid; - request->payload_len = sizeof(message->app_file); - request->payload = bh_malloc(request->payload_len); - - if (request->url == NULL || request->payload == NULL) { - request_cleaner(request); - return false; - } - - /* Request payload is set to wasm_app_file_t struct, - * but not whole app buffer */ - bh_memcpy_s(request->payload, request->payload_len, - &message->app_file, request->payload_len); - - /* Since it's a wasm app install request, so directly post to app-mgr's - * queue. The benefit is that section list can be freed when the msg - * failed to post to app-mgr's queue. The defect is missing url check. */ - if (!(msg = bh_new_msg(RESTFUL_REQUEST, request, sizeof(*request), - request_cleaner))) { - request_cleaner(request); - return false; - } - - if (!bh_post_msg2(get_app_manager_queue(), msg)) - return false; - - return true; -} - -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 -static void -destroy_all_wasm_sections(wasm_section_list_t sections) -{ - wasm_section_t *cur = sections; - while (cur) { - wasm_section_t *next = cur->next; - if (cur->section_body != NULL) - bh_free(cur->section_body); - bh_free(cur); - cur = next; - } -} - -static void -destroy_part_wasm_sections(wasm_section_list_t *p_sections, - uint8 *section_types, - int section_cnt) -{ - int i; - for (i = 0; i < section_cnt; i++) { - uint8 section_type = section_types[i]; - wasm_section_t *cur = *p_sections, *prev = NULL; - - while (cur) { - wasm_section_t *next = cur->next; - if (cur->section_type == section_type) { - if (prev) - prev->next = next; - else - *p_sections = next; - - if (cur->section_body != NULL) - bh_free(cur->section_body); - bh_free(cur); - break; - } - else { - prev = cur; - cur = next; - } - } - } -} -#endif - -#if WASM_ENABLE_AOT != 0 -static void -destroy_all_aot_sections(aot_section_list_t sections) -{ - aot_section_t *cur = sections; - while (cur) { - aot_section_t *next = cur->next; - if (cur->section_body != NULL) { - if (cur->section_type == AOT_SECTION_TYPE_TEXT) - bh_munmap(cur->section_body, cur->section_body_size); - else - bh_free(cur->section_body); - } - bh_free(cur); - cur = next; - } -} - - -static void -destroy_part_aot_sections(aot_section_list_t *p_sections, - uint8 *section_types, - int section_cnt) -{ - int i; - for (i = 0; i < section_cnt; i++) { - uint8 section_type = section_types[i]; - aot_section_t *cur = *p_sections, *prev = NULL; - - while (cur) { - aot_section_t *next = cur->next; - if (cur->section_type == section_type) { - if (prev) - prev->next = next; - else - *p_sections = next; - - if (cur->section_body != NULL) { - if (cur->section_type == AOT_SECTION_TYPE_TEXT) - bh_munmap(cur->section_body, cur->section_body_size); - else - bh_free(cur->section_body); - } - bh_free(cur); - break; - } - else { - prev = cur; - cur = next; - } - } - } -} -#endif - -#if WASM_ENABLE_LIBC_WASI != 0 -static char wasi_root_dir[PATH_MAX] = { '.' }; - -bool -wasm_set_wasi_root_dir(const char *root_dir) -{ - char *path, resolved_path[PATH_MAX]; - - if (!(path = realpath(root_dir, resolved_path))) - return false; - - strncpy(wasi_root_dir, path, sizeof(wasi_root_dir)); - return true; -} - -const char * -wasm_get_wasi_root_dir() -{ - return wasi_root_dir; -} -#endif - diff --git a/core/app-mgr/app-manager/module_wasm_app.h b/core/app-mgr/app-manager/module_wasm_app.h deleted file mode 100644 index 7a967e220d..0000000000 --- a/core/app-mgr/app-manager/module_wasm_app.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _MODULE_WASM_APP_H_ -#define _MODULE_WASM_APP_H_ - -#include "bh_queue.h" -#include "app_manager_export.h" -#include "wasm_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define SECTION_TYPE_USER 0 -#define SECTION_TYPE_TYPE 1 -#define SECTION_TYPE_IMPORT 2 -#define SECTION_TYPE_FUNC 3 -#define SECTION_TYPE_TABLE 4 -#define SECTION_TYPE_MEMORY 5 -#define SECTION_TYPE_GLOBAL 6 -#define SECTION_TYPE_EXPORT 7 -#define SECTION_TYPE_START 8 -#define SECTION_TYPE_ELEM 9 -#define SECTION_TYPE_CODE 10 -#define SECTION_TYPE_DATA 11 - -typedef enum AOTSectionType { - AOT_SECTION_TYPE_TARGET_INFO = 0, - AOT_SECTION_TYPE_INIT_DATA, - AOT_SECTION_TYPE_TEXT, - AOT_SECTION_TYPE_FUNCTION, - AOT_SECTION_TYPE_EXPORT, - AOT_SECTION_TYPE_RELOCATION, - AOT_SECTION_TYPE_SIGANATURE -} AOTSectionType; - -enum { - WASM_Msg_Start = BASE_EVENT_MAX, - TIMER_EVENT_WASM, - SENSOR_EVENT_WASM, - CONNECTION_EVENT_WASM, - WIDGET_EVENT_WASM, - WASM_Msg_End = WASM_Msg_Start + 100 -}; - -typedef struct wasm_data { - /* for easily access the containing wasm module */ - wasm_module_t wasm_module; - wasm_module_inst_t wasm_module_inst; - /* Permissions of the WASM app */ - char *perms; - /* thread list mapped with this WASM module */ - korp_tid thread_id; - /* for easily access the containing module data */ - module_data* m_data; - /* is bytecode or aot */ - bool is_bytecode; - /* sections of wasm bytecode or aot file */ - void *sections; - /* execution environment */ - wasm_exec_env_t exec_env; -} wasm_data; - -/* sensor event */ -typedef struct _sensor_event_data { - uint32 sensor_id; - - int data_fmt; - /* event of attribute container from context core */ - void *data; -} sensor_event_data_t; - -/* WASM Bytecode File */ -typedef struct wasm_bytecode_file { - /* magics */ - int magic; - /* current version */ - int version; - /* WASM section list */ - wasm_section_list_t sections; - /* Last WASM section in the list */ - wasm_section_t *section_end; -} wasm_bytecode_file_t; - -/* WASM AOT File */ -typedef struct wasm_aot_file { - /* magics */ - int magic; - /* current version */ - int version; - /* AOT section list */ - aot_section_list_t sections; - /* Last AOT section in the list */ - aot_section_t *section_end; -} wasm_aot_file_t; - -/* WASM App File */ -typedef struct wasm_app_file_t { - union { - wasm_bytecode_file_t bytecode; - wasm_aot_file_t aot; - } u; -} wasm_app_file_t; - -extern module_interface wasm_app_module_interface; - -typedef void (*message_type_handler_t)(module_data *m_data, bh_message_t msg); -extern bool wasm_register_msg_callback(int msg_type, - message_type_handler_t message_handler); - -typedef void (*resource_cleanup_handler_t)(uint32 module_id); -extern bool wasm_register_cleanup_callback(resource_cleanup_handler_t handler); - -/** - * Set WASI root dir for modules. On each wasm app installation, a sub dir named - * with the app's name will be created autamically. That wasm app can only access - * this sub dir. - * - * @param root_dir the root dir to set - * @return true for success, false otherwise - */ -bool -wasm_set_wasi_root_dir(const char *root_dir); - -/** - * Get WASI root dir - * - * @return the WASI root dir - */ -const char * -wasm_get_wasi_root_dir(); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _MODULE_WASM_APP_H_ */ diff --git a/core/app-mgr/app-manager/module_wasm_lib.c b/core/app-mgr/app-manager/module_wasm_lib.c deleted file mode 100644 index bdf3215c82..0000000000 --- a/core/app-mgr/app-manager/module_wasm_lib.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - - -#include "module_wasm_lib.h" - -static bool wasm_lib_module_init(void) -{ - return false; -} - -static bool wasm_lib_module_install(request_t *msg) -{ - (void) msg; - return false; -} - -static bool wasm_lib_module_uninstall(request_t *msg) -{ - (void) msg; - return false; -} - -static void wasm_lib_module_watchdog_kill(module_data *m_data) -{ - (void) m_data; -} - -static bool wasm_lib_module_handle_host_url(void *queue_msg) -{ - (void) queue_msg; - return false; -} - -static module_data* -wasm_lib_module_get_module_data(void *inst) -{ - (void) inst; - return NULL; -} - -module_interface wasm_lib_module_interface = { wasm_lib_module_init, - wasm_lib_module_install, wasm_lib_module_uninstall, - wasm_lib_module_watchdog_kill, wasm_lib_module_handle_host_url, - wasm_lib_module_get_module_data, - NULL }; - diff --git a/core/app-mgr/app-manager/module_wasm_lib.h b/core/app-mgr/app-manager/module_wasm_lib.h deleted file mode 100644 index 63ffd92b5a..0000000000 --- a/core/app-mgr/app-manager/module_wasm_lib.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _MODULE_WASM_LIB_H_ -#define _MODULE_WASM_LIB_H_ - -#include "app_manager.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern module_interface wasm_lib_module_interface; - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _MODULE_WASM_LIB_H_ */ diff --git a/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c b/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c deleted file mode 100644 index 45957bc183..0000000000 --- a/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "app_manager.h" - -void* -app_manager_timer_create(void (*timer_callback)(void*), - watchdog_timer *wd_timer) -{ - /* TODO */ - return NULL; -} - -void app_manager_timer_destroy(void *timer) -{ - /* TODO */ -} - -void app_manager_timer_start(void *timer, int timeout) -{ - /* TODO */ -} - -void app_manager_timer_stop(void *timer) -{ - /* TODO */ -} - -watchdog_timer * -app_manager_get_wd_timer_from_timer_handle(void *timer) -{ - /* TODO */ - return NULL; -} - -int app_manager_signature_verify(const uint8_t *file, unsigned int file_len, - const uint8_t *signature, unsigned int sig_size) -{ - return 1; -} - diff --git a/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c b/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c deleted file mode 100644 index 67ae69b4f8..0000000000 --- a/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "app_manager.h" -#include "bh_platform.h" -#include "bh_memory.h" -#include -#include -#include -#if 0 -#include -#endif -typedef struct k_timer_watchdog { - struct k_timer timer; - watchdog_timer *wd_timer; -} k_timer_watchdog; - -void* -app_manager_timer_create(void (*timer_callback)(void*), - watchdog_timer *wd_timer) -{ - struct k_timer_watchdog *timer = bh_malloc(sizeof(struct k_timer_watchdog)); - - if (timer) { - k_timer_init(&timer->timer, (void (*)(struct k_timer*)) timer_callback, - NULL); - timer->wd_timer = wd_timer; - } - - return timer; -} - -void app_manager_timer_destroy(void *timer) -{ - bh_free(timer); -} - -void app_manager_timer_start(void *timer, int timeout) -{ - k_timer_start(timer, timeout, 0); -} - -void app_manager_timer_stop(void *timer) -{ - k_timer_stop(timer); -} - -watchdog_timer * -app_manager_get_wd_timer_from_timer_handle(void *timer) -{ - return ((k_timer_watchdog*) timer)->wd_timer; -} -#if 0 -int app_manager_signature_verify(const uint8_t *file, unsigned int file_len, - const uint8_t *signature, unsigned int sig_size) -{ - return signature_verify(file, file_len, signature, sig_size); -} -#endif diff --git a/core/app-mgr/app-manager/resource_reg.c b/core/app-mgr/app-manager/resource_reg.c deleted file mode 100644 index 854357e098..0000000000 --- a/core/app-mgr/app-manager/resource_reg.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - - -#include "native_interface.h" -#include "app_manager.h" -#include "app_manager_export.h" -#include "bi-inc/shared_utils.h" -#include "bi-inc/attr_container.h" -#include "coap_ext.h" - -typedef struct _app_res_register { - struct _app_res_register *next; - char * url; - void (*request_handler)(request_t *, void *); - uint32 register_id; -} app_res_register_t; - -static app_res_register_t * g_resources = NULL; - -void module_request_handler(request_t *request, void *user_data) -{ - unsigned int mod_id = (unsigned int)(uintptr_t)user_data; - bh_message_t msg; - module_data *m_data; - request_t *req; - - /* Check module name */ - m_data = module_data_list_lookup_id(mod_id); - if (!m_data) { - return; - } - - if (m_data->wd_timer.is_interrupting) { - return; - } - - req = clone_request(request); - if (!req) { - return; - } - - /* Set queue message and send to applet's queue */ - msg = bh_new_msg(RESTFUL_REQUEST, req, sizeof(*req), request_cleaner); - if (!msg) { - request_cleaner(req); - return; - } - - if (!bh_post_msg2(m_data->queue, msg)) { - return; - } - - app_manager_printf("Send request to app %s success.\n", - m_data->module_name); -} - -void targeted_app_request_handler(request_t *request, void *unused) -{ - char applet_name[128] = { 0 }; - int offset; - char *url = request->url; - module_data *m_data; - - offset = check_url_start(request->url, strlen(request->url), "/app/"); - - if (offset <= 0) { - return; - } - - strncpy(applet_name, request->url + offset, sizeof(applet_name) - 1); - char *p = strrchr(applet_name, '/'); - if (p) { - *p = 0; - } else - return; - app_manager_printf("Send request to applet: %s\n", applet_name); - - request->url = p + 1; - - /* Check module name */ - m_data = module_data_list_lookup(applet_name); - if (!m_data) { - SEND_ERR_RESPONSE(request->mid, - "Send request to applet failed: invalid applet name"); - goto end; - } - - module_request_handler(request, (void *)(uintptr_t)m_data->id); - end: request->url = url; - -} - -void am_send_response(response_t *response) -{ - module_data *m_data; - - // if the receiver is not any of modules, just forward it to the host - m_data = module_data_list_lookup_id(response->reciever); - if (!m_data) { - send_response_to_host(response); - - } else { - response_t * resp_for_send = clone_response(response); - if (!resp_for_send) { - return; - } - - bh_message_t msg = bh_new_msg(RESTFUL_RESPONSE, resp_for_send, - sizeof(*resp_for_send), response_cleaner); - if (!msg) { - response_cleaner(resp_for_send); - return; - } - - if (!bh_post_msg2(m_data->queue, msg)) { - return; - } - } -} - -void * am_dispatch_request(request_t *request) -{ - app_res_register_t *r = g_resources; - - while (r) { - if (check_url_start(request->url, strlen(request->url), r->url) > 0) { - r->request_handler(request, (void *)(uintptr_t)r->register_id); - return r; - } - r = r->next; - } - return NULL; -} - -bool am_register_resource(const char *url, - void (*request_handler)(request_t *, void *), uint32 register_id) -{ - app_res_register_t * r = g_resources; - int register_num = 0; - - while (r) { - if (strcmp(r->url, url) == 0) { - return false; - } - - if (r->register_id == register_id) - register_num++; - - r = r->next; - } - - if (strlen(url) > RESOUCE_EVENT_URL_LEN_MAX) - return false; - - if (register_num >= RESOURCE_REGISTRATION_NUM_MAX) - return false; - - r = (app_res_register_t *) bh_malloc(sizeof(app_res_register_t)); - if (r == NULL) - return false; - - memset(r, 0, sizeof(*r)); - r->url = bh_strdup(url); - if (r->url == NULL) { - bh_free(r); - return false; - } - - r->request_handler = request_handler; - r->next = g_resources; - r->register_id = register_id; - g_resources = r; - - return true; -} - -void am_cleanup_registeration(uint32 register_id) -{ - app_res_register_t * r = g_resources; - app_res_register_t * prev = NULL; - - while (r) { - app_res_register_t *next = r->next; - - if (register_id == r->register_id) { - if (prev) - prev->next = next; - else - g_resources = next; - - bh_free(r->url); - bh_free(r); - } else - /* if r is freed, should not change prev. Only set prev to r - when r isn't freed. */ - prev = r; - - r = next; - } -} diff --git a/core/app-mgr/app-manager/watchdog.c b/core/app-mgr/app-manager/watchdog.c deleted file mode 100644 index e23939452a..0000000000 --- a/core/app-mgr/app-manager/watchdog.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "watchdog.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "jeff_export.h" - -#define WATCHDOG_THREAD_PRIORITY 5 - -/* Queue of watchdog */ -static bh_queue *watchdog_queue; - -#ifdef WATCHDOG_ENABLED /* TODO */ -static void watchdog_timer_callback(void *timer) -{ - watchdog_timer *wd_timer = app_manager_get_wd_timer_from_timer_handle( - timer); - - watchdog_timer_stop(wd_timer); - - vm_mutex_lock(&wd_timer->lock); - - if (!wd_timer->is_stopped) { - - wd_timer->is_interrupting = true; - - bh_post_msg(watchdog_queue, WD_TIMEOUT, wd_timer->module_data, - sizeof(module_data)); - } - - vm_mutex_unlock(&wd_timer->lock); -} -#endif - -bool watchdog_timer_init(module_data *m_data) -{ -#ifdef WATCHDOG_ENABLED /* TODO */ - watchdog_timer *wd_timer = &m_data->wd_timer; - - if (0 != vm_mutex_init(&wd_timer->lock)) - return false; - - if (!(wd_timer->timer_handle = - app_manager_timer_create(watchdog_timer_callback, wd_timer))) { - vm_mutex_destroy(&wd_timer->lock); - return false; - } - - wd_timer->module_data = m_data; - wd_timer->is_interrupting = false; - wd_timer->is_stopped = false; -#endif - return true; -} - -void watchdog_timer_destroy(watchdog_timer *wd_timer) -{ -#ifdef WATCHDOG_ENABLED /* TODO */ - app_manager_timer_destroy(wd_timer->timer_handle); - vm_mutex_destroy(&wd_timer->lock); -#endif -} - -void watchdog_timer_start(watchdog_timer *wd_timer) -{ - vm_mutex_lock(&wd_timer->lock); - - wd_timer->is_interrupting = false; - wd_timer->is_stopped = false; - app_manager_timer_start(wd_timer->timer_handle, - wd_timer->module_data->timeout); - - vm_mutex_unlock(&wd_timer->lock); -} - -void watchdog_timer_stop(watchdog_timer *wd_timer) -{ - app_manager_timer_stop(wd_timer->timer_handle); -} - -#ifdef WATCHDOG_ENABLED /* TODO */ -static void watchdog_queue_callback(void *queue_msg) -{ - if (bh_message_type(queue_msg) == WD_TIMEOUT) { - module_data *m_data = (module_data *) bh_message_payload(queue_msg); - if (g_module_interfaces[m_data->module_type] - && g_module_interfaces[m_data->module_type]->module_watchdog_kill) { - g_module_interfaces[m_data->module_type]->module_watchdog_kill( - m_data); - app_manager_post_applets_update_event(); - } - } -} -#endif - -#ifdef WATCHDOG_ENABLED /* TODO */ -static void* -watchdog_thread_routine(void *arg) -{ - /* Enter loop run */ - bh_queue_enter_loop_run(watchdog_queue, watchdog_queue_callback); - - (void) arg; - return NULL; -} -#endif - -bool watchdog_startup() -{ - if (!(watchdog_queue = bh_queue_create())) { - app_manager_printf( - "App Manager start failed: create watchdog queue failed.\n"); - return false; - } -#if 0 -//todo: enable watchdog - /* Start watchdog thread */ - if (!jeff_runtime_create_supervisor_thread_with_prio(watchdog_thread_routine, NULL, - WATCHDOG_THREAD_PRIORITY)) { - bh_queue_destroy(watchdog_queue); - return false; - } -#endif - return true; -} diff --git a/core/app-mgr/app-manager/watchdog.h b/core/app-mgr/app-manager/watchdog.h deleted file mode 100644 index a7a508bdb6..0000000000 --- a/core/app-mgr/app-manager/watchdog.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - - -#ifndef _WATCHDOG_H_ -#define _WATCHDOG_H_ - -#include "app_manager.h" - -#ifdef __cplusplus -extern "C" { -#endif - -bool -watchdog_timer_init(module_data *module_data); - -void -watchdog_timer_destroy(watchdog_timer *wd_timer); - -void -watchdog_timer_start(watchdog_timer *wd_timer); - -void -watchdog_timer_stop(watchdog_timer *wd_timer); - -watchdog_timer* -app_manager_get_watchdog_timer(void *timer); - -bool -watchdog_startup(); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif /* _WATCHDOG_H_ */ diff --git a/core/app-mgr/app-mgr-shared/app_manager_export.h b/core/app-mgr/app-mgr-shared/app_manager_export.h deleted file mode 100644 index 877a2df556..0000000000 --- a/core/app-mgr/app-mgr-shared/app_manager_export.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _APP_MANAGER_EXPORT_H_ -#define _APP_MANAGER_EXPORT_H_ - -#include "native_interface.h" -#include "bi-inc/shared_utils.h" -#include "bh_queue.h" -#include "host_link.h" -#include "runtime_timer.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Special module IDs */ -#define ID_HOST -3 -#define ID_APP_MGR -2 -/* Invalid module ID */ -#define ID_NONE (uint32)-1 - -struct attr_container_t; - -/* Queue message type */ -typedef enum QUEUE_MSG_TYPE { - COAP_PARSED = LINK_MSG_TYPE_MAX + 1, - RESTFUL_REQUEST, - RESTFUL_RESPONSE, - TIMER_EVENT = 5, - SENSOR_EVENT = 6, - GPIO_INTERRUPT_EVENT = 7, - BLE_EVENT = 8, - JDWP_REQUEST = 9, - WD_TIMEOUT = 10, - BASE_EVENT_MAX = 100 - -} QUEUE_MSG_TYPE; - -typedef enum { - Module_Jeff, Module_WASM_App, Module_WASM_Lib, Module_Max -} Module_Type; - -struct module_data; - -/* Watchdog timer of module */ -typedef struct watchdog_timer { - /* Timer handle of the platform */ - void *timer_handle; - /* Module of the watchdog timer */ - struct module_data *module_data; - /* Lock of the watchdog timer */ - korp_mutex lock; - /* Flag indicates module is being interrupted by watchdog */ - bool is_interrupting; - /* Flag indicates watchdog timer is stopped */ - bool is_stopped; -} watchdog_timer; - -typedef struct module_data { - struct module_data *next; - - /* ID of the module */ - uint32 id; - - /* Type of the module */ - Module_Type module_type; - - /* Heap of the module */ - void *heap; - - /* Heap size of the module */ - int heap_size; - - /* Module execution timeout in millisecond */ - int timeout; - - /* Queue of the module */ - bh_queue *queue; - - /* Watchdog timer of the module*/ - struct watchdog_timer wd_timer; - - timer_ctx_t timer_ctx; - - /* max timers number app can create */ - int timers; - - /* Internal data of the module */ - void *internal_data; - - /* Module name */ - char module_name[1]; -} module_data; - -/* Module function types */ -typedef bool (*module_init_func)(void); -typedef bool (*module_install_func)(request_t *msg); -typedef bool (*module_uninstall_func)(request_t *msg); -typedef void (*module_watchdog_kill_func)(module_data *module_data); -typedef bool (*module_handle_host_url_func)(void *queue_msg); -typedef module_data *(*module_get_module_data_func)(void *inst); - -/** - * @typedef module_on_install_request_byte_arrive_func - * - * @brief Define the signature of function to handle one byte of - * module app install request for struct module_interface. - * - * @param ch the byte to be received and handled - * @param total_size total size of the request - * @param received_total_size currently received total size when the function return - * - * @return true if success, false otherwise - */ -typedef bool (*module_on_install_request_byte_arrive_func) ( - uint8 ch, int total_size, int *received_total_size); - -/* Interfaces of each module */ -typedef struct module_interface { - module_init_func module_init; - module_install_func module_install; - module_uninstall_func module_uninstall; - module_watchdog_kill_func module_watchdog_kill; - module_handle_host_url_func module_handle_host_url; - module_get_module_data_func module_get_module_data; - module_on_install_request_byte_arrive_func module_on_install; -} module_interface; - -/** - * @typedef host_init_func - * @brief Define the host initialize callback function signature for - * struct host_interface. - * - * @return true if success, false if fail - */ -typedef bool (*host_init_func)(void); - -/** - * @typedef host_send_fun - * @brief Define the host send callback function signature for - * struct host_interface. - * - * @param buf data buffer to send. - * @param size size of the data to send. - * - * @return size of the data sent in bytes - */ -typedef int (*host_send_fun)(void * ctx, const char *buf, int size); - -/** - * @typedef host_destroy_fun - * @brief Define the host receive callback function signature for - * struct host_interface. - * - */ -typedef void (*host_destroy_fun)(); - -/* Interfaces of host communication */ -typedef struct host_interface { - host_init_func init; - host_send_fun send; - host_destroy_fun destroy; -} host_interface; - -/** - * Initialize communication with Host - * - * @param interface host communication interface - * - * @return true if success, false otherwise - */ -bool -app_manager_host_init(host_interface *interface); - -/** - * Send message to Host - * - * @param buf buffer to send - * @param size size of buffer - * - * @return size of buffer sent - */ - -/* Startup app manager */ -void -app_manager_startup(host_interface *interface); - -/* Get queue of current applet */ -void * -app_manager_get_module_queue(uint32 module_type, void *module_inst); - -/* Get applet name of current applet */ -const char * -app_manager_get_module_name(uint32 module_type, void *module_inst); - -/* Get heap of current applet */ -void * -app_manager_get_module_heap(uint32 module_type, void *module_inst); - -void* -get_app_manager_queue(); - -module_data* -app_manager_get_module_data(uint32 module_type, void *module_inst); - -unsigned int -app_manager_get_module_id(uint32 module_type, void *module_inst); - -module_data* -app_manager_lookup_module_data(const char *name); - -module_data* -module_data_list_lookup(const char *module_name); - -module_data* -module_data_list_lookup_id(unsigned int module_id); - -void -app_manager_post_applets_update_event(); - -bool -am_register_resource(const char *url, - void (*request_handler)(request_t *, void *), uint32 register_id); - -void am_cleanup_registeration(uint32 register_id); - -bool -am_register_event(const char *url, uint32_t reg_client); - -bool -am_unregister_event(const char *url, uint32_t reg_client); - -void am_publish_event(request_t * event); - -void * am_dispatch_request(request_t *request); - -void am_send_response(response_t *response); - -void module_request_handler(request_t *request, void *user_data); - -/** - * Send request message to host - * - * @param msg the request or event message. - * It is event when msg->action==COAP_EVENT - * - * @return true if success, false otherwise - */ -bool -send_request_to_host(request_t *msg); - -/** - * Send response message to host - * - * @param msg the response message - * - * @return true if success, false otherwise - */ -bool -send_response_to_host(response_t *msg); - -/** - * Send response with mid and code to host - * - * @param mid the message id of response - * @param code the code/status of response - * @param msg the detailed message - * - * @return true if success, false otherwise - */ -bool -send_error_response_to_host(int mid, int code, const char *msg); - -/** - * Check whether the applet has the permission - * - * @param perm the permission needed to check - * - * @return true if success, false otherwise - */ -bool -bh_applet_check_permission(const char *perm); - -int -app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size); - -#ifdef __cplusplus -} /* end of extern "C" */ -#endif - -#endif - diff --git a/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake b/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake deleted file mode 100644 index f370e8b29b..0000000000 --- a/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -set (APP_MGR_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) - -include_directories(${APP_MGR_SHARED_DIR}) - - -file (GLOB_RECURSE source_all ${APP_MGR_SHARED_DIR}/*.c) - -set (APP_MGR_SHARED_SOURCE ${source_all}) - -file (GLOB header - ${APP_MGR_SHARED_DIR}/*.h -) -LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/app-mgr/app-mgr-shared/host_link.h b/core/app-mgr/app-mgr-shared/host_link.h deleted file mode 100644 index e3a37fb408..0000000000 --- a/core/app-mgr/app-mgr-shared/host_link.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef DEPS_APP_MGR_APP_MGR_SHARED_HOST_LINK_H_ -#define DEPS_APP_MGR_APP_MGR_SHARED_HOST_LINK_H_ - -typedef enum LINK_MSG_TYPE { - COAP_TCP_RAW = 0, - COAP_UDP_RAW = 1, - REQUEST_PACKET, - RESPONSE_PACKET, - INSTALL_WASM_APP, - CBOR_GENERIC = 30, - - LINK_MSG_TYPE_MAX = 50 -} LINK_MSG_TYPE; - -/* Link message, or message between host and app manager */ -typedef struct bh_link_msg_t { - /* 2 bytes leading */ - uint16_t leading_bytes; - /* message type, must be COAP_TCP or COAP_UDP */ - uint16_t message_type; - /* size of payload */ - uint32_t payload_size; - char *payload; -} bh_link_msg_t; - -#endif /* DEPS_APP_MGR_APP_MGR_SHARED_HOST_LINK_H_ */ diff --git a/core/app-mgr/module.json b/core/app-mgr/module.json deleted file mode 100644 index b2faeca5f7..0000000000 --- a/core/app-mgr/module.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "aee", - "version": "0.0.1", - "description": "aee", - "type": "source", - "category": "middleware", - "arch": "x86, arc, posix", - "includes": [ - "Beihai/classlib/include", - "Beihai/runtime/include", - "Beihai/runtime/platform/include", - "Beihai/runtime/platform/zephyr", - "Beihai/runtime/utils/coap/er-coap", - "Beihai/runtime/utils/coap/extension", - "iwasm/runtime/include", - "iwasm/runtime/platform/include", - "iwasm/runtime/platform/zephyr", - "iwasm/runtime/vmcore_wasm" - ], - "sources": [ - "Beihai/classlib/native/internal/*.c", - "Beihai/classlib/native/*.c", - "Beihai/runtime/gc/*.c", - "Beihai/runtime/platform/zephyr/*.c", - "Beihai/runtime/utils/*.c", - "Beihai/runtime/utils/coap/er-coap/*.c", - "Beihai/runtime/utils/coap/extension/*.c", - "Beihai/runtime/vmcore_jeff/*.c", - "app-manager/app-manager.c", - "app-manager/app-manager-host.c", - "app-manager/app_mgr_zephyr.c", - "app-manager/event.c", - "app-manager/message.c", - "app-manager/module_jeff.c", - "app-manager/module_wasm_lib.c", - "app-manager/module_wasm_app.c", - "app-manager/watchdog.c", - "Beihai/products/iMRT/*.c", - "iwasm/runtime/utils/*.c", - "iwasm/runtime/platform/zephyr/*.c", - "iwasm/runtime/vmcore_wasm/*.c", - "iwasm/lib/lib-export\.c", - "iwasm/lib/aee/*.c", - "iwasm/products/zephyr/sample/src/*.c" - ], - "compile_definitions": [ - "__JLF__", - "__ZEPHYR__" - ], - "target": "aee", - "dependencies": [] -} diff --git a/core/config.h b/core/config.h new file mode 100644 index 0000000000..31404deb95 --- /dev/null +++ b/core/config.h @@ -0,0 +1,739 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* clang-format off */ +#if !defined(BUILD_TARGET_X86_64) \ + && !defined(BUILD_TARGET_AMD_64) \ + && !defined(BUILD_TARGET_AARCH64) \ + && !defined(BUILD_TARGET_X86_32) \ + && !defined(BUILD_TARGET_ARM) \ + && !defined(BUILD_TARGET_ARM_VFP) \ + && !defined(BUILD_TARGET_THUMB) \ + && !defined(BUILD_TARGET_THUMB_VFP) \ + && !defined(BUILD_TARGET_MIPS) \ + && !defined(BUILD_TARGET_XTENSA) \ + && !defined(BUILD_TARGET_RISCV64_LP64D) \ + && !defined(BUILD_TARGET_RISCV64_LP64) \ + && !defined(BUILD_TARGET_RISCV32_ILP32D) \ + && !defined(BUILD_TARGET_RISCV32_ILP32F) \ + && !defined(BUILD_TARGET_RISCV32_ILP32) \ + && !defined(BUILD_TARGET_ARC) +/* clang-format on */ +#if defined(__x86_64__) || defined(__x86_64) +#define BUILD_TARGET_X86_64 +#elif defined(__amd64__) || defined(__amd64) +#define BUILD_TARGET_AMD_64 +#elif defined(__aarch64__) +#define BUILD_TARGET_AARCH64 +#elif defined(__i386__) || defined(__i386) || defined(i386) +#define BUILD_TARGET_X86_32 +#elif defined(__thumb__) +#define BUILD_TARGET_THUMB +#define BUILD_TARGET "THUMBV4T" +#elif defined(__arm__) +#define BUILD_TARGET_ARM +#define BUILD_TARGET "ARMV4T" +#elif defined(__mips__) || defined(__mips) || defined(mips) +#define BUILD_TARGET_MIPS +#elif defined(__XTENSA__) +#define BUILD_TARGET_XTENSA +#elif defined(__riscv) && (__riscv_xlen == 64) +#define BUILD_TARGET_RISCV64_LP64D +#elif defined(__riscv) && (__riscv_xlen == 32) && !defined(__riscv_flen) +#define BUILD_TARGET_RISCV32_ILP32 +#elif defined(__riscv) && (__riscv_xlen == 32) && (__riscv_flen == 32) +#define BUILD_TARGET_RISCV32_ILP32F +#elif defined(__riscv) && (__riscv_xlen == 32) && (__riscv_flen == 64) +#define BUILD_TARGET_RISCV32_ILP32D +#elif defined(__arc__) +#define BUILD_TARGET_ARC +#else +#error "Build target isn't set" +#endif +#endif + +#ifndef BH_DEBUG +#define BH_DEBUG 0 +#endif + +#define MEM_ALLOCATOR_EMS 0 +#define MEM_ALLOCATOR_TLSF 1 + +/* Default memory allocator */ +#define DEFAULT_MEM_ALLOCATOR MEM_ALLOCATOR_EMS + +#ifndef WASM_ENABLE_INTERP +#define WASM_ENABLE_INTERP 0 +#endif + +#ifndef WASM_ENABLE_AOT +#define WASM_ENABLE_AOT 0 +#endif + +#ifndef WASM_ENABLE_DYNAMIC_AOT_DEBUG +#define WASM_ENABLE_DYNAMIC_AOT_DEBUG 0 +#endif + +#ifndef WASM_ENABLE_WORD_ALIGN_READ +#define WASM_ENABLE_WORD_ALIGN_READ 0 +#endif + +#define AOT_MAGIC_NUMBER 0x746f6100 +#define AOT_CURRENT_VERSION 6 + +#ifndef WASM_ENABLE_JIT +#define WASM_ENABLE_JIT 0 +#endif + +#ifndef WASM_ENABLE_LAZY_JIT +#define WASM_ENABLE_LAZY_JIT 0 +#endif + +#ifndef WASM_ORC_JIT_BACKEND_THREAD_NUM +/* The number of backend threads created by runtime */ +#define WASM_ORC_JIT_BACKEND_THREAD_NUM 4 +#endif + +#if WASM_ORC_JIT_BACKEND_THREAD_NUM < 1 +#error "WASM_ORC_JIT_BACKEND_THREAD_NUM must be greater than 0" +#endif + +#ifndef WASM_ORC_JIT_COMPILE_THREAD_NUM +/* The number of compilation threads created by LLVM JIT */ +#define WASM_ORC_JIT_COMPILE_THREAD_NUM 4 +#endif + +#if WASM_ORC_JIT_COMPILE_THREAD_NUM < 1 +#error "WASM_ORC_JIT_COMPILE_THREAD_NUM must be greater than 0" +#endif + +#if (WASM_ENABLE_AOT == 0) && (WASM_ENABLE_JIT != 0) +/* LLVM JIT can only be enabled when AOT is enabled */ +#undef WASM_ENABLE_JIT +#define WASM_ENABLE_JIT 0 + +#undef WASM_ENABLE_LAZY_JIT +#define WASM_ENABLE_LAZY_JIT 0 +#endif + +#ifndef WASM_ENABLE_FAST_JIT +#define WASM_ENABLE_FAST_JIT 0 +#endif + +#ifndef WASM_ENABLE_FAST_JIT_DUMP +#define WASM_ENABLE_FAST_JIT_DUMP 0 +#endif + +#ifndef FAST_JIT_DEFAULT_CODE_CACHE_SIZE +#define FAST_JIT_DEFAULT_CODE_CACHE_SIZE 10 * 1024 * 1024 +#endif + +#ifndef WASM_ENABLE_WAMR_COMPILER +#define WASM_ENABLE_WAMR_COMPILER 0 +#endif + +#ifndef WASM_ENABLE_LIBC_BUILTIN +#define WASM_ENABLE_LIBC_BUILTIN 0 +#endif + +#ifndef WASM_ENABLE_LIBC_WASI +#define WASM_ENABLE_LIBC_WASI 0 +#endif + +#ifndef WASM_ENABLE_UVWASI +#define WASM_ENABLE_UVWASI 0 +#endif + +#ifndef WASM_ENABLE_WASI_NN +#define WASM_ENABLE_WASI_NN 0 +#endif + +#ifndef WASM_ENABLE_WASI_NN_GPU +#define WASM_ENABLE_WASI_NN_GPU 0 +#endif + +#ifndef WASM_ENABLE_WASI_NN_EXTERNAL_DELEGATE +#define WASM_ENABLE_WASI_NN_EXTERNAL_DELEGATE 0 +#endif + +#ifndef WASM_ENABLE_WASI_EPHEMERAL_NN +#define WASM_ENABLE_WASI_EPHEMERAL_NN 0 +#endif + +/* Default disable libc emcc */ +#ifndef WASM_ENABLE_LIBC_EMCC +#define WASM_ENABLE_LIBC_EMCC 0 +#endif + +#ifndef WASM_ENABLE_LIB_RATS +#define WASM_ENABLE_LIB_RATS 0 +#endif + +#ifndef WASM_ENABLE_LIB_PTHREAD +#define WASM_ENABLE_LIB_PTHREAD 0 +#endif + +#ifndef WASM_ENABLE_LIB_PTHREAD_SEMAPHORE +#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_COPY_CALL_STACK +#define WASM_ENABLE_COPY_CALL_STACK 0 +#endif + +#ifndef WASM_ENABLE_BASE_LIB +#define WASM_ENABLE_BASE_LIB 0 +#endif + +#ifndef WASM_ENABLE_APP_FRAMEWORK +#define WASM_ENABLE_APP_FRAMEWORK 0 +#endif + +#ifndef WASM_HAVE_MREMAP +#define WASM_HAVE_MREMAP 0 +#endif + +/* Bulk memory operation */ +#ifndef WASM_ENABLE_BULK_MEMORY +#define WASM_ENABLE_BULK_MEMORY 0 +#endif + +#ifndef WASM_ENABLE_BULK_MEMORY_OPT +#define WASM_ENABLE_BULK_MEMORY_OPT 0 +#endif + +/* Shared memory */ +#ifndef WASM_ENABLE_SHARED_MEMORY +#define WASM_ENABLE_SHARED_MEMORY 0 +#endif + +/* Thread manager */ +#ifndef WASM_ENABLE_THREAD_MGR +#define WASM_ENABLE_THREAD_MGR 0 +#endif + +/* Source debugging */ +#ifndef WASM_ENABLE_DEBUG_INTERP +#define WASM_ENABLE_DEBUG_INTERP 0 +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 +#ifndef DEBUG_EXECUTION_MEMORY_SIZE +/* 0x85000 is the size required by lldb, if this is changed to a smaller value, + * then the debugger will not be able to evaluate user expressions, other + * functionality such as breakpoint and stepping are not influenced by this */ +#define DEBUG_EXECUTION_MEMORY_SIZE 0x85000 +#endif +#endif /* end of WASM_ENABLE_DEBUG_INTERP != 0 */ + +#ifndef WASM_ENABLE_DEBUG_AOT +#define WASM_ENABLE_DEBUG_AOT 0 +#endif + +/* Custom sections */ +#ifndef WASM_ENABLE_LOAD_CUSTOM_SECTION +#define WASM_ENABLE_LOAD_CUSTOM_SECTION 0 +#endif + +/* WASM log system */ +#ifndef WASM_ENABLE_LOG +#define WASM_ENABLE_LOG 1 +#endif + +/* When this flag is set, WAMR will not automatically + * initialize sockets on Windows platforms. The host + * application is responsible for calling WSAStartup() + * before executing WAMR code that uses sockets, and + * calling WSACleanup() after. + * This flag passes control of socket initialization from + * WAMR to the host application. */ +#ifndef WASM_ENABLE_HOST_SOCKET_INIT +#define WASM_ENABLE_HOST_SOCKET_INIT 0 +#endif + +#ifndef WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS +#if defined(BUILD_TARGET_X86_32) || defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AARCH64) +#define WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS 1 +#else +#define WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS 0 +#endif +#endif + +/* WASM Interpreter labels-as-values feature */ +#ifndef WASM_ENABLE_LABELS_AS_VALUES +#ifdef __GNUC__ +#define WASM_ENABLE_LABELS_AS_VALUES 1 +#else +#define WASM_ENABLE_LABELS_AS_VALUES 0 +#endif +#endif + +/* Enable fast interpreter or not */ +#ifndef WASM_ENABLE_FAST_INTERP +#define WASM_ENABLE_FAST_INTERP 0 +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 +#define WASM_DEBUG_PREPROCESSOR 0 +#endif + +/* Enable opcode counter or not */ +#ifndef WASM_ENABLE_OPCODE_COUNTER +#define WASM_ENABLE_OPCODE_COUNTER 0 +#endif + +/* Support a module with dependency, other modules */ +#ifndef WASM_ENABLE_MULTI_MODULE +#define WASM_ENABLE_MULTI_MODULE 0 +#endif + +/* Enable wasm mini loader or not */ +#ifndef WASM_ENABLE_MINI_LOADER +#define WASM_ENABLE_MINI_LOADER 0 +#endif + +/* Disable boundary check with hardware trap or not, + * enable it by default if it is supported */ +#ifndef WASM_DISABLE_HW_BOUND_CHECK +#define WASM_DISABLE_HW_BOUND_CHECK 0 +#endif + +/* Disable native stack access boundary check with hardware + * trap or not, enable it by default if it is supported */ +#ifndef WASM_DISABLE_STACK_HW_BOUND_CHECK +#define WASM_DISABLE_STACK_HW_BOUND_CHECK 0 +#endif + +/* Disable SIMD unless it is manually enabled somewhere */ +#ifndef WASM_ENABLE_SIMD +#define WASM_ENABLE_SIMD 0 +#endif + +/* Disable SIMDe (used in the fast interpreter for SIMD opcodes) +unless used elsewhere */ +#ifndef WASM_ENABLE_SIMDE +#define WASM_ENABLE_SIMDE 0 +#endif + +/* GC performance profiling */ +#ifndef WASM_ENABLE_GC_PERF_PROFILING +#define WASM_ENABLE_GC_PERF_PROFILING 0 +#endif + +/* Memory profiling */ +#ifndef WASM_ENABLE_MEMORY_PROFILING +#define WASM_ENABLE_MEMORY_PROFILING 0 +#endif + +/* Memory tracing */ +#ifndef WASM_ENABLE_MEMORY_TRACING +#define WASM_ENABLE_MEMORY_TRACING 0 +#endif + +/* Performance profiling */ +#ifndef WASM_ENABLE_PERF_PROFILING +#define WASM_ENABLE_PERF_PROFILING 0 +#endif + +/* Dump call stack */ +#ifndef WASM_ENABLE_DUMP_CALL_STACK +#define WASM_ENABLE_DUMP_CALL_STACK 0 +#endif + +/* AOT stack frame */ +#ifndef WASM_ENABLE_AOT_STACK_FRAME +#define WASM_ENABLE_AOT_STACK_FRAME 0 +#endif + +/* Heap verification */ +#ifndef BH_ENABLE_GC_VERIFY +#define BH_ENABLE_GC_VERIFY 0 +#endif + +/* Heap corruption check, enabled by default */ +#ifndef BH_ENABLE_GC_CORRUPTION_CHECK +#define BH_ENABLE_GC_CORRUPTION_CHECK 1 +#endif + +/* Enable global heap pool if heap verification is enabled */ +#if BH_ENABLE_GC_VERIFY != 0 +#define WASM_ENABLE_GLOBAL_HEAP_POOL 1 +#endif + +/* Global heap pool */ +#ifndef WASM_ENABLE_GLOBAL_HEAP_POOL +#define WASM_ENABLE_GLOBAL_HEAP_POOL 0 +#endif + +#ifndef WASM_ENABLE_SPEC_TEST +#define WASM_ENABLE_SPEC_TEST 0 +#endif + +#ifndef WASM_ENABLE_WASI_TEST +#define WASM_ENABLE_WASI_TEST 0 +#endif + +/* Global heap pool size in bytes */ +#ifndef WASM_GLOBAL_HEAP_SIZE +#define WASM_GLOBAL_HEAP_SIZE (10 * 1024 * 1024) +#endif + +/* Default length of queue */ +#ifndef DEFAULT_QUEUE_LENGTH +#define DEFAULT_QUEUE_LENGTH 50 +#endif + +/* The max percentage of global heap that app memory space can grow */ +#ifndef APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT +#define APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT 1 / 3 +#endif + +/* Default min/max heap size of each app */ +#ifndef APP_HEAP_SIZE_DEFAULT +#define APP_HEAP_SIZE_DEFAULT (8 * 1024) +#endif +#define APP_HEAP_SIZE_MIN (256) +/* The ems memory allocator supports maximal heap size 1GB, + see ems_gc_internal.h */ +#define APP_HEAP_SIZE_MAX (1024 * 1024 * 1024) + +/* Default min/max gc heap size of each app */ +#ifndef GC_HEAP_SIZE_DEFAULT +#define GC_HEAP_SIZE_DEFAULT (128 * 1024) +#endif +#define GC_HEAP_SIZE_MIN (4 * 1024) +#define GC_HEAP_SIZE_MAX (1024 * 1024 * 1024) + +/* Default wasm stack size of each app */ +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#define DEFAULT_WASM_STACK_SIZE (16 * 1024) +#else +#define DEFAULT_WASM_STACK_SIZE (12 * 1024) +#endif +/* Min auxiliary stack size of each wasm thread */ +#define WASM_THREAD_AUX_STACK_SIZE_MIN (256) + +/* Default/min native stack size of each app thread */ +#if !(defined(APP_THREAD_STACK_SIZE_DEFAULT) \ + && defined(APP_THREAD_STACK_SIZE_MIN)) +#if defined(BH_PLATFORM_ZEPHYR) || defined(BH_PLATFORM_ALIOS_THINGS) \ + || defined(BH_PLATFORM_ESP_IDF) || defined(BH_PLATFORM_OPENRTOS) +#define APP_THREAD_STACK_SIZE_DEFAULT (6 * 1024) +#define APP_THREAD_STACK_SIZE_MIN (4 * 1024) +#elif defined(PTHREAD_STACK_DEFAULT) && defined(PTHREAD_STACK_MIN) +#define APP_THREAD_STACK_SIZE_DEFAULT PTHREAD_STACK_DEFAULT +#define APP_THREAD_STACK_SIZE_MIN PTHREAD_STACK_MIN +#elif WASM_ENABLE_UVWASI != 0 +/* UVWASI requires larger native stack */ +#define APP_THREAD_STACK_SIZE_DEFAULT (64 * 1024) +#define APP_THREAD_STACK_SIZE_MIN (48 * 1024) +#else +#define APP_THREAD_STACK_SIZE_DEFAULT (128 * 1024) +#define APP_THREAD_STACK_SIZE_MIN (24 * 1024) +#endif +#endif /* end of !(defined(APP_THREAD_STACK_SIZE_DEFAULT) \ + && defined(APP_THREAD_STACK_SIZE_MIN)) */ + +/* Max native stack size of each app thread */ +#if !defined(APP_THREAD_STACK_SIZE_MAX) +#define APP_THREAD_STACK_SIZE_MAX (8 * 1024 * 1024) +#endif + +/* Reserved bytes to the native thread stack boundary, throw native + * stack overflow exception if the guard boundary is reached + * + * WASM_STACK_GUARD_SIZE needs to be large enough for: + * + * - native functions + * + * w/o hw bound check, the overhead (aot_call_function etc) + the native + * function itself. as of writing this, the former is about 1000 bytes + * on macOS amd64. + * + * with hw bound check, theoretically, only needs to cover the logic to + * set up the jmp_buf stack. + * + * - aot runtime functions + * eg. aot_enlarge_memory. + * + * - w/o hw bound check, the interpreter loop + * + * the stack consumption heavily depends on compiler settings, + * especially for huge functions like the classic interpreter's + * wasm_interp_call_func_bytecode: + * + * 200 bytes (release build, macOS/amd64) + * 2600 bytes (debug build, macOS/amd64) + * + * - platform-provided functions (eg. libc) + * + * the following are examples of the stack consumptions observed for + * host APIs. + * + * snprintf: (used by eg. wasm_runtime_set_exception) + * - about 1600 bytes on macOS/amd64 + * - about 2000 bytes on Ubuntu amd64 20.04 + * + * gethostbyname: + * - 3KB-6KB on macOS/amd64 + * - 10KB on Ubuntu amd64 20.04 + * + * getaddrinfo: + * - 4KB-17KB on macOS/amd64 + * - 12KB on Ubuntu amd64 20.04 + * - 0.3-1.5KB on NuttX/esp32s3 + * + * - stack check wrapper functions generated by the aot compiler + * (--stack-bounds-checks=1) + * + * wamrc issues a warning + * "precheck functions themselves consume relatively large amount of stack" + * when it detects wrapper functions requiring more than 1KB. + * + * - the ABI-defined red zone. eg. 128 bytes for SYSV x86-64 ABI. + * cf. https://en.wikipedia.org/wiki/Red_zone_(computing) + * + * Note: on platforms with lazy function binding, don't forget to consider + * the symbol resolution overhead on the first call. For example, + * on Ubuntu amd64 20.04, it seems to consume about 1500 bytes. + * For some reasons, macOS amd64 12.7.4 seems to resolve symbols eagerly. + * (Observed with a binary with traditional non-chained fixups.) + * The latest macOS seems to apply chained fixups in kernel on page-in time. + * (thus it wouldn't consume userland stack.) + */ +#ifndef WASM_STACK_GUARD_SIZE +#if WASM_ENABLE_UVWASI != 0 +/* UVWASI requires larger native stack */ +#define WASM_STACK_GUARD_SIZE (4096 * 6) +#else +/* + * Use a larger default for platforms like macOS/Linux. + * + * For example, the classic interpreter loop which ended up with a trap + * (wasm_runtime_set_exception) would consume about 2KB stack on x86-64 + * macOS. On Ubuntu amd64 20.04, it seems to consume a bit more. + * + * Although product-mini/platforms/nuttx always overrides + * WASM_STACK_GUARD_SIZE, exclude NuttX here just in case. + */ +#if defined(__APPLE__) || (defined(__unix__) && !defined(__NuttX__)) +#if BH_DEBUG != 0 /* assumption: BH_DEBUG matches CMAKE_BUILD_TYPE=Debug */ +#define WASM_STACK_GUARD_SIZE (1024 * 5) +#else +#define WASM_STACK_GUARD_SIZE (1024 * 3) +#endif +#else +/* + * Otherwise, assume very small requirement for now. + * + * Embedders for very small devices likely fine-tune WASM_STACK_GUARD_SIZE + * for their specific applications anyway. + */ +#define WASM_STACK_GUARD_SIZE 1024 +#endif +#endif +#endif + +/* Guard page count for stack overflow check with hardware trap */ +#ifndef STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT +#if defined(__APPLE__) && defined(__aarch64__) +/* Note: on macOS/iOS arm64, the user page size is 16KB */ +#define STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT 1 +#else +#define STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT 3 +#endif +#endif + +/* Default wasm block address cache size and conflict list size */ +#ifndef BLOCK_ADDR_CACHE_SIZE +#define BLOCK_ADDR_CACHE_SIZE 64 +#endif +#define BLOCK_ADDR_CONFLICT_SIZE 2 + +/* Default max thread num per cluster. Can be overwrite by + wasm_runtime_set_max_thread_num */ +#define CLUSTER_MAX_THREAD_NUM 4 + +#ifndef WASM_ENABLE_TAIL_CALL +#define WASM_ENABLE_TAIL_CALL 0 +#endif + +#ifndef WASM_ENABLE_CUSTOM_NAME_SECTION +#define WASM_ENABLE_CUSTOM_NAME_SECTION 0 +#endif + +#ifndef WASM_ENABLE_REF_TYPES +#define WASM_ENABLE_REF_TYPES 0 +#endif + +#ifndef WASM_ENABLE_CALL_INDIRECT_OVERLONG +#define WASM_ENABLE_CALL_INDIRECT_OVERLONG 0 +#endif + +#ifndef WASM_ENABLE_BRANCH_HINTS +#define WASM_ENABLE_BRANCH_HINTS 0 +#endif + +#ifndef WASM_ENABLE_GC +#define WASM_ENABLE_GC 0 +#endif + +#ifndef WASM_CONST_EXPR_STACK_SIZE +#if WASM_ENABLE_GC != 0 +#define WASM_CONST_EXPR_STACK_SIZE 8 +#else +#define WASM_CONST_EXPR_STACK_SIZE 4 +#endif +#endif + +#ifndef WASM_ENABLE_STRINGREF +#define WASM_ENABLE_STRINGREF 0 +#endif + +#ifndef GC_REFTYPE_MAP_SIZE_DEFAULT +#define GC_REFTYPE_MAP_SIZE_DEFAULT 64 +#endif + +#ifndef GC_RTTOBJ_MAP_SIZE_DEFAULT +#define GC_RTTOBJ_MAP_SIZE_DEFAULT 64 +#endif + +#ifndef WASM_ENABLE_EXCE_HANDLING +#define WASM_ENABLE_EXCE_HANDLING 0 +#endif + +#ifndef WASM_ENABLE_TAGS +#define WASM_ENABLE_TAGS 0 +#endif + +#ifndef WASM_ENABLE_SGX_IPFS +#define WASM_ENABLE_SGX_IPFS 0 +#endif + +#ifndef WASM_MEM_ALLOC_WITH_USER_DATA +#define WASM_MEM_ALLOC_WITH_USER_DATA 0 +#endif + +#ifndef WASM_ENABLE_WASM_CACHE +#define WASM_ENABLE_WASM_CACHE 0 +#endif + +#ifndef WASM_ENABLE_STATIC_PGO +#define WASM_ENABLE_STATIC_PGO 0 +#endif + +/* Disable writing linear memory base address to GS segment register, + by default only in linux x86-64, linear memory base addr is written + to GS segment register before calling wasm/aot function. */ +#ifndef WASM_DISABLE_WRITE_GS_BASE +#define WASM_DISABLE_WRITE_GS_BASE 0 +#endif + +/* Configurable bounds checks */ +#ifndef WASM_CONFIGURABLE_BOUNDS_CHECKS +#define WASM_CONFIGURABLE_BOUNDS_CHECKS 0 +#endif + +/* Some chip cannot support external ram with rwx attr at the same time, + it has to map it into 2 spaces of idbus and dbus, code in dbus can be + read/written and read/executed in ibus. so there are 2 steps to execute + the code, first, copy & do relocation in dbus space, and second execute + it in ibus space, since in the 2 spaces the contents are the same, + so we call it bus mirror. + */ +#ifndef WASM_MEM_DUAL_BUS_MIRROR +#define WASM_MEM_DUAL_BUS_MIRROR 0 +#endif + +/* The max number of module instance contexts. */ +#ifndef WASM_MAX_INSTANCE_CONTEXTS +#define WASM_MAX_INSTANCE_CONTEXTS 8 +#endif + +/* linux perf support */ +#ifndef WASM_ENABLE_LINUX_PERF +#define WASM_ENABLE_LINUX_PERF 0 +#endif + +/* Support registering quick AOT/JIT function entries of some func types + to speed up the calling process of invoking the AOT/JIT functions of + these types from the host embedder */ +#ifndef WASM_ENABLE_QUICK_AOT_ENTRY +#define WASM_ENABLE_QUICK_AOT_ENTRY 1 +#endif + +/* Support AOT intrinsic functions which can be called from the AOT code + when `--disable-llvm-intrinsics` flag or + `--enable-builtin-intrinsics=` is used by wamrc to + generate the AOT file */ +#ifndef WASM_ENABLE_AOT_INTRINSICS +#define WASM_ENABLE_AOT_INTRINSICS 1 +#endif + +/* Disable memory64 by default */ +#ifndef WASM_ENABLE_MEMORY64 +#define WASM_ENABLE_MEMORY64 0 +#endif + +/* Disable multi-memory by default */ +#ifndef WASM_ENABLE_MULTI_MEMORY +#define WASM_ENABLE_MULTI_MEMORY 0 +#endif + +#ifndef WASM_TABLE_MAX_SIZE +#define WASM_TABLE_MAX_SIZE 1024 +#endif + +#ifndef WASM_MEM_ALLOC_WITH_USAGE +#define WASM_MEM_ALLOC_WITH_USAGE 0 +#endif + +#ifndef WASM_ENABLE_FUZZ_TEST +#define WASM_ENABLE_FUZZ_TEST 0 +#endif + +#if WASM_ENABLE_FUZZ_TEST != 0 +#ifndef WASM_MEM_ALLOC_MAX_SIZE +/* In oss-fuzz, the maximum RAM is ~2.5G */ +#define WASM_MEM_ALLOC_MAX_SIZE (2U * 1024 * 1024 * 1024) +#endif +#endif /* WASM_ENABLE_FUZZ_TEST != 0 */ + +#ifndef WASM_ENABLE_SHARED_HEAP +#define WASM_ENABLE_SHARED_HEAP 0 +#endif + +#ifndef WASM_ENABLE_SHRUNK_MEMORY +#define WASM_ENABLE_SHRUNK_MEMORY 1 +#endif + +#ifndef WASM_ENABLE_AOT_VALIDATOR +#define WASM_ENABLE_AOT_VALIDATOR 0 +#endif + +#ifndef WASM_ENABLE_INSTRUCTION_METERING +#define WASM_ENABLE_INSTRUCTION_METERING 0 +#endif + +#ifndef WASM_ENABLE_EXTENDED_CONST_EXPR +#define WASM_ENABLE_EXTENDED_CONST_EXPR 0 +#endif + +#endif /* end of _CONFIG_H_ */ diff --git a/core/deps/install_tensorflow.sh b/core/deps/install_tensorflow.sh new file mode 100755 index 0000000000..14e86bd837 --- /dev/null +++ b/core/deps/install_tensorflow.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +DEPS_ROOT=$(cd "$(dirname "$0")/" && pwd) +cd ${DEPS_ROOT} + +echo "Downloading tensorflow in ${PWD}..." + +git clone https://github.com/tensorflow/tensorflow.git tensorflow-src \ + --branch v2.12.0 + +# NOTE: fixes this https://github.com/tensorflow/tensorflow/issues/59631 +cd tensorflow-src +git cherry-pick 5115fa96d7c5b41451674892317be43e30b7c389 + +exit 0 diff --git a/core/iwasm/README.md b/core/iwasm/README.md index e69de29bb2..65ab78a495 100644 --- a/core/iwasm/README.md +++ b/core/iwasm/README.md @@ -0,0 +1,14 @@ +# vmcore architecture +- [WAMR memory model overview](https://bytecodealliance.github.io/wamr.dev/blog/the-wamr-memory-model/) + +## Wasm function +- [Wasm function architecture](./doc/wasm_function.MD) + +## Exports +- [Wasm export architecture](./doc/wasm_exports.MD) + +## globals +- [Wasm globals architecture](./doc/wasm_globals.MD) + +## classic interpreter +- [classic interpreter](./doc/classic_interpreter.MD) \ No newline at end of file diff --git a/core/iwasm/aot/SConscript b/core/iwasm/aot/SConscript new file mode 100644 index 0000000000..790f284041 --- /dev/null +++ b/core/iwasm/aot/SConscript @@ -0,0 +1,33 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import re +Import('rtconfig') + +cwd = GetCurrentDir() + +src = Split(''' +aot_loader.c +aot_runtime.c +aot_intrinsic.c +''') + +if rtconfig.ARCH == 'arm': + if re.match('^cortex-m.*', rtconfig.CPU): + src += ['arch/aot_reloc_thumb.c'] + elif re.match('^cortex-a.*', rtconfig.CPU): + src += ['arch/aot_reloc_arm.c'] +elif rtconfig.ARCH == 'ia32': + src += ['arch/aot_reloc_x86_32.c'] + +CPPPATH = [cwd, cwd + '/../include'] + +CPPDEFINES = ['WASM_ENABLE_AOT=1'] + +group = DefineGroup('iwasm_aot', src, depend = [''], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') diff --git a/core/iwasm/aot/aot_intrinsic.c b/core/iwasm/aot/aot_intrinsic.c new file mode 100644 index 0000000000..f296b4a25f --- /dev/null +++ b/core/iwasm/aot/aot_intrinsic.c @@ -0,0 +1,934 @@ +/* + * Copyright (C) 2021 XiaoMi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_intrinsic.h" + +float32 +aot_intrinsic_fadd_f32(float32 a, float32 b) +{ + return a + b; +} + +float64 +aot_intrinsic_fadd_f64(float64 a, float64 b) +{ + return a + b; +} + +float32 +aot_intrinsic_fsub_f32(float32 a, float32 b) +{ + return a - b; +} + +float64 +aot_intrinsic_fsub_f64(float64 a, float64 b) +{ + return a - b; +} + +float32 +aot_intrinsic_fmul_f32(float32 a, float32 b) +{ + return a * b; +} + +float64 +aot_intrinsic_fmul_f64(float64 a, float64 b) +{ + return a * b; +} + +float32 +aot_intrinsic_fdiv_f32(float32 a, float32 b) +{ + return a / b; +} + +float64 +aot_intrinsic_fdiv_f64(float64 a, float64 b) +{ + return a / b; +} + +float32 +aot_intrinsic_fabs_f32(float32 a) +{ + return fabsf(a); +} + +float64 +aot_intrinsic_fabs_f64(float64 a) +{ + return fabs(a); +} + +float32 +aot_intrinsic_ceil_f32(float32 a) +{ + return ceilf(a); +} + +float64 +aot_intrinsic_ceil_f64(float64 a) +{ + return ceil(a); +} + +float32 +aot_intrinsic_floor_f32(float32 a) +{ + return floorf(a); +} + +float64 +aot_intrinsic_floor_f64(float64 a) +{ + return floor(a); +} + +float32 +aot_intrinsic_trunc_f32(float32 a) +{ + return truncf(a); +} + +float64 +aot_intrinsic_trunc_f64(float64 a) +{ + return trunc(a); +} + +float32 +aot_intrinsic_rint_f32(float32 a) +{ + return rintf(a); +} + +float64 +aot_intrinsic_rint_f64(float64 a) +{ + return rint(a); +} + +float32 +aot_intrinsic_sqrt_f32(float32 a) +{ + return sqrtf(a); +} + +float64 +aot_intrinsic_sqrt_f64(float64 a) +{ + return sqrt(a); +} + +float32 +aot_intrinsic_copysign_f32(float32 a, float32 b) +{ + return signbit(b) ? -fabsf(a) : fabsf(a); +} + +float64 +aot_intrinsic_copysign_f64(float64 a, float64 b) +{ + return signbit(b) ? -fabs(a) : fabs(a); +} + +float32 +aot_intrinsic_fmin_f32(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +float64 +aot_intrinsic_fmin_f64(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +float32 +aot_intrinsic_fmax_f32(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +float64 +aot_intrinsic_fmax_f64(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +uint32 +aot_intrinsic_clz_i32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 0x80000000)) { + num++; + type <<= 1; + } + return num; +} + +uint64 +aot_intrinsic_clz_i64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +uint32 +aot_intrinsic_ctz_i32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +uint64 +aot_intrinsic_ctz_i64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +uint32 +aot_intrinsic_popcnt_i32(uint32 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +uint64 +aot_intrinsic_popcnt_i64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +float32 +aot_intrinsic_i32_to_f32(int32 i) +{ + return (float32)i; +} + +float32 +aot_intrinsic_u32_to_f32(uint32 u) +{ + return (float32)u; +} + +float64 +aot_intrinsic_i32_to_f64(int32 i) +{ + return (float64)i; +} + +float64 +aot_intrinsic_u32_to_f64(uint32 u) +{ + return (float64)u; +} + +float32 +aot_intrinsic_i64_to_f32(int64 i) +{ + return (float32)i; +} + +float32 +aot_intrinsic_u64_to_f32(uint64 u) +{ + return (float32)u; +} + +float64 +aot_intrinsic_i64_to_f64(int64 i) +{ + return (float64)i; +} + +float64 +aot_intrinsic_u64_to_f64(uint64 u) +{ + return (float64)u; +} + +int32 +aot_intrinsic_f32_to_i32(float32 f) +{ + return (int32)f; +} + +uint32 +aot_intrinsic_f32_to_u32(float32 f) +{ + return (uint32)f; +} + +int64 +aot_intrinsic_f32_to_i64(float32 f) +{ + return (int64)f; +} + +uint64 +aot_intrinsic_f32_to_u64(float32 f) +{ + return (uint64)f; +} + +int32 +aot_intrinsic_f64_to_i32(float64 f) +{ + return (int32)f; +} + +uint32 +aot_intrinsic_f64_to_u32(float64 f) +{ + return (uint32)f; +} + +int64 +aot_intrinsic_f64_to_i64(float64 f) +{ + return (int64)f; +} + +uint64 +aot_intrinsic_f64_to_u64(float64 f) +{ + return (uint64)f; +} + +float64 +aot_intrinsic_f32_to_f64(float32 f) +{ + return (float64)f; +} + +float32 +aot_intrinsic_f64_to_f32(float64 f) +{ + return (float32)f; +} + +int32 +aot_intrinsic_f32_cmp(AOTFloatCond cond, float32 lhs, float32 rhs) +{ + switch (cond) { + case FLOAT_EQ: + return lhs == rhs ? 1 : 0; + + case FLOAT_LT: + return lhs < rhs ? 1 : 0; + + case FLOAT_GT: + return lhs > rhs ? 1 : 0; + + case FLOAT_LE: + return lhs <= rhs ? 1 : 0; + + case FLOAT_GE: + return lhs >= rhs ? 1 : 0; + + case FLOAT_NE: + return (isnan(lhs) || isnan(rhs) || lhs != rhs) ? 1 : 0; + + case FLOAT_UNO: + return (isnan(lhs) || isnan(rhs)) ? 1 : 0; + + default: + break; + } + return 0; +} + +int32 +aot_intrinsic_f64_cmp(AOTFloatCond cond, float64 lhs, float64 rhs) +{ + switch (cond) { + case FLOAT_EQ: + return lhs == rhs ? 1 : 0; + + case FLOAT_LT: + return lhs < rhs ? 1 : 0; + + case FLOAT_GT: + return lhs > rhs ? 1 : 0; + + case FLOAT_LE: + return lhs <= rhs ? 1 : 0; + + case FLOAT_GE: + return lhs >= rhs ? 1 : 0; + + case FLOAT_NE: + return (isnan(lhs) || isnan(rhs) || lhs != rhs) ? 1 : 0; + + case FLOAT_UNO: + return (isnan(lhs) || isnan(rhs)) ? 1 : 0; + + default: + break; + } + return 0; +} + +int64 +aot_intrinsic_i64_div_s(int64 l, int64 r) +{ + return l / r; +} + +int32 +aot_intrinsic_i32_div_s(int32 l, int32 r) +{ + return l / r; +} + +uint32 +aot_intrinsic_i32_div_u(uint32 l, uint32 r) +{ + return l / r; +} + +int32 +aot_intrinsic_i32_rem_s(int32 l, int32 r) +{ + return l % r; +} + +uint32 +aot_intrinsic_i32_rem_u(uint32 l, uint32 r) +{ + return l % r; +} + +uint64 +aot_intrinsic_i64_div_u(uint64 l, uint64 r) +{ + return l / r; +} + +int64 +aot_intrinsic_i64_rem_s(int64 l, int64 r) +{ + return l % r; +} + +uint64 +aot_intrinsic_i64_rem_u(uint64 l, uint64 r) +{ + return l % r; +} + +uint64 +aot_intrinsic_i64_bit_or(uint64 l, uint64 r) +{ + return l | r; +} + +uint64 +aot_intrinsic_i64_bit_and(uint64 l, uint64 r) +{ + return l & r; +} + +uint64 +aot_intrinsic_i64_mul(uint64 l, uint64 r) +{ + return l * r; +} + +uint64 +aot_intrinsic_i64_shl(uint64 l, uint64 r) +{ + return l << r; +} + +uint64 +aot_intrinsic_i64_shr_s(uint64 l, uint64 r) +{ + return (int64)l >> r; +} + +uint64 +aot_intrinsic_i64_shr_u(uint64 l, uint64 r) +{ + return l >> r; +} + +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + +typedef struct { + const char *llvm_intrinsic; + const char *native_intrinsic; + uint64 flag; +} aot_intrinsic; + +/* clang-format off */ +static const aot_intrinsic g_intrinsic_mapping[] = { + { "llvm.experimental.constrained.fadd.f32", "aot_intrinsic_fadd_f32", AOT_INTRINSIC_FLAG_F32_FADD }, + { "llvm.experimental.constrained.fadd.f64", "aot_intrinsic_fadd_f64", AOT_INTRINSIC_FLAG_F64_FADD }, + { "llvm.experimental.constrained.fsub.f32", "aot_intrinsic_fsub_f32", AOT_INTRINSIC_FLAG_F32_FSUB }, + { "llvm.experimental.constrained.fsub.f64", "aot_intrinsic_fsub_f64", AOT_INTRINSIC_FLAG_F64_FSUB }, + { "llvm.experimental.constrained.fmul.f32", "aot_intrinsic_fmul_f32", AOT_INTRINSIC_FLAG_F32_FMUL }, + { "llvm.experimental.constrained.fmul.f64", "aot_intrinsic_fmul_f64", AOT_INTRINSIC_FLAG_F64_FMUL }, + { "llvm.experimental.constrained.fdiv.f32", "aot_intrinsic_fdiv_f32", AOT_INTRINSIC_FLAG_F32_FDIV }, + { "llvm.experimental.constrained.fdiv.f64", "aot_intrinsic_fdiv_f64", AOT_INTRINSIC_FLAG_F64_FDIV }, + { "llvm.fabs.f32", "aot_intrinsic_fabs_f32", AOT_INTRINSIC_FLAG_F32_FABS }, + { "llvm.fabs.f64", "aot_intrinsic_fabs_f64", AOT_INTRINSIC_FLAG_F64_FABS }, + { "llvm.ceil.f32", "aot_intrinsic_ceil_f32", AOT_INTRINSIC_FLAG_F32_CEIL }, + { "llvm.ceil.f64", "aot_intrinsic_ceil_f64", AOT_INTRINSIC_FLAG_F64_CEIL }, + { "llvm.floor.f32", "aot_intrinsic_floor_f32", AOT_INTRINSIC_FLAG_F32_FLOOR }, + { "llvm.floor.f64", "aot_intrinsic_floor_f64", AOT_INTRINSIC_FLAG_F64_FLOOR }, + { "llvm.trunc.f32", "aot_intrinsic_trunc_f32", AOT_INTRINSIC_FLAG_F32_TRUNC }, + { "llvm.trunc.f64", "aot_intrinsic_trunc_f64", AOT_INTRINSIC_FLAG_F64_TRUNC }, + { "llvm.rint.f32", "aot_intrinsic_rint_f32", AOT_INTRINSIC_FLAG_F32_RINT }, + { "llvm.rint.f64", "aot_intrinsic_rint_f64", AOT_INTRINSIC_FLAG_F64_RINT }, + { "llvm.sqrt.f32", "aot_intrinsic_sqrt_f32", AOT_INTRINSIC_FLAG_F32_SQRT }, + { "llvm.sqrt.f64", "aot_intrinsic_sqrt_f64", AOT_INTRINSIC_FLAG_F64_SQRT }, + { "llvm.copysign.f32", "aot_intrinsic_copysign_f32", AOT_INTRINSIC_FLAG_F32_COPYSIGN }, + { "llvm.copysign.f64", "aot_intrinsic_copysign_f64", AOT_INTRINSIC_FLAG_F64_COPYSIGN }, + { "llvm.minnum.f32", "aot_intrinsic_fmin_f32", AOT_INTRINSIC_FLAG_F32_MIN }, + { "llvm.minnum.f64", "aot_intrinsic_fmin_f64", AOT_INTRINSIC_FLAG_F64_MIN }, + { "llvm.maxnum.f32", "aot_intrinsic_fmax_f32", AOT_INTRINSIC_FLAG_F32_MAX }, + { "llvm.maxnum.f64", "aot_intrinsic_fmax_f64", AOT_INTRINSIC_FLAG_F64_MAX }, + { "llvm.ctlz.i32", "aot_intrinsic_clz_i32", AOT_INTRINSIC_FLAG_I32_CLZ }, + { "llvm.ctlz.i64", "aot_intrinsic_clz_i64", AOT_INTRINSIC_FLAG_I64_CLZ }, + { "llvm.cttz.i32", "aot_intrinsic_ctz_i32", AOT_INTRINSIC_FLAG_I32_CTZ }, + { "llvm.cttz.i64", "aot_intrinsic_ctz_i64", AOT_INTRINSIC_FLAG_I64_CTZ }, + { "llvm.ctpop.i32", "aot_intrinsic_popcnt_i32", AOT_INTRINSIC_FLAG_I32_POPCNT }, + { "llvm.ctpop.i64", "aot_intrinsic_popcnt_i64", AOT_INTRINSIC_FLAG_I64_POPCNT }, + { "f64_convert_i32_s", "aot_intrinsic_i32_to_f64", AOT_INTRINSIC_FLAG_I32_TO_F64 }, + { "f64_convert_i32_u", "aot_intrinsic_u32_to_f64", AOT_INTRINSIC_FLAG_U32_TO_F64 }, + { "f32_convert_i32_s", "aot_intrinsic_i32_to_f32", AOT_INTRINSIC_FLAG_I32_TO_F32 }, + { "f32_convert_i32_u", "aot_intrinsic_u32_to_f32", AOT_INTRINSIC_FLAG_U32_TO_F32 }, + { "f64_convert_i64_s", "aot_intrinsic_i64_to_f64", AOT_INTRINSIC_FLAG_I32_TO_F64 }, + { "f64_convert_i64_u", "aot_intrinsic_u64_to_f64", AOT_INTRINSIC_FLAG_U64_TO_F64 }, + { "f32_convert_i64_s", "aot_intrinsic_i64_to_f32", AOT_INTRINSIC_FLAG_I64_TO_F32 }, + { "f32_convert_i64_u", "aot_intrinsic_u64_to_f32", AOT_INTRINSIC_FLAG_U64_TO_F32 }, + { "i32_trunc_f32_u", "aot_intrinsic_f32_to_u32", AOT_INTRINSIC_FLAG_F32_TO_U32 }, + { "i32_trunc_f32_s", "aot_intrinsic_f32_to_i32", AOT_INTRINSIC_FLAG_F32_TO_I32 }, + { "i32_trunc_f64_u", "aot_intrinsic_f64_to_u32", AOT_INTRINSIC_FLAG_F64_TO_U32 }, + { "i32_trunc_f64_s", "aot_intrinsic_f64_to_i32", AOT_INTRINSIC_FLAG_F64_TO_I32 }, + { "i64_trunc_f64_u", "aot_intrinsic_f64_to_u64", AOT_INTRINSIC_FLAG_F64_TO_U64 }, + { "i64_trunc_f32_s", "aot_intrinsic_f32_to_i64", AOT_INTRINSIC_FLAG_F32_TO_I64 }, + { "i64_trunc_f32_u", "aot_intrinsic_f32_to_u64", AOT_INTRINSIC_FLAG_F32_TO_U64 }, + { "i64_trunc_f64_s", "aot_intrinsic_f64_to_i64", AOT_INTRINSIC_FLAG_F64_TO_I64 }, + { "f32_demote_f64", "aot_intrinsic_f64_to_f32", AOT_INTRINSIC_FLAG_F64_TO_F32 }, + { "f64_promote_f32", "aot_intrinsic_f32_to_f64", AOT_INTRINSIC_FLAG_F32_TO_F64 }, + { "f32_cmp", "aot_intrinsic_f32_cmp", AOT_INTRINSIC_FLAG_F32_CMP }, + { "f64_cmp", "aot_intrinsic_f64_cmp", AOT_INTRINSIC_FLAG_F64_CMP }, + { "i32.const", NULL, AOT_INTRINSIC_FLAG_I32_CONST }, + { "i64.const", NULL, AOT_INTRINSIC_FLAG_I64_CONST }, + { "f32.const", NULL, AOT_INTRINSIC_FLAG_F32_CONST }, + { "f64.const", NULL, AOT_INTRINSIC_FLAG_F64_CONST }, + { "i64.div_s", "aot_intrinsic_i64_div_s", AOT_INTRINSIC_FLAG_I64_DIV_S}, + { "i32.div_s", "aot_intrinsic_i32_div_s", AOT_INTRINSIC_FLAG_I32_DIV_S}, + { "i32.div_u", "aot_intrinsic_i32_div_u", AOT_INTRINSIC_FLAG_I32_DIV_U}, + { "i32.rem_s", "aot_intrinsic_i32_rem_s", AOT_INTRINSIC_FLAG_I32_REM_S}, + { "i32.rem_u", "aot_intrinsic_i32_rem_u", AOT_INTRINSIC_FLAG_I32_REM_U}, + { "i64.div_u", "aot_intrinsic_i64_div_u", AOT_INTRINSIC_FLAG_I64_DIV_U}, + { "i64.rem_s", "aot_intrinsic_i64_rem_s", AOT_INTRINSIC_FLAG_I64_REM_S}, + { "i64.rem_u", "aot_intrinsic_i64_rem_u", AOT_INTRINSIC_FLAG_I64_REM_U}, + { "i64.or", "aot_intrinsic_i64_bit_or", AOT_INTRINSIC_FLAG_I64_BIT_OR}, + { "i64.and", "aot_intrinsic_i64_bit_and", AOT_INTRINSIC_FLAG_I64_BIT_AND}, + { "i64.mul", "aot_intrinsic_i64_mul", AOT_INTRINSIC_FLAG_I64_MUL}, + { "i64.shl", "aot_intrinsic_i64_shl", AOT_INTRINSIC_FLAG_I64_SHL}, + { "i64.shr_s", "aot_intrinsic_i64_shr_s", AOT_INTRINSIC_FLAG_I64_SHR_S}, + { "i64.shr_u", "aot_intrinsic_i64_shr_u", AOT_INTRINSIC_FLAG_I64_SHR_U}, +}; +/* clang-format on */ + +static const uint32 g_intrinsic_count = + sizeof(g_intrinsic_mapping) / sizeof(aot_intrinsic); + +const char * +aot_intrinsic_get_symbol(const char *llvm_intrinsic) +{ + uint32 cnt; + for (cnt = 0; cnt < g_intrinsic_count; cnt++) { + if (!strcmp(llvm_intrinsic, g_intrinsic_mapping[cnt].llvm_intrinsic)) { + return g_intrinsic_mapping[cnt].native_intrinsic; + } + } + return NULL; +} + +static void +add_intrinsic_capability(AOTCompContext *comp_ctx, uint64 flag) +{ + uint64 group = AOT_INTRINSIC_GET_GROUP_FROM_FLAG(flag); + if (group < sizeof(comp_ctx->flags) / sizeof(uint64)) { + comp_ctx->flags[group] |= flag; + } + else { + bh_log(BH_LOG_LEVEL_WARNING, __FILE__, __LINE__, + "intrinsic exceeds max limit."); + } +} + +static void +add_i64_common_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_DIV_S); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_DIV_U); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_REM_S); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_REM_U); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_BIT_OR); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_BIT_AND); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_MUL); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_SHL); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_SHR_S); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_SHR_U); +} + +static void +add_i32_common_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_DIV_S); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_DIV_U); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_REM_S); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_REM_U); +} + +static void +add_f32_common_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_FABS); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_FADD); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_FSUB); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_FMUL); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_FDIV); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_SQRT); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_CMP); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_MIN); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_MAX); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_CEIL); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_FLOOR); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TRUNC); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_RINT); +} + +static void +add_f64_common_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_FABS); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_FADD); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_FSUB); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_FMUL); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_MIN); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_MAX); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_CEIL); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_FLOOR); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TRUNC); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_RINT); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_FDIV); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_SQRT); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_CMP); +} + +static void +add_f32xi32_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_I32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_U32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U32_TO_F32); +} + +static void +add_f64xi32_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_I32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_U32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_TO_F64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U32_TO_F64); +} + +static void +add_f32xi64_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_I64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_U64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U64_TO_F32); +} + +static void +add_f64xi64_intrinsics(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_I64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_U64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_TO_F64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U64_TO_F64); +} + +static void +add_common_float_integer_conversion(AOTCompContext *comp_ctx) +{ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U32_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_TO_F64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U32_TO_F64); + + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U64_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_TO_F64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_U64_TO_F64); + + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_I32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_U32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_I64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_U64); + + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_I32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_U32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_I64); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_U64); + + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_TO_F32); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_TO_F64); +} + +bool +aot_intrinsic_check_capability(const AOTCompContext *comp_ctx, + const char *llvm_intrinsic) +{ + uint32 cnt; + uint64 flag; + uint64 group; + + for (cnt = 0; cnt < g_intrinsic_count; cnt++) { + if (!strcmp(llvm_intrinsic, g_intrinsic_mapping[cnt].llvm_intrinsic)) { + flag = g_intrinsic_mapping[cnt].flag; + group = AOT_INTRINSIC_GET_GROUP_FROM_FLAG(flag); + flag &= AOT_INTRINSIC_FLAG_MASK; + if (group < sizeof(comp_ctx->flags) / sizeof(uint64)) { + if (comp_ctx->flags[group] & flag) { + return true; + } + } + else { + bh_log(BH_LOG_LEVEL_WARNING, __FILE__, __LINE__, + "intrinsic exceeds max limit."); + } + } + } + return false; +} + +void +aot_intrinsic_fill_capability_flags(AOTCompContext *comp_ctx) +{ + uint32 i; + + memset(comp_ctx->flags, 0, sizeof(comp_ctx->flags)); + + /* Intrinsics from command line have highest priority */ + + if (comp_ctx->builtin_intrinsics) { + + /* Handle 'all' group */ + if (strstr(comp_ctx->builtin_intrinsics, "all")) { + for (i = 0; i < g_intrinsic_count; i++) { + add_intrinsic_capability(comp_ctx, g_intrinsic_mapping[i].flag); + } + return; + } + + /* Handle 'i32.common' group */ + if (strstr(comp_ctx->builtin_intrinsics, "i32.common")) { + add_i32_common_intrinsics(comp_ctx); + } + + /* Handle 'i64.common' group */ + if (strstr(comp_ctx->builtin_intrinsics, "i64.common")) { + add_i64_common_intrinsics(comp_ctx); + } + + /* Handle 'fp.common' group */ + if (strstr(comp_ctx->builtin_intrinsics, "fp.common")) { + add_f32_common_intrinsics(comp_ctx); + add_f64_common_intrinsics(comp_ctx); + } + + /* Handle 'f32.common' group */ + if (strstr(comp_ctx->builtin_intrinsics, "f32.common")) { + add_f32_common_intrinsics(comp_ctx); + } + + /* Handle 'f64.common' group */ + if (strstr(comp_ctx->builtin_intrinsics, "f64.common")) { + add_f64_common_intrinsics(comp_ctx); + } + + /* Handle 'f32xi32' group */ + if (strstr(comp_ctx->builtin_intrinsics, "f32xi32")) { + add_f32xi32_intrinsics(comp_ctx); + } + + /* Handle 'f64xi32' group */ + if (strstr(comp_ctx->builtin_intrinsics, "f64xi32")) { + add_f64xi32_intrinsics(comp_ctx); + } + + /* Handle 'f32xi64' group */ + if (strstr(comp_ctx->builtin_intrinsics, "f32xi64")) { + add_f32xi64_intrinsics(comp_ctx); + } + + /* Handle 'f64xi64' group */ + if (strstr(comp_ctx->builtin_intrinsics, "f64xi64")) { + add_f64xi64_intrinsics(comp_ctx); + } + + /* Handle 'fpxint' group */ + if (strstr(comp_ctx->builtin_intrinsics, "fpxint")) { + add_f32xi32_intrinsics(comp_ctx); + add_f64xi32_intrinsics(comp_ctx); + add_f32xi64_intrinsics(comp_ctx); + add_f64xi64_intrinsics(comp_ctx); + } + + /* Handle 'constop' group */ + if (strstr(comp_ctx->builtin_intrinsics, "constop")) { + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_CONST); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_CONST); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_CONST); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_CONST); + } + + /* Handle 'fp.common' group */ + if (strstr(comp_ctx->builtin_intrinsics, "fp.common")) { + add_f32_common_intrinsics(comp_ctx); + add_f64_common_intrinsics(comp_ctx); + } + + /* Handle other single items */ + for (i = 0; i < g_intrinsic_count; i++) { + if (strstr(comp_ctx->builtin_intrinsics, + g_intrinsic_mapping[i].llvm_intrinsic)) { + add_intrinsic_capability(comp_ctx, g_intrinsic_mapping[i].flag); + } + } + + return; + } + + if (!comp_ctx->target_cpu) + return; + + if (!strncmp(comp_ctx->target_arch, "thumb", 5)) { + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_CONST); + add_i32_common_intrinsics(comp_ctx); + if (!strcmp(comp_ctx->target_cpu, "cortex-m7")) { + } + else if (!strcmp(comp_ctx->target_cpu, "cortex-m4")) { + add_f64_common_intrinsics(comp_ctx); + } + else { + add_f32_common_intrinsics(comp_ctx); + add_f64_common_intrinsics(comp_ctx); + add_i64_common_intrinsics(comp_ctx); + add_common_float_integer_conversion(comp_ctx); + } + } + else if (!strncmp(comp_ctx->target_arch, "riscv", 5)) { + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_CONST); + /* + * Note: Use builtin intrinsics since hardware float operation + * will cause rodata relocation + */ + add_f32_common_intrinsics(comp_ctx); + add_f64_common_intrinsics(comp_ctx); + add_common_float_integer_conversion(comp_ctx); + if (!strncmp(comp_ctx->target_arch, "riscv32", 7)) { + add_i64_common_intrinsics(comp_ctx); + } + /* + * LLVM 16 and later expands cttz intrinsic to a table lookup, + * which involves some relocations. (unless ZBB is available, + * in which case the native instructions are preferred over + * the table-based lowering.) + * https://reviews.llvm.org/D128911 + */ +#if LLVM_VERSION_MAJOR >= 16 + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I32_CTZ); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_I64_CTZ); +#endif + } + else if (!strncmp(comp_ctx->target_arch, "xtensa", 6)) { + /* + * Note: Use builtin intrinsics since hardware float operation + * will cause rodata relocation + */ + add_f32_common_intrinsics(comp_ctx); + add_i32_common_intrinsics(comp_ctx); + add_f64_common_intrinsics(comp_ctx); + add_i64_common_intrinsics(comp_ctx); + add_common_float_integer_conversion(comp_ctx); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_CONST); + } + else { + /* + * Use constant value table by default + */ + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F32_CONST); + add_intrinsic_capability(comp_ctx, AOT_INTRINSIC_FLAG_F64_CONST); + } +} + +#endif /* WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 */ diff --git a/core/iwasm/aot/aot_intrinsic.h b/core/iwasm/aot/aot_intrinsic.h new file mode 100644 index 0000000000..e54c82516a --- /dev/null +++ b/core/iwasm/aot/aot_intrinsic.h @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2021 XiaoMi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_INTRINSIC_H +#define _AOT_INTRINSIC_H + +#include "aot_runtime.h" +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 +#include "aot_llvm.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define AOT_INTRINSIC_GROUPS 2 + +/* Use uint64 as flag container: + * - The upper 16 bits are the intrinsic group number + * - The lower 48 bits are the intrinsic capability mask + */ + +#define AOT_INTRINSIC_FLAG(group, number) \ + ((((uint64)(group & 0xffffLL)) << 48) | ((uint64)1 << number)) + +#define AOT_INTRINSIC_FLAG_MASK (0x0000ffffffffffffLL) + +#define AOT_INTRINSIC_GET_GROUP_FROM_FLAG(flag) \ + ((((uint64)flag) >> 48) & 0xffffLL) + +/* clang-format off */ +#define AOT_INTRINSIC_FLAG_F32_FADD AOT_INTRINSIC_FLAG(0, 0) +#define AOT_INTRINSIC_FLAG_F32_FSUB AOT_INTRINSIC_FLAG(0, 1) +#define AOT_INTRINSIC_FLAG_F32_FMUL AOT_INTRINSIC_FLAG(0, 2) +#define AOT_INTRINSIC_FLAG_F32_FDIV AOT_INTRINSIC_FLAG(0, 3) +#define AOT_INTRINSIC_FLAG_F32_FABS AOT_INTRINSIC_FLAG(0, 4) +#define AOT_INTRINSIC_FLAG_F32_CEIL AOT_INTRINSIC_FLAG(0, 5) +#define AOT_INTRINSIC_FLAG_F32_FLOOR AOT_INTRINSIC_FLAG(0, 6) +#define AOT_INTRINSIC_FLAG_F32_TRUNC AOT_INTRINSIC_FLAG(0, 7) +#define AOT_INTRINSIC_FLAG_F32_RINT AOT_INTRINSIC_FLAG(0, 8) +#define AOT_INTRINSIC_FLAG_F32_SQRT AOT_INTRINSIC_FLAG(0, 9) +#define AOT_INTRINSIC_FLAG_F32_COPYSIGN AOT_INTRINSIC_FLAG(0, 10) +#define AOT_INTRINSIC_FLAG_F32_MIN AOT_INTRINSIC_FLAG(0, 11) +#define AOT_INTRINSIC_FLAG_F32_MAX AOT_INTRINSIC_FLAG(0, 12) +#define AOT_INTRINSIC_FLAG_I32_CLZ AOT_INTRINSIC_FLAG(0, 13) +#define AOT_INTRINSIC_FLAG_I32_CTZ AOT_INTRINSIC_FLAG(0, 14) +#define AOT_INTRINSIC_FLAG_I32_POPCNT AOT_INTRINSIC_FLAG(0, 15) +#define AOT_INTRINSIC_FLAG_I32_TO_F32 AOT_INTRINSIC_FLAG(0, 16) +#define AOT_INTRINSIC_FLAG_U32_TO_F32 AOT_INTRINSIC_FLAG(0, 17) +#define AOT_INTRINSIC_FLAG_I32_TO_F64 AOT_INTRINSIC_FLAG(0, 18) +#define AOT_INTRINSIC_FLAG_U32_TO_F64 AOT_INTRINSIC_FLAG(0, 19) +#define AOT_INTRINSIC_FLAG_F32_TO_I32 AOT_INTRINSIC_FLAG(0, 20) +#define AOT_INTRINSIC_FLAG_F32_TO_U32 AOT_INTRINSIC_FLAG(0, 21) +#define AOT_INTRINSIC_FLAG_F32_TO_I64 AOT_INTRINSIC_FLAG(0, 22) +#define AOT_INTRINSIC_FLAG_F32_TO_U64 AOT_INTRINSIC_FLAG(0, 23) +#define AOT_INTRINSIC_FLAG_F32_TO_F64 AOT_INTRINSIC_FLAG(0, 24) +#define AOT_INTRINSIC_FLAG_F32_CMP AOT_INTRINSIC_FLAG(0, 25) +#define AOT_INTRINSIC_FLAG_F32_CONST AOT_INTRINSIC_FLAG(0, 26) +#define AOT_INTRINSIC_FLAG_I32_CONST AOT_INTRINSIC_FLAG(0, 27) +#define AOT_INTRINSIC_FLAG_I32_DIV_U AOT_INTRINSIC_FLAG(0, 28) +#define AOT_INTRINSIC_FLAG_I32_REM_S AOT_INTRINSIC_FLAG(0, 29) +#define AOT_INTRINSIC_FLAG_I32_REM_U AOT_INTRINSIC_FLAG(0, 30) +#define AOT_INTRINSIC_FLAG_I32_DIV_S AOT_INTRINSIC_FLAG(0, 31) + +#define AOT_INTRINSIC_FLAG_F64_FADD AOT_INTRINSIC_FLAG(1, 0) +#define AOT_INTRINSIC_FLAG_F64_FSUB AOT_INTRINSIC_FLAG(1, 1) +#define AOT_INTRINSIC_FLAG_F64_FMUL AOT_INTRINSIC_FLAG(1, 2) +#define AOT_INTRINSIC_FLAG_F64_FDIV AOT_INTRINSIC_FLAG(1, 3) +#define AOT_INTRINSIC_FLAG_F64_FABS AOT_INTRINSIC_FLAG(1, 4) +#define AOT_INTRINSIC_FLAG_F64_CEIL AOT_INTRINSIC_FLAG(1, 5) +#define AOT_INTRINSIC_FLAG_F64_FLOOR AOT_INTRINSIC_FLAG(1, 6) +#define AOT_INTRINSIC_FLAG_F64_TRUNC AOT_INTRINSIC_FLAG(1, 7) +#define AOT_INTRINSIC_FLAG_F64_RINT AOT_INTRINSIC_FLAG(1, 8) +#define AOT_INTRINSIC_FLAG_F64_SQRT AOT_INTRINSIC_FLAG(1, 9) +#define AOT_INTRINSIC_FLAG_F64_COPYSIGN AOT_INTRINSIC_FLAG(1, 10) +#define AOT_INTRINSIC_FLAG_F64_MIN AOT_INTRINSIC_FLAG(1, 11) +#define AOT_INTRINSIC_FLAG_F64_MAX AOT_INTRINSIC_FLAG(1, 12) +#define AOT_INTRINSIC_FLAG_I64_CLZ AOT_INTRINSIC_FLAG(1, 13) +#define AOT_INTRINSIC_FLAG_I64_CTZ AOT_INTRINSIC_FLAG(1, 14) +#define AOT_INTRINSIC_FLAG_I64_POPCNT AOT_INTRINSIC_FLAG(1, 15) +#define AOT_INTRINSIC_FLAG_I64_TO_F32 AOT_INTRINSIC_FLAG(1, 16) +#define AOT_INTRINSIC_FLAG_U64_TO_F32 AOT_INTRINSIC_FLAG(1, 17) +#define AOT_INTRINSIC_FLAG_I64_TO_F64 AOT_INTRINSIC_FLAG(1, 18) +#define AOT_INTRINSIC_FLAG_U64_TO_F64 AOT_INTRINSIC_FLAG(1, 19) +#define AOT_INTRINSIC_FLAG_F64_TO_I32 AOT_INTRINSIC_FLAG(1, 20) +#define AOT_INTRINSIC_FLAG_F64_TO_U32 AOT_INTRINSIC_FLAG(1, 21) +#define AOT_INTRINSIC_FLAG_F64_TO_I64 AOT_INTRINSIC_FLAG(1, 22) +#define AOT_INTRINSIC_FLAG_F64_TO_U64 AOT_INTRINSIC_FLAG(1, 23) +#define AOT_INTRINSIC_FLAG_F64_TO_F32 AOT_INTRINSIC_FLAG(1, 24) +#define AOT_INTRINSIC_FLAG_F64_CMP AOT_INTRINSIC_FLAG(1, 25) +#define AOT_INTRINSIC_FLAG_F64_CONST AOT_INTRINSIC_FLAG(1, 26) +#define AOT_INTRINSIC_FLAG_I64_CONST AOT_INTRINSIC_FLAG(1, 27) +#define AOT_INTRINSIC_FLAG_I64_DIV_S AOT_INTRINSIC_FLAG(1, 28) +#define AOT_INTRINSIC_FLAG_I64_DIV_U AOT_INTRINSIC_FLAG(1, 29) +#define AOT_INTRINSIC_FLAG_I64_REM_S AOT_INTRINSIC_FLAG(1, 30) +#define AOT_INTRINSIC_FLAG_I64_REM_U AOT_INTRINSIC_FLAG(1, 31) +#define AOT_INTRINSIC_FLAG_I64_BIT_OR AOT_INTRINSIC_FLAG(1, 32) +#define AOT_INTRINSIC_FLAG_I64_BIT_AND AOT_INTRINSIC_FLAG(1, 33) +#define AOT_INTRINSIC_FLAG_I64_MUL AOT_INTRINSIC_FLAG(1, 34) +#define AOT_INTRINSIC_FLAG_I64_SHL AOT_INTRINSIC_FLAG(1, 35) +#define AOT_INTRINSIC_FLAG_I64_SHR_S AOT_INTRINSIC_FLAG(1, 36) +#define AOT_INTRINSIC_FLAG_I64_SHR_U AOT_INTRINSIC_FLAG(1, 37) + +/* clang-format on */ + +float32 +aot_intrinsic_fadd_f32(float32 a, float32 b); + +float64 +aot_intrinsic_fadd_f64(float64 a, float64 b); + +float32 +aot_intrinsic_fsub_f32(float32 a, float32 b); + +float64 +aot_intrinsic_fsub_f64(float64 a, float64 b); + +float32 +aot_intrinsic_fmul_f32(float32 a, float32 b); + +float64 +aot_intrinsic_fmul_f64(float64 a, float64 b); + +float32 +aot_intrinsic_fdiv_f32(float32 a, float32 b); + +float64 +aot_intrinsic_fdiv_f64(float64 a, float64 b); + +float32 +aot_intrinsic_fabs_f32(float32 a); + +float64 +aot_intrinsic_fabs_f64(float64 a); + +float32 +aot_intrinsic_ceil_f32(float32 a); + +float64 +aot_intrinsic_ceil_f64(float64 a); + +float32 +aot_intrinsic_floor_f32(float32 a); + +float64 +aot_intrinsic_floor_f64(float64 a); + +float32 +aot_intrinsic_trunc_f32(float32 a); + +float64 +aot_intrinsic_trunc_f64(float64 a); + +float32 +aot_intrinsic_rint_f32(float32 a); + +float64 +aot_intrinsic_rint_f64(float64 a); + +float32 +aot_intrinsic_sqrt_f32(float32 a); + +float64 +aot_intrinsic_sqrt_f64(float64 a); + +float32 +aot_intrinsic_copysign_f32(float32 a, float32 b); + +float64 +aot_intrinsic_copysign_f64(float64 a, float64 b); + +float32 +aot_intrinsic_fmin_f32(float32 a, float32 b); + +float64 +aot_intrinsic_fmin_f64(float64 a, float64 b); + +float32 +aot_intrinsic_fmax_f32(float32 a, float32 b); + +float64 +aot_intrinsic_fmax_f64(float64 a, float64 b); + +uint32 +aot_intrinsic_clz_i32(uint32 type); + +uint64 +aot_intrinsic_clz_i64(uint64 type); + +uint32 +aot_intrinsic_ctz_i32(uint32 type); + +uint64 +aot_intrinsic_ctz_i64(uint64 type); + +uint32 +aot_intrinsic_popcnt_i32(uint32 u); + +uint64 +aot_intrinsic_popcnt_i64(uint64 u); + +float32 +aot_intrinsic_i32_to_f32(int32 i); + +float32 +aot_intrinsic_u32_to_f32(uint32 u); + +float64 +aot_intrinsic_i32_to_f64(int32 i); + +float64 +aot_intrinsic_u32_to_f64(uint32 u); + +float32 +aot_intrinsic_i64_to_f32(int64 i); + +float32 +aot_intrinsic_u64_to_f32(uint64 u); + +float64 +aot_intrinsic_i64_to_f64(int64 i); + +float64 +aot_intrinsic_u64_to_f64(uint64 u); + +int32 +aot_intrinsic_f32_to_i32(float32 f); + +uint32 +aot_intrinsic_f32_to_u32(float32 f); + +int64 +aot_intrinsic_f32_to_i64(float32 f); + +uint64 +aot_intrinsic_f32_to_u64(float32 f); + +int32 +aot_intrinsic_f64_to_i32(float64 f); + +uint32 +aot_intrinsic_f64_to_u32(float64 f); + +int64 +aot_intrinsic_f64_to_i64(float64 f); + +uint64 +aot_intrinsic_f64_to_u64(float64 f); + +float64 +aot_intrinsic_f32_to_f64(float32 f); + +float32 +aot_intrinsic_f64_to_f32(float64 f); + +int32 +aot_intrinsic_f32_cmp(AOTFloatCond cond, float32 lhs, float32 rhs); + +int32 +aot_intrinsic_f64_cmp(AOTFloatCond cond, float64 lhs, float64 rhs); + +int64 +aot_intrinsic_i64_div_s(int64 l, int64 r); + +int32 +aot_intrinsic_i32_div_s(int32 l, int32 r); + +uint32 +aot_intrinsic_i32_div_u(uint32 l, uint32 r); + +int32 +aot_intrinsic_i32_rem_s(int32 l, int32 r); + +uint32 +aot_intrinsic_i32_rem_u(uint32 l, uint32 r); + +uint64 +aot_intrinsic_i64_div_u(uint64 l, uint64 r); + +int64 +aot_intrinsic_i64_rem_s(int64 l, int64 r); + +uint64 +aot_intrinsic_i64_rem_u(uint64 l, uint64 r); + +uint64 +aot_intrinsic_i64_bit_or(uint64 l, uint64 r); + +uint64 +aot_intrinsic_i64_bit_and(uint64 l, uint64 r); + +uint64 +aot_intrinsic_i64_mul(uint64 l, uint64 r); + +uint64 +aot_intrinsic_i64_shl(uint64 l, uint64 r); + +uint64 +aot_intrinsic_i64_shr_s(uint64 l, uint64 r); + +uint64 +aot_intrinsic_i64_shr_u(uint64 l, uint64 r); + +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 +const char * +aot_intrinsic_get_symbol(const char *llvm_intrinsic); + +bool +aot_intrinsic_check_capability(const AOTCompContext *comp_ctx, + const char *llvm_intrinsic); + +void +aot_intrinsic_fill_capability_flags(AOTCompContext *comp_ctx); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _AOT_INTRINSIC_H */ diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 9aa98f7302..121708d669 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -4,23 +4,50 @@ */ #include "aot_runtime.h" -#include "bh_common.h" -#include "bh_memory.h" -#include "bh_log.h" +#include "aot_reloc.h" +#include "bh_platform.h" #include "../common/wasm_runtime_common.h" #include "../common/wasm_native.h" +#include "../common/wasm_loader_common.h" #include "../compilation/aot.h" -#if WASM_ENABLE_JIT != 0 -#include "../compilation/aot_llvm.h" -#include "../interpreter/wasm_loader.h" +#if WASM_ENABLE_AOT_VALIDATOR != 0 +#include "aot_validator.h" #endif +#if WASM_ENABLE_DEBUG_AOT != 0 +#include "debug/elf_parser.h" +#include "debug/jit_debug.h" +#endif + +#if WASM_ENABLE_LINUX_PERF != 0 +#include "aot_perf_map.h" +#endif + +#define YMM_PLT_PREFIX "__ymm@" +#define XMM_PLT_PREFIX "__xmm@" +#define REAL_PLT_PREFIX "__real@" static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, "%s", string); + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, "AOT module load failed: %s", + string); + } +} + +static void +set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, ...) +{ + va_list args; + char buf[128]; + + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + snprintf(error_buf, error_buf_size, "AOT module load failed: %s", buf); + } } #define exchange_uint8(p_data) (void)0 @@ -46,10 +73,15 @@ exchange_uint32(uint8 *p_data) } static void -exchange_uint64(uint8 *pData) +exchange_uint64(uint8 *p_data) { - exchange_uint32(pData); - exchange_uint32(pData + 4); + uint32 value; + + value = *(uint32 *)p_data; + *(uint32 *)p_data = *(uint32 *)(p_data + 4); + *(uint32 *)(p_data + 4) = value; + exchange_uint32(p_data); + exchange_uint32(p_data + 4); } static union { @@ -59,175 +91,332 @@ static union { #define is_little_endian() (__ue.b == 1) -#define CHECK_BUF(buf, buf_end, length) do { \ - if (buf + length > buf_end) { \ - set_error_buf(error_buf, error_buf_size, \ - "Read data failed: unexpected end."); \ - goto fail; \ - } \ - } while (0) +static bool +check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length, + char *error_buf, uint32 error_buf_size) +{ + if ((uintptr_t)buf + length < (uintptr_t)buf + || (uintptr_t)buf + length > (uintptr_t)buf_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + return true; +} + +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + if (!check_buf(buf, buf_end, length, error_buf, error_buf_size)) { \ + goto fail; \ + } \ + } while (0) -static uint8* +static uint8 * align_ptr(const uint8 *p, uint32 b) { uintptr_t v = (uintptr_t)p; uintptr_t m = b - 1; - return (uint8*)((v + m) & ~m); + return (uint8 *)((v + m) & ~m); } static inline uint64 GET_U64_FROM_ADDR(uint32 *addr) { - union { uint64 val; uint32 parts[2]; } u; + union { + uint64 val; + uint32 parts[2]; + } u; u.parts[0] = addr[0]; u.parts[1] = addr[1]; return u.val; } -#define TEMPLATE_READ(p, p_end, res, type) do { \ - if (sizeof(type) != sizeof(uint64)) \ - p = (uint8*)align_ptr(p, sizeof(type)); \ - else \ - /* align 4 bytes if type is uint64 */ \ - p = (uint8*)align_ptr(p, sizeof(uint32)); \ - CHECK_BUF(p, p_end, sizeof(type)); \ - if (sizeof(type) != sizeof(uint64)) \ - res = *(type*)p; \ - else \ - res = (type)GET_U64_FROM_ADDR((uint32*)p); \ - if (!is_little_endian()) \ - exchange_##type((uint8*)&res); \ - p += sizeof(type); \ - } while (0) +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + +static inline uint8 +GET_U8_FROM_ADDR(const uint8 *p) +{ + uint8 res = 0; + bh_assert(p); + + const uint8 *p_aligned = align_ptr(p, 4); + p_aligned = (p_aligned > p) ? p_aligned - 4 : p_aligned; + + uint32 buf32 = *(const uint32 *)p_aligned; + const uint8 *pbuf = (const uint8 *)&buf32; + + res = *(uint8 *)(pbuf + (p - p_aligned)); + + return res; +} + +static inline uint16 +GET_U16_FROM_ADDR(const uint8 *p) +{ + uint16 res = 0; + bh_assert(p); + + const uint8 *p_aligned = align_ptr(p, 4); + p_aligned = (p_aligned > p) ? p_aligned - 4 : p_aligned; + + uint32 buf32 = *(const uint32 *)p_aligned; + const uint8 *pbuf = (const uint8 *)&buf32; + + res = *(uint16 *)(pbuf + (p - p_aligned)); + + return res; +} + +#define TEMPLATE_READ(p, p_end, res, type) \ + do { \ + if (sizeof(type) != sizeof(uint64)) \ + p = (uint8 *)align_ptr(p, sizeof(type)); \ + else \ + /* align 4 bytes if type is uint64 */ \ + p = (uint8 *)align_ptr(p, sizeof(uint32)); \ + CHECK_BUF(p, p_end, sizeof(type)); \ + if (sizeof(type) == sizeof(uint8)) \ + res = GET_U8_FROM_ADDR(p); \ + else if (sizeof(type) == sizeof(uint16)) \ + res = GET_U16_FROM_ADDR(p); \ + else if (sizeof(type) == sizeof(uint32)) \ + res = *(type *)p; \ + else \ + res = (type)GET_U64_FROM_ADDR((uint32 *)p); \ + if (!is_little_endian()) \ + exchange_##type((uint8 *)&res); \ + p += sizeof(type); \ + } while (0) + +#define read_byte_array(p, p_end, addr, len) \ + do { \ + CHECK_BUF(p, p_end, len); \ + bh_memcpy_wa(addr, len, p, len); \ + p += len; \ + } while (0) + +#define read_string(p, p_end, str) \ + do { \ + if (!(str = load_string((uint8 **)&p, p_end, module, \ + is_load_from_file_buf, true, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#else /* else of (WASM_ENABLE_WORD_ALIGN_READ != 0) */ + +#define TEMPLATE_READ(p, p_end, res, type) \ + do { \ + if (sizeof(type) != sizeof(uint64)) \ + p = (uint8 *)align_ptr(p, sizeof(type)); \ + else \ + /* align 4 bytes if type is uint64 */ \ + p = (uint8 *)align_ptr(p, sizeof(uint32)); \ + CHECK_BUF(p, p_end, sizeof(type)); \ + if (sizeof(type) != sizeof(uint64)) \ + res = *(type *)p; \ + else \ + res = (type)GET_U64_FROM_ADDR((uint32 *)p); \ + if (!is_little_endian()) \ + exchange_##type((uint8 *)&res); \ + p += sizeof(type); \ + } while (0) + +/* NOLINTBEGIN, disable lint for this region with clang-tidy */ + +#define read_byte_array(p, p_end, addr, len) \ + do { \ + CHECK_BUF(p, p_end, len); \ + bh_memcpy_s(addr, len, p, len); \ + p += len; \ + } while (0) + +#define read_string(p, p_end, str) \ + do { \ + if (!(str = load_string((uint8 **)&p, p_end, module, \ + is_load_from_file_buf, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#endif /* end of (WASM_ENABLE_WORD_ALIGN_READ != 0) */ #define read_uint8(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint8) #define read_uint16(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint16) #define read_uint32(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint32) #define read_uint64(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint64) -#define read_byte_array(p, p_end, addr, len) do { \ - CHECK_BUF(p, p_end, len); \ - memcpy(addr, p, len); \ - p += len; \ - } while (0) - -#define read_string(p, p_end, str) do { \ - uint16 str_len; \ - read_uint16(p, p_end, str_len); \ - CHECK_BUF(p, p_end, str_len); \ - if (!(str = const_str_set_insert \ - (p, str_len, module, \ - error_buf, error_buf_size))) { \ - goto fail; \ - } \ - p += str_len; \ - } while (0) +/* NOLINTEND */ /* Legal values for bin_type */ -#define BIN_TYPE_ELF32L 0 /* 32-bit little endian */ -#define BIN_TYPE_ELF32B 1 /* 32-bit big endian */ -#define BIN_TYPE_ELF64L 2 /* 64-bit little endian */ -#define BIN_TYPE_ELF64B 3 /* 64-bit big endian */ +#define BIN_TYPE_ELF32L 0 /* 32-bit little endian */ +#define BIN_TYPE_ELF32B 1 /* 32-bit big endian */ +#define BIN_TYPE_ELF64L 2 /* 64-bit little endian */ +#define BIN_TYPE_ELF64B 3 /* 64-bit big endian */ +#define BIN_TYPE_COFF32 4 /* 32-bit little endian */ +#define BIN_TYPE_COFF64 6 /* 64-bit little endian */ /* Legal values for e_type (object file type). */ -#define E_TYPE_NONE 0 /* No file type */ -#define E_TYPE_REL 1 /* Relocatable file */ -#define E_TYPE_EXEC 2 /* Executable file */ -#define E_TYPE_DYN 3 /* Shared object file */ +#define E_TYPE_NONE 0 /* No file type */ +#define E_TYPE_REL 1 /* Relocatable file */ +#define E_TYPE_EXEC 2 /* Executable file */ +#define E_TYPE_DYN 3 /* Shared object file */ +#define E_TYPE_XIP 4 /* eXecute In Place file */ /* Legal values for e_machine (architecture). */ -#define E_MACHINE_386 3 /* Intel 80386 */ -#define E_MACHINE_MIPS 8 /* MIPS R3000 big-endian */ -#define E_MACHINE_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ -#define E_MACHINE_ARM 40 /* ARM/Thumb */ -#define E_MACHINE_ARC 45 /* Argonaut RISC Core */ -#define E_MACHINE_IA_64 50 /* Intel Merced */ -#define E_MACHINE_MIPS_X 51 /* Stanford MIPS-X */ -#define E_MACHINE_X86_64 62 /* AMD x86-64 architecture */ -#define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define E_MACHINE_386 3 /* Intel 80386 */ +#define E_MACHINE_MIPS 8 /* MIPS R3000 big-endian */ +#define E_MACHINE_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ +#define E_MACHINE_ARM 40 /* ARM/Thumb */ +#define E_MACHINE_AARCH64 183 /* AArch64 */ +#define E_MACHINE_ARC 45 /* Argonaut RISC Core */ +#define E_MACHINE_IA_64 50 /* Intel Merced */ +#define E_MACHINE_MIPS_X 51 /* Stanford MIPS-X */ +#define E_MACHINE_X86_64 62 /* AMD x86-64 architecture */ +#define E_MACHINE_ARC_COMPACT 93 /* ARC International ARCompact */ +#define E_MACHINE_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */ +#define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define E_MACHINE_RISCV 243 /* RISC-V 32/64 */ +#define E_MACHINE_WIN_I386 0x14c /* Windows i386 architecture */ +#define E_MACHINE_WIN_X86_64 0x8664 /* Windows x86-64 architecture */ /* Legal values for e_version */ -#define E_VERSION_CURRENT 1 /* Current version */ +#define E_VERSION_CURRENT 1 /* Current version */ -static char* -const_str_set_insert(const uint8 *str, int32 len, AOTModule *module, - char* error_buf, uint32 error_buf_size) +static void * +loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) { - HashMap *set = module->const_str_set; - char *c_str = wasm_malloc((uint32)len + 1), *value; + void *mem; - if (!c_str) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); return NULL; } - bh_memcpy_s(c_str, (uint32)(len + 1), str, (uint32)len); - c_str[len] = '\0'; + memset(mem, 0, (uint32)size); + return mem; +} - if ((value = bh_hash_map_find(set, c_str))) { - wasm_free(c_str); - return value; +static void * +loader_mmap(uint32 size, bool prot_exec, char *error_buf, uint32 error_buf_size) +{ + int map_prot = + MMAP_PROT_READ | MMAP_PROT_WRITE | (prot_exec ? MMAP_PROT_EXEC : 0); + int map_flags; + void *mem; + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) +#if !defined(__APPLE__) && !defined(BH_PLATFORM_LINUX_SGX) \ + && !defined(BH_PLATFORM_NUTTX) + /* The mmapped AOT data and code in 64-bit targets had better be in + range 0 to 2G, or aot loader may fail to apply some relocations, + e.g., R_X86_64_32/R_X86_64_32S/R_X86_64_PC32/R_RISCV_32. + We try to mmap with MMAP_MAP_32BIT flag first, and if fails, mmap + again without the flag. */ + /* sgx_tprotect_rsrv_mem() and sgx_alloc_rsrv_mem() will ignore flags */ + map_flags = MMAP_MAP_32BIT; + if ((mem = os_mmap(NULL, size, map_prot, map_flags, + os_get_invalid_handle()))) { + /* Test whether the mmapped memory in the first 2 Gigabytes of the + process address space */ + if ((uintptr_t)mem >= INT32_MAX) + LOG_WARNING( + "Warning: loader mmap memory address is not in the first 2 " + "Gigabytes of the process address space."); + return mem; } +#endif +#endif - if (!bh_hash_map_insert(set, c_str, c_str)) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "insert string to hash map failed."); - wasm_free(c_str); + map_flags = MMAP_MAP_NONE; + if (!(mem = os_mmap(NULL, size, map_prot, map_flags, + os_get_invalid_handle()))) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); return NULL; } - - return c_str; + return mem; } -static void -get_current_target(char *target_buf, uint32 target_buf_size) -{ -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - snprintf(target_buf, target_buf_size, "x86_64"); -#elif defined(BUILD_TARGET_X86_32) - snprintf(target_buf, target_buf_size, "i386"); -#elif defined(BUILD_TARGET_ARM) \ - || defined(BUILD_TARGET_ARM_VFP) \ - || defined(BUILD_TARGET_THUMB) \ - || defined(BUILD_TARGET_THUMB_VFP) - char *build_target = BUILD_TARGET; - char *p = target_buf, *p_end; - snprintf(target_buf, target_buf_size, "%s", build_target); - p_end = p + strlen(target_buf); - while (p < p_end) { - if (*p >= 'A' && *p <= 'Z') - *p++ += 'a' - 'A'; - else - p++; +static char * +load_string(uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + bool is_load_from_file_buf, +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + bool is_vram_word_align, +#endif + char *error_buf, uint32 error_buf_size) +{ + uint8 *p = *p_buf; + const uint8 *p_end = buf_end; + char *str; + uint16 str_len; + + read_uint16(p, p_end, str_len); + CHECK_BUF(p, p_end, str_len); + + if (str_len == 0) { + str = ""; + } +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + else if (is_vram_word_align) { + if (!(str = aot_const_str_set_insert((uint8 *)p, str_len, module, + is_vram_word_align, error_buf, + error_buf_size))) { + goto fail; + } + } +#endif + else if (is_load_from_file_buf) { + /* The string is always terminated with '\0', use it directly. + * In this case, the file buffer can be referred to after loading. + */ + if (p[str_len - 1] != '\0') + goto fail; + + str = (char *)p; } - if (!strcmp(target_buf, "arm")) - snprintf(target_buf, target_buf_size, "armv4"); - else if (!strcmp(target_buf, "thumb")) - snprintf(target_buf, target_buf_size, "thumbv4t"); -#elif defined(BUILD_TARGET_MIPS) - snprintf(target_buf, target_buf_size, "mips"); -#elif defined(BUILD_TARGET_XTENSA) - snprintf(target_buf, target_buf_size, "xtensa"); + else { + /* Load from sections, the file buffer cannot be referred to + after loading, we must create another string and insert it + into const string set */ + if (p[str_len - 1] != '\0') + goto fail; + + if (!(str = aot_const_str_set_insert((uint8 *)p, str_len, module, +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + is_vram_word_align, #endif + error_buf, error_buf_size))) { + goto fail; + } + } + p += str_len; + + *p_buf = p; + return str; +fail: + return NULL; } static bool -get_aot_file_target(AOTTargetInfo *target_info, - char *target_buf, uint32 target_buf_size, - char *error_buf, uint32 error_buf_size) +get_aot_file_target(AOTTargetInfo *target_info, char *target_buf, + uint32 target_buf_size, char *error_buf, + uint32 error_buf_size) { char *machine_type = NULL; switch (target_info->e_machine) { case E_MACHINE_X86_64: + case E_MACHINE_WIN_X86_64: machine_type = "x86_64"; break; case E_MACHINE_386: + case E_MACHINE_WIN_I386: machine_type = "i386"; break; case E_MACHINE_ARM: + case E_MACHINE_AARCH64: + /* TODO: this will make following `strncmp()` ~L392 unnecessary. + * Use const strings here */ machine_type = target_info->arch; break; case E_MACHINE_MIPS: @@ -236,19 +425,23 @@ get_aot_file_target(AOTTargetInfo *target_info, case E_MACHINE_XTENSA: machine_type = "xtensa"; break; + case E_MACHINE_RISCV: + machine_type = "riscv"; + break; + case E_MACHINE_ARC_COMPACT: + case E_MACHINE_ARC_COMPACT2: + machine_type = "arc"; + break; default: - if (error_buf) - snprintf(error_buf, error_buf_size, - "AOT module load failed: unknown machine type %d.", - target_info->e_machine); + set_error_buf_v(error_buf, error_buf_size, + "unknown machine type %d", target_info->e_machine); return false; } if (strncmp(target_info->arch, machine_type, strlen(machine_type))) { - if (error_buf) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "machine type (%s) isn't consistent with target type (%s).", - machine_type, target_info->arch); + set_error_buf_v( + error_buf, error_buf_size, + "machine type (%s) isn't consistent with target type (%s)", + machine_type, target_info->arch); return false; } snprintf(target_buf, target_buf_size, "%s", target_info->arch); @@ -256,8 +449,8 @@ get_aot_file_target(AOTTargetInfo *target_info, } static bool -check_machine_info(AOTTargetInfo *target_info, - char *error_buf, uint32 error_buf_size) +check_machine_info(AOTTargetInfo *target_info, char *error_buf, + uint32 error_buf_size) { char target_expected[32], target_got[32]; @@ -267,23 +460,82 @@ check_machine_info(AOTTargetInfo *target_info, error_buf, error_buf_size)) return false; - if (strcmp(target_expected, target_got)) { - if (error_buf) { - snprintf(error_buf, error_buf_size, - "AOT module load failed: invalid target type, " - "expected %s but got %s.", - target_expected, target_got); - } + if (strncmp(target_expected, target_got, strlen(target_expected))) { + set_error_buf_v(error_buf, error_buf_size, + "invalid target type, expected %s but got %s", + target_expected, target_got); + return false; + } + + return true; +} + +static bool +check_feature_flags(char *error_buf, uint32 error_buf_size, + uint64 feature_flags) +{ +#if WASM_ENABLE_SIMD == 0 + if (feature_flags & WASM_FEATURE_SIMD_128BIT) { + set_error_buf(error_buf, error_buf_size, + "SIMD is not enabled in this build"); + return false; + } +#endif + +#if WASM_ENABLE_BULK_MEMORY == 0 + if (feature_flags & WASM_FEATURE_BULK_MEMORY) { + set_error_buf(error_buf, error_buf_size, + "bulk memory is not enabled in this build"); + return false; + } +#endif + +#if WASM_ENABLE_THREAD_MGR == 0 + if (feature_flags & WASM_FEATURE_MULTI_THREAD) { + set_error_buf(error_buf, error_buf_size, + "thread is not enabled in this build"); + return false; + } +#endif + +#if WASM_ENABLE_REF_TYPES == 0 + if (feature_flags & WASM_FEATURE_REF_TYPES) { + set_error_buf(error_buf, error_buf_size, + "reference types is not enabled in this build"); + return false; + } +#endif + +#if WASM_ENABLE_GC == 0 + if (feature_flags & WASM_FEATURE_GARBAGE_COLLECTION) { + set_error_buf(error_buf, error_buf_size, + "garbage collection is not enabled in this build"); return false; } +#endif return true; } +#if WASM_ENABLE_GC != 0 +static WASMRefType * +reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type, + char *error_buf, uint32 error_buf_size) +{ + WASMRefType *ret = wasm_reftype_set_insert(ref_type_set, ref_type); + + if (!ret) { + set_error_buf(error_buf, error_buf_size, + "insert ref type to hash set failed"); + } + return ret; +} +#endif + static bool load_target_info_section(const uint8 *buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) + AOTModule *module, char *error_buf, + uint32 error_buf_size) { AOTTargetInfo target_info; const uint8 *p = buf, *p_end = buf_end; @@ -295,258 +547,1591 @@ load_target_info_section(const uint8 *buf, const uint8 *buf_end, read_uint16(p, p_end, target_info.e_machine); read_uint32(p, p_end, target_info.e_version); read_uint32(p, p_end, target_info.e_flags); - read_uint32(p, p_end, target_info.reserved); - read_byte_array(p, p_end, - target_info.arch, sizeof(target_info.arch)); + read_uint64(p, p_end, target_info.feature_flags); + read_uint64(p, p_end, target_info.reserved); + read_byte_array(p, p_end, target_info.arch, sizeof(target_info.arch)); + + if (target_info.arch[sizeof(target_info.arch) - 1] != '\0') { + set_error_buf(error_buf, error_buf_size, "invalid arch string"); + return false; + } if (p != buf_end) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid section size."); + set_error_buf(error_buf, error_buf_size, "invalid section size"); return false; } /* Check target endian type */ is_target_little_endian = target_info.bin_type & 1 ? false : true; if (is_little_endian() != is_target_little_endian) { - if (error_buf) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid target endian type, expected %s but got %s.", - is_little_endian() ? "little endian" : "big endian", - is_target_little_endian ? "little endian" : "big endian"); + set_error_buf_v(error_buf, error_buf_size, + "invalid target endian type, expected %s but got %s", + is_little_endian() ? "little endian" : "big endian", + is_target_little_endian ? "little endian" + : "big endian"); return false; } /* Check target bit width */ is_target_64_bit = target_info.bin_type & 2 ? true : false; - if ((sizeof(void*) == 8 ? true : false) != is_target_64_bit) { - if (error_buf) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid target bit width, expected %s but got %s.", - sizeof(void*) == 8 ? "64-bit" : "32-bit", - is_target_64_bit ? "64-bit" : "32-bit"); + if ((sizeof(void *) == 8 ? true : false) != is_target_64_bit) { + set_error_buf_v(error_buf, error_buf_size, + "invalid target bit width, expected %s but got %s", + sizeof(void *) == 8 ? "64-bit" : "32-bit", + is_target_64_bit ? "64-bit" : "32-bit"); return false; } /* Check target elf file type */ - if (target_info.e_type != E_TYPE_REL) { + if (target_info.e_type != E_TYPE_REL && target_info.e_type != E_TYPE_XIP) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid object file type, " - "expected relocatable file type but got others."); + "invalid object file type, " + "expected relocatable or XIP file type but got others"); return false; } + /* for backwards compatibility with previous wamrc aot files */ + if (!strcmp(target_info.arch, "arm64") + || !strcmp(target_info.arch, "aarch64")) + bh_strcpy_s(target_info.arch, sizeof(target_info.arch), "aarch64v8"); + /* Check machine info */ if (!check_machine_info(&target_info, error_buf, error_buf_size)) { return false; } if (target_info.e_version != E_VERSION_CURRENT) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid elf file version."); + set_error_buf(error_buf, error_buf_size, "invalid elf file version"); return false; } - return true; +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + module->feature_flags = target_info.feature_flags; +#endif + + /* Finally, check feature flags */ + return check_feature_flags(error_buf, error_buf_size, + target_info.feature_flags); fail: return false; } -static void -destroy_mem_init_data_list(AOTMemInitData **data_list, uint32 count, - bool is_jit_mode) +static void * +get_native_symbol_by_name(const char *name) { - if (!is_jit_mode) { - uint32 i; - for (i = 0; i < count; i++) - if (data_list[i]) - wasm_free(data_list[i]); - wasm_free(data_list); + void *func = NULL; + uint32 symnum = 0; + SymbolMap *sym = NULL; + + sym = get_target_symbol_map(&symnum); + + while (symnum && symnum--) { + if (strcmp(sym->symbol_name, name) == 0) { + func = sym->symbol_addr; + break; + } + sym++; } + + return func; } static bool -load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) +str2uint32(const char *buf, uint32 *p_res); + +static bool +str2uint64(const char *buf, uint64 *p_res); + +static bool +load_native_symbol_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, bool is_load_from_file_buf, + char *error_buf, uint32 error_buf_size) { - const uint8 *buf = *p_buf; - AOTMemInitData **data_list; - uint64 size; - uint32 i; + const uint8 *p = buf, *p_end = buf_end; + uint32 cnt; + int32 i; + const char *symbol; - /* Allocate memory */ - size = sizeof(AOTMemInitData *) * (uint64)module->mem_init_data_count; - if (size >= UINT32_MAX - || !(module->mem_init_data_list = - data_list = wasm_malloc((uint32)size))) { + if (module->native_symbol_list) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + "duplicated native symbol section"); return false; } - memset(data_list, 0, size); + read_uint32(p, p_end, cnt); - /* Create each memory data segment */ - for (i = 0; i < module->mem_init_data_count; i++) { - uint32 init_expr_type, byte_count; - uint64 init_expr_value; - read_uint32(buf, buf_end, init_expr_type); - read_uint64(buf, buf_end, init_expr_value); - read_uint32(buf, buf_end, byte_count); - size = offsetof(AOTMemInitData, bytes) + (uint64)byte_count; - if (size >= UINT32_MAX - || !(data_list[i] = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); - return false; + if (cnt > 0) { + uint64 list_size = cnt * (uint64)sizeof(void *); + module->native_symbol_list = + loader_malloc(list_size, error_buf, error_buf_size); + if (module->native_symbol_list == NULL) { + goto fail; } - data_list[i]->offset.init_expr_type = (uint8)init_expr_type; - data_list[i]->offset.u.i64 = (int64)init_expr_value; - data_list[i]->byte_count = byte_count; - read_byte_array(buf, buf_end, - data_list[i]->bytes, data_list[i]->byte_count); + for (i = cnt - 1; i >= 0; i--) { + read_string(p, p_end, symbol); + if (!strlen(symbol)) + continue; + + if (!strncmp(symbol, "f32#", 4) || !strncmp(symbol, "i32#", 4)) { + uint32 u32; + /* Resolve the raw int bits of f32 const */ + if (!str2uint32(symbol + 4, &u32)) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); + goto fail; + } + *(uint32 *)(&module->native_symbol_list[i]) = u32; + } + else if (!strncmp(symbol, "f64#", 4) + || !strncmp(symbol, "i64#", 4)) { + uint64 u64; + /* Resolve the raw int bits of f64 const */ + if (!str2uint64(symbol + 4, &u64)) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); + goto fail; + } + *(uint64 *)(&module->native_symbol_list[i]) = u64; + } + else if (!strncmp(symbol, "__ignore", 8)) { + /* Padding bytes to make f64 on 8-byte aligned address, + or it is the second 32-bit slot in 32-bit system */ + continue; + } + else { + module->native_symbol_list[i] = + get_native_symbol_by_name(symbol); + if (module->native_symbol_list[i] == NULL) { + set_error_buf_v(error_buf, error_buf_size, + "missing native symbol: %s", symbol); + goto fail; + } + } + } } - *p_buf = buf; return true; fail: return false; } static bool -load_memory_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) +load_name_section(const uint8 *buf, const uint8 *buf_end, AOTModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) { - const uint8 *buf = *p_buf; +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + const uint8 *p = buf, *p_end = buf_end; + uint32 *aux_func_indexes; + const char **aux_func_names; + uint32 name_type, subsection_size; + uint32 previous_name_type = 0; + uint32 num_func_name; + uint32 func_index; + uint32 previous_func_index = ~0U; + uint32 name_index; + int i = 0; + uint32 name_len; + uint64 size; - read_uint32(buf, buf_end, module->num_bytes_per_page); - read_uint32(buf, buf_end, module->mem_init_page_count); - read_uint32(buf, buf_end, module->mem_max_page_count); - read_uint32(buf, buf_end, module->mem_init_data_count); + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } - /* load memory init data list */ - if (module->mem_init_data_count > 0 - && !load_mem_init_data_list(&buf, buf_end, module, - error_buf, error_buf_size)) + read_uint32(p, p_end, name_len); + + if (name_len != 4 || p + name_len > p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); return false; + } + + if (memcmp(p, "name", 4) != 0) { + set_error_buf(error_buf, error_buf_size, "invalid custom name section"); + return false; + } + p += name_len; + + while (p < p_end) { + read_uint32(p, p_end, name_type); + if (i != 0) { + if (name_type == previous_name_type) { + set_error_buf(error_buf, error_buf_size, + "duplicate sub-section"); + return false; + } + if (name_type < previous_name_type) { + set_error_buf(error_buf, error_buf_size, + "out-of-order sub-section"); + return false; + } + } + previous_name_type = name_type; + read_uint32(p, p_end, subsection_size); + CHECK_BUF(p, p_end, subsection_size); + switch (name_type) { + case SUB_SECTION_TYPE_FUNC: + if (subsection_size) { + read_uint32(p, p_end, num_func_name); + if (num_func_name + > module->import_func_count + module->func_count) { + set_error_buf(error_buf, error_buf_size, + "function name count out of bounds"); + return false; + } + module->aux_func_name_count = num_func_name; + + /* Allocate memory */ + size = sizeof(uint32) * (uint64)module->aux_func_name_count; + if (!(aux_func_indexes = module->aux_func_indexes = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + size = + sizeof(char **) * (uint64)module->aux_func_name_count; + if (!(aux_func_names = module->aux_func_names = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + for (name_index = 0; name_index < num_func_name; + name_index++) { + read_uint32(p, p_end, func_index); + if (name_index != 0 + && func_index == previous_func_index) { + set_error_buf(error_buf, error_buf_size, + "duplicate function name"); + return false; + } + if (name_index != 0 + && func_index < previous_func_index) { + set_error_buf(error_buf, error_buf_size, + "out-of-order function index "); + return false; + } + if (func_index + >= module->import_func_count + module->func_count) { + set_error_buf(error_buf, error_buf_size, + "function index out of bounds"); + return false; + } + previous_func_index = func_index; + *(aux_func_indexes + name_index) = func_index; + read_string(p, p_end, *(aux_func_names + name_index)); +#if 0 + LOG_DEBUG("func_index %u -> aux_func_name = %s\n", + func_index, *(aux_func_names + name_index)); +#endif + } + } + break; + case SUB_SECTION_TYPE_MODULE: /* TODO: Parse for module subsection + */ + case SUB_SECTION_TYPE_LOCAL: /* TODO: Parse for local subsection */ + default: + p = p + subsection_size; + break; + } + i++; + } - *p_buf = buf; return true; fail: return false; +#else + return true; +#endif /* WASM_ENABLE_CUSTOM_NAME_SECTION != 0 */ } -static void -destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count, - bool is_jit_mode) -{ - if (!is_jit_mode) { - uint32 i; - for (i = 0; i < count; i++) - if (data_list[i]) - wasm_free(data_list[i]); - wasm_free(data_list); - } -} - +#if WASM_ENABLE_STRINGREF != 0 static bool -load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) +load_string_literal_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, bool is_load_from_file_buf, + char *error_buf, uint32 error_buf_size) { - const uint8 *buf = *p_buf; - AOTTableInitData **data_list; + const uint8 *p = buf, *p_end = buf_end; + uint32 reserved = 0, string_count = 0, i; uint64 size; - uint32 i; - /* Allocate memory */ - size = sizeof(AOTTableInitData *) * (uint64)module->table_init_data_count; - if (size >= UINT32_MAX - || !(module->table_init_data_list = - data_list = wasm_malloc((uint32)size))) { + read_uint32(p, p_end, reserved); + if (reserved != 0) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); - return false; + "invalid reserved slot in string literal count"); + goto fail; } - memset(data_list, 0, size); + read_uint32(p, p_end, string_count); + if (string_count == 0) { + set_error_buf(error_buf, error_buf_size, + "invalid string literal count"); + goto fail; + } + module->string_literal_count = string_count; - /* Create each table data segment */ - for (i = 0; i < module->table_init_data_count; i++) { - uint32 init_expr_type, func_index_count; - uint64 init_expr_value, size1; + size = (uint64)sizeof(char *) * string_count; + if (!(module->string_literal_ptrs = + loader_malloc(size, error_buf, error_buf_size))) { + goto fail; + } - read_uint32(buf, buf_end, init_expr_type); - read_uint64(buf, buf_end, init_expr_value); - read_uint32(buf, buf_end, func_index_count); + size = (uint64)sizeof(uint32) * string_count; + if (!(module->string_literal_lengths = + loader_malloc(size, error_buf, error_buf_size))) { + goto fail; + } - size1 = sizeof(uint32) * (uint64)func_index_count; - size = offsetof(AOTTableInitData, func_indexes) + size1; - if (size >= UINT32_MAX - || !(data_list[i] = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); - return false; - } + for (i = 0; i < string_count; i++) { + read_uint32(p, p_end, module->string_literal_lengths[i]); + } - data_list[i]->offset.init_expr_type = (uint8)init_expr_type; - data_list[i]->offset.u.i64 = (int64)init_expr_value; - data_list[i]->func_index_count = func_index_count; - read_byte_array(buf, buf_end, data_list[i]->func_indexes, size1); + for (i = 0; i < string_count; i++) { + module->string_literal_ptrs[i] = p; + p += module->string_literal_lengths[i]; } - *p_buf = buf; return true; + fail: return false; } +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ static bool -load_table_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) +load_custom_section(const uint8 *buf, const uint8 *buf_end, AOTModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) { - const uint8 *buf = *p_buf; + const uint8 *p = buf, *p_end = buf_end; + uint32 sub_section_type; - read_uint32(buf, buf_end, module->table_size); - read_uint32(buf, buf_end, module->table_init_data_count); + read_uint32(p, p_end, sub_section_type); + buf = p; - /* load table init data list */ - if (module->table_init_data_count > 0 - && !load_table_init_data_list(&buf, buf_end, module, - error_buf, error_buf_size)) - return false; + switch (sub_section_type) { + case AOT_CUSTOM_SECTION_NATIVE_SYMBOL: + if (!load_native_symbol_section(buf, buf_end, module, + is_load_from_file_buf, error_buf, + error_buf_size)) + goto fail; + break; + case AOT_CUSTOM_SECTION_NAME: + if (!load_name_section(buf, buf_end, module, is_load_from_file_buf, + error_buf, error_buf_size)) + LOG_VERBOSE("Load name section failed."); + else + LOG_VERBOSE("Load name section success."); + break; +#if WASM_ENABLE_STRINGREF != 0 + case AOT_CUSTOM_SECTION_STRING_LITERAL: + if (!load_string_literal_section(buf, buf_end, module, + is_load_from_file_buf, error_buf, + error_buf_size)) + goto fail; + break; +#endif +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + case AOT_CUSTOM_SECTION_RAW: + { + const char *section_name; + WASMCustomSection *section; - *p_buf = buf; - return true; -fail: - return false; -} + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + goto fail; + } -static void -destroy_func_types(AOTFuncType **func_types, uint32 count, bool is_jit_mode) -{ - if (!is_jit_mode) { - uint32 i; - for (i = 0; i < count; i++) - if (func_types[i]) - wasm_free(func_types[i]); - wasm_free(func_types); - } -} + read_string(p, p_end, section_name); -static bool -load_func_types(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) + section = loader_malloc(sizeof(WASMCustomSection), error_buf, + error_buf_size); + if (!section) { + goto fail; + } + + section->name_addr = (char *)section_name; + section->name_len = (uint32)strlen(section_name); + section->content_addr = (uint8 *)p; + section->content_len = (uint32)(p_end - p); + + section->next = module->custom_section_list; + module->custom_section_list = section; + LOG_VERBOSE("Load custom section [%s] success.", section_name); + break; + } +#endif /* end of WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 */ + default: + break; + } + + return true; +fail: + return false; +} + +#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0 +static void +destroy_init_expr(InitializerExpression *expr) +{ +#if WASM_ENABLE_GC != 0 + if (expr->init_expr_type == INIT_EXPR_TYPE_STRUCT_NEW + || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW + || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + wasm_runtime_free(expr->u.unary.v.data); + } +#endif + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + /* free left expr and right expr for binary operand */ + if (!is_expr_binary_op(expr->init_expr_type)) { + return; + } + if (expr->u.binary.l_expr) { + destroy_init_expr_recursive(expr->u.binary.l_expr); + } + if (expr->u.binary.r_expr) { + destroy_init_expr_recursive(expr->u.binary.r_expr); + } + expr->u.binary.l_expr = expr->u.binary.r_expr = NULL; +#endif +} +#endif /* end of WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0 \ + */ + +static void +destroy_import_memories(AOTImportMemory *import_memories) +{ + wasm_runtime_free(import_memories); +} + +/** + * Free memory initialization data segments. + * + * @param module the AOT module containing the data + * @param data_list array of memory initialization data segments to free + * @param count number of segments in the data_list array + */ + +static void +destroy_mem_init_data_list(AOTModule *module, AOTMemInitData **data_list, + uint32 count) +{ + uint32 i; + /* Free each memory initialization data segment */ + for (i = 0; i < count; i++) + if (data_list[i]) { + /* If the module owns the binary data, free the bytes buffer */ + if (module->is_binary_freeable && data_list[i]->bytes) + wasm_runtime_free(data_list[i]->bytes); + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(&data_list[i]->offset); +#endif + /* Free the data segment structure itself */ + wasm_runtime_free(data_list[i]); + } + /* Free the array of data segment pointers */ + wasm_runtime_free(data_list); +} + +static bool +load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + InitializerExpression *expr, char *error_buf, + uint32 error_buf_size); + +/** + * Load memory initialization data segments from the AOT module. + * + * This function reads memory initialization data segments from the buffer and + * creates AOTMemInitData structures for each segment. The data can either be + * cloned into new memory or referenced directly from the buffer. + * + * @param p_buf pointer to buffer containing memory init data + * @param buf_end end of buffer + * @param module the AOT module being loaded + * @param error_buf buffer for error messages + * @param error_buf_size size of error buffer + * + * @return true if successful, false if error occurred + */ + +static bool +load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTMemInitData **data_list; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTMemInitData *) * (uint64)module->mem_init_data_count; + if (!(module->mem_init_data_list = data_list = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each memory data segment */ + for (i = 0; i < module->mem_init_data_count; i++) { + uint32 byte_count; + uint32 is_passive; + uint32 memory_index; + InitializerExpression offset_expr; + + read_uint32(buf, buf_end, is_passive); + read_uint32(buf, buf_end, memory_index); + if (!load_init_expr(&buf, buf_end, module, &offset_expr, error_buf, + error_buf_size)) { + return false; + } + read_uint32(buf, buf_end, byte_count); + if (!(data_list[i] = loader_malloc(sizeof(AOTMemInitData), error_buf, + error_buf_size))) { + return false; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + /* is_passive and memory_index is only used in bulk memory mode */ + data_list[i]->is_passive = (bool)is_passive; + data_list[i]->memory_index = memory_index; +#endif + data_list[i]->offset = offset_expr; + data_list[i]->byte_count = byte_count; + data_list[i]->bytes = NULL; + /* If the module owns the binary data, clone the bytes buffer */ + if (module->is_binary_freeable) { + if (byte_count > 0) { + if (!(data_list[i]->bytes = loader_malloc(byte_count, error_buf, + error_buf_size))) { + return false; + } + read_byte_array(buf, buf_end, data_list[i]->bytes, + data_list[i]->byte_count); + } + } + else { + data_list[i]->bytes = (uint8 *)buf; + buf += byte_count; + } + } + + *p_buf = buf; + return true; +fail: + return false; +} + +/** + * Load memory information from the AOT module. + * + * This function reads memory-related data including import memory count, + * memory count, memory flags, page sizes, and memory initialization data. + * + * @param p_buf pointer to buffer containing memory info + * @param buf_end end of buffer + * @param module the AOT module being loaded + * @param error_buf buffer for error messages + * @param error_buf_size size of error buffer + * + * @return true if successful, false if error occurred + */ + +static bool +load_memory_info(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + uint64 total_size; + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->import_memory_count); + + read_uint32(buf, buf_end, module->memory_count); + total_size = sizeof(AOTMemory) * (uint64)module->memory_count; + if (!(module->memories = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->memory_count; i++) { + read_uint32(buf, buf_end, module->memories[i].flags); + + if (!wasm_memory_check_flags(module->memories[i].flags, error_buf, + error_buf_size, true)) { + return false; + } + + read_uint32(buf, buf_end, module->memories[i].num_bytes_per_page); + read_uint32(buf, buf_end, module->memories[i].init_page_count); + read_uint32(buf, buf_end, module->memories[i].max_page_count); + } + + read_uint32(buf, buf_end, module->mem_init_data_count); + + /* load memory init data list */ + if (module->mem_init_data_count > 0 + && !load_mem_init_data_list(&buf, buf_end, module, error_buf, + error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_import_tables(AOTImportTable *import_tables) +{ + wasm_runtime_free(import_tables); +} + +static void +destroy_tables(AOTTable *tables) +{ + wasm_runtime_free(tables); +} + +static void +destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count) +{ + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) { +#if WASM_ENABLE_GC != 0 + uint32 j; + for (j = 0; j < data_list[i]->value_count; j++) { + destroy_init_expr(&data_list[i]->init_values[j]); + } +#endif +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(&data_list[i]->offset); +#endif + wasm_runtime_free(data_list[i]); + } + wasm_runtime_free(data_list); +} + +static bool +load_init_expr(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + InitializerExpression *expr, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + uint32 init_expr_type = 0; + uint64 *i64x2 = NULL; + bool free_if_fail = false; + + buf = (uint8 *)align_ptr(buf, 4); + + read_uint32(buf, buf_end, init_expr_type); + + switch (init_expr_type) { + case INIT_EXPR_NONE: + break; + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_F32_CONST: + read_uint32(buf, buf_end, expr->u.unary.v.i32); + break; + case INIT_EXPR_TYPE_I64_CONST: + case INIT_EXPR_TYPE_F64_CONST: + read_uint64(buf, buf_end, expr->u.unary.v.i64); + break; + case INIT_EXPR_TYPE_V128_CONST: + i64x2 = (uint64 *)expr->u.unary.v.v128.i64x2; + CHECK_BUF(buf, buf_end, sizeof(uint64) * 2); + wasm_runtime_read_v128(buf, &i64x2[0], &i64x2[1]); + buf += sizeof(uint64) * 2; + break; + case INIT_EXPR_TYPE_GET_GLOBAL: + read_uint32(buf, buf_end, expr->u.unary.v.global_index); + break; + /* INIT_EXPR_TYPE_FUNCREF_CONST can be used when + both reference types and GC are disabled */ + case INIT_EXPR_TYPE_FUNCREF_CONST: + read_uint32(buf, buf_end, expr->u.unary.v.ref_index); + break; +#if WASM_ENABLE_GC != 0 || WASM_ENABLE_REF_TYPES != 0 + case INIT_EXPR_TYPE_REFNULL_CONST: + read_uint32(buf, buf_end, expr->u.unary.v.ref_index); + break; +#endif /* end of WASM_ENABLE_GC != 0 || WASM_ENABLE_REF_TYPES != 0 */ +#if WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_I31_NEW: + read_uint32(buf, buf_end, expr->u.unary.v.i32); + break; + case INIT_EXPR_TYPE_STRUCT_NEW: + { + uint64 size; + uint32 type_idx, field_count; + AOTStructType *struct_type = NULL; + WASMStructNewInitValues *init_values = NULL; + + read_uint32(buf, buf_end, type_idx); + read_uint32(buf, buf_end, field_count); + + size = offsetof(WASMStructNewInitValues, fields) + + sizeof(WASMValue) * (uint64)field_count; + if (!(init_values = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + free_if_fail = true; + init_values->count = field_count; + init_values->type_idx = type_idx; + expr->u.unary.v.data = init_values; + + if (type_idx >= module->type_count) { + set_error_buf(error_buf, error_buf_size, + "unknown struct type."); + goto fail; + } + + struct_type = (AOTStructType *)module->types[type_idx]; + + if (struct_type->field_count != field_count) { + set_error_buf(error_buf, error_buf_size, + "invalid field count."); + goto fail; + } + + if (field_count > 0) { + uint32 i; + + for (i = 0; i < field_count; i++) { + uint32 field_size = + wasm_value_type_size(struct_type->fields[i].field_type); + if (field_size <= sizeof(uint32)) + read_uint32(buf, buf_end, init_values->fields[i].u32); + else if (field_size == sizeof(uint64)) + read_uint64(buf, buf_end, init_values->fields[i].u64); + else if (field_size == sizeof(uint64) * 2) + read_byte_array(buf, buf_end, &init_values->fields[i], + field_size); + else { + bh_assert(0); + } + } + } + + break; + } + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + read_uint32(buf, buf_end, expr->u.unary.v.type_index); + break; + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + uint32 array_elem_type; + uint32 type_idx, length; + WASMArrayNewInitValues *init_values = NULL; + + /* Note: at this time the aot types haven't been loaded */ + read_uint32(buf, buf_end, array_elem_type); + read_uint32(buf, buf_end, type_idx); + read_uint32(buf, buf_end, length); + + if (type_idx >= module->type_count + || !wasm_type_is_array_type(module->types[type_idx])) { + set_error_buf(error_buf, error_buf_size, + "invalid or non-array type index."); + goto fail; + } + + if (init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) { + expr->u.unary.v.array_new_default.type_index = type_idx; + expr->u.unary.v.array_new_default.length = length; + } + else { + uint32 i, elem_size, elem_data_count; + uint64 size = offsetof(WASMArrayNewInitValues, elem_data) + + sizeof(WASMValue) * (uint64)length; + if (!(init_values = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + free_if_fail = true; + expr->u.unary.v.data = init_values; + + init_values->type_idx = type_idx; + init_values->length = length; + + elem_data_count = + (init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) ? length + : 1; + elem_size = wasm_value_type_size((uint8)array_elem_type); + + for (i = 0; i < elem_data_count; i++) { + if (elem_size <= sizeof(uint32)) + read_uint32(buf, buf_end, + init_values->elem_data[i].u32); + else if (elem_size == sizeof(uint64)) + read_uint64(buf, buf_end, + init_values->elem_data[i].u64); + else if (elem_size == sizeof(uint64) * 2) + read_byte_array(buf, buf_end, + &init_values->elem_data[i], elem_size); + else { + bh_assert(0); + } + } + } + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: + { + expr->u.binary.l_expr = expr->u.binary.r_expr = NULL; + if (!(expr->u.binary.l_expr = + loader_malloc(sizeof(InitializerExpression), error_buf, + error_buf_size))) { + goto fail; + } + if (!load_init_expr(&buf, buf_end, module, expr->u.binary.l_expr, + error_buf, error_buf_size)) + goto fail; + if (!(expr->u.binary.r_expr = + loader_malloc(sizeof(InitializerExpression), error_buf, + error_buf_size))) { + goto fail; + } + if (!load_init_expr(&buf, buf_end, module, expr->u.binary.r_expr, + error_buf, error_buf_size)) + goto fail; + break; + } +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */ + default: + set_error_buf(error_buf, error_buf_size, "invalid init expr type."); + return false; + } + + expr->init_expr_type = (uint8)init_expr_type; + + *p_buf = buf; + return true; +fail: +#if WASM_ENABLE_GC != 0 + if (free_if_fail) { + wasm_runtime_free(expr->u.unary.v.data); + } +#else + (void)free_if_fail; +#endif +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(expr); +#endif + return false; +} + +static bool +load_import_table_list(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTImportTable *import_table; +#if WASM_ENABLE_GC != 0 + WASMRefType ref_type; +#endif + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTImportTable) * (uint64)module->import_table_count; + if (!(module->import_tables = import_table = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* keep sync with aot_emit_table_info() aot_emit_aot_file */ + for (i = 0; i < module->import_table_count; i++, import_table++) { + read_uint8(buf, buf_end, import_table->table_type.elem_type); + read_uint8(buf, buf_end, import_table->table_type.flags); + read_uint8(buf, buf_end, import_table->table_type.possible_grow); +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(import_table->table_type.elem_type)) { + read_uint8(buf, buf_end, ref_type.ref_ht_common.nullable); + } + else +#endif + { + /* Skip 1 byte */ + buf += 1; + } + read_uint32(buf, buf_end, import_table->table_type.init_size); + read_uint32(buf, buf_end, import_table->table_type.max_size); +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(import_table->table_type.elem_type)) { + read_uint32(buf, buf_end, ref_type.ref_ht_common.heap_type); + + ref_type.ref_type = import_table->table_type.elem_type; + /* TODO: check ref_type */ + if (!(import_table->table_type.elem_ref_type = + wasm_reftype_set_insert(module->ref_type_set, + &ref_type))) { + set_error_buf(error_buf, error_buf_size, + "insert ref type to hash set failed"); + return false; + } + } +#endif + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_table_list(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTTable *table; +#if WASM_ENABLE_GC != 0 + WASMRefType ref_type; +#endif + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTTable) * (uint64)module->table_count; + if (!(module->tables = table = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each table data segment */ + for (i = 0; i < module->table_count; i++, table++) { + read_uint8(buf, buf_end, table->table_type.elem_type); + read_uint8(buf, buf_end, table->table_type.flags); + + if (!wasm_table_check_flags(table->table_type.flags, error_buf, + error_buf_size, true)) { + return false; + } + + read_uint8(buf, buf_end, table->table_type.possible_grow); +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(table->table_type.elem_type)) { + read_uint8(buf, buf_end, ref_type.ref_ht_common.nullable); + } + else +#endif + { + /* Skip 1 byte */ + buf += 1; + } + read_uint32(buf, buf_end, table->table_type.init_size); + read_uint32(buf, buf_end, table->table_type.max_size); +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(table->table_type.elem_type)) { + read_uint32(buf, buf_end, ref_type.ref_ht_common.heap_type); + + ref_type.ref_type = table->table_type.elem_type; + /* TODO: check ref_type */ + if (!(table->table_type.elem_ref_type = wasm_reftype_set_insert( + module->ref_type_set, &ref_type))) { + set_error_buf(error_buf, error_buf_size, + "insert ref type to hash set failed"); + return false; + } + } + if (!load_init_expr(&buf, buf_end, module, &table->init_expr, error_buf, + error_buf_size)) + return false; + + if (table->init_expr.init_expr_type >= INIT_EXPR_TYPE_STRUCT_NEW + && table->init_expr.init_expr_type + <= INIT_EXPR_TYPE_EXTERN_CONVERT_ANY) { + set_error_buf(error_buf, error_buf_size, + "unsupported initializer expression for table"); + return false; + } +#endif + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTTableInitData **data_list; +#if WASM_ENABLE_GC != 0 + WASMRefType reftype; +#endif + uint64 size; + uint32 i, j; + + /* Allocate memory */ + size = sizeof(AOTTableInitData *) * (uint64)module->table_init_data_count; + if (!(module->table_init_data_list = data_list = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each table data segment */ + for (i = 0; i < module->table_init_data_count; i++) { + uint32 mode, elem_type; + uint32 table_index, value_count; + uint64 size1; + InitializerExpression offset_expr; + + read_uint32(buf, buf_end, mode); + read_uint32(buf, buf_end, elem_type); + read_uint32(buf, buf_end, table_index); + if (!load_init_expr(&buf, buf_end, module, &offset_expr, error_buf, + error_buf_size)) + return false; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(elem_type)) { + uint16 ref_type, nullable; + read_uint16(buf, buf_end, ref_type); + if (elem_type != ref_type) { + set_error_buf(error_buf, error_buf_size, "invalid elem type"); + return false; + } + reftype.ref_ht_common.ref_type = (uint8)ref_type; + read_uint16(buf, buf_end, nullable); + if (nullable != 0 && nullable != 1) { + set_error_buf(error_buf, error_buf_size, + "invalid nullable value"); + return false; + } + reftype.ref_ht_common.nullable = (uint8)nullable; + read_uint32(buf, buf_end, reftype.ref_ht_common.heap_type); + } + else +#endif + { + /* Skip 8 byte(2+2+4) for ref type info */ + buf += 8; + } + + read_uint32(buf, buf_end, value_count); + + size1 = sizeof(InitializerExpression) * (uint64)value_count; + size = offsetof(AOTTableInitData, init_values) + size1; + if (!(data_list[i] = loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + data_list[i]->mode = mode; + data_list[i]->elem_type = elem_type; + data_list[i]->table_index = table_index; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(elem_type)) { + if (!(data_list[i]->elem_ref_type = + reftype_set_insert(module->ref_type_set, &reftype, + error_buf, error_buf_size))) { + goto fail; + } + } +#endif + data_list[i]->offset = offset_expr; + data_list[i]->value_count = value_count; + for (j = 0; j < data_list[i]->value_count; j++) { + if (!load_init_expr(&buf, buf_end, module, + &data_list[i]->init_values[j], error_buf, + error_buf_size)) + return false; + } + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_table_info(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->import_table_count); + if (module->import_table_count > 0 + && !load_import_table_list(&buf, buf_end, module, error_buf, + error_buf_size)) + return false; + + read_uint32(buf, buf_end, module->table_count); + if (module->table_count > 0 + && !load_table_list(&buf, buf_end, module, error_buf, error_buf_size)) + return false; + + read_uint32(buf, buf_end, module->table_init_data_count); + + /* load table init data list */ + if (module->table_init_data_count > 0 + && !load_table_init_data_list(&buf, buf_end, module, error_buf, + error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_type(AOTType *type) +{ +#if WASM_ENABLE_GC != 0 + if (type->ref_count > 1) { + /* The type is referenced by other types + of current aot module */ + type->ref_count--; + return; + } + + if (type->type_flag == WASM_TYPE_FUNC) { + AOTFuncType *func_type = (AOTFuncType *)type; + if (func_type->ref_type_maps != NULL) { + bh_assert(func_type->ref_type_map_count > 0); + wasm_runtime_free(func_type->ref_type_maps); + } + } + else if (type->type_flag == WASM_TYPE_STRUCT) { + AOTStructType *struct_type = (AOTStructType *)type; + if (struct_type->ref_type_maps != NULL) { + bh_assert(struct_type->ref_type_map_count > 0); + wasm_runtime_free(struct_type->ref_type_maps); + } + } +#endif + wasm_runtime_free(type); +} + +static void +destroy_types(AOTType **types, uint32 count) +{ + uint32 i; + for (i = 0; i < count; i++) { + if (types[i]) { + destroy_type(types[i]); + } + } + wasm_runtime_free(types); +} + +#if WASM_ENABLE_GC != 0 +static void +init_base_type(AOTType *base_type, uint32 type_idx, uint16 type_flag, + bool is_sub_final, uint32 parent_type_idx, uint16 rec_count, + uint16 rec_idx) +{ + base_type->type_flag = type_flag; + base_type->ref_count = 1; + base_type->is_sub_final = is_sub_final; + base_type->parent_type_idx = parent_type_idx; + base_type->rec_count = rec_count; + base_type->rec_idx = rec_idx; + base_type->rec_begin_type_idx = type_idx - rec_idx; +} + +static bool +load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTType **types; + uint64 size; + uint32 i, j; + uint32 type_flag, param_cell_num, ret_cell_num; + uint16 param_count, result_count, ref_type_map_count, rec_count, rec_idx; + bool is_equivalence_type, is_sub_final; + uint32 parent_type_idx; + WASMRefType ref_type; + + /* Allocate memory */ + size = sizeof(AOTFuncType *) * (uint64)module->type_count; + if (!(types = loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + module->types = types; + + /* Create each type */ + for (i = 0; i < module->type_count; i++) { + buf = align_ptr(buf, 4); + + /* Read base type info */ + read_uint16(buf, buf_end, type_flag); + + read_uint8(buf, buf_end, is_equivalence_type); + /* If there is an equivalence type, reuse it */ + if (is_equivalence_type) { + uint8 u8; + /* padding */ + read_uint8(buf, buf_end, u8); + (void)u8; + + read_uint32(buf, buf_end, j); +#if WASM_ENABLE_AOT_VALIDATOR != 0 + /* an equivalence type should be before the type it refers to */ + if (j >= i) { + set_error_buf(error_buf, error_buf_size, "invalid type index"); + goto fail; + } +#endif + if (module->types[j]->ref_count == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "wasm type's ref count too large"); + goto fail; + } + module->types[j]->ref_count++; + module->types[i] = module->types[j]; + continue; + } + + read_uint8(buf, buf_end, is_sub_final); + read_uint32(buf, buf_end, parent_type_idx); + read_uint16(buf, buf_end, rec_count); + read_uint16(buf, buf_end, rec_idx); +#if WASM_ENABLE_AOT_VALIDATOR != 0 + if (rec_count > module->type_count) { + set_error_buf(error_buf, error_buf_size, "invalid rec count"); + goto fail; + } + if (rec_idx > i || rec_idx >= rec_count) { + set_error_buf(error_buf, error_buf_size, "invalid rec idx"); + goto fail; + } + if (parent_type_idx >= i) { + set_error_buf( + error_buf, error_buf_size, + "parent type index must be smaller than current type index"); + goto fail; + } +#endif + if (type_flag == WASM_TYPE_FUNC) { + AOTFuncType *func_type; + + /* Read param count */ + read_uint16(buf, buf_end, param_count); + /* Read result count */ + read_uint16(buf, buf_end, result_count); + /* Read ref_type_map_count */ + read_uint16(buf, buf_end, ref_type_map_count); + + func_type = + loader_malloc(sizeof(AOTFuncType) + param_count + result_count, + error_buf, error_buf_size); + + if (!func_type) { + goto fail; + } + + types[i] = (AOTType *)func_type; + + init_base_type((AOTType *)func_type, i, type_flag, is_sub_final, + parent_type_idx, rec_count, rec_idx); + func_type->param_count = param_count; + func_type->result_count = result_count; + + /* Read types of params */ + read_byte_array(buf, buf_end, func_type->types, + func_type->param_count + func_type->result_count); + + func_type->ref_type_map_count = ref_type_map_count; + + if (!is_valid_func_type(func_type)) + goto fail; + + param_cell_num = wasm_get_cell_num(func_type->types, param_count); + ret_cell_num = + wasm_get_cell_num(func_type->types + param_count, result_count); + if (param_cell_num > UINT16_MAX || ret_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "param count or result count too large"); + goto fail; + } + + func_type->param_cell_num = param_cell_num; + func_type->ret_cell_num = ret_cell_num; + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + func_type->quick_aot_entry = + wasm_native_lookup_quick_aot_entry(func_type); +#endif + + LOG_VERBOSE("type %u: func, param count: %d, result count: %d, " + "ref type map count: %d", + i, param_count, result_count, ref_type_map_count); + + /* If ref_type_map is not empty, read ref_type_map */ + if (ref_type_map_count > 0) { + bh_assert(func_type->ref_type_map_count + <= func_type->param_count + func_type->result_count); + + /* align to 4 since param_count + result_count may be odd */ + buf = align_ptr(buf, 4); + + if (!(func_type->ref_type_maps = + loader_malloc(sizeof(WASMRefTypeMap) + * func_type->ref_type_map_count, + error_buf, error_buf_size))) { + goto fail; + } + + for (j = 0; j < func_type->ref_type_map_count; j++) { + read_uint16(buf, buf_end, + func_type->ref_type_maps[j].index); + read_uint8(buf, buf_end, ref_type.ref_ht_common.ref_type); + read_uint8(buf, buf_end, ref_type.ref_ht_common.nullable); + read_uint32(buf, buf_end, ref_type.ref_ht_common.heap_type); + /* TODO: check ref_type */ + if (!(func_type->ref_type_maps[j].ref_type = + wasm_reftype_set_insert(module->ref_type_set, + &ref_type))) { + set_error_buf(error_buf, error_buf_size, + "insert ref type to hash set failed"); + goto fail; + } + } + + func_type->result_ref_type_maps = func_type->ref_type_maps; + for (j = 0; j < func_type->param_count; j++) { + if (wasm_is_type_multi_byte_type(func_type->types[j])) + func_type->result_ref_type_maps++; + } + } + } + else if (type_flag == WASM_TYPE_STRUCT) { + AOTStructType *struct_type; + const uint8 *buf_org; + uint16 *reference_table; + uint16 field_count, ref_field_count = 0; + uint32 offset; + + read_uint16(buf, buf_end, field_count); + read_uint16(buf, buf_end, ref_type_map_count); + + buf_org = buf; + + /* Traverse first time to get ref_field_count */ + for (j = 0; j < field_count; j++) { + uint8 field_flags, field_type; + + read_uint8(buf, buf_end, field_flags); + read_uint8(buf, buf_end, field_type); + if (wasm_is_type_reftype(field_type)) + ref_field_count++; + + (void)field_flags; + } + + struct_type = loader_malloc( + sizeof(AOTStructType) + + sizeof(WASMStructFieldType) * (uint64)field_count + + sizeof(uint16) * (uint64)(ref_field_count + 1), + error_buf, error_buf_size); + if (!struct_type) { + goto fail; + } + + offset = (uint32)offsetof(WASMStructObject, field_data); + types[i] = (AOTType *)struct_type; + + init_base_type((AOTType *)struct_type, i, type_flag, is_sub_final, + parent_type_idx, rec_count, rec_idx); + struct_type->field_count = field_count; + struct_type->ref_type_map_count = ref_type_map_count; + + struct_type->reference_table = reference_table = + (uint16 *)((uint8 *)struct_type + + offsetof(AOTStructType, fields) + + sizeof(WASMStructFieldType) * field_count); + *reference_table++ = ref_field_count; + + LOG_VERBOSE( + "type %u: struct, field count: %d, ref type map count: %d", i, + field_count, ref_type_map_count); + + buf = buf_org; + + /* Traverse again to read each field */ + for (j = 0; j < field_count; j++) { + uint8 field_type, field_size; + + read_uint8(buf, buf_end, struct_type->fields[j].field_flags); + read_uint8(buf, buf_end, field_type); +#if WASM_ENABLE_AOT_VALIDATOR != 0 + if (!is_valid_field_type(field_type)) { + set_error_buf(error_buf, error_buf_size, + "invalid field type"); + goto fail; + } +#endif + struct_type->fields[j].field_type = field_type; + struct_type->fields[j].field_size = field_size = + (uint8)wasm_reftype_size(field_type); +#if !(defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_X86_32)) + if (field_size == 2) + offset = align_uint(offset, 2); + else if (field_size >= 4) /* field size is 4 or 8 */ + offset = align_uint(offset, 4); +#endif + struct_type->fields[j].field_offset = offset; + if (wasm_is_type_reftype(field_type)) + *reference_table++ = offset; + offset += field_size; + LOG_VERBOSE(" field: %d, flags: %d, type: %d", j, + struct_type->fields[j].field_flags, + struct_type->fields[j].field_type); + } + + struct_type->total_size = offset; + buf = align_ptr(buf, 4); + + /* If ref_type_map is not empty, read ref_type_map */ + if (ref_type_map_count > 0) { + + bh_assert(struct_type->ref_type_map_count <= field_count); + + if (!(struct_type->ref_type_maps = + loader_malloc(sizeof(WASMRefTypeMap) + * struct_type->ref_type_map_count, + error_buf, error_buf_size))) { + goto fail; + } + + for (j = 0; j < struct_type->ref_type_map_count; j++) { + read_uint16(buf, buf_end, + struct_type->ref_type_maps[j].index); + read_uint8(buf, buf_end, ref_type.ref_ht_common.ref_type); + read_uint8(buf, buf_end, ref_type.ref_ht_common.nullable); + read_uint32(buf, buf_end, ref_type.ref_ht_common.heap_type); + /* TODO: check ref_type */ + if (!(struct_type->ref_type_maps[j].ref_type = + wasm_reftype_set_insert(module->ref_type_set, + &ref_type))) { + set_error_buf(error_buf, error_buf_size, + "insert ref type to hash set failed"); + goto fail; + } + } + } + } + else if (type_flag == WASM_TYPE_ARRAY) { + AOTArrayType *array_type; + + array_type = + loader_malloc(sizeof(AOTArrayType), error_buf, error_buf_size); + + if (!array_type) { + goto fail; + } + + types[i] = (AOTType *)array_type; + + init_base_type((AOTType *)array_type, i, type_flag, is_sub_final, + parent_type_idx, rec_count, rec_idx); + read_uint16(buf, buf_end, array_type->elem_flags); + read_uint8(buf, buf_end, array_type->elem_type); + if (wasm_is_type_multi_byte_type(array_type->elem_type)) { + read_uint8(buf, buf_end, ref_type.ref_ht_common.nullable); + read_uint32(buf, buf_end, ref_type.ref_ht_common.heap_type); + ref_type.ref_type = array_type->elem_type; + /* TODO: check ref_type */ + if (!(array_type->elem_ref_type = wasm_reftype_set_insert( + module->ref_type_set, &ref_type))) { + set_error_buf(error_buf, error_buf_size, + "insert ref type to hash set failed"); + goto fail; + } + } + + LOG_VERBOSE("type %u: array", i); + } + else { + set_error_buf_v(error_buf, error_buf_size, + "invalid type flag: %" PRIu32, type_flag); + goto fail; + } + + if ((rec_count == 0) || (rec_idx == rec_count - 1)) { + if (rec_count == 0) { + bh_assert(rec_idx == 0); + } + for (j = i - rec_idx; j <= i; j++) { + AOTType *cur_type = module->types[j]; + parent_type_idx = cur_type->parent_type_idx; + if (parent_type_idx != (uint32)-1) { /* has parent */ + AOTType *parent_type = module->types[parent_type_idx]; + + module->types[j]->parent_type = parent_type; + module->types[j]->root_type = parent_type->root_type; + if (parent_type->inherit_depth == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "parent type's inherit depth too large"); + goto fail; + } + module->types[j]->inherit_depth = + parent_type->inherit_depth + 1; + } + else { + module->types[j]->parent_type = NULL; + module->types[j]->root_type = module->types[j]; + module->types[j]->inherit_depth = 0; + } + } + + for (j = i - rec_idx; j <= i; j++) { + AOTType *cur_type = module->types[j]; + parent_type_idx = cur_type->parent_type_idx; + if (parent_type_idx != (uint32)-1) { /* has parent */ + AOTType *parent_type = module->types[parent_type_idx]; + /* subtyping has been checked during compilation */ + bh_assert(wasm_type_is_subtype_of( + module->types[j], parent_type, module->types, i + 1)); + (void)parent_type; + } + } + } + } + + if (module->type_count) { + if (!(module->rtt_types = loader_malloc((uint64)sizeof(WASMRttType *) + * module->type_count, + error_buf, error_buf_size))) { + goto fail; + } + } + + *p_buf = buf; + return true; + +fail: + /* Destroy all types */ + destroy_types(types, module->type_count); + module->types = NULL; + return false; +} +#else +static bool +load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; AOTFuncType **func_types; @@ -554,38 +2139,54 @@ load_func_types(const uint8 **p_buf, const uint8 *buf_end, uint32 i; /* Allocate memory */ - size = sizeof(AOTFuncType *) * (uint64)module->func_type_count; - if (size >= UINT32_MAX - || !(module->func_types = func_types = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + size = sizeof(AOTFuncType *) * (uint64)module->type_count; + if (!(func_types = loader_malloc(size, error_buf, error_buf_size))) { return false; } - memset(func_types, 0, size); + module->types = (AOTType **)func_types; /* Create each function type */ - for (i = 0; i < module->func_type_count; i++) { + for (i = 0; i < module->type_count; i++) { + uint32 type_flag; uint32 param_count, result_count; + uint32 param_cell_num, ret_cell_num; uint64 size1; - read_uint32(buf, buf_end, param_count); - read_uint32(buf, buf_end, result_count); + buf = align_ptr(buf, 4); + read_uint16(buf, buf_end, type_flag); + read_uint16(buf, buf_end, param_count); + read_uint16(buf, buf_end, result_count); size1 = (uint64)param_count + (uint64)result_count; size = offsetof(AOTFuncType, types) + size1; - if (size >= UINT32_MAX - || !(func_types[i] = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(func_types[i] = loader_malloc(size, error_buf, error_buf_size))) { return false; } - func_types[i]->param_count = param_count; - func_types[i]->result_count = result_count; + func_types[i]->param_count = (uint16)param_count; + func_types[i]->result_count = (uint16)result_count; read_byte_array(buf, buf_end, func_types[i]->types, (uint32)size1); + + if (!is_valid_func_type(func_types[i])) + goto fail; + + param_cell_num = wasm_get_cell_num(func_types[i]->types, param_count); + ret_cell_num = + wasm_get_cell_num(func_types[i]->types + param_count, result_count); + if (param_cell_num > UINT16_MAX || ret_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "param count or result count too large"); + return false; + } + + func_types[i]->param_cell_num = (uint16)param_cell_num; + func_types[i]->ret_cell_num = (uint16)ret_cell_num; + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + func_types[i]->quick_aot_entry = + wasm_native_lookup_quick_aot_entry(func_types[i]); +#endif } *p_buf = buf; @@ -593,19 +2194,19 @@ load_func_types(const uint8 **p_buf, const uint8 *buf_end, fail: return false; } +#endif /* end of WASM_ENABLE_GC != 0 */ static bool -load_func_type_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) +load_type_info(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; - read_uint32(buf, buf_end, module->func_type_count); + read_uint32(buf, buf_end, module->type_count); /* load function type */ - if (module->func_type_count > 0 - && !load_func_types(&buf, buf_end, module, error_buf, error_buf_size)) + if (module->type_count > 0 + && !load_types(&buf, buf_end, module, error_buf, error_buf_size)) return false; *p_buf = buf; @@ -615,43 +2216,64 @@ load_func_type_info(const uint8 **p_buf, const uint8 *buf_end, } static void -destroy_import_globals(AOTImportGlobal *import_globals, bool is_jit_mode) +destroy_import_globals(AOTImportGlobal *import_globals) { - if (!is_jit_mode) - wasm_free(import_globals); + wasm_runtime_free(import_globals); } static bool load_import_globals(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, + AOTModule *module, bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; AOTImportGlobal *import_globals; uint64 size; uint32 i, data_offset = 0; +#if WASM_ENABLE_LIBC_BUILTIN != 0 + WASMGlobalImport tmp_global; +#endif /* Allocate memory */ size = sizeof(AOTImportGlobal) * (uint64)module->import_global_count; - if (size >= UINT32_MAX - || !(module->import_globals = - import_globals = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(module->import_globals = import_globals = + loader_malloc(size, error_buf, error_buf_size))) { return false; } - memset(import_globals, 0, size); - /* Create each import global */ for (i = 0; i < module->import_global_count; i++) { - read_uint8(buf, buf_end, import_globals[i].type); - read_uint8(buf, buf_end, import_globals[i].is_mutable); + buf = (uint8 *)align_ptr(buf, 2); + read_uint8(buf, buf_end, import_globals[i].type.val_type); + read_uint8(buf, buf_end, import_globals[i].type.is_mutable); read_string(buf, buf_end, import_globals[i].module_name); read_string(buf, buf_end, import_globals[i].global_name); - import_globals[i].size = wasm_value_type_size(import_globals[i].type); + if (!is_valid_value_type(import_globals[i].type.val_type)) { + return false; + } + +#if WASM_ENABLE_LIBC_BUILTIN != 0 + if (wasm_native_lookup_libc_builtin_global( + import_globals[i].module_name, import_globals[i].global_name, + &tmp_global)) { + if (tmp_global.type.val_type != import_globals[i].type.val_type + || tmp_global.type.is_mutable + != import_globals[i].type.is_mutable) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + 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.val_type); import_globals[i].data_offset = data_offset; data_offset += import_globals[i].size; module->global_data_size += import_globals[i].size; @@ -665,7 +2287,7 @@ load_import_globals(const uint8 **p_buf, const uint8 *buf_end, static bool load_import_global_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, + AOTModule *module, bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; @@ -674,7 +2296,7 @@ load_import_global_info(const uint8 **p_buf, const uint8 *buf_end, /* load import globals */ if (module->import_global_count > 0 - && !load_import_globals(&buf, buf_end, module, + && !load_import_globals(&buf, buf_end, module, is_load_from_file_buf, error_buf, error_buf_size)) return false; @@ -685,15 +2307,13 @@ load_import_global_info(const uint8 **p_buf, const uint8 *buf_end, } static void -destroy_globals(AOTGlobal *globals, bool is_jit_mode) +destroy_globals(AOTGlobal *globals) { - if (!is_jit_mode) - wasm_free(globals); + wasm_runtime_free(globals); } static bool -load_globals(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, +load_globals(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; @@ -704,36 +2324,33 @@ load_globals(const uint8 **p_buf, const uint8 *buf_end, /* Allocate memory */ size = sizeof(AOTGlobal) * (uint64)module->global_count; - if (size >= UINT32_MAX - || !(module->globals = globals = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(module->globals = globals = + loader_malloc(size, error_buf, error_buf_size))) { return false; } - memset(globals, 0, size); - if (module->import_global_count > 0) { last_import_global = &module->import_globals[module->import_global_count - 1]; - data_offset = last_import_global->data_offset - + last_import_global->size; + data_offset = + last_import_global->data_offset + last_import_global->size; } /* Create each global */ for (i = 0; i < module->global_count; i++) { - uint16 init_expr_type; - uint64 init_expr_value; + read_uint8(buf, buf_end, globals[i].type.val_type); + read_uint8(buf, buf_end, globals[i].type.is_mutable); + + if (!is_valid_value_type(globals[i].type.val_type)) + return false; + + buf = align_ptr(buf, 4); - read_uint8(buf, buf_end, globals[i].type); - read_uint8(buf, buf_end, globals[i].is_mutable); - read_uint16(buf, buf_end, init_expr_type); - read_uint64(buf, buf_end, init_expr_value); - globals[i].init_expr.init_expr_type = (uint8)init_expr_type; - globals[i].init_expr.u.i64 = (int64)init_expr_value; + if (!load_init_expr(&buf, buf_end, module, &globals[i].init_expr, + error_buf, error_buf_size)) + return false; - globals[i].size = wasm_value_type_size(globals[i].type); + globals[i].size = wasm_value_type_size(globals[i].type.val_type); globals[i].data_offset = data_offset; data_offset += globals[i].size; module->global_data_size += globals[i].size; @@ -746,13 +2363,15 @@ load_globals(const uint8 **p_buf, const uint8 *buf_end, } static bool -load_global_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, +load_global_info(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; read_uint32(buf, buf_end, module->global_count); + if (is_indices_overflow(module->import_global_count, module->global_count, + error_buf, error_buf_size)) + return false; /* load globals */ if (module->global_count > 0 @@ -766,48 +2385,16 @@ load_global_info(const uint8 **p_buf, const uint8 *buf_end, } static void -destroy_import_funcs(AOTImportFunc *import_funcs, bool is_jit_mode) +destroy_import_funcs(AOTImportFunc *import_funcs) { - if (!is_jit_mode) - wasm_free(import_funcs); -} - -static void* -resolve_sym(const char *module_name, const char *field_name) -{ - void *sym; - -#if WASM_ENABLE_LIBC_BUILTIN != 0 - if ((sym = wasm_native_lookup_libc_builtin_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_LIBC_WASI != 0 - if ((sym = wasm_native_lookup_libc_wasi_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_BASE_LIB != 0 - if ((sym = wasm_native_lookup_base_lib_func(module_name, - field_name))) - return sym; -#endif - - if ((sym = wasm_native_lookup_extension_lib_func(module_name, - field_name))) - return sym; - - return NULL; + wasm_runtime_free(import_funcs); } static bool -load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) +load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + bool is_load_from_file_buf, bool no_resolve, char *error_buf, + uint32 error_buf_size) { - const char *module_name, *field_name; const uint8 *buf = *p_buf; AOTImportFunc *import_funcs; uint64 size; @@ -815,40 +2402,35 @@ load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, /* Allocate memory */ size = sizeof(AOTImportFunc) * (uint64)module->import_func_count; - if (size >= UINT32_MAX - || !(module->import_funcs = - import_funcs = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(module->import_funcs = import_funcs = + loader_malloc(size, error_buf, error_buf_size))) { return false; } - memset(import_funcs, 0, size); - /* Create each import func */ for (i = 0; i < module->import_func_count; i++) { read_uint16(buf, buf_end, import_funcs[i].func_type_index); - if (import_funcs[i].func_type_index >= module->func_type_count) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid function type index."); + if (import_funcs[i].func_type_index >= module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); return false; } + + import_funcs[i].func_type = + (AOTFuncType *)module->types[import_funcs[i].func_type_index]; read_string(buf, buf_end, import_funcs[i].module_name); read_string(buf, buf_end, import_funcs[i].func_name); + import_funcs[i].attachment = NULL; + import_funcs[i].signature = NULL; + import_funcs[i].call_conv_raw = false; - module_name = import_funcs[i].module_name; - field_name = import_funcs[i].func_name; - if (!(import_funcs[i].func_ptr_linked = - resolve_sym(module_name, field_name))) { - LOG_WARNING("warning: fail to link import function (%s, %s)\n", - module_name, field_name); + if (!no_resolve) { + aot_resolve_import_func(module, &import_funcs[i]); } #if WASM_ENABLE_LIBC_WASI != 0 - if (!strcmp(import_funcs[i].module_name, "wasi_unstable")) - module->is_wasi_module = true; + if (!strcmp(import_funcs[i].module_name, "wasi_unstable") + || !strcmp(import_funcs[i].module_name, "wasi_snapshot_preview1")) + module->import_wasi_api = true; #endif } @@ -860,8 +2442,8 @@ load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, static bool load_import_func_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) + AOTModule *module, bool is_load_from_file_buf, + bool no_resolve, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; @@ -869,8 +2451,8 @@ load_import_func_info(const uint8 **p_buf, const uint8 *buf_end, /* load import funcs */ if (module->import_func_count > 0 - && !load_import_funcs(&buf, buf_end, module, - error_buf, error_buf_size)) + && !load_import_funcs(&buf, buf_end, module, is_load_from_file_buf, + no_resolve, error_buf, error_buf_size)) return false; *p_buf = buf; @@ -886,64 +2468,84 @@ destroy_object_data_sections(AOTObjectDataSection *data_sections, uint32 i; AOTObjectDataSection *data_section = data_sections; for (i = 0; i < data_section_count; i++, data_section++) - if (data_section->data) - bh_munmap(data_section->data, data_section->size); - wasm_free(data_sections); + if (data_section->data) { +#if WASM_ENABLE_STATIC_PGO != 0 + if (!strncmp(data_section->name, "__llvm_prf_data", 15)) { + LLVMProfileData *data = (LLVMProfileData *)data_section->data; + if (data->values) { + uint32 num_value_sites = + data->num_value_sites[0] + data->num_value_sites[1]; + uint32 j; + for (j = 0; j < num_value_sites; j++) { + ValueProfNode *node = data->values[j], *node_next; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + } + wasm_runtime_free(data->values); + } + } +#endif + } + wasm_runtime_free(data_sections); } static bool load_object_data_sections(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, + AOTModule *module, bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; AOTObjectDataSection *data_sections; uint64 size; uint32 i; + uint64 total_size = 0; + uint32 page_size = os_getpagesize(); + uint8 *merged_sections = NULL; /* Allocate memory */ size = sizeof(AOTObjectDataSection) * (uint64)module->data_section_count; - if (size >= UINT32_MAX - || !(module->data_sections = - data_sections = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(module->data_sections = data_sections = + loader_malloc(size, error_buf, error_buf_size))) { return false; } - memset(data_sections, 0, size); - - /* Create each data section */ + /* First iteration: read data from buf, and calculate total memory needed */ for (i = 0; i < module->data_section_count; i++) { - int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE; -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* aot code and data in x86_64 must be in range 0 to 2G due to - relocation for R_X86_64_32/32S/PC32 */ - int map_flags = MMAP_MAP_32BIT; -#else - int map_flags = MMAP_MAP_NONE; -#endif - read_string(buf, buf_end, data_sections[i].name); read_uint32(buf, buf_end, data_sections[i].size); - + CHECK_BUF(buf, buf_end, data_sections[i].size); + /* Temporary record data ptr for merge, will be replaced after the + merged_data_sections is mmapped */ + if (data_sections[i].size > 0) + data_sections[i].data = (uint8 *)buf; + buf += data_sections[i].size; + total_size += align_uint64((uint64)data_sections[i].size, page_size); + } + if (total_size > UINT32_MAX) { + set_error_buf(error_buf, error_buf_size, "data sections too large"); + return false; + } + if (total_size > 0) { /* Allocate memory for data */ - if (!(data_sections[i].data = - bh_mmap(NULL, data_sections[i].size, map_prot, map_flags))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + merged_sections = module->merged_data_sections = + loader_mmap((uint32)total_size, false, error_buf, error_buf_size); + if (!merged_sections) { return false; } -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* address must be in the first 2 Gigabytes of - the process address space */ - bh_assert((uintptr_t)data_sections[i].data < INT32_MAX); -#endif + module->merged_data_sections_size = (uint32)total_size; + } - read_byte_array(buf, buf_end, + /* Second iteration: Create each data section */ + for (i = 0; i < module->data_section_count; i++) { + if (data_sections[i].size > 0) { + bh_memcpy_s(merged_sections, data_sections[i].size, data_sections[i].data, data_sections[i].size); + data_sections[i].data = merged_sections; + merged_sections += align_uint(data_sections[i].size, page_size); + } } *p_buf = buf; @@ -954,7 +2556,7 @@ load_object_data_sections(const uint8 **p_buf, const uint8 *buf_end, static bool load_object_data_sections_info(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, + AOTModule *module, bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size) { const uint8 *buf = *p_buf; @@ -964,7 +2566,8 @@ load_object_data_sections_info(const uint8 **p_buf, const uint8 *buf_end, /* load object data sections */ if (module->data_section_count > 0 && !load_object_data_sections(&buf, buf_end, module, - error_buf, error_buf_size)) + is_load_from_file_buf, error_buf, + error_buf_size)) return false; *p_buf = buf; @@ -975,107 +2578,220 @@ load_object_data_sections_info(const uint8 **p_buf, const uint8 *buf_end, static bool load_init_data_section(const uint8 *buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) + AOTModule *module, bool is_load_from_file_buf, + bool no_resolve, char *error_buf, uint32 error_buf_size) { const uint8 *p = buf, *p_end = buf_end; if (!load_memory_info(&p, p_end, module, error_buf, error_buf_size) || !load_table_info(&p, p_end, module, error_buf, error_buf_size) - || !load_func_type_info(&p, p_end, module, error_buf, error_buf_size) - || !load_import_global_info(&p, p_end, module, error_buf, error_buf_size) + || !load_type_info(&p, p_end, module, error_buf, error_buf_size) + || !load_import_global_info(&p, p_end, module, is_load_from_file_buf, + error_buf, error_buf_size) || !load_global_info(&p, p_end, module, error_buf, error_buf_size) - || !load_import_func_info(&p, p_end, module, error_buf, error_buf_size)) + || !load_import_func_info(&p, p_end, module, is_load_from_file_buf, + no_resolve, error_buf, error_buf_size)) return false; /* load function count and start function index */ read_uint32(p, p_end, module->func_count); + if (is_indices_overflow(module->import_func_count, module->func_count, + error_buf, error_buf_size)) + return false; + read_uint32(p, p_end, module->start_func_index); /* check start function index */ if (module->start_func_index != (uint32)-1 - && (module->start_func_index < module->import_func_count - || module->start_func_index >= module->import_func_count - + module->func_count)) { + && (module->start_func_index + >= module->import_func_count + module->func_count)) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " "invalid start function index"); return false; } - read_uint32(p, p_end, module->llvm_aux_data_end); - read_uint32(p, p_end, module->llvm_aux_stack_bottom); - read_uint32(p, p_end, module->llvm_aux_stack_size); - read_uint32(p, p_end, module->llvm_aux_stack_global_index); + read_uint32(p, p_end, module->aux_data_end_global_index); + read_uint64(p, p_end, module->aux_data_end); + read_uint32(p, p_end, module->aux_heap_base_global_index); + read_uint64(p, p_end, module->aux_heap_base); + read_uint32(p, p_end, module->aux_stack_top_global_index); + read_uint64(p, p_end, module->aux_stack_bottom); + read_uint32(p, p_end, module->aux_stack_size); + + if (module->aux_data_end >= MAX_LINEAR_MEMORY_SIZE + || module->aux_heap_base >= MAX_LINEAR_MEMORY_SIZE + || module->aux_stack_bottom >= MAX_LINEAR_MEMORY_SIZE) { + set_error_buf( + error_buf, error_buf_size, + "invalid range of aux_date_end/aux_heap_base/aux_stack_bottom"); + return false; + } if (!load_object_data_sections_info(&p, p_end, module, - error_buf, error_buf_size)) + is_load_from_file_buf, error_buf, + error_buf_size)) return false; if (p != p_end) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " "invalid init data section size"); return false; } return true; - fail: return false; } -static uint32 -get_plt_item_size(); +#if !defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF) +static bool +try_merge_data_and_text(const uint8 **buf, const uint8 **buf_end, + AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + uint8 *old_buf = (uint8 *)*buf; + uint8 *old_end = (uint8 *)*buf_end; + size_t code_size = (size_t)(old_end - old_buf); + uint32 page_size = os_getpagesize(); + uint64 total_size = 0; + uint32 i; + uint8 *sections; + + if (code_size == 0) { + return true; + } -static uint32 -get_plt_table_size(); + /* calculate the total memory needed */ + total_size += align_uint64((uint64)code_size, page_size); + for (i = 0; i < module->data_section_count; ++i) { + total_size += + align_uint64((uint64)module->data_sections[i].size, page_size); + } + /* distance between .data and .text should not be greater than 4GB + for some targets (e.g. arm64 reloc need < 4G distance) */ + if (total_size > UINT32_MAX) { + return false; + } + /* code_size was checked and must be larger than 0 here */ + bh_assert(total_size > 0); -static void -init_plt_table(uint8 *plt); + sections = loader_mmap((uint32)total_size, false, NULL, 0); + if (!sections) { + /* merge failed but may be not critical for some targets */ + return false; + } + +#ifdef BH_PLATFORM_WINDOWS + if (!os_mem_commit(sections, code_size, + MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC)) { + os_munmap(sections, (uint32)total_size); + return false; + } +#endif + + /* change the code part to be executable */ + if (os_mprotect(sections, code_size, + MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC) + != 0) { + os_munmap(sections, (uint32)total_size); + return false; + } + + module->merged_data_text_sections = sections; + module->merged_data_text_sections_size = (uint32)total_size; + + /* order not essential just as compiler does: .text section first */ + *buf = sections; + *buf_end = sections + code_size; + bh_memcpy_s(sections, (uint32)code_size, old_buf, (uint32)code_size); + os_munmap(old_buf, code_size); + sections += align_uint((uint32)code_size, page_size); + + /* then migrate .data sections */ + for (i = 0; i < module->data_section_count; ++i) { + AOTObjectDataSection *data_section = module->data_sections + i; + uint8 *old_data = data_section->data; + data_section->data = sections; + bh_memcpy_s(data_section->data, data_section->size, old_data, + data_section->size); + sections += align_uint(data_section->size, page_size); + } + /* free the original data sections */ + if (module->merged_data_sections) { + os_munmap(module->merged_data_sections, + module->merged_data_sections_size); + module->merged_data_sections = NULL; + module->merged_data_sections_size = 0; + } + + return true; +} +#endif /* ! defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF) */ static bool -load_text_section(const uint8 *buf, const uint8 *buf_end, - AOTModule *module, +load_text_section(const uint8 *buf, const uint8 *buf_end, AOTModule *module, char *error_buf, uint32 error_buf_size) { uint8 *plt_base; if (module->func_count > 0 && buf_end == buf) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid code size."); + set_error_buf(error_buf, error_buf_size, "invalid code size"); return false; } - module->code = (void*)buf; - module->code_size = (uint32)(buf_end - buf); + /* The layout is: literal size + literal + code (with plt table) */ + read_uint32(buf, buf_end, module->literal_size); + + /* literal data is at beginning of the text section */ + module->literal = (uint8 *)buf; + module->code = (void *)(buf + module->literal_size); + module->code_size = (uint32)(buf_end - (uint8 *)module->code); + +#if WASM_ENABLE_DEBUG_AOT != 0 + module->elf_size = module->code_size; + + if (is_ELF(module->code)) { + /* Now code points to an ELF object, we pull it down to .text section */ + uint64 offset; + uint64 size; + char *code_buf = module->code; + module->elf_hdr = code_buf; + if (!get_text_section(code_buf, &offset, &size)) { + set_error_buf(error_buf, error_buf_size, + "get text section of ELF failed"); + return false; + } + module->code = code_buf + offset; + module->code_size -= (uint32)offset; + } +#endif - if (module->code_size > 0) { - plt_base = (uint8*)buf_end - get_plt_table_size(); + if ((module->code_size > 0) && !module->is_indirect_mode) { + plt_base = (uint8 *)buf_end - get_plt_table_size(); init_plt_table(plt_base); } return true; +fail: + return false; } static bool -load_function_section(const uint8 *buf, const uint8 *buf_end, - AOTModule *module, +load_function_section(const uint8 *buf, const uint8 *buf_end, AOTModule *module, char *error_buf, uint32 error_buf_size) { const uint8 *p = buf, *p_end = buf_end; uint32 i; uint64 size, text_offset; - size = sizeof(void*) * (uint64)module->func_count; - if (size >= UINT32_MAX - || !(module->func_ptrs = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: allocate memory failed."); + size = sizeof(void *) * (uint64)module->func_count; + if (size > 0 + && !(module->func_ptrs = + loader_malloc(size, error_buf, error_buf_size))) { return false; } for (i = 0; i < module->func_count; i++) { - if (sizeof(void*) == 8) { + if (sizeof(void *) == 8) { read_uint64(p, p_end, text_offset); } else { @@ -1085,322 +2801,271 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, } if (text_offset >= module->code_size) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid function code offset."); + "invalid function code offset"); return false; } - module->func_ptrs[i] = (uint8*)module->code + text_offset; + module->func_ptrs[i] = (uint8 *)module->code + text_offset; #if defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_THUMB_VFP) /* bits[0] of thumb function address must be 1 */ - module->func_ptrs[i] = (void*)((uintptr_t)module->func_ptrs[i] | 1); + module->func_ptrs[i] = (void *)((uintptr_t)module->func_ptrs[i] | 1); #endif } /* Set start function when function pointers are resolved */ if (module->start_func_index != (uint32)-1) { - module->start_function = - module->func_ptrs[module->start_func_index - - module->import_func_count]; + if (module->start_func_index >= module->import_func_count) + module->start_function = + module->func_ptrs[module->start_func_index + - module->import_func_count]; + else + /* TODO: fix start function can be import function issue */ + module->start_function = NULL; } else { module->start_function = NULL; } size = sizeof(uint32) * (uint64)module->func_count; - if (size >= UINT32_MAX - || !(module->func_type_indexes = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: allocate memory failed."); + if (size > 0 + && !(module->func_type_indexes = + loader_malloc(size, error_buf, error_buf_size))) { return false; } for (i = 0; i < module->func_count; i++) { read_uint32(p, p_end, module->func_type_indexes[i]); - if (module->func_type_indexes[i] >= module->func_type_count) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid function type index."); + if (module->func_type_indexes[i] >= module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); return false; } } - if (p != buf_end) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid function section size"); - return false; - } + size = sizeof(uint32) * (uint64)module->func_count; - return true; -fail: - return false; -} + if (size > 0) { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (!(module->max_local_cell_nums = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } -static void -destroy_export_funcs(AOTExportFunc *export_funcs, bool is_jit_mode) -{ - if (!is_jit_mode) - wasm_free(export_funcs); -} + for (i = 0; i < module->func_count; i++) { + read_uint32(p, p_end, module->max_local_cell_nums[i]); + } -static bool -load_export_funcs(const uint8 **p_buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) -{ - const uint8 *buf = *p_buf; - AOTExportFunc *export_funcs; - uint64 size; - uint32 i; + if (!(module->max_stack_cell_nums = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } - /* Allocate memory */ - size = sizeof(AOTExportFunc) * (uint64)module->export_func_count; - if (size >= UINT32_MAX - || !(module->export_funcs = - export_funcs = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); - return false; + for (i = 0; i < module->func_count; i++) { + read_uint32(p, p_end, module->max_stack_cell_nums[i]); + } +#else + /* Ignore max_local_cell_num and max_stack_cell_num of each function */ + CHECK_BUF(p, p_end, ((uint32)size * 2)); + p += (uint32)size * 2; +#endif } - memset(export_funcs, 0, size); - - /* Create each export func */ - for (i = 0; i < module->export_func_count; i++) { - read_uint32(buf, buf_end, export_funcs[i].func_index); - if (export_funcs[i].func_index >= - module->func_count + module->import_func_count) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "function index is out of range."); +#if WASM_ENABLE_GC != 0 + /* Local(params and locals) ref flags for all import and non-imported + * functions. The flags indicate whether each cell in the AOTFrame local + * area is a GC reference. */ + size = sizeof(LocalRefFlag) + * (uint64)(module->import_func_count + module->func_count); + if (size > 0) { + if (!(module->func_local_ref_flags = + loader_malloc(size, error_buf, error_buf_size))) { return false; } - read_string(buf, buf_end, export_funcs[i].func_name); - } - *p_buf = buf; - return true; -fail: - return false; -} + for (i = 0; i < module->import_func_count + module->func_count; i++) { + uint32 local_ref_flag_cell_num; -static bool -load_export_section(const uint8 *buf, const uint8 *buf_end, - AOTModule *module, - char *error_buf, uint32 error_buf_size) -{ - const uint8 *p = buf, *p_end = buf_end; + buf = (uint8 *)align_ptr(buf, sizeof(uint32)); + read_uint32( + p, p_end, + module->func_local_ref_flags[i].local_ref_flag_cell_num); - /* load export functions */ - read_uint32(p, p_end, module->export_func_count); - if (module->export_func_count > 0 - && !load_export_funcs(&p, p_end, module, error_buf, error_buf_size)) - return false; + local_ref_flag_cell_num = + module->func_local_ref_flags[i].local_ref_flag_cell_num; + size = sizeof(uint8) * (uint64)local_ref_flag_cell_num; + if (size > 0) { + if (!(module->func_local_ref_flags[i].local_ref_flags = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + read_byte_array(p, p_end, + module->func_local_ref_flags[i].local_ref_flags, + local_ref_flag_cell_num); + } + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ - if (p != p_end) { + if (p != buf_end) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid export section size"); + "invalid function section size"); return false; } return true; - fail: return false; } -#define R_386_32 1 /* Direct 32 bit */ -#define R_386_PC32 2 /* PC relative 32 bit */ - -#define R_X86_64_64 1 /* Direct 64 bit */ -#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ -#define R_X86_64_PLT32 4 /* 32 bit PLT address */ -#define R_X86_64_32 10 /* Direct 32 bit zero extended */ -#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ - -#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ -#define R_ARM_JMP24 29 /* PC relative 24 bit (B/BL). */ -#define R_ARM_ABS32 2 /* Direct 32 bit */ - -#define R_ARM_THM_CALL 10 /* PC relative (Thumb BL and ARMv5 Thumb BLX). */ -#define R_ARM_THM_JMP24 30 /* B.W */ - -#define R_MIPS_32 2 /* Direct 32 bit */ -#define R_MIPS_26 4 /* Direct 26 bit shifted */ - -#ifndef BH_MB -#define BH_MB 1024 * 1024 -#endif - -static bool -check_reloc_offset(uint32 target_section_size, - uint64 reloc_offset, uint32 reloc_data_size, - char *error_buf, uint32 error_buf_size) +static void +destroy_exports(AOTExport *exports) { - if (!(reloc_offset < (uint64)target_section_size - && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid relocation offset."); - return false; - } - return true; + wasm_runtime_free(exports); } -#define CHECK_RELOC_OFFSET(data_size) do { \ - if (!check_reloc_offset(target_section_size, reloc_offset, data_size, \ - error_buf, error_buf_size)) \ - return false; \ - } while (0) +static int +cmp_export_name(const void *a, const void *b) +{ + return strcmp(*(char **)a, *(char **)b); +} static bool -apply_relocation(AOTModule *module, - uint8 *target_section_addr, uint32 target_section_size, - uint64 reloc_offset, uint64 reloc_addend, - uint32 reloc_type, void *symbol_addr, int32 symbol_index, - char *error_buf, uint32 error_buf_size) +check_duplicate_exports(AOTModule *module, char *error_buf, + uint32 error_buf_size) { - switch (reloc_type) { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - case R_X86_64_64: - { - intptr_t value; - - CHECK_RELOC_OFFSET(sizeof(void*)); - value = *(intptr_t*)(target_section_addr + (uint32)reloc_offset); - *(uint8**)(target_section_addr + reloc_offset) - = (uint8*)symbol_addr + reloc_addend + value; /* S + A */ - break; - } - case R_X86_64_PC32: - { - intptr_t target_addr = (intptr_t) /* S + A - P */ - ((uint8*)symbol_addr + reloc_addend - - (target_section_addr + reloc_offset)); - - CHECK_RELOC_OFFSET(sizeof(int32)); - if ((int32)target_addr != target_addr) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "relocation truncated to fit R_X86_64_PC32 failed. " - "Try using wamrc with --size-level=1 option."); - return false; - } - - *(int32*)(target_section_addr + reloc_offset) = (int32)target_addr; - break; - } - case R_X86_64_32: - case R_X86_64_32S: - { - char buf[128]; - uintptr_t target_addr = (uintptr_t) /* S + A */ - ((uint8*)symbol_addr + reloc_addend); - - CHECK_RELOC_OFFSET(sizeof(int32)); - - if ((reloc_type == R_X86_64_32 - && (uint32)target_addr != (uint64)target_addr) - || (reloc_type == R_X86_64_32S - && (int32)target_addr != (int64)target_addr)) { - snprintf(buf, sizeof(buf), - "AOT module load failed: " - "relocation truncated to fit %s failed. " - "Try using wamrc with --size-level=1 option.", - reloc_type == R_X86_64_32 - ? "R_X86_64_32" : "R_X86_64_32S"); - set_error_buf(error_buf, error_buf_size, buf); - return false; - } - - *(int32*)(target_section_addr + reloc_offset) = (int32)target_addr; - break; + uint32 i; + bool result = false; + char *names_buf[32], **names = names_buf; + if (module->export_count > 32) { + names = loader_malloc(module->export_count * sizeof(char *), error_buf, + error_buf_size); + if (!names) { + return result; } - case R_X86_64_PLT32: - { - uint8 *plt = module->code + module->code_size - get_plt_table_size() - + get_plt_item_size() * symbol_index; - intptr_t target_addr = (intptr_t) /* L + A - P */ - (plt + reloc_addend - - (target_section_addr + reloc_offset)); - - CHECK_RELOC_OFFSET(sizeof(int32)); + } - if (symbol_index < 0) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid symbol index for relocation"); - return false; - } + for (i = 0; i < module->export_count; i++) { + names[i] = module->exports[i].name; + } - if ((int32)target_addr != target_addr) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "relocation truncated to fit R_X86_64_PC32 failed. " - "Try using wamrc with --size-level=1 option."); - return false; - } + qsort(names, module->export_count, sizeof(char *), cmp_export_name); - *(int32*)(target_section_addr + reloc_offset) = (int32)target_addr; - break; + for (i = 1; i < module->export_count; i++) { + if (!strcmp(names[i], names[i - 1])) { + set_error_buf(error_buf, error_buf_size, "duplicate export name"); + goto cleanup; } -#endif /* end of BUILD_TARGET_X86_64 || BUILD_TARGET_AMD_64 */ + } -#if defined(BUILD_TARGET_X86_32) - case R_386_32: - { - intptr_t value; + result = true; +cleanup: + if (module->export_count > 32) { + wasm_runtime_free(names); + } + return result; +} - CHECK_RELOC_OFFSET(sizeof(void*)); - value = *(intptr_t*)(target_section_addr + (uint32)reloc_offset); - *(uint8**)(target_section_addr + reloc_offset) - = (uint8*)symbol_addr + reloc_addend + value; /* S + A */ - break; +static bool +load_exports(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, + bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTExport *exports; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTExport) * (uint64)module->export_count; + if (!(module->exports = exports = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each export */ + for (i = 0; i < module->export_count; i++) { + read_uint32(buf, buf_end, exports[i].index); + read_uint8(buf, buf_end, exports[i].kind); + read_string(buf, buf_end, exports[i].name); + + /* Check export kind and index */ + switch (exports[i].kind) { + case EXPORT_KIND_FUNC: + if (exports[i].index + >= module->import_func_count + module->func_count) { + set_error_buf(error_buf, error_buf_size, + "unknown function"); + return false; + } + break; + case EXPORT_KIND_TABLE: + if (exports[i].index + >= module->import_table_count + module->table_count) { + set_error_buf(error_buf, error_buf_size, "unknown table"); + return false; + } + break; + case EXPORT_KIND_MEMORY: + if (exports[i].index + >= module->import_memory_count + module->memory_count) { + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + } + break; + case EXPORT_KIND_GLOBAL: + if (exports[i].index + >= module->import_global_count + module->global_count) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return false; + } + break; +#if WASM_ENABLE_TAGS != 0 + /* TODO + case EXPORT_KIND_TAG: + if (index >= module->import_tag_count + module->tag_count) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + return false; + } + break; + */ +#endif + default: + set_error_buf(error_buf, error_buf_size, "invalid export kind"); + return false; } + } - case R_386_PC32: - { - int32 value; - - CHECK_RELOC_OFFSET(sizeof(void*)); - value = *(int32*)(target_section_addr + (uint32)reloc_offset); - *(uint32*)(target_section_addr + (uint32)reloc_offset) = (uint32) - ((uint8*)symbol_addr + (uint32)reloc_addend - - (uint8*)(target_section_addr + (uint32)reloc_offset) - + value); /* S + A - P */ - break; + if (module->export_count > 0) { + if (!check_duplicate_exports(module, error_buf, error_buf_size)) { + return false; } -#endif /* end of BUILD_TARGET_X86_32 */ + } -#if defined(BUILD_TARGET_ARM) || defined(BUILD_TARGET_ARM_VFP) - /* TODO: implement ARM relocation */ - case R_ARM_CALL: - case R_ARM_JMP24: - case R_ARM_ABS32: -#endif + *p_buf = buf; + return true; +fail: + return false; +} -#if defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_THUMB_VFP) - /* TODO: implement THUMB relocation */ - case R_ARM_THM_CALL: - case R_ARM_THM_JMP24: -#endif +static bool +load_export_section(const uint8 *buf, const uint8 *buf_end, AOTModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; -#if defined(BUILD_TARGET_MIPS_32) - case R_MIPS_26: - case R_MIPS_32: - /* TODO: implement relocation for mips */ -#endif + /* load export functions */ + read_uint32(p, p_end, module->export_count); + if (module->export_count > 0 + && !load_exports(&p, p_end, module, is_load_from_file_buf, error_buf, + error_buf_size)) + return false; - default: - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "Load import section failed: " - "invalid relocation type %d.", - reloc_type); - return false; + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "invalid export section size"); + return false; } + return true; +fail: + return false; } static void * @@ -1410,244 +3075,131 @@ get_data_section_addr(AOTModule *module, const char *section_name, uint32 i; AOTObjectDataSection *data_section = module->data_sections; - for (i = 0; i < module->data_section_count; i++, data_section++) + for (i = 0; i < module->data_section_count; i++, data_section++) { if (!strcmp(data_section->name, section_name)) { if (p_data_size) *p_data_size = data_section->size; return data_section->data; } + } return NULL; } -typedef struct { - const char *symbol_name; - void *symbol_addr; -} SymbolMap; - -#define REG_SYM(symbol) { #symbol, (void*)symbol } - -#if defined(BUILD_TARGET_X86_32) -void __divdi3(); -void __udivdi3(); -void __moddi3(); -void __umoddi3(); -#endif - -#if defined(BUILD_TARGET_ARM) \ - || defined(BUILD_TARGET_ARM_VFP) \ - || defined(BUILD_TARGET_THUMB) \ - || defined(BUILD_TARGET_THUMB_VFP) -void __divdi3(); -void __udivdi3(); -void __moddi3(); -void __umoddi3(); -void __divsi3(); -void __udivsi3(); -void __modsi3(); -void __udivmoddi4(); -void __clzsi2(); -void __fixsfdi(); -void __fixunssfdi(); -void __fixdfdi(); -void __fixunsdfdi(); -void __floatdisf(); -void __floatundisf(); -void __floatdidf(); -void __floatundidf(); -void __aeabi_l2f(); -void __aeabi_f2lz(); -void __aeabi_ul2f(); -void __aeabi_d2lz(); -void __aeabi_l2d(); -void __aeabi_f2ulz(); -void __aeabi_ul2d(); -void __aeabi_d2ulz(); -void __aeabi_idiv(); -void __aeabi_uidiv(); -void __aeabi_idivmod(); -void __aeabi_uidivmod(); -void __aeabi_ldivmod(); -void __aeabi_uldivmod(); -#endif - -static SymbolMap target_sym_map[] = { - REG_SYM(aot_set_exception_with_id), - REG_SYM(aot_get_exception), - REG_SYM(aot_is_wasm_type_equal), - REG_SYM(wasm_runtime_enlarge_memory), - REG_SYM(wasm_runtime_set_exception), - REG_SYM(fmin), - REG_SYM(fminf), - REG_SYM(fmax), - REG_SYM(fmaxf), - REG_SYM(ceil), - REG_SYM(ceilf), - REG_SYM(floor), - REG_SYM(floorf), - REG_SYM(trunc), - REG_SYM(truncf), - REG_SYM(rint), - REG_SYM(rintf), - /* compiler-rt symbols that come from compiler(e.g. gcc) */ -#if defined(BUILD_TARGET_X86_32) - REG_SYM(__divdi3), - REG_SYM(__udivdi3), - REG_SYM(__moddi3), - REG_SYM(__umoddi3) -#elif defined(BUILD_TARGET_ARM) \ - || defined(BUILD_TARGET_ARM_VFP) \ - || defined(BUILD_TARGET_THUMB) \ - || defined(BUILD_TARGET_THUMB_VFP) - REG_SYM(__divdi3), - REG_SYM(__udivdi3), - REG_SYM(__umoddi3), - REG_SYM(__divsi3), - REG_SYM(__udivsi3), - REG_SYM(__modsi3), - REG_SYM(__udivmoddi4), - REG_SYM(__clzsi2), - REG_SYM(__fixsfdi), - REG_SYM(__fixunssfdi), - REG_SYM(__fixdfdi), - REG_SYM(__fixunsdfdi), - REG_SYM(__floatdisf), - REG_SYM(__floatundisf), - REG_SYM(__floatdidf), - REG_SYM(__floatundidf), - REG_SYM(__aeabi_l2f), - REG_SYM(__aeabi_f2lz), - REG_SYM(__aeabi_ul2f), - REG_SYM(__aeabi_d2lz), - REG_SYM(__aeabi_l2d), - REG_SYM(__aeabi_f2ulz), - REG_SYM(__aeabi_ul2d), - REG_SYM(__aeabi_d2ulz), - REG_SYM(__aeabi_idiv), - REG_SYM(__aeabi_uidiv), - REG_SYM(__aeabi_idivmod), - REG_SYM(__aeabi_uidivmod), - REG_SYM(__aeabi_ldivmod), - REG_SYM(__aeabi_uldivmod), - -#endif /* end of BUILD_TARGET_X86_32 */ -}; +const void * +aot_get_data_section_addr(AOTModule *module, const char *section_name, + uint32 *p_data_size) +{ + return get_data_section_addr(module, section_name, p_data_size); +} static void * resolve_target_sym(const char *symbol, int32 *p_index) { - uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); - for (i = 0; i < num; i++) - if (!strcmp(target_sym_map[i].symbol_name, symbol)) { + uint32 i, num = 0; + SymbolMap *target_sym_map; + + if (!(target_sym_map = get_target_symbol_map(&num))) + return NULL; + + for (i = 0; i < num; i++) { + if (!strcmp(target_sym_map[i].symbol_name, symbol) +#if defined(_WIN32) || defined(_WIN32_) + /* In Win32, the symbol name of function added by + LLVMAddFunction() is prefixed by '_', ignore it */ + || (strlen(symbol) > 1 && symbol[0] == '_' + && !strcmp(target_sym_map[i].symbol_name, symbol + 1)) +#endif + ) { *p_index = (int32)i; return target_sym_map[i].symbol_addr; } + } return NULL; } -static inline uint32 -get_plt_item_size() +static bool +is_literal_relocation(const char *reloc_sec_name) { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* size of mov instruction and jmp instruction */ - return 12; -#elif defined(BUILD_TARGET_ARM) || defined(BUILD_TARGET_ARM_VFP) - /* 20 bytes instructions and 4 bytes symbol address */ - return 24; -#elif defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_THUMB_VFP) - /* 16 bytes instructions and 4 bytes symbol address */ - return 20; -#endif - return 0; + return !strcmp(reloc_sec_name, ".rela.literal"); } -static uint32 -get_plt_table_size() +static bool +str2uint32(const char *buf, uint32 *p_res) { - return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); + uint32 res = 0, val; + const char *buf_end = buf + 8; + char ch; + + while (buf < buf_end) { + ch = *buf++; + if (ch >= '0' && ch <= '9') + val = ch - '0'; + else if (ch >= 'a' && ch <= 'f') + val = ch - 'a' + 0xA; + else if (ch >= 'A' && ch <= 'F') + val = ch - 'A' + 0xA; + else + return false; + res = (res << 4) | val; + } + *p_res = res; + return true; } -static void -init_plt_table(uint8 *plt) +static bool +str2uint64(const char *buf, uint64 *p_res) { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); - for (i = 0; i < num; i++) { - uint8 *p = plt; - /* mov symbol_addr, rax */ - *p++ = 0x48; - *p++ = 0xB8; - *(uint64*)p = (uint64)(uintptr_t)target_sym_map[i].symbol_addr; - p += sizeof(uint64); - /* jmp rax */ - *p++ = 0xFF; - *p++ = 0xE0; - plt += get_plt_item_size(); - } -#endif - -#if defined(BUILD_TARGET_ARM) || defined(BUILD_TARGET_ARM_VFP) - uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); - for (i = 0; i < num; i++) { - uint32 *p = (uint32*)plt; - /* push {lr} */ - *p++ = 0xe52de004; - /* ldr lr, [pc, #8] */ - *p++ = 0xe59fe008; - /* blx lr */ - *p++ = 0xe12fff3e; - /* pop {lr} */ - *p++ = 0xe49de004; - /* bx lr */ - *p++ = 0xe12fff1e; - /* symbol addr */ - *p++ = (uint32)(uintptr_t)target_sym_map[i].symbol_addr;; - plt += get_plt_item_size(); + uint64 res = 0, val; + const char *buf_end = buf + 16; + char ch; + + while (buf < buf_end) { + ch = *buf++; + if (ch >= '0' && ch <= '9') + val = ch - '0'; + else if (ch >= 'a' && ch <= 'f') + val = ch - 'a' + 0xA; + else if (ch >= 'A' && ch <= 'F') + val = ch - 'A' + 0xA; + else + return false; + res = (res << 4) | val; } -#endif + *p_res = res; + return true; +} -#if defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_THUMB_VFP) - uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); - for (i = 0; i < num; i++) { - uint16 *p = (uint16*)plt; - /* push {lr} */ - *p++ = 0xb500; - /* push {r4, r5} */ - *p++ = 0xb430; - /* add r4, pc, #8 */ - *p++ = 0xa402; - /* ldr r5, [r4, #0] */ - *p++ = 0x6825; - /* blx r5 */ - *p++ = 0x47a8; - /* pop {r4, r5} */ - *p++ = 0xbc30; - /* pop {pc} */ - *p++ = 0xbd00; - p++; - /* symbol addr */ - *(uint32*)p = (uint32)(uintptr_t)target_sym_map[i].symbol_addr;; - plt += get_plt_item_size(); - } +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative offset to GOT */ +#define R_X86_64_GOTPCRELX 41 /* relaxable GOTPCREL */ +#define R_X86_64_REX_GOTPCRELX 42 /* relaxable GOTPCREL with REX prefix */ -#endif +static bool +is_text_section(const char *section_name) +{ + return !strcmp(section_name, ".text") || !strcmp(section_name, ".ltext"); } static bool -do_text_relocation(AOTModule *module, - AOTRelocationGroup *group, +do_text_relocation(AOTModule *module, AOTRelocationGroup *group, char *error_buf, uint32 error_buf_size) { - uint8 *aot_text = module->code; - uint32 aot_text_size = module->code_size; + bool is_literal = is_literal_relocation(group->section_name); + uint8 *aot_text = is_literal ? module->literal : module->code; + uint32 aot_text_size = + is_literal ? module->literal_size : module->code_size; uint32 i, func_index, symbol_len; - char symbol_buf[128] = { 0 }, *symbol, *p; +#if defined(BH_PLATFORM_WINDOWS) + uint32 ymm_plt_index = 0, xmm_plt_index = 0; + uint32 real_plt_index = 0, float_plt_index = 0, j; +#endif + char symbol_buf[128] = { 0 }, *symbol, *p; void *symbol_addr; AOTRelocation *relocation = group->relocations; if (group->relocation_count > 0 && !aot_text) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid text relocation count."); + "invalid text relocation count"); return false; } @@ -1657,65 +3209,197 @@ do_text_relocation(AOTModule *module, if (symbol_len + 1 <= sizeof(symbol_buf)) symbol = symbol_buf; else { - if (!(symbol = wasm_malloc(symbol_len + 1))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(symbol = loader_malloc(symbol_len + 1, error_buf, + error_buf_size))) { return false; } } - memcpy(symbol, relocation->symbol_name, symbol_len); + bh_memcpy_s(symbol, symbol_len, relocation->symbol_name, symbol_len); symbol[symbol_len] = '\0'; +#if WASM_ENABLE_STATIC_PGO != 0 + if (!strcmp(symbol, "__llvm_profile_runtime") + || !strcmp(symbol, "__llvm_profile_register_function") + || !strcmp(symbol, "__llvm_profile_register_names_function")) { + continue; + } +#endif + if (!strncmp(symbol, AOT_FUNC_PREFIX, strlen(AOT_FUNC_PREFIX))) { p = symbol + strlen(AOT_FUNC_PREFIX); if (*p == '\0' - || (func_index = (uint32)atoi(p)) > module->func_count) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid import symbol %s.", - symbol); + || (func_index = (uint32)atoi(p)) >= module->func_count) { + set_error_buf_v(error_buf, error_buf_size, + "invalid import symbol %s", symbol); + goto check_symbol_fail; + } +#if (defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)) \ + && !defined(BH_PLATFORM_WINDOWS) + if (relocation->relocation_type == R_X86_64_GOTPCREL + || relocation->relocation_type == R_X86_64_GOTPCRELX + || relocation->relocation_type == R_X86_64_REX_GOTPCRELX) { + GOTItem *got_item = module->got_item_list; + uint32 got_item_idx = 0; + + while (got_item) { + if (got_item->func_idx == func_index) + break; + got_item_idx++; + got_item = got_item->next; + } + /* Calculate `GOT + G` */ + symbol_addr = module->got_func_ptrs + got_item_idx; + } + else + symbol_addr = module->func_ptrs[func_index]; +#else + symbol_addr = module->func_ptrs[func_index]; +#endif + } +#if defined(BH_PLATFORM_WINDOWS) && defined(BUILD_TARGET_X86_32) + /* AOT function name starts with '_' in windows x86-32 */ + else if (!strncmp(symbol, "_" AOT_FUNC_PREFIX, + strlen("_" AOT_FUNC_PREFIX))) { + p = symbol + strlen("_" AOT_FUNC_PREFIX); + if (*p == '\0' + || (func_index = (uint32)atoi(p)) >= module->func_count) { + set_error_buf_v(error_buf, error_buf_size, "invalid symbol %s", + symbol); + goto check_symbol_fail; + } + symbol_addr = module->func_ptrs[func_index]; + } + else if (!strncmp(symbol, "_" AOT_FUNC_INTERNAL_PREFIX, + strlen("_" AOT_FUNC_INTERNAL_PREFIX))) { + p = symbol + strlen("_" AOT_FUNC_INTERNAL_PREFIX); + if (*p == '\0' + || (func_index = (uint32)atoi(p)) >= module->func_count) { + set_error_buf_v(error_buf, error_buf_size, "invalid symbol %s", + symbol); goto check_symbol_fail; } symbol_addr = module->func_ptrs[func_index]; } - else if (!strcmp(symbol, ".text")) { +#endif + else if (is_text_section(symbol)) { symbol_addr = module->code; } - else if (!strcmp(symbol, ".data") - || !strcmp(symbol, ".rodata") + else if (!strcmp(symbol, ".data") || !strcmp(symbol, ".sdata") + || !strcmp(symbol, ".rdata") || !strcmp(symbol, ".rodata") + || !strcmp(symbol, ".srodata") /* ".rodata.cst4/8/16/.." */ - || !strncmp(symbol, ".rodata.cst", strlen(".rodata.cst"))) { + || !strncmp(symbol, ".rodata.cst", strlen(".rodata.cst")) + /* ".srodata.cst4/8/16/.." */ + || !strncmp(symbol, ".srodata.cst", strlen(".srodata.cst")) + /* ".rodata.strn.m" */ + || !strncmp(symbol, ".rodata.str", strlen(".rodata.str")) + || !strcmp(symbol, AOT_STACK_SIZES_SECTION_NAME) +#if WASM_ENABLE_STATIC_PGO != 0 + || !strncmp(symbol, "__llvm_prf_cnts", 15) + || !strncmp(symbol, "__llvm_prf_data", 15) + || !strncmp(symbol, "__llvm_prf_names", 16) +#endif + ) { symbol_addr = get_data_section_addr(module, symbol, NULL); if (!symbol_addr) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid data section (%s).", - symbol); + set_error_buf_v(error_buf, error_buf_size, + "invalid data section (%s)", symbol); + goto check_symbol_fail; + } + } + else if (!strcmp(symbol, ".literal")) { + symbol_addr = module->literal; + } +#if defined(BH_PLATFORM_WINDOWS) + /* Relocation for symbols which start with "__ymm@", "__xmm@" or + "__real@" and end with the ymm value, xmm value or real value. + In Windows PE file, the data is stored in some individual ".rdata" + sections. We simply create extra plt data, parse the values from + the symbols and stored them into the extra plt data. */ + else if (!strcmp(group->section_name, ".text") + && !strncmp(symbol, YMM_PLT_PREFIX, strlen(YMM_PLT_PREFIX)) + && strlen(symbol) == strlen(YMM_PLT_PREFIX) + 64) { + char ymm_buf[17] = { 0 }; + + symbol_addr = module->extra_plt_data + ymm_plt_index * 32; + for (j = 0; j < 4; j++) { + bh_memcpy_s(ymm_buf, sizeof(ymm_buf), + symbol + strlen(YMM_PLT_PREFIX) + 48 - 16 * j, 16); + if (!str2uint64(ymm_buf, + (uint64 *)((uint8 *)symbol_addr + 8 * j))) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); + goto check_symbol_fail; + } + } + ymm_plt_index++; + } + else if (!strcmp(group->section_name, ".text") + && !strncmp(symbol, XMM_PLT_PREFIX, strlen(XMM_PLT_PREFIX)) + && strlen(symbol) == strlen(XMM_PLT_PREFIX) + 32) { + char xmm_buf[17] = { 0 }; + + symbol_addr = module->extra_plt_data + module->ymm_plt_count * 32 + + xmm_plt_index * 16; + for (j = 0; j < 2; j++) { + bh_memcpy_s(xmm_buf, sizeof(xmm_buf), + symbol + strlen(XMM_PLT_PREFIX) + 16 - 16 * j, 16); + if (!str2uint64(xmm_buf, + (uint64 *)((uint8 *)symbol_addr + 8 * j))) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); + goto check_symbol_fail; + } + } + xmm_plt_index++; + } + else if (!strcmp(group->section_name, ".text") + && !strncmp(symbol, REAL_PLT_PREFIX, strlen(REAL_PLT_PREFIX)) + && strlen(symbol) == strlen(REAL_PLT_PREFIX) + 16) { + char real_buf[17] = { 0 }; + + symbol_addr = module->extra_plt_data + module->ymm_plt_count * 32 + + module->xmm_plt_count * 16 + real_plt_index * 8; + bh_memcpy_s(real_buf, sizeof(real_buf), + symbol + strlen(REAL_PLT_PREFIX), 16); + if (!str2uint64(real_buf, (uint64 *)symbol_addr)) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); + goto check_symbol_fail; + } + real_plt_index++; + } + else if (!strcmp(group->section_name, ".text") + && !strncmp(symbol, REAL_PLT_PREFIX, strlen(REAL_PLT_PREFIX)) + && strlen(symbol) == strlen(REAL_PLT_PREFIX) + 8) { + char float_buf[9] = { 0 }; + + symbol_addr = module->extra_plt_data + module->ymm_plt_count * 32 + + module->xmm_plt_count * 16 + + module->real_plt_count * 8 + float_plt_index * 4; + bh_memcpy_s(float_buf, sizeof(float_buf), + symbol + strlen(REAL_PLT_PREFIX), 8); + if (!str2uint32(float_buf, (uint32 *)symbol_addr)) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); goto check_symbol_fail; } + float_plt_index++; } +#endif /* end of defined(BH_PLATFORM_WINDOWS) */ else if (!(symbol_addr = resolve_target_sym(symbol, &symbol_index))) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "resolve symbol %s failed.", - symbol); + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); goto check_symbol_fail; } if (symbol != symbol_buf) - wasm_free(symbol); - - if (!apply_relocation(module, - aot_text, aot_text_size, - relocation->relocation_offset, - relocation->relocation_addend, - relocation->relocation_type, - symbol_addr, symbol_index, - error_buf, error_buf_size)) + wasm_runtime_free(symbol); + + if (!apply_relocation( + module, aot_text, aot_text_size, relocation->relocation_offset, + relocation->relocation_addend, relocation->relocation_type, + symbol_addr, symbol_index, error_buf, error_buf_size)) return false; } @@ -1723,20 +3407,19 @@ do_text_relocation(AOTModule *module, check_symbol_fail: if (symbol != symbol_buf) - wasm_free(symbol); + wasm_runtime_free(symbol); return false; } static bool -do_data_relocation(AOTModule *module, - AOTRelocationGroup *group, +do_data_relocation(AOTModule *module, AOTRelocationGroup *group, char *error_buf, uint32 error_buf_size) { uint8 *data_addr; uint32 data_size = 0, i; AOTRelocation *relocation = group->relocations; - void *symbol_addr; + void *symbol_addr = NULL; char *symbol, *data_section_name; if (!strncmp(group->section_name, ".rela.", 6)) { @@ -1745,42 +3428,89 @@ do_data_relocation(AOTModule *module, else if (!strncmp(group->section_name, ".rel.", 5)) { data_section_name = group->section_name + strlen(".rel"); } + else if (!strcmp(group->section_name, ".rdata")) { + data_section_name = group->section_name; + } +#if WASM_ENABLE_STATIC_PGO != 0 + else if (!strncmp(group->section_name, ".rel__llvm_prf_data", 19)) { + data_section_name = group->section_name + strlen(".rel"); + } + else if (!strncmp(group->section_name, ".rela__llvm_prf_data", 20)) { + data_section_name = group->section_name + strlen(".rela"); + } +#endif else { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid data relocation section name."); + "invalid data relocation section name"); return false; } - data_addr = get_data_section_addr(module, data_section_name, - &data_size); + data_addr = get_data_section_addr(module, data_section_name, &data_size); + if (group->relocation_count > 0 && !data_addr) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid data relocation count."); + "invalid data relocation count"); return false; } for (i = 0; i < group->relocation_count; i++, relocation++) { symbol = relocation->symbol_name; - if (!strcmp(symbol, ".text")) { + if (is_text_section(symbol)) { symbol_addr = module->code; } +#if WASM_ENABLE_STATIC_PGO != 0 + else if (!strncmp(symbol, AOT_FUNC_PREFIX, strlen(AOT_FUNC_PREFIX))) { + char *p = symbol + strlen(AOT_FUNC_PREFIX); + uint32 func_index; + if (*p == '\0' + || (func_index = (uint32)atoi(p)) >= module->func_count) { + set_error_buf_v(error_buf, error_buf_size, + "invalid relocation symbol %s", symbol); + return false; + } + symbol_addr = module->func_ptrs[func_index]; + } + else if (!strcmp(symbol, "__llvm_prf_cnts")) { + uint32 j; + for (j = 0; j < module->data_section_count; j++) { + if (!strncmp(module->data_sections[j].name, symbol, 15)) { + bh_assert(relocation->relocation_addend + sizeof(uint64) + <= module->data_sections[j].size); + symbol_addr = module->data_sections[j].data; + break; + } + } + if (j == module->data_section_count) { + set_error_buf_v(error_buf, error_buf_size, + "invalid relocation symbol %s", symbol); + return false; + } + } + else if (!strncmp(symbol, "__llvm_prf_cnts", 15)) { + uint32 j; + for (j = 0; j < module->data_section_count; j++) { + if (!strcmp(module->data_sections[j].name, symbol)) { + symbol_addr = module->data_sections[j].data; + break; + } + } + if (j == module->data_section_count) { + set_error_buf_v(error_buf, error_buf_size, + "invalid relocation symbol %s", symbol); + return false; + } + } +#endif /* end of WASM_ENABLE_STATIC_PGO != 0 */ else { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid relocation symbol %s.", - symbol); + set_error_buf_v(error_buf, error_buf_size, + "invalid relocation symbol %s", symbol); return false; } - if (!apply_relocation(module, - data_addr, data_size, - relocation->relocation_offset, - relocation->relocation_addend, - relocation->relocation_type, - symbol_addr, -1, - error_buf, error_buf_size)) + if (!apply_relocation( + module, data_addr, data_size, relocation->relocation_offset, + relocation->relocation_addend, relocation->relocation_type, + symbol_addr, -1, error_buf, error_buf_size)) return false; } @@ -1788,8 +3518,7 @@ do_data_relocation(AOTModule *module, } static bool -validate_symbol_table(uint8 *buf, uint8 *buf_end, - uint32 *offsets, uint32 count, +validate_symbol_table(uint8 *buf, uint8 *buf_end, uint32 *offsets, uint32 count, char *error_buf, uint32 error_buf_size) { uint32 i, str_len_addr = 0; @@ -1803,7 +3532,7 @@ validate_symbol_table(uint8 *buf, uint8 *buf_end, str_len_addr += (uint32)sizeof(uint16) + str_len; str_len_addr = align_uint(str_len_addr, 2); buf += str_len; - buf = (uint8*)align_ptr(buf, 2); + buf = (uint8 *)align_ptr(buf, 2); } if (buf == buf_end) @@ -1814,17 +3543,18 @@ validate_symbol_table(uint8 *buf, uint8 *buf_end, static bool load_relocation_section(const uint8 *buf, const uint8 *buf_end, - AOTModule *module, + AOTModule *module, bool is_load_from_file_buf, char *error_buf, uint32 error_buf_size) { AOTRelocationGroup *groups = NULL, *group; uint32 symbol_count = 0; - uint32 group_count = 0, i, j, func_index, func_type_index; + uint32 group_count = 0, i, j, got_item_count = 0; uint64 size; uint32 *symbol_offsets, total_string_len; uint8 *symbol_buf, *symbol_buf_end; + int map_prot, map_flags; bool ret = false; - AOTExportFunc *export_func; + char **symbols = NULL; read_uint32(buf, buf_end, symbol_count); @@ -1838,78 +3568,300 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end, symbol_buf = (uint8 *)buf; symbol_buf_end = symbol_buf + total_string_len; - if (!validate_symbol_table(symbol_buf, symbol_buf_end, - symbol_offsets, symbol_count, - error_buf, error_buf_size)) { + if (!validate_symbol_table(symbol_buf, symbol_buf_end, symbol_offsets, + symbol_count, error_buf, error_buf_size)) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "validate symbol table failed."); + "validate symbol table failed"); goto fail; } + if (symbol_count > 0) { + symbols = loader_malloc((uint64)sizeof(*symbols) * symbol_count, + error_buf, error_buf_size); + if (symbols == NULL) { + goto fail; + } + } + +#if defined(BH_PLATFORM_WINDOWS) + buf = symbol_buf_end; + read_uint32(buf, buf_end, group_count); + + for (i = 0; i < group_count; i++) { + uint32 name_index, relocation_count; + uint16 group_name_len; + uint8 *group_name; + + /* section name address is 4 bytes aligned. */ + buf = (uint8 *)align_ptr(buf, sizeof(uint32)); + read_uint32(buf, buf_end, name_index); + + if (name_index >= symbol_count) { + set_error_buf(error_buf, error_buf_size, + "symbol index out of range"); + goto fail; + } + + group_name = symbol_buf + symbol_offsets[name_index]; + group_name_len = *(uint16 *)group_name; + group_name += sizeof(uint16); + + read_uint32(buf, buf_end, relocation_count); + + for (j = 0; j < relocation_count; j++) { + AOTRelocation relocation = { 0 }; + char group_name_buf[128] = { 0 }; + char symbol_name_buf[128] = { 0 }; + uint32 symbol_index, offset32; + int32 addend32; + uint16 symbol_name_len; + uint8 *symbol_name; + + if (sizeof(void *) == 8) { + read_uint64(buf, buf_end, relocation.relocation_offset); + read_uint64(buf, buf_end, relocation.relocation_addend); + } + else { + read_uint32(buf, buf_end, offset32); + relocation.relocation_offset = (uint64)offset32; + read_uint32(buf, buf_end, addend32); + relocation.relocation_addend = (int64)addend32; + } + read_uint32(buf, buf_end, relocation.relocation_type); + read_uint32(buf, buf_end, symbol_index); + + if (symbol_index >= symbol_count) { + set_error_buf(error_buf, error_buf_size, + "symbol index out of range"); + goto fail; + } + + symbol_name = symbol_buf + symbol_offsets[symbol_index]; + symbol_name_len = *(uint16 *)symbol_name; + symbol_name += sizeof(uint16); + + bh_memcpy_s(group_name_buf, (uint32)sizeof(group_name_buf), + group_name, group_name_len); + bh_memcpy_s(symbol_name_buf, (uint32)sizeof(symbol_name_buf), + symbol_name, symbol_name_len); + + /* aot compiler emits string with '\0' since 2.0.0 */ + if (group_name_len == strlen(".text") + 1 + && !strncmp(group_name, ".text", strlen(".text"))) { + /* aot compiler emits string with '\0' since 2.0.0 */ + if (symbol_name_len == strlen(YMM_PLT_PREFIX) + 64 + 1 + && !strncmp(symbol_name, YMM_PLT_PREFIX, + strlen(YMM_PLT_PREFIX))) { + module->ymm_plt_count++; + } + else if (symbol_name_len == strlen(XMM_PLT_PREFIX) + 32 + 1 + && !strncmp(symbol_name, XMM_PLT_PREFIX, + strlen(XMM_PLT_PREFIX))) { + module->xmm_plt_count++; + } + else if (symbol_name_len == strlen(REAL_PLT_PREFIX) + 16 + 1 + && !strncmp(symbol_name, REAL_PLT_PREFIX, + strlen(REAL_PLT_PREFIX))) { + module->real_plt_count++; + } + else if (symbol_name_len >= strlen(REAL_PLT_PREFIX) + 8 + 1 + && !strncmp(symbol_name, REAL_PLT_PREFIX, + strlen(REAL_PLT_PREFIX))) { + module->float_plt_count++; + } + } + } + } + + /* Allocate memory for extra plt data */ + size = sizeof(uint64) * 4 * module->ymm_plt_count + + sizeof(uint64) * 2 * module->xmm_plt_count + + sizeof(uint64) * module->real_plt_count + + sizeof(uint32) * module->float_plt_count; + if (size > 0) { + if (size > UINT32_MAX + || !(module->extra_plt_data = loader_mmap( + (uint32)size, true, error_buf, error_buf_size))) { + goto fail; + } + module->extra_plt_data_size = (uint32)size; + } +#endif /* end of defined(BH_PLATFORM_WINDOWS) */ + +#if (defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)) \ + && !defined(BH_PLATFORM_WINDOWS) + buf = symbol_buf_end; + read_uint32(buf, buf_end, group_count); + + /* Resolve the relocations of type R_X86_64_GOTPCREL */ + for (i = 0; i < group_count; i++) { + uint32 name_index, relocation_count; + uint16 group_name_len; + uint8 *group_name; + + /* section name address is 4 bytes aligned. */ + buf = (uint8 *)align_ptr(buf, sizeof(uint32)); + read_uint32(buf, buf_end, name_index); + + if (name_index >= symbol_count) { + set_error_buf(error_buf, error_buf_size, + "symbol index out of range"); + goto fail; + } + + group_name = symbol_buf + symbol_offsets[name_index]; + group_name_len = *(uint16 *)group_name; + group_name += sizeof(uint16); + + read_uint32(buf, buf_end, relocation_count); + + for (j = 0; j < relocation_count; j++) { + AOTRelocation relocation = { 0 }; + char group_name_buf[128] = { 0 }; + char symbol_name_buf[128] = { 0 }; + uint32 symbol_index; + uint16 symbol_name_len; + uint8 *symbol_name; + + /* relocation offset and addend */ + buf += sizeof(void *) * 2; + + read_uint32(buf, buf_end, relocation.relocation_type); + read_uint32(buf, buf_end, symbol_index); + + if (symbol_index >= symbol_count) { + set_error_buf(error_buf, error_buf_size, + "symbol index out of range"); + goto fail; + } + + symbol_name = symbol_buf + symbol_offsets[symbol_index]; + symbol_name_len = *(uint16 *)symbol_name; + symbol_name += sizeof(uint16); + + bh_memcpy_s(group_name_buf, (uint32)sizeof(group_name_buf), + group_name, group_name_len); + bh_memcpy_s(symbol_name_buf, (uint32)sizeof(symbol_name_buf), + symbol_name, symbol_name_len); + + if ((relocation.relocation_type == R_X86_64_GOTPCREL + || relocation.relocation_type == R_X86_64_GOTPCRELX + || relocation.relocation_type == R_X86_64_REX_GOTPCRELX) + && !strncmp(symbol_name_buf, AOT_FUNC_PREFIX, + strlen(AOT_FUNC_PREFIX))) { + uint32 func_idx = + atoi(symbol_name_buf + strlen(AOT_FUNC_PREFIX)); + GOTItem *got_item = module->got_item_list; + + if (func_idx >= module->func_count) { + set_error_buf(error_buf, error_buf_size, + "func index out of range"); + goto fail; + } + + while (got_item) { + if (got_item->func_idx == func_idx) + break; + got_item = got_item->next; + } + + if (!got_item) { + /* Create the got item and append to the list */ + got_item = wasm_runtime_malloc(sizeof(GOTItem)); + if (!got_item) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail; + } + + got_item->func_idx = func_idx; + got_item->next = NULL; + if (!module->got_item_list) { + module->got_item_list = module->got_item_list_end = + got_item; + } + else { + module->got_item_list_end->next = got_item; + module->got_item_list_end = got_item; + } + + got_item_count++; + } + } + } + } + + if (got_item_count) { + GOTItem *got_item = module->got_item_list; + uint32 got_item_idx = 0; + + /* Create the GOT for func_ptrs, note that it is different from + the .got section of a dynamic object file */ + size = (uint64)sizeof(void *) * got_item_count; + if (size > UINT32_MAX + || !(module->got_func_ptrs = loader_mmap( + (uint32)size, false, error_buf, error_buf_size))) { + goto fail; + } + + while (got_item) { + module->got_func_ptrs[got_item_idx++] = + module->func_ptrs[got_item->func_idx]; + got_item = got_item->next; + } + + module->got_item_count = got_item_count; + } +#else + (void)got_item_count; +#endif /* (defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)) && \ + !defined(BH_PLATFORM_WINDOWS) */ + buf = symbol_buf_end; read_uint32(buf, buf_end, group_count); /* Allocate memory for relocation groups */ size = sizeof(AOTRelocationGroup) * (uint64)group_count; - if (size >= UINT32_MAX || !(groups = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (size > 0 + && !(groups = loader_malloc(size, error_buf, error_buf_size))) { goto fail; } - memset(groups, 0, size); - /* Load each relocation group */ for (i = 0, group = groups; i < group_count; i++, group++) { AOTRelocation *relocation; uint32 name_index; - uint16 str_len; - uint8 *name_addr; /* section name address is 4 bytes aligned. */ - buf = (uint8*)align_ptr(buf, sizeof(uint32)); + buf = (uint8 *)align_ptr(buf, sizeof(uint32)); read_uint32(buf, buf_end, name_index); if (name_index >= symbol_count) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "symbol index out of range."); + "symbol index out of range"); goto fail; } - name_addr = symbol_buf + symbol_offsets[name_index]; - str_len = *(uint16 *)name_addr; - - if (!(group->section_name = - const_str_set_insert(name_addr + sizeof(uint16), - (int32)str_len, module, - error_buf, error_buf_size))) { - goto fail; + if (symbols[name_index] == NULL) { + uint8 *name_addr = symbol_buf + symbol_offsets[name_index]; + + read_string(name_addr, buf_end, symbols[name_index]); } + group->section_name = symbols[name_index]; read_uint32(buf, buf_end, group->relocation_count); /* Allocate memory for relocations */ size = sizeof(AOTRelocation) * (uint64)group->relocation_count; - if (size >= UINT32_MAX - || !(group->relocations = relocation = - wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(group->relocations = relocation = + loader_malloc(size, error_buf, error_buf_size))) { ret = false; goto fail; } - memset(group->relocations, 0, size); - /* Load each relocation */ for (j = 0; j < group->relocation_count; j++, relocation++) { uint32 symbol_index; - uint16 str_len; - uint8 *symbol_addr; if (sizeof(void *) == 8) { read_uint64(buf, buf_end, relocation->relocation_offset); @@ -1920,75 +3872,132 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end, read_uint32(buf, buf_end, offset32); relocation->relocation_offset = (uint64)offset32; read_uint32(buf, buf_end, addend32); - relocation->relocation_addend = (uint64)addend32; + relocation->relocation_addend = (int64)(int32)addend32; } read_uint32(buf, buf_end, relocation->relocation_type); read_uint32(buf, buf_end, symbol_index); if (symbol_index >= symbol_count) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "symbol index out of range."); + "symbol index out of range"); goto fail; } - symbol_addr = symbol_buf + symbol_offsets[symbol_index]; - str_len = *(uint16 *)symbol_addr; + if (symbols[symbol_index] == NULL) { + uint8 *symbol_addr = symbol_buf + symbol_offsets[symbol_index]; - if (!(relocation->symbol_name = - const_str_set_insert(symbol_addr + sizeof(uint16), - (int32)str_len, module, - error_buf, error_buf_size))) { - goto fail; + read_string(symbol_addr, buf_end, symbols[symbol_index]); } + relocation->symbol_name = symbols[symbol_index]; } if (!strcmp(group->section_name, ".rel.text") - || !strcmp(group->section_name, ".rela.text")) { + || !strcmp(group->section_name, ".rela.text") + || !strcmp(group->section_name, ".rel.ltext") + || !strcmp(group->section_name, ".rela.ltext") + || !strcmp(group->section_name, ".rela.literal") +#ifdef BH_PLATFORM_WINDOWS + || !strcmp(group->section_name, ".text") +#endif + ) { +#if !defined(BH_PLATFORM_LINUX) && !defined(BH_PLATFORM_LINUX_SGX) \ + && !defined(BH_PLATFORM_DARWIN) && !defined(BH_PLATFORM_WINDOWS) \ + && !defined(BH_PLATFORM_ANDROID) + if (module->is_indirect_mode) { + set_error_buf(error_buf, error_buf_size, + "cannot apply relocation to text section " + "for aot file generated with " + "\"--enable-indirect-mode\" flag"); + goto fail; + } +#endif if (!do_text_relocation(module, group, error_buf, error_buf_size)) - return false; + goto fail; } else { if (!do_data_relocation(module, group, error_buf, error_buf_size)) - return false; + goto fail; } } - export_func = module->export_funcs; - for (i = 0; i < module->export_func_count; i++, export_func++) { - func_index = export_func->func_index - module->import_func_count; - if (func_index >= module->func_count) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid export function index."); - ret = false; - goto fail; + /* Set read only for AOT code and some data sections */ + map_prot = MMAP_PROT_READ | MMAP_PROT_EXEC; + + if (module->code) { + /* The layout is: literal size + literal + code (with plt table) */ + uint8 *mmap_addr = module->literal - sizeof(uint32); + uint32 total_size = + sizeof(uint32) + module->literal_size + module->code_size; + os_mprotect(mmap_addr, total_size, map_prot); + } + + map_prot = MMAP_PROT_READ; + +#if defined(BH_PLATFORM_WINDOWS) + if (module->extra_plt_data) { + os_mprotect(module->extra_plt_data, module->extra_plt_data_size, + map_prot); + } +#endif + + for (i = 0; i < module->data_section_count; i++) { + AOTObjectDataSection *data_section = module->data_sections + i; + if (!strcmp(data_section->name, ".rdata") + || !strcmp(data_section->name, ".rodata") + /* ".rodata.cst4/8/16/.." */ + || !strncmp(data_section->name, ".rodata.cst", + strlen(".rodata.cst")) + /* ".rodata.strn.m" */ + || !strncmp(data_section->name, ".rodata.str", + strlen(".rodata.str"))) { + os_mprotect(data_section->data, data_section->size, map_prot); } - func_type_index = module->func_type_indexes[func_index]; - export_func->func_type = module->func_types[func_type_index]; - export_func->func_ptr = module->func_ptrs[func_index]; } ret = true; fail: + if (symbols) { + wasm_runtime_free(symbols); + } if (groups) { for (i = 0, group = groups; i < group_count; i++, group++) if (group->relocations) - wasm_free(group->relocations); - wasm_free(groups); + wasm_runtime_free(group->relocations); + wasm_runtime_free(groups); } + (void)map_flags; return ret; } +#if WASM_ENABLE_MEMORY64 != 0 +static bool +has_module_memory64(AOTModule *module) +{ + /* TODO: multi-memories for now assuming the memory idx type is consistent + * across multi-memories */ + if (module->import_memory_count > 0) + return !!(module->import_memories[0].mem_type.flags & MEMORY64_FLAG); + else if (module->memory_count > 0) + return !!(module->memories[0].flags & MEMORY64_FLAG); + + return false; +} +#endif + static bool load_from_sections(AOTModule *module, AOTSection *sections, - char *error_buf, uint32 error_buf_size) + bool is_load_from_file_buf, bool no_resolve, char *error_buf, + uint32 error_buf_size) { AOTSection *section = sections; const uint8 *buf, *buf_end; uint32 last_section_type = (uint32)-1, section_type; + uint32 i, func_index, func_type_index; + AOTFuncType *func_type; + AOTExport *exports; + uint8 malloc_free_io_type = VALUE_TYPE_I32; while (section) { buf = section->section_body; @@ -1998,109 +4007,250 @@ load_from_sections(AOTModule *module, AOTSection *sections, if ((last_section_type == (uint32)-1 && section_type != AOT_SECTION_TYPE_TARGET_INFO) || (last_section_type != (uint32)-1 - && section_type != last_section_type + 1)) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid section order."); + && (section_type != last_section_type + 1 + && section_type != AOT_SECTION_TYPE_CUSTOM))) { + set_error_buf(error_buf, error_buf_size, "invalid section order"); return false; } last_section_type = section_type; switch (section_type) { case AOT_SECTION_TYPE_TARGET_INFO: - if (!load_target_info_section(buf, buf_end, module, - error_buf, error_buf_size)) + if (!load_target_info_section(buf, buf_end, module, error_buf, + error_buf_size)) return false; break; case AOT_SECTION_TYPE_INIT_DATA: if (!load_init_data_section(buf, buf_end, module, + is_load_from_file_buf, no_resolve, error_buf, error_buf_size)) return false; break; case AOT_SECTION_TYPE_TEXT: - if (!load_text_section(buf, buf_end, module, - error_buf, error_buf_size)) +#if !defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF) + /* try to merge .data and .text, with exceptions: + * 1. XIP mode + * 2. pre-mmapped module load from aot_load_from_sections() + * 3. nuttx & esp-idf: have separate region for MMAP_PROT_EXEC + */ + if (!module->is_indirect_mode && is_load_from_file_buf) + if (!try_merge_data_and_text(&buf, &buf_end, module, + error_buf, error_buf_size)) + LOG_WARNING("merge .data and .text sections failed"); +#endif /* ! defined(BH_PLATFORM_NUTTX) && !defined(BH_PLATFORM_ESP_IDF) */ + if (!load_text_section(buf, buf_end, module, error_buf, + error_buf_size)) return false; break; case AOT_SECTION_TYPE_FUNCTION: - if (!load_function_section(buf, buf_end, module, - error_buf, error_buf_size)) + if (!load_function_section(buf, buf_end, module, error_buf, + error_buf_size)) return false; break; case AOT_SECTION_TYPE_EXPORT: if (!load_export_section(buf, buf_end, module, - error_buf, error_buf_size)) + is_load_from_file_buf, error_buf, + error_buf_size)) return false; break; case AOT_SECTION_TYPE_RELOCATION: if (!load_relocation_section(buf, buf_end, module, - error_buf, error_buf_size)) + is_load_from_file_buf, error_buf, + error_buf_size)) + return false; + break; + case AOT_SECTION_TYPE_CUSTOM: + if (!load_custom_section(buf, buf_end, module, + is_load_from_file_buf, error_buf, + error_buf_size)) return false; break; + default: + set_error_buf(error_buf, error_buf_size, + "invalid aot section type"); + return false; } section = section->next; } - if (last_section_type != AOT_SECTION_TYPE_RELOCATION) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: section missing."); + if (last_section_type != AOT_SECTION_TYPE_RELOCATION + && last_section_type != AOT_SECTION_TYPE_CUSTOM) { + set_error_buf(error_buf, error_buf_size, "section missing"); return false; } - return true; -} + /* Resolve malloc and free function */ + module->malloc_func_index = (uint32)-1; + module->free_func_index = (uint32)-1; + module->retain_func_index = (uint32)-1; +#if WASM_ENABLE_MEMORY64 != 0 + if (has_module_memory64(module)) + malloc_free_io_type = VALUE_TYPE_I64; +#endif + exports = module->exports; + for (i = 0; i < module->export_count; i++) { + if (exports[i].kind == EXPORT_KIND_FUNC + && exports[i].index >= module->import_func_count) { + if (!strcmp(exports[i].name, "malloc")) { + func_index = exports[i].index - module->import_func_count; + func_type_index = module->func_type_indexes[func_index]; + func_type = (AOTFuncType *)module->types[func_type_index]; + if (func_type->param_count == 1 && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { + bh_assert(module->malloc_func_index == (uint32)-1); + module->malloc_func_index = func_index; + LOG_VERBOSE("Found malloc function, name: %s, index: %u", + exports[i].name, exports[i].index); + } + } + else if (!strcmp(exports[i].name, "__new")) { + func_index = exports[i].index - module->import_func_count; + func_type_index = module->func_type_indexes[func_index]; + func_type = (AOTFuncType *)module->types[func_type_index]; + if (func_type->param_count == 2 && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == VALUE_TYPE_I32 + && func_type->types[2] == malloc_free_io_type) { + uint32 j; + WASMExport *export_tmp; + + bh_assert(module->malloc_func_index == (uint32)-1); + module->malloc_func_index = func_index; + LOG_VERBOSE("Found malloc function, name: %s, index: %u", + exports[i].name, exports[i].index); + + /* resolve retain function. + If not find, reset malloc function index */ + export_tmp = module->exports; + for (j = 0; j < module->export_count; j++, export_tmp++) { + if ((export_tmp->kind == EXPORT_KIND_FUNC) + && (!strcmp(export_tmp->name, "__retain") + || !strcmp(export_tmp->name, "__pin"))) { + func_index = + export_tmp->index - module->import_func_count; + func_type_index = + module->func_type_indexes[func_index]; + func_type = + (AOTFuncType *)module->types[func_type_index]; + if (func_type->param_count == 1 + && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { + bh_assert(module->retain_func_index + == (uint32)-1); + module->retain_func_index = export_tmp->index; + LOG_VERBOSE("Found retain function, name: %s, " + "index: %u", + export_tmp->name, + export_tmp->index); + break; + } + } + } + if (j == module->export_count) { + module->malloc_func_index = (uint32)-1; + LOG_VERBOSE("Can't find retain function," + "reset malloc function index to -1"); + } + } + } + else if ((!strcmp(exports[i].name, "free")) + || (!strcmp(exports[i].name, "__release")) + || (!strcmp(exports[i].name, "__unpin"))) { + func_index = exports[i].index - module->import_func_count; + func_type_index = module->func_type_indexes[func_index]; + func_type = (AOTFuncType *)module->types[func_type_index]; + if (func_type->param_count == 1 && func_type->result_count == 0 + && func_type->types[0] == malloc_free_io_type) { + bh_assert(module->free_func_index == (uint32)-1); + module->free_func_index = func_index; + LOG_VERBOSE("Found free function, name: %s, index: %u", + exports[i].name, exports[i].index); + } + } + } + } -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 -static void aot_free(void *ptr) -{ - wasm_free(ptr); -} -#else -#define aot_free wasm_free + /* Flush data cache before executing AOT code, + * otherwise unpredictable behavior can occur. */ + os_dcache_flush(); + +#if WASM_ENABLE_MEMORY_TRACING != 0 + wasm_runtime_dump_module_mem_consumption((WASMModuleCommon *)module); +#endif + +#if WASM_ENABLE_DEBUG_AOT != 0 + if (!jit_code_entry_create(module->elf_hdr, module->elf_size)) { + set_error_buf(error_buf, error_buf_size, + "create jit code entry failed"); + return false; + } #endif + return true; +} -static AOTModule* -create_module(char *error_buf, uint32 error_buf_size) +static AOTModule * +create_module(char *name, char *error_buf, uint32 error_buf_size) { - AOTModule *module = wasm_malloc(sizeof(AOTModule)); + AOTModule *module = + loader_malloc(sizeof(AOTModule), error_buf, error_buf_size); + bh_list_status ret; if (!module) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); return NULL; } - memset(module, 0, sizeof(AOTModule)); - module->module_type = Wasm_Module_AoT; - if (!(module->const_str_set = - bh_hash_map_create(32, false, - (HashFunc)wasm_string_hash, - (KeyEqualFunc)wasm_string_equal, - NULL, - aot_free))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "create const string set failed."); - wasm_free(module); - return NULL; + module->name = name; + module->is_binary_freeable = false; + +#if WASM_ENABLE_MULTI_MODULE != 0 + module->import_module_list = &module->import_module_list_head; + ret = bh_list_init(module->import_module_list); + bh_assert(ret == BH_LIST_SUCCESS); +#endif + (void)ret; + +#if WASM_ENABLE_GC != 0 + if (!(module->ref_type_set = + wasm_reftype_set_create(GC_REFTYPE_MAP_SIZE_DEFAULT))) { + set_error_buf(error_buf, error_buf_size, "create reftype map failed"); + goto fail1; } + if (os_mutex_init(&module->rtt_type_lock)) { + set_error_buf(error_buf, error_buf_size, "init rtt type lock failed"); + goto fail2; + } +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + wasi_args_set_defaults(&module->wasi_args); +#endif /* WASM_ENABLE_LIBC_WASI != 0 */ + return module; +#if WASM_ENABLE_GC != 0 +fail2: + bh_hash_map_destroy(module->ref_type_set); +fail1: +#endif + wasm_runtime_free(module); + return NULL; } -AOTModule* -aot_load_from_sections(AOTSection *section_list, - char *error_buf, uint32 error_buf_size) +AOTModule * +aot_load_from_sections(AOTSection *section_list, char *error_buf, + uint32 error_buf_size) { - AOTModule *module = create_module(error_buf, error_buf_size); + AOTModule *module = create_module("", error_buf, error_buf_size); if (!module) return NULL; - if (!load_from_sections(module, section_list, - error_buf, error_buf_size)) { + if (!load_from_sections(module, section_list, false, false, error_buf, + error_buf_size)) { aot_unload(module); return NULL; } @@ -2115,85 +4265,128 @@ destroy_sections(AOTSection *section_list, bool destroy_aot_text) AOTSection *section = section_list, *next; while (section) { next = section->next; - if (destroy_aot_text - && section->section_type == AOT_SECTION_TYPE_TEXT + if (destroy_aot_text && section->section_type == AOT_SECTION_TYPE_TEXT && section->section_body) - bh_munmap((uint8*)section->section_body, section->section_body_size); - wasm_free(section); + os_munmap((uint8 *)section->section_body, + section->section_body_size); + wasm_runtime_free(section); section = next; } } static bool -create_sections(const uint8 *buf, uint32 size, - AOTSection **p_section_list, - char *error_buf, uint32 error_buf_size) +resolve_execute_mode(const uint8 *buf, uint32 size, bool *p_mode, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf + size; + uint32 section_type; + uint32 section_size = 0; + uint16 e_type = 0; + + p += 8; + while (p < p_end) { + read_uint32(p, p_end, section_type); + if (section_type <= AOT_SECTION_TYPE_SIGNATURE) { + read_uint32(p, p_end, section_size); + CHECK_BUF(p, p_end, section_size); + if (section_type == AOT_SECTION_TYPE_TARGET_INFO) { + p += 4; + read_uint16(p, p_end, e_type); + if (e_type == E_TYPE_XIP) { + *p_mode = true; + } + else { + *p_mode = false; + } + break; + } + } + else { /* section_type > AOT_SECTION_TYPE_SIGNATURE */ + set_error_buf(error_buf, error_buf_size, + "resolve execute mode failed"); + break; + } + p += section_size; + } + return true; +fail: + return false; +} + +static bool +create_sections(AOTModule *module, const uint8 *buf, uint32 size, + AOTSection **p_section_list, char *error_buf, + uint32 error_buf_size) { AOTSection *section_list = NULL, *section_list_end = NULL, *section; const uint8 *p = buf, *p_end = buf + size; + bool destroy_aot_text = false; + bool is_indirect_mode = false; uint32 section_type; uint32 section_size; uint64 total_size; uint8 *aot_text; +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + uint8 *mirrored_text; +#endif + + if (!resolve_execute_mode(buf, size, &is_indirect_mode, error_buf, + error_buf_size)) { + goto fail; + } + + module->is_indirect_mode = is_indirect_mode; p += 8; while (p < p_end) { read_uint32(p, p_end, section_type); - if (section_type < AOT_SECTION_TYPE_SIGANATURE) { + if (section_type < AOT_SECTION_TYPE_SIGNATURE + || section_type == AOT_SECTION_TYPE_CUSTOM) { read_uint32(p, p_end, section_size); CHECK_BUF(p, p_end, section_size); - if (!(section = wasm_malloc(sizeof(AOTSection)))) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "allocate memory failed."); + if (!(section = loader_malloc(sizeof(AOTSection), error_buf, + error_buf_size))) { goto fail; } memset(section, 0, sizeof(AOTSection)); section->section_type = (int32)section_type; - section->section_body = p; + section->section_body = (uint8 *)p; section->section_body_size = section_size; if (section_type == AOT_SECTION_TYPE_TEXT) { - if (section_size > 0) { - int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE - | MMAP_PROT_EXEC; -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* aot code and data in x86_64 must be in range 0 to 2G due to - relocation for R_X86_64_32/32S/PC32 */ - int map_flags = MMAP_MAP_32BIT; -#else - int map_flags = MMAP_MAP_NONE; -#endif - total_size = (uint64)section_size + aot_get_plt_table_size(); + if ((section_size > 0) && !module->is_indirect_mode) { + total_size = + (uint64)section_size + aot_get_plt_table_size(); total_size = (total_size + 3) & ~((uint64)3); if (total_size >= UINT32_MAX - || !(aot_text = bh_mmap(NULL, (uint32)total_size, - map_prot, map_flags))) { - wasm_free(section); - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "mmap memory failed."); + || !(aot_text = + loader_mmap((uint32)total_size, true, + error_buf, error_buf_size))) { + wasm_runtime_free(section); goto fail; } -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - /* address must be in the first 2 Gigabytes of - the process address space */ - bh_assert((uintptr_t)aot_text < INT32_MAX); -#endif + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + mirrored_text = os_get_dbus_mirror(aot_text); + bh_assert(mirrored_text != NULL); + bh_memcpy_s(mirrored_text, (uint32)total_size, + section->section_body, (uint32)section_size); + os_dcache_flush(); +#else bh_memcpy_s(aot_text, (uint32)total_size, section->section_body, (uint32)section_size); +#endif section->section_body = aot_text; + destroy_aot_text = true; if ((uint32)total_size > section->section_body_size) { - memset(aot_text + (uint32)section_size, - 0, (uint32)total_size - section_size); + memset(aot_text + (uint32)section_size, 0, + (uint32)total_size - section_size); section->section_body_size = (uint32)total_size; } } - else - section->section_body = NULL; } if (!section_list) @@ -2206,15 +4399,13 @@ create_sections(const uint8 *buf, uint32 size, p += section_size; } else { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: invalid section id."); + set_error_buf(error_buf, error_buf_size, "invalid section id"); goto fail; } } if (!section_list) { - set_error_buf(error_buf, error_buf_size, - "AOT module load failed: create section list failed."); + set_error_buf(error_buf, error_buf_size, "create section list failed"); return false; } @@ -2222,13 +4413,24 @@ create_sections(const uint8 *buf, uint32 size, return true; fail: if (section_list) - destroy_sections(section_list, true); + destroy_sections(section_list, destroy_aot_text); return false; } +static bool +aot_compatible_version(uint32 version) +{ + /* + * refer to "AoT-compiled module compatibility among WAMR versions" in + * ./doc/build_wasm_app.md + */ + return version == AOT_CURRENT_VERSION; +} + static bool load(const uint8 *buf, uint32 size, AOTModule *module, - char *error_buf, uint32 error_buf_size) + bool wasm_binary_freeable, bool no_resolve, char *error_buf, + uint32 error_buf_size) { const uint8 *buf_end = buf + size; const uint8 *p = buf, *p_end = buf_end; @@ -2243,19 +4445,27 @@ load(const uint8 *buf, uint32 size, AOTModule *module, } read_uint32(p, p_end, version); - if (version != AOT_CURRENT_VERSION) { + if (!aot_compatible_version(version)) { set_error_buf(error_buf, error_buf_size, "unknown binary version"); return false; } - if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size)) + module->package_version = version; + + if (!create_sections(module, buf, size, §ion_list, error_buf, + error_buf_size)) return false; - ret = load_from_sections(module, section_list, error_buf, error_buf_size); + ret = load_from_sections(module, section_list, !wasm_binary_freeable, + no_resolve, error_buf, error_buf_size); if (!ret) { /* If load_from_sections() fails, then aot text is destroyed in destroy_sections() */ - destroy_sections(section_list, true); + destroy_sections(section_list, + module->is_indirect_mode + || module->merged_data_text_sections + ? false + : true); /* aot_unload() won't destroy aot text again */ module->code = NULL; } @@ -2264,264 +4474,294 @@ load(const uint8 *buf, uint32 size, AOTModule *module, module->code and will be destroyed in aot_unload() */ destroy_sections(section_list, false); } + +#if 0 + { + uint32 i; + for (i = 0; i < module->func_count; i++) { + LOG_VERBOSE("AOT func %u, addr: %p\n", i, module->func_ptrs[i]); + } + } +#endif + +#if WASM_ENABLE_LINUX_PERF != 0 + if (wasm_runtime_get_linux_perf()) + if (!aot_create_perf_map(module, error_buf, error_buf_size)) + goto fail; +#endif + return ret; fail: return false; } -AOTModule* -aot_load_from_aot_file(const uint8 *buf, uint32 size, +AOTModule * +aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args, char *error_buf, uint32 error_buf_size) { - AOTModule *module = create_module(error_buf, error_buf_size); + AOTModule *module = create_module(args->name, error_buf, error_buf_size); if (!module) return NULL; - if (!load(buf, size, module, error_buf, error_buf_size)) { + os_thread_jit_write_protect_np(false); /* Make memory writable */ + if (!load(buf, size, module, args->wasm_binary_freeable, args->no_resolve, + error_buf, error_buf_size)) { + aot_unload(module); + return NULL; + } + os_thread_jit_write_protect_np(true); /* Make memory executable */ + os_icache_flush(module->code, module->code_size); + +#if WASM_ENABLE_AOT_VALIDATOR != 0 + if (!aot_module_validate(module, error_buf, error_buf_size)) { aot_unload(module); return NULL; } +#endif /* WASM_ENABLE_AOT_VALIDATOR != 0 */ LOG_VERBOSE("Load module success.\n"); return module; } -#if WASM_ENABLE_JIT != 0 -static AOTModule* -aot_load_from_comp_data(AOTCompData *comp_data, AOTCompContext *comp_ctx, - char *error_buf, uint32 error_buf_size) +void +aot_unload(AOTModule *module) { - uint32 i; - uint64 size; - char func_name[32]; - AOTModule *module; + if (module->import_memories) + destroy_import_memories(module->import_memories); - /* Allocate memory for module */ - if (!(module = wasm_malloc(sizeof(AOTModule)))) { - set_error_buf(error_buf, error_buf_size, - "Allocate memory for AOT module failed."); - return NULL; - } + if (module->memories) + wasm_runtime_free(module->memories); - memset(module, 0, sizeof(AOTModule)); + if (module->mem_init_data_list) + destroy_mem_init_data_list(module, module->mem_init_data_list, + module->mem_init_data_count); - module->module_type = Wasm_Module_AoT; - module->num_bytes_per_page = comp_data->num_bytes_per_page; - module->mem_init_page_count = comp_data->mem_init_page_count; - module->mem_max_page_count = comp_data->mem_max_page_count; + if (module->native_symbol_list) + wasm_runtime_free(module->native_symbol_list); - module->mem_init_data_list = comp_data->mem_init_data_list; - module->mem_init_data_count = comp_data->mem_init_data_count; + if (module->import_tables) + destroy_import_tables(module->import_tables); - module->table_init_data_list = comp_data->table_init_data_list; - module->table_init_data_count = comp_data->table_init_data_count; - module->table_size = comp_data->table_size; + if (module->tables) { +#if WASM_ENABLE_GC != 0 + uint32 i; + for (i = 0; i < module->table_count; i++) { + destroy_init_expr(&module->tables[i].init_expr); + } +#endif + destroy_tables(module->tables); + } - module->func_type_count = comp_data->func_type_count; - module->func_types = comp_data->func_types; + if (module->table_init_data_list) + destroy_table_init_data_list(module->table_init_data_list, + module->table_init_data_count); - module->import_global_count = comp_data->import_global_count; - module->import_globals = comp_data->import_globals; + if (module->types) + destroy_types(module->types, module->type_count); - module->global_count = comp_data->global_count; - module->globals = comp_data->globals; + if (module->import_globals) + destroy_import_globals(module->import_globals); - module->global_count = comp_data->global_count; - module->globals = comp_data->globals; + if (module->globals) { +#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + uint32 i; + for (i = 0; i < module->global_count; i++) { + destroy_init_expr(&module->globals[i].init_expr); + } +#endif + destroy_globals(module->globals); + } - module->global_data_size = comp_data->global_data_size; + if (module->import_funcs) + destroy_import_funcs(module->import_funcs); - module->import_func_count = comp_data->import_func_count; - module->import_funcs = comp_data->import_funcs; + if (module->exports) + destroy_exports(module->exports); - module->func_count = comp_data->func_count; + if (module->func_type_indexes) + wasm_runtime_free(module->func_type_indexes); - /* Allocate memory for function pointers */ - size = (uint64)module->func_count * sizeof(void *); - if (size >= UINT32_MAX - || !(module->func_ptrs = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, "Create func ptrs fail."); - goto fail1; - } +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (module->max_local_cell_nums) + wasm_runtime_free(module->max_local_cell_nums); + if (module->max_stack_cell_nums) + wasm_runtime_free(module->max_stack_cell_nums); +#endif - /* Resolve function addresses */ - bh_assert(comp_ctx->exec_engine); - memset(module->func_ptrs, 0, (uint32)size); - for (i = 0; i < comp_data->func_count; i++) { - snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, i); - if (!(module->func_ptrs[i] = - (void *)LLVMGetFunctionAddress(comp_ctx->exec_engine, - func_name))) { - set_error_buf(error_buf, error_buf_size, - "Get function address fail."); - goto fail2; +#if WASM_ENABLE_GC != 0 + if (module->func_local_ref_flags) { + uint32 i; + for (i = 0; i < module->import_func_count + module->func_count; i++) { + if (module->func_local_ref_flags[i].local_ref_flags) { + wasm_runtime_free( + module->func_local_ref_flags[i].local_ref_flags); + } } - } - /* Allocation memory for function type indexes */ - size = (uint64)module->func_count * sizeof(uint32); - if (size >= UINT32_MAX - || !(module->func_type_indexes = wasm_malloc((uint32)size))) { - set_error_buf(error_buf, error_buf_size, "Create func type indexes fail."); - goto fail2; + wasm_runtime_free(module->func_local_ref_flags); } - memset(module->func_type_indexes, 0, (uint32)size); - for (i = 0; i < comp_data->func_count; i++) - module->func_type_indexes[i] = comp_data->funcs[i]->func_type_index; +#endif - module->export_func_count = comp_data->export_func_count; - module->export_funcs = comp_data->export_funcs; + if (module->func_ptrs) + wasm_runtime_free(module->func_ptrs); - /* Set export function pointers */ - for (i = 0; i < module->export_func_count; i++) { - module->export_funcs[i].func_ptr = - module->func_ptrs[module->export_funcs[i].func_index - - module->import_func_count]; + if (module->const_str_set) + bh_hash_map_destroy(module->const_str_set); +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just release the sub module list */ + if (module->import_module_list) { + WASMRegisteredModule *node = + bh_list_first_elem(module->import_module_list); + while (node) { + WASMRegisteredModule *next = bh_list_elem_next(node); + bh_list_remove(module->import_module_list, node); + wasm_runtime_free(node); + node = next; + } } +#endif - module->start_func_index = comp_data->start_func_index; - if (comp_data->start_func_index != (uint32)-1) { - bh_assert(comp_data->start_func_index >= module->import_func_count - && comp_data->start_func_index < module->import_func_count - + module->func_count); - module->start_function = - module->func_ptrs[comp_data->start_func_index - - module->import_func_count]; + if (module->code && !module->is_indirect_mode + && !module->merged_data_text_sections) { + /* The layout is: literal size + literal + code (with plt table) */ + uint8 *mmap_addr = module->literal - sizeof(uint32); + uint32 total_size = + sizeof(uint32) + module->literal_size + module->code_size; + os_munmap(mmap_addr, total_size); } - else { - module->start_function = NULL; + +#if defined(BH_PLATFORM_WINDOWS) + if (module->extra_plt_data) { + os_munmap(module->extra_plt_data, module->extra_plt_data_size); } +#endif + +#if (defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64)) \ + && !defined(BH_PLATFORM_WINDOWS) + { + GOTItem *got_item = module->got_item_list, *got_item_next; - module->llvm_aux_data_end = comp_data->llvm_aux_data_end; - module->llvm_aux_stack_bottom = comp_data->llvm_aux_stack_bottom; - module->llvm_aux_stack_size = comp_data->llvm_aux_stack_size; - module->llvm_aux_stack_global_index = comp_data->llvm_aux_stack_global_index; + if (module->got_func_ptrs) { + os_munmap(module->got_func_ptrs, + sizeof(void *) * module->got_item_count); + } + while (got_item) { + got_item_next = got_item->next; + wasm_runtime_free(got_item); + got_item = got_item_next; + } + } +#endif - module->code = NULL; - module->code_size = 0; + if (module->data_sections) + destroy_object_data_sections(module->data_sections, + module->data_section_count); - module->is_jit_mode = true; + if (module->merged_data_sections) + os_munmap(module->merged_data_sections, + module->merged_data_sections_size); - module->wasm_module = comp_data->wasm_module; - module->comp_ctx = comp_ctx; - module->comp_data = comp_data; + if (module->merged_data_text_sections) + os_munmap(module->merged_data_text_sections, + module->merged_data_text_sections_size); -#if WASM_ENABLE_LIBC_WASI != 0 - module->is_wasi_module = comp_data->wasm_module->is_wasi_module; +#if WASM_ENABLE_DEBUG_AOT != 0 + jit_code_entry_destroy(module->elf_hdr); #endif - return module; +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + if (module->aux_func_indexes) { + wasm_runtime_free(module->aux_func_indexes); + } + if (module->aux_func_names) { + wasm_runtime_free((void *)module->aux_func_names); + } +#endif -fail2: - wasm_free(module->func_ptrs); -fail1: - wasm_free(module); - return NULL; -} +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + wasm_runtime_destroy_custom_sections(module->custom_section_list); +#endif -AOTModule* -aot_convert_wasm_module(WASMModule *wasm_module, - char *error_buf, uint32 error_buf_size) -{ - AOTCompData *comp_data; - AOTCompContext *comp_ctx; - AOTModule *aot_module; - AOTCompOption option = { 0 }; - char *aot_last_error; - - comp_data = aot_create_comp_data(wasm_module); - if (!comp_data) { - aot_last_error = aot_get_last_error(); - bh_assert(aot_last_error != NULL); - set_error_buf(error_buf, error_buf_size, aot_last_error); - return NULL; +#if WASM_ENABLE_GC != 0 + if (module->ref_type_set) { + bh_hash_map_destroy(module->ref_type_set); } - - option.is_jit_mode = true; - comp_ctx = aot_create_comp_context(comp_data, &option); - if (!comp_ctx) { - aot_last_error = aot_get_last_error(); - bh_assert(aot_last_error != NULL); - set_error_buf(error_buf, error_buf_size, aot_last_error); - goto fail1; + os_mutex_destroy(&module->rtt_type_lock); + if (module->rtt_types) { + uint32 i; + for (i = 0; i < module->type_count; i++) { + if (module->rtt_types[i]) + wasm_runtime_free(module->rtt_types[i]); + } + wasm_runtime_free(module->rtt_types); } +#if WASM_ENABLE_STRINGREF != 0 + { + uint32 i; + for (i = 0; i < WASM_TYPE_STRINGVIEWITER - WASM_TYPE_STRINGREF + 1; + i++) { + if (module->stringref_rtts[i]) + wasm_runtime_free(module->stringref_rtts[i]); + } - if (!aot_compile_wasm(comp_ctx)) { - aot_last_error = aot_get_last_error(); - bh_assert(aot_last_error != NULL); - set_error_buf(error_buf, error_buf_size, aot_last_error); - goto fail2; - } + if (module->string_literal_lengths) { + wasm_runtime_free(module->string_literal_lengths); + } - aot_module = aot_load_from_comp_data(comp_data, comp_ctx, - error_buf, error_buf_size); - if (!aot_module) { - goto fail2; + if (module->string_literal_ptrs) { + wasm_runtime_free((void *)module->string_literal_ptrs); + } } +#endif +#endif - return aot_module; + wasm_runtime_free(module); +} + +uint32 +aot_get_plt_table_size(void) +{ + return get_plt_table_size(); +} + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +const uint8 * +aot_get_custom_section(const AOTModule *module, const char *name, uint32 *len) +{ + WASMCustomSection *section = module->custom_section_list; + + while (section) { + if (strcmp(section->name_addr, name) == 0) { + if (len) { + *len = section->content_len; + } + return section->content_addr; + } + + section = section->next; + } -fail2: - aot_destroy_comp_context(comp_ctx); -fail1: - aot_destroy_comp_data(comp_data); return NULL; } -#endif +#endif /* end of WASM_ENABLE_LOAD_CUSTOM_SECTION */ +#if WASM_ENABLE_STATIC_PGO != 0 void -aot_unload(AOTModule *module) +aot_exchange_uint16(uint8 *p_data) { -#if WASM_ENABLE_JIT != 0 - if (module->comp_data) - aot_destroy_comp_data(module->comp_data); - if (module->comp_ctx) - aot_destroy_comp_context(module->comp_ctx); - if (module->wasm_module) - wasm_loader_unload(module->wasm_module); -#endif - if (module->mem_init_data_list) - destroy_mem_init_data_list(module->mem_init_data_list, - module->mem_init_data_count, - module->is_jit_mode); - if (module->table_init_data_list) - destroy_table_init_data_list(module->table_init_data_list, - module->table_init_data_count, - module->is_jit_mode); - if (module->func_types) - destroy_func_types(module->func_types, - module->func_type_count, - module->is_jit_mode); - if (module->import_globals) - destroy_import_globals(module->import_globals, - module->is_jit_mode); - if (module->globals) - destroy_globals(module->globals, - module->is_jit_mode); - if (module->import_funcs) - destroy_import_funcs(module->import_funcs, - module->is_jit_mode); - if (module->export_funcs) - destroy_export_funcs(module->export_funcs, - module->is_jit_mode); - if (module->func_type_indexes) - wasm_free(module->func_type_indexes); - if (module->func_ptrs) - wasm_free(module->func_ptrs); - if (module->const_str_set) - bh_hash_map_destroy(module->const_str_set); - if (module->code) - bh_munmap(module->code, module->code_size); - if (module->data_sections) - destroy_object_data_sections(module->data_sections, - module->data_section_count); - wasm_free(module); + return exchange_uint16(p_data); } -uint32 -aot_get_plt_table_size() +void +aot_exchange_uint32(uint8 *p_data) { - return get_plt_table_size(); + return exchange_uint32(p_data); } +void +aot_exchange_uint64(uint8 *p_data) +{ + return exchange_uint64(p_data); +} +#endif diff --git a/core/iwasm/aot/aot_perf_map.c b/core/iwasm/aot/aot_perf_map.c new file mode 100644 index 0000000000..b072c9851f --- /dev/null +++ b/core/iwasm/aot/aot_perf_map.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_perf_map.h" +#include "bh_log.h" +#include "bh_platform.h" + +struct func_info { + uint32 idx; + void *ptr; +}; + +static uint32 +get_func_size(const AOTModule *module, struct func_info *sorted_func_ptrs, + uint32 idx) +{ + uint32 func_sz; + + if (idx == module->func_count - 1) + func_sz = (uintptr_t)module->code + module->code_size + - (uintptr_t)(sorted_func_ptrs[idx].ptr); + else + func_sz = (uintptr_t)(sorted_func_ptrs[idx + 1].ptr) + - (uintptr_t)(sorted_func_ptrs[idx].ptr); + + return func_sz; +} + +static int +compare_func_ptrs(const void *f1, const void *f2) +{ + uintptr_t ptr1 = (uintptr_t)((struct func_info *)f1)->ptr; + uintptr_t ptr2 = (uintptr_t)((struct func_info *)f2)->ptr; + + if (ptr1 < ptr2) + return -1; + else if (ptr1 > ptr2) + return 1; + else + return 0; +} + +static struct func_info * +sort_func_ptrs(const AOTModule *module, char *error_buf, uint32 error_buf_size) +{ + uint64 content_len; + struct func_info *sorted_func_ptrs; + unsigned i; + + content_len = (uint64)sizeof(struct func_info) * module->func_count; + sorted_func_ptrs = wasm_runtime_malloc(content_len); + if (!sorted_func_ptrs) { + (void)snprintf(error_buf, error_buf_size, + "allocate memory failed when creating perf map"); + return NULL; + } + + for (i = 0; i < module->func_count; i++) { + sorted_func_ptrs[i].idx = i; + sorted_func_ptrs[i].ptr = module->func_ptrs[i]; + } + + qsort(sorted_func_ptrs, module->func_count, sizeof(struct func_info), + compare_func_ptrs); + + return sorted_func_ptrs; +} + +bool +aot_create_perf_map(const AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + struct func_info *sorted_func_ptrs = NULL; + char perf_map_path[64] = { 0 }; + char perf_map_info[128] = { 0 }; + FILE *perf_map = NULL; + uint32 i; + pid_t pid = getpid(); + bool ret = false; + + sorted_func_ptrs = sort_func_ptrs(module, error_buf, error_buf_size); + if (!sorted_func_ptrs) + goto quit; + + (void)snprintf(perf_map_path, sizeof(perf_map_path) - 1, "/tmp/perf-%d.map", + pid); + perf_map = fopen(perf_map_path, "a"); + if (!perf_map) { + LOG_WARNING("warning: can't create /tmp/perf-%d.map, because %s", pid, + strerror(errno)); + goto quit; + } + + const char *module_name = aot_get_module_name((AOTModule *)module); + for (i = 0; i < module->func_count; i++) { + memset(perf_map_info, 0, 128); + if (strlen(module_name) > 0) { + (void)snprintf(perf_map_info, 128, + "%" PRIxPTR " %x [%s]#aot_func#%u\n", + (uintptr_t)sorted_func_ptrs[i].ptr, + get_func_size(module, sorted_func_ptrs, i), + module_name, sorted_func_ptrs[i].idx); + } + else { + (void)snprintf(perf_map_info, 128, + "%" PRIxPTR " %x aot_func#%u\n", + (uintptr_t)sorted_func_ptrs[i].ptr, + get_func_size(module, sorted_func_ptrs, i), + sorted_func_ptrs[i].idx); + } + + /* fwrite() is thread safe */ + (void)fwrite(perf_map_info, 1, strlen(perf_map_info), perf_map); + } + + LOG_VERBOSE("write map information from %s into /tmp/perf-%d.map", + module_name, pid); + ret = true; + +quit: + if (sorted_func_ptrs) + wasm_runtime_free(sorted_func_ptrs); + + if (perf_map) + (void)fclose(perf_map); + + return ret; +} diff --git a/core/iwasm/aot/aot_perf_map.h b/core/iwasm/aot/aot_perf_map.h new file mode 100644 index 0000000000..3e6583c5cd --- /dev/null +++ b/core/iwasm/aot/aot_perf_map.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_PERF_MAP_H_ +#define _AOT_PERF_MAP_H_ + +#include "aot_runtime.h" + +bool +aot_create_perf_map(const AOTModule *module, char *error_buf, + uint32 error_buf_size); + +#endif /* _AOT_PERF_MAP_H_ */ \ No newline at end of file diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h new file mode 100644 index 0000000000..ed225b5e0d --- /dev/null +++ b/core/iwasm/aot/aot_reloc.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_RELOC_H_ +#define _AOT_RELOC_H_ + +#include "aot_runtime.h" +#include "aot_intrinsic.h" + +#if WASM_ENABLE_STRINGREF != 0 +#include "string_object.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const char *symbol_name; + void *symbol_addr; +} SymbolMap; + +/* clang-format off */ +#define REG_SYM(symbol) { #symbol, (void *)symbol } + +#if WASM_ENABLE_BULK_MEMORY != 0 +#define REG_BULK_MEMORY_SYM() \ + REG_SYM(aot_memory_init), \ + REG_SYM(aot_data_drop), +#else +#define REG_BULK_MEMORY_SYM() +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#define REG_ATOMIC_WAIT_SYM() \ + REG_SYM(wasm_runtime_atomic_wait), \ + REG_SYM(wasm_runtime_atomic_notify), +#else +#define REG_ATOMIC_WAIT_SYM() +#endif + +#if WASM_ENABLE_REF_TYPES != 0 +#define REG_REF_TYPES_SYM() \ + REG_SYM(aot_drop_table_seg), \ + REG_SYM(aot_table_init), \ + REG_SYM(aot_table_copy), \ + REG_SYM(aot_table_fill), \ + REG_SYM(aot_table_grow), +#else +#define REG_REF_TYPES_SYM() +#endif + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 +#define REG_AOT_TRACE_SYM() \ + REG_SYM(aot_alloc_frame), \ + REG_SYM(aot_free_frame), \ + REG_SYM(aot_frame_update_profile_info), +#else +#define REG_AOT_TRACE_SYM() +#endif + +#if WASM_ENABLE_AOT_INTRINSICS != 0 +#define REG_INTRINSIC_SYM() \ + REG_SYM(aot_intrinsic_fabs_f32), \ + REG_SYM(aot_intrinsic_fabs_f64), \ + REG_SYM(aot_intrinsic_floor_f32), \ + REG_SYM(aot_intrinsic_floor_f64), \ + REG_SYM(aot_intrinsic_ceil_f32), \ + REG_SYM(aot_intrinsic_ceil_f64), \ + REG_SYM(aot_intrinsic_trunc_f32), \ + REG_SYM(aot_intrinsic_trunc_f64), \ + REG_SYM(aot_intrinsic_rint_f32), \ + REG_SYM(aot_intrinsic_rint_f64), \ + REG_SYM(aot_intrinsic_sqrt_f32), \ + REG_SYM(aot_intrinsic_sqrt_f64), \ + REG_SYM(aot_intrinsic_copysign_f32), \ + REG_SYM(aot_intrinsic_copysign_f64), \ + REG_SYM(aot_intrinsic_fadd_f32), \ + REG_SYM(aot_intrinsic_fadd_f64), \ + REG_SYM(aot_intrinsic_fsub_f32), \ + REG_SYM(aot_intrinsic_fsub_f64), \ + REG_SYM(aot_intrinsic_fmul_f32), \ + REG_SYM(aot_intrinsic_fmul_f64), \ + REG_SYM(aot_intrinsic_fdiv_f32), \ + REG_SYM(aot_intrinsic_fdiv_f64), \ + REG_SYM(aot_intrinsic_fmin_f32), \ + REG_SYM(aot_intrinsic_fmin_f64), \ + REG_SYM(aot_intrinsic_fmax_f32), \ + REG_SYM(aot_intrinsic_fmax_f64), \ + REG_SYM(aot_intrinsic_clz_i32), \ + REG_SYM(aot_intrinsic_clz_i64), \ + REG_SYM(aot_intrinsic_ctz_i32), \ + REG_SYM(aot_intrinsic_ctz_i64), \ + REG_SYM(aot_intrinsic_popcnt_i32), \ + REG_SYM(aot_intrinsic_popcnt_i64), \ + REG_SYM(aot_intrinsic_i32_to_f32), \ + REG_SYM(aot_intrinsic_u32_to_f32), \ + REG_SYM(aot_intrinsic_i32_to_f64), \ + REG_SYM(aot_intrinsic_u32_to_f64), \ + REG_SYM(aot_intrinsic_i64_to_f32), \ + REG_SYM(aot_intrinsic_u64_to_f32), \ + REG_SYM(aot_intrinsic_i64_to_f64), \ + REG_SYM(aot_intrinsic_u64_to_f64), \ + REG_SYM(aot_intrinsic_f64_to_f32), \ + REG_SYM(aot_intrinsic_f32_to_i32), \ + REG_SYM(aot_intrinsic_f32_to_u32), \ + REG_SYM(aot_intrinsic_f32_to_i64), \ + REG_SYM(aot_intrinsic_f32_to_u64), \ + REG_SYM(aot_intrinsic_f64_to_i32), \ + REG_SYM(aot_intrinsic_f64_to_u32), \ + REG_SYM(aot_intrinsic_f64_to_i64), \ + REG_SYM(aot_intrinsic_f64_to_u64), \ + REG_SYM(aot_intrinsic_f32_to_f64), \ + REG_SYM(aot_intrinsic_f32_cmp), \ + REG_SYM(aot_intrinsic_f64_cmp), \ + REG_SYM(aot_intrinsic_i64_div_s), \ + REG_SYM(aot_intrinsic_i64_div_u), \ + REG_SYM(aot_intrinsic_i64_rem_s), \ + REG_SYM(aot_intrinsic_i64_rem_u), \ + REG_SYM(aot_intrinsic_i64_bit_or), \ + REG_SYM(aot_intrinsic_i64_bit_and), \ + REG_SYM(aot_intrinsic_i64_mul), \ + REG_SYM(aot_intrinsic_i64_shl), \ + REG_SYM(aot_intrinsic_i64_shr_s), \ + REG_SYM(aot_intrinsic_i64_shr_u), \ + REG_SYM(aot_intrinsic_i32_div_s), \ + REG_SYM(aot_intrinsic_i32_div_u), \ + REG_SYM(aot_intrinsic_i32_rem_s), \ + REG_SYM(aot_intrinsic_i32_rem_u), +#else +#define REG_INTRINSIC_SYM() +#endif + +#if WASM_ENABLE_STATIC_PGO != 0 +#define REG_LLVM_PGO_SYM() \ + { "__llvm_profile_instrument_target", llvm_profile_instrument_target }, \ + { "__llvm_profile_instrument_memop", llvm_profile_instrument_memop }, +#else +#define REG_LLVM_PGO_SYM() +#endif + +#if WASM_ENABLE_GC != 0 +#define REG_GC_SYM() \ + REG_SYM(aot_array_init_with_data), \ + REG_SYM(aot_create_func_obj), \ + REG_SYM(aot_obj_is_instance_of), \ + REG_SYM(aot_func_type_is_super_of), \ + REG_SYM(aot_rtt_type_new), \ + REG_SYM(wasm_array_obj_copy), \ + REG_SYM(wasm_array_obj_new), \ + REG_SYM(wasm_externref_obj_to_internal_obj), \ + REG_SYM(wasm_internal_obj_to_externref_obj), \ + REG_SYM(wasm_obj_is_type_of), \ + REG_SYM(wasm_struct_obj_new), +#else +#define REG_GC_SYM() +#endif + +#if WASM_ENABLE_STRINGREF != 0 +#define REG_STRINGREF_SYM() \ + REG_SYM(wasm_stringref_obj_new), \ + REG_SYM(wasm_stringview_wtf8_obj_new), \ + REG_SYM(wasm_stringview_wtf16_obj_new), \ + REG_SYM(wasm_stringview_iter_obj_new), \ + REG_SYM(wasm_string_destroy), \ + REG_SYM(wasm_string_new_const), \ + REG_SYM(wasm_string_new_with_encoding), \ + REG_SYM(wasm_string_measure), \ + REG_SYM(wasm_string_wtf16_get_length), \ + REG_SYM(wasm_string_encode), \ + REG_SYM(wasm_string_concat), \ + REG_SYM(wasm_string_eq), \ + REG_SYM(wasm_string_is_usv_sequence), \ + REG_SYM(wasm_string_create_view), \ + REG_SYM(wasm_string_advance), \ + REG_SYM(wasm_string_slice), \ + REG_SYM(wasm_string_get_wtf16_codeunit),\ + REG_SYM(wasm_string_next_codepoint), \ + REG_SYM(wasm_string_rewind), \ + REG_SYM(wasm_string_dump), +#else +#define REG_STRINGREF_SYM() +#endif + +#if WASM_ENABLE_SHARED_HEAP != 0 +#define REG_SHARED_HEAP_SYM() \ + REG_SYM(wasm_runtime_check_and_update_last_used_shared_heap), +#else +#define REG_SHARED_HEAP_SYM() +#endif + +#define REG_COMMON_SYMBOLS \ + REG_SYM(aot_set_exception_with_id), \ + REG_SYM(aot_invoke_native), \ + REG_SYM(aot_call_indirect), \ + REG_SYM(aot_enlarge_memory), \ + REG_SYM(aot_set_exception), \ + REG_SYM(aot_check_app_addr_and_convert),\ + REG_SYM(wasm_runtime_quick_invoke_c_api_native),\ + { "memset", (void*)aot_memset }, \ + { "memmove", (void*)aot_memmove }, \ + { "memcpy", (void*)aot_memmove }, \ + { "sqrt", (void*)aot_sqrt }, \ + { "sqrtf", (void*)aot_sqrtf }, \ + REG_SYM(fmin), \ + REG_SYM(fminf), \ + REG_SYM(fmax), \ + REG_SYM(fmaxf), \ + REG_SYM(ceil), \ + REG_SYM(ceilf), \ + REG_SYM(floor), \ + REG_SYM(floorf), \ + REG_SYM(trunc), \ + REG_SYM(truncf), \ + REG_SYM(rint), \ + REG_SYM(rintf), \ + REG_BULK_MEMORY_SYM() \ + REG_ATOMIC_WAIT_SYM() \ + REG_REF_TYPES_SYM() \ + REG_AOT_TRACE_SYM() \ + REG_INTRINSIC_SYM() \ + REG_LLVM_PGO_SYM() \ + REG_GC_SYM() \ + REG_STRINGREF_SYM() \ + REG_SHARED_HEAP_SYM() \ + +#define CHECK_RELOC_OFFSET(data_size) do { \ + if (!check_reloc_offset(target_section_size, \ + reloc_offset, data_size, \ + error_buf, error_buf_size)) \ + return false; \ + } while (0) + +SymbolMap * +get_target_symbol_map(uint32 *sym_num); + +uint32 +get_plt_table_size(void); + +void +init_plt_table(uint8 *plt); + +void +get_current_target(char *target_buf, uint32 target_buf_size); + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, int64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size); +/* clang-format off */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _AOT_RELOC_H_ */ diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 9deacf0c9a..84bc3132d6 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -4,857 +4,5708 @@ */ #include "aot_runtime.h" -#include "bh_memory.h" +#include "../compilation/aot_stack_frame.h" #include "bh_log.h" #include "mem_alloc.h" +#include "../common/wasm_runtime_common.h" +#include "../common/wasm_memory.h" +#include "../interpreter/wasm_runtime.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif + +/* + * Note: These offsets need to match the values hardcoded in + * AoT compilation code: aot_create_func_context, check_suspend_flags. + */ + +bh_static_assert(offsetof(WASMExecEnv, cur_frame) == 1 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, module_inst) == 2 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, argv_buf) == 3 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, native_stack_boundary) + == 4 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, suspend_flags) == 5 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, aux_stack_boundary) + == 6 * sizeof(uintptr_t)); +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(WASMExecEnv, wasm_stack.top_boundary) + == 10 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, wasm_stack.top) + == 11 * sizeof(uintptr_t)); +bh_static_assert(offsetof(WASMExecEnv, wasm_stack.bottom) + == 12 * sizeof(uintptr_t)); + +bh_static_assert(offsetof(AOTModuleInstance, memories) == 1 * sizeof(uint64)); +bh_static_assert(offsetof(AOTModuleInstance, func_ptrs) == 5 * sizeof(uint64)); +bh_static_assert(offsetof(AOTModuleInstance, func_type_indexes) + == 6 * sizeof(uint64)); +bh_static_assert(offsetof(AOTModuleInstance, cur_exception) + == 13 * sizeof(uint64)); +bh_static_assert(offsetof(AOTModuleInstance, c_api_func_imports) + == 13 * sizeof(uint64) + 128 + 7 * sizeof(uint64)); +bh_static_assert(offsetof(AOTModuleInstance, global_table_data) + == 13 * sizeof(uint64) + 128 + 14 * sizeof(uint64)); + +bh_static_assert(sizeof(AOTMemoryInstance) == 120); +bh_static_assert(offsetof(AOTTableInstance, elems) == 24); + +bh_static_assert(offsetof(AOTModuleInstanceExtra, stack_sizes) == 0); +bh_static_assert(offsetof(AOTModuleInstanceExtra, shared_heap_base_addr_adj) + == 8); +bh_static_assert(offsetof(AOTModuleInstanceExtra, shared_heap_start_off) == 16); +bh_static_assert(offsetof(AOTModuleInstanceExtra, shared_heap_end_off) == 24); +bh_static_assert(offsetof(AOTModuleInstanceExtra, shared_heap) == 32); + +bh_static_assert(offsetof(WASMSharedHeap, next) == 0); +bh_static_assert(offsetof(WASMSharedHeap, chain_next) == 8); +bh_static_assert(offsetof(WASMSharedHeap, heap_handle) == 16); +bh_static_assert(offsetof(WASMSharedHeap, base_addr) == 24); +bh_static_assert(offsetof(WASMSharedHeap, size) == 32); +bh_static_assert(offsetof(WASMSharedHeap, start_off_mem64) == 40); +bh_static_assert(offsetof(WASMSharedHeap, start_off_mem32) == 48); + +bh_static_assert(sizeof(CApiFuncImport) == sizeof(uintptr_t) * 3); + +bh_static_assert(sizeof(wasm_val_t) == 16); +bh_static_assert(offsetof(wasm_val_t, of) == 8); + +bh_static_assert(offsetof(AOTFrame, prev_frame) == sizeof(uintptr_t) * 0); +bh_static_assert(offsetof(AOTFrame, func_index) == sizeof(uintptr_t) * 1); +bh_static_assert(offsetof(AOTFrame, time_started) == sizeof(uintptr_t) * 2); +bh_static_assert(offsetof(AOTFrame, func_perf_prof_info) + == sizeof(uintptr_t) * 3); +bh_static_assert(offsetof(AOTFrame, ip_offset) == sizeof(uintptr_t) * 4); +bh_static_assert(offsetof(AOTFrame, sp) == sizeof(uintptr_t) * 5); +bh_static_assert(offsetof(AOTFrame, frame_ref) == sizeof(uintptr_t) * 6); +bh_static_assert(offsetof(AOTFrame, lp) == sizeof(uintptr_t) * 7); + +bh_static_assert(offsetof(AOTTinyFrame, func_index) == sizeof(uint32) * 0); +bh_static_assert(offsetof(AOTTinyFrame, ip_offset) == sizeof(uint32) * 1); +bh_static_assert(sizeof(AOTTinyFrame) == sizeof(uint32) * 2); + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, "AOT module instantiate failed: %s", + string); + } +} + +static void +set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, ...) +{ + va_list args; + char buf[128]; + + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + snprintf(error_buf, error_buf_size, "AOT module instantiate failed: %s", + buf); + } +} + +static void +aot_unlinked_import_func_trap(WASMExecEnv *exec_env) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + aot_set_exception_with_id(module_inst, EXCE_CALL_UNLINKED_IMPORT_FUNC); +} + +static void * +runtime_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 +static bool +is_tiny_frame(WASMExecEnv *exec_env) +{ + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)exec_env->module_inst)->module; + + return module->feature_flags & WASM_FEATURE_TINY_STACK_FRAME; +} + +static bool +is_frame_per_function(WASMExecEnv *exec_env) +{ + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)exec_env->module_inst)->module; + + return module->feature_flags & WASM_FEATURE_FRAME_PER_FUNCTION; +} + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +static bool +is_frame_func_idx_disabled(WASMExecEnv *exec_env) +{ + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)exec_env->module_inst)->module; + + return module->feature_flags & WASM_FEATURE_FRAME_NO_FUNC_IDX; +} +#endif + +static void * +get_top_frame(WASMExecEnv *exec_env) +{ + if (is_tiny_frame(exec_env)) { + return exec_env->wasm_stack.top > exec_env->wasm_stack.bottom + ? exec_env->wasm_stack.top - sizeof(AOTTinyFrame) + : NULL; + } + else { + return exec_env->cur_frame; + } +} + +static void * +get_prev_frame(WASMExecEnv *exec_env, void *cur_frame) +{ + bh_assert(cur_frame); + + if (is_tiny_frame(exec_env)) { + if ((uint8 *)cur_frame == exec_env->wasm_stack.bottom) { + return NULL; + } + return ((AOTTinyFrame *)cur_frame) - 1; + } + else { + return ((AOTFrame *)cur_frame)->prev_frame; + } +} +#endif + +static bool +check_global_init_expr(const AOTModule *module, uint32 global_index, + char *error_buf, uint32 error_buf_size) +{ + if (global_index >= module->import_global_count + module->global_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown global %d", + global_index); + return false; + } + + /** + * Currently, constant expressions occurring as initializers of + * globals are further constrained in that contained global.get + * instructions are only allowed to refer to imported globals. + * + * And initializer expression cannot reference a mutable global. + */ + if (global_index >= module->import_global_count + /* make spec test happy */ +#if WASM_ENABLE_GC != 0 + + module->global_count +#endif + ) { + set_error_buf_v(error_buf, error_buf_size, "unknown global %u", + global_index); + return false; + } + + if ( + /* make spec test happy */ +#if WASM_ENABLE_GC != 0 + global_index < module->import_global_count && +#endif + module->import_globals[global_index].type.is_mutable) { + set_error_buf(error_buf, error_buf_size, + "constant expression required"); + return false; + } + + return true; +} + +static void +init_global_data(uint8 *global_data, uint8 type, WASMValue *initial_value) +{ + switch (type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + *(int32 *)global_data = initial_value->i32; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_memcpy_s(global_data, sizeof(int64), &initial_value->i64, + sizeof(int64)); + break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + bh_memcpy_s(global_data, sizeof(V128), &initial_value->v128, + sizeof(V128)); + break; +#endif + default: +#if WASM_ENABLE_GC != 0 + if ((type >= (uint8)REF_TYPE_ARRAYREF + && type <= (uint8)REF_TYPE_NULLFUNCREF) + || (type >= (uint8)REF_TYPE_HT_NULLABLE + && type <= (uint8)REF_TYPE_HT_NON_NULLABLE) +#if WASM_ENABLE_STRINGREF != 0 + || (type >= (uint8)REF_TYPE_STRINGVIEWWTF8 + && type <= (uint8)REF_TYPE_STRINGREF) + || (type >= (uint8)REF_TYPE_STRINGVIEWITER + && type <= (uint8)REF_TYPE_STRINGVIEWWTF16) +#endif + ) { + bh_memcpy_s(global_data, sizeof(wasm_obj_t), + &initial_value->gc_obj, sizeof(wasm_obj_t)); + break; + } +#endif /* end of WASM_ENABLE_GC */ + bh_assert(0); + } +} + +#if WASM_ENABLE_GC != 0 +static bool +assign_table_init_value(AOTModuleInstance *module_inst, AOTModule *module, + InitializerExpression *init_expr, void *addr, + char *error_buf, uint32 error_buf_size) +{ + uint8 flag = init_expr->init_expr_type; + + bh_assert(flag >= INIT_EXPR_TYPE_GET_GLOBAL + && flag <= INIT_EXPR_TYPE_EXTERN_CONVERT_ANY); + + switch (flag) { + case INIT_EXPR_TYPE_GET_GLOBAL: + { + if (!check_global_init_expr(module, + init_expr->u.unary.v.global_index, + error_buf, error_buf_size)) { + return false; + } + if (init_expr->u.unary.v.global_index + < module->import_global_count) { + PUT_REF_TO_ADDR( + addr, + module->import_globals[init_expr->u.unary.v.global_index] + .global_data_linked.gc_obj); + } + else { + uint32 global_idx = init_expr->u.unary.v.global_index + - module->import_global_count; + return assign_table_init_value( + module_inst, module, &module->globals[global_idx].init_expr, + addr, error_buf, error_buf_size); + } + break; + } + case INIT_EXPR_TYPE_REFNULL_CONST: + { + WASMObjectRef gc_obj = NULL_REF; + PUT_REF_TO_ADDR(addr, gc_obj); + break; + } + case INIT_EXPR_TYPE_FUNCREF_CONST: + { + WASMFuncObjectRef func_obj = NULL; + uint32 func_idx = init_expr->u.unary.v.u32; + + if (func_idx != UINT32_MAX) { + if (!(func_obj = + aot_create_func_obj(module_inst, func_idx, false, + error_buf, error_buf_size))) { + return false; + } + } + + PUT_REF_TO_ADDR(addr, func_obj); + break; + } + case INIT_EXPR_TYPE_I31_NEW: + { + WASMI31ObjectRef i31_obj = + wasm_i31_obj_new(init_expr->u.unary.v.i32); + PUT_REF_TO_ADDR(addr, i31_obj); + break; + } + case INIT_EXPR_TYPE_STRUCT_NEW: + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + { + WASMRttType *rtt_type; + WASMStructObjectRef struct_obj; + WASMStructType *struct_type; + WASMStructNewInitValues *init_values = NULL; + uint32 type_idx; + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + init_values = + (WASMStructNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + } + else { + type_idx = init_expr->u.unary.v.type_index; + } + + struct_type = (WASMStructType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)struct_type, type_idx, module->rtt_types, + module->type_count, &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, + "create rtt object failed"); + return false; + } + + if (!(struct_obj = wasm_struct_obj_new_internal( + ((AOTModuleInstanceExtra *)module_inst->e) + ->common.gc_heap_handle, + rtt_type))) { + set_error_buf(error_buf, error_buf_size, + "create struct object failed"); + return false; + } + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + uint32 field_idx; + + bh_assert(init_values->count == struct_type->field_count); + + for (field_idx = 0; field_idx < init_values->count; + field_idx++) { + wasm_struct_obj_set_field(struct_obj, field_idx, + &init_values->fields[field_idx]); + } + } + + PUT_REF_TO_ADDR(addr, struct_obj); + break; + } + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + WASMRttType *rtt_type; + WASMArrayObjectRef array_obj; + WASMArrayType *array_type; + WASMArrayNewInitValues *init_values = NULL; + WASMValue *arr_init_val = NULL, empty_val = { 0 }; + uint32 type_idx, len; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) { + type_idx = init_expr->u.unary.v.array_new_default.type_index; + len = init_expr->u.unary.v.array_new_default.length; + arr_init_val = &empty_val; + } + else { + init_values = + (WASMArrayNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + len = init_values->length; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW) { + arr_init_val = init_values->elem_data; + } + } + + array_type = (WASMArrayType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_idx, module->rtt_types, + module->type_count, &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, + "create rtt object failed"); + return false; + } + + if (!(array_obj = wasm_array_obj_new_internal( + ((AOTModuleInstanceExtra *)module_inst->e) + ->common.gc_heap_handle, + rtt_type, len, arr_init_val))) { + set_error_buf(error_buf, error_buf_size, + "create array object failed"); + return false; + } + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + uint32 elem_idx; + + bh_assert(init_values); + + for (elem_idx = 0; elem_idx < len; elem_idx++) { + wasm_array_obj_set_elem(array_obj, elem_idx, + &init_values->elem_data[elem_idx]); + } + } + + PUT_REF_TO_ADDR(addr, array_obj); + break; + } + default: + set_error_buf(error_buf, error_buf_size, "invalid init expr type."); + return false; + } + + return true; +} +#endif /* end of WASM_ENABLE_GC != 0 */ + +static bool +get_init_value_recursive(AOTModuleInstance *module_inst, AOTModule *module, + InitializerExpression *expr, WASMValue *value, + char *error_buf, uint32 error_buf_size) +{ + uint8 flag = expr->init_expr_type; + switch (flag) { + case INIT_EXPR_TYPE_GET_GLOBAL: + { + if (!check_global_init_expr(module, expr->u.unary.v.global_index, + error_buf, error_buf_size)) { + return false; + } +#if WASM_ENABLE_GC == 0 + *value = module->import_globals[expr->u.unary.v.global_index] + .global_data_linked; +#else + if (expr->u.unary.v.global_index < module->import_global_count) { + *value = module->import_globals[expr->u.unary.v.global_index] + .global_data_linked; + } + else { + *value = module + ->globals[expr->u.unary.v.global_index + - module->import_global_count] + .init_expr.u.unary.v; + } +#endif + break; + } + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_I64_CONST: + { + *value = expr->u.unary.v; + break; + } +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: + { + WASMValue l_value, r_value; + if (!get_init_value_recursive(module_inst, module, + expr->u.binary.l_expr, &l_value, + error_buf, error_buf_size)) { + return false; + } + if (!get_init_value_recursive(module_inst, module, + expr->u.binary.r_expr, &r_value, + error_buf, error_buf_size)) { + return false; + } + + if (flag == INIT_EXPR_TYPE_I32_ADD) { + value->i32 = l_value.i32 + r_value.i32; + } + else if (flag == INIT_EXPR_TYPE_I32_SUB) { + value->i32 = l_value.i32 - r_value.i32; + } + else if (flag == INIT_EXPR_TYPE_I32_MUL) { + value->i32 = l_value.i32 * r_value.i32; + } + else if (flag == INIT_EXPR_TYPE_I64_ADD) { + value->i64 = l_value.i64 + r_value.i64; + } + else if (flag == INIT_EXPR_TYPE_I64_SUB) { + value->i64 = l_value.i64 - r_value.i64; + } + else if (flag == INIT_EXPR_TYPE_I64_MUL) { + value->i64 = l_value.i64 * r_value.i64; + } + break; + } +#endif + default: + return false; + } + + return true; +} + +static bool +global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + InitializerExpression *init_expr; + uint8 *p = module_inst->global_data; + AOTImportGlobal *import_global = module->import_globals; + AOTGlobal *global = module->globals; + + /* Initialize import global data */ + for (i = 0; i < module->import_global_count; i++, import_global++) { + bh_assert(import_global->data_offset + == (uint32)(p - module_inst->global_data)); + init_global_data(p, import_global->type.val_type, + &import_global->global_data_linked); + p += import_global->size; + } + + /* Initialize defined global data */ + for (i = 0; i < module->global_count; i++, global++) { + uint8 flag; + bh_assert(global->data_offset + == (uint32)(p - module_inst->global_data)); + init_expr = &global->init_expr; + flag = init_expr->init_expr_type; + switch (flag) { + case INIT_EXPR_TYPE_GET_GLOBAL: + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_I64_CONST: +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: +#endif + { + WASMValue value; + if (!get_init_value_recursive(module_inst, module, init_expr, + &value, error_buf, + error_buf_size)) { + return false; + } + init_global_data(p, global->type.val_type, &value); + break; + } +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case INIT_EXPR_TYPE_REFNULL_CONST: + { + *(uint32 *)p = NULL_REF; + break; + } +#elif WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_REFNULL_CONST: + { + WASMObjectRef gc_obj = NULL_REF; + PUT_REF_TO_ADDR(p, gc_obj); + break; + } +#endif +#if WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_FUNCREF_CONST: + { + WASMFuncObjectRef func_obj = NULL; + uint32 func_idx = init_expr->u.unary.v.ref_index; + + if (func_idx != UINT32_MAX) { + if (!(func_obj = + aot_create_func_obj(module_inst, func_idx, false, + error_buf, error_buf_size))) { + return false; + } + } + + PUT_REF_TO_ADDR(p, func_obj); + break; + } + case INIT_EXPR_TYPE_I31_NEW: + { + WASMI31ObjectRef i31_obj = + wasm_i31_obj_new(init_expr->u.unary.v.i32); + PUT_REF_TO_ADDR(p, i31_obj); + break; + } + case INIT_EXPR_TYPE_STRUCT_NEW: + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + { + WASMRttType *rtt_type; + WASMStructObjectRef struct_obj; + WASMStructType *struct_type; + WASMStructNewInitValues *init_values = NULL; + uint32 type_idx; + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + init_values = + (WASMStructNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + } + else { + type_idx = init_expr->u.unary.v.type_index; + } + + struct_type = (WASMStructType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)struct_type, type_idx, module->rtt_types, + module->type_count, &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, + "create rtt object failed"); + return false; + } + + if (!(struct_obj = wasm_struct_obj_new_internal( + ((AOTModuleInstanceExtra *)module_inst->e) + ->common.gc_heap_handle, + rtt_type))) { + set_error_buf(error_buf, error_buf_size, + "create struct object failed"); + return false; + } + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + uint32 field_idx; + + bh_assert(init_values->count == struct_type->field_count); + + for (field_idx = 0; field_idx < init_values->count; + field_idx++) { + wasm_struct_obj_set_field( + struct_obj, field_idx, + &init_values->fields[field_idx]); + } + } + + PUT_REF_TO_ADDR(p, struct_obj); + break; + } + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + WASMRttType *rtt_type; + WASMArrayObjectRef array_obj; + WASMArrayType *array_type; + WASMArrayNewInitValues *init_values = NULL; + WASMValue *arr_init_val = NULL, empty_val = { 0 }; + uint32 type_idx, len; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) { + type_idx = + init_expr->u.unary.v.array_new_default.type_index; + len = init_expr->u.unary.v.array_new_default.length; + arr_init_val = &empty_val; + } + else { + init_values = + (WASMArrayNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + len = init_values->length; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW) { + arr_init_val = init_values->elem_data; + } + } + + array_type = (WASMArrayType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_idx, module->rtt_types, + module->type_count, &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, + "create rtt object failed"); + return false; + } + + if (!(array_obj = wasm_array_obj_new_internal( + ((AOTModuleInstanceExtra *)module_inst->e) + ->common.gc_heap_handle, + rtt_type, len, arr_init_val))) { + set_error_buf(error_buf, error_buf_size, + "create array object failed"); + return false; + } + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + uint32 elem_idx; + + bh_assert(init_values); + + for (elem_idx = 0; elem_idx < len; elem_idx++) { + wasm_array_obj_set_elem( + array_obj, elem_idx, + &init_values->elem_data[elem_idx]); + } + } + + PUT_REF_TO_ADDR(p, array_obj); + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + default: + { + init_global_data(p, global->type.val_type, + &init_expr->u.unary.v); + break; + } + } + p += global->size; + } + + bh_assert(module_inst->global_data_size + == (uint32)(p - module_inst->global_data)); + return true; +} + +static bool +tables_instantiate(AOTModuleInstance *module_inst, AOTModule *module, + AOTTableInstance *first_tbl_inst, char *error_buf, + uint32 error_buf_size) +{ + uint32 i, global_index, global_data_offset, base_offset, length; + uint64 total_size; + AOTTableInitData *table_seg; + AOTTableInstance *tbl_inst = first_tbl_inst; + uint8 offset_flag; + + total_size = (uint64)sizeof(AOTTableInstance *) * module_inst->table_count; + if (total_size > 0 + && !(module_inst->tables = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* + * treat import table like a local one until we enable module linking + * in AOT mode + */ + for (i = 0; i != module_inst->table_count; ++i) { + if (i < module->import_table_count) { + AOTImportTable *import_table = module->import_tables + i; + tbl_inst->cur_size = import_table->table_type.init_size; + tbl_inst->max_size = + aot_get_imp_tbl_data_slots(import_table, false); + tbl_inst->elem_type = import_table->table_type.elem_type; + tbl_inst->is_table64 = + import_table->table_type.flags & TABLE64_FLAG; +#if WASM_ENABLE_GC != 0 + tbl_inst->elem_ref_type.elem_ref_type = + import_table->table_type.elem_ref_type; +#endif + } + else { + AOTTable *table = module->tables + (i - module->import_table_count); + tbl_inst->cur_size = table->table_type.init_size; + tbl_inst->max_size = aot_get_tbl_data_slots(table, false); + tbl_inst->elem_type = table->table_type.elem_type; + tbl_inst->is_table64 = table->table_type.flags & TABLE64_FLAG; +#if WASM_ENABLE_GC != 0 + tbl_inst->elem_ref_type.elem_ref_type = + table->table_type.elem_ref_type; +#endif + } + + /* Set all elements to -1 or NULL_REF to mark them as uninitialized + * elements */ +#if WASM_ENABLE_GC == 0 + memset(tbl_inst->elems, 0xff, + sizeof(table_elem_type_t) * tbl_inst->max_size); +#else + memset(tbl_inst->elems, 0x00, + sizeof(table_elem_type_t) * tbl_inst->max_size); +#endif + + module_inst->tables[i] = tbl_inst; + tbl_inst = (AOTTableInstance *)((uint8 *)tbl_inst + + offsetof(AOTTableInstance, elems) + + sizeof(table_elem_type_t) + * tbl_inst->max_size); + } + + /* fill table with element segment content */ + for (i = 0; i < module->table_init_data_count; i++) { +#if WASM_ENABLE_GC == 0 + uint32 j; +#endif + table_seg = module->table_init_data_list[i]; + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + if (!wasm_elem_is_active(table_seg->mode)) + continue; +#endif + + bh_assert(table_seg->table_index < module_inst->table_count); + + tbl_inst = module_inst->tables[table_seg->table_index]; + bh_assert(tbl_inst); + + offset_flag = table_seg->offset.init_expr_type; + +#if WASM_ENABLE_REF_TYPES != 0 + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || offset_flag == INIT_EXPR_TYPE_FUNCREF_CONST + || offset_flag == INIT_EXPR_TYPE_REFNULL_CONST + || (tbl_inst->is_table64 ? is_valid_i64_offset(offset_flag) + : is_valid_i32_offset(offset_flag))); +#else + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || (tbl_inst->is_table64 ? is_valid_i64_offset(offset_flag) + : is_valid_i32_offset(offset_flag))); +#endif + + /* Resolve table data base offset */ + /* TODO: The table64 current implementation assumes table max size + * UINT32_MAX, so the offset conversion here is safe */ + if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) { + global_index = table_seg->offset.u.unary.v.global_index; + + if (!check_global_init_expr(module, global_index, error_buf, + error_buf_size)) { + return false; + } + + if (global_index < module->import_global_count) + global_data_offset = + module->import_globals[global_index].data_offset; + else + global_data_offset = + module->globals[global_index - module->import_global_count] + .data_offset; + + base_offset = + *(uint32 *)(module_inst->global_data + global_data_offset); + } + else { + WASMValue offset_value; + if (!get_init_value_recursive(module_inst, module, + &table_seg->offset, &offset_value, + error_buf, error_buf_size)) { + return false; + } + base_offset = (uint32)offset_value.i32; + } + + /* Copy table data */ + /* base_offset only since length might negative */ + if (base_offset > tbl_inst->cur_size) { +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); +#endif + return false; + } + + /* base_offset + length(could be zero) */ + length = table_seg->value_count; + if (base_offset + length > tbl_inst->cur_size) { +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); +#endif + return false; + } + + /** + * Check function index in the current module inst for now. + * will check the linked table inst owner in future + */ +#if WASM_ENABLE_GC == 0 + for (j = 0; j < length; j++) { + tbl_inst->elems[base_offset + j] = + table_seg->init_values[j].u.unary.v.ref_index; + } +#endif + } + + return true; +} + +static void +memories_deinstantiate(AOTModuleInstance *module_inst) +{ + uint32 i; + AOTMemoryInstance *memory_inst; + + for (i = 0; i < module_inst->memory_count; i++) { + memory_inst = module_inst->memories[i]; + if (memory_inst) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (shared_memory_is_shared(memory_inst)) { + uint32 ref_count = shared_memory_dec_reference(memory_inst); + /* if the reference count is not zero, + don't free the memory */ + if (ref_count > 0) + continue; + } +#endif + if (memory_inst->heap_handle) { + mem_allocator_destroy(memory_inst->heap_handle); + wasm_runtime_free(memory_inst->heap_handle); + } + + if (memory_inst->memory_data) { + wasm_deallocate_linear_memory(memory_inst); + } + } + } + wasm_runtime_free(module_inst->memories); +} + +static AOTMemoryInstance * +memory_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent, + AOTModule *module, AOTMemoryInstance *memory_inst, + AOTMemory *memory, uint32 memory_idx, uint32 heap_size, + uint32 max_memory_pages, char *error_buf, + uint32 error_buf_size) +{ + void *heap_handle; + uint32 num_bytes_per_page = memory->num_bytes_per_page; + uint32 init_page_count = memory->init_page_count; + uint32 max_page_count = wasm_runtime_get_max_mem( + max_memory_pages, memory->init_page_count, memory->max_page_count); + uint32 default_max_pages; + uint32 inc_page_count, global_idx; + uint32 bytes_of_last_page, bytes_to_page_end; + uint64 aux_heap_base, + heap_offset = (uint64)num_bytes_per_page * init_page_count; + uint64 memory_data_size, max_memory_data_size; + uint8 *p = NULL, *global_addr; + bool is_memory64 = memory->flags & MEMORY64_FLAG; + + bool is_shared_memory = false; +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared_memory = memory->flags & SHARED_MEMORY_FLAG ? true : false; + /* Shared memory */ + if (is_shared_memory && parent != NULL) { + AOTMemoryInstance *shared_memory_instance; + bh_assert(memory_idx == 0); + bh_assert(parent->memory_count > memory_idx); + shared_memory_instance = parent->memories[memory_idx]; + shared_memory_inc_reference(shared_memory_instance); + return shared_memory_instance; + } +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + default_max_pages = DEFAULT_MEM64_MAX_PAGES; + } + else +#endif + { + default_max_pages = DEFAULT_MAX_PAGES; + } + + if (heap_size > 0 && module->malloc_func_index != (uint32)-1 + && module->free_func_index != (uint32)-1) { + /* Disable app heap, use malloc/free function exported + by wasm app to allocate/free memory instead */ + heap_size = 0; + } + + /* If initial memory is the largest size allowed, disallowing insert host + * managed heap */ + if (heap_size > 0 && heap_offset == MAX_LINEAR_MEMORY_SIZE) { + set_error_buf(error_buf, error_buf_size, + "failed to insert app heap into linear memory, " + "try using `--heap-size=0` option"); + return NULL; + } + + if (init_page_count == max_page_count && init_page_count == 1) { + /* If only one page and at most one page, we just append + the app heap to the end of linear memory, enlarge the + num_bytes_per_page, and don't change the page count */ + if (heap_size > UINT32_MAX - num_bytes_per_page) { + set_error_buf(error_buf, error_buf_size, + "failed to insert app heap into linear memory, " + "try using `--heap-size=0` option"); + return NULL; + } + heap_offset = num_bytes_per_page; + num_bytes_per_page += heap_size; + } + else if (heap_size > 0) { + if (init_page_count == max_page_count && init_page_count == 0) { + /* If the memory data size is always 0, we resize it to + one page for app heap */ + num_bytes_per_page = heap_size; + heap_offset = 0; + inc_page_count = 1; + } + else if (module->aux_heap_base_global_index != (uint32)-1 + && module->aux_heap_base + < (uint64)num_bytes_per_page * init_page_count) { + /* Insert app heap before __heap_base */ + aux_heap_base = module->aux_heap_base; + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + inc_page_count = + (heap_size - bytes_to_page_end + num_bytes_per_page - 1) + / num_bytes_per_page; + heap_offset = aux_heap_base; + aux_heap_base += heap_size; + + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + if (bytes_to_page_end < 1 * BH_KB) { + aux_heap_base += 1 * BH_KB; + inc_page_count++; + } + + /* Adjust __heap_base global value */ + global_idx = module->aux_heap_base_global_index + - module->import_global_count; + global_addr = module_inst->global_data + + module->globals[global_idx].data_offset; + *(uint32 *)global_addr = (uint32)aux_heap_base; + LOG_VERBOSE("Reset __heap_base global to %" PRIu64, aux_heap_base); + } + else { + /* Insert app heap before new page */ + inc_page_count = + (heap_size + num_bytes_per_page - 1) / num_bytes_per_page; + heap_offset = (uint64)num_bytes_per_page * init_page_count; + heap_size = (uint64)num_bytes_per_page * inc_page_count; + if (heap_size > 0) + heap_size -= 1 * BH_KB; + } + init_page_count += inc_page_count; + max_page_count += inc_page_count; + if (init_page_count > default_max_pages) { + set_error_buf(error_buf, error_buf_size, + "failed to insert app heap into linear memory, " + "try using `--heap-size=0` option"); + return NULL; + } + if (max_page_count > default_max_pages) + max_page_count = default_max_pages; + } + + LOG_VERBOSE("Memory instantiate:"); + LOG_VERBOSE(" page bytes: %u, init pages: %u, max pages: %u", + num_bytes_per_page, init_page_count, max_page_count); + LOG_VERBOSE(" data offset: %" PRIu64 ", stack size: %d", + module->aux_data_end, module->aux_stack_size); + LOG_VERBOSE(" heap offset: %" PRIu64 ", heap size: %d\n", heap_offset, + heap_size); + + max_memory_data_size = (uint64)num_bytes_per_page * max_page_count; + bh_assert(max_memory_data_size <= GET_MAX_LINEAR_MEMORY_SIZE(is_memory64)); + (void)max_memory_data_size; + + /* TODO: memory64 uses is_memory64 flag */ + if (wasm_allocate_linear_memory(&p, is_shared_memory, is_memory64, + num_bytes_per_page, init_page_count, + max_page_count, &memory_data_size) + != BHT_OK) { + set_error_buf(error_buf, error_buf_size, + "allocate linear memory failed"); + return NULL; + } + + memory_inst->module_type = Wasm_Module_AoT; + memory_inst->num_bytes_per_page = num_bytes_per_page; + memory_inst->cur_page_count = init_page_count; + memory_inst->max_page_count = max_page_count; + memory_inst->memory_data_size = memory_data_size; +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + memory_inst->is_memory64 = 1; + } +#endif + + /* Init memory info */ + memory_inst->memory_data = p; + memory_inst->memory_data_end = p + memory_data_size; + + /* Initialize heap info */ + memory_inst->heap_data = p + heap_offset; + memory_inst->heap_data_end = p + heap_offset + heap_size; + if (heap_size > 0) { + uint32 heap_struct_size = mem_allocator_get_heap_struct_size(); + + if (!(heap_handle = runtime_malloc((uint64)heap_struct_size, error_buf, + error_buf_size))) { + goto fail1; + } + + memory_inst->heap_handle = heap_handle; + + if (!mem_allocator_create_with_struct_and_pool( + heap_handle, heap_struct_size, memory_inst->heap_data, + heap_size)) { + set_error_buf(error_buf, error_buf_size, "init app heap failed"); + goto fail2; + } + } + + if (memory_data_size > 0) { + wasm_runtime_set_mem_bound_check_bytes(memory_inst, memory_data_size); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (is_shared_memory) { + memory_inst->is_shared_memory = 1; + memory_inst->ref_count = 1; + } +#endif + + return memory_inst; + +fail2: + if (heap_size > 0) + wasm_runtime_free(memory_inst->heap_handle); +fail1: + wasm_deallocate_linear_memory(memory_inst); + + return NULL; +} + +AOTMemoryInstance * +aot_lookup_memory(AOTModuleInstance *module_inst, char const *name) +{ +#if WASM_ENABLE_MULTI_MEMORY != 0 + uint32 i; + for (i = 0; i < module_inst->export_memory_count; i++) + if (!strcmp(module_inst->export_memories[i].name, name)) + return module_inst->export_memories[i].memory; + return NULL; +#else + (void)module_inst->export_memories; + if (!module_inst->memories) + return NULL; + return module_inst->memories[0]; +#endif +} + +AOTMemoryInstance * +aot_get_default_memory(AOTModuleInstance *module_inst) +{ + if (module_inst->memories) + return module_inst->memories[0]; + else + return NULL; +} + +AOTMemoryInstance * +aot_get_memory_with_idx(AOTModuleInstance *module_inst, uint32 mem_idx) +{ + if ((mem_idx >= module_inst->memory_count) || !module_inst->memories) + return NULL; + return module_inst->memories[mem_idx]; +} + +static bool +memories_instantiate(AOTModuleInstance *module_inst, AOTModuleInstance *parent, + AOTModule *module, uint32 heap_size, + uint32 max_memory_pages, char *error_buf, + uint32 error_buf_size) +{ + uint32 global_index, global_data_offset, length; + uint32 i, memory_count = module->memory_count; + AOTMemoryInstance *memories, *memory_inst; + AOTMemInitData *data_seg; + uint64 total_size; + mem_offset_t base_offset; + uint8 offset_flag; + + module_inst->memory_count = memory_count; + total_size = sizeof(AOTMemoryInstance *) * (uint64)memory_count; + if (!(module_inst->memories = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + memories = module_inst->global_table_data.memory_instances; + for (i = 0; i < memory_count; i++, memories++) { + memory_inst = memory_instantiate( + module_inst, parent, module, memories, &module->memories[i], i, + heap_size, max_memory_pages, error_buf, error_buf_size); + if (!memory_inst) { + return false; + } + + module_inst->memories[i] = memory_inst; + } + + /* Get default memory instance */ + memory_inst = aot_get_default_memory(module_inst); + if (!memory_inst) { + /* Ignore setting memory init data if no memory inst is created */ + return true; + } + + for (i = 0; i < module->mem_init_data_count; i++) { + data_seg = module->mem_init_data_list[i]; +#if WASM_ENABLE_BULK_MEMORY != 0 + if (data_seg->is_passive) + continue; +#endif + if (parent != NULL) + /* Ignore setting memory init data if the memory has been + initialized */ + continue; + + offset_flag = data_seg->offset.init_expr_type; + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || (memory_inst->is_memory64 + ? is_valid_i64_offset(offset_flag) + : is_valid_i32_offset(offset_flag))); + + /* Resolve memory data base offset */ + if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) { + global_index = data_seg->offset.u.unary.v.global_index; + + if (!check_global_init_expr(module, global_index, error_buf, + error_buf_size)) { + return false; + } + + if (global_index < module->import_global_count) + global_data_offset = + module->import_globals[global_index].data_offset; + else + global_data_offset = + module->globals[global_index - module->import_global_count] + .data_offset; + +#if WASM_ENABLE_MEMORY64 != 0 + if (memory_inst->is_memory64) { + base_offset = + *(uint64 *)(module_inst->global_data + global_data_offset); + } + else +#endif + { + base_offset = + *(uint32 *)(module_inst->global_data + global_data_offset); + } + } + else { + WASMValue offset_value; + if (!get_init_value_recursive(module_inst, module, + &data_seg->offset, &offset_value, + error_buf, error_buf_size)) { + return false; + } +#if WASM_ENABLE_MEMORY64 != 0 + if (memory_inst->is_memory64) { + base_offset = offset_value.i64; + } + else +#endif + { + base_offset = offset_value.u32; + } + } + + /* Copy memory data */ + bh_assert(memory_inst->memory_data + || memory_inst->memory_data_size == 0); + + /* Check memory data */ + /* check offset since length might negative */ + if (base_offset > memory_inst->memory_data_size) { + LOG_DEBUG("base_offset(%" PR_MEM_OFFSET + ") > memory_data_size(%" PRIu64 ")", + base_offset, memory_inst->memory_data_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); +#endif + return false; + } + + /* check offset + length(could be zero) */ + length = data_seg->byte_count; + if (base_offset + length > memory_inst->memory_data_size) { + LOG_DEBUG("base_offset(%" PR_MEM_OFFSET + ") + length(%d) > memory_data_size(%" PRIu64 ")", + base_offset, length, memory_inst->memory_data_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); +#endif + return false; + } + + if (memory_inst->memory_data) { + bh_memcpy_s((uint8 *)memory_inst->memory_data + base_offset, + (uint32)(memory_inst->memory_data_size - base_offset), + data_seg->bytes, length); + } + } + + return true; +} + +static bool +init_func_ptrs(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + void **func_ptrs; + uint64 total_size = ((uint64)module->import_func_count + module->func_count) + * sizeof(void *); + + if (module->import_func_count + module->func_count == 0) + return true; + + /* Allocate memory */ + if (!(module_inst->func_ptrs = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set import function pointers */ + func_ptrs = (void **)module_inst->func_ptrs; + for (i = 0; i < module->import_func_count; i++, func_ptrs++) { + *func_ptrs = (void *)module->import_funcs[i].func_ptr_linked; + if (!*func_ptrs) { + const char *module_name = module->import_funcs[i].module_name; + const char *field_name = module->import_funcs[i].func_name; + + /* AOT mode: If linking an imported function fails, we only issue + * a warning here instead of throwing an error. However, during the + * subsequent `invoke_native` stage, calling this unresolved import + * will likely crash. + * + * See: + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/4539 + * + * Debugging: Check if the import is resolved at link time */ + LOG_WARNING("warning: failed to link import function (%s, %s)", + module_name, field_name); + *func_ptrs = (void *)aot_unlinked_import_func_trap; + } + } + + /* Set defined function pointers */ + bh_memcpy_s(func_ptrs, sizeof(void *) * module->func_count, + module->func_ptrs, sizeof(void *) * module->func_count); + return true; +} + +static int +cmp_export_func_map(const void *a, const void *b) +{ + uint32 func_idx1 = ((const ExportFuncMap *)a)->func_idx; + uint32 func_idx2 = ((const ExportFuncMap *)b)->func_idx; + return func_idx1 < func_idx2 ? -1 : (func_idx1 > func_idx2 ? 1 : 0); +} + +AOTFunctionInstance * +aot_lookup_function_with_idx(AOTModuleInstance *module_inst, uint32 func_idx) +{ + AOTModuleInstanceExtra *extra = (AOTModuleInstanceExtra *)module_inst->e; + AOTFunctionInstance *export_funcs = + (AOTFunctionInstance *)module_inst->export_functions; + AOTFunctionInstance *func_inst = NULL; + ExportFuncMap *export_func_maps, *export_func_map, key; + uint64 size; + uint32 i; + + if (module_inst->export_func_count == 0) + return NULL; + + exception_lock(module_inst); + + /* create the func_idx to export_idx maps if it hasn't been created */ + if (!extra->export_func_maps) { + size = sizeof(ExportFuncMap) * (uint64)module_inst->export_func_count; + if (!(export_func_maps = extra->export_func_maps = + runtime_malloc(size, NULL, 0))) { + /* allocate memory failed, lookup the export function one by one */ + for (i = 0; i < module_inst->export_func_count; i++) { + if (export_funcs[i].func_index == func_idx) { + func_inst = &export_funcs[i]; + break; + } + } + goto unlock_and_return; + } + + for (i = 0; i < module_inst->export_func_count; i++) { + export_func_maps[i].func_idx = export_funcs[i].func_index; + export_func_maps[i].export_idx = i; + } + + qsort(export_func_maps, module_inst->export_func_count, + sizeof(ExportFuncMap), cmp_export_func_map); + } + + /* lookup the map to get the export_idx of the func_idx */ + key.func_idx = func_idx; + export_func_map = + bsearch(&key, extra->export_func_maps, module_inst->export_func_count, + sizeof(ExportFuncMap), cmp_export_func_map); + if (export_func_map) + func_inst = &export_funcs[export_func_map->export_idx]; + +unlock_and_return: + exception_unlock(module_inst); + return func_inst; +} + +AOTFunctionInstance * +aot_get_function_instance(AOTModuleInstance *module_inst, uint32 func_idx) +{ + AOTModule *module = (AOTModule *)module_inst->module; + AOTModuleInstanceExtra *extra = (AOTModuleInstanceExtra *)module_inst->e; + AOTFunctionInstance *func_inst; + + /* lookup from export functions first */ + if ((func_inst = aot_lookup_function_with_idx(module_inst, func_idx))) + return func_inst; + + exception_lock(module_inst); + + /* allocate functions array if needed */ + if (!extra->functions) { + uint64 func_count = + ((uint64)module->import_func_count + module->func_count); + uint64 total_size = func_count * (uint64)sizeof(AOTFunctionInstance *); + + if ((func_count == 0) + || !(extra->functions = runtime_malloc(total_size, NULL, 0))) { + exception_unlock(module_inst); + return NULL; + } + + extra->function_count = (uint32)func_count; + } + + /* instantiate function if needed */ + bh_assert(func_idx < extra->function_count); + if (!extra->functions[func_idx]) { + AOTFunctionInstance *function = (AOTFunctionInstance *)runtime_malloc( + sizeof(AOTFunctionInstance), NULL, 0); + if (!function) { + exception_unlock(module_inst); + return NULL; + } + + if (func_idx < module->import_func_count) { + /* instantiate function from import section */ + function->is_import_func = true; + function->func_name = module->import_funcs[func_idx].func_name; + function->func_index = func_idx; + function->u.func_import = &module->import_funcs[func_idx]; + } + else { + /* instantiate non-import function */ + uint32 ftype_index = + module->func_type_indexes[func_idx - module->import_func_count]; + function->is_import_func = false; + function->func_index = func_idx; + function->u.func.func_type = + (AOTFuncType *)module->types[ftype_index]; + function->u.func.func_ptr = + module->func_ptrs[func_idx - module->import_func_count]; + } + + extra->functions[func_idx] = function; + } + + exception_unlock(module_inst); + + return extra->functions[func_idx]; +} + +static bool +init_func_type_indexes(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + uint32 *func_type_index; + uint64 total_size = ((uint64)module->import_func_count + module->func_count) + * sizeof(uint32); + + if (module->import_func_count + module->func_count == 0) + return true; + + /* Allocate memory */ + if (!(module_inst->func_type_indexes = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set import function type indexes */ + func_type_index = module_inst->func_type_indexes; + for (i = 0; i < module->import_func_count; i++, func_type_index++) + *func_type_index = module->import_funcs[i].func_type_index; + + bh_memcpy_s(func_type_index, sizeof(uint32) * module->func_count, + module->func_type_indexes, sizeof(uint32) * module->func_count); + return true; +} + +static int +cmp_func_inst(const void *a, const void *b) +{ + const AOTFunctionInstance *func_inst1 = (const AOTFunctionInstance *)a; + const AOTFunctionInstance *func_inst2 = (const AOTFunctionInstance *)b; + + return strcmp(func_inst1->func_name, func_inst2->func_name); +} + +static bool +create_export_funcs(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + AOTExport *exports = module->exports; + AOTFunctionInstance *export_func; + uint64 size; + uint32 i, func_index, ftype_index; + + if (module_inst->export_func_count > 0) { + /* Allocate memory */ + size = sizeof(AOTFunctionInstance) + * (uint64)module_inst->export_func_count; + if (!(export_func = runtime_malloc(size, error_buf, error_buf_size))) { + return false; + } + module_inst->export_functions = (void *)export_func; + + for (i = 0; i < module->export_count; i++) { + if (exports[i].kind == EXPORT_KIND_FUNC) { + export_func->func_name = exports[i].name; + export_func->func_index = exports[i].index; + if (export_func->func_index < module->import_func_count) { + export_func->is_import_func = true; + export_func->u.func_import = + &module->import_funcs[export_func->func_index]; + } + else { + export_func->is_import_func = false; + func_index = + export_func->func_index - module->import_func_count; + ftype_index = module->func_type_indexes[func_index]; + export_func->u.func.func_type = + (AOTFuncType *)module->types[ftype_index]; + export_func->u.func.func_ptr = + module->func_ptrs[func_index]; + } + export_func++; + } + } + + qsort(module_inst->export_functions, module_inst->export_func_count, + sizeof(AOTFunctionInstance), cmp_func_inst); + } + + return true; +} + +#if WASM_ENABLE_MULTI_MEMORY != 0 +static WASMExportMemInstance * +export_memories_instantiate(const AOTModule *module, + AOTModuleInstance *module_inst, + uint32 export_mem_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportMemInstance *export_memories, *export_memory; + AOTExport *export = module->exports; + uint32 i; + uint64 total_size = + sizeof(WASMExportMemInstance) * (uint64)export_mem_count; + + if (!(export_memory = export_memories = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + for (i = 0; i < module->export_count; i++, export ++) + if (export->kind == EXPORT_KIND_MEMORY) { + export_memory->name = export->name; + export_memory->memory = module_inst->memories[export->index]; + export_memory++; + } + + bh_assert((uint32)(export_memory - export_memories) == export_mem_count); + return export_memories; +} +#endif /* end of if WASM_ENABLE_MULTI_MEMORY != 0 */ + +static bool +create_exports(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + AOTExport *exports = module->exports; + uint32 i; + + for (i = 0; i < module->export_count; i++) { + switch (exports[i].kind) { + case EXPORT_KIND_FUNC: + module_inst->export_func_count++; + break; + case EXPORT_KIND_GLOBAL: + module_inst->export_global_count++; + break; + case EXPORT_KIND_TABLE: + module_inst->export_table_count++; + break; + case EXPORT_KIND_MEMORY: + module_inst->export_memory_count++; + break; + default: + return false; + } + } + +#if WASM_ENABLE_MULTI_MEMORY != 0 + if (module_inst->export_memory_count) { + module_inst->export_memories = export_memories_instantiate( + module, module_inst, module_inst->export_memory_count, error_buf, + error_buf_size); + if (!module_inst->export_memories) { + return false; + } + } +#endif + + return create_export_funcs(module_inst, module, error_buf, error_buf_size); +} + +static AOTFunctionInstance * +lookup_post_instantiate_func(AOTModuleInstance *module_inst, + const char *func_name) +{ + AOTFunctionInstance *func; + AOTFuncType *func_type; + + if (!(func = aot_lookup_function(module_inst, func_name))) + /* 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(AOTModuleInstance *module_inst, + bool is_sub_inst, WASMExecEnv *exec_env_main) +{ + AOTModule *module = (AOTModule *)module_inst->module; + AOTFunctionInstance *initialize_func = NULL; + AOTFunctionInstance *post_inst_func = NULL; + AOTFunctionInstance *call_ctors_func = NULL; + WASMModuleInstanceCommon *module_inst_main = NULL; +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + WASMExecEnv *exec_env = NULL, *exec_env_created = 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 + + /* 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 (is_sub_inst) { + bh_assert(exec_env_main); +#ifdef OS_ENABLE_HW_BOUND_CHECK + /* May come from pthread_create_wrapper, thread_spawn_wrapper and + wasm_cluster_spawn_exec_env. If it comes from the former two, + the exec_env_tls must be not NULL and equal to exec_env_main, + else if it comes from the last one, it may be NULL. */ + if (exec_env_tls) + bh_assert(exec_env_tls == exec_env_main); +#endif + exec_env = exec_env_main; + + /* Temporarily replace parent exec_env'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_main->module_inst; + wasm_exec_env_set_module_inst(exec_env, + (WASMModuleInstanceCommon *)module_inst); + } + else { + /* Try using the existing exec_env */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = exec_env_tls; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) + exec_env = wasm_clusters_search_exec_env( + (WASMModuleInstanceCommon *)module_inst); +#endif + if (!exec_env) { + if (!(exec_env = exec_env_created = wasm_exec_env_create( + (WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + aot_set_exception(module_inst, "allocate memory failed"); + return false; + } + } + else { + /* Temporarily replace exec_env's module inst with current + module inst to ensure that the exec_env's module inst + is the correct one. */ + module_inst_main = exec_env->module_inst; + wasm_exec_env_set_module_inst( + exec_env, (WASMModuleInstanceCommon *)module_inst); + } + } + +#if defined(os_writegsbase) + { + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + if (memory_inst) + /* write base addr of linear memory to GS segment register */ + os_writegsbase(memory_inst->memory_data); + } +#endif + + /* Execute start function for both main instance 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 = + (AOTFuncType *)module->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: + if (is_sub_inst) { + /* Restore the parent exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env_main, module_inst_main); + } + else { + if (module_inst_main) + /* Restore the existing exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env, module_inst_main); + if (exec_env_created) + wasm_exec_env_destroy(exec_env_created); + } + + 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, AOTModuleInstance *parent, + WASMExecEnv *exec_env_main, + const struct InstantiationArgs2 *args, char *error_buf, + uint32 error_buf_size) +{ + AOTModuleInstance *module_inst; +#if WASM_ENABLE_BULK_MEMORY != 0 || WASM_ENABLE_REF_TYPES != 0 + WASMModuleInstanceExtraCommon *common; +#endif + AOTModuleInstanceExtra *extra = NULL; + const uint32 module_inst_struct_size = + offsetof(AOTModuleInstance, global_table_data.bytes); + const uint64 module_inst_mem_inst_size = + (uint64)module->memory_count * sizeof(AOTMemoryInstance); + uint64 total_size, table_size = 0; + uint8 *p; + uint32 i, extra_info_offset; + const bool is_sub_inst = parent != NULL; +#if WASM_ENABLE_MULTI_MODULE != 0 + bool ret = false; +#endif + uint32 stack_size = args->v1.default_stack_size; + uint32 heap_size = args->v1.host_managed_heap_size; + uint32 max_memory_pages = args->v1.max_memory_pages; + + /* Align and validate heap size */ + heap_size = align_uint(heap_size, 8); + if (heap_size > APP_HEAP_SIZE_MAX) + heap_size = APP_HEAP_SIZE_MAX; + + total_size = (uint64)module_inst_struct_size + module_inst_mem_inst_size + + module->global_data_size; + + /* + * calculate size of table data + */ + for (i = 0; i != module->import_table_count; ++i) { + table_size += offsetof(AOTTableInstance, elems); + table_size += (uint64)sizeof(table_elem_type_t) + * (uint64)aot_get_imp_tbl_data_slots( + module->import_tables + i, false); + } + + for (i = 0; i != module->table_count; ++i) { + table_size += offsetof(AOTTableInstance, elems); + table_size += + (uint64)sizeof(table_elem_type_t) + * (uint64)aot_get_tbl_data_slots(module->tables + i, false); + } + total_size += table_size; + + /* The offset of AOTModuleInstanceExtra, make it 8-byte aligned */ + total_size = (total_size + 7LL) & ~7LL; + extra_info_offset = (uint32)total_size; + total_size += sizeof(AOTModuleInstanceExtra); + + /* Allocate module instance, global data, table data and heap data */ + if (!(module_inst = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + module_inst->module_type = Wasm_Module_AoT; + module_inst->module = (void *)module; + module_inst->e = + (WASMModuleInstanceExtra *)((uint8 *)module_inst + extra_info_offset); + extra = (AOTModuleInstanceExtra *)module_inst->e; +#if WASM_ENABLE_THREAD_MGR != 0 + if (os_mutex_init(&extra->common.exception_lock) != 0) { + wasm_runtime_free(module_inst); + return NULL; + } +#endif + +#if WASM_ENABLE_GC != 0 + /* Initialize gc heap first since it may be used when initializing + globals and others */ + if (!is_sub_inst) { + uint32 gc_heap_size = wasm_runtime_get_gc_heap_size_default(); + + if (gc_heap_size < GC_HEAP_SIZE_MIN) + gc_heap_size = GC_HEAP_SIZE_MIN; + if (gc_heap_size > GC_HEAP_SIZE_MAX) + gc_heap_size = GC_HEAP_SIZE_MAX; + + extra->common.gc_heap_pool = + runtime_malloc(gc_heap_size, error_buf, error_buf_size); + if (!extra->common.gc_heap_pool) + goto fail; + + extra->common.gc_heap_handle = + mem_allocator_create(extra->common.gc_heap_pool, gc_heap_size); + if (!extra->common.gc_heap_handle) + goto fail; + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + extra->sub_module_inst_list = &extra->sub_module_inst_list_head; + + /* Allocate memory for import_func_module_insts*/ + if (module->import_func_count > 0 + && !(extra->import_func_module_insts = + runtime_malloc((uint64)module->import_func_count + * sizeof(WASMModuleInstanceCommon *), + error_buf, error_buf_size))) { + goto fail; + } + + ret = wasm_runtime_sub_module_instantiate( + (WASMModuleCommon *)module, (WASMModuleInstanceCommon *)module_inst, + args, error_buf, error_buf_size); + if (!ret) { + LOG_DEBUG("build a sub module list failed"); + goto fail; + } +#endif + + /* Initialize function type indexes before initializing global info, + module_inst->func_type_indexes may be used in the latter */ + if (!init_func_type_indexes(module_inst, module, error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_BULK_MEMORY != 0 || WASM_ENABLE_REF_TYPES != 0 + common = &extra->common; +#endif +#if WASM_ENABLE_BULK_MEMORY != 0 + if (module->mem_init_data_count > 0) { + common->data_dropped = bh_bitmap_new(0, module->mem_init_data_count); + if (common->data_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + for (i = 0; i < module->mem_init_data_count; i++) { + if (!module->mem_init_data_list[i]->is_passive) + bh_bitmap_set_bit(common->data_dropped, i); + } + } +#endif +#if WASM_ENABLE_REF_TYPES != 0 + if (module->table_init_data_count > 0) { + common->elem_dropped = bh_bitmap_new(0, module->table_init_data_count); + if (common->elem_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + for (i = 0; i < module->table_init_data_count; i++) { + if (wasm_elem_is_active(module->table_init_data_list[i]->mode) + || wasm_elem_is_declarative( + module->table_init_data_list[i]->mode)) + bh_bitmap_set_bit(common->elem_dropped, i); + } + } +#endif + + /* Initialize global info */ + p = (uint8 *)module_inst + module_inst_struct_size + + module_inst_mem_inst_size; + module_inst->global_data = p; + module_inst->global_data_size = module->global_data_size; + if (!global_instantiate(module_inst, module, error_buf, error_buf_size)) + goto fail; + + /* Initialize table info */ + p += module->global_data_size; + module_inst->table_count = module->table_count + module->import_table_count; + if (!tables_instantiate(module_inst, module, (AOTTableInstance *)p, + error_buf, error_buf_size)) + goto fail; + + /* Initialize memory space */ + if (!memories_instantiate(module_inst, parent, module, heap_size, + max_memory_pages, error_buf, error_buf_size)) + goto fail; + + /* Initialize function pointers */ + if (!init_func_ptrs(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; + +#if WASM_ENABLE_LIBC_WASI != 0 + if (!is_sub_inst) { + const WASIArguments *wasi_args = &args->wasi; + if (module->wasi_args.set_by_user) { + if (wasi_args->set_by_user) { + set_error_buf(error_buf, error_buf_size, + "WASI configuration was given via both of module " + "and InstantiationArgs2"); + goto fail; + } + wasi_args = &module->wasi_args; + } + if (!wasm_runtime_init_wasi( + (WASMModuleInstanceCommon *)module_inst, wasi_args->dir_list, + wasi_args->dir_count, wasi_args->map_dir_list, + wasi_args->map_dir_count, wasi_args->env, wasi_args->env_count, + wasi_args->addr_pool, wasi_args->addr_count, + wasi_args->ns_lookup_pool, wasi_args->ns_lookup_count, + wasi_args->argv, wasi_args->argc, wasi_args->stdio[0], + wasi_args->stdio[1], wasi_args->stdio[2], error_buf, + error_buf_size)) + goto fail; + } +#endif + + /* Initialize the thread related data */ + if (stack_size == 0) + stack_size = DEFAULT_WASM_STACK_SIZE; + + module_inst->default_wasm_stack_size = stack_size; + + extra->stack_sizes = + aot_get_data_section_addr(module, AOT_STACK_SIZES_SECTION_NAME, NULL); + + /* + * The AOT code checks whether the n bytes to access are in shared heap + * by checking whether the beginning address meets: + * addr >= start_off && addr <= end_off - n-bytes + 1 + * where n is 1/2/4/8/16 and `end_off - n-bytes + 1` is constant, e.g., + * UINT32_MAX, UINT32_MAX-1, UINT32_MAX-3 for n = 1, 2 or 4 in 32-bit + * target. To simplify the check, when shared heap is disabled, we set + * the start off to UINT64_MAX in 64-bit target and UINT32_MAX in 32-bit + * target, so in the checking, the above formula will be false, we don't + * need to check whether the shared heap is enabled or not in the AOT + * code. + */ +#if UINTPTR_MAX == UINT64_MAX + extra->shared_heap_start_off.u64 = UINT64_MAX; +#else + extra->shared_heap_start_off.u32[0] = UINT32_MAX; +#endif + /* After shared heap chain, will early stop if shared heap is NULL */ + extra->shared_heap = NULL; + +#if WASM_ENABLE_PERF_PROFILING != 0 + total_size = sizeof(AOTFuncPerfProfInfo) + * ((uint64)module->import_func_count + module->func_count); + if (!(module_inst->func_perf_profilings = + runtime_malloc(total_size, error_buf, error_buf_size))) { + goto fail; + } +#endif + +#if WASM_ENABLE_GC != 0 + for (i = 0; i < module_inst->table_count; i++) { + uint32 j; + AOTTable *table; + AOTTableInstance *table_inst; + table_elem_type_t *table_data; + + /* bypass imported table since AOTImportTable doesn't have init_expr */ + if (i < module->import_table_count) + continue; + + table = &module->tables[i - module->import_table_count]; + bh_assert(table); + + if (table->init_expr.init_expr_type == INIT_EXPR_NONE) { + continue; + } + + table_inst = module_inst->tables[i]; + bh_assert(table_inst); + + table_data = table_inst->elems; + bh_assert(table_data); + + for (j = 0; j < table_inst->cur_size; j++) { + if (!assign_table_init_value(module_inst, module, &table->init_expr, + table_data + j, error_buf, + error_buf_size)) { + goto fail; + } + } + } + + /* Initialize the table data with table init data */ + for (i = 0; + module_inst->table_count > 0 && i < module->table_init_data_count; + i++) { + + AOTTableInitData *table_init_data = module->table_init_data_list[i]; + AOTTableInstance *table; + table_elem_type_t *table_data; + uint8 tbl_elem_type; + uint32 tbl_init_size, tbl_max_size, j; + WASMRefType *tbl_elem_ref_type; + WASMValue offset_value; + + bh_assert(table_init_data); + + bh_assert(table_init_data->table_index < module_inst->table_count); + table = module_inst->tables[table_init_data->table_index]; + bh_assert(table); + + table_data = table->elems; + bh_assert(table_data); + + wasm_runtime_get_table_inst_elem_type( + (WASMModuleInstanceCommon *)module_inst, + table_init_data->table_index, &tbl_elem_type, &tbl_elem_ref_type, + &tbl_init_size, &tbl_max_size); + + if (!wasm_elem_is_declarative(table_init_data->mode) + && !wasm_reftype_is_subtype_of( + table_init_data->elem_type, table_init_data->elem_ref_type, + table->elem_type, table->elem_ref_type.elem_ref_type, + module->types, module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); + goto fail; + } + + (void)tbl_init_size; + (void)tbl_max_size; + + if (!wasm_elem_is_active(table_init_data->mode)) { + continue; + } + uint8 offset_flag = table_init_data->offset.init_expr_type; + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || offset_flag == INIT_EXPR_TYPE_FUNCREF_CONST + || offset_flag == INIT_EXPR_TYPE_REFNULL_CONST + || offset_flag == INIT_EXPR_TYPE_I32_CONST + || offset_flag == INIT_EXPR_TYPE_I32_ADD + || offset_flag == INIT_EXPR_TYPE_I32_SUB + || offset_flag == INIT_EXPR_TYPE_I32_MUL); + + /* init vec(funcidx) or vec(expr) */ + if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) { + uint32 data_offset; + if (!check_global_init_expr( + module, table_init_data->offset.u.unary.v.global_index, + error_buf, error_buf_size)) { + goto fail; + } + + if (table_init_data->offset.u.unary.v.global_index + < module->import_global_count) { + data_offset = module + ->import_globals[table_init_data->offset.u + .unary.v.global_index] + .data_offset; + } + else { + data_offset = + module + ->globals[table_init_data->offset.u.unary.v.global_index + - module->import_global_count] + .data_offset; + } + offset_value.i32 = + *(uint32 *)(module_inst->global_data + data_offset); + } + else { + if (!get_init_value_recursive( + module_inst, module, &table_init_data->offset, + &offset_value, error_buf, error_buf_size)) { + goto fail; + } + } + + /* check offset since length might negative */ + if ((uint32)offset_value.i32 > table->cur_size) { + LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", offset_value.i32, + table->cur_size); + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); + goto fail; + } + + if ((uint32)offset_value.i32 + table_init_data->value_count + > table->cur_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > table->cur_size(%d)", + offset_value.i32, table_init_data->value_count, + table->cur_size); + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); + goto fail; + } + + for (j = 0; j < module->table_init_data_list[i]->value_count; j++) { + if (!assign_table_init_value(module_inst, module, + &table_init_data->init_values[j], + table_data + offset_value.i32 + j, + error_buf, error_buf_size)) { + goto fail; + } + } + } +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (!(module_inst->frames = + runtime_malloc(sizeof(Vector), error_buf, error_buf_size))) { + goto fail; + } +#endif + + if (!execute_post_instantiate_functions(module_inst, is_sub_inst, + exec_env_main)) { + set_error_buf(error_buf, error_buf_size, module_inst->cur_exception); + goto fail; + } + +#if WASM_ENABLE_MEMORY_TRACING != 0 + wasm_runtime_dump_module_inst_mem_consumption( + (WASMModuleInstanceCommon *)module_inst); +#endif + + return module_inst; + +fail: + aot_deinstantiate(module_inst, is_sub_inst); + return NULL; +} + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +static void +destroy_c_api_frames(Vector *frames) +{ + WASMCApiFrame frame = { 0 }; + uint32 i, total_frames, ret; + + total_frames = (uint32)bh_vector_size(frames); + + for (i = 0; i < total_frames; i++) { + ret = bh_vector_get(frames, i, &frame); + bh_assert(ret); + + if (frame.lp) + wasm_runtime_free(frame.lp); + } + + ret = bh_vector_destroy(frames); + bh_assert(ret); + (void)ret; +} +#endif + +void +aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) +{ + AOTModuleInstanceExtra *extra = (AOTModuleInstanceExtra *)module_inst->e; + WASMModuleInstanceExtraCommon *common = &extra->common; + 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_THREAD_MGR != 0 + os_mutex_destroy(&extra->common.exception_lock); +#endif + +#if WASM_ENABLE_PERF_PROFILING != 0 + if (module_inst->func_perf_profilings) + wasm_runtime_free(module_inst->func_perf_profilings); +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (module_inst->frames) { + destroy_c_api_frames(module_inst->frames); + wasm_runtime_free(module_inst->frames); + module_inst->frames = NULL; + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_sub_module_deinstantiate( + (WASMModuleInstanceCommon *)module_inst); + if (extra->import_func_module_insts) + wasm_runtime_free(extra->import_func_module_insts); +#endif + + if (module_inst->tables) + wasm_runtime_free(module_inst->tables); + + if (module_inst->memories) + memories_deinstantiate(module_inst); + + if (module_inst->export_functions) + wasm_runtime_free(module_inst->export_functions); + + if (extra->export_func_maps) + wasm_runtime_free(extra->export_func_maps); + +#if WASM_ENABLE_MULTI_MEMORY != 0 + if (module_inst->export_memories) + wasm_runtime_free(module_inst->export_memories); +#endif + + if (extra->functions) { + uint32 func_idx; + for (func_idx = 0; func_idx < extra->function_count; ++func_idx) { + if (extra->functions[func_idx]) { + wasm_runtime_free(extra->functions[func_idx]); + } + } + wasm_runtime_free(extra->functions); + } + + if (module_inst->func_ptrs) + wasm_runtime_free(module_inst->func_ptrs); + + if (module_inst->func_type_indexes) + wasm_runtime_free(module_inst->func_type_indexes); + + if (module_inst->c_api_func_imports) + wasm_runtime_free(module_inst->c_api_func_imports); + +#if WASM_ENABLE_GC != 0 + if (!is_sub_inst) { + if (common->gc_heap_handle) + mem_allocator_destroy(common->gc_heap_handle); + if (common->gc_heap_pool) + wasm_runtime_free(common->gc_heap_pool); + } +#endif + + if (!is_sub_inst) { + wasm_native_call_context_dtors((WASMModuleInstanceCommon *)module_inst); + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_bitmap_delete(common->data_dropped); +#endif +#if WASM_ENABLE_REF_TYPES != 0 + bh_bitmap_delete(common->elem_dropped); +#endif + + wasm_runtime_free(module_inst); +} + +AOTFunctionInstance * +aot_lookup_function(const AOTModuleInstance *module_inst, const char *name) +{ + AOTFunctionInstance *export_funcs = + (AOTFunctionInstance *)module_inst->export_functions; + AOTFunctionInstance key = { .func_name = (char *)name }; + + if (!export_funcs) + return NULL; + + return bsearch(&key, export_funcs, module_inst->export_func_count, + sizeof(AOTFunctionInstance), cmp_func_inst); +} + +#ifdef OS_ENABLE_HW_BOUND_CHECK +static bool +invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, + const char *signature, void *attachment, + uint32 *argv, uint32 argc, uint32 *argv_ret) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); + WASMJmpBuf jmpbuf_node = { 0 }, *jmpbuf_node_pop; +#ifdef BH_PLATFORM_WINDOWS + int result; + bool has_exception; + char exception[EXCEPTION_BUF_LEN]; +#endif + bool ret; + + if (!exec_env_tls) { + if (!os_thread_signal_inited()) { + aot_set_exception(module_inst, "thread signal env not inited"); + return false; + } + + /* Set thread handle and stack boundary if they haven't been set */ + wasm_exec_env_set_thread_info(exec_env); + + wasm_runtime_set_exec_env_tls(exec_env); + } + else { + if (exec_env_tls != exec_env) { + aot_set_exception(module_inst, "invalid exec env"); + return false; + } + } + + /* 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. */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + wasm_runtime_set_exec_env_tls(NULL); + return false; + } + + wasm_exec_env_push_jmpbuf(exec_env, &jmpbuf_node); + + /* In AOT mode, this is primarily a design choice for performance reasons. + * Before invoke_native, we do not check whether every imported caller is + * NULL, unlike wasm_interp_call_func_import() and + * wasm_interp_call_func_native(). + * + * See: https://github.com/bytecodealliance/wasm-micro-runtime/issues/4539 + */ + + if (os_setjmp(jmpbuf_node.jmpbuf) == 0) { +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + /* Quick call if the quick aot entry is registered */ + if (!signature && func_type->quick_aot_entry) { + void (*invoke_native)(void *func_ptr, void *exec_env, uint32 *argv, + uint32 *argv_ret) = + func_type->quick_aot_entry; + exec_env->attachment = attachment; + invoke_native(func_ptr, exec_env, argv, argv_ret); + exec_env->attachment = NULL; + ret = !aot_copy_exception(module_inst, NULL); + } + else +#endif + { + ret = wasm_runtime_invoke_native(exec_env, func_ptr, func_type, + signature, attachment, argv, argc, + argv_ret); + } +#ifdef BH_PLATFORM_WINDOWS + has_exception = aot_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(); + bh_assert(result != 0); + } +#endif + } + else { + /* Exception has been set in signal handler before calling longjmp */ + ret = false; + } + + jmpbuf_node_pop = wasm_exec_env_pop_jmpbuf(exec_env); + bh_assert(&jmpbuf_node == jmpbuf_node_pop); + if (!exec_env->jmpbuf_stack_top) { + wasm_runtime_set_exec_env_tls(NULL); + } + if (!ret) { + os_sigreturn(); + os_signal_unmask(); + } + (void)jmpbuf_node_pop; + return ret; +} +#define invoke_native_internal invoke_native_with_hw_bound_check /* NOLINT */ +#else /* else of OS_ENABLE_HW_BOUND_CHECK */ +static inline bool +invoke_native_internal(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, const char *signature, + void *attachment, uint32 *argv, uint32 argc, + uint32 *argv_ret) +{ +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + /* Quick call if the quick aot entry is registered */ + if (!signature && func_type->quick_aot_entry) { + AOTModuleInstance *module_inst = + (AOTModuleInstance *)exec_env->module_inst; + void (*invoke_native)(void *func_ptr, void *exec_env, uint32 *argv, + uint32 *argv_ret) = func_type->quick_aot_entry; + invoke_native(func_ptr, exec_env, argv, argv_ret); + return !aot_copy_exception(module_inst, NULL); + } +#endif + return wasm_runtime_invoke_native(exec_env, func_ptr, func_type, signature, + attachment, argv, argc, argv_ret); +} +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + +#ifdef AOT_STACK_FRAME_DEBUG +typedef void (*stack_frame_callback_t)(struct WASMExecEnv *exec_env); +static stack_frame_callback_t aot_stack_frame_callback; + +/* set the callback, only for debug purpose */ +void +aot_set_stack_frame_callback(stack_frame_callback_t callback) +{ + aot_stack_frame_callback = callback; +} +#endif + +bool +aot_call_function(WASMExecEnv *exec_env, AOTFunctionInstance *function, + unsigned argc, uint32 argv[]) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->module; + AOTFuncType *func_type = function->is_import_func + ? function->u.func_import->func_type + : function->u.func.func_type; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + bool ret; + void *func_ptr = function->is_import_func + ? function->u.func_import->func_ptr_linked + : function->u.func.func_ptr; + void *attachment = NULL; +#if WASM_ENABLE_MULTI_MODULE != 0 + bh_list *sub_module_list_node = NULL; + const char *sub_inst_name = NULL; + const char *func_name = function->u.func_import->module_name; + if (function->is_import_func) { + sub_module_list_node = + ((AOTModuleInstanceExtra *)module_inst->e)->sub_module_inst_list; + sub_module_list_node = bh_list_first_elem(sub_module_list_node); + while (sub_module_list_node) { + sub_inst_name = + ((AOTSubModInstNode *)sub_module_list_node)->module_name; + if (strcmp(sub_inst_name, func_name) == 0) { + exec_env = wasm_runtime_get_exec_env_singleton( + (WASMModuleInstanceCommon *)((AOTSubModInstNode *) + sub_module_list_node) + ->module_inst); + module_inst = (AOTModuleInstance *)exec_env->module_inst; + break; + } + sub_module_list_node = bh_list_elem_next(sub_module_list_node); + } + if (exec_env == NULL) { + wasm_runtime_set_exception((WASMModuleInstanceCommon *)module_inst, + "create singleton exec_env failed"); + return false; + } + } +#endif + + if (argc < func_type->param_cell_num) { + char buf[108]; + snprintf(buf, sizeof(buf), + "invalid argument count %u, must be no smaller than %u", argc, + func_type->param_cell_num); + aot_set_exception(module_inst, buf); + return false; + } + argc = func_type->param_cell_num; + +#if defined(os_writegsbase) + { + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + if (memory_inst) + /* write base addr of linear memory to GS segment register */ + os_writegsbase(memory_inst->memory_data); + } +#endif + + /* func pointer was looked up previously */ + bh_assert(func_ptr != NULL); + +#ifndef OS_ENABLE_HW_BOUND_CHECK + /* Set thread handle and stack boundary */ + wasm_exec_env_set_thread_info(exec_env); +#else + /* Set thread info in invoke_native_with_hw_bound_check when + hw bound check is enabled */ +#endif + + if (function->func_index < module->import_func_count) { + attachment = function->u.func_import->attachment; + } + + /* Set exec env, so it can be later retrieved from instance */ + module_inst->cur_exec_env = exec_env; + + if (ext_ret_count > 0) { + uint32 cell_num = 0, i; + uint8 *ext_ret_types = func_type->types + func_type->param_count + 1; + uint32 argv1_buf[32], *argv1 = argv1_buf, *ext_rets = NULL; + uint32 *argv_ret = argv; + uint32 ext_ret_cell = wasm_get_cell_num(ext_ret_types, ext_ret_count); + uint64 size; +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + void *prev_frame = get_top_frame(exec_env); +#endif + + /* Allocate memory all arguments */ + size = + sizeof(uint32) * (uint64)argc /* original arguments */ + + sizeof(void *) + * (uint64)ext_ret_count /* extra result values' addr */ + + sizeof(uint32) * (uint64)ext_ret_cell; /* extra result values */ + if (size > sizeof(argv1_buf) + && !(argv1 = runtime_malloc(size, module_inst->cur_exception, + sizeof(module_inst->cur_exception)))) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY); + return false; + } + + /* Copy original arguments */ + bh_memcpy_s(argv1, (uint32)size, argv, sizeof(uint32) * argc); + + /* Get the extra result value's address */ + ext_rets = + argv1 + argc + sizeof(void *) / sizeof(uint32) * ext_ret_count; + + /* Append each extra result value's address to original arguments */ + for (i = 0; i < ext_ret_count; i++) { + *(uintptr_t *)(argv1 + argc + sizeof(void *) / sizeof(uint32) * i) = + (uintptr_t)(ext_rets + cell_num); + cell_num += wasm_value_type_cell_num(ext_ret_types[i]); + } + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (!is_frame_per_function(exec_env) + && !aot_alloc_frame(exec_env, function->func_index)) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + return false; + } +#endif + + ret = invoke_native_internal(exec_env, function->u.func.func_ptr, + func_type, NULL, attachment, argv1, argc, + argv); + + if (!ret) { +#ifdef AOT_STACK_FRAME_DEBUG + if (aot_stack_frame_callback) { + aot_stack_frame_callback(exec_env); + } +#endif +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (aot_create_call_stack(exec_env)) { + aot_dump_call_stack(exec_env, true, NULL, 0); + } +#endif + } + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* Free all frames allocated, note that some frames + may be allocated in AOT code and haven't been + freed if exception occurred */ + while (get_top_frame(exec_env) != prev_frame) + aot_free_frame(exec_env); +#endif + if (!ret) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + return ret; + } + + /* Get extra result values */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + argv_ret++; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + argv_ret += 2; + break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + argv_ret += 4; + break; +#endif + default: + bh_assert(0); + break; + } + ext_rets = + argv1 + argc + sizeof(void *) / sizeof(uint32) * ext_ret_count; + bh_memcpy_s(argv_ret, sizeof(uint32) * cell_num, ext_rets, + sizeof(uint32) * cell_num); + + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + return true; + } + else { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + void *prev_frame = get_top_frame(exec_env); + /* Only allocate frame for frame-per-call mode; in the + frame-per-function mode the frame is allocated at the + beginning of the function. */ + if (!is_frame_per_function(exec_env) + && !aot_alloc_frame(exec_env, function->func_index)) { + return false; + } +#endif + + ret = invoke_native_internal(exec_env, func_ptr, func_type, NULL, + attachment, argv, argc, argv); + + if (!ret) { +#ifdef AOT_STACK_FRAME_DEBUG + if (aot_stack_frame_callback) { + aot_stack_frame_callback(exec_env); + } +#endif +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (aot_create_call_stack(exec_env)) { + aot_dump_call_stack(exec_env, true, NULL, 0); + } +#endif + } + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* Free all frames allocated, note that some frames + may be allocated in AOT code and haven't been + freed if exception occurred */ + while (get_top_frame(exec_env) != prev_frame) + aot_free_frame(exec_env); +#endif + + return ret; + } +} + +void +aot_set_exception(AOTModuleInstance *module_inst, const char *exception) +{ + wasm_set_exception(module_inst, exception); +} + +void +aot_set_exception_with_id(AOTModuleInstance *module_inst, uint32 id) +{ + if (id != EXCE_ALREADY_THROWN) + wasm_set_exception_with_id(module_inst, id); +#ifdef OS_ENABLE_HW_BOUND_CHECK + wasm_runtime_access_exce_check_guard_page(); +#endif +} + +const char * +aot_get_exception(AOTModuleInstance *module_inst) +{ + return wasm_get_exception(module_inst); +} + +bool +aot_copy_exception(AOTModuleInstance *module_inst, char *exception_buf) +{ + /* The field offsets of cur_exception in AOTModuleInstance and + WASMModuleInstance are the same */ + return wasm_copy_exception(module_inst, exception_buf); +} + +static bool +execute_malloc_function(AOTModuleInstance *module_inst, WASMExecEnv *exec_env, + AOTFunctionInstance *malloc_func, + AOTFunctionInstance *retain_func, uint64 size, + uint64 *p_result) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + WASMExecEnv *exec_env_created = NULL; + WASMModuleInstanceCommon *module_inst_old = NULL; + union { + uint32 u32[3]; + uint64 u64; + } argv; + uint32 argc; + bool ret; + +#if WASM_ENABLE_MEMORY64 != 0 + bool is_memory64 = module_inst->memories[0]->is_memory64; + if (is_memory64) { + argc = 2; + PUT_I64_TO_ADDR(&argv.u64, size); + } + else +#endif + { + argc = 1; + argv.u32[0] = (uint32)size; + } + + /* if __retain is exported, then this module is compiled by + assemblyscript, the memory should be managed by as's runtime, + in this case we need to call the retain function after malloc + the memory */ + if (retain_func) { + /* the malloc function from assemblyscript is: + function __new(size: usize, id: u32) + id = 0 means this is an ArrayBuffer object */ + argv.u32[argc] = 0; + argc++; + } + + if (exec_env) { +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (exec_env_tls) { + bh_assert(exec_env_tls == exec_env); + } +#endif + bh_assert(exec_env->module_inst + == (WASMModuleInstanceCommon *)module_inst); + } + else { + /* Try using the existing exec_env */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = exec_env_tls; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) + exec_env = wasm_clusters_search_exec_env( + (WASMModuleInstanceCommon *)module_inst); +#endif + if (!exec_env) { + if (!(exec_env = exec_env_created = wasm_exec_env_create( + (WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } + } + else { + /* Temporarily replace exec_env's module inst with current + module inst to ensure that the exec_env's module inst + is the correct one. */ + module_inst_old = exec_env->module_inst; + wasm_exec_env_set_module_inst( + exec_env, (WASMModuleInstanceCommon *)module_inst); + } + } + + ret = aot_call_function(exec_env, malloc_func, argc, argv.u32); + + if (retain_func && ret) + ret = aot_call_function(exec_env, retain_func, 1, argv.u32); + + if (module_inst_old) + /* Restore the existing exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env, module_inst_old); + + if (exec_env_created) + wasm_exec_env_destroy(exec_env_created); + + if (ret) { +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) + *p_result = argv.u64; + else +#endif + { + *p_result = argv.u32[0]; + } + } + return ret; +} + +static bool +execute_free_function(AOTModuleInstance *module_inst, WASMExecEnv *exec_env, + AOTFunctionInstance *free_func, uint64 offset) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + WASMExecEnv *exec_env_created = NULL; + WASMModuleInstanceCommon *module_inst_old = NULL; + union { + uint32 u32[2]; + uint64 u64; + } argv; + uint32 argc; + bool ret; + +#if WASM_ENABLE_MEMORY64 != 0 + if (module_inst->memories[0]->is_memory64) { + PUT_I64_TO_ADDR(&argv.u64, offset); + argc = 2; + } + else +#endif + { + argv.u32[0] = (uint32)offset; + argc = 1; + } + + if (exec_env) { +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (exec_env_tls) { + bh_assert(exec_env_tls == exec_env); + } +#endif + bh_assert(exec_env->module_inst + == (WASMModuleInstanceCommon *)module_inst); + } + else { + /* Try using the existing exec_env */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = exec_env_tls; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) + exec_env = wasm_clusters_search_exec_env( + (WASMModuleInstanceCommon *)module_inst); +#endif + if (!exec_env) { + if (!(exec_env = exec_env_created = wasm_exec_env_create( + (WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } + } + else { + /* Temporarily replace exec_env's module inst with current + module inst to ensure that the exec_env's module inst + is the correct one. */ + module_inst_old = exec_env->module_inst; + wasm_exec_env_set_module_inst( + exec_env, (WASMModuleInstanceCommon *)module_inst); + } + } + + ret = aot_call_function(exec_env, free_func, argc, argv.u32); + + if (module_inst_old) + /* Restore the existing exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env, module_inst_old); + + if (exec_env_created) + wasm_exec_env_destroy(exec_env_created); + + return ret; +} + +uint64 +aot_module_malloc_internal(AOTModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 size, + void **p_native_addr) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + AOTModule *module = (AOTModule *)module_inst->module; + uint8 *addr = NULL; + uint64 offset = 0; + + /* TODO: Memory64 size check based on memory idx type */ + bh_assert(size <= UINT32_MAX); + + if (!memory_inst) { + aot_set_exception(module_inst, "uninitialized memory"); + return 0; + } + + if (memory_inst->heap_handle) { + addr = mem_allocator_malloc(memory_inst->heap_handle, (uint32)size); + } + else if (module->malloc_func_index != (uint32)-1 + && module->free_func_index != (uint32)-1) { + AOTFunctionInstance *malloc_func, *retain_func = NULL; + char *malloc_func_name; + + if (module->retain_func_index != (uint32)-1) { + malloc_func_name = "__new"; + retain_func = aot_lookup_function(module_inst, "__retain"); + if (!retain_func) + retain_func = aot_lookup_function(module_inst, "__pin"); + bh_assert(retain_func); + } + else { + malloc_func_name = "malloc"; + } + malloc_func = aot_lookup_function(module_inst, malloc_func_name); + + if (!malloc_func + || !execute_malloc_function(module_inst, exec_env, malloc_func, + retain_func, size, &offset)) { + return 0; + } + addr = offset ? (uint8 *)memory_inst->memory_data + offset : NULL; + } + + if (!addr) { + if (memory_inst->heap_handle + && mem_allocator_is_heap_corrupted(memory_inst->heap_handle)) { + wasm_runtime_show_app_heap_corrupted_prompt(); + aot_set_exception(module_inst, "app heap corrupted"); + } + else { + LOG_WARNING("warning: allocate %" PRIu64 " bytes memory failed", + size); + } + return 0; + } + if (p_native_addr) + *p_native_addr = addr; + return (uint64)(addr - memory_inst->memory_data); +} + +uint64 +aot_module_realloc_internal(AOTModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 ptr, uint64 size, + void **p_native_addr) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + uint8 *addr = NULL; + + /* TODO: Memory64 ptr and size check based on memory idx type */ + bh_assert(ptr <= UINT32_MAX); + bh_assert(size <= UINT32_MAX); + + if (!memory_inst) { + aot_set_exception(module_inst, "uninitialized memory"); + return 0; + } + + if (memory_inst->heap_handle) { + addr = mem_allocator_realloc( + memory_inst->heap_handle, + (uint32)ptr ? memory_inst->memory_data + (uint32)ptr : NULL, + (uint32)size); + } + + /* Only support realloc in WAMR's app heap */ + (void)exec_env; + + if (!addr) { + if (memory_inst->heap_handle + && mem_allocator_is_heap_corrupted(memory_inst->heap_handle)) { + aot_set_exception(module_inst, "app heap corrupted"); + } + else { + aot_set_exception(module_inst, "out of memory"); + } + return 0; + } + + if (p_native_addr) + *p_native_addr = addr; + return (uint64)(addr - memory_inst->memory_data); +} + +void +aot_module_free_internal(AOTModuleInstance *module_inst, WASMExecEnv *exec_env, + uint64 ptr) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + AOTModule *module = (AOTModule *)module_inst->module; + + if (!memory_inst) { + return; + } + + /* TODO: Memory64 ptr and size check based on memory idx type */ + bh_assert(ptr <= UINT32_MAX); + + if (ptr) { + uint8 *addr = memory_inst->memory_data + (uint32)ptr; + uint8 *memory_data_end; + + /* memory->memory_data_end may be changed in memory grow */ + SHARED_MEMORY_LOCK(memory_inst); + memory_data_end = memory_inst->memory_data_end; + SHARED_MEMORY_UNLOCK(memory_inst); + + if (memory_inst->heap_handle && memory_inst->heap_data < addr + && addr < memory_inst->heap_data_end) { + mem_allocator_free(memory_inst->heap_handle, addr); + } + else if (module->malloc_func_index != (uint32)-1 + && module->free_func_index != (uint32)-1 + && memory_inst->memory_data <= addr + && addr < memory_data_end) { + AOTFunctionInstance *free_func; + char *free_func_name; + + if (module->retain_func_index != (uint32)-1) { + free_func_name = "__release"; + } + else { + free_func_name = "free"; + } + free_func = aot_lookup_function(module_inst, free_func_name); + if (!free_func && module->retain_func_index != (uint32)-1) + free_func = aot_lookup_function(module_inst, "__unpin"); + + if (free_func) + execute_free_function(module_inst, exec_env, free_func, ptr); + } + } +} + +uint64 +aot_module_malloc(AOTModuleInstance *module_inst, uint64 size, + void **p_native_addr) +{ + return aot_module_malloc_internal(module_inst, NULL, size, p_native_addr); +} + +uint64 +aot_module_realloc(AOTModuleInstance *module_inst, uint64 ptr, uint64 size, + void **p_native_addr) +{ + return aot_module_realloc_internal(module_inst, NULL, ptr, size, + p_native_addr); +} + +void +aot_module_free(AOTModuleInstance *module_inst, uint64 ptr) +{ + aot_module_free_internal(module_inst, NULL, ptr); +} + +uint64 +aot_module_dup_data(AOTModuleInstance *module_inst, const char *src, + uint64 size) +{ + char *buffer; + uint64 buffer_offset; + + /* TODO: Memory64 size check based on memory idx type */ + bh_assert(size <= UINT32_MAX); + + buffer_offset = aot_module_malloc(module_inst, size, (void **)&buffer); + + if (buffer_offset != 0) { + buffer = wasm_runtime_addr_app_to_native( + (WASMModuleInstanceCommon *)module_inst, buffer_offset); + bh_memcpy_s(buffer, (uint32)size, src, (uint32)size); + } + return buffer_offset; +} + +bool +aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count) +{ + return wasm_enlarge_memory(module_inst, inc_page_count); +} + +bool +aot_enlarge_memory_with_idx(AOTModuleInstance *module_inst, + uint32 inc_page_count, uint32 memidx) +{ + return wasm_enlarge_memory_with_idx(module_inst, inc_page_count, memidx); +} + +bool +aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, + uint32 *argv) +{ + AOTModuleInstance *module_inst = + (AOTModuleInstance *)wasm_runtime_get_module_inst(exec_env); + AOTModule *aot_module = (AOTModule *)module_inst->module; + CApiFuncImport *c_api_func_import = + module_inst->c_api_func_imports + ? module_inst->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 = (AOTFuncType *)aot_module->types[func_type_idx]; + void **func_ptrs = module_inst->func_ptrs; + void *func_ptr = func_ptrs[func_idx]; + AOTImportFunc *import_func; + const char *signature; + void *attachment; + char buf[96]; + bool ret = false; + bh_assert(func_idx < aot_module->import_func_count); + +#if defined(__has_feature) +#if __has_feature(memory_sanitizer) +#include + __msan_unpoison(argv, (sizeof(uint32) * argc)); +#endif +#endif + import_func = aot_module->import_funcs + func_idx; + if (import_func->call_conv_wasm_c_api) + func_ptr = + c_api_func_import ? c_api_func_import->func_ptr_linked : NULL; + + if (!func_ptr) { + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + import_func->module_name, import_func->func_name); + aot_set_exception(module_inst, buf); + goto fail; + } + + attachment = import_func->attachment; + if (import_func->call_conv_wasm_c_api) { + ret = wasm_runtime_invoke_c_api_native( + (WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc, + argv, c_api_func_import->with_env_arg, c_api_func_import->env_arg); + } + else if (!import_func->call_conv_raw) { + signature = import_func->signature; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstanceCommon *sub_inst = NULL; + if ((sub_inst = ((AOTModuleInstanceExtra *)module_inst->e) + ->import_func_module_insts[func_idx])) { + exec_env = wasm_runtime_get_exec_env_singleton(sub_inst); + } + if (exec_env == NULL) { + wasm_runtime_set_exception((WASMModuleInstanceCommon *)module_inst, + "create singleton exec_env failed"); + goto fail; + } +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + void *prev_frame = get_top_frame(exec_env); + + if (!aot_alloc_frame(exec_env, func_idx)) { + goto fail; + } +#endif +#endif /* WASM_ENABLE_MULTI_MODULE != 0 */ + ret = + wasm_runtime_invoke_native(exec_env, func_ptr, func_type, signature, + attachment, argv, argc, argv); +#if WASM_ENABLE_MULTI_MODULE != 0 && WASM_ENABLE_AOT_STACK_FRAME != 0 + /* Free all frames allocated, note that some frames + may be allocated in AOT code and haven't been + freed if exception occurred */ + while (get_top_frame(exec_env) != prev_frame) + aot_free_frame(exec_env); +#endif + } + else { + signature = import_func->signature; + ret = wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type, + signature, attachment, argv, argc, + argv); + } + +fail: +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!ret) + wasm_runtime_access_exce_check_guard_page(); +#endif + return ret; +} + +bool +aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx, + uint32 argc, uint32 *argv) +{ + AOTModuleInstance *module_inst = + (AOTModuleInstance *)wasm_runtime_get_module_inst(exec_env); + AOTModule *aot_module = (AOTModule *)module_inst->module; + uint32 *func_type_indexes = module_inst->func_type_indexes; + AOTTableInstance *tbl_inst; + AOTFuncType *func_type; + void **func_ptrs = module_inst->func_ptrs, *func_ptr; + uint32 func_type_idx, func_idx, ext_ret_count; + table_elem_type_t tbl_elem_val = NULL_REF; + AOTImportFunc *import_func; + const char *signature = NULL; + void *attachment = NULL; + char buf[96]; + bool ret; + + /* this function is called from native code, so exec_env->handle and + exec_env->native_stack_boundary must have been set, we don't set + it again */ + + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + goto fail; + } + + tbl_inst = module_inst->tables[tbl_idx]; + bh_assert(tbl_inst); + + if (table_elem_idx >= tbl_inst->cur_size) { + aot_set_exception_with_id(module_inst, EXCE_UNDEFINED_ELEMENT); + goto fail; + } + + tbl_elem_val = ((table_elem_type_t *)tbl_inst->elems)[table_elem_idx]; + if (tbl_elem_val == NULL_REF) { + aot_set_exception_with_id(module_inst, EXCE_UNINITIALIZED_ELEMENT); + goto fail; + } + +#if WASM_ENABLE_GC == 0 + func_idx = (uint32)tbl_elem_val; +#else + func_idx = + wasm_func_obj_get_func_idx_bound((WASMFuncObjectRef)tbl_elem_val); +#endif + + func_type_idx = func_type_indexes[func_idx]; + func_type = (AOTFuncType *)aot_module->types[func_type_idx]; + + if (func_idx >= aot_module->import_func_count) { + /* func pointer was looked up previously */ + bh_assert(func_ptrs[func_idx] != NULL); + } + + if (!(func_ptr = func_ptrs[func_idx])) { + bh_assert(func_idx < aot_module->import_func_count); + import_func = aot_module->import_funcs + func_idx; + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + import_func->module_name, import_func->func_name); + aot_set_exception(module_inst, buf); + goto fail; + } + + if (func_idx < aot_module->import_func_count) { + /* Call native function */ + import_func = aot_module->import_funcs + func_idx; + signature = import_func->signature; + attachment = import_func->attachment; + if (import_func->call_conv_raw) { + ret = wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type, + signature, attachment, argv, + argc, argv); + if (!ret) + goto fail; + + return true; + } + } + + ext_ret_count = + func_type->result_count > 1 ? func_type->result_count - 1 : 0; + if (ext_ret_count > 0) { + uint32 argv1_buf[32], *argv1 = argv1_buf; + uint32 *ext_rets = NULL, *argv_ret = argv; + uint32 cell_num = 0, i; + uint8 *ext_ret_types = func_type->types + func_type->param_count + 1; + uint32 ext_ret_cell = wasm_get_cell_num(ext_ret_types, ext_ret_count); + uint64 size; + + /* Allocate memory all arguments */ + size = + sizeof(uint32) * (uint64)argc /* original arguments */ + + sizeof(void *) + * (uint64)ext_ret_count /* extra result values' addr */ + + sizeof(uint32) * (uint64)ext_ret_cell; /* extra result values */ + if (size > sizeof(argv1_buf) + && !(argv1 = runtime_malloc(size, module_inst->cur_exception, + sizeof(module_inst->cur_exception)))) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY); + goto fail; + } + + /* Copy original arguments */ + bh_memcpy_s(argv1, (uint32)size, argv, sizeof(uint32) * argc); + + /* Get the extra result value's address */ + ext_rets = + argv1 + argc + sizeof(void *) / sizeof(uint32) * ext_ret_count; + + /* Append each extra result value's address to original arguments */ + for (i = 0; i < ext_ret_count; i++) { + *(uintptr_t *)(argv1 + argc + sizeof(void *) / sizeof(uint32) * i) = + (uintptr_t)(ext_rets + cell_num); + cell_num += wasm_value_type_cell_num(ext_ret_types[i]); + } + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + void *prev_frame = get_top_frame(exec_env); + if (!is_frame_per_function(exec_env) + && !aot_alloc_frame(exec_env, func_idx)) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + return false; + } +#endif + ret = invoke_native_internal(exec_env, func_ptr, func_type, signature, + attachment, argv1, argc, argv); +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* Free all frames allocated, note that some frames + may be allocated in AOT code and haven't been + freed if exception occurred */ + while (get_top_frame(exec_env) != prev_frame) + aot_free_frame(exec_env); +#endif + + if (!ret) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + goto fail; + } + + /* Get extra result values */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + argv_ret++; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + argv_ret += 2; + break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + argv_ret += 4; + break; +#endif + default: + bh_assert(0); + break; + } + ext_rets = + argv1 + argc + sizeof(void *) / sizeof(uint32) * ext_ret_count; + bh_memcpy_s(argv_ret, sizeof(uint32) * cell_num, ext_rets, + sizeof(uint32) * cell_num); + + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + + return true; + } + else { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + void *prev_frame = get_top_frame(exec_env); + /* Only allocate frame for frame-per-call mode; in the + frame-per-function mode the frame is allocated at the + beginning of the function. */ + if (!is_frame_per_function(exec_env) + && !aot_alloc_frame(exec_env, func_idx)) { + return false; + } +#endif + ret = invoke_native_internal(exec_env, func_ptr, func_type, signature, + attachment, argv, argc, argv); +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* Free all frames allocated, note that some frames + may be allocated in AOT code and haven't been + freed if exception occurred */ + while (get_top_frame(exec_env) != prev_frame) + aot_free_frame(exec_env); +#endif + if (!ret) + goto fail; + + return true; + } + +fail: +#ifdef OS_ENABLE_HW_BOUND_CHECK + wasm_runtime_access_exce_check_guard_page(); +#endif + return false; +} + +bool +aot_check_app_addr_and_convert(AOTModuleInstance *module_inst, bool is_str, + uint64 app_buf_addr, uint64 app_buf_size, + void **p_native_addr) +{ + bool ret; + + ret = wasm_check_app_addr_and_convert(module_inst, is_str, app_buf_addr, + app_buf_size, p_native_addr); + +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!ret) + wasm_runtime_access_exce_check_guard_page(); +#endif + + return ret; +} + +void * +aot_memmove(void *dest, const void *src, size_t n) +{ + return memmove(dest, src, n); +} + +void * +aot_memset(void *s, int c, size_t n) +{ + return memset(s, c, n); +} + +double +aot_sqrt(double x) +{ + return sqrt(x); +} + +float +aot_sqrtf(float x) +{ + return sqrtf(x); +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, uint32 offset, + uint32 len, size_t dst) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + AOTModule *aot_module; + uint8 *data; + uint8 *maddr; + uint64 seg_len; + + if (bh_bitmap_get_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.data_dropped, + seg_index)) { + seg_len = 0; + data = NULL; + } + else { + aot_module = (AOTModule *)module_inst->module; + seg_len = aot_module->mem_init_data_list[seg_index]->byte_count; + data = aot_module->mem_init_data_list[seg_index]->bytes; + } + + if (!wasm_runtime_validate_app_addr((WASMModuleInstanceCommon *)module_inst, + (uint64)dst, (uint64)len)) + return false; + + if ((uint64)offset + (uint64)len > seg_len) { + aot_set_exception(module_inst, "out of bounds memory access"); + return false; + } + + maddr = wasm_runtime_addr_app_to_native( + (WASMModuleInstanceCommon *)module_inst, (uint64)dst); + + SHARED_MEMORY_LOCK(memory_inst); + bh_memcpy_s(maddr, CLAMP_U64_TO_U32(memory_inst->memory_data_size - dst), + data + offset, len); + SHARED_MEMORY_UNLOCK(memory_inst); + return true; +} + +bool +aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index) +{ + bh_bitmap_set_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.data_dropped, + seg_index); + /* Currently we can't free the dropped data segment + as the mem_init_data_count is a continuous array */ + return true; +} +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +aot_set_aux_stack(WASMExecEnv *exec_env, uint64 start_offset, uint32 size) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->module; + + uint32 stack_top_idx = module->aux_stack_top_global_index; + uint64 data_end = module->aux_data_end; + uint64 stack_bottom = 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; + + if (stack_top_idx != (uint32)-1) { + /* The aux stack top is a wasm global, + set the initial value for the global */ + uint32 global_offset = module->globals[stack_top_idx].data_offset; + uint8 *global_addr = module_inst->global_data + global_offset; + /* TODO: Memory64 the type i32/i64 depends on memory idx type*/ + *(int32 *)global_addr = (uint32)start_offset; + + /* The aux stack boundary is a constant value, + set the value to exec_env */ + exec_env->aux_stack_boundary = (uintptr_t)start_offset - size; + exec_env->aux_stack_bottom = (uintptr_t)start_offset; + return true; + } + + return false; +} + +bool +aot_get_aux_stack(WASMExecEnv *exec_env, uint64 *start_offset, uint32 *size) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->module; + + /* The aux stack information is resolved in loader + and store in module */ + uint64 stack_bottom = module->aux_stack_bottom; + uint32 total_aux_stack_size = module->aux_stack_size; + + if (stack_bottom != 0 && total_aux_stack_size != 0) { + if (start_offset) + *start_offset = stack_bottom; + if (size) + *size = total_aux_stack_size; + return true; + } + return false; +} +#endif + +#if (WASM_ENABLE_MEMORY_PROFILING != 0) || (WASM_ENABLE_MEMORY_TRACING != 0) +static void +const_string_node_size_cb(void *key, void *value, void *p_const_string_size) +{ + uint32 const_string_size = 0; + const_string_size += bh_hash_map_get_elem_struct_size(); + const_string_size += strlen((const char *)value) + 1; + *(uint32 *)p_const_string_size += const_string_size; +} + +void +aot_get_module_mem_consumption(const AOTModule *module, + WASMModuleMemConsumption *mem_conspn) +{ + uint32 i, size; + + memset(mem_conspn, 0, sizeof(*mem_conspn)); + + mem_conspn->module_struct_size = sizeof(AOTModule); + + mem_conspn->types_size = sizeof(AOTFuncType *) * module->type_count; + for (i = 0; i < module->type_count; i++) { + AOTFuncType *type = (AOTFuncType *)module->types[i]; + size = offsetof(AOTFuncType, types) + + sizeof(uint8) * (type->param_count + type->result_count); + mem_conspn->types_size += size; + } + + mem_conspn->imports_size = + sizeof(AOTImportMemory) * module->import_memory_count + + sizeof(AOTImportTable) * module->import_table_count + + sizeof(AOTImportGlobal) * module->import_global_count + + sizeof(AOTImportFunc) * module->import_func_count; + + /* func_ptrs and func_type_indexes */ + mem_conspn->functions_size = + (sizeof(void *) + sizeof(uint32)) * module->func_count; + + mem_conspn->tables_size = sizeof(AOTTable) * module->table_count; + + mem_conspn->memories_size = sizeof(AOTMemory) * module->memory_count; + mem_conspn->globals_size = sizeof(AOTGlobal) * module->global_count; + mem_conspn->exports_size = sizeof(AOTExport) * module->export_count; + + mem_conspn->table_segs_size = + sizeof(AOTTableInitData *) * module->table_init_data_count; + for (i = 0; i < module->table_init_data_count; i++) { + AOTTableInitData *init_data = module->table_init_data_list[i]; + size = offsetof(AOTTableInitData, init_values) + + sizeof(InitializerExpression) * init_data->value_count; + mem_conspn->table_segs_size += size; + } + + mem_conspn->data_segs_size = + sizeof(AOTMemInitData *) * module->mem_init_data_count; + for (i = 0; i < module->mem_init_data_count; i++) { + mem_conspn->data_segs_size += sizeof(AOTMemInitData); + } + + if (module->const_str_set) { + uint32 const_string_size = 0; + + mem_conspn->const_strs_size = + bh_hash_map_get_struct_size(module->const_str_set); + + bh_hash_map_traverse(module->const_str_set, const_string_node_size_cb, + (void *)&const_string_size); + mem_conspn->const_strs_size += const_string_size; + } + + /* code size + literal size + object data section size */ + mem_conspn->aot_code_size = + module->code_size + module->literal_size + + sizeof(AOTObjectDataSection) * module->data_section_count; + for (i = 0; i < module->data_section_count; i++) { + AOTObjectDataSection *obj_data = module->data_sections + i; + mem_conspn->aot_code_size += sizeof(uint8) * obj_data->size; + } + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + { + WASMCustomSection *section = module->custom_section_list; + while (section) { + mem_conspn->custom_sections_size += + sizeof(WASMCustomSection) + section->content_len; + section = section->next; + } + } +#endif + + mem_conspn->total_size += mem_conspn->module_struct_size; + mem_conspn->total_size += mem_conspn->types_size; + mem_conspn->total_size += mem_conspn->imports_size; + mem_conspn->total_size += mem_conspn->functions_size; + mem_conspn->total_size += mem_conspn->tables_size; + mem_conspn->total_size += mem_conspn->memories_size; + mem_conspn->total_size += mem_conspn->globals_size; + mem_conspn->total_size += mem_conspn->exports_size; + mem_conspn->total_size += mem_conspn->table_segs_size; + mem_conspn->total_size += mem_conspn->data_segs_size; + mem_conspn->total_size += mem_conspn->const_strs_size; + mem_conspn->total_size += mem_conspn->aot_code_size; +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + mem_conspn->total_size += mem_conspn->custom_sections_size; +#endif +} + +void +aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, + WASMModuleInstMemConsumption *mem_conspn) +{ + AOTTableInstance *tbl_inst; + uint32 i; + + memset(mem_conspn, 0, sizeof(*mem_conspn)); + + mem_conspn->module_inst_struct_size = sizeof(AOTModuleInstance); + + mem_conspn->memories_size = + sizeof(void *) * module_inst->memory_count + + sizeof(AOTMemoryInstance) * module_inst->memory_count; + for (i = 0; i < module_inst->memory_count; i++) { + AOTMemoryInstance *mem_inst = module_inst->memories[i]; + mem_conspn->memories_size += + (uint64)mem_inst->num_bytes_per_page * mem_inst->cur_page_count; + mem_conspn->app_heap_size = + mem_inst->heap_data_end - mem_inst->heap_data; + /* size of app heap structure */ + mem_conspn->memories_size += mem_allocator_get_heap_struct_size(); + } + + mem_conspn->tables_size += + sizeof(AOTTableInstance *) * module_inst->table_count; + for (i = 0; i < module_inst->table_count; i++) { + tbl_inst = module_inst->tables[i]; + mem_conspn->tables_size += offsetof(AOTTableInstance, elems); + mem_conspn->tables_size += sizeof(uint32) * tbl_inst->max_size; + } + + /* func_ptrs and func_type_indexes */ + mem_conspn->functions_size = + (sizeof(void *) + sizeof(uint32)) + * (((AOTModule *)module_inst->module)->import_func_count + + ((AOTModule *)module_inst->module)->func_count); + + mem_conspn->globals_size = module_inst->global_data_size; + + mem_conspn->exports_size = + sizeof(AOTFunctionInstance) * (uint64)module_inst->export_func_count; + + mem_conspn->total_size += mem_conspn->module_inst_struct_size; + mem_conspn->total_size += mem_conspn->memories_size; + mem_conspn->total_size += mem_conspn->functions_size; + mem_conspn->total_size += mem_conspn->tables_size; + mem_conspn->total_size += mem_conspn->globals_size; + mem_conspn->total_size += mem_conspn->exports_size; +} +#endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \ + || (WASM_ENABLE_MEMORY_TRACING != 0) */ + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +void +aot_drop_table_seg(AOTModuleInstance *module_inst, uint32 tbl_seg_idx) +{ + bh_bitmap_set_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.elem_dropped, + tbl_seg_idx); +} + +void +aot_table_init(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 tbl_seg_idx, uint32 length, uint32 src_offset, + uint32 dst_offset) +{ + AOTTableInstance *tbl_inst; + AOTTableInitData *tbl_seg; + const AOTModule *module = (AOTModule *)module_inst->module; + table_elem_type_t *table_elems; + InitializerExpression *tbl_seg_init_values = NULL, *init_values; + uint32 i, tbl_seg_len = 0; +#if WASM_ENABLE_GC != 0 + void *func_obj; +#endif + + tbl_inst = module_inst->tables[tbl_idx]; + bh_assert(tbl_inst); + + tbl_seg = module->table_init_data_list[tbl_seg_idx]; + bh_assert(tbl_seg); + + if (!bh_bitmap_get_bit( + ((AOTModuleInstanceExtra *)module_inst->e)->common.elem_dropped, + tbl_seg_idx)) { + /* table segment isn't dropped */ + tbl_seg_init_values = tbl_seg->init_values; + tbl_seg_len = tbl_seg->value_count; + } + + if (offset_len_out_of_bounds(src_offset, length, tbl_seg_len) + || offset_len_out_of_bounds(dst_offset, length, tbl_inst->cur_size)) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + if (!length) { + return; + } + + table_elems = tbl_inst->elems + dst_offset; + init_values = tbl_seg_init_values + src_offset; + + for (i = 0; i < length; i++) { +#if WASM_ENABLE_GC != 0 + /* UINT32_MAX indicates that it is a null ref */ + if (init_values[i].u.unary.v.ref_index != UINT32_MAX) { + if (!(func_obj = aot_create_func_obj( + module_inst, init_values[i].u.unary.v.ref_index, true, + NULL, 0))) { + aot_set_exception_with_id(module_inst, EXCE_NULL_FUNC_OBJ); + return; + } + table_elems[i] = func_obj; + } + else { + table_elems[i] = NULL_REF; + } +#else + table_elems[i] = init_values[i].u.unary.v.ref_index; +#endif + } +} + +void +aot_table_copy(AOTModuleInstance *module_inst, uint32 src_tbl_idx, + uint32 dst_tbl_idx, uint32 length, uint32 src_offset, + uint32 dst_offset) +{ + AOTTableInstance *src_tbl_inst, *dst_tbl_inst; + + src_tbl_inst = module_inst->tables[src_tbl_idx]; + bh_assert(src_tbl_inst); + + dst_tbl_inst = module_inst->tables[dst_tbl_idx]; + bh_assert(dst_tbl_inst); + + if (offset_len_out_of_bounds(dst_offset, length, dst_tbl_inst->cur_size) + || offset_len_out_of_bounds(src_offset, length, + src_tbl_inst->cur_size)) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + /* if src_offset >= dst_offset, copy from front to back */ + /* if src_offset < dst_offset, copy from back to front */ + /* merge all together */ + bh_memmove_s((uint8 *)dst_tbl_inst + offsetof(AOTTableInstance, elems) + + dst_offset * sizeof(table_elem_type_t), + (dst_tbl_inst->cur_size - dst_offset) + * sizeof(table_elem_type_t), + (uint8 *)src_tbl_inst + offsetof(AOTTableInstance, elems) + + src_offset * sizeof(table_elem_type_t), + length * sizeof(table_elem_type_t)); +} + +void +aot_table_fill(AOTModuleInstance *module_inst, uint32 tbl_idx, uint32 length, + table_elem_type_t val, uint32 data_offset) +{ + AOTTableInstance *tbl_inst; + + tbl_inst = module_inst->tables[tbl_idx]; + bh_assert(tbl_inst); + + if (offset_len_out_of_bounds(data_offset, length, tbl_inst->cur_size)) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + for (; length != 0; data_offset++, length--) { + tbl_inst->elems[data_offset] = val; + } +} + +uint32 +aot_table_grow(AOTModuleInstance *module_inst, uint32 tbl_idx, uint32 inc_size, + table_elem_type_t init_val) +{ + AOTTableInstance *tbl_inst; + uint32 i, orig_size, total_size; + + tbl_inst = module_inst->tables[tbl_idx]; + if (!tbl_inst) { + return (uint32)-1; + } + + orig_size = tbl_inst->cur_size; + + if (!inc_size) { + return orig_size; + } + + if (tbl_inst->cur_size > UINT32_MAX - inc_size) { +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of integer overflow", + tbl_inst->cur_size, inc_size); +#endif + return (uint32)-1; + } + + total_size = tbl_inst->cur_size + inc_size; + if (total_size > tbl_inst->max_size) { +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of over max size", + tbl_inst->cur_size, inc_size); +#endif + return (uint32)-1; + } + + /* fill in */ + for (i = 0; i < inc_size; ++i) { + tbl_inst->elems[tbl_inst->cur_size + i] = init_val; + } + + tbl_inst->cur_size = total_size; + return orig_size; +} +#endif /* WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 +#if WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_PERF_PROFILING != 0 +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 +static const char * +lookup_func_name(const char **func_names, uint32 *func_indexes, + uint32 func_index_count, uint32 func_index) +{ + int64 low = 0, mid; + int64 high = func_index_count - 1; + + if (!func_names || !func_indexes || func_index_count == 0) + return NULL; + + while (low <= high) { + mid = (low + high) / 2; + if (func_index == func_indexes[mid]) { + return func_names[mid]; + } + else if (func_index < func_indexes[mid]) + high = mid - 1; + else + low = mid + 1; + } + + return NULL; +} +#endif /* WASM_ENABLE_CUSTOM_NAME_SECTION != 0 */ + +static const char * +get_func_name_from_index(const AOTModuleInstance *module_inst, + uint32 func_index) +{ + const char *func_name = NULL; + AOTModule *module = (AOTModule *)module_inst->module; + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + if ((func_name = + lookup_func_name(module->aux_func_names, module->aux_func_indexes, + module->aux_func_name_count, func_index))) { + return func_name; + } +#endif + + if (func_index < module->import_func_count) { + func_name = module->import_funcs[func_index].func_name; + } + else { + uint32 i; + + for (i = 0; i < module->export_count; i++) { + AOTExport export = module->exports[i]; + if (export.index == func_index && export.kind == EXPORT_KIND_FUNC) { + func_name = export.name; + break; + } + } + } + + return func_name; +} +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK != 0 || \ + WASM_ENABLE_PERF_PROFILING != 0 */ + +#if WASM_ENABLE_GC == 0 +static bool +aot_alloc_standard_frame(WASMExecEnv *exec_env, uint32 func_index) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; +#if WASM_ENABLE_PERF_PROFILING != 0 + AOTFuncPerfProfInfo *func_perf_prof = + module_inst->func_perf_profilings + func_index; +#endif + AOTFrame *cur_frame, *frame; + uint32 size = (uint32)offsetof(AOTFrame, lp); + + cur_frame = (AOTFrame *)exec_env->cur_frame; + if (!cur_frame) + frame = (AOTFrame *)exec_env->wasm_stack.bottom; + else + frame = (AOTFrame *)((uint8 *)cur_frame + size); + + if ((uint8 *)frame + size > exec_env->wasm_stack.top_boundary) { + aot_set_exception(module_inst, "wasm operand stack overflow"); + return false; + } + + frame->func_index = func_index; + /* No need to initialize ip, it will be committed in jitted code + when needed */ + /* frame->ip = NULL; */ + frame->prev_frame = (AOTFrame *)exec_env->cur_frame; + +#if WASM_ENABLE_PERF_PROFILING != 0 + frame->time_started = (uintptr_t)os_time_thread_cputime_us(); + frame->func_perf_prof_info = func_perf_prof; +#endif +#if WASM_ENABLE_MEMORY_PROFILING != 0 + { + uint32 wasm_stack_used = + (uint8 *)frame + size - exec_env->wasm_stack.bottom; + if (wasm_stack_used > exec_env->max_wasm_stack_used) + exec_env->max_wasm_stack_used = wasm_stack_used; + } +#endif + + exec_env->cur_frame = (struct WASMInterpFrame *)frame; + + return true; +} + +#else /* else of WASM_ENABLE_GC == 0 */ + +static bool +aot_alloc_standard_frame(WASMExecEnv *exec_env, uint32 func_index) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->module; +#if WASM_ENABLE_PERF_PROFILING != 0 + AOTFuncPerfProfInfo *func_perf_prof = + module_inst->func_perf_profilings + func_index; +#endif + AOTFrame *frame; + uint32 max_local_cell_num, max_stack_cell_num, all_cell_num; + uint32 aot_func_idx, frame_size; + + if (func_index >= module->import_func_count) { + aot_func_idx = func_index - module->import_func_count; + max_local_cell_num = module->max_local_cell_nums[aot_func_idx]; + max_stack_cell_num = module->max_stack_cell_nums[aot_func_idx]; + } + else { + AOTFuncType *func_type = module->import_funcs[func_index].func_type; + max_local_cell_num = + func_type->param_cell_num > 2 ? func_type->param_cell_num : 2; + max_stack_cell_num = 0; + } + + all_cell_num = max_local_cell_num + max_stack_cell_num; +#if WASM_ENABLE_GC == 0 + frame_size = (uint32)offsetof(AOTFrame, lp) + all_cell_num * 4; +#else + frame_size = + (uint32)offsetof(AOTFrame, lp) + align_uint(all_cell_num * 5, 4); +#endif + frame = wasm_exec_env_alloc_wasm_frame(exec_env, frame_size); -static void -set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) -{ - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, "%s", string); + if (!frame) { + aot_set_exception(module_inst, "wasm operand stack overflow"); + return false; + } + +#if WASM_ENABLE_PERF_PROFILING != 0 + frame->time_started = (uintptr_t)os_time_thread_cputime_us(); + frame->func_perf_prof_info = func_perf_prof; +#endif + +#if WASM_ENABLE_GC != 0 + frame->sp = frame->lp + max_local_cell_num; + frame->frame_ref = (uint8 *)(frame->sp + max_stack_cell_num); +#endif + + frame->prev_frame = (AOTFrame *)exec_env->cur_frame; + exec_env->cur_frame = (struct WASMInterpFrame *)frame; + + frame->func_index = func_index; + return true; } +#endif /* end of WASM_ENABLE_GC == 0 */ static bool -global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, - char *error_buf, uint32 error_buf_size) +aot_alloc_tiny_frame(WASMExecEnv *exec_env, uint32 func_index) { - uint32 i; - InitializerExpression *init_expr; - uint8 *p = (uint8*)module_inst->global_data.ptr; - AOTImportGlobal *import_global = module->import_globals;; - AOTGlobal *global = module->globals; + AOTTinyFrame *new_frame = (AOTTinyFrame *)exec_env->wasm_stack.top; - /* Initialize import global data */ - for (i = 0; i < module->import_global_count; i++, import_global++) { - bh_assert(import_global->data_offset == - p - (uint8*)module_inst->global_data.ptr); - memcpy(p, &import_global->global_data_linked, import_global->size); - p += import_global->size; + if ((uint8 *)new_frame + sizeof(AOTTinyFrame) + > exec_env->wasm_stack.top_boundary) { + aot_set_exception((WASMModuleInstance *)exec_env->module_inst, + "wasm operand stack overflow"); + return false; } - /* Initialize defined global data */ - for (i = 0; i < module->global_count; i++, global++) { - bh_assert(global->data_offset == - p - (uint8*)module_inst->global_data.ptr); - init_expr = &global->init_expr; - switch (init_expr->init_expr_type) { - case INIT_EXPR_TYPE_GET_GLOBAL: - bh_assert(init_expr->u.global_index < module->import_global_count); - memcpy(p, - &module->import_globals[init_expr->u.global_index].global_data_linked, - global->size); - break; - default: - /* TODO: check whether global type and init_expr type are matching */ - memcpy(p, &init_expr->u, global->size); - break; - } - p += global->size; + new_frame->func_index = func_index; + exec_env->wasm_stack.top += sizeof(AOTTinyFrame); + return true; +} + +bool +aot_alloc_frame(WASMExecEnv *exec_env, uint32 func_index) +{ + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)exec_env->module_inst)->module; + + if (is_frame_per_function(exec_env) + && func_index >= module->import_func_count) { + /* in frame per function mode the frame is allocated at + the beginning of each frame, so we only need to allocate + the frame for imported functions */ + return true; + } + if (is_tiny_frame(exec_env)) { + return aot_alloc_tiny_frame(exec_env, func_index); } + else { + return aot_alloc_standard_frame(exec_env, func_index); + } +} - bh_assert(module_inst->global_data_size == p - (uint8*)module_inst->global_data.ptr); - return true; +static inline void +aot_free_standard_frame(WASMExecEnv *exec_env) +{ + AOTFrame *cur_frame = (AOTFrame *)exec_env->cur_frame; + AOTFrame *prev_frame = (AOTFrame *)cur_frame->prev_frame; + +#if WASM_ENABLE_PERF_PROFILING != 0 + uint64 time_elapsed = + (uintptr_t)os_time_thread_cputime_us() - cur_frame->time_started; + + cur_frame->func_perf_prof_info->total_exec_time += time_elapsed; + cur_frame->func_perf_prof_info->total_exec_cnt++; + + /* parent function */ + if (prev_frame) + prev_frame->func_perf_prof_info->children_exec_time += time_elapsed; +#endif + +#if WASM_ENABLE_GC != 0 + wasm_exec_env_free_wasm_frame(exec_env, cur_frame); +#endif + exec_env->cur_frame = (struct WASMInterpFrame *)prev_frame; } -static bool -table_instantiate(AOTModuleInstance *module_inst, AOTModule *module, - char *error_buf, uint32 error_buf_size) +static inline void +aot_free_tiny_frame(WASMExecEnv *exec_env) { - uint32 i, global_index, global_data_offset, base_offset, length; - AOTTableInitData *table_seg; + exec_env->wasm_stack.top = + get_prev_frame(exec_env, exec_env->wasm_stack.top); +} - if (module->table_init_data_count > 0) { - for (i = 0; i < module->table_init_data_count; i++) { - table_seg = module->table_init_data_list[i]; - bh_assert(table_seg->offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || table_seg->offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); - - /* Resolve table data base offset */ - if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - global_index = table_seg->offset.u.global_index; - bh_assert(global_index < - module->import_global_count + module->global_count); - /* TODO: && globals[table_seg->offset.u.global_index].type == - VALUE_TYPE_I32*/ - if (global_index < module->import_global_count) - global_data_offset = - module->import_globals[global_index].data_offset; - else - global_data_offset = - module->globals[global_index - module->import_global_count] - .data_offset; - - base_offset = *(uint32*) - ((uint8*)module_inst->global_data.ptr + global_data_offset); - } - else - base_offset = (uint32)table_seg->offset.u.i32; +void +aot_free_frame(WASMExecEnv *exec_env) +{ + if (is_tiny_frame(exec_env)) { + aot_free_tiny_frame(exec_env); + } + else { + aot_free_standard_frame(exec_env); + } +} - /* Copy table data */ - length = table_seg->func_index_count; - if (base_offset < module_inst->table_size) { - memcpy((uint32*)module_inst->table_data.ptr + base_offset, - table_seg->func_indexes, length * sizeof(uint32)); - } +void +aot_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame) +{ +#if WASM_ENABLE_PERF_PROFILING != 0 + AOTFrame *cur_frame = (AOTFrame *)exec_env->cur_frame; + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + AOTFuncPerfProfInfo *func_perf_prof = + module_inst->func_perf_profilings + cur_frame->func_index; + + if (alloc_frame) { + cur_frame->time_started = (uintptr_t)os_time_thread_cputime_us(); + cur_frame->func_perf_prof_info = func_perf_prof; + } + else { + AOTFrame *prev_frame = cur_frame->prev_frame; + uint64 time_elapsed = + (uintptr_t)os_time_thread_cputime_us() - cur_frame->time_started; + + cur_frame->func_perf_prof_info->total_exec_time += time_elapsed; + cur_frame->func_perf_prof_info->total_exec_cnt++; + + /* parent function */ + if (prev_frame) + prev_frame->func_perf_prof_info->children_exec_time += time_elapsed; + } +#endif + +#if WASM_ENABLE_MEMORY_PROFILING != 0 + if (alloc_frame) { +#if WASM_ENABLE_GC == 0 + uint32 wasm_stack_used = (uint8 *)exec_env->cur_frame + + (uint32)offsetof(AOTFrame, lp) + - exec_env->wasm_stack.bottom; +#else + uint32 wasm_stack_used = + exec_env->wasm_stack.top - exec_env->wasm_stack.bottom; +#endif + if (wasm_stack_used > exec_env->max_wasm_stack_used) + exec_env->max_wasm_stack_used = wasm_stack_used; + } +#endif +} +#endif /* end of WASM_ENABLE_AOT_STACK_FRAME != 0 */ + +#if WASM_ENABLE_COPY_CALL_STACK != 0 +uint32 +aot_copy_callstack_tiny_frame(WASMExecEnv *exec_env, WASMCApiFrame *buffer, + const uint32 length, const uint32 skip_n, + char *error_buf, uint32 error_buf_size) +{ + /* + * Note for devs: please refrain from such modifications inside of + * aot_copy_callstack_tiny_frame + * - any allocations/freeing memory + * - dereferencing any pointers other than: exec_env, exec_env->module_inst, + * exec_env->module_inst->module, pointers between stack's bottom and + * top_boundary For more details check wasm_copy_callstack in + * wasm_export.h + */ + uint8 *top_boundary = exec_env->wasm_stack.top_boundary; + uint8 *top = exec_env->wasm_stack.top; + uint8 *bottom = exec_env->wasm_stack.bottom; + uint32 count = 0; + + bool is_top_index_in_range = + top_boundary >= top && top >= (bottom + sizeof(AOTTinyFrame)); + if (!is_top_index_in_range) { + char *err_msg = + "Top of the stack pointer is outside of the stack boundaries"; + strncpy(error_buf, err_msg, error_buf_size); + return 0; + } + bool is_top_aligned_with_bottom = + (unsigned long)(top - bottom) % sizeof(AOTTinyFrame) == 0; + if (!is_top_aligned_with_bottom) { + char *err_msg = "Top of the stack is not aligned with the bottom"; + strncpy(error_buf, err_msg, error_buf_size); + return 0; + } + + AOTTinyFrame *frame = (AOTTinyFrame *)(top - sizeof(AOTTinyFrame)); + WASMCApiFrame record_frame; + memset(&record_frame, 0, sizeof(WASMCApiFrame)); + while (frame && (uint8_t *)frame >= bottom && count < (skip_n + length)) { + if (count < skip_n) { + ++count; + frame -= 1; + continue; } + record_frame.instance = exec_env->module_inst; + record_frame.module_offset = 0; + record_frame.func_index = frame->func_index; + record_frame.func_offset = frame->ip_offset; + buffer[count - skip_n] = record_frame; + frame -= 1; + ++count; } + return count >= skip_n ? count - skip_n : 0; +} - return true; +uint32 +aot_copy_callstack_standard_frame(WASMExecEnv *exec_env, WASMCApiFrame *buffer, + const uint32 length, const uint32 skip_n, + char *error_buf, uint32_t error_buf_size) +{ + /* + * Note for devs: please refrain from such modifications inside of + * aot_iterate_callstack_standard_frame + * - any allocations/freeing memory + * - dereferencing any pointers other than: exec_env, exec_env->module_inst, + * exec_env->module_inst->module, pointers between stack's bottom and + * top_boundary For more details check wasm_iterate_callstack in + * wasm_export.h + */ + + uint32 count = 0; +#if WASM_ENABLE_GC == 0 + WASMModuleInstance *module_inst = + (WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env); + AOTFrame *cur_frame = (AOTFrame *)wasm_exec_env_get_cur_frame(exec_env); + uint8 *top_boundary = exec_env->wasm_stack.top_boundary; + uint8 *bottom = exec_env->wasm_stack.bottom; + uint32 frame_size = (uint32)offsetof(AOTFrame, lp); + + WASMCApiFrame record_frame; + memset(&record_frame, 0, sizeof(WASMCApiFrame)); + while (cur_frame && (uint8_t *)cur_frame >= bottom + && (uint8_t *)cur_frame + frame_size <= top_boundary + && count < (skip_n + length)) { + if (count < skip_n) { + ++count; + cur_frame = cur_frame->prev_frame; + continue; + } + record_frame.instance = module_inst; + record_frame.module_offset = 0; + record_frame.func_index = (uint32)cur_frame->func_index; + record_frame.func_offset = (uint32)cur_frame->ip_offset; + buffer[count - skip_n] = record_frame; + cur_frame = cur_frame->prev_frame; + ++count; + } +#else +/* + * TODO: add support for standard frames when GC is enabled + * now it poses a risk due to variable size of the frame + */ +#endif + return count >= skip_n ? count - skip_n : 0; } -static bool -memory_instantiate(AOTModuleInstance *module_inst, AOTModule *module, - char *error_buf, uint32 error_buf_size) +uint32 +aot_copy_callstack(WASMExecEnv *exec_env, WASMCApiFrame *buffer, + const uint32 length, const uint32 skip_n, char *error_buf, + uint32_t error_buf_size) { - uint32 i, global_index, global_data_offset, base_offset, length; - AOTMemInitData *data_seg; - uint64 total_size = (uint64)module->num_bytes_per_page * module->mem_init_page_count; + /* + * Note for devs: please refrain from such modifications inside of + * aot_iterate_callstack + * - any allocations/freeing memory + * - dereferencing any pointers other than: exec_env, exec_env->module_inst, + * exec_env->module_inst->module, pointers between stack's bottom and + * top_boundary For more details check wasm_iterate_callstack in + * wasm_export.h + */ + if (!is_tiny_frame(exec_env)) { + return aot_copy_callstack_standard_frame( + exec_env, buffer, length, skip_n, error_buf, error_buf_size); + } + else { + return aot_copy_callstack_tiny_frame(exec_env, buffer, length, skip_n, + error_buf, error_buf_size); + } +} +#endif // WASM_ENABLE_COPY_CALL_STACK - /* Allocate memory */ - if (total_size >= UINT32_MAX - || !(module_inst->memory_data.ptr = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: allocate memory failed."); +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +bool +aot_create_call_stack(struct WASMExecEnv *exec_env) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->module; + uint32 n = 0; + + void *top_frame = get_top_frame(exec_env); + while (top_frame) { + top_frame = get_prev_frame(exec_env, top_frame); + n++; + } + + /* release previous stack frames and create new ones */ + destroy_c_api_frames(module_inst->frames); + if (!bh_vector_init(module_inst->frames, n, sizeof(WASMCApiFrame), false)) { return false; } - memset(module_inst->memory_data.ptr, 0, (uint32)total_size); + top_frame = get_top_frame(exec_env); + while (n-- > 0) { + uint32 func_index, ip_offset; + uint32 *lp = NULL; +#if WASM_ENABLE_GC != 0 + uint32 *sp = NULL; + uint8 *frame_ref = NULL; +#endif + if (is_tiny_frame(exec_env)) { + AOTTinyFrame *frame = (AOTTinyFrame *)top_frame; + func_index = (uint32)frame->func_index; + ip_offset = (uint32)frame->ip_offset; + } + else { + AOTFrame *frame = (AOTFrame *)top_frame; + func_index = (uint32)frame->func_index; + ip_offset = (uint32)frame->ip_offset; + lp = frame->lp; +#if WASM_ENABLE_GC != 0 + sp = frame->sp; + frame_ref = frame->frame_ref; +#endif + } + WASMCApiFrame frame = { 0 }; + uint32 max_local_cell_num = 0, max_stack_cell_num = 0; + uint32 all_cell_num, lp_size; - /* Init memory info */ - module_inst->memory_data_end.ptr = (uint8*)module_inst->memory_data.ptr - + total_size; - module_inst->memory_data_size = (uint32)total_size; - module_inst->mem_cur_page_count = module->mem_init_page_count; - module_inst->mem_max_page_count = module->mem_max_page_count; + frame.instance = module_inst; + frame.module_offset = 0; + frame.func_index = func_index; + frame.func_offset = ip_offset; + frame.func_name_wp = get_func_name_from_index(module_inst, func_index); - if (module->mem_init_page_count > 0) { - for (i = 0; i < module->mem_init_data_count; i++) { - data_seg = module->mem_init_data_list[i]; - bh_assert(data_seg->offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || data_seg->offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); - - /* Resolve memory data base offset */ - if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - global_index = data_seg->offset.u.global_index; - bh_assert(global_index < - module->import_global_count + module->global_count); - /* TODO: && globals[data_seg->offset.u.global_index].type == - VALUE_TYPE_I32*/ - if (global_index < module->import_global_count) - global_data_offset = - module->import_globals[global_index].data_offset; - else - global_data_offset = - module->globals[global_index - module->import_global_count] - .data_offset; - - base_offset = *(uint32*) - ((uint8*)module_inst->global_data.ptr + global_data_offset); + if (!is_frame_func_idx_disabled(exec_env)) { + if (func_index >= module->import_func_count) { + uint32 aot_func_idx = func_index - module->import_func_count; + max_local_cell_num = module->max_local_cell_nums[aot_func_idx]; + max_stack_cell_num = module->max_stack_cell_nums[aot_func_idx]; } - else - base_offset = (uint32)data_seg->offset.u.i32; - - length = data_seg->byte_count; + else { + AOTFuncType *func_type = + module->import_funcs[func_index].func_type; + max_local_cell_num = func_type->param_cell_num > 2 + ? func_type->param_cell_num + : 2; + max_stack_cell_num = 0; + } + } - /* Check memory data */ - if (length > 0 - && (base_offset >= module_inst->memory_data_size - || base_offset + length > module_inst->memory_data_size)) { - wasm_free(module_inst->memory_data.ptr); - module_inst->memory_data.ptr = NULL; - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: data segment out of range."); + all_cell_num = max_local_cell_num + max_stack_cell_num; +#if WASM_ENABLE_GC == 0 + lp_size = all_cell_num * 4; +#else + lp_size = align_uint(all_cell_num * 5, 4); +#endif + if (lp_size > 0 && !is_tiny_frame(exec_env)) { + if (!(frame.lp = wasm_runtime_malloc(lp_size))) { + destroy_c_api_frames(module_inst->frames); return false; } + bh_memcpy_s(frame.lp, lp_size, lp, lp_size); - /* Copy memory data */ - memcpy((uint8*)module_inst->memory_data.ptr + base_offset, - data_seg->bytes, length); +#if WASM_ENABLE_GC != 0 + uint32 local_ref_flags_cell_num = + module->func_local_ref_flags[frame.func_index] + .local_ref_flag_cell_num; + uint8 *local_ref_flags = + module->func_local_ref_flags[frame.func_index].local_ref_flags; + frame.sp = frame.lp + (sp - lp); + frame.frame_ref = (uint8 *)frame.lp + (frame_ref - (uint8 *)lp); + /* copy local ref flags from AOT module */ + bh_memcpy_s(frame.frame_ref, local_ref_flags_cell_num, + local_ref_flags, local_ref_flags_cell_num); +#endif + } + + if (!bh_vector_append(module_inst->frames, &frame)) { + if (frame.lp) + wasm_runtime_free(frame.lp); + destroy_c_api_frames(module_inst->frames); + return false; } + + top_frame = get_prev_frame(exec_env, top_frame); } return true; } -static bool -init_func_ptrs(AOTModuleInstance *module_inst, AOTModule *module, - char *error_buf, uint32 error_buf_size) +#define PRINT_OR_DUMP() \ + do { \ + total_len += \ + wasm_runtime_dump_line_buf_impl(line_buf, print, &buf, &len); \ + if ((!print) && buf && (len == 0)) { \ + exception_unlock(module_inst); \ + return total_len; \ + } \ + } while (0) + +uint32 +aot_dump_call_stack(WASMExecEnv *exec_env, bool print, char *buf, uint32 len) { - uint32 i; - void **func_ptrs; - uint64 total_size = - ((uint64)module->import_func_count + module->func_count) * sizeof(void*); + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + uint32 n = 0, total_len = 0, total_frames; + /* reserve 256 bytes for line buffer, any line longer than 256 bytes + * will be truncated */ + char line_buf[256]; - /* Allocate memory */ - if (total_size >= UINT32_MAX - || !(module_inst->func_ptrs.ptr = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: allocate memory failed."); - return false; + if (!module_inst->frames) { + return 0; } - memset(module_inst->func_ptrs.ptr, 0, (uint32)total_size); + total_frames = (uint32)bh_vector_size(module_inst->frames); + if (total_frames == 0) { + return 0; + } - /* Set import function pointers */ - func_ptrs = (void**)module_inst->func_ptrs.ptr; - for (i = 0; i < module->import_func_count; i++, func_ptrs++) - *func_ptrs = (void*)module->import_funcs[i].func_ptr_linked; + exception_lock(module_inst); + snprintf(line_buf, sizeof(line_buf), "\n"); + PRINT_OR_DUMP(); - /* Set defined function pointers */ - memcpy(func_ptrs, module->func_ptrs, module->func_count * sizeof(void*)); - return true; + while (n < total_frames) { + WASMCApiFrame frame = { 0 }; + uint32 line_length, i; + + if (!bh_vector_get(module_inst->frames, n, &frame)) { + exception_unlock(module_inst); + return 0; + } + + /* function name not exported, print number instead */ + if (frame.func_name_wp == NULL) { + line_length = snprintf(line_buf, sizeof(line_buf), + "#%02" PRIu32 ": 0x%04x - $f%" PRIu32 "\n", + n, frame.func_offset, frame.func_index); + } + else { + line_length = snprintf(line_buf, sizeof(line_buf), + "#%02" PRIu32 ": 0x%04x - %s\n", n, + frame.func_offset, frame.func_name_wp); + } + + if (line_length >= sizeof(line_buf)) { + uint32 line_buffer_len = sizeof(line_buf); + /* If line too long, ensure the last character is '\n' */ + for (i = line_buffer_len - 5; i < line_buffer_len - 2; i++) { + line_buf[i] = '.'; + } + line_buf[line_buffer_len - 2] = '\n'; + } + + PRINT_OR_DUMP(); + + n++; + } + snprintf(line_buf, sizeof(line_buf), "\n"); + PRINT_OR_DUMP(); + exception_unlock(module_inst); + + return total_len + 1; } +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK != 0 */ -static bool -init_func_type_indexes(AOTModuleInstance *module_inst, AOTModule *module, - char *error_buf, uint32 error_buf_size) +#if WASM_ENABLE_PERF_PROFILING != 0 +void +aot_dump_perf_profiling(const AOTModuleInstance *module_inst) { - uint32 i; - uint32 *func_type_index; - uint64 total_size = - ((uint64)module->import_func_count + module->func_count) * sizeof(uint32); + AOTFuncPerfProfInfo *perf_prof = + (AOTFuncPerfProfInfo *)module_inst->func_perf_profilings; + AOTModule *module = (AOTModule *)module_inst->module; + uint32 total_func_count = module->import_func_count + module->func_count, i; + const char *func_name; - /* Allocate memory */ - if (total_size >= UINT32_MAX - || !(module_inst->func_type_indexes.ptr = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: allocate memory failed."); - return false; + os_printf("Performance profiler data:\n"); + for (i = 0; i < total_func_count; i++, perf_prof++) { + if (perf_prof->total_exec_cnt == 0) + continue; + + func_name = get_func_name_from_index(module_inst, i); + + if (func_name) + os_printf( + " func %s, execution time: %.3f ms, execution count: %" PRIu32 + " times, children execution time: %.3f ms\n", + func_name, perf_prof->total_exec_time / 1000.0, + perf_prof->total_exec_cnt, + perf_prof->children_exec_time / 1000.0); + else + os_printf(" func %" PRIu32 + ", execution time: %.3f ms, execution count: %" PRIu32 + " times, children execution time: %.3f ms\n", + i, perf_prof->total_exec_time / 1000.0, + perf_prof->total_exec_cnt, + perf_prof->children_exec_time / 1000.0); } +} - memset(module_inst->func_type_indexes.ptr, 0, (uint32)total_size); +double +aot_summarize_wasm_execute_time(const AOTModuleInstance *inst) +{ + double ret = 0; - /* Set import function type indexes */ - func_type_index = (uint32*)module_inst->func_type_indexes.ptr; - for (i = 0; i < module->import_func_count; i++, func_type_index++) - *func_type_index = module->import_funcs[i].func_type_index; + AOTModule *module = (AOTModule *)inst->module; + uint32 total_func_count = module->import_func_count + module->func_count, i; - memcpy(func_type_index, module->func_type_indexes, - module->func_count * sizeof(uint32)); + for (i = 0; i < total_func_count; i++) { + AOTFuncPerfProfInfo *perf_prof = + (AOTFuncPerfProfInfo *)inst->func_perf_profilings + i; + ret += (perf_prof->total_exec_time - perf_prof->children_exec_time) + / 1000.0; + } - return true; + return ret; } -static bool -execute_post_inst_function(AOTModuleInstance *module_inst) +double +aot_get_wasm_func_exec_time(const AOTModuleInstance *inst, + const char *func_name) { - AOTFunctionInstance *post_inst_func = - aot_lookup_function(module_inst, "__post_instantiate", "()"); + AOTModule *module = (AOTModule *)inst->module; + uint32 total_func_count = module->import_func_count + module->func_count, i; - if (!post_inst_func) - /* Not found */ - return true; + for (i = 0; i < total_func_count; i++) { + const char *name_in_wasm = get_func_name_from_index(inst, i); + if (name_in_wasm && strcmp(func_name, name_in_wasm) == 0) { + AOTFuncPerfProfInfo *perf_prof = + (AOTFuncPerfProfInfo *)inst->func_perf_profilings + i; + return (perf_prof->total_exec_time - perf_prof->children_exec_time) + / 1000.0; + } + } - return aot_create_exec_env_and_call_function(module_inst, post_inst_func, 0, NULL); + return -1.0; } +#endif /* end of WASM_ENABLE_PERF_PROFILING != 0 */ + +#if WASM_ENABLE_STATIC_PGO != 0 + +/* indirect call target */ +#define IPVK_IndirectCallTarget 0 +/* memory intrinsic functions size */ +#define IPVK_MemOPSize 1 +#define IPVK_First IPVK_IndirectCallTarget +#define IPVK_Last IPVK_MemOPSize + +#define INSTR_PROF_DEFAULT_NUM_VAL_PER_SITE 24 +#define INSTR_PROF_MAX_NUM_VAL_PER_SITE 255 + +static int hasNonDefaultValsPerSite = 0; +static uint32 VPMaxNumValsPerSite = INSTR_PROF_DEFAULT_NUM_VAL_PER_SITE; static bool -execute_start_function(AOTModuleInstance *module_inst) +cmpxchg_ptr(void **ptr, void *old_val, void *new_val) { - AOTModule *module = (AOTModule*)module_inst->aot_module.ptr; - WASMExecEnv *exec_env; - typedef void (*F)(WASMExecEnv*); - union { F f; void *v; } u; - - if (!module->start_function) +#if defined(os_atomic_cmpxchg) + return os_atomic_cmpxchg((_Atomic(void *) *)ptr, &old_val, new_val); +#else + /* TODO: add lock when thread-manager is enabled */ + void *read = *ptr; + if (read == old_val) { + *ptr = new_val; return true; + } + return false; +#endif +} - if (!(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; +static int +allocateValueProfileCounters(LLVMProfileData *Data) +{ + ValueProfNode **Mem; + uint64 NumVSites = 0, total_size; + uint32 VKI; + + /* When dynamic allocation is enabled, allow tracking the max number of + values allowed. */ + if (!hasNonDefaultValsPerSite) + VPMaxNumValsPerSite = INSTR_PROF_MAX_NUM_VAL_PER_SITE; + + for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI) + NumVSites += Data->num_value_sites[VKI]; + + /* If NumVSites = 0, calloc is allowed to return a non-null pointer. */ + bh_assert(NumVSites > 0 && "NumVSites can't be zero"); + + total_size = (uint64)sizeof(ValueProfNode *) * NumVSites; + if (total_size > UINT32_MAX + || !(Mem = (ValueProfNode **)wasm_runtime_malloc((uint32)total_size))) { + return 0; + } + memset(Mem, 0, (uint32)total_size); + + if (!cmpxchg_ptr((void **)&Data->values, NULL, Mem)) { + wasm_runtime_free(Mem); + return 0; } + return 1; +} - u.v = module->start_function; - u.f(exec_env); +static ValueProfNode * +allocateOneNode(void) +{ + ValueProfNode *Node; - wasm_exec_env_destroy(exec_env); - return !aot_get_exception(module_inst); + Node = wasm_runtime_malloc((uint32)sizeof(ValueProfNode)); + if (Node) + memset(Node, 0, sizeof(ValueProfNode)); + return Node; } -AOTModuleInstance* -aot_instantiate(AOTModule *module, - uint32 stack_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size) +static void +instrumentTargetValueImpl(uint64 TargetValue, void *Data, uint32 CounterIndex, + uint64 CountValue) { - AOTModuleInstance *module_inst; - uint32 module_inst_struct_size = - offsetof(AOTModuleInstance, global_table_heap_data.bytes); - uint64 table_data_size = (uint64)module->table_size * sizeof(uint32); - uint64 total_size = (uint64)module_inst_struct_size - + module->global_data_size - + table_data_size + heap_size; - void *heap_handle; - uint8 *p; + ValueProfNode **ValueCounters; + ValueProfNode *PrevVNode = NULL, *MinCountVNode = NULL, *CurVNode; + LLVMProfileData *PData = (LLVMProfileData *)Data; + uint64 MinCount = UINT64_MAX; + uint8 VDataCount = 0; + bool success = false; - /* Check heap size */ - heap_size = align_uint(heap_size, 8); - if (heap_size == 0) - heap_size = APP_HEAP_SIZE_DEFAULT; - if (heap_size < APP_HEAP_SIZE_MIN) - heap_size = APP_HEAP_SIZE_MIN; - if (heap_size > APP_HEAP_SIZE_MAX) - heap_size = APP_HEAP_SIZE_MAX; + if (!PData) + return; + if (!CountValue) + return; + if (!PData->values) { + if (!allocateValueProfileCounters(PData)) + return; + } - /* Allocate module instance, global data, table data and heap data */ - if (total_size >= UINT32_MAX - || !(module_inst = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: allocate memory failed."); - return NULL; + ValueCounters = (ValueProfNode **)PData->values; + CurVNode = ValueCounters[CounterIndex]; + + while (CurVNode) { + if (TargetValue == CurVNode->value) { + CurVNode->count += CountValue; + return; + } + if (CurVNode->count < MinCount) { + MinCount = CurVNode->count; + MinCountVNode = CurVNode; + } + PrevVNode = CurVNode; + CurVNode = CurVNode->next; + ++VDataCount; } - memset(module_inst, 0, total_size); - module_inst->module_type = Wasm_Module_AoT; - module_inst->aot_module.ptr = module; + if (VDataCount >= VPMaxNumValsPerSite) { + if (MinCountVNode->count <= CountValue) { + CurVNode = MinCountVNode; + CurVNode->value = TargetValue; + CurVNode->count = CountValue; + } + else + MinCountVNode->count -= CountValue; - /* Initialize global info */ - p = (uint8*)module_inst + module_inst_struct_size; - module_inst->global_data.ptr = p; - module_inst->global_data_size = module->global_data_size; - if (!global_instantiate(module_inst, module, error_buf, error_buf_size)) - goto fail; + return; + } - /* Initialize table info */ - p += module->global_data_size; - module_inst->table_data.ptr = p; - module_inst->table_size = module->table_size; - /* Set all elements to -1 to mark them as uninitialized elements */ - memset(module_inst->table_data.ptr, -1, (uint32)table_data_size); - if (!table_instantiate(module_inst, module, error_buf, error_buf_size)) - goto fail; + CurVNode = allocateOneNode(); + if (!CurVNode) + return; + CurVNode->value = TargetValue; + CurVNode->count += CountValue; + + if (!ValueCounters[CounterIndex]) { + success = + cmpxchg_ptr((void **)&ValueCounters[CounterIndex], NULL, CurVNode); + } + else if (PrevVNode && !PrevVNode->next) { + success = cmpxchg_ptr((void **)&PrevVNode->next, 0, CurVNode); + } + + if (!success) { + wasm_runtime_free(CurVNode); + } +} + +void +llvm_profile_instrument_target(uint64 target_value, void *data, + uint32 counter_idx) +{ + instrumentTargetValueImpl(target_value, data, counter_idx, 1); +} + +static inline uint32 +popcount64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static inline uint32 +clz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +/* Map an (observed) memop size value to the representative value of its range. + For example, 5 -> 5, 22 -> 17, 99 -> 65, 256 -> 256, 1001 -> 513. */ +static uint64 +InstrProfGetRangeRepValue(uint64 Value) +{ + if (Value <= 8) + /* The first ranges are individually tracked. Use the value as is. */ + return Value; + else if (Value >= 513) + /* The last range is mapped to its lowest value. */ + return 513; + else if (popcount64(Value) == 1) + /* If it's a power of two, use it as is. */ + return Value; + else + /* Otherwise, take to the previous power of two + 1. */ + return (((uint64)1) << (64 - clz64(Value) - 1)) + 1; +} + +void +llvm_profile_instrument_memop(uint64 target_value, void *data, + uint32 counter_idx) +{ + uint64 rep_value = InstrProfGetRangeRepValue(target_value); + instrumentTargetValueImpl(rep_value, data, counter_idx, 1); +} + +static uint32 +get_pgo_prof_data_size(AOTModuleInstance *module_inst, uint32 *p_num_prof_data, + uint32 *p_num_prof_counters, uint32 *p_padding_size, + uint32 *p_prof_counters_size, uint32 *p_prof_names_size, + uint32 *p_value_counters_size, uint8 **p_prof_names) +{ + AOTModule *module = (AOTModule *)module_inst->module; + LLVMProfileData *prof_data; + uint8 *prof_names = NULL; + uint32 num_prof_data = 0, num_prof_counters = 0, padding_size, i; + uint32 prof_counters_size = 0, prof_names_size = 0; + uint32 total_size, total_size_wo_value_counters; - /* Initialize heap info */ - p += (uint32)table_data_size; - module_inst->heap_data.ptr = p; - p += heap_size; - module_inst->heap_data_end.ptr = p; - module_inst->heap_data_size = heap_size; -#if WASM_ENABLE_MEMORY_GROW != 0 - module_inst->heap_base_offset = DEFAULT_APP_HEAP_BASE_OFFSET; -#else - module_inst->heap_base_offset = module_inst->memory_data_size; -#endif - if (!(heap_handle = mem_allocator_create(module_inst->heap_data.ptr, - heap_size))) { - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: init app heap failed."); - goto fail; + for (i = 0; i < module->data_section_count; i++) { + if (!strncmp(module->data_sections[i].name, "__llvm_prf_data", 15)) { + bh_assert(module->data_sections[i].size == sizeof(LLVMProfileData)); + num_prof_data++; + prof_data = (LLVMProfileData *)module->data_sections[i].data; + num_prof_counters += prof_data->num_counters; + } + else if (!strncmp(module->data_sections[i].name, "__llvm_prf_cnts", + 15)) { + prof_counters_size += module->data_sections[i].size; + } + else if (!strncmp(module->data_sections[i].name, "__llvm_prf_names", + 16)) { + prof_names_size = module->data_sections[i].size; + prof_names = module->data_sections[i].data; + } } - module_inst->heap_handle.ptr = heap_handle; - /* Initialize memory space */ - if (!memory_instantiate(module_inst, module, error_buf, error_buf_size)) - goto fail; + if (prof_counters_size != num_prof_counters * sizeof(uint64)) + return 0; - /* Initialize function pointers */ - if (!init_func_ptrs(module_inst, module, error_buf, error_buf_size)) - goto fail; + total_size = sizeof(LLVMProfileRawHeader) + + num_prof_data * sizeof(LLVMProfileData_64) + + prof_counters_size + prof_names_size; + padding_size = sizeof(uint64) - (prof_names_size % sizeof(uint64)); + if (padding_size != sizeof(uint64)) + total_size += padding_size; - /* Initialize function type indexes */ - if (!init_func_type_indexes(module_inst, module, error_buf, error_buf_size)) - goto fail; + /* Total size excluding value counters */ + total_size_wo_value_counters = total_size; -#if WASM_ENABLE_LIBC_WASI != 0 - if (!wasm_runtime_init_wasi((WASMModuleInstanceCommon*)module_inst, - module->wasi_args.dir_list, - module->wasi_args.dir_count, - module->wasi_args.map_dir_list, - module->wasi_args.map_dir_count, - module->wasi_args.env, - module->wasi_args.env_count, - module->wasi_args.argv, - module->wasi_args.argc, - error_buf, error_buf_size)) - goto fail; -#endif + for (i = 0; i < module->data_section_count; i++) { + if (!strncmp(module->data_sections[i].name, "__llvm_prf_data", 15)) { + uint32 j, k, num_value_sites, num_value_nodes; + ValueProfNode **values, *value_node; - /* Initialize the thread related data */ - if (stack_size == 0) - stack_size = DEFAULT_WASM_STACK_SIZE; - module_inst->default_wasm_stack_size = stack_size; + prof_data = (LLVMProfileData *)module->data_sections[i].data; + values = prof_data->values; - /* Execute __post_instantiate function and start function*/ - if (!execute_post_inst_function(module_inst) - || !execute_start_function(module_inst)) { - set_error_buf(error_buf, error_buf_size, - module_inst->cur_exception); - goto fail; + if (prof_data->num_value_sites[0] > 0 + || prof_data->num_value_sites[1] > 0) { + /* TotalSize (uint32) and NumValueKinds (uint32) */ + total_size += 8; + for (j = 0; j < 2; j++) { + if ((num_value_sites = prof_data->num_value_sites[j]) > 0) { + /* ValueKind (uint32) and NumValueSites (uint32) */ + total_size += 8; + /* (Value + Counter) group counts of each value site, + each count is one byte */ + total_size += align_uint(num_value_sites, 8); + + if (values) { + for (k = 0; k < num_value_sites; k++) { + num_value_nodes = 0; + value_node = *values; + while (value_node) { + num_value_nodes++; + value_node = value_node->next; + } + if (num_value_nodes) { + /* (Value + Counter) groups */ + total_size += num_value_nodes * 8 * 2; + } + values++; + } + } + } + } + } + } } - return module_inst; + if (p_num_prof_data) + *p_num_prof_data = num_prof_data; + if (p_num_prof_counters) + *p_num_prof_counters = num_prof_counters; + if (p_padding_size) + *p_padding_size = padding_size; + if (p_prof_counters_size) + *p_prof_counters_size = prof_counters_size; + if (p_prof_names_size) + *p_prof_names_size = prof_names_size; + if (p_value_counters_size) + *p_value_counters_size = total_size - total_size_wo_value_counters; + if (p_prof_names) + *p_prof_names = prof_names; -fail: - aot_deinstantiate(module_inst); - return NULL; + return total_size; } -void -aot_deinstantiate(AOTModuleInstance *module_inst) +uint32 +aot_get_pgo_prof_data_size(AOTModuleInstance *module_inst) { -#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, - these fields will be set to NULL, we cannot free their internal data - which may allocated from global heap. */ - wasm_runtime_destroy_wasi((WASMModuleInstanceCommon*)module_inst); -#endif + return get_pgo_prof_data_size(module_inst, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); +} - if (module_inst->memory_data.ptr) - wasm_free(module_inst->memory_data.ptr); +static union { + int a; + char b; +} __ue = { .a = 1 }; - if (module_inst->heap_handle.ptr) - mem_allocator_destroy(module_inst->heap_handle.ptr); +#define is_little_endian() (__ue.b == 1) - if (module_inst->func_ptrs.ptr) - wasm_free(module_inst->func_ptrs.ptr); +uint32 +aot_dump_pgo_prof_data_to_buf(AOTModuleInstance *module_inst, char *buf, + uint32 len) +{ + AOTModule *module = (AOTModule *)module_inst->module; + LLVMProfileRawHeader prof_header = { 0 }; + LLVMProfileData *prof_data; + uint8 *prof_names = NULL; + uint32 num_prof_data = 0, num_prof_counters = 0, padding_size, i; + uint32 prof_counters_size = 0, prof_names_size = 0; + uint32 value_counters_size = 0, value_counters_size_backup = 0; + uint32 total_size, size; + int64 counters_delta, offset_counters; - if (module_inst->func_type_indexes.ptr) - wasm_free(module_inst->func_type_indexes.ptr); + total_size = get_pgo_prof_data_size(module_inst, &num_prof_data, + &num_prof_counters, &padding_size, + &prof_counters_size, &prof_names_size, + &value_counters_size, &prof_names); + if (len < total_size) + return 0; - wasm_free(module_inst); -} + value_counters_size_backup = value_counters_size; + value_counters_size = 0; -static bool -check_type(uint8 type, const char *p) -{ - const char *str = "i32"; + prof_header.counters_delta = counters_delta = + sizeof(LLVMProfileData_64) * num_prof_data; + offset_counters = 0; + for (i = 0; i < module->data_section_count; i++) { + if (!strncmp(module->data_sections[i].name, "__llvm_prf_data", 15)) { + prof_data = (LLVMProfileData *)module->data_sections[i].data; + prof_data->offset_counters = counters_delta + offset_counters; + offset_counters += prof_data->num_counters * sizeof(uint64); + counters_delta -= sizeof(LLVMProfileData_64); + } + } - if (strlen(p) < 3) - return false; + prof_header.magic = 0xFF6C70726F667281LL; + /* Version 9 */ + prof_header.version = 0x0000000000000009LL; + /* with VARIANT_MASK_IR_PROF (IR Instrumentation) */ + prof_header.version |= 0x1ULL << 56; + /* with VARIANT_MASK_MEMPROF (Memory Profile) */ + prof_header.version |= 0x1ULL << 62; + prof_header.num_prof_data = num_prof_data; + prof_header.num_prof_counters = num_prof_counters; + prof_header.names_size = prof_names_size; + prof_header.value_kind_last = 1; + /* __llvm_prf_bits won't be used in PGO, set dummy value here */ + prof_header.num_prof_bitmaps = 0; + prof_header.bitmap_delta = 0; - switch (type) { - case VALUE_TYPE_I32: - str = "i32"; - break; - case VALUE_TYPE_I64: - str = "i64"; - break; - case VALUE_TYPE_F32: - str = "f32"; - break; - case VALUE_TYPE_F64: - str = "f64"; - break; + if (!is_little_endian()) { + aot_exchange_uint64((uint8 *)&prof_header.magic); + aot_exchange_uint64((uint8 *)&prof_header.version); + aot_exchange_uint64((uint8 *)&prof_header.num_prof_data); + aot_exchange_uint64((uint8 *)&prof_header.num_prof_counters); + aot_exchange_uint64((uint8 *)&prof_header.num_prof_bitmaps); + aot_exchange_uint64((uint8 *)&prof_header.names_size); + aot_exchange_uint64((uint8 *)&prof_header.counters_delta); + aot_exchange_uint64((uint8 *)&prof_header.bitmap_delta); + aot_exchange_uint64((uint8 *)&prof_header.value_kind_last); } - if (strncmp(p, str, 3)) - return false; - return true; -} + size = sizeof(LLVMProfileRawHeader); + bh_memcpy_s(buf, size, &prof_header, size); + buf += size; -static bool -check_function_type(const WASMType *type, - const char *signature) -{ - uint32 i; - const char *p = signature; + for (i = 0; i < module->data_section_count; i++) { + if (!strncmp(module->data_sections[i].name, "__llvm_prf_data", 15)) { + LLVMProfileData_64 *prof_data_64 = (LLVMProfileData_64 *)buf; - if (!p || *p++ != '(') - return false; + /* Convert LLVMProfileData to LLVMProfileData_64, the pointer width + in the output file is always 8 bytes */ + prof_data = (LLVMProfileData *)module->data_sections[i].data; + prof_data_64->func_md5 = prof_data->func_md5; + prof_data_64->func_hash = prof_data->func_hash; + prof_data_64->offset_counters = prof_data->offset_counters; + prof_data_64->offset_bitmaps = prof_data->offset_bitmaps; + prof_data_64->func_ptr = prof_data->func_ptr; + prof_data_64->values = (uint64)(uintptr_t)prof_data->values; + prof_data_64->num_counters = prof_data->num_counters; + /* __llvm_prf_bits won't be used in PGO, set dummy value here */ + prof_data_64->num_bitmaps = 0; + prof_data_64->num_value_sites[0] = prof_data->num_value_sites[0]; + prof_data_64->num_value_sites[1] = prof_data->num_value_sites[1]; - for (i = 0; i < type->param_count; i++) { - if (!check_type(type->types[i], p)) - return false; - p += 3; + if (!is_little_endian()) { + aot_exchange_uint64((uint8 *)&prof_data_64->func_hash); + aot_exchange_uint64((uint8 *)&prof_data_64->offset_counters); + aot_exchange_uint64((uint8 *)&prof_data_64->offset_bitmaps); + aot_exchange_uint64((uint8 *)&prof_data_64->func_ptr); + aot_exchange_uint64((uint8 *)&prof_data_64->values); + aot_exchange_uint32((uint8 *)&prof_data_64->num_counters); + aot_exchange_uint32((uint8 *)&prof_data_64->num_bitmaps); + aot_exchange_uint16((uint8 *)&prof_data_64->num_value_sites[0]); + aot_exchange_uint16((uint8 *)&prof_data_64->num_value_sites[1]); + } + buf += sizeof(LLVMProfileData_64); + } } - if (*p++ != ')') - return false; + for (i = 0; i < module->data_section_count; i++) { + if (!strncmp(module->data_sections[i].name, "__llvm_prf_cnts", 15)) { + size = module->data_sections[i].size; + bh_memcpy_s(buf, size, module->data_sections[i].data, size); + buf += size; + } + } - if (type->result_count) { - if (!check_type(type->types[type->param_count], p)) - return false; - p += 3; + if (prof_names && prof_names_size > 0) { + size = prof_names_size; + bh_memcpy_s(buf, size, prof_names, size); + buf += size; + padding_size = sizeof(uint64) - (prof_names_size % sizeof(uint64)); + if (padding_size != sizeof(uint64)) { + char padding_buf[8] = { 0 }; + bh_memcpy_s(buf, padding_size, padding_buf, padding_size); + buf += padding_size; + } } - if (*p != '\0') - return false; + for (i = 0; i < module->data_section_count; i++) { + if (!strncmp(module->data_sections[i].name, "__llvm_prf_data", 15)) { + uint32 j, k, num_value_sites, num_value_nodes; + ValueProfNode **values, **values_tmp, *value_node; - return true; -} + prof_data = (LLVMProfileData *)module->data_sections[i].data; + values = values_tmp = prof_data->values; -AOTFunctionInstance* -aot_lookup_function(const AOTModuleInstance *module_inst, - const char *name, const char *signature) -{ - uint32 i; - AOTModule *module = (AOTModule*)module_inst->aot_module.ptr; + if (prof_data->num_value_sites[0] > 0 + || prof_data->num_value_sites[1] > 0) { + uint32 *buf_total_size = (uint32 *)buf; - for (i = 0; i < module->export_func_count; i++) { - if (!strcmp(module->export_funcs[i].func_name, name) - && check_function_type(module->export_funcs[i].func_type, - signature)) - return &module->export_funcs[i]; - } + buf += 4; /* emit TotalSize later */ + *(uint32 *)buf = (prof_data->num_value_sites[0] > 0 + && prof_data->num_value_sites[1] > 0) + ? 2 + : 1; + if (!is_little_endian()) + aot_exchange_uint32((uint8 *)buf); + buf += 4; - return NULL; -} + for (j = 0; j < 2; j++) { + if ((num_value_sites = prof_data->num_value_sites[j]) > 0) { + /* ValueKind */ + *(uint32 *)buf = j; + if (!is_little_endian()) + aot_exchange_uint32((uint8 *)buf); + buf += 4; + /* NumValueSites */ + *(uint32 *)buf = num_value_sites; + if (!is_little_endian()) + aot_exchange_uint32((uint8 *)buf); + buf += 4; -#define PUT_I64_TO_ADDR(addr, value) do { \ - union { int64 val; uint32 parts[2]; } u; \ - u.val = (value); \ - (addr)[0] = u.parts[0]; \ - (addr)[1] = u.parts[1]; \ - } while (0) + for (k = 0; k < num_value_sites; k++) { + num_value_nodes = 0; + if (values_tmp) { + value_node = *values_tmp; + while (value_node) { + num_value_nodes++; + value_node = value_node->next; + } + values_tmp++; + } + bh_assert(num_value_nodes < 255); + *(uint8 *)buf++ = (uint8)num_value_nodes; + } + if (num_value_sites % 8) { + buf += 8 - (num_value_sites % 8); + } -#define PUT_F64_TO_ADDR(addr, value) do { \ - union { float64 val; uint32 parts[2]; } u; \ - u.val = (value); \ - (addr)[0] = u.parts[0]; \ - (addr)[1] = u.parts[1]; \ - } while (0) + for (k = 0; k < num_value_sites; k++) { + if (values) { + value_node = *values; + while (value_node) { + *(uint64 *)buf = value_node->value; + if (!is_little_endian()) + aot_exchange_uint64((uint8 *)buf); + buf += 8; + *(uint64 *)buf = value_node->count; + if (!is_little_endian()) + aot_exchange_uint64((uint8 *)buf); + buf += 8; + value_node = value_node->next; + } + values++; + } + } + } + } -bool -aot_call_function(WASMExecEnv *exec_env, - AOTFunctionInstance *function, - unsigned argc, uint32 argv[]) -{ - AOTModuleInstance *module_inst = (AOTModuleInstance*)exec_env->module_inst; - AOTFuncType *func_type = function->func_type; - bool ret = wasm_runtime_invoke_native(function->func_ptr, func_type, - exec_env, argv, argc, argv); - return ret && !aot_get_exception(module_inst) ? true : false; + /* TotalSize */ + *(uint32 *)buf_total_size = + (uint8 *)buf - (uint8 *)buf_total_size; + if (!is_little_endian()) + aot_exchange_uint64((uint8 *)buf_total_size); + value_counters_size += (uint8 *)buf - (uint8 *)buf_total_size; + } + } + } + + bh_assert(value_counters_size == value_counters_size_backup); + (void)value_counters_size_backup; + + return total_size; } +#endif /* end of WASM_ENABLE_STATIC_PGO != 0 */ -bool -aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, - AOTFunctionInstance *func, - unsigned argc, uint32 argv[]) +#if WASM_ENABLE_GC != 0 +void * +aot_create_func_obj(AOTModuleInstance *module_inst, uint32 func_idx, + bool throw_exce, char *error_buf, uint32 error_buf_size) { - WASMExecEnv *exec_env; - bool ret; + AOTModule *module = (AOTModule *)module_inst->module; + WASMRttTypeRef rtt_type; + WASMFuncObjectRef func_obj; + AOTFuncType *func_type; + uint32 type_idx; - if (!(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; + if (throw_exce) { + error_buf = module_inst->cur_exception; + error_buf_size = sizeof(module_inst->cur_exception); } - ret = aot_call_function(exec_env, func, argc, argv); - wasm_exec_env_destroy(exec_env); - return ret; -} + if (func_idx >= module->import_func_count + module->func_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown function %d", + func_idx); + return NULL; + } -void -aot_set_exception(AOTModuleInstance *module_inst, - const char *exception) -{ - if (exception) - snprintf(module_inst->cur_exception, - sizeof(module_inst->cur_exception), - "Exception: %s", exception); - else - module_inst->cur_exception[0] = '\0'; -} + type_idx = module_inst->func_type_indexes[func_idx]; + func_type = (AOTFuncType *)module->types[type_idx]; -void -aot_set_exception_with_id(AOTModuleInstance *module_inst, - uint32 id) -{ - switch (id) { - case EXCE_UNREACHABLE: - aot_set_exception(module_inst, "unreachable"); - break; - case EXCE_OUT_OF_MEMORY: - aot_set_exception(module_inst, "allocate memory failed"); - break; - case EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS: - aot_set_exception(module_inst, "out of bounds memory access"); - break; - case EXCE_INTEGER_OVERFLOW: - aot_set_exception(module_inst, "integer overflow"); - break; - case EXCE_INTEGER_DIVIDE_BY_ZERO: - aot_set_exception(module_inst, "integer divide by zero"); - break; - case EXCE_INVALID_CONVERSION_TO_INTEGER: - aot_set_exception(module_inst, "invalid conversion to integer"); - break; - case EXCE_INVALID_FUNCTION_TYPE_INDEX: - aot_set_exception(module_inst, "indirect call type mismatch"); - break; - case EXCE_INVALID_FUNCTION_INDEX: - aot_set_exception(module_inst, "invalid function index"); - break; - case EXCE_UNDEFINED_ELEMENT: - aot_set_exception(module_inst, "undefined element"); - break; - case EXCE_UNINITIALIZED_ELEMENT: - aot_set_exception(module_inst, "uninitialized element"); - break; - case EXCE_CALL_UNLINKED_IMPORT_FUNC: - aot_set_exception(module_inst, "fail to call unlinked import function"); - break; - default: - break; + if (!(rtt_type = wasm_rtt_type_new((AOTType *)func_type, type_idx, + module->rtt_types, module->type_count, + &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, "create rtt object failed"); + return NULL; } -} -const char* -aot_get_exception(AOTModuleInstance *module_inst) -{ - if (module_inst->cur_exception[0] == '\0') + if (!(func_obj = wasm_func_obj_new_internal( + ((AOTModuleInstanceExtra *)module_inst->e)->common.gc_heap_handle, + rtt_type, func_idx))) { + set_error_buf(error_buf, error_buf_size, "create func object failed"); return NULL; - else - return module_inst->cur_exception; + } + + return func_obj; } -void -aot_clear_exception(AOTModuleInstance *module_inst) +bool +aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj, + uint32 type_index) { - module_inst->cur_exception[0] = '\0'; + AOTModule *aot_module = (AOTModule *)module_inst->module; + AOTType **types = aot_module->types; + uint32 type_count = aot_module->type_count; + + return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count); } -int32 -aot_module_malloc(AOTModuleInstance *module_inst, uint32 size) +bool +aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1, + uint32 type_idx2) { - uint8 *addr = - mem_allocator_malloc(module_inst->heap_handle.ptr, size); + AOTModule *aot_module = (AOTModule *)module_inst->module; + AOTType **types = aot_module->types; - if (!addr) { - aot_set_exception(module_inst, "out of memory"); - return 0; - } - return (int32)(module_inst->heap_base_offset - + (addr - (uint8*)module_inst->heap_data.ptr)); + if (type_idx1 == type_idx2) + return true; + + bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC); + bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC); + return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1], + (WASMFuncType *)types[type_idx2]); } -void -aot_module_free(AOTModuleInstance *module_inst, int32 ptr) +WASMRttTypeRef +aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index) { - if (ptr) { - uint8 *addr = (uint8*)module_inst->heap_data.ptr - + (ptr - module_inst->heap_base_offset); - if ((uint8*)module_inst->heap_data.ptr < addr - && addr < (uint8*)module_inst->heap_data_end.ptr) - mem_allocator_free(module_inst->heap_handle.ptr, addr); - } + AOTModule *aot_module = (AOTModule *)module_inst->module; + AOTType *defined_type = aot_module->types[type_index]; + WASMRttType **rtt_types = aot_module->rtt_types; + uint32 rtt_type_count = aot_module->type_count; + korp_mutex *rtt_type_lock = &aot_module->rtt_type_lock; + + return wasm_rtt_type_new(defined_type, type_index, rtt_types, + rtt_type_count, rtt_type_lock); } -int32 -aot_module_dup_data(AOTModuleInstance *module_inst, - const char *src, uint32 size) +bool +aot_array_init_with_data(AOTModuleInstance *module_inst, uint32 seg_index, + uint32 data_seg_offset, WASMArrayObjectRef array_obj, + uint32 elem_size, uint32 array_len) { - int32 buffer_offset = aot_module_malloc(module_inst, size); + AOTModule *aot_module; + uint8 *data = NULL; + uint8 *array_elem_base; + uint64 seg_len = 0; + uint64 total_size = (int64)elem_size * array_len; - if (buffer_offset != 0) { - char *buffer; - buffer = aot_addr_app_to_native(module_inst, buffer_offset); - memcpy(buffer, src, size); + aot_module = (AOTModule *)module_inst->module; + seg_len = aot_module->mem_init_data_list[seg_index]->byte_count; + data = aot_module->mem_init_data_list[seg_index]->bytes; + + if (data_seg_offset >= seg_len || total_size > seg_len - data_seg_offset) { + aot_set_exception(module_inst, "out of bounds memory access"); + return false; } - return buffer_offset; + + array_elem_base = (uint8 *)wasm_array_obj_first_elem_addr(array_obj); + bh_memcpy_s(array_elem_base, (uint32)total_size, data + data_seg_offset, + (uint32)total_size); + + return true; } -bool -aot_validate_app_addr(AOTModuleInstance *module_inst, - int32 app_offset, uint32 size) +static bool +aot_global_traverse_gc_rootset(AOTModuleInstance *module_inst, void *heap) { - uint8 *addr; + AOTModule *module = (AOTModule *)module_inst->module; + uint8 *global_data = module_inst->global_data; + AOTImportGlobal *import_global = module->import_globals; + AOTGlobal *global = module->globals; + WASMObjectRef gc_obj; + uint32 i; - /* integer overflow check */ - if(app_offset + (int32)size < app_offset) { - goto fail; + for (i = 0; i < module->import_global_count; i++, import_global++) { + if (wasm_is_type_reftype(import_global->type.val_type)) { + gc_obj = GET_REF_FROM_ADDR((uint32 *)global_data); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } + global_data += import_global->size; } - if (0 <= app_offset - && app_offset < (int32)module_inst->memory_data_size) { - addr = (uint8*)module_inst->memory_data.ptr + app_offset; - if (!((uint8*)module_inst->memory_data.ptr <= addr - && addr + size <= (uint8*)module_inst->memory_data_end.ptr)) - goto fail; - return true; - } - /* Currently heap_size is no more than 1G, and heap_base_offset is 1G, - heap_base_offset + heap_data_size will not be larger than INT32_MAX */ - else if (module_inst->heap_base_offset < app_offset - && app_offset < module_inst->heap_base_offset - + (int32)module_inst->heap_data_size) { - addr = (uint8*)module_inst->heap_data.ptr - + (app_offset - module_inst->heap_base_offset); - if (!((uint8*)module_inst->heap_data.ptr <= addr - && addr + size <= (uint8*)module_inst->heap_data_end.ptr)) - goto fail; - return true; + for (i = 0; i < module->global_count; i++, global++) { + if (wasm_is_type_reftype(global->type.val_type)) { + gc_obj = GET_REF_FROM_ADDR((uint32 *)global_data); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } + global_data += global->size; } -fail: - aot_set_exception(module_inst, "out of bounds memory access"); - return false; + return true; } -bool -aot_validate_native_addr(AOTModuleInstance *module_inst, - void *native_ptr, uint32 size) +static bool +aot_table_traverse_gc_rootset(WASMModuleInstance *module_inst, void *heap) { - uint8 *addr = native_ptr; + AOTTableInstance **tables = (AOTTableInstance **)module_inst->tables; + AOTTableInstance *table; + uint32 table_count = module_inst->table_count, i, j; + WASMObjectRef gc_obj, *table_elems; - /* integer overflow check */ - if (addr + size < addr) { - goto fail; + for (i = 0; i < table_count; i++) { + table = tables[i]; + table_elems = (WASMObjectRef *)table->elems; + for (j = 0; j < table->cur_size; j++) { + gc_obj = table_elems[j]; + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } } - if (((uint8*)module_inst->memory_data.ptr <= addr - && addr + size <= (uint8*)module_inst->memory_data_end.ptr) - || ((uint8*)module_inst->heap_data.ptr <= addr - && addr + size <= (uint8*)module_inst->heap_data_end.ptr) - ) - return true; - -fail: - aot_set_exception(module_inst, "out of bounds memory access"); - return false; + return true; } -void * -aot_addr_app_to_native(AOTModuleInstance *module_inst, int32 app_offset) +static bool +local_object_refs_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) { - if (0 <= app_offset && app_offset < module_inst->heap_base_offset) - return (uint8*)module_inst->memory_data.ptr + app_offset; + WASMLocalObjectRef *r; + WASMObjectRef gc_obj; - if (module_inst->heap_base_offset < app_offset - && app_offset < module_inst->heap_base_offset - + (int32)module_inst->heap_data_size) - return (uint8*)module_inst->heap_data.ptr - + (app_offset - module_inst->heap_base_offset); - - return NULL; + for (r = exec_env->cur_local_object_ref; r; r = r->prev) { + gc_obj = r->val; + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } + return true; } -int32 -aot_addr_native_to_app(AOTModuleInstance *module_inst, void *native_ptr) +static bool +aot_frame_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) { - if ((uint8*)module_inst->memory_data.ptr <= (uint8*)native_ptr - && (uint8*)native_ptr < (uint8*)module_inst->memory_data_end.ptr) - return (int32)((uint8*)native_ptr - (uint8*)module_inst->memory_data.ptr); + AOTFrame *frame; + AOTModule *module; + LocalRefFlag frame_local_flags; + WASMObjectRef gc_obj; + uint32 i, local_ref_flag_cell_num; - if ((uint8*)module_inst->heap_data.ptr <= (uint8*)native_ptr - && (uint8*)native_ptr < (uint8*)module_inst->heap_data_end.ptr) - return (int32)(module_inst->heap_base_offset - + ((uint8*)native_ptr - (uint8*)module_inst->heap_data.ptr)); + module = (AOTModule *)wasm_exec_env_get_module(exec_env); + frame = (AOTFrame *)wasm_exec_env_get_cur_frame(exec_env); + for (; frame; frame = frame->prev_frame) { + /* local ref flags */ + frame_local_flags = module->func_local_ref_flags[frame->func_index]; + local_ref_flag_cell_num = frame_local_flags.local_ref_flag_cell_num; + for (i = 0; i < local_ref_flag_cell_num; i++) { + if (frame_local_flags.local_ref_flags[i]) { + gc_obj = GET_REF_FROM_ADDR(frame->lp + i); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) { + return false; + } + } +#if UINTPTR_MAX == UINT64_MAX + bh_assert(frame_local_flags.local_ref_flags[i + 1]); + i++; +#endif + } + } - return 0; + /* stack ref flags */ + uint8 *frame_ref = frame->frame_ref; + for (i = local_ref_flag_cell_num; i < (uint32)(frame->sp - frame->lp); + i++) { + if (frame_ref[i]) { + gc_obj = GET_REF_FROM_ADDR(frame->lp + i); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) { + return false; + } + } +#if UINTPTR_MAX == UINT64_MAX + bh_assert(frame_ref[i + 1]); + i++; +#endif + } + } + } + return true; } bool -aot_get_app_addr_range(AOTModuleInstance *module_inst, - int32 app_offset, - int32 *p_app_start_offset, - int32 *p_app_end_offset) +aot_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) { - int32 app_start_offset, app_end_offset; + AOTModuleInstance *module_inst = (AOTModuleInstance *)exec_env->module_inst; + bool ret; - if (0 <= app_offset && app_offset < (int32)module_inst->memory_data_size) { - app_start_offset = 0; - app_end_offset = (int32)module_inst->memory_data_size; - } - else if (module_inst->heap_base_offset < app_offset - && app_offset < module_inst->heap_base_offset - + (int32)module_inst->heap_data_size) { - app_start_offset = module_inst->heap_base_offset; - app_end_offset = module_inst->heap_base_offset - + (int32)module_inst->heap_data_size; - } - else - return false; + ret = aot_global_traverse_gc_rootset(module_inst, heap); + if (!ret) + return ret; + + ret = aot_table_traverse_gc_rootset(module_inst, heap); + if (!ret) + return ret; + + ret = local_object_refs_traverse_gc_rootset(exec_env, heap); + if (!ret) + return ret; + + ret = aot_frame_traverse_gc_rootset(exec_env, heap); + if (!ret) + return ret; - if (p_app_start_offset) - *p_app_start_offset = app_start_offset; - if (p_app_end_offset) - *p_app_end_offset = app_end_offset; return true; } +#endif /* end of WASM_ENABLE_GC != 0 */ -bool -aot_get_native_addr_range(AOTModuleInstance *module_inst, - uint8 *native_ptr, - uint8 **p_native_start_addr, - uint8 **p_native_end_addr) +char * +aot_const_str_set_insert(const uint8 *str, int32 len, AOTModule *module, +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + bool is_vram_word_align, +#endif + char *error_buf, uint32 error_buf_size) { - uint8 *native_start_addr, *native_end_addr; + HashMap *set = module->const_str_set; + char *c_str, *value; + + /* Create const string set if it isn't created */ + if (!set + && !(set = module->const_str_set = bh_hash_map_create( + 32, false, (HashFunc)wasm_string_hash, + (KeyEqualFunc)wasm_string_equal, NULL, wasm_runtime_free))) { + set_error_buf(error_buf, error_buf_size, + "create const string set failed"); + return NULL; + } - if ((uint8*)module_inst->memory_data.ptr <= (uint8*)native_ptr - && (uint8*)native_ptr < (uint8*)module_inst->memory_data_end.ptr) { - native_start_addr = (uint8*)module_inst->memory_data.ptr; - native_end_addr = (uint8*)module_inst->memory_data_end.ptr; + /* Lookup const string set, use the string if found */ + if (!(c_str = runtime_malloc((uint32)len, error_buf, error_buf_size))) { + return NULL; } - else if ((uint8*)module_inst->heap_data.ptr <= (uint8*)native_ptr - && (uint8*)native_ptr < (uint8*)module_inst->heap_data_end.ptr) { - native_start_addr = (uint8*)module_inst->heap_data.ptr; - native_end_addr = (uint8*)module_inst->heap_data_end.ptr; +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + if (is_vram_word_align) { + bh_memcpy_wa(c_str, (uint32)len, str, (uint32)len); } else - return false; +#endif + { + bh_memcpy_s(c_str, len, str, (uint32)len); + } - if (p_native_start_addr) - *p_native_start_addr = native_start_addr; - if (p_native_end_addr) - *p_native_end_addr = native_end_addr; - return true; + if ((value = bh_hash_map_find(set, c_str))) { + wasm_runtime_free(c_str); + return value; + } + + if (!bh_hash_map_insert(set, c_str, c_str)) { + set_error_buf(error_buf, error_buf_size, + "insert string to hash map failed"); + wasm_runtime_free(c_str); + return NULL; + } + + return c_str; } -bool -aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count) +#if WASM_ENABLE_DYNAMIC_AOT_DEBUG != 0 +AOTModule *g_dynamic_aot_module = NULL; + +void __attribute__((noinline)) __enable_dynamic_aot_debug(void) { - uint8 *mem_data_old = module_inst->memory_data.ptr, *mem_data_new; - uint32 num_bytes_per_page = - ((AOTModule*)module_inst->aot_module.ptr)->num_bytes_per_page; - uint32 cur_page_count = module_inst->mem_cur_page_count; - uint32 max_page_count = module_inst->mem_max_page_count; - uint32 total_page_count = cur_page_count + inc_page_count; - uint32 old_size = num_bytes_per_page * cur_page_count; - uint64 total_size = (uint64)num_bytes_per_page * total_page_count; - - if (inc_page_count <= 0) - /* No need to enlarge memory */ - return true; + /* empty implementation. */ +} - if (total_page_count < cur_page_count /* integer overflow */ - || total_page_count > max_page_count) { - aot_set_exception(module_inst, "fail to enlarge memory."); - return false; - } +void (*__enable_dynamic_aot_debug_ptr)(void) + __attribute__((visibility("default"))) = __enable_dynamic_aot_debug; +#endif - if (total_size >= UINT32_MAX - || !(mem_data_new = wasm_malloc((uint32)total_size))) { - aot_set_exception(module_inst, "fail to enlarge memory."); +bool +aot_set_module_name(AOTModule *module, const char *name, char *error_buf, + uint32_t error_buf_size) +{ + if (!name) return false; - } - memcpy(mem_data_new, mem_data_old, old_size); - memset(mem_data_new + old_size, 0, (uint32)total_size - old_size); - module_inst->mem_cur_page_count = total_page_count; - module_inst->memory_data_size = (uint32)total_size; - module_inst->memory_data.ptr = mem_data_new; - module_inst->memory_data_end.ptr = mem_data_new + (uint32)total_size; + module->name = aot_const_str_set_insert((const uint8 *)name, + (uint32)(strlen(name) + 1), module, +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + false, +#endif + error_buf, error_buf_size); +#if WASM_ENABLE_DYNAMIC_AOT_DEBUG != 0 + /* export g_dynamic_aot_module for dynamic aot debug */ + g_dynamic_aot_module = module; + /* trigger breakpoint __enable_dynamic_aot_debug */ + (*__enable_dynamic_aot_debug_ptr)(); +#endif + return module->name != NULL; +} - wasm_free(mem_data_old); - return true; +const char * +aot_get_module_name(AOTModule *module) +{ + return module->name; } bool -aot_is_wasm_type_equal(AOTModuleInstance *module_inst, - uint32 type1_idx, uint32 type2_idx) +aot_resolve_symbols(AOTModule *module) { - WASMType *type1, *type2; - AOTModule *module = (AOTModule*)module_inst->aot_module.ptr; + bool ret = true; + uint32 idx; + for (idx = 0; idx < module->import_func_count; ++idx) { + AOTImportFunc *aot_import_func = &module->import_funcs[idx]; + if (!aot_import_func->func_ptr_linked) { + if (!aot_resolve_import_func(module, aot_import_func)) { + LOG_WARNING("Failed to link function (%s, %s)", + aot_import_func->module_name, + aot_import_func->func_name); + ret = false; + } + } + } + return ret; +} - if (type1_idx >= module->func_type_count - || type2_idx >= module->func_type_count) { - aot_set_exception(module_inst, "type index out of bounds"); - return false; +#if WASM_ENABLE_MULTI_MODULE != 0 +static void * +aot_resolve_function(const AOTModule *module, const char *function_name, + const AOTFuncType *expected_function_type, char *error_buf, + uint32 error_buf_size); + +static void * +aot_resolve_function_ex(const char *module_name, const char *function_name, + const AOTFuncType *expected_function_type, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_AoT) { + LOG_DEBUG("can not find a module named %s for function %s", module_name, + function_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; } + return aot_resolve_function((AOTModule *)module_reg, function_name, + expected_function_type, error_buf, + error_buf_size); +} - if (type1_idx == type2_idx) - return true; +static void * +aot_resolve_function(const AOTModule *module, const char *function_name, + const AOTFuncType *expected_function_type, char *error_buf, + uint32 error_buf_size) +{ + void *function = NULL; + AOTExport *export = NULL; + AOTFuncType *target_function_type = NULL; + + export = loader_find_export((WASMModuleCommon *)module, module->name, + function_name, EXPORT_KIND_FUNC, error_buf, + error_buf_size); + if (!export) { + return NULL; + } - type1 = module->func_types[type1_idx]; - type2 = module->func_types[type2_idx]; + /* resolve function type and function */ + if (export->index < module->import_func_count) { + target_function_type = module->import_funcs[export->index].func_type; + function = module->import_funcs[export->index].func_ptr_linked; + } + else { + target_function_type = + (AOTFuncType *)module + ->types[module->func_type_indexes[export->index + - module->import_func_count]]; + function = + (module->func_ptrs[export->index - module->import_func_count]); + } + /* check function type */ + if (!wasm_type_equal((WASMType *)expected_function_type, + (WASMType *)target_function_type, module->types, + module->type_count)) { + LOG_DEBUG("%s.%s failed the type check", module->name, function_name); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return function; +} +#endif /* end of WASM_ENABLE_MULTI_MODULE */ - return wasm_type_equal(type1, type2); +bool +aot_resolve_import_func(AOTModule *module, AOTImportFunc *import_func) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + char error_buf[128]; + AOTModule *sub_module = NULL; +#endif + import_func->func_ptr_linked = wasm_native_resolve_symbol( + import_func->module_name, import_func->func_name, + import_func->func_type, &import_func->signature, + &import_func->attachment, &import_func->call_conv_raw); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!import_func->func_ptr_linked) { + if (!wasm_runtime_is_built_in_module(import_func->module_name)) { + sub_module = (AOTModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)module, import_func->module_name, error_buf, + sizeof(error_buf)); + if (!sub_module) { + LOG_WARNING("Failed to load sub module: %s", error_buf); + } + if (!sub_module) + import_func->func_ptr_linked = aot_resolve_function_ex( + import_func->module_name, import_func->func_name, + import_func->func_type, error_buf, sizeof(error_buf)); + else + import_func->func_ptr_linked = aot_resolve_function( + sub_module, import_func->func_name, import_func->func_type, + error_buf, sizeof(error_buf)); + if (!import_func->func_ptr_linked) { + LOG_WARNING("Failed to link function: %s", error_buf); + } + } + } +#endif + return import_func->func_ptr_linked != NULL; } diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 5a941321ad..687c75e142 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -10,56 +10,76 @@ #include "../common/wasm_runtime_common.h" #include "../interpreter/wasm_runtime.h" #include "../compilation/aot.h" -#if WASM_ENABLE_JIT != 0 -#include "../compilation/aot_llvm.h" +#if WASM_ENABLE_GC != 0 +#include "gc_export.h" #endif #ifdef __cplusplus extern "C" { #endif -#define AOT_MAGIC_NUMBER 0x746f6100 -#define AOT_CURRENT_VERSION 1 - -typedef enum AOTExceptionID { - EXCE_UNREACHABLE = 0, - EXCE_OUT_OF_MEMORY, - EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, - EXCE_INTEGER_OVERFLOW, - EXCE_INTEGER_DIVIDE_BY_ZERO, - EXCE_INVALID_CONVERSION_TO_INTEGER, - EXCE_INVALID_FUNCTION_TYPE_INDEX, - EXCE_INVALID_FUNCTION_INDEX, - EXCE_UNDEFINED_ELEMENT, - EXCE_UNINITIALIZED_ELEMENT, - EXCE_CALL_UNLINKED_IMPORT_FUNC, - EXCE_NUM, -} AOTExceptionID; +/* Wasm feature supported, mainly used by AOTTargetInfo now */ +#define WASM_FEATURE_SIMD_128BIT (1 << 0) +#define WASM_FEATURE_BULK_MEMORY (1 << 1) +#define WASM_FEATURE_MULTI_THREAD (1 << 2) +#define WASM_FEATURE_REF_TYPES (1 << 3) +#define WASM_FEATURE_GARBAGE_COLLECTION (1 << 4) +#define WASM_FEATURE_EXCEPTION_HANDLING (1 << 5) +#define WASM_FEATURE_TINY_STACK_FRAME (1 << 6) +#define WASM_FEATURE_MULTI_MEMORY (1 << 7) +#define WASM_FEATURE_DYNAMIC_LINKING (1 << 8) +#define WASM_FEATURE_COMPONENT_MODEL (1 << 9) +#define WASM_FEATURE_RELAXED_SIMD (1 << 10) +#define WASM_FEATURE_FLEXIBLE_VECTORS (1 << 11) +/* Stack frame is created at the beginning of the function, + * and not at the beginning of each function call */ +#define WASM_FEATURE_FRAME_PER_FUNCTION (1 << 12) +#define WASM_FEATURE_FRAME_NO_FUNC_IDX (1 << 13) typedef enum AOTSectionType { AOT_SECTION_TYPE_TARGET_INFO = 0, - AOT_SECTION_TYPE_INIT_DATA, - AOT_SECTION_TYPE_TEXT, - AOT_SECTION_TYPE_FUNCTION, - AOT_SECTION_TYPE_EXPORT, - AOT_SECTION_TYPE_RELOCATION, - AOT_SECTION_TYPE_SIGANATURE + AOT_SECTION_TYPE_INIT_DATA = 1, + AOT_SECTION_TYPE_TEXT = 2, + AOT_SECTION_TYPE_FUNCTION = 3, + AOT_SECTION_TYPE_EXPORT = 4, + AOT_SECTION_TYPE_RELOCATION = 5, + /* + * Note: We haven't had anything to use AOT_SECTION_TYPE_SIGNATURE. + * It's just reserved for possible module signing features. + */ + AOT_SECTION_TYPE_SIGNATURE = 6, + AOT_SECTION_TYPE_CUSTOM = 100, } AOTSectionType; +typedef enum AOTCustomSectionType { + AOT_CUSTOM_SECTION_RAW = 0, + AOT_CUSTOM_SECTION_NATIVE_SYMBOL = 1, + AOT_CUSTOM_SECTION_ACCESS_CONTROL = 2, + AOT_CUSTOM_SECTION_NAME = 3, + AOT_CUSTOM_SECTION_STRING_LITERAL = 4, +} AOTCustomSectionType; + typedef struct AOTObjectDataSection { char *name; uint8 *data; uint32 size; +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + bool is_name_allocated; + bool is_data_allocated; +#endif } AOTObjectDataSection; /* Relocation info */ typedef struct AOTRelocation { uint64 relocation_offset; - uint64 relocation_addend; + int64 relocation_addend; uint32 relocation_type; char *symbol_name; /* index in the symbol offset field */ uint32 symbol_index; +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + bool is_symbol_name_allocated; +#endif } AOTRelocation; /* Relocation Group */ @@ -69,28 +89,115 @@ typedef struct AOTRelocationGroup { uint32 name_index; uint32 relocation_count; AOTRelocation *relocations; +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + bool is_section_name_allocated; +#endif } AOTRelocationGroup; +/* AOT function instance */ +typedef struct AOTFunctionInstance { + char *func_name; + uint32 func_index; + bool is_import_func; + union { + struct { + AOTFuncType *func_type; + /* function pointer linked */ + void *func_ptr; + } func; + AOTImportFunc *func_import; + } u; +} AOTFunctionInstance; + +/* Map of a function index to the element ith in + the export functions array */ +typedef struct ExportFuncMap { + uint32 func_idx; + uint32 export_idx; +} ExportFuncMap; + +typedef struct AOTModuleInstanceExtra { + DefPointer(const uint32 *, stack_sizes); + /* + * Adjusted shared heap based addr to simple the calculation + * in the aot code. The value is: + * shared_heap->base_addr - shared_heap->start_off + */ + DefPointer(uint8 *, shared_heap_base_addr_adj); + MemBound shared_heap_start_off; + MemBound shared_heap_end_off; + DefPointer(WASMSharedHeap *, shared_heap); + + WASMModuleInstanceExtraCommon common; + + /** + * maps of func indexes to export func indexes, which + * is sorted by func index for a quick lookup and is + * created only when first time used. + */ + ExportFuncMap *export_func_maps; + AOTFunctionInstance **functions; + uint32 function_count; +#if WASM_ENABLE_MULTI_MODULE != 0 + bh_list sub_module_inst_list_head; + bh_list *sub_module_inst_list; + WASMModuleInstanceCommon **import_func_module_insts; +#endif + +} AOTModuleInstanceExtra; + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +typedef struct GOTItem { + uint32 func_idx; + struct GOTItem *next; +} GOTItem, *GOTItemList; +#endif + +#if WASM_ENABLE_GC != 0 +typedef struct LocalRefFlag { + uint32 local_ref_flag_cell_num; + uint8 *local_ref_flags; +} LocalRefFlag; +#endif + typedef struct AOTModule { uint32 module_type; + /* the package version read from the AOT file */ + uint32 package_version; + + /* import memories */ + uint32 import_memory_count; + AOTImportMemory *import_memories; + /* memory info */ - uint32 num_bytes_per_page; - uint32 mem_init_page_count; - uint32 mem_max_page_count; + uint32 memory_count; + AOTMemory *memories; + + /* init data */ uint32 mem_init_data_count; AOTMemInitData **mem_init_data_list; - /* table info */ - uint32 table_size; + /* native symbol */ + void **native_symbol_list; + + /* import tables */ + uint32 import_table_count; + AOTImportTable *import_tables; + + /* tables */ + uint32 table_count; + AOTTable *tables; + + /* table init data info */ uint32 table_init_data_count; AOTTableInitData **table_init_data_list; - /* function type info */ - uint32 func_type_count; - AOTFuncType **func_types; + /* type info */ + uint32 type_count; + AOTType **types; - /* import global varaible info */ + /* import global variable info */ uint32 import_global_count; AOTImportGlobal *import_globals; @@ -107,111 +214,160 @@ typedef struct AOTModule { /* function info */ uint32 func_count; - /* point to AOTed/JITed functions */ + /* func pointers of AOTed (un-imported) functions */ void **func_ptrs; - /* function type indexes */ + /* func type indexes of AOTed (un-imported) functions */ uint32 *func_type_indexes; +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* max local cell nums of AOTed (un-imported) functions */ + uint32 *max_local_cell_nums; + /* max stack cell nums of AOTed (un-imported) functions */ + uint32 *max_stack_cell_nums; +#endif - /* export function info */ - uint32 export_func_count; - AOTExportFunc *export_funcs; +#if WASM_ENABLE_GC != 0 + /* params + locals ref flags of (both import and AOTed) functions */ + LocalRefFlag *func_local_ref_flags; +#endif + + /* export info */ + uint32 export_count; + AOTExport *exports; /* start function index, -1 denotes no start function */ uint32 start_func_index; - /* start function, point to AOTed/JITed function */ + /* start function, point to AOTed function */ void *start_function; - /* AOTed code, NULL for JIT mode */ + uint32 malloc_func_index; + uint32 free_func_index; + uint32 retain_func_index; + + /* AOTed code */ void *code; uint32 code_size; + /* literal for AOTed code */ + uint8 *literal; + uint32 literal_size; + +#if defined(BH_PLATFORM_WINDOWS) + /* extra plt data area for __ymm, __xmm and __real constants + in Windows platform */ + uint8 *extra_plt_data; + uint32 extra_plt_data_size; + uint32 ymm_plt_count; + uint32 xmm_plt_count; + uint32 real_plt_count; + uint32 float_plt_count; +#endif + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + uint32 got_item_count; + GOTItemList got_item_list; + GOTItemList got_item_list_end; + void **got_func_ptrs; +#endif + /* data sections in AOT object file, including .data, .rodata - * and .rodata.cstN. NULL for JIT mode. */ + and .rodata.cstN. */ AOTObjectDataSection *data_sections; uint32 data_section_count; /* constant string set */ HashMap *const_str_set; - uint32 llvm_aux_data_end; - uint32 llvm_aux_stack_bottom; - uint32 llvm_aux_stack_size; - uint32 llvm_aux_stack_global_index; + /* the index of auxiliary __data_end global, + -1 means unexported */ + uint32 aux_data_end_global_index; + /* auxiliary __data_end exported by wasm app */ + uint64 aux_data_end; + + /* the index of auxiliary __heap_base global, + -1 means unexported */ + uint32 aux_heap_base_global_index; + /* auxiliary __heap_base exported by wasm app */ + uint64 aux_heap_base; + + /* the index of auxiliary stack top global, + -1 means unexported */ + uint32 aux_stack_top_global_index; + /* auxiliary stack bottom resolved */ + uint64 aux_stack_bottom; + /* auxiliary stack size resolved */ + uint32 aux_stack_size; + + /* is indirect mode or not */ + bool is_indirect_mode; - /* is jit mode or not */ - bool is_jit_mode; +#if WASM_ENABLE_LIBC_WASI != 0 + WASIArguments wasi_args; + bool import_wasi_api; +#endif -#if WASM_ENABLE_JIT - WASMModule *wasm_module; - AOTCompContext *comp_ctx; - AOTCompData *comp_data; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* TODO: add mutex for mutli-thread? */ + bh_list import_module_list_head; + bh_list *import_module_list; #endif -#if WASM_ENABLE_LIBC_WASI != 0 - WASIArguments wasi_args; - bool is_wasi_module; +#if WASM_ENABLE_GC != 0 + /* Ref types hash set */ + HashMap *ref_type_set; + struct WASMRttType **rtt_types; + korp_mutex rtt_type_lock; +#if WASM_ENABLE_STRINGREF != 0 + /* special rtts for stringref types + - stringref + - stringview_wtf8 + - stringview_wtf16 + - stringview_iter + */ + struct WASMRttType *stringref_rtts[4]; + uint32 string_literal_count; + uint32 *string_literal_lengths; + const uint8 **string_literal_ptrs; +#endif #endif -} AOTModule; -typedef union { - uint64 _make_it_8_bytes_; - void *ptr; -} AOTPointer; +#if WASM_ENABLE_DEBUG_AOT != 0 + void *elf_hdr; + uint32 elf_size; +#endif +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + const char **aux_func_names; + uint32 *aux_func_indexes; + uint32 aux_func_name_count; +#endif +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + WASMCustomSection *custom_section_list; +#endif -typedef struct AOTModuleInstance { - uint32 module_type; + /* user defined name */ + char *name; - /* memory space info */ - uint32 mem_cur_page_count; - uint32 mem_max_page_count; - uint32 memory_data_size; - AOTPointer memory_data; - AOTPointer memory_data_end; - - /* heap space info */ - int32 heap_base_offset; - uint32 heap_data_size; - AOTPointer heap_data; - AOTPointer heap_data_end; - AOTPointer heap_handle; - - /* global and table info */ - uint32 global_data_size; - uint32 table_size; - AOTPointer global_data; - AOTPointer table_data; - - /* funciton pointer array */ - AOTPointer func_ptrs; - /* function type indexes */ - AOTPointer func_type_indexes; - - /* The exception buffer for current thread. */ - char cur_exception[128]; - /* The custom data that can be set/get by - * wasm_runtime_set_custom_data/wasm_runtime_get_custom_data */ - AOTPointer custom_data; - /* The AOT module */ - AOTPointer aot_module; - /* WASI context */ - AOTPointer wasi_ctx; - - /* others */ - int32 temp_ret; - uint32 llvm_stack; - int32 DYNAMICTOP_PTR_offset; - uint32 default_wasm_stack_size; - - /* reserved */ - uint32 reserved[16]; + /* Whether the underlying wasm binary buffer can be freed */ + bool is_binary_freeable; - union { - uint64 _make_it_8_byte_aligned_; - uint8 bytes[1]; - } global_table_heap_data; -} AOTModuleInstance; + /* `.data` sections merged into one mmaped to reduce the tlb cache miss */ + uint8 *merged_data_sections; + uint32 merged_data_sections_size; + /* `.data` and `.text` sections merged into one large mmaped section */ + uint8 *merged_data_text_sections; + uint32 merged_data_text_sections_size; + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + uint32 feature_flags; +#endif +} AOTModule; -typedef AOTExportFunc AOTFunctionInstance; +#define AOTMemoryInstance WASMMemoryInstance +#define AOTTableInstance WASMTableInstance +#define AOTModuleInstance WASMModuleInstance + +#if WASM_ENABLE_MULTI_MODULE != 0 +#define AOTSubModInstNode WASMSubModInstNode +#endif /* Target info, read from ELF header of object file */ typedef struct AOTTargetInfo { @@ -227,12 +383,116 @@ typedef struct AOTTargetInfo { uint32 e_version; /* Processor-specific flags */ uint32 e_flags; + /* Specify wasm features supported */ + uint64 feature_flags; /* Reserved */ - uint32 reserved; + uint64 reserved; /* Arch name */ char arch[16]; } AOTTargetInfo; +typedef struct AOTFuncPerfProfInfo { + /* total execution time */ + uint64 total_exec_time; + /* total execution count */ + uint32 total_exec_cnt; + /* children execution time */ + uint64 children_exec_time; +} AOTFuncPerfProfInfo; + +/* AOT auxiliary call stack */ +typedef struct AOTFrame { + /* The frame of the caller which is calling current function */ + struct AOTFrame *prev_frame; + + /* The non-imported function index of current function */ + uintptr_t func_index; + + /* Used when performance profiling is enabled */ + uintptr_t time_started; + + /* Used when performance profiling is enabled */ + AOTFuncPerfProfInfo *func_perf_prof_info; + + /* Instruction pointer: offset to the bytecode array */ + uintptr_t ip_offset; + + /* Operand stack top pointer of the current frame */ + uint32 *sp; + + /* Frame ref flags (GC only) */ + uint8 *frame_ref; + + /** + * Frame data, the layout is: + * local area: parameters and local variables + * stack area: wasm operand stack + * frame ref flags (GC only): + * whether each cell in local and stack area is a GC obj + * currently local's ref flags are stored in AOTModule, + * here we only reserve the padding bytes + */ + uint32 lp[1]; +} AOTFrame; + +#if WASM_ENABLE_STATIC_PGO != 0 +/* The bitmaps fields in LLVMProfileRawHeader, LLVMProfileData, + * LLVMProfileData_64 all dummy fields, it's used in MC/DC code coverage + * instead of PGO. See https://llvm.org/docs/InstrProfileFormat.html#bitmap */ +typedef struct LLVMProfileRawHeader { + uint64 magic; + uint64 version; + uint64 binary_ids_size; + uint64 num_prof_data; + uint64 padding_bytes_before_counters; + uint64 num_prof_counters; + uint64 padding_bytes_after_counters; + uint64 num_prof_bitmaps; + uint64 padding_bytes_after_bitmaps; + uint64 names_size; + uint64 counters_delta; + uint64 bitmap_delta; + uint64 names_delta; + uint64 value_kind_last; +} LLVMProfileRawHeader; + +typedef struct ValueProfNode { + uint64 value; + uint64 count; + struct ValueProfNode *next; +} ValueProfNode; + +/* The profiling data of data sections created by aot compiler and + used when profiling, the width of pointer can be 8 bytes (64-bit) + or 4 bytes (32-bit) */ +typedef struct LLVMProfileData { + uint64 func_md5; + uint64 func_hash; + uint64 offset_counters; + uint64 offset_bitmaps; + uintptr_t func_ptr; + ValueProfNode **values; + uint32 num_counters; + uint16 num_value_sites[2]; + uint32 num_bitmaps; +} LLVMProfileData; + +/* The profiling data for writing to the output file, the width of + pointer is 8 bytes suppose we always use wamrc and llvm-profdata + with 64-bit mode */ +typedef struct LLVMProfileData_64 { + uint64 func_md5; + uint64 func_hash; + uint64 offset_counters; + uint64 offset_bitmaps; + uint64 func_ptr; + uint64 values; + uint32 num_counters; + uint16 num_value_sites[2]; + uint32 num_bitmaps; +} LLVMProfileData_64; +#endif /* end of WASM_ENABLE_STATIC_PGO != 0 */ + /** * Load a AOT module from aot file buffer * @param buf the byte buffer which contains the AOT file data @@ -242,8 +502,8 @@ typedef struct AOTTargetInfo { * * @return return AOT module loaded, NULL if failed */ -AOTModule* -aot_load_from_aot_file(const uint8 *buf, uint32 size, +AOTModule * +aot_load_from_aot_file(const uint8 *buf, uint32 size, const LoadArgs *args, char *error_buf, uint32 error_buf_size); /** @@ -255,24 +515,9 @@ aot_load_from_aot_file(const uint8 *buf, uint32 size, * * @return return AOT module loaded, NULL if failed */ -AOTModule* -aot_load_from_sections(AOTSection *section_list, - char *error_buf, uint32 error_buf_size); - -#if WASM_ENABLE_JIT != 0 -/** - * Convert WASM module to AOT module - * - * @param wasm_module the WASM module to convert - * @param error_buf output of the error info - * @param error_buf_size the size of the error string - * - * @return return AOT module loaded, NULL if failed - */ -AOTModule* -aot_convert_wasm_module(WASMModule *wasm_module, - char *error_buf, uint32 error_buf_size); -#endif +AOTModule * +aot_load_from_sections(AOTSection *section_list, char *error_buf, + uint32 error_buf_size); /** * Unload a AOT module. @@ -282,45 +527,82 @@ aot_convert_wasm_module(WASMModule *wasm_module, void aot_unload(AOTModule *module); +/** + * Resolve symbols for an AOT module + */ +bool +aot_resolve_symbols(AOTModule *module); + +/** + * Helper function to resolve a single function + */ +bool +aot_resolve_import_func(AOTModule *module, AOTImportFunc *import_func); + /** * Instantiate a AOT module. * * @param module the AOT module to instantiate - * @param heap_size the default heap size of the module instance, a heap will - * be created besides the app memory space. Both wasm app and native - * function can allocate memory from the heap. If heap_size is 0, the - * default heap size will be used. + * @param parent the parent module instance + * @param args the instantiation parameters * @param error_buf buffer to output the error info if failed * @param error_buf_size the size of the error buffer * * @return return the instantiated AOT module instance, NULL if failed */ -AOTModuleInstance* -aot_instantiate(AOTModule *module, - uint32 stack_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size); +AOTModuleInstance * +aot_instantiate(AOTModule *module, AOTModuleInstance *parent, + WASMExecEnv *exec_env_main, + const struct InstantiationArgs2 *args, char *error_buf, + uint32 error_buf_size); /** * Deinstantiate a AOT module instance, destroy the resources. * * @param module_inst the AOT module instance to destroy + * @param is_sub_inst the flag of sub instance */ void -aot_deinstantiate(AOTModuleInstance *module_inst); +aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst); /** * Lookup an exported function in the AOT module instance. * * @param module_inst the module instance * @param name the name of the function - * @param signature the signature of the function, use "i32"/"i64"/"f32"/"f64" - * to represent the type of i32/i64/f32/f64, e.g. "(i32i64)" "(i32)f32" * * @return the function instance found */ -AOTFunctionInstance* -aot_lookup_function(const AOTModuleInstance *module_inst, - const char *name, const char *signature); +AOTFunctionInstance * +aot_lookup_function(const AOTModuleInstance *module_inst, const char *name); + +/** + * Lookup an exported function in the AOT module instance with + * the function index. + */ +AOTFunctionInstance * +aot_lookup_function_with_idx(AOTModuleInstance *module_inst, uint32 func_idx); + +AOTMemoryInstance * +aot_lookup_memory(AOTModuleInstance *module_inst, char const *name); + +AOTMemoryInstance * +aot_get_default_memory(AOTModuleInstance *module_inst); + +AOTMemoryInstance * +aot_get_memory_with_idx(AOTModuleInstance *module_inst, uint32 mem_idx); + +/** + * Get a function in the AOT module instance. + * + * @param module_inst the module instance + * @param func_idx the index of the function + * + * @return the function instance found + */ +AOTFunctionInstance * +aot_get_function_instance(AOTModuleInstance *module_inst, uint32 func_idx); + /** * Call the given AOT function of a AOT module instance with * arguments. @@ -337,14 +619,9 @@ aot_lookup_function(const AOTModuleInstance *module_inst, * the caller can call aot_get_exception to get exception info. */ bool -aot_call_function(WASMExecEnv *exec_env, - AOTFunctionInstance *function, +aot_call_function(WASMExecEnv *exec_env, AOTFunctionInstance *function, unsigned argc, uint32 argv[]); -bool -aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, - AOTFunctionInstance *function, - unsigned argc, uint32 argv[]); /** * Set AOT module instance exception with exception string * @@ -353,12 +630,10 @@ aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, * @param exception current exception string */ void -aot_set_exception(AOTModuleInstance *module_inst, - const char *exception); +aot_set_exception(AOTModuleInstance *module_inst, const char *exception); void -aot_set_exception_with_id(AOTModuleInstance *module_inst, - uint32 id); +aot_set_exception_with_id(AOTModuleInstance *module_inst, uint32 id); /** * Get exception info of the AOT module instance. @@ -367,76 +642,255 @@ aot_set_exception_with_id(AOTModuleInstance *module_inst, * * @return the exception string */ -const char* +const char * aot_get_exception(AOTModuleInstance *module_inst); /** - * Clear exception info of the AOT module instance. - * - * @param module_inst the AOT module instance + * @brief Copy exception in buffer passed as parameter. Thread-safe version of + * `aot_get_exception()` + * @note Buffer size must be no smaller than EXCEPTION_BUF_LEN + * @return true if exception found, false otherwise */ +bool +aot_copy_exception(AOTModuleInstance *module_inst, char *exception_buf); + +uint64 +aot_module_malloc_internal(AOTModuleInstance *module_inst, WASMExecEnv *env, + uint64 size, void **p_native_addr); + +uint64 +aot_module_realloc_internal(AOTModuleInstance *module_inst, WASMExecEnv *env, + uint64 ptr, uint64 size, void **p_native_addr); + void -aot_clear_exception(AOTModuleInstance *module_inst); +aot_module_free_internal(AOTModuleInstance *module_inst, WASMExecEnv *env, + uint64 ptr); -int32 -aot_module_malloc(AOTModuleInstance *module_inst, uint32 size); +uint64 +aot_module_malloc(AOTModuleInstance *module_inst, uint64 size, + void **p_native_addr); + +uint64 +aot_module_realloc(AOTModuleInstance *module_inst, uint64 ptr, uint64 size, + void **p_native_addr); void -aot_module_free(AOTModuleInstance *module_inst, int32 ptr); +aot_module_free(AOTModuleInstance *module_inst, uint64 ptr); -int32 -aot_module_dup_data(AOTModuleInstance *module_inst, - const char *src, uint32 size); +uint64 +aot_module_dup_data(AOTModuleInstance *module_inst, const char *src, + uint64 size); bool -aot_validate_app_addr(AOTModuleInstance *module_inst, - int32 app_offset, uint32 size); +aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count); +bool +aot_enlarge_memory_with_idx(AOTModuleInstance *module_inst, + uint32 inc_page_count, uint32 memidx); +/** + * Invoke native function from aot code + */ +bool +aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, + uint32 *argv); + +bool +aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx, + uint32 argc, uint32 *argv); + +/** + * Check whether the app address and the buf is inside the linear memory, + * and convert the app address into native address + */ bool -aot_validate_native_addr(AOTModuleInstance *module_inst, - void *native_ptr, uint32 size); +aot_check_app_addr_and_convert(AOTModuleInstance *module_inst, bool is_str, + uint64 app_buf_addr, uint64 app_buf_size, + void **p_native_addr); + +uint32 +aot_get_plt_table_size(void); + +void * +aot_memmove(void *dest, const void *src, size_t n); void * -aot_addr_app_to_native(AOTModuleInstance *module_inst, int32 app_offset); +aot_memset(void *s, int c, size_t n); + +double +aot_sqrt(double x); -int32 -aot_addr_native_to_app(AOTModuleInstance *module_inst, void *native_ptr); +float +aot_sqrtf(float x); +#if WASM_ENABLE_BULK_MEMORY != 0 bool -aot_get_app_addr_range(AOTModuleInstance *module_inst, - int32 app_offset, - int32 *p_app_start_offset, - int32 *p_app_end_offset); +aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, uint32 offset, + uint32 len, size_t dst); bool -aot_get_native_addr_range(AOTModuleInstance *module_inst, - uint8 *native_ptr, - uint8 **p_native_start_addr, - uint8 **p_native_end_addr); +aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index); +#endif +#if WASM_ENABLE_THREAD_MGR != 0 bool -aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count); +aot_set_aux_stack(WASMExecEnv *exec_env, uint64 start_offset, uint32 size); + +bool +aot_get_aux_stack(WASMExecEnv *exec_env, uint64 *start_offset, uint32 *size); +#endif + +void +aot_get_module_mem_consumption(const AOTModule *module, + WASMModuleMemConsumption *mem_conspn); + +void +aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, + WASMModuleInstMemConsumption *mem_conspn); + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +void +aot_drop_table_seg(AOTModuleInstance *module_inst, uint32 tbl_seg_idx); + +void +aot_table_init(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 tbl_seg_idx, uint32 length, uint32 src_offset, + uint32 dst_offset); + +void +aot_table_copy(AOTModuleInstance *module_inst, uint32 src_tbl_idx, + uint32 dst_tbl_idx, uint32 length, uint32 src_offset, + uint32 dst_offset); + +void +aot_table_fill(AOTModuleInstance *module_inst, uint32 tbl_idx, uint32 length, + table_elem_type_t val, uint32 data_offset); + +uint32 +aot_table_grow(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 inc_entries, table_elem_type_t init_val); +#endif + +bool +aot_alloc_frame(WASMExecEnv *exec_env, uint32 func_index); + +void +aot_free_frame(WASMExecEnv *exec_env); + +void +aot_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame); + +bool +aot_create_call_stack(struct WASMExecEnv *exec_env); + +#if WASM_ENABLE_COPY_CALL_STACK != 0 +uint32 +aot_copy_callstack(WASMExecEnv *exec_env, WASMCApiFrame *buffer, + const uint32 length, const uint32 skip_n, char *error_buf, + uint32_t error_buf_size); +#endif // WASM_ENABLE_COPY_CALL_STACK /** - * Compare whether two wasm types are equal according to the indexs + * @brief Dump wasm call stack or get the size * - * @param module_inst the AOT module instance - * @param type1_idx index of the first wasm type - * @param type2_idx index of the second wasm type + * @param exec_env the execution environment + * @param print whether to print to stdout or not + * @param buf buffer to store the dumped content + * @param len length of the buffer * - * @return true if equal, false otherwise + * @return when print is true, return the bytes printed out to stdout; when + * print is false and buf is NULL, return the size required to store the + * callstack content; when print is false and buf is not NULL, return the size + * dumped to the buffer, 0 means error and data in buf may be invalid */ -bool -aot_is_wasm_type_equal(AOTModuleInstance *module_inst, - uint32 type1_idx, uint32 type2_idx); +uint32 +aot_dump_call_stack(WASMExecEnv *exec_env, bool print, char *buf, uint32 len); + +void +aot_dump_perf_profiling(const AOTModuleInstance *module_inst); + +double +aot_summarize_wasm_execute_time(const AOTModuleInstance *inst); + +double +aot_get_wasm_func_exec_time(const AOTModuleInstance *inst, + const char *func_name); + +const uint8 * +aot_get_custom_section(const AOTModule *module, const char *name, uint32 *len); + +const void * +aot_get_data_section_addr(AOTModule *module, const char *section_name, + uint32 *p_data_size); + +#if WASM_ENABLE_STATIC_PGO != 0 +void +llvm_profile_instrument_target(uint64 target_value, void *data, + uint32 counter_idx); + +void +llvm_profile_instrument_memop(uint64 target_value, void *data, + uint32 counter_idx); + +uint32 +aot_get_pgo_prof_data_size(AOTModuleInstance *module_inst); uint32 -aot_get_plt_table_size(); +aot_dump_pgo_prof_data_to_buf(AOTModuleInstance *module_inst, char *buf, + uint32 len); + +void +aot_exchange_uint16(uint8 *p_data); + +void +aot_exchange_uint32(uint8 *p_data); + +void +aot_exchange_uint64(uint8 *p_data); +#endif /* end of WASM_ENABLE_STATIC_PGO != 0 */ + +#if WASM_ENABLE_GC != 0 +void * +aot_create_func_obj(AOTModuleInstance *module_inst, uint32 func_idx, + bool throw_exce, char *error_buf, uint32 error_buf_size); + +bool +aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj, + uint32 type_index); + +/* Whether func type1 is one of super types of func type2 */ +bool +aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1, + uint32 type_idx2); + +WASMRttTypeRef +aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index); + +bool +aot_array_init_with_data(AOTModuleInstance *module_inst, uint32 seg_index, + uint32 data_seg_offset, WASMArrayObjectRef array_obj, + uint32 elem_size, uint32 array_len); + +bool +aot_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap); +#endif /* end of WASM_ENABLE_GC != 0 */ + +char * +aot_const_str_set_insert(const uint8 *str, int32 len, AOTModule *module, +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + bool is_vram_word_align, +#endif + char *error_buf, uint32 error_buf_size); + +bool +aot_set_module_name(AOTModule *module, const char *name, char *error_buf, + uint32_t error_buf_size); + +const char * +aot_get_module_name(AOTModule *module); #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_RUNTIME_H_ */ - diff --git a/core/iwasm/aot/aot_validator.c b/core/iwasm/aot/aot_validator.c new file mode 100644 index 0000000000..58757f767f --- /dev/null +++ b/core/iwasm/aot/aot_validator.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_validator.h" + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "AOT module load failed: from validator. %s", string); + } +} + +static bool +aot_memory_info_validate(const AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + if (module->import_memory_count > 0) { + set_error_buf(error_buf, error_buf_size, + "import memory is not supported"); + return false; + } + + if (module->memory_count < 1) { + set_error_buf(error_buf, error_buf_size, + "there should be >=1 memory in one aot module"); + return false; + } + + return true; +} + +bool +aot_module_validate(const AOTModule *module, char *error_buf, + uint32 error_buf_size) +{ + if (!aot_memory_info_validate(module, error_buf, error_buf_size)) { + return false; + } + + return true; +} diff --git a/core/iwasm/aot/aot_validator.h b/core/iwasm/aot/aot_validator.h new file mode 100644 index 0000000000..dd8f0ecb5e --- /dev/null +++ b/core/iwasm/aot/aot_validator.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2025 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_VALIDATOR_H_ +#define _AOT_VALIDATOR_H_ + +#include "aot_runtime.h" + +bool +aot_module_validate(const AOTModule *module, char *error_buf, + uint32 error_buf_size); + +#endif /* _AOT_VALIDATOR_H_ */ diff --git a/core/iwasm/aot/arch/aot_reloc_aarch64.c b/core/iwasm/aot/arch/aot_reloc_aarch64.c new file mode 100644 index 0000000000..e42d2cd8e8 --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_aarch64.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_AARCH64_MOVW_UABS_G0 263 +#define R_AARCH64_MOVW_UABS_G0_NC 264 +#define R_AARCH64_MOVW_UABS_G1 265 +#define R_AARCH64_MOVW_UABS_G1_NC 266 +#define R_AARCH64_MOVW_UABS_G2 267 +#define R_AARCH64_MOVW_UABS_G2_NC 268 +#define R_AARCH64_MOVW_UABS_G3 269 + +#define R_AARCH64_MOVW_SABS_G0 270 +#define R_AARCH64_MOVW_SABS_G1 271 +#define R_AARCH64_MOVW_SABS_G2 272 + +#define R_AARCH64_ADR_PREL_LO19 273 +#define R_AARCH64_ADR_PREL_LO21 274 +#define R_AARCH64_ADR_PREL_PG_HI21 275 +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 + +#define R_AARCH64_ADD_ABS_LO12_NC 277 + +#define R_AARCH64_LDST8_ABS_LO12_NC 278 +#define R_AARCH64_LDST16_ABS_LO12_NC 284 +#define R_AARCH64_LDST32_ABS_LO12_NC 285 +#define R_AARCH64_LDST64_ABS_LO12_NC 286 +#define R_AARCH64_LDST128_ABS_LO12_NC 299 + +#define R_AARCH64_JUMP26 282 +#define R_AARCH64_CALL26 283 + +/* clang-format off */ +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +}; +/* clang-format on */ + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + const char *s = BUILD_TARGET; + size_t s_size = sizeof(BUILD_TARGET); + char *d = target_buf; + + /* Set to "aarch64v8" by default if sub version isn't specified */ + if (strcmp(s, "AARCH64") == 0) { + s = "aarch64v8"; + s_size = 10; /* sizeof("aarch64v8"); */ + } + if (target_buf_size < s_size) { + s_size = target_buf_size; + } + while (--s_size) { + if (*s >= 'A' && *s <= 'Z') + *d++ = *s++ + 'a' - 'A'; + else + *d++ = *s++; + } + /* Ensure the string is null byte ('\0') terminated */ + *d = '\0'; +} + +static uint32 +get_plt_item_size(void) +{ + /* 6*4 bytes instructions and 8 bytes symbol address */ + return 32; +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint32 *p = (uint32 *)plt; + *p++ = 0xf81f0ffe; /* str x30, [sp, #-16]! */ + *p++ = 0x100000be; /* adr x30, #20; symbol addr is PC + 5 instructions + below */ + *p++ = 0xf94003de; /* ldr x30, [x30] */ + *p++ = 0xd63f03c0; /* blr x30 */ + *p++ = 0xf84107fe; /* ldr x30, [sp], #16 */ + *p++ = 0xd61f03c0; /* br x30 */ + /* symbol addr */ + *(uint64 *)p = (uint64)(uintptr_t)target_sym_map[i].symbol_addr; + p += 2; + plt += get_plt_item_size(); + } +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +#define SIGN_EXTEND_TO_INT64(val, bits, val_ext) \ + do { \ + int64 m = (int64)((uint64)1 << (bits - 1)); \ + val_ext = ((int64)val ^ m) - m; \ + } while (0) + +#define Page(expr) ((expr) & ~0xFFF) + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + { + void *S, *P = (void *)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, imm26; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32 *)P; + imm26 = insn & 0x3FFFFFF; + SIGN_EXTEND_TO_INT64(imm26 << 2, 28, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + if (symbol_index < 0) { + /* Symbol address itself is an AOT function. + * Apply relocation with the symbol directly. + * Suppose the symbol address is in +-128MB relative + * to the relocation address. + */ + S = symbol_addr; + } + else { + uint8 *plt; + if (reloc_addend > 0) { + set_error_buf( + error_buf, error_buf_size, + "AOT module load failed: relocate to plt table " + "with reloc addend larger than 0 is unsupported."); + return false; + } + /* Symbol address is not an AOT function, + * but a function of runtime or native. Its address is + * beyond of the +-128MB space. Apply relocation with + * the PLT which branch to the target symbol address. + */ + S = plt = (uint8 *)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index; + } + + /* S + A - P */ + X = (int64)S + A - (int64)P; + + /* Check overflow: +-128MB */ + if (X > (128 * BH_MB) || X < (-128 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + /* write the imm26 back to instruction */ + *(int32 *)P = (insn & 0xFC000000) | ((int32)((X >> 2) & 0x3FFFFFF)); + break; + } + + case R_AARCH64_MOVW_UABS_G0: + case R_AARCH64_MOVW_UABS_G0_NC: + case R_AARCH64_MOVW_UABS_G1: + case R_AARCH64_MOVW_UABS_G1_NC: + case R_AARCH64_MOVW_UABS_G2: + case R_AARCH64_MOVW_UABS_G2_NC: + case R_AARCH64_MOVW_UABS_G3: + { + void *S = symbol_addr, + *P = (void *)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, imm16; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32 *)P; + imm16 = (insn >> 5) & 0xFFFF; + + SIGN_EXTEND_TO_INT64(imm16, 16, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + /* S + A */ + X = (int64)S + A; + + /* No need to check overflow for this relocation type */ + switch (reloc_type) { + case R_AARCH64_MOVW_UABS_G0: + if (X < 0 || X >= (1LL << 16)) + goto overflow_check_fail; + break; + case R_AARCH64_MOVW_UABS_G1: + if (X < 0 || X >= (1LL << 32)) + goto overflow_check_fail; + break; + case R_AARCH64_MOVW_UABS_G2: + if (X < 0 || X >= (1LL << 48)) + goto overflow_check_fail; + break; + default: + break; + } + + /* write the imm16 back to bits[5:20] of instruction */ + switch (reloc_type) { + case R_AARCH64_MOVW_UABS_G0: + case R_AARCH64_MOVW_UABS_G0_NC: + *(int32 *)P = + (insn & 0xFFE0001F) | ((int32)((X & 0xFFFF) << 5)); + break; + case R_AARCH64_MOVW_UABS_G1: + case R_AARCH64_MOVW_UABS_G1_NC: + *(int32 *)P = (insn & 0xFFE0001F) + | ((int32)(((X >> 16) & 0xFFFF) << 5)); + break; + case R_AARCH64_MOVW_UABS_G2: + case R_AARCH64_MOVW_UABS_G2_NC: + *(int32 *)P = (insn & 0xFFE0001F) + | ((int32)(((X >> 32) & 0xFFFF) << 5)); + break; + case R_AARCH64_MOVW_UABS_G3: + *(int32 *)P = (insn & 0xFFE0001F) + | ((int32)(((X >> 48) & 0xFFFF) << 5)); + break; + default: + bh_assert(0); + break; + } + break; + } + + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADR_PREL_PG_HI21_NC: + { + void *S = symbol_addr, + *P = (void *)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, immhi19, immlo2, imm21; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32 *)P; + immhi19 = (insn >> 5) & 0x7FFFF; + immlo2 = (insn >> 29) & 0x3; + imm21 = (immhi19 << 2) | immlo2; + + SIGN_EXTEND_TO_INT64(imm21 << 12, 33, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + /* Page(S+A) - Page(P) */ + X = Page((int64)S + A) - Page((int64)P); + + /* Check overflow: +-4GB */ + if (reloc_type == R_AARCH64_ADR_PREL_PG_HI21 + && (X > ((int64)4 * BH_GB) || X < ((int64)-4 * BH_GB))) + goto overflow_check_fail; + + /* write the imm21 back to instruction */ + immhi19 = (int32)(((X >> 12) >> 2) & 0x7FFFF); + immlo2 = (int32)((X >> 12) & 0x3); + *(int32 *)P = (insn & 0x9F00001F) | (immlo2 << 29) | (immhi19 << 5); + + break; + } + + case R_AARCH64_ADD_ABS_LO12_NC: + { + void *S = symbol_addr, + *P = (void *)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, imm12; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32 *)P; + imm12 = (insn >> 10) & 0xFFF; + + SIGN_EXTEND_TO_INT64(imm12, 12, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + /* S + A */ + X = (int64)S + A; + + /* No need to check overflow for this relocation type */ + + /* write the imm12 back to instruction */ + *(int32 *)P = (insn & 0xFFC003FF) | ((int32)((X & 0xFFF) << 10)); + break; + } + + case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_LDST16_ABS_LO12_NC: + case R_AARCH64_LDST32_ABS_LO12_NC: + case R_AARCH64_LDST64_ABS_LO12_NC: + case R_AARCH64_LDST128_ABS_LO12_NC: + { + void *S = symbol_addr, + *P = (void *)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, imm12; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32 *)P; + imm12 = (insn >> 10) & 0xFFF; + + SIGN_EXTEND_TO_INT64(imm12, 12, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + /* S + A */ + X = (int64)S + A; + + /* No need to check overflow for this relocation type */ + + /* write the imm12 back to instruction */ + switch (reloc_type) { + case R_AARCH64_LDST8_ABS_LO12_NC: + *(int32 *)P = + (insn & 0xFFC003FF) | ((int32)((X & 0xFFF) << 10)); + break; + case R_AARCH64_LDST16_ABS_LO12_NC: + *(int32 *)P = (insn & 0xFFC003FF) + | ((int32)(((X & 0xFFF) >> 1) << 10)); + break; + case R_AARCH64_LDST32_ABS_LO12_NC: + *(int32 *)P = (insn & 0xFFC003FF) + | ((int32)(((X & 0xFFF) >> 2) << 10)); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + *(int32 *)P = (insn & 0xFFC003FF) + | ((int32)(((X & 0xFFF) >> 3) << 10)); + break; + case R_AARCH64_LDST128_ABS_LO12_NC: + *(int32 *)P = (insn & 0xFFC003FF) + | ((int32)(((X & 0xFFF) >> 4) << 10)); + break; + default: + bh_assert(0); + break; + } + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; + +overflow_check_fail: + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; +} diff --git a/core/iwasm/aot/arch/aot_reloc_arc.c b/core/iwasm/aot/arch/aot_reloc_arc.c new file mode 100644 index 0000000000..2ce1154426 --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_arc.c @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_ARC_S21H_PCREL 14 +#define R_ARC_S21W_PCREL 15 +#define R_ARC_S25H_PCREL 16 +#define R_ARC_S25W_PCREL 17 +#define R_ARC_32 4 +#define R_ARC_32_ME 27 + +/* clang-format off */ +#ifndef __CCAC__ +void __st_r13_to_r15(); +void __st_r13_to_r16(); +void __st_r13_to_r17(); +void __st_r13_to_r18(); +void __st_r13_to_r19(); +void __st_r13_to_r20(); +void __st_r13_to_r21(); +void __st_r13_to_r22(); +void __st_r13_to_r23(); +void __st_r13_to_r24(); +void __st_r13_to_r25(); +void __ld_r13_to_r15(); +void __ld_r13_to_r16(); +void __ld_r13_to_r17(); +void __ld_r13_to_r18(); +void __ld_r13_to_r19(); +void __ld_r13_to_r20(); +void __ld_r13_to_r21(); +void __ld_r13_to_r22(); +void __ld_r13_to_r23(); +void __ld_r13_to_r24(); +void __ld_r13_to_r25(); +void __adddf3(); +void __addsf3(); +void __divdf3(); +void __divdi3(); +void __divsf3(); +void __divsi3(); +void __extendsfdf2(); +void __fixdfsi(); +void __floatsidf(); +void __floatsisf(); +void __muldf3(); +void __mulsf3(); +void __subdf3(); +void __subsf3(); +void __truncdfsf2(); +void __floatunsisf(); +void __fixunsdfsi(); +void __floatdisf(); +void __floatdidf(); +void __fixdfdi(); +void __ltsf2(); +void __gesf2(); +void __eqdf2(); +void __nedf2(); +void __ltsf2(); +void __nesf2(); +void __unordsf2(); +void __fixunssfsi(); +#else +void __ac_push_13_to_13(); +void __ac_push_13_to_14(); +void __ac_push_13_to_15(); +void __ac_push_13_to_16(); +void __ac_push_13_to_17(); +void __ac_push_13_to_18(); +void __ac_push_13_to_19(); +void __ac_push_13_to_20(); +void __ac_push_13_to_21(); +void __ac_push_13_to_22(); +void __ac_push_13_to_23(); +void __ac_push_13_to_24(); +void __ac_push_13_to_25(); +void __ac_push_13_to_26(); +void __ac_push_none(); +void __ac_pop_13_to_26(); +void __ac_pop_13_to_26v(); +void __ac_pop_13_to_25(); +void __ac_pop_13_to_25v(); +void __ac_pop_13_to_24(); +void __ac_pop_13_to_24v(); +void __ac_pop_13_to_23(); +void __ac_pop_13_to_23v(); +void __ac_pop_13_to_22(); +void __ac_pop_13_to_22v(); +void __ac_pop_13_to_21(); +void __ac_pop_13_to_21v(); +void __ac_pop_13_to_20(); +void __ac_pop_13_to_20v(); +void __ac_pop_13_to_19(); +void __ac_pop_13_to_19v(); +void __ac_pop_13_to_18(); +void __ac_pop_13_to_18v(); +void __ac_pop_13_to_17(); +void __ac_pop_13_to_17v(); +void __ac_pop_13_to_16(); +void __ac_pop_13_to_16v(); +void __ac_pop_13_to_15(); +void __ac_pop_13_to_15v(); +void __ac_pop_13_to_14(); +void __ac_pop_13_to_14v(); +void __ac_pop_13_to_13(); +void __ac_pop_13_to_13v(); +void __ac_pop_none(); +void __ac_pop_nonev(); +void __eqdf2(); +void __nedf2(); +void __ltsf2(); +void __nesf2(); +void __gesf2(); +void __gtsf2(); +void __unordsf2(); +void __truncdfhf2(); +void __truncsfhf2(); +#endif /* end of __CCAC__ */ + +void __ledf2(); +void __ltdf2(); +void __gedf2(); +void __gtdf2(); +void __eqsf2(); +void __lesf2(); +void __unorddf2(); +/* clang-format on */ + +static SymbolMap target_sym_map[] = { + /* clang-format off */ + REG_COMMON_SYMBOLS +#ifndef __CCAC__ + REG_SYM(__st_r13_to_r15), + REG_SYM(__st_r13_to_r16), + REG_SYM(__st_r13_to_r17), + REG_SYM(__st_r13_to_r18), + REG_SYM(__st_r13_to_r19), + REG_SYM(__st_r13_to_r20), + REG_SYM(__st_r13_to_r21), + REG_SYM(__st_r13_to_r22), + REG_SYM(__st_r13_to_r23), + REG_SYM(__st_r13_to_r24), + REG_SYM(__st_r13_to_r25), + REG_SYM(__ld_r13_to_r15), + REG_SYM(__ld_r13_to_r16), + REG_SYM(__ld_r13_to_r17), + REG_SYM(__ld_r13_to_r18), + REG_SYM(__ld_r13_to_r19), + REG_SYM(__ld_r13_to_r20), + REG_SYM(__ld_r13_to_r21), + REG_SYM(__ld_r13_to_r22), + REG_SYM(__ld_r13_to_r23), + REG_SYM(__ld_r13_to_r24), + REG_SYM(__ld_r13_to_r25), + REG_SYM(__adddf3), + REG_SYM(__addsf3), + REG_SYM(__divdf3), + REG_SYM(__divdi3), + REG_SYM(__divsf3), + REG_SYM(__divsi3), + REG_SYM(__extendsfdf2), + REG_SYM(__fixdfsi), + REG_SYM(__floatsidf), + REG_SYM(__floatsisf), + REG_SYM(__muldf3), + REG_SYM(__mulsf3), + REG_SYM(__subdf3), + REG_SYM(__subsf3), + REG_SYM(__truncdfsf2), + REG_SYM(__floatunsisf), + REG_SYM(__fixunsdfsi), + REG_SYM(__floatdisf), + REG_SYM(__floatdidf), + REG_SYM(__fixdfdi), + REG_SYM(__ltsf2), + REG_SYM(__gesf2), + REG_SYM(__eqdf2), + REG_SYM(__nedf2), + REG_SYM(__ltsf2), + REG_SYM(__nesf2), + REG_SYM(__unordsf2), + REG_SYM(__fixunssfsi), +#else + REG_SYM(__ac_push_13_to_13), + REG_SYM(__ac_push_13_to_14), + REG_SYM(__ac_push_13_to_15), + REG_SYM(__ac_push_13_to_16), + REG_SYM(__ac_push_13_to_17), + REG_SYM(__ac_push_13_to_18), + REG_SYM(__ac_push_13_to_19), + REG_SYM(__ac_push_13_to_20), + REG_SYM(__ac_push_13_to_21), + REG_SYM(__ac_push_13_to_22), + REG_SYM(__ac_push_13_to_23), + REG_SYM(__ac_push_13_to_24), + REG_SYM(__ac_push_13_to_25), + REG_SYM(__ac_push_13_to_26), + REG_SYM(__ac_push_none), + REG_SYM(__ac_pop_13_to_26), + REG_SYM(__ac_pop_13_to_26v), + REG_SYM(__ac_pop_13_to_25), + REG_SYM(__ac_pop_13_to_25v), + REG_SYM(__ac_pop_13_to_24), + REG_SYM(__ac_pop_13_to_24v), + REG_SYM(__ac_pop_13_to_23), + REG_SYM(__ac_pop_13_to_23v), + REG_SYM(__ac_pop_13_to_22), + REG_SYM(__ac_pop_13_to_22v), + REG_SYM(__ac_pop_13_to_21), + REG_SYM(__ac_pop_13_to_21v), + REG_SYM(__ac_pop_13_to_20), + REG_SYM(__ac_pop_13_to_20v), + REG_SYM(__ac_pop_13_to_19), + REG_SYM(__ac_pop_13_to_19v), + REG_SYM(__ac_pop_13_to_18), + REG_SYM(__ac_pop_13_to_18v), + REG_SYM(__ac_pop_13_to_17), + REG_SYM(__ac_pop_13_to_17v), + REG_SYM(__ac_pop_13_to_16), + REG_SYM(__ac_pop_13_to_16v), + REG_SYM(__ac_pop_13_to_15), + REG_SYM(__ac_pop_13_to_15v), + REG_SYM(__ac_pop_13_to_14), + REG_SYM(__ac_pop_13_to_14v), + REG_SYM(__ac_pop_13_to_13), + REG_SYM(__ac_pop_13_to_13v), + REG_SYM(__ac_pop_none), + REG_SYM(__ac_pop_nonev), + REG_SYM(__eqdf2), + REG_SYM(__nedf2), + REG_SYM(__ltsf2), + REG_SYM(__nesf2), + REG_SYM(__gesf2), + REG_SYM(__gtsf2), + REG_SYM(__unordsf2), + REG_SYM(__truncdfhf2), + REG_SYM(__truncsfhf2), +#endif /* end of __CCAC__ */ + + REG_SYM(__ledf2), + REG_SYM(__ltdf2), + REG_SYM(__gedf2), + REG_SYM(__gtdf2), + REG_SYM(__eqsf2), + REG_SYM(__lesf2), + REG_SYM(__unorddf2), + /* clang-format on */ +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "arc"); +} + +uint32 +get_plt_table_size() +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +static uint32 +middle_endian_convert(uint32 insn) +{ + return ((insn & 0xFFFF0000) >> 16) | ((insn & 0x0000FFFF) << 16); +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_ARC_S21H_PCREL: + { + uint32 insn = LOAD_I32(target_section_addr + reloc_offset); + int32 addend, value; + uintptr_t S, P; + intptr_t A; + + CHECK_RELOC_OFFSET(sizeof(void *)); + + /* Convert from middle endian */ + insn = middle_endian_convert(insn); + + /* Extract the first 10 bits from Position 6 to 15 in insn */ + addend = (insn << 16) >> 22; + addend = addend << 10; + /* Extract the remaining 10 bits from Position 17 to 26 in insn */ + addend |= ((insn << 5) >> 22); + /* Fill in 1 bits to get the 21 bit Offset Value */ + addend = addend << 1; + + /* (S + A) - P */ + S = (uintptr_t)(uint8 *)symbol_addr; + A = (intptr_t)reloc_addend; + P = (uintptr_t)(target_section_addr + reloc_offset); + P &= (uintptr_t)~1; + value = (int32)(S + A + addend - P); + + insn = insn & 0xf801003f; + insn |= ((((value >> 1) & 0x3ff) << 17) + | (((value >> 1) & 0xffc00) >> 4)); + + /* Convert to middle endian */ + insn = middle_endian_convert(insn); + + STORE_U32(target_section_addr + reloc_offset, insn); + break; + } + case R_ARC_S21W_PCREL: + { + uint32 insn = LOAD_I32(target_section_addr + reloc_offset); + int32 addend, value; + uintptr_t S, P; + intptr_t A; + + CHECK_RELOC_OFFSET(sizeof(void *)); + + /* Convert from middle endian */ + insn = middle_endian_convert(insn); + + /* Extract the first 10 bits from Position 6 to 15 in insn */ + addend = (insn << 16) >> 22; + addend = addend << 9; + /* Extract the remaining 9 bits from Position 18 to 26 in insn */ + addend |= ((insn << 5) >> 23); + /* Fill in 2 bits to get the 21 bit Offset Value */ + addend = addend << 2; + + /* (S + A) - P */ + S = (uintptr_t)(uint8 *)symbol_addr; + A = (intptr_t)reloc_addend; + P = (uintptr_t)(target_section_addr + reloc_offset); + P &= (uintptr_t)~3; + value = (int32)(S + A + addend - P); + + insn = insn & 0xf803003f; + insn |= ((((value >> 2) & 0x1ff) << 18) + | (((value >> 2) & 0x7fe00) >> 3)); + + /* Convert to middle endian */ + insn = middle_endian_convert(insn); + + STORE_U32(target_section_addr + reloc_offset, insn); + break; + } + case R_ARC_S25H_PCREL: + { + uint32 insn = LOAD_I32(target_section_addr + reloc_offset); + int32 addend, value; + uintptr_t S, P; + intptr_t A; + + CHECK_RELOC_OFFSET(sizeof(void *)); + + /* Convert from middle endian */ + insn = middle_endian_convert(insn); + + addend = ((insn << 28) >> 28) << 10; + /* Extract the next 10 bits from Position 6 to 15 in insn */ + addend |= ((insn << 16) >> 22); + addend = addend << 10; + /* Extract the remaining 10 bits from Position 17 to 26 in insn */ + addend |= ((insn << 5) >> 22); + /* Fill in 1 bits to get the 25 bit Offset Value */ + addend = addend << 1; + + /* (S + A) - P */ + S = (uintptr_t)(uint8 *)symbol_addr; + A = (intptr_t)reloc_addend; + P = (uintptr_t)(target_section_addr + reloc_offset); + P &= (uintptr_t)~1; + value = (int32)(S + A + addend - P); + + insn = insn & 0xf8010030; + insn |= ((((value >> 1) & 0x3ff) << 17) + | (((value >> 1) & 0xffc00) >> 4) + | (((value >> 1) & 0xf00000) >> 20)); + + /* Convert to middle endian */ + insn = middle_endian_convert(insn); + + STORE_U32(target_section_addr + reloc_offset, insn); + break; + } + case R_ARC_S25W_PCREL: + { + uint32 insn = LOAD_I32(target_section_addr + reloc_offset); + int32 addend, value; + uintptr_t S, P; + intptr_t A; + + CHECK_RELOC_OFFSET(sizeof(void *)); + + /* Convert from middle endian */ + insn = middle_endian_convert(insn); + + addend = ((insn << 28) >> 28) << 10; + /* Extract the next 10 bits from Position 6 to 15 in insn */ + addend |= ((insn << 16) >> 22); + addend = addend << 9; + /* Extract the remaining 9 bits from Position 18 to 26 in insn */ + addend |= ((insn << 5) >> 23); + /* Fill in 2 bits to get the 25 bit Offset Value */ + addend = addend << 2; + + /* (S + A) - P */ + S = (uintptr_t)(uint8 *)symbol_addr; + A = (intptr_t)reloc_addend; + P = (uintptr_t)(target_section_addr + reloc_offset); + P &= (uintptr_t)~3; + value = (int32)(S + A + addend - P); + + insn = insn & 0xf8030030; + insn |= ((((value >> 2) & 0x1ff) << 18) + | (((value >> 2) & 0x7fe00) >> 3) + | (((value >> 2) & 0x780000) >> 19)); + + /* Convert to middle endian */ + insn = middle_endian_convert(insn); + + STORE_U32(target_section_addr + reloc_offset, insn); + break; + } + case R_ARC_32: + case R_ARC_32_ME: + { + uint32 insn; + + CHECK_RELOC_OFFSET(sizeof(void *)); + + /* (S + A) */ + insn = (uint32)((uintptr_t)symbol_addr + (intptr_t)reloc_addend); + + if (reloc_type == R_ARC_32_ME) + /* Convert to middle endian */ + insn = middle_endian_convert(insn); + + STORE_U32(target_section_addr + reloc_offset, insn); + break; + } + default: + { + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + } + return true; +} diff --git a/core/iwasm/aot/arch/aot_reloc_arm.c b/core/iwasm/aot/arch/aot_reloc_arm.c new file mode 100644 index 0000000000..0be17ef4cc --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_arm.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JMP24 29 /* PC relative 24 bit (B/BL). */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_MOVW_ABS_NC 43 +#define R_ARM_MOVT_ABS 44 + +/* clang-format off */ +void __adddf3(void); +void __addsf3(void); +void __aeabi_d2f(void); +void __aeabi_d2iz(void); +void __aeabi_d2lz(void); +void __aeabi_d2uiz(void); +void __aeabi_d2ulz(void); +void __aeabi_dadd(void); +void __aeabi_dcmpeq(void); +void __aeabi_dcmpge(void); +void __aeabi_dcmpgt(void); +void __aeabi_dcmple(void); +void __aeabi_dcmplt(void); +void __aeabi_dcmpun(void); +void __aeabi_ddiv(void); +void __aeabi_dmul(void); +void __aeabi_dsub(void); +void __aeabi_f2d(void); +void __aeabi_f2iz(void); +void __aeabi_f2lz(void); +void __aeabi_f2ulz(void); +void __aeabi_fadd(void); +void __aeabi_fcmpeq(void); +void __aeabi_fcmpge(void); +void __aeabi_fcmpgt(void); +void __aeabi_fcmple(void); +void __aeabi_fcmplt(void); +void __aeabi_fcmpun(void); +void __aeabi_fdiv(void); +void __aeabi_fmul(void); +void __aeabi_fsub(void); +void __aeabi_i2d(void); +void __aeabi_i2f(void); +void __aeabi_idiv(void); +void __aeabi_idivmod(void); +void __aeabi_l2d(void); +void __aeabi_l2f(void); +void __aeabi_ldivmod(void); +void __aeabi_memclr(void); +void __aeabi_memcpy(void); +void __aeabi_memmove(void); +void __aeabi_memset(void); +void __aeabi_ui2d(void); +void __aeabi_ui2f(void); +void __aeabi_uidiv(void); +void __aeabi_uidivmod(void); +void __aeabi_ul2d(void); +void __aeabi_ul2f(void); +void __aeabi_uldivmod(void); +void __clzsi2(void); +void __divdf3(void); +void __divdi3(void); +void __divsf3(void); +void __divsi3(void); +void __eqdf2(void); +void __eqsf2(void); +void __extendsfdf2(void); +void __fixdfdi(void); +void __fixdfsi(void); +void __fixsfdi(void); +void __fixsfsi(void); +void __fixunsdfdi(void); +void __fixunsdfsi(void); +void __fixunssfdi(void); +void __floatdidf(void); +void __floatdisf(void); +void __floatsidf(void); +void __floatsisf(void); +void __floatundidf(void); +void __floatundisf(void); +void __floatunsidf(void); +void __floatunsisf(void); +void __gedf2(void); +void __gesf2(void); +void __gtdf2(void); +void __gtsf2(void); +void __ledf2(void); +void __lesf2(void); +void __ltdf2(void); +void __ltsf2(void); +void __moddi3(void); +void __modsi3(void); +void __muldf3(void); +void __mulsf3(void); +void __nedf2(void); +void __nesf2(void); +void __subdf3(void); +void __subsf3(void); +void __truncdfsf2(void); +void __udivdi3(void); +void __udivmoddi4(void); +void __udivsi3(void); +void __umoddi3(void); +void __umodsi3(void); +void __unorddf2(void); +void __unordsf2(void); +/* clang-format on */ + +static SymbolMap target_sym_map[] = { + /* clang-format off */ + REG_COMMON_SYMBOLS + /* compiler-rt symbols that come from compiler(e.g. gcc) */ + REG_SYM(__adddf3), + REG_SYM(__addsf3), + /* clang-format on */ + REG_SYM(__aeabi_d2f), + REG_SYM(__aeabi_d2iz), + REG_SYM(__aeabi_d2lz), + REG_SYM(__aeabi_d2uiz), + REG_SYM(__aeabi_d2ulz), + REG_SYM(__aeabi_dadd), + REG_SYM(__aeabi_dcmpeq), + REG_SYM(__aeabi_dcmpge), + REG_SYM(__aeabi_dcmpgt), + REG_SYM(__aeabi_dcmple), + REG_SYM(__aeabi_dcmplt), + REG_SYM(__aeabi_dcmpun), + REG_SYM(__aeabi_ddiv), + REG_SYM(__aeabi_dmul), + REG_SYM(__aeabi_dsub), + REG_SYM(__aeabi_f2d), + REG_SYM(__aeabi_f2iz), + REG_SYM(__aeabi_f2lz), + REG_SYM(__aeabi_f2ulz), + REG_SYM(__aeabi_fadd), + REG_SYM(__aeabi_fcmpeq), + REG_SYM(__aeabi_fcmpge), + REG_SYM(__aeabi_fcmpgt), + REG_SYM(__aeabi_fcmple), + REG_SYM(__aeabi_fcmplt), + REG_SYM(__aeabi_fcmpun), + REG_SYM(__aeabi_fdiv), + REG_SYM(__aeabi_fmul), + REG_SYM(__aeabi_fsub), + REG_SYM(__aeabi_i2d), + REG_SYM(__aeabi_i2f), + REG_SYM(__aeabi_idiv), + REG_SYM(__aeabi_idivmod), + REG_SYM(__aeabi_l2d), + REG_SYM(__aeabi_l2f), + REG_SYM(__aeabi_ldivmod), + REG_SYM(__aeabi_memclr), + REG_SYM(__aeabi_memcpy), + REG_SYM(__aeabi_memmove), + REG_SYM(__aeabi_memset), + REG_SYM(__aeabi_ui2d), + REG_SYM(__aeabi_ui2f), + REG_SYM(__aeabi_uidiv), + REG_SYM(__aeabi_uidivmod), + REG_SYM(__aeabi_ul2d), + REG_SYM(__aeabi_ul2f), + REG_SYM(__aeabi_uldivmod), + REG_SYM(__clzsi2), + REG_SYM(__divdf3), + REG_SYM(__divdi3), + REG_SYM(__divsf3), + REG_SYM(__divsi3), + REG_SYM(__eqdf2), + REG_SYM(__eqsf2), + REG_SYM(__extendsfdf2), + REG_SYM(__fixdfdi), + REG_SYM(__fixdfsi), + REG_SYM(__fixsfdi), + REG_SYM(__fixsfsi), + REG_SYM(__fixunsdfdi), + REG_SYM(__fixunsdfsi), + REG_SYM(__fixunssfdi), + REG_SYM(__floatdidf), + REG_SYM(__floatdisf), + REG_SYM(__floatsidf), + REG_SYM(__floatsisf), + REG_SYM(__floatundidf), + REG_SYM(__floatundisf), + REG_SYM(__floatunsidf), + REG_SYM(__floatunsisf), + REG_SYM(__gedf2), + REG_SYM(__gesf2), + REG_SYM(__gtdf2), + REG_SYM(__gtsf2), + REG_SYM(__ledf2), + REG_SYM(__lesf2), + REG_SYM(__ltdf2), + REG_SYM(__ltsf2), + REG_SYM(__moddi3), + REG_SYM(__modsi3), + REG_SYM(__muldf3), + REG_SYM(__mulsf3), + REG_SYM(__nedf2), + REG_SYM(__nesf2), + REG_SYM(__subdf3), + REG_SYM(__subsf3), + REG_SYM(__truncdfsf2), + REG_SYM(__udivdi3), + REG_SYM(__udivmoddi4), + REG_SYM(__udivsi3), + REG_SYM(__umoddi3), + REG_SYM(__umodsi3), + REG_SYM(__unorddf2), + REG_SYM(__unordsf2), +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +#define BUILD_TARGET_ARM_DEFAULT "armv4" +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + const char *s = BUILD_TARGET; + size_t s_size = sizeof(BUILD_TARGET); + char *d = target_buf; + + /* Set to "armv4" by default if sub version isn't specified */ + if (strcmp(s, "ARM") == 0) { + s = BUILD_TARGET_ARM_DEFAULT; + s_size = sizeof(BUILD_TARGET_ARM_DEFAULT); + } + if (target_buf_size < s_size) { + s_size = target_buf_size; + } + while (--s_size) { + if (*s >= 'A' && *s <= 'Z') + *d++ = *s++ + 'a' - 'A'; + else + *d++ = *s++; + } + /* Ensure the string is null byte ('\0') terminated */ + *d = '\0'; +} +#undef BUILD_TARGET_ARM_DEFAULT + +uint32 +get_plt_item_size(void) +{ + /* 8 bytes instructions and 4 bytes symbol address */ + return 12; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint32 *p = (uint32 *)plt; + /* ldr pc, [pc] */ + *p++ = 0xe59ff000; + /* nop */ + *p++ = 0xe1a00000; + /* symbol addr */ + *p++ = (uint32)(uintptr_t)target_sym_map[i].symbol_addr; + plt += get_plt_item_size(); + } +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_ARM_CALL: + case R_ARM_JMP24: + { + intptr_t result; + int32 RESULT_MASK = 0x03FFFFFE; + int32 insn = *(int32 *)(target_section_addr + reloc_offset); + /* Initial addend: sign_extend(insn[23:0] << 2) */ + int32 initial_addend = + ((insn & 0xFFFFFF) << 2) | ((insn & 0x800000) ? 0xFC000000 : 0); + + CHECK_RELOC_OFFSET(sizeof(int32)); + + if (symbol_index < 0) { + /* Symbol address itself is an AOT function. + * Apply relocation with the symbol directly. + * Suppose the symbol address is in +-32MB relative + * to the relocation address. + */ + /* operation: ((S + A) | T) - P where S is symbol address and T + * is 0 */ + result = + (intptr_t)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + + reloc_offset)); + } + else { + if (reloc_addend > 0) { + set_error_buf( + error_buf, error_buf_size, + "AOT module load failed: relocate to plt table " + "with reloc addend larger than 0 is unsupported."); + return false; + } + + /* Symbol address is not an AOT function, + * but a function of runtime or native. Its address is + * beyond of the +-32MB space. Apply relocation with + * the PLT which branch to the target symbol address. + */ + /* operation: ((S + A) | T) - P where S is PLT address and T is + * 0 */ + uint8 *plt = (uint8 *)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index; + result = (intptr_t)((uintptr_t)plt + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + + reloc_offset)); + } + + result += initial_addend; + + /* Check overflow: +-32MB */ + if (result > (32 * BH_MB) || result < (-32 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + *(int32 *)(target_section_addr + reloc_offset) = + (int32)((insn & 0xff000000) + | (((int32)result & RESULT_MASK) >> 2)); + break; + } + case R_ARM_ABS32: + { + intptr_t initial_addend; + /* (S + A) | T where T is 0 */ + CHECK_RELOC_OFFSET(sizeof(void *)); + initial_addend = + *(intptr_t *)(target_section_addr + (uint32)reloc_offset); + *(uintptr_t *)(target_section_addr + reloc_offset) = + (uintptr_t)symbol_addr + initial_addend + + (intptr_t)reloc_addend; + break; + } + case R_ARM_MOVW_ABS_NC: + case R_ARM_MOVT_ABS: + { + uintptr_t *loc; + uintptr_t addr; + CHECK_RELOC_OFFSET(sizeof(void *)); + loc = (uintptr_t *)(target_section_addr + (uint32)reloc_offset); + addr = (uintptr_t)symbol_addr + (intptr_t)reloc_addend; + if (reloc_type == R_ARM_MOVT_ABS) { + addr >>= 16; + } + *loc = ((*loc) & 0xfff0f000) | ((addr << 4) & 0x000f0000) + | (addr & 0x00000fff); + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} diff --git a/core/iwasm/aot/arch/aot_reloc_dummy.c b/core/iwasm/aot/arch/aot_reloc_dummy.c new file mode 100644 index 0000000000..bc05d7b784 --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_dummy.c @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + abort(); +} + +uint32 +get_plt_table_size(void) +{ + abort(); +} + +void +init_plt_table(uint8 *plt) +{ + abort(); +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + abort(); +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + abort(); +} diff --git a/core/iwasm/aot/arch/aot_reloc_mips.c b/core/iwasm/aot/arch/aot_reloc_mips.c new file mode 100644 index 0000000000..4b856119c4 --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_mips.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ + +/* clang-format off */ +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +}; +/* clang-format on */ + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "mips"); +} + +static uint32 +get_plt_item_size(void) +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + /* TODO: implement relocation for mips */ + case R_MIPS_26: + case R_MIPS_32: + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} diff --git a/core/iwasm/aot/arch/aot_reloc_riscv.c b/core/iwasm/aot/arch/aot_reloc_riscv.c new file mode 100644 index 0000000000..8df9f9f8ed --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_riscv.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2021 XiaoMi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_RISCV_32 1 +#define R_RISCV_64 2 +#define R_RISCV_CALL 18 +#define R_RISCV_CALL_PLT 19 +#define R_RISCV_PCREL_HI20 23 +#define R_RISCV_PCREL_LO12_I 24 +#define R_RISCV_PCREL_LO12_S 25 +#define R_RISCV_HI20 26 +#define R_RISCV_LO12_I 27 +#define R_RISCV_LO12_S 28 + +#define RV_OPCODE_SW 0x23 + +#undef NEED_SOFT_FP +#undef NEED_SOFT_DP +#undef NEED_SOFT_I32_MUL +#undef NEED_SOFT_I32_DIV +#undef NEED_SOFT_I64_MUL +#undef NEED_SOFT_I64_DIV +#undef NEED_SOFT_ATOMIC + +#ifdef __riscv_flen +#if __riscv_flen == 32 +#define NEED_SOFT_DP +#endif +#else +#define NEED_SOFT_FP +#define NEED_SOFT_DP +#endif + +#ifndef __riscv_mul +#define NEED_SOFT_I32_MUL +#define NEED_SOFT_I64_MUL +#elif __riscv_xlen == 32 +#define NEED_SOFT_I64_MUL +#endif + +#ifndef __riscv_div +#define NEED_SOFT_I32_DIV +#define NEED_SOFT_I64_DIV +#elif __riscv_xlen == 32 +#define NEED_SOFT_I64_DIV +#endif + +#ifndef __riscv_atomic +#define NEED_SOFT_ATOMIC +#endif + +/* clang-format off */ +void __adddf3(void); +void __addsf3(void); +void __divdf3(void); +void __divdi3(void); +void __divsf3(void); +void __divsi3(void); +void __eqdf2(void); +void __eqsf2(void); +void __extendsfdf2(void); +void __fixdfdi(void); +void __fixdfsi(void); +void __fixsfdi(void); +void __fixsfsi(void); +void __fixunsdfdi(void); +void __fixunsdfsi(void); +void __fixunssfdi(void); +void __fixunssfsi(void); +void __floatdidf(void); +void __floatdisf(void); +void __floatsidf(void); +void __floatsisf(void); +void __floatundidf(void); +void __floatundisf(void); +void __floatunsidf(void); +void __floatunsisf(void); +void __gedf2(void); +void __gesf2(void); +void __gtdf2(void); +void __gtsf2(void); +void __ledf2(void); +void __lesf2(void); +void __ltdf2(void); +void __ltsf2(void); +void __moddi3(void); +void __modsi3(void); +void __muldf3(void); +void __muldi3(void); +void __mulsf3(void); +void __mulsi3(void); +void __nedf2(void); +void __negdf2(void); +void __negsf2(void); +void __nesf2(void); +void __subdf3(void); +void __subsf3(void); +void __truncdfsf2(void); +void __udivdi3(void); +void __udivsi3(void); +void __umoddi3(void); +void __umodsi3(void); +void __unorddf2(void); +void __unordsf2(void); +bool __atomic_compare_exchange_4(volatile void *, void *, unsigned int, + bool, int, int); +void __atomic_store_4(volatile void *, unsigned int, int); +/* clang-format on */ + +static SymbolMap target_sym_map[] = { + /* clang-format off */ + REG_COMMON_SYMBOLS +#ifdef NEED_SOFT_FP + REG_SYM(__addsf3), + REG_SYM(__divsf3), + REG_SYM(__eqsf2), + REG_SYM(__fixsfdi), + REG_SYM(__fixunssfdi), + REG_SYM(__fixunssfsi), + REG_SYM(__floatsidf), + REG_SYM(__gesf2), + REG_SYM(__gtsf2), + REG_SYM(__lesf2), + REG_SYM(__mulsf3), + REG_SYM(__negsf2), + REG_SYM(__nesf2), + REG_SYM(__subsf3), + REG_SYM(__unordsf2), +#elif __riscv_xlen == 32 + /* rv32f, support FP instruction but need soft routines + * to convert float and long long + */ + REG_SYM(__floatundisf), + REG_SYM(__floatdisf), +#endif +#ifdef NEED_SOFT_DP + REG_SYM(__adddf3), + REG_SYM(__divdf3), + REG_SYM(__eqdf2), + REG_SYM(__extendsfdf2), + REG_SYM(__fixdfdi), + REG_SYM(__fixdfsi), + REG_SYM(__fixunsdfdi), + REG_SYM(__fixunsdfsi), + REG_SYM(__floatdidf), + REG_SYM(__floatsidf), + REG_SYM(__floatundidf), + REG_SYM(__floatunsidf), + REG_SYM(__gedf2), + REG_SYM(__gtdf2), + REG_SYM(__ledf2), + REG_SYM(__ltdf2), + REG_SYM(__muldf3), + REG_SYM(__nedf2), + REG_SYM(__negdf2), + REG_SYM(__subdf3), + REG_SYM(__truncdfsf2), + REG_SYM(__unorddf2), +#elif __riscv_xlen == 32 + /* rv32d, support DP instruction but need soft routines + * to convert double and long long + */ + REG_SYM(__fixdfdi), + REG_SYM(__floatundidf), +#endif +#ifdef NEED_SOFT_I32_MUL + REG_SYM(__mulsi3), +#endif +#ifdef NEED_SOFT_I32_DIV + REG_SYM(__divsi3), + REG_SYM(__modsi3), + REG_SYM(__udivsi3), + REG_SYM(__umodsi3), +#endif +#ifdef NEED_SOFT_I64_MUL + REG_SYM(__muldi3), +#endif +#ifdef NEED_SOFT_I64_DIV + REG_SYM(__divdi3), + REG_SYM(__moddi3), + REG_SYM(__udivdi3), + REG_SYM(__umoddi3), +#endif +#ifdef NEED_SOFT_ATOMIC + REG_SYM(__atomic_compare_exchange_4), + REG_SYM(__atomic_store_4), +#endif + /* clang-format on */ +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "riscv"); +} + +uint32 +get_plt_item_size(void) +{ +#if __riscv_xlen == 64 + /* auipc + ld + jalr + nop + addr */ + return 20; +#else + return 0; +#endif +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +/* Get a val from given address */ +static uint32 +rv_get_val(uint16 *addr) +{ + uint32 ret; + ret = *addr | (*(addr + 1)) << 16; + return ret; +} + +/* Set a val to given address */ +static void +rv_set_val(uint16 *addr, uint32 val) +{ + *addr = (val & 0xffff); + *(addr + 1) = (val >> 16); + +#ifdef __riscv_zifencei + __asm__ volatile("fence.i"); +#else + __asm__ volatile("fence"); +#endif +} + +/* Add a val to given address */ +static void +rv_add_val(uint16 *addr, uint32 val) +{ + uint32 cur = rv_get_val(addr); + rv_set_val(addr, cur + val); +} + +/** + * Get imm_hi and imm_lo from given integer + * + * @param imm given integer, signed 32bit + * @param imm_hi signed 20bit + * @param imm_lo signed 12bit + * + */ +static void +rv_calc_imm(int32 imm, int32 *imm_hi, int32 *imm_lo) +{ + int32 lo; + int32 hi = imm / 4096; + int32 r = imm % 4096; + + if (2047 < r) { + hi++; + } + else if (r < -2048) { + hi--; + } + + lo = imm - (hi * 4096); + + *imm_lo = lo; + *imm_hi = hi; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ +#if __riscv_xlen == 64 + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + uint8 *p; + + for (i = 0; i < num; i++) { + p = plt; + /* auipc t1, 0 */ + *(uint16 *)p = 0x0317; + p += 2; + *(uint16 *)p = 0x0000; + p += 2; + /* ld t1, 8(t1) */ + *(uint16 *)p = 0x3303; + p += 2; + *(uint16 *)p = 0x00C3; + p += 2; + /* jr t1 */ + *(uint16 *)p = 0x8302; + p += 2; + /* nop */ + *(uint16 *)p = 0x0001; + p += 2; + bh_memcpy_s(p, 8, &target_sym_map[i].symbol_addr, 8); + p += 8; + plt += get_plt_item_size(); + } +#endif +} + +typedef struct RelocTypeStrMap { + uint32 reloc_type; + char *reloc_str; +} RelocTypeStrMap; + +#define RELOC_TYPE_MAP(reloc_type) \ + { \ + reloc_type, #reloc_type \ + } + +static RelocTypeStrMap reloc_type_str_maps[] = { + RELOC_TYPE_MAP(R_RISCV_32), RELOC_TYPE_MAP(R_RISCV_64), + RELOC_TYPE_MAP(R_RISCV_CALL), RELOC_TYPE_MAP(R_RISCV_CALL_PLT), + RELOC_TYPE_MAP(R_RISCV_PCREL_HI20), RELOC_TYPE_MAP(R_RISCV_PCREL_LO12_I), + RELOC_TYPE_MAP(R_RISCV_PCREL_LO12_S), RELOC_TYPE_MAP(R_RISCV_HI20), + RELOC_TYPE_MAP(R_RISCV_LO12_I), RELOC_TYPE_MAP(R_RISCV_LO12_S), +}; + +static const char * +reloc_type_to_str(uint32 reloc_type) +{ + uint32 i; + + for (i = 0; i < sizeof(reloc_type_str_maps) / sizeof(RelocTypeStrMap); + i++) { + if (reloc_type_str_maps[i].reloc_type == reloc_type) + return reloc_type_str_maps[i].reloc_str; + } + + return "Unknown_Reloc_Type"; +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + int32 val, imm_hi, imm_lo, insn; + uint8 *addr = target_section_addr + reloc_offset; + char buf[128]; + + switch (reloc_type) { + case R_RISCV_32: + { + uint32 val_32 = + (uint32)((uintptr_t)symbol_addr + (intptr_t)reloc_addend); + + CHECK_RELOC_OFFSET(sizeof(uint32)); + if (val_32 != ((uintptr_t)symbol_addr + (intptr_t)reloc_addend)) { + goto fail_addr_out_of_range; + } + + rv_set_val((uint16 *)addr, val_32); + break; + } + +#if __riscv_xlen == 64 + case R_RISCV_64: + { + uint64 val_64 = + (uint64)((intptr_t)symbol_addr + (intptr_t)reloc_addend); + + CHECK_RELOC_OFFSET(sizeof(uint64)); + if (val_64 + != (uint64)((intptr_t)symbol_addr + (intptr_t)reloc_addend)) { + goto fail_addr_out_of_range; + } + + bh_memcpy_s(addr, 8, &val_64, 8); +#ifdef __riscv_zifencei + __asm__ volatile("fence.i"); +#else + __asm__ volatile("fence"); +#endif + break; + } +#endif + + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + case R_RISCV_PCREL_HI20: /* S + A - P */ + { + val = (int32)(intptr_t)((uint8 *)symbol_addr + reloc_addend - addr); + + CHECK_RELOC_OFFSET(sizeof(uint32)); + if (val != (intptr_t)((uint8 *)symbol_addr + reloc_addend - addr)) { + if (symbol_index >= 0) { + /* Call runtime function by plt code */ + symbol_addr = (uint8 *)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index; + val = (int32)(intptr_t)((uint8 *)symbol_addr - addr); + } + } + + if (val != (intptr_t)((uint8 *)symbol_addr + reloc_addend - addr)) { + goto fail_addr_out_of_range; + } + + rv_calc_imm(val, &imm_hi, &imm_lo); + + rv_add_val((uint16 *)addr, (imm_hi << 12)); + if ((rv_get_val((uint16 *)(addr + 4)) & 0x7f) == RV_OPCODE_SW) { + /* Adjust imm for SW : S-type */ + val = (((int32)imm_lo >> 5) << 25) + + (((int32)imm_lo & 0x1f) << 7); + + rv_add_val((uint16 *)(addr + 4), val); + } + else { + /* Adjust imm for MV(ADDI)/JALR : I-type */ + rv_add_val((uint16 *)(addr + 4), ((int32)imm_lo << 20)); + } + break; + } + + case R_RISCV_HI20: /* S + A */ + { + val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend); + + CHECK_RELOC_OFFSET(sizeof(uint32)); + if (val != ((intptr_t)symbol_addr + (intptr_t)reloc_addend)) { + goto fail_addr_out_of_range; + } + + insn = rv_get_val((uint16 *)addr); + rv_calc_imm(val, &imm_hi, &imm_lo); + insn = (insn & 0x00000fff) | (imm_hi << 12); + rv_set_val((uint16 *)addr, insn); + break; + } + + case R_RISCV_PCREL_LO12_I: /* S - P */ + case R_RISCV_PCREL_LO12_S: /* S - P */ + { + /* Already handled in R_RISCV_PCREL_HI20, it should be skipped for + * most cases. But it is still needed for some special cases, e.g. + * ``` + * label: + * auipc t0, %pcrel_hi(symbol) # R_RISCV_PCREL_HI20 (symbol) + * lui t1, 1 + * lw t2, t0, %pcrel_lo(label) # R_RISCV_PCREL_LO12_I (label) + * add t2, t2, t1 + * sw t2, t0, %pcrel_lo(label) # R_RISCV_PCREL_LO12_S (label) + * ``` + * In this case, the R_RISCV_PCREL_LO12_I/S relocation should be + * handled after R_RISCV_PCREL_HI20 relocation. + * + * So, if the R_RISCV_PCREL_LO12_I/S relocation is not followed by + * R_RISCV_PCREL_HI20 relocation, it should be handled here but + * not implemented yet. + */ + + if ((uintptr_t)addr - (uintptr_t)symbol_addr + - (uintptr_t)reloc_addend + != 4) { + goto fail_addr_out_of_range; + } + break; + } + + case R_RISCV_LO12_I: /* S + A */ + { + + val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend); + + CHECK_RELOC_OFFSET(sizeof(uint32)); + + if (val != (intptr_t)symbol_addr + (intptr_t)reloc_addend) { + goto fail_addr_out_of_range; + } + + addr = target_section_addr + reloc_offset; + insn = rv_get_val((uint16 *)addr); + rv_calc_imm(val, &imm_hi, &imm_lo); + insn = (insn & 0x000fffff) | (imm_lo << 20); + rv_set_val((uint16 *)addr, insn); + break; + } + + case R_RISCV_LO12_S: + { + val = (int32)((intptr_t)symbol_addr + (intptr_t)reloc_addend); + + CHECK_RELOC_OFFSET(sizeof(uint32)); + if (val != ((intptr_t)symbol_addr + (intptr_t)reloc_addend)) { + goto fail_addr_out_of_range; + } + + addr = target_section_addr + reloc_offset; + rv_calc_imm(val, &imm_hi, &imm_lo); + val = (((int32)imm_lo >> 5) << 25) + (((int32)imm_lo & 0x1f) << 7); + rv_add_val((uint16 *)addr, val); + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %" PRIu32 ".", + reloc_type); + return false; + } + + return true; + +fail_addr_out_of_range: + snprintf(buf, sizeof(buf), + "AOT module load failed: " + "relocation truncated to fit %s failed.", + reloc_type_to_str(reloc_type)); + set_error_buf(error_buf, error_buf_size, buf); + return false; +} diff --git a/core/iwasm/aot/arch/aot_reloc_thumb.c b/core/iwasm/aot/arch/aot_reloc_thumb.c new file mode 100644 index 0000000000..263dac587b --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_thumb.c @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_THM_CALL 10 /* PC relative (Thumb BL and ARMv5 Thumb BLX). */ +#define R_ARM_THM_JMP24 30 /* B.W */ +#define R_ARM_THM_MOVW_ABS_NC 47 +#define R_ARM_THM_MOVT_ABS 48 +#define R_ARM_THM_MOVW_PREL_NC 49 +#define R_ARM_THM_MOVT_PREL 50 + +/* clang-format off */ +void __adddf3(void); +void __addsf3(void); +void __aeabi_d2f(void); +void __aeabi_d2iz(void); +void __aeabi_d2lz(void); +void __aeabi_d2uiz(void); +void __aeabi_d2ulz(void); +void __aeabi_dadd(void); +void __aeabi_dcmpeq(void); +void __aeabi_dcmpge(void); +void __aeabi_dcmpgt(void); +void __aeabi_dcmple(void); +void __aeabi_dcmplt(void); +void __aeabi_dcmpun(void); +void __aeabi_ddiv(void); +void __aeabi_dmul(void); +void __aeabi_dsub(void); +void __aeabi_f2d(void); +void __aeabi_f2iz(void); +void __aeabi_f2lz(void); +void __aeabi_f2ulz(void); +void __aeabi_fadd(void); +void __aeabi_fcmpeq(void); +void __aeabi_fcmpge(void); +void __aeabi_fcmpgt(void); +void __aeabi_fcmple(void); +void __aeabi_fcmplt(void); +void __aeabi_fcmpun(void); +void __aeabi_fdiv(void); +void __aeabi_fmul(void); +void __aeabi_fsub(void); +void __aeabi_i2d(void); +void __aeabi_i2f(void); +void __aeabi_idiv(void); +void __aeabi_idivmod(void); +void __aeabi_l2d(void); +void __aeabi_l2f(void); +void __aeabi_ldivmod(void); +void __aeabi_memclr(void); +void __aeabi_memcpy(void); +void __aeabi_memmove(void); +void __aeabi_memset(void); +void __aeabi_llsl(void); +void __aeabi_llsr(void); +void __aeabi_lmul(void); +void __aeabi_ui2d(void); +void __aeabi_ui2f(void); +void __aeabi_uidiv(void); +void __aeabi_uidivmod(void); +void __aeabi_ul2d(void); +void __aeabi_ul2f(void); +void __aeabi_uldivmod(void); +void __ashldi3(void); +void __clzsi2(void); +void __divdf3(void); +void __divdi3(void); +void __divsi3(void); +void __eqdf2(void); +void __eqsf2(void); +void __extendsfdf2(void); +void __fixdfdi(void); +void __fixdfsi(void); +void __fixsfdi(void); +void __fixunsdfdi(void); +void __fixunsdfsi(void); +void __fixunssfdi(void); +void __floatdidf(void); +void __floatdisf(void); +void __floatsidf(void); +void __floatsisf(void); +void __floatundidf(void); +void __floatundisf(void); +void __floatunsidf(void); +void __floatunsisf(void); +void __gedf2(void); +void __gesf2(void); +void __gtdf2(void); +void __gtsf2(void); +void __ledf2(void); +void __lesf2(void); +void __lshrdi3(void); +void __ltdf2(void); +void __ltsf2(void); +void __moddi3(void); +void __modsi3(void); +void __muldf3(void); +void __muldi3(void); +void __mulsf3(void); +void __nedf2(void); +void __nesf2(void); +void __subdf3(void); +void __subsf3(void); +void __truncdfsf2(void); +void __udivdi3(void); +void __udivmoddi4(void); +void __udivsi3(void); +void __umoddi3(void); +void __umodsi3(void); +void __unorddf2(void); +void __unordsf2(void); +/* clang-format on */ + +static SymbolMap target_sym_map[] = { + /* clang-format off */ + REG_COMMON_SYMBOLS + /* compiler-rt symbols that come from compiler(e.g. gcc) */ +#if __ARM_ARCH != 6 + REG_SYM(__adddf3), + REG_SYM(__addsf3), + REG_SYM(__divdf3), + REG_SYM(__extendsfdf2), + REG_SYM(__fixdfsi), + REG_SYM(__floatsidf), + REG_SYM(__floatsisf), + REG_SYM(__floatunsidf), + REG_SYM(__floatunsisf), + REG_SYM(__muldf3), + REG_SYM(__mulsf3), + REG_SYM(__subdf3), + REG_SYM(__subsf3), + REG_SYM(__truncdfsf2), + REG_SYM(__unorddf2), + REG_SYM(__unordsf2), +#endif + /* clang-format on */ + REG_SYM(__aeabi_d2f), + REG_SYM(__aeabi_d2iz), + REG_SYM(__aeabi_d2lz), + REG_SYM(__aeabi_d2uiz), + REG_SYM(__aeabi_d2ulz), + REG_SYM(__aeabi_dadd), + REG_SYM(__aeabi_dcmpeq), + REG_SYM(__aeabi_dcmpge), + REG_SYM(__aeabi_dcmpgt), + REG_SYM(__aeabi_dcmple), + REG_SYM(__aeabi_dcmplt), + REG_SYM(__aeabi_dcmpun), + REG_SYM(__aeabi_ddiv), + REG_SYM(__aeabi_dmul), + REG_SYM(__aeabi_dsub), + REG_SYM(__aeabi_f2d), + REG_SYM(__aeabi_f2iz), + REG_SYM(__aeabi_f2lz), + REG_SYM(__aeabi_f2ulz), + REG_SYM(__aeabi_fadd), + REG_SYM(__aeabi_fcmpeq), + REG_SYM(__aeabi_fcmpge), + REG_SYM(__aeabi_fcmpgt), + REG_SYM(__aeabi_fcmple), + REG_SYM(__aeabi_fcmplt), + REG_SYM(__aeabi_fcmpun), + REG_SYM(__aeabi_fdiv), + REG_SYM(__aeabi_fmul), + REG_SYM(__aeabi_fsub), + REG_SYM(__aeabi_i2d), + REG_SYM(__aeabi_i2f), + REG_SYM(__aeabi_idiv), + REG_SYM(__aeabi_idivmod), + REG_SYM(__aeabi_l2d), + REG_SYM(__aeabi_l2f), + REG_SYM(__aeabi_ldivmod), + REG_SYM(__aeabi_memclr), + REG_SYM(__aeabi_memcpy), + REG_SYM(__aeabi_memmove), + REG_SYM(__aeabi_memset), + REG_SYM(__aeabi_llsl), + REG_SYM(__aeabi_llsr), + REG_SYM(__aeabi_lmul), + REG_SYM(__aeabi_ui2d), + REG_SYM(__aeabi_ui2f), + REG_SYM(__aeabi_uidiv), + REG_SYM(__aeabi_uidivmod), + REG_SYM(__aeabi_ul2d), + REG_SYM(__aeabi_ul2f), + REG_SYM(__aeabi_uldivmod), + REG_SYM(__ashldi3), + REG_SYM(__clzsi2), + REG_SYM(__divdi3), + REG_SYM(__divsi3), + REG_SYM(__eqdf2), + REG_SYM(__eqsf2), + REG_SYM(__fixdfdi), + REG_SYM(__fixsfdi), + REG_SYM(__fixunsdfdi), + REG_SYM(__fixunsdfsi), + REG_SYM(__fixunssfdi), + REG_SYM(__floatdidf), + REG_SYM(__floatdisf), + REG_SYM(__floatundidf), + REG_SYM(__floatundisf), + REG_SYM(__gedf2), + REG_SYM(__gesf2), + REG_SYM(__gtdf2), + REG_SYM(__gtsf2), + REG_SYM(__ledf2), + REG_SYM(__lesf2), + REG_SYM(__lshrdi3), + REG_SYM(__ltdf2), + REG_SYM(__ltsf2), + REG_SYM(__moddi3), + REG_SYM(__modsi3), + REG_SYM(__muldi3), + REG_SYM(__nedf2), + REG_SYM(__nesf2), + REG_SYM(__udivdi3), + REG_SYM(__udivmoddi4), + REG_SYM(__udivsi3), + REG_SYM(__umoddi3), + REG_SYM(__umodsi3), +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +#define BUILD_TARGET_THUMB_V4T "thumbv4t" +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + const char *s = BUILD_TARGET; + size_t s_size = sizeof(BUILD_TARGET); + char *d = target_buf; + + /* Set to "thumbv4t" by default if sub version isn't specified */ + if (strcmp(s, "THUMB") == 0) { + s = BUILD_TARGET_THUMB_V4T; + s_size = sizeof(BUILD_TARGET_THUMB_V4T); + } + if (target_buf_size < s_size) { + s_size = target_buf_size; + } + while (--s_size) { + if (*s >= 'A' && *s <= 'Z') + *d++ = *s++ + 'a' - 'A'; + else + *d++ = *s++; + } + /* Ensure the string is null byte ('\0') terminated */ + *d = '\0'; +} +#undef BUILD_TARGET_THUMB_V4T + +uint32 +get_plt_item_size(void) +{ + /* 16 bytes instructions and 4 bytes symbol address */ + return 20; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint16 *p = (uint16 *)plt; + /* nop */ + *p++ = 0xbf00; + /* push {r4} */ + *p++ = 0xb410; + /* add r4, pc, #8 */ + *p++ = 0xa402; + /* ldr r4, [r4, #0] */ + *p++ = 0x6824; + /* mov ip, r4 */ + *p++ = 0x46a4; + /* pop {r4} */ + *p++ = 0xbc10; + /* mov pc, ip */ + *p++ = 0x46e7; + /* nop */ + *p++ = 0xbf00; + /* symbol addr */ + *(uint32 *)p = (uint32)(uintptr_t)target_sym_map[i].symbol_addr; + plt += get_plt_item_size(); + } +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_ARM_THM_CALL: + case R_ARM_THM_JMP24: + { + int32 RESULT_MASK = 0x01FFFFFE; + int32 result, result_masked; + int16 *reloc_addr; + int32 initial_addend_0, initial_addend_1, initial_addend; + bool sign; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + reloc_addr = (int16 *)(target_section_addr + reloc_offset); + initial_addend_0 = (*reloc_addr) & 0x7FF; + initial_addend_1 = (*(reloc_addr + 1)) & 0x7FF; + sign = (initial_addend_0 & 0x400) ? true : false; + initial_addend = (initial_addend_0 << 12) | (initial_addend_1 << 1) + | (sign ? 0xFF800000 : 0); + + if (symbol_index < 0) { + /* Symbol address itself is an AOT function. + * Apply relocation with the symbol directly. + * Suppose the symbol address is in +-4MB relative + * to the relocation address. + */ + /* operation: ((S + A) | T) - P where S is symbol address + and T is 1 */ + result = + (int32)(((intptr_t)((uintptr_t)symbol_addr + + (intptr_t)reloc_addend) + | 1) + - (intptr_t)(target_section_addr + reloc_offset)); + } + else { + if (reloc_addend > 0) { + set_error_buf( + error_buf, error_buf_size, + "AOT module load failed: relocate to plt table " + "with reloc addend larger than 0 is unsupported."); + return false; + } + + /* Symbol address is not an AOT function, + * but a function of runtime or native. Its address is + * beyond of the +-4MB space. Apply relocation with + * the PLT which branch to the target symbol address. + */ + /* operation: ((S + A) | T) - P where S is PLT address + and T is 1 */ + uint8 *plt = (uint8 *)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index + 1; + result = + (int32)(((intptr_t)plt | 1) + - (intptr_t)(target_section_addr + reloc_offset)); + } + + result += initial_addend; + + /* Check overflow: +-4MB */ + if (result > (4 * BH_MB) || result < (-4 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + result_masked = (int32)result & RESULT_MASK; + initial_addend_0 = (result_masked >> 12) & 0x7FF; + initial_addend_1 = (result_masked >> 1) & 0x7FF; + + *reloc_addr = (*reloc_addr & ~0x7FF) | initial_addend_0; + *(reloc_addr + 1) = (*(reloc_addr + 1) & ~0x7FF) | initial_addend_1; + break; + } + case R_ARM_ABS32: + { + intptr_t initial_addend; + /* (S + A) | T where T is 0 */ + CHECK_RELOC_OFFSET(sizeof(void *)); + initial_addend = + *(intptr_t *)(target_section_addr + (uint32)reloc_offset); + *(uintptr_t *)(target_section_addr + reloc_offset) = + (uintptr_t)symbol_addr + initial_addend + + (intptr_t)reloc_addend; + break; + } + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + case R_ARM_THM_MOVW_PREL_NC: + case R_ARM_THM_MOVT_PREL: + { + uint16 upper = *(uint16 *)(target_section_addr + reloc_offset); + uint16 lower = *(uint16 *)(target_section_addr + reloc_offset + 2); + int32 offset; + + /* + * MOVT/MOVW instructions encoding in Thumb-2: + * + * i = upper[10] + * imm4 = upper[3:0] + * imm3 = lower[14:12] + * imm8 = lower[7:0] + * + * imm16 = imm4:i:imm3:imm8 + */ + + offset = ((upper & 0x000f) << 12) | ((upper & 0x0400) << 1) + | ((lower & 0x7000) >> 4) | (lower & 0x00ff); + offset = (offset ^ 0x8000) - 0x8000; + + offset += (symbol_addr + reloc_addend); + + if (reloc_type == R_ARM_THM_MOVT_PREL + || reloc_type == R_ARM_THM_MOVW_PREL_NC) + offset -= (int32)(target_section_addr + reloc_offset); + if (reloc_type == R_ARM_THM_MOVT_ABS + || reloc_type == R_ARM_THM_MOVT_PREL) + offset >>= 16; + + upper = (uint16)((upper & 0xfbf0) | ((offset & 0xf000) >> 12) + | ((offset & 0x0800) >> 1)); + lower = (uint16)((lower & 0x8f00) | ((offset & 0x0700) << 4) + | (offset & 0x00ff)); + + *(uint16 *)(target_section_addr + reloc_offset) = upper; + *(uint16 *)(target_section_addr + reloc_offset + 2) = lower; + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %" PRId32 ".", + reloc_type); + return false; + } + return true; +} diff --git a/core/iwasm/aot/arch/aot_reloc_x86_32.c b/core/iwasm/aot/arch/aot_reloc_x86_32.c new file mode 100644 index 0000000000..f7f2421f48 --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_x86_32.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +/* clang-format off */ +#if !defined(BH_PLATFORM_WINDOWS) +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_PLT32 4 /* 32-bit address ProcedureLinkageTable */ +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#else +#define IMAGE_REL_I386_DIR32 6 /* The target's 32-bit VA */ +#define IMAGE_REL_I386_REL32 20 /* The 32-bit relative displacement + to the target */ +#endif +/* clang-format on */ + +#if !defined(_WIN32) && !defined(_WIN32_) +/* clang-format off */ +void __divdi3(); +void __udivdi3(); +void __moddi3(); +void __umoddi3(); +/* clang-format on */ +#else +#pragma function(floor) +#pragma function(ceil) + +static int64 __stdcall __divdi3(int64 a, int64 b) +{ + return a / b; +} + +static uint64 __stdcall __udivdi3(uint64 a, uint64 b) +{ + return a / b; +} + +static int64 __stdcall __moddi3(int64 a, int64 b) +{ + return a % b; +} + +static uint64 __stdcall __umoddi3(uint64 a, uint64 b) +{ + return a % b; +} + +static uint64 __stdcall __aulldiv(uint64 a, uint64 b) +{ + return a / b; +} +static int64 __stdcall __alldiv(int64 a, int64 b) +{ + return a / b; +} +static int64 __stdcall __allrem(int64 a, int64 b) +{ + return a % b; +} +#endif /* !defined(_WIN32) && !defined(_WIN32_) */ + +/* clang-format off */ +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS + /* compiler-rt symbols that come from compiler(e.g. gcc) */ + REG_SYM(__divdi3), + REG_SYM(__udivdi3), + REG_SYM(__moddi3), + REG_SYM(__umoddi3), +#if defined(_WIN32) || defined(_WIN32_) + REG_SYM(__aulldiv), + REG_SYM(__alldiv), + REG_SYM(__allrem) +#endif /* defined(_WIN32) || defined(_WIN32_) */ +}; +/* clang-format on */ + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "i386"); +} + +uint32 +get_plt_table_size() +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { +#if !defined(BH_PLATFORM_WINDOWS) + case R_386_32: +#if WASM_ENABLE_STATIC_PGO != 0 + case R_386_TLS_GD_32: +#endif +#else + case IMAGE_REL_I386_DIR32: +#endif + { + intptr_t value; + + CHECK_RELOC_OFFSET(sizeof(void *)); + value = *(intptr_t *)(target_section_addr + (uint32)reloc_offset); + *(uintptr_t *)(target_section_addr + reloc_offset) = + (uintptr_t)symbol_addr + (intptr_t)reloc_addend + + value; /* S + A */ + break; + } + +#if !defined(BH_PLATFORM_WINDOWS) + /* + * Handle R_386_PLT32 like R_386_PC32 since it should be able to reach + * any 32 bit address + */ + case R_386_PLT32: + case R_386_PC32: +#else + case IMAGE_REL_I386_REL32: +#endif + { + int32 value; + + CHECK_RELOC_OFFSET(sizeof(void *)); + value = *(int32 *)(target_section_addr + (uint32)reloc_offset); + *(uint32 *)(target_section_addr + (uint32)reloc_offset) = + (uint32)((uintptr_t)symbol_addr + (intptr_t)reloc_addend + - (uintptr_t)(target_section_addr + + (uint32)reloc_offset) +#if defined(BH_PLATFORM_WINDOWS) + - sizeof(int32) +#endif + + value); /* S + A - P */ + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + return true; +} diff --git a/core/iwasm/aot/arch/aot_reloc_x86_64.c b/core/iwasm/aot/arch/aot_reloc_x86_64.c new file mode 100644 index 0000000000..2f36c4c3bc --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_x86_64.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#if !defined(BH_PLATFORM_WINDOWS) +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTPCRELX 41 /* relaxable GOTPCREL */ +#define R_X86_64_REX_GOTPCRELX 42 /* relaxable GOTPCREL with REX prefix */ +#else +#ifndef IMAGE_REL_AMD64_ADDR64 +#define IMAGE_REL_AMD64_ADDR64 1 /* The 64-bit VA of the relocation target */ +#define IMAGE_REL_AMD64_ADDR32 2 /* The 32-bit VA of the relocation target */ +/* clang-format off */ +#define IMAGE_REL_AMD64_REL32 4 /* The 32-bit relative address from + the byte following the relocation*/ +/* clang-format on */ +#endif +#endif + +#if defined(BH_PLATFORM_WINDOWS) +#pragma function(floor) +#pragma function(ceil) +#pragma function(floorf) +#pragma function(ceilf) +#endif + +/* clang-format off */ +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +}; +/* clang-format on */ + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "x86_64"); +} + +static uint32 +get_plt_item_size(void) +{ + /* size of mov instruction and jmp instruction */ + return 12; +} + +uint32 +get_plt_table_size() +{ + uint32 size = + get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); + return size; +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + uint8 *p; + + for (i = 0; i < num; i++) { + p = plt; + /* mov symbol_addr, rax */ + *p++ = 0x48; + *p++ = 0xB8; + *(uint64 *)p = (uint64)(uintptr_t)target_sym_map[i].symbol_addr; + p += sizeof(uint64); + /* jmp rax */ + *p++ = 0xFF; + *p++ = 0xE0; + plt += get_plt_item_size(); + } +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { +#if !defined(BH_PLATFORM_WINDOWS) + case R_X86_64_64: +#else + case IMAGE_REL_AMD64_ADDR64: +#endif + { + intptr_t value; + + CHECK_RELOC_OFFSET(sizeof(void *)); + value = *(intptr_t *)(target_section_addr + (uint32)reloc_offset); + *(uintptr_t *)(target_section_addr + reloc_offset) = + (uintptr_t)symbol_addr + reloc_addend + value; /* S + A */ + break; + } +#if defined(BH_PLATFORM_WINDOWS) + case IMAGE_REL_AMD64_ADDR32: + { + int32 value; + uintptr_t target_addr; + + CHECK_RELOC_OFFSET(sizeof(void *)); + value = *(int32 *)(target_section_addr + (uint32)reloc_offset); + target_addr = (uintptr_t)symbol_addr + reloc_addend + value; + if ((int32)target_addr != target_addr) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "relocation truncated to fit " + "IMAGE_REL_AMD64_ADDR32 failed. " + "Try using wamrc with --size-level=1 option."); + return false; + } + + *(int32 *)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } +#endif +#if !defined(BH_PLATFORM_WINDOWS) + case R_X86_64_PC32: + case R_X86_64_GOTPCREL: /* GOT + G has been calculated as symbol_addr */ + case R_X86_64_GOTPCRELX: + case R_X86_64_REX_GOTPCRELX: + { + intptr_t target_addr = (intptr_t) /* S + A - P */ + ((uintptr_t)symbol_addr + reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + CHECK_RELOC_OFFSET(sizeof(int32)); + if ((int32)target_addr != target_addr) { + set_error_buf( + error_buf, error_buf_size, + "AOT module load failed: " + "relocation truncated to fit R_X86_64_PC32 failed. " + "Try using wamrc with --size-level=1 or 0 option."); + return false; + } + + *(int32 *)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } + case R_X86_64_PC64: + { + intptr_t target_addr = (intptr_t) /* S + A - P */ + ((uintptr_t)symbol_addr + reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + + CHECK_RELOC_OFFSET(sizeof(int64)); + *(int64 *)(target_section_addr + reloc_offset) = (int64)target_addr; + break; + } + case R_X86_64_32: + case R_X86_64_32S: + { + char buf[128]; + uintptr_t target_addr = /* S + A */ + (uintptr_t)symbol_addr + reloc_addend; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + if ((reloc_type == R_X86_64_32 + && (uint32)target_addr != (uint64)target_addr) + || (reloc_type == R_X86_64_32S + && (int32)target_addr != (int64)target_addr)) { + snprintf(buf, sizeof(buf), + "AOT module load failed: " + "relocation truncated to fit %s failed. " + "Try using wamrc with --size-level=1 or 0 option.", + reloc_type == R_X86_64_32 ? "R_X86_64_32" + : "R_X86_64_32S"); + set_error_buf(error_buf, error_buf_size, buf); + return false; + } + + *(int32 *)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } +#endif +#if !defined(BH_PLATFORM_WINDOWS) + case R_X86_64_PLT32: +#else + case IMAGE_REL_AMD64_REL32: +#endif + { + uint8 *plt; + intptr_t target_addr = 0; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + if (symbol_index >= 0) { + plt = (uint8 *)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index; + target_addr = (intptr_t) /* L + A - P */ + ((uintptr_t)plt + reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + } + else { + target_addr = (intptr_t) /* S + A - P */ + ((uintptr_t)symbol_addr + reloc_addend + - (uintptr_t)(target_section_addr + reloc_offset)); + } + +#if defined(BH_PLATFORM_WINDOWS) + target_addr -= sizeof(int32); +#endif + if ((int32)target_addr != target_addr) { + set_error_buf( + error_buf, error_buf_size, + "AOT module load failed: " + "relocation truncated to fit " +#if !defined(BH_PLATFORM_WINDOWS) + "R_X86_64_PLT32 failed. " +#else + "IMAGE_REL_AMD64_32 failed." +#endif + "Try using wamrc with --size-level=1 or 0 option."); + return false; + } + *(int32 *)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} diff --git a/core/iwasm/aot/arch/aot_reloc_xtensa.c b/core/iwasm/aot/arch/aot_reloc_xtensa.c new file mode 100644 index 0000000000..a866883d7c --- /dev/null +++ b/core/iwasm/aot/arch/aot_reloc_xtensa.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_XTENSA_32 1 /* Direct 32 bit */ +#define R_XTENSA_SLOT0_OP 20 /* PC relative */ + +/* clang-format off */ +/* for soft-float */ +void __floatsidf(void); +void __divdf3(void); +void __ltdf2(void); + +/* for mul32 */ +void __mulsi3(void); +void __muldi3(void); + +void __modsi3(void); + +void __divdi3(void); + +void __udivdi3(void); +void __unorddf2(void); +void __adddf3(void); +void __eqdf2(void); +void __muldf3(void); +void __gedf2(void); +void __ledf2(void); +void __fixunsdfsi(void); +void __floatunsidf(void); +void __subdf3(void); +void __nedf2(void); +void __fixdfsi(void); +void __moddi3(void); +void __extendsfdf2(void); +void __truncdfsf2(void); +void __gtdf2(void); +void __umoddi3(void); +void __floatdidf(void); +void __divsf3(void); +void __fixdfdi(void); +void __floatundidf(void); +void __fixsfdi(void); +void __fixunssfdi(void); +void __fixunsdfdi(void); +void __floatdisf(void); +void __floatundisf(void); + + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS + + /* API's for soft-float */ + /* TODO: only register these symbols when Floating-Point Coprocessor + * Option is not enabled */ + REG_SYM(__floatsidf), + REG_SYM(__divdf3), + REG_SYM(__ltdf2), + + /* API's for 32-bit integer multiply */ + /* TODO: only register these symbols when 32-bit Integer Multiply Option + * is not enabled */ + REG_SYM(__mulsi3), + REG_SYM(__muldi3), + + REG_SYM(__modsi3), + REG_SYM(__divdi3), + + REG_SYM(__udivdi3), + REG_SYM(__unorddf2), + REG_SYM(__adddf3), + REG_SYM(__eqdf2), + REG_SYM(__muldf3), + REG_SYM(__gedf2), + REG_SYM(__ledf2), + REG_SYM(__fixunsdfsi), + REG_SYM(__floatunsidf), + REG_SYM(__subdf3), + REG_SYM(__nedf2), + REG_SYM(__fixdfsi), + REG_SYM(__moddi3), + REG_SYM(__extendsfdf2), + REG_SYM(__truncdfsf2), + REG_SYM(__gtdf2), + REG_SYM(__umoddi3), + REG_SYM(__floatdidf), + REG_SYM(__divsf3), + REG_SYM(__fixdfdi), + REG_SYM(__floatundidf), + REG_SYM(__fixsfdi), + REG_SYM(__fixunssfdi), + REG_SYM(__fixunsdfdi), + REG_SYM(__floatdisf), + REG_SYM(__floatundisf), +}; +/* clang-format on */ + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "xtensa"); +} + +static uint32 +get_plt_item_size(void) +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +static bool +check_reloc_offset(uint32 target_section_size, uint64 reloc_offset, + uint32 reloc_data_size, char *error_buf, + uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +/* + * CPU like esp32 can read and write data through the instruction bus, but only + * in a word aligned manner; non-word-aligned access will cause a CPU exception. + * This function uses a world aligned manner to write 16bit value to instruction + * address. + */ +static void +put_imm16_to_addr(int16 imm16, int16 *addr) +{ + int8 bytes[8]; + int32 *addr_aligned1, *addr_aligned2; + + addr_aligned1 = (int32 *)((intptr_t)addr & ~3); + + if ((intptr_t)addr % 4 != 3) { + *(int32 *)bytes = *addr_aligned1; + *(int16 *)(bytes + ((intptr_t)addr % 4)) = imm16; + *addr_aligned1 = *(int32 *)bytes; + } + else { + addr_aligned2 = (int32 *)(((intptr_t)addr + 3) & ~3); + *(int32 *)bytes = *addr_aligned1; + *(int32 *)(bytes + 4) = *addr_aligned2; + *(int16 *)(bytes + 3) = imm16; + memcpy(addr_aligned1, bytes, 8); + } +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +#if !defined(__packed) +/* + * Note: This version check is a bit relaxed. + * The __packed__ attribute has been there since gcc 2 era. + */ +#if __GNUC__ >= 3 +#define __packed __attribute__((__packed__)) +#endif +#endif + +typedef union { + struct l32r_le { + int8 other; + int16 imm16; + } __packed l; + + struct l32r_be { + int16 imm16; + int8 other; + } __packed b; +} l32r_insn_t; + +bool +apply_relocation(AOTModule *module, uint8 *target_section_addr, + uint32 target_section_size, uint64 reloc_offset, + int64 reloc_addend, uint32 reloc_type, void *symbol_addr, + int32 symbol_index, char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_XTENSA_32: + { + uint8 *insn_addr = target_section_addr + reloc_offset; +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + insn_addr = os_get_dbus_mirror((void *)insn_addr); + bh_assert(insn_addr != NULL); +#endif + int32 initial_addend; + /* (S + A) */ + if ((intptr_t)insn_addr & 3) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "instruction address unaligned."); + return false; + } + CHECK_RELOC_OFFSET(4); + initial_addend = *(int32 *)insn_addr; + *(uintptr_t *)insn_addr = (uintptr_t)symbol_addr + initial_addend + + (intptr_t)reloc_addend; + break; + } + + case R_XTENSA_SLOT0_OP: + { + uint8 *insn_addr = target_section_addr + reloc_offset; + /* Currently only l32r instruction generates R_XTENSA_SLOT0_OP + * relocation */ + l32r_insn_t *l32r_insn = (l32r_insn_t *)insn_addr; + uint8 *reloc_addr; + int32 relative_offset /*, initial_addend */; + int16 imm16; + + CHECK_RELOC_OFFSET(3); /* size of l32r instruction */ + + /* + imm16 = is_little_endian() ? + l32r_insn->l.imm16 : l32r_insn->b.imm16; + initial_addend = (int32)imm16 << 2; + */ + + reloc_addr = + (uint8 *)((uintptr_t)symbol_addr + (intptr_t)reloc_addend); + + if ((intptr_t)reloc_addr & 3) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "relocation address unaligned."); + return false; + } + + relative_offset = + (int32)((intptr_t)reloc_addr + - (((intptr_t)insn_addr + 3) & ~(intptr_t)3)); + /* relative_offset += initial_addend; */ + + /* check relative offset boundary */ + if (relative_offset < -256 * BH_KB || relative_offset > -4) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range.\n" + "Try using `wamrc --size-level=0` to generate " + ".literal island."); + return false; + } + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + insn_addr = os_get_dbus_mirror((void *)insn_addr); + bh_assert(insn_addr != NULL); + l32r_insn = (l32r_insn_t *)insn_addr; +#endif + imm16 = (int16)(relative_offset >> 2); + + /* write back the imm16 to the l32r instruction */ + + /* GCC >= 9 complains if we have a pointer that could be + * unaligned. This can happen because the struct is packed. + * These pragma are to suppress the warnings because the + * function put_imm16_to_addr already handles unaligned + * pointers correctly. */ +#if __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif + if (is_little_endian()) + put_imm16_to_addr(imm16, &l32r_insn->l.imm16); + else + put_imm16_to_addr(imm16, &l32r_insn->b.imm16); +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + (int)reloc_type); + return false; + } + + return true; +} diff --git a/core/iwasm/aot/debug/LICENSE_NUTTX b/core/iwasm/aot/debug/LICENSE_NUTTX new file mode 100644 index 0000000000..43fa6abd19 --- /dev/null +++ b/core/iwasm/aot/debug/LICENSE_NUTTX @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/core/iwasm/aot/debug/NOTICE_NUTTX b/core/iwasm/aot/debug/NOTICE_NUTTX new file mode 100644 index 0000000000..17227cb29b --- /dev/null +++ b/core/iwasm/aot/debug/NOTICE_NUTTX @@ -0,0 +1,5 @@ +Apache NuttX +Copyright 2020 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/core/iwasm/aot/debug/elf.h b/core/iwasm/aot/debug/elf.h new file mode 100644 index 0000000000..9bdad6521d --- /dev/null +++ b/core/iwasm/aot/debug/elf.h @@ -0,0 +1,368 @@ +/**************************************************************************** + * include/elf.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_ELF_H +#define __INCLUDE_ELF_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#define EI_NIDENT 16 /* Size of e_ident[] */ + +/* NOTE: elf64.h and elf32.h refer EI_NIDENT defined above */ + +#include "elf64.h" +#include "elf32.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Values for Elf_Ehdr::e_type */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* Values for Elf_Ehdr::e_machine (most of this were not included in the + * original SCO document but have been gleaned from elsewhere). + */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#define EM_486 6 /* Intel 486+ */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 Big-Endian */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ +#define EM_PARISC 15 /* HPPA */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC64 */ +#define EM_ARM 40 /* ARM */ +#define EM_SH 42 /* SuperH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_H8_300 46 +#define EM_IA_64 50 /* HP/Intel IA-64 */ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_S390 22 /* IBM S/390 */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Renesas M32R */ +#define EM_XTENSA 94 /* Tensilica Xtensa */ +#define EM_RISCV 243 /* RISC-V */ +#define EM_ALPHA 0x9026 +#define EM_CYGNUS_V850 0x9080 +#define EM_CYGNUS_M32R 0x9041 +#define EM_S390_OLD 0xa390 +#define EM_FRV 0x5441 + +/* Values for Elf_Ehdr::e_version */ + +#define EV_NONE 0 /* Invalid version */ +#define EV_CURRENT 1 /* The current version */ + +/* Table 2. Ehe ELF identifier */ + +#define EI_MAG0 0 /* File identification */ +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 /* File class */ +#define EI_DATA 5 /* Data encoding */ +#define EI_VERSION 6 /* File version */ +#define EI_OSABI 7 /* OS ABI */ +#define EI_PAD 8 /* Start of padding bytes */ + +/* EI_NIDENT is defined in "Included Files" section */ + +#define EI_MAGIC_SIZE 4 +#define EI_MAGIC \ + { \ + 0x7f, 'E', 'L', 'F' \ + } + +#define ELFMAG0 0x7f /* EI_MAG */ +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' +#define ELFMAG "\177ELF" + +/* Table 3. Values for EI_CLASS */ + +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ + +/* Table 4. Values for EI_DATA */ + +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB \ + 1 /* Least significant byte occupying the lowest address \ + */ +#define ELFDATA2MSB 2 /* Most significant byte occupying the lowest address */ + +/* Table 6. Values for EI_OSABI */ + +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU +/* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#ifndef ELF_OSABI +#define ELF_OSABI ELFOSABI_NONE +#endif + +/* Table 7: Special Section Indexes */ + +#define SHN_UNDEF 0 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 + +/* Figure 4-9: Section Types, sh_type */ + +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +/* Figure 4-11: Section Attribute Flags, sh_flags */ + +#define SHF_WRITE 1 +#define SHF_ALLOC 2 +#define SHF_EXECINSTR 4 +#define SHF_MASKPROC 0xf0000000 + +/* Figure 4-16: Symbol Binding, ELF_ST_BIND */ + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +/* Figure 4-17: Symbol Types, ELF_ST_TYPE */ + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +/* Figure 5-2: Segment Types, p_type */ + +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +/* Figure 5-3: Segment Flag Bits, p_flags */ + +#define PF_X 1 /* Execute */ +#define PF_W 2 /* Write */ +#define PF_R 4 /* Read */ +#define PF_MASKPROC 0xf0000000 /* Unspecified */ + +/* Figure 5-10: Dynamic Array Tags, d_tag */ + +#define DT_NULL 0 /* d_un=ignored */ +#define DT_NEEDED 1 /* d_un=d_val */ +#define DT_PLTRELSZ 2 /* d_un=d_val */ +#define DT_PLTGOT 3 /* d_un=d_ptr */ +#define DT_HASH 4 /* d_un=d_ptr */ +#define DT_STRTAB 5 /* d_un=d_ptr */ +#define DT_SYMTAB 6 /* d_un=d_ptr */ +#define DT_RELA 7 /* d_un=d_ptr */ +#define DT_RELASZ 8 /* d_un=d_val */ +#define DT_RELAENT 9 /* d_un=d_val */ +#define DT_STRSZ 10 /* d_un=d_val */ +#define DT_SYMENT 11 /* d_un=d_val */ +#define DT_INIT 12 /* d_un=d_ptr */ +#define DT_FINI 13 /* d_un=d_ptr */ +#define DT_SONAME 14 /* d_un=d_val */ +#define DT_RPATH 15 /* d_un=d_val */ +#define DT_SYMBOLIC 16 /* d_un=ignored */ +#define DT_REL 17 /* d_un=d_ptr */ +#define DT_RELSZ 18 /* d_un=d_val */ +#define DT_RELENT 19 /* d_un=d_val */ +#define DT_PLTREL 20 /* d_un=d_val */ +#define DT_DEBUG 21 /* d_un=d_ptr */ +#define DT_TEXTREL 22 /* d_un=ignored */ +#define DT_JMPREL 23 /* d_un=d_ptr */ +#define DT_BINDNOW 24 /* d_un=ignored */ +#define DT_LOPROC 0x70000000 /* d_un=unspecified */ +#define DT_HIPROC 0x7fffffff /* d_un= unspecified */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_PRFPREG 2 /* Contains copy of fpregset struct. */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_SIGINFO 0x53494749 +/* Contains copy of siginfo_t, + * size might increase + */ +#define NT_FILE 0x46494c45 +/* Contains information about mapped + * files + */ +#define NT_PRXFPREG 0x46e62b7f +/* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_PPC_TAR 0x103 /* Target Address Register */ +#define NT_PPC_PPR 0x104 /* Program Priority Register */ +#define NT_PPC_DSCR 0x105 /* Data Stream Control Register */ +#define NT_PPC_EBB 0x106 /* Event Based Branch Registers */ +#define NT_PPC_PMU 0x107 /* Performance Monitor Registers */ +#define NT_PPC_TM_CGPR 0x108 /* TM checkpointed GPR Registers */ +#define NT_PPC_TM_CFPR 0x109 /* TM checkpointed FPR Registers */ +#define NT_PPC_TM_CVMX 0x10a /* TM checkpointed VMX Registers */ +#define NT_PPC_TM_CVSX 0x10b /* TM checkpointed VSX Registers */ +#define NT_PPC_TM_SPR 0x10c /* TM Special Purpose Registers */ +#define NT_PPC_TM_CTAR \ + 0x10d /* TM checkpointed Target Address \ + * Register \ + */ +#define NT_PPC_TM_CPPR \ + 0x10e /* TM checkpointed Program Priority \ + * Register \ + */ +#define NT_PPC_TM_CDSCR \ + 0x10f /* TM checkpointed Data Stream Control \ + * Register \ + */ +#define NT_PPC_PKEY \ + 0x110 /* Memory Protection Keys \ + * registers. \ + */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ +#define NT_S390_VXRS_LOW \ + 0x309 /* s390 vector registers 0-15 \ + * upper half. \ + */ +#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31. */ +#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers. */ +#define NT_S390_GS_BC \ + 0x30c /* s390 guarded storage \ + * broadcast control block. \ + */ +#define NT_S390_RI_CB 0x30d /* s390 runtime instrumentation. */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ +#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ +#define NT_ARM_SVE \ + 0x405 /* ARM Scalable Vector Extension \ + * registers \ + */ +#define NT_ARM_PAC_MASK \ + 0x406 /* ARM pointer authentication \ + * code masks. \ + */ +#define NT_ARM_PACA_KEYS \ + 0x407 /* ARM pointer authentication \ + * address keys. \ + */ +#define NT_ARM_PACG_KEYS \ + 0x408 /* ARM pointer authentication \ + * generic key. \ + */ +#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */ +#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */ +#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */ +#define NT_MIPS_MSA 0x802 /* MIPS SIMD registers. */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + +#endif /* __INCLUDE_ELF_H */ diff --git a/core/iwasm/aot/debug/elf32.h b/core/iwasm/aot/debug/elf32.h new file mode 100644 index 0000000000..b4b27948ea --- /dev/null +++ b/core/iwasm/aot/debug/elf32.h @@ -0,0 +1,161 @@ +/**************************************************************************** + * include/elf32.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_ELF32_H +#define __INCLUDE_ELF32_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ELF32_ST_BIND(i) ((i) >> 4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b, t) (((b) << 4) | ((t)&0xf)) + +/* Definitions for Elf32_Rel*::r_info */ + +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((i)&0xff) +#define ELF32_R_INFO(s, t) (((s) << 8) | ((t)&0xff)) + +#if 0 +#define ELF_R_SYM(i) ELF32_R_SYM(i) +#endif + +/**************************************************************************** + * Public Type Definitions + ****************************************************************************/ + +/* Figure 4.2: 32-Bit Data Types */ + +typedef uint32_t Elf32_Addr; /* Unsigned program address */ +typedef uint16_t Elf32_Half; /* Unsigned medium integer */ +typedef uint32_t Elf32_Off; /* Unsigned file offset */ +typedef int32_t Elf32_Sword; /* Signed large integer */ +typedef uint32_t Elf32_Word; /* Unsigned large integer */ + +/* Figure 4-3: ELF Header */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +/* Figure 4-8: Section Header */ + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +/* Figure 4-15: Symbol Table Entry */ + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +/* Figure 4-19: Relocation Entries */ + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Figure 5-1: Program Header */ + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +/* Figure 5-7: Note Information */ + +typedef struct { + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +/* Figure 5-9: Dynamic Structure */ + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +#if 0 +typedef Elf32_Addr Elf_Addr; +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Rel Elf_Rel; +typedef Elf32_Rela Elf_Rela; +typedef Elf32_Nhdr Elf_Nhdr; +typedef Elf32_Phdr Elf_Phdr; +typedef Elf32_Sym Elf_Sym; +typedef Elf32_Shdr Elf_Shdr; +typedef Elf32_Word Elf_Word; +#endif + +#endif /* __INCLUDE_ELF32_H */ diff --git a/core/iwasm/aot/debug/elf64.h b/core/iwasm/aot/debug/elf64.h new file mode 100644 index 0000000000..499c737c11 --- /dev/null +++ b/core/iwasm/aot/debug/elf64.h @@ -0,0 +1,161 @@ +/**************************************************************************** + * include/elf64.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_ELF64_H +#define __INCLUDE_ELF64_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* See ELF-64 Object File Format: Version 1.5 Draft 2 */ + +/* Definitions for Elf64_Rel*::r_info */ + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i)&0xffffffffL) +#define ELF64_R_INFO(s, t) (((s) << 32) + ((t)&0xffffffffL)) + +#if 0 +#define ELF_R_SYM(i) ELF64_R_SYM(i) +#endif + +/**************************************************************************** + * Public Type Definitions + ****************************************************************************/ + +/* Table 1: ELF-64 Data Types */ + +typedef uint64_t Elf64_Addr; /* Unsigned program address */ +typedef uint64_t Elf64_Off; /* Unsigned file offset */ +typedef uint16_t Elf64_Half; /* Unsigned medium integer */ +typedef uint32_t Elf64_Word; /* Unsigned long integer */ +typedef int32_t Elf64_Sword; /* Signed integer */ +typedef uint64_t Elf64_Xword; /* Unsigned long integer */ +typedef int64_t Elf64_Sxword; /* Signed large integer */ + +/* Figure 2: ELF-64 Header */ + +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* ELF identification */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Machine type */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point address */ + Elf64_Off e_phoff; /* Program header offset */ + Elf64_Off e_shoff; /* Section header offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size */ + Elf64_Half e_phentsize; /* Size of program header entry */ + Elf64_Half e_phnum; /* Number of program header entry */ + Elf64_Half e_shentsize; /* Size of section header entry */ + Elf64_Half e_shnum; /* Number of section header entries */ + Elf64_Half e_shstrndx; /* Section name string table index */ +} Elf64_Ehdr; + +/* Figure 3: ELF-64 Section Header */ + +typedef struct { + Elf64_Word sh_name; /* Section name */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section attributes */ + Elf64_Addr sh_addr; /* Virtual address in memory */ + Elf64_Off sh_offset; /* Offset in file */ + Elf64_Xword sh_size; /* Size of section */ + Elf64_Word sh_link; /* Link to other section */ + Elf64_Word sh_info; /* Miscellaneous information */ + Elf64_Xword sh_addralign; /* Address alignment boundary */ + Elf64_Xword sh_entsize; /* Size of entries, if section has table */ +} Elf64_Shdr; + +/* Figure 4: ELF-64 Symbol Table Entry */ + +typedef struct { + Elf64_Word st_name; /* Symbol name */ + unsigned char st_info; /* Type and Binding attributes */ + unsigned char st_other; /* Reserved */ + Elf64_Half st_shndx; /* Section table index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Size of object (e.g., common) */ +} Elf64_Sym; + +/* Figure 5: ELF-64 Relocation Entries */ + +typedef struct { + Elf64_Addr r_offset; /* Address of reference */ + Elf64_Xword r_info; /* Symbol index and type of relocation */ +} Elf64_Rel; + +typedef struct { + Elf64_Addr r_offset; /* Address of reference */ + Elf64_Xword r_info; /* Symbol index and type of relocation */ + Elf64_Sxword r_addend; /* Constant part of expression */ +} Elf64_Rela; + +/* Figure 6: ELF-64 Program Header Table Entry */ + +typedef struct { + Elf64_Word p_type; /* Type of segment */ + Elf64_Word p_flags; /* Segment attributes */ + Elf64_Off p_offset; /* Offset in file */ + Elf64_Addr p_vaddr; /* Virtual address in memory */ + Elf64_Addr p_paddr; /* Reserved */ + Elf64_Word p_filesz; /* Size of segment in file */ + Elf64_Word p_memsz; /* Size of segment in memory */ + Elf64_Word p_align; /* Alignment of segment */ +} Elf64_Phdr; + +/* Figure 7. Format of a Note Section */ + +typedef struct { + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Figure 8: Dynamic Table Structure */ + +typedef struct { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +#if 0 +typedef Elf64_Addr Elf_Addr; +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Rel Elf_Rel; +typedef Elf64_Rela Elf_Rela; +typedef Elf64_Nhdr Elf_Nhdr; +typedef Elf64_Phdr Elf_Phdr; +typedef Elf64_Sym Elf_Sym; +typedef Elf64_Shdr Elf_Shdr; +typedef Elf64_Word Elf_Word; +#endif + +#endif /* __INCLUDE_ELF64_H */ diff --git a/core/iwasm/aot/debug/elf_parser.c b/core/iwasm/aot/debug/elf_parser.c new file mode 100644 index 0000000000..7b0c57b81f --- /dev/null +++ b/core/iwasm/aot/debug/elf_parser.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "elf.h" + +#include "aot_runtime.h" +#include "bh_log.h" +#include "elf_parser.h" + +bool +is_ELF(void *buf) +{ + Elf32_Ehdr *eh = (Elf32_Ehdr *)buf; + if (!strncmp((char *)eh->e_ident, "\177ELF", 4)) { + LOG_VERBOSE("the buffer is ELF entry!"); + return true; + } + LOG_VERBOSE("the buffer is not ELF entry!"); + return false; +} + +static bool +is64Bit(Elf32_Ehdr *eh) +{ + if (eh->e_ident[EI_CLASS] == ELFCLASS64) + return true; + else + return false; +} + +static bool +is32Bit(Elf32_Ehdr *eh) +{ + if (eh->e_ident[EI_CLASS] == ELFCLASS32) + return true; + else + return false; +} + +bool +is_ELF64(void *buf) +{ + Elf64_Ehdr *eh = (Elf64_Ehdr *)buf; + if (!strncmp((char *)eh->e_ident, "\177ELF", 4)) { + LOG_VERBOSE("the buffer is ELF entry!"); + return true; + } + LOG_VERBOSE("the buffer is not ELF entry!"); + return false; +} + +static void +read_section_header_table(Elf32_Ehdr *eh, Elf32_Shdr *sh_table[]) +{ + uint32_t i; + char *buf = (char *)eh; + buf += eh->e_shoff; + LOG_VERBOSE("str index = %d count=%d", eh->e_shstrndx, eh->e_shnum); + for (i = 0; i < eh->e_shnum; i++) { + sh_table[i] = (Elf32_Shdr *)buf; + buf += eh->e_shentsize; + } +} + +static void +read_section_header_table64(Elf64_Ehdr *eh, Elf64_Shdr *sh_table[]) +{ + uint32_t i; + char *buf = (char *)eh; + buf += eh->e_shoff; + + for (i = 0; i < eh->e_shnum; i++) { + sh_table[i] = (Elf64_Shdr *)buf; + buf += eh->e_shentsize; + } +} + +static char * +get_section(Elf32_Ehdr *eh, Elf32_Shdr *section_header) +{ + char *buf = (char *)eh; + return buf + section_header->sh_offset; +} + +static char * +get_section64(Elf64_Ehdr *eh, Elf64_Shdr *section_header) +{ + char *buf = (char *)eh; + return buf + section_header->sh_offset; +} + +static bool +is_text_section(const char *section_name) +{ + return !strcmp(section_name, ".text") || !strcmp(section_name, ".ltext"); +} + +bool +get_text_section(void *buf, uint64_t *offset, uint64_t *size) +{ + bool ret = false; + uint32 i; + char *sh_str; + + /* Assumption: Only one of .text or .ltext is non-empty. */ + if (is64Bit(buf)) { + Elf64_Ehdr *eh = (Elf64_Ehdr *)buf; + Elf64_Shdr **sh_table = + wasm_runtime_malloc(eh->e_shnum * sizeof(Elf64_Shdr *)); + if (sh_table) { + read_section_header_table64(eh, sh_table); + sh_str = get_section64(eh, sh_table[eh->e_shstrndx]); + for (i = 0; i < eh->e_shnum; i++) { + if (is_text_section(sh_str + sh_table[i]->sh_name)) { + *offset = sh_table[i]->sh_offset; + *size = sh_table[i]->sh_size; + sh_table[i]->sh_addr = + (Elf64_Addr)(uintptr_t)((char *)buf + + sh_table[i]->sh_offset); + ret = true; + if (*size > 0) { + break; + } + } + } + wasm_runtime_free(sh_table); + } + } + else if (is32Bit(buf)) { + Elf32_Ehdr *eh = (Elf32_Ehdr *)buf; + Elf32_Shdr **sh_table = + wasm_runtime_malloc(eh->e_shnum * sizeof(Elf32_Shdr *)); + if (sh_table) { + read_section_header_table(eh, sh_table); + sh_str = get_section(eh, sh_table[eh->e_shstrndx]); + for (i = 0; i < eh->e_shnum; i++) { + if (is_text_section(sh_str + sh_table[i]->sh_name)) { + *offset = sh_table[i]->sh_offset; + *size = sh_table[i]->sh_size; + sh_table[i]->sh_addr = + (Elf32_Addr)(uintptr_t)((char *)buf + + sh_table[i]->sh_offset); + ret = true; + if (*size > 0) { + break; + } + } + } + wasm_runtime_free(sh_table); + } + } + + return ret; +} diff --git a/core/iwasm/aot/debug/elf_parser.h b/core/iwasm/aot/debug/elf_parser.h new file mode 100644 index 0000000000..887d82fa96 --- /dev/null +++ b/core/iwasm/aot/debug/elf_parser.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _ELF_PARSER_H_ +#define _ELF_PARSER_H_ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool +is_ELF(void *buf); + +bool +is_ELF64(void *buf); + +bool +get_text_section(void *buf, uint64_t *offset, uint64_t *size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/aot/debug/jit_debug.c b/core/iwasm/aot/debug/jit_debug.c new file mode 100644 index 0000000000..9f92dd393e --- /dev/null +++ b/core/iwasm/aot/debug/jit_debug.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_log.h" +#include "bh_platform.h" +#include "../../interpreter/wasm_runtime.h" + +#include +#include +#include +#include +#include +#include + +/* This must be kept in sync with gdb/gdb/jit.h */ +#ifdef __cplusplus +extern "C" { +#endif + +/* clang-format off */ +typedef enum JITAction { + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} JITAction; +/* clang-format on */ + +typedef struct JITCodeEntry { + struct JITCodeEntry *next_; + struct JITCodeEntry *prev_; + const uint8 *symfile_addr_; + uint64 symfile_size_; +} JITCodeEntry; + +typedef struct JITDescriptor { + uint32 version_; + uint32 action_flag_; + JITCodeEntry *relevant_entry_; + JITCodeEntry *first_entry_; +} JITDescriptor; + +#if defined(_WIN32) || defined(_WIN32_) +#define attribute_noinline __declspec(noinline) +#else +#define attribute_noinline __attribute__((noinline)) +#endif + +/* LLVM has already define this */ +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) +/** + * GDB will place breakpoint into this function. + * To prevent GCC from inlining or removing it we place noinline attribute + * and inline assembler statement inside. + */ +void attribute_noinline +__jit_debug_register_code(void); + +void attribute_noinline +__jit_debug_register_code(void) +{ + int x; + *(char *)&x = '\0'; +} + +/** + * GDB will inspect contents of this descriptor. + * Static initialization is necessary to prevent GDB from seeing + * uninitialized descriptor. + */ + +JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, NULL, NULL }; +#else +extern void +__jit_debug_register_code(); +extern JITDescriptor __jit_debug_descriptor; +#endif + +/** + * Call __jit_debug_register_code indirectly via global variable. + * This gives the debugger an easy way to inject custom code to + * handle the events. + */ +void (*__jit_debug_register_code_ptr)(void) = __jit_debug_register_code; + +#ifdef __cplusplus +} +#endif + +typedef struct WASMJITDebugEngine { + korp_mutex jit_entry_lock; + bh_list jit_entry_list; +} WASMJITDebugEngine; + +typedef struct WASMJITEntryNode { + struct WASMJITEntryNode *next; + JITCodeEntry *entry; +} WASMJITEntryNode; + +static WASMJITDebugEngine *jit_debug_engine; + +static JITCodeEntry * +CreateJITCodeEntryInternal(const uint8 *symfile_addr, uint64 symfile_size) +{ + JITCodeEntry *entry; + + os_mutex_lock(&jit_debug_engine->jit_entry_lock); + + if (!(entry = wasm_runtime_malloc(sizeof(JITCodeEntry)))) { + LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory"); + os_mutex_unlock(&jit_debug_engine->jit_entry_lock); + return NULL; + } + entry->symfile_addr_ = symfile_addr; + entry->symfile_size_ = symfile_size; + entry->prev_ = NULL; + + entry->next_ = __jit_debug_descriptor.first_entry_; + if (entry->next_ != NULL) { + entry->next_->prev_ = entry; + } + __jit_debug_descriptor.first_entry_ = entry; + __jit_debug_descriptor.relevant_entry_ = entry; + + __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN; + + (*__jit_debug_register_code_ptr)(); + + os_mutex_unlock(&jit_debug_engine->jit_entry_lock); + return entry; +} + +static void +DestroyJITCodeEntryInternal(JITCodeEntry *entry) +{ + os_mutex_lock(&jit_debug_engine->jit_entry_lock); + + if (entry->prev_ != NULL) { + entry->prev_->next_ = entry->next_; + } + else { + __jit_debug_descriptor.first_entry_ = entry->next_; + } + + if (entry->next_ != NULL) { + entry->next_->prev_ = entry->prev_; + } + + __jit_debug_descriptor.relevant_entry_ = entry; + __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN; + (*__jit_debug_register_code_ptr)(); + + wasm_runtime_free(entry); + + os_mutex_unlock(&jit_debug_engine->jit_entry_lock); +} + +bool +jit_debug_engine_init(void) +{ + if (jit_debug_engine) { + return true; + } + + if (!(jit_debug_engine = wasm_runtime_malloc(sizeof(WASMJITDebugEngine)))) { + LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory"); + return false; + } + memset(jit_debug_engine, 0, sizeof(WASMJITDebugEngine)); + + if (os_mutex_init(&jit_debug_engine->jit_entry_lock) != 0) { + wasm_runtime_free(jit_debug_engine); + jit_debug_engine = NULL; + return false; + } + + bh_list_init(&jit_debug_engine->jit_entry_list); + return true; +} + +void +jit_debug_engine_destroy(void) +{ + if (jit_debug_engine) { + WASMJITEntryNode *node, *node_next; + + /* Destroy all nodes */ + node = bh_list_first_elem(&jit_debug_engine->jit_entry_list); + while (node) { + node_next = bh_list_elem_next(node); + DestroyJITCodeEntryInternal(node->entry); + bh_list_remove(&jit_debug_engine->jit_entry_list, node); + wasm_runtime_free(node); + node = node_next; + } + + /* Destroy JIT Debug Engine */ + os_mutex_destroy(&jit_debug_engine->jit_entry_lock); + wasm_runtime_free(jit_debug_engine); + jit_debug_engine = NULL; + } +} + +bool +jit_code_entry_create(const uint8 *symfile_addr, uint64 symfile_size) +{ + JITCodeEntry *entry; + WASMJITEntryNode *node; + + if (!(node = wasm_runtime_malloc(sizeof(WASMJITEntryNode)))) { + LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory"); + return false; + } + + entry = CreateJITCodeEntryInternal(symfile_addr, symfile_size); + + if (!entry) { + wasm_runtime_free(node); + return false; + } + + node->entry = entry; + os_mutex_lock(&jit_debug_engine->jit_entry_lock); + bh_list_insert(&jit_debug_engine->jit_entry_list, node); + os_mutex_unlock(&jit_debug_engine->jit_entry_lock); + return true; +} + +void +jit_code_entry_destroy(const uint8 *symfile_addr) +{ + WASMJITEntryNode *node; + + node = bh_list_first_elem(&jit_debug_engine->jit_entry_list); + while (node) { + WASMJITEntryNode *next_node = bh_list_elem_next(node); + if (node->entry->symfile_addr_ == symfile_addr) { + DestroyJITCodeEntryInternal(node->entry); + os_mutex_lock(&jit_debug_engine->jit_entry_lock); + bh_list_remove(&jit_debug_engine->jit_entry_list, node); + os_mutex_unlock(&jit_debug_engine->jit_entry_lock); + wasm_runtime_free(node); + } + node = next_node; + } +} diff --git a/core/iwasm/aot/debug/jit_debug.h b/core/iwasm/aot/debug/jit_debug.h new file mode 100644 index 0000000000..813c8b782b --- /dev/null +++ b/core/iwasm/aot/debug/jit_debug.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_DEBUG_H_ +#define _JIT_DEBUG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_debug_engine_init(void); + +void +jit_debug_engine_destroy(void); + +bool +jit_code_entry_create(const uint8 *symfile_addr, uint64 symfile_size); + +void +jit_code_entry_destroy(const uint8 *symfile_addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/aot/iwasm_aot.cmake b/core/iwasm/aot/iwasm_aot.cmake index d1bd0c610e..1084681aca 100644 --- a/core/iwasm/aot/iwasm_aot.cmake +++ b/core/iwasm/aot/iwasm_aot.cmake @@ -1,13 +1,106 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + set (IWASM_AOT_DIR ${CMAKE_CURRENT_LIST_DIR}) add_definitions (-DWASM_ENABLE_AOT=1) include_directories (${IWASM_AOT_DIR}) -file (GLOB c_source_all ${IWASM_AOT_DIR}/*.c) +list (APPEND c_source_all + ${IWASM_AOT_DIR}/aot_intrinsic.c + ${IWASM_AOT_DIR}/aot_loader.c + ${IWASM_AOT_DIR}/aot_runtime.c +) + +if (WAMR_BUILD_LINUX_PERF EQUAL 1) + list (APPEND c_source_all ${IWASM_AOT_DIR}/aot_perf_map.c) +endif () + +if (WAMR_BUILD_AOT_VALIDATOR EQUAL 1) + list (APPEND c_source_all ${IWASM_AOT_DIR}/aot_validator.c) +endif () + +if (WAMR_BUILD_WAMR_COMPILER EQUAL 1) + # AOT reloc functions are not used during AOT compilation + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_dummy.c) +elseif (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_x86_64.c) +elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_x86_32.c) +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_aarch64.c) +elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_arm.c) +elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_thumb.c) +elseif (WAMR_BUILD_TARGET STREQUAL "MIPS") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_mips.c) +elseif (WAMR_BUILD_TARGET STREQUAL "XTENSA") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_xtensa.c) +elseif (WAMR_BUILD_TARGET MATCHES "RISCV*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_riscv.c) +elseif (WAMR_BUILD_TARGET STREQUAL "ARC") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_arc.c) +else () + message (FATAL_ERROR "Build target isn't set") +endif () + +if (WAMR_BUILD_DEBUG_AOT EQUAL 1) + add_definitions(-DWASM_ENABLE_DEBUG_AOT=1) + file(GLOB debug_source ${IWASM_AOT_DIR}/debug/*.c) +endif() + +if ((WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + AND (WAMR_BUILD_PLATFORM STREQUAL "windows") + AND (NOT WAMR_DISABLE_HW_BOUND_CHECK EQUAL 1)) + include(FetchContent) + + FetchContent_Declare( + zycore + GIT_REPOSITORY https://github.com/zyantific/zycore-c.git + ) + FetchContent_GetProperties(zycore) + if (NOT zycore_POPULATED) + message ("-- Fetching zycore ..") + FetchContent_Populate(zycore) + include_directories(SYSTEM "${zycore_SOURCE_DIR}/include") + include_directories(SYSTEM "${zycore_BINARY_DIR}") + add_definitions(-DZYCORE_STATIC_BUILD=1) + add_subdirectory(${zycore_SOURCE_DIR} ${zycore_BINARY_DIR} EXCLUDE_FROM_ALL) + file (GLOB_RECURSE c_source_zycore ${zycore_SOURCE_DIR}/src/*.c) + endif () + + FetchContent_Declare( + zydis + GIT_REPOSITORY https://github.com/zyantific/zydis.git + GIT_TAG e14a07895136182a5b53e181eec3b1c6e0b434de + ) + FetchContent_GetProperties(zydis) + if (NOT zydis_POPULATED) + message ("-- Fetching zydis ..") + FetchContent_Populate(zydis) + option(ZYDIS_FEATURE_ENCODER "" OFF) + option(ZYDIS_BUILD_TOOLS "" OFF) + option(ZYDIS_BUILD_EXAMPLES "" OFF) + option(ZYDIS_BUILD_MAN "" OFF) + option(ZYDIS_BUILD_DOXYGEN "" OFF) + include_directories(SYSTEM "${zydis_BINARY_DIR}") + include_directories(SYSTEM "${zydis_SOURCE_DIR}/include") + include_directories(SYSTEM "${zydis_SOURCE_DIR}/src") + add_definitions(-DZYDIS_STATIC_BUILD=1) + add_subdirectory(${zydis_SOURCE_DIR} ${zydis_BINARY_DIR} EXCLUDE_FROM_ALL) + file (GLOB_RECURSE c_source_zydis ${zydis_SOURCE_DIR}/src/*.c) + endif () +endif () -set (IWASM_AOT_SOURCE ${c_source_all}) +set (IWASM_AOT_SOURCE ${c_source_all} ${arch_source} ${debug_source} + ${c_source_zycore} ${c_source_zydis}) diff --git a/core/iwasm/common/SConscript b/core/iwasm/common/SConscript new file mode 100644 index 0000000000..eb69744e90 --- /dev/null +++ b/core/iwasm/common/SConscript @@ -0,0 +1,25 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import re + +Import('rtconfig') + +cwd = GetCurrentDir() + +src = Glob('*.c') + +if rtconfig.ARCH == 'arm' and re.match('^cortex-m.*', rtconfig.CPU): + src += ['arch/invokeNative_thumb.s'] +else: + src.append(f"arch/invokeNative_{rtconfig.ARCH}.s") + +CPPPATH = [cwd, cwd + '/../include'] + +group = DefineGroup('iwasm_common', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/iwasm/common/arch/fneh.txt b/core/iwasm/common/arch/fneh.txt new file mode 100644 index 0000000000..965af94f2f --- /dev/null +++ b/core/iwasm/common/arch/fneh.txt @@ -0,0 +1,3 @@ +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_aarch64.s b/core/iwasm/common/arch/invokeNative_aarch64.s new file mode 100644 index 0000000000..ea5d9c7490 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_aarch64.s @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + +/* + * Arguments passed in: + * + * x0 function ptr + * x1 argv + * x2 nstacks + */ + + sub sp, sp, #0x30 + stp x19, x20, [sp, #0x20] /* save the registers */ + stp x21, x22, [sp, #0x10] + stp x23, x24, [sp, #0x0] + + mov x19, x0 /* x19 = function ptr */ + mov x20, x1 /* x20 = argv */ + mov x21, x2 /* x21 = nstacks */ + mov x22, sp /* save the sp before call function */ + + /* Fill in float-point registers */ + ldp d0, d1, [x20], #16 /* d0 = argv[0], d1 = argv[1] */ + ldp d2, d3, [x20], #16 /* d2 = argv[2], d3 = argv[3] */ + ldp d4, d5, [x20], #16 /* d4 = argv[4], d5 = argv[5] */ + ldp d6, d7, [x20], #16 /* d6 = argv[6], d7 = argv[7] */ + + /* Fill integer registers */ + ldp x0, x1, [x20], #16 /* x0 = argv[8] = exec_env, x1 = argv[9] */ + ldp x2, x3, [x20], #16 /* x2 = argv[10], x3 = argv[11] */ + ldp x4, x5, [x20], #16 /* x4 = argv[12], x5 = argv[13] */ + ldp x6, x7, [x20], #16 /* x6 = argv[14], x7 = argv[15] */ + + /* Now x20 points to stack args */ + + /* Directly call the function if no args in stack */ + cmp x21, #0 + beq call_func + + /* Fill all stack args: reserve stack space and fill one by one */ + mov x23, sp + bic sp, x23, #15 /* Ensure stack is 16 bytes aligned */ + lsl x23, x21, #3 /* x23 = nstacks * 8 */ + add x23, x23, #15 /* x23 = (x23 + 15) & ~15 */ + bic x23, x23, #15 + sub sp, sp, x23 /* reserved stack space for stack arguments */ + mov x23, sp + +loop_stack_args: /* copy stack arguments to stack */ + cmp x21, #0 + beq call_func + ldr x24, [x20], #8 + str x24, [x23], #8 + sub x21, x21, #1 + b loop_stack_args + +call_func: + mov x20, x30 /* save x30(lr) */ + blr x19 + mov sp, x22 /* restore sp which is saved before calling function*/ + +return: + mov x30, x20 /* restore x30(lr) */ + ldp x19, x20, [sp, #0x20] /* restore the registers in stack */ + ldp x21, x22, [sp, #0x10] + ldp x23, x24, [sp, #0x0] + add sp, sp, #0x30 /* restore sp */ + ret + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_aarch64_simd.s b/core/iwasm/common/arch/invokeNative_aarch64_simd.s new file mode 100644 index 0000000000..e1c7207624 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_aarch64_simd.s @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 Intel Corporation Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + +/* + * Arguments passed in: + * + * x0 function ptr + * x1 argv + * x2 nstacks + */ + + sub sp, sp, #0x30 + stp x19, x20, [sp, #0x20] /* save the registers */ + stp x21, x22, [sp, #0x10] + stp x23, x24, [sp, #0x0] + + mov x19, x0 /* x19 = function ptr */ + mov x20, x1 /* x20 = argv */ + mov x21, x2 /* x21 = nstacks */ + mov x22, sp /* save the sp before call function */ + + /* Fill in float-point registers */ + ld1 {v0.2D, v1.2D, v2.2D, v3.2D}, [x20], #64 /* v0 = argv[0], v1 = argv[1], v2 = argv[2], v3 = argv[3]*/ + ld1 {v4.2D, v5.2D, v6.2D, v7.2D}, [x20], #64 /* v4 = argv[4], v5 = argv[5], v6 = argv[6], v7 = argv[7]*/ + + /* Fill inteter registers */ + ldp x0, x1, [x20], #16 /* x0 = argv[8] = exec_env, x1 = argv[9] */ + ldp x2, x3, [x20], #16 /* x2 = argv[10], x3 = argv[11] */ + ldp x4, x5, [x20], #16 /* x4 = argv[12], x5 = argv[13] */ + ldp x6, x7, [x20], #16 /* x6 = argv[14], x7 = argv[15] */ + + /* Now x20 points to stack args */ + + /* Directly call the function if no args in stack */ + cmp x21, #0 + beq call_func + + /* Fill all stack args: reserve stack space and fill one by one */ + mov x23, sp + bic sp, x23, #15 /* Ensure stack is 16 bytes aligned */ + lsl x23, x21, #3 /* x23 = nstacks * 8 */ + add x23, x23, #15 /* x23 = (x23 + 15) & ~15 */ + bic x23, x23, #15 + sub sp, sp, x23 /* reserved stack space for stack arguments */ + mov x23, sp + +loop_stack_args: /* copy stack arguments to stack */ + cmp x21, #0 + beq call_func + ldr x24, [x20], #8 + str x24, [x23], #8 + sub x21, x21, #1 + b loop_stack_args + +call_func: + mov x20, x30 /* save x30(lr) */ + blr x19 + mov sp, x22 /* restore sp which is saved before calling function*/ + +return: + mov x30, x20 /* restore x30(lr) */ + ldp x19, x20, [sp, #0x20] /* restore the registers in stack */ + ldp x21, x22, [sp, #0x10] + ldp x23, x24, [sp, #0x0] + add sp, sp, #0x30 /* restore sp */ + ret + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_arc.s b/core/iwasm/common/arch/invokeNative_arc.s new file mode 100644 index 0000000000..e277dda03a --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_arc.s @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + +/* + * Arguments passed in: + * r0: function ptr + * r1: argv + * r2: nstacks + * ARC ABI: + * r0-r7: function arguments, caller-saved + * r8-r12: temp registers, caller-saved + */ + + push_s blink /* push return addr */ + st.aw fp, [sp, -4] /* push fp */ + mov fp, sp /* fp = sp */ + + mov r8, r0 /* r8 = func_ptr */ + mov r9, r1 /* r9 = argv */ + mov r10, r2 /* r10 = nstacks */ + + ld r0, [r9, 0] /* r0 = argv[0] */ + ld r1, [r9, 4] /* r1 = argv[1] */ + ld r2, [r9, 8] /* r2 = argv[2] */ + ld r3, [r9, 12] /* r3 = argv[3] */ + ld r4, [r9, 16] /* r4 = argv[4] */ + ld r5, [r9, 20] /* r5 = argv[5] */ + ld r6, [r9, 24] /* r6 = argv[6] */ + ld r7, [r9, 28] /* r7 = argv[7] */ + + add r9, r9, 32 /* r9 = stack_args */ + breq r10, 0, call_func /* if (r10 == 0) goto call_func */ + + asl r11, r10, 2 /* r11 = nstacks * 4 */ + sub sp, sp, r11 /* sp = sp - nstacks * 4 */ + and sp, sp, ~7 /* make sp 8-byte aligned */ + mov r11, sp /* r11 = sp */ + +loop_stack_args: + breq r10, 0, call_func /* if (r10 == 0) goto call_func */ + ld r12, [r9] /* r12 = stack_args[i] */ + st r12, [r11] /* stack[i] = r12 */ + add r9, r9, 4 /* r9 = r9 + 4 */ + add r11, r11, 4 /* r11 = r11 + 4 */ + sub r10, r10, 1 /* r10 = r10 + 1 */ + j loop_stack_args + +call_func: + jl [r8] /* call function */ + + mov sp, fp /* sp = fp */ + ld.ab fp, [sp, 4] /* pop fp */ + pop_s blink /* pop return addr */ + j_s [blink] /* ret */ + nop_s + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_arm.s b/core/iwasm/common/arch/invokeNative_arm.s index 9bd5768246..afcd51449a 100644 --- a/core/iwasm/common/arch/invokeNative_arm.s +++ b/core/iwasm/common/arch/invokeNative_arm.s @@ -4,8 +4,14 @@ */ .text .align 2 - .global invokeNative - .type invokeNative,function +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ /* * Arguments passed in: @@ -15,8 +21,8 @@ * r2 argc */ -invokeNative: stmfd sp!, {r4, r5, r6, r7, lr} + sub sp, sp, #4 /* make sp 8 byte aligned */ mov ip, r0 /* ip = function ptr */ mov r4, r1 /* r4 = argv */ mov r5, r2 /* r5 = argc */ @@ -48,7 +54,6 @@ invokeNative: mov r6, r5, lsl#2 /* r6 = argc * 4 */ add r6, r6, #7 /* r6 = (r6 + 7) & ~7 */ bic r6, r6, #7 - add r6, r6, #4 /* +4 because odd(5) registers are in stack */ sub sp, sp, r6 /* reserved stack space for left arguments */ mov r7, sp @@ -65,5 +70,9 @@ call_func: add sp, sp, r6 /* restore sp */ return: + add sp, sp, #4 ldmfd sp!, {r4, r5, r6, r7, lr} bx lr +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_arm_vfp.s b/core/iwasm/common/arch/invokeNative_arm_vfp.s index 0020f28a59..52d2a72588 100644 --- a/core/iwasm/common/arch/invokeNative_arm_vfp.s +++ b/core/iwasm/common/arch/invokeNative_arm_vfp.s @@ -4,8 +4,14 @@ */ .text .align 2 - .global invokeNative - .type invokeNative,function +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ /* * Arguments passed in: @@ -15,8 +21,8 @@ * r2 nstacks */ -invokeNative: stmfd sp!, {r4, r5, r6, r7, lr} + sub sp, sp, #4 /* make sp 8 byte aligned */ mov ip, r0 /* ip = function ptr */ mov r4, r1 /* r4 = argv */ mov r5, r2 /* r5 = nstacks */ @@ -47,12 +53,12 @@ invokeNative: vldr s13, [r4, #52] vldr s14, [r4, #56] vldr s15, [r4, #60] - /* Directly call the fucntion if no args in stack */ + /* Directly call the function if no args in stack */ cmp r5, #0 beq call_func - /* Fill all stack args: reserve stack space and fill ony by one */ + /* Fill all stack args: reserve stack space and fill one by one */ add r4, r4, #64 /* r4 points to stack args */ bic sp, sp, #7 /* Ensure stack is 8 byte aligned */ mov r7, r5, lsl#2 /* r7 = nstacks * 4 */ @@ -74,6 +80,10 @@ call_func: mov sp, r6 /* restore sp */ return: + add sp, sp, #4 /* make sp 8 byte aligned */ ldmfd sp!, {r4, r5, r6, r7, lr} bx lr +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_armasm64.asm b/core/iwasm/common/arch/invokeNative_armasm64.asm new file mode 100644 index 0000000000..0abe160e61 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_armasm64.asm @@ -0,0 +1,73 @@ + ; Copyright (C) 2019 Intel Corporation. All rights reserved. + ; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + AREA |.text|, CODE, READONLY, ALIGN=2 + + EXPORT invokeNative + +; ------------------------ direct call path ------------------------ + +call_func + mov x20, x30 ; save x30(lr) + blr x19 + mov sp, x22 ; restore sp saved before function call + +return_label + mov x30, x20 ; restore x30(lr) + ldp x19, x20, [sp, #0x20] + ldp x21, x22, [sp, #0x10] + ldp x23, x24, [sp, #0x0] + add sp, sp, #0x30 + ret + +; ------------------------ stack-args path ------------------------ + +handle_stack + ; Reserve aligned stack space for stack arguments and copy them + mov x23, sp + bic sp, x23, #15 ; Ensure 16-byte alignment + lsl x23, x21, #3 ; x23 = nstacks * 8 + add x23, x23, #15 + bic x23, x23, #15 + sub sp, sp, x23 + mov x23, sp + +copy_loop + cmp x21, #0 + b.eq call_func ; when done, branch back to call path + ldr x24, [x20], #8 + str x24, [x23], #8 + sub x21, x21, #1 + b copy_loop + +; ------------------------ function entry ------------------------ + +invokeNative + sub sp, sp, #0x30 + stp x19, x20, [sp, #0x20] ; save the registers + stp x21, x22, [sp, #0x10] + stp x23, x24, [sp, #0x0] + + mov x19, x0 ; x19 = function ptr + mov x20, x1 ; x20 = argv + mov x21, x2 ; x21 = nstacks + mov x22, sp ; save the sp before call function + + ; Fill in floating-point registers + ldp d0, d1, [x20], #16 + ldp d2, d3, [x20], #16 + ldp d4, d5, [x20], #16 + ldp d6, d7, [x20], #16 + + ; Fill integer registers + ldp x0, x1, [x20], #16 ; x0 = argv[8] = exec_env, x1 = argv[9] + ldp x2, x3, [x20], #16 + ldp x4, x5, [x20], #16 + ldp x6, x7, [x20], #16 + + ; Now x20 points to stack args + cmp x21, #0 + b.ne handle_stack ; backward: there are stack args + b call_func ; backward: no stack args + + END diff --git a/core/iwasm/common/arch/invokeNative_armasm64_simd.asm b/core/iwasm/common/arch/invokeNative_armasm64_simd.asm new file mode 100644 index 0000000000..d209f10751 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_armasm64_simd.asm @@ -0,0 +1,73 @@ + ; Copyright (C) 2019 Intel Corporation. All rights reserved. + ; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + AREA |.text|, CODE, READONLY, ALIGN=2 + + EXPORT invokeNative + +; ------------------------ direct call path ------------------------ + +call_func + mov x20, x30 ; save x30(lr) + blr x19 + mov sp, x22 ; restore sp saved before function call + +return_label + mov x30, x20 ; restore x30(lr) + ldp x19, x20, [sp, #0x20] + ldp x21, x22, [sp, #0x10] + ldp x23, x24, [sp, #0x0] + add sp, sp, #0x30 + ret + +; ------------------------ stack-args path ------------------------ + +handle_stack + ; Reserve aligned stack space for stack arguments and copy them + mov x23, sp + bic sp, x23, #15 ; Ensure 16-byte alignment + lsl x23, x21, #3 ; x23 = nstacks * 8 + add x23, x23, #15 + bic x23, x23, #15 + sub sp, sp, x23 + mov x23, sp + +copy_loop + cmp x21, #0 + b.eq call_func ; when done, branch back to call path + ldr x24, [x20], #8 + str x24, [x23], #8 + sub x21, x21, #1 + b copy_loop + +; ------------------------ function entry ------------------------ + +invokeNative + sub sp, sp, #0x30 + stp x19, x20, [sp, #0x20] ; save the registers + stp x21, x22, [sp, #0x10] + stp x23, x24, [sp, #0x0] + + mov x19, x0 ; x19 = function ptr + mov x20, x1 ; x20 = argv + mov x21, x2 ; x21 = nstacks + mov x22, sp ; save the sp before call function + + ; Fill in floating-point registers + ; v0 = argv[0], v1 = argv[1], v2 = argv[2], v3 = argv[3] + ld1 {v0.2D, v1.2D, v2.2D, v3.2D}, [x20], #64 + ; v4 = argv[4], v5 = argv[5], v6 = argv[6], v7 = argv[7] + ld1 {v4.2D, v5.2D, v6.2D, v7.2D}, [x20], #64 + + ; Fill integer registers + ldp x0, x1, [x20], #16 ; x0 = argv[8] = exec_env, x1 = argv[9] + ldp x2, x3, [x20], #16 + ldp x4, x5, [x20], #16 + ldp x6, x7, [x20], #16 + + ; Now x20 points to stack args + cmp x21, #0 + b.ne handle_stack ; (backward) there are stack args + b call_func ; (backward) no stack args + + END diff --git a/core/iwasm/common/arch/invokeNative_em64.asm b/core/iwasm/common/arch/invokeNative_em64.asm new file mode 100644 index 0000000000..df8115397b --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_em64.asm @@ -0,0 +1,62 @@ +; +; Copyright (C) 2019 Intel Corporation. All rights reserved. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; + +_TEXT SEGMENT + ; rcx func_ptr + ; rdx argv + ; r8 n_stacks + +invokeNative PROC + push rbp + mov rbp, rsp + + mov r10, rcx ; func_ptr + mov rax, rdx ; argv + mov rcx, r8 ; n_stacks + +; fill all fp args + movsd xmm0, qword ptr [rax + 0] + movsd xmm1, qword ptr [rax + 8] + movsd xmm2, qword ptr [rax + 16] + movsd xmm3, qword ptr [rax + 24] + +; check for stack args + cmp rcx, 0 + jz cycle_end + + mov rdx, rsp + and rdx, 15 + jz no_abort + int 3 +no_abort: + mov rdx, rcx + and rdx, 1 + shl rdx, 3 + sub rsp, rdx + +; store stack args + lea r9, qword ptr [rax + rcx * 8 + 56] + sub r9, rsp ; offset +cycle: + push qword ptr [rsp + r9] + loop cycle + +cycle_end: + mov rcx, [rax + 32] + mov rdx, [rax + 40] + mov r8, [rax + 48] + mov r9, [rax + 56] + + sub rsp, 32 ; shadow space + + call r10 + leave + ret + +invokeNative ENDP + +_TEXT ENDS + +END diff --git a/core/iwasm/common/arch/invokeNative_em64.s b/core/iwasm/common/arch/invokeNative_em64.s index 739e84e4ce..fa34c2eeec 100644 --- a/core/iwasm/common/arch/invokeNative_em64.s +++ b/core/iwasm/common/arch/invokeNative_em64.s @@ -62,3 +62,6 @@ push_args_end: leave ret +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_em64_simd.asm b/core/iwasm/common/arch/invokeNative_em64_simd.asm new file mode 100644 index 0000000000..084a0f6671 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_em64_simd.asm @@ -0,0 +1,62 @@ +; +; Copyright (C) 2019 Intel Corporation. All rights reserved. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; + +_TEXT SEGMENT + ; rcx func_ptr + ; rdx argv + ; r8 n_stacks + +invokeNative PROC + push rbp + mov rbp, rsp + + mov r10, rcx ; func_ptr + mov rax, rdx ; argv + mov rcx, r8 ; n_stacks + +; fill all fp args + movdqu xmm0, xmmword ptr [rax + 0] + movdqu xmm1, xmmword ptr [rax + 16] + movdqu xmm2, xmmword ptr [rax + 32] + movdqu xmm3, xmmword ptr [rax + 48] + +; check for stack args + cmp rcx, 0 + jz cycle_end + + mov rdx, rsp + and rdx, 15 + jz no_abort + int 3 +no_abort: + mov rdx, rcx + and rdx, 1 + shl rdx, 3 + sub rsp, rdx + +; store stack args + lea r9, qword ptr [rax + rcx * 8 + 88] + sub r9, rsp ; offset +cycle: + push qword ptr [rsp + r9] + loop cycle + +cycle_end: + mov rcx, [rax + 64] + mov rdx, [rax + 72] + mov r8, [rax + 80] + mov r9, [rax + 88] + + sub rsp, 32 ; shadow space + + call r10 + leave + ret + +invokeNative ENDP + +_TEXT ENDS + +END diff --git a/core/iwasm/common/arch/invokeNative_em64_simd.s b/core/iwasm/common/arch/invokeNative_em64_simd.s new file mode 100644 index 0000000000..6c8d3febbc --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_em64_simd.s @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN +.globl invokeNative + .type invokeNative, @function +invokeNative: +#else +.globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + /* rdi - function ptr */ + /* rsi - argv */ + /* rdx - n_stacks */ + + push %rbp + mov %rsp, %rbp + + mov %rdx, %r10 + mov %rsp, %r11 /* Check that stack is aligned on */ + and $8, %r11 /* 16 bytes. This code may be removed */ + je check_stack_succ /* when we are sure that compiler always */ + int3 /* calls us with aligned stack */ +check_stack_succ: + mov %r10, %r11 /* Align stack on 16 bytes before pushing */ + and $1, %r11 /* stack arguments in case we have an odd */ + shl $3, %r11 /* number of stack arguments */ + sub %r11, %rsp + /* store memory args */ + movq %rdi, %r11 /* func ptr */ + movq %r10, %rcx /* counter */ + lea 128+48-8(%rsi,%rcx,8), %r10 + sub %rsp, %r10 + cmpq $0, %rcx + je push_args_end +push_args: + push 0(%rsp,%r10) + loop push_args +push_args_end: + /* fill all fp args */ + movdqu 0x00(%rsi), %xmm0 + movdqu 0x10(%rsi), %xmm1 + movdqu 0x20(%rsi), %xmm2 + movdqu 0x30(%rsi), %xmm3 + movdqu 0x40(%rsi), %xmm4 + movdqu 0x50(%rsi), %xmm5 + movdqu 0x60(%rsi), %xmm6 + movdqu 0x70(%rsi), %xmm7 + + /* fill all int args */ + movq 0x80(%rsi), %rdi + movq 0x90(%rsi), %rdx + movq 0x98(%rsi), %rcx + movq 0xa0(%rsi), %r8 + movq 0xa8(%rsi), %r9 + movq 0x88(%rsi), %rsi + + call *%r11 + leave + ret + +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_general.c b/core/iwasm/common/arch/invokeNative_general.c index 8d6e70f4f4..52ffee5ad3 100644 --- a/core/iwasm/common/arch/invokeNative_general.c +++ b/core/iwasm/common/arch/invokeNative_general.c @@ -6,11 +6,17 @@ #include "../wasm_runtime_common.h" #include "../wasm_exec_env.h" -void invokeNative(void (*native_code)(), uint32 argv[], uint32 argc) +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-non-prototype" +#endif + +void +invokeNative(void (*native_code)(), uint32 argv[], uint32 argc) { - bh_assert(argc >= sizeof(WASMExecEnv*)/sizeof(uint32)); + bh_assert(argc >= sizeof(WASMExecEnv *) / sizeof(uint32)); - switch(argc) { + switch (argc) { case 0: native_code(); break; @@ -33,54 +39,85 @@ void invokeNative(void (*native_code)(), uint32 argv[], uint32 argc) native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); break; case 7: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6]); break; case 8: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7]); break; case 9: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8]); break; case 10: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9]); break; case 11: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10]); break; case 12: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); break; case 13: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12]); break; case 14: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13]); break; case 15: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14]); break; case 16: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14], argv[15]); break; case 17: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14], argv[15], argv[16]); break; case 18: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14], argv[15], argv[16], + argv[17]); break; case 19: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17], argv[18]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14], argv[15], argv[16], + argv[17], argv[18]); break; case 20: - native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17], argv[18], argv[19]); + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], + argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], + argv[12], argv[13], argv[14], argv[15], argv[16], + argv[17], argv[18], argv[19]); break; default: { /* FIXME: If this happen, add more cases. */ - WASMExecEnv *exec_env = *(WASMExecEnv**)argv; + WASMExecEnv *exec_env = *(WASMExecEnv **)argv; WASMModuleInstanceCommon *module_inst = exec_env->module_inst; - wasm_runtime_set_exception(module_inst, "the argument number of native function exceeds maximum"); + wasm_runtime_set_exception( + module_inst, + "the argument number of native function exceeds maximum"); return; } } } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif diff --git a/core/iwasm/common/arch/invokeNative_ia32.asm b/core/iwasm/common/arch/invokeNative_ia32.asm new file mode 100644 index 0000000000..c52c8d6ed9 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_ia32.asm @@ -0,0 +1,27 @@ +; +; Copyright (C) 2019 Intel Corporation. All rights reserved. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; + + .386 + .model flat + .code +_invokeNative PROC + push ebp + mov ebp,esp + mov ecx, [ebp+16] ; ecx = argc */ + mov edx, [ebp+12] ; edx = argv */ + test ecx, ecx + jz skip_push_args ; if ecx == 0, skip pushing arguments */ + lea edx, [edx+ecx*4-4] ; edx = edx + ecx * 4 - 4 */ + sub edx,esp ; edx = edx - esp */ +loop_push: + push [esp+edx] + loop loop_push ; loop ecx counts */ +skip_push_args: + mov edx, [ebp+8] ; edx = func_ptr */ + call edx + leave + ret +_invokeNative ENDP +END \ No newline at end of file diff --git a/core/iwasm/common/arch/invokeNative_ia32.s b/core/iwasm/common/arch/invokeNative_ia32.s index 0056a53e3f..845cd93bf0 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: @@ -30,3 +35,6 @@ skip_push_args: leave ret +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_mingw_x64.s b/core/iwasm/common/arch/invokeNative_mingw_x64.s new file mode 100644 index 0000000000..cefaa28c1c --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_mingw_x64.s @@ -0,0 +1,57 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +.text +.align 2 +.globl invokeNative +invokeNative: + + # %rcx func_ptr + # %rdx argv + # %r8 n_stacks + + push %rbp + mov %rsp, %rbp + + mov %rcx, %r10 # func_ptr + mov %rdx, %rax # argv + mov %r8, %rcx # n_stacks + + # fill all fp args + movsd 0(%rax), %xmm0 + movsd 8(%rax), %xmm1 + movsd 16(%rax), %xmm2 + movsd 24(%rax), %xmm3 + + # check for stack args + cmp $0, %rcx + jz cycle_end + + mov %rsp, %rdx + and $15, %rdx + jz no_abort + int $3 +no_abort: + mov %rcx, %rdx + and $1, %rdx + shl $3, %rdx + sub %rdx, %rsp + + # store stack args + lea 56(%rax, %rcx, 8), %r9 + sub %rsp, %r9 # offset +cycle: + push (%rsp, %r9) + loop cycle + +cycle_end: + mov 32(%rax), %rcx + mov 40(%rax), %rdx + mov 48(%rax), %r8 + mov 56(%rax), %r9 + + sub $32, %rsp # shadow space + + call *%r10 + leave + ret diff --git a/core/iwasm/common/arch/invokeNative_mingw_x64_simd.s b/core/iwasm/common/arch/invokeNative_mingw_x64_simd.s new file mode 100644 index 0000000000..48ae52480a --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_mingw_x64_simd.s @@ -0,0 +1,57 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +.text +.align 2 +.globl invokeNative +invokeNative: + + # %rcx func_ptr + # %rdx argv + # %r8 n_stacks + + push %rbp + mov %rsp, %rbp + + mov %rcx, %r10 # func_ptr + mov %rdx, %rax # argv + mov %r8, %rcx # n_stacks + + # fill all fp args + movdqu 0(%rax), %xmm0 + movdqu 16(%rax), %xmm1 + movdqu 32(%rax), %xmm2 + movdqu 48(%rax), %xmm3 + + # check for stack args + cmp $0, %rcx + jz cycle_end + + mov %rsp, %rdx + and $15, %rdx + jz no_abort + int $3 +no_abort: + mov %rcx, %rdx + and $1, %rdx + shl $3, %rdx + sub %rdx, %rsp + + # store stack args + lea 88(%rax, %rcx, 8), %r9 + sub %rsp, %r9 # offset +cycle: + push (%rsp, %r9) + loop cycle + +cycle_end: + mov 64(%rax), %rcx + mov 72(%rax), %rdx + mov 80(%rax), %r8 + mov 88(%rax), %r9 + + sub $32, %rsp # shadow space + + call *%r10 + leave + ret diff --git a/core/iwasm/common/arch/invokeNative_mips.s b/core/iwasm/common/arch/invokeNative_mips.s index 645f4f2ec4..d6e48114ea 100644 --- a/core/iwasm/common/arch/invokeNative_mips.s +++ b/core/iwasm/common/arch/invokeNative_mips.s @@ -72,3 +72,6 @@ done: j $31 .end invokeNative +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_osx_universal.s b/core/iwasm/common/arch/invokeNative_osx_universal.s new file mode 100644 index 0000000000..fadaae5646 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_osx_universal.s @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if defined(__aarch64__) +#if WASM_ENABLE_SIMD == 0 +#include "invokeNative_aarch64.s" +#else +#include "invokeNative_aarch64_simd.s" +#endif +#else +#if WASM_ENABLE_SIMD == 0 +#include "invokeNative_em64.s" +#else +#include "invokeNative_em64_simd.s" +#endif +#endif diff --git a/core/iwasm/common/arch/invokeNative_riscv.S b/core/iwasm/common/arch/invokeNative_riscv.S new file mode 100644 index 0000000000..87039f44c1 --- /dev/null +++ b/core/iwasm/common/arch/invokeNative_riscv.S @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * The float abi macros used below are from risc-v c api: + * https://github.com/riscv/riscv-c-api-doc/blob/master/riscv-c-api.md + * + */ + +#if defined(__riscv_float_abi_soft) +#define RV_FPREG_SIZE 0 +#elif defined(__riscv_float_abi_single) +#define RV_OP_LOADFPREG flw +#define RV_OP_STROEFPREG fsw +#define RV_FPREG_SIZE 4 +#elif defined(__riscv_float_abi_double) +#define RV_OP_LOADFPREG fld +#define RV_OP_STROEFPREG fsd +#define RV_FPREG_SIZE 8 +#endif + +#if __riscv_xlen == 32 +#define RV_OP_LOADREG lw +#define RV_OP_STOREREG sw +#define RV_REG_SIZE 4 +#define RV_REG_SHIFT 2 +#define RV_FP_OFFSET (8 * RV_REG_SIZE) +#define RV_INT_OFFSET 0 +#else +#define RV_OP_LOADREG ld +#define RV_OP_STOREREG sd +#define RV_REG_SIZE 8 +#define RV_REG_SHIFT 3 +#define RV_FP_OFFSET 0 +#define RV_INT_OFFSET (8 * RV_FPREG_SIZE) +#endif + + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + +/* + * Arguments passed in: + * + * a0 function ptr + * a1 argv + * a2 nstacks + */ + +/* + * sp (stack pointer) + * |- sd/sw to store 64/32-bit values from register to memory + * |- ld/lw to load from stack to register + * fp/s0 (frame pointer) + * a0-a7 (8 integer arguments) + * |- sd/sw to store + * |- ld/lw to load + * fa0-a7 (8 float arguments) + * |- fsd/fsw to store + * |- fld/fsw to load + * t0-t6 (temporaries regisgers) + * |- caller saved + */ + + /* reserve space on stack to save return address and frame pointer */ + addi sp, sp, - 2 * RV_REG_SIZE + RV_OP_STOREREG fp, 0 * RV_REG_SIZE(sp) /* save frame pointer */ + RV_OP_STOREREG ra, 1 * RV_REG_SIZE(sp) /* save return address */ + + mv fp, sp /* set frame pointer to bottom of fixed frame */ + + /* save function ptr, argv & nstacks */ + mv t0, a0 /* t0 = function ptr */ + mv t1, a1 /* t1 = argv array address */ + mv t2, a2 /* t2 = nstack */ + +#ifndef __riscv_float_abi_soft + /* fill in fa0-7 float-registers*/ + RV_OP_LOADFPREG fa0, RV_FP_OFFSET + 0 * RV_FPREG_SIZE(t1) /* fa0 */ + RV_OP_LOADFPREG fa1, RV_FP_OFFSET + 1 * RV_FPREG_SIZE(t1) /* fa1 */ + RV_OP_LOADFPREG fa2, RV_FP_OFFSET + 2 * RV_FPREG_SIZE(t1) /* fa2 */ + RV_OP_LOADFPREG fa3, RV_FP_OFFSET + 3 * RV_FPREG_SIZE(t1) /* fa3 */ + RV_OP_LOADFPREG fa4, RV_FP_OFFSET + 4 * RV_FPREG_SIZE(t1) /* fa4 */ + RV_OP_LOADFPREG fa5, RV_FP_OFFSET + 5 * RV_FPREG_SIZE(t1) /* fa5 */ + RV_OP_LOADFPREG fa6, RV_FP_OFFSET + 6 * RV_FPREG_SIZE(t1) /* fa6 */ + RV_OP_LOADFPREG fa7, RV_FP_OFFSET + 7 * RV_FPREG_SIZE(t1) /* fa7 */ +#endif + + /* fill in a0-7 integer-registers*/ + RV_OP_LOADREG a0, RV_INT_OFFSET + 0 * RV_REG_SIZE(t1) /* a0 */ + RV_OP_LOADREG a1, RV_INT_OFFSET + 1 * RV_REG_SIZE(t1) /* a1 */ + RV_OP_LOADREG a2, RV_INT_OFFSET + 2 * RV_REG_SIZE(t1) /* a2 */ + RV_OP_LOADREG a3, RV_INT_OFFSET + 3 * RV_REG_SIZE(t1) /* a3 */ + RV_OP_LOADREG a4, RV_INT_OFFSET + 4 * RV_REG_SIZE(t1) /* a4 */ + RV_OP_LOADREG a5, RV_INT_OFFSET + 5 * RV_REG_SIZE(t1) /* a5 */ + RV_OP_LOADREG a6, RV_INT_OFFSET + 6 * RV_REG_SIZE(t1) /* a6 */ + RV_OP_LOADREG a7, RV_INT_OFFSET + 7 * RV_REG_SIZE(t1) /* a7 */ + + /* t1 points to stack args */ + + /* RV_FPREG_SIZE is zero when __riscv_float_abi_soft defined */ + addi t1, t1, RV_REG_SIZE * 8 + RV_FPREG_SIZE * 8 + + /* directly call the function if no args in stack, + x0 always holds 0 */ + beq t2, x0, call_func + + /* reserve enough stack space for function arguments */ + sll t3, t2, RV_REG_SHIFT /* shift left 3 bits. t3 = n_stacks * 8 */ + sub sp, sp, t3 + + /* make 16-byte aligned */ + li t3, 15 + not t3, t3 + and sp, sp, t3 + + /* save sp in t4 register */ + mv t4, sp + + /* copy left arguments from caller stack to own frame stack */ +loop_stack_args: + beq t2, x0, call_func + RV_OP_LOADREG t5, 0(t1) /* load stack argument, t5 = argv[i] */ + RV_OP_STOREREG t5, 0(t4) /* store t5 to reserved stack, sp[j] = t5 */ + addi t1, t1, RV_REG_SIZE /* move to next stack argument */ + addi t4, t4, RV_REG_SIZE /* move to next stack pointer */ + addi t2, t2, -1 /* decrease t2 every loop, nstacks = nstacks -1 */ + j loop_stack_args + +call_func: + jalr t0 + + /* restore registers pushed in stack or saved in another register */ +return: + mv sp, fp /* restore sp saved in fp before function call */ + RV_OP_LOADREG fp, 0 * RV_REG_SIZE(sp) /* load previous frame pointer to fp register */ + RV_OP_LOADREG ra, 1 * RV_REG_SIZE(sp) /* load previous return address to ra register */ + addi sp, sp, 2 * RV_REG_SIZE /* pop frame, restore sp */ + jr ra diff --git a/core/iwasm/common/arch/invokeNative_thumb.s b/core/iwasm/common/arch/invokeNative_thumb.s index 571c6a2623..9a3f651b40 100644 --- a/core/iwasm/common/arch/invokeNative_thumb.s +++ b/core/iwasm/common/arch/invokeNative_thumb.s @@ -4,9 +4,15 @@ */ .text .align 2 - .global invokeNative - .type invokeNative,function - +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + .cfi_startproc /* * Arguments passed in: * @@ -15,37 +21,43 @@ * r2 argc */ -invokeNative: push {r4, r5, r6, r7} push {lr} + sub sp, sp, #4 /* make sp 8 byte aligned */ + .cfi_def_cfa_offset 24 + .cfi_offset lr, -20 + .cfi_offset r4, -16 + .cfi_offset r5, -12 + .cfi_offset r6, -8 + .cfi_offset r7, -4 mov ip, r0 /* ip = function ptr */ mov r4, r1 /* r4 = argv */ mov r5, r2 /* r5 = argc */ cmp r5, #1 /* at least one argument required: exec_env */ - blt return + blt .Lreturn mov r6, #0 /* increased stack size */ ldr r0, [r4] /* r0 = argv[0] = exec_env */ add r4, r4, #4 /* r4 += 4 */ cmp r5, #1 - beq call_func + beq .Lcall_func ldr r1, [r4] /* r1 = argv[1] */ add r4, r4, #4 cmp r5, #2 - beq call_func + beq .Lcall_func ldr r2, [r4] /* r2 = argv[2] */ add r4, r4, #4 cmp r5, #3 - beq call_func + beq .Lcall_func ldr r3, [r4] /* r3 = argv[3] */ add r4, r4, #4 cmp r5, #4 - beq call_func + beq .Lcall_func sub r5, r5, #4 /* argc -= 4, now we have r0 ~ r3 */ @@ -60,25 +72,31 @@ invokeNative: mov sp, r7 mov lr, r2 /* save r2 */ -loop_args: /* copy left arguments to stack */ + +.Lloop_args: /* copy left arguments to stack */ cmp r5, #0 - beq call_func1 + beq .Lcall_func1 ldr r2, [r4] add r4, r4, #4 str r2, [r7] add r7, r7, #4 sub r5, r5, #1 - b loop_args + b .Lloop_args -call_func1: +.Lcall_func1: mov r2, lr /* restore r2 */ -call_func: +.Lcall_func: blx ip add sp, sp, r6 /* restore sp */ -return: +.Lreturn: + add sp, sp, #4 /* make sp 8 byte aligned */ pop {r3} pop {r4, r5, r6, r7} mov lr, r3 bx lr + .cfi_endproc +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_thumb_vfp.s b/core/iwasm/common/arch/invokeNative_thumb_vfp.s index faab2139f6..6277c0012b 100644 --- a/core/iwasm/common/arch/invokeNative_thumb_vfp.s +++ b/core/iwasm/common/arch/invokeNative_thumb_vfp.s @@ -4,9 +4,15 @@ */ .text .align 2 - .global invokeNative - .type invokeNative,function - +#ifndef BH_PLATFORM_DARWIN + .globl invokeNative + .type invokeNative, function +invokeNative: +#else + .globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + .cfi_startproc /* * Arguments passed in: * @@ -15,13 +21,20 @@ * r2 nstacks */ -invokeNative: push {r4, r5, r6, r7} push {lr} + sub sp, sp, #4 /* make sp 8 byte aligned */ + .cfi_def_cfa_offset 24 + .cfi_offset lr, -20 + .cfi_offset r4, -16 + .cfi_offset r5, -12 + .cfi_offset r6, -8 + .cfi_offset r7, -4 mov ip, r0 /* ip = function ptr */ mov r4, r1 /* r4 = argv */ mov r5, r2 /* r5 = nstacks */ mov r7, sp + .cfi_def_cfa r7, 24 /* Fill all int args */ ldr r0, [r4, #0] /* r0 = *(int*)&argv[0] = exec_env */ @@ -49,9 +62,9 @@ invokeNative: vldr s13, [r4, #52] vldr s14, [r4, #56] vldr s15, [r4, #60] - /* Directly call the fucntion if no args in stack */ + /* Directly call the function if no args in stack */ cmp r5, #0 - beq call_func + beq .Lcall_func mov lr, r2 /* save r2 */ @@ -67,27 +80,32 @@ invokeNative: mov r7, sp mov sp, r6 -loop_stack_args: /* copy stack arguments to stack */ +.Lloop_stack_args: /* copy stack arguments to stack */ cmp r5, #0 - beq call_func1 + beq .Lcall_func1 ldr r2, [r4] /* Note: caller should insure int64 and */ add r4, r4, #4 /* double are placed in 8 bytes aligned address */ str r2, [r6] add r6, r6, #4 sub r5, r5, #1 - b loop_stack_args + b .Lloop_stack_args -call_func1: +.Lcall_func1: mov r2, lr /* restore r2 */ -call_func: +.Lcall_func: blx ip mov sp, r7 /* restore sp */ -return: +.Lreturn: + add sp, sp, #4 /* make sp 8 byte aligned */ pop {r3} pop {r4, r5, r6, r7} mov lr, r3 bx lr + .cfi_endproc +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/arch/invokeNative_xtensa.s b/core/iwasm/common/arch/invokeNative_xtensa.s index ce03f12c1c..9bc094d3ca 100644 --- a/core/iwasm/common/arch/invokeNative_xtensa.s +++ b/core/iwasm/common/arch/invokeNative_xtensa.s @@ -72,3 +72,6 @@ call_func: return: retw.n +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/core/iwasm/common/gc/gc_common.c b/core/iwasm/common/gc/gc_common.c new file mode 100644 index 0000000000..7c2c154337 --- /dev/null +++ b/core/iwasm/common/gc/gc_common.c @@ -0,0 +1,1002 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../wasm_runtime_common.h" +#include "gc_export.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif + +static bool +wasm_ref_type_normalize(wasm_ref_type_t *ref_type) +{ + wasm_value_type_t value_type = ref_type->value_type; + int32 heap_type = ref_type->heap_type; + + if (!((value_type >= VALUE_TYPE_I16 && value_type <= VALUE_TYPE_I32) + || ((value_type >= (uint8)REF_TYPE_ARRAYREF + && value_type <= (uint8)REF_TYPE_NULLFUNCREF) + || (value_type >= (uint8)REF_TYPE_HT_NULLABLE + && value_type <= (uint8)REF_TYPE_HT_NON_NULLABLE) +#if WASM_ENABLE_STRINGREF != 0 + || (value_type >= (uint8)REF_TYPE_STRINGVIEWWTF8 + && value_type <= (uint8)REF_TYPE_STRINGREF) + || (value_type >= (uint8)REF_TYPE_STRINGVIEWITER + && value_type <= (uint8)REF_TYPE_STRINGVIEWWTF16) +#endif + ))) { + return false; + } + if (value_type == VALUE_TYPE_HT_NULLABLE_REF + || value_type == VALUE_TYPE_HT_NON_NULLABLE_REF) { + if (heap_type < 0 && !wasm_is_valid_heap_type(heap_type)) { + return false; + } + } + + if (value_type != REF_TYPE_HT_NULLABLE) { + ref_type->nullable = false; + } + else { + if (wasm_is_valid_heap_type(heap_type)) { + ref_type->value_type = +#if WASM_ENABLE_STRINGREF != 0 + (uint8)(REF_TYPE_STRINGVIEWITER + heap_type + - HEAP_TYPE_STRINGVIEWITER); +#else + (uint8)(REF_TYPE_ARRAYREF + heap_type - HEAP_TYPE_ARRAY); +#endif + ref_type->nullable = false; + ref_type->heap_type = 0; + } + else { + ref_type->nullable = true; + } + } + + return true; +} + +uint32 +wasm_get_defined_type_count(WASMModuleCommon *const module) +{ + uint32 type_count = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + type_count = wasm_module->type_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + type_count = aot_module->type_count; + } +#endif + + return type_count; +} + +WASMType * +wasm_get_defined_type(WASMModuleCommon *const module, uint32 index) +{ + WASMType *type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + bh_assert(index < wasm_module->type_count); + type = wasm_module->types[index]; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + bh_assert(index < aot_module->type_count); + type = aot_module->types[index]; + } +#endif + + return type; +} + +WASMType * +wasm_obj_get_defined_type(const WASMObjectRef obj) +{ + if ((!wasm_obj_is_struct_obj(obj)) && (!wasm_obj_is_array_obj(obj)) + && (!wasm_obj_is_func_obj(obj))) { + bh_assert(false); + } + + return ((WASMRttTypeRef)(obj->header))->defined_type; +} + +int32 +wasm_obj_get_defined_type_idx(WASMModuleCommon *const module, + const WASMObjectRef obj) +{ + WASMType *type = wasm_obj_get_defined_type(obj); + uint32 i, type_idx = (uint32)-1; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + uint32 type_count = wasm_module->type_count; + + for (i = 0; i < type_count; i++) { + if (wasm_module->types[i] == type) { + type_idx = i; + break; + } + } + bh_assert(type_idx < type_count); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + uint32 type_count = aot_module->type_count; + + for (i = 0; i < type_count; i++) { + if (aot_module->types[i] == type) { + type_idx = i; + break; + } + } + bh_assert(type_idx < type_count); + } +#endif + + return type_idx; +} + +bool +wasm_defined_type_is_func_type(WASMType *const def_type) +{ + return wasm_type_is_func_type(def_type); +} + +bool +wasm_defined_type_is_struct_type(WASMType *const def_type) +{ + return wasm_type_is_struct_type(def_type); +} + +bool +wasm_defined_type_is_array_type(WASMType *const def_type) +{ + return wasm_type_is_array_type(def_type); +} + +wasm_ref_type_t +wasm_func_type_get_param_type(WASMFuncType *const func_type, uint32 param_idx) +{ + wasm_ref_type_t ref_type = { 0 }; + + bh_assert(param_idx < func_type->param_count); + + ref_type.value_type = func_type->types[param_idx]; + + if (wasm_is_type_multi_byte_type(func_type->types[param_idx])) { + WASMRefType *param_ref_type = wasm_reftype_map_find( + func_type->ref_type_maps, func_type->ref_type_map_count, param_idx); + bh_assert(param_ref_type); + ref_type.nullable = param_ref_type->ref_ht_common.nullable; + ref_type.heap_type = param_ref_type->ref_ht_common.heap_type; + } + + return ref_type; +} + +wasm_ref_type_t +wasm_func_type_get_result_type(WASMFuncType *const func_type, uint32 result_idx) +{ + wasm_ref_type_t ref_type = { 0 }; + uint32 result_idx_with_param; + + result_idx_with_param = func_type->param_count + result_idx; + bh_assert(result_idx < func_type->result_count); + + ref_type.value_type = func_type->types[result_idx_with_param]; + + if (wasm_is_type_multi_byte_type(func_type->types[result_idx_with_param])) { + WASMRefType *result_ref_type = wasm_reftype_map_find( + func_type->ref_type_maps, func_type->ref_type_map_count, + result_idx_with_param); + bh_assert(result_ref_type); + ref_type.nullable = result_ref_type->ref_ht_common.nullable; + ref_type.heap_type = result_ref_type->ref_ht_common.heap_type; + } + + return ref_type; +} + +uint32 +wasm_struct_type_get_field_count(WASMStructType *const struct_type) +{ + bh_assert(struct_type->base_type.type_flag == WASM_TYPE_STRUCT); + return struct_type->field_count; +} + +wasm_ref_type_t +wasm_struct_type_get_field_type(WASMStructType *const struct_type, + uint32 field_idx, bool *p_is_mutable) +{ + wasm_ref_type_t ref_type = { 0 }; + WASMStructFieldType field; + + bh_assert(struct_type->base_type.type_flag == WASM_TYPE_STRUCT); + bh_assert(field_idx < struct_type->field_count); + + field = struct_type->fields[field_idx]; + ref_type.value_type = field.field_type; + + if (wasm_is_type_multi_byte_type(field.field_type)) { + WASMRefType *field_ref_type = + wasm_reftype_map_find(struct_type->ref_type_maps, + struct_type->ref_type_map_count, field_idx); + bh_assert(field_ref_type); + ref_type.nullable = field_ref_type->ref_ht_common.nullable; + ref_type.heap_type = field_ref_type->ref_ht_common.heap_type; + } + + if (p_is_mutable) { + *p_is_mutable = field.field_flags & 1; + } + + return ref_type; +} + +wasm_ref_type_t +wasm_array_type_get_elem_type(WASMArrayType *const array_type, + bool *p_is_mutable) +{ + wasm_ref_type_t ref_type = { 0 }; + + ref_type.value_type = array_type->elem_type; + + if (wasm_is_type_multi_byte_type(array_type->elem_type)) { + WASMRefType *elem_ref_type = array_type->elem_ref_type; + ref_type.nullable = elem_ref_type->ref_ht_common.nullable; + ref_type.heap_type = elem_ref_type->ref_ht_common.heap_type; + } + + if (p_is_mutable) { + *p_is_mutable = array_type->elem_flags & 1; + } + + return ref_type; +} + +bool +wasm_defined_type_equal(WASMType *const def_type1, WASMType *const def_type2, + WASMModuleCommon *const module) +{ + WASMTypePtr *types = NULL; + uint32 type_count = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + types = wasm_module->types; + type_count = wasm_module->type_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + types = aot_module->types; + type_count = aot_module->type_count; + } +#endif + + bh_assert(types); + + return wasm_type_equal(def_type1, def_type2, types, type_count); +} + +bool +wasm_defined_type_is_subtype_of(WASMType *const def_type1, + WASMType *const def_type2, + WASMModuleCommon *const module) +{ + WASMTypePtr *types = NULL; + uint32 type_count = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + types = wasm_module->types; + type_count = wasm_module->type_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + types = aot_module->types; + type_count = aot_module->type_count; + } +#endif + + bh_assert(types); + + return wasm_type_is_subtype_of(def_type1, def_type2, types, type_count); +} + +void +wasm_ref_type_set_type_idx(wasm_ref_type_t *ref_type, bool nullable, + int32 type_idx) +{ + bh_assert(type_idx >= 0); + ref_type->value_type = + nullable ? VALUE_TYPE_HT_NULLABLE_REF : VALUE_TYPE_HT_NON_NULLABLE_REF; + ref_type->nullable = nullable; + ref_type->heap_type = type_idx; +} + +void +wasm_ref_type_set_heap_type(wasm_ref_type_t *ref_type, bool nullable, + int32 heap_type) +{ + bool ret; + + bh_assert((heap_type >= HEAP_TYPE_ARRAY && heap_type <= HEAP_TYPE_NOFUNC) +#if WASM_ENABLE_STRINGREF != 0 + || heap_type == HEAP_TYPE_STRINGREF + || heap_type == HEAP_TYPE_STRINGVIEWWTF8 + || heap_type == HEAP_TYPE_STRINGVIEWWTF16 + || heap_type == HEAP_TYPE_STRINGVIEWITER +#endif + ); + + ref_type->value_type = + nullable ? VALUE_TYPE_HT_NULLABLE_REF : VALUE_TYPE_HT_NON_NULLABLE_REF; + ref_type->nullable = nullable; + ref_type->heap_type = heap_type; + ret = wasm_ref_type_normalize(ref_type); + bh_assert(ret); + (void)ret; +} + +bool +wasm_ref_type_equal(const wasm_ref_type_t *ref_type1, + const wasm_ref_type_t *ref_type2, + WASMModuleCommon *const module) +{ + wasm_ref_type_t ref_type1_norm = { 0 }; + wasm_ref_type_t ref_type2_norm = { 0 }; + uint32 type_count = 0; + WASMTypePtr *types = NULL; + uint8 type1; + uint8 type2; + + bh_memcpy_s(&ref_type1_norm, (uint32)sizeof(wasm_ref_type_t), ref_type1, + (uint32)sizeof(wasm_ref_type_t)); + bh_memcpy_s(&ref_type2_norm, (uint32)sizeof(wasm_ref_type_t), ref_type2, + (uint32)sizeof(wasm_ref_type_t)); + if (!wasm_ref_type_normalize(&ref_type1_norm)) { + return false; + } + if (!wasm_ref_type_normalize(&ref_type2_norm)) { + return false; + } + type1 = ref_type1_norm.value_type; + type2 = ref_type2_norm.value_type; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + types = ((WASMModule *)module)->types; + type_count = wasm_get_defined_type_count(module); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + types = ((AOTModule *)module)->types; + type_count = wasm_get_defined_type_count(module); + } +#endif + + return wasm_reftype_equal(type1, (WASMRefType *)&ref_type1_norm, type2, + (WASMRefType *)&ref_type2_norm, types, + type_count); +} + +bool +wasm_ref_type_is_subtype_of(const wasm_ref_type_t *ref_type1, + const wasm_ref_type_t *ref_type2, + WASMModuleCommon *const module) +{ + wasm_ref_type_t ref_type1_norm = { 0 }; + wasm_ref_type_t ref_type2_norm = { 0 }; + uint8 type1; + uint8 type2; + WASMTypePtr *types = NULL; + uint32 type_count = 0; + + bh_memcpy_s(&ref_type1_norm, (uint32)sizeof(wasm_ref_type_t), ref_type1, + (uint32)sizeof(wasm_ref_type_t)); + bh_memcpy_s(&ref_type2_norm, (uint32)sizeof(wasm_ref_type_t), ref_type2, + (uint32)sizeof(wasm_ref_type_t)); + if (!wasm_ref_type_normalize(&ref_type1_norm)) { + return false; + } + if (!wasm_ref_type_normalize(&ref_type2_norm)) { + return false; + } + type1 = ref_type1_norm.value_type; + type2 = ref_type2_norm.value_type; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + types = ((WASMModule *)module)->types; + type_count = wasm_get_defined_type_count(module); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + types = ((AOTModule *)module)->types; + type_count = wasm_get_defined_type_count(module); + } +#endif + + bh_assert(types); + + return wasm_reftype_is_subtype_of(type1, (WASMRefType *)&ref_type1_norm, + type2, (WASMRefType *)&ref_type2_norm, + types, type_count); +} + +WASMStructObjectRef +wasm_struct_obj_new_with_typeidx(WASMExecEnv *exec_env, uint32 type_idx) +{ + WASMStructObjectRef struct_obj; + WASMModuleInstanceCommon *module_inst = + wasm_runtime_get_module_inst(exec_env); + WASMType *type = NULL; + WASMRttTypeRef rtt_type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModule *module = ((WASMModuleInstance *)module_inst)->module; + + bh_assert(type_idx < module->type_count); + type = module->types[type_idx]; + bh_assert(wasm_defined_type_is_struct_type(type)); + rtt_type = + wasm_rtt_type_new(type, type_idx, module->rtt_types, + module->type_count, &module->rtt_type_lock); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)module_inst)->module; + + bh_assert(type_idx < module->type_count); + type = module->types[type_idx]; + bh_assert(wasm_defined_type_is_struct_type(type)); + rtt_type = + wasm_rtt_type_new(type, type_idx, module->rtt_types, + module->type_count, &module->rtt_type_lock); + } +#endif + + if (!rtt_type) { + return NULL; + } + struct_obj = wasm_struct_obj_new(exec_env, rtt_type); + + return struct_obj; +} + +WASMStructObjectRef +wasm_struct_obj_new_with_type(WASMExecEnv *exec_env, WASMStructType *type) +{ + WASMStructObjectRef struct_obj; + WASMModuleInstanceCommon *module_inst = + wasm_runtime_get_module_inst(exec_env); + WASMRttTypeRef rtt_type = NULL; + uint32 i = 0; + uint32 type_count = 0; + + bh_assert(type->base_type.type_flag == WASM_TYPE_STRUCT); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModule *module = ((WASMModuleInstance *)module_inst)->module; + + type_count = module->type_count; + + for (i = 0; i < type_count; i++) { + if (module->types[i] == (WASMType *)type) { + break; + } + } + bh_assert(i < type_count); + rtt_type = + wasm_rtt_type_new((WASMType *)type, i, module->rtt_types, + module->type_count, &module->rtt_type_lock); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)module_inst)->module; + + type_count = module->type_count; + + for (i = 0; i < type_count; i++) { + if (module->types[i] == (AOTType *)type) { + break; + } + } + bh_assert(i < type_count); + rtt_type = + wasm_rtt_type_new((AOTType *)type, i, module->rtt_types, + module->type_count, &module->rtt_type_lock); + } +#endif + + if (!rtt_type) { + return NULL; + } + struct_obj = wasm_struct_obj_new(exec_env, rtt_type); + + return struct_obj; +} + +WASMArrayObjectRef +wasm_array_obj_new_with_typeidx(WASMExecEnv *exec_env, uint32 type_idx, + uint32 length, wasm_value_t *init_value) +{ + WASMArrayObjectRef array_obj; + WASMModuleCommon *module = wasm_exec_env_get_module(exec_env); + WASMType *defined_type = wasm_get_defined_type(module, type_idx); + WASMRttTypeRef rtt_type = NULL; + + bh_assert(wasm_defined_type_is_array_type(defined_type)); + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + rtt_type = wasm_rtt_type_new( + defined_type, type_idx, wasm_module->rtt_types, + wasm_module->type_count, &wasm_module->rtt_type_lock); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + rtt_type = wasm_rtt_type_new( + defined_type, type_idx, aot_module->rtt_types, + aot_module->type_count, &aot_module->rtt_type_lock); + } +#endif + + if (!rtt_type) { + return NULL; + } + array_obj = wasm_array_obj_new(exec_env, rtt_type, length, init_value); + + return array_obj; +} + +WASMArrayObjectRef +wasm_array_obj_new_with_type(WASMExecEnv *exec_env, WASMArrayType *type, + uint32 length, wasm_value_t *init_value) +{ + WASMArrayObjectRef array_obj; + uint32 i, type_count, type_idx = 0; + WASMModuleCommon *module = wasm_exec_env_get_module(exec_env); + + bh_assert(type->base_type.type_flag == WASM_TYPE_ARRAY); + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + type_count = wasm_module->type_count; + for (i = 0; i < type_count; i++) { + if (wasm_module->types[i] == (WASMType *)type) { + break; + } + } + bh_assert(i < wasm_module->type_count); + + type_idx = i; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + type_count = aot_module->type_count; + for (i = 0; i < type_count; i++) { + if (aot_module->types[i] == (AOTType *)type) { + break; + } + } + bh_assert(i < aot_module->type_count); + + type_idx = i; + } +#endif + + array_obj = + wasm_array_obj_new_with_typeidx(exec_env, type_idx, length, init_value); + + return array_obj; +} + +WASMFuncObjectRef +wasm_func_obj_new_with_typeidx(WASMExecEnv *exec_env, uint32 type_idx, + uint32 func_idx_bound) +{ + WASMFuncObjectRef func_obj; + WASMRttTypeRef rtt_type = NULL; + WASMModuleCommon *module = wasm_exec_env_get_module(exec_env); + WASMType *defined_type = wasm_get_defined_type(module, type_idx); + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + rtt_type = wasm_rtt_type_new( + defined_type, type_idx, wasm_module->rtt_types, + wasm_module->type_count, &wasm_module->rtt_type_lock); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + rtt_type = wasm_rtt_type_new( + defined_type, type_idx, aot_module->rtt_types, + aot_module->type_count, &aot_module->rtt_type_lock); + } +#endif + + if (!rtt_type) { + return NULL; + } + func_obj = wasm_func_obj_new(exec_env, rtt_type, func_idx_bound); + + return func_obj; +} + +WASMFuncObjectRef +wasm_func_obj_new_with_type(WASMExecEnv *exec_env, WASMFuncType *type, + uint32 func_idx_bound) +{ + WASMFuncObjectRef func_obj; + uint32 i, type_count, type_idx = 0; + WASMModuleCommon *module = wasm_exec_env_get_module(exec_env); + + bh_assert(type->base_type.type_flag == WASM_TYPE_FUNC); + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + type_count = wasm_module->type_count; + for (i = 0; i < type_count; i++) { + if (wasm_module->types[i] == (WASMType *)type) { + break; + } + } + bh_assert(i < wasm_module->type_count); + + type_idx = i; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + type_count = aot_module->type_count; + for (i = 0; i < type_count; i++) { + if (aot_module->types[i] == (AOTType *)type) { + break; + } + } + bh_assert(i < aot_module->type_count); + + type_idx = i; + } +#endif + + func_obj = + wasm_func_obj_new_with_typeidx(exec_env, type_idx, func_idx_bound); + + return func_obj; +} + +bool +wasm_runtime_call_func_ref(WASMExecEnv *exec_env, + const WASMFuncObjectRef func_obj, uint32 argc, + uint32 argv[]) +{ + WASMFunctionInstanceCommon *func_inst = NULL; + uint32 func_idx = wasm_func_obj_get_func_idx_bound(func_obj); +#if WASM_ENABLE_AOT != 0 + AOTFunctionInstance aot_func_inst = { 0 }; +#endif + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func_inst; + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + + bh_assert(func_idx < module_inst->module->import_function_count + + module_inst->module->function_count); + wasm_func_inst = module_inst->e->functions + func_idx; + func_inst = (WASMFunctionInstanceCommon *)wasm_func_inst; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) { + uint32 func_type_idx; + AOTModuleInstance *module_inst = + (AOTModuleInstance *)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->module; + (void)module_inst; + + bh_assert(func_idx < module->import_func_count + module->func_count); + + aot_func_inst.func_name = ""; + aot_func_inst.func_index = func_idx; + aot_func_inst.is_import_func = false; + func_type_idx = + module->func_type_indexes[func_idx - module->import_func_count]; + aot_func_inst.u.func.func_type = + (AOTFuncType *)module->types[func_type_idx]; + aot_func_inst.u.func.func_ptr = + module->func_ptrs[func_idx - module->import_func_count]; + + func_inst = (WASMFunctionInstanceCommon *)(&aot_func_inst); + } +#endif + + bh_assert(func_inst); + return wasm_runtime_call_wasm(exec_env, func_inst, argc, argv); +} + +bool +wasm_runtime_call_func_ref_a(WASMExecEnv *exec_env, + const WASMFuncObjectRef func_obj, + uint32 num_results, wasm_val_t results[], + uint32 num_args, wasm_val_t *args) +{ + /* TODO */ + return false; +} + +bool +wasm_runtime_call_func_ref_v(wasm_exec_env_t exec_env, + const WASMFuncObjectRef func_obj, + uint32 num_results, wasm_val_t results[], + uint32 num_args, ...) +{ + /* TODO */ + return false; +} + +bool +wasm_obj_is_instance_of_defined_type(WASMObjectRef obj, WASMType *defined_type, + WASMModuleCommon *const module) +{ + WASMType **types = NULL; + uint32 type_count = 0; + uint32 type_idx = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + type_count = wasm_module->type_count; + types = wasm_module->types; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + type_count = aot_module->type_count; + types = (WASMType **)aot_module->types; + } +#endif + + for (type_idx = 0; type_idx < type_count; type_idx++) { + if (types[type_idx] == defined_type) { + break; + } + } + bh_assert(type_idx < type_count); + + return wasm_obj_is_instance_of(obj, type_idx, types, type_count); +} + +bool +wasm_obj_is_instance_of_type_idx(WASMObjectRef obj, uint32 type_idx, + WASMModuleCommon *const module) +{ + WASMType **types = NULL; + uint32 type_count = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + + types = wasm_module->types; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + + types = (WASMType **)aot_module->types; + } +#endif + + bh_assert(types); + + return wasm_obj_is_instance_of(obj, type_idx, types, type_count); +} + +bool +wasm_obj_is_instance_of_ref_type(const WASMObjectRef obj, + const wasm_ref_type_t *ref_type) +{ + int32 heap_type = ref_type->heap_type; + return wasm_obj_is_type_of(obj, heap_type); +} + +void +wasm_runtime_push_local_obj_ref(WASMExecEnv *exec_env, WASMLocalObjectRef *ref) +{ + ref->val = NULL; + ref->prev = exec_env->cur_local_object_ref; + exec_env->cur_local_object_ref = ref; +} + +WASMLocalObjectRef * +wasm_runtime_pop_local_obj_ref(WASMExecEnv *exec_env) +{ + WASMLocalObjectRef *local_ref = exec_env->cur_local_object_ref; + exec_env->cur_local_object_ref = exec_env->cur_local_object_ref->prev; + return local_ref; +} + +void +wasm_runtime_pop_local_obj_refs(WASMExecEnv *exec_env, uint32 n) +{ + bh_assert(n > 0); + + do { + exec_env->cur_local_object_ref = exec_env->cur_local_object_ref->prev; + } while (--n > 0); +} + +WASMLocalObjectRef * +wasm_runtime_get_cur_local_obj_ref(WASMExecEnv *exec_env) +{ + WASMLocalObjectRef *local_ref = exec_env->cur_local_object_ref; + + bh_assert(local_ref); + return local_ref; +} + +void +wasm_runtime_gc_prepare(WASMExecEnv *exec_env) +{ +#if 0 + /* TODO: implement wasm_runtime_gc_prepare for multi-thread */ + exec_env->is_gc_reclaiming = false; + wasm_thread_suspend_all(); + exec_env->is_gc_reclaim = 1; + exec_env->requesting_suspend = 0; +#endif +} + +void +wasm_runtime_gc_finalize(WASMExecEnv *exec_env) +{ +#if 0 + /* TODO: implement wasm_runtime_gc_finalize for multi-thread */ + wasm_thread_resume_all(); + exec_env->doing_gc_reclaim = 0; +#endif +} + +bool +wasm_runtime_get_wasm_object_ref_list(WASMObjectRef obj, + bool *p_is_compact_mode, + uint32 *p_ref_num, uint16 **p_ref_list, + uint32 *p_ref_start_offset) +{ + return wasm_object_get_ref_list(obj, p_is_compact_mode, p_ref_num, + p_ref_list, p_ref_start_offset); +} + +bool +wasm_runtime_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) +{ +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_traverse_gc_rootset(exec_env, heap); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) { + return aot_traverse_gc_rootset(exec_env, heap); + } +#endif + return false; +} + +void +wasm_runtime_set_gc_heap_handle(WASMModuleInstanceCommon *module_inst, + void *gc_heap_handle) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + ((WASMModuleInstance *)module_inst)->e->common.gc_heap_handle = + gc_heap_handle; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + e->common.gc_heap_handle = gc_heap_handle; + } +#endif +} + +void * +wasm_runtime_get_gc_heap_handle(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return ((WASMModuleInstance *)module_inst)->e->common.gc_heap_handle; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + return e->common.gc_heap_handle; + } +#endif + return NULL; +} + +bool +wasm_runtime_get_wasm_object_extra_info_flag(WASMObjectRef obj) +{ + return obj->header & WASM_OBJ_EXTRA_INFO_FLAG; +} + +void +wasm_runtime_set_wasm_object_extra_info_flag(WASMObjectRef obj, bool set) +{ + if (set) { + obj->header |= WASM_OBJ_EXTRA_INFO_FLAG; + } + else { + obj->header &= ~WASM_OBJ_EXTRA_INFO_FLAG; + } +} diff --git a/core/iwasm/common/gc/gc_object.c b/core/iwasm/common/gc/gc_object.c new file mode 100644 index 0000000000..333effcf6f --- /dev/null +++ b/core/iwasm/common/gc/gc_object.c @@ -0,0 +1,1081 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "gc_object.h" +#include "mem_alloc.h" +#include "../wasm_runtime_common.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif +#if WASM_ENABLE_STRINGREF != 0 +#include "string_object.h" +#endif + +WASMRttTypeRef +wasm_rtt_type_new(WASMType *defined_type, uint32 defined_type_idx, + WASMRttType **rtt_types, uint32 rtt_type_count, + korp_mutex *rtt_type_lock) +{ + WASMRttType *rtt_type; + + bh_assert(defined_type_idx < rtt_type_count); + + os_mutex_lock(rtt_type_lock); + + if (rtt_types[defined_type_idx]) { + os_mutex_unlock(rtt_type_lock); + return rtt_types[defined_type_idx]; + } + + if ((rtt_type = wasm_runtime_malloc(sizeof(WASMRttType)))) { + rtt_type->type_flag = defined_type->type_flag; + rtt_type->inherit_depth = defined_type->inherit_depth; + rtt_type->defined_type = defined_type; + rtt_type->root_type = defined_type->root_type; + + rtt_types[defined_type_idx] = rtt_type; + } + + os_mutex_unlock(rtt_type_lock); + return rtt_type; +} + +static void * +gc_obj_malloc(void *heap_handle, uint64 size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = mem_allocator_malloc_with_gc(heap_handle, (uint32)size))) { + LOG_WARNING("warning: failed to allocate memory for gc object"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static void * +get_gc_heap_handle(WASMExecEnv *exec_env) +{ + void *gc_heap_handle = NULL; + WASMModuleInstanceCommon *module_inst = exec_env->module_inst; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + gc_heap_handle = + ((WASMModuleInstance *)module_inst)->e->common.gc_heap_handle; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + gc_heap_handle = + ((AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e) + ->common.gc_heap_handle; +#endif + + bh_assert(gc_heap_handle); + return gc_heap_handle; +} + +WASMStructObjectRef +wasm_struct_obj_new_internal(void *heap_handle, WASMRttTypeRef rtt_type) +{ + WASMStructObjectRef struct_obj; + WASMStructType *struct_type; + + bh_assert(rtt_type->type_flag == WASM_TYPE_STRUCT); + + struct_type = (WASMStructType *)rtt_type->defined_type; + if (!(struct_obj = gc_obj_malloc(heap_handle, struct_type->total_size))) { + return NULL; + } + + struct_obj->header = (WASMObjectHeader)rtt_type; + + return struct_obj; +} + +WASMStructObjectRef +wasm_struct_obj_new(WASMExecEnv *exec_env, WASMRttTypeRef rtt_type) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + return wasm_struct_obj_new_internal(heap_handle, rtt_type); +} + +void +wasm_struct_obj_set_field(WASMStructObjectRef struct_obj, uint32 field_idx, + const WASMValue *value) +{ + WASMRttTypeRef rtt_type = + (WASMRttTypeRef)wasm_object_header((WASMObjectRef)struct_obj); + WASMStructType *struct_type = (WASMStructType *)rtt_type->defined_type; + WASMStructFieldType *field; + uint8 field_size, *field_data; + + bh_assert(field_idx < struct_type->field_count); + + field = struct_type->fields + field_idx; + field_data = (uint8 *)struct_obj + field->field_offset; + field_size = field->field_size; + + if (field_size == 4) { + *(int32 *)field_data = value->i32; + } + else if (field_size == 8) { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_X86_32) + *(int64 *)field_data = value->i64; +#else + PUT_I64_TO_ADDR((uint32 *)field_data, value->i64); +#endif + } + else if (field_size == 1) { + *(int8 *)field_data = (int8)value->i32; + } + else if (field_size == 2) { + *(int16 *)field_data = (int16)value->i32; + } + else { + bh_assert(0); + } +} + +void +wasm_struct_obj_get_field(const WASMStructObjectRef struct_obj, + uint32 field_idx, bool sign_extend, WASMValue *value) +{ + WASMRttTypeRef rtt_type = + (WASMRttTypeRef)wasm_object_header((WASMObjectRef)struct_obj); + WASMStructType *struct_type = (WASMStructType *)rtt_type->defined_type; + WASMStructFieldType *field; + uint8 *field_data, field_size; + + bh_assert(field_idx < struct_type->field_count); + + field = struct_type->fields + field_idx; + field_data = (uint8 *)struct_obj + field->field_offset; + field_size = field->field_size; + + if (field_size == 4) { + value->i32 = *(int32 *)field_data; + } + else if (field_size == 8) { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_X86_32) + value->i64 = *(int64 *)field_data; +#else + value->i64 = GET_I64_FROM_ADDR((uint32 *)field_data); +#endif + } + else if (field_size == 1) { + if (sign_extend) + value->i32 = (int32)(*(int8 *)field_data); + else + value->u32 = (uint32)(*(uint8 *)field_data); + } + else if (field_size == 2) { + if (sign_extend) + value->i32 = (int32)(*(int16 *)field_data); + else + value->u32 = (uint32)(*(uint16 *)field_data); + } + else { + bh_assert(0); + } +} + +uint32 +wasm_struct_obj_get_field_count(const WASMStructObjectRef struct_obj) +{ + WASMRttTypeRef rtt_type = + (WASMRttTypeRef)wasm_object_header((WASMObjectRef)struct_obj); + WASMStructType *struct_type = (WASMStructType *)rtt_type->defined_type; + + return struct_type->field_count; +} + +WASMArrayObjectRef +wasm_array_obj_new_internal(void *heap_handle, WASMRttTypeRef rtt_type, + uint32 length, WASMValue *init_value) +{ + WASMArrayObjectRef array_obj; + WASMArrayType *array_type; + uint64 total_size; + uint32 elem_size, elem_size_log, i; + + bh_assert(rtt_type->type_flag == WASM_TYPE_ARRAY); + + if (length >= (1 << 29)) + return NULL; + + array_type = (WASMArrayType *)rtt_type->defined_type; + if (array_type->elem_type == PACKED_TYPE_I8) { + elem_size = 1; + elem_size_log = 0; + } + else if (array_type->elem_type == PACKED_TYPE_I16) { + elem_size = 2; + elem_size_log = 1; + } + else { + elem_size = wasm_value_type_size(array_type->elem_type); + elem_size_log = (elem_size == 4) ? 2 : 3; + } + + total_size = + offsetof(WASMArrayObject, elem_data) + (uint64)elem_size * length; + if (!(array_obj = gc_obj_malloc(heap_handle, total_size))) { + return NULL; + } + + array_obj->header = (WASMObjectHeader)rtt_type; + array_obj->length = (length << 2) | elem_size_log; + + if (init_value != NULL) { + for (i = 0; i < length; i++) { + if (wasm_is_type_reftype(array_type->elem_type)) { + uint32 *elem_addr = + (uint32 *)array_obj->elem_data + REF_CELL_NUM * i; + PUT_REF_TO_ADDR(elem_addr, init_value->gc_obj); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32) { + ((int32 *)array_obj->elem_data)[i] = init_value->i32; + } + else if (array_type->elem_type == PACKED_TYPE_I8) { + ((int8 *)array_obj->elem_data)[i] = (int8)init_value->i32; + } + else if (array_type->elem_type == PACKED_TYPE_I16) { + ((int16 *)array_obj->elem_data)[i] = (int16)init_value->i32; + } + else { + uint32 *elem_addr = (uint32 *)array_obj->elem_data + 2 * i; + PUT_I64_TO_ADDR(elem_addr, init_value->i64); + } + } + } + + return array_obj; +} + +WASMArrayObjectRef +wasm_array_obj_new(WASMExecEnv *exec_env, WASMRttTypeRef rtt_type, + uint32 length, WASMValue *init_value) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + return wasm_array_obj_new_internal(heap_handle, rtt_type, length, + init_value); +} + +void +wasm_array_obj_set_elem(WASMArrayObjectRef array_obj, uint32 elem_idx, + const WASMValue *value) +{ + uint8 *elem_data = wasm_array_obj_elem_addr(array_obj, elem_idx); + uint32 elem_size = 1 << wasm_array_obj_elem_size_log(array_obj); + + switch (elem_size) { + case 1: + *(int8 *)elem_data = (int8)value->i32; + break; + case 2: + *(int16 *)elem_data = (int16)value->i32; + break; + case 4: + *(int32 *)elem_data = value->i32; + break; + case 8: + PUT_I64_TO_ADDR((uint32 *)elem_data, value->i64); + break; + } +} + +void +wasm_array_obj_get_elem(const WASMArrayObjectRef array_obj, uint32 elem_idx, + bool sign_extend, WASMValue *value) +{ + uint8 *elem_data = wasm_array_obj_elem_addr(array_obj, elem_idx); + uint32 elem_size = 1 << wasm_array_obj_elem_size_log(array_obj); + + switch (elem_size) { + case 1: + value->i32 = sign_extend ? (int32)(*(int8 *)elem_data) + : (int32)(uint32)(*(uint8 *)elem_data); + break; + case 2: + value->i32 = sign_extend ? (int32)(*(int16 *)elem_data) + : (int32)(uint32)(*(uint16 *)elem_data); + break; + case 4: + value->i32 = *(int32 *)elem_data; + break; + case 8: + value->i64 = GET_I64_FROM_ADDR((uint32 *)elem_data); + break; + } +} + +void +wasm_array_obj_fill(const WASMArrayObjectRef array_obj, uint32 elem_idx, + uint32 len, WASMValue *value) +{ + uint32 i; + uint8 *elem_data = wasm_array_obj_elem_addr(array_obj, elem_idx); + uint32 elem_size = 1 << wasm_array_obj_elem_size_log(array_obj); + + if (elem_size == 1) { + memset(elem_data, (int8)value->i32, len); + return; + } + + for (i = 0; i < len; i++) { + switch (elem_size) { + case 2: + *(int16 *)elem_data = (int16)value->i32; + break; + case 4: + *(int32 *)elem_data = value->i32; + break; + case 8: + PUT_I64_TO_ADDR((uint32 *)elem_data, value->i64); + break; + } + elem_data += elem_size; + } +} + +void +wasm_array_obj_copy(WASMArrayObjectRef dst_obj, uint32 dst_idx, + WASMArrayObjectRef src_obj, uint32 src_idx, uint32 len) +{ + uint8 *dst_data = wasm_array_obj_elem_addr(dst_obj, dst_idx); + uint8 *src_data = wasm_array_obj_elem_addr(src_obj, src_idx); + uint32 elem_size = 1 << wasm_array_obj_elem_size_log(dst_obj); + + bh_memmove_s(dst_data, elem_size * len, src_data, elem_size * len); +} + +uint32 +wasm_array_obj_length(const WASMArrayObjectRef array_obj) +{ + return array_obj->length >> WASM_ARRAY_LENGTH_SHIFT; +} + +void * +wasm_array_obj_first_elem_addr(const WASMArrayObjectRef array_obj) +{ + return array_obj->elem_data; +} + +void * +wasm_array_obj_elem_addr(const WASMArrayObjectRef array_obj, uint32 elem_idx) +{ + return array_obj->elem_data + + (elem_idx << wasm_array_obj_elem_size_log(array_obj)); +} + +WASMFuncObjectRef +wasm_func_obj_new_internal(void *heap_handle, WASMRttTypeRef rtt_type, + uint32 func_idx_bound) +{ + WASMFuncObjectRef func_obj; + uint64 total_size; + + bh_assert(rtt_type->type_flag == WASM_TYPE_FUNC); + + total_size = sizeof(WASMFuncObject); + if (!(func_obj = gc_obj_malloc(heap_handle, total_size))) { + return NULL; + } + + func_obj->header = (WASMObjectHeader)rtt_type; + func_obj->func_idx_bound = func_idx_bound; + + return func_obj; +} + +WASMFuncObjectRef +wasm_func_obj_new(WASMExecEnv *exec_env, WASMRttTypeRef rtt_type, + uint32 func_idx_bound) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + return wasm_func_obj_new_internal(heap_handle, rtt_type, func_idx_bound); +} + +uint32 +wasm_func_obj_get_func_idx_bound(const WASMFuncObjectRef func_obj) +{ + return func_obj->func_idx_bound; +} + +WASMFuncType * +wasm_func_obj_get_func_type(const WASMFuncObjectRef func_obj) +{ + WASMRttTypeRef rtt_type = + (WASMRttTypeRef)wasm_object_header((WASMObjectRef)func_obj); + bh_assert(rtt_type->type_flag == WASM_TYPE_FUNC); + return (WASMFuncType *)rtt_type->defined_type; +} + +WASMExternrefObjectRef +wasm_externref_obj_new(WASMExecEnv *exec_env, const void *host_obj) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + WASMAnyrefObjectRef anyref_obj; + WASMExternrefObjectRef externref_obj; + WASMLocalObjectRef local_ref; + + if (!(anyref_obj = gc_obj_malloc(heap_handle, sizeof(WASMAnyrefObject)))) { + return NULL; + } + + anyref_obj->header = WASM_OBJ_ANYREF_OBJ_FLAG; + anyref_obj->host_obj = host_obj; + + /* Lock anyref_obj in case it is reclaimed when allocating memory below */ + wasm_runtime_push_local_obj_ref(exec_env, &local_ref); + local_ref.val = (WASMObjectRef)anyref_obj; + + if (!(externref_obj = + gc_obj_malloc(heap_handle, sizeof(WASMExternrefObject)))) { + wasm_runtime_pop_local_obj_ref(exec_env); + return NULL; + } + + externref_obj->header = WASM_OBJ_EXTERNREF_OBJ_FLAG; + externref_obj->internal_obj = (WASMObjectRef)anyref_obj; + + wasm_runtime_pop_local_obj_ref(exec_env); + return externref_obj; +} + +WASMAnyrefObjectRef +wasm_anyref_obj_new(WASMExecEnv *exec_env, const void *host_obj) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + WASMAnyrefObjectRef anyref_obj; + + if (!(anyref_obj = gc_obj_malloc(heap_handle, sizeof(WASMAnyrefObject)))) { + return NULL; + } + + anyref_obj->header = WASM_OBJ_ANYREF_OBJ_FLAG; + anyref_obj->host_obj = host_obj; + + return anyref_obj; +} + +WASMObjectRef +wasm_externref_obj_to_internal_obj(WASMExternrefObjectRef externref_obj) +{ + return externref_obj->internal_obj; +} + +WASMExternrefObjectRef +wasm_internal_obj_to_externref_obj(WASMExecEnv *exec_env, + WASMObjectRef internal_obj) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + WASMExternrefObjectRef externref_obj; + + if (!(externref_obj = + gc_obj_malloc(heap_handle, sizeof(WASMExternrefObject)))) { + return NULL; + } + + externref_obj->header = WASM_OBJ_EXTERNREF_OBJ_FLAG; + externref_obj->internal_obj = internal_obj; + + return externref_obj; +} + +const void * +wasm_anyref_obj_get_value(WASMAnyrefObjectRef anyref_obj) +{ + return anyref_obj->host_obj; +} + +const void * +wasm_externref_obj_get_value(const WASMExternrefObjectRef externref_obj) +{ + if (wasm_obj_is_anyref_obj(externref_obj->internal_obj)) + return ((WASMAnyrefObjectRef)externref_obj->internal_obj)->host_obj; + else + return externref_obj->internal_obj; +} + +WASMI31ObjectRef +wasm_i31_obj_new(uint32 i31_value) +{ + return (WASMI31ObjectRef)((i31_value << 1) | 1); +} + +uint32 +wasm_i31_obj_get_value(WASMI31ObjectRef i31_obj, bool sign_extend) +{ + uint32 i31_value = (uint32)(((uintptr_t)i31_obj) >> 1); + if (sign_extend && (i31_value & 0x40000000)) /* bit 30 is 1 */ + /* set bit 31 to 1 */ + i31_value |= 0x80000000; + return i31_value; +} + +bool +wasm_obj_is_i31_obj(WASMObjectRef obj) +{ + bh_assert(obj); + return (((uintptr_t)obj) & 1) ? true : false; +} + +bool +wasm_obj_is_externref_obj(WASMObjectRef obj) +{ + bh_assert(obj); + return (!wasm_obj_is_i31_obj(obj) + && (obj->header & WASM_OBJ_EXTERNREF_OBJ_FLAG)) + ? true + : false; +} + +bool +wasm_obj_is_anyref_obj(WASMObjectRef obj) +{ + bh_assert(obj); + return (!wasm_obj_is_i31_obj(obj) + && (obj->header & WASM_OBJ_ANYREF_OBJ_FLAG)) + ? true + : false; +} + +bool +wasm_obj_is_i31_externref_or_anyref_obj(WASMObjectRef obj) +{ + bh_assert(obj); + return (wasm_obj_is_i31_obj(obj) + || (obj->header + & (WASM_OBJ_EXTERNREF_OBJ_FLAG | WASM_OBJ_ANYREF_OBJ_FLAG))) + ? true + : false; +} + +bool +wasm_obj_is_struct_obj(WASMObjectRef obj) +{ + WASMRttTypeRef rtt_type; + + bh_assert(obj); + + if (wasm_obj_is_i31_externref_or_anyref_obj(obj)) + return false; + + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); + return rtt_type->type_flag == WASM_TYPE_STRUCT ? true : false; +} + +bool +wasm_obj_is_array_obj(WASMObjectRef obj) +{ + WASMRttTypeRef rtt_type; + + bh_assert(obj); + + if (wasm_obj_is_i31_externref_or_anyref_obj(obj)) + return false; + + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); + return rtt_type->type_flag == WASM_TYPE_ARRAY ? true : false; +} + +bool +wasm_obj_is_func_obj(WASMObjectRef obj) +{ + WASMRttTypeRef rtt_type; + + bh_assert(obj); + + if (wasm_obj_is_i31_externref_or_anyref_obj(obj)) + return false; + + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); + return rtt_type->type_flag == WASM_TYPE_FUNC ? true : false; +} + +bool +wasm_obj_is_internal_obj(WASMObjectRef obj) +{ + WASMRttTypeRef rtt_type; + + bh_assert(obj); + + if (wasm_obj_is_i31_obj(obj)) + return true; + else if (obj->header & WASM_OBJ_ANYREF_OBJ_FLAG) + return true; + else if (obj->header & WASM_OBJ_EXTERNREF_OBJ_FLAG) + return false; + else { + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); + return (rtt_type->type_flag == WASM_TYPE_STRUCT + || rtt_type->type_flag == WASM_TYPE_ARRAY) + ? true + : false; + } +} + +bool +wasm_obj_is_eq_obj(WASMObjectRef obj) +{ + WASMRttTypeRef rtt_type; + + bh_assert(obj); + + if (wasm_obj_is_i31_obj(obj)) + return true; + else if ((obj->header & WASM_OBJ_ANYREF_OBJ_FLAG) + || (obj->header & WASM_OBJ_EXTERNREF_OBJ_FLAG)) + return false; + else { + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); + return (rtt_type->type_flag == WASM_TYPE_STRUCT + || rtt_type->type_flag == WASM_TYPE_ARRAY) + ? true + : false; + } +} + +bool +wasm_obj_is_instance_of(WASMObjectRef obj, uint32 type_idx, WASMType **types, + uint32 type_count) +{ + WASMRttTypeRef rtt_type_sub; + WASMType *type_sub, *type_parent; + uint32 distance, i; + + bh_assert(obj); + bh_assert(type_idx < type_count); + + if (wasm_obj_is_i31_externref_or_anyref_obj(obj)) + return false; + + rtt_type_sub = (WASMRttTypeRef)wasm_object_header(obj); + type_parent = types[type_idx]; + + if (!(rtt_type_sub->root_type == type_parent->root_type + && rtt_type_sub->inherit_depth >= type_parent->inherit_depth)) + return false; + + type_sub = rtt_type_sub->defined_type; + distance = type_sub->inherit_depth - type_parent->inherit_depth; + + for (i = 0; i < distance; i++) { + type_sub = type_sub->parent_type; + } + + return (type_sub == type_parent) ? true : false; +} + +bool +wasm_obj_is_type_of(WASMObjectRef obj, int32 heap_type) +{ + bh_assert(obj); + + switch (heap_type) { + case HEAP_TYPE_FUNC: + return wasm_obj_is_func_obj(obj); + case HEAP_TYPE_EXTERN: + return wasm_obj_is_externref_obj(obj); + case HEAP_TYPE_ANY: + return wasm_obj_is_internal_obj(obj); + case HEAP_TYPE_EQ: + return wasm_obj_is_eq_obj(obj); + case HEAP_TYPE_I31: + return wasm_obj_is_i31_obj(obj); + case HEAP_TYPE_STRUCT: + return wasm_obj_is_struct_obj(obj); + case HEAP_TYPE_ARRAY: + return wasm_obj_is_array_obj(obj); +#if WASM_ENABLE_STRINGREF != 0 + case HEAP_TYPE_STRINGREF: + return wasm_obj_is_stringref_obj(obj); + case HEAP_TYPE_STRINGVIEWWTF8: + return wasm_obj_is_stringview_wtf8_obj(obj); + case HEAP_TYPE_STRINGVIEWWTF16: + return wasm_obj_is_stringview_wtf16_obj(obj); +#endif + case HEAP_TYPE_NONE: + case HEAP_TYPE_NOFUNC: + case HEAP_TYPE_NOEXTERN: + return false; + default: + bh_assert(0); + break; + } + return false; +} + +bool +wasm_obj_equal(WASMObjectRef obj1, WASMObjectRef obj2) +{ + /* TODO: do we need to compare the internal details of the objects */ + return obj1 == obj2 ? true : false; +} + +bool +wasm_object_get_ref_list(WASMObjectRef obj, bool *p_is_compact_mode, + uint32 *p_ref_num, uint16 **p_ref_list, + uint32 *p_ref_start_offset) +{ + WASMRttTypeRef rtt_type; + + bh_assert(wasm_obj_is_created_from_heap(obj)); + + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); + + if (obj->header & WASM_OBJ_EXTERNREF_OBJ_FLAG) { + /* externref object */ + static uint16 externref_obj_ref_list[] = { (uint16)offsetof( + WASMExternrefObject, internal_obj) }; + *p_is_compact_mode = false; + *p_ref_num = 1; + *p_ref_list = externref_obj_ref_list; + return true; + } + else if (obj->header & WASM_OBJ_ANYREF_OBJ_FLAG) { + /* anyref object */ + *p_is_compact_mode = false; + *p_ref_num = 0; + *p_ref_list = NULL; + return true; + } +#if WASM_ENABLE_STRINGREF != 0 + else if (rtt_type->type_flag == WASM_TYPE_STRINGREF + || rtt_type->type_flag == WASM_TYPE_STRINGVIEWWTF8 + || rtt_type->type_flag == WASM_TYPE_STRINGVIEWWTF16 + || rtt_type->type_flag == WASM_TYPE_STRINGVIEWITER) { + /* stringref/stringview_wtf8/stringview_wtf16/stringview_iter object */ + *p_is_compact_mode = false; + *p_ref_num = 0; + *p_ref_list = NULL; + return true; + } +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + else if (rtt_type->defined_type->type_flag == WASM_TYPE_FUNC) { + /* function object */ + *p_is_compact_mode = false; + *p_ref_num = 0; + *p_ref_list = NULL; + return true; + } + else if (rtt_type->defined_type->type_flag == WASM_TYPE_STRUCT) { + /* struct object */ + WASMStructType *type = (WASMStructType *)rtt_type->defined_type; + *p_is_compact_mode = false; + *p_ref_num = *type->reference_table; + *p_ref_list = type->reference_table + 1; + return true; + } + else if (rtt_type->defined_type->type_flag == WASM_TYPE_ARRAY) { + /* array object */ + WASMArrayType *type = (WASMArrayType *)rtt_type->defined_type; + if (wasm_is_type_reftype(type->elem_type)) { + *p_is_compact_mode = true; + *p_ref_num = wasm_array_obj_length((WASMArrayObjectRef)obj); + *p_ref_start_offset = (uint16)offsetof(WASMArrayObject, elem_data); + } + else { + *p_is_compact_mode = false; + *p_ref_num = 0; + *p_ref_list = NULL; + } + + return true; + } + else { + bh_assert(0); + return false; + } +} + +bool +wasm_obj_set_gc_finalizer(wasm_exec_env_t exec_env, const wasm_obj_t obj, + wasm_obj_finalizer_t cb, void *data) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + return mem_allocator_set_gc_finalizer(heap_handle, obj, (gc_finalizer_t)cb, + data); +} + +void +wasm_obj_unset_gc_finalizer(wasm_exec_env_t exec_env, void *obj) +{ + void *heap_handle = get_gc_heap_handle(exec_env); + mem_allocator_unset_gc_finalizer(heap_handle, obj); +} + +#if WASM_ENABLE_STRINGREF != 0 +WASMRttTypeRef +wasm_stringref_rtt_type_new(uint16 type_flag, WASMRttType **rtt_types, + korp_mutex *rtt_type_lock) +{ + WASMRttType *rtt_type; + uint32 index; + + bh_assert(type_flag >= WASM_TYPE_STRINGREF + && type_flag <= WASM_TYPE_STRINGVIEWITER); + + index = type_flag - WASM_TYPE_STRINGREF; + + os_mutex_lock(rtt_type_lock); + + if (rtt_types[index]) { + os_mutex_unlock(rtt_type_lock); + return rtt_types[index]; + } + + if ((rtt_type = wasm_runtime_malloc(sizeof(WASMRttType)))) { + memset(rtt_type, 0, sizeof(WASMRttType)); + rtt_type->type_flag = type_flag; + + rtt_types[index] = rtt_type; + } + + os_mutex_unlock(rtt_type_lock); + return rtt_type; +} + +static void +wasm_stringref_obj_finalizer(WASMStringrefObjectRef stringref_obj, void *data) +{ + wasm_string_destroy( + (WASMString)wasm_stringref_obj_get_value(stringref_obj)); +} + +static void +wasm_stringview_wtf8_obj_finalizer(WASMStringviewWTF8ObjectRef stringref_obj, + void *data) +{ + wasm_string_destroy( + (WASMString)wasm_stringview_wtf8_obj_get_value(stringref_obj)); +} + +static void +wasm_stringview_wtf16_obj_finalizer(WASMStringviewWTF16ObjectRef stringref_obj, + void *data) +{ + wasm_string_destroy( + (WASMString)wasm_stringview_wtf16_obj_get_value(stringref_obj)); +} + +static void +wasm_stringview_iter_obj_finalizer(WASMStringviewIterObjectRef stringref_obj, + void *data) +{ + wasm_string_destroy( + (WASMString)wasm_stringview_iter_obj_get_value(stringref_obj)); +} + +static WASMObjectRef +stringref_obj_new(WASMExecEnv *exec_env, uint32 type, const void *str_obj, + int32 pos) +{ + WASMObjectRef stringref_obj = NULL; + void *heap_handle = get_gc_heap_handle(exec_env); + WASMModuleInstanceCommon *module_inst = + wasm_runtime_get_module_inst(exec_env); + WASMRttTypeRef rtt_type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModule *module = ((WASMModuleInstance *)module_inst)->module; + rtt_type = wasm_stringref_rtt_type_new(type, module->stringref_rtts, + &module->rtt_type_lock); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)module_inst)->module; + rtt_type = wasm_stringref_rtt_type_new(type, module->stringref_rtts, + &module->rtt_type_lock); + } +#endif + + if (!rtt_type) { + return NULL; + } + + if (type == WASM_TYPE_STRINGREF) { + if (!(stringref_obj = + gc_obj_malloc(heap_handle, sizeof(WASMStringrefObject)))) { + return NULL; + } + ((WASMStringrefObjectRef)stringref_obj)->header = + (WASMObjectHeader)rtt_type; + ((WASMStringrefObjectRef)stringref_obj)->str_obj = str_obj; + wasm_obj_set_gc_finalizer( + exec_env, (wasm_obj_t)stringref_obj, + (wasm_obj_finalizer_t)wasm_stringref_obj_finalizer, NULL); + } + else if (type == WASM_TYPE_STRINGVIEWWTF8) { + if (!(stringref_obj = gc_obj_malloc( + heap_handle, sizeof(WASMStringviewWTF8Object)))) { + return NULL; + } + ((WASMStringviewWTF8ObjectRef)stringref_obj)->header = + (WASMObjectHeader)rtt_type; + ((WASMStringviewWTF8ObjectRef)stringref_obj)->str_obj = str_obj; + wasm_obj_set_gc_finalizer( + exec_env, (wasm_obj_t)stringref_obj, + (wasm_obj_finalizer_t)wasm_stringview_wtf8_obj_finalizer, NULL); + } + else if (type == WASM_TYPE_STRINGVIEWWTF16) { + if (!(stringref_obj = gc_obj_malloc( + heap_handle, sizeof(WASMStringviewWTF16Object)))) { + return NULL; + } + ((WASMStringviewWTF16ObjectRef)stringref_obj)->header = + (WASMObjectHeader)rtt_type; + ((WASMStringviewWTF16ObjectRef)stringref_obj)->str_obj = str_obj; + wasm_obj_set_gc_finalizer( + exec_env, (wasm_obj_t)stringref_obj, + (wasm_obj_finalizer_t)wasm_stringview_wtf16_obj_finalizer, NULL); + } + else if (type == WASM_TYPE_STRINGVIEWITER) { + if (!(stringref_obj = gc_obj_malloc( + heap_handle, sizeof(WASMStringviewIterObject)))) { + return NULL; + } + ((WASMStringviewIterObjectRef)stringref_obj)->header = + (WASMObjectHeader)rtt_type; + ((WASMStringviewIterObjectRef)stringref_obj)->str_obj = str_obj; + ((WASMStringviewIterObjectRef)stringref_obj)->pos = pos; + wasm_obj_set_gc_finalizer( + exec_env, (wasm_obj_t)stringref_obj, + (wasm_obj_finalizer_t)wasm_stringview_iter_obj_finalizer, NULL); + } + + return stringref_obj; +} + +WASMStringrefObjectRef +wasm_stringref_obj_new(WASMExecEnv *exec_env, const void *str_obj) +{ + WASMStringrefObjectRef stringref_obj; + + stringref_obj = (WASMStringrefObjectRef)stringref_obj_new( + exec_env, WASM_TYPE_STRINGREF, str_obj, 0); + + return stringref_obj; +} + +WASMStringviewWTF8ObjectRef +wasm_stringview_wtf8_obj_new(WASMExecEnv *exec_env, const void *str_obj) +{ + WASMStringviewWTF8ObjectRef stringview_wtf8_obj; + + stringview_wtf8_obj = (WASMStringviewWTF8ObjectRef)stringref_obj_new( + exec_env, WASM_TYPE_STRINGVIEWWTF8, str_obj, 0); + + return stringview_wtf8_obj; +} + +WASMStringviewWTF16ObjectRef +wasm_stringview_wtf16_obj_new(WASMExecEnv *exec_env, const void *str_obj) +{ + WASMStringviewWTF16ObjectRef stringview_wtf16_obj; + + stringview_wtf16_obj = (WASMStringviewWTF16ObjectRef)stringref_obj_new( + exec_env, WASM_TYPE_STRINGVIEWWTF16, str_obj, 0); + + return stringview_wtf16_obj; +} + +WASMStringviewIterObjectRef +wasm_stringview_iter_obj_new(WASMExecEnv *exec_env, const void *str_obj, + int32 pos) +{ + WASMStringviewIterObjectRef stringview_iter_obj; + + stringview_iter_obj = (WASMStringviewIterObjectRef)stringref_obj_new( + exec_env, WASM_TYPE_STRINGVIEWITER, str_obj, pos); + + return stringview_iter_obj; +} + +const void * +wasm_stringref_obj_get_value(WASMStringrefObjectRef stringref_obj) +{ + return stringref_obj->str_obj; +} + +const void * +wasm_stringview_wtf8_obj_get_value( + WASMStringviewWTF8ObjectRef stringview_wtf8_obj) +{ + return stringview_wtf8_obj->str_obj; +} + +const void * +wasm_stringview_wtf16_obj_get_value( + WASMStringviewWTF16ObjectRef stringview_wtf16_obj) +{ + return stringview_wtf16_obj->str_obj; +} + +const void * +wasm_stringview_iter_obj_get_value( + WASMStringviewIterObjectRef stringview_iter_obj) +{ + return stringview_iter_obj->str_obj; +} + +int32 +wasm_stringview_iter_obj_get_pos( + WASMStringviewIterObjectRef stringview_iter_obj) +{ + return stringview_iter_obj->pos; +} + +void +wasm_stringview_iter_obj_update_pos( + WASMStringviewIterObjectRef stringview_iter_obj, int32 pos) +{ + stringview_iter_obj->pos = pos; +} + +#define WASM_OBJ_IS_STRINGREF_IMPL(flag) \ + WASMRttTypeRef rtt_type; \ + \ + bh_assert(obj); \ + \ + if (wasm_obj_is_i31_externref_or_anyref_obj(obj)) \ + return false; \ + \ + rtt_type = (WASMRttTypeRef)wasm_object_header(obj); \ + return rtt_type->type_flag == flag ? true : false + +bool +wasm_obj_is_stringref_obj(WASMObjectRef obj) +{ + WASM_OBJ_IS_STRINGREF_IMPL(WASM_TYPE_STRINGREF); +} + +bool +wasm_obj_is_stringview_wtf8_obj(WASMObjectRef obj) +{ + WASM_OBJ_IS_STRINGREF_IMPL(WASM_TYPE_STRINGVIEWWTF8); +} + +bool +wasm_obj_is_stringview_wtf16_obj(WASMObjectRef obj) +{ + WASM_OBJ_IS_STRINGREF_IMPL(WASM_TYPE_STRINGVIEWWTF16); +} +#undef WASM_OBJ_IS_STRINGREF_IMPL + +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ diff --git a/core/iwasm/common/gc/gc_object.h b/core/iwasm/common/gc/gc_object.h new file mode 100644 index 0000000000..75fdbef5d8 --- /dev/null +++ b/core/iwasm/common/gc/gc_object.h @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GC_OBJECT_H_ +#define _GC_OBJECT_H_ + +#include "gc_type.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Object header of a WASM object, as the address of allocated memory + * must be 8-byte aligned, the lowest 3 bits are zero, we use them to + * mark the object: + * bits[0] is 1: the object is an externref object + * bits[1] is 1: the object is an anyref object + * bits[2] is 1: the object has extra information + * if both bits[0] and bits[1] are 0, then this object header must + * be a pointer of a WASMRttType, denotes that the object is a + * struct object, or an array object, or a function object + */ +typedef uintptr_t WASMObjectHeader; + +#define WASM_OBJ_HEADER_MASK (~((uintptr_t)7)) + +#define WASM_OBJ_EXTERNREF_OBJ_FLAG (((uintptr_t)1) << 0) + +#define WASM_OBJ_ANYREF_OBJ_FLAG (((uintptr_t)1) << 1) + +#define WASM_OBJ_EXTRA_INFO_FLAG (((uintptr_t)1) << 2) + +/* Representation of WASM objects */ +typedef struct WASMObject { + WASMObjectHeader header; +} WASMObject, *WASMObjectRef; + +/* Representation of WASM rtt types */ +typedef struct WASMRttType { + /* type_flag must be WASM_TYPE_FUNC/STRUCT/ARRAY to + denote an object of func, struct or array */ + uint32 type_flag; + uint32 inherit_depth; + WASMType *defined_type; + WASMType *root_type; +} WASMRttType, *WASMRttTypeRef; + +/* Representation of WASM externref objects */ +typedef struct WASMExternrefObject { + /* bits[0] must be 1, denotes an externref object */ + WASMObjectHeader header; + /* an object of WASMAnyrefObjectRef which encapsulates the external + object passed from host, or other internal objects passed to + opcode extern.externalize */ + WASMObjectRef internal_obj; +} WASMExternrefObject, *WASMExternrefObjectRef; + +/* Representation of WASM anyref objects which encapsulate the + external object passed from host */ +typedef struct WASMAnyrefObject { + /* bits[1] must be 1, denotes an anyref object */ + WASMObjectHeader header; + const void *host_obj; +} WASMAnyrefObject, *WASMAnyrefObjectRef; + +/** + * Representation of WASM i31 objects, the lowest bit is 1: + * for a pointer of WASMObjectRef, if the lowest bit is 1, then it is an + * i31 object and bits[1..31] stores the actual i31 value, otherwise + * it is a normal object of rtt/externref/struct/array/func */ +typedef uintptr_t WASMI31ObjectRef; + +/* Representation of WASM struct objects */ +typedef struct WASMStructObject { + /* Must be pointer of WASMRttObject of struct type */ + WASMObjectHeader header; + uint8 field_data[1]; +} WASMStructObject, *WASMStructObjectRef; + +/* Representation of WASM array objects */ +typedef struct WASMArrayObject { + /* Must be pointer of WASMRttObject of array type */ + WASMObjectHeader header; + /* ( << 2) | , + * elem_count = length >> 2 + * elem_size = 2 ^ (length & 0x3) + */ + uint32 length; + uint8 elem_data[1]; +} WASMArrayObject, *WASMArrayObjectRef; + +#define WASM_ARRAY_LENGTH_SHIFT 2 +#define WASM_ARRAY_ELEM_SIZE_MASK 3 + +/* Representation of WASM function objects */ +typedef struct WASMFuncObject { + /* must be pointer of WASMRttObject of func type */ + WASMObjectHeader header; + uint32 func_idx_bound; +} WASMFuncObject, *WASMFuncObjectRef; + +/* Representation of WASM stringref objects */ +typedef struct WASMStringrefObject { + WASMObjectHeader header; + const void *str_obj; +} WASMStringrefObject, *WASMStringrefObjectRef; + +typedef struct WASMStringviewWTF8Object { + WASMObjectHeader header; + const void *str_obj; +} WASMStringviewWTF8Object, *WASMStringviewWTF8ObjectRef; + +typedef struct WASMStringviewWTF16Object { + WASMObjectHeader header; + const void *str_obj; +} WASMStringviewWTF16Object, *WASMStringviewWTF16ObjectRef; + +typedef struct WASMStringviewIterObject { + WASMObjectHeader header; + const void *str_obj; + int32 pos; +} WASMStringviewIterObject, *WASMStringviewIterObjectRef; + +struct WASMExecEnv; + +inline static WASMObjectHeader +wasm_object_header(const WASMObjectRef obj) +{ + return (obj->header & WASM_OBJ_HEADER_MASK); +} + +WASMRttTypeRef +wasm_rtt_type_new(WASMType *defined_type, uint32 defined_type_idx, + WASMRttType **rtt_types, uint32 rtt_type_count, + korp_mutex *rtt_type_lock); + +inline static WASMType * +wasm_rtt_type_get_defined_type(const WASMRttTypeRef rtt_type) +{ + return rtt_type->defined_type; +} + +WASMStructObjectRef +wasm_struct_obj_new_internal(void *heap_handle, WASMRttTypeRef rtt_type); + +WASMStructObjectRef +wasm_struct_obj_new(struct WASMExecEnv *exec_env, WASMRttTypeRef rtt_type); + +void +wasm_struct_obj_set_field(WASMStructObjectRef struct_obj, uint32 field_idx, + const WASMValue *value); + +void +wasm_struct_obj_get_field(const WASMStructObjectRef struct_obj, + uint32 field_idx, bool sign_extend, WASMValue *value); + +/** + * Return the field count of the WASM struct object. + * + * @param struct_obj the WASM struct object + * + * @return the field count of the WASM struct object + */ +uint32 +wasm_struct_obj_get_field_count(const WASMStructObjectRef struct_obj); + +WASMArrayObjectRef +wasm_array_obj_new_internal(void *heap_handle, WASMRttTypeRef rtt_type, + uint32 length, WASMValue *init_value); + +WASMArrayObjectRef +wasm_array_obj_new(struct WASMExecEnv *exec_env, WASMRttTypeRef rtt_type, + uint32 length, WASMValue *init_value); + +void +wasm_array_obj_set_elem(WASMArrayObjectRef array_obj, uint32 elem_idx, + const WASMValue *value); + +void +wasm_array_obj_get_elem(const WASMArrayObjectRef array_obj, uint32 elem_idx, + bool sign_extend, WASMValue *value); + +void +wasm_array_obj_fill(const WASMArrayObjectRef array_obj, uint32 elem_idx, + uint32 len, WASMValue *value); + +void +wasm_array_obj_copy(WASMArrayObjectRef dst_obj, uint32 dst_idx, + WASMArrayObjectRef src_obj, uint32 src_idx, uint32 len); + +/** + * Return the logarithm of the size of array element. + * + * @param array the WASM array object + * + * @return log(size of the array element) + */ +inline static uint32 +wasm_array_obj_elem_size_log(const WASMArrayObjectRef array_obj) +{ + return (array_obj->length & WASM_ARRAY_ELEM_SIZE_MASK); +} + +/** + * Return the length of the array. + * + * @param array_obj the WASM array object + * + * @return the length of the array + */ +uint32 +wasm_array_obj_length(const WASMArrayObjectRef array_obj); + +/** + * Return the address of the first element of an array object. + * + * @param array_obj the WASM array object + * + * @return the address of the first element of the array object + */ +void * +wasm_array_obj_first_elem_addr(const WASMArrayObjectRef array_obj); + +/** + * Return the address of the i-th element of an array object. + * + * @param array_obj the WASM array object + * @param index the index of the element + * + * @return the address of the i-th element of the array object + */ +void * +wasm_array_obj_elem_addr(const WASMArrayObjectRef array_obj, uint32 elem_idx); + +WASMFuncObjectRef +wasm_func_obj_new_internal(void *heap_handle, WASMRttTypeRef rtt_type, + uint32 func_idx_bound); + +WASMFuncObjectRef +wasm_func_obj_new(struct WASMExecEnv *exec_env, WASMRttTypeRef rtt_type, + uint32 func_idx_bound); + +uint32 +wasm_func_obj_get_func_idx_bound(const WASMFuncObjectRef func_obj); + +WASMFuncType * +wasm_func_obj_get_func_type(const WASMFuncObjectRef func_obj); + +WASMExternrefObjectRef +wasm_externref_obj_new(struct WASMExecEnv *exec_env, const void *host_obj); + +WASMAnyrefObjectRef +wasm_anyref_obj_new(struct WASMExecEnv *exec_env, const void *host_obj); + +/* Implementation of opcode extern.internalize */ +WASMObjectRef +wasm_externref_obj_to_internal_obj(const WASMExternrefObjectRef externref_obj); + +/* Implementation of opcode extern.externalize */ +WASMExternrefObjectRef +wasm_internal_obj_to_externref_obj(struct WASMExecEnv *exec_env, + const WASMObjectRef internal_obj); + +const void * +wasm_anyref_obj_get_value(const WASMAnyrefObjectRef anyref_obj); + +const void * +wasm_externref_obj_get_value(const WASMExternrefObjectRef externref_obj); + +WASMI31ObjectRef +wasm_i31_obj_new(uint32 i31_value); + +uint32 +wasm_i31_obj_get_value(WASMI31ObjectRef i31_obj, bool sign_extend); + +bool +wasm_obj_is_i31_obj(WASMObjectRef obj); + +bool +wasm_obj_is_externref_obj(WASMObjectRef obj); + +bool +wasm_obj_is_anyref_obj(WASMObjectRef obj); + +bool +wasm_obj_is_i31_externref_or_anyref_obj(WASMObjectRef obj); + +bool +wasm_obj_is_struct_obj(WASMObjectRef obj); + +bool +wasm_obj_is_array_obj(WASMObjectRef obj); + +bool +wasm_obj_is_func_obj(WASMObjectRef obj); + +bool +wasm_obj_is_internal_obj(WASMObjectRef obj); + +bool +wasm_obj_is_eq_obj(WASMObjectRef obj); + +inline static bool +wasm_obj_is_null_obj(WASMObjectRef obj) +{ + return obj == NULL_REF ? true : false; +} + +inline static bool +wasm_obj_is_created_from_heap(WASMObjectRef obj) +{ + if (obj == NULL || (((uintptr_t)obj) & 1)) + /* null object or i31 object */ + return false; + return true; +} + +bool +wasm_obj_is_instance_of(WASMObjectRef obj, uint32 type_idx, WASMType **types, + uint32 type_count); + +bool +wasm_obj_is_type_of(WASMObjectRef obj, int32 heap_type); + +bool +wasm_obj_equal(WASMObjectRef obj1, WASMObjectRef obj2); + +bool +wasm_object_get_ref_list(WASMObjectRef obj, bool *p_is_compact_mode, + uint32 *p_ref_num, uint16 **p_ref_list, + uint32 *p_ref_start_offset); + +#if WASM_ENABLE_STRINGREF != 0 +WASMStringrefObjectRef +wasm_stringref_obj_new(struct WASMExecEnv *exec_env, const void *str_obj); + +WASMStringviewWTF8ObjectRef +wasm_stringview_wtf8_obj_new(struct WASMExecEnv *exec_env, const void *str_obj); + +WASMStringviewWTF16ObjectRef +wasm_stringview_wtf16_obj_new(struct WASMExecEnv *exec_env, + const void *str_obj); + +WASMStringviewIterObjectRef +wasm_stringview_iter_obj_new(struct WASMExecEnv *exec_env, const void *str_obj, + int32 pos); + +const void * +wasm_stringref_obj_get_value(WASMStringrefObjectRef stringref_obj); + +const void * +wasm_stringview_wtf8_obj_get_value( + WASMStringviewWTF8ObjectRef stringview_wtf8_obj); + +const void * +wasm_stringview_wtf16_obj_get_value( + WASMStringviewWTF16ObjectRef stringview_wtf16_obj); + +const void * +wasm_stringview_iter_obj_get_value( + WASMStringviewIterObjectRef stringview_iter_obj); + +int32 +wasm_stringview_iter_obj_get_pos( + WASMStringviewIterObjectRef stringview_iter_obj); + +void +wasm_stringview_iter_obj_update_pos( + WASMStringviewIterObjectRef stringview_iter_obj, int32 pos); + +bool +wasm_obj_is_stringref_obj(WASMObjectRef obj); + +bool +wasm_obj_is_stringview_wtf8_obj(WASMObjectRef obj); + +bool +wasm_obj_is_stringview_wtf16_obj(WASMObjectRef obj); +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _GC_OBJECT_H_ */ diff --git a/core/iwasm/common/gc/gc_type.c b/core/iwasm/common/gc/gc_type.c new file mode 100644 index 0000000000..8ae12f6424 --- /dev/null +++ b/core/iwasm/common/gc/gc_type.c @@ -0,0 +1,1357 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "gc_type.h" + +void +wasm_dump_value_type(uint8 type, const WASMRefType *ref_type) +{ + switch (type) { + case VALUE_TYPE_I32: + os_printf("i32"); + break; + case VALUE_TYPE_I64: + os_printf("i64"); + break; + case VALUE_TYPE_F32: + os_printf("f32"); + break; + case VALUE_TYPE_F64: + os_printf("f64"); + break; + case VALUE_TYPE_V128: + os_printf("v128"); + break; + case PACKED_TYPE_I8: + os_printf("i8"); + break; + case PACKED_TYPE_I16: + os_printf("i16"); + break; + case REF_TYPE_FUNCREF: + os_printf("funcref"); + break; + case REF_TYPE_EXTERNREF: + os_printf("externref"); + break; + case REF_TYPE_ANYREF: + os_printf("anyref"); + break; + case REF_TYPE_EQREF: + os_printf("eqref"); + break; + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + { + os_printf("(ref "); + if (ref_type->ref_ht_common.nullable) + os_printf("null "); + if (wasm_is_refheaptype_common(&ref_type->ref_ht_common)) { + switch (ref_type->ref_ht_common.heap_type) { + case HEAP_TYPE_FUNC: + os_printf("func"); + break; + case HEAP_TYPE_EXTERN: + os_printf("extern"); + break; + case HEAP_TYPE_ANY: + os_printf("any"); + break; + case HEAP_TYPE_EQ: + os_printf("eq"); + break; + case HEAP_TYPE_I31: + os_printf("i31"); + break; + case HEAP_TYPE_STRUCT: + os_printf("struct"); + break; + case HEAP_TYPE_ARRAY: + os_printf("array"); + break; + case HEAP_TYPE_NONE: + os_printf("none"); + break; + case HEAP_TYPE_NOFUNC: + os_printf("nofunc"); + break; + case HEAP_TYPE_NOEXTERN: + os_printf("noextern"); + break; + default: + bh_assert(0); + break; + } + } + else if (wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common)) { + os_printf("%" PRId32, ref_type->ref_ht_typeidx.type_idx); + } + else { + bh_assert(0); + } + os_printf(")"); + break; + } + case REF_TYPE_I31REF: + os_printf("i31ref"); + break; + case REF_TYPE_STRUCTREF: + os_printf("structref"); + break; + case REF_TYPE_ARRAYREF: + os_printf("arrayref"); + break; + case REF_TYPE_NULLREF: + os_printf("nullref"); + break; + case REF_TYPE_NULLFUNCREF: + os_printf("nullfuncref"); + break; + case REF_TYPE_NULLEXTERNREF: + os_printf("nullexternref"); + break; + default: + bh_assert(0); + } +} + +void +wasm_dump_func_type(const WASMFuncType *type) +{ + uint32 i, j = 0; + const WASMRefType *ref_type = NULL; + + if (type->base_type.parent_type_idx != (uint32)-1) { + if (!type->base_type.is_sub_final) + os_printf("sub "); + else + os_printf("sub final "); + os_printf("%" PRIu32 " ", type->base_type.parent_type_idx); + } + + os_printf("func ["); + + for (i = 0; i < type->param_count; i++) { + if (wasm_is_type_multi_byte_type(type->types[i])) { + bh_assert(j < type->ref_type_map_count); + bh_assert(i == type->ref_type_maps[j].index); + ref_type = type->ref_type_maps[j++].ref_type; + } + else + ref_type = NULL; + wasm_dump_value_type(type->types[i], ref_type); + if (i < (uint32)type->param_count - 1) + os_printf(" "); + } + + os_printf("] -> ["); + + for (; i < (uint32)(type->param_count + type->result_count); i++) { + if (wasm_is_type_multi_byte_type(type->types[i])) { + bh_assert(j < type->ref_type_map_count); + bh_assert(i == type->ref_type_maps[j].index); + ref_type = type->ref_type_maps[j++].ref_type; + } + else + ref_type = NULL; + wasm_dump_value_type(type->types[i], ref_type); + if (i < (uint32)type->param_count + type->result_count - 1) + os_printf(" "); + } + + os_printf("]\n"); +} + +void +wasm_dump_struct_type(const WASMStructType *type) +{ + uint32 i, j = 0; + const WASMRefType *ref_type = NULL; + + if (type->base_type.parent_type_idx != (uint32)-1) { + if (!type->base_type.is_sub_final) + os_printf("sub "); + else + os_printf("sub final "); + os_printf("%" PRIu32 " ", type->base_type.parent_type_idx); + } + + os_printf("struct"); + + for (i = 0; i < type->field_count; i++) { + os_printf(" (field "); + if (type->fields[i].field_flags & 1) + os_printf("(mut "); + if (wasm_is_type_multi_byte_type(type->fields[i].field_type)) { + bh_assert(j < type->ref_type_map_count); + bh_assert(i == type->ref_type_maps[j].index); + ref_type = type->ref_type_maps[j++].ref_type; + } + else + ref_type = NULL; + wasm_dump_value_type(type->fields[i].field_type, ref_type); + if (type->fields[i].field_flags & 1) + os_printf(")"); + os_printf(")"); + } + + os_printf("\n"); +} + +void +wasm_dump_array_type(const WASMArrayType *type) +{ + if (type->base_type.parent_type_idx != (uint32)-1) { + if (!type->base_type.is_sub_final) + os_printf("sub "); + else + os_printf("sub final "); + os_printf("%" PRIu32 " ", type->base_type.parent_type_idx); + } + + os_printf("array "); + + if (type->elem_flags & 1) + os_printf("(mut "); + wasm_dump_value_type(type->elem_type, type->elem_ref_type); + if (type->elem_flags & 1) + os_printf(")"); + os_printf("\n"); +} + +bool +wasm_value_types_is_subtype_of(const uint8 *types1, + const WASMRefTypeMap *ref_type_maps1, + const uint8 *types2, + const WASMRefTypeMap *ref_type_maps2, + uint32 value_type_count, + const WASMTypePtr *types, uint32 type_count) +{ + uint32 i; + WASMRefType *ref_type1, *ref_type2; + + for (i = 0; i < value_type_count; i++) { + ref_type1 = ref_type2 = NULL; + if (wasm_is_type_multi_byte_type(types1[i])) { + ref_type1 = ref_type_maps1->ref_type; + ref_type_maps1++; + } + if (wasm_is_type_multi_byte_type(types2[i])) { + ref_type2 = ref_type_maps2->ref_type; + ref_type_maps2++; + } + if (!wasm_reftype_is_subtype_of(types1[i], ref_type1, types2[i], + ref_type2, types, type_count)) { + return false; + } + } + return true; +} + +static bool +rec_ref_type_equal(const WASMRefType *ref_type1, const WASMRefType *ref_type2, + uint32 rec_begin_type_idx1, uint32 rec_begin_type_idx2, + uint32 rec_count, const WASMTypePtr *types, + uint32 type_count) +{ + uint32 type_idx1, type_idx2; + + if (!wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common) + || !wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) + return ref_type1->ref_ht_common.heap_type + == ref_type2->ref_ht_common.heap_type + ? true + : false; + + /* Now both ref types are type of (ref type_idx) */ + type_idx1 = ref_type1->ref_ht_typeidx.type_idx; + type_idx2 = ref_type2->ref_ht_typeidx.type_idx; + + if (type_idx1 >= rec_begin_type_idx1 + && type_idx1 < rec_begin_type_idx1 + rec_count) { + /* The converted iso-recursive types should be the same */ + bool ret = (type_idx2 >= rec_begin_type_idx2 + && type_idx2 < rec_begin_type_idx2 + rec_count + && type_idx1 - rec_begin_type_idx1 + == type_idx2 - rec_begin_type_idx2) + ? true + : false; + return ret; + } + else if (type_idx2 >= rec_begin_type_idx2 + && type_idx2 < rec_begin_type_idx2 + rec_count) { + /* The converted iso-recursive types should be the same */ + bool ret = (type_idx1 >= rec_begin_type_idx1 + && type_idx1 < rec_begin_type_idx1 + rec_count + && type_idx1 - rec_begin_type_idx1 + == type_idx2 - rec_begin_type_idx2) + ? true + : false; + return ret; + } + + return types[type_idx1] == types[type_idx2] ? true : false; +} + +bool +wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + uint32 i, j = 0; + + if (type1 == type2) + return true; + + if (type1->param_count != type2->param_count + || type1->result_count != type2->result_count + || type1->ref_type_map_count != type2->ref_type_map_count) + return false; + + for (i = 0; i < (uint32)(type1->param_count + type1->result_count); i++) { + if (type1->types[i] != type2->types[i]) + return false; + + if (wasm_is_type_multi_byte_type(type1->types[i])) { + const WASMRefType *ref_type1, *ref_type2; + + bh_assert(j < type1->ref_type_map_count); + bh_assert(i == type1->ref_type_maps[j].index + && i == type2->ref_type_maps[j].index); + + ref_type1 = type1->ref_type_maps[j].ref_type; + ref_type2 = type2->ref_type_maps[j].ref_type; + + if (!rec_ref_type_equal( + ref_type1, ref_type2, type1->base_type.rec_begin_type_idx, + type2->base_type.rec_begin_type_idx, + type1->base_type.rec_count, types, type_count)) + return false; + + j++; + } + } + + return true; +} + +bool +wasm_struct_type_equal(const WASMStructType *type1, const WASMStructType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + uint32 i, j = 0; + + if (type1 == type2) + return true; + + if (type1->field_count != type2->field_count + || type1->ref_type_map_count != type2->ref_type_map_count) + return false; + + for (i = 0; i < type1->field_count; i++) { + if (type1->fields[i].field_type != type2->fields[i].field_type + || type1->fields[i].field_flags != type2->fields[i].field_flags) + return false; + + if (wasm_is_type_multi_byte_type(type1->fields[i].field_type)) { + const WASMRefType *ref_type1, *ref_type2; + + bh_assert(j < type1->ref_type_map_count); + bh_assert(i == type1->ref_type_maps[j].index + && i == type2->ref_type_maps[j].index); + + ref_type1 = type1->ref_type_maps[j].ref_type; + ref_type2 = type2->ref_type_maps[j].ref_type; + + if (!rec_ref_type_equal( + ref_type1, ref_type2, type1->base_type.rec_begin_type_idx, + type2->base_type.rec_begin_type_idx, + type1->base_type.rec_count, types, type_count)) + return false; + + j++; + } + } + + return true; +} + +bool +wasm_array_type_equal(const WASMArrayType *type1, const WASMArrayType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + if (type1 == type2) + return true; + + if (type1->elem_flags != type2->elem_flags) + return false; + + if (type1->elem_type != type2->elem_type) + return false; + + if (!wasm_is_type_multi_byte_type(type1->elem_type)) + return true; + + return rec_ref_type_equal(type1->elem_ref_type, type2->elem_ref_type, + type1->base_type.rec_begin_type_idx, + type2->base_type.rec_begin_type_idx, + type1->base_type.rec_count, types, type_count); +} + +bool +wasm_type_equal(const WASMType *type1, const WASMType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + uint32 rec_begin_type_idx1 = type1->rec_begin_type_idx; + uint32 rec_begin_type_idx2 = type2->rec_begin_type_idx; + uint32 parent_type_idx1, parent_type_idx2, rec_count; + + if (type1 == type2) + return true; + + if (!(type1->type_flag == type2->type_flag + && type1->is_sub_final == type2->is_sub_final + && type1->rec_count == type2->rec_count + && type1->rec_idx == type2->rec_idx)) + return false; + + rec_count = type1->rec_count; + + parent_type_idx1 = type1->parent_type_idx; + parent_type_idx2 = type2->parent_type_idx; + + if (parent_type_idx1 >= rec_begin_type_idx1 + && parent_type_idx1 < rec_begin_type_idx1 + rec_count) { + /* The converted iso-recursive types should be the same */ + if (!(parent_type_idx2 >= rec_begin_type_idx2 + && parent_type_idx2 < rec_begin_type_idx2 + rec_count + && parent_type_idx1 - rec_begin_type_idx1 + == parent_type_idx2 - rec_begin_type_idx2)) { + return false; + } + } + else if (parent_type_idx2 >= rec_begin_type_idx2 + && parent_type_idx2 < rec_begin_type_idx2 + rec_count) { + /* The converted iso-recursive types should be the same */ + if (!(parent_type_idx1 >= rec_begin_type_idx1 + && parent_type_idx1 < rec_begin_type_idx1 + rec_count + && parent_type_idx1 - rec_begin_type_idx1 + == parent_type_idx2 - rec_begin_type_idx2)) { + return false; + } + } + else if (type1->parent_type != type2->parent_type) { + /* The parent types should be same since they have been + normalized and equivalence types with different type + indexes are referring to a same WASMType */ + return false; + } + + if (wasm_type_is_func_type(type1)) + return wasm_func_type_equal((WASMFuncType *)type1, + (WASMFuncType *)type2, types, type_count); + else if (wasm_type_is_struct_type(type1)) + return wasm_struct_type_equal((WASMStructType *)type1, + (WASMStructType *)type2, types, + type_count); + else if (wasm_type_is_array_type(type1)) + return wasm_array_type_equal((WASMArrayType *)type1, + (WASMArrayType *)type2, types, type_count); + + bh_assert(0); + return false; +} + +bool +wasm_func_type_is_subtype_of(const WASMFuncType *type1, + const WASMFuncType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + const WASMRefType *ref_type1 = NULL, *ref_type2 = NULL; + uint32 i, j1 = 0, j2 = 0; + + if (type1 == type2) + return true; + + if (type1->param_count != type2->param_count + || type1->result_count != type2->result_count) + return false; + + for (i = 0; i < type1->param_count; i++) { + if (wasm_is_type_multi_byte_type(type1->types[i])) { + bh_assert(j1 < type1->ref_type_map_count); + ref_type1 = type1->ref_type_maps[j1++].ref_type; + } + if (wasm_is_type_multi_byte_type(type2->types[i])) { + bh_assert(j2 < type2->ref_type_map_count); + ref_type2 = type2->ref_type_maps[j2++].ref_type; + } + if (!wasm_reftype_is_subtype_of(type2->types[i], ref_type2, + type1->types[i], ref_type1, types, + type_count)) { + return false; + } + } + + for (; i < (uint32)(type1->param_count + type1->result_count); i++) { + if (wasm_is_type_multi_byte_type(type1->types[i])) { + bh_assert(j1 < type1->ref_type_map_count); + ref_type1 = type1->ref_type_maps[j1++].ref_type; + } + if (wasm_is_type_multi_byte_type(type2->types[i])) { + bh_assert(j2 < type2->ref_type_map_count); + ref_type2 = type2->ref_type_maps[j2++].ref_type; + } + if (!wasm_reftype_is_subtype_of(type1->types[i], ref_type1, + type2->types[i], ref_type2, types, + type_count)) { + return false; + } + } + + return true; +} + +bool +wasm_func_type_result_is_subtype_of(const WASMFuncType *type1, + const WASMFuncType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + const WASMRefType *ref_type1 = NULL, *ref_type2 = NULL; + const WASMRefTypeMap *ref_type_map1, *ref_type_map2; + uint32 i; + + if (type1 == type2) + return true; + + if (type1->result_count != type2->result_count) + return false; + + ref_type_map1 = type1->result_ref_type_maps; + ref_type_map2 = type2->result_ref_type_maps; + + for (i = 0; i < type1->result_count; i++) { + ref_type1 = ref_type2 = NULL; + if (wasm_is_type_multi_byte_type( + type1->types[type1->param_count + i])) { + bh_assert(ref_type_map1 + && ref_type_map1->index == type1->param_count + i); + ref_type1 = ref_type_map1->ref_type; + ref_type_map1++; + } + if (wasm_is_type_multi_byte_type( + type2->types[type2->param_count + i])) { + bh_assert(ref_type_map2 + && ref_type_map2->index == type1->param_count + i); + ref_type2 = ref_type_map2->ref_type; + ref_type_map2++; + } + if (!wasm_reftype_is_subtype_of(type1->types[type1->param_count + i], + ref_type1, + type2->types[type2->param_count + i], + ref_type2, types, type_count)) { + return false; + } + } + return true; +} + +bool +wasm_struct_type_is_subtype_of(const WASMStructType *type1, + const WASMStructType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + const WASMRefType *ref_type1 = NULL, *ref_type2 = NULL; + uint32 i, j1 = 0, j2 = 0; + + /** + * A structure type is a supertype of another structure type if + * its field list is a prefix of the other (width subtyping). + * A structure type also is a supertype of another structure type + * if they have the same fields and for each field type: + * The field is mutable in both types and the storage types + * are the same. + * The field is immutable in both types and their storage types + * are in (covariant) subtype relation (depth subtyping). + */ + + if (type1 == type2) + return true; + + if (type1->field_count > type2->field_count) { + /* Check whether type1's field list is a prefix of type2 */ + for (i = 0; i < type2->field_count; i++) { + if (type1->fields[i].field_flags != type2->fields[i].field_flags) + return false; + if (wasm_is_type_multi_byte_type(type1->fields[i].field_type)) { + bh_assert(j1 < type1->ref_type_map_count); + ref_type1 = type1->ref_type_maps[j1++].ref_type; + } + if (wasm_is_type_multi_byte_type(type2->fields[i].field_type)) { + bh_assert(j2 < type2->ref_type_map_count); + ref_type2 = type2->ref_type_maps[j2++].ref_type; + } + if (!wasm_reftype_is_subtype_of(type1->fields[i].field_type, + ref_type1, + type2->fields[i].field_type, + ref_type2, types, type_count)) { + return false; + } + } + return true; + } + else if (type1->field_count == type2->field_count) { + /* Check each field's flag and type */ + for (i = 0; i < type1->field_count; i++) { + if (type1->fields[i].field_flags != type2->fields[i].field_flags) + return false; + + if (type1->fields[i].field_flags & 1) { + /* The field is mutable in both types: the storage types + must be the same */ + if (type1->fields[i].field_type != type2->fields[i].field_type) + return false; + if (wasm_is_type_multi_byte_type(type1->fields[i].field_type)) { + bh_assert(j1 < type1->ref_type_map_count); + bh_assert(j2 < type2->ref_type_map_count); + ref_type1 = type1->ref_type_maps[j1++].ref_type; + ref_type2 = type2->ref_type_maps[j2++].ref_type; + if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1, + ref_type2->ref_type, ref_type2, + types, type_count)) + return false; + } + } + else { + /* The field is immutable in both types: their storage types + must be in (covariant) subtype relation (depth subtyping) */ + if (wasm_is_type_multi_byte_type(type1->fields[i].field_type)) { + bh_assert(j1 < type1->ref_type_map_count); + ref_type1 = type1->ref_type_maps[j1++].ref_type; + } + if (wasm_is_type_multi_byte_type(type2->fields[i].field_type)) { + bh_assert(j2 < type2->ref_type_map_count); + ref_type2 = type2->ref_type_maps[j2++].ref_type; + } + if (!wasm_reftype_is_subtype_of(type1->fields[i].field_type, + ref_type1, + type2->fields[i].field_type, + ref_type2, types, type_count)) + return false; + } + } + return true; + } + + return false; +} + +bool +wasm_array_type_is_subtype_of(const WASMArrayType *type1, + const WASMArrayType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + /** + * An array type is a supertype of another array type if: + * Both element types are mutable and the storage types are the same. + * Both element types are immutable and their storage types are in + * (covariant) subtype relation (depth subtyping). + */ + + if (type1->elem_flags != type2->elem_flags) + return false; + + if (type1->elem_flags & 1) { + /* The elem is mutable in both types: the storage types + must be the same */ + return wasm_reftype_equal(type1->elem_type, type1->elem_ref_type, + type2->elem_type, type2->elem_ref_type, types, + type_count); + } + else { + /* The elem is immutable in both types: their storage types + must be in (covariant) subtype relation (depth subtyping) */ + return wasm_reftype_is_subtype_of( + type1->elem_type, type1->elem_ref_type, type2->elem_type, + type2->elem_ref_type, types, type_count); + } + return false; +} + +bool +wasm_type_is_subtype_of(const WASMType *type1, const WASMType *type2, + const WASMTypePtr *types, uint32 type_count) +{ + if (type1 == type2) + return true; + + if (type1->type_flag != type2->type_flag) + return false; + + if (wasm_type_is_func_type(type1)) + return wasm_func_type_is_subtype_of( + (WASMFuncType *)type1, (WASMFuncType *)type2, types, type_count); + else if (wasm_type_is_struct_type(type1)) + return wasm_struct_type_is_subtype_of((WASMStructType *)type1, + (WASMStructType *)type2, types, + type_count); + else if (wasm_type_is_array_type(type1)) + return wasm_array_type_is_subtype_of( + (WASMArrayType *)type1, (WASMArrayType *)type2, types, type_count); + + bh_assert(0); + return false; +} + +uint32 +wasm_reftype_size(uint8 type) +{ + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + return 4; + else if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + return 8; + else if ((type >= (uint8)REF_TYPE_ARRAYREF + && type <= (uint8)REF_TYPE_NULLFUNCREF) + || (type >= (uint8)REF_TYPE_HT_NULLABLE + && type <= (uint8)REF_TYPE_HT_NON_NULLABLE) +#if WASM_ENABLE_STRINGREF != 0 + || (type >= (uint8)REF_TYPE_STRINGVIEWWTF8 + && type <= (uint8)REF_TYPE_STRINGREF) + || (type >= (uint8)REF_TYPE_STRINGVIEWITER + && type <= (uint8)REF_TYPE_STRINGVIEWWTF16) +#endif + ) + return sizeof(uintptr_t); + else if (type == PACKED_TYPE_I8) + return 1; + else if (type == PACKED_TYPE_I16) + return 2; + else if (type == VALUE_TYPE_V128) + return 16; + else { + bh_assert(0); + return 0; + } + + return 0; +} + +uint32 +wasm_reftype_struct_size(const WASMRefType *ref_type) +{ + bh_assert(wasm_is_reftype_htref_nullable(ref_type->ref_type) + || wasm_is_reftype_htref_non_nullable(ref_type->ref_type)); + bh_assert(wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common) + || wasm_is_refheaptype_common(&ref_type->ref_ht_common)); + + return (uint32)sizeof(RefHeapType_Common); +} + +bool +wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1, + const RefHeapType_Common *ref_heap_type2, + const WASMTypePtr *types, uint32 type_count) +{ + if (ref_heap_type1 == ref_heap_type2) + return true; + + if (ref_heap_type1->ref_type != ref_heap_type2->ref_type) + return false; + + if (ref_heap_type1->heap_type != ref_heap_type2->heap_type) { + if (wasm_is_refheaptype_typeidx(ref_heap_type1) + && wasm_is_refheaptype_typeidx(ref_heap_type2)) { + if (ref_heap_type1->heap_type == ref_heap_type2->heap_type) + return true; + else + /* the type_count may be 0 when called from reftype_equal */ + return ((uint32)ref_heap_type1->heap_type < type_count + && (uint32)ref_heap_type2->heap_type < type_count + && types[ref_heap_type1->heap_type] + == types[ref_heap_type2->heap_type]) + ? true + : false; + } + return false; + } + + /* No need to check extra info for common types and (type i) + as their heap_types are the same */ + return true; +} + +bool +wasm_reftype_equal(uint8 type1, const WASMRefType *reftype1, uint8 type2, + const WASMRefType *reftype2, const WASMTypePtr *types, + uint32 type_count) +{ + /* For (ref null func/extern/any/eq/i31/struct/array/none/nofunc/noextern), + they are same as funcref/externref/anyref/eqref/i31ref/structref/arayref/ + nullref/nullfuncref/nullexternref, and have been converted into to the + related one-byte type when loading, so here we don't consider the + situations again: + one is (ref null func/extern/any/eq/i31/struct/array/..), + the other is + funcref/externref/anyref/eqref/i31ref/structref/arrayref/.. */ + if (type1 != type2) + return false; + + if (!wasm_is_type_multi_byte_type(type1)) + /* one byte type */ + return true; + + bh_assert(type1 == (uint8)REF_TYPE_HT_NULLABLE + || type1 == (uint8)REF_TYPE_HT_NON_NULLABLE); + + /* (ref null ht) or (ref ht) */ + return wasm_refheaptype_equal((RefHeapType_Common *)reftype1, + (RefHeapType_Common *)reftype2, types, + type_count); +} + +inline static bool +wasm_is_reftype_supers_of_eq(uint8 type) +{ + return (type == REF_TYPE_EQREF || type == REF_TYPE_ANYREF) ? true : false; +} + +inline static bool +wasm_is_reftype_supers_of_i31(uint8 type) +{ + return (type == REF_TYPE_I31REF || wasm_is_reftype_supers_of_eq(type)) + ? true + : false; +} + +inline static bool +wasm_is_reftype_supers_of_struct(uint8 type) +{ + return (type == REF_TYPE_STRUCTREF || wasm_is_reftype_supers_of_eq(type)) + ? true + : false; +} + +inline static bool +wasm_is_reftype_supers_of_array(uint8 type) +{ + return (type == REF_TYPE_ARRAYREF || wasm_is_reftype_supers_of_eq(type)) + ? true + : false; +} + +inline static bool +wasm_is_reftype_supers_of_func(uint8 type) +{ + return (type == REF_TYPE_FUNCREF) ? true : false; +} + +#if WASM_ENABLE_STRINGREF != 0 +inline static bool +wasm_is_reftype_supers_of_string(uint8 type) +{ + return (type == REF_TYPE_STRINGREF || type == REF_TYPE_ANYREF) ? true + : false; +} +#endif + +inline static bool +wasm_is_reftype_supers_of_none(uint8 type, const WASMRefType *ref_type, + const WASMTypePtr *types, uint32 type_count) +{ + if (type == REF_TYPE_NULLREF || type == REF_TYPE_I31REF + || type == REF_TYPE_STRUCTREF || type == REF_TYPE_ARRAYREF + || wasm_is_reftype_supers_of_eq(type) +#if WASM_ENABLE_STRINGREF != 0 + || type == REF_TYPE_STRINGREF +#endif + ) + return true; + + if (type == REF_TYPE_HT_NULLABLE && ref_type != NULL + && wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common) + && (types[ref_type->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRUCT + || types[ref_type->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_ARRAY)) + return true; + + return false; +} + +inline static bool +wasm_is_reftype_supers_of_nofunc(uint8 type, const WASMRefType *ref_type, + const WASMTypePtr *types, uint32 type_count) +{ + if (type == REF_TYPE_NULLFUNCREF || type == REF_TYPE_FUNCREF) + return true; + + if (type == REF_TYPE_HT_NULLABLE && ref_type != NULL + && wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common) + && (types[ref_type->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_FUNC)) + return true; + + return false; +} + +inline static bool +wasm_is_reftype_supers_of_noextern(uint8 type) +{ + return (type == REF_TYPE_NULLEXTERNREF || type == REF_TYPE_EXTERNREF) + ? true + : false; +} + +/* Whether type1 is one of super types of type2 */ +static bool +wasm_type_is_supers_of(const WASMType *type1, const WASMType *type2) +{ + uint32 i, inherit_depth_diff; + + if (type1 == type2) + return true; + + if (!(type1->root_type == type2->root_type + && type1->inherit_depth < type2->inherit_depth)) + return false; + + inherit_depth_diff = type2->inherit_depth - type1->inherit_depth; + for (i = 0; i < inherit_depth_diff; i++) { + type2 = type2->parent_type; + if (type2 == type1) + return true; + } + + return false; +} + +bool +wasm_func_type_is_super_of(const WASMFuncType *type1, const WASMFuncType *type2) +{ + return wasm_type_is_supers_of((const WASMType *)type1, + (const WASMType *)type2); +} + +bool +wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1, + uint8 type2, const WASMRefType *ref_type2, + const WASMTypePtr *types, uint32 type_count) +{ + if (type1 >= PACKED_TYPE_I16 && type1 <= VALUE_TYPE_I32) { + /* Primitive types (I32/I64/F32/F64/V128/I8/I16) are not + subtypes of each other */ + return type1 == type2 ? true : false; + } + + /** + * Check subtype relationship of two ref types, the ref type hierarchy can + * be described as: + * + * anyref -> eqref + * |-> i31ref + * |-> structref -> (ref null $t) -> (ref $t), $t is struct + * |-> arrayref -> (ref null $t) -> (ref $t), $t is array + * + * funcref -> (ref null $t) -> (ref $t), $t is func + * externref + */ + + if (type1 == REF_TYPE_ANYREF) { + /* any <: any */ + return type2 == REF_TYPE_ANYREF ? true : false; + } + else if (type1 == REF_TYPE_FUNCREF) { + /* func <: func */ + return type2 == REF_TYPE_FUNCREF ? true : false; + } + else if (type1 == REF_TYPE_EXTERNREF) { + /* extern <: extern */ + return type2 == REF_TYPE_EXTERNREF ? true : false; + } + else if (type1 == REF_TYPE_EQREF) { + /* eq <: [eq, any] */ + return wasm_is_reftype_supers_of_eq(type2); + } + else if (type1 == REF_TYPE_I31REF) { + /* i31 <: [i31, eq, any] */ + return wasm_is_reftype_supers_of_i31(type2); + } + else if (type1 == REF_TYPE_STRUCTREF) { + /* struct <: [struct, eq, any] */ + return wasm_is_reftype_supers_of_struct(type2); + } + else if (type1 == REF_TYPE_ARRAYREF) { + /* array <: [array, eq, any] */ + return wasm_is_reftype_supers_of_array(type2); + } + else if (type1 == REF_TYPE_NULLREF) { + return wasm_is_reftype_supers_of_none(type2, ref_type2, types, + type_count); + } + else if (type1 == REF_TYPE_NULLFUNCREF) { + return wasm_is_reftype_supers_of_nofunc(type2, ref_type2, types, + type_count); + } + else if (type1 == REF_TYPE_NULLEXTERNREF) { + return wasm_is_reftype_supers_of_noextern(type2); + } +#if WASM_ENABLE_STRINGREF != 0 + else if (type1 == REF_TYPE_STRINGREF) { + return wasm_is_reftype_supers_of_string(type2); + } + else if (type1 == REF_TYPE_STRINGVIEWWTF8) { + return type2 == REF_TYPE_STRINGVIEWWTF8 ? true : false; + } + else if (type1 == REF_TYPE_STRINGVIEWWTF16) { + return type2 == REF_TYPE_STRINGVIEWWTF16 ? true : false; + } + else if (type1 == REF_TYPE_STRINGVIEWITER) { + return type2 == REF_TYPE_STRINGVIEWITER ? true : false; + } +#endif + else if (type1 == REF_TYPE_HT_NULLABLE) { + if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) { + bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count); + /* reftype1 is (ref null $t) */ + if (type2 == REF_TYPE_HT_NULLABLE && ref_type2 != NULL + && wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) { + bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx + < type_count); + return wasm_type_is_supers_of( + types[ref_type2->ref_ht_typeidx.type_idx], + types[ref_type1->ref_ht_typeidx.type_idx]); + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRUCT) + return wasm_is_reftype_supers_of_struct(type2); + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_ARRAY) + return wasm_is_reftype_supers_of_array(type2); + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_FUNC) + return wasm_is_reftype_supers_of_func(type2); +#if WASM_ENABLE_STRINGREF != 0 + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRINGREF) + return wasm_is_reftype_supers_of_string(type2); + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRINGVIEWWTF8) { + return type2 == REF_TYPE_STRINGVIEWWTF8 ? true : false; + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRINGVIEWWTF16) { + return type2 == REF_TYPE_STRINGVIEWWTF16 ? true : false; + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRINGVIEWITER) { + return type2 == REF_TYPE_STRINGVIEWITER ? true : false; + } +#endif + else + return false; + } + else { + /* (ref null func/extern/any/eq/i31/struct/array/..) have been + converted into + funcref/externref/anyref/eqref/i31ref/structref/arrayref/.. + when loading */ + bh_assert(0); + } + } + else if (type1 == REF_TYPE_HT_NON_NULLABLE) { + bh_assert(ref_type1); + if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) { + bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count); + /* reftype1 is (ref $t) */ + if ((type2 == REF_TYPE_HT_NULLABLE + || type2 == REF_TYPE_HT_NON_NULLABLE) + && ref_type2 != NULL + && wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) { + bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx + < type_count); + return wasm_type_is_supers_of( + types[ref_type2->ref_ht_typeidx.type_idx], + types[ref_type1->ref_ht_typeidx.type_idx]); + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_STRUCT) { + /* the super type is (ref null struct) or (ref struct) */ + if (type2 == REF_TYPE_HT_NULLABLE + || type2 == REF_TYPE_HT_NON_NULLABLE) { + bh_assert(ref_type2); + uint8 ref_type = + (uint8)(ref_type2->ref_ht_common.heap_type + + REF_TYPE_FUNCREF - HEAP_TYPE_FUNC); + return wasm_is_reftype_supers_of_struct(ref_type); + } + else + /* the super type is structref or anyref */ + return wasm_is_reftype_supers_of_struct(type2); + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_ARRAY) { + /* the super type is (ref null array) or (ref array) */ + if (type2 == REF_TYPE_HT_NULLABLE + || type2 == REF_TYPE_HT_NON_NULLABLE) { + bh_assert(ref_type2); + uint8 ref_type = + (uint8)(ref_type2->ref_ht_common.heap_type + + REF_TYPE_FUNCREF - HEAP_TYPE_FUNC); + return wasm_is_reftype_supers_of_array(ref_type); + } + else + /* the super type is arrayref, eqref or anyref */ + return wasm_is_reftype_supers_of_array(type2); + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == WASM_TYPE_FUNC) { + /* the super type is (ref null func) or (ref func) */ + if (type2 == REF_TYPE_HT_NULLABLE + || type2 == REF_TYPE_HT_NON_NULLABLE) { + bh_assert(ref_type2); + uint8 ref_type = + (uint8)(ref_type2->ref_ht_common.heap_type + + REF_TYPE_FUNCREF - HEAP_TYPE_FUNC); + return wasm_is_reftype_supers_of_func(ref_type); + } + else + /* the super type is funcref */ + return wasm_is_reftype_supers_of_func(type2); + } + else if (types[ref_type1->ref_ht_typeidx.type_idx]->type_flag + == REF_TYPE_I31REF) { + /* the super type is (ref null i31) or (ref i31) */ + if (type2 == REF_TYPE_HT_NULLABLE + || type2 == REF_TYPE_HT_NON_NULLABLE) { + bh_assert(ref_type2); + uint8 ref_type = + (uint8)(ref_type2->ref_ht_common.heap_type + + REF_TYPE_FUNCREF - HEAP_TYPE_FUNC); + return wasm_is_reftype_supers_of_i31(ref_type); + } + else + /* the super type is i31ref, eqref or anyref */ + return wasm_is_reftype_supers_of_i31(type2); + } + else { + return false; + } + } + else if (wasm_is_refheaptype_common(&ref_type1->ref_ht_common)) { + /* reftype1 is (ref func/extern/any/eq/i31/struct/array/..) */ + if (wasm_reftype_equal(type1, ref_type1, type2, ref_type2, types, + type_count)) + return true; + else { + int32 heap_type = ref_type1->ref_ht_common.heap_type; + // We don't care whether type2 is nullable or not. So + // we normalize it into its related one-byte type. + if (type2 == REF_TYPE_HT_NULLABLE + || type2 == REF_TYPE_HT_NON_NULLABLE) { + bh_assert(ref_type2); + type2 = (uint8)(ref_type2->ref_ht_common.heap_type + + REF_TYPE_FUNCREF - HEAP_TYPE_FUNC); + } + if (heap_type == HEAP_TYPE_ANY) { + /* (ref any) <: anyref */ + return type2 == REF_TYPE_ANYREF ? true : false; + } + else if (heap_type == HEAP_TYPE_EXTERN) { + /* (ref extern) <: externref */ + return type2 == REF_TYPE_EXTERNREF ? true : false; + } + else if (heap_type == HEAP_TYPE_EQ) { + /* (ref eq) <: [eqref, anyref] */ + return wasm_is_reftype_supers_of_eq(type2); + } + else if (heap_type == HEAP_TYPE_I31) { + /* (ref i31) <: [i31ref, eqref, anyref] */ + return wasm_is_reftype_supers_of_i31(type2); + } + else if (heap_type == HEAP_TYPE_STRUCT) { + /* (ref struct) <: [structref, eqref, anyref] */ + return wasm_is_reftype_supers_of_struct(type2); + } + else if (heap_type == HEAP_TYPE_ARRAY) { + /* (ref array) <: [arrayref, eqref, anyref] */ + return wasm_is_reftype_supers_of_array(type2); + } + else if (heap_type == HEAP_TYPE_FUNC) { + /* (ref func) <: [funcref] */ + return wasm_is_reftype_supers_of_func(type2); + } +#if WASM_ENABLE_STRINGREF != 0 + else if (heap_type == HEAP_TYPE_STRINGREF) { + return wasm_is_reftype_supers_of_string(type2); + } + else if (heap_type == HEAP_TYPE_STRINGVIEWWTF8) { + return type2 == REF_TYPE_STRINGVIEWWTF8 ? true : false; + } + else if (heap_type == HEAP_TYPE_STRINGVIEWWTF16) { + return type2 == REF_TYPE_STRINGVIEWWTF16 ? true : false; + } + else if (heap_type == HEAP_TYPE_STRINGVIEWITER) { + return type2 == REF_TYPE_STRINGVIEWITER ? true : false; + } +#endif + else if (heap_type == HEAP_TYPE_NONE) { + return wasm_is_reftype_supers_of_none(type2, NULL, types, + type_count); + } + else if (heap_type == HEAP_TYPE_NOEXTERN) { + return wasm_is_reftype_supers_of_noextern(type2); + } + else if (heap_type == HEAP_TYPE_NOFUNC) { + return wasm_is_reftype_supers_of_nofunc(type2, NULL, types, + type_count); + } + else { + bh_assert(0); + } + } + } + else { + /* unknown type detected */ + LOG_ERROR("unknown sub type 0x%02x", type1); + bh_assert(0); + } + } + else { + bh_assert(0); + } + + return false; +} + +static uint32 +reftype_hash(const void *key) +{ + WASMRefType *reftype = (WASMRefType *)key; + + switch (reftype->ref_type) { + case (uint8)REF_TYPE_HT_NULLABLE: + case (uint8)REF_TYPE_HT_NON_NULLABLE: + { + RefHeapType_Common *ref_heap_type = (RefHeapType_Common *)reftype; + + if (wasm_is_refheaptype_common(ref_heap_type) + /* type indexes of defined type are same */ + || wasm_is_refheaptype_typeidx(ref_heap_type)) { + return (uint32)reftype->ref_type + ^ (uint32)ref_heap_type->heap_type; + } + + break; + } + + default: + break; + } + + bh_assert(0); + return 0; +} + +static bool +reftype_equal(void *type1, void *type2) +{ + WASMRefType *reftype1 = (WASMRefType *)type1; + WASMRefType *reftype2 = (WASMRefType *)type2; + + return wasm_reftype_equal(reftype1->ref_type, reftype1, reftype2->ref_type, + reftype2, NULL, 0); +} + +WASMRefType * +wasm_reftype_dup(const WASMRefType *ref_type) +{ + if (wasm_is_reftype_htref_nullable(ref_type->ref_type) + || wasm_is_reftype_htref_non_nullable(ref_type->ref_type)) { + if (wasm_is_refheaptype_common(&ref_type->ref_ht_common) + || wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common)) { + RefHeapType_Common *ht_common; + if (!(ht_common = wasm_runtime_malloc(sizeof(RefHeapType_Common)))) + return NULL; + + ht_common->ref_type = ref_type->ref_ht_common.ref_type; + ht_common->nullable = ref_type->ref_ht_common.nullable; + ht_common->heap_type = ref_type->ref_ht_common.heap_type; + return (WASMRefType *)ht_common; + } + } + + bh_assert(0); + return NULL; +} + +void +wasm_set_refheaptype_typeidx(RefHeapType_TypeIdx *ref_ht_typeidx, bool nullable, + int32 type_idx) +{ + ref_ht_typeidx->ref_type = + nullable ? REF_TYPE_HT_NULLABLE : REF_TYPE_HT_NON_NULLABLE; + ref_ht_typeidx->nullable = nullable; + ref_ht_typeidx->type_idx = type_idx; +} + +void +wasm_set_refheaptype_common(RefHeapType_Common *ref_ht_common, bool nullable, + int32 heap_type) +{ + ref_ht_common->ref_type = + nullable ? REF_TYPE_HT_NULLABLE : REF_TYPE_HT_NON_NULLABLE; + ref_ht_common->nullable = nullable; + ref_ht_common->heap_type = heap_type; +} + +WASMRefType * +wasm_reftype_map_find(WASMRefTypeMap *ref_type_maps, uint32 ref_type_map_count, + uint32 index_to_find) +{ + int low = 0, mid; + int high = (int32)ref_type_map_count - 1; + uint32 index; + + while (low <= high) { + mid = (low + high) / 2; + index = ref_type_maps[mid].index; + if (index_to_find == index) { + return ref_type_maps[mid].ref_type; + } + else if (index_to_find < index) + high = mid - 1; + else + low = mid + 1; + } + + return NULL; +} + +HashMap * +wasm_reftype_set_create(uint32 size) +{ + HashMap *ref_type_set = bh_hash_map_create( + size, false, reftype_hash, reftype_equal, NULL, wasm_runtime_free); + + return ref_type_set; +} + +WASMRefType * +wasm_reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type) +{ + WASMRefType *ref_type_ret; + + if ((ref_type_ret = bh_hash_map_find(ref_type_set, (void *)ref_type))) + return ref_type_ret; + + if (!(ref_type_ret = wasm_reftype_dup(ref_type))) + return NULL; + + if (!bh_hash_map_insert(ref_type_set, ref_type_ret, ref_type_ret)) { + wasm_runtime_free(ref_type_ret); + return NULL; + } + + return ref_type_ret; +} diff --git a/core/iwasm/common/gc/gc_type.h b/core/iwasm/common/gc/gc_type.h new file mode 100644 index 0000000000..29b171e5a0 --- /dev/null +++ b/core/iwasm/common/gc/gc_type.h @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GC_TYPE_H_ +#define _GC_TYPE_H_ + +#include "../interpreter/wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +wasm_dump_value_type(uint8 type, const WASMRefType *ref_type); + +void +wasm_dump_func_type(const WASMFuncType *type); + +void +wasm_dump_struct_type(const WASMStructType *type); + +void +wasm_dump_array_type(const WASMArrayType *type); + +/* Whether a group of value types is subtype of + another group of value types */ +bool +wasm_value_types_is_subtype_of(const uint8 *types1, + const WASMRefTypeMap *ref_type_maps1, + const uint8 *types2, + const WASMRefTypeMap *ref_type_maps2, + uint32 value_type_count, + const WASMTypePtr *types, uint32 type_count); + +/* Operations of function type */ + +/* Whether two function types are equal */ +bool +wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Whether func type1 is subtype of func type2 */ +bool +wasm_func_type_is_subtype_of(const WASMFuncType *type1, + const WASMFuncType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Whether func type1 is one of super types of func type2, + used for the func type check in call_indirect/call_ref opcodes */ +bool +wasm_func_type_is_super_of(const WASMFuncType *type1, + const WASMFuncType *type2); + +/* Whether func type1's result types are subtype of + func type2's result types */ +bool +wasm_func_type_result_is_subtype_of(const WASMFuncType *type, + const WASMFuncType *type2, + const WASMTypePtr *types, + uint32 type_count); + +/* Operations of struct type */ + +/* Whether two struct types are equal */ +bool +wasm_struct_type_equal(const WASMStructType *type1, const WASMStructType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Whether struct type1 is subtype of struct type2 */ +bool +wasm_struct_type_is_subtype_of(const WASMStructType *type1, + const WASMStructType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Operations of array type */ + +/* Whether two array types are equal */ +bool +wasm_array_type_equal(const WASMArrayType *type1, const WASMArrayType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Whether array type1 is subtype of array type2 */ +bool +wasm_array_type_is_subtype_of(const WASMArrayType *type1, + const WASMArrayType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Operations of wasm type */ + +/* Whether a wasm type is a function type */ +inline static bool +wasm_type_is_func_type(const WASMType *type) +{ + return type->type_flag == WASM_TYPE_FUNC ? true : false; +} + +/* Whether a wasm type is a struct type */ +inline static bool +wasm_type_is_struct_type(const WASMType *type) +{ + return type->type_flag == WASM_TYPE_STRUCT ? true : false; +} + +/* Whether a wasm type is an array type */ +inline static bool +wasm_type_is_array_type(const WASMType *type) +{ + return type->type_flag == WASM_TYPE_ARRAY ? true : false; +} + +/* Whether two wasm types are equal */ +bool +wasm_type_equal(const WASMType *type1, const WASMType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Whether wasm type1 is subtype of wasm type2 */ +bool +wasm_type_is_subtype_of(const WASMType *type1, const WASMType *type2, + const WASMTypePtr *types, uint32 type_count); + +/* Operations of reference type */ + +/* Whether a value type is a reference type */ +inline static bool +wasm_is_type_reftype(uint8 type) +{ + return ((type >= (uint8)REF_TYPE_ARRAYREF + && type <= (uint8)REF_TYPE_NULLFUNCREF) + || (type >= (uint8)REF_TYPE_HT_NULLABLE + && type <= (uint8)REF_TYPE_HT_NON_NULLABLE) +#if WASM_ENABLE_STRINGREF != 0 + || (type >= (uint8)REF_TYPE_STRINGVIEWWTF8 + && type <= (uint8)REF_TYPE_STRINGREF) + || (type >= (uint8)REF_TYPE_STRINGVIEWITER + && type <= (uint8)REF_TYPE_STRINGVIEWWTF16) +#endif + ) + ? true + : false; +} + +/* Whether a negative value is a valid heap type */ +inline static bool +wasm_is_valid_heap_type(int32 heap_type) +{ + return ((heap_type <= HEAP_TYPE_NOFUNC && heap_type >= HEAP_TYPE_ARRAY) +#if WASM_ENABLE_STRINGREF != 0 + || heap_type == HEAP_TYPE_STRINGREF + || heap_type == HEAP_TYPE_STRINGVIEWWTF8 + || heap_type == HEAP_TYPE_STRINGVIEWWTF16 + || heap_type == HEAP_TYPE_STRINGVIEWITER +#endif + ) + ? true + : false; +} + +/* Whether a value type is multi-byte type, or, requires ref type map + to retrieve extra info */ +inline static bool +wasm_is_type_multi_byte_type(uint8 type) +{ + return (type == (uint8)REF_TYPE_HT_NULLABLE + || type == (uint8)REF_TYPE_HT_NON_NULLABLE) + ? true + : false; +} + +/* Whether a reference type is a funcref type */ +inline static bool +wasm_is_reftype_funcref(uint8 type) +{ + return type == (uint8)REF_TYPE_FUNCREF ? true : false; +} + +/* Whether a reference type is an externref type */ +inline static bool +wasm_is_reftype_externref(uint8 type) +{ + return type == (uint8)REF_TYPE_EXTERNREF ? true : false; +} + +/* Whether a reference type is an anyref type */ +inline static bool +wasm_is_reftype_anyref(uint8 type) +{ + return type == (uint8)REF_TYPE_ANYREF ? true : false; +} + +/* Whether a reference type is an eqref type */ +inline static bool +wasm_is_reftype_eqref(uint8 type) +{ + return type == (uint8)REF_TYPE_EQREF ? true : false; +} + +/* Whether a reference type is a (ref null ht) type */ +inline static bool +wasm_is_reftype_htref_nullable(uint8 type) +{ + return type == (uint8)REF_TYPE_HT_NULLABLE ? true : false; +} + +/* Whether a reference type is a (ref ht) type */ +inline static bool +wasm_is_reftype_htref_non_nullable(uint8 type) +{ + return type == (uint8)REF_TYPE_HT_NON_NULLABLE ? true : false; +} + +/* Whether a reference type is an i31ref type */ +inline static bool +wasm_is_reftype_i31ref(uint8 type) +{ + return type == (uint8)REF_TYPE_I31REF ? true : false; +} + +/* Whether a reference type is a structref type */ +inline static bool +wasm_is_reftype_structref(uint8 type) +{ + return type == (uint8)REF_TYPE_STRUCTREF ? true : false; +} + +/* Whether a reference type is an arrayref type */ +inline static bool +wasm_is_reftype_arrayref(uint8 type) +{ + return type == (uint8)REF_TYPE_ARRAYREF ? true : false; +} + +/* Whether a reference type is a nullref type */ +inline static bool +wasm_is_reftype_nullref(uint8 type) +{ + return type == (uint8)REF_TYPE_NULLREF ? true : false; +} + +/* Whether a reference type is a nullfuncref type */ +inline static bool +wasm_is_reftype_nullfuncref(uint8 type) +{ + return type == (uint8)REF_TYPE_NULLFUNCREF ? true : false; +} + +/* Whether a reference type is a nullexternref type */ +inline static bool +wasm_is_reftype_nullexternref(uint8 type) +{ + return type == (uint8)REF_TYPE_NULLEXTERNREF ? true : false; +} + +/* Return the size of a reference type */ +uint32 +wasm_reftype_size(uint8 type); + +/* Return the actual WASMRefType struct size required of a reference type */ +uint32 +wasm_reftype_struct_size(const WASMRefType *ref_type); + +/* Operations of ref heap type */ + +/* Whether a ref heap type is (type i), i : typeidx, >= 0 */ +inline static bool +wasm_is_refheaptype_typeidx(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type >= 0 ? true : false; +} + +/* Whether a ref heap type is a common type: + func/any/eq/i31/data/nofunc/noextern, not (type i) or (rtt n i) or (rtt i) */ +inline static bool +wasm_is_refheaptype_common(const RefHeapType_Common *ref_heap_type) +{ + return ((ref_heap_type->heap_type >= (int32)HEAP_TYPE_ARRAY + && ref_heap_type->heap_type <= (int32)HEAP_TYPE_NOFUNC) +#if WASM_ENABLE_STRINGREF != 0 + || ref_heap_type->heap_type == (int32)HEAP_TYPE_STRINGREF + || ref_heap_type->heap_type == (int32)HEAP_TYPE_STRINGVIEWWTF8 + || ref_heap_type->heap_type == (int32)HEAP_TYPE_STRINGVIEWWTF16 + || ref_heap_type->heap_type == (int32)HEAP_TYPE_STRINGVIEWITER +#endif + ) + ? true + : false; +} + +/* Whether a ref heap type is a func type */ +inline static bool +wasm_is_refheaptype_func(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type == (int32)HEAP_TYPE_FUNC ? true : false; +} + +/* Whether a ref heap type is an any type */ +inline static bool +wasm_is_refheaptype_any(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type == (int32)HEAP_TYPE_ANY ? true : false; +} + +/* Whether a ref heap type is an eq type */ +inline static bool +wasm_is_refheaptype_eq(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type == (int32)HEAP_TYPE_EQ ? true : false; +} + +/* Whether a ref heap type is an i31 type */ +inline static bool +wasm_is_refheaptype_i31(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type == (int32)HEAP_TYPE_I31 ? true : false; +} + +/* Whether a ref heap type is an array type */ +inline static bool +wasm_is_refheaptype_array(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type == (int32)HEAP_TYPE_ARRAY ? true : false; +} + +#if WASM_ENABLE_STRINGREF != 0 +inline static bool +wasm_is_refheaptype_stringrefs(const RefHeapType_Common *ref_heap_type) +{ + return ref_heap_type->heap_type <= (int32)HEAP_TYPE_STRINGREF + && ref_heap_type->heap_type >= HEAP_TYPE_STRINGVIEWITER + ? true + : false; +} +#endif + +/* Whether two ref heap types are equal */ +bool +wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1, + const RefHeapType_Common *ref_heap_type2, + const WASMTypePtr *types, uint32 type_count); + +/* Whether two ref types are equal */ +bool +wasm_reftype_equal(uint8 type1, const WASMRefType *reftype1, uint8 type2, + const WASMRefType *reftype2, const WASMTypePtr *types, + uint32 type_count); + +/* Whether ref type1 is subtype of ref type2 */ +bool +wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *reftype1, + uint8 type2, const WASMRefType *reftype2, + const WASMTypePtr *types, uint32 type_count); + +/* Returns a new reference type which is a duplication of ref_type, + the caller should use wasm_runtime_free() to free the new ref type */ +WASMRefType * +wasm_reftype_dup(const WASMRefType *ref_type); + +/* Set fields of RefHeapType_TypeIdx */ +void +wasm_set_refheaptype_typeidx(RefHeapType_TypeIdx *ref_ht_typeidx, bool nullable, + int32 type_idx); + +/* Set fields of RefHeapType_Common */ +void +wasm_set_refheaptype_common(RefHeapType_Common *ref_ht_common, bool nullable, + int32 heap_type); + +/* Find the related reftype in reftype map array with index */ +WASMRefType * +wasm_reftype_map_find(WASMRefTypeMap *ref_type_maps, uint32 ref_type_map_count, + uint32 index_to_find); + +/* Create a new hash set of reference type */ +HashMap * +wasm_reftype_set_create(uint32 size); + +/* Insert a reference type into the hash set */ +WASMRefType * +wasm_reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _GC_TYPE_H_ */ diff --git a/core/iwasm/common/gc/iwasm_gc.cmake b/core/iwasm/common/gc/iwasm_gc.cmake new file mode 100644 index 0000000000..5e243c396b --- /dev/null +++ b/core/iwasm/common/gc/iwasm_gc.cmake @@ -0,0 +1,36 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (IWASM_GC_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_GC=1) + +if (WAMR_TEST_GC EQUAL 1) + add_definitions (-DGC_MANUALLY=1 -DGC_IN_EVERY_ALLOCATION=1) +endif () + +include_directories (${IWASM_GC_DIR}) + +file (GLOB source_all ${IWASM_GC_DIR}/*.c) + +set (IWASM_GC_SOURCE ${source_all}) + +if (WAMR_BUILD_STRINGREF EQUAL 1) + set (IWASM_STRINGREF_DIR ${CMAKE_CURRENT_LIST_DIR}/stringref) + + add_definitions (-DWASM_ENABLE_STRINGREF=1) + + include_directories (${IWASM_STRINGREF_DIR}) + + if (NOT DEFINED WAMR_STRINGREF_IMPL_SOURCE) + message(FATAL_ERROR "stringref feature enabled, but WAMR_STRINGREF_IMPL_SOURCE not set" ) + else () + if (${WAMR_STRINGREF_IMPL_SOURCE} STREQUAL "STUB") + set (IWASM_STRINGREF_SOURCE ${IWASM_STRINGREF_DIR}/stringref_stub.c) + else() + set (IWASM_STRINGREF_SOURCE ${WAMR_STRINGREF_IMPL_SOURCE}) + endif() + endif () + + set (IWASM_GC_SOURCE ${IWASM_GC_SOURCE} ${IWASM_STRINGREF_SOURCE}) +endif () diff --git a/core/iwasm/common/gc/stringref/string_object.h b/core/iwasm/common/gc/stringref/string_object.h new file mode 100644 index 0000000000..88135a6e02 --- /dev/null +++ b/core/iwasm/common/gc/stringref/string_object.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _STRING_OBJECT_H_ +#define _STRING_OBJECT_H_ + +#include "wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum EncodingFlag { + UTF8, + WTF8, + WTF16, + LOSSY_UTF8, +} EncodingFlag; + +typedef enum StringViewType { + STRING_VIEW_WTF8, + STRING_VIEW_WTF16, + STRING_VIEW_ITER, +} StringViewType; + +typedef enum ErrorCode { + Insufficient_Space = -3, + Encode_Fail = -2, + Isolated_Surrogate = -1, +} ErrorCode; + +/******************* gc finalizer *****************/ +void +wasm_string_destroy(WASMString str_obj); + +/******************* opcode functions *****************/ + +/* string.const */ +WASMString +wasm_string_new_const(const char *content, uint32 length); + +/* string.new_xx8/new_wtf16 */ +/* string.new_xx8_array */ +/* string.new_wtf16_array */ +WASMString +wasm_string_new_with_encoding(void *addr, uint32 count, EncodingFlag flag); + +/* string.measure */ +int32 +wasm_string_measure(WASMString str_obj, EncodingFlag flag); + +/* stringview_wtf16.length */ +int32 +wasm_string_wtf16_get_length(WASMString str_obj); + +/* string.encode_xx8 */ +/* string.encode_wtf16 */ +/* stringview_wtf8.encode_xx */ +/* stringview_wtf16.encode */ +/* string.encode_xx8_array */ +/* string.encode_wtf16_array */ +int32 +wasm_string_encode(WASMString str_obj, uint32 pos, uint32 count, void *addr, + uint32 *next_pos, EncodingFlag flag); + +/* string.concat */ +WASMString +wasm_string_concat(WASMString str_obj1, WASMString str_obj2); + +/* string.eq */ +int32 +wasm_string_eq(WASMString str_obj1, WASMString str_obj2); + +/* string.is_usv_sequence */ +int32 +wasm_string_is_usv_sequence(WASMString str_obj); + +/* string.as_wtf8 */ +/* string.as_wtf16 */ +/* string.as_iter */ +WASMString +wasm_string_create_view(WASMString str_obj, StringViewType type); + +/* stringview_wtf8.advance */ +/* stringview_iter.advance */ +int32 +wasm_string_advance(WASMString str_obj, uint32 pos, uint32 count, + uint32 *target_pos); + +/* stringview_wtf8.slice */ +/* stringview_wtf16.slice */ +/* stringview_iter.slice */ +WASMString +wasm_string_slice(WASMString str_obj, uint32 start, uint32 end, + StringViewType type); + +/* stringview_wtf16.get_codeunit */ +int16 +wasm_string_get_wtf16_codeunit(WASMString str_obj, int32 pos); + +/* stringview_iter.next */ +uint32 +wasm_string_next_codepoint(WASMString str_obj, uint32 pos); + +/* stringview_iter.rewind */ +uint32 +wasm_string_rewind(WASMString str_obj, uint32 pos, uint32 count, + uint32 *target_pos); + +/******************* application functions *****************/ + +void +wasm_string_dump(WASMString str_obj); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _STRING_OBJECT_H_ */ diff --git a/core/iwasm/common/gc/stringref/stringref_stub.c b/core/iwasm/common/gc/stringref/stringref_stub.c new file mode 100644 index 0000000000..8a6d624967 --- /dev/null +++ b/core/iwasm/common/gc/stringref/stringref_stub.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* This file is the stub for stringref implementation, only used for wamrc + * compiler. The runtime embedder SHOULD NOT use this file */ + +#include "string_object.h" + +/******************* gc finalizer *****************/ +void +wasm_string_destroy(WASMString str_obj) +{} + +/******************* opcode functions *****************/ + +/* string.const */ +WASMString +wasm_string_new_const(const char *str, uint32 length) +{ + return NULL; +} + +/* string.new_xx8 */ +/* string.new_wtf16 */ +/* string.new_xx8_array */ +/* string.new_wtf16_array */ +WASMString +wasm_string_new_with_encoding(void *addr, uint32 count, EncodingFlag flag) +{ + return NULL; +} + +/* string.measure */ +/* stringview_wtf16.length */ +int32 +wasm_string_measure(WASMString str_obj, EncodingFlag flag) +{ + return 0; +} + +/* stringview_wtf16.length */ +int32 +wasm_string_wtf16_get_length(WASMString str_obj) +{ + return 0; +} + +/* string.encode_xx8 */ +/* string.encode_wtf16 */ +/* stringview_wtf8.encode_xx */ +/* stringview_wtf16.encode */ +/* string.encode_xx8_array */ +/* string.encode_wtf16_array */ +int32 +wasm_string_encode(WASMString str_obj, uint32 pos, uint32 count, void *addr, + uint32 *next_pos, EncodingFlag flag) +{ + return 0; +} + +/* string.concat */ +WASMString +wasm_string_concat(WASMString str_obj1, WASMString str_obj2) +{ + return NULL; +} + +/* string.eq */ +int32 +wasm_string_eq(WASMString str_obj1, WASMString str_obj2) +{ + return 0; +} + +/* string.is_usv_sequence */ +int32 +wasm_string_is_usv_sequence(WASMString str_obj) +{ + return 0; +} + +/* string.as_wtf8 */ +/* string.as_wtf16 */ +/* string.as_iter */ +WASMString +wasm_string_create_view(WASMString str_obj, StringViewType type) +{ + return NULL; +} + +/* stringview_wtf8.advance */ +/* stringview_iter.advance */ +int32 +wasm_string_advance(WASMString str_obj, uint32 pos, uint32 count, + uint32 *consumed) +{ + return 0; +} + +/* stringview_wtf8.slice */ +/* stringview_wtf16.slice */ +/* stringview_iter.slice */ +WASMString +wasm_string_slice(WASMString str_obj, uint32 start, uint32 end, + StringViewType type) +{ + return NULL; +} + +/* stringview_wtf16.get_codeunit */ +int16 +wasm_string_get_wtf16_codeunit(WASMString str_obj, int32 pos) +{ + return 0; +} + +/* stringview_iter.next */ +uint32 +wasm_string_next_codepoint(WASMString str_obj, uint32 pos) +{ + return 0; +} + +/* stringview_iter.rewind */ +uint32 +wasm_string_rewind(WASMString str_obj, uint32 pos, uint32 count, + uint32 *consumed) +{ + return 0; +} + +void +wasm_string_dump(WASMString str_obj) +{} diff --git a/core/iwasm/common/iwasm_common.cmake b/core/iwasm/common/iwasm_common.cmake index 42ce00417c..c3653f156c 100644 --- a/core/iwasm/common/iwasm_common.cmake +++ b/core/iwasm/common/iwasm_common.cmake @@ -4,37 +4,157 @@ set (IWASM_COMMON_DIR ${CMAKE_CURRENT_LIST_DIR}) include_directories (${IWASM_COMMON_DIR}) +if (MSVC AND WAMR_BUILD_PLATFORM STREQUAL "windows" AND WAMR_BUILD_TARGET MATCHES "AARCH64.*") + if (DEFINED ENV{VCToolsInstallDir}) + # Detect host tool dir + set(_ARMASM64_CANDIDATES + "$ENV{VCToolsInstallDir}/bin/HostX64/ARM64/armasm64.exe" + "$ENV{VCToolsInstallDir}/bin/HostARM64/arm64/armasm64.exe") + set(_ARMASM64_EXE "") + foreach(_p IN LISTS _ARMASM64_CANDIDATES) + if (EXISTS "${_p}") + set(_ARMASM64_EXE "${_p}") + break() + endif() + endforeach() + if (_ARMASM64_EXE STREQUAL "") + message(FATAL_ERROR "armasm64.exe not found under VCToolsInstallDir") + endif() + + # Wrapper without spaces to avoid quoting hell on NMake/cmd.exe + set(_WRAP "${CMAKE_BINARY_DIR}/armasm64_wrapper.bat") + file(WRITE "${_WRAP}" +"@echo off\r\n\"${_ARMASM64_EXE}\" %*\r\n") + + # Use wrapper as compiler (no spaces in path) + set(CMAKE_ASM_MASM_COMPILER + "${_WRAP}" + CACHE FILEPATH "" FORCE) + + # Quote ONLY object and source (compiler path has no spaces now) + set(CMAKE_ASM_MASM_COMPILE_OBJECT + " /nologo -o \"\" \"\"" + CACHE STRING "" FORCE) + + else() + message(FATAL_ERROR "VCToolsInstallDir is not defined. Please run from a Developer Command Prompt or specify armasm64.exe manually.") + endif() +endif() + +add_definitions(-DBH_MALLOC=wasm_runtime_malloc) +add_definitions(-DBH_FREE=wasm_runtime_free) file (GLOB c_source_all ${IWASM_COMMON_DIR}/*.c) -if (${WAMR_BUILD_TARGET} STREQUAL "X86_64" OR ${WAMR_BUILD_TARGET} STREQUAL "AMD_64") - set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64.s) -elseif (${WAMR_BUILD_TARGET} STREQUAL "X86_32") - set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_ia32.s) -elseif (${WAMR_BUILD_TARGET} MATCHES "ARM.*") - if (${WAMR_BUILD_TARGET} MATCHES "ARM.*_VFP") +if (WAMR_DISABLE_APP_ENTRY EQUAL 1) + list(REMOVE_ITEM c_source_all "${IWASM_COMMON_DIR}/wasm_application.c") +endif () + +if (CMAKE_OSX_ARCHITECTURES) + string(TOLOWER "${CMAKE_OSX_ARCHITECTURES}" OSX_ARCHS) + + list(FIND OSX_ARCHS arm64 OSX_AARCH64) + list(FIND OSX_ARCHS x86_64 OSX_X86_64) + + if (NOT "${OSX_AARCH64}" STREQUAL "-1" AND NOT "${OSX_X86_64}" STREQUAL "-1") + set(OSX_UNIVERSAL_BUILD 1) + endif() +endif() + +if (WAMR_BUILD_INVOKE_NATIVE_GENERAL EQUAL 1) + # Use invokeNative C version instead of asm code version + # if WAMR_BUILD_INVOKE_NATIVE_GENERAL is explicitly set. + # Note: + # the maximum number of native arguments is limited to 20, + # and there are possible issues when passing arguments to + # native function for some cpus, e.g. int64 and double arguments + # in arm and mips need to be 8-bytes aligned, and some arguments + # of x86_64 are passed by registers but not stack + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_general.c) +elseif (OSX_UNIVERSAL_BUILD EQUAL 1) + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_osx_universal.s) +elseif (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT WAMR_BUILD_SIMD EQUAL 1) + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + if (NOT MINGW) + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64.asm) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_mingw_x64.s) + endif () + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64.s) + endif () + else () + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + if (NOT MINGW) + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64_simd.asm) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_mingw_x64_simd.s) + endif () + else() + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64_simd.s) + endif() + endif () +elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_ia32.asm) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_ia32.s) + endif () +elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + if (WAMR_BUILD_TARGET MATCHES "ARM.*_VFP") set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_arm_vfp.s) else () set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_arm.s) endif () -elseif (${WAMR_BUILD_TARGET} MATCHES "THUMB.*") - if (${WAMR_BUILD_TARGET} MATCHES "THUMB.*_VFP") +elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") + if (WAMR_BUILD_TARGET MATCHES "THUMB.*_VFP") set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_thumb_vfp.s) else () set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_thumb.s) endif () -elseif (${WAMR_BUILD_TARGET} STREQUAL "MIPS") +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + if (NOT WAMR_BUILD_SIMD EQUAL 1) + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + if (MSVC) + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_armasm64.asm) + set(_WAMR_ARM64_MASM_SOURCES ${IWASM_COMMON_DIR}/arch/invokeNative_armasm64.asm) + set_source_files_properties(${_WAMR_ARM64_MASM_SOURCES} + PROPERTIES + LANGUAGE ASM_MASM + COMPILE_DEFINITIONS "" + INCLUDE_DIRECTORIES "" + COMPILE_OPTIONS "/nologo" + ) + endif () + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_aarch64.s) + endif () + else() + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + if (MSVC) + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_armasm64_simd.asm) + set(_WAMR_ARM64_MASM_SOURCES_SIMD ${IWASM_COMMON_DIR}/arch/invokeNative_armasm64_simd.asm) + set_source_files_properties(${_WAMR_ARM64_MASM_SOURCES_SIMD} + PROPERTIES + LANGUAGE ASM_MASM + COMPILE_DEFINITIONS "" + INCLUDE_DIRECTORIES "" + COMPILE_OPTIONS "/nologo" + ) + endif () + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_aarch64_simd.s) + endif () + endif() +elseif (WAMR_BUILD_TARGET STREQUAL "MIPS") set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_mips.s) -elseif (${WAMR_BUILD_TARGET} STREQUAL "XTENSA") +elseif (WAMR_BUILD_TARGET STREQUAL "XTENSA") set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_xtensa.s) -elseif (${WAMR_BUILD_TARGET} STREQUAL "GENERAL") - # Use invokeNative_general.c instead of assembly code, - # but the maximum number of native arguments is limited to 20, - # and there are possible issues when passing arguments to - # native function for some cpus, e.g. int64 and double arguments - # in arm and mips need to be 8-bytes aligned, and some arguments - # of x86_64 are passed by registers but not stack - set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_general.c) +elseif (WAMR_BUILD_TARGET MATCHES "RISCV*") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_riscv.S) +elseif (WAMR_BUILD_TARGET STREQUAL "ARC") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_arc.s) else () message (FATAL_ERROR "Build target isn't set") endif () diff --git a/core/iwasm/common/wasm_application.c b/core/iwasm/common/wasm_application.c new file mode 100644 index 0000000000..7bbd43d166 --- /dev/null +++ b/core/iwasm/common/wasm_application.c @@ -0,0 +1,936 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif +#if WASM_ENABLE_GC != 0 +#include "gc/gc_object.h" +#if WASM_ENABLE_STRINGREF != 0 +#include "string_object.h" +#endif +#if WASM_ENABLE_GC_PERF_PROFILING != 0 +#include "../../shared/mem-alloc/mem_alloc.h" +#endif +#endif + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +static void * +runtime_malloc(uint64 size, WASMModuleInstanceCommon *module_inst, + char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + if (module_inst != NULL) { + wasm_runtime_set_exception(module_inst, "allocate memory failed"); + } + else if (error_buf != NULL) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + } + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) /* NOLINT */ + +/** + * Implementation of wasm_application_execute_main() + */ +static bool +check_main_func_type(const WASMFuncType *type, bool is_memory64) +{ + if (!(type->param_count == 0 || type->param_count == 2) + || type->result_count > 1) { + LOG_ERROR( + "WASM execute application failed: invalid main function type.\n"); + return false; + } + + if (type->param_count == 2 + && !(type->types[0] == VALUE_TYPE_I32 + && type->types[1] + == (is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32))) { + LOG_ERROR( + "WASM execute application failed: invalid main function type.\n"); + return false; + } + + if (type->result_count + && type->types[type->param_count] != VALUE_TYPE_I32) { + LOG_ERROR( + "WASM execute application failed: invalid main function type.\n"); + return false; + } + + return true; +} + +static bool +execute_main(WASMModuleInstanceCommon *module_inst, int32 argc, char *argv[]) +{ + WASMFunctionInstanceCommon *func; + WASMFuncType *func_type = NULL; + WASMExecEnv *exec_env = NULL; + uint32 argc1 = 0, argv1[3] = { 0 }; + uint32 total_argv_size = 0; + uint64 total_size; + uint64 argv_buf_offset = 0; + int32 i; + char *argv_buf, *p, *p_end; + uint32 *argv_offsets, module_type; + bool ret, is_import_func = true, is_memory64 = false; +#if WASM_ENABLE_MEMORY64 != 0 + WASMModuleInstance *wasm_module_inst = (WASMModuleInstance *)module_inst; + if (wasm_module_inst->memory_count > 0) + is_memory64 = wasm_module_inst->memories[0]->is_memory64; +#endif + + exec_env = wasm_runtime_get_exec_env_singleton(module_inst); + if (!exec_env) { + wasm_runtime_set_exception(module_inst, + "create singleton exec_env failed"); + return false; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + /* In wasi mode, we should call the function named "_start" + which initializes the wasi environment and then calls + the actual main function. Directly calling main function + may cause exception thrown. */ + if ((func = wasm_runtime_lookup_wasi_start_function(module_inst))) { + const char *wasi_proc_exit_exception = "wasi proc exit"; + + ret = wasm_runtime_call_wasm(exec_env, func, 0, NULL); +#if WASM_ENABLE_THREAD_MGR != 0 + if (ret) { + /* On a successful return from the `_start` function, + we terminate other threads by mimicking wasi:proc_exit(0). + + Note: + - A return from the `main` function is an equivalent of + exit(). (C standard) + - When exit code is 0, wasi-libc's `_start` function just + returns w/o calling `proc_exit`. + - A process termination should terminate threads in + the process. */ + + wasm_runtime_set_exception(module_inst, wasi_proc_exit_exception); + /* exit_code is zero-initialized */ + ret = false; + } +#endif + /* report wasm proc exit as a success */ + WASMModuleInstance *inst = (WASMModuleInstance *)module_inst; + if (!ret && strstr(inst->cur_exception, wasi_proc_exit_exception)) { + inst->cur_exception[0] = 0; + ret = true; + } + return ret; + } +#endif /* end of WASM_ENABLE_LIBC_WASI */ + + if (!(func = wasm_runtime_lookup_function(module_inst, "main")) + && !(func = + wasm_runtime_lookup_function(module_inst, "__main_argc_argv")) + && !(func = wasm_runtime_lookup_function(module_inst, "_main"))) { +#if WASM_ENABLE_LIBC_WASI != 0 + wasm_runtime_set_exception( + module_inst, "lookup the entry point symbol (like _start, main, " + "_main, __main_argc_argv) failed"); +#else + wasm_runtime_set_exception(module_inst, + "lookup the entry point symbol (like main, " + "_main, __main_argc_argv) failed"); +#endif + return false; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + is_import_func = ((WASMFunctionInstance *)func)->is_import_func; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + is_import_func = ((AOTFunctionInstance *)func)->is_import_func; + } +#endif + + if (is_import_func) { + wasm_runtime_set_exception(module_inst, "lookup main function failed"); + return false; + } + + module_type = module_inst->module_type; + func_type = wasm_runtime_get_function_type(func, module_type); + + if (!func_type) { + LOG_ERROR("invalid module instance type"); + return false; + } + + if (!check_main_func_type(func_type, is_memory64)) { + wasm_runtime_set_exception(module_inst, + "invalid function type of main function"); + return false; + } + + if (func_type->param_count) { + for (i = 0; i < argc; i++) + total_argv_size += (uint32)(strlen(argv[i]) + 1); +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) + /* `char **argv` is an array of 64-bit elements in memory64 */ + total_argv_size = align_uint(total_argv_size, 8); + else +#endif + total_argv_size = align_uint(total_argv_size, 4); + +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) + /* `char **argv` is an array of 64-bit elements in memory64 */ + total_size = + (uint64)total_argv_size + sizeof(uint64) * (uint64)argc; + else +#endif + total_size = + (uint64)total_argv_size + sizeof(uint32) * (uint64)argc; + + if (total_size >= UINT32_MAX + || !(argv_buf_offset = wasm_runtime_module_malloc( + module_inst, total_size, (void **)&argv_buf))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed"); + return false; + } + + p = argv_buf; + argv_offsets = (uint32 *)(p + total_argv_size); + p_end = p + total_size; + + for (i = 0; i < argc; i++) { + bh_memcpy_s(p, (uint32)(p_end - p), argv[i], + (uint32)(strlen(argv[i]) + 1)); +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) + /* `char **argv` is an array of 64-bit elements in memory64 */ + ((uint64 *)argv_offsets)[i] = + (uint32)argv_buf_offset + (uint32)(p - argv_buf); + else +#endif + argv_offsets[i] = + (uint32)argv_buf_offset + (uint32)(p - argv_buf); + p += strlen(argv[i]) + 1; + } + + argv1[0] = (uint32)argc; +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + argc1 = 3; + uint64 app_addr = + wasm_runtime_addr_native_to_app(module_inst, argv_offsets); + PUT_I64_TO_ADDR(&argv[1], app_addr); + } + else +#endif + { + argc1 = 2; + argv1[1] = (uint32)wasm_runtime_addr_native_to_app(module_inst, + argv_offsets); + } + } + + ret = wasm_runtime_call_wasm(exec_env, func, argc1, argv1); + if (ret && func_type->result_count > 0 && argc > 0 && argv) + /* copy the return value */ + *(int *)argv = (int)argv1[0]; + + if (argv_buf_offset) + wasm_runtime_module_free(module_inst, argv_buf_offset); + + return ret; +} + +bool +wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, int32 argc, + char *argv[]) +{ + bool ret; +#if (WASM_ENABLE_MEMORY_PROFILING != 0) + WASMExecEnv *exec_env; +#endif + + ret = execute_main(module_inst, argc, argv); + +#if WASM_ENABLE_MEMORY_PROFILING != 0 + exec_env = wasm_runtime_get_exec_env_singleton(module_inst); + if (exec_env) { + wasm_runtime_dump_mem_consumption(exec_env); + } +#endif + +#if WASM_ENABLE_GC_PERF_PROFILING != 0 + void *handle = wasm_runtime_get_gc_heap_handle(module_inst); + mem_allocator_dump_perf_profiling(handle); +#endif + +#if WASM_ENABLE_PERF_PROFILING != 0 + wasm_runtime_dump_perf_profiling(module_inst); +#endif + + if (ret) + ret = wasm_runtime_get_exception(module_inst) == NULL; + + return ret; +} + +/** + * Implementation of wasm_application_execute_func() + */ + +union ieee754_float { + float f; + + /* This is the IEEE 754 single-precision format. */ + union { + struct { + unsigned int negative : 1; + unsigned int exponent : 8; + unsigned int mantissa : 23; + } ieee_big_endian; + struct { + unsigned int mantissa : 23; + unsigned int exponent : 8; + unsigned int negative : 1; + } ieee_little_endian; + } ieee; +}; + +union ieee754_double { + double d; + + /* This is the IEEE 754 double-precision format. */ + union { + struct { + unsigned int negative : 1; + unsigned int exponent : 11; + /* Together these comprise the mantissa. */ + unsigned int mantissa0 : 20; + unsigned int mantissa1 : 32; + } ieee_big_endian; + + struct { + /* Together these comprise the mantissa. */ + unsigned int mantissa1 : 32; + unsigned int mantissa0 : 20; + unsigned int exponent : 11; + unsigned int negative : 1; + } ieee_little_endian; + } ieee; +}; + +static bool +execute_func(WASMModuleInstanceCommon *module_inst, const char *name, + int32 argc, char *argv[]) +{ + WASMFunctionInstanceCommon *target_func; + WASMFuncType *type = NULL; + WASMExecEnv *exec_env = NULL; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *ref_type_map; + WASMLocalObjectRef *local_ref; + uint32 num_local_ref_pushed = 0; +#endif + uint32 argc1, *argv1 = NULL, cell_num = 0, j, k = 0; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + uint32 param_size_in_double_world = 0, result_size_in_double_world = 0; +#endif + int32 i, p, module_type; + uint64 total_size; + char buf[128]; + + bh_assert(argc >= 0); + LOG_DEBUG("call a function \"%s\" with %d arguments", name, argc); + + if (!(target_func = wasm_runtime_lookup_function(module_inst, name))) { + snprintf(buf, sizeof(buf), "lookup function %s failed", name); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + + module_type = module_inst->module_type; + type = wasm_runtime_get_function_type(target_func, module_type); + + if (!type) { + LOG_ERROR("invalid module instance type"); + return false; + } + + if (type->param_count != (uint32)argc) { + wasm_runtime_set_exception(module_inst, "invalid input argument count"); + goto fail; + } + + exec_env = wasm_runtime_get_exec_env_singleton(module_inst); + if (!exec_env) { + wasm_runtime_set_exception(module_inst, + "create singleton exec_env failed"); + goto fail; + } + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + for (i = 0; i < type->param_count; i++) { + param_size_in_double_world += + wasm_value_type_cell_num_outside(type->types[i]); + } + for (i = 0; i < type->result_count; i++) { + result_size_in_double_world += wasm_value_type_cell_num_outside( + type->types[type->param_count + i]); + } + argc1 = param_size_in_double_world; + cell_num = (param_size_in_double_world >= result_size_in_double_world) + ? param_size_in_double_world + : result_size_in_double_world; +#else + argc1 = type->param_cell_num; + cell_num = (argc1 > type->ret_cell_num) ? argc1 : type->ret_cell_num; +#endif + + total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2); + if ((!(argv1 = runtime_malloc((uint32)total_size, module_inst, NULL, 0)))) { + goto fail; + } + +#if WASM_ENABLE_GC != 0 + ref_type_map = type->ref_type_maps; +#endif + /* Parse arguments */ + for (i = 0, p = 0; i < argc; i++) { + char *endptr = NULL; + bh_assert(argv[i] != NULL); + if (argv[i][0] == '\0') { + snprintf(buf, sizeof(buf), "invalid input argument %" PRId32, i); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + switch (type->types[i]) { + case VALUE_TYPE_I32: + argv1[p++] = (uint32)strtoul(argv[i], &endptr, 0); + break; + case VALUE_TYPE_I64: + { + union { + uint64 val; + uint32 parts[2]; + } u; + u.val = strtoull(argv[i], &endptr, 0); + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; + break; + } + case VALUE_TYPE_F32: + { + float32 f32 = strtof(argv[i], &endptr); + if (isnan(f32)) { +#ifdef _MSC_VER + /* + * Spec tests require the binary representation of NaN to be + * 0x7fc00000 for float and 0x7ff8000000000000 for float; + * however, in MSVC compiler, strtof doesn't return this + * exact value, causing some of the spec test failures. We + * use the value returned by nan/nanf as it is the one + * expected by spec tests. + * + */ + f32 = nanf(""); +#endif + if (argv[i][0] == '-') { + union ieee754_float u; + u.f = f32; + if (is_little_endian()) + u.ieee.ieee_little_endian.negative = 1; + else + u.ieee.ieee_big_endian.negative = 1; + bh_memcpy_s(&f32, sizeof(float), &u.f, sizeof(float)); + } + if (endptr[0] == ':') { + uint32 sig; + union ieee754_float u; + sig = (uint32)strtoul(endptr + 1, &endptr, 0); + u.f = f32; + if (is_little_endian()) + u.ieee.ieee_little_endian.mantissa = sig; + else + u.ieee.ieee_big_endian.mantissa = sig; + bh_memcpy_s(&f32, sizeof(float), &u.f, sizeof(float)); + } + } + bh_memcpy_s(&argv1[p], (uint32)total_size - p, &f32, + (uint32)sizeof(float)); + p++; + break; + } + case VALUE_TYPE_F64: + { + union { + float64 val; + uint32 parts[2]; + } u; + u.val = strtod(argv[i], &endptr); + if (isnan(u.val)) { +#ifdef _MSC_VER + u.val = nan(""); +#endif + if (argv[i][0] == '-') { + union ieee754_double ud; + ud.d = u.val; + if (is_little_endian()) + ud.ieee.ieee_little_endian.negative = 1; + else + ud.ieee.ieee_big_endian.negative = 1; + bh_memcpy_s(&u.val, sizeof(double), &ud.d, + sizeof(double)); + } + if (endptr && endptr[0] == ':') { + uint64 sig; + union ieee754_double ud; + sig = strtoull(endptr + 1, &endptr, 0); + ud.d = u.val; + if (is_little_endian()) { + ud.ieee.ieee_little_endian.mantissa0 = sig >> 32; + ud.ieee.ieee_little_endian.mantissa1 = (uint32)sig; + } + else { + ud.ieee.ieee_big_endian.mantissa0 = sig >> 32; + ud.ieee.ieee_big_endian.mantissa1 = (uint32)sig; + } + bh_memcpy_s(&u.val, sizeof(double), &ud.d, + sizeof(double)); + } + } + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; + break; + } +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + { + /* it likes 0x123\0x234 or 123\234 */ + /* retrieve first i64 */ + *(uint64 *)(argv1 + p) = strtoull(argv[i], &endptr, 0); + /* skip \ */ + endptr++; + /* retrieve second i64 */ + *(uint64 *)(argv1 + p + 2) = strtoull(endptr, &endptr, 0); + p += 4; + break; + } +#endif /* WASM_ENABLE_SIMD != 0 */ +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#if UINTPTR_MAX == UINT32_MAX + case VALUE_TYPE_EXTERNREF: +#endif + { + if (strncasecmp(argv[i], "null", 4) == 0) { + argv1[p++] = (uint32)-1; + } + else { + argv1[p++] = (uint32)strtoul(argv[i], &endptr, 0); + } + break; + } +#if UINTPTR_MAX == UINT64_MAX + case VALUE_TYPE_EXTERNREF: + { + union { + uintptr_t val; + uint32 parts[2]; + } u; + if (strncasecmp(argv[i], "null", 4) == 0) { + u.val = (uintptr_t)-1LL; + } + else { + u.val = strtoull(argv[i], &endptr, 0); + } + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; + break; + } +#endif +#endif /* WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 */ + default: + { +#if WASM_ENABLE_GC != 0 + bool is_extern_ref = false; + bool is_anyref = false; + + if (wasm_is_type_reftype(type->types[i])) { + if (strncasecmp(argv[i], "null", 4) == 0) { + PUT_REF_TO_ADDR(argv1 + p, NULL_REF); + p += REF_CELL_NUM; + break; + } + else if (type->types[i] == VALUE_TYPE_EXTERNREF) { + is_extern_ref = true; + } + else if (type->types[i] == VALUE_TYPE_ANYREF) { + is_anyref = true; + } + + if (wasm_is_type_multi_byte_type(type->types[i])) { + WASMRefType *ref_type = ref_type_map->ref_type; + if (wasm_is_refheaptype_common( + &ref_type->ref_ht_common)) { + int32 heap_type = ref_type->ref_ht_common.heap_type; + if (heap_type == HEAP_TYPE_EXTERN) { + is_extern_ref = true; + } + else if (heap_type == HEAP_TYPE_ANY) { + is_anyref = true; + } + } + + ref_type_map++; + } + + if (is_extern_ref) { + WASMExternrefObjectRef gc_obj; + void *extern_obj = + (void *)(uintptr_t)strtoull(argv[i], &endptr, 0); + gc_obj = wasm_externref_obj_new(exec_env, extern_obj); + if (!gc_obj) { + wasm_runtime_set_exception( + module_inst, "create extern object failed"); + goto fail; + } + if (!(local_ref = + runtime_malloc(sizeof(WASMLocalObjectRef), + module_inst, NULL, 0))) { + goto fail; + } + wasm_runtime_push_local_obj_ref(exec_env, local_ref); + local_ref->val = (WASMObjectRef)gc_obj; + num_local_ref_pushed++; + PUT_REF_TO_ADDR(argv1 + p, gc_obj); + p += REF_CELL_NUM; + } + else if (is_anyref) { + /* If a parameter type is (ref null? any) and its value + * is not null, then we treat the value as host ptr */ + WASMAnyrefObjectRef gc_obj; + void *host_obj = + (void *)(uintptr_t)strtoull(argv[i], &endptr, 0); + gc_obj = wasm_anyref_obj_new(exec_env, host_obj); + if (!gc_obj) { + wasm_runtime_set_exception( + module_inst, "create anyref object failed"); + goto fail; + } + if (!(local_ref = + runtime_malloc(sizeof(WASMLocalObjectRef), + module_inst, NULL, 0))) { + goto fail; + } + wasm_runtime_push_local_obj_ref(exec_env, local_ref); + local_ref->val = (WASMObjectRef)gc_obj; + num_local_ref_pushed++; + PUT_REF_TO_ADDR(argv1 + p, gc_obj); + p += REF_CELL_NUM; + } + + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + bh_assert(0); + break; + } + } + if (endptr && *endptr != '\0' && *endptr != '_') { + snprintf(buf, sizeof(buf), "invalid input argument %" PRId32 ": %s", + i, argv[i]); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + } + + wasm_runtime_set_exception(module_inst, NULL); +#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0 + bh_assert(p == (int32)argc1); +#endif + + if (!wasm_runtime_call_wasm(exec_env, target_func, argc1, argv1)) { + goto fail; + } + +#if WASM_ENABLE_GC != 0 + ref_type_map = type->result_ref_type_maps; +#endif + /* print return value */ + for (j = 0; j < type->result_count; j++) { + switch (type->types[type->param_count + j]) { + case VALUE_TYPE_I32: + { + os_printf("0x%" PRIx32 ":i32", argv1[k]); + k++; + break; + } + case VALUE_TYPE_I64: + { + union { + uint64 val; + uint32 parts[2]; + } u; + u.parts[0] = argv1[k]; + u.parts[1] = argv1[k + 1]; + k += 2; + os_printf("0x%" PRIx64 ":i64", u.val); + break; + } + case VALUE_TYPE_F32: + { + // Explicit cast to double to avoid warning. + // Float arguments are promoted to double in variadic + // functions per section 6.5.2.2 of the C99 standard. + os_printf("%.7g:f32", (double)*(float32 *)(argv1 + k)); + k++; + break; + } + case VALUE_TYPE_F64: + { + union { + float64 val; + uint32 parts[2]; + } u; + u.parts[0] = argv1[k]; + u.parts[1] = argv1[k + 1]; + k += 2; + os_printf("%.7g:f64", u.val); + break; + } +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + { + if (argv1[k] != NULL_REF) + os_printf("%" PRIu32 ":ref.func", argv1[k]); + else + os_printf("func:ref.null"); + k++; + break; + } + case VALUE_TYPE_EXTERNREF: + { +#if UINTPTR_MAX == UINT32_MAX + if (argv1[k] != 0 && argv1[k] != (uint32)-1) + os_printf("0x%" PRIxPTR ":ref.extern", (uintptr_t)argv1[k]); + else + os_printf("extern:ref.null"); + k++; +#else + union { + uintptr_t val; + uint32 parts[2]; + } u; + u.parts[0] = argv1[k]; + u.parts[1] = argv1[k + 1]; + k += 2; + if (u.val && u.val != (uintptr_t)-1LL) + os_printf("0x%" PRIxPTR ":ref.extern", u.val); + else + os_printf("extern:ref.null"); +#endif + break; + } +#endif /* end of WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 */ +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + { + uint64 *v = (uint64 *)(argv1 + k); + os_printf("<0x%016" PRIx64 " 0x%016" PRIx64 ">:v128", *v, + *(v + 1)); + k += 4; + break; + } +#endif /* WASM_ENABLE_SIMD != 0 */ + default: + { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_reftype(type->types[type->param_count + j])) { + void *gc_obj = GET_REF_FROM_ADDR(argv1 + k); + k += REF_CELL_NUM; + if (!gc_obj) { + uint8 type1 = type->types[type->param_count + j]; + WASMRefType *ref_type1 = NULL; + WASMType **types = NULL; + uint32 type_count = 0; + + if (wasm_is_type_multi_byte_type( + type->types[type->param_count + j])) + ref_type1 = ref_type_map->ref_type; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModule *module = + ((WASMModuleInstance *)module_inst)->module; + types = module->types; + type_count = module->type_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)module_inst) + ->module; + types = module->types; + type_count = module->type_count; + } +#endif + bh_assert(type); + if (wasm_reftype_is_subtype_of(type1, ref_type1, + REF_TYPE_ANYREF, NULL, + types, type_count)) + os_printf("any:"); + else if (wasm_reftype_is_subtype_of( + type1, ref_type1, REF_TYPE_FUNCREF, NULL, + types, type_count)) + os_printf("func:"); + if (wasm_reftype_is_subtype_of(type1, ref_type1, + REF_TYPE_EXTERNREF, NULL, + types, type_count)) + os_printf("extern:"); + os_printf("ref.null"); + } + else if (wasm_obj_is_func_obj(gc_obj)) + os_printf("ref.func"); +#if WASM_ENABLE_STRINGREF != 0 + else if (wasm_obj_is_stringref_obj(gc_obj) + || wasm_obj_is_stringview_wtf8_obj(gc_obj)) { + wasm_string_dump( + (WASMString)wasm_stringref_obj_get_value(gc_obj)); + } + else if (wasm_obj_is_stringview_wtf16_obj(gc_obj)) { + wasm_string_dump( + (WASMString)wasm_stringview_wtf16_obj_get_value( + gc_obj)); + } +#endif + else if (wasm_obj_is_externref_obj(gc_obj)) { +#if WASM_ENABLE_SPEC_TEST != 0 + WASMObjectRef obj = wasm_externref_obj_to_internal_obj( + (WASMExternrefObjectRef)gc_obj); + if (wasm_obj_is_anyref_obj(obj)) + os_printf("0x%" PRIxPTR ":ref.extern", + (uintptr_t)wasm_anyref_obj_get_value( + (WASMAnyrefObjectRef)obj)); + else +#endif + os_printf("ref.extern"); + } + else if (wasm_obj_is_i31_obj(gc_obj)) + os_printf("ref.i31"); + else if (wasm_obj_is_array_obj(gc_obj)) + os_printf("ref.array"); + else if (wasm_obj_is_struct_obj(gc_obj)) + os_printf("ref.struct"); + else if (wasm_obj_is_eq_obj(gc_obj)) + os_printf("ref.eq"); + else if (wasm_obj_is_anyref_obj(gc_obj)) + os_printf("0x%" PRIxPTR ":ref.host", + (uintptr_t)wasm_anyref_obj_get_value( + (WASMAnyrefObjectRef)gc_obj)); + else if (wasm_obj_is_internal_obj(gc_obj)) + os_printf("ref.any"); + + if (wasm_is_type_multi_byte_type( + type->types[type->param_count + j])) + ref_type_map++; + + break; + } +#endif /* endof WASM_ENABLE_GC != 0 */ + bh_assert(0); + break; + } + } + if (j < (uint32)(type->result_count - 1)) + os_printf(","); + } + os_printf("\n"); + +#if WASM_ENABLE_GC != 0 + for (j = 0; j < num_local_ref_pushed; j++) { + local_ref = wasm_runtime_pop_local_obj_ref(exec_env); + wasm_runtime_free(local_ref); + } +#endif + + wasm_runtime_free(argv1); + return true; + +fail: + if (argv1) + wasm_runtime_free(argv1); + +#if WASM_ENABLE_GC != 0 + for (j = 0; j < num_local_ref_pushed; j++) { + local_ref = wasm_runtime_pop_local_obj_ref(exec_env); + wasm_runtime_free(local_ref); + } +#endif + + bh_assert(wasm_runtime_get_exception(module_inst)); + return false; +} + +bool +wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, + const char *name, int32 argc, char *argv[]) +{ + bool ret; +#if WASM_ENABLE_MEMORY_PROFILING != 0 + WASMExecEnv *exec_env; +#endif + + ret = execute_func(module_inst, name, argc, argv); + +#if WASM_ENABLE_MEMORY_PROFILING != 0 + exec_env = wasm_runtime_get_exec_env_singleton(module_inst); + if (exec_env) { + wasm_runtime_dump_mem_consumption(exec_env); + } +#endif + +#if WASM_ENABLE_GC_PERF_PROFILING != 0 + void *handle = wasm_runtime_get_gc_heap_handle(module_inst); + mem_allocator_dump_perf_profiling(handle); +#endif + +#if WASM_ENABLE_PERF_PROFILING != 0 + wasm_runtime_dump_perf_profiling(module_inst); +#endif + + return (ret && !wasm_runtime_get_exception(module_inst)) ? true : false; +} diff --git a/core/iwasm/common/wasm_blocking_op.c b/core/iwasm/common/wasm_blocking_op.c new file mode 100644 index 0000000000..25777c8d74 --- /dev/null +++ b/core/iwasm/common/wasm_blocking_op.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_runtime_common.h" + +#include "bh_platform.h" +#include "bh_common.h" +#include "bh_assert.h" + +#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP) + +#define LOCK(env) WASM_SUSPEND_FLAGS_LOCK((env)->wait_lock) +#define UNLOCK(env) WASM_SUSPEND_FLAGS_UNLOCK((env)->wait_lock) + +#define ISSET(env, bit) \ + ((WASM_SUSPEND_FLAGS_GET((env)->suspend_flags) & WASM_SUSPEND_FLAG_##bit) \ + != 0) +#define SET(env, bit) \ + WASM_SUSPEND_FLAGS_FETCH_OR((env)->suspend_flags, WASM_SUSPEND_FLAG_##bit) +#define CLR(env, bit) \ + WASM_SUSPEND_FLAGS_FETCH_AND((env)->suspend_flags, ~WASM_SUSPEND_FLAG_##bit) + +bool +wasm_runtime_begin_blocking_op(wasm_exec_env_t env) +{ + LOCK(env); + bh_assert(!ISSET(env, BLOCKING)); + SET(env, BLOCKING); + if (ISSET(env, TERMINATE)) { + CLR(env, BLOCKING); + UNLOCK(env); + return false; + } + UNLOCK(env); + os_begin_blocking_op(); + return true; +} + +void +wasm_runtime_end_blocking_op(wasm_exec_env_t env) +{ + int saved_errno = errno; + LOCK(env); + bh_assert(ISSET(env, BLOCKING)); + CLR(env, BLOCKING); + UNLOCK(env); + os_end_blocking_op(); + errno = saved_errno; +} + +void +wasm_runtime_interrupt_blocking_op(wasm_exec_env_t env) +{ + /* + * ISSET(BLOCKING) here means that the target thread + * is in somewhere between wasm_begin_blocking_op and + * wasm_end_blocking_op. + * keep waking it up until it reaches wasm_end_blocking_op, + * which clears the BLOCKING bit. + * + * this dumb loop is necessary because posix doesn't provide + * a way to unmask signal and block atomically. + */ + + LOCK(env); + SET(env, TERMINATE); + while (ISSET(env, BLOCKING)) { + UNLOCK(env); + os_wakeup_blocking_op(env->handle); + + /* relax a bit */ + os_usleep(50 * 1000); + LOCK(env); + } + UNLOCK(env); +} + +#else /* WASM_ENABLE_THREAD_MGR && OS_ENABLE_WAKEUP_BLOCKING_OP */ + +bool +wasm_runtime_begin_blocking_op(wasm_exec_env_t env) +{ + return true; +} + +void +wasm_runtime_end_blocking_op(wasm_exec_env_t env) +{} + +#endif /* WASM_ENABLE_THREAD_MGR && OS_ENABLE_WAKEUP_BLOCKING_OP */ diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c new file mode 100644 index 0000000000..53d3a87f04 --- /dev/null +++ b/core/iwasm/common/wasm_c_api.c @@ -0,0 +1,5416 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_log.h" +#include "wasm_c_api_internal.h" + +#include "bh_assert.h" +#include "wasm_export.h" +#include "wasm_memory.h" +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT == 0 +#include "aot.h" +#include "aot_llvm.h" +#endif /*WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT == 0*/ +#endif /*WASM_ENABLE_AOT != 0*/ + +#if WASM_ENABLE_WASM_CACHE != 0 +#include +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "thread_manager.h" +#endif + +/* + * Thread Model: + * - Only one wasm_engine_t in one process + * - One wasm_store_t is only accessed by one thread. wasm_store_t can't be + * shared in threads + * - wasm_module_t can be shared in threads + * - wasm_instance_t can not be shared in threads + */ + +#define ASSERT_NOT_IMPLEMENTED() bh_assert(!"not implemented") +#define UNREACHABLE() bh_assert(!"unreachable") + +typedef struct wasm_module_ex_t { + struct WASMModuleCommon *module_comm_rt; + wasm_byte_vec_t *binary; + /* If true, binary in wasm_module_ex_t contains a copy of the WASM binary */ + bool is_binary_cloned; + korp_mutex lock; + uint32 ref_count; +#if WASM_ENABLE_WASM_CACHE != 0 + char hash[SHA256_DIGEST_LENGTH]; +#endif +} wasm_module_ex_t; + +#ifndef os_thread_local_attribute +typedef struct thread_local_stores { + korp_tid tid; + unsigned stores_num; +} thread_local_stores; +#endif + +static void +wasm_module_delete_internal(wasm_module_t *); + +static void +wasm_instance_delete_internal(wasm_instance_t *); + +/* temporarily put stubs here */ +static wasm_store_t * +wasm_store_copy(const wasm_store_t *src) +{ + (void)src; + LOG_WARNING("in the stub of %s", __FUNCTION__); + return NULL; +} + +wasm_module_t * +wasm_module_copy(const wasm_module_t *src) +{ + (void)src; + LOG_WARNING("in the stub of %s", __FUNCTION__); + return NULL; +} + +wasm_instance_t * +wasm_instance_copy(const wasm_instance_t *src) +{ + (void)src; + LOG_WARNING("in the stub of %s", __FUNCTION__); + return NULL; +} + +/* ---------------------------------------------------------------------- */ +static inline void * +malloc_internal(uint64 size) +{ + void *mem = NULL; + + if (size < UINT32_MAX && (mem = wasm_runtime_malloc((uint32)size))) { + memset(mem, 0, size); + } + + return mem; +} + +/* clang-format off */ +#define RETURN_OBJ(obj, obj_del_func) \ + return obj; \ +failed: \ + obj_del_func(obj); \ + return NULL; + +#define RETURN_VOID(obj, obj_del_func) \ + return; \ +failed: \ + obj_del_func(obj); \ + return; +/* clang-format on */ + +/* Vectors */ +#define INIT_VEC(vector_p, init_func, ...) \ + do { \ + if (!(vector_p = malloc_internal(sizeof(*(vector_p))))) { \ + goto failed; \ + } \ + \ + init_func(vector_p, ##__VA_ARGS__); \ + if (vector_p->size && !vector_p->data) { \ + LOG_DEBUG("%s failed", #init_func); \ + goto failed; \ + } \ + } while (false) + +#define DEINIT_VEC(vector_p, deinit_func) \ + if ((vector_p)) { \ + deinit_func(vector_p); \ + wasm_runtime_free(vector_p); \ + vector_p = NULL; \ + } + +#define WASM_DEFINE_VEC(name) \ + void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t *out) \ + { \ + wasm_##name##_vec_new_uninitialized(out, 0); \ + } \ + void wasm_##name##_vec_new_uninitialized(own wasm_##name##_vec_t *out, \ + size_t size) \ + { \ + wasm_##name##_vec_new(out, size, NULL); \ + } + +/* vectors with no ownership management of elements */ +#define WASM_DEFINE_VEC_PLAIN(name) \ + WASM_DEFINE_VEC(name) \ + void wasm_##name##_vec_new(own wasm_##name##_vec_t *out, size_t size, \ + own wasm_##name##_t const data[]) \ + { \ + if (!out) { \ + return; \ + } \ + \ + memset(out, 0, sizeof(wasm_##name##_vec_t)); \ + \ + if (!size) { \ + return; \ + } \ + \ + if (!bh_vector_init((Vector *)out, size, sizeof(wasm_##name##_t), \ + true)) { \ + LOG_DEBUG("bh_vector_init failed"); \ + goto failed; \ + } \ + \ + if (data) { \ + uint32 size_in_bytes = 0; \ + size_in_bytes = (uint32)(size * sizeof(wasm_##name##_t)); \ + bh_memcpy_s(out->data, size_in_bytes, data, size_in_bytes); \ + out->num_elems = size; \ + } \ + \ + RETURN_VOID(out, wasm_##name##_vec_delete) \ + } \ + void wasm_##name##_vec_copy(wasm_##name##_vec_t *out, \ + const wasm_##name##_vec_t *src) \ + { \ + if (!src) { \ + return; \ + } \ + wasm_##name##_vec_new(out, src->size, src->data); \ + } \ + void wasm_##name##_vec_delete(wasm_##name##_vec_t *v) \ + { \ + if (v) { \ + bh_vector_destroy((Vector *)v); \ + } \ + } + +/* vectors that own their elements */ +#define WASM_DEFINE_VEC_OWN(name, elem_destroy_func) \ + WASM_DEFINE_VEC(name) \ + void wasm_##name##_vec_new(own wasm_##name##_vec_t *out, size_t size, \ + own wasm_##name##_t *const data[]) \ + { \ + if (!out) { \ + return; \ + } \ + \ + memset(out, 0, sizeof(wasm_##name##_vec_t)); \ + \ + if (!size) { \ + return; \ + } \ + \ + if (!bh_vector_init((Vector *)out, size, sizeof(wasm_##name##_t *), \ + true)) { \ + LOG_DEBUG("bh_vector_init failed"); \ + goto failed; \ + } \ + \ + if (data) { \ + uint32 size_in_bytes = 0; \ + size_in_bytes = (uint32)(size * sizeof(wasm_##name##_t *)); \ + bh_memcpy_s(out->data, size_in_bytes, data, size_in_bytes); \ + out->num_elems = size; \ + } \ + \ + RETURN_VOID(out, wasm_##name##_vec_delete) \ + } \ + void wasm_##name##_vec_copy(own wasm_##name##_vec_t *out, \ + const wasm_##name##_vec_t *src) \ + { \ + size_t i = 0; \ + \ + if (!out) { \ + return; \ + } \ + memset(out, 0, sizeof(Vector)); \ + \ + if (!src || !src->size) { \ + return; \ + } \ + \ + if (!bh_vector_init((Vector *)out, src->size, \ + sizeof(wasm_##name##_t *), true)) { \ + LOG_DEBUG("bh_vector_init failed"); \ + goto failed; \ + } \ + \ + for (i = 0; i != src->num_elems; ++i) { \ + if (!(out->data[i] = wasm_##name##_copy(src->data[i]))) { \ + LOG_DEBUG("wasm_%s_copy failed", #name); \ + goto failed; \ + } \ + } \ + out->num_elems = src->num_elems; \ + \ + RETURN_VOID(out, wasm_##name##_vec_delete) \ + } \ + void wasm_##name##_vec_delete(wasm_##name##_vec_t *v) \ + { \ + size_t i = 0; \ + if (!v) { \ + return; \ + } \ + for (i = 0; i != v->num_elems && v->data; ++i) { \ + elem_destroy_func(*(v->data + i)); \ + } \ + bh_vector_destroy((Vector *)v); \ + } + +WASM_DEFINE_VEC_PLAIN(byte) +WASM_DEFINE_VEC_PLAIN(val) + +WASM_DEFINE_VEC_OWN(exporttype, wasm_exporttype_delete) +WASM_DEFINE_VEC_OWN(extern, wasm_extern_delete) +WASM_DEFINE_VEC_OWN(frame, wasm_frame_delete) +WASM_DEFINE_VEC_OWN(functype, wasm_functype_delete) +WASM_DEFINE_VEC_OWN(importtype, wasm_importtype_delete) +WASM_DEFINE_VEC_OWN(instance, wasm_instance_delete_internal) +WASM_DEFINE_VEC_OWN(module, wasm_module_delete_internal) +WASM_DEFINE_VEC_OWN(store, wasm_store_delete) +WASM_DEFINE_VEC_OWN(valtype, wasm_valtype_delete) + +#ifndef NDEBUG +#if WASM_ENABLE_MEMORY_PROFILING != 0 +#define WASM_C_DUMP_PROC_MEM() LOG_PROC_MEM() +#else +#define WASM_C_DUMP_PROC_MEM() (void)0 +#endif +#else +#define WASM_C_DUMP_PROC_MEM() (void)0 +#endif + +/* Runtime Environment */ +own wasm_config_t * +wasm_config_new(void) +{ + /* since wasm_runtime_malloc is not ready */ + wasm_config_t *config = os_malloc(sizeof(wasm_config_t)); + if (!config) + return NULL; + + memset(config, 0, sizeof(wasm_config_t)); + config->mem_alloc_type = Alloc_With_System_Allocator; + + return config; +} + +void +wasm_config_delete(own wasm_config_t *config) +{ + if (config) + os_free(config); +} + +wasm_config_t * +wasm_config_set_mem_alloc_opt(wasm_config_t *config, + mem_alloc_type_t mem_alloc_type, + MemAllocOption *mem_alloc_option) +{ + if (!config) + return NULL; + + config->mem_alloc_type = mem_alloc_type; + if (mem_alloc_option) + memcpy(&config->mem_alloc_option, mem_alloc_option, + sizeof(MemAllocOption)); + return config; +} + +wasm_config_t * +wasm_config_set_linux_perf_opt(wasm_config_t *config, bool enable) +{ + if (!config) + return NULL; + + config->enable_linux_perf = enable; + return config; +} + +wasm_config_t * +wasm_config_set_segue_flags(wasm_config_t *config, uint32 segue_flags) +{ + if (!config) + return NULL; + + config->segue_flags = segue_flags; + return config; +} + +static void +wasm_engine_delete_internal(wasm_engine_t *engine) +{ + if (engine) { + /* clean all created wasm_module_t and their locks */ + unsigned i; + + for (i = 0; i < engine->modules.num_elems; i++) { + wasm_module_ex_t *module; + if (bh_vector_get(&engine->modules, i, &module)) { + os_mutex_destroy(&module->lock); + wasm_runtime_free(module); + } + } + + bh_vector_destroy(&engine->modules); + +#ifndef os_thread_local_attribute + bh_vector_destroy(&engine->stores_by_tid); +#endif + + wasm_runtime_free(engine); + } + + wasm_runtime_destroy(); +} + +static wasm_engine_t * +wasm_engine_new_internal(wasm_config_t *config) +{ + wasm_engine_t *engine = NULL; + /* init runtime */ + RuntimeInitArgs init_args = { 0 }; +#if WASM_ENABLE_JIT != 0 + LLVMJITOptions *jit_options = wasm_runtime_get_llvm_jit_options(); +#endif + +#ifndef NDEBUG + bh_log_set_verbose_level(BH_LOG_LEVEL_VERBOSE); +#else + bh_log_set_verbose_level(BH_LOG_LEVEL_WARNING); +#endif + + WASM_C_DUMP_PROC_MEM(); + + /* wasm_config_t->MemAllocOption -> RuntimeInitArgs->MemAllocOption */ + init_args.mem_alloc_type = config->mem_alloc_type; + memcpy(&init_args.mem_alloc_option, &config->mem_alloc_option, + sizeof(MemAllocOption)); + init_args.enable_linux_perf = config->enable_linux_perf; + init_args.segue_flags = config->segue_flags; + +#if WASM_ENABLE_JIT != 0 + jit_options->quick_invoke_c_api_import = true; +#endif + + if (!wasm_runtime_full_init(&init_args)) { + LOG_DEBUG("wasm_runtime_full_init failed"); + goto failed; + } + + /* create wasm_engine_t */ + if (!(engine = malloc_internal(sizeof(wasm_engine_t)))) { + goto failed; + } + + if (!bh_vector_init(&engine->modules, DEFAULT_VECTOR_INIT_SIZE, + sizeof(wasm_module_ex_t *), true)) + goto failed; + +#ifndef os_thread_local_attribute + if (!bh_vector_init(&engine->stores_by_tid, DEFAULT_VECTOR_INIT_SIZE, + sizeof(thread_local_stores), true)) + goto failed; +#endif + + engine->ref_count = 1; + + WASM_C_DUMP_PROC_MEM(); + + RETURN_OBJ(engine, wasm_engine_delete_internal) +} + +/* global engine instance */ +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; +#endif +#if defined(OS_THREAD_MUTEX_INITIALIZER) +/** + * lock for the singleton_engine + * Note: if the platform has mutex initializer, we use a global lock to + * lock the operations of the singleton_engine, otherwise when there are + * operations happening simultaneously in multiple threads, developer + * must create the lock by himself, and use it to lock the operations + */ +static korp_mutex engine_lock = OS_THREAD_MUTEX_INITIALIZER; +#endif + +own wasm_engine_t * +wasm_engine_new() +{ + wasm_config_t config = { 0 }; + wasm_config_set_mem_alloc_opt(&config, Alloc_With_System_Allocator, NULL); + wasm_engine_t *engine = wasm_engine_new_with_config(&config); + return engine; +} + +own wasm_engine_t * +wasm_engine_new_with_config(wasm_config_t *config) +{ +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&engine_lock); +#endif + + if (!singleton_engine) + singleton_engine = wasm_engine_new_internal(config); + else + singleton_engine->ref_count++; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&engine_lock); +#endif + + return singleton_engine; +} + +own wasm_engine_t * +wasm_engine_new_with_args(mem_alloc_type_t type, const MemAllocOption *opts) +{ + wasm_config_t config = { 0 }; + config.mem_alloc_type = type; + memcpy(&config.mem_alloc_option, opts, sizeof(MemAllocOption)); + return wasm_engine_new_with_config(&config); +} + +void +wasm_engine_delete(wasm_engine_t *engine) +{ + if (!engine) + return; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&engine_lock); +#endif + + if (!singleton_engine) { +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&engine_lock); +#endif + return; + } + + bh_assert(engine == singleton_engine); + bh_assert(singleton_engine->ref_count > 0); + + singleton_engine->ref_count--; + if (singleton_engine->ref_count == 0) { + wasm_engine_delete_internal(engine); + singleton_engine = NULL; + } + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&engine_lock); +#endif +} + +#ifndef os_thread_local_attribute +static bool +search_thread_local_store_num(Vector *stores_by_tid, korp_tid tid, + thread_local_stores *out_ts, unsigned *out_i) +{ + unsigned i; + + for (i = 0; i < stores_by_tid->num_elems; i++) { + bool ret = bh_vector_get(stores_by_tid, i, out_ts); + bh_assert(ret); + (void)ret; + + if (out_ts->tid == tid) { + *out_i = i; + return true; + } + } + + return false; +} +#endif + +static unsigned +retrieve_thread_local_store_num(Vector *stores_by_tid, korp_tid tid) +{ +#ifndef os_thread_local_attribute + unsigned i = 0; + thread_local_stores ts = { 0 }; + unsigned ret = 0; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&engine_lock); +#endif + + if (search_thread_local_store_num(stores_by_tid, tid, &ts, &i)) + ret = ts.stores_num; + else + ret = 0; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&engine_lock); +#endif + + return ret; +#else + (void)stores_by_tid; + (void)tid; + + return thread_local_stores_num; +#endif +} + +static bool +increase_thread_local_store_num(Vector *stores_by_tid, korp_tid tid) +{ +#ifndef os_thread_local_attribute + unsigned i = 0; + thread_local_stores ts = { 0 }; + bool ret = false; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&engine_lock); +#endif + + if (search_thread_local_store_num(stores_by_tid, tid, &ts, &i)) { + /* just in case if integer overflow */ + if (ts.stores_num + 1 < ts.stores_num) { + ret = false; + } + else { + ts.stores_num++; + ret = bh_vector_set(stores_by_tid, i, &ts); + bh_assert(ret); + } + } + else { + ts.tid = tid; + ts.stores_num = 1; + ret = bh_vector_append(stores_by_tid, &ts); + } + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&engine_lock); +#endif + return ret; +#else + (void)stores_by_tid; + (void)tid; + + /* just in case if integer overflow */ + if (thread_local_stores_num + 1 < thread_local_stores_num) + return false; + + thread_local_stores_num++; + return true; +#endif +} + +static bool +decrease_thread_local_store_num(Vector *stores_by_tid, korp_tid tid) +{ +#ifndef os_thread_local_attribute + unsigned i = 0; + thread_local_stores ts = { 0 }; + bool ret = false; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&engine_lock); +#endif + + ret = search_thread_local_store_num(stores_by_tid, tid, &ts, &i); + bh_assert(ret); + + /* just in case if integer overflow */ + if (ts.stores_num - 1 > ts.stores_num) { + ret = false; + } + else { + ts.stores_num--; + ret = bh_vector_set(stores_by_tid, i, &ts); + bh_assert(ret); + } + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&engine_lock); +#endif + + return ret; +#else + (void)stores_by_tid; + (void)tid; + + /* just in case if integer overflow */ + if (thread_local_stores_num - 1 > thread_local_stores_num) + return false; + + thread_local_stores_num--; + return true; +#endif +} + +wasm_store_t * +wasm_store_new(wasm_engine_t *engine) +{ + wasm_store_t *store = NULL; + + WASM_C_DUMP_PROC_MEM(); + + if (!engine || singleton_engine != engine) + return NULL; + + if (!retrieve_thread_local_store_num(&engine->stores_by_tid, + os_self_thread())) { + if (!wasm_runtime_init_thread_env()) { + LOG_ERROR("init thread environment failed"); + return NULL; + } + + if (!increase_thread_local_store_num(&engine->stores_by_tid, + os_self_thread())) { + wasm_runtime_destroy_thread_env(); + return NULL; + } + + if (!(store = malloc_internal(sizeof(wasm_store_t)))) { + decrease_thread_local_store_num(&singleton_engine->stores_by_tid, + os_self_thread()); + wasm_runtime_destroy_thread_env(); + return NULL; + } + } + else { + if (!increase_thread_local_store_num(&engine->stores_by_tid, + os_self_thread())) + return NULL; + + if (!(store = malloc_internal(sizeof(wasm_store_t)))) { + decrease_thread_local_store_num(&singleton_engine->stores_by_tid, + os_self_thread()); + return NULL; + } + } + + /* new a vector, and new its data */ + INIT_VEC(store->modules, wasm_module_vec_new_uninitialized, + DEFAULT_VECTOR_INIT_LENGTH); + INIT_VEC(store->instances, wasm_instance_vec_new_uninitialized, + DEFAULT_VECTOR_INIT_LENGTH); + + if (!(store->foreigns = malloc_internal(sizeof(Vector))) + || !(bh_vector_init(store->foreigns, 24, sizeof(wasm_foreign_t *), + true))) { + goto failed; + } + + WASM_C_DUMP_PROC_MEM(); + + return store; +failed: + wasm_store_delete(store); + return NULL; +} + +void +wasm_store_delete(wasm_store_t *store) +{ + if (!store) { + return; + } + + DEINIT_VEC(store->instances, wasm_instance_vec_delete); + DEINIT_VEC(store->modules, wasm_module_vec_delete); + if (store->foreigns) { + bh_vector_destroy(store->foreigns); + wasm_runtime_free(store->foreigns); + } + + wasm_runtime_free(store); + + if (decrease_thread_local_store_num(&singleton_engine->stores_by_tid, + os_self_thread())) { + if (!retrieve_thread_local_store_num(&singleton_engine->stores_by_tid, + os_self_thread())) { + wasm_runtime_destroy_thread_env(); + } + } +} + +/* Type Representations */ +static inline wasm_valkind_t +val_type_rt_2_valkind(uint8 val_type_rt) +{ + switch (val_type_rt) { +#define WAMR_VAL_TYPE_2_WASM_VAL_KIND(name) \ + case VALUE_TYPE_##name: \ + return WASM_##name; + + WAMR_VAL_TYPE_2_WASM_VAL_KIND(I32) + WAMR_VAL_TYPE_2_WASM_VAL_KIND(I64) + WAMR_VAL_TYPE_2_WASM_VAL_KIND(F32) + WAMR_VAL_TYPE_2_WASM_VAL_KIND(F64) + WAMR_VAL_TYPE_2_WASM_VAL_KIND(V128) + WAMR_VAL_TYPE_2_WASM_VAL_KIND(FUNCREF) +#undef WAMR_VAL_TYPE_2_WASM_VAL_KIND + + default: + return WASM_EXTERNREF; + } +} + +static wasm_valtype_t * +wasm_valtype_new_internal(uint8 val_type_rt) +{ + return wasm_valtype_new(val_type_rt_2_valkind(val_type_rt)); +} + +wasm_valtype_t * +wasm_valtype_new(wasm_valkind_t kind) +{ + wasm_valtype_t *val_type; + + if (kind > WASM_V128 && WASM_FUNCREF != kind +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + && WASM_EXTERNREF != kind +#endif + ) { + return NULL; + } + + if (!(val_type = malloc_internal(sizeof(wasm_valtype_t)))) { + return NULL; + } + + val_type->kind = kind; + + return val_type; +} + +void +wasm_valtype_delete(wasm_valtype_t *val_type) +{ + if (val_type) { + wasm_runtime_free(val_type); + } +} + +wasm_valtype_t * +wasm_valtype_copy(const wasm_valtype_t *src) +{ + return src ? wasm_valtype_new(src->kind) : NULL; +} + +wasm_valkind_t +wasm_valtype_kind(const wasm_valtype_t *val_type) +{ + return val_type ? val_type->kind : WASM_EXTERNREF; +} + +static wasm_functype_t * +wasm_functype_new_internal(WASMFuncType *type_rt) +{ + wasm_functype_t *type = NULL; + wasm_valtype_t *param_type = NULL, *result_type = NULL; + uint32 i = 0; + + if (!type_rt) { + return NULL; + } + + if (!(type = malloc_internal(sizeof(wasm_functype_t)))) { + return NULL; + } + + type->extern_kind = WASM_EXTERN_FUNC; + + /* WASMFuncType->types[0 : type_rt->param_count) -> type->params */ + INIT_VEC(type->params, wasm_valtype_vec_new_uninitialized, + type_rt->param_count); + for (i = 0; i < type_rt->param_count; ++i) { + if (!(param_type = wasm_valtype_new_internal(*(type_rt->types + i)))) { + goto failed; + } + + if (!bh_vector_append((Vector *)type->params, ¶m_type)) { + LOG_DEBUG("bh_vector_append failed"); + goto failed; + } + } + + /* WASMFuncType->types[type_rt->param_count : type_rt->result_count) -> + * type->results */ + INIT_VEC(type->results, wasm_valtype_vec_new_uninitialized, + type_rt->result_count); + for (i = 0; i < type_rt->result_count; ++i) { + if (!(result_type = wasm_valtype_new_internal( + *(type_rt->types + type_rt->param_count + i)))) { + goto failed; + } + + if (!bh_vector_append((Vector *)type->results, &result_type)) { + LOG_DEBUG("bh_vector_append failed"); + goto failed; + } + } + + return type; + +failed: + wasm_valtype_delete(param_type); + wasm_valtype_delete(result_type); + wasm_functype_delete(type); + return NULL; +} + +wasm_functype_t * +wasm_functype_new(own wasm_valtype_vec_t *params, + own wasm_valtype_vec_t *results) +{ + wasm_functype_t *type = NULL; + + if (!(type = malloc_internal(sizeof(wasm_functype_t)))) { + goto failed; + } + + type->extern_kind = WASM_EXTERN_FUNC; + + /* take ownership */ + if (!(type->params = malloc_internal(sizeof(wasm_valtype_vec_t)))) { + goto failed; + } + if (params) { + bh_memcpy_s(type->params, sizeof(wasm_valtype_vec_t), params, + sizeof(wasm_valtype_vec_t)); + } + + if (!(type->results = malloc_internal(sizeof(wasm_valtype_vec_t)))) { + goto failed; + } + if (results) { + bh_memcpy_s(type->results, sizeof(wasm_valtype_vec_t), results, + sizeof(wasm_valtype_vec_t)); + } + + return type; + +failed: + wasm_functype_delete(type); + return NULL; +} + +wasm_functype_t * +wasm_functype_copy(const wasm_functype_t *src) +{ + wasm_functype_t *functype; + wasm_valtype_vec_t params = { 0 }, results = { 0 }; + + if (!src) { + return NULL; + } + + wasm_valtype_vec_copy(¶ms, src->params); + if (src->params->size && !params.data) { + goto failed; + } + + wasm_valtype_vec_copy(&results, src->results); + if (src->results->size && !results.data) { + goto failed; + } + + if (!(functype = wasm_functype_new(¶ms, &results))) { + goto failed; + } + + return functype; + +failed: + wasm_valtype_vec_delete(¶ms); + wasm_valtype_vec_delete(&results); + return NULL; +} + +void +wasm_functype_delete(wasm_functype_t *func_type) +{ + if (!func_type) { + return; + } + + DEINIT_VEC(func_type->params, wasm_valtype_vec_delete); + DEINIT_VEC(func_type->results, wasm_valtype_vec_delete); + + wasm_runtime_free(func_type); +} + +const wasm_valtype_vec_t * +wasm_functype_params(const wasm_functype_t *func_type) +{ + if (!func_type) { + return NULL; + } + + return func_type->params; +} + +const wasm_valtype_vec_t * +wasm_functype_results(const wasm_functype_t *func_type) +{ + if (!func_type) { + return NULL; + } + + return func_type->results; +} + +static bool +cmp_val_kind_with_val_type(wasm_valkind_t v_k, uint8 v_t) +{ + return (v_k == WASM_I32 && v_t == VALUE_TYPE_I32) + || (v_k == WASM_I64 && v_t == VALUE_TYPE_I64) + || (v_k == WASM_F32 && v_t == VALUE_TYPE_F32) + || (v_k == WASM_F64 && v_t == VALUE_TYPE_F64) + || (v_k == WASM_V128 && v_t == VALUE_TYPE_V128) + || (v_k == WASM_EXTERNREF && v_t == VALUE_TYPE_EXTERNREF) + || (v_k == WASM_FUNCREF && v_t == VALUE_TYPE_FUNCREF); +} + +/* + *to compare a function type of wasm-c-api with a function type of wasm_runtime + */ +static bool +wasm_functype_same_internal(const wasm_functype_t *type, + const WASMFuncType *type_intl) +{ + uint32 i = 0; + + if (!type || !type_intl || type->params->num_elems != type_intl->param_count + || type->results->num_elems != type_intl->result_count) + return false; + + for (i = 0; i < type->params->num_elems; i++) { + wasm_valtype_t *v_t = type->params->data[i]; + if (!cmp_val_kind_with_val_type(wasm_valtype_kind(v_t), + type_intl->types[i])) + return false; + } + + for (i = 0; i < type->results->num_elems; i++) { + wasm_valtype_t *v_t = type->results->data[i]; + if (!cmp_val_kind_with_val_type( + wasm_valtype_kind(v_t), + type_intl->types[i + type->params->num_elems])) + return false; + } + + return true; +} + +wasm_globaltype_t * +wasm_globaltype_new(own wasm_valtype_t *val_type, wasm_mutability_t mut) +{ + wasm_globaltype_t *global_type = NULL; + + if (!val_type) { + return NULL; + } + + if (!(global_type = malloc_internal(sizeof(wasm_globaltype_t)))) { + return NULL; + } + + global_type->extern_kind = WASM_EXTERN_GLOBAL; + global_type->val_type = val_type; + global_type->mutability = mut; + + return global_type; +} + +wasm_globaltype_t * +wasm_globaltype_new_internal(uint8 val_type_rt, bool is_mutable) +{ + wasm_globaltype_t *globaltype; + wasm_valtype_t *val_type; + + if (!(val_type = wasm_valtype_new(val_type_rt_2_valkind(val_type_rt)))) { + return NULL; + } + + if (!(globaltype = wasm_globaltype_new( + val_type, is_mutable ? WASM_VAR : WASM_CONST))) { + wasm_valtype_delete(val_type); + } + + return globaltype; +} + +void +wasm_globaltype_delete(wasm_globaltype_t *global_type) +{ + if (!global_type) { + return; + } + + if (global_type->val_type) { + wasm_valtype_delete(global_type->val_type); + global_type->val_type = NULL; + } + + wasm_runtime_free(global_type); +} + +wasm_globaltype_t * +wasm_globaltype_copy(const wasm_globaltype_t *src) +{ + wasm_globaltype_t *global_type; + wasm_valtype_t *val_type; + + if (!src) { + return NULL; + } + + if (!(val_type = wasm_valtype_copy(src->val_type))) { + return NULL; + } + + if (!(global_type = wasm_globaltype_new(val_type, src->mutability))) { + wasm_valtype_delete(val_type); + } + + return global_type; +} + +const wasm_valtype_t * +wasm_globaltype_content(const wasm_globaltype_t *global_type) +{ + if (!global_type) { + return NULL; + } + + return global_type->val_type; +} + +wasm_mutability_t +wasm_globaltype_mutability(const wasm_globaltype_t *global_type) +{ + if (!global_type) { + return false; + } + + return global_type->mutability; +} + +static wasm_tabletype_t * +wasm_tabletype_new_internal(uint8 val_type_rt, uint32 init_size, + uint32 max_size) +{ + wasm_tabletype_t *table_type; + wasm_limits_t limits = { init_size, max_size }; + wasm_valtype_t *val_type; + + if (!(val_type = wasm_valtype_new_internal(val_type_rt))) { + return NULL; + } + + if (!(table_type = wasm_tabletype_new(val_type, &limits))) { + wasm_valtype_delete(val_type); + } + + return table_type; +} + +wasm_tabletype_t * +wasm_tabletype_new(own wasm_valtype_t *val_type, const wasm_limits_t *limits) +{ + wasm_tabletype_t *table_type = NULL; + + if (!val_type || !limits) { + return NULL; + } + + if (wasm_valtype_kind(val_type) != WASM_FUNCREF +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + && wasm_valtype_kind(val_type) != WASM_EXTERNREF +#endif + ) { + return NULL; + } + + if (!(table_type = malloc_internal(sizeof(wasm_tabletype_t)))) { + return NULL; + } + + table_type->extern_kind = WASM_EXTERN_TABLE; + table_type->val_type = val_type; + table_type->limits.min = limits->min; + table_type->limits.max = limits->max; + + return table_type; +} + +wasm_tabletype_t * +wasm_tabletype_copy(const wasm_tabletype_t *src) +{ + wasm_tabletype_t *table_type; + wasm_valtype_t *val_type; + + if (!src) { + return NULL; + } + + if (!(val_type = wasm_valtype_copy(src->val_type))) { + return NULL; + } + + if (!(table_type = wasm_tabletype_new(val_type, &src->limits))) { + wasm_valtype_delete(val_type); + } + + return table_type; +} + +void +wasm_tabletype_delete(wasm_tabletype_t *table_type) +{ + if (!table_type) { + return; + } + + if (table_type->val_type) { + wasm_valtype_delete(table_type->val_type); + table_type->val_type = NULL; + } + + wasm_runtime_free(table_type); +} + +const wasm_valtype_t * +wasm_tabletype_element(const wasm_tabletype_t *table_type) +{ + if (!table_type) { + return NULL; + } + + return table_type->val_type; +} + +const wasm_limits_t * +wasm_tabletype_limits(const wasm_tabletype_t *table_type) +{ + if (!table_type) { + return NULL; + } + + return &(table_type->limits); +} + +static wasm_memorytype_t * +wasm_memorytype_new_internal(uint32 min_pages, uint32 max_pages) +{ + wasm_limits_t limits = { min_pages, max_pages }; + return wasm_memorytype_new(&limits); +} + +wasm_memorytype_t * +wasm_memorytype_new(const wasm_limits_t *limits) +{ + wasm_memorytype_t *memory_type = NULL; + + if (!limits) { + return NULL; + } + + if (!(memory_type = malloc_internal(sizeof(wasm_memorytype_t)))) { + return NULL; + } + + memory_type->extern_kind = WASM_EXTERN_MEMORY; + memory_type->limits.min = limits->min; + memory_type->limits.max = limits->max; + + return memory_type; +} + +wasm_memorytype_t * +wasm_memorytype_copy(const wasm_memorytype_t *src) +{ + if (!src) { + return NULL; + } + + return wasm_memorytype_new(&src->limits); +} + +void +wasm_memorytype_delete(wasm_memorytype_t *memory_type) +{ + if (memory_type) { + wasm_runtime_free(memory_type); + } +} + +const wasm_limits_t * +wasm_memorytype_limits(const wasm_memorytype_t *memory_type) +{ + if (!memory_type) { + return NULL; + } + + return &(memory_type->limits); +} + +wasm_externkind_t +wasm_externtype_kind(const wasm_externtype_t *extern_type) +{ + if (!extern_type) { + return WASM_EXTERN_FUNC; + } + + return extern_type->extern_kind; +} + +#define BASIC_FOUR_TYPE_LIST(V) \ + V(functype) \ + V(globaltype) \ + V(memorytype) \ + V(tabletype) + +#define WASM_EXTERNTYPE_AS_OTHERTYPE(name) \ + wasm_##name##_t *wasm_externtype_as_##name(wasm_externtype_t *extern_type) \ + { \ + return (wasm_##name##_t *)extern_type; \ + } + +BASIC_FOUR_TYPE_LIST(WASM_EXTERNTYPE_AS_OTHERTYPE) +#undef WASM_EXTERNTYPE_AS_OTHERTYPE + +#define WASM_OTHERTYPE_AS_EXTERNTYPE(name) \ + wasm_externtype_t *wasm_##name##_as_externtype(wasm_##name##_t *other) \ + { \ + return (wasm_externtype_t *)other; \ + } + +BASIC_FOUR_TYPE_LIST(WASM_OTHERTYPE_AS_EXTERNTYPE) +#undef WASM_OTHERTYPE_AS_EXTERNTYPE + +#define WASM_EXTERNTYPE_AS_OTHERTYPE_CONST(name) \ + const wasm_##name##_t *wasm_externtype_as_##name##_const( \ + const wasm_externtype_t *extern_type) \ + { \ + return (const wasm_##name##_t *)extern_type; \ + } + +BASIC_FOUR_TYPE_LIST(WASM_EXTERNTYPE_AS_OTHERTYPE_CONST) +#undef WASM_EXTERNTYPE_AS_OTHERTYPE_CONST + +#define WASM_OTHERTYPE_AS_EXTERNTYPE_CONST(name) \ + const wasm_externtype_t *wasm_##name##_as_externtype_const( \ + const wasm_##name##_t *other) \ + { \ + return (const wasm_externtype_t *)other; \ + } + +BASIC_FOUR_TYPE_LIST(WASM_OTHERTYPE_AS_EXTERNTYPE_CONST) +#undef WASM_OTHERTYPE_AS_EXTERNTYPE_CONST + +wasm_externtype_t * +wasm_externtype_copy(const wasm_externtype_t *src) +{ + wasm_externtype_t *extern_type = NULL; + + if (!src) { + return NULL; + } + + switch (src->extern_kind) { +#define COPY_EXTERNTYPE(NAME, name) \ + case WASM_EXTERN_##NAME: \ + { \ + extern_type = wasm_##name##_as_externtype( \ + wasm_##name##_copy(wasm_externtype_as_##name##_const(src))); \ + break; \ + } + COPY_EXTERNTYPE(FUNC, functype) + COPY_EXTERNTYPE(GLOBAL, globaltype) + COPY_EXTERNTYPE(MEMORY, memorytype) + COPY_EXTERNTYPE(TABLE, tabletype) +#undef COPY_EXTERNTYPE + default: + LOG_WARNING("%s meets unsupported kind %u", __FUNCTION__, + src->extern_kind); + break; + } + return extern_type; +} + +void +wasm_externtype_delete(wasm_externtype_t *extern_type) +{ + if (!extern_type) { + return; + } + + switch (wasm_externtype_kind(extern_type)) { + case WASM_EXTERN_FUNC: + wasm_functype_delete(wasm_externtype_as_functype(extern_type)); + break; + case WASM_EXTERN_GLOBAL: + wasm_globaltype_delete(wasm_externtype_as_globaltype(extern_type)); + break; + case WASM_EXTERN_MEMORY: + wasm_memorytype_delete(wasm_externtype_as_memorytype(extern_type)); + break; + case WASM_EXTERN_TABLE: + wasm_tabletype_delete(wasm_externtype_as_tabletype(extern_type)); + break; + default: + LOG_WARNING("%s meets unsupported type %u", __FUNCTION__, + wasm_externtype_kind(extern_type)); + break; + } +} + +own wasm_importtype_t * +wasm_importtype_new(own wasm_byte_vec_t *module_name, + own wasm_byte_vec_t *field_name, + own wasm_externtype_t *extern_type) +{ + wasm_importtype_t *import_type = NULL; + + if (!module_name || !field_name || !extern_type) { + return NULL; + } + + if (!(import_type = malloc_internal(sizeof(wasm_importtype_t)))) { + return NULL; + } + + /* take ownership */ + if (!(import_type->module_name = + malloc_internal(sizeof(wasm_byte_vec_t)))) { + goto failed; + } + bh_memcpy_s(import_type->module_name, sizeof(wasm_byte_vec_t), module_name, + sizeof(wasm_byte_vec_t)); + + if (!(import_type->name = malloc_internal(sizeof(wasm_byte_vec_t)))) { + goto failed; + } + bh_memcpy_s(import_type->name, sizeof(wasm_byte_vec_t), field_name, + sizeof(wasm_byte_vec_t)); + + import_type->extern_type = extern_type; + + return import_type; +failed: + wasm_importtype_delete(import_type); + return NULL; +} + +void +wasm_importtype_delete(own wasm_importtype_t *import_type) +{ + if (!import_type) { + return; + } + + DEINIT_VEC(import_type->module_name, wasm_byte_vec_delete); + DEINIT_VEC(import_type->name, wasm_byte_vec_delete); + wasm_externtype_delete(import_type->extern_type); + import_type->extern_type = NULL; + wasm_runtime_free(import_type); +} + +own wasm_importtype_t * +wasm_importtype_copy(const wasm_importtype_t *src) +{ + wasm_byte_vec_t module_name = { 0 }, name = { 0 }; + wasm_externtype_t *extern_type = NULL; + wasm_importtype_t *import_type = NULL; + + if (!src) { + return NULL; + } + + wasm_byte_vec_copy(&module_name, src->module_name); + if (src->module_name->size && !module_name.data) { + goto failed; + } + + wasm_byte_vec_copy(&name, src->name); + if (src->name->size && !name.data) { + goto failed; + } + + if (!(extern_type = wasm_externtype_copy(src->extern_type))) { + goto failed; + } + + if (!(import_type = + wasm_importtype_new(&module_name, &name, extern_type))) { + goto failed; + } + + return import_type; + +failed: + wasm_byte_vec_delete(&module_name); + wasm_byte_vec_delete(&name); + wasm_externtype_delete(extern_type); + wasm_importtype_delete(import_type); + return NULL; +} + +const wasm_byte_vec_t * +wasm_importtype_module(const wasm_importtype_t *import_type) +{ + if (!import_type) { + return NULL; + } + + return import_type->module_name; +} + +const wasm_byte_vec_t * +wasm_importtype_name(const wasm_importtype_t *import_type) +{ + if (!import_type) { + return NULL; + } + + return import_type->name; +} + +const wasm_externtype_t * +wasm_importtype_type(const wasm_importtype_t *import_type) +{ + if (!import_type) { + return NULL; + } + + 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) +{ + wasm_exporttype_t *export_type = NULL; + + if (!name || !extern_type) { + return NULL; + } + + if (!(export_type = malloc_internal(sizeof(wasm_exporttype_t)))) { + return NULL; + } + + if (!(export_type->name = malloc_internal(sizeof(wasm_byte_vec_t)))) { + wasm_exporttype_delete(export_type); + return NULL; + } + bh_memcpy_s(export_type->name, sizeof(wasm_byte_vec_t), name, + sizeof(wasm_byte_vec_t)); + + export_type->extern_type = extern_type; + + return export_type; +} + +wasm_exporttype_t * +wasm_exporttype_copy(const wasm_exporttype_t *src) +{ + wasm_exporttype_t *export_type; + wasm_byte_vec_t name = { 0 }; + wasm_externtype_t *extern_type = NULL; + + if (!src) { + return NULL; + } + + wasm_byte_vec_copy(&name, src->name); + if (src->name->size && !name.data) { + goto failed; + } + + if (!(extern_type = wasm_externtype_copy(src->extern_type))) { + goto failed; + } + + if (!(export_type = wasm_exporttype_new(&name, extern_type))) { + goto failed; + } + + return export_type; +failed: + wasm_byte_vec_delete(&name); + wasm_externtype_delete(extern_type); + return NULL; +} + +void +wasm_exporttype_delete(wasm_exporttype_t *export_type) +{ + if (!export_type) { + return; + } + + DEINIT_VEC(export_type->name, wasm_byte_vec_delete); + + wasm_externtype_delete(export_type->extern_type); + + wasm_runtime_free(export_type); +} + +const wasm_byte_vec_t * +wasm_exporttype_name(const wasm_exporttype_t *export_type) +{ + if (!export_type) { + return NULL; + } + return export_type->name; +} + +const wasm_externtype_t * +wasm_exporttype_type(const wasm_exporttype_t *export_type) +{ + if (!export_type) { + return NULL; + } + return export_type->extern_type; +} + +/* Runtime Objects */ +void +wasm_val_delete(wasm_val_t *v) +{ + if (v) + wasm_runtime_free(v); +} + +void +wasm_val_copy(wasm_val_t *out, const wasm_val_t *src) +{ + if (!out || !src) { + return; + } + + bh_memcpy_s(out, sizeof(wasm_val_t), src, sizeof(wasm_val_t)); +} + +bool +rt_val_to_wasm_val(const uint8 *data, uint8 val_type_rt, wasm_val_t *out) +{ + bool ret = true; + switch (val_type_rt) { + case VALUE_TYPE_I32: + out->kind = WASM_I32; + out->of.i32 = *((int32 *)data); + break; + case VALUE_TYPE_F32: + out->kind = WASM_F32; + out->of.f32 = *((float32 *)data); + break; + case VALUE_TYPE_I64: + out->kind = WASM_I64; + out->of.i64 = *((int64 *)data); + break; + case VALUE_TYPE_F64: + out->kind = WASM_F64; + out->of.f64 = *((float64 *)data); + break; + case VALUE_TYPE_V128: + bh_assert(0); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + out->kind = WASM_EXTERNREF; + if (NULL_REF == *(uint32 *)data) { + out->of.ref = NULL; + } + else { + ret = wasm_externref_ref2obj(*(uint32 *)data, + (void **)&out->of.ref); + } + break; +#endif + default: + LOG_WARNING("unexpected value type %d", val_type_rt); + ret = false; + } + return ret; +} + +bool +wasm_val_to_rt_val(WASMModuleInstanceCommon *inst_comm_rt, uint8 val_type_rt, + const wasm_val_t *v, uint8 *data) +{ + bool ret = true; + switch (val_type_rt) { + case VALUE_TYPE_I32: + bh_assert(WASM_I32 == v->kind); + *((int32 *)data) = v->of.i32; + break; + case VALUE_TYPE_F32: + bh_assert(WASM_F32 == v->kind); + *((float32 *)data) = v->of.f32; + break; + case VALUE_TYPE_I64: + bh_assert(WASM_I64 == v->kind); + *((int64 *)data) = v->of.i64; + break; + case VALUE_TYPE_F64: + bh_assert(WASM_F64 == v->kind); + *((float64 *)data) = v->of.f64; + break; + case VALUE_TYPE_V128: + bh_assert(0); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + bh_assert(WASM_EXTERNREF == v->kind); + ret = + wasm_externref_obj2ref(inst_comm_rt, v->of.ref, (uint32 *)data); + break; +#endif + default: + LOG_WARNING("unexpected value type %d", val_type_rt); + ret = false; + break; + } + + (void)inst_comm_rt; + return ret; +} + +wasm_ref_t * +wasm_ref_new_internal(wasm_store_t *store, enum wasm_reference_kind kind, + uint32 ref_idx_rt, WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_ref_t *ref; + + if (!store) { + return NULL; + } + + if (!(ref = malloc_internal(sizeof(wasm_ref_t)))) { + return NULL; + } + + ref->store = store; + ref->kind = kind; + ref->ref_idx_rt = ref_idx_rt; + ref->inst_comm_rt = inst_comm_rt; + + /* workaround */ + if (WASM_REF_foreign == kind) { + wasm_foreign_t *foreign; + + if (!(bh_vector_get(ref->store->foreigns, ref->ref_idx_rt, &foreign)) + || !foreign) { + wasm_runtime_free(ref); + return NULL; + } + + foreign->ref_cnt++; + } + /* others doesn't include ref counters */ + + return ref; +} + +own wasm_ref_t * +wasm_ref_copy(const wasm_ref_t *src) +{ + if (!src) + return NULL; + + /* host_info are different in wasm_ref_t(s) */ + return wasm_ref_new_internal(src->store, src->kind, src->ref_idx_rt, + src->inst_comm_rt); +} + +#define DELETE_HOST_INFO(obj) \ + if (obj->host_info.info) { \ + if (obj->host_info.finalizer) { \ + obj->host_info.finalizer(obj->host_info.info); \ + } \ + } + +void +wasm_ref_delete(own wasm_ref_t *ref) +{ + if (!ref || !ref->store) + return; + + DELETE_HOST_INFO(ref); + + if (WASM_REF_foreign == ref->kind) { + wasm_foreign_t *foreign = NULL; + + if (bh_vector_get(ref->store->foreigns, ref->ref_idx_rt, &foreign) + && foreign) { + wasm_foreign_delete(foreign); + } + } + + wasm_runtime_free(ref); +} + +#define WASM_DEFINE_REF_BASE(name) \ + bool wasm_##name##_same(const wasm_##name##_t *o1, \ + const wasm_##name##_t *o2) \ + { \ + return (!o1 && !o2) ? true \ + : (!o1 || !o2) ? false \ + : (o1->kind != o2->kind) \ + ? false \ + : o1->name##_idx_rt == o2->name##_idx_rt; \ + } \ + \ + void *wasm_##name##_get_host_info(const wasm_##name##_t *obj) \ + { \ + return obj ? obj->host_info.info : NULL; \ + } \ + \ + void wasm_##name##_set_host_info(wasm_##name##_t *obj, void *host_info) \ + { \ + if (obj) { \ + obj->host_info.info = host_info; \ + obj->host_info.finalizer = NULL; \ + } \ + } \ + \ + void wasm_##name##_set_host_info_with_finalizer( \ + wasm_##name##_t *obj, void *host_info, void (*finalizer)(void *)) \ + { \ + if (obj) { \ + obj->host_info.info = host_info; \ + obj->host_info.finalizer = finalizer; \ + } \ + } + +#define WASM_DEFINE_REF(name) \ + WASM_DEFINE_REF_BASE(name) \ + \ + wasm_ref_t *wasm_##name##_as_ref(wasm_##name##_t *name) \ + { \ + if (!name) { \ + return NULL; \ + } \ + \ + return wasm_ref_new_internal(name->store, WASM_REF_##name, \ + name->name##_idx_rt, name->inst_comm_rt); \ + } \ + \ + const wasm_ref_t *wasm_##name##_as_ref_const(const wasm_##name##_t *name) \ + { \ + if (!name) { \ + return NULL; \ + } \ + \ + return wasm_ref_new_internal(name->store, WASM_REF_##name, \ + name->name##_idx_rt, name->inst_comm_rt); \ + } \ + \ + wasm_##name##_t *wasm_ref_as_##name(wasm_ref_t *ref) \ + { \ + if (!ref || WASM_REF_##name != ref->kind) { \ + return NULL; \ + } \ + \ + return wasm_##name##_new_internal(ref->store, ref->ref_idx_rt, \ + ref->inst_comm_rt); \ + } \ + \ + const wasm_##name##_t *wasm_ref_as_##name##_const(const wasm_ref_t *ref) \ + { \ + if (!ref || WASM_REF_##name != ref->kind) { \ + return NULL; \ + } \ + \ + return wasm_##name##_new_internal(ref->store, ref->ref_idx_rt, \ + ref->inst_comm_rt); \ + } + +WASM_DEFINE_REF_BASE(ref) +WASM_DEFINE_REF(foreign) +WASM_DEFINE_REF(func) +WASM_DEFINE_REF(global) +WASM_DEFINE_REF(memory) +WASM_DEFINE_REF(table) + +static wasm_frame_t * +wasm_frame_new(wasm_instance_t *instance, size_t module_offset, + uint32 func_index, size_t func_offset) +{ + wasm_frame_t *frame; + + if (!(frame = malloc_internal(sizeof(wasm_frame_t)))) { + return NULL; + } + + frame->instance = instance; + frame->module_offset = (uint32)module_offset; + frame->func_index = func_index; + frame->func_offset = (uint32)func_offset; + return frame; +} + +own wasm_frame_t * +wasm_frame_copy(const wasm_frame_t *src) +{ + if (!src) { + return NULL; + } + + return wasm_frame_new(src->instance, src->module_offset, src->func_index, + src->func_offset); +} + +void +wasm_frame_delete(own wasm_frame_t *frame) +{ + if (frame) { + wasm_runtime_free(frame); + } +} + +struct wasm_instance_t * +wasm_frame_instance(const wasm_frame_t *frame) +{ + return frame ? frame->instance : NULL; +} + +size_t +wasm_frame_module_offset(const wasm_frame_t *frame) +{ + return frame ? frame->module_offset : 0; +} + +uint32_t +wasm_frame_func_index(const wasm_frame_t *frame) +{ + return frame ? frame->func_index : 0; +} + +size_t +wasm_frame_func_offset(const wasm_frame_t *frame) +{ + return frame ? frame->func_offset : 0; +} + +void +wasm_frame_vec_clone_internal(Vector *src, Vector *out) +{ + if (src->num_elems == 0) { + bh_vector_destroy(out); + return; + } + + if (!bh_vector_destroy(out) + || !bh_vector_init(out, src->num_elems, sizeof(WASMCApiFrame), false)) { + return; + } + + bh_memcpy_s(out->data, (uint32)(src->num_elems * sizeof(WASMCApiFrame)), + src->data, (uint32)(src->num_elems * sizeof(WASMCApiFrame))); + out->num_elems = src->num_elems; +} + +static wasm_trap_t * +wasm_trap_new_internal(wasm_store_t *store, + WASMModuleInstanceCommon *inst_comm_rt, + const char *error_info, Vector *cluster_frames) +{ + wasm_trap_t *trap; +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + wasm_instance_vec_t *instances; + wasm_instance_t *frame_instance = NULL; + uint32 i; +#endif + + if (!singleton_engine) + return NULL; + + if (!(trap = malloc_internal(sizeof(wasm_trap_t)))) { + return NULL; + } + + /* fill in message */ + if (error_info && strlen(error_info) > 0) { + if (!(trap->message = malloc_internal(sizeof(wasm_byte_vec_t)))) { + goto failed; + } + + wasm_name_new_from_string_nt(trap->message, error_info); + if (!trap->message->data) { + goto failed; + } + } + + /* fill in frames */ +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + trap->frames = cluster_frames + ? cluster_frames + : ((WASMModuleInstance *)inst_comm_rt)->frames; + + if (trap->frames) { + /* fill in instances */ + instances = store->instances; + bh_assert(instances != NULL); + + for (i = 0; i < instances->num_elems; i++) { + if (instances->data[i]->inst_comm_rt == inst_comm_rt) { + frame_instance = instances->data[i]; + break; + } + } + + for (i = 0; i < trap->frames->num_elems; i++) { + (((wasm_frame_t *)trap->frames->data) + i)->instance = + frame_instance; + } + } +#else + (void)store; + (void)inst_comm_rt; +#endif /* WASM_ENABLE_DUMP_CALL_STACK != 0 */ + + return trap; +failed: + wasm_trap_delete(trap); + return NULL; +} + +wasm_trap_t * +wasm_trap_new(wasm_store_t *store, const wasm_message_t *message) +{ + wasm_trap_t *trap; + + if (!store) { + return NULL; + } + + if (!(trap = malloc_internal(sizeof(wasm_trap_t)))) { + return NULL; + } + + if (message) { + INIT_VEC(trap->message, wasm_byte_vec_new, message->size, + message->data); + } + + return trap; +failed: + wasm_trap_delete(trap); + return NULL; +} + +void +wasm_trap_delete(wasm_trap_t *trap) +{ + if (!trap) { + return; + } + + DEINIT_VEC(trap->message, wasm_byte_vec_delete); + /* reuse frames of WASMModuleInstance, do not free it here */ + + wasm_runtime_free(trap); +} + +void +wasm_trap_message(const wasm_trap_t *trap, own wasm_message_t *out) +{ + if (!trap || !out) { + return; + } + + wasm_byte_vec_copy(out, trap->message); +} + +own wasm_frame_t * +wasm_trap_origin(const wasm_trap_t *trap) +{ + wasm_frame_t *latest_frame; + + if (!trap || !trap->frames || !trap->frames->num_elems) { + return NULL; + } + + /* first frame is the latest frame */ + latest_frame = (wasm_frame_t *)trap->frames->data; + return wasm_frame_copy(latest_frame); +} + +void +wasm_trap_trace(const wasm_trap_t *trap, own wasm_frame_vec_t *out) +{ + uint32 i; + + if (!trap || !out) { + return; + } + + if (!trap->frames || !trap->frames->num_elems) { + wasm_frame_vec_new_empty(out); + return; + } + + wasm_frame_vec_new_uninitialized(out, trap->frames->num_elems); + if (out->size == 0 || !out->data) { + return; + } + + for (i = 0; i < trap->frames->num_elems; i++) { + wasm_frame_t *frame = ((wasm_frame_t *)trap->frames->data) + i; + if (!(out->data[i] = + wasm_frame_new(frame->instance, frame->module_offset, + frame->func_index, frame->func_offset))) { + goto failed; + } + out->num_elems++; + } + + return; +failed: + for (i = 0; i < out->num_elems; i++) { + if (out->data[i]) { + wasm_runtime_free(out->data[i]); + } + } + + wasm_runtime_free(out->data); +} + +wasm_foreign_t * +wasm_foreign_new_internal(wasm_store_t *store, uint32 foreign_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_foreign_t *foreign = NULL; + + if (!store || !store->foreigns) + return NULL; + + if (!(bh_vector_get(store->foreigns, foreign_idx_rt, &foreign)) + || !foreign) { + return NULL; + } + + foreign->ref_cnt++; + (void)inst_comm_rt; + return foreign; +} + +own wasm_foreign_t * +wasm_foreign_new(wasm_store_t *store) +{ + wasm_foreign_t *foreign; + + if (!store) + return NULL; + + if (!(foreign = malloc_internal(sizeof(wasm_foreign_t)))) + return NULL; + + foreign->store = store; + foreign->kind = WASM_REF_foreign; + foreign->foreign_idx_rt = (uint32)bh_vector_size(store->foreigns); + if (!(bh_vector_append(store->foreigns, &foreign))) { + wasm_runtime_free(foreign); + return NULL; + } + + return foreign; +} + +void +wasm_foreign_delete(wasm_foreign_t *foreign) +{ + if (!foreign) + return; + + if (foreign->ref_cnt < 1) { + return; + } + + foreign->ref_cnt--; + if (!foreign->ref_cnt) { + wasm_runtime_free(foreign); + } +} + +static inline wasm_module_t * +module_ext_to_module(wasm_module_ex_t *module_ex) +{ + return (wasm_module_t *)module_ex; +} + +static inline wasm_module_ex_t * +module_to_module_ext(wasm_module_t *module) +{ + return (wasm_module_ex_t *)module; +} + +#if WASM_ENABLE_INTERP != 0 +#define MODULE_INTERP(module_comm) ((WASMModule *)(*module_comm)) +#endif + +#if WASM_ENABLE_AOT != 0 +#define MODULE_AOT(module_comm) ((AOTModule *)(*module_comm)) +#endif + +#if WASM_ENABLE_WASM_CACHE != 0 +static wasm_module_ex_t * +check_loaded_module(Vector *modules, char *binary_hash) +{ + unsigned i; + wasm_module_ex_t *module = NULL; + + for (i = 0; i < modules->num_elems; i++) { + bh_vector_get(modules, i, &module); + if (!module) { + LOG_ERROR("Unexpected failure at %d\n", __LINE__); + return NULL; + } + + os_mutex_lock(&module->lock); + bool is_valid = + (module->ref_count > 0 + && memcmp(module->hash, binary_hash, SHA256_DIGEST_LENGTH) == 0); + os_mutex_unlock(&module->lock); + + if (is_valid) { + return module; + } + } + return NULL; +} + +static wasm_module_ex_t * +try_reuse_loaded_module(wasm_store_t *store, char *binary_hash) +{ + wasm_module_ex_t *cached = NULL; + wasm_module_ex_t *ret = NULL; + + cached = check_loaded_module(&singleton_engine->modules, binary_hash); + if (!cached) + goto quit; + + os_mutex_lock(&cached->lock); + if (!cached->ref_count) + goto unlock; + + if (!bh_vector_append((Vector *)store->modules, &cached)) + goto unlock; + + cached->ref_count += 1; + ret = cached; + +unlock: + os_mutex_unlock(&cached->lock); +quit: + return ret; +} +#endif /* WASM_ENABLE_WASM_CACHE != 0 */ + +wasm_module_t * +wasm_module_new_ex(wasm_store_t *store, wasm_byte_vec_t *binary, LoadArgs *args) +{ + char error_buf[128] = { 0 }; + wasm_module_ex_t *module_ex = NULL; +#if WASM_ENABLE_WASM_CACHE != 0 + char binary_hash[SHA256_DIGEST_LENGTH] = { 0 }; +#endif + + bh_assert(singleton_engine); + + if (!store || !binary || binary->size == 0 || binary->size > UINT32_MAX) + goto quit; + + /* whether the combination of compilation flags are compatible with the + * package type */ + { + PackageType pkg_type; + pkg_type = + get_package_type((uint8 *)binary->data, (uint32)binary->size); + bool result = false; +#if WASM_ENABLE_INTERP != 0 + result = (pkg_type == Wasm_Module_Bytecode); +#endif + +#if WASM_ENABLE_AOT != 0 + result = result || (pkg_type == Wasm_Module_AoT); +#endif + if (!result) { + LOG_VERBOSE("current building isn't compatible with the module," + "may need recompile"); + goto quit; + } + } + +#if WASM_ENABLE_WASM_CACHE != 0 + /* if cached */ + SHA256((void *)binary->data, binary->num_elems, (uint8_t *)binary_hash); + module_ex = try_reuse_loaded_module(store, binary_hash); + if (module_ex) + return module_ext_to_module(module_ex); +#endif + + WASM_C_DUMP_PROC_MEM(); + + module_ex = malloc_internal(sizeof(wasm_module_ex_t)); + if (!module_ex) + goto quit; + + module_ex->is_binary_cloned = args->clone_wasm_binary; + if (args->clone_wasm_binary) { + module_ex->binary = malloc_internal(sizeof(wasm_byte_vec_t)); + if (!module_ex->binary) + goto free_module; + + wasm_byte_vec_copy(module_ex->binary, binary); + if (!module_ex->binary->data) + goto free_binary; + } + else { + module_ex->binary = binary; + } + + args->wasm_binary_freeable = !args->clone_wasm_binary; + module_ex->module_comm_rt = wasm_runtime_load_ex( + (uint8 *)module_ex->binary->data, (uint32)module_ex->binary->size, args, + error_buf, (uint32)sizeof(error_buf)); + if (!(module_ex->module_comm_rt)) { + LOG_ERROR("%s", error_buf); + goto free_vec; + } + + /* append it to a watching list in store */ + if (!bh_vector_append((Vector *)store->modules, &module_ex)) + goto unload; + + if (os_mutex_init(&module_ex->lock) != BHT_OK) + goto remove_last; + + if (!bh_vector_append(&singleton_engine->modules, &module_ex)) + goto destroy_lock; + +#if WASM_ENABLE_WASM_CACHE != 0 + bh_memcpy_s(module_ex->hash, sizeof(module_ex->hash), binary_hash, + sizeof(binary_hash)); +#endif + + module_ex->ref_count = 1; + + WASM_C_DUMP_PROC_MEM(); + + return module_ext_to_module(module_ex); + +destroy_lock: + os_mutex_destroy(&module_ex->lock); +remove_last: + bh_vector_remove((Vector *)store->modules, + (uint32)(store->modules->num_elems - 1), NULL); +unload: + wasm_runtime_unload(module_ex->module_comm_rt); +free_vec: + if (args->clone_wasm_binary) + wasm_byte_vec_delete(module_ex->binary); +free_binary: + if (args->clone_wasm_binary) + wasm_runtime_free(module_ex->binary); +free_module: + wasm_runtime_free(module_ex); +quit: + LOG_ERROR("%s failed", __FUNCTION__); + return NULL; +} + +wasm_module_t * +wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) +{ + LoadArgs args = { 0 }; + args.name = ""; + args.clone_wasm_binary = true; + return wasm_module_new_ex(store, (wasm_byte_vec_t *)binary, &args); +} + +bool +wasm_module_validate(wasm_store_t *store, const wasm_byte_vec_t *binary) +{ + wasm_byte_vec_t local_binary = { 0 }; + struct WASMModuleCommon *module_rt; + char error_buf[128] = { 0 }; + bool ret; + + bh_assert(singleton_engine); + + if (!store || !binary || binary->size > UINT32_MAX) { + LOG_ERROR("%s failed", __FUNCTION__); + return false; + } + + /* make a copy of binary */ + wasm_byte_vec_copy(&local_binary, binary); + + if (binary->size && !local_binary.data) + return false; + + module_rt = wasm_runtime_load((uint8 *)local_binary.data, + (uint32)local_binary.size, error_buf, 128); + wasm_byte_vec_delete(&local_binary); + if (module_rt) { + wasm_runtime_unload(module_rt); + ret = true; + } + else { + ret = false; + LOG_VERBOSE("%s", error_buf); + } + + return ret; +} + +static void +wasm_module_delete_internal(wasm_module_t *module) +{ + wasm_module_ex_t *module_ex; + + if (!module) { + return; + } + + module_ex = module_to_module_ext(module); + + os_mutex_lock(&module_ex->lock); + + /* N -> N-1 -> 0 -> UINT32_MAX */ + module_ex->ref_count--; + if (module_ex->ref_count > 0) { + os_mutex_unlock(&module_ex->lock); + return; + } + + if (module_ex->is_binary_cloned) + DEINIT_VEC(module_ex->binary, wasm_byte_vec_delete); + + if (module_ex->module_comm_rt) { + wasm_runtime_unload(module_ex->module_comm_rt); + module_ex->module_comm_rt = NULL; + } + +#if WASM_ENABLE_WASM_CACHE != 0 + memset(module_ex->hash, 0, sizeof(module_ex->hash)); +#endif + + os_mutex_unlock(&module_ex->lock); +} + +void +wasm_module_delete(wasm_module_t *module) +{ + /* the module will be released when releasing the store */ + (void)module; +} + +void +wasm_module_imports(const wasm_module_t *module, own wasm_importtype_vec_t *out) +{ + uint32 i, import_func_count = 0, import_memory_count = 0, + import_global_count = 0, import_table_count = 0, import_count = 0; + wasm_byte_vec_t module_name = { 0 }, name = { 0 }; + wasm_externtype_t *extern_type = NULL; + wasm_importtype_t *import_type = NULL; + + if (!module || !out) { + return; + } + + wasm_module_ex_t *module_ex = module_to_module_ext((wasm_module_t *)module); + os_mutex_lock(&module_ex->lock); + if (module_ex->ref_count == 0) { + os_mutex_unlock(&module_ex->lock); + return; + } + os_mutex_unlock(&module_ex->lock); + +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + 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) { + 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 + + import_count = import_func_count + import_global_count + import_table_count + + import_memory_count; + + wasm_importtype_vec_new_uninitialized(out, import_count); + /* + * a wrong combination of module filetype and compilation flags + * also leads to below branch + */ + if (!out->data) { + return; + } + + for (i = 0; i != import_count; ++i) { + char *module_name_rt = NULL, *field_name_rt = NULL; + + memset(&module_name, 0, sizeof(wasm_val_vec_t)); + memset(&name, 0, sizeof(wasm_val_vec_t)); + extern_type = NULL; + import_type = NULL; + + if (i < import_func_count) { + wasm_functype_t *type = NULL; + WASMFuncType *type_rt = NULL; + +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + WASMImport *import = + MODULE_INTERP(module)->import_functions + i; + module_name_rt = import->u.names.module_name; + field_name_rt = import->u.names.field_name; + type_rt = import->u.function.func_type; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + AOTImportFunc *import = MODULE_AOT(module)->import_funcs + i; + module_name_rt = import->module_name; + field_name_rt = import->func_name; + type_rt = import->func_type; + } +#endif + + if (!module_name_rt || !field_name_rt || !type_rt) { + continue; + } + + if (!(type = wasm_functype_new_internal(type_rt))) { + goto failed; + } + + extern_type = wasm_functype_as_externtype(type); + } + else if (i < import_func_count + import_global_count) { + wasm_globaltype_t *type = NULL; + uint8 val_type_rt = 0; + bool mutability_rt = 0; + +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + WASMImport *import = MODULE_INTERP(module)->import_globals + + (i - import_func_count); + module_name_rt = import->u.names.module_name; + field_name_rt = import->u.names.field_name; + val_type_rt = import->u.global.type.val_type; + mutability_rt = import->u.global.type.is_mutable; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + AOTImportGlobal *import = MODULE_AOT(module)->import_globals + + (i - import_func_count); + module_name_rt = import->module_name; + field_name_rt = import->global_name; + val_type_rt = import->type.val_type; + mutability_rt = import->type.is_mutable; + } +#endif + + if (!module_name_rt || !field_name_rt) { + continue; + } + + if (!(type = wasm_globaltype_new_internal(val_type_rt, + mutability_rt))) { + goto failed; + } + + extern_type = wasm_globaltype_as_externtype(type); + } + else if (i < import_func_count + import_global_count + + import_memory_count) { + wasm_memorytype_t *type = NULL; + uint32 min_page = 0, max_page = 0; + +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + WASMImport *import = + MODULE_INTERP(module)->import_memories + + (i - import_func_count - import_global_count); + module_name_rt = import->u.names.module_name; + field_name_rt = import->u.names.field_name; + min_page = import->u.memory.mem_type.init_page_count; + max_page = import->u.memory.mem_type.max_page_count; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + AOTImportMemory *import = + MODULE_AOT(module)->import_memories + + (i - import_func_count - import_global_count); + module_name_rt = import->module_name; + field_name_rt = import->memory_name; + min_page = import->mem_type.init_page_count; + max_page = import->mem_type.max_page_count; + } +#endif + + if (!module_name_rt || !field_name_rt) { + continue; + } + + if (!(type = wasm_memorytype_new_internal(min_page, max_page))) { + goto failed; + } + + extern_type = wasm_memorytype_as_externtype(type); + } + else { + wasm_tabletype_t *type = NULL; + uint8 elem_type_rt = 0; + uint32 min_size = 0, max_size = 0; + +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + WASMImport *import = + MODULE_INTERP(module)->import_tables + + (i - import_func_count - import_global_count + - import_memory_count); + module_name_rt = import->u.names.module_name; + field_name_rt = import->u.names.field_name; + elem_type_rt = import->u.table.table_type.elem_type; + min_size = import->u.table.table_type.init_size; + max_size = import->u.table.table_type.max_size; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + AOTImportTable *import = + MODULE_AOT(module)->import_tables + + (i - import_func_count - import_global_count + - import_memory_count); + module_name_rt = import->module_name; + field_name_rt = import->table_name; + elem_type_rt = import->table_type.elem_type; + min_size = import->table_type.init_size; + max_size = import->table_type.max_size; + } +#endif + + if (!module_name_rt || !field_name_rt) { + continue; + } + + if (!(type = wasm_tabletype_new_internal(elem_type_rt, min_size, + max_size))) { + goto failed; + } + + extern_type = wasm_tabletype_as_externtype(type); + } + + bh_assert(extern_type); + + 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_nt(&name, field_name_rt); + if (strlen(field_name_rt) && !name.data) { + goto failed; + } + + if (!(import_type = + wasm_importtype_new(&module_name, &name, extern_type))) { + goto failed; + } + + if (!bh_vector_append((Vector *)out, &import_type)) { + goto failed_importtype_new; + } + + continue; + + failed: + wasm_byte_vec_delete(&module_name); + wasm_byte_vec_delete(&name); + wasm_externtype_delete(extern_type); + failed_importtype_new: + wasm_importtype_delete(import_type); + } +} + +void +wasm_module_exports(const wasm_module_t *module, wasm_exporttype_vec_t *out) +{ + uint32 i, export_count = 0; + wasm_byte_vec_t name = { 0 }; + wasm_externtype_t *extern_type = NULL; + wasm_exporttype_t *export_type = NULL; + + if (!module || !out) { + return; + } + + wasm_module_ex_t *module_ex = module_to_module_ext((wasm_module_t *)module); + os_mutex_lock(&module_ex->lock); + if (module_ex->ref_count == 0) { + os_mutex_unlock(&module_ex->lock); + return; + } + os_mutex_unlock(&module_ex->lock); + +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + export_count = MODULE_INTERP(module)->export_count; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + export_count = MODULE_AOT(module)->export_count; + } +#endif + + wasm_exporttype_vec_new_uninitialized(out, export_count); + /* + * a wrong combination of module filetype and compilation flags + * also leads to below branch + */ + if (!out->data) { + return; + } + + for (i = 0; i != export_count; i++) { + WASMExport *export = NULL; +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + export = MODULE_INTERP(module)->exports + i; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + export = MODULE_AOT(module)->exports + i; + } +#endif + + if (!export) { + continue; + } + + /* byte* -> wasm_byte_vec_t */ + wasm_name_new_from_string_nt(&name, export->name); + if (strlen(export->name) && !name.data) { + goto failed; + } + + /* WASMExport -> (WASMFuncType, (uint8, bool)) -> (wasm_functype_t, + * wasm_globaltype_t) -> wasm_externtype_t*/ + switch (export->kind) { + case EXPORT_KIND_FUNC: + { + wasm_functype_t *type = NULL; + WASMFuncType *type_rt; + + if (!wasm_runtime_get_export_func_type(*module, export, + &type_rt)) { + goto failed; + } + + if (!(type = wasm_functype_new_internal(type_rt))) { + goto failed; + } + + extern_type = wasm_functype_as_externtype(type); + break; + } + case EXPORT_KIND_GLOBAL: + { + wasm_globaltype_t *type = NULL; + uint8 val_type_rt = 0; + bool mutability_rt = 0; + + if (!wasm_runtime_get_export_global_type( + *module, export, &val_type_rt, &mutability_rt)) { + goto failed; + } + + if (!(type = wasm_globaltype_new_internal(val_type_rt, + mutability_rt))) { + goto failed; + } + + extern_type = wasm_globaltype_as_externtype(type); + break; + } + case EXPORT_KIND_MEMORY: + { + wasm_memorytype_t *type = NULL; + uint32 min_page = 0, max_page = 0; + + if (!wasm_runtime_get_export_memory_type( + *module, export, &min_page, &max_page)) { + goto failed; + } + + if (!(type = + wasm_memorytype_new_internal(min_page, max_page))) { + goto failed; + } + + extern_type = wasm_memorytype_as_externtype(type); + break; + } + case EXPORT_KIND_TABLE: + { + wasm_tabletype_t *type = NULL; + uint8 elem_type_rt = 0; +#if WASM_ENABLE_GC != 0 + WASMRefType *elem_ref_type_rt; +#endif + uint32 min_size = 0, max_size = 0; + + if (!wasm_runtime_get_export_table_type(*module, export, + &elem_type_rt, +#if WASM_ENABLE_GC != 0 + &elem_ref_type_rt, +#endif + &min_size, &max_size)) { + goto failed; + } +#if WASM_ENABLE_GC != 0 + (void)elem_ref_type_rt; /* TODO */ +#endif + + if (!(type = wasm_tabletype_new_internal(elem_type_rt, min_size, + max_size))) { + goto failed; + } + + extern_type = wasm_tabletype_as_externtype(type); + break; + } + default: + { + LOG_WARNING("%s meets unsupported type %u", __FUNCTION__, + export->kind); + break; + } + } + + if (!(export_type = wasm_exporttype_new(&name, extern_type))) { + goto failed; + } + + if (!(bh_vector_append((Vector *)out, &export_type))) { + goto failed_exporttype_new; + } + } + + return; + +failed: + wasm_byte_vec_delete(&name); + wasm_externtype_delete(extern_type); +failed_exporttype_new: + wasm_exporttype_delete(export_type); + wasm_exporttype_vec_delete(out); +} + +#if WASM_ENABLE_JIT == 0 || WASM_ENABLE_LAZY_JIT != 0 +void +wasm_module_serialize(wasm_module_t *module, own wasm_byte_vec_t *out) +{ + (void)module; + (void)out; + LOG_ERROR("only supported serialization in JIT with eager compilation"); +} + +own wasm_module_t * +wasm_module_deserialize(wasm_store_t *module, const wasm_byte_vec_t *binary) +{ + (void)module; + (void)binary; + LOG_ERROR("only supported deserialization in JIT with eager compilation"); + return NULL; +} +#else + +extern uint8 * +aot_emit_aot_file_buf(AOTCompContext *comp_ctx, AOTCompData *comp_data, + uint32 *p_aot_file_size); +void +wasm_module_serialize(wasm_module_t *module, own wasm_byte_vec_t *out) +{ + wasm_module_ex_t *module_ex; + AOTCompContext *comp_ctx; + AOTCompData *comp_data; + uint8 *aot_file_buf = NULL; + uint32 aot_file_size = 0; + + if (!module || !out) + return; + + module_ex = module_to_module_ext(module); + os_mutex_lock(&module_ex->lock); + if (module_ex->ref_count == 0) { + os_mutex_unlock(&module_ex->lock); + return; + } + os_mutex_unlock(&module_ex->lock); + comp_ctx = ((WASMModule *)(module_ex->module_comm_rt))->comp_ctx; + comp_data = ((WASMModule *)(module_ex->module_comm_rt))->comp_data; + bh_assert(comp_ctx != NULL && comp_data != NULL); + + aot_file_buf = aot_emit_aot_file_buf(comp_ctx, comp_data, &aot_file_size); + if (!aot_file_buf) + return; + + wasm_byte_vec_new(out, aot_file_size, (wasm_byte_t *)aot_file_buf); + wasm_runtime_free(aot_file_buf); + return; +} + +own wasm_module_t * +wasm_module_deserialize(wasm_store_t *store, const wasm_byte_vec_t *binary) +{ + return wasm_module_new(store, binary); +} +#endif + +wasm_module_t * +wasm_module_obtain(wasm_store_t *store, wasm_shared_module_t *shared_module) +{ + wasm_module_ex_t *module_ex = NULL; + + if (!store || !shared_module) + return NULL; + + module_ex = (wasm_module_ex_t *)shared_module; + + os_mutex_lock(&module_ex->lock); + + /* deleting the module... */ + if (module_ex->ref_count == 0) { + LOG_WARNING("wasm_module_obtain re-enter a module under deleting."); + os_mutex_unlock(&module_ex->lock); + return NULL; + } + + /* add it to a watching list in store */ + if (!bh_vector_append((Vector *)store->modules, &module_ex)) { + os_mutex_unlock(&module_ex->lock); + return NULL; + } + + module_ex->ref_count++; + os_mutex_unlock(&module_ex->lock); + + return (wasm_module_t *)shared_module; +} + +wasm_shared_module_t * +wasm_module_share(wasm_module_t *module) +{ + wasm_module_ex_t *module_ex = NULL; + + if (!module) + return NULL; + + module_ex = (wasm_module_ex_t *)module; + + os_mutex_lock(&module_ex->lock); + + /* deleting the module... */ + if (module_ex->ref_count == 0) { + LOG_WARNING("wasm_module_share re-enter a module under deleting."); + os_mutex_unlock(&module_ex->lock); + return NULL; + } + + module_ex->ref_count++; + + os_mutex_unlock(&module_ex->lock); + + return (wasm_shared_module_t *)module; +} + +void +wasm_shared_module_delete(own wasm_shared_module_t *shared_module) +{ + wasm_module_delete_internal((wasm_module_t *)shared_module); +} + +bool +wasm_module_set_name(wasm_module_t *module, const char *name) +{ + char error_buf[256] = { 0 }; + wasm_module_ex_t *module_ex = NULL; + + if (!module) + return false; + + module_ex = module_to_module_ext(module); + bool ret = wasm_runtime_set_module_name(module_ex->module_comm_rt, name, + error_buf, sizeof(error_buf) - 1); + if (!ret) + LOG_WARNING("set module name failed: %s", error_buf); + return ret; +} + +const char * +wasm_module_get_name(wasm_module_t *module) +{ + wasm_module_ex_t *module_ex = NULL; + if (!module) + return ""; + + module_ex = module_to_module_ext(module); + return wasm_runtime_get_module_name(module_ex->module_comm_rt); +} + +bool +wasm_module_is_underlying_binary_freeable(const wasm_module_t *module) +{ + if (((wasm_module_ex_t *)module)->is_binary_cloned) + return true; + + return wasm_runtime_is_underlying_binary_freeable(*module); +} + +static wasm_func_t * +wasm_func_new_basic(wasm_store_t *store, const wasm_functype_t *type, + wasm_func_callback_t func_callback) +{ + wasm_func_t *func = NULL; + + if (!type) { + goto failed; + } + + if (!(func = malloc_internal(sizeof(wasm_func_t)))) { + goto failed; + } + + func->store = store; + func->kind = WASM_EXTERN_FUNC; + func->func_idx_rt = (uint16)-1; + func->with_env = false; + func->u.cb = func_callback; + + if (!(func->type = wasm_functype_copy(type))) { + goto failed; + } + /* func type's param_count and result_count were checked in + loader and are no larger than UINT16_MAX */ + func->param_count = (uint16)func->type->params->num_elems; + func->result_count = (uint16)func->type->results->num_elems; + + RETURN_OBJ(func, wasm_func_delete) +} + +static wasm_func_t * +wasm_func_new_with_env_basic(wasm_store_t *store, const wasm_functype_t *type, + wasm_func_callback_with_env_t callback, void *env, + void (*finalizer)(void *)) +{ + wasm_func_t *func = NULL; + + if (!type) { + goto failed; + } + + if (!(func = malloc_internal(sizeof(wasm_func_t)))) { + goto failed; + } + + func->store = store; + func->kind = WASM_EXTERN_FUNC; + func->func_idx_rt = (uint16)-1; + func->with_env = true; + func->u.cb_env.cb = callback; + func->u.cb_env.env = env; + func->u.cb_env.finalizer = finalizer; + + if (!(func->type = wasm_functype_copy(type))) { + goto failed; + } + /* func type's param_count and result_count were checked in + loader and are no larger than UINT16_MAX */ + func->param_count = (uint16)func->type->params->num_elems; + func->result_count = (uint16)func->type->results->num_elems; + + RETURN_OBJ(func, wasm_func_delete) +} + +wasm_func_t * +wasm_func_new(wasm_store_t *store, const wasm_functype_t *type, + wasm_func_callback_t callback) +{ + bh_assert(singleton_engine); + if (!callback) { + return NULL; + } + return wasm_func_new_basic(store, type, callback); +} + +wasm_func_t * +wasm_func_new_with_env(wasm_store_t *store, const wasm_functype_t *type, + wasm_func_callback_with_env_t callback, void *env, + void (*finalizer)(void *)) +{ + bh_assert(singleton_engine); + if (!callback) { + return NULL; + } + return wasm_func_new_with_env_basic(store, type, callback, env, finalizer); +} + +wasm_func_t * +wasm_func_new_internal(wasm_store_t *store, uint16 func_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_func_t *func = NULL; + WASMFuncType *type_rt = NULL; + + bh_assert(singleton_engine); + + if (!inst_comm_rt) { + return NULL; + } + + func = malloc_internal(sizeof(wasm_func_t)); + if (!func) { + goto failed; + } + + func->kind = WASM_EXTERN_FUNC; + +#if WASM_ENABLE_INTERP != 0 + if (inst_comm_rt->module_type == Wasm_Module_Bytecode) { + bh_assert(func_idx_rt + < ((WASMModuleInstance *)inst_comm_rt)->e->function_count); + WASMFunctionInstance *func_interp = + ((WASMModuleInstance *)inst_comm_rt)->e->functions + func_idx_rt; + type_rt = func_interp->is_import_func + ? func_interp->u.func_import->func_type + : func_interp->u.func->func_type; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (inst_comm_rt->module_type == Wasm_Module_AoT) { + /* use same index to trace the function type in AOTFuncType **func_types + */ + AOTModule *module_aot = + (AOTModule *)((AOTModuleInstance *)inst_comm_rt)->module; + if (func_idx_rt < module_aot->import_func_count) { + type_rt = (module_aot->import_funcs + func_idx_rt)->func_type; + } + else { + type_rt = + (AOTFuncType *)module_aot + ->types[module_aot->func_type_indexes + [func_idx_rt - module_aot->import_func_count]]; + } + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * also leads to below branch + */ + if (!type_rt) { + goto failed; + } + + func->type = wasm_functype_new_internal(type_rt); + if (!func->type) { + goto failed; + } + /* func type's param_count and result_count were checked in + loader and are no larger than UINT16_MAX */ + func->param_count = (uint16)func->type->params->num_elems; + func->result_count = (uint16)func->type->results->num_elems; + + /* will add name information when processing "exports" */ + func->store = store; + func->module_name = NULL; + func->name = NULL; + func->func_idx_rt = func_idx_rt; + func->inst_comm_rt = inst_comm_rt; + return func; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_func_delete(func); + 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) +{ + if (!func) { + return; + } + + if (func->type) { + wasm_functype_delete(func->type); + func->type = NULL; + } + + if (func->with_env) { + if (func->u.cb_env.finalizer) { + func->u.cb_env.finalizer(func->u.cb_env.env); + func->u.cb_env.finalizer = NULL; + func->u.cb_env.env = NULL; + } + } + + DELETE_HOST_INFO(func) + + wasm_runtime_free(func); +} + +own wasm_func_t * +wasm_func_copy(const wasm_func_t *func) +{ + wasm_func_t *cloned = NULL; + + if (!func) { + return NULL; + } + + if (!(cloned = func->with_env ? wasm_func_new_with_env_basic( + func->store, func->type, func->u.cb_env.cb, + func->u.cb_env.env, func->u.cb_env.finalizer) + : wasm_func_new_basic(func->store, func->type, + func->u.cb))) { + goto failed; + } + + cloned->func_idx_rt = func->func_idx_rt; + cloned->inst_comm_rt = func->inst_comm_rt; + + RETURN_OBJ(cloned, wasm_func_delete) +} + +own wasm_functype_t * +wasm_func_type(const wasm_func_t *func) +{ + if (!func) { + return NULL; + } + return wasm_functype_copy(func->type); +} + +static bool +params_to_argv(const wasm_val_vec_t *params, + const wasm_valtype_vec_t *param_defs, uint32 *argv, + uint32 *ptr_argc) +{ + uint32 *argv_org = argv; + const wasm_val_t *param, *param_end; + + bh_assert(params && params->num_elems && params->size && params->data); + + param = params->data; + param_end = param + param_defs->num_elems; + + for (; param < param_end; param++) { + switch (param->kind) { + case WASM_I32: + case WASM_F32: + *(int32 *)argv = param->of.i32; + argv += 1; + break; + case WASM_I64: + case WASM_F64: + *(int64 *)argv = param->of.i64; + argv += 2; + break; + case WASM_V128: + bh_assert(0); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case WASM_EXTERNREF: + *(uintptr_t *)argv = (uintptr_t)param->of.ref; + argv += sizeof(uintptr_t) / sizeof(uint32); + break; +#endif + default: + LOG_WARNING("unexpected parameter val type %d", param->kind); + return false; + } + } + + *ptr_argc = (uint32)(argv - argv_org); + return true; +} + +static bool +argv_to_results(const uint32 *argv, const wasm_valtype_vec_t *result_defs, + wasm_val_vec_t *results) +{ + wasm_valtype_t **result_def, **result_def_end; + wasm_val_t *result; + + bh_assert(results && results->size && results->data); + + result_def = result_defs->data; + result_def_end = result_def + result_defs->num_elems; + result = results->data; + + for (; result_def < result_def_end; result_def++, result++) { + result->kind = result_def[0]->kind; + switch (result->kind) { + case WASM_I32: + case WASM_F32: + result->of.i32 = *(int32 *)argv; + argv += 1; + break; + case WASM_I64: + case WASM_F64: + result->of.i64 = *(int64 *)argv; + argv += 2; + break; + case WASM_V128: + bh_assert(0); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case WASM_EXTERNREF: + case WASM_FUNCREF: + result->of.ref = (struct wasm_ref_t *)(*(uintptr_t *)argv); + argv += sizeof(uintptr_t) / sizeof(uint32); + break; +#endif + default: + LOG_WARNING("%s meets unsupported type: %d", __FUNCTION__, + result->kind); + return false; + } + } + + return true; +} + +wasm_trap_t * +wasm_func_call(const wasm_func_t *func, const wasm_val_vec_t *params, + wasm_val_vec_t *results) +{ + /* parameters count as if all are uint32 */ + /* a int64 or float64 parameter means 2 */ + uint32 argc = 0; + /* a parameter list and a return value list */ + uint32 argv_buf[32] = { 0 }, *argv = argv_buf; + WASMFunctionInstanceCommon *func_comm_rt = NULL; + WASMExecEnv *exec_env = NULL; + size_t param_count, result_count, alloc_count; + Vector *cluster_frames = NULL; + + bh_assert(func && func->type); + + if (!func->inst_comm_rt) { + wasm_name_t message = { 0 }; + wasm_trap_t *trap; + + wasm_name_new_from_string_nt(&message, + "failed to call unlinked function"); + trap = wasm_trap_new(func->store, &message); + wasm_byte_vec_delete(&message); + + return trap; + } + + if (func->inst_comm_rt->module_type == Wasm_Module_Bytecode) { +#if WASM_ENABLE_INTERP != 0 + func_comm_rt = ((WASMModuleInstance *)func->inst_comm_rt)->e->functions + + func->func_idx_rt; +#endif + } + else if (func->inst_comm_rt->module_type == Wasm_Module_AoT) { +#if WASM_ENABLE_AOT != 0 + if (!(func_comm_rt = func->func_comm_rt)) { + AOTModuleInstance *inst_aot = + (AOTModuleInstance *)func->inst_comm_rt; + func_comm_rt = ((wasm_func_t *)func)->func_comm_rt = + aot_lookup_function_with_idx(inst_aot, func->func_idx_rt); + } +#endif + } + + /* + * a wrong combination of module filetype and compilation flags + * also leads to below branch + */ + if (!func_comm_rt) { + goto failed; + } + + param_count = wasm_func_param_arity(func); + result_count = wasm_func_result_arity(func); + + alloc_count = (param_count > result_count) ? param_count : result_count; + if (alloc_count > (size_t)sizeof(argv_buf) / sizeof(uint64)) { + if (!(argv = malloc_internal(sizeof(uint64) * alloc_count))) { + goto failed; + } + } + + /* copy parameters */ + if (param_count + && !params_to_argv(params, wasm_functype_params(func->type), argv, + &argc)) { + goto failed; + } + +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = wasm_runtime_get_exec_env_tls(); +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) { + exec_env = wasm_clusters_search_exec_env(func->inst_comm_rt); + } +#endif + if (!exec_env) { + exec_env = wasm_runtime_get_exec_env_singleton(func->inst_comm_rt); + } + if (!exec_env) { + goto failed; + } + + wasm_runtime_set_exception(func->inst_comm_rt, NULL); + if (!wasm_runtime_call_wasm(exec_env, func_comm_rt, argc, argv)) { + if (wasm_runtime_get_exception(func->inst_comm_rt)) { + LOG_DEBUG("%s", wasm_runtime_get_exception(func->inst_comm_rt)); + goto failed; + } + } + + /* copy results */ + if (result_count) { + if (!argv_to_results(argv, wasm_functype_results(func->type), + results)) { + wasm_runtime_set_exception(func->inst_comm_rt, + "argv_to_results failed"); + goto failed; + } + results->num_elems = result_count; + results->size = result_count; + } + + if (argv != argv_buf) + wasm_runtime_free(argv); + return NULL; + +failed: + if (argv != argv_buf) + wasm_runtime_free(argv); + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 && WASM_ENABLE_THREAD_MGR != 0 + WASMCluster *cluster = NULL; + if (exec_env) { + cluster = wasm_exec_env_get_cluster(exec_env); + } + if (cluster) { + cluster_frames = &cluster->exception_frames; + } + if (cluster_frames) { + wasm_cluster_traverse_lock(exec_env); + } +#endif + + wasm_trap_t *trap = wasm_trap_new_internal( + func->store, func->inst_comm_rt, + wasm_runtime_get_exception(func->inst_comm_rt), cluster_frames); + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 && WASM_ENABLE_THREAD_MGR != 0 + if (cluster_frames) { + wasm_cluster_traverse_unlock(exec_env); + } +#endif + return trap; +} + +size_t +wasm_func_param_arity(const wasm_func_t *func) +{ + return func->param_count; +} + +size_t +wasm_func_result_arity(const wasm_func_t *func) +{ + return func->result_count; +} + +wasm_global_t * +wasm_global_new(wasm_store_t *store, const wasm_globaltype_t *global_type, + const wasm_val_t *init) +{ + wasm_global_t *global = NULL; + + bh_assert(singleton_engine); + + if (!global_type || !init) { + goto failed; + } + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) { + goto failed; + } + + global->store = store; + global->kind = WASM_EXTERN_GLOBAL; + global->type = wasm_globaltype_copy(global_type); + if (!global->type) { + goto failed; + } + + global->init = malloc_internal(sizeof(wasm_val_t)); + if (!global->init) { + goto failed; + } + + wasm_val_copy(global->init, init); + /* TODO: how to check if above is failed */ + + return global; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + 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) +{ + wasm_global_t *global = NULL; + + if (!src) { + return NULL; + } + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) { + goto failed; + } + + global->kind = WASM_EXTERN_GLOBAL; + global->type = wasm_globaltype_copy(src->type); + if (!global->type) { + goto failed; + } + + global->init = malloc_internal(sizeof(wasm_val_t)); + if (!global->init) { + goto failed; + } + + wasm_val_copy(global->init, src->init); + + global->global_idx_rt = src->global_idx_rt; + global->inst_comm_rt = src->inst_comm_rt; + + return global; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + +void +wasm_global_delete(wasm_global_t *global) +{ + if (!global) { + return; + } + + if (global->init) { + wasm_val_delete(global->init); + global->init = NULL; + } + + if (global->type) { + wasm_globaltype_delete(global->type); + global->type = NULL; + } + + DELETE_HOST_INFO(global) + + wasm_runtime_free(global); +} + +#if WASM_ENABLE_INTERP != 0 +static bool +interp_global_set(const WASMModuleInstance *inst_interp, uint16 global_idx_rt, + const wasm_val_t *v) +{ + const WASMGlobalInstance *global_interp = + inst_interp->e->globals + global_idx_rt; + uint8 val_type_rt = global_interp->type; +#if WASM_ENABLE_MULTI_MODULE != 0 + uint8 *data = global_interp->import_global_inst + ? global_interp->import_module_inst->global_data + + global_interp->import_global_inst->data_offset + : inst_interp->global_data + global_interp->data_offset; +#else + uint8 *data = inst_interp->global_data + global_interp->data_offset; +#endif + + return wasm_val_to_rt_val((WASMModuleInstanceCommon *)inst_interp, + val_type_rt, v, data); +} + +static bool +interp_global_get(const WASMModuleInstance *inst_interp, uint16 global_idx_rt, + wasm_val_t *out) +{ + WASMGlobalInstance *global_interp = inst_interp->e->globals + global_idx_rt; + uint8 val_type_rt = global_interp->type; +#if WASM_ENABLE_MULTI_MODULE != 0 + uint8 *data = global_interp->import_global_inst + ? global_interp->import_module_inst->global_data + + global_interp->import_global_inst->data_offset + : inst_interp->global_data + global_interp->data_offset; +#else + uint8 *data = inst_interp->global_data + global_interp->data_offset; +#endif + + return rt_val_to_wasm_val(data, val_type_rt, out); +} +#endif + +#if WASM_ENABLE_AOT != 0 +static bool +aot_global_set(const AOTModuleInstance *inst_aot, uint16 global_idx_rt, + const wasm_val_t *v) +{ + AOTModule *module_aot = (AOTModule *)inst_aot->module; + uint8 val_type_rt = 0; + uint32 data_offset = 0; + void *data = NULL; + + if (global_idx_rt < module_aot->import_global_count) { + data_offset = module_aot->import_globals[global_idx_rt].data_offset; + val_type_rt = module_aot->import_globals[global_idx_rt].type.val_type; + } + else { + data_offset = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .data_offset; + val_type_rt = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .type.val_type; + } + + data = (void *)(inst_aot->global_data + data_offset); + return wasm_val_to_rt_val((WASMModuleInstanceCommon *)inst_aot, val_type_rt, + v, data); +} + +static bool +aot_global_get(const AOTModuleInstance *inst_aot, uint16 global_idx_rt, + wasm_val_t *out) +{ + AOTModule *module_aot = (AOTModule *)inst_aot->module; + uint8 val_type_rt = 0; + uint32 data_offset = 0; + uint8 *data = NULL; + + if (global_idx_rt < module_aot->import_global_count) { + data_offset = module_aot->import_globals[global_idx_rt].data_offset; + val_type_rt = module_aot->import_globals[global_idx_rt].type.val_type; + } + else { + data_offset = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .data_offset; + val_type_rt = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .type.val_type; + } + + data = inst_aot->global_data + data_offset; + return rt_val_to_wasm_val(data, val_type_rt, out); +} +#endif + +void +wasm_global_set(wasm_global_t *global, const wasm_val_t *v) +{ + if (!global || !v || !global->inst_comm_rt) { + return; + } + +#if WASM_ENABLE_INTERP != 0 + if (global->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + (void)interp_global_set((WASMModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, v); + return; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (global->inst_comm_rt->module_type == Wasm_Module_AoT) { + (void)aot_global_set((AOTModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, v); + return; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + UNREACHABLE(); +} + +void +wasm_global_get(const wasm_global_t *global, wasm_val_t *out) +{ + if (!global || !out) { + return; + } + + if (!global->inst_comm_rt) { + return; + } + + memset(out, 0, sizeof(wasm_val_t)); + +#if WASM_ENABLE_INTERP != 0 + if (global->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + (void)interp_global_get((WASMModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, out); + return; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (global->inst_comm_rt->module_type == Wasm_Module_AoT) { + (void)aot_global_get((AOTModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, out); + return; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + UNREACHABLE(); +} + +wasm_global_t * +wasm_global_new_internal(wasm_store_t *store, uint16 global_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_global_t *global = NULL; + uint8 val_type_rt = 0; + bool is_mutable = 0; + bool init = false; + + bh_assert(singleton_engine); + + if (!inst_comm_rt) { + return NULL; + } + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) { + goto failed; + } + + global->store = store; + global->kind = WASM_EXTERN_GLOBAL; + +#if WASM_ENABLE_INTERP != 0 + if (inst_comm_rt->module_type == Wasm_Module_Bytecode) { + WASMGlobalInstance *global_interp = + ((WASMModuleInstance *)inst_comm_rt)->e->globals + global_idx_rt; + val_type_rt = global_interp->type; + is_mutable = global_interp->is_mutable; + init = true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (inst_comm_rt->module_type == Wasm_Module_AoT) { + AOTModuleInstance *inst_aot = (AOTModuleInstance *)inst_comm_rt; + AOTModule *module_aot = (AOTModule *)inst_aot->module; + + init = true; + + if (global_idx_rt < module_aot->import_global_count) { + AOTImportGlobal *global_import_aot = + module_aot->import_globals + global_idx_rt; + val_type_rt = global_import_aot->type.val_type; + is_mutable = global_import_aot->type.is_mutable; + } + else { + AOTGlobal *global_aot = + module_aot->globals + + (global_idx_rt - module_aot->import_global_count); + val_type_rt = global_aot->type.val_type; + is_mutable = global_aot->type.is_mutable; + } + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + if (!init) { + goto failed; + } + + global->type = wasm_globaltype_new_internal(val_type_rt, is_mutable); + if (!global->type) { + goto failed; + } + + global->init = malloc_internal(sizeof(wasm_val_t)); + if (!global->init) { + goto failed; + } + +#if WASM_ENABLE_INTERP != 0 + if (inst_comm_rt->module_type == Wasm_Module_Bytecode) { + interp_global_get((WASMModuleInstance *)inst_comm_rt, global_idx_rt, + global->init); + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (inst_comm_rt->module_type == Wasm_Module_AoT) { + aot_global_get((AOTModuleInstance *)inst_comm_rt, global_idx_rt, + global->init); + } +#endif + + global->inst_comm_rt = inst_comm_rt; + global->global_idx_rt = global_idx_rt; + + return global; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + +wasm_globaltype_t * +wasm_global_type(const wasm_global_t *global) +{ + if (!global) { + return NULL; + } + return wasm_globaltype_copy(global->type); +} + +static wasm_table_t * +wasm_table_new_basic(wasm_store_t *store, const wasm_tabletype_t *type) +{ + wasm_table_t *table = NULL; + + if (!(table = malloc_internal(sizeof(wasm_table_t)))) { + goto failed; + } + + table->store = store; + table->kind = WASM_EXTERN_TABLE; + + if (!(table->type = wasm_tabletype_copy(type))) { + goto failed; + } + + RETURN_OBJ(table, wasm_table_delete); +} + +wasm_table_t * +wasm_table_new_internal(wasm_store_t *store, uint16 table_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_table_t *table = NULL; + uint8 val_type_rt = 0; +#if WASM_ENABLE_GC != 0 + WASMRefType *val_ref_type_rt; +#endif + uint32 init_size = 0, max_size = 0; + + bh_assert(singleton_engine); + + if (!inst_comm_rt) { + return NULL; + } + + if (!(table = malloc_internal(sizeof(wasm_table_t)))) { + goto failed; + } + + table->store = store; + table->kind = WASM_EXTERN_TABLE; + + if (!wasm_runtime_get_table_inst_elem_type(inst_comm_rt, table_idx_rt, + &val_type_rt, +#if WASM_ENABLE_GC != 0 + &val_ref_type_rt, +#endif + &init_size, &max_size)) { + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + goto failed; + } +#if WASM_ENABLE_GC != 0 + (void)val_ref_type_rt; /* TODO */ +#endif + + if (!(table->type = + wasm_tabletype_new_internal(val_type_rt, init_size, max_size))) { + goto failed; + } + + table->inst_comm_rt = inst_comm_rt; + table->table_idx_rt = table_idx_rt; + + RETURN_OBJ(table, wasm_table_delete); +} + +/* will not actually apply this new table into the runtime */ +wasm_table_t * +wasm_table_new(wasm_store_t *store, const wasm_tabletype_t *table_type, + wasm_ref_t *init) +{ + wasm_table_t *table; + (void)init; + + bh_assert(singleton_engine); + + if ((table = wasm_table_new_basic(store, table_type))) { + table->store = store; + } + + return table; +} + +wasm_table_t * +wasm_table_copy(const wasm_table_t *src) +{ + wasm_table_t *table; + + if (!(table = wasm_table_new_basic(src->store, src->type))) { + return NULL; + } + + table->table_idx_rt = src->table_idx_rt; + table->inst_comm_rt = src->inst_comm_rt; + return table; +} + +void +wasm_table_delete(wasm_table_t *table) +{ + if (!table) { + return; + } + + if (table->type) { + wasm_tabletype_delete(table->type); + table->type = NULL; + } + + DELETE_HOST_INFO(table) + + wasm_runtime_free(table); +} + +wasm_tabletype_t * +wasm_table_type(const wasm_table_t *table) +{ + if (!table) { + return NULL; + } + return wasm_tabletype_copy(table->type); +} + +#if WASM_ENABLE_GC == 0 +own wasm_ref_t * +wasm_table_get(const wasm_table_t *table, wasm_table_size_t index) +{ + uint32 ref_idx = NULL_REF; + + if (!table || !table->inst_comm_rt) { + return NULL; + } + +#if WASM_ENABLE_INTERP != 0 + if (table->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + WASMTableInstance *table_interp = + ((WASMModuleInstance *)table->inst_comm_rt) + ->tables[table->table_idx_rt]; + if (index >= table_interp->cur_size) { + return NULL; + } + ref_idx = (uint32)table_interp->elems[index]; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (table->inst_comm_rt->module_type == Wasm_Module_AoT) { + AOTModuleInstance *inst_aot = (AOTModuleInstance *)table->inst_comm_rt; + AOTTableInstance *table_aot = inst_aot->tables[table->table_idx_rt]; + if (index >= table_aot->cur_size) { + return NULL; + } + ref_idx = (uint32)table_aot->elems[index]; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * also leads to below branch + */ + if (ref_idx == NULL_REF) { + return NULL; + } + +#if WASM_ENABLE_REF_TYPES != 0 + if (table->type->val_type->kind == WASM_EXTERNREF) { + void *externref_obj; + if (!wasm_externref_ref2obj(ref_idx, &externref_obj)) { + return NULL; + } + + return externref_obj; + } + else +#endif + { + return wasm_ref_new_internal(table->store, WASM_REF_func, ref_idx, + table->inst_comm_rt); + } +} + +bool +wasm_table_set(wasm_table_t *table, wasm_table_size_t index, + own wasm_ref_t *ref) +{ + uint32 *p_ref_idx = NULL; + uint32 function_count = 0; + + if (!table || !table->inst_comm_rt) { + return false; + } + + if (ref +#if WASM_ENABLE_REF_TYPES != 0 + && !(WASM_REF_foreign == ref->kind + && WASM_EXTERNREF == table->type->val_type->kind) +#endif + && !(WASM_REF_func == ref->kind + && WASM_FUNCREF == table->type->val_type->kind)) { + return false; + } + +#if WASM_ENABLE_INTERP != 0 + if (table->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + WASMTableInstance *table_interp = + ((WASMModuleInstance *)table->inst_comm_rt) + ->tables[table->table_idx_rt]; + + if (index >= table_interp->cur_size) { + return false; + } + + p_ref_idx = (uint32 *)(table_interp->elems + index); + function_count = + ((WASMModuleInstance *)table->inst_comm_rt)->e->function_count; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (table->inst_comm_rt->module_type == Wasm_Module_AoT) { + AOTModuleInstance *inst_aot = (AOTModuleInstance *)table->inst_comm_rt; + AOTModule *module_aot = (AOTModule *)inst_aot->module; + AOTTableInstance *table_aot = inst_aot->tables[table->table_idx_rt]; + + if (index >= table_aot->cur_size) { + return false; + } + + p_ref_idx = (uint32 *)(table_aot->elems + index); + function_count = module_aot->func_count; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + if (!p_ref_idx) { + return false; + } + +#if WASM_ENABLE_REF_TYPES != 0 + if (table->type->val_type->kind == WASM_EXTERNREF) { + return wasm_externref_obj2ref(table->inst_comm_rt, ref, p_ref_idx); + } + else +#endif + { + if (ref) { + if (NULL_REF != ref->ref_idx_rt) { + if (ref->ref_idx_rt >= function_count) { + return false; + } + } + *p_ref_idx = ref->ref_idx_rt; + wasm_ref_delete(ref); + } + else { + *p_ref_idx = NULL_REF; + } + } + + return true; +} +#else /* else of WASM_ENABLE_GC == 0 */ +own wasm_ref_t * +wasm_table_get(const wasm_table_t *table, wasm_table_size_t index) +{ + /* TODO */ + return NULL; +} + +bool +wasm_table_set(wasm_table_t *table, wasm_table_size_t index, + own wasm_ref_t *ref) +{ + /* TODO */ + return false; +} +#endif /* end of WASM_ENABLE_GC == 0 */ + +wasm_table_size_t +wasm_table_size(const wasm_table_t *table) +{ + if (!table || !table->inst_comm_rt) { + return 0; + } + +#if WASM_ENABLE_INTERP != 0 + if (table->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + WASMTableInstance *table_interp = + ((WASMModuleInstance *)table->inst_comm_rt) + ->tables[table->table_idx_rt]; + return table_interp->cur_size; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (table->inst_comm_rt->module_type == Wasm_Module_AoT) { + AOTModuleInstance *inst_aot = (AOTModuleInstance *)table->inst_comm_rt; + AOTModule *module_aot = (AOTModule *)inst_aot->module; + + if (table->table_idx_rt < module_aot->import_table_count) { + AOTImportTable *table_aot = + module_aot->import_tables + table->table_idx_rt; + return table_aot->table_type.init_size; + } + else { + AOTTable *table_aot = + module_aot->tables + + (table->table_idx_rt - module_aot->import_table_count); + return table_aot->table_type.init_size; + } + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + return 0; +} + +bool +wasm_table_grow(wasm_table_t *table, wasm_table_size_t delta, + own wasm_ref_t *init) +{ + (void)table; + (void)delta; + (void)init; + LOG_WARNING("Calling wasm_table_grow() by host is not supported." + "Only allow growing a table via the opcode table.grow"); + return false; +} + +static wasm_memory_t * +wasm_memory_new_basic(wasm_store_t *store, const wasm_memorytype_t *type) +{ + wasm_memory_t *memory = NULL; + + if (!type) { + goto failed; + } + + if (!(memory = malloc_internal(sizeof(wasm_memory_t)))) { + goto failed; + } + + memory->store = store; + memory->kind = WASM_EXTERN_MEMORY; + memory->type = wasm_memorytype_copy(type); + + RETURN_OBJ(memory, wasm_memory_delete) +} + +wasm_memory_t * +wasm_memory_new(wasm_store_t *store, const wasm_memorytype_t *type) +{ + bh_assert(singleton_engine); + return wasm_memory_new_basic(store, type); +} + +wasm_memory_t * +wasm_memory_copy(const wasm_memory_t *src) +{ + wasm_memory_t *dst = NULL; + + if (!src) { + return NULL; + } + + if (!(dst = wasm_memory_new_basic(src->store, src->type))) { + goto failed; + } + + dst->memory_idx_rt = src->memory_idx_rt; + dst->inst_comm_rt = src->inst_comm_rt; + + RETURN_OBJ(dst, wasm_memory_delete) +} + +wasm_memory_t * +wasm_memory_new_internal(wasm_store_t *store, uint16 memory_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_memory_t *memory = NULL; + uint32 min_pages = 0, max_pages = 0; + bool init_flag = false; + + bh_assert(singleton_engine); + + if (!inst_comm_rt) { + return NULL; + } + + if (!(memory = malloc_internal(sizeof(wasm_memory_t)))) { + goto failed; + } + + memory->store = store; + memory->kind = WASM_EXTERN_MEMORY; + +#if WASM_ENABLE_INTERP != 0 + if (inst_comm_rt->module_type == Wasm_Module_Bytecode) { + WASMMemoryInstance *memory_interp = + ((WASMModuleInstance *)inst_comm_rt)->memories[memory_idx_rt]; + min_pages = memory_interp->cur_page_count; + max_pages = memory_interp->max_page_count; + init_flag = true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (inst_comm_rt->module_type == Wasm_Module_AoT) { + AOTModuleInstance *inst_aot = (AOTModuleInstance *)inst_comm_rt; + AOTModule *module_aot = (AOTModule *)inst_aot->module; + + if (memory_idx_rt < module_aot->import_memory_count) { + min_pages = module_aot->import_memories->mem_type.init_page_count; + max_pages = module_aot->import_memories->mem_type.max_page_count; + } + else { + min_pages = module_aot->memories->init_page_count; + max_pages = module_aot->memories->max_page_count; + } + init_flag = true; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + if (!init_flag) { + goto failed; + } + + if (!(memory->type = wasm_memorytype_new_internal(min_pages, max_pages))) { + goto failed; + } + + memory->inst_comm_rt = inst_comm_rt; + memory->memory_idx_rt = memory_idx_rt; + + RETURN_OBJ(memory, wasm_memory_delete); +} + +void +wasm_memory_delete(wasm_memory_t *memory) +{ + if (!memory) { + return; + } + + if (memory->type) { + wasm_memorytype_delete(memory->type); + memory->type = NULL; + } + + DELETE_HOST_INFO(memory) + + wasm_runtime_free(memory); +} + +wasm_memorytype_t * +wasm_memory_type(const wasm_memory_t *memory) +{ + if (!memory) { + return NULL; + } + + return wasm_memorytype_copy(memory->type); +} + +byte_t * +wasm_memory_data(wasm_memory_t *memory) +{ + WASMModuleInstanceCommon *module_inst_comm; + + if (!memory || !memory->inst_comm_rt) { + return NULL; + } + + module_inst_comm = memory->inst_comm_rt; +#if WASM_ENABLE_INTERP != 0 + if (module_inst_comm->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *module_inst = + (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst = + module_inst->memories[memory->memory_idx_rt]; + return (byte_t *)memory_inst->memory_data; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst_comm->module_type == Wasm_Module_AoT) { + AOTModuleInstance *module_inst = (AOTModuleInstance *)module_inst_comm; + AOTMemoryInstance *memory_inst = + ((AOTMemoryInstance **) + module_inst->memories)[memory->memory_idx_rt]; + return (byte_t *)memory_inst->memory_data; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + return NULL; +} + +size_t +wasm_memory_data_size(const wasm_memory_t *memory) +{ + WASMModuleInstanceCommon *module_inst_comm; + + if (!memory || !memory->inst_comm_rt) { + return 0; + } + + module_inst_comm = memory->inst_comm_rt; +#if WASM_ENABLE_INTERP != 0 + if (module_inst_comm->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *module_inst = + (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst = + module_inst->memories[memory->memory_idx_rt]; + return (size_t)memory_inst->cur_page_count + * memory_inst->num_bytes_per_page; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst_comm->module_type == Wasm_Module_AoT) { + AOTModuleInstance *module_inst = (AOTModuleInstance *)module_inst_comm; + AOTMemoryInstance *memory_inst = + ((AOTMemoryInstance **) + module_inst->memories)[memory->memory_idx_rt]; + return (size_t)memory_inst->cur_page_count + * memory_inst->num_bytes_per_page; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + return 0; +} + +wasm_memory_pages_t +wasm_memory_size(const wasm_memory_t *memory) +{ + WASMModuleInstanceCommon *module_inst_comm; + + if (!memory || !memory->inst_comm_rt) { + return 0; + } + + module_inst_comm = memory->inst_comm_rt; +#if WASM_ENABLE_INTERP != 0 + if (module_inst_comm->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *module_inst = + (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst = + module_inst->memories[memory->memory_idx_rt]; + return memory_inst->cur_page_count; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst_comm->module_type == Wasm_Module_AoT) { + AOTModuleInstance *module_inst = (AOTModuleInstance *)module_inst_comm; + AOTMemoryInstance *memory_inst = + ((AOTMemoryInstance **) + module_inst->memories)[memory->memory_idx_rt]; + return memory_inst->cur_page_count; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + return 0; +} + +bool +wasm_memory_grow(wasm_memory_t *memory, wasm_memory_pages_t delta) +{ + (void)memory; + (void)delta; + LOG_WARNING("Calling wasm_memory_grow() by host is not supported." + "Only allow growing a memory via the opcode memory.grow"); + return false; +} + +#if WASM_ENABLE_INTERP != 0 +static bool +interp_link_func(const wasm_instance_t *inst, const WASMModule *module_interp, + uint16 func_idx_rt, wasm_func_t *import) +{ + WASMImport *imported_func_interp = NULL; + + bh_assert(inst && module_interp && import); + bh_assert(func_idx_rt < module_interp->import_function_count); + bh_assert(WASM_EXTERN_FUNC == import->kind); + + 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( + import->type, imported_func_interp->u.function.func_type)) + return false; + + imported_func_interp->u.function.call_conv_wasm_c_api = true; + /* only set func_ptr_linked to avoid unlink warning during instantiation, + func_ptr_linked, with_env and env will be stored in module instance's + c_api_func_imports later and used when calling import function */ + if (import->with_env) + 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; + return true; +} + +static bool +interp_link_global(const WASMModule *module_interp, uint16 global_idx_rt, + wasm_global_t *import) +{ + WASMImport *imported_global_interp = NULL; + + bh_assert(module_interp && import); + bh_assert(global_idx_rt < module_interp->import_global_count); + bh_assert(WASM_EXTERN_GLOBAL == import->kind); + + 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.val_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 = + import->init->of.i32; + break; + case WASM_I64: + imported_global_interp->u.global.global_data_linked.i64 = + import->init->of.i64; + break; + case WASM_F32: + imported_global_interp->u.global.global_data_linked.f32 = + import->init->of.f32; + break; + case WASM_F64: + imported_global_interp->u.global.global_data_linked.f64 = + import->init->of.f64; + break; + default: + return false; + } + + import->global_idx_rt = global_idx_rt; + imported_global_interp->u.global.is_linked = true; + return true; +} + +static bool +interp_process_export(wasm_store_t *store, + const WASMModuleInstance *inst_interp, + wasm_extern_vec_t *externals) +{ + WASMExport *exports = NULL; + WASMExport *export = NULL; + wasm_extern_t *external = NULL; + uint32 export_cnt = 0; + uint32 i = 0; + + bh_assert(store && inst_interp && inst_interp->module && externals); + + exports = inst_interp->module->exports; + export_cnt = inst_interp->module->export_count; + + for (i = 0; i < export_cnt; ++i) { + export = exports + i; + + switch (export->kind) { + case EXPORT_KIND_FUNC: + { + wasm_func_t *func; + if (!(func = wasm_func_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_interp))) { + goto failed; + } + + external = wasm_func_as_extern(func); + break; + } + case EXPORT_KIND_GLOBAL: + { + wasm_global_t *global; + if (!(global = wasm_global_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_interp))) { + goto failed; + } + + external = wasm_global_as_extern(global); + break; + } + case EXPORT_KIND_TABLE: + { + wasm_table_t *table; + if (!(table = wasm_table_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_interp))) { + goto failed; + } + + external = wasm_table_as_extern(table); + break; + } + case EXPORT_KIND_MEMORY: + { + wasm_memory_t *memory; + if (!(memory = wasm_memory_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_interp))) { + goto failed; + } + + external = wasm_memory_as_extern(memory); + break; + } + default: + LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, + export->kind); + goto failed; + } + + if (!bh_vector_append((Vector *)externals, &external)) { + goto failed; + } + } + + return true; + +failed: + wasm_extern_delete(external); + return false; +} +#endif /* WASM_ENABLE_INTERP */ + +#if WASM_ENABLE_AOT != 0 +static bool +aot_link_func(const wasm_instance_t *inst, const AOTModule *module_aot, + uint32 import_func_idx_rt, wasm_func_t *import) +{ + AOTImportFunc *import_aot_func = NULL; + + bh_assert(inst && module_aot && import); + + 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; + + import_aot_func->call_conv_wasm_c_api = true; + /* only set func_ptr_linked to avoid unlink warning during instantiation, + func_ptr_linked, with_env and env will be stored in module instance's + c_api_func_imports later and used when calling import function */ + if (import->with_env) + 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; +} + +static bool +aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, + wasm_global_t *import) +{ + AOTImportGlobal *import_aot_global = NULL; + const wasm_valtype_t *val_type = NULL; + + bh_assert(module_aot && import); + + 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); + + if (!cmp_val_kind_with_val_type(wasm_valtype_kind(val_type), + import_aot_global->type.val_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; + break; + case WASM_I64: + import_aot_global->global_data_linked.i64 = import->init->of.i64; + break; + case WASM_F32: + import_aot_global->global_data_linked.f32 = import->init->of.f32; + break; + case WASM_F64: + import_aot_global->global_data_linked.f64 = import->init->of.f64; + break; + default: + goto failed; + } + + 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_process_export(wasm_store_t *store, const AOTModuleInstance *inst_aot, + wasm_extern_vec_t *externals) +{ + uint32 i; + wasm_extern_t *external = NULL; + AOTModule *module_aot = NULL; + + bh_assert(store && inst_aot && externals); + + module_aot = (AOTModule *)inst_aot->module; + bh_assert(module_aot); + + for (i = 0; i < module_aot->export_count; ++i) { + AOTExport *export = module_aot->exports + i; + + switch (export->kind) { + case EXPORT_KIND_FUNC: + { + wasm_func_t *func = NULL; + if (!(func = wasm_func_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_aot))) { + goto failed; + } + + external = wasm_func_as_extern(func); + break; + } + case EXPORT_KIND_GLOBAL: + { + wasm_global_t *global = NULL; + if (!(global = wasm_global_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_aot))) { + goto failed; + } + + external = wasm_global_as_extern(global); + break; + } + case EXPORT_KIND_TABLE: + { + wasm_table_t *table; + if (!(table = wasm_table_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_aot))) { + goto failed; + } + + external = wasm_table_as_extern(table); + break; + } + case EXPORT_KIND_MEMORY: + { + wasm_memory_t *memory; + if (!(memory = wasm_memory_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_aot))) { + goto failed; + } + + external = wasm_memory_as_extern(memory); + break; + } + default: + LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, + export->kind); + goto failed; + } + + if (!(external->name = malloc_internal(sizeof(wasm_byte_vec_t)))) { + goto failed; + } + + wasm_name_new_from_string_nt(external->name, export->name); + if (strlen(export->name) && !external->name->data) { + goto failed; + } + + if (!bh_vector_append((Vector *)externals, &external)) { + goto failed; + } + } + + return true; + +failed: + wasm_extern_delete(external); + return false; +} +#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) +{ + return wasm_instance_new_with_args(store, module, imports, trap, + KILOBYTE(32), KILOBYTE(32)); +} + +wasm_instance_t * +wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, + const wasm_extern_vec_t *imports, + own wasm_trap_t **trap, const uint32 stack_size, + const uint32 heap_size) +{ + InstantiationArgs inst_args = { 0 }; + inst_args.default_stack_size = stack_size; + inst_args.host_managed_heap_size = heap_size; + return wasm_instance_new_with_args_ex(store, module, imports, trap, + &inst_args); +} + +wasm_instance_t * +wasm_instance_new_with_args_ex(wasm_store_t *store, const wasm_module_t *module, + const wasm_extern_vec_t *imports, + own wasm_trap_t **trap, + const InstantiationArgs *inst_args) +{ + char sub_error_buf[128] = { 0 }; + char error_buf[256] = { 0 }; + wasm_instance_t *instance = NULL; + CApiFuncImport *func_import = NULL, **p_func_imports = NULL; + uint32 i = 0, import_func_count = 0; + uint64 total_size; + bool build_exported = false; + + bh_assert(singleton_engine); + + if (!module) + return NULL; + + /* + * will do the check at the end of wasm_runtime_instantiate + */ + + WASM_C_DUMP_PROC_MEM(); + + instance = malloc_internal(sizeof(wasm_instance_t)); + if (!instance) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Failed to malloc instance"); + goto failed; + } + + /* 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 validate imports"); + goto failed; + } + } + /* + * will do the linking result check at the end of wasm_runtime_instantiate + */ + + instance->inst_comm_rt = wasm_runtime_instantiate_ex( + *module, inst_args, sub_error_buf, sizeof(sub_error_buf)); + if (!instance->inst_comm_rt) { + goto failed; + } + + if (!wasm_runtime_create_exec_env_singleton(instance->inst_comm_rt)) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Failed to create exec env singleton"); + goto failed; + } + + /* create the c-api func import list */ +#if WASM_ENABLE_INTERP != 0 + if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *wasm_module_inst = + (WASMModuleInstance *)instance->inst_comm_rt; + p_func_imports = &(wasm_module_inst->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) { + AOTModuleInstance *aot_module_inst = + (AOTModuleInstance *)instance->inst_comm_rt; + p_func_imports = &(aot_module_inst->c_api_func_imports); + import_func_count = MODULE_AOT(module)->import_func_count; + } +#endif + bh_assert(p_func_imports); + + total_size = (uint64)sizeof(CApiFuncImport) * import_func_count; + if (total_size > 0 + && !(*p_func_imports = func_import = malloc_internal(total_size))) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Failed to create wasm-c-api func imports"); + goto failed; + } + + /* fill in module_inst->c_api_func_imports */ + for (i = 0; imports && i < imports->num_elems; i++) { + wasm_func_t *func_host = NULL; + wasm_extern_t *in = imports->data[i]; + bh_assert(in); + + 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) { + func_import->func_ptr_linked = func_host->u.cb_env.cb; + func_import->env_arg = func_host->u.cb_env.env; + } + else { + func_import->func_ptr_linked = func_host->u.cb; + func_import->env_arg = NULL; + } + bh_assert(func_import->func_ptr_linked); + + func_import++; + } + + /* 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 = + instance->inst_comm_rt; + break; + case WASM_EXTERN_GLOBAL: + wasm_extern_as_global(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + case WASM_EXTERN_MEMORY: + wasm_extern_as_memory(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + case WASM_EXTERN_TABLE: + wasm_extern_as_table(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + default: + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Unknown import kind"); + goto failed; + } + } + + /* build the exports list */ +#if WASM_ENABLE_INTERP != 0 + if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) { + uint32 export_cnt = ((WASMModuleInstance *)instance->inst_comm_rt) + ->module->export_count; + + INIT_VEC(instance->exports, wasm_extern_vec_new_uninitialized, + export_cnt); + + if (!interp_process_export(store, + (WASMModuleInstance *)instance->inst_comm_rt, + instance->exports)) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Interpreter failed to process exports"); + goto failed; + } + + build_exported = true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (instance->inst_comm_rt->module_type == Wasm_Module_AoT) { + uint32 export_cnt = + ((AOTModuleInstance *)instance->inst_comm_rt)->export_func_count + + ((AOTModuleInstance *)instance->inst_comm_rt)->export_global_count + + ((AOTModuleInstance *)instance->inst_comm_rt)->export_table_count + + ((AOTModuleInstance *)instance->inst_comm_rt) + ->export_memory_count; + + INIT_VEC(instance->exports, wasm_extern_vec_new_uninitialized, + export_cnt); + + if (!aot_process_export(store, + (AOTModuleInstance *)instance->inst_comm_rt, + instance->exports)) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "AOT failed to process exports"); + goto failed; + } + + build_exported = true; + } +#endif + + /* + * a wrong combination of module filetype and compilation flags + * leads to below branch + */ + if (!build_exported) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Incorrect filetype and compilation flags"); + goto failed; + } + + /* add it to a watching list in store */ + if (!bh_vector_append((Vector *)store->instances, &instance)) { + snprintf(sub_error_buf, sizeof(sub_error_buf), + "Failed to add to store instances"); + goto failed; + } + + WASM_C_DUMP_PROC_MEM(); + + return instance; + +failed: + snprintf(error_buf, sizeof(error_buf), "%s failed: %s", __FUNCTION__, + sub_error_buf); + if (trap != NULL) { + wasm_message_t message = { 0 }; + wasm_name_new_from_string_nt(&message, error_buf); + *trap = wasm_trap_new(store, &message); + wasm_byte_vec_delete(&message); + } + LOG_DEBUG("%s", error_buf); + wasm_instance_delete_internal(instance); + return NULL; +} + +static void +wasm_instance_delete_internal(wasm_instance_t *instance) +{ + if (!instance) { + return; + } + + DEINIT_VEC(instance->exports, wasm_extern_vec_delete); + + if (instance->inst_comm_rt) { + wasm_runtime_deinstantiate(instance->inst_comm_rt); + instance->inst_comm_rt = NULL; + } + wasm_runtime_free(instance); +} + +void +wasm_instance_delete(wasm_instance_t *inst) +{ + DELETE_HOST_INFO(inst) + /* will release instance when releasing the store */ +} + +void +wasm_instance_exports(const wasm_instance_t *instance, + own wasm_extern_vec_t *out) +{ + if (!instance || !out) { + return; + } + wasm_extern_vec_copy(out, instance->exports); +} + +wasm_extern_t * +wasm_extern_copy(const wasm_extern_t *src) +{ + wasm_extern_t *dst = NULL; + + if (!src) { + return NULL; + } + + switch (wasm_extern_kind(src)) { + case WASM_EXTERN_FUNC: + dst = wasm_func_as_extern( + wasm_func_copy(wasm_extern_as_func_const(src))); + break; + case WASM_EXTERN_GLOBAL: + dst = wasm_global_as_extern( + wasm_global_copy(wasm_extern_as_global_const(src))); + break; + case WASM_EXTERN_MEMORY: + dst = wasm_memory_as_extern( + wasm_memory_copy(wasm_extern_as_memory_const(src))); + break; + case WASM_EXTERN_TABLE: + dst = wasm_table_as_extern( + wasm_table_copy(wasm_extern_as_table_const(src))); + break; + default: + LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, + src->kind); + break; + } + + if (!dst) { + goto failed; + } + + return dst; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_extern_delete(dst); + return NULL; +} + +void +wasm_extern_delete(wasm_extern_t *external) +{ + if (!external) { + return; + } + + if (external->name) { + wasm_byte_vec_delete(external->name); + wasm_runtime_free(external->name); + external->name = NULL; + } + + switch (wasm_extern_kind(external)) { + case WASM_EXTERN_FUNC: + wasm_func_delete(wasm_extern_as_func(external)); + break; + case WASM_EXTERN_GLOBAL: + wasm_global_delete(wasm_extern_as_global(external)); + break; + case WASM_EXTERN_MEMORY: + wasm_memory_delete(wasm_extern_as_memory(external)); + break; + case WASM_EXTERN_TABLE: + wasm_table_delete(wasm_extern_as_table(external)); + break; + default: + LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, + external->kind); + break; + } +} + +wasm_externkind_t +wasm_extern_kind(const wasm_extern_t *external) +{ + if (!external) { + return WASM_EXTERNREF; + } + + return external->kind; +} + +own wasm_externtype_t * +wasm_extern_type(const wasm_extern_t *external) +{ + if (!external) { + return NULL; + } + + switch (wasm_extern_kind(external)) { + case WASM_EXTERN_FUNC: + return wasm_functype_as_externtype( + wasm_func_type(wasm_extern_as_func_const(external))); + case WASM_EXTERN_GLOBAL: + return wasm_globaltype_as_externtype( + wasm_global_type(wasm_extern_as_global_const(external))); + case WASM_EXTERN_MEMORY: + return wasm_memorytype_as_externtype( + wasm_memory_type(wasm_extern_as_memory_const(external))); + case WASM_EXTERN_TABLE: + return wasm_tabletype_as_externtype( + wasm_table_type(wasm_extern_as_table_const(external))); + default: + LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, + external->kind); + break; + } + return NULL; +} + +#define BASIC_FOUR_LIST(V) \ + V(func) \ + V(global) \ + V(memory) \ + V(table) + +#define WASM_EXTERN_AS_OTHER(name) \ + wasm_##name##_t *wasm_extern_as_##name(wasm_extern_t *external) \ + { \ + return (wasm_##name##_t *)external; \ + } + +BASIC_FOUR_LIST(WASM_EXTERN_AS_OTHER) +#undef WASM_EXTERN_AS_OTHER + +#define WASM_OTHER_AS_EXTERN(name) \ + wasm_extern_t *wasm_##name##_as_extern(wasm_##name##_t *other) \ + { \ + return (wasm_extern_t *)other; \ + } + +BASIC_FOUR_LIST(WASM_OTHER_AS_EXTERN) +#undef WASM_OTHER_AS_EXTERN + +#define WASM_EXTERN_AS_OTHER_CONST(name) \ + const wasm_##name##_t *wasm_extern_as_##name##_const( \ + const wasm_extern_t *external) \ + { \ + return (const wasm_##name##_t *)external; \ + } + +BASIC_FOUR_LIST(WASM_EXTERN_AS_OTHER_CONST) +#undef WASM_EXTERN_AS_OTHER_CONST + +#define WASM_OTHER_AS_EXTERN_CONST(name) \ + const wasm_extern_t *wasm_##name##_as_extern_const( \ + const wasm_##name##_t *other) \ + { \ + return (const wasm_extern_t *)other; \ + } + +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; +} + +double +wasm_instance_sum_wasm_exec_time(const wasm_instance_t *instance) +{ +#if WASM_ENABLE_PERF_PROFILING != 0 + return wasm_runtime_sum_wasm_exec_time(instance->inst_comm_rt); +#else + return -1.0; +#endif +} + +double +wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance, + const char *name) +{ +#if WASM_ENABLE_PERF_PROFILING != 0 + return wasm_runtime_get_wasm_func_exec_time(instance->inst_comm_rt, name); +#else + return -1.0; +#endif +} diff --git a/core/iwasm/common/wasm_c_api_internal.h b/core/iwasm/common/wasm_c_api_internal.h new file mode 100644 index 0000000000..49a17a964b --- /dev/null +++ b/core/iwasm/common/wasm_c_api_internal.h @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_C_API_INTERNAL_H +#define _WASM_C_API_INTERNAL_H + +#include "../include/wasm_c_api.h" +#include "wasm_runtime_common.h" + +#ifndef own +#define own +#endif + +/* Vectors */ +/* we will malloc resource for the vector's data field */ +/* we will release resource of data */ +/* caller needs to take care resource for the vector itself */ +#define DEFAULT_VECTOR_INIT_LENGTH (64) + +WASM_DECLARE_VEC(instance, *) +WASM_DECLARE_VEC(module, *) +WASM_DECLARE_VEC(store, *) + +/* Runtime Environment */ +struct wasm_engine_t { + uint32 ref_count; + /* list of wasm_module_ex_t */ + Vector modules; + /* list of stores which are classified according to tids */ + Vector stores_by_tid; +}; + +struct wasm_store_t { + /* maybe should remove the list */ + wasm_module_vec_t *modules; + wasm_instance_vec_t *instances; + Vector *foreigns; +}; + +/* Type Representations */ +struct wasm_valtype_t { + wasm_valkind_t kind; +}; + +struct wasm_functype_t { + uint32 extern_kind; + /* gona to new and delete own */ + wasm_valtype_vec_t *params; + wasm_valtype_vec_t *results; +}; + +struct wasm_globaltype_t { + uint32 extern_kind; + /* gona to new and delete own */ + wasm_valtype_t *val_type; + wasm_mutability_t mutability; +}; + +struct wasm_tabletype_t { + uint32 extern_kind; + wasm_valtype_t *val_type; + wasm_limits_t limits; +}; + +struct wasm_memorytype_t { + uint32 extern_kind; + wasm_limits_t limits; +}; + +struct wasm_externtype_t { + uint32 extern_kind; + /* reserved space */ + uint8 data[1]; +}; + +struct wasm_importtype_t { + wasm_name_t *module_name; + wasm_name_t *name; + wasm_externtype_t *extern_type; +}; + +struct wasm_exporttype_t { + wasm_name_t *name; + wasm_externtype_t *extern_type; +}; + +/* Runtime Objects */ +enum wasm_reference_kind { + WASM_REF_foreign, + WASM_REF_func, + WASM_REF_global, + WASM_REF_memory, + WASM_REF_table, +}; + +struct wasm_host_info { + void *info; + void (*finalizer)(void *); +}; + +struct wasm_ref_t { + wasm_store_t *store; + enum wasm_reference_kind kind; + struct wasm_host_info host_info; + uint32 ref_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_trap_t { + wasm_byte_vec_t *message; + Vector *frames; +}; + +struct wasm_foreign_t { + wasm_store_t *store; + enum wasm_reference_kind kind; + struct wasm_host_info host_info; + int32 ref_cnt; + uint32 foreign_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_func_t { + wasm_store_t *store; + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + struct wasm_host_info host_info; + wasm_functype_t *type; + uint16 param_count; + uint16 result_count; + + bool with_env; + union { + wasm_func_callback_t cb; + struct callback_ext { + void *env; + wasm_func_callback_with_env_t cb; + void (*finalizer)(void *); + } cb_env; + } u; + /* + * an index in both functions runtime instance lists + * of interpreter mode and aot mode + */ + uint16 func_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; + WASMFunctionInstanceCommon *func_comm_rt; +}; + +struct wasm_global_t { + wasm_store_t *store; + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + struct wasm_host_info host_info; + wasm_globaltype_t *type; + wasm_val_t *init; + /* + * an index in both global runtime instance lists + * of interpreter mode and aot mode + */ + uint16 global_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_memory_t { + wasm_store_t *store; + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + struct wasm_host_info host_info; + wasm_memorytype_t *type; + /* + * an index in both memory runtime instance lists + * of interpreter mode and aot mode + */ + uint16 memory_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_table_t { + wasm_store_t *store; + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + struct wasm_host_info host_info; + wasm_tabletype_t *type; + /* + * an index in both table runtime instance lists + * of interpreter mode and aot mode + */ + uint16 table_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_extern_t { + wasm_store_t *store; + wasm_name_t *module_name; + wasm_name_t *name; + wasm_externkind_t kind; + /* reserved space */ + uint8 data[1]; +}; + +struct wasm_instance_t { + wasm_store_t *store; + wasm_extern_vec_t *exports; + struct wasm_host_info host_info; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +wasm_ref_t * +wasm_ref_new_internal(wasm_store_t *store, enum wasm_reference_kind kind, + uint32 obj_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt); + +wasm_foreign_t * +wasm_foreign_new_internal(wasm_store_t *store, uint32 foreign_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt); + +wasm_func_t * +wasm_func_new_internal(wasm_store_t *store, uint16 func_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt); + +wasm_global_t * +wasm_global_new_internal(wasm_store_t *store, uint16 global_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt); + +wasm_memory_t * +wasm_memory_new_internal(wasm_store_t *store, uint16 memory_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt); + +wasm_table_t * +wasm_table_new_internal(wasm_store_t *store, uint16 table_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt); + +void +wasm_frame_vec_clone_internal(Vector *src, Vector *out); +#endif /* _WASM_C_API_INTERNAL_H */ diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c index 6e7054e265..47752950f2 100644 --- a/core/iwasm/common/wasm_exec_env.c +++ b/core/iwasm/common/wasm_exec_env.c @@ -4,34 +4,219 @@ */ #include "wasm_exec_env.h" -#include "bh_memory.h" #include "wasm_runtime_common.h" +#if WASM_ENABLE_GC != 0 +#include "mem_alloc.h" +#endif +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif + +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#if WASM_ENABLE_DEBUG_INTERP != 0 +#include "../libraries/debug-engine/debug_engine.h" +#endif +#endif WASMExecEnv * -wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, - uint32 stack_size) +wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size) { - uint64 total_size = offsetof(WASMExecEnv, wasm_stack.s.bottom) - + (uint64)stack_size; + uint64 total_size = + offsetof(WASMExecEnv, wasm_stack_u.bottom) + (uint64)stack_size; WASMExecEnv *exec_env; if (total_size >= UINT32_MAX - || !(exec_env = wasm_malloc((uint32)total_size))) + || !(exec_env = wasm_runtime_malloc((uint32)total_size))) return NULL; memset(exec_env, 0, (uint32)total_size); + +#if WASM_ENABLE_AOT != 0 + if (!(exec_env->argv_buf = wasm_runtime_malloc(sizeof(uint32) * 64))) { + goto fail1; + } +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + if (os_mutex_init(&exec_env->wait_lock) != 0) + goto fail2; + + if (os_cond_init(&exec_env->wait_cond) != 0) + goto fail3; + +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!(exec_env->current_status = wasm_cluster_create_exenv_status())) + goto fail4; +#endif +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!(exec_env->exce_check_guard_page = + os_mmap(NULL, os_getpagesize(), MMAP_PROT_NONE, MMAP_MAP_NONE, + os_get_invalid_handle()))) + goto fail5; +#endif + exec_env->module_inst = module_inst; exec_env->wasm_stack_size = stack_size; - exec_env->wasm_stack.s.top_boundary = - exec_env->wasm_stack.s.bottom + stack_size; - exec_env->wasm_stack.s.top = exec_env->wasm_stack.s.bottom; + exec_env->wasm_stack.bottom = exec_env->wasm_stack_u.bottom; + exec_env->wasm_stack.top_boundary = + exec_env->wasm_stack.bottom + stack_size; + exec_env->wasm_stack.top = exec_env->wasm_stack.bottom; + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *i = (AOTModuleInstance *)module_inst; + AOTModule *m = (AOTModule *)i->module; + exec_env->native_symbol = m->native_symbol_list; + } +#endif + +#if WASM_ENABLE_MEMORY_TRACING != 0 + wasm_runtime_dump_exec_env_mem_consumption(exec_env); +#endif + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + exec_env->instructions_to_execute = -1; +#endif + + return exec_env; + +#ifdef OS_ENABLE_HW_BOUND_CHECK +fail5: +#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 + wasm_cluster_destroy_exenv_status(exec_env->current_status); +#endif +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#if WASM_ENABLE_DEBUG_INTERP != 0 +fail4: + os_cond_destroy(&exec_env->wait_cond); +#endif +fail3: + os_mutex_destroy(&exec_env->wait_lock); +fail2: +#endif +#if WASM_ENABLE_AOT != 0 + wasm_runtime_free(exec_env->argv_buf); +fail1: +#endif + wasm_runtime_free(exec_env); + return NULL; +} + +void +wasm_exec_env_destroy_internal(WASMExecEnv *exec_env) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + os_munmap(exec_env->exce_check_guard_page, os_getpagesize()); +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + os_mutex_destroy(&exec_env->wait_lock); + os_cond_destroy(&exec_env->wait_cond); +#if WASM_ENABLE_DEBUG_INTERP != 0 + wasm_cluster_destroy_exenv_status(exec_env->current_status); +#endif +#endif +#if WASM_ENABLE_AOT != 0 + wasm_runtime_free(exec_env->argv_buf); +#endif + wasm_runtime_free(exec_env); +} + +WASMExecEnv * +wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + WASMCluster *cluster; +#endif + WASMExecEnv *exec_env = + wasm_exec_env_create_internal(module_inst, stack_size); +#if WASM_ENABLE_GC != 0 + void *gc_heap_handle = NULL; +#endif + + if (!exec_env) + return NULL; + +#if WASM_ENABLE_INTERP != 0 + /* Set the aux_stack_boundary and aux_stack_bottom */ + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModule *module = ((WASMModuleInstance *)module_inst)->module; + exec_env->aux_stack_bottom = (uintptr_t)module->aux_stack_bottom; + exec_env->aux_stack_boundary = + (uintptr_t)module->aux_stack_bottom - module->aux_stack_size; +#if WASM_ENABLE_GC != 0 + gc_heap_handle = + ((WASMModuleInstance *)module_inst)->e->common.gc_heap_pool; +#endif + } +#endif +#if WASM_ENABLE_AOT != 0 + /* Set the aux_stack_boundary and aux_stack_bottom */ + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModule *module = + (AOTModule *)((AOTModuleInstance *)module_inst)->module; + exec_env->aux_stack_bottom = (uintptr_t)module->aux_stack_bottom; + exec_env->aux_stack_boundary = + (uintptr_t)module->aux_stack_bottom - module->aux_stack_size; +#if WASM_ENABLE_GC != 0 + gc_heap_handle = + ((AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e) + ->common.gc_heap_handle; +#endif + } +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Create a new cluster for this exec_env */ + if (!(cluster = wasm_cluster_create(exec_env))) { + wasm_exec_env_destroy_internal(exec_env); + return NULL; + } +#if WASM_ENABLE_GC != 0 + mem_allocator_enable_gc_reclaim(gc_heap_handle, cluster); +#endif +#else +#if WASM_ENABLE_GC != 0 + mem_allocator_enable_gc_reclaim(gc_heap_handle, exec_env); +#endif +#endif /* end of WASM_ENABLE_THREAD_MGR */ + return exec_env; } void wasm_exec_env_destroy(WASMExecEnv *exec_env) { - wasm_free(exec_env); +#if WASM_ENABLE_THREAD_MGR != 0 + /* Wait for all sub-threads */ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + if (cluster) { + 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 overridden 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 */ + + wasm_exec_env_destroy_internal(exec_env); } WASMModuleInstanceCommon * @@ -40,3 +225,112 @@ wasm_exec_env_get_module_inst(WASMExecEnv *exec_env) return exec_env->module_inst; } +void +wasm_exec_env_set_module_inst(WASMExecEnv *exec_env, + WASMModuleInstanceCommon *const module_inst) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_traverse_lock(exec_env); +#endif + exec_env->module_inst = module_inst; +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_traverse_unlock(exec_env); +#endif +} + +void +wasm_exec_env_restore_module_inst( + WASMExecEnv *exec_env, WASMModuleInstanceCommon *const module_inst_common) +{ + WASMModuleInstanceCommon *old_module_inst_common = exec_env->module_inst; + WASMModuleInstance *old_module_inst = + (WASMModuleInstance *)old_module_inst_common; + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_common; + char cur_exception[EXCEPTION_BUF_LEN]; + +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_traverse_lock(exec_env); +#endif + exec_env->module_inst = module_inst_common; + /* + * propagate an exception if any. + */ + exception_lock(old_module_inst); + if (old_module_inst->cur_exception[0] != '\0') { + bh_memcpy_s(cur_exception, sizeof(cur_exception), + old_module_inst->cur_exception, + sizeof(old_module_inst->cur_exception)); + } + else { + cur_exception[0] = '\0'; + } + exception_unlock(old_module_inst); +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_traverse_unlock(exec_env); +#endif + if (cur_exception[0] != '\0') { + exception_lock(module_inst); + bh_memcpy_s(module_inst->cur_exception, + sizeof(module_inst->cur_exception), cur_exception, + sizeof(cur_exception)); + exception_unlock(module_inst); + } +} + +void +wasm_exec_env_set_thread_info(WASMExecEnv *exec_env) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + os_mutex_lock(&exec_env->wait_lock); +#endif + exec_env->handle = os_self_thread(); + if (exec_env->user_native_stack_boundary) + /* WASM_STACK_GUARD_SIZE isn't added for flexibility to developer, + he must ensure that enough guard bytes are kept. */ + exec_env->native_stack_boundary = exec_env->user_native_stack_boundary; + else { + uint8 *stack_boundary = os_thread_get_stack_boundary(); + 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 +void * +wasm_exec_env_get_thread_arg(WASMExecEnv *exec_env) +{ + return exec_env->thread_arg; +} + +void +wasm_exec_env_set_thread_arg(WASMExecEnv *exec_env, void *thread_arg) +{ + exec_env->thread_arg = thread_arg; +} +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +void +wasm_exec_env_push_jmpbuf(WASMExecEnv *exec_env, WASMJmpBuf *jmpbuf) +{ + jmpbuf->prev = exec_env->jmpbuf_stack_top; + exec_env->jmpbuf_stack_top = jmpbuf; +} + +WASMJmpBuf * +wasm_exec_env_pop_jmpbuf(WASMExecEnv *exec_env) +{ + WASMJmpBuf *stack_top = exec_env->jmpbuf_stack_top; + + if (stack_top) { + exec_env->jmpbuf_stack_top = stack_top->prev; + return stack_top; + } + + return NULL; +} +#endif diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index 26c34d5a50..5d80312fb1 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -6,8 +6,11 @@ #ifndef _WASM_EXEC_ENV_H #define _WASM_EXEC_ENV_H -#include "bh_thread.h" #include "bh_assert.h" +#include "wasm_suspend_flags.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm.h" +#endif #ifdef __cplusplus extern "C" { @@ -16,27 +19,148 @@ extern "C" { struct WASMModuleInstanceCommon; struct WASMInterpFrame; +#if WASM_ENABLE_THREAD_MGR != 0 +typedef struct WASMCluster WASMCluster; +#if WASM_ENABLE_DEBUG_INTERP != 0 +typedef struct WASMCurrentEnvStatus WASMCurrentEnvStatus; +#endif +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +typedef struct WASMJmpBuf { + struct WASMJmpBuf *prev; + korp_jmpbuf jmpbuf; +} WASMJmpBuf; +#endif + /* Execution environment */ typedef struct WASMExecEnv { - /* Next thread's exec env of a WASM module instance. */ + /* Next thread's exec env of a WASM module instance. */ struct WASMExecEnv *next; - /* Previous thread's exec env of a WASM module instance. */ - struct WASMExecEnv *prev; + /* Current interpreter/AOT frame of current thread */ + struct WASMInterpFrame *cur_frame; + + /* Note: field module_inst, argv_buf, native_stack_boundary, + suspend_flags, aux_stack_boundary, aux_stack_bottom, and + native_symbol are used by AOTed code, don't change the + places of them */ /* The WASM module instance of current thread */ struct WASMModuleInstanceCommon *module_inst; - /* Current interpreter frame of current thread */ - struct WASMInterpFrame *cur_frame; +#if WASM_ENABLE_AOT != 0 + uint32 *argv_buf; +#endif + + /* The boundary of native stack. When runtime detects that native + frame may overrun this boundary, it throws stack overflow + exception. */ + uint8 *native_stack_boundary; + + /* Used to terminate or suspend current thread */ + WASMSuspendFlags suspend_flags; + + /* Auxiliary stack boundary */ + uintptr_t aux_stack_boundary; + + /* Auxiliary stack bottom */ + uintptr_t aux_stack_bottom; + +#if WASM_ENABLE_AOT != 0 + /* Native symbol list, reserved */ + void **native_symbol; +#endif + + /* + * The lowest stack pointer value observed. + * Assumption: native stack grows to the lower address. + */ + uint8 *native_stack_top_min; + + struct { + /* The top boundary of the stack. */ + uint8 *top_boundary; + /* The top to of the wasm stack which is free. */ + uint8 *top; + /* The bottom of the wasm stack. */ + uint8 *bottom; + } wasm_stack; + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + /* instructions to execute */ + int instructions_to_execute; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 + /** + * Cache for + * - jit native operations in 32-bit target which hasn't 64-bit + * int/float registers, mainly for the operations of double and int64, + * such as F64TOI64, F32TOI64, I64 MUL/REM, and so on. + * - SSE instructions. + **/ + uint64 jit_cache[2]; +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + /* thread return value */ + void *thread_ret_value; + + /* Must be provided by thread library */ + void *(*thread_start_routine)(void *); + void *thread_arg; + + /* pointer to the cluster */ + WASMCluster *cluster; + + /* used to support debugger */ + korp_mutex wait_lock; + korp_cond wait_cond; + /* the count of threads which are joining current thread */ + uint32 wait_count; + + /* whether current thread is detached */ + bool thread_is_detached; + + /* whether the aux stack is allocated */ + bool is_aux_stack_allocated; +#endif + +#if WASM_ENABLE_GC != 0 + /* Current local object reference variable */ + struct WASMLocalObjectRef *cur_local_object_ref; +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + WASMCurrentEnvStatus *current_status; +#endif + + /* attachment for native function */ + void *attachment; + + void *user_data; + + /* The boundary of native stack set by host embedder. It is used + if it is not NULL when calling wasm functions. */ + uint8 *user_native_stack_boundary; /* The native thread handle of current thread */ korp_tid handle; - /* The boundary of native stack. When interpreter detects that native - frame may overrun this boundary, it throws a stack overflow - exception. */ - void *native_stack_boundary; +#if WASM_ENABLE_INTERP != 0 && WASM_ENABLE_FAST_INTERP == 0 + BlockAddr block_addr_cache[BLOCK_ADDR_CACHE_SIZE][BLOCK_ADDR_CONFLICT_SIZE]; +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMJmpBuf *jmpbuf_stack_top; + /* One guard page for the exception check */ + uint8 *exce_check_guard_page; +#endif + +#if WASM_ENABLE_MEMORY_PROFILING != 0 + uint32 max_wasm_stack_used; +#endif /* The WASM stack size */ uint32 wasm_stack_size; @@ -44,19 +168,28 @@ typedef struct WASMExecEnv { /* The WASM stack of current thread */ union { uint64 __make_it_8_byte_aligned_; + /* The WASM stack. */ + uint8 bottom[1]; + } wasm_stack_u; +} WASMExecEnv; - struct { - /* The top boundary of the stack. */ - uint8 *top_boundary; +#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 - /* Top cell index which is free. */ - uint8 *top; +WASMExecEnv * +wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size); - /* The Java stack. */ - uint8 bottom[1]; - } s; - } wasm_stack; -} WASMExecEnv; +void +wasm_exec_env_destroy_internal(WASMExecEnv *exec_env); WASMExecEnv * wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, @@ -65,6 +198,12 @@ 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 != 0 || exec_env->aux_stack_bottom != 0; +} + /** * Allocate a WASM frame from the WASM stack. * @@ -77,29 +216,40 @@ wasm_exec_env_destroy(WASMExecEnv *exec_env); static inline void * wasm_exec_env_alloc_wasm_frame(WASMExecEnv *exec_env, unsigned size) { - uint8 *addr = exec_env->wasm_stack.s.top; + uint8 *addr = exec_env->wasm_stack.top; bh_assert(!(size & 3)); - /* The outs area size cannot be larger than the frame size, so - multiplying by 2 is enough. */ - if (addr + size * 2 > exec_env->wasm_stack.s.top_boundary) { + /* For classic interpreter, the outs area doesn't contain the const cells, + its size cannot be larger than the frame size, so here checking stack + overflow with multiplying by 2 is enough. For fast interpreter, since + the outs area contains const cells, its size may be larger than current + frame size, we should check again before putting the function arguments + into the outs area. */ + if (size * 2 + > (uint32)(uintptr_t)(exec_env->wasm_stack.top_boundary - addr)) { /* WASM stack overflow. */ - /* When throwing SOE, the preserved space must be enough. */ - /* bh_assert(!exec_env->throwing_soe);*/ return NULL; } - exec_env->wasm_stack.s.top += size; + exec_env->wasm_stack.top += size; +#if WASM_ENABLE_MEMORY_PROFILING != 0 + { + uint32 wasm_stack_used = + exec_env->wasm_stack.top - exec_env->wasm_stack.bottom; + if (wasm_stack_used > exec_env->max_wasm_stack_used) + exec_env->max_wasm_stack_used = wasm_stack_used; + } +#endif return addr; } static inline void wasm_exec_env_free_wasm_frame(WASMExecEnv *exec_env, void *prev_top) { - bh_assert((uint8 *)prev_top >= exec_env->wasm_stack.s.bottom); - exec_env->wasm_stack.s.top = (uint8 *)prev_top; + bh_assert((uint8 *)prev_top >= exec_env->wasm_stack.bottom); + exec_env->wasm_stack.top = (uint8 *)prev_top; } /** @@ -109,10 +259,10 @@ wasm_exec_env_free_wasm_frame(WASMExecEnv *exec_env, void *prev_top) * * @return the current WASM stack top pointer */ -static inline void* +static inline void * wasm_exec_env_wasm_stack_top(WASMExecEnv *exec_env) { - return exec_env->wasm_stack.s.top; + return exec_env->wasm_stack.top; } /** @@ -135,7 +285,7 @@ wasm_exec_env_set_cur_frame(WASMExecEnv *exec_env, * * @return the current frame pointer */ -static inline struct WASMInterpFrame* +static inline struct WASMInterpFrame * wasm_exec_env_get_cur_frame(WASMExecEnv *exec_env) { return exec_env->cur_frame; @@ -144,6 +294,33 @@ wasm_exec_env_get_cur_frame(WASMExecEnv *exec_env) struct WASMModuleInstanceCommon * wasm_exec_env_get_module_inst(WASMExecEnv *exec_env); +void +wasm_exec_env_set_module_inst( + WASMExecEnv *exec_env, struct WASMModuleInstanceCommon *const module_inst); + +void +wasm_exec_env_restore_module_inst( + WASMExecEnv *exec_env, struct WASMModuleInstanceCommon *const module_inst); + +void +wasm_exec_env_set_thread_info(WASMExecEnv *exec_env); + +#if WASM_ENABLE_THREAD_MGR != 0 +void * +wasm_exec_env_get_thread_arg(WASMExecEnv *exec_env); + +void +wasm_exec_env_set_thread_arg(WASMExecEnv *exec_env, void *thread_arg); +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +void +wasm_exec_env_push_jmpbuf(WASMExecEnv *exec_env, WASMJmpBuf *jmpbuf); + +WASMJmpBuf * +wasm_exec_env_pop_jmpbuf(WASMExecEnv *exec_env); +#endif + #ifdef __cplusplus } #endif diff --git a/core/iwasm/common/wasm_loader_common.c b/core/iwasm/common/wasm_loader_common.c new file mode 100644 index 0000000000..397144e8e2 --- /dev/null +++ b/core/iwasm/common/wasm_loader_common.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2024 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "wasm_loader_common.h" +#include "bh_leb128.h" +#include "bh_log.h" +#if WASM_ENABLE_GC != 0 +#include "../common/gc/gc_type.h" +#endif + +void +wasm_loader_set_error_buf(char *error_buf, uint32 error_buf_size, + const char *string, bool is_aot) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, "%s module load failed: %s", + is_aot ? "AOT" : "WASM", string); + } +} + +#if WASM_ENABLE_MEMORY64 != 0 +bool +check_memory64_flags_consistency(WASMModule *module, char *error_buf, + uint32 error_buf_size, bool is_aot) +{ + uint32 i; + bool wasm64_flag, all_wasm64 = true, none_wasm64 = true; + + for (i = 0; i < module->import_memory_count; ++i) { + wasm64_flag = + module->import_memories[i].u.memory.mem_type.flags & MEMORY64_FLAG; + all_wasm64 &= wasm64_flag; + none_wasm64 &= !wasm64_flag; + } + + for (i = 0; i < module->memory_count; ++i) { + wasm64_flag = module->memories[i].flags & MEMORY64_FLAG; + all_wasm64 &= wasm64_flag; + none_wasm64 &= !wasm64_flag; + } + + if (!(all_wasm64 || none_wasm64)) { + wasm_loader_set_error_buf( + error_buf, error_buf_size, + "inconsistent limits wasm64 flags for memory sections", is_aot); + return false; + } + return true; +} +#endif + +bool +wasm_memory_check_flags(const uint8 mem_flag, char *error_buf, + uint32 error_buf_size, bool is_aot) +{ + /* Check whether certain features indicated by mem_flag are enabled in + * runtime */ + if (mem_flag > MAX_PAGE_COUNT_FLAG) { +#if WASM_ENABLE_SHARED_MEMORY == 0 + if (mem_flag & SHARED_MEMORY_FLAG) { + LOG_VERBOSE("shared memory flag was found, please enable shared " + "memory, lib-pthread or lib-wasi-threads"); + wasm_loader_set_error_buf(error_buf, error_buf_size, + "invalid limits flags", is_aot); + return false; + } +#endif +#if WASM_ENABLE_MEMORY64 == 0 + if (mem_flag & MEMORY64_FLAG) { + LOG_VERBOSE("memory64 flag was found, please enable memory64"); + wasm_loader_set_error_buf(error_buf, error_buf_size, + "invalid limits flags", is_aot); + return false; + } +#endif + } + + if (mem_flag > MAX_PAGE_COUNT_FLAG + SHARED_MEMORY_FLAG + MEMORY64_FLAG) { + wasm_loader_set_error_buf(error_buf, error_buf_size, + "invalid limits flags", is_aot); + return false; + } + else if ((mem_flag & SHARED_MEMORY_FLAG) + && !(mem_flag & MAX_PAGE_COUNT_FLAG)) { + wasm_loader_set_error_buf(error_buf, error_buf_size, + "shared memory must have maximum", is_aot); + return false; + } + + return true; +} + +bool +wasm_table_check_flags(const uint8 table_flag, char *error_buf, + uint32 error_buf_size, bool is_aot) +{ + /* Check whether certain features indicated by mem_flag are enabled in + * runtime */ + if (table_flag > MAX_TABLE_SIZE_FLAG) { + if (table_flag & SHARED_TABLE_FLAG) { + wasm_loader_set_error_buf(error_buf, error_buf_size, + "tables cannot be shared", is_aot); + } +#if WASM_ENABLE_MEMORY64 == 0 + if (table_flag & TABLE64_FLAG) { + wasm_loader_set_error_buf(error_buf, error_buf_size, + "invalid limits flags(table64 flag was " + "found, please enable memory64)", + is_aot); + return false; + } +#endif + } + + if (table_flag > MAX_TABLE_SIZE_FLAG + TABLE64_FLAG) { + wasm_loader_set_error_buf(error_buf, error_buf_size, + "invalid limits flags", is_aot); + return false; + } + + return true; +} + +/* + * compare with a bigger type set in `wasm_value_type_size_internal()`, + * this function will only cover global value type, function's param + * value type and function's result value type. + * + * please feel free to add more if there are more requirements + */ +bool +is_valid_value_type(uint8 type) +{ + if (/* I32/I64/F32/F64, 0x7C to 0x7F */ + (type >= VALUE_TYPE_F64 && type <= VALUE_TYPE_I32) +#if WASM_ENABLE_GC != 0 + /* reference types, 0x65 to 0x70 */ + || wasm_is_type_reftype(type) +#elif WASM_ENABLE_REF_TYPES != 0 + || (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF) +#endif +#if WASM_ENABLE_SIMD != 0 + || type == VALUE_TYPE_V128 /* 0x7B */ +#endif + ) + return true; + return false; +} + +bool +is_valid_value_type_for_interpreter(uint8 value_type) +{ +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_SIMDE == 0) + /* + * Note: regardless of WASM_ENABLE_SIMD, our classic interpreters don't + * have SIMD implemented. + * + * WASM_ENABLE_SIMDE is used to control SIMD feaure in fast interpreter + */ + if (value_type == VALUE_TYPE_V128) + return false; +#endif + return is_valid_value_type(value_type); +} + +bool +is_valid_func_type(const WASMFuncType *func_type) +{ + unsigned i; + for (i = 0; + i < (unsigned)(func_type->param_count + func_type->result_count); + i++) { + if (!is_valid_value_type(func_type->types[i])) + return false; + } + + return true; +} + +bool +is_valid_packed_type(uint8 packed_type) +{ + return packed_type == PACKED_TYPE_I8 || packed_type == PACKED_TYPE_I16; +} + +bool +is_valid_field_type(uint8 field_type) +{ + if (is_valid_value_type(field_type) || is_valid_packed_type(field_type)) + return true; + return false; +} + +/* + * Indices are represented as a u32. + */ +bool +is_indices_overflow(uint32 import, uint32 other, char *error_buf, + uint32 error_buf_size) +{ + if (import > UINT32_MAX - other) { + snprintf(error_buf, error_buf_size, + "too many items in the index space(%" PRIu32 "+%" PRIu32 ").", + import, other); + return true; + } + + return false; +} + +bool +read_leb(uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, bool sign, + uint64 *p_result, char *error_buf, uint32 error_buf_size) +{ + size_t offset = 0; + bh_leb_read_status_t status = + bh_leb_read(*p_buf, buf_end, maxbits, sign, p_result, &offset); + + switch (status) { + case BH_LEB_READ_SUCCESS: + *p_buf += offset; + return true; + case BH_LEB_READ_TOO_LONG: + wasm_loader_set_error_buf(error_buf, error_buf_size, + "integer representation too long", false); + return false; + case BH_LEB_READ_OVERFLOW: + wasm_loader_set_error_buf(error_buf, error_buf_size, + "integer too large", false); + return false; + case BH_LEB_READ_UNEXPECTED_END: + wasm_loader_set_error_buf(error_buf, error_buf_size, + "unexpected end", false); + return false; + default: + bh_assert(false); + return false; + } +} + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 +void +destroy_init_expr_recursive(InitializerExpression *expr) +{ + if (expr == NULL) { + return; + } + if (is_expr_binary_op(expr->init_expr_type)) { + destroy_init_expr_recursive(expr->u.binary.l_expr); + destroy_init_expr_recursive(expr->u.binary.r_expr); + } + wasm_runtime_free(expr); +} +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */ diff --git a/core/iwasm/common/wasm_loader_common.h b/core/iwasm/common/wasm_loader_common.h new file mode 100644 index 0000000000..1c4c393468 --- /dev/null +++ b/core/iwasm/common/wasm_loader_common.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_LOADER_COMMON_H +#define _WASM_LOADER_COMMON_H + +#include "platform_common.h" +#include "../interpreter/wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if WASM_ENABLE_MEMORY64 != 0 +/* check consistency of memory64 flags across all memories, + * they must be either all wasm64 or all wasm32 */ +bool +check_memory64_flags_consistency(WASMModule *module, char *error_buf, + uint32 error_buf_size, bool is_aot); +#endif + +bool +wasm_memory_check_flags(const uint8 mem_flag, char *error_buf, + uint32 error_buf_size, bool is_aot); + +bool +wasm_table_check_flags(const uint8 table_flag, char *error_buf, + uint32 error_buf_size, bool is_aot); + +bool +is_valid_value_type(uint8 value_tpye); + +bool +is_valid_value_type_for_interpreter(uint8 value_tpye); + +bool +is_valid_func_type(const WASMFuncType *func_type); + +bool +is_valid_packed_type(uint8 packed_type); + +bool +is_valid_field_type(uint8 field_type); + +bool +is_indices_overflow(uint32 import, uint32 other, char *error_buf, + uint32 error_buf_size); + +bool +read_leb(uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, bool sign, + uint64 *p_result, char *error_buf, uint32 error_buf_size); + +void +wasm_loader_set_error_buf(char *error_buf, uint32 error_buf_size, + const char *string, bool is_aot); + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 +void +destroy_init_expr_recursive(InitializerExpression *expr); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_LOADER_COMMON_H */ diff --git a/core/iwasm/common/wasm_memory.c b/core/iwasm/common/wasm_memory.c new file mode 100644 index 0000000000..3d1f148118 --- /dev/null +++ b/core/iwasm/common/wasm_memory.c @@ -0,0 +1,2120 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_runtime_common.h" +#include "../interpreter/wasm_runtime.h" +#include "../aot/aot_runtime.h" +#include "mem_alloc.h" +#include "wasm_memory.h" + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif + +typedef enum Memory_Mode { + MEMORY_MODE_UNKNOWN = 0, + MEMORY_MODE_POOL, + MEMORY_MODE_ALLOCATOR, + MEMORY_MODE_SYSTEM_ALLOCATOR +} Memory_Mode; + +static Memory_Mode memory_mode = MEMORY_MODE_UNKNOWN; + +static mem_allocator_t pool_allocator = NULL; + +#if WASM_ENABLE_SHARED_HEAP != 0 +static WASMSharedHeap *shared_heap_list = NULL; +static korp_mutex shared_heap_list_lock; +#endif + +static enlarge_memory_error_callback_t enlarge_memory_error_cb; +static void *enlarge_memory_error_user_data; + +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 +static void *allocator_user_data = NULL; +#endif + +static void *(*malloc_func)( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + unsigned int size) = NULL; + +static void *(*realloc_func)( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, bool full_size_mmaped, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr, unsigned int size) = NULL; + +static void (*free_func)( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr) = NULL; + +static unsigned int global_pool_size; + +static uint64 +align_as_and_cast(uint64 size, uint64 alignment) +{ + uint64 aligned_size = (size + alignment - 1) & ~(alignment - 1); + + return aligned_size; +} + +static bool +wasm_memory_init_with_pool(void *mem, unsigned int bytes) +{ + mem_allocator_t allocator = mem_allocator_create(mem, bytes); + + if (allocator) { + memory_mode = MEMORY_MODE_POOL; + pool_allocator = allocator; + global_pool_size = bytes; + return true; + } + LOG_ERROR("Init memory with pool (%p, %u) failed.\n", mem, bytes); + return false; +} + +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 +static bool +wasm_memory_init_with_allocator(void *_user_data, void *_malloc_func, + void *_realloc_func, void *_free_func) +{ + if (_malloc_func && _free_func && _malloc_func != _free_func) { + memory_mode = MEMORY_MODE_ALLOCATOR; + allocator_user_data = _user_data; + malloc_func = _malloc_func; + realloc_func = _realloc_func; + free_func = _free_func; + return true; + } + LOG_ERROR("Init memory with allocator (%p, %p, %p, %p) failed.\n", + _user_data, _malloc_func, _realloc_func, _free_func); + return false; +} +#else +static bool +wasm_memory_init_with_allocator(void *malloc_func_ptr, void *realloc_func_ptr, + void *free_func_ptr) +{ + if (malloc_func_ptr && free_func_ptr && malloc_func_ptr != free_func_ptr) { + memory_mode = MEMORY_MODE_ALLOCATOR; + malloc_func = malloc_func_ptr; + realloc_func = realloc_func_ptr; + free_func = free_func_ptr; + return true; + } + LOG_ERROR("Init memory with allocator (%p, %p, %p) failed.\n", + malloc_func_ptr, realloc_func_ptr, free_func_ptr); + return false; +} +#endif + +static inline bool +is_bounds_checks_enabled(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + if (!module_inst) { + return true; + } + + return wasm_runtime_is_bounds_checks_enabled(module_inst); +#else + return true; +#endif +} + +#if WASM_ENABLE_SHARED_HEAP != 0 +static void * +wasm_mmap_linear_memory(uint64 map_size, uint64 commit_size); +static void +wasm_munmap_linear_memory(void *mapped_mem, uint64 commit_size, + uint64 map_size); + +static void * +runtime_malloc(uint64 size) +{ + void *mem; + + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + LOG_WARNING("Allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static void +destroy_runtime_managed_shared_heap(WASMSharedHeap *heap) +{ + uint64 map_size; + + mem_allocator_destroy(heap->heap_handle); + wasm_runtime_free(heap->heap_handle); + heap->heap_handle = NULL; + +#ifndef OS_ENABLE_HW_BOUND_CHECK + map_size = heap->size; +#else + map_size = 8 * (uint64)BH_GB; +#endif + wasm_munmap_linear_memory(heap->base_addr, heap->size, map_size); + heap->base_addr = NULL; +} + +static bool +create_runtime_managed_shared_heap(WASMSharedHeap *heap, + uint64 heap_struct_size) +{ + uint64 map_size; + + heap->heap_handle = runtime_malloc(mem_allocator_get_heap_struct_size()); + if (!heap->heap_handle) { + heap->base_addr = NULL; + return false; + } + +#ifndef OS_ENABLE_HW_BOUND_CHECK + map_size = heap->size; +#else + /* Totally 8G is mapped, the opcode load/store address range is 0 to 8G: + * ea = i + memarg.offset + * both i and memarg.offset are u32 in range 0 to 4G + * so the range of ea is 0 to 8G + */ + map_size = 8 * (uint64)BH_GB; +#endif + + if (!(heap->base_addr = wasm_mmap_linear_memory(map_size, heap->size))) { + goto fail1; + } + if (!mem_allocator_create_with_struct_and_pool( + heap->heap_handle, heap_struct_size, heap->base_addr, heap->size)) { + LOG_WARNING("init share heap failed"); + goto fail2; + } + + LOG_VERBOSE("Create runtime managed shared heap %p with size %u", + heap->base_addr, (uint32)heap->size); + return true; + +fail2: + wasm_munmap_linear_memory(heap->base_addr, heap->size, map_size); +fail1: + wasm_runtime_free(heap->heap_handle); + heap->heap_handle = NULL; + heap->base_addr = NULL; + return false; +} + +WASMSharedHeap * +wasm_runtime_create_shared_heap(SharedHeapInitArgs *init_args) +{ + uint64 heap_struct_size = sizeof(WASMSharedHeap); + uint32 size = init_args->size; + WASMSharedHeap *heap; + + if (size == 0) { + goto fail1; + } + + if (!(heap = runtime_malloc(heap_struct_size))) { + goto fail1; + } + + size = align_uint(size, os_getpagesize()); + if (size != init_args->size) { + LOG_WARNING("Shared heap size aligned from %u to %u", init_args->size, + size); + } + + if (size > APP_HEAP_SIZE_MAX || size < APP_HEAP_SIZE_MIN) { + LOG_WARNING("Invalid size of shared heap"); + goto fail2; + } + + heap->size = size; + heap->start_off_mem64 = UINT64_MAX - heap->size + 1; + heap->start_off_mem32 = UINT32_MAX - heap->size + 1; + heap->attached_count = 0; + + if (init_args->pre_allocated_addr != NULL) { + /* Create shared heap from a pre allocated buffer, its size need to + * align with system page */ + if (size != init_args->size) { + LOG_WARNING("Pre allocated size need to be aligned with system " + "page size to create shared heap"); + goto fail2; + } + + heap->heap_handle = NULL; + heap->base_addr = init_args->pre_allocated_addr; + LOG_VERBOSE("Create preallocated shared heap %p with size %u", + heap->base_addr, size); + } + else { + if (!create_runtime_managed_shared_heap(heap, heap_struct_size)) { + goto fail2; + } + } + + os_mutex_lock(&shared_heap_list_lock); + if (shared_heap_list == NULL) { + shared_heap_list = heap; + } + else { + heap->next = shared_heap_list; + shared_heap_list = heap; + } + os_mutex_unlock(&shared_heap_list_lock); + return heap; + +fail2: + wasm_runtime_free(heap); +fail1: + return NULL; +} + +WASMSharedHeap * +wasm_runtime_chain_shared_heaps(WASMSharedHeap *head, WASMSharedHeap *body) +{ + WASMSharedHeap *cur; + bool heap_handle_exist = false; + + if (!head || !body) { + LOG_WARNING("Invalid shared heap to chain."); + return NULL; + } + heap_handle_exist = head->heap_handle != NULL; + + os_mutex_lock(&shared_heap_list_lock); + if (head->attached_count != 0 || body->attached_count != 0) { + LOG_WARNING("To create shared heap chain, all shared heap need to be " + "detached first."); + os_mutex_unlock(&shared_heap_list_lock); + return NULL; + } + for (cur = shared_heap_list; cur; cur = cur->next) { + if (cur->chain_next == body || cur->chain_next == head) { + LOG_WARNING( + "To create shared heap chain, both the 'head' and 'body' " + "shared heap can't already be the 'body' in another a chain"); + os_mutex_unlock(&shared_heap_list_lock); + return NULL; + } + if (cur == head && cur->chain_next) { + LOG_WARNING( + "To create shared heap chain, the 'head' shared heap can't " + "already be the 'head' in another a chain"); + os_mutex_unlock(&shared_heap_list_lock); + return NULL; + } + } + for (cur = body; cur; cur = cur->chain_next) { + if (cur->heap_handle && heap_handle_exist) { + LOG_WARNING( + "To create shared heap chain, only one of shared heap can " + "dynamically shared_heap_malloc and shared_heap_free, the rest " + "can only be pre-allocated shared heap"); + os_mutex_unlock(&shared_heap_list_lock); + return NULL; + } + if (cur->heap_handle) + heap_handle_exist = true; + } + + head->start_off_mem64 = body->start_off_mem64 - head->size; + head->start_off_mem32 = body->start_off_mem32 - head->size; + head->chain_next = body; + os_mutex_unlock(&shared_heap_list_lock); + return head; +} + +WASMSharedHeap * +wasm_runtime_unchain_shared_heaps(WASMSharedHeap *head, bool entire_chain) +{ + WASMSharedHeap *cur, *tmp; + + if (!head || !head->chain_next) { + LOG_WARNING("Invalid shared heap chain to disconnect the head from."); + return NULL; + } + + os_mutex_lock(&shared_heap_list_lock); + if (head->attached_count != 0) { + LOG_WARNING("To disconnect the shared heap head from the shared heap " + "chain, the shared heap chain needs to be detached first."); + os_mutex_unlock(&shared_heap_list_lock); + return NULL; + } + + cur = head; + while (cur && cur->chain_next) { + cur->start_off_mem64 = UINT64_MAX - cur->size + 1; + cur->start_off_mem32 = UINT32_MAX - cur->size + 1; + tmp = cur; + cur = cur->chain_next; + tmp->chain_next = NULL; + if (!entire_chain) + break; + } + os_mutex_unlock(&shared_heap_list_lock); + return cur; +} + +bool +wasm_runtime_reset_shared_heap_chain(WASMSharedHeap *shared_heap) +{ + uint64 heap_struct_size = sizeof(WASMSharedHeap); + WASMSharedHeap *cur; + + if (!shared_heap) { + return false; + } + + os_mutex_lock(&shared_heap_list_lock); + if (shared_heap->attached_count != 0) { + os_mutex_unlock(&shared_heap_list_lock); + return false; + } + + for (cur = shared_heap; cur; cur = cur->chain_next) { + if (cur->heap_handle) { + destroy_runtime_managed_shared_heap(cur); + + if (!create_runtime_managed_shared_heap(cur, heap_struct_size)) { + os_mutex_unlock(&shared_heap_list_lock); + return false; + } + } + else { + memset(cur->base_addr, 0, (size_t)cur->size); + } + } + + os_mutex_unlock(&shared_heap_list_lock); + return true; +} + +static uint8 * +get_last_used_shared_heap_base_addr_adj(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e; + return e->shared_heap_base_addr_adj; + } +#endif /* end of WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + return e->shared_heap_base_addr_adj; + } +#endif /* end of WASM_ENABLE_AOT != 0 */ + return 0; +} + +static uintptr_t +get_last_used_shared_heap_start_offset(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e; +#if UINTPTR_MAX == UINT64_MAX + return e->shared_heap_start_off.u64; +#else + return e->shared_heap_start_off.u32[0]; +#endif + } +#endif /* end of WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; +#if UINTPTR_MAX == UINT64_MAX + return e->shared_heap_start_off.u64; +#else + return e->shared_heap_start_off.u32[0]; +#endif + } +#endif /* end of WASM_ENABLE_AOT != 0 */ + return 0; +} + +static uintptr_t +get_last_used_shared_heap_end_offset(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e; +#if UINTPTR_MAX == UINT64_MAX + return e->shared_heap_end_off.u64; +#else + return e->shared_heap_end_off.u32[0]; +#endif + } +#endif /* end of WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; +#if UINTPTR_MAX == UINT64_MAX + return e->shared_heap_end_off.u64; +#else + return e->shared_heap_end_off.u32[0]; +#endif + } +#endif /* end of WASM_ENABLE_AOT != 0 */ + return 0; +} + +static void +update_last_used_shared_heap(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *shared_heap, bool is_memory64) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e; +#if UINTPTR_MAX == UINT64_MAX + if (is_memory64) + e->shared_heap_start_off.u64 = shared_heap->start_off_mem64; + else + e->shared_heap_start_off.u64 = shared_heap->start_off_mem32; + e->shared_heap_end_off.u64 = + e->shared_heap_start_off.u64 - 1 + shared_heap->size; + e->shared_heap_base_addr_adj = + shared_heap->base_addr - e->shared_heap_start_off.u64; +#else + e->shared_heap_start_off.u32[0] = (uint32)shared_heap->start_off_mem32; + e->shared_heap_end_off.u32[0] = + e->shared_heap_start_off.u32[0] - 1 + shared_heap->size; + e->shared_heap_base_addr_adj = + shared_heap->base_addr - e->shared_heap_start_off.u32[0]; +#endif + } +#endif /* end of WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; +#if UINTPTR_MAX == UINT64_MAX + if (is_memory64) + e->shared_heap_start_off.u64 = shared_heap->start_off_mem64; + else + e->shared_heap_start_off.u64 = shared_heap->start_off_mem32; + e->shared_heap_end_off.u64 = + e->shared_heap_start_off.u64 - 1 + shared_heap->size; + e->shared_heap_base_addr_adj = + shared_heap->base_addr - e->shared_heap_start_off.u64; +#else + e->shared_heap_start_off.u32[0] = (uint32)shared_heap->start_off_mem32; + e->shared_heap_end_off.u32[0] = + e->shared_heap_start_off.u32[0] - 1 + shared_heap->size; + e->shared_heap_base_addr_adj = + shared_heap->base_addr - e->shared_heap_start_off.u32[0]; +#endif + } +#endif /* end of WASM_ENABLE_AOT != 0 */ +} + +bool +wasm_runtime_attach_shared_heap_internal(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *shared_heap) +{ + WASMMemoryInstance *memory = + wasm_get_default_memory((WASMModuleInstance *)module_inst); + uint64 linear_mem_size; + + if (!memory) + return false; + + linear_mem_size = memory->memory_data_size; + + /* check if linear memory and shared heap are overlapped */ + if ((memory->is_memory64 && linear_mem_size > shared_heap->start_off_mem64) + || (!memory->is_memory64 + && linear_mem_size > shared_heap->start_off_mem32)) { + LOG_WARNING("Linear memory address is overlapped with shared heap"); + return false; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e; + if (e->shared_heap) { + LOG_WARNING("A shared heap is already attached"); + return false; + } + e->shared_heap = shared_heap; + } +#endif /* end of WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + if (e->shared_heap) { + LOG_WARNING("A shared heap is already attached"); + return false; + } + e->shared_heap = shared_heap; + } +#endif /* end of WASM_ENABLE_AOT != 0 */ + update_last_used_shared_heap(module_inst, shared_heap, memory->is_memory64); + + os_mutex_lock(&shared_heap_list_lock); + shared_heap->attached_count++; + os_mutex_unlock(&shared_heap_list_lock); + LOG_VERBOSE("Shared heap %p is attached to module instance %p", shared_heap, + module_inst); + return true; +} + +bool +wasm_runtime_attach_shared_heap(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *shared_heap) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + return wasm_cluster_attach_shared_heap(module_inst, shared_heap); +#else + return wasm_runtime_attach_shared_heap_internal(module_inst, shared_heap); +#endif +} + +void +wasm_runtime_detach_shared_heap_internal(WASMModuleInstanceCommon *module_inst) +{ + /* Reset shared_heap_end_off = UINT64/32_MAX - 1 to handling a corner case, + app_offset >= shared_heap_start && app_offset <= shared_heap_end-bytes+1 + when bytes=1 and both e->shared_heap_start_off and e->shared_heap_end_off + is 0xffffffff */ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e; + if (e->shared_heap != NULL) { + os_mutex_lock(&shared_heap_list_lock); + e->shared_heap->attached_count--; + os_mutex_unlock(&shared_heap_list_lock); + } + e->shared_heap = NULL; +#if UINTPTR_MAX == UINT64_MAX + e->shared_heap_start_off.u64 = UINT64_MAX; + e->shared_heap_end_off.u64 = UINT64_MAX - 1; +#else + e->shared_heap_start_off.u32[0] = UINT32_MAX; + e->shared_heap_end_off.u32[0] = UINT32_MAX - 1; +#endif + e->shared_heap_base_addr_adj = NULL; + } +#endif /* end of WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + if (e->shared_heap != NULL) { + os_mutex_lock(&shared_heap_list_lock); + e->shared_heap->attached_count--; + os_mutex_unlock(&shared_heap_list_lock); + } + e->shared_heap = NULL; +#if UINTPTR_MAX == UINT64_MAX + e->shared_heap_start_off.u64 = UINT64_MAX; + e->shared_heap_end_off.u64 = UINT64_MAX - 1; +#else + e->shared_heap_start_off.u32[0] = UINT32_MAX; + e->shared_heap_end_off.u32[0] = UINT32_MAX - 1; +#endif + e->shared_heap_base_addr_adj = NULL; + } +#endif /* end of WASM_ENABLE_AOT != 0 */ + LOG_VERBOSE("Shared heap is detached from module instance %p", module_inst); +} + +void +wasm_runtime_detach_shared_heap(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_detach_shared_heap(module_inst); +#else + wasm_runtime_detach_shared_heap_internal(module_inst); +#endif +} + +static WASMSharedHeap * +get_shared_heap(WASMModuleInstanceCommon *module_inst_comm) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst_comm->module_type == Wasm_Module_Bytecode) { + return ((WASMModuleInstance *)module_inst_comm)->e->shared_heap; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst_comm->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst_comm) + ->e; + return e->shared_heap; + } +#endif + return NULL; +} + +WASMSharedHeap * +wasm_runtime_get_shared_heap(WASMModuleInstanceCommon *module_inst_comm) +{ + return get_shared_heap(module_inst_comm); +} + +bool +is_app_addr_in_shared_heap(WASMModuleInstanceCommon *module_inst, + bool is_memory64, uint64 app_offset, uint64 bytes) +{ + WASMSharedHeap *heap = get_shared_heap(module_inst), *cur; + uint64 shared_heap_start, shared_heap_end; + + if (!heap || bytes > APP_HEAP_SIZE_MAX) { + goto fail; + } + + if (bytes == 0) { + bytes = 1; + } + + shared_heap_start = + (uint64)get_last_used_shared_heap_start_offset(module_inst); + shared_heap_end = (uint64)get_last_used_shared_heap_end_offset(module_inst); + if (bytes - 1 <= shared_heap_end && app_offset >= shared_heap_start + && app_offset <= shared_heap_end - bytes + 1) { + return true; + } + + /* Early stop for app start address not in the shared heap(chain) at all */ + shared_heap_start = + is_memory64 ? heap->start_off_mem64 : heap->start_off_mem32; + shared_heap_end = is_memory64 ? UINT64_MAX : UINT32_MAX; + if (bytes - 1 > shared_heap_end || app_offset < shared_heap_start + || app_offset > shared_heap_end - bytes + 1) { + goto fail; + } + + /* Find the exact shared heap that app addr is in, and update last used + * shared heap info in module inst extra */ + for (cur = heap; cur; cur = cur->chain_next) { + shared_heap_start = + is_memory64 ? cur->start_off_mem64 : cur->start_off_mem32; + shared_heap_end = shared_heap_start - 1 + cur->size; + if (bytes - 1 <= shared_heap_end && app_offset >= shared_heap_start + && app_offset <= shared_heap_end - bytes + 1) { + update_last_used_shared_heap(module_inst, cur, is_memory64); + return true; + } + } + +fail: + return false; +} + +static bool +is_native_addr_in_shared_heap(WASMModuleInstanceCommon *module_inst, + bool is_memory64, uint8 *addr, uint64 bytes) +{ + WASMSharedHeap *cur, *heap = get_shared_heap(module_inst); + uintptr_t base_addr, addr_int, end_addr; + + if (!heap || bytes > APP_HEAP_SIZE_MAX) { + goto fail; + } + + /* Iterate through shared heap chain to find whether native addr in one of + * shared heap */ + for (cur = heap; cur != NULL; cur = cur->chain_next) { + base_addr = (uintptr_t)cur->base_addr; + addr_int = (uintptr_t)addr; + if (addr_int < base_addr) + continue; + + end_addr = addr_int + bytes; + /* Check for overflow */ + if (end_addr <= addr_int) + continue; + + if (end_addr > base_addr + cur->size) + continue; + + update_last_used_shared_heap(module_inst, cur, is_memory64); + return true; + } + +fail: + return false; +} + +uint64 +wasm_runtime_shared_heap_malloc(WASMModuleInstanceCommon *module_inst, + uint64 size, void **p_native_addr) +{ + WASMMemoryInstance *memory = + wasm_get_default_memory((WASMModuleInstance *)module_inst); + WASMSharedHeap *shared_heap = get_shared_heap(module_inst); + void *native_addr = NULL; + + if (!memory || !shared_heap) + return 0; + + while (shared_heap && !shared_heap->heap_handle) { + shared_heap = shared_heap->chain_next; + } + if (!shared_heap) { + LOG_WARNING("Can't allocate from pre allocated shared heap"); + return 0; + } + + native_addr = mem_allocator_malloc(shared_heap->heap_handle, size); + if (!native_addr) + return 0; + + if (p_native_addr) { + *p_native_addr = native_addr; + } + + return (memory->is_memory64 ? shared_heap->start_off_mem64 + : shared_heap->start_off_mem32) + + (uintptr_t)((uint8 *)native_addr - shared_heap->base_addr); +} + +void +wasm_runtime_shared_heap_free(WASMModuleInstanceCommon *module_inst, uint64 ptr) +{ + WASMMemoryInstance *memory = + wasm_get_default_memory((WASMModuleInstance *)module_inst); + WASMSharedHeap *shared_heap = get_shared_heap(module_inst); + uint8 *addr = NULL; + + if (!memory || !shared_heap) { + return; + } + + while (shared_heap && !shared_heap->heap_handle) { + shared_heap = shared_heap->chain_next; + } + if (!shared_heap) { + LOG_WARNING("The address to free is from pre allocated shared heap"); + return; + } + + if (memory->is_memory64) { + if (ptr < shared_heap->start_off_mem64) { /* ptr can not > UINT64_MAX */ + LOG_WARNING("The address to free isn't in shared heap"); + return; + } + addr = shared_heap->base_addr + (ptr - shared_heap->start_off_mem64); + } + else { + if (ptr < shared_heap->start_off_mem32 || ptr > UINT32_MAX) { + LOG_WARNING("The address to free isn't in shared heap"); + return; + } + addr = shared_heap->base_addr + (ptr - shared_heap->start_off_mem32); + } + + mem_allocator_free(shared_heap->heap_handle, addr); +} +#endif /* end of WASM_ENABLE_SHARED_HEAP != 0 */ + +bool +wasm_runtime_memory_init(mem_alloc_type_t mem_alloc_type, + const MemAllocOption *alloc_option) +{ + bool ret = false; + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (os_mutex_init(&shared_heap_list_lock)) { + return false; + } +#endif + + if (mem_alloc_type == Alloc_With_Pool) { + ret = wasm_memory_init_with_pool(alloc_option->pool.heap_buf, + alloc_option->pool.heap_size); + } + else if (mem_alloc_type == Alloc_With_Allocator) { + ret = wasm_memory_init_with_allocator( +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + alloc_option->allocator.user_data, +#endif + alloc_option->allocator.malloc_func, + alloc_option->allocator.realloc_func, + alloc_option->allocator.free_func); + } + else if (mem_alloc_type == Alloc_With_System_Allocator) { + memory_mode = MEMORY_MODE_SYSTEM_ALLOCATOR; + ret = true; + } + else { + ret = false; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (!ret) { + os_mutex_destroy(&shared_heap_list_lock); + } +#endif + + return ret; +} + +#if WASM_ENABLE_SHARED_HEAP != 0 +static void +destroy_shared_heaps() +{ + WASMSharedHeap *heap; + WASMSharedHeap *cur; + uint64 map_size; + + os_mutex_lock(&shared_heap_list_lock); + heap = shared_heap_list; + shared_heap_list = NULL; + os_mutex_unlock(&shared_heap_list_lock); + + while (heap) { + cur = heap; + heap = heap->next; + if (cur->heap_handle) { + destroy_runtime_managed_shared_heap(cur); + } + wasm_runtime_free(cur); + } + os_mutex_destroy(&shared_heap_list_lock); +} +#endif + +void +wasm_runtime_memory_destroy(void) +{ +#if WASM_ENABLE_SHARED_HEAP != 0 + destroy_shared_heaps(); +#endif + + if (memory_mode == MEMORY_MODE_POOL) { +#if BH_ENABLE_GC_VERIFY == 0 + (void)mem_allocator_destroy(pool_allocator); +#else + int ret = mem_allocator_destroy(pool_allocator); + if (ret != 0) { + /* Memory leak detected */ + exit(-1); + } +#endif + } + memory_mode = MEMORY_MODE_UNKNOWN; +} + +unsigned +wasm_runtime_memory_pool_size(void) +{ + if (memory_mode == MEMORY_MODE_POOL) + return global_pool_size; + else + return UINT32_MAX; +} + +static inline void * +wasm_runtime_malloc_internal(unsigned int size) +{ + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_WARNING( + "wasm_runtime_malloc failed: memory hasn't been initialized.\n"); + return NULL; + } + else if (memory_mode == MEMORY_MODE_POOL) { + return mem_allocator_malloc(pool_allocator, size); + } + else if (memory_mode == MEMORY_MODE_ALLOCATOR) { + return malloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + Alloc_For_Runtime, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + size); + } + else { + return os_malloc(size); + } +} + +static inline void * +wasm_runtime_realloc_internal(void *ptr, unsigned int size) +{ + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_WARNING( + "wasm_runtime_realloc failed: memory hasn't been initialized.\n"); + return NULL; + } + else if (memory_mode == MEMORY_MODE_POOL) { + return mem_allocator_realloc(pool_allocator, ptr, size); + } + else if (memory_mode == MEMORY_MODE_ALLOCATOR) { + if (realloc_func) + return realloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + Alloc_For_Runtime, false, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + ptr, size); + else + return NULL; + } + else { + return os_realloc(ptr, size); + } +} + +static inline void +wasm_runtime_free_internal(void *ptr) +{ + if (!ptr) { + LOG_WARNING("warning: wasm_runtime_free with NULL pointer\n"); +#if BH_ENABLE_GC_VERIFY != 0 + exit(-1); +#endif + return; + } + + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_WARNING("warning: wasm_runtime_free failed: " + "memory hasn't been initialize.\n"); + } + else if (memory_mode == MEMORY_MODE_POOL) { + mem_allocator_free(pool_allocator, ptr); + } + else if (memory_mode == MEMORY_MODE_ALLOCATOR) { + free_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + Alloc_For_Runtime, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + ptr); + } + else { + os_free(ptr); + } +} + +static inline void * +wasm_runtime_aligned_alloc_internal(unsigned int size, unsigned int alignment) +{ + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_ERROR("wasm_runtime_aligned_alloc failed: memory hasn't been " + "initialized.\n"); + return NULL; + } + + if (memory_mode != MEMORY_MODE_POOL) { + LOG_ERROR("wasm_runtime_aligned_alloc failed: only supported in POOL " + "memory mode.\n"); + return NULL; + } + + return mem_allocator_malloc_aligned(pool_allocator, size, alignment); +} + +void * +wasm_runtime_malloc(unsigned int size) +{ + if (size == 0) { + LOG_WARNING("warning: wasm_runtime_malloc with size zero\n"); + /* At lease alloc 1 byte to avoid malloc failed */ + size = 1; +#if BH_ENABLE_GC_VERIFY != 0 + exit(-1); +#endif + } + +#if WASM_ENABLE_FUZZ_TEST != 0 + if (size >= WASM_MEM_ALLOC_MAX_SIZE) { + LOG_WARNING("warning: wasm_runtime_malloc with too large size\n"); + return NULL; + } +#endif + + return wasm_runtime_malloc_internal(size); +} + +void * +wasm_runtime_aligned_alloc(unsigned int size, unsigned int alignment) +{ + if (alignment == 0) { + LOG_WARNING( + "warning: wasm_runtime_aligned_alloc with zero alignment\n"); + return NULL; + } + + if (size == 0) { + LOG_WARNING("warning: wasm_runtime_aligned_alloc with size zero\n"); + /* Allocate at least alignment bytes (smallest multiple of alignment) */ + size = alignment; +#if BH_ENABLE_GC_VERIFY != 0 + exit(-1); +#endif + } + +#if WASM_ENABLE_FUZZ_TEST != 0 + if (size >= WASM_MEM_ALLOC_MAX_SIZE) { + LOG_WARNING( + "warning: wasm_runtime_aligned_alloc with too large size\n"); + return NULL; + } +#endif + + return wasm_runtime_aligned_alloc_internal(size, alignment); +} + +void * +wasm_runtime_realloc(void *ptr, unsigned int size) +{ + return wasm_runtime_realloc_internal(ptr, size); +} + +void +wasm_runtime_free(void *ptr) +{ + wasm_runtime_free_internal(ptr); +} + +bool +wasm_runtime_get_mem_alloc_info(mem_alloc_info_t *mem_alloc_info) +{ + if (memory_mode == MEMORY_MODE_POOL) { + return mem_allocator_get_alloc_info(pool_allocator, mem_alloc_info); + } + return false; +} + +bool +wasm_runtime_validate_app_addr(WASMModuleInstanceCommon *module_inst_comm, + uint64 app_offset, uint64 size) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint64 max_linear_memory_size = MAX_LINEAR_MEMORY_SIZE; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + if (!is_bounds_checks_enabled(module_inst_comm)) { + return true; + } + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + goto fail; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (is_app_addr_in_shared_heap(module_inst_comm, memory_inst->is_memory64, + app_offset, size)) { + return true; + } +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + if (memory_inst->is_memory64) + max_linear_memory_size = MAX_LINEAR_MEM64_MEMORY_SIZE; +#endif + /* boundary overflow check */ + if (size > max_linear_memory_size + || app_offset > max_linear_memory_size - size) { + goto fail; + } + + SHARED_MEMORY_LOCK(memory_inst); + + if (app_offset + size <= memory_inst->memory_data_size) { + SHARED_MEMORY_UNLOCK(memory_inst); + return true; + } + + SHARED_MEMORY_UNLOCK(memory_inst); + +fail: + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +bool +wasm_runtime_validate_app_str_addr(WASMModuleInstanceCommon *module_inst_comm, + uint64 app_str_offset) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint64 app_end_offset, max_linear_memory_size = MAX_LINEAR_MEMORY_SIZE; + char *str, *str_end; +#if WASM_ENABLE_SHARED_HEAP != 0 + uintptr_t shared_heap_end_off; + char *shared_heap_base_addr_adj; +#endif + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + if (!is_bounds_checks_enabled(module_inst_comm)) { + return true; + } + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + goto fail; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (is_app_addr_in_shared_heap(module_inst_comm, memory_inst->is_memory64, + app_str_offset, 1)) { + shared_heap_end_off = + get_last_used_shared_heap_end_offset(module_inst_comm); + shared_heap_base_addr_adj = + (char *)get_last_used_shared_heap_base_addr_adj(module_inst_comm); + str = shared_heap_base_addr_adj + app_str_offset; + str_end = shared_heap_base_addr_adj + shared_heap_end_off + 1; + } + else +#endif + { + if (!wasm_runtime_get_app_addr_range(module_inst_comm, app_str_offset, + NULL, &app_end_offset)) + goto fail; + +#if WASM_ENABLE_MEMORY64 != 0 + if (memory_inst->is_memory64) + max_linear_memory_size = MAX_LINEAR_MEM64_MEMORY_SIZE; +#endif + /* boundary overflow check, max start offset can be size - 1, while end + offset can be size */ + if (app_str_offset >= max_linear_memory_size + || app_end_offset > max_linear_memory_size) + goto fail; + + str = wasm_runtime_addr_app_to_native(module_inst_comm, app_str_offset); + str_end = str + (app_end_offset - app_str_offset); + } + + while (str < str_end && *str != '\0') + str++; + if (str == str_end) + goto fail; + + return true; +fail: + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +bool +wasm_runtime_validate_native_addr(WASMModuleInstanceCommon *module_inst_comm, + void *native_ptr, uint64 size) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint8 *addr = (uint8 *)native_ptr; + uint64 max_linear_memory_size = MAX_LINEAR_MEMORY_SIZE; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + if (!is_bounds_checks_enabled(module_inst_comm)) { + return true; + } + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + goto fail; + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (memory_inst->is_memory64) + max_linear_memory_size = MAX_LINEAR_MEM64_MEMORY_SIZE; +#endif + /* boundary overflow check */ + if (size > max_linear_memory_size || (uintptr_t)addr > UINTPTR_MAX - size) { + goto fail; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (is_native_addr_in_shared_heap( + module_inst_comm, memory_inst->is_memory64, native_ptr, size)) { + return true; + } +#endif + + SHARED_MEMORY_LOCK(memory_inst); + + if (memory_inst->memory_data <= addr + && addr + size <= memory_inst->memory_data_end) { + SHARED_MEMORY_UNLOCK(memory_inst); + return true; + } + + SHARED_MEMORY_UNLOCK(memory_inst); + +fail: + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +void * +wasm_runtime_addr_app_to_native(WASMModuleInstanceCommon *module_inst_comm, + uint64 app_offset) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint8 *addr; + bool bounds_checks; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + bounds_checks = is_bounds_checks_enabled(module_inst_comm); + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + return NULL; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (is_app_addr_in_shared_heap(module_inst_comm, memory_inst->is_memory64, + app_offset, 1)) { + return get_last_used_shared_heap_base_addr_adj(module_inst_comm) + + app_offset; + } +#endif + + SHARED_MEMORY_LOCK(memory_inst); + + addr = memory_inst->memory_data + (uintptr_t)app_offset; + + if (bounds_checks) { + if (memory_inst->memory_data <= addr + && addr < memory_inst->memory_data_end) { + SHARED_MEMORY_UNLOCK(memory_inst); + return addr; + } + SHARED_MEMORY_UNLOCK(memory_inst); + return NULL; + } + + /* If bounds checks is disabled, return the address directly */ + SHARED_MEMORY_UNLOCK(memory_inst); + return addr; +} + +uint64 +wasm_runtime_addr_native_to_app(WASMModuleInstanceCommon *module_inst_comm, + void *native_ptr) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint8 *addr = (uint8 *)native_ptr; + bool bounds_checks; + uint64 ret; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + bounds_checks = is_bounds_checks_enabled(module_inst_comm); + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + return 0; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (is_native_addr_in_shared_heap(module_inst_comm, + memory_inst->is_memory64, addr, 1)) { + return (uint64)(uintptr_t)(addr + - get_last_used_shared_heap_base_addr_adj( + module_inst_comm)); + } +#endif + + SHARED_MEMORY_LOCK(memory_inst); + + if (bounds_checks) { + if (memory_inst->memory_data <= addr + && addr < memory_inst->memory_data_end) { + ret = (uint64)(addr - memory_inst->memory_data); + SHARED_MEMORY_UNLOCK(memory_inst); + return ret; + } + } + /* If bounds checks is disabled, return the offset directly */ + else if (addr != NULL) { + ret = (uint64)(addr - memory_inst->memory_data); + SHARED_MEMORY_UNLOCK(memory_inst); + return ret; + } + + SHARED_MEMORY_UNLOCK(memory_inst); + return 0; +} + +bool +wasm_runtime_get_app_addr_range(WASMModuleInstanceCommon *module_inst_comm, + uint64 app_offset, uint64 *p_app_start_offset, + uint64 *p_app_end_offset) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint64 memory_data_size; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + return false; + } + + SHARED_MEMORY_LOCK(memory_inst); + + memory_data_size = memory_inst->memory_data_size; + + if (app_offset < memory_data_size) { + if (p_app_start_offset) + *p_app_start_offset = 0; + if (p_app_end_offset) + *p_app_end_offset = memory_data_size; + SHARED_MEMORY_UNLOCK(memory_inst); + return true; + } + + SHARED_MEMORY_UNLOCK(memory_inst); + return false; +} + +bool +wasm_runtime_get_native_addr_range(WASMModuleInstanceCommon *module_inst_comm, + uint8 *native_ptr, + uint8 **p_native_start_addr, + uint8 **p_native_end_addr) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMMemoryInstance *memory_inst; + uint8 *addr = (uint8 *)native_ptr; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + memory_inst = wasm_get_default_memory(module_inst); + if (!memory_inst) { + return false; + } + + SHARED_MEMORY_LOCK(memory_inst); + + if (memory_inst->memory_data <= addr + && addr < memory_inst->memory_data_end) { + if (p_native_start_addr) + *p_native_start_addr = memory_inst->memory_data; + if (p_native_end_addr) + *p_native_end_addr = memory_inst->memory_data_end; + SHARED_MEMORY_UNLOCK(memory_inst); + return true; + } + + SHARED_MEMORY_UNLOCK(memory_inst); + return false; +} + +bool +wasm_check_app_addr_and_convert(WASMModuleInstance *module_inst, bool is_str, + uint64 app_buf_addr, uint64 app_buf_size, + void **p_native_addr) +{ + WASMMemoryInstance *memory_inst = wasm_get_default_memory(module_inst); + uint8 *native_addr; + bool bounds_checks; +#if WASM_ENABLE_SHARED_HEAP != 0 + uint8 *shared_heap_base_addr_adj = NULL; + uintptr_t shared_heap_end_off = 0; +#endif + + bh_assert(app_buf_addr <= UINTPTR_MAX && app_buf_size <= UINTPTR_MAX); + + if (!memory_inst) { + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (is_app_addr_in_shared_heap((WASMModuleInstanceCommon *)module_inst, + memory_inst->is_memory64, app_buf_addr, + app_buf_size)) { + const char *str, *str_end; + shared_heap_base_addr_adj = get_last_used_shared_heap_base_addr_adj( + (WASMModuleInstanceCommon *)module_inst); + shared_heap_end_off = get_last_used_shared_heap_end_offset( + (WASMModuleInstanceCommon *)module_inst); + native_addr = shared_heap_base_addr_adj + (uintptr_t)app_buf_addr; + + /* The whole string must be in the shared heap */ + str = (const char *)native_addr; + str_end = + (const char *)shared_heap_base_addr_adj + shared_heap_end_off + 1; + while (str < str_end && *str != '\0') + str++; + if (str == str_end) { + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; + } + else + goto success; + } +#endif + + native_addr = memory_inst->memory_data + (uintptr_t)app_buf_addr; + bounds_checks = + is_bounds_checks_enabled((WASMModuleInstanceCommon *)module_inst); + + if (!bounds_checks) { + if (app_buf_addr == 0) { + native_addr = NULL; + } + goto success; + } + + /* No need to check the app_offset and buf_size if memory access + boundary check with hardware trap is enabled */ +#ifndef OS_ENABLE_HW_BOUND_CHECK + SHARED_MEMORY_LOCK(memory_inst); + + if (app_buf_addr >= memory_inst->memory_data_size) { + goto fail; + } + + if (!is_str) { + if (app_buf_size > memory_inst->memory_data_size - app_buf_addr) { + goto fail; + } + } + else { + const char *str, *str_end; + + /* The whole string must be in the linear memory */ + str = (const char *)native_addr; + str_end = (const char *)memory_inst->memory_data_end; + while (str < str_end && *str != '\0') + str++; + if (str == str_end) + goto fail; + } + + SHARED_MEMORY_UNLOCK(memory_inst); +#endif + +success: + *p_native_addr = (void *)native_addr; + return true; + +#ifndef OS_ENABLE_HW_BOUND_CHECK +fail: + SHARED_MEMORY_UNLOCK(memory_inst); + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; +#endif +} + +WASMMemoryInstance * +wasm_get_default_memory(WASMModuleInstance *module_inst) +{ + if (module_inst->memories) + return module_inst->memories[0]; + else + return NULL; +} + +WASMMemoryInstance * +wasm_get_memory_with_idx(WASMModuleInstance *module_inst, uint32 index) +{ + if ((index >= module_inst->memory_count) || !module_inst->memories) + return NULL; + return module_inst->memories[index]; +} + +void +wasm_runtime_set_mem_bound_check_bytes(WASMMemoryInstance *memory, + uint64 memory_data_size) +{ +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 || WASM_ENABLE_AOT != 0 +#if UINTPTR_MAX == UINT64_MAX + memory->mem_bound_check_1byte.u64 = memory_data_size - 1; + memory->mem_bound_check_2bytes.u64 = memory_data_size - 2; + memory->mem_bound_check_4bytes.u64 = memory_data_size - 4; + memory->mem_bound_check_8bytes.u64 = memory_data_size - 8; + memory->mem_bound_check_16bytes.u64 = memory_data_size - 16; +#else + memory->mem_bound_check_1byte.u32[0] = (uint32)memory_data_size - 1; + memory->mem_bound_check_2bytes.u32[0] = (uint32)memory_data_size - 2; + memory->mem_bound_check_4bytes.u32[0] = (uint32)memory_data_size - 4; + memory->mem_bound_check_8bytes.u32[0] = (uint32)memory_data_size - 8; + memory->mem_bound_check_16bytes.u32[0] = (uint32)memory_data_size - 16; +#endif +#endif +} + +static void +wasm_munmap_linear_memory(void *mapped_mem, uint64 commit_size, uint64 map_size) +{ +#ifdef BH_PLATFORM_WINDOWS + os_mem_decommit(mapped_mem, commit_size); +#else + (void)commit_size; +#endif + os_munmap(mapped_mem, map_size); +} + +static void * +wasm_mremap_linear_memory(void *mapped_mem, uint64 old_size, uint64 new_size, + uint64 commit_size) +{ + void *new_mem; + + bh_assert(new_size > 0); + bh_assert(new_size > old_size); + +#if UINTPTR_MAX == UINT32_MAX + if (new_size == 4 * (uint64)BH_GB) { + LOG_WARNING("On 32 bit platform, linear memory can't reach maximum " + "size of 4GB\n"); + return NULL; + } +#endif + + if (mapped_mem) { + new_mem = os_mremap(mapped_mem, old_size, new_size); + } + else { + new_mem = os_mmap(NULL, new_size, MMAP_PROT_NONE, MMAP_MAP_NONE, + os_get_invalid_handle()); + } + if (!new_mem) { + return NULL; + } + +#ifdef BH_PLATFORM_WINDOWS + if (commit_size > 0 + && !os_mem_commit(new_mem, commit_size, + MMAP_PROT_READ | MMAP_PROT_WRITE)) { + os_munmap(new_mem, new_size); + return NULL; + } +#endif + + if (os_mprotect(new_mem, commit_size, MMAP_PROT_READ | MMAP_PROT_WRITE) + != 0) { + wasm_munmap_linear_memory(new_mem, new_size, new_size); + return NULL; + } + + return new_mem; +} + +static void * +wasm_mmap_linear_memory(uint64 map_size, uint64 commit_size) +{ + return wasm_mremap_linear_memory(NULL, 0, map_size, commit_size); +} + +static bool +wasm_enlarge_memory_internal(WASMModuleInstanceCommon *module, + WASMMemoryInstance *memory, uint32 inc_page_count) +{ +#if WASM_ENABLE_SHARED_HEAP != 0 + WASMSharedHeap *shared_heap; +#endif + uint8 *memory_data_old, *memory_data_new, *heap_data_old; + uint32 num_bytes_per_page, heap_size; + uint32 cur_page_count, max_page_count, total_page_count; + uint64 total_size_old = 0, total_size_new; + bool ret = true, full_size_mmaped; + enlarge_memory_error_reason_t failure_reason = INTERNAL_ERROR; + + if (!memory) { + ret = false; + goto return_func; + } + +#ifdef OS_ENABLE_HW_BOUND_CHECK + full_size_mmaped = true; +#elif WASM_ENABLE_SHARED_MEMORY != 0 + full_size_mmaped = shared_memory_is_shared(memory); +#else + full_size_mmaped = false; +#endif + + memory_data_old = memory->memory_data; + total_size_old = memory->memory_data_size; + + heap_data_old = memory->heap_data; + heap_size = (uint32)(memory->heap_data_end - memory->heap_data); + + num_bytes_per_page = memory->num_bytes_per_page; + cur_page_count = memory->cur_page_count; + max_page_count = memory->max_page_count; + total_page_count = inc_page_count + cur_page_count; + total_size_new = num_bytes_per_page * (uint64)total_page_count; + + if (inc_page_count <= 0) + /* No need to enlarge memory */ + return true; + + if (total_page_count < cur_page_count) { /* integer overflow */ + ret = false; + goto return_func; + } + + if (total_page_count > max_page_count) { + failure_reason = MAX_SIZE_REACHED; + ret = false; + goto return_func; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + shared_heap = get_shared_heap(module); + if (shared_heap) { + if (memory->is_memory64 + && total_size_new > shared_heap->start_off_mem64) { + LOG_WARNING("Linear memory address is overlapped with shared heap"); + ret = false; + goto return_func; + } + else if (!memory->is_memory64 + && total_size_new > shared_heap->start_off_mem32) { + LOG_WARNING("Linear memory address is overlapped with shared heap"); + ret = false; + goto return_func; + } + } +#endif + + bh_assert(total_size_new + <= GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64)); + +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + if (!(memory_data_new = + realloc_func(Alloc_For_LinearMemory, full_size_mmaped, +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + memory_data_old, total_size_new))) { + ret = false; + goto return_func; + } + if (heap_size > 0) { + if (mem_allocator_migrate(memory->heap_handle, + (char *)heap_data_old + + (memory_data_new - memory_data_old), + heap_size) + != 0) { + ret = false; + } + } + memory->heap_data = memory_data_new + (heap_data_old - memory_data_old); + memory->heap_data_end = memory->heap_data + heap_size; + memory->memory_data = memory_data_new; +#else + if (full_size_mmaped) { +#ifdef BH_PLATFORM_WINDOWS + if (!os_mem_commit(memory->memory_data_end, + total_size_new - total_size_old, + MMAP_PROT_READ | MMAP_PROT_WRITE)) { + ret = false; + goto return_func; + } +#endif + + if (os_mprotect(memory->memory_data_end, + total_size_new - total_size_old, + MMAP_PROT_READ | MMAP_PROT_WRITE) + != 0) { +#ifdef BH_PLATFORM_WINDOWS + os_mem_decommit(memory->memory_data_end, + total_size_new - total_size_old); +#endif + ret = false; + goto return_func; + } + } + else { + if (heap_size > 0) { + if (mem_allocator_is_heap_corrupted(memory->heap_handle)) { + wasm_runtime_show_app_heap_corrupted_prompt(); + ret = false; + goto return_func; + } + } + + if (!(memory_data_new = + wasm_mremap_linear_memory(memory_data_old, total_size_old, + total_size_new, total_size_new))) { + ret = false; + goto return_func; + } + + if (heap_size > 0) { + if (mem_allocator_migrate(memory->heap_handle, + (char *)heap_data_old + + (memory_data_new - memory_data_old), + heap_size) + != 0) { + /* Don't return here as memory->memory_data is obsolete and + must be updated to be correctly used later. */ + ret = false; + } + } + + memory->heap_data = memory_data_new + (heap_data_old - memory_data_old); + memory->heap_data_end = memory->heap_data + heap_size; + memory->memory_data = memory_data_new; +#if defined(os_writegsbase) + /* write base addr of linear memory to GS segment register */ + os_writegsbase(memory_data_new); +#endif + } +#endif /* end of WASM_MEM_ALLOC_WITH_USAGE */ + + /* + * AOT compiler assumes at least 8 byte alignment. + * see aot_check_memory_overflow. + */ + bh_assert(((uintptr_t)memory->memory_data & 0x7) == 0); + + memory->num_bytes_per_page = num_bytes_per_page; + memory->cur_page_count = total_page_count; + memory->max_page_count = max_page_count; + SET_LINEAR_MEMORY_SIZE(memory, total_size_new); + memory->memory_data_end = memory->memory_data + total_size_new; + + wasm_runtime_set_mem_bound_check_bytes(memory, total_size_new); + +return_func: + if (!ret && module && enlarge_memory_error_cb) { + WASMExecEnv *exec_env = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) + exec_env = ((WASMModuleInstance *)module)->cur_exec_env; +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + exec_env = ((AOTModuleInstance *)module)->cur_exec_env; +#endif + + enlarge_memory_error_cb(inc_page_count, total_size_old, 0, + failure_reason, module, exec_env, + enlarge_memory_error_user_data); + } + + return ret; +} + +bool +wasm_runtime_enlarge_memory(WASMModuleInstanceCommon *module_inst, + uint64 inc_page_count) +{ + if (inc_page_count > UINT32_MAX) { + return false; + } + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_enlarge_memory((AOTModuleInstance *)module_inst, + (uint32)inc_page_count); + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_enlarge_memory((WASMModuleInstance *)module_inst, + (uint32)inc_page_count); + } +#endif + + return false; +} + +void +wasm_runtime_set_enlarge_mem_error_callback( + const enlarge_memory_error_callback_t callback, void *user_data) +{ + enlarge_memory_error_cb = callback; + enlarge_memory_error_user_data = user_data; +} + +bool +wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) +{ + bool ret = false; + + if (module->memory_count > 0) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + shared_memory_lock(module->memories[0]); +#endif + ret = wasm_enlarge_memory_internal((WASMModuleInstanceCommon *)module, + module->memories[0], inc_page_count); +#if WASM_ENABLE_SHARED_MEMORY != 0 + shared_memory_unlock(module->memories[0]); +#endif + } + + return ret; +} + +bool +wasm_enlarge_memory_with_idx(WASMModuleInstance *module, uint32 inc_page_count, + uint32 memidx) +{ + bool ret = false; + + if (memidx < module->memory_count) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + shared_memory_lock(module->memories[memidx]); +#endif + ret = wasm_enlarge_memory_internal((WASMModuleInstanceCommon *)module, + module->memories[memidx], + inc_page_count); +#if WASM_ENABLE_SHARED_MEMORY != 0 + shared_memory_unlock(module->memories[memidx]); +#endif + } + + return ret; +} + +WASMMemoryInstance * +wasm_runtime_lookup_memory(WASMModuleInstanceCommon *module_inst, + const char *name) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_lookup_memory((WASMModuleInstance *)module_inst, name); +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_lookup_memory((WASMModuleInstance *)module_inst, name); +#endif + + return NULL; +} + +WASMMemoryInstance * +wasm_runtime_get_default_memory(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_get_default_memory((WASMModuleInstance *)module_inst); +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_get_default_memory((AOTModuleInstance *)module_inst); +#endif + + return NULL; +} + +WASMMemoryInstance * +wasm_runtime_get_memory(WASMModuleInstanceCommon *module_inst, uint32 index) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_get_memory_with_idx((WASMModuleInstance *)module_inst, + index); +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_get_memory_with_idx((AOTModuleInstance *)module_inst, index); +#endif + + return NULL; +} + +uint64 +wasm_memory_get_cur_page_count(WASMMemoryInstance *memory) +{ + return memory->cur_page_count; +} + +uint64 +wasm_memory_get_max_page_count(WASMMemoryInstance *memory) +{ + return memory->max_page_count; +} + +uint64 +wasm_memory_get_bytes_per_page(WASMMemoryInstance *memory) +{ + return memory->num_bytes_per_page; +} + +bool +wasm_memory_get_shared(WASMMemoryInstance *memory) +{ + return memory->is_shared_memory; +} + +void * +wasm_memory_get_base_address(WASMMemoryInstance *memory) +{ + return memory->memory_data; +} + +bool +wasm_memory_enlarge(WASMMemoryInstance *memory, uint64 inc_page_count) +{ + bool ret = false; + + if (memory) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + shared_memory_lock(memory); +#endif + ret = + wasm_enlarge_memory_internal(NULL, memory, (uint32)inc_page_count); +#if WASM_ENABLE_SHARED_MEMORY != 0 + shared_memory_unlock(memory); +#endif + } + + return ret; +} + +void +wasm_deallocate_linear_memory(WASMMemoryInstance *memory_inst) +{ + uint64 map_size; + + bh_assert(memory_inst); + bh_assert(memory_inst->memory_data); + +#ifndef OS_ENABLE_HW_BOUND_CHECK +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (shared_memory_is_shared(memory_inst)) { + map_size = (uint64)memory_inst->num_bytes_per_page + * memory_inst->max_page_count; + } + else +#endif + { + map_size = (uint64)memory_inst->num_bytes_per_page + * memory_inst->cur_page_count; + } +#else + map_size = 8 * (uint64)BH_GB; +#endif + +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + (void)map_size; + free_func(Alloc_For_LinearMemory, +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + memory_inst->memory_data); +#else + wasm_munmap_linear_memory(memory_inst->memory_data, + memory_inst->memory_data_size, map_size); +#endif + + memory_inst->memory_data = NULL; +} + +int +wasm_allocate_linear_memory(uint8 **data, bool is_shared_memory, + bool is_memory64, uint64 num_bytes_per_page, + uint64 init_page_count, uint64 max_page_count, + uint64 *memory_data_size) +{ + uint64 map_size, page_size; + + bh_assert(data); + bh_assert(memory_data_size); + +#ifndef OS_ENABLE_HW_BOUND_CHECK +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (is_shared_memory) { + /* Allocate maximum memory size when memory is shared */ + map_size = max_page_count * num_bytes_per_page; + } + else +#endif + { + map_size = init_page_count * num_bytes_per_page; + } +#else /* else of OS_ENABLE_HW_BOUND_CHECK */ + /* Totally 8G is mapped, the opcode load/store address range is 0 to 8G: + * ea = i + memarg.offset + * both i and memarg.offset are u32 in range 0 to 4G + * so the range of ea is 0 to 8G + */ + map_size = 8 * (uint64)BH_GB; +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + + page_size = os_getpagesize(); + *memory_data_size = init_page_count * num_bytes_per_page; + + bh_assert(*memory_data_size <= GET_MAX_LINEAR_MEMORY_SIZE(is_memory64)); + *memory_data_size = align_as_and_cast(*memory_data_size, page_size); + + if (map_size > 0) { +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + (void)wasm_mmap_linear_memory; + if (!(*data = malloc_func(Alloc_For_LinearMemory, +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + allocator_user_data, +#endif + *memory_data_size))) { + return BHT_ERROR; + } +#else + if (!(*data = wasm_mmap_linear_memory(map_size, *memory_data_size))) { + return BHT_ERROR; + } +#endif + } + + /* + * AOT compiler assumes at least 8 byte alignment. + * see aot_check_memory_overflow. + */ + bh_assert(((uintptr_t)*data & 0x7) == 0); + + return BHT_OK; +} diff --git a/core/iwasm/common/wasm_memory.h b/core/iwasm/common/wasm_memory.h new file mode 100644 index 0000000000..a96e250400 --- /dev/null +++ b/core/iwasm/common/wasm_memory.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_MEMORY_H +#define _WASM_MEMORY_H + +#include "bh_common.h" +#include "../include/wasm_export.h" +#include "../interpreter/wasm_runtime.h" +#include "../common/wasm_shared_memory.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 && BH_ATOMIC_64_IS_ATOMIC != 0 +#define GET_LINEAR_MEMORY_SIZE(memory) \ + BH_ATOMIC_64_LOAD(memory->memory_data_size) +#define SET_LINEAR_MEMORY_SIZE(memory, size) \ + BH_ATOMIC_64_STORE(memory->memory_data_size, size) +#elif WASM_ENABLE_SHARED_MEMORY != 0 +static inline uint64 +GET_LINEAR_MEMORY_SIZE(const WASMMemoryInstance *memory) +{ + SHARED_MEMORY_LOCK(memory); + uint64 memory_data_size = BH_ATOMIC_64_LOAD(memory->memory_data_size); + SHARED_MEMORY_UNLOCK(memory); + return memory_data_size; +} +static inline void +SET_LINEAR_MEMORY_SIZE(WASMMemoryInstance *memory, uint64 size) +{ + SHARED_MEMORY_LOCK(memory); + BH_ATOMIC_64_STORE(memory->memory_data_size, size); + SHARED_MEMORY_UNLOCK(memory); +} +#else +#define GET_LINEAR_MEMORY_SIZE(memory) memory->memory_data_size +#define SET_LINEAR_MEMORY_SIZE(memory, size) memory->memory_data_size = size +#endif + +#if WASM_ENABLE_INTERP != 0 +#if WASM_ENABLE_SHARED_HEAP != 0 + +#if WASM_ENABLE_MULTI_MEMORY != 0 +/* Only enable shared heap for the default memory */ +#define is_default_memory (memidx == 0) +#else +#define is_default_memory true +#endif + +#if UINTPTR_MAX == UINT64_MAX +#define get_shared_heap_end_off() module->e->shared_heap_end_off.u64 +#else +#define get_shared_heap_end_off() \ + (uint64)(module->e->shared_heap_end_off.u32[0]) +#endif + +#if WASM_ENABLE_MEMORY64 != 0 +#define shared_heap_is_memory64 is_memory64 +#else +#define shared_heap_is_memory64 false +#endif + +#define app_addr_in_shared_heap(app_addr, bytes) \ + (is_default_memory \ + && is_app_addr_in_shared_heap((WASMModuleInstanceCommon *)module, \ + shared_heap_is_memory64, (uint64)app_addr, \ + bytes)) +#define shared_heap_addr_app_to_native(app_addr, native_addr) \ + native_addr = module->e->shared_heap_base_addr_adj + app_addr +#define CHECK_SHARED_HEAP_OVERFLOW(app_addr, bytes, native_addr) \ + if (app_addr_in_shared_heap(app_addr, bytes)) \ + shared_heap_addr_app_to_native(app_addr, native_addr); \ + else + +#else /* else of WASM_ENABLE_SHARED_HEAP != 0 */ +#define CHECK_SHARED_HEAP_OVERFLOW(app_addr, bytes, native_addr) +#endif /* end of WASM_ENABLE_SHARED_HEAP != 0 */ +#endif /* end of WASM_ENABLE_INTERP != 0 */ + +#if WASM_ENABLE_SHARED_HEAP != 0 +bool +is_app_addr_in_shared_heap(WASMModuleInstanceCommon *module_inst, + bool is_memory64, uint64 app_offset, uint64 bytes); + +WASMSharedHeap * +wasm_runtime_create_shared_heap(SharedHeapInitArgs *init_args); + +WASMSharedHeap * +wasm_runtime_chain_shared_heaps(WASMSharedHeap *head, WASMSharedHeap *body); + +WASMSharedHeap * +wasm_runtime_unchain_shared_heaps(WASMSharedHeap *head, bool entire_chain); + +bool +wasm_runtime_reset_shared_heap_chain(WASMSharedHeap *shared_heap); + +bool +wasm_runtime_attach_shared_heap(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *shared_heap); +bool +wasm_runtime_attach_shared_heap_internal(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *shared_heap); + +void +wasm_runtime_detach_shared_heap(WASMModuleInstanceCommon *module_inst); + +void +wasm_runtime_detach_shared_heap_internal(WASMModuleInstanceCommon *module_inst); + +WASMSharedHeap * +wasm_runtime_get_shared_heap(WASMModuleInstanceCommon *module_inst_comm); + +uint64 +wasm_runtime_shared_heap_malloc(WASMModuleInstanceCommon *module_inst, + uint64 size, void **p_native_addr); + +void +wasm_runtime_shared_heap_free(WASMModuleInstanceCommon *module_inst, + uint64 ptr); +#endif /* end of WASM_ENABLE_SHARED_HEAP != 0 */ + +bool +wasm_runtime_memory_init(mem_alloc_type_t mem_alloc_type, + const MemAllocOption *alloc_option); + +void +wasm_runtime_memory_destroy(void); + +unsigned +wasm_runtime_memory_pool_size(void); + +void +wasm_runtime_set_mem_bound_check_bytes(WASMMemoryInstance *memory, + uint64 memory_data_size); + +void +wasm_runtime_set_enlarge_mem_error_callback( + const enlarge_memory_error_callback_t callback, void *user_data); + +void +wasm_deallocate_linear_memory(WASMMemoryInstance *memory_inst); + +int +wasm_allocate_linear_memory(uint8 **data, bool is_shared_memory, + bool is_memory64, uint64 num_bytes_per_page, + uint64 init_page_count, uint64 max_page_count, + uint64 *memory_data_size); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_MEMORY_H */ diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index d7244391c8..e1d7fbc031 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -4,122 +4,1568 @@ */ #include "wasm_native.h" +#include "wasm_runtime_common.h" +#include "bh_log.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif +#if WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +#include "wasi_nn_host.h" +#endif +static NativeSymbolsList g_native_symbols_list = NULL; -typedef struct NativeSymbol { - const char *symbol; - void *func_ptr; -} NativeSymbol; +#if WASM_ENABLE_LIBC_WASI != 0 +static void *g_wasi_context_key; +#endif /* WASM_ENABLE_LIBC_WASI */ + +uint32 +get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis); + +#if WASM_ENABLE_SPEC_TEST != 0 +uint32 +get_spectest_export_apis(NativeSymbol **p_libc_builtin_apis); +#endif + +#if WASM_ENABLE_SHARED_HEAP != 0 +uint32 +get_lib_shared_heap_export_apis(NativeSymbol **p_shared_heap_apis); +#endif + +uint32 +get_libc_wasi_export_apis(NativeSymbol **p_libc_wasi_apis); + +uint32 +get_base_lib_export_apis(NativeSymbol **p_base_lib_apis); + +uint32 +get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis); + +#if WASM_ENABLE_LIB_PTHREAD != 0 +bool +lib_pthread_init(); + +void +lib_pthread_destroy(); + +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); + +uint32 +get_lib_rats_export_apis(NativeSymbol **p_lib_rats_apis); static bool -sort_symbol_ptr(NativeSymbol *ptr, int len) +compare_type_with_signature(uint8 type, const char signature) { - int i, j; - NativeSymbol temp; + const char num_sig_map[] = { 'F', 'f', 'I', 'i' }; + + if (VALUE_TYPE_F64 <= type && type <= VALUE_TYPE_I32 + && signature == num_sig_map[type - VALUE_TYPE_F64]) { + return true; + } + +#if WASM_ENABLE_REF_TYPES != 0 + if ('r' == signature +#if WASM_ENABLE_GC != 0 +#if WASM_ENABLE_STRINGREF != 0 + && (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_NULLFUNCREF) +#else + && (type >= REF_TYPE_HT_NULLABLE && type <= REF_TYPE_NULLFUNCREF) +#endif +#else + && type == VALUE_TYPE_EXTERNREF +#endif + ) + return true; +#endif + + /* TODO: a v128 parameter */ + return false; +} + +static bool +check_symbol_signature(const WASMFuncType *type, const char *signature) +{ + const char *p = signature, *p_end; + char sig; + uint32 i = 0; + + if (!p || strlen(p) < 2) + return false; - for (i = 0; i < len - 1; ++i) { - for (j = i + 1; j < len; ++j) { - if (strcmp((ptr+i)->symbol, (ptr+j)->symbol) > 0) { - temp = ptr[i]; - ptr[i] = ptr[j]; - ptr[j] = temp; + p_end = p + strlen(signature); + + if (*p++ != '(') + return false; + + if ((uint32)(p_end - p) < (uint32)(type->param_count + 1)) + /* signatures of parameters, and ')' */ + return false; + + for (i = 0; i < type->param_count; i++) { + sig = *p++; + + /* a f64/f32/i64/i32/externref parameter */ + if (compare_type_with_signature(type->types[i], sig)) + continue; + + /* a pointer/string parameter */ + if (type->types[i] != VALUE_TYPE_I32) + /* pointer and string must be i32 type */ + return false; + + if (sig == '*') { + /* it is a pointer */ + if (i + 1 < type->param_count + && type->types[i + 1] == VALUE_TYPE_I32 && *p == '~') { + /* pointer length followed */ + i++; + p++; } } + else if (sig == '$') { + /* it is a string */ + } + else { + /* invalid signature */ + return false; + } + } + + if (*p++ != ')') + return false; + + if (type->result_count) { + if (p >= p_end) + return false; + + /* result types includes: f64,f32,i64,i32,externref */ + if (!compare_type_with_signature(type->types[i], *p)) + return false; + + p++; } + if (*p != '\0') + return false; + return true; } +static int +native_symbol_cmp(const void *native_symbol1, const void *native_symbol2) +{ + return strcmp(((const NativeSymbol *)native_symbol1)->symbol, + ((const NativeSymbol *)native_symbol2)->symbol); +} + static void * -lookup_symbol(NativeSymbol *ptr, int len, const char *symbol) +lookup_symbol(NativeSymbol *native_symbols, uint32 n_native_symbols, + const char *symbol, const char **p_signature, void **p_attachment) { - int low = 0, mid, ret; - int high = len - 1; + NativeSymbol *native_symbol, key = { 0 }; + + key.symbol = symbol; + + if ((native_symbol = bsearch(&key, native_symbols, n_native_symbols, + sizeof(NativeSymbol), native_symbol_cmp))) { + *p_signature = native_symbol->signature; + *p_attachment = native_symbol->attachment; + return native_symbol->func_ptr; + } - while (low <= high) { - mid = (low + high) / 2; - ret = strcmp(symbol, ptr[mid].symbol); + 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 WASMFuncType *func_type, + const char **p_signature, void **p_attachment, + bool *p_call_conv_raw) +{ + NativeSymbolsNode *node, *node_next; + const char *signature = NULL; + void *func_ptr = NULL, *attachment = NULL; - if (ret == 0) - return ptr[mid].func_ptr; - else if (ret < 0) - high = mid - 1; + node = g_native_symbols_list; + while (node) { + node_next = node->next; + if (!strcmp(node->module_name, module_name)) { + if ((func_ptr = + lookup_symbol(node->native_symbols, node->n_native_symbols, + field_name, &signature, &attachment)) + || (field_name[0] == '_' + && (func_ptr = lookup_symbol( + node->native_symbols, node->n_native_symbols, + field_name + 1, &signature, &attachment)))) + break; + } + 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 (!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 " + "pointer params for import function (%s, %s)\n", + signature, module_name, field_name); +#endif + return NULL; + } + else + /* Save signature for runtime to do pointer check and + address conversion */ + *p_signature = signature; + } else - low = mid + 1; + /* signature is empty */ + *p_signature = NULL; + + *p_attachment = attachment; + *p_call_conv_raw = node->call_conv_raw; } + return func_ptr; +} + +static bool +register_natives(const char *module_name, NativeSymbol *native_symbols, + uint32 n_native_symbols, bool call_conv_raw) +{ + NativeSymbolsNode *node; + + if (!(node = wasm_runtime_malloc(sizeof(NativeSymbolsNode)))) + return false; +#if WASM_ENABLE_MEMORY_TRACING != 0 + LOG_VERBOSE("Register native, size: %u", sizeof(NativeSymbolsNode)); +#endif + + node->module_name = module_name; + node->native_symbols = native_symbols; + node->n_native_symbols = n_native_symbols; + node->call_conv_raw = call_conv_raw; + + /* Add to list head */ + node->next = g_native_symbols_list; + g_native_symbols_list = node; + + qsort(native_symbols, n_native_symbols, sizeof(NativeSymbol), + native_symbol_cmp); + + return true; +} + +bool +wasm_native_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return register_natives(module_name, native_symbols, n_native_symbols, + false); +} + +bool +wasm_native_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return register_natives(module_name, native_symbols, n_native_symbols, + true); +} + +bool +wasm_native_unregister_natives(const char *module_name, + NativeSymbol *native_symbols) +{ + NativeSymbolsNode **prevp; + NativeSymbolsNode *node; + + prevp = &g_native_symbols_list; + while ((node = *prevp) != NULL) { + if (node->native_symbols == native_symbols + && !strcmp(node->module_name, module_name)) { + *prevp = node->next; + wasm_runtime_free(node); + return true; + } + prevp = &node->next; + } + return false; +} + +#if WASM_ENABLE_MODULE_INST_CONTEXT != 0 +static uint32 +context_key_to_idx(void *key) +{ + bh_assert(key != NULL); + uint32 idx = (uint32)(uintptr_t)key; + bh_assert(idx > 0); + bh_assert(idx <= WASM_MAX_INSTANCE_CONTEXTS); + return idx - 1; +} + +static void * +context_idx_to_key(uint32 idx) +{ + bh_assert(idx < WASM_MAX_INSTANCE_CONTEXTS); + return (void *)(uintptr_t)(idx + 1); +} + +typedef void (*dtor_t)(WASMModuleInstanceCommon *, void *); +static dtor_t g_context_dtors[WASM_MAX_INSTANCE_CONTEXTS]; + +static void +dtor_noop(WASMModuleInstanceCommon *inst, void *ctx) +{ +} + +void * +wasm_native_create_context_key(void (*dtor)(WASMModuleInstanceCommon *inst, + void *ctx)) +{ + uint32 i; + for (i = 0; i < WASM_MAX_INSTANCE_CONTEXTS; i++) { + if (g_context_dtors[i] == NULL) { + if (dtor == NULL) { + dtor = dtor_noop; + } + g_context_dtors[i] = dtor; + return context_idx_to_key(i); + } + } + LOG_ERROR("failed to allocate instance context key"); return NULL; } -#if WASM_ENABLE_BASE_LIB != 0 -static bool is_base_lib_sorted = false; -static NativeSymbol *base_native_symbol_defs; -static int base_native_symbol_len; +void +wasm_native_destroy_context_key(void *key) +{ + uint32 idx = context_key_to_idx(key); + bh_assert(g_context_dtors[idx] != NULL); + g_context_dtors[idx] = NULL; +} -int -get_base_lib_export_apis(NativeSymbol **p_base_lib_apis); +static WASMModuleInstanceExtraCommon * +wasm_module_inst_extra_common(WASMModuleInstanceCommon *inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (inst->module_type == Wasm_Module_Bytecode) { + return &((WASMModuleInstance *)inst)->e->common; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (inst->module_type == Wasm_Module_AoT) { + return &((AOTModuleInstanceExtra *)((AOTModuleInstance *)inst)->e) + ->common; + } +#endif + bh_assert(false); + return NULL; +} + +void +wasm_native_set_context(WASMModuleInstanceCommon *inst, void *key, void *ctx) +{ + uint32 idx = context_key_to_idx(key); + WASMModuleInstanceExtraCommon *common = wasm_module_inst_extra_common(inst); + common->contexts[idx] = ctx; +} + +void +wasm_native_set_context_spread(WASMModuleInstanceCommon *inst, void *key, + void *ctx) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_set_context(inst, key, ctx); +#else + wasm_native_set_context(inst, key, ctx); +#endif +} void * -wasm_native_lookup_base_lib_func(const char *module_name, - const char *func_name) +wasm_native_get_context(WASMModuleInstanceCommon *inst, void *key) { - void *ret; + uint32 idx = context_key_to_idx(key); + WASMModuleInstanceExtraCommon *common = wasm_module_inst_extra_common(inst); + return common->contexts[idx]; +} - if (strcmp(module_name, "env")) - return NULL; +void +wasm_native_call_context_dtors(WASMModuleInstanceCommon *inst) +{ + WASMModuleInstanceExtraCommon *common = wasm_module_inst_extra_common(inst); + uint32 i; + for (i = 0; i < WASM_MAX_INSTANCE_CONTEXTS; i++) { + dtor_t dtor = g_context_dtors[i]; + if (dtor != NULL) { + dtor(inst, common->contexts[i]); + } + } +} + +void +wasm_native_inherit_contexts(WASMModuleInstanceCommon *child, + WASMModuleInstanceCommon *parent) +{ + WASMModuleInstanceExtraCommon *parent_common = + wasm_module_inst_extra_common(parent); + WASMModuleInstanceExtraCommon *child_common = + wasm_module_inst_extra_common(child); + bh_memcpy_s(child_common->contexts, + sizeof(*child_common->contexts) * WASM_MAX_INSTANCE_CONTEXTS, + parent_common->contexts, + sizeof(*parent_common->contexts) * WASM_MAX_INSTANCE_CONTEXTS); +} +#endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */ + +#if WASM_ENABLE_LIBC_WASI != 0 +WASIContext * +wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst_comm) +{ + return wasm_native_get_context(module_inst_comm, g_wasi_context_key); +} + +void +wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst_comm, + WASIContext *wasi_ctx) +{ + wasm_native_set_context(module_inst_comm, g_wasi_context_key, wasi_ctx); +} - if (!is_base_lib_sorted) { - base_native_symbol_len = get_base_lib_export_apis(&base_native_symbol_defs); +static void +wasi_context_dtor(WASMModuleInstanceCommon *inst, void *ctx) +{ + if (ctx == NULL) { + return; + } + wasm_runtime_destroy_wasi(inst); +} +#endif /* end of WASM_ENABLE_LIBC_WASI */ + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 +static bool +quick_aot_entry_init(void); +#endif + +bool +wasm_native_init() +{ +#if WASM_ENABLE_SPEC_TEST != 0 || WASM_ENABLE_LIBC_BUILTIN != 0 \ + || 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_WASI_THREADS != 0 \ + || WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 \ + || WASM_ENABLE_SHARED_HEAP != 0 + NativeSymbol *native_symbols; + uint32 n_native_symbols; +#endif - if (base_native_symbol_len > 0) - sort_symbol_ptr(base_native_symbol_defs, base_native_symbol_len); +#if WASM_ENABLE_LIBC_BUILTIN != 0 + n_native_symbols = get_libc_builtin_export_apis(&native_symbols); + if (!wasm_native_register_natives("env", native_symbols, n_native_symbols)) + goto fail; +#endif /* WASM_ENABLE_LIBC_BUILTIN */ - is_base_lib_sorted = true; +#if WASM_ENABLE_SPEC_TEST + n_native_symbols = get_spectest_export_apis(&native_symbols); + if (!wasm_native_register_natives("spectest", native_symbols, + n_native_symbols)) + goto fail; +#endif /* WASM_ENABLE_SPEC_TEST */ + +#if WASM_ENABLE_LIBC_WASI != 0 + g_wasi_context_key = wasm_native_create_context_key(wasi_context_dtor); + if (g_wasi_context_key == NULL) { + goto fail; } + n_native_symbols = get_libc_wasi_export_apis(&native_symbols); + if (!wasm_native_register_natives("wasi_unstable", native_symbols, + n_native_symbols)) + goto fail; + if (!wasm_native_register_natives("wasi_snapshot_preview1", native_symbols, + n_native_symbols)) + goto fail; +#endif - if ((ret = lookup_symbol(base_native_symbol_defs, base_native_symbol_len, - func_name)) - || (func_name[0] == '_' - && (ret = lookup_symbol(base_native_symbol_defs, base_native_symbol_len, - func_name + 1)))) - return ret; +#if WASM_ENABLE_SHARED_HEAP != 0 + n_native_symbols = get_lib_shared_heap_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", native_symbols, + n_native_symbols)) + goto fail; +#endif - return NULL; +#if WASM_ENABLE_BASE_LIB != 0 + n_native_symbols = get_base_lib_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", native_symbols, + n_native_symbols)) + goto fail; +#endif + +#if WASM_ENABLE_APP_FRAMEWORK != 0 + n_native_symbols = get_ext_lib_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", native_symbols, + n_native_symbols)) + goto fail; +#endif + +#if WASM_ENABLE_LIB_PTHREAD != 0 + if (!lib_pthread_init()) + goto fail; + + n_native_symbols = get_lib_pthread_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", native_symbols, + n_native_symbols)) + 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 + && !wasm_native_register_natives("env", native_symbols, + n_native_symbols)) + goto fail; +#endif /* WASM_ENABLE_LIBC_EMCC */ + +#if WASM_ENABLE_LIB_RATS != 0 + n_native_symbols = get_lib_rats_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", native_symbols, + n_native_symbols)) + goto fail; +#endif /* WASM_ENABLE_LIB_RATS */ + +#if WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + if (!wasi_nn_initialize()) + goto fail; + + n_native_symbols = get_wasi_nn_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives( +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + "wasi_ephemeral_nn", +#else + "wasi_nn", +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + native_symbols, n_native_symbols)) + goto fail; +#endif /* WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + if (!quick_aot_entry_init()) { +#if WASM_ENABLE_SPEC_TEST != 0 || WASM_ENABLE_LIBC_BUILTIN != 0 \ + || 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_WASI_THREADS != 0 \ + || WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 \ + || WASM_ENABLE_SHARED_HEAP != 0 + goto fail; +#else + return false; +#endif + } +#endif + + return true; +#if WASM_ENABLE_SPEC_TEST != 0 || WASM_ENABLE_LIBC_BUILTIN != 0 \ + || 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_WASI_THREADS != 0 \ + || WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 \ + || WASM_ENABLE_SHARED_HEAP != 0 +fail: + wasm_native_destroy(); + return false; +#endif } -#endif /* end of WASM_ENABLE_BASE_LIB */ -static bool is_ext_lib_sorted = false; -static NativeSymbol *ext_native_symbol_defs; -static int ext_native_symbol_len; +void +wasm_native_destroy() +{ + NativeSymbolsNode *node, *node_next; -int -get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis); +#if WASM_ENABLE_LIBC_WASI != 0 + if (g_wasi_context_key != NULL) { + wasm_native_destroy_context_key(g_wasi_context_key); + g_wasi_context_key = NULL; + } +#endif + +#if WASM_ENABLE_LIB_PTHREAD != 0 + lib_pthread_destroy(); +#endif + +#if WASM_ENABLE_LIB_WASI_THREADS != 0 + lib_wasi_threads_destroy(); +#endif + +#if WASM_ENABLE_WASI_NN != 0 || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + wasi_nn_destroy(); +#endif + + node = g_native_symbols_list; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + + g_native_symbols_list = NULL; +} + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 +static void +invoke_no_args_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *) = func_ptr; + native_code(exec_env); + (void)argv; + (void)argv_ret; +} +static void +invoke_no_args_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *) = func_ptr; + argv_ret[0] = (uint32)native_code(exec_env); + (void)argv; +} +static void +invoke_no_args_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *) = func_ptr; + int64 ret = native_code(exec_env); + PUT_I64_TO_ADDR(argv_ret, ret); + (void)argv; +} + +static void +invoke_i_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32) = func_ptr; + native_code(exec_env, argv[0]); + (void)argv_ret; +} +static void +invoke_i_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0]); +} +static void +invoke_i_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32) = func_ptr; + int64 ret = native_code(exec_env, argv[0]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_I_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv)); + (void)argv_ret; +} +static void +invoke_I_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv)); +} +static void +invoke_I_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_ii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32) = func_ptr; + native_code(exec_env, argv[0], argv[1]); + (void)argv_ret; +} +static void +invoke_ii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1]); +} +static void +invoke_ii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1)); + (void)argv_ret; +} +static void +invoke_iI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64) = func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1)); +} +static void +invoke_iI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64) = func_ptr; + int64 ret = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_Ii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2]); + (void)argv_ret; +} +static void +invoke_Ii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32) = func_ptr; + argv_ret[0] = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2]); +} +static void +invoke_Ii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32) = func_ptr; + int64 ret = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_II_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2)); + (void)argv_ret; +} +static void +invoke_II_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2)); +} +static void +invoke_II_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int32) = func_ptr; + native_code(exec_env, argv[0], argv[1], argv[2]); + (void)argv_ret; +} +static void +invoke_iii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1], argv[2]); +} +static void +invoke_iii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int32) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1], argv[2]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iiI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int64) = func_ptr; + native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2)); + (void)argv_ret; +} +static void +invoke_iiI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2)); +} +static void +invoke_iiI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int64) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iIi_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64, int32) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3]); + (void)argv_ret; +} +static void +invoke_iIi_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], + GET_I64_FROM_ADDR((uint32 *)argv + 1), argv[3]); +} +static void +invoke_iIi_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64, int32) = func_ptr; + int64 ret = native_code(exec_env, argv[0], + GET_I64_FROM_ADDR((uint32 *)argv + 1), argv[3]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iII_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64, int64) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3)); + (void)argv_ret; +} +static void +invoke_iII_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64, int64) = func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3)); +} +static void +invoke_iII_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64, int64) = func_ptr; + int64 ret = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_Iii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], argv[3]); + (void)argv_ret; +} +static void +invoke_Iii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], argv[3]); +} +static void +invoke_Iii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32, int32) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], argv[3]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IiI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3)); + (void)argv_ret; +} +static void +invoke_IiI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], GET_I64_FROM_ADDR((uint32 *)argv + 3)); +} +static void +invoke_IiI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], GET_I64_FROM_ADDR((uint32 *)argv + 3)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IIi_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4]); + (void)argv_ret; +} +static void +invoke_IIi_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4]); +} +static void +invoke_IIi_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64, int32) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_III_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4)); + (void)argv_ret; +} +static void +invoke_III_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4)); +} +static void +invoke_III_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iiii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int32, int32) = func_ptr; + native_code(exec_env, argv[0], argv[1], argv[2], argv[3]); + (void)argv_ret; +} +static void +invoke_iiii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int32, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1], argv[2], argv[3]); +} +static void +invoke_iiii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int32, int32) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1], argv[2], argv[3]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iiiI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int32, int64) = func_ptr; + native_code(exec_env, argv[0], argv[1], argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3)); + (void)argv_ret; +} +static void +invoke_iiiI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int32, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1], argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3)); +} +static void +invoke_iiiI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int32, int64) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1], argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iiIi_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int64, int32) = func_ptr; + native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4]); + (void)argv_ret; +} +static void +invoke_iiIi_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int64, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4]); +} +static void +invoke_iiIi_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int64, int32) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iiII_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int64, int64) = func_ptr; + native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4)); + (void)argv_ret; +} +static void +invoke_iiII_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int64, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4)); +} +static void +invoke_iiII_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int64, int64) = func_ptr; + int64 ret = native_code(exec_env, argv[0], argv[1], + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iIii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64, int32, int32) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3], argv[4]); + (void)argv_ret; +} +static void +invoke_iIii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64, int32, int32) = func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3], argv[4]); +} +static void +invoke_iIii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64, int32, int32) = func_ptr; + int64 ret = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3], argv[4]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iIiI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64, int32, int64) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3], GET_I64_FROM_ADDR((uint32 *)argv + 4)); + (void)argv_ret; +} +static void +invoke_iIiI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64, int32, int64) = func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3], GET_I64_FROM_ADDR((uint32 *)argv + 4)); +} +static void +invoke_iIiI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64, int32, int64) = func_ptr; + int64 ret = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + argv[3], GET_I64_FROM_ADDR((uint32 *)argv + 4)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iIIi_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64, int64, int32) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3), argv[5]); + (void)argv_ret; +} +static void +invoke_iIIi_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64, int64, int32) = func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3), argv[5]); +} +static void +invoke_iIIi_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64, int64, int32) = func_ptr; + int64 ret = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3), argv[5]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iIII_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int64, int64, int64) = func_ptr; + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3), + GET_I64_FROM_ADDR((uint32 *)argv + 5)); + (void)argv_ret; +} +static void +invoke_iIII_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int64, int64, int64) = func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3), + GET_I64_FROM_ADDR((uint32 *)argv + 5)); +} +static void +invoke_iIII_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int64, int64, int64) = func_ptr; + int64 ret = + native_code(exec_env, argv[0], GET_I64_FROM_ADDR((uint32 *)argv + 1), + GET_I64_FROM_ADDR((uint32 *)argv + 3), + GET_I64_FROM_ADDR((uint32 *)argv + 5)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_Iiii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32, int32, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], argv[3], + argv[4]); + (void)argv_ret; +} +static void +invoke_Iiii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32, int32, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], argv[3], argv[4]); +} +static void +invoke_Iiii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32, int32, int32) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], argv[3], argv[4]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IiiI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32, int32, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], argv[3], + GET_I64_FROM_ADDR((uint32 *)argv + 4)); + (void)argv_ret; +} + +static void +invoke_IiiI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32, int32, int64) = func_ptr; + argv_ret[0] = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + argv[3], GET_I64_FROM_ADDR((uint32 *)argv + 4)); +} + +static void +invoke_IiiI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32, int32, int64) = func_ptr; + int64 ret = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + argv[3], GET_I64_FROM_ADDR((uint32 *)argv + 4)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IiIi_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32, int64, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3), argv[5]); + (void)argv_ret; +} +static void +invoke_IiIi_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32, int64, int32) = func_ptr; + argv_ret[0] = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3), argv[5]); +} +static void +invoke_IiIi_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32, int64, int32) = func_ptr; + int64 ret = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3), argv[5]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IiII_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int32, int64, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), argv[2], + GET_I64_FROM_ADDR((uint32 *)argv + 3), + GET_I64_FROM_ADDR((uint32 *)argv + 5)); + (void)argv_ret; +} +static void +invoke_IiII_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int32, int64, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], GET_I64_FROM_ADDR((uint32 *)argv + 3), + GET_I64_FROM_ADDR((uint32 *)argv + 5)); +} +static void +invoke_IiII_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int32, int64, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + argv[2], GET_I64_FROM_ADDR((uint32 *)argv + 3), + GET_I64_FROM_ADDR((uint32 *)argv + 5)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IIii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64, int32, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4], argv[5]); + (void)argv_ret; +} +static void +invoke_IIii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64, int32, int32) = func_ptr; + argv_ret[0] = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4], argv[5]); +} +static void +invoke_IIii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64, int32, int32) = func_ptr; + int64 ret = + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4], argv[5]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IIiI_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64, int32, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4], + GET_I64_FROM_ADDR((uint32 *)argv + 5)); + (void)argv_ret; +} +static void +invoke_IIiI_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64, int32, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4], + GET_I64_FROM_ADDR((uint32 *)argv + 5)); +} +static void +invoke_IIiI_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64, int32, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), argv[4], + GET_I64_FROM_ADDR((uint32 *)argv + 5)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IIIi_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64, int64, int32) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4), argv[6]); + (void)argv_ret; +} +static void +invoke_IIIi_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64, int64, int32) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4), argv[6]); +} +static void +invoke_IIIi_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64, int64, int32) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4), argv[6]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_IIII_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int64, int64, int64, int64) = func_ptr; + native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4), + GET_I64_FROM_ADDR((uint32 *)argv + 6)); + (void)argv_ret; +} +static void +invoke_IIII_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int64, int64, int64, int64) = func_ptr; + argv_ret[0] = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4), + GET_I64_FROM_ADDR((uint32 *)argv + 6)); +} +static void +invoke_IIII_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int64, int64, int64, int64) = func_ptr; + int64 ret = native_code(exec_env, GET_I64_FROM_ADDR((uint32 *)argv), + GET_I64_FROM_ADDR((uint32 *)argv + 2), + GET_I64_FROM_ADDR((uint32 *)argv + 4), + GET_I64_FROM_ADDR((uint32 *)argv + 6)); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +static void +invoke_iiiii_v(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + void (*native_code)(WASMExecEnv *, int32, int32, int32, int32, int32) = + func_ptr; + native_code(exec_env, argv[0], argv[1], argv[2], argv[3], argv[4]); + (void)argv_ret; +} +static void +invoke_iiiii_i(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int32 (*native_code)(WASMExecEnv *, int32, int32, int32, int32, int32) = + func_ptr; + argv_ret[0] = + native_code(exec_env, argv[0], argv[1], argv[2], argv[3], argv[4]); +} +static void +invoke_iiiii_I(void *func_ptr, void *exec_env, uint32 *argv, uint32 *argv_ret) +{ + int64 (*native_code)(WASMExecEnv *, int32, int32, int32, int32, int32) = + func_ptr; + int64 ret = + native_code(exec_env, argv[0], argv[1], argv[2], argv[3], argv[4]); + PUT_I64_TO_ADDR(argv_ret, ret); +} + +typedef struct QuickAOTEntry { + const char *signature; + void *func_ptr; +} QuickAOTEntry; + +/* clang-format off */ +static QuickAOTEntry quick_aot_entries[] = { + { "()v", invoke_no_args_v }, + { "()i", invoke_no_args_i }, + { "()I", invoke_no_args_I }, + + { "(i)v", invoke_i_v }, { "(i)i", invoke_i_i }, { "(i)I", invoke_i_I }, + { "(I)v", invoke_I_v }, { "(I)i", invoke_I_i }, { "(I)I", invoke_I_I }, + + { "(ii)v", invoke_ii_v }, { "(ii)i", invoke_ii_i }, { "(ii)I", invoke_ii_I }, + { "(iI)v", invoke_iI_v }, { "(iI)i", invoke_iI_i }, { "(iI)I", invoke_iI_I }, + { "(Ii)v", invoke_Ii_v }, { "(Ii)i", invoke_Ii_i }, { "(Ii)I", invoke_Ii_I }, + { "(II)v", invoke_II_v }, { "(II)i", invoke_II_i }, { "(II)I", invoke_II_I }, + + { "(iii)v", invoke_iii_v }, { "(iii)i", invoke_iii_i }, { "(iii)I", invoke_iii_I }, + { "(iiI)v", invoke_iiI_v }, { "(iiI)i", invoke_iiI_i }, { "(iiI)I", invoke_iiI_I }, + { "(iIi)v", invoke_iIi_v }, { "(iIi)i", invoke_iIi_i }, { "(iIi)I", invoke_iIi_I }, + { "(iII)v", invoke_iII_v }, { "(iII)i", invoke_iII_i }, { "(iII)I", invoke_iII_I }, + { "(Iii)v", invoke_Iii_v }, { "(Iii)i", invoke_Iii_i }, { "(Iii)I", invoke_Iii_I }, + { "(IiI)v", invoke_IiI_v }, { "(IiI)i", invoke_IiI_i }, { "(IiI)I", invoke_IiI_I }, + { "(IIi)v", invoke_IIi_v }, { "(IIi)i", invoke_IIi_i }, { "(IIi)I", invoke_IIi_I }, + { "(III)v", invoke_III_v }, { "(III)i", invoke_III_i }, { "(III)I", invoke_III_I }, + + { "(iiii)v", invoke_iiii_v }, { "(iiii)i", invoke_iiii_i }, { "(iiii)I", invoke_iiii_I }, + { "(iiiI)v", invoke_iiiI_v }, { "(iiiI)i", invoke_iiiI_i }, { "(iiiI)I", invoke_iiiI_I }, + { "(iiIi)v", invoke_iiIi_v }, { "(iiIi)i", invoke_iiIi_i }, { "(iiIi)I", invoke_iiIi_I }, + { "(iiII)v", invoke_iiII_v }, { "(iiII)i", invoke_iiII_i }, { "(iiII)I", invoke_iiII_I }, + { "(iIii)v", invoke_iIii_v }, { "(iIii)i", invoke_iIii_i }, { "(iIii)I", invoke_iIii_I }, + { "(iIiI)v", invoke_iIiI_v }, { "(iIiI)i", invoke_iIiI_i }, { "(iIiI)I", invoke_iIiI_I }, + { "(iIIi)v", invoke_iIIi_v }, { "(iIIi)i", invoke_iIIi_i }, { "(iIIi)I", invoke_iIIi_I }, + { "(iIII)v", invoke_iIII_v }, { "(iIII)i", invoke_iIII_i }, { "(iIII)I", invoke_iIII_I }, + { "(Iiii)v", invoke_Iiii_v }, { "(Iiii)i", invoke_Iiii_i }, { "(Iiii)I", invoke_Iiii_I }, + { "(IiiI)v", invoke_IiiI_v }, { "(IiiI)i", invoke_IiiI_i }, { "(IiiI)I", invoke_IiiI_I }, + { "(IiIi)v", invoke_IiIi_v }, { "(IiIi)i", invoke_IiIi_i }, { "(IiIi)I", invoke_IiIi_I }, + { "(IiII)v", invoke_IiII_v }, { "(IiII)i", invoke_IiII_i }, { "(IiII)I", invoke_IiII_I }, + { "(IIii)v", invoke_IIii_v }, { "(IIii)i", invoke_IIii_i }, { "(IIii)I", invoke_IIii_I }, + { "(IIiI)v", invoke_IIiI_v }, { "(IIiI)i", invoke_IIiI_i }, { "(IIiI)I", invoke_IIiI_I }, + { "(IIIi)v", invoke_IIIi_v }, { "(IIIi)i", invoke_IIIi_i }, { "(IIIi)I", invoke_IIIi_I }, + { "(IIII)v", invoke_IIII_v }, { "(IIII)i", invoke_IIII_i }, { "(IIII)I", invoke_IIII_I }, + + { "(iiiii)v", invoke_iiiii_v }, { "(iiiii)i", invoke_iiiii_i }, { "(iiiii)I", invoke_iiiii_I }, +}; +/* clang-format on */ + +static int +quick_aot_entry_cmp(const void *quick_aot_entry1, const void *quick_aot_entry2) +{ + return strcmp(((const QuickAOTEntry *)quick_aot_entry1)->signature, + ((const QuickAOTEntry *)quick_aot_entry2)->signature); +} + +static bool +quick_aot_entry_init(void) +{ + qsort(quick_aot_entries, sizeof(quick_aot_entries) / sizeof(QuickAOTEntry), + sizeof(QuickAOTEntry), quick_aot_entry_cmp); + + return true; +} void * -wasm_native_lookup_extension_lib_func(const char *module_name, - const char *func_name) +wasm_native_lookup_quick_aot_entry(const WASMFuncType *func_type) { - void *ret; + char signature[16] = { 0 }; + uint32 param_count = func_type->param_count; + uint32 result_count = func_type->result_count, i, j = 0; + const uint8 *types = func_type->types; + QuickAOTEntry *quick_aot_entry, key = { 0 }; - if (strcmp(module_name, "env")) + if (param_count > 5 || result_count > 1) return NULL; - if (!is_ext_lib_sorted) { - ext_native_symbol_len = get_ext_lib_export_apis(&ext_native_symbol_defs); + signature[j++] = '('; + + for (i = 0; i < param_count; i++) { + if (types[i] == VALUE_TYPE_I32) + signature[j++] = 'i'; + else if (types[i] == VALUE_TYPE_I64) + signature[j++] = 'I'; + else + return NULL; + } - if (ext_native_symbol_len > 0) - sort_symbol_ptr(ext_native_symbol_defs, ext_native_symbol_len); + signature[j++] = ')'; - is_ext_lib_sorted = true; + if (result_count == 0) { + signature[j++] = 'v'; + } + else { + if (types[i] == VALUE_TYPE_I32) + signature[j++] = 'i'; + else if (types[i] == VALUE_TYPE_I64) + signature[j++] = 'I'; + else + return NULL; } - if ((ret = lookup_symbol(ext_native_symbol_defs, ext_native_symbol_len, - func_name)) - || (func_name[0] == '_' - && (ret = lookup_symbol(ext_native_symbol_defs, ext_native_symbol_len, - func_name + 1)))) - return ret; + key.signature = signature; + if ((quick_aot_entry = + bsearch(&key, quick_aot_entries, + sizeof(quick_aot_entries) / sizeof(QuickAOTEntry), + sizeof(QuickAOTEntry), quick_aot_entry_cmp))) { + return quick_aot_entry->func_ptr; + } return NULL; } - +#endif /* end of WASM_ENABLE_QUICK_AOT_ENTRY != 0 */ diff --git a/core/iwasm/common/wasm_native.h b/core/iwasm/common/wasm_native.h index a8fdc3f7a5..9a6afee195 100644 --- a/core/iwasm/common/wasm_native.h +++ b/core/iwasm/common/wasm_native.h @@ -7,86 +7,111 @@ #define _WASM_NATIVE_H #include "bh_common.h" -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 +#include "../include/wasm_export.h" #include "../interpreter/wasm.h" -#endif #ifdef __cplusplus extern "C" { #endif -/** - * Lookup native function implementation of a given import function - * in libc builtin API's - * - * @param module_name the module name of the import function - * @param func_name the function name of the import function - * - * @return return the native function pointer if success, NULL otherwise - */ -void * -wasm_native_lookup_libc_builtin_func(const char *module_name, - const char *func_name); +typedef struct NativeSymbolsNode { + struct NativeSymbolsNode *next; + const char *module_name; + NativeSymbol *native_symbols; + uint32 n_native_symbols; + bool call_conv_raw; +} NativeSymbolsNode, *NativeSymbolsList; -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 /** * Lookup global variable of a given import global - * in libc builtin globals + * from libc builtin globals * * @param module_name the module name of the import global * @param global_name the global name of the import global * @param global return the global data * - * @param return true if success, false otherwise + * @param true if success, false otherwise */ bool wasm_native_lookup_libc_builtin_global(const char *module_name, const char *global_name, WASMGlobalImport *global); -#endif /** - * Lookup native function implementation of a given import function - * in libc wasi API's + * Resolve native symbol in all libraries, including libc-builtin, libc-wasi, + * base lib and extension lib, and user registered natives + * function, which can be auto checked by vm before calling native function * * @param module_name the module name of the import function * @param func_name the function name of the import function + * @param func_type the function prototype of the import function + * @param p_signature output the signature if resolve success * - * @return return the native function pointer if success, NULL otherwise + * @return the native function pointer if success, NULL otherwise */ void * -wasm_native_lookup_libc_wasi_func(const char *module_name, - const char *func_name); +wasm_native_resolve_symbol(const char *module_name, const char *field_name, + const WASMFuncType *func_type, + const char **p_signature, void **p_attachment, + bool *p_call_conv_raw); + +bool +wasm_native_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +bool +wasm_native_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +bool +wasm_native_unregister_natives(const char *module_name, + NativeSymbol *native_symbols); + +#if WASM_ENABLE_MODULE_INST_CONTEXT != 0 +struct WASMModuleInstanceCommon; -/** - * Lookup native function implementation of a given import function - * in base lib API's - * - * @param module_name the module name of the import function - * @param func_name the function name of the import function - * - * @return return the native function pointer if success, NULL otherwise - */ void * -wasm_native_lookup_base_lib_func(const char *module_name, - const char *func_name); +wasm_native_create_context_key( + void (*dtor)(struct WASMModuleInstanceCommon *inst, void *ctx)); -/** - * Lookup native function implementation of a given import function - * in extension lib API's - * - * @param module_name the module name of the import function - * @param func_name the function name of the import function - * - * @return return the native function pointer if success, NULL otherwise - */ +void +wasm_native_destroy_context_key(void *key); + +void +wasm_native_set_context(struct WASMModuleInstanceCommon *inst, void *key, + void *ctx); +void +wasm_native_set_context_spread(struct WASMModuleInstanceCommon *inst, void *key, + void *ctx); void * -wasm_native_lookup_extension_lib_func(const char *module_name, - const char *func_name); +wasm_native_get_context(struct WASMModuleInstanceCommon *inst, void *key); + +void +wasm_native_call_context_dtors(struct WASMModuleInstanceCommon *inst); + +void +wasm_native_inherit_contexts(struct WASMModuleInstanceCommon *child, + struct WASMModuleInstanceCommon *parent); +#else /* WASM_ENABLE_MODULE_INST_CONTEXT */ +#define wasm_native_call_context_dtors(inst) (void)(inst) +#define wasm_native_inherit_contexts(child, parent) (void)(parent) +#endif /* WASM_ENABLE_MODULE_INST_CONTEXT */ + +bool +wasm_native_init(void); + +void +wasm_native_destroy(void); + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 +void * +wasm_native_lookup_quick_aot_entry(const WASMFuncType *func_type); +#endif #ifdef __cplusplus } #endif #endif /* end of _WASM_NATIVE_H */ - diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 4727fd511a..1a16af4196 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -3,18 +3,106 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#include "config.h" #include "bh_platform.h" #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 #include "../interpreter/wasm_runtime.h" #endif #if WASM_ENABLE_AOT != 0 #include "../aot/aot_runtime.h" +#if WASM_ENABLE_DEBUG_AOT != 0 +#include "../aot/debug/jit_debug.h" #endif +#endif +#if WASM_ENABLE_GC != 0 +#include "gc/gc_object.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#if WASM_ENABLE_DEBUG_INTERP != 0 +#include "../libraries/debug-engine/debug_engine.h" +#endif +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#endif +#if WASM_ENABLE_FAST_JIT != 0 +#include "../fast-jit/jit_compiler.h" +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 +#include "../compilation/aot_llvm.h" +#endif +#include "../common/wasm_c_api_internal.h" +#include "../../version.h" + +/** + * For runtime build, BH_MALLOC/BH_FREE should be defined as + * wasm_runtime_malloc/wasm_runtime_free. + */ +#define CHECK(a) CHECK1(a) +#define CHECK1(a) SHOULD_BE_##a + +#define SHOULD_BE_wasm_runtime_malloc 1 +#if !CHECK(BH_MALLOC) +#error unexpected BH_MALLOC +#endif +#undef SHOULD_BE_wasm_runtime_malloc + +#define SHOULD_BE_wasm_runtime_free 1 +#if !CHECK(BH_FREE) +#error unexpected BH_FREE +#endif +#undef SHOULD_BE_wasm_runtime_free + +#undef CHECK +#undef CHECK1 + +#if WASM_ENABLE_MULTI_MODULE != 0 +/** + * A safety insurance to prevent + * circular dependencies which leads stack overflow + * try to break early + */ +typedef struct LoadingModule { + bh_list_link l; + /* point to a string pool */ + const char *module_name; +} LoadingModule; + +static bh_list loading_module_list_head; +static bh_list *const loading_module_list = &loading_module_list_head; +static korp_mutex loading_module_list_lock; + +/** + * A list to store all exported functions/globals/memories/tables + * of every fully loaded module + */ +static bh_list registered_module_list_head; +static bh_list *const registered_module_list = ®istered_module_list_head; +static korp_mutex registered_module_list_lock; +static void +wasm_runtime_destroy_registered_module_list(void); +#endif /* WASM_ENABLE_MULTI_MODULE */ + +#define E_TYPE_XIP 4 + +static uint8 +val_type_to_val_kind(uint8 value_type); + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 +/* Initialize externref hashmap */ +static bool +wasm_externref_map_init(void); + +/* Destroy externref hashmap */ +static void +wasm_externref_map_destroy(void); +#endif /* end of WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 */ static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) @@ -23,1798 +111,8059 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) snprintf(error_buf, error_buf_size, "%s", string); } -bool -wasm_runtime_init() +static void * +runtime_malloc(uint64 size, WASMModuleInstanceCommon *module_inst, + char *error_buf, uint32 error_buf_size) { - if (bh_platform_init() != 0) - return false; - - if (bh_log_init() != 0) - return false; + void *mem; - if (vm_thread_sys_init() != 0) - return false; + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + if (module_inst != NULL) { + wasm_runtime_set_exception(module_inst, "allocate memory failed"); + } + else if (error_buf != NULL) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + } + return NULL; + } - return true; + memset(mem, 0, (uint32)size); + return mem; } -void -wasm_runtime_destroy() -{ - vm_thread_sys_destroy(); -} +#if WASM_ENABLE_MULTI_MODULE != 0 +/* TODO: Let loader_malloc be a general API both for AOT and WASM. */ -PackageType -get_package_type(const uint8 *buf, uint32 size) +#define loader_malloc(size, error_buf, error_buf_size) \ + runtime_malloc(size, NULL, error_buf, error_buf_size) + +static void +set_error_buf_v(const WASMModuleCommon *module, char *error_buf, + uint32 error_buf_size, const char *format, ...) { - if (buf && size >= 4) { - if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 's' && buf[3] == 'm') - return Wasm_Module_Bytecode; - if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 'o' && buf[3] == 't') - return Wasm_Module_AoT; + va_list args; + char buf[128]; + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (module->module_type == Wasm_Module_AoT) { + snprintf(error_buf, error_buf_size, "AOT module load failed: %s", + buf); + } + else if (module->module_type == Wasm_Module_Bytecode) { + snprintf(error_buf, error_buf_size, "WASM module load failed: %s", + buf); + } } - return Package_Type_Unknown; } +#endif -WASMModuleCommon * -wasm_runtime_load(const uint8 *buf, uint32 size, - char *error_buf, uint32 error_buf_size) -{ - if (get_package_type(buf, size) == Wasm_Module_Bytecode) { -#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_JIT != 0 - AOTModule *aot_module; - WASMModule *module = wasm_load(buf, size, error_buf, error_buf_size); - if (!module) - return NULL; +#if WASM_ENABLE_FAST_JIT != 0 +static JitCompOptions jit_options = { 0 }; +#endif - if (!(aot_module = aot_convert_wasm_module(module, - error_buf, error_buf_size))) { - wasm_unload(module); - return NULL; - } - return (WASMModuleCommon*)aot_module; -#elif WASM_ENABLE_INTERP != 0 - return (WASMModuleCommon*) - wasm_load(buf, size, error_buf, error_buf_size); +#if WASM_ENABLE_JIT != 0 +/* opt_level: 3, size_level: 3, segue-flags: 0, + quick_invoke_c_api_import: false */ +static LLVMJITOptions llvm_jit_options = { 3, 3, 0, false }; #endif + +#if WASM_ENABLE_GC != 0 +static uint32 gc_heap_size_default = GC_HEAP_SIZE_DEFAULT; +#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 + of signal handler */ +static os_thread_local_attribute WASMExecEnv *exec_env_tls = NULL; + +static bool +is_sig_addr_in_guard_pages(void *sig_addr, WASMModuleInstance *module_inst) +{ + WASMMemoryInstance *memory_inst; +#if WASM_ENABLE_SHARED_HEAP != 0 + WASMSharedHeap *shared_heap; +#endif + uint8 *mapped_mem_start_addr = NULL; + uint8 *mapped_mem_end_addr = NULL; + uint32 i; + + for (i = 0; i < module_inst->memory_count; ++i) { + /* To be compatible with multi memory, get the ith memory instance */ + memory_inst = wasm_get_memory_with_idx(module_inst, i); + mapped_mem_start_addr = memory_inst->memory_data; + mapped_mem_end_addr = memory_inst->memory_data + 8 * (uint64)BH_GB; + if (mapped_mem_start_addr <= (uint8 *)sig_addr + && (uint8 *)sig_addr < mapped_mem_end_addr) { + /* The address which causes segmentation fault is inside + the memory instance's guard regions */ + return true; + } } - else if (get_package_type(buf, size) == Wasm_Module_AoT) { -#if WASM_ENABLE_AOT != 0 - return (WASMModuleCommon*) - aot_load_from_aot_file(buf, size, error_buf, error_buf_size); -#endif /* end of WASM_ENABLE_AOT */ +#if WASM_ENABLE_SHARED_HEAP != 0 + shared_heap = + wasm_runtime_get_shared_heap((WASMModuleInstanceCommon *)module_inst); + if (shared_heap) { + mapped_mem_start_addr = shared_heap->base_addr; + mapped_mem_end_addr = shared_heap->base_addr + 8 * (uint64)BH_GB; + if (mapped_mem_start_addr <= (uint8 *)sig_addr + && (uint8 *)sig_addr < mapped_mem_end_addr) { + /* The address which causes segmentation fault is inside + the shared heap's guard regions */ + return true; + } } +#endif - if (size < 4) - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: unexpected end"); - else - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: magic header not detected"); - return NULL; + return false; } -WASMModuleCommon * -wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, - char *error_buf, uint32_t error_buf_size) +#ifndef BH_PLATFORM_WINDOWS +static void +runtime_signal_handler(void *sig_addr) { -#if WASM_ENABLE_INTERP != 0 - if (!is_aot) - return (WASMModuleCommon*) - wasm_load_from_sections(section_list, - error_buf, error_buf_size); + WASMModuleInstance *module_inst; + WASMJmpBuf *jmpbuf_node; + uint32 page_size = os_getpagesize(); +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint8 *stack_min_addr; + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; #endif -#if WASM_ENABLE_AOT != 0 - if (is_aot) - return (WASMModuleCommon*) - aot_load_from_sections(section_list, - error_buf, error_buf_size); + + /* Check whether current thread is running wasm function */ + if (exec_env_tls && exec_env_tls->handle == os_self_thread() + && (jmpbuf_node = exec_env_tls->jmpbuf_stack_top)) { + /* Get mapped mem info of current instance */ + module_inst = (WASMModuleInstance *)exec_env_tls->module_inst; + +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + /* Get stack info of current thread */ + stack_min_addr = os_thread_get_stack_boundary(); #endif - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: invalid section list type"); - return NULL; + if (is_sig_addr_in_guard_pages(sig_addr, module_inst)) { + wasm_set_exception(module_inst, "out of bounds memory access"); + os_longjmp(jmpbuf_node->jmpbuf, 1); + } +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + else if (stack_min_addr <= (uint8 *)sig_addr + && (uint8 *)sig_addr + < stack_min_addr + page_size * guard_page_count) { + /* The address which causes segmentation fault is inside + native thread's guard page */ + wasm_set_exception(module_inst, "native stack overflow"); + os_longjmp(jmpbuf_node->jmpbuf, 1); + } +#endif + 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_copy_exception(module_inst, NULL)); + os_longjmp(jmpbuf_node->jmpbuf, 1); + } + } } +#else /* else of BH_PLATFORM_WINDOWS */ -void -wasm_runtime_unload(WASMModuleCommon *module) +#if WASM_ENABLE_AOT != 0 +#include + +static uint32 +decode_insn(uint8 *insn) { -#if WASM_ENABLE_INTERP != 0 - if (module->module_type == Wasm_Module_Bytecode) { - wasm_unload((WASMModule*)module); - return; - } + uint8 *data = (uint8 *)insn; + uint32 length = 32; /* reserve enough size */ + + /* Initialize decoder context */ + ZydisDecoder decoder; + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, + ZYDIS_STACK_WIDTH_64); + + /* Initialize formatter */ + ZydisFormatter formatter; + ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); + + /* Loop over the instructions in our buffer */ + ZyanU64 runtime_address = (ZyanU64)(uintptr_t)data; + ZyanUSize offset = 0; + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE]; + char buffer[256]; + + if (ZYAN_SUCCESS(ZydisDecoderDecodeFull( + &decoder, data + offset, length - offset, &instruction, operands, + ZYDIS_MAX_OPERAND_COUNT_VISIBLE, + ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY))) { + + /* Format & print the binary instruction structure to + human readable format */ + ZydisFormatterFormatInstruction(&formatter, &instruction, operands, + instruction.operand_count_visible, + buffer, sizeof(buffer), + runtime_address); + +#if 0 + /* Print current instruction */ + os_printf("%012" PRIX64 " ", runtime_address); + puts(buffer); #endif -#if WASM_ENABLE_AOT != 0 - if (module->module_type == Wasm_Module_AoT) { - aot_unload((AOTModule*)module); - return; + + return instruction.length; } -#endif + + /* Decode failed */ + return 0; } +#endif /* end of WASM_ENABLE_AOT != 0 */ -WASMModuleInstanceCommon * -wasm_runtime_instantiate(WASMModuleCommon *module, - uint32 stack_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size) +static LONG +next_action(WASMModuleInstance *module_inst, EXCEPTION_POINTERS *exce_info) { -#if WASM_ENABLE_INTERP != 0 - if (module->module_type == Wasm_Module_Bytecode) - return (WASMModuleInstanceCommon*) - wasm_instantiate((WASMModule*)module, - stack_size, heap_size, - error_buf, error_buf_size); +#if WASM_ENABLE_AOT != 0 + uint32 insn_size; #endif + + if (module_inst->module_type == Wasm_Module_Bytecode + && module_inst->e->running_mode == Mode_Interp) { + /* Continue to search next exception handler for + interpreter mode as it can be caught by + `__try { .. } __except { .. }` sentences in + wasm_runtime.c */ + return EXCEPTION_CONTINUE_SEARCH; + } + #if WASM_ENABLE_AOT != 0 - if (module->module_type == Wasm_Module_AoT) - return (WASMModuleInstanceCommon*) - aot_instantiate((AOTModule*)module, - stack_size, heap_size, - error_buf, error_buf_size); + /* Skip current instruction and continue to run for AOT/JIT mode. + TODO: implement unwind support for AOT/JIT code in Windows platform */ + insn_size = decode_insn((uint8 *)exce_info->ContextRecord->Rip); + if (insn_size > 0) { + exce_info->ContextRecord->Rip += insn_size; + return EXCEPTION_CONTINUE_EXECUTION; + } #endif - set_error_buf(error_buf, error_buf_size, - "Instantiate module failed, invalid module type"); - return NULL; + /* return different value from EXCEPTION_CONTINUE_SEARCH (= 0) + and EXCEPTION_CONTINUE_EXECUTION (= -1) */ + return -2; } -void -wasm_runtime_deinstantiate(WASMModuleInstanceCommon *module_inst) +static LONG +runtime_exception_handler(EXCEPTION_POINTERS *exce_info) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - wasm_deinstantiate((WASMModuleInstance*)module_inst); - return; + PEXCEPTION_RECORD ExceptionRecord = exce_info->ExceptionRecord; + uint8 *sig_addr = (uint8 *)ExceptionRecord->ExceptionInformation[1]; + WASMModuleInstance *module_inst; + WASMJmpBuf *jmpbuf_node; + uint8 *mapped_mem_start_addr = NULL; + uint8 *mapped_mem_end_addr = NULL; + uint32 page_size = os_getpagesize(); + LONG ret; + + if (exec_env_tls && exec_env_tls->handle == os_self_thread() + && (jmpbuf_node = exec_env_tls->jmpbuf_stack_top)) { + module_inst = (WASMModuleInstance *)exec_env_tls->module_inst; + if (ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + if (is_sig_addr_in_guard_pages(sig_addr, module_inst)) { + /* The address which causes segmentation fault is inside + the memory instance's guard regions. + Set exception and let the wasm func continue to run, when + the wasm func returns, the caller will check whether the + exception is thrown and return to runtime. */ + wasm_set_exception(module_inst, "out of bounds memory access"); + ret = next_action(module_inst, exce_info); + if (ret == EXCEPTION_CONTINUE_SEARCH + || ret == EXCEPTION_CONTINUE_EXECUTION) + return ret; + } + 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_copy_exception(module_inst, NULL)); + ret = next_action(module_inst, exce_info); + if (ret == EXCEPTION_CONTINUE_SEARCH + || ret == EXCEPTION_CONTINUE_EXECUTION) + return ret; + } + } +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + else if (ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { + /* Set stack overflow exception and let the wasm func continue + to run, when the wasm func returns, the caller will check + whether the exception is thrown and return to runtime, and + the damaged stack will be recovered by _resetstkoflw(). */ + wasm_set_exception(module_inst, "native stack overflow"); + ret = next_action(module_inst, exce_info); + if (ret == EXCEPTION_CONTINUE_SEARCH + || ret == EXCEPTION_CONTINUE_EXECUTION) + return ret; + } +#endif + else { + LOG_WARNING("Unhandled exception thrown: exception code: 0x%lx, " + "exception address: %p, exception information: %p\n", + ExceptionRecord->ExceptionCode, + ExceptionRecord->ExceptionAddress, sig_addr); + } } + + return EXCEPTION_CONTINUE_SEARCH; +} +#endif /* end of BH_PLATFORM_WINDOWS */ + +#ifdef BH_PLATFORM_WINDOWS +static PVOID runtime_exception_handler_handle = NULL; +static int32 runtime_exception_handler_ref_count = 0; +static korp_mutex runtime_exception_handler_lock = NULL; #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - aot_deinstantiate((AOTModuleInstance*)module_inst); - return; + +static bool +runtime_signal_init() +{ +#ifndef BH_PLATFORM_WINDOWS + return os_thread_signal_init(runtime_signal_handler) == 0 ? true : false; +#else + os_mutex_lock(&runtime_exception_handler_lock); + + if (os_thread_signal_init() != 0) { + os_mutex_unlock(&runtime_exception_handler_lock); + return false; + } + + if (runtime_exception_handler_ref_count == 0) { + runtime_exception_handler_handle = + AddVectoredExceptionHandler(1, runtime_exception_handler); } + + if (!runtime_exception_handler_handle) { + os_thread_signal_destroy(); + os_mutex_unlock(&runtime_exception_handler_lock); + return false; + } + + runtime_exception_handler_ref_count++; + + os_mutex_unlock(&runtime_exception_handler_lock); #endif + return true; } -WASMExecEnv * -wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, - uint32 stack_size) +static void +runtime_signal_destroy() { - return wasm_exec_env_create(module_inst, stack_size); +#ifdef BH_PLATFORM_WINDOWS + os_mutex_lock(&runtime_exception_handler_lock); + + if (runtime_exception_handler_ref_count > 0) { + runtime_exception_handler_ref_count--; + } + + if (runtime_exception_handler_ref_count == 0 + && runtime_exception_handler_handle) { + if (RemoveVectoredExceptionHandler(runtime_exception_handler_handle)) { + runtime_exception_handler_handle = NULL; + } + else { + /* Keep the handle so future init/destroy cycles can retry remove. + * Clearing it here may leave a live callback registered forever. */ + runtime_exception_handler_ref_count = 1; + } + } + + os_mutex_unlock(&runtime_exception_handler_lock); +#endif + os_thread_signal_destroy(); } void -wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env) +wasm_runtime_set_exec_env_tls(WASMExecEnv *exec_env) { - wasm_exec_env_destroy(exec_env); + exec_env_tls = exec_env; } -WASMModuleInstanceCommon * -wasm_runtime_get_module_inst(WASMExecEnv *exec_env) +WASMExecEnv * +wasm_runtime_get_exec_env_tls() { - return wasm_exec_env_get_module_inst(exec_env); + return exec_env_tls; } +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ -WASMFunctionInstanceCommon * -wasm_runtime_lookup_function(const WASMModuleInstanceCommon *module_inst, - const char *name, - const char *signature) +static bool +wasm_runtime_env_init(void) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return (WASMFunctionInstanceCommon*) - wasm_lookup_function((const WASMModuleInstance*)module_inst, - name, signature); -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return (WASMFunctionInstanceCommon*) - aot_lookup_function((const AOTModuleInstance*)module_inst, - name, signature); -#endif - return NULL; -} - -bool -wasm_runtime_call_wasm(WASMExecEnv *exec_env, - WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]) -{ - if (!exec_env - || !exec_env->module_inst - || exec_env->wasm_stack_size == 0 - || exec_env->wasm_stack.s.top_boundary != - exec_env->wasm_stack.s.bottom + exec_env->wasm_stack_size - || exec_env->wasm_stack.s.top > exec_env->wasm_stack.s.top_boundary) { - LOG_ERROR("Invalid exec env stack info."); + if (bh_platform_init() != 0) return false; + + if (wasm_native_init() == false) { + goto fail1; } - exec_env->handle = vm_self_thread(); +#if WASM_ENABLE_MULTI_MODULE + if (BHT_OK != os_mutex_init(®istered_module_list_lock)) { + goto fail2; + } -#if WASM_ENABLE_INTERP != 0 - if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) - return wasm_call_function(exec_env, - (WASMFunctionInstance*)function, - argc, argv); -#endif -#if WASM_ENABLE_AOT != 0 - if (exec_env->module_inst->module_type == Wasm_Module_AoT) - return aot_call_function(exec_env, - (AOTFunctionInstance*)function, - argc, argv); + if (BHT_OK != os_mutex_init(&loading_module_list_lock)) { + goto fail3; + } #endif - return false; -} -bool -wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, - WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_create_exec_env_and_call_function( - (WASMModuleInstance*)module_inst, - (WASMFunctionInstance*)function, - argc, argv); +#if WASM_ENABLE_SHARED_MEMORY + if (!wasm_shared_memory_init()) { + goto fail4; + } #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_create_exec_env_and_call_function( - (AOTModuleInstance*)module_inst, - (AOTFunctionInstance*)function, - argc, argv); + +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0) + if (!thread_manager_init()) { + goto fail5; + } #endif - return false; -} -void -wasm_runtime_set_exception(WASMModuleInstanceCommon *module_inst, - const char *exception) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - wasm_set_exception((WASMModuleInstance*)module_inst, exception); - return; +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!runtime_signal_init()) { + goto fail6; } #endif + #if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - aot_set_exception((AOTModuleInstance*)module_inst, exception); - return; +#if WASM_ENABLE_DEBUG_AOT != 0 + if (!jit_debug_engine_init()) { + goto fail7; } #endif -} +#endif -const char* -wasm_runtime_get_exception(WASMModuleInstanceCommon *module_inst) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - return wasm_get_exception((WASMModuleInstance*)module_inst); +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + if (!wasm_externref_map_init()) { + goto fail8; } #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - return aot_get_exception((AOTModuleInstance*)module_inst); + +#if WASM_ENABLE_FAST_JIT != 0 + if (!jit_compiler_init(&jit_options)) { + goto fail9; } #endif - return NULL; -} -void -wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst) -{ - wasm_runtime_set_exception(module_inst, NULL); -} - -void -wasm_runtime_set_custom_data(WASMModuleInstanceCommon *module_inst, - void *custom_data) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - ((WASMModuleInstance*)module_inst)->custom_data = custom_data; - return; +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + if (!aot_compiler_init()) { + goto fail10; } #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - ((AOTModuleInstance*)module_inst)->custom_data.ptr = custom_data; - return; + +#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP) + if (os_blocking_op_init() != BHT_OK) { + goto fail11; } + os_end_blocking_op(); #endif -} -void* -wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return ((WASMModuleInstance*)module_inst)->custom_data; -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return ((AOTModuleInstance*)module_inst)->custom_data.ptr; + return true; + +#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP) +fail11: +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + aot_compiler_destroy(); +#endif +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 +fail10: +#if WASM_ENABLE_FAST_JIT != 0 + jit_compiler_destroy(); #endif - return NULL; -} - -int32 -wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_module_malloc((WASMModuleInstance*)module_inst, size); +#endif +#if WASM_ENABLE_FAST_JIT != 0 +fail9: +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + wasm_externref_map_destroy(); +#endif +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 +fail8: #endif #if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_module_malloc((AOTModuleInstance*)module_inst, size); +#if WASM_ENABLE_DEBUG_AOT != 0 + jit_debug_engine_destroy(); +fail7: #endif - return 0; +#endif +#ifdef OS_ENABLE_HW_BOUND_CHECK + runtime_signal_destroy(); +fail6: +#endif +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0) + thread_manager_destroy(); +fail5: +#endif +#if WASM_ENABLE_SHARED_MEMORY + wasm_shared_memory_destroy(); +fail4: +#endif +#if WASM_ENABLE_MULTI_MODULE + os_mutex_destroy(&loading_module_list_lock); +fail3: + os_mutex_destroy(®istered_module_list_lock); +fail2: +#endif + wasm_native_destroy(); +fail1: + bh_platform_destroy(); + + return false; } -void -wasm_runtime_module_free(WASMModuleInstanceCommon *module_inst, int32 ptr) +static bool +wasm_runtime_exec_env_check(WASMExecEnv *exec_env) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - wasm_module_free((WASMModuleInstance*)module_inst, ptr); - return; - } + return exec_env && exec_env->module_inst + && exec_env->wasm_stack.top_boundary + == exec_env->wasm_stack.bottom + exec_env->wasm_stack_size + && exec_env->wasm_stack.top <= exec_env->wasm_stack.top_boundary; +} + +#if defined(OS_THREAD_MUTEX_INITIALIZER) +/** + * lock for wasm_runtime_init/wasm_runtime_full_init and runtime_ref_count + * Note: if the platform has mutex initializer, we use a global lock to + * lock the operations of runtime init/full_init, otherwise when there are + * operations happening simultaneously in multiple threads, developer + * must create the lock by himself, and use it to lock the operations + */ +static korp_mutex runtime_lock = OS_THREAD_MUTEX_INITIALIZER; #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - aot_module_free((AOTModuleInstance*)module_inst, ptr); - return; +static int32 runtime_ref_count = 0; + +static bool +wasm_runtime_init_internal(void) +{ + if (!wasm_runtime_memory_init(Alloc_With_System_Allocator, NULL)) + return false; + + if (!wasm_runtime_env_init()) { + wasm_runtime_memory_destroy(); + return false; } -#endif + + return true; } -int32 -wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, - const char *src, uint32 size) +bool +wasm_runtime_init() { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - return wasm_module_dup_data((WASMModuleInstance*)module_inst, src, size); - } + bool ret = true; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&runtime_lock); #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - return aot_module_dup_data((AOTModuleInstance*)module_inst, src, size); + + bh_assert(runtime_ref_count >= 0); + if (runtime_ref_count == 0) { + ret = wasm_runtime_init_internal(); + } + if (ret) { + runtime_ref_count++; } + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&runtime_lock); #endif - return 0; + + return ret; } -bool -wasm_runtime_validate_app_addr(WASMModuleInstanceCommon *module_inst, - int32 app_offset, uint32 size) +static void +wasm_runtime_destroy_internal(void) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_validate_app_addr((WASMModuleInstance*)module_inst, - app_offset, size); +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + wasm_externref_map_destroy(); #endif + #if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_validate_app_addr((AOTModuleInstance*)module_inst, - app_offset, size); +#if WASM_ENABLE_DEBUG_AOT != 0 + jit_debug_engine_destroy(); +#endif #endif - return false; -} -bool -wasm_runtime_validate_app_str_addr(WASMModuleInstanceCommon *module_inst, - int32 app_str_offset) -{ - int32 app_end_offset; - char *str, *str_end; +#ifdef OS_ENABLE_HW_BOUND_CHECK + runtime_signal_destroy(); +#endif - if (!wasm_runtime_get_app_addr_range(module_inst, app_str_offset, - NULL, &app_end_offset)) - goto fail; + /* runtime env destroy */ +#if WASM_ENABLE_MULTI_MODULE + wasm_runtime_destroy_loading_module_list(); + os_mutex_destroy(&loading_module_list_lock); - str = wasm_runtime_addr_app_to_native(module_inst, app_str_offset); - str_end = str + (app_end_offset - app_str_offset); - while (str < str_end && *str != '\0') - str++; - if (str == str_end) - goto fail; - return true; + wasm_runtime_destroy_registered_module_list(); + os_mutex_destroy(®istered_module_list_lock); +#endif -fail: - wasm_runtime_set_exception(module_inst, "out of bounds memory access"); - return false; -} +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + /* Destroy LLVM-JIT compiler after destroying the modules + * loaded by multi-module feature, since these modules may + * create backend threads to compile the wasm functions, + * which may access the LLVM resources. We wait until they + * finish the compilation to avoid accessing the destroyed + * resources in the compilation threads. + */ + aot_compiler_destroy(); +#endif -bool -wasm_runtime_validate_native_addr(WASMModuleInstanceCommon *module_inst, - void *native_ptr, uint32 size) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_validate_native_addr((WASMModuleInstance*)module_inst, - native_ptr, size); +#if WASM_ENABLE_FAST_JIT != 0 + /* Destroy Fast-JIT compiler after destroying the modules + * loaded by multi-module feature, since the Fast JIT's + * code cache allocator may be used by these modules. + */ + jit_compiler_destroy(); #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_validate_native_addr((AOTModuleInstance*)module_inst, - native_ptr, size); + +#if WASM_ENABLE_SHARED_MEMORY + wasm_shared_memory_destroy(); #endif - return false; -} -void * -wasm_runtime_addr_app_to_native(WASMModuleInstanceCommon *module_inst, - int32 app_offset) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_addr_app_to_native((WASMModuleInstance*)module_inst, - app_offset); +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0) +#if WASM_ENABLE_DEBUG_INTERP != 0 + wasm_debug_engine_destroy(); #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_addr_app_to_native((AOTModuleInstance*)module_inst, - app_offset); + thread_manager_destroy(); #endif - return NULL; + + wasm_native_destroy(); + bh_platform_destroy(); + + wasm_runtime_memory_destroy(); } -int32 -wasm_runtime_addr_native_to_app(WASMModuleInstanceCommon *module_inst, - void *native_ptr) +void +wasm_runtime_destroy() { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_addr_native_to_app((WASMModuleInstance*)module_inst, - native_ptr); +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&runtime_lock); #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_addr_native_to_app((AOTModuleInstance*)module_inst, - native_ptr); + + bh_assert(runtime_ref_count > 0); + runtime_ref_count--; + if (runtime_ref_count == 0) { + wasm_runtime_destroy_internal(); + } + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&runtime_lock); #endif - return 0; } -bool -wasm_runtime_get_app_addr_range(WASMModuleInstanceCommon *module_inst, - int32 app_offset, - int32 *p_app_start_offset, - int32 *p_app_end_offset) +RunningMode +wasm_runtime_get_default_running_mode(void) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_get_app_addr_range((WASMModuleInstance*)module_inst, - app_offset, p_app_start_offset, - p_app_end_offset); -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_get_app_addr_range((AOTModuleInstance*)module_inst, - app_offset, p_app_start_offset, - p_app_end_offset); -#endif - return false; + return runtime_running_mode; } -bool -wasm_runtime_get_native_addr_range(WASMModuleInstanceCommon *module_inst, - uint8_t *native_ptr, - uint8_t **p_native_start_addr, - uint8_t **p_native_end_addr) +#if WASM_ENABLE_JIT != 0 +LLVMJITOptions * +wasm_runtime_get_llvm_jit_options(void) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_get_native_addr_range((WASMModuleInstance*)module_inst, - native_ptr, p_native_start_addr, - p_native_end_addr); -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return aot_get_native_addr_range((AOTModuleInstance*)module_inst, - native_ptr, p_native_start_addr, - p_native_end_addr); -#endif - return false; + return &llvm_jit_options; } +#endif +#if WASM_ENABLE_GC != 0 uint32 -wasm_runtime_get_temp_ret(WASMModuleInstanceCommon *module_inst) +wasm_runtime_get_gc_heap_size_default(void) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return ((WASMModuleInstance*)module_inst)->temp_ret; -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return ((AOTModuleInstance*)module_inst)->temp_ret; -#endif - return 0; + return gc_heap_size_default; } +#endif -void -wasm_runtime_set_temp_ret(WASMModuleInstanceCommon *module_inst, - uint32 temp_ret) +static bool +wasm_runtime_full_init_internal(RuntimeInitArgs *init_args) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - ((WASMModuleInstance*)module_inst)->temp_ret = temp_ret; - return; + if (!wasm_runtime_memory_init(init_args->mem_alloc_type, + &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_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - ((AOTModuleInstance*)module_inst)->temp_ret = temp_ret; - return; + +#if WASM_ENABLE_GC != 0 + uint32 gc_heap_size = init_args->gc_heap_size; + if (gc_heap_size > 0) { + gc_heap_size_default = gc_heap_size; } #endif -} -uint32 -wasm_runtime_get_llvm_stack(WASMModuleInstanceCommon *module_inst) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return ((WASMModuleInstance*)module_inst)->llvm_stack; +#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; + llvm_jit_options.segue_flags = init_args->segue_flags; #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return ((AOTModuleInstance*)module_inst)->llvm_stack; + +#if WASM_ENABLE_LINUX_PERF != 0 + wasm_runtime_set_linux_perf(init_args->enable_linux_perf); +#else + if (init_args->enable_linux_perf) + LOG_WARNING("warning: to enable linux perf support, please recompile " + "with -DWAMR_BUILD_LINUX_PERF=1"); #endif - return 0; -} -void -wasm_runtime_set_llvm_stack(WASMModuleInstanceCommon *module_inst, - uint32 llvm_stack) -{ -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - ((WASMModuleInstance*)module_inst)->llvm_stack = llvm_stack; - return; + if (!wasm_runtime_env_init()) { + wasm_runtime_memory_destroy(); + return false; } + +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (strlen(init_args->ip_addr)) + if (!wasm_debug_engine_init(init_args->ip_addr, + init_args->instance_port)) { + wasm_runtime_destroy(); + return false; + } #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - ((AOTModuleInstance*)module_inst)->llvm_stack = llvm_stack; - return; + + if (init_args->n_native_symbols > 0 + && !wasm_runtime_register_natives(init_args->native_module_name, + init_args->native_symbols, + init_args->n_native_symbols)) { + wasm_runtime_destroy(); + return false; } + +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_set_max_thread_num(init_args->max_thread_num); #endif + + return true; } bool -wasm_runtime_enlarge_memory(WASMModuleInstanceCommon *module, - uint32 inc_page_count) +wasm_runtime_full_init(RuntimeInitArgs *init_args) { -#if WASM_ENABLE_INTERP != 0 - if (module->module_type == Wasm_Module_Bytecode) - return wasm_enlarge_memory((WASMModuleInstance*)module, - inc_page_count); + bool ret = true; + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_lock(&runtime_lock); #endif -#if WASM_ENABLE_AOT != 0 - if (module->module_type == Wasm_Module_AoT) - return aot_enlarge_memory((AOTModuleInstance*)module, - inc_page_count); + + bh_assert(runtime_ref_count >= 0); + if (runtime_ref_count == 0) { + ret = wasm_runtime_full_init_internal(init_args); + } + if (ret) { + runtime_ref_count++; + } + +#if defined(OS_THREAD_MUTEX_INITIALIZER) + os_mutex_unlock(&runtime_lock); #endif - return false; + + return ret; } -#if WASM_ENABLE_LIBC_WASI != 0 void -wasm_runtime_set_wasi_args(WASMModuleCommon *module, - const char *dir_list[], uint32 dir_count, - const char *map_dir_list[], uint32 map_dir_count, - const char *env_list[], uint32 env_count, - const char *argv[], uint32 argc) +wasm_runtime_set_log_level(log_level_t level) { - WASIArguments *wasi_args = NULL; - -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 - if (module->module_type == Wasm_Module_Bytecode) - wasi_args = &((WASMModule*)module)->wasi_args; -#endif -#if WASM_ENABLE_AOT != 0 - if (module->module_type == Wasm_Module_AoT) - wasi_args = &((AOTModule*)module)->wasi_args; -#endif - - if (wasi_args) { - wasi_args->dir_list = dir_list; - wasi_args->dir_count = dir_count; - wasi_args->map_dir_list = map_dir_list; - wasi_args->map_dir_count = map_dir_count; - wasi_args->env = env_list; - wasi_args->env_count = env_count; - wasi_args->argv = argv; - wasi_args->argc = argc; - } + bh_log_set_verbose_level(level); } bool -wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, - const char *dir_list[], uint32 dir_count, - const char *map_dir_list[], uint32 map_dir_count, - const char *env[], uint32 env_count, - const char *argv[], uint32 argc, - char *error_buf, uint32 error_buf_size) +wasm_runtime_is_running_mode_supported(RunningMode running_mode) { - WASIContext *wasi_ctx; - size_t *argv_offsets = NULL; - char *argv_buf = NULL; - size_t *env_offsets = NULL; - char *env_buf = NULL; - uint64 argv_buf_len = 0, env_buf_len = 0; - uint32 argv_buf_offset = 0, env_buf_offset = 0; - struct fd_table *curfds; - struct fd_prestats *prestats; - struct argv_environ_values *argv_environ; - int32 offset_argv_offsets = 0, offset_env_offsets = 0; - int32 offset_argv_buf = 0, offset_env_buf = 0; - int32 offset_curfds = 0; - int32 offset_prestats = 0; - int32 offset_argv_environ = 0; - __wasi_fd_t wasm_fd = 3; - int32 raw_fd; - char *path, resolved_path[PATH_MAX]; - uint64 total_size; - uint32 i; - - if (!(wasi_ctx = wasm_malloc(sizeof(WASIContext)))) { - set_error_buf(error_buf, error_buf_size, - "Init wasi environment failed: allocate memory failed."); - return false; + if (running_mode == Mode_Default) { + return true; } - - memset(wasi_ctx, 0, sizeof(WASIContext)); - wasm_runtime_set_wasi_ctx(module_inst, wasi_ctx); - + else if (running_mode == Mode_Interp) { #if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode - && !((WASMModuleInstance*)module_inst)->default_memory) return true; #endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT - && !((AOTModuleInstance*)module_inst)->memory_data.ptr) + } + else if (running_mode == Mode_Fast_JIT) { +#if WASM_ENABLE_FAST_JIT != 0 return true; #endif - - /* process argv[0], trip the path and suffix, only keep the program name */ - for (i = 0; i < argc; i++) - argv_buf_len += strlen(argv[i]) + 1; - - total_size = sizeof(size_t) * (uint64)argc; - if (total_size >= UINT32_MAX - || !(offset_argv_offsets = wasm_runtime_module_malloc - (module_inst, (uint32)total_size)) - || argv_buf_len >= UINT32_MAX - || !(offset_argv_buf = wasm_runtime_module_malloc - (module_inst, (uint32)argv_buf_len))) { - set_error_buf(error_buf, error_buf_size, - "Init wasi environment failed: allocate memory failed."); - goto fail; } - - argv_offsets = (size_t*) - wasm_runtime_addr_app_to_native(module_inst, offset_argv_offsets); - argv_buf = (char*) - wasm_runtime_addr_app_to_native(module_inst, offset_argv_buf); - - for (i = 0; i < argc; i++) { - argv_offsets[i] = argv_buf_offset; - bh_strcpy_s(argv_buf + argv_buf_offset, - (uint32)argv_buf_len - argv_buf_offset, argv[i]); - argv_buf_offset += (uint32)(strlen(argv[i]) + 1); + else if (running_mode == Mode_LLVM_JIT) { +#if WASM_ENABLE_JIT != 0 + return true; +#endif } - - for (i = 0; i < env_count; i++) - env_buf_len += strlen(env[i]) + 1; - - total_size = sizeof(size_t) * (uint64)argc; - if (total_size >= UINT32_MAX - || !(offset_env_offsets = wasm_runtime_module_malloc - (module_inst, (uint32)total_size)) - || env_buf_len >= UINT32_MAX - || !(offset_env_buf = wasm_runtime_module_malloc - (module_inst, (uint32)env_buf_len))) { - set_error_buf(error_buf, error_buf_size, - "Init wasi environment failed: allocate memory failed."); - goto fail; + 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 } - env_offsets = (size_t*) - wasm_runtime_addr_app_to_native(module_inst, offset_env_offsets); - env_buf = (char*) - wasm_runtime_addr_app_to_native(module_inst, offset_env_buf); + return false; +} - for (i = 0; i < env_count; i++) { - env_offsets[i] = env_buf_offset; - bh_strcpy_s(env_buf + env_buf_offset, - (uint32)env_buf_len - env_buf_offset, env[i]); - env_buf_offset += (uint32)(strlen(env[i]) + 1); +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; +} - if (!(offset_curfds = wasm_runtime_module_malloc - (module_inst, sizeof(struct fd_table))) - || !(offset_prestats = wasm_runtime_module_malloc - (module_inst, sizeof(struct fd_prestats))) - || !(offset_argv_environ = wasm_runtime_module_malloc - (module_inst, sizeof(struct argv_environ_values)))) { - set_error_buf(error_buf, error_buf_size, - "Init wasi environment failed: allocate memory failed."); - goto fail; +PackageType +get_package_type(const uint8 *buf, uint32 size) +{ + if (buf && size >= 4) { +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + uint32 buf32 = *(uint32 *)buf; + buf = (const uint8 *)&buf32; +#endif + if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 's' && buf[3] == 'm') + return Wasm_Module_Bytecode; + if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 'o' && buf[3] == 't') + return Wasm_Module_AoT; } + return Package_Type_Unknown; +} - curfds = wasi_ctx->curfds = (struct fd_table*) - wasm_runtime_addr_app_to_native(module_inst, offset_curfds); - prestats = wasi_ctx->prestats = (struct fd_prestats*) - wasm_runtime_addr_app_to_native(module_inst, offset_prestats); - argv_environ = wasi_ctx->argv_environ = - (struct argv_environ_values*)wasm_runtime_addr_app_to_native - (module_inst, offset_argv_environ); - - fd_table_init(curfds); - fd_prestats_init(prestats); +PackageType +wasm_runtime_get_file_package_type(const uint8 *buf, uint32 size) +{ + return get_package_type(buf, size); +} - if (!argv_environ_init(argv_environ, - argv_offsets, argc, - argv_buf, argv_buf_len, - env_offsets, env_count, - env_buf, env_buf_len)) { - set_error_buf(error_buf, error_buf_size, - "Init wasi environment failed: " - "init argument environment failed."); - goto fail; - } - - /* Prepopulate curfds with stdin, stdout, and stderr file descriptors. */ - if (!fd_table_insert_existing(curfds, 0, 0) - || !fd_table_insert_existing(curfds, 1, 1) - || !fd_table_insert_existing(curfds, 2, 2)) { - set_error_buf(error_buf, error_buf_size, - "Init wasi environment failed: init fd table failed."); - goto fail; +PackageType +wasm_runtime_get_module_package_type(WASMModuleCommon *const module) +{ + if (!module) { + return Package_Type_Unknown; } - wasm_fd = 3; - for (i = 0; i < dir_count; i++, wasm_fd++) { - path = realpath(dir_list[i], resolved_path); - if (!path) { - if (error_buf) - snprintf(error_buf, error_buf_size, - "error while pre-opening directory %s: %d\n", - dir_list[i], errno); - goto fail; - } - - raw_fd = open(path, O_RDONLY | O_DIRECTORY, 0); - if (raw_fd == -1) { - if (error_buf) - snprintf(error_buf, error_buf_size, - "error while pre-opening directory %s: %d\n", - dir_list[i], errno); - goto fail; - } + return module->module_type; +} - fd_table_insert_existing(curfds, wasm_fd, raw_fd); - fd_prestats_insert(prestats, dir_list[i], wasm_fd); +uint32 +wasm_runtime_get_file_package_version(const uint8 *buf, uint32 size) +{ + if (buf && size >= 8) { + uint32 version; +#if (WASM_ENABLE_WORD_ALIGN_READ != 0) + uint32 buf32 = *(uint32 *)(buf + sizeof(uint32)); + buf = (const uint8 *)&buf32; + version = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; +#else + version = buf[4] | buf[5] << 8 | buf[6] << 16 | buf[7] << 24; +#endif + return version; } - return true; - -fail: - if (offset_curfds != 0) - wasm_runtime_module_free(module_inst, offset_curfds); - if (offset_prestats != 0) - wasm_runtime_module_free(module_inst, offset_prestats); - if (offset_argv_environ != 0) - wasm_runtime_module_free(module_inst, offset_argv_environ); - if (offset_argv_buf) - wasm_runtime_module_free(module_inst, offset_argv_buf); - if (offset_argv_offsets) - wasm_runtime_module_free(module_inst, offset_argv_offsets); - if (offset_env_buf) - wasm_runtime_module_free(module_inst, offset_env_buf); - if (offset_env_offsets) - wasm_runtime_module_free(module_inst, offset_env_offsets); - return false; + return 0; } -bool -wasm_runtime_is_wasi_mode(WASMModuleInstanceCommon *module_inst) +uint32 +wasm_runtime_get_module_package_version(WASMModuleCommon *const module) { + if (!module) { + return 0; + } + #if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode - && ((WASMModuleInstance*)module_inst)->module->is_wasi_module) - return true; + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + return wasm_module->package_version; + } #endif + #if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT - && ((AOTModule*)((AOTModuleInstance*)module_inst)->aot_module.ptr) - ->is_wasi_module) - return true; + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + return aot_module->package_version; + } #endif - return false; + + return 0; } -WASMFunctionInstanceCommon * -wasm_runtime_lookup_wasi_start_function(WASMModuleInstanceCommon *module_inst) +uint32 +wasm_runtime_get_current_package_version(package_type_t package_type) { - uint32 i; - -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - WASMModuleInstance *wasm_inst = (WASMModuleInstance*)module_inst; - WASMFunctionInstance *func; - for (i = 0; i < wasm_inst->export_func_count; i++) { - if (!strcmp(wasm_inst->export_functions[i].name, "_start")) { - func = wasm_inst->export_functions[i].function; - if (func->u.func->func_type->param_count != 0 - || func->u.func->func_type->result_count != 0) { - LOG_ERROR("Lookup wasi _start function failed: " - "invalid function type.\n"); - return NULL; - } - return (WASMFunctionInstanceCommon*)func; - } - } - return NULL; + switch (package_type) { + case Wasm_Module_Bytecode: + return WASM_CURRENT_VERSION; + case Wasm_Module_AoT: + return AOT_CURRENT_VERSION; + case Package_Type_Unknown: + default: + return 0; } -#endif +} #if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst; - AOTModule *module = (AOTModule*)aot_inst->aot_module.ptr; - for (i = 0; i < module->export_func_count; i++) { - if (!strcmp(module->export_funcs[i].func_name, "_start")) { - AOTFuncType *func_type = module->export_funcs[i].func_type; - if (func_type->param_count != 0 - || func_type->result_count != 0) { - LOG_ERROR("Lookup wasi _start function failed: " - "invalid function type.\n"); - return NULL; - } - return (WASMFunctionInstanceCommon*)&module->export_funcs[i]; - } +static uint8 * +align_ptr(const uint8 *p, uint32 b) +{ + uintptr_t v = (uintptr_t)p; + uintptr_t m = b - 1; + return (uint8 *)((v + m) & ~m); +} + +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + if ((uintptr_t)buf + length < (uintptr_t)buf \ + || (uintptr_t)buf + length > (uintptr_t)buf_end) \ + return false; \ + } while (0) + +/* NOLINTNEXTLINE */ +#define read_uint16(p, p_end, res) \ + do { \ + p = (uint8 *)align_ptr(p, sizeof(uint16)); \ + CHECK_BUF(p, p_end, sizeof(uint16)); \ + res = *(uint16 *)p; \ + p += sizeof(uint16); \ + } while (0) + +/* NOLINTNEXTLINE */ +#define read_uint32(p, p_end, res) \ + do { \ + p = (uint8 *)align_ptr(p, sizeof(uint32)); \ + CHECK_BUF(p, p_end, sizeof(uint32)); \ + res = *(uint32 *)p; \ + p += sizeof(uint32); \ + } while (0) + +bool +wasm_runtime_is_xip_file(const uint8 *buf, uint32 size) +{ + const uint8 *p = buf, *p_end = buf + size; + uint32 section_type, section_size; + uint16 e_type; + + if (get_package_type(buf, size) != Wasm_Module_AoT) + return false; + + CHECK_BUF(p, p_end, 8); + p += 8; + while (p < p_end) { + read_uint32(p, p_end, section_type); + read_uint32(p, p_end, section_size); + CHECK_BUF(p, p_end, section_size); + + if (section_type == AOT_SECTION_TYPE_TARGET_INFO) { + p += 4; + read_uint16(p, p_end, e_type); + return (e_type == E_TYPE_XIP) ? true : false; } - return NULL; + else if (section_type >= AOT_SECTION_TYPE_SIGNATURE) { + return false; + } + p += section_size; } -#endif /* end of WASM_ENABLE_AOT */ - return NULL; + return false; } +#endif /* end of WASM_ENABLE_AOT */ -void -wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst) +#if (WASM_ENABLE_THREAD_MGR != 0) && (WASM_ENABLE_DEBUG_INTERP != 0) +uint32 +wasm_runtime_start_debug_instance_with_port(WASMExecEnv *exec_env, int32_t port) { - WASIContext *wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + WASMModuleInstanceCommon *module_inst = + wasm_runtime_get_module_inst(exec_env); + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(module_inst); + bh_assert(cluster); + + if (module_inst->module_type != Wasm_Module_Bytecode) { + LOG_WARNING("Attempt to create a debug instance for an AOT module"); + return 0; + } - if (wasi_ctx) { - if (wasi_ctx->argv_environ) - argv_environ_destroy(wasi_ctx->argv_environ); - if (wasi_ctx->curfds) - fd_table_destroy(wasi_ctx->curfds); - if (wasi_ctx->prestats) - fd_prestats_destroy(wasi_ctx->prestats); - bh_free(wasi_ctx); + if (cluster->debug_inst) { + LOG_WARNING("Cluster already bind to a debug instance"); + return cluster->debug_inst->control_thread->port; } + + if (wasm_debug_instance_create(cluster, port)) { + return cluster->debug_inst->control_thread->port; + } + + return 0; } -WASIContext * -wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst) +uint32 +wasm_runtime_start_debug_instance(WASMExecEnv *exec_env) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - return ((WASMModuleInstance*)module_inst)->wasi_ctx; -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - return ((AOTModuleInstance*)module_inst)->wasi_ctx.ptr; -#endif - return NULL; + return wasm_runtime_start_debug_instance_with_port(exec_env, -1); } +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 +static module_reader reader; +static module_destroyer destroyer; void -wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst, - WASIContext *wasi_ctx) +wasm_runtime_set_module_reader(const module_reader reader_cb, + const module_destroyer destroyer_cb) { -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) - ((WASMModuleInstance*)module_inst)->wasi_ctx = wasi_ctx; -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - ((AOTModuleInstance*)module_inst)->wasi_ctx.ptr = wasi_ctx; -#endif + reader = reader_cb; + destroyer = destroyer_cb; } -#endif /* end of WASM_ENABLE_LIBC_WASI */ -/** - * Implementation of wasm_application_execute_main() - */ +module_reader +wasm_runtime_get_module_reader() +{ + return reader; +} -static WASMFunctionInstanceCommon * -resolve_main_function(const WASMModuleInstanceCommon *module_inst) +module_destroyer +wasm_runtime_get_module_destroyer() { - uint32 i; + return destroyer; +} -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - WASMModuleInstance *wasm_inst = (WASMModuleInstance*)module_inst; - for (i = 0; i < wasm_inst->export_func_count; i++) { - if (!strcmp(wasm_inst->export_functions[i].name, "_main") - || !strcmp(wasm_inst->export_functions[i].name, "main")) - return (WASMFunctionInstanceCommon*) - wasm_inst->export_functions[i].function; - } - LOG_ERROR("WASM execute application failed: main function not found.\n"); - return NULL; - } -#endif +static WASMRegisteredModule * +wasm_runtime_find_module_registered_by_reference(WASMModuleCommon *module) +{ + WASMRegisteredModule *reg_module = NULL; -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst; - AOTModule *module = (AOTModule*)aot_inst->aot_module.ptr; - for (i = 0; i < module->export_func_count; i++) { - if (!strcmp(module->export_funcs[i].func_name, "_main") - || !strcmp(module->export_funcs[i].func_name, "main")) - return (WASMFunctionInstanceCommon*)&module->export_funcs[i]; - } - LOG_ERROR("WASM execute application failed: main function not found.\n"); - return NULL; + os_mutex_lock(®istered_module_list_lock); + reg_module = bh_list_first_elem(registered_module_list); + while (reg_module && module != reg_module->module) { + reg_module = bh_list_elem_next(reg_module); } -#endif + os_mutex_unlock(®istered_module_list_lock); - return NULL; + return reg_module; } -static bool -check_main_func_type(const WASMType *type) +bool +wasm_runtime_register_module_internal(const char *module_name, + WASMModuleCommon *module, + uint8 *orig_file_buf, + uint32 orig_file_buf_size, + char *error_buf, uint32 error_buf_size) { - if (!(type->param_count == 0 || type->param_count == 2) - ||type->result_count > 1) { - LOG_ERROR("WASM execute application failed: invalid main function type.\n"); - return false; - } - - if (type->param_count == 2 - && !(type->types[0] == VALUE_TYPE_I32 - && type->types[1] == VALUE_TYPE_I32)) { - LOG_ERROR("WASM execute application failed: invalid main function type.\n"); - return false; + WASMRegisteredModule *node = NULL; + + node = wasm_runtime_find_module_registered_by_reference(module); + if (node) { /* module has been registered */ + if (node->module_name) { /* module has name */ + if (!module_name || strcmp(node->module_name, module_name)) { + /* module has different name */ + LOG_DEBUG("module(%p) has been registered with name %s", module, + node->module_name); + set_error_buf(error_buf, error_buf_size, + "Register module failed: " + "failed to rename the module"); + return false; + } + else { + /* module has the same name */ + LOG_DEBUG( + "module(%p) has been registered with the same name %s", + module, node->module_name); + return true; + } + } + else { + /* module has empty name, reset it */ + node->module_name = module_name; + return true; + } } - if (type->result_count - && type->types[type->param_count] != VALUE_TYPE_I32) { - LOG_ERROR("WASM execute application failed: invalid main function type.\n"); + /* module hasn't been registered */ + node = runtime_malloc(sizeof(WASMRegisteredModule), NULL, NULL, 0); + if (!node) { + LOG_DEBUG("malloc WASMRegisteredModule failed. SZ=%zu", + sizeof(WASMRegisteredModule)); return false; } + /* share the string and the module */ + node->module_name = module_name; + node->module = module; + node->orig_file_buf = orig_file_buf; + node->orig_file_buf_size = orig_file_buf_size; + + os_mutex_lock(®istered_module_list_lock); + bh_list_status ret = bh_list_insert(registered_module_list, node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + os_mutex_unlock(®istered_module_list_lock); return true; } bool -wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, - int argc, char *argv[]) +wasm_runtime_register_module(const char *module_name, WASMModuleCommon *module, + char *error_buf, uint32 error_buf_size) { - WASMFunctionInstanceCommon *func; - WASMType *func_type = NULL; - uint32 argc1 = 0, argv1[2] = { 0 }; - uint32 total_argv_size = 0; - uint64 total_size; - int32 argv_buf_offset, i; - char *argv_buf, *p, *p_end; - int32 *argv_offsets; - -#if WASM_ENABLE_LIBC_WASI != 0 - if (wasm_runtime_is_wasi_mode(module_inst)) { - /* In wasi mode, we should call function named "_start" - which initializes the wasi envrionment and then calls - the actual main function. Directly call main function - may cause exception thrown. */ - if ((func = wasm_runtime_lookup_wasi_start_function(module_inst))) - return wasm_runtime_create_exec_env_and_call_wasm( - module_inst, func, 0, NULL); - /* if no start function is found, we execute - the main function as normal */ - } -#endif /* end of WASM_ENABLE_LIBC_WASI */ - - func = resolve_main_function(module_inst); - if (!func) { - wasm_runtime_set_exception(module_inst, - "lookup main function failed."); + if (!error_buf || !error_buf_size) { + LOG_ERROR("error buffer is required"); return false; } -#if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - if (((WASMFunctionInstance*)func)->is_import_func) { - wasm_runtime_set_exception(module_inst, - "lookup main function failed."); - return false; - } - func_type = ((WASMFunctionInstance*)func)->u.func->func_type; + if (!module_name || !module) { + LOG_DEBUG("module_name and module are required"); + set_error_buf(error_buf, error_buf_size, + "Register module failed: " + "module_name and module are required"); + return false; } -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - func_type = ((AOTFunctionInstance*)func)->func_type; -#endif - if (!check_main_func_type(func_type)) { - wasm_runtime_set_exception(module_inst, - "invalid function type of main function."); + if (wasm_runtime_is_built_in_module(module_name)) { + LOG_DEBUG("%s is a built-in module name", module_name); + set_error_buf(error_buf, error_buf_size, + "Register module failed: " + "can not register as a built-in module"); return false; } - if (func_type->param_count) { - for (i = 0; i < argc; i++) - total_argv_size += (uint32)(strlen(argv[i]) + 1); - total_argv_size = align_uint(total_argv_size, 4); + return wasm_runtime_register_module_internal(module_name, module, NULL, 0, + error_buf, error_buf_size); +} - total_size = (uint64)total_argv_size + sizeof(int32) * (uint64)argc; +void +wasm_runtime_unregister_module(const WASMModuleCommon *module) +{ + WASMRegisteredModule *registered_module = NULL; - if (total_size >= UINT32_MAX - || !(argv_buf_offset = - wasm_runtime_module_malloc(module_inst, (uint32)total_size))) { - wasm_runtime_set_exception(module_inst, - "allocate memory failed."); - return false; - } + os_mutex_lock(®istered_module_list_lock); + registered_module = bh_list_first_elem(registered_module_list); + while (registered_module && module != registered_module->module) { + registered_module = bh_list_elem_next(registered_module); + } - argv_buf = p = wasm_runtime_addr_app_to_native(module_inst, argv_buf_offset); - argv_offsets = (int32*)(p + total_argv_size); - p_end = p + total_size; + /* it does not matter if it is not exist. after all, it is gone */ + if (registered_module) { + bh_list_remove(registered_module_list, registered_module); + wasm_runtime_free(registered_module); + } + os_mutex_unlock(®istered_module_list_lock); +} - for (i = 0; i < argc; i++) { - bh_memcpy_s(p, (uint32)(p_end - p), argv[i], (uint32)(strlen(argv[i]) + 1)); - argv_offsets[i] = argv_buf_offset + (int32)(p - argv_buf); - p += strlen(argv[i]) + 1; - } +WASMModuleCommon * +wasm_runtime_find_module_registered(const char *module_name) +{ + WASMRegisteredModule *module = NULL, *module_next; - argc1 = 2; - argv1[0] = (uint32)argc; - argv1[1] = (uint32)wasm_runtime_addr_native_to_app(module_inst, argv_offsets); + os_mutex_lock(®istered_module_list_lock); + module = bh_list_first_elem(registered_module_list); + while (module) { + module_next = bh_list_elem_next(module); + if (module->module_name && !strcmp(module_name, module->module_name)) { + break; + } + module = module_next; } + os_mutex_unlock(®istered_module_list_lock); - return wasm_runtime_create_exec_env_and_call_wasm(module_inst, func, - argc1, argv1); + return module ? module->module : NULL; } -/** - * Implementation of wasm_application_execute_func() +/* + * simply destroy all */ - -static WASMFunctionInstanceCommon* -resolve_function(const WASMModuleInstanceCommon *module_inst, - const char *name) +static void +wasm_runtime_destroy_registered_module_list() { - uint32 i; + WASMRegisteredModule *reg_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + reg_module = bh_list_first_elem(registered_module_list); + while (reg_module) { + WASMRegisteredModule *next_reg_module = bh_list_elem_next(reg_module); + + bh_list_remove(registered_module_list, reg_module); + /* now, it is time to release every module in the runtime */ + if (reg_module->module->module_type == Wasm_Module_Bytecode) { #if WASM_ENABLE_INTERP != 0 - if (module_inst->module_type == Wasm_Module_Bytecode) { - WASMModuleInstance *wasm_inst = (WASMModuleInstance*)module_inst; - for (i = 0; i < wasm_inst->export_func_count; i++) { - if (!strcmp(wasm_inst->export_functions[i].name, name)) - return wasm_inst->export_functions[i].function; + wasm_unload((WASMModule *)reg_module->module); +#endif } - return NULL; - } + else { +#if WASM_ENABLE_AOT != 0 + aot_unload((AOTModule *)reg_module->module); #endif + } -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst; - AOTModule *module = (AOTModule*)aot_inst->aot_module.ptr; - for (i = 0; i < module->export_func_count; i++) { - if (!strcmp(module->export_funcs[i].func_name, name)) - return (WASMFunctionInstance*)&module->export_funcs[i]; + /* destroy the file buffer */ + if (destroyer && reg_module->orig_file_buf) { + destroyer(reg_module->orig_file_buf, + reg_module->orig_file_buf_size); + reg_module->orig_file_buf = NULL; + reg_module->orig_file_buf_size = 0; } - return NULL; + + wasm_runtime_free(reg_module); + reg_module = next_reg_module; } -#endif + os_mutex_unlock(®istered_module_list_lock); +} - return NULL; +bool +wasm_runtime_add_loading_module(const char *module_name, char *error_buf, + uint32 error_buf_size) +{ + LOG_DEBUG("add %s into a loading list", module_name); + LoadingModule *loadingModule = + runtime_malloc(sizeof(LoadingModule), NULL, error_buf, error_buf_size); + + if (!loadingModule) { + return false; + } + + /* share the incoming string */ + loadingModule->module_name = module_name; + + os_mutex_lock(&loading_module_list_lock); + bh_list_status ret = bh_list_insert(loading_module_list, loadingModule); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + os_mutex_unlock(&loading_module_list_lock); + return true; } -union ieee754_float { - float f; - - /* This is the IEEE 754 single-precision format. */ - union { - struct { - unsigned int negative:1; - unsigned int exponent:8; - unsigned int mantissa:23; - } ieee_big_endian; - struct { - unsigned int mantissa:23; - unsigned int exponent:8; - unsigned int negative:1; - } ieee_little_endian; - } ieee; -}; +void +wasm_runtime_delete_loading_module(const char *module_name) +{ + LOG_DEBUG("delete %s from a loading list", module_name); -union ieee754_double { - double d; - - /* This is the IEEE 754 double-precision format. */ - union { - struct { - unsigned int negative:1; - unsigned int exponent:11; - /* Together these comprise the mantissa. */ - unsigned int mantissa0:20; - unsigned int mantissa1:32; - } ieee_big_endian; - - struct { - /* Together these comprise the mantissa. */ - unsigned int mantissa1:32; - unsigned int mantissa0:20; - unsigned int exponent:11; - unsigned int negative:1; - } ieee_little_endian; - } ieee; -}; + LoadingModule *module = NULL; -static union { - int a; - char b; -} __ue = { .a = 1 }; + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module && strcmp(module->module_name, module_name)) { + module = bh_list_elem_next(module); + } -#define is_little_endian() (__ue.b == 1) + /* it does not matter if it is not exist. after all, it is gone */ + if (module) { + bh_list_remove(loading_module_list, module); + wasm_runtime_free(module); + } + os_mutex_unlock(&loading_module_list_lock); +} bool -wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, - const char *name, int argc, char *argv[]) +wasm_runtime_is_loading_module(const char *module_name) { - WASMFunctionInstanceCommon *func; - WASMType *type = NULL; - uint32 argc1, *argv1 = NULL; - int32 i, p; - uint64 total_size; - const char *exception; - char buf[128]; + LOG_DEBUG("find %s in a loading list", module_name); - bh_assert(argc >= 0); - func = resolve_function(module_inst, name); + LoadingModule *module = NULL; - if (!func) { - snprintf(buf, sizeof(buf), "lookup function %s failed.", name); - wasm_runtime_set_exception(module_inst, buf); - goto fail; + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module && strcmp(module_name, module->module_name)) { + module = bh_list_elem_next(module); + } + os_mutex_unlock(&loading_module_list_lock); + + return module != NULL; +} + +void +wasm_runtime_destroy_loading_module_list() +{ + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module) { + LoadingModule *next_module = bh_list_elem_next(module); + + bh_list_remove(loading_module_list, module); + /* + * will not free the module_name since it is + * shared one of the const string pool + */ + wasm_runtime_free(module); + + module = next_module; } + os_mutex_unlock(&loading_module_list_lock); +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +bool +wasm_runtime_is_built_in_module(const char *module_name) +{ + return (!strcmp("env", module_name) || !strcmp("wasi_unstable", module_name) + || !strcmp("wasi_snapshot_preview1", module_name) +#if WASM_ENABLE_SPEC_TEST != 0 + || !strcmp("spectest", module_name) +#endif +#if WASM_ENABLE_WASI_TEST != 0 + || !strcmp("foo", module_name) +#endif + || !strcmp("", module_name)); +} + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_exec_env_set_aux_stack(WASMExecEnv *exec_env, uint64 start_offset, + uint32 size) +{ + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) { - WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)func; - if (wasm_func->is_import_func) { - snprintf(buf, sizeof(buf), "lookup function %s failed.", name); - wasm_runtime_set_exception(module_inst, buf); - goto fail; - } - type = wasm_func->u.func->func_type; - argc1 = wasm_func->param_cell_num; + return wasm_set_aux_stack(exec_env, start_offset, size); } #endif #if WASM_ENABLE_AOT != 0 if (module_inst->module_type == Wasm_Module_AoT) { - type = ((AOTFunctionInstance*)func)->func_type; - argc1 = wasm_type_param_cell_num(type); + return aot_set_aux_stack(exec_env, start_offset, size); } #endif + return false; +} - if (type->param_count != (uint32)argc) { - wasm_runtime_set_exception(module_inst, - "invalid input argument count."); - goto fail; +bool +wasm_exec_env_get_aux_stack(WASMExecEnv *exec_env, uint64 *start_offset, + uint32 *size) +{ + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_get_aux_stack(exec_env, start_offset, size); } - - total_size = sizeof(uint32) * (uint64)(argc1 > 2 ? argc1 : 2); - if (total_size >= UINT32_MAX - || (!(argv1 = wasm_malloc((uint32)total_size)))) { - wasm_runtime_set_exception(module_inst, "allocate memory failed."); - goto fail; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_get_aux_stack(exec_env, start_offset, size); } +#endif + return false; +} - /* Clear errno before parsing arguments */ - errno = 0; +void +wasm_runtime_set_max_thread_num(uint32 num) +{ + wasm_cluster_set_max_thread_num(num); +} +#endif /* end of WASM_ENABLE_THREAD_MGR */ - /* Parse arguments */ - for (i = 0, p = 0; i < argc; i++) { - char *endptr = NULL; - bh_assert(argv[i] != NULL); - if (argv[i][0] == '\0') { - snprintf(buf, sizeof(buf), "invalid input argument %d.", i); - wasm_runtime_set_exception(module_inst, buf); - goto fail; +static WASMModuleCommon * +register_module_with_null_name(WASMModuleCommon *module_common, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + if (module_common) { + if (!wasm_runtime_register_module_internal(NULL, module_common, NULL, 0, + error_buf, error_buf_size)) { + wasm_runtime_unload(module_common); + return NULL; } - switch (type->types[i]) { - case VALUE_TYPE_I32: - argv1[p++] = (uint32)strtoul(argv[i], &endptr, 0); + return module_common; + } + else + return NULL; +#else + return module_common; +#endif +} + +WASMModuleCommon * +wasm_runtime_load_ex(uint8 *buf, uint32 size, const LoadArgs *args, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_common = NULL; + uint32 package_type; + bool magic_header_detected = false; + + if (!args) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: null load arguments"); + return NULL; + } + + if (size < 4) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: unexpected end"); + return NULL; + } + + package_type = get_package_type(buf, size); + if (package_type == Wasm_Module_Bytecode) { +#if WASM_ENABLE_INTERP != 0 + magic_header_detected = true; +#endif + } + else if (package_type == Wasm_Module_AoT) { +#if WASM_ENABLE_AOT != 0 + magic_header_detected = true; +#endif + } + if (!magic_header_detected) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: magic header not detected"); + return NULL; + } + + if (package_type == Wasm_Module_Bytecode) { +#if WASM_ENABLE_INTERP != 0 + module_common = + (WASMModuleCommon *)wasm_load(buf, size, +#if WASM_ENABLE_MULTI_MODULE != 0 + true, +#endif + args, error_buf, error_buf_size); + if (module_common) + ((WASMModule *)module_common)->is_binary_freeable = + args->wasm_binary_freeable; +#endif + } + else if (package_type == Wasm_Module_AoT) { +#if WASM_ENABLE_AOT != 0 + module_common = (WASMModuleCommon *)aot_load_from_aot_file( + buf, size, args, error_buf, error_buf_size); + if (module_common) + ((AOTModule *)module_common)->is_binary_freeable = + args->wasm_binary_freeable; +#endif + } + + if (!module_common) { + LOG_DEBUG("WASM module load failed"); + return NULL; + } + + /*TODO: use file name as name and register with name? */ + return register_module_with_null_name(module_common, error_buf, + error_buf_size); +} + +bool +wasm_runtime_resolve_symbols(WASMModuleCommon *module) +{ +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + return wasm_resolve_symbols((WASMModule *)module); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + return aot_resolve_symbols((AOTModule *)module); + } +#endif + return false; +} + +WASMModuleCommon * +wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, + uint32 error_buf_size) +{ + LoadArgs args = { 0 }; + args.name = ""; + args.wasm_binary_freeable = false; + return wasm_runtime_load_ex(buf, size, &args, error_buf, error_buf_size); +} + +WASMModuleCommon * +wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_common; + + if (!is_aot) { +#if WASM_ENABLE_INTERP != 0 + module_common = (WASMModuleCommon *)wasm_load_from_sections( + section_list, error_buf, error_buf_size); + if (!module_common) { + LOG_DEBUG("WASM module load failed from sections"); + return NULL; + } + ((WASMModule *)module_common)->is_binary_freeable = true; + return register_module_with_null_name(module_common, error_buf, + error_buf_size); +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + module_common = (WASMModuleCommon *)aot_load_from_sections( + section_list, error_buf, error_buf_size); + if (!module_common) { + LOG_DEBUG("WASM module load failed from sections"); + return NULL; + } + ((AOTModule *)module_common)->is_binary_freeable = true; + return register_module_with_null_name(module_common, error_buf, + error_buf_size); +#endif + } + +#if WASM_ENABLE_INTERP == 0 || WASM_ENABLE_AOT == 0 + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: invalid section list type"); + return NULL; +#endif +} + +void +wasm_runtime_unload(WASMModuleCommon *module) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + /** + * since we will unload and free all module when runtime_destroy() + * we don't want users to unwillingly disrupt it + */ + return; +#endif + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + wasm_unload((WASMModule *)module); + return; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + aot_unload((AOTModule *)module); + return; + } +#endif +} + +uint32 +wasm_runtime_get_max_mem(uint32 max_memory_pages, uint32 module_init_page_count, + uint32 module_max_page_count) +{ + if (max_memory_pages == 0) { + /* Max memory not overwritten by runtime, use value from wasm module */ + return module_max_page_count; + } + + if (max_memory_pages < module_init_page_count) { + LOG_WARNING("Cannot override max memory with value lower than module " + "initial memory"); + return module_init_page_count; + } + + if (max_memory_pages > module_max_page_count) { + LOG_WARNING("Cannot override max memory with value greater than module " + "max memory"); + return module_max_page_count; + } + + return max_memory_pages; +} + +WASMModuleInstanceCommon * +wasm_runtime_instantiate_internal(WASMModuleCommon *module, + WASMModuleInstanceCommon *parent, + WASMExecEnv *exec_env_main, + const struct InstantiationArgs2 *args, + char *error_buf, uint32 error_buf_size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) + return (WASMModuleInstanceCommon *)wasm_instantiate( + (WASMModule *)module, (WASMModuleInstance *)parent, exec_env_main, + args, error_buf, error_buf_size); +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + return (WASMModuleInstanceCommon *)aot_instantiate( + (AOTModule *)module, (AOTModuleInstance *)parent, exec_env_main, + args, error_buf, error_buf_size); +#endif + set_error_buf(error_buf, error_buf_size, + "Instantiate module failed, invalid module type"); + return NULL; +} + +void +wasm_runtime_instantiation_args_set_defaults(struct InstantiationArgs2 *args) +{ + memset(args, 0, sizeof(*args)); +#if WASM_ENABLE_LIBC_WASI != 0 + wasi_args_set_defaults(&args->wasi); +#endif +} + +WASMModuleInstanceCommon * +wasm_runtime_instantiate(WASMModuleCommon *module, uint32 stack_size, + uint32 heap_size, char *error_buf, + uint32 error_buf_size) +{ + struct InstantiationArgs2 args; + wasm_runtime_instantiation_args_set_defaults(&args); + wasm_runtime_instantiation_args_set_default_stack_size(&args, stack_size); + wasm_runtime_instantiation_args_set_host_managed_heap_size(&args, + heap_size); + return wasm_runtime_instantiate_internal(module, NULL, NULL, &args, + error_buf, error_buf_size); +} + +WASMModuleInstanceCommon * +wasm_runtime_instantiate_ex(WASMModuleCommon *module, + const InstantiationArgs *args, char *error_buf, + uint32 error_buf_size) +{ + struct InstantiationArgs2 v2; + wasm_runtime_instantiation_args_set_defaults(&v2); + v2.v1 = *args; + return wasm_runtime_instantiate_ex2(module, &v2, error_buf, error_buf_size); +} + +bool +wasm_runtime_instantiation_args_create(struct InstantiationArgs2 **p) +{ + struct InstantiationArgs2 *args = wasm_runtime_malloc(sizeof(*args)); + if (args == NULL) { + return false; + } + wasm_runtime_instantiation_args_set_defaults(args); + *p = args; + return true; +} + +void +wasm_runtime_instantiation_args_destroy(struct InstantiationArgs2 *p) +{ + wasm_runtime_free(p); +} + +void +wasm_runtime_instantiation_args_set_default_stack_size( + struct InstantiationArgs2 *p, uint32 v) +{ + p->v1.default_stack_size = v; +} + +void +wasm_runtime_instantiation_args_set_host_managed_heap_size( + struct InstantiationArgs2 *p, uint32 v) +{ + p->v1.host_managed_heap_size = v; +} + +void +wasm_runtime_instantiation_args_set_max_memory_pages( + struct InstantiationArgs2 *p, uint32 v) +{ + p->v1.max_memory_pages = v; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +void +wasm_runtime_instantiation_args_set_wasi_arg(struct InstantiationArgs2 *p, + char *argv[], int argc) +{ + WASIArguments *wasi_args = &p->wasi; + + wasi_args->argv = argv; + wasi_args->argc = (uint32)argc; + wasi_args->set_by_user = true; +} + +void +wasm_runtime_instantiation_args_set_wasi_env(struct InstantiationArgs2 *p, + const char *env[], + uint32 env_count) +{ + WASIArguments *wasi_args = &p->wasi; + + wasi_args->env = env; + wasi_args->env_count = env_count; + wasi_args->set_by_user = true; +} + +void +wasm_runtime_instantiation_args_set_wasi_dir(struct InstantiationArgs2 *p, + const char *dir_list[], + uint32 dir_count, + const char *map_dir_list[], + uint32 map_dir_count) +{ + WASIArguments *wasi_args = &p->wasi; + + wasi_args->dir_list = dir_list; + wasi_args->dir_count = dir_count; + wasi_args->map_dir_list = map_dir_list; + wasi_args->map_dir_count = map_dir_count; + wasi_args->set_by_user = true; +} + +void +wasm_runtime_instantiation_args_set_wasi_stdio(struct InstantiationArgs2 *p, + int64 stdinfd, int64 stdoutfd, + int64 stderrfd) +{ + WASIArguments *wasi_args = &p->wasi; + + wasi_args->stdio[0] = (os_raw_file_handle)stdinfd; + wasi_args->stdio[1] = (os_raw_file_handle)stdoutfd; + wasi_args->stdio[2] = (os_raw_file_handle)stderrfd; + wasi_args->set_by_user = true; +} + +void +wasm_runtime_instantiation_args_set_wasi_addr_pool(struct InstantiationArgs2 *p, + const char *addr_pool[], + uint32 addr_pool_size) +{ + WASIArguments *wasi_args = &p->wasi; + + wasi_args->addr_pool = addr_pool; + wasi_args->addr_count = addr_pool_size; + wasi_args->set_by_user = true; +} + +void +wasm_runtime_instantiation_args_set_wasi_ns_lookup_pool( + struct InstantiationArgs2 *p, const char *ns_lookup_pool[], + uint32 ns_lookup_pool_size) +{ + WASIArguments *wasi_args = &p->wasi; + + wasi_args->ns_lookup_pool = ns_lookup_pool; + wasi_args->ns_lookup_count = ns_lookup_pool_size; + wasi_args->set_by_user = true; +} +#endif /* WASM_ENABLE_LIBC_WASI != 0 */ + +WASMModuleInstanceCommon * +wasm_runtime_instantiate_ex2(WASMModuleCommon *module, + const struct InstantiationArgs2 *args, + char *error_buf, uint32 error_buf_size) +{ + return wasm_runtime_instantiate_internal(module, NULL, NULL, args, + error_buf, error_buf_size); +} + +void +wasm_runtime_deinstantiate_internal(WASMModuleInstanceCommon *module_inst, + bool is_sub_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_deinstantiate((WASMModuleInstance *)module_inst, is_sub_inst); + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_deinstantiate((AOTModuleInstance *)module_inst, is_sub_inst); + return; + } +#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) +{ + wasm_runtime_deinstantiate_internal(module_inst, false); +} + +WASMModuleCommon * +wasm_runtime_get_module(WASMModuleInstanceCommon *module_inst) +{ + return (WASMModuleCommon *)((WASMModuleInstance *)module_inst)->module; +} + +WASMExecEnv * +wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, + uint32 stack_size) +{ + return wasm_exec_env_create(module_inst, stack_size); +} + +void +wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env) +{ + wasm_exec_env_destroy(exec_env); +} + +#if WASM_ENABLE_COPY_CALL_STACK != 0 +uint32 +wasm_copy_callstack(const wasm_exec_env_t exec_env, WASMCApiFrame *buffer, + const uint32 length, const uint32 skip_n, char *error_buf, + uint32_t error_buf_size) +{ + /* + * Note for devs: please refrain from such modifications inside of + * wasm_copy_callstack to preserve async-signal-safety + * - any allocations/freeing memory + * - dereferencing any pointers other than: exec_env, exec_env->module_inst, + * exec_env->module_inst->module, pointers between stack's bottom and + * top_boundary For more details check wasm_copy_callstack in + * wasm_export.h + */ +#if WASM_ENABLE_DUMP_CALL_STACK + WASMModuleInstance *module_inst = + (WASMModuleInstance *)get_module_inst(exec_env); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_interp_copy_callstack(exec_env, buffer, length, skip_n, + error_buf, error_buf_size); + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_copy_callstack(exec_env, buffer, length, skip_n, error_buf, + error_buf_size); + } +#endif +#endif + char *err_msg = "No copy_callstack API was actually executed"; + strncpy(error_buf, err_msg, error_buf_size); + return 0; +} +#endif // WASM_ENABLE_COPY_CALL_STACK + +bool +wasm_runtime_init_thread_env(void) +{ +#ifdef BH_PLATFORM_WINDOWS + if (os_thread_env_init() != 0) + return false; +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!runtime_signal_init()) { +#ifdef BH_PLATFORM_WINDOWS + os_thread_env_destroy(); +#endif + return false; + } +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 && defined(OS_ENABLE_WAKEUP_BLOCKING_OP) + os_end_blocking_op(); +#endif + + return true; +} + +void +wasm_runtime_destroy_thread_env(void) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + runtime_signal_destroy(); +#endif + +#ifdef BH_PLATFORM_WINDOWS + os_thread_env_destroy(); +#endif +} + +bool +wasm_runtime_thread_env_inited(void) +{ +#ifdef BH_PLATFORM_WINDOWS + if (!os_thread_env_inited()) + return false; +#endif + +#if WASM_ENABLE_AOT != 0 +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!os_thread_signal_inited()) + return false; +#endif +#endif + return true; +} + +#if (WASM_ENABLE_MEMORY_PROFILING != 0) || (WASM_ENABLE_MEMORY_TRACING != 0) +void +wasm_runtime_dump_module_mem_consumption(const WASMModuleCommon *module) +{ + WASMModuleMemConsumption mem_conspn = { 0 }; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + wasm_get_module_mem_consumption((WASMModule *)module, &mem_conspn); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + aot_get_module_mem_consumption((AOTModule *)module, &mem_conspn); + } +#endif + + LOG_VERBOSE("WASM module memory consumption, total size: %u", + mem_conspn.total_size); + LOG_VERBOSE(" module struct size: %u", mem_conspn.module_struct_size); + LOG_VERBOSE(" types size: %u", mem_conspn.types_size); + LOG_VERBOSE(" imports size: %u", mem_conspn.imports_size); + LOG_VERBOSE(" funcs size: %u", mem_conspn.functions_size); + LOG_VERBOSE(" tables size: %u", mem_conspn.tables_size); + LOG_VERBOSE(" memories size: %u", mem_conspn.memories_size); + LOG_VERBOSE(" globals size: %u", mem_conspn.globals_size); + LOG_VERBOSE(" exports size: %u", mem_conspn.exports_size); + LOG_VERBOSE(" table segs size: %u", mem_conspn.table_segs_size); + LOG_VERBOSE(" data segs size: %u", mem_conspn.data_segs_size); + LOG_VERBOSE(" const strings size: %u", mem_conspn.const_strs_size); +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + LOG_VERBOSE(" custom sections size: %u", + mem_conspn.custom_sections_size); +#endif +#if WASM_ENABLE_AOT != 0 + LOG_VERBOSE(" aot code size: %u", mem_conspn.aot_code_size); +#endif +} + +void +wasm_runtime_dump_module_inst_mem_consumption( + const WASMModuleInstanceCommon *module_inst) +{ + WASMModuleInstMemConsumption mem_conspn = { 0 }; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_get_module_inst_mem_consumption((WASMModuleInstance *)module_inst, + &mem_conspn); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_get_module_inst_mem_consumption((AOTModuleInstance *)module_inst, + &mem_conspn); + } +#endif + + LOG_VERBOSE("WASM module inst memory consumption, total size: %lu", + mem_conspn.total_size); + LOG_VERBOSE(" module inst struct size: %u", + mem_conspn.module_inst_struct_size); + LOG_VERBOSE(" memories size: %lu", mem_conspn.memories_size); + LOG_VERBOSE(" app heap size: %u", mem_conspn.app_heap_size); + LOG_VERBOSE(" tables size: %u", mem_conspn.tables_size); + LOG_VERBOSE(" functions size: %u", mem_conspn.functions_size); + LOG_VERBOSE(" globals size: %u", mem_conspn.globals_size); + LOG_VERBOSE(" exports size: %u", mem_conspn.exports_size); +} + +void +wasm_runtime_dump_exec_env_mem_consumption(const WASMExecEnv *exec_env) +{ + uint32 total_size = + offsetof(WASMExecEnv, wasm_stack_u.bottom) + exec_env->wasm_stack_size; + + LOG_VERBOSE("Exec env memory consumption, total size: %u", total_size); + LOG_VERBOSE(" exec env struct size: %u", + offsetof(WASMExecEnv, wasm_stack_u.bottom)); +#if WASM_ENABLE_INTERP != 0 && WASM_ENABLE_FAST_INTERP == 0 + LOG_VERBOSE(" block addr cache size: %u", + sizeof(exec_env->block_addr_cache)); +#endif + LOG_VERBOSE(" stack size: %u", exec_env->wasm_stack_size); +} + +uint32 +gc_get_heap_highmark_size(void *heap); + +void +wasm_runtime_dump_mem_consumption(WASMExecEnv *exec_env) +{ + WASMModuleInstMemConsumption module_inst_mem_consps; + WASMModuleMemConsumption module_mem_consps; + WASMModuleInstanceCommon *module_inst_common; + WASMModuleCommon *module_common = NULL; + void *heap_handle = NULL; + uint32 app_heap_peak_size = 0; + uint32 max_aux_stack_used = -1; + uint64 total_size = 0; + + module_inst_common = exec_env->module_inst; +#if WASM_ENABLE_INTERP != 0 + if (module_inst_common->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *wasm_module_inst = + (WASMModuleInstance *)module_inst_common; + WASMModule *wasm_module = wasm_module_inst->module; + module_common = (WASMModuleCommon *)wasm_module; + if (wasm_module_inst->memories) { + heap_handle = wasm_module_inst->memories[0]->heap_handle; + } + wasm_get_module_inst_mem_consumption(wasm_module_inst, + &module_inst_mem_consps); + wasm_get_module_mem_consumption(wasm_module, &module_mem_consps); + if (wasm_module_inst->module->aux_stack_top_global_index != (uint32)-1) + max_aux_stack_used = wasm_module_inst->e->max_aux_stack_used; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst_common->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_module_inst = + (AOTModuleInstance *)module_inst_common; + AOTModule *aot_module = (AOTModule *)aot_module_inst->module; + module_common = (WASMModuleCommon *)aot_module; + if (aot_module_inst->memories) { + AOTMemoryInstance **memories = aot_module_inst->memories; + heap_handle = memories[0]->heap_handle; + } + aot_get_module_inst_mem_consumption(aot_module_inst, + &module_inst_mem_consps); + aot_get_module_mem_consumption(aot_module, &module_mem_consps); + } +#endif + + bh_assert(module_common != NULL); + + if (heap_handle) { + app_heap_peak_size = gc_get_heap_highmark_size(heap_handle); + } + + total_size = offsetof(WASMExecEnv, wasm_stack_u.bottom) + + exec_env->wasm_stack_size + module_mem_consps.total_size + + module_inst_mem_consps.total_size; + + LOG_VERBOSE("Memory consumption summary (bytes):"); + wasm_runtime_dump_module_mem_consumption(module_common); + wasm_runtime_dump_module_inst_mem_consumption(module_inst_common); + wasm_runtime_dump_exec_env_mem_consumption(exec_env); + LOG_VERBOSE("Total memory consumption of module, module inst and " + "exec env: %" PRIu64, + total_size); + LOG_VERBOSE("Total interpreter stack used: %u", + exec_env->max_wasm_stack_used); + + if (max_aux_stack_used != (uint32)-1) + LOG_VERBOSE("Total auxiliary stack used: %u", max_aux_stack_used); + else + LOG_VERBOSE("Total aux stack used: no enough info to profile"); + + /* + * 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) + LOG_VERBOSE("Native stack left: %zd", + exec_env->native_stack_top_min + - exec_env->native_stack_boundary); + else + LOG_VERBOSE("Native stack left: no enough info to profile"); + + LOG_VERBOSE("Total app heap used: %u", app_heap_peak_size); +} +#endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \ + || (WASM_ENABLE_MEMORY_TRACING != 0) */ + +#if WASM_ENABLE_PERF_PROFILING != 0 +void +wasm_runtime_dump_perf_profiling(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_dump_perf_profiling((WASMModuleInstance *)module_inst); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_dump_perf_profiling((AOTModuleInstance *)module_inst); + } +#endif +} + +double +wasm_runtime_sum_wasm_exec_time(WASMModuleInstanceCommon *inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (inst->module_type == Wasm_Module_Bytecode) + return wasm_summarize_wasm_execute_time((WASMModuleInstance *)inst); +#endif + +#if WASM_ENABLE_AOT != 0 + if (inst->module_type == Wasm_Module_AoT) + return aot_summarize_wasm_execute_time((AOTModuleInstance *)inst); +#endif + + return 0.0; +} + +double +wasm_runtime_get_wasm_func_exec_time(WASMModuleInstanceCommon *inst, + const char *func_name) +{ +#if WASM_ENABLE_INTERP != 0 + if (inst->module_type == Wasm_Module_Bytecode) + return wasm_get_wasm_func_exec_time((WASMModuleInstance *)inst, + func_name); +#endif + +#if WASM_ENABLE_AOT != 0 + if (inst->module_type == Wasm_Module_AoT) + return aot_get_wasm_func_exec_time((AOTModuleInstance *)inst, + func_name); +#endif + + return 0.0; +} +#endif /* WASM_ENABLE_PERF_PROFILING != 0 */ + +WASMModuleInstanceCommon * +wasm_runtime_get_module_inst(WASMExecEnv *exec_env) +{ + return wasm_exec_env_get_module_inst(exec_env); +} + +void +wasm_runtime_set_module_inst(WASMExecEnv *exec_env, + WASMModuleInstanceCommon *const module_inst) +{ + wasm_exec_env_set_module_inst(exec_env, module_inst); +} + +bool +wasm_runtime_get_export_global_inst(WASMModuleInstanceCommon *const module_inst, + char const *name, + wasm_global_inst_t *global_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + const WASMModuleInstance *wasm_module_inst = + (const WASMModuleInstance *)module_inst; + const WASMModule *wasm_module = wasm_module_inst->module; + uint32 i; + for (i = 0; i < wasm_module->export_count; i++) { + const WASMExport *wasm_export = &wasm_module->exports[i]; + if ((wasm_export->kind == WASM_IMPORT_EXPORT_KIND_GLOBAL) + && !strcmp(wasm_export->name, name)) { + const WASMModuleInstanceExtra *e = + (WASMModuleInstanceExtra *)wasm_module_inst->e; + const WASMGlobalInstance *global = + &e->globals[wasm_export->index]; + global_inst->kind = val_type_to_val_kind(global->type); + global_inst->is_mutable = global->is_mutable; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_inst->global_data = + wasm_module_inst->global_data + global->data_offset; +#else + global_inst->global_data = + global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : wasm_module_inst->global_data + global->data_offset; +#endif + return true; + } + } + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + const AOTModuleInstance *aot_module_inst = + (AOTModuleInstance *)module_inst; + const AOTModule *aot_module = (AOTModule *)aot_module_inst->module; + uint32 i; + for (i = 0; i < aot_module->export_count; i++) { + const AOTExport *aot_export = &aot_module->exports[i]; + if ((aot_export->kind == WASM_IMPORT_EXPORT_KIND_GLOBAL) + && !strcmp(aot_export->name, name)) { + const AOTGlobal *global = + &aot_module->globals[aot_export->index]; + global_inst->kind = val_type_to_val_kind(global->type.val_type); + global_inst->is_mutable = global->type.is_mutable; + global_inst->global_data = + aot_module_inst->global_data + global->data_offset; + return true; + } + } + } +#endif + + return false; +} + +bool +wasm_runtime_get_export_table_inst(WASMModuleInstanceCommon *const module_inst, + char const *name, + wasm_table_inst_t *table_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + const WASMModuleInstance *wasm_module_inst = + (const WASMModuleInstance *)module_inst; + const WASMModule *wasm_module = wasm_module_inst->module; + uint32 i; + for (i = 0; i < wasm_module->export_count; i++) { + const WASMExport *wasm_export = &wasm_module->exports[i]; + if ((wasm_export->kind == WASM_IMPORT_EXPORT_KIND_TABLE) + && !strcmp(wasm_export->name, name)) { + const WASMTableInstance *wasm_table_inst = + wasm_module_inst->tables[wasm_export->index]; + table_inst->elem_kind = + val_type_to_val_kind(wasm_table_inst->elem_type); + table_inst->cur_size = wasm_table_inst->cur_size; + table_inst->max_size = wasm_table_inst->max_size; + table_inst->elems = (void *)wasm_table_inst->elems; + return true; + } + } + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + const AOTModuleInstance *aot_module_inst = + (AOTModuleInstance *)module_inst; + const AOTModule *aot_module = (AOTModule *)aot_module_inst->module; + uint32 i; + for (i = 0; i < aot_module->export_count; i++) { + const AOTExport *aot_export = &aot_module->exports[i]; + if ((aot_export->kind == WASM_IMPORT_EXPORT_KIND_TABLE) + && !strcmp(aot_export->name, name)) { + const AOTTableInstance *aot_table_inst = + aot_module_inst->tables[aot_export->index]; + table_inst->elem_kind = + val_type_to_val_kind(aot_table_inst->elem_type); + table_inst->cur_size = aot_table_inst->cur_size; + table_inst->max_size = aot_table_inst->max_size; + table_inst->elems = (void *)aot_table_inst->elems; + return true; + } + } + } +#endif + + return false; +} + +WASMFunctionInstanceCommon * +wasm_table_get_func_inst(struct WASMModuleInstanceCommon *const module_inst, + const wasm_table_inst_t *table_inst, uint32_t idx) +{ + if (!table_inst) { + bh_assert(0); + return NULL; + } + + if (idx >= table_inst->cur_size) { + bh_assert(0); + return NULL; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + const WASMModuleInstance *wasm_module_inst = + (const WASMModuleInstance *)module_inst; + table_elem_type_t tbl_elem_val = + ((table_elem_type_t *)table_inst->elems)[idx]; + if (tbl_elem_val == NULL_REF) { + return NULL; + } + +#if WASM_ENABLE_GC == 0 + uint32 func_idx = (uint32)tbl_elem_val; +#else + uint32 func_idx = + wasm_func_obj_get_func_idx_bound((WASMFuncObjectRef)tbl_elem_val); +#endif + + bh_assert(func_idx < wasm_module_inst->e->function_count); + return wasm_module_inst->e->functions + func_idx; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_module_inst = (AOTModuleInstance *)module_inst; + uint32 func_idx; + table_elem_type_t tbl_elem_val = + ((table_elem_type_t *)table_inst->elems)[idx]; + if (tbl_elem_val == NULL_REF) { + return NULL; + } + +#if WASM_ENABLE_GC == 0 + func_idx = (uint32)tbl_elem_val; +#else + func_idx = + wasm_func_obj_get_func_idx_bound((WASMFuncObjectRef)tbl_elem_val); +#endif + + return aot_get_function_instance(aot_module_inst, func_idx); + } +#endif + + return NULL; +} + +void * +wasm_runtime_get_function_attachment(WASMExecEnv *exec_env) +{ + return exec_env->attachment; +} + +void +wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data) +{ + exec_env->user_data = user_data; +} + +void * +wasm_runtime_get_user_data(WASMExecEnv *exec_env) +{ + return exec_env->user_data; +} + +void +wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env, + uint8 *native_stack_boundary) +{ + exec_env->user_native_stack_boundary = native_stack_boundary; +} + +#ifdef OS_ENABLE_HW_BOUND_CHECK +void +wasm_runtime_access_exce_check_guard_page() +{ + if (exec_env_tls && exec_env_tls->handle == os_self_thread()) { + uint32 page_size = os_getpagesize(); + memset(exec_env_tls->exce_check_guard_page, 0, page_size); + } +} +#endif + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +void +wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, + int instructions_to_execute) +{ + exec_env->instructions_to_execute = instructions_to_execute; +} +#endif + +WASMFuncType * +wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, + uint32 module_type) +{ + WASMFuncType *type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance *)function; + type = wasm_func->is_import_func ? wasm_func->u.func_import->func_type + : wasm_func->u.func->func_type; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_type == Wasm_Module_AoT) { + AOTFunctionInstance *aot_func = (AOTFunctionInstance *)function; + type = aot_func->is_import_func ? aot_func->u.func_import->func_type + : aot_func->u.func.func_type; + } +#endif + + return type; +} + +WASMFunctionInstanceCommon * +wasm_runtime_lookup_function(WASMModuleInstanceCommon *const module_inst, + const char *name) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return (WASMFunctionInstanceCommon *)wasm_lookup_function( + (const WASMModuleInstance *)module_inst, name); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return (WASMFunctionInstanceCommon *)aot_lookup_function( + (const AOTModuleInstance *)module_inst, name); +#endif + return NULL; +} + +uint32 +wasm_func_get_param_count(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst) +{ + WASMFuncType *type = + wasm_runtime_get_function_type(func_inst, module_inst->module_type); + bh_assert(type); + + return type->param_count; +} + +uint32 +wasm_func_get_result_count(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst) +{ + WASMFuncType *type = + wasm_runtime_get_function_type(func_inst, module_inst->module_type); + bh_assert(type); + + return type->result_count; +} + +static uint8 +val_type_to_val_kind(uint8 value_type) +{ + switch (value_type) { + case VALUE_TYPE_I32: + return WASM_I32; + case VALUE_TYPE_I64: + return WASM_I64; + case VALUE_TYPE_F32: + return WASM_F32; + case VALUE_TYPE_F64: + return WASM_F64; + case VALUE_TYPE_V128: + return WASM_V128; + case VALUE_TYPE_FUNCREF: + return WASM_FUNCREF; + case VALUE_TYPE_EXTERNREF: + return WASM_EXTERNREF; + default: + bh_assert(0); + return 0; + } +} + +void +wasm_func_get_param_types(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst, + wasm_valkind_t *param_types) +{ + WASMFuncType *type = + wasm_runtime_get_function_type(func_inst, module_inst->module_type); + uint32 i; + + bh_assert(type); + + for (i = 0; i < type->param_count; i++) { + param_types[i] = val_type_to_val_kind(type->types[i]); + } +} + +void +wasm_func_get_result_types(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst, + wasm_valkind_t *result_types) +{ + WASMFuncType *type = + wasm_runtime_get_function_type(func_inst, module_inst->module_type); + uint32 i; + + bh_assert(type); + + for (i = 0; i < type->result_count; i++) { + result_types[i] = + val_type_to_val_kind(type->types[type->param_count + i]); + } +} + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 +/* (uintptr_t)externref -> (uint32)index */ +/* argv -> *ret_argv */ +static bool +wasm_runtime_prepare_call_function(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 *argv, uint32 argc, uint32 **ret_argv, + uint32 *ret_argc_param, + uint32 *ret_argc_result) +{ + uint32 *new_argv = NULL, argv_i = 0, new_argv_i = 0, param_i = 0, + result_i = 0; + bool need_param_transform = false, need_result_transform = false; + uint64 size = 0; + WASMFuncType *func_type = wasm_runtime_get_function_type( + function, exec_env->module_inst->module_type); + + bh_assert(func_type); + + *ret_argc_param = func_type->param_cell_num; + *ret_argc_result = func_type->ret_cell_num; + for (param_i = 0; param_i < func_type->param_count; param_i++) { + if (VALUE_TYPE_EXTERNREF == func_type->types[param_i]) { + need_param_transform = true; + } + } + + for (result_i = 0; result_i < func_type->result_count; result_i++) { + if (VALUE_TYPE_EXTERNREF + == func_type->types[func_type->param_count + result_i]) { + need_result_transform = true; + } + } + + if (!need_param_transform && !need_result_transform) { + *ret_argv = argv; + return true; + } + + if (func_type->param_cell_num >= func_type->ret_cell_num) { + size = sizeof(uint32) * func_type->param_cell_num; + } + else { + size = sizeof(uint32) * func_type->ret_cell_num; + } + + if (!(new_argv = runtime_malloc(size, exec_env->module_inst, NULL, 0))) { + return false; + } + + if (!need_param_transform) { + bh_memcpy_s(new_argv, (uint32)size, argv, (uint32)size); + } + else { + for (param_i = 0; param_i < func_type->param_count && argv_i < argc + && new_argv_i < func_type->param_cell_num; + param_i++) { + uint8 param_type = func_type->types[param_i]; + if (VALUE_TYPE_EXTERNREF == param_type) { + void *externref_obj; + uint32 externref_index; + +#if UINTPTR_MAX == UINT32_MAX + externref_obj = (void *)argv[argv_i]; +#else + union { + uintptr_t val; + uint32 parts[2]; + } u; + + u.parts[0] = argv[argv_i]; + u.parts[1] = argv[argv_i + 1]; + externref_obj = (void *)u.val; +#endif + if (!wasm_externref_obj2ref(exec_env->module_inst, + externref_obj, &externref_index)) { + wasm_runtime_free(new_argv); + return false; + } + + new_argv[new_argv_i] = externref_index; + argv_i += sizeof(uintptr_t) / sizeof(uint32); + new_argv_i++; + } + else { + uint16 param_cell_num = wasm_value_type_cell_num(param_type); + uint32 param_size = sizeof(uint32) * param_cell_num; + bh_memcpy_s(new_argv + new_argv_i, param_size, argv + argv_i, + param_size); + argv_i += param_cell_num; + new_argv_i += param_cell_num; + } + } + } + + *ret_argv = new_argv; + return true; +} + +/* (uintptr_t)externref <- (uint32)index */ +/* argv <- new_argv */ +static bool +wasm_runtime_finalize_call_function(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 *argv, uint32 argc, uint32 *ret_argv) +{ + uint32 argv_i = 0, result_i = 0, ret_argv_i = 0; + WASMFuncType *func_type; + + bh_assert((argv && ret_argv) || (argc == 0)); + + if (argv == ret_argv) { + /* no need to transform externref results */ + return true; + } + + func_type = wasm_runtime_get_function_type( + function, exec_env->module_inst->module_type); + bh_assert(func_type); + + for (result_i = 0; result_i < func_type->result_count && argv_i < argc; + result_i++) { + uint8 result_type = func_type->types[func_type->param_count + result_i]; + if (result_type == VALUE_TYPE_EXTERNREF) { + void *externref_obj; +#if UINTPTR_MAX != UINT32_MAX + union { + uintptr_t val; + uint32 parts[2]; + } u; +#endif + + if (!wasm_externref_ref2obj(argv[argv_i], &externref_obj)) { + wasm_runtime_free(argv); + return false; + } + +#if UINTPTR_MAX == UINT32_MAX + ret_argv[ret_argv_i] = (uintptr_t)externref_obj; +#else + u.val = (uintptr_t)externref_obj; + ret_argv[ret_argv_i] = u.parts[0]; + ret_argv[ret_argv_i + 1] = u.parts[1]; +#endif + argv_i += 1; + ret_argv_i += sizeof(uintptr_t) / sizeof(uint32); + } + else { + uint16 result_cell_num = wasm_value_type_cell_num(result_type); + uint32 result_size = sizeof(uint32) * result_cell_num; + bh_memcpy_s(ret_argv + ret_argv_i, result_size, argv + argv_i, + result_size); + argv_i += result_cell_num; + ret_argv_i += result_cell_num; + } + } + + wasm_runtime_free(argv); + return true; +} +#endif + +bool +wasm_runtime_call_wasm(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, uint32 argc, + uint32 argv[]) +{ + bool ret = false; + uint32 *new_argv = NULL, param_argc; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + uint32 result_argc = 0; +#endif + + if (!wasm_runtime_exec_env_check(exec_env)) { + LOG_ERROR("Invalid exec env stack info."); + return false; + } + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + if (!wasm_runtime_prepare_call_function(exec_env, function, argv, argc, + &new_argv, ¶m_argc, + &result_argc)) { + wasm_runtime_set_exception(exec_env->module_inst, + "the arguments conversion is failed"); + return false; + } +#else + new_argv = argv; + param_argc = argc; +#endif + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) + ret = wasm_call_function(exec_env, (WASMFunctionInstance *)function, + param_argc, new_argv); +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) + ret = aot_call_function(exec_env, (AOTFunctionInstance *)function, + param_argc, new_argv); +#endif + if (!ret) { + if (new_argv != argv) { + wasm_runtime_free(new_argv); + } + return false; + } + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + if (!wasm_runtime_finalize_call_function(exec_env, function, new_argv, + result_argc, argv)) { + wasm_runtime_set_exception(exec_env->module_inst, + "the result conversion is failed"); + return false; + } +#endif + + return ret; +} + +static void +parse_args_to_uint32_array(WASMFuncType *type, wasm_val_t *args, + uint32 *out_argv) +{ + uint32 i, p; + + for (i = 0, p = 0; i < type->param_count; i++) { + switch (args[i].kind) { + case WASM_I32: + out_argv[p++] = args[i].of.i32; + break; + case WASM_I64: + { + union { + uint64 val; + uint32 parts[2]; + } u; + u.val = args[i].of.i64; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; + break; + } + case WASM_F32: + { + union { + float32 val; + uint32 part; + } u; + u.val = args[i].of.f32; + out_argv[p++] = u.part; + break; + } + case WASM_F64: + { + union { + float64 val; + uint32 parts[2]; + } u; + u.val = args[i].of.f64; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; + break; + } + case WASM_V128: + { + bh_assert(0); + break; + } +#if WASM_ENABLE_REF_TYPES != 0 +#if WASM_ENABLE_GC == 0 + case WASM_FUNCREF: + { + out_argv[p++] = args[i].of.i32; + break; + } +#else + case WASM_FUNCREF: +#endif + case WASM_EXTERNREF: + { +#if UINTPTR_MAX == UINT32_MAX + out_argv[p++] = args[i].of.foreign; +#else + union { + uintptr_t val; + uint32 parts[2]; + } u; + + u.val = (uintptr_t)args[i].of.foreign; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; +#endif + break; + } +#endif + default: + bh_assert(0); + break; + } + } +} + +static void +parse_uint32_array_to_results(WASMFuncType *type, uint32 *argv, + wasm_val_t *out_results) +{ + uint32 i, p; + + for (i = 0, p = 0; i < type->result_count; i++) { + switch (type->types[type->param_count + i]) { + case VALUE_TYPE_I32: + out_results[i].kind = WASM_I32; + out_results[i].of.i32 = (int32)argv[p++]; + break; + case VALUE_TYPE_I64: + { + union { + uint64 val; + uint32 parts[2]; + } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_I64; + out_results[i].of.i64 = u.val; + break; + } + case VALUE_TYPE_F32: + { + union { + float32 val; + uint32 part; + } u; + u.part = argv[p++]; + out_results[i].kind = WASM_F32; + out_results[i].of.f32 = u.val; + break; + } + case VALUE_TYPE_F64: + { + union { + float64 val; + uint32 parts[2]; + } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_F64; + out_results[i].of.f64 = u.val; + break; + } + case VALUE_TYPE_V128: + { + bh_assert(0); + break; + } +#if WASM_ENABLE_REF_TYPES != 0 +#if WASM_ENABLE_GC == 0 + case VALUE_TYPE_FUNCREF: + { + out_results[i].kind = WASM_I32; + out_results[i].of.i32 = (int32)argv[p++]; + break; + } + case VALUE_TYPE_EXTERNREF: +#else + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#endif /* end of WASM_ENABLE_GC == 0 */ + { +#if UINTPTR_MAX == UINT32_MAX + out_results[i].kind = WASM_EXTERNREF; + out_results[i].of.foreign = (uintptr_t)argv[p++]; +#else + union { + uintptr_t val; + uint32 parts[2]; + } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_EXTERNREF; + out_results[i].of.foreign = u.val; +#endif + break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 */ + default: + bh_assert(0); + break; + } + } +} + +bool +wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t results[], + uint32 num_args, wasm_val_t args[]) +{ + uint32 argc, argv_buf[16] = { 0 }, *argv = argv_buf, cell_num, module_type; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + uint32 i, param_size_in_double_world = 0, result_size_in_double_world = 0; +#endif + uint64 total_size; + WASMFuncType *type; + bool ret = false; + + module_type = exec_env->module_inst->module_type; + type = wasm_runtime_get_function_type(function, module_type); + + if (!type) { + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be " + "enabled at least one."); + goto fail1; + } + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + for (i = 0; i < type->param_count; i++) { + param_size_in_double_world += + wasm_value_type_cell_num_outside(type->types[i]); + } + for (i = 0; i < type->result_count; i++) { + result_size_in_double_world += wasm_value_type_cell_num_outside( + type->types[type->param_count + i]); + } + argc = param_size_in_double_world; + cell_num = (argc >= result_size_in_double_world) + ? argc + : result_size_in_double_world; +#else + argc = type->param_cell_num; + cell_num = (argc > type->ret_cell_num) ? argc : type->ret_cell_num; +#endif + + if (num_results != type->result_count) { + LOG_ERROR( + "The result value number does not match the function declaration."); + goto fail1; + } + + if (num_args != type->param_count) { + LOG_ERROR("The argument value number does not match the function " + "declaration."); + goto fail1; + } + + total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2); + if (total_size > sizeof(argv_buf)) { + if (!(argv = + runtime_malloc(total_size, exec_env->module_inst, NULL, 0))) { + goto fail1; + } + } + + parse_args_to_uint32_array(type, args, argv); + if (!(ret = wasm_runtime_call_wasm(exec_env, function, argc, argv))) + goto fail2; + + parse_uint32_array_to_results(type, argv, results); + +fail2: + if (argv != argv_buf) + wasm_runtime_free(argv); +fail1: + return ret; +} + +bool +wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t results[], + uint32 num_args, ...) +{ + wasm_val_t args_buf[8] = { 0 }, *args = args_buf; + WASMFuncType *type = NULL; + bool ret = false; + uint64 total_size; + uint32 i = 0, module_type; + va_list vargs; + + module_type = exec_env->module_inst->module_type; + type = wasm_runtime_get_function_type(function, module_type); + + if (!type) { + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT " + "must be enabled at least one."); + goto fail1; + } + + if (num_args != type->param_count) { + LOG_ERROR("The argument value number does not match the " + "function declaration."); + goto fail1; + } + + total_size = sizeof(wasm_val_t) * (uint64)num_args; + if (total_size > sizeof(args_buf)) { + if (!(args = + runtime_malloc(total_size, exec_env->module_inst, NULL, 0))) { + goto fail1; + } + } + + va_start(vargs, num_args); + for (i = 0; i < num_args; i++) { + switch (type->types[i]) { + case VALUE_TYPE_I32: + args[i].kind = WASM_I32; + args[i].of.i32 = va_arg(vargs, uint32); + break; + case VALUE_TYPE_I64: + args[i].kind = WASM_I64; + args[i].of.i64 = va_arg(vargs, uint64); + break; + case VALUE_TYPE_F32: + args[i].kind = WASM_F32; + args[i].of.f32 = (float32)va_arg(vargs, float64); + break; + case VALUE_TYPE_F64: + args[i].kind = WASM_F64; + args[i].of.f64 = va_arg(vargs, float64); + break; + case VALUE_TYPE_V128: + bh_assert(0); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + { + args[i].kind = WASM_FUNCREF; + args[i].of.i32 = va_arg(vargs, uint32); + break; + } + case VALUE_TYPE_EXTERNREF: + { + args[i].kind = WASM_EXTERNREF; + args[i].of.foreign = va_arg(vargs, uintptr_t); + break; + } +#endif + default: + bh_assert(0); + break; + } + } + va_end(vargs); + + ret = wasm_runtime_call_wasm_a(exec_env, function, num_results, results, + num_args, args); + if (args != args_buf) + wasm_runtime_free(args); + +fail1: + return ret; +} + +bool +wasm_runtime_create_exec_env_singleton( + WASMModuleInstanceCommon *module_inst_comm) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module_inst_comm; + WASMExecEnv *exec_env = NULL; + + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + + if (module_inst->exec_env_singleton) { + return true; + } + + exec_env = wasm_exec_env_create(module_inst_comm, + module_inst->default_wasm_stack_size); + if (exec_env) + module_inst->exec_env_singleton = exec_env; + + return exec_env ? true : false; +} + +WASMExecEnv * +wasm_runtime_get_exec_env_singleton(WASMModuleInstanceCommon *module_inst_comm) +{ + 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); + + if (!module_inst->exec_env_singleton) { + wasm_runtime_create_exec_env_singleton(module_inst_comm); + } + return module_inst->exec_env_singleton; +} + +static void +wasm_set_exception_local(WASMModuleInstance *module_inst, const char *exception) +{ + exception_lock(module_inst); + if (exception) { + snprintf(module_inst->cur_exception, sizeof(module_inst->cur_exception), + "Exception: %s", exception); + } + else { + module_inst->cur_exception[0] = '\0'; + } + exception_unlock(module_inst); +} + +void +wasm_set_exception(WASMModuleInstance *module_inst, const char *exception) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + WASMExecEnv *exec_env = + wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst); + if (exec_env) { + wasm_cluster_set_exception(exec_env, exception); + } + else { + wasm_set_exception_local(module_inst, exception); + } +#else + wasm_set_exception_local(module_inst, exception); +#endif +} + +/* clang-format off */ +static const char *exception_msgs[] = { + "unreachable", /* EXCE_UNREACHABLE */ + "allocate memory failed", /* EXCE_OUT_OF_MEMORY */ + "out of bounds memory access", /* EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS */ + "integer overflow", /* EXCE_INTEGER_OVERFLOW */ + "integer divide by zero", /* EXCE_INTEGER_DIVIDE_BY_ZERO */ + "invalid conversion to integer", /* EXCE_INVALID_CONVERSION_TO_INTEGER */ + "indirect call type mismatch", /* EXCE_INVALID_FUNCTION_TYPE_INDEX */ + "invalid function index", /* EXCE_INVALID_FUNCTION_INDEX */ + "undefined element", /* EXCE_UNDEFINED_ELEMENT */ + "uninitialized element", /* EXCE_UNINITIALIZED_ELEMENT */ + "failed to call unlinked import function", /* EXCE_CALL_UNLINKED_IMPORT_FUNC */ + "native stack overflow", /* EXCE_NATIVE_STACK_OVERFLOW */ + "unaligned atomic", /* EXCE_UNALIGNED_ATOMIC */ + "wasm auxiliary stack overflow", /* EXCE_AUX_STACK_OVERFLOW */ + "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 */ + "failed to compile fast jit function", /* EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC */ + /* GC related exceptions */ + "null function reference", /* EXCE_NULL_FUNC_OBJ */ + "null structure reference", /* EXCE_NULL_STRUCT_OBJ */ + "null array reference", /* EXCE_NULL_ARRAY_OBJ */ + "null i31 reference", /* EXCE_NULL_I31_OBJ */ + "null reference", /* EXCE_NULL_REFERENCE */ + "create rtt type failed", /* EXCE_FAILED_TO_CREATE_RTT_TYPE */ + "create struct object failed", /* EXCE_FAILED_TO_CREATE_STRUCT_OBJ */ + "create array object failed", /* EXCE_FAILED_TO_CREATE_ARRAY_OBJ */ + "create externref object failed", /* EXCE_FAILED_TO_CREATE_EXTERNREF_OBJ */ + "cast failure", /* EXCE_CAST_FAILURE */ + "out of bounds array access", /* EXCE_ARRAY_IDX_OOB */ + /* stringref related exceptions */ + "create string object failed", /* EXCE_FAILED_TO_CREATE_STRING */ + "create stringref failed", /* EXCE_FAILED_TO_CREATE_STRINGREF */ + "create stringview failed", /* EXCE_FAILED_TO_CREATE_STRINGVIEW */ + "encode failed", /* EXCE_FAILED_TO_ENCODE_STRING */ + "", /* EXCE_ALREADY_THROWN */ +}; +/* clang-format on */ + +void +wasm_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id) +{ + if (id < EXCE_NUM) + wasm_set_exception(module_inst, exception_msgs[id]); + else + wasm_set_exception(module_inst, "unknown exception"); +} + +const char * +wasm_get_exception(WASMModuleInstance *module_inst) +{ + if (module_inst->cur_exception[0] == '\0') + return NULL; + else + return module_inst->cur_exception; +} + +bool +wasm_copy_exception(WASMModuleInstance *module_inst, char *exception_buf) +{ + bool has_exception = false; + + exception_lock(module_inst); + 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; + } + exception_unlock(module_inst); + + return has_exception; +} + +void +wasm_runtime_set_exception(WASMModuleInstanceCommon *module_inst_comm, + const char *exception) +{ + 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); + wasm_set_exception(module_inst, exception); +} + +const char * +wasm_runtime_get_exception(WASMModuleInstanceCommon *module_inst_comm) +{ + 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_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) +{ + bh_assert(module_inst_comm->module_type == Wasm_Module_Bytecode + || module_inst_comm->module_type == Wasm_Module_AoT); + wasm_runtime_set_exception(module_inst_comm, NULL); +} + +void +wasm_runtime_terminate(WASMModuleInstanceCommon *module_inst_comm) +{ + 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); + wasm_set_exception(module_inst, "terminated by user"); +} + +void +wasm_runtime_set_custom_data_internal( + WASMModuleInstanceCommon *module_inst_comm, void *custom_data) +{ + 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); + module_inst->custom_data = custom_data; +} + +void +wasm_runtime_set_custom_data(WASMModuleInstanceCommon *module_inst, + void *custom_data) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_spread_custom_data(module_inst, custom_data); +#else + wasm_runtime_set_custom_data_internal(module_inst, custom_data); +#endif +} + +void * +wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst_comm) +{ + 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 module_inst->custom_data; +} + +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 +void +wasm_runtime_set_bounds_checks(WASMModuleInstanceCommon *module_inst, + bool enable) +{ + /* Always disable bounds checks if hw bounds checks is enabled */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + enable = false; +#endif +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + ((WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e) + ->common.disable_bounds_checks = enable ? false : true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + ((AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e) + ->common.disable_bounds_checks = enable ? false : true; + } +#endif +} + +bool +wasm_runtime_is_bounds_checks_enabled(WASMModuleInstanceCommon *module_inst) +{ + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return !((WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst) + ->e) + ->common.disable_bounds_checks; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return !((AOTModuleInstanceExtra *)((WASMModuleInstance *)module_inst) + ->e) + ->common.disable_bounds_checks; + } +#endif + + return true; +} +#endif + +uint64 +wasm_runtime_module_malloc_internal(WASMModuleInstanceCommon *module_inst, + WASMExecEnv *exec_env, uint64 size, + void **p_native_addr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_module_malloc_internal((WASMModuleInstance *)module_inst, + exec_env, size, p_native_addr); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_module_malloc_internal((AOTModuleInstance *)module_inst, + exec_env, size, p_native_addr); +#endif + return 0; +} + +uint64 +wasm_runtime_module_realloc_internal(WASMModuleInstanceCommon *module_inst, + WASMExecEnv *exec_env, uint64 ptr, + uint64 size, void **p_native_addr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_module_realloc_internal((WASMModuleInstance *)module_inst, + exec_env, ptr, size, p_native_addr); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_module_realloc_internal((AOTModuleInstance *)module_inst, + exec_env, ptr, size, p_native_addr); +#endif + return 0; +} + +void +wasm_runtime_module_free_internal(WASMModuleInstanceCommon *module_inst, + WASMExecEnv *exec_env, uint64 ptr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_module_free_internal((WASMModuleInstance *)module_inst, exec_env, + ptr); + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_module_free_internal((AOTModuleInstance *)module_inst, exec_env, + ptr); + return; + } +#endif +} + +uint64 +wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint64 size, + void **p_native_addr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_module_malloc((WASMModuleInstance *)module_inst, size, + p_native_addr); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_module_malloc((AOTModuleInstance *)module_inst, size, + p_native_addr); +#endif + return 0; +} + +uint64 +wasm_runtime_module_realloc(WASMModuleInstanceCommon *module_inst, uint64 ptr, + uint64 size, void **p_native_addr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_module_realloc((WASMModuleInstance *)module_inst, ptr, size, + p_native_addr); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_module_realloc((AOTModuleInstance *)module_inst, ptr, size, + p_native_addr); +#endif + return 0; +} + +void +wasm_runtime_module_free(WASMModuleInstanceCommon *module_inst, uint64 ptr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_module_free((WASMModuleInstance *)module_inst, ptr); + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_module_free((AOTModuleInstance *)module_inst, ptr); + return; + } +#endif +} + +uint64 +wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, + const char *src, uint64 size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_module_dup_data((WASMModuleInstance *)module_inst, src, + size); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_module_dup_data((AOTModuleInstance *)module_inst, src, size); + } +#endif + return 0; +} + +#if WASM_ENABLE_LIBC_WASI != 0 + +void +wasi_args_set_defaults(WASIArguments *args) +{ + memset(args, 0, sizeof(*args)); +#if WASM_ENABLE_UVWASI == 0 + args->stdio[0] = os_invalid_raw_handle(); + args->stdio[1] = os_invalid_raw_handle(); + args->stdio[2] = os_invalid_raw_handle(); +#else + args->stdio[0] = os_get_invalid_handle(); + args->stdio[1] = os_get_invalid_handle(); + args->stdio[2] = os_get_invalid_handle(); +#endif /* WASM_ENABLE_UVWASI == 0 */ +} + +static WASIArguments * +get_wasi_args_from_module(wasm_module_t module) +{ + WASIArguments *wasi_args = NULL; + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + if (module->module_type == Wasm_Module_Bytecode) + wasi_args = &((WASMModule *)module)->wasi_args; +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + wasi_args = &((AOTModule *)module)->wasi_args; +#endif + + return wasi_args; +} + +void +wasm_runtime_set_wasi_args_ex(WASMModuleCommon *module, const char *dir_list[], + uint32 dir_count, const char *map_dir_list[], + uint32 map_dir_count, const char *env_list[], + uint32 env_count, char *argv[], int argc, + int64 stdinfd, int64 stdoutfd, int64 stderrfd) +{ + WASIArguments *wasi_args = get_wasi_args_from_module(module); + + bh_assert(wasi_args); + + wasi_args->dir_list = dir_list; + wasi_args->dir_count = dir_count; + wasi_args->map_dir_list = map_dir_list; + wasi_args->map_dir_count = map_dir_count; + wasi_args->env = env_list; + wasi_args->env_count = env_count; + wasi_args->argv = argv; + wasi_args->argc = (uint32)argc; + wasi_args->stdio[0] = (os_raw_file_handle)stdinfd; + wasi_args->stdio[1] = (os_raw_file_handle)stdoutfd; + wasi_args->stdio[2] = (os_raw_file_handle)stderrfd; + wasi_args->set_by_user = true; + +#if WASM_ENABLE_MULTI_MODULE != 0 +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + wasm_propagate_wasi_args((WASMModule *)module); + } +#endif +#endif +} + +void +wasm_runtime_set_wasi_args(WASMModuleCommon *module, const char *dir_list[], + uint32 dir_count, const char *map_dir_list[], + uint32 map_dir_count, const char *env_list[], + uint32 env_count, char *argv[], int argc) +{ + wasm_runtime_set_wasi_args_ex(module, dir_list, dir_count, map_dir_list, + map_dir_count, env_list, env_count, argv, + argc, -1, -1, -1); +} + +void +wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], + uint32 addr_pool_size) +{ + WASIArguments *wasi_args = get_wasi_args_from_module(module); + + if (wasi_args) { + wasi_args->addr_pool = addr_pool; + wasi_args->addr_count = addr_pool_size; + wasi_args->set_by_user = true; + } +} + +void +wasm_runtime_set_wasi_ns_lookup_pool(wasm_module_t module, + const char *ns_lookup_pool[], + uint32 ns_lookup_pool_size) +{ + WASIArguments *wasi_args = get_wasi_args_from_module(module); + + if (wasi_args) { + wasi_args->ns_lookup_pool = ns_lookup_pool; + wasi_args->ns_lookup_count = ns_lookup_pool_size; + wasi_args->set_by_user = true; + } +} + +#if WASM_ENABLE_UVWASI == 0 +static bool +copy_string_array(const char *array[], uint32 array_size, char **buf_ptr, + char ***list_ptr, uint64 *out_buf_size) +{ + uint64 buf_size = 0, total_size; + uint32 buf_offset = 0, i; + char *buf = NULL, **list = NULL; + + for (i = 0; i < array_size; i++) + buf_size += strlen(array[i]) + 1; + + /* We add +1 to generate null-terminated array of strings */ + total_size = sizeof(char *) * ((uint64)array_size + 1); + if (total_size >= UINT32_MAX + /* total_size must be larger than 0, don' check it again */ + || !(list = wasm_runtime_malloc((uint32)total_size)) + || buf_size >= UINT32_MAX + || (buf_size > 0 && !(buf = wasm_runtime_malloc((uint32)buf_size)))) { + + if (buf) + wasm_runtime_free(buf); + if (list) + wasm_runtime_free(list); + return false; + } + + for (i = 0; i < array_size; i++) { + list[i] = buf + buf_offset; + bh_strcpy_s(buf + buf_offset, (uint32)buf_size - buf_offset, array[i]); + buf_offset += (uint32)(strlen(array[i]) + 1); + } + list[array_size] = NULL; + + *list_ptr = list; + *buf_ptr = buf; + if (out_buf_size) + *out_buf_size = buf_size; + + return true; +} + +bool +wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, + const char *dir_list[], uint32 dir_count, + const char *map_dir_list[], uint32 map_dir_count, + const char *env[], uint32 env_count, + const char *addr_pool[], uint32 addr_pool_size, + const char *ns_lookup_pool[], uint32 ns_lookup_pool_size, + char *argv[], uint32 argc, os_raw_file_handle stdinfd, + os_raw_file_handle stdoutfd, os_raw_file_handle stderrfd, + char *error_buf, uint32 error_buf_size) +{ + WASIContext *wasi_ctx; + char *argv_buf = NULL; + char **argv_list = NULL; + char *env_buf = NULL; + char **env_list = NULL; + char *ns_lookup_buf = NULL; + char **ns_lookup_list = NULL; + uint64 argv_buf_size = 0, env_buf_size = 0; + struct fd_table *curfds = NULL; + struct fd_prestats *prestats = NULL; + struct argv_environ_values *argv_environ = NULL; + struct addr_pool *apool = NULL; + bool fd_table_inited = false, fd_prestats_inited = false; + bool argv_environ_inited = false; + bool addr_pool_inited = false; + __wasi_fd_t wasm_fd = 3; + os_file_handle file_handle; + char *path, resolved_path[PATH_MAX]; + uint32 i; + + if (!(wasi_ctx = runtime_malloc(sizeof(WASIContext), NULL, error_buf, + error_buf_size))) { + return false; + } + + wasm_runtime_set_wasi_ctx(module_inst, wasi_ctx); + + /* process argv[0], trip the path and suffix, only keep the program name + */ + if (!copy_string_array((const char **)argv, argc, &argv_buf, &argv_list, + &argv_buf_size)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: allocate memory failed"); + goto fail; + } + + if (!copy_string_array(env, env_count, &env_buf, &env_list, + &env_buf_size)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: allocate memory failed"); + goto fail; + } + + if (!(curfds = wasm_runtime_malloc(sizeof(struct fd_table))) + || !(prestats = wasm_runtime_malloc(sizeof(struct fd_prestats))) + || !(argv_environ = + wasm_runtime_malloc(sizeof(struct argv_environ_values))) + || !(apool = wasm_runtime_malloc(sizeof(struct addr_pool)))) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: allocate memory failed"); + goto fail; + } + + if (!fd_table_init(curfds)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init fd table failed"); + goto fail; + } + fd_table_inited = true; + + if (!fd_prestats_init(prestats)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init fd prestats failed"); + goto fail; + } + fd_prestats_inited = true; + + if (!argv_environ_init(argv_environ, argv_buf, argv_buf_size, argv_list, + argc, env_buf, env_buf_size, env_list, env_count)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init argument environment failed"); + goto fail; + } + argv_environ_inited = true; + + if (!addr_pool_init(apool)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init the address pool failed"); + goto fail; + } + addr_pool_inited = true; + + os_file_handle stdin_file_handle = os_convert_stdin_handle(stdinfd); + os_file_handle stdout_file_handle = os_convert_stdout_handle(stdoutfd); + os_file_handle stderr_file_handle = os_convert_stderr_handle(stderrfd); + + if (!os_is_handle_valid(&stdin_file_handle) + || !os_is_handle_valid(&stdout_file_handle) + || !os_is_handle_valid(&stderr_file_handle)) + goto fail; + + /* Prepopulate curfds with stdin, stdout, and stderr file descriptors. */ + if (!fd_table_insert_existing(curfds, 0, stdin_file_handle, true) + || !fd_table_insert_existing(curfds, 1, stdout_file_handle, true) + || !fd_table_insert_existing(curfds, 2, stderr_file_handle, true)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: init fd table failed"); + goto fail; + } + + wasm_fd = 3; + for (i = 0; i < dir_count; i++, wasm_fd++) { + path = os_realpath(dir_list[i], resolved_path); + if (!path) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening directory %s: %d\n", + dir_list[i], errno); + goto fail; + } + + __wasi_errno_t error = os_open_preopendir(path, &file_handle); + + if (error != __WASI_ESUCCESS) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening directory %s: %d\n", + dir_list[i], error); + goto fail; + } + + if (!fd_table_insert_existing(curfds, wasm_fd, file_handle, false)) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error inserting preopen fd %u (directory %s) into fd " + "table", + (unsigned int)wasm_fd, dir_list[i]); + goto fail; + } + + if (!fd_prestats_insert(prestats, dir_list[i], wasm_fd)) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error inserting preopen fd %u (directory %s) into " + "prestats table", + (unsigned int)wasm_fd, dir_list[i]); + goto fail; + } + } + + for (i = 0; i < map_dir_count; i++, wasm_fd++) { + char mapping_copy_buf[256]; + char *mapping_copy = mapping_copy_buf; + char *map_mapped = NULL, *map_host = NULL; + const unsigned long max_len = + (unsigned long)strlen(map_dir_list[i]) * 2 + 3; + + /* Allocation limit for runtime environments with reduced stack size */ + if (max_len > 256) { + if (!(mapping_copy = wasm_runtime_malloc(max_len))) { + snprintf(error_buf, error_buf_size, + "error while allocating for directory mapping\n"); + goto fail; + } + } + + bh_memcpy_s(mapping_copy, max_len, map_dir_list[i], + (uint32)(strlen(map_dir_list[i]) + 1)); + + const char *delim = "::"; + char *delim_pos = strstr(mapping_copy, delim); + if (delim_pos) { + *delim_pos = '\0'; + map_mapped = mapping_copy; + map_host = delim_pos + strlen(delim); + } + + if (!map_mapped || !map_host) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening mapped directory: " + "invalid map\n"); + if (mapping_copy != mapping_copy_buf) + wasm_runtime_free(mapping_copy); + goto fail; + } + + path = os_realpath(map_host, resolved_path); + if (!path) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening mapped directory %s: %d\n", + map_host, errno); + if (mapping_copy != mapping_copy_buf) + wasm_runtime_free(mapping_copy); + goto fail; + } + + __wasi_errno_t error = os_open_preopendir(path, &file_handle); + if (error != __WASI_ESUCCESS) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening mapped directory %s: %d\n", + map_host, errno); + if (mapping_copy != mapping_copy_buf) + wasm_runtime_free(mapping_copy); + goto fail; + } + + if (!fd_table_insert_existing(curfds, wasm_fd, file_handle, false) + || !fd_prestats_insert(prestats, map_mapped, wasm_fd)) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening mapped directory %s: " + "insertion failed\n", + dir_list[i]); + if (mapping_copy != mapping_copy_buf) + wasm_runtime_free(mapping_copy); + goto fail; + } + + if (mapping_copy != mapping_copy_buf) + wasm_runtime_free(mapping_copy); + } + + /* addr_pool(textual) -> apool */ + for (i = 0; i < addr_pool_size; i++) { + char *cp, *address, *mask, *nextptr, *endptr; + long mask_val; + bool ret = false; + + cp = bh_strdup(addr_pool[i]); + if (!cp) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: copy address failed"); + goto fail; + } + + address = bh_strtok_r(cp, "/", &nextptr); + mask = bh_strtok_r(NULL, "/", &nextptr); + + if (!mask) { + snprintf(error_buf, error_buf_size, + "Invalid address pool entry: %s, must be in the format of " + "ADDRESS/MASK", + addr_pool[i]); + wasm_runtime_free(cp); + goto fail; + } + + errno = 0; + mask_val = strtol(mask, &endptr, 10); + + if (mask == endptr || *endptr != '\0') { + snprintf(error_buf, error_buf_size, + "Invalid address pool entry: mask must be a number"); + wasm_runtime_free(cp); + goto fail; + } + if (errno != 0 || mask_val < 0) { + snprintf(error_buf, error_buf_size, + "Init wasi environment failed: invalid mask number"); + wasm_runtime_free(cp); + goto fail; + } + + ret = addr_pool_insert(apool, address, (uint8)mask_val); + wasm_runtime_free(cp); + if (!ret) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: store address failed"); + goto fail; + } + } + + if (!copy_string_array(ns_lookup_pool, ns_lookup_pool_size, &ns_lookup_buf, + &ns_lookup_list, NULL)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: allocate memory failed"); + goto fail; + } + + wasi_ctx->curfds = curfds; + wasi_ctx->prestats = prestats; + wasi_ctx->argv_environ = argv_environ; + wasi_ctx->addr_pool = apool; + wasi_ctx->argv_buf = argv_buf; + wasi_ctx->argv_list = argv_list; + wasi_ctx->env_buf = env_buf; + wasi_ctx->env_list = env_list; + wasi_ctx->ns_lookup_buf = ns_lookup_buf; + wasi_ctx->ns_lookup_list = ns_lookup_list; + + return true; + +fail: + if (argv_environ_inited) + argv_environ_destroy(argv_environ); + if (fd_prestats_inited) + fd_prestats_destroy(prestats); + if (fd_table_inited) + fd_table_destroy(curfds); + if (addr_pool_inited) + addr_pool_destroy(apool); + if (curfds) + wasm_runtime_free(curfds); + if (prestats) + wasm_runtime_free(prestats); + if (argv_environ) + wasm_runtime_free(argv_environ); + if (apool) + wasm_runtime_free(apool); + if (argv_buf) + wasm_runtime_free(argv_buf); + if (argv_list) + wasm_runtime_free(argv_list); + if (env_buf) + wasm_runtime_free(env_buf); + if (env_list) + wasm_runtime_free(env_list); + if (ns_lookup_buf) + wasm_runtime_free(ns_lookup_buf); + if (ns_lookup_list) + wasm_runtime_free(ns_lookup_list); + return false; +} +#else /* else of WASM_ENABLE_UVWASI == 0 */ +static void * +wasm_uvwasi_malloc(size_t size, void *mem_user_data) +{ + return runtime_malloc(size, NULL, NULL, 0); + (void)mem_user_data; +} + +static void +wasm_uvwasi_free(void *ptr, void *mem_user_data) +{ + if (ptr) + wasm_runtime_free(ptr); + (void)mem_user_data; +} + +static void * +wasm_uvwasi_calloc(size_t nmemb, size_t size, void *mem_user_data) +{ + uint64 total_size = (uint64)nmemb * size; + return runtime_malloc(total_size, NULL, NULL, 0); + (void)mem_user_data; +} + +static void * +wasm_uvwasi_realloc(void *ptr, size_t size, void *mem_user_data) +{ + if (size >= UINT32_MAX) { + return NULL; + } + return wasm_runtime_realloc(ptr, (uint32)size); +} + +/* clang-format off */ +static uvwasi_mem_t uvwasi_allocator = { + .mem_user_data = 0, + .malloc = wasm_uvwasi_malloc, + .free = wasm_uvwasi_free, + .calloc = wasm_uvwasi_calloc, + .realloc = wasm_uvwasi_realloc +}; +/* clang-format on */ + +bool +wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, + const char *dir_list[], uint32 dir_count, + const char *map_dir_list[], uint32 map_dir_count, + const char *env[], uint32 env_count, + const char *addr_pool[], uint32 addr_pool_size, + const char *ns_lookup_pool[], uint32 ns_lookup_pool_size, + char *argv[], uint32 argc, os_raw_file_handle stdinfd, + os_raw_file_handle stdoutfd, os_raw_file_handle stderrfd, + char *error_buf, uint32 error_buf_size) +{ + WASIContext *ctx; + uvwasi_t *uvwasi; + uvwasi_options_t init_options; + const char **envp = NULL; + uint64 total_size; + uint32 i; + bool ret = false; + + ctx = runtime_malloc(sizeof(*ctx), module_inst, error_buf, error_buf_size); + if (!ctx) + return false; + uvwasi = &ctx->uvwasi; + + /* Setup the initialization options */ + uvwasi_options_init(&init_options); + init_options.allocator = &uvwasi_allocator; + init_options.argc = argc; + init_options.argv = (const char **)argv; + init_options.in = (stdinfd != os_get_invalid_handle()) + ? (uvwasi_fd_t)stdinfd + : init_options.in; + init_options.out = (stdoutfd != os_get_invalid_handle()) + ? (uvwasi_fd_t)stdoutfd + : init_options.out; + init_options.err = (stderrfd != os_get_invalid_handle()) + ? (uvwasi_fd_t)stderrfd + : init_options.err; + + if (dir_count > 0) { + init_options.preopenc = dir_count; + + total_size = sizeof(uvwasi_preopen_t) * (uint64)init_options.preopenc; + init_options.preopens = (uvwasi_preopen_t *)runtime_malloc( + total_size, module_inst, error_buf, error_buf_size); + if (init_options.preopens == NULL) + goto fail; + + for (i = 0; i < init_options.preopenc; i++) { + init_options.preopens[i].real_path = dir_list[i]; + init_options.preopens[i].mapped_path = + (i < map_dir_count) ? map_dir_list[i] : dir_list[i]; + } + } + + if (env_count > 0) { + total_size = sizeof(char *) * (uint64)(env_count + 1); + envp = + runtime_malloc(total_size, module_inst, error_buf, error_buf_size); + if (envp == NULL) + goto fail; + + for (i = 0; i < env_count; i++) { + envp[i] = env[i]; + } + envp[env_count] = NULL; + init_options.envp = envp; + } + + if (UVWASI_ESUCCESS != uvwasi_init(uvwasi, &init_options)) { + set_error_buf(error_buf, error_buf_size, "uvwasi init failed"); + goto fail; + } + + wasm_runtime_set_wasi_ctx(module_inst, ctx); + + ret = true; + +fail: + if (envp) + wasm_runtime_free((void *)envp); + + if (init_options.preopens) + wasm_runtime_free(init_options.preopens); + + if (!ret && uvwasi) + wasm_runtime_free(uvwasi); + + return ret; +} +#endif /* end of WASM_ENABLE_UVWASI */ + +bool +wasm_runtime_is_wasi_mode(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode + && ((WASMModuleInstance *)module_inst)->module->import_wasi_api) + return true; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT + && ((AOTModule *)((AOTModuleInstance *)module_inst)->module) + ->import_wasi_api) + return true; +#endif + return false; +} + +WASMFunctionInstanceCommon * +wasm_runtime_lookup_wasi_start_function(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *wasm_inst = (WASMModuleInstance *)module_inst; + WASMFunctionInstance *func = wasm_lookup_function(wasm_inst, "_start"); + if (func) { + if (func->u.func->func_type->param_count != 0 + || func->u.func->func_type->result_count != 0) { + LOG_ERROR("Lookup wasi _start function failed: " + "invalid function type.\n"); + return NULL; + } + return (WASMFunctionInstanceCommon *)func; + } + return NULL; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_inst = (AOTModuleInstance *)module_inst; + AOTFunctionInstance *func = aot_lookup_function(aot_inst, "_start"); + if (func) { + AOTFuncType *func_type = func->u.func.func_type; + if (func_type->param_count != 0 || func_type->result_count != 0) { + LOG_ERROR("Lookup wasi _start function failed: " + "invalid function type.\n"); + return NULL; + } + return func; + } + return NULL; + } +#endif /* end of WASM_ENABLE_AOT */ + + return NULL; +} + +#if WASM_ENABLE_UVWASI == 0 +void +wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst) +{ + WASIContext *wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + + if (wasi_ctx) { + if (wasi_ctx->argv_environ) { + argv_environ_destroy(wasi_ctx->argv_environ); + wasm_runtime_free(wasi_ctx->argv_environ); + } + if (wasi_ctx->curfds) { + fd_table_destroy(wasi_ctx->curfds); + wasm_runtime_free(wasi_ctx->curfds); + } + if (wasi_ctx->prestats) { + fd_prestats_destroy(wasi_ctx->prestats); + wasm_runtime_free(wasi_ctx->prestats); + } + if (wasi_ctx->addr_pool) { + addr_pool_destroy(wasi_ctx->addr_pool); + wasm_runtime_free(wasi_ctx->addr_pool); + } + if (wasi_ctx->argv_buf) + wasm_runtime_free(wasi_ctx->argv_buf); + if (wasi_ctx->argv_list) + wasm_runtime_free(wasi_ctx->argv_list); + if (wasi_ctx->env_buf) + wasm_runtime_free(wasi_ctx->env_buf); + if (wasi_ctx->env_list) + wasm_runtime_free(wasi_ctx->env_list); + if (wasi_ctx->ns_lookup_buf) + wasm_runtime_free(wasi_ctx->ns_lookup_buf); + if (wasi_ctx->ns_lookup_list) + wasm_runtime_free(wasi_ctx->ns_lookup_list); + + wasm_runtime_free(wasi_ctx); + } +} +#else +void +wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst) +{ + WASIContext *wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + + if (wasi_ctx) { + uvwasi_destroy(&wasi_ctx->uvwasi); + wasm_runtime_free(wasi_ctx); + } +} +#endif + +uint32_t +wasm_runtime_get_wasi_exit_code(WASMModuleInstanceCommon *module_inst) +{ + WASIContext *wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); +#if WASM_ENABLE_THREAD_MGR != 0 + WASMCluster *cluster; + WASMExecEnv *exec_env; + + exec_env = wasm_runtime_get_exec_env_singleton(module_inst); + if (exec_env && (cluster = wasm_exec_env_get_cluster(exec_env))) { + /** + * The main thread may exit earlier than other threads, and + * the exit_code of wasi_ctx may be changed by other thread + * when it runs into wasi_proc_exit, here we wait until all + * other threads exit to avoid getting invalid exit_code. + */ + wasm_cluster_wait_for_all_except_self(cluster, exec_env); + } +#endif + return wasi_ctx->exit_code; +} +#endif /* end of WASM_ENABLE_LIBC_WASI */ + +WASMModuleCommon * +wasm_exec_env_get_module(WASMExecEnv *exec_env) +{ + WASMModuleInstanceCommon *module_inst_comm = + wasm_runtime_get_module_inst(exec_env); + 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 (WASMModuleCommon *)module_inst->module; +} + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +const uint8 * +wasm_runtime_get_custom_section(WASMModuleCommon *const module_comm, + const char *name, uint32 *len) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_comm->module_type == Wasm_Module_Bytecode) + return wasm_loader_get_custom_section((WASMModule *)module_comm, name, + len); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_comm->module_type == Wasm_Module_AoT) + return aot_get_custom_section((AOTModule *)module_comm, name, len); +#endif + return NULL; +} +#endif /* end of WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 */ + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) /* NOLINT */ + +int32 +wasm_runtime_get_import_count(WASMModuleCommon *const module) +{ + if (!module) { + bh_assert(0); + return -1; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + return (int32)(aot_module->import_func_count + + aot_module->import_global_count + + aot_module->import_table_count + + aot_module->import_memory_count); + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + return (int32)wasm_module->import_count; + } +#endif + + return -1; +} + +void +wasm_runtime_get_import_type(WASMModuleCommon *const module, int32 import_index, + wasm_import_t *import_type) +{ + if (!import_type) { + bh_assert(0); + return; + } + + memset(import_type, 0, sizeof(wasm_import_t)); + + if (!module) { + bh_assert(0); + return; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + + uint32 func_index = (uint32)import_index; + if (func_index < aot_module->import_func_count) { + const AOTImportFunc *aot_import_func = + &aot_module->import_funcs[func_index]; + import_type->module_name = aot_import_func->module_name; + import_type->name = aot_import_func->func_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_FUNC; + import_type->linked = + aot_import_func->func_ptr_linked ? true : false; + import_type->u.func_type = + (WASMFuncType *)aot_import_func->func_type; + return; + } + + uint32 global_index = func_index - aot_module->import_func_count; + if (global_index < aot_module->import_global_count) { + const AOTImportGlobal *aot_import_global = + &aot_module->import_globals[global_index]; + import_type->module_name = aot_import_global->module_name; + import_type->name = aot_import_global->global_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_GLOBAL; + import_type->linked = aot_import_global->is_linked; + import_type->u.global_type = + (WASMGlobalType *)&aot_import_global->type; + return; + } + + uint32 table_index = global_index - aot_module->import_global_count; + if (table_index < aot_module->import_table_count) { + const AOTImportTable *aot_import_table = + &aot_module->import_tables[table_index]; + import_type->module_name = aot_import_table->module_name; + import_type->name = aot_import_table->table_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_TABLE; + import_type->linked = false; /* not supported */ + import_type->u.table_type = + (WASMTableType *)&aot_import_table->table_type; + return; + } + + uint32 memory_index = table_index - aot_module->import_table_count; + if (memory_index < aot_module->import_memory_count) { + const AOTImportMemory *aot_import_memory = + &aot_module->import_memories[memory_index]; + import_type->module_name = aot_import_memory->module_name; + import_type->name = aot_import_memory->memory_name; + import_type->kind = WASM_IMPORT_EXPORT_KIND_MEMORY; + import_type->linked = false; /* not supported */ + import_type->u.memory_type = + (WASMMemoryType *)&aot_import_memory->mem_type; + return; + } + + bh_assert(0); + return; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + + if ((uint32)import_index >= wasm_module->import_count) { + bh_assert(0); + return; + } + + const WASMImport *wasm_import = &wasm_module->imports[import_index]; + + import_type->module_name = wasm_import->u.names.module_name; + import_type->name = wasm_import->u.names.field_name; + import_type->kind = wasm_import->kind; + switch (import_type->kind) { + case WASM_IMPORT_EXPORT_KIND_FUNC: + import_type->linked = wasm_import->u.function.func_ptr_linked; + import_type->u.func_type = + (WASMFuncType *)wasm_import->u.function.func_type; + break; + case WASM_IMPORT_EXPORT_KIND_GLOBAL: + import_type->linked = wasm_import->u.global.is_linked; + import_type->u.global_type = + (WASMGlobalType *)&wasm_import->u.global.type; + break; + case WASM_IMPORT_EXPORT_KIND_TABLE: + import_type->linked = false; /* not supported */ + import_type->u.table_type = + (WASMTableType *)&wasm_import->u.table.table_type; + break; + case WASM_IMPORT_EXPORT_KIND_MEMORY: + import_type->linked = false; /* not supported */ + import_type->u.memory_type = + (WASMMemoryType *)&wasm_import->u.memory.mem_type; + break; + default: + bh_assert(0); + break; + } + + return; + } +#endif +} + +int32 +wasm_runtime_get_export_count(WASMModuleCommon *const module) +{ + if (!module) { + bh_assert(0); + return -1; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + return (int32)aot_module->export_count; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + return (int32)wasm_module->export_count; + } +#endif + + return -1; +} + +void +wasm_runtime_get_export_type(WASMModuleCommon *const module, int32 export_index, + wasm_export_t *export_type) +{ + if (!export_type) { + bh_assert(0); + return; + } + + memset(export_type, 0, sizeof(wasm_export_t)); + + if (!module) { + bh_assert(0); + return; + } + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + const AOTModule *aot_module = (const AOTModule *)module; + + if ((uint32)export_index >= aot_module->export_count) { + bh_assert(0); + return; + } + + const AOTExport *aot_export = &aot_module->exports[export_index]; + export_type->name = aot_export->name; + export_type->kind = aot_export->kind; + switch (export_type->kind) { + case WASM_IMPORT_EXPORT_KIND_FUNC: + { + if (aot_export->index < aot_module->import_func_count) { + export_type->u.func_type = + (AOTFuncType *)aot_module + ->import_funcs[aot_export->index] + .func_type; + } + else { + export_type->u.func_type = + (AOTFuncType *)aot_module + ->types[aot_module->func_type_indexes + [aot_export->index + - aot_module->import_func_count]]; + } + break; + } + case WASM_IMPORT_EXPORT_KIND_GLOBAL: + { + if (aot_export->index < aot_module->import_global_count) { + export_type->u.global_type = + &aot_module->import_globals[aot_export->index].type; + } + else { + export_type->u.global_type = + &aot_module + ->globals[aot_export->index + - aot_module->import_global_count] + .type; + } + break; + } + case WASM_IMPORT_EXPORT_KIND_TABLE: + { + if (aot_export->index < aot_module->import_table_count) { + export_type->u.table_type = + &aot_module->import_tables[aot_export->index] + .table_type; + } + else { + export_type->u.table_type = + &aot_module + ->tables[aot_export->index + - aot_module->import_table_count] + .table_type; + } + break; + } + case WASM_IMPORT_EXPORT_KIND_MEMORY: + { + if (aot_export->index < aot_module->import_memory_count) { + export_type->u.memory_type = + &aot_module->import_memories[aot_export->index] + .mem_type; + } + else { + export_type->u.memory_type = + &aot_module + ->memories[aot_export->index + - aot_module->import_memory_count]; + } + break; + } + default: + bh_assert(0); + break; + } + return; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + const WASMModule *wasm_module = (const WASMModule *)module; + + if ((uint32)export_index >= wasm_module->export_count) { + bh_assert(0); + return; + } + + const WASMExport *wasm_export = &wasm_module->exports[export_index]; + export_type->name = wasm_export->name; + export_type->kind = wasm_export->kind; + switch (export_type->kind) { + case WASM_IMPORT_EXPORT_KIND_FUNC: + { + if (wasm_export->index < wasm_module->import_function_count) { + export_type->u.func_type = + (WASMFuncType *)wasm_module + ->import_functions[wasm_export->index] + .u.function.func_type; + } + else { + export_type->u.func_type = + wasm_module + ->functions[wasm_export->index + - wasm_module->import_function_count] + ->func_type; + } + + break; + } + case WASM_IMPORT_EXPORT_KIND_GLOBAL: + { + if (wasm_export->index < wasm_module->import_global_count) { + export_type->u.global_type = + (WASMGlobalType *)&wasm_module + ->import_globals[wasm_export->index] + .u.global.type; + } + else { + export_type->u.global_type = + &wasm_module + ->globals[wasm_export->index + - wasm_module->import_global_count] + .type; + } + + break; + } + case WASM_IMPORT_EXPORT_KIND_TABLE: + { + if (wasm_export->index < wasm_module->import_table_count) { + export_type->u.table_type = + (WASMTableType *)&wasm_module + ->import_tables[wasm_export->index] + .u.table.table_type; + } + else { + export_type->u.table_type = + &wasm_module + ->tables[wasm_export->index + - wasm_module->import_table_count] + .table_type; + } + + break; + } + case WASM_IMPORT_EXPORT_KIND_MEMORY: + { + if (wasm_export->index < wasm_module->import_memory_count) { + export_type->u.memory_type = + (WASMMemoryType *)&wasm_module + ->import_memories[wasm_export->index] + .u.memory.mem_type; + } + else { + export_type->u.memory_type = + &wasm_module + ->memories[wasm_export->index + - wasm_module->import_memory_count]; + } + + break; + } + default: + bh_assert(0); + break; + } + return; + } +#endif +} + +uint32 +wasm_func_type_get_param_count(WASMFuncType *const func_type) +{ + bh_assert(func_type); + + return func_type->param_count; +} + +wasm_valkind_t +wasm_func_type_get_param_valkind(WASMFuncType *const func_type, + uint32 param_index) +{ + if (!func_type || (param_index >= func_type->param_count)) { + bh_assert(0); + return (wasm_valkind_t)-1; + } + + switch (func_type->types[param_index]) { + case VALUE_TYPE_I32: + return WASM_I32; + case VALUE_TYPE_I64: + return WASM_I64; + case VALUE_TYPE_F32: + return WASM_F32; + case VALUE_TYPE_F64: + return WASM_F64; + case VALUE_TYPE_V128: + return WASM_V128; + case VALUE_TYPE_FUNCREF: + return WASM_FUNCREF; + case VALUE_TYPE_EXTERNREF: + return WASM_EXTERNREF; + + case VALUE_TYPE_VOID: + default: + { + bh_assert(0); + return (wasm_valkind_t)-1; + } + } +} + +uint32 +wasm_func_type_get_result_count(WASMFuncType *const func_type) +{ + bh_assert(func_type); + + return func_type->result_count; +} + +wasm_valkind_t +wasm_func_type_get_result_valkind(WASMFuncType *const func_type, + uint32 result_index) +{ + if (!func_type || (result_index >= func_type->result_count)) { + bh_assert(0); + return (wasm_valkind_t)-1; + } + + switch (func_type->types[func_type->param_count + result_index]) { + case VALUE_TYPE_I32: + return WASM_I32; + case VALUE_TYPE_I64: + return WASM_I64; + case VALUE_TYPE_F32: + return WASM_F32; + case VALUE_TYPE_F64: + return WASM_F64; + case VALUE_TYPE_FUNCREF: + return WASM_FUNCREF; + +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + return WASM_V128; +#endif +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_VOID: + default: + { + bh_assert(0); + return (wasm_valkind_t)-1; + } + } +} + +wasm_valkind_t +wasm_global_type_get_valkind(WASMGlobalType *const global_type) +{ + bh_assert(global_type); + + return val_type_to_val_kind(global_type->val_type); +} + +bool +wasm_global_type_get_mutable(WASMGlobalType *const global_type) +{ + bh_assert(global_type); + + return global_type->is_mutable; +} + +bool +wasm_memory_type_get_shared(WASMMemoryType *const memory_type) +{ + bh_assert(memory_type); + + return (memory_type->flags & SHARED_MEMORY_FLAG) ? true : false; +} + +uint32 +wasm_memory_type_get_init_page_count(WASMMemoryType *const memory_type) +{ + bh_assert(memory_type); + + return memory_type->init_page_count; +} + +uint32 +wasm_memory_type_get_max_page_count(WASMMemoryType *const memory_type) +{ + bh_assert(memory_type); + + return memory_type->max_page_count; +} + +wasm_valkind_t +wasm_table_type_get_elem_kind(WASMTableType *const table_type) +{ + bh_assert(table_type); + + return val_type_to_val_kind(table_type->elem_type); +} + +bool +wasm_table_type_get_shared(WASMTableType *const table_type) +{ + bh_assert(table_type); + + return (table_type->flags & 2) ? true : false; +} + +uint32 +wasm_table_type_get_init_size(WASMTableType *const table_type) +{ + bh_assert(table_type); + + return table_type->init_size; +} + +uint32 +wasm_table_type_get_max_size(WASMTableType *const table_type) +{ + bh_assert(table_type); + + return table_type->max_size; +} + +bool +wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return wasm_native_register_natives(module_name, native_symbols, + n_native_symbols); +} + +bool +wasm_runtime_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return wasm_native_register_natives_raw(module_name, native_symbols, + n_native_symbols); +} + +bool +wasm_runtime_unregister_natives(const char *module_name, + NativeSymbol *native_symbols) +{ + return wasm_native_unregister_natives(module_name, native_symbols); +} + +bool +wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, + const char *signature, void *attachment, + uint32 *argv, uint32 argc, uint32 *argv_ret) +{ + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); +#if WASM_ENABLE_MEMORY64 != 0 + WASMMemoryInstance *memory = + wasm_get_default_memory((WASMModuleInstance *)module); + bool is_memory64 = memory ? memory->is_memory64 : false; +#endif + typedef void (*NativeRawFuncPtr)(WASMExecEnv *, uint64 *); + NativeRawFuncPtr invoke_native_raw = (NativeRawFuncPtr)func_ptr; + uint64 argv_buf[16] = { 0 }, *argv1 = argv_buf, *argv_dst, size; + uint32 *argv_src = argv, i, argc1, ptr_len; + uint32 arg_i32; + bool ret = false; + + argc1 = func_type->param_count; + if (argc1 > sizeof(argv_buf) / sizeof(uint64)) { + size = sizeof(uint64) * (uint64)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, NULL, + 0))) { + return false; + } + } + + argv_dst = argv1; + + /* Traverse secondly to fill in each argument */ + for (i = 0; i < func_type->param_count; i++, argv_dst++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + { + *(uint32 *)argv_dst = arg_i32 = *argv_src++; + if (signature +#if WASM_ENABLE_MEMORY64 != 0 + && !is_memory64 +#endif + ) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr( + module, (uint64)arg_i32, (uint64)ptr_len)) + goto fail; + + *(uintptr_t *)argv_dst = + (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr( + module, (uint64)arg_i32)) + goto fail; + + *(uintptr_t *)argv_dst = + (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + } + break; + } + case VALUE_TYPE_I64: +#if WASM_ENABLE_MEMORY64 != 0 + { + uint64 arg_i64; + + PUT_I64_TO_ADDR((uint32 *)argv_dst, + GET_I64_FROM_ADDR(argv_src)); + argv_src += 2; + arg_i64 = *argv_dst; + if (signature && is_memory64) { + /* TODO: memory64 pointer with length need a new symbol + * to represent type i64, with '~' still represent i32 + * length */ + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr(module, arg_i64, + (uint64)ptr_len)) + goto fail; + + *argv_dst = (uint64)wasm_runtime_addr_app_to_native( + module, arg_i64); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr(module, + arg_i64)) + goto fail; + + *argv_dst = (uint64)wasm_runtime_addr_app_to_native( + module, arg_i64); + } + } + break; + } +#endif + case VALUE_TYPE_F64: + bh_memcpy_s(argv_dst, sizeof(uint64), argv_src, + sizeof(uint32) * 2); + argv_src += 2; + break; + case VALUE_TYPE_F32: + *(float32 *)argv_dst = *(float32 *)argv_src++; + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + uint32 externref_idx = *argv_src++; + + void *externref_obj; + + if (!wasm_externref_ref2obj(externref_idx, &externref_obj)) + goto fail; + + bh_memcpy_s(argv_dst, sizeof(uintptr_t), argv_src, + sizeof(uintptr_t)); + break; + } +#endif +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + { + bh_memcpy_s(argv_dst, sizeof(uintptr_t), argv_src, + sizeof(uintptr_t)); + argv_src += sizeof(uintptr_t) / sizeof(uint32); + break; + } +#endif + default: + bh_assert(0); + break; + } + } + + exec_env->attachment = attachment; + invoke_native_raw(exec_env, argv1); + exec_env->attachment = NULL; + + if (func_type->result_count > 0) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + argv_ret[0] = *(uint32 *)argv1; + break; + case VALUE_TYPE_F32: + *(float32 *)argv_ret = *(float32 *)argv1; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_memcpy_s(argv_ret, sizeof(uint32) * 2, argv1, + sizeof(uint64)); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + uint32 externref_idx; + uint64 externref_obj; + + bh_memcpy_s(&externref_obj, sizeof(uint64), argv1, + sizeof(uint64)); + + if (!wasm_externref_obj2ref(exec_env->module_inst, + (void *)(uintptr_t)externref_obj, + &externref_idx)) + goto fail; + argv_ret[0] = externref_idx; + break; + } +#endif +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + { + bh_memcpy_s(argv_ret, sizeof(uintptr_t), argv1, + sizeof(uintptr_t)); + break; + } +#endif + default: + bh_assert(0); + break; + } + } + + ret = !wasm_runtime_copy_exception(module, NULL); + +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + return ret; +} + +/** + * Implementation of wasm_runtime_invoke_native() + */ + +/** + * The invoke native implementation on ARM platform with VFP co-processor, + * RISCV32 platform with/without FPU/DPFPU and ARC platform. + */ +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) \ + || defined(BUILD_TARGET_RISCV32_ILP32D) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) \ + || defined(BUILD_TARGET_RISCV32_ILP32) || defined(BUILD_TARGET_ARC) +typedef void (*GenericFunctionPointer)(void); +void +invokeNative(GenericFunctionPointer f, uint32 *args, uint32 n_stacks); + +typedef float64 (*Float64FuncPtr)(GenericFunctionPointer, uint32 *, uint32); +typedef float32 (*Float32FuncPtr)(GenericFunctionPointer, uint32 *, uint32); +typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint32 *, uint32); +typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint32 *, uint32); +typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint32 *, uint32); + +static volatile Float64FuncPtr invokeNative_Float64 = + (Float64FuncPtr)(uintptr_t)invokeNative; +static volatile Float32FuncPtr invokeNative_Float32 = + (Float32FuncPtr)(uintptr_t)invokeNative; +static volatile Int64FuncPtr invokeNative_Int64 = + (Int64FuncPtr)(uintptr_t)invokeNative; +static volatile Int32FuncPtr invokeNative_Int32 = + (Int32FuncPtr)(uintptr_t)invokeNative; +static volatile VoidFuncPtr invokeNative_Void = + (VoidFuncPtr)(uintptr_t)invokeNative; + +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) +#define MAX_REG_INTS 4 +#define MAX_REG_FLOATS 16 +#else +#define MAX_REG_INTS 8 +#define MAX_REG_FLOATS 8 +#endif + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, const char *signature, + void *attachment, uint32 *argv, uint32 argc, + uint32 *argv_ret) +{ + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); + /* argv buf layout: int args(fix cnt) + float args(fix cnt) + stack args + */ + uint32 argv_buf[32], *argv1 = argv_buf, *ints, *stacks, size; + uint32 *argv_src = argv, i, argc1, n_ints = 0, n_stacks = 0; + uint32 arg_i32, ptr_len; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + bool ret = false; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + bool is_aot_func = (NULL == signature); +#endif +#if !defined(BUILD_TARGET_RISCV32_ILP32) && !defined(BUILD_TARGET_ARC) + uint32 *fps; + int n_fps = 0; +#else +#define fps ints +#define n_fps n_ints +#endif + + n_ints++; /* exec env */ + + /* Traverse firstly to calculate stack args count */ + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + if (n_ints < MAX_REG_INTS) + n_ints++; + else + n_stacks++; + break; + case VALUE_TYPE_I64: + if (n_ints < MAX_REG_INTS - 1) { +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_ints & 1) + n_ints++; +#endif + n_ints += 2; + } +#if defined(BUILD_TARGET_RISCV32_ILP32) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) \ + || defined(BUILD_TARGET_RISCV32_ILP32D) || defined(BUILD_TARGET_ARC) + /* part in register, part in stack */ + else if (n_ints == MAX_REG_INTS - 1) { + n_ints++; + n_stacks++; + } +#endif + else { + /* 64-bit data in stack must be 8 bytes aligned + in arm and riscv32 */ +#if !defined(BUILD_TARGET_ARC) + if (n_stacks & 1) + n_stacks++; +#endif + n_stacks += 2; + } + break; +#if !defined(BUILD_TARGET_RISCV32_ILP32D) + case VALUE_TYPE_F32: + if (n_fps < MAX_REG_FLOATS) + n_fps++; +#if defined(BUILD_TARGET_RISCV32_ILP32F) + else if (n_ints < MAX_REG_INTS) { + n_ints++; + } +#endif + else + n_stacks++; + break; + case VALUE_TYPE_F64: +#if defined(BUILD_TARGET_RISCV32_ILP32) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) || defined(BUILD_TARGET_ARC) + if (n_ints < MAX_REG_INTS - 1) { + n_ints += 2; + } + else if (n_ints == MAX_REG_INTS - 1) { + n_ints++; + n_stacks++; + } +#endif +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) + if (n_fps < MAX_REG_FLOATS - 1) { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_fps & 1) + n_fps++; + n_fps += 2; + } + else if (n_fps == MAX_REG_FLOATS - 1) { + n_fps++; + n_stacks++; + } +#endif + else { + /* 64-bit data in stack must be 8 bytes aligned + in arm and riscv32 */ +#if !defined(BUILD_TARGET_ARC) + if (n_stacks & 1) + n_stacks++; +#endif + n_stacks += 2; + } + break; +#else /* BUILD_TARGET_RISCV32_ILP32D */ + case VALUE_TYPE_F32: + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS) { + n_fps++; + } + else if (func_type->types[i] == VALUE_TYPE_F32 + && n_ints < MAX_REG_INTS) { + /* use int reg firstly if available */ + n_ints++; + } + else if (func_type->types[i] == VALUE_TYPE_F64 + && n_ints < MAX_REG_INTS - 1) { + /* use int regs firstly if available */ + if (n_ints & 1) + n_ints++; + n_ints += 2; + } + else { + /* 64-bit data in stack must be 8 bytes aligned in riscv32 + */ + if (n_stacks & 1) + n_stacks++; + n_stacks += 2; + } + break; +#endif /* BUILD_TARGET_RISCV32_ILP32D */ + default: + bh_assert(0); + break; + } + } + + for (i = 0; i < ext_ret_count; i++) { + if (n_ints < MAX_REG_INTS) + n_ints++; + else + n_stacks++; + } + +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) + argc1 = MAX_REG_INTS + MAX_REG_FLOATS + n_stacks; +#elif defined(BUILD_TARGET_RISCV32_ILP32) || defined(BUILD_TARGET_ARC) + argc1 = MAX_REG_INTS + n_stacks; +#else /* for BUILD_TARGET_RISCV32_ILP32D */ + argc1 = MAX_REG_INTS + MAX_REG_FLOATS * 2 + n_stacks; +#endif + + if (argc1 > sizeof(argv_buf) / sizeof(uint32)) { + size = sizeof(uint32) * (uint32)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, NULL, + 0))) { + return false; + } + } + + ints = argv1; +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) + fps = ints + MAX_REG_INTS; + stacks = fps + MAX_REG_FLOATS; +#elif defined(BUILD_TARGET_RISCV32_ILP32) || defined(BUILD_TARGET_ARC) + stacks = ints + MAX_REG_INTS; +#else /* for BUILD_TARGET_RISCV32_ILP32D */ + fps = ints + MAX_REG_INTS; + stacks = fps + MAX_REG_FLOATS * 2; +#endif + + n_ints = 0; + n_fps = 0; + n_stacks = 0; + ints[n_ints++] = (uint32)(uintptr_t)exec_env; + + /* Traverse secondly to fill in each argument */ + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + { + arg_i32 = *argv_src++; + + if (signature) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr( + module, (uint64)arg_i32, (uint64)ptr_len)) + goto fail; + + arg_i32 = (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr( + module, (uint64)arg_i32)) + goto fail; + + arg_i32 = (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + } + + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = arg_i32; + else + stacks[n_stacks++] = arg_i32; + break; + } + case VALUE_TYPE_I64: + { + if (n_ints < MAX_REG_INTS - 1) { +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_ints & 1) + n_ints++; +#endif + ints[n_ints++] = *argv_src++; + ints[n_ints++] = *argv_src++; + } +#if defined(BUILD_TARGET_RISCV32_ILP32) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) \ + || defined(BUILD_TARGET_RISCV32_ILP32D) || defined(BUILD_TARGET_ARC) + else if (n_ints == MAX_REG_INTS - 1) { + ints[n_ints++] = *argv_src++; + stacks[n_stacks++] = *argv_src++; + } +#endif + else { + /* 64-bit data in stack must be 8 bytes aligned + in arm and riscv32 */ +#if !defined(BUILD_TARGET_ARC) + if (n_stacks & 1) + n_stacks++; +#endif + stacks[n_stacks++] = *argv_src++; + stacks[n_stacks++] = *argv_src++; + } + break; + } +#if !defined(BUILD_TARGET_RISCV32_ILP32D) + case VALUE_TYPE_F32: + { + if (n_fps < MAX_REG_FLOATS) + *(float32 *)&fps[n_fps++] = *(float32 *)argv_src++; +#if defined(BUILD_TARGET_RISCV32_ILP32F) + else if (n_ints < MAX_REG_INTS) { + ints[n_ints++] = *argv_src++; + } +#endif + else + *(float32 *)&stacks[n_stacks++] = *(float32 *)argv_src++; + break; + } + case VALUE_TYPE_F64: + { +#if defined(BUILD_TARGET_RISCV32_ILP32) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) || defined(BUILD_TARGET_ARC) + if (n_ints < MAX_REG_INTS - 1) { + ints[n_ints++] = *argv_src++; + ints[n_ints++] = *argv_src++; + } + else if (n_ints == MAX_REG_INTS - 1) { + ints[n_ints++] = *argv_src++; + stacks[n_stacks++] = *argv_src++; + } +#endif +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) + if (n_fps < MAX_REG_FLOATS - 1) { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_fps & 1) + n_fps++; + fps[n_fps++] = *argv_src++; + fps[n_fps++] = *argv_src++; + } + else if (n_fps == MAX_REG_FLOATS - 1) { + fps[n_fps++] = *argv_src++; + stacks[n_stacks++] = *argv_src++; + } +#endif + else { + /* 64-bit data in stack must be 8 bytes aligned + in arm and riscv32 */ +#if !defined(BUILD_TARGET_ARC) + if (n_stacks & 1) + n_stacks++; +#endif + stacks[n_stacks++] = *argv_src++; + stacks[n_stacks++] = *argv_src++; + } + break; + } +#else /* BUILD_TARGET_RISCV32_ILP32D */ + case VALUE_TYPE_F32: + case VALUE_TYPE_F64: + { + if (n_fps < MAX_REG_FLOATS) { + if (func_type->types[i] == VALUE_TYPE_F32) { + *(float32 *)&fps[n_fps * 2] = *(float32 *)argv_src++; + /* NaN boxing, the upper bits of a valid NaN-boxed + value must be all 1s. */ + fps[n_fps * 2 + 1] = 0xFFFFFFFF; + } + else { + *(float64 *)&fps[n_fps * 2] = *(float64 *)argv_src; + argv_src += 2; + } + n_fps++; + } + else if (func_type->types[i] == VALUE_TYPE_F32 + && n_ints < MAX_REG_INTS) { + /* use int reg firstly if available */ + *(float32 *)&ints[n_ints++] = *(float32 *)argv_src++; + } + else if (func_type->types[i] == VALUE_TYPE_F64 + && n_ints < MAX_REG_INTS - 1) { + /* use int regs firstly if available */ + if (n_ints & 1) + n_ints++; + *(float64 *)&ints[n_ints] = *(float64 *)argv_src; + n_ints += 2; + argv_src += 2; + } + else { + /* 64-bit data in stack must be 8 bytes aligned in riscv32 + */ + if (n_stacks & 1) + n_stacks++; + if (func_type->types[i] == VALUE_TYPE_F32) { + *(float32 *)&stacks[n_stacks++] = + *(float32 *)argv_src++; + } + else { + *(float64 *)&stacks[n_stacks] = *(float64 *)argv_src; + argv_src += 2; + n_stacks += 2; + } + } + break; + } +#endif /* BUILD_TARGET_RISCV32_ILP32D */ +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + uint32 externref_idx = *argv_src++; + + if (is_aot_func) { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = externref_idx; + else + stacks[n_stacks++] = externref_idx; + } + else { + void *externref_obj; + + if (!wasm_externref_ref2obj(externref_idx, &externref_obj)) + goto fail; + + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = (uintptr_t)externref_obj; + else + stacks[n_stacks++] = (uintptr_t)externref_obj; + } + break; + } +#endif + default: + bh_assert(0); + break; + } + } + + /* Save extra result values' address to argv1 */ + for (i = 0; i < ext_ret_count; i++) { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *(uint32 *)argv_src++; + else + stacks[n_stacks++] = *(uint32 *)argv_src++; + } + + exec_env->attachment = attachment; + if (func_type->result_count == 0) { + invokeNative_Void(func_ptr, argv1, n_stacks); + } + else { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + argv_ret[0] = + (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_I64: + PUT_I64_TO_ADDR(argv_ret, + invokeNative_Int64(func_ptr, argv1, n_stacks)); + break; + case VALUE_TYPE_F32: + *(float32 *)argv_ret = + invokeNative_Float32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_F64: + PUT_F64_TO_ADDR( + argv_ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + if (is_aot_func) { + uint32 externref_idx = + (uint32)invokeNative_Int32(func_ptr, argv1, argc1); + argv_ret[0] = externref_idx; + } + else { + uint32 externref_idx; + void *externref_obj; + + externref_obj = (void *)(uintptr_t)invokeNative_Int32( + func_ptr, argv1, argc1); + + if (!wasm_externref_obj2ref(exec_env->module_inst, + externref_obj, &externref_idx)) + goto fail; + + argv_ret[0] = externref_idx; + } + break; + } +#endif + default: + bh_assert(0); + break; + } + } + exec_env->attachment = NULL; + + ret = !wasm_runtime_copy_exception(module, NULL); + +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + return ret; +} +#endif /* end of defined(BUILD_TARGET_ARM_VFP) \ + || defined(BUILD_TARGET_THUMB_VFP) \ + || defined(BUILD_TARGET_RISCV32_ILP32D) \ + || defined(BUILD_TARGET_RISCV32_ILP32F) \ + || defined(BUILD_TARGET_RISCV32_ILP32) \ + || defined(BUILD_TARGET_ARC) */ + +#if defined(BUILD_TARGET_X86_32) || defined(BUILD_TARGET_ARM) \ + || defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_MIPS) \ + || defined(BUILD_TARGET_XTENSA) +typedef void (*GenericFunctionPointer)(void); +void +invokeNative(GenericFunctionPointer f, uint32 *args, uint32 sz); + +typedef float64 (*Float64FuncPtr)(GenericFunctionPointer f, uint32 *, uint32); +typedef float32 (*Float32FuncPtr)(GenericFunctionPointer f, uint32 *, uint32); +typedef int64 (*Int64FuncPtr)(GenericFunctionPointer f, uint32 *, uint32); +typedef int32 (*Int32FuncPtr)(GenericFunctionPointer f, uint32 *, uint32); +typedef void (*VoidFuncPtr)(GenericFunctionPointer f, uint32 *, uint32); + +static volatile Int64FuncPtr invokeNative_Int64 = + (Int64FuncPtr)(uintptr_t)invokeNative; +static volatile Int32FuncPtr invokeNative_Int32 = + (Int32FuncPtr)(uintptr_t)invokeNative; +static volatile Float64FuncPtr invokeNative_Float64 = + (Float64FuncPtr)(uintptr_t)invokeNative; +static volatile Float32FuncPtr invokeNative_Float32 = + (Float32FuncPtr)(uintptr_t)invokeNative; +static volatile VoidFuncPtr invokeNative_Void = + (VoidFuncPtr)(uintptr_t)invokeNative; + +static inline void +word_copy(uint32 *dest, uint32 *src, unsigned num) +{ + for (; num > 0; num--) + *dest++ = *src++; +} + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, const char *signature, + void *attachment, uint32 *argv, uint32 argc, + uint32 *argv_ret) +{ + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); + uint32 argv_buf[32], *argv1 = argv_buf, argc1, i, j = 0; + uint32 arg_i32, ptr_len; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + uint64 size; + bool ret = false; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + bool is_aot_func = (NULL == signature); +#endif + +#if defined(BUILD_TARGET_X86_32) + argc1 = argc + ext_ret_count + 2; +#else + /* arm/thumb/mips/xtensa, 64-bit data must be 8 bytes aligned, + so we need to allocate more memory. */ + argc1 = func_type->param_count * 2 + ext_ret_count + 2; +#endif + + if (argc1 > sizeof(argv_buf) / sizeof(uint32)) { + size = sizeof(uint32) * (uint64)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, NULL, + 0))) { + return false; + } + } + + for (i = 0; i < sizeof(WASMExecEnv *) / sizeof(uint32); i++) + argv1[j++] = ((uint32 *)&exec_env)[i]; + + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + { + arg_i32 = *argv++; + + if (signature) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr( + module, (uint64)arg_i32, (uint64)ptr_len)) + goto fail; + + arg_i32 = (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr( + module, (uint64)arg_i32)) + goto fail; + + arg_i32 = (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + } + + argv1[j++] = arg_i32; + break; + } + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: +#if !defined(BUILD_TARGET_X86_32) + /* 64-bit data must be 8 bytes aligned in arm, thumb, mips + and xtensa */ + if (j & 1) + j++; +#endif + argv1[j++] = *argv++; + argv1[j++] = *argv++; + break; + case VALUE_TYPE_F32: + argv1[j++] = *argv++; + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + uint32 externref_idx = *argv++; + if (is_aot_func) { + argv1[j++] = externref_idx; + } + else { + void *externref_obj; + + if (!wasm_externref_ref2obj(externref_idx, &externref_obj)) + goto fail; + + argv1[j++] = (uintptr_t)externref_obj; + } + break; + } +#endif + default: + bh_assert(0); + break; + } + } + + /* Save extra result values' address to argv1 */ + word_copy(argv1 + j, argv, ext_ret_count); + + argc1 = j + ext_ret_count; + exec_env->attachment = attachment; + if (func_type->result_count == 0) { + invokeNative_Void(func_ptr, argv1, argc1); + } + else { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + argv_ret[0] = + (uint32)invokeNative_Int32(func_ptr, argv1, argc1); + break; + case VALUE_TYPE_I64: + PUT_I64_TO_ADDR(argv_ret, + invokeNative_Int64(func_ptr, argv1, argc1)); + break; + case VALUE_TYPE_F32: + *(float32 *)argv_ret = + invokeNative_Float32(func_ptr, argv1, argc1); + break; + case VALUE_TYPE_F64: + PUT_F64_TO_ADDR(argv_ret, + invokeNative_Float64(func_ptr, argv1, argc1)); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + if (is_aot_func) { + uint32 externref_idx = + (uint32)invokeNative_Int32(func_ptr, argv1, argc1); + argv_ret[0] = externref_idx; + } + else { + void *externref_obj = (void *)(uintptr_t)invokeNative_Int32( + func_ptr, argv1, argc1); + uint32 externref_idx; + if (!wasm_externref_obj2ref(exec_env->module_inst, + externref_obj, &externref_idx)) + goto fail; + argv_ret[0] = externref_idx; + } + break; + } +#endif + default: + bh_assert(0); + break; + } + } + exec_env->attachment = NULL; + + ret = !wasm_runtime_copy_exception(module, NULL); + +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + return ret; +} + +#endif /* end of defined(BUILD_TARGET_X86_32) \ + || defined(BUILD_TARGET_ARM) \ + || defined(BUILD_TARGET_THUMB) \ + || defined(BUILD_TARGET_MIPS) \ + || defined(BUILD_TARGET_XTENSA) */ + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) + +#if WASM_ENABLE_SIMD != 0 +#ifdef v128 +#undef v128 +#endif + +#if defined(_WIN32) || defined(_WIN32_) +typedef union __declspec(intrin_type) __declspec(align(8)) v128 { + __int8 m128i_i8[16]; + __int16 m128i_i16[8]; + __int32 m128i_i32[4]; + __int64 m128i_i64[2]; + unsigned __int8 m128i_u8[16]; + unsigned __int16 m128i_u16[8]; + unsigned __int32 m128i_u32[4]; + unsigned __int64 m128i_u64[2]; +} v128; +#elif defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) +typedef long long v128 + __attribute__((__vector_size__(16), __may_alias__, __aligned__(1))); +#elif defined(BUILD_TARGET_AARCH64) +#include +typedef uint32x4_t __m128i; +#define v128 __m128i +#endif + +#endif /* end of WASM_ENABLE_SIMD != 0 */ + +typedef void (*GenericFunctionPointer)(void); +void +invokeNative(GenericFunctionPointer f, uint64 *args, uint64 n_stacks); + +typedef float64 (*Float64FuncPtr)(GenericFunctionPointer, uint64 *, uint64); +typedef float32 (*Float32FuncPtr)(GenericFunctionPointer, uint64 *, uint64); +typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint64 *, uint64); +typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint64 *, uint64); +typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint64 *, uint64); + +/* NOLINTBEGIN */ +static volatile Float64FuncPtr invokeNative_Float64 = + (Float64FuncPtr)(uintptr_t)invokeNative; +static volatile Float32FuncPtr invokeNative_Float32 = + (Float32FuncPtr)(uintptr_t)invokeNative; +static volatile Int64FuncPtr invokeNative_Int64 = + (Int64FuncPtr)(uintptr_t)invokeNative; +static volatile Int32FuncPtr invokeNative_Int32 = + (Int32FuncPtr)(uintptr_t)invokeNative; +static volatile VoidFuncPtr invokeNative_Void = + (VoidFuncPtr)(uintptr_t)invokeNative; + +#if WASM_ENABLE_SIMD != 0 +typedef v128 (*V128FuncPtr)(GenericFunctionPointer, uint64 *, uint64); +static V128FuncPtr invokeNative_V128 = (V128FuncPtr)(uintptr_t)invokeNative; +#endif +/* NOLINTEND */ + +#if defined(_WIN32) || defined(_WIN32_) +#define MAX_REG_FLOATS 4 +#define MAX_REG_INTS 4 +#else /* else of defined(_WIN32) || defined(_WIN32_) */ +#define MAX_REG_FLOATS 8 +#if defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) +#define MAX_REG_INTS 8 +#else +#define MAX_REG_INTS 6 +#endif /* end of defined(BUILD_TARGET_AARCH64) \ + || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) */ +#endif /* end of defined(_WIN32) || defined(_WIN32_) */ + +/* + * ASAN is not designed to work with custom stack unwind or other low-level + * things. Ignore a function that does some low-level magic. (e.g. walking + * through the thread's stack bypassing the frame boundaries) + */ +#if defined(__GNUC__) || defined(__clang__) +__attribute__((no_sanitize_address)) +#endif +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, const char *signature, + void *attachment, uint32 *argv, uint32 argc, + uint32 *argv_ret) +{ + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); +#if WASM_ENABLE_MEMORY64 != 0 + WASMMemoryInstance *memory = + wasm_get_default_memory((WASMModuleInstance *)module); + bool is_memory64 = memory ? memory->is_memory64 : false; +#endif + uint64 argv_buf[32] = { 0 }, *argv1 = argv_buf, *ints, *stacks, size, + arg_i64; + uint32 *argv_src = argv, i, argc1, n_ints = 0, n_stacks = 0; + uint32 arg_i32, ptr_len; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + bool ret = false; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + bool is_aot_func = (NULL == signature); +#endif +#ifndef BUILD_TARGET_RISCV64_LP64 +#if WASM_ENABLE_SIMD == 0 + uint64 *fps; +#else + v128 *fps; +#endif +#else /* else of BUILD_TARGET_RISCV64_LP64 */ +#define fps ints +#endif /* end of BUILD_TARGET_RISCV64_LP64 */ + +#if defined(_WIN32) || defined(_WIN32_) || defined(BUILD_TARGET_RISCV64_LP64) + /* important difference in calling conventions */ +#define n_fps n_ints +#else + int n_fps = 0; +#endif + +#if WASM_ENABLE_SIMD == 0 + argc1 = 1 + MAX_REG_FLOATS + (uint32)func_type->param_count + ext_ret_count; +#else + argc1 = 1 + MAX_REG_FLOATS * 2 + (uint32)func_type->param_count * 2 + + ext_ret_count; +#endif + if (argc1 > sizeof(argv_buf) / sizeof(uint64)) { + size = sizeof(uint64) * (uint64)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, NULL, + 0))) { + return false; + } + } + +#ifndef BUILD_TARGET_RISCV64_LP64 +#if WASM_ENABLE_SIMD == 0 + fps = argv1; + ints = fps + MAX_REG_FLOATS; +#else + fps = (v128 *)argv1; + ints = (uint64 *)(fps + MAX_REG_FLOATS); +#endif +#else /* else of BUILD_TARGET_RISCV64_LP64 */ + ints = argv1; +#endif /* end of BUILD_TARGET_RISCV64_LP64 */ + stacks = ints + MAX_REG_INTS; + + ints[n_ints++] = (uint64)(uintptr_t)exec_env; + + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + { + arg_i32 = *argv_src++; + arg_i64 = arg_i32; + if (signature +#if WASM_ENABLE_MEMORY64 != 0 + && !is_memory64 +#endif + ) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr( + module, (uint64)arg_i32, (uint64)ptr_len)) + goto fail; + + arg_i64 = (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr( + module, (uint64)arg_i32)) + goto fail; + + arg_i64 = (uintptr_t)wasm_runtime_addr_app_to_native( + module, (uint64)arg_i32); + } + } + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = arg_i64; + else + stacks[n_stacks++] = arg_i64; + break; + } + case VALUE_TYPE_I64: +#if WASM_ENABLE_MEMORY64 != 0 + { + arg_i64 = GET_I64_FROM_ADDR(argv_src); + argv_src += 2; + if (signature && is_memory64) { + /* TODO: memory64 pointer with length need a new symbol + * to represent type i64, with '~' still represent i32 + * length */ + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr(module, arg_i64, + (uint64)ptr_len)) + goto fail; + + arg_i64 = (uint64)wasm_runtime_addr_app_to_native( + module, arg_i64); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr(module, + arg_i64)) + goto fail; + + arg_i64 = (uint64)wasm_runtime_addr_app_to_native( + module, arg_i64); + } + } + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = arg_i64; + else + stacks[n_stacks++] = arg_i64; + break; + } +#endif +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *(uint64 *)argv_src; + else + stacks[n_stacks++] = *(uint64 *)argv_src; + argv_src += 2; + break; + case VALUE_TYPE_F32: + if (n_fps < MAX_REG_FLOATS) { + *(float32 *)&fps[n_fps++] = *(float32 *)argv_src++; + } + else { + *(float32 *)&stacks[n_stacks++] = *(float32 *)argv_src++; + } + break; + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS) { + *(float64 *)&fps[n_fps++] = *(float64 *)argv_src; + } + else { + *(float64 *)&stacks[n_stacks++] = *(float64 *)argv_src; + } + argv_src += 2; + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + uint32 externref_idx = *argv_src++; + if (is_aot_func) { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = externref_idx; + else + stacks[n_stacks++] = externref_idx; + } + else { + void *externref_obj; + + if (!wasm_externref_ref2obj(externref_idx, &externref_obj)) + goto fail; + + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = (uintptr_t)externref_obj; + else + stacks[n_stacks++] = (uintptr_t)externref_obj; + } + break; + } +#endif +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + if (n_fps < MAX_REG_FLOATS) { + *(v128 *)&fps[n_fps++] = *(v128 *)argv_src; + } + else { + *(v128 *)&stacks[n_stacks++] = *(v128 *)argv_src; + n_stacks++; + } + argv_src += 4; + break; +#endif + default: + bh_assert(0); + break; + } + } + + /* Save extra result values' address to argv1 */ + for (i = 0; i < ext_ret_count; i++) { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *(uint64 *)argv_src; + else + stacks[n_stacks++] = *(uint64 *)argv_src; + argv_src += 2; + } + + exec_env->attachment = attachment; + if (result_count == 0) { + invokeNative_Void(func_ptr, argv1, n_stacks); + } + else { + /* Invoke the native function and get the first result value */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: +#endif + argv_ret[0] = + (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_I64: +#if WASM_ENABLE_GC != 0 + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case REF_TYPE_NULLREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif +#endif + PUT_I64_TO_ADDR(argv_ret, + invokeNative_Int64(func_ptr, argv1, n_stacks)); + break; + case VALUE_TYPE_F32: + *(float32 *)argv_ret = + invokeNative_Float32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_F64: + PUT_F64_TO_ADDR( + argv_ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + { + if (is_aot_func) { + argv_ret[0] = invokeNative_Int32(func_ptr, argv1, n_stacks); + } + else { + uint32 externref_idx; + void *externref_obj = (void *)(uintptr_t)invokeNative_Int64( + func_ptr, argv1, n_stacks); + + if (!wasm_externref_obj2ref(exec_env->module_inst, + externref_obj, &externref_idx)) + goto fail; + + argv_ret[0] = externref_idx; + } + break; + } +#endif +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + *(v128 *)argv_ret = + invokeNative_V128(func_ptr, argv1, n_stacks); + break; +#endif + default: + bh_assert(0); + break; + } + } + exec_env->attachment = NULL; + + ret = !wasm_runtime_copy_exception(module, NULL); +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + + return ret; +} + +#endif /* end of defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) \ + || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) */ + +bool +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; + } + + /* this function is called from native code, so exec_env->handle and + exec_env->native_stack_boundary must have been set, we don't set + it again */ + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) + 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) + ret = aot_call_indirect(exec_env, 0, element_index, argc, argv); +#endif + + return ret; +} + +static void +exchange_uint32(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static void +exchange_uint64(uint8 *p_data) +{ + uint32 value; + + value = *(uint32 *)p_data; + *(uint32 *)p_data = *(uint32 *)(p_data + 4); + *(uint32 *)(p_data + 4) = value; + exchange_uint32(p_data); + exchange_uint32(p_data + 4); +} + +void +wasm_runtime_read_v128(const uint8 *bytes, uint64 *ret1, uint64 *ret2) +{ + uint64 u1, u2; + + bh_memcpy_s(&u1, 8, bytes, 8); + bh_memcpy_s(&u2, 8, bytes + 8, 8); + + if (!is_little_endian()) { + exchange_uint64((uint8 *)&u1); + exchange_uint64((uint8 *)&u2); + *ret1 = u2; + *ret2 = u1; + } + else { + *ret1 = u1; + *ret2 = u2; + } +} + +#if WASM_ENABLE_THREAD_MGR != 0 +typedef struct WASMThreadArg { + WASMExecEnv *new_exec_env; + wasm_thread_callback_t callback; + void *arg; +} WASMThreadArg; + +WASMExecEnv * +wasm_runtime_spawn_exec_env(WASMExecEnv *exec_env) +{ + return wasm_cluster_spawn_exec_env(exec_env); +} + +void +wasm_runtime_destroy_spawned_exec_env(WASMExecEnv *exec_env) +{ + wasm_cluster_destroy_spawned_exec_env(exec_env); +} + +static void * +wasm_runtime_thread_routine(void *arg) +{ + WASMThreadArg *thread_arg = (WASMThreadArg *)arg; + void *ret; + + bh_assert(thread_arg->new_exec_env); + ret = thread_arg->callback(thread_arg->new_exec_env, thread_arg->arg); + + wasm_runtime_destroy_spawned_exec_env(thread_arg->new_exec_env); + wasm_runtime_free(thread_arg); + + os_thread_exit(ret); + return ret; +} + +int32 +wasm_runtime_spawn_thread(WASMExecEnv *exec_env, wasm_thread_t *tid, + wasm_thread_callback_t callback, void *arg) +{ + WASMExecEnv *new_exec_env = wasm_runtime_spawn_exec_env(exec_env); + WASMThreadArg *thread_arg; + int32 ret; + + if (!new_exec_env) + return -1; + + if (!(thread_arg = wasm_runtime_malloc(sizeof(WASMThreadArg)))) { + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + return -1; + } + + thread_arg->new_exec_env = new_exec_env; + thread_arg->callback = callback; + thread_arg->arg = arg; + + ret = os_thread_create((korp_tid *)tid, wasm_runtime_thread_routine, + thread_arg, APP_THREAD_STACK_SIZE_DEFAULT); + + if (ret != 0) { + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + wasm_runtime_free(thread_arg); + } + + return ret; +} + +int32 +wasm_runtime_join_thread(wasm_thread_t tid, void **retval) +{ + return os_thread_join((korp_tid)tid, retval); +} + +#endif /* end of WASM_ENABLE_THREAD_MGR */ + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + +static korp_mutex externref_lock; +static uint32 externref_global_id = 1; +static HashMap *externref_map; + +typedef struct ExternRefMapNode { + /* The extern object from runtime embedder */ + void *extern_obj; + /* The module instance it belongs to */ + WASMModuleInstanceCommon *module_inst; + /* Whether it is retained */ + bool retained; + /* Whether it is marked by runtime */ + bool marked; + /* cleanup function called when the externref is freed */ + void (*cleanup)(void *); +} ExternRefMapNode; + +static uint32 +wasm_externref_hash(const void *key) +{ + uint32 externref_idx = (uint32)(uintptr_t)key; + return externref_idx; +} + +static bool +wasm_externref_equal(void *key1, void *key2) +{ + uint32 externref_idx1 = (uint32)(uintptr_t)key1; + uint32 externref_idx2 = (uint32)(uintptr_t)key2; + return externref_idx1 == externref_idx2 ? true : false; +} + +static bool +wasm_externref_map_init() +{ + if (os_mutex_init(&externref_lock) != 0) + return false; + + if (!(externref_map = bh_hash_map_create(32, false, wasm_externref_hash, + wasm_externref_equal, NULL, + wasm_runtime_free))) { + os_mutex_destroy(&externref_lock); + return false; + } + + externref_global_id = 1; + return true; +} + +static void +wasm_externref_map_destroy() +{ + bh_hash_map_destroy(externref_map); + os_mutex_destroy(&externref_lock); +} + +typedef struct LookupExtObj_UserData { + ExternRefMapNode node; + bool found; + uint32 externref_idx; +} LookupExtObj_UserData; + +static void +lookup_extobj_callback(void *key, void *value, void *user_data) +{ + uint32 externref_idx = (uint32)(uintptr_t)key; + ExternRefMapNode *node = (ExternRefMapNode *)value; + LookupExtObj_UserData *user_data_lookup = + (LookupExtObj_UserData *)user_data; + + if (node->extern_obj == user_data_lookup->node.extern_obj + && node->module_inst == user_data_lookup->node.module_inst) { + user_data_lookup->found = true; + user_data_lookup->externref_idx = externref_idx; + } +} + +static void +delete_externref(void *key, ExternRefMapNode *node) +{ + bh_hash_map_remove(externref_map, key, NULL, NULL); + if (node->cleanup) { + (*node->cleanup)(node->extern_obj); + } + wasm_runtime_free(node); +} + +static void +delete_extobj_callback(void *key, void *value, void *user_data) +{ + ExternRefMapNode *node = (ExternRefMapNode *)value; + LookupExtObj_UserData *lookup_user_data = + (LookupExtObj_UserData *)user_data; + + if (node->extern_obj == lookup_user_data->node.extern_obj + && node->module_inst == lookup_user_data->node.module_inst) { + lookup_user_data->found = true; + delete_externref(key, node); + } +} + +bool +wasm_externref_objdel(WASMModuleInstanceCommon *module_inst, void *extern_obj) +{ + LookupExtObj_UserData lookup_user_data = { 0 }; + bool ok = false; + + /* in a wrapper, extern_obj could be any value */ + lookup_user_data.node.extern_obj = extern_obj; + lookup_user_data.node.module_inst = module_inst; + lookup_user_data.found = false; + + os_mutex_lock(&externref_lock); + /* Lookup hashmap firstly */ + bh_hash_map_traverse(externref_map, delete_extobj_callback, + (void *)&lookup_user_data); + if (lookup_user_data.found) { + ok = true; + } + os_mutex_unlock(&externref_lock); + + return ok; +} + +bool +wasm_externref_set_cleanup(WASMModuleInstanceCommon *module_inst, + void *extern_obj, void (*extern_obj_cleanup)(void *)) +{ + + LookupExtObj_UserData lookup_user_data = { 0 }; + bool ok = false; + + /* in a wrapper, extern_obj could be any value */ + lookup_user_data.node.extern_obj = extern_obj; + lookup_user_data.node.module_inst = module_inst; + lookup_user_data.found = false; + + os_mutex_lock(&externref_lock); + /* Lookup hashmap firstly */ + bh_hash_map_traverse(externref_map, lookup_extobj_callback, + (void *)&lookup_user_data); + if (lookup_user_data.found) { + void *key = (void *)(uintptr_t)lookup_user_data.externref_idx; + ExternRefMapNode *node = bh_hash_map_find(externref_map, key); + bh_assert(node); + node->cleanup = extern_obj_cleanup; + ok = true; + } + os_mutex_unlock(&externref_lock); + + return ok; +} + +bool +wasm_externref_obj2ref(WASMModuleInstanceCommon *module_inst, void *extern_obj, + uint32 *p_externref_idx) +{ + LookupExtObj_UserData lookup_user_data = { 0 }; + ExternRefMapNode *node; + uint32 externref_idx; + + /* + * to catch a parameter from `wasm_application_execute_func`, + * which represents a string 'null' + */ +#if UINTPTR_MAX == UINT32_MAX + if ((uint32)-1 == (uintptr_t)extern_obj) { +#else + if ((uint64)-1LL == (uintptr_t)extern_obj) { +#endif + *p_externref_idx = NULL_REF; + return true; + } + + /* in a wrapper, extern_obj could be any value */ + lookup_user_data.node.extern_obj = extern_obj; + lookup_user_data.node.module_inst = module_inst; + lookup_user_data.found = false; + + os_mutex_lock(&externref_lock); + + /* Lookup hashmap firstly */ + bh_hash_map_traverse(externref_map, lookup_extobj_callback, + (void *)&lookup_user_data); + if (lookup_user_data.found) { + *p_externref_idx = lookup_user_data.externref_idx; + os_mutex_unlock(&externref_lock); + return true; + } + + /* Not found in hashmap */ + if (externref_global_id == NULL_REF || externref_global_id == 0) { + goto fail1; + } + + if (!(node = wasm_runtime_malloc(sizeof(ExternRefMapNode)))) { + goto fail1; + } + + memset(node, 0, sizeof(ExternRefMapNode)); + node->extern_obj = extern_obj; + node->module_inst = module_inst; + node->cleanup = NULL; + + externref_idx = externref_global_id; + + if (!bh_hash_map_insert(externref_map, (void *)(uintptr_t)externref_idx, + (void *)node)) { + goto fail2; + } + + externref_global_id++; + *p_externref_idx = externref_idx; + os_mutex_unlock(&externref_lock); + return true; +fail2: + wasm_runtime_free(node); +fail1: + os_mutex_unlock(&externref_lock); + return false; +} + +bool +wasm_externref_ref2obj(uint32 externref_idx, void **p_extern_obj) +{ + ExternRefMapNode *node; + + /* catch a `ref.null` variable */ + if (externref_idx == NULL_REF) { + *p_extern_obj = NULL; + return true; + } + + os_mutex_lock(&externref_lock); + node = bh_hash_map_find(externref_map, (void *)(uintptr_t)externref_idx); + os_mutex_unlock(&externref_lock); + + if (!node) + return false; + + *p_extern_obj = node->extern_obj; + return true; +} + +static void +reclaim_extobj_callback(void *key, void *value, void *user_data) +{ + ExternRefMapNode *node = (ExternRefMapNode *)value; + WASMModuleInstanceCommon *module_inst = + (WASMModuleInstanceCommon *)user_data; + + if (node->module_inst == module_inst) { + if (!node->marked && !node->retained) { + delete_externref(key, node); + } + else { + node->marked = false; + } + } +} + +static void +mark_externref(uint32 externref_idx) +{ + ExternRefMapNode *node; + + if (externref_idx != NULL_REF) { + node = + bh_hash_map_find(externref_map, (void *)(uintptr_t)externref_idx); + if (node) { + node->marked = true; + } + } +} + +#if WASM_ENABLE_INTERP != 0 +static void +interp_mark_all_externrefs(WASMModuleInstance *module_inst) +{ + uint32 i, j, externref_idx; + table_elem_type_t *table_data; + uint8 *global_data = module_inst->global_data; + WASMGlobalInstance *global; + WASMTableInstance *table; + + global = module_inst->e->globals; + for (i = 0; i < module_inst->e->global_count; i++, global++) { + if (global->type == VALUE_TYPE_EXTERNREF) { + externref_idx = *(uint32 *)(global_data + global->data_offset); + mark_externref(externref_idx); + } + } + + for (i = 0; i < module_inst->table_count; i++) { + uint8 elem_type = 0; + uint32 init_size, max_size; + + table = wasm_get_table_inst(module_inst, i); + (void)wasm_runtime_get_table_inst_elem_type( + (WASMModuleInstanceCommon *)module_inst, i, &elem_type, &init_size, + &max_size); + + if (elem_type == VALUE_TYPE_EXTERNREF) { + table_data = table->elems; + for (j = 0; j < table->cur_size; j++) { + externref_idx = table_data[j]; + mark_externref(externref_idx); + } + } + (void)init_size; + (void)max_size; + } +} +#endif + +#if WASM_ENABLE_AOT != 0 +static void +aot_mark_all_externrefs(AOTModuleInstance *module_inst) +{ + uint32 i = 0, j = 0; + const AOTModule *module = (AOTModule *)module_inst->module; + const AOTTable *table = module->tables; + const AOTGlobal *global = module->globals; + const AOTTableInstance *table_inst; + + for (i = 0; i < module->global_count; i++, global++) { + if (global->type.val_type == VALUE_TYPE_EXTERNREF) { + mark_externref( + *(uint32 *)(module_inst->global_data + global->data_offset)); + } + } + + for (i = 0; i < module->table_count; i++) { + table_inst = module_inst->tables[i]; + if ((table + i)->table_type.elem_type == VALUE_TYPE_EXTERNREF) { + while (j < table_inst->cur_size) { + mark_externref(table_inst->elems[j++]); + } + } + } +} +#endif + +void +wasm_externref_reclaim(WASMModuleInstanceCommon *module_inst) +{ + os_mutex_lock(&externref_lock); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + interp_mark_all_externrefs((WASMModuleInstance *)module_inst); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + aot_mark_all_externrefs((AOTModuleInstance *)module_inst); +#endif + + bh_hash_map_traverse(externref_map, reclaim_extobj_callback, + (void *)module_inst); + os_mutex_unlock(&externref_lock); +} + +static void +cleanup_extobj_callback(void *key, void *value, void *user_data) +{ + ExternRefMapNode *node = (ExternRefMapNode *)value; + WASMModuleInstanceCommon *module_inst = + (WASMModuleInstanceCommon *)user_data; + + if (node->module_inst == module_inst) { + delete_externref(key, node); + } +} + +void +wasm_externref_cleanup(WASMModuleInstanceCommon *module_inst) +{ + os_mutex_lock(&externref_lock); + bh_hash_map_traverse(externref_map, cleanup_extobj_callback, + (void *)module_inst); + os_mutex_unlock(&externref_lock); +} + +bool +wasm_externref_retain(uint32 externref_idx) +{ + ExternRefMapNode *node; + + os_mutex_lock(&externref_lock); + + if (externref_idx != NULL_REF) { + node = + bh_hash_map_find(externref_map, (void *)(uintptr_t)externref_idx); + if (node) { + node->retained = true; + os_mutex_unlock(&externref_lock); + return true; + } + } + + os_mutex_unlock(&externref_lock); + return false; +} +#endif /* end of WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 */ + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +uint32 +wasm_runtime_dump_line_buf_impl(const char *line_buf, bool dump_or_print, + char **buf, uint32 *len) +{ + if (dump_or_print) { + return (uint32)os_printf("%s", line_buf); + } + else if (*buf) { + uint32 dump_len; + + dump_len = snprintf(*buf, *len, "%s", line_buf); + if (dump_len >= *len) { + dump_len = *len; + } + + *len = *len - dump_len; + *buf = *buf + dump_len; + return dump_len; + } + else { + return (uint32)strlen(line_buf); + } +} + +void +wasm_runtime_dump_call_stack(WASMExecEnv *exec_env) +{ + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_interp_dump_call_stack(exec_env, true, NULL, 0); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_dump_call_stack(exec_env, true, NULL, 0); + } +#endif +} + +uint32 +wasm_runtime_get_call_stack_buf_size(wasm_exec_env_t exec_env) +{ + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_interp_dump_call_stack(exec_env, false, NULL, 0); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_dump_call_stack(exec_env, false, NULL, 0); + } +#endif + + return 0; +} + +uint32 +wasm_runtime_dump_call_stack_to_buf(wasm_exec_env_t exec_env, char *buf, + uint32 len) +{ + WASMModuleInstanceCommon *module_inst = + wasm_exec_env_get_module_inst(exec_env); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_interp_dump_call_stack(exec_env, false, buf, len); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_dump_call_stack(exec_env, false, buf, len); + } +#endif + + return 0; +} +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK */ + +#if WASM_ENABLE_STATIC_PGO != 0 +uint32 +wasm_runtime_get_pgo_prof_data_size(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_inst = (AOTModuleInstance *)module_inst; + return aot_get_pgo_prof_data_size(aot_inst); + } +#endif + return 0; +} + +uint32 +wasm_runtime_dump_pgo_prof_data_to_buf(WASMModuleInstanceCommon *module_inst, + char *buf, uint32 len) +{ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_inst = (AOTModuleInstance *)module_inst; + return aot_dump_pgo_prof_data_to_buf(aot_inst, buf, len); + } +#endif + return 0; +} +#endif /* end of WASM_ENABLE_STATIC_PGO != 0 */ + +bool +wasm_runtime_get_table_elem_type(const WASMModuleCommon *module_comm, + uint32 table_idx, uint8 *out_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **out_ref_type, +#endif + uint32 *out_min_size, uint32 *out_max_size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_comm->module_type == Wasm_Module_Bytecode) { + WASMModule *module = (WASMModule *)module_comm; + + if (table_idx < module->import_table_count) { + WASMTableImport *import_table = + &((module->import_tables + table_idx)->u.table); + *out_elem_type = import_table->table_type.elem_type; +#if WASM_ENABLE_GC != 0 + *out_ref_type = import_table->table_type.elem_ref_type; +#endif + *out_min_size = import_table->table_type.init_size; + *out_max_size = import_table->table_type.max_size; + } + else { + WASMTable *table = + module->tables + (table_idx - module->import_table_count); + *out_elem_type = table->table_type.elem_type; +#if WASM_ENABLE_GC != 0 + *out_ref_type = table->table_type.elem_ref_type; +#endif + *out_min_size = table->table_type.init_size; + *out_max_size = table->table_type.max_size; + } + return true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_comm->module_type == Wasm_Module_AoT) { + AOTModule *module = (AOTModule *)module_comm; + + if (table_idx < module->import_table_count) { + AOTImportTable *import_table = module->import_tables + table_idx; + *out_elem_type = import_table->table_type.elem_type; +#if WASM_ENABLE_GC != 0 + *out_ref_type = NULL; /* TODO */ +#endif + *out_min_size = import_table->table_type.init_size; + *out_max_size = import_table->table_type.max_size; + } + else { + AOTTable *table = + module->tables + (table_idx - module->import_table_count); + *out_elem_type = table->table_type.elem_type; +#if WASM_ENABLE_GC != 0 + *out_ref_type = NULL; /* TODO */ +#endif + *out_min_size = table->table_type.init_size; + *out_max_size = table->table_type.max_size; + } + return true; + } +#endif + + return false; +} + +bool +wasm_runtime_get_table_inst_elem_type( + const WASMModuleInstanceCommon *module_inst_comm, uint32 table_idx, + uint8 *out_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **out_ref_type, +#endif + uint32 *out_min_size, uint32 *out_max_size) +{ + 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_runtime_get_table_elem_type( + (WASMModuleCommon *)module_inst->module, table_idx, out_elem_type, +#if WASM_ENABLE_GC != 0 + out_ref_type, +#endif + out_min_size, out_max_size); +} + +bool +wasm_runtime_get_export_func_type(const WASMModuleCommon *module_comm, + const WASMExport *export, WASMFuncType **out) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_comm->module_type == Wasm_Module_Bytecode) { + WASMModule *module = (WASMModule *)module_comm; + + if (export->index < module->import_function_count) { + *out = module->import_functions[export->index].u.function.func_type; + } + else { + *out = + module->functions[export->index - module->import_function_count] + ->func_type; + } + return true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_comm->module_type == Wasm_Module_AoT) { + AOTModule *module = (AOTModule *)module_comm; + + if (export->index < module->import_func_count) { + *out = (WASMFuncType *) + module->types[module->import_funcs[export->index] + .func_type_index]; + } + else { + *out = (WASMFuncType *)module + ->types[module->func_type_indexes + [export->index - module->import_func_count]]; + } + return true; + } +#endif + return false; +} + +bool +wasm_runtime_get_export_global_type(const WASMModuleCommon *module_comm, + const WASMExport *export, + uint8 *out_val_type, bool *out_mutability) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_comm->module_type == Wasm_Module_Bytecode) { + WASMModule *module = (WASMModule *)module_comm; + + if (export->index < module->import_global_count) { + WASMGlobalImport *import_global = + &((module->import_globals + export->index)->u.global); + *out_val_type = import_global->type.val_type; + *out_mutability = import_global->type.is_mutable; + } + else { + WASMGlobal *global = + module->globals + (export->index - module->import_global_count); + *out_val_type = global->type.val_type; + *out_mutability = global->type.is_mutable; + } + return true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_comm->module_type == Wasm_Module_AoT) { + AOTModule *module = (AOTModule *)module_comm; + + if (export->index < module->import_global_count) { + AOTImportGlobal *import_global = + module->import_globals + export->index; + *out_val_type = import_global->type.val_type; + *out_mutability = import_global->type.is_mutable; + } + else { + AOTGlobal *global = + module->globals + (export->index - module->import_global_count); + *out_val_type = global->type.val_type; + *out_mutability = global->type.is_mutable; + } + return true; + } +#endif + return false; +} + +bool +wasm_runtime_get_export_memory_type(const WASMModuleCommon *module_comm, + const WASMExport *export, + uint32 *out_min_page, uint32 *out_max_page) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_comm->module_type == Wasm_Module_Bytecode) { + WASMModule *module = (WASMModule *)module_comm; + + if (export->index < module->import_memory_count) { + WASMMemoryImport *import_memory = + &((module->import_memories + export->index)->u.memory); + *out_min_page = import_memory->mem_type.init_page_count; + *out_max_page = import_memory->mem_type.max_page_count; + } + else { + WASMMemory *memory = + module->memories + + (export->index - module->import_memory_count); + *out_min_page = memory->init_page_count; + *out_max_page = memory->max_page_count; + } + return true; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_comm->module_type == Wasm_Module_AoT) { + AOTModule *module = (AOTModule *)module_comm; + + if (export->index < module->import_memory_count) { + AOTImportMemory *import_memory = + module->import_memories + export->index; + *out_min_page = import_memory->mem_type.init_page_count; + *out_max_page = import_memory->mem_type.max_page_count; + } + else { + AOTMemory *memory = module->memories + + (export->index - module->import_memory_count); + *out_min_page = memory->init_page_count; + *out_max_page = memory->max_page_count; + } + return true; + } +#endif + return false; +} + +bool +wasm_runtime_get_export_table_type(const WASMModuleCommon *module_comm, + const WASMExport *export, + uint8 *out_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **out_ref_type, +#endif + uint32 *out_min_size, uint32 *out_max_size) +{ + return wasm_runtime_get_table_elem_type(module_comm, export->index, + out_elem_type, +#if WASM_ENABLE_GC != 0 + out_ref_type, +#endif + out_min_size, out_max_size); +} + +static inline bool +argv_to_params(wasm_val_t *out_params, const uint32 *argv, + WASMFuncType *func_type) +{ + wasm_val_t *param = out_params; + uint32 i = 0, *u32; + + for (i = 0; i < func_type->param_count; i++, param++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + param->kind = WASM_I32; + param->of.i32 = *argv++; break; case VALUE_TYPE_I64: - { - union { uint64 val; uint32 parts[2]; } u; - u.val = strtoull(argv[i], &endptr, 0); - argv1[p++] = u.parts[0]; - argv1[p++] = u.parts[1]; + param->kind = WASM_I64; + u32 = (uint32 *)¶m->of.i64; + u32[0] = *argv++; + u32[1] = *argv++; break; - } case VALUE_TYPE_F32: - { - float32 f32 = strtof(argv[i], &endptr); - if (isnan(f32)) { - if (argv[i][0] == '-') { - union ieee754_float u; - u.f = f32; - if (is_little_endian()) - u.ieee.ieee_little_endian.negative = 1; - else - u.ieee.ieee_big_endian.negative = 1; - memcpy(&f32, &u.f, sizeof(float)); - } - if (endptr[0] == ':') { - uint32 sig; - union ieee754_float u; - sig = (uint32)strtoul(endptr + 1, &endptr, 0); - u.f = f32; - if (is_little_endian()) - u.ieee.ieee_little_endian.mantissa = sig; - else - u.ieee.ieee_big_endian.mantissa = sig; - memcpy(&f32, &u.f, sizeof(float)); - } - } - memcpy(&argv1[p++], &f32, sizeof(float)); + param->kind = WASM_F32; + param->of.f32 = *(float32 *)argv++; break; - } case VALUE_TYPE_F64: - { - union { float64 val; uint32 parts[2]; } u; - u.val = strtod(argv[i], &endptr); - if (isnan(u.val)) { - if (argv[i][0] == '-') { - union ieee754_double ud; - ud.d = u.val; - if (is_little_endian()) - ud.ieee.ieee_little_endian.negative = 1; - else - ud.ieee.ieee_big_endian.negative = 1; - memcpy(&u.val, &ud.d, sizeof(double)); - } - if (endptr[0] == ':') { - uint64 sig; - union ieee754_double ud; - sig = strtoull(endptr + 1, &endptr, 0); - ud.d = u.val; - if (is_little_endian()) { - ud.ieee.ieee_little_endian.mantissa0 = sig >> 32; - ud.ieee.ieee_little_endian.mantissa1 = (uint32)sig; - } - else { - ud.ieee.ieee_big_endian.mantissa0 = sig >> 32; - ud.ieee.ieee_big_endian.mantissa1 = (uint32)sig; - } - memcpy(&u.val, &ud.d, sizeof(double)); - } + param->kind = WASM_F64; + u32 = (uint32 *)¶m->of.i64; + u32[0] = *argv++; + u32[1] = *argv++; + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + param->kind = WASM_EXTERNREF; + + if (!wasm_externref_ref2obj(*argv, + (void **)¶m->of.foreign)) { + return false; + } + + argv++; + break; +#endif + default: + return false; + } + } + + return true; +} + +static inline bool +results_to_argv(WASMModuleInstanceCommon *module_inst, uint32 *out_argv, + const wasm_val_t *results, WASMFuncType *func_type) +{ + const wasm_val_t *result = results; + uint32 *argv = out_argv, *u32, i; + uint8 *result_types = func_type->types + func_type->param_count; + + for (i = 0; i < func_type->result_count; i++, result++) { + switch (result_types[i]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + *(int32 *)argv++ = result->of.i32; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + u32 = (uint32 *)&result->of.i64; + *argv++ = u32[0]; + *argv++ = u32[1]; + break; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + if (!wasm_externref_obj2ref(module_inst, + (void *)result->of.foreign, + (uint32 *)argv)) { + return false; } - argv1[p++] = u.parts[0]; - argv1[p++] = u.parts[1]; + argv++; break; - } +#endif + default: + return false; } - if (endptr && *endptr != '\0' && *endptr != '_') { - snprintf(buf, sizeof(buf), "invalid input argument %d: %s.", - i, argv[i]); - wasm_runtime_set_exception(module_inst, buf); - goto fail; + } + + return true; +} + +bool +wasm_runtime_invoke_c_api_native(WASMModuleInstanceCommon *module_inst, + void *func_ptr, WASMFuncType *func_type, + uint32 argc, uint32 *argv, bool with_env, + void *wasm_c_api_env) +{ + wasm_val_t params_buf[16] = { 0 }, results_buf[4] = { 0 }; + wasm_val_t *params = params_buf, *results = results_buf; + wasm_trap_t *trap = NULL; + bool ret = false; + wasm_val_vec_t params_vec = { 0 }, results_vec = { 0 }; + + if (func_type->param_count > 16) { + if (!(params = + runtime_malloc(sizeof(wasm_val_t) * func_type->param_count, + module_inst, NULL, 0))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed"); + return false; } - if (errno != 0) { - snprintf(buf, sizeof(buf), - "prepare function argument error, errno: %d.", errno); - wasm_runtime_set_exception(module_inst, buf); + } + + if (!argv_to_params(params, argv, func_type)) { + wasm_runtime_set_exception(module_inst, "unsupported param type"); + goto fail; + } + + if (func_type->result_count > 4) { + if (!(results = + runtime_malloc(sizeof(wasm_val_t) * func_type->result_count, + module_inst, NULL, 0))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed"); goto fail; } } - bh_assert(p == (int32)argc1); - wasm_runtime_set_exception(module_inst, NULL); - if (!wasm_runtime_create_exec_env_and_call_wasm(module_inst, func, - argc1, argv1)) { - goto fail; + params_vec.data = params; + params_vec.num_elems = func_type->param_count; + params_vec.size = func_type->param_count; + + results_vec.data = results; + results_vec.num_elems = 0; + results_vec.size = func_type->result_count; + + if (!with_env) { + wasm_func_callback_t callback = (wasm_func_callback_t)func_ptr; + trap = callback(¶ms_vec, &results_vec); + } + else { + wasm_func_callback_with_env_t callback = + (wasm_func_callback_with_env_t)func_ptr; + trap = callback(wasm_c_api_env, ¶ms_vec, &results_vec); } - /* print return value */ - switch (type->types[type->param_count]) { - case VALUE_TYPE_I32: - bh_printf("0x%x:i32", argv1[0]); - break; - case VALUE_TYPE_I64: - { - char buf[16]; - union { uint64 val; uint32 parts[2]; } u; - u.parts[0] = argv1[0]; - u.parts[1] = argv1[1]; - if (sizeof(long) == 4) - snprintf(buf, sizeof(buf), "%s", "0x%llx:i64"); - else - snprintf(buf, sizeof(buf), "%s", "0x%lx:i64"); - bh_printf(buf, u.val); - break; + if (trap) { + if (trap->message->data) { + /* since trap->message->data does not end with '\0' */ + char trap_message[108] = { 0 }; + uint32 max_size_to_copy = (uint32)sizeof(trap_message) - 1; + uint32 size_to_copy = (trap->message->size < max_size_to_copy) + ? (uint32)trap->message->size + : max_size_to_copy; + bh_memcpy_s(trap_message, (uint32)sizeof(trap_message), + trap->message->data, size_to_copy); + wasm_runtime_set_exception(module_inst, trap_message); } - case VALUE_TYPE_F32: - bh_printf("%.7g:f32", *(float32*)argv1); - break; - case VALUE_TYPE_F64: - { - union { float64 val; uint32 parts[2]; } u; - u.parts[0] = argv1[0]; - u.parts[1] = argv1[1]; - bh_printf("%.7g:f64", u.val); - break; + else { + wasm_runtime_set_exception( + module_inst, "native function throw unknown exception"); } + wasm_trap_delete(trap); + goto fail; } - bh_printf("\n"); - wasm_free(argv1); - return true; + if (!results_to_argv(module_inst, argv, results, func_type)) { + wasm_runtime_set_exception(module_inst, "unsupported result type"); + goto fail; + } + ret = true; fail: - if (argv1) - wasm_free(argv1); + if (params != params_buf) + wasm_runtime_free(params); + if (results != results_buf) + wasm_runtime_free(results); + return ret; +} - exception = wasm_runtime_get_exception(module_inst); - bh_assert(exception); - bh_printf("%s\n", exception); - return false; +bool +wasm_runtime_quick_invoke_c_api_native(WASMModuleInstanceCommon *inst_comm, + CApiFuncImport *c_api_import, + wasm_val_t *params, uint32 param_count, + wasm_val_t *results, uint32 result_count) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)inst_comm; + void *func_ptr = c_api_import->func_ptr_linked; + bool with_env_arg = c_api_import->with_env_arg, ret = true; + wasm_val_vec_t params_vec = { 0 }, results_vec = { 0 }; + wasm_trap_t *trap = NULL; + + params_vec.data = params; + params_vec.num_elems = param_count; + params_vec.size = param_count; + + results_vec.data = results; + results_vec.num_elems = 0; + results_vec.size = result_count; + + if (!func_ptr) { + wasm_set_exception_with_id(module_inst, EXCE_CALL_UNLINKED_IMPORT_FUNC); + ret = false; + goto fail; + } + + if (!with_env_arg) { + wasm_func_callback_t callback = (wasm_func_callback_t)func_ptr; + trap = callback(¶ms_vec, &results_vec); + } + else { + void *wasm_c_api_env = c_api_import->env_arg; + wasm_func_callback_with_env_t callback = + (wasm_func_callback_with_env_t)func_ptr; + trap = callback(wasm_c_api_env, ¶ms_vec, &results_vec); + } + + if (trap) { + if (trap->message->data) { + /* since trap->message->data does not end with '\0' */ + char trap_message[108] = { 0 }; + uint32 max_size_to_copy = (uint32)sizeof(trap_message) - 1; + uint32 size_to_copy = (trap->message->size < max_size_to_copy) + ? (uint32)trap->message->size + : max_size_to_copy; + bh_memcpy_s(trap_message, (uint32)sizeof(trap_message), + trap->message->data, size_to_copy); + wasm_set_exception(module_inst, trap_message); + } + else { + wasm_set_exception(module_inst, + "native function throw unknown exception"); + } + wasm_trap_delete(trap); + ret = false; + } + +fail: +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!ret) + wasm_runtime_access_exce_check_guard_page(); +#endif + return ret; } -/** - * Implementation of wasm_runtime_invoke_native() - */ +void +wasm_runtime_show_app_heap_corrupted_prompt() +{ + LOG_ERROR("Error: app heap is corrupted, if the wasm file " + "is compiled by wasi-sdk-12.0 or higher version, " + "please add -Wl,--export=malloc -Wl,--export=free " + "to export malloc and free functions. If it is " + "compiled by asc, please add --exportRuntime to " + "export the runtime helpers."); +} -static inline void -word_copy(uint32 *dest, uint32 *src, unsigned num) +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +void +wasm_runtime_destroy_custom_sections(WASMCustomSection *section_list) { - for (; num > 0; num--) - *dest++ = *src++; + WASMCustomSection *section = section_list, *next; + while (section) { + next = section->next; + wasm_runtime_free(section); + section = next; + } } +#endif /* end of WASM_ENABLE_LOAD_CUSTOM_SECTION */ -#define PUT_I64_TO_ADDR(addr, value) do { \ - union { int64 val; uint32 parts[2]; } u; \ - u.val = (value); \ - (addr)[0] = u.parts[0]; \ - (addr)[1] = u.parts[1]; \ - } while (0) +void +wasm_runtime_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch) +{ + *major = WAMR_VERSION_MAJOR; + *minor = WAMR_VERSION_MINOR; + *patch = WAMR_VERSION_PATCH; +} -#define PUT_F64_TO_ADDR(addr, value) do { \ - union { float64 val; uint32 parts[2]; } u; \ - u.val = (value); \ - (addr)[0] = u.parts[0]; \ - (addr)[1] = u.parts[1]; \ - } while (0) +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); +} -/* The invoke native implementation on ARM platform with VFP co-processor */ -#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) -typedef void (*GenericFunctionPointer)(); -int64 invokeNative(GenericFunctionPointer f, uint32 *args, uint32 n_stacks); - -typedef float64 (*Float64FuncPtr)(GenericFunctionPointer, uint32*, uint32); -typedef float32 (*Float32FuncPtr)(GenericFunctionPointer, uint32*, uint32); -typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint32*,uint32); -typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint32*, uint32); -typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint32*, uint32); - -static Float64FuncPtr invokeNative_Float64 = (Float64FuncPtr)invokeNative; -static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)invokeNative; -static Int64FuncPtr invokeNative_Int64 = (Int64FuncPtr)invokeNative; -static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)invokeNative; -static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; - -#define MAX_REG_INTS 4 -#define MAX_REG_FLOATS 16 +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 +} + +#if WASM_ENABLE_LIBC_WASI != 0 || WASM_ENABLE_MULTI_MODULE != 0 +WASMExport * +loader_find_export(const WASMModuleCommon *module, const char *module_name, + const char *field_name, uint8 export_kind, char *error_buf, + uint32 error_buf_size) +{ + WASMExport *exports = NULL, *result = NULL, *export; + uint32 export_count = 0, i; +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModule *aot_module = (AOTModule *)module; + exports = (WASMExport *)aot_module->exports; + export_count = aot_module->export_count; + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModule *wasm_module = (WASMModule *)module; + exports = wasm_module->exports; + export_count = wasm_module->export_count; + } +#endif + for (i = 0, export = exports; i < export_count; ++i, ++export) { + if (export->kind == export_kind && !strcmp(field_name, export->name)) { + result = export; + goto exit; + } + } + if (i == export_count) { + LOG_DEBUG("can not find an export %d named %s in the module %s", + export_kind, field_name, module_name); + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + } +exit: + return result; +} +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMModuleCommon * +wasm_runtime_search_sub_module(const WASMModuleCommon *parent_module, + const char *sub_module_name) +{ + WASMRegisteredModule *node = NULL; +#if WASM_ENABLE_AOT != 0 + if (parent_module->module_type == Wasm_Module_AoT) { + node = bh_list_first_elem( + ((AOTModule *)parent_module)->import_module_list); + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (parent_module->module_type == Wasm_Module_Bytecode) { + node = bh_list_first_elem( + ((WASMModule *)parent_module)->import_module_list); + } +#endif + while (node && strcmp(sub_module_name, node->module_name)) { + node = bh_list_elem_next(node); + } + return node ? node->module : NULL; +} bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret) +wasm_runtime_register_sub_module(const WASMModuleCommon *parent_module, + const char *sub_module_name, + WASMModuleCommon *sub_module) { - /* argv buf layout: int args(fix cnt) + float args(fix cnt) + stack args */ - uint32 argv_buf[32], *argv1 = argv_buf, *fps, *ints, *stacks, size; - uint32 *argv_src = argv, i, argc1, n_ints = 0, n_fps = 0, n_stacks = 0; + /* register sub_module into its parent sub module list */ + WASMRegisteredModule *node = NULL; + bh_list_status ret = BH_LIST_ERROR; - n_ints++; /* exec env */ + if (wasm_runtime_search_sub_module(parent_module, sub_module_name)) { + LOG_DEBUG("%s has been registered in its parent", sub_module_name); + return true; + } - /* Traverse firstly to calculate stack args count */ - for (i = 0; i < func_type->param_count; i++) { - switch (func_type->types[i]) { - case VALUE_TYPE_I32: - if (n_ints < MAX_REG_INTS) - n_ints++; - else - n_stacks++; - break; - case VALUE_TYPE_I64: - if (n_ints < MAX_REG_INTS - 1) { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_ints & 1) - n_ints++; - n_ints += 2; - } - else { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_stacks & 1) - n_stacks++; - n_stacks += 2; - } - break; - case VALUE_TYPE_F32: - if (n_fps < MAX_REG_FLOATS) - n_fps++; - else - n_stacks++; - break; - case VALUE_TYPE_F64: - if (n_fps < MAX_REG_FLOATS - 1) { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_fps & 1) - n_fps++; - n_fps += 2; - } - else { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_stacks & 1) - n_stacks++; - n_stacks += 2; - } - break; - default: - bh_assert(0); - break; - } + node = loader_malloc(sizeof(WASMRegisteredModule), NULL, 0); + if (!node) { + return false; } - argc1 = MAX_REG_INTS + MAX_REG_FLOATS + n_stacks; - if (argc1 > sizeof(argv_buf) / sizeof(uint32)) { - size = sizeof(uint32) * (uint32)argc1; - if (size >= UINT32_MAX - || !(argv1 = wasm_malloc((uint32)size))) { - wasm_runtime_set_exception(exec_env->module_inst, - "allocate memory failed."); - return false; - } + node->module_name = sub_module_name; + node->module = sub_module; +#if WASM_ENABLE_AOT != 0 + if (parent_module->module_type == Wasm_Module_AoT) { + ret = bh_list_insert(((AOTModule *)parent_module)->import_module_list, + node); + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (parent_module->module_type == Wasm_Module_Bytecode) { + ret = bh_list_insert(((WASMModule *)parent_module)->import_module_list, + node); } +#endif + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + return true; +} - ints = argv1; - fps = ints + MAX_REG_INTS; - stacks = fps + MAX_REG_FLOATS; +WASMModuleCommon * +wasm_runtime_load_depended_module(const WASMModuleCommon *parent_module, + const char *sub_module_name, char *error_buf, + uint32 error_buf_size) +{ + WASMModuleCommon *sub_module = NULL; + bool ret = false; + uint8 *buffer = NULL; + uint32 buffer_size = 0; + LoadArgs args = { 0 }; + + /* check the registered module list of the parent */ + sub_module = wasm_runtime_search_sub_module(parent_module, sub_module_name); + if (sub_module) { + LOG_DEBUG("%s has been loaded before", sub_module_name); + return sub_module; + } - n_ints = 0; - n_fps = 0; - n_stacks = 0; - ints[n_ints++] = (uint32)(uintptr_t)exec_env; + /* check the global registered module list */ + sub_module = wasm_runtime_find_module_registered(sub_module_name); + if (sub_module) { + LOG_DEBUG("%s has been loaded", sub_module_name); + goto wasm_runtime_register_sub_module; + } + LOG_VERBOSE("loading %s", sub_module_name); + if (!reader) { + set_error_buf_v(parent_module, error_buf, error_buf_size, + "no sub module reader to load %s", sub_module_name); + return NULL; + } + /* start to maintain a loading module list */ + ret = wasm_runtime_is_loading_module(sub_module_name); + if (ret) { + set_error_buf_v(parent_module, error_buf, error_buf_size, + "found circular dependency on %s", sub_module_name); + return NULL; + } + ret = wasm_runtime_add_loading_module(sub_module_name, error_buf, + error_buf_size); + if (!ret) { + LOG_DEBUG("can not add %s into loading module list\n", sub_module_name); + return NULL; + } - /* Traverse secondly to fill in each argument */ - for (i = 0; i < func_type->param_count; i++) { - switch (func_type->types[i]) { - case VALUE_TYPE_I32: - if (n_ints < MAX_REG_INTS) - ints[n_ints++] = *argv_src++; - else - stacks[n_stacks++] = *argv_src++; - break; - case VALUE_TYPE_I64: - if (n_ints < MAX_REG_INTS - 1) { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_ints & 1) - n_ints++; - *(uint64*)&ints[n_ints] = *(uint64*)argv_src; - n_ints += 2; - } - else { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_stacks & 1) - n_stacks++; - *(uint64*)&stacks[n_stacks] = *(uint64*)argv_src; - n_stacks += 2; - } - argv_src += 2; - break; - case VALUE_TYPE_F32: - if (n_fps < MAX_REG_FLOATS) - *(float32*)&fps[n_fps++] = *(float32*)argv_src++; - else - *(float32*)&stacks[n_stacks++] = *(float32*)argv_src++; - break; - case VALUE_TYPE_F64: - if (n_fps < MAX_REG_FLOATS - 1) { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_fps & 1) - n_fps++; - *(float64*)&fps[n_fps] = *(float64*)argv_src; - n_fps += 2; - } - else { - /* 64-bit data must be 8 bytes aligned in arm */ - if (n_stacks & 1) - n_stacks++; - *(float64*)&stacks[n_stacks] = *(float64*)argv_src; - n_stacks += 2; + ret = reader(parent_module->module_type, sub_module_name, &buffer, + &buffer_size); + if (!ret) { + LOG_DEBUG("read the file of %s failed", sub_module_name); + set_error_buf_v(parent_module, error_buf, error_buf_size, + "unknown import %s", sub_module_name); + goto delete_loading_module; + } + if (get_package_type(buffer, buffer_size) != parent_module->module_type) { + LOG_DEBUG("module %s type error", sub_module_name); + goto destroy_file_buffer; + } + + args.name = (char *)sub_module_name; + if (get_package_type(buffer, buffer_size) == Wasm_Module_Bytecode) { +#if WASM_ENABLE_INTERP != 0 + sub_module = (WASMModuleCommon *)wasm_load( + buffer, buffer_size, false, &args, error_buf, error_buf_size); +#endif + } + else if (get_package_type(buffer, buffer_size) == Wasm_Module_AoT) { +#if WASM_ENABLE_AOT != 0 + sub_module = (WASMModuleCommon *)aot_load_from_aot_file( + buffer, buffer_size, &args, error_buf, error_buf_size); +#endif + } + if (!sub_module) { + LOG_DEBUG("error: can not load the sub_module %s", sub_module_name); + /* others will be destroyed in runtime_destroy() */ + goto destroy_file_buffer; + } + wasm_runtime_delete_loading_module(sub_module_name); + /* register on a global list */ + ret = wasm_runtime_register_module_internal( + sub_module_name, (WASMModuleCommon *)sub_module, buffer, buffer_size, + error_buf, error_buf_size); + if (!ret) { + LOG_DEBUG("error: can not register module %s globally\n", + sub_module_name); + /* others will be unloaded in runtime_destroy() */ + goto unload_module; + } + + /* register into its parent list */ +wasm_runtime_register_sub_module: + ret = wasm_runtime_register_sub_module(parent_module, sub_module_name, + sub_module); + if (!ret) { + set_error_buf_v(parent_module, error_buf, error_buf_size, + "failed to register sub module %s", sub_module_name); + /* since it is in the global module list, no need to + * unload the module. the runtime_destroy() will do it + */ + return NULL; + } + + return sub_module; + +unload_module: + wasm_runtime_unload(sub_module); + +destroy_file_buffer: + if (destroyer) { + destroyer(buffer, buffer_size); + } + else { + LOG_WARNING("need to release the reading buffer of %s manually", + sub_module_name); + } + +delete_loading_module: + wasm_runtime_delete_loading_module(sub_module_name); + return NULL; +} + +bool +wasm_runtime_sub_module_instantiate(WASMModuleCommon *module, + WASMModuleInstanceCommon *module_inst, + const struct InstantiationArgs2 *args, + char *error_buf, uint32 error_buf_size) +{ + bh_list *sub_module_inst_list = NULL; + WASMRegisteredModule *sub_module_list_node = NULL; + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + sub_module_inst_list = + ((AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e) + ->sub_module_inst_list; + sub_module_list_node = + bh_list_first_elem(((AOTModule *)module)->import_module_list); + } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + sub_module_inst_list = + ((WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e) + ->sub_module_inst_list; + sub_module_list_node = + bh_list_first_elem(((WASMModule *)module)->import_module_list); + } +#endif + while (sub_module_list_node) { + WASMSubModInstNode *sub_module_inst_list_node = NULL; + WASMModuleCommon *sub_module = sub_module_list_node->module; + WASMModuleInstanceCommon *sub_module_inst = NULL; + sub_module_inst = wasm_runtime_instantiate_internal( + sub_module, NULL, NULL, args, error_buf, error_buf_size); + if (!sub_module_inst) { + LOG_DEBUG("instantiate %s failed", + sub_module_list_node->module_name); + return false; + } + sub_module_inst_list_node = loader_malloc(sizeof(WASMSubModInstNode), + error_buf, error_buf_size); + if (!sub_module_inst_list_node) { + LOG_DEBUG("Malloc WASMSubModInstNode failed, SZ: %zu", + sizeof(WASMSubModInstNode)); + if (sub_module_inst) + wasm_runtime_deinstantiate_internal(sub_module_inst, false); + return false; + } + sub_module_inst_list_node->module_inst = + (WASMModuleInstance *)sub_module_inst; + sub_module_inst_list_node->module_name = + sub_module_list_node->module_name; + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_module_inst = + (AOTModuleInstance *)module_inst; + AOTModule *aot_module = (AOTModule *)module; + AOTModuleInstanceExtra *aot_extra = + (AOTModuleInstanceExtra *)aot_module_inst->e; + uint32 i; + AOTImportFunc *import_func; + for (i = 0; i < aot_module->import_func_count; i++) { + if (aot_extra->import_func_module_insts[i]) + continue; + + import_func = &aot_module->import_funcs[i]; + if (strcmp(sub_module_inst_list_node->module_name, + import_func->module_name) + == 0) { + aot_extra->import_func_module_insts[i] = + (WASMModuleInstanceCommon *) + sub_module_inst_list_node->module_inst; } - argv_src += 2; - break; - default: - bh_assert(0); - break; + } } +#endif + + bh_list_status ret = + bh_list_insert(sub_module_inst_list, sub_module_inst_list_node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + sub_module_list_node = bh_list_elem_next(sub_module_list_node); } - if (func_type->result_count == 0) { - invokeNative_Void(func_ptr, argv1, n_stacks); + return true; +} + +void +wasm_runtime_sub_module_deinstantiate(WASMModuleInstanceCommon *module_inst) +{ + bh_list *list = NULL; +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + list = ((AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e) + ->sub_module_inst_list; } - else { - switch (func_type->types[func_type->param_count]) { - case VALUE_TYPE_I32: - ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); - break; - case VALUE_TYPE_I64: - PUT_I64_TO_ADDR(ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); - break; - case VALUE_TYPE_F32: - *(float32*)ret = invokeNative_Float32(func_ptr, argv1, n_stacks); - break; - case VALUE_TYPE_F64: - PUT_F64_TO_ADDR(ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); - break; - default: - bh_assert(0); - break; - } +#endif +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + list = + ((WASMModuleInstanceExtra *)((WASMModuleInstance *)module_inst)->e) + ->sub_module_inst_list; } - if (argv1 != argv_buf) - wasm_free(argv1); +#endif - return true; + WASMSubModInstNode *node = bh_list_first_elem(list); + while (node) { + WASMSubModInstNode *next_node = bh_list_elem_next(node); + bh_list_remove(list, node); + wasm_runtime_deinstantiate_internal( + (WASMModuleInstanceCommon *)node->module_inst, false); + wasm_runtime_free(node); + node = next_node; + } +} +#endif /* end of WASM_ENABLE_MULTI_MODULE */ +#if WASM_ENABLE_MODULE_INST_CONTEXT != 0 +void * +wasm_runtime_create_context_key(void (*dtor)(WASMModuleInstanceCommon *inst, + void *ctx)) +{ + return wasm_native_create_context_key(dtor); } -#endif /* end of defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) */ -#if defined(BUILD_TARGET_X86_32) \ - || defined(BUILD_TARGET_ARM) \ - || defined(BUILD_TARGET_THUMB) \ - || defined(BUILD_TARGET_MIPS) \ - || defined(BUILD_TARGET_XTENSA) -typedef void (*GenericFunctionPointer)(); -int64 invokeNative(GenericFunctionPointer f, uint32 *args, uint32 sz); +void +wasm_runtime_destroy_context_key(void *key) +{ + wasm_native_destroy_context_key(key); +} + +void +wasm_runtime_set_context(WASMModuleInstanceCommon *inst, void *key, void *ctx) +{ + wasm_native_set_context(inst, key, ctx); +} + +void +wasm_runtime_set_context_spread(WASMModuleInstanceCommon *inst, void *key, + void *ctx) +{ + wasm_native_set_context_spread(inst, key, ctx); +} -typedef float64 (*Float64FuncPtr)(GenericFunctionPointer f, uint32*, uint32); -typedef float32 (*Float32FuncPtr)(GenericFunctionPointer f, uint32*, uint32); -typedef int64 (*Int64FuncPtr)(GenericFunctionPointer f, uint32*, uint32); -typedef int32 (*Int32FuncPtr)(GenericFunctionPointer f, uint32*, uint32); -typedef void (*VoidFuncPtr)(GenericFunctionPointer f, uint32*, uint32); +void * +wasm_runtime_get_context(WASMModuleInstanceCommon *inst, void *key) +{ + return wasm_native_get_context(inst, key); +} +#endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */ -static Int64FuncPtr invokeNative_Int64 = (Int64FuncPtr)invokeNative; -static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)invokeNative; -static Float64FuncPtr invokeNative_Float64 = (Float64FuncPtr)invokeNative; -static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)invokeNative; -static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; +#if WASM_ENABLE_LINUX_PERF != 0 +static bool enable_linux_perf = false; bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret) +wasm_runtime_get_linux_perf(void) { - uint32 argv_buf[32], *argv1 = argv_buf, argc1, i, j = 0; - uint64 size; + return enable_linux_perf; +} -#if defined(BUILD_TARGET_X86_32) - argc1 = argc + 2; -#else - /* arm/thumb/mips/xtensa, 64-bit data must be 8 bytes aligned, - so we need to allocate more memory. */ - argc1 = func_type->param_count * 2 + 2; +void +wasm_runtime_set_linux_perf(bool flag) +{ + enable_linux_perf = flag; +} #endif - if (argc1 > sizeof(argv_buf) / sizeof(uint32)) { - size = sizeof(uint32) * (uint64)argc1; - if (size >= UINT_MAX - || !(argv1 = wasm_malloc((uint32)size))) { - wasm_runtime_set_exception(exec_env->module_inst, - "allocate memory failed."); - return false; - } - } - - for (i = 0; i < sizeof(WASMExecEnv*) / sizeof(uint32); i++) - argv1[j++] = ((uint32*)&exec_env)[i]; +bool +wasm_runtime_set_module_name(wasm_module_t module, const char *name, + char *error_buf, uint32_t error_buf_size) +{ + if (!module) + return false; -#if defined(BUILD_TARGET_X86_32) - word_copy(argv1 + j, argv, argc); - j += argc; -#else - for (i = 0; i < func_type->param_count; i++) { - switch (func_type->types[i]) { - case VALUE_TYPE_I32: - argv1[j++] = *argv++; - break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - /* 64-bit data must be 8 bytes aligned in arm and mips */ - if (j & 1) - j++; - argv1[j++] = *argv++; - argv1[j++] = *argv++; - break; - case VALUE_TYPE_F32: - argv1[j++] = *argv++; - break; - default: - bh_assert(0); - break; - } - } -#endif /* end of defined(BUILD_TARGET_X86_32) */ +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) + return wasm_set_module_name((WASMModule *)module, name, error_buf, + error_buf_size); +#endif - argc1 = j; - if (func_type->result_count == 0) { - invokeNative_Void(func_ptr, argv1, argc1); - } - else { - switch (func_type->types[func_type->param_count]) { - case VALUE_TYPE_I32: - ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, argc1); - break; - case VALUE_TYPE_I64: - PUT_I64_TO_ADDR(ret, invokeNative_Int64(func_ptr, argv1, argc1)); - break; - case VALUE_TYPE_F32: - *(float32*)ret = invokeNative_Float32(func_ptr, argv1, argc1); - break; - case VALUE_TYPE_F64: - PUT_F64_TO_ADDR(ret, invokeNative_Float64(func_ptr, argv1, argc1)); - break; - default: - bh_assert(0); - break; - } - } +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + return aot_set_module_name((AOTModule *)module, name, error_buf, + error_buf_size); +#endif - if (argv1 != argv_buf) - wasm_free(argv1); - return true; + return false; } -#endif /* end of defined(BUILD_TARGET_X86_32) \ - || defined(BUILD_TARGET_ARM) \ - || defined(BUILD_TARGET_THUMB) \ - || defined(BUILD_TARGET_MIPS) \ - || defined(BUILD_TARGET_XTENSA) */ +const char * +wasm_runtime_get_module_name(wasm_module_t module) +{ + if (!module) + return ""; -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) -typedef void (*GenericFunctionPointer)(); -int64 invokeNative(GenericFunctionPointer f, uint64 *args, uint64 n_stacks); +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) + return wasm_get_module_name((WASMModule *)module); +#endif -typedef float64 (*Float64FuncPtr)(GenericFunctionPointer, uint64*, uint64); -typedef float32 (*Float32FuncPtr)(GenericFunctionPointer, uint64*, uint64); -typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint64*,uint64); -typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint64*, uint64); -typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint64*, uint64); +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + return aot_get_module_name((AOTModule *)module); +#endif -static Float64FuncPtr invokeNative_Float64 = (Float64FuncPtr)invokeNative; -static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)invokeNative; -static Int64FuncPtr invokeNative_Int64 = (Int64FuncPtr)invokeNative; -static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)invokeNative; -static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; + return ""; +} -#if defined(_WIN32) || defined(_WIN32_) -#define MAX_REG_FLOATS 4 -#define MAX_REG_INTS 4 +#if defined(__GNUC__) || defined(__clang__) +/* In few places we use addresses of local variables for estimating used stack + size. This logic conficts with ASAN, since it uses fake stack for local + variables storage. +*/ +#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) #else -#define MAX_REG_FLOATS 8 -#define MAX_REG_INTS 6 +#define NO_SANITIZE_ADDRESS #endif +/* + * wasm_runtime_detect_native_stack_overflow + * + * - raise "native stack overflow" exception if available native stack + * at this point is less than WASM_STACK_GUARD_SIZE. in that case, + * return false. + * + * - update native_stack_top_min. + */ +NO_SANITIZE_ADDRESS bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret) +wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) { - uint64 argv_buf[32], *argv1 = argv_buf, *fps, *ints, *stacks, size; - uint32 *argv_src = argv, i, argc1, n_ints = 0, n_stacks = 0; -#if defined(_WIN32) || defined(_WIN32_) - /* important difference in calling conventions */ -#define n_fps n_ints -#else - int n_fps = 0; + uint8 *boundary = exec_env->native_stack_boundary; + RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary); + if (boundary == NULL) { + /* the platform doesn't support os_thread_get_stack_boundary */ + return true; + } +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + boundary = boundary + page_size * guard_page_count; +#endif + if ((uint8 *)&boundary < boundary) { + wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), + "native stack overflow"); + return false; + } + return true; +} + +NO_SANITIZE_ADDRESS +bool +wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, + uint32 requested_size) +{ + uint8 *boundary = exec_env->native_stack_boundary; + RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary); + if (boundary == NULL) { + /* the platform doesn't support os_thread_get_stack_boundary */ + return true; + } +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + boundary = boundary + page_size * guard_page_count; #endif + /* adjust the boundary for the requested size */ + boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size; + if ((uint8 *)&boundary < boundary) { + wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), + "native stack overflow"); + return false; + } + return true; +} - argc1 = 1 + MAX_REG_FLOATS + func_type->param_count + 2; - if (argc1 > sizeof(argv_buf) / sizeof(uint64)) { - size = sizeof(uint64) * (uint64)argc1; - if (size >= UINT32_MAX - || !(argv1 = wasm_malloc((uint32)size))) { - wasm_runtime_set_exception(exec_env->module_inst, - "allocate memory failed."); +bool +wasm_runtime_is_underlying_binary_freeable(WASMModuleCommon *const module) +{ +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { +#if (WASM_ENABLE_JIT != 0 || WASM_ENABLE_FAST_JIT != 0) \ + && (WASM_ENABLE_LAZY_JIT != 0) + return false; +#elif WASM_ENABLE_FAST_INTERP == 0 + return false; +#else + /* Fast interpreter mode */ + if (!((WASMModule *)module)->is_binary_freeable) return false; - } +#if WASM_ENABLE_GC != 0 && WASM_ENABLE_STRINGREF != 0 + if (((WASMModule *)module)->string_literal_ptrs) + return false; +#endif +#endif + } +#endif /* WASM_ENABLE_INTERP != 0 */ +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + if (!((AOTModule *)module)->is_binary_freeable) + return false; +#if WASM_ENABLE_GC != 0 && WASM_ENABLE_STRINGREF != 0 + if (((AOTModule *)module)->string_literal_ptrs) + return false; +#endif } +#endif /* WASM_ENABLE_AOT != 0 */ - fps = argv1; - ints = fps + MAX_REG_FLOATS; - stacks = ints + MAX_REG_INTS; + return true; +} - ints[n_ints++] = (uint64)(uintptr_t)exec_env; +#if WASM_ENABLE_SHARED_HEAP != 0 +bool +wasm_runtime_check_and_update_last_used_shared_heap( + WASMModuleInstanceCommon *module_inst, uintptr_t app_offset, size_t bytes, + uintptr_t *shared_heap_start_off_p, uintptr_t *shared_heap_end_off_p, + uint8 **shared_heap_base_addr_adj_p, bool is_memory64) +{ + WASMSharedHeap *heap = wasm_runtime_get_shared_heap(module_inst), *cur; + uint64 shared_heap_start, shared_heap_end; - for (i = 0; i < func_type->param_count; i++) { - switch (func_type->types[i]) { - case VALUE_TYPE_I32: - if (n_ints < MAX_REG_INTS) - ints[n_ints++] = *argv_src++; - else - stacks[n_stacks++] = *argv_src++; - break; - case VALUE_TYPE_I64: - if (n_ints < MAX_REG_INTS) - ints[n_ints++] = *(uint64*)argv_src; - else - stacks[n_stacks++] = *(uint64*)argv_src; - argv_src += 2; - break; - case VALUE_TYPE_F32: - if (n_fps < MAX_REG_FLOATS) - *(float32*)&fps[n_fps++] = *(float32*)argv_src++; - else - *(float32*)&stacks[n_stacks++] = *(float32*)argv_src++; - break; - case VALUE_TYPE_F64: - if (n_fps < MAX_REG_FLOATS) - *(float64*)&fps[n_fps++] = *(float64*)argv_src; - else - *(float64*)&stacks[n_stacks++] = *(float64*)argv_src; - argv_src += 2; - break; - default: - bh_assert(0); - break; - } + if (bytes == 0) { + bytes = 1; } - if (func_type->result_count == 0) { - invokeNative_Void(func_ptr, argv1, n_stacks); - } - else { - switch (func_type->types[func_type->param_count]) { - case VALUE_TYPE_I32: - ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); - break; - case VALUE_TYPE_I64: - PUT_I64_TO_ADDR(ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); - break; - case VALUE_TYPE_F32: - *(float32*)ret = invokeNative_Float32(func_ptr, argv1, n_stacks); - break; - case VALUE_TYPE_F64: - PUT_F64_TO_ADDR(ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); - break; - default: - bh_assert(0); - break; + /* Find the exact shared heap that app addr is in, and update last used + * shared heap info in func context */ + for (cur = heap; cur; cur = cur->chain_next) { + shared_heap_start = + is_memory64 ? cur->start_off_mem64 : cur->start_off_mem32; + shared_heap_end = shared_heap_start - 1 + cur->size; + if (bytes - 1 <= shared_heap_end && app_offset >= shared_heap_start + && app_offset <= shared_heap_end - bytes + 1) { + *shared_heap_start_off_p = (uintptr_t)shared_heap_start; + *shared_heap_end_off_p = (uintptr_t)shared_heap_end; + *shared_heap_base_addr_adj_p = + cur->base_addr - (uintptr_t)shared_heap_start; + return true; } } - if (argv1 != argv_buf) - wasm_free(argv1); - - return true; + return false; } +#endif -#endif /* end of defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) */ +WASMModuleInstanceExtraCommon * +GetModuleInstanceExtraCommon(WASMModuleInstance *module_inst) +{ +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return &((AOTModuleInstanceExtra *)module_inst->e)->common; + } + else { + return &module_inst->e->common; + } +#else + return &module_inst->e->common; +#endif +} diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 5e3fc25014..0fdece2663 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -8,27 +8,481 @@ #include "bh_platform.h" #include "bh_common.h" -#include "bh_thread.h" #include "wasm_exec_env.h" +#include "wasm_native.h" +#include "../include/wasm_export.h" #include "../interpreter/wasm.h" +#if WASM_ENABLE_GC != 0 +#include "gc/gc_object.h" +#endif + #if WASM_ENABLE_LIBC_WASI != 0 -#include "wasmtime_ssp.h" +#if WASM_ENABLE_UVWASI == 0 #include "posix.h" +#else +#include "uvwasi.h" +#endif #endif #ifdef __cplusplus extern "C" { #endif -#define wasm_malloc bh_malloc -#define wasm_free bh_free +/* 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) \ + do { \ + *(int64 *)(addr) = (int64)(value); \ + } while (0) +#define PUT_V128_TO_ADDR(addr, value) \ + do { \ + *(V128 *)(addr) = (value); \ + } while (0) +#define PUT_F64_TO_ADDR(addr, value) \ + do { \ + *(float64 *)(addr) = (float64)(value); \ + } while (0) +#define PUT_REF_TO_ADDR(addr, value) \ + do { \ + *(void **)(addr) = (void *)(value); \ + } while (0) + +#define GET_I64_FROM_ADDR(addr) (*(int64 *)(addr)) +#define GET_F64_FROM_ADDR(addr) (*(float64 *)(addr)) +#define GET_REF_FROM_ADDR(addr) (*(void **)(addr)) +#define GET_V128_FROM_ADDR(addr) (*(V128 *)(addr)) + +/* For STORE opcodes */ +#define STORE_I64 PUT_I64_TO_ADDR +static inline void +STORE_U32(void *addr, uint32_t value) +{ + *(uint32_t *)(addr) = (uint32_t)(value); +} +static inline void +STORE_U16(void *addr, uint16_t value) +{ + *(uint16_t *)(addr) = (uint16_t)(value); +} +static inline void +STORE_U8(void *addr, uint8_t value) +{ + *(uint8 *)addr = value; +} + +static inline void +STORE_V128(void *addr, V128 value) +{ + *(V128 *)addr = value; +} + +/* For LOAD opcodes */ +#define LOAD_I64(addr) (*(int64 *)(addr)) +#define LOAD_F64(addr) (*(float64 *)(addr)) +#define LOAD_I32(addr) (*(int32 *)(addr)) +#define LOAD_U32(addr) (*(uint32 *)(addr)) +#define LOAD_I16(addr) (*(int16 *)(addr)) +#define LOAD_U16(addr) (*(uint16 *)(addr)) +#define LOAD_V128(addr) (*(V128 *)(addr)) + +#define STORE_PTR(addr, ptr) \ + do { \ + *(void **)addr = (void *)ptr; \ + } while (0) + +#else /* WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 */ + +#define PUT_V128_TO_ADDR(addr, value) \ + do { \ + uint32 *addr_u32 = (uint32 *)(addr); \ + addr_u32[0] = (value).i32x4[0]; \ + addr_u32[1] = (value).i32x4[1]; \ + addr_u32[2] = (value).i32x4[2]; \ + addr_u32[3] = (value).i32x4[3]; \ + } while (0) + +#define PUT_I64_TO_ADDR(addr, value) \ + do { \ + uint32 *addr_u32 = (uint32 *)(addr); \ + union { \ + int64 val; \ + uint32 parts[2]; \ + } u; \ + u.val = (int64)(value); \ + addr_u32[0] = u.parts[0]; \ + addr_u32[1] = u.parts[1]; \ + } while (0) +#define PUT_F64_TO_ADDR(addr, value) \ + do { \ + uint32 *addr_u32 = (uint32 *)(addr); \ + union { \ + float64 val; \ + uint32 parts[2]; \ + } u; \ + u.val = (value); \ + addr_u32[0] = u.parts[0]; \ + addr_u32[1] = u.parts[1]; \ + } while (0) +#if UINTPTR_MAX == UINT64_MAX +#define PUT_REF_TO_ADDR(addr, value) \ + do { \ + uint32 *addr_u32 = (uint32 *)(addr); \ + union { \ + void *val; \ + uint32 parts[2]; \ + } u; \ + u.val = (void *)(value); \ + addr_u32[0] = u.parts[0]; \ + addr_u32[1] = u.parts[1]; \ + } while (0) +#else +#define PUT_REF_TO_ADDR(addr, value) \ + do { \ + *(void **)(addr) = (void *)(value); \ + } while (0) +#endif + +static inline V128 +GET_V128_FROM_ADDR(uint32 *addr) +{ + V128 ret; + ret.i32x4[0] = addr[0]; + ret.i32x4[1] = addr[1]; + ret.i32x4[2] = addr[2]; + ret.i32x4[3] = addr[3]; + return ret; +} + +static inline int64 +GET_I64_FROM_ADDR(uint32 *addr) +{ + union { + int64 val; + uint32 parts[2]; + } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +static inline float64 +GET_F64_FROM_ADDR(uint32 *addr) +{ + union { + float64 val; + uint32 parts[2]; + } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +#if UINTPTR_MAX == UINT64_MAX +static inline void * +GET_REF_FROM_ADDR(uint32 *addr) +{ + union { + void *val; + uint32 parts[2]; + } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} +#else +#define GET_REF_FROM_ADDR(addr) (*(void **)(addr)) +#endif + +/* For STORE opcodes */ +#define STORE_I64(addr, value) \ + do { \ + uintptr_t addr_ = (uintptr_t)(addr); \ + union { \ + int64 val; \ + uint32 u32[2]; \ + uint16 u16[4]; \ + uint8 u8[8]; \ + } u; \ + if ((addr_ & (uintptr_t)7) == 0) \ + *(int64 *)(addr) = (int64)(value); \ + else { \ + u.val = (int64)(value); \ + if ((addr_ & (uintptr_t)3) == 0) { \ + ((uint32 *)(addr))[0] = u.u32[0]; \ + ((uint32 *)(addr))[1] = u.u32[1]; \ + } \ + else if ((addr_ & (uintptr_t)1) == 0) { \ + ((uint16 *)(addr))[0] = u.u16[0]; \ + ((uint16 *)(addr))[1] = u.u16[1]; \ + ((uint16 *)(addr))[2] = u.u16[2]; \ + ((uint16 *)(addr))[3] = u.u16[3]; \ + } \ + else { \ + int32 t; \ + for (t = 0; t < 8; t++) \ + ((uint8 *)(addr))[t] = u.u8[t]; \ + } \ + } \ + } while (0) + +static inline void +STORE_U32(void *addr, uint32_t value) +{ + uintptr_t addr_ = (uintptr_t)(addr); + union { + uint32_t val; + uint16_t u16[2]; + uint8_t u8[4]; + } u; + if ((addr_ & (uintptr_t)3) == 0) + *(uint32_t *)(addr) = (uint32_t)(value); + else { + u.val = (uint32_t)(value); + if ((addr_ & (uintptr_t)1) == 0) { + ((uint16_t *)(addr))[0] = u.u16[0]; + ((uint16_t *)(addr))[1] = u.u16[1]; + } + else { + ((uint8_t *)(addr))[0] = u.u8[0]; + ((uint8_t *)(addr))[1] = u.u8[1]; + ((uint8_t *)(addr))[2] = u.u8[2]; + ((uint8_t *)(addr))[3] = u.u8[3]; + } + } +} + +static inline void +STORE_U8(void *addr, uint8_t value) +{ + *(uint8 *)addr = value; +} + +static inline void +STORE_U16(void *addr, uint16_t value) +{ + union { + uint16_t val; + uint8_t u8[2]; + } u; + u.val = (uint16_t)(value); + ((uint8_t *)(addr))[0] = u.u8[0]; + ((uint8_t *)(addr))[1] = u.u8[1]; +} + +static inline void +STORE_V128(void *addr, V128 value) +{ + uintptr_t addr_ = (uintptr_t)(addr); + union { + V128 val; + uint64 u64[2]; + uint32 u32[4]; + uint16 u16[8]; + uint8 u8[16]; + } u; + + if ((addr_ & (uintptr_t)15) == 0) { + *(V128 *)addr = value; + } + else if ((addr_ & (uintptr_t)7) == 0) { + u.val = value; + ((uint64 *)(addr))[0] = u.u64[0]; + ((uint64 *)(addr))[1] = u.u64[1]; + } + else if ((addr_ & (uintptr_t)3) == 0) { + u.val = value; + ((uint32 *)addr)[0] = u.u32[0]; + ((uint32 *)addr)[1] = u.u32[1]; + ((uint32 *)addr)[2] = u.u32[2]; + ((uint32 *)addr)[3] = u.u32[3]; + } + else if ((addr_ & (uintptr_t)1) == 0) { + u.val = value; + ((uint16 *)addr)[0] = u.u16[0]; + ((uint16 *)addr)[1] = u.u16[1]; + ((uint16 *)addr)[2] = u.u16[2]; + ((uint16 *)addr)[3] = u.u16[3]; + ((uint16 *)addr)[4] = u.u16[4]; + ((uint16 *)addr)[5] = u.u16[5]; + ((uint16 *)addr)[6] = u.u16[6]; + ((uint16 *)addr)[7] = u.u16[7]; + } + else { + u.val = value; + for (int i = 0; i < 16; i++) + ((uint8 *)addr)[i] = u.u8[i]; + } +} + +/* For LOAD opcodes */ +static inline V128 +LOAD_V128(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + V128 val; + uint64 u64[2]; + uint32 u32[4]; + uint16 u16[8]; + uint8 u8[16]; + } u; + if ((addr1 & (uintptr_t)15) == 0) + return *(V128 *)addr; + + if ((addr1 & (uintptr_t)7) == 0) { + u.u64[0] = ((uint64 *)addr)[0]; + u.u64[1] = ((uint64 *)addr)[1]; + } + else if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32 *)addr)[0]; + u.u32[1] = ((uint32 *)addr)[1]; + u.u32[2] = ((uint32 *)addr)[2]; + u.u32[3] = ((uint32 *)addr)[3]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + u.u16[2] = ((uint16 *)addr)[2]; + u.u16[3] = ((uint16 *)addr)[3]; + u.u16[4] = ((uint16 *)addr)[4]; + u.u16[5] = ((uint16 *)addr)[5]; + u.u16[6] = ((uint16 *)addr)[6]; + u.u16[7] = ((uint16 *)addr)[7]; + } + else { + for (int i = 0; i < 16; i++) + u.u8[i] = ((uint8 *)addr)[i]; + } + return u.val; +} + +static inline int64 +LOAD_I64(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + int64 val; + uint32 u32[2]; + uint16 u16[4]; + uint8 u8[8]; + } u; + if ((addr1 & (uintptr_t)7) == 0) + return *(int64 *)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32 *)addr)[0]; + u.u32[1] = ((uint32 *)addr)[1]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + u.u16[2] = ((uint16 *)addr)[2]; + u.u16[3] = ((uint16 *)addr)[3]; + } + else { + int32 t; + for (t = 0; t < 8; t++) + u.u8[t] = ((uint8 *)addr)[t]; + } + return u.val; +} -/* Package Type */ -typedef enum { - Wasm_Module_Bytecode = 0, - Wasm_Module_AoT, - Package_Type_Unknown = 0xFFFF -} PackageType; +static inline float64 +LOAD_F64(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + float64 val; + uint32 u32[2]; + uint16 u16[4]; + uint8 u8[8]; + } u; + if ((addr1 & (uintptr_t)7) == 0) + return *(float64 *)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32 *)addr)[0]; + u.u32[1] = ((uint32 *)addr)[1]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + u.u16[2] = ((uint16 *)addr)[2]; + u.u16[3] = ((uint16 *)addr)[3]; + } + else { + int32 t; + for (t = 0; t < 8; t++) + u.u8[t] = ((uint8 *)addr)[t]; + } + return u.val; +} + +static inline int32 +LOAD_I32(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + int32 val; + uint16 u16[2]; + uint8 u8[4]; + } u; + if ((addr1 & (uintptr_t)3) == 0) + return *(int32 *)addr; + + if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + } + else { + u.u8[0] = ((uint8 *)addr)[0]; + u.u8[1] = ((uint8 *)addr)[1]; + u.u8[2] = ((uint8 *)addr)[2]; + u.u8[3] = ((uint8 *)addr)[3]; + } + return u.val; +} + +static inline int16 +LOAD_I16(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + int16 val; + uint8 u8[2]; + } u; + if ((addr1 & (uintptr_t)1)) { + u.u8[0] = ((uint8 *)addr)[0]; + u.u8[1] = ((uint8 *)addr)[1]; + return u.val; + } + return *(int16 *)addr; +} + +#define LOAD_U32(addr) ((uint32)LOAD_I32(addr)) +#define LOAD_U16(addr) ((uint16)LOAD_I16(addr)) + +#if UINTPTR_MAX == UINT32_MAX +#define STORE_PTR(addr, ptr) STORE_U32(addr, (uintptr_t)ptr) +#elif UINTPTR_MAX == UINT64_MAX +#define STORE_PTR(addr, ptr) STORE_I64(addr, (uintptr_t)ptr) +#endif + +#endif /* WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 */ + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#define SHARED_MEMORY_LOCK(memory) shared_memory_lock(memory) +#define SHARED_MEMORY_UNLOCK(memory) shared_memory_unlock(memory) +#else +#define SHARED_MEMORY_LOCK(memory) (void)0 +#define SHARED_MEMORY_UNLOCK(memory) (void)0 +#endif + +#define CLAMP_U64_TO_U32(value) \ + ((value) > UINT32_MAX ? UINT32_MAX : (uint32)(value)) typedef struct WASMModuleCommon { /* Module type, for module loaded from WASM bytecode binary, @@ -38,6 +492,11 @@ typedef struct WASMModuleCommon { Wasm_Module_AoT, and this structure should be treated as AOTModule structure. */ uint32 module_type; + + /* The following uint8[1] member is a dummy just to indicate + some module_type dependent members follow. + Typically, it should be accessed by casting to the corresponding + actual module_type dependent structure, not via this member. */ uint8 module_data[1]; } WASMModuleCommon; @@ -49,216 +508,673 @@ typedef struct WASMModuleInstanceCommon { Wasm_Module_AoT, and this structure should be treated as AOTModuleInstance structure. */ uint32 module_type; + + /* The following uint8[1] member is a dummy just to indicate + some module_type dependent members follow. + Typically, it should be accessed by casting to the corresponding + actual module_type dependent structure, not via this member. */ uint8 module_inst_data[1]; } WASMModuleInstanceCommon; -typedef void WASMFunctionInstanceCommon; +typedef struct WASMModuleMemConsumption { + uint32 total_size; + uint32 module_struct_size; + uint32 types_size; + uint32 imports_size; + uint32 functions_size; + uint32 tables_size; + uint32 memories_size; + uint32 globals_size; + uint32 exports_size; + uint32 table_segs_size; + uint32 data_segs_size; + uint32 const_strs_size; +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + uint32 custom_sections_size; +#endif +#if WASM_ENABLE_AOT != 0 + uint32 aot_code_size; +#endif +} WASMModuleMemConsumption; -/* WASM section */ -typedef struct WASMSection { - struct WASMSection *next; - /* section type */ - int section_type; - /* section body, not include type and size */ - const uint8 *section_body; - /* section body size */ - uint32 section_body_size; -} WASMSection, AOTSection; +typedef struct WASMModuleInstMemConsumption { + uint64 total_size; + uint32 module_inst_struct_size; + uint32 app_heap_size; + uint64 memories_size; + uint32 tables_size; + uint32 globals_size; + uint32 functions_size; + uint32 exports_size; +} WASMModuleInstMemConsumption; #if WASM_ENABLE_LIBC_WASI != 0 +#if WASM_ENABLE_UVWASI == 0 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; } WASIContext; +#else +typedef struct WASIContext { + uvwasi_t uvwasi; + uint32_t exit_code; +} WASIContext; +#endif #endif +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMRegisteredModule { + bh_list_link l; + /* point to a string pool */ + const char *module_name; + WASMModuleCommon *module; + /* to store the original module file buffer address */ + uint8 *orig_file_buf; + uint32 orig_file_buf_size; +} WASMRegisteredModule; +#endif + +typedef package_type_t PackageType; +typedef wasm_section_t WASMSection, AOTSection; + +#if WASM_ENABLE_JIT != 0 +typedef struct LLVMJITOptions { + uint32 opt_level; + uint32 size_level; + uint32 segue_flags; + bool quick_invoke_c_api_import; +} LLVMJITOptions; +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +/* Signal info passing to interp/aot signal handler */ +typedef struct WASMSignalInfo { + WASMExecEnv *exec_env_tls; +#ifndef BH_PLATFORM_WINDOWS + void *sig_addr; +#else + EXCEPTION_POINTERS *exce_info; +#endif +} WASMSignalInfo; + +/* Set exec_env of thread local storage */ +void +wasm_runtime_set_exec_env_tls(WASMExecEnv *exec_env); + +/* Get exec_env of thread local storage */ +WASMExecEnv * +wasm_runtime_get_exec_env_tls(void); +#endif + +struct InstantiationArgs2 { + InstantiationArgs v1; +#if WASM_ENABLE_LIBC_WASI != 0 + WASIArguments wasi; +#endif +}; + +void +wasm_runtime_instantiation_args_set_defaults(struct InstantiationArgs2 *args); + /* See wasm_export.h for description */ -bool -wasm_runtime_init(); +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 + +#if WASM_ENABLE_GC != 0 +/* Internal API */ +uint32 +wasm_runtime_get_gc_heap_size_default(void); +#endif /* See wasm_export.h for description */ -void -wasm_runtime_destroy(); +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 */ -PackageType +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); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN PackageType get_package_type(const uint8 *buf, uint32 size); /* See wasm_export.h for description */ -WASMModuleCommon * -wasm_runtime_load(const uint8 *buf, uint32 size, - char *error_buf, uint32 error_buf_size); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_xip_file(const uint8 *buf, uint32 size); /* See wasm_export.h for description */ -WASMModuleCommon * +WASM_RUNTIME_API_EXTERN WASMModuleCommon * +wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf, + uint32 error_buf_size); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN WASMModuleCommon * wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, - char *error_buf, uint32_t error_buf_size); + char *error_buf, uint32 error_buf_size); /* See wasm_export.h for description */ -void +WASM_RUNTIME_API_EXTERN void wasm_runtime_unload(WASMModuleCommon *module); -/* See wasm_export.h for description */ +/* Internal API */ +uint32 +wasm_runtime_get_max_mem(uint32 max_memory_pages, uint32 module_init_page_count, + uint32 module_max_page_count); + +/* Internal API */ WASMModuleInstanceCommon * -wasm_runtime_instantiate(WASMModuleCommon *module, - uint32 stack_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size); +wasm_runtime_instantiate_internal(WASMModuleCommon *module, + WASMModuleInstanceCommon *parent, + WASMExecEnv *exec_env_main, + const struct InstantiationArgs2 *args, + char *error_buf, uint32 error_buf_size); + +/* Internal API */ +void +wasm_runtime_deinstantiate_internal(WASMModuleInstanceCommon *module_inst, + bool is_sub_inst); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon * +wasm_runtime_instantiate(WASMModuleCommon *module, uint32 default_stack_size, + uint32 host_managed_heap_size, char *error_buf, + uint32 error_buf_size); /* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon * +wasm_runtime_instantiate_ex(WASMModuleCommon *module, + const InstantiationArgs *args, char *error_buf, + uint32 error_buf_size); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN +bool +wasm_runtime_instantiation_args_create(struct InstantiationArgs2 **p); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN +void +wasm_runtime_instantiation_args_destroy(struct InstantiationArgs2 *p); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN +void +wasm_runtime_instantiation_args_set_default_stack_size( + struct InstantiationArgs2 *p, uint32 v); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_host_managed_heap_size( + struct InstantiationArgs2 *p, uint32 v); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN +void +wasm_runtime_instantiation_args_set_max_memory_pages( + struct InstantiationArgs2 *p, uint32 v); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_arg(struct InstantiationArgs2 *p, + char *argv[], int argc); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_env(struct InstantiationArgs2 *p, + const char *env[], + uint32 env_count); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_dir(struct InstantiationArgs2 *p, + const char *dir_list[], + uint32 dir_count, + const char *map_dir_list[], + uint32 map_dir_count); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_stdio(struct InstantiationArgs2 *p, + int64 stdinfd, int64 stdoutfd, + int64 stderrfd); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_addr_pool(struct InstantiationArgs2 *p, + const char *addr_pool[], + uint32 addr_pool_size); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_ns_lookup_pool( + struct InstantiationArgs2 *p, const char *ns_lookup_pool[], + uint32 ns_lookup_pool_size); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon * +wasm_runtime_instantiate_ex2(WASMModuleCommon *module, + const struct InstantiationArgs2 *args, + 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); /* See wasm_export.h for description */ -WASMFunctionInstanceCommon * -wasm_runtime_lookup_function(const WASMModuleInstanceCommon *module_inst, - const char *name, const char *signature); +WASM_RUNTIME_API_EXTERN WASMModuleCommon * +wasm_runtime_get_module(WASMModuleInstanceCommon *module_inst); /* See wasm_export.h for description */ -WASMExecEnv * +WASM_RUNTIME_API_EXTERN WASMFunctionInstanceCommon * +wasm_runtime_lookup_function(WASMModuleInstanceCommon *const module_inst, + const char *name); + +/* Internal API */ +WASMFuncType * +wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, + uint32 module_type); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN uint32 +wasm_func_get_param_count(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN uint32 +wasm_func_get_result_count(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_func_get_param_types(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst, + wasm_valkind_t *param_types); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_func_get_result_types(WASMFunctionInstanceCommon *const func_inst, + WASMModuleInstanceCommon *const module_inst, + wasm_valkind_t *result_types); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN WASMExecEnv * wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, uint32 stack_size); /* See wasm_export.h for description */ -void +WASM_RUNTIME_API_EXTERN void wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env); +#if WASM_ENABLE_COPY_CALL_STACK != 0 +WASM_RUNTIME_API_EXTERN uint32_t +wasm_copy_callstack(const wasm_exec_env_t exec_env, WASMCApiFrame *buffer, + const uint32 length, const uint32 skip_n, char *error_buf, + uint32 error_buf_size); +#endif // WASM_ENABLE_COPY_CALL_STACK + /* See wasm_export.h for description */ -WASMModuleInstanceCommon * +WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon * wasm_runtime_get_module_inst(WASMExecEnv *exec_env); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_module_inst(WASMExecEnv *exec_env, + WASMModuleInstanceCommon *const module_inst); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_function_attachment(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_user_data(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env, + uint8 *native_stack_boundary); + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, + int instructions_to_execute); +#endif + +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN +void +wasm_runtime_set_bounds_checks(WASMModuleInstanceCommon *module_inst, + bool enable); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_bounds_checks_enabled(WASMModuleInstanceCommon *module_inst); +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +/* Access exception check guard page to trigger the signal handler */ +void +wasm_runtime_access_exce_check_guard_page(); +#endif + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool wasm_runtime_call_wasm(WASMExecEnv *exec_env, - WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]); + WASMFunctionInstanceCommon *function, uint32 argc, + uint32 argv[]); -bool -wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, - WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t *results, + uint32 num_args, wasm_val_t *args); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t *results, + uint32 num_args, ...); /* See wasm_export.h for description */ -bool -wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, - int argc, char *argv[]); +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 +wasm_runtime_start_debug_instance_with_port(WASMExecEnv *exec_env, + int32_t port); /* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN uint32 +wasm_runtime_start_debug_instance(WASMExecEnv *exec_env); +#endif + bool +wasm_runtime_create_exec_env_singleton(WASMModuleInstanceCommon *module_inst); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN WASMExecEnv * +wasm_runtime_get_exec_env_singleton(WASMModuleInstanceCommon *module_inst); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, int32 argc, + char *argv[]); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, - const char *name, int argc, char *argv[]); + const char *name, int32 argc, char *argv[]); /* See wasm_export.h for description */ -void +WASM_RUNTIME_API_EXTERN void wasm_runtime_set_exception(WASMModuleInstanceCommon *module, const char *exception); /* See wasm_export.h for description */ -const char * +WASM_RUNTIME_API_EXTERN const char * wasm_runtime_get_exception(WASMModuleInstanceCommon *module); /* See wasm_export.h for description */ -void +WASM_RUNTIME_API_EXTERN void wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst); /* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_terminate(WASMModuleInstanceCommon *module); + +/* Internal API */ void +wasm_runtime_set_custom_data_internal(WASMModuleInstanceCommon *module_inst, + void *custom_data); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void wasm_runtime_set_custom_data(WASMModuleInstanceCommon *module_inst, void *custom_data); /* See wasm_export.h for description */ -void * +WASM_RUNTIME_API_EXTERN void * wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst); +/* Internal API */ +uint64 +wasm_runtime_module_malloc_internal(WASMModuleInstanceCommon *module_inst, + WASMExecEnv *exec_env, uint64 size, + void **p_native_addr); + +/* Internal API */ +uint64 +wasm_runtime_module_realloc_internal(WASMModuleInstanceCommon *module_inst, + WASMExecEnv *exec_env, uint64 ptr, + uint64 size, void **p_native_addr); + +/* Internal API */ +void +wasm_runtime_module_free_internal(WASMModuleInstanceCommon *module_inst, + WASMExecEnv *exec_env, uint64 ptr); + /* See wasm_export.h for description */ -int32 -wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size); +WASM_RUNTIME_API_EXTERN uint64 +wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint64 size, + void **p_native_addr); /* See wasm_export.h for description */ -void -wasm_runtime_module_free(WASMModuleInstanceCommon *module_inst, int32 ptr); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_module_free(WASMModuleInstanceCommon *module_inst, uint64 ptr); /* See wasm_export.h for description */ -int32 +WASM_RUNTIME_API_EXTERN uint64 wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, - const char *src, uint32 size); + const char *src, uint64 size); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN bool wasm_runtime_validate_app_addr(WASMModuleInstanceCommon *module_inst, - int32 app_offset, uint32 size); + uint64 app_offset, uint64 size); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN bool wasm_runtime_validate_app_str_addr(WASMModuleInstanceCommon *module_inst, - int32 app_str_offset); + uint64 app_str_offset); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN bool wasm_runtime_validate_native_addr(WASMModuleInstanceCommon *module_inst, - void *native_ptr, uint32 size); + void *native_ptr, uint64 size); /* See wasm_export.h for description */ -void * +WASM_RUNTIME_API_EXTERN void * wasm_runtime_addr_app_to_native(WASMModuleInstanceCommon *module_inst, - int32 app_offset); + uint64 app_offset); /* See wasm_export.h for description */ -int32 +WASM_RUNTIME_API_EXTERN uint64 wasm_runtime_addr_native_to_app(WASMModuleInstanceCommon *module_inst, void *native_ptr); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN bool wasm_runtime_get_app_addr_range(WASMModuleInstanceCommon *module_inst, - int32 app_offset, - int32 *p_app_start_offset, - int32 *p_app_end_offset); + uint64 app_offset, uint64 *p_app_start_offset, + uint64 *p_app_end_offset); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN bool wasm_runtime_get_native_addr_range(WASMModuleInstanceCommon *module_inst, uint8 *native_ptr, uint8 **p_native_start_addr, uint8 **p_native_end_addr); -uint32 -wasm_runtime_get_temp_ret(WASMModuleInstanceCommon *module_inst); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN const uint8 * +wasm_runtime_get_custom_section(WASMModuleCommon *const module_comm, + const char *name, uint32 *len); + +#if WASM_ENABLE_MULTI_MODULE != 0 +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); + +module_reader +wasm_runtime_get_module_reader(void); + +module_destroyer +wasm_runtime_get_module_destroyer(void); + +bool +wasm_runtime_register_module_internal(const char *module_name, + WASMModuleCommon *module, + uint8 *orig_file_buf, + uint32 orig_file_buf_size, + char *error_buf, uint32 error_buf_size); void -wasm_runtime_set_temp_ret(WASMModuleInstanceCommon *module_inst, - uint32 temp_ret); +wasm_runtime_unregister_module(const WASMModuleCommon *module); -uint32 -wasm_runtime_get_llvm_stack(WASMModuleInstanceCommon *module_inst); +WASMModuleCommon * +wasm_runtime_find_module_registered(const char *module_name); + +bool +wasm_runtime_add_loading_module(const char *module_name, char *error_buf, + uint32 error_buf_size); + +void +wasm_runtime_delete_loading_module(const char *module_name); + +bool +wasm_runtime_is_loading_module(const char *module_name); void -wasm_runtime_set_llvm_stack(WASMModuleInstanceCommon *module_inst, - uint32 llvm_stack); +wasm_runtime_destroy_loading_module_list(void); + +WASMModuleCommon * +wasm_runtime_search_sub_module(const WASMModuleCommon *parent_module, + const char *sub_module_name); + +bool +wasm_runtime_register_sub_module(const WASMModuleCommon *parent_module, + const char *sub_module_name, + WASMModuleCommon *sub_module); + +WASMModuleCommon * +wasm_runtime_load_depended_module(const WASMModuleCommon *parent_module, + const char *sub_module_name, char *error_buf, + uint32 error_buf_size); + +bool +wasm_runtime_sub_module_instantiate(WASMModuleCommon *module, + WASMModuleInstanceCommon *module_inst, + const struct InstantiationArgs2 *args, + char *error_buf, uint32 error_buf_size); +void +wasm_runtime_sub_module_deinstantiate(WASMModuleInstanceCommon *module_inst); +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 || WASM_ENABLE_MULTI_MODULE != 0 +WASMExport * +loader_find_export(const WASMModuleCommon *module, const char *module_name, + const char *field_name, uint8 export_kind, char *error_buf, + uint32 error_buf_size); +#endif /* WASM_ENABLE_MULTI_MODULE */ + +bool +wasm_runtime_is_built_in_module(const char *module_name); + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_exec_env_get_aux_stack(WASMExecEnv *exec_env, uint64 *start_offset, + uint32 *size); + +bool +wasm_exec_env_set_aux_stack(WASMExecEnv *exec_env, uint64 start_offset, + uint32 size); +#endif #if WASM_ENABLE_LIBC_WASI != 0 +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_args_ex(WASMModuleCommon *module, const char *dir_list[], + uint32 dir_count, const char *map_dir_list[], + uint32 map_dir_count, const char *env_list[], + uint32 env_count, char *argv[], int argc, + int64 stdinfd, int64 stdoutfd, int64 stderrfd); + /* See wasm_export.h for description */ -void -wasm_runtime_set_wasi_args(WASMModuleCommon *module, - const char *dir_list[], uint32 dir_count, - const char *map_dir_list[], uint32 map_dir_count, - const char *env_list[], uint32 env_count, - const char *argv[], uint32 argc); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_args(WASMModuleCommon *module, const char *dir_list[], + uint32 dir_count, const char *map_dir_list[], + uint32 map_dir_count, const char *env_list[], + uint32 env_count, char *argv[], int argc); /* See wasm_export.h for description */ -bool +WASM_RUNTIME_API_EXTERN bool wasm_runtime_is_wasi_mode(WASMModuleInstanceCommon *module_inst); /* See wasm_export.h for description */ -WASMFunctionInstanceCommon * +WASM_RUNTIME_API_EXTERN WASMFunctionInstanceCommon * wasm_runtime_lookup_wasi_start_function(WASMModuleInstanceCommon *module_inst); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_wasi_exit_code(WASMModuleInstanceCommon *module_inst); + +void +wasi_args_set_defaults(WASIArguments *args); + bool wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, const char *dir_list[], uint32 dir_count, const char *map_dir_list[], uint32 map_dir_count, const char *env[], uint32 env_count, - const char *argv[], uint32 argc, + const char *addr_pool[], uint32 addr_pool_size, + const char *ns_lookup_pool[], uint32 ns_lookup_pool_size, + char *argv[], uint32 argc, os_raw_file_handle stdinfd, + os_raw_file_handle stdoutfd, os_raw_file_handle stderrfd, char *error_buf, uint32 error_buf_size); void @@ -270,27 +1186,255 @@ wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst, WASIContext * wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], + uint32 addr_pool_size); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_ns_lookup_pool(wasm_module_t module, + const char *ns_lookup_pool[], + uint32 ns_lookup_pool_size); #endif /* end of WASM_ENABLE_LIBC_WASI */ +#if WASM_ENABLE_GC != 0 +void +wasm_runtime_set_gc_heap_handle(WASMModuleInstanceCommon *module_inst, + void *gc_heap_handle); + +void * +wasm_runtime_get_gc_heap_handle(WASMModuleInstanceCommon *module_inst); +#endif + +#if WASM_ENABLE_REF_TYPES != 0 +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_obj2ref(WASMModuleInstanceCommon *module_inst, void *extern_obj, + uint32 *p_externref_idx); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_ref2obj(uint32 externref_idx, void **p_extern_obj); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_retain(uint32 externref_idx); + +/** + * Reclaim the externref objects/indexes which are not used by + * module instance + */ +void +wasm_externref_reclaim(WASMModuleInstanceCommon *module_inst); + /** - * Enlarge wasm memory data space. + * Cleanup the externref objects/indexes of the module instance + */ +void +wasm_externref_cleanup(WASMModuleInstanceCommon *module_inst); +#endif /* end of WASM_ENABLE_REF_TYPES */ + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +/** + * @brief Internal implementation for dumping or printing callstack line + * + * @note if dump_or_print is true, then print to stdout directly; + * if dump_or_print is false, but *buf is NULL, then return the length of the + * line; + * if dump_or_print is false, and *buf is not NULL, then dump content to + * the memory pointed by *buf, and adjust *buf and *len according to actual + * bytes dumped, and return the actual dumped length * - * @param module the wasm module instance - * @param inc_page_count denote the page number to increase - * @return return true if enlarge successfully, false otherwise + * @param line_buf current line to dump or print + * @param dump_or_print whether to print to stdout or dump to buf + * @param buf [INOUT] pointer to the buffer + * @param len [INOUT] pointer to remaining length + * @return bytes printed to stdout or dumped to buf */ +uint32 +wasm_runtime_dump_line_buf_impl(const char *line_buf, bool dump_or_print, + char **buf, uint32 *len); +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK != 0 */ + +/* Get module of the current exec_env */ +WASMModuleCommon * +wasm_exec_env_get_module(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_unregister_natives(const char *module_name, + NativeSymbol *native_symbols); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_create_context_key(void (*dtor)(WASMModuleInstanceCommon *inst, + void *ctx)); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_destroy_context_key(void *key); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_context(WASMModuleInstanceCommon *inst, void *key, void *ctx); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_context_spread(WASMModuleInstanceCommon *inst, void *key, + void *ctx); +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_context(WASMModuleInstanceCommon *inst, void *key); + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, const char *signature, + void *attachment, uint32 *argv, uint32 argc, + uint32 *ret); + +bool +wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, + const WASMFuncType *func_type, + const char *signature, void *attachment, + uint32 *argv, uint32 argc, uint32 *ret); + +void +wasm_runtime_read_v128(const uint8 *bytes, uint64 *ret1, uint64 *ret2); + +void +wasm_runtime_dump_module_mem_consumption(const WASMModuleCommon *module); + +void +wasm_runtime_dump_module_inst_mem_consumption( + const WASMModuleInstanceCommon *module_inst); + +void +wasm_runtime_dump_exec_env_mem_consumption(const WASMExecEnv *exec_env); + +bool +wasm_runtime_get_table_elem_type(const WASMModuleCommon *module_comm, + uint32 table_idx, uint8 *out_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **out_ref_type, +#endif + uint32 *out_min_size, uint32 *out_max_size); + +bool +wasm_runtime_get_table_inst_elem_type( + const WASMModuleInstanceCommon *module_inst_comm, uint32 table_idx, + uint8 *out_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **out_ref_type, +#endif + uint32 *out_min_size, uint32 *out_max_size); + +bool +wasm_runtime_get_export_func_type(const WASMModuleCommon *module_comm, + const WASMExport *export_, + WASMFuncType **out); + +bool +wasm_runtime_get_export_global_type(const WASMModuleCommon *module_comm, + const WASMExport *export_, + uint8 *out_val_type, bool *out_mutability); + +bool +wasm_runtime_get_export_memory_type(const WASMModuleCommon *module_comm, + const WASMExport *export_, + uint32 *out_min_page, uint32 *out_max_page); + +bool +wasm_runtime_get_export_table_type(const WASMModuleCommon *module_comm, + const WASMExport *export_, + uint8 *out_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **out_ref_type, +#endif + uint32 *out_min_size, uint32 *out_max_size); + +bool +wasm_runtime_invoke_c_api_native(WASMModuleInstanceCommon *module_inst, + void *func_ptr, WASMFuncType *func_type, + uint32 argc, uint32 *argv, bool with_env, + void *wasm_c_api_env); + +struct CApiFuncImport; +/* A quick version of wasm_runtime_invoke_c_api_native to directly invoke + wasm-c-api import function from jitted code to improve performance */ bool -wasm_runtime_enlarge_memory(WASMModuleInstanceCommon *module, uint32 inc_page_count); +wasm_runtime_quick_invoke_c_api_native(WASMModuleInstanceCommon *module_inst, + struct CApiFuncImport *c_api_import, + wasm_val_t *params, uint32 param_count, + wasm_val_t *results, + uint32 result_count); +void +wasm_runtime_show_app_heap_corrupted_prompt(void); + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +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); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_begin_blocking_op(WASMExecEnv *exec_env); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_end_blocking_op(WASMExecEnv *exec_env); + +void +wasm_runtime_interrupt_blocking_op(WASMExecEnv *exec_env); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, + uint32 requested_size); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_underlying_binary_freeable(WASMModuleCommon *const module); + +#if WASM_ENABLE_LINUX_PERF != 0 +bool +wasm_runtime_get_linux_perf(void); + +void +wasm_runtime_set_linux_perf(bool flag); +#endif + +#if WASM_ENABLE_SHARED_HEAP != 0 bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret); +wasm_runtime_check_and_update_last_used_shared_heap( + WASMModuleInstanceCommon *module_inst, uintptr_t app_offset, size_t bytes, + uintptr_t *shared_heap_start_off_p, uintptr_t *shared_heap_end_off_p, + uint8 **shared_heap_base_addr_adj_p, bool is_memory64); +#endif +struct WASMModuleInstanceExtraCommon * +GetModuleInstanceExtraCommon(struct WASMModuleInstance *module_inst); #ifdef __cplusplus } #endif #endif /* end of _WASM_COMMON_H */ - diff --git a/core/iwasm/common/wasm_shared_memory.c b/core/iwasm/common/wasm_shared_memory.c new file mode 100644 index 0000000000..c1aa18f5c3 --- /dev/null +++ b/core/iwasm/common/wasm_shared_memory.c @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_log.h" +#include "wasm_shared_memory.h" +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif + +/* + * Note: this lock can be per memory. + * + * For now, just use a global because: + * - it's a bit cumbersome to extend WASMMemoryInstance w/o breaking + * the AOT ABI. + * - If you care performance, it's better to make the interpreters + * use atomic ops. + */ +korp_mutex g_shared_memory_lock; + +/* clang-format off */ +enum { + S_WAITING, + S_NOTIFIED +}; +/* clang-format on */ + +typedef struct AtomicWaitInfo { + bh_list wait_list_head; + bh_list *wait_list; + /* WARNING: insert to the list allowed only in acquire_wait_info + otherwise there will be data race as described in PR #2016 */ +} AtomicWaitInfo; + +typedef struct AtomicWaitNode { + bh_list_link l; + uint8 status; + korp_cond wait_cond; +} AtomicWaitNode; + +/* Atomic wait map */ +static HashMap *wait_map; + +static uint32 +wait_address_hash(const void *address); + +static bool +wait_address_equal(void *h1, void *h2); + +static void +destroy_wait_info(void *wait_info); + +bool +wasm_shared_memory_init() +{ + if (os_mutex_init(&g_shared_memory_lock) != 0) + 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(&g_shared_memory_lock); + return false; + } + return true; +} + +void +wasm_shared_memory_destroy() +{ + bh_hash_map_destroy(wait_map); + os_mutex_destroy(&g_shared_memory_lock); +} + +uint16 +shared_memory_inc_reference(WASMMemoryInstance *memory) +{ + bh_assert(shared_memory_is_shared(memory)); + uint16 old; +#if BH_ATOMIC_16_IS_ATOMIC == 0 + os_mutex_lock(&g_shared_memory_lock); +#endif + old = BH_ATOMIC_16_FETCH_ADD(memory->ref_count, 1); +#if BH_ATOMIC_16_IS_ATOMIC == 0 + os_mutex_unlock(&g_shared_memory_lock); +#endif + bh_assert(old >= 1); + bh_assert(old < UINT16_MAX); + return old + 1; +} + +uint16 +shared_memory_dec_reference(WASMMemoryInstance *memory) +{ + bh_assert(shared_memory_is_shared(memory)); + uint16 old; +#if BH_ATOMIC_16_IS_ATOMIC == 0 + os_mutex_lock(&g_shared_memory_lock); +#endif + old = BH_ATOMIC_16_FETCH_SUB(memory->ref_count, 1); +#if BH_ATOMIC_16_IS_ATOMIC == 0 + os_mutex_unlock(&g_shared_memory_lock); +#endif + bh_assert(old > 0); + return old - 1; +} + +static korp_mutex * +shared_memory_get_lock_pointer(WASMMemoryInstance *memory) +{ + bh_assert(memory != NULL); + return &g_shared_memory_lock; +} + +/* Atomics wait && notify APIs */ +static uint32 +wait_address_hash(const void *address) +{ + return (uint32)(uintptr_t)address; +} + +static bool +wait_address_equal(void *h1, void *h2) +{ + return h1 == h2 ? true : false; +} + +static bool +is_wait_node_exists(bh_list *wait_list, AtomicWaitNode *node) +{ + AtomicWaitNode *curr; + curr = bh_list_first_elem(wait_list); + + while (curr) { + if (curr == node) { + return true; + } + curr = bh_list_elem_next(curr); + } + + return false; +} + +static uint32 +notify_wait_list(bh_list *wait_list, uint32 count) +{ + AtomicWaitNode *node, *next; + uint32 i, notify_count = count; + + if (count > wait_list->len) + notify_count = wait_list->len; + + node = bh_list_first_elem(wait_list); + if (!node) + return 0; + + for (i = 0; i < notify_count; i++) { + bh_assert(node); + next = bh_list_elem_next(node); + + node->status = S_NOTIFIED; + /* wakeup */ + os_cond_signal(&node->wait_cond); + + node = next; + } + + return notify_count; +} + +static AtomicWaitInfo * +acquire_wait_info(void *address, AtomicWaitNode *wait_node) +{ + AtomicWaitInfo *wait_info = NULL; + bh_list_status ret; + + bh_assert(address != NULL); + + wait_info = (AtomicWaitInfo *)bh_hash_map_find(wait_map, address); + + if (!wait_node) { + return wait_info; + } + + /* No wait info on this address, create new info */ + if (!wait_info) { + if (!(wait_info = (AtomicWaitInfo *)wasm_runtime_malloc( + sizeof(AtomicWaitInfo)))) { + return NULL; + } + memset(wait_info, 0, sizeof(AtomicWaitInfo)); + + /* init wait list */ + wait_info->wait_list = &wait_info->wait_list_head; + ret = bh_list_init(wait_info->wait_list); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; + + if (!bh_hash_map_insert(wait_map, address, (void *)wait_info)) { + wasm_runtime_free(wait_info); + return NULL; + } + } + + ret = bh_list_insert(wait_info->wait_list, wait_node); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; + + return wait_info; +} + +static void +destroy_wait_info(void *wait_info) +{ + AtomicWaitNode *node, *next; + + if (wait_info) { + + node = bh_list_first_elem(((AtomicWaitInfo *)wait_info)->wait_list); + + while (node) { + next = bh_list_elem_next(node); + os_cond_destroy(&node->wait_cond); + wasm_runtime_free(node); + node = next; + } + + wasm_runtime_free(wait_info); + } +} + +static void +map_try_release_wait_info(HashMap *wait_hash_map, AtomicWaitInfo *wait_info, + void *address) +{ + if (wait_info->wait_list->len > 0) { + return; + } + + bh_hash_map_remove(wait_hash_map, address, NULL, NULL); + destroy_wait_info(wait_info); +} + +#if WASM_ENABLE_SHARED_HEAP != 0 +static bool +is_native_addr_in_shared_heap(WASMModuleInstanceCommon *module_inst, + uint8 *addr, uint64 bytes) +{ + WASMSharedHeap *shared_heap = NULL; + + if (bytes > APP_HEAP_SIZE_MAX) { + return false; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + shared_heap = ((WASMModuleInstance *)module_inst)->e->shared_heap; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + shared_heap = e->shared_heap; + } +#endif + + return shared_heap && addr >= shared_heap->base_addr + && addr + bytes <= shared_heap->base_addr + shared_heap->size; +} +#endif + +uint32 +wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, + uint64 expect, int64 timeout, bool wait64) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module; + AtomicWaitInfo *wait_info; + AtomicWaitNode *wait_node; + korp_mutex *lock; +#if WASM_ENABLE_THREAD_MGR != 0 + WASMExecEnv *exec_env; +#endif + uint64 timeout_left, timeout_wait, timeout_1sec; + bool check_ret, is_timeout, no_wait; + + 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 (!shared_memory_is_shared(module_inst->memories[0])) { + wasm_runtime_set_exception(module, "expected shared memory"); + return -1; + } + + shared_memory_lock(module_inst->memories[0]); + if ( +#if WASM_ENABLE_SHARED_HEAP != 0 + /* not in shared heap */ + !is_native_addr_in_shared_heap((WASMModuleInstanceCommon *)module_inst, + address, wait64 ? 8 : 4) + && +#endif + /* and not in linear memory */ + ((uint8 *)address < module_inst->memories[0]->memory_data + || (uint8 *)address + (wait64 ? 8 : 4) + > module_inst->memories[0]->memory_data_end)) { + shared_memory_unlock(module_inst->memories[0]); + wasm_runtime_set_exception(module, "out of bounds memory access"); + return -1; + } + shared_memory_unlock(module_inst->memories[0]); + +#if WASM_ENABLE_THREAD_MGR != 0 + exec_env = + wasm_clusters_search_exec_env((WASMModuleInstanceCommon *)module_inst); + bh_assert(exec_env); +#endif + + lock = shared_memory_get_lock_pointer(module_inst->memories[0]); + + /* Lock the shared_mem_lock for the whole atomic wait process, + and use it to os_cond_reltimedwait */ + os_mutex_lock(lock); + + no_wait = (!wait64 && *(uint32 *)address != (uint32)expect) + || (wait64 && *(uint64 *)address != expect); + + if (no_wait) { + os_mutex_unlock(lock); + return 1; + } + + if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) { + os_mutex_unlock(lock); + wasm_runtime_set_exception(module, "failed to create wait node"); + return -1; + } + memset(wait_node, 0, sizeof(AtomicWaitNode)); + + if (0 != os_cond_init(&wait_node->wait_cond)) { + os_mutex_unlock(lock); + wasm_runtime_free(wait_node); + wasm_runtime_set_exception(module, "failed to init wait cond"); + return -1; + } + + wait_node->status = S_WAITING; + + /* Acquire the wait info, create new one if not exists */ + wait_info = acquire_wait_info(address, wait_node); + + if (!wait_info) { + os_mutex_unlock(lock); + os_cond_destroy(&wait_node->wait_cond); + wasm_runtime_free(wait_node); + wasm_runtime_set_exception(module, "failed to acquire wait_info"); + return -1; + } + + /* unit of timeout is nsec, convert it to usec */ + timeout_left = (uint64)timeout / 1000; + timeout_1sec = (uint64)1e6; + + while (1) { + if (timeout < 0) { + /* wait forever until it is notified or terminated + here we keep waiting and checking every second */ + os_cond_reltimedwait(&wait_node->wait_cond, lock, + (uint64)timeout_1sec); + if (wait_node->status == S_NOTIFIED /* notified by atomic.notify */ +#if WASM_ENABLE_THREAD_MGR != 0 + /* terminated by other thread */ + || wasm_cluster_is_thread_terminated(exec_env) +#endif + ) { + break; + } + } + else { + timeout_wait = + timeout_left < timeout_1sec ? timeout_left : timeout_1sec; + os_cond_reltimedwait(&wait_node->wait_cond, lock, timeout_wait); + if (wait_node->status == S_NOTIFIED /* notified by atomic.notify */ + || timeout_left <= timeout_wait /* time out */ +#if WASM_ENABLE_THREAD_MGR != 0 + /* terminated by other thread */ + || wasm_cluster_is_thread_terminated(exec_env) +#endif + ) { + break; + } + timeout_left -= timeout_wait; + } + } + + is_timeout = wait_node->status == S_WAITING ? true : false; + + check_ret = is_wait_node_exists(wait_info->wait_list, wait_node); + bh_assert(check_ret); + (void)check_ret; + + /* Remove wait node from wait list */ + bh_list_remove(wait_info->wait_list, wait_node); + os_cond_destroy(&wait_node->wait_cond); + wasm_runtime_free(wait_node); + + /* Release wait info if no wait nodes are attached */ + map_try_release_wait_info(wait_map, wait_info, address); + + os_mutex_unlock(lock); + + return is_timeout ? 2 : 0; +} + +uint32 +wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address, + uint32 count) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance *)module; + uint32 notify_result; + AtomicWaitInfo *wait_info; + korp_mutex *lock; + bool out_of_bounds; + + bh_assert(module->module_type == Wasm_Module_Bytecode + || module->module_type == Wasm_Module_AoT); + + shared_memory_lock(module_inst->memories[0]); + out_of_bounds = +#if WASM_ENABLE_SHARED_HEAP != 0 + /* not in shared heap */ + !is_native_addr_in_shared_heap(module, address, 4) && +#endif + /* and not in linear memory */ + ((uint8 *)address < module_inst->memories[0]->memory_data + || (uint8 *)address + 4 > module_inst->memories[0]->memory_data_end); + shared_memory_unlock(module_inst->memories[0]); + + if (out_of_bounds) { + wasm_runtime_set_exception(module, "out of bounds memory access"); + return -1; + } + + /* Currently we have only one memory instance */ + if (!shared_memory_is_shared(module_inst->memories[0])) { + /* Always return 0 for ushared linear memory since there is + no way to create a waiter on it */ + return 0; + } + + lock = shared_memory_get_lock_pointer(module_inst->memories[0]); + + /* Lock the shared_mem_lock for the whole atomic notify process, + and use it to os_cond_signal */ + os_mutex_lock(lock); + + wait_info = acquire_wait_info(address, NULL); + + /* Nobody wait on this address */ + if (!wait_info) { + os_mutex_unlock(lock); + return 0; + } + + /* Notify each wait node in the wait list */ + notify_result = notify_wait_list(wait_info->wait_list, count); + + os_mutex_unlock(lock); + + return notify_result; +} diff --git a/core/iwasm/common/wasm_shared_memory.h b/core/iwasm/common/wasm_shared_memory.h new file mode 100644 index 0000000000..e1c5154a53 --- /dev/null +++ b/core/iwasm/common/wasm_shared_memory.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_SHARED_MEMORY_H +#define _WASM_SHARED_MEMORY_H + +#include "bh_common.h" +#include "../interpreter/wasm_runtime.h" +#include "wasm_runtime_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern korp_mutex g_shared_memory_lock; + +bool +wasm_shared_memory_init(void); + +void +wasm_shared_memory_destroy(void); + +uint16 +shared_memory_inc_reference(WASMMemoryInstance *memory); + +uint16 +shared_memory_dec_reference(WASMMemoryInstance *memory); + +#define shared_memory_is_shared(memory) memory->is_shared_memory + +#define shared_memory_lock(memory) \ + do { \ + /* \ + * Note: exception logic is currently abusing this lock. \ + * cf. \ + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/2407 \ + */ \ + bh_assert(memory != NULL); \ + if (memory->is_shared_memory) \ + os_mutex_lock(&g_shared_memory_lock); \ + } while (0) + +#define shared_memory_unlock(memory) \ + do { \ + if (memory->is_shared_memory) \ + os_mutex_unlock(&g_shared_memory_lock); \ + } while (0) + +uint32 +wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, + uint64 expect, int64 timeout, bool wait64); + +uint32 +wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, void *address, + uint32 count); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_SHARED_MEMORY_H */ diff --git a/core/iwasm/common/wasm_suspend_flags.h b/core/iwasm/common/wasm_suspend_flags.h new file mode 100644 index 0000000000..92661b7bde --- /dev/null +++ b/core/iwasm/common/wasm_suspend_flags.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_SUSPEND_FLAGS_H +#define _WASM_SUSPEND_FLAGS_H + +#include "bh_atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Need to terminate */ +#define WASM_SUSPEND_FLAG_TERMINATE 0x1 +/* Need to suspend */ +#define WASM_SUSPEND_FLAG_SUSPEND 0x2 +/* Need to go into breakpoint */ +#define WASM_SUSPEND_FLAG_BREAKPOINT 0x4 +/* Return from pthread_exit */ +#define WASM_SUSPEND_FLAG_EXIT 0x8 +/* The thread might be blocking */ +#define WASM_SUSPEND_FLAG_BLOCKING 0x10 + +typedef union WASMSuspendFlags { + bh_atomic_32_t flags; + uintptr_t __padding__; +} WASMSuspendFlags; + +#define WASM_SUSPEND_FLAGS_IS_ATOMIC BH_ATOMIC_32_IS_ATOMIC +#define WASM_SUSPEND_FLAGS_GET(s_flags) BH_ATOMIC_32_LOAD(s_flags.flags) +#define WASM_SUSPEND_FLAGS_FETCH_OR(s_flags, val) \ + BH_ATOMIC_32_FETCH_OR(s_flags.flags, val) +#define WASM_SUSPEND_FLAGS_FETCH_AND(s_flags, val) \ + BH_ATOMIC_32_FETCH_AND(s_flags.flags, val) + +#define WASM_SUSPEND_FLAG_INHERIT_MASK (~WASM_SUSPEND_FLAG_BLOCKING) + +#if WASM_SUSPEND_FLAGS_IS_ATOMIC != 0 +#define WASM_SUSPEND_FLAGS_LOCK(lock) (void)0 +#define WASM_SUSPEND_FLAGS_UNLOCK(lock) (void)0 +#else /* else of WASM_SUSPEND_FLAGS_IS_ATOMIC */ +#define WASM_SUSPEND_FLAGS_LOCK(lock) os_mutex_lock(&lock) +#define WASM_SUSPEND_FLAGS_UNLOCK(lock) os_mutex_unlock(&lock); +#endif /* WASM_SUSPEND_FLAGS_IS_ATOMIC */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_SUSPEND_FLAGS_H */ diff --git a/core/iwasm/compilation/aot.c b/core/iwasm/compilation/aot.c index 5c2f9eb974..8e3eeec134 100644 --- a/core/iwasm/compilation/aot.c +++ b/core/iwasm/compilation/aot.c @@ -4,511 +4,933 @@ */ #include "aot.h" -#include "bh_memory.h" - static char aot_error[128]; -char* -aot_get_last_error() +char * +aot_get_last_error(void) +{ + return aot_error[0] == '\0' ? "" : aot_error; +} + +void +aot_set_last_error_v(const char *format, ...) { - return aot_error[0] == '\0' ? "" : aot_error; + va_list args; + va_start(args, format); + vsnprintf(aot_error, sizeof(aot_error), format, args); + va_end(args); } void aot_set_last_error(const char *error) { - if (error) - snprintf(aot_error, sizeof(aot_error), "Error: %s", error); - else - aot_error[0] = '\0'; + if (error) + snprintf(aot_error, sizeof(aot_error), "Error: %s", error); + else + aot_error[0] = '\0'; } static void aot_destroy_mem_init_data_list(AOTMemInitData **data_list, uint32 count) { - uint32 i; - for (i = 0; i < count; i++) - if (data_list[i]) - wasm_free(data_list[i]); - wasm_free(data_list); + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) { + if (data_list[i]->bytes) + wasm_runtime_free(data_list[i]->bytes); + wasm_runtime_free(data_list[i]); + } + wasm_runtime_free(data_list); } static AOTMemInitData ** aot_create_mem_init_data_list(const WASMModule *module) { - AOTMemInitData **data_list; - uint64 size; - uint32 i; - - /* Allocate memory */ - size = sizeof(AOTMemInitData *) * (uint64)module->data_seg_count; - if (size >= UINT32_MAX - || !(data_list = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } - - memset(data_list, 0, size); + AOTMemInitData **data_list; + uint64 size; + uint32 i; - /* Create each memory data segment */ - for (i = 0; i < module->data_seg_count; i++) { - size = offsetof(AOTMemInitData, bytes) + - (uint64)module->data_segments[i]->data_length; + /* Allocate memory */ + size = sizeof(AOTMemInitData *) * (uint64)module->data_seg_count; if (size >= UINT32_MAX - || !(data_list[i] = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - goto fail; + || !(data_list = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; } - data_list[i]->offset = module->data_segments[i]->base_offset; - data_list[i]->byte_count = module->data_segments[i]->data_length; - memcpy(data_list[i]->bytes, module->data_segments[i]->data, - module->data_segments[i]->data_length); - } + memset(data_list, 0, size); + + /* Create each memory data segment */ + for (i = 0; i < module->data_seg_count; i++) { + size = sizeof(AOTMemInitData); + if (size >= UINT32_MAX + || !(data_list[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + /* Set bulk memory specific properties if enabled */ + data_list[i]->is_passive = module->data_segments[i]->is_passive; + data_list[i]->memory_index = module->data_segments[i]->memory_index; +#endif + data_list[i]->offset = module->data_segments[i]->base_offset; + data_list[i]->byte_count = module->data_segments[i]->data_length; + data_list[i]->bytes = NULL; + /* Allocate memory for AOT compiler is OK, because the data segment + * is small and the host memory is enough */ + if (data_list[i]->byte_count > 0) { + data_list[i]->bytes = wasm_runtime_malloc(data_list[i]->byte_count); + if (!data_list[i]->bytes) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + /* Copy the actual data bytes from the WASM module */ + memcpy(data_list[i]->bytes, module->data_segments[i]->data, + module->data_segments[i]->data_length); + } + } - return data_list; + return data_list; fail: - aot_destroy_mem_init_data_list(data_list, module->data_seg_count); - return NULL; + /* Clean up allocated memory in case of failure */ + aot_destroy_mem_init_data_list(data_list, module->data_seg_count); + return NULL; } static void aot_destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count) { - uint32 i; - for (i = 0; i < count; i++) - if (data_list[i]) - wasm_free(data_list[i]); - wasm_free(data_list); + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) + wasm_runtime_free(data_list[i]); + wasm_runtime_free(data_list); } static AOTTableInitData ** aot_create_table_init_data_list(const WASMModule *module) { - AOTTableInitData **data_list; - uint64 size; - uint32 i; - - /* Allocate memory */ - size = sizeof(AOTTableInitData *) * (uint64)module->table_seg_count; - if (size >= UINT32_MAX - || !(data_list = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } - - memset(data_list, 0, size); + AOTTableInitData **data_list; + uint64 size; + uint32 i; - /* Create each table data segment */ - for (i = 0; i < module->table_seg_count; i++) { - size = offsetof(AOTTableInitData, func_indexes) + - sizeof(uint32) * (uint64)module->table_segments[i].function_count; + /* Allocate memory */ + size = sizeof(AOTTableInitData *) * (uint64)module->table_seg_count; if (size >= UINT32_MAX - || !(data_list[i] = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - goto fail; + || !(data_list = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; } - data_list[i]->offset = module->table_segments[i].base_offset; - data_list[i]->func_index_count = module->table_segments[i].function_count; - memcpy(data_list[i]->func_indexes, module->table_segments[i].func_indexes, - sizeof(uint32) * module->table_segments[i].function_count); - } + memset(data_list, 0, size); + + /* Create each table data segment */ + for (i = 0; i < module->table_seg_count; i++) { + size = offsetof(AOTTableInitData, init_values) + + sizeof(InitializerExpression) + * (uint64)module->table_segments[i].value_count; + if (size >= UINT32_MAX + || !(data_list[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + data_list[i]->offset = module->table_segments[i].base_offset; + data_list[i]->value_count = module->table_segments[i].value_count; + data_list[i]->mode = module->table_segments[i].mode; + data_list[i]->elem_type = module->table_segments[i].elem_type; + /* runtime control it */ + data_list[i]->table_index = module->table_segments[i].table_index; + bh_memcpy_s(&data_list[i]->offset, sizeof(AOTInitExpr), + &module->table_segments[i].base_offset, + sizeof(AOTInitExpr)); + data_list[i]->value_count = module->table_segments[i].value_count; +#if WASM_ENABLE_GC != 0 + data_list[i]->elem_ref_type = module->table_segments[i].elem_ref_type; +#endif + bh_memcpy_s(data_list[i]->init_values, + sizeof(InitializerExpression) + * module->table_segments[i].value_count, + module->table_segments[i].init_values, + sizeof(InitializerExpression) + * module->table_segments[i].value_count); + } - return data_list; + return data_list; fail: - aot_destroy_table_init_data_list(data_list, module->table_seg_count); - return NULL; + aot_destroy_table_init_data_list(data_list, module->table_seg_count); + return NULL; +} + +static void +get_value_type_size(uint8 value_type, bool gc_enabled, uint32 *p_size_64bit, + uint32 *p_size_32bit) +{ + uint32 size_64bit = 0, size_32bit = 0; + + if (value_type == VALUE_TYPE_I32 || value_type == VALUE_TYPE_F32) + size_64bit = size_32bit = sizeof(int32); + else if (value_type == VALUE_TYPE_I64 || value_type == VALUE_TYPE_F64) + size_64bit = size_32bit = sizeof(int64); + else if (value_type == VALUE_TYPE_V128) + size_64bit = size_32bit = sizeof(int64) * 2; + else if (!gc_enabled + && (value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF)) + size_64bit = size_32bit = sizeof(int32); + else if (gc_enabled + && ((value_type >= (uint8)REF_TYPE_ARRAYREF + && value_type <= (uint8)REF_TYPE_NULLFUNCREF) + || (value_type >= (uint8)REF_TYPE_HT_NULLABLE + && value_type <= (uint8)REF_TYPE_HT_NON_NULLABLE) +#if WASM_ENABLE_STRINGREF != 0 + || (value_type >= (uint8)REF_TYPE_STRINGVIEWWTF8 + && value_type <= (uint8)REF_TYPE_STRINGREF) + || (value_type >= (uint8)REF_TYPE_STRINGVIEWITER + && value_type <= (uint8)REF_TYPE_STRINGVIEWWTF16) +#endif + )) { + size_64bit = sizeof(uint64); + size_32bit = sizeof(uint32); + } + else if (gc_enabled && value_type == PACKED_TYPE_I8) { + size_64bit = size_32bit = sizeof(int8); + } + else if (gc_enabled && value_type == PACKED_TYPE_I16) { + size_64bit = size_32bit = sizeof(int16); + } + else { + bh_assert(0); + } + + *p_size_64bit = size_64bit; + *p_size_32bit = size_32bit; } static AOTImportGlobal * -aot_create_import_globals(const WASMModule *module, - uint32 *p_import_global_data_size) +aot_create_import_globals(const WASMModule *module, bool gc_enabled, + uint32 *p_import_global_data_size_64bit, + uint32 *p_import_global_data_size_32bit) { - AOTImportGlobal *import_globals; - uint64 size; - uint32 i, data_offset = 0; - - /* Allocate memory */ - size = sizeof(AOTImportGlobal) * (uint64)module->import_global_count; - if (size >= UINT32_MAX - || !(import_globals = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } - - memset(import_globals, 0, (uint32)size); - - /* Create each import global */ - for (i = 0; i < module->import_global_count; i++) { - WASMGlobalImport *import_global = &module->import_globals[i].u.global; - import_globals[i].module_name = import_global->module_name; - import_globals[i].global_name = import_global->field_name; - import_globals[i].type = import_global->type; - import_globals[i].is_mutable = import_global->is_mutable; - import_globals[i].global_data_linked = import_global->global_data_linked; - import_globals[i].size = wasm_value_type_size(import_global->type); - /* Calculate data offset */ - import_globals[i].data_offset = data_offset; - data_offset += wasm_value_type_size(import_global->type); - } - - *p_import_global_data_size = data_offset; - return import_globals; + AOTImportGlobal *import_globals; + uint64 size; + uint32 i, data_offset_64bit = 0, data_offset_32bit = 0; + uint32 value_size_64bit, value_size_32bit; + + /* Allocate memory */ + size = sizeof(AOTImportGlobal) * (uint64)module->import_global_count; + if (size >= UINT32_MAX + || !(import_globals = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(import_globals, 0, (uint32)size); + + /* Create each import global */ + for (i = 0; i < module->import_global_count; i++) { + WASMGlobalImport *import_global = &module->import_globals[i].u.global; + import_globals[i].module_name = import_global->module_name; + import_globals[i].global_name = import_global->field_name; + import_globals[i].type.val_type = import_global->type.val_type; + import_globals[i].type.is_mutable = import_global->type.is_mutable; + import_globals[i].global_data_linked = + import_global->global_data_linked; + + import_globals[i].data_offset_64bit = data_offset_64bit; + import_globals[i].data_offset_32bit = data_offset_32bit; + + get_value_type_size(import_global->type.val_type, gc_enabled, + &value_size_64bit, &value_size_32bit); + + import_globals[i].size_64bit = value_size_64bit; + import_globals[i].size_32bit = value_size_32bit; + data_offset_64bit += value_size_64bit; + data_offset_32bit += value_size_32bit; + } + + *p_import_global_data_size_64bit = data_offset_64bit; + *p_import_global_data_size_32bit = data_offset_32bit; + return import_globals; } static AOTGlobal * -aot_create_globals(const WASMModule *module, - uint32 global_data_start_offset, - uint32 *p_global_data_size) +aot_create_globals(const WASMModule *module, bool gc_enabled, + uint32 global_data_start_offset_64bit, + uint32 global_data_start_offset_32bit, + uint32 *p_global_data_size_64bit, + uint32 *p_global_data_size_32bit) { - AOTGlobal *globals; - uint64 size; - uint32 i, data_offset = global_data_start_offset; - - /* Allocate memory */ - size = sizeof(AOTGlobal) * (uint64)module->global_count; - if (size >= UINT32_MAX - || !(globals = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } - - memset(globals, 0, (uint32)size); - - /* Create each global */ - for (i = 0; i < module->global_count; i++) { - WASMGlobal *global = &module->globals[i]; - globals[i].type = global->type; - globals[i].is_mutable = global->is_mutable; - globals[i].size = wasm_value_type_size(global->type); - memcpy(&globals[i].init_expr, &global->init_expr, - sizeof(global->init_expr)); - /* Calculate data offset */ - globals[i].data_offset = data_offset; - data_offset += wasm_value_type_size(global->type); - } - - *p_global_data_size = data_offset - global_data_start_offset; - return globals; + AOTGlobal *globals; + uint64 size; + uint32 i; + uint32 data_offset_64bit = global_data_start_offset_64bit; + uint32 data_offset_32bit = global_data_start_offset_32bit; + uint32 value_size_64bit, value_size_32bit; + + /* Allocate memory */ + size = sizeof(AOTGlobal) * (uint64)module->global_count; + if (size >= UINT32_MAX || !(globals = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(globals, 0, (uint32)size); + + /* Create each global */ + for (i = 0; i < module->global_count; i++) { + WASMGlobal *global = &module->globals[i]; + globals[i].type.val_type = global->type.val_type; + globals[i].type.is_mutable = global->type.is_mutable; + memcpy(&globals[i].init_expr, &global->init_expr, + sizeof(global->init_expr)); + + globals[i].data_offset_64bit = data_offset_64bit; + globals[i].data_offset_32bit = data_offset_32bit; + + get_value_type_size(global->type.val_type, gc_enabled, + &value_size_64bit, &value_size_32bit); + + globals[i].size_64bit = value_size_64bit; + globals[i].size_32bit = value_size_32bit; + data_offset_64bit += value_size_64bit; + data_offset_32bit += value_size_32bit; + } + + *p_global_data_size_64bit = + data_offset_64bit - global_data_start_offset_64bit; + *p_global_data_size_32bit = + data_offset_32bit - global_data_start_offset_32bit; + return globals; } -static void -aot_destroy_func_types(AOTFuncType **func_types, uint32 count) +static AOTImportFunc * +aot_create_import_funcs(const WASMModule *module) { - uint32 i; - for (i = 0; i < count; i++) - if (func_types[i]) - wasm_free(func_types[i]); - wasm_free(func_types); + AOTImportFunc *import_funcs; + uint64 size; + uint32 i, j; + + /* Allocate memory */ + size = sizeof(AOTImportFunc) * (uint64)module->import_function_count; + if (size >= UINT32_MAX + || !(import_funcs = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + /* Create each import function */ + for (i = 0; i < module->import_function_count; i++) { + WASMFunctionImport *import_func = + &module->import_functions[i].u.function; + import_funcs[i].module_name = import_func->module_name; + import_funcs[i].func_name = import_func->field_name; + import_funcs[i].func_ptr_linked = import_func->func_ptr_linked; + import_funcs[i].func_type = import_func->func_type; + import_funcs[i].signature = import_func->signature; + import_funcs[i].attachment = import_func->attachment; + import_funcs[i].call_conv_raw = import_func->call_conv_raw; + import_funcs[i].call_conv_wasm_c_api = false; + /* Resolve function type index */ + for (j = 0; j < module->type_count; j++) + if (import_func->func_type == (WASMFuncType *)module->types[j]) { + import_funcs[i].func_type_index = j; + break; + } + } + + return import_funcs; } -static AOTFuncType ** -aot_create_func_types(const WASMModule *module) +static void +aot_destroy_funcs(AOTFunc **funcs, uint32 count) { - AOTFuncType **func_types; - uint64 size; - uint32 i; - - /* Allocate memory */ - size = sizeof(AOTFuncType*) * (uint64)module->type_count; - if (size >= UINT32_MAX - || !(func_types = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } + uint32 i; + + for (i = 0; i < count; i++) + if (funcs[i]) { + if (funcs[i]->local_offsets) + wasm_runtime_free(funcs[i]->local_offsets); + wasm_runtime_free(funcs[i]); + } + wasm_runtime_free(funcs); +} - memset(func_types, 0, size); +static AOTFunc ** +aot_create_funcs(const WASMModule *module, uint32 pointer_size) +{ + AOTFunc **funcs; + uint64 size; + uint32 i, j; + + /* Allocate memory */ + size = sizeof(AOTFunc *) * (uint64)module->function_count; + if (size >= UINT32_MAX || !(funcs = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } - /* Create each function type */ - for (i = 0; i < module->type_count; i++) { - size = offsetof(AOTFuncType, types) + - (uint64)module->types[i]->param_count + - (uint64)module->types[i]->result_count; - if (size >= UINT32_MAX - || !(func_types[i] = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - goto fail; + memset(funcs, 0, size); + + /* Create each function */ + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + AOTFuncType *func_type = NULL; + AOTFunc *aot_func = NULL; + uint64 total_size; + uint32 offset = 0; + + size = sizeof(AOTFunc); + if (!(aot_func = funcs[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + memset(aot_func, 0, sizeof(AOTFunc)); + + func_type = aot_func->func_type = func->func_type; + + /* Resolve function type index */ + for (j = 0; j < module->type_count; j++) { + if (func_type == (WASMFuncType *)module->types[j]) { + aot_func->func_type_index = j; + break; + } + } + + total_size = sizeof(uint16) + * ((uint64)func_type->param_count + func->local_count); + if ((total_size > 0) + && (total_size >= UINT32_MAX + || !(aot_func->local_offsets = + wasm_runtime_malloc((uint32)total_size)))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + /* Resolve local variable info and code info */ + aot_func->local_count = func->local_count; + aot_func->local_types_wp = func->local_types; + aot_func->code = func->code; + aot_func->code_size = func->code_size; +#if WASM_ENABLE_BRANCH_HINTS != 0 + aot_func->code_body_begin = func->code_body_begin; +#endif + + /* Resolve local offsets */ + for (j = 0; j < func_type->param_count; j++) { + aot_func->local_offsets[j] = (uint16)offset; + offset += wasm_value_type_cell_num_internal(func_type->types[j], + pointer_size); + } + aot_func->param_cell_num = offset; + + for (j = 0; j < func->local_count; j++) { + aot_func->local_offsets[func_type->param_count + j] = + (uint16)offset; + offset += wasm_value_type_cell_num_internal(func->local_types[j], + pointer_size); + } + aot_func->local_cell_num = offset - aot_func->param_cell_num; + + aot_func->max_stack_cell_num = func->max_stack_cell_num; + /* We use max_stack_cell_num calculated from wasm_loader, which is based + * on wamrc's target type. + * - If the wamrc is compiled as 64bit, then the number is enough for + * both 32bit and 64bit runtime target + * - If the wamrc is compiled as 32bit, then we multiply this number by + * two to avoid overflow on 64bit runtime target */ + if (sizeof(uintptr_t) == 4) { + aot_func->max_stack_cell_num *= 2; + } } - memcpy(func_types[i], module->types[i], size); - } - return func_types; + return funcs; fail: - aot_destroy_func_types(func_types, module->type_count); - return NULL; + aot_destroy_funcs(funcs, module->function_count); + return NULL; } -static AOTImportFunc * -aot_create_import_funcs(const WASMModule *module) +#if WASM_ENABLE_GC != 0 +static void +calculate_struct_field_sizes_offsets(AOTCompData *comp_data, bool is_target_x86, + bool gc_enabled) { - AOTImportFunc *import_funcs; - uint64 size; - uint32 i, j; - - /* Allocate memory */ - size = sizeof(AOTImportFunc) * (uint64)module->import_function_count; - if (size >= UINT32_MAX - || !(import_funcs = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } - - /* Create each import function */ - for (i = 0; i < module->import_function_count; i++) { - WASMFunctionImport *import_func = &module->import_functions[i].u.function; - import_funcs[i].module_name = import_func->module_name; - import_funcs[i].func_name = import_func->field_name; - import_funcs[i].func_ptr_linked = import_func->func_ptr_linked; - import_funcs[i].func_type = import_func->func_type; - /* Resolve function type index */ - for (j = 0; j < module->type_count; j++) - if (import_func->func_type == module->types[j]) { - import_funcs[i].func_type_index = j; - break; - } - } - - return import_funcs; + uint32 i; + + for (i = 0; i < comp_data->type_count; i++) { + if (comp_data->types[i]->type_flag == WASM_TYPE_STRUCT) { + WASMStructType *struct_type = (WASMStructType *)comp_data->types[i]; + WASMStructFieldType *fields = struct_type->fields; + uint32 field_offset_64bit, field_offset_32bit; + uint32 field_size_64bit, field_size_32bit, j; + + /* offsetof(WASMStructObject, field_data) in 64-bit */ + field_offset_64bit = sizeof(uint64); + + /* offsetof(WASMStructObject, field_data) in 32-bit */ + field_offset_32bit = sizeof(uint32); + + for (j = 0; j < struct_type->field_count; j++) { + get_value_type_size(fields[j].field_type, gc_enabled, + &field_size_64bit, &field_size_32bit); + + fields[j].field_size_64bit = field_size_64bit; + fields[j].field_size_32bit = field_size_32bit; + + if (!is_target_x86) { + if (field_size_64bit == 2) + field_offset_64bit = align_uint(field_offset_64bit, 2); + else if (field_size_64bit >= 4) + field_offset_64bit = align_uint(field_offset_64bit, 4); + + if (field_size_32bit == 2) + field_offset_32bit = align_uint(field_offset_32bit, 2); + else if (field_size_32bit >= 4) + field_offset_32bit = align_uint(field_offset_32bit, 4); + } + + fields[j].field_offset_64bit = field_offset_64bit; + fields[j].field_offset_32bit = field_offset_32bit; + + field_offset_64bit += field_size_64bit; + field_offset_32bit += field_size_32bit; + } + } + } +} +#endif + +/** + * Checks if target architecture is 64-bit based on target_arch string. + * + * @param target_arch The target architecture string (e.g. "x86_64", "aarch64") + * @return true if target is 64-bit architecture, false otherwise + * + * If target_arch is NULL, detection is based on UINTPTR_MAX. + * Otherwise looks for "64" in target_arch string. + */ +static bool +arch_is_64bit(const char *target_arch) +{ + if (!target_arch) { +#if UINTPTR_MAX == UINT64_MAX + return true; +#else + return false; +#endif + } + /* All 64bit targets contains "64" string in their target name */ + return strstr(target_arch, "64") != NULL; } -static void -aot_destroy_funcs(AOTFunc **funcs, uint32 count) +/** + * Checks if target architecture is x86/x64 based on target_arch string. + * + * @param target_arch The target architecture string (e.g. "x86_64", "i386") + * @return true if target is x86/x64 architecture, false otherwise + * + * If target_arch is NULL, detection is based on build-time definitions. + * Otherwise checks for x86_64 or i386 in target_arch string. + */ +static bool +arch_is_x86(const char *target_arch) +{ + if (!target_arch) { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_X86_32) + return true; +#else + return false; +#endif + } + return !strncmp(target_arch, "x86_64", 6) + || !strncmp(target_arch, "i386", 4); +} + +/** + * Initialize memory information in AOT compilation data from WASM module. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing memory information + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_memories(AOTCompData *comp_data, WASMModule *module) { - uint32 i; + uint32 i, j; + uint64 size; + + comp_data->memory_count = + module->import_memory_count + module->memory_count; + + /* Allocate memory for memory array, reserve one AOTMemory space at least */ + if (!comp_data->memory_count) + comp_data->memory_count = 1; + + size = (uint64)comp_data->memory_count * sizeof(AOTMemory); + if (size >= UINT32_MAX + || !(comp_data->memories = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("create memories array failed.\n"); + return false; + } + memset(comp_data->memories, 0, size); + + if (!(module->import_memory_count + module->memory_count)) { + comp_data->memories[0].num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + } + + /* Set memory page count */ + for (i = 0; i < module->import_memory_count + module->memory_count; i++) { + if (i < module->import_memory_count) { + comp_data->memories[i].flags = + module->import_memories[i].u.memory.mem_type.flags; + comp_data->memories[i].num_bytes_per_page = + module->import_memories[i].u.memory.mem_type.num_bytes_per_page; + comp_data->memories[i].init_page_count = + module->import_memories[i].u.memory.mem_type.init_page_count; + comp_data->memories[i].max_page_count = + module->import_memories[i].u.memory.mem_type.max_page_count; + } + else { + j = i - module->import_memory_count; + comp_data->memories[i].flags = module->memories[j].flags; + comp_data->memories[i].num_bytes_per_page = + module->memories[j].num_bytes_per_page; + comp_data->memories[i].init_page_count = + module->memories[j].init_page_count; + comp_data->memories[i].max_page_count = + module->memories[j].max_page_count; + } + } - for (i = 0; i < count; i++) - if (funcs[i]) - wasm_free(funcs[i]); - wasm_free(funcs); + return true; } -static AOTFunc ** -aot_create_funcs(const WASMModule *module) +/** + * Initialize table information in AOT compilation data from WASM module. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing table information + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_tables(AOTCompData *comp_data, WASMModule *module) { - AOTFunc **funcs; - uint64 size; - uint32 i, j; - - /* Allocate memory */ - size = sizeof(AOTFunc*) * (uint64)module->function_count; - if (size >= UINT32_MAX - || !(funcs = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } + uint32 i, j; + uint64 size; + + comp_data->table_count = module->import_table_count + module->table_count; + + if (comp_data->table_count > 0) { + size = sizeof(AOTTable) * (uint64)comp_data->table_count; + if (size >= UINT32_MAX + || !(comp_data->tables = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("create tables array failed.\n"); + return false; + } + memset(comp_data->tables, 0, size); + for (i = 0; i < comp_data->table_count; i++) { + if (i < module->import_table_count) { + comp_data->tables[i].table_type.elem_type = + module->import_tables[i].u.table.table_type.elem_type; + comp_data->tables[i].table_type.flags = + module->import_tables[i].u.table.table_type.flags; + comp_data->tables[i].table_type.init_size = + module->import_tables[i].u.table.table_type.init_size; + comp_data->tables[i].table_type.max_size = + module->import_tables[i].u.table.table_type.max_size; +#if WASM_ENABLE_GC != 0 + comp_data->tables[i].table_type.elem_ref_type = + module->import_tables[i].u.table.table_type.elem_ref_type; +#endif + comp_data->tables[i].table_type.possible_grow = + module->import_tables[i].u.table.table_type.possible_grow; + } + else { + j = i - module->import_table_count; + comp_data->tables[i].table_type.elem_type = + module->tables[j].table_type.elem_type; + comp_data->tables[i].table_type.flags = + module->tables[j].table_type.flags; + comp_data->tables[i].table_type.init_size = + module->tables[j].table_type.init_size; + comp_data->tables[i].table_type.max_size = + module->tables[j].table_type.max_size; + comp_data->tables[i].table_type.possible_grow = + module->tables[j].table_type.possible_grow; +#if WASM_ENABLE_GC != 0 + comp_data->tables[j].table_type.elem_ref_type = + module->tables[j].table_type.elem_ref_type; + /* Note: if the init_expr contains extra data for struct/array + * initialization information (init_expr.u.data), the pointer is + * copied. + * The pointers should still belong to wasm module, so DO NOT + * free the pointers copied to comp_data */ + comp_data->tables[j].init_expr = module->tables[j].init_expr; +#endif + } + } + } + + return true; +} - memset(funcs, 0, size); +/** + * Initialize memory segment information in AOT compilation data. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing memory segments + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_memory_segments(AOTCompData *comp_data, WASMModule *module) +{ + comp_data->mem_init_data_count = module->data_seg_count; + if (comp_data->mem_init_data_count > 0 + && !(comp_data->mem_init_data_list = + aot_create_mem_init_data_list(module))) { + return false; + } + return true; +} - /* Create each function */ - for (i = 0; i < module->function_count; i++) { - WASMFunction *func = module->functions[i]; - size = sizeof (AOTFunc); - if (!(funcs[i] = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - goto fail; +/** + * Initialize table segment information in AOT compilation data. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing table segments + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_table_segments(AOTCompData *comp_data, WASMModule *module) +{ + comp_data->table_init_data_count = module->table_seg_count; + if (comp_data->table_init_data_count > 0 + && !(comp_data->table_init_data_list = + aot_create_table_init_data_list(module))) { + return false; } + return true; +} - funcs[i]->func_type = func->func_type; +/** + * Initialize global variable information in AOT compilation data. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing global information + * @param gc_enabled whether garbage collection is enabled + * @param import_global_data_size_64bit [out] size of imported global data for + * 64-bit + * @param import_global_data_size_32bit [out] size of imported global data for + * 32-bit + * @param global_data_size_64bit [out] size of global data for 64-bit + * @param global_data_size_32bit [out] size of global data for 32-bit + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_globals(AOTCompData *comp_data, WASMModule *module, bool gc_enabled, + uint32 *import_global_data_size_64bit, + uint32 *import_global_data_size_32bit, + uint32 *global_data_size_64bit, uint32 *global_data_size_32bit) +{ + comp_data->import_global_count = module->import_global_count; + if (comp_data->import_global_count > 0 + && !(comp_data->import_globals = aot_create_import_globals( + module, gc_enabled, import_global_data_size_64bit, + import_global_data_size_32bit))) { + return false; + } - /* Resolve function type index */ - for (j = 0; j < module->type_count; j++) - if (func->func_type == module->types[j]) { - funcs[i]->func_type_index = j; - break; - } + comp_data->global_count = module->global_count; + if (comp_data->global_count + && !(comp_data->globals = aot_create_globals( + module, gc_enabled, *import_global_data_size_64bit, + *import_global_data_size_32bit, global_data_size_64bit, + global_data_size_32bit))) { + return false; + } - /* Resolve local variable info and code info */ - funcs[i]->local_count = func->local_count; - funcs[i]->local_types = func->local_types; - funcs[i]->code = func->code; - funcs[i]->code_size = func->code_size; - } + comp_data->global_data_size_64bit = + *import_global_data_size_64bit + *global_data_size_64bit; + comp_data->global_data_size_32bit = + *import_global_data_size_32bit + *global_data_size_32bit; - return funcs; + return true; +} -fail: - aot_destroy_funcs(funcs, module->function_count); - return NULL; +/** + * Initialize type information in AOT compilation data. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing type information + * @param is_target_x86 whether the target architecture is x86 + * @param gc_enabled whether garbage collection is enabled + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_types(AOTCompData *comp_data, WASMModule *module, bool is_target_x86, + bool gc_enabled) +{ + comp_data->type_count = module->type_count; + comp_data->types = module->types; +#if WASM_ENABLE_GC != 0 + calculate_struct_field_sizes_offsets(comp_data, is_target_x86, gc_enabled); +#endif + return true; } -static AOTExportFunc * -aot_create_export_funcs(const WASMModule *module, - uint32 export_func_count) +/** + * Initialize function information in AOT compilation data. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing function information + * @param is_64bit_target whether the target architecture is 64-bit + * @return true if initialization succeeded, false otherwise + */ +static bool +aot_init_functions(AOTCompData *comp_data, WASMModule *module, + bool is_64bit_target) { - AOTExportFunc *export_funcs; - uint64 size; - uint32 i, j = 0; - - /* Allocate memory */ - size = sizeof(AOTExportFunc) * (uint64)export_func_count; - if (size >= UINT32_MAX - || !(export_funcs = wasm_malloc((uint32)size))) { - aot_set_last_error("allocate memory failed."); - return NULL; - } - - /* Create each export function */ - for (i = 0; i < module->export_count; i++) { - if (module->exports[i].kind == EXPORT_KIND_FUNC) { - export_funcs[j].func_name = module->exports[i].name; - export_funcs[j].func_index = module->exports[i].index; - export_funcs[j].func_type = - module->functions[module->exports[i].index - - module->import_function_count]->func_type; - /* Function pointer to be linked in JIT mode */ - export_funcs[j].func_ptr = NULL; - j++; + comp_data->import_func_count = module->import_function_count; + if (comp_data->import_func_count + && !(comp_data->import_funcs = aot_create_import_funcs(module))) { + return false; } - } - return export_funcs; + + comp_data->func_count = module->function_count; + if (comp_data->func_count + && !(comp_data->funcs = + aot_create_funcs(module, is_64bit_target ? 8 : 4))) { + return false; + } + + return true; +} + +/** + * Initialize auxiliary data in AOT compilation data. + * + * @param comp_data the AOT compilation data structure to initialize + * @param module the source WASM module containing auxiliary data + */ +static void +aot_init_aux_data(AOTCompData *comp_data, WASMModule *module) +{ + comp_data->aux_data_end_global_index = module->aux_data_end_global_index; + comp_data->aux_data_end = module->aux_data_end; + comp_data->aux_heap_base_global_index = module->aux_heap_base_global_index; + comp_data->aux_heap_base = module->aux_heap_base; + comp_data->aux_stack_top_global_index = module->aux_stack_top_global_index; + comp_data->aux_stack_bottom = module->aux_stack_bottom; + comp_data->aux_stack_size = module->aux_stack_size; + + comp_data->start_func_index = module->start_function; + comp_data->malloc_func_index = module->malloc_function; + comp_data->free_func_index = module->free_function; + comp_data->retain_func_index = module->retain_function; + +#if WASM_ENABLE_STRINGREF != 0 + comp_data->string_literal_count = module->string_literal_count; + comp_data->string_literal_ptrs_wp = module->string_literal_ptrs; + comp_data->string_literal_lengths_wp = module->string_literal_lengths; +#endif } -AOTCompData* -aot_create_comp_data(WASMModule *module) +AOTCompData * +aot_create_comp_data(WASMModule *module, const char *target_arch, + bool gc_enabled) { - AOTCompData *comp_data; - uint32 import_global_data_size = 0, global_data_size = 0, i; + AOTCompData *comp_data; + uint32 import_global_data_size_64bit = 0, global_data_size_64bit = 0; + uint32 import_global_data_size_32bit = 0, global_data_size_32bit = 0; + bool is_64bit_target = arch_is_64bit(target_arch); + bool is_target_x86 = arch_is_x86(target_arch); + + if (!(comp_data = wasm_runtime_malloc(sizeof(AOTCompData)))) { + aot_set_last_error("create compile data failed.\n"); + return NULL; + } + memset(comp_data, 0, sizeof(AOTCompData)); + + if (!aot_init_memories(comp_data, module) + || !aot_init_memory_segments(comp_data, module) + || !aot_init_tables(comp_data, module) + || !aot_init_table_segments(comp_data, module) + || !aot_init_globals(comp_data, module, gc_enabled, + &import_global_data_size_64bit, + &import_global_data_size_32bit, + &global_data_size_64bit, &global_data_size_32bit) + || !aot_init_types(comp_data, module, is_target_x86, gc_enabled) + || !aot_init_functions(comp_data, module, is_64bit_target)) { + goto fail; + } - /* Allocate memory */ - if (!(comp_data = wasm_malloc(sizeof(AOTCompData)))) { - aot_set_last_error("create compile data failed.\n"); - return NULL; - } - - memset(comp_data, 0, sizeof(AOTCompData)); - - /* Set memory page count */ - if (module->import_memory_count) { - comp_data->num_bytes_per_page = - module->import_memories[0].u.memory.num_bytes_per_page; - comp_data->mem_init_page_count = - module->import_memories[0].u.memory.init_page_count; - comp_data->mem_max_page_count = - module->import_memories[0].u.memory.max_page_count; - } - else if (module->memory_count) { - comp_data->num_bytes_per_page = - module->memories[0].num_bytes_per_page; - comp_data->mem_init_page_count = - module->memories[0].init_page_count; - comp_data->mem_max_page_count = - module->memories[0].max_page_count; - } - - /* Create memory data segments */ - comp_data->mem_init_data_count = module->data_seg_count; - if (comp_data->mem_init_data_count > 0 - && !(comp_data->mem_init_data_list = - aot_create_mem_init_data_list(module))) - goto fail; - - /* Set table size */ - if (module->import_table_count) - comp_data->table_size = module->import_tables[0].u.table.init_size; - else if (module->table_count) - comp_data->table_size = module->tables[0].init_size; - - /* Create table data segments */ - comp_data->table_init_data_count = module->table_seg_count; - if (comp_data->table_init_data_count > 0 - && !(comp_data->table_init_data_list = - aot_create_table_init_data_list(module))) - goto fail; - - /* Create import globals */ - comp_data->import_global_count = module->import_global_count; - if (comp_data->import_global_count > 0 - && !(comp_data->import_globals = - aot_create_import_globals(module, &import_global_data_size))) - goto fail; - - /* Create globals */ - comp_data->global_count = module->global_count; - if (comp_data->global_count - && !(comp_data->globals = aot_create_globals - (module, import_global_data_size, &global_data_size))) - goto fail; - - comp_data->global_data_size = import_global_data_size + - global_data_size; - - /* Create function types */ - comp_data->func_type_count = module->type_count; - if (comp_data->func_type_count - && !(comp_data->func_types = aot_create_func_types(module))) - goto fail; - - /* Create import functions */ - comp_data->import_func_count = module->import_function_count; - if (comp_data->import_func_count - && !(comp_data->import_funcs = aot_create_import_funcs(module))) - goto fail; - - /* Create functions */ - comp_data->func_count = module->function_count; - if (comp_data->func_count - && !(comp_data->funcs = aot_create_funcs(module))) - goto fail; - - /* Create export functions */ - for (i = 0; i < module->export_count; i++) - if (module->exports[i].kind == EXPORT_KIND_FUNC) - comp_data->export_func_count++; - - if (comp_data->export_func_count - && !(comp_data->export_funcs = aot_create_export_funcs - (module, comp_data->export_func_count))) - goto fail; - - comp_data->start_func_index = module->start_function; - comp_data->wasm_module = module; - - return comp_data; +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + comp_data->name_section_buf = module->name_section_buf; + comp_data->name_section_buf_end = module->name_section_buf_end; +#endif + +#if WASM_ENABLE_BRANCH_HINTS != 0 + comp_data->function_hints = module->function_hints; +#endif + + aot_init_aux_data(comp_data, module); + + comp_data->wasm_module = module; + + return comp_data; fail: - aot_destroy_comp_data(comp_data); - return NULL; + aot_destroy_comp_data(comp_data); + return NULL; } void aot_destroy_comp_data(AOTCompData *comp_data) { - if (!comp_data) - return; + if (!comp_data) + return; - if (comp_data->mem_init_data_list) - aot_destroy_mem_init_data_list(comp_data->mem_init_data_list, - comp_data->mem_init_data_count); + if (comp_data->import_memories) + wasm_runtime_free(comp_data->import_memories); - if (comp_data->table_init_data_list) - aot_destroy_table_init_data_list(comp_data->table_init_data_list, - comp_data->table_init_data_count); + if (comp_data->memories) + wasm_runtime_free(comp_data->memories); - if (comp_data->import_globals) - wasm_free(comp_data->import_globals); + if (comp_data->mem_init_data_list) + aot_destroy_mem_init_data_list(comp_data->mem_init_data_list, + comp_data->mem_init_data_count); - if (comp_data->globals) - wasm_free(comp_data->globals); + if (comp_data->import_tables) + wasm_runtime_free(comp_data->import_tables); - if (comp_data->func_types) - aot_destroy_func_types(comp_data->func_types, - comp_data->func_type_count); + if (comp_data->tables) + wasm_runtime_free(comp_data->tables); - if (comp_data->import_funcs) - wasm_free(comp_data->import_funcs); + if (comp_data->table_init_data_list) + aot_destroy_table_init_data_list(comp_data->table_init_data_list, + comp_data->table_init_data_count); - if (comp_data->funcs) - aot_destroy_funcs(comp_data->funcs, comp_data->func_count); + if (comp_data->import_globals) + wasm_runtime_free(comp_data->import_globals); - if (comp_data->export_funcs) - wasm_free(comp_data->export_funcs); + if (comp_data->globals) + wasm_runtime_free(comp_data->globals); - wasm_free(comp_data); -} + if (comp_data->import_funcs) + wasm_runtime_free(comp_data->import_funcs); + + if (comp_data->funcs) + aot_destroy_funcs(comp_data->funcs, comp_data->func_count); + if (comp_data->aot_name_section_buf) + wasm_runtime_free(comp_data->aot_name_section_buf); + + wasm_runtime_free(comp_data); +} diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index be33867def..f1ecccfb37 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -15,157 +15,359 @@ extern "C" { #endif +#ifndef AOT_FUNC_PREFIX #define AOT_FUNC_PREFIX "aot_func#" +#endif + +#ifndef AOT_FUNC_INTERNAL_PREFIX +#define AOT_FUNC_INTERNAL_PREFIX "aot_func_internal#" +#endif + +#ifndef AOT_STACK_SIZES_NAME +#define AOT_STACK_SIZES_NAME "aot_stack_sizes" +#endif +extern const char *aot_stack_sizes_name; + +#ifndef AOT_STACK_SIZES_ALIAS_NAME +#define AOT_STACK_SIZES_ALIAS_NAME "aot_stack_sizes_alias" +#endif +extern const char *aot_stack_sizes_alias_name; + +#ifndef AOT_STACK_SIZES_SECTION_NAME +#define AOT_STACK_SIZES_SECTION_NAME ".aot_stack_sizes" +#endif +extern const char *aot_stack_sizes_section_name; typedef InitializerExpression AOTInitExpr; -typedef WASMType AOTFuncType; +typedef WASMType AOTType; +typedef WASMFuncType AOTFuncType; +#if WASM_ENABLE_GC != 0 +typedef WASMStructType AOTStructType; +typedef WASMArrayType AOTArrayType; +#endif +typedef WASMExport AOTExport; +typedef WASMMemory AOTMemory; +typedef WASMMemoryType AOTMemoryType; +typedef WASMTableType AOTTableType; +typedef WASMTable AOTTable; + +#if WASM_ENABLE_DEBUG_AOT != 0 +typedef void *dwarf_extractor_handle_t; +#endif + +typedef enum AOTIntCond { + INT_EQZ = 0, + INT_EQ, + INT_NE, + INT_LT_S, + INT_LT_U, + INT_GT_S, + INT_GT_U, + INT_LE_S, + INT_LE_U, + INT_GE_S, + INT_GE_U +} AOTIntCond; + +typedef enum AOTFloatCond { + FLOAT_EQ = 0, + FLOAT_NE, + FLOAT_LT, + FLOAT_GT, + FLOAT_LE, + FLOAT_GE, + FLOAT_UNO +} AOTFloatCond; + +/** + * Import memory + */ +typedef struct AOTImportMemory { + char *module_name; + char *memory_name; + AOTMemoryType mem_type; +} AOTImportMemory; /** * A segment of memory init data */ typedef struct AOTMemInitData { - /* Start address of init data */ - AOTInitExpr offset; - /* Byte count */ - uint32 byte_count; - /* Byte array */ - uint8 bytes[1]; +#if WASM_ENABLE_BULK_MEMORY != 0 + /* Passive flag */ + bool is_passive; + /* memory index */ + uint32 memory_index; +#endif + /* Start address of init data */ + AOTInitExpr offset; + /* Byte count */ + uint32 byte_count; + /* Byte array */ + uint8 *bytes; } AOTMemInitData; +/** + * Import table + */ +typedef struct AOTImportTable { + char *module_name; + char *table_name; + AOTTableType table_type; +} AOTImportTable; + /** * A segment of table init data */ typedef struct AOTTableInitData { - /* Start address of init data */ - AOTInitExpr offset; - /* Function index count */ - uint32 func_index_count; - /* Function index array */ - uint32 func_indexes[1]; + /* 0 to 7 */ + uint32 mode; + /* funcref or externref, elemkind will be considered as funcref */ + uint32 elem_type; +#if WASM_ENABLE_GC != 0 + WASMRefType *elem_ref_type; +#endif + /* optional, only for active */ + uint32 table_index; + /* Start address of init data */ + AOTInitExpr offset; + /* Function index count */ + uint32 value_count; + /* Function index array */ + InitializerExpression init_values[1]; } AOTTableInitData; /** * Import global variable */ typedef struct AOTImportGlobal { - char *module_name; - char *global_name; - /* VALUE_TYPE_I32/I64/F32/F64 */ - uint8 type; - bool is_mutable; - uint32 size; - /* The data offset of current global in global data */ - uint32 data_offset; - /* global data after linked */ - WASMValue global_data_linked; + char *module_name; + char *global_name; + WASMGlobalType type; + uint32 size; + /* The data offset of current global in global data */ + uint32 data_offset; +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + /* + * The data size and data offset of a wasm global may vary + * in 32-bit target and 64-bit target, e.g., the size of a + * GC obj is 4 bytes in the former and 8 bytes in the + * latter, the AOT compiler needs to use the correct data + * offset according to the target info. + */ + uint32 size_64bit; + uint32 size_32bit; + uint32 data_offset_64bit; + uint32 data_offset_32bit; +#endif + /* global data after linked */ + WASMValue global_data_linked; + bool is_linked; } AOTImportGlobal; /** * Global variable */ typedef struct AOTGlobal { - /* VALUE_TYPE_I32/I64/F32/F64 */ - uint8 type; - bool is_mutable; - uint32 size; - /* The data offset of current global in global data */ - uint32 data_offset; - AOTInitExpr init_expr; + WASMGlobalType type; + uint32 size; + /* The data offset of current global in global data */ + uint32 data_offset; +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + /* See comments in AOTImportGlobal */ + uint32 size_64bit; + uint32 size_32bit; + uint32 data_offset_64bit; + uint32 data_offset_32bit; +#endif + AOTInitExpr init_expr; } AOTGlobal; /** * Import function */ typedef struct AOTImportFunc { - char *module_name; - char *func_name; - AOTFuncType *func_type; - uint32 func_type_index; - /* function pointer after linked */ - void *func_ptr_linked; + char *module_name; + char *func_name; + AOTFuncType *func_type; + uint32 func_type_index; + /* function pointer after linked */ + void *func_ptr_linked; + /* signature from registered native symbols */ + const char *signature; + /* attachment */ + void *attachment; + bool call_conv_raw; + bool call_conv_wasm_c_api; + bool wasm_c_api_with_env; } AOTImportFunc; /** * Function */ typedef struct AOTFunc { - AOTFuncType *func_type; - uint32 func_type_index; - uint32 local_count; - uint8 *local_types; - uint32 code_size; - uint8 *code; + AOTFuncType *func_type; + uint32 func_type_index; + uint32 local_count; + uint8 *local_types_wp; + uint16 param_cell_num; + uint16 local_cell_num; + uint32 max_stack_cell_num; + uint32 code_size; + uint8 *code; + /* offset of each local, including function parameters + and local variables */ + uint16 *local_offsets; +#if WASM_ENABLE_BRANCH_HINTS != 0 + uint8 *code_body_begin; +#endif } AOTFunc; -/** - * Export function - */ -typedef struct AOTExportFunc { - char *func_name; - AOTFuncType *func_type; - /* function pointer linked */ - void *func_ptr; - uint32 func_index; -} AOTExportFunc; - typedef struct AOTCompData { - /* Memory and memory init data info */ - uint32 num_bytes_per_page; - uint32 mem_init_page_count; - uint32 mem_max_page_count; - uint32 mem_init_data_count; - AOTMemInitData **mem_init_data_list; + /* Import memories */ + uint32 import_memory_count; + AOTImportMemory *import_memories; + + /* Memories */ + uint32 memory_count; + AOTMemory *memories; + + /* Memory init data info */ + uint32 mem_init_data_count; + AOTMemInitData **mem_init_data_list; - /* Table and table init data info */ - uint32 table_size; - AOTTableInitData **table_init_data_list; - uint32 table_init_data_count; + /* Import tables */ + uint32 import_table_count; + AOTImportTable *import_tables; - AOTImportGlobal *import_globals; - uint32 import_global_count; + /* Tables */ + uint32 table_count; + AOTTable *tables; - AOTGlobal *globals; - uint32 global_count; + /* Table init data info */ + uint32 table_init_data_count; + AOTTableInitData **table_init_data_list; - AOTFuncType **func_types; - uint32 func_type_count; + /* Import globals */ + uint32 import_global_count; + AOTImportGlobal *import_globals; - AOTImportFunc *import_funcs; - uint32 import_func_count; + /* Globals */ + uint32 global_count; + AOTGlobal *globals; - AOTFunc **funcs; - uint32 func_count; + /* Function types */ + uint32 type_count; + AOTType **types; - AOTExportFunc *export_funcs; - uint32 export_func_count; + /* Import functions */ + uint32 import_func_count; + AOTImportFunc *import_funcs; - uint32 start_func_index; - uint32 addr_data_size; - uint32 global_data_size; + /* Functions */ + uint32 func_count; + AOTFunc **funcs; + + /* Custom name sections */ + const uint8 *name_section_buf; + const uint8 *name_section_buf_end; + uint8 *aot_name_section_buf; + uint32 aot_name_section_size; + + uint32 global_data_size_64bit; + uint32 global_data_size_32bit; + + uint32 start_func_index; + uint32 malloc_func_index; + uint32 free_func_index; + uint32 retain_func_index; + + uint32 aux_data_end_global_index; + uint64 aux_data_end; + uint32 aux_heap_base_global_index; + uint64 aux_heap_base; + uint32 aux_stack_top_global_index; + uint64 aux_stack_bottom; + uint32 aux_stack_size; + +#if WASM_ENABLE_STRINGREF != 0 + uint32 string_literal_count; + uint32 *string_literal_lengths_wp; + const uint8 **string_literal_ptrs_wp; +#endif - uint32 llvm_aux_data_end; - uint32 llvm_aux_stack_bottom; - uint32 llvm_aux_stack_size; - uint32 llvm_aux_stack_global_index; + WASMModule *wasm_module; +#if WASM_ENABLE_DEBUG_AOT != 0 + dwarf_extractor_handle_t extractor; +#endif - WASMModule *wasm_module; +#if WASM_ENABLE_BRANCH_HINTS != 0 + struct WASMCompilationHint **function_hints; +#endif } AOTCompData; -AOTCompData* -aot_create_comp_data(WASMModule *module); +typedef struct AOTNativeSymbol { + bh_list_link link; + char symbol[48]; + int32 index; +} AOTNativeSymbol; + +AOTCompData * +aot_create_comp_data(WASMModule *module, const char *target_arch, + bool gc_enabled); void aot_destroy_comp_data(AOTCompData *comp_data); -char* -aot_get_last_error(); +char * +aot_get_last_error(void); void aot_set_last_error(const char *error); +void +aot_set_last_error_v(const char *format, ...); + +#if BH_DEBUG != 0 +#define HANDLE_FAILURE(callee) \ + do { \ + aot_set_last_error_v("call %s failed in %s:%d", (callee), \ + __FUNCTION__, __LINE__); \ + } while (0) +#else +#define HANDLE_FAILURE(callee) \ + do { \ + aot_set_last_error_v("call %s failed", (callee)); \ + } while (0) +#endif + +static inline uint32 +aot_get_imp_tbl_data_slots(const AOTImportTable *tbl, bool is_jit_mode) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + if (is_jit_mode) + return tbl->table_type.max_size; +#else + (void)is_jit_mode; +#endif + return tbl->table_type.possible_grow ? tbl->table_type.max_size + : tbl->table_type.init_size; +} + +static inline uint32 +aot_get_tbl_data_slots(const AOTTable *tbl, bool is_jit_mode) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + if (is_jit_mode) + return tbl->table_type.max_size; +#else + (void)is_jit_mode; +#endif + return tbl->table_type.possible_grow ? tbl->table_type.max_size + : tbl->table_type.init_size; +} + #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_H_ */ - diff --git a/core/iwasm/compilation/aot_compiler.c b/core/iwasm/compilation/aot_compiler.c index 8d078344ea..29026e8008 100644 --- a/core/iwasm/compilation/aot_compiler.c +++ b/core/iwasm/compilation/aot_compiler.c @@ -14,754 +14,4122 @@ #include "aot_emit_control.h" #include "aot_emit_function.h" #include "aot_emit_parametric.h" -#include "bh_memory.h" +#include "aot_emit_table.h" +#include "aot_emit_gc.h" +#include "aot_stack_frame_comp.h" +#include "simd/simd_access_lanes.h" +#include "simd/simd_bitmask_extracts.h" +#include "simd/simd_bit_shifts.h" +#include "simd/simd_bitwise_ops.h" +#include "simd/simd_bool_reductions.h" +#include "simd/simd_comparisons.h" +#include "simd/simd_conversions.h" +#include "simd/simd_construct_values.h" +#include "simd/simd_conversions.h" +#include "simd/simd_floating_point.h" +#include "simd/simd_int_arith.h" +#include "simd/simd_load_store.h" +#include "simd/simd_sat_int_arith.h" #include "../aot/aot_runtime.h" #include "../interpreter/wasm_opcode.h" #include +#if WASM_ENABLE_DEBUG_AOT != 0 +#include "debug/dwarf_extractor.h" +#endif + +#if WASM_ENABLE_STRINGREF != 0 +#include "string_object.h" +#include "aot_emit_stringref.h" +#endif + +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + if (buf + length > buf_end) { \ + aot_set_last_error("read leb failed: unexpected end."); \ + return false; \ + } \ + } while (0) + +static bool +read_leb(const uint8 *buf, const uint8 *buf_end, uint32 *p_offset, + uint32 maxbits, bool sign, uint64 *p_result) +{ + uint64 result = 0; + uint32 shift = 0; + uint32 bcnt = 0; + uint64 byte; + + while (true) { + CHECK_BUF(buf, buf_end, 1); + byte = buf[*p_offset]; + *p_offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + bcnt += 1; + } + if (bcnt > (maxbits + 6) / 7) { + aot_set_last_error("read leb failed: " + "integer representation too long"); + return false; + } + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= (~((uint64)0)) << shift; + } + *p_result = result; + return true; +} + +/* NOLINTNEXTLINE */ +#define read_leb_generic(p, p_end, res, res_type, sign) \ + do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(p, p_end, &off, sizeof(res_type) << 3, sign, &res64)) \ + return false; \ + p += off; \ + res = (res_type)res64; \ + } while (0) + +/* NOLINTNEXTLINE */ +#define read_leb_int32(p, p_end, res) \ + read_leb_generic(p, p_end, res, int32, true) + +/* NOLINTNEXTLINE */ +#define read_leb_int64(p, p_end, res) \ + read_leb_generic(p, p_end, res, int64, true) + +/* NOLINTNEXTLINE */ +#define read_leb_uint32(p, p_end, res) \ + read_leb_generic(p, p_end, res, uint32, false) + +/* NOLINTNEXTLINE */ +#define read_leb_uint64(p, p_end, res) \ + read_leb_generic(p, p_end, res, uint64, false) + +/* NOLINTNEXTLINE */ +#if WASM_ENABLE_MEMORY64 != 0 +#define read_leb_mem_offset(p, p_end, res) \ + do { \ + if (IS_MEMORY64) { \ + read_leb_uint64(p, p_end, res); \ + } \ + else { \ + read_leb_uint32(p, p_end, res); \ + } \ + } while (0) +#else +#define read_leb_mem_offset read_leb_uint32 +#endif + +/** + * Since wamrc uses a full feature Wasm loader, + * add a post-validator here to run checks according + * to options, like enable_tail_call, enable_ref_types, + * and so on. + */ +static bool +aot_validate_wasm(AOTCompContext *comp_ctx) +{ + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + /* Doesn't support multiple tables unless enabling reference type */ + if (comp_ctx->comp_data->import_table_count + + comp_ctx->comp_data->table_count + > 1) { + aot_set_last_error("multiple tables"); + return false; + } + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (comp_ctx->pointer_size < sizeof(uint64)) { + if (IS_MEMORY64) { + aot_set_last_error("Compiling wasm64(contains i64 memory section) " + "to 32bit platform is not allowed"); + return false; + } + + for (uint32 i = 0; i < comp_ctx->comp_data->table_count; ++i) { + if (IS_TABLE64(i)) { + aot_set_last_error("Compiling wasm64(contains i64 table " + "section) to 32bit platform is not allowed"); + return false; + } + } + } +#endif + + return true; +} + +#define COMPILE_ATOMIC_RMW(OP, NAME) \ + case WASM_OP_ATOMIC_RMW_I32_##NAME: \ + bytes = 4; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME: \ + bytes = 8; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I32_##NAME##8_U: \ + bytes = 1; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I32_##NAME##16_U: \ + bytes = 2; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##8_U: \ + bytes = 1; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##16_U: \ + bytes = 2; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##32_U: \ + bytes = 4; \ + op_type = VALUE_TYPE_I64; \ + OP_ATOMIC_##OP : bin_op = LLVMAtomicRMWBinOp##OP; \ + goto build_atomic_rmw; + +uint32 +offset_of_local_in_outs_area(AOTCompContext *comp_ctx, unsigned n) +{ + AOTCompFrame *frame = comp_ctx->aot_frame; + return frame->cur_frame_size + offset_of_local(comp_ctx, n); +} + +static bool +store_value(AOTCompContext *comp_ctx, LLVMValueRef value, uint8 value_type, + LLVMValueRef cur_frame, uint32 offset) +{ + LLVMValueRef value_offset, value_addr, value_ptr = NULL, res; + LLVMTypeRef value_ptr_type = NULL; + + if (!(value_offset = I32_CONST(offset))) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, cur_frame, + &value_offset, 1, "value_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + switch (value_type) { + case VALUE_TYPE_I32: + value_ptr_type = INT32_PTR_TYPE; + break; + case VALUE_TYPE_I64: + value_ptr_type = INT64_PTR_TYPE; + break; + case VALUE_TYPE_F32: + value_ptr_type = F32_PTR_TYPE; + break; + case VALUE_TYPE_F64: + value_ptr_type = F64_PTR_TYPE; + break; + case VALUE_TYPE_V128: + value_ptr_type = V128_PTR_TYPE; + break; +#if WASM_ENABLE_GC != 0 + case VALUE_TYPE_GC_REF: + value_ptr_type = GC_REF_PTR_TYPE; + break; +#endif + default: + bh_assert(0); + break; + } + + if (!(value_ptr = LLVMBuildBitCast(comp_ctx->builder, value_addr, + value_ptr_type, "value_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, value, value_ptr))) { + aot_set_last_error("llvm build store failed"); + return false; + } + + LLVMSetAlignment(res, 4); + + return true; +} + +void +aot_call_stack_features_init_default(AOTCallStackFeatures *features) +{ + memset(features, 1, sizeof(AOTCallStackFeatures)); + features->frame_per_function = false; +} + +bool +aot_frame_store_value(AOTCompContext *comp_ctx, LLVMValueRef value, + uint8 value_type, LLVMValueRef cur_frame, uint32 offset) +{ + return store_value(comp_ctx, value, value_type, cur_frame, offset); +} + +static bool +store_ref(AOTCompContext *comp_ctx, uint32 ref, LLVMValueRef cur_frame, + uint32 offset, uint32 nbytes) +{ + LLVMValueRef value_ref = NULL, value_offset, value_addr, res; + + if (!(value_offset = I32_CONST(offset))) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, cur_frame, + &value_offset, 1, "value_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + switch (nbytes) { + case 1: + if (!(value_ref = I8_CONST((uint8)ref))) { + aot_set_last_error("llvm build const failed"); + } + break; + case 2: + ref = (ref << 8) | ref; + + if (!(value_ref = LLVMConstInt(INT16_TYPE, (uint16)ref, false))) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value_addr = + LLVMBuildBitCast(comp_ctx->builder, value_addr, + INT16_PTR_TYPE, "value_addr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + break; + case 4: + ref = (ref << 24) | (ref << 16) | (ref << 8) | ref; + + if (!(value_ref = I32_CONST(ref))) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value_addr = + LLVMBuildBitCast(comp_ctx->builder, value_addr, + INT32_PTR_TYPE, "value_addr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + break; + default: + bh_assert(0); + break; + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, value_ref, value_addr))) { + aot_set_last_error("llvm build store failed"); + return false; + } + LLVMSetAlignment(res, 1); + + return true; +} + +bool +aot_gen_commit_values(AOTCompFrame *frame) +{ + AOTCompContext *comp_ctx = frame->comp_ctx; + AOTFuncContext *func_ctx = frame->func_ctx; + AOTValueSlot *p, *end; + LLVMValueRef value; + uint32 n; + + if (!frame->comp_ctx->call_stack_features.values) { + return true; + } + + /* First, commit reference flags + * For LLVM JIT, iterate all local and stack ref flags + * For AOT, ignore local(params + locals) ref flags */ + for (p = comp_ctx->is_jit_mode ? frame->lp + : frame->lp + frame->max_local_cell_num; + p < frame->sp; p++) { + if (!p->dirty) + continue; + + n = (uint32)(p - frame->lp); + + /* Commit reference flag */ + if (comp_ctx->enable_gc) { + switch (p->type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + case VALUE_TYPE_I1: + if (p->ref != p->committed_ref - 1) { + if (!store_ref(comp_ctx, p->ref, func_ctx->cur_frame, + offset_of_ref(comp_ctx, n), 1)) + return false; + p->committed_ref = p->ref + 1; + } + break; + + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_assert(p->ref == (p + 1)->ref); + if (p->ref != p->committed_ref - 1 + || p->ref != (p + 1)->committed_ref - 1) { + if (!store_ref(comp_ctx, p->ref, func_ctx->cur_frame, + offset_of_ref(comp_ctx, n), 2)) + return false; + p->committed_ref = (p + 1)->committed_ref = p->ref + 1; + } + p++; + break; + + case VALUE_TYPE_V128: + bh_assert(p->ref == (p + 1)->ref && p->ref == (p + 2)->ref + && p->ref == (p + 3)->ref); + if (p->ref != p->committed_ref - 1 + || p->ref != (p + 1)->committed_ref - 1 + || p->ref != (p + 2)->committed_ref - 1 + || p->ref != (p + 3)->committed_ref - 1) { + if (!store_ref(comp_ctx, p->ref, func_ctx->cur_frame, + offset_of_ref(comp_ctx, n), 4)) + return false; + p->committed_ref = (p + 1)->committed_ref = + (p + 2)->committed_ref = (p + 3)->committed_ref = + p->ref + 1; + } + p += 3; + break; + + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + case REF_TYPE_FUNCREF: + case REF_TYPE_EXTERNREF: + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + case VALUE_TYPE_GC_REF: + if (comp_ctx->pointer_size == sizeof(uint64)) { + bh_assert(p->ref == (p + 1)->ref); + if (p->ref != p->committed_ref - 1 + || p->ref != (p + 1)->committed_ref - 1) { + if (!store_ref(comp_ctx, p->ref, + func_ctx->cur_frame, + offset_of_ref(comp_ctx, n), 2)) + return false; + p->committed_ref = (p + 1)->committed_ref = + p->ref + 1; + } + p++; + } + else { + if (p->ref != p->committed_ref - 1) { + if (!store_ref(comp_ctx, p->ref, + func_ctx->cur_frame, + offset_of_ref(comp_ctx, n), 1)) + return false; + p->committed_ref = p->ref + 1; + } + } + break; + + default: + bh_assert(0); + break; + } + } + } + + /* Second, commit all values */ + for (p = frame->lp; p < frame->sp; p++) { + if (!p->dirty) + continue; + + p->dirty = 0; + n = (uint32)(p - frame->lp); + + /* Commit values */ + switch (p->type) { + case VALUE_TYPE_I32: + if (!store_value(comp_ctx, p->value, VALUE_TYPE_I32, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_I64: + (++p)->dirty = 0; + if (!store_value(comp_ctx, p->value, VALUE_TYPE_I64, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_F32: + if (!store_value(comp_ctx, p->value, VALUE_TYPE_F32, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_F64: + (++p)->dirty = 0; + if (!store_value(comp_ctx, p->value, VALUE_TYPE_F64, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_V128: + (++p)->dirty = 0; + (++p)->dirty = 0; + (++p)->dirty = 0; + if (!store_value(comp_ctx, p->value, VALUE_TYPE_V128, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_I1: + if (!(value = LLVMBuildZExt(comp_ctx->builder, p->value, + I32_TYPE, "i32_val"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (!store_value(comp_ctx, value, VALUE_TYPE_I32, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + if (comp_ctx->enable_ref_types) { + if (!store_value(comp_ctx, p->value, VALUE_TYPE_I32, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + } +#if WASM_ENABLE_GC != 0 + else if (comp_ctx->enable_gc) { + if (comp_ctx->pointer_size == sizeof(uint64)) + (++p)->dirty = 0; + if (!store_value(comp_ctx, p->value, VALUE_TYPE_GC_REF, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + } +#endif + else { + bh_assert(0); + } + break; +#if WASM_ENABLE_GC != 0 + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case VALUE_TYPE_GC_REF: + if (comp_ctx->pointer_size == sizeof(uint64)) + (++p)->dirty = 0; + if (!store_value(comp_ctx, p->value, VALUE_TYPE_GC_REF, + func_ctx->cur_frame, + offset_of_local(comp_ctx, n))) + return false; + break; +#endif + default: + bh_assert(0); + break; + } + } + + if (comp_ctx->enable_gc) { + end = frame->lp + frame->max_local_cell_num + frame->max_stack_cell_num; + + /* Clear reference flags for unused stack slots. */ + for (p = frame->sp; p < end; p++) { + bh_assert(!p->ref); + n = (uint32)(p - frame->lp); + + /* Commit reference flag. */ + if (p->ref != p->committed_ref - 1) { + if (!store_ref(comp_ctx, p->ref, func_ctx->cur_frame, + offset_of_ref(comp_ctx, n), 1)) + return false; + p->committed_ref = 1 + p->ref; + } + } + } + + return true; +} + +static bool +aot_standard_frame_gen_commit_ip(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef ip_value, bool is_64bit) +{ + LLVMValueRef cur_frame = func_ctx->cur_frame; + LLVMValueRef value_offset, value_addr, value_ptr; + uint32 offset_ip; + + if (!comp_ctx->is_jit_mode) + offset_ip = comp_ctx->pointer_size * 4; + else + offset_ip = offsetof(WASMInterpFrame, ip); + + if (!(value_offset = I32_CONST(offset_ip))) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, cur_frame, + &value_offset, 1, "ip_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(value_ptr = LLVMBuildBitCast( + comp_ctx->builder, value_addr, + is_64bit ? INT64_PTR_TYPE : INT32_PTR_TYPE, "ip_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!LLVMBuildStore(comp_ctx->builder, ip_value, value_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + + return true; +} + +bool +aot_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef ip_value, bool is_64bit) +{ + switch (comp_ctx->aux_stack_frame_type) { + case AOT_STACK_FRAME_TYPE_STANDARD: + return aot_standard_frame_gen_commit_ip(comp_ctx, func_ctx, + ip_value, is_64bit); + case AOT_STACK_FRAME_TYPE_TINY: + return aot_tiny_frame_gen_commit_ip(comp_ctx, func_ctx, ip_value); + default: + aot_set_last_error( + "unsupported mode when generating commit_ip code"); + return false; + } +} + +bool +aot_gen_commit_sp_ip(AOTCompFrame *frame, bool commit_sp, bool commit_ip) +{ + AOTCompContext *comp_ctx = frame->comp_ctx; + AOTFuncContext *func_ctx = frame->func_ctx; + LLVMValueRef cur_frame = func_ctx->cur_frame; + LLVMValueRef value_offset, value_addr, value_ptr, value; + LLVMTypeRef int8_ptr_ptr_type; + uint32 offset_sp, n; + bool is_64bit = (comp_ctx->pointer_size == sizeof(uint64)) ? true : false; + const AOTValueSlot *sp = frame->sp; + const uint8 *ip = frame->frame_ip; + + if (!comp_ctx->is_jit_mode) { + offset_sp = frame->comp_ctx->pointer_size * 5; + } + else { + offset_sp = offsetof(WASMInterpFrame, sp); + } + + if (commit_ip && comp_ctx->call_stack_features.ip) { + if (!comp_ctx->is_jit_mode) { + WASMModule *module = comp_ctx->comp_data->wasm_module; + if (is_64bit) + value = I64_CONST((uint64)(uintptr_t)(ip - module->load_addr)); + else + value = I32_CONST((uint32)(uintptr_t)(ip - module->load_addr)); + } + else { + if (is_64bit) + value = I64_CONST((uint64)(uintptr_t)ip); + else + value = I32_CONST((uint32)(uintptr_t)ip); + } + + if (!value) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!aot_gen_commit_ip(comp_ctx, func_ctx, value, is_64bit)) { + return false; + } + } + + if (commit_sp && comp_ctx->call_stack_features.values) { + n = (uint32)(sp - frame->lp); + value = I32_CONST(offset_of_local(comp_ctx, n)); + if (!value) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + cur_frame, &value, 1, "sp"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(value_offset = I32_CONST(offset_sp))) { + aot_set_last_error("llvm build const failed"); + return false; + } + + if (!(value_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, cur_frame, + &value_offset, 1, "sp_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(int8_ptr_ptr_type = LLVMPointerType(INT8_PTR_TYPE, 0))) { + aot_set_last_error("llvm build pointer type failed"); + return false; + } + + if (!(value_ptr = LLVMBuildBitCast(comp_ctx->builder, value_addr, + int8_ptr_ptr_type, "sp_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!LLVMBuildStore(comp_ctx->builder, value, value_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + + return true; +} + +static uint32 +get_cur_frame_size(const AOTCompContext *comp_ctx, uint32 max_local_cell_num, + uint32 max_stack_cell_num) +{ + uint32 all_cell_num = max_local_cell_num + max_stack_cell_num; + uint32 frame_size; + + if (!comp_ctx->is_jit_mode) { + /* Refer to aot_alloc_frame */ + if (!comp_ctx->enable_gc) + frame_size = comp_ctx->pointer_size + * (offsetof(AOTFrame, lp) / sizeof(uintptr_t)) + + all_cell_num * 4; + else + frame_size = comp_ctx->pointer_size + * (offsetof(AOTFrame, lp) / sizeof(uintptr_t)) + + align_uint(all_cell_num * 5, 4); + } + else { + /* Refer to wasm_interp_interp_frame_size */ + if (!comp_ctx->enable_gc) + frame_size = offsetof(WASMInterpFrame, lp) + all_cell_num * 4; + else + frame_size = + offsetof(WASMInterpFrame, lp) + align_uint(all_cell_num * 5, 4); + } + + return frame_size; +} + +static bool +init_comp_frame(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 func_idx) +{ + AOTCompFrame *aot_frame; + AOTFunc *aot_func = func_ctx->aot_func; + AOTFuncType *func_type = aot_func->func_type; + AOTBlock *block = func_ctx->block_stack.block_list_end; + LLVMValueRef local_value; + uint32 max_local_cell_num = + aot_func->param_cell_num + aot_func->local_cell_num; + uint32 max_stack_cell_num = aot_func->max_stack_cell_num; + uint32 all_cell_num = max_local_cell_num + max_stack_cell_num; + uint32 i, n; + uint64 total_size; + uint8 local_type; + + /* Free aot_frame if it was allocated previously for + compiling other functions */ + if (comp_ctx->aot_frame) { + wasm_runtime_free(comp_ctx->aot_frame); + comp_ctx->aot_frame = NULL; + } + + /* Allocate extra 2 cells since some operations may push more + operands than the number calculated in wasm loader, such as + PUSH_F64(F64_CONST(1.0)) in aot_compile_op_f64_promote_f32 */ + all_cell_num += 2; + total_size = offsetof(AOTCompFrame, lp) + + (uint64)sizeof(AOTValueSlot) * all_cell_num; + + if (total_size > UINT32_MAX + || !(comp_ctx->aot_frame = aot_frame = + wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + return false; + } + memset(aot_frame, 0, (uint32)total_size); + + aot_frame->comp_ctx = comp_ctx; + aot_frame->func_ctx = func_ctx; + + aot_frame->max_local_cell_num = max_local_cell_num; + aot_frame->max_stack_cell_num = max_stack_cell_num; + aot_frame->cur_frame_size = + get_cur_frame_size(comp_ctx, max_local_cell_num, max_stack_cell_num); + + aot_frame->sp = aot_frame->lp + max_local_cell_num; + + /* Init the frame_sp_begin and frame_sp_max_reached + of the function block */ + block->frame_sp_begin = block->frame_sp_max_reached = aot_frame->sp; + + n = 0; + + /* Set all params dirty since they were set to llvm value but + haven't been committed to the AOT/JIT stack frame */ + for (i = 0; i < func_type->param_count; i++) { + local_type = func_type->types[i]; + local_value = LLVMGetParam(func_ctx->func, i + 1); + + switch (local_type) { + case VALUE_TYPE_I32: + set_local_i32(comp_ctx->aot_frame, n, local_value); + n++; + break; + case VALUE_TYPE_I64: + set_local_i64(comp_ctx->aot_frame, n, local_value); + n += 2; + break; + case VALUE_TYPE_F32: + set_local_f32(comp_ctx->aot_frame, n, local_value); + n++; + break; + case VALUE_TYPE_F64: + set_local_f64(comp_ctx->aot_frame, n, local_value); + n += 2; + break; + case VALUE_TYPE_V128: + set_local_v128(comp_ctx->aot_frame, n, local_value); + n += 4; + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + { + if (comp_ctx->enable_ref_types) { + set_local_ref(comp_ctx->aot_frame, n, local_value, + local_type); + n++; + } +#if WASM_ENABLE_GC != 0 + else if (comp_ctx->enable_gc) { + set_local_gc_ref(comp_ctx->aot_frame, n, local_value, + VALUE_TYPE_GC_REF); + n += comp_ctx->pointer_size / sizeof(uint32); + } +#endif + else { + bh_assert(0); + } + break; + } +#if WASM_ENABLE_GC != 0 + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + bh_assert(comp_ctx->enable_gc); + set_local_gc_ref(comp_ctx->aot_frame, n, local_value, + VALUE_TYPE_GC_REF); + n += comp_ctx->pointer_size / sizeof(uint32); + break; +#endif + default: + bh_assert(0); + break; + } + } + + /* TODO: re-calculate param_cell_num according to the build target + after creating comp_ctx */ + /* bh_assert(n == aot_func->param_cell_num); */ + + /* Set all locals dirty since they were set to llvm value but + haven't been committed to the AOT/JIT stack frame */ + for (i = 0; i < aot_func->local_count; i++) { + local_type = aot_func->local_types_wp[i]; + + switch (local_type) { + case VALUE_TYPE_I32: + set_local_i32(comp_ctx->aot_frame, n, I32_ZERO); + n++; + break; + case VALUE_TYPE_I64: + set_local_i64(comp_ctx->aot_frame, n, I64_ZERO); + n += 2; + break; + case VALUE_TYPE_F32: + set_local_f32(comp_ctx->aot_frame, n, F32_ZERO); + n++; + break; + case VALUE_TYPE_F64: + set_local_f64(comp_ctx->aot_frame, n, F64_ZERO); + n += 2; + break; + case VALUE_TYPE_V128: + set_local_v128(comp_ctx->aot_frame, n, V128_f64x2_ZERO); + n += 4; + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + { + if (comp_ctx->enable_ref_types) { + set_local_ref(comp_ctx->aot_frame, n, I32_ZERO, local_type); + n++; + } +#if WASM_ENABLE_GC != 0 + else if (comp_ctx->enable_gc) { + set_local_gc_ref(comp_ctx->aot_frame, n, GC_REF_NULL, + VALUE_TYPE_GC_REF); + n += comp_ctx->pointer_size / sizeof(uint32); + } +#endif + else { + bh_assert(0); + } + break; + } +#if WASM_ENABLE_GC != 0 + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + bh_assert(comp_ctx->enable_gc); + set_local_gc_ref(comp_ctx->aot_frame, n, GC_REF_NULL, + VALUE_TYPE_GC_REF); + n += comp_ctx->pointer_size / sizeof(uint32); + break; +#endif + default: + bh_assert(0); + break; + } + } + + /* TODO: re-calculate local_cell_num according to the build target + after creating comp_ctx */ + /* bh_assert(n == aot_func->param_cell_num + aot_func->local_cell_num); */ + + /* No need to initialize aot_frame all cells' committed_ref flags + and all stack cells' ref flags since they have been initialized + as 0 (uncommitted and not-reference) by the memset above */ + + return true; +} + +static bool +aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) +{ + AOTFuncContext *func_ctx = comp_ctx->func_ctxes[func_index]; + LLVMValueRef func_index_ref; + uint8 *frame_ip = func_ctx->aot_func->code, opcode, *p_f32, *p_f64; + uint8 *frame_ip_end = frame_ip + func_ctx->aot_func->code_size; + uint8 *param_types = NULL; + uint8 *result_types = NULL; + uint8 value_type; + uint16 param_count; + uint16 result_count; + uint32 br_depth, *br_depths, br_count; + uint32 func_idx, type_idx, mem_idx, local_idx, global_idx, i; + uint32 bytes = 4, align; + mem_offset_t offset; + uint32 type_index; + bool sign = true; + int32 i32_const; + int64 i64_const; + float32 f32_const; + float64 f64_const; + AOTFuncType *func_type = NULL; +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMMetadataRef location; +#endif + + /* Start to translate the opcodes */ + LLVMPositionBuilderAtEnd( + comp_ctx->builder, + func_ctx->block_stack.block_list_head->llvm_entry_block); + + if (comp_ctx->aux_stack_frame_type + && comp_ctx->call_stack_features.frame_per_function) { + INT_CONST(func_index_ref, + func_index + comp_ctx->comp_data->import_func_count, I32_TYPE, + true); + if (!aot_alloc_frame_per_function_frame_for_aot_func(comp_ctx, func_ctx, + func_index_ref)) { + return false; + } + } + if (comp_ctx->aux_stack_frame_type) { + if (!init_comp_frame(comp_ctx, func_ctx, func_index)) { + return false; + } + } + + while (frame_ip < frame_ip_end) { + opcode = *frame_ip++; + + if (comp_ctx->aot_frame) { + comp_ctx->aot_frame->frame_ip = frame_ip - 1; + } + +#if WASM_ENABLE_DEBUG_AOT != 0 + location = dwarf_gen_location( + comp_ctx, func_ctx, + (frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code); + if (location != NULL) { + LLVMSetCurrentDebugLocation2(comp_ctx->builder, location); + } +#endif + + switch (opcode) { + case WASM_OP_UNREACHABLE: + if (!aot_compile_op_unreachable(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_NOP: + break; + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + { + value_type = *frame_ip++; + if (value_type == VALUE_TYPE_I32 || value_type == VALUE_TYPE_I64 + || value_type == VALUE_TYPE_F32 + || value_type == VALUE_TYPE_F64 + || value_type == VALUE_TYPE_V128 + || value_type == VALUE_TYPE_VOID + || (comp_ctx->enable_ref_types + && (value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF)) + || (comp_ctx->enable_gc /* single byte type */ + && aot_is_type_gc_reftype(value_type))) { + param_count = 0; + param_types = NULL; + if (value_type == VALUE_TYPE_VOID) { + result_count = 0; + result_types = NULL; + } + else { + if (comp_ctx->enable_gc + && aot_is_type_gc_reftype(value_type)) + value_type = VALUE_TYPE_GC_REF; + result_count = 1; + result_types = &value_type; + } + } + else { + frame_ip--; + read_leb_int32(frame_ip, frame_ip_end, type_index); + /* type index was checked in wasm loader */ + bh_assert(type_index < comp_ctx->comp_data->type_count); + func_type = + (AOTFuncType *)comp_ctx->comp_data->types[type_index]; + param_count = func_type->param_count; + param_types = func_type->types; + result_count = func_type->result_count; + result_types = func_type->types + param_count; + } + if (!aot_compile_op_block( + comp_ctx, func_ctx, &frame_ip, frame_ip_end, + (uint32)(LABEL_TYPE_BLOCK + opcode - WASM_OP_BLOCK), + param_count, param_types, result_count, result_types)) + return false; + break; + } + + case EXT_OP_BLOCK: + case EXT_OP_LOOP: + case EXT_OP_IF: + { + read_leb_int32(frame_ip, frame_ip_end, type_index); + /* type index was checked in wasm loader */ + bh_assert(type_index < comp_ctx->comp_data->type_count); + func_type = + (AOTFuncType *)comp_ctx->comp_data->types[type_index]; + param_count = func_type->param_count; + param_types = func_type->types; + result_count = func_type->result_count; + result_types = func_type->types + param_count; + if (!aot_compile_op_block( + comp_ctx, func_ctx, &frame_ip, frame_ip_end, + (uint32)(LABEL_TYPE_BLOCK + opcode - EXT_OP_BLOCK), + param_count, param_types, result_count, result_types)) + return false; + break; + } + + case WASM_OP_ELSE: + if (!aot_compile_op_else(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_END: + if (!aot_compile_op_end(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_BR: + { + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!aot_compile_op_br(comp_ctx, func_ctx, br_depth, &frame_ip)) + return false; + break; + } + + case WASM_OP_BR_IF: + { + if (!aot_compile_op_br_if(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + } + + case WASM_OP_BR_TABLE: + { + read_leb_uint32(frame_ip, frame_ip_end, br_count); + if (!(br_depths = wasm_runtime_malloc((uint32)sizeof(uint32) + * (br_count + 1)))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + for (i = 0; i <= br_count; i++) + read_leb_uint32(frame_ip, frame_ip_end, br_depths[i]); +#else + for (i = 0; i <= br_count; i++) + br_depths[i] = *frame_ip++; +#endif + + if (!aot_compile_op_br_table(comp_ctx, func_ctx, br_depths, + br_count, &frame_ip)) { + wasm_runtime_free(br_depths); + return false; + } + + wasm_runtime_free(br_depths); + break; + } + +#if WASM_ENABLE_FAST_INTERP == 0 + case EXT_OP_BR_TABLE_CACHE: + { + BrTableCache *node = bh_list_first_elem( + comp_ctx->comp_data->wasm_module->br_table_cache_list); + BrTableCache *node_next; + const uint8 *frame_ip_org = frame_ip - 1; + + read_leb_uint32(frame_ip, frame_ip_end, br_count); + + while (node) { + node_next = bh_list_elem_next(node); + if (node->br_table_op_addr == frame_ip_org) { + br_depths = node->br_depths; + if (!aot_compile_op_br_table(comp_ctx, func_ctx, + br_depths, br_count, + &frame_ip)) { + return false; + } + break; + } + node = node_next; + } + bh_assert(node); + + break; + } +#endif + + case WASM_OP_RETURN: + if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_CALL: + { + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!aot_compile_op_call(comp_ctx, func_ctx, func_idx, false)) + return false; + break; + } + + case WASM_OP_CALL_INDIRECT: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, type_idx); + if (comp_ctx->enable_gc + || comp_ctx->enable_call_indirect_overlong) { + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + } + else { + frame_ip++; + tbl_idx = 0; + } + + if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx, + tbl_idx)) + return false; + break; + } + +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: + { + if (!comp_ctx->enable_tail_call) { + aot_set_last_error("unsupported opcode"); + return false; + } + + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!aot_compile_op_call(comp_ctx, func_ctx, func_idx, true)) + return false; + if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + } + + case WASM_OP_RETURN_CALL_INDIRECT: + { + uint32 tbl_idx; + + if (!comp_ctx->enable_tail_call) { + aot_set_last_error("unsupported opcode"); + return false; + } + + read_leb_uint32(frame_ip, frame_ip_end, type_idx); + if (comp_ctx->enable_gc || comp_ctx->enable_ref_types) { + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + } + else { + frame_ip++; + tbl_idx = 0; + } + + if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx, + tbl_idx)) + return false; + if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + } +#endif /* end of WASM_ENABLE_TAIL_CALL */ + + case WASM_OP_DROP: + if (!aot_compile_op_drop(comp_ctx, func_ctx, true)) + return false; + break; + + case WASM_OP_DROP_64: + if (!aot_compile_op_drop(comp_ctx, func_ctx, false)) + return false; + break; + + case WASM_OP_SELECT: + if (!aot_compile_op_select(comp_ctx, func_ctx, true)) + return false; + break; + + case WASM_OP_SELECT_64: + if (!aot_compile_op_select(comp_ctx, func_ctx, false)) + return false; + break; + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_SELECT_T: + { + uint32 vec_len; + + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + goto unsupport_gc_and_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, vec_len); + bh_assert(vec_len == 1); + (void)vec_len; + + type_idx = *frame_ip++; + if (!aot_compile_op_select( + comp_ctx, func_ctx, + (type_idx != VALUE_TYPE_I64) + && (type_idx != VALUE_TYPE_F64) +#if WASM_ENABLE_GC != 0 + && !(comp_ctx->enable_gc + && comp_ctx->pointer_size == sizeof(uint64) + && wasm_is_type_reftype(type_idx)) +#endif + )) + return false; + + break; + } + case WASM_OP_TABLE_GET: + { + uint32 tbl_idx; + + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + goto unsupport_gc_and_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_get(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_SET: + { + uint32 tbl_idx; + + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + goto unsupport_gc_and_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_set(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } + case WASM_OP_REF_NULL: + { + uint32 type; + + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + goto unsupport_gc_and_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, type); + + if (!aot_compile_op_ref_null(comp_ctx, func_ctx)) + return false; + + (void)type; + break; + } + case WASM_OP_REF_IS_NULL: + { + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + goto unsupport_gc_and_ref_types; + } + + if (!aot_compile_op_ref_is_null(comp_ctx, func_ctx)) + return false; + break; + } + case WASM_OP_REF_FUNC: + { + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + goto unsupport_gc_and_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!aot_compile_op_ref_func(comp_ctx, func_ctx, func_idx)) + return false; + break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_GC != 0 + case WASM_OP_CALL_REF: + { + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + read_leb_uint32(frame_ip, frame_ip_end, type_idx); + if (!aot_compile_op_call_ref(comp_ctx, func_ctx, type_idx, + false)) + return false; + break; + } + + case WASM_OP_RETURN_CALL_REF: + { + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + read_leb_uint32(frame_ip, frame_ip_end, type_idx); + if (!aot_compile_op_call_ref(comp_ctx, func_ctx, type_idx, + true)) + return false; + if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + } + + case WASM_OP_REF_EQ: + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + if (!aot_compile_op_ref_eq(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_REF_AS_NON_NULL: + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + if (!aot_compile_op_ref_as_non_null(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_BR_ON_NULL: + { + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!aot_compile_op_br_on_null(comp_ctx, func_ctx, br_depth, + &frame_ip)) + return false; + break; + } + + case WASM_OP_BR_ON_NON_NULL: + { + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!aot_compile_op_br_on_non_null(comp_ctx, func_ctx, br_depth, + &frame_ip)) + return false; + break; + } + + case WASM_OP_GC_PREFIX: + { + uint32 opcode1, field_idx, data_seg_idx, array_len; + + if (!comp_ctx->enable_gc) { + goto unsupport_gc; + } + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_STRUCT_NEW: + case WASM_OP_STRUCT_NEW_DEFAULT: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + if (!aot_compile_op_struct_new( + comp_ctx, func_ctx, type_index, + opcode == WASM_OP_STRUCT_NEW_DEFAULT)) + return false; + break; + + case WASM_OP_STRUCT_GET: + case WASM_OP_STRUCT_GET_S: + case WASM_OP_STRUCT_GET_U: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, field_idx); + if (!aot_compile_op_struct_get( + comp_ctx, func_ctx, type_index, field_idx, + opcode == WASM_OP_STRUCT_GET_S)) + return false; + break; + + case WASM_OP_STRUCT_SET: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, field_idx); + if (!aot_compile_op_struct_set(comp_ctx, func_ctx, + type_index, field_idx)) + return false; + break; + + case WASM_OP_ARRAY_NEW: + case WASM_OP_ARRAY_NEW_DEFAULT: + case WASM_OP_ARRAY_NEW_FIXED: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + if (opcode == WASM_OP_ARRAY_NEW_FIXED) + read_leb_uint32(frame_ip, frame_ip_end, array_len); + else + array_len = 0; + if (!aot_compile_op_array_new( + comp_ctx, func_ctx, type_index, + opcode == WASM_OP_ARRAY_NEW_DEFAULT, + opcode == WASM_OP_ARRAY_NEW_FIXED, array_len)) + return false; + break; + + case WASM_OP_ARRAY_NEW_DATA: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, data_seg_idx); + if (!aot_compile_op_array_new_data( + comp_ctx, func_ctx, type_index, data_seg_idx)) + return false; + break; + + case WASM_OP_ARRAY_NEW_ELEM: + /* TODO */ + aot_set_last_error("unsupported opcode"); + return false; + + case WASM_OP_ARRAY_GET: + case WASM_OP_ARRAY_GET_S: + case WASM_OP_ARRAY_GET_U: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + if (!aot_compile_op_array_get( + comp_ctx, func_ctx, type_index, + opcode == WASM_OP_ARRAY_GET_S)) + return false; + break; + + case WASM_OP_ARRAY_SET: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + if (!aot_compile_op_array_set(comp_ctx, func_ctx, + type_index)) + return false; + break; + + case WASM_OP_ARRAY_FILL: + read_leb_uint32(frame_ip, frame_ip_end, type_index); + if (!aot_compile_op_array_fill(comp_ctx, func_ctx, + type_index)) + return false; + break; + + case WASM_OP_ARRAY_COPY: + { + uint32 src_type_index; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, src_type_index); + if (!aot_compile_op_array_copy( + comp_ctx, func_ctx, type_index, src_type_index)) + return false; + break; + } + + case WASM_OP_ARRAY_LEN: + if (!aot_compile_op_array_len(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_REF_I31: + if (!aot_compile_op_i31_new(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I31_GET_S: + case WASM_OP_I31_GET_U: + if (!aot_compile_op_i31_get( + comp_ctx, func_ctx, + opcode == WASM_OP_I31_GET_S ? true : false)) + return false; + break; + + case WASM_OP_REF_TEST: + case WASM_OP_REF_TEST_NULLABLE: + { + int32 heap_type; + + read_leb_int32(frame_ip, frame_ip_end, heap_type); + if (!aot_compile_op_ref_test( + comp_ctx, func_ctx, heap_type, + opcode == WASM_OP_REF_TEST_NULLABLE ? true + : false)) + return false; + break; + } + + case WASM_OP_REF_CAST: + case WASM_OP_REF_CAST_NULLABLE: + { + int32 heap_type; + + read_leb_int32(frame_ip, frame_ip_end, heap_type); + if (!aot_compile_op_ref_cast( + comp_ctx, func_ctx, heap_type, + opcode == WASM_OP_REF_CAST_NULLABLE ? true + : false)) + return false; + break; + } + + case WASM_OP_BR_ON_CAST: + case WASM_OP_BR_ON_CAST_FAIL: + { + uint8 castflags; + int32 heap_type, dst_heap_type; + + CHECK_BUF(frame_ip, frame_ip_end, 1); + castflags = *frame_ip++; + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + read_leb_int32(frame_ip, frame_ip_end, heap_type); + read_leb_int32(frame_ip, frame_ip_end, dst_heap_type); + + /* + * castflags should be 0~3: + * 0: (non-null, non-null) + * 1: (null, non-null) + * 2: (non-null, null) + * 3: (null, null) + * The nullability of source type has been checked in + * wasm loader, here we just need the dst nullability + */ + if (!aot_compile_op_br_on_cast( + comp_ctx, func_ctx, dst_heap_type, + castflags & 0x02, + opcode == WASM_OP_BR_ON_CAST_FAIL, br_depth, + &frame_ip)) + return false; + + (void)heap_type; + break; + } + + case WASM_OP_ANY_CONVERT_EXTERN: + if (!aot_compile_op_extern_internalize(comp_ctx, + func_ctx)) + return false; + break; + + case WASM_OP_EXTERN_CONVERT_ANY: + if (!aot_compile_op_extern_externalize(comp_ctx, + func_ctx)) + return false; + break; + +#if WASM_ENABLE_STRINGREF != 0 + case WASM_OP_STRING_NEW_UTF8: + case WASM_OP_STRING_NEW_WTF16: + case WASM_OP_STRING_NEW_LOSSY_UTF8: + case WASM_OP_STRING_NEW_WTF8: + { + EncodingFlag flag = WTF8; + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + bh_assert(mem_idx == 0); + + if (opcode == WASM_OP_STRING_NEW_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_NEW_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_WTF8) { + flag = WTF8; + } + + if (!aot_compile_op_string_new(comp_ctx, func_ctx, + flag)) + return false; + break; + } + case WASM_OP_STRING_CONST: + { + uint32 contents; + read_leb_uint32(frame_ip, frame_ip_end, contents); + + if (!aot_compile_op_string_const(comp_ctx, func_ctx, + contents)) + return false; + break; + } + case WASM_OP_STRING_MEASURE_UTF8: + case WASM_OP_STRING_MEASURE_WTF8: + case WASM_OP_STRING_MEASURE_WTF16: + { + EncodingFlag flag = WTF8; + + if (opcode == WASM_OP_STRING_MEASURE_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_MEASURE_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_MEASURE_WTF8) { + flag = LOSSY_UTF8; + } + + if (!aot_compile_op_string_measure(comp_ctx, func_ctx, + flag)) + return false; + break; + } + case WASM_OP_STRING_ENCODE_UTF8: + case WASM_OP_STRING_ENCODE_WTF16: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8: + case WASM_OP_STRING_ENCODE_WTF8: + { + EncodingFlag flag = WTF8; + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + bh_assert(mem_idx == 0); + + if (opcode == WASM_OP_STRING_ENCODE_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_ENCODE_WTF8) { + flag = WTF8; + } + + if (!aot_compile_op_string_encode(comp_ctx, func_ctx, + mem_idx, flag)) + return false; + break; + } + case WASM_OP_STRING_CONCAT: + if (!aot_compile_op_string_concat(comp_ctx, func_ctx)) + return false; + break; + case WASM_OP_STRING_EQ: + if (!aot_compile_op_string_eq(comp_ctx, func_ctx)) + return false; + break; + case WASM_OP_STRING_IS_USV_SEQUENCE: + if (!aot_compile_op_string_is_usv_sequence(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRING_AS_WTF8: + if (!aot_compile_op_string_as_wtf8(comp_ctx, func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_WTF8_ADVANCE: + if (!aot_compile_op_stringview_wtf8_advance(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8: + { + EncodingFlag flag = WTF8; + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + bh_assert(mem_idx == 0); + + if (opcode == WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode + == WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8) { + flag = WTF8; + } + + if (!aot_compile_op_stringview_wtf8_encode( + comp_ctx, func_ctx, mem_idx, flag)) + return false; + break; + } + case WASM_OP_STRINGVIEW_WTF8_SLICE: + if (!aot_compile_op_stringview_wtf8_slice(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRING_AS_WTF16: + if (!aot_compile_op_string_as_wtf16(comp_ctx, func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_WTF16_LENGTH: + if (!aot_compile_op_stringview_wtf16_length(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_WTF16_GET_CODEUNIT: + if (!aot_compile_op_stringview_wtf16_get_codeunit( + comp_ctx, func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_WTF16_ENCODE: + { + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + bh_assert(mem_idx == 0); + + if (!aot_compile_op_stringview_wtf16_encode( + comp_ctx, func_ctx, mem_idx)) + return false; + break; + } + case WASM_OP_STRINGVIEW_WTF16_SLICE: + if (!aot_compile_op_stringview_wtf16_slice(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRING_AS_ITER: + if (!aot_compile_op_string_as_iter(comp_ctx, func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_ITER_NEXT: + if (!aot_compile_op_stringview_iter_next(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_ITER_ADVANCE: + if (!aot_compile_op_stringview_iter_advance(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_ITER_REWIND: + if (!aot_compile_op_stringview_iter_rewind(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRINGVIEW_ITER_SLICE: + if (!aot_compile_op_stringview_iter_slice(comp_ctx, + func_ctx)) + return false; + break; + case WASM_OP_STRING_NEW_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF16_ARRAY: + case WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF8_ARRAY: + { + EncodingFlag flag = WTF8; + + if (opcode == WASM_OP_STRING_NEW_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_NEW_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_WTF8) { + flag = WTF8; + } + + if (!aot_compile_op_string_new_array(comp_ctx, func_ctx, + flag)) + return false; + + break; + } + case WASM_OP_STRING_ENCODE_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF16_ARRAY: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF8_ARRAY: + { + EncodingFlag flag = WTF8; + + if (opcode == WASM_OP_STRING_ENCODE_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_ENCODE_WTF8) { + flag = WTF8; + } + + if (!aot_compile_op_string_encode_array(comp_ctx, + func_ctx, flag)) + return false; + break; + } +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + + default: + aot_set_last_error("unsupported opcode"); + return false; + } + break; + } + +#endif /* end of WASM_ENABLE_GC != 0 */ + + case WASM_OP_GET_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!aot_compile_op_get_local(comp_ctx, func_ctx, local_idx)) + return false; + break; + + case WASM_OP_SET_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!aot_compile_op_set_local(comp_ctx, func_ctx, local_idx)) + return false; + break; + + case WASM_OP_TEE_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!aot_compile_op_tee_local(comp_ctx, func_ctx, local_idx)) + return false; + break; + + case WASM_OP_GET_GLOBAL: + case WASM_OP_GET_GLOBAL_64: + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + if (!aot_compile_op_get_global(comp_ctx, func_ctx, global_idx)) + return false; + break; + + case WASM_OP_SET_GLOBAL: + case WASM_OP_SET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_AUX_STACK: + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + if (!aot_compile_op_set_global( + comp_ctx, func_ctx, global_idx, + opcode == WASM_OP_SET_GLOBAL_AUX_STACK ? true : false)) + return false; + break; + + case WASM_OP_I32_LOAD: + bytes = 4; + sign = true; + goto op_i32_load; + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + bytes = 1; + sign = (opcode == WASM_OP_I32_LOAD8_S) ? true : false; + goto op_i32_load; + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + bytes = 2; + sign = (opcode == WASM_OP_I32_LOAD16_S) ? true : false; + op_i32_load: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i32_load(comp_ctx, func_ctx, align, offset, + bytes, sign, false)) + return false; + break; + + case WASM_OP_I64_LOAD: + bytes = 8; + sign = true; + goto op_i64_load; + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + bytes = 1; + sign = (opcode == WASM_OP_I64_LOAD8_S) ? true : false; + goto op_i64_load; + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + bytes = 2; + sign = (opcode == WASM_OP_I64_LOAD16_S) ? true : false; + goto op_i64_load; + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + bytes = 4; + sign = (opcode == WASM_OP_I64_LOAD32_S) ? true : false; + op_i64_load: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i64_load(comp_ctx, func_ctx, align, offset, + bytes, sign, false)) + return false; + break; + + case WASM_OP_F32_LOAD: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f32_load(comp_ctx, func_ctx, align, offset)) + return false; + break; + + case WASM_OP_F64_LOAD: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f64_load(comp_ctx, func_ctx, align, offset)) + return false; + break; + + case WASM_OP_I32_STORE: + bytes = 4; + goto op_i32_store; + case WASM_OP_I32_STORE8: + bytes = 1; + goto op_i32_store; + case WASM_OP_I32_STORE16: + bytes = 2; + op_i32_store: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i32_store(comp_ctx, func_ctx, align, offset, + bytes, false)) + return false; + break; + + case WASM_OP_I64_STORE: + bytes = 8; + goto op_i64_store; + case WASM_OP_I64_STORE8: + bytes = 1; + goto op_i64_store; + case WASM_OP_I64_STORE16: + bytes = 2; + goto op_i64_store; + case WASM_OP_I64_STORE32: + bytes = 4; + op_i64_store: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i64_store(comp_ctx, func_ctx, align, offset, + bytes, false)) + return false; + break; + + case WASM_OP_F32_STORE: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f32_store(comp_ctx, func_ctx, align, + offset)) + return false; + break; + + case WASM_OP_F64_STORE: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f64_store(comp_ctx, func_ctx, align, + offset)) + return false; + break; + + case WASM_OP_MEMORY_SIZE: + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!aot_compile_op_memory_size(comp_ctx, func_ctx)) + return false; + (void)mem_idx; + break; + + case WASM_OP_MEMORY_GROW: + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!aot_compile_op_memory_grow(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_CONST: + read_leb_int32(frame_ip, frame_ip_end, i32_const); + if (!aot_compile_op_i32_const(comp_ctx, func_ctx, i32_const)) + return false; + break; + + case WASM_OP_I64_CONST: + read_leb_int64(frame_ip, frame_ip_end, i64_const); + if (!aot_compile_op_i64_const(comp_ctx, func_ctx, i64_const)) + return false; + break; + + case WASM_OP_F32_CONST: + p_f32 = (uint8 *)&f32_const; + for (i = 0; i < sizeof(float32); i++) + *p_f32++ = *frame_ip++; + if (!aot_compile_op_f32_const(comp_ctx, func_ctx, f32_const)) + return false; + break; + + case WASM_OP_F64_CONST: + p_f64 = (uint8 *)&f64_const; + for (i = 0; i < sizeof(float64); i++) + *p_f64++ = *frame_ip++; + if (!aot_compile_op_f64_const(comp_ctx, func_ctx, f64_const)) + return false; + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + if (!aot_compile_op_i32_compare( + comp_ctx, func_ctx, INT_EQZ + opcode - WASM_OP_I32_EQZ)) + return false; + break; + + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + if (!aot_compile_op_i64_compare( + comp_ctx, func_ctx, INT_EQZ + opcode - WASM_OP_I64_EQZ)) + return false; + break; + + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + if (!aot_compile_op_f32_compare( + comp_ctx, func_ctx, FLOAT_EQ + opcode - WASM_OP_F32_EQ)) + return false; + break; + + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + if (!aot_compile_op_f64_compare( + comp_ctx, func_ctx, FLOAT_EQ + opcode - WASM_OP_F64_EQ)) + return false; + break; + + case WASM_OP_I32_CLZ: + if (!aot_compile_op_i32_clz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_CTZ: + if (!aot_compile_op_i32_ctz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_POPCNT: + if (!aot_compile_op_i32_popcnt(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + if (!aot_compile_op_i32_arithmetic( + comp_ctx, func_ctx, INT_ADD + opcode - WASM_OP_I32_ADD, + &frame_ip)) + return false; + break; + + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + if (!aot_compile_op_i32_bitwise( + comp_ctx, func_ctx, INT_SHL + opcode - WASM_OP_I32_AND)) + return false; + break; + + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + if (!aot_compile_op_i32_shift( + comp_ctx, func_ctx, INT_SHL + opcode - WASM_OP_I32_SHL)) + return false; + break; + + case WASM_OP_I64_CLZ: + if (!aot_compile_op_i64_clz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_CTZ: + if (!aot_compile_op_i64_ctz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_POPCNT: + if (!aot_compile_op_i64_popcnt(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + if (!aot_compile_op_i64_arithmetic( + comp_ctx, func_ctx, INT_ADD + opcode - WASM_OP_I64_ADD, + &frame_ip)) + return false; + break; + + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + if (!aot_compile_op_i64_bitwise( + comp_ctx, func_ctx, INT_SHL + opcode - WASM_OP_I64_AND)) + return false; + break; + + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + if (!aot_compile_op_i64_shift( + comp_ctx, func_ctx, INT_SHL + opcode - WASM_OP_I64_SHL)) + return false; + break; + + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + if (!aot_compile_op_f32_math(comp_ctx, func_ctx, + FLOAT_ABS + opcode + - WASM_OP_F32_ABS)) + return false; + break; + + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + if (!aot_compile_op_f32_arithmetic(comp_ctx, func_ctx, + FLOAT_ADD + opcode + - WASM_OP_F32_ADD)) + return false; + break; + + case WASM_OP_F32_COPYSIGN: + if (!aot_compile_op_f32_copysign(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + if (!aot_compile_op_f64_math(comp_ctx, func_ctx, + FLOAT_ABS + opcode + - WASM_OP_F64_ABS)) + return false; + break; + + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + if (!aot_compile_op_f64_arithmetic(comp_ctx, func_ctx, + FLOAT_ADD + opcode + - WASM_OP_F64_ADD)) + return false; + break; + + case WASM_OP_F64_COPYSIGN: + if (!aot_compile_op_f64_copysign(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_WRAP_I64: + if (!aot_compile_op_i32_wrap_i64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + sign = (opcode == WASM_OP_I32_TRUNC_S_F32) ? true : false; + if (!aot_compile_op_i32_trunc_f32(comp_ctx, func_ctx, sign, + false)) + return false; + break; + + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + sign = (opcode == WASM_OP_I32_TRUNC_S_F64) ? true : false; + if (!aot_compile_op_i32_trunc_f64(comp_ctx, func_ctx, sign, + false)) + return false; + break; + + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + sign = (opcode == WASM_OP_I64_EXTEND_S_I32) ? true : false; + if (!aot_compile_op_i64_extend_i32(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + sign = (opcode == WASM_OP_I64_TRUNC_S_F32) ? true : false; + if (!aot_compile_op_i64_trunc_f32(comp_ctx, func_ctx, sign, + false)) + return false; + break; + + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + sign = (opcode == WASM_OP_I64_TRUNC_S_F64) ? true : false; + if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign, + false)) + return false; + break; + + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + sign = (opcode == WASM_OP_F32_CONVERT_S_I32) ? true : false; + if (!aot_compile_op_f32_convert_i32(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + sign = (opcode == WASM_OP_F32_CONVERT_S_I64) ? true : false; + if (!aot_compile_op_f32_convert_i64(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F32_DEMOTE_F64: + if (!aot_compile_op_f32_demote_f64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + sign = (opcode == WASM_OP_F64_CONVERT_S_I32) ? true : false; + if (!aot_compile_op_f64_convert_i32(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + sign = (opcode == WASM_OP_F64_CONVERT_S_I64) ? true : false; + if (!aot_compile_op_f64_convert_i64(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F64_PROMOTE_F32: + if (!aot_compile_op_f64_promote_f32(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_REINTERPRET_F32: + if (!aot_compile_op_i32_reinterpret_f32(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_REINTERPRET_F64: + if (!aot_compile_op_i64_reinterpret_f64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F32_REINTERPRET_I32: + if (!aot_compile_op_f32_reinterpret_i32(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F64_REINTERPRET_I64: + if (!aot_compile_op_f64_reinterpret_i64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_EXTEND8_S: + if (!aot_compile_op_i32_extend_i32(comp_ctx, func_ctx, 8)) + return false; + break; + + case WASM_OP_I32_EXTEND16_S: + if (!aot_compile_op_i32_extend_i32(comp_ctx, func_ctx, 16)) + return false; + break; + + case WASM_OP_I64_EXTEND8_S: + if (!aot_compile_op_i64_extend_i64(comp_ctx, func_ctx, 8)) + return false; + break; + + case WASM_OP_I64_EXTEND16_S: + if (!aot_compile_op_i64_extend_i64(comp_ctx, func_ctx, 16)) + return false; + break; -#define CHECK_BUF(buf, buf_end, length) do { \ - if (buf + length > buf_end) { \ - aot_set_last_error("read leb failed: unexpected end."); \ - return false; \ - } \ -} while (0) + case WASM_OP_I64_EXTEND32_S: + if (!aot_compile_op_i64_extend_i64(comp_ctx, func_ctx, 32)) + return false; + break; -static bool -read_leb(const uint8 *buf, const uint8 *buf_end, - uint32 *p_offset, uint32 maxbits, - bool sign, uint64 *p_result) -{ - uint64 result = 0; - uint32 shift = 0; - uint32 bcnt = 0; - uint64 byte; - - while (true) { - CHECK_BUF(buf, buf_end, 1); - byte = buf[*p_offset]; - *p_offset += 1; - result |= ((byte & 0x7f) << shift); - shift += 7; - if ((byte & 0x80) == 0) { - break; - } - bcnt += 1; - } - if (bcnt > (((maxbits + 8) >> 3) - (maxbits + 8))) { - aot_set_last_error("read leb failed: unsigned leb overflow."); + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + if (WASM_OP_MEMORY_COPY <= opcode + && opcode <= WASM_OP_MEMORY_FILL + && !comp_ctx->enable_bulk_memory_opt) { + goto unsupport_bulk_memory_opt; + } +#endif + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (WASM_OP_MEMORY_INIT <= opcode + && opcode <= WASM_OP_MEMORY_FILL + && !comp_ctx->enable_bulk_memory) { + goto unsupport_bulk_memory; + } +#endif + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + if (WASM_OP_TABLE_INIT <= opcode && opcode <= WASM_OP_TABLE_FILL + && (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc)) { + goto unsupport_ref_types; + } +#endif + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + sign = (opcode == WASM_OP_I32_TRUNC_SAT_S_F32) ? true + : false; + if (!aot_compile_op_i32_trunc_f32(comp_ctx, func_ctx, + sign, true)) + return false; + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + sign = (opcode == WASM_OP_I32_TRUNC_SAT_S_F64) ? true + : false; + if (!aot_compile_op_i32_trunc_f64(comp_ctx, func_ctx, + sign, true)) + return false; + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + sign = (opcode == WASM_OP_I64_TRUNC_SAT_S_F32) ? true + : false; + if (!aot_compile_op_i64_trunc_f32(comp_ctx, func_ctx, + sign, true)) + return false; + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + sign = (opcode == WASM_OP_I64_TRUNC_SAT_S_F64) ? true + : false; + if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, + sign, true)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 seg_index; + read_leb_uint32(frame_ip, frame_ip_end, seg_index); + frame_ip++; + if (!aot_compile_op_memory_init(comp_ctx, func_ctx, + seg_index)) + return false; + break; + } + case WASM_OP_DATA_DROP: + { + uint32 seg_index; + read_leb_uint32(frame_ip, frame_ip_end, seg_index); + if (!aot_compile_op_data_drop(comp_ctx, func_ctx, + seg_index)) + return false; + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + { + frame_ip += 2; + if (!aot_compile_op_memory_copy(comp_ctx, func_ctx)) + return false; + break; + } + case WASM_OP_MEMORY_FILL: + { + frame_ip++; + if (!aot_compile_op_memory_fill(comp_ctx, func_ctx)) + return false; + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx, tbl_seg_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_seg_idx); + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_init(comp_ctx, func_ctx, + tbl_idx, tbl_seg_idx)) + return false; + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 tbl_seg_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_seg_idx); + if (!aot_compile_op_elem_drop(comp_ctx, func_ctx, + tbl_seg_idx)) + return false; + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, dst_tbl_idx); + read_leb_uint32(frame_ip, frame_ip_end, src_tbl_idx); + if (!aot_compile_op_table_copy( + comp_ctx, func_ctx, src_tbl_idx, dst_tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_GROW: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_grow(comp_ctx, func_ctx, + tbl_idx)) + return false; + break; + } + + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_size(comp_ctx, func_ctx, + tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_fill(comp_ctx, func_ctx, + tbl_idx)) + return false; + break; + } +#endif /* WASM_ENABLE_REF_TYPES || WASM_ENABLE_GC */ + default: + aot_set_last_error("unsupported opcode"); + return false; + } + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + uint8 bin_op, op_type; + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + if (opcode != WASM_OP_ATOMIC_FENCE) { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + } + switch (opcode) { + case WASM_OP_ATOMIC_WAIT32: + if (!aot_compile_op_atomic_wait(comp_ctx, func_ctx, + VALUE_TYPE_I32, align, + offset, 4)) + return false; + break; + case WASM_OP_ATOMIC_WAIT64: + if (!aot_compile_op_atomic_wait(comp_ctx, func_ctx, + VALUE_TYPE_I64, align, + offset, 8)) + return false; + break; + case WASM_OP_ATOMIC_NOTIFY: + if (!aot_compiler_op_atomic_notify( + comp_ctx, func_ctx, align, offset, bytes)) + return false; + break; + 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; + goto op_atomic_i32_load; + case WASM_OP_ATOMIC_I32_LOAD8_U: + bytes = 1; + goto op_atomic_i32_load; + case WASM_OP_ATOMIC_I32_LOAD16_U: + bytes = 2; + op_atomic_i32_load: + if (!aot_compile_op_i32_load(comp_ctx, func_ctx, align, + offset, bytes, sign, true)) + return false; + break; + + case WASM_OP_ATOMIC_I64_LOAD: + bytes = 8; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD8_U: + bytes = 1; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD16_U: + bytes = 2; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD32_U: + bytes = 4; + op_atomic_i64_load: + if (!aot_compile_op_i64_load(comp_ctx, func_ctx, align, + offset, bytes, sign, true)) + return false; + break; + + case WASM_OP_ATOMIC_I32_STORE: + bytes = 4; + goto op_atomic_i32_store; + case WASM_OP_ATOMIC_I32_STORE8: + bytes = 1; + goto op_atomic_i32_store; + case WASM_OP_ATOMIC_I32_STORE16: + bytes = 2; + op_atomic_i32_store: + if (!aot_compile_op_i32_store(comp_ctx, func_ctx, align, + offset, bytes, true)) + return false; + break; + + case WASM_OP_ATOMIC_I64_STORE: + bytes = 8; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE8: + bytes = 1; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE16: + bytes = 2; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE32: + bytes = 4; + op_atomic_i64_store: + if (!aot_compile_op_i64_store(comp_ctx, func_ctx, align, + offset, bytes, true)) + return false; + break; + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + bytes = 4; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + bytes = 8; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + bytes = 1; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + bytes = 2; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + bytes = 1; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + bytes = 2; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + bytes = 4; + op_type = VALUE_TYPE_I64; + op_atomic_cmpxchg: + if (!aot_compile_op_atomic_cmpxchg(comp_ctx, func_ctx, + op_type, align, + offset, bytes)) + return false; + break; + + COMPILE_ATOMIC_RMW(Add, ADD); + COMPILE_ATOMIC_RMW(Sub, SUB); + COMPILE_ATOMIC_RMW(And, AND); + COMPILE_ATOMIC_RMW(Or, OR); + COMPILE_ATOMIC_RMW(Xor, XOR); + COMPILE_ATOMIC_RMW(Xchg, XCHG); + + build_atomic_rmw: + if (!aot_compile_op_atomic_rmw(comp_ctx, func_ctx, + bin_op, op_type, align, + offset, bytes)) + return false; + break; + + default: + aot_set_last_error("unsupported opcode"); + return false; + } + break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + +#if WASM_ENABLE_SIMD != 0 + case WASM_OP_SIMD_PREFIX: + { + uint32 opcode1; + + if (!comp_ctx->enable_simd) { + goto unsupport_simd; + } + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + /* follow the order of enum WASMSimdEXTOpcode in + wasm_opcode.h */ + switch (opcode) { + /* Memory instruction */ + case SIMD_v128_load: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_v128_load(comp_ctx, func_ctx, + align, offset)) + return false; + break; + } + + case SIMD_v128_load8x8_s: + case SIMD_v128_load8x8_u: + case SIMD_v128_load16x4_s: + case SIMD_v128_load16x4_u: + case SIMD_v128_load32x2_s: + case SIMD_v128_load32x2_u: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_load_extend( + comp_ctx, func_ctx, opcode, align, offset)) + return false; + break; + } + + case SIMD_v128_load8_splat: + case SIMD_v128_load16_splat: + case SIMD_v128_load32_splat: + case SIMD_v128_load64_splat: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_load_splat(comp_ctx, func_ctx, + opcode, align, offset)) + return false; + break; + } + + case SIMD_v128_store: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_v128_store(comp_ctx, func_ctx, + align, offset)) + return false; + break; + } + + /* Basic operation */ + case SIMD_v128_const: + { + if (!aot_compile_simd_v128_const(comp_ctx, func_ctx, + frame_ip)) + return false; + frame_ip += 16; + break; + } + + case SIMD_v8x16_shuffle: + { + if (!aot_compile_simd_shuffle(comp_ctx, func_ctx, + frame_ip)) + return false; + frame_ip += 16; + break; + } + + case SIMD_v8x16_swizzle: + { + if (!aot_compile_simd_swizzle(comp_ctx, func_ctx)) + return false; + break; + } + + /* Splat operation */ + case SIMD_i8x16_splat: + case SIMD_i16x8_splat: + case SIMD_i32x4_splat: + case SIMD_i64x2_splat: + case SIMD_f32x4_splat: + case SIMD_f64x2_splat: + { + if (!aot_compile_simd_splat(comp_ctx, func_ctx, opcode)) + return false; + break; + } + + /* Lane operation */ + case SIMD_i8x16_extract_lane_s: + case SIMD_i8x16_extract_lane_u: + { + if (!aot_compile_simd_extract_i8x16( + comp_ctx, func_ctx, *frame_ip++, + SIMD_i8x16_extract_lane_s == opcode)) + return false; + break; + } + + case SIMD_i8x16_replace_lane: + { + if (!aot_compile_simd_replace_i8x16(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_i16x8_extract_lane_s: + case SIMD_i16x8_extract_lane_u: + { + if (!aot_compile_simd_extract_i16x8( + comp_ctx, func_ctx, *frame_ip++, + SIMD_i16x8_extract_lane_s == opcode)) + return false; + break; + } + + case SIMD_i16x8_replace_lane: + { + if (!aot_compile_simd_replace_i16x8(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_i32x4_extract_lane: + { + if (!aot_compile_simd_extract_i32x4(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_i32x4_replace_lane: + { + if (!aot_compile_simd_replace_i32x4(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_i64x2_extract_lane: + { + if (!aot_compile_simd_extract_i64x2(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_i64x2_replace_lane: + { + if (!aot_compile_simd_replace_i64x2(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_f32x4_extract_lane: + { + if (!aot_compile_simd_extract_f32x4(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_f32x4_replace_lane: + { + if (!aot_compile_simd_replace_f32x4(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_f64x2_extract_lane: + { + if (!aot_compile_simd_extract_f64x2(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + case SIMD_f64x2_replace_lane: + { + if (!aot_compile_simd_replace_f64x2(comp_ctx, func_ctx, + *frame_ip++)) + return false; + break; + } + + /* i8x16 Cmp */ + case SIMD_i8x16_eq: + case SIMD_i8x16_ne: + case SIMD_i8x16_lt_s: + case SIMD_i8x16_lt_u: + case SIMD_i8x16_gt_s: + case SIMD_i8x16_gt_u: + case SIMD_i8x16_le_s: + case SIMD_i8x16_le_u: + case SIMD_i8x16_ge_s: + case SIMD_i8x16_ge_u: + { + if (!aot_compile_simd_i8x16_compare( + comp_ctx, func_ctx, + INT_EQ + opcode - SIMD_i8x16_eq)) + return false; + break; + } + + /* i16x8 Cmp */ + case SIMD_i16x8_eq: + case SIMD_i16x8_ne: + case SIMD_i16x8_lt_s: + case SIMD_i16x8_lt_u: + case SIMD_i16x8_gt_s: + case SIMD_i16x8_gt_u: + case SIMD_i16x8_le_s: + case SIMD_i16x8_le_u: + case SIMD_i16x8_ge_s: + case SIMD_i16x8_ge_u: + { + if (!aot_compile_simd_i16x8_compare( + comp_ctx, func_ctx, + INT_EQ + opcode - SIMD_i16x8_eq)) + return false; + break; + } + + /* i32x4 Cmp */ + case SIMD_i32x4_eq: + case SIMD_i32x4_ne: + case SIMD_i32x4_lt_s: + case SIMD_i32x4_lt_u: + case SIMD_i32x4_gt_s: + case SIMD_i32x4_gt_u: + case SIMD_i32x4_le_s: + case SIMD_i32x4_le_u: + case SIMD_i32x4_ge_s: + case SIMD_i32x4_ge_u: + { + if (!aot_compile_simd_i32x4_compare( + comp_ctx, func_ctx, + INT_EQ + opcode - SIMD_i32x4_eq)) + return false; + break; + } + + /* f32x4 Cmp */ + case SIMD_f32x4_eq: + case SIMD_f32x4_ne: + case SIMD_f32x4_lt: + case SIMD_f32x4_gt: + case SIMD_f32x4_le: + case SIMD_f32x4_ge: + { + if (!aot_compile_simd_f32x4_compare( + comp_ctx, func_ctx, + FLOAT_EQ + opcode - SIMD_f32x4_eq)) + return false; + break; + } + + /* f64x2 Cmp */ + case SIMD_f64x2_eq: + case SIMD_f64x2_ne: + case SIMD_f64x2_lt: + case SIMD_f64x2_gt: + case SIMD_f64x2_le: + case SIMD_f64x2_ge: + { + if (!aot_compile_simd_f64x2_compare( + comp_ctx, func_ctx, + FLOAT_EQ + opcode - SIMD_f64x2_eq)) + return false; + break; + } + + /* v128 Op */ + case SIMD_v128_not: + case SIMD_v128_and: + case SIMD_v128_andnot: + case SIMD_v128_or: + case SIMD_v128_xor: + case SIMD_v128_bitselect: + { + if (!aot_compile_simd_v128_bitwise(comp_ctx, func_ctx, + V128_NOT + opcode + - SIMD_v128_not)) + return false; + break; + } + + case SIMD_v128_any_true: + { + if (!aot_compile_simd_v128_any_true(comp_ctx, func_ctx)) + return false; + break; + } + + /* Load Lane Op */ + case SIMD_v128_load8_lane: + case SIMD_v128_load16_lane: + case SIMD_v128_load32_lane: + case SIMD_v128_load64_lane: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_load_lane(comp_ctx, func_ctx, + opcode, align, offset, + *frame_ip++)) + return false; + break; + } + + case SIMD_v128_store8_lane: + case SIMD_v128_store16_lane: + case SIMD_v128_store32_lane: + case SIMD_v128_store64_lane: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_store_lane(comp_ctx, func_ctx, + opcode, align, offset, + *frame_ip++)) + return false; + break; + } + + case SIMD_v128_load32_zero: + case SIMD_v128_load64_zero: + { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + if (!aot_compile_simd_load_zero(comp_ctx, func_ctx, + opcode, align, offset)) + return false; + break; + } + + /* Float conversion */ + case SIMD_f32x4_demote_f64x2_zero: + { + if (!aot_compile_simd_f64x2_demote(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f64x2_promote_low_f32x4_zero: + { + if (!aot_compile_simd_f32x4_promote(comp_ctx, func_ctx)) + return false; + break; + } + + /* i8x16 Op */ + case SIMD_i8x16_abs: + { + if (!aot_compile_simd_i8x16_abs(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_neg: + { + if (!aot_compile_simd_i8x16_neg(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_popcnt: + { + if (!aot_compile_simd_i8x16_popcnt(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_all_true: + { + if (!aot_compile_simd_i8x16_all_true(comp_ctx, + func_ctx)) + return false; + break; + } + + case SIMD_i8x16_bitmask: + { + if (!aot_compile_simd_i8x16_bitmask(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_narrow_i16x8_s: + case SIMD_i8x16_narrow_i16x8_u: + { + if (!aot_compile_simd_i8x16_narrow_i16x8( + comp_ctx, func_ctx, + (opcode == SIMD_i8x16_narrow_i16x8_s))) + return false; + break; + } + + case SIMD_f32x4_ceil: + { + if (!aot_compile_simd_f32x4_ceil(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f32x4_floor: + { + if (!aot_compile_simd_f32x4_floor(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f32x4_trunc: + { + if (!aot_compile_simd_f32x4_trunc(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f32x4_nearest: + { + if (!aot_compile_simd_f32x4_nearest(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_shl: + case SIMD_i8x16_shr_s: + case SIMD_i8x16_shr_u: + { + if (!aot_compile_simd_i8x16_shift(comp_ctx, func_ctx, + INT_SHL + opcode + - SIMD_i8x16_shl)) + return false; + break; + } + + case SIMD_i8x16_add: + { + if (!aot_compile_simd_i8x16_arith(comp_ctx, func_ctx, + V128_ADD)) + return false; + break; + } + + case SIMD_i8x16_add_sat_s: + case SIMD_i8x16_add_sat_u: + { + if (!aot_compile_simd_i8x16_saturate( + comp_ctx, func_ctx, V128_ADD, + opcode == SIMD_i8x16_add_sat_s)) + return false; + break; + } + + case SIMD_i8x16_sub: + { + if (!aot_compile_simd_i8x16_arith(comp_ctx, func_ctx, + V128_SUB)) + return false; + break; + } + + case SIMD_i8x16_sub_sat_s: + case SIMD_i8x16_sub_sat_u: + { + if (!aot_compile_simd_i8x16_saturate( + comp_ctx, func_ctx, V128_SUB, + opcode == SIMD_i8x16_sub_sat_s)) + return false; + break; + } + + case SIMD_f64x2_ceil: + { + if (!aot_compile_simd_f64x2_ceil(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f64x2_floor: + { + if (!aot_compile_simd_f64x2_floor(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_min_s: + case SIMD_i8x16_min_u: + { + if (!aot_compile_simd_i8x16_cmp( + comp_ctx, func_ctx, V128_MIN, + opcode == SIMD_i8x16_min_s)) + return false; + break; + } + + case SIMD_i8x16_max_s: + case SIMD_i8x16_max_u: + { + if (!aot_compile_simd_i8x16_cmp( + comp_ctx, func_ctx, V128_MAX, + opcode == SIMD_i8x16_max_s)) + return false; + break; + } + + case SIMD_f64x2_trunc: + { + if (!aot_compile_simd_f64x2_trunc(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i8x16_avgr_u: + { + if (!aot_compile_simd_i8x16_avgr_u(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i16x8_extadd_pairwise_i8x16_s: + case SIMD_i16x8_extadd_pairwise_i8x16_u: + { + if (!aot_compile_simd_i16x8_extadd_pairwise_i8x16( + comp_ctx, func_ctx, + SIMD_i16x8_extadd_pairwise_i8x16_s == opcode)) + return false; + break; + } + + case SIMD_i32x4_extadd_pairwise_i16x8_s: + case SIMD_i32x4_extadd_pairwise_i16x8_u: + { + if (!aot_compile_simd_i32x4_extadd_pairwise_i16x8( + comp_ctx, func_ctx, + SIMD_i32x4_extadd_pairwise_i16x8_s == opcode)) + return false; + break; + } + + /* i16x8 Op */ + case SIMD_i16x8_abs: + { + if (!aot_compile_simd_i16x8_abs(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i16x8_neg: + { + if (!aot_compile_simd_i16x8_neg(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i16x8_q15mulr_sat_s: + { + if (!aot_compile_simd_i16x8_q15mulr_sat(comp_ctx, + func_ctx)) + return false; + break; + } + + case SIMD_i16x8_all_true: + { + if (!aot_compile_simd_i16x8_all_true(comp_ctx, + func_ctx)) + return false; + break; + } + + case SIMD_i16x8_bitmask: + { + if (!aot_compile_simd_i16x8_bitmask(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i16x8_narrow_i32x4_s: + case SIMD_i16x8_narrow_i32x4_u: + { + if (!aot_compile_simd_i16x8_narrow_i32x4( + comp_ctx, func_ctx, + SIMD_i16x8_narrow_i32x4_s == opcode)) + return false; + break; + } + + case SIMD_i16x8_extend_low_i8x16_s: + case SIMD_i16x8_extend_high_i8x16_s: + { + if (!aot_compile_simd_i16x8_extend_i8x16( + comp_ctx, func_ctx, + SIMD_i16x8_extend_low_i8x16_s == opcode, true)) + return false; + break; + } + + case SIMD_i16x8_extend_low_i8x16_u: + case SIMD_i16x8_extend_high_i8x16_u: + { + if (!aot_compile_simd_i16x8_extend_i8x16( + comp_ctx, func_ctx, + SIMD_i16x8_extend_low_i8x16_u == opcode, false)) + return false; + break; + } + + case SIMD_i16x8_shl: + case SIMD_i16x8_shr_s: + case SIMD_i16x8_shr_u: + { + if (!aot_compile_simd_i16x8_shift(comp_ctx, func_ctx, + INT_SHL + opcode + - SIMD_i16x8_shl)) + return false; + break; + } + + case SIMD_i16x8_add: + { + if (!aot_compile_simd_i16x8_arith(comp_ctx, func_ctx, + V128_ADD)) + return false; + break; + } + + case SIMD_i16x8_add_sat_s: + case SIMD_i16x8_add_sat_u: + { + if (!aot_compile_simd_i16x8_saturate( + comp_ctx, func_ctx, V128_ADD, + opcode == SIMD_i16x8_add_sat_s ? true : false)) + return false; + break; + } + + case SIMD_i16x8_sub: + { + if (!aot_compile_simd_i16x8_arith(comp_ctx, func_ctx, + V128_SUB)) + return false; + break; + } + + case SIMD_i16x8_sub_sat_s: + case SIMD_i16x8_sub_sat_u: + { + if (!aot_compile_simd_i16x8_saturate( + comp_ctx, func_ctx, V128_SUB, + opcode == SIMD_i16x8_sub_sat_s ? true : false)) + return false; + break; + } + + case SIMD_f64x2_nearest: + { + if (!aot_compile_simd_f64x2_nearest(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i16x8_mul: + { + if (!aot_compile_simd_i16x8_arith(comp_ctx, func_ctx, + V128_MUL)) + return false; + break; + } + + case SIMD_i16x8_min_s: + case SIMD_i16x8_min_u: + { + if (!aot_compile_simd_i16x8_cmp( + comp_ctx, func_ctx, V128_MIN, + opcode == SIMD_i16x8_min_s)) + return false; + break; + } + + case SIMD_i16x8_max_s: + case SIMD_i16x8_max_u: + { + if (!aot_compile_simd_i16x8_cmp( + comp_ctx, func_ctx, V128_MAX, + opcode == SIMD_i16x8_max_s)) + return false; + break; + } + + case SIMD_i16x8_avgr_u: + { + if (!aot_compile_simd_i16x8_avgr_u(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i16x8_extmul_low_i8x16_s: + case SIMD_i16x8_extmul_high_i8x16_s: + { + if (!(aot_compile_simd_i16x8_extmul_i8x16( + comp_ctx, func_ctx, + SIMD_i16x8_extmul_low_i8x16_s == opcode, true))) + return false; + break; + } + + case SIMD_i16x8_extmul_low_i8x16_u: + case SIMD_i16x8_extmul_high_i8x16_u: + { + if (!(aot_compile_simd_i16x8_extmul_i8x16( + comp_ctx, func_ctx, + SIMD_i16x8_extmul_low_i8x16_u == opcode, + false))) + return false; + break; + } + + /* i32x4 Op */ + case SIMD_i32x4_abs: + { + if (!aot_compile_simd_i32x4_abs(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i32x4_neg: + { + if (!aot_compile_simd_i32x4_neg(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i32x4_all_true: + { + if (!aot_compile_simd_i32x4_all_true(comp_ctx, + func_ctx)) + return false; + break; + } + + case SIMD_i32x4_bitmask: + { + if (!aot_compile_simd_i32x4_bitmask(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i32x4_extend_low_i16x8_s: + case SIMD_i32x4_extend_high_i16x8_s: + { + if (!aot_compile_simd_i32x4_extend_i16x8( + comp_ctx, func_ctx, + SIMD_i32x4_extend_low_i16x8_s == opcode, true)) + return false; + break; + } + + case SIMD_i32x4_extend_low_i16x8_u: + case SIMD_i32x4_extend_high_i16x8_u: + { + if (!aot_compile_simd_i32x4_extend_i16x8( + comp_ctx, func_ctx, + SIMD_i32x4_extend_low_i16x8_u == opcode, false)) + return false; + break; + } + + case SIMD_i32x4_shl: + case SIMD_i32x4_shr_s: + case SIMD_i32x4_shr_u: + { + if (!aot_compile_simd_i32x4_shift(comp_ctx, func_ctx, + INT_SHL + opcode + - SIMD_i32x4_shl)) + return false; + break; + } + + case SIMD_i32x4_add: + { + if (!aot_compile_simd_i32x4_arith(comp_ctx, func_ctx, + V128_ADD)) + return false; + break; + } + + case SIMD_i32x4_sub: + { + if (!aot_compile_simd_i32x4_arith(comp_ctx, func_ctx, + V128_SUB)) + return false; + break; + } + + case SIMD_i32x4_mul: + { + if (!aot_compile_simd_i32x4_arith(comp_ctx, func_ctx, + V128_MUL)) + return false; + break; + } + + case SIMD_i32x4_min_s: + case SIMD_i32x4_min_u: + { + if (!aot_compile_simd_i32x4_cmp( + comp_ctx, func_ctx, V128_MIN, + SIMD_i32x4_min_s == opcode)) + return false; + break; + } + + case SIMD_i32x4_max_s: + case SIMD_i32x4_max_u: + { + if (!aot_compile_simd_i32x4_cmp( + comp_ctx, func_ctx, V128_MAX, + SIMD_i32x4_max_s == opcode)) + return false; + break; + } + + case SIMD_i32x4_dot_i16x8_s: + { + if (!aot_compile_simd_i32x4_dot_i16x8(comp_ctx, + func_ctx)) + return false; + break; + } + + case SIMD_i32x4_extmul_low_i16x8_s: + case SIMD_i32x4_extmul_high_i16x8_s: + { + if (!aot_compile_simd_i32x4_extmul_i16x8( + comp_ctx, func_ctx, + SIMD_i32x4_extmul_low_i16x8_s == opcode, true)) + return false; + break; + } + + case SIMD_i32x4_extmul_low_i16x8_u: + case SIMD_i32x4_extmul_high_i16x8_u: + { + if (!aot_compile_simd_i32x4_extmul_i16x8( + comp_ctx, func_ctx, + SIMD_i32x4_extmul_low_i16x8_u == opcode, false)) + return false; + break; + } + + /* i64x2 Op */ + case SIMD_i64x2_abs: + { + if (!aot_compile_simd_i64x2_abs(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i64x2_neg: + { + if (!aot_compile_simd_i64x2_neg(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i64x2_all_true: + { + if (!aot_compile_simd_i64x2_all_true(comp_ctx, + func_ctx)) + return false; + break; + } + + case SIMD_i64x2_bitmask: + { + if (!aot_compile_simd_i64x2_bitmask(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_i64x2_extend_low_i32x4_s: + case SIMD_i64x2_extend_high_i32x4_s: + { + if (!aot_compile_simd_i64x2_extend_i32x4( + comp_ctx, func_ctx, + SIMD_i64x2_extend_low_i32x4_s == opcode, true)) + return false; + break; + } + + case SIMD_i64x2_extend_low_i32x4_u: + case SIMD_i64x2_extend_high_i32x4_u: + { + if (!aot_compile_simd_i64x2_extend_i32x4( + comp_ctx, func_ctx, + SIMD_i64x2_extend_low_i32x4_u == opcode, false)) + return false; + break; + } + + case SIMD_i64x2_shl: + case SIMD_i64x2_shr_s: + case SIMD_i64x2_shr_u: + { + if (!aot_compile_simd_i64x2_shift(comp_ctx, func_ctx, + INT_SHL + opcode + - SIMD_i64x2_shl)) + return false; + break; + } + + case SIMD_i64x2_add: + { + if (!aot_compile_simd_i64x2_arith(comp_ctx, func_ctx, + V128_ADD)) + return false; + break; + } + + case SIMD_i64x2_sub: + { + if (!aot_compile_simd_i64x2_arith(comp_ctx, func_ctx, + V128_SUB)) + return false; + break; + } + + case SIMD_i64x2_mul: + { + if (!aot_compile_simd_i64x2_arith(comp_ctx, func_ctx, + V128_MUL)) + return false; + break; + } + + case SIMD_i64x2_eq: + case SIMD_i64x2_ne: + case SIMD_i64x2_lt_s: + case SIMD_i64x2_gt_s: + case SIMD_i64x2_le_s: + case SIMD_i64x2_ge_s: + { + IntCond icond[] = { INT_EQ, INT_NE, INT_LT_S, + INT_GT_S, INT_LE_S, INT_GE_S }; + if (!aot_compile_simd_i64x2_compare( + comp_ctx, func_ctx, + icond[opcode - SIMD_i64x2_eq])) + return false; + break; + } + + case SIMD_i64x2_extmul_low_i32x4_s: + case SIMD_i64x2_extmul_high_i32x4_s: + { + if (!aot_compile_simd_i64x2_extmul_i32x4( + comp_ctx, func_ctx, + SIMD_i64x2_extmul_low_i32x4_s == opcode, true)) + return false; + break; + } + + case SIMD_i64x2_extmul_low_i32x4_u: + case SIMD_i64x2_extmul_high_i32x4_u: + { + if (!aot_compile_simd_i64x2_extmul_i32x4( + comp_ctx, func_ctx, + SIMD_i64x2_extmul_low_i32x4_u == opcode, false)) + return false; + break; + } + + /* f32x4 Op */ + case SIMD_f32x4_abs: + { + if (!aot_compile_simd_f32x4_abs(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f32x4_neg: + { + if (!aot_compile_simd_f32x4_neg(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f32x4_sqrt: + { + if (!aot_compile_simd_f32x4_sqrt(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f32x4_add: + case SIMD_f32x4_sub: + case SIMD_f32x4_mul: + case SIMD_f32x4_div: + { + if (!aot_compile_simd_f32x4_arith(comp_ctx, func_ctx, + FLOAT_ADD + opcode + - SIMD_f32x4_add)) + return false; + break; + } + + case SIMD_f32x4_min: + case SIMD_f32x4_max: + { + if (!aot_compile_simd_f32x4_min_max( + comp_ctx, func_ctx, SIMD_f32x4_min == opcode)) + return false; + break; + } + + case SIMD_f32x4_pmin: + case SIMD_f32x4_pmax: + { + if (!aot_compile_simd_f32x4_pmin_pmax( + comp_ctx, func_ctx, SIMD_f32x4_pmin == opcode)) + return false; + break; + } + + /* f64x2 Op */ + + case SIMD_f64x2_abs: + { + if (!aot_compile_simd_f64x2_abs(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f64x2_neg: + { + if (!aot_compile_simd_f64x2_neg(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f64x2_sqrt: + { + if (!aot_compile_simd_f64x2_sqrt(comp_ctx, func_ctx)) + return false; + break; + } + + case SIMD_f64x2_add: + case SIMD_f64x2_sub: + case SIMD_f64x2_mul: + case SIMD_f64x2_div: + { + if (!aot_compile_simd_f64x2_arith(comp_ctx, func_ctx, + FLOAT_ADD + opcode + - SIMD_f64x2_add)) + return false; + break; + } + + case SIMD_f64x2_min: + case SIMD_f64x2_max: + { + if (!aot_compile_simd_f64x2_min_max( + comp_ctx, func_ctx, SIMD_f64x2_min == opcode)) + return false; + break; + } + + case SIMD_f64x2_pmin: + case SIMD_f64x2_pmax: + { + if (!aot_compile_simd_f64x2_pmin_pmax( + comp_ctx, func_ctx, SIMD_f64x2_pmin == opcode)) + return false; + break; + } + + /* Conversion Op */ + case SIMD_i32x4_trunc_sat_f32x4_s: + case SIMD_i32x4_trunc_sat_f32x4_u: + { + if (!aot_compile_simd_i32x4_trunc_sat_f32x4( + comp_ctx, func_ctx, + SIMD_i32x4_trunc_sat_f32x4_s == opcode)) + return false; + break; + } + + case SIMD_f32x4_convert_i32x4_s: + case SIMD_f32x4_convert_i32x4_u: + { + if (!aot_compile_simd_f32x4_convert_i32x4( + comp_ctx, func_ctx, + SIMD_f32x4_convert_i32x4_s == opcode)) + return false; + break; + } + + case SIMD_i32x4_trunc_sat_f64x2_s_zero: + case SIMD_i32x4_trunc_sat_f64x2_u_zero: + { + if (!aot_compile_simd_i32x4_trunc_sat_f64x2( + comp_ctx, func_ctx, + SIMD_i32x4_trunc_sat_f64x2_s_zero == opcode)) + return false; + break; + } + + case SIMD_f64x2_convert_low_i32x4_s: + case SIMD_f64x2_convert_low_i32x4_u: + { + if (!aot_compile_simd_f64x2_convert_i32x4( + comp_ctx, func_ctx, + SIMD_f64x2_convert_low_i32x4_s == opcode)) + return false; + break; + } + + default: + aot_set_last_error("unsupported SIMD opcode"); + return false; + } + break; + } +#endif /* end of WASM_ENABLE_SIMD */ + + default: + aot_set_last_error("unsupported opcode"); + return false; + } + } + + /* Move func_return block to the bottom */ + if (func_ctx->func_return_block) { + LLVMBasicBlockRef last_block = LLVMGetLastBasicBlock(func_ctx->func); + if (last_block != func_ctx->func_return_block) + LLVMMoveBasicBlockAfter(func_ctx->func_return_block, last_block); + } + + /* Move got_exception block to the bottom */ + if (func_ctx->got_exception_block) { + LLVMBasicBlockRef last_block = LLVMGetLastBasicBlock(func_ctx->func); + if (last_block != func_ctx->got_exception_block) + LLVMMoveBasicBlockAfter(func_ctx->got_exception_block, last_block); + } + return true; + +#if WASM_ENABLE_SIMD != 0 +unsupport_simd: + aot_set_last_error("SIMD instruction was found, " + "try removing --disable-simd option"); return false; - } - if (sign && (shift < maxbits) && (byte & 0x40)) { - /* Sign extend */ - result |= (uint64)(- (((uint64)1) << shift)); - } - *p_result = result; - return true; -} +#endif + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +unsupport_ref_types: + aot_set_last_error("reference type instruction was found, " + "try removing --disable-ref-types option " + "or adding --enable-gc option"); + return false; +#endif + +#if WASM_ENABLE_GC != 0 +unsupport_gc: + aot_set_last_error("GC instruction was found, " + "try adding --enable-gc option"); + return false; +#endif + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +unsupport_gc_and_ref_types: + aot_set_last_error( + "reference type or gc instruction was found, try removing " + "--disable-ref-types option or adding --enable-gc option"); + return false; +#endif + +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 +unsupport_bulk_memory_opt: + aot_set_last_error("bulk memory opt instruction was found, " + "try enabling bulk-memory-opt or bulk-memory option"); + return false; +#endif + +#if WASM_ENABLE_BULK_MEMORY != 0 +unsupport_bulk_memory: + aot_set_last_error("bulk memory instruction was found, " + "try removing --disable-bulk-memory option"); + return false; +#endif -#define read_leb_uint32(p, p_end, res) do { \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 32, false, &res64)) \ - return false; \ - p += off; \ - res = (uint32)res64; \ -} while (0) - -#define read_leb_int32(p, p_end, res) do { \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 32, true, &res64)) \ - return false; \ - p += off; \ - res = (int32)res64; \ -} while (0) - -#define read_leb_int64(p, p_end, res) do { \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 64, true, &res64)) \ - return false; \ - p += off; \ - res = (int64)res64; \ -} while (0) +fail: + return false; +} static bool -aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) +verify_module(AOTCompContext *comp_ctx) { - AOTFuncContext *func_ctx = comp_ctx->func_ctxes[func_index]; - uint8 *frame_ip = func_ctx->aot_func->code, opcode, *p_f32, *p_f64; - uint8 *frame_ip_end = frame_ip + func_ctx->aot_func->code_size; - uint32 block_ret_type, br_depth, *br_depths, br_count; - uint32 func_idx, type_idx, mem_idx, local_idx, global_idx, i; - uint32 bytes = 4, align, offset; - bool sign = true; - int32 i32_const; - int64 i64_const; - float32 f32_const; - float64 f64_const; - - /* Start to translate the opcodes */ - LLVMPositionBuilderAtEnd(comp_ctx->builder, - func_ctx->block_stack.block_list_head - ->llvm_entry_block); - while (frame_ip < frame_ip_end) { - opcode = *frame_ip++; - switch (opcode) { - case WASM_OP_UNREACHABLE: - if (!aot_compile_op_unreachable(comp_ctx, func_ctx, &frame_ip)) - return false; - break; - - case WASM_OP_NOP: - break; - - case WASM_OP_BLOCK: - case WASM_OP_LOOP: - case WASM_OP_IF: - read_leb_uint32(frame_ip, frame_ip_end, block_ret_type); - if (!aot_compile_op_block(comp_ctx, func_ctx, - &frame_ip, frame_ip_end, - (uint32)(BLOCK_TYPE_BLOCK + opcode - WASM_OP_BLOCK), - block_ret_type)) - return false; - break; - - case WASM_OP_ELSE: - if (!aot_compile_op_else(comp_ctx, func_ctx, &frame_ip)) - return false; - break; - - case WASM_OP_END: - if (!aot_compile_op_end(comp_ctx, func_ctx, &frame_ip)) - return false; - break; - - case WASM_OP_BR: - read_leb_uint32(frame_ip, frame_ip_end, br_depth); - if (!aot_compile_op_br(comp_ctx, func_ctx, br_depth, &frame_ip)) - return false; - break; - - case WASM_OP_BR_IF: - read_leb_uint32(frame_ip, frame_ip_end, br_depth); - if (!aot_compile_op_br_if(comp_ctx, func_ctx, br_depth, &frame_ip)) - return false; - break; - - case WASM_OP_BR_TABLE: - read_leb_uint32(frame_ip, frame_ip_end, br_count); - if (!(br_depths = wasm_malloc((uint32)sizeof(uint32) * (br_count + 1)))) { - aot_set_last_error("allocate memory failed."); - goto fail; - } - for (i = 0; i <= br_count; i++) - read_leb_uint32(frame_ip, frame_ip_end, br_depths[i]); - - if (!aot_compile_op_br_table(comp_ctx, func_ctx, - br_depths, br_count, &frame_ip)) { - wasm_free(br_depths); - return false; - } - - wasm_free(br_depths); - break; - - case WASM_OP_RETURN: - if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) - return false; - break; - - case WASM_OP_CALL: - read_leb_uint32(frame_ip, frame_ip_end, func_idx); - if (!aot_compile_op_call(comp_ctx, func_ctx, func_idx, &frame_ip)) - return false; - break; - - case WASM_OP_CALL_INDIRECT: - read_leb_uint32(frame_ip, frame_ip_end, type_idx); - frame_ip++; /* skip 0x00 */ - if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx)) - return false; - break; - - case WASM_OP_DROP: - if (!aot_compile_op_drop(comp_ctx, func_ctx, true)) + char *msg = NULL; + bool ret; + + ret = LLVMVerifyModule(comp_ctx->module, LLVMPrintMessageAction, &msg); + if (!ret && msg) { + if (msg[0] != '\0') { + aot_set_last_error(msg); + LLVMDisposeMessage(msg); return false; - break; + } + LLVMDisposeMessage(msg); + } + + return true; +} + +bool +aot_compile_wasm(AOTCompContext *comp_ctx) +{ + uint32 i; + + if (!aot_validate_wasm(comp_ctx)) { + return false; + } - case WASM_OP_DROP_64: - if (!aot_compile_op_drop(comp_ctx, func_ctx, false)) + bh_print_time("Begin to compile WASM bytecode to LLVM IR"); + for (i = 0; i < comp_ctx->func_ctx_count; i++) { + if (!aot_compile_func(comp_ctx, i)) { return false; - break; - - case WASM_OP_SELECT: - if (!aot_compile_op_select(comp_ctx, func_ctx, true)) - return false; - break; - - case WASM_OP_SELECT_64: - if (!aot_compile_op_select(comp_ctx, func_ctx, false)) - return false; - break; - - case WASM_OP_GET_LOCAL: - read_leb_uint32(frame_ip, frame_ip_end, local_idx); - if (!aot_compile_op_get_local(comp_ctx, func_ctx, local_idx)) - return false; - break; - - case WASM_OP_SET_LOCAL: - read_leb_uint32(frame_ip, frame_ip_end, local_idx); - if (!aot_compile_op_set_local(comp_ctx, func_ctx, local_idx)) - return false; - break; - - case WASM_OP_TEE_LOCAL: - read_leb_uint32(frame_ip, frame_ip_end, local_idx); - if (!aot_compile_op_tee_local(comp_ctx, func_ctx, local_idx)) - return false; - break; - - case WASM_OP_GET_GLOBAL: - read_leb_uint32(frame_ip, frame_ip_end, global_idx); - if (!aot_compile_op_get_global(comp_ctx, func_ctx, global_idx)) - return false; - break; - - case WASM_OP_SET_GLOBAL: - read_leb_uint32(frame_ip, frame_ip_end, global_idx); - if (!aot_compile_op_set_global(comp_ctx, func_ctx, global_idx)) - return false; - break; - - case WASM_OP_I32_LOAD: - bytes = 4; - sign = true; - goto op_i32_load; - case WASM_OP_I32_LOAD8_S: - case WASM_OP_I32_LOAD8_U: - bytes = 1; - sign = (opcode == WASM_OP_I32_LOAD8_S) ? true : false; - goto op_i32_load; - case WASM_OP_I32_LOAD16_S: - case WASM_OP_I32_LOAD16_U: - bytes = 2; - sign = (opcode == WASM_OP_I32_LOAD16_S) ? true : false; - op_i32_load: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_i32_load(comp_ctx, func_ctx, align, offset, - bytes, sign)) - return false; - break; - - case WASM_OP_I64_LOAD: - bytes = 8; - sign = true; - goto op_i64_load; - case WASM_OP_I64_LOAD8_S: - case WASM_OP_I64_LOAD8_U: - bytes = 1; - sign = (opcode == WASM_OP_I64_LOAD8_S) ? true : false; - goto op_i64_load; - case WASM_OP_I64_LOAD16_S: - case WASM_OP_I64_LOAD16_U: - bytes = 2; - sign = (opcode == WASM_OP_I64_LOAD16_S) ? true : false; - goto op_i64_load; - case WASM_OP_I64_LOAD32_S: - case WASM_OP_I64_LOAD32_U: - bytes = 4; - sign = (opcode == WASM_OP_I64_LOAD32_S) ? true : false; - op_i64_load: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_i64_load(comp_ctx, func_ctx, align, offset, - bytes, sign)) - return false; - break; - - case WASM_OP_F32_LOAD: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_f32_load(comp_ctx, func_ctx, align, offset)) - return false; - break; - - case WASM_OP_F64_LOAD: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_f64_load(comp_ctx, func_ctx, align, offset)) - return false; - break; - - case WASM_OP_I32_STORE: - bytes = 4; - goto op_i32_store; - case WASM_OP_I32_STORE8: - bytes = 1; - goto op_i32_store; - case WASM_OP_I32_STORE16: - bytes = 2; - op_i32_store: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_i32_store(comp_ctx, func_ctx, align, offset, bytes)) - return false; - break; - - case WASM_OP_I64_STORE: - bytes = 8; - goto op_i64_store; - case WASM_OP_I64_STORE8: - bytes = 1; - goto op_i64_store; - case WASM_OP_I64_STORE16: - bytes = 2; - goto op_i64_store; - case WASM_OP_I64_STORE32: - bytes = 4; - op_i64_store: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_i64_store(comp_ctx, func_ctx, align, offset, bytes)) - return false; - break; - - case WASM_OP_F32_STORE: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_f32_store(comp_ctx, func_ctx, align, offset)) - return false; - break; - - case WASM_OP_F64_STORE: - read_leb_uint32(frame_ip, frame_ip_end, align); - read_leb_uint32(frame_ip, frame_ip_end, offset); - if (!aot_compile_op_f64_store(comp_ctx, func_ctx, align, offset)) - return false; - break; - - case WASM_OP_MEMORY_SIZE: - read_leb_uint32(frame_ip, frame_ip_end, mem_idx); - if (!aot_compile_op_memory_size(comp_ctx, func_ctx)) - return false; - (void)mem_idx; - break; - - case WASM_OP_MEMORY_GROW: - read_leb_uint32(frame_ip, frame_ip_end, mem_idx); - if (!aot_compile_op_memory_grow(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_CONST: - read_leb_int32(frame_ip, frame_ip_end, i32_const); - if (!aot_compile_op_i32_const(comp_ctx, func_ctx, i32_const)) - return false; - break; - - case WASM_OP_I64_CONST: - read_leb_int64(frame_ip, frame_ip_end, i64_const); - if (!aot_compile_op_i64_const(comp_ctx, func_ctx, i64_const)) - return false; - break; - - case WASM_OP_F32_CONST: - p_f32 = (uint8*)&f32_const; - for (i = 0; i < sizeof(float32); i++) - *p_f32++ = *frame_ip++; - if (!aot_compile_op_f32_const(comp_ctx, func_ctx, f32_const)) - return false; - break; - - case WASM_OP_F64_CONST: - p_f64 = (uint8*)&f64_const; - for (i = 0; i < sizeof(float64); i++) - *p_f64++ = *frame_ip++; - if (!aot_compile_op_f64_const(comp_ctx, func_ctx, f64_const)) - return false; - break; - - case WASM_OP_I32_EQZ: - case WASM_OP_I32_EQ: - case WASM_OP_I32_NE: - case WASM_OP_I32_LT_S: - case WASM_OP_I32_LT_U: - case WASM_OP_I32_GT_S: - case WASM_OP_I32_GT_U: - case WASM_OP_I32_LE_S: - case WASM_OP_I32_LE_U: - case WASM_OP_I32_GE_S: - case WASM_OP_I32_GE_U: - if (!aot_compile_op_i32_compare(comp_ctx, func_ctx, - INT_EQZ + opcode - WASM_OP_I32_EQZ)) - return false; - break; - - case WASM_OP_I64_EQZ: - case WASM_OP_I64_EQ: - case WASM_OP_I64_NE: - case WASM_OP_I64_LT_S: - case WASM_OP_I64_LT_U: - case WASM_OP_I64_GT_S: - case WASM_OP_I64_GT_U: - case WASM_OP_I64_LE_S: - case WASM_OP_I64_LE_U: - case WASM_OP_I64_GE_S: - case WASM_OP_I64_GE_U: - if (!aot_compile_op_i64_compare(comp_ctx, func_ctx, - INT_EQZ + opcode - WASM_OP_I64_EQZ)) - return false; - break; - - case WASM_OP_F32_EQ: - case WASM_OP_F32_NE: - case WASM_OP_F32_LT: - case WASM_OP_F32_GT: - case WASM_OP_F32_LE: - case WASM_OP_F32_GE: - if (!aot_compile_op_f32_compare(comp_ctx, func_ctx, - FLOAT_EQ + opcode - WASM_OP_F32_EQ)) - return false; - break; - - case WASM_OP_F64_EQ: - case WASM_OP_F64_NE: - case WASM_OP_F64_LT: - case WASM_OP_F64_GT: - case WASM_OP_F64_LE: - case WASM_OP_F64_GE: - if (!aot_compile_op_f64_compare(comp_ctx, func_ctx, - FLOAT_EQ + opcode - WASM_OP_F64_EQ)) - return false; - break; - - case WASM_OP_I32_CLZ: - if (!aot_compile_op_i32_clz(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_CTZ: - if (!aot_compile_op_i32_ctz(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_POPCNT: - if (!aot_compile_op_i32_popcnt(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_ADD: - case WASM_OP_I32_SUB: - case WASM_OP_I32_MUL: - case WASM_OP_I32_DIV_S: - case WASM_OP_I32_DIV_U: - case WASM_OP_I32_REM_S: - case WASM_OP_I32_REM_U: - if (!aot_compile_op_i32_arithmetic(comp_ctx, func_ctx, - INT_ADD + opcode - WASM_OP_I32_ADD, - &frame_ip)) - return false; - break; - - case WASM_OP_I32_AND: - case WASM_OP_I32_OR: - case WASM_OP_I32_XOR: - if (!aot_compile_op_i32_bitwise(comp_ctx, func_ctx, - INT_SHL + opcode - WASM_OP_I32_AND)) - return false; - break; - - case WASM_OP_I32_SHL: - case WASM_OP_I32_SHR_S: - case WASM_OP_I32_SHR_U: - case WASM_OP_I32_ROTL: - case WASM_OP_I32_ROTR: - if (!aot_compile_op_i32_shift(comp_ctx, func_ctx, - INT_SHL + opcode - WASM_OP_I32_SHL)) - return false; - break; - - case WASM_OP_I64_CLZ: - if (!aot_compile_op_i64_clz(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I64_CTZ: - if (!aot_compile_op_i64_ctz(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I64_POPCNT: - if (!aot_compile_op_i64_popcnt(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I64_ADD: - case WASM_OP_I64_SUB: - case WASM_OP_I64_MUL: - case WASM_OP_I64_DIV_S: - case WASM_OP_I64_DIV_U: - case WASM_OP_I64_REM_S: - case WASM_OP_I64_REM_U: - if (!aot_compile_op_i64_arithmetic(comp_ctx, func_ctx, - INT_ADD + opcode - WASM_OP_I64_ADD, - &frame_ip)) - return false; - break; - - case WASM_OP_I64_AND: - case WASM_OP_I64_OR: - case WASM_OP_I64_XOR: - if (!aot_compile_op_i64_bitwise(comp_ctx, func_ctx, - INT_SHL + opcode - WASM_OP_I64_AND)) - return false; - break; - - case WASM_OP_I64_SHL: - case WASM_OP_I64_SHR_S: - case WASM_OP_I64_SHR_U: - case WASM_OP_I64_ROTL: - case WASM_OP_I64_ROTR: - if (!aot_compile_op_i64_shift(comp_ctx, func_ctx, - INT_SHL + opcode - WASM_OP_I64_SHL)) - return false; - break; - - case WASM_OP_F32_ABS: - case WASM_OP_F32_NEG: - case WASM_OP_F32_CEIL: - case WASM_OP_F32_FLOOR: - case WASM_OP_F32_TRUNC: - case WASM_OP_F32_NEAREST: - case WASM_OP_F32_SQRT: - if (!aot_compile_op_f32_math(comp_ctx, func_ctx, - FLOAT_ABS + opcode - WASM_OP_F32_ABS)) - return false; - break; - - case WASM_OP_F32_ADD: - case WASM_OP_F32_SUB: - case WASM_OP_F32_MUL: - case WASM_OP_F32_DIV: - case WASM_OP_F32_MIN: - case WASM_OP_F32_MAX: - if (!aot_compile_op_f32_arithmetic(comp_ctx, func_ctx, - FLOAT_ADD + opcode - WASM_OP_F32_ADD)) - return false; - break; - - case WASM_OP_F32_COPYSIGN: - if (!aot_compile_op_f32_copysign(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_F64_ABS: - case WASM_OP_F64_NEG: - case WASM_OP_F64_CEIL: - case WASM_OP_F64_FLOOR: - case WASM_OP_F64_TRUNC: - case WASM_OP_F64_NEAREST: - case WASM_OP_F64_SQRT: - if (!aot_compile_op_f64_math(comp_ctx, func_ctx, - FLOAT_ABS + opcode - WASM_OP_F64_ABS)) - return false; - break; - - case WASM_OP_F64_ADD: - case WASM_OP_F64_SUB: - case WASM_OP_F64_MUL: - case WASM_OP_F64_DIV: - case WASM_OP_F64_MIN: - case WASM_OP_F64_MAX: - if (!aot_compile_op_f64_arithmetic(comp_ctx, func_ctx, - FLOAT_ADD + opcode - WASM_OP_F64_ADD)) - return false; - break; - - case WASM_OP_F64_COPYSIGN: - if (!aot_compile_op_f64_copysign(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_WRAP_I64: - if (!aot_compile_op_i32_wrap_i64(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_TRUNC_S_F32: - case WASM_OP_I32_TRUNC_U_F32: - sign = (opcode == WASM_OP_I32_TRUNC_S_F32) ? true : false; - if (!aot_compile_op_i32_trunc_f32(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_I32_TRUNC_S_F64: - case WASM_OP_I32_TRUNC_U_F64: - sign = (opcode == WASM_OP_I32_TRUNC_S_F64) ? true : false; - if (!aot_compile_op_i32_trunc_f64(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_I64_EXTEND_S_I32: - case WASM_OP_I64_EXTEND_U_I32: - sign = (opcode == WASM_OP_I64_EXTEND_S_I32) ? true : false; - if (!aot_compile_op_i64_extend_i32(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_I64_TRUNC_S_F32: - case WASM_OP_I64_TRUNC_U_F32: - sign = (opcode == WASM_OP_I64_TRUNC_S_F32) ? true : false; - if (!aot_compile_op_i64_trunc_f32(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_I64_TRUNC_S_F64: - case WASM_OP_I64_TRUNC_U_F64: - sign = (opcode == WASM_OP_I64_TRUNC_S_F64) ? true : false; - if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_F32_CONVERT_S_I32: - case WASM_OP_F32_CONVERT_U_I32: - sign = (opcode == WASM_OP_F32_CONVERT_S_I32) ? true : false; - if (!aot_compile_op_f32_convert_i32(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_F32_CONVERT_S_I64: - case WASM_OP_F32_CONVERT_U_I64: - sign = (opcode == WASM_OP_F32_CONVERT_S_I64) ? true : false; - if (!aot_compile_op_f32_convert_i64(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_F32_DEMOTE_F64: - if (!aot_compile_op_f32_demote_f64(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_F64_CONVERT_S_I32: - case WASM_OP_F64_CONVERT_U_I32: - sign = (opcode == WASM_OP_F64_CONVERT_S_I32) ? true : false; - if (!aot_compile_op_f64_convert_i32(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_F64_CONVERT_S_I64: - case WASM_OP_F64_CONVERT_U_I64: - sign = (opcode == WASM_OP_F64_CONVERT_S_I64) ? true : false; - if (!aot_compile_op_f64_convert_i64(comp_ctx, func_ctx, sign)) - return false; - break; - - case WASM_OP_F64_PROMOTE_F32: - if (!aot_compile_op_f64_promote_f32(comp_ctx, func_ctx)) - return false; - break; - - case WASM_OP_I32_REINTERPRET_F32: - if (!aot_compile_op_i32_reinterpret_f32(comp_ctx, func_ctx)) + } + } + +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMDIBuilderFinalize(comp_ctx->debug_builder); +#endif + + /* Disable LLVM module verification for jit mode to speedup + the compilation process */ + if (!comp_ctx->is_jit_mode) { + bh_print_time("Begin to verify LLVM module"); + if (!verify_module(comp_ctx)) { return false; - break; + } + } + + /* Run IR optimization before feeding in ORCJIT and AOT codegen */ + if (comp_ctx->optimize) { + /* Run passes for AOT/JIT mode. + TODO: Apply these passes in the do_ir_transform callback of + TransformLayer when compiling each jit function, so as to + speedup the launch process. Now there are two issues in the + JIT: one is memory leak in do_ir_transform, the other is + possible core dump. */ + bh_print_time("Begin to run llvm optimization passes"); + aot_apply_llvm_new_pass_manager(comp_ctx, comp_ctx->module); + bh_print_time("Finish llvm optimization passes"); + } + +#ifdef DUMP_MODULE + LLVMDumpModule(comp_ctx->module); + os_printf("\n"); +#endif + + if (comp_ctx->is_jit_mode) { + LLVMErrorRef err; + LLVMOrcJITDylibRef orc_main_dylib; + LLVMOrcThreadSafeModuleRef orc_thread_safe_module; - case WASM_OP_I64_REINTERPRET_F64: - if (!aot_compile_op_i64_reinterpret_f64(comp_ctx, func_ctx)) + orc_main_dylib = LLVMOrcLLLazyJITGetMainJITDylib(comp_ctx->orc_jit); + if (!orc_main_dylib) { + aot_set_last_error( + "failed to get orc orc_jit main dynamic library"); return false; - break; + } - case WASM_OP_F32_REINTERPRET_I32: - if (!aot_compile_op_f32_reinterpret_i32(comp_ctx, func_ctx)) + orc_thread_safe_module = LLVMOrcCreateNewThreadSafeModule( + comp_ctx->module, comp_ctx->orc_thread_safe_context); + if (!orc_thread_safe_module) { + aot_set_last_error("failed to create thread safe module"); return false; - break; + } - case WASM_OP_F64_REINTERPRET_I64: - if (!aot_compile_op_f64_reinterpret_i64(comp_ctx, func_ctx)) + if ((err = LLVMOrcLLLazyJITAddLLVMIRModule( + comp_ctx->orc_jit, orc_main_dylib, orc_thread_safe_module))) { + /* If adding the ThreadSafeModule fails then we need to clean it up + by ourselves, otherwise the orc orc_jit will manage the memory. + */ + LLVMOrcDisposeThreadSafeModule(orc_thread_safe_module); + aot_handle_llvm_errmsg("failed to addIRModule", err); return false; - break; - - default: - break; - } - } - - /* Move func_return block to the bottom */ - if (func_ctx->func_return_block) { - LLVMBasicBlockRef last_block = - LLVMGetLastBasicBlock(func_ctx->func); - if (last_block != func_ctx->func_return_block) - LLVMMoveBasicBlockAfter(func_ctx->func_return_block, - last_block); - } - - /* Move got_exception block to the bottom */ - if (func_ctx->got_exception_block) { - LLVMBasicBlockRef last_block = - LLVMGetLastBasicBlock(func_ctx->func); - if (last_block != func_ctx->got_exception_block) - LLVMMoveBasicBlockAfter(func_ctx->got_exception_block, - last_block); - - /* Move all other exception blocks before got_exception block */ - for (i = 0; i < EXCE_NUM; i++) { - if (func_ctx->exception_blocks[i]) - LLVMMoveBasicBlockBefore(func_ctx->exception_blocks[i], - func_ctx->got_exception_block); - } - } - return true; + } -fail: - return false; + if (comp_ctx->stack_sizes != NULL) { + LLVMOrcJITTargetAddress addr; + if ((err = LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &addr, + aot_stack_sizes_alias_name))) { + aot_handle_llvm_errmsg("failed to look up stack_sizes", err); + return false; + } + comp_ctx->jit_stack_sizes = (uint32 *)addr; + } + } + + return true; } -bool -aot_compile_wasm(AOTCompContext *comp_ctx) +char * +aot_generate_tempfile_name(const char *prefix, const char *extension, + char *buffer, uint32 len) { - char *msg = NULL; - bool ret; - uint32 i; - - for (i = 0; i < comp_ctx->func_ctx_count; i++) - if (!aot_compile_func(comp_ctx, i)) { -#if 0 - LLVMDumpModule(comp_ctx->module); - char *err; - LLVMTargetMachineEmitToFile(comp_ctx->target_machine, comp_ctx->module, - "./test.o", LLVMObjectFile, &err); -#endif - return false; + int name_len; + + name_len = snprintf(buffer, len, "%s-XXXXXX", prefix); + + if (!bh_mkstemp(buffer, name_len + 1)) { + aot_set_last_error("make temp file failed."); + return NULL; } -#if 0 - LLVMDumpModule(comp_ctx->module); - /* Clear error no, LLVMDumpModule may set errno */ - errno = 0; -#endif + /* Check if buffer length is enough */ + /* name_len + '.' + extension + '\0' */ + if (name_len + 1 + strlen(extension) + 1 > len) { + aot_set_last_error("temp file name too long."); + return NULL; + } - ret = LLVMVerifyModule(comp_ctx->module, LLVMPrintMessageAction, &msg); - if (!ret && msg) { - if (msg[0] != '\0') { - aot_set_last_error(msg); - LLVMDisposeMessage(msg); - return false; - } - LLVMDisposeMessage(msg); - } - - if (comp_ctx->optimize) { - LLVMInitializeFunctionPassManager(comp_ctx->pass_mgr); - for (i = 0; i < comp_ctx->func_ctx_count; i++) - LLVMRunFunctionPassManager(comp_ctx->pass_mgr, - comp_ctx->func_ctxes[i]->func); - } - - return true; + snprintf(buffer + name_len, len - name_len, ".%s", extension); + return buffer; } bool @@ -769,6 +4137,8 @@ aot_emit_llvm_file(AOTCompContext *comp_ctx, const char *file_name) { char *err = NULL; + bh_print_time("Begin to emit LLVM IR file"); + if (LLVMPrintModuleToFile(comp_ctx->module, file_name, &err) != 0) { if (err) { LLVMDisposeMessage(err); @@ -781,24 +4151,188 @@ aot_emit_llvm_file(AOTCompContext *comp_ctx, const char *file_name) return true; } +static bool +aot_move_file(const char *dest, const char *src) +{ + FILE *dfp = fopen(dest, "w"); + FILE *sfp = fopen(src, "r"); + size_t rsz; + char buf[128]; + bool success = false; + + if (dfp == NULL || sfp == NULL) { + LOG_DEBUG("open error %s %s", dest, src); + goto fail; + } + do { + rsz = fread(buf, 1, sizeof(buf), sfp); + if (rsz > 0) { + size_t wsz = fwrite(buf, 1, rsz, dfp); + if (wsz < rsz) { + LOG_DEBUG("write error"); + goto fail; + } + } + if (rsz < sizeof(buf)) { + if (ferror(sfp)) { + LOG_DEBUG("read error"); + goto fail; + } + } + } while (rsz > 0); + success = true; +fail: + if (dfp != NULL) { + if (fclose(dfp)) { + LOG_DEBUG("close error"); + success = false; + } + if (!success) { + (void)unlink(dest); + } + } + if (sfp != NULL) { + (void)fclose(sfp); + } + if (success) { + (void)unlink(src); + } + return success; +} + bool aot_emit_object_file(AOTCompContext *comp_ctx, char *file_name) { char *err = NULL; + LLVMCodeGenFileType file_type = LLVMObjectFile; + LLVMTargetRef target = LLVMGetTargetMachineTarget(comp_ctx->target_machine); + + bh_print_time("Begin to emit object file"); + + if (comp_ctx->external_llc_compiler || comp_ctx->external_asm_compiler) { + char cmd[1024]; + int ret; + + if (comp_ctx->external_llc_compiler) { + const char *stack_usage_flag = ""; + char bc_file_name[64]; + char su_file_name[65]; /* See the comment below */ + + if (comp_ctx->stack_usage_file != NULL) { + /* + * Note: we know the caller uses 64 byte buffer for + * file_name. It will get 1 byte longer because we + * replace ".o" with ".su". + */ + size_t len = strlen(file_name); + bh_assert(len + 1 <= sizeof(su_file_name)); + bh_assert(len > 3); + bh_assert(file_name[len - 2] == '.'); + bh_assert(file_name[len - 1] == 'o'); + snprintf(su_file_name, sizeof(su_file_name), "%.*s.su", + (int)(len - 2), file_name); + stack_usage_flag = " -fstack-usage"; + } - if (LLVMTargetMachineEmitToFile(comp_ctx->target_machine, - comp_ctx->module, - file_name, - LLVMObjectFile, - &err) != 0) { + if (!aot_generate_tempfile_name("wamrc-bc", "bc", bc_file_name, + sizeof(bc_file_name))) { + return false; + } + + if (LLVMWriteBitcodeToFile(comp_ctx->module, bc_file_name) != 0) { + aot_set_last_error("emit llvm bitcode file failed."); + return false; + } + + snprintf(cmd, sizeof(cmd), "%s%s %s -o %s %s", + comp_ctx->external_llc_compiler, stack_usage_flag, + comp_ctx->llc_compiler_flags ? comp_ctx->llc_compiler_flags + : "-O3 -c", + file_name, bc_file_name); + LOG_VERBOSE("invoking external LLC compiler:\n\t%s", cmd); + + ret = bh_system(cmd); + /* remove temp bitcode file */ + unlink(bc_file_name); + + if (ret != 0) { + aot_set_last_error("failed to compile LLVM bitcode to obj file " + "with external LLC compiler."); + return false; + } + if (comp_ctx->stack_usage_file != NULL) { + /* + * move the temporary .su file to the specified location. + * + * Note: the former is automatically inferred from the output + * filename (file_name here) by clang. + * + * Note: the latter might be user-specified. + * (wamrc --stack-usage=) + */ + if (!aot_move_file(comp_ctx->stack_usage_file, su_file_name)) { + aot_set_last_error("failed to move su file."); + (void)unlink(su_file_name); + return false; + } + } + } + else if (comp_ctx->external_asm_compiler) { + char asm_file_name[64]; + + if (!aot_generate_tempfile_name("wamrc-asm", "s", asm_file_name, + sizeof(asm_file_name))) { + return false; + } + + if (LLVMTargetMachineEmitToFile(comp_ctx->target_machine, + comp_ctx->module, asm_file_name, + LLVMAssemblyFile, &err) + != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("emit elf to assembly file failed."); + return false; + } + + snprintf(cmd, sizeof(cmd), "%s %s -o %s %s", + comp_ctx->external_asm_compiler, + comp_ctx->asm_compiler_flags ? comp_ctx->asm_compiler_flags + : "-O3 -c", + file_name, asm_file_name); + LOG_VERBOSE("invoking external ASM compiler:\n\t%s", cmd); + + ret = bh_system(cmd); + /* remove temp assembly file */ + unlink(asm_file_name); + + if (ret != 0) { + aot_set_last_error("failed to compile Assembly file to obj " + "file with external ASM compiler."); + return false; + } + } + + return true; + } + + if (!strncmp(LLVMGetTargetName(target), "arc", 3)) + /* Emit to assembly file instead for arc target + as it cannot emit to object file */ + file_type = LLVMAssemblyFile; + + if (LLVMTargetMachineEmitToFile(comp_ctx->target_machine, comp_ctx->module, + file_name, file_type, &err) + != 0) { if (err) { LLVMDisposeMessage(err); err = NULL; } - aot_set_last_error("emit elf to memory buffer failed."); + aot_set_last_error("emit elf to object file failed."); return false; } return true; } - diff --git a/core/iwasm/compilation/aot_compiler.h b/core/iwasm/compilation/aot_compiler.h index fc447090c6..70d01c578d 100644 --- a/core/iwasm/compilation/aot_compiler.h +++ b/core/iwasm/compilation/aot_compiler.h @@ -8,175 +8,633 @@ #include "aot.h" #include "aot_llvm.h" -#include "bh_memory.h" +#include "../interpreter/wasm_interp.h" +#include "../aot/aot_runtime.h" #ifdef __cplusplus extern "C" { #endif -typedef enum IntCond { - INT_EQZ = 0, - INT_EQ, - INT_NE, - INT_LT_S, - INT_LT_U, - INT_GT_S, - INT_GT_U, - INT_LE_S, - INT_LE_U, - INT_GE_S, - INT_GE_U -} IntCond; - -typedef enum FloatCond { - FLOAT_EQ = 0, - FLOAT_NE, - FLOAT_LT, - FLOAT_GT, - FLOAT_LE, - FLOAT_GE -} FloatCond; +typedef AOTIntCond IntCond; +typedef AOTFloatCond FloatCond; typedef enum IntArithmetic { - INT_ADD = 0, - INT_SUB, - INT_MUL, - INT_DIV_S, - INT_DIV_U, - INT_REM_S, - INT_REM_U + INT_ADD = 0, + INT_SUB, + INT_MUL, + INT_DIV_S, + INT_DIV_U, + INT_REM_S, + INT_REM_U } IntArithmetic; +typedef enum V128Arithmetic { + V128_ADD = 0, + V128_SUB, + V128_MUL, + V128_DIV, + V128_NEG, + V128_MIN, + V128_MAX, +} V128Arithmetic; + typedef enum IntBitwise { - INT_AND = 0, - INT_OR, - INT_XOR, + INT_AND = 0, + INT_OR, + INT_XOR, } IntBitwise; +typedef enum V128Bitwise { + V128_NOT, + V128_AND, + V128_ANDNOT, + V128_OR, + V128_XOR, + V128_BITSELECT, +} V128Bitwise; + typedef enum IntShift { - INT_SHL = 0, - INT_SHR_S, - INT_SHR_U, - INT_ROTL, - INT_ROTR + INT_SHL = 0, + INT_SHR_S, + INT_SHR_U, + INT_ROTL, + INT_ROTR } IntShift; typedef enum FloatMath { - FLOAT_ABS = 0, - FLOAT_NEG, - FLOAT_CEIL, - FLOAT_FLOOR, - FLOAT_TRUNC, - FLOAT_NEAREST, - FLOAT_SQRT + FLOAT_ABS = 0, + FLOAT_NEG, + FLOAT_CEIL, + FLOAT_FLOOR, + FLOAT_TRUNC, + FLOAT_NEAREST, + FLOAT_SQRT } FloatMath; typedef enum FloatArithmetic { - FLOAT_ADD = 0, - FLOAT_SUB, - FLOAT_MUL, - FLOAT_DIV, - FLOAT_MIN, - FLOAT_MAX + FLOAT_ADD = 0, + FLOAT_SUB, + FLOAT_MUL, + FLOAT_DIV, + FLOAT_MIN, + FLOAT_MAX, } FloatArithmetic; -#define CHECK_STACK() do { \ - if (!func_ctx->block_stack.block_list_end) { \ - aot_set_last_error("WASM block stack underflow."); \ - goto fail; \ - } \ - if (!func_ctx->block_stack.block_list_end-> \ - value_stack.value_list_end) { \ - aot_set_last_error("WASM data stack underflow."); \ - goto fail; \ - } \ - } while (0) - -#define POP(llvm_value, value_type) do { \ - AOTValue *aot_value; \ - CHECK_STACK(); \ - aot_value = aot_value_stack_pop \ - (&func_ctx->block_stack.block_list_end->value_stack); \ - if ((value_type != VALUE_TYPE_I32 \ - && aot_value->type != value_type) \ - || (value_type == VALUE_TYPE_I32 \ - && (aot_value->type != VALUE_TYPE_I32 \ - && aot_value->type != VALUE_TYPE_I1))) { \ - aot_set_last_error("invalid WASM stack data type."); \ - wasm_free(aot_value); \ - goto fail; \ - } \ - if (aot_value->type == value_type) \ - llvm_value = aot_value->value; \ - else { \ - bh_assert(aot_value->type == VALUE_TYPE_I1); \ - if (!(llvm_value = LLVMBuildZExt(comp_ctx->builder, \ - aot_value->value, I32_TYPE, "i1toi32"))) { \ - aot_set_last_error("invalid WASM stack data type.");\ - wasm_free(aot_value); \ - goto fail; \ - } \ - } \ - wasm_free(aot_value); \ - } while (0) +/** + * Check whether a value type is a GC reference type, + * don't use wasm_is_type_reftype since it requires + * GC feature and may result in compilation error when + * GC feature isn't compiled + */ +static inline bool +aot_is_type_gc_reftype(uint8 type) +{ + return ((type >= (uint8)REF_TYPE_ARRAYREF + && type <= (uint8)REF_TYPE_NULLFUNCREF) + || (type >= (uint8)REF_TYPE_HT_NULLABLE + && type <= (uint8)REF_TYPE_HT_NON_NULLABLE) +#if WASM_ENABLE_STRINGREF != 0 + || (type >= (uint8)REF_TYPE_STRINGVIEWWTF8 + && type <= (uint8)REF_TYPE_STRINGREF) + || (type >= (uint8)REF_TYPE_STRINGVIEWITER + && type <= (uint8)REF_TYPE_STRINGVIEWWTF16) +#endif + ) + ? true + : false; +} + +static inline bool +check_type_compatible(const AOTCompContext *comp_ctx, uint8 src_type, + uint8 dst_type) +{ + if (src_type == dst_type) { + return true; + } + + /* ext i1 to i32 */ + if (src_type == VALUE_TYPE_I1 && dst_type == VALUE_TYPE_I32) { + return true; + } + + /* i32 <==> func.ref, i32 <==> extern.ref */ + if (src_type == VALUE_TYPE_I32 + && (comp_ctx->enable_ref_types + && (dst_type == VALUE_TYPE_EXTERNREF + || dst_type == VALUE_TYPE_FUNCREF))) { + return true; + } + + if (dst_type == VALUE_TYPE_I32 + && (comp_ctx->enable_ref_types + && (src_type == VALUE_TYPE_FUNCREF + || src_type == VALUE_TYPE_EXTERNREF))) { + return true; + } + + return false; +} + +/** + * Operations for AOTCompFrame + */ + +/** + * Get the offset from frame pointer to the n-th local variable slot. + * + * @param n the index to the local variable array + * + * @return the offset from frame pointer to the local variable slot + */ +static inline uint32 +offset_of_local(AOTCompContext *comp_ctx, unsigned n) +{ + if (!comp_ctx->is_jit_mode) + /* In AOTFrame, there are 7 pointers before field lp */ + return comp_ctx->pointer_size + * (offsetof(AOTFrame, lp) / sizeof(uintptr_t)) + + sizeof(uint32) * n; + else + return offsetof(WASMInterpFrame, lp) + sizeof(uint32) * n; +} + +uint32 +offset_of_local_in_outs_area(AOTCompContext *comp_ctx, unsigned n); + +/** + * Get the offset from frame pointer to the n-th local variable's + * reference flag slot. + * + * @param n the index to the local variable array + * + * @return the offset from frame pointer to the local variable slot + */ +static inline unsigned +offset_of_ref(AOTCompContext *comp_ctx, unsigned n) +{ + AOTCompFrame *frame = comp_ctx->aot_frame; + uint32 all_cell_num = frame->max_local_cell_num + frame->max_stack_cell_num; + return offset_of_local(comp_ctx, all_cell_num) + n; +} + +/** + * Generate instructions to commit computation result to the frame. + * The general principle is to only commit values that will be used + * through the frame. + * + * @param frame the frame information + */ +bool +aot_gen_commit_values(AOTCompFrame *frame); + +/** + * Generate instructions to commit SP and IP pointers to the frame. + * + * @param frame the frame information + */ +bool +aot_gen_commit_sp_ip(AOTCompFrame *frame, bool commit_sp, bool commit_ip); + +/** + * Generate instructions to commit IP pointer to the frame. + * + * @param frame the frame information + */ +bool +aot_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef ip_value, bool is_64bit); + +bool +aot_frame_store_value(AOTCompContext *comp_ctx, LLVMValueRef value, + uint8 value_type, LLVMValueRef cur_frame, uint32 offset); + +static inline void +push_32bit(AOTCompFrame *frame, AOTValue *aot_value) +{ + frame->sp->value = aot_value->value; + frame->sp->type = aot_value->type; + frame->sp->dirty = 1; + frame->sp++; +} + +static inline void +push_64bit(AOTCompFrame *frame, AOTValue *aot_value) +{ + push_32bit(frame, aot_value); + push_32bit(frame, aot_value); +} + +static inline void +push_i32(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(aot_value->type == VALUE_TYPE_I32 + || aot_value->type == VALUE_TYPE_I1); + push_32bit(frame, aot_value); +} + +static inline void +push_i64(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(aot_value->type == VALUE_TYPE_I64); + push_64bit(frame, aot_value); +} + +static inline void +push_f32(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(aot_value->type == VALUE_TYPE_F32); + push_32bit(frame, aot_value); +} + +static inline void +push_f64(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(aot_value->type == VALUE_TYPE_F64); + push_64bit(frame, aot_value); +} + +static inline void +push_v128(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(aot_value->type == VALUE_TYPE_V128); + push_64bit(frame, aot_value); + push_64bit(frame, aot_value); +} + +static inline void +push_ref(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(frame->comp_ctx->enable_ref_types); + push_32bit(frame, aot_value); +} + +#if WASM_ENABLE_GC != 0 +static inline void +push_gc_ref(AOTCompFrame *frame, AOTValue *aot_value) +{ + bh_assert(frame->comp_ctx->enable_gc); + bh_assert(aot_value->type == VALUE_TYPE_GC_REF); + if (frame->comp_ctx->pointer_size == sizeof(uint64)) { + push_64bit(frame, aot_value); + (frame->sp - 1)->ref = (frame->sp - 2)->ref = 1; + } + else { + push_32bit(frame, aot_value); + (frame->sp - 1)->ref = 1; + } +} +#endif + +/* Clear value slots except ref and committed_ref */ +static inline void +clear_frame_value_slots(AOTValueSlot *slots, uint32 n) +{ + uint32 i; + for (i = 0; i < n; i++) { + slots[i].value = 0; + slots[i].type = 0; + slots[i].dirty = 0; + } +} + +static inline void +pop_i32(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 1); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_I32 + || (frame->sp - 1)->type == VALUE_TYPE_I1); + frame->sp--; + clear_frame_value_slots(frame->sp, 1); +} + +static inline void +pop_i64(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 2); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_I64 + && (frame->sp - 2)->type == VALUE_TYPE_I64); + frame->sp -= 2; + clear_frame_value_slots(frame->sp, 2); +} + +static inline void +pop_f32(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 1); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_F32); + frame->sp--; + clear_frame_value_slots(frame->sp, 1); +} + +static inline void +pop_f64(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 2); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_F64 + && (frame->sp - 2)->type == VALUE_TYPE_F64); + frame->sp -= 2; + clear_frame_value_slots(frame->sp, 2); +} + +static inline void +pop_v128(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 4); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_V128 + && (frame->sp - 2)->type == VALUE_TYPE_V128 + && (frame->sp - 3)->type == VALUE_TYPE_V128 + && (frame->sp - 4)->type == VALUE_TYPE_V128); + frame->sp -= 4; + clear_frame_value_slots(frame->sp, 4); +} + +static inline void +pop_ref(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 1); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_FUNCREF + || (frame->sp - 1)->type == VALUE_TYPE_EXTERNREF); + frame->sp -= 1; + clear_frame_value_slots(frame->sp, 1); +} + +#if WASM_ENABLE_GC != 0 +static inline void +pop_gc_ref(AOTCompFrame *frame) +{ + bh_assert(frame->sp - frame->lp >= 1); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_GC_REF); + frame->sp -= 1; + clear_frame_value_slots(frame->sp, 1); + frame->sp->ref = 0; + if (frame->comp_ctx->pointer_size == sizeof(uint64)) { + bh_assert(frame->sp - frame->lp >= 1); + bh_assert((frame->sp - 1)->type == VALUE_TYPE_GC_REF); + frame->sp -= 1; + clear_frame_value_slots(frame->sp, 1); + frame->sp->ref = 0; + } +} +#endif + +static inline void +set_local_i32(AOTCompFrame *frame, int n, LLVMValueRef value) +{ + frame->lp[n].value = value; + frame->lp[n].type = VALUE_TYPE_I32; + frame->lp[n].dirty = 1; +} + +static inline void +set_local_i64(AOTCompFrame *frame, int n, LLVMValueRef value) +{ + frame->lp[n].value = value; + frame->lp[n].type = VALUE_TYPE_I64; + frame->lp[n].dirty = 1; + frame->lp[n + 1].value = value; + frame->lp[n + 1].type = VALUE_TYPE_I64; + frame->lp[n + 1].dirty = 1; +} + +static inline void +set_local_f32(AOTCompFrame *frame, int n, LLVMValueRef value) +{ + frame->lp[n].value = value; + frame->lp[n].type = VALUE_TYPE_F32; + frame->lp[n].dirty = 1; +} + +static inline void +set_local_f64(AOTCompFrame *frame, int n, LLVMValueRef value) +{ + frame->lp[n].value = value; + frame->lp[n].type = VALUE_TYPE_F64; + frame->lp[n].dirty = 1; + frame->lp[n + 1].value = value; + frame->lp[n + 1].type = VALUE_TYPE_F64; + frame->lp[n + 1].dirty = 1; +} + +static inline void +set_local_v128(AOTCompFrame *frame, int n, LLVMValueRef value) +{ + uint32 i; + for (i = 0; i < 4; i++) { + frame->lp[n + i].value = value; + frame->lp[n + i].type = VALUE_TYPE_V128; + frame->lp[n + i].dirty = 1; + } +} + +static inline void +set_local_ref(AOTCompFrame *frame, int n, LLVMValueRef value, uint8 ref_type) +{ + bh_assert(frame->comp_ctx->enable_ref_types); + frame->lp[n].value = value; + frame->lp[n].type = ref_type; + frame->lp[n].dirty = 1; +} + +#if WASM_ENABLE_GC != 0 +static inline void +set_local_gc_ref(AOTCompFrame *frame, int n, LLVMValueRef value, uint8 ref_type) +{ + bh_assert(frame->comp_ctx->enable_gc); + bh_assert(ref_type == VALUE_TYPE_GC_REF); + frame->lp[n].value = value; + frame->lp[n].type = ref_type; + frame->lp[n].dirty = 1; + frame->lp[n].ref = 1; + if (frame->comp_ctx->pointer_size == sizeof(uint64)) { + frame->lp[n + 1].value = value; + frame->lp[n + 1].type = ref_type; + frame->lp[n + 1].dirty = 1; + frame->lp[n + 1].ref = 1; + } +} +#endif + +#define CHECK_STACK() \ + do { \ + if (!func_ctx->block_stack.block_list_end) { \ + aot_set_last_error("WASM block stack underflow."); \ + goto fail; \ + } \ + if (!func_ctx->block_stack.block_list_end->value_stack \ + .value_list_end) { \ + aot_set_last_error("WASM data stack underflow."); \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_GC != 0 + +#define GET_GC_REF_FROM_STACK(llvm_value) \ + do { \ + AOTValue *aot_value; \ + CHECK_STACK(); \ + aot_value = \ + func_ctx->block_stack.block_list_end->value_stack.value_list_end; \ + if (aot_value->type != VALUE_TYPE_GC_REF) { \ + aot_set_last_error("WASM stack data type is not reference"); \ + goto fail; \ + } \ + llvm_value = aot_value->value; \ + } while (0) + +#endif + +#define POP(llvm_value, value_type) \ + do { \ + AOTValue *aot_value; \ + uint8 val_type_to_pop = value_type; \ + CHECK_STACK(); \ + aot_value = aot_value_stack_pop( \ + comp_ctx, &func_ctx->block_stack.block_list_end->value_stack); \ + if (comp_ctx->enable_gc && aot_is_type_gc_reftype(value_type)) \ + val_type_to_pop = VALUE_TYPE_GC_REF; \ + if (!check_type_compatible(comp_ctx, aot_value->type, \ + val_type_to_pop)) { \ + aot_set_last_error("invalid WASM stack data type."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + if (aot_value->type == val_type_to_pop) \ + llvm_value = aot_value->value; \ + else { \ + if (aot_value->type == VALUE_TYPE_I1) { \ + if (!(llvm_value = \ + LLVMBuildZExt(comp_ctx->builder, aot_value->value, \ + I32_TYPE, "i1toi32"))) { \ + aot_set_last_error("invalid WASM stack data type."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + } \ + else { \ + bh_assert( \ + aot_value->type == VALUE_TYPE_I32 \ + || (comp_ctx->enable_ref_types \ + && (aot_value->type == VALUE_TYPE_FUNCREF \ + || aot_value->type == VALUE_TYPE_EXTERNREF))); \ + bh_assert( \ + val_type_to_pop == VALUE_TYPE_I32 \ + || (comp_ctx->enable_ref_types \ + && (val_type_to_pop == VALUE_TYPE_FUNCREF \ + || val_type_to_pop == VALUE_TYPE_EXTERNREF))); \ + llvm_value = aot_value->value; \ + } \ + } \ + wasm_runtime_free(aot_value); \ + } while (0) + +#if WASM_ENABLE_MEMORY64 != 0 +#define IS_MEMORY64 (comp_ctx->comp_data->memories[0].flags & MEMORY64_FLAG) +#define MEMORY64_COND_VALUE(VAL_IF_ENABLED, VAL_IF_DISABLED) \ + (IS_MEMORY64 ? VAL_IF_ENABLED : VAL_IF_DISABLED) +#define IS_TABLE64(i) \ + (comp_ctx->comp_data->tables[i].table_type.flags & TABLE64_FLAG) +#define TABLE64_COND_VALUE(i, VAL_IF_ENABLED, VAL_IF_DISABLED) \ + (IS_TABLE64(i) ? VAL_IF_ENABLED : VAL_IF_DISABLED) +#else +#define MEMORY64_COND_VALUE(VAL_IF_ENABLED, VAL_IF_DISABLED) (VAL_IF_DISABLED) +#define TABLE64_COND_VALUE(i, VAL_IF_ENABLED, VAL_IF_DISABLED) (VAL_IF_DISABLED) +#endif #define POP_I32(v) POP(v, VALUE_TYPE_I32) #define POP_I64(v) POP(v, VALUE_TYPE_I64) #define POP_F32(v) POP(v, VALUE_TYPE_F32) #define POP_F64(v) POP(v, VALUE_TYPE_F64) +#define POP_V128(v) POP(v, VALUE_TYPE_V128) +#define POP_FUNCREF(v) POP(v, VALUE_TYPE_FUNCREF) +#define POP_EXTERNREF(v) POP(v, VALUE_TYPE_EXTERNREF) +#define POP_GC_REF(v) POP(v, VALUE_TYPE_GC_REF) +#define POP_MEM_OFFSET(v) \ + POP(v, MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32)) +#define POP_PAGE_COUNT(v) \ + POP(v, MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32)) +#define POP_TBL_ELEM_IDX(v) \ + POP(v, TABLE64_COND_VALUE(tbl_idx, VALUE_TYPE_I64, VALUE_TYPE_I32)) +#define POP_TBL_ELEM_LEN(v) POP_TBL_ELEM_IDX(v) + +#define POP_COND(llvm_value) \ + do { \ + AOTValue *aot_value; \ + CHECK_STACK(); \ + aot_value = aot_value_stack_pop( \ + comp_ctx, &func_ctx->block_stack.block_list_end->value_stack); \ + if (aot_value->type != VALUE_TYPE_I1 \ + && aot_value->type != VALUE_TYPE_I32) { \ + aot_set_last_error("invalid WASM stack data type."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + if (aot_value->type == VALUE_TYPE_I1) \ + llvm_value = aot_value->value; \ + else { \ + if (!(llvm_value = \ + LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, \ + aot_value->value, I32_ZERO, "i1_cond"))) { \ + aot_set_last_error("llvm build trunc failed."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + } \ + wasm_runtime_free(aot_value); \ + } while (0) -#define POP_COND(llvm_value) do { \ - AOTValue *aot_value; \ - CHECK_STACK(); \ - aot_value = aot_value_stack_pop \ - (&func_ctx->block_stack.block_list_end->value_stack); \ - if (aot_value->type != VALUE_TYPE_I1 \ - && aot_value->type != VALUE_TYPE_I32) { \ - aot_set_last_error("invalid WASM stack data type."); \ - wasm_free(aot_value); \ - goto fail; \ - } \ - if (aot_value->type == VALUE_TYPE_I1) \ - llvm_value = aot_value->value; \ - else { \ - if (!(llvm_value = LLVMBuildICmp(comp_ctx->builder, \ - LLVMIntNE, aot_value->value, I32_ZERO, \ - "i1_cond"))){ \ - aot_set_last_error("llvm build trunc failed."); \ - wasm_free(aot_value); \ - goto fail; \ - } \ - } \ - wasm_free(aot_value); \ - } while (0) - -#define PUSH(llvm_value, value_type) do { \ - AOTValue *aot_value; \ - if (!func_ctx->block_stack.block_list_end) { \ - aot_set_last_error("WASM block stack underflow."); \ - goto fail; \ - } \ - aot_value = wasm_malloc(sizeof(AOTValue)); \ - memset(aot_value, 0, sizeof(AOTValue)); \ - if (!aot_value) { \ - aot_set_last_error("allocate memory failed."); \ - goto fail; \ - } \ - aot_value->type = value_type; \ - aot_value->value = llvm_value; \ - aot_value_stack_push \ - (&func_ctx->block_stack.block_list_end->value_stack,\ - aot_value); \ - } while (0) +#define PUSH(llvm_value, value_type) \ + do { \ + AOTValue *aot_value; \ + if (!func_ctx->block_stack.block_list_end) { \ + aot_set_last_error("WASM block stack underflow."); \ + goto fail; \ + } \ + aot_value = wasm_runtime_malloc(sizeof(AOTValue)); \ + if (!aot_value) { \ + aot_set_last_error("allocate memory failed."); \ + goto fail; \ + } \ + memset(aot_value, 0, sizeof(AOTValue)); \ + if (comp_ctx->enable_gc && aot_is_type_gc_reftype(value_type)) \ + aot_value->type = VALUE_TYPE_GC_REF; \ + else if (comp_ctx->enable_ref_types \ + && (value_type == VALUE_TYPE_FUNCREF \ + || value_type == VALUE_TYPE_EXTERNREF)) \ + aot_value->type = VALUE_TYPE_I32; \ + else \ + aot_value->type = value_type; \ + aot_value->value = llvm_value; \ + aot_value_stack_push( \ + comp_ctx, &func_ctx->block_stack.block_list_end->value_stack, \ + aot_value); \ + } while (0) #define PUSH_I32(v) PUSH(v, VALUE_TYPE_I32) #define PUSH_I64(v) PUSH(v, VALUE_TYPE_I64) #define PUSH_F32(v) PUSH(v, VALUE_TYPE_F32) #define PUSH_F64(v) PUSH(v, VALUE_TYPE_F64) +#define PUSH_V128(v) PUSH(v, VALUE_TYPE_V128) #define PUSH_COND(v) PUSH(v, VALUE_TYPE_I1) +#define PUSH_FUNCREF(v) PUSH(v, VALUE_TYPE_FUNCREF) +#define PUSH_EXTERNREF(v) PUSH(v, VALUE_TYPE_EXTERNREF) +#define PUSH_GC_REF(v) PUSH(v, VALUE_TYPE_GC_REF) +#define PUSH_PAGE_COUNT(v) \ + PUSH(v, MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32)) +#define PUSH_TBL_ELEM_IDX(v) \ + PUSH(v, TABLE64_COND_VALUE(tbl_idx, VALUE_TYPE_I64, VALUE_TYPE_I32)) +#define PUSH_TBL_ELEM_LEN(v) PUSH_TBL_ELEM_IDX(v) + +#define SET_CONST(v) \ + do { \ + AOTValue *aot_value = \ + func_ctx->block_stack.block_list_end->value_stack.value_list_end; \ + aot_value->is_const = true; \ + aot_value->const_value = (v); \ + } while (0) #define TO_LLVM_TYPE(wasm_type) \ - wasm_type_to_llvm_type(&comp_ctx->basic_types, wasm_type) + wasm_type_to_llvm_type(comp_ctx, &comp_ctx->basic_types, wasm_type) #define I32_TYPE comp_ctx->basic_types.int32_type #define I64_TYPE comp_ctx->basic_types.int64_type @@ -186,45 +644,174 @@ typedef enum FloatArithmetic { #define INT1_TYPE comp_ctx->basic_types.int1_type #define INT8_TYPE comp_ctx->basic_types.int8_type #define INT16_TYPE comp_ctx->basic_types.int16_type +#define INTPTR_T_TYPE comp_ctx->basic_types.intptr_t_type +#define SIZE_T_TYPE comp_ctx->basic_types.size_t_type #define MD_TYPE comp_ctx->basic_types.meta_data_type #define INT8_PTR_TYPE comp_ctx->basic_types.int8_ptr_type #define INT16_PTR_TYPE comp_ctx->basic_types.int16_ptr_type #define INT32_PTR_TYPE comp_ctx->basic_types.int32_ptr_type #define INT64_PTR_TYPE comp_ctx->basic_types.int64_ptr_type +#define INTPTR_T_PTR_TYPE comp_ctx->basic_types.intptr_t_ptr_type #define F32_PTR_TYPE comp_ctx->basic_types.float32_ptr_type #define F64_PTR_TYPE comp_ctx->basic_types.float64_ptr_type -#define VOID_PTR_TYPE comp_ctx->basic_types.void_ptr_type +#define FUNC_REF_TYPE comp_ctx->basic_types.funcref_type +#define EXTERN_REF_TYPE comp_ctx->basic_types.externref_type +#define GC_REF_TYPE comp_ctx->basic_types.gc_ref_type +#define GC_REF_PTR_TYPE comp_ctx->basic_types.gc_ref_ptr_type + +#define INT8_PTR_TYPE_GS comp_ctx->basic_types.int8_ptr_type_gs +#define INT16_PTR_TYPE_GS comp_ctx->basic_types.int16_ptr_type_gs +#define INT32_PTR_TYPE_GS comp_ctx->basic_types.int32_ptr_type_gs +#define INT64_PTR_TYPE_GS comp_ctx->basic_types.int64_ptr_type_gs +#define F32_PTR_TYPE_GS comp_ctx->basic_types.float32_ptr_type_gs +#define F64_PTR_TYPE_GS comp_ctx->basic_types.float64_ptr_type_gs #define I32_CONST(v) LLVMConstInt(I32_TYPE, v, true) #define I64_CONST(v) LLVMConstInt(I64_TYPE, v, true) -#define F32_CONST(v) LLVMConstReal(F32_TYPE, v) +#define F32_CONST(v) LLVMConstReal(F32_TYPE, (double)(v)) #define F64_CONST(v) LLVMConstReal(F64_TYPE, v) #define I8_CONST(v) LLVMConstInt(INT8_TYPE, v, true) -#define I8_ZERO (comp_ctx->llvm_consts.i8_zero) -#define I32_ZERO (comp_ctx->llvm_consts.i32_zero) -#define I64_ZERO (comp_ctx->llvm_consts.i64_zero) -#define F32_ZERO (comp_ctx->llvm_consts.f32_zero) -#define F64_ZERO (comp_ctx->llvm_consts.f64_zero) -#define I32_ONE (comp_ctx->llvm_consts.i32_one) -#define I32_TWO (comp_ctx->llvm_consts.i32_two) -#define I32_FOUR (comp_ctx->llvm_consts.i32_four) -#define I32_EIGHT (comp_ctx->llvm_consts.i32_eight) -#define I32_NEG_ONE (comp_ctx->llvm_consts.i32_neg_one) -#define I64_NEG_ONE (comp_ctx->llvm_consts.i64_neg_one) -#define I32_MIN (comp_ctx->llvm_consts.i32_min) -#define I64_MIN (comp_ctx->llvm_consts.i64_min) -#define I32_31 (comp_ctx->llvm_consts.i32_31) -#define I32_32 (comp_ctx->llvm_consts.i32_32) -#define I64_63 (comp_ctx->llvm_consts.i64_63) -#define I64_64 (comp_ctx->llvm_consts.i64_64) - -#define CHECK_LLVM_CONST(v) do { \ - if (!v) { \ - aot_set_last_error("create llvm const failed."); \ - goto fail; \ - } \ - } while (0) +#define INT_CONST(variable, value, type, is_signed) \ + do { \ + variable = LLVMConstInt(type, value, is_signed); \ + if (!variable) { \ + aot_set_last_error("llvm build const failed"); \ + return false; \ + } \ + } while (0) + +#define LLVM_CONST(name) (comp_ctx->llvm_consts.name) +#define I1_ZERO LLVM_CONST(i1_zero) +#define I1_ONE LLVM_CONST(i1_one) +#define I8_ZERO LLVM_CONST(i8_zero) +#define I8_ONE LLVM_CONST(i8_one) +#define I32_ZERO LLVM_CONST(i32_zero) +#define I64_ZERO LLVM_CONST(i64_zero) +#define F32_ZERO LLVM_CONST(f32_zero) +#define F64_ZERO LLVM_CONST(f64_zero) +#define I32_ONE LLVM_CONST(i32_one) +#define I32_TWO LLVM_CONST(i32_two) +#define I32_THREE LLVM_CONST(i32_three) +#define I32_FOUR LLVM_CONST(i32_four) +#define I32_FIVE LLVM_CONST(i32_five) +#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_TEN LLVM_CONST(i32_ten) +#define I32_ELEVEN LLVM_CONST(i32_eleven) +#define I32_TWELVE LLVM_CONST(i32_twelve) +#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) +#define I64_MIN LLVM_CONST(i64_min) +#define I32_31 LLVM_CONST(i32_31) +#define I32_32 LLVM_CONST(i32_32) +#define I64_63 LLVM_CONST(i64_63) +#define I64_64 LLVM_CONST(i64_64) +#define REF_NULL I32_NEG_ONE +#define GC_REF_NULL LLVM_CONST(gc_ref_null) +#define I8_PTR_NULL LLVM_CONST(i8_ptr_null) + +#define V128_TYPE comp_ctx->basic_types.v128_type +#define V128_PTR_TYPE comp_ctx->basic_types.v128_ptr_type +#define V128_PTR_TYPE_GS comp_ctx->basic_types.v128_ptr_type_gs +#define V128_i8x16_TYPE comp_ctx->basic_types.i8x16_vec_type +#define V128_i16x8_TYPE comp_ctx->basic_types.i16x8_vec_type +#define V128_i32x4_TYPE comp_ctx->basic_types.i32x4_vec_type +#define V128_i64x2_TYPE comp_ctx->basic_types.i64x2_vec_type +#define V128_f32x4_TYPE comp_ctx->basic_types.f32x4_vec_type +#define V128_f64x2_TYPE comp_ctx->basic_types.f64x2_vec_type + +#define V128_i8x16_ZERO LLVM_CONST(i8x16_vec_zero) +#define V128_i16x8_ZERO LLVM_CONST(i16x8_vec_zero) +#define V128_i32x4_ZERO LLVM_CONST(i32x4_vec_zero) +#define V128_i64x2_ZERO LLVM_CONST(i64x2_vec_zero) +#define V128_f32x4_ZERO LLVM_CONST(f32x4_vec_zero) +#define V128_f64x2_ZERO LLVM_CONST(f64x2_vec_zero) + +#define TO_V128_i8x16(v) \ + LLVMBuildBitCast(comp_ctx->builder, v, V128_i8x16_TYPE, "i8x16_val") +#define TO_V128_i16x8(v) \ + LLVMBuildBitCast(comp_ctx->builder, v, V128_i16x8_TYPE, "i16x8_val") +#define TO_V128_i32x4(v) \ + LLVMBuildBitCast(comp_ctx->builder, v, V128_i32x4_TYPE, "i32x4_val") +#define TO_V128_i64x2(v) \ + LLVMBuildBitCast(comp_ctx->builder, v, V128_i64x2_TYPE, "i64x2_val") +#define TO_V128_f32x4(v) \ + LLVMBuildBitCast(comp_ctx->builder, v, V128_f32x4_TYPE, "f32x4_val") +#define TO_V128_f64x2(v) \ + LLVMBuildBitCast(comp_ctx->builder, v, V128_f64x2_TYPE, "f64x2_val") + +#define CHECK_LLVM_CONST(v) \ + do { \ + if (!v) { \ + aot_set_last_error("create llvm const failed."); \ + goto fail; \ + } \ + } while (0) + +#define GET_AOT_FUNCTION(name, argc) \ + do { \ + if (!(func_type = \ + LLVMFunctionType(ret_type, param_types, argc, false))) { \ + aot_set_last_error("llvm add function type failed."); \ + goto fail; \ + } \ + if (comp_ctx->is_jit_mode) { \ + /* JIT mode, call the function directly */ \ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ + aot_set_last_error("llvm add pointer type failed."); \ + goto fail; \ + } \ + if (!(value = I64_CONST((uint64)(uintptr_t)name)) \ + || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \ + aot_set_last_error("create LLVM value failed."); \ + goto fail; \ + } \ + } \ + else if (comp_ctx->is_indirect_mode) { \ + int32 func_index; \ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ + aot_set_last_error("create LLVM function type failed."); \ + goto fail; \ + } \ + \ + func_index = aot_get_native_symbol_index(comp_ctx, #name); \ + if (func_index < 0) { \ + goto fail; \ + } \ + if (!(func = aot_get_func_from_table( \ + comp_ctx, func_ctx->native_symbol, func_ptr_type, \ + func_index))) { \ + goto fail; \ + } \ + } \ + else { \ + char *func_name = #name; \ + /* AOT mode, declare the function */ \ + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) \ + && !(func = LLVMAddFunction(func_ctx->module, func_name, \ + func_type))) { \ + aot_set_last_error("llvm add function failed."); \ + goto fail; \ + } \ + } \ + } while (0) + +/* if val is a constant integer and its value is not undef or poison */ +static inline bool +LLVMIsEfficientConstInt(LLVMValueRef val) +{ + return LLVMIsConstant(val) + && LLVMGetValueKind(val) == LLVMConstantIntValueKind + && !LLVMIsUndef(val) +#if LLVM_VERSION_NUMBER >= 12 + && !LLVMIsPoison(addr) +#endif + ; +} bool aot_compile_wasm(AOTCompContext *comp_ctx); @@ -232,17 +819,15 @@ aot_compile_wasm(AOTCompContext *comp_ctx); bool aot_emit_llvm_file(AOTCompContext *comp_ctx, const char *file_name); -bool -aot_emit_aot_file(AOTCompContext *comp_ctx, - AOTCompData *comp_data, - const char *file_name); - bool aot_emit_object_file(AOTCompContext *comp_ctx, char *file_name); +char * +aot_generate_tempfile_name(const char *prefix, const char *extension, + char *buffer, uint32 len); + #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_COMPILER_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_aot_file.c b/core/iwasm/compilation/aot_emit_aot_file.c index 29d706fd65..12749305b7 100644 --- a/core/iwasm/compilation/aot_emit_aot_file.c +++ b/core/iwasm/compilation/aot_emit_aot_file.c @@ -3,27 +3,35 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#include "aot_compiler.h" +#include "aot_emit_aot_file.h" #include "../aot/aot_runtime.h" -#define PUT_U64_TO_ADDR(addr, value) do { \ - union { uint64 val; uint32 parts[2]; } u; \ - u.val = (value); \ - ((uint32*)(addr))[0] = u.parts[0]; \ - ((uint32*)(addr))[1] = u.parts[1]; \ - } while (0) - -#define CHECK_SIZE(size) do { \ - if (size == (uint32)-1) { \ - aot_set_last_error("get symbol size failed."); \ - return (uint32)-1; \ - } \ - } while (0) +#define PUT_U64_TO_ADDR(addr, value) \ + do { \ + union { \ + uint64 val; \ + uint32 parts[2]; \ + } u; \ + u.val = (value); \ + ((uint32 *)(addr))[0] = u.parts[0]; \ + ((uint32 *)(addr))[1] = u.parts[1]; \ + } while (0) + +#define CHECK_SIZE(size) \ + do { \ + if (size == (uint32)-1) { \ + aot_set_last_error("get symbol size failed."); \ + return (uint32)-1; \ + } \ + } while (0) /* Internal function in object file */ typedef struct AOTObjectFunc { char *func_name; + /* text offset of aot_func#n */ uint64 text_offset; + /* text offset of aot_func_internal#n */ + uint64 text_offset_of_aot_func_internal; } AOTObjectFunc; /* Symbol table list node */ @@ -40,7 +48,9 @@ typedef struct AOTSymbolList { } AOTSymbolList; /* AOT object data */ -typedef struct AOTObjectData { +struct AOTObjectData { + AOTCompContext *comp_ctx; + LLVMMemoryBufferRef mem_buf; LLVMBinaryRef binary; @@ -49,6 +59,16 @@ typedef struct AOTObjectData { void *text; uint32 text_size; + void *text_unlikely; + uint32 text_unlikely_size; + + void *text_hot; + uint32 text_hot_size; + + /* literal data and size */ + void *literal; + uint32 literal_size; + AOTObjectDataSection *data_sections; uint32 data_sections_count; @@ -58,7 +78,11 @@ typedef struct AOTObjectData { AOTSymbolList symbol_list; AOTRelocationGroup *relocation_groups; uint32 relocation_group_count; -} AOTObjectData; + + const char *stack_sizes_section_name; + uint32 stack_sizes_offset; + uint32 *stack_sizes; +}; #if 0 static void dump_buf(uint8 *buf, uint32 size, char *title) @@ -75,17 +99,17 @@ static void dump_buf(uint8 *buf, uint32 size, char *title) #endif static bool -is_32bit_binary(LLVMBinaryRef binary) +is_32bit_binary(const AOTObjectData *obj_data) { - LLVMBinaryType type = LLVMBinaryGetType(binary); - return (type == LLVMBinaryTypeELF32L || type == LLVMBinaryTypeELF32B); + /* bit 1: 0 is 32-bit, 1 is 64-bit */ + return obj_data->target_info.bin_type & 2 ? false : true; } static bool -is_little_endian_binary(LLVMBinaryRef binary) +is_little_endian_binary(const AOTObjectData *obj_data) { - LLVMBinaryType type = LLVMBinaryGetType(binary); - return (type == LLVMBinaryTypeELF32L || type == LLVMBinaryTypeELF64L); + /* bit 0: 0 is little-endian, 1 is big-endian */ + return obj_data->target_info.bin_type & 1 ? false : true; } static bool @@ -103,10 +127,10 @@ get_file_header_size() } static uint32 -get_string_size(const char *s) +get_string_size(AOTCompContext *comp_ctx, const char *s) { - /* string size (2 bytes) + string content without '\0' */ - return (uint32)sizeof(uint16) + (uint32)strlen(s); + /* string size (2 bytes) + string content + '\0' */ + return (uint32)sizeof(uint16) + (uint32)strlen(s) + 1; } static uint32 @@ -116,16 +140,33 @@ get_target_info_section_size() } static uint32 -get_mem_init_data_size(AOTMemInitData *mem_init_data) +get_init_expr_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data, + InitializerExpression *expr); + +static uint32 +get_mem_init_data_size(AOTCompContext *comp_ctx, AOTMemInitData *mem_init_data) { - /* init expr type (4 bytes) + init expr value (8 bytes) - + byte count (4 bytes) + bytes */ - return (uint32)(sizeof(uint32) + sizeof(uint64) - + sizeof(uint32) + mem_init_data->byte_count); + /* init expr type (4 bytes) + * + init expr value (4 bytes, valid value can only be i32/get_global) + * + byte count (4 bytes) + bytes */ + uint32 total_size = + (uint32)(get_init_expr_size(comp_ctx, comp_ctx->comp_data, + &mem_init_data->offset) + + sizeof(uint32) + mem_init_data->byte_count); + + /* bulk_memory enabled: + is_passive (4 bytes) + memory_index (4 bytes) + bulk memory disabled: + placeholder (4 bytes) + placeholder (4 bytes) + */ + total_size += (sizeof(uint32) + sizeof(uint32)); + + return total_size; } static uint32 -get_mem_init_data_list_size(AOTMemInitData **mem_init_data_list, +get_mem_init_data_list_size(AOTCompContext *comp_ctx, + AOTMemInitData **mem_init_data_list, uint32 mem_init_data_count) { AOTMemInitData **mem_init_data = mem_init_data_list; @@ -133,96 +174,492 @@ get_mem_init_data_list_size(AOTMemInitData **mem_init_data_list, for (i = 0; i < mem_init_data_count; i++, mem_init_data++) { size = align_uint(size, 4); - size += get_mem_init_data_size(*mem_init_data); + size += get_mem_init_data_size(comp_ctx, *mem_init_data); } return size; } static uint32 -get_mem_info_size(AOTCompData *comp_data) +get_import_memory_size(AOTCompData *comp_data) +{ + /* currently we only emit import_memory_count = 0 */ + return sizeof(uint32); +} + +static uint32 +get_memory_size(AOTCompData *comp_data) +{ + /* memory_count + count * (flags + num_bytes_per_page + + init_page_count + max_page_count) */ + return (uint32)(sizeof(uint32) + + comp_data->memory_count * sizeof(uint32) * 4); +} + +static uint32 +get_mem_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) { - /* num bytes per page + init page count + max page count - + init data count + init data list */ - return (uint32)sizeof(uint32) * 4 - + get_mem_init_data_list_size(comp_data->mem_init_data_list, + /* import_memory_size + memory_size + + init_data_count + init_data_list */ + return get_import_memory_size(comp_data) + get_memory_size(comp_data) + + (uint32)sizeof(uint32) + + get_mem_init_data_list_size(comp_ctx, + comp_data->mem_init_data_list, comp_data->mem_init_data_count); } static uint32 -get_table_init_data_size(AOTTableInitData *table_init_data) +get_init_expr_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data, + InitializerExpression *expr) +{ + /* init_expr_type */ + uint32 size = sizeof(uint32); +#if WASM_ENABLE_GC != 0 + WASMModule *module = comp_data->wasm_module; +#endif + bh_assert(expr != NULL); + /* + init value size */ + switch (expr->init_expr_type) { + case INIT_EXPR_NONE: + /* no init value, used in table initializer */ + break; + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_F32_CONST: + case INIT_EXPR_TYPE_GET_GLOBAL: + size += sizeof(uint32); + break; + case INIT_EXPR_TYPE_I64_CONST: + case INIT_EXPR_TYPE_F64_CONST: + size += sizeof(uint64); + break; + case INIT_EXPR_TYPE_V128_CONST: + size += sizeof(uint64) * 2; + break; + case INIT_EXPR_TYPE_FUNCREF_CONST: + case INIT_EXPR_TYPE_REFNULL_CONST: + /* ref_index */ + size += sizeof(uint32); + break; +#if WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_I31_NEW: + /* i32 */ + size += sizeof(uint32); + break; + case INIT_EXPR_TYPE_STRUCT_NEW: + { + uint32 i; + WASMStructNewInitValues *struct_new_init_values = + (WASMStructNewInitValues *)expr->u.unary.v.data; + + /* type_index + field_count + fields */ + size += sizeof(uint32) + sizeof(uint32); + + bh_assert(struct_new_init_values->type_idx < module->type_count); + + for (i = 0; i < struct_new_init_values->count; i++) { + WASMStructType *struct_type = + (WASMStructType *) + module->types[struct_new_init_values->type_idx]; + uint32 field_size; + + bh_assert(struct_type); + bh_assert(struct_type->field_count + == struct_new_init_values->count); + + field_size = wasm_value_type_size_internal( + struct_type->fields[i].field_type, comp_ctx->pointer_size); + if (field_size < sizeof(uint32)) + field_size = sizeof(uint32); + size += field_size; + } + break; + } + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + /* type_index */ + size += sizeof(uint32); + break; + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + /* array_elem_type + type_index + len */ + size += sizeof(uint32) * 3; + break; + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + WASMArrayNewInitValues *array_new_init_values = + (WASMArrayNewInitValues *)expr->u.unary.v.data; + WASMArrayType *array_type = NULL; + uint32 value_count; + + array_type = + (WASMArrayType *)module->types[array_new_init_values->type_idx]; + + bh_assert(array_type); + bh_assert(array_new_init_values->type_idx < module->type_count); + + value_count = + (expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) + ? array_new_init_values->length + : 1; + + /* array_elem_type + type_index + len + elems */ + size += sizeof(uint32) * 3 + + (uint64)wasm_value_type_size_internal( + array_type->elem_type, comp_ctx->pointer_size) + * value_count; + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: + { + size += + get_init_expr_size(comp_ctx, comp_data, expr->u.binary.l_expr); + size += + get_init_expr_size(comp_ctx, comp_data, expr->u.binary.r_expr); + break; + } +#endif + default: + bh_assert(0); + } + + return size; +} + +static uint32 +get_table_init_data_size(AOTCompContext *comp_ctx, + AOTTableInitData *table_init_data) { - /* init expr type (4 bytes) + init expr value (8 bytes) - + func index count (4 bytes) + func indexes */ - return (uint32)(sizeof(uint32) + sizeof(uint64) + sizeof(uint32) - + sizeof(uint32) * table_init_data->func_index_count); + uint32 size, i; + + /* + * mode (4 bytes), elem_type (4 bytes) + * + * table_index(4 bytes) + */ + size = (uint32)(sizeof(uint32) * 2 + sizeof(uint32)) + /* Size of WasmRefType - inner padding (ref type + nullable + + heap_type) */ + + 8; + + size += get_init_expr_size(comp_ctx, comp_ctx->comp_data, + &table_init_data->offset); + + /* + value count/func index count (4 bytes) + init_values */ + size += sizeof(uint32); + for (i = 0; i < table_init_data->value_count; i++) { + size += get_init_expr_size(comp_ctx, comp_ctx->comp_data, + &table_init_data->init_values[i]); + } + + return size; } static uint32 -get_table_init_data_list_size(AOTTableInitData **table_init_data_list, +get_table_init_data_list_size(AOTCompContext *comp_ctx, + AOTTableInitData **table_init_data_list, uint32 table_init_data_count) { + /* + * ------------------------------ + * | table_init_data_count + * ------------------------------ + * | | U32 mode + * | AOTTableInitData[N] | U32 elem_type + * | | U32 table_index + * | | U32 offset.init_expr_type + * | | U64 offset.u.i64 + * | | U32 func_index_count / elem_count + * | | UINTPTR [func_index_count] / [elem_count] + * ------------------------------ + */ AOTTableInitData **table_init_data = table_init_data_list; uint32 size = 0, i; + /* table_init_data_count(4 bytes) */ + size = (uint32)sizeof(uint32); + for (i = 0; i < table_init_data_count; i++, table_init_data++) { size = align_uint(size, 4); - size += get_table_init_data_size(*table_init_data); + size += get_table_init_data_size(comp_ctx, *table_init_data); } return size; } static uint32 -get_table_info_size(AOTCompData *comp_data) +get_import_table_size(const AOTCompContext *comp_ctx, + const AOTCompData *comp_data) { - /* table size + init data count + init data list */ - return (uint32)sizeof(uint32) * 2 - + get_table_init_data_list_size(comp_data->table_init_data_list, - comp_data->table_init_data_count); + /* + * ------------------------------ + * | import_table_count + * ------------------------------ + * | | U8 elem_type + * | | U8 flags + * | | U8 possible_grow + * | AOTImportTable[N] | U8 elem_ref_type.nullable (for GC only) + * | | U32 init_size + * | | U32 max_size + * | | U32 elem_ref_type.heap_type (for GC only) + * ------------------------------ + */ + uint32 size = 0, i; + + size = (uint32)sizeof(uint32); + for (i = 0; i < comp_data->import_table_count; i++) { + size += sizeof(uint32) * 3; +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc + && comp_data->import_tables[i].table_type.elem_ref_type) + size += sizeof(uint32); +#endif + } + return size; } static uint32 -get_func_type_size(AOTFuncType *func_type) +get_table_size(const AOTCompContext *comp_ctx, const AOTCompData *comp_data) { - /* param count + result count + types */ - return (uint32)sizeof(uint32) * 2 - + func_type->param_count + func_type->result_count; + /* + * ------------------------------ + * | table_count + * ------------------------------ + * | | U8 elem_type + * | | U8 flags + * | | U8 possible_grow + * | AOTTable[N] | U8 elem_ref_type.nullable (for GC only) + * | | U32 init_size + * | | U32 max_size + * | | U32 elem_ref_type.heap_type (for GC only) + * | | N init_expr (for GC only) + * ------------------------------ + */ + uint32 size = 0, i; + + size = (uint32)sizeof(uint32); + for (i = 0; i < comp_data->table_count; i++) { + size += sizeof(uint32) * 3; +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + if (comp_data->tables[i].table_type.elem_ref_type) { + size += sizeof(uint32); + } + size += get_init_expr_size(comp_ctx, comp_data, + &comp_data->tables[i].init_expr); + } +#endif + } + return size; } static uint32 -get_func_types_size(AOTFuncType **func_types, uint32 func_type_count) +get_table_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) { - AOTFuncType **func_type = func_types; - uint32 size = 0, i; + /* + * ------------------------------ + * | import_table_count + * ------------------------------ + * | + * | AOTImportTable[import_table_count] + * | + * ------------------------------ + * | table_count + * ------------------------------ + * | + * | AOTTable[table_count] + * | + * ------------------------------ + * | table_init_data_count + * ------------------------------ + * | + * | AOTTableInitData*[table_init_data_count] + * | + * ------------------------------ + */ + return get_import_table_size(comp_ctx, comp_data) + + get_table_size(comp_ctx, comp_data) + + get_table_init_data_list_size(comp_ctx, + comp_data->table_init_data_list, + comp_data->table_init_data_count); +} - for (i = 0; i < func_type_count; i++, func_type++) { +static uint32 +get_func_type_size(AOTCompContext *comp_ctx, AOTFuncType *func_type) +{ +#if WASM_ENABLE_GC != 0 + /* type flag + equivalence type flag + is_sub_final + parent_type_idx + + rec_count + rec_idx + param count + result count + + ref_type_map_count + types + context of ref_type_map */ + if (comp_ctx->enable_gc) { + uint32 size = 0; + + /* type flag */ + size += sizeof(func_type->base_type.type_flag); + /* equivalence type flag + is_sub_final */ + size += sizeof(uint16); + /* parent_type_idx */ + size += sizeof(func_type->base_type.parent_type_idx); + /* rec_count */ + size += sizeof(func_type->base_type.rec_count); + /* rec_idx */ + size += sizeof(func_type->base_type.rec_idx); + /* param count */ + size += sizeof(func_type->param_count); + /* result count */ + size += sizeof(func_type->result_count); + /* ref_type_map_count */ + size += sizeof(func_type->ref_type_map_count); + /* param and result types */ + size += func_type->param_count + func_type->result_count; + /* align size */ size = align_uint(size, 4); - size += get_func_type_size(*func_type); + /* ref_type_map */ + size += func_type->ref_type_map_count * 8; + + return size; } + else +#endif + { + /* type flag + param count + result count + types */ + return (uint32)sizeof(uint16) * 3 + func_type->param_count + + func_type->result_count; + } +} + +#if WASM_ENABLE_GC != 0 +static uint32 +get_struct_type_size(AOTCompContext *comp_ctx, AOTStructType *struct_type) +{ + uint32 size = 0; + /* type flag + equivalence type flag + is_sub_final + parent_type_idx + + rec_count + rec_idx + field count + fields */ + + /* type flag */ + size += sizeof(struct_type->base_type.type_flag); + /* equivalence type flag + is_sub_final */ + size += sizeof(uint16); + /* parent_type_idx */ + size += sizeof(struct_type->base_type.parent_type_idx); + /* rec_count */ + size += sizeof(struct_type->base_type.rec_count); + /* rec_idx */ + size += sizeof(struct_type->base_type.rec_idx); + /* field count */ + size += sizeof(struct_type->field_count); + /* field types */ + size += struct_type->field_count * 2; + /* ref_type_map_count */ + size += sizeof(struct_type->ref_type_map_count); + size = align_uint(size, 4); + /* ref_type_map */ + size += struct_type->ref_type_map_count * 8; return size; } static uint32 -get_func_type_info_size(AOTCompData *comp_data) +get_array_type_size(AOTCompContext *comp_ctx, AOTArrayType *array_type) { - /* func type count + func type list */ - return (uint32)sizeof(uint32) - + get_func_types_size(comp_data->func_types, - comp_data->func_type_count); + uint32 size = 0; + /* type flag + equivalence type flag + is_sub_final + parent_type_idx + + rec_count + rec_idx + elem_flags + elem_type + elem_ref_type */ + + /* type flag */ + size += sizeof(array_type->base_type.type_flag); + /* equivalence type flag + is_sub_final */ + size += sizeof(uint16); + /* parent_type_idx (u32) */ + size += sizeof(array_type->base_type.parent_type_idx); + /* rec_count */ + size += sizeof(array_type->base_type.rec_count); + /* rec_idx */ + size += sizeof(array_type->base_type.rec_idx); + /* elem_flags (u16) */ + size += sizeof(array_type->elem_flags); + /* elem_type (u8) */ + size += sizeof(array_type->elem_type); + /* elem_ref_type */ + if (array_type->elem_ref_type) { + /* nullable (u8) */ + size += sizeof(uint8); + /* heap type (u32) */ + size += sizeof(uint32); + } + + return size; +} +#endif + +static uint32 +get_type_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) +{ + /* Initial size with size of type count */ + uint32 size = 4; + uint32 i; + +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + for (i = 0; i < comp_data->type_count; i++) { + uint32 j; + + size = align_uint(size, 4); + + /* Emit simple info if there is an equivalence type */ + for (j = 0; j < i; j++) { + if (comp_data->types[j] == comp_data->types[i]) { + /* type_flag (2 bytes) + equivalence type flag (1 byte) + + padding (1 byte) + equivalence type index */ + size += 8; + break; + } + } + if (j < i) + continue; + + if (comp_data->types[i]->type_flag == WASM_TYPE_FUNC) + size += get_func_type_size(comp_ctx, + (AOTFuncType *)comp_data->types[i]); + else if (comp_data->types[i]->type_flag == WASM_TYPE_STRUCT) + size += get_struct_type_size( + comp_ctx, (AOTStructType *)comp_data->types[i]); + else if (comp_data->types[i]->type_flag == WASM_TYPE_ARRAY) + size += get_array_type_size( + comp_ctx, (AOTArrayType *)comp_data->types[i]); + else + bh_assert(0); + } + } + else +#endif + { + for (i = 0; i < comp_data->type_count; i++) { + size = align_uint(size, 4); + size += get_func_type_size(comp_ctx, + (AOTFuncType *)comp_data->types[i]); + } + } + + return size; } static uint32 -get_import_global_size(AOTImportGlobal *import_global) +get_import_global_size(AOTCompContext *comp_ctx, AOTImportGlobal *import_global) { /* type (1 byte) + is_mutable (1 byte) + module_name + global_name */ uint32 size = (uint32)sizeof(uint8) * 2 - + get_string_size(import_global->module_name); + + get_string_size(comp_ctx, import_global->module_name); size = align_uint(size, 2); - size += get_string_size(import_global->global_name); + size += get_string_size(comp_ctx, import_global->global_name); return size; } static uint32 -get_import_globals_size(AOTImportGlobal *import_globals, +get_import_globals_size(AOTCompContext *comp_ctx, + AOTImportGlobal *import_globals, uint32 import_global_count) { AOTImportGlobal *import_global = import_globals; @@ -230,63 +667,66 @@ get_import_globals_size(AOTImportGlobal *import_globals, for (i = 0; i < import_global_count; i++, import_global++) { size = align_uint(size, 2); - size += get_import_global_size(import_global); + size += get_import_global_size(comp_ctx, import_global); } return size; } static uint32 -get_import_global_info_size(AOTCompData *comp_data) +get_import_global_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) { /* import global count + import globals */ return (uint32)sizeof(uint32) - + get_import_globals_size(comp_data->import_globals, + + get_import_globals_size(comp_ctx, comp_data->import_globals, comp_data->import_global_count); } static uint32 -get_global_size(AOTGlobal *global) +get_global_size(AOTCompContext *comp_ctx, AOTGlobal *global) { - /* type (1 byte) + is_mutable (1 byte) - + init expr type (2 byes) + init expr value (8 byes) */ - return sizeof(uint8) * 2 + sizeof(uint16) + sizeof(uint64); + /* type (1 byte) + is_mutable (1 byte) + padding (2 bytes) + + init expr value (include init expr type) */ + return sizeof(uint8) * 2 + sizeof(uint8) * 2 + + get_init_expr_size(comp_ctx, comp_ctx->comp_data, + &global->init_expr); } static uint32 -get_globals_size(AOTGlobal *globals, uint32 global_count) +get_globals_size(AOTCompContext *comp_ctx, AOTGlobal *globals, + uint32 global_count) { AOTGlobal *global = globals; uint32 size = 0, i; for (i = 0; i < global_count; i++, global++) { size = align_uint(size, 4); - size += get_global_size(global); + size += get_global_size(comp_ctx, global); } return size; } static uint32 -get_global_info_size(AOTCompData *comp_data) +get_global_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) { /* global count + globals */ return (uint32)sizeof(uint32) - + get_globals_size(comp_data->globals, + + get_globals_size(comp_ctx, comp_data->globals, comp_data->global_count); } static uint32 -get_import_func_size(AOTImportFunc *import_func) +get_import_func_size(AOTCompContext *comp_ctx, AOTImportFunc *import_func) { /* type index (2 bytes) + module_name + func_name */ uint32 size = (uint32)sizeof(uint16) - + get_string_size(import_func->module_name); + + get_string_size(comp_ctx, import_func->module_name); size = align_uint(size, 2); - size += get_string_size(import_func->func_name); + size += get_string_size(comp_ctx, import_func->func_name); return size; } static uint32 -get_import_funcs_size(AOTImportFunc *import_funcs, +get_import_funcs_size(AOTCompContext *comp_ctx, AOTImportFunc *import_funcs, uint32 import_func_count) { AOTImportFunc *import_func = import_funcs; @@ -294,138 +734,179 @@ get_import_funcs_size(AOTImportFunc *import_funcs, for (i = 0; i < import_func_count; i++, import_func++) { size = align_uint(size, 2); - size += get_import_func_size(import_func); + size += get_import_func_size(comp_ctx, import_func); } return size; } static uint32 -get_import_func_info_size(AOTCompData *comp_data) +get_import_func_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) { /* import func count + import funcs */ return (uint32)sizeof(uint32) - + get_import_funcs_size(comp_data->import_funcs, + + get_import_funcs_size(comp_ctx, comp_data->import_funcs, comp_data->import_func_count); } static uint32 -get_object_data_section_size(AOTObjectDataSection *data_section) -{ - /* name + size + data */ - uint32 size = get_string_size(data_section->name); - size = align_uint(size, 4); - size += (uint32)sizeof(uint32); - size += data_section->size; - return size; -} - -static uint32 -get_object_data_sections_size(AOTObjectDataSection *data_sections, +get_object_data_sections_size(AOTCompContext *comp_ctx, + AOTObjectDataSection *data_sections, uint32 data_sections_count) { AOTObjectDataSection *data_section = data_sections; uint32 size = 0, i; for (i = 0; i < data_sections_count; i++, data_section++) { + /* name + size + data */ size = align_uint(size, 2); - size += get_object_data_section_size(data_section); + size += get_string_size(comp_ctx, data_section->name); + size = align_uint(size, 4); + size += (uint32)sizeof(uint32); + size += data_section->size; } return size; } static uint32 -get_object_data_section_info_size(AOTObjectData *obj_data) +get_object_data_section_info_size(AOTCompContext *comp_ctx, + AOTObjectData *obj_data) { /* data sections count + data sections */ return (uint32)sizeof(uint32) - + get_object_data_sections_size(obj_data->data_sections, + + get_object_data_sections_size(comp_ctx, obj_data->data_sections, obj_data->data_sections_count); } static uint32 -get_init_data_section_size(AOTCompData *comp_data, AOTObjectData *obj_data) +get_init_data_section_size(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 size = 0; - size += get_mem_info_size(comp_data); + size += get_mem_info_size(comp_ctx, comp_data); size = align_uint(size, 4); - size += get_table_info_size(comp_data); + size += get_table_info_size(comp_ctx, comp_data); size = align_uint(size, 4); - size += get_func_type_info_size(comp_data); + size += get_type_info_size(comp_ctx, comp_data); size = align_uint(size, 4); - size += get_import_global_info_size(comp_data); + size += get_import_global_info_size(comp_ctx, comp_data); size = align_uint(size, 4); - size += get_global_info_size(comp_data); + size += get_global_info_size(comp_ctx, comp_data); size = align_uint(size, 4); - size += get_import_func_info_size(comp_data); + size += get_import_func_info_size(comp_ctx, comp_data); /* func count + start func index */ size = align_uint(size, 4); size += (uint32)sizeof(uint32) * 2; - /* llvm aux data end + llvm aux stack bottom - + llvm aux stack size + llvm stack global index */ - size += sizeof(uint32) * 4; + /* aux data/heap/stack data */ + size += sizeof(uint32) * 10; - size += get_object_data_section_info_size(obj_data); + size += get_object_data_section_info_size(comp_ctx, obj_data); return size; } static uint32 get_text_section_size(AOTObjectData *obj_data) { - return obj_data->text_size; + return sizeof(uint32) + align_uint(obj_data->literal_size, 4) + + align_uint(obj_data->text_size, 4) + + align_uint(obj_data->text_unlikely_size, 4) + + align_uint(obj_data->text_hot_size, 4); } static uint32 -get_func_section_size(AOTCompData *comp_data, AOTObjectData *obj_data) +get_func_section_size(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { - /* text offsets + function type indexs */ uint32 size = 0; - if (is_32bit_binary(obj_data->binary)) + /* text offsets */ + if (is_32bit_binary(obj_data)) size = (uint32)sizeof(uint32) * comp_data->func_count; else size = (uint32)sizeof(uint64) * comp_data->func_count; + /* function type indexes */ + size += (uint32)sizeof(uint32) * comp_data->func_count; + + /* max_local_cell_nums */ + size += (uint32)sizeof(uint32) * comp_data->func_count; + + /* max_stack_cell_nums */ size += (uint32)sizeof(uint32) * comp_data->func_count; + +#if WASM_ENABLE_GC != 0 + /* func_local_ref_flags */ + if (comp_ctx->enable_gc) { + AOTFuncType *func_type; + uint32 i, j, local_ref_flags_cell_num; + + for (i = 0; i < comp_data->import_func_count; i++) { + func_type = comp_data->import_funcs[i].func_type; + /* recalculate cell_num based on target pointer size */ + local_ref_flags_cell_num = 0; + for (j = 0; j < func_type->param_count; j++) { + local_ref_flags_cell_num += wasm_value_type_cell_num_internal( + func_type->types[j], comp_ctx->pointer_size); + } + local_ref_flags_cell_num = + local_ref_flags_cell_num > 2 ? local_ref_flags_cell_num : 2; + + size = align_uint(size, 4); + size += (uint32)sizeof(uint32); + size += (uint32)sizeof(uint8) * local_ref_flags_cell_num; + } + + for (i = 0; i < comp_data->func_count; i++) { + func_type = comp_data->funcs[i]->func_type; + local_ref_flags_cell_num = comp_data->funcs[i]->param_cell_num + + comp_data->funcs[i]->local_cell_num; + + size = align_uint(size, 4); + size += (uint32)sizeof(uint32); + size += (uint32)sizeof(uint8) * local_ref_flags_cell_num; + } + } +#endif + return size; } static uint32 -get_export_func_size(AOTExportFunc *export_func) +get_export_size(AOTCompContext *comp_ctx, AOTExport *export) { - /* export func index + export func name */ - return (uint32)sizeof(uint32) - + get_string_size(export_func->func_name); + /* export index + export kind + 1 byte padding + export name */ + return (uint32)sizeof(uint32) + sizeof(uint8) + 1 + + get_string_size(comp_ctx, export->name); } static uint32 -get_export_funcs_size(AOTExportFunc *export_funcs, - uint32 export_func_count) +get_exports_size(AOTCompContext *comp_ctx, AOTExport *exports, + uint32 export_count) { - AOTExportFunc *export_func = export_funcs; + AOTExport *export = exports; uint32 size = 0, i; - for (i = 0; i < export_func_count; i++, export_func++) { + for (i = 0; i < export_count; i++, export ++) { size = align_uint(size, 4); - size += get_export_func_size(export_func); + size += get_export_size(comp_ctx, export); } return size; } static uint32 -get_export_section_size(AOTCompData *comp_data) +get_export_section_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) { - /* export func count + export funcs */ + /* export count + exports */ return (uint32)sizeof(uint32) - + get_export_funcs_size(comp_data->export_funcs, - comp_data->export_func_count); + + get_exports_size(comp_ctx, comp_data->wasm_module->exports, + comp_data->wasm_module->export_count); } static uint32 @@ -436,21 +917,43 @@ get_relocation_size(AOTRelocation *relocation, bool is_32bin) if (is_32bin) size = sizeof(uint32) * 2; /* offset and addend */ else - size = sizeof(uint64) * 2; /* offset and addend */ + size = sizeof(uint64) * 2; /* offset and addend */ size += (uint32)sizeof(uint32); /* relocation type */ size += (uint32)sizeof(uint32); /* symbol name index */ return size; } static uint32 -get_relocations_size(AOTRelocation *relocations, - uint32 relocation_count, +get_relocations_size(AOTObjectData *obj_data, + AOTRelocationGroup *relocation_group, + AOTRelocation *relocations, uint32 relocation_count, bool is_32bin) { AOTRelocation *relocation = relocations; uint32 size = 0, i; for (i = 0; i < relocation_count; i++, relocation++) { + /* ignore the relocations to aot_func_internal#n in text section + for windows platform since they will be applied in + aot_emit_text_section */ + + const char *name = relocation->symbol_name; + if ((!strcmp(relocation_group->section_name, ".text") + || !strcmp(relocation_group->section_name, ".ltext")) + && !strncmp(name, AOT_FUNC_INTERNAL_PREFIX, + strlen(AOT_FUNC_INTERNAL_PREFIX)) + && ((!strncmp(obj_data->comp_ctx->target_arch, "x86_64", 6) + /* Windows AOT_COFF64_BIN_TYPE */ + && obj_data->target_info.bin_type == 6 + /* IMAGE_REL_AMD64_REL32 in windows x86_64 */ + && relocation->relocation_type == 4) + || (!strncmp(obj_data->comp_ctx->target_arch, "i386", 4) + /* Windows AOT_COFF32_BIN_TYPE */ + && obj_data->target_info.bin_type == 4 + /* IMAGE_REL_I386_REL32 in windows x86_32 */ + && relocation->relocation_type == 20))) { + continue; + } size = align_uint(size, 4); size += get_relocation_size(relocation, is_32bin); } @@ -458,30 +961,30 @@ get_relocations_size(AOTRelocation *relocations, } static uint32 -get_relocation_group_size(AOTRelocationGroup *relocation_group, - bool is_32bin) +get_relocation_group_size(AOTObjectData *obj_data, + AOTRelocationGroup *relocation_group, bool is_32bin) { uint32 size = 0; /* section name index + relocation count + relocations */ size += (uint32)sizeof(uint32); size += (uint32)sizeof(uint32); - size += get_relocations_size(relocation_group->relocations, - relocation_group->relocation_count, - is_32bin); + size += get_relocations_size(obj_data, relocation_group, + relocation_group->relocations, + relocation_group->relocation_count, is_32bin); return size; } static uint32 -get_relocation_groups_size(AOTRelocationGroup *relocation_groups, - uint32 relocation_group_count, - bool is_32bin) +get_relocation_groups_size(AOTObjectData *obj_data, + AOTRelocationGroup *relocation_groups, + uint32 relocation_group_count, bool is_32bin) { AOTRelocationGroup *relocation_group = relocation_groups; uint32 size = 0, i; for (i = 0; i < relocation_group_count; i++, relocation_group++) { size = align_uint(size, 4); - size += get_relocation_group_size(relocation_group, is_32bin); + size += get_relocation_group_size(obj_data, relocation_group, is_32bin); } return size; } @@ -489,8 +992,7 @@ get_relocation_groups_size(AOTRelocationGroup *relocation_groups, /* return the index (in order of insertion) of the symbol, create if not exits, -1 if failed */ static uint32 -get_relocation_symbol_index(const char *symbol_name, - bool *is_new, +get_relocation_symbol_index(const char *symbol_name, bool *is_new, AOTSymbolList *symbol_list) { AOTSymbolNode *sym; @@ -505,11 +1007,11 @@ get_relocation_symbol_index(const char *symbol_name, } sym = sym->next; - index ++; + index++; } /* Not found in symbol_list, add it */ - sym = bh_malloc(sizeof(AOTSymbolNode)); + sym = wasm_runtime_malloc(sizeof(AOTSymbolNode)); if (!sym) { return (uint32)-1; } @@ -525,7 +1027,7 @@ get_relocation_symbol_index(const char *symbol_name, symbol_list->end->next = sym; symbol_list->end = sym; } - symbol_list->len ++; + symbol_list->len++; if (is_new) *is_new = true; @@ -533,18 +1035,18 @@ get_relocation_symbol_index(const char *symbol_name, } static uint32 -get_relocation_symbol_size(AOTRelocation *relocation, +get_relocation_symbol_size(AOTCompContext *comp_ctx, AOTRelocation *relocation, AOTSymbolList *symbol_list) { uint32 size = 0, index = 0; bool is_new = false; - index = get_relocation_symbol_index(relocation->symbol_name, &is_new, symbol_list); + index = get_relocation_symbol_index(relocation->symbol_name, &is_new, + symbol_list); CHECK_SIZE(index); if (is_new) { - size += (uint32)sizeof(uint16); - size += (uint32)strlen(relocation->symbol_name); + size += get_string_size(comp_ctx, relocation->symbol_name); size = align_uint(size, 2); } @@ -553,15 +1055,16 @@ get_relocation_symbol_size(AOTRelocation *relocation, } static uint32 -get_relocations_symbol_size(AOTRelocation *relocations, - uint32 relocation_count, +get_relocations_symbol_size(AOTCompContext *comp_ctx, + AOTRelocation *relocations, uint32 relocation_count, AOTSymbolList *symbol_list) { AOTRelocation *relocation = relocations; uint32 size = 0, curr_size, i; for (i = 0; i < relocation_count; i++, relocation++) { - curr_size = get_relocation_symbol_size(relocation, symbol_list); + curr_size = + get_relocation_symbol_size(comp_ctx, relocation, symbol_list); CHECK_SIZE(curr_size); size += curr_size; @@ -570,28 +1073,27 @@ get_relocations_symbol_size(AOTRelocation *relocations, } static uint32 -get_relocation_group_symbol_size(AOTRelocationGroup *relocation_group, +get_relocation_group_symbol_size(AOTCompContext *comp_ctx, + AOTRelocationGroup *relocation_group, AOTSymbolList *symbol_list) { uint32 size = 0, index = 0, curr_size; bool is_new = false; - index = get_relocation_symbol_index(relocation_group->section_name, - &is_new, + index = get_relocation_symbol_index(relocation_group->section_name, &is_new, symbol_list); CHECK_SIZE(index); if (is_new) { - size += (uint32)sizeof(uint16); - size += (uint32)strlen(relocation_group->section_name); + size += get_string_size(comp_ctx, relocation_group->section_name); size = align_uint(size, 2); } relocation_group->name_index = index; - curr_size = get_relocations_symbol_size(relocation_group->relocations, - relocation_group->relocation_count, - symbol_list); + curr_size = get_relocations_symbol_size( + comp_ctx, relocation_group->relocations, + relocation_group->relocation_count, symbol_list); CHECK_SIZE(curr_size); size += curr_size; @@ -599,7 +1101,8 @@ get_relocation_group_symbol_size(AOTRelocationGroup *relocation_group, } static uint32 -get_relocation_groups_symbol_size(AOTRelocationGroup *relocation_groups, +get_relocation_groups_symbol_size(AOTCompContext *comp_ctx, + AOTRelocationGroup *relocation_groups, uint32 relocation_group_count, AOTSymbolList *symbol_list) { @@ -607,7 +1110,7 @@ get_relocation_groups_symbol_size(AOTRelocationGroup *relocation_groups, uint32 size = 0, curr_size, i; for (i = 0; i < relocation_group_count; i++, relocation_group++) { - curr_size = get_relocation_group_symbol_size(relocation_group, + curr_size = get_relocation_group_symbol_size(comp_ctx, relocation_group, symbol_list); CHECK_SIZE(curr_size); size += curr_size; @@ -616,7 +1119,8 @@ get_relocation_groups_symbol_size(AOTRelocationGroup *relocation_groups, } static uint32 -get_symbol_size_from_symbol_list(AOTSymbolList *symbol_list) +get_symbol_size_from_symbol_list(AOTCompContext *comp_ctx, + AOTSymbolList *symbol_list) { AOTSymbolNode *sym; uint32 size = 0; @@ -624,7 +1128,7 @@ get_symbol_size_from_symbol_list(AOTSymbolList *symbol_list) sym = symbol_list->head; while (sym) { /* (uint16)str_len + str */ - size += (uint32)sizeof(uint16) + sym->str_len; + size += get_string_size(comp_ctx, sym->symbol); size = align_uint(size, 2); sym = sym->next; } @@ -633,7 +1137,8 @@ get_symbol_size_from_symbol_list(AOTSymbolList *symbol_list) } static uint32 -get_relocation_section_symbol_size(AOTObjectData *obj_data) +get_relocation_section_symbol_size(AOTCompContext *comp_ctx, + AOTObjectData *obj_data) { AOTRelocationGroup *relocation_groups = obj_data->relocation_groups; uint32 relocation_group_count = obj_data->relocation_group_count; @@ -643,44 +1148,75 @@ get_relocation_section_symbol_size(AOTObjectData *obj_data) get symbol size from symbol list directly in the second calculation */ if (obj_data->symbol_list.len > 0) { symbol_table_size = - get_symbol_size_from_symbol_list(&obj_data->symbol_list); + get_symbol_size_from_symbol_list(comp_ctx, &obj_data->symbol_list); } else { - symbol_table_size = - get_relocation_groups_symbol_size(relocation_groups, - relocation_group_count, - &obj_data->symbol_list); + symbol_table_size = get_relocation_groups_symbol_size( + comp_ctx, relocation_groups, relocation_group_count, + &obj_data->symbol_list); } CHECK_SIZE(symbol_table_size); string_count = obj_data->symbol_list.len; - /* string_count + string_offsets + total_string_len + [str (string_len + str)] */ + /* string_count + string_offsets + total_string_len + + [str (string_len + str)] */ return (uint32)(sizeof(uint32) + sizeof(uint32) * string_count + sizeof(uint32) + symbol_table_size); } static uint32 -get_relocation_section_size(AOTObjectData *obj_data) +get_relocation_section_size(AOTCompContext *comp_ctx, AOTObjectData *obj_data) { AOTRelocationGroup *relocation_groups = obj_data->relocation_groups; uint32 relocation_group_count = obj_data->relocation_group_count; uint32 symbol_table_size = 0; - symbol_table_size = get_relocation_section_symbol_size(obj_data); + symbol_table_size = get_relocation_section_symbol_size(comp_ctx, obj_data); CHECK_SIZE(symbol_table_size); symbol_table_size = align_uint(symbol_table_size, 4); /* relocation group count + symbol_table + relocation groups */ return (uint32)sizeof(uint32) + symbol_table_size - + get_relocation_groups_size(relocation_groups, + + get_relocation_groups_size(obj_data, relocation_groups, relocation_group_count, - is_32bit_binary(obj_data->binary)); + is_32bit_binary(obj_data)); +} + +static uint32 +get_native_symbol_list_size(AOTCompContext *comp_ctx) +{ + uint32 len = 0; + AOTNativeSymbol *sym = NULL; + + sym = bh_list_first_elem(&comp_ctx->native_symbols); + + while (sym) { + len = align_uint(len, 2); + len += get_string_size(comp_ctx, sym->symbol); + sym = bh_list_elem_next(sym); + } + + return len; } +#if WASM_ENABLE_STRINGREF != 0 +static uint32 +get_string_literal_section_size(AOTCompContext *comp_ctx, + AOTCompData *comp_data); +#endif + static uint32 -get_aot_file_size(AOTCompData *comp_data, AOTObjectData *obj_data) +get_custom_sections_size(AOTCompContext *comp_ctx, AOTCompData *comp_data); + +uint32 +aot_get_aot_file_size(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 size = 0; + uint32 size_custom_section = 0; +#if WASM_ENABLE_STRINGREF != 0 + uint32 size_string_literal_section = 0; +#endif /* aot file header */ size += get_file_header_size(); @@ -695,7 +1231,7 @@ get_aot_file_size(AOTCompData *comp_data, AOTObjectData *obj_data) size = align_uint(size, 4); /* section id + section size */ size += (uint32)sizeof(uint32) * 2; - size += get_init_data_section_size(comp_data, obj_data); + size += get_init_data_section_size(comp_ctx, comp_data, obj_data); /* text section */ size = align_uint(size, 4); @@ -707,19 +1243,45 @@ get_aot_file_size(AOTCompData *comp_data, AOTObjectData *obj_data) size = align_uint(size, 4); /* section id + section size */ size += (uint32)sizeof(uint32) * 2; - size += get_func_section_size(comp_data, obj_data); + size += get_func_section_size(comp_ctx, comp_data, obj_data); /* export section */ size = align_uint(size, 4); /* section id + section size */ size += (uint32)sizeof(uint32) * 2; - size += get_export_section_size(comp_data); + size += get_export_section_size(comp_ctx, comp_data); /* relocation section */ size = align_uint(size, 4); /* section id + section size */ size += (uint32)sizeof(uint32) * 2; - size += get_relocation_section_size(obj_data); + size += get_relocation_section_size(comp_ctx, obj_data); + + if (get_native_symbol_list_size(comp_ctx) > 0) { + /* emit only when there are native symbols */ + size = align_uint(size, 4); + /* section id + section size + sub section id + symbol count */ + size += (uint32)sizeof(uint32) * 4; + size += get_native_symbol_list_size(comp_ctx); + } + + size_custom_section = get_custom_sections_size(comp_ctx, comp_data); + if (size_custom_section > 0) { + size = align_uint(size, 4); + size += size_custom_section; + } + +#if WASM_ENABLE_STRINGREF != 0 + /* string literal section */ + size_string_literal_section = + get_string_literal_section_size(comp_ctx, comp_data); + if (size_string_literal_section > 0) { + size = align_uint(size, 4); + /* section id + section size + sub section id */ + size += (uint32)sizeof(uint32) * 3; + size += size_string_literal_section; + } +#endif return size; } @@ -747,10 +1309,28 @@ exchange_uint32(uint8 *p_data) } static void -exchange_uint64(uint8 *pData) +exchange_uint64(uint8 *p_data) +{ + uint32 value; + + value = *(uint32 *)p_data; + *(uint32 *)p_data = *(uint32 *)(p_data + 4); + *(uint32 *)(p_data + 4) = value; + exchange_uint32(p_data); + exchange_uint32(p_data + 4); +} + +static void +exchange_uint128(uint8 *p_data) { - exchange_uint32(pData); - exchange_uint32(pData + 4); + /* swap high 64bit and low 64bit */ + uint64 value = *(uint64 *)p_data; + *(uint64 *)p_data = *(uint64 *)(p_data + 8); + *(uint64 *)(p_data + 8) = value; + /* exchange high 64bit */ + exchange_uint64(p_data); + /* exchange low 64bit */ + exchange_uint64(p_data + 8); } static union { @@ -760,70 +1340,386 @@ static union { #define is_little_endian() (__ue.b == 1) -#define CHECK_BUF(length) do { \ - if (buf + offset + length > buf_end) { \ - aot_set_last_error("buf overflow"); \ - return false; \ - } \ -} while (0) - -#define EMIT_U8(v) do { \ - CHECK_BUF(1); \ - *(uint8*)(buf + offset) = (uint8)v; \ - offset++; \ - } while (0) - -#define EMIT_U16(v) do { \ - uint16 t = (uint16)v; \ - CHECK_BUF(2); \ - if (!is_little_endian()) \ - exchange_uint16((uint8*)&t); \ - *(uint16*)(buf + offset) = t; \ - offset += (uint32)sizeof(uint16); \ - } while (0) - -#define EMIT_U32(v) do { \ - uint32 t = (uint32)v; \ - CHECK_BUF(4); \ - if (!is_little_endian()) \ - exchange_uint32((uint8*)&t); \ - *(uint32*)(buf + offset) = t; \ - offset += (uint32)sizeof(uint32); \ - } while (0) - -#define EMIT_U64(v) do { \ - uint64 t = (uint64)v; \ - CHECK_BUF(8); \ - if (!is_little_endian()) \ - exchange_uint64((uint8*)&t); \ - PUT_U64_TO_ADDR(buf + offset, t); \ - offset += (uint32)sizeof(uint64); \ - } while (0) - -#define EMIT_BUF(v, len) do { \ - CHECK_BUF(len); \ - memcpy(buf + offset, v, len); \ - offset += len; \ - } while (0) - -#define EMIT_STR(s) do { \ - uint32 str_len = (uint32)strlen(s); \ - EMIT_U16(str_len); \ - EMIT_BUF(s, str_len); \ - } while (0) +#define CHECK_BUF(length) \ + do { \ + if (buf + offset + length > buf_end) { \ + aot_set_last_error("buf overflow"); \ + return false; \ + } \ + } while (0) + +#define EMIT_U8(v) \ + do { \ + CHECK_BUF(1); \ + *(uint8 *)(buf + offset) = (uint8)v; \ + offset++; \ + } while (0) + +#define EMIT_U16(v) \ + do { \ + uint16 t = (uint16)v; \ + CHECK_BUF(2); \ + if (!is_little_endian()) \ + exchange_uint16((uint8 *)&t); \ + *(uint16 *)(buf + offset) = t; \ + offset += (uint32)sizeof(uint16); \ + } while (0) + +#define EMIT_U32(v) \ + do { \ + uint32 t = (uint32)v; \ + CHECK_BUF(4); \ + if (!is_little_endian()) \ + exchange_uint32((uint8 *)&t); \ + *(uint32 *)(buf + offset) = t; \ + offset += (uint32)sizeof(uint32); \ + } while (0) + +#define EMIT_U64(v) \ + do { \ + uint64 t = (uint64)v; \ + CHECK_BUF(8); \ + if (!is_little_endian()) \ + exchange_uint64((uint8 *)&t); \ + PUT_U64_TO_ADDR(buf + offset, t); \ + offset += (uint32)sizeof(uint64); \ + } while (0) + +#define EMIT_V128(v) \ + do { \ + uint64 *t = (uint64 *)v.i64x2; \ + CHECK_BUF(16); \ + if (!is_little_endian()) \ + exchange_uint128((uint8 *)t); \ + PUT_U64_TO_ADDR(buf + offset, t[0]); \ + offset += (uint32)sizeof(uint64); \ + PUT_U64_TO_ADDR(buf + offset, t[1]); \ + offset += (uint32)sizeof(uint64); \ + } while (0) + +#define EMIT_BUF(v, len) \ + do { \ + CHECK_BUF(len); \ + memcpy(buf + offset, v, len); \ + offset += len; \ + } while (0) + +/* Emit string with '\0' + */ +#define EMIT_STR(s) \ + do { \ + uint32 str_len = (uint32)strlen(s) + 1; \ + if (str_len > INT16_MAX) { \ + aot_set_last_error("emit string failed: " \ + "string too long"); \ + return false; \ + } \ + EMIT_U16(str_len); \ + EMIT_BUF(s, str_len); \ + } while (0) + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +static bool +read_leb(uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, bool sign, + uint64 *p_result) +{ + const uint8 *buf = *p_buf; + uint64 result = 0; + uint32 shift = 0; + uint32 offset = 0, bcnt = 0; + uint64 byte; + + while (true) { + /* uN or SN must not exceed ceil(N/7) bytes */ + if (bcnt + 1 > (maxbits + 6) / 7) { + aot_set_last_error("integer representation too long"); + return false; + } + + if (buf + offset + 1 > buf_end) { + aot_set_last_error("unexpected end of section or function"); + return false; + } + byte = buf[offset]; + offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + if (!sign && maxbits == 32 && shift >= maxbits) { + /* The top bits set represent values > 32 bits */ + if (((uint8)byte) & 0xf0) + goto fail_integer_too_large; + } + else if (sign && maxbits == 32) { + if (shift < maxbits) { + /* Sign extend, second highest bit is the sign bit */ + if ((uint8)byte & 0x40) + result |= (~((uint64)0)) << shift; + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x8; + int top_bits = ((uint8)byte) & 0xf0; + if ((sign_bit_set && top_bits != 0x70) + || (!sign_bit_set && top_bits != 0)) + goto fail_integer_too_large; + } + } + else if (sign && maxbits == 64) { + if (shift < maxbits) { + /* Sign extend, second highest bit is the sign bit */ + if ((uint8)byte & 0x40) + result |= (~((uint64)0)) << shift; + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x1; + int top_bits = ((uint8)byte) & 0xfe; + + if ((sign_bit_set && top_bits != 0x7e) + || (!sign_bit_set && top_bits != 0)) + goto fail_integer_too_large; + } + } + + *p_buf += offset; + *p_result = result; + return true; + +fail_integer_too_large: + aot_set_last_error("integer too large"); + return false; +} + +/* NOLINTNEXTLINE */ +#define read_leb_uint32(p, p_end, res) \ + do { \ + uint64 res64; \ + if (!read_leb((uint8 **)&p, p_end, 32, false, &res64)) \ + goto fail; \ + res = (uint32)res64; \ + } while (0) + +/* + * - transfer .name section in .wasm (comp_data->name_section_buf) to + * aot buf (comp_data->aot_name_section_buf) + * - leb128 to u32 + * - add `\0` at the end of every name, and adjust length(+1) + */ +static uint32 +get_name_section_size(AOTCompData *comp_data) +{ + /* original name section content in .wasm */ + const uint8 *p = comp_data->name_section_buf, + *p_end = comp_data->name_section_buf_end; + uint8 *buf, *buf_end; + uint32 name_type, subsection_size; + uint32 previous_name_type = 0; + uint32 num_func_name; + uint32 func_index; + uint32 previous_func_index = ~0U; + uint32 func_name_len; + uint32 name_index; + int i = 0; + uint32 name_len; + uint32 offset = 0; + uint32 max_aot_buf_size = 0; + + if (p >= p_end) { + aot_set_last_error("unexpected end"); + return 0; + } + + max_aot_buf_size = 4 * (uint32)(p_end - p); + if (!(buf = comp_data->aot_name_section_buf = + wasm_runtime_malloc(max_aot_buf_size))) { + aot_set_last_error("allocate memory for custom name section failed."); + return 0; + } + memset(buf, 0, (uint32)max_aot_buf_size); + buf_end = buf + max_aot_buf_size; + + /* the size of "name". it should be 4 */ + read_leb_uint32(p, p_end, name_len); + offset = align_uint(offset, 4); + EMIT_U32(name_len); + + if (name_len != 4 || p + name_len > p_end) { + aot_set_last_error("unexpected end"); + return 0; + } + + /* "name" */ + if (memcmp(p, "name", 4) != 0) { + aot_set_last_error("invalid custom name section"); + return 0; + } + EMIT_BUF(p, name_len); + p += name_len; + + while (p < p_end) { + read_leb_uint32(p, p_end, name_type); + if (i != 0) { + if (name_type == previous_name_type) { + aot_set_last_error("duplicate sub-section"); + return 0; + } + if (name_type < previous_name_type) { + aot_set_last_error("out-of-order sub-section"); + return 0; + } + } + previous_name_type = name_type; + read_leb_uint32(p, p_end, subsection_size); + switch (name_type) { + case SUB_SECTION_TYPE_FUNC: + if (subsection_size) { + offset = align_uint(offset, 4); + EMIT_U32(name_type); + EMIT_U32(subsection_size); + + read_leb_uint32(p, p_end, num_func_name); + EMIT_U32(num_func_name); + + for (name_index = 0; name_index < num_func_name; + name_index++) { + read_leb_uint32(p, p_end, func_index); + offset = align_uint(offset, 4); + EMIT_U32(func_index); + if (func_index == previous_func_index) { + aot_set_last_error("duplicate function name"); + return 0; + } + if (func_index < previous_func_index + && previous_func_index != ~0U) { + aot_set_last_error("out-of-order function index "); + return 0; + } + previous_func_index = func_index; + read_leb_uint32(p, p_end, func_name_len); + offset = align_uint(offset, 2); + + /* emit a string ends with `\0` */ + if (func_name_len + 1 > UINT16_MAX) { + aot_set_last_error( + "emit string failed: string too long"); + goto fail; + } + /* extra 1 byte for \0 */ + EMIT_U16(func_name_len + 1); + EMIT_BUF(p, func_name_len); + p += func_name_len; + EMIT_U8(0); + } + } + break; + case SUB_SECTION_TYPE_MODULE: /* TODO: Parse for module subsection + */ + case SUB_SECTION_TYPE_LOCAL: /* TODO: Parse for local subsection */ + default: + p = p + subsection_size; + break; + } + i++; + } + + return offset; +fail: + return 0; +} +#endif /* end of WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 */ + +#if WASM_ENABLE_STRINGREF != 0 +static uint32 +get_string_literal_section_size(AOTCompContext *comp_ctx, + AOTCompData *comp_data) +{ + uint32 i; + uint32 size = 0; + uint32 string_count = comp_data->string_literal_count; + + if (string_count == 0) { + return 0; + } + + /* reserved slot + string count + string_lengths */ + size += sizeof(uint32) * (2 + string_count); + + for (i = 0; i < string_count; i++) { + size += comp_data->string_literal_lengths_wp[i]; + } + + return size; +} +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + +static uint32 +get_custom_sections_size(AOTCompContext *comp_ctx, AOTCompData *comp_data) +{ +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + uint32 size = 0, i; + + for (i = 0; i < comp_ctx->custom_sections_count; i++) { + const char *section_name = comp_ctx->custom_sections_wp[i]; + const uint8 *content = NULL; + uint32 length = 0; + + if (strcmp(section_name, "name") == 0) { + /* custom name section */ + comp_data->aot_name_section_size = get_name_section_size(comp_data); + if (comp_data->aot_name_section_size == 0) { + LOG_WARNING("Can't find custom section [name], ignore it"); + continue; + } + + size = align_uint(size, 4); + /* section id + section size + sub section id */ + size += (uint32)sizeof(uint32) * 3; + size += comp_data->aot_name_section_size; + continue; + } + + content = wasm_loader_get_custom_section(comp_data->wasm_module, + section_name, &length); + if (!content) { + LOG_WARNING("Can't find custom section [%s], ignore it", + section_name); + continue; + } + + size = align_uint(size, 4); + /* section id + section size + sub section id */ + size += (uint32)sizeof(uint32) * 3; + /* section name and len */ + size += get_string_size(comp_ctx, section_name); + /* section content */ + size += length; + } + + return size; +#else + return 0; +#endif +} static bool aot_emit_file_header(uint8 *buf, uint8 *buf_end, uint32 *p_offset, AOTCompData *comp_data, AOTObjectData *obj_data) { uint32 offset = *p_offset; + uint32 aot_curr_version = AOT_CURRENT_VERSION; EMIT_U8('\0'); EMIT_U8('a'); EMIT_U8('o'); EMIT_U8('t'); - EMIT_U32(1); + EMIT_U32(aot_curr_version); *p_offset = offset; return true; @@ -848,7 +1744,8 @@ aot_emit_target_info_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_U16(target_info->e_machine); EMIT_U32(target_info->e_version); EMIT_U32(target_info->e_flags); - EMIT_U32(target_info->reserved); + EMIT_U64(target_info->feature_flags); + EMIT_U64(target_info->reserved); EMIT_BUF(target_info->arch, sizeof(target_info->arch)); if (offset - *p_offset != section_size + sizeof(uint32) * 2) { @@ -861,29 +1758,62 @@ aot_emit_target_info_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, return true; } +static bool +aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, InitializerExpression *expr); + static bool aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 offset = *p_offset, i; AOTMemInitData **init_datas = comp_data->mem_init_data_list; *p_offset = offset = align_uint(offset, 4); - EMIT_U32(comp_data->num_bytes_per_page); - EMIT_U32(comp_data->mem_init_page_count); - EMIT_U32(comp_data->mem_max_page_count); - EMIT_U32(comp_data->mem_init_data_count); + /* Emit import memory count, only emit 0 currently. + TODO: emit the actual import memory count and + the full import memory info. */ + EMIT_U32(0); + + /* Emit memory count */ + EMIT_U32(comp_data->memory_count); + /* Emit memory items */ + for (i = 0; i < comp_data->memory_count; i++) { + EMIT_U32(comp_data->memories[i].flags); + EMIT_U32(comp_data->memories[i].num_bytes_per_page); + EMIT_U32(comp_data->memories[i].init_page_count); + EMIT_U32(comp_data->memories[i].max_page_count); + } + /* Emit mem init data count */ + EMIT_U32(comp_data->mem_init_data_count); + /* Emit mem init data items */ for (i = 0; i < comp_data->mem_init_data_count; i++) { offset = align_uint(offset, 4); - EMIT_U32(init_datas[i]->offset.init_expr_type); - EMIT_U64(init_datas[i]->offset.u.i64); +#if WASM_ENABLE_BULK_MEMORY != 0 + if (comp_ctx->enable_bulk_memory) { + EMIT_U32(init_datas[i]->is_passive); + EMIT_U32(init_datas[i]->memory_index); + } + else +#endif + { + /* emit two placeholder to keep the same size */ + EMIT_U32(0); + EMIT_U32(0); + } + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + &init_datas[i]->offset)) + return false; EMIT_U32(init_datas[i]->byte_count); - EMIT_BUF(init_datas[i]->bytes, init_datas[i]->byte_count); + if (init_datas[i]->byte_count) { + EMIT_BUF(init_datas[i]->bytes, init_datas[i]->byte_count); + } } - if (offset - *p_offset != get_mem_info_size(comp_data)) { + if (offset - *p_offset != get_mem_info_size(comp_ctx, comp_data)) { aot_set_last_error("emit memory info failed."); return false; } @@ -894,27 +1824,281 @@ aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, } static bool -aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) +aot_emit_init_expr(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, InitializerExpression *expr) { - uint32 offset = *p_offset, i, j; - AOTTableInitData **init_datas = comp_data->table_init_data_list; + if (expr == NULL) { + aot_set_last_error("invalid init expr."); + return false; + } + uint32 offset = *p_offset; +#if WASM_ENABLE_GC != 0 + WASMModule *module = comp_ctx->comp_data->wasm_module; +#endif *p_offset = offset = align_uint(offset, 4); - EMIT_U32(comp_data->table_size); - EMIT_U32(comp_data->table_init_data_count); - - for (i = 0; i < comp_data->table_init_data_count; i++) { - offset = align_uint(offset, 4); - EMIT_U32(init_datas[i]->offset.init_expr_type); - EMIT_U64(init_datas[i]->offset.u.i64); - EMIT_U32(init_datas[i]->func_index_count); - for (j = 0; j < init_datas[i]->func_index_count; j++) - EMIT_U32(init_datas[i]->func_indexes[j]); + EMIT_U32(expr->init_expr_type); + switch (expr->init_expr_type) { + case INIT_EXPR_NONE: + break; + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_F32_CONST: + EMIT_U32(expr->u.unary.v.i32); + break; + case INIT_EXPR_TYPE_I64_CONST: + case INIT_EXPR_TYPE_F64_CONST: + EMIT_U64(expr->u.unary.v.i64); + break; + case INIT_EXPR_TYPE_V128_CONST: + EMIT_V128(expr->u.unary.v.v128); + break; + case INIT_EXPR_TYPE_GET_GLOBAL: + EMIT_U32(expr->u.unary.v.global_index); + break; + case INIT_EXPR_TYPE_FUNCREF_CONST: + case INIT_EXPR_TYPE_REFNULL_CONST: + EMIT_U32(expr->u.unary.v.ref_index); + break; +#if WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_I31_NEW: + EMIT_U32(expr->u.unary.v.i32); + break; + case INIT_EXPR_TYPE_STRUCT_NEW: + { + uint32 i; + WASMStructNewInitValues *init_values = + (WASMStructNewInitValues *)expr->u.unary.v.data; + WASMStructType *struct_type = NULL; + + EMIT_U32(init_values->type_idx); + EMIT_U32(init_values->count); + + bh_assert(init_values->type_idx < module->type_count); + + struct_type = + (WASMStructType *)module->types[init_values->type_idx]; + + bh_assert(struct_type); + bh_assert(struct_type->field_count == init_values->count); + + for (i = 0; i < init_values->count; i++) { + uint32 field_size = wasm_value_type_size_internal( + struct_type->fields[i].field_type, comp_ctx->pointer_size); + if (field_size <= sizeof(uint32)) + EMIT_U32(init_values->fields[i].u32); + else if (field_size == sizeof(uint64)) + EMIT_U64(init_values->fields[i].u64); + else if (field_size == sizeof(uint64) * 2) + EMIT_V128(init_values->fields[i].v128); + else { + bh_assert(0); + } + } + + break; + } + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + EMIT_U32(expr->u.unary.v.type_index); + break; + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + { + WASMArrayType *array_type = NULL; + + bh_assert(expr->u.unary.v.array_new_default.type_index + < module->type_count); + array_type = + (WASMArrayType *) + module->types[expr->u.unary.v.array_new_default.type_index]; + + EMIT_U32(array_type->elem_type); + EMIT_U32(expr->u.unary.v.array_new_default.type_index); + EMIT_U32(expr->u.unary.v.array_new_default.length); + break; + } + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + uint32 value_count, i, field_size; + WASMArrayNewInitValues *init_values = + (WASMArrayNewInitValues *)expr->u.unary.v.data; + WASMArrayType *array_type = NULL; + + bh_assert(init_values->type_idx < module->type_count); + array_type = (WASMArrayType *)module->types[init_values->type_idx]; + + EMIT_U32(array_type->elem_type); + EMIT_U32(init_values->type_idx); + EMIT_U32(init_values->length); + + value_count = + (expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) + ? init_values->length + : 1; + + field_size = wasm_value_type_size_internal(array_type->elem_type, + comp_ctx->pointer_size); + + for (i = 0; i < value_count; i++) { + if (field_size <= sizeof(uint32)) + EMIT_U32(init_values->elem_data[i].u32); + else if (field_size == sizeof(uint64)) + EMIT_U64(init_values->elem_data[i].u64); + else if (field_size == sizeof(uint64) * 2) + EMIT_V128(init_values->elem_data[i].v128); + else { + bh_assert(0); + } + } + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: + if (comp_ctx->enable_extended_const) { + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + expr->u.binary.l_expr)) { + return false; + } + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + expr->u.binary.r_expr)) { + return false; + } + } + break; +#endif + default: + aot_set_last_error("invalid init expr type."); + return false; } - if (offset - *p_offset != get_table_info_size(comp_data)) { + *p_offset = offset; + return true; +} + +static bool +aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i, j; + AOTTableInitData **init_datas = comp_data->table_init_data_list; + + *p_offset = offset = align_uint(offset, 4); + + /* Emit import table count */ + EMIT_U32(comp_data->import_table_count); + /* Emit table items */ + for (i = 0; i < comp_data->import_table_count; i++) { + /* TODO: + * EMIT_STR(comp_data->import_tables[i].module_name ); + * EMIT_STR(comp_data->import_tables[i].table_name); + */ + EMIT_U8(comp_data->import_tables[i].table_type.elem_type); + EMIT_U8(comp_data->import_tables[i].table_type.flags); + EMIT_U8(comp_data->import_tables[i].table_type.possible_grow); +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc + && comp_data->import_tables[i].table_type.elem_ref_type) { + EMIT_U8(comp_data->import_tables[i] + .table_type.elem_ref_type->ref_ht_common.nullable); + } + else +#endif + { + /* emit one placeholder to keep the same size */ + EMIT_U8(0); + } + EMIT_U32(comp_data->import_tables[i].table_type.init_size); + EMIT_U32(comp_data->import_tables[i].table_type.max_size); +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc + && comp_data->import_tables[i].table_type.elem_ref_type) { + bh_assert(wasm_is_type_multi_byte_type( + comp_data->import_tables[i].table_type.elem_type)); + EMIT_U32(comp_data->import_tables[i] + .table_type.elem_ref_type->ref_ht_common.heap_type); + } +#endif + } + + /* Emit table count */ + EMIT_U32(comp_data->table_count); + /* Emit table items */ + for (i = 0; i < comp_data->table_count; i++) { + EMIT_U8(comp_data->tables[i].table_type.elem_type); + EMIT_U8(comp_data->tables[i].table_type.flags); + EMIT_U8(comp_data->tables[i].table_type.possible_grow); +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc + && comp_data->tables[i].table_type.elem_ref_type) { + EMIT_U8(comp_data->tables[i] + .table_type.elem_ref_type->ref_ht_common.nullable); + } + else +#endif + { + /* emit one placeholder to keep the same size */ + EMIT_U8(0); + } + EMIT_U32(comp_data->tables[i].table_type.init_size); + EMIT_U32(comp_data->tables[i].table_type.max_size); +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + if (comp_data->tables[i].table_type.elem_ref_type) { + bh_assert(wasm_is_type_multi_byte_type( + comp_data->tables[i].table_type.elem_type)); + EMIT_U32( + comp_data->tables[i] + .table_type.elem_ref_type->ref_ht_common.heap_type); + } + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + &comp_data->tables[i].init_expr)) { + return false; + } + } +#endif + } + + /* Emit table init data count */ + EMIT_U32(comp_data->table_init_data_count); + /* Emit table init data items */ + for (i = 0; i < comp_data->table_init_data_count; i++) { + offset = align_uint(offset, 4); + EMIT_U32(init_datas[i]->mode); + EMIT_U32(init_datas[i]->elem_type); + EMIT_U32(init_datas[i]->table_index); + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + &init_datas[i]->offset)) + return false; + +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc && init_datas[i]->elem_ref_type) { + EMIT_U16(init_datas[i]->elem_ref_type->ref_ht_common.ref_type); + EMIT_U16(init_datas[i]->elem_ref_type->ref_ht_common.nullable); + EMIT_U32(init_datas[i]->elem_ref_type->ref_ht_common.heap_type); + } + else +#endif + { + EMIT_U16(init_datas[i]->elem_type); + EMIT_U16(0); + EMIT_U32(0); + } + EMIT_U32(init_datas[i]->value_count); + for (j = 0; j < init_datas[i]->value_count; j++) { + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + &init_datas[i]->init_values[j])) + return false; + } + } + + if (offset - *p_offset != get_table_info_size(comp_ctx, comp_data)) { aot_set_last_error("emit table info failed."); return false; } @@ -924,38 +2108,166 @@ aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, return true; } +#if WASM_ENABLE_GC != 0 static bool -aot_emit_func_type_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) +aot_emit_reftype_map(uint8 *buf, uint8 *buf_end, uint32 *p_offset, uint32 count, + WASMRefTypeMap *refmap) { uint32 offset = *p_offset, i; - AOTFuncType **func_types = comp_data->func_types; - *p_offset = offset = align_uint(offset, 4); + for (i = 0; i < count; i++) { + EMIT_U16(refmap->index); + WASMRefType *ref_type = refmap->ref_type; - EMIT_U32(comp_data->func_type_count); + /* Note: WASMRefType is a union type */ + EMIT_U8(ref_type->ref_ht_common.ref_type); + EMIT_U8(ref_type->ref_ht_common.nullable); + EMIT_U32(ref_type->ref_ht_common.heap_type); - for (i = 0; i < comp_data->func_type_count; i++) { - offset = align_uint(offset, 4); - EMIT_U32(func_types[i]->param_count); - EMIT_U32(func_types[i]->result_count); - EMIT_BUF(func_types[i]->types, - func_types[i]->param_count + func_types[i]->result_count); + refmap++; } - if (offset - *p_offset != get_func_type_info_size(comp_data)) { - aot_set_last_error("emit function type info failed."); - return false; + *p_offset = offset; + return true; +} +#endif + +static bool +aot_emit_type_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(comp_data->type_count); + +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + AOTType **types = comp_data->types; + int32 idx; + uint32 j; + + for (i = 0; i < comp_data->type_count; i++) { + offset = align_uint(offset, 4); + + /* Emit simple info if there is an equivalence type */ + for (j = 0; j < i; j++) { + if (types[j] == types[i]) { + EMIT_U16(types[i]->type_flag); + /* equivalence type flag is true */ + EMIT_U8(1); + EMIT_U8(0); + /* equivalence type index */ + EMIT_U32(j); + break; + } + } + if (j < i) + continue; + + EMIT_U16(types[i]->type_flag); + /* equivalence type flag is false */ + EMIT_U8(0); + EMIT_U8(types[i]->is_sub_final); + EMIT_U32(types[i]->parent_type_idx); + + EMIT_U16(types[i]->rec_count); + EMIT_U16(types[i]->rec_idx); + + /* Emit WASM_TYPE_FUNC */ + if (types[i]->type_flag == WASM_TYPE_FUNC) { + AOTFuncType *func_type = (AOTFuncType *)types[i]; + EMIT_U16(func_type->param_count); + EMIT_U16(func_type->result_count); + EMIT_U16(func_type->ref_type_map_count); + EMIT_BUF(func_type->types, + func_type->param_count + func_type->result_count); + + offset = align_uint(offset, 4); + + aot_emit_reftype_map(buf, buf_end, &offset, + func_type->ref_type_map_count, + func_type->ref_type_maps); + } + /* Emit WASM_TYPE_STRUCT */ + else if (types[i]->type_flag == WASM_TYPE_STRUCT) { + AOTStructType *struct_type = (AOTStructType *)types[i]; + EMIT_U16(struct_type->field_count); + EMIT_U16(struct_type->ref_type_map_count); + + for (idx = 0; idx < struct_type->field_count; idx++) { + EMIT_U8(struct_type->fields[idx].field_flags); + EMIT_U8(struct_type->fields[idx].field_type); + } + + offset = align_uint(offset, 4); + + aot_emit_reftype_map(buf, buf_end, &offset, + struct_type->ref_type_map_count, + struct_type->ref_type_maps); + } + /* Emit WASM_TYPE_ARRAY */ + else if (types[i]->type_flag == WASM_TYPE_ARRAY) { + AOTArrayType *array_type = (AOTArrayType *)types[i]; + EMIT_U16(array_type->elem_flags); + EMIT_U8(array_type->elem_type); + if (array_type->elem_ref_type) { + bh_assert( + wasm_is_type_multi_byte_type(array_type->elem_type)); + EMIT_U8(array_type->elem_ref_type->ref_ht_common.nullable); + EMIT_U32( + array_type->elem_ref_type->ref_ht_common.heap_type); + } + } + else { + aot_set_last_error("invalid type flag."); + return false; + } + } + + if (offset - *p_offset != get_type_info_size(comp_ctx, comp_data)) { + aot_set_last_error("emit function type info failed."); + return false; + } + + *p_offset = offset; } + else +#endif + { + AOTFuncType **func_types = (AOTFuncType **)comp_data->types; - *p_offset = offset; + for (i = 0; i < comp_data->type_count; i++) { + offset = align_uint(offset, 4); + /* If GC is disabled, only emit function type info */ + EMIT_U16(WASM_TYPE_FUNC); + /* Omit to emit dummy padding for is_sub_final, + * parent_type_index, rec_count, rec_idx, 10 bytes in total */ + EMIT_U16(func_types[i]->param_count); + EMIT_U16(func_types[i]->result_count); + /* Omit to emit dummy padding for ref_type_map_count, 2 bytes in + * total */ + EMIT_BUF(func_types[i]->types, + func_types[i]->param_count + func_types[i]->result_count); + } + + if (offset - *p_offset != get_type_info_size(comp_ctx, comp_data)) { + aot_set_last_error("emit function type info failed."); + return false; + } + + *p_offset = offset; + } return true; } static bool aot_emit_import_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 offset = *p_offset, i; AOTImportGlobal *import_global = comp_data->import_globals; @@ -966,14 +2278,15 @@ aot_emit_import_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, for (i = 0; i < comp_data->import_global_count; i++, import_global++) { offset = align_uint(offset, 2); - EMIT_U8(import_global->type); - EMIT_U8(import_global->is_mutable); + EMIT_U8(import_global->type.val_type); + EMIT_U8(import_global->type.is_mutable); EMIT_STR(import_global->module_name); offset = align_uint(offset, 2); EMIT_STR(import_global->global_name); } - if (offset - *p_offset != get_import_global_info_size(comp_data)) { + if (offset - *p_offset + != get_import_global_info_size(comp_ctx, comp_data)) { aot_set_last_error("emit import global info failed."); return false; } @@ -985,7 +2298,8 @@ aot_emit_import_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 offset = *p_offset, i; AOTGlobal *global = comp_data->globals; @@ -996,13 +2310,16 @@ aot_emit_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, for (i = 0; i < comp_data->global_count; i++, global++) { offset = align_uint(offset, 4); - EMIT_U8(global->type); - EMIT_U8(global->is_mutable); - EMIT_U16(global->init_expr.init_expr_type); - EMIT_U64(global->init_expr.u.i64); + EMIT_U8(global->type.val_type); + EMIT_U8(global->type.is_mutable); + + offset = align_uint(offset, 4); + if (!aot_emit_init_expr(buf, buf_end, &offset, comp_ctx, + &global->init_expr)) + return false; } - if (offset - *p_offset != get_global_info_size(comp_data)) { + if (offset - *p_offset != get_global_info_size(comp_ctx, comp_data)) { aot_set_last_error("emit global info failed."); return false; } @@ -1014,7 +2331,8 @@ aot_emit_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_import_func_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 offset = *p_offset, i; AOTImportFunc *import_func = comp_data->import_funcs; @@ -1031,7 +2349,7 @@ aot_emit_import_func_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_STR(import_func->func_name); } - if (offset - *p_offset != get_import_func_info_size(comp_data)) { + if (offset - *p_offset != get_import_func_info_size(comp_ctx, comp_data)) { aot_set_last_error("emit import function info failed."); return false; } @@ -1043,6 +2361,7 @@ aot_emit_import_func_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, AOTObjectData *obj_data) { uint32 offset = *p_offset, i; @@ -1057,10 +2376,35 @@ aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_STR(data_section->name); offset = align_uint(offset, 4); EMIT_U32(data_section->size); - EMIT_BUF(data_section->data, data_section->size); + if (obj_data->stack_sizes_section_name != NULL + && !strcmp(obj_data->stack_sizes_section_name, + data_section->name)) { + uint32 ss_offset = obj_data->stack_sizes_offset; + uint32 ss_size = + obj_data->func_count * sizeof(*obj_data->stack_sizes); + LOG_VERBOSE("Replacing stack_sizes in %s section, offset %" PRIu32 + ", size %" PRIu32, + obj_data->stack_sizes_section_name, ss_offset, ss_size); + bh_assert(ss_offset + ss_size <= data_section->size); + /* 0 .. ss_offset */ + if (ss_offset > 0) { + EMIT_BUF(data_section->data, ss_offset); + } + /* ss_offset .. ss_offset+ss_size */ + EMIT_BUF(obj_data->stack_sizes, ss_size); + /* ss_offset+ss_size .. data_section->size */ + if (data_section->size > ss_offset + ss_size) { + EMIT_BUF(data_section->data + ss_offset + ss_size, + data_section->size - (ss_offset + ss_size)); + } + } + else { + EMIT_BUF(data_section->data, data_section->size); + } } - if (offset - *p_offset != get_object_data_section_info_size(obj_data)) { + if (offset - *p_offset + != get_object_data_section_info_size(comp_ctx, obj_data)) { aot_set_last_error("emit object data section info failed."); return false; } @@ -1072,9 +2416,11 @@ aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { - uint32 section_size = get_init_data_section_size(comp_data, obj_data); + uint32 section_size = + get_init_data_section_size(comp_ctx, comp_data, obj_data); uint32 offset = *p_offset; *p_offset = offset = align_uint(offset, 4); @@ -1082,24 +2428,33 @@ aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_U32(AOT_SECTION_TYPE_INIT_DATA); EMIT_U32(section_size); - if (!aot_emit_mem_info(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_table_info(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_func_type_info(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_import_global_info(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_global_info(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_import_func_info(buf, buf_end, &offset, comp_data, obj_data)) + if (!aot_emit_mem_info(buf, buf_end, &offset, comp_ctx, comp_data, obj_data) + || !aot_emit_table_info(buf, buf_end, &offset, comp_ctx, comp_data, + obj_data) + || !aot_emit_type_info(buf, buf_end, &offset, comp_ctx, comp_data, + obj_data) + || !aot_emit_import_global_info(buf, buf_end, &offset, comp_ctx, + comp_data, obj_data) + || !aot_emit_global_info(buf, buf_end, &offset, comp_ctx, comp_data, + obj_data) + || !aot_emit_import_func_info(buf, buf_end, &offset, comp_ctx, + comp_data, obj_data)) return false; offset = align_uint(offset, 4); EMIT_U32(comp_data->func_count); EMIT_U32(comp_data->start_func_index); - EMIT_U32(comp_data->llvm_aux_data_end); - EMIT_U32(comp_data->llvm_aux_stack_bottom); - EMIT_U32(comp_data->llvm_aux_stack_size); - EMIT_U32(comp_data->llvm_aux_stack_global_index); + EMIT_U32(comp_data->aux_data_end_global_index); + EMIT_U64(comp_data->aux_data_end); + EMIT_U32(comp_data->aux_heap_base_global_index); + EMIT_U64(comp_data->aux_heap_base); + EMIT_U32(comp_data->aux_stack_top_global_index); + EMIT_U64(comp_data->aux_stack_bottom); + EMIT_U32(comp_data->aux_stack_size); - if (!aot_emit_object_data_section_info(buf, buf_end, &offset, obj_data)) + if (!aot_emit_object_data_section_info(buf, buf_end, &offset, comp_ctx, + obj_data)) return false; if (offset - *p_offset != section_size + sizeof(uint32) * 2) { @@ -1118,28 +2473,143 @@ aot_emit_text_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, { uint32 section_size = get_text_section_size(obj_data); uint32 offset = *p_offset; + uint8 placeholder = 0; + AOTRelocationGroup *relocation_group; + AOTRelocation *relocation; + uint32 i, j, relocation_count; + uint8 *text; *p_offset = offset = align_uint(offset, 4); EMIT_U32(AOT_SECTION_TYPE_TEXT); EMIT_U32(section_size); - EMIT_BUF(obj_data->text, obj_data->text_size); + EMIT_U32(obj_data->literal_size); + + if (obj_data->literal_size > 0) { + EMIT_BUF(obj_data->literal, obj_data->literal_size); + while (offset & 3) + EMIT_BUF(&placeholder, 1); + } + + text = buf + offset; + + if (obj_data->text_size > 0) { + EMIT_BUF(obj_data->text, obj_data->text_size); + while (offset & 3) + EMIT_BUF(&placeholder, 1); + } + if (obj_data->text_unlikely_size > 0) { + EMIT_BUF(obj_data->text_unlikely, obj_data->text_unlikely_size); + while (offset & 3) + EMIT_BUF(&placeholder, 1); + } + if (obj_data->text_hot_size > 0) { + EMIT_BUF(obj_data->text_hot, obj_data->text_hot_size); + while (offset & 3) + EMIT_BUF(&placeholder, 1); + } if (offset - *p_offset != section_size + sizeof(uint32) * 2) { aot_set_last_error("emit text section failed."); return false; } + /* apply relocations to aot_func_internal#n in text section for + windows platform */ + if ((!strncmp(obj_data->comp_ctx->target_arch, "x86_64", 6) + /* Windows AOT_COFF64_BIN_TYPE */ + && obj_data->target_info.bin_type == 6) + || (!strncmp(obj_data->comp_ctx->target_arch, "i386", 4) + /* Windows AOT_COFF32_BIN_TYPE */ + && obj_data->target_info.bin_type == 4)) { + relocation_group = obj_data->relocation_groups; + for (i = 0; i < obj_data->relocation_group_count; + i++, relocation_group++) { + /* relocation in text section */ + if ((!strcmp(relocation_group->section_name, ".text") + || !strcmp(relocation_group->section_name, ".ltext"))) { + relocation = relocation_group->relocations; + relocation_count = relocation_group->relocation_count; + for (j = 0; j < relocation_count; j++) { + /* relocation to aot_func_internal#n */ + const char *name = relocation->symbol_name; + if (str_starts_with(name, AOT_FUNC_INTERNAL_PREFIX) + && ((obj_data->target_info.bin_type + == 6 /* AOT_COFF64_BIN_TYPE */ + && relocation->relocation_type + == 4 /* IMAGE_REL_AMD64_REL32 */) + || (obj_data->target_info.bin_type + == 4 /* AOT_COFF32_BIN_TYPE */ + && relocation->relocation_type + == 20 /* IMAGE_REL_I386_REL32 */))) { + uint32 func_idx = + atoi(name + strlen(AOT_FUNC_INTERNAL_PREFIX)); + uint64 text_offset, reloc_offset, reloc_addend; + + bh_assert(func_idx < obj_data->func_count); + + text_offset = obj_data->funcs[func_idx] + .text_offset_of_aot_func_internal; + reloc_offset = relocation->relocation_offset; + reloc_addend = relocation->relocation_addend; + /* S + A - P */ + *(uint32 *)(text + reloc_offset) = + (uint32)(text_offset + reloc_addend - reloc_offset + - 4); + + /* remove current relocation as it has been applied */ + if (j < relocation_count - 1) { + uint32 move_size = + (uint32)(sizeof(AOTRelocation) + * (relocation_count - 1 - j)); + bh_memmove_s(relocation, move_size, relocation + 1, + move_size); + } + relocation_group->relocation_count--; + } + else { + relocation++; + } + } + } + } + } + *p_offset = offset; return true; } +#if WASM_ENABLE_GC != 0 +static bool +aot_emit_ref_flag(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + uint8 pointer_size, int8 type) +{ + uint32 j, offset = *p_offset; + uint16 value_type_cell_num; + + if (wasm_is_type_reftype(type) && !wasm_is_reftype_i31ref(type)) { + EMIT_U8(1); + if (pointer_size == sizeof(uint64)) + EMIT_U8(1); + } + else { + value_type_cell_num = wasm_value_type_cell_num(type); + for (j = 0; j < value_type_cell_num; j++) + EMIT_U8(0); + } + + *p_offset = offset; + return true; +} +#endif + static bool aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { - uint32 section_size = get_func_section_size(comp_data, obj_data); + uint32 section_size = get_func_section_size(comp_ctx, comp_data, obj_data); uint32 i, offset = *p_offset; AOTObjectFunc *func = obj_data->funcs; AOTFunc **funcs = comp_data->funcs; @@ -1150,7 +2620,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_U32(section_size); for (i = 0; i < obj_data->func_count; i++, func++) { - if (is_32bit_binary(obj_data->binary)) + if (is_32bit_binary(obj_data)) EMIT_U32(func->text_offset); else EMIT_U64(func->text_offset); @@ -1159,6 +2629,71 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, for (i = 0; i < comp_data->func_count; i++) EMIT_U32(funcs[i]->func_type_index); + for (i = 0; i < comp_data->func_count; i++) { + uint32 max_local_cell_num = + funcs[i]->param_cell_num + funcs[i]->local_cell_num; + EMIT_U32(max_local_cell_num); + } + + for (i = 0; i < comp_data->func_count; i++) + EMIT_U32(funcs[i]->max_stack_cell_num); + +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + /* emit func_local_ref_flag arrays for both import and AOTed funcs */ + AOTFuncType *func_type; + uint32 j, local_ref_flags_cell_num, paddings; + + for (i = 0; i < comp_data->import_func_count; i++) { + func_type = comp_data->import_funcs[i].func_type; + /* recalculate cell_num based on target pointer size */ + local_ref_flags_cell_num = 0; + for (j = 0; j < func_type->param_count; j++) { + local_ref_flags_cell_num += wasm_value_type_cell_num_internal( + func_type->types[j], comp_ctx->pointer_size); + } + paddings = + local_ref_flags_cell_num < 2 ? 2 - local_ref_flags_cell_num : 0; + local_ref_flags_cell_num = + local_ref_flags_cell_num > 2 ? local_ref_flags_cell_num : 2; + + offset = align_uint(offset, 4); + EMIT_U32(local_ref_flags_cell_num); + for (j = 0; j < func_type->param_count; j++) { + if (!aot_emit_ref_flag(buf, buf_end, &offset, + comp_ctx->pointer_size, + func_type->types[j])) + return false; + } + for (j = 0; j < paddings; j++) + EMIT_U8(0); + } + + for (i = 0; i < comp_data->func_count; i++) { + func_type = funcs[i]->func_type; + local_ref_flags_cell_num = + funcs[i]->param_cell_num + funcs[i]->local_cell_num; + + offset = align_uint(offset, 4); + EMIT_U32(local_ref_flags_cell_num); + /* emit local_ref_flag for param variables */ + for (j = 0; j < func_type->param_count; j++) { + if (!aot_emit_ref_flag(buf, buf_end, &offset, + comp_ctx->pointer_size, + func_type->types[j])) + return false; + } + /* emit local_ref_flag for local variables */ + for (j = 0; j < funcs[i]->local_count; j++) { + if (!aot_emit_ref_flag(buf, buf_end, &offset, + comp_ctx->pointer_size, + funcs[i]->local_types_wp[j])) + return false; + } + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { aot_set_last_error("emit function section failed."); return false; @@ -1171,22 +2706,26 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_export_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { - uint32 section_size = get_export_section_size(comp_data); - AOTExportFunc *func = comp_data->export_funcs;; - uint32 i, offset = *p_offset, export_func_count = comp_data->export_func_count; + uint32 section_size = get_export_section_size(comp_ctx, comp_data); + AOTExport *export = comp_data->wasm_module->exports; + uint32 export_count = comp_data->wasm_module->export_count; + uint32 i, offset = *p_offset; *p_offset = offset = align_uint(offset, 4); EMIT_U32(AOT_SECTION_TYPE_EXPORT); EMIT_U32(section_size); - EMIT_U32(export_func_count); + EMIT_U32(export_count); - for (i = 0; i < export_func_count; i++, func++) { + for (i = 0; i < export_count; i++, export ++) { offset = align_uint(offset, 4); - EMIT_U32(func->func_index); - EMIT_STR(func->func_name); + EMIT_U32(export->index); + EMIT_U8(export->kind); + EMIT_U8(0); + EMIT_STR(export->name); } if (offset - *p_offset != section_size + sizeof(uint32) * 2) { @@ -1201,7 +2740,9 @@ aot_emit_export_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_relocation_symbol_table(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, + AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 symbol_offset = 0, total_string_len = 0; uint32 offset = *p_offset; @@ -1211,10 +2752,10 @@ aot_emit_relocation_symbol_table(uint8 *buf, uint8 *buf_end, uint32 *p_offset, /* emit symbol offsets */ sym = (AOTSymbolNode *)(obj_data->symbol_list.head); - while(sym) { + while (sym) { EMIT_U32(symbol_offset); /* string_len + str[0 .. string_len - 1] */ - symbol_offset += (uint32)sizeof(uint16) + sym->str_len; + symbol_offset += get_string_size(comp_ctx, sym->symbol); symbol_offset = align_uint(symbol_offset, 2); sym = sym->next; } @@ -1237,9 +2778,10 @@ aot_emit_relocation_symbol_table(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_relocation_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { - uint32 section_size = get_relocation_section_size(obj_data); + uint32 section_size = get_relocation_section_size(comp_ctx, obj_data); uint32 i, offset = *p_offset; AOTRelocationGroup *relocation_group = obj_data->relocation_groups; @@ -1251,7 +2793,8 @@ aot_emit_relocation_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_U32(AOT_SECTION_TYPE_RELOCATION); EMIT_U32(section_size); - aot_emit_relocation_symbol_table(buf, buf_end, &offset, comp_data, obj_data); + aot_emit_relocation_symbol_table(buf, buf_end, &offset, comp_ctx, comp_data, + obj_data); offset = align_uint(offset, 4); EMIT_U32(obj_data->relocation_group_count); @@ -1269,7 +2812,7 @@ aot_emit_relocation_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, /* emit each relocation */ for (j = 0; j < relocation_group->relocation_count; j++, relocation++) { offset = align_uint(offset, 4); - if (is_32bit_binary(obj_data->binary)) { + if (is_32bit_binary(obj_data)) { EMIT_U32(relocation->relocation_offset); EMIT_U32(relocation->relocation_addend); } @@ -1291,29 +2834,205 @@ aot_emit_relocation_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, return true; } +static bool +aot_emit_native_symbol(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx) +{ + uint32 offset = *p_offset; + AOTNativeSymbol *sym = NULL; + + if (bh_list_length(&comp_ctx->native_symbols) == 0) + /* emit only when there are native symbols */ + return true; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_CUSTOM); + /* sub section id + symbol count + symbol list */ + EMIT_U32(sizeof(uint32) * 2 + get_native_symbol_list_size(comp_ctx)); + EMIT_U32(AOT_CUSTOM_SECTION_NATIVE_SYMBOL); + EMIT_U32(bh_list_length(&comp_ctx->native_symbols)); + + sym = bh_list_first_elem(&comp_ctx->native_symbols); + + while (sym) { + offset = align_uint(offset, 2); + EMIT_STR(sym->symbol); + sym = bh_list_elem_next(sym); + } + + *p_offset = offset; + + return true; +} + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +static bool +aot_emit_name_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTCompContext *comp_ctx) +{ + uint32 offset = *p_offset; + + if (comp_data->aot_name_section_size == 0) + return true; + + offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_CUSTOM); + /* sub section id + name section size */ + EMIT_U32(sizeof(uint32) * 1 + comp_data->aot_name_section_size); + EMIT_U32(AOT_CUSTOM_SECTION_NAME); + bh_memcpy_s((uint8 *)(buf + offset), (uint32)(buf_end - buf), + comp_data->aot_name_section_buf, + (uint32)comp_data->aot_name_section_size); + offset += comp_data->aot_name_section_size; + + *p_offset = offset; + + LOG_DEBUG("emit name section"); + return true; +} +#endif + +#if WASM_ENABLE_STRINGREF != 0 +static bool +aot_emit_string_literal_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, + AOTCompContext *comp_ctx) +{ + uint32 string_count = comp_data->string_literal_count; + + if (string_count > 0) { + uint32 offset = *p_offset; + uint32 i; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_CUSTOM); + /* sub section id + string literal section size */ + EMIT_U32(sizeof(uint32) * 1 + + get_string_literal_section_size(comp_ctx, comp_data)); + EMIT_U32(AOT_CUSTOM_SECTION_STRING_LITERAL); + + /* reserved */ + EMIT_U32(0); + + /* string literal count */ + EMIT_U32(string_count); + + for (i = 0; i < string_count; i++) { + EMIT_U32(comp_data->string_literal_lengths_wp[i]); + } + + for (i = 0; i < string_count; i++) { + uint32 string_length = comp_data->string_literal_lengths_wp[i]; + bh_memcpy_s((uint8 *)(buf + offset), (uint32)(buf_end - buf), + comp_data->string_literal_ptrs_wp[i], string_length); + offset += string_length; + } + + *p_offset = offset; + } + + return true; +} +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + +static bool +aot_emit_custom_sections(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTCompContext *comp_ctx) +{ +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + uint32 offset = *p_offset, i; + + for (i = 0; i < comp_ctx->custom_sections_count; i++) { + const char *section_name = comp_ctx->custom_sections_wp[i]; + const uint8 *content = NULL; + uint32 length = 0; + + if (strcmp(section_name, "name") == 0) { + *p_offset = offset; + if (!aot_emit_name_section(buf, buf_end, p_offset, comp_data, + comp_ctx)) + return false; + + offset = *p_offset; + continue; + } + + content = wasm_loader_get_custom_section(comp_data->wasm_module, + section_name, &length); + if (!content) { + /* Warning has been reported during calculating size */ + continue; + } + + offset = align_uint(offset, 4); + EMIT_U32(AOT_SECTION_TYPE_CUSTOM); + /* sub section id + content */ + EMIT_U32(sizeof(uint32) * 1 + get_string_size(comp_ctx, section_name) + + length); + EMIT_U32(AOT_CUSTOM_SECTION_RAW); + EMIT_STR(section_name); + bh_memcpy_s((uint8 *)(buf + offset), (uint32)(buf_end - buf), content, + length); + offset += length; + } + + *p_offset = offset; +#endif + + return true; +} + +typedef uint32 U32; +typedef int32 I32; +typedef uint16 U16; +typedef uint8 U8; + +struct coff_hdr { + U16 u16Machine; + U16 u16NumSections; + U32 u32DateTimeStamp; + U32 u32SymTblPtr; + U32 u32NumSymbols; + U16 u16PeHdrSize; + U16 u16Characs; +}; + +#define E_TYPE_REL 1 +#define E_TYPE_XIP 4 + +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 + +#define AOT_COFF32_BIN_TYPE 4 /* 32-bit little endian */ +#define AOT_COFF64_BIN_TYPE 6 /* 64-bit little endian */ + #define EI_NIDENT 16 -typedef uint32 elf32_word; -typedef int32 elf32_sword; -typedef uint16 elf32_half; -typedef uint32 elf32_off; -typedef uint32 elf32_addr; +typedef uint32 elf32_word; +typedef int32 elf32_sword; +typedef uint16 elf32_half; +typedef uint32 elf32_off; +typedef uint32 elf32_addr; struct elf32_ehdr { - unsigned char e_ident[EI_NIDENT]; /* ident bytes */ - elf32_half e_type; /* file type */ - elf32_half e_machine; /* target machine */ - elf32_word e_version; /* file version */ - elf32_addr e_entry; /* start address */ - elf32_off e_phoff; /* phdr file offset */ - elf32_off e_shoff; /* shdr file offset */ - elf32_word e_flags; /* file flags */ - elf32_half e_ehsize; /* sizeof ehdr */ - elf32_half e_phentsize; /* sizeof phdr */ - elf32_half e_phnum; /* number phdrs */ - elf32_half e_shentsize; /* sizeof shdr */ - elf32_half e_shnum; /* number shdrs */ - elf32_half e_shstrndx; /* shdr string index */ + unsigned char e_ident[EI_NIDENT]; /* ident bytes */ + elf32_half e_type; /* file type */ + elf32_half e_machine; /* target machine */ + elf32_word e_version; /* file version */ + elf32_addr e_entry; /* start address */ + elf32_off e_phoff; /* phdr file offset */ + elf32_off e_shoff; /* shdr file offset */ + elf32_word e_flags; /* file flags */ + elf32_half e_ehsize; /* sizeof ehdr */ + elf32_half e_phentsize; /* sizeof phdr */ + elf32_half e_phnum; /* number phdrs */ + elf32_half e_shentsize; /* sizeof shdr */ + elf32_half e_shnum; /* number shdrs */ + elf32_half e_shstrndx; /* shdr string index */ }; struct elf32_rel { @@ -1327,29 +3046,29 @@ struct elf32_rela { elf32_sword r_addend; } elf32_rela; -typedef uint32 elf64_word; -typedef int32 elf64_sword; -typedef uint64 elf64_xword; -typedef int64 elf64_sxword; -typedef uint16 elf64_half; -typedef uint64 elf64_off; -typedef uint64 elf64_addr; +typedef uint32 elf64_word; +typedef int32 elf64_sword; +typedef uint64 elf64_xword; +typedef int64 elf64_sxword; +typedef uint16 elf64_half; +typedef uint64 elf64_off; +typedef uint64 elf64_addr; struct elf64_ehdr { - unsigned char e_ident[EI_NIDENT]; /* ident bytes */ - elf64_half e_type; /* file type */ - elf64_half e_machine; /* target machine */ - elf64_word e_version; /* file version */ - elf64_addr e_entry; /* start address */ - elf64_off e_phoff; /* phdr file offset */ - elf64_off e_shoff; /* shdr file offset */ - elf64_word e_flags; /* file flags */ - elf64_half e_ehsize; /* sizeof ehdr */ - elf64_half e_phentsize; /* sizeof phdr */ - elf64_half e_phnum; /* number phdrs */ - elf64_half e_shentsize; /* sizeof shdr */ - elf64_half e_shnum; /* number shdrs */ - elf64_half e_shstrndx; /* shdr string index */ + unsigned char e_ident[EI_NIDENT]; /* ident bytes */ + elf64_half e_type; /* file type */ + elf64_half e_machine; /* target machine */ + elf64_word e_version; /* file version */ + elf64_addr e_entry; /* start address */ + elf64_off e_phoff; /* phdr file offset */ + elf64_off e_shoff; /* shdr file offset */ + elf64_word e_flags; /* file flags */ + elf64_half e_ehsize; /* sizeof ehdr */ + elf64_half e_phentsize; /* sizeof phdr */ + elf64_half e_phnum; /* number phdrs */ + elf64_half e_shentsize; /* sizeof shdr */ + elf64_half e_shnum; /* number shdrs */ + elf64_half e_shstrndx; /* shdr string index */ }; typedef struct elf64_rel { @@ -1363,14 +3082,38 @@ typedef struct elf64_rela { elf64_sxword r_addend; } elf64_rela; +#define SET_TARGET_INFO_VALUE(f, val, type, little) \ + do { \ + type tmp = val; \ + if ((little && !is_little_endian()) \ + || (!little && is_little_endian())) \ + exchange_##type((uint8 *)&tmp); \ + obj_data->target_info.f = tmp; \ + } while (0) + +#define SET_TARGET_INFO_FIELD(f, v, type, little) \ + SET_TARGET_INFO_VALUE(f, elf_header->v, type, little) + +/* in windows 32, the symbol name may start with '_' */ +static char * +LLVMGetSymbolNameAndUnDecorate(LLVMSymbolIteratorRef si, + AOTTargetInfo target_info) +{ + char *original_name = (char *)LLVMGetSymbolName(si); + if (!original_name) { + return NULL; + } -#define SET_TARGET_INFO(f, v, type, little) do { \ - type tmp = elf_header->v; \ - if ((little && !is_little_endian()) \ - || (!little && is_little_endian())) \ - exchange_##type((uint8*)&tmp); \ - obj_data->target_info.f = tmp; \ - } while (0) + if (target_info.bin_type != AOT_COFF32_BIN_TYPE) { + return original_name; + } + + if (*original_name == '_') { + return ++original_name; + } + + return original_name; +} static bool aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) @@ -1379,19 +3122,49 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) const uint8 *elf_buf = (uint8 *)LLVMGetBufferStart(obj_data->mem_buf); uint32 elf_size = (uint32)LLVMGetBufferSize(obj_data->mem_buf); - if (bin_type != LLVMBinaryTypeELF32L - && bin_type != LLVMBinaryTypeELF32B - && bin_type != LLVMBinaryTypeELF64L - && bin_type != LLVMBinaryTypeELF64B) { - aot_set_last_error("invaid llvm binary bin_type."); + if (bin_type != LLVMBinaryTypeCOFF && bin_type != LLVMBinaryTypeELF32L + && bin_type != LLVMBinaryTypeELF32B && bin_type != LLVMBinaryTypeELF64L + && bin_type != LLVMBinaryTypeELF64B + && bin_type != LLVMBinaryTypeMachO32L + && bin_type != LLVMBinaryTypeMachO32B + && bin_type != LLVMBinaryTypeMachO64L + && bin_type != LLVMBinaryTypeMachO64B) { + aot_set_last_error("invalid llvm binary bin_type."); return false; } obj_data->target_info.bin_type = bin_type - LLVMBinaryTypeELF32L; - if (bin_type == LLVMBinaryTypeELF32L || bin_type == LLVMBinaryTypeELF32B) { + if (bin_type == LLVMBinaryTypeCOFF) { + struct coff_hdr *coff_header; + + if (!elf_buf || elf_size < sizeof(struct coff_hdr)) { + aot_set_last_error("invalid coff_hdr buffer."); + return false; + } + coff_header = (struct coff_hdr *)elf_buf; + + /* Emit eXecute In Place file type while in indirect mode */ + if (comp_ctx->is_indirect_mode) + obj_data->target_info.e_type = E_TYPE_XIP; + else + obj_data->target_info.e_type = E_TYPE_REL; + + obj_data->target_info.e_machine = coff_header->u16Machine; + obj_data->target_info.e_version = 1; + obj_data->target_info.e_flags = 0; + + if (coff_header->u16Machine == IMAGE_FILE_MACHINE_AMD64 + || coff_header->u16Machine == IMAGE_FILE_MACHINE_IA64) + obj_data->target_info.bin_type = AOT_COFF64_BIN_TYPE; + else if (coff_header->u16Machine == IMAGE_FILE_MACHINE_I386) + obj_data->target_info.bin_type = AOT_COFF32_BIN_TYPE; + } + else if (bin_type == LLVMBinaryTypeELF32L + || bin_type == LLVMBinaryTypeELF32B) { struct elf32_ehdr *elf_header; bool is_little_bin = bin_type == LLVMBinaryTypeELF32L; + uint16 e_type; if (!elf_buf || elf_size < sizeof(struct elf32_ehdr)) { aot_set_last_error("invalid elf32 buffer."); @@ -1399,14 +3172,22 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) } elf_header = (struct elf32_ehdr *)elf_buf; - SET_TARGET_INFO(e_type, e_type, uint16, is_little_bin); - SET_TARGET_INFO(e_machine, e_machine, uint16, is_little_bin); - SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin); - SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin); + e_type = elf_header->e_type; + + /* Emit eXecute In Place file type while in indirect mode */ + if (comp_ctx->is_indirect_mode) + e_type = E_TYPE_XIP; + + SET_TARGET_INFO_VALUE(e_type, e_type, uint16, is_little_bin); + SET_TARGET_INFO_FIELD(e_machine, e_machine, uint16, is_little_bin); + SET_TARGET_INFO_FIELD(e_version, e_version, uint32, is_little_bin); + SET_TARGET_INFO_FIELD(e_flags, e_flags, uint32, is_little_bin); } - else { + else if (bin_type == LLVMBinaryTypeELF64L + || bin_type == LLVMBinaryTypeELF64B) { struct elf64_ehdr *elf_header; bool is_little_bin = bin_type == LLVMBinaryTypeELF64L; + uint16 e_type; if (!elf_buf || elf_size < sizeof(struct elf64_ehdr)) { aot_set_last_error("invalid elf64 buffer."); @@ -1414,20 +3195,89 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) } elf_header = (struct elf64_ehdr *)elf_buf; - SET_TARGET_INFO(e_type, e_type, uint16, is_little_bin); - SET_TARGET_INFO(e_machine, e_machine, uint16, is_little_bin); - SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin); - SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin); + e_type = elf_header->e_type; + + /* Emit eXecute In Place file type while in indirect mode */ + if (comp_ctx->is_indirect_mode) + e_type = E_TYPE_XIP; + + SET_TARGET_INFO_VALUE(e_type, e_type, uint16, is_little_bin); + SET_TARGET_INFO_FIELD(e_machine, e_machine, uint16, is_little_bin); + SET_TARGET_INFO_FIELD(e_version, e_version, uint32, is_little_bin); + SET_TARGET_INFO_FIELD(e_flags, e_flags, uint32, is_little_bin); + } + else if (bin_type == LLVMBinaryTypeMachO32L + || bin_type == LLVMBinaryTypeMachO32B) { + /* TODO: parse file type of Mach-O 32 */ + aot_set_last_error("invalid llvm binary bin_type."); + return false; + } + else if (bin_type == LLVMBinaryTypeMachO64L + || bin_type == LLVMBinaryTypeMachO64B) { + /* TODO: parse file type of Mach-O 64 */ + aot_set_last_error("invalid llvm binary bin_type."); + return false; } - strncpy(obj_data->target_info.arch, comp_ctx->target_arch, - sizeof(obj_data->target_info.arch)); + bh_assert(sizeof(obj_data->target_info.arch) + == sizeof(comp_ctx->target_arch)); + bh_memcpy_s(obj_data->target_info.arch, sizeof(obj_data->target_info.arch), + comp_ctx->target_arch, sizeof(comp_ctx->target_arch)); return true; } static bool aot_resolve_text(AOTObjectData *obj_data) +{ +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMBinaryType bin_type = LLVMBinaryGetType(obj_data->binary); + if (bin_type == LLVMBinaryTypeELF32L || bin_type == LLVMBinaryTypeELF64L) { + obj_data->text = (char *)LLVMGetBufferStart(obj_data->mem_buf); + obj_data->text_size = (uint32)LLVMGetBufferSize(obj_data->mem_buf); + } + else +#endif + { + LLVMSectionIteratorRef sec_itr; + char *name; + + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return false; + } + while ( + !LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr))) { + if (!strcmp(name, ".text") || !strcmp(name, ".ltext")) { + obj_data->text = (char *)LLVMGetSectionContents(sec_itr); + obj_data->text_size = (uint32)LLVMGetSectionSize(sec_itr); + } + else if (!strcmp(name, ".text.unlikely.") + || !strcmp(name, ".ltext.unlikely.")) { + obj_data->text_unlikely = + (char *)LLVMGetSectionContents(sec_itr); + obj_data->text_unlikely_size = + (uint32)LLVMGetSectionSize(sec_itr); + } + else if (!strcmp(name, ".text.hot.") + || !strcmp(name, ".ltext.hot.")) { + obj_data->text_hot = + (char *)LLVMGetSectionContents(sec_itr); + obj_data->text_hot_size = + (uint32)LLVMGetSectionSize(sec_itr); + } + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + } + + return true; +} + +static bool +aot_resolve_literal(AOTObjectData *obj_data) { LLVMSectionIteratorRef sec_itr; char *name; @@ -1437,9 +3287,10 @@ aot_resolve_text(AOTObjectData *obj_data) return false; } while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { - if ((name = (char *)LLVMGetSectionName(sec_itr)) && !strcmp(name, ".text")) { - obj_data->text = (char *)LLVMGetSectionContents(sec_itr); - obj_data->text_size = (uint32)LLVMGetSectionSize(sec_itr); + if ((name = (char *)LLVMGetSectionName(sec_itr)) + && !strcmp(name, ".literal")) { + obj_data->literal = (char *)LLVMGetSectionContents(sec_itr); + obj_data->literal_size = (uint32)LLVMGetSectionSize(sec_itr); break; } LLVMMoveToNextSection(sec_itr); @@ -1450,16 +3301,41 @@ aot_resolve_text(AOTObjectData *obj_data) } static bool -is_data_section(char *section_name) +get_relocations_count(LLVMSectionIteratorRef sec_itr, uint32 *p_count); + +static bool +is_data_section(AOTObjectData *obj_data, LLVMSectionIteratorRef sec_itr, + char *section_name) { - return (!strcmp(section_name, ".data") + uint32 relocation_count = 0; + + return (!strcmp(section_name, ".data") || !strcmp(section_name, ".sdata") || !strcmp(section_name, ".rodata") +#if LLVM_VERSION_MAJOR >= 19 + /* https://github.com/llvm/llvm-project/pull/82214 */ + || !strcmp(section_name, ".srodata") +#endif /* ".rodata.cst4/8/16/.." */ - || !strncmp(section_name, ".rodata.cst", strlen(".rodata.cst"))); + || !strncmp(section_name, ".rodata.cst", strlen(".rodata.cst")) +#if LLVM_VERSION_MAJOR >= 19 + /* https://github.com/llvm/llvm-project/pull/82214 + * ".srodata.cst4/8/16/.." */ + || !strncmp(section_name, ".srodata.cst", strlen(".srodata.cst")) +#endif + /* ".rodata.strn.m" */ + || !strncmp(section_name, ".rodata.str", strlen(".rodata.str")) + || (!strcmp(section_name, ".rdata") + && get_relocations_count(sec_itr, &relocation_count) + && relocation_count > 0) + || !strcmp(section_name, aot_stack_sizes_section_name) + || (obj_data->comp_ctx->enable_llvm_pgo + && (!strncmp(section_name, "__llvm_prf_cnts", 15) + || !strncmp(section_name, "__llvm_prf_data", 15) + || !strncmp(section_name, "__llvm_prf_names", 16)))); } -static uint32 -get_object_data_sections_count(AOTObjectData *obj_data) +static bool +get_object_data_sections_count(AOTObjectData *obj_data, uint32 *p_count) { LLVMSectionIteratorRef sec_itr; char *name; @@ -1467,18 +3343,19 @@ get_object_data_sections_count(AOTObjectData *obj_data) if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { aot_set_last_error("llvm get section iterator failed."); - return 0; + return false; } while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { if ((name = (char *)LLVMGetSectionName(sec_itr)) - && (is_data_section(name))) { + && (is_data_section(obj_data, sec_itr, name))) { count++; } LLVMMoveToNextSection(sec_itr); } LLVMDisposeSectionIterator(sec_itr); - return count; + *p_count = count; + return true; } static bool @@ -1487,12 +3364,20 @@ aot_resolve_object_data_sections(AOTObjectData *obj_data) LLVMSectionIteratorRef sec_itr; char *name; AOTObjectDataSection *data_section; - uint32 sections_count = get_object_data_sections_count(obj_data); + uint32 sections_count; uint32 size; + if (!get_object_data_sections_count(obj_data, §ions_count)) { + return false; + } + if (sections_count > 0) { + uint32 llvm_prf_cnts_idx = 0, llvm_prf_data_idx = 0; + char buf[32]; + size = (uint32)sizeof(AOTObjectDataSection) * sections_count; - if (!(data_section = obj_data->data_sections = bh_malloc(size))) { + if (!(data_section = obj_data->data_sections = + wasm_runtime_malloc(size))) { aot_set_last_error("allocate memory for data sections failed."); return false; } @@ -1503,13 +3388,55 @@ aot_resolve_object_data_sections(AOTObjectData *obj_data) aot_set_last_error("llvm get section iterator failed."); return false; } - while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, - sec_itr)) { + while ( + !LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { if ((name = (char *)LLVMGetSectionName(sec_itr)) - && (is_data_section(name))) { + && (is_data_section(obj_data, sec_itr, name))) { data_section->name = name; - data_section->data = (uint8 *)LLVMGetSectionContents(sec_itr); - data_section->size = (uint32)LLVMGetSectionSize(sec_itr); + if (obj_data->comp_ctx->enable_llvm_pgo + && !strcmp(name, "__llvm_prf_cnts")) { + snprintf(buf, sizeof(buf), "%s%u", name, + llvm_prf_cnts_idx++); + size = (uint32)(strlen(buf) + 1); + if (!(data_section->name = wasm_runtime_malloc(size))) { + aot_set_last_error( + "allocate memory for data section name failed."); + return false; + } + bh_memcpy_s(data_section->name, size, buf, size); + data_section->is_name_allocated = true; + } + else if (obj_data->comp_ctx->enable_llvm_pgo + && !strcmp(name, "__llvm_prf_data")) { + snprintf(buf, sizeof(buf), "%s%u", name, + llvm_prf_data_idx++); + size = (uint32)(strlen(buf) + 1); + if (!(data_section->name = wasm_runtime_malloc(size))) { + aot_set_last_error( + "allocate memory for data section name failed."); + return false; + } + bh_memcpy_s(data_section->name, size, buf, size); + data_section->is_name_allocated = true; + } + else if (obj_data->comp_ctx->enable_llvm_pgo + && !strcmp(name, "__llvm_prf_bits")) { + LOG_WARNING("__llvm_prf_bits section is not supported and " + "shouldn't be used in PGO."); + return false; + } + + if (obj_data->comp_ctx->enable_llvm_pgo + && !strcmp(name, "__llvm_prf_names")) { + data_section->data = (uint8 *)aot_compress_aot_func_names( + obj_data->comp_ctx, &data_section->size); + data_section->is_data_allocated = true; + } + else { + data_section->data = + (uint8 *)LLVMGetSectionContents(sec_itr); + data_section->size = (uint32)LLVMGetSectionSize(sec_itr); + } data_section++; } LLVMMoveToNextSection(sec_itr); @@ -1520,22 +3447,322 @@ aot_resolve_object_data_sections(AOTObjectData *obj_data) return true; } +static bool +read_stack_usage_file(const AOTCompContext *comp_ctx, const char *filename, + uint32 *sizes, uint32 count) +{ + FILE *fp = NULL; + if (filename == NULL) { + aot_set_last_error("no stack usage file is specified."); + return false; + } + fp = fopen(filename, "r"); + if (fp == NULL) { + LOG_ERROR("failed to open stack usage file: %s", filename); + goto fail; + } + /* + * the file consists of lines like: + * + * WASM Module:aot_func#9 72 static + */ + const char *aot_func_prefix = AOT_FUNC_PREFIX; + const char *aot_func_internal_prefix = AOT_FUNC_INTERNAL_PREFIX; + uint32 precheck_found = 0; + uint32 precheck_stack_size_max = 0; + uint32 precheck_stack_size_min = UINT32_MAX; + uint32 found = 0; + while (true) { + const char *prefix; + char line[100]; + char *cp = fgets(line, sizeof(line), fp); + char *fn; + char *colon; + uintmax_t func_idx; + uintmax_t sz; + int ret; + + if (cp == NULL) { + break; + } + /* + * Note: strrchr (not strchr) because a module name can contain + * colons. + */ + colon = strrchr(cp, ':'); + if (colon == NULL) { + goto fail; + } + fn = strstr(colon, aot_func_prefix); + if (fn != NULL) { + prefix = aot_func_prefix; + } + else { + fn = strstr(colon, aot_func_internal_prefix); + if (fn == NULL) { + LOG_ERROR("failed to parse stack usage line: %s", cp); + goto fail; + } + prefix = aot_func_internal_prefix; + } + ret = sscanf(fn + strlen(prefix), "%ju %ju static", &func_idx, &sz); + if (ret != 2) { + goto fail; + } + if (sz > UINT32_MAX) { + goto fail; + } + if (func_idx > UINT32_MAX) { + goto fail; + } + if (func_idx >= count) { + goto fail; + } + if (prefix == aot_func_prefix) { + if (sz < precheck_stack_size_min) { + precheck_stack_size_min = (uint32)sz; + } + if (sz > precheck_stack_size_max) { + precheck_stack_size_max = (uint32)sz; + } + precheck_found++; + continue; + } + sizes[func_idx] = (uint32)sz; + found++; + } + fclose(fp); + if (precheck_found != count) { + LOG_ERROR("%" PRIu32 " precheck entries found while %" PRIu32 + " entries are expected", + precheck_found, count); + return false; + } + if (found != count) { + /* + * LLVM seems to eliminate calls to an empty function + * (and eliminate the function) even if it's marked noinline. + */ + LOG_VERBOSE("%" PRIu32 " entries found while %" PRIu32 + " entries are expected. Maybe LLVM optimization eliminated " + "some functions.", + found, count); + } + if (precheck_stack_size_min != precheck_stack_size_max) { + /* + * Note: this is too strict. + * + * actually, the stack consumption of the precheck functions + * can depend on the type of them. + * that is, depending on various factors including + * calling conventions and compilers, a function with many + * parameters can consume more stack, even if it merely does + * a tail-call to another function. + */ + bool musttail = aot_target_precheck_can_use_musttail(comp_ctx); + if (musttail) { + LOG_WARNING( + "precheck functions use variable amount of stack. (%" PRIu32 + " - %" PRIu32 ")", + precheck_stack_size_min, precheck_stack_size_max); + } + else { + LOG_VERBOSE("precheck functions use %" PRIu32 " - %" PRIu32 + " bytes of stack.", + precheck_stack_size_min, precheck_stack_size_max); + } + } + else { + LOG_VERBOSE("precheck functions use %" PRIu32 " bytes of stack.", + precheck_stack_size_max); + } + if (precheck_stack_size_max >= 1024) { + LOG_WARNING("precheck functions themselves consume relatively large " + "amount of stack (%" PRIu32 + "). Please ensure the runtime has large enough " + "WASM_STACK_GUARD_SIZE.", + precheck_stack_size_max); + } + return true; +fail: + if (fp != NULL) + fclose(fp); + aot_set_last_error("failed to read stack usage file."); + return false; +} + +static bool +aot_resolve_stack_sizes(AOTCompContext *comp_ctx, AOTObjectData *obj_data) +{ + LLVMSectionIteratorRef sec_itr = NULL; + LLVMSymbolIteratorRef sym_itr; + const char *name; + + if (!(sym_itr = LLVMObjectFileCopySymbolIterator(obj_data->binary))) { + aot_set_last_error("llvm get symbol iterator failed."); + return false; + } + + while (!LLVMObjectFileIsSymbolIteratorAtEnd(obj_data->binary, sym_itr)) { + if ((name = + LLVMGetSymbolNameAndUnDecorate(sym_itr, obj_data->target_info)) + && (!strcmp(name, aot_stack_sizes_alias_name))) { +#if 0 /* cf. https://github.com/llvm/llvm-project/issues/67765 */ + uint64 sz = LLVMGetSymbolSize(sym_itr); + if (sz != sizeof(uint32) * obj_data->func_count + /* sz of COFF64/COFF32 is 0, ignore the check */ + && obj_data->target_info.bin_type != AOT_COFF64_BIN_TYPE + && obj_data->target_info.bin_type != AOT_COFF32_BIN_TYPE) { + aot_set_last_error("stack_sizes had unexpected size."); + goto fail; + } +#endif + uint64 addr = LLVMGetSymbolAddress(sym_itr); + if (!(sec_itr = + LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + goto fail; + } + LLVMMoveToContainingSection(sec_itr, sym_itr); + const char *sec_name = LLVMGetSectionName(sec_itr); + LOG_VERBOSE("stack_sizes found in section %s offset %" PRIu64 ".", + sec_name, addr); + if (strcmp(sec_name, aot_stack_sizes_section_name) || addr != 0) { + aot_set_last_error( + "stack_sizes found at an unexpected location."); + goto fail; + } + /* + * Note: We can't always modify stack_sizes in-place. + * E.g. When WAMRC_LLC_COMPILER is used, LLVM sometimes uses + * read-only mmap of the temporary file to back + * LLVMGetSectionContents. + */ + const uint32 *ro_stack_sizes = + (const uint32 *)(LLVMGetSectionContents(sec_itr) + addr); + uint32 i; + for (i = 0; i < obj_data->func_count; i++) { + /* Note: -1 == AOT_NEG_ONE from aot_create_stack_sizes */ + if (ro_stack_sizes[i] != (uint32)-1) { + aot_set_last_error("unexpected data in stack_sizes."); + goto fail; + } + } + /* + * Record section/offset and construct a copy of stack_sizes. + * aot_emit_object_data_section_info will emit this copy. + */ + obj_data->stack_sizes_section_name = sec_name; + obj_data->stack_sizes_offset = (uint32)addr; + obj_data->stack_sizes = wasm_runtime_malloc( + obj_data->func_count * sizeof(*obj_data->stack_sizes)); + if (obj_data->stack_sizes == NULL) { + aot_set_last_error("failed to allocate memory."); + goto fail; + } + uint32 *stack_sizes = obj_data->stack_sizes; + for (i = 0; i < obj_data->func_count; i++) { + stack_sizes[i] = (uint32)-1; + } + if (!read_stack_usage_file(comp_ctx, comp_ctx->stack_usage_file, + stack_sizes, obj_data->func_count)) { + goto fail; + } + for (i = 0; i < obj_data->func_count; i++) { + const AOTFuncContext *func_ctx = comp_ctx->func_ctxes[i]; + bool musttail = aot_target_precheck_can_use_musttail(comp_ctx); + unsigned int stack_consumption_to_call_wrapped_func = + musttail ? 0 + : aot_estimate_stack_usage_for_function_call( + comp_ctx, func_ctx->aot_func->func_type); + + /* + * LLVM seems to eliminate calls to an empty function + * (and eliminate the function) even if it's marked noinline. + * + * Note: -1 == AOT_NEG_ONE from aot_create_stack_sizes + */ + if (stack_sizes[i] == (uint32)-1) { + if (func_ctx->stack_consumption_for_func_call != 0) { + /* + * This happens if a function calling another + * function has been optimized out. + * + * for example, + * + * (func $func + * (local i32) + * local.get 0 + * if + * call $another + * end + * ) + */ + LOG_VERBOSE("AOT func#%" PRIu32 + " had call(s) but eliminated?", + i); + } + else { + LOG_VERBOSE("AOT func#%" PRIu32 " eliminated?", i); + } + stack_sizes[i] = 0; + } + else { + LOG_VERBOSE("AOT func#%" PRIu32 " stack_size %u + %" PRIu32 + " + %u", + i, stack_consumption_to_call_wrapped_func, + stack_sizes[i], + func_ctx->stack_consumption_for_func_call); + if (UINT32_MAX - stack_sizes[i] + < func_ctx->stack_consumption_for_func_call) { + aot_set_last_error("stack size overflow."); + goto fail; + } + stack_sizes[i] += func_ctx->stack_consumption_for_func_call; + if (UINT32_MAX - stack_sizes[i] + < stack_consumption_to_call_wrapped_func) { + aot_set_last_error("stack size overflow."); + goto fail; + } + stack_sizes[i] += stack_consumption_to_call_wrapped_func; + } + } + LLVMDisposeSectionIterator(sec_itr); + LLVMDisposeSymbolIterator(sym_itr); + return true; + } + LLVMMoveToNextSymbol(sym_itr); + } + aot_set_last_error("stack_sizes not found."); +fail: + if (sec_itr) + LLVMDisposeSectionIterator(sec_itr); + LLVMDisposeSymbolIterator(sym_itr); + return false; +} + static bool aot_resolve_functions(AOTCompContext *comp_ctx, AOTObjectData *obj_data) { AOTObjectFunc *func; LLVMSymbolIteratorRef sym_itr; char *name, *prefix = AOT_FUNC_PREFIX; - uint32 func_index; + uint32 func_index, total_size; /* allocate memory for aot function */ obj_data->func_count = comp_ctx->comp_data->func_count; - if (!(obj_data->funcs - = bh_malloc((uint32)sizeof(AOTObjectFunc) * obj_data->func_count))) { - aot_set_last_error("allocate memory for functions failed."); - return false; + if (obj_data->func_count) { + if ((comp_ctx->enable_stack_bound_check + || comp_ctx->enable_stack_estimation) + && !aot_resolve_stack_sizes(comp_ctx, obj_data)) + return false; + total_size = (uint32)sizeof(AOTObjectFunc) * obj_data->func_count; + if (!(obj_data->funcs = wasm_runtime_malloc(total_size))) { + aot_set_last_error("allocate memory for functions failed."); + return false; + } + memset(obj_data->funcs, 0, total_size); } - memset(obj_data->funcs, 0, sizeof(AOTObjectFunc) * obj_data->func_count); if (!(sym_itr = LLVMObjectFileCopySymbolIterator(obj_data->binary))) { aot_set_last_error("llvm get symbol iterator failed."); @@ -1543,13 +3770,82 @@ aot_resolve_functions(AOTCompContext *comp_ctx, AOTObjectData *obj_data) } while (!LLVMObjectFileIsSymbolIteratorAtEnd(obj_data->binary, sym_itr)) { - if ((name = (char *)LLVMGetSymbolName(sym_itr)) - && str_starts_with(name, prefix)) { + name = LLVMGetSymbolNameAndUnDecorate(sym_itr, obj_data->target_info); + if (name && str_starts_with(name, prefix)) { + /* symbol aot_func#n */ func_index = (uint32)atoi(name + strlen(prefix)); if (func_index < obj_data->func_count) { + LLVMSectionIteratorRef contain_section; + char *contain_section_name; + func = obj_data->funcs + func_index; func->func_name = name; - func->text_offset = LLVMGetSymbolAddress(sym_itr); + + if (!(contain_section = LLVMObjectFileCopySectionIterator( + obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + LLVMDisposeSymbolIterator(sym_itr); + return false; + } + LLVMMoveToContainingSection(contain_section, sym_itr); + contain_section_name = + (char *)LLVMGetSectionName(contain_section); + LLVMDisposeSectionIterator(contain_section); + + if (!strcmp(contain_section_name, ".text.unlikely.") + || !strcmp(contain_section_name, ".ltext.unlikely.")) { + func->text_offset = align_uint(obj_data->text_size, 4) + + LLVMGetSymbolAddress(sym_itr); + } + else if (!strcmp(contain_section_name, ".text.hot.") + || !strcmp(contain_section_name, ".ltext.hot.")) { + func->text_offset = + align_uint(obj_data->text_size, 4) + + align_uint(obj_data->text_unlikely_size, 4) + + LLVMGetSymbolAddress(sym_itr); + } + else { + func->text_offset = LLVMGetSymbolAddress(sym_itr); + } + } + } + else if (name && str_starts_with(name, AOT_FUNC_INTERNAL_PREFIX)) { + /* symbol aot_func_internal#n */ + func_index = (uint32)atoi(name + strlen(AOT_FUNC_INTERNAL_PREFIX)); + if (func_index < obj_data->func_count) { + LLVMSectionIteratorRef contain_section; + char *contain_section_name; + + func = obj_data->funcs + func_index; + + if (!(contain_section = LLVMObjectFileCopySectionIterator( + obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + LLVMDisposeSymbolIterator(sym_itr); + return false; + } + LLVMMoveToContainingSection(contain_section, sym_itr); + contain_section_name = + (char *)LLVMGetSectionName(contain_section); + LLVMDisposeSectionIterator(contain_section); + + if (!strcmp(contain_section_name, ".text.unlikely.") + || !strcmp(contain_section_name, ".ltext.unlikely.")) { + func->text_offset_of_aot_func_internal = + align_uint(obj_data->text_size, 4) + + LLVMGetSymbolAddress(sym_itr); + } + else if (!strcmp(contain_section_name, ".text.hot.") + || !strcmp(contain_section_name, ".ltext.hot.")) { + func->text_offset_of_aot_func_internal = + align_uint(obj_data->text_size, 4) + + align_uint(obj_data->text_unlikely_size, 4) + + LLVMGetSymbolAddress(sym_itr); + } + else { + func->text_offset_of_aot_func_internal = + LLVMGetSymbolAddress(sym_itr); + } } } LLVMMoveToNextSymbol(sym_itr); @@ -1589,12 +3885,12 @@ aot_resolve_object_relocation_group(AOTObjectData *obj_data, LLVMRelocationIteratorRef rel_itr; AOTRelocation *relocation = group->relocations; uint32 size; - bool is_binary_32bit = is_32bit_binary(obj_data->binary); - bool is_binary_little_endian = is_little_endian_binary(obj_data->binary); + bool is_binary_32bit = is_32bit_binary(obj_data); + bool is_binary_little_endian = is_little_endian_binary(obj_data); bool has_addend = str_starts_with(group->section_name, ".rela"); uint8 *rela_content = NULL; - /* calculate relocations count and allcate memory */ + /* calculate relocations count and allocate memory */ if (!get_relocations_count(rel_sec, &group->relocation_count)) return false; if (group->relocation_count == 0) { @@ -1602,7 +3898,7 @@ aot_resolve_object_relocation_group(AOTObjectData *obj_data, return false; } size = (uint32)sizeof(AOTRelocation) * group->relocation_count; - if (!(relocation = group->relocations = bh_malloc(size))) { + if (!(relocation = group->relocations = wasm_runtime_malloc(size))) { aot_set_last_error("allocate memory for relocations failed."); return false; } @@ -1624,7 +3920,7 @@ aot_resolve_object_relocation_group(AOTObjectData *obj_data, } } - /* pares each relocation */ + /* parse each relocation */ if (!(rel_itr = LLVMGetRelocations(rel_sec))) { aot_set_last_error("llvm get relocations failed."); return false; @@ -1639,17 +3935,19 @@ aot_resolve_object_relocation_group(AOTObjectData *obj_data, goto fail; } - /* parse relocation addend from reloction content */ + /* parse relocation addend from relocation content */ if (has_addend) { if (is_binary_32bit) { - uint32 addend = (uint32)(((struct elf32_rela *)rela_content)->r_addend); + int32 addend = + (int32)(((struct elf32_rela *)rela_content)->r_addend); if (is_binary_little_endian != is_little_endian()) exchange_uint32((uint8 *)&addend); - relocation->relocation_addend = (uint64)addend; + relocation->relocation_addend = (int64)addend; rela_content += sizeof(struct elf32_rela); } else { - uint64 addend = (uint64)(((struct elf64_rela *)rela_content)->r_addend); + int64 addend = + (int64)(((struct elf64_rela *)rela_content)->r_addend); if (is_binary_little_endian != is_little_endian()) exchange_uint64((uint8 *)&addend); relocation->relocation_addend = addend; @@ -1658,29 +3956,137 @@ aot_resolve_object_relocation_group(AOTObjectData *obj_data, } /* set relocation fields */ - relocation->relocation_offset = offset; relocation->relocation_type = (uint32)type; - relocation->symbol_name = (char *)LLVMGetSymbolName(rel_sym); + relocation->symbol_name = + LLVMGetSymbolNameAndUnDecorate(rel_sym, obj_data->target_info); + relocation->relocation_offset = offset; + if (!strcmp(group->section_name, ".rela.text.unlikely.") + || !strcmp(group->section_name, ".rel.text.unlikely.")) { + relocation->relocation_offset += align_uint(obj_data->text_size, 4); + } + else if (!strcmp(group->section_name, ".rela.text.hot.") + || !strcmp(group->section_name, ".rel.text.hot.")) { + relocation->relocation_offset += + align_uint(obj_data->text_size, 4) + + align_uint(obj_data->text_unlikely_size, 4); + } + if (!strcmp(relocation->symbol_name, ".text.unlikely.")) { + relocation->symbol_name = ".text"; + relocation->relocation_addend += align_uint(obj_data->text_size, 4); + } + if (!strcmp(relocation->symbol_name, ".text.hot.")) { + relocation->symbol_name = ".text"; + relocation->relocation_addend += + align_uint(obj_data->text_size, 4) + + align_uint(obj_data->text_unlikely_size, 4); + } - /* for ".LCPIxxx" relocation, transform the symbol name to real - * section name and set addend to the symbol address */ + /* + * Note: aot_stack_sizes_section_name section only contains + * stack_sizes table. + */ + if (!strcmp(relocation->symbol_name, aot_stack_sizes_name)) { + /* discard const */ + relocation->symbol_name = (char *)aot_stack_sizes_section_name; + } + + if (obj_data->comp_ctx->enable_llvm_pgo + && (!strcmp(relocation->symbol_name, "__llvm_prf_cnts") + || !strcmp(relocation->symbol_name, "__llvm_prf_data"))) { + LLVMSectionIteratorRef sec_itr; + char buf[32], *section_name; + uint32 prof_section_idx = 0; + + if (!(sec_itr = + LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + LLVMDisposeSymbolIterator(rel_sym); + goto fail; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, + sec_itr)) { + section_name = (char *)LLVMGetSectionName(sec_itr); + if (section_name + && !strcmp(section_name, relocation->symbol_name)) { + if (LLVMGetSectionContainsSymbol(sec_itr, rel_sym)) + break; + prof_section_idx++; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + + if (!strcmp(group->section_name, ".rela.text") + || !strcmp(group->section_name, ".rel.text")) { + snprintf(buf, sizeof(buf), "%s%u", relocation->symbol_name, + prof_section_idx); + size = (uint32)(strlen(buf) + 1); + if (!(relocation->symbol_name = wasm_runtime_malloc(size))) { + aot_set_last_error( + "allocate memory for relocation symbol name failed."); + LLVMDisposeSymbolIterator(rel_sym); + goto fail; + } + bh_memcpy_s(relocation->symbol_name, size, buf, size); + relocation->is_symbol_name_allocated = true; + } + else if (!strncmp(group->section_name, ".rela__llvm_prf_data", 20) + || !strncmp(group->section_name, ".rel__llvm_prf_data", + 19)) { + snprintf(buf, sizeof(buf), "%s%u", relocation->symbol_name, + prof_section_idx); + size = (uint32)(strlen(buf) + 1); + if (!(relocation->symbol_name = wasm_runtime_malloc(size))) { + aot_set_last_error( + "allocate memory for relocation symbol name failed."); + LLVMDisposeSymbolIterator(rel_sym); + goto fail; + } + bh_memcpy_s(relocation->symbol_name, size, buf, size); + relocation->is_symbol_name_allocated = true; + } + } + + /* for ".LCPIxxx", ".LJTIxxx", ".LBBxxx" and switch lookup table + * relocation, transform the symbol name to real section name and set + * addend to the offset of the symbol in the real section */ if (relocation->symbol_name - && str_starts_with(relocation->symbol_name, ".LCPI")) { - /* change relocation->relocation_addend and relocation->symbol_name */ + && (str_starts_with(relocation->symbol_name, ".LCPI") + || str_starts_with(relocation->symbol_name, ".LJTI") + || str_starts_with(relocation->symbol_name, ".LBB") + || str_starts_with(relocation->symbol_name, ".Lswitch.table.") +#if LLVM_VERSION_MAJOR >= 16 + /* cf. https://reviews.llvm.org/D123264 */ + || str_starts_with(relocation->symbol_name, ".Lpcrel_hi") +#endif +#if LLVM_VERSION_MAJOR >= 19 + /* cf. + * https://github.com/llvm/llvm-project/pull/95031 + * https://github.com/llvm/llvm-project/pull/89693 + * + * note: the trailing space in ".L0 " is intentional. */ + || !strcmp(relocation->symbol_name, "") + || !strcmp(relocation->symbol_name, ".L0 ") +#endif + )) { + /* change relocation->relocation_addend and + relocation->symbol_name */ LLVMSectionIteratorRef contain_section; - if (!(contain_section - = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + if (!(contain_section = + LLVMObjectFileCopySectionIterator(obj_data->binary))) { aot_set_last_error("llvm get section iterator failed."); goto fail; } LLVMMoveToContainingSection(contain_section, rel_sym); - if (LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, contain_section)) { + if (LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, + contain_section)) { LLVMDisposeSectionIterator(contain_section); aot_set_last_error("llvm get containing section failed."); goto fail; } relocation->relocation_addend += LLVMGetSymbolAddress(rel_sym); - relocation->symbol_name = (char *)LLVMGetSectionName(contain_section); + relocation->symbol_name = + (char *)LLVMGetSectionName(contain_section); LLVMDisposeSectionIterator(contain_section); } @@ -1697,14 +4103,30 @@ aot_resolve_object_relocation_group(AOTObjectData *obj_data, } static bool -is_relocation_section(char *section_name) +is_relocation_section_name(AOTObjectData *obj_data, char *section_name) { return (!strcmp(section_name, ".rela.text") || !strcmp(section_name, ".rel.text") + || !strcmp(section_name, ".rela.text.unlikely.") + || !strcmp(section_name, ".rel.text.unlikely.") + || !strcmp(section_name, ".rela.text.hot.") + || !strcmp(section_name, ".rel.text.hot.") + || !strcmp(section_name, ".rela.ltext") + || !strcmp(section_name, ".rel.ltext") + || !strcmp(section_name, ".rela.ltext.unlikely.") + || !strcmp(section_name, ".rel.ltext.unlikely.") + || !strcmp(section_name, ".rela.ltext.hot.") + || !strcmp(section_name, ".rel.ltext.hot.") + || !strcmp(section_name, ".rela.literal") || !strcmp(section_name, ".rela.data") || !strcmp(section_name, ".rel.data") + || !strcmp(section_name, ".rela.sdata") + || !strcmp(section_name, ".rel.sdata") || !strcmp(section_name, ".rela.rodata") || !strcmp(section_name, ".rel.rodata") + || (obj_data->comp_ctx->enable_llvm_pgo + && (!strcmp(section_name, ".rela__llvm_prf_data") + || !strcmp(section_name, ".rel__llvm_prf_data"))) /* ".rela.rodata.cst4/8/16/.." */ || !strncmp(section_name, ".rela.rodata.cst", strlen(".rela.rodata.cst")) @@ -1713,20 +4135,43 @@ is_relocation_section(char *section_name) strlen(".rel.rodata.cst"))); } +static bool +is_relocation_section(AOTObjectData *obj_data, LLVMSectionIteratorRef sec_itr) +{ + uint32 count = 0; + char *name = (char *)LLVMGetSectionName(sec_itr); + if (name) { + if (is_relocation_section_name(obj_data, name)) + return true; + else if ((!strcmp(name, ".text") || !strcmp(name, ".text.unlikely.") + || !strcmp(name, ".text.hot.") || !strcmp(name, ".rdata")) + && get_relocations_count(sec_itr, &count) && count > 0) + return true; + } + return false; +} + +static bool +is_readonly_section(const char *name) +{ + return !strcmp(name, ".rel.text") || !strcmp(name, ".rela.text") + || !strcmp(name, ".rel.ltext") || !strcmp(name, ".rela.ltext") + || !strcmp(name, ".rela.literal") || !strcmp(name, ".text") + || !strcmp(name, ".ltext"); +} + static bool get_relocation_groups_count(AOTObjectData *obj_data, uint32 *p_count) { uint32 count = 0; LLVMSectionIteratorRef sec_itr; - char *name; if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { aot_set_last_error("llvm get section iterator failed."); return false; } while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { - if ((name = (char *)LLVMGetSectionName(sec_itr)) - && is_relocation_section(name)) { + if (is_relocation_section(obj_data, sec_itr)) { count++; } LLVMMoveToNextSection(sec_itr); @@ -1742,11 +4187,11 @@ aot_resolve_object_relocation_groups(AOTObjectData *obj_data) { LLVMSectionIteratorRef sec_itr; AOTRelocationGroup *relocation_group; - uint32 group_count; + uint32 group_count, llvm_prf_data_idx = 0; char *name; uint32 size; - /* calculate relocation groups count and allcate memory */ + /* calculate relocation groups count and allocate memory */ if (!get_relocation_groups_count(obj_data, &group_count)) return false; @@ -1754,7 +4199,8 @@ aot_resolve_object_relocation_groups(AOTObjectData *obj_data) return true; size = (uint32)sizeof(AOTRelocationGroup) * group_count; - if (!(relocation_group = obj_data->relocation_groups = bh_malloc(size))) { + if (!(relocation_group = obj_data->relocation_groups = + wasm_runtime_malloc(size))) { aot_set_last_error("allocate memory for relocation groups failed."); return false; } @@ -1767,16 +4213,75 @@ aot_resolve_object_relocation_groups(AOTObjectData *obj_data) return false; } while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { - if ((name = (char *)LLVMGetSectionName(sec_itr)) - && is_relocation_section(name)) { + if (is_relocation_section(obj_data, sec_itr)) { + name = (char *)LLVMGetSectionName(sec_itr); relocation_group->section_name = name; - if (!aot_resolve_object_relocation_group( - obj_data, - relocation_group, - sec_itr)) { + + if (obj_data->comp_ctx->enable_llvm_pgo + && (!strcmp(name, ".rela__llvm_prf_data") + || !strcmp(name, ".rel__llvm_prf_data"))) { + char buf[32]; + snprintf(buf, sizeof(buf), "%s%u", name, llvm_prf_data_idx); + size = (uint32)(strlen(buf) + 1); + if (!(relocation_group->section_name = + wasm_runtime_malloc(size))) { + aot_set_last_error( + "allocate memory for section name failed."); + LLVMDisposeSectionIterator(sec_itr); + return false; + } + bh_memcpy_s(relocation_group->section_name, size, buf, size); + relocation_group->is_section_name_allocated = true; + } + + if (!aot_resolve_object_relocation_group(obj_data, relocation_group, + sec_itr)) { LLVMDisposeSectionIterator(sec_itr); return false; } + + if (obj_data->comp_ctx->enable_llvm_pgo + && (!strcmp(name, ".rela__llvm_prf_data") + || !strcmp(name, ".rel__llvm_prf_data"))) { + llvm_prf_data_idx++; + } + + if (!strcmp(relocation_group->section_name, ".rela.text.unlikely.") + || !strcmp(relocation_group->section_name, ".rela.text.hot.")) { + relocation_group->section_name = ".rela.text"; + } + else if (!strcmp(relocation_group->section_name, + ".rela.ltext.unlikely.") + || !strcmp(relocation_group->section_name, + ".rela.ltext.hot.")) { + relocation_group->section_name = ".rela.ltext"; + } + else if (!strcmp(relocation_group->section_name, + ".rel.text.unlikely.") + || !strcmp(relocation_group->section_name, + ".rel.text.hot.")) { + relocation_group->section_name = ".rel.text"; + } + else if (!strcmp(relocation_group->section_name, + ".rel.ltext.unlikely.") + || !strcmp(relocation_group->section_name, + ".rel.ltext.hot.")) { + relocation_group->section_name = ".rel.ltext"; + } + + /* + * Relocations in read-only sections are problematic, + * especially for XIP on platforms which don't have + * copy-on-write mappings. + */ + if (obj_data->comp_ctx->is_indirect_mode + && is_readonly_section(relocation_group->section_name)) { + LOG_WARNING("%" PRIu32 + " text relocations in %s section for indirect mode", + relocation_group->relocation_count, + relocation_group->section_name); + } + relocation_group++; } LLVMMoveToNextSection(sec_itr); @@ -1790,13 +4295,22 @@ static void destroy_relocation_groups(AOTRelocationGroup *relocation_groups, uint32 relocation_group_count) { - uint32 i; + uint32 i, j; AOTRelocationGroup *relocation_group = relocation_groups; - for (i = 0; i < relocation_group_count; i++, relocation_group++) - if (relocation_group->relocations) - bh_free(relocation_group->relocations); - bh_free(relocation_groups); + for (i = 0; i < relocation_group_count; i++, relocation_group++) { + if (relocation_group->relocations) { + for (j = 0; j < relocation_group->relocation_count; j++) { + if (relocation_group->relocations[j].is_symbol_name_allocated) + wasm_runtime_free( + relocation_group->relocations[j].symbol_name); + } + wasm_runtime_free(relocation_group->relocations); + } + if (relocation_group->is_section_name_allocated) + wasm_runtime_free(relocation_group->section_name); + } + wasm_runtime_free(relocation_groups); } static void @@ -1807,12 +4321,12 @@ destroy_relocation_symbol_list(AOTSymbolList *symbol_list) elem = symbol_list->head; while (elem) { AOTSymbolNode *next = elem->next; - bh_free(elem); + wasm_runtime_free(elem); elem = next; } } -static void +void aot_obj_data_destroy(AOTObjectData *obj_data) { if (obj_data->binary) @@ -1820,44 +4334,151 @@ aot_obj_data_destroy(AOTObjectData *obj_data) if (obj_data->mem_buf) LLVMDisposeMemoryBuffer(obj_data->mem_buf); if (obj_data->funcs) - bh_free(obj_data->funcs); - if (obj_data->data_sections) - bh_free(obj_data->data_sections); + wasm_runtime_free(obj_data->funcs); + if (obj_data->data_sections) { + uint32 i; + for (i = 0; i < obj_data->data_sections_count; i++) { + if (obj_data->data_sections[i].name + && obj_data->data_sections[i].is_name_allocated) { + wasm_runtime_free(obj_data->data_sections[i].name); + } + if (obj_data->data_sections[i].data + && obj_data->data_sections[i].is_data_allocated) { + wasm_runtime_free(obj_data->data_sections[i].data); + } + } + wasm_runtime_free(obj_data->data_sections); + } if (obj_data->relocation_groups) destroy_relocation_groups(obj_data->relocation_groups, obj_data->relocation_group_count); if (obj_data->symbol_list.len) destroy_relocation_symbol_list(&obj_data->symbol_list); - bh_free(obj_data); + if (obj_data->stack_sizes) + wasm_runtime_free(obj_data->stack_sizes); + wasm_runtime_free(obj_data); } -static AOTObjectData * +AOTObjectData * aot_obj_data_create(AOTCompContext *comp_ctx) { char *err = NULL; AOTObjectData *obj_data; + LLVMTargetRef target = LLVMGetTargetMachineTarget(comp_ctx->target_machine); + + bh_print_time("Begin to emit object file to buffer"); - if (!(obj_data = bh_malloc(sizeof(AOTObjectData)))) { + if (!(obj_data = wasm_runtime_malloc(sizeof(AOTObjectData)))) { aot_set_last_error("allocate memory failed."); return false; } memset(obj_data, 0, sizeof(AOTObjectData)); + obj_data->comp_ctx = comp_ctx; - if (LLVMTargetMachineEmitToMemoryBuffer(comp_ctx->target_machine, - comp_ctx->module, - LLVMObjectFile, - &err, - &obj_data->mem_buf) != 0) { - if (err) { - LLVMDisposeMessage(err); - err = NULL; + bh_print_time("Begin to emit object file"); + if (comp_ctx->external_llc_compiler || comp_ctx->external_asm_compiler) { + /* Generate a temp file name */ + int ret; + char obj_file_name[64]; + + if (!aot_generate_tempfile_name("wamrc-obj", "o", obj_file_name, + sizeof(obj_file_name))) { + goto fail; + } + + if (!aot_emit_object_file(comp_ctx, obj_file_name)) { + goto fail; + } + + /* create memory buffer from object file */ + ret = LLVMCreateMemoryBufferWithContentsOfFile( + obj_file_name, &obj_data->mem_buf, &err); + /* remove temp object file */ + unlink(obj_file_name); + + if (ret != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("create mem buffer with file failed."); + goto fail; + } + } + else if (!strncmp(LLVMGetTargetName(target), "arc", 3)) { + /* Emit to assembly file instead for arc target + as it cannot emit to object file */ + char file_name[] = "wasm-XXXXXX", buf[128]; + int ret; + + if (!bh_mkstemp(file_name, sizeof(file_name))) { + aot_set_last_error("make temp file failed."); + goto fail; + } + + snprintf(buf, sizeof(buf), "%s%s", file_name, ".s"); + if (LLVMTargetMachineEmitToFile(comp_ctx->target_machine, + comp_ctx->module, buf, LLVMAssemblyFile, + &err) + != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("emit elf to object file failed."); + goto fail; + } + + /* call arc gcc to compile assembly file to object file */ + /* TODO: get arc gcc from environment variable firstly + and check whether the toolchain exists actually */ + snprintf(buf, sizeof(buf), "%s%s%s%s%s%s", + "/opt/zephyr-sdk/arc-zephyr-elf/bin/arc-zephyr-elf-gcc ", + "-mcpu=arcem -o ", file_name, ".o -c ", file_name, ".s"); + /* TODO: use try..catch to handle possible exceptions */ + ret = bh_system(buf); + /* remove temp assembly file */ + snprintf(buf, sizeof(buf), "%s%s", file_name, ".s"); + unlink(buf); + + if (ret != 0) { + aot_set_last_error("failed to compile asm file to obj file " + "with arc gcc toolchain."); + goto fail; + } + + /* create memory buffer from object file */ + snprintf(buf, sizeof(buf), "%s%s", file_name, ".o"); + ret = LLVMCreateMemoryBufferWithContentsOfFile(buf, &obj_data->mem_buf, + &err); + /* remove temp object file */ + snprintf(buf, sizeof(buf), "%s%s", file_name, ".o"); + unlink(buf); + + if (ret != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("create mem buffer with file failed."); + goto fail; + } + } + else { + if (LLVMTargetMachineEmitToMemoryBuffer( + comp_ctx->target_machine, comp_ctx->module, LLVMObjectFile, + &err, &obj_data->mem_buf) + != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("llvm emit to memory buffer failed."); + goto fail; } - aot_set_last_error("llvm emit to memory buffer failed."); - goto fail; } - if (!(obj_data->binary = - LLVMCreateBinary(obj_data->mem_buf, NULL, &err))) { + if (!(obj_data->binary = LLVMCreateBinary(obj_data->mem_buf, NULL, &err))) { if (err) { LLVMDisposeMessage(err); err = NULL; @@ -1866,12 +4487,41 @@ aot_obj_data_create(AOTCompContext *comp_ctx) goto fail; } + /* Create wasm feature flags form compile options */ + obj_data->target_info.feature_flags = 0; + if (comp_ctx->enable_simd) { + obj_data->target_info.feature_flags |= WASM_FEATURE_SIMD_128BIT; + } + if (comp_ctx->enable_bulk_memory) { + obj_data->target_info.feature_flags |= WASM_FEATURE_BULK_MEMORY; + } + if (comp_ctx->enable_thread_mgr) { + obj_data->target_info.feature_flags |= WASM_FEATURE_MULTI_THREAD; + } + if (comp_ctx->enable_ref_types) { + obj_data->target_info.feature_flags |= WASM_FEATURE_REF_TYPES; + } + if (comp_ctx->enable_gc) { + obj_data->target_info.feature_flags |= WASM_FEATURE_GARBAGE_COLLECTION; + } + if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_TINY) { + obj_data->target_info.feature_flags |= WASM_FEATURE_TINY_STACK_FRAME; + } + if (comp_ctx->call_stack_features.frame_per_function) { + obj_data->target_info.feature_flags |= WASM_FEATURE_FRAME_PER_FUNCTION; + } + if (!comp_ctx->call_stack_features.func_idx) { + obj_data->target_info.feature_flags |= WASM_FEATURE_FRAME_NO_FUNC_IDX; + } + + bh_print_time("Begin to resolve object file info"); + /* resolve target info/text/relocations/functions */ if (!aot_resolve_target_info(comp_ctx, obj_data) - || !aot_resolve_text(obj_data) + || !aot_resolve_text(obj_data) || !aot_resolve_literal(obj_data) || !aot_resolve_object_data_sections(obj_data) - || !aot_resolve_object_relocation_groups(obj_data) - || !aot_resolve_functions(comp_ctx, obj_data)) + || !aot_resolve_functions(comp_ctx, obj_data) + || !aot_resolve_object_relocation_groups(obj_data)) goto fail; return obj_data; @@ -1881,37 +4531,74 @@ aot_obj_data_create(AOTCompContext *comp_ctx) return NULL; } -bool -aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, - const char *file_name) +uint8 * +aot_emit_aot_file_buf(AOTCompContext *comp_ctx, AOTCompData *comp_data, + uint32 *p_aot_file_size) { AOTObjectData *obj_data = aot_obj_data_create(comp_ctx); - uint8 *aot_file_buf, *buf, *buf_end; - uint32 aot_file_size, offset = 0; - bool ret = false; - FILE *file; + uint8 *aot_file_buf; + uint32 aot_file_size; if (!obj_data) - return false; + return NULL; - aot_file_size = get_aot_file_size(comp_data, obj_data); + aot_file_size = aot_get_aot_file_size(comp_ctx, comp_data, obj_data); + if (aot_file_size == 0) { + aot_set_last_error("get aot file size failed"); + goto fail1; + } - if (!(buf = aot_file_buf = bh_malloc(aot_file_size))) { + if (!(aot_file_buf = wasm_runtime_malloc(aot_file_size))) { aot_set_last_error("allocate memory failed."); goto fail1; } memset(aot_file_buf, 0, aot_file_size); - buf_end = buf + aot_file_size; + if (!aot_emit_aot_file_buf_ex(comp_ctx, comp_data, obj_data, aot_file_buf, + aot_file_size)) + goto fail2; + + *p_aot_file_size = aot_file_size; + + aot_obj_data_destroy(obj_data); + return aot_file_buf; + +fail2: + wasm_runtime_free(aot_file_buf); + +fail1: + aot_obj_data_destroy(obj_data); + return NULL; +} + +bool +aot_emit_aot_file_buf_ex(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data, uint8 *buf, + uint32 aot_file_size) +{ + uint8 *buf_end = buf + aot_file_size; + uint32 offset = 0; if (!aot_emit_file_header(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_target_info_section(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_init_data_section(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_target_info_section(buf, buf_end, &offset, comp_data, + obj_data) + || !aot_emit_init_data_section(buf, buf_end, &offset, comp_ctx, + comp_data, obj_data) || !aot_emit_text_section(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_func_section(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_export_section(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_relocation_section(buf, buf_end, &offset, comp_data, obj_data)) - goto fail2; + || !aot_emit_func_section(buf, buf_end, &offset, comp_ctx, comp_data, + obj_data) + || !aot_emit_export_section(buf, buf_end, &offset, comp_ctx, comp_data, + obj_data) + || !aot_emit_relocation_section(buf, buf_end, &offset, comp_ctx, + comp_data, obj_data) + || !aot_emit_native_symbol(buf, buf_end, &offset, comp_ctx) + || !aot_emit_custom_sections(buf, buf_end, &offset, comp_data, comp_ctx) +#if WASM_ENABLE_STRINGREF != 0 + || !aot_emit_string_literal_section(buf, buf_end, &offset, comp_data, + comp_ctx) +#endif + ) + return false; #if 0 dump_buf(buf, offset, "sections"); @@ -1919,28 +4606,45 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, if (offset != aot_file_size) { aot_set_last_error("emit aot file failed."); - goto fail2; + return false; + } + + return true; +} + +bool +aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, + const char *file_name) +{ + uint8 *aot_file_buf; + uint32 aot_file_size; + bool ret = false; + FILE *file; + + bh_print_time("Begin to emit AOT file"); + + if (!(aot_file_buf = + aot_emit_aot_file_buf(comp_ctx, comp_data, &aot_file_size))) { + return false; } /* write buffer to file */ if (!(file = fopen(file_name, "wb"))) { aot_set_last_error("open or create aot file failed."); - goto fail2; + goto fail1; } if (!fwrite(aot_file_buf, aot_file_size, 1, file)) { aot_set_last_error("write to aot file failed."); - goto fail3; + goto fail2; } ret = true; -fail3: - fclose(file); - fail2: - bh_free(aot_file_buf); + fclose(file); fail1: - aot_obj_data_destroy(obj_data); + wasm_runtime_free(aot_file_buf); + return ret; } diff --git a/core/iwasm/compilation/aot_emit_aot_file.h b/core/iwasm/compilation/aot_emit_aot_file.h new file mode 100644 index 0000000000..efdf44ff94 --- /dev/null +++ b/core/iwasm/compilation/aot_emit_aot_file.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_AOT_FILE_H_ +#define _AOT_EMIT_AOT_FILE_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AOTObjectData AOTObjectData; + +AOTObjectData * +aot_obj_data_create(AOTCompContext *comp_ctx); + +void +aot_obj_data_destroy(AOTObjectData *obj_data); + +uint32 +aot_get_aot_file_size(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data); + +bool +aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, + const char *file_name); + +uint8 * +aot_emit_aot_file_buf(AOTCompContext *comp_ctx, AOTCompData *comp_data, + uint32 *p_aot_file_size); + +bool +aot_emit_aot_file_buf_ex(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data, uint8 *aot_file_buf, + uint32 aot_file_size); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_AOT_FILE_H_ */ diff --git a/core/iwasm/compilation/aot_emit_compare.c b/core/iwasm/compilation/aot_emit_compare.c index 94c96a539f..c57bdee402 100644 --- a/core/iwasm/compilation/aot_emit_compare.c +++ b/core/iwasm/compilation/aot_emit_compare.c @@ -4,6 +4,7 @@ */ #include "aot_emit_compare.h" +#include "../aot/aot_intrinsic.h" static bool int_cond_to_llvm_op(IntCond cond, LLVMIntPredicate *op) @@ -22,25 +23,25 @@ int_cond_to_llvm_op(IntCond cond, LLVMIntPredicate *op) case INT_LT_S: *op = LLVMIntSLT; break; - case INT_LT_U: + case INT_LT_U: *op = LLVMIntULT; break; case INT_GT_S: *op = LLVMIntSGT; break; - case INT_GT_U: + case INT_GT_U: *op = LLVMIntUGT; break; case INT_LE_S: *op = LLVMIntSLE; break; - case INT_LE_U: + case INT_LE_U: *op = LLVMIntULE; break; case INT_GE_S: *op = LLVMIntSGE; break; - case INT_GE_U: + case INT_GE_U: *op = LLVMIntUGE; break; default: @@ -157,7 +158,25 @@ aot_compile_op_f32_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, POP_F32(rhs); POP_F32(lhs); - if (!(res = LLVMBuildFCmp(comp_ctx->builder, op, lhs, rhs, "f32_cmp"))) { + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, "f32_cmp")) { + LLVMTypeRef param_types[3]; + LLVMValueRef opcond = LLVMConstInt(I32_TYPE, cond, true); + param_types[0] = I32_TYPE; + param_types[1] = F32_TYPE; + param_types[2] = F32_TYPE; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, "f32_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, "f32_cmp"); + } + + if (!res) { aot_set_last_error("llvm build compare failed."); return false; } @@ -183,7 +202,25 @@ aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, POP_F64(rhs); POP_F64(lhs); - if (!(res = LLVMBuildFCmp(comp_ctx->builder, op, lhs, rhs, "f64_cmp"))) { + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, "f64_cmp")) { + LLVMTypeRef param_types[3]; + LLVMValueRef opcond = LLVMConstInt(I32_TYPE, cond, true); + param_types[0] = I32_TYPE; + param_types[1] = F64_TYPE; + param_types[2] = F64_TYPE; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, "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, "f64_cmp"); + } + + if (!res) { aot_set_last_error("llvm build compare failed."); return false; } @@ -194,3 +231,26 @@ aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } +bool +aot_compile_op_ref_eq(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef gc_obj1 = NULL, gc_obj2 = NULL, res; + + POP_GC_REF(gc_obj1); + POP_GC_REF(gc_obj2); + + /* LLVM pointer values pointers are compared using LLVMBuildICmp */ + res = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, gc_obj1, gc_obj2, + "cmp_gc_obj_eq"); + + if (!res) { + aot_set_last_error("llvm build compare failed."); + return false; + } + + PUSH_COND(res); + + return true; +fail: + return false; +} diff --git a/core/iwasm/compilation/aot_emit_compare.h b/core/iwasm/compilation/aot_emit_compare.h index 6b06f5f094..f0bfa8ad48 100644 --- a/core/iwasm/compilation/aot_emit_compare.h +++ b/core/iwasm/compilation/aot_emit_compare.h @@ -28,10 +28,15 @@ bool aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, FloatCond cond); +#if WASM_ENABLE_GC != 0 + +bool +aot_compile_op_ref_eq(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +#endif #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_EMIT_COMPARE_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_const.c b/core/iwasm/compilation/aot_emit_const.c index 5e9085b898..64fa3ded1a 100644 --- a/core/iwasm/compilation/aot_emit_const.c +++ b/core/iwasm/compilation/aot_emit_const.c @@ -4,112 +4,167 @@ */ #include "aot_emit_const.h" - +#include "../aot/aot_intrinsic.h" bool aot_compile_op_i32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, int32 i32_const) { - LLVMValueRef value = I32_CONST((uint32)i32_const); - CHECK_LLVM_CONST(value); - PUSH_I32(value); - return true; + LLVMValueRef value; + + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "i32.const")) { + WASMValue wasm_value; + wasm_value.i32 = i32_const; + value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol, + &wasm_value, VALUE_TYPE_I32); + if (!value) { + return false; + } + } + else { + value = I32_CONST((uint32)i32_const); + CHECK_LLVM_CONST(value); + } + + PUSH_I32(value); + SET_CONST((uint64)(uint32)i32_const); + return true; fail: - return false; + return false; } bool aot_compile_op_i64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, int64 i64_const) { - LLVMValueRef value = I64_CONST((uint64)i64_const); - CHECK_LLVM_CONST(value); - PUSH_I64(value); - return true; + LLVMValueRef value; + + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "i64.const")) { + WASMValue wasm_value; + wasm_value.i64 = i64_const; + value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol, + &wasm_value, VALUE_TYPE_I64); + if (!value) { + return false; + } + } + else { + value = I64_CONST((uint64)i64_const); + CHECK_LLVM_CONST(value); + } + + PUSH_I64(value); + SET_CONST((uint64)i64_const); + return true; fail: - return false; + return false; } bool aot_compile_op_f32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, float32 f32_const) { - LLVMValueRef alloca, value; + LLVMValueRef alloca, value; - if (!isnan(f32_const)) { - value = F32_CONST(f32_const); - CHECK_LLVM_CONST(value); - PUSH_F32(value); - } - else { - int32 i32_const; - memcpy(&i32_const, &f32_const, sizeof(int32)); - if (!(alloca = LLVMBuildAlloca(comp_ctx->builder, - INT32_PTR_TYPE, "i32_ptr"))) { - aot_set_last_error("llvm build alloca failed."); - return false; - } - if (!LLVMBuildStore(comp_ctx->builder, - I32_CONST((uint32)i32_const), alloca)) { - aot_set_last_error("llvm build store failed."); - return false; - } - if (!(alloca = LLVMBuildBitCast(comp_ctx->builder, - alloca, F32_PTR_TYPE, "f32_ptr"))) { - aot_set_last_error("llvm build bitcast failed."); - return false; - } - if (!(value = LLVMBuildLoad(comp_ctx->builder, alloca, ""))) { - aot_set_last_error("llvm build load failed."); - return false; - } - PUSH_F32(value); - } + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "f32.const")) { + WASMValue wasm_value; + memcpy(&wasm_value.f32, &f32_const, sizeof(float32)); + value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol, + &wasm_value, VALUE_TYPE_F32); + if (!value) { + return false; + } + PUSH_F32(value); + } + else if (!isnan(f32_const)) { + value = F32_CONST(f32_const); + CHECK_LLVM_CONST(value); + PUSH_F32(value); + } + else { + int32 i32_const; + memcpy(&i32_const, &f32_const, sizeof(int32)); + if (!(alloca = + LLVMBuildAlloca(comp_ctx->builder, I32_TYPE, "i32_ptr"))) { + aot_set_last_error("llvm build alloca failed."); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, I32_CONST((uint32)i32_const), + alloca)) { + aot_set_last_error("llvm build store failed."); + return false; + } + if (!(alloca = LLVMBuildBitCast(comp_ctx->builder, alloca, F32_PTR_TYPE, + "f32_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + return false; + } + if (!(value = + LLVMBuildLoad2(comp_ctx->builder, F32_TYPE, alloca, ""))) { + aot_set_last_error("llvm build load failed."); + return false; + } + PUSH_F32(value); + } - return true; + return true; fail: - return false; + return false; } bool aot_compile_op_f64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, float64 f64_const) { - LLVMValueRef alloca, value; + LLVMValueRef alloca, value; - if (!isnan(f64_const)) { - value = F64_CONST(f64_const); - CHECK_LLVM_CONST(value); - PUSH_F64(value); - } - else { - int64 i64_const; - memcpy(&i64_const, &f64_const, sizeof(int64)); - if (!(alloca = LLVMBuildAlloca(comp_ctx->builder, - I64_TYPE, "i64_ptr"))) { - aot_set_last_error("llvm build alloca failed."); - return false; - } - value = I64_CONST((uint64)i64_const); - CHECK_LLVM_CONST(value); - if (!LLVMBuildStore(comp_ctx->builder, value, alloca)) { - aot_set_last_error("llvm build store failed."); - return false; - } - if (!(alloca = LLVMBuildBitCast(comp_ctx->builder, - alloca, F64_PTR_TYPE, "f64_ptr"))) { - aot_set_last_error("llvm build bitcast failed."); - return false; - } - if (!(value = LLVMBuildLoad(comp_ctx->builder, alloca, ""))) { - aot_set_last_error("llvm build load failed."); - return false; - } - PUSH_F64(value); - } + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "f64.const")) { + WASMValue wasm_value; + memcpy(&wasm_value.f64, &f64_const, sizeof(float64)); + value = aot_load_const_from_table(comp_ctx, func_ctx->native_symbol, + &wasm_value, VALUE_TYPE_F64); + if (!value) { + return false; + } + PUSH_F64(value); + } + else if (!isnan(f64_const)) { + value = F64_CONST(f64_const); + CHECK_LLVM_CONST(value); + PUSH_F64(value); + } + else { + int64 i64_const; + memcpy(&i64_const, &f64_const, sizeof(int64)); + if (!(alloca = + LLVMBuildAlloca(comp_ctx->builder, I64_TYPE, "i64_ptr"))) { + aot_set_last_error("llvm build alloca failed."); + return false; + } + value = I64_CONST((uint64)i64_const); + CHECK_LLVM_CONST(value); + if (!LLVMBuildStore(comp_ctx->builder, value, alloca)) { + aot_set_last_error("llvm build store failed."); + return false; + } + if (!(alloca = LLVMBuildBitCast(comp_ctx->builder, alloca, F64_PTR_TYPE, + "f64_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + return false; + } + if (!(value = + LLVMBuildLoad2(comp_ctx->builder, F64_TYPE, alloca, ""))) { + aot_set_last_error("llvm build load failed."); + return false; + } + PUSH_F64(value); + } - return true; + return true; fail: - return false; + return false; } - diff --git a/core/iwasm/compilation/aot_emit_const.h b/core/iwasm/compilation/aot_emit_const.h index 1d20a7a140..0b56cb13b0 100644 --- a/core/iwasm/compilation/aot_emit_const.h +++ b/core/iwasm/compilation/aot_emit_const.h @@ -33,4 +33,3 @@ aot_compile_op_f64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, #endif #endif /* end of _AOT_EMIT_CONST_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_control.c b/core/iwasm/compilation/aot_emit_control.c index d010fc6f67..ce4c2e1bdb 100644 --- a/core/iwasm/compilation/aot_emit_control.c +++ b/core/iwasm/compilation/aot_emit_control.c @@ -4,42 +4,61 @@ */ #include "aot_emit_control.h" +#include "aot_compiler.h" #include "aot_emit_exception.h" +#include "aot_stack_frame_comp.h" +#if WASM_ENABLE_GC != 0 +#include "aot_emit_gc.h" +#endif #include "../aot/aot_runtime.h" #include "../interpreter/wasm_loader.h" -#include "bh_memory.h" +#include "../common/wasm_loader_common.h" + +#if WASM_ENABLE_DEBUG_AOT != 0 +#include "debug/dwarf_extractor.h" +#endif static char *block_name_prefix[] = { "block", "loop", "if" }; static char *block_name_suffix[] = { "begin", "else", "end" }; +/* clang-format off */ enum { LABEL_BEGIN = 0, LABEL_ELSE, LABEL_END }; +/* clang-format on */ static void -format_block_name(char *name, uint32 name_size, - uint32 block_index, uint32 block_type, - uint32 label_type) +format_block_name(char *name, uint32 name_size, uint32 block_index, + uint32 label_type, uint32 label_id) { - if (block_type != BLOCK_TYPE_FUNCTION) - snprintf(name, name_size, "%s%d%s%s", - block_name_prefix[block_type], block_index, - "_", block_name_suffix[label_type]); + if (label_type != LABEL_TYPE_FUNCTION) + snprintf(name, name_size, "%s%d%s%s", block_name_prefix[label_type], + block_index, "_", block_name_suffix[label_id]); else snprintf(name, name_size, "%s", "func_end"); } -#define CREATE_BLOCK(new_llvm_block, name) do { \ - if (!(new_llvm_block = \ - LLVMAppendBasicBlockInContext(comp_ctx->context, \ - func_ctx->func, \ - name))) { \ - aot_set_last_error("add LLVM basic block failed.");\ - goto fail; \ - } \ - } while (0) +#define CREATE_BLOCK(new_llvm_block, name) \ + do { \ + if (!(new_llvm_block = LLVMAppendBasicBlockInContext( \ + comp_ctx->context, func_ctx->func, name))) { \ + aot_set_last_error("add LLVM basic block failed."); \ + goto fail; \ + } \ + if (!strcmp(name, "func_end") && comp_ctx->aux_stack_frame_type \ + && comp_ctx->call_stack_features.frame_per_function) { \ + LLVMBasicBlockRef cur_block = \ + LLVMGetInsertBlock(comp_ctx->builder); \ + SET_BUILDER_POS(new_llvm_block); \ + if (!aot_free_frame_per_function_frame_for_aot_func(comp_ctx, \ + func_ctx)) { \ + goto fail; \ + } \ + SET_BUILDER_POS(cur_block); \ + } \ + } while (0) #define CURR_BLOCK() LLVMGetInsertBlock(comp_ctx->builder) @@ -52,46 +71,93 @@ format_block_name(char *name, uint32 name_size, #define MOVE_BLOCK_BEFORE(llvm_block, llvm_block_before) \ LLVMMoveBasicBlockBefore(llvm_block, llvm_block_before) -#define BUILD_BR(llvm_block) do { \ - if (!LLVMBuildBr(comp_ctx->builder, llvm_block)) { \ - aot_set_last_error("llvm build br failed."); \ - goto fail; \ - } \ - } while (0) - -#define BUILD_COND_BR(value_if, block_then, block_else) do {\ - if (!LLVMBuildCondBr(comp_ctx->builder, value_if, \ - block_then, block_else)) { \ - aot_set_last_error("llvm build cond br failed."); \ - goto fail; \ - } \ - } while (0) +#define BUILD_BR(llvm_block) \ + do { \ + if (!LLVMBuildBr(comp_ctx->builder, llvm_block)) { \ + aot_set_last_error("llvm build br failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_COND_BR(value_if, block_then, block_else) \ + do { \ + if (!LLVMBuildCondBr(comp_ctx->builder, value_if, block_then, \ + block_else)) { \ + aot_set_last_error("llvm build cond br failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_COND_BR_V(value_if, block_then, block_else, instr) \ + do { \ + if (!(instr = LLVMBuildCondBr(comp_ctx->builder, value_if, block_then, \ + block_else))) { \ + aot_set_last_error("llvm build cond br failed."); \ + goto fail; \ + } \ + } while (0) #define SET_BUILDER_POS(llvm_block) \ LLVMPositionBuilderAtEnd(comp_ctx->builder, llvm_block) -#define CREATE_RETURN_VALUE_PHI(block) do { \ - if (block->return_type != VALUE_TYPE_VOID \ - && !block->return_value_phi) { \ - LLVMBasicBlockRef block_curr = CURR_BLOCK(); \ - SET_BUILDER_POS(block->llvm_end_block); \ - if (!(block->return_value_phi = \ - LLVMBuildPhi(comp_ctx->builder, \ - TO_LLVM_TYPE(block->return_type),\ - "phi"))) { \ - aot_set_last_error("llvm build phi failed."); \ - goto fail; \ - } \ - SET_BUILDER_POS(block_curr); \ - } \ - } while (0) - -#define ADD_TO_RETURN_PHI(block, value) do { \ - LLVMBasicBlockRef block_curr = CURR_BLOCK(); \ - LLVMAddIncoming(block->return_value_phi, \ - &value, &block_curr, 1); \ - } while (0) - +#define CREATE_RESULT_VALUE_PHIS(block) \ + do { \ + if (block->result_count && !block->result_phis) { \ + uint32 _i; \ + uint64 _size; \ + LLVMBasicBlockRef _block_curr = CURR_BLOCK(); \ + /* Allocate memory */ \ + _size = sizeof(LLVMValueRef) * (uint64)block->result_count; \ + if (_size >= UINT32_MAX \ + || !(block->result_phis = \ + wasm_runtime_malloc((uint32)_size))) { \ + aot_set_last_error("allocate memory failed."); \ + goto fail; \ + } \ + SET_BUILDER_POS(block->llvm_end_block); \ + LLVMValueRef first_instr = \ + get_first_non_phi(block->llvm_end_block); \ + if (first_instr) { \ + LLVMPositionBuilderBefore(comp_ctx->builder, first_instr); \ + } \ + for (_i = 0; _i < block->result_count; _i++) { \ + if (!(block->result_phis[_i] = LLVMBuildPhi( \ + comp_ctx->builder, \ + TO_LLVM_TYPE(block->result_types[_i]), "phi"))) { \ + aot_set_last_error("llvm build phi failed."); \ + goto fail; \ + } \ + } \ + SET_BUILDER_POS(_block_curr); \ + } \ + } while (0) + +#define ADD_TO_RESULT_PHIS(block, value, idx) \ + do { \ + LLVMBasicBlockRef _block_curr = CURR_BLOCK(); \ + LLVMTypeRef phi_ty = LLVMTypeOf(block->result_phis[idx]); \ + LLVMTypeRef value_ty = LLVMTypeOf(value); \ + bh_assert(LLVMGetTypeKind(phi_ty) == LLVMGetTypeKind(value_ty)); \ + bh_assert(LLVMGetTypeContext(phi_ty) == LLVMGetTypeContext(value_ty)); \ + LLVMAddIncoming(block->result_phis[idx], &value, &_block_curr, 1); \ + (void)phi_ty; \ + (void)value_ty; \ + } while (0) + +#define BUILD_ICMP(op, left, right, res, name) \ + do { \ + if (!(res = \ + LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \ + aot_set_last_error("llvm build icmp failed."); \ + goto fail; \ + } \ + } while (0) + +#define ADD_TO_PARAM_PHIS(block, value, idx) \ + do { \ + LLVMBasicBlockRef _block_curr = CURR_BLOCK(); \ + LLVMAddIncoming(block->param_phis[idx], &value, &_block_curr, 1); \ + } while (0) static LLVMBasicBlockRef find_next_llvm_end_block(AOTBlock *block) @@ -102,7 +168,7 @@ find_next_llvm_end_block(AOTBlock *block) return block ? block->llvm_end_block : NULL; } -static AOTBlock* +static AOTBlock * get_target_block(AOTFuncContext *func_ctx, uint32 br_depth) { uint32 i = br_depth; @@ -119,21 +185,158 @@ get_target_block(AOTFuncContext *func_ctx, uint32 br_depth) return block; } +LLVMValueRef +get_first_non_phi(LLVMBasicBlockRef block) +{ + LLVMValueRef instr = LLVMGetFirstInstruction(block); + + while (instr && LLVMIsAPHINode(instr)) { + instr = LLVMGetNextInstruction(instr); + } + + return instr; +} + +static void +clear_frame_locals(AOTCompFrame *aot_frame) +{ + uint32 i; + + for (i = 0; i < aot_frame->max_local_cell_num; i++) { + aot_frame->lp[i].dirty = 0; + aot_frame->lp[i].value = NULL; + if (aot_frame->comp_ctx->enable_gc) + /* Mark the ref flag as committed */ + aot_frame->lp[i].committed_ref = aot_frame->lp[i].ref + 1; + } +} + +static void +restore_frame_sp_for_op_else(AOTBlock *block, AOTCompFrame *aot_frame) +{ + uint32 all_cell_num = + aot_frame->max_local_cell_num + aot_frame->max_stack_cell_num; + AOTValueSlot *p_end = aot_frame->lp + all_cell_num, *p; + + /* Reset all the value slots from current frame sp for the else + branch since they be the same as starting to translate the + if branch */ + for (p = block->frame_sp_begin; p < p_end; p++) { + p->dirty = 0; + p->value = NULL; + p->type = 0; + if (aot_frame->comp_ctx->enable_gc) { + p->ref = 0; + p->committed_ref = 1; + } + } + + bh_assert(aot_frame->sp >= block->frame_sp_begin); + aot_frame->sp = block->frame_sp_begin; +} + +static void +restore_frame_sp_for_op_end(AOTBlock *block, AOTCompFrame *aot_frame) +{ + uint32 all_cell_num = + aot_frame->max_local_cell_num + aot_frame->max_stack_cell_num; + AOTValueSlot *p_end = aot_frame->lp + all_cell_num, *p; + + bh_assert(block->frame_sp_max_reached >= block->frame_sp_begin); + + /* Reset all the value slots from current frame sp to be same as + starting to translate this block, except for the frame ref + flags: set the flags to uncommitted before the max frame sp + ever reached, set the flags to committed non-ref after that */ + for (p = block->frame_sp_begin; p < p_end; p++) { + p->dirty = 0; + p->value = NULL; + p->type = 0; + if (aot_frame->comp_ctx->enable_gc) { + p->ref = 0; + if (p < block->frame_sp_max_reached) + p->committed_ref = 0; + else + p->committed_ref = 1; + } + } + + bh_assert(aot_frame->sp >= block->frame_sp_begin); + aot_frame->sp = block->frame_sp_begin; +} + +#if WASM_ENABLE_BRANCH_HINTS != 0 +static void +aot_emit_branch_hint(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 offset, LLVMValueRef br_if_instr) +{ + struct WASMCompilationHint *hint = func_ctx->function_hints; + while (hint != NULL) { + if (hint->type == WASM_COMPILATION_BRANCH_HINT + && ((struct WASMCompilationHintBranchHint *)hint)->offset + == offset) { + break; + } + hint = hint->next; + } + if (hint != NULL) { + // same weight llvm MDBuilder::createLikelyBranchWeights assigns + const uint32_t likely_weight = (1U << 20) - 1; + const uint32_t unlikely_weight = 1; + aot_set_cond_br_weights( + comp_ctx, br_if_instr, + ((struct WASMCompilationHintBranchHint *)hint)->is_likely + ? likely_weight + : unlikely_weight, + ((struct WASMCompilationHintBranchHint *)hint)->is_likely + ? unlikely_weight + : likely_weight); + } +} +#endif + static bool -handle_next_reachable_block(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, +handle_next_reachable_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint8 **p_frame_ip) { AOTBlock *block = func_ctx->block_stack.block_list_end; AOTBlock *block_prev; - uint8 *frame_ip; + AOTCompFrame *aot_frame = comp_ctx->aot_frame; + uint8 *frame_ip = NULL; + uint32 i; + AOTFuncType *func_type; + LLVMValueRef ret; +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMMetadataRef return_location; +#endif + + aot_checked_addr_list_destroy(func_ctx); + bh_assert(block); + +#if WASM_ENABLE_DEBUG_AOT != 0 + return_location = dwarf_gen_location( + comp_ctx, func_ctx, + (*p_frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code); +#endif + + if (aot_frame) { + /* Clear frame local variables since they have been committed */ + clear_frame_locals(aot_frame); + } - if (block->block_type == BLOCK_TYPE_IF - && block->llvm_else_block - && !block->skip_wasm_code_else + if (block->label_type == LABEL_TYPE_IF && block->llvm_else_block && *p_frame_ip <= block->wasm_code_else) { /* Clear value stack and start to translate else branch */ - aot_value_stack_destroy(&block->value_stack); + aot_value_stack_destroy(comp_ctx, &block->value_stack); + + if (aot_frame) { + /* Restore the frame sp */ + restore_frame_sp_for_op_else(block, aot_frame); + } + + /* Recover parameters of else branch */ + for (i = 0; i < block->param_count; i++) + PUSH(block->else_param_phis[i], block->param_types[i]); SET_BUILDER_POS(block->llvm_else_block); *p_frame_ip = block->wasm_code_else + 1; return true; @@ -143,14 +346,35 @@ handle_next_reachable_block(AOTCompContext *comp_ctx, block_prev = block->prev; block = aot_block_stack_pop(&func_ctx->block_stack); - if (block->block_type == BLOCK_TYPE_IF - && block->llvm_end_block) { - LLVMDeleteBasicBlock(block->llvm_end_block); - block->llvm_end_block = NULL; + if (block->label_type == LABEL_TYPE_IF) { + if (block->llvm_else_block && !block->skip_wasm_code_else + && *p_frame_ip <= block->wasm_code_else) { + /* Clear value stack and start to translate else branch */ + aot_value_stack_destroy(comp_ctx, &block->value_stack); + + if (aot_frame) { + /* Restore the frame sp */ + restore_frame_sp_for_op_else(block, aot_frame); + } + + SET_BUILDER_POS(block->llvm_else_block); + *p_frame_ip = block->wasm_code_else + 1; + /* Push back the block */ + aot_block_stack_push(&func_ctx->block_stack, block); + /* Recover parameters of else branch */ + for (i = 0; i < block->param_count; i++) + PUSH(block->else_param_phis[i], block->param_types[i]); + return true; + } + else if (block->llvm_end_block) { + /* Remove unreachable basic block */ + LLVMDeleteBasicBlock(block->llvm_end_block); + block->llvm_end_block = NULL; + } } frame_ip = block->wasm_code_end; - aot_block_destroy(block); + aot_block_destroy(comp_ctx, block); block = block_prev; } @@ -159,32 +383,222 @@ handle_next_reachable_block(AOTCompContext *comp_ctx, return true; } + if (block->label_type == LABEL_TYPE_IF && block->llvm_else_block + && !block->skip_wasm_code_else + && *p_frame_ip <= block->wasm_code_else) { + /* Clear value stack and start to translate else branch */ + aot_value_stack_destroy(comp_ctx, &block->value_stack); + + if (aot_frame) { + /* Restore the frame sp */ + restore_frame_sp_for_op_else(block, aot_frame); + } + + /* Recover parameters of else branch */ + for (i = 0; i < block->param_count; i++) + PUSH(block->else_param_phis[i], block->param_types[i]); + SET_BUILDER_POS(block->llvm_else_block); + *p_frame_ip = block->wasm_code_else + 1; + return true; + } + *p_frame_ip = block->wasm_code_end + 1; SET_BUILDER_POS(block->llvm_end_block); /* Pop block, push its return value, and destroy the block */ block = aot_block_stack_pop(&func_ctx->block_stack); - if (block->return_type != VALUE_TYPE_VOID) { - bh_assert(block->return_value_phi); - if (block->block_type != BLOCK_TYPE_FUNCTION) - PUSH(block->return_value_phi, block->return_type); - else - LLVMBuildRet(comp_ctx->builder, block->return_value_phi); + + if (aot_frame) { + /* Restore the frame sp */ + restore_frame_sp_for_op_end(block, aot_frame); + } + + func_type = func_ctx->aot_func->func_type; + for (i = 0; i < block->result_count; i++) { + bh_assert(block->result_phis[i]); + if (block->label_type != LABEL_TYPE_FUNCTION) { + PUSH(block->result_phis[i], block->result_types[i]); + } + else { + /* Store extra return values to function parameters */ + if (i != 0) { + LLVMValueRef res; + uint32 param_index = func_type->param_count + i; + if (!(res = LLVMBuildStore( + comp_ctx->builder, block->result_phis[i], + LLVMGetParam(func_ctx->func, param_index)))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + LLVMSetAlignment(res, 1); + } + } } - else if (block->block_type == BLOCK_TYPE_FUNCTION) { - LLVMBuildRetVoid(comp_ctx->builder); + if (block->label_type == LABEL_TYPE_FUNCTION) { + if (block->result_count) { + /* Return the first return value */ + if (!(ret = + LLVMBuildRet(comp_ctx->builder, block->result_phis[0]))) { + aot_set_last_error("llvm build return failed."); + goto fail; + } +#if WASM_ENABLE_DEBUG_AOT != 0 + if (return_location != NULL) { + LLVMInstructionSetDebugLoc(ret, return_location); + } +#endif + } + else { + if (!(ret = LLVMBuildRetVoid(comp_ctx->builder))) { + aot_set_last_error("llvm build return void failed."); + goto fail; + } +#if WASM_ENABLE_DEBUG_AOT != 0 + if (return_location != NULL) { + LLVMInstructionSetDebugLoc(ret, return_location); + } +#endif + } } - aot_block_destroy(block); + aot_block_destroy(comp_ctx, block); return true; fail: return false; } +static bool +push_aot_block_to_stack_and_pass_params(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + AOTBlock *block) +{ + uint32 i, param_index; + LLVMValueRef value, br_inst; + uint64 size; + char name[32]; + LLVMBasicBlockRef block_curr = CURR_BLOCK(); + + if (block->param_count) { + size = sizeof(LLVMValueRef) * (uint64)block->param_count; + if (size >= UINT32_MAX + || !(block->param_phis = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return false; + } + + if (block->label_type == LABEL_TYPE_IF && !block->skip_wasm_code_else + && !(block->else_param_phis = wasm_runtime_malloc((uint32)size))) { + wasm_runtime_free(block->param_phis); + block->param_phis = NULL; + aot_set_last_error("allocate memory failed."); + return false; + } + + /* Create param phis */ + for (i = 0; i < block->param_count; i++) { + if (block->llvm_entry_block) { + SET_BUILDER_POS(block->llvm_entry_block); + snprintf(name, sizeof(name), "%s%d_phi%d", + block_name_prefix[block->label_type], + block->block_index, i); + if (!(block->param_phis[i] = LLVMBuildPhi( + comp_ctx->builder, + TO_LLVM_TYPE(block->param_types[i]), name))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + } + + if (block->label_type == LABEL_TYPE_IF + && !block->skip_wasm_code_else && block->llvm_else_block) { + /* Build else param phis */ + SET_BUILDER_POS(block->llvm_else_block); + snprintf(name, sizeof(name), "else%d_phi%d", block->block_index, + i); + if (!(block->else_param_phis[i] = LLVMBuildPhi( + comp_ctx->builder, + TO_LLVM_TYPE(block->param_types[i]), name))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + } + } + + /* At this point, the branch instruction was already built to jump to + * the new BB, to avoid generating zext instruction from the popped + * operand that would come after branch instruction, we should position + * the builder before the last branch instruction */ + br_inst = LLVMGetLastInstruction(block_curr); + bh_assert(LLVMGetInstructionOpcode(br_inst) == LLVMBr); + LLVMPositionBuilderBefore(comp_ctx->builder, br_inst); + + /* Pop param values from current block's + * value stack and add to param phis. + */ + for (i = 0; i < block->param_count; i++) { + param_index = block->param_count - 1 - i; + POP(value, block->param_types[param_index]); + if (block->llvm_entry_block) + /* Only add incoming phis if the entry block was created */ + ADD_TO_PARAM_PHIS(block, value, param_index); + if (block->label_type == LABEL_TYPE_IF + && !block->skip_wasm_code_else) { + if (block->llvm_else_block) { + /* has else branch, add to else param phis */ + LLVMAddIncoming(block->else_param_phis[param_index], &value, + &block_curr, 1); + } + else { + /* no else branch, add to result phis */ + CREATE_RESULT_VALUE_PHIS(block); + ADD_TO_RESULT_PHIS(block, value, param_index); + } + } + } + } + + /* Push the new block to block stack */ + aot_block_stack_push(&func_ctx->block_stack, block); + if (comp_ctx->aot_frame) { + block->frame_sp_begin = block->frame_sp_max_reached = + comp_ctx->aot_frame->sp; + } + + /* Push param phis to the new block */ + for (i = 0; i < block->param_count; i++) { + if (block->llvm_entry_block) + /* Push param phis if the entry basic block was created */ + PUSH(block->param_phis[i], block->param_types[i]); + else { + bh_assert(block->label_type == LABEL_TYPE_IF + && block->llvm_else_block && block->else_param_phis + && !block->skip_wasm_code_else); + /* Push else param phis if we start to translate the + else branch */ + PUSH(block->else_param_phis[i], block->param_types[i]); + } + } + + return true; + +fail: + if (block->param_phis) { + wasm_runtime_free(block->param_phis); + block->param_phis = NULL; + } + if (block->else_param_phis) { + wasm_runtime_free(block->else_param_phis); + block->else_param_phis = NULL; + } + return false; +} + bool aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint8 **p_frame_ip, uint8 *frame_ip_end, - uint32 block_type, uint32 block_ret_type) + uint8 **p_frame_ip, uint8 *frame_ip_end, uint32 label_type, + uint32 param_count, uint8 *param_types, + uint32 result_count, uint8 *result_types) { + BlockAddr block_addr_cache[BLOCK_ADDR_CACHE_SIZE][BLOCK_ADDR_CONFLICT_SIZE]; AOTBlock *block; uint8 *else_addr, *end_addr; LLVMValueRef value; @@ -196,117 +610,183 @@ aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } + memset(block_addr_cache, 0, sizeof(block_addr_cache)); + /* Get block info */ - if (!(wasm_loader_find_block_addr(comp_ctx->comp_data->wasm_module, - *p_frame_ip, frame_ip_end, (uint8)block_type, - &else_addr, &end_addr, NULL, 0))) { + if (!(wasm_loader_find_block_addr( + NULL, (BlockAddr *)block_addr_cache, *p_frame_ip, frame_ip_end, + (uint8)label_type, &else_addr, &end_addr))) { aot_set_last_error("find block end addr failed."); return false; } /* Allocate memory */ - if (!(block = wasm_malloc(sizeof(AOTBlock)))) { + if (!(block = wasm_runtime_malloc(sizeof(AOTBlock)))) { aot_set_last_error("allocate memory failed."); return false; } + memset(block, 0, sizeof(AOTBlock)); + if (param_count + && !(block->param_types = wasm_runtime_malloc(param_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + if (result_count) { + if (!(block->result_types = wasm_runtime_malloc(result_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + } /* Init aot block data */ - memset(block, 0, sizeof(AOTBlock)); - block->block_type = block_type; - block->return_type = (uint8)block_ret_type; + block->label_type = label_type; + block->param_count = param_count; + if (param_count) { + bh_memcpy_s(block->param_types, param_count, param_types, param_count); + } + block->result_count = result_count; + if (result_count) { + bh_memcpy_s(block->result_types, result_count, result_types, + result_count); + } block->wasm_code_else = else_addr; block->wasm_code_end = end_addr; - block->block_index = func_ctx->block_stack.block_index[block_type]; - func_ctx->block_stack.block_index[block_type]++; + block->block_index = func_ctx->block_stack.block_index[label_type]; + func_ctx->block_stack.block_index[label_type]++; - if (block_type == BLOCK_TYPE_BLOCK - || block_type == BLOCK_TYPE_LOOP) { + if (comp_ctx->aot_frame) { + if (label_type != LABEL_TYPE_BLOCK && comp_ctx->enable_gc + && !aot_gen_commit_values(comp_ctx->aot_frame)) { + goto fail; + } + } + + if (label_type == LABEL_TYPE_BLOCK || label_type == LABEL_TYPE_LOOP) { /* Create block */ - format_block_name(name, sizeof(name), - block->block_index, block_type, LABEL_BEGIN); + format_block_name(name, sizeof(name), block->block_index, label_type, + LABEL_BEGIN); CREATE_BLOCK(block->llvm_entry_block, name); MOVE_BLOCK_AFTER_CURR(block->llvm_entry_block); /* Jump to the entry block */ BUILD_BR(block->llvm_entry_block); + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, block)) + goto fail; /* Start to translate the block */ SET_BUILDER_POS(block->llvm_entry_block); - aot_block_stack_push(&func_ctx->block_stack, block); + if (label_type == LABEL_TYPE_LOOP) + aot_checked_addr_list_destroy(func_ctx); } - else if (block_type == BLOCK_TYPE_IF) { + else if (label_type == LABEL_TYPE_IF) { POP_COND(value); - if (!LLVMIsConstant(value)) { + + if (LLVMIsUndef(value) +#if LLVM_VERSION_NUMBER >= 12 + || LLVMIsPoison(value) +#endif + ) { + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, + false, NULL, NULL))) { + goto fail; + } + aot_block_destroy(comp_ctx, block); + return aot_handle_next_reachable_block(comp_ctx, func_ctx, + p_frame_ip); + } + + if (!LLVMIsEfficientConstInt(value)) { /* Compare value is not constant, create condition br IR */ /* Create entry block */ - format_block_name(name, sizeof(name), - block->block_index, block_type, LABEL_BEGIN); + format_block_name(name, sizeof(name), block->block_index, + label_type, LABEL_BEGIN); CREATE_BLOCK(block->llvm_entry_block, name); MOVE_BLOCK_AFTER_CURR(block->llvm_entry_block); /* Create end block */ - format_block_name(name, sizeof(name), - block->block_index, block_type, LABEL_END); + format_block_name(name, sizeof(name), block->block_index, + label_type, LABEL_END); CREATE_BLOCK(block->llvm_end_block, name); MOVE_BLOCK_AFTER(block->llvm_end_block, block->llvm_entry_block); if (else_addr) { /* Create else block */ - format_block_name(name, sizeof(name), - block->block_index, block_type, LABEL_ELSE); + format_block_name(name, sizeof(name), block->block_index, + label_type, LABEL_ELSE); CREATE_BLOCK(block->llvm_else_block, name); - MOVE_BLOCK_AFTER(block->llvm_else_block, block->llvm_entry_block); + MOVE_BLOCK_AFTER(block->llvm_else_block, + block->llvm_entry_block); /* Create condition br IR */ +#if WASM_ENABLE_BRANCH_HINTS != 0 + LLVMValueRef br_if_val = NULL; + BUILD_COND_BR_V(value, block->llvm_entry_block, + block->llvm_else_block, br_if_val); + const uint32 off = + *p_frame_ip - func_ctx->aot_func->code_body_begin; + aot_emit_branch_hint(comp_ctx, func_ctx, off, br_if_val); +#else BUILD_COND_BR(value, block->llvm_entry_block, block->llvm_else_block); +#endif } else { /* Create condition br IR */ +#if WASM_ENABLE_BRANCH_HINTS != 0 + LLVMValueRef br_if_val = NULL; + BUILD_COND_BR_V(value, block->llvm_entry_block, + block->llvm_end_block, br_if_val); + const uint32 off = + *p_frame_ip - func_ctx->aot_func->code_body_begin; + aot_emit_branch_hint(comp_ctx, func_ctx, off, br_if_val); +#else BUILD_COND_BR(value, block->llvm_entry_block, block->llvm_end_block); +#endif block->is_reachable = true; } + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, + block)) + goto fail; /* Start to translate if branch of BLOCK if */ SET_BUILDER_POS(block->llvm_entry_block); - aot_block_stack_push(&func_ctx->block_stack, block); } else { if ((int32)LLVMConstIntGetZExtValue(value) != 0) { - /* Compare value is not 0, condtion is true, else branch of + /* Compare value is not 0, condition is true, else branch of BLOCK if cannot be reached */ block->skip_wasm_code_else = true; /* Create entry block */ - format_block_name(name, sizeof(name), - block->block_index, block_type, LABEL_BEGIN); + format_block_name(name, sizeof(name), block->block_index, + label_type, LABEL_BEGIN); CREATE_BLOCK(block->llvm_entry_block, name); MOVE_BLOCK_AFTER_CURR(block->llvm_entry_block); /* Jump to the entry block */ BUILD_BR(block->llvm_entry_block); + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, + block)) + goto fail; /* Start to translate the if branch */ SET_BUILDER_POS(block->llvm_entry_block); - aot_block_stack_push(&func_ctx->block_stack, block); } else { - /* Compare value is not 0, condtion is false, if branch of + /* Compare value is not 0, condition is false, if branch of BLOCK if cannot be reached */ if (else_addr) { /* Create else block */ - format_block_name(name, sizeof(name), - block->block_index, block_type, LABEL_ELSE); + format_block_name(name, sizeof(name), block->block_index, + label_type, LABEL_ELSE); CREATE_BLOCK(block->llvm_else_block, name); MOVE_BLOCK_AFTER_CURR(block->llvm_else_block); /* Jump to the else block */ BUILD_BR(block->llvm_else_block); + if (!push_aot_block_to_stack_and_pass_params( + comp_ctx, func_ctx, block)) + goto fail; /* Start to translate the else branch */ SET_BUILDER_POS(block->llvm_else_block); *p_frame_ip = else_addr + 1; - aot_block_stack_push(&func_ctx->block_stack, block); } else { - if (block->return_type != VALUE_TYPE_VOID) { - aot_set_last_error("WASM value stack underflow."); - goto fail; - } /* skip the block */ - wasm_free(block); + aot_block_destroy(comp_ctx, block); *p_frame_ip = end_addr + 1; } } @@ -319,7 +799,7 @@ aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return true; fail: - wasm_free(block); + aot_block_destroy(comp_ctx, block); return false; } @@ -329,24 +809,25 @@ aot_compile_op_else(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { AOTBlock *block = func_ctx->block_stack.block_list_end; LLVMValueRef value; + AOTCompFrame *aot_frame = comp_ctx->aot_frame; char name[32]; + uint32 i, result_index; /* Check block */ if (!block) { aot_set_last_error("WASM block stack underflow."); return false; } - if (block->block_type != BLOCK_TYPE_IF - || (!block->skip_wasm_code_else - && !block->llvm_else_block)) { + if (block->label_type != LABEL_TYPE_IF + || (!block->skip_wasm_code_else && !block->llvm_else_block)) { aot_set_last_error("Invalid WASM block type."); return false; } /* Create end block if needed */ if (!block->llvm_end_block) { - format_block_name(name, sizeof(name), - block->block_index, block->block_type, LABEL_END); + format_block_name(name, sizeof(name), block->block_index, + block->label_type, LABEL_END); CREATE_BLOCK(block->llvm_end_block, name); if (block->llvm_else_block) MOVE_BLOCK_AFTER(block->llvm_end_block, block->llvm_else_block); @@ -357,20 +838,37 @@ aot_compile_op_else(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, block->is_reachable = true; /* Comes from the if branch of BLOCK if */ - if (block->return_type != VALUE_TYPE_VOID) { - POP(value, block->return_type); - CREATE_RETURN_VALUE_PHI(block); - ADD_TO_RETURN_PHI(block, value); + CREATE_RESULT_VALUE_PHIS(block); + for (i = 0; i < block->result_count; i++) { + result_index = block->result_count - 1 - i; + POP(value, block->result_types[result_index]); + ADD_TO_RESULT_PHIS(block, value, result_index); + } + + if (aot_frame) { + bh_assert(block->frame_sp_begin == aot_frame->sp); + if (comp_ctx->enable_gc && !aot_gen_commit_values(aot_frame)) { + goto fail; + } } /* Jump to end block */ BUILD_BR(block->llvm_end_block); - if (!block->skip_wasm_code_else - && block->llvm_else_block) { - /* Clear value stack and start to translate else branch */ - aot_value_stack_destroy(&block->value_stack); + if (!block->skip_wasm_code_else && block->llvm_else_block) { + /* Clear value stack, recover param values + and start to translate else branch. */ + aot_value_stack_destroy(comp_ctx, &block->value_stack); + + if (comp_ctx->aot_frame) { + clear_frame_locals(aot_frame); + restore_frame_sp_for_op_else(block, aot_frame); + } + + for (i = 0; i < block->param_count; i++) + PUSH(block->else_param_phis[i], block->param_types[i]); SET_BUILDER_POS(block->llvm_else_block); + aot_checked_addr_list_destroy(func_ctx); return true; } @@ -389,6 +887,7 @@ aot_compile_op_end(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMValueRef value; LLVMBasicBlockRef next_llvm_end_block; char name[32]; + uint32 i, result_index; /* Check block stack */ if (!(block = func_ctx->block_stack.block_list_end)) { @@ -398,18 +897,32 @@ aot_compile_op_end(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Create the end block */ if (!block->llvm_end_block) { - format_block_name(name, sizeof(name), - block->block_index, block->block_type, LABEL_END); + format_block_name(name, sizeof(name), block->block_index, + block->label_type, LABEL_END); CREATE_BLOCK(block->llvm_end_block, name); if ((next_llvm_end_block = find_next_llvm_end_block(block))) MOVE_BLOCK_BEFORE(block->llvm_end_block, next_llvm_end_block); } - /* Handle block return value */ - if (block->return_type != VALUE_TYPE_VOID) { - POP(value, block->return_type); - CREATE_RETURN_VALUE_PHI(block); - ADD_TO_RETURN_PHI(block, value); + if (comp_ctx->aot_frame) { + if (block->label_type != LABEL_TYPE_FUNCTION && comp_ctx->enable_gc + && !aot_gen_commit_values(comp_ctx->aot_frame)) { + return false; + } + } + + /* Handle block result values */ + CREATE_RESULT_VALUE_PHIS(block); + for (i = 0; i < block->result_count; i++) { + value = NULL; + result_index = block->result_count - 1 - i; + POP(value, block->result_types[result_index]); + bh_assert(value); + ADD_TO_RESULT_PHIS(block, value, result_index); + } + + if (comp_ctx->aot_frame) { + bh_assert(comp_ctx->aot_frame->sp == block->frame_sp_begin); } /* Jump to the end block */ @@ -421,30 +934,132 @@ aot_compile_op_end(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } +bool +check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool check_terminate_and_suspend) +{ + LLVMValueRef terminate_addr, terminate_flags, flag, offset, res; + LLVMBasicBlockRef terminate_block, non_terminate_block; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + bool is_shared_memory = + comp_ctx->comp_data->memories[0].flags & 0x02 ? true : false; + + /* Only need to check the suspend flags when memory is shared since + shared memory must be enabled for multi-threading */ + if (!is_shared_memory) { + return true; + } + + /* Offset of suspend_flags */ + offset = I32_FIVE; + + if (!(terminate_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1, + "terminate_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(terminate_addr = + LLVMBuildBitCast(comp_ctx->builder, terminate_addr, + INT32_PTR_TYPE, "terminate_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!(terminate_flags = + LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, terminate_addr, + "terminate_flags"))) { + aot_set_last_error("llvm build LOAD failed"); + return false; + } + /* Set terminate_flags memory access to volatile, so that the value + will always be loaded from memory rather than register */ + LLVMSetVolatile(terminate_flags, true); + + if (!(flag = LLVMBuildAnd(comp_ctx->builder, terminate_flags, I32_ONE, + "termination_flag"))) { + aot_set_last_error("llvm build AND failed"); + return false; + } + + CREATE_BLOCK(non_terminate_block, "non_terminate"); + MOVE_BLOCK_AFTER_CURR(non_terminate_block); + + CREATE_BLOCK(terminate_block, "terminate"); + MOVE_BLOCK_AFTER_CURR(terminate_block); + + BUILD_ICMP(LLVMIntEQ, flag, I32_ZERO, res, "flag_terminate"); + BUILD_COND_BR(res, non_terminate_block, terminate_block); + + /* Move builder to terminate block */ + SET_BUILDER_POS(terminate_block); + if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { + goto fail; + } + + /* Move builder to non terminate block */ + SET_BUILDER_POS(non_terminate_block); + return true; + +fail: + return false; +} + bool aot_compile_op_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 br_depth, uint8 **p_frame_ip) { AOTBlock *block_dst; - LLVMValueRef value_ret; + LLVMValueRef value_ret, value_param; LLVMBasicBlockRef next_llvm_end_block; char name[32]; + uint32 i, param_index, result_index; if (!(block_dst = get_target_block(func_ctx, br_depth))) { return false; } - if (block_dst->block_type == BLOCK_TYPE_LOOP) { + if (comp_ctx->aot_frame) { + if (comp_ctx->enable_gc && !aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + if (comp_ctx->enable_thread_mgr) { + /* Commit sp when GC is enabled, don't commit ip */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, + comp_ctx->enable_gc, false)) + return false; + } + } + else { + if (comp_ctx->aot_frame->sp > block_dst->frame_sp_max_reached) + block_dst->frame_sp_max_reached = comp_ctx->aot_frame->sp; + } + } + + /* Terminate or suspend current thread only when this is a backward jump */ + if (comp_ctx->enable_thread_mgr + && block_dst->label_type == LABEL_TYPE_LOOP) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) + return false; + } + + if (block_dst->label_type == LABEL_TYPE_LOOP) { /* Dest block is Loop block */ + /* Handle Loop parameters */ + for (i = 0; i < block_dst->param_count; i++) { + param_index = block_dst->param_count - 1 - i; + POP(value_param, block_dst->param_types[param_index]); + ADD_TO_PARAM_PHIS(block_dst, value_param, param_index); + } BUILD_BR(block_dst->llvm_entry_block); } else { /* Dest block is Block/If/Function block */ /* Create the end block */ if (!block_dst->llvm_end_block) { - format_block_name(name, sizeof(name), - block_dst->block_index, block_dst->block_type, - LABEL_END); + format_block_name(name, sizeof(name), block_dst->block_index, + block_dst->label_type, LABEL_END); CREATE_BLOCK(block_dst->llvm_end_block, name); if ((next_llvm_end_block = find_next_llvm_end_block(block_dst))) MOVE_BLOCK_BEFORE(block_dst->llvm_end_block, @@ -453,13 +1068,13 @@ aot_compile_op_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, block_dst->is_reachable = true; - /* Handle return value */ - if (block_dst->return_type != VALUE_TYPE_VOID) { - POP(value_ret, block_dst->return_type); - CREATE_RETURN_VALUE_PHI(block_dst); - ADD_TO_RETURN_PHI(block_dst, value_ret); + /* Handle result values */ + CREATE_RESULT_VALUE_PHIS(block_dst); + for (i = 0; i < block_dst->result_count; i++) { + result_index = block_dst->result_count - 1 - i; + POP(value_ret, block_dst->result_types[result_index]); + ADD_TO_RESULT_PHIS(block_dst, value_ret, result_index); } - /* Jump to the end block */ BUILD_BR(block_dst->llvm_end_block); } @@ -469,30 +1084,109 @@ aot_compile_op_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } -bool -aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 br_depth, uint8 **p_frame_ip) +static bool +aot_compile_conditional_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef value_cmp, uint8 **p_frame_ip) { AOTBlock *block_dst; - LLVMValueRef value_cmp, value_ret; + LLVMValueRef value, *values = NULL; LLVMBasicBlockRef llvm_else_block, next_llvm_end_block; char name[32]; + uint32 i, param_index, result_index; + uint64 size; + + // ip is advanced by one byte for the opcode +#if WASM_ENABLE_BRANCH_HINTS != 0 + uint32 instr_offset = + (*p_frame_ip - 0x1) - (func_ctx->aot_func->code_body_begin); +#else + uint32 instr_offset = 0; +#endif + uint64 br_depth; + if (!read_leb(p_frame_ip, *p_frame_ip + 5, 32, false, &br_depth, NULL, 0)) + return false; - POP_COND(value_cmp); - if (!LLVMIsConstant(value_cmp)) { - /* Compare value is not constant, create condition br IR */ - if (!(block_dst = get_target_block(func_ctx, br_depth))) { + if (!(block_dst = get_target_block(func_ctx, br_depth))) { + return false; + } + + if (comp_ctx->aot_frame) { + if (comp_ctx->enable_gc && !aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + if (comp_ctx->enable_thread_mgr) { + /* Commit sp when GC is enabled, don't commit ip */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, + comp_ctx->enable_gc, false)) + return false; + } + } + else { + if (comp_ctx->aot_frame->sp > block_dst->frame_sp_max_reached) + block_dst->frame_sp_max_reached = comp_ctx->aot_frame->sp; + } + } + + /* Terminate or suspend current thread only when this is + a backward jump */ + if (comp_ctx->enable_thread_mgr + && block_dst->label_type == LABEL_TYPE_LOOP) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) return false; + } + + if (LLVMIsUndef(value_cmp) +#if LLVM_VERSION_NUMBER >= 12 + || LLVMIsPoison(value_cmp) +#endif + ) { + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, + false, NULL, NULL))) { + goto fail; } + return aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); + } + + if (!LLVMIsEfficientConstInt(value_cmp)) { + /* Compare value is not constant, create condition br IR */ /* Create llvm else block */ CREATE_BLOCK(llvm_else_block, "br_if_else"); MOVE_BLOCK_AFTER_CURR(llvm_else_block); - if (block_dst->block_type == BLOCK_TYPE_LOOP) { + if (block_dst->label_type == LABEL_TYPE_LOOP) { /* Dest block is Loop block */ + /* Handle Loop parameters */ + if (block_dst->param_count) { + size = sizeof(LLVMValueRef) * (uint64)block_dst->param_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + for (i = 0; i < block_dst->param_count; i++) { + param_index = block_dst->param_count - 1 - i; + POP(value, block_dst->param_types[param_index]); + ADD_TO_PARAM_PHIS(block_dst, value, param_index); + values[param_index] = value; + } + for (i = 0; i < block_dst->param_count; i++) { + PUSH(values[i], block_dst->param_types[i]); + } + wasm_runtime_free(values); + values = NULL; + } + +#if WASM_ENABLE_BRANCH_HINTS != 0 + LLVMValueRef br_if_val = NULL; + BUILD_COND_BR_V(value_cmp, block_dst->llvm_entry_block, + llvm_else_block, br_if_val); + aot_emit_branch_hint(comp_ctx, func_ctx, instr_offset, br_if_val); +#else BUILD_COND_BR(value_cmp, block_dst->llvm_entry_block, llvm_else_block); +#endif /* Move builder to else block */ SET_BUILDER_POS(llvm_else_block); @@ -501,96 +1195,242 @@ aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Dest block is Block/If/Function block */ /* Create the end block */ if (!block_dst->llvm_end_block) { - format_block_name(name, sizeof(name), - block_dst->block_index, block_dst->block_type, - LABEL_END); + format_block_name(name, sizeof(name), block_dst->block_index, + block_dst->label_type, LABEL_END); CREATE_BLOCK(block_dst->llvm_end_block, name); if ((next_llvm_end_block = find_next_llvm_end_block(block_dst))) MOVE_BLOCK_BEFORE(block_dst->llvm_end_block, next_llvm_end_block); } - /* Set reachable flag and create condtion br IR */ + /* Set reachable flag and create condition br IR */ block_dst->is_reachable = true; - /* Handle return value */ - if (block_dst->return_type != VALUE_TYPE_VOID) { - POP(value_ret, block_dst->return_type); - CREATE_RETURN_VALUE_PHI(block_dst); - ADD_TO_RETURN_PHI(block_dst, value_ret); - PUSH(value_ret, block_dst->return_type); + /* Handle result values */ + if (block_dst->result_count) { + size = sizeof(LLVMValueRef) * (uint64)block_dst->result_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + CREATE_RESULT_VALUE_PHIS(block_dst); + for (i = 0; i < block_dst->result_count; i++) { + result_index = block_dst->result_count - 1 - i; + POP(value, block_dst->result_types[result_index]); + values[result_index] = value; + ADD_TO_RESULT_PHIS(block_dst, value, result_index); + } + for (i = 0; i < block_dst->result_count; i++) { + PUSH(values[i], block_dst->result_types[i]); + } + wasm_runtime_free(values); + values = NULL; } /* Condition jump to end block */ +#if WASM_ENABLE_BRANCH_HINTS != 0 + LLVMValueRef br_if_val = NULL; + BUILD_COND_BR_V(value_cmp, block_dst->llvm_end_block, + llvm_else_block, br_if_val); + aot_emit_branch_hint(comp_ctx, func_ctx, instr_offset, br_if_val); +#else BUILD_COND_BR(value_cmp, block_dst->llvm_end_block, llvm_else_block); - +#endif /* Move builder to else block */ SET_BUILDER_POS(llvm_else_block); } } else { if ((int32)LLVMConstIntGetZExtValue(value_cmp) != 0) { - /* Compare value is not 0, condtion is true, same as op_br */ + /* Compare value is not 0, condition is true, same as op_br */ return aot_compile_op_br(comp_ctx, func_ctx, br_depth, p_frame_ip); } else { - /* Compare value is not 0, condtion is false, skip br_if */ + /* Compare value is not 0, condition is false, skip br_if */ return true; } } return true; +fail: + if (values) + wasm_runtime_free(values); + return false; +} + +bool +aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + LLVMValueRef value_cmp; + + POP_COND(value_cmp); + + return aot_compile_conditional_br(comp_ctx, func_ctx, value_cmp, + p_frame_ip); fail: return false; } bool aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 *br_depths, uint32 br_count, - uint8 **p_frame_ip) + uint32 *br_depths, uint32 br_count, uint8 **p_frame_ip) { - uint32 i; - LLVMValueRef value_switch, value_cmp, value_case, value_ret = NULL; + uint32 i, j; + LLVMValueRef value_switch, value_cmp, value_case, value, *values = NULL; LLVMBasicBlockRef default_llvm_block = NULL, target_llvm_block; LLVMBasicBlockRef next_llvm_end_block; AOTBlock *target_block; uint32 br_depth, depth_idx; + uint32 param_index, result_index; + uint64 size; char name[32]; POP_I32(value_cmp); - if (!LLVMIsConstant(value_cmp)) { + + if (LLVMIsUndef(value_cmp) +#if LLVM_VERSION_NUMBER >= 12 + || LLVMIsPoison(value_cmp) +#endif + ) { + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, + false, NULL, NULL))) { + goto fail; + } + return aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); + } + + /* + * if (value_cmp > br_count) + * value_cmp = br_count; + */ + LLVMValueRef br_count_value = I32_CONST(br_count); + CHECK_LLVM_CONST(br_count_value); + + LLVMValueRef clap_value_cmp_cond = + LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, value_cmp, br_count_value, + "cmp_w_br_count"); + if (!clap_value_cmp_cond) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + value_cmp = LLVMBuildSelect(comp_ctx->builder, clap_value_cmp_cond, + br_count_value, value_cmp, "clap_value_cmp"); + if (!value_cmp) { + aot_set_last_error("llvm build select failed."); + return false; + } + + if (!LLVMIsEfficientConstInt(value_cmp)) { + if (comp_ctx->aot_frame) { + if (comp_ctx->enable_gc + && !aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (comp_ctx->enable_thread_mgr) { + /* Commit sp when GC is enabled, don't commit ip */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, + comp_ctx->enable_gc, false)) + return false; + } + + for (i = 0; i <= br_count; i++) { + target_block = get_target_block(func_ctx, br_depths[i]); + if (!target_block) + return false; + if (target_block->label_type != LABEL_TYPE_LOOP) { + if (comp_ctx->aot_frame->sp + > target_block->frame_sp_max_reached) + target_block->frame_sp_max_reached = + comp_ctx->aot_frame->sp; + } + } + } + + if (comp_ctx->enable_thread_mgr) { + for (i = 0; i <= br_count; i++) { + target_block = get_target_block(func_ctx, br_depths[i]); + if (!target_block) + return false; + /* Terminate or suspend current thread only when this is a + backward jump */ + if (target_block->label_type == LABEL_TYPE_LOOP) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) + return false; + break; + } + } + } + /* Compare value is not constant, create switch IR */ for (i = 0; i <= br_count; i++) { target_block = get_target_block(func_ctx, br_depths[i]); if (!target_block) return false; - if (target_block->block_type != BLOCK_TYPE_LOOP) { + if (target_block->label_type != LABEL_TYPE_LOOP) { /* Dest block is Block/If/Function block */ /* Create the end block */ if (!target_block->llvm_end_block) { format_block_name(name, sizeof(name), target_block->block_index, - target_block->block_type, - LABEL_END); + target_block->label_type, LABEL_END); CREATE_BLOCK(target_block->llvm_end_block, name); if ((next_llvm_end_block = - find_next_llvm_end_block(target_block))) + find_next_llvm_end_block(target_block))) MOVE_BLOCK_BEFORE(target_block->llvm_end_block, next_llvm_end_block); } - /* Handle return value */ - if (target_block->return_type != VALUE_TYPE_VOID) { - POP(value_ret, target_block->return_type); - CREATE_RETURN_VALUE_PHI(target_block); - ADD_TO_RETURN_PHI(target_block, value_ret); - PUSH(value_ret, target_block->return_type); + /* Handle result values */ + if (target_block->result_count) { + size = sizeof(LLVMValueRef) + * (uint64)target_block->result_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + CREATE_RESULT_VALUE_PHIS(target_block); + for (j = 0; j < target_block->result_count; j++) { + result_index = target_block->result_count - 1 - j; + POP(value, target_block->result_types[result_index]); + values[result_index] = value; + ADD_TO_RESULT_PHIS(target_block, value, result_index); + } + for (j = 0; j < target_block->result_count; j++) { + PUSH(values[j], target_block->result_types[j]); + } + wasm_runtime_free(values); + values = NULL; } target_block->is_reachable = true; if (i == br_count) default_llvm_block = target_block->llvm_end_block; } else { + /* Handle Loop parameters */ + if (target_block->param_count) { + size = sizeof(LLVMValueRef) + * (uint64)target_block->param_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + for (j = 0; j < target_block->param_count; j++) { + param_index = target_block->param_count - 1 - j; + POP(value, target_block->param_types[param_index]); + values[param_index] = value; + ADD_TO_PARAM_PHIS(target_block, value, param_index); + } + for (j = 0; j < target_block->param_count; j++) { + PUSH(values[j], target_block->param_types[j]); + } + wasm_runtime_free(values); + values = NULL; + } if (i == br_count) default_llvm_block = target_block->llvm_entry_block; } @@ -610,9 +1450,9 @@ aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, target_block = get_target_block(func_ctx, br_depths[i]); if (!target_block) return false; - target_llvm_block = target_block->block_type != BLOCK_TYPE_LOOP - ? target_block->llvm_end_block - : target_block->llvm_entry_block; + target_llvm_block = target_block->label_type != LABEL_TYPE_LOOP + ? target_block->llvm_end_block + : target_block->llvm_entry_block; LLVMAddCase(value_switch, value_case, target_llvm_block); } @@ -628,6 +1468,8 @@ aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return aot_compile_op_br(comp_ctx, func_ctx, br_depth, p_frame_ip); } fail: + if (values) + wasm_runtime_free(values); return false; } @@ -637,14 +1479,63 @@ aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { AOTBlock *block_func = func_ctx->block_stack.block_list_head; LLVMValueRef value; + LLVMValueRef ret; + AOTFuncType *func_type; + uint32 i, param_index, result_index; +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMMetadataRef return_location; +#endif bh_assert(block_func); - if (block_func->return_type != VALUE_TYPE_VOID) { - POP(value, block_func->return_type); - LLVMBuildRet(comp_ctx->builder, value); + func_type = func_ctx->aot_func->func_type; + +#if WASM_ENABLE_DEBUG_AOT != 0 + return_location = dwarf_gen_location( + comp_ctx, func_ctx, + (*p_frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code); +#endif + + if (comp_ctx->aux_stack_frame_type + && comp_ctx->call_stack_features.frame_per_function + && !aot_free_frame_per_function_frame_for_aot_func(comp_ctx, + func_ctx)) { + return false; + } + + if (block_func->result_count) { + /* Store extra result values to function parameters */ + for (i = 0; i < block_func->result_count - 1; i++) { + LLVMValueRef res; + result_index = block_func->result_count - 1 - i; + POP(value, block_func->result_types[result_index]); + param_index = func_type->param_count + result_index; + if (!(res = LLVMBuildStore( + comp_ctx->builder, value, + LLVMGetParam(func_ctx->func, param_index)))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + LLVMSetAlignment(res, 1); + } + /* Return the first result value */ + POP(value, block_func->result_types[0]); + if (!(ret = LLVMBuildRet(comp_ctx->builder, value))) { + aot_set_last_error("llvm build return failed."); + goto fail; + } +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMInstructionSetDebugLoc(ret, return_location); +#endif + } + else { + if (!(ret = LLVMBuildRetVoid(comp_ctx->builder))) { + aot_set_last_error("llvm build return void failed."); + goto fail; + } +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMInstructionSetDebugLoc(ret, return_location); +#endif } - else - LLVMBuildRetVoid(comp_ctx->builder); return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); fail: @@ -652,12 +1543,11 @@ aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } bool -aot_compile_op_unreachable(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, +aot_compile_op_unreachable(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint8 **p_frame_ip) { - if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_UNREACHABLE, - false, NULL, NULL)) + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_UNREACHABLE, false, NULL, + NULL)) return false; return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); @@ -665,8 +1555,288 @@ aot_compile_op_unreachable(AOTCompContext *comp_ctx, bool aot_handle_next_reachable_block(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, - uint8 **p_frame_ip) + AOTFuncContext *func_ctx, uint8 **p_frame_ip) { return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); } + +#if WASM_ENABLE_GC != 0 +static bool +commit_gc_and_check_suspend_flags(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 br_depth) +{ + AOTBlock *block_dst; + + if (!(block_dst = get_target_block(func_ctx, br_depth))) { + return false; + } + + if (comp_ctx->aot_frame) { + /* Note that GC is enabled, no need to check it again */ + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + if (comp_ctx->enable_thread_mgr) { + /* Note that GC is enabled, no need to check it again */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, false)) + return false; + } + } + else { + if (comp_ctx->aot_frame->sp > block_dst->frame_sp_max_reached) + block_dst->frame_sp_max_reached = comp_ctx->aot_frame->sp; + } + } + + /* Terminate or suspend current thread only when this is + a backward jump */ + if (comp_ctx->enable_thread_mgr + && block_dst->label_type == LABEL_TYPE_LOOP) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) + return false; + } + + return true; +} + +static bool +compile_gc_cond_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, LLVMValueRef value_cmp) +{ + AOTBlock *block_dst; + LLVMValueRef value, *values = NULL; + LLVMBasicBlockRef llvm_else_block, next_llvm_end_block; + char name[32]; + uint32 i, param_index, result_index; + uint64 size; + + if (!(block_dst = get_target_block(func_ctx, br_depth))) { + return false; + } + + /* Create llvm else block */ + CREATE_BLOCK(llvm_else_block, "br_if_else"); + MOVE_BLOCK_AFTER_CURR(llvm_else_block); + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + /* Dest block is Loop block */ + /* Handle Loop parameters */ + if (block_dst->param_count) { + size = sizeof(LLVMValueRef) * (uint64)block_dst->param_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + for (i = 0; i < block_dst->param_count; i++) { + param_index = block_dst->param_count - 1 - i; + POP(value, block_dst->param_types[param_index]); + ADD_TO_PARAM_PHIS(block_dst, value, param_index); + values[param_index] = value; + } + for (i = 0; i < block_dst->param_count; i++) { + PUSH(values[i], block_dst->param_types[i]); + } + wasm_runtime_free(values); + values = NULL; + } + + BUILD_COND_BR(value_cmp, block_dst->llvm_entry_block, llvm_else_block); + + /* Move builder to else block */ + SET_BUILDER_POS(llvm_else_block); + } + else { + /* Dest block is Block/If/Function block */ + /* Create the end block */ + if (!block_dst->llvm_end_block) { + format_block_name(name, sizeof(name), block_dst->block_index, + block_dst->label_type, LABEL_END); + CREATE_BLOCK(block_dst->llvm_end_block, name); + if ((next_llvm_end_block = find_next_llvm_end_block(block_dst))) + MOVE_BLOCK_BEFORE(block_dst->llvm_end_block, + next_llvm_end_block); + } + + /* Set reachable flag and create condition br IR */ + block_dst->is_reachable = true; + + /* Handle result values */ + if (block_dst->result_count) { + size = sizeof(LLVMValueRef) * (uint64)block_dst->result_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + CREATE_RESULT_VALUE_PHIS(block_dst); + for (i = 0; i < block_dst->result_count; i++) { + result_index = block_dst->result_count - 1 - i; + POP(value, block_dst->result_types[result_index]); + values[result_index] = value; + ADD_TO_RESULT_PHIS(block_dst, value, result_index); + } + for (i = 0; i < block_dst->result_count; i++) { + PUSH(values[i], block_dst->result_types[i]); + } + wasm_runtime_free(values); + values = NULL; + } + + /* Condition jump to end block */ + BUILD_COND_BR(value_cmp, block_dst->llvm_end_block, llvm_else_block); + + /* Move builder to else block */ + SET_BUILDER_POS(llvm_else_block); + } + + return true; +fail: + if (values) + wasm_runtime_free(values); + return false; +} + +bool +aot_compile_op_br_on_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, uint8 **p_frame_ip) +{ + LLVMValueRef gc_obj, value_cmp; + + if (!commit_gc_and_check_suspend_flags(comp_ctx, func_ctx, br_depth)) { + return false; + } + + POP_GC_REF(gc_obj); + + if (!(value_cmp = + LLVMBuildIsNull(comp_ctx->builder, gc_obj, "cmp_gc_obj"))) { + aot_set_last_error("llvm build isnull failed."); + goto fail; + } + + if (!compile_gc_cond_br(comp_ctx, func_ctx, br_depth, value_cmp)) { + goto fail; + } + + PUSH_GC_REF(gc_obj); + return true; +fail: + return false; +} + +bool +aot_compile_op_br_on_non_null(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 br_depth, + uint8 **p_frame_ip) +{ + LLVMValueRef gc_obj, value_cmp; + + if (!commit_gc_and_check_suspend_flags(comp_ctx, func_ctx, br_depth)) { + return false; + } + + GET_GC_REF_FROM_STACK(gc_obj); + + if (!(value_cmp = + LLVMBuildIsNotNull(comp_ctx->builder, gc_obj, "cmp_gc_obj"))) { + aot_set_last_error("llvm build isnotnull failed."); + goto fail; + } + + if (!compile_gc_cond_br(comp_ctx, func_ctx, br_depth, value_cmp)) { + goto fail; + } + + POP_GC_REF(gc_obj); + return true; +fail: + return false; +} + +bool +aot_compile_op_br_on_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 heap_type, bool nullable, bool br_on_fail, + uint32 br_depth, uint8 **p_frame_ip) +{ + LLVMValueRef gc_obj, is_null, castable, not_castable, br_if_phi; + LLVMBasicBlockRef block_curr, block_non_null, block_br_if; + + if (!commit_gc_and_check_suspend_flags(comp_ctx, func_ctx, br_depth)) { + return false; + } + + GET_GC_REF_FROM_STACK(gc_obj); + + block_curr = CURR_BLOCK(); + + CREATE_BLOCK(block_non_null, "obj_non_null"); + MOVE_BLOCK_AFTER_CURR(block_non_null); + CREATE_BLOCK(block_br_if, "br_if"); + MOVE_BLOCK_AFTER(block_br_if, block_non_null); + + SET_BUILDER_POS(block_br_if); + if (!(br_if_phi = + LLVMBuildPhi(comp_ctx->builder, INT1_TYPE, "br_if_phi"))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + + SET_BUILDER_POS(block_curr); + + if (!(is_null = LLVMBuildIsNull(comp_ctx->builder, gc_obj, "is_null"))) { + aot_set_last_error("llvm build isnull failed."); + goto fail; + } + + BUILD_COND_BR(is_null, block_br_if, block_non_null); + + if ((!br_on_fail && nullable) || (br_on_fail && !nullable)) { + LLVMAddIncoming(br_if_phi, &I1_ONE, &block_curr, 1); + } + else { /* (!br_on_fail && !nullable) || (br_on_fail && nullable)) */ + LLVMAddIncoming(br_if_phi, &I1_ZERO, &block_curr, 1); + } + + SET_BUILDER_POS(block_non_null); + if (heap_type >= 0) { + if (!aot_call_aot_obj_is_instance_of(comp_ctx, func_ctx, gc_obj, + I32_CONST(heap_type), &castable)) + goto fail; + } + else { + if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj, + I32_CONST(heap_type), &castable)) + goto fail; + } + + if (!br_on_fail) { + if (!(castable = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, castable, + I8_ZERO, "castable"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + LLVMAddIncoming(br_if_phi, &castable, &block_non_null, 1); + } + else { + if (!(not_castable = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, + castable, I8_ZERO, "castable"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + LLVMAddIncoming(br_if_phi, ¬_castable, &block_non_null, 1); + } + BUILD_BR(block_br_if); + + SET_BUILDER_POS(block_br_if); + if (!compile_gc_cond_br(comp_ctx, func_ctx, br_depth, br_if_phi)) { + goto fail; + } + + return true; +fail: + return false; +} + +#endif /* End of WASM_ENABLE_GC != 0 */ diff --git a/core/iwasm/compilation/aot_emit_control.h b/core/iwasm/compilation/aot_emit_control.h index d222ea607f..7ea527a20c 100644 --- a/core/iwasm/compilation/aot_emit_control.h +++ b/core/iwasm/compilation/aot_emit_control.h @@ -14,8 +14,9 @@ extern "C" { bool aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint8 **p_frame_ip, uint8 *frame_ip_end, - uint32 block_type, uint32 block_ret_type); + uint8 **p_frame_ip, uint8 *frame_ip_end, uint32 label_type, + uint32 param_count, uint8 *param_types, + uint32 result_count, uint8 *result_types); bool aot_compile_op_else(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, @@ -31,30 +32,47 @@ aot_compile_op_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 br_depth, uint8 **p_frame_ip); + uint8 **p_frame_ip); bool aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 *br_depths, uint32 br_count, - uint8 **p_frame_ip); + uint32 *br_depths, uint32 br_count, uint8 **p_frame_ip); bool aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint8 **p_frame_ip); bool -aot_compile_op_unreachable(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, +aot_compile_op_unreachable(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint8 **p_frame_ip); bool aot_handle_next_reachable_block(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx, - uint8 **p_frame_ip); + AOTFuncContext *func_ctx, uint8 **p_frame_ip); + +bool +check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool check_terminate_and_suspend); + +#if WASM_ENABLE_GC != 0 +bool +aot_compile_op_br_on_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, uint8 **p_frame_ip); + +bool +aot_compile_op_br_on_non_null(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 br_depth, + + uint8 **p_frame_ip); + +bool +aot_compile_op_br_on_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 heap_type, bool nullable, bool br_on_fail, + uint32 br_depth, uint8 **p_frame_ip); +#endif #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_EMIT_CONTROL_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_conversion.c b/core/iwasm/compilation/aot_emit_conversion.c index 706c267b1b..fa5c2ab955 100644 --- a/core/iwasm/compilation/aot_emit_conversion.c +++ b/core/iwasm/compilation/aot_emit_conversion.c @@ -6,27 +6,58 @@ #include "aot_emit_conversion.h" #include "aot_emit_exception.h" #include "aot_emit_numberic.h" +#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 dest_type, - LLVMValueRef min_value, LLVMValueRef max_value, - char *name, bool sign) + LLVMValueRef operand, LLVMTypeRef src_type, + LLVMTypeRef dest_type, LLVMValueRef min_value, + LLVMValueRef max_value, char *name, bool sign) { LLVMBasicBlockRef check_nan_succ, check_overflow_succ; LLVMValueRef is_less, is_greater, res; - if (!(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."); goto fail; } - if (!(check_nan_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_nan_succ"))) { + if (!(check_nan_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_nan_succ"))) { aot_set_last_error("llvm add basic block failed."); goto fail; } @@ -34,32 +65,38 @@ trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMMoveBasicBlockAfter(check_nan_succ, LLVMGetInsertBlock(comp_ctx->builder)); - if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INVALID_CONVERSION_TO_INTEGER, - true, res, check_nan_succ))) + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_INVALID_CONVERSION_TO_INTEGER, true, res, + check_nan_succ))) goto fail; - if (!(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 (!(is_greater = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGE, operand, - max_value, "fcmp_max_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."); goto fail; } - if (!(res = LLVMBuildOr(comp_ctx->builder, is_less, is_greater, "is_overflow"))) { + if (!(res = LLVMBuildOr(comp_ctx->builder, is_less, is_greater, + "is_overflow"))) { aot_set_last_error("llvm build logic and failed."); goto fail; } /* Check if float value out of range */ - if (!(check_overflow_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_overflow_succ"))) { + if (!(check_overflow_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_overflow_succ"))) { aot_set_last_error("llvm add basic block failed."); goto fail; } @@ -67,14 +104,24 @@ trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMMoveBasicBlockAfter(check_overflow_succ, LLVMGetInsertBlock(comp_ctx->builder)); - if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, - true, res, check_overflow_succ))) + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, true, + res, check_overflow_succ))) goto fail; - if (sign) - res = LLVMBuildFPToSI(comp_ctx->builder, operand, dest_type, name); - else - res = LLVMBuildFPToUI(comp_ctx->builder, operand, dest_type, name); + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, name)) { + LLVMTypeRef param_types[1]; + param_types[0] = src_type; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, name, dest_type, + param_types, 1, operand); + } + else { + if (sign) + res = LLVMBuildFPToSI(comp_ctx->builder, operand, dest_type, name); + else + res = LLVMBuildFPToUI(comp_ctx->builder, operand, dest_type, name); + } + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; @@ -89,6 +136,189 @@ trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } +#define ADD_BASIC_BLOCK(block, name) \ + do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + \ + LLVMMoveBasicBlockAfter(block, LLVMGetInsertBlock(comp_ctx->builder)); \ + } while (0) + +static bool +trunc_sat_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef operand, LLVMTypeRef src_type, + LLVMTypeRef dest_type, LLVMValueRef min_value, + LLVMValueRef max_value, char *name, bool sign) +{ + LLVMBasicBlockRef check_nan_succ, check_less_succ, check_greater_succ; + LLVMBasicBlockRef is_nan_block, is_less_block, is_greater_block, res_block; + LLVMValueRef is_less, is_greater, res, phi; + LLVMValueRef zero = (dest_type == I32_TYPE) ? I32_ZERO : I64_ZERO; + LLVMValueRef vmin, vmax; + + 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; + } + + ADD_BASIC_BLOCK(check_nan_succ, "check_nan_succ"); + ADD_BASIC_BLOCK(is_nan_block, "is_nan_block"); + ADD_BASIC_BLOCK(check_less_succ, "check_less_succ"); + ADD_BASIC_BLOCK(is_less_block, "is_less_block"); + ADD_BASIC_BLOCK(check_greater_succ, "check_greater_succ"); + ADD_BASIC_BLOCK(is_greater_block, "is_greater_block"); + ADD_BASIC_BLOCK(res_block, "res_block"); + + if (!LLVMBuildCondBr(comp_ctx->builder, res, is_nan_block, + check_nan_succ)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Start to translate is_nan block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, is_nan_block); + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate check_nan_succ block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_nan_succ); + 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; + } + if (!LLVMBuildCondBr(comp_ctx->builder, is_less, is_less_block, + check_less_succ)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Start to translate is_less block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, is_less_block); + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate check_less_succ block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_less_succ); + 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; + } + if (!LLVMBuildCondBr(comp_ctx->builder, is_greater, is_greater_block, + check_greater_succ)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Start to translate is_greater block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, is_greater_block); + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate check_greater_succ block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_greater_succ); + + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, name)) { + LLVMTypeRef param_types[1]; + param_types[0] = src_type; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, name, dest_type, + param_types, 1, operand); + } + else { + char intrinsic[128]; + + /* Integer width is always 32 or 64 here. */ + + snprintf(intrinsic, sizeof(intrinsic), "i%d_trunc_f%d_%c", + LLVMGetIntTypeWidth(dest_type), + LLVMGetTypeKind(src_type) == LLVMFloatTypeKind ? 32 : 64, + sign ? 's' : 'u'); + + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, intrinsic)) { + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, + dest_type, &src_type, 1, operand); + } + else { + if (sign) { + res = LLVMBuildFPToSI(comp_ctx->builder, operand, dest_type, + name); + } + else { + res = LLVMBuildFPToUI(comp_ctx->builder, operand, dest_type, + name); + } + } + } + + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate res_block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, res_block); + /* Create result phi */ + if (!(phi = LLVMBuildPhi(comp_ctx->builder, dest_type, + "trunc_sat_result_phi"))) { + aot_set_last_error("llvm build phi failed."); + return false; + } + + /* Add phi incoming values */ + if (dest_type == I32_TYPE) { + if (sign) { + vmin = I32_CONST(INT32_MIN); + vmax = I32_CONST(INT32_MAX); + } + else { + vmin = I32_CONST(0); + vmax = I32_CONST(UINT32_MAX); + } + } + else if (dest_type == I64_TYPE) { + if (sign) { + vmin = I64_CONST(INT64_MIN); + vmax = I64_CONST(INT64_MAX); + } + else { + vmin = I64_CONST(0); + vmax = I64_CONST(UINT64_MAX); + } + } + LLVMAddIncoming(phi, &zero, &is_nan_block, 1); + LLVMAddIncoming(phi, &vmin, &is_less_block, 1); + LLVMAddIncoming(phi, &vmax, &is_greater_block, 1); + LLVMAddIncoming(phi, &res, &check_greater_succ, 1); + + if (dest_type == I32_TYPE) + PUSH_I32(phi); + else if (dest_type == I64_TYPE) + PUSH_I64(phi); + return true; +fail: + return false; +} + bool aot_compile_op_i32_wrap_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { @@ -96,7 +326,8 @@ aot_compile_op_i32_wrap_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) POP_I64(value); - if (!(res = LLVMBuildTrunc(comp_ctx->builder, value, I32_TYPE, "i32_wrap_i64"))) { + if (!(res = LLVMBuildTrunc(comp_ctx->builder, value, I32_TYPE, + "i32_wrap_i64"))) { aot_set_last_error("llvm build conversion failed."); return false; } @@ -109,66 +340,166 @@ aot_compile_op_i32_wrap_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) bool aot_compile_op_i32_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) + bool sign, bool saturating) { LLVMValueRef value; LLVMValueRef min_value, max_value; POP_F32(value); - if (sign) { - min_value = F32_CONST(-2147483904.0f); - max_value = F32_CONST(2147483648.0f); + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "f32.const")) { + WASMValue wasm_value; + if (sign) { + wasm_value.f32 = -2147483904.0f; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + wasm_value.f32 = 2147483648.0f; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + } + else { + wasm_value.f32 = -1.0f; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + wasm_value.f32 = 4294967296.0f; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + } } else { - min_value = F32_CONST(-1.0f); - max_value = F32_CONST(4294967296.0f); + if (sign) { + min_value = F32_CONST(-2147483904.0f); + max_value = F32_CONST(2147483648.0f); + } + else { + min_value = F32_CONST(-1.0f); + max_value = F32_CONST(4294967296.0f); + } } + CHECK_LLVM_CONST(min_value); + CHECK_LLVM_CONST(max_value); - return trunc_float_to_int(comp_ctx, func_ctx, value, - I32_TYPE, min_value, max_value, - sign ? "i32_trunc_f32_s" : "i32_trunc_f32_u", sign); + if (!saturating) + return trunc_float_to_int( + comp_ctx, func_ctx, value, F32_TYPE, I32_TYPE, min_value, max_value, + sign ? "i32_trunc_f32_s" : "i32_trunc_f32_u", sign); + else + return trunc_sat_float_to_int( + comp_ctx, func_ctx, value, F32_TYPE, I32_TYPE, min_value, max_value, + sign ? "i32_trunc_sat_f32_s" : "i32_trunc_sat_f32_u", sign); fail: return false; } bool aot_compile_op_i32_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) + bool sign, bool saturating) { LLVMValueRef value; LLVMValueRef min_value, max_value; POP_F64(value); - if (sign) { - min_value = F64_CONST(-2147483649.0); - max_value = F64_CONST(2147483648.0); + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "f64.const")) { + WASMValue wasm_value; + if (sign) { + wasm_value.f64 = -2147483649.0; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + wasm_value.f64 = 2147483648.0; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + } + else { + wasm_value.f64 = -1.0; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + wasm_value.f64 = 4294967296.0; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + } } else { - min_value = F64_CONST(-1.0); - max_value = F64_CONST(4294967296.0); + if (sign) { + min_value = F64_CONST(-2147483649.0); + max_value = F64_CONST(2147483648.0); + } + else { + min_value = F64_CONST(-1.0); + max_value = F64_CONST(4294967296.0); + } } + CHECK_LLVM_CONST(min_value); + CHECK_LLVM_CONST(max_value); - return trunc_float_to_int(comp_ctx, func_ctx, value, - I32_TYPE, min_value, max_value, - sign ? "i32_trunc_f64_s" : "i32_trunc_f64_u", sign); + if (!saturating) + return trunc_float_to_int( + comp_ctx, func_ctx, value, F64_TYPE, I32_TYPE, min_value, max_value, + sign ? "i32_trunc_f64_s" : "i32_trunc_f64_u", sign); + else + return trunc_sat_float_to_int( + comp_ctx, func_ctx, value, F64_TYPE, I32_TYPE, min_value, max_value, + sign ? "i32_trunc_sat_f64_s" : "i32_trunc_sat_f64_u", sign); fail: return false; } bool -aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) +aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign) { LLVMValueRef value, res; POP_I32(value); if (sign) - res = LLVMBuildSExt(comp_ctx->builder, value, I64_TYPE, "i64_extend_i32_s"); + res = LLVMBuildSExt(comp_ctx->builder, value, I64_TYPE, + "i64_extend_i32_s"); else - res = LLVMBuildZExt(comp_ctx->builder, value, I64_TYPE, "i64_extend_i32_u"); + res = LLVMBuildZExt(comp_ctx->builder, value, I64_TYPE, + "i64_extend_i32_u"); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_I64(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_extend_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, int8 bitwidth) +{ + LLVMValueRef value, res, cast_value = NULL; + + POP_I64(value); + + if (bitwidth == 8) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, INT8_TYPE, + true, "i8_intcast_i64"); + } + else if (bitwidth == 16) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, INT16_TYPE, + true, "i16_intcast_i64"); + } + else if (bitwidth == 32) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, I32_TYPE, true, + "i32_intcast_i64"); + } + + if (!cast_value) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + res = LLVMBuildSExt(comp_ctx->builder, cast_value, I64_TYPE, + "i64_extend_i64_s"); + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; @@ -180,68 +511,177 @@ aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx return false; } +bool +aot_compile_op_i32_extend_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, int8 bitwidth) +{ + LLVMValueRef value, res, cast_value = NULL; + + POP_I32(value); + + if (bitwidth == 8) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, INT8_TYPE, + true, "i8_intcast_i32"); + } + else if (bitwidth == 16) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, INT16_TYPE, + true, "i16_intcast_i32"); + } + + if (!cast_value) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + res = LLVMBuildSExt(comp_ctx->builder, cast_value, I32_TYPE, + "i32_extend_i32_s"); + + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_I32(res); + return true; +fail: + return false; +} + bool aot_compile_op_i64_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) + bool sign, bool saturating) { LLVMValueRef value; LLVMValueRef min_value, max_value; POP_F32(value); - if (sign) { - min_value = F32_CONST(-9223373136366403584.0f); - max_value = F32_CONST(9223372036854775808.0f); + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "f32.const")) { + WASMValue wasm_value; + if (sign) { + wasm_value.f32 = -9223373136366403584.0f; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + wasm_value.f32 = 9223372036854775808.0f; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + } + else { + wasm_value.f32 = -1.0f; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + wasm_value.f32 = 18446744073709551616.0f; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F32); + } } else { - min_value = F32_CONST(-1.0f); - max_value = F32_CONST(18446744073709551616.0f); + if (sign) { + min_value = F32_CONST(-9223373136366403584.0f); + max_value = F32_CONST(9223372036854775808.0f); + } + else { + min_value = F32_CONST(-1.0f); + max_value = F32_CONST(18446744073709551616.0f); + } } + CHECK_LLVM_CONST(min_value); + CHECK_LLVM_CONST(max_value); - return trunc_float_to_int(comp_ctx, func_ctx, value, - I64_TYPE, min_value, max_value, - sign ? "i64_trunc_f32_s" : "i64_trunc_f32_u", sign); + if (!saturating) + return trunc_float_to_int( + comp_ctx, func_ctx, value, F32_TYPE, I64_TYPE, min_value, max_value, + sign ? "i64_trunc_f32_s" : "i64_trunc_f32_u", sign); + else + return trunc_sat_float_to_int( + comp_ctx, func_ctx, value, F32_TYPE, I64_TYPE, min_value, max_value, + sign ? "i64_trunc_sat_f32_s" : "i64_trunc_sat_f32_u", sign); fail: return false; } bool aot_compile_op_i64_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) + bool sign, bool saturating) { LLVMValueRef value; LLVMValueRef min_value, max_value; POP_F64(value); - if (sign) { - min_value = F64_CONST(-9223372036854777856.0); - max_value = F64_CONST(9223372036854775808.0); + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability(comp_ctx, "f64.const")) { + WASMValue wasm_value; + if (sign) { + wasm_value.f64 = -9223372036854777856.0; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + wasm_value.f64 = 9223372036854775808.0; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + } + else { + wasm_value.f64 = -1.0; + min_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + wasm_value.f64 = 18446744073709551616.0; + max_value = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, VALUE_TYPE_F64); + } } else { - min_value = F64_CONST(-1.0); - max_value = F64_CONST(18446744073709551616.0); + if (sign) { + min_value = F64_CONST(-9223372036854777856.0); + max_value = F64_CONST(9223372036854775808.0); + } + else { + min_value = F64_CONST(-1.0); + max_value = F64_CONST(18446744073709551616.0); + } } + CHECK_LLVM_CONST(min_value); + CHECK_LLVM_CONST(max_value); + + if (!saturating) + return trunc_float_to_int( + comp_ctx, func_ctx, value, F64_TYPE, I64_TYPE, min_value, max_value, + sign ? "i64_trunc_f64_s" : "i64_trunc_f64_u", sign); + else + return trunc_sat_float_to_int( + comp_ctx, func_ctx, value, F64_TYPE, I64_TYPE, min_value, max_value, + sign ? "i64_trunc_sat_f64_s" : "i64_trunc_sat_f64_u", sign); - return trunc_float_to_int(comp_ctx, func_ctx, value, - I64_TYPE, min_value, max_value, - sign ? "i64_trunc_f64_s" : "i64_trunc_f64_u", sign); fail: return false; } bool -aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) +aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign) { LLVMValueRef value, res; POP_I32(value); - if (sign) - res = LLVMBuildSIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i32_s"); - else - res = LLVMBuildUIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i32_u"); + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability( + comp_ctx, sign ? "f32_convert_i32_s" : "f32_convert_i32_u")) { + LLVMTypeRef param_types[1]; + param_types[0] = I32_TYPE; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, + sign ? "f32_convert_i32_s" + : "f32_convert_i32_u", + F32_TYPE, param_types, 1, value); + } + else { + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F32_TYPE, + "f32_convert_i32_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F32_TYPE, + "f32_convert_i32_u"); + } if (!res) { aot_set_last_error("llvm build conversion failed."); return false; @@ -254,17 +694,32 @@ aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ct } bool -aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) +aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign) { LLVMValueRef value, res; POP_I64(value); - if (sign) - res = LLVMBuildSIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i64_s"); - else - res = LLVMBuildUIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i64_u"); + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability( + comp_ctx, sign ? "f32_convert_i64_s" : "f32_convert_i64_u")) { + LLVMTypeRef param_types[1]; + param_types[0] = I64_TYPE; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, + sign ? "f32_convert_i64_s" + : "f32_convert_i64_u", + F32_TYPE, param_types, 1, value); + } + else { + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F32_TYPE, + "f32_convert_i64_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F32_TYPE, + "f32_convert_i64_u"); + } + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; @@ -277,13 +732,26 @@ aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ct } bool -aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) { LLVMValueRef value, res; POP_F64(value); - if (!(res = LLVMBuildFPTrunc(comp_ctx->builder, value, F32_TYPE, "f32_demote_f64"))) { + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, "f32_demote_f64")) { + LLVMTypeRef param_types[1]; + param_types[0] = F64_TYPE; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, "f32_demote_f64", + F32_TYPE, param_types, 1, value); + } + else { + res = LLVMBuildFPTrunc(comp_ctx->builder, value, F32_TYPE, + "f32_demote_f64"); + } + + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; } @@ -295,17 +763,33 @@ aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx } bool -aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) +aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign) { LLVMValueRef value, res; POP_I32(value); - if (sign) - res = LLVMBuildSIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i32_s"); - else - res = LLVMBuildUIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i32_u"); + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability( + comp_ctx, sign ? "f64_convert_i32_s" : "f64_convert_i32_u")) { + LLVMTypeRef param_types[1]; + param_types[0] = I32_TYPE; + + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, + sign ? "f64_convert_i32_s" + : "f64_convert_i32_u", + F64_TYPE, param_types, 1, value); + } + else { + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F64_TYPE, + "f64_convert_i32_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F64_TYPE, + "f64_convert_i32_u"); + } + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; @@ -318,17 +802,33 @@ aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ct } bool -aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign) +aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign) { LLVMValueRef value, res; POP_I64(value); - if (sign) - res = LLVMBuildSIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i64_s"); - else - res = LLVMBuildUIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i64_u"); + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability( + comp_ctx, sign ? "f64_convert_i64_s" : "f64_convert_i64_u")) { + LLVMTypeRef param_types[1]; + param_types[0] = I64_TYPE; + + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, + sign ? "f64_convert_i64_s" + : "f64_convert_i64_u", + F64_TYPE, param_types, 1, value); + } + else { + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F64_TYPE, + "f64_convert_i64_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F64_TYPE, + "f64_convert_i64_u"); + } + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; @@ -341,13 +841,26 @@ aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ct } bool -aot_compile_op_f64_promote_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +aot_compile_op_f64_promote_f32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) { LLVMValueRef value, res; POP_F32(value); - if (!(res = LLVMBuildFPExt(comp_ctx->builder, value, F64_TYPE, "f64_promote_f32"))) { + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, "f64_promote_f32")) { + LLVMTypeRef param_types[1]; + param_types[0] = F32_TYPE; + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, "f64_promote_f32", + F64_TYPE, param_types, 1, value); + } + else { + res = LLVMBuildFPExt(comp_ctx->builder, value, F64_TYPE, + "f64_promote_f32"); + } + + if (!res) { aot_set_last_error("llvm build conversion failed."); return false; } @@ -367,8 +880,8 @@ aot_compile_op_i64_reinterpret_f64(AOTCompContext *comp_ctx, { LLVMValueRef value; POP_F64(value); - if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, - I64_TYPE, "i64"))) { + if (!(value = + LLVMBuildBitCast(comp_ctx->builder, value, I64_TYPE, "i64"))) { aot_set_last_error("llvm build fp to si failed."); return false; } @@ -378,15 +891,14 @@ aot_compile_op_i64_reinterpret_f64(AOTCompContext *comp_ctx, return false; } - bool aot_compile_op_i32_reinterpret_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { LLVMValueRef value; POP_F32(value); - if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, - I32_TYPE, "i32"))) { + if (!(value = + LLVMBuildBitCast(comp_ctx->builder, value, I32_TYPE, "i32"))) { aot_set_last_error("llvm build fp to si failed."); return false; } @@ -402,8 +914,8 @@ aot_compile_op_f64_reinterpret_i64(AOTCompContext *comp_ctx, { LLVMValueRef value; POP_I64(value); - if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, - F64_TYPE, "f64"))) { + if (!(value = + LLVMBuildBitCast(comp_ctx->builder, value, F64_TYPE, "f64"))) { aot_set_last_error("llvm build si to fp failed."); return false; } @@ -419,8 +931,8 @@ aot_compile_op_f32_reinterpret_i32(AOTCompContext *comp_ctx, { LLVMValueRef value; POP_I32(value); - if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, - F32_TYPE, "f32"))) { + if (!(value = + LLVMBuildBitCast(comp_ctx->builder, value, F32_TYPE, "f32"))) { aot_set_last_error("llvm build si to fp failed."); return false; } @@ -429,4 +941,3 @@ aot_compile_op_f32_reinterpret_i32(AOTCompContext *comp_ctx, fail: return false; } - diff --git a/core/iwasm/compilation/aot_emit_conversion.h b/core/iwasm/compilation/aot_emit_conversion.h index d12ffe6157..a0e2fcb2ec 100644 --- a/core/iwasm/compilation/aot_emit_conversion.h +++ b/core/iwasm/compilation/aot_emit_conversion.h @@ -17,45 +17,55 @@ aot_compile_op_i32_wrap_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); bool aot_compile_op_i32_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); + bool sign, bool saturating); bool aot_compile_op_i32_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); + bool sign, bool saturating); bool -aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); +aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign); + +bool +aot_compile_op_i64_extend_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, int8 bitwidth); + +bool +aot_compile_op_i32_extend_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, int8 bitwidth); bool aot_compile_op_i64_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); + bool sign, bool saturating); bool aot_compile_op_i64_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); + bool sign, bool saturating); bool -aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); +aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign); bool -aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); +aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign); bool -aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); bool -aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); +aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign); bool -aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool sign); +aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool sign); bool -aot_compile_op_f64_promote_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +aot_compile_op_f64_promote_f32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); bool aot_compile_op_i64_reinterpret_f64(AOTCompContext *comp_ctx, @@ -78,4 +88,3 @@ aot_compile_op_f32_reinterpret_i32(AOTCompContext *comp_ctx, #endif #endif /* end of _AOT_EMIT_CONVERSION_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_exception.c b/core/iwasm/compilation/aot_emit_exception.c index 4089904854..1527e83e51 100644 --- a/core/iwasm/compilation/aot_emit_exception.c +++ b/core/iwasm/compilation/aot_emit_exception.c @@ -4,34 +4,20 @@ */ #include "aot_emit_exception.h" +#include "aot_compiler.h" +#include "../interpreter/wasm_runtime.h" #include "../aot/aot_runtime.h" -static char *exce_block_names[] = { - "exce_unreachable", /* EXCE_UNREACHABLE */ - "exce_out_of_memory", /* EXCE_OUT_OF_MEMORY */ - "exce_out_of_bounds_mem_access",/* EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS */ - "exce_integer_overflow", /* EXCE_INTEGER_OVERFLOW */ - "exce_divide_by_zero", /* EXCE_INTEGER_DIVIDE_BY_ZERO */ - "exce_invalid_convert_to_int", /* EXCE_INVALID_CONVERSION_TO_INTEGER */ - "exce_invalid_func_type_idx", /* EXCE_INVALID_FUNCTION_TYPE_INDEX */ - "exce_invalid_func_idx", /* EXCE_INVALID_FUNCTION_INDEX */ - "exce_undefined_element", /* EXCE_UNDEFINED_ELEMENT */ - "exce_uninit_element", /* EXCE_UNINITIALIZED_ELEMENT */ - "exce_call_unlinked" /* EXCE_CALL_UNLINKED_IMPORT_FUNC */ -}; - bool aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - int32 exception_id, - bool is_cond_br, - LLVMValueRef cond_br_if, + int32 exception_id, bool is_cond_br, LLVMValueRef cond_br_if, LLVMBasicBlockRef cond_br_else_block) { - LLVMBasicBlockRef exce_block; LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); LLVMValueRef exce_id = I32_CONST((uint32)exception_id), func_const, func; LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; LLVMValueRef param_values[2]; + bool is_64bit = (comp_ctx->pointer_size == sizeof(uint64)) ? true : false; bh_assert(exception_id >= 0 && exception_id < EXCE_NUM); @@ -39,10 +25,8 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Create got_exception block if needed */ if (!func_ctx->got_exception_block) { - if (!(func_ctx->got_exception_block = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "got_exception"))) { + if (!(func_ctx->got_exception_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "got_exception"))) { aot_set_last_error("add LLVM basic block failed."); return false; } @@ -50,23 +34,36 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMPositionBuilderAtEnd(comp_ctx->builder, func_ctx->got_exception_block); - /* Create exection id phi */ - if (!(func_ctx->exception_id_phi = - LLVMBuildPhi(comp_ctx->builder, - comp_ctx->basic_types.int32_type, - "exception_id_phi"))) { + /* Create exception id phi */ + if (!(func_ctx->exception_id_phi = LLVMBuildPhi( + comp_ctx->builder, I32_TYPE, "exception_id_phi"))) { aot_set_last_error("llvm build phi failed."); return false; } + if (comp_ctx->aot_frame && comp_ctx->call_stack_features.trap_ip) { + /* Create exception ip phi */ + if (!(func_ctx->exception_ip_phi = LLVMBuildPhi( + comp_ctx->builder, is_64bit ? I64_TYPE : I32_TYPE, + "exception_ip_phi"))) { + aot_set_last_error("llvm build phi failed."); + return false; + } + + /* Commit ip to current frame */ + if (!aot_gen_commit_ip(comp_ctx, func_ctx, + func_ctx->exception_ip_phi, is_64bit)) { + return false; + } + } + /* Call aot_set_exception_with_id() to throw exception */ param_types[0] = INT8_PTR_TYPE; param_types[1] = I32_TYPE; ret_type = VOID_TYPE; /* Create function type */ - if (!(func_type = LLVMFunctionType(ret_type, param_types, - 2, false))) { + if (!(func_type = LLVMFunctionType(ret_type, param_types, 2, false))) { aot_set_last_error("create LLVM function type failed."); return false; } @@ -79,17 +76,35 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } /* Create LLVM function with const function pointer */ if (!(func_const = - I64_CONST((uint64)(uintptr_t)aot_set_exception_with_id)) + I64_CONST((uint64)(uintptr_t)jit_set_exception_with_id)) || !(func = LLVMConstIntToPtr(func_const, func_ptr_type))) { aot_set_last_error("create LLVM value failed."); return false; } } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + func_index = aot_get_native_symbol_index( + comp_ctx, "aot_set_exception_with_id"); + if (func_index < 0) { + return false; + } + if (!(func = + aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } else { /* Create LLVM function with external function pointer */ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, + if (!(func = LLVMGetNamedFunction(func_ctx->module, "aot_set_exception_with_id")) - && !(func = LLVMAddFunction(comp_ctx->module, + && !(func = LLVMAddFunction(func_ctx->module, "aot_set_exception_with_id", func_type))) { aot_set_last_error("add LLVM function failed."); @@ -100,68 +115,57 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Call the aot_set_exception_with_id() function */ param_values[0] = func_ctx->aot_inst; param_values[1] = func_ctx->exception_id_phi; - if (!LLVMBuildCall(comp_ctx->builder, func, param_values, - 2, "")) { + if (!LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 2, + "")) { aot_set_last_error("llvm build call failed."); return false; } /* Create return IR */ AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; - if (aot_func_type->result_count) { - switch (aot_func_type->types[aot_func_type->param_count]) { - case VALUE_TYPE_I32: - LLVMBuildRet(comp_ctx->builder, I32_ZERO); - break; - case VALUE_TYPE_I64: - LLVMBuildRet(comp_ctx->builder, I64_ZERO); - break; - case VALUE_TYPE_F32: - LLVMBuildRet(comp_ctx->builder, F32_ZERO); - break; - case VALUE_TYPE_F64: - LLVMBuildRet(comp_ctx->builder, F64_ZERO); - break; - } - } - else { - LLVMBuildRetVoid(comp_ctx->builder); + if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { + return false; } /* Resume the builder position */ LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); } - /* Create exception block if needed */ - if (!(exce_block = func_ctx->exception_blocks[exception_id])) { - if (!(func_ctx->exception_blocks[exception_id] = exce_block = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - exce_block_names[exception_id]))) { - aot_set_last_error("add LLVM basic block failed."); - return false; + /* Add phi incoming value to got_exception block */ + LLVMAddIncoming(func_ctx->exception_id_phi, &exce_id, &block_curr, 1); + + if (comp_ctx->aot_frame && comp_ctx->call_stack_features.trap_ip) { + const uint8 *ip = comp_ctx->aot_frame->frame_ip; + LLVMValueRef exce_ip = NULL; + + if (!comp_ctx->is_jit_mode) { + WASMModule *module = comp_ctx->comp_data->wasm_module; + if (is_64bit) + exce_ip = + I64_CONST((uint64)(uintptr_t)(ip - module->load_addr)); + else + exce_ip = + I32_CONST((uint32)(uintptr_t)(ip - module->load_addr)); + } + else { + if (is_64bit) + exce_ip = I64_CONST((uint64)(uintptr_t)ip); + else + exce_ip = I32_CONST((uint32)(uintptr_t)ip); } - /* Move before got_exception block */ - LLVMMoveBasicBlockBefore(exce_block, func_ctx->got_exception_block); - - /* Add phi incoming value to got_exception block */ - LLVMAddIncoming(func_ctx->exception_id_phi, &exce_id, &exce_block, 1); - - /* Jump to got exception block */ - LLVMPositionBuilderAtEnd(comp_ctx->builder, exce_block); - if (!LLVMBuildBr(comp_ctx->builder, func_ctx->got_exception_block)) { - aot_set_last_error("llvm build br failed."); + if (!exce_ip) { + aot_set_last_error("llvm build const failed"); return false; } - } - /* Resume builder position */ - LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + /* Add phi incoming value to got_exception block */ + LLVMAddIncoming(func_ctx->exception_ip_phi, &exce_ip, &block_curr, 1); + } if (!is_cond_br) { /* not condition br, create br IR */ - if (!LLVMBuildBr(comp_ctx->builder, exce_block)) { + if (!LLVMBuildBr(comp_ctx->builder, func_ctx->got_exception_block)) { aot_set_last_error("llvm build br failed."); return false; } @@ -169,7 +173,8 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, else { /* Create condition br */ if (!LLVMBuildCondBr(comp_ctx->builder, cond_br_if, - exce_block, cond_br_else_block)) { + func_ctx->got_exception_block, + cond_br_else_block)) { aot_set_last_error("llvm build cond br failed."); return false; } @@ -181,4 +186,3 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, fail: return false; } - diff --git a/core/iwasm/compilation/aot_emit_exception.h b/core/iwasm/compilation/aot_emit_exception.h index e86faf0360..91c8bd3cf5 100644 --- a/core/iwasm/compilation/aot_emit_exception.h +++ b/core/iwasm/compilation/aot_emit_exception.h @@ -14,9 +14,7 @@ extern "C" { bool aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - int32 exception_id, - bool is_cond_br, - LLVMValueRef cond_br_if, + int32 exception_id, bool is_cond_br, LLVMValueRef cond_br_if, LLVMBasicBlockRef cond_br_else_block); #ifdef __cplusplus @@ -24,4 +22,3 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, #endif #endif /* end of _AOT_EMIT_EXCEPTION_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index 3311806b76..a1bc46332c 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -6,30 +6,93 @@ #include "aot_emit_function.h" #include "aot_emit_exception.h" #include "aot_emit_control.h" +#include "aot_emit_table.h" +#include "aot_stack_frame_comp.h" #include "../aot/aot_runtime.h" +#if WASM_ENABLE_GC != 0 +#include "aot_emit_gc.h" +#endif + +#define ADD_BASIC_BLOCK(block, name) \ + do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + } while (0) + +static bool +is_win_platform(AOTCompContext *comp_ctx) +{ + char *triple = LLVMGetTargetMachineTriple(comp_ctx->target_machine); + bool ret; + + bh_assert(triple); + ret = (strstr(triple, "win32") || strstr(triple, "win")) ? true : false; + + LLVMDisposeMessage(triple); + + return ret; +} + +static bool +create_func_return_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + + /* Create function return block if it isn't created */ + if (!func_ctx->func_return_block) { + if (!(func_ctx->func_return_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "func_ret"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + /* Create return IR */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, + func_ctx->func_return_block); + if (!comp_ctx->enable_bound_check) { + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ALREADY_THROWN, + false, NULL, NULL)) { + return false; + } + } + else if (!aot_build_zero_function_ret(comp_ctx, func_ctx, + aot_func_type)) { + return false; + } + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + return true; +} /* Check whether there was exception thrown, if yes, return directly */ static bool check_exception_thrown(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { - AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; LLVMBasicBlockRef block_curr, check_exce_succ; LLVMValueRef value, cmp; + /* Create function return block if it isn't created */ + if (!create_func_return_block(comp_ctx, func_ctx)) + return false; + /* Load the first byte of aot_module_inst->cur_exception, and check whether it is '\0'. If yes, no exception was thrown. */ - if (!(value = LLVMBuildLoad(comp_ctx->builder, func_ctx->cur_exception, - "exce_value")) - || !(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, - value, I8_ZERO, "cmp"))) { + if (!(value = LLVMBuildLoad2(comp_ctx->builder, INT8_TYPE, + func_ctx->cur_exception, "exce_value")) + || !(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, value, I8_ZERO, + "cmp"))) { aot_set_last_error("llvm build icmp failed."); return false; } - /* Add check exection success block */ - if (!(check_exce_succ = LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_exce_succ"))) { + /* Add check exception success block */ + if (!(check_exce_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_exce_succ"))) { aot_set_last_error("llvm add basic block failed."); return false; } @@ -37,512 +100,3157 @@ check_exception_thrown(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) block_curr = LLVMGetInsertBlock(comp_ctx->builder); LLVMMoveBasicBlockAfter(check_exce_succ, block_curr); + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + /* Create condition br */ + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, check_exce_succ, + func_ctx->func_return_block)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_exce_succ); + return true; +} + +/* Check whether there was exception thrown, if yes, return directly */ +static bool +check_call_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef res) +{ + LLVMBasicBlockRef block_curr, check_call_succ; + LLVMValueRef cmp; + /* Create function return block if it isn't created */ - if (!func_ctx->func_return_block) { - if (!(func_ctx->func_return_block = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "func_ret"))) { - aot_set_last_error("llvm add basic block failed."); + if (!create_func_return_block(comp_ctx, func_ctx)) + return false; + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, res, I8_ZERO, + "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + /* Add check exception success block */ + if (!(check_call_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_call_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMMoveBasicBlockAfter(check_call_succ, block_curr); + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + /* Create condition br */ + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, check_call_succ, + func_ctx->func_return_block)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_call_succ); + return true; +} + +static bool +call_aot_invoke_native_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef func_idx, AOTFuncType *aot_func_type, + LLVMTypeRef *param_types, + LLVMValueRef *param_values, uint32 param_count, + uint32 param_cell_num, LLVMTypeRef ret_type, + uint8 wasm_ret_type, LLVMValueRef *p_value_ret, + LLVMValueRef *p_res) +{ + LLVMTypeRef func_type, func_ptr_type, func_param_types[4]; + LLVMTypeRef ret_ptr_type, elem_ptr_type; + LLVMValueRef func, elem_idx, elem_ptr; + LLVMValueRef func_param_values[4], value_ret = NULL, res; + char buf[32], *func_name = "aot_invoke_native"; + uint32 i, cell_num = 0; + + /* prepare function type of aot_invoke_native */ + func_param_types[0] = comp_ctx->exec_env_type; /* exec_env */ + func_param_types[1] = I32_TYPE; /* func_idx */ + func_param_types[2] = I32_TYPE; /* argc */ + func_param_types[3] = INT32_PTR_TYPE; /* argv */ + if (!(func_type = + LLVMFunctionType(INT8_TYPE, func_param_types, 4, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + /* prepare function pointer */ + if (comp_ctx->is_jit_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); return false; } - /* Create return IR */ - LLVMPositionBuilderAtEnd(comp_ctx->builder, func_ctx->func_return_block); - if (aot_func_type->result_count) { - switch (aot_func_type->types[aot_func_type->param_count]) { - case VALUE_TYPE_I32: - LLVMBuildRet(comp_ctx->builder, I32_ZERO); - break; - case VALUE_TYPE_I64: - LLVMBuildRet(comp_ctx->builder, I64_ZERO); - break; - case VALUE_TYPE_F32: - LLVMBuildRet(comp_ctx->builder, F32_ZERO); - break; - case VALUE_TYPE_F64: - LLVMBuildRet(comp_ctx->builder, F64_ZERO); - break; - } + /* JIT mode, call the function directly */ + if (!(func = I64_CONST((uint64)(uintptr_t)llvm_jit_invoke_native)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; } - else { - LLVMBuildRetVoid(comp_ctx->builder); + } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + func_index = aot_get_native_symbol_index(comp_ctx, func_name); + if (func_index < 0) { + return false; + } + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) + && !(func = + LLVMAddFunction(func_ctx->module, func_name, func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; } } - LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); - /* Create condition br */ - if (!LLVMBuildCondBr(comp_ctx->builder, cmp, - check_exce_succ, func_ctx->func_return_block)) { + if (param_cell_num > 64) { + aot_set_last_error("prepare native arguments failed: " + "maximum 64 parameter cell number supported."); + return false; + } + + /* prepare frame_lp */ + for (i = 0; i < param_count; i++) { + if (!(elem_idx = I32_CONST(cell_num)) + || !(elem_ptr_type = LLVMPointerType(param_types[i], 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + return false; + } + + snprintf(buf, sizeof(buf), "%s%d", "elem", i); + if (!(elem_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, I32_TYPE, + func_ctx->argv_buf, &elem_idx, 1, buf)) + || !(elem_ptr = LLVMBuildBitCast(comp_ctx->builder, elem_ptr, + elem_ptr_type, buf))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, param_values[i], + elem_ptr))) { + aot_set_last_error("llvm build store failed."); + return false; + } + LLVMSetAlignment(res, 1); + + cell_num += wasm_value_type_cell_num_internal(aot_func_type->types[i], + comp_ctx->pointer_size); + } + + func_param_values[0] = func_ctx->exec_env; + func_param_values[1] = func_idx; + func_param_values[2] = I32_CONST(param_cell_num); + func_param_values[3] = func_ctx->argv_buf; + + if (!func_param_values[2]) { + aot_set_last_error("llvm create const failed."); + return false; + } + + /* call aot_invoke_native() function */ + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, + func_param_values, 4, "res"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + /* get function return value */ + if (wasm_ret_type != VALUE_TYPE_VOID) { + if (!(ret_ptr_type = LLVMPointerType(ret_type, 0))) { + aot_set_last_error("llvm add pointer type failed."); + return false; + } + + if (!(value_ret = + LLVMBuildBitCast(comp_ctx->builder, func_ctx->argv_buf, + ret_ptr_type, "argv_ret"))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + if (!(*p_value_ret = LLVMBuildLoad2(comp_ctx->builder, ret_type, + value_ret, "value_ret"))) { + aot_set_last_error("llvm build load failed."); + return false; + } + } + + *p_res = res; + return true; +} + +static bool +call_aot_invoke_c_api_native(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 import_func_idx, AOTFuncType *aot_func_type, + LLVMValueRef *params) +{ + LLVMTypeRef int8_ptr_type, param_types[6], ret_type; + LLVMTypeRef value_ptr_type = NULL, value_type = NULL; + LLVMTypeRef func_type, func_ptr_type; + LLVMValueRef param_values[6], res, func, value = NULL, offset; + LLVMValueRef c_api_func_imports, c_api_func_import; + LLVMValueRef c_api_params, c_api_results, value_ret; + LLVMValueRef c_api_param_kind, c_api_param_value; + LLVMValueRef c_api_result_value; + uint32 offset_c_api_func_imports, i; + uint32 offset_param_kind, offset_param_value; + char buf[16]; + + /* `int8 **` type */ + int8_ptr_type = LLVMPointerType(INT8_PTR_TYPE, 0); + if (!int8_ptr_type) { + aot_set_last_error("create llvm pointer type failed"); + return false; + } + + param_types[0] = INT8_PTR_TYPE; /* module_inst */ + param_types[1] = INT8_PTR_TYPE; /* CApiFuncImport *c_api_import */ + param_types[2] = INT8_PTR_TYPE; /* wasm_val_t *params */ + param_types[3] = I32_TYPE; /* uint32 param_count */ + param_types[4] = INT8_PTR_TYPE; /* wasm_val_t *results */ + param_types[5] = I32_TYPE; /* uint32 result_count */ + + ret_type = INT8_TYPE; + + GET_AOT_FUNCTION(wasm_runtime_quick_invoke_c_api_native, 6); + + param_values[0] = func_ctx->aot_inst; + + /* Get module_inst->c_api_func_imports, jit mode WASMModuleInstance is the + * same layout with AOTModuleInstance */ + offset_c_api_func_imports = offsetof(AOTModuleInstance, c_api_func_imports); + offset = I32_CONST(offset_c_api_func_imports); + CHECK_LLVM_CONST(offset); + c_api_func_imports = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, + &offset, 1, "c_api_func_imports_addr"); + c_api_func_imports = + LLVMBuildBitCast(comp_ctx->builder, c_api_func_imports, int8_ptr_type, + "c_api_func_imports_ptr"); + c_api_func_imports = + LLVMBuildLoad2(comp_ctx->builder, INT8_PTR_TYPE, c_api_func_imports, + "c_api_func_imports"); + + /* Get &c_api_func_imports[func_idx], note size of CApiFuncImport + is pointer_size * 3 */ + offset = I32_CONST((unsigned long long)comp_ctx->pointer_size * 3 + * import_func_idx); + CHECK_LLVM_CONST(offset); + c_api_func_import = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, c_api_func_imports, + &offset, 1, "c_api_func_import"); + + param_values[1] = c_api_func_import; + param_values[2] = c_api_params = func_ctx->argv_buf; + param_values[3] = I32_CONST(aot_func_type->param_count); + CHECK_LLVM_CONST(param_values[3]); + + /* Ensure sizeof(wasm_val_t) is 16 bytes */ + offset = I32_CONST(sizeof(wasm_val_t) * aot_func_type->param_count); + c_api_results = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_ctx->argv_buf, + &offset, 1, "results"); + param_values[4] = c_api_results; + + param_values[5] = I32_CONST(aot_func_type->result_count); + CHECK_LLVM_CONST(param_values[5]); + + /* Set each c api param */ + for (i = 0; i < aot_func_type->param_count; i++) { + /* Ensure sizeof(wasm_val_t) is 16 bytes */ + offset_param_kind = sizeof(wasm_val_t) * i; + offset = I32_CONST(offset_param_kind); + CHECK_LLVM_CONST(offset); + c_api_param_kind = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, c_api_params, + &offset, 1, "c_api_param_kind_addr"); + c_api_param_kind = + LLVMBuildBitCast(comp_ctx->builder, c_api_param_kind, INT8_PTR_TYPE, + "c_api_param_kind_ptr"); + + switch (aot_func_type->types[i]) { + case VALUE_TYPE_I32: + value = I8_CONST(WASM_I32); + break; + case VALUE_TYPE_F32: + value = I8_CONST(WASM_F32); + break; + case VALUE_TYPE_I64: + value = I8_CONST(WASM_I64); + break; + case VALUE_TYPE_F64: + value = I8_CONST(WASM_F64); + break; + default: + bh_assert(0); + break; + } + CHECK_LLVM_CONST(value); + + LLVMBuildStore(comp_ctx->builder, value, c_api_param_kind); + + /* Ensure offsetof(wasm_val_t, of) is 8 bytes */ + offset_param_value = offset_param_kind + offsetof(wasm_val_t, of); + offset = I32_CONST(offset_param_value); + CHECK_LLVM_CONST(offset); + c_api_param_value = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, c_api_params, + &offset, 1, "c_api_param_value_addr"); + + switch (aot_func_type->types[i]) { + case VALUE_TYPE_I32: + value_ptr_type = INT32_PTR_TYPE; + break; + case VALUE_TYPE_F32: + value_ptr_type = F32_PTR_TYPE; + break; + case VALUE_TYPE_I64: + value_ptr_type = INT64_PTR_TYPE; + break; + case VALUE_TYPE_F64: + value_ptr_type = F64_PTR_TYPE; + break; + default: + bh_assert(0); + break; + } + + c_api_param_value = + LLVMBuildBitCast(comp_ctx->builder, c_api_param_value, + value_ptr_type, "c_api_param_value_ptr"); + LLVMBuildStore(comp_ctx->builder, params[i], c_api_param_value); + } + + /* Call the function */ + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 6, "call"))) { + aot_set_last_error("LLVM build call failed."); + goto fail; + } + + /* Check whether exception was thrown when executing the function */ + if (comp_ctx->enable_bound_check + && !check_call_return(comp_ctx, func_ctx, res)) { + goto fail; + } + + for (i = 0; i < aot_func_type->result_count; i++) { + /* Ensure sizeof(wasm_val_t) is 16 bytes and + offsetof(wasm_val_t, of) is 8 bytes */ + uint32 offset_result_value = + sizeof(wasm_val_t) * i + offsetof(wasm_val_t, of); + + offset = I32_CONST(offset_result_value); + CHECK_LLVM_CONST(offset); + c_api_result_value = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, c_api_results, + &offset, 1, "c_api_result_value_addr"); + + switch (aot_func_type->types[aot_func_type->param_count + i]) { + case VALUE_TYPE_I32: + value_type = I32_TYPE; + value_ptr_type = INT32_PTR_TYPE; + break; + case VALUE_TYPE_F32: + value_type = F32_TYPE; + value_ptr_type = F32_PTR_TYPE; + break; + case VALUE_TYPE_I64: + value_type = I64_TYPE; + value_ptr_type = INT64_PTR_TYPE; + break; + case VALUE_TYPE_F64: + value_type = F64_TYPE; + value_ptr_type = F64_PTR_TYPE; + break; + default: + bh_assert(0); + break; + } + + c_api_result_value = + LLVMBuildBitCast(comp_ctx->builder, c_api_result_value, + value_ptr_type, "c_api_result_value_ptr"); + snprintf(buf, sizeof(buf), "%s%u", "ret", i); + value_ret = LLVMBuildLoad2(comp_ctx->builder, value_type, + c_api_result_value, buf); + + PUSH(value_ret, aot_func_type->types[aot_func_type->param_count + i]); + } + + return true; +fail: + return false; +} + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 +static bool +call_aot_alloc_frame_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef func_idx) +{ + LLVMValueRef param_values[2], ret_value, value, func; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef frame_alloc_fail, frame_alloc_success; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + + param_types[0] = comp_ctx->exec_env_type; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + +#if WASM_ENABLE_JIT != 0 + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_alloc_frame, 2); + else +#endif + GET_AOT_FUNCTION(aot_alloc_frame, 2); + + param_values[0] = func_ctx->exec_env; + param_values[1] = func_idx; + + if (!(ret_value = + LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 2, "call_aot_alloc_frame"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + if (!(ret_value = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, ret_value, + I8_ZERO, "frame_alloc_ret"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + ADD_BASIC_BLOCK(frame_alloc_fail, "frame_alloc_fail"); + ADD_BASIC_BLOCK(frame_alloc_success, "frame_alloc_success"); + + LLVMMoveBasicBlockAfter(frame_alloc_fail, block_curr); + LLVMMoveBasicBlockAfter(frame_alloc_success, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, ret_value, frame_alloc_success, + frame_alloc_fail)) { aot_set_last_error("llvm build cond br failed."); return false; } - LLVMPositionBuilderAtEnd(comp_ctx->builder, check_exce_succ); + /* If frame alloc failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, frame_alloc_fail); + if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, frame_alloc_success); + return true; + +fail: + return false; } -bool -aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 func_idx, uint8 **p_frame_ip) -{ - uint32 import_func_count = comp_ctx->comp_data->import_func_count; - AOTImportFunc *import_funcs = comp_ctx->comp_data->import_funcs; - uint32 func_count = comp_ctx->func_ctx_count; - AOTFuncContext **func_ctxes = comp_ctx->func_ctxes; - AOTFuncType *func_type; - LLVMTypeRef *param_types = NULL, ret_type, f_type, f_ptr_type; - LLVMValueRef *param_values = NULL, value_ret, func, value, cmp; - LLVMBasicBlockRef check_func_ptr_succ; - int32 i, j = 0, param_count; - void *func_ptr; - uint64 total_size; - bool ret = false; +static bool +alloc_frame_for_aot_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 func_idx) +{ + LLVMValueRef wasm_stack_top_bound = func_ctx->wasm_stack_top_bound; + LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr, + wasm_stack_top; + LLVMValueRef wasm_stack_top_max, offset, cmp; + LLVMValueRef cur_frame, new_frame, prev_frame_ptr; + LLVMValueRef cur_frame_ptr = func_ctx->cur_frame_ptr; + LLVMValueRef func_idx_ptr, func_idx_val, func_inst_ptr, func_inst; + LLVMTypeRef int8_ptr_type; + LLVMBasicBlockRef check_wasm_stack_succ; + uint32 import_func_count = comp_ctx->comp_data->import_func_count; + uint32 param_cell_num = 0, local_cell_num = 0, i; + uint32 max_local_cell_num, max_stack_cell_num; + uint32 all_cell_num, frame_size, frame_size_with_outs_area; + uint32 aot_frame_ptr_num = offsetof(AOTFrame, lp) / sizeof(uintptr_t); + AOTImportFunc *import_funcs = comp_ctx->comp_data->import_funcs; + AOTFuncType *aot_func_type; + AOTFunc *aot_func = NULL; + + /* `int8 **` type */ + int8_ptr_type = LLVMPointerType(INT8_PTR_TYPE, 0); + if (!int8_ptr_type) { + aot_set_last_error("create llvm pointer type failed"); + return false; + } + + /* Get param_cell_num, local_cell_num and max_stack_cell_num */ + if (func_idx < import_func_count) { + aot_func_type = import_funcs[func_idx].func_type; + for (i = 0; i < aot_func_type->param_count; i++) + param_cell_num += wasm_value_type_cell_num_internal( + aot_func_type->types[i], comp_ctx->pointer_size); + max_local_cell_num = param_cell_num > 2 ? param_cell_num : 2; + max_stack_cell_num = 0; + } + else { + aot_func = comp_ctx->comp_data->funcs[func_idx - import_func_count]; + param_cell_num = aot_func->param_cell_num; + local_cell_num = aot_func->local_cell_num; + max_local_cell_num = param_cell_num + local_cell_num; + max_stack_cell_num = aot_func->max_stack_cell_num; + } + + all_cell_num = max_local_cell_num + max_stack_cell_num; + + /* Get size of the frame to allocate and get size with outs_area to + check whether wasm operand stack is overflow */ + if (!comp_ctx->is_jit_mode) { + /* Refer to aot_alloc_frame */ + if (!comp_ctx->enable_gc) { + frame_size = frame_size_with_outs_area = + comp_ctx->pointer_size * aot_frame_ptr_num; + } + else { + frame_size = comp_ctx->pointer_size * aot_frame_ptr_num + + align_uint(all_cell_num * 5, 4); + frame_size_with_outs_area = + frame_size + comp_ctx->pointer_size * aot_frame_ptr_num + + max_stack_cell_num * 4; + } + } + else { + /* Refer to wasm_interp_interp_frame_size */ + if (!comp_ctx->enable_gc) { + frame_size = frame_size_with_outs_area = + offsetof(WASMInterpFrame, lp); + } + else { + frame_size = + offsetof(WASMInterpFrame, lp) + align_uint(all_cell_num * 5, 4); + frame_size_with_outs_area = frame_size + + offsetof(WASMInterpFrame, lp) + + max_stack_cell_num * 4; + } + } + + cur_frame = func_ctx->cur_frame; + + if (!comp_ctx->enable_gc) { + offset = I32_CONST(frame_size); + CHECK_LLVM_CONST(offset); + if (!(wasm_stack_top = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, cur_frame, + &offset, 1, "wasm_stack_top"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + offset = I32_CONST(frame_size * 2); + CHECK_LLVM_CONST(offset); + if (!(wasm_stack_top_max = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, cur_frame, + &offset, 1, "wasm_stack_top_max"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + else { + /* Get exec_env->wasm_stack.top */ + if (!(wasm_stack_top = + LLVMBuildLoad2(comp_ctx->builder, INT8_PTR_TYPE, + wasm_stack_top_ptr, "wasm_stack_top"))) { + aot_set_last_error("load wasm_stack.top failed"); + return false; + } + + /* Check whether wasm operand stack is overflow */ + offset = I32_CONST(frame_size_with_outs_area); + CHECK_LLVM_CONST(offset); + if (!(wasm_stack_top_max = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, wasm_stack_top, &offset, 1, + "wasm_stack_top_max"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + + new_frame = wasm_stack_top; + + if (comp_ctx->call_stack_features.bounds_checks) { + if (!(check_wasm_stack_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, + "check_wasm_stack_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + LLVMMoveBasicBlockAfter(check_wasm_stack_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, + wasm_stack_top_max, wasm_stack_top_bound, + "cmp"))) { + aot_set_last_error("llvm build icmp failed"); + return false; + } + + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_OPERAND_STACK_OVERFLOW, true, cmp, + check_wasm_stack_succ))) { + return false; + } + } + +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + LLVMValueRef wasm_stack_top_new, frame_ref, frame_ref_ptr; + uint32 j, k; + + /* exec_env->wasm_stack.top += frame_size */ + offset = I32_CONST(frame_size); + CHECK_LLVM_CONST(offset); + if (!(wasm_stack_top_new = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, wasm_stack_top, &offset, 1, + "wasm_stack_top_new"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, wasm_stack_top_new, + wasm_stack_top_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + + if (func_idx < import_func_count) { + LLVMValueRef frame_sp, frame_sp_ptr; + + /* Only need to initialize new_frame->sp when it's import function + otherwise they will be committed in AOT code if needed */ + + /* new_frame->sp = new_frame->lp + max_local_cell_num */ + if (!comp_ctx->is_jit_mode) + offset = I32_CONST(comp_ctx->pointer_size * 5); + else + offset = I32_CONST(offsetof(WASMInterpFrame, sp)); + CHECK_LLVM_CONST(offset); + if (!(frame_sp_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, new_frame, &offset, 1, + "frame_sp_addr")) + || !(frame_sp_ptr = + LLVMBuildBitCast(comp_ctx->builder, frame_sp_ptr, + int8_ptr_type, "frame_sp_ptr"))) { + aot_set_last_error("llvm get frame_sp_ptr failed"); + return false; + } + + if (!comp_ctx->is_jit_mode) + offset = I32_CONST(comp_ctx->pointer_size * aot_frame_ptr_num + + max_local_cell_num * sizeof(uint32)); + else + offset = I32_CONST(offsetof(WASMInterpFrame, lp) + + max_local_cell_num * sizeof(uint32)); + CHECK_LLVM_CONST(offset); + if (!(frame_sp = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + new_frame, &offset, 1, + "frame_sp"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, frame_sp, frame_sp_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + + if (!comp_ctx->is_jit_mode) { + /* new_frame->frame_ref = new_frame->lp + max_local_cell_num + + max_stack_cell_num */ + offset = I32_CONST(comp_ctx->pointer_size * 6); + CHECK_LLVM_CONST(offset); + if (!(frame_ref_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, new_frame, &offset, 1, + "frame_ref_addr")) + || !(frame_ref_ptr = + LLVMBuildBitCast(comp_ctx->builder, frame_ref_ptr, + int8_ptr_type, "frame_ref_ptr"))) { + aot_set_last_error("llvm get frame_ref_ptr failed"); + return false; + } + + offset = I32_CONST(comp_ctx->pointer_size * aot_frame_ptr_num + + (max_local_cell_num + max_stack_cell_num) + * sizeof(uint32)); + CHECK_LLVM_CONST(offset); + if (!(frame_ref = LLVMBuildInBoundsGEP2(comp_ctx->builder, + INT8_TYPE, new_frame, + &offset, 1, "frame_ref"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, frame_ref, frame_ref_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + else { + /* Get frame_ref in WASMInterpFrame */ + offset = I32_CONST(offsetof(WASMInterpFrame, lp) + + (max_local_cell_num + max_stack_cell_num) + * sizeof(uint32)); + CHECK_LLVM_CONST(offset); + if (!(frame_ref = LLVMBuildInBoundsGEP2(comp_ctx->builder, + INT8_TYPE, new_frame, + &offset, 1, "frame_ref"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + + /* Initialize frame ref flags for import function only in JIT mode */ + if (func_idx < import_func_count && comp_ctx->is_jit_mode) { + aot_func_type = import_funcs[func_idx].func_type; + for (i = 0, j = 0; i < aot_func_type->param_count; i++) { + if (aot_is_type_gc_reftype(aot_func_type->types[i]) + && !wasm_is_reftype_i31ref(aot_func_type->types[i])) { + for (k = 0; k < comp_ctx->pointer_size / sizeof(uint32); + k++) { + /* frame_ref[j++] = 1 */ + offset = I32_CONST(j); + CHECK_LLVM_CONST(offset); + frame_ref_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, frame_ref, &offset, 1, + "frame_ref_ptr"); + if (!LLVMBuildStore(comp_ctx->builder, I8_ONE, + frame_ref_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + j++; + } + } + else { + uint32 value_type_cell_num = + wasm_value_type_cell_num_internal( + aot_func_type->types[i], comp_ctx->pointer_size); + for (k = 0; k < value_type_cell_num; k++) { + /* frame_ref[j++] = 0 */ + offset = I32_CONST(j); + CHECK_LLVM_CONST(offset); + frame_ref_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, frame_ref, &offset, 1, + "frame_ref_ptr"); + if (!LLVMBuildStore(comp_ctx->builder, I8_ZERO, + frame_ref_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + j++; + } + } + } + + for (; j < 2; j++) { + /* frame_ref[j++] = 0 */ + offset = I32_CONST(j); + CHECK_LLVM_CONST(offset); + frame_ref_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, frame_ref, &offset, 1, + "frame_ref_ptr"); + if (!LLVMBuildStore(comp_ctx->builder, I8_ZERO, + frame_ref_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + /* new_frame->prev_frame = cur_frame */ + if (!(prev_frame_ptr = LLVMBuildBitCast(comp_ctx->builder, new_frame, + int8_ptr_type, "prev_frame_ptr"))) { + aot_set_last_error("llvm build bitcast failed"); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, cur_frame, prev_frame_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + + if (!comp_ctx->is_jit_mode) { + if (comp_ctx->call_stack_features.func_idx) { + /* aot mode: new_frame->func_idx = func_idx */ + func_idx_val = comp_ctx->pointer_size == sizeof(uint64) + ? I64_CONST(func_idx) + : I32_CONST(func_idx); + offset = I32_CONST(comp_ctx->pointer_size); + CHECK_LLVM_CONST(func_idx_val); + CHECK_LLVM_CONST(offset); + if (!(func_idx_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, new_frame, &offset, 1, + "func_idx_addr")) + || !(func_idx_ptr = + LLVMBuildBitCast(comp_ctx->builder, func_idx_ptr, + INTPTR_T_PTR_TYPE, "func_idx_ptr"))) { + aot_set_last_error("llvm get func_idx_ptr failed"); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, func_idx_val, + func_idx_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + } + else { + /* jit mode: frame->function = module_inst->e->functions + func_index */ + LLVMValueRef functions; + uint32 offset_functions = + get_module_inst_extra_offset(comp_ctx) + + offsetof(WASMModuleInstanceExtra, functions); + + offset = I32_CONST(offset_functions); + CHECK_LLVM_CONST(offset); + if (!(functions = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, 1, + "functions_addr"))) { + aot_set_last_error("llvm build inbounds gep failed"); + return false; + } + + if (!(functions = LLVMBuildBitCast(comp_ctx->builder, functions, + int8_ptr_type, "functions_ptr"))) { + aot_set_last_error("llvm build bitcast failed"); + return false; + } + + if (!(functions = LLVMBuildLoad2(comp_ctx->builder, INT8_PTR_TYPE, + functions, "functions"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + + offset = I32_CONST(sizeof(WASMFunctionInstance) * func_idx); + CHECK_LLVM_CONST(offset); + if (!(func_inst = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, functions, + &offset, 1, "func_inst"))) { + aot_set_last_error("llvm build inbounds gep failed"); + return false; + } + + offset = I32_CONST(offsetof(WASMInterpFrame, function)); + CHECK_LLVM_CONST(offset); + if (!(func_inst_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, new_frame, + &offset, 1, "func_inst_addr")) + || !(func_inst_ptr = + LLVMBuildBitCast(comp_ctx->builder, func_inst_ptr, + int8_ptr_type, "func_inst_ptr"))) { + aot_set_last_error("llvm get func_inst_ptr failed"); + return false; + } + + if (!LLVMBuildStore(comp_ctx->builder, func_inst, func_inst_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + + /* No need to initialize new_frame->sp and new_frame->ip_offset + since they will be committed in AOT/JIT code if needed */ + + /* exec_env->cur_frame = new_frame */ + if (!LLVMBuildStore(comp_ctx->builder, new_frame, cur_frame_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + + if (comp_ctx->enable_perf_profiling || comp_ctx->enable_memory_profiling) { + LLVMTypeRef param_types[2], func_type, func_ptr_type; + LLVMValueRef param_values[2], func = NULL, res; + char *func_name = "aot_frame_update_profile_info"; + + /* Call aot_frame_update_profile_info for AOT or + llvm_jit_frame_update_profile_info for JIT */ + + param_types[0] = comp_ctx->exec_env_type; + param_types[1] = INT8_TYPE; + + if (!(func_type = LLVMFunctionType(VOID_TYPE, param_types, 2, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + +#if WASM_ENABLE_JIT != 0 \ + && (WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_MEMORY_PROFILING != 0) + /* JIT mode, call the function directly */ + if (!(func = I64_CONST( + (uint64)(uintptr_t)llvm_jit_frame_update_profile_info)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } +#endif + } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + func_index = aot_get_native_symbol_index(comp_ctx, func_name); + if (func_index < 0) { + return false; + } + if (!(func = + aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) + && !(func = LLVMAddFunction(func_ctx->module, func_name, + func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; + } + } + + param_values[0] = func_ctx->exec_env; + param_values[1] = I8_ONE; + + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, ""))) { + aot_set_last_error("llvm build call failed."); + return false; + } + } + + return true; +fail: + + return false; +} + +static bool +free_frame_for_aot_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef cur_frame_ptr = func_ctx->cur_frame_ptr, cur_frame; + LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr; + LLVMTypeRef int8_ptr_type; + + /* `int8 **` type */ + int8_ptr_type = LLVMPointerType(INT8_PTR_TYPE, 0); + if (!int8_ptr_type) { + aot_set_last_error("create llvm pointer type failed"); + return false; + } + + if (comp_ctx->enable_perf_profiling) { + LLVMTypeRef param_types[2], func_type, func_ptr_type; + LLVMValueRef param_values[2], func = NULL, res; + char *func_name = "aot_frame_update_profile_info"; + + /* call aot_frame_update_profile_info for AOT or + llvm_jit_frame_update_profile_info for JIT */ + + param_types[0] = comp_ctx->exec_env_type; + param_types[1] = INT8_TYPE; + + if (!(func_type = LLVMFunctionType(VOID_TYPE, param_types, 2, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + +#if WASM_ENABLE_JIT != 0 \ + && (WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_MEMORY_PROFILING != 0) + /* JIT mode, call the function directly */ + if (!(func = I64_CONST( + (uint64)(uintptr_t)llvm_jit_frame_update_profile_info)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } +#endif + } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + func_index = aot_get_native_symbol_index(comp_ctx, func_name); + if (func_index < 0) { + return false; + } + if (!(func = + aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) + && !(func = LLVMAddFunction(func_ctx->module, func_name, + func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; + } + } + + param_values[0] = func_ctx->exec_env; + param_values[1] = I8_ZERO; + + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, ""))) { + aot_set_last_error("llvm build call failed."); + return false; + } + } + + if (comp_ctx->enable_gc) { + /* cur_frame = exec_env->cur_frame */ + if (!(cur_frame = LLVMBuildLoad2(comp_ctx->builder, INT8_PTR_TYPE, + cur_frame_ptr, "cur_frame"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + + /* exec_env->wasm_stack.top = cur_frame */ + if (!LLVMBuildStore(comp_ctx->builder, cur_frame, wasm_stack_top_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + } + + /* exec_env->cur_frame = prev_frame */ + if (!LLVMBuildStore(comp_ctx->builder, func_ctx->cur_frame, + cur_frame_ptr)) { + aot_set_last_error("llvm build store failed"); + return false; + } + + return true; +} +#endif /* end of WASM_ENABLE_AOT_STACK_FRAME != 0 */ + +/** + * Check whether the app address and its buffer are inside the linear memory, + * if no, throw exception + */ +static bool +check_app_addr_and_convert(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_str_arg, LLVMValueRef app_addr, + LLVMValueRef buf_size, + LLVMValueRef *p_native_addr_converted) +{ + LLVMTypeRef func_type, func_ptr_type, func_param_types[5]; + LLVMValueRef func, func_param_values[5], res, native_addr_ptr; + char *func_name = "aot_check_app_addr_and_convert"; + + /* prepare function type of aot_check_app_addr_and_convert */ + func_param_types[0] = comp_ctx->aot_inst_type; /* module_inst */ + func_param_types[1] = INT8_TYPE; /* is_str_arg */ + func_param_types[2] = I64_TYPE; /* app_offset */ + func_param_types[3] = I64_TYPE; /* buf_size */ + func_param_types[4] = + comp_ctx->basic_types.int8_pptr_type; /* p_native_addr */ + if (!(func_type = + LLVMFunctionType(INT8_TYPE, func_param_types, 5, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + /* prepare function pointer */ + if (comp_ctx->is_jit_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + /* JIT mode, call the function directly */ + if (!(func = + I64_CONST((uint64)(uintptr_t)jit_check_app_addr_and_convert)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + func_index = aot_get_native_symbol_index(comp_ctx, func_name); + if (func_index < 0) { + return false; + } + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) + && !(func = + LLVMAddFunction(func_ctx->module, func_name, func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; + } + } + + if (!(native_addr_ptr = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->argv_buf, + comp_ctx->basic_types.int8_pptr_type, "p_native_addr"))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + func_param_values[0] = func_ctx->aot_inst; + func_param_values[1] = I8_CONST(is_str_arg); + func_param_values[2] = app_addr; + func_param_values[3] = buf_size; + func_param_values[4] = native_addr_ptr; + + if (!func_param_values[1]) { + aot_set_last_error("llvm create const failed."); + return false; + } + + /* call aot_check_app_addr_and_convert() function */ + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, + func_param_values, 5, "res"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + /* Check whether exception was thrown when executing the function */ + if ((comp_ctx->enable_bound_check || is_win_platform(comp_ctx)) + && !check_call_return(comp_ctx, func_ctx, res)) { + return false; + } + + if (!(*p_native_addr_converted = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, native_addr_ptr, + "native_addr"))) { + aot_set_last_error("llvm build load failed."); + return false; + } + + return true; +} + +static void +aot_estimate_and_record_stack_usage_for_function_call( + const AOTCompContext *comp_ctx, AOTFuncContext *caller_func_ctx, + const AOTFuncType *callee_func_type) +{ + unsigned int size; + + if (!(comp_ctx->enable_stack_bound_check + || comp_ctx->enable_stack_estimation)) { + return; + } + + size = + aot_estimate_stack_usage_for_function_call(comp_ctx, callee_func_type); + /* + * only record the max value, assuming that LLVM emits machine code + * which rewinds the stack before making the next call in the + * function. + */ + if (caller_func_ctx->stack_consumption_for_func_call < size) { + caller_func_ctx->stack_consumption_for_func_call = size; + } +} + +static bool +commit_params_to_frame_of_import_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + AOTFuncType *func_type, + const LLVMValueRef *param_values) +{ + uint32 i, n; + + if (!comp_ctx->call_stack_features.values) { + return true; + } + + for (i = 0, n = 0; i < func_type->param_count; i++, n++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_I32, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_I64: + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_I64, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + n++; + break; + case VALUE_TYPE_F32: + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_F32, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + break; + case VALUE_TYPE_F64: + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_F64, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + n++; + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + if (comp_ctx->enable_ref_types) { + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_I32, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + } +#if WASM_ENABLE_GC != 0 + else if (comp_ctx->enable_gc) { + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_GC_REF, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + if (comp_ctx->pointer_size == sizeof(uint64)) + n++; + } +#endif + else { + bh_assert(0); + } + break; +#if WASM_ENABLE_GC != 0 + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: + case VALUE_TYPE_GC_REF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + if (!aot_frame_store_value( + comp_ctx, param_values[i], VALUE_TYPE_GC_REF, + func_ctx->cur_frame, + offset_of_local_in_outs_area(comp_ctx, n))) + return false; + if (comp_ctx->pointer_size == sizeof(uint64)) + n++; + break; +#endif + default: + bh_assert(0); + break; + } + } + + return true; +} + +bool +aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 func_idx, bool tail_call) +{ + uint32 import_func_count = comp_ctx->comp_data->import_func_count; + AOTImportFunc *import_funcs = comp_ctx->comp_data->import_funcs; + uint32 func_count = comp_ctx->func_ctx_count, param_cell_num = 0; + uint32 ext_ret_cell_num = 0, cell_num = 0; + AOTFuncContext **func_ctxes = comp_ctx->func_ctxes; + AOTFuncType *func_type; + LLVMTypeRef *param_types = NULL, ret_type; + LLVMTypeRef ext_ret_ptr_type; + LLVMValueRef *param_values = NULL, value_ret = NULL, func; + LLVMValueRef import_func_idx, res; + LLVMValueRef ext_ret, ext_ret_ptr, ext_ret_idx; +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + LLVMValueRef func_idx_ref; +#endif + int32 i, j = 0, param_count, result_count, ext_ret_count; + uint64 total_size; + uint8 wasm_ret_type; + uint8 *ext_ret_types = NULL; + const char *signature = NULL; + bool ret = false; + char buf[32]; + bool quick_invoke_c_api_import = false; + + /* Check function index */ + if (func_idx >= import_func_count + func_count) { + aot_set_last_error("Function index out of range."); + return false; + } + + /* Get function type */ + if (func_idx < import_func_count) { + func_type = import_funcs[func_idx].func_type; + signature = import_funcs[func_idx].signature; + } + else { + func_type = + func_ctxes[func_idx - import_func_count]->aot_func->func_type; + } + aot_estimate_and_record_stack_usage_for_function_call(comp_ctx, func_ctx, + func_type); + + /* Commit stack operands, sp and ip */ + if (comp_ctx->aot_frame) { + if (comp_ctx->enable_gc && !aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + /* Commit sp if gc is enabled and commit ip for func call */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, comp_ctx->enable_gc, + true)) + return false; + } + + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) + return false; + } + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (comp_ctx->aux_stack_frame_type) { + if (func_idx < import_func_count + && comp_ctx->call_stack_features.frame_per_function) { + INT_CONST(func_idx_ref, func_idx, I32_TYPE, true); + if (!aot_alloc_frame_per_function_frame_for_aot_func( + comp_ctx, func_ctx, func_idx_ref)) { + return false; + } + } + else if (!comp_ctx->call_stack_features.frame_per_function) { + if (comp_ctx->aux_stack_frame_type + != AOT_STACK_FRAME_TYPE_STANDARD) { + aot_set_last_error("unsupported mode"); + return false; + } + if (!alloc_frame_for_aot_func(comp_ctx, func_ctx, func_idx)) { + return false; + } + } + } +#endif + + /* Get param cell number */ + param_cell_num = func_type->param_cell_num; + + /* Allocate memory for parameters. + * Parameters layout: + * - exec env + * - wasm function's parameters + * - extra results'(except the first one) addresses + */ + param_count = (int32)func_type->param_count; + result_count = (int32)func_type->result_count; + ext_ret_count = result_count > 1 ? result_count - 1 : 0; + total_size = + sizeof(LLVMValueRef) * (uint64)(param_count + 1 + ext_ret_count); + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + return false; + } + + /* First parameter is exec env */ + param_values[j++] = func_ctx->exec_env; + + /* Pop parameters from stack */ + for (i = param_count - 1; i >= 0; i--) + POP(param_values[i + j], func_type->types[i]); + + /* Set parameters for multiple return values, the first return value + is returned by function return value, and the other return values + are returned by function parameters with pointer types */ + if (ext_ret_count > 0) { + ext_ret_types = func_type->types + param_count + 1; + ext_ret_cell_num = wasm_get_cell_num(ext_ret_types, ext_ret_count); + if (ext_ret_cell_num > 64) { + aot_set_last_error("prepare extra results's return " + "address arguments failed: " + "maximum 64 parameter cell number supported."); + goto fail; + } + + for (i = 0; i < ext_ret_count; i++) { + if (!(ext_ret_idx = I32_CONST(cell_num)) + || !(ext_ret_ptr_type = + LLVMPointerType(TO_LLVM_TYPE(ext_ret_types[i]), 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + goto fail; + } + + snprintf(buf, sizeof(buf), "ext_ret%d_ptr", i); + if (!(ext_ret_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, I32_TYPE, func_ctx->argv_buf, + &ext_ret_idx, 1, buf))) { + aot_set_last_error("llvm build GEP failed."); + goto fail; + } + snprintf(buf, sizeof(buf), "ext_ret%d_ptr_cast", i); + if (!(ext_ret_ptr = LLVMBuildBitCast(comp_ctx->builder, ext_ret_ptr, + ext_ret_ptr_type, buf))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } + param_values[param_count + 1 + i] = ext_ret_ptr; + cell_num += wasm_value_type_cell_num_internal( + ext_ret_types[i], comp_ctx->pointer_size); + } + } + + if (func_idx < import_func_count) { + if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_STANDARD + && !commit_params_to_frame_of_import_func( + comp_ctx, func_ctx, func_type, param_values + 1)) { + goto fail; + } + + if (!(import_func_idx = I32_CONST(func_idx))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } + + /* Initialize parameter types of the LLVM function */ + total_size = sizeof(LLVMTypeRef) * (uint64)(param_count + 1); + if (total_size >= UINT32_MAX + || !(param_types = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + j = 0; + param_types[j++] = comp_ctx->exec_env_type; + + for (i = 0; i < param_count; i++, j++) { + param_types[j] = TO_LLVM_TYPE(func_type->types[i]); + + /* If the signature can be gotten, e.g. the signature of the builtin + native libraries, just check the app offset and buf size, and + then convert app offset to native addr and call the native func + directly, no need to call aot_invoke_native to call it */ + if (signature) { + LLVMValueRef native_addr, native_addr_size; + if (signature[i + 1] == '*' || signature[i + 1] == '$') { + param_types[j] = INT8_PTR_TYPE; + } + if (signature[i + 1] == '*') { + if (signature[i + 2] == '~') + native_addr_size = param_values[i + 2]; + else + native_addr_size = I64_CONST(1); + if (!(native_addr_size = LLVMBuildZExtOrBitCast( + comp_ctx->builder, native_addr_size, I64_TYPE, + "native_addr_size_i64"))) { + aot_set_last_error("llvm build zextOrBitCast failed."); + goto fail; + } + if (!(param_values[j] = LLVMBuildZExtOrBitCast( + comp_ctx->builder, param_values[j], I64_TYPE, + "native_addr_i64"))) { + aot_set_last_error("llvm build zextOrBitCast failed."); + goto fail; + } + if (!check_app_addr_and_convert( + comp_ctx, func_ctx, false, param_values[j], + native_addr_size, &native_addr)) { + goto fail; + } + param_values[j] = native_addr; + } + else if (signature[i + 1] == '$') { + native_addr_size = I64_ZERO; + if (!(param_values[j] = LLVMBuildZExtOrBitCast( + comp_ctx->builder, param_values[j], I64_TYPE, + "native_addr_i64"))) { + aot_set_last_error("llvm build zextOrBitCast failed."); + goto fail; + } + if (!check_app_addr_and_convert( + comp_ctx, func_ctx, true, param_values[j], + native_addr_size, &native_addr)) { + goto fail; + } + param_values[j] = native_addr; + } + } + } + + if (func_type->result_count) { + wasm_ret_type = func_type->types[func_type->param_count]; + ret_type = TO_LLVM_TYPE(wasm_ret_type); + } + else { + wasm_ret_type = VALUE_TYPE_VOID; + ret_type = VOID_TYPE; + } + + if (!signature) { + if (comp_ctx->quick_invoke_c_api_import) { + uint32 buf_size_needed = + sizeof(wasm_val_t) * (param_count + result_count); + + /* length of exec_env->argv_buf is 64 */ + if (buf_size_needed < sizeof(uint32) * 64) { + for (i = 0; i < param_count + result_count; i++) { + /* Only support i32/i64/f32/f64 now */ + if (!(func_type->types[i] == VALUE_TYPE_I32 + || func_type->types[i] == VALUE_TYPE_I64 + || func_type->types[i] == VALUE_TYPE_F32 + || func_type->types[i] == VALUE_TYPE_F64)) + break; + } + if (i == param_count + result_count) + quick_invoke_c_api_import = true; + } + } + if (quick_invoke_c_api_import) { + if (!call_aot_invoke_c_api_native(comp_ctx, func_ctx, func_idx, + func_type, param_values + 1)) + goto fail; + } + else { + /* call aot_invoke_native() */ + if (!call_aot_invoke_native_func( + comp_ctx, func_ctx, import_func_idx, func_type, + param_types + 1, param_values + 1, param_count, + param_cell_num, ret_type, wasm_ret_type, &value_ret, + &res)) + goto fail; + /* Check whether there was exception thrown when executing + the function */ + if ((comp_ctx->enable_bound_check || is_win_platform(comp_ctx)) + && !check_call_return(comp_ctx, func_ctx, res)) + goto fail; + } + } + else { /* call native func directly */ + LLVMTypeRef native_func_type, func_ptr_type; + LLVMValueRef func_ptr; + + if (!(native_func_type = LLVMFunctionType( + ret_type, param_types, param_count + 1, false))) { + aot_set_last_error("llvm add function type failed."); + goto fail; + } + + if (!(func_ptr_type = LLVMPointerType(native_func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + goto fail; + } + + /* Load function pointer */ + if (!(func_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->func_ptrs, + &import_func_idx, 1, "native_func_ptr_tmp"))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } + + if (!(func_ptr = LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ptr, "native_func_ptr"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!(func = LLVMBuildBitCast(comp_ctx->builder, func_ptr, + func_ptr_type, "native_func"))) { + aot_set_last_error("llvm bit cast failed."); + goto fail; + } + + /* Call the function */ + if (!(value_ret = LLVMBuildCall2( + comp_ctx->builder, native_func_type, func, param_values, + (uint32)param_count + 1 + ext_ret_count, + (func_type->result_count > 0 ? "call" : "")))) { + aot_set_last_error("LLVM build call failed."); + goto fail; + } + + /* Check whether there was exception thrown when executing + the function */ + if (!check_exception_thrown(comp_ctx, func_ctx)) { + goto fail; + } + } + } + else { +#if LLVM_VERSION_MAJOR >= 14 + LLVMTypeRef llvm_func_type; +#endif + if (comp_ctx->is_indirect_mode) { + LLVMTypeRef func_ptr_type; + + if (!(func_ptr_type = LLVMPointerType( + func_ctxes[func_idx - import_func_count]->func_type, + 0))) { + aot_set_last_error("construct func ptr type failed."); + goto fail; + } + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->func_ptrs, + func_ptr_type, func_idx))) { + goto fail; + } + } + else { + if (func_ctxes[func_idx - import_func_count] == func_ctx) { + /* recursive call */ + func = func_ctx->precheck_func; + } + else { + if (!comp_ctx->is_jit_mode) { + func = + func_ctxes[func_idx - import_func_count]->precheck_func; + } + else { +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) + func = + func_ctxes[func_idx - import_func_count]->precheck_func; +#else + /* JIT tier-up, load func ptr from func_ptrs[func_idx] */ + LLVMValueRef func_ptr, func_idx_const; + LLVMTypeRef func_ptr_type; + + if (!(func_idx_const = I32_CONST(func_idx))) { + aot_set_last_error("llvm build const failed."); + goto fail; + } + + if (!(func_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->func_ptrs, &func_idx_const, 1, + "func_ptr_tmp"))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } + + if (!(func_ptr = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ptr, "func_ptr"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!(func_ptr_type = LLVMPointerType( + func_ctxes[func_idx - import_func_count] + ->func_type, + 0))) { + aot_set_last_error("construct func ptr type failed."); + goto fail; + } + + if (!(func = LLVMBuildBitCast(comp_ctx->builder, func_ptr, + func_ptr_type, + "indirect_func"))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } +#endif /* end of !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) */ + } + } + } + +#if LLVM_VERSION_MAJOR >= 14 + llvm_func_type = func_ctxes[func_idx - import_func_count]->func_type; +#endif + + /* Call the function */ + if (!(value_ret = LLVMBuildCall2( + comp_ctx->builder, llvm_func_type, func, param_values, + (uint32)param_count + 1 + ext_ret_count, + (func_type->result_count > 0 ? "call" : "")))) { + aot_set_last_error("LLVM build call failed."); + goto fail; + } + + if (tail_call) + LLVMSetTailCall(value_ret, true); + + /* Check whether there was exception thrown when executing + the function */ + if (!tail_call + && (comp_ctx->enable_bound_check || is_win_platform(comp_ctx)) + && !check_exception_thrown(comp_ctx, func_ctx)) + goto fail; + } + + if (func_type->result_count > 0 && !quick_invoke_c_api_import) { + /* Push the first result to stack */ + PUSH(value_ret, func_type->types[func_type->param_count]); + /* Load extra result from its address and push to stack */ + for (i = 0; i < ext_ret_count; i++) { + snprintf(buf, sizeof(buf), "func%d_ext_ret%d", func_idx, i); + if (!(ext_ret = LLVMBuildLoad2( + comp_ctx->builder, TO_LLVM_TYPE(ext_ret_types[i]), + param_values[1 + param_count + i], buf))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + LLVMSetAlignment(ext_ret, 4); + PUSH(ext_ret, ext_ret_types[i]); + } + } + +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (comp_ctx->aux_stack_frame_type) { + if (func_idx < import_func_count + && comp_ctx->call_stack_features.frame_per_function) { + if (!aot_free_frame_per_function_frame_for_aot_func(comp_ctx, + func_ctx)) { + goto fail; + } + } + else if (!comp_ctx->call_stack_features.frame_per_function) { + if (comp_ctx->aux_stack_frame_type + != AOT_STACK_FRAME_TYPE_STANDARD) { + aot_set_last_error("unsupported mode"); + } + if (!free_frame_for_aot_func(comp_ctx, func_ctx)) { + goto fail; + } + } + } +#endif + + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, false)) + goto fail; + } + + ret = true; +fail: + if (param_types) + wasm_runtime_free(param_types); + if (param_values) + wasm_runtime_free(param_values); + return ret; +} + +#if WASM_ENABLE_GC != 0 +static LLVMValueRef +call_aot_func_type_is_super_of_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef type_idx1, + LLVMValueRef type_idx2) +{ + LLVMValueRef param_values[3], ret_value, value, func; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + + param_types[0] = comp_ctx->aot_inst_type; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + ret_type = INT8_TYPE; + +#if WASM_ENABLE_JIT != 0 + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_func_type_is_super_of, 3); + else +#endif + GET_AOT_FUNCTION(aot_func_type_is_super_of, 3); + + param_values[0] = func_ctx->aot_inst; + param_values[1] = type_idx1; + param_values[2] = type_idx2; + + if (!(ret_value = + LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 3, "call_aot_func_type_is_super_of"))) { + aot_set_last_error("llvm build call failed."); + return NULL; + } + + if (!(ret_value = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, ret_value, + I8_ZERO, "check_fail"))) { + aot_set_last_error("llvm build icmp failed."); + return NULL; + } + + return ret_value; + +fail: + return NULL; +} +#endif + +static bool +call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + AOTFuncType *aot_func_type, + LLVMValueRef func_type_idx, LLVMValueRef table_idx, + LLVMValueRef table_elem_idx, + LLVMTypeRef *param_types, + LLVMValueRef *param_values, uint32 param_count, + uint32 param_cell_num, uint32 result_count, + uint8 *wasm_ret_types, LLVMValueRef *value_rets, + LLVMValueRef *p_res) +{ + LLVMTypeRef func_type, func_ptr_type, func_param_types[6]; + LLVMTypeRef ret_type, ret_ptr_type, elem_ptr_type; + LLVMValueRef func, ret_idx, ret_ptr, elem_idx, elem_ptr; + LLVMValueRef func_param_values[6], res = NULL; + char buf[32], *func_name = "aot_call_indirect"; + uint32 i, cell_num = 0, ret_cell_num, argv_cell_num; + + /* prepare function type of aot_call_indirect */ + func_param_types[0] = comp_ctx->exec_env_type; /* exec_env */ + func_param_types[1] = I32_TYPE; /* table_idx */ + func_param_types[2] = I32_TYPE; /* table_elem_idx */ + func_param_types[3] = I32_TYPE; /* argc */ + func_param_types[4] = INT32_PTR_TYPE; /* argv */ + if (!(func_type = + LLVMFunctionType(INT8_TYPE, func_param_types, 5, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + /* prepare function pointer */ + if (comp_ctx->is_jit_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + /* JIT mode, call the function directly */ + if (!(func = I64_CONST((uint64)(uintptr_t)llvm_jit_call_indirect)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + func_index = aot_get_native_symbol_index(comp_ctx, func_name); + if (func_index < 0) { + return false; + } + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) + && !(func = + LLVMAddFunction(func_ctx->module, func_name, func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; + } + } + + ret_cell_num = wasm_get_cell_num(wasm_ret_types, result_count); + argv_cell_num = + param_cell_num > ret_cell_num ? param_cell_num : ret_cell_num; + if (argv_cell_num > 64) { + aot_set_last_error("prepare native arguments failed: " + "maximum 64 parameter cell number supported."); + return false; + } + + /* prepare frame_lp */ + for (i = 0; i < param_count; i++) { + if (!(elem_idx = I32_CONST(cell_num)) + || !(elem_ptr_type = LLVMPointerType(param_types[i], 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + return false; + } + + snprintf(buf, sizeof(buf), "%s%d", "elem", i); + if (!(elem_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, I32_TYPE, + func_ctx->argv_buf, &elem_idx, 1, buf)) + || !(elem_ptr = LLVMBuildBitCast(comp_ctx->builder, elem_ptr, + elem_ptr_type, buf))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, param_values[i], + elem_ptr))) { + aot_set_last_error("llvm build store failed."); + return false; + } + LLVMSetAlignment(res, 1); + + cell_num += wasm_value_type_cell_num_internal(aot_func_type->types[i], + comp_ctx->pointer_size); + } + + func_param_values[0] = func_ctx->exec_env; + func_param_values[1] = table_idx; + func_param_values[2] = table_elem_idx; + func_param_values[3] = I32_CONST(param_cell_num); + func_param_values[4] = func_ctx->argv_buf; + + if (!func_param_values[3]) { + aot_set_last_error("llvm create const failed."); + return false; + } + + /* call aot_call_indirect() function */ + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, + func_param_values, 5, "res"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + /* get function result values */ + cell_num = 0; + for (i = 0; i < result_count; i++) { + ret_type = TO_LLVM_TYPE(wasm_ret_types[i]); + if (!(ret_idx = I32_CONST(cell_num)) + || !(ret_ptr_type = LLVMPointerType(ret_type, 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + return false; + } + + snprintf(buf, sizeof(buf), "argv_ret%d", i); + if (!(ret_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, I32_TYPE, + func_ctx->argv_buf, &ret_idx, 1, buf)) + || !(ret_ptr = LLVMBuildBitCast(comp_ctx->builder, ret_ptr, + ret_ptr_type, buf))) { + aot_set_last_error("llvm build GEP or bit cast failed."); + return false; + } + + snprintf(buf, sizeof(buf), "ret%d", i); + if (!(value_rets[i] = + LLVMBuildLoad2(comp_ctx->builder, ret_type, ret_ptr, buf))) { + aot_set_last_error("llvm build load failed."); + return false; + } + LLVMSetAlignment(value_rets[i], 4); + cell_num += wasm_value_type_cell_num_internal(wasm_ret_types[i], + comp_ctx->pointer_size); + } + + *p_res = res; + return true; +} + +bool +aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_idx, uint32 tbl_idx) +{ + AOTFuncType *func_type; + LLVMValueRef tbl_idx_value, elem_idx, func_idx; + LLVMValueRef table_elem_base, table_elem_addr, table_elem; + LLVMValueRef ftype_idx_ptr, ftype_idx, ftype_idx_const; + LLVMValueRef cmp_func_obj, cmp_elem_idx, cmp_func_idx, cmp_ftype_idx; + LLVMValueRef func, func_ptr, table_size_const; + LLVMValueRef ext_ret_offset, ext_ret_ptr, ext_ret, res; + LLVMValueRef *param_values = NULL, *value_rets = NULL; + LLVMValueRef *result_phis = NULL, value_ret, import_func_count; +#if WASM_ENABLE_MEMORY64 != 0 + LLVMValueRef u32_max, u32_cmp_result = NULL; +#endif + LLVMTypeRef *param_types = NULL, ret_type; + LLVMTypeRef llvm_func_type, llvm_func_ptr_type; + LLVMTypeRef ext_ret_ptr_type; + LLVMBasicBlockRef check_func_obj_succ, check_elem_idx_succ, + check_ftype_idx_succ; + LLVMBasicBlockRef check_func_idx_succ, block_return, block_curr; + LLVMBasicBlockRef block_call_import, block_call_non_import; + LLVMValueRef offset; + uint32 total_param_count, func_param_count, func_result_count; + uint32 ext_cell_num, param_cell_num, i, j; + uint8 wasm_ret_type, *wasm_ret_types; + uint64 total_size; + char buf[32]; + bool ret = false; + + /* Check function type index */ + if (type_idx >= comp_ctx->comp_data->type_count) { + aot_set_last_error("function type index out of range"); + return false; + } + + if (!comp_ctx->enable_gc) { + /* Find the equivalent function type whose type index is the smallest: + the callee function's type index is also converted to the smallest + one in wasm loader, so we can just check whether the two type indexes + are equal (the type index of call_indirect opcode and callee func), + we don't need to check whether the whole function types are equal, + including param types and result types. */ + type_idx = wasm_get_smallest_type_idx( + (WASMTypePtr *)comp_ctx->comp_data->types, + comp_ctx->comp_data->type_count, type_idx); + } + else { + /* Call aot_func_type_is_super_of to check whether the func type + provided in the bytecode is a super type of the func type of + the function to call */ + } + + ftype_idx_const = I32_CONST(type_idx); + CHECK_LLVM_CONST(ftype_idx_const); + + func_type = (AOTFuncType *)comp_ctx->comp_data->types[type_idx]; + aot_estimate_and_record_stack_usage_for_function_call(comp_ctx, func_ctx, + func_type); + /* Commit stack operands, sp and ip */ + if (comp_ctx->aot_frame) { + if (comp_ctx->enable_gc && !aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + /* Commit sp if gc is enabled and always commit ip for call_indirect */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, comp_ctx->enable_gc, + true)) + return false; + } + + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) + return false; + } + + func_param_count = func_type->param_count; + func_result_count = func_type->result_count; + + POP_TBL_ELEM_IDX(elem_idx); + + /* get the cur size of the table instance */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, cur_size)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_size_const = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, + 1, "cur_size_i8p"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(table_size_const = + LLVMBuildBitCast(comp_ctx->builder, table_size_const, + INT32_PTR_TYPE, "cur_size_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_size_const = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, + table_size_const, "cur_size"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + +#if WASM_ENABLE_MEMORY64 != 0 + /* Check if elem index >= UINT32_MAX */ + if (IS_TABLE64(tbl_idx)) { + if (!(u32_max = I64_CONST(UINT32_MAX))) { + aot_set_last_error("llvm build const failed"); + goto fail; + } + if (!(u32_cmp_result = + LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx, + u32_max, "cmp_elem_idx_u32_max"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + if (!(elem_idx = LLVMBuildTrunc(comp_ctx->builder, elem_idx, I32_TYPE, + "elem_idx_i32"))) { + aot_set_last_error("llvm build trunc failed."); + goto fail; + } + } +#endif + + /* Check if (uint32)elem index >= table size */ + if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx, + table_size_const, "cmp_elem_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (IS_TABLE64(tbl_idx)) { + if (!(cmp_elem_idx = + LLVMBuildOr(comp_ctx->builder, cmp_elem_idx, u32_cmp_result, + "larger_than_u32_max_or_cur_size"))) { + aot_set_last_error("llvm build or failed."); + goto fail; + } + } +#endif + + /* Throw exception if elem index >= table size or elem index >= UINT32_MAX + */ + if (!(check_elem_idx_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_elem_idx_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_elem_idx_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_UNDEFINED_ELEMENT, true, + cmp_elem_idx, check_elem_idx_succ))) + goto fail; + + /* load data as i32* */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, elems)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_elem_base = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, + 1, "table_elem_base_i8p"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + + /* Load function index */ + if (comp_ctx->enable_gc) { + /* table elem is func_obj when gc is enabled */ + if (!(table_elem_base = + LLVMBuildBitCast(comp_ctx->builder, table_elem_base, + GC_REF_PTR_TYPE, "table_elem_base"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_elem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, GC_REF_TYPE, table_elem_base, &elem_idx, 1, + "table_elem_addr"))) { + HANDLE_FAILURE("LLVMBuildNUWAdd"); + goto fail; + } + + if (!(table_elem = LLVMBuildLoad2(comp_ctx->builder, GC_REF_TYPE, + table_elem_addr, "table_elem"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + /* Check if func object is NULL */ + if (!(cmp_func_obj = LLVMBuildIsNull(comp_ctx->builder, table_elem, + "cmp_func_obj"))) { + aot_set_last_error("llvm build isnull failed."); + goto fail; + } + + /* Throw exception if func object is NULL */ + if (!(check_func_obj_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_func_obj_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_func_obj_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_UNINITIALIZED_ELEMENT, + true, cmp_func_obj, check_func_obj_succ))) + goto fail; + + /* Get the func idx bound of the WASMFuncObject, the offset may be + * different in 32-bit runtime and 64-bit runtime since WASMObjectHeader + * is uintptr_t. Use comp_ctx->pointer_size as the + * offsetof(WASMFuncObject, func_idx_bound) + */ + if (!(offset = I32_CONST(comp_ctx->pointer_size))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(func_idx = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + table_elem, &offset, 1, + "func_idx_bound_i8p"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(func_idx = + LLVMBuildBitCast(comp_ctx->builder, func_idx, INT32_PTR_TYPE, + "func_idx_bound_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(func_idx = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, func_idx, + "func_idx_bound"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + } + else { + if (!(table_elem_base = + LLVMBuildBitCast(comp_ctx->builder, table_elem_base, + INTPTR_T_PTR_TYPE, "table_elem_base"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_elem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INTPTR_T_TYPE, table_elem_base, &elem_idx, + 1, "table_elem_addr"))) { + HANDLE_FAILURE("LLVMBuildNUWAdd"); + goto fail; + } + + if (!(func_idx = LLVMBuildLoad2(comp_ctx->builder, INTPTR_T_TYPE, + table_elem_addr, "func_idx"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!(func_idx = LLVMBuildIntCast2(comp_ctx->builder, func_idx, + I32_TYPE, true, "func_idx_i32"))) { + aot_set_last_error("llvm build int cast failed."); + goto fail; + } + + /* Check if func_idx == -1 */ + if (!(cmp_func_idx = + LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, func_idx, + I32_NEG_ONE, "cmp_func_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + + /* Throw exception if func_idx == -1 */ + if (!(check_func_idx_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_func_idx_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_func_idx_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_UNINITIALIZED_ELEMENT, + true, cmp_func_idx, check_func_idx_succ))) + goto fail; + } + /* Load function type index */ + if (!(ftype_idx_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, I32_TYPE, func_ctx->func_type_indexes, + &func_idx, 1, "ftype_idx_ptr"))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } + + if (!(ftype_idx = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, ftype_idx_ptr, + "ftype_idx"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + if (!(cmp_ftype_idx = call_aot_func_type_is_super_of_func( + comp_ctx, func_ctx, ftype_idx_const, ftype_idx))) { + goto fail; + } + } + else +#endif + { + /* Check if function type index not equal */ + if (!(cmp_ftype_idx = + LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx, + ftype_idx_const, "cmp_ftype_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + } + + /* Throw exception if ftype_idx != ftype_idx_const */ + if (!(check_ftype_idx_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_ftype_idx_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_ftype_idx_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_INVALID_FUNCTION_TYPE_INDEX, true, + cmp_ftype_idx, check_ftype_idx_succ))) + goto fail; + + /* Initialize parameter types of the LLVM function */ + total_param_count = 1 + func_param_count; + + /* Extra function results' addresses (except the first one) are + appended to aot function parameters. */ + if (func_result_count > 1) + total_param_count += func_result_count - 1; + + total_size = sizeof(LLVMTypeRef) * (uint64)total_param_count; + if (total_size >= UINT32_MAX + || !(param_types = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + /* Prepare param types */ + j = 0; + param_types[j++] = comp_ctx->exec_env_type; + for (i = 0; i < func_param_count; i++) + param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); + + for (i = 1; i < func_result_count; i++, j++) { + param_types[j] = TO_LLVM_TYPE(func_type->types[func_param_count + i]); + if (!(param_types[j] = LLVMPointerType(param_types[j], 0))) { + aot_set_last_error("llvm get pointer type failed."); + goto fail; + } + } + + /* Resolve return type of the LLVM function */ + if (func_result_count) { + wasm_ret_type = func_type->types[func_param_count]; + ret_type = TO_LLVM_TYPE(wasm_ret_type); + } + else { + wasm_ret_type = VALUE_TYPE_VOID; + ret_type = VOID_TYPE; + } + + /* Allocate memory for parameters */ + total_size = sizeof(LLVMValueRef) * (uint64)total_param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + /* First parameter is exec env */ + j = 0; + param_values[j++] = func_ctx->exec_env; + + /* Pop parameters from stack */ + for (i = func_param_count - 1; (int32)i >= 0; i--) + POP(param_values[i + j], func_type->types[i]); + + /* Prepare extra parameters */ + ext_cell_num = 0; + for (i = 1; i < func_result_count; i++) { + ext_ret_offset = I32_CONST(ext_cell_num); + CHECK_LLVM_CONST(ext_ret_offset); + + snprintf(buf, sizeof(buf), "ext_ret%d_ptr", i - 1); + if (!(ext_ret_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, I32_TYPE, + func_ctx->argv_buf, + &ext_ret_offset, 1, buf))) { + aot_set_last_error("llvm build GEP failed."); + goto fail; + } + + ext_ret_ptr_type = param_types[func_param_count + i]; + snprintf(buf, sizeof(buf), "ext_ret%d_ptr_cast", i - 1); + if (!(ext_ret_ptr = LLVMBuildBitCast(comp_ctx->builder, ext_ret_ptr, + ext_ret_ptr_type, buf))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } + + param_values[func_param_count + i] = ext_ret_ptr; + ext_cell_num += wasm_value_type_cell_num_internal( + func_type->types[func_param_count + i], comp_ctx->pointer_size); + } + + if (ext_cell_num > 64) { + aot_set_last_error("prepare call-indirect arguments failed: " + "maximum 64 extra cell number supported."); + goto fail; + } + + if (comp_ctx->aux_stack_frame_type + && !comp_ctx->call_stack_features.frame_per_function) { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* TODO: use current frame instead of allocating new frame + for WASM_OP_RETURN_CALL_INDIRECT */ + if (!call_aot_alloc_frame_func(comp_ctx, func_ctx, func_idx)) + goto fail; +#endif + } + + /* Add basic blocks */ + block_call_import = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "call_import"); + block_call_non_import = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "call_non_import"); + block_return = LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, "func_return"); + if (!block_call_import || !block_call_non_import || !block_return) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(block_call_import, + LLVMGetInsertBlock(comp_ctx->builder)); + LLVMMoveBasicBlockAfter(block_call_non_import, block_call_import); + LLVMMoveBasicBlockAfter(block_return, block_call_non_import); + + import_func_count = I32_CONST(comp_ctx->comp_data->import_func_count); + CHECK_LLVM_CONST(import_func_count); + + /* Check if func_idx < import_func_count */ + if (!(cmp_func_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntULT, func_idx, + import_func_count, "cmp_func_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + + /* If func_idx < import_func_count, jump to call import block, + else jump to call non-import block */ + if (!LLVMBuildCondBr(comp_ctx->builder, cmp_func_idx, block_call_import, + block_call_non_import)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Add result phis for return block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_return); + + if (func_result_count > 0) { + total_size = sizeof(LLVMValueRef) * (uint64)func_result_count; + if (total_size >= UINT32_MAX + || !(result_phis = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + memset(result_phis, 0, (uint32)total_size); + for (i = 0; i < func_result_count; i++) { + LLVMTypeRef tmp_type = + TO_LLVM_TYPE(func_type->types[func_param_count + i]); + if (!(result_phis[i] = + LLVMBuildPhi(comp_ctx->builder, tmp_type, "phi"))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + } + } + + /* Translate call import block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_import); + + if (comp_ctx->aot_frame && comp_ctx->call_stack_features.frame_per_function + && !aot_alloc_frame_per_function_frame_for_aot_func(comp_ctx, func_ctx, + func_idx)) { + goto fail; + } + + if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_STANDARD + && !commit_params_to_frame_of_import_func(comp_ctx, func_ctx, func_type, + param_values + 1)) { + goto fail; + } + + /* Allocate memory for result values */ + if (func_result_count > 0) { + total_size = sizeof(LLVMValueRef) * (uint64)func_result_count; + if (total_size >= UINT32_MAX + || !(value_rets = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + memset(value_rets, 0, (uint32)total_size); + } + + param_cell_num = func_type->param_cell_num; + wasm_ret_types = func_type->types + func_type->param_count; + + tbl_idx_value = I32_CONST(tbl_idx); + if (!tbl_idx_value) { + aot_set_last_error("llvm create const failed."); + goto fail; + } + + if (!call_aot_call_indirect_func( + comp_ctx, func_ctx, func_type, ftype_idx, tbl_idx_value, elem_idx, + param_types + 1, param_values + 1, func_param_count, param_cell_num, + func_result_count, wasm_ret_types, value_rets, &res)) + goto fail; + + /* Check whether exception was thrown when executing the function */ + if ((comp_ctx->enable_bound_check || is_win_platform(comp_ctx)) + && !check_call_return(comp_ctx, func_ctx, res)) + goto fail; + + if (comp_ctx->aot_frame && comp_ctx->call_stack_features.frame_per_function + && !aot_free_frame_per_function_frame_for_aot_func(comp_ctx, + func_ctx)) { + goto fail; + } + + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + for (i = 0; i < func_result_count; i++) { + LLVMAddIncoming(result_phis[i], &value_rets[i], &block_curr, 1); + } + + if (!LLVMBuildBr(comp_ctx->builder, block_return)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Translate call non-import block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_non_import); + + /* Load function pointer */ + if (!(func_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->func_ptrs, &func_idx, 1, + "func_ptr_tmp"))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } + + if (!(func_ptr = LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, func_ptr, + "func_ptr"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } - /* Check function index */ - if (func_idx >= import_func_count + func_count) { - aot_set_last_error("Function index out of range."); - return false; + if (!(llvm_func_type = + LLVMFunctionType(ret_type, param_types, total_param_count, false)) + || !(llvm_func_ptr_type = LLVMPointerType(llvm_func_type, 0))) { + aot_set_last_error("llvm add function type failed."); + goto fail; } - /* Get function type */ - if (func_idx < import_func_count) - func_type = import_funcs[func_idx].func_type; - else - func_type = func_ctxes[func_idx - import_func_count]-> - aot_func->func_type; + if (!(func = LLVMBuildBitCast(comp_ctx->builder, func_ptr, + llvm_func_ptr_type, "indirect_func"))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } - /* Allocate memory for parameters */ - param_count = (int32)func_type->param_count; - total_size = sizeof(LLVMValueRef) * (uint64)(param_count + 1); - if (total_size >= UINT32_MAX - || !(param_values = wasm_malloc((uint32)total_size))) { - aot_set_last_error("Allocate memory failed."); - return false; + if (!(value_ret = LLVMBuildCall2(comp_ctx->builder, llvm_func_type, func, + param_values, total_param_count, + func_result_count > 0 ? "ret" : ""))) { + aot_set_last_error("llvm build call failed."); + goto fail; } - /* First parameter is exec env */ - param_values[j++] = func_ctx->exec_env; + /* Check whether exception was thrown when executing the function */ + if ((comp_ctx->enable_bound_check || is_win_platform(comp_ctx)) + && !check_exception_thrown(comp_ctx, func_ctx)) + goto fail; - /* Pop parameters from stack */ - for (i = param_count - 1; i >= 0; i--) - POP(param_values[i + j], func_type->types[i]); + if (func_result_count > 0) { + block_curr = LLVMGetInsertBlock(comp_ctx->builder); - if (func_idx < import_func_count) { - /* Get function pointer linked */ - func_ptr = import_funcs[func_idx].func_ptr_linked; + /* Push the first result to stack */ + LLVMAddIncoming(result_phis[0], &value_ret, &block_curr, 1); - /* Initialize parameter types of the LLVM function */ - total_size = sizeof(LLVMTypeRef) * (uint64)(param_count + 1); - if (total_size >= UINT32_MAX - || !(param_types = wasm_malloc((uint32)total_size))) { - aot_set_last_error("Allocate memory failed."); - goto fail; + /* Load extra result from its address and push to stack */ + for (i = 1; i < func_result_count; i++) { + ret_type = TO_LLVM_TYPE(func_type->types[func_param_count + i]); + snprintf(buf, sizeof(buf), "ext_ret%d", i - 1); + if (!(ext_ret = LLVMBuildLoad2(comp_ctx->builder, ret_type, + param_values[func_param_count + i], + buf))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + LLVMSetAlignment(ext_ret, 4); + LLVMAddIncoming(result_phis[i], &ext_ret, &block_curr, 1); } + } - j = 0; - param_types[j++] = comp_ctx->exec_env_type; + if (!LLVMBuildBr(comp_ctx->builder, block_return)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } - for (i = 0; i < param_count; i++) - param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); + /* Translate function return block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_return); - /* Resolve return type of the LLVM function */ - if (func_type->result_count) - ret_type = TO_LLVM_TYPE(func_type->types[func_type->param_count]); - else - ret_type = VOID_TYPE; + for (i = 0; i < func_result_count; i++) { + PUSH(result_phis[i], func_type->types[func_param_count + i]); + } - /* Resolve function prototype */ - if (!(f_type = LLVMFunctionType(ret_type, param_types, - (uint32)param_count + 1, false)) - || !(f_ptr_type = LLVMPointerType(f_type, 0))) { - aot_set_last_error("create LLVM function type failed."); + if (comp_ctx->aux_stack_frame_type + && !comp_ctx->call_stack_features.frame_per_function) { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (!free_frame_for_aot_func(comp_ctx, func_ctx)) goto fail; - } +#endif + } - if (comp_ctx->is_jit_mode) { - if (!func_ptr) { - /* The import function isn't linked, throw exception - when calling it. */ - if (!aot_emit_exception(comp_ctx, func_ctx, - EXCE_CALL_UNLINKED_IMPORT_FUNC, - false, NULL, NULL)) - goto fail; - ret = aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); - goto fail; - } + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, false)) + goto fail; + } - /* JIT mode, call the linked function directly */ - if (!(value = I64_CONST((uint64)(uintptr_t)func_ptr)) - || !(func = LLVMConstIntToPtr(value, f_ptr_type))) { - aot_set_last_error("create LLVM value failed."); - goto fail; - } - } - else { - /* Load function pointer */ - if (!(value = I32_CONST(func_idx)) - || !(func_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->func_ptrs, - &value, 1, "func_ptr"))) { - aot_set_last_error("llvm build inbounds gep failed."); - goto fail; - } + ret = true; - if (!(func = LLVMBuildLoad(comp_ctx->builder, func_ptr, "func_tmp"))) { - aot_set_last_error("llvm build load failed."); - goto fail; - } +fail: + if (param_values) + wasm_runtime_free(param_values); + if (param_types) + wasm_runtime_free(param_types); + if (value_rets) + wasm_runtime_free(value_rets); + if (result_phis) + wasm_runtime_free(result_phis); + return ret; +} - /* Check whether import function is NULL */ - if (!(cmp = LLVMBuildIsNull(comp_ctx->builder, func, "is_func_null"))) { - aot_set_last_error("llvm build icmp failed."); - goto fail; - } +bool +aot_compile_op_ref_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + if (comp_ctx->enable_gc) + PUSH_GC_REF(GC_REF_NULL); + else + PUSH_I32(REF_NULL); - /* Throw exception if import function is NULL */ - if (!(check_func_ptr_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_func_ptr_succ"))) { - aot_set_last_error("llvm add basic block failed."); - goto fail; - } + return true; +fail: + return false; +} - LLVMMoveBasicBlockAfter(check_func_ptr_succ, - LLVMGetInsertBlock(comp_ctx->builder)); +bool +aot_compile_op_ref_is_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef lhs = NULL, res; - if (!(aot_emit_exception(comp_ctx, func_ctx, - EXCE_CALL_UNLINKED_IMPORT_FUNC, - true, cmp, check_func_ptr_succ))) - goto fail; + if (comp_ctx->enable_gc) { + POP_GC_REF(lhs); - if (!(func = LLVMBuildBitCast(comp_ctx->builder, func, - f_ptr_type, "func"))) { - aot_set_last_error("create LLVM value failed."); - goto fail; - } + if (!(res = LLVMBuildIsNull(comp_ctx->builder, lhs, "lhs is null"))) { + HANDLE_FAILURE("LLVMBuildIsNull"); + goto fail; } } else { - func = func_ctxes[func_idx - import_func_count]->func; + POP_I32(lhs); + + if (!(res = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, lhs, REF_NULL, + "cmp_w_null"))) { + HANDLE_FAILURE("LLVMBuildICmp"); + goto fail; + } } - /* Call the function */ - if (!(value_ret = LLVMBuildCall(comp_ctx->builder, func, - param_values, (uint32)param_count + 1, - (func_type->result_count > 0 - ? "call" : "")))) { - aot_set_last_error("LLVM build call failed."); + if (!(res = LLVMBuildZExt(comp_ctx->builder, res, I32_TYPE, "r_i"))) { + HANDLE_FAILURE("LLVMBuildZExt"); goto fail; } - /* Set calling convention for the call with the func's calling convention */ - LLVMSetInstructionCallConv(value_ret, LLVMGetFunctionCallConv(func)); + PUSH_I32(res); - if (func_type->result_count > 0) - PUSH(value_ret, func_type->types[func_type->param_count]); + return true; +fail: + return false; +} + +bool +aot_compile_op_ref_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 func_idx) +{ + LLVMValueRef ref_idx; +#if WASM_ENABLE_GC != 0 + LLVMValueRef gc_obj; +#endif - /* Check whether there was exception thrown when executing the function */ - if (!check_exception_thrown(comp_ctx, func_ctx)) + if (!(ref_idx = I32_CONST(func_idx))) { + HANDLE_FAILURE("LLVMConstInt"); goto fail; + } - ret = true; +#if WASM_ENABLE_GC != 0 + if (comp_ctx->enable_gc) { + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + /* Commit sp and ip if gc is enabled */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + if (!aot_call_aot_create_func_obj(comp_ctx, func_ctx, ref_idx, + &gc_obj)) { + goto fail; + } + + PUSH_GC_REF(gc_obj); + } + else +#endif + { + PUSH_I32(ref_idx); + } + + return true; fail: - if (param_types) - wasm_free(param_types); - if (param_values) - wasm_free(param_values); - return ret; + return false; } +#if WASM_ENABLE_GC != 0 bool -aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 type_idx) +aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_idx, bool tail_call) { AOTFuncType *func_type; - LLVMValueRef elem_idx, table_elem, func_idx, ftype_idx_ptr, ftype_idx; - LLVMValueRef cmp_elem_idx, cmp_func_idx, is_ftype_match, is_ftype_mismatch; - LLVMValueRef func, func_ptr, func_const, table_size_const, cmp_func_ptr; - LLVMValueRef *param_values = NULL, param_values_tmp[3], value_ret; - LLVMTypeRef *param_types = NULL, param_types_tmp[3], ret_type, - f_type, f_ptr_type; - LLVMBasicBlockRef check_elem_idx_succ, check_ftype_idx_succ; - LLVMBasicBlockRef check_func_idx_succ, check_func_ptr_succ; - int32 i, j = 0, param_count; + LLVMValueRef func_obj, func_idx; + LLVMValueRef cmp_func_obj, cmp_func_idx; + LLVMValueRef func, func_ptr; + LLVMValueRef ext_ret_offset, ext_ret_ptr, ext_ret, res; + LLVMValueRef *param_values = NULL; + LLVMValueRef *result_phis = NULL, value_ret, import_func_count; + LLVMTypeRef *param_types = NULL, ret_type; + LLVMTypeRef llvm_func_type, llvm_func_ptr_type; + LLVMTypeRef ext_ret_ptr_type; + LLVMBasicBlockRef check_func_obj_succ, block_return, block_curr; + LLVMBasicBlockRef block_call_import, block_call_non_import; + LLVMValueRef offset; + uint32 total_param_count, func_param_count, func_result_count; + uint32 ext_cell_num, param_cell_num, i, j; + uint8 wasm_ret_type; uint64 total_size; - bool ret; - char *func_name = "aot_is_wasm_type_equal"; + char buf[32]; + bool ret = false; /* Check function type index */ - if (type_idx >= comp_ctx->comp_data->func_type_count) { - aot_set_last_error("type index is overflow"); - return false; - } + bh_assert(type_idx < comp_ctx->comp_data->type_count); + + func_type = (AOTFuncType *)comp_ctx->comp_data->types[type_idx]; + aot_estimate_and_record_stack_usage_for_function_call(comp_ctx, func_ctx, + func_type); + func_param_count = func_type->param_count; + func_result_count = func_type->result_count; + param_cell_num = func_type->param_cell_num; + + /* Commit stack operands, sp and ip to aot frame */ + if (comp_ctx->aot_frame) { + /* Note that GC is enabled, no need to check it again */ + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; - func_type = comp_ctx->comp_data->func_types[type_idx]; + /* Commit sp if gc is enabled and always commit ip for call_ref */ + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + } - POP_I32(elem_idx); + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, true)) + return false; + } - table_size_const = I32_CONST(comp_ctx->comp_data->table_size); - CHECK_LLVM_CONST(table_size_const); + POP_GC_REF(func_obj); - /* Check if (uint32)elem index >= table size */ - if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, - elem_idx, table_size_const, - "cmp_elem_idx"))) { - aot_set_last_error("llvm build icmp failed."); + /* Check if func object is NULL */ + if (!(cmp_func_obj = + LLVMBuildIsNull(comp_ctx->builder, func_obj, "cmp_func_obj"))) { + aot_set_last_error("llvm build isnull failed."); goto fail; } - /* Throw exception if elem index >= table size */ - if (!(check_elem_idx_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_elem_idx_succ"))) { + /* Throw exception if func object is NULL */ + if (!(check_func_obj_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_func_obj_succ"))) { aot_set_last_error("llvm add basic block failed."); goto fail; } - LLVMMoveBasicBlockAfter(check_elem_idx_succ, + LLVMMoveBasicBlockAfter(check_func_obj_succ, LLVMGetInsertBlock(comp_ctx->builder)); - if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_UNDEFINED_ELEMENT, - true, cmp_elem_idx, check_elem_idx_succ))) + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_FUNC_OBJ, true, + cmp_func_obj, check_func_obj_succ))) goto fail; - /* Load function index */ - if (!(table_elem = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->table_base, - &elem_idx, 1, "table_elem"))) { - aot_set_last_error("llvm build add failed."); + /* Get the func idx bound of the WASMFuncObject, the offset may be + * different in 32-bit runtime and 64-bit runtime since WASMObjectHeader + * is uintptr_t. Use comp_ctx->pointer_size as the + * offsetof(WASMFuncObject, func_idx_bound) */ + if (!(offset = I32_CONST(comp_ctx->pointer_size))) { + HANDLE_FAILURE("LLVMConstInt"); goto fail; } - if (!(func_idx = LLVMBuildLoad(comp_ctx->builder, table_elem, "func_idx"))) { - aot_set_last_error("llvm build load failed."); + if (!(func_idx = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_obj, + &offset, 1, "func_idx_bound_i8p"))) { + HANDLE_FAILURE("LLVMBuildGEP"); goto fail; } - /* Check if func_idx == -1 */ - if (!(cmp_func_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, - func_idx, I32_NEG_ONE, - "cmp_func_idx"))) { - aot_set_last_error("llvm build icmp failed."); + if (!(func_idx = LLVMBuildBitCast(comp_ctx->builder, func_idx, + INT32_PTR_TYPE, "func_idx_bound_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); goto fail; } - /* Throw exception if func_idx == -1 */ - if (!(check_func_idx_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_func_idx_succ"))) { - aot_set_last_error("llvm add basic block failed."); + if (!(func_idx = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, func_idx, + "func_idx_bound"))) { + HANDLE_FAILURE("LLVMBuildLoad"); goto fail; } - LLVMMoveBasicBlockAfter(check_func_idx_succ, - LLVMGetInsertBlock(comp_ctx->builder)); + /* Initialize parameter types of the LLVM function */ + total_param_count = 1 + func_param_count; - if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_UNINITIALIZED_ELEMENT, - true, cmp_func_idx, check_func_idx_succ))) - goto fail; + /* Extra function results' addresses (except the first one) are + appended to aot function parameters. */ + if (func_result_count > 1) + total_param_count += func_result_count - 1; - /* Load function type index */ - if (!(ftype_idx_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->func_type_indexes, - &func_idx, 1, - "ftype_idx_ptr"))) { - aot_set_last_error("llvm build inbounds gep failed."); + total_size = sizeof(LLVMTypeRef) * (uint64)total_param_count; + if (total_size >= UINT32_MAX + || !(param_types = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); goto fail; } - if (!(ftype_idx = LLVMBuildLoad(comp_ctx->builder, ftype_idx_ptr, - "ftype_idx"))) { - aot_set_last_error("llvm build load failed."); - goto fail; + /* Prepare param types */ + j = 0; + param_types[j++] = comp_ctx->exec_env_type; + for (i = 0; i < func_param_count; i++) + param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); + + for (i = 1; i < func_result_count; i++, j++) { + param_types[j] = TO_LLVM_TYPE(func_type->types[func_param_count + i]); + if (!(param_types[j] = LLVMPointerType(param_types[j], 0))) { + aot_set_last_error("llvm get pointer type failed."); + goto fail; + } } - /* Call aot_is_type_equal() to check whether function type match */ - param_types_tmp[0] = INT8_PTR_TYPE; - param_types_tmp[1] = I32_TYPE; - param_types_tmp[2] = I32_TYPE; - ret_type = INT8_TYPE; + /* Resolve return type of the LLVM function */ + if (func_result_count) { + wasm_ret_type = func_type->types[func_param_count]; + ret_type = TO_LLVM_TYPE(wasm_ret_type); + } + else { + wasm_ret_type = VALUE_TYPE_VOID; + ret_type = VOID_TYPE; + } - /* Create function type */ - if (!(f_type = LLVMFunctionType(ret_type, param_types_tmp, - 3, false))) { - aot_set_last_error("create LLVM function type failed."); + /* Allocate memory for parameters */ + total_size = sizeof(LLVMValueRef) * (uint64)total_param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); goto fail; } - if (comp_ctx->is_jit_mode) { - /* Create function type */ - if (!(f_ptr_type = LLVMPointerType(f_type, 0))) { - aot_set_last_error("create LLVM function type failed."); + /* First parameter is exec env */ + j = 0; + param_values[j++] = func_ctx->exec_env; + + /* Pop parameters from stack */ + for (i = func_param_count - 1; (int32)i >= 0; i--) + POP(param_values[i + j], func_type->types[i]); + + /* Prepare extra parameters */ + ext_cell_num = 0; + for (i = 1; i < func_result_count; i++) { + ext_ret_offset = I32_CONST(ext_cell_num); + CHECK_LLVM_CONST(ext_ret_offset); + + snprintf(buf, sizeof(buf), "ext_ret%d_ptr", i - 1); + if (!(ext_ret_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, I32_TYPE, + func_ctx->argv_buf, + &ext_ret_offset, 1, buf))) { + aot_set_last_error("llvm build GEP failed."); goto fail; } - /* Create LLVM function with const function pointer */ - if (!(func_const = - I64_CONST((uint64)(uintptr_t)aot_is_wasm_type_equal)) - || !(func = LLVMConstIntToPtr(func_const, f_ptr_type))) { - aot_set_last_error("create LLVM value failed."); + + ext_ret_ptr_type = param_types[func_param_count + i]; + snprintf(buf, sizeof(buf), "ext_ret%d_ptr_cast", i - 1); + if (!(ext_ret_ptr = LLVMBuildBitCast(comp_ctx->builder, ext_ret_ptr, + ext_ret_ptr_type, buf))) { + aot_set_last_error("llvm build bit cast failed."); goto fail; } + + param_values[func_param_count + i] = ext_ret_ptr; + ext_cell_num += wasm_value_type_cell_num_internal( + func_type->types[func_param_count + i], comp_ctx->pointer_size); } - else { - /* Create LLVM function with external function pointer */ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) - && !(func = LLVMAddFunction(comp_ctx->module, func_name, f_type))) { - aot_set_last_error("add LLVM function failed."); + + if (ext_cell_num > 64) { + aot_set_last_error("prepare call-indirect arguments failed: " + "maximum 64 extra cell number supported."); + goto fail; + } + + if (comp_ctx->aux_stack_frame_type + && !comp_ctx->call_stack_features.frame_per_function) { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + /* TODO: use current frame instead of allocating new frame + for WASM_OP_RETURN_CALL_REF */ + if (!call_aot_alloc_frame_func(comp_ctx, func_ctx, func_idx)) goto fail; - } +#endif + } + + /* Add basic blocks */ + block_call_import = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "call_import"); + block_call_non_import = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "call_non_import"); + block_return = LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, "func_return"); + if (!block_call_import || !block_call_non_import || !block_return) { + aot_set_last_error("llvm add basic block failed."); + goto fail; } - /* Call the aot_is_type_equal() function */ - param_values_tmp[0] = func_ctx->aot_inst; - param_values_tmp[1] = I32_CONST(type_idx); - param_values_tmp[2] = ftype_idx; + LLVMMoveBasicBlockAfter(block_call_import, + LLVMGetInsertBlock(comp_ctx->builder)); + LLVMMoveBasicBlockAfter(block_call_non_import, block_call_import); + LLVMMoveBasicBlockAfter(block_return, block_call_non_import); - CHECK_LLVM_CONST(param_values_tmp[1]); + import_func_count = I32_CONST(comp_ctx->comp_data->import_func_count); + CHECK_LLVM_CONST(import_func_count); - if (!(is_ftype_match = LLVMBuildCall(comp_ctx->builder, func, - param_values_tmp, 3, - "is_ftype_match"))) { + /* Check if func_idx < import_func_count */ + if (!(cmp_func_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntULT, func_idx, + import_func_count, "cmp_func_idx"))) { aot_set_last_error("llvm build icmp failed."); goto fail; } - if (!(is_ftype_mismatch = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, - is_ftype_match, I8_ZERO, - "is_ftype_mismatch"))) { - aot_set_last_error("llvm build icmp failed."); + /* If func_idx < import_func_count, jump to call import block, + else jump to call non-import block */ + if (!LLVMBuildCondBr(comp_ctx->builder, cmp_func_idx, block_call_import, + block_call_non_import)) { + aot_set_last_error("llvm build cond br failed."); goto fail; } - if (!(check_ftype_idx_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_ftype_idx_success"))) { - aot_set_last_error("llvm add basic block failed."); + /* Add result phis for return block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_return); + + if (func_result_count > 0) { + total_size = sizeof(LLVMValueRef) * (uint64)func_result_count; + if (total_size >= UINT32_MAX + || !(result_phis = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + memset(result_phis, 0, (uint32)total_size); + for (i = 0; i < func_result_count; i++) { + LLVMTypeRef tmp_type = + TO_LLVM_TYPE(func_type->types[func_param_count + i]); + if (!(result_phis[i] = + LLVMBuildPhi(comp_ctx->builder, tmp_type, "phi"))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + } + } + + /* Translate call import block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_import); + + if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_STANDARD + && !commit_params_to_frame_of_import_func(comp_ctx, func_ctx, func_type, + param_values + 1)) { goto fail; } - LLVMMoveBasicBlockAfter(check_ftype_idx_succ, - LLVMGetInsertBlock(comp_ctx->builder)); + /* Similar to opcode call_indirect, but for opcode ref.func needs to call + * aot_invoke_native_func instead */ + if (!call_aot_invoke_native_func(comp_ctx, func_ctx, func_idx, func_type, + param_types + 1, param_values + 1, + func_param_count, param_cell_num, ret_type, + wasm_ret_type, &value_ret, &res)) + goto fail; + + /* Check whether exception was thrown when executing the function */ + if (comp_ctx->enable_bound_check + && !check_call_return(comp_ctx, func_ctx, res)) + goto fail; + + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + + /* Get function return values, for aot_invoke_native_func, the extra ret + * values are put into param's array */ + if (func_result_count > 0) { + /* Push the first result to stack */ + LLVMAddIncoming(result_phis[0], &value_ret, &block_curr, 1); + + /* Load extra result from its address and push to stack */ + for (i = 1; i < func_result_count; i++) { + ret_type = TO_LLVM_TYPE(func_type->types[func_param_count + i]); + snprintf(buf, sizeof(buf), "ext_ret%d", i - 1); + if (!(ext_ret = LLVMBuildLoad2(comp_ctx->builder, ret_type, + param_values[func_param_count + i], + buf))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + LLVMSetAlignment(ext_ret, 4); + LLVMAddIncoming(result_phis[i], &ext_ret, &block_curr, 1); + } + } - if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INVALID_FUNCTION_TYPE_INDEX, - true, is_ftype_mismatch, check_ftype_idx_succ))) + if (!LLVMBuildBr(comp_ctx->builder, block_return)) { + aot_set_last_error("llvm build br failed."); goto fail; + } + + /* Translate call non-import block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_non_import); /* Load function pointer */ - if (!(func_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->func_ptrs, - &func_idx, 1, "func_ptr"))) { + if (!(func_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->func_ptrs, &func_idx, 1, + "func_ptr_tmp"))) { aot_set_last_error("llvm build inbounds gep failed."); goto fail; } - if (!(func = LLVMBuildLoad(comp_ctx->builder, func_ptr, "func_tmp"))) { + if (!(func_ptr = LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, func_ptr, + "func_ptr"))) { aot_set_last_error("llvm build load failed."); goto fail; } - /* Check whether import function is NULL */ - if (!(cmp_func_ptr = LLVMBuildIsNull(comp_ctx->builder, func, "is_func_null"))) { - aot_set_last_error("llvm build is null failed."); + if (!(llvm_func_type = + LLVMFunctionType(ret_type, param_types, total_param_count, false)) + || !(llvm_func_ptr_type = LLVMPointerType(llvm_func_type, 0))) { + aot_set_last_error("llvm add function type failed."); goto fail; } - /* Throw exception if import function is NULL */ - if (!(check_func_ptr_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_func_ptr_succ"))) { - aot_set_last_error("llvm add basic block failed."); + if (!(func = LLVMBuildBitCast(comp_ctx->builder, func_ptr, + llvm_func_ptr_type, "indirect_func"))) { + aot_set_last_error("llvm build bit cast failed."); goto fail; } - LLVMMoveBasicBlockAfter(check_func_ptr_succ, - LLVMGetInsertBlock(comp_ctx->builder)); - - if (!(aot_emit_exception(comp_ctx, func_ctx, - EXCE_CALL_UNLINKED_IMPORT_FUNC, - true, cmp_func_ptr, check_func_ptr_succ))) - goto fail; - - /* Initialize parameter types of the LLVM function */ - param_count = (int32)func_type->param_count; - total_size = sizeof(LLVMTypeRef) * (uint64)(param_count + 1); - if (total_size >= UINT32_MAX - || !(param_types = wasm_malloc((uint32)total_size))) { - aot_set_last_error("Allocate memory failed."); + if (!(value_ret = LLVMBuildCall2(comp_ctx->builder, llvm_func_type, func, + param_values, total_param_count, + func_result_count > 0 ? "ret" : ""))) { + aot_set_last_error("llvm build call failed."); goto fail; } - j = 0; - param_types[j++] = comp_ctx->exec_env_type; - for (i = 0; i < param_count; i++) - param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); + /* Set calling convention for the call with the func's calling + convention */ + LLVMSetInstructionCallConv(value_ret, LLVMGetFunctionCallConv(func)); - /* Resolve return type of the LLVM function */ - if (func_type->result_count) - ret_type = TO_LLVM_TYPE(func_type->types[func_type->param_count]); - else - ret_type = VOID_TYPE; + if (tail_call) + LLVMSetTailCall(value_ret, true); - /* Resolve function prototype */ - if (!(f_type = LLVMFunctionType(ret_type, param_types, - (uint32)param_count + 1, false)) - || !(f_ptr_type = LLVMPointerType(f_type, 0))) { - aot_set_last_error("create LLVM function type failed."); + /* Check whether exception was thrown when executing the function */ + if (!tail_call + && (comp_ctx->enable_bound_check || is_win_platform(comp_ctx)) + && !check_exception_thrown(comp_ctx, func_ctx)) goto fail; - } - if (!(func = LLVMBuildBitCast(comp_ctx->builder, func, - f_ptr_type, "func"))) { - aot_set_last_error("create LLVM value failed."); - goto fail; + if (func_result_count > 0) { + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + + /* Push the first result to stack */ + LLVMAddIncoming(result_phis[0], &value_ret, &block_curr, 1); + + /* Load extra result from its address and push to stack */ + for (i = 1; i < func_result_count; i++) { + ret_type = TO_LLVM_TYPE(func_type->types[func_param_count + i]); + snprintf(buf, sizeof(buf), "ext_ret%d", i - 1); + if (!(ext_ret = LLVMBuildLoad2(comp_ctx->builder, ret_type, + param_values[func_param_count + i], + buf))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + LLVMSetAlignment(ext_ret, 4); + LLVMAddIncoming(result_phis[i], &ext_ret, &block_curr, 1); + } } - /* Allocate memory for parameters */ - total_size = sizeof(LLVMValueRef) * (uint64)(param_count + 1); - if (total_size >= UINT32_MAX - || !(param_values = wasm_malloc((uint32)total_size))) { - aot_set_last_error("Allocate memory failed."); + if (!LLVMBuildBr(comp_ctx->builder, block_return)) { + aot_set_last_error("llvm build br failed."); goto fail; } - /* First parameter is exec env */ - j = 0; - param_values[j++] = func_ctx->exec_env; - - /* Pop parameters from stack */ - for (i = param_count - 1; i >= 0; i--) - POP(param_values[i + j], func_type->types[i]); + /* Translate function return block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_return); - /* Call the function */ - if (!(value_ret = LLVMBuildCall(comp_ctx->builder, func, - param_values, (uint32)param_count + 1, - (func_type->result_count > 0 - ? "call_indirect" : "")))) { - aot_set_last_error("LLVM build call failed."); - goto fail; + for (i = 0; i < func_result_count; i++) { + PUSH(result_phis[i], func_type->types[func_param_count + i]); } - if (func_type->result_count > 0) - PUSH(value_ret, func_type->types[func_type->param_count]); + if (comp_ctx->aux_stack_frame_type + && !comp_ctx->call_stack_features.frame_per_function) { +#if WASM_ENABLE_AOT_STACK_FRAME != 0 + if (!free_frame_for_aot_func(comp_ctx, func_ctx)) + goto fail; +#endif + } - /* Check whether there was exception thrown when executing the function */ - if (!check_exception_thrown(comp_ctx, func_ctx)) - goto fail; + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, false)) + goto fail; + } ret = true; fail: if (param_values) - wasm_free(param_values); + wasm_runtime_free(param_values); if (param_types) - wasm_free(param_types); + wasm_runtime_free(param_types); + if (result_phis) + wasm_runtime_free(result_phis); return ret; } +#endif /* end of WASM_ENABLE_GC != 0 */ diff --git a/core/iwasm/compilation/aot_emit_function.h b/core/iwasm/compilation/aot_emit_function.h index 28d7bff984..45f7bbe596 100644 --- a/core/iwasm/compilation/aot_emit_function.h +++ b/core/iwasm/compilation/aot_emit_function.h @@ -14,15 +14,30 @@ extern "C" { bool aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 func_idx, uint8 **p_frame_ip); + uint32 func_idx, bool tail_call); bool aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 type_idx); + uint32 type_idx, uint32 tbl_idx); + +bool +aot_compile_op_ref_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_ref_is_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_ref_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 func_idx); + +#if WASM_ENABLE_GC != 0 +bool +aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_idx, bool tail_call); +#endif #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_EMIT_FUNCTION_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_gc.c b/core/iwasm/compilation/aot_emit_gc.c new file mode 100644 index 0000000000..7bd7affc3d --- /dev/null +++ b/core/iwasm/compilation/aot_emit_gc.c @@ -0,0 +1,2144 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_gc.h" +#include "aot_compiler.h" +#include "aot_emit_exception.h" + +#if WASM_ENABLE_GC != 0 + +#define BUILD_ISNULL(ptr, res, name) \ + do { \ + if (!(res = LLVMBuildIsNull(comp_ctx->builder, ptr, name))) { \ + aot_set_last_error("llvm build isnull failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_ISNOTNULL(ptr, res, name) \ + do { \ + if (!(res = LLVMBuildIsNotNull(comp_ctx->builder, ptr, name))) { \ + aot_set_last_error("llvm build isnotnull failed."); \ + goto fail; \ + } \ + } while (0) + +#define ADD_BASIC_BLOCK(block, name) \ + do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + } while (0) + +#define CURR_BLOCK() LLVMGetInsertBlock(comp_ctx->builder) + +#define MOVE_BLOCK_AFTER(llvm_block, llvm_block_after) \ + LLVMMoveBasicBlockAfter(llvm_block, llvm_block_after) + +#define MOVE_BLOCK_AFTER_CURR(llvm_block) \ + LLVMMoveBasicBlockAfter(llvm_block, CURR_BLOCK()) + +#define MOVE_BLOCK_BEFORE(llvm_block, llvm_block_before) \ + LLVMMoveBasicBlockBefore(llvm_block, llvm_block_before) + +#define BUILD_COND_BR(value_if, block_then, block_else) \ + do { \ + if (!LLVMBuildCondBr(comp_ctx->builder, value_if, block_then, \ + block_else)) { \ + aot_set_last_error("llvm build cond br failed."); \ + goto fail; \ + } \ + } while (0) + +#define SET_BUILDER_POS(llvm_block) \ + LLVMPositionBuilderAtEnd(comp_ctx->builder, llvm_block) + +#define BUILD_BR(llvm_block) \ + do { \ + if (!LLVMBuildBr(comp_ctx->builder, llvm_block)) { \ + aot_set_last_error("llvm build br failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_ICMP(op, left, right, res, name) \ + do { \ + if (!(res = \ + LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \ + aot_set_last_error("llvm build icmp failed."); \ + goto fail; \ + } \ + } while (0) + +static bool +is_target_x86(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "x86_64", 6) + || !strncmp(comp_ctx->target_arch, "i386", 4); +} + +bool +aot_call_aot_create_func_obj(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef func_idx, LLVMValueRef *p_gc_obj) +{ + LLVMValueRef gc_obj, cmp_gc_obj, param_values[5], func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef init_gc_obj_fail, init_gc_obj_succ; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = INT8_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = I32_TYPE; + ret_type = GC_REF_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_create_func_obj, 5); + else + GET_AOT_FUNCTION(aot_create_func_obj, 5); + + /* Call function llvm_jit/aot_create_func_obj() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = func_idx; + param_values[2] = I8_CONST(1); + param_values[3] = I8_PTR_NULL; + param_values[4] = I32_ZERO; + if (!(gc_obj = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 5, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ISNOTNULL(gc_obj, cmp_gc_obj, "gc_obj_not_null"); + + ADD_BASIC_BLOCK(init_gc_obj_fail, "init_gc_obj_fail"); + ADD_BASIC_BLOCK(init_gc_obj_succ, "init_gc_obj_success"); + + LLVMMoveBasicBlockAfter(init_gc_obj_fail, block_curr); + LLVMMoveBasicBlockAfter(init_gc_obj_succ, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, cmp_gc_obj, init_gc_obj_succ, + init_gc_obj_fail)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* If init gc_obj failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, init_gc_obj_fail); + if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { + goto fail; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, init_gc_obj_succ); + *p_gc_obj = gc_obj; + + return true; +fail: + return false; +} + +bool +aot_call_aot_obj_is_instance_of(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, LLVMValueRef gc_obj, + LLVMValueRef heap_type, LLVMValueRef *castable) +{ + LLVMValueRef param_values[3], func, value, res; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = GC_REF_TYPE; + param_types[2] = I32_TYPE; + ret_type = INT8_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_obj_is_instance_of, 3); + else + GET_AOT_FUNCTION(aot_obj_is_instance_of, 3); + + /* Call function aot_obj_is_instance_of() or llvm_jit_obj_is_instance_of() + */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = gc_obj; + param_values[2] = heap_type; + + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 3, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *castable = res; + + return true; +fail: + return false; +} + +bool +aot_call_wasm_obj_is_type_of(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef gc_obj, LLVMValueRef heap_type, + LLVMValueRef *castable) +{ + LLVMValueRef param_values[2], func, value, res; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + param_types[0] = GC_REF_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + + GET_AOT_FUNCTION(wasm_obj_is_type_of, 2); + + /* Call function wasm_obj_is_type_of() */ + param_values[0] = gc_obj; + param_values[1] = heap_type; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 2, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *castable = res; + + return true; +fail: + return false; +} + +bool +aot_call_aot_rtt_type_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef type_index, LLVMValueRef *rtt_type) +{ + LLVMValueRef param_values[2], func, value, res; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = GC_REF_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_rtt_type_new, 2); + else + GET_AOT_FUNCTION(aot_rtt_type_new, 2); + + /* Call function llvm_jit/aot_rtt_type_new() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = type_index; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 2, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *rtt_type = res; + return true; +fail: + return false; +} + +bool +aot_compile_op_ref_as_non_null(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef gc_obj, cmp_gc_obj; + LLVMBasicBlockRef check_gc_obj_succ; + + GET_GC_REF_FROM_STACK(gc_obj); + + /* Check if gc object is NULL */ + BUILD_ISNULL(gc_obj, cmp_gc_obj, "cmp_gc_obj"); + + ADD_BASIC_BLOCK(check_gc_obj_succ, "check_gc_obj_succ"); + MOVE_BLOCK_AFTER_CURR(check_gc_obj_succ); + + /* Throw exception if it is NULL */ + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_REFERENCE, true, + cmp_gc_obj, check_gc_obj_succ)) + goto fail; + + return true; +fail: + return false; +} + +static bool +aot_call_wasm_struct_obj_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef rtt_type, LLVMValueRef *struct_obj) +{ + LLVMValueRef param_values[2], func, value, res; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + ret_type = GC_REF_TYPE; + + GET_AOT_FUNCTION(wasm_struct_obj_new, 2); + + /* Call function wasm_struct_obj_new() */ + param_values[0] = func_ctx->exec_env; + param_values[1] = rtt_type; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 2, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *struct_obj = res; + return true; +fail: + return false; +} + +static void +get_struct_field_data_types(const AOTCompContext *comp_ctx, uint8 field_type, + LLVMTypeRef *p_field_data_type, + LLVMTypeRef *p_field_data_ptr_type, + bool *p_trunc_or_extend) +{ + LLVMTypeRef field_data_type = NULL, field_data_ptr_type = NULL; + bool trunc_or_extend = false; + + if (wasm_is_type_reftype(field_type)) { + field_data_type = GC_REF_TYPE; + field_data_ptr_type = GC_REF_PTR_TYPE; + } + else { + switch (field_type) { + case VALUE_TYPE_I32: + field_data_type = I32_TYPE; + field_data_ptr_type = INT32_PTR_TYPE; + break; + case VALUE_TYPE_I64: + field_data_type = I64_TYPE; + field_data_ptr_type = INT64_PTR_TYPE; + break; + case VALUE_TYPE_F32: + field_data_type = F32_TYPE; + field_data_ptr_type = F32_PTR_TYPE; + break; + case VALUE_TYPE_F64: + field_data_type = F64_TYPE; + field_data_ptr_type = F64_PTR_TYPE; + break; + case PACKED_TYPE_I8: + field_data_type = INT8_TYPE; + field_data_ptr_type = INT8_PTR_TYPE; + trunc_or_extend = true; + break; + case PACKED_TYPE_I16: + field_data_type = INT16_TYPE; + field_data_ptr_type = INT16_PTR_TYPE; + trunc_or_extend = true; + break; + default: + bh_assert(0); + break; + } + } + + *p_field_data_type = field_data_type; + *p_field_data_ptr_type = field_data_ptr_type; + *p_trunc_or_extend = trunc_or_extend; +} + +static bool +aot_struct_obj_set_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj, + LLVMValueRef field_offset, LLVMValueRef field_value, + uint8 field_type) +{ + bool trunc = false; + LLVMValueRef field_data_ptr, res; + LLVMTypeRef field_data_type = NULL, field_data_ptr_type = NULL; + + get_struct_field_data_types(comp_ctx, field_type, &field_data_type, + &field_data_ptr_type, &trunc); + + /* Truncate field_value if necessary */ + if (trunc) { + if (!(field_value = + LLVMBuildTrunc(comp_ctx->builder, field_value, + field_data_type, "field_value_trunc"))) { + aot_set_last_error("llvm build trunc failed."); + goto fail; + } + } + + if (!(struct_obj = LLVMBuildBitCast(comp_ctx->builder, struct_obj, + INT8_PTR_TYPE, "struct_obj_i8p"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + /* Build field data ptr and store the value */ + if (!(field_data_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, struct_obj, + &field_offset, 1, "field_data_i8p"))) { + aot_set_last_error("llvm build gep failed."); + goto fail; + } + + /* Cast to the field data type ptr */ + if (!(field_data_ptr = + LLVMBuildBitCast(comp_ctx->builder, field_data_ptr, + field_data_ptr_type, "field_value_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(res = + LLVMBuildStore(comp_ctx->builder, field_value, field_data_ptr))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + + if (!is_target_x86(comp_ctx) + && (field_data_type == I64_TYPE || field_data_type == F64_TYPE + || field_data_type == GC_REF_TYPE)) { + LLVMSetAlignment(res, 4); + } + + return true; +fail: + return false; +} + +static bool +aot_struct_obj_get_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj, + LLVMValueRef field_offset, LLVMValueRef *p_field_value, + uint8 field_type, bool sign_extend) +{ + bool extend = false; + LLVMValueRef field_value, field_data_ptr; + LLVMTypeRef field_data_type = NULL, field_data_ptr_type = NULL; + + get_struct_field_data_types(comp_ctx, field_type, &field_data_type, + &field_data_ptr_type, &extend); + + if (!(struct_obj = LLVMBuildBitCast(comp_ctx->builder, struct_obj, + INT8_PTR_TYPE, "struct_obj_i8p"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(field_data_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, struct_obj, + &field_offset, 1, "field_data_i8p"))) { + aot_set_last_error("llvm build gep failed."); + goto fail; + } + + if (!(field_data_ptr = + LLVMBuildBitCast(comp_ctx->builder, field_data_ptr, + field_data_ptr_type, "field_value_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(field_value = LLVMBuildLoad2(comp_ctx->builder, field_data_type, + field_data_ptr, "field_value"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!is_target_x86(comp_ctx) + && (field_data_type == I64_TYPE || field_data_type == F64_TYPE + || field_data_type == GC_REF_TYPE)) { + LLVMSetAlignment(field_value, 4); + } + + if (extend) { + if (sign_extend) { + if (!(field_value = LLVMBuildSExt(comp_ctx->builder, field_value, + I32_TYPE, "field_value_sext"))) { + aot_set_last_error("llvm build signed ext failed."); + goto fail; + } + } + else { + if (!(field_value = LLVMBuildZExt(comp_ctx->builder, field_value, + I32_TYPE, "field_value_zext"))) { + aot_set_last_error("llvm build unsigned ext failed."); + goto fail; + } + } + } + + *p_field_value = field_value; + return true; +fail: + return false; +} + +static bool +struct_new_canon_init_fields(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, LLVMValueRef struct_obj) +{ + LLVMValueRef field_value = NULL; + /* Used in compile time, to distinguish what type of AOTValue POP, + * field_data offset, size */ + WASMStructType *compile_time_struct_type = + (WASMStructType *)comp_ctx->comp_data->types[type_index]; + WASMStructFieldType *fields = compile_time_struct_type->fields; + int32 field_count = (int32)compile_time_struct_type->field_count; + int32 field_idx; + uint32 field_offset; + uint8 field_type; + + for (field_idx = field_count - 1; field_idx >= 0; field_idx--) { + field_type = fields[field_idx].field_type; + field_offset = comp_ctx->pointer_size == sizeof(uint64) + ? fields[field_idx].field_offset_64bit + : fields[field_idx].field_offset_32bit; + + if (wasm_is_type_reftype(field_type)) { + POP_GC_REF(field_value); + } + else if (field_type == VALUE_TYPE_I32 || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + POP_I32(field_value); + } + else if (field_type == VALUE_TYPE_I64) { + POP_I64(field_value); + } + else if (field_type == VALUE_TYPE_F32) { + POP_F32(field_value); + } + else if (field_type == VALUE_TYPE_F64) { + POP_F64(field_value); + } + else { + bh_assert(0); + } + + if (!aot_struct_obj_set_field(comp_ctx, struct_obj, + I32_CONST(field_offset), field_value, + field_type)) + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_struct_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, bool init_with_default) +{ + LLVMValueRef rtt_type, struct_obj, cmp; + LLVMBasicBlockRef check_rtt_type_succ, check_struct_obj_succ; + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + /* Generate call wasm_rtt_type_new and check for exception */ + if (!aot_call_aot_rtt_type_new(comp_ctx, func_ctx, I32_CONST(type_index), + &rtt_type)) + goto fail; + + ADD_BASIC_BLOCK(check_rtt_type_succ, "check rtt type succ"); + MOVE_BLOCK_AFTER_CURR(check_rtt_type_succ); + + BUILD_ISNULL(rtt_type, cmp, "cmp_rtt_type"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_RTT_TYPE, + true, cmp, check_rtt_type_succ)) + goto fail; + + /* Generate call wasm_struct_obj_new and check for exception */ + if (!aot_call_wasm_struct_obj_new(comp_ctx, func_ctx, rtt_type, + &struct_obj)) + goto fail; + + ADD_BASIC_BLOCK(check_struct_obj_succ, "check struct obj succ"); + MOVE_BLOCK_AFTER(check_struct_obj_succ, check_rtt_type_succ); + + BUILD_ISNULL(struct_obj, cmp, "cmp_struct_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_FAILED_TO_CREATE_STRUCT_OBJ, true, cmp, + check_struct_obj_succ)) + goto fail; + + SET_BUILDER_POS(check_struct_obj_succ); + + /* For WASM_OP_STRUCT_NEW, init field with poped value */ + if (!init_with_default + && !struct_new_canon_init_fields(comp_ctx, func_ctx, type_index, + struct_obj)) { + goto fail; + } + + PUSH_GC_REF(struct_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_struct_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, uint32 field_idx, bool sign) +{ + LLVMValueRef struct_obj, cmp, field_value; + LLVMBasicBlockRef check_struct_obj_succ; + + /* Used in compile time, to distinguish what type of AOTValue PUSH, + * field_data offset, size */ + WASMStructType *compile_time_struct_type = + (WASMStructType *)comp_ctx->comp_data->types[type_index]; + WASMStructFieldType *field; + uint32 field_offset; + uint8 field_type; + + field = compile_time_struct_type->fields + field_idx; + field_type = field->field_type; + field_offset = comp_ctx->pointer_size == sizeof(uint64) + ? field->field_offset_64bit + : field->field_offset_32bit; + + if (field_idx >= compile_time_struct_type->field_count) { + aot_set_last_error("struct field index out of bounds"); + goto fail; + } + + POP_GC_REF(struct_obj); + + ADD_BASIC_BLOCK(check_struct_obj_succ, "check struct obj succ"); + MOVE_BLOCK_AFTER_CURR(check_struct_obj_succ); + + BUILD_ISNULL(struct_obj, cmp, "cmp_struct_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_STRUCT_OBJ, true, cmp, + check_struct_obj_succ)) + goto fail; + + if (!aot_struct_obj_get_field(comp_ctx, struct_obj, I32_CONST(field_offset), + &field_value, field_type, sign)) + goto fail; + + if (wasm_is_type_reftype(field_type)) { + PUSH_GC_REF(field_value); + } + else if (field_type == VALUE_TYPE_I32 || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + PUSH_I32(field_value); + } + else if (field_type == VALUE_TYPE_I64) { + PUSH_I64(field_value); + } + else if (field_type == VALUE_TYPE_F32) { + PUSH_F32(field_value); + } + else if (field_type == VALUE_TYPE_F64) { + PUSH_F64(field_value); + } + else { + bh_assert(0); + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_struct_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, uint32 field_idx) +{ + LLVMValueRef struct_obj, cmp, field_value = NULL; + LLVMBasicBlockRef check_struct_obj_succ; + /* Used in compile time, to distinguish what type of AOTValue POP, + * field_data offset, size */ + WASMStructType *compile_time_struct_type = + (WASMStructType *)comp_ctx->comp_data->types[type_index]; + WASMStructFieldType *field; + uint32 field_offset; + uint8 field_type; + + field = compile_time_struct_type->fields + field_idx; + field_type = field->field_type; + field_offset = comp_ctx->pointer_size == sizeof(uint64) + ? field->field_offset_64bit + : field->field_offset_32bit; + + if (field_idx >= compile_time_struct_type->field_count) { + aot_set_last_error("struct field index out of bounds"); + goto fail; + } + + if (wasm_is_type_reftype(field_type)) { + POP_GC_REF(field_value); + } + else if (field_type == VALUE_TYPE_I32 || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + POP_I32(field_value); + } + else if (field_type == VALUE_TYPE_I64) { + POP_I64(field_value); + } + else if (field_type == VALUE_TYPE_F32) { + POP_F32(field_value); + } + else if (field_type == VALUE_TYPE_F64) { + POP_F64(field_value); + } + else { + bh_assert(0); + } + + POP_GC_REF(struct_obj); + + ADD_BASIC_BLOCK(check_struct_obj_succ, "check struct obj succ"); + MOVE_BLOCK_AFTER_CURR(check_struct_obj_succ); + + BUILD_ISNULL(struct_obj, cmp, "cmp_struct_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_STRUCT_OBJ, true, cmp, + check_struct_obj_succ)) + goto fail; + + if (!aot_struct_obj_set_field(comp_ctx, struct_obj, I32_CONST(field_offset), + field_value, field_type)) + goto fail; + + return true; +fail: + return false; +} + +static bool +aot_call_wasm_array_obj_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef rtt_type, LLVMValueRef array_len, + LLVMValueRef array_elem, LLVMValueRef *array_obj) +{ + LLVMValueRef param_values[4], func, value, res, array_elem_ptr; + LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type; + + if (!(array_elem_ptr = LLVMBuildAlloca( + comp_ctx->builder, LLVMTypeOf(array_elem), "array_elem_ptr"))) { + aot_set_last_error("llvm build alloca failed."); + goto fail; + } + if (!LLVMBuildStore(comp_ctx->builder, array_elem, array_elem_ptr)) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + if (!(array_elem_ptr = LLVMBuildBitCast(comp_ctx->builder, array_elem_ptr, + INT8_PTR_TYPE, "array_elem_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + ret_type = GC_REF_TYPE; + + GET_AOT_FUNCTION(wasm_array_obj_new, 4); + + /* Call function wasm_array_obj_new() */ + param_values[0] = func_ctx->exec_env; + param_values[1] = rtt_type; + param_values[2] = array_len; + param_values[3] = array_elem_ptr; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 4, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *array_obj = res; + return true; +fail: + return false; +} + +static uint32 +aot_array_obj_elem_size_log(AOTCompContext *comp_ctx, uint8 array_elem_type) +{ + uint32 elem_size_log = 0; + + if (wasm_is_type_reftype(array_elem_type)) { + elem_size_log = comp_ctx->pointer_size == sizeof(uint32) ? 2 : 3; + } + else if (array_elem_type == PACKED_TYPE_I8) { + elem_size_log = 0; + } + else if (array_elem_type == PACKED_TYPE_I16) { + elem_size_log = 1; + } + else if (array_elem_type == VALUE_TYPE_I32 + || array_elem_type == VALUE_TYPE_F32) { + elem_size_log = 2; + } + else if (array_elem_type == VALUE_TYPE_I64 + || array_elem_type == VALUE_TYPE_F64) { + elem_size_log = 3; + } + else { + bh_assert(0); + } + + return elem_size_log; +} + +/* array_obj->elem_data + (elem_idx << elem_size_log) */ +bool +aot_array_obj_elem_addr(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef array_obj, LLVMValueRef elem_idx, + LLVMValueRef *p_elem_data, uint8 array_elem_type) +{ + uint32 elem_size_log = 0; + LLVMValueRef start_offset, elem_offset, elem_data; + + elem_size_log = aot_array_obj_elem_size_log(comp_ctx, array_elem_type); + + /* Get the elem data start offset of the WASMArrayObject, the offset may be + * different in 32-bit runtime and 64-bit runtime since WASMObjectHeader + * is uintptr_t. Use comp_ctx->pointer_size + 4(uint32 for length) as the + * offsetof(WASMArrayObject, length)*/ + if (!(start_offset = I32_CONST(comp_ctx->pointer_size + sizeof(uint32)))) { + aot_set_last_error("llvm build const failed."); + goto fail; + } + + if (!(elem_offset = + LLVMBuildShl(comp_ctx->builder, elem_idx, + I32_CONST(elem_size_log), "elem_offset"))) { + aot_set_last_error("llvm build shl failed."); + goto fail; + } + + if (!(elem_offset = LLVMBuildAdd(comp_ctx->builder, start_offset, + elem_offset, "total_offset"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + + if (!(elem_data = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + array_obj, &elem_offset, 1, + "array_obj_elem_data_i8p"))) { + aot_set_last_error("llvm build gep failed."); + goto fail; + } + + *p_elem_data = elem_data; + return true; +fail: + return false; +} + +static bool +aot_array_obj_set_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef array_obj, LLVMValueRef elem_idx, + LLVMValueRef array_elem, uint8 array_elem_type) +{ + bool trunc = false; + LLVMValueRef elem_data_ptr, res; + LLVMTypeRef elem_data_type = NULL, elem_data_ptr_type = NULL; + + if (!aot_array_obj_elem_addr(comp_ctx, func_ctx, array_obj, elem_idx, + &elem_data_ptr, array_elem_type)) + goto fail; + + if (wasm_is_type_reftype(array_elem_type)) { + elem_data_type = GC_REF_TYPE; + elem_data_ptr_type = GC_REF_PTR_TYPE; + } + else + switch (array_elem_type) { + case PACKED_TYPE_I8: + elem_data_type = INT8_TYPE; + elem_data_ptr_type = INT8_PTR_TYPE; + trunc = true; + break; + case PACKED_TYPE_I16: + elem_data_type = INT16_TYPE; + elem_data_ptr_type = INT16_PTR_TYPE; + trunc = true; + break; + case VALUE_TYPE_I32: + elem_data_type = I32_TYPE; + elem_data_ptr_type = INT32_PTR_TYPE; + break; + case VALUE_TYPE_I64: + elem_data_type = I64_TYPE; + elem_data_ptr_type = INT64_PTR_TYPE; + break; + case VALUE_TYPE_F32: + elem_data_type = F32_TYPE; + elem_data_ptr_type = F32_PTR_TYPE; + break; + case VALUE_TYPE_F64: + elem_data_type = F64_TYPE; + elem_data_ptr_type = F64_PTR_TYPE; + break; + default: + bh_assert(0); + break; + } + + /* Based on elem_size, trunc array_elem if necessary */ + if (trunc) { + if (!(array_elem = + LLVMBuildTrunc(comp_ctx->builder, array_elem, elem_data_type, + "array_elem_trunc"))) { + aot_set_last_error("llvm build trunc failed."); + goto fail; + } + } + + /* Cast to the field data type ptr */ + if (!(elem_data_ptr = + LLVMBuildBitCast(comp_ctx->builder, elem_data_ptr, + elem_data_ptr_type, "elem_data_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, array_elem, elem_data_ptr))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + + if (!is_target_x86(comp_ctx) + && (elem_data_type == I64_TYPE || elem_data_type == F64_TYPE + || elem_data_type == GC_REF_TYPE)) { + LLVMSetAlignment(res, 4); + } + + return true; +fail: + return false; +} + +static bool +aot_call_aot_array_init_with_data( + AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMValueRef seg_index, + LLVMValueRef data_seg_offset, LLVMValueRef array_obj, + LLVMValueRef elem_size, LLVMValueRef array_len) +{ + LLVMValueRef param_values[6], func, value, res, cmp; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef init_success; + + ADD_BASIC_BLOCK(init_success, "init success"); + MOVE_BLOCK_AFTER_CURR(init_success); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = I32_TYPE; + param_types[5] = I32_TYPE; + ret_type = INT8_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_array_init_with_data, 6); + else + GET_AOT_FUNCTION(aot_array_init_with_data, 6); + + /* Call function aot_array_init_with_data() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg_index; + param_values[2] = data_seg_offset; + param_values[3] = array_obj; + param_values[4] = elem_size; + param_values[5] = array_len; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 6, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + BUILD_ICMP(LLVMIntEQ, res, I8_ZERO, cmp, "array_init_ret"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp, + init_success)) + goto fail; + + return true; +fail: + return false; +} + +static bool +aot_call_wasm_array_get_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef array_obj, LLVMValueRef elem_idx, + LLVMValueRef *p_array_elem, uint8 array_elem_type, + bool sign) +{ + bool extend = false; + LLVMValueRef elem_data_ptr, array_elem; + LLVMTypeRef elem_data_type = NULL, elem_data_ptr_type = NULL; + + if (!aot_array_obj_elem_addr(comp_ctx, func_ctx, array_obj, elem_idx, + &elem_data_ptr, array_elem_type)) + goto fail; + + if (wasm_is_type_reftype(array_elem_type)) { + elem_data_type = GC_REF_TYPE; + elem_data_ptr_type = GC_REF_PTR_TYPE; + } + else + switch (array_elem_type) { + case PACKED_TYPE_I8: + elem_data_type = INT8_TYPE; + elem_data_ptr_type = INT8_PTR_TYPE; + extend = true; + break; + case PACKED_TYPE_I16: + elem_data_type = INT16_TYPE; + elem_data_ptr_type = INT16_PTR_TYPE; + extend = true; + break; + case VALUE_TYPE_I32: + elem_data_type = I32_TYPE; + elem_data_ptr_type = INT32_PTR_TYPE; + break; + case VALUE_TYPE_I64: + elem_data_type = I64_TYPE; + elem_data_ptr_type = INT64_PTR_TYPE; + break; + case VALUE_TYPE_F32: + elem_data_type = F32_TYPE; + elem_data_ptr_type = F32_PTR_TYPE; + break; + case VALUE_TYPE_F64: + elem_data_type = F64_TYPE; + elem_data_ptr_type = F64_PTR_TYPE; + break; + default: + bh_assert(0); + break; + } + + /* Based on elem_size, trunc array_elem if necessary */ + if (!(elem_data_ptr = + LLVMBuildBitCast(comp_ctx->builder, elem_data_ptr, + elem_data_ptr_type, "elem_data_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(array_elem = LLVMBuildLoad2(comp_ctx->builder, elem_data_type, + elem_data_ptr, "array_elem"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!is_target_x86(comp_ctx) + && (elem_data_type == I64_TYPE || elem_data_type == F64_TYPE + || elem_data_type == GC_REF_TYPE)) { + LLVMSetAlignment(array_elem, 4); + } + + if (extend) { + if (sign) { + if (!(array_elem = LLVMBuildSExt(comp_ctx->builder, array_elem, + I32_TYPE, "array_elem_sext"))) { + aot_set_last_error("llvm build signed ext failed."); + goto fail; + } + } + else { + if (!(array_elem = LLVMBuildZExt(comp_ctx->builder, array_elem, + I32_TYPE, "array_elem_zext"))) { + aot_set_last_error("llvm build unsigned ext failed."); + goto fail; + } + } + } + + *p_array_elem = array_elem; + return true; +fail: + return false; +} + +/* array_obj->length >> WASM_ARRAY_LENGTH_SHIFT */ +bool +aot_array_obj_length(AOTCompContext *comp_ctx, LLVMValueRef array_obj, + LLVMValueRef *p_array_len) +{ + LLVMValueRef offset, array_len; + + /* Get the length of the WASMArrayObject, the offset may be + * different in 32-bit runtime and 64-bit runtime since WASMObjectHeader + * is uintptr_t. Use comp_ctx->pointer_size as the + * offsetof(WASMArrayObject, length)*/ + if (!(offset = I32_CONST(comp_ctx->pointer_size))) { + aot_set_last_error("llvm build const failed."); + goto fail; + } + + if (!(array_len = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, array_obj, + &offset, 1, "array_obj_length_i8p"))) { + aot_set_last_error("llvm build gep failed."); + goto fail; + } + + if (!(array_len = + LLVMBuildBitCast(comp_ctx->builder, array_len, INT32_PTR_TYPE, + "array_obj_length_i32ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(array_len = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, array_len, + "array_obj_length"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + if (!(array_len = LLVMBuildLShr(comp_ctx->builder, array_len, + I32_CONST(WASM_ARRAY_LENGTH_SHIFT), + "array_obj_length_shr"))) { + aot_set_last_error("llvm build lshr failed."); + goto fail; + } + + *p_array_len = array_len; + return true; +fail: + return false; +} + +bool +aot_compile_op_array_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, bool init_with_default, + bool fixed_size, uint32 array_len) +{ + LLVMValueRef array_length, array_elem = NULL, array_obj; + LLVMValueRef rtt_type, cmp, elem_idx; + LLVMBasicBlockRef check_rtt_type_succ, check_array_obj_succ; + /* Use for distinguish what type of AOTValue POP */ + WASMArrayType *compile_time_array_type = + (WASMArrayType *)comp_ctx->comp_data->types[type_index]; + uint8 array_elem_type = compile_time_array_type->elem_type; + uint32 i; + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + /* Generate call aot_rtt_type_new and check for exception */ + if (!aot_call_aot_rtt_type_new(comp_ctx, func_ctx, I32_CONST(type_index), + &rtt_type)) + goto fail; + + ADD_BASIC_BLOCK(check_rtt_type_succ, "check rtt type succ"); + MOVE_BLOCK_AFTER_CURR(check_rtt_type_succ); + + BUILD_ISNULL(rtt_type, cmp, "cmp_rtt_type"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_RTT_TYPE, + true, cmp, check_rtt_type_succ)) + goto fail; + + if (!fixed_size) + POP_I32(array_length); + else + array_length = I32_CONST(array_len); + + /* For WASM_OP_ARRAY_NEW */ + if (!fixed_size && !init_with_default) { + if (wasm_is_type_reftype(array_elem_type)) { + POP_GC_REF(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I32 + || array_elem_type == PACKED_TYPE_I8 + || array_elem_type == PACKED_TYPE_I16) { + POP_I32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I64) { + POP_I64(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F32) { + POP_F32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F64) { + POP_F64(array_elem); + } + else { + bh_assert(0); + } + } + else { + /* I64 will alloca large enough space for all union access includes + * array_elem.gc_ob, i32, i64 to be interpreted as 0*/ + array_elem = I64_ZERO; + } + + /* Generate call wasm_array_obj_new and check for exception */ + if (!aot_call_wasm_array_obj_new(comp_ctx, func_ctx, rtt_type, array_length, + array_elem, &array_obj)) + goto fail; + + ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ"); + MOVE_BLOCK_AFTER(check_array_obj_succ, check_rtt_type_succ); + + BUILD_ISNULL(array_obj, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_ARRAY_OBJ, + true, cmp, check_array_obj_succ)) + goto fail; + + if (fixed_size) { + for (i = 0; i < array_len; i++) { + if (wasm_is_type_reftype(array_elem_type)) { + POP_GC_REF(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I32 + || array_elem_type == PACKED_TYPE_I8 + || array_elem_type == PACKED_TYPE_I16) { + POP_I32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I64) { + POP_I64(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F32) { + POP_F32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F64) { + POP_F64(array_elem); + } + else { + bh_assert(0); + } + + /* array_len - 1 - i */ + if (!(elem_idx = LLVMBuildSub(comp_ctx->builder, array_length, + I32_CONST(i + 1), "elem_idx"))) { + aot_set_last_error("llvm build sub failed."); + goto fail; + } + + if (!aot_array_obj_set_elem(comp_ctx, func_ctx, array_obj, elem_idx, + array_elem, array_elem_type)) + goto fail; + } + } + + PUSH_GC_REF(array_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_array_new_data(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 type_index, + uint32 data_seg_index) +{ + LLVMValueRef array_length, data_seg_offset, rtt_type, + elem_size = NULL, array_elem, array_obj, cmp; + LLVMBasicBlockRef check_rtt_type_succ, check_array_obj_succ; + /* Use for distinguish what type of element in array */ + WASMArrayType *compile_time_array_type = + (WASMArrayType *)comp_ctx->comp_data->types[type_index]; + uint8 array_elem_type = compile_time_array_type->elem_type; + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + /* Generate call aot_rtt_type_new and check for exception */ + if (!aot_call_aot_rtt_type_new(comp_ctx, func_ctx, I32_CONST(type_index), + &rtt_type)) + goto fail; + + ADD_BASIC_BLOCK(check_rtt_type_succ, "check rtt type succ"); + MOVE_BLOCK_AFTER_CURR(check_rtt_type_succ); + + BUILD_ISNULL(rtt_type, cmp, "cmp_rtt_type"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_RTT_TYPE, + true, cmp, check_rtt_type_succ)) + goto fail; + + POP_I32(array_length); + POP_I32(data_seg_offset); + + switch (array_elem_type) { + case PACKED_TYPE_I8: + elem_size = I32_ONE; + break; + case PACKED_TYPE_I16: + elem_size = I32_TWO; + break; + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + elem_size = I32_FOUR; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + elem_size = I32_EIGHT; + break; + default: + bh_assert(0); + } + + if (elem_size == I32_EIGHT) + array_elem = I64_ZERO; + else + array_elem = I32_ZERO; + + /* Generate call wasm_array_obj_new and check for exception */ + if (!aot_call_wasm_array_obj_new(comp_ctx, func_ctx, rtt_type, array_length, + array_elem, &array_obj)) + goto fail; + + ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ"); + MOVE_BLOCK_AFTER(check_array_obj_succ, check_rtt_type_succ); + + BUILD_ISNULL(array_obj, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_ARRAY_OBJ, + true, cmp, check_array_obj_succ)) + goto fail; + + if (!aot_call_aot_array_init_with_data( + comp_ctx, func_ctx, I32_CONST(data_seg_index), data_seg_offset, + array_obj, elem_size, array_length)) + goto fail; + + PUSH_GC_REF(array_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_array_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, bool sign) +{ + LLVMValueRef elem_idx, array_obj, cmp, array_len, array_elem; + LLVMBasicBlockRef check_array_obj_succ, check_boundary_succ; + /* Use for distinguish what type of AOTValue PUSH */ + WASMArrayType *compile_time_array_type = + (WASMArrayType *)comp_ctx->comp_data->types[type_index]; + uint8 array_elem_type = compile_time_array_type->elem_type; + + POP_I32(elem_idx); + POP_GC_REF(array_obj); + + ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ"); + MOVE_BLOCK_AFTER_CURR(check_array_obj_succ); + + BUILD_ISNULL(array_obj, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, cmp, + check_array_obj_succ)) + goto fail; + + SET_BUILDER_POS(check_array_obj_succ); + if (!aot_array_obj_length(comp_ctx, array_obj, &array_len)) + goto fail; + + ADD_BASIC_BLOCK(check_boundary_succ, "check boundary succ"); + MOVE_BLOCK_AFTER(check_boundary_succ, check_array_obj_succ); + + BUILD_ICMP(LLVMIntUGE, elem_idx, array_len, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp, + check_boundary_succ)) + goto fail; + + SET_BUILDER_POS(check_boundary_succ); + if (!aot_call_wasm_array_get_elem(comp_ctx, func_ctx, array_obj, elem_idx, + &array_elem, array_elem_type, sign)) + goto fail; + + if (wasm_is_type_reftype(array_elem_type)) { + PUSH_GC_REF(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I32 + || array_elem_type == PACKED_TYPE_I8 + || array_elem_type == PACKED_TYPE_I16) { + PUSH_I32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I64) { + PUSH_I64(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F32) { + PUSH_F32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F64) { + PUSH_F64(array_elem); + } + else { + bh_assert(0); + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_array_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index) +{ + LLVMValueRef elem_idx, array_obj, cmp, array_len, array_elem = NULL; + LLVMBasicBlockRef check_array_obj_succ, check_boundary_succ; + /* Use for distinguish what type of AOTValue POP */ + WASMArrayType *compile_time_array_type = + (WASMArrayType *)comp_ctx->comp_data->types[type_index]; + uint8 array_elem_type = compile_time_array_type->elem_type; + + /* Get LLVM type based on array_elem_type */ + if (wasm_is_type_reftype(array_elem_type)) { + POP_GC_REF(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I32 + || array_elem_type == PACKED_TYPE_I8 + || array_elem_type == PACKED_TYPE_I16) { + POP_I32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_I64) { + POP_I64(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F32) { + POP_F32(array_elem); + } + else if (array_elem_type == VALUE_TYPE_F64) { + POP_F64(array_elem); + } + else { + bh_assert(0); + } + + POP_I32(elem_idx); + POP_GC_REF(array_obj); + + ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ"); + MOVE_BLOCK_AFTER_CURR(check_array_obj_succ); + + BUILD_ISNULL(array_obj, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, cmp, + check_array_obj_succ)) + goto fail; + + SET_BUILDER_POS(check_array_obj_succ); + if (!aot_array_obj_length(comp_ctx, array_obj, &array_len)) + goto fail; + + ADD_BASIC_BLOCK(check_boundary_succ, "check boundary succ"); + MOVE_BLOCK_AFTER(check_boundary_succ, check_array_obj_succ); + + BUILD_ICMP(LLVMIntUGE, elem_idx, array_len, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp, + check_boundary_succ)) + goto fail; + + SET_BUILDER_POS(check_boundary_succ); + if (!aot_array_obj_set_elem(comp_ctx, func_ctx, array_obj, elem_idx, + array_elem, array_elem_type)) { + aot_set_last_error("llvm build alloca failed."); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_array_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index) +{ + LLVMValueRef len, array_obj, fill_value = NULL, offset, array_len, cmp[2], + boundary, loop_counter_addr, loop_counter_val; + LLVMBasicBlockRef check_obj_succ, len_gt_zero, len_le_zero, inner_else; + LLVMBasicBlockRef fill_loop_header, fill_loop_body; + WASMArrayType *compile_time_array_type = + (WASMArrayType *)comp_ctx->comp_data->types[type_index]; + uint8 array_elem_type = compile_time_array_type->elem_type; + + POP_I32(len); + /* Get LLVM type based on array_elem_type */ + if (wasm_is_type_reftype(array_elem_type)) { + POP_GC_REF(fill_value); + } + else if (array_elem_type == VALUE_TYPE_I32 + || array_elem_type == PACKED_TYPE_I8 + || array_elem_type == PACKED_TYPE_I16) { + POP_I32(fill_value); + } + else if (array_elem_type == VALUE_TYPE_I64) { + POP_I64(fill_value); + } + else if (array_elem_type == VALUE_TYPE_F32) { + POP_F32(fill_value); + } + else if (array_elem_type == VALUE_TYPE_F64) { + POP_F64(fill_value); + } + else { + bh_assert(0); + } + + POP_I32(offset); + POP_GC_REF(array_obj); + + ADD_BASIC_BLOCK(check_obj_succ, "check array objs succ"); + MOVE_BLOCK_AFTER_CURR(check_obj_succ); + + BUILD_ISNULL(array_obj, cmp[0], "cmp_obj"); + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, + cmp[0], check_obj_succ)) + goto fail; + + /* Create if block */ + ADD_BASIC_BLOCK(len_gt_zero, "len_gt_zero"); + MOVE_BLOCK_AFTER_CURR(len_gt_zero); + + /* Create inner else block */ + ADD_BASIC_BLOCK(inner_else, "inner_else"); + MOVE_BLOCK_AFTER(inner_else, len_gt_zero); + + /* Create fill_loop_header block */ + ADD_BASIC_BLOCK(fill_loop_header, "fill_loop_header"); + MOVE_BLOCK_AFTER(fill_loop_header, len_gt_zero); + + /* Create fill_loop_body block */ + ADD_BASIC_BLOCK(fill_loop_body, "fill_loop_body"); + MOVE_BLOCK_AFTER(fill_loop_body, len_gt_zero); + + /* Create else(end) block */ + ADD_BASIC_BLOCK(len_le_zero, "len_le_zero"); + MOVE_BLOCK_AFTER(len_le_zero, len_gt_zero); + + BUILD_ICMP(LLVMIntSGT, len, I32_ZERO, cmp[0], "cmp_len"); + BUILD_COND_BR(cmp[0], len_gt_zero, len_le_zero); + + /* Move builder to len > 0 block */ + SET_BUILDER_POS(len_gt_zero); + /* dst_offset > UINT32_MAX - len */ + if (!(boundary = LLVMBuildAdd(comp_ctx->builder, offset, len, ""))) { + aot_set_last_error("llvm build failed."); + goto fail; + } + BUILD_ICMP(LLVMIntUGT, boundary, I32_CONST(UINT32_MAX), cmp[0], + "boundary_check1"); + /* dst_offset + len > wasm_array_obj_length(dst_obj) */ + if (!aot_array_obj_length(comp_ctx, array_obj, &array_len)) + goto fail; + BUILD_ICMP(LLVMIntUGT, boundary, array_len, cmp[1], "boundary_check2"); + + if (!(cmp[0] = LLVMBuildOr(comp_ctx->builder, cmp[0], cmp[1], ""))) { + aot_set_last_error("llvm build failed."); + goto fail; + } + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, + cmp[0], inner_else)) + goto fail; + + if (!(loop_counter_addr = LLVMBuildAlloca(comp_ctx->builder, I32_TYPE, + "fill_loop_counter"))) { + aot_set_last_error("llvm build alloc failed."); + goto fail; + } + + if (!is_target_x86(comp_ctx)) { + LLVMSetAlignment(loop_counter_addr, 4); + } + + if (!LLVMBuildStore(comp_ctx->builder, offset, loop_counter_addr)) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + + BUILD_BR(fill_loop_header); + SET_BUILDER_POS(fill_loop_header); + + if (!(loop_counter_val = + LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, loop_counter_addr, + "fill_loop_counter"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + BUILD_ICMP(LLVMIntULT, loop_counter_val, boundary, cmp[0], + "cmp_loop_counter"); + BUILD_COND_BR(cmp[0], fill_loop_body, len_le_zero); + + SET_BUILDER_POS(fill_loop_body); + + if (!aot_array_obj_set_elem(comp_ctx, func_ctx, array_obj, loop_counter_val, + fill_value, array_elem_type)) + goto fail; + + if (!(loop_counter_val = LLVMBuildAdd(comp_ctx->builder, loop_counter_val, + I32_ONE, "fill_loop_counter"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + + if (!LLVMBuildStore(comp_ctx->builder, loop_counter_val, + loop_counter_addr)) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + + BUILD_BR(fill_loop_header); + + SET_BUILDER_POS(len_le_zero); + + return true; +fail: + return false; +} + +static bool +aot_call_wasm_array_obj_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef dst_obj, LLVMValueRef dst_offset, + LLVMValueRef src_obj, LLVMValueRef src_offset, + LLVMValueRef len) +{ + LLVMValueRef param_values[5], func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + + param_types[0] = GC_REF_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = GC_REF_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + ret_type = VOID_TYPE; + + GET_AOT_FUNCTION(wasm_array_obj_copy, 5); + + /* Call function wasm_array_obj_copy() */ + param_values[0] = dst_obj; + param_values[1] = dst_offset; + param_values[2] = src_obj; + param_values[3] = src_offset; + param_values[4] = len; + if (!LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 5, + "")) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_array_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, uint32 src_type_index) +{ + LLVMValueRef len, src_offset, src_obj, dst_offset, dst_obj, array_len, + cmp[4], boundary; + LLVMBasicBlockRef check_objs_succ, len_gt_zero, len_le_zero, inner_else; + int i; + + POP_I32(len); + POP_I32(src_offset); + POP_GC_REF(src_obj); + POP_I32(dst_offset); + POP_GC_REF(dst_obj); + + ADD_BASIC_BLOCK(check_objs_succ, "check array objs succ"); + MOVE_BLOCK_AFTER_CURR(check_objs_succ); + + BUILD_ISNULL(src_obj, cmp[0], "cmp_src_obj"); + BUILD_ISNULL(dst_obj, cmp[1], "cmp_dst_obj"); + + /* src_obj is null or dst_obj is null, throw exception */ + if (!(cmp[0] = LLVMBuildOr(comp_ctx->builder, cmp[0], cmp[1], ""))) { + aot_set_last_error("llvm build or failed."); + goto fail; + } + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, + cmp[0], check_objs_succ)) + goto fail; + + /* Create if block */ + ADD_BASIC_BLOCK(len_gt_zero, "len_gt_zero"); + MOVE_BLOCK_AFTER_CURR(len_gt_zero); + + /* Create else(end) block */ + ADD_BASIC_BLOCK(len_le_zero, "len_le_zero"); + MOVE_BLOCK_AFTER(len_le_zero, len_gt_zero); + + /* Create inner else block */ + ADD_BASIC_BLOCK(inner_else, "inner_else"); + MOVE_BLOCK_AFTER(inner_else, len_gt_zero); + + BUILD_ICMP(LLVMIntSGT, len, I32_ZERO, cmp[0], "cmp_len"); + BUILD_COND_BR(cmp[0], len_gt_zero, len_le_zero); + + /* Move builder to len > 0 block */ + SET_BUILDER_POS(len_gt_zero); + /* dst_offset > UINT32_MAX - len */ + if (!(boundary = LLVMBuildAdd(comp_ctx->builder, dst_offset, len, ""))) { + aot_set_last_error("llvm build failed."); + goto fail; + } + BUILD_ICMP(LLVMIntUGT, boundary, I32_CONST(UINT32_MAX), cmp[0], + "boundary_check1"); + /* dst_offset + len > wasm_array_obj_length(dst_obj) */ + if (!aot_array_obj_length(comp_ctx, dst_obj, &array_len)) + goto fail; + BUILD_ICMP(LLVMIntUGT, boundary, array_len, cmp[1], "boundary_check2"); + /* src_offset > UINT32_MAX - len */ + if (!(boundary = LLVMBuildAdd(comp_ctx->builder, src_offset, len, ""))) { + aot_set_last_error("llvm build failed."); + goto fail; + } + BUILD_ICMP(LLVMIntUGT, boundary, I32_CONST(UINT32_MAX), cmp[2], + "boundary_check3"); + /* src_offset + len > wasm_array_obj_length(src_obj) */ + if (!aot_array_obj_length(comp_ctx, src_obj, &array_len)) + goto fail; + BUILD_ICMP(LLVMIntUGT, boundary, array_len, cmp[3], "boundary_check4"); + + /* logical or above 4 boundary checks */ + for (i = 1; i < 4; ++i) { + if (!(cmp[0] = LLVMBuildOr(comp_ctx->builder, cmp[0], cmp[i], ""))) { + aot_set_last_error("llvm build failed."); + goto fail; + } + } + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, + cmp[0], inner_else)) + goto fail; + + if (!aot_call_wasm_array_obj_copy(comp_ctx, func_ctx, dst_obj, dst_offset, + src_obj, src_offset, len)) + goto fail; + + BUILD_BR(len_le_zero); + SET_BUILDER_POS(len_le_zero); + + return true; +fail: + return false; +} + +bool +aot_compile_op_array_len(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef array_obj, cmp, array_len; + LLVMBasicBlockRef check_array_obj_succ; + + POP_GC_REF(array_obj); + + ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ"); + MOVE_BLOCK_AFTER_CURR(check_array_obj_succ); + + BUILD_ISNULL(array_obj, cmp, "cmp_array_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, cmp, + check_array_obj_succ)) + goto fail; + + if (!aot_array_obj_length(comp_ctx, array_obj, &array_len)) + goto fail; + + PUSH_I32(array_len); + + return true; +fail: + return false; +} + +bool +aot_compile_op_i31_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef i31_val, i31_obj; + + POP_I32(i31_val); + + /* i31_val <<= 1 */ + if (!(i31_val = LLVMBuildShl(comp_ctx->builder, i31_val, I32_ONE, + "i31_val_shl"))) { + aot_set_last_error("llvm build shl failed."); + goto fail; + } + + /* i31_val |= 1 */ + if (!(i31_val = + LLVMBuildOr(comp_ctx->builder, i31_val, I32_ONE, "i31_val_or"))) { + aot_set_last_error("llvm build or failed."); + goto fail; + } + + if (!(i31_obj = LLVMBuildIntToPtr(comp_ctx->builder, i31_val, GC_REF_TYPE, + "i31_obj"))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } + + PUSH_GC_REF(i31_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_i31_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign) +{ + LLVMValueRef i31_obj, i31_val, cmp_i31_obj; + LLVMBasicBlockRef check_i31_obj_succ; + + POP_GC_REF(i31_obj); + + ADD_BASIC_BLOCK(check_i31_obj_succ, "check_i31_obj_succ"); + MOVE_BLOCK_AFTER_CURR(check_i31_obj_succ); + + /* Check if i31 object is NULL, throw exception if it is */ + BUILD_ISNULL(i31_obj, cmp_i31_obj, "cmp_i31_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_I31_OBJ, true, + cmp_i31_obj, check_i31_obj_succ)) { + goto fail; + } + + if (!(i31_val = LLVMBuildPtrToInt(comp_ctx->builder, i31_obj, I32_TYPE, + "i31_val"))) { + aot_set_last_error("llvm build ptr to init failed."); + goto fail; + } + + if (!sign) { + if (!(i31_val = LLVMBuildLShr(comp_ctx->builder, i31_val, I32_ONE, + "i31_value"))) { + aot_set_last_error("llvm build lshr failed."); + goto fail; + } + } + else { + if (!(i31_val = LLVMBuildAShr(comp_ctx->builder, i31_val, I32_ONE, + "i31_value"))) { + aot_set_last_error("llvm build ashr failed."); + goto fail; + } + } + + PUSH_I32(i31_val); + + return true; +fail: + return false; +} + +bool +aot_compile_op_ref_test(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 heap_type, bool nullable) +{ + LLVMValueRef gc_obj, ref_test_phi, cmp, castable; + LLVMBasicBlockRef block_curr, block_obj_non_null, block_end; + + POP_GC_REF(gc_obj); + + block_curr = CURR_BLOCK(); + + /* Create non-null object block */ + ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj"); + MOVE_BLOCK_AFTER_CURR(block_obj_non_null); + + /* Create end block */ + ADD_BASIC_BLOCK(block_end, "ref_test_end"); + MOVE_BLOCK_AFTER(block_end, block_obj_non_null); + + /* Create ref test result phi */ + SET_BUILDER_POS(block_end); + if (!(ref_test_phi = + LLVMBuildPhi(comp_ctx->builder, INT1_TYPE, "ref_test_res"))) { + aot_set_last_error("llvm build phi failed"); + return false; + } + + /* Check if gc object is NULL */ + SET_BUILDER_POS(block_curr); + BUILD_ISNULL(gc_obj, cmp, "cmp_gc_obj"); + BUILD_COND_BR(cmp, block_end, block_obj_non_null); + + if (nullable) + LLVMAddIncoming(ref_test_phi, &I1_ONE, &block_curr, 1); + else + LLVMAddIncoming(ref_test_phi, &I1_ZERO, &block_curr, 1); + + /* Move builder to non-null object block */ + SET_BUILDER_POS(block_obj_non_null); + + if (heap_type >= 0) { + if (!aot_call_aot_obj_is_instance_of(comp_ctx, func_ctx, gc_obj, + I32_CONST(heap_type), &castable)) + return false; + } + else { + if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj, + I32_CONST(heap_type), &castable)) + return false; + } + + if (!(castable = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, castable, + I8_ZERO, "castable"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + BUILD_BR(block_end); + LLVMAddIncoming(ref_test_phi, &castable, &block_obj_non_null, 1); + + SET_BUILDER_POS(block_end); + PUSH_COND(ref_test_phi); + + return true; +fail: + return false; +} + +bool +aot_compile_op_ref_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 heap_type, bool nullable) +{ + LLVMValueRef gc_obj, cmp, castable; + LLVMBasicBlockRef block_obj_non_null, block_end; + + GET_GC_REF_FROM_STACK(gc_obj); + + /* Create non null block */ + ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj"); + MOVE_BLOCK_AFTER_CURR(block_obj_non_null); + + /* Create end block */ + ADD_BASIC_BLOCK(block_end, "ref_cast_end"); + MOVE_BLOCK_AFTER(block_end, block_obj_non_null); + + BUILD_ISNULL(gc_obj, cmp, "obj_is_null"); + if (nullable) { + BUILD_COND_BR(cmp, block_end, block_obj_non_null); + } + else { + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_CAST_FAILURE, true, + cmp, block_obj_non_null)) { + return false; + } + } + + SET_BUILDER_POS(block_obj_non_null); + + if (heap_type >= 0) { + if (!aot_call_aot_obj_is_instance_of(comp_ctx, func_ctx, gc_obj, + I32_CONST(heap_type), &castable)) + return false; + } + else { + if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj, + I32_CONST(heap_type), &castable)) + return false; + } + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, castable, I8_ZERO, + "is_uncastable"))) { + aot_set_last_error("llvm build not failed"); + return false; + } + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_CAST_FAILURE, true, cmp, + block_end)) { + return false; + } + + SET_BUILDER_POS(block_end); + + return true; +fail: + return false; +} + +static bool +aot_call_wasm_externref_obj_to_internal_obj(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef externref_obj, + LLVMValueRef *gc_obj) +{ + LLVMValueRef param_values[1], func, value, res; + LLVMTypeRef param_types[1], ret_type, func_type, func_ptr_type; + + param_types[0] = GC_REF_TYPE; + ret_type = GC_REF_TYPE; + + GET_AOT_FUNCTION(wasm_externref_obj_to_internal_obj, 1); + + /* Call function wasm_externref_obj_to_internal_obj */ + param_values[0] = externref_obj; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 1, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *gc_obj = res; + + return true; +fail: + return false; +} + +bool +aot_compile_op_extern_internalize(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef externref_obj, gc_obj, cmp, internal_obj_phi; + LLVMBasicBlockRef block_curr, block_obj_non_null, block_end; + + POP_GC_REF(externref_obj); + + block_curr = CURR_BLOCK(); + + /* Create non-null object block */ + ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj"); + MOVE_BLOCK_AFTER_CURR(block_obj_non_null); + + /* Create end block */ + ADD_BASIC_BLOCK(block_end, "internalize_end"); + MOVE_BLOCK_AFTER(block_end, block_obj_non_null); + + /* Create internalized object phi */ + SET_BUILDER_POS(block_end); + if (!(internal_obj_phi = + LLVMBuildPhi(comp_ctx->builder, GC_REF_TYPE, "internal_obj"))) { + aot_set_last_error("llvm build phi failed"); + return false; + } + + /* Check if externref object is NULL */ + SET_BUILDER_POS(block_curr); + BUILD_ISNULL(externref_obj, cmp, "cmp_externref_obj"); + BUILD_COND_BR(cmp, block_end, block_obj_non_null); + LLVMAddIncoming(internal_obj_phi, &GC_REF_NULL, &block_curr, 1); + + /* Move builder to non-null object block */ + SET_BUILDER_POS(block_obj_non_null); + if (!aot_call_wasm_externref_obj_to_internal_obj(comp_ctx, func_ctx, + externref_obj, &gc_obj)) { + return false; + } + BUILD_BR(block_end); + LLVMAddIncoming(internal_obj_phi, &gc_obj, &block_obj_non_null, 1); + + /* Move builder to end block */ + SET_BUILDER_POS(block_end); + PUSH_GC_REF(internal_obj_phi); + + return true; +fail: + return false; +} + +static bool +aot_call_wasm_internal_obj_to_external_obj(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef gc_obj, + LLVMValueRef *externref_obj) +{ + LLVMValueRef param_values[2], func, value, res; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = GC_REF_TYPE; + ret_type = GC_REF_TYPE; + + GET_AOT_FUNCTION(wasm_internal_obj_to_externref_obj, 2); + + /* Call function wasm_internal_obj_to_externref_obj() */ + param_values[0] = func_ctx->exec_env; + param_values[1] = gc_obj; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 2, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *externref_obj = res; + + return true; +fail: + return false; +} + +bool +aot_compile_op_extern_externalize(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef gc_obj, cmp, external_obj_phi, externref_obj; + LLVMBasicBlockRef block_curr, block_obj_non_null, block_end; + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_GC_REF(gc_obj); + + block_curr = CURR_BLOCK(); + + /* Create non-null object block */ + ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj"); + MOVE_BLOCK_AFTER_CURR(block_obj_non_null); + + /* Create end block */ + ADD_BASIC_BLOCK(block_end, "externalize_end"); + MOVE_BLOCK_AFTER(block_end, block_obj_non_null); + + /* Create externalized object phi */ + SET_BUILDER_POS(block_end); + if (!(external_obj_phi = + LLVMBuildPhi(comp_ctx->builder, GC_REF_TYPE, "external_obj"))) { + aot_set_last_error("llvm build phi failed"); + return false; + } + + /* Check if gc object is NULL */ + SET_BUILDER_POS(block_curr); + BUILD_ISNULL(gc_obj, cmp, "cmp_gc_obj"); + BUILD_COND_BR(cmp, block_end, block_obj_non_null); + LLVMAddIncoming(external_obj_phi, &GC_REF_NULL, &block_curr, 1); + + /* Move builder to non-null object block */ + SET_BUILDER_POS(block_obj_non_null); + + if (!aot_call_wasm_internal_obj_to_external_obj(comp_ctx, func_ctx, gc_obj, + &externref_obj)) { + return false; + } + + /* Check whether failed to externalize */ + BUILD_ISNULL(externref_obj, cmp, "cmp_externref_obj"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_FAILED_TO_CREATE_EXTERNREF_OBJ, true, cmp, + block_end)) { + return false; + } + + LLVMAddIncoming(external_obj_phi, &externref_obj, &block_obj_non_null, 1); + + /* Move builder to end block */ + SET_BUILDER_POS(block_end); + PUSH_GC_REF(external_obj_phi); + + return true; +fail: + return false; +} + +#endif /* end of WASM_ENABLE_GC != 0 */ diff --git a/core/iwasm/compilation/aot_emit_gc.h b/core/iwasm/compilation/aot_emit_gc.h new file mode 100644 index 0000000000..40a3c23609 --- /dev/null +++ b/core/iwasm/compilation/aot_emit_gc.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_GC_H_ +#define _AOT_EMIT_GC_H_ + +#include "aot_compiler.h" +#include "aot_runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if WASM_ENABLE_GC != 0 + +bool +aot_call_aot_create_func_obj(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef func_idx, LLVMValueRef *p_gc_obj); + +bool +aot_call_aot_obj_is_instance_of(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, LLVMValueRef gc_obj, + LLVMValueRef heap_type, LLVMValueRef *castable); + +bool +aot_call_wasm_obj_is_type_of(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef gc_obj, LLVMValueRef heap_type, + LLVMValueRef *castable); + +bool +aot_call_aot_rtt_type_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef type_index, LLVMValueRef *rtt_type); + +bool +aot_compile_op_ref_as_non_null(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_struct_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, bool init_with_default); + +bool +aot_compile_op_struct_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, uint32 field_idx, bool sign); + +bool +aot_compile_op_struct_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, uint32 field_idx); + +bool +aot_compile_op_array_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, bool init_with_default, + bool fixed_size, uint32 array_len); + +bool +aot_compile_op_array_new_data(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 type_index, + uint32 data_seg_index); + +bool +aot_array_obj_length(AOTCompContext *comp_ctx, LLVMValueRef array_obj, + LLVMValueRef *p_array_len); + +bool +aot_array_obj_elem_addr(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef array_obj, LLVMValueRef elem_idx, + LLVMValueRef *p_elem_data, uint8 array_elem_type); + +bool +aot_compile_op_array_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, bool sign); + +bool +aot_compile_op_array_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index); + +bool +aot_compile_op_array_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index); + +bool +aot_compile_op_array_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_index, uint32 src_type_index); + +bool +aot_compile_op_array_len(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i31_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i31_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign); + +bool +aot_compile_op_ref_test(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 heap_type, bool nullable); + +bool +aot_compile_op_ref_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 heap_type, bool nullable); + +bool +aot_compile_op_extern_internalize(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_extern_externalize(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_GC_H_ */ diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c index 8b779240e7..420096c64b 100644 --- a/core/iwasm/compilation/aot_emit_memory.c +++ b/core/iwasm/compilation/aot_emit_memory.c @@ -4,308 +4,1049 @@ */ #include "aot_emit_memory.h" +#include "aot_compiler.h" #include "aot_emit_exception.h" #include "../aot/aot_runtime.h" +#include "aot_intrinsic.h" +#include "aot_emit_control.h" + +#define BUILD_IS_NOT_NULL(value, res, name) \ + do { \ + if (!(res = LLVMBuildIsNotNull(comp_ctx->builder, value, name))) { \ + aot_set_last_error("llvm build is not null failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_BR(llvm_block) \ + do { \ + if (!LLVMBuildBr(comp_ctx->builder, llvm_block)) { \ + aot_set_last_error("llvm build br failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_COND_BR(value_if, block_then, block_else) \ + do { \ + if (!LLVMBuildCondBr(comp_ctx->builder, value_if, block_then, \ + block_else)) { \ + aot_set_last_error("llvm build cond br failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_TRUNC(value, data_type) \ + do { \ + if (!(value = LLVMBuildTrunc(comp_ctx->builder, value, data_type, \ + "val_trunc"))) { \ + aot_set_last_error("llvm build trunc failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_ICMP(op, left, right, res, name) \ + do { \ + if (!(res = \ + LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \ + aot_set_last_error("llvm build icmp failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_OP(Op, left, right, res, name) \ + do { \ + if (!(res = LLVMBuild##Op(comp_ctx->builder, left, right, name))) { \ + aot_set_last_error("llvm build " #Op " fail."); \ + goto fail; \ + } \ + } while (0) + +#define ADD_BASIC_BLOCK(block, name) \ + do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + } while (0) + +#define SET_BUILD_POS(block) LLVMPositionBuilderAtEnd(comp_ctx->builder, block) + +static bool +zero_extend_u64(AOTCompContext *comp_ctx, LLVMValueRef *value, const char *name) +{ + if (comp_ctx->pointer_size == sizeof(uint64)) { + /* zero extend to uint64 if the target is 64-bit */ + *value = LLVMBuildZExt(comp_ctx->builder, *value, I64_TYPE, name); + if (!*value) { + aot_set_last_error("llvm build zero extend failed."); + return false; + } + } + return true; +} + +static LLVMValueRef +get_memory_check_bound(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 bytes) +{ + LLVMValueRef mem_check_bound = NULL; + switch (bytes) { + case 1: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_1byte; + break; + case 2: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_2bytes; + break; + case 4: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_4bytes; + break; + case 8: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_8bytes; + break; + case 16: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_16bytes; + break; + default: + bh_assert(0); + return NULL; + } + + if (func_ctx->mem_space_unchanged) + return mem_check_bound; + + if (!(mem_check_bound = LLVMBuildLoad2( + comp_ctx->builder, + (comp_ctx->pointer_size == sizeof(uint64)) ? I64_TYPE : I32_TYPE, + mem_check_bound, "mem_check_bound"))) { + aot_set_last_error("llvm build load failed."); + return NULL; + } + return mem_check_bound; +} + +#if defined(_WIN32) || defined(_WIN32_) +static inline int +ffs(int n) +{ + int pos = 0; + + if (n == 0) + return 0; -#define BUILD_ICMP(op, left, right, res, name) do { \ - if (!(res = LLVMBuildICmp(comp_ctx->builder, op, \ - left, right, name))) { \ - aot_set_last_error("llvm build icmp failed."); \ - goto fail; \ - } \ - } while (0) - -#define BUILD_OP(Op, left, right, res, name) do { \ - if (!(res = LLVMBuild##Op(comp_ctx->builder, \ - left, right, name))) { \ - aot_set_last_error("llvm build " #Op " fail."); \ - goto fail; \ - } \ - } while (0) - -#define BUILD_COND_BR(cmp_val, then_block, else_block) do { \ - if (!LLVMBuildCondBr(comp_ctx->builder, cmp_val, \ - then_block, else_block)) { \ - aot_set_last_error("llvm build cond br failed."); \ - goto fail; \ - } \ - } while (0) - -#define ADD_BASIC_BLOCK(block, name) do { \ - if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ - func_ctx->func, \ - name))) { \ - aot_set_last_error("llvm add basic block failed."); \ - goto fail; \ - } \ - } while (0) - -#define SET_BUILD_POS(block) \ - LLVMPositionBuilderAtEnd(comp_ctx->builder, block) + while (!(n & 1)) { + pos++; + n >>= 1; + } + return pos + 1; +} +#endif static LLVMValueRef -check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 offset, uint32 bytes) -{ - LLVMValueRef offset_const = I32_CONST(offset); - LLVMValueRef size_const = I32_CONST(bytes); - LLVMValueRef addr, maddr, moffset; - LLVMValueRef cmp, phi; - LLVMValueRef mem_base_addr, mem_data_size; - LLVMValueRef heap_base_addr, heap_base_offset; - LLVMValueRef mem_offset_max = NULL, heap_offset_max = NULL; - LLVMBasicBlockRef check_mem_space, check_heap_space, check_succ; - LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); +get_memory_curr_page_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +#if WASM_ENABLE_SHARED_HEAP != 0 +uint32 +get_module_inst_extra_offset(AOTCompContext *comp_ctx); + +#define BUILD_LOAD_PTR(ptr, data_type, res) \ + do { \ + if (!(res = LLVMBuildLoad2(comp_ctx->builder, data_type, ptr, \ + "load_value"))) { \ + aot_set_last_error("llvm build load failed"); \ + goto fail; \ + } \ + } while (0) + +/* Update last used shared heap info(alloc ptr) in function ctx: + * 1. shared_heap_start_off 2. shared_heap_end_off 3. shared_heap_base_addr_adj + */ +bool +aot_check_shared_heap_chain_and_update(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMBasicBlockRef check_succ, + LLVMValueRef start_offset, + LLVMValueRef bytes, bool is_memory64) +{ + LLVMValueRef param_values[7], ret_value, func, value, cmp; + LLVMTypeRef param_types[7], ret_type, func_type, func_ptr_type; - CHECK_LLVM_CONST(offset_const); - CHECK_LLVM_CONST(size_const); + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INTPTR_T_TYPE; + param_types[2] = SIZE_T_TYPE; + param_types[3] = INTPTR_T_PTR_TYPE; + param_types[4] = INTPTR_T_PTR_TYPE; + param_types[5] = INT8_PTR_TYPE; + param_types[6] = INT8_TYPE; + ret_type = INT8_TYPE; - heap_base_addr = func_ctx->heap_base_addr; - heap_base_offset = func_ctx->heap_base_offset; + GET_AOT_FUNCTION(wasm_runtime_check_and_update_last_used_shared_heap, 7); - POP_I32(addr); - BUILD_OP(Add, offset_const, addr, moffset, "moffset"); + /* Call function */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = start_offset; + param_values[2] = bytes; + /* pass alloc ptr */ + param_values[3] = func_ctx->shared_heap_start_off; + param_values[4] = func_ctx->shared_heap_end_off; + param_values[5] = func_ctx->shared_heap_base_addr_adj; + param_values[6] = is_memory64 ? I8_ONE : I8_ZERO; + + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 7, "call"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } - /* return addres directly if constant offset and inside memory space */ - if (LLVMIsConstant(moffset)) { - uint32 memory_offset = (uint32)LLVMConstIntGetZExtValue(moffset); - uint32 init_page_count = comp_ctx->comp_data->mem_init_page_count; - if (init_page_count > 0 - && memory_offset <= comp_ctx->comp_data->num_bytes_per_page - * init_page_count - bytes) { - /* inside memory space */ - if (!func_ctx->mem_space_unchanged) { - if (!(mem_base_addr = LLVMBuildLoad(comp_ctx->builder, - func_ctx->mem_base_addr, - "mem_base"))) { - aot_set_last_error("llvm build load failed."); - return NULL; - } - } - else { - mem_base_addr = func_ctx->mem_base_addr; - } + BUILD_ICMP(LLVMIntEQ, ret_value, I8_ZERO, cmp, "shared_heap_oob"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, true, cmp, + check_succ)) { + goto fail; + } - /* maddr = mem_base_addr + moffset */ - if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, - mem_base_addr, - &moffset, 1, "maddr"))) { - aot_set_last_error("llvm build add failed."); - goto fail; - } - return maddr; - } + return true; +fail: + return false; +} + +/* + * Setup the basic blocks for shared heap and shared chain memory checks. + * + * Arguments: + * block_curr: The current basic block. + * app_addr_in_cache_shared_heap: Output, block for cache shared heap. + * app_addr_in_linear_mem: Output, block for linear memory. + * app_addr_in_shared_heap_chain: Output, block for shared heap chain + * (only for shared heap chain). + * check_shared_heap_chain: Output, block for checking shared heap chain + * (only for shared heap chain). + * + * Topology: + * If enable_shared_heap: + * block_curr -> app_addr_in_cache_shared_heap + * -> app_addr_in_linear_mem + * If enable_shared_chain: + * block_curr -> app_addr_in_shared_heap_chain + * -> app_addr_in_cache_shared_heap + * -> check_shared_heap_chain + * -> app_addr_in_linear_mem + */ +static bool +setup_shared_heap_blocks(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMBasicBlockRef block_curr, + LLVMBasicBlockRef *app_addr_in_cache_shared_heap, + LLVMBasicBlockRef *app_addr_in_linear_mem, + LLVMBasicBlockRef *app_addr_in_shared_heap_chain, + LLVMBasicBlockRef *check_shared_heap_chain) +{ + ADD_BASIC_BLOCK(*app_addr_in_cache_shared_heap, + "app_addr_in_cache_shared_heap"); + ADD_BASIC_BLOCK(*app_addr_in_linear_mem, "app_addr_in_linear_mem"); + + if (comp_ctx->enable_shared_heap) { + LLVMMoveBasicBlockAfter(*app_addr_in_cache_shared_heap, block_curr); + LLVMMoveBasicBlockAfter(*app_addr_in_linear_mem, + *app_addr_in_cache_shared_heap); + } + else if (comp_ctx->enable_shared_chain) { + ADD_BASIC_BLOCK(*app_addr_in_shared_heap_chain, + "app_addr_in_shared_heap_chain"); + ADD_BASIC_BLOCK(*check_shared_heap_chain, "check_shared_heap_chain"); + LLVMMoveBasicBlockAfter(*app_addr_in_shared_heap_chain, block_curr); + LLVMMoveBasicBlockAfter(*app_addr_in_cache_shared_heap, + *app_addr_in_shared_heap_chain); + LLVMMoveBasicBlockAfter(*check_shared_heap_chain, + *app_addr_in_cache_shared_heap); + LLVMMoveBasicBlockAfter(*app_addr_in_linear_mem, + *app_addr_in_cache_shared_heap); } - /* Add basic blocks */ - ADD_BASIC_BLOCK(check_heap_space, "check_heap_space"); - ADD_BASIC_BLOCK(check_succ, "check_succ"); + return true; +fail: + return false; +} + +/* + * Build a branch to check if start_offset is in the shared heap chain region. + * + * Arguments: + * start_offset: The offset to check. + * app_addr_in_shared_heap_chain: Block to branch if in shared heap chain. + * app_addr_in_linear_mem: Block to branch if not in shared heap chain. + */ +static bool +build_check_app_addr_in_shared_heap_chain( + AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef start_offset, LLVMBasicBlockRef app_addr_in_shared_heap_chain, + LLVMBasicBlockRef app_addr_in_linear_mem) +{ + LLVMValueRef is_in_shared_heap = NULL; + + /* Use start_offset > func_ctx->shared_heap_head_start_off to test + * start_off falls in shared heap chain memory region. The shared heap + * chain oob will be detected in app_addr_in_shared_heap block or + * aot_check_shared_heap_chain_and_update function + */ + BUILD_ICMP(LLVMIntUGT, start_offset, func_ctx->shared_heap_head_start_off, + is_in_shared_heap, "shared_heap_lb_cmp"); + BUILD_COND_BR(is_in_shared_heap, app_addr_in_shared_heap_chain, + app_addr_in_linear_mem); + + SET_BUILD_POS(app_addr_in_shared_heap_chain); + + return true; +fail: + return false; +} + +/* + * Build the conditional branch for cache shared heap or shared heap chain. + * + * Arguments: + * cmp: The condition for being in cache shared heap. + * app_addr_in_cache_shared_heap: Block for cache shared heap. + * app_addr_in_linear_mem: Block for linear memory. + * check_shared_heap_chain: Block for checking shared heap chain. + * bytes: The access size in bytes. + * start_offset: The offset to check. + * is_memory64: Whether memory is 64-bit. + */ +static bool +build_shared_heap_conditional_branching( + AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMValueRef cmp, + LLVMBasicBlockRef app_addr_in_cache_shared_heap, + LLVMBasicBlockRef app_addr_in_linear_mem, + LLVMBasicBlockRef check_shared_heap_chain, LLVMValueRef bytes, + LLVMValueRef start_offset, bool is_memory64) +{ + if (comp_ctx->enable_shared_heap) { + BUILD_COND_BR(cmp, app_addr_in_cache_shared_heap, + app_addr_in_linear_mem); + } + else if (comp_ctx->enable_shared_chain) { + BUILD_COND_BR(cmp, app_addr_in_cache_shared_heap, + check_shared_heap_chain); + SET_BUILD_POS(check_shared_heap_chain); + if (!aot_check_shared_heap_chain_and_update( + comp_ctx, func_ctx, app_addr_in_cache_shared_heap, start_offset, + bytes, is_memory64)) + goto fail; + } + return true; +fail: + return false; +} + +/* + * Get the native address in the cache shared heap. + * + * Arguments: + * start_offset: The offset to use for address calculation. + * maddr: Output, the native address that in the cache shared heap. + */ +static bool +build_get_maddr_in_cache_shared_heap(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef start_offset, + LLVMValueRef *maddr) +{ + LLVMValueRef shared_heap_base_addr_adj; + /* load the local variable */ + BUILD_LOAD_PTR(func_ctx->shared_heap_base_addr_adj, INT8_PTR_TYPE, + shared_heap_base_addr_adj); + if (!(*maddr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, shared_heap_base_addr_adj, + &start_offset, 1, "maddr_cache_shared_heap"))) { + aot_set_last_error("llvm build inbounds gep failed"); + goto fail; + } + + return true; +fail: + return false; +} + +/* + * Check for memory overflow in shared heap for normal memory access. + * + * Arguments: + * block_curr: The current basic block. + * block_maddr_phi: The phi block for memory address. + * maddr_phi: The phi node for memory address. + * start_offset: The first offset to check. + * mem_base_addr: The base address of memory. Only used with segue. + * bytes_u32: The access size in bytes. + * is_memory64: Whether memory is wasm64 memory. + * is_target_64bit: Whether target is 64-bit. + * enable_segue: Whether to use segment register addressing. + */ +static bool +aot_check_shared_heap_memory_overflow( + AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMBasicBlockRef block_curr, LLVMBasicBlockRef block_maddr_phi, + LLVMValueRef maddr_phi, LLVMValueRef start_offset, + LLVMValueRef mem_base_addr, uint32 bytes_u32, bool is_memory64, + bool is_target_64bit, bool enable_segue) +{ + LLVMBasicBlockRef app_addr_in_cache_shared_heap, app_addr_in_linear_mem; + LLVMBasicBlockRef app_addr_in_shared_heap_chain = NULL, + check_shared_heap_chain = NULL; + LLVMValueRef cmp, cmp1, cmp2, shared_heap_start_off, shared_heap_end_off, + shared_heap_check_bound, maddr = NULL; + /* On 64/32-bit target, the offset is 64/32-bit */ + LLVMTypeRef offset_type = is_target_64bit ? I64_TYPE : I32_TYPE; + LLVMValueRef length, bytes; + + if (!setup_shared_heap_blocks( + comp_ctx, func_ctx, block_curr, &app_addr_in_cache_shared_heap, + &app_addr_in_linear_mem, &app_addr_in_shared_heap_chain, + &check_shared_heap_chain)) + goto fail; + LLVMMoveBasicBlockAfter(block_maddr_phi, app_addr_in_linear_mem); + + /* Early branching when it's not in shared heap chain at all */ + if (comp_ctx->enable_shared_chain + && !build_check_app_addr_in_shared_heap_chain( + comp_ctx, func_ctx, start_offset, app_addr_in_shared_heap_chain, + app_addr_in_linear_mem)) + goto fail; - LLVMMoveBasicBlockAfter(check_heap_space, block_curr); - LLVMMoveBasicBlockAfter(check_succ, check_heap_space); + /* Load the local variable of the function */ + BUILD_LOAD_PTR(func_ctx->shared_heap_start_off, offset_type, + shared_heap_start_off); + BUILD_LOAD_PTR(func_ctx->shared_heap_end_off, offset_type, + shared_heap_end_off); + /* Check if the app address is in the cache shared heap range. + * If yes, branch to the cache branch; if not, check the shared heap chain + */ + BUILD_ICMP(LLVMIntUGE, start_offset, shared_heap_start_off, cmp, + "cmp_cache_shared_heap_start"); + length = + is_target_64bit ? I64_CONST(bytes_u32 - 1) : I32_CONST(bytes_u32 - 1); + CHECK_LLVM_CONST(length); + BUILD_OP(Sub, shared_heap_end_off, length, shared_heap_check_bound, + "cache_shared_heap_end_bound"); + BUILD_ICMP(LLVMIntULE, start_offset, shared_heap_check_bound, cmp1, + "cmp_cache_shared_heap_end"); + BUILD_OP(And, cmp, cmp1, cmp2, "is_in_cache_shared_heap"); + /* Conditional branching based on whether in cached shared heap */ + bytes = is_target_64bit ? I64_CONST(bytes_u32) : I32_CONST(bytes_u32); + if (!build_shared_heap_conditional_branching( + comp_ctx, func_ctx, cmp2, app_addr_in_cache_shared_heap, + app_addr_in_linear_mem, check_shared_heap_chain, bytes, + start_offset, is_memory64)) + goto fail; - /* Add return maddress phi for check_succ block */ - SET_BUILD_POS(check_succ); - if (!(phi = LLVMBuildPhi(comp_ctx->builder, - INT8_PTR_TYPE, "maddr_phi"))) { - aot_set_last_error("llvm build phi failed."); + SET_BUILD_POS(app_addr_in_cache_shared_heap); + if (!build_get_maddr_in_cache_shared_heap(comp_ctx, func_ctx, start_offset, + &maddr)) goto fail; + + if (enable_segue) { + LLVMValueRef mem_base_addr_u64, maddr_u64, offset_to_mem_base; + if (!(maddr_u64 = LLVMBuildPtrToInt(comp_ctx->builder, maddr, I64_TYPE, + "maddr_u64")) + || !(mem_base_addr_u64 = + LLVMBuildPtrToInt(comp_ctx->builder, mem_base_addr, + I64_TYPE, "mem_base_addr_u64"))) { + aot_set_last_error("llvm build ptr to int failed"); + goto fail; + } + if (!(offset_to_mem_base = + LLVMBuildSub(comp_ctx->builder, maddr_u64, mem_base_addr_u64, + "offset_to_mem_base"))) { + aot_set_last_error("llvm build sub failed"); + goto fail; + } + if (!(maddr = LLVMBuildIntToPtr(comp_ctx->builder, offset_to_mem_base, + INT8_PTR_TYPE_GS, + "maddr_shared_heap_segue"))) { + aot_set_last_error("llvm build int to ptr failed."); + goto fail; + } } - SET_BUILD_POS(block_curr); - /* Get memory data size */ - if (!func_ctx->mem_space_unchanged) { - if (!(mem_data_size = LLVMBuildLoad(comp_ctx->builder, - func_ctx->mem_data_size, - "mem_data_size"))) { - aot_set_last_error("llvm build load failed."); + LLVMAddIncoming(maddr_phi, &maddr, &app_addr_in_cache_shared_heap, 1); + BUILD_BR(block_maddr_phi); + SET_BUILD_POS(app_addr_in_linear_mem); + + return true; +fail: + return false; +} + +/* + * Check for memory overflow in shared heap for bulk memory access. + * + * Arguments: + * block_curr: The current basic block. + * block_maddr_phi: The phi block for memory address. + * check_succ: The block to branch to on success. + * maddr_phi: The phi node for memory address. + * start_offset: The offset to check. + * max_addr: The maximum address to check. + * bytes: The access size in bytes (LLVMValueRef). + * is_memory64: Whether memory is wasm64 memory. + * is_target_64bit: Whether target is 64-bit. + */ +static bool +aot_check_bulk_memory_shared_heap_memory_overflow( + AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMBasicBlockRef block_curr, LLVMBasicBlockRef block_maddr_phi, + LLVMBasicBlockRef check_succ, LLVMValueRef maddr_phi, + LLVMValueRef start_offset, LLVMValueRef max_addr, LLVMValueRef bytes, + bool is_memory64, bool is_target_64bit) +{ + LLVMBasicBlockRef app_addr_in_cache_shared_heap, app_addr_in_linear_mem; + LLVMBasicBlockRef app_addr_in_shared_heap_chain = NULL, + check_shared_heap_chain = NULL; + LLVMValueRef cmp, cmp1, cmp2, shared_heap_start_off, shared_heap_end_off, + maddr = NULL, max_offset; + /* On 64/32-bit target, the offset is 64/32-bit */ + LLVMTypeRef offset_type = is_target_64bit ? I64_TYPE : I32_TYPE; + + if (!setup_shared_heap_blocks( + comp_ctx, func_ctx, block_curr, &app_addr_in_cache_shared_heap, + &app_addr_in_linear_mem, &app_addr_in_shared_heap_chain, + &check_shared_heap_chain)) + goto fail; + LLVMMoveBasicBlockAfter(block_maddr_phi, check_succ); + + /* Early branching when it's not in shared heap chain at all */ + if (comp_ctx->enable_shared_chain + && !build_check_app_addr_in_shared_heap_chain( + comp_ctx, func_ctx, start_offset, app_addr_in_shared_heap_chain, + app_addr_in_linear_mem)) + goto fail; + + /* Load the local variable of the function */ + BUILD_LOAD_PTR(func_ctx->shared_heap_start_off, offset_type, + shared_heap_start_off); + BUILD_LOAD_PTR(func_ctx->shared_heap_end_off, offset_type, + shared_heap_end_off); + /* Check if the app address is in the cache shared heap range. + * If yes, branch to the cache branch; if not, check the shared heap chain + */ + BUILD_ICMP(LLVMIntUGE, start_offset, shared_heap_start_off, cmp, + "cmp_cache_shared_heap_start"); + BUILD_OP(Add, max_addr, is_target_64bit ? I64_NEG_ONE : I32_NEG_ONE, + max_offset, "max_offset"); + BUILD_ICMP(LLVMIntULE, max_offset, shared_heap_end_off, cmp1, + "cmp_cache_shared_heap_end"); + BUILD_OP(And, cmp, cmp1, cmp2, "is_in_cache_shared_heap"); + /* Conditional branching based on whether in cached shared heap */ + if (!build_shared_heap_conditional_branching( + comp_ctx, func_ctx, cmp2, app_addr_in_cache_shared_heap, + app_addr_in_linear_mem, check_shared_heap_chain, bytes, + start_offset, is_memory64)) + goto fail; + + SET_BUILD_POS(app_addr_in_cache_shared_heap); + if (!build_get_maddr_in_cache_shared_heap(comp_ctx, func_ctx, start_offset, + &maddr)) + goto fail; + + LLVMAddIncoming(maddr_phi, &maddr, &app_addr_in_cache_shared_heap, 1); + BUILD_BR(block_maddr_phi); + SET_BUILD_POS(app_addr_in_linear_mem); + + return true; +fail: + return false; +} +#endif + +LLVMValueRef +aot_check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + mem_offset_t offset, uint32 bytes, bool enable_segue, + unsigned int *alignp) +{ + LLVMValueRef offset_const = + MEMORY64_COND_VALUE(I64_CONST(offset), I32_CONST(offset)); + LLVMValueRef addr, maddr, offset1, cmp1, cmp; + LLVMValueRef mem_base_addr, mem_check_bound; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_succ; + AOTValue *aot_value_top; + uint32 local_idx_of_aot_value = 0; + uint64 const_value; + bool is_target_64bit, is_local_of_aot_value = false; + bool is_const = false; +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory = + comp_ctx->comp_data->memories[0].flags & SHARED_MEMORY_FLAG; +#endif +#if WASM_ENABLE_MEMORY64 == 0 + bool is_memory64 = false; +#else + bool is_memory64 = IS_MEMORY64; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + LLVMValueRef maddr_phi = NULL; + LLVMBasicBlockRef block_maddr_phi = NULL; +#endif + + is_target_64bit = (comp_ctx->pointer_size == sizeof(uint64)) ? true : false; + + if (comp_ctx->is_indirect_mode + && aot_intrinsic_check_capability( + comp_ctx, MEMORY64_COND_VALUE("i64.const", "i32.const"))) { + WASMValue wasm_value; +#if WASM_ENABLE_MEMORY64 != 0 + if (IS_MEMORY64) { + wasm_value.i64 = offset; + } + else +#endif + { + wasm_value.i32 = (int32)offset; + } + offset_const = aot_load_const_from_table( + comp_ctx, func_ctx->native_symbol, &wasm_value, + MEMORY64_COND_VALUE(VALUE_TYPE_I64, VALUE_TYPE_I32)); + if (!offset_const) { return NULL; } } else { - mem_data_size = func_ctx->mem_data_size; + CHECK_LLVM_CONST(offset_const); } - if (comp_ctx->comp_data->mem_init_page_count == 0) { - ADD_BASIC_BLOCK(check_mem_space, "check_mem_space"); - LLVMMoveBasicBlockAfter(check_mem_space, block_curr); + /* Get memory base address and memory data size */ + if (func_ctx->mem_space_unchanged +#if WASM_ENABLE_SHARED_MEMORY != 0 + || is_shared_memory +#endif + ) { + mem_base_addr = func_ctx->mem_info[0].mem_base_addr; + } + else { + if (!(mem_base_addr = LLVMBuildLoad2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->mem_info[0].mem_base_addr, "mem_base"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } - /* if mem_data_size is zero, check heap space */ - BUILD_ICMP(LLVMIntEQ, mem_data_size, I32_ZERO, cmp, - "cmp_mem_data_size"); - BUILD_COND_BR(cmp, check_heap_space, check_mem_space); - SET_BUILD_POS(check_mem_space); + aot_value_top = + func_ctx->block_stack.block_list_end->value_stack.value_list_end; + if (aot_value_top) { + /* aot_value_top is freed in the following POP_I32(addr), + so save its fields here for further use */ + is_local_of_aot_value = aot_value_top->is_local; + is_const = aot_value_top->is_const; + local_idx_of_aot_value = aot_value_top->local_idx; + const_value = aot_value_top->const_value; } - /* Get memory base address */ - if (!func_ctx->mem_space_unchanged) { - if (!(mem_base_addr = LLVMBuildLoad(comp_ctx->builder, - func_ctx->mem_base_addr, - "mem_base"))) { - aot_set_last_error("llvm build load failed."); - return NULL; + POP_MEM_OFFSET(addr); + + /* + * Note: not throw the integer-overflow-exception here since it must + * have been thrown when converting float to integer before + */ + /* return address directly if constant offset and inside memory space */ + if (LLVMIsEfficientConstInt(addr) || is_const) { + uint64 value; + if (LLVMIsEfficientConstInt(addr)) { + value = (uint64)LLVMConstIntGetZExtValue(addr); + } + else { + value = const_value; + } + uint64 mem_offset = value + (uint64)offset; + uint32 num_bytes_per_page = + comp_ctx->comp_data->memories[0].num_bytes_per_page; + uint32 init_page_count = + comp_ctx->comp_data->memories[0].init_page_count; + uint64 mem_data_size = (uint64)num_bytes_per_page * init_page_count; + + if (alignp != NULL) { + /* + * A note about max_align below: + * the assumption here is the base address of a linear memory + * has the natural alignment. for platforms using mmap, it can + * be even larger. for now, use a conservative value. + */ + const unsigned int max_align = 8; + int shift = ffs((int)(unsigned int)mem_offset); + if (shift == 0) { + *alignp = max_align; + } + else { + unsigned int align = 1U << (shift - 1); + if (align > max_align) { + align = max_align; + } + *alignp = align; + } + } + if (mem_offset + bytes <= mem_data_size) { + /* inside memory space */ + if (comp_ctx->pointer_size == sizeof(uint64)) + offset1 = I64_CONST(mem_offset); + else + offset1 = I32_CONST((uint32)mem_offset); + CHECK_LLVM_CONST(offset1); + if (!enable_segue) { + if (!(maddr = LLVMBuildInBoundsGEP2(comp_ctx->builder, + INT8_TYPE, mem_base_addr, + &offset1, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + } + else { + if (!(maddr = LLVMBuildIntToPtr(comp_ctx->builder, offset1, + INT8_PTR_TYPE_GS, "maddr"))) { + aot_set_last_error("llvm build IntToPtr failed."); + goto fail; + } + } + return maddr; } } - else { - mem_base_addr = func_ctx->mem_base_addr; + else if (alignp != NULL) { + *alignp = 1; } - /* maddr = mem_base_addr + moffset */ - if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr, - &moffset, 1, "maddr"))) { - aot_set_last_error("llvm build add failed."); - goto fail; + /* The overflow check needs to be done under following conditions: + * 1. In 64-bit target, offset and addr will be extended to 64-bit + * 1.1 offset + addr can overflow when it's memory64 + * 1.2 no overflow when it's memory32 + * 2. In 32-bit target, offset and addr will be 32-bit + * 2.1 offset + addr can overflow when it's memory32 + */ + if (is_target_64bit) { + if (!(offset_const = LLVMBuildZExt(comp_ctx->builder, offset_const, + I64_TYPE, "offset_i64")) + || !(addr = LLVMBuildZExt(comp_ctx->builder, addr, I64_TYPE, + "addr_i64"))) { + aot_set_last_error("llvm build zero extend failed."); + goto fail; + } + } + + /* offset1 = offset + addr; */ + BUILD_OP(Add, offset_const, addr, offset1, "offset1"); + + /* 1.1 offset + addr can overflow when it's memory64 + * 2.1 Or when it's on 32-bit platform */ + if (is_memory64 || !is_target_64bit) { + /* Check whether integer overflow occurs in offset + addr */ + LLVMBasicBlockRef check_integer_overflow_end; + ADD_BASIC_BLOCK(check_integer_overflow_end, + "check_integer_overflow_end"); + LLVMMoveBasicBlockAfter(check_integer_overflow_end, block_curr); + + BUILD_ICMP(LLVMIntULT, offset1, offset_const, cmp1, "cmp1"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, true, cmp1, + check_integer_overflow_end)) { + goto fail; + } + SET_BUILD_POS(check_integer_overflow_end); + block_curr = check_integer_overflow_end; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (comp_ctx->enable_shared_heap + || comp_ctx->enable_shared_chain /* TODO: && mem_idx == 0 */) { + ADD_BASIC_BLOCK(block_maddr_phi, "maddr_phi"); + SET_BUILD_POS(block_maddr_phi); + if (!(maddr_phi = + LLVMBuildPhi(comp_ctx->builder, + enable_segue ? INT8_PTR_TYPE_GS : INT8_PTR_TYPE, + "maddr_phi"))) { + aot_set_last_error("llvm build phi failed"); + goto fail; + } + SET_BUILD_POS(block_curr); + + if (!aot_check_shared_heap_memory_overflow( + comp_ctx, func_ctx, block_curr, block_maddr_phi, maddr_phi, + offset1, mem_base_addr, bytes, is_memory64, is_target_64bit, + enable_segue)) { + goto fail; + } + } +#endif + + if (comp_ctx->enable_bound_check + && !(is_local_of_aot_value + && aot_checked_addr_list_find(func_ctx, local_idx_of_aot_value, + offset, bytes))) { + uint32 init_page_count = + comp_ctx->comp_data->memories[0].init_page_count; + if (init_page_count == 0) { + LLVMValueRef mem_size; + + if (!(mem_size = get_memory_curr_page_count(comp_ctx, func_ctx))) { + goto fail; + } + BUILD_ICMP(LLVMIntEQ, mem_size, + MEMORY64_COND_VALUE(I64_ZERO, I32_ZERO), cmp, "is_zero"); + ADD_BASIC_BLOCK(check_succ, "check_mem_size_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, true, cmp, + check_succ)) { + goto fail; + } + + SET_BUILD_POS(check_succ); + block_curr = check_succ; + } + + if (!(mem_check_bound = + get_memory_check_bound(comp_ctx, func_ctx, bytes))) { + goto fail; + } + + BUILD_ICMP(LLVMIntUGT, offset1, mem_check_bound, cmp, "cmp"); + + /* Add basic blocks */ + ADD_BASIC_BLOCK(check_succ, "check_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, true, cmp, + check_succ)) { + goto fail; + } + + SET_BUILD_POS(check_succ); + + if (is_local_of_aot_value) { + if (!aot_checked_addr_list_add(func_ctx, local_idx_of_aot_value, + offset, bytes)) + goto fail; + } } - block_curr = LLVMGetInsertBlock(comp_ctx->builder); - LLVMAddIncoming(phi, &maddr, &block_curr, 1); - if (!func_ctx->mem_space_unchanged) { - /* mem_offset_max = mem_data_size - bytes to load/read */ - if (!(mem_offset_max = LLVMBuildSub(comp_ctx->builder, - mem_data_size, size_const, - "mem_offset_max"))) { - aot_set_last_error("llvm build sub failed."); + if (!enable_segue) { + /* maddr = mem_base_addr + offset1 */ + if (!(maddr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + mem_base_addr, &offset1, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); goto fail; } } else { - if (bytes == 1) - mem_offset_max = func_ctx->mem_bound_1_byte; - else if (bytes == 2) - mem_offset_max = func_ctx->mem_bound_2_bytes; - else if (bytes == 4) - mem_offset_max = func_ctx->mem_bound_4_bytes; - else if (bytes == 8) - mem_offset_max = func_ctx->mem_bound_8_bytes; - } - - /* in linear memory if (uint32)moffset <= (uint32)mem_offset_max, - else check heap space */ - BUILD_ICMP(LLVMIntULE, moffset, mem_offset_max, cmp, "cmp_mem_offset"); - - /* Create condtion br */ - BUILD_COND_BR(cmp, check_succ, check_heap_space); - - /* Start to translate the check_heap_space block */ - SET_BUILD_POS(check_heap_space); - - /* moffset -= heap_base_offset */ - if (!(moffset = LLVMBuildSub(comp_ctx->builder, - moffset, heap_base_offset, - "moffset_to_heap"))) { - aot_set_last_error("llvm build sub failed."); - goto fail; + LLVMValueRef maddr_base; + + if (!(maddr_base = LLVMBuildIntToPtr(comp_ctx->builder, addr, + INT8_PTR_TYPE_GS, "maddr_base"))) { + aot_set_last_error("llvm build int to ptr failed."); + goto fail; + } + if (!(maddr = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + maddr_base, &offset_const, 1, + "maddr"))) { + aot_set_last_error("llvm build inboundgep failed."); + goto fail; + } } - /* maddr = heap_base_addr + moffset */ - if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, heap_base_addr, - &moffset, 1, "maddr"))) { - aot_set_last_error("llvm build add failed."); +#if WASM_ENABLE_SHARED_HEAP != 0 + if (comp_ctx->enable_shared_heap + || comp_ctx->enable_shared_chain /* TODO: && mem_idx == 0 */) { + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMAddIncoming(maddr_phi, &maddr, &block_curr, 1); + if (!LLVMBuildBr(comp_ctx->builder, block_maddr_phi)) { + aot_set_last_error("llvm build br failed"); + goto fail; + } + SET_BUILD_POS(block_maddr_phi); + return maddr_phi; + } + else +#endif + return maddr; +fail: + return NULL; +} + +#define BUILD_PTR_CAST(ptr_type) \ + do { \ + if (!(maddr = LLVMBuildBitCast(comp_ctx->builder, maddr, ptr_type, \ + "data_ptr"))) { \ + aot_set_last_error("llvm build bit cast failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_LOAD(data_type) \ + do { \ + if (!(value = LLVMBuildLoad2(comp_ctx->builder, data_type, maddr, \ + "data"))) { \ + aot_set_last_error("llvm build load failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(value, known_align); \ + } while (0) + +#define BUILD_STORE() \ + do { \ + LLVMValueRef res; \ + if (!(res = LLVMBuildStore(comp_ctx->builder, value, maddr))) { \ + aot_set_last_error("llvm build store failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(res, known_align); \ + } while (0) + +#define BUILD_SIGN_EXT(dst_type) \ + do { \ + if (!(value = LLVMBuildSExt(comp_ctx->builder, value, dst_type, \ + "data_s_ext"))) { \ + aot_set_last_error("llvm build sign ext failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_ZERO_EXT(dst_type) \ + do { \ + if (!(value = LLVMBuildZExt(comp_ctx->builder, value, dst_type, \ + "data_z_ext"))) { \ + aot_set_last_error("llvm build zero ext failed."); \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_SHARED_MEMORY != 0 || WASM_ENABLE_STRINGREF != 0 +bool +check_memory_alignment(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef addr, uint32 align) +{ + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_align_succ; + LLVMValueRef align_mask = I32_CONST(((uint32)1 << align) - 1); + LLVMValueRef res; + + CHECK_LLVM_CONST(align_mask); + + /* Convert pointer to int */ + if (!(addr = LLVMBuildPtrToInt(comp_ctx->builder, addr, I32_TYPE, + "address"))) { + aot_set_last_error("llvm build ptr to int failed."); goto fail; } - block_curr = LLVMGetInsertBlock(comp_ctx->builder); - LLVMAddIncoming(phi, &maddr, &block_curr, 1); - - /* heap space base addr and size is unchanged, - the heap boundary is unchanged also. */ - if (bytes == 1) - heap_offset_max = func_ctx->heap_bound_1_byte; - else if (bytes == 2) - heap_offset_max = func_ctx->heap_bound_2_bytes; - else if (bytes == 4) - heap_offset_max = func_ctx->heap_bound_4_bytes; - else if (bytes == 8) - heap_offset_max = func_ctx->heap_bound_8_bytes; - - /* in heap space if (uint32)moffset <= (uint32)heap_offset_max, - else throw exception */ - BUILD_ICMP(LLVMIntUGT, moffset, heap_offset_max, cmp, "cmp_heap_offset"); - if (!aot_emit_exception(comp_ctx, func_ctx, - EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, - true, cmp, check_succ)) + + /* The memory address should be aligned */ + BUILD_OP(And, addr, align_mask, res, "and"); + BUILD_ICMP(LLVMIntNE, res, I32_ZERO, res, "cmp"); + + /* Add basic blocks */ + ADD_BASIC_BLOCK(check_align_succ, "check_align_succ"); + LLVMMoveBasicBlockAfter(check_align_succ, block_curr); + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_UNALIGNED_ATOMIC, true, + res, check_align_succ)) { goto fail; + } - SET_BUILD_POS(check_succ); - return phi; + SET_BUILD_POS(check_align_succ); + + return true; fail: - return NULL; + return false; } - -#define BUILD_PTR_CAST(ptr_type) do { \ - if (!(maddr = LLVMBuildBitCast(comp_ctx->builder, maddr,\ - ptr_type, "data_ptr"))) {\ - aot_set_last_error("llvm build bit cast failed."); \ - goto fail; \ - } \ - } while (0) - -#define BUILD_LOAD() do { \ - if (!(value = LLVMBuildLoad(comp_ctx->builder, maddr, \ - "data"))) { \ - aot_set_last_error("llvm build load failed."); \ - goto fail; \ - } \ - LLVMSetAlignment(value, 1); \ - } while (0) - -#define BUILD_TRUNC(data_type) do { \ - if (!(value = LLVMBuildTrunc(comp_ctx->builder, value, \ - data_type, "val_trunc"))){ \ - aot_set_last_error("llvm build trunc failed."); \ - goto fail; \ - } \ - } while (0) - -#define BUILD_STORE() do { \ - LLVMValueRef res; \ - if (!(res = LLVMBuildStore(comp_ctx->builder, value, maddr))) { \ - aot_set_last_error("llvm build store failed."); \ - goto fail; \ - } \ - LLVMSetAlignment(res, 1); \ - } while (0) - -#define BUILD_SIGN_EXT(dst_type) do { \ - if (!(value = LLVMBuildSExt(comp_ctx->builder, value, \ - dst_type, "data_s_ext"))) { \ - aot_set_last_error("llvm build sign ext failed."); \ - goto fail; \ - } \ - } while (0) - -#define BUILD_ZERO_EXT(dst_type) do { \ - if (!(value = LLVMBuildZExt(comp_ctx->builder, value, \ - dst_type, "data_z_ext"))) { \ - aot_set_last_error("llvm build zero ext failed."); \ - goto fail; \ - } \ - } while (0) +#endif /* WASM_ENABLE_SHARED_MEMORY != 0 || WASM_ENABLE_STRINGREF != 0 */ + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#define BUILD_ATOMIC_LOAD(align, data_type) \ + do { \ + if (!(check_memory_alignment(comp_ctx, func_ctx, maddr, align))) { \ + goto fail; \ + } \ + if (!(value = LLVMBuildLoad2(comp_ctx->builder, data_type, maddr, \ + "data"))) { \ + aot_set_last_error("llvm build load failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(value, 1 << align); \ + LLVMSetVolatile(value, true); \ + LLVMSetOrdering(value, LLVMAtomicOrderingSequentiallyConsistent); \ + } while (0) + +#define BUILD_ATOMIC_STORE(align) \ + do { \ + LLVMValueRef res; \ + if (!(check_memory_alignment(comp_ctx, func_ctx, maddr, align))) { \ + goto fail; \ + } \ + if (!(res = LLVMBuildStore(comp_ctx->builder, value, maddr))) { \ + aot_set_last_error("llvm build store failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(res, 1 << align); \ + LLVMSetVolatile(res, true); \ + LLVMSetOrdering(res, LLVMAtomicOrderingSequentiallyConsistent); \ + } while (0) +#endif bool aot_compile_op_i32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes, bool sign) + uint32 align, mem_offset_t offset, uint32 bytes, + bool sign, bool atomic) { LLVMValueRef maddr, value = NULL; + LLVMTypeRef data_type; + bool enable_segue = comp_ctx->enable_segue_i32_load; - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + enable_segue, &known_align))) return false; switch (bytes) { case 4: - BUILD_PTR_CAST(INT32_PTR_TYPE); - BUILD_LOAD(); + if (!enable_segue) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else + BUILD_PTR_CAST(INT32_PTR_TYPE_GS); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_LOAD(align, I32_TYPE); + else +#endif + BUILD_LOAD(I32_TYPE); break; case 2: case 1: - if (bytes == 2) - BUILD_PTR_CAST(INT16_PTR_TYPE); - else - BUILD_PTR_CAST(INT8_PTR_TYPE); - BUILD_LOAD(); - if (sign) - BUILD_SIGN_EXT(I32_TYPE); - else + if (bytes == 2) { + if (!enable_segue) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT16_PTR_TYPE_GS); + data_type = INT16_TYPE; + } + else { + if (!enable_segue) + BUILD_PTR_CAST(INT8_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE_GS); + data_type = INT8_TYPE; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + BUILD_ATOMIC_LOAD(align, data_type); BUILD_ZERO_EXT(I32_TYPE); + } + else +#endif + { + BUILD_LOAD(data_type); + if (sign) + BUILD_SIGN_EXT(I32_TYPE); + else + BUILD_ZERO_EXT(I32_TYPE); + } break; default: bh_assert(0); @@ -313,6 +1054,7 @@ aot_compile_op_i32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } PUSH_I32(value); + (void)data_type; return true; fail: return false; @@ -320,342 +1062,1222 @@ aot_compile_op_i32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool aot_compile_op_i64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes, bool sign) + uint32 align, mem_offset_t offset, uint32 bytes, + bool sign, bool atomic) { LLVMValueRef maddr, value = NULL; + LLVMTypeRef data_type; + bool enable_segue = comp_ctx->enable_segue_i64_load; - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + enable_segue, &known_align))) return false; switch (bytes) { case 8: - BUILD_PTR_CAST(INT64_PTR_TYPE); - BUILD_LOAD(); + if (!enable_segue) + BUILD_PTR_CAST(INT64_PTR_TYPE); + else + BUILD_PTR_CAST(INT64_PTR_TYPE_GS); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_LOAD(align, I64_TYPE); + else +#endif + BUILD_LOAD(I64_TYPE); break; case 4: case 2: case 1: - if (bytes == 4) - BUILD_PTR_CAST(INT32_PTR_TYPE); - else if (bytes == 2) - BUILD_PTR_CAST(INT16_PTR_TYPE); - else + if (bytes == 4) { + if (!enable_segue) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else + BUILD_PTR_CAST(INT32_PTR_TYPE_GS); + data_type = I32_TYPE; + } + else if (bytes == 2) { + if (!enable_segue) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT16_PTR_TYPE_GS); + data_type = INT16_TYPE; + } + else { + if (!enable_segue) + BUILD_PTR_CAST(INT8_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE_GS); + data_type = INT8_TYPE; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + BUILD_ATOMIC_LOAD(align, data_type); + BUILD_ZERO_EXT(I64_TYPE); + } + else +#endif + { + BUILD_LOAD(data_type); + if (sign) + BUILD_SIGN_EXT(I64_TYPE); + else + BUILD_ZERO_EXT(I64_TYPE); + } + break; + default: + bh_assert(0); + break; + } + + PUSH_I64(value); + (void)data_type; + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset) +{ + LLVMValueRef maddr, value; + bool enable_segue = comp_ctx->enable_segue_f32_load; + + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, 4, + enable_segue, &known_align))) + return false; + + if (!enable_segue) + BUILD_PTR_CAST(F32_PTR_TYPE); + else + BUILD_PTR_CAST(F32_PTR_TYPE_GS); + BUILD_LOAD(F32_TYPE); + + PUSH_F32(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset) +{ + LLVMValueRef maddr, value; + bool enable_segue = comp_ctx->enable_segue_f64_load; + + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, 8, + enable_segue, &known_align))) + return false; + + if (!enable_segue) + BUILD_PTR_CAST(F64_PTR_TYPE); + else + BUILD_PTR_CAST(F64_PTR_TYPE_GS); + BUILD_LOAD(F64_TYPE); + + PUSH_F64(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_i32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset, uint32 bytes, + bool atomic) +{ + LLVMValueRef maddr, value; + bool enable_segue = comp_ctx->enable_segue_i32_store; + + POP_I32(value); + + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + enable_segue, &known_align))) + return false; + + switch (bytes) { + case 4: + if (!enable_segue) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else + BUILD_PTR_CAST(INT32_PTR_TYPE_GS); + break; + case 2: + if (!enable_segue) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT16_PTR_TYPE_GS); + BUILD_TRUNC(value, INT16_TYPE); + break; + case 1: + if (!enable_segue) BUILD_PTR_CAST(INT8_PTR_TYPE); - BUILD_LOAD(); - if (sign) - BUILD_SIGN_EXT(I64_TYPE); else - BUILD_ZERO_EXT(I64_TYPE); + BUILD_PTR_CAST(INT8_PTR_TYPE_GS); + BUILD_TRUNC(value, INT8_TYPE); + break; + default: + bh_assert(0); + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_STORE(align); + else +#endif + BUILD_STORE(); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset, uint32 bytes, + bool atomic) +{ + LLVMValueRef maddr, value; + bool enable_segue = comp_ctx->enable_segue_i64_store; + + POP_I64(value); + + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + enable_segue, &known_align))) + return false; + + switch (bytes) { + case 8: + if (!enable_segue) + BUILD_PTR_CAST(INT64_PTR_TYPE); + else + BUILD_PTR_CAST(INT64_PTR_TYPE_GS); + break; + case 4: + if (!enable_segue) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else + BUILD_PTR_CAST(INT32_PTR_TYPE_GS); + BUILD_TRUNC(value, I32_TYPE); + break; + case 2: + if (!enable_segue) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT16_PTR_TYPE_GS); + BUILD_TRUNC(value, INT16_TYPE); + break; + case 1: + if (!enable_segue) + BUILD_PTR_CAST(INT8_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE_GS); + BUILD_TRUNC(value, INT8_TYPE); break; default: bh_assert(0); break; } - PUSH_I64(value); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_STORE(align); + else +#endif + BUILD_STORE(); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset) +{ + LLVMValueRef maddr, value; + bool enable_segue = comp_ctx->enable_segue_f32_store; + + POP_F32(value); + + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, 4, + enable_segue, &known_align))) + return false; + + if (!enable_segue) + BUILD_PTR_CAST(F32_PTR_TYPE); + else + BUILD_PTR_CAST(F32_PTR_TYPE_GS); + BUILD_STORE(); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset) +{ + LLVMValueRef maddr, value; + bool enable_segue = comp_ctx->enable_segue_f64_store; + + POP_F64(value); + + unsigned int known_align; + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, 8, + enable_segue, &known_align))) + return false; + + if (!enable_segue) + BUILD_PTR_CAST(F64_PTR_TYPE); + else + BUILD_PTR_CAST(F64_PTR_TYPE_GS); + BUILD_STORE(); + return true; +fail: + return false; +} + +static LLVMValueRef +get_memory_curr_page_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef mem_size; + + if (func_ctx->mem_space_unchanged) { + mem_size = func_ctx->mem_info[0].mem_cur_page_count_addr; + } + else { + if (!(mem_size = LLVMBuildLoad2( + comp_ctx->builder, I32_TYPE, + func_ctx->mem_info[0].mem_cur_page_count_addr, "mem_size"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + return LLVMBuildIntCast(comp_ctx->builder, mem_size, + MEMORY64_COND_VALUE(I64_TYPE, I32_TYPE), ""); +fail: + return NULL; +} + +bool +aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef mem_size = get_memory_curr_page_count(comp_ctx, func_ctx); + + if (mem_size) + PUSH_PAGE_COUNT(mem_size); + return mem_size ? true : false; +fail: + return false; +} + +bool +aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef mem_size = get_memory_curr_page_count(comp_ctx, func_ctx); + LLVMValueRef delta, param_values[2], ret_value, func, value; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + int32 func_index; +#if WASM_ENABLE_MEMORY64 != 0 + LLVMValueRef u32_max, u32_cmp_result; +#endif + + if (!mem_size) + return false; + + POP_PAGE_COUNT(delta); + + /* TODO: multi-memory aot_enlarge_memory_with_idx() */ + /* Function type of aot_enlarge_memory() */ + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + + if (!(func_type = LLVMFunctionType(ret_type, param_types, 2, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + /* JIT mode, call the function directly */ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("llvm add pointer type failed."); + return false; + } + if (!(value = I64_CONST((uint64)(uintptr_t)wasm_enlarge_memory)) + || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else if (comp_ctx->is_indirect_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + func_index = + aot_get_native_symbol_index(comp_ctx, "aot_enlarge_memory"); + if (func_index < 0) { + return false; + } + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + char *func_name = "aot_enlarge_memory"; + /* AOT mode, declare the function */ + if (!(func = LLVMGetNamedFunction(func_ctx->module, func_name)) + && !(func = + LLVMAddFunction(func_ctx->module, func_name, func_type))) { + aot_set_last_error("llvm add function failed."); + return false; + } + } + + /* Call function aot_enlarge_memory() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = LLVMBuildTrunc(comp_ctx->builder, delta, I32_TYPE, ""); + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_grow_ret"); +#if WASM_ENABLE_MEMORY64 != 0 + if (IS_MEMORY64) { + if (!(u32_max = I64_CONST(UINT32_MAX))) { + aot_set_last_error("llvm build const failed"); + return false; + } + BUILD_ICMP(LLVMIntULE, delta, u32_max, u32_cmp_result, "page_size_cmp"); + BUILD_OP(And, ret_value, u32_cmp_result, ret_value, "and"); + } +#endif + + /* ret_value = ret_value == true ? pre_page_count : -1 */ + if (!(ret_value = LLVMBuildSelect( + comp_ctx->builder, ret_value, mem_size, + MEMORY64_COND_VALUE(I64_NEG_ONE, I32_NEG_ONE), "mem_grow_ret"))) { + aot_set_last_error("llvm build select failed."); + return false; + } + + PUSH_PAGE_COUNT(ret_value); + return true; +fail: + return false; +} + +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 || WASM_ENABLE_STRINGREF != 0 +LLVMValueRef +check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef offset, LLVMValueRef bytes) +{ + LLVMValueRef maddr, max_addr, cmp, cmp1, offset1; + LLVMValueRef mem_base_addr; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_succ; + LLVMValueRef mem_size; + bool is_target_64bit; +#if WASM_ENABLE_MEMORY64 == 0 + bool is_memory64 = false; +#else + bool is_memory64 = IS_MEMORY64; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + LLVMValueRef maddr_phi = NULL; + LLVMBasicBlockRef block_maddr_phi = NULL; +#endif + + is_target_64bit = (comp_ctx->pointer_size == sizeof(uint64)) ? true : false; + + /* Get memory base address and memory data size */ +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory = comp_ctx->comp_data->memories[0].flags & 0x02; + + if (func_ctx->mem_space_unchanged || is_shared_memory) { +#else + if (func_ctx->mem_space_unchanged) { +#endif + mem_base_addr = func_ctx->mem_info[0].mem_base_addr; + } + else { + if (!(mem_base_addr = LLVMBuildLoad2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->mem_info[0].mem_base_addr, "mem_base"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + /* + * Note: not throw the integer-overflow-exception here since it must + * have been thrown when converting float to integer before + */ + /* return address directly if constant offset and inside memory space */ + if (LLVMIsEfficientConstInt(offset) && LLVMIsEfficientConstInt(bytes)) { + uint64 mem_offset = (uint64)LLVMConstIntGetZExtValue(offset); + uint64 mem_len = (uint64)LLVMConstIntGetZExtValue(bytes); + uint32 num_bytes_per_page = + comp_ctx->comp_data->memories[0].num_bytes_per_page; + uint32 init_page_count = + comp_ctx->comp_data->memories[0].init_page_count; + uint64 mem_data_size = (uint64)num_bytes_per_page * init_page_count; + if (mem_data_size > 0 && mem_offset + mem_len <= mem_data_size) { + /* inside memory space */ + /* maddr = mem_base_addr + moffset */ + /* Perform zero extension in advance to avoid LLVMBuildInBoundsGEP2 + * interpreting a negative address due to sign extension when + * mem_offset >= 2GiB */ + if (comp_ctx->pointer_size == sizeof(uint64)) { + offset1 = I64_CONST(mem_offset); + } + else { + offset1 = I32_CONST((uint32)mem_offset); + } + CHECK_LLVM_CONST(offset1); + if (!(maddr = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + mem_base_addr, &offset1, 1, + "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; + } + } + + if (func_ctx->mem_space_unchanged) { + mem_size = func_ctx->mem_info[0].mem_data_size_addr; + } + else { + if (!(mem_size = LLVMBuildLoad2( + comp_ctx->builder, I64_TYPE, + func_ctx->mem_info[0].mem_data_size_addr, "mem_size"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + ADD_BASIC_BLOCK(check_succ, "check_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + + /* Same logic with aot_check_memory_overflow, offset and bytes are 32/64 + * bits on 32/64 bits platform */ + if (is_target_64bit) { + offset = + LLVMBuildZExt(comp_ctx->builder, offset, I64_TYPE, "extend_offset"); + bytes = LLVMBuildZExt(comp_ctx->builder, bytes, I64_TYPE, "extend_len"); + if (!offset || !bytes) { + aot_set_last_error("llvm build zext failed."); + goto fail; + } + } + + BUILD_OP(Add, offset, bytes, max_addr, "max_addr"); + + /* Check overflow when it's memory64 or it's on 32 bits platform */ + if (is_memory64 || !is_target_64bit) { + /* Check whether integer overflow occurs in offset + bytes */ + LLVMBasicBlockRef check_integer_overflow_end; + ADD_BASIC_BLOCK(check_integer_overflow_end, + "check_integer_overflow_end"); + LLVMMoveBasicBlockAfter(check_integer_overflow_end, block_curr); + + /* offset + bytes can overflow yet is valid(for example, 0xffffffff, 1), + * allow it to be 0(either 0, 0 or overflow and valid) */ + BUILD_ICMP(LLVMIntULT, max_addr, offset, cmp, "cmp"); + BUILD_ICMP(LLVMIntNE, max_addr, is_target_64bit ? I64_ZERO : I32_ZERO, + cmp1, "cmp1"); + BUILD_OP(And, cmp, cmp1, cmp, "overflow"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, true, cmp, + check_integer_overflow_end)) { + goto fail; + } + SET_BUILD_POS(check_integer_overflow_end); + block_curr = check_integer_overflow_end; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (comp_ctx->enable_shared_heap + || comp_ctx->enable_shared_chain /* TODO: && mem_idx == 0 */) { + ADD_BASIC_BLOCK(block_maddr_phi, "maddr_phi"); + SET_BUILD_POS(block_maddr_phi); + if (!(maddr_phi = LLVMBuildPhi(comp_ctx->builder, INT8_PTR_TYPE, + "maddr_phi"))) { + aot_set_last_error("llvm build phi failed"); + goto fail; + } + SET_BUILD_POS(block_curr); + + if (!aot_check_bulk_memory_shared_heap_memory_overflow( + comp_ctx, func_ctx, block_curr, block_maddr_phi, check_succ, + maddr_phi, offset, max_addr, bytes, is_memory64, + is_target_64bit)) { + goto fail; + } + } +#endif + + /* mem_size is always 64-bit, extend max_addr on 32 bits platform */ + if (!is_target_64bit + && !(max_addr = LLVMBuildZExt(comp_ctx->builder, max_addr, I64_TYPE, + "extend_max_addr"))) { + aot_set_last_error("llvm build zext failed."); + goto fail; + } + BUILD_ICMP(LLVMIntUGT, max_addr, mem_size, cmp, "cmp_max_mem_addr"); + + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, true, cmp, + check_succ)) { + goto fail; + } + + /* maddr = mem_base_addr + offset */ + if (!(maddr = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + mem_base_addr, &offset, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (comp_ctx->enable_shared_heap + || comp_ctx->enable_shared_chain /* TODO: && mem_idx == 0 */) { + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMAddIncoming(maddr_phi, &maddr, &block_curr, 1); + if (!LLVMBuildBr(comp_ctx->builder, block_maddr_phi)) { + aot_set_last_error("llvm build br failed"); + goto fail; + } + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_maddr_phi); + return maddr_phi; + } + else +#endif + return maddr; +fail: + return NULL; +} +#endif /* end of WASM_ENABLE_BULK_MEMORY != 0 || WASM_ENABLE_STRINGREF != 0 */ + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index) +{ + LLVMValueRef seg, offset, dst, len, param_values[5], ret_value, func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef mem_init_fail, init_success; + + seg = I32_CONST(seg_index); + + POP_I32(len); + POP_I32(offset); + POP_MEM_OFFSET(dst); + + if (!zero_extend_u64(comp_ctx, &dst, "dst64")) { + return false; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = SIZE_T_TYPE; + ret_type = INT8_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_memory_init, 5); + else + GET_AOT_FUNCTION(aot_memory_init, 5); + + /* Call function aot_memory_init() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg; + param_values[2] = offset; + param_values[3] = len; + param_values[4] = dst; + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 5, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_init_ret"); + + ADD_BASIC_BLOCK(mem_init_fail, "mem_init_fail"); + ADD_BASIC_BLOCK(init_success, "init_success"); + + LLVMMoveBasicBlockAfter(mem_init_fail, block_curr); + LLVMMoveBasicBlockAfter(init_success, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, ret_value, init_success, + mem_init_fail)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* If memory.init failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, mem_init_fail); + if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { + goto fail; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, init_success); + + return true; +fail: + return false; +} + +bool +aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index) +{ + LLVMValueRef seg, param_values[2], ret_value, func, value; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + seg = I32_CONST(seg_index); + CHECK_LLVM_CONST(seg); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_data_drop, 2); + else + GET_AOT_FUNCTION(aot_data_drop, 2); + + /* Call function aot_data_drop() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg; + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + return true; fail: return false; } +#endif /* end of WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 bool -aot_compile_op_f32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset) +aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { - LLVMValueRef maddr, value; + LLVMValueRef src, dst, src_addr, dst_addr, len, res; + bool call_aot_memmove = false; + + POP_MEM_OFFSET(len); + POP_MEM_OFFSET(src); + POP_MEM_OFFSET(dst); - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 4))) + if (!(src_addr = check_bulk_memory_overflow(comp_ctx, func_ctx, src, len))) return false; - BUILD_PTR_CAST(F32_PTR_TYPE); - BUILD_LOAD(); - PUSH_F32(value); + if (!(dst_addr = check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len))) + return false; + + if (!zero_extend_u64(comp_ctx, &len, "len64")) { + return false; + } + + call_aot_memmove = comp_ctx->is_indirect_mode || comp_ctx->is_jit_mode; + if (call_aot_memmove) { + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + LLVMValueRef func, params[3]; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + param_types[2] = SIZE_T_TYPE; + ret_type = INT8_PTR_TYPE; + + if (!(func_type = LLVMFunctionType(ret_type, param_types, 3, false))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function pointer type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + if (!(func = I64_CONST((uint64)(uintptr_t)aot_memmove)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else { + int32 func_index; + func_index = aot_get_native_symbol_index(comp_ctx, "memmove"); + if (func_index < 0) { + return false; + } + if (!(func = + aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + + params[0] = dst_addr; + params[1] = src_addr; + params[2] = len; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, params, + 3, "call_memmove"))) { + aot_set_last_error("llvm build memmove failed."); + return false; + } + } + else { + if (!(res = LLVMBuildMemMove(comp_ctx->builder, dst_addr, 1, src_addr, + 1, len))) { + aot_set_last_error("llvm build memmove failed."); + return false; + } + } + return true; fail: return false; } +static void * +jit_memset(void *s, int c, size_t n) +{ + return memset(s, c, n); +} + bool -aot_compile_op_f64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset) +aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { - LLVMValueRef maddr, value; + LLVMValueRef val, dst, dst_addr, len, res; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + LLVMValueRef func, params[3]; - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 8))) + POP_MEM_OFFSET(len); + POP_I32(val); + POP_MEM_OFFSET(dst); + + if (!(dst_addr = check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len))) return false; - BUILD_PTR_CAST(F64_PTR_TYPE); - BUILD_LOAD(); - PUSH_F64(value); + if (!zero_extend_u64(comp_ctx, &len, "len64")) { + return false; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = SIZE_T_TYPE; + ret_type = INT8_PTR_TYPE; + + if (!(func_type = LLVMFunctionType(ret_type, param_types, 3, false))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function pointer type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + if (!(func = I64_CONST((uint64)(uintptr_t)jit_memset)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else if (comp_ctx->is_indirect_mode) { + int32 func_index; + func_index = aot_get_native_symbol_index(comp_ctx, "memset"); + if (func_index < 0) { + return false; + } + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_ptr_type, func_index))) { + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(func_ctx->module, "memset")) + && !(func = + LLVMAddFunction(func_ctx->module, "memset", func_type))) { + aot_set_last_error("llvm add function failed."); + return false; + } + } + + params[0] = dst_addr; + params[1] = val; + params[2] = len; + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, params, 3, + "call_memset"))) { + aot_set_last_error("llvm build memset failed."); + return false; + } + return true; fail: return false; } +#endif /* end of WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_SHARED_MEMORY != 0 bool -aot_compile_op_i32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes) +aot_compile_op_atomic_rmw(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 atomic_op, uint8 op_type, uint32 align, + mem_offset_t offset, uint32 bytes) { - LLVMValueRef maddr, value; - - POP_I32(value); + LLVMValueRef maddr, value, result; + bool enable_segue = (op_type == VALUE_TYPE_I32) + ? comp_ctx->enable_segue_i32_load + && comp_ctx->enable_segue_i32_store + : comp_ctx->enable_segue_i64_load + && comp_ctx->enable_segue_i64_store; + + if (op_type == VALUE_TYPE_I32) + POP_I32(value); + else + POP_I64(value); + + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + enable_segue, NULL))) + return false; - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) return false; switch (bytes) { + case 8: + if (!enable_segue) + BUILD_PTR_CAST(INT64_PTR_TYPE); + else + BUILD_PTR_CAST(INT64_PTR_TYPE_GS); + break; case 4: - BUILD_PTR_CAST(INT32_PTR_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else + BUILD_PTR_CAST(INT32_PTR_TYPE_GS); + if (op_type == VALUE_TYPE_I64) + BUILD_TRUNC(value, I32_TYPE); break; case 2: - BUILD_PTR_CAST(INT16_PTR_TYPE); - BUILD_TRUNC(INT16_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT16_PTR_TYPE_GS); + BUILD_TRUNC(value, INT16_TYPE); break; case 1: - BUILD_PTR_CAST(INT8_PTR_TYPE); - BUILD_TRUNC(INT8_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT8_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE_GS); + BUILD_TRUNC(value, INT8_TYPE); break; default: bh_assert(0); break; } - BUILD_STORE(); + if (!(result = LLVMBuildAtomicRMW( + comp_ctx->builder, atomic_op, maddr, value, + LLVMAtomicOrderingSequentiallyConsistent, false))) { + goto fail; + } + + LLVMSetVolatile(result, true); + + if (op_type == VALUE_TYPE_I32) { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I32_TYPE, + "result_i32"))) { + goto fail; + } + PUSH_I32(result); + } + else { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I64_TYPE, + "result_i64"))) { + goto fail; + } + PUSH_I64(result); + } + return true; fail: return false; } bool -aot_compile_op_i64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes) +aot_compile_op_atomic_cmpxchg(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 op_type, + uint32 align, mem_offset_t offset, uint32 bytes) { - LLVMValueRef maddr, value; + LLVMValueRef maddr, value, expect, result; + bool enable_segue = (op_type == VALUE_TYPE_I32) + ? comp_ctx->enable_segue_i32_load + && comp_ctx->enable_segue_i32_store + : comp_ctx->enable_segue_i64_load + && comp_ctx->enable_segue_i64_store; + + if (op_type == VALUE_TYPE_I32) { + POP_I32(value); + POP_I32(expect); + } + else { + POP_I64(value); + POP_I64(expect); + } - POP_I64(value); + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + enable_segue, NULL))) + return false; - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) return false; switch (bytes) { case 8: - BUILD_PTR_CAST(INT64_PTR_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT64_PTR_TYPE); + else + BUILD_PTR_CAST(INT64_PTR_TYPE_GS); break; case 4: - BUILD_PTR_CAST(INT32_PTR_TYPE); - BUILD_TRUNC(I32_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else + BUILD_PTR_CAST(INT32_PTR_TYPE_GS); + if (op_type == VALUE_TYPE_I64) { + BUILD_TRUNC(value, I32_TYPE); + BUILD_TRUNC(expect, I32_TYPE); + } break; case 2: - BUILD_PTR_CAST(INT16_PTR_TYPE); - BUILD_TRUNC(INT16_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT16_PTR_TYPE_GS); + BUILD_TRUNC(value, INT16_TYPE); + BUILD_TRUNC(expect, INT16_TYPE); break; case 1: - BUILD_PTR_CAST(INT8_PTR_TYPE); - BUILD_TRUNC(INT8_TYPE); + if (!enable_segue) + BUILD_PTR_CAST(INT8_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE_GS); + BUILD_TRUNC(value, INT8_TYPE); + BUILD_TRUNC(expect, INT8_TYPE); break; default: bh_assert(0); break; } - BUILD_STORE(); - return true; -fail: - return false; -} + if (!(result = LLVMBuildAtomicCmpXchg( + comp_ctx->builder, maddr, expect, value, + LLVMAtomicOrderingSequentiallyConsistent, + LLVMAtomicOrderingSequentiallyConsistent, false))) { + goto fail; + } -bool -aot_compile_op_f32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset) -{ - LLVMValueRef maddr, value; + LLVMSetVolatile(result, true); - POP_F32(value); + /* CmpXchg return {i32, i1} structure, + we need to extract the previous_value from the structure */ + if (!(result = LLVMBuildExtractValue(comp_ctx->builder, result, 0, + "previous_value"))) { + goto fail; + } - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 4))) - return false; + if (op_type == VALUE_TYPE_I32) { + if (LLVMTypeOf(result) != I32_TYPE) { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I32_TYPE, + "result_i32"))) { + goto fail; + } + } + PUSH_I32(result); + } + else { + if (LLVMTypeOf(result) != I64_TYPE) { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I64_TYPE, + "result_i64"))) { + goto fail; + } + } + PUSH_I64(result); + } - BUILD_PTR_CAST(F32_PTR_TYPE); - BUILD_STORE(); return true; fail: return false; } bool -aot_compile_op_f64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset) +aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 op_type, uint32 align, mem_offset_t offset, + uint32 bytes) { - LLVMValueRef maddr, value; + LLVMValueRef maddr, value, timeout, expect, cmp; + LLVMValueRef param_values[5], ret_value, func, is_wait64; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef wait_fail, wait_success; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + + POP_I64(timeout); + if (op_type == VALUE_TYPE_I32) { + POP_I32(expect); + is_wait64 = I8_CONST(false); + if (!(expect = LLVMBuildZExt(comp_ctx->builder, expect, I64_TYPE, + "expect_i64"))) { + goto fail; + } + } + else { + POP_I64(expect); + is_wait64 = I8_CONST(true); + } - POP_F64(value); + CHECK_LLVM_CONST(is_wait64); - if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 8))) + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + false, NULL))) return false; - BUILD_PTR_CAST(F64_PTR_TYPE); - BUILD_STORE(); - return true; -fail: - return false; -} + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) + return false; -static LLVMValueRef -get_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) -{ - uint32 offset = offsetof(AOTModuleInstance, mem_cur_page_count); - LLVMValueRef mem_size_offset, mem_size_ptr, mem_size; - - /* mem_size_offset = aot_inst + offset */ - mem_size_offset = I32_CONST(offset); - CHECK_LLVM_CONST(mem_size_offset); - if (!(mem_size_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->aot_inst, - &mem_size_offset, 1, - "mem_size_ptr_tmp"))) { - aot_set_last_error("llvm build inbounds gep failed."); - return NULL; + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + param_types[2] = I64_TYPE; + param_types[3] = I64_TYPE; + param_types[4] = INT8_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_runtime_atomic_wait, 5); + + /* Call function wasm_runtime_atomic_wait() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = maddr; + param_values[2] = expect; + param_values[3] = timeout; + param_values[4] = is_wait64; + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 5, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; } - /* cast to int32* */ - if (!(mem_size_ptr = LLVMBuildBitCast(comp_ctx->builder, mem_size_ptr, - INT32_PTR_TYPE, "mem_size_ptr"))) { - aot_set_last_error("llvm build bitcast failed."); - return NULL; + 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"); + + LLVMMoveBasicBlockAfter(wait_fail, block_curr); + LLVMMoveBasicBlockAfter(wait_success, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, wait_success, wait_fail)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; } - /* load memory size, or current page count */ - if (!(mem_size = LLVMBuildLoad(comp_ctx->builder, - mem_size_ptr, "mem_size"))) { - aot_set_last_error("llvm build load failed."); - return NULL; + /* If atomic wait failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, wait_fail); + if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) { + goto fail; } - return mem_size; -fail: - return NULL; -} + LLVMPositionBuilderAtEnd(comp_ctx->builder, wait_success); -bool -aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) -{ - LLVMValueRef mem_size = get_memory_size(comp_ctx, func_ctx); + PUSH_I32(ret_value); - if (mem_size) - PUSH_I32(mem_size); - return mem_size ? true : false; + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx, false)) + return false; + } + + return true; fail: return false; } bool -aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +aot_compiler_op_atomic_notify(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 align, + mem_offset_t offset, uint32 bytes) { - LLVMValueRef mem_size = get_memory_size(comp_ctx, func_ctx); - LLVMValueRef delta, param_values[2], ret_value, func, value; - LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + LLVMValueRef maddr, value, count; + LLVMValueRef param_values[3], ret_value, func; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; - if (!mem_size) + POP_I32(count); + + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, bytes, + false, NULL))) return false; - POP_I32(delta); + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) + return false; - /* Function type of wasm_runtime_enlarge_memory() */ param_types[0] = INT8_PTR_TYPE; - param_types[1] = I32_TYPE; - ret_type = INT8_TYPE; - - if (!(func_type = LLVMFunctionType(ret_type, param_types, 2, false))) { - aot_set_last_error("llvm add function type failed."); - return false; - } + param_types[1] = INT8_PTR_TYPE; + param_types[2] = I32_TYPE; + ret_type = I32_TYPE; - if (comp_ctx->is_jit_mode) { - /* JIT mode, call the function directly */ - if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { - aot_set_last_error("llvm add pointer type failed."); - return false; - } - if (!(value = I64_CONST((uint64)(uintptr_t)wasm_runtime_enlarge_memory)) - || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { - aot_set_last_error("create LLVM value failed."); - return false; - } - } - else { - char *func_name = "wasm_runtime_enlarge_memory"; - /* AOT mode, delcare the function */ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) - && !(func = LLVMAddFunction(comp_ctx->module, - func_name, func_type))) { - aot_set_last_error("llvm add function failed."); - return false; - } - } + GET_AOT_FUNCTION(wasm_runtime_atomic_notify, 3); - /* Call function wasm_runtime_enlarge_memory() */ + /* Call function wasm_runtime_atomic_notify() */ param_values[0] = func_ctx->aot_inst; - param_values[1] = delta; - if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, - param_values, 2, "call"))) { + param_values[1] = maddr; + param_values[2] = count; + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 3, "call"))) { aot_set_last_error("llvm build call failed."); return false; } - BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_grow_ret"); - - /* ret_value = ret_value == true ? delta : pre_page_count */ - if (!(ret_value = LLVMBuildSelect(comp_ctx->builder, ret_value, - mem_size, I32_NEG_ONE, - "mem_grow_ret"))) { - aot_set_last_error("llvm build select failed."); - return false; - } - PUSH_I32(ret_value); - /* To be simple, call wasm_runtime_set_exception() no matter - enlarge success or not */ - param_types[1] = VOID_PTR_TYPE; - ret_type = VOID_TYPE; - if (!(func_type = LLVMFunctionType(ret_type, param_types, 2, false))) { - aot_set_last_error("llvm add function type failed."); - return false; - } - - if (comp_ctx->is_jit_mode) { - /* JIT mode, call the function directly */ - if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { - aot_set_last_error("llvm add pointer type failed."); - return false; - } - if (!(value = I64_CONST((uint64)(uintptr_t)wasm_runtime_set_exception)) - || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { - aot_set_last_error("create LLVM value failed."); - return false; - } - } - else { - char *func_name = "wasm_runtime_set_exception"; - /* AOT mode, delcare the function */ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) - && !(func = LLVMAddFunction(comp_ctx->module, - func_name, func_type))) { - aot_set_last_error("llvm add function failed."); - return false; - } - } - - /* Call function wasm_runtime_set_exception(aot_inst, NULL) */ - param_values[1] = LLVMConstNull(VOID_PTR_TYPE); - CHECK_LLVM_CONST(param_values[1]); - if (!(LLVMBuildCall(comp_ctx->builder, func, param_values, 2, ""))) { - aot_set_last_error("llvm build call failed."); - return false; - } - return true; 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 d34264c361..5b87377beb 100644 --- a/core/iwasm/compilation/aot_emit_memory.h +++ b/core/iwasm/compilation/aot_emit_memory.h @@ -7,6 +7,9 @@ #define _AOT_EMIT_MEMORY_H_ #include "aot_compiler.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#endif #ifdef __cplusplus extern "C" { @@ -14,35 +17,44 @@ extern "C" { bool aot_compile_op_i32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes, bool sign); + uint32 align, mem_offset_t offset, uint32 bytes, + bool sign, bool atomic); bool aot_compile_op_i64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes, bool sign); + uint32 align, mem_offset_t offset, uint32 bytes, + bool sign, bool atomic); bool aot_compile_op_f32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset); + uint32 align, mem_offset_t offset); bool aot_compile_op_f64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset); + uint32 align, mem_offset_t offset); bool aot_compile_op_i32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes); + uint32 align, mem_offset_t offset, uint32 bytes, + bool atomic); bool aot_compile_op_i64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset, uint32 bytes); + uint32 align, mem_offset_t offset, uint32 bytes, + bool atomic); bool aot_compile_op_f32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset); + uint32 align, mem_offset_t offset); bool aot_compile_op_f64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 align, uint32 offset); + uint32 align, mem_offset_t offset); + +LLVMValueRef +aot_check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + mem_offset_t offset, uint32 bytes, bool enable_segue, + unsigned int *alignp); bool aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); @@ -50,9 +62,60 @@ aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); bool aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +bool +check_memory_alignment(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef addr, uint32 align); + +LLVMValueRef +check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef offset, LLVMValueRef bytes); + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index); + +bool +aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index); +#endif + +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 +bool +aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +bool +aot_compile_op_atomic_rmw(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 atomic_op, uint8 op_type, uint32 align, + mem_offset_t offset, uint32 bytes); + +bool +aot_compile_op_atomic_cmpxchg(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 op_type, + uint32 align, mem_offset_t offset, uint32 bytes); + +bool +aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 op_type, uint32 align, mem_offset_t offset, + uint32 bytes); + +bool +aot_compiler_op_atomic_notify(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 align, + mem_offset_t offset, uint32 bytes); + +bool +aot_compiler_op_atomic_fence(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); +#endif + #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_EMIT_MEMORY_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_numberic.c b/core/iwasm/compilation/aot_emit_numberic.c index 46ed1cc3d9..492c3048c7 100644 --- a/core/iwasm/compilation/aot_emit_numberic.c +++ b/core/iwasm/compilation/aot_emit_numberic.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2020 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ @@ -7,260 +7,192 @@ #include "aot_emit_exception.h" #include "aot_emit_control.h" #include "../aot/aot_runtime.h" +#include "../aot/aot_intrinsic.h" #include -#define LLVM_BUILD_ICMP(op, left, right, res, name) do { \ - if (!(res = LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \ - aot_set_last_error("llvm build "name" fail."); \ - return false; \ - } \ -} while (0) - -#define LLVM_BUILD_OP(Op, left, right, res, name, err_ret) do { \ - if (!(res = LLVMBuild##Op(comp_ctx->builder, left, right, name))) { \ - aot_set_last_error("llvm build " #name " fail."); \ - return err_ret; \ - } \ -} while (0) - -#define ADD_BASIC_BLOCK(block, name) do { \ - if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ - func_ctx->func, \ - name))) { \ - aot_set_last_error("llvm add basic block failed."); \ - goto fail; \ - } \ - \ - LLVMMoveBasicBlockAfter(block, LLVMGetInsertBlock(comp_ctx->builder)); \ -} while (0) - -#define IS_CONST_ZERO(val) \ - (LLVMIsConstant(val) \ - && ((is_i32 && (int32)LLVMConstIntGetZExtValue(val) == 0) \ +#define LLVM_BUILD_ICMP(op, left, right, res, name) \ + do { \ + if (!(res = \ + LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \ + aot_set_last_error("llvm build " name " fail."); \ + return false; \ + } \ + } while (0) + +#define LLVM_BUILD_OP(Op, left, right, res, name, err_ret) \ + do { \ + if (!(res = LLVMBuild##Op(comp_ctx->builder, left, right, name))) { \ + aot_set_last_error("llvm build " #name " fail."); \ + return err_ret; \ + } \ + } while (0) + +#define LLVM_BUILD_OP_OR_INTRINSIC(Op, left, right, res, intrinsic, name, \ + err_ret) \ + do { \ + if (comp_ctx->disable_llvm_intrinsics \ + && aot_intrinsic_check_capability(comp_ctx, intrinsic)) { \ + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, \ + param_types[0], param_types, 2, \ + left, right); \ + } \ + else { \ + LLVM_BUILD_OP(Op, left, right, res, name, false); \ + } \ + } while (0) + +#define ADD_BASIC_BLOCK(block, name) \ + do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + \ + LLVMMoveBasicBlockAfter(block, LLVMGetInsertBlock(comp_ctx->builder)); \ + } while (0) + +#if LLVM_VERSION_NUMBER >= 12 +#define IS_CONST_ZERO(val) \ + (LLVMIsEfficientConstInt(val) \ + && ((is_i32 && (int32)LLVMConstIntGetZExtValue(val) == 0) \ || (!is_i32 && (int64)LLVMConstIntGetSExtValue(val) == 0))) - -#define CHECK_INT_OVERFLOW(type) do { \ - LLVMValueRef cmp_min_int, cmp_neg_one; \ - LLVM_BUILD_ICMP(LLVMIntEQ, left, type##_MIN, \ - cmp_min_int, "cmp_min_int"); \ - LLVM_BUILD_ICMP(LLVMIntEQ, right, type##_NEG_ONE, \ - cmp_neg_one, "cmp_neg_one"); \ - LLVM_BUILD_OP(And, cmp_min_int, cmp_neg_one, \ - overflow, "overflow", false); \ -} while (0) - -#define PUSH_INT(v) do { \ - if (is_i32) \ - PUSH_I32(v); \ - else \ - PUSH_I64(v); \ -} while (0) - -#define POP_INT(v) do { \ - if (is_i32) \ - POP_I32(v); \ - else \ - POP_I64(v); \ -} while (0) - -#define PUSH_FLOAT(v) do { \ - if (is_f32) \ - PUSH_F32(v); \ - else \ - PUSH_F64(v); \ -} while (0) - -#define POP_FLOAT(v) do { \ - if (is_f32) \ - POP_F32(v); \ - else \ - POP_F64(v); \ -} while (0) - -#define DEF_INT_UNARY_OP(op, err) do { \ - LLVMValueRef res, operand; \ - POP_INT(operand); \ - if (!(res = op)) { \ - if (err) \ - aot_set_last_error(err); \ - return false; \ - } \ - PUSH_INT(res); \ -} while (0) - -#define DEF_INT_BINARY_OP(op, err) do { \ - LLVMValueRef res, left, right; \ - POP_INT(right); \ - POP_INT(left); \ - if (!(res = op)) { \ - if (err) \ - aot_set_last_error(err); \ - return false; \ - } \ - PUSH_INT(res); \ -} while (0) - -#define DEF_FP_UNARY_OP(op, err) do { \ - LLVMValueRef res, operand; \ - POP_FLOAT(operand); \ - if (!(res = op)) { \ - if (err) \ - aot_set_last_error(err); \ - return false; \ - } \ - PUSH_FLOAT(res); \ -} while (0) - -#define DEF_FP_BINARY_OP(op, err) do { \ - LLVMValueRef res, left, right; \ - POP_FLOAT(right); \ - POP_FLOAT(left); \ - if (!(res = op)) { \ - if (err) \ - aot_set_last_error(err); \ - return false; \ - } \ - PUSH_FLOAT(res); \ -} while (0) - -#define SHIFT_COUNT_MASK do { \ - /* LLVM has undefined behavior if shift count is greater than bits count \ - * while Webassembly spec requires the shift count be wrapped. */ \ - LLVMValueRef shift_count_mask, bits_minus_one; \ - bits_minus_one = is_i32 ? I32_31 : I64_63; \ - LLVM_BUILD_OP(And, right, bits_minus_one, \ - shift_count_mask, "shift_count_mask", NULL); \ - right = shift_count_mask; \ -} while (0) - - -static LLVMValueRef -__call_llvm_intrinsic(AOTCompContext *comp_ctx, - const char *name, - LLVMTypeRef ret_type, - LLVMTypeRef *param_types, - int param_count, - LLVMValueRef *param_values) -{ - LLVMValueRef func, ret; - LLVMTypeRef func_type; - - /* Declare llvm intrinsic function if necessary */ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, name))) { - if (!(func_type = - LLVMFunctionType(ret_type, param_types, (uint32)param_count, false))) { - aot_set_last_error("create LLVM function type failed."); - return NULL; - } - - if (!(func = LLVMAddFunction(comp_ctx->module, name, func_type))) { - aot_set_last_error("add LLVM function failed."); - return NULL; - } - } - - /* Call the LLVM intrinsic function */ - if (!(ret = LLVMBuildCall(comp_ctx->builder, func, param_values, - (uint32)param_count, "call"))) { - aot_set_last_error("llvm build call failed."); - return NULL; - } - - return ret; -} - -static LLVMValueRef -call_llvm_intrinsic(AOTCompContext *comp_ctx, - const char *name, - LLVMTypeRef ret_type, - LLVMTypeRef *param_types, - int param_count, - ...) -{ - LLVMValueRef *param_values, ret; - va_list argptr; - uint64 total_size; - int i = 0; - - /* Create param values */ - total_size = sizeof(LLVMValueRef) * (uint64)param_count; - if (total_size >= UINT32_MAX - || !(param_values = wasm_malloc((uint32)total_size))) { - aot_set_last_error("allocate memory for param values failed."); - return false; - } - - /* Load each param value */ - va_start(argptr, param_count); - while (i < param_count) - param_values[i++] = va_arg(argptr, LLVMValueRef); - va_end(argptr); - - ret = __call_llvm_intrinsic(comp_ctx, name, ret_type, - param_types, param_count, - param_values); - - wasm_free(param_values); - - return ret; -} - -static LLVMValueRef -call_llvm_intrinsic_v(AOTCompContext *comp_ctx, - const char *name, - LLVMTypeRef ret_type, - LLVMTypeRef *param_types, - int param_count, - va_list param_value_list) -{ - LLVMValueRef *param_values, ret; - uint64 total_size; - int i = 0; - - /* Create param values */ - total_size = sizeof(LLVMValueRef) * (uint64)param_count; - if (total_size >= UINT32_MAX - || !(param_values = wasm_malloc((uint32)total_size))) { - aot_set_last_error("allocate memory for param values failed."); - return false; - } - - /* Load each param value */ - while (i < param_count) - param_values[i++] = va_arg(param_value_list, LLVMValueRef); - - ret = __call_llvm_intrinsic(comp_ctx, name, ret_type, - param_types, param_count, - param_values); - - wasm_free(param_values); - - return ret; -} +#else +#define IS_CONST_ZERO(val) \ + (LLVMIsEfficientConstInt(val) \ + && ((is_i32 && (int32)LLVMConstIntGetZExtValue(val) == 0) \ + || (!is_i32 && (int64)LLVMConstIntGetSExtValue(val) == 0))) +#endif + +#define CHECK_INT_OVERFLOW(type) \ + do { \ + LLVMValueRef cmp_min_int, cmp_neg_one; \ + LLVM_BUILD_ICMP(LLVMIntEQ, left, type##_MIN, cmp_min_int, \ + "cmp_min_int"); \ + LLVM_BUILD_ICMP(LLVMIntEQ, right, type##_NEG_ONE, cmp_neg_one, \ + "cmp_neg_one"); \ + LLVM_BUILD_OP(And, cmp_min_int, cmp_neg_one, overflow, "overflow", \ + false); \ + } while (0) + +#define PUSH_INT(v) \ + do { \ + if (is_i32) \ + PUSH_I32(v); \ + else \ + PUSH_I64(v); \ + } while (0) + +#define POP_INT(v) \ + do { \ + if (is_i32) \ + POP_I32(v); \ + else \ + POP_I64(v); \ + } while (0) + +#define PUSH_FLOAT(v) \ + do { \ + if (is_f32) \ + PUSH_F32(v); \ + else \ + PUSH_F64(v); \ + } while (0) + +#define POP_FLOAT(v) \ + do { \ + if (is_f32) \ + POP_F32(v); \ + else \ + POP_F64(v); \ + } while (0) + +#define DEF_INT_UNARY_OP(op, err) \ + do { \ + LLVMValueRef res, operand; \ + POP_INT(operand); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_INT(res); \ + } while (0) + +#define DEF_INT_BINARY_OP(op, err) \ + do { \ + LLVMValueRef res, left, right; \ + POP_INT(right); \ + POP_INT(left); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_INT(res); \ + } while (0) + +#define DEF_FP_UNARY_OP(op, err) \ + do { \ + LLVMValueRef res, operand; \ + POP_FLOAT(operand); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_FLOAT(res); \ + } while (0) + +#define DEF_FP_BINARY_OP(op, err) \ + do { \ + LLVMValueRef res, left, right; \ + POP_FLOAT(right); \ + POP_FLOAT(left); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_FLOAT(res); \ + } while (0) + +#define SHIFT_COUNT_MASK \ + do { \ + /* LLVM has undefined behavior if shift count is greater than \ + * bits count while Webassembly spec requires the shift count \ + * be wrapped. \ + */ \ + LLVMValueRef shift_count_mask, bits_minus_one; \ + bits_minus_one = is_i32 ? I32_31 : I64_63; \ + LLVM_BUILD_OP(And, right, bits_minus_one, shift_count_mask, \ + "shift_count_mask", NULL); \ + right = shift_count_mask; \ + } while (0) /* Call llvm constrained floating-point intrinsic */ static LLVMValueRef -call_llvm_float_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, - const char *intrinsic, - bool is_f32, - ...) +call_llvm_float_experimental_constrained_intrinsic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_f32, + const char *intrinsic, ...) { va_list param_value_list; LLVMValueRef ret; LLVMTypeRef param_types[4], ret_type = is_f32 ? F32_TYPE : F64_TYPE; + int param_count = (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, intrinsic)) + ? 2 + : 4; param_types[0] = param_types[1] = ret_type; param_types[2] = param_types[3] = MD_TYPE; - va_start(param_value_list, is_f32); + va_start(param_value_list, intrinsic); - ret = call_llvm_intrinsic_v(comp_ctx, - intrinsic, - ret_type, - param_types, - 4, - param_value_list); + ret = aot_call_llvm_intrinsic_v(comp_ctx, func_ctx, intrinsic, ret_type, + param_types, param_count, param_value_list); va_end(param_value_list); @@ -269,10 +201,10 @@ call_llvm_float_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, /* Call llvm constrained libm-equivalent intrinsic */ static LLVMValueRef -call_llvm_libm_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, - const char *intrinsic, - bool is_f32, - ...) +call_llvm_libm_experimental_constrained_intrinsic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_f32, + const char *intrinsic, ...) { va_list param_value_list; LLVMValueRef ret; @@ -281,14 +213,10 @@ call_llvm_libm_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, param_types[0] = ret_type; param_types[1] = param_types[2] = MD_TYPE; - va_start(param_value_list, is_f32); + va_start(param_value_list, intrinsic); - ret = call_llvm_intrinsic_v(comp_ctx, - intrinsic, - ret_type, - param_types, - 3, - param_value_list); + ret = aot_call_llvm_intrinsic_v(comp_ctx, func_ctx, intrinsic, ret_type, + param_types, 3, param_value_list); va_end(param_value_list); @@ -296,74 +224,106 @@ call_llvm_libm_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, } static LLVMValueRef -compile_op_float_min_max(AOTCompContext *comp_ctx, - bool is_f32, - LLVMValueRef left, - LLVMValueRef right, +compile_op_float_min_max(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_f32, LLVMValueRef left, LLVMValueRef right, bool is_min) { + LLVMTypeRef float_param_types[2]; LLVMTypeRef param_types[2], ret_type = is_f32 ? F32_TYPE : F64_TYPE, - int_type = is_f32 ? I32_TYPE : I64_TYPE; + int_type = is_f32 ? I32_TYPE : I64_TYPE; LLVMValueRef cmp, is_eq, is_nan, ret, left_int, right_int, tmp, - nan = LLVMConstRealOfString(ret_type, "NaN"); - char *intrinsic = is_min ? - (is_f32 ? "llvm.minnum.f32" : "llvm.minnum.f64") : - (is_f32 ? "llvm.maxnum.f32" : "llvm.maxnum.f64"); - + nan = LLVMConstRealOfString(ret_type, "NaN"); + char *intrinsic = is_min ? (is_f32 ? "llvm.minnum.f32" : "llvm.minnum.f64") + : (is_f32 ? "llvm.maxnum.f32" : "llvm.maxnum.f64"); CHECK_LLVM_CONST(nan); - param_types[0] = param_types[1] = ret_type; + /* Note: param_types is used by LLVM_BUILD_OP_OR_INTRINSIC */ + param_types[0] = param_types[1] = int_type; + float_param_types[0] = float_param_types[1] = ret_type; + + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, + is_f32 ? "f32_cmp" : "f64_cmp")) { + LLVMTypeRef param_types_intrinsic[3]; + LLVMValueRef opcond = LLVMConstInt(I32_TYPE, FLOAT_UNO, true); + param_types_intrinsic[0] = I32_TYPE; + param_types_intrinsic[1] = is_f32 ? F32_TYPE : F64_TYPE; + param_types_intrinsic[2] = param_types_intrinsic[1]; + is_nan = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, is_f32 ? "f32_cmp" : "f64_cmp", I32_TYPE, + param_types_intrinsic, 3, opcond, left, right); + + opcond = LLVMConstInt(I32_TYPE, FLOAT_EQ, true); + is_eq = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, is_f32 ? "f32_cmp" : "f64_cmp", I32_TYPE, + param_types_intrinsic, 3, opcond, left, right); + + if (!is_nan || !is_eq) { + return NULL; + } + + if (!(is_nan = LLVMBuildIntCast(comp_ctx->builder, is_nan, INT1_TYPE, + "bit_cast_is_nan"))) { + aot_set_last_error("llvm build is_nan bit cast fail."); + return NULL; + } - if (!(is_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, - left, right, "is_nan")) - || !(is_eq = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOEQ, - left, right, "is_eq"))) { + if (!(is_eq = LLVMBuildIntCast(comp_ctx->builder, is_eq, INT1_TYPE, + "bit_cast_is_eq"))) { + aot_set_last_error("llvm build is_eq bit cast fail."); + return NULL; + } + } + else if (!(is_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, left, + right, "is_nan")) + || !(is_eq = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOEQ, left, + right, "is_eq"))) { aot_set_last_error("llvm build fcmp fail."); return NULL; } /* If left and right are equal, they may be zero with different sign. - * Webassembly spec assert -0 < +0. So do a bitwise here. */ - if (!(left_int = LLVMBuildBitCast(comp_ctx->builder, left, - int_type, "left_int")) - || !(right_int = LLVMBuildBitCast(comp_ctx->builder, right, - int_type, "right_int"))) { + Webassembly spec assert -0 < +0. So do a bitwise here. */ + if (!(left_int = + LLVMBuildBitCast(comp_ctx->builder, left, int_type, "left_int")) + || !(right_int = LLVMBuildBitCast(comp_ctx->builder, right, int_type, + "right_int"))) { aot_set_last_error("llvm build bitcast fail."); return NULL; } if (is_min) - LLVM_BUILD_OP(Or, left_int, right_int, tmp, "tmp_int", NULL); + LLVM_BUILD_OP_OR_INTRINSIC(Or, left_int, right_int, tmp, + is_f32 ? "i32.or" : "i64.or", "tmp_int", + false); else - LLVM_BUILD_OP(And, left_int, right_int, tmp, "tmp_int", NULL); + LLVM_BUILD_OP_OR_INTRINSIC(And, left_int, right_int, tmp, + is_f32 ? "i32.and" : "i64.and", "tmp_int", + false); if (!(tmp = LLVMBuildBitCast(comp_ctx->builder, tmp, ret_type, "tmp"))) { aot_set_last_error("llvm build bitcast fail."); return NULL; } - if (!(cmp = call_llvm_intrinsic(comp_ctx, - intrinsic, - ret_type, - param_types, - 2, - left, - right))) + if (!(cmp = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, ret_type, + float_param_types, 2, left, right))) return NULL; - if (!(cmp = LLVMBuildSelect(comp_ctx->builder, - is_eq, - tmp, - cmp, - "cmp"))) { + /* The result of XIP intrinsic is 0 or 1, should return it directly */ + + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, + is_f32 ? "f32_cmp" : "f64_cmp")) { + return cmp; + } + + if (!(cmp = LLVMBuildSelect(comp_ctx->builder, is_eq, tmp, cmp, "cmp"))) { aot_set_last_error("llvm build select fail."); return NULL; } - if (!(ret = LLVMBuildSelect(comp_ctx->builder, - is_nan, - nan, - cmp, + if (!(ret = LLVMBuildSelect(comp_ctx->builder, is_nan, nan, cmp, is_min ? "min" : "max"))) { aot_set_last_error("llvm build select fail."); return NULL; @@ -375,21 +335,24 @@ compile_op_float_min_max(AOTCompContext *comp_ctx, } typedef enum BitCountType { - CLZ32 = 0, - CLZ64, - CTZ32, - CTZ64, - POP_CNT32, - POP_CNT64 + CLZ32 = 0, + CLZ64, + CTZ32, + CTZ64, + POP_CNT32, + POP_CNT64 } BitCountType; -static char *bit_cnt_llvm_intrinsic[] = { "llvm.ctlz.i32", - "llvm.ctlz.i64", - "llvm.cttz.i32", - "llvm.cttz.i64", - "llvm.ctpop.i32", - "llvm.ctpop.i64", - }; +/* clang-format off */ +static char *bit_cnt_llvm_intrinsic[] = { + "llvm.ctlz.i32", + "llvm.ctlz.i64", + "llvm.cttz.i32", + "llvm.cttz.i64", + "llvm.ctpop.i32", + "llvm.ctpop.i64", +}; +/* clang-format on */ static bool aot_compile_int_bit_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, @@ -406,21 +369,14 @@ aot_compile_int_bit_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Call the LLVM intrinsic function */ if (type < POP_CNT32) - DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, - bit_cnt_llvm_intrinsic[type], - ret_type, - param_types, - 2, - operand, - zero_undef), + DEF_INT_UNARY_OP(aot_call_llvm_intrinsic( + comp_ctx, func_ctx, bit_cnt_llvm_intrinsic[type], + ret_type, param_types, 2, operand, zero_undef), NULL); else - DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, - bit_cnt_llvm_intrinsic[type], - ret_type, - param_types, - 1, - operand), + DEF_INT_UNARY_OP(aot_call_llvm_intrinsic( + comp_ctx, func_ctx, bit_cnt_llvm_intrinsic[type], + ret_type, param_types, 1, operand), NULL); return true; @@ -431,11 +387,14 @@ aot_compile_int_bit_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, static bool compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - LLVMValueRef left, LLVMValueRef right, - LLVMValueRef overflow_cond, bool is_i32) + LLVMValueRef left, LLVMValueRef right, LLVMValueRef overflow_cond, + bool is_i32) { LLVMValueRef phi, no_overflow_value, zero = is_i32 ? I32_ZERO : I64_ZERO; LLVMBasicBlockRef block_curr, no_overflow_block, rems_end_block; + LLVMTypeRef param_types[2]; + + param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE; block_curr = LLVMGetInsertBlock(comp_ctx->builder); @@ -444,8 +403,8 @@ compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, ADD_BASIC_BLOCK(no_overflow_block, "rems_no_overflow"); /* Create condition br */ - if (!LLVMBuildCondBr(comp_ctx->builder, overflow_cond, - rems_end_block, no_overflow_block)) { + if (!LLVMBuildCondBr(comp_ctx->builder, overflow_cond, rems_end_block, + no_overflow_block)) { aot_set_last_error("llvm build cond br failed."); return false; } @@ -453,8 +412,9 @@ compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Translate no_overflow_block */ LLVMPositionBuilderAtEnd(comp_ctx->builder, no_overflow_block); - /* Calculate the rem value */ - LLVM_BUILD_OP(SRem, left, right, no_overflow_value, "rem_s", false); + LLVM_BUILD_OP_OR_INTRINSIC(SRem, left, right, no_overflow_value, + is_i32 ? "i32.rem_s" : "i64.rem_s", "rem_s", + false); /* Jump to rems_end block */ if (!LLVMBuildBr(comp_ctx->builder, rems_end_block)) { @@ -466,8 +426,7 @@ compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMPositionBuilderAtEnd(comp_ctx->builder, rems_end_block); /* Create result phi */ - if (!(phi = LLVMBuildPhi(comp_ctx->builder, - is_i32 ? I32_TYPE : I64_TYPE, + if (!(phi = LLVMBuildPhi(comp_ctx->builder, is_i32 ? I32_TYPE : I64_TYPE, "rems_result_phi"))) { aot_set_last_error("llvm build phi failed."); return false; @@ -494,26 +453,41 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { LLVMValueRef left, right, cmp_div_zero, overflow, res; LLVMBasicBlockRef check_div_zero_succ, check_overflow_succ; + LLVMTypeRef param_types[2]; + const char *intrinsic = NULL; - bh_assert(arith_op == INT_DIV_S - || arith_op == INT_DIV_U - || arith_op == INT_REM_S - || arith_op == INT_REM_U); + param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE; + + bh_assert(arith_op == INT_DIV_S || arith_op == INT_DIV_U + || arith_op == INT_REM_S || arith_op == INT_REM_U); POP_INT(right); POP_INT(left); - if (LLVMIsConstant(right)) { + if (LLVMIsUndef(right) || LLVMIsUndef(left) +#if LLVM_VERSION_NUMBER >= 12 + || LLVMIsPoison(right) || LLVMIsPoison(left) +#endif + ) { + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, + false, NULL, NULL))) { + goto fail; + } + return aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); + } + + if (LLVMIsEfficientConstInt(right)) { int64 right_val = (int64)LLVMConstIntGetSExtValue(right); switch (right_val) { case 0: /* Directly throw exception if divided by zero */ if (!(aot_emit_exception(comp_ctx, func_ctx, - EXCE_INTEGER_DIVIDE_BY_ZERO, - false, NULL, NULL))) + EXCE_INTEGER_DIVIDE_BY_ZERO, false, + NULL, NULL))) goto fail; - return aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); + return aot_handle_next_reachable_block(comp_ctx, func_ctx, + p_frame_ip); case 1: if (arith_op == INT_DIV_S || arith_op == INT_DIV_U) PUSH_INT(left); @@ -522,17 +496,15 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return true; case -1: if (arith_op == INT_DIV_S) { - LLVM_BUILD_ICMP(LLVMIntEQ, left, - is_i32 ? I32_MIN : I64_MIN, + LLVM_BUILD_ICMP(LLVMIntEQ, left, is_i32 ? I32_MIN : I64_MIN, overflow, "overflow"); ADD_BASIC_BLOCK(check_overflow_succ, "check_overflow_success"); /* Throw conditional exception if overflow */ if (!(aot_emit_exception(comp_ctx, func_ctx, - EXCE_INTEGER_OVERFLOW, - true, overflow, - check_overflow_succ))) + EXCE_INTEGER_OVERFLOW, true, + overflow, check_overflow_succ))) goto fail; /* Push -(left) to stack */ @@ -551,21 +523,29 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* fall to default */ goto handle_default; } -handle_default: + handle_default: default: /* Build div */ switch (arith_op) { case INT_DIV_S: - LLVM_BUILD_OP(SDiv, left, right, res, "div_s", false); + LLVM_BUILD_OP_OR_INTRINSIC( + SDiv, left, right, res, + is_i32 ? "i32.div_s" : "i64.div_s", "div_s", false); break; case INT_DIV_U: - LLVM_BUILD_OP(UDiv, left, right, res, "div_u", false); + LLVM_BUILD_OP_OR_INTRINSIC( + UDiv, left, right, res, + is_i32 ? "i32.div_u" : "i64.div_u", "div_u", false); break; case INT_REM_S: - LLVM_BUILD_OP(SRem, left, right, res, "rem_s", false); + LLVM_BUILD_OP_OR_INTRINSIC( + SRem, left, right, res, + is_i32 ? "i32.rem_s" : "i64.rem_s", "rem_s", false); break; case INT_REM_U: - LLVM_BUILD_OP(URem, left, right, res, "rem_u", false); + LLVM_BUILD_OP_OR_INTRINSIC( + URem, left, right, res, + is_i32 ? "i32.rem_u" : "i64.rem_u", "rem_u", false); break; default: bh_assert(0); @@ -577,7 +557,7 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } } else { - /* Check divied by zero */ + /* Check divided by zero */ LLVM_BUILD_ICMP(LLVMIntEQ, right, is_i32 ? I32_ZERO : I64_ZERO, cmp_div_zero, "cmp_div_zero"); ADD_BASIC_BLOCK(check_div_zero_succ, "check_div_zero_success"); @@ -600,15 +580,26 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Throw conditional exception if integer overflow */ if (!(aot_emit_exception(comp_ctx, func_ctx, - EXCE_INTEGER_OVERFLOW, - true, overflow, check_overflow_succ))) + EXCE_INTEGER_OVERFLOW, true, overflow, + check_overflow_succ))) goto fail; - LLVM_BUILD_OP(SDiv, left, right, res, "div_s", false); + LLVM_BUILD_OP_OR_INTRINSIC(SDiv, left, right, res, + is_i32 ? "i32.div_s" : "i64.div_s", + "div_s", false); PUSH_INT(res); return true; case INT_DIV_U: - LLVM_BUILD_OP(UDiv, left, right, res, "div_u", false); + intrinsic = is_i32 ? "i32.div_u" : "i64.div_u"; + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, intrinsic)) { + res = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, + param_types[0], param_types, + 2, left, right); + } + else { + LLVM_BUILD_OP(UDiv, left, right, res, "div_u", false); + } PUSH_INT(res); return true; case INT_REM_S: @@ -617,16 +608,17 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, CHECK_INT_OVERFLOW(I32); else CHECK_INT_OVERFLOW(I64); - return compile_rems(comp_ctx, func_ctx, - left, right, overflow, + return compile_rems(comp_ctx, func_ctx, left, right, overflow, is_i32); case INT_REM_U: - LLVM_BUILD_OP(URem, left, right, res, "rem_u", false); + LLVM_BUILD_OP_OR_INTRINSIC(URem, left, right, res, + is_i32 ? "i32.rem_u" : "i64.rem_u", + "rem_u", false); PUSH_INT(res); return true; default: bh_assert(0); - return false;; + return false; } } @@ -635,8 +627,7 @@ compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } static LLVMValueRef -compile_int_add(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, +compile_int_add(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right, bool is_i32) { /* If one of the operands is 0, just return the other */ @@ -650,8 +641,7 @@ compile_int_add(AOTCompContext *comp_ctx, } static LLVMValueRef -compile_int_sub(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, +compile_int_sub(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right, bool is_i32) { /* If the right operand is 0, just return the left */ @@ -663,21 +653,28 @@ compile_int_sub(AOTCompContext *comp_ctx, } static LLVMValueRef -compile_int_mul(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, - bool is_i32) +compile_int_mul(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef left, LLVMValueRef right, bool is_i32) { /* If one of the operands is 0, just return constant 0 */ if (IS_CONST_ZERO(left) || IS_CONST_ZERO(right)) return is_i32 ? I32_ZERO : I64_ZERO; /* Build mul */ - return LLVMBuildMul(comp_ctx->builder, left, right, "mul"); + LLVMTypeRef param_types[2]; + param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE; + + LLVMValueRef res; + LLVM_BUILD_OP_OR_INTRINSIC(Mul, left, right, res, + is_i32 ? "i32.mul" : "i64.mul", "mul", false); + + return res; } static bool compile_op_int_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - IntArithmetic arith_op, bool is_i32, uint8 **p_frame_ip) + IntArithmetic arith_op, bool is_i32, + uint8 **p_frame_ip) { switch (arith_op) { case INT_ADD: @@ -689,14 +686,16 @@ compile_op_int_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, "compile int sub fail."); return true; case INT_MUL: - DEF_INT_BINARY_OP(compile_int_mul(comp_ctx, left, right, is_i32), - "compile int mul fail."); + DEF_INT_BINARY_OP( + compile_int_mul(comp_ctx, func_ctx, left, right, is_i32), + "compile int mul fail."); return true; case INT_DIV_S: case INT_DIV_U: case INT_REM_S: case INT_REM_U: - return compile_int_div(comp_ctx, func_ctx, arith_op, is_i32, p_frame_ip); + return compile_int_div(comp_ctx, func_ctx, arith_op, is_i32, + p_frame_ip); default: bh_assert(0); return false; @@ -712,19 +711,18 @@ compile_op_int_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { switch (bitwise_op) { case INT_AND: - DEF_INT_BINARY_OP(LLVMBuildAnd(comp_ctx->builder, - left, right, "and"), - "llvm build and fail."); + DEF_INT_BINARY_OP( + LLVMBuildAnd(comp_ctx->builder, left, right, "and"), + "llvm build and fail."); return true; case INT_OR: - DEF_INT_BINARY_OP(LLVMBuildOr(comp_ctx->builder, - left, right, "or"), + DEF_INT_BINARY_OP(LLVMBuildOr(comp_ctx->builder, left, right, "or"), "llvm build or fail."); return true; case INT_XOR: - DEF_INT_BINARY_OP(LLVMBuildXor(comp_ctx->builder, - left, right, "xor"), - "llvm build xor fail."); + DEF_INT_BINARY_OP( + LLVMBuildXor(comp_ctx->builder, left, right, "xor"), + "llvm build xor fail."); return true; default: bh_assert(0); @@ -736,82 +734,93 @@ compile_op_int_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } static LLVMValueRef -compile_int_shl(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, - bool is_i32) +compile_int_shl(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef left, LLVMValueRef right, bool is_i32) { LLVMValueRef res; - if (strcmp(comp_ctx->target_arch, "x86_64") != 0 - && strcmp(comp_ctx->target_arch, "i386") != 0) - SHIFT_COUNT_MASK; + SHIFT_COUNT_MASK; /* Build shl */ - LLVM_BUILD_OP(Shl, left, right, res, "shl", NULL); + LLVMTypeRef param_types[2]; + param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE; + + LLVM_BUILD_OP_OR_INTRINSIC(Shl, left, right, res, + is_i32 ? "i32.shl" : "i64.shl", "shl", false); return res; } static LLVMValueRef -compile_int_shr_s(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, - bool is_i32) +compile_int_shr_s(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef left, LLVMValueRef right, bool is_i32) { LLVMValueRef res; - if (strcmp(comp_ctx->target_arch, "x86_64") != 0 - && strcmp(comp_ctx->target_arch, "i386") != 0) - SHIFT_COUNT_MASK; + SHIFT_COUNT_MASK; /* Build shl */ - LLVM_BUILD_OP(AShr, left, right, res, "shr_s", NULL); + LLVMTypeRef param_types[2]; + param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE; + + LLVM_BUILD_OP_OR_INTRINSIC(AShr, left, right, res, + is_i32 ? "i32.shr_s" : "i64.shr_s", "shr_s", + false); return res; } static LLVMValueRef -compile_int_shr_u(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, - bool is_i32) +compile_int_shr_u(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef left, LLVMValueRef right, bool is_i32) { LLVMValueRef res; - if (strcmp(comp_ctx->target_arch, "x86_64") != 0 - && strcmp(comp_ctx->target_arch, "i386") != 0) - SHIFT_COUNT_MASK; + SHIFT_COUNT_MASK; /* Build shl */ - LLVM_BUILD_OP(LShr, left, right, res, "shr_u", NULL); + LLVMTypeRef param_types[2]; + param_types[1] = param_types[0] = is_i32 ? I32_TYPE : I64_TYPE; + + LLVM_BUILD_OP_OR_INTRINSIC(LShr, left, right, res, + is_i32 ? "i32.shr_u" : "i64.shr_u", "shr_u", + false); return res; } static LLVMValueRef -compile_int_rot(AOTCompContext *comp_ctx, - LLVMValueRef left, LLVMValueRef right, - bool is_rotl, - bool is_i32) +compile_int_rot(AOTCompContext *comp_ctx, LLVMValueRef left, LLVMValueRef right, + bool is_rotl, bool is_i32) { LLVMValueRef bits_minus_shift_count, res, tmp_l, tmp_r; char *name = is_rotl ? "rotl" : "rotr"; SHIFT_COUNT_MASK; - /* Calculate (bits - shif_count) */ - LLVM_BUILD_OP(Sub, - is_i32 ? I32_32 : I64_64, - right, - bits_minus_shift_count, - "bits_minus_shift_count", - NULL); + /* rotl/rotr with 0 */ + if (IS_CONST_ZERO(right)) + return left; + + /* Calculate (bits - shift_count) */ + LLVM_BUILD_OP(Sub, is_i32 ? I32_32 : I64_64, right, bits_minus_shift_count, + "bits_minus_shift_count", NULL); + /* Calculate (bits - shift_count) & mask */ + bits_minus_shift_count = + LLVMBuildAnd(comp_ctx->builder, bits_minus_shift_count, + is_i32 ? I32_31 : I64_63, "bits_minus_shift_count_and"); + if (!bits_minus_shift_count) { + aot_set_last_error("llvm build and failed."); + return NULL; + } if (is_rotl) { - /* left<>(BITS-count) */ + /* (left << count) | (left >> ((BITS - count) & mask)) */ LLVM_BUILD_OP(Shl, left, right, tmp_l, "tmp_l", NULL); LLVM_BUILD_OP(LShr, left, bits_minus_shift_count, tmp_r, "tmp_r", NULL); } else { - /* left>>count | left<<(BITS-count) */ + /* (left >> count) | (left << ((BITS - count) & mask)) */ LLVM_BUILD_OP(LShr, left, right, tmp_l, "tmp_l", NULL); LLVM_BUILD_OP(Shl, left, bits_minus_shift_count, tmp_r, "tmp_r", NULL); } @@ -827,26 +836,26 @@ compile_op_int_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { switch (shift_op) { case INT_SHL: - DEF_INT_BINARY_OP(compile_int_shl(comp_ctx, left, right, is_i32), - NULL); + DEF_INT_BINARY_OP( + compile_int_shl(comp_ctx, func_ctx, left, right, is_i32), NULL); return true; case INT_SHR_S: - DEF_INT_BINARY_OP(compile_int_shr_s(comp_ctx, left, right, is_i32), - NULL); - return true; + DEF_INT_BINARY_OP( + compile_int_shr_s(comp_ctx, func_ctx, left, right, is_i32), + NULL); + return true; case INT_SHR_U: - DEF_INT_BINARY_OP(compile_int_shr_u(comp_ctx, left, right, is_i32), - NULL); + DEF_INT_BINARY_OP( + compile_int_shr_u(comp_ctx, func_ctx, left, right, is_i32), + NULL); return true; case INT_ROTL: - DEF_INT_BINARY_OP(compile_int_rot(comp_ctx, left, right, - true, is_i32), - NULL); + DEF_INT_BINARY_OP( + compile_int_rot(comp_ctx, left, right, true, is_i32), NULL); return true; case INT_ROTR: - DEF_INT_BINARY_OP(compile_int_rot(comp_ctx, left, right, - false, is_i32), - NULL); + DEF_INT_BINARY_OP( + compile_int_rot(comp_ctx, left, right, false, is_i32), NULL); return true; default: bh_assert(0); @@ -857,76 +866,165 @@ compile_op_int_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } +static bool +is_target_arm(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "arm", 3) + || !strncmp(comp_ctx->target_arch, "aarch64", 7) + || !strncmp(comp_ctx->target_arch, "thumb", 5); +} + +static bool +is_target_x86(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "x86_64", 6) + || !strncmp(comp_ctx->target_arch, "i386", 4); +} + +static bool +is_target_xtensa(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "xtensa", 6); +} + +static bool +is_target_mips(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "mips", 4); +} + +static bool +is_target_riscv(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "riscv", 5); +} + +static bool +is_targeting_soft_float(AOTCompContext *comp_ctx, bool is_f32) +{ + bool ret = false; + char *feature_string; + + if (!(feature_string = + LLVMGetTargetMachineFeatureString(comp_ctx->target_machine))) { + aot_set_last_error("llvm get target machine feature string fail."); + return false; + } + + /* Note: + * LLVM CodeGen uses FPU Coprocessor registers by default, + * so user must specify '--cpu-features=+soft-float' to wamrc if the target + * doesn't have or enable FPU on arm, x86 or mips. */ + if (is_target_arm(comp_ctx) || is_target_x86(comp_ctx) + || is_target_mips(comp_ctx)) { + ret = strstr(feature_string, "+soft-float") ? true : false; + } + else if (is_target_xtensa(comp_ctx)) { + /* Note: + * 1. The Floating-Point Coprocessor Option of xtensa only support + * single-precision floating-point operations, so must use soft-float + * for f64(i.e. double). + * 2. LLVM CodeGen uses Floating-Point Coprocessor registers by default, + * so user must specify '--cpu-features=-fp' to wamrc if the target + * doesn't have or enable Floating-Point Coprocessor Option on xtensa. + */ + if (comp_ctx->disable_llvm_intrinsics) + ret = false; + else + ret = (!is_f32 || strstr(feature_string, "-fp")) ? true : false; + } + else if (is_target_riscv(comp_ctx)) { + /* + * Note: Use builtin intrinsics since hardware float operation + * will cause rodata relocation, this will try to use hardware + * float unit (by return false) but handled by software finally + */ + if (comp_ctx->disable_llvm_intrinsics) + ret = false; + else + ret = !strstr(feature_string, "+d") ? true : false; + } + else { + ret = true; + } + + LLVMDisposeMessage(feature_string); + return ret; +} + static bool compile_op_float_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, FloatArithmetic arith_op, bool is_f32) { switch (arith_op) { case FLOAT_ADD: - DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( - comp_ctx, - (is_f32 - ? "llvm.experimental.constrained.fadd.f32" - : "llvm.experimental.constrained.fadd.f64"), - is_f32, - left, - right, - comp_ctx->fp_rounding_mode, - comp_ctx->fp_exception_behavior), - NULL); + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP( + LLVMBuildFAdd(comp_ctx->builder, left, right, "fadd"), + "llvm build fadd fail."); + else + DEF_FP_BINARY_OP( + call_llvm_float_experimental_constrained_intrinsic( + comp_ctx, func_ctx, is_f32, + (is_f32 ? "llvm.experimental.constrained.fadd.f32" + : "llvm.experimental.constrained.fadd.f64"), + left, right, comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); return true; case FLOAT_SUB: - DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( - comp_ctx, - (is_f32 - ? "llvm.experimental.constrained.fsub.f32" - : "llvm.experimental.constrained.fsub.f64"), - is_f32, - left, - right, - comp_ctx->fp_rounding_mode, - comp_ctx->fp_exception_behavior), - NULL); + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP( + LLVMBuildFSub(comp_ctx->builder, left, right, "fsub"), + "llvm build fsub fail."); + else + DEF_FP_BINARY_OP( + call_llvm_float_experimental_constrained_intrinsic( + comp_ctx, func_ctx, is_f32, + (is_f32 ? "llvm.experimental.constrained.fsub.f32" + : "llvm.experimental.constrained.fsub.f64"), + left, right, comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); return true; case FLOAT_MUL: - DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( - comp_ctx, - (is_f32 - ? "llvm.experimental.constrained.fmul.f32" - : "llvm.experimental.constrained.fmul.f64"), - is_f32, - left, - right, - comp_ctx->fp_rounding_mode, - comp_ctx->fp_exception_behavior), - NULL); + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP( + LLVMBuildFMul(comp_ctx->builder, left, right, "fmul"), + "llvm build fmul fail."); + else + DEF_FP_BINARY_OP( + call_llvm_float_experimental_constrained_intrinsic( + comp_ctx, func_ctx, is_f32, + (is_f32 ? "llvm.experimental.constrained.fmul.f32" + : "llvm.experimental.constrained.fmul.f64"), + left, right, comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); return true; case FLOAT_DIV: - DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( - comp_ctx, - (is_f32 - ? "llvm.experimental.constrained.fdiv.f32" - : "llvm.experimental.constrained.fdiv.f64"), - is_f32, - left, - right, - comp_ctx->fp_rounding_mode, - comp_ctx->fp_exception_behavior), - NULL); + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP( + LLVMBuildFDiv(comp_ctx->builder, left, right, "fdiv"), + "llvm build fdiv fail."); + else + DEF_FP_BINARY_OP( + call_llvm_float_experimental_constrained_intrinsic( + comp_ctx, func_ctx, is_f32, + (is_f32 ? "llvm.experimental.constrained.fdiv.f32" + : "llvm.experimental.constrained.fdiv.f64"), + left, right, comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); return true; case FLOAT_MIN: - DEF_FP_BINARY_OP(compile_op_float_min_max(comp_ctx, - is_f32, - left, - right, - true), + DEF_FP_BINARY_OP(compile_op_float_min_max( + comp_ctx, func_ctx, is_f32, left, right, true), NULL); return true; case FLOAT_MAX: - DEF_FP_BINARY_OP(compile_op_float_min_max(comp_ctx, - is_f32, - left, - right, + DEF_FP_BINARY_OP(compile_op_float_min_max(comp_ctx, func_ctx, + is_f32, left, right, false), NULL); @@ -942,9 +1040,8 @@ compile_op_float_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, static LLVMValueRef call_llvm_float_math_intrinsic(AOTCompContext *comp_ctx, - const char *intrinsic, - bool is_f32, - ...) + AOTFuncContext *func_ctx, bool is_f32, + const char *intrinsic, ...) { va_list param_value_list; LLVMValueRef ret; @@ -952,14 +1049,10 @@ call_llvm_float_math_intrinsic(AOTCompContext *comp_ctx, param_type = ret_type; - va_start(param_value_list, is_f32); + va_start(param_value_list, intrinsic); - ret = call_llvm_intrinsic_v(comp_ctx, - intrinsic, - ret_type, - ¶m_type, - 1, - param_value_list); + ret = aot_call_llvm_intrinsic_v(comp_ctx, func_ctx, intrinsic, ret_type, + ¶m_type, 1, param_value_list); va_end(param_value_list); @@ -972,11 +1065,10 @@ compile_op_float_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { switch (math_op) { case FLOAT_ABS: - DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, - is_f32 ? "llvm.fabs.f32" : - "llvm.fabs.f64", - is_f32, - operand), + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic( + comp_ctx, func_ctx, is_f32, + is_f32 ? "llvm.fabs.f32" : "llvm.fabs.f64", + operand), NULL); return true; case FLOAT_NEG: @@ -985,48 +1077,50 @@ compile_op_float_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return true; case FLOAT_CEIL: - DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, - is_f32 ? "llvm.ceil.f32" : - "llvm.ceil.f64", - is_f32, - operand), + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic( + comp_ctx, func_ctx, is_f32, + is_f32 ? "llvm.ceil.f32" : "llvm.ceil.f64", + operand), NULL); return true; case FLOAT_FLOOR: - DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, - is_f32 ? "llvm.floor.f32" : - "llvm.floor.f64", - is_f32, - operand), + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic( + comp_ctx, func_ctx, is_f32, + is_f32 ? "llvm.floor.f32" : "llvm.floor.f64", + operand), NULL); return true; case FLOAT_TRUNC: - DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, - is_f32 ? "llvm.trunc.f32" : - "llvm.trunc.f64", - is_f32, - operand), + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic( + comp_ctx, func_ctx, is_f32, + is_f32 ? "llvm.trunc.f32" : "llvm.trunc.f64", + operand), NULL); return true; case FLOAT_NEAREST: - DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, - is_f32 ? "llvm.rint.f32" : - "llvm.rint.f64", - is_f32, - operand), + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic( + comp_ctx, func_ctx, is_f32, + is_f32 ? "llvm.rint.f32" : "llvm.rint.f64", + operand), NULL); return true; case FLOAT_SQRT: - DEF_FP_UNARY_OP(call_llvm_libm_expermental_constrained_intrinsic( - comp_ctx, - (is_f32 - ? "llvm.experimental.constrained.sqrt.f32" - : "llvm.experimental.constrained.sqrt.f64"), - is_f32, - operand, - comp_ctx->fp_rounding_mode, - comp_ctx->fp_exception_behavior), - NULL); + if (is_targeting_soft_float(comp_ctx, is_f32) + || comp_ctx->disable_llvm_intrinsics) + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic( + comp_ctx, func_ctx, is_f32, + is_f32 ? "llvm.sqrt.f32" : "llvm.sqrt.f64", + operand), + NULL); + else + DEF_FP_UNARY_OP( + call_llvm_libm_experimental_constrained_intrinsic( + comp_ctx, func_ctx, is_f32, + (is_f32 ? "llvm.experimental.constrained.sqrt.f32" + : "llvm.experimental.constrained.sqrt.f64"), + operand, comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); return true; default: bh_assert(0); @@ -1047,15 +1141,11 @@ compile_float_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, param_types[0] = param_types[1] = ret_type = is_f32 ? F32_TYPE : F64_TYPE; - DEF_FP_BINARY_OP(call_llvm_intrinsic(comp_ctx, - is_f32 ? "llvm.copysign.f32" : - "llvm.copysign.f64", - ret_type, - param_types, - 2, - left, - right), - NULL); + DEF_FP_BINARY_OP(aot_call_llvm_intrinsic( + comp_ctx, func_ctx, + is_f32 ? "llvm.copysign.f32" : "llvm.copysign.f64", + ret_type, param_types, 2, left, right), + NULL); return true; fail: @@ -1099,17 +1189,21 @@ aot_compile_op_i64_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) } bool -aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - IntArithmetic arith_op, uint8 **p_frame_ip) +aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntArithmetic arith_op, + uint8 **p_frame_ip) { - return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, true, p_frame_ip); + return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, true, + p_frame_ip); } bool -aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - IntArithmetic arith_op, uint8 **p_frame_ip) +aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntArithmetic arith_op, + uint8 **p_frame_ip) { - return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, false, p_frame_ip); + return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, false, + p_frame_ip); } bool @@ -1181,4 +1275,3 @@ aot_compile_op_f64_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { return compile_float_copysign(comp_ctx, func_ctx, false); } - diff --git a/core/iwasm/compilation/aot_emit_numberic.h b/core/iwasm/compilation/aot_emit_numberic.h index c9dd307ba8..7206315dfe 100644 --- a/core/iwasm/compilation/aot_emit_numberic.h +++ b/core/iwasm/compilation/aot_emit_numberic.h @@ -31,12 +31,14 @@ bool aot_compile_op_i64_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); bool -aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - IntArithmetic arith_op, uint8 **p_frame_ip); +aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntArithmetic arith_op, + uint8 **p_frame_ip); bool -aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - IntArithmetic arith_op, uint8 **p_frame_ip); +aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntArithmetic arith_op, + uint8 **p_frame_ip); bool aot_compile_op_i32_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, @@ -63,11 +65,13 @@ aot_compile_op_f64_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, FloatMath math_op); bool -aot_compile_op_f32_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, +aot_compile_op_f32_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, FloatArithmetic arith_op); bool -aot_compile_op_f64_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, +aot_compile_op_f64_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, FloatArithmetic arith_op); bool @@ -81,4 +85,3 @@ aot_compile_op_f64_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); #endif #endif /* end of _AOT_EMIT_NUMBERIC_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_parametric.c b/core/iwasm/compilation/aot_emit_parametric.c index 0660f9562f..198e045220 100644 --- a/core/iwasm/compilation/aot_emit_parametric.c +++ b/core/iwasm/compilation/aot_emit_parametric.c @@ -7,8 +7,7 @@ static bool pop_value_from_wasm_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - LLVMValueRef *p_value, - bool is_32, uint8 *p_type) + LLVMValueRef *p_value, bool is_32, uint8 *p_type) { AOTValue *aot_value; uint8 type; @@ -22,14 +21,14 @@ pop_value_from_wasm_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } - aot_value = aot_value_stack_pop - (&func_ctx->block_stack.block_list_end->value_stack); + aot_value = aot_value_stack_pop( + comp_ctx, &func_ctx->block_stack.block_list_end->value_stack); type = aot_value->type; if (aot_value->type == VALUE_TYPE_I1) { if (!(aot_value->value = - LLVMBuildZExt(comp_ctx->builder, aot_value->value, - I32_TYPE, "val_s_ext"))) { + LLVMBuildZExt(comp_ctx->builder, aot_value->value, I32_TYPE, + "val_s_ext"))) { aot_set_last_error("llvm build sign ext failed."); return false; } @@ -43,20 +42,41 @@ pop_value_from_wasm_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, *p_value = aot_value->value; } - wasm_free(aot_value); - - if ((is_32 - && (type != VALUE_TYPE_I32 && type != VALUE_TYPE_F32)) - || (!is_32 - && (type != VALUE_TYPE_I64 && type != VALUE_TYPE_F64))) { - aot_set_last_error("invalid WASM stack data type."); - return false; + wasm_runtime_free(aot_value); + + if (is_32) { + /* is_32: i32, f32, ref.func, ref.extern, v128, + or GC ref types */ + if (!(type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 + || type == VALUE_TYPE_V128 + || (comp_ctx->enable_ref_types + && (type == VALUE_TYPE_FUNCREF + || type == VALUE_TYPE_EXTERNREF)) +#if WASM_ENABLE_GC != 0 + || (comp_ctx->enable_gc && type == VALUE_TYPE_GC_REF) +#endif + )) { + aot_set_last_error("invalid WASM stack data type."); + return false; + } + } + else { + /* !is_32: i64, f64, or GC ref types */ + if (!(type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64 +#if WASM_ENABLE_GC != 0 + || (comp_ctx->enable_gc && type == VALUE_TYPE_GC_REF) + /* may be i32 which denotes funcref/externref */ + || (!comp_ctx->enable_gc && type == VALUE_TYPE_I32) +#endif + )) { + aot_set_last_error("invalid WASM stack data type."); + return false; + } } return true; } - bool aot_compile_op_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool is_drop_32) @@ -76,8 +96,10 @@ aot_compile_op_select(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, POP_COND(cond); - if (!pop_value_from_wasm_stack(comp_ctx, func_ctx, &val2, is_select_32, &val2_type) - || !pop_value_from_wasm_stack(comp_ctx, func_ctx, &val1, is_select_32, &val1_type)) + if (!pop_value_from_wasm_stack(comp_ctx, func_ctx, &val2, is_select_32, + &val2_type) + || !pop_value_from_wasm_stack(comp_ctx, func_ctx, &val1, is_select_32, + &val1_type)) return false; if (val1_type != val2_type) { @@ -85,9 +107,8 @@ aot_compile_op_select(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return false; } - if (!(selected = LLVMBuildSelect(comp_ctx->builder, - cond, val1, val2, - "select"))) { + if (!(selected = + LLVMBuildSelect(comp_ctx->builder, cond, val1, val2, "select"))) { aot_set_last_error("llvm build select failed."); return false; } @@ -99,4 +120,3 @@ aot_compile_op_select(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, fail: return false; } - diff --git a/core/iwasm/compilation/aot_emit_parametric.h b/core/iwasm/compilation/aot_emit_parametric.h index 9587cb17eb..68fe8f11d3 100644 --- a/core/iwasm/compilation/aot_emit_parametric.h +++ b/core/iwasm/compilation/aot_emit_parametric.h @@ -18,12 +18,10 @@ aot_compile_op_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool aot_compile_op_select(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - bool is_select_32); - + bool is_select_32); #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_EMIT_PARAMETRIC_H_ */ - diff --git a/core/iwasm/compilation/aot_emit_stringref.c b/core/iwasm/compilation/aot_emit_stringref.c new file mode 100644 index 0000000000..1642cee00b --- /dev/null +++ b/core/iwasm/compilation/aot_emit_stringref.c @@ -0,0 +1,1443 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if WASM_ENABLE_STRINGREF != 0 + +#include "aot_emit_stringref.h" +#include "aot_emit_exception.h" +#include "aot_emit_memory.h" +#include "aot_emit_gc.h" +#include "aot.h" +#include "aot_compiler.h" +#include "aot_emit_memory.h" +#include "gc_object.h" +#include "string_object.h" + +#define BUILD_ISNULL(ptr, res, name) \ + do { \ + if (!(res = LLVMBuildIsNull(comp_ctx->builder, ptr, name))) { \ + aot_set_last_error("llvm build isnull failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_ISNOTNULL(ptr, res, name) \ + do { \ + if (!(res = LLVMBuildIsNotNull(comp_ctx->builder, ptr, name))) { \ + aot_set_last_error("llvm build isnotnull failed."); \ + goto fail; \ + } \ + } while (0) + +#define ADD_BASIC_BLOCK(block, name) \ + do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + } while (0) + +#define CURR_BLOCK() LLVMGetInsertBlock(comp_ctx->builder) + +#define MOVE_BLOCK_AFTER(llvm_block, llvm_block_after) \ + LLVMMoveBasicBlockAfter(llvm_block, llvm_block_after) + +#define MOVE_BLOCK_AFTER_CURR(llvm_block) \ + LLVMMoveBasicBlockAfter(llvm_block, CURR_BLOCK()) + +#define DEFINE_STRINGREF_CHECK_VAR() \ + LLVMBasicBlockRef check_string_obj_succ, check_stringref_obj_succ; \ + LLVMValueRef cmp + +#define CHECK_STRING_OBJ(str_obj) \ + do { \ + ADD_BASIC_BLOCK(check_string_obj_succ, "check string obj succ"); \ + MOVE_BLOCK_AFTER_CURR(check_string_obj_succ); \ + \ + BUILD_ISNULL(str_obj, cmp, "cmp_string_obj"); \ + if (!aot_emit_exception(comp_ctx, func_ctx, \ + EXCE_FAILED_TO_CREATE_STRING, true, cmp, \ + check_string_obj_succ)) \ + goto fail; \ + } while (0) + +#define CHECK_STRINGREF_INTERNAL(stringref_obj, exce_id, name) \ + do { \ + ADD_BASIC_BLOCK(check_stringref_obj_succ, "check " name " obj succ"); \ + MOVE_BLOCK_AFTER(check_stringref_obj_succ, check_string_obj_succ); \ + \ + BUILD_ISNULL(stringref_obj, cmp, "cmp_" name "_obj"); \ + if (!aot_emit_exception(comp_ctx, func_ctx, exce_id, true, cmp, \ + check_stringref_obj_succ)) \ + goto fail; \ + } while (0) + +#define CHECK_STRINGREF_OBJ(stringref_obj) \ + CHECK_STRINGREF_INTERNAL(stringref_obj, EXCE_FAILED_TO_CREATE_STRINGREF, \ + "stringref") + +#define CHECK_STRINGVIEW_OBJ(stringview_obj) \ + CHECK_STRINGREF_INTERNAL(stringview_obj, EXCE_FAILED_TO_CREATE_STRINGVIEW, \ + "stringview") + +#define CHECK_STRING_ENCODE(value) \ + do { \ + ADD_BASIC_BLOCK(check_string_encode_succ, "check string encode succ"); \ + MOVE_BLOCK_AFTER_CURR(check_string_encode_succ); \ + \ + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntSLT, value, \ + I32_ZERO, "cmp_string_encode"))) { \ + aot_set_last_error("llvm build icmp failed."); \ + goto fail; \ + } \ + \ + if (!aot_emit_exception(comp_ctx, func_ctx, \ + EXCE_FAILED_TO_ENCODE_STRING, true, cmp, \ + check_string_encode_succ)) \ + goto fail; \ + } while (0) + +static bool +aot_call_wasm_stringref_obj_new(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, LLVMValueRef str_obj, + uint32 stringref_type, uint32 pos, + LLVMValueRef *stringref_obj) +{ + LLVMValueRef param_values[3], func, value, res; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + uint32 argc = 2; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + param_types[2] = I32_TYPE; + ret_type = INT8_PTR_TYPE; + + if (stringref_type == WASM_TYPE_STRINGREF) { + GET_AOT_FUNCTION(wasm_stringref_obj_new, argc); + } + else if (stringref_type == WASM_TYPE_STRINGVIEWWTF8) { + GET_AOT_FUNCTION(wasm_stringview_wtf8_obj_new, argc); + } + else if (stringref_type == WASM_TYPE_STRINGVIEWWTF16) { + GET_AOT_FUNCTION(wasm_stringview_wtf16_obj_new, argc); + } + else { + argc = 3; + GET_AOT_FUNCTION(wasm_stringview_iter_obj_new, argc); + } + + param_values[0] = func_ctx->exec_env; + param_values[1] = str_obj; + if (stringref_type == WASM_TYPE_STRINGVIEWITER) { + param_values[2] = I32_CONST(pos); + } + + if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + argc, "create_stringref"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + *stringref_obj = res; + + return true; +fail: + return false; +} + +static LLVMValueRef +aot_stringref_obj_get_value(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef stringref_obj) +{ + LLVMValueRef str_obj_ptr, str_obj, host_ptr_offset; + + /* header */ + host_ptr_offset = I32_CONST(comp_ctx->pointer_size); + + if (!(stringref_obj = + LLVMBuildBitCast(comp_ctx->builder, stringref_obj, INT8_PTR_TYPE, + "stringref_obj_i8p"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(str_obj_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, stringref_obj, + &host_ptr_offset, 1, "str_obj_i8p"))) { + aot_set_last_error("llvm build gep failed."); + goto fail; + } + + if (!(str_obj_ptr = LLVMBuildBitCast(comp_ctx->builder, str_obj_ptr, + GC_REF_PTR_TYPE, "str_obj_gcref_p"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(str_obj = LLVMBuildLoad2(comp_ctx->builder, GC_REF_TYPE, str_obj_ptr, + "str_obj"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + LLVMSetAlignment(str_obj, 4); + + return str_obj; + +fail: + return NULL; +} + +static LLVMValueRef +get_stringview_iter_pos_addr(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef stringview_iter_obj) +{ + LLVMValueRef iter_pos_ptr, host_ptr_offset; + + /* header + str_obj */ + host_ptr_offset = I32_CONST(comp_ctx->pointer_size * 2); + + if (!(stringview_iter_obj = + LLVMBuildBitCast(comp_ctx->builder, stringview_iter_obj, + INT8_PTR_TYPE, "stringview_iter_obj_i8p"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + if (!(iter_pos_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, stringview_iter_obj, + &host_ptr_offset, 1, "iter_pos_i8p"))) { + aot_set_last_error("llvm build gep failed."); + goto fail; + } + + if (!(iter_pos_ptr = LLVMBuildBitCast(comp_ctx->builder, iter_pos_ptr, + INT32_PTR_TYPE, "iter_pos_i32p"))) { + aot_set_last_error("llvm build bitcast failed."); + goto fail; + } + + return iter_pos_ptr; + +fail: + return NULL; +} + +static LLVMValueRef +aot_call_wasm_string_measure(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef stringref_obj, uint32 encoding) +{ + LLVMValueRef param_values[3], func, value, str_obj; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_measure, 2); + + /* Call function wasm_string_measure() */ + param_values[0] = str_obj; + param_values[1] = I32_CONST(encoding); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "string_measure"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + return value; +fail: + return NULL; +} + +static LLVMValueRef +aot_call_wasm_string_create_view(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef stringref_obj, uint32 encoding) +{ + LLVMValueRef param_values[3], func, value, str_obj; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_create_view, 2); + + /* Call function wasm_string_create_view() */ + param_values[0] = str_obj; + param_values[1] = I32_CONST(encoding); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "string_create_view"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + return value; +fail: + return NULL; +} + +static LLVMValueRef +aot_call_wasm_string_advance(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef stringref_obj, LLVMValueRef bytes, + LLVMValueRef pos) +{ + LLVMValueRef param_values[4], func, value, str_obj; + LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type; + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT32_PTR_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_advance, 4); + + /* Call function wasm_string_advance() */ + param_values[0] = str_obj; + param_values[1] = pos; + param_values[2] = bytes; + param_values[3] = I8_PTR_NULL; + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 4, "string_advance"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + return value; +fail: + return NULL; +} + +static LLVMValueRef +aot_call_wasm_string_slice(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef stringref_obj, LLVMValueRef start, + LLVMValueRef end, StringViewType stringview_type) +{ + LLVMValueRef param_values[4], func, value, str_obj; + LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type; + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_slice, 4); + + /* Call function wasm_string_slice() */ + param_values[0] = str_obj; + param_values[1] = start; + param_values[2] = end; + param_values[3] = I32_CONST(stringview_type); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 4, "string_slice"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + return value; +fail: + return NULL; +} + +bool +aot_compile_op_string_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 encoding) +{ + LLVMValueRef maddr, byte_length, offset, str_obj, stringref_obj; + LLVMValueRef param_values[5], func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_I32(byte_length); + POP_I32(offset); + + if (!(maddr = check_bulk_memory_overflow(comp_ctx, func_ctx, offset, + byte_length))) + goto fail; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_new_with_encoding, 3); + + /* Call function wasm_struct_obj_new() */ + param_values[0] = maddr; + param_values[1] = byte_length; + param_values[2] = I32_CONST(encoding); + + if (!(str_obj = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 3, "wasm_string_new"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + CHECK_STRING_OBJ(str_obj); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, str_obj, + WASM_TYPE_STRINGREF, 0, + &stringref_obj)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj); + + PUSH_GC_REF(stringref_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 contents) +{ + LLVMValueRef param_values[2], func, value, str_obj, stringref_obj; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_new_const, 2); + + bh_assert(contents < comp_ctx->comp_data->string_literal_count); + param_values[0] = LLVMConstIntToPtr( + I64_CONST((unsigned long long)(uintptr_t) + comp_ctx->comp_data->string_literal_ptrs_wp[contents]), + INT8_PTR_TYPE); + param_values[1] = + I32_CONST(comp_ctx->comp_data->string_literal_lengths_wp[contents]); + + if (!(str_obj = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "create_stringref"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + CHECK_STRING_OBJ(str_obj); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, str_obj, + WASM_TYPE_STRINGREF, 0, + &stringref_obj)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj); + + PUSH_GC_REF(stringref_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_measure(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 encoding) +{ + LLVMValueRef stringref_obj, value; + + POP_GC_REF(stringref_obj); + + if (!(value = aot_call_wasm_string_measure(comp_ctx, func_ctx, + stringref_obj, encoding))) { + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_encode(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 mem_idx, uint32 encoding) +{ + LLVMValueRef param_values[6], func, value, offset, length, maddr, str_obj, + stringref_obj; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef check_string_encode_succ; + LLVMValueRef cmp; + + POP_I32(offset); + POP_GC_REF(stringref_obj); + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + if (!(length = aot_call_wasm_string_measure(comp_ctx, func_ctx, + stringref_obj, encoding))) { + goto fail; + } + + if (!(maddr = + check_bulk_memory_overflow(comp_ctx, func_ctx, offset, length))) + goto fail; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = INT8_PTR_TYPE; + param_types[5] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_encode, 6); + + /* Call function wasm_string_measure() */ + param_values[0] = str_obj; + param_values[1] = I32_ZERO; + param_values[2] = length; + param_values[3] = maddr; + param_values[4] = I8_PTR_NULL; + param_values[5] = I32_CONST(encoding); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 6, "string_encode"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + CHECK_STRING_ENCODE(value); + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_concat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef param_values[2], func, value, str_obj_lhs, str_obj_rhs, + stringref_obj_lhs, stringref_obj_rhs, stringref_obj_new; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_GC_REF(stringref_obj_rhs); + POP_GC_REF(stringref_obj_lhs); + + if (!(str_obj_lhs = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringref_obj_lhs))) { + goto fail; + } + + if (!(str_obj_rhs = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringref_obj_rhs))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_concat, 2); + + /* Call function wasm_string_concat() */ + param_values[0] = str_obj_lhs; + param_values[1] = str_obj_rhs; + + if (!(str_obj_lhs = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "string_concat"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + CHECK_STRING_OBJ(str_obj_lhs); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, str_obj_lhs, + WASM_TYPE_STRINGREF, 0, + &stringref_obj_new)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj_new); + + PUSH_GC_REF(stringref_obj_new); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_eq(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef param_values[2], func, value, str_obj_lhs, str_obj_rhs, + stringref_obj_lhs, stringref_obj_rhs; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + POP_GC_REF(stringref_obj_lhs); + POP_GC_REF(stringref_obj_rhs); + + if (!(str_obj_lhs = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringref_obj_lhs))) { + goto fail; + } + + if (!(str_obj_rhs = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringref_obj_rhs))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_eq, 2); + + /* Call function wasm_string_eq() */ + param_values[0] = str_obj_lhs; + param_values[1] = str_obj_rhs; + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "string_eq"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_is_usv_sequence(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef param_values[1], func, value, str_obj, stringref_obj; + LLVMTypeRef param_types[1], ret_type, func_type, func_ptr_type; + + POP_GC_REF(stringref_obj); + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_is_usv_sequence, 1); + + /* Call function wasm_string_is_usv_sequence() */ + param_values[0] = str_obj; + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 1, "string_is_usv_sequence"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_as_wtf8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef str_obj, stringref_obj, stringview_wtf8_obj; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_GC_REF(stringref_obj); + + if (!(str_obj = aot_call_wasm_string_create_view( + comp_ctx, func_ctx, stringref_obj, STRING_VIEW_WTF8))) { + goto fail; + } + CHECK_STRING_OBJ(str_obj); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, str_obj, + WASM_TYPE_STRINGVIEWWTF8, 0, + &stringview_wtf8_obj)) { + goto fail; + } + CHECK_STRINGVIEW_OBJ(stringref_obj); + + PUSH_GC_REF(stringview_wtf8_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf8_advance(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef stringref_obj, bytes, pos, value; + + POP_I32(bytes); + POP_I32(pos); + POP_GC_REF(stringref_obj); + + if (!(value = aot_call_wasm_string_advance(comp_ctx, func_ctx, + stringref_obj, bytes, pos))) { + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf8_encode(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 mem_idx, + uint32 encoding) +{ + LLVMValueRef param_values[6], func, value, offset, maddr, str_obj, + stringref_obj; + LLVMValueRef bytes, pos, next_pos; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef check_string_encode_succ; + LLVMValueRef cmp; + + POP_I32(bytes); + POP_I32(pos); + POP_I32(offset); + + next_pos = LLVMBuildAlloca(comp_ctx->builder, I32_TYPE, "next_pos"); + if (!next_pos) { + aot_set_last_error("failed to build alloca"); + goto fail; + } + + if (!(maddr = + check_bulk_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + goto fail; + + POP_GC_REF(stringref_obj); + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = INT8_PTR_TYPE; + param_types[5] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_encode, 6); + + /* Call function wasm_string_measure() */ + param_values[0] = str_obj; + param_values[1] = pos; + param_values[2] = bytes; + param_values[3] = maddr; + param_values[4] = next_pos; + param_values[5] = I32_CONST(encoding); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 6, "string_encode"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + CHECK_STRING_ENCODE(value); + + next_pos = + LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, next_pos, "next_pos"); + if (!next_pos) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + + LLVMSetAlignment(next_pos, 4); + + PUSH_I32(next_pos); + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf8_slice(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef stringref_obj, start, end, stringref_obj_new, value; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_I32(start); + POP_I32(end); + POP_GC_REF(stringref_obj); + + if (!(value = aot_call_wasm_string_slice(comp_ctx, func_ctx, stringref_obj, + start, end, STRING_VIEW_WTF8))) { + goto fail; + } + CHECK_STRING_OBJ(value); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, value, + WASM_TYPE_STRINGREF, 0, + &stringref_obj_new)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj_new); + + PUSH_GC_REF(stringref_obj_new); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_as_wtf16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef str_obj, stringref_obj, stringview_wtf16_obj; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_GC_REF(stringref_obj); + + if (!(str_obj = aot_call_wasm_string_create_view( + comp_ctx, func_ctx, stringref_obj, STRING_VIEW_WTF16))) { + goto fail; + } + CHECK_STRING_OBJ(str_obj); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, str_obj, + WASM_TYPE_STRINGVIEWWTF16, 0, + &stringview_wtf16_obj)) { + goto fail; + } + CHECK_STRINGVIEW_OBJ(stringview_wtf16_obj); + + PUSH_GC_REF(stringview_wtf16_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf16_length(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef param_values[2], func, value, str_obj, stringview_wtf16_obj; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + POP_GC_REF(stringview_wtf16_obj); + + if (!(str_obj = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringview_wtf16_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_wtf16_get_length, 6); + + /* Call function wasm_string_wtf16_get_length() */ + param_values[0] = str_obj; + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 1, "stringview_wtf16_length"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf16_get_codeunit(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef param_values[2], func, value, str_obj, stringview_wtf16_obj, + pos; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + POP_I32(pos); + POP_GC_REF(stringview_wtf16_obj); + + if (!(str_obj = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringview_wtf16_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_get_wtf16_codeunit, 2); + + /* Call function wasm_string_get_wtf16_codeunit() */ + param_values[0] = str_obj; + param_values[1] = pos; + + if (!(value = + LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 2, "stringview_wtf16_get_codeunit"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf16_encode(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 mem_idx) +{ + LLVMValueRef param_values[6], func, value, offset, maddr, str_obj, + stringref_obj; + LLVMValueRef len, pos; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef check_string_encode_succ; + LLVMValueRef cmp; + + POP_I32(len); + POP_I32(pos); + POP_I32(offset); + + if (!(maddr = check_bulk_memory_overflow( + comp_ctx, func_ctx, offset, + LLVMBuildMul(comp_ctx->builder, len, I32_CONST(2), "wtf16_len")))) + goto fail; + + POP_GC_REF(stringref_obj); + + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, 2)) { + goto fail; + } + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = INT8_PTR_TYPE; + param_types[5] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_encode, 6); + + /* Call function wasm_string_measure() */ + param_values[0] = str_obj; + param_values[1] = pos; + param_values[2] = len; + param_values[3] = maddr; + param_values[4] = I8_PTR_NULL; + param_values[5] = I32_CONST(WTF16); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 6, "string_encode"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + CHECK_STRING_ENCODE(value); + + PUSH_I32(value); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_wtf16_slice(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef stringref_obj, start, end, stringref_obj_new, value; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_I32(end); + POP_I32(start); + POP_GC_REF(stringref_obj); + + if (!(value = aot_call_wasm_string_slice(comp_ctx, func_ctx, stringref_obj, + start, end, STRING_VIEW_WTF16))) { + goto fail; + } + CHECK_STRING_OBJ(value); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, value, + WASM_TYPE_STRINGREF, 0, + &stringref_obj_new)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj_new); + + PUSH_GC_REF(stringref_obj_new); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_as_iter(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef stringref_obj, stringview_iter_obj, str_obj; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_GC_REF(stringref_obj); + + if (!(str_obj = aot_call_wasm_string_create_view( + comp_ctx, func_ctx, stringref_obj, STRING_VIEW_WTF8))) { + goto fail; + } + CHECK_STRING_OBJ(str_obj); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, stringref_obj, + WASM_TYPE_STRINGVIEWITER, 0, + &stringview_iter_obj)) { + goto fail; + } + CHECK_STRINGVIEW_OBJ(stringview_iter_obj); + + PUSH_GC_REF(stringview_iter_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_stringview_iter_next(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef param_values[2], func, value, stringview_iter_obj, str_obj, + iter_pos_addr, pos; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + POP_GC_REF(stringview_iter_obj); + + if (!(str_obj = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringview_iter_obj))) { + goto fail; + } + + if (!(iter_pos_addr = get_stringview_iter_pos_addr(comp_ctx, func_ctx, + stringview_iter_obj))) { + goto fail; + } + + pos = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, iter_pos_addr, + "get_iter_pos"); + LLVMSetAlignment(pos, 4); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_next_codepoint, 2); + + /* Call function wasm_string_measure() */ + param_values[0] = str_obj; + param_values[1] = pos; + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, "stringview_iter_next"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + PUSH_I32(value); + + return true; +fail: + return false; +} + +static bool +stringview_iter_advance_or_rewind(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_rewind) +{ + LLVMValueRef param_values[4], func, value, stringview_iter_obj, str_obj, + code_points_consumed, iter_pos_addr, pos, code_points_count, res; + LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type; + + POP_I32(code_points_count); + POP_GC_REF(stringview_iter_obj); + + if (!(str_obj = aot_stringref_obj_get_value(comp_ctx, func_ctx, + stringview_iter_obj))) { + goto fail; + } + + if (!(iter_pos_addr = get_stringview_iter_pos_addr(comp_ctx, func_ctx, + stringview_iter_obj))) { + goto fail; + } + + if (!(pos = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, iter_pos_addr, + "get_iter_pos"))) { + goto fail; + } + LLVMSetAlignment(pos, 4); + + if (!(code_points_consumed = LLVMBuildAlloca(comp_ctx->builder, I32_TYPE, + "code_points_consumed"))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT32_PTR_TYPE; + ret_type = I32_TYPE; + + if (is_rewind) { + GET_AOT_FUNCTION(wasm_string_rewind, 4); + } + else { + GET_AOT_FUNCTION(wasm_string_advance, 4); + } + + /* Call function wasm_string_advance() */ + param_values[0] = str_obj; + param_values[1] = pos; + param_values[2] = code_points_count; + param_values[3] = code_points_consumed; + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 4, "string_advance"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + if (!(code_points_consumed = + LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, code_points_consumed, + "get_code_points_consumed"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + LLVMSetAlignment(code_points_consumed, 4); + + if (!(res = LLVMBuildStore(comp_ctx->builder, code_points_consumed, + iter_pos_addr))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + LLVMSetAlignment(res, 4); + + PUSH_I32(code_points_consumed); +fail: + return false; +} + +bool +aot_compile_op_stringview_iter_advance(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return stringview_iter_advance_or_rewind(comp_ctx, func_ctx, false); +} + +bool +aot_compile_op_stringview_iter_rewind(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return stringview_iter_advance_or_rewind(comp_ctx, func_ctx, true); +} + +bool +aot_compile_op_stringview_iter_slice(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef stringview_iter_obj, start, end, stringref_obj_new, value, + iter_pos_addr, code_points_count; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_I32(code_points_count); + POP_GC_REF(stringview_iter_obj); + + if (!(iter_pos_addr = get_stringview_iter_pos_addr(comp_ctx, func_ctx, + stringview_iter_obj))) { + goto fail; + } + + if (!(start = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, iter_pos_addr, + "get_iter_pos"))) { + goto fail; + } + LLVMSetAlignment(start, 4); + + if (!(end = LLVMBuildAdd(comp_ctx->builder, start, code_points_count, + "calc_slice_end"))) { + goto fail; + } + + if (!(value = aot_call_wasm_string_slice(comp_ctx, func_ctx, + stringview_iter_obj, start, end, + STRING_VIEW_ITER))) { + goto fail; + } + CHECK_STRING_OBJ(value); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, value, + WASM_TYPE_STRINGREF, 0, + &stringref_obj_new)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj_new); + + PUSH_GC_REF(stringref_obj_new); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_new_array(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 encoding) +{ + LLVMValueRef start, end, count, str_obj, stringref_obj, array_obj, + elem_data_ptr; + LLVMValueRef param_values[5], func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + DEFINE_STRINGREF_CHECK_VAR(); + + if (!aot_gen_commit_values(comp_ctx->aot_frame)) + return false; + + if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true)) + return false; + + POP_I32(end); + POP_I32(start); + POP_GC_REF(array_obj); + + if (!aot_array_obj_elem_addr( + comp_ctx, func_ctx, array_obj, start, &elem_data_ptr, + encoding == WTF16 ? PACKED_TYPE_I16 : PACKED_TYPE_I8)) { + goto fail; + } + + if (!(count = LLVMBuildSub(comp_ctx->builder, end, start, "calc_count"))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + ret_type = INT8_PTR_TYPE; + + GET_AOT_FUNCTION(wasm_string_new_with_encoding, 3); + + /* Call function wasm_struct_obj_new() */ + param_values[0] = elem_data_ptr; + param_values[1] = count; + param_values[2] = I32_CONST(encoding); + + if (!(str_obj = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 3, "wasm_string_new"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + CHECK_STRING_OBJ(str_obj); + + if (!aot_call_wasm_stringref_obj_new(comp_ctx, func_ctx, str_obj, + WASM_TYPE_STRINGREF, 0, + &stringref_obj)) { + goto fail; + } + CHECK_STRINGREF_OBJ(stringref_obj); + + PUSH_GC_REF(stringref_obj); + + return true; +fail: + return false; +} + +bool +aot_compile_op_string_encode_array(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 encoding) +{ + LLVMValueRef param_values[6], func, value, count, start, str_obj, + stringref_obj, array_obj, elem_data_ptr, array_len; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef check_string_encode_succ, check_array_index_succ; + LLVMValueRef cmp; + + POP_I32(start); + POP_GC_REF(array_obj); + POP_GC_REF(stringref_obj); + + if (!(str_obj = + aot_stringref_obj_get_value(comp_ctx, func_ctx, stringref_obj))) { + goto fail; + } + + if (!aot_array_obj_length(comp_ctx, array_obj, &array_len)) + goto fail; + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, start, array_len, + "check_array_index"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + + ADD_BASIC_BLOCK(check_array_index_succ, "check array index succ"); + MOVE_BLOCK_AFTER_CURR(check_array_index_succ); + + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp, + check_array_index_succ)) { + goto fail; + } + + if (!aot_array_obj_elem_addr( + comp_ctx, func_ctx, stringref_obj, start, &elem_data_ptr, + encoding == WTF16 ? PACKED_TYPE_I16 : PACKED_TYPE_I8)) { + goto fail; + } + + if (!(count = aot_call_wasm_string_measure(comp_ctx, func_ctx, + stringref_obj, encoding))) { + goto fail; + } + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = INT8_PTR_TYPE; + param_types[5] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_string_encode, 6); + + /* Call function wasm_string_measure() */ + param_values[0] = str_obj; + param_values[1] = start; + param_values[2] = count; + param_values[3] = elem_data_ptr; + param_values[4] = I8_PTR_NULL; + param_values[5] = I32_CONST(encoding); + + if (!(value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 6, "string_encode"))) { + aot_set_last_error("llvm build call failed."); + goto fail; + } + + CHECK_STRING_ENCODE(value); + + PUSH_I32(value); + + return true; +fail: + return false; +} + +#endif /* WASM_ENABLE_STRINGREF != 0 */ diff --git a/core/iwasm/compilation/aot_emit_stringref.h b/core/iwasm/compilation/aot_emit_stringref.h new file mode 100644 index 0000000000..a1b68a44e0 --- /dev/null +++ b/core/iwasm/compilation/aot_emit_stringref.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_STRINGREF_H_ +#define _AOT_EMIT_STRINGREF_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_string_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 encoding); + +bool +aot_compile_op_string_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 contents); + +bool +aot_compile_op_string_measure(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 encoding); + +bool +aot_compile_op_string_encode(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 mem_idx, uint32 encoding); + +bool +aot_compile_op_string_concat(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_string_eq(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_string_is_usv_sequence(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_string_as_wtf8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_wtf8_advance(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_wtf8_encode(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 mem_idx, + uint32 encoding); + +bool +aot_compile_op_stringview_wtf8_slice(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_string_as_wtf16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_wtf16_length(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_wtf16_get_codeunit(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_wtf16_encode(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 mem_idx); + +bool +aot_compile_op_stringview_wtf16_slice(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_string_as_iter(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_iter_next(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_iter_advance(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_iter_rewind(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_stringview_iter_slice(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_string_new_array(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 encoding); + +bool +aot_compile_op_string_encode_array(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 encoding); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_STRINGREF_H_ */ diff --git a/core/iwasm/compilation/aot_emit_table.c b/core/iwasm/compilation/aot_emit_table.c new file mode 100644 index 0000000000..e8dae410bd --- /dev/null +++ b/core/iwasm/compilation/aot_emit_table.c @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_table.h" +#include "aot_emit_exception.h" +#include "../aot/aot_runtime.h" +#if WASM_ENABLE_GC != 0 +#include "aot_emit_gc.h" +#endif + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +#if WASM_ENABLE_MEMORY64 != 0 +static bool +zero_extend_u64(AOTCompContext *comp_ctx, LLVMValueRef *value, const char *name) +{ + if (comp_ctx->pointer_size == sizeof(uint64)) { + /* zero extend to uint64 if the target is 64-bit */ + *value = LLVMBuildZExt(comp_ctx->builder, *value, I64_TYPE, name); + if (!*value) { + aot_set_last_error("llvm build zero extend failed."); + return false; + } + } + return true; +} +#endif + +/* check whether a table64 elem idx is greater than UINT32_MAX, if so, throw + * exception, otherwise trunc it to uint32 */ +static bool +check_tbl_elem_idx_and_trunc(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef *elem_idx, uint32 tbl_idx) +{ +#if WASM_ENABLE_MEMORY64 != 0 + LLVMValueRef u32_max, u32_cmp_result; + LLVMBasicBlockRef check_elem_idx_succ; + + if (!IS_TABLE64(tbl_idx)) { + return true; + } + + /* Check if elem index >= UINT32_MAX */ + if (!(u32_max = I64_CONST(UINT32_MAX))) { + aot_set_last_error("llvm build const failed"); + goto fail; + } + if (!(u32_cmp_result = + LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, *elem_idx, u32_max, + "cmp_elem_idx_u32_max"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + if (!(*elem_idx = LLVMBuildTrunc(comp_ctx->builder, *elem_idx, I32_TYPE, + "elem_idx_i32"))) { + aot_set_last_error("llvm build trunc failed."); + goto fail; + } + + /* Throw exception if elem index >= UINT32_MAX*/ + if (!(check_elem_idx_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_elem_idx_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_elem_idx_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, true, + u32_cmp_result, check_elem_idx_succ))) + goto fail; + + return true; +fail: + return false; +#else + return true; +#endif +} +#endif /* WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC !=0 */ + +uint64 +get_tbl_inst_offset(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, uint32 tbl_idx) +{ + uint64 offset = 0, i = 0; + AOTImportTable *imp_tbls = comp_ctx->comp_data->import_tables; + AOTTable *tbls = comp_ctx->comp_data->tables; + + offset = + offsetof(AOTModuleInstance, global_table_data.bytes) + + (uint64)comp_ctx->comp_data->memory_count * sizeof(AOTMemoryInstance) + /* Get global data size according to target info */ + + (comp_ctx->pointer_size == sizeof(uint64) + ? comp_ctx->comp_data->global_data_size_64bit + : comp_ctx->comp_data->global_data_size_32bit); + + while (i < tbl_idx && i < comp_ctx->comp_data->import_table_count) { + offset += offsetof(AOTTableInstance, elems); + /* avoid loading from current AOTTableInstance */ + offset += + (uint64)comp_ctx->pointer_size + * aot_get_imp_tbl_data_slots(imp_tbls + i, comp_ctx->is_jit_mode); + ++i; + } + + if (i == tbl_idx) { + return offset; + } + + tbl_idx -= comp_ctx->comp_data->import_table_count; + i -= comp_ctx->comp_data->import_table_count; + while (i < tbl_idx && i < comp_ctx->comp_data->table_count) { + offset += offsetof(AOTTableInstance, elems); + /* avoid loading from current AOTTableInstance */ + offset += (uint64)comp_ctx->pointer_size + * aot_get_tbl_data_slots(tbls + i, comp_ctx->is_jit_mode); + ++i; + } + + return offset; +} + +uint32 +get_module_inst_extra_offset(AOTCompContext *comp_ctx) +{ + const AOTCompData *comp_data = comp_ctx->comp_data; + uint32 table_count = comp_data->import_table_count + comp_data->table_count; + uint64 offset = get_tbl_inst_offset(comp_ctx, NULL, table_count); + uint32 offset_32 = (uint32)offset; + bh_assert(offset <= UINT32_MAX); + offset_32 = align_uint(offset_32, 8); + return offset_32; +} + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + +LLVMValueRef +aot_compile_get_tbl_inst(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef offset, tbl_inst; + + if (!(offset = + I64_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(tbl_inst = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, 1, + "tbl_inst"))) { + HANDLE_FAILURE("LLVMBuildInBoundsGEP"); + goto fail; + } + + return tbl_inst; +fail: + return NULL; +} + +bool +aot_compile_op_elem_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_seg_idx) +{ + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + LLVMValueRef param_values[2], ret_value, func, value; + + /* void aot_drop_table_seg(AOTModuleInstance *, uint32 ) */ + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = VOID_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_drop_table_seg, 2); + else + GET_AOT_FUNCTION(aot_drop_table_seg, 2); + + param_values[0] = func_ctx->aot_inst; + if (!(param_values[1] = I32_CONST(tbl_seg_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* "" means return void */ + if (!(ret_value = LLVMBuildCall2(comp_ctx->builder, func_type, func, + param_values, 2, ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +static bool +aot_check_table_access(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx, LLVMValueRef elem_idx) +{ + LLVMValueRef offset, tbl_sz, cmp_elem_idx; + LLVMBasicBlockRef check_elem_idx_succ; + + /* get the cur size of the table instance */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, cur_size)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, 1, + "cur_size_i8p"))) { + HANDLE_FAILURE("LLVMBuildInBoundsGEP"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildBitCast(comp_ctx->builder, tbl_sz, INT32_PTR_TYPE, + "cur_size_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, tbl_sz, + "cur_size"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, &elem_idx, tbl_idx)) { + goto fail; + } + + /* Check if (uint32)elem index >= table size */ + if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx, + tbl_sz, "cmp_elem_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + + /* Throw exception if elem index >= table size */ + if (!(check_elem_idx_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_elem_idx_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_elem_idx_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, true, + cmp_elem_idx, check_elem_idx_succ))) + goto fail; + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef elem_idx, offset, func_idx; + LLVMValueRef table_elem_base, table_elem_addr, table_elem; + + POP_TBL_ELEM_IDX(elem_idx); + + if (!aot_check_table_access(comp_ctx, func_ctx, tbl_idx, elem_idx)) { + goto fail; + } + + /* load data as i32* */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, elems)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_elem_base = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, + 1, "table_elem_base_i8p"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + + /* Load function object reference or function index */ + if (comp_ctx->enable_gc) { + if (!(table_elem_base = + LLVMBuildBitCast(comp_ctx->builder, table_elem_base, + GC_REF_PTR_TYPE, "table_elem_base"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_elem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, GC_REF_TYPE, table_elem_base, &elem_idx, 1, + "table_elem_addr"))) { + HANDLE_FAILURE("LLVMBuildNUWAdd"); + goto fail; + } + + if (!(table_elem = LLVMBuildLoad2(comp_ctx->builder, GC_REF_TYPE, + table_elem_addr, "table_elem"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + + PUSH_GC_REF(table_elem); + } + else { + if (!(table_elem_base = + LLVMBuildBitCast(comp_ctx->builder, table_elem_base, + INTPTR_T_PTR_TYPE, "table_elem_base"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_elem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INTPTR_T_TYPE, table_elem_base, &elem_idx, + 1, "table_elem_addr"))) { + HANDLE_FAILURE("LLVMBuildNUWAdd"); + goto fail; + } + + if (!(table_elem = LLVMBuildLoad2(comp_ctx->builder, INTPTR_T_TYPE, + table_elem_addr, "table_elem"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + + if (!(func_idx = LLVMBuildIntCast2(comp_ctx->builder, table_elem, + I32_TYPE, true, "func_idx"))) { + HANDLE_FAILURE("LLVMBuildIntCast"); + goto fail; + } + + PUSH_I32(func_idx); + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef val = NULL, elem_idx, offset, table_elem_base, table_elem_addr; + + if (comp_ctx->enable_gc) + POP_GC_REF(val); + else { + POP_I32(val); + + if (!(val = LLVMBuildIntCast2(comp_ctx->builder, val, INTPTR_T_TYPE, + true, "val_intptr_t"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + } + + POP_TBL_ELEM_IDX(elem_idx); + + if (!aot_check_table_access(comp_ctx, func_ctx, tbl_idx, elem_idx)) { + goto fail; + } + + /* load data as gc_obj_ref* or i32* */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, elems)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_elem_base = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, + 1, "table_elem_base_i8p"))) { + HANDLE_FAILURE("LLVMBuildInBoundsGEP"); + goto fail; + } + + if (comp_ctx->enable_gc) { + if (!(table_elem_base = + LLVMBuildBitCast(comp_ctx->builder, table_elem_base, + GC_REF_PTR_TYPE, "table_elem_base"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_elem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, GC_REF_TYPE, table_elem_base, &elem_idx, 1, + "table_elem_addr"))) { + HANDLE_FAILURE("LLVMBuildInBoundsGEP"); + goto fail; + } + } + else { + if (!(table_elem_base = + LLVMBuildBitCast(comp_ctx->builder, table_elem_base, + INTPTR_T_PTR_TYPE, "table_elem_base"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_elem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INTPTR_T_TYPE, table_elem_base, &elem_idx, + 1, "table_elem_addr"))) { + HANDLE_FAILURE("LLVMBuildInBoundsGEP"); + goto fail; + } + } + + if (!(LLVMBuildStore(comp_ctx->builder, val, table_elem_addr))) { + HANDLE_FAILURE("LLVMBuildStore"); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx, uint32 tbl_seg_idx) + +{ + LLVMValueRef func, param_values[6], value; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + param_types[5] = I32_TYPE; + ret_type = VOID_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_table_init, 6); + else + GET_AOT_FUNCTION(aot_table_init, 6); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(param_values[2] = I32_CONST(tbl_seg_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_I32(param_values[3]); + /* s */ + POP_I32(param_values[4]); + /* d */ + POP_TBL_ELEM_IDX(param_values[5]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[5], + tbl_idx)) { + goto fail; + } + + /* "" means return void */ + if (!(LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 6, + ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 src_tbl_idx, uint32 dst_tbl_idx) +{ + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMValueRef func, param_values[6], value; + uint32 tbl_idx; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + param_types[5] = I32_TYPE; + ret_type = VOID_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_table_copy, 6); + else + GET_AOT_FUNCTION(aot_table_copy, 6); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(src_tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(param_values[2] = I32_CONST(dst_tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* In table64, the length should be i32 type if any one of src/dst table + * is i32 type, set the table index to the lesser-or-equal table when + * popping length n */ + if (!(comp_ctx->comp_data->tables[src_tbl_idx].table_type.flags + & TABLE64_FLAG)) + tbl_idx = src_tbl_idx; + else + tbl_idx = dst_tbl_idx; + /* n */ + POP_TBL_ELEM_LEN(param_values[3]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[3], + tbl_idx)) { + goto fail; + } + /* s */ + tbl_idx = src_tbl_idx; + POP_TBL_ELEM_IDX(param_values[4]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[4], + tbl_idx)) { + goto fail; + } + /* d */ + tbl_idx = dst_tbl_idx; + POP_TBL_ELEM_IDX(param_values[5]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[5], + tbl_idx)) { + goto fail; + } + + /* "" means return void */ + if (!(LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 6, + ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef offset, tbl_sz; + + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, cur_size)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, 1, + "tbl_sz_ptr_i8"))) { + HANDLE_FAILURE("LLVMBuildInBoundsGEP"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildBitCast(comp_ctx->builder, tbl_sz, INT32_PTR_TYPE, + "tbl_sz_ptr"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(tbl_sz = + LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, tbl_sz, "tbl_sz"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (IS_TABLE64(tbl_idx)) { + if (!zero_extend_u64(comp_ctx, &tbl_sz, "length64")) { + goto fail; + } + } +#endif + PUSH_TBL_ELEM_IDX(tbl_sz); + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type; + LLVMValueRef func, param_values[4], ret, value; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + ret_type = I32_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_table_grow, 4); + else + GET_AOT_FUNCTION(aot_table_grow, 4); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_TBL_ELEM_LEN(param_values[2]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[2], + tbl_idx)) { + goto fail; + } + /* v */ + + if (comp_ctx->enable_gc) { + POP_GC_REF(param_values[3]); + if (!(param_values[3] = + LLVMBuildBitCast(comp_ctx->builder, param_values[3], + INT8_PTR_TYPE, "table_elem_i8p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + } + else { + POP_I32(param_values[3]); + if (!(param_values[3] = + LLVMBuildIntToPtr(comp_ctx->builder, param_values[3], + INT8_PTR_TYPE, "table_elem_i8p"))) { + HANDLE_FAILURE("LLVMBuildIntToPtr"); + goto fail; + } + } + + if (!(ret = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + 4, "table_grow"))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (IS_TABLE64(tbl_idx)) { + if (!zero_extend_u64(comp_ctx, &ret, "table_size64")) { + goto fail; + } + } +#endif + PUSH_TBL_ELEM_LEN(ret); + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + LLVMValueRef func, param_values[5], value; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = INT8_PTR_TYPE; + param_types[4] = I32_TYPE; + ret_type = VOID_TYPE; + + if (comp_ctx->is_jit_mode) + GET_AOT_FUNCTION(llvm_jit_table_fill, 5); + else + GET_AOT_FUNCTION(aot_table_fill, 5); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_TBL_ELEM_LEN(param_values[2]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[2], + tbl_idx)) { + goto fail; + } + /* v */ + + if (comp_ctx->enable_gc) { + POP_GC_REF(param_values[3]); + if (!(param_values[3] = + LLVMBuildBitCast(comp_ctx->builder, param_values[3], + INT8_PTR_TYPE, "table_elem_i8p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + } + else { + POP_I32(param_values[3]); + if (!(param_values[3] = + LLVMBuildIntToPtr(comp_ctx->builder, param_values[3], + INT8_PTR_TYPE, "table_elem_i8p"))) { + HANDLE_FAILURE("LLVMBuildIntToPtr"); + goto fail; + } + } + /* i */ + POP_TBL_ELEM_IDX(param_values[4]); + if (!check_tbl_elem_idx_and_trunc(comp_ctx, func_ctx, ¶m_values[4], + tbl_idx)) { + goto fail; + } + + /* "" means return void */ + if (!(LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 5, + ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +#endif /* WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC !=0 */ diff --git a/core/iwasm/compilation/aot_emit_table.h b/core/iwasm/compilation/aot_emit_table.h new file mode 100644 index 0000000000..f294cca9ae --- /dev/null +++ b/core/iwasm/compilation/aot_emit_table.h @@ -0,0 +1,62 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_TABLE_H_ +#define _AOT_EMIT_TABLE_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_elem_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_seg_idx); + +bool +aot_compile_op_table_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx, uint32 tbl_seg_idx); + +bool +aot_compile_op_table_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 src_tbl_idx, uint32 dst_tbl_idx); + +bool +aot_compile_op_table_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx); + +uint64 +get_tbl_inst_offset(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, uint32 tbl_idx); + +uint32 +get_module_inst_extra_offset(AOTCompContext *comp_ctx); + +LLVMValueRef +aot_compile_get_tbl_inst(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 tbl_idx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif +#endif diff --git a/core/iwasm/compilation/aot_emit_variable.c b/core/iwasm/compilation/aot_emit_variable.c index a5565841a1..fed8c5550e 100644 --- a/core/iwasm/compilation/aot_emit_variable.c +++ b/core/iwasm/compilation/aot_emit_variable.c @@ -4,24 +4,34 @@ */ #include "aot_emit_variable.h" +#include "aot_emit_exception.h" #include "../aot/aot_runtime.h" -#define CHECK_LOCAL(idx) do { \ - if (idx >= func_ctx->aot_func->func_type->param_count \ - + func_ctx->aot_func->local_count) { \ - aot_set_last_error("local index out of range"); \ - return false; \ - } \ - } while (0) +#define CHECK_LOCAL(idx) \ + do { \ + if (idx >= func_ctx->aot_func->func_type->param_count \ + + func_ctx->aot_func->local_count) { \ + aot_set_last_error("local index out of range"); \ + return false; \ + } \ + } while (0) static uint8 -get_local_type(AOTFuncContext *func_ctx, uint32 local_idx) +get_local_type(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx) { AOTFunc *aot_func = func_ctx->aot_func; uint32 param_count = aot_func->func_type->param_count; - return local_idx < param_count - ? aot_func->func_type->types[local_idx] - : aot_func->local_types[local_idx - param_count]; + uint8 local_type; + + local_type = local_idx < param_count + ? aot_func->func_type->types[local_idx] + : aot_func->local_types_wp[local_idx - param_count]; + + if (comp_ctx->enable_gc && aot_is_type_gc_reftype(local_type)) + local_type = VALUE_TYPE_GC_REF; + + return local_type; } bool @@ -30,41 +40,94 @@ aot_compile_op_get_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, { char name[32]; LLVMValueRef value; + AOTValue *aot_value_top; + uint8 local_type; CHECK_LOCAL(local_idx); + local_type = get_local_type(comp_ctx, func_ctx, local_idx); + snprintf(name, sizeof(name), "%s%d%s", "local", local_idx, "#"); - if (!(value = LLVMBuildLoad(comp_ctx->builder, - func_ctx->locals[local_idx], - name))) { + if (!(value = LLVMBuildLoad2(comp_ctx->builder, TO_LLVM_TYPE(local_type), + func_ctx->locals[local_idx], name))) { aot_set_last_error("llvm build load fail"); return false; } - PUSH(value, get_local_type(func_ctx, local_idx)); + PUSH(value, local_type); + + aot_value_top = + func_ctx->block_stack.block_list_end->value_stack.value_list_end; + aot_value_top->is_local = true; + aot_value_top->local_idx = local_idx; return true; fail: return false; } -bool -aot_compile_op_set_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 local_idx) +static bool +aot_compile_op_set_or_tee_local(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint32 local_idx, + bool is_tee_local) { LLVMValueRef value; + uint8 local_type; + uint32 n; CHECK_LOCAL(local_idx); - POP(value, get_local_type(func_ctx, local_idx)); + local_type = get_local_type(comp_ctx, func_ctx, local_idx); + + POP(value, local_type); + + if (comp_ctx->aot_frame) { + /* Get the slot index */ + n = func_ctx->aot_func->local_offsets[local_idx]; + bh_assert(comp_ctx->aot_frame->lp[n].type == local_type); + + switch (local_type) { + case VALUE_TYPE_I32: + set_local_i32(comp_ctx->aot_frame, n, value); + break; + case VALUE_TYPE_I64: + set_local_i64(comp_ctx->aot_frame, n, value); + break; + case VALUE_TYPE_F32: + set_local_f32(comp_ctx->aot_frame, n, value); + break; + case VALUE_TYPE_F64: + set_local_f64(comp_ctx->aot_frame, n, value); + break; + case VALUE_TYPE_V128: + set_local_v128(comp_ctx->aot_frame, n, value); + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + set_local_ref(comp_ctx->aot_frame, n, value, local_type); + break; +#if WASM_ENABLE_GC != 0 + case VALUE_TYPE_GC_REF: + set_local_gc_ref(comp_ctx->aot_frame, n, value, local_type); + break; +#endif + default: + bh_assert(0); + break; + } + } - if (!LLVMBuildStore(comp_ctx->builder, - value, + if (!LLVMBuildStore(comp_ctx->builder, value, func_ctx->locals[local_idx])) { aot_set_last_error("llvm build store fail"); return false; } + if (is_tee_local) { + PUSH(value, local_type); + } + + aot_checked_addr_list_del(func_ctx, local_idx); return true; fail: @@ -72,104 +135,181 @@ aot_compile_op_set_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } bool -aot_compile_op_tee_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, +aot_compile_op_set_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 local_idx) { - LLVMValueRef value; - uint8 type; - - CHECK_LOCAL(local_idx); - - type = get_local_type(func_ctx, local_idx); - - POP(value, type); - - if (!LLVMBuildStore(comp_ctx->builder, - value, - func_ctx->locals[local_idx])) { - aot_set_last_error("llvm build store fail"); - return false; - } - - PUSH(value, type); - return true; + return aot_compile_op_set_or_tee_local(comp_ctx, func_ctx, local_idx, + false); +} -fail: - return false; +bool +aot_compile_op_tee_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx) +{ + return aot_compile_op_set_or_tee_local(comp_ctx, func_ctx, local_idx, true); } static bool compile_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 global_idx, bool is_set) + uint32 global_idx, bool is_set, bool is_aux_stack) { - AOTCompData *comp_data = comp_ctx->comp_data; + const AOTCompData *comp_data = comp_ctx->comp_data; uint32 import_global_count = comp_data->import_global_count; - uint32 global_base_offset = offsetof(AOTModuleInstance, - global_table_heap_data.bytes); + uint32 global_base_offset; uint32 global_offset; uint8 global_type; - LLVMValueRef offset, global_ptr, global; + LLVMValueRef offset, global_ptr, global, res; LLVMTypeRef ptr_type = NULL; + global_base_offset = + offsetof(AOTModuleInstance, global_table_data.bytes) + + sizeof(AOTMemoryInstance) * comp_ctx->comp_data->memory_count; + bh_assert(global_idx < import_global_count + comp_data->global_count); if (global_idx < import_global_count) { - global_offset = global_base_offset - + comp_data->import_globals[global_idx].data_offset; - global_type = comp_data->import_globals[global_idx].type; + global_offset = + global_base_offset + /* Get global data offset according to target info */ + + (comp_ctx->pointer_size == sizeof(uint64) + ? comp_data->import_globals[global_idx].data_offset_64bit + : comp_data->import_globals[global_idx].data_offset_32bit); + global_type = comp_data->import_globals[global_idx].type.val_type; } else { - global_offset = global_base_offset - + comp_data->globals[global_idx - import_global_count].data_offset; + global_offset = + global_base_offset + /* Get global data offset according to target info */ + + (comp_ctx->pointer_size == sizeof(uint64) + ? comp_data->globals[global_idx - import_global_count] + .data_offset_64bit + : comp_data->globals[global_idx - import_global_count] + .data_offset_32bit); global_type = - comp_data->globals[global_idx - import_global_count].type; + comp_data->globals[global_idx - import_global_count].type.val_type; } + if (comp_ctx->enable_gc && aot_is_type_gc_reftype(global_type)) + global_type = VALUE_TYPE_GC_REF; + offset = I32_CONST(global_offset); - if (!(global_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, - &offset, 1, "global_ptr_tmp"))) { + if (!(global_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, 1, + "global_ptr_tmp"))) { aot_set_last_error("llvm build in bounds gep failed."); return false; } switch (global_type) { case VALUE_TYPE_I32: - ptr_type = comp_ctx->basic_types.int32_ptr_type; + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: + ptr_type = INT32_PTR_TYPE; break; case VALUE_TYPE_I64: - ptr_type = comp_ctx->basic_types.int64_ptr_type; + ptr_type = INT64_PTR_TYPE; break; case VALUE_TYPE_F32: - ptr_type = comp_ctx->basic_types.float32_ptr_type; + ptr_type = F32_PTR_TYPE; break; case VALUE_TYPE_F64: - ptr_type = comp_ctx->basic_types.float64_ptr_type; + ptr_type = F64_PTR_TYPE; + break; + case VALUE_TYPE_V128: + ptr_type = V128_PTR_TYPE; + break; +#if WASM_ENABLE_GC != 0 + case VALUE_TYPE_GC_REF: + ptr_type = GC_REF_PTR_TYPE; break; +#endif default: - bh_assert(0); + bh_assert("unknown type"); break; } - if (!(global_ptr = LLVMBuildBitCast(comp_ctx->builder, global_ptr, - ptr_type, "global_ptr"))) { + if (!(global_ptr = LLVMBuildBitCast(comp_ctx->builder, global_ptr, ptr_type, + "global_ptr"))) { aot_set_last_error("llvm build bit cast failed."); return false; } if (!is_set) { - if (!(global = LLVMBuildLoad(comp_ctx->builder, - global_ptr, "global"))) { + if (!(global = + LLVMBuildLoad2(comp_ctx->builder, TO_LLVM_TYPE(global_type), + global_ptr, "global"))) { aot_set_last_error("llvm build load failed."); return false; } + /* All globals' data is 4-byte aligned */ + LLVMSetAlignment(global, 4); PUSH(global, global_type); } else { POP(global, global_type); - if (!LLVMBuildStore(comp_ctx->builder, global, global_ptr)) { + + if (is_aux_stack && comp_ctx->enable_aux_stack_check) { + LLVMBasicBlockRef block_curr = + LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_overflow_succ, check_underflow_succ; + LLVMValueRef cmp, global_i64; + + /* Add basic blocks */ + if (!(check_overflow_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, + "check_overflow_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + LLVMMoveBasicBlockAfter(check_overflow_succ, block_curr); + + if (!(check_underflow_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, + "check_underflow_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + LLVMMoveBasicBlockAfter(check_underflow_succ, check_overflow_succ); + + if (!(global_i64 = LLVMBuildZExt(comp_ctx->builder, global, + I64_TYPE, "global_i64"))) { + aot_set_last_error("llvm build zext failed."); + return false; + } + + /* Check aux stack overflow */ + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntULE, global_i64, + func_ctx->aux_stack_bound, "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_AUX_STACK_OVERFLOW, + true, cmp, check_overflow_succ)) { + return false; + } + + /* Check aux stack underflow */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_overflow_succ); + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, global_i64, + func_ctx->aux_stack_bottom, "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_AUX_STACK_UNDERFLOW, true, cmp, + check_underflow_succ)) { + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_underflow_succ); + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, global, global_ptr))) { aot_set_last_error("llvm build store failed."); return false; } + /* All globals' data is 4-byte aligned */ + LLVMSetAlignment(res, 4); } return true; @@ -181,13 +321,12 @@ bool aot_compile_op_get_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 global_idx) { - return compile_global(comp_ctx, func_ctx, global_idx, false); + return compile_global(comp_ctx, func_ctx, global_idx, false, false); } bool aot_compile_op_set_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 global_idx) + uint32 global_idx, bool is_aux_stack) { - return compile_global(comp_ctx, func_ctx, global_idx, true); + return compile_global(comp_ctx, func_ctx, global_idx, true, is_aux_stack); } - diff --git a/core/iwasm/compilation/aot_emit_variable.h b/core/iwasm/compilation/aot_emit_variable.h index de2b35f62e..28c0bd0939 100644 --- a/core/iwasm/compilation/aot_emit_variable.h +++ b/core/iwasm/compilation/aot_emit_variable.h @@ -30,11 +30,10 @@ aot_compile_op_get_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool aot_compile_op_set_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 global_idx); + uint32 global_idx, bool is_aux_stack); #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_EMIT_VARIABLE_H_ */ - diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index e07aea1e62..1a9da63fac 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -4,50 +4,655 @@ */ #include "aot_llvm.h" -#include "bh_memory.h" +#include "aot_llvm_extra2.h" #include "aot_compiler.h" +#include "aot_emit_exception.h" +#include "aot_emit_table.h" #include "../aot/aot_runtime.h" +#include "../aot/aot_intrinsic.h" +#include "../interpreter/wasm_runtime.h" +#if WASM_ENABLE_DEBUG_AOT != 0 +#include "debug/dwarf_extractor.h" +#endif + +static bool +create_native_symbol(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +static bool +create_native_stack_bound(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); +static bool +create_native_stack_top_min(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); LLVMTypeRef -wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type) +wasm_type_to_llvm_type(const AOTCompContext *comp_ctx, + const AOTLLVMTypes *llvm_types, uint8 wasm_type) { switch (wasm_type) { case VALUE_TYPE_I32: return llvm_types->int32_type; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + if (comp_ctx->enable_ref_types) + return llvm_types->int32_type; + else { + bh_assert(comp_ctx->enable_gc); + return llvm_types->gc_ref_type; + } case VALUE_TYPE_I64: return llvm_types->int64_type; case VALUE_TYPE_F32: return llvm_types->float32_type; case VALUE_TYPE_F64: return llvm_types->float64_type; + case VALUE_TYPE_V128: + return llvm_types->i64x2_vec_type; case VALUE_TYPE_VOID: return llvm_types->void_type; + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + case VALUE_TYPE_GC_REF: + bh_assert(comp_ctx->enable_gc); + return llvm_types->gc_ref_type; + default: + break; } + bh_assert(0); return NULL; } +static LLVMValueRef +aot_add_llvm_func1(const AOTCompContext *comp_ctx, LLVMModuleRef module, + uint32 func_index, uint32 param_count, LLVMTypeRef func_type, + const char *prefix) +{ + char func_name[48] = { 0 }; + LLVMValueRef func; + LLVMValueRef local_value; + uint32 i, j; + + /* Add LLVM function */ + snprintf(func_name, sizeof(func_name), "%s%d", prefix, func_index); + if (!(func = LLVMAddFunction(module, func_name, func_type))) { + aot_set_last_error("add LLVM function failed."); + return NULL; + } + + j = 0; + local_value = LLVMGetParam(func, j++); + LLVMSetValueName(local_value, "exec_env"); + + /* Set parameter names */ + for (i = 0; i < param_count; i++) { + local_value = LLVMGetParam(func, j++); + LLVMSetValueName(local_value, ""); + } + + return func; +} + +/* + * create a basic func_ctx enough to call aot_emit_exception. + * + * that is: + * - exec_env + * - aot_inst + * - native_symbol (if is_indirect_mode) + */ +static bool +create_basic_func_context(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef aot_inst_offset = I32_TWO, aot_inst_addr; + + /* Save the parameters for fast access */ + func_ctx->exec_env = LLVMGetParam(func_ctx->func, 0); + + /* Get aot inst address, the layout of exec_env is: + exec_env->next, exec_env->prev, exec_env->module_inst, and argv_buf */ + if (!(aot_inst_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, + &aot_inst_offset, 1, "aot_inst_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + goto fail; + } + + /* Load aot inst */ + if (!(func_ctx->aot_inst = LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + aot_inst_addr, "aot_inst"))) { + aot_set_last_error("llvm build load failed"); + goto fail; + } + + if (comp_ctx->is_indirect_mode + && !create_native_symbol(comp_ctx, func_ctx)) { + goto fail; + } + + return true; +fail: + return false; +} + +/* + * return if the "precheck" wrapper function can use tail call optimization + */ +bool +aot_target_precheck_can_use_musttail(const AOTCompContext *comp_ctx) +{ + if (!strcmp(comp_ctx->target_arch, "xtensa")) { + /* + * xtensa windowed ABI doesn't have tail call optimization. + * + * Note: as of writing this, the xtensa version of LLVM + * simply ignores the musttail attribute. + * https://github.com/espressif/llvm-project/pull/73 + */ + return false; + } + if (!strcmp(comp_ctx->target_arch, "riscv32") + || !strcmp(comp_ctx->target_arch, "riscv64")) { + /* + * REVISIT: actually, riscv can use tail call optimization + * in some cases. I (yamamoto) don't know the exact conditions + * though. + */ + return false; + } + if (!strcmp(comp_ctx->target_arch, "mips")) { + /* + * cf. + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/2412 + */ + return false; + } + if (strstr(comp_ctx->target_arch, "thumb")) { + /* + * cf. + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/2412 + */ + return false; + } + /* + * x86-64/i386: true + * + * others: assume true for now + */ + return true; +} + +unsigned int +aot_estimate_stack_usage_for_function_call(const AOTCompContext *comp_ctx, + const AOTFuncType *callee_func_type) +{ + /* + * Estimate how much stack is necessary to make a function call. + * This does not include the stack consumption of the callee function. + * + * For precise estimation, ideally this function needs to be + * target-specific. + * However, this implementation aims to be target-independent, + * allowing a small overstimation, which is probably ok for our purpose. + * (overflow detection and memory profiling) + * On the other hand, an underestimation should be avoided as it + * can cause more serious problems like silent data corruptions. + * + * Assumptions: + * + * - the first result is returned via a register. + * + * - all parameters, including exec_env and pointers to non-first + * results, are passed via stack. + * (this is a bit pessimistic than many of real calling conventions, + * where some of parameters are passed via register.) + * + * - N-byte value needs N-byte alignment on stack. + * + * - a value smaller than a pointer is extended. + * (eg. 4 byte values are extended to 8 byte on x86-64.) + */ + + const unsigned int param_count = callee_func_type->param_count; + const unsigned int result_count = callee_func_type->result_count; + unsigned int size = 0; + unsigned int i; + unsigned int nb; + + if (!strcmp(comp_ctx->target_arch, "xtensa")) { + /* + * In the xtensa windowed ABI, outgoing arguments are already + * included in the callee's stack frame size, which equals to + * the operand of the ENTRY instruction and what LLVM + * MFI->getStackSize returns. + */ + return 0; + } + + /* exec_env */ + size = comp_ctx->pointer_size; + + /* parameters */ + for (i = 0; i < param_count; i++) { + nb = wasm_value_type_cell_num(callee_func_type->types[i]) * 4; + if (nb < comp_ctx->pointer_size) { + nb = comp_ctx->pointer_size; + } + size = align_uint(size, nb) + nb; + } + + /* pointers to results */ + nb = comp_ctx->pointer_size; + for (i = 1; i < result_count; i++) { + size = align_uint(size, nb) + nb; + } + + /* return address */ + nb = comp_ctx->pointer_size; + size = align_uint(size, nb) + nb; + + /* + * some extra for possible arch-dependent things like + * 16-byte alignment for x86_64. + */ + size += 16; + return size; +} + +/* + * a "precheck" function performs a few things before calling wrapped_func. + * + * - update native_stack_top_min if necessary + * - stack overflow check (if it does, trap) + */ +static bool +aot_build_precheck_function(AOTCompContext *comp_ctx, LLVMModuleRef module, + LLVMValueRef precheck_func, uint32 func_index, + LLVMTypeRef func_type, LLVMValueRef wrapped_func) +{ + LLVMBasicBlockRef begin = NULL; + LLVMBasicBlockRef check_top_block = NULL; + LLVMBasicBlockRef update_top_block = NULL; + LLVMBasicBlockRef stack_bound_check_block = NULL; + LLVMBasicBlockRef call_wrapped_func_block = NULL; + LLVMValueRef *params = NULL; + + begin = LLVMAppendBasicBlockInContext(comp_ctx->context, precheck_func, + "begin"); + check_top_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, precheck_func, "check_top_block"); + if (comp_ctx->enable_stack_estimation) { + update_top_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, precheck_func, "update_top_block"); + if (!update_top_block) { + goto fail; + } + } + stack_bound_check_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, precheck_func, "stack_bound_check_block"); + call_wrapped_func_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, precheck_func, "call_wrapped_func"); + if (!begin || !check_top_block || !stack_bound_check_block + || !call_wrapped_func_block) { + goto fail; + } + LLVMBuilderRef b = comp_ctx->builder; + LLVMPositionBuilderAtEnd(b, begin); + + /* create a temporary minimum func_ctx */ + AOTFuncContext tmp; + AOTFuncContext *func_ctx = &tmp; + memset(func_ctx, 0, sizeof(*func_ctx)); + func_ctx->func = precheck_func; + func_ctx->module = module; + func_ctx->aot_func = comp_ctx->comp_data->funcs[func_index]; +#if WASM_ENABLE_DEBUG_AOT != 0 + func_ctx->debug_func = NULL; +#endif + if (!create_basic_func_context(comp_ctx, func_ctx)) + goto fail; + if (comp_ctx->enable_stack_bound_check + && !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; + } + + uint32 param_count = LLVMCountParams(precheck_func); + uint32 sz = param_count * (uint32)sizeof(LLVMValueRef); + params = wasm_runtime_malloc(sz); + if (params == NULL) { + goto fail; + } + LLVMGetParams(precheck_func, params); + + const bool is_64bit = comp_ctx->pointer_size == sizeof(uint64); + LLVMTypeRef uintptr_type; + if (is_64bit) + uintptr_type = I64_TYPE; + else + uintptr_type = I32_TYPE; + + /* + * load the stack pointer + */ + LLVMValueRef sp_ptr = LLVMBuildAlloca(b, I32_TYPE, "sp_ptr"); + if (!sp_ptr) { + goto fail; + } + LLVMValueRef sp = LLVMBuildPtrToInt(b, sp_ptr, uintptr_type, "sp"); + if (!sp) { + goto fail; + } + + /* + * load the value for this wrapped function from the stack_sizes array + */ + LLVMValueRef stack_sizes; + if (comp_ctx->is_indirect_mode) { + uint32 offset_u32; + LLVMValueRef offset; + LLVMValueRef stack_sizes_p; + + offset_u32 = get_module_inst_extra_offset(comp_ctx); + offset_u32 += offsetof(AOTModuleInstanceExtra, stack_sizes); + offset = I32_CONST(offset_u32); + if (!offset) { + goto fail; + } + stack_sizes_p = + LLVMBuildInBoundsGEP2(b, INT8_TYPE, func_ctx->aot_inst, &offset, 1, + "aot_inst_stack_sizes_p"); + if (!stack_sizes_p) { + goto fail; + } + stack_sizes = + LLVMBuildLoad2(b, INT32_PTR_TYPE, stack_sizes_p, "stack_sizes"); + if (!stack_sizes) { + goto fail; + } + } + else { + stack_sizes = comp_ctx->stack_sizes; + } + LLVMValueRef func_index_const = I32_CONST(func_index); + LLVMValueRef sizes = + LLVMBuildBitCast(b, stack_sizes, INT32_PTR_TYPE, "sizes"); + if (!sizes) { + goto fail; + } + LLVMValueRef sizep = LLVMBuildInBoundsGEP2(b, I32_TYPE, sizes, + &func_index_const, 1, "sizep"); + if (!sizep) { + goto fail; + } + LLVMValueRef size32 = LLVMBuildLoad2(b, I32_TYPE, sizep, "size32"); + if (!size32) { + goto fail; + } + LLVMValueRef size; + if (is_64bit) { + size = LLVMBuildZExt(b, size32, uintptr_type, "size"); + if (!size) { + goto fail; + } + } + else { + size = size32; + } + /* + * calculate new sp + */ + LLVMValueRef underflow = + LLVMBuildICmp(b, LLVMIntULT, sp, size, "underflow"); + if (!underflow) { + goto fail; + } + LLVMValueRef new_sp = LLVMBuildSub(b, sp, size, "new_sp"); + if (!new_sp) { + goto fail; + } + if (!LLVMBuildBr(b, check_top_block)) { + goto fail; + } + + LLVMPositionBuilderAtEnd(b, check_top_block); + if (comp_ctx->enable_stack_estimation) { + /* + * load native_stack_top_min from the exec_env + */ + LLVMValueRef top_min = + LLVMBuildLoad2(b, OPQ_PTR_TYPE, func_ctx->native_stack_top_min_addr, + "native_stack_top_min"); + if (!top_min) { + goto fail; + } + LLVMValueRef top_min_int = LLVMBuildPtrToInt( + b, top_min, uintptr_type, "native_stack_top_min_int"); + if (!top_min_int) { + goto fail; + } + + bh_assert(update_top_block); + + /* + * update native_stack_top_min if + * new_sp = sp - size < native_stack_top_min + * + * Note: unless the stack has already overflown in this exec_env, + * native_stack_bound <= native_stack_top_min + */ + LLVMValueRef cmp_top = + LLVMBuildICmp(b, LLVMIntULT, new_sp, top_min_int, "cmp_top"); + if (!cmp_top) { + goto fail; + } + cmp_top = LLVMBuildOr(b, underflow, cmp_top, "cmp_top2"); + if (!cmp_top) { + goto fail; + } + if (!LLVMBuildCondBr(b, cmp_top, update_top_block, + call_wrapped_func_block)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* + * update native_stack_top_min + */ + LLVMPositionBuilderAtEnd(b, update_top_block); + LLVMValueRef new_sp_ptr = + LLVMBuildIntToPtr(b, new_sp, INT8_PTR_TYPE, "new_sp_ptr"); + if (!new_sp_ptr) { + goto fail; + } + if (!LLVMBuildStore(b, new_sp_ptr, + func_ctx->native_stack_top_min_addr)) { + goto fail; + } + if (!LLVMBuildBr(b, stack_bound_check_block)) { + goto fail; + } + } + else { + if (!LLVMBuildBr(b, stack_bound_check_block)) { + goto fail; + } + } + + LLVMPositionBuilderAtEnd(b, stack_bound_check_block); + if (comp_ctx->enable_stack_bound_check) { + /* + * trap if new_sp < native_stack_bound + */ + LLVMValueRef bound_int = LLVMBuildPtrToInt( + b, func_ctx->native_stack_bound, uintptr_type, "bound_base_int"); + if (!bound_int) { + goto fail; + } + LLVMValueRef cmp = + LLVMBuildICmp(b, LLVMIntULT, new_sp, bound_int, "cmp"); + if (!cmp) { + goto fail; + } + cmp = LLVMBuildOr(b, underflow, cmp, "cmp2"); + if (!cmp) { + goto fail; + } + /* todo: @llvm.expect.i1(i1 %cmp, i1 0) */ + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NATIVE_STACK_OVERFLOW, + true, cmp, call_wrapped_func_block)) + goto fail; + } + else { + if (!LLVMBuildBr(b, call_wrapped_func_block)) { + goto fail; + } + } + + /* + * call the wrapped function + * use a tail-call if possible + */ + LLVMPositionBuilderAtEnd(b, call_wrapped_func_block); + const char *name = "tail_call"; + LLVMTypeRef ret_type = LLVMGetReturnType(func_type); + if (ret_type == VOID_TYPE) { + name = ""; + } + LLVMValueRef retval = + LLVMBuildCall2(b, func_type, wrapped_func, params, param_count, name); + if (!retval) { + goto fail; + } + wasm_runtime_free(params); + params = NULL; + if (aot_target_precheck_can_use_musttail(comp_ctx)) { + LLVMSetTailCallKind(retval, LLVMTailCallKindMustTail); + } + else { + LLVMSetTailCallKind(retval, LLVMTailCallKindTail); + } + if (ret_type == VOID_TYPE) { + if (!LLVMBuildRetVoid(b)) { + goto fail; + } + } + else { + if (!LLVMBuildRet(b, retval)) { + goto fail; + } + } + + return true; +fail: + if (params != NULL) { + wasm_runtime_free(params); + } + aot_set_last_error("failed to build precheck wrapper function."); + return false; +} + +static bool +check_wasm_type(AOTCompContext *comp_ctx, uint8 type) +{ + if (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF) { + if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) { + aot_set_last_error("funcref or externref type was found, " + "try removing --disable-ref-types option " + "or adding --enable-gc option."); + return false; + } + else + return true; + } + else if (aot_is_type_gc_reftype(type)) { + if (!comp_ctx->enable_gc) { + aot_set_last_error("GC reference type was found, " + "try adding --enable-gc option."); + return false; + } + else + return true; + } + else if (type == VALUE_TYPE_V128) { + if (!comp_ctx->enable_simd) { + aot_set_last_error("SIMD type was found, try removing " + " --disable-simd option."); + return false; + } + return true; + } + else if (type != VALUE_TYPE_I32 && type != VALUE_TYPE_I64 + && type != VALUE_TYPE_F32 && type != VALUE_TYPE_F64) { + bh_assert(0); + } + + return true; +} + /** * Add LLVM function */ static LLVMValueRef -aot_add_llvm_func(AOTCompContext *comp_ctx, AOTFuncType *aot_func_type, - uint32 func_index) +aot_add_llvm_func(AOTCompContext *comp_ctx, LLVMModuleRef module, + const AOTFuncType *aot_func_type, uint32 func_index, + LLVMTypeRef *p_func_type, LLVMValueRef *p_precheck_func) { + WASMFunction *aot_func = + comp_ctx->comp_data->wasm_module->functions[func_index]; LLVMValueRef func = NULL; LLVMTypeRef *param_types, ret_type, func_type; - LLVMValueRef local_value; - char func_name[32]; + LLVMTypeRef func_type_wrapper; + LLVMValueRef func_wrapper; + LLVMBasicBlockRef func_begin; + char func_name[48]; uint64 size; uint32 i, j = 0, param_count = (uint64)aot_func_type->param_count; + uint32 backend_thread_num, compile_thread_num; + + /* Check function parameter types and result types */ + for (i = 0; + i < (uint32)(aot_func_type->param_count + aot_func_type->result_count); + i++) { + if (!check_wasm_type(comp_ctx, aot_func_type->types[i])) + return NULL; + } + /* Check function local types */ + for (i = 0; i < aot_func->local_count; i++) { + if (!check_wasm_type(comp_ctx, aot_func->local_types[i])) + return NULL; + } - /* aot context as first parameter */ + /* exec env as first parameter */ param_count++; + /* Extra wasm function results(except the first one)'s address are + * appended to aot function parameters. */ + if (aot_func_type->result_count > 1) + param_count += aot_func_type->result_count - 1; + /* Initialize parameter types of the LLVM function */ size = sizeof(LLVMTypeRef) * ((uint64)param_count); if (size >= UINT32_MAX - || !(param_types = wasm_malloc((uint32)size))) { + || !(param_types = wasm_runtime_malloc((uint32)size))) { aot_set_last_error("allocate memory failed."); return NULL; } @@ -56,72 +661,211 @@ aot_add_llvm_func(AOTCompContext *comp_ctx, AOTFuncType *aot_func_type, param_types[j++] = comp_ctx->exec_env_type; for (i = 0; i < aot_func_type->param_count; i++) param_types[j++] = TO_LLVM_TYPE(aot_func_type->types[i]); + /* Extra results' address */ + for (i = 1; i < aot_func_type->result_count; i++, j++) { + param_types[j] = + TO_LLVM_TYPE(aot_func_type->types[aot_func_type->param_count + i]); + if (!(param_types[j] = LLVMPointerType(param_types[j], 0))) { + aot_set_last_error("llvm get pointer type failed."); + goto fail; + } + } /* Resolve return type of the LLVM function */ if (aot_func_type->result_count) - ret_type = TO_LLVM_TYPE(aot_func_type->types[aot_func_type->param_count]); + ret_type = + TO_LLVM_TYPE(aot_func_type->types[aot_func_type->param_count]); else ret_type = VOID_TYPE; /* Resolve function prototype */ - if (!(func_type = LLVMFunctionType(ret_type, param_types, - param_count, false))) { + if (!(func_type = + LLVMFunctionType(ret_type, param_types, param_count, false))) { aot_set_last_error("create LLVM function type failed."); goto fail; } - /* Add LLVM function */ - snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, func_index); - if (!(func = LLVMAddFunction(comp_ctx->module, func_name, func_type))) { - aot_set_last_error("add LLVM function failed."); + bh_assert(func_index < comp_ctx->func_ctx_count); + bh_assert(LLVMGetReturnType(func_type) == ret_type); + + const char *prefix = AOT_FUNC_PREFIX; + const bool need_precheck = + comp_ctx->enable_stack_bound_check || comp_ctx->enable_stack_estimation; + LLVMValueRef precheck_func = NULL; + + if (need_precheck) { + precheck_func = aot_add_llvm_func1(comp_ctx, module, func_index, + aot_func_type->param_count, + func_type, AOT_FUNC_PREFIX); + if (!precheck_func) { + goto fail; + } + /* + * REVISIT: probably this breaks windows hw bound check + * (the RtlAddFunctionTable stuff) + */ + prefix = AOT_FUNC_INTERNAL_PREFIX; + } + if (!(func = aot_add_llvm_func1(comp_ctx, module, func_index, + aot_func_type->param_count, func_type, + prefix))) goto fail; + + if (comp_ctx->disable_llvm_jump_tables) { + LLVMAttributeRef attr_no_jump_tables = LLVMCreateStringAttribute( + comp_ctx->context, "no-jump-tables", + (uint32)strlen("no-jump-tables"), "true", (uint32)strlen("true")); + LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, + attr_no_jump_tables); } - j = 0; - local_value = LLVMGetParam(func, j++); - LLVMSetValueName(local_value, "exec_env"); + /* spread fp.all to every function */ + if (comp_ctx->emit_frame_pointer) { + const char *key = "frame-pointer"; + const char *val = "all"; + LLVMAttributeRef no_omit_fp = LLVMCreateStringAttribute( + comp_ctx->context, key, (unsigned)strlen(key), val, + (unsigned)strlen(val)); + if (!no_omit_fp) { + aot_set_last_error("create LLVM attribute (frame-pointer) failed."); + goto fail; + } + LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, no_omit_fp); + } - /* Set parameter names */ - for (i = 0; i < aot_func_type->param_count; i++) { - local_value = LLVMGetParam(func, j++); - LLVMSetValueName(local_value, ""); + if (need_precheck) { + if (!comp_ctx->is_jit_mode) + LLVMSetLinkage(func, LLVMInternalLinkage); + unsigned int kind = + LLVMGetEnumAttributeKindForName("noinline", strlen("noinline")); + LLVMAttributeRef attr_noinline = + LLVMCreateEnumAttribute(comp_ctx->context, kind, 0); + LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, + attr_noinline); + if (!strcmp(comp_ctx->target_arch, "xtensa")) { + /* Because "func" is only called by "precheck_func", short-call + * should be ok. We prefer short-call because it's smaller + * and more importantly doesn't involve relocations. + */ + LLVMAttributeRef attr_short_call = LLVMCreateStringAttribute( + comp_ctx->context, "short-call", (unsigned)strlen("short-call"), + "", 0); + LLVMAddAttributeAtIndex(func, LLVMAttributeFunctionIndex, + attr_short_call); + } + if (!aot_build_precheck_function(comp_ctx, module, precheck_func, + func_index, func_type, func)) + goto fail; + LLVMAddAttributeAtIndex(precheck_func, LLVMAttributeFunctionIndex, + attr_noinline); + *p_precheck_func = precheck_func; + } + else { + *p_precheck_func = func; + } + + if (p_func_type) + *p_func_type = func_type; + + backend_thread_num = WASM_ORC_JIT_BACKEND_THREAD_NUM; + compile_thread_num = WASM_ORC_JIT_COMPILE_THREAD_NUM; + + /* Add the jit wrapper function with simple prototype, so that we + can easily call it to trigger its compilation and let LLVM JIT + compile the actual jit functions by adding them into the function + list in the PartitionFunction callback */ + if (comp_ctx->is_jit_mode + && (func_index % (backend_thread_num * compile_thread_num) + < backend_thread_num)) { + func_type_wrapper = LLVMFunctionType(VOID_TYPE, NULL, 0, false); + if (!func_type_wrapper) { + aot_set_last_error("create LLVM function type failed."); + goto fail; + } + + snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, + func_index, "_wrapper"); + if (!(func_wrapper = + LLVMAddFunction(module, func_name, func_type_wrapper))) { + aot_set_last_error("add LLVM function failed."); + goto fail; + } + + if (!(func_begin = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_wrapper, "func_begin"))) { + aot_set_last_error("add LLVM basic block failed."); + goto fail; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, func_begin); + if (!LLVMBuildRetVoid(comp_ctx->builder)) { + aot_set_last_error("llvm build ret failed."); + goto fail; + } } fail: - wasm_free(param_types); + wasm_runtime_free(param_types); return func; } +static void +free_block_memory(AOTBlock *block) +{ + if (block->param_types) + wasm_runtime_free(block->param_types); + if (block->result_types) + wasm_runtime_free(block->result_types); + wasm_runtime_free(block); +} + /** * Create first AOTBlock, or function block for the function */ static AOTBlock * -aot_create_func_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - AOTFunc *func, AOTFuncType *aot_func_type) +aot_create_func_block(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, const AOTFunc *func, + const AOTFuncType *aot_func_type) { AOTBlock *aot_block; + uint32 param_count = aot_func_type->param_count, + result_count = aot_func_type->result_count; /* Allocate memory */ - if (!(aot_block = wasm_malloc(sizeof(AOTBlock)))) { + if (!(aot_block = wasm_runtime_malloc(sizeof(AOTBlock)))) { aot_set_last_error("allocate memory failed."); return NULL; } - memset(aot_block, 0, sizeof(AOTBlock)); + if (param_count + && !(aot_block->param_types = wasm_runtime_malloc(param_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + if (result_count) { + if (!(aot_block->result_types = wasm_runtime_malloc(result_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + } - /* Set block type and return type */ - aot_block->block_type = BLOCK_TYPE_FUNCTION; - if (aot_func_type->result_count) - aot_block->return_type = aot_func_type->types[aot_func_type->param_count]; - else - aot_block->return_type = VALUE_TYPE_VOID; - + /* Set block data */ + aot_block->label_type = LABEL_TYPE_FUNCTION; + aot_block->param_count = param_count; + if (param_count) { + bh_memcpy_s(aot_block->param_types, param_count, aot_func_type->types, + param_count); + } + aot_block->result_count = result_count; + if (result_count) { + bh_memcpy_s(aot_block->result_types, result_count, + aot_func_type->types + param_count, result_count); + } aot_block->wasm_code_end = func->code + func->code_size; /* Add function entry block */ - if (!(aot_block->llvm_entry_block = - LLVMAppendBasicBlockInContext(comp_ctx->context, func_ctx->func, - "func_begin"))) { + if (!(aot_block->llvm_entry_block = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "func_begin"))) { aot_set_last_error("add LLVM basic block failed."); goto fail; } @@ -129,220 +873,809 @@ aot_create_func_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return aot_block; fail: - wasm_free(aot_block); + free_block_memory(aot_block); return NULL; } static bool -create_exception_blocks(AOTFuncContext *func_ctx) +create_argv_buf(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { - if (!(func_ctx->exception_blocks = - wasm_malloc(sizeof(LLVMBasicBlockRef) * EXCE_NUM))) { - aot_set_last_error("allocate memory failed."); - return false;; + LLVMValueRef argv_buf_offset = I32_THREE, argv_buf_addr; + LLVMTypeRef int32_ptr_type; + + /* Get argv buffer address */ + if (!(argv_buf_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, + &argv_buf_offset, 1, "argv_buf_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(int32_ptr_type = LLVMPointerType(INT32_PTR_TYPE, 0))) { + aot_set_last_error("llvm add pointer type failed"); + return false; + } + + /* Convert to int32 pointer type */ + if (!(argv_buf_addr = LLVMBuildBitCast(comp_ctx->builder, argv_buf_addr, + int32_ptr_type, "argv_buf_ptr"))) { + aot_set_last_error("llvm build load failed"); + return false; } - memset(func_ctx->exception_blocks, 0, - sizeof(LLVMBasicBlockRef) * EXCE_NUM); - return true; + if (!(func_ctx->argv_buf = LLVMBuildLoad2(comp_ctx->builder, INT32_PTR_TYPE, + argv_buf_addr, "argv_buf"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + + return true; } static bool -create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, +create_native_stack_bound(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef stack_bound_offset = I32_FOUR, stack_bound_addr; + + if (!(stack_bound_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, + &stack_bound_offset, 1, "stack_bound_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(func_ctx->native_stack_bound = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, stack_bound_addr, + "native_stack_bound"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + + return true; +} + +static bool +create_native_stack_top_min(const 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(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef aux_stack_bound_offset = I32_SIX, aux_stack_bound_addr; + LLVMValueRef aux_stack_bottom_offset = I32_SEVEN, aux_stack_bottom_addr; + + /* Get aux stack boundary address */ + if (!(aux_stack_bound_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, + &aux_stack_bound_offset, 1, "aux_stack_bound_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(aux_stack_bound_addr = + LLVMBuildBitCast(comp_ctx->builder, aux_stack_bound_addr, + INTPTR_T_PTR_TYPE, "aux_stack_bound_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!(func_ctx->aux_stack_bound = + LLVMBuildLoad2(comp_ctx->builder, INTPTR_T_TYPE, + aux_stack_bound_addr, "aux_stack_bound_intptr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(func_ctx->aux_stack_bound = + LLVMBuildZExt(comp_ctx->builder, func_ctx->aux_stack_bound, + I64_TYPE, "aux_stack_bound_i64"))) { + aot_set_last_error("llvm build truncOrBitCast failed."); + return false; + } + + /* Get aux stack bottom address */ + if (!(aux_stack_bottom_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, + &aux_stack_bottom_offset, 1, "aux_stack_bottom_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(aux_stack_bottom_addr = + LLVMBuildBitCast(comp_ctx->builder, aux_stack_bottom_addr, + INTPTR_T_PTR_TYPE, "aux_stack_bottom_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!(func_ctx->aux_stack_bottom = + LLVMBuildLoad2(comp_ctx->builder, INTPTR_T_TYPE, + aux_stack_bottom_addr, "aux_stack_bottom"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(func_ctx->aux_stack_bottom = + LLVMBuildZExt(comp_ctx->builder, func_ctx->aux_stack_bottom, + I64_TYPE, "aux_stack_bottom_i64"))) { + aot_set_last_error("llvm build truncOrBitCast failed."); + return false; + } + + return true; +} + +static bool +create_aux_stack_frame(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef wasm_stack_top_bound_ptr, offset; + + offset = I32_ONE; + if (!(func_ctx->cur_frame_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1, + "cur_frame_ptr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(func_ctx->cur_frame = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->cur_frame_ptr, "cur_frame"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + + /* Get exec_env->wasm_stack.top_boundary and its address */ + offset = I32_TEN; + if (!(wasm_stack_top_bound_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1, + "wasm_stack_top_bound_ptr")) + || !(func_ctx->wasm_stack_top_bound = LLVMBuildLoad2( + comp_ctx->builder, INT8_PTR_TYPE, wasm_stack_top_bound_ptr, + "wasm_stack_top_bound"))) { + aot_set_last_error("load wasm_stack.top_boundary failed"); + return false; + } + + offset = I32_ELEVEN; + if (!(func_ctx->wasm_stack_top_ptr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1, + "wasm_stack_top_ptr"))) { + aot_set_last_error("llvm build inbounds gep failed"); + return false; + } + + return true; +} + +static bool +create_native_symbol(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef native_symbol_offset = I32_EIGHT, native_symbol_addr; + + if (!(native_symbol_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, + &native_symbol_offset, 1, "native_symbol_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + + if (!(func_ctx->native_symbol = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + native_symbol_addr, "native_symbol_tmp"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!(func_ctx->native_symbol = + LLVMBuildBitCast(comp_ctx->builder, func_ctx->native_symbol, + comp_ctx->exec_env_type, "native_symbol"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + return true; +} + +static bool +create_local_variables(const AOTCompData *comp_data, + const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + const AOTFunc *func) +{ + AOTFuncType *aot_func_type = + (AOTFuncType *)comp_data->types[func->func_type_index]; + char local_name[32]; + uint32 i, j = 1; + + for (i = 0; i < aot_func_type->param_count; i++, j++) { + snprintf(local_name, sizeof(local_name), "l%d", i); + func_ctx->locals[i] = + LLVMBuildAlloca(comp_ctx->builder, + TO_LLVM_TYPE(aot_func_type->types[i]), local_name); + if (!func_ctx->locals[i]) { + aot_set_last_error("llvm build alloca failed."); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, LLVMGetParam(func_ctx->func, j), + func_ctx->locals[i])) { + aot_set_last_error("llvm build store failed."); + return false; + } + } + + for (i = 0; i < func->local_count; i++) { + LLVMTypeRef local_type; + LLVMValueRef local_value = NULL; + snprintf(local_name, sizeof(local_name), "l%d", + aot_func_type->param_count + i); + local_type = TO_LLVM_TYPE(func->local_types_wp[i]); + func_ctx->locals[aot_func_type->param_count + i] = + LLVMBuildAlloca(comp_ctx->builder, local_type, local_name); + if (!func_ctx->locals[aot_func_type->param_count + i]) { + aot_set_last_error("llvm build alloca failed."); + return false; + } + switch (func->local_types_wp[i]) { + case VALUE_TYPE_I32: + local_value = I32_ZERO; + break; + case VALUE_TYPE_I64: + local_value = I64_ZERO; + break; + case VALUE_TYPE_F32: + local_value = F32_ZERO; + break; + case VALUE_TYPE_F64: + local_value = F64_ZERO; + break; + case VALUE_TYPE_V128: + local_value = V128_i64x2_ZERO; + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + if (!comp_ctx->enable_gc) + local_value = REF_NULL; + else + local_value = GC_REF_NULL; + break; +#if WASM_ENABLE_GC != 0 + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + local_value = GC_REF_NULL; + break; +#endif + default: + bh_assert(0); + break; + } + if (!LLVMBuildStore(comp_ctx->builder, local_value, + func_ctx->locals[aot_func_type->param_count + i])) { + aot_set_last_error("llvm build store failed."); + return false; + } + } + + return true; +} + +static bool +create_memory_info(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMTypeRef int8_ptr_type, uint32 func_index) { - LLVMValueRef offset; + LLVMValueRef offset, mem_info_base; + uint32 memory_count; WASMModule *module = comp_ctx->comp_data->wasm_module; WASMFunction *func = module->functions[func_index]; - bool mem_space_unchanged = (!func->has_op_memory_grow && !func->has_op_func_call) - || (!module->possible_memory_grow); + LLVMTypeRef bound_check_type; + bool mem_space_unchanged = + (!func->has_op_memory_grow && !func->has_op_func_call) + || (!module->possible_memory_grow); +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory; +#endif + + func_ctx->mem_space_unchanged = mem_space_unchanged; + + memory_count = module->memory_count + module->import_memory_count; + /* If the module doesn't have memory, reserve + one mem_info space with empty content */ + if (memory_count == 0) + memory_count = 1; + + if (!(func_ctx->mem_info = + wasm_runtime_malloc(sizeof(AOTMemInfo) * memory_count))) { + return false; + } + memset(func_ctx->mem_info, 0, sizeof(AOTMemInfo)); + + /* Currently we only create memory info for memory 0 */ + /* Load memory base address */ +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared_memory = + comp_ctx->comp_data->memories[0].flags & 0x02 ? true : false; + if (is_shared_memory) { + LLVMValueRef shared_mem_addr; + offset = I32_CONST(offsetof(AOTModuleInstance, memories)); + if (!offset) { + aot_set_last_error("create llvm const failed."); + return false; + } + + /* aot_inst->memories */ + if (!(shared_mem_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, &offset, 1, + "shared_mem_addr_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(shared_mem_addr = + LLVMBuildBitCast(comp_ctx->builder, shared_mem_addr, + int8_ptr_type, "shared_mem_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + /* aot_inst->memories[0] */ + if (!(shared_mem_addr = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + shared_mem_addr, "shared_mem_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(shared_mem_addr = + LLVMBuildBitCast(comp_ctx->builder, shared_mem_addr, + int8_ptr_type, "shared_mem_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (!(shared_mem_addr = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + shared_mem_addr, "shared_mem_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + /* memories[0]->memory_data */ + offset = I32_CONST(offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_base_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, shared_mem_addr, &offset, 1, + "mem_base_addr_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + /* memories[0]->cur_page_count */ + offset = I32_CONST(offsetof(AOTMemoryInstance, cur_page_count)); + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + shared_mem_addr, &offset, 1, + "mem_cur_page_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + /* memories[0]->memory_data_size */ + offset = I32_CONST(offsetof(AOTMemoryInstance, memory_data_size)); + if (!(func_ctx->mem_info[0].mem_data_size_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, shared_mem_addr, &offset, 1, + "mem_data_size_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + else +#endif + { + uint32 offset_of_global_table_data; - func_ctx->mem_space_unchanged = mem_space_unchanged; + if (comp_ctx->is_jit_mode) + offset_of_global_table_data = + offsetof(WASMModuleInstance, global_table_data); + else + offset_of_global_table_data = + offsetof(AOTModuleInstance, global_table_data); + + offset = I32_CONST(offset_of_global_table_data + + offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_base_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, &offset, 1, + "mem_base_addr_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + offset = I32_CONST(offset_of_global_table_data + + offsetof(AOTMemoryInstance, cur_page_count)); + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + func_ctx->aot_inst, &offset, 1, + "mem_cur_page_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + offset = I32_CONST(offset_of_global_table_data + + offsetof(AOTMemoryInstance, memory_data_size)); + if (!(func_ctx->mem_info[0].mem_data_size_addr = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, &offset, 1, + "mem_data_size_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + /* Store mem info base address before cast */ + mem_info_base = func_ctx->mem_info[0].mem_base_addr; - /* Load memory base address */ - offset = I32_CONST(offsetof(AOTModuleInstance, memory_data.ptr)); - if (!(func_ctx->mem_base_addr = - LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, - &offset, 1, "mem_base_addr_offset"))) { - aot_set_last_error("llvm build in bounds gep failed"); + if (!(func_ctx->mem_info[0].mem_base_addr = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_base_addr, + int8_ptr_type, "mem_base_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_cur_page_count_addr, + INT32_PTR_TYPE, "mem_cur_page_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); return false; } - if (!(func_ctx->mem_base_addr = - LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_base_addr, - int8_ptr_type, "mem_base_addr_ptr"))) { + if (!(func_ctx->mem_info[0].mem_data_size_addr = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_data_size_addr, + INT64_PTR_TYPE, "mem_data_size_ptr"))) { aot_set_last_error("llvm build bit cast failed"); return false; } if (mem_space_unchanged) { - if (!(func_ctx->mem_base_addr = - LLVMBuildLoad(comp_ctx->builder, func_ctx->mem_base_addr, - "mem_base_addr"))) { + if (!(func_ctx->mem_info[0].mem_base_addr = LLVMBuildLoad2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->mem_info[0].mem_base_addr, "mem_base_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, + func_ctx->mem_info[0].mem_cur_page_count_addr, + "mem_cur_page_count"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_data_size_addr = LLVMBuildLoad2( + comp_ctx->builder, I64_TYPE, + func_ctx->mem_info[0].mem_data_size_addr, "mem_data_size"))) { aot_set_last_error("llvm build load failed"); return false; } } - - /* Load memory data size */ - offset = I32_CONST(offsetof(AOTModuleInstance, memory_data_size)); - if (!(func_ctx->mem_data_size = - LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, - &offset, 1, "mem_data_size_offset"))) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + else if (is_shared_memory) { + /* The base address for shared memory will never changed, + we can load the value here */ + if (!(func_ctx->mem_info[0].mem_base_addr = LLVMBuildLoad2( + comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->mem_info[0].mem_base_addr, "mem_base_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } +#endif + + bound_check_type = (comp_ctx->pointer_size == sizeof(uint64)) + ? INT64_PTR_TYPE + : INT32_PTR_TYPE; + + /* Load memory bound check constants */ + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_1byte) + - offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_bound_check_1byte = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, mem_info_base, + &offset, 1, "bound_check_1byte_offset"))) { aot_set_last_error("llvm build in bounds gep failed"); return false; } - if (!(func_ctx->mem_data_size = - LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_data_size, - INT32_PTR_TYPE, "mem_data_size_ptr"))) { + if (!(func_ctx->mem_info[0].mem_bound_check_1byte = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_bound_check_1byte, + bound_check_type, "bound_check_1byte_ptr"))) { aot_set_last_error("llvm build bit cast failed"); return false; } if (mem_space_unchanged) { - if (!(func_ctx->mem_data_size = - LLVMBuildLoad(comp_ctx->builder, func_ctx->mem_data_size, - "mem_data_size"))) { + if (!(func_ctx->mem_info[0].mem_bound_check_1byte = LLVMBuildLoad2( + comp_ctx->builder, + (comp_ctx->pointer_size == sizeof(uint64)) ? I64_TYPE + : I32_TYPE, + func_ctx->mem_info[0].mem_bound_check_1byte, + "bound_check_1byte"))) { aot_set_last_error("llvm build load failed"); return false; } - if (!(func_ctx->mem_bound_1_byte = - LLVMBuildSub(comp_ctx->builder, - func_ctx->mem_data_size, I32_ONE, - "mem_bound_1_byte")) - || !(func_ctx->mem_bound_2_bytes = - LLVMBuildSub(comp_ctx->builder, - func_ctx->mem_data_size, I32_TWO, - "mem_bound_2_bytes")) - || !(func_ctx->mem_bound_4_bytes = - LLVMBuildSub(comp_ctx->builder, - func_ctx->mem_data_size, I32_FOUR, - "mem_bound_4_bytes")) - || !(func_ctx->mem_bound_8_bytes = - LLVMBuildSub(comp_ctx->builder, - func_ctx->mem_data_size, I32_EIGHT, - "mem_bound_8_bytes"))) { - aot_set_last_error("llvm build sub failed"); - return false; - } } - /* Load heap base address */ - offset = I32_CONST(offsetof(AOTModuleInstance, heap_data.ptr)); - if (!(func_ctx->heap_base_addr = - LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, - &offset, 1, "heap_base_addr_offset"))) { + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_2bytes) + - offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_bound_check_2bytes = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, mem_info_base, + &offset, 1, "bound_check_2bytes_offset"))) { aot_set_last_error("llvm build in bounds gep failed"); return false; } - if (!(func_ctx->heap_base_addr = - LLVMBuildBitCast(comp_ctx->builder, func_ctx->heap_base_addr, - int8_ptr_type, "heap_base_addr_tmp"))) { + if (!(func_ctx->mem_info[0].mem_bound_check_2bytes = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_bound_check_2bytes, + bound_check_type, "bound_check_2bytes_ptr"))) { aot_set_last_error("llvm build bit cast failed"); return false; } - if (!(func_ctx->heap_base_addr = - LLVMBuildLoad(comp_ctx->builder, func_ctx->heap_base_addr, - "heap_base_addr"))) { - aot_set_last_error("llvm build load failed"); - return false; + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_2bytes = LLVMBuildLoad2( + comp_ctx->builder, + (comp_ctx->pointer_size == sizeof(uint64)) ? I64_TYPE + : I32_TYPE, + func_ctx->mem_info[0].mem_bound_check_2bytes, + "bound_check_2bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } } - /* Load heap base offset */ - offset = I32_CONST(offsetof(AOTModuleInstance, heap_base_offset)); - if (!(func_ctx->heap_base_offset = - LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, - &offset, 1, "heap_base_offset_offset"))) { + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_4bytes) + - offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_bound_check_4bytes = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, mem_info_base, + &offset, 1, "bound_check_4bytes_offset"))) { aot_set_last_error("llvm build in bounds gep failed"); return false; } - if (!(func_ctx->heap_base_offset = - LLVMBuildBitCast(comp_ctx->builder, func_ctx->heap_base_offset, - INT32_PTR_TYPE, "heap_base_offset_tmp"))) { + if (!(func_ctx->mem_info[0].mem_bound_check_4bytes = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_bound_check_4bytes, + bound_check_type, "bound_check_4bytes_ptr"))) { aot_set_last_error("llvm build bit cast failed"); return false; } - if (!(func_ctx->heap_base_offset = - LLVMBuildLoad(comp_ctx->builder, func_ctx->heap_base_offset, - "heap_base_offset"))) { - aot_set_last_error("llvm build load failed"); - return false; + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_4bytes = LLVMBuildLoad2( + comp_ctx->builder, + (comp_ctx->pointer_size == sizeof(uint64)) ? I64_TYPE + : I32_TYPE, + func_ctx->mem_info[0].mem_bound_check_4bytes, + "bound_check_4bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } } - /* Load heap data size */ - offset = I32_CONST(offsetof(AOTModuleInstance, heap_data_size)); - if (!(func_ctx->heap_data_size = - LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, - &offset, 1, "heap_data_size_offset"))) { + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_8bytes) + - offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_bound_check_8bytes = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, mem_info_base, + &offset, 1, "bound_check_8bytes_offset"))) { aot_set_last_error("llvm build in bounds gep failed"); return false; } - if (!(func_ctx->heap_data_size = - LLVMBuildBitCast(comp_ctx->builder, func_ctx->heap_data_size, - INT32_PTR_TYPE, "heap_data_size_tmp"))) { + if (!(func_ctx->mem_info[0].mem_bound_check_8bytes = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_bound_check_8bytes, + bound_check_type, "bound_check_8bytes_ptr"))) { aot_set_last_error("llvm build bit cast failed"); return false; } - if (!(func_ctx->heap_data_size = - LLVMBuildLoad(comp_ctx->builder, func_ctx->heap_data_size, - "heap_data_size"))) { - aot_set_last_error("llvm build load failed"); + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_8bytes = LLVMBuildLoad2( + comp_ctx->builder, + (comp_ctx->pointer_size == sizeof(uint64)) ? I64_TYPE + : I32_TYPE, + func_ctx->mem_info[0].mem_bound_check_8bytes, + "bound_check_8bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } + + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_16bytes) + - offsetof(AOTMemoryInstance, memory_data)); + if (!(func_ctx->mem_info[0].mem_bound_check_16bytes = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, mem_info_base, &offset, 1, + "bound_check_16bytes_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); return false; } - if (!(func_ctx->heap_bound_1_byte = - LLVMBuildSub(comp_ctx->builder, - func_ctx->heap_data_size, I32_ONE, - "heap_bound_1_byte")) - || !(func_ctx->heap_bound_2_bytes = - LLVMBuildSub(comp_ctx->builder, - func_ctx->heap_data_size, I32_TWO, - "heap_bound_2_bytes")) - || !(func_ctx->heap_bound_4_bytes = - LLVMBuildSub(comp_ctx->builder, - func_ctx->heap_data_size, I32_FOUR, - "heap_bound_4_bytes")) - || !(func_ctx->heap_bound_8_bytes = - LLVMBuildSub(comp_ctx->builder, - func_ctx->heap_data_size, I32_EIGHT, - "heap_bound_8_bytes"))) { - aot_set_last_error("llvm build sub failed"); + if (!(func_ctx->mem_info[0].mem_bound_check_16bytes = LLVMBuildBitCast( + comp_ctx->builder, func_ctx->mem_info[0].mem_bound_check_16bytes, + bound_check_type, "bound_check_16bytes_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); return false; } + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_16bytes = LLVMBuildLoad2( + comp_ctx->builder, + (comp_ctx->pointer_size == sizeof(uint64)) ? I64_TYPE + : I32_TYPE, + func_ctx->mem_info[0].mem_bound_check_16bytes, + "bound_check_16bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } return true; } +#define BUILD_IS_NOT_NULL(value, res, name) \ + do { \ + if (!(res = LLVMBuildIsNotNull(comp_ctx->builder, value, name))) { \ + aot_set_last_error("llvm build is not null failed."); \ + goto fail; \ + } \ + } while (0) + +#define get_module_extra_field_offset(field) \ + do { \ + offset_u32 = get_module_inst_extra_offset(comp_ctx); \ + if (comp_ctx->is_jit_mode) \ + offset_u32 += offsetof(WASMModuleInstanceExtra, field); \ + else \ + offset_u32 += offsetof(AOTModuleInstanceExtra, field); \ + } while (0) + +#define LOAD_MODULE_EXTRA_FIELD_AND_ALLOCA(field, type) \ + do { \ + get_module_extra_field_offset(field); \ + offset = I32_CONST(offset_u32); \ + CHECK_LLVM_CONST(offset); \ + if (!(field_p = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, \ + func_ctx->aot_inst, &offset, 1, \ + #field "_p"))) { \ + aot_set_last_error("llvm build inbounds gep failed"); \ + goto fail; \ + } \ + if (!(load_val = \ + LLVMBuildLoad2(comp_ctx->builder, type, field_p, #field))) { \ + aot_set_last_error("llvm build load failed"); \ + goto fail; \ + } \ + if (!(func_ctx->field = \ + LLVMBuildAlloca(comp_ctx->builder, type, #field))) { \ + aot_set_last_error("llvm build alloca failed"); \ + goto fail; \ + } \ + if (!LLVMBuildStore(comp_ctx->builder, load_val, func_ctx->field)) { \ + aot_set_last_error("llvm build store failed"); \ + goto fail; \ + } \ + } while (0) + static bool -create_table_base(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +create_shared_heap_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { - LLVMValueRef offset; +#if WASM_ENABLE_SHARED_HEAP != 0 + LLVMValueRef offset, field_p, load_val, shared_heap_head_p, + shared_heap_head, cmp, field_p_or_default, shared_heap_head_start_off, + shared_heap_head_start_off_minus_one; + LLVMTypeRef shared_heap_offset_type; + uint32 offset_u32; +#if WASM_ENABLE_MEMORY64 == 0 + bool is_memory64 = false; +#else + bool is_memory64 = IS_MEMORY64; +#endif + + shared_heap_offset_type = + comp_ctx->pointer_size == sizeof(uint64) ? I64_TYPE : I32_TYPE; + + /* shared_heap_base_addr_adj, shared_heap_start_off, and + * shared_heap_end_off can be updated later, use local variable to + * represent them */ + LOAD_MODULE_EXTRA_FIELD_AND_ALLOCA(shared_heap_base_addr_adj, + INT8_PTR_TYPE); + LOAD_MODULE_EXTRA_FIELD_AND_ALLOCA(shared_heap_start_off, + shared_heap_offset_type); + LOAD_MODULE_EXTRA_FIELD_AND_ALLOCA(shared_heap_end_off, + shared_heap_offset_type); + + /* Shared Heap head start off won't be updated, no need to alloca */ + get_module_extra_field_offset(shared_heap); + offset = I32_CONST(offset_u32); + CHECK_LLVM_CONST(offset); + if (!(shared_heap_head_p = LLVMBuildInBoundsGEP2( + comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, &offset, 1, + "shared_heap_head_p"))) { + aot_set_last_error("llvm build inbounds gep failed"); + goto fail; + } + if (!(shared_heap_head = + LLVMBuildLoad2(comp_ctx->builder, INT8_PTR_TYPE, + shared_heap_head_p, "shared_heap_head"))) { + aot_set_last_error("llvm build load failed"); + goto fail; + } + BUILD_IS_NOT_NULL(shared_heap_head, cmp, "has_shared_heap"); - offset = I32_CONST(offsetof(AOTModuleInstance, global_table_heap_data.bytes) - + comp_ctx->comp_data->global_data_size); - func_ctx->table_base = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->aot_inst, - &offset, 1, - "table_base_tmp"); - if (!func_ctx->table_base) { - aot_set_last_error("llvm build in bounds gep failed."); - return false; + if (is_memory64) { + offset_u32 = offsetof(WASMSharedHeap, start_off_mem64); } - func_ctx->table_base = LLVMBuildBitCast(comp_ctx->builder, func_ctx->table_base, - INT32_PTR_TYPE, "table_base"); - if (!func_ctx->table_base) { - aot_set_last_error("llvm build bit cast failed."); - return false; + else { + offset_u32 = offsetof(WASMSharedHeap, start_off_mem32); + } + offset = I32_CONST(offset_u32); + CHECK_LLVM_CONST(offset); + if (!(field_p = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, + shared_heap_head, &offset, 1, + "head_start_off_p"))) { + aot_set_last_error("llvm build inbounds gep failed"); + goto fail; + } + + /* Select a valid shared heap head ptr or safe alloca ptr stores + * shared_heap_start_off(UINT32_MAX/UINT64_MAX) */ + if (!(field_p_or_default = LLVMBuildSelect(comp_ctx->builder, cmp, field_p, + func_ctx->shared_heap_start_off, + "ptr_or_default"))) { + aot_set_last_error("llvm build select failed"); + goto fail; + } + + if (!(shared_heap_head_start_off = LLVMBuildLoad2( + comp_ctx->builder, shared_heap_offset_type, field_p_or_default, + "shared_heap_head_start_off"))) { + aot_set_last_error("llvm build load failed"); + goto fail; + } + if (!(shared_heap_head_start_off_minus_one = LLVMBuildAdd( + comp_ctx->builder, shared_heap_head_start_off, + comp_ctx->pointer_size == sizeof(uint64) ? I64_NEG_ONE + : I32_NEG_ONE, + "head_start_off_minus_one"))) { + aot_set_last_error("llvm build load failed"); + goto fail; + } + + /* if there is attached shared heap(s), the value will be valid start_off-1, + * otherwise it will be UINT32_MAX/UINT64_MAX, so during the bounds checks, + * when has attached shared heap: + * offset > start_off - 1 => offset >= start_off + * when no attached shared heap: + * offset > UINT32_MAX/UINT64_MAX is always false + * */ + if (!(func_ctx->shared_heap_head_start_off = LLVMBuildSelect( + comp_ctx->builder, cmp, shared_heap_head_start_off_minus_one, + shared_heap_head_start_off, "head_start_off"))) { + aot_set_last_error("llvm build select failed"); + goto fail; } return true; +fail: + return false; +#else /* else of WASM_ENABLE_SHARED_HEAP != 0 */ + return true; +#endif /* end of WASM_ENABLE_SHARED_HEAP != 0 */ } static bool -create_cur_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +create_cur_exception(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { LLVMValueRef offset; offset = I32_CONST(offsetof(AOTModuleInstance, cur_exception)); - func_ctx->cur_exception = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->aot_inst, - &offset, 1, - "cur_execption"); + func_ctx->cur_exception = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, + &offset, 1, "cur_exception"); if (!func_ctx->cur_exception) { aot_set_last_error("llvm build in bounds gep failed."); return false; @@ -351,82 +1684,166 @@ create_cur_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) } static bool -create_func_ptrs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +create_func_type_indexes(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) { - LLVMValueRef offset, func_ptrs_ptr; - LLVMTypeRef void_ptr_type; - - offset = I32_CONST(offsetof(AOTModuleInstance, func_ptrs.ptr)); - func_ptrs_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->aot_inst, - &offset, 1, - "func_ptrs_ptr"); - if (!func_ptrs_ptr) { - aot_set_last_error("llvm build in bounds gep failed."); + LLVMValueRef offset, func_type_indexes_ptr; + LLVMTypeRef int32_ptr_type; + + offset = I32_CONST(offsetof(AOTModuleInstance, func_type_indexes)); + func_type_indexes_ptr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, + &offset, 1, "func_type_indexes_ptr"); + if (!func_type_indexes_ptr) { + aot_set_last_error("llvm build add failed."); return false; } - if (!(void_ptr_type = LLVMPointerType(VOID_PTR_TYPE, 0)) - || !(void_ptr_type = LLVMPointerType(void_ptr_type, 0))) { + if (!(int32_ptr_type = LLVMPointerType(INT32_PTR_TYPE, 0))) { aot_set_last_error("llvm get pointer type failed."); return false; } - func_ctx->func_ptrs = LLVMBuildBitCast(comp_ctx->builder, func_ptrs_ptr, - void_ptr_type, "func_ptrs_tmp"); + func_ctx->func_type_indexes = + LLVMBuildBitCast(comp_ctx->builder, func_type_indexes_ptr, + int32_ptr_type, "func_type_indexes_tmp"); + if (!func_ctx->func_type_indexes) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + func_ctx->func_type_indexes = + LLVMBuildLoad2(comp_ctx->builder, INT32_PTR_TYPE, + func_ctx->func_type_indexes, "func_type_indexes"); + if (!func_ctx->func_type_indexes) { + aot_set_last_error("llvm build load failed."); + return false; + } + return true; +} + +static bool +create_func_ptrs(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef offset; + + offset = I32_CONST(offsetof(AOTModuleInstance, func_ptrs)); + func_ctx->func_ptrs = + LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_ctx->aot_inst, + &offset, 1, "func_ptrs_offset"); + if (!func_ctx->func_ptrs) { + aot_set_last_error("llvm build in bounds gep failed."); + return false; + } + func_ctx->func_ptrs = + LLVMBuildBitCast(comp_ctx->builder, func_ctx->func_ptrs, + comp_ctx->exec_env_type, "func_ptrs_tmp"); if (!func_ctx->func_ptrs) { aot_set_last_error("llvm build bit cast failed."); return false; } - func_ctx->func_ptrs = LLVMBuildLoad(comp_ctx->builder, func_ctx->func_ptrs, - "func_ptrs"); + func_ctx->func_ptrs = LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, + func_ctx->func_ptrs, "func_ptrs_ptr"); if (!func_ctx->func_ptrs) { aot_set_last_error("llvm build load failed."); return false; } + func_ctx->func_ptrs = + LLVMBuildBitCast(comp_ctx->builder, func_ctx->func_ptrs, + comp_ctx->exec_env_type, "func_ptrs"); + if (!func_ctx->func_ptrs) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + return true; } +const char *aot_stack_sizes_name = AOT_STACK_SIZES_NAME; +const char *aot_stack_sizes_alias_name = AOT_STACK_SIZES_ALIAS_NAME; +const char *aot_stack_sizes_section_name = AOT_STACK_SIZES_SECTION_NAME; + static bool -create_func_type_indexes(AOTCompContext *comp_ctx, - AOTFuncContext *func_ctx) +aot_create_stack_sizes(const AOTCompData *comp_data, AOTCompContext *comp_ctx) { - LLVMValueRef offset, func_type_indexes_ptr; - LLVMTypeRef int32_ptr_type; + LLVMValueRef stack_sizes, *values, array, alias; + LLVMTypeRef stack_sizes_type; +#if LLVM_VERSION_MAJOR <= 13 + LLVMTypeRef alias_type; +#endif + uint64 size; + uint32 i; - offset = I32_CONST(offsetof(AOTModuleInstance, func_type_indexes.ptr)); - func_type_indexes_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->aot_inst, - &offset, 1, - "func_type_indexes_ptr"); - if (!func_type_indexes_ptr) { - aot_set_last_error("llvm build add failed."); + stack_sizes_type = LLVMArrayType(I32_TYPE, comp_data->func_count); + if (!stack_sizes_type) { + aot_set_last_error("failed to create stack_sizes type."); return false; } - if (!(int32_ptr_type = LLVMPointerType(INT32_PTR_TYPE, 0))) { - aot_set_last_error("llvm get pointer type failed."); + stack_sizes = + LLVMAddGlobal(comp_ctx->module, stack_sizes_type, aot_stack_sizes_name); + if (!stack_sizes) { + aot_set_last_error("failed to create stack_sizes global."); return false; } - func_ctx->func_type_indexes = LLVMBuildBitCast(comp_ctx->builder, - func_type_indexes_ptr, - int32_ptr_type, - "func_type_indexes_tmp"); - if (!func_ctx->func_type_indexes) { - aot_set_last_error("llvm build bit cast failed."); + size = sizeof(LLVMValueRef) * comp_data->func_count; + if (size >= UINT32_MAX || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); return false; } - func_ctx->func_type_indexes = LLVMBuildLoad(comp_ctx->builder, - func_ctx->func_type_indexes, - "func_type_indexes"); - if (!func_ctx->func_type_indexes) { - aot_set_last_error("llvm build load failed."); + for (i = 0; i < comp_data->func_count; i++) { + /* + * This value is a placeholder, which will be replaced + * after the corresponding functions are compiled. + * + * Don't use zeros because LLVM can optimize them to + * zeroinitializer. + */ + values[i] = I32_NEG_ONE; + } + + array = LLVMConstArray(I32_TYPE, values, comp_data->func_count); + wasm_runtime_free(values); + if (!array) { + aot_set_last_error("failed to create stack_sizes initializer."); + return false; + } + LLVMSetInitializer(stack_sizes, array); + + /* + * create an alias so that aot_resolve_stack_sizes can find it. + */ +#if LLVM_VERSION_MAJOR > 13 + alias = LLVMAddAlias2(comp_ctx->module, stack_sizes_type, 0, stack_sizes, + aot_stack_sizes_alias_name); +#else + alias_type = LLVMPointerType(stack_sizes_type, 0); + if (!alias_type) { + aot_set_last_error("failed to create alias type."); return false; } + alias = LLVMAddAlias(comp_ctx->module, alias_type, stack_sizes, + aot_stack_sizes_alias_name); +#endif + if (!alias) { + aot_set_last_error("failed to create stack_sizes alias."); + return false; + } + + if (!comp_ctx->is_jit_mode) { + LLVMSetLinkage(stack_sizes, LLVMInternalLinkage); + /* + * for AOT, place it into a dedicated section for the convenience + * of the AOT file generation and symbol resolutions. + */ + LLVMSetSection(stack_sizes, aot_stack_sizes_section_name); + } + comp_ctx->stack_sizes_type = stack_sizes_type; + comp_ctx->stack_sizes = stack_sizes; return true; } @@ -434,23 +1851,23 @@ create_func_type_indexes(AOTCompContext *comp_ctx, * Create function compiler context */ static AOTFuncContext * -aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, +aot_create_func_context(const AOTCompData *comp_data, AOTCompContext *comp_ctx, AOTFunc *func, uint32 func_index) { AOTFuncContext *func_ctx; - AOTFuncType *aot_func_type = comp_data->func_types[func->func_type_index]; + AOTFuncType *aot_func_type = + (AOTFuncType *)comp_data->types[func->func_type_index]; + WASMModule *module = comp_ctx->comp_data->wasm_module; + WASMFunction *wasm_func = module->functions[func_index]; AOTBlock *aot_block; LLVMTypeRef int8_ptr_type; - LLVMValueRef aot_inst_offset = I32_TWO, aot_inst_addr; - char local_name[32]; uint64 size; - uint32 i, j = 0; /* Allocate memory for the function context */ - size = offsetof(AOTFuncContext, locals) + sizeof(LLVMValueRef) * - ((uint64)aot_func_type->param_count + func->local_count); - if (size >= UINT32_MAX - || !(func_ctx = wasm_malloc((uint32)size))) { + size = offsetof(AOTFuncContext, locals) + + sizeof(LLVMValueRef) + * ((uint64)aot_func_type->param_count + func->local_count); + if (size >= UINT32_MAX || !(func_ctx = wasm_runtime_malloc((uint32)size))) { aot_set_last_error("allocate memory failed."); return NULL; } @@ -458,91 +1875,53 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, memset(func_ctx, 0, (uint32)size); func_ctx->aot_func = func; + func_ctx->module = comp_ctx->module; + /* Add LLVM function */ - if (!(func_ctx->func = aot_add_llvm_func(comp_ctx, aot_func_type, func_index))) + if (!(func_ctx->func = aot_add_llvm_func( + comp_ctx, func_ctx->module, aot_func_type, func_index, + &func_ctx->func_type, &func_ctx->precheck_func))) { goto fail; + } /* Create function's first AOTBlock */ - if (!(aot_block = aot_create_func_block(comp_ctx, func_ctx, - func, aot_func_type))) + if (!(aot_block = + aot_create_func_block(comp_ctx, func_ctx, func, aot_func_type))) { goto fail; + } + +#if WASM_ENABLE_DEBUG_AOT != 0 + func_ctx->debug_func = dwarf_gen_func_info(comp_ctx, func_ctx); +#endif aot_block_stack_push(&func_ctx->block_stack, aot_block); /* Add local variables */ LLVMPositionBuilderAtEnd(comp_ctx->builder, aot_block->llvm_entry_block); - /* Save the pameters for fast access */ - func_ctx->exec_env = LLVMGetParam(func_ctx->func, j++); + if (!create_basic_func_context(comp_ctx, func_ctx)) { + goto fail; + } - /* Get aot inst address, the layout of exec_env is: - exec_env->next, exec_env->prev, and exec_env->module_inst */ - if (!(aot_inst_addr = - LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->exec_env, - &aot_inst_offset, 1, "aot_inst_addr"))) { - aot_set_last_error("llvm build in bounds gep failed"); + /* Get argv buffer address */ + if (wasm_func->has_op_func_call && !create_argv_buf(comp_ctx, func_ctx)) { goto fail; } - /* Load aot inst */ - if (!(func_ctx->aot_inst = LLVMBuildLoad(comp_ctx->builder, - aot_inst_addr, "aot_inst"))) { - aot_set_last_error("llvm build load failed"); + /* Get auxiliary stack info */ + if (wasm_func->has_op_set_global_aux_stack + && !create_aux_stack_info(comp_ctx, func_ctx)) { goto fail; } - for (i = 0; i < aot_func_type->param_count; i++, j++) { - snprintf(local_name, sizeof(local_name), "l%d", i); - func_ctx->locals[i] = - LLVMBuildAlloca(comp_ctx->builder, - TO_LLVM_TYPE(aot_func_type->types[i]), - local_name); - if (!func_ctx->locals[i]) { - aot_set_last_error("llvm build alloca failed."); - goto fail; - } - if (!LLVMBuildStore(comp_ctx->builder, - LLVMGetParam(func_ctx->func, j), - func_ctx->locals[i])) { - aot_set_last_error("llvm build store failed."); - goto fail; - } + if (comp_ctx->aux_stack_frame_type + && !create_aux_stack_frame(comp_ctx, func_ctx)) { + goto fail; } - for (i = 0; i < func->local_count; i++) { - LLVMTypeRef local_type; - LLVMValueRef local_value = NULL; - snprintf(local_name, sizeof(local_name), "l%d", - aot_func_type->param_count + i); - local_type = TO_LLVM_TYPE(func->local_types[i]); - func_ctx->locals[aot_func_type->param_count + i] = - LLVMBuildAlloca(comp_ctx->builder, local_type, local_name); - if (!func_ctx->locals[aot_func_type->param_count + i]) { - aot_set_last_error("llvm build alloca failed."); - goto fail; - } - switch (func->local_types[i]) { - case VALUE_TYPE_I32: - local_value = I32_ZERO; - break; - case VALUE_TYPE_I64: - local_value = I64_ZERO; - break; - case VALUE_TYPE_F32: - local_value = F32_ZERO; - break; - case VALUE_TYPE_F64: - local_value = F64_ZERO; - break; - default: - bh_assert(0); - break; - } - if (!LLVMBuildStore(comp_ctx->builder, local_value, - func_ctx->locals[aot_func_type->param_count + i])) { - aot_set_last_error("llvm build store failed."); - goto fail; - } + /* Create local variables */ + if (!create_local_variables(comp_data, comp_ctx, func_ctx, func)) { + goto fail; } if (!(int8_ptr_type = LLVMPointerType(INT8_PTR_TYPE, 0))) { @@ -550,69 +1929,87 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, goto fail; } - /* Create exception blocks */ - if (!create_exception_blocks(func_ctx)) - goto fail; - /* Create base addr, end addr, data size of mem, heap */ - if (!create_memory_info(comp_ctx, func_ctx, int8_ptr_type, func_index)) + if (wasm_func->has_memory_operations + && !create_memory_info(comp_ctx, func_ctx, int8_ptr_type, func_index)) { goto fail; + } - /* Load table base */ - if (!create_table_base(comp_ctx, func_ctx)) + /* Load current exception */ + if (!create_cur_exception(comp_ctx, func_ctx)) { goto fail; + } - /* Load current exception */ - if (!create_cur_exception(comp_ctx, func_ctx)) + /* Load function type indexes */ + if (wasm_func->has_op_call_indirect + && !create_func_type_indexes(comp_ctx, func_ctx)) { goto fail; + } /* Load function pointers */ - if (!create_func_ptrs(comp_ctx, func_ctx)) - goto fail; + if (!create_func_ptrs(comp_ctx, func_ctx)) { + goto fail; + } - /* Load function type indexes */ - if (!create_func_type_indexes(comp_ctx, func_ctx)) + /* Load shared heap, shared heap start off mem32 or mem64 */ + if ((comp_ctx->enable_shared_heap || comp_ctx->enable_shared_chain) + && !create_shared_heap_info(comp_ctx, func_ctx)) { goto fail; + } + +#if WASM_ENABLE_BRANCH_HINTS != 0 + func_ctx->function_hints = + comp_ctx->comp_data->function_hints + ? comp_ctx->comp_data->function_hints[func_index] + : NULL; +#endif return func_ctx; fail: - if (func_ctx->exception_blocks) - wasm_free(func_ctx->exception_blocks); - aot_block_stack_destroy(&func_ctx->block_stack); - wasm_free(func_ctx); + if (func_ctx->mem_info) + wasm_runtime_free(func_ctx->mem_info); + aot_block_stack_destroy(comp_ctx, &func_ctx->block_stack); + wasm_runtime_free(func_ctx); return NULL; } static void -aot_destroy_func_contexts(AOTFuncContext **func_ctxes, uint32 count) +aot_destroy_func_contexts(AOTCompContext *comp_ctx, AOTFuncContext **func_ctxes, + uint32 count) { uint32 i; for (i = 0; i < count; i++) if (func_ctxes[i]) { - if (func_ctxes[i]->exception_blocks) - wasm_free(func_ctxes[i]->exception_blocks); - aot_block_stack_destroy(&func_ctxes[i]->block_stack); - wasm_free(func_ctxes[i]); + if (func_ctxes[i]->mem_info) + wasm_runtime_free(func_ctxes[i]->mem_info); + aot_block_stack_destroy(comp_ctx, &func_ctxes[i]->block_stack); + aot_checked_addr_list_destroy(func_ctxes[i]); + wasm_runtime_free(func_ctxes[i]); } - wasm_free(func_ctxes); + wasm_runtime_free(func_ctxes); } /** * Create function compiler contexts */ static AOTFuncContext ** -aot_create_func_contexts(AOTCompData *comp_data, AOTCompContext *comp_ctx) +aot_create_func_contexts(const AOTCompData *comp_data, AOTCompContext *comp_ctx) { AOTFuncContext **func_ctxes; uint64 size; uint32 i; + if ((comp_ctx->enable_stack_bound_check + || comp_ctx->enable_stack_estimation) + && !aot_create_stack_sizes(comp_data, comp_ctx)) + return NULL; + /* Allocate memory */ - size = sizeof(AOTFuncContext*) * (uint64)comp_data->func_count; + size = sizeof(AOTFuncContext *) * (uint64)comp_data->func_count; if (size >= UINT32_MAX - || !(func_ctxes = wasm_malloc((uint32)size))) { + || !(func_ctxes = wasm_runtime_malloc((uint32)size))) { aot_set_last_error("allocate memory failed."); return NULL; } @@ -622,9 +2019,10 @@ aot_create_func_contexts(AOTCompData *comp_data, AOTCompContext *comp_ctx) /* Create each function context */ for (i = 0; i < comp_data->func_count; i++) { AOTFunc *func = comp_data->funcs[i]; - if (!(func_ctxes[i] = aot_create_func_context(comp_data, comp_ctx, - func, i))) { - aot_destroy_func_contexts(func_ctxes, comp_data->func_count); + if (!(func_ctxes[i] = + aot_create_func_context(comp_data, comp_ctx, func, i))) { + aot_destroy_func_contexts(comp_ctx, func_ctxes, + comp_data->func_count); return NULL; } } @@ -633,7 +2031,8 @@ aot_create_func_contexts(AOTCompData *comp_data, AOTCompContext *comp_ctx) } static bool -aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context) +aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context, + int pointer_size) { basic_types->int1_type = LLVMInt1TypeInContext(context); basic_types->int8_type = LLVMInt8TypeInContext(context); @@ -647,61 +2046,182 @@ aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context) basic_types->meta_data_type = LLVMMetadataTypeInContext(context); basic_types->int8_ptr_type = LLVMPointerType(basic_types->int8_type, 0); + + if (basic_types->int8_ptr_type) { + basic_types->int8_pptr_type = + LLVMPointerType(basic_types->int8_ptr_type, 0); + } + basic_types->int16_ptr_type = LLVMPointerType(basic_types->int16_type, 0); basic_types->int32_ptr_type = LLVMPointerType(basic_types->int32_type, 0); basic_types->int64_ptr_type = LLVMPointerType(basic_types->int64_type, 0); - basic_types->float32_ptr_type = LLVMPointerType(basic_types->float32_type, 0); - basic_types->float64_ptr_type = LLVMPointerType(basic_types->float64_type, 0); - basic_types->void_ptr_type = LLVMPointerType(basic_types->void_type, 0); - - return (basic_types->int8_ptr_type - && basic_types->int16_ptr_type - && basic_types->int32_ptr_type - && basic_types->int64_ptr_type - && basic_types->float32_ptr_type - && basic_types->float64_ptr_type - && basic_types->void_ptr_type - && basic_types->meta_data_type) ? true : false; + basic_types->float32_ptr_type = + LLVMPointerType(basic_types->float32_type, 0); + basic_types->float64_ptr_type = + LLVMPointerType(basic_types->float64_type, 0); + + basic_types->i8x16_vec_type = LLVMVectorType(basic_types->int8_type, 16); + basic_types->i16x8_vec_type = LLVMVectorType(basic_types->int16_type, 8); + basic_types->i32x4_vec_type = LLVMVectorType(basic_types->int32_type, 4); + basic_types->i64x2_vec_type = LLVMVectorType(basic_types->int64_type, 2); + basic_types->f32x4_vec_type = LLVMVectorType(basic_types->float32_type, 4); + basic_types->f64x2_vec_type = LLVMVectorType(basic_types->float64_type, 2); + + basic_types->v128_type = basic_types->i64x2_vec_type; + basic_types->v128_ptr_type = LLVMPointerType(basic_types->v128_type, 0); + + basic_types->int8_ptr_type_gs = + LLVMPointerType(basic_types->int8_type, 256); + basic_types->int16_ptr_type_gs = + LLVMPointerType(basic_types->int16_type, 256); + basic_types->int32_ptr_type_gs = + LLVMPointerType(basic_types->int32_type, 256); + basic_types->int64_ptr_type_gs = + LLVMPointerType(basic_types->int64_type, 256); + basic_types->float32_ptr_type_gs = + LLVMPointerType(basic_types->float32_type, 256); + basic_types->float64_ptr_type_gs = + LLVMPointerType(basic_types->float64_type, 256); + basic_types->v128_ptr_type_gs = + LLVMPointerType(basic_types->v128_type, 256); + if (!basic_types->int8_ptr_type_gs || !basic_types->int16_ptr_type_gs + || !basic_types->int32_ptr_type_gs || !basic_types->int64_ptr_type_gs + || !basic_types->float32_ptr_type_gs + || !basic_types->float64_ptr_type_gs + || !basic_types->v128_ptr_type_gs) { + return false; + } + + basic_types->i1x2_vec_type = LLVMVectorType(basic_types->int1_type, 2); + + basic_types->funcref_type = LLVMInt32TypeInContext(context); + basic_types->externref_type = LLVMInt32TypeInContext(context); + + if (pointer_size == 4) { + basic_types->intptr_t_type = basic_types->int32_type; + basic_types->intptr_t_ptr_type = basic_types->int32_ptr_type; + basic_types->size_t_type = basic_types->int32_type; + } + else { + basic_types->intptr_t_type = basic_types->int64_type; + basic_types->intptr_t_ptr_type = basic_types->int64_ptr_type; + basic_types->size_t_type = basic_types->int64_type; + } + + basic_types->gc_ref_type = basic_types->int8_ptr_type; + basic_types->gc_ref_ptr_type = basic_types->int8_pptr_type; + + return (basic_types->int8_ptr_type && basic_types->int8_pptr_type + && basic_types->int16_ptr_type && basic_types->int32_ptr_type + && basic_types->int64_ptr_type && basic_types->intptr_t_type + && basic_types->intptr_t_ptr_type && basic_types->float32_ptr_type + && basic_types->float64_ptr_type && basic_types->i8x16_vec_type + && basic_types->i16x8_vec_type && basic_types->i32x4_vec_type + && basic_types->i64x2_vec_type && basic_types->f32x4_vec_type + && basic_types->f64x2_vec_type && basic_types->i1x2_vec_type + && basic_types->meta_data_type && basic_types->funcref_type + && basic_types->externref_type && basic_types->gc_ref_type + && basic_types->gc_ref_ptr_type) + ? true + : false; } -static bool -aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) -{ - consts->i8_zero = I8_CONST(0); - consts->i32_zero = I32_CONST(0); - consts->i64_zero = I64_CONST(0); - consts->f32_zero = F32_CONST(0); - consts->f64_zero = F64_CONST(0); - consts->i32_one = I32_CONST(1); - consts->i32_two = I32_CONST(2); - consts->i32_four = I32_CONST(4); - consts->i32_eight = I32_CONST(8); - consts->i32_neg_one = I32_CONST((uint32)-1); - consts->i64_neg_one = I64_CONST((uint64)-1); - consts->i32_min = I32_CONST((uint32)INT32_MIN); - consts->i64_min = I64_CONST((uint64)INT64_MIN); - consts->i32_31 = I32_CONST(31); - consts->i32_32 = I32_CONST(32); - consts->i64_63 = I64_CONST(63); - consts->i64_64 = I64_CONST(64); - - return (consts->i8_zero - && consts->i32_zero - && consts->i64_zero - && consts->f32_zero - && consts->f64_zero - && consts->i32_one - && consts->i32_two - && consts->i32_four - && consts->i32_eight - && consts->i32_neg_one - && consts->i64_neg_one - && consts->i32_min - && consts->i64_min - && consts->i32_31 - && consts->i32_32 - && consts->i64_63 - && consts->i64_64) ? true : false; +static bool +aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) +{ +#define CREATE_I1_CONST(name, value) \ + if (!(consts->i1_##name = \ + LLVMConstInt(comp_ctx->basic_types.int1_type, value, true))) \ + return false; + + CREATE_I1_CONST(zero, 0) + CREATE_I1_CONST(one, 1) +#undef CREATE_I1_CONST + + if (!(consts->i8_zero = I8_CONST(0))) + return false; + + if (!(consts->i8_one = I8_CONST(1))) + return false; + + if (!(consts->f32_zero = F32_CONST(0))) + return false; + + if (!(consts->f64_zero = F64_CONST(0))) + return false; + +#define CREATE_I32_CONST(name, value) \ + if (!(consts->i32_##name = LLVMConstInt(I32_TYPE, value, true))) \ + return false; + + CREATE_I32_CONST(min, (uint32)INT32_MIN) + CREATE_I32_CONST(neg_one, (uint32)-1) + CREATE_I32_CONST(zero, 0) + CREATE_I32_CONST(one, 1) + CREATE_I32_CONST(two, 2) + CREATE_I32_CONST(three, 3) + CREATE_I32_CONST(four, 4) + CREATE_I32_CONST(five, 5) + CREATE_I32_CONST(six, 6) + CREATE_I32_CONST(seven, 7) + CREATE_I32_CONST(eight, 8) + CREATE_I32_CONST(nine, 9) + CREATE_I32_CONST(ten, 10) + CREATE_I32_CONST(eleven, 11) + CREATE_I32_CONST(twelve, 12) + CREATE_I32_CONST(thirteen, 13) + CREATE_I32_CONST(fourteen, 14) + CREATE_I32_CONST(fifteen, 15) + CREATE_I32_CONST(31, 31) + CREATE_I32_CONST(32, 32) +#undef CREATE_I32_CONST + +#define CREATE_I64_CONST(name, value) \ + if (!(consts->i64_##name = LLVMConstInt(I64_TYPE, value, true))) \ + return false; + + CREATE_I64_CONST(min, (uint64)INT64_MIN) + CREATE_I64_CONST(neg_one, (uint64)-1) + CREATE_I64_CONST(zero, 0) + CREATE_I64_CONST(63, 63) + CREATE_I64_CONST(64, 64) +#undef CREATE_I64_CONST + +#define CREATE_V128_CONST(name, type) \ + if (!(consts->name##_vec_zero = LLVMConstNull(type))) \ + return false; \ + if (!(consts->name##_undef = LLVMGetUndef(type))) \ + return false; + + CREATE_V128_CONST(i8x16, V128_i8x16_TYPE) + CREATE_V128_CONST(i16x8, V128_i16x8_TYPE) + CREATE_V128_CONST(i32x4, V128_i32x4_TYPE) + CREATE_V128_CONST(i64x2, V128_i64x2_TYPE) + CREATE_V128_CONST(f32x4, V128_f32x4_TYPE) + CREATE_V128_CONST(f64x2, V128_f64x2_TYPE) +#undef CREATE_V128_CONST + +#define CREATE_VEC_ZERO_MASK(slot) \ + { \ + LLVMTypeRef type = LLVMVectorType(I32_TYPE, slot); \ + if (!type || !(consts->i32x##slot##_zero = LLVMConstNull(type))) \ + return false; \ + } + + CREATE_VEC_ZERO_MASK(16) + CREATE_VEC_ZERO_MASK(8) + CREATE_VEC_ZERO_MASK(4) + CREATE_VEC_ZERO_MASK(2) +#undef CREATE_VEC_ZERO_MASK + + if (!(consts->gc_ref_null = + LLVMConstNull(comp_ctx->basic_types.gc_ref_type))) + return false; + if (!(consts->i8_ptr_null = + LLVMConstNull(comp_ctx->basic_types.int8_ptr_type))) + return false; + + return true; } typedef struct ArchItem { @@ -709,10 +2229,25 @@ typedef struct ArchItem { bool support_eb; } ArchItem; +/* clang-format off */ static ArchItem valid_archs[] = { { "x86_64", false }, { "i386", false }, + { "xtensa", false }, { "mips", true }, + { "mipsel", false }, + { "aarch64v8", false }, + { "aarch64v8.1", false }, + { "aarch64v8.2", false }, + { "aarch64v8.3", false }, + { "aarch64v8.4", false }, + { "aarch64v8.5", false }, + { "aarch64_bev8", false }, /* big endian */ + { "aarch64_bev8.1", false }, + { "aarch64_bev8.2", false }, + { "aarch64_bev8.3", false }, + { "aarch64_bev8.4", false }, + { "aarch64_bev8.5", false }, { "armv4", true }, { "armv4t", true }, { "armv5t", true }, @@ -748,36 +2283,68 @@ static ArchItem valid_archs[] = { { "thumbv8r", true }, { "thumbv8m.base", true }, { "thumbv8m.main", true }, - { "thumbv8.1m.main", true } + { "thumbv8.1m.main", true }, + { "riscv32", true }, + { "riscv64", true }, + { "arc", true } }; static const char *valid_abis[] = { "gnu", "eabi", - "gnueabihf" + "eabihf", + "gnueabihf", + "msvc", + "ilp32", + "ilp32f", + "ilp32d", + "lp64", + "lp64f", + "lp64d" }; +/* clang-format on */ static void print_supported_targets() { uint32 i; - bh_printf("Supported targets:\n"); - for (i = 0; i < sizeof(valid_archs) / sizeof(ArchItem); i++) { - bh_printf("%s ", valid_archs[i].arch); - if (valid_archs[i].support_eb) - bh_printf("%seb ", valid_archs[i].arch); + const char *target_name; + + os_printf("Supported targets:\n"); + /* over the list of all available targets */ + for (LLVMTargetRef target = LLVMGetFirstTarget(); target != NULL; + target = LLVMGetNextTarget(target)) { + target_name = LLVMGetTargetName(target); + /* Skip mipsel, aarch64_be since prefix mips, aarch64 will cover them */ + if (strcmp(target_name, "mipsel") == 0) + continue; + else if (strcmp(target_name, "aarch64_be") == 0) + continue; + + if (strcmp(target_name, "x86-64") == 0) + os_printf(" x86_64\n"); + else if (strcmp(target_name, "x86") == 0) + os_printf(" i386\n"); + else { + for (i = 0; i < sizeof(valid_archs) / sizeof(ArchItem); i++) { + /* If target_name is prefix for valid_archs[i].arch */ + if ((strncmp(target_name, valid_archs[i].arch, + strlen(target_name)) + == 0)) + os_printf(" %s\n", valid_archs[i].arch); + } + } } - bh_printf("\n"); } static void print_supported_abis() { uint32 i; - bh_printf("Supported ABI: "); + os_printf("Supported ABI: "); for (i = 0; i < sizeof(valid_abis) / sizeof(const char *); i++) - bh_printf("%s ", valid_abis[i]); - bh_printf("\n"); + os_printf("%s ", valid_abis[i]); + os_printf("\n"); } static bool @@ -792,8 +2359,9 @@ check_target_arch(const char *target_arch) support_eb = valid_archs[i].support_eb; if (!strncmp(target_arch, arch, strlen(arch)) - && ((support_eb && (!strcmp(target_arch + strlen(arch), "eb") - || !strcmp(target_arch + strlen(arch), ""))) + && ((support_eb + && (!strcmp(target_arch + strlen(arch), "eb") + || !strcmp(target_arch + strlen(arch), ""))) || (!support_eb && !strcmp(target_arch + strlen(arch), "")))) { return true; } @@ -812,7 +2380,6 @@ check_target_abi(const char *target_abi) return false; } - static void get_target_arch_from_triple(const char *triple, char *arch_buf, uint32 buf_size) { @@ -823,32 +2390,258 @@ get_target_arch_from_triple(const char *triple, char *arch_buf, uint32 buf_size) bh_assert(*triple == '-' || *triple == '\0'); } -void LLVMAddPromoteMemoryToRegisterPass(LLVMPassManagerRef PM); +static bool +is_baremetal_target(const char *target, const char *cpu, const char *abi) +{ + /* TODO: support more baremetal targets */ + if (target) { + /* If target is thumbxxx, then it is baremetal target */ + if (!strncmp(target, "thumb", strlen("thumb"))) + return true; + } + return false; +} -AOTCompContext * -aot_create_comp_context(AOTCompData *comp_data, - aot_comp_option_t option) +void +aot_handle_llvm_errmsg(const char *string, LLVMErrorRef err) { - AOTCompContext *comp_ctx, *ret = NULL; - /*LLVMTypeRef elem_types[8];*/ - struct LLVMMCJITCompilerOptions jit_options; - LLVMTargetRef target; - char *triple = NULL, *triple_norm, *arch, *abi, *cpu, *features, buf[128]; - char *triple_norm_new = NULL, *cpu_new = NULL; - char *err = NULL, *fp_round= "round.tonearest", *fp_exce = "fpexcept.strict"; - char triple_buf[32] = {0}; - uint32 opt_level, size_level; - LLVMCodeModel code_model; + char *err_msg = LLVMGetErrorMessage(err); + aot_set_last_error_v("%s: %s", string, err_msg); + LLVMDisposeErrorMessage(err_msg); +} + +static bool +create_target_machine_detect_host(AOTCompContext *comp_ctx) +{ + char *triple = NULL; + LLVMTargetRef target = NULL; + char *err_msg = NULL; + char *cpu = NULL; + char *features = NULL; + LLVMTargetMachineRef target_machine = NULL; + bool ret = false; + + triple = LLVMGetDefaultTargetTriple(); + if (triple == NULL) { + aot_set_last_error("failed to get default target triple."); + goto fail; + } + + if (LLVMGetTargetFromTriple(triple, &target, &err_msg) != 0) { + aot_set_last_error_v("failed to get llvm target from triple %s.", + err_msg); + LLVMDisposeMessage(err_msg); + goto fail; + } + + if (!LLVMTargetHasJIT(target)) { + aot_set_last_error("unsupported JIT on this platform."); + goto fail; + } + + cpu = LLVMGetHostCPUName(); + if (cpu == NULL) { + aot_set_last_error("failed to get host cpu information."); + goto fail; + } + + features = LLVMGetHostCPUFeatures(); + if (features == NULL) { + aot_set_last_error("failed to get host cpu features."); + goto fail; + } + + LOG_VERBOSE("LLVM ORCJIT detected CPU \"%s\", with features \"%s\"\n", cpu, + features); + + /* create TargetMachine */ + target_machine = LLVMCreateTargetMachine( + target, triple, cpu, features, LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelJITDefault); + if (!target_machine) { + aot_set_last_error("failed to create target machine."); + goto fail; + } + comp_ctx->target_machine = target_machine; + + /* Save target arch */ + get_target_arch_from_triple(triple, comp_ctx->target_arch, + sizeof(comp_ctx->target_arch)); + ret = true; + +fail: + if (triple) + LLVMDisposeMessage(triple); + if (features) + LLVMDisposeMessage(features); + if (cpu) + LLVMDisposeMessage(cpu); + + return ret; +} + +static void +jit_stack_size_callback(void *user_data, const char *name, size_t namelen, + size_t stack_size) +{ + AOTCompContext *comp_ctx = user_data; + /* + * Note: the longest name we care is + * something like "aot_func_internal#4294967295". + */ + char buf[64]; + uint32 func_idx; + const AOTFuncContext *func_ctx; + bool musttail; + unsigned int stack_consumption_to_call_wrapped_func; + unsigned int call_size; + int ret; + + bh_assert(comp_ctx != NULL); + bh_assert(comp_ctx->jit_stack_sizes != NULL); + + if (namelen >= sizeof(buf)) { + LOG_DEBUG("too long name: %.*s", (int)namelen, name); + return; + } + /* ensure NUL termination */ + bh_memcpy_s(buf, (uint32)sizeof(buf), name, (uint32)namelen); + buf[namelen] = 0; + + ret = sscanf(buf, AOT_FUNC_INTERNAL_PREFIX "%" SCNu32, &func_idx); + if (ret != 1) { + return; + } + + bh_assert(func_idx < comp_ctx->func_ctx_count); + func_ctx = comp_ctx->func_ctxes[func_idx]; + call_size = func_ctx->stack_consumption_for_func_call; + musttail = aot_target_precheck_can_use_musttail(comp_ctx); + stack_consumption_to_call_wrapped_func = + musttail ? 0 + : aot_estimate_stack_usage_for_function_call( + comp_ctx, func_ctx->aot_func->func_type); + LOG_VERBOSE("func %.*s stack %u + %zu + %u", (int)namelen, name, + stack_consumption_to_call_wrapped_func, stack_size, call_size); + + /* Note: -1 == AOT_NEG_ONE from aot_create_stack_sizes */ + bh_assert(comp_ctx->jit_stack_sizes[func_idx] == (uint32)-1); + comp_ctx->jit_stack_sizes[func_idx] = (uint32)stack_size + call_size; +} + +static bool +orc_jit_create(AOTCompContext *comp_ctx) +{ + LLVMErrorRef err; + LLVMOrcLLLazyJITRef orc_jit = NULL; + LLVMOrcLLLazyJITBuilderRef builder = NULL; + LLVMOrcJITTargetMachineBuilderRef jtmb = NULL; + bool ret = false; + + builder = LLVMOrcCreateLLLazyJITBuilder(); + if (builder == NULL) { + aot_set_last_error("failed to create jit builder."); + goto fail; + } + if (comp_ctx->enable_stack_bound_check || comp_ctx->enable_stack_estimation) + LLVMOrcLLJITBuilderSetCompileFunctionCreatorWithStackSizesCallback( + builder, jit_stack_size_callback, comp_ctx); + + err = LLVMOrcJITTargetMachineBuilderDetectHost(&jtmb); + if (err != LLVMErrorSuccess) { + aot_handle_llvm_errmsg( + "quited to create LLVMOrcJITTargetMachineBuilderRef", err); + goto fail; + } + + LLVMOrcLLLazyJITBuilderSetNumCompileThreads( + builder, WASM_ORC_JIT_COMPILE_THREAD_NUM); + + /* Ownership transfer: + LLVMOrcJITTargetMachineBuilderRef -> LLVMOrcLLJITBuilderRef */ + LLVMOrcLLLazyJITBuilderSetJITTargetMachineBuilder(builder, jtmb); + err = LLVMOrcCreateLLLazyJIT(&orc_jit, builder); + if (err != LLVMErrorSuccess) { + aot_handle_llvm_errmsg("quited to create llvm lazy orcjit instance", + err); + goto fail; + } + /* Ownership transfer: LLVMOrcLLJITBuilderRef -> LLVMOrcLLJITRef */ + builder = NULL; + +#if WASM_ENABLE_LINUX_PERF != 0 + if (wasm_runtime_get_linux_perf()) { + LOG_DEBUG("Enable linux perf support in JIT"); + LLVMOrcObjectLayerRef obj_linking_layer = + (LLVMOrcObjectLayerRef)LLVMOrcLLLazyJITGetObjLinkingLayer(orc_jit); + LLVMOrcRTDyldObjectLinkingLayerRegisterJITEventListener( + obj_linking_layer, LLVMCreatePerfJITEventListener()); + } +#endif + + /* Ownership transfer: local -> AOTCompContext */ + comp_ctx->orc_jit = orc_jit; + orc_jit = NULL; + ret = true; + +fail: + if (builder) + LLVMOrcDisposeLLLazyJITBuilder(builder); + + if (orc_jit) + LLVMOrcDisposeLLLazyJIT(orc_jit); + return ret; +} + +bool +aot_compiler_init(void) +{ /* Initialize LLVM environment */ +#if LLVM_VERSION_MAJOR < 17 + LLVMInitializeCore(LLVMGetGlobalPassRegistry()); +#endif + +/* fuzzing only use host targets for simple */ +#if WASM_ENABLE_WAMR_COMPILER != 0 && WASM_ENABLE_FUZZ_TEST == 0 + /* Init environment of all targets for AOT compiler */ LLVMInitializeAllTargetInfos(); LLVMInitializeAllTargets(); LLVMInitializeAllTargetMCs(); LLVMInitializeAllAsmPrinters(); - LLVMLinkInMCJIT(); +#else + /* Init environment of native for JIT compiler */ + LLVMInitializeNativeTarget(); + LLVMInitializeNativeTarget(); + LLVMInitializeNativeAsmPrinter(); +#endif + + return true; +} + +void +aot_compiler_destroy(void) +{ + LLVMShutdown(); +} + +AOTCompContext * +aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option) +{ + AOTCompContext *comp_ctx, *ret = NULL; + LLVMTargetRef target; + char *triple = NULL, *triple_norm, *arch, *abi; + char *cpu = NULL, *features, buf[128]; + char *triple_norm_new = NULL, *cpu_new = NULL; + char *err = NULL, *fp_round = "round.tonearest", + *fp_exce = "fpexcept.strict"; + char triple_buf[128] = { 0 }, features_buf[128] = { 0 }; + uint32 opt_level, size_level, i; + LLVMCodeModel code_model; + LLVMTargetDataRef target_data_ref; /* Allocate memory */ - if (!(comp_ctx = wasm_malloc(sizeof(AOTCompContext)))) { + if (!(comp_ctx = wasm_runtime_malloc(sizeof(AOTCompContext)))) { aot_set_last_error("allocate memory failed."); return NULL; } @@ -857,8 +2650,18 @@ aot_create_comp_context(AOTCompData *comp_data, comp_ctx->comp_data = comp_data; /* Create LLVM context, module and builder */ - if (!(comp_ctx->context = LLVMContextCreate())) { - aot_set_last_error("create LLVM context failed."); + comp_ctx->orc_thread_safe_context = LLVMOrcCreateNewThreadSafeContext(); + if (!comp_ctx->orc_thread_safe_context) { + aot_set_last_error("create LLVM ThreadSafeContext failed."); + goto fail; + } + + /* Get a reference to the underlying LLVMContext, note: + different from non LAZY JIT mode, no need to dispose this context, + if will be disposed when the thread safe context is disposed */ + if (!(comp_ctx->context = LLVMOrcThreadSafeContextGetContext( + comp_ctx->orc_thread_safe_context))) { + aot_set_last_error("get context from LLVM ThreadSafeContext failed."); goto fail; } @@ -867,38 +2670,227 @@ aot_create_comp_context(AOTCompData *comp_data, goto fail; } - if (!(comp_ctx->module = - LLVMModuleCreateWithNameInContext("WASM Module", comp_ctx->context))) { + /* Create LLVM module for each jit function, note: + different from non ORC JIT mode, no need to dispose it, + it will be disposed when the thread safe context is disposed */ + if (!(comp_ctx->module = LLVMModuleCreateWithNameInContext( + "WASM Module", comp_ctx->context))) { aot_set_last_error("create LLVM module failed."); goto fail; } +#if LLVM_VERSION_MAJOR >= 19 + LLVMSetIsNewDbgInfoFormat(comp_ctx->module, true); +#endif + +#if WASM_ENABLE_LINUX_PERF != 0 + if (wasm_runtime_get_linux_perf()) { + /* FramePointerKind.All */ + LLVMMetadataRef val = + LLVMValueAsMetadata(LLVMConstInt(LLVMInt32Type(), 2, false)); + const char *key = "frame-pointer"; + LLVMAddModuleFlag(comp_ctx->module, LLVMModuleFlagBehaviorWarning, key, + strlen(key), val); + + comp_ctx->emit_frame_pointer = true; + } +#endif + + if (BH_LIST_ERROR == bh_list_init(&comp_ctx->native_symbols)) { + goto fail; + } + +#if WASM_ENABLE_DEBUG_AOT != 0 + if (!(comp_ctx->debug_builder = LLVMCreateDIBuilder(comp_ctx->module))) { + aot_set_last_error("create LLVM Debug Infor builder failed."); + goto fail; + } + + LLVMAddModuleFlag( + comp_ctx->module, LLVMModuleFlagBehaviorWarning, "Debug Info Version", + strlen("Debug Info Version"), + LLVMValueAsMetadata(LLVMConstInt(LLVMInt32Type(), 3, false))); + + comp_ctx->debug_file = dwarf_gen_file_info(comp_ctx); + if (!comp_ctx->debug_file) { + aot_set_last_error("dwarf generate file info failed"); + goto fail; + } + comp_ctx->debug_comp_unit = dwarf_gen_comp_unit_info(comp_ctx); + if (!comp_ctx->debug_comp_unit) { + aot_set_last_error("dwarf generate compile unit info failed"); + goto fail; + } +#endif + + if (option->enable_bulk_memory) + comp_ctx->enable_bulk_memory = true; + + if (option->enable_bulk_memory_opt) + comp_ctx->enable_bulk_memory_opt = true; + + if (option->enable_thread_mgr) + comp_ctx->enable_thread_mgr = true; + + if (option->enable_tail_call) + comp_ctx->enable_tail_call = true; + + if (option->enable_ref_types) + comp_ctx->enable_ref_types = true; + + if (option->enable_call_indirect_overlong) + comp_ctx->enable_call_indirect_overlong = true; + + comp_ctx->aux_stack_frame_type = option->aux_stack_frame_type; + comp_ctx->call_stack_features = option->call_stack_features; + + if (option->enable_perf_profiling) + comp_ctx->enable_perf_profiling = true; + + if (option->enable_memory_profiling) + comp_ctx->enable_memory_profiling = true; + + if (option->enable_aux_stack_check) + comp_ctx->enable_aux_stack_check = true; + + if (option->is_indirect_mode) { + comp_ctx->is_indirect_mode = true; + /* avoid LUT relocations ("switch-table") */ + comp_ctx->disable_llvm_jump_tables = true; + } + + if (option->disable_llvm_intrinsics) + comp_ctx->disable_llvm_intrinsics = true; + + if (option->disable_llvm_jump_tables) + comp_ctx->disable_llvm_jump_tables = true; + + if (option->disable_llvm_lto) + comp_ctx->disable_llvm_lto = true; + + if (option->enable_llvm_pgo) + comp_ctx->enable_llvm_pgo = true; + + if (option->use_prof_file) + comp_ctx->use_prof_file = option->use_prof_file; + + if (option->enable_stack_estimation) + comp_ctx->enable_stack_estimation = true; + + if (option->quick_invoke_c_api_import) + comp_ctx->quick_invoke_c_api_import = true; + + if (option->llvm_passes) + comp_ctx->llvm_passes = option->llvm_passes; + + if (option->builtin_intrinsics) + comp_ctx->builtin_intrinsics = option->builtin_intrinsics; + + if (option->enable_gc) + comp_ctx->enable_gc = true; + + if (option->enable_shared_heap) + comp_ctx->enable_shared_heap = true; + + if (option->enable_shared_chain) + comp_ctx->enable_shared_chain = true; + + if (option->enable_extended_const) + comp_ctx->enable_extended_const = true; + + comp_ctx->opt_level = option->opt_level; + comp_ctx->size_level = option->size_level; + + comp_ctx->custom_sections_wp = option->custom_sections; + comp_ctx->custom_sections_count = option->custom_sections_count; if (option->is_jit_mode) { - /* Create LLVM execution engine */ - LLVMInitializeMCJITCompilerOptions(&jit_options, sizeof(jit_options)); - jit_options.OptLevel = LLVMCodeGenLevelAggressive; - /*jit_options.CodeModel = LLVMCodeModelSmall;*/ - if (LLVMCreateMCJITCompilerForModule - (&comp_ctx->exec_engine, comp_ctx->module, - &jit_options, sizeof(jit_options), &err) != 0) { - if (err) { - LLVMDisposeMessage(err); - err = NULL; - } - aot_set_last_error("create LLVM JIT compiler failed."); - goto fail; - } comp_ctx->is_jit_mode = true; + +#ifndef OS_ENABLE_HW_BOUND_CHECK + comp_ctx->enable_bound_check = true; + /* Always enable stack boundary check if `bounds-checks` + is enabled */ + comp_ctx->enable_stack_bound_check = true; +#else + comp_ctx->enable_bound_check = false; + /* When `bounds-checks` is disabled, we set stack boundary + check status according to the compilation option */ +#if WASM_DISABLE_STACK_HW_BOUND_CHECK != 0 + /* Native stack overflow check with hardware trap is disabled, + we need to enable the check by LLVM JITed/AOTed code */ + comp_ctx->enable_stack_bound_check = true; +#else + /* Native stack overflow check with hardware trap is enabled, + no need to enable the check by LLVM JITed/AOTed code */ + comp_ctx->enable_stack_bound_check = false; +#endif +#endif + + /* Create TargetMachine */ + if (!create_target_machine_detect_host(comp_ctx)) + goto fail; + + /* Create LLJIT Instance */ + if (!orc_jit_create(comp_ctx)) + goto fail; } else { /* Create LLVM target machine */ - arch = option->target_arch; - abi = option->target_abi; - cpu = option->target_cpu; - features = option->cpu_features; + if (!option->target_arch || !strstr(option->target_arch, "-")) { + /* Retrieve the target triple based on user input */ + triple = NULL; + arch = option->target_arch; + abi = option->target_abi; + cpu = option->target_cpu; + features = option->cpu_features; + } + else { + /* Form a target triple */ + triple = option->target_arch; + arch = NULL; + abi = NULL; + cpu = NULL; + features = NULL; + } + opt_level = option->opt_level; size_level = option->size_level; + /* verify external llc compiler */ + comp_ctx->external_llc_compiler = getenv("WAMRC_LLC_COMPILER"); + if (comp_ctx->external_llc_compiler) { + if (access(comp_ctx->external_llc_compiler, X_OK) != 0) { + LOG_WARNING("WAMRC_LLC_COMPILER [%s] not found, fallback to " + "default pipeline", + comp_ctx->external_llc_compiler); + comp_ctx->external_llc_compiler = NULL; + } + else { + comp_ctx->llc_compiler_flags = getenv("WAMRC_LLC_FLAGS"); + LOG_VERBOSE("Using external LLC compiler [%s]", + comp_ctx->external_llc_compiler); + } + } + + /* verify external asm compiler */ + if (!comp_ctx->external_llc_compiler) { + comp_ctx->external_asm_compiler = getenv("WAMRC_ASM_COMPILER"); + if (comp_ctx->external_asm_compiler) { + if (access(comp_ctx->external_asm_compiler, X_OK) != 0) { + LOG_WARNING( + "WAMRC_ASM_COMPILER [%s] not found, fallback to " + "default pipeline", + comp_ctx->external_asm_compiler); + comp_ctx->external_asm_compiler = NULL; + } + else { + comp_ctx->asm_compiler_flags = getenv("WAMRC_ASM_FLAGS"); + LOG_VERBOSE("Using external ASM compiler [%s]", + comp_ctx->external_asm_compiler); + } + } + } + if (arch) { /* Add default sub-arch if not specified */ if (!strcmp(arch, "arm")) @@ -909,6 +2901,10 @@ aot_create_comp_context(AOTCompData *comp_data, arch = "thumbv4t"; else if (!strcmp(arch, "thumbeb")) arch = "thumbv4teb"; + else if (!strcmp(arch, "aarch64")) + arch = "aarch64v8"; + else if (!strcmp(arch, "aarch64_be")) + arch = "aarch64_bev8"; } /* Check target arch */ @@ -916,8 +2912,9 @@ aot_create_comp_context(AOTCompData *comp_data, if (!strcmp(arch, "help")) print_supported_targets(); else - aot_set_last_error("Invalid target. " - "Use --target=help to list all supported targets"); + aot_set_last_error( + "Invalid target. " + "Use --target=help to list all supported targets"); goto fail; } @@ -926,20 +2923,158 @@ aot_create_comp_context(AOTCompData *comp_data, if (!strcmp(abi, "help")) print_supported_abis(); else - aot_set_last_error("Invalid target ABI. " - "Use --target-abi=help to list all supported ABI"); + aot_set_last_error( + "Invalid target ABI. " + "Use --target-abi=help to list all supported ABI"); goto fail; } - if (arch) { + /* Set default abi for riscv target */ + if (arch && !strncmp(arch, "riscv", 5) && !abi) { + if (!strcmp(arch, "riscv64")) + abi = "lp64d"; + else + abi = "ilp32d"; + } + +#if defined(__APPLE__) || defined(__MACH__) + if (!abi) { + /* On MacOS platform, set abi to "gnu" to avoid generating + object file of Mach-O binary format which is unsupported */ + abi = "gnu"; + if (!arch && !cpu && !features) { + /* Get CPU name of the host machine to avoid checking + SIMD capability failed */ + if (!(cpu = cpu_new = LLVMGetHostCPUName())) { + aot_set_last_error("llvm get host cpu name failed."); + goto fail; + } + } + } +#endif + + if (abi) { + /* Construct target triple: --- */ + const char *vendor_sys; + char *arch1 = arch, default_arch[32] = { 0 }; + + if (!arch1) { + char *default_triple = LLVMGetDefaultTargetTriple(); + + if (!default_triple) { + aot_set_last_error( + "llvm get default target triple failed."); + goto fail; + } + + vendor_sys = strstr(default_triple, "-"); + bh_assert(vendor_sys); + bh_memcpy_s(default_arch, sizeof(default_arch), default_triple, + (uint32)(vendor_sys - default_triple)); + /** + * On Mac M[1-9]+ LLVM will report arm64 as the + * architecture, for the purposes of wamr this is the + * same as aarch64v8 so we'll normalize it here. + */ + if (!strcmp(default_arch, "arm64")) { + bh_strcpy_s(default_arch, sizeof(default_arch), + "aarch64v8"); + } + arch1 = default_arch; + + LLVMDisposeMessage(default_triple); + } + + /** + * Set - according to abi to generate the object file + * with the correct file format which might be different from the + * default object file format of the host, e.g., generating AOT file + * for Windows/MacOS under Linux host, or generating AOT file for + * Linux/MacOS under Windows host. + */ + + if (!strcmp(abi, "msvc")) { + if (!strcmp(arch1, "i386")) + vendor_sys = "-pc-win32-"; + else + vendor_sys = "-pc-windows-"; + } + else if (!strcmp(abi, "darwin") || !strcmp(abi, "macho")) { + /* macOS/Darwin: x18 is reserved by Apple */ + vendor_sys = "-apple-"; + } + else { + if (is_baremetal_target(arch, cpu, abi)) + vendor_sys = "-unknown-none-"; + else + vendor_sys = "-pc-linux-"; + } + + bh_assert(strlen(arch1) + strlen(vendor_sys) + strlen(abi) + < sizeof(triple_buf)); + bh_memcpy_s(triple_buf, (uint32)sizeof(triple_buf), arch1, + (uint32)strlen(arch1)); + bh_memcpy_s(triple_buf + strlen(arch1), + (uint32)(sizeof(triple_buf) - strlen(arch1)), + vendor_sys, (uint32)strlen(vendor_sys)); + bh_memcpy_s(triple_buf + strlen(arch1) + strlen(vendor_sys), + (uint32)(sizeof(triple_buf) - strlen(arch1) + - strlen(vendor_sys)), + abi, (uint32)strlen(abi)); + triple = triple_buf; + } + else if (arch) { /* Construct target triple: --- */ - const char *vendor_sys = "-pc-linux-"; - if (!abi) - abi = "gnu"; - bh_assert(strlen(arch) + strlen(vendor_sys) + strlen(abi) < sizeof(triple_buf)); - memcpy(triple_buf, arch, strlen(arch)); - memcpy(triple_buf + strlen(arch), vendor_sys, strlen(vendor_sys)); - memcpy(triple_buf + strlen(arch) + strlen(vendor_sys), abi, strlen(abi)); + const char *vendor_sys; + char *default_triple = LLVMGetDefaultTargetTriple(); + + if (!default_triple) { + aot_set_last_error("llvm get default target triple failed."); + goto fail; + } + + if (strstr(default_triple, "windows")) { + vendor_sys = "-pc-windows-"; + if (!abi) + abi = "msvc"; + } + else if (strstr(default_triple, "win32")) { + vendor_sys = "-pc-win32-"; + if (!abi) + abi = "msvc"; + } + else if (is_baremetal_target(arch, cpu, abi)) { + vendor_sys = "-unknown-none-"; + if (!abi) + abi = "gnu"; + } + else if (strstr(default_triple, "darwin") + || strstr(default_triple, "apple")) { + /* macOS/Darwin: x18 is reserved by Apple, must use correct + * triple to prevent LLVM from using it */ + vendor_sys = "-apple-darwin"; + if (!abi) + abi = ""; + } + else { + vendor_sys = "-pc-linux-"; + if (!abi) + abi = "gnu"; + } + + LLVMDisposeMessage(default_triple); + + bh_assert(strlen(arch) + strlen(vendor_sys) + strlen(abi) + < sizeof(triple_buf)); + bh_memcpy_s(triple_buf, (uint32)sizeof(triple_buf), arch, + (uint32)strlen(arch)); + bh_memcpy_s(triple_buf + strlen(arch), + (uint32)(sizeof(triple_buf) - strlen(arch)), vendor_sys, + (uint32)strlen(vendor_sys)); + bh_memcpy_s(triple_buf + strlen(arch) + strlen(vendor_sys), + (uint32)(sizeof(triple_buf) - strlen(arch) + - strlen(vendor_sys)), + abi, (uint32)strlen(abi)); triple = triple_buf; } @@ -950,7 +3085,8 @@ aot_create_comp_context(AOTCompData *comp_data, if (!triple && !cpu) { /* Get a triple for the host machine */ - if (!(triple_norm = triple_norm_new = LLVMGetDefaultTargetTriple())) { + if (!(triple_norm = triple_norm_new = + LLVMGetDefaultTargetTriple())) { aot_set_last_error("llvm get default target triple failed."); goto fail; } @@ -962,25 +3098,77 @@ aot_create_comp_context(AOTCompData *comp_data, } else if (triple) { /* Normalize a target triple */ - if (!(triple_norm = triple_norm_new = LLVMNormalizeTargetTriple(triple))) { + if (!(triple_norm = triple_norm_new = + LLVMNormalizeTargetTriple(triple))) { snprintf(buf, sizeof(buf), "llvm normlalize target triple (%s) failed.", triple); aot_set_last_error(buf); goto fail; } + LOG_VERBOSE("triple: %s => normailized: %s", triple, triple_norm); if (!cpu) cpu = ""; } - else { /* triple is NULL, cpu isn't NULL */ - snprintf(buf, sizeof(buf), - "target isn't specified for cpu %s.", cpu); + else { + /* triple is NULL, cpu isn't NULL */ + snprintf(buf, sizeof(buf), "target isn't specified for cpu %s.", + cpu); aot_set_last_error(buf); goto fail; } + /* Add module flag and cpu feature for riscv target */ + if (arch && !strncmp(arch, "riscv", 5)) { + LLVMMetadataRef meta_target_abi; + + if (!(meta_target_abi = LLVMMDStringInContext2(comp_ctx->context, + abi, strlen(abi)))) { + aot_set_last_error("create metadata string failed."); + goto fail; + } + LLVMAddModuleFlag(comp_ctx->module, LLVMModuleFlagBehaviorError, + "target-abi", strlen("target-abi"), + meta_target_abi); + + if (!strcmp(abi, "lp64d") || !strcmp(abi, "ilp32d")) { + if (features && !strstr(features, "+d")) { + snprintf(features_buf, sizeof(features_buf), "%s%s", + features, ",+d"); + features = features_buf; + } + else if (!features) { + features = "+d"; + } + } + } + if (!features) features = ""; +#if (defined(__APPLE__) || defined(__MACH__)) && defined(BUILD_TARGET_AARCH64) + /* On macOS ARM64, x18 is reserved by Apple for TLS. Even though we're + * generating ELF (Linux-style) AOT files, we must tell LLVM to not + * use x18, otherwise the AOT code will crash when running on macOS. */ + { + bool is_aarch64 = false; + if (arch && !strncmp(arch, "aarch64", 7)) + is_aarch64 = true; + else if (triple_norm && strstr(triple_norm, "aarch64")) + is_aarch64 = true; + + if (is_aarch64) { + if (features[0] != '\0') { + snprintf(features_buf, sizeof(features_buf), + "%s,+reserve-x18", features); + features = features_buf; + } + else { + features = "+reserve-x18"; + } + } + } +#endif + /* Get target with triple, note that LLVMGetTargetFromTriple() return 0 when success, but not true. */ if (LLVMGetTargetFromTriple(triple_norm, &target, &err) != 0) { @@ -998,27 +3186,71 @@ aot_create_comp_context(AOTCompData *comp_data, get_target_arch_from_triple(triple_norm, comp_ctx->target_arch, sizeof(comp_ctx->target_arch)); - bh_printf("Create AoT compiler with:\n"); - bh_printf(" target: %s\n", comp_ctx->target_arch); - bh_printf(" target cpu: %s\n", cpu); - bh_printf(" cpu features: %s\n", features); - bh_printf(" opt level: %d\n", opt_level); - bh_printf(" size level: %d\n", size_level); + if (option->bounds_checks == 1 || option->bounds_checks == 0) { + /* Set by the user */ + comp_ctx->enable_bound_check = + (option->bounds_checks == 1) ? true : false; + } + else { + /* Unset by the user, use the default value */ + if (strstr(comp_ctx->target_arch, "64") + && !option->is_sgx_platform) { + comp_ctx->enable_bound_check = false; + } + else { + comp_ctx->enable_bound_check = true; + } + } + + if (option->stack_bounds_checks == 1 + || option->stack_bounds_checks == 0) { + /* Set by the user */ + comp_ctx->enable_stack_bound_check = + (option->stack_bounds_checks == 1) ? true : false; + } + else { + /* Unset by the user, use the default value, it will be the same + * value as the bound check */ + comp_ctx->enable_stack_bound_check = comp_ctx->enable_bound_check; + } + + if ((comp_ctx->enable_stack_bound_check + || comp_ctx->enable_stack_estimation) + && option->stack_usage_file == NULL) { + if (!aot_generate_tempfile_name( + "wamrc-su", "su", comp_ctx->stack_usage_temp_file, + sizeof(comp_ctx->stack_usage_temp_file))) + goto fail; + comp_ctx->stack_usage_file = comp_ctx->stack_usage_temp_file; + } + else { + comp_ctx->stack_usage_file = option->stack_usage_file; + } + + os_printf("Create AoT compiler with:\n"); + os_printf(" target: %s\n", comp_ctx->target_arch); + os_printf(" target cpu: %s\n", cpu); + os_printf(" target triple: %s\n", triple_norm); + os_printf(" cpu features: %s\n", features); + os_printf(" opt level: %d\n", opt_level); + os_printf(" size level: %d\n", size_level); switch (option->output_format) { case AOT_LLVMIR_UNOPT_FILE: - bh_printf(" output format: unoptimized LLVM IR\n"); + os_printf(" output format: unoptimized LLVM IR\n"); break; case AOT_LLVMIR_OPT_FILE: - bh_printf(" output format: optimized LLVM IR\n"); + os_printf(" output format: optimized LLVM IR\n"); break; case AOT_FORMAT_FILE: - bh_printf(" output format: AoT file\n"); + os_printf(" output format: AoT file\n"); break; case AOT_OBJECT_FILE: - bh_printf(" output format: native object file\n"); + os_printf(" output format: native object file\n"); break; } + LLVMSetTarget(comp_ctx->module, triple_norm); + if (!LLVMTargetHasTargetMachine(target)) { snprintf(buf, sizeof(buf), "no target machine for this target (%s).", triple_norm); @@ -1026,9 +3258,15 @@ aot_create_comp_context(AOTCompData *comp_data, goto fail; } - if (!LLVMTargetHasAsmBackend(target)) { - snprintf(buf, sizeof(buf), - "no asm backend for this target (%s).", LLVMGetTargetName(target)); + /* Report error if target isn't arc and hasn't asm backend. + For arc target, as it cannot emit to memory buffer of elf file + currently, we let it emit to assembly file instead, and then call + arc-gcc to compile + asm file to elf file, and read elf file to memory buffer. */ + if (strncmp(comp_ctx->target_arch, "arc", 3) + && !LLVMTargetHasAsmBackend(target)) { + snprintf(buf, sizeof(buf), "no asm backend for this target (%s).", + LLVMGetTargetName(target)); aot_set_last_error(buf); goto fail; } @@ -1044,44 +3282,161 @@ aot_create_comp_context(AOTCompData *comp_data, code_model = LLVMCodeModelSmall; /* Create the target machine */ - if (!(comp_ctx->target_machine = - LLVMCreateTargetMachine(target, triple_norm, cpu, features, - opt_level, LLVMRelocStatic, - code_model))) { + if (!(comp_ctx->target_machine = LLVMCreateTargetMachineWithOpts( + target, triple_norm, cpu, features, opt_level, + LLVMRelocStatic, code_model, false, + comp_ctx->stack_usage_file))) { aot_set_last_error("create LLVM target machine failed."); goto fail; } + + /* If only to create target machine for querying information, early stop + */ + if ((arch && !strcmp(arch, "help")) || (abi && !strcmp(abi, "help")) + || (cpu && !strcmp(cpu, "help")) + || (features && !strcmp(features, "+help"))) { + LOG_DEBUG( + "create LLVM target machine only for printing help info."); + goto fail; + } } - comp_ctx->optimize = true; - if (option->output_format == AOT_LLVMIR_UNOPT_FILE) - comp_ctx->optimize = false; + triple = LLVMGetTargetMachineTriple(comp_ctx->target_machine); + if (!triple) { + aot_set_last_error("get target machine triple failed."); + goto fail; + } + if (strstr(triple, "linux") && !strcmp(comp_ctx->target_arch, "x86_64")) { + if (option->segue_flags) { + if (option->segue_flags & (1 << 0)) + comp_ctx->enable_segue_i32_load = true; + if (option->segue_flags & (1 << 1)) + comp_ctx->enable_segue_i64_load = true; + if (option->segue_flags & (1 << 2)) + comp_ctx->enable_segue_f32_load = true; + if (option->segue_flags & (1 << 3)) + comp_ctx->enable_segue_f64_load = true; + if (option->segue_flags & (1 << 4)) + comp_ctx->enable_segue_v128_load = true; + if (option->segue_flags & (1 << 8)) + comp_ctx->enable_segue_i32_store = true; + if (option->segue_flags & (1 << 9)) + comp_ctx->enable_segue_i64_store = true; + if (option->segue_flags & (1 << 10)) + comp_ctx->enable_segue_f32_store = true; + if (option->segue_flags & (1 << 11)) + comp_ctx->enable_segue_f64_store = true; + if (option->segue_flags & (1 << 12)) + comp_ctx->enable_segue_v128_store = true; + } + } + LLVMDisposeMessage(triple); + +#if WASM_ENABLE_WAMR_COMPILER != 0 + WASMModule *wasm_module = (WASMModule *)comp_data->wasm_module; + bool is_memory64 = false; + + /* TODO: multi-memories for now assuming the memory64 flag of a memory is + * consistent across multi-memories */ + if (wasm_module->import_memory_count > 0) + is_memory64 = !!(wasm_module->import_memories[0].u.memory.mem_type.flags + & MEMORY64_FLAG); + else if (wasm_module->memory_count > 0) + is_memory64 = !!(wasm_module->memories[0].flags & MEMORY64_FLAG); + + if (!(option->bounds_checks == 1 || option->bounds_checks == 0) + && is_memory64) { + /* For memory64, the boundary check default value is true */ + comp_ctx->enable_bound_check = true; + } + + /* Return error if SIMD is disabled by command line but SIMD instructions + * are used */ + if (!option->enable_simd && wasm_module->is_simd_used) { + aot_set_last_error("SIMD is disabled by --disable-simd but SIMD " + "instructions are used in this module"); + goto fail; + } + + /* Return error if ref-types and GC are disabled by command line but + ref-types instructions are used */ + if (!option->enable_call_indirect_overlong && !option->enable_gc + && wasm_module->is_ref_types_used) { + aot_set_last_error("ref-types instruction was found, " + "try removing --disable-ref-types option " + "or adding --enable-gc option."); + goto fail; + } + + /* Disable features when they are not actually used */ + if (!wasm_module->is_simd_used) { + option->enable_simd = comp_ctx->enable_simd = false; + } + if (!wasm_module->is_ref_types_used) { + option->enable_ref_types = comp_ctx->enable_ref_types = false; + option->enable_call_indirect_overlong = + comp_ctx->enable_call_indirect_overlong = false; + } + if (!wasm_module->is_bulk_memory_used) { + option->enable_bulk_memory = comp_ctx->enable_bulk_memory = false; + option->enable_bulk_memory_opt = comp_ctx->enable_bulk_memory_opt = + false; + } +#endif + + if (option->enable_simd && strcmp(comp_ctx->target_arch, "x86_64") != 0 + && strncmp(comp_ctx->target_arch, "aarch64", 7) != 0 + && strcmp(comp_ctx->target_arch, "arc") != 0) { + /* Disable simd if it isn't supported by target arch */ + option->enable_simd = false; + } + + if (option->enable_simd) { + char *tmp; + bool check_simd_ret; + + comp_ctx->enable_simd = true; + + if (!(tmp = LLVMGetTargetMachineCPU(comp_ctx->target_machine))) { + aot_set_last_error("get CPU from Target Machine fail"); + goto fail; + } + + check_simd_ret = + aot_check_simd_compatibility(comp_ctx->target_arch, tmp); + LLVMDisposeMessage(tmp); + if (!check_simd_ret) { + aot_set_last_error("SIMD compatibility check failed, " + "try adding --cpu= to specify a cpu " + "or adding --disable-simd to disable SIMD"); + goto fail; + } + } - if (!(comp_ctx->pass_mgr = LLVMCreateFunctionPassManagerForModule - (comp_ctx->module))) { - aot_set_last_error("create LLVM pass manager failed."); + if (!(target_data_ref = + LLVMCreateTargetDataLayout(comp_ctx->target_machine))) { + aot_set_last_error("create LLVM target data layout failed."); goto fail; } - LLVMAddPromoteMemoryToRegisterPass(comp_ctx->pass_mgr); - LLVMAddInstructionCombiningPass(comp_ctx->pass_mgr); - LLVMAddCFGSimplificationPass(comp_ctx->pass_mgr); - LLVMAddJumpThreadingPass(comp_ctx->pass_mgr); - LLVMAddConstantPropagationPass(comp_ctx->pass_mgr); + LLVMSetModuleDataLayout(comp_ctx->module, target_data_ref); + comp_ctx->pointer_size = LLVMPointerSize(target_data_ref); + LLVMDisposeTargetData(target_data_ref); + + comp_ctx->optimize = true; + if (option->output_format == AOT_LLVMIR_UNOPT_FILE) + comp_ctx->optimize = false; /* Create metadata for llvm float experimental constrained intrinsics */ - if (!(comp_ctx->fp_rounding_mode = - LLVMMDStringInContext(comp_ctx->context, - fp_round, - (uint32)strlen(fp_round))) - || !(comp_ctx->fp_exception_behavior = - LLVMMDStringInContext(comp_ctx->context, - fp_exce, - (uint32)strlen(fp_exce)))) { + if (!(comp_ctx->fp_rounding_mode = LLVMMDStringInContext( + comp_ctx->context, fp_round, (uint32)strlen(fp_round))) + || !(comp_ctx->fp_exception_behavior = LLVMMDStringInContext( + comp_ctx->context, fp_exce, (uint32)strlen(fp_exce)))) { aot_set_last_error("create float llvm metadata failed."); goto fail; } - if (!aot_set_llvm_basic_types(&comp_ctx->basic_types, comp_ctx->context)) { + if (!aot_set_llvm_basic_types(&comp_ctx->basic_types, comp_ctx->context, + comp_ctx->pointer_size)) { aot_set_last_error("create LLVM basic types failed."); goto fail; } @@ -1092,29 +3447,43 @@ aot_create_comp_context(AOTCompData *comp_data, } /* set exec_env data type to int8** */ - if (!(comp_ctx->exec_env_type = LLVMPointerType(INT8_PTR_TYPE, 0))) { - aot_set_last_error("llvm get pointer type failed."); - goto fail; - } + comp_ctx->exec_env_type = comp_ctx->basic_types.int8_pptr_type; /* set aot_inst data type to int8* */ comp_ctx->aot_inst_type = INT8_PTR_TYPE; - /* Create function context for each function */ - comp_ctx->func_ctx_count = comp_data->func_count; - if (!(comp_ctx->func_ctxes = aot_create_func_contexts(comp_data, comp_ctx))) - goto fail; + /* Create function context for each function */ + comp_ctx->func_ctx_count = comp_data->func_count; + if (comp_data->func_count > 0 + && !(comp_ctx->func_ctxes = + aot_create_func_contexts(comp_data, comp_ctx))) + goto fail; + + if (cpu) { + uint32 len = (uint32)strlen(cpu) + 1; + if (!(comp_ctx->target_cpu = wasm_runtime_malloc(len))) { + aot_set_last_error("allocate memory failed"); + goto fail; + } + bh_memcpy_s(comp_ctx->target_cpu, len, cpu, len); + } + + if (comp_ctx->disable_llvm_intrinsics) + aot_intrinsic_fill_capability_flags(comp_ctx); ret = comp_ctx; fail: if (triple_norm_new) LLVMDisposeMessage(triple_norm_new); + if (cpu_new) LLVMDisposeMessage(cpu_new); if (!ret) aot_destroy_comp_context(comp_ctx); + + (void)i; return ret; } @@ -1124,8 +3493,9 @@ aot_destroy_comp_context(AOTCompContext *comp_ctx) if (!comp_ctx) return; - if (comp_ctx->pass_mgr) - LLVMDisposePassManager(comp_ctx->pass_mgr); + if (comp_ctx->stack_usage_file == comp_ctx->stack_usage_temp_file) { + (void)unlink(comp_ctx->stack_usage_temp_file); + } if (comp_ctx->target_machine) LLVMDisposeTargetMachine(comp_ctx->target_machine); @@ -1133,25 +3503,129 @@ aot_destroy_comp_context(AOTCompContext *comp_ctx) if (comp_ctx->builder) LLVMDisposeBuilder(comp_ctx->builder); - if (comp_ctx->exec_engine) { - LLVMDisposeExecutionEngine(comp_ctx->exec_engine); - /* The LLVM module is freed when disposing execution engine, - no need to dispose it again. */ - } - else if (comp_ctx->module) - LLVMDisposeModule(comp_ctx->module); +#if WASM_ENABLE_DEBUG_AOT != 0 + if (comp_ctx->debug_builder) + LLVMDisposeDIBuilder(comp_ctx->debug_builder); +#endif + + if (comp_ctx->orc_thread_safe_context) + LLVMOrcDisposeThreadSafeContext(comp_ctx->orc_thread_safe_context); - if (comp_ctx->context) - LLVMContextDispose(comp_ctx->context); + /* Note: don't dispose comp_ctx->context and comp_ctx->module as + they are disposed when disposing the thread safe context */ + + /* Has to be the last one */ + if (comp_ctx->orc_jit) + LLVMOrcDisposeLLLazyJIT(comp_ctx->orc_jit); if (comp_ctx->func_ctxes) - aot_destroy_func_contexts(comp_ctx->func_ctxes, comp_ctx->func_ctx_count); + aot_destroy_func_contexts(comp_ctx, comp_ctx->func_ctxes, + comp_ctx->func_ctx_count); + + if (bh_list_length(&comp_ctx->native_symbols) > 0) { + AOTNativeSymbol *sym = bh_list_first_elem(&comp_ctx->native_symbols); + while (sym) { + AOTNativeSymbol *t = bh_list_elem_next(sym); + bh_list_remove(&comp_ctx->native_symbols, sym); + wasm_runtime_free(sym); + sym = t; + } + } + + if (comp_ctx->target_cpu) { + wasm_runtime_free(comp_ctx->target_cpu); + } + + if (comp_ctx->aot_frame) { + wasm_runtime_free(comp_ctx->aot_frame); + } + + wasm_runtime_free(comp_ctx); +} + +static bool +insert_native_symbol(AOTCompContext *comp_ctx, const char *symbol, int32 idx) +{ + AOTNativeSymbol *sym = wasm_runtime_malloc(sizeof(AOTNativeSymbol)); + int ret; + + if (!sym) { + aot_set_last_error("alloc native symbol failed."); + return false; + } + + memset(sym, 0, sizeof(AOTNativeSymbol)); + bh_assert(strlen(symbol) <= sizeof(sym->symbol)); + ret = snprintf(sym->symbol, sizeof(sym->symbol), "%s", symbol); + if (ret < 0 || ret + 1 > (int)sizeof(sym->symbol)) { + wasm_runtime_free(sym); + aot_set_last_error_v("symbol name too long: %s", symbol); + return false; + } + sym->index = idx; + + if (BH_LIST_ERROR == bh_list_insert(&comp_ctx->native_symbols, sym)) { + wasm_runtime_free(sym); + aot_set_last_error("insert native symbol to list failed."); + return false; + } + + return true; +} - wasm_free(comp_ctx); +int32 +aot_get_native_symbol_index(AOTCompContext *comp_ctx, const char *symbol) +{ + int32 idx = -1; + AOTNativeSymbol *sym = NULL; + + sym = bh_list_first_elem(&comp_ctx->native_symbols); + + /* Lookup an existing symbol record */ + + while (sym) { + if (strcmp(sym->symbol, symbol) == 0) { + idx = sym->index; + break; + } + sym = bh_list_elem_next(sym); + } + + /* Given symbol is not exist in list, then we alloc a new index for it */ + + if (idx < 0) { + if (comp_ctx->pointer_size == sizeof(uint32) + && (!strncmp(symbol, "f64#", 4) || !strncmp(symbol, "i64#", 4))) { + idx = bh_list_length(&comp_ctx->native_symbols); + /* Add 4 bytes padding on 32-bit target to make sure that + the f64 const is stored on 8-byte aligned address */ + if (idx & 1) { + if (!insert_native_symbol(comp_ctx, "__ignore", idx)) { + return -1; + } + } + } + + idx = bh_list_length(&comp_ctx->native_symbols); + if (!insert_native_symbol(comp_ctx, symbol, idx)) { + return -1; + } + + if (comp_ctx->pointer_size == sizeof(uint32) + && (!strncmp(symbol, "f64#", 4) || !strncmp(symbol, "i64#", 4))) { + /* f64 const occupies 2 pointer slots on 32-bit target */ + if (!insert_native_symbol(comp_ctx, "__ignore", idx + 1)) { + return -1; + } + } + } + + return idx; } void -aot_value_stack_push(AOTValueStack *stack, AOTValue *value) +aot_value_stack_push(const AOTCompContext *comp_ctx, AOTValueStack *stack, + AOTValue *value) { if (!stack->value_list_head) stack->value_list_head = stack->value_list_end = value; @@ -1160,10 +3634,44 @@ aot_value_stack_push(AOTValueStack *stack, AOTValue *value) value->prev = stack->value_list_end; stack->value_list_end = value; } + + if (comp_ctx->aot_frame) { + switch (value->type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_I1: + push_i32(comp_ctx->aot_frame, value); + break; + case VALUE_TYPE_I64: + push_i64(comp_ctx->aot_frame, value); + break; + case VALUE_TYPE_F32: + push_f32(comp_ctx->aot_frame, value); + break; + case VALUE_TYPE_F64: + push_f64(comp_ctx->aot_frame, value); + break; + case VALUE_TYPE_V128: + push_v128(comp_ctx->aot_frame, value); + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + push_ref(comp_ctx->aot_frame, value); + break; +#if WASM_ENABLE_GC != 0 + case VALUE_TYPE_GC_REF: + bh_assert(comp_ctx->enable_gc); + push_gc_ref(comp_ctx->aot_frame, value); + break; +#endif + default: + bh_assert(0); + break; + } + } } AOTValue * -aot_value_stack_pop(AOTValueStack *stack) +aot_value_stack_pop(const AOTCompContext *comp_ctx, AOTValueStack *stack) { AOTValue *value = stack->value_list_end; @@ -1177,19 +3685,60 @@ aot_value_stack_pop(AOTValueStack *stack) value->prev = NULL; } + if (comp_ctx->aot_frame) { + bh_assert(value); + bh_assert(value->value == (comp_ctx->aot_frame->sp - 1)->value); + bh_assert(value->type == (comp_ctx->aot_frame->sp - 1)->type); + + switch (value->type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_I1: + pop_i32(comp_ctx->aot_frame); + break; + case VALUE_TYPE_I64: + pop_i64(comp_ctx->aot_frame); + break; + case VALUE_TYPE_F32: + pop_f32(comp_ctx->aot_frame); + break; + case VALUE_TYPE_F64: + pop_f64(comp_ctx->aot_frame); + break; + case VALUE_TYPE_V128: + pop_v128(comp_ctx->aot_frame); + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + pop_ref(comp_ctx->aot_frame); + break; +#if WASM_ENABLE_GC != 0 + case VALUE_TYPE_GC_REF: + bh_assert(comp_ctx->enable_gc); + pop_gc_ref(comp_ctx->aot_frame); + break; +#endif + default: + bh_assert(0); + break; + } + } + return value; } void -aot_value_stack_destroy(AOTValueStack *stack) +aot_value_stack_destroy(AOTCompContext *comp_ctx, AOTValueStack *stack) { AOTValue *value = stack->value_list_head, *p; while (value) { p = value->next; - wasm_free(value); + wasm_runtime_free(value); value = p; } + + stack->value_list_head = NULL; + stack->value_list_end = NULL; } void @@ -1223,21 +3772,466 @@ aot_block_stack_pop(AOTBlockStack *stack) } void -aot_block_stack_destroy(AOTBlockStack *stack) +aot_block_stack_destroy(AOTCompContext *comp_ctx, AOTBlockStack *stack) { AOTBlock *block = stack->block_list_head, *p; while (block) { p = block->next; - aot_value_stack_destroy(&block->value_stack); - wasm_free(block); + aot_value_stack_destroy(comp_ctx, &block->value_stack); + aot_block_destroy(comp_ctx, block); block = p; } + + stack->block_list_head = NULL; + stack->block_list_end = NULL; +} + +void +aot_block_destroy(AOTCompContext *comp_ctx, AOTBlock *block) +{ + aot_value_stack_destroy(comp_ctx, &block->value_stack); + if (block->param_types) + wasm_runtime_free(block->param_types); + if (block->param_phis) + wasm_runtime_free(block->param_phis); + if (block->else_param_phis) + wasm_runtime_free(block->else_param_phis); + if (block->result_types) + wasm_runtime_free(block->result_types); + if (block->result_phis) + wasm_runtime_free(block->result_phis); + wasm_runtime_free(block); +} + +bool +aot_checked_addr_list_add(AOTFuncContext *func_ctx, uint32 local_idx, + uint64 offset, uint32 bytes) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list; + + if (!(node = wasm_runtime_malloc(sizeof(AOTCheckedAddr)))) { + aot_set_last_error("allocate memory failed."); + return false; + } + + node->local_idx = local_idx; + node->offset = offset; + node->bytes = bytes; + + node->next = func_ctx->checked_addr_list; + func_ctx->checked_addr_list = node; + return true; +} + +void +aot_checked_addr_list_del(AOTFuncContext *func_ctx, uint32 local_idx) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list; + AOTCheckedAddr *node_prev = NULL, *node_next; + + while (node) { + node_next = node->next; + + if (node->local_idx == local_idx) { + if (!node_prev) + func_ctx->checked_addr_list = node_next; + else + node_prev->next = node_next; + wasm_runtime_free(node); + } + else { + node_prev = node; + } + + node = node_next; + } +} + +bool +aot_checked_addr_list_find(AOTFuncContext *func_ctx, uint32 local_idx, + uint64 offset, uint32 bytes) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list; + + while (node) { + if (node->local_idx == local_idx && node->offset == offset + && node->bytes >= bytes) { + return true; + } + node = node->next; + } + + return false; } void -aot_block_destroy(AOTBlock *block) +aot_checked_addr_list_destroy(AOTFuncContext *func_ctx) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list, *node_next; + + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + + func_ctx->checked_addr_list = NULL; +} + +bool +aot_build_zero_function_ret(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, AOTFuncType *func_type) +{ + LLVMValueRef ret = NULL; + + if (func_type->result_count) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + ret = LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + ret = LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + ret = LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + ret = LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + case VALUE_TYPE_V128: + ret = + LLVMBuildRet(comp_ctx->builder, LLVM_CONST(i64x2_vec_zero)); + break; + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + if (comp_ctx->enable_ref_types) + ret = LLVMBuildRet(comp_ctx->builder, REF_NULL); +#if WASM_ENABLE_GC != 0 + else if (comp_ctx->enable_gc) + ret = LLVMBuildRet(comp_ctx->builder, GC_REF_NULL); +#endif + else + bh_assert(0); + break; +#if WASM_ENABLE_GC != 0 + case REF_TYPE_NULLFUNCREF: + case REF_TYPE_NULLEXTERNREF: + case REF_TYPE_NULLREF: + /* case REF_TYPE_FUNCREF: */ + /* case REF_TYPE_EXTERNREF: */ + case REF_TYPE_ANYREF: + case REF_TYPE_EQREF: + case REF_TYPE_HT_NULLABLE: + case REF_TYPE_HT_NON_NULLABLE: + case REF_TYPE_I31REF: + case REF_TYPE_STRUCTREF: + case REF_TYPE_ARRAYREF: +#if WASM_ENABLE_STRINGREF != 0 + case REF_TYPE_STRINGREF: + case REF_TYPE_STRINGVIEWWTF8: + case REF_TYPE_STRINGVIEWWTF16: + case REF_TYPE_STRINGVIEWITER: +#endif + bh_assert(comp_ctx->enable_gc); + ret = LLVMBuildRet(comp_ctx->builder, GC_REF_NULL); + break; +#endif + default: + bh_assert(0); + } + } + else { + ret = LLVMBuildRetVoid(comp_ctx->builder); + } + + if (!ret) { + aot_set_last_error("llvm build ret failed."); + return false; + } +#if WASM_ENABLE_DEBUG_AOT != 0 + /* debug_func is NULL for precheck function */ + if (func_ctx->debug_func != NULL) { + LLVMMetadataRef return_location = + dwarf_gen_func_ret_location(comp_ctx, func_ctx); + LLVMInstructionSetDebugLoc(ret, return_location); + } +#endif + return true; +} + +static LLVMValueRef +__call_llvm_intrinsic(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, const char *name, + LLVMTypeRef ret_type, LLVMTypeRef *param_types, + int param_count, LLVMValueRef *param_values) +{ + LLVMValueRef func, ret; + LLVMTypeRef func_type; + const char *symname; + int32 func_idx; + + if (comp_ctx->disable_llvm_intrinsics + && aot_intrinsic_check_capability(comp_ctx, name)) { + if (func_ctx == NULL) { + aot_set_last_error_v("invalid func_ctx for intrinsic: %s", name); + return NULL; + } + + if (!(func_type = LLVMFunctionType(ret_type, param_types, + (uint32)param_count, false))) { + aot_set_last_error("create LLVM intrinsic function type failed."); + return NULL; + } + if (!(func_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error( + "create LLVM intrinsic function pointer type failed."); + return NULL; + } + + if (!(symname = aot_intrinsic_get_symbol(name))) { + aot_set_last_error_v("runtime intrinsic not implemented: %s\n", + name); + return NULL; + } + + func_idx = + aot_get_native_symbol_index((AOTCompContext *)comp_ctx, symname); + if (func_idx < 0) { + aot_set_last_error_v("get runtime intrinsc index failed: %s\n", + name); + return NULL; + } + + if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol, + func_type, func_idx))) { + aot_set_last_error_v("get runtime intrinsc failed: %s\n", name); + return NULL; + } + } + else { + /* Declare llvm intrinsic function if necessary */ + if (!(func = LLVMGetNamedFunction(func_ctx->module, name))) { + if (!(func_type = LLVMFunctionType(ret_type, param_types, + (uint32)param_count, false))) { + aot_set_last_error( + "create LLVM intrinsic function type failed."); + return NULL; + } + + if (!(func = LLVMAddFunction(func_ctx->module, name, func_type))) { + aot_set_last_error("add LLVM intrinsic function failed."); + return NULL; + } + } + } + +#if LLVM_VERSION_MAJOR >= 14 + func_type = + LLVMFunctionType(ret_type, param_types, (uint32)param_count, false); +#endif + + /* Call the LLVM intrinsic function */ + if (!(ret = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, + (uint32)param_count, "call"))) { + aot_set_last_error("llvm build intrinsic call failed."); + return NULL; + } + + return ret; +} + +LLVMValueRef +aot_call_llvm_intrinsic(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, const char *intrinsic, + LLVMTypeRef ret_type, LLVMTypeRef *param_types, + int param_count, ...) +{ + LLVMValueRef *param_values, ret; + va_list argptr; + uint64 total_size; + int i = 0; + + /* Create param values */ + total_size = sizeof(LLVMValueRef) * (uint64)param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory for param values failed."); + return false; + } + + /* Load each param value */ + va_start(argptr, param_count); + while (i < param_count) + param_values[i++] = va_arg(argptr, LLVMValueRef); + va_end(argptr); + + ret = __call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, ret_type, + param_types, param_count, param_values); + + wasm_runtime_free(param_values); + + return ret; +} + +LLVMValueRef +aot_call_llvm_intrinsic_v(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, const char *intrinsic, + LLVMTypeRef ret_type, LLVMTypeRef *param_types, + int param_count, va_list param_value_list) +{ + LLVMValueRef *param_values, ret; + uint64 total_size; + int i = 0; + + /* Create param values */ + total_size = sizeof(LLVMValueRef) * (uint64)param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory for param values failed."); + return false; + } + + /* Load each param value */ + while (i < param_count) + param_values[i++] = va_arg(param_value_list, LLVMValueRef); + + ret = __call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, ret_type, + param_types, param_count, param_values); + + wasm_runtime_free(param_values); + + return ret; +} + +LLVMValueRef +aot_get_func_from_table(const AOTCompContext *comp_ctx, LLVMValueRef base, + LLVMTypeRef func_type, int32 index) +{ + LLVMValueRef func; + LLVMValueRef func_addr; + + if (!(func_addr = I32_CONST(index))) { + aot_set_last_error("construct function index failed."); + goto fail; + } + + if (!(func_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, OPQ_PTR_TYPE, base, + &func_addr, 1, "func_addr"))) { + aot_set_last_error("get function addr by index failed."); + goto fail; + } + + func = + LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, func_addr, "func_tmp"); + + if (func == NULL) { + aot_set_last_error("get function pointer failed."); + goto fail; + } + + if (!(func = + LLVMBuildBitCast(comp_ctx->builder, func, func_type, "func"))) { + aot_set_last_error("cast function failed."); + goto fail; + } + + return func; +fail: + return NULL; +} + +LLVMValueRef +aot_load_const_from_table(AOTCompContext *comp_ctx, LLVMValueRef base, + const WASMValue *value, uint8 value_type) { - aot_value_stack_destroy(&block->value_stack); - wasm_free(block); + LLVMValueRef const_index, const_addr, const_value; + LLVMTypeRef const_ptr_type, const_type; + char buf[128] = { 0 }; + int32 index; + + switch (value_type) { + case VALUE_TYPE_I32: + /* Store the raw int bits of i32 const as a hex string */ + snprintf(buf, sizeof(buf), "i32#%08" PRIX32, value->i32); + const_ptr_type = INT32_PTR_TYPE; + const_type = I32_TYPE; + break; + case VALUE_TYPE_I64: + /* Store the raw int bits of i64 const as a hex string */ + snprintf(buf, sizeof(buf), "i64#%016" PRIX64, value->i64); + const_ptr_type = INT64_PTR_TYPE; + const_type = I64_TYPE; + break; + case VALUE_TYPE_F32: + /* Store the raw int bits of f32 const as a hex string */ + snprintf(buf, sizeof(buf), "f32#%08" PRIX32, value->i32); + const_ptr_type = F32_PTR_TYPE; + const_type = F32_TYPE; + break; + case VALUE_TYPE_F64: + /* Store the raw int bits of f64 const as a hex string */ + snprintf(buf, sizeof(buf), "f64#%016" PRIX64, value->i64); + const_ptr_type = F64_PTR_TYPE; + const_type = F64_TYPE; + break; + default: + bh_assert(0); + return NULL; + } + + /* Load f32/f64 const from exec_env->native_symbol[index] */ + + index = aot_get_native_symbol_index(comp_ctx, buf); + if (index < 0) { + return NULL; + } + + if (!(const_index = I32_CONST(index))) { + aot_set_last_error("construct const index failed."); + return NULL; + } + + if (!(const_addr = + LLVMBuildInBoundsGEP2(comp_ctx->builder, OPQ_PTR_TYPE, base, + &const_index, 1, "const_addr_tmp"))) { + aot_set_last_error("get const addr by index failed."); + return NULL; + } + + if (!(const_addr = LLVMBuildBitCast(comp_ctx->builder, const_addr, + const_ptr_type, "const_addr"))) { + aot_set_last_error("cast const failed."); + return NULL; + } + + if (!(const_value = LLVMBuildLoad2(comp_ctx->builder, const_type, + const_addr, "const_value"))) { + aot_set_last_error("load const failed."); + return NULL; + } + + (void)const_type; + return const_value; +} + +bool +aot_set_cond_br_weights(AOTCompContext *comp_ctx, LLVMValueRef cond_br, + int32 weights_true, int32 weights_false) +{ + LLVMMetadataRef md_nodes[3], meta_data; + LLVMValueRef meta_data_as_value; + + md_nodes[0] = LLVMMDStringInContext2(comp_ctx->context, "branch_weights", + strlen("branch_weights")); + md_nodes[1] = LLVMValueAsMetadata(I32_CONST(weights_true)); + md_nodes[2] = LLVMValueAsMetadata(I32_CONST(weights_false)); + + meta_data = LLVMMDNodeInContext2(comp_ctx->context, md_nodes, 3); + meta_data_as_value = LLVMMetadataAsValue(comp_ctx->context, meta_data); + + LLVMSetMetadata(cond_br, 2, meta_data_as_value); + + return true; } diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index c654eae7ab..5bd75a38ce 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -7,151 +7,350 @@ #define _AOT_LLVM_H_ #include "aot.h" +#include "llvm/Config/llvm-config.h" #include "llvm-c/Types.h" #include "llvm-c/Target.h" #include "llvm-c/Core.h" #include "llvm-c/Object.h" +#include "llvm-c/OrcEE.h" #include "llvm-c/ExecutionEngine.h" #include "llvm-c/Analysis.h" +#include "llvm-c/BitWriter.h" +#if LLVM_VERSION_MAJOR < 17 +#include "llvm-c/Transforms/Utils.h" #include "llvm-c/Transforms/Scalar.h" +#include "llvm-c/Transforms/Vectorize.h" +#include "llvm-c/Transforms/PassManagerBuilder.h" +#include "llvm-c/Initialization.h" +#endif + +#include "llvm-c/Orc.h" +#include "llvm-c/Error.h" +#include "llvm-c/Support.h" + +#include "llvm-c/TargetMachine.h" +#include "llvm-c/LLJIT.h" +#if WASM_ENABLE_DEBUG_AOT != 0 +#include "llvm-c/DebugInfo.h" +#endif + +#include "aot_orc_extra.h" +#include "aot_comp_option.h" + +#if defined(_WIN32) || defined(_WIN32_) +#include +#define access _access +/* On windows there is no X_OK flag to check for executablity, only check for + * existence */ +#ifdef X_OK +#undef X_OK +#endif +#define X_OK 00 +#define unlink _unlink +#endif #ifdef __cplusplus extern "C" { #endif +#if LLVM_VERSION_MAJOR < 14 +#define LLVMBuildLoad2(builder, type, value, name) \ + LLVMBuildLoad(builder, value, name) + +#define LLVMBuildCall2(builder, type, func, args, num_args, name) \ + LLVMBuildCall(builder, func, args, num_args, name) + +#define LLVMBuildInBoundsGEP2(builder, type, ptr, indices, num_indices, name) \ + LLVMBuildInBoundsGEP(builder, ptr, indices, num_indices, name) +#else +/* Opaque pointer type */ +#define OPQ_PTR_TYPE INT8_PTR_TYPE +#endif + +#ifndef NDEBUG +#undef DEBUG_PASS +#undef DUMP_MODULE +// #define DEBUG_PASS +// #define DUMP_MODULE +#else +#undef DEBUG_PASS +#undef DUMP_MODULE +#endif + +struct AOTValueSlot; /** * Value in the WASM operation stack, each stack element * is an LLVM value */ typedef struct AOTValue { - struct AOTValue *next; - struct AOTValue *prev; - LLVMValueRef value; - /* VALUE_TYPE_I32/I64/F32/F64/VOID */ - uint8 type; + struct AOTValue *next; + struct AOTValue *prev; + LLVMValueRef value; + uint64 const_value; /* valid if is_const is true */ + uint32 local_idx; + /* VALUE_TYPE_I32/I64/F32/F64/VOID */ + uint8 type; + bool is_local; + bool is_const; } AOTValue; /** * Value stack, represents stack elements in a WASM block */ typedef struct AOTValueStack { - AOTValue *value_list_head; - AOTValue *value_list_end; + AOTValue *value_list_head; + AOTValue *value_list_end; } AOTValueStack; +/* Record information of a value slot of local variable or stack + during translation */ +typedef struct AOTValueSlot { + /* The LLVM value of this slot */ + LLVMValueRef value; + + /* The value type of this slot */ + uint8 type; + + /* The dirty bit of the value slot. It's set if the value in + register is newer than the value in memory. */ + uint32 dirty : 1; + + /* Whether the new value in register is a reference, which is valid + only when the dirty bit is set. */ + uint32 ref : 1; + + /* Committed reference flag: + 0: uncommitted, 1: not-reference, 2: reference */ + uint32 committed_ref : 2; +} AOTValueSlot; + +/* Frame information for translation */ +typedef struct AOTCompFrame { + /* The current compilation context */ + struct AOTCompContext *comp_ctx; + /* The current function context */ + struct AOTFuncContext *func_ctx; + /* The current instruction pointer which is being compiled */ + const uint8 *frame_ip; + + /* Max local slot number */ + uint32 max_local_cell_num; + + /* Max operand stack slot number */ + uint32 max_stack_cell_num; + + /* Size of current AOTFrame/WASMInterpFrame */ + uint32 cur_frame_size; + + /* Stack top pointer */ + AOTValueSlot *sp; + + /* Local variables + stack operands */ + AOTValueSlot lp[1]; +} AOTCompFrame; + typedef struct AOTBlock { - struct AOTBlock *next; - struct AOTBlock *prev; - - /* Block index */ - uint32 block_index; - /* BLOCK_TYPE_BLOCK/LOOP/IF/FUNCTION */ - uint32 block_type; - /* VALUE_TYPE_I32/I64/F32/F64/VOID */ - uint8 return_type; - /* Whether it is reachable */ - bool is_reachable; - /* Whether skip translation of wasm else branch */ - bool skip_wasm_code_else; - - /* code of else opcode of this block, if it is a IF block */ - uint8 *wasm_code_else; - /* code end of this block */ - uint8 *wasm_code_end; - - /* LLVM label points to code begin */ - LLVMBasicBlockRef llvm_entry_block; - /* LLVM label points to code else */ - LLVMBasicBlockRef llvm_else_block; - /* LLVM label points to code end */ - LLVMBasicBlockRef llvm_end_block; - - /* WASM operation stack */ - AOTValueStack value_stack; - - /* Return value of this block, a PHI node */ - LLVMValueRef return_value_phi; + struct AOTBlock *next; + struct AOTBlock *prev; + + /* Block index */ + uint32 block_index; + /* LABEL_TYPE_BLOCK/LOOP/IF/FUNCTION */ + uint32 label_type; + /* Whether it is reachable */ + bool is_reachable; + /* Whether skip translation of wasm else branch */ + bool skip_wasm_code_else; + + /* code of else opcode of this block, if it is a IF block */ + uint8 *wasm_code_else; + /* code end of this block */ + uint8 *wasm_code_end; + + /* LLVM label points to code begin */ + LLVMBasicBlockRef llvm_entry_block; + /* LLVM label points to code else */ + LLVMBasicBlockRef llvm_else_block; + /* LLVM label points to code end */ + LLVMBasicBlockRef llvm_end_block; + + /* WASM operation stack */ + AOTValueStack value_stack; + + /* Param count/types/PHIs of this block */ + uint32 param_count; + uint8 *param_types; + LLVMValueRef *param_phis; + LLVMValueRef *else_param_phis; + + /* Result count/types/PHIs of this block */ + uint32 result_count; + uint8 *result_types; + LLVMValueRef *result_phis; + + /* The begin frame stack pointer of this block */ + AOTValueSlot *frame_sp_begin; + /* The max frame stack pointer that br/br_if/br_table/br_on_xxx + opcodes ever reached when they jumped to the end this block */ + AOTValueSlot *frame_sp_max_reached; } AOTBlock; /** * Block stack, represents WASM block stack elements */ typedef struct AOTBlockStack { - AOTBlock *block_list_head; - AOTBlock *block_list_end; - /* Current block index of each block type */ - uint32 block_index[3]; + AOTBlock *block_list_head; + AOTBlock *block_list_end; + /* Current block index of each block type */ + uint32 block_index[3]; } AOTBlockStack; +typedef struct AOTCheckedAddr { + struct AOTCheckedAddr *next; + uint32 local_idx; + uint64 offset; + uint32 bytes; +} AOTCheckedAddr, *AOTCheckedAddrList; + +typedef struct AOTMemInfo { + LLVMValueRef mem_base_addr; + LLVMValueRef mem_data_size_addr; + LLVMValueRef mem_cur_page_count_addr; + LLVMValueRef mem_bound_check_1byte; + LLVMValueRef mem_bound_check_2bytes; + LLVMValueRef mem_bound_check_4bytes; + LLVMValueRef mem_bound_check_8bytes; + LLVMValueRef mem_bound_check_16bytes; +} AOTMemInfo; + typedef struct AOTFuncContext { - AOTFunc *aot_func; - LLVMValueRef func; - AOTBlockStack block_stack; - - LLVMValueRef exec_env; - LLVMValueRef aot_inst; - LLVMValueRef table_base; - - LLVMValueRef mem_data_size; - LLVMValueRef mem_base_addr; - LLVMValueRef mem_bound_1_byte; - LLVMValueRef mem_bound_2_bytes; - LLVMValueRef mem_bound_4_bytes; - LLVMValueRef mem_bound_8_bytes; - - LLVMValueRef heap_base_offset; - LLVMValueRef heap_base_addr; - LLVMValueRef heap_data_size; - LLVMValueRef heap_bound_1_byte; - LLVMValueRef heap_bound_2_bytes; - LLVMValueRef heap_bound_4_bytes; - LLVMValueRef heap_bound_8_bytes; - - LLVMValueRef cur_exception; - - bool mem_space_unchanged; - - LLVMBasicBlockRef *exception_blocks; - LLVMBasicBlockRef got_exception_block; - LLVMBasicBlockRef func_return_block; - LLVMValueRef exception_id_phi; - LLVMValueRef func_ptrs; - LLVMValueRef func_type_indexes; - LLVMValueRef locals[1]; + AOTFunc *aot_func; + LLVMValueRef func; + LLVMValueRef precheck_func; + LLVMTypeRef func_type; + LLVMModuleRef module; + AOTBlockStack block_stack; + + LLVMValueRef exec_env; + 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; + LLVMValueRef func_ptrs; + + AOTMemInfo *mem_info; + + LLVMValueRef cur_exception; + + LLVMValueRef cur_frame; + LLVMValueRef cur_frame_ptr; + LLVMValueRef wasm_stack_top_bound; + LLVMValueRef wasm_stack_top_ptr; + + bool mem_space_unchanged; + AOTCheckedAddrList checked_addr_list; + + /* The last accessed shared heap info */ + LLVMValueRef shared_heap_base_addr_adj; + LLVMValueRef shared_heap_start_off; + LLVMValueRef shared_heap_end_off; + /* The start offset of the head of shared heap chain */ + LLVMValueRef shared_heap_head_start_off; + + LLVMBasicBlockRef got_exception_block; + LLVMBasicBlockRef func_return_block; + LLVMValueRef exception_id_phi; + /* current ip when exception is thrown */ + LLVMValueRef exception_ip_phi; + LLVMValueRef func_type_indexes; +#if WASM_ENABLE_DEBUG_AOT != 0 + LLVMMetadataRef debug_func; +#endif +#if WASM_ENABLE_BRANCH_HINTS != 0 + struct WASMCompilationHint *function_hints; +#endif + + unsigned int stack_consumption_for_func_call; + + LLVMValueRef locals[1]; } AOTFuncContext; typedef struct AOTLLVMTypes { - LLVMTypeRef int1_type; - LLVMTypeRef int8_type; - LLVMTypeRef int16_type; - LLVMTypeRef int32_type; - LLVMTypeRef int64_type; - LLVMTypeRef float32_type; - LLVMTypeRef float64_type; - LLVMTypeRef void_type; - - LLVMTypeRef int8_ptr_type; - LLVMTypeRef int16_ptr_type; - LLVMTypeRef int32_ptr_type; - LLVMTypeRef int64_ptr_type; - LLVMTypeRef float32_ptr_type; - LLVMTypeRef float64_ptr_type; - LLVMTypeRef void_ptr_type; - - LLVMTypeRef meta_data_type; + LLVMTypeRef int1_type; + LLVMTypeRef int8_type; + LLVMTypeRef int16_type; + LLVMTypeRef int32_type; + LLVMTypeRef int64_type; + LLVMTypeRef intptr_t_type; + LLVMTypeRef size_t_type; + LLVMTypeRef float32_type; + LLVMTypeRef float64_type; + LLVMTypeRef void_type; + + LLVMTypeRef int8_ptr_type; + LLVMTypeRef int8_pptr_type; + LLVMTypeRef int16_ptr_type; + LLVMTypeRef int32_ptr_type; + LLVMTypeRef int64_ptr_type; + LLVMTypeRef intptr_t_ptr_type; + LLVMTypeRef float32_ptr_type; + LLVMTypeRef float64_ptr_type; + + LLVMTypeRef v128_type; + LLVMTypeRef v128_ptr_type; + LLVMTypeRef i8x16_vec_type; + LLVMTypeRef i16x8_vec_type; + LLVMTypeRef i32x4_vec_type; + LLVMTypeRef i64x2_vec_type; + LLVMTypeRef f32x4_vec_type; + LLVMTypeRef f64x2_vec_type; + + LLVMTypeRef int8_ptr_type_gs; + LLVMTypeRef int16_ptr_type_gs; + LLVMTypeRef int32_ptr_type_gs; + LLVMTypeRef int64_ptr_type_gs; + LLVMTypeRef float32_ptr_type_gs; + LLVMTypeRef float64_ptr_type_gs; + LLVMTypeRef v128_ptr_type_gs; + + LLVMTypeRef i1x2_vec_type; + + LLVMTypeRef meta_data_type; + + LLVMTypeRef funcref_type; + LLVMTypeRef externref_type; + LLVMTypeRef gc_ref_type; + LLVMTypeRef gc_ref_ptr_type; } AOTLLVMTypes; typedef struct AOTLLVMConsts { + LLVMValueRef i1_zero; + LLVMValueRef i1_one; LLVMValueRef i8_zero; + LLVMValueRef i8_one; LLVMValueRef i32_zero; LLVMValueRef i64_zero; LLVMValueRef f32_zero; LLVMValueRef f64_zero; LLVMValueRef i32_one; LLVMValueRef i32_two; + LLVMValueRef i32_three; LLVMValueRef i32_four; + LLVMValueRef i32_five; + LLVMValueRef i32_six; + LLVMValueRef i32_seven; LLVMValueRef i32_eight; + LLVMValueRef i32_nine; + LLVMValueRef i32_ten; + LLVMValueRef i32_eleven; + LLVMValueRef i32_twelve; + LLVMValueRef i32_thirteen; + LLVMValueRef i32_fourteen; + LLVMValueRef i32_fifteen; LLVMValueRef i32_neg_one; LLVMValueRef i64_neg_one; LLVMValueRef i32_min; @@ -160,49 +359,201 @@ typedef struct AOTLLVMConsts { LLVMValueRef i32_32; LLVMValueRef i64_63; LLVMValueRef i64_64; + LLVMValueRef i8x16_vec_zero; + LLVMValueRef i16x8_vec_zero; + LLVMValueRef i32x4_vec_zero; + LLVMValueRef i64x2_vec_zero; + LLVMValueRef f32x4_vec_zero; + LLVMValueRef f64x2_vec_zero; + LLVMValueRef i8x16_undef; + LLVMValueRef i16x8_undef; + LLVMValueRef i32x4_undef; + LLVMValueRef i64x2_undef; + LLVMValueRef f32x4_undef; + LLVMValueRef f64x2_undef; + LLVMValueRef i32x16_zero; + LLVMValueRef i32x8_zero; + LLVMValueRef i32x4_zero; + LLVMValueRef i32x2_zero; + LLVMValueRef gc_ref_null; + LLVMValueRef i8_ptr_null; } AOTLLVMConsts; /** * Compiler context */ typedef struct AOTCompContext { - AOTCompData *comp_data; + const AOTCompData *comp_data; + + /* LLVM variables required to emit LLVM IR */ + LLVMContextRef context; + LLVMBuilderRef builder; +#if WASM_ENABLE_DEBUG_AOT + LLVMDIBuilderRef debug_builder; + LLVMMetadataRef debug_file; + LLVMMetadataRef debug_comp_unit; +#endif + LLVMTargetMachineRef target_machine; + char *target_cpu; + char target_arch[16]; + unsigned pointer_size; + + /* Hardware intrinsic compatibility flags */ + uint64 flags[8]; + + /* required by JIT */ + LLVMOrcLLLazyJITRef orc_jit; + LLVMOrcThreadSafeContextRef orc_thread_safe_context; + + LLVMModuleRef module; + + bool is_jit_mode; + + /* AOT indirect mode flag & symbol list */ + bool is_indirect_mode; + bh_list native_symbols; + + /* Bulk memory feature */ + bool enable_bulk_memory; + + /* Bulk memory opt feature. will be enabled alongside the + * enable_bulk_memory */ + bool enable_bulk_memory_opt; + + /* Boundary Check */ + bool enable_bound_check; + + /* Native stack boundary Check */ + bool enable_stack_bound_check; + + /* Native stack usage estimation */ + bool enable_stack_estimation; + + /* 128-bit SIMD */ + bool enable_simd; + + /* Auxiliary stack overflow/underflow check */ + bool enable_aux_stack_check; + + /* Generate auxiliary stack frame */ + AOTStackFrameType aux_stack_frame_type; + + /* Auxiliary call stack features */ + AOTCallStackFeatures call_stack_features; + + /* Function performance profiling */ + bool enable_perf_profiling; - /* LLVM variables required to emit LLVM IR */ - LLVMContextRef context; - LLVMModuleRef module; - LLVMBuilderRef builder; - LLVMTargetMachineRef target_machine; - char *target_cpu; - char target_arch[16]; + /* Memory usage profiling */ + bool enable_memory_profiling; - /* LLVM execution engine required by JIT */ - LLVMExecutionEngineRef exec_engine; - bool is_jit_mode; + /* Thread Manager */ + bool enable_thread_mgr; - /* Whether optimize the JITed code */ - bool optimize; + /* Tail Call */ + bool enable_tail_call; - /* LLVM pass manager to optimize the JITed code */ - LLVMPassManagerRef pass_mgr; + /* Reference Types */ + bool enable_ref_types; - /* LLVM floating-point rounding mode metadata */ - LLVMValueRef fp_rounding_mode; + /* Call Indirect Overlong. will be enabled alongside the enable_ref_types */ + bool enable_call_indirect_overlong; - /* LLVM floating-point exception behavior metadata */ - LLVMValueRef fp_exception_behavior; + /* Disable LLVM built-in intrinsics */ + bool disable_llvm_intrinsics; - /* LLVM data types */ - AOTLLVMTypes basic_types; - LLVMTypeRef exec_env_type; - LLVMTypeRef aot_inst_type; + /* Disable LLVM jump tables */ + bool disable_llvm_jump_tables; - /* LLVM const values */ - AOTLLVMConsts llvm_consts; + /* Disable LLVM link time optimization */ + bool disable_llvm_lto; - /* Function contexts */ - AOTFuncContext **func_ctxes; - uint32 func_ctx_count; + /* Enable LLVM PGO (Profile-Guided Optimization) */ + bool enable_llvm_pgo; + + /* Enable extended constant expression */ + bool enable_extended_const; + + /* Treat unknown import function as wasm-c-api import function + and allow to directly invoke it from AOT/JIT code */ + bool quick_invoke_c_api_import; + + /* Use profile file collected by LLVM PGO */ + char *use_prof_file; + + /* Enable to use segment register as the base addr + of linear memory for load/store operations */ + bool enable_segue_i32_load; + bool enable_segue_i64_load; + bool enable_segue_f32_load; + bool enable_segue_f64_load; + bool enable_segue_v128_load; + bool enable_segue_i32_store; + bool enable_segue_i64_store; + bool enable_segue_f32_store; + bool enable_segue_f64_store; + bool enable_segue_v128_store; + + /* Whether optimize the JITed code */ + bool optimize; + + bool emit_frame_pointer; + + /* Enable GC */ + bool enable_gc; + + bool enable_shared_heap; + bool enable_shared_chain; + + uint32 opt_level; + uint32 size_level; + + /* LLVM floating-point rounding mode metadata */ + LLVMValueRef fp_rounding_mode; + + /* LLVM floating-point exception behavior metadata */ + LLVMValueRef fp_exception_behavior; + + /* a global array to store stack sizes */ + LLVMTypeRef stack_sizes_type; + LLVMValueRef stack_sizes; + uint32 *jit_stack_sizes; /* for JIT */ + + /* LLVM data types */ + AOTLLVMTypes basic_types; + LLVMTypeRef exec_env_type; + LLVMTypeRef aot_inst_type; + + /* LLVM const values */ + AOTLLVMConsts llvm_consts; + + /* Function contexts */ + AOTFuncContext **func_ctxes; + uint32 func_ctx_count; + char **custom_sections_wp; + uint32 custom_sections_count; + + /* 3rd-party toolchains */ + /* External llc compiler, if specified, wamrc will emit the llvm-ir file and + * invoke the llc compiler to generate object file. + * This can be used when we want to benefit from the optimization of other + * LLVM based toolchains */ + const char *external_llc_compiler; + const char *llc_compiler_flags; + /* External asm compiler, if specified, wamrc will emit the text-based + * assembly file (.s) and invoke the llc compiler to generate object file. + * This will be useful when the upstream LLVM doesn't support to emit object + * file for some architecture (such as arc) */ + const char *external_asm_compiler; + const char *asm_compiler_flags; + + const char *stack_usage_file; + char stack_usage_temp_file[64]; + const char *llvm_passes; + const char *builtin_intrinsics; + + /* Current frame information for translation */ + AOTCompFrame *aot_frame; } AOTCompContext; enum { @@ -212,41 +563,39 @@ enum { AOT_LLVMIR_OPT_FILE, }; -typedef struct AOTCompOption{ - bool is_jit_mode; - char *target_arch; - char *target_abi; - char *target_cpu; - char *cpu_features; - uint32 opt_level; - uint32 size_level; - uint32 output_format; -} AOTCompOption, *aot_comp_option_t; +bool +aot_compiler_init(void); + +void +aot_compiler_destroy(void); AOTCompContext * -aot_create_comp_context(AOTCompData *comp_data, - aot_comp_option_t option); +aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option); void aot_destroy_comp_context(AOTCompContext *comp_ctx); +int32 +aot_get_native_symbol_index(AOTCompContext *comp_ctx, const char *symbol); + bool aot_compile_wasm(AOTCompContext *comp_ctx); -uint8* +uint8 * aot_emit_elf_file(AOTCompContext *comp_ctx, uint32 *p_elf_file_size); void aot_destroy_elf_file(uint8 *elf_file); void -aot_value_stack_push(AOTValueStack *stack, AOTValue *value); +aot_value_stack_push(const AOTCompContext *comp_ctx, AOTValueStack *stack, + AOTValue *value); AOTValue * -aot_value_stack_pop(AOTValueStack *stack); +aot_value_stack_pop(const AOTCompContext *comp_ctx, AOTValueStack *stack); void -aot_value_stack_destroy(AOTValueStack *stack); +aot_value_stack_destroy(AOTCompContext *comp_ctx, AOTValueStack *stack); void aot_block_stack_push(AOTBlockStack *stack, AOTBlock *block); @@ -255,17 +604,78 @@ AOTBlock * aot_block_stack_pop(AOTBlockStack *stack); void -aot_block_stack_destroy(AOTBlockStack *stack); +aot_block_stack_destroy(AOTCompContext *comp_ctx, AOTBlockStack *stack); void -aot_block_destroy(AOTBlock *block); +aot_block_destroy(AOTCompContext *comp_ctx, AOTBlock *block); LLVMTypeRef -wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type); +wasm_type_to_llvm_type(const AOTCompContext *comp_ctx, + const AOTLLVMTypes *llvm_types, uint8 wasm_type); + +bool +aot_checked_addr_list_add(AOTFuncContext *func_ctx, uint32 local_idx, + uint64 offset, uint32 bytes); + +void +aot_checked_addr_list_del(AOTFuncContext *func_ctx, uint32 local_idx); + +bool +aot_checked_addr_list_find(AOTFuncContext *func_ctx, uint32 local_idx, + uint64 offset, uint32 bytes); + +void +aot_checked_addr_list_destroy(AOTFuncContext *func_ctx); + +bool +aot_build_zero_function_ret(const AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, AOTFuncType *func_type); + +LLVMValueRef +aot_call_llvm_intrinsic(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, const char *intrinsic, + LLVMTypeRef ret_type, LLVMTypeRef *param_types, + int param_count, ...); + +LLVMValueRef +aot_call_llvm_intrinsic_v(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, const char *intrinsic, + LLVMTypeRef ret_type, LLVMTypeRef *param_types, + int param_count, va_list param_value_list); + +LLVMValueRef +aot_get_func_from_table(const AOTCompContext *comp_ctx, LLVMValueRef base, + LLVMTypeRef func_type, int32 index); + +LLVMValueRef +aot_load_const_from_table(AOTCompContext *comp_ctx, LLVMValueRef base, + const WASMValue *value, uint8 value_type); + +bool +aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str); + +void +aot_apply_llvm_new_pass_manager(AOTCompContext *comp_ctx, LLVMModuleRef module); + +void +aot_handle_llvm_errmsg(const char *string, LLVMErrorRef err); + +char * +aot_compress_aot_func_names(AOTCompContext *comp_ctx, uint32 *p_size); + +bool +aot_set_cond_br_weights(AOTCompContext *comp_ctx, LLVMValueRef cond_br, + int32 weights_true, int32 weights_false); + +bool +aot_target_precheck_can_use_musttail(const AOTCompContext *comp_ctx); + +unsigned int +aot_estimate_stack_usage_for_function_call(const AOTCompContext *comp_ctx, + const AOTFuncType *callee_func_type); #ifdef __cplusplus } /* end of extern "C" */ #endif #endif /* end of _AOT_LLVM_H_ */ - diff --git a/core/iwasm/compilation/aot_llvm_extra.cpp b/core/iwasm/compilation/aot_llvm_extra.cpp new file mode 100644 index 0000000000..35b25c8300 --- /dev/null +++ b/core/iwasm/compilation/aot_llvm_extra.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#if LLVM_VERSION_MAJOR < 17 +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LLVM_VERSION_MAJOR < 17 +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LLVM_VERSION_MAJOR >= 17 +#include +#include +#endif +#include +#include +#include +#if LLVM_VERSION_MAJOR >= 17 +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LLVM_VERSION_MAJOR >= 12 +#include +#endif +#include + +#include +#include "../aot/aot_runtime.h" +#include "aot_llvm.h" + +using namespace llvm; +using namespace llvm::orc; + +#if LLVM_VERSION_MAJOR >= 17 +namespace llvm { +template +using Optional = std::optional; +} +#endif + +LLVM_C_EXTERN_C_BEGIN + +bool +aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str); + +void +aot_apply_llvm_new_pass_manager(AOTCompContext *comp_ctx, LLVMModuleRef module); + +LLVM_C_EXTERN_C_END + +ExitOnError ExitOnErr; + +class ExpandMemoryOpPass : public PassInfoMixin +{ + public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); +}; + +PreservedAnalyses +ExpandMemoryOpPass::run(Function &F, FunctionAnalysisManager &AM) +{ + SmallVector MemCalls; + + /* Iterate over all instructions in the function, looking for memcpy, + * memmove, and memset. When we find one, expand it into a loop. */ + + for (auto &BB : F) { + for (auto &Inst : BB) { + if (auto *Memcpy = dyn_cast_or_null(&Inst)) { + MemCalls.push_back(Memcpy); + } + else if (auto *Memmove = dyn_cast_or_null(&Inst)) { + MemCalls.push_back(Memmove); + } + else if (auto *Memset = dyn_cast_or_null(&Inst)) { + MemCalls.push_back(Memset); + } + } + } + + for (MemIntrinsic *MemCall : MemCalls) { + if (MemCpyInst *Memcpy = dyn_cast(MemCall)) { + Function *ParentFunc = Memcpy->getParent()->getParent(); + const TargetTransformInfo &TTI = + AM.getResult(*ParentFunc); + expandMemCpyAsLoop(Memcpy, TTI); + Memcpy->eraseFromParent(); + } + else if (MemMoveInst *Memmove = dyn_cast(MemCall)) { +#if LLVM_VERSION_MAJOR >= 17 + Function *ParentFunc = Memmove->getParent()->getParent(); + const TargetTransformInfo &TTI = + AM.getResult(*ParentFunc); + expandMemMoveAsLoop(Memmove, TTI); +#else + expandMemMoveAsLoop(Memmove); +#endif + Memmove->eraseFromParent(); + } + else if (MemSetInst *Memset = dyn_cast(MemCall)) { + expandMemSetAsLoop(Memset); + Memset->eraseFromParent(); + } + } + + PreservedAnalyses PA; + PA.preserveSet(); + + return PA; +} + +bool +aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str) +{ +#if WASM_ENABLE_SIMD != 0 + if (!arch_c_str || !cpu_c_str) { + return false; + } + + llvm::SmallVector targetAttributes; + llvm::Triple targetTriple(arch_c_str, "", ""); + auto targetMachine = + std::unique_ptr(llvm::EngineBuilder().selectTarget( + targetTriple, "", std::string(cpu_c_str), targetAttributes)); + if (!targetMachine) { + return false; + } + + const llvm::Triple::ArchType targetArch = + targetMachine->getTargetTriple().getArch(); + const llvm::MCSubtargetInfo *subTargetInfo = + targetMachine->getMCSubtargetInfo(); + if (subTargetInfo == nullptr) { + return false; + } + + if (targetArch == llvm::Triple::x86_64) { + return subTargetInfo->checkFeatures("+sse4.1"); + } + else if (targetArch == llvm::Triple::aarch64) { + return subTargetInfo->checkFeatures("+neon"); + } + else if (targetArch == llvm::Triple::arc) { + return true; + } + else { + return false; + } +#else + (void)arch_c_str; + (void)cpu_c_str; + return true; +#endif /* WASM_ENABLE_SIMD */ +} + +void +aot_apply_llvm_new_pass_manager(AOTCompContext *comp_ctx, LLVMModuleRef module) +{ + TargetMachine *TM = + reinterpret_cast(comp_ctx->target_machine); + PipelineTuningOptions PTO; + PTO.LoopVectorization = true; + PTO.SLPVectorization = true; + PTO.LoopUnrolling = true; + +#if LLVM_VERSION_MAJOR >= 16 + Optional PGO = std::nullopt; +#else + Optional PGO = llvm::None; +#endif + + if (comp_ctx->enable_llvm_pgo) { + /* Disable static counter allocation for value profiler, + it will be allocated by runtime */ + const char *argv[] = { "", "-vp-static-alloc=false" }; + cl::ParseCommandLineOptions(2, argv); +#if LLVM_VERSION_MAJOR < 17 + PGO = PGOOptions("", "", "", PGOOptions::IRInstr); +#else + auto FS = vfs::getRealFileSystem(); + PGO = PGOOptions("", "", "", "", FS, PGOOptions::IRInstr); +#endif + } + else if (comp_ctx->use_prof_file) { +#if LLVM_VERSION_MAJOR < 17 + PGO = PGOOptions(comp_ctx->use_prof_file, "", "", PGOOptions::IRUse); +#else + auto FS = vfs::getRealFileSystem(); + PGO = PGOOptions(comp_ctx->use_prof_file, "", "", "", FS, + PGOOptions::IRUse); +#endif + } + +#ifdef DEBUG_PASS + PassInstrumentationCallbacks PIC; + PassBuilder PB(TM, PTO, PGO, &PIC); +#else +#if LLVM_VERSION_MAJOR == 12 + PassBuilder PB(false, TM, PTO, PGO); +#else + PassBuilder PB(TM, PTO, PGO); +#endif +#endif + + /* Register all the basic analyses with the managers */ + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + /* Register the target library analysis directly and give it a + customized preset TLI */ + std::unique_ptr TLII( + new TargetLibraryInfoImpl(Triple(TM->getTargetTriple()))); + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + + /* Register the AA manager first so that our version is the one used */ + AAManager AA = PB.buildDefaultAAPipeline(); + FAM.registerPass([&] { return std::move(AA); }); + +#ifdef DEBUG_PASS + StandardInstrumentations SI(true, false); + SI.registerCallbacks(PIC, &FAM); +#endif + + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + +#if LLVM_VERSION_MAJOR <= 13 + PassBuilder::OptimizationLevel OL; + + switch (comp_ctx->opt_level) { + case 0: + OL = PassBuilder::OptimizationLevel::O0; + break; + case 1: + OL = PassBuilder::OptimizationLevel::O1; + break; + case 2: + OL = PassBuilder::OptimizationLevel::O2; + break; + case 3: + default: + OL = PassBuilder::OptimizationLevel::O3; + break; + } +#else + OptimizationLevel OL; + + switch (comp_ctx->opt_level) { + case 0: + OL = OptimizationLevel::O0; + break; + case 1: + OL = OptimizationLevel::O1; + break; + case 2: + OL = OptimizationLevel::O2; + break; + case 3: + default: + OL = OptimizationLevel::O3; + break; + } +#endif /* end of LLVM_VERSION_MAJOR */ + + bool disable_llvm_lto = comp_ctx->disable_llvm_lto; +#if WASM_ENABLE_SPEC_TEST != 0 + disable_llvm_lto = true; +#endif + + Module *M = reinterpret_cast(module); + if (disable_llvm_lto) { + for (Function &F : *M) { + F.addFnAttr("disable-tail-calls", "true"); + } + } + + ModulePassManager MPM; + + if (comp_ctx->is_jit_mode) { +#if LLVM_VERSION_MAJOR >= 18 +#define INSTCOMBINE "instcombine" +#else +#define INSTCOMBINE "instcombine" +#endif + const char *Passes = + "loop-vectorize,slp-vectorizer," + "load-store-vectorizer,vector-combine," + "mem2reg," INSTCOMBINE ",simplifycfg,jump-threading,indvars"; + ExitOnErr(PB.parsePassPipeline(MPM, Passes)); + } + else { + FunctionPassManager FPM; + + /* Apply Vectorize related passes for AOT mode */ + FPM.addPass(LoopVectorizePass()); + FPM.addPass(SLPVectorizerPass()); + FPM.addPass(LoadStoreVectorizerPass()); + FPM.addPass(VectorCombinePass()); + + if (comp_ctx->enable_llvm_pgo || comp_ctx->use_prof_file) { + /* LICM pass: loop invariant code motion, attempting to remove + as much code from the body of a loop as possible. Experiments + show it is good to enable it when pgo is enabled. */ +#if LLVM_VERSION_MAJOR >= 15 + LICMOptions licm_opt; + FPM.addPass( + createFunctionToLoopPassAdaptor(LICMPass(licm_opt), true)); +#else + FPM.addPass(createFunctionToLoopPassAdaptor(LICMPass(), true)); +#endif + } + + /* + FPM.addPass(createFunctionToLoopPassAdaptor(LoopRotatePass())); + FPM.addPass(createFunctionToLoopPassAdaptor(SimpleLoopUnswitchPass())); + */ + + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + + if (comp_ctx->llvm_passes) { + ExitOnErr(PB.parsePassPipeline(MPM, comp_ctx->llvm_passes)); + } + + if ( +#if LLVM_VERSION_MAJOR <= 13 + PassBuilder::OptimizationLevel::O0 == OL +#else + OptimizationLevel::O0 == OL +#endif + ) { + MPM.addPass(PB.buildO0DefaultPipeline(OL)); + } + else { + if (!disable_llvm_lto) { + /* Apply LTO for AOT mode */ + if (comp_ctx->comp_data->func_count >= 10 + || comp_ctx->enable_llvm_pgo || comp_ctx->use_prof_file) + /* Add the pre-link optimizations if the func count + is large enough or PGO is enabled */ + MPM.addPass(PB.buildLTOPreLinkDefaultPipeline(OL)); + else + MPM.addPass(PB.buildLTODefaultPipeline(OL, NULL)); + } + else { + MPM.addPass(PB.buildPerModuleDefaultPipeline(OL)); + } + } + + /* Run specific passes for AOT indirect mode in last since general + optimization may create some intrinsic function calls like + llvm.memset, so let's remove these function calls here. */ + if (comp_ctx->is_indirect_mode) { + FunctionPassManager FPM1; + FPM1.addPass(ExpandMemoryOpPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM1))); + } + } + + MPM.run(*M, MAM); +} + +char * +aot_compress_aot_func_names(AOTCompContext *comp_ctx, uint32 *p_size) +{ + std::vector NameStrs; + std::string Result; + char buf[32], *compressed_str; + uint32 compressed_str_len, i; + + for (i = 0; i < comp_ctx->func_ctx_count; i++) { + snprintf(buf, sizeof(buf), "%s%d", AOT_FUNC_PREFIX, i); + std::string str(buf); + NameStrs.push_back(str); + } + +#if LLVM_VERSION_MAJOR < 18 +#define collectGlobalObjectNameStrings collectPGOFuncNameStrings +#endif + if (collectGlobalObjectNameStrings(NameStrs, true, Result)) { + aot_set_last_error("collect pgo func name strings failed"); + return NULL; + } + + compressed_str_len = (uint32)Result.size(); + if (!(compressed_str = (char *)wasm_runtime_malloc(compressed_str_len))) { + aot_set_last_error("allocate memory failed"); + return NULL; + } + + bh_memcpy_s(compressed_str, compressed_str_len, Result.c_str(), + compressed_str_len); + *p_size = compressed_str_len; + return compressed_str; +} diff --git a/core/iwasm/compilation/aot_llvm_extra2.cpp b/core/iwasm/compilation/aot_llvm_extra2.cpp new file mode 100644 index 0000000000..bc49c54bbb --- /dev/null +++ b/core/iwasm/compilation/aot_llvm_extra2.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c)2023 YAMAMOTO Takashi. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#if LLVM_VERSION_MAJOR < 17 +#include +#include +#endif +#include +#if LLVM_VERSION_MAJOR >= 14 +#include +#else +#include +#endif +#include + +#include "bh_assert.h" + +#include "aot_llvm_extra2.h" + +#if LLVM_VERSION_MAJOR >= 17 +namespace llvm { +template +using Optional = std::optional; +} +#endif + +static llvm::Optional +convert(LLVMRelocMode reloc_mode) +{ + switch (reloc_mode) { + case LLVMRelocDefault: +#if LLVM_VERSION_MAJOR >= 16 + return std::nullopt; +#else + return llvm::None; +#endif + case LLVMRelocStatic: + return llvm::Reloc::Static; + case LLVMRelocPIC: + return llvm::Reloc::PIC_; + case LLVMRelocDynamicNoPic: + return llvm::Reloc::DynamicNoPIC; + case LLVMRelocROPI: + return llvm::Reloc::ROPI; + case LLVMRelocRWPI: + return llvm::Reloc::RWPI; + case LLVMRelocROPI_RWPI: + return llvm::Reloc::ROPI_RWPI; + } + bh_assert(0); +#if LLVM_VERSION_MAJOR >= 16 + return std::nullopt; +#else + return llvm::None; +#endif +} + +#if LLVM_VERSION_MAJOR < 18 +static llvm::CodeGenOpt::Level +convert(LLVMCodeGenOptLevel opt_level) +{ + switch (opt_level) { + case LLVMCodeGenLevelNone: + return llvm::CodeGenOpt::None; + case LLVMCodeGenLevelLess: + return llvm::CodeGenOpt::Less; + case LLVMCodeGenLevelDefault: + return llvm::CodeGenOpt::Default; + case LLVMCodeGenLevelAggressive: + return llvm::CodeGenOpt::Aggressive; + } + bh_assert(0); + return llvm::CodeGenOpt::None; +} +#else +static llvm::CodeGenOptLevel +convert(LLVMCodeGenOptLevel opt_level) +{ + switch (opt_level) { + case LLVMCodeGenLevelNone: + return llvm::CodeGenOptLevel::None; + case LLVMCodeGenLevelLess: + return llvm::CodeGenOptLevel::Less; + case LLVMCodeGenLevelDefault: + return llvm::CodeGenOptLevel::Default; + case LLVMCodeGenLevelAggressive: + return llvm::CodeGenOptLevel::Aggressive; + } + bh_assert(0); + return llvm::CodeGenOptLevel::None; +} +#endif + +static llvm::Optional +convert(LLVMCodeModel code_model, bool *jit) +{ + *jit = false; + switch (code_model) { + case LLVMCodeModelDefault: +#if LLVM_VERSION_MAJOR >= 16 + return std::nullopt; +#else + return llvm::None; +#endif + case LLVMCodeModelJITDefault: + *jit = true; +#if LLVM_VERSION_MAJOR >= 16 + return std::nullopt; +#else + return llvm::None; +#endif + case LLVMCodeModelTiny: + return llvm::CodeModel::Tiny; + case LLVMCodeModelSmall: + return llvm::CodeModel::Small; + case LLVMCodeModelKernel: + return llvm::CodeModel::Kernel; + case LLVMCodeModelMedium: + return llvm::CodeModel::Medium; + case LLVMCodeModelLarge: + return llvm::CodeModel::Large; + } + bh_assert(0); +#if LLVM_VERSION_MAJOR >= 16 + return std::nullopt; +#else + return llvm::None; +#endif +} + +LLVMTargetMachineRef +LLVMCreateTargetMachineWithOpts(LLVMTargetRef ctarget, const char *triple, + const char *cpu, const char *features, + LLVMCodeGenOptLevel opt_level, + LLVMRelocMode reloc_mode, + LLVMCodeModel code_model, + bool EmitStackSizeSection, + const char *StackUsageOutput) +{ + llvm::TargetOptions opts; + + // -fstack-size-section equiv + // emit it to ".stack_sizes" section in case of ELF + // you can read it with "llvm-readobj --stack-sizes" + opts.EmitStackSizeSection = EmitStackSizeSection; + + // -fstack-usage equiv + if (StackUsageOutput != NULL) { + opts.StackUsageOutput = StackUsageOutput; + } + + auto target = reinterpret_cast(ctarget); + auto rm = convert(reloc_mode); + auto ol = convert(opt_level); + bool jit; + auto cm = convert(code_model, &jit); +#if LLVM_VERSION_MAJOR >= 21 + auto targetmachine = target->createTargetMachine( + llvm::Triple(triple), cpu, features, opts, rm, cm, ol, jit); +#else + auto targetmachine = target->createTargetMachine(triple, cpu, features, + opts, rm, cm, ol, jit); +#endif +#if LLVM_VERSION_MAJOR >= 18 + // always place data in normal data section. + // + // note that: + // - our aot file emitter/loader doesn't support x86-64 large data + // sections. (eg .lrodata) + // - for our purposes, "data" is usually something the compiler + // generated. (eg. jump tables) we probably never benefit from + // large data sections. + targetmachine->setLargeDataThreshold(UINT64_MAX); +#endif + return reinterpret_cast(targetmachine); +} + +/* https://reviews.llvm.org/D153107 */ +#if LLVM_VERSION_MAJOR < 18 +using namespace llvm; + +LLVMTailCallKind +LLVMGetTailCallKind(LLVMValueRef Call) +{ + return (LLVMTailCallKind)unwrap(Call)->getTailCallKind(); +} + +void +LLVMSetTailCallKind(LLVMValueRef Call, LLVMTailCallKind kind) +{ + unwrap(Call)->setTailCallKind((CallInst::TailCallKind)kind); +} +#endif diff --git a/core/iwasm/compilation/aot_llvm_extra2.h b/core/iwasm/compilation/aot_llvm_extra2.h new file mode 100644 index 0000000000..be89faae08 --- /dev/null +++ b/core/iwasm/compilation/aot_llvm_extra2.h @@ -0,0 +1,34 @@ +/* + * Copyright (c)2023 YAMAMOTO Takashi. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +LLVM_C_EXTERN_C_BEGIN +LLVMTargetMachineRef +LLVMCreateTargetMachineWithOpts(LLVMTargetRef ctarget, const char *triple, + const char *cpu, const char *features, + LLVMCodeGenOptLevel opt_level, + LLVMRelocMode reloc_mode, + LLVMCodeModel code_model, + bool EmitStackSizeSection, + const char *StackUsageOutput); + +/* https://reviews.llvm.org/D153107 */ +#if LLVM_VERSION_MAJOR < 18 +typedef enum { + LLVMTailCallKindNone = 0, + LLVMTailCallKindTail = 1, + LLVMTailCallKindMustTail = 2, + LLVMTailCallKindNoTail = 3, +} LLVMTailCallKind; + +LLVMTailCallKind +LLVMGetTailCallKind(LLVMValueRef CallInst); +void +LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind kind); +#endif + +LLVM_C_EXTERN_C_END diff --git a/core/iwasm/compilation/aot_orc_extra.cpp b/core/iwasm/compilation/aot_orc_extra.cpp new file mode 100644 index 0000000000..d9cf3e711f --- /dev/null +++ b/core/iwasm/compilation/aot_orc_extra.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "llvm-c/LLJIT.h" +#include "llvm-c/Orc.h" +#include "llvm-c/OrcEE.h" +#include "llvm-c/TargetMachine.h" + +#if LLVM_VERSION_MAJOR < 17 +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#endif +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" +#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/Support/CBindingWrapping.h" + +#include "aot_orc_extra.h" +#include "aot.h" + +#if LLVM_VERSION_MAJOR >= 17 +namespace llvm { +template +using Optional = std::optional; +} +#endif + +using namespace llvm; +using namespace llvm::orc; +using GlobalValueSet = std::set; + +namespace llvm { +namespace orc { + +class InProgressLookupState; + +class OrcV2CAPIHelper +{ + public: +#if LLVM_VERSION_MAJOR < 18 + using PoolEntry = SymbolStringPtr::PoolEntry; + using PoolEntryPtr = SymbolStringPtr::PoolEntryPtr; + + // Move from SymbolStringPtr to PoolEntryPtr (no change in ref count). + static PoolEntryPtr moveFromSymbolStringPtr(SymbolStringPtr S) + { + PoolEntryPtr Result = nullptr; + std::swap(Result, S.S); + return Result; + } + + // Move from a PoolEntryPtr to a SymbolStringPtr (no change in ref count). + static SymbolStringPtr moveToSymbolStringPtr(PoolEntryPtr P) + { + SymbolStringPtr S; + S.S = P; + return S; + } + + // Copy a pool entry to a SymbolStringPtr (increments ref count). + static SymbolStringPtr copyToSymbolStringPtr(PoolEntryPtr P) + { + return SymbolStringPtr(P); + } + + static PoolEntryPtr getRawPoolEntryPtr(const SymbolStringPtr &S) + { + return S.S; + } + + static void retainPoolEntry(PoolEntryPtr P) + { + SymbolStringPtr S(P); + S.S = nullptr; + } + + static void releasePoolEntry(PoolEntryPtr P) + { + SymbolStringPtr S; + S.S = P; + } + +#endif + static InProgressLookupState *extractLookupState(LookupState &LS) + { + return LS.IPLS.release(); + } + + static void resetLookupState(LookupState &LS, InProgressLookupState *IPLS) + { + return LS.reset(IPLS); + } +}; + +} // namespace orc +} // namespace llvm + +// ORC.h +#if LLVM_VERSION_MAJOR >= 18 +inline LLVMOrcSymbolStringPoolEntryRef +wrap(SymbolStringPoolEntryUnsafe E) +{ + return reinterpret_cast(E.rawPtr()); +} + +inline SymbolStringPoolEntryUnsafe +unwrap(LLVMOrcSymbolStringPoolEntryRef E) +{ + return reinterpret_cast(E); +} +#endif + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ExecutionSession, LLVMOrcExecutionSessionRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(IRTransformLayer, LLVMOrcIRTransformLayerRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(JITDylib, LLVMOrcJITDylibRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(JITTargetMachineBuilder, + LLVMOrcJITTargetMachineBuilderRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ObjectTransformLayer, + LLVMOrcObjectTransformLayerRef) +#if LLVM_VERSION_MAJOR < 18 +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(OrcV2CAPIHelper::PoolEntry, + LLVMOrcSymbolStringPoolEntryRef) +#endif +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ObjectLayer, LLVMOrcObjectLayerRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SymbolStringPool, LLVMOrcSymbolStringPoolRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ThreadSafeModule, LLVMOrcThreadSafeModuleRef) + +// LLJIT.h +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLJITBuilder, LLVMOrcLLJITBuilderRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLLazyJITBuilder, LLVMOrcLLLazyJITBuilderRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(LLLazyJIT, LLVMOrcLLLazyJITRef) + +void +LLVMOrcLLJITBuilderSetNumCompileThreads(LLVMOrcLLJITBuilderRef Builder, + unsigned NumCompileThreads) +{ + unwrap(Builder)->setNumCompileThreads(NumCompileThreads); +} + +LLVMOrcLLLazyJITBuilderRef +LLVMOrcCreateLLLazyJITBuilder(void) +{ + return wrap(new LLLazyJITBuilder()); +} + +void +LLVMOrcDisposeLLLazyJITBuilder(LLVMOrcLLLazyJITBuilderRef Builder) +{ + delete unwrap(Builder); +} + +void +LLVMOrcLLLazyJITBuilderSetNumCompileThreads(LLVMOrcLLLazyJITBuilderRef Builder, + unsigned NumCompileThreads) +{ + unwrap(Builder)->setNumCompileThreads(NumCompileThreads); +} + +void +LLVMOrcLLLazyJITBuilderSetJITTargetMachineBuilder( + LLVMOrcLLLazyJITBuilderRef Builder, LLVMOrcJITTargetMachineBuilderRef JTMP) +{ + unwrap(Builder)->setJITTargetMachineBuilder(*unwrap(JTMP)); + /* Destroy the JTMP, similar to + LLVMOrcLLJITBuilderSetJITTargetMachineBuilder */ + LLVMOrcDisposeJITTargetMachineBuilder(JTMP); +} + +static Optional +PartitionFunction(GlobalValueSet Requested) +{ + std::vector GVsToAdd; + + for (auto *GV : Requested) { + if (isa(GV) && GV->hasName()) { + auto &F = cast(*GV); /* get LLVM function */ + const Module *M = F.getParent(); /* get LLVM module */ + auto GVName = GV->getName(); /* get the function name */ + const char *gvname = GVName.begin(); /* C function name */ + const char *wrapper; + uint32 prefix_len = (uint32)strlen(AOT_FUNC_PREFIX); + + LOG_DEBUG("requested func %s", gvname); + /* Convert "aot_func#n_wrapper" to "aot_func#n" */ + if (strstr(gvname, AOT_FUNC_PREFIX)) { + char buf[16] = { 0 }; + char func_name[64]; + int group_stride, i, j; + int num; + + /* + * if the jit wrapper (which has "_wrapper" suffix in + * the name) is requested, compile others in the group too. + * otherwise, only compile the requested one. + * (and possibly the corresponding wrapped function, + * which has AOT_FUNC_INTERNAL_PREFIX.) + */ + wrapper = strstr(gvname + prefix_len, "_wrapper"); + if (wrapper != NULL) { + num = WASM_ORC_JIT_COMPILE_THREAD_NUM; + } + else { + num = 1; + wrapper = strchr(gvname + prefix_len, 0); + } + bh_assert(wrapper - (gvname + prefix_len) > 0); + /* Get AOT function index */ + bh_memcpy_s(buf, (uint32)sizeof(buf), gvname + prefix_len, + (uint32)(wrapper - (gvname + prefix_len))); + i = atoi(buf); + + group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; + + /* Compile some functions each time */ + for (j = 0; j < num; j++) { + Function *F1; + snprintf(func_name, sizeof(func_name), "%s%d", + AOT_FUNC_PREFIX, i + j * group_stride); + F1 = M->getFunction(func_name); + if (F1) { + LOG_DEBUG("compile func %s", func_name); + GVsToAdd.push_back(cast(F1)); + } + snprintf(func_name, sizeof(func_name), "%s%d", + AOT_FUNC_INTERNAL_PREFIX, i + j * group_stride); + F1 = M->getFunction(func_name); + if (F1) { + LOG_DEBUG("compile func %s", func_name); + GVsToAdd.push_back(cast(F1)); + } + } + } + } + } + + for (auto *GV : GVsToAdd) { + Requested.insert(GV); + } + + return Requested; +} + +LLVMErrorRef +LLVMOrcCreateLLLazyJIT(LLVMOrcLLLazyJITRef *Result, + LLVMOrcLLLazyJITBuilderRef Builder) +{ + assert(Result && "Result can not be null"); + + if (!Builder) + Builder = LLVMOrcCreateLLLazyJITBuilder(); + + auto J = unwrap(Builder)->create(); + LLVMOrcDisposeLLLazyJITBuilder(Builder); + + if (!J) { + Result = nullptr; + return 0; + } + + LLLazyJIT *lazy_jit = J->release(); + lazy_jit->setPartitionFunction(PartitionFunction); + + *Result = wrap(lazy_jit); + return LLVMErrorSuccess; +} + +LLVMErrorRef +LLVMOrcDisposeLLLazyJIT(LLVMOrcLLLazyJITRef J) +{ + delete unwrap(J); + return LLVMErrorSuccess; +} + +LLVMErrorRef +LLVMOrcLLLazyJITAddLLVMIRModule(LLVMOrcLLLazyJITRef J, LLVMOrcJITDylibRef JD, + LLVMOrcThreadSafeModuleRef TSM) +{ + std::unique_ptr TmpTSM(unwrap(TSM)); + return wrap(unwrap(J)->addLazyIRModule(*unwrap(JD), std::move(*TmpTSM))); +} + +LLVMErrorRef +LLVMOrcLLLazyJITLookup(LLVMOrcLLLazyJITRef J, LLVMOrcExecutorAddress *Result, + const char *Name) +{ + assert(Result && "Result can not be null"); + + auto Sym = unwrap(J)->lookup(Name); + if (!Sym) { + *Result = 0; + return wrap(Sym.takeError()); + } + +#if LLVM_VERSION_MAJOR < 15 + *Result = Sym->getAddress(); +#else + *Result = Sym->getValue(); +#endif + return LLVMErrorSuccess; +} + +LLVMOrcSymbolStringPoolEntryRef +LLVMOrcLLLazyJITMangleAndIntern(LLVMOrcLLLazyJITRef J, + const char *UnmangledName) +{ +#if LLVM_VERSION_MAJOR < 18 + return wrap(OrcV2CAPIHelper::moveFromSymbolStringPtr( + unwrap(J)->mangleAndIntern(UnmangledName))); +#else + return wrap(SymbolStringPoolEntryUnsafe::take( + unwrap(J)->mangleAndIntern(UnmangledName))); +#endif +} + +LLVMOrcJITDylibRef +LLVMOrcLLLazyJITGetMainJITDylib(LLVMOrcLLLazyJITRef J) +{ + return wrap(&unwrap(J)->getMainJITDylib()); +} + +const char * +LLVMOrcLLLazyJITGetTripleString(LLVMOrcLLLazyJITRef J) +{ + return unwrap(J)->getTargetTriple().str().c_str(); +} + +LLVMOrcExecutionSessionRef +LLVMOrcLLLazyJITGetExecutionSession(LLVMOrcLLLazyJITRef J) +{ + return wrap(&unwrap(J)->getExecutionSession()); +} + +LLVMOrcIRTransformLayerRef +LLVMOrcLLLazyJITGetIRTransformLayer(LLVMOrcLLLazyJITRef J) +{ + return wrap(&unwrap(J)->getIRTransformLayer()); +} + +LLVMOrcObjectTransformLayerRef +LLVMOrcLLLazyJITGetObjTransformLayer(LLVMOrcLLLazyJITRef J) +{ + return wrap(&unwrap(J)->getObjTransformLayer()); +} + +LLVMOrcObjectLayerRef +LLVMOrcLLLazyJITGetObjLinkingLayer(LLVMOrcLLLazyJITRef J) +{ + return wrap(&unwrap(J)->getObjLinkingLayer()); +} diff --git a/core/iwasm/compilation/aot_orc_extra.h b/core/iwasm/compilation/aot_orc_extra.h new file mode 100644 index 0000000000..d94fd8c1b9 --- /dev/null +++ b/core/iwasm/compilation/aot_orc_extra.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_ORC_LAZINESS_H_ +#define _AOT_ORC_LAZINESS_H_ + +#include "llvm-c/Error.h" +#include "llvm-c/ExternC.h" +#include "llvm-c/LLJIT.h" +#include "llvm-c/Orc.h" +#include "llvm-c/Types.h" + +LLVM_C_EXTERN_C_BEGIN + +typedef struct LLVMOrcOpaqueLLLazyJITBuilder *LLVMOrcLLLazyJITBuilderRef; +typedef struct LLVMOrcOpaqueLLLazyJIT *LLVMOrcLLLazyJITRef; + +// Extra bindings for LLJIT +void +LLVMOrcLLJITBuilderSetNumCompileThreads(LLVMOrcLLJITBuilderRef Builder, + unsigned NumCompileThreads); + +// Extra bindings for LLLazyJIT +LLVMOrcLLLazyJITBuilderRef +LLVMOrcCreateLLLazyJITBuilder(void); + +void +LLVMOrcDisposeLLLazyJITBuilder(LLVMOrcLLLazyJITBuilderRef Builder); + +void +LLVMOrcLLLazyJITBuilderSetJITTargetMachineBuilder( + LLVMOrcLLLazyJITBuilderRef Builder, LLVMOrcJITTargetMachineBuilderRef JTMP); + +void +LLVMOrcLLLazyJITBuilderSetNumCompileThreads(LLVMOrcLLLazyJITBuilderRef Builder, + unsigned NumCompileThreads); + +LLVMErrorRef +LLVMOrcCreateLLLazyJIT(LLVMOrcLLLazyJITRef *Result, + LLVMOrcLLLazyJITBuilderRef Builder); + +LLVMErrorRef +LLVMOrcDisposeLLLazyJIT(LLVMOrcLLLazyJITRef J); + +LLVMErrorRef +LLVMOrcLLLazyJITAddLLVMIRModule(LLVMOrcLLLazyJITRef J, LLVMOrcJITDylibRef JD, + LLVMOrcThreadSafeModuleRef TSM); + +LLVMErrorRef +LLVMOrcLLLazyJITLookup(LLVMOrcLLLazyJITRef J, LLVMOrcExecutorAddress *Result, + const char *Name); + +LLVMOrcSymbolStringPoolEntryRef +LLVMOrcLLLazyJITMangleAndIntern(LLVMOrcLLLazyJITRef J, + const char *UnmangledName); + +LLVMOrcJITDylibRef +LLVMOrcLLLazyJITGetMainJITDylib(LLVMOrcLLLazyJITRef J); + +const char * +LLVMOrcLLLazyJITGetTripleString(LLVMOrcLLLazyJITRef J); + +LLVMOrcExecutionSessionRef +LLVMOrcLLLazyJITGetExecutionSession(LLVMOrcLLLazyJITRef J); + +LLVMOrcIRTransformLayerRef +LLVMOrcLLLazyJITGetIRTransformLayer(LLVMOrcLLLazyJITRef J); + +LLVMOrcObjectTransformLayerRef +LLVMOrcLLLazyJITGetObjTransformLayer(LLVMOrcLLLazyJITRef J); + +void +LLVMOrcLLJITBuilderSetCompileFunctionCreatorWithStackSizesCallback( + LLVMOrcLLLazyJITBuilderRef Builder, + void (*cb)(void *, const char *, size_t, size_t), void *cb_data); + +LLVMOrcObjectLayerRef +LLVMOrcLLLazyJITGetObjLinkingLayer(LLVMOrcLLLazyJITRef J); + +LLVM_C_EXTERN_C_END +#endif diff --git a/core/iwasm/compilation/aot_orc_extra2.cpp b/core/iwasm/compilation/aot_orc_extra2.cpp new file mode 100644 index 0000000000..2979737e76 --- /dev/null +++ b/core/iwasm/compilation/aot_orc_extra2.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "llvm/ExecutionEngine/Orc/CompileUtils.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunctionPass.h" + +#include "aot_orc_extra.h" +#include "bh_log.h" + +typedef void (*cb_t)(void *, const char *, size_t, size_t); + +class MyCompiler : public llvm::orc::IRCompileLayer::IRCompiler +{ + public: + MyCompiler(llvm::orc::JITTargetMachineBuilder JTMB, cb_t cb, void *cb_data); + llvm::Expected operator()( + llvm::Module &M) override; + + private: + llvm::orc::JITTargetMachineBuilder JTMB; + + cb_t cb; + void *cb_data; +}; + +MyCompiler::MyCompiler(llvm::orc::JITTargetMachineBuilder JTMB, cb_t cb, + void *cb_data) + : IRCompiler(llvm::orc::irManglingOptionsFromTargetOptions(JTMB.getOptions())) + , JTMB(std::move(JTMB)) + , cb(cb) + , cb_data(cb_data) +{} + +class PrintStackSizes : public llvm::MachineFunctionPass +{ + public: + PrintStackSizes(cb_t cb, void *cb_data); + bool runOnMachineFunction(llvm::MachineFunction &MF) override; + static char ID; + + private: + cb_t cb; + void *cb_data; +}; + +PrintStackSizes::PrintStackSizes(cb_t cb, void *cb_data) + : MachineFunctionPass(ID) + , cb(cb) + , cb_data(cb_data) +{} + +char PrintStackSizes::ID = 0; + +bool +PrintStackSizes::runOnMachineFunction(llvm::MachineFunction &MF) +{ + auto name = MF.getName(); + auto MFI = &MF.getFrameInfo(); + size_t sz = MFI->getStackSize(); + cb(cb_data, name.data(), name.size(), sz); + return false; +} + +class MyPassManager : public llvm::legacy::PassManager +{ + public: + void add(llvm::Pass *P) override; +}; + +void +MyPassManager::add(llvm::Pass *P) +{ + // a hack to avoid having a copy of the whole addPassesToEmitMC. + // we want to add PrintStackSizes before FreeMachineFunctionPass. + if (P->getPassName() == "Free MachineFunction") { + delete P; + return; + } + llvm::legacy::PassManager::add(P); +} + +// a modified copy from llvm/lib/ExecutionEngine/Orc/CompileUtils.cpp +llvm::Expected +MyCompiler::operator()(llvm::Module &M) +{ + auto TM = cantFail(JTMB.createTargetMachine()); + llvm::SmallVector ObjBufferSV; + + { + llvm::raw_svector_ostream ObjStream(ObjBufferSV); + + MyPassManager PM; + llvm::MCContext *Ctx; + if (TM->addPassesToEmitMC(PM, Ctx, ObjStream)) + return llvm::make_error( + "Target does not support MC emission", + llvm::inconvertibleErrorCode()); + PM.add(new PrintStackSizes(cb, cb_data)); + dynamic_cast(&PM)->add( + llvm::createFreeMachineFunctionPass()); + PM.run(M); + } + +#if LLVM_VERSION_MAJOR > 13 + auto ObjBuffer = std::make_unique( + std::move(ObjBufferSV), + M.getModuleIdentifier() + "-jitted-objectbuffer", + /*RequiresNullTerminator=*/false); +#else + auto ObjBuffer = std::make_unique( + std::move(ObjBufferSV), + M.getModuleIdentifier() + "-jitted-objectbuffer"); +#endif + + return ObjBuffer; +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(llvm::orc::LLLazyJITBuilder, + LLVMOrcLLLazyJITBuilderRef) + +void +LLVMOrcLLJITBuilderSetCompileFunctionCreatorWithStackSizesCallback( + LLVMOrcLLLazyJITBuilderRef Builder, + void (*cb)(void *, const char *, size_t, size_t), void *cb_data) +{ + auto b = unwrap(Builder); + b->setCompileFunctionCreator( + [cb, cb_data](llvm::orc::JITTargetMachineBuilder JTMB) + -> llvm::Expected< + std::unique_ptr> { + return std::make_unique( + MyCompiler(std::move(JTMB), cb, cb_data)); + }); +} diff --git a/core/iwasm/compilation/aot_stack_frame.h b/core/iwasm/compilation/aot_stack_frame.h new file mode 100644 index 0000000000..6155ee6e9d --- /dev/null +++ b/core/iwasm/compilation/aot_stack_frame.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_STACK_FRAME_H_ +#define _AOT_STACK_FRAME_H_ + +#include "platform_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* The non-imported function index of current function */ + uint32 func_index; + + /* Instruction pointer: offset to the bytecode array */ + uint32 ip_offset; +} AOTTinyFrame; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/compilation/aot_stack_frame_comp.c b/core/iwasm/compilation/aot_stack_frame_comp.c new file mode 100644 index 0000000000..fb540e643f --- /dev/null +++ b/core/iwasm/compilation/aot_stack_frame_comp.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2024 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "aot_stack_frame_comp.h" +#include "aot_emit_exception.h" + +#define ADD_IN_BOUNDS_GEP(variable, type, pointer, indices, num_indices) \ + do { \ + if (!(variable = \ + LLVMBuildInBoundsGEP2(comp_ctx->builder, type, pointer, \ + indices, num_indices, #variable))) { \ + aot_set_last_error("llvm build in bounds gep failed"); \ + return false; \ + } \ + } while (0) + +#define ADD_STORE(value, pointer) \ + do { \ + if (!LLVMBuildStore(comp_ctx->builder, value, pointer)) { \ + aot_set_last_error("llvm build store failed"); \ + return false; \ + } \ + } while (0) + +#define ADD_LOAD(value, type, pointer) \ + do { \ + if (!(value = \ + LLVMBuildLoad2(comp_ctx->builder, type, pointer, #value))) { \ + aot_set_last_error("llvm build load failed"); \ + return false; \ + } \ + } while (0) + +static bool +aot_alloc_tiny_frame_for_aot_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef func_index) +{ + LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr, + wasm_stack_top_bound = func_ctx->wasm_stack_top_bound, + wasm_stack_top, cmp; + LLVMBasicBlockRef check_wasm_stack_succ; + LLVMValueRef offset; + + ADD_LOAD(wasm_stack_top, INT8_PTR_TYPE, wasm_stack_top_ptr); + + if (comp_ctx->call_stack_features.bounds_checks) { + if (!(check_wasm_stack_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, + "check_wasm_stack_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + LLVMMoveBasicBlockAfter(check_wasm_stack_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, wasm_stack_top, + wasm_stack_top_bound, "cmp"))) { + aot_set_last_error("llvm build icmp failed"); + return false; + } + + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_OPERAND_STACK_OVERFLOW, true, cmp, + check_wasm_stack_succ))) { + return false; + } + } + + /* Save the func_idx on the top of the stack */ + if (comp_ctx->call_stack_features.func_idx) { + ADD_STORE(func_index, wasm_stack_top); + } + + /* increment the stack pointer */ + INT_CONST(offset, sizeof(AOTTinyFrame), I32_TYPE, true); + ADD_IN_BOUNDS_GEP(wasm_stack_top, INT8_TYPE, wasm_stack_top, &offset, 1); + ADD_STORE(wasm_stack_top, wasm_stack_top_ptr); + + return true; +} + +static bool +aot_free_tiny_frame_for_aot_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr, + wasm_stack_top; + LLVMValueRef offset; + + ADD_LOAD(wasm_stack_top, INT8_PTR_TYPE, wasm_stack_top_ptr); + + INT_CONST(offset, -sizeof(AOTTinyFrame), + comp_ctx->pointer_size == 8 ? I64_TYPE : I32_TYPE, true); + ADD_IN_BOUNDS_GEP(wasm_stack_top, INT8_TYPE, wasm_stack_top, &offset, 1); + ADD_STORE(wasm_stack_top, wasm_stack_top_ptr); + + return true; +} + +bool +aot_tiny_frame_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef ip_value) +{ + LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr, + wasm_stack_top; + LLVMValueRef offset, ip_addr; + + bh_assert(ip_value); + + ADD_LOAD(wasm_stack_top, INT8_PTR_TYPE, wasm_stack_top_ptr); + + INT_CONST(offset, -4, comp_ctx->pointer_size == 8 ? I64_TYPE : I32_TYPE, + true); + ADD_IN_BOUNDS_GEP(ip_addr, INT8_TYPE, wasm_stack_top, &offset, 1); + + ADD_STORE(ip_value, ip_addr); + + return true; +} + +bool +aot_alloc_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef func_index) +{ + switch (comp_ctx->aux_stack_frame_type) { + case AOT_STACK_FRAME_TYPE_TINY: + return aot_alloc_tiny_frame_for_aot_func(comp_ctx, func_ctx, + func_index); + default: + aot_set_last_error("unsupported mode"); + return false; + } +} + +bool +aot_free_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + switch (comp_ctx->aux_stack_frame_type) { + case AOT_STACK_FRAME_TYPE_TINY: + return aot_free_tiny_frame_for_aot_func(comp_ctx, func_ctx); + default: + aot_set_last_error("unsupported mode"); + return false; + } +} diff --git a/core/iwasm/compilation/aot_stack_frame_comp.h b/core/iwasm/compilation/aot_stack_frame_comp.h new file mode 100644 index 0000000000..7980b8c08b --- /dev/null +++ b/core/iwasm/compilation/aot_stack_frame_comp.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_STACK_FRAME_COMP_H_ +#define _AOT_STACK_FRAME_COMP_H_ + +#include "aot_stack_frame.h" +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_alloc_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + LLVMValueRef func_index); + +bool +aot_free_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_tiny_frame_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef ip_value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/compilation/debug/dwarf_extractor.cpp b/core/iwasm/compilation/debug/dwarf_extractor.cpp new file mode 100644 index 0000000000..f08f766168 --- /dev/null +++ b/core/iwasm/compilation/debug/dwarf_extractor.cpp @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API//SBFunction.h" +#include "lldb/API//SBModule.h" +#include "lldb/API//SBProcess.h" +#include "lldb/API//SBStream.h" +#include "lldb/API//SBSymbol.h" +#include "lldb/API//SBTarget.h" +#include "lldb/API//SBThread.h" +#include "lldb/API/SBDeclaration.h" + +#include "dwarf_extractor.h" +#include "../aot_llvm.h" + +#include "bh_log.h" +#include "../../aot/aot_runtime.h" + +#include "llvm/BinaryFormat/Dwarf.h" + +using namespace lldb; + +typedef struct dwarf_extractor { + SBDebugger debugger; + SBTarget target; + SBModule module; + +} dwarf_extractor; + +#define TO_HANDLE(extractor) (dwarf_extractor_handle_t)(extractor) + +#define TO_EXTRACTOR(handle) (dwarf_extractor *)(handle) + +static const char *compiler_name = "WAMR AoT compiler"; +static bool is_debugger_initialized; + +dwarf_extractor_handle_t +create_dwarf_extractor(AOTCompData *comp_data, char *file_name) +{ + char *arch = NULL; + char *platform = NULL; + dwarf_extractor *extractor = NULL; + + //__attribute__((constructor)) may be better? + if (!is_debugger_initialized) { + SBError error = SBDebugger::InitializeWithErrorHandling(); + if (error.Fail()) { + LOG_ERROR("Init Dwarf Debugger failed"); + return TO_HANDLE(NULL); + } + is_debugger_initialized = true; + } + + SBError error; + SBFileSpec exe_file_spec(file_name, true); + + if (!(extractor = new dwarf_extractor())) { + LOG_ERROR("Create Dwarf Extractor error: failed to allocate memory"); + goto fail3; + } + + extractor->debugger = SBDebugger::Create(); + if (!extractor->debugger.IsValid()) { + LOG_ERROR("Create Dwarf Debugger failed"); + goto fail2; + } + + extractor->target = extractor->debugger.CreateTarget( + file_name, arch, platform, false, error); + + if (!error.Success()) { + LOG_ERROR("Create Dwarf target failed:%s", error.GetCString()); + goto fail1; + } + + if (!extractor->target.IsValid()) { + LOG_ERROR("Create Dwarf target not valid"); + goto fail1; + } + + extractor->module = extractor->target.FindModule(exe_file_spec); + comp_data->extractor = TO_HANDLE(extractor); + + return TO_HANDLE(extractor); + +fail1: + SBDebugger::Destroy(extractor->debugger); + +fail2: + wasm_runtime_free(extractor); + +fail3: + return TO_HANDLE(NULL); +} + +void +destroy_dwarf_extractor(dwarf_extractor_handle_t handle) +{ + dwarf_extractor *extractor = TO_EXTRACTOR(handle); + if (!extractor) + return; + extractor->debugger.DeleteTarget(extractor->target); + SBDebugger::Destroy(extractor->debugger); + delete extractor; + SBDebugger::Terminate(); + is_debugger_initialized = false; +} + +LLVMMetadataRef +dwarf_gen_file_info(const AOTCompContext *comp_ctx) +{ + dwarf_extractor *extractor; + int units_number; + LLVMMetadataRef file_info = NULL; + const char *file_name; + const char *dir_name; + + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return NULL; + + units_number = extractor->module.GetNumCompileUnits(); + + if (units_number > 0) { + SBCompileUnit compile_unit = extractor->module.GetCompileUnitAtIndex(0); + auto filespec = compile_unit.GetFileSpec(); + file_name = filespec.GetFilename(); + dir_name = filespec.GetDirectory(); + if (file_name || dir_name) { + file_info = LLVMDIBuilderCreateFile( + comp_ctx->debug_builder, file_name, + file_name ? strlen(file_name) : 0, dir_name, + dir_name ? strlen(dir_name) : 0); + } + } + return file_info; +} + +#if 0 +void +dwarf_gen_mock_vm_info(AOTCompContext *comp_ctx) +{ + LLVMMetadataRef file_info = NULL; + LLVMMetadataRef comp_unit = NULL; + file_info = LLVMDIBuilderCreateFile(comp_ctx->debug_builder, + "ant_runtime_mock.c", 18, ".", 1); + + comp_unit = LLVMDIBuilderCreateCompileUnit( + comp_ctx->debug_builder, LLVMDWARFSourceLanguageC, file_info, + "WAMR AoT compiler", 12, 0, NULL, 0, 1, NULL, 0, LLVMDWARFEmissionFull, 0, 0, + 0, "/", 1, "", 0); + + LLVMTypeRef ParamTys[] = { + LLVMVoidType(), + }; + + LLVMTypeRef FuncTy = LLVMFunctionType(LLVMVoidType(), ParamTys, 0, 0); + + LLVMValueRef Function = + LLVMAddFunction(comp_ctx->module, "ant_runtime_mock", FuncTy); + + LLVMMetadataRef ParamTypes[0]; + LLVMMetadataRef FunctionTy = LLVMDIBuilderCreateSubroutineType( + comp_ctx->debug_builder, file_info, ParamTypes, 0, LLVMDIFlagZero); + + /* 0x0015 is subroutine_type */ + LLVMMetadataRef ReplaceableFunctionMetadata = + LLVMDIBuilderCreateReplaceableCompositeType( + comp_ctx->debug_builder, 0x15, "ant_runtime_mock", 16, file_info, + file_info, 2, 0, 0, 0, LLVMDIFlagFwdDecl, "", 0); + + LLVMMetadataRef FunctionMetadata = LLVMDIBuilderCreateFunction( + comp_ctx->debug_builder, file_info, "ant_runtime_mock", 16, + "ant_runtime_mock", 16, file_info, 2, FunctionTy, true, true, 2, LLVMDIFlagZero, + false); + + LLVMMetadataReplaceAllUsesWith(ReplaceableFunctionMetadata, + FunctionMetadata); + + LLVMSetSubprogram(Function, FunctionMetadata); + + comp_ctx->vm_debug_comp_unit = comp_unit; + comp_ctx->vm_debug_file = file_info; + comp_ctx->vm_debug_func = FunctionMetadata; +} +#endif + +LLVMMetadataRef +dwarf_gen_comp_unit_info(const AOTCompContext *comp_ctx) +{ + dwarf_extractor *extractor; + int units_number; + LLVMMetadataRef comp_unit = NULL; + + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return NULL; + + units_number = extractor->module.GetNumCompileUnits(); + + if (units_number > 0) { + SBCompileUnit compile_unit = extractor->module.GetCompileUnitAtIndex(0); + auto lang_type = compile_unit.GetLanguage(); + + comp_unit = LLVMDIBuilderCreateCompileUnit( + comp_ctx->debug_builder, LLDB_TO_LLVM_LANG_TYPE(lang_type), + comp_ctx->debug_file, compiler_name, strlen(compiler_name), 0, NULL, + 0, 1, NULL, 0, LLVMDWARFEmissionFull, 0, 0, 0, "/", 1, "", 0); + } + return comp_unit; +} + +static LLVMDWARFTypeEncoding +lldb_get_basic_type_encoding(BasicType basic_type) +{ + LLVMDWARFTypeEncoding encoding = 0; + switch (basic_type) { + case eBasicTypeUnsignedChar: + encoding = llvm::dwarf::DW_ATE_unsigned_char; + break; + case eBasicTypeSignedChar: + encoding = llvm::dwarf::DW_ATE_signed_char; + break; + case eBasicTypeUnsignedInt: + case eBasicTypeUnsignedLong: + case eBasicTypeUnsignedLongLong: + case eBasicTypeUnsignedWChar: + case eBasicTypeUnsignedInt128: + case eBasicTypeUnsignedShort: + encoding = llvm::dwarf::DW_ATE_unsigned; + break; + case eBasicTypeInt: + case eBasicTypeLong: + case eBasicTypeLongLong: + case eBasicTypeWChar: + case eBasicTypeInt128: + case eBasicTypeShort: + encoding = llvm::dwarf::DW_ATE_signed; + break; + case eBasicTypeBool: + encoding = llvm::dwarf::DW_ATE_boolean; + break; + case eBasicTypeHalf: + case eBasicTypeFloat: + case eBasicTypeDouble: + case eBasicTypeLongDouble: + encoding = llvm::dwarf::DW_ATE_float; + break; + default: + break; + } + return encoding; +} + +static LLVMMetadataRef +lldb_type_to_type_dbi(const AOTCompContext *comp_ctx, SBType &type) +{ + LLVMMetadataRef type_info = NULL; + BasicType basic_type = type.GetBasicType(); + uint64_t bit_size = type.GetByteSize() * 8; + LLVMDIBuilderRef DIB = comp_ctx->debug_builder; + LLVMDWARFTypeEncoding encoding; + + if (basic_type != eBasicTypeInvalid) { + encoding = lldb_get_basic_type_encoding(basic_type); + type_info = LLVMDIBuilderCreateBasicType( + DIB, type.GetName(), strlen(type.GetName()), bit_size, encoding, + LLVMDIFlagZero); + } + else if (type.IsPointerType()) { + SBType pointee_type = type.GetPointeeType(); + type_info = LLVMDIBuilderCreatePointerType( + DIB, lldb_type_to_type_dbi(comp_ctx, pointee_type), bit_size, 0, 0, + "", 0); + } + + return type_info; +} + +static LLVMMetadataRef +lldb_function_to_function_dbi(const AOTCompContext *comp_ctx, + SBSymbolContext &sc, + const AOTFuncContext *func_ctx) +{ + SBFunction function(sc.GetFunction()); + const char *function_name = function.GetName(); + const char *link_name = function.GetMangledName(); + SBTypeList function_args = function.GetType().GetFunctionArgumentTypes(); + SBType return_type = function.GetType().GetFunctionReturnType(); + const size_t num_function_args = function_args.GetSize(); + dwarf_extractor *extractor; + + /* + * Process only known languages. + * We have a few assumptions which might not be true for non-C functions. + * + * At least it's known broken for C++ and Rust: + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3187 + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3163 + */ + LanguageType language_type = function.GetLanguage(); + bool cplusplus = false; + switch (language_type) { + case eLanguageTypeC89: + case eLanguageTypeC: + case eLanguageTypeC99: + case eLanguageTypeC11: +#if LLVM_VERSION_MAJOR >= 17 + case eLanguageTypeC17: +#endif + break; + case eLanguageTypeC_plus_plus: + case eLanguageTypeC_plus_plus_03: + case eLanguageTypeC_plus_plus_11: + case eLanguageTypeC_plus_plus_14: +#if LLVM_VERSION_MAJOR >= 17 + case eLanguageTypeC_plus_plus_17: + case eLanguageTypeC_plus_plus_20: +#endif + cplusplus = true; + break; + default: + LOG_WARNING("func %s has unsupported language_type 0x%x", + function_name, (int)language_type); + return NULL; + } + + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return NULL; + + LLVMDIBuilderRef DIB = comp_ctx->debug_builder; + LLVMMetadataRef File = comp_ctx->debug_file; /* a fallback */ + + LLVMMetadataRef ParamTypes[num_function_args + 1]; + size_t num_param_types = 0; + + if (!cplusplus) { + num_param_types = num_function_args + 1; + ParamTypes[0] = lldb_type_to_type_dbi(comp_ctx, return_type); + + for (uint32_t function_arg_idx = 0; + function_arg_idx < num_function_args; ++function_arg_idx) { + SBType function_arg_type = + function_args.GetTypeAtIndex(function_arg_idx); + + if (function_arg_type.IsValid()) { + ParamTypes[function_arg_idx + 1] = + lldb_type_to_type_dbi(comp_ctx, function_arg_type); + if (ParamTypes[function_arg_idx + 1] == NULL) { + LOG_WARNING( + "func %s arg %" PRIu32 + " has a type not implemented by lldb_type_to_type_dbi", + function_name, function_arg_idx); + } + } + else { + LOG_WARNING("func %s arg %" PRIu32 ": GetTypeAtIndex failed", + function_name, function_arg_idx); + ParamTypes[function_arg_idx + 1] = NULL; + } + } + } + + auto compile_unit = sc.GetCompileUnit(); + auto file_spec = compile_unit.GetFileSpec(); + const char *file_name = file_spec.GetFilename(); + const char *dir_name = file_spec.GetDirectory(); + LLVMMetadataRef file_info = NULL; + if (file_name || dir_name) { + file_info = + LLVMDIBuilderCreateFile(comp_ctx->debug_builder, file_name, + file_name ? strlen(file_name) : 0, dir_name, + dir_name ? strlen(dir_name) : 0); + } + if (file_info) { + File = file_info; + } + + LLVMMetadataRef FunctionTy = LLVMDIBuilderCreateSubroutineType( + DIB, File, ParamTypes, num_param_types, LLVMDIFlagZero); + + auto line_entry = sc.GetLineEntry(); + LLVMMetadataRef ReplaceableFunctionMetadata = + LLVMDIBuilderCreateReplaceableCompositeType( + DIB, 0x15, function_name, strlen(function_name), File, File, + line_entry.GetLine(), 0, 0, 0, LLVMDIFlagFwdDecl, "", 0); + + LLVMMetadataRef FunctionMetadata = LLVMDIBuilderCreateFunction( + DIB, File, function_name, strlen(function_name), link_name, + link_name != NULL ? strlen(link_name) : 0, File, line_entry.GetLine(), + FunctionTy, true, true, line_entry.GetLine(), LLVMDIFlagZero, false); + + LLVMMetadataReplaceAllUsesWith(ReplaceableFunctionMetadata, + FunctionMetadata); + + LLVMSetSubprogram(func_ctx->func, FunctionMetadata); + + LLVMMetadataRef ParamExpression = + LLVMDIBuilderCreateExpression(DIB, NULL, 0); + + LLVMMetadataRef ParamLocation = LLVMDIBuilderCreateDebugLocation( + comp_ctx->context, line_entry.GetLine(), 0, FunctionMetadata, NULL); + + // TODO:change to void * or WasmExenv * ? + LLVMMetadataRef voidtype = + LLVMDIBuilderCreateBasicType(DIB, "void", 4, 0, 0, LLVMDIFlagZero); + LLVMMetadataRef voidpointer = + LLVMDIBuilderCreatePointerType(DIB, voidtype, 64, 0, 0, "void *", 6); + + LLVMMetadataRef ParamVar = LLVMDIBuilderCreateParameterVariable( + DIB, FunctionMetadata, "exenv", 5, 1, + File, // starts form 1, and 1 is exenv, + line_entry.GetLine(), voidpointer, true, LLVMDIFlagZero); + LLVMValueRef Param = LLVMGetParam(func_ctx->func, 0); + LLVMBasicBlockRef block_curr = LLVMGetEntryBasicBlock(func_ctx->func); + LLVMDIBuilderInsertDbgValueAtEnd(DIB, Param, ParamVar, ParamExpression, + ParamLocation, block_curr); + + if (num_function_args != func_ctx->aot_func->func_type->param_count) { + // for C, this happens when the compiler optimized out some of + // function parameters. + // + // for C++, this mismatch is normal because of the "this" pointer. + if (!cplusplus) { + LOG_WARNING("function args number mismatch! num_function_args: %d, " + "wasm func params: %d, func: %s", + num_function_args, + func_ctx->aot_func->func_type->param_count, + function_name); + } + } + else if (!cplusplus) { + auto variable_list = function.GetBlock().GetVariables( + extractor->target, true, false, false); + if (num_function_args != variable_list.GetSize()) { + LOG_ERROR("function args number mismatch!:value number=%d, " + "function args=%d", + variable_list.GetSize(), num_function_args); + } + for (uint32_t function_arg_idx = 0; + function_arg_idx < variable_list.GetSize(); ++function_arg_idx) { + SBValue variable(variable_list.GetValueAtIndex(function_arg_idx)); + if (variable.IsValid() + && ParamTypes[function_arg_idx + 1] != NULL) { + SBDeclaration dec(variable.GetDeclaration()); + auto valtype = variable.GetType(); + LLVMMetadataRef ParamLocation = + LLVMDIBuilderCreateDebugLocation( + comp_ctx->context, dec.GetLine(), dec.GetColumn(), + FunctionMetadata, NULL); + const char *varname = variable.GetName(); + LLVMMetadataRef ParamVar = LLVMDIBuilderCreateParameterVariable( + DIB, FunctionMetadata, varname, + varname ? strlen(varname) : 0, function_arg_idx + 1 + 1, + File, // starts form 1, and 1 is exenv, + dec.GetLine(), ParamTypes[function_arg_idx + 1], true, + LLVMDIFlagZero); + LLVMValueRef Param = + LLVMGetParam(func_ctx->func, function_arg_idx + 1); + LLVMDIBuilderInsertDbgValueAtEnd(DIB, Param, ParamVar, + ParamExpression, ParamLocation, + block_curr); + } + } + } + + return FunctionMetadata; +} + +LLVMMetadataRef +dwarf_gen_func_info(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx) +{ + LLVMMetadataRef func_info = NULL; + dwarf_extractor *extractor; + uint64_t vm_offset; + AOTFunc *func = func_ctx->aot_func; + + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return NULL; + + // A code address in DWARF for WebAssembly is the offset of an + // instruction relative within the Code section of the WebAssembly file. + // For this reason Section::GetFileAddress() must return zero for the + // Code section. (refer to ObjectFileWasm.cpp) + vm_offset = func->code - comp_ctx->comp_data->wasm_module->buf_code; + + auto sbaddr = extractor->target.ResolveFileAddress(vm_offset); + SBSymbolContext sc(sbaddr.GetSymbolContext(eSymbolContextFunction + | eSymbolContextLineEntry)); + if (sc.IsValid()) { + SBFunction function(sc.GetFunction()); + if (function.IsValid()) { + func_info = lldb_function_to_function_dbi(comp_ctx, sc, func_ctx); + } + } + return func_info; +} + +void +dwarf_get_func_name(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, char *name, int len) +{ + LLVMMetadataRef func_info = NULL; + dwarf_extractor *extractor; + uint64_t vm_offset; + AOTFunc *func = func_ctx->aot_func; + + name[0] = '\0'; + + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return; + + // A code address in DWARF for WebAssembly is the offset of an + // instruction relative within the Code section of the WebAssembly file. + // For this reason Section::GetFileAddress() must return zero for the + // Code section. (refer to ObjectFileWasm.cpp) + vm_offset = func->code - comp_ctx->comp_data->wasm_module->buf_code; + + auto sbaddr = extractor->target.ResolveFileAddress(vm_offset); + SBSymbolContext sc(sbaddr.GetSymbolContext(eSymbolContextFunction + | eSymbolContextLineEntry)); + if (sc.IsValid()) { + SBFunction function(sc.GetFunction()); + if (function.IsValid()) { + bh_strcpy_s(name, len, function.GetName()); + } + } +} + +LLVMMetadataRef +dwarf_gen_location(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, uint64_t vm_offset) +{ + LLVMMetadataRef location_info = NULL; + dwarf_extractor *extractor; + AOTFunc *func = func_ctx->aot_func; + + if (func_ctx->debug_func == NULL) + return NULL; + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return NULL; + + auto sbaddr = extractor->target.ResolveFileAddress(vm_offset); + SBSymbolContext sc(sbaddr.GetSymbolContext(eSymbolContextFunction + | eSymbolContextLineEntry)); + if (sc.IsValid()) { + // TODO:need to check if the vm_offset is belong to + SBFunction function(sc.GetFunction()); + if (function.IsValid()) { + uint64_t start = func_ctx->aot_func->code + - comp_ctx->comp_data->wasm_module->buf_code; + uint64_t end = func_ctx->aot_func->code + - comp_ctx->comp_data->wasm_module->buf_code + + func_ctx->aot_func->code_size; + if (function.GetStartAddress().GetOffset() <= start + && end <= function.GetEndAddress().GetOffset()) { + auto line_entry = sc.GetLineEntry(); + location_info = LLVMDIBuilderCreateDebugLocation( + comp_ctx->context, line_entry.GetLine(), + line_entry.GetColumn(), func_ctx->debug_func, NULL); + // LOG_VERBOSE("Gen the location l:%d, c:%d at %lx", + // line_entry.GetLine(), line_entry.GetColumn(), vm_offset); + } + else + LOG_WARNING("the offset and function is not matched"); + } + } + return location_info; +} + +LLVMMetadataRef +dwarf_gen_func_ret_location(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx) +{ + LLVMMetadataRef func_info = NULL; + dwarf_extractor *extractor; + uint64_t vm_offset; + AOTFunc *func = func_ctx->aot_func; + LLVMMetadataRef location_info = NULL; + + if (!(extractor = TO_EXTRACTOR(comp_ctx->comp_data->extractor))) + return NULL; + + // A code address in DWARF for WebAssembly is the offset of an + // instruction relative within the Code section of the WebAssembly file. + // For this reason Section::GetFileAddress() must return zero for the + // Code section. (refer to ObjectFileWasm.cpp) + vm_offset = (func->code + func->code_size - 1) + - comp_ctx->comp_data->wasm_module->buf_code; + location_info = dwarf_gen_location(comp_ctx, func_ctx, vm_offset); + + return location_info; +} diff --git a/core/iwasm/compilation/debug/dwarf_extractor.h b/core/iwasm/compilation/debug/dwarf_extractor.h new file mode 100644 index 0000000000..0bacb97fa2 --- /dev/null +++ b/core/iwasm/compilation/debug/dwarf_extractor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _DWARF_EXTRACTOR_H_ +#define _DWARF_EXTRACTOR_H_ + +#include "llvm-c/DebugInfo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int LLDBLangType; +#define LLDB_TO_LLVM_LANG_TYPE(lldb_lang_type) \ + (LLVMDWARFSourceLanguage)(((lldb_lang_type) > 0 ? (lldb_lang_type)-1 : 1)) + +struct AOTCompData; +typedef struct AOTCompData *aot_comp_data_t; +typedef void *dwarf_extractor_handle_t; + +struct AOTCompContext; +typedef struct AOTCompContext AOTCompContext; + +struct AOTFuncContext; + +typedef struct AOTFuncContext AOTFuncContext; +dwarf_extractor_handle_t +create_dwarf_extractor(aot_comp_data_t comp_data, char *file_name); + +LLVMMetadataRef +dwarf_gen_file_info(const AOTCompContext *comp_ctx); + +LLVMMetadataRef +dwarf_gen_comp_unit_info(const AOTCompContext *comp_ctx); + +LLVMMetadataRef +dwarf_gen_func_info(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx); + +LLVMMetadataRef +dwarf_gen_location(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, uint64_t vm_offset); + +LLVMMetadataRef +dwarf_gen_func_ret_location(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx); + +void +dwarf_get_func_name(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, char *name, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/compilation/iwasm_compl.cmake b/core/iwasm/compilation/iwasm_compl.cmake index 36efd335e8..9db1d5bfe4 100644 --- a/core/iwasm/compilation/iwasm_compl.cmake +++ b/core/iwasm/compilation/iwasm_compl.cmake @@ -1,8 +1,27 @@ set (IWASM_COMPL_DIR ${CMAKE_CURRENT_LIST_DIR}) include_directories(${IWASM_COMPL_DIR}) +enable_language(CXX) -file (GLOB_RECURSE source_all ${IWASM_COMPL_DIR}/*.c) +if (WAMR_BUILD_DEBUG_AOT EQUAL 1) + file (GLOB_RECURSE source_all + ${IWASM_COMPL_DIR}/*.c + ${IWASM_COMPL_DIR}/*.cpp) +else() + file (GLOB source_all + ${IWASM_COMPL_DIR}/simd/*.c + ${IWASM_COMPL_DIR}/simd/*.cpp + ${IWASM_COMPL_DIR}/*.c + ${IWASM_COMPL_DIR}/*.cpp) +endif() set (IWASM_COMPL_SOURCE ${source_all}) +# Disable rtti to works with LLVM + +if (MSVC) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-") +else() + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif() + diff --git a/core/iwasm/compilation/simd/simd_access_lanes.c b/core/iwasm/compilation/simd/simd_access_lanes.c new file mode 100644 index 0000000000..16b0f7b6af --- /dev/null +++ b/core/iwasm/compilation/simd/simd_access_lanes.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_access_lanes.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +bool +aot_compile_simd_shuffle(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + const uint8 *frame_ip) +{ + LLVMValueRef vec1, vec2, mask, result; + uint8 imm[16] = { 0 }; + int values[16]; + unsigned i; + + wasm_runtime_read_v128(frame_ip, (uint64 *)imm, (uint64 *)(imm + 8)); + for (i = 0; i < 16; i++) { + values[i] = imm[i]; + } + + if (!(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i8x16_TYPE, + "vec2"))) { + goto fail; + } + + if (!(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i8x16_TYPE, + "vec1"))) { + goto fail; + } + + /* build a vector <16 x i32> */ + if (!(mask = simd_build_const_integer_vector(comp_ctx, I32_TYPE, values, + 16))) { + goto fail; + } + + if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, vec1, vec2, mask, + "new_vector"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + goto fail; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); + +fail: + return false; +} + +/*TODO: llvm.experimental.vector.*/ +/* shufflevector is not an option, since it requires *mask as a const */ +bool +aot_compile_simd_swizzle_x86(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef vector, mask, max_lanes, condition, mask_lanes, result; + LLVMTypeRef param_types[2]; + + if (!(mask = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i8x16_TYPE, + "mask"))) { + goto fail; + } + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i8x16_TYPE, "vec"))) { + goto fail; + } + + /* icmp uge <16 x i8> mask, <16, 16, 16, 16, ...> */ + if (!(max_lanes = simd_build_splat_const_integer_vector(comp_ctx, INT8_TYPE, + 16, 16))) { + goto fail; + } + + /* if the highest bit of every i8 of mask is 1, means doesn't pick up + from vector */ + /* select <16 x i1> %condition, <16 x i8> <0x80, 0x80, ...>, + <16 x i8> %mask */ + if (!(mask_lanes = simd_build_splat_const_integer_vector( + comp_ctx, INT8_TYPE, 0x80, 16))) { + goto fail; + } + + if (!(condition = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, mask, + max_lanes, "compare_with_16"))) { + HANDLE_FAILURE("LLVMBuildICmp"); + goto fail; + } + + if (!(mask = LLVMBuildSelect(comp_ctx->builder, condition, mask_lanes, mask, + "mask"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + goto fail; + } + + param_types[0] = V128_i8x16_TYPE; + param_types[1] = V128_i8x16_TYPE; + if (!(result = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, "llvm.x86.ssse3.pshuf.b.128", V128_i8x16_TYPE, + param_types, 2, vector, mask))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + if (!(result = LLVMBuildBitCast(comp_ctx->builder, result, V128_i64x2_TYPE, + "ret"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + PUSH_V128(result); + + return true; +fail: + return false; +} + +static bool +aot_compile_simd_swizzle_common(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef vector, mask, default_lane_value, condition, max_lane_id, + result, idx, id, replace_with_zero, elem, elem_or_zero, undef; + uint8 i; + + if (!(mask = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i8x16_TYPE, + "mask"))) { + goto fail; + } + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i8x16_TYPE, "vec"))) { + goto fail; + } + + if (!(undef = LLVMGetUndef(V128_i8x16_TYPE))) { + HANDLE_FAILURE("LLVMGetUndef"); + goto fail; + } + + /* icmp uge <16 x i8> mask, <16, 16, 16, 16, ...> */ + if (!(max_lane_id = simd_build_splat_const_integer_vector( + comp_ctx, INT8_TYPE, 16, 16))) { + goto fail; + } + + if (!(condition = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, mask, + max_lane_id, "out_of_range"))) { + HANDLE_FAILURE("LLVMBuldICmp"); + goto fail; + } + + /* if the id is out of range (>=16), set the id as 0 */ + if (!(default_lane_value = simd_build_splat_const_integer_vector( + comp_ctx, INT8_TYPE, 0, 16))) { + goto fail; + } + + if (!(idx = LLVMBuildSelect(comp_ctx->builder, condition, + default_lane_value, mask, "mask"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + goto fail; + } + + for (i = 0; i < 16; i++) { + if (!(id = LLVMBuildExtractElement(comp_ctx->builder, idx, I8_CONST(i), + "id"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + goto fail; + } + + if (!(replace_with_zero = + LLVMBuildExtractElement(comp_ctx->builder, condition, + I8_CONST(i), "replace_with_zero"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + goto fail; + } + + if (!(elem = LLVMBuildExtractElement(comp_ctx->builder, vector, id, + "vector[mask[i]]"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + goto fail; + } + + if (!(elem_or_zero = + LLVMBuildSelect(comp_ctx->builder, replace_with_zero, + I8_CONST(0), elem, "elem_or_zero"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + goto fail; + } + + if (!(undef = + LLVMBuildInsertElement(comp_ctx->builder, undef, elem_or_zero, + I8_CONST(i), "new_vector"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + goto fail; + } + } + + if (!(result = LLVMBuildBitCast(comp_ctx->builder, undef, V128_i64x2_TYPE, + "ret"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + PUSH_V128(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_swizzle(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + if (is_target_x86(comp_ctx)) { + return aot_compile_simd_swizzle_x86(comp_ctx, func_ctx); + } + else { + return aot_compile_simd_swizzle_common(comp_ctx, func_ctx); + } +} + +static bool +aot_compile_simd_extract(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 lane_id, bool need_extend, bool is_signed, + LLVMTypeRef vector_type, LLVMTypeRef result_type, + unsigned aot_value_type) +{ + LLVMValueRef vector, lane, result; + + if (!(lane = simd_lane_id_to_llvm_value(comp_ctx, lane_id))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* bitcast <2 x i64> %0 to */ + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec"))) { + goto fail; + } + + /* extractelement %vector, i8 lane_id*/ + if (!(result = LLVMBuildExtractElement(comp_ctx->builder, vector, lane, + "element"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + goto fail; + } + + if (need_extend) { + if (is_signed) { + /* sext %element to */ + if (!(result = LLVMBuildSExt(comp_ctx->builder, result, result_type, + "ret"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + goto fail; + } + } + else { + /* sext %element to */ + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, result_type, + "ret"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + goto fail; + } + } + } + + PUSH(result, aot_value_type); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_extract_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id, + bool is_signed) +{ + return aot_compile_simd_extract(comp_ctx, func_ctx, lane_id, true, + is_signed, V128_i8x16_TYPE, I32_TYPE, + VALUE_TYPE_I32); +} + +bool +aot_compile_simd_extract_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id, + bool is_signed) +{ + return aot_compile_simd_extract(comp_ctx, func_ctx, lane_id, true, + is_signed, V128_i16x8_TYPE, I32_TYPE, + VALUE_TYPE_I32); +} + +bool +aot_compile_simd_extract_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_extract(comp_ctx, func_ctx, lane_id, false, false, + V128_i32x4_TYPE, I32_TYPE, VALUE_TYPE_I32); +} + +bool +aot_compile_simd_extract_i64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_extract(comp_ctx, func_ctx, lane_id, false, false, + V128_i64x2_TYPE, I64_TYPE, VALUE_TYPE_I64); +} + +bool +aot_compile_simd_extract_f32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_extract(comp_ctx, func_ctx, lane_id, false, false, + V128_f32x4_TYPE, F32_TYPE, VALUE_TYPE_F32); +} + +bool +aot_compile_simd_extract_f64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_extract(comp_ctx, func_ctx, lane_id, false, false, + V128_f64x2_TYPE, F64_TYPE, VALUE_TYPE_F64); +} + +static bool +aot_compile_simd_replace(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 lane_id, unsigned new_value_type, + LLVMTypeRef vector_type, bool need_reduce, + LLVMTypeRef element_type) +{ + LLVMValueRef vector, new_value, lane, result; + + POP(new_value, new_value_type); + + if (!(lane = simd_lane_id_to_llvm_value(comp_ctx, lane_id))) { + goto fail; + } + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec"))) { + goto fail; + } + + /* trunc to */ + if (need_reduce) { + if (!(new_value = LLVMBuildTrunc(comp_ctx->builder, new_value, + element_type, "element"))) { + HANDLE_FAILURE("LLVMBuildTrunc"); + goto fail; + } + } + + /* insertelement %vector, %element, + i32 lane */ + if (!(result = LLVMBuildInsertElement(comp_ctx->builder, vector, new_value, + lane, "new_vector"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + goto fail; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); + +fail: + return false; +} + +bool +aot_compile_simd_replace_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_replace(comp_ctx, func_ctx, lane_id, VALUE_TYPE_I32, + V128_i8x16_TYPE, true, INT8_TYPE); +} + +bool +aot_compile_simd_replace_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_replace(comp_ctx, func_ctx, lane_id, VALUE_TYPE_I32, + V128_i16x8_TYPE, true, INT16_TYPE); +} + +bool +aot_compile_simd_replace_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_replace(comp_ctx, func_ctx, lane_id, VALUE_TYPE_I32, + V128_i32x4_TYPE, false, I32_TYPE); +} + +bool +aot_compile_simd_replace_i64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_replace(comp_ctx, func_ctx, lane_id, VALUE_TYPE_I64, + V128_i64x2_TYPE, false, I64_TYPE); +} + +bool +aot_compile_simd_replace_f32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_replace(comp_ctx, func_ctx, lane_id, VALUE_TYPE_F32, + V128_f32x4_TYPE, false, F32_TYPE); +} + +bool +aot_compile_simd_replace_f64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id) +{ + return aot_compile_simd_replace(comp_ctx, func_ctx, lane_id, VALUE_TYPE_F64, + V128_f64x2_TYPE, false, F64_TYPE); +} diff --git a/core/iwasm/compilation/simd/simd_access_lanes.h b/core/iwasm/compilation/simd/simd_access_lanes.h new file mode 100644 index 0000000000..75ca71ced7 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_access_lanes.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_ACCESS_LANES_H_ +#define _SIMD_ACCESS_LANES_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_shuffle(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + const uint8 *frame_ip); + +bool +aot_compile_simd_swizzle(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_extract_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id, + bool is_signed); + +bool +aot_compile_simd_extract_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id, + bool is_signed); + +bool +aot_compile_simd_extract_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_extract_i64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_extract_f32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_extract_f64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_replace_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_replace_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_replace_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_replace_i64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_replace_f32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_replace_f64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, uint8 lane_id); + +bool +aot_compile_simd_load8_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 lane_id); + +bool +aot_compile_simd_load16_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 lane_id); + +bool +aot_compile_simd_load32_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 lane_id); + +bool +aot_compile_simd_load64_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 lane_id); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_ACCESS_LANES_H_ */ diff --git a/core/iwasm/compilation/simd/simd_bit_shifts.c b/core/iwasm/compilation/simd/simd_bit_shifts.c new file mode 100644 index 0000000000..1d645ed714 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bit_shifts.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_bit_shifts.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +enum integer_shift { + e_shift_i8x16, + e_shift_i16x8, + e_shift_i32x4, + e_shift_i64x2, +}; + +static bool +simd_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op, enum integer_shift itype) +{ + LLVMValueRef vector, offset, result = NULL; + LLVMTypeRef vector_type[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, + V128_i32x4_TYPE, V128_i64x2_TYPE }; + LLVMTypeRef element_type[] = { INT8_TYPE, INT16_TYPE, I32_TYPE, I64_TYPE }; + + LLVMValueRef undef[] = { LLVM_CONST(i8x16_undef), LLVM_CONST(i16x8_undef), + LLVM_CONST(i32x4_undef), LLVM_CONST(i64x2_undef) }; + LLVMValueRef mask[] = { LLVM_CONST(i8x16_vec_zero), + LLVM_CONST(i16x8_vec_zero), + LLVM_CONST(i32x4_vec_zero), + LLVM_CONST(i64x2_vec_zero) }; + LLVMValueRef lane_shift_masks[] = { + LLVMConstInt(I32_TYPE, 7, true), + LLVMConstInt(I32_TYPE, 15, true), + LLVMConstInt(I32_TYPE, 31, true), + LLVMConstInt(I32_TYPE, 63, true), + }; + + POP_I32(offset); + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + vector_type[itype], "vec"))) { + return false; + } + + /* offset = offset & shift_mask */ + if (!lane_shift_masks[itype] + || !(offset = LLVMBuildAnd(comp_ctx->builder, offset, + lane_shift_masks[itype], "offset_fix"))) { + HANDLE_FAILURE("LLVMBuildAnd"); + return false; + } + + /* change type */ + if (itype < e_shift_i32x4) { + offset = LLVMBuildTrunc(comp_ctx->builder, offset, element_type[itype], + "offset_trunc"); + } + else if (itype == e_shift_i64x2) { + offset = LLVMBuildZExt(comp_ctx->builder, offset, element_type[itype], + "offset_ext"); + } + + if (!offset) { + HANDLE_FAILURE("LLVMBuildZext/LLVMBuildTrunc"); + return false; + } + + /* splat to a vector */ + if (!(offset = + LLVMBuildInsertElement(comp_ctx->builder, undef[itype], offset, + I32_ZERO, "offset_vector_base"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + return false; + } + + if (!(offset = + LLVMBuildShuffleVector(comp_ctx->builder, offset, undef[itype], + mask[itype], "offset_vector"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + switch (shift_op) { + case INT_SHL: + { + result = LLVMBuildShl(comp_ctx->builder, vector, offset, "shl"); + break; + } + case INT_SHR_S: + { + result = LLVMBuildAShr(comp_ctx->builder, vector, offset, "ashr"); + break; + } + case INT_SHR_U: + { + result = LLVMBuildLShr(comp_ctx->builder, vector, offset, "lshr"); + break; + } + default: + { + break; + } + } + + if (!result) { + HANDLE_FAILURE("LLVMBuildShl/LLVMBuildLShr/LLVMBuildAShr"); + goto fail; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); + +fail: + return false; +} + +bool +aot_compile_simd_i8x16_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op) +{ + return simd_shift(comp_ctx, func_ctx, shift_op, e_shift_i8x16); +} + +bool +aot_compile_simd_i16x8_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op) +{ + return simd_shift(comp_ctx, func_ctx, shift_op, e_shift_i16x8); +} + +bool +aot_compile_simd_i32x4_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op) +{ + return simd_shift(comp_ctx, func_ctx, shift_op, e_shift_i32x4); +} + +bool +aot_compile_simd_i64x2_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op) +{ + return simd_shift(comp_ctx, func_ctx, shift_op, e_shift_i64x2); +} diff --git a/core/iwasm/compilation/simd/simd_bit_shifts.h b/core/iwasm/compilation/simd/simd_bit_shifts.h new file mode 100644 index 0000000000..06e86cad01 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bit_shifts.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_BIT_SHIFTS_H_ +#define _SIMD_BIT_SHIFTS_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op); + +bool +aot_compile_simd_i16x8_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op); + +bool +aot_compile_simd_i32x4_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op); + +bool +aot_compile_simd_i64x2_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_BIT_SHIFTS_H_ */ diff --git a/core/iwasm/compilation/simd/simd_bitmask_extracts.c b/core/iwasm/compilation/simd/simd_bitmask_extracts.c new file mode 100644 index 0000000000..3b1e325845 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bitmask_extracts.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_bitmask_extracts.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +enum integer_bitmask_type { + e_bitmask_i8x16, + e_bitmask_i16x8, + e_bitmask_i32x4, + e_bitmask_i64x2, +}; + +/* TODO: should use a much clever intrinsic */ +static bool +simd_build_bitmask(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, + enum integer_bitmask_type itype) +{ + LLVMValueRef vector, mask, result; + uint8 i; + LLVMTypeRef vector_ext_type; + + uint32 lanes[] = { 16, 8, 4, 2 }; + uint32 lane_bits[] = { 8, 16, 32, 64 }; + LLVMTypeRef element_type[] = { INT8_TYPE, INT16_TYPE, I32_TYPE, I64_TYPE }; + LLVMTypeRef vector_type[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, + V128_i32x4_TYPE, V128_i64x2_TYPE }; + int32 mask_element[16] = { 0 }; + const char *intrinsic[] = { + "llvm.vector.reduce.or.v16i64", + "llvm.vector.reduce.or.v8i64", + "llvm.vector.reduce.or.v4i64", + "llvm.vector.reduce.or.v2i64", + }; + + LLVMValueRef ashr_distance; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + vector_type[itype], "vec"))) { + goto fail; + } + + /* fill every bit in a lane with its sign bit */ + if (!(ashr_distance = simd_build_splat_const_integer_vector( + comp_ctx, element_type[itype], lane_bits[itype] - 1, + lanes[itype]))) { + goto fail; + } + + if (!(vector = LLVMBuildAShr(comp_ctx->builder, vector, ashr_distance, + "vec_ashr"))) { + HANDLE_FAILURE("LLVMBuildAShr"); + goto fail; + } + + if (!(vector_ext_type = LLVMVectorType(I64_TYPE, lanes[itype]))) { + HANDLE_FAILURE("LLVMVectorType"); + goto fail; + } + + if (e_bitmask_i64x2 != itype) { + if (!(vector = LLVMBuildSExt(comp_ctx->builder, vector, vector_ext_type, + "zext_to_i64"))) { + goto fail; + } + } + + for (i = 0; i < 16; i++) { + mask_element[i] = 0x1 << i; + } + + if (!(mask = simd_build_const_integer_vector(comp_ctx, I64_TYPE, + mask_element, lanes[itype]))) { + goto fail; + } + + if (!(vector = + LLVMBuildAnd(comp_ctx->builder, vector, mask, "mask_bits"))) { + HANDLE_FAILURE("LLVMBuildAnd"); + goto fail; + } + + if (!(result = + aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic[itype], + I64_TYPE, &vector_ext_type, 1, vector))) { + goto fail; + } + + if (!(result = + LLVMBuildTrunc(comp_ctx->builder, result, I32_TYPE, "to_i32"))) { + HANDLE_FAILURE("LLVMBuildTrunc"); + goto fail; + } + + PUSH_I32(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_i8x16_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_build_bitmask(comp_ctx, func_ctx, e_bitmask_i8x16); +} + +bool +aot_compile_simd_i16x8_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_build_bitmask(comp_ctx, func_ctx, e_bitmask_i16x8); +} + +bool +aot_compile_simd_i32x4_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_build_bitmask(comp_ctx, func_ctx, e_bitmask_i32x4); +} + +bool +aot_compile_simd_i64x2_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_build_bitmask(comp_ctx, func_ctx, e_bitmask_i64x2); +} diff --git a/core/iwasm/compilation/simd/simd_bitmask_extracts.h b/core/iwasm/compilation/simd/simd_bitmask_extracts.h new file mode 100644 index 0000000000..aac4cc2ce5 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bitmask_extracts.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_BITMASK_EXTRACTS_H_ +#define _SIMD_BITMASK_EXTRACTS_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i16x8_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i32x4_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i64x2_bitmask(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_BITMASK_EXTRACTS_H_ */ diff --git a/core/iwasm/compilation/simd/simd_bitwise_ops.c b/core/iwasm/compilation/simd/simd_bitwise_ops.c new file mode 100644 index 0000000000..66aef3637e --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bitwise_ops.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_bitwise_ops.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +static bool +v128_bitwise_two_component(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Bitwise bitwise_op) +{ + LLVMValueRef vector1, vector2, result; + + POP_V128(vector2); + POP_V128(vector1); + + switch (bitwise_op) { + case V128_AND: + if (!(result = LLVMBuildAnd(comp_ctx->builder, vector1, vector2, + "and"))) { + HANDLE_FAILURE("LLVMBuildAnd"); + goto fail; + } + break; + case V128_OR: + if (!(result = + LLVMBuildOr(comp_ctx->builder, vector1, vector2, "or"))) { + HANDLE_FAILURE("LLVMBuildAnd"); + goto fail; + } + break; + case V128_XOR: + if (!(result = LLVMBuildXor(comp_ctx->builder, vector1, vector2, + "xor"))) { + HANDLE_FAILURE("LLVMBuildAnd"); + goto fail; + } + break; + case V128_ANDNOT: + { + /* v128.and(a, v128.not(b)) */ + if (!(vector2 = LLVMBuildNot(comp_ctx->builder, vector2, "not"))) { + HANDLE_FAILURE("LLVMBuildNot"); + goto fail; + } + + if (!(result = LLVMBuildAnd(comp_ctx->builder, vector1, vector2, + "and"))) { + HANDLE_FAILURE("LLVMBuildAnd"); + goto fail; + } + + break; + } + default: + bh_assert(0); + goto fail; + } + + PUSH_V128(result); + return true; +fail: + return false; +} + +static bool +v128_bitwise_not(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef vector, result; + + POP_V128(vector); + + if (!(result = LLVMBuildNot(comp_ctx->builder, vector, "not"))) { + HANDLE_FAILURE("LLVMBuildNot"); + goto fail; + } + + PUSH_V128(result); + return true; +fail: + return false; +} + +/* v128.or(v128.and(v1, c), v128.and(v2, v128.not(c))) */ +static bool +v128_bitwise_bitselect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef vector1, vector2, vector3, result; + + POP_V128(vector3); + POP_V128(vector2); + POP_V128(vector1); + + if (!(vector1 = + LLVMBuildAnd(comp_ctx->builder, vector1, vector3, "a_and_c"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + goto fail; + } + + if (!(vector3 = LLVMBuildNot(comp_ctx->builder, vector3, "not_c"))) { + HANDLE_FAILURE("LLVMBuildNot"); + goto fail; + } + + if (!(vector2 = + LLVMBuildAnd(comp_ctx->builder, vector2, vector3, "b_and_c"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + goto fail; + } + + if (!(result = + LLVMBuildOr(comp_ctx->builder, vector1, vector2, "a_or_b"))) { + HANDLE_FAILURE("LLVMBuildOr"); + goto fail; + } + + PUSH_V128(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_v128_bitwise(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, V128Bitwise bitwise_op) +{ + switch (bitwise_op) { + case V128_AND: + case V128_OR: + case V128_XOR: + case V128_ANDNOT: + return v128_bitwise_two_component(comp_ctx, func_ctx, bitwise_op); + case V128_NOT: + return v128_bitwise_not(comp_ctx, func_ctx); + case V128_BITSELECT: + return v128_bitwise_bitselect(comp_ctx, func_ctx); + default: + bh_assert(0); + return false; + } +} diff --git a/core/iwasm/compilation/simd/simd_bitwise_ops.h b/core/iwasm/compilation/simd/simd_bitwise_ops.h new file mode 100644 index 0000000000..ddf81c0b7d --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bitwise_ops.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_BITWISE_OPS_H_ +#define _SIMD_BITWISE_OPS_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_v128_bitwise(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, V128Bitwise bitwise_op); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_BITWISE_OPS_H_ */ diff --git a/core/iwasm/compilation/simd/simd_bool_reductions.c b/core/iwasm/compilation/simd/simd_bool_reductions.c new file mode 100644 index 0000000000..4607d680ae --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bool_reductions.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_bool_reductions.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +enum integer_all_true { + e_int_all_true_v16i8, + e_int_all_true_v8i16, + e_int_all_true_v4i32, + e_int_all_true_v2i64, +}; + +static bool +simd_all_true(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + enum integer_all_true itype) +{ + LLVMValueRef vector, result; + LLVMTypeRef vector_i1_type; + LLVMTypeRef vector_type[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, + V128_i32x4_TYPE, V128_i64x2_TYPE }; + uint32 lanes[] = { 16, 8, 4, 2 }; + const char *intrinsic[] = { + "llvm.vector.reduce.and.v16i1", + "llvm.vector.reduce.and.v8i1", + "llvm.vector.reduce.and.v4i1", + "llvm.vector.reduce.and.v2i1", + }; + LLVMValueRef zero[] = { + LLVM_CONST(i8x16_vec_zero), + LLVM_CONST(i16x8_vec_zero), + LLVM_CONST(i32x4_vec_zero), + LLVM_CONST(i64x2_vec_zero), + }; + + if (!(vector_i1_type = LLVMVectorType(INT1_TYPE, lanes[itype]))) { + HANDLE_FAILURE("LLVMVectorType"); + goto fail; + } + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + vector_type[itype], "vector"))) { + goto fail; + } + + /* compare with zero */ + if (!(result = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, vector, + zero[itype], "ne_zero"))) { + HANDLE_FAILURE("LLVMBuildICmp"); + goto fail; + } + + /* check zero */ + if (!(result = + aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic[itype], + INT1_TYPE, &vector_i1_type, 1, result))) { + goto fail; + } + + if (!(result = + LLVMBuildZExt(comp_ctx->builder, result, I32_TYPE, "to_i32"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + goto fail; + } + + PUSH_I32(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_i8x16_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_all_true(comp_ctx, func_ctx, e_int_all_true_v16i8); +} + +bool +aot_compile_simd_i16x8_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_all_true(comp_ctx, func_ctx, e_int_all_true_v8i16); +} + +bool +aot_compile_simd_i32x4_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_all_true(comp_ctx, func_ctx, e_int_all_true_v4i32); +} + +bool +aot_compile_simd_i64x2_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_all_true(comp_ctx, func_ctx, e_int_all_true_v2i64); +} + +bool +aot_compile_simd_v128_any_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMTypeRef vector_type; + LLVMValueRef vector, result; + + if (!(vector_type = LLVMVectorType(INT1_TYPE, 128))) { + return false; + } + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vector"))) { + goto fail; + } + + if (!(result = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, "llvm.vector.reduce.or.v128i1", INT1_TYPE, + &vector_type, 1, vector))) { + goto fail; + } + + if (!(result = + LLVMBuildZExt(comp_ctx->builder, result, I32_TYPE, "to_i32"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + goto fail; + } + + PUSH_I32(result); + + return true; +fail: + return false; +} diff --git a/core/iwasm/compilation/simd/simd_bool_reductions.h b/core/iwasm/compilation/simd/simd_bool_reductions.h new file mode 100644 index 0000000000..649d5a5e2e --- /dev/null +++ b/core/iwasm/compilation/simd/simd_bool_reductions.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_BOOL_REDUCTIONS_H_ +#define _SIMD_BOOL_REDUCTIONS_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i16x8_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i32x4_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i64x2_all_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_v128_any_true(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_BOOL_REDUCTIONS_H_ */ diff --git a/core/iwasm/compilation/simd/simd_common.c b/core/iwasm/compilation/simd/simd_common.c new file mode 100644 index 0000000000..c495ee4104 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_common.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_common.h" + +LLVMValueRef +simd_pop_v128_and_bitcast(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, LLVMTypeRef vec_type, + const char *name) +{ + LLVMValueRef number; + + POP_V128(number); + + if (!(number = + LLVMBuildBitCast(comp_ctx->builder, number, vec_type, name))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + return number; +fail: + return NULL; +} + +bool +simd_bitcast_and_push_v128(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, LLVMValueRef vector, + const char *name) +{ + if (!(vector = LLVMBuildBitCast(comp_ctx->builder, vector, V128_i64x2_TYPE, + name))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + /* push result into the stack */ + PUSH_V128(vector); + + return true; +fail: + return false; +} + +LLVMValueRef +simd_lane_id_to_llvm_value(AOTCompContext *comp_ctx, uint8 lane_id) +{ + LLVMValueRef lane_indexes[] = { + LLVM_CONST(i32_zero), LLVM_CONST(i32_one), + LLVM_CONST(i32_two), LLVM_CONST(i32_three), + LLVM_CONST(i32_four), LLVM_CONST(i32_five), + LLVM_CONST(i32_six), LLVM_CONST(i32_seven), + LLVM_CONST(i32_eight), LLVM_CONST(i32_nine), + LLVM_CONST(i32_ten), LLVM_CONST(i32_eleven), + LLVM_CONST(i32_twelve), LLVM_CONST(i32_thirteen), + LLVM_CONST(i32_fourteen), LLVM_CONST(i32_fifteen), + }; + + return lane_id < 16 ? lane_indexes[lane_id] : NULL; +} + +LLVMValueRef +simd_build_const_integer_vector(const AOTCompContext *comp_ctx, + const LLVMTypeRef element_type, + const int *element_value, uint32 length) +{ + LLVMValueRef vector = NULL; + LLVMValueRef *elements; + unsigned i; + + if (!(elements = wasm_runtime_malloc(sizeof(LLVMValueRef) * length))) { + return NULL; + } + + for (i = 0; i < length; i++) { + if (!(elements[i] = + LLVMConstInt(element_type, element_value[i], true))) { + HANDLE_FAILURE("LLVMConstInst"); + goto fail; + } + } + + if (!(vector = LLVMConstVector(elements, length))) { + HANDLE_FAILURE("LLVMConstVector"); + goto fail; + } + +fail: + wasm_runtime_free(elements); + return vector; +} + +LLVMValueRef +simd_build_splat_const_integer_vector(const AOTCompContext *comp_ctx, + const LLVMTypeRef element_type, + const int64 element_value, uint32 length) +{ + LLVMValueRef vector = NULL, element; + LLVMValueRef *elements; + unsigned i; + + if (!(elements = wasm_runtime_malloc(sizeof(LLVMValueRef) * length))) { + return NULL; + } + + if (!(element = LLVMConstInt(element_type, element_value, true))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + for (i = 0; i < length; i++) { + elements[i] = element; + } + + if (!(vector = LLVMConstVector(elements, length))) { + HANDLE_FAILURE("LLVMConstVector"); + goto fail; + } + +fail: + wasm_runtime_free(elements); + return vector; +} + +LLVMValueRef +simd_build_splat_const_float_vector(const AOTCompContext *comp_ctx, + const LLVMTypeRef element_type, + const float element_value, uint32 length) +{ + LLVMValueRef vector = NULL, element; + LLVMValueRef *elements; + unsigned i; + + if (!(elements = wasm_runtime_malloc(sizeof(LLVMValueRef) * length))) { + return NULL; + } + + if (!(element = LLVMConstReal(element_type, (double)element_value))) { + HANDLE_FAILURE("LLVMConstReal"); + goto fail; + } + + for (i = 0; i < length; i++) { + elements[i] = element; + } + + if (!(vector = LLVMConstVector(elements, length))) { + HANDLE_FAILURE("LLVMConstVector"); + goto fail; + } + +fail: + wasm_runtime_free(elements); + return vector; +} diff --git a/core/iwasm/compilation/simd/simd_common.h b/core/iwasm/compilation/simd/simd_common.h new file mode 100644 index 0000000000..c7a08dbc73 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_common.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_COMMON_H_ +#define _SIMD_COMMON_H_ + +#include "../aot_compiler.h" + +static inline bool +is_target_x86(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "x86_64", 6) + || !strncmp(comp_ctx->target_arch, "i386", 4); +} + +LLVMValueRef +simd_pop_v128_and_bitcast(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, LLVMTypeRef vec_type, + const char *name); + +bool +simd_bitcast_and_push_v128(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, LLVMValueRef vector, + const char *name); + +LLVMValueRef +simd_lane_id_to_llvm_value(AOTCompContext *comp_ctx, uint8 lane_id); + +LLVMValueRef +simd_build_const_integer_vector(const AOTCompContext *comp_ctx, + const LLVMTypeRef element_type, + const int *element_value, uint32 length); + +LLVMValueRef +simd_build_splat_const_integer_vector(const AOTCompContext *comp_ctx, + const LLVMTypeRef element_type, + const int64 element_value, uint32 length); + +LLVMValueRef +simd_build_splat_const_float_vector(const AOTCompContext *comp_ctx, + const LLVMTypeRef element_type, + const float element_value, uint32 length); +#endif /* _SIMD_COMMON_H_ */ \ No newline at end of file diff --git a/core/iwasm/compilation/simd/simd_comparisons.c b/core/iwasm/compilation/simd/simd_comparisons.c new file mode 100644 index 0000000000..b7888c6dc8 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_comparisons.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_comparisons.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +static bool +float_cond_2_predicate(FloatCond cond, LLVMRealPredicate *out) +{ + switch (cond) { + case FLOAT_EQ: + *out = LLVMRealOEQ; + break; + case FLOAT_NE: + *out = LLVMRealUNE; + break; + case FLOAT_LT: + *out = LLVMRealOLT; + break; + case FLOAT_GT: + *out = LLVMRealOGT; + break; + case FLOAT_LE: + *out = LLVMRealOLE; + break; + case FLOAT_GE: + *out = LLVMRealOGE; + break; + default: + bh_assert(0); + goto fail; + } + + return true; +fail: + return false; +} + +static bool +int_cond_2_predicate(IntCond cond, LLVMIntPredicate *out) +{ + switch (cond) { + case INT_EQZ: + case INT_EQ: + *out = LLVMIntEQ; + break; + case INT_NE: + *out = LLVMIntNE; + break; + case INT_LT_S: + *out = LLVMIntSLT; + break; + case INT_LT_U: + *out = LLVMIntULT; + break; + case INT_GT_S: + *out = LLVMIntSGT; + break; + case INT_GT_U: + *out = LLVMIntUGT; + break; + case INT_LE_S: + *out = LLVMIntSLE; + break; + case INT_LE_U: + *out = LLVMIntULE; + break; + case INT_GE_S: + *out = LLVMIntSGE; + break; + case INT_GE_U: + *out = LLVMIntUGE; + break; + default: + bh_assert(0); + goto fail; + } + + return true; +fail: + return false; +} + +static bool +integer_vector_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntCond cond, LLVMTypeRef vector_type) +{ + LLVMValueRef vec1, vec2, result; + LLVMIntPredicate int_pred; + + if (!(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec2"))) { + goto fail; + } + + if (!(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec1"))) { + goto fail; + } + + if (!int_cond_2_predicate(cond, &int_pred)) { + HANDLE_FAILURE("int_cond_2_predicate"); + goto fail; + } + /* icmp %vec1, %vec2 */ + if (!(result = + LLVMBuildICmp(comp_ctx->builder, int_pred, vec1, vec2, "cmp"))) { + HANDLE_FAILURE("LLVMBuildICmp"); + goto fail; + } + + /* sext %result to */ + if (!(result = + LLVMBuildSExt(comp_ctx->builder, result, vector_type, "ext"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + goto fail; + } + + /* bitcast %result to <2 x i64> */ + if (!(result = LLVMBuildBitCast(comp_ctx->builder, result, V128_i64x2_TYPE, + "result"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + PUSH_V128(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_i8x16_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond) +{ + return integer_vector_compare(comp_ctx, func_ctx, cond, V128_i8x16_TYPE); +} + +bool +aot_compile_simd_i16x8_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond) +{ + return integer_vector_compare(comp_ctx, func_ctx, cond, V128_i16x8_TYPE); +} + +bool +aot_compile_simd_i32x4_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond) +{ + return integer_vector_compare(comp_ctx, func_ctx, cond, V128_i32x4_TYPE); +} + +bool +aot_compile_simd_i64x2_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond) +{ + return integer_vector_compare(comp_ctx, func_ctx, cond, V128_i64x2_TYPE); +} + +static bool +float_vector_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatCond cond, LLVMTypeRef vector_type, + LLVMTypeRef result_type) +{ + LLVMValueRef vec1, vec2, result; + LLVMRealPredicate real_pred; + + if (!(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec2"))) { + goto fail; + } + + if (!(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec1"))) { + goto fail; + } + + if (!float_cond_2_predicate(cond, &real_pred)) { + HANDLE_FAILURE("float_cond_2_predicate"); + goto fail; + } + /* fcmp %vec1, %vec2 */ + if (!(result = + LLVMBuildFCmp(comp_ctx->builder, real_pred, vec1, vec2, "cmp"))) { + HANDLE_FAILURE("LLVMBuildFCmp"); + goto fail; + } + + /* sext %result to */ + if (!(result = + LLVMBuildSExt(comp_ctx->builder, result, result_type, "ext"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + goto fail; + } + + /* bitcast %result to <2 x i64> */ + if (!(result = LLVMBuildBitCast(comp_ctx->builder, result, V128_i64x2_TYPE, + "result"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + PUSH_V128(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_f32x4_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, FloatCond cond) +{ + return float_vector_compare(comp_ctx, func_ctx, cond, V128_f32x4_TYPE, + V128_i32x4_TYPE); +} + +bool +aot_compile_simd_f64x2_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, FloatCond cond) +{ + return float_vector_compare(comp_ctx, func_ctx, cond, V128_f64x2_TYPE, + V128_i64x2_TYPE); +} diff --git a/core/iwasm/compilation/simd/simd_comparisons.h b/core/iwasm/compilation/simd/simd_comparisons.h new file mode 100644 index 0000000000..322ebefb28 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_comparisons.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_COMPARISONS_H_ +#define _SIMD_COMPARISONS_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond); + +bool +aot_compile_simd_i16x8_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond); + +bool +aot_compile_simd_i32x4_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond); + +bool +aot_compile_simd_i64x2_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, IntCond cond); + +bool +aot_compile_simd_f32x4_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, FloatCond cond); + +bool +aot_compile_simd_f64x2_compare(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, FloatCond cond); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_COMPARISONS_H_ */ diff --git a/core/iwasm/compilation/simd/simd_construct_values.c b/core/iwasm/compilation/simd/simd_construct_values.c new file mode 100644 index 0000000000..ceb09e370f --- /dev/null +++ b/core/iwasm/compilation/simd/simd_construct_values.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_common.h" +#include "simd_construct_values.h" +#include "../aot_emit_exception.h" +#include "../interpreter/wasm_opcode.h" +#include "../../aot/aot_runtime.h" + +bool +aot_compile_simd_v128_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + const uint8 *imm_bytes) +{ + uint64 imm1, imm2; + LLVMValueRef first_long, agg1, second_long, agg2; + + wasm_runtime_read_v128(imm_bytes, &imm1, &imm2); + + /* %agg1 = insertelement <2 x i64> undef, i16 0, i64 ${*imm} */ + if (!(first_long = I64_CONST(imm1))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(agg1 = + LLVMBuildInsertElement(comp_ctx->builder, LLVM_CONST(i64x2_undef), + first_long, I32_ZERO, "agg1"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + goto fail; + } + + /* %agg2 = insertelement <2 x i64> %agg1, i16 1, i64 ${*(imm + 1)} */ + if (!(second_long = I64_CONST(imm2))) { + HANDLE_FAILURE("LLVMGetUndef"); + goto fail; + } + + if (!(agg2 = LLVMBuildInsertElement(comp_ctx->builder, agg1, second_long, + I32_ONE, "agg2"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + goto fail; + } + + PUSH_V128(agg2); + return true; +fail: + return false; +} + +bool +aot_compile_simd_splat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode) +{ + uint32 opcode_index = opcode - SIMD_i8x16_splat; + LLVMValueRef value = NULL, base, new_vector; + LLVMValueRef undefs[] = { + LLVM_CONST(i8x16_undef), LLVM_CONST(i16x8_undef), + LLVM_CONST(i32x4_undef), LLVM_CONST(i64x2_undef), + LLVM_CONST(f32x4_undef), LLVM_CONST(f64x2_undef), + }; + LLVMValueRef masks[] = { + LLVM_CONST(i32x16_zero), LLVM_CONST(i32x8_zero), LLVM_CONST(i32x4_zero), + LLVM_CONST(i32x2_zero), LLVM_CONST(i32x4_zero), LLVM_CONST(i32x2_zero), + }; + + switch (opcode) { + case SIMD_i8x16_splat: + { + LLVMValueRef input; + POP_I32(input); + /* trunc i32 %input to i8 */ + value = + LLVMBuildTrunc(comp_ctx->builder, input, INT8_TYPE, "trunc"); + break; + } + case SIMD_i16x8_splat: + { + LLVMValueRef input; + POP_I32(input); + /* trunc i32 %input to i16 */ + value = + LLVMBuildTrunc(comp_ctx->builder, input, INT16_TYPE, "trunc"); + break; + } + case SIMD_i32x4_splat: + { + POP_I32(value); + break; + } + case SIMD_i64x2_splat: + { + POP(value, VALUE_TYPE_I64); + break; + } + case SIMD_f32x4_splat: + { + POP(value, VALUE_TYPE_F32); + break; + } + case SIMD_f64x2_splat: + { + POP(value, VALUE_TYPE_F64); + break; + } + default: + { + break; + } + } + + if (!value) { + goto fail; + } + + /* insertelement undef, ty %value, i32 0 */ + if (!(base = LLVMBuildInsertElement(comp_ctx->builder, undefs[opcode_index], + value, I32_ZERO, "base"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + goto fail; + } + + /* shufflevector %base, undef, zeroinitializer */ + if (!(new_vector = LLVMBuildShuffleVector( + comp_ctx->builder, base, undefs[opcode_index], + masks[opcode_index], "new_vector"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + goto fail; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, new_vector, "result"); +fail: + return false; +} diff --git a/core/iwasm/compilation/simd/simd_construct_values.h b/core/iwasm/compilation/simd/simd_construct_values.h new file mode 100644 index 0000000000..8cd50c88bc --- /dev/null +++ b/core/iwasm/compilation/simd/simd_construct_values.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_CONSTRUCT_VALUES_H_ +#define _SIMD_CONSTRUCT_VALUES_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_v128_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + const uint8 *imm_bytes); + +bool +aot_compile_simd_splat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 splat_opcode); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_CONSTRUCT_VALUES_H_ */ diff --git a/core/iwasm/compilation/simd/simd_conversions.c b/core/iwasm/compilation/simd/simd_conversions.c new file mode 100644 index 0000000000..56cfe2ad11 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_conversions.c @@ -0,0 +1,738 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_conversions.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../aot_emit_numberic.h" +#include "../../aot/aot_runtime.h" + +static bool +simd_integer_narrow_x86(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef in_vector_type, LLVMTypeRef out_vector_type, + const char *intrinsic) +{ + LLVMValueRef vector1, vector2, result; + LLVMTypeRef param_types[2] = { in_vector_type, in_vector_type }; + + if (!(vector2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type, "vec2")) + || !(vector1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type, "vec1"))) { + return false; + } + + if (!(result = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, + out_vector_type, param_types, 2, + vector1, vector2))) { + HANDLE_FAILURE("LLVMBuildCall"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +enum integer_sat_type { + e_sat_i16x8 = 0, + e_sat_i32x4, + e_sat_i64x2, + e_sat_i32x8, +}; + +static LLVMValueRef +simd_saturate(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + enum integer_sat_type itype, LLVMValueRef vector, + LLVMValueRef min, LLVMValueRef max, bool is_signed) +{ + LLVMValueRef result; + LLVMTypeRef vector_type; + + LLVMTypeRef param_types[][2] = { + { V128_i16x8_TYPE, V128_i16x8_TYPE }, + { V128_i32x4_TYPE, V128_i32x4_TYPE }, + { V128_i64x2_TYPE, V128_i64x2_TYPE }, + { 0 }, + }; + + const char *smin_intrinsic[] = { + "llvm.smin.v8i16", + "llvm.smin.v4i32", + "llvm.smin.v2i64", + "llvm.smin.v8i32", + }; + + const char *umin_intrinsic[] = { + "llvm.umin.v8i16", + "llvm.umin.v4i32", + "llvm.umin.v2i64", + "llvm.umin.v8i32", + }; + + const char *smax_intrinsic[] = { + "llvm.smax.v8i16", + "llvm.smax.v4i32", + "llvm.smax.v2i64", + "llvm.smax.v8i32", + }; + + const char *umax_intrinsic[] = { + "llvm.umax.v8i16", + "llvm.umax.v4i32", + "llvm.umax.v2i64", + "llvm.umax.v8i32", + }; + + if (e_sat_i32x8 == itype) { + if (!(vector_type = LLVMVectorType(I32_TYPE, 8))) { + HANDLE_FAILURE("LLVMVectorType"); + return NULL; + } + + param_types[itype][0] = vector_type; + param_types[itype][1] = vector_type; + } + + if (!(result = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, + is_signed ? smin_intrinsic[itype] : umin_intrinsic[itype], + param_types[itype][0], param_types[itype], 2, vector, max)) + || !(result = aot_call_llvm_intrinsic( + comp_ctx, func_ctx, + is_signed ? smax_intrinsic[itype] : umax_intrinsic[itype], + param_types[itype][0], param_types[itype], 2, result, min))) { + return NULL; + } + + return result; +} + +static bool +simd_integer_narrow_common(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + enum integer_sat_type itype, bool is_signed) +{ + LLVMValueRef vec1, vec2, min, max, mask, result; + LLVMTypeRef in_vector_type[] = { V128_i16x8_TYPE, V128_i32x4_TYPE, + V128_i64x2_TYPE }; + LLVMTypeRef min_max_type[] = { INT16_TYPE, I32_TYPE, I64_TYPE }; + LLVMTypeRef trunc_type[3] = { 0 }; + uint8 length[] = { 8, 4, 2 }; + + int64 smin[] = { 0xff80, 0xffFF8000, 0xffFFffFF80000000 }; + int64 umin[] = { 0x0, 0x0, 0x0 }; + int64 smax[] = { 0x007f, 0x00007fff, 0x000000007fFFffFF }; + int64 umax[] = { 0x00ff, 0x0000ffff, 0x00000000ffFFffFF }; + + LLVMValueRef mask_element[] = { + LLVM_CONST(i32_zero), LLVM_CONST(i32_one), + LLVM_CONST(i32_two), LLVM_CONST(i32_three), + LLVM_CONST(i32_four), LLVM_CONST(i32_five), + LLVM_CONST(i32_six), LLVM_CONST(i32_seven), + LLVM_CONST(i32_eight), LLVM_CONST(i32_nine), + LLVM_CONST(i32_ten), LLVM_CONST(i32_eleven), + LLVM_CONST(i32_twelve), LLVM_CONST(i32_thirteen), + LLVM_CONST(i32_fourteen), LLVM_CONST(i32_fifteen), + }; + + if (!(trunc_type[0] = LLVMVectorType(INT8_TYPE, 8)) + || !(trunc_type[1] = LLVMVectorType(INT16_TYPE, 4)) + || !(trunc_type[2] = LLVMVectorType(I32_TYPE, 2))) { + HANDLE_FAILURE("LLVMVectorType"); + return false; + } + + if (!(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type[itype], "vec2")) + || !(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type[itype], "vec1"))) { + return false; + } + + if (!(max = simd_build_splat_const_integer_vector( + comp_ctx, min_max_type[itype], + is_signed ? smax[itype] : umax[itype], length[itype])) + || !(min = simd_build_splat_const_integer_vector( + comp_ctx, min_max_type[itype], + is_signed ? smin[itype] : umin[itype], length[itype]))) { + return false; + } + + /* Refer to: + * https://github.com/WebAssembly/spec/blob/main/proposals/simd/SIMD.md#integer-to-integer-narrowing + * Regardless of the whether the operation is signed or unsigned, the input + * lanes are interpreted as signed integers. + */ + if (!(vec1 = simd_saturate(comp_ctx, func_ctx, e_sat_i16x8, vec1, min, max, + true)) + || !(vec2 = simd_saturate(comp_ctx, func_ctx, e_sat_i16x8, vec2, min, + max, true))) { + return false; + } + + /* trunc */ + if (!(vec1 = LLVMBuildTrunc(comp_ctx->builder, vec1, trunc_type[itype], + "vec1_trunc")) + || !(vec2 = LLVMBuildTrunc(comp_ctx->builder, vec2, trunc_type[itype], + "vec2_trunc"))) { + HANDLE_FAILURE("LLVMBuildTrunc"); + return false; + } + + /* combine */ + if (!(mask = LLVMConstVector(mask_element, (length[itype] << 1)))) { + HANDLE_FAILURE("LLVMConstInt"); + return false; + } + + if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, vec1, vec2, mask, + "vec_shuffle"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_narrow_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed) +{ + if (is_target_x86(comp_ctx)) { + return simd_integer_narrow_x86( + comp_ctx, func_ctx, V128_i16x8_TYPE, V128_i8x16_TYPE, + is_signed ? "llvm.x86.sse2.packsswb.128" + : "llvm.x86.sse2.packuswb.128"); + } + else { + return simd_integer_narrow_common(comp_ctx, func_ctx, e_sat_i16x8, + is_signed); + } +} + +bool +aot_compile_simd_i16x8_narrow_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed) +{ + if (is_target_x86(comp_ctx)) { + return simd_integer_narrow_x86(comp_ctx, func_ctx, V128_i32x4_TYPE, + V128_i16x8_TYPE, + is_signed ? "llvm.x86.sse2.packssdw.128" + : "llvm.x86.sse41.packusdw"); + } + else { + return simd_integer_narrow_common(comp_ctx, func_ctx, e_sat_i32x4, + is_signed); + } +} + +enum integer_extend_type { + e_ext_i8x16, + e_ext_i16x8, + e_ext_i32x4, +}; + +static LLVMValueRef +simd_integer_extension(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + enum integer_extend_type itype, LLVMValueRef vector, + bool lower_half, bool is_signed) +{ + LLVMValueRef mask, sub_vector, result; + LLVMValueRef bits[] = { + LLVM_CONST(i32_zero), LLVM_CONST(i32_one), + LLVM_CONST(i32_two), LLVM_CONST(i32_three), + LLVM_CONST(i32_four), LLVM_CONST(i32_five), + LLVM_CONST(i32_six), LLVM_CONST(i32_seven), + LLVM_CONST(i32_eight), LLVM_CONST(i32_nine), + LLVM_CONST(i32_ten), LLVM_CONST(i32_eleven), + LLVM_CONST(i32_twelve), LLVM_CONST(i32_thirteen), + LLVM_CONST(i32_fourteen), LLVM_CONST(i32_fifteen), + }; + LLVMTypeRef out_vector_type[] = { V128_i16x8_TYPE, V128_i32x4_TYPE, + V128_i64x2_TYPE }; + LLVMValueRef undef[] = { LLVM_CONST(i8x16_undef), LLVM_CONST(i16x8_undef), + LLVM_CONST(i32x4_undef) }; + uint32 sub_vector_length[] = { 8, 4, 2 }; + + if (!(mask = lower_half ? LLVMConstVector(bits, sub_vector_length[itype]) + : LLVMConstVector(bits + sub_vector_length[itype], + sub_vector_length[itype]))) { + HANDLE_FAILURE("LLVMConstVector"); + return false; + } + + /* retrieve the low or high half */ + if (!(sub_vector = LLVMBuildShuffleVector(comp_ctx->builder, vector, + undef[itype], mask, "half"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + if (is_signed) { + if (!(result = LLVMBuildSExt(comp_ctx->builder, sub_vector, + out_vector_type[itype], "sext"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + return false; + } + } + else { + if (!(result = LLVMBuildZExt(comp_ctx->builder, sub_vector, + out_vector_type[itype], "zext"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + return false; + } + } + + return result; +} + +static bool +simd_integer_extension_wrapper(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + enum integer_extend_type itype, bool lower_half, + bool is_signed) +{ + LLVMValueRef vector, result; + + LLVMTypeRef in_vector_type[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, + V128_i32x4_TYPE }; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type[itype], "vec"))) { + return false; + } + + if (!(result = simd_integer_extension(comp_ctx, func_ctx, itype, vector, + lower_half, is_signed))) { + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i16x8_extend_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed) +{ + return simd_integer_extension_wrapper(comp_ctx, func_ctx, e_ext_i8x16, + lower_half, is_signed); +} + +bool +aot_compile_simd_i32x4_extend_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed) +{ + return simd_integer_extension_wrapper(comp_ctx, func_ctx, e_ext_i16x8, + lower_half, is_signed); +} + +bool +aot_compile_simd_i64x2_extend_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed) +{ + return simd_integer_extension_wrapper(comp_ctx, func_ctx, e_ext_i32x4, + lower_half, is_signed); +} + +static LLVMValueRef +simd_trunc_sat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + const char *intrinsics, LLVMTypeRef in_vector_type, + LLVMTypeRef out_vector_type) +{ + LLVMValueRef vector, result; + LLVMTypeRef param_types[] = { in_vector_type }; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, in_vector_type, + "vector"))) { + return false; + } + + if (!(result = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsics, + out_vector_type, param_types, 1, + vector))) { + return false; + } + + return result; +} + +bool +aot_compile_simd_i32x4_trunc_sat_f32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed) +{ + LLVMValueRef result; + if (!(result = simd_trunc_sat(comp_ctx, func_ctx, + is_signed ? "llvm.fptosi.sat.v4i32.v4f32" + : "llvm.fptoui.sat.v4i32.v4f32", + V128_f32x4_TYPE, V128_i32x4_TYPE))) { + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i32x4_trunc_sat_f64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed) +{ + LLVMValueRef result, zero, mask; + LLVMTypeRef out_vector_type; + LLVMValueRef lanes[] = { + LLVM_CONST(i32_zero), + LLVM_CONST(i32_one), + LLVM_CONST(i32_two), + LLVM_CONST(i32_three), + }; + + if (!(out_vector_type = LLVMVectorType(I32_TYPE, 2))) { + HANDLE_FAILURE("LLVMVectorType"); + return false; + } + + if (!(result = simd_trunc_sat(comp_ctx, func_ctx, + is_signed ? "llvm.fptosi.sat.v2i32.v2f64" + : "llvm.fptoui.sat.v2i32.v2f64", + V128_f64x2_TYPE, out_vector_type))) { + return false; + } + + if (!(zero = LLVMConstNull(out_vector_type))) { + HANDLE_FAILURE("LLVMConstNull"); + return false; + } + + /* v2i32 -> v4i32 */ + if (!(mask = LLVMConstVector(lanes, 4))) { + HANDLE_FAILURE("LLVMConstVector"); + return false; + } + + if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, result, zero, mask, + "extend"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +static LLVMValueRef +simd_integer_convert(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_signed, LLVMValueRef vector, + LLVMTypeRef out_vector_type) + +{ + LLVMValueRef result; + result = is_signed ? LLVMBuildSIToFP(comp_ctx->builder, vector, + out_vector_type, "converted") + : LLVMBuildUIToFP(comp_ctx->builder, vector, + out_vector_type, "converted"); + if (!result) { + HANDLE_FAILURE("LLVMBuildSIToFP/LLVMBuildUIToFP"); + } + + return result; +} + +bool +aot_compile_simd_f32x4_convert_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed) +{ + LLVMValueRef vector, result; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i32x4_TYPE, "vec"))) { + return false; + } + + if (!(result = simd_integer_convert(comp_ctx, func_ctx, is_signed, vector, + V128_f32x4_TYPE))) { + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_f64x2_convert_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed) +{ + LLVMValueRef vector, mask, result; + LLVMValueRef lanes[] = { + LLVM_CONST(i32_zero), + LLVM_CONST(i32_one), + }; + LLVMTypeRef out_vector_type; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i32x4_TYPE, "vec"))) { + return false; + } + + if (!(out_vector_type = LLVMVectorType(F64_TYPE, 4))) { + HANDLE_FAILURE("LLVMVectorType"); + return false; + } + + if (!(result = simd_integer_convert(comp_ctx, func_ctx, is_signed, vector, + out_vector_type))) { + return false; + } + + /* v4f64 -> v2f64 */ + if (!(mask = LLVMConstVector(lanes, 2))) { + HANDLE_FAILURE("LLVMConstVector"); + return false; + } + + if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, result, result, + mask, "trunc"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +static bool +simd_extadd_pairwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef in_vector_type, LLVMTypeRef out_vector_type, + bool is_signed) +{ + LLVMValueRef vector, even_mask, odd_mask, sub_vector_even, sub_vector_odd, + result; + + LLVMValueRef even_element[] = { + LLVM_CONST(i32_zero), LLVM_CONST(i32_two), LLVM_CONST(i32_four), + LLVM_CONST(i32_six), LLVM_CONST(i32_eight), LLVM_CONST(i32_ten), + LLVM_CONST(i32_twelve), LLVM_CONST(i32_fourteen), + }; + + LLVMValueRef odd_element[] = { + LLVM_CONST(i32_one), LLVM_CONST(i32_three), + LLVM_CONST(i32_five), LLVM_CONST(i32_seven), + LLVM_CONST(i32_nine), LLVM_CONST(i32_eleven), + LLVM_CONST(i32_thirteen), LLVM_CONST(i32_fifteen), + }; + + /* assumption about i16x8 from i8x16 and i32x4 from i16x8 */ + uint8 mask_length = V128_i16x8_TYPE == out_vector_type ? 8 : 4; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, in_vector_type, + "vector"))) { + return false; + } + + if (!(even_mask = LLVMConstVector(even_element, mask_length)) + || !(odd_mask = LLVMConstVector(odd_element, mask_length))) { + HANDLE_FAILURE("LLVMConstVector"); + return false; + } + + /* shuffle a <16xi8> vector to two <8xi8> vectors */ + if (!(sub_vector_even = LLVMBuildShuffleVector( + comp_ctx->builder, vector, vector, even_mask, "pick_even")) + || !(sub_vector_odd = LLVMBuildShuffleVector( + comp_ctx->builder, vector, vector, odd_mask, "pick_odd"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + /* sext/zext <8xi8> to <8xi16> */ + if (is_signed) { + if (!(sub_vector_even = + LLVMBuildSExt(comp_ctx->builder, sub_vector_even, + out_vector_type, "even_sext")) + || !(sub_vector_odd = + LLVMBuildSExt(comp_ctx->builder, sub_vector_odd, + out_vector_type, "odd_sext"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + return false; + } + } + else { + if (!(sub_vector_even = + LLVMBuildZExt(comp_ctx->builder, sub_vector_even, + out_vector_type, "even_zext")) + || !(sub_vector_odd = + LLVMBuildZExt(comp_ctx->builder, sub_vector_odd, + out_vector_type, "odd_zext"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + return false; + } + } + + if (!(result = LLVMBuildAdd(comp_ctx->builder, sub_vector_even, + sub_vector_odd, "sum"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i16x8_extadd_pairwise_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_signed) +{ + return simd_extadd_pairwise(comp_ctx, func_ctx, V128_i8x16_TYPE, + V128_i16x8_TYPE, is_signed); +} + +bool +aot_compile_simd_i32x4_extadd_pairwise_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_signed) +{ + return simd_extadd_pairwise(comp_ctx, func_ctx, V128_i16x8_TYPE, + V128_i32x4_TYPE, is_signed); +} + +bool +aot_compile_simd_i16x8_q15mulr_sat(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef lhs, rhs, pad, offset, min, max, result; + LLVMTypeRef vector_ext_type; + + if (!(rhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i16x8_TYPE, + "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i16x8_TYPE, "lhs"))) { + return false; + } + + if (!(vector_ext_type = LLVMVectorType(I32_TYPE, 8))) { + HANDLE_FAILURE("LLVMVectorType"); + return false; + } + + if (!(lhs = LLVMBuildSExt(comp_ctx->builder, lhs, vector_ext_type, + "lhs_v8i32")) + || !(rhs = LLVMBuildSExt(comp_ctx->builder, rhs, vector_ext_type, + "rhs_v8i32"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + return false; + } + + /* 0x4000 and 15*/ + if (!(pad = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE, + 0x4000, 8)) + || !(offset = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE, + 15, 8))) { + return false; + } + + /* TODO: looking for x86 intrinsics about integer"fused multiply-and-add" */ + /* S.SignedSaturate((x * y + 0x4000) >> 15) */ + if (!(result = LLVMBuildMul(comp_ctx->builder, lhs, rhs, "mul"))) { + HANDLE_FAILURE("LLVMBuildMul"); + return false; + } + + if (!(result = LLVMBuildAdd(comp_ctx->builder, result, pad, "add"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + return false; + } + + if (!(result = LLVMBuildAShr(comp_ctx->builder, result, offset, "ashr"))) { + HANDLE_FAILURE("LLVMBuildAShr"); + return false; + } + + if (!(min = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE, + 0xffff8000, 8)) + || !(max = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE, + 0x00007fff, 8))) { + return false; + } + + /* sat after trunc will let *sat* part be optimized */ + if (!(result = simd_saturate(comp_ctx, func_ctx, e_sat_i32x8, result, min, + max, true))) { + return false; + } + + if (!(result = LLVMBuildTrunc(comp_ctx->builder, result, V128_i16x8_TYPE, + "down_to_v8i16"))) { + HANDLE_FAILURE("LLVMBuildTrunc"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +enum integer_extmul_type { + e_i16x8_extmul_i8x16, + e_i32x4_extmul_i16x8, + e_i64x2_extmul_i32x4, +}; + +static bool +simd_integer_extmul(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool lower_half, bool is_signed, + enum integer_extmul_type itype) +{ + LLVMValueRef vec1, vec2, result; + enum integer_extend_type ext_type[] = { + e_ext_i8x16, + e_ext_i16x8, + e_ext_i32x4, + }; + LLVMTypeRef in_vector_type[] = { + V128_i8x16_TYPE, + V128_i16x8_TYPE, + V128_i32x4_TYPE, + }; + + if (!(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type[itype], "vec1")) + || !(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + in_vector_type[itype], "vec2"))) { + return false; + } + + if (!(vec1 = simd_integer_extension(comp_ctx, func_ctx, ext_type[itype], + vec1, lower_half, is_signed)) + || !(vec2 = simd_integer_extension(comp_ctx, func_ctx, ext_type[itype], + vec2, lower_half, is_signed))) { + return false; + } + + if (!(result = LLVMBuildMul(comp_ctx->builder, vec1, vec2, "product"))) { + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i16x8_extmul_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed) +{ + return simd_integer_extmul(comp_ctx, func_ctx, lower_half, is_signed, + e_i16x8_extmul_i8x16); +} + +bool +aot_compile_simd_i32x4_extmul_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed) +{ + return simd_integer_extmul(comp_ctx, func_ctx, lower_half, is_signed, + e_i32x4_extmul_i16x8); +} + +bool +aot_compile_simd_i64x2_extmul_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed) +{ + return simd_integer_extmul(comp_ctx, func_ctx, lower_half, is_signed, + e_i64x2_extmul_i32x4); +} diff --git a/core/iwasm/compilation/simd/simd_conversions.h b/core/iwasm/compilation/simd/simd_conversions.h new file mode 100644 index 0000000000..e3a1a3521c --- /dev/null +++ b/core/iwasm/compilation/simd/simd_conversions.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_CONVERSIONS_H_ +#define _SIMD_CONVERSIONS_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_narrow_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed); + +bool +aot_compile_simd_i16x8_narrow_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed); + +bool +aot_compile_simd_i16x8_extend_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_low, + bool is_signed); + +bool +aot_compile_simd_i32x4_extend_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_low, + bool is_signed); + +bool +aot_compile_simd_i64x2_extend_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed); + +bool +aot_compile_simd_i32x4_trunc_sat_f32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_signed); + +bool +aot_compile_simd_i32x4_trunc_sat_f64x2(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_signed); + +bool +aot_compile_simd_f32x4_convert_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed); + +bool +aot_compile_simd_f64x2_convert_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_signed); +bool +aot_compile_simd_i16x8_extadd_pairwise_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_signed); + +bool +aot_compile_simd_i32x4_extadd_pairwise_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + bool is_signed); +bool +aot_compile_simd_i16x8_q15mulr_sat(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i16x8_extmul_i8x16(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_low, + bool is_signed); + +bool +aot_compile_simd_i32x4_extmul_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool is_low, + bool is_signed); + +bool +aot_compile_simd_i64x2_extmul_i32x4(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool lower_half, + bool is_signed); +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_CONVERSIONS_H_ */ diff --git a/core/iwasm/compilation/simd/simd_floating_point.c b/core/iwasm/compilation/simd/simd_floating_point.c new file mode 100644 index 0000000000..536ef5b289 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_floating_point.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_floating_point.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../aot_emit_numberic.h" +#include "../../aot/aot_runtime.h" + +static bool +simd_v128_float_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op, LLVMTypeRef vector_type) +{ + LLVMValueRef lhs, rhs, result = NULL; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + switch (arith_op) { + case FLOAT_ADD: + result = LLVMBuildFAdd(comp_ctx->builder, lhs, rhs, "sum"); + break; + case FLOAT_SUB: + result = LLVMBuildFSub(comp_ctx->builder, lhs, rhs, "difference"); + break; + case FLOAT_MUL: + result = LLVMBuildFMul(comp_ctx->builder, lhs, rhs, "product"); + break; + case FLOAT_DIV: + result = LLVMBuildFDiv(comp_ctx->builder, lhs, rhs, "quotient"); + break; + default: + return false; + } + + if (!result) { + HANDLE_FAILURE( + "LLVMBuildFAdd/LLVMBuildFSub/LLVMBuildFMul/LLVMBuildFDiv"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_f32x4_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op) +{ + return simd_v128_float_arith(comp_ctx, func_ctx, arith_op, V128_f32x4_TYPE); +} + +bool +aot_compile_simd_f64x2_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op) +{ + return simd_v128_float_arith(comp_ctx, func_ctx, arith_op, V128_f64x2_TYPE); +} + +static bool +simd_v128_float_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef vector_type) +{ + LLVMValueRef vector, result; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vector"))) { + return false; + } + + if (!(result = LLVMBuildFNeg(comp_ctx->builder, vector, "neg"))) { + HANDLE_FAILURE("LLVMBuildFNeg"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_f32x4_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_v128_float_neg(comp_ctx, func_ctx, V128_f32x4_TYPE); +} + +bool +aot_compile_simd_f64x2_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_v128_float_neg(comp_ctx, func_ctx, V128_f64x2_TYPE); +} + +static bool +simd_float_intrinsic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef vector_type, const char *intrinsic) +{ + LLVMValueRef vector, result; + LLVMTypeRef param_types[1] = { vector_type }; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vector"))) { + return false; + } + + if (!(result = + aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, + vector_type, param_types, 1, vector))) { + HANDLE_FAILURE("LLVMBuildCall"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_f32x4_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, + "llvm.fabs.v4f32"); +} + +bool +aot_compile_simd_f64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, + "llvm.fabs.v2f64"); +} + +bool +aot_compile_simd_f32x4_sqrt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, + "llvm.sqrt.v4f32"); +} + +bool +aot_compile_simd_f64x2_sqrt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, + "llvm.sqrt.v2f64"); +} + +bool +aot_compile_simd_f32x4_ceil(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, + "llvm.ceil.v4f32"); +} + +bool +aot_compile_simd_f64x2_ceil(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, + "llvm.ceil.v2f64"); +} + +bool +aot_compile_simd_f32x4_floor(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, + "llvm.floor.v4f32"); +} + +bool +aot_compile_simd_f64x2_floor(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, + "llvm.floor.v2f64"); +} + +bool +aot_compile_simd_f32x4_trunc(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, + "llvm.trunc.v4f32"); +} + +bool +aot_compile_simd_f64x2_trunc(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, + "llvm.trunc.v2f64"); +} + +bool +aot_compile_simd_f32x4_nearest(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f32x4_TYPE, + "llvm.rint.v4f32"); +} + +bool +aot_compile_simd_f64x2_nearest(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_float_intrinsic(comp_ctx, func_ctx, V128_f64x2_TYPE, + "llvm.rint.v2f64"); +} + +static bool +simd_float_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic op, LLVMTypeRef vector_type) +{ + LLVMValueRef lhs, rhs, cmp, selected; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + if (!(cmp = LLVMBuildFCmp(comp_ctx->builder, + op == FLOAT_MIN ? LLVMRealOLT : LLVMRealOGT, rhs, + lhs, "cmp"))) { + HANDLE_FAILURE("LLVMBuildFCmp"); + return false; + } + + if (!(selected = + LLVMBuildSelect(comp_ctx->builder, cmp, rhs, lhs, "selected"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, selected, "result"); +} + +static bool +simd_float_min(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef vector_type) +{ + LLVMValueRef lhs, rhs, lhs_nan, rhs_nan, olt_ret, ogt_ret, or_ret, ret1, + ret2, ret3, ret4; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + if (!(lhs_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, lhs, lhs, + "lhs_nan"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealUNO"); + return false; + } + + if (!(rhs_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, rhs, rhs, + "rhs_nan"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealUNO"); + return false; + } + + if (!(olt_ret = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOLT, lhs, rhs, + "olt_ret"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealOLT"); + return false; + } + + if (!(ogt_ret = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGT, lhs, rhs, + "ogt_ret"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealOGT"); + return false; + } + + /* lhs or rhs */ + { + LLVMValueRef integer_l, integer_r, integer_or; + + if (!(integer_l = LLVMBuildBitCast(comp_ctx->builder, lhs, + V128_i64x2_TYPE, "lhs_to_int"))) { + HANDLE_FAILURE("LLVMBuildBitCas"); + return false; + } + + if (!(integer_r = LLVMBuildBitCast(comp_ctx->builder, rhs, + V128_i64x2_TYPE, "rhs_to_int"))) { + HANDLE_FAILURE("LLVMBuildBitCas"); + return false; + } + + if (!(integer_or = + LLVMBuildOr(comp_ctx->builder, integer_l, integer_r, "or"))) { + HANDLE_FAILURE("LLVMBuildOr"); + return false; + } + + if (!(or_ret = LLVMBuildBitCast(comp_ctx->builder, integer_or, + vector_type, "holder"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + return false; + } + } + + if (!(ret1 = LLVMBuildSelect(comp_ctx->builder, olt_ret, lhs, or_ret, + "sel_olt"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + if (!(ret2 = LLVMBuildSelect(comp_ctx->builder, ogt_ret, rhs, ret1, + "sel_ogt"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + if (!(ret3 = LLVMBuildSelect(comp_ctx->builder, lhs_nan, lhs, ret2, + "sel_lhs_nan"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + if (!(ret4 = LLVMBuildSelect(comp_ctx->builder, rhs_nan, rhs, ret3, + "sel_rhs_nan"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, ret4, "result"); +} + +static bool +simd_float_max(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef vector_type) +{ + LLVMValueRef lhs, rhs, lhs_nan, rhs_nan, olt_ret, ogt_ret, and_ret, ret1, + ret2, ret3, ret4; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + if (!(lhs_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, lhs, lhs, + "lhs_nan"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealUNO"); + return false; + } + + if (!(rhs_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, rhs, rhs, + "rhs_nan"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealUNO"); + return false; + } + + if (!(olt_ret = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOLT, lhs, rhs, + "olt_ret"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealOLT"); + return false; + } + + if (!(ogt_ret = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGT, lhs, rhs, + "ogt_ret"))) { + HANDLE_FAILURE("LLVMBuildFCmp + LLVMRealOGT"); + return false; + } + + /* lhs and rhs */ + { + LLVMValueRef integer_l, integer_r, integer_and; + + if (!(integer_l = LLVMBuildBitCast(comp_ctx->builder, lhs, + V128_i64x2_TYPE, "lhs_to_int"))) { + HANDLE_FAILURE("LLVMBuildBitCas"); + return false; + } + + if (!(integer_r = LLVMBuildBitCast(comp_ctx->builder, rhs, + V128_i64x2_TYPE, "rhs_to_int"))) { + HANDLE_FAILURE("LLVMBuildBitCas"); + return false; + } + + if (!(integer_and = LLVMBuildAnd(comp_ctx->builder, integer_l, + integer_r, "and"))) { + HANDLE_FAILURE("LLVMBuildOr"); + return false; + } + + if (!(and_ret = LLVMBuildBitCast(comp_ctx->builder, integer_and, + vector_type, "holder"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + return false; + } + } + + if (!(ret1 = LLVMBuildSelect(comp_ctx->builder, ogt_ret, lhs, and_ret, + "sel_ogt"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + if (!(ret2 = LLVMBuildSelect(comp_ctx->builder, olt_ret, rhs, ret1, + "sel_olt"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + if (!(ret3 = LLVMBuildSelect(comp_ctx->builder, lhs_nan, lhs, ret2, + "sel_lhs_nan"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + if (!(ret4 = LLVMBuildSelect(comp_ctx->builder, rhs_nan, rhs, ret3, + "sel_rhs_nan"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, ret4, "result"); +} + +bool +aot_compile_simd_f32x4_min_max(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min) +{ + return run_min ? simd_float_min(comp_ctx, func_ctx, V128_f32x4_TYPE) + : simd_float_max(comp_ctx, func_ctx, V128_f32x4_TYPE); +} + +bool +aot_compile_simd_f64x2_min_max(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min) +{ + return run_min ? simd_float_min(comp_ctx, func_ctx, V128_f64x2_TYPE) + : simd_float_max(comp_ctx, func_ctx, V128_f64x2_TYPE); +} + +bool +aot_compile_simd_f32x4_pmin_pmax(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min) +{ + return simd_float_cmp(comp_ctx, func_ctx, run_min ? FLOAT_MIN : FLOAT_MAX, + V128_f32x4_TYPE); +} + +bool +aot_compile_simd_f64x2_pmin_pmax(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min) +{ + return simd_float_cmp(comp_ctx, func_ctx, run_min ? FLOAT_MIN : FLOAT_MAX, + V128_f64x2_TYPE); +} + +bool +aot_compile_simd_f64x2_demote(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef vector, elem_0, elem_1, result; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_f64x2_TYPE, "vector"))) { + return false; + } + + if (!(elem_0 = LLVMBuildExtractElement(comp_ctx->builder, vector, + LLVM_CONST(i32_zero), "elem_0")) + || !(elem_1 = LLVMBuildExtractElement(comp_ctx->builder, vector, + LLVM_CONST(i32_one), "elem_1"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + return false; + } + + /* fptrunc elem to */ + if (!(elem_0 = LLVMBuildFPTrunc(comp_ctx->builder, elem_0, F32_TYPE, + "elem_0_trunc")) + || !(elem_1 = LLVMBuildFPTrunc(comp_ctx->builder, elem_1, F32_TYPE, + "elem_1_trunc"))) { + HANDLE_FAILURE("LLVMBuildFPTrunc"); + return false; + } + + if (!(result = LLVMBuildInsertElement(comp_ctx->builder, + LLVM_CONST(f32x4_vec_zero), elem_0, + LLVM_CONST(i32_zero), "new_vector_0")) + || !(result = + LLVMBuildInsertElement(comp_ctx->builder, result, elem_1, + LLVM_CONST(i32_one), "new_vector_1"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_f32x4_promote(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef vector, elem_0, elem_1, result; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_f32x4_TYPE, "vector"))) { + return false; + } + + if (!(elem_0 = LLVMBuildExtractElement(comp_ctx->builder, vector, + LLVM_CONST(i32_zero), "elem_0")) + || !(elem_1 = LLVMBuildExtractElement(comp_ctx->builder, vector, + LLVM_CONST(i32_one), "elem_1"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + return false; + } + + /* fpext elem to */ + if (!(elem_0 = + LLVMBuildFPExt(comp_ctx->builder, elem_0, F64_TYPE, "elem_0_ext")) + || !(elem_1 = LLVMBuildFPExt(comp_ctx->builder, elem_1, F64_TYPE, + "elem_1_ext"))) { + HANDLE_FAILURE("LLVMBuildFPExt"); + return false; + } + + if (!(result = LLVMBuildInsertElement(comp_ctx->builder, + LLVM_CONST(f64x2_vec_zero), elem_0, + LLVM_CONST(i32_zero), "new_vector_0")) + || !(result = + LLVMBuildInsertElement(comp_ctx->builder, result, elem_1, + LLVM_CONST(i32_one), "new_vector_1"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} diff --git a/core/iwasm/compilation/simd/simd_floating_point.h b/core/iwasm/compilation/simd/simd_floating_point.h new file mode 100644 index 0000000000..39e37c872e --- /dev/null +++ b/core/iwasm/compilation/simd/simd_floating_point.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_FLOATING_POINT_H_ +#define _SIMD_FLOATING_POINT_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_f32x4_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op); + +bool +aot_compile_simd_f64x2_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op); + +bool +aot_compile_simd_f32x4_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_sqrt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_sqrt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_ceil(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_ceil(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_floor(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_floor(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_trunc(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_trunc(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_nearest(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f64x2_nearest(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_min_max(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min); + +bool +aot_compile_simd_f64x2_min_max(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min); + +bool +aot_compile_simd_f32x4_pmin_pmax(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min); + +bool +aot_compile_simd_f64x2_pmin_pmax(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, bool run_min); + +bool +aot_compile_simd_f64x2_demote(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_f32x4_promote(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_FLOATING_POINT_H_ */ diff --git a/core/iwasm/compilation/simd/simd_int_arith.c b/core/iwasm/compilation/simd/simd_int_arith.c new file mode 100644 index 0000000000..ada4b5d35b --- /dev/null +++ b/core/iwasm/compilation/simd/simd_int_arith.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_int_arith.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +static bool +simd_integer_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, LLVMTypeRef vector_type) +{ + LLVMValueRef lhs, rhs, result = NULL; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + switch (arith_op) { + case V128_ADD: + result = LLVMBuildAdd(comp_ctx->builder, lhs, rhs, "sum"); + break; + case V128_SUB: + result = LLVMBuildSub(comp_ctx->builder, lhs, rhs, "difference"); + break; + case V128_MUL: + result = LLVMBuildMul(comp_ctx->builder, lhs, rhs, "product"); + break; + default: + HANDLE_FAILURE("Unsupported arith_op"); + break; + } + + if (!result) { + HANDLE_FAILURE("LLVMBuildAdd/LLVMBuildSub/LLVMBuildMul"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op) +{ + return simd_integer_arith(comp_ctx, func_ctx, arith_op, V128_i8x16_TYPE); +} + +bool +aot_compile_simd_i16x8_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op) +{ + return simd_integer_arith(comp_ctx, func_ctx, arith_op, V128_i16x8_TYPE); +} + +bool +aot_compile_simd_i32x4_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op) +{ + return simd_integer_arith(comp_ctx, func_ctx, arith_op, V128_i32x4_TYPE); +} + +bool +aot_compile_simd_i64x2_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op) +{ + return simd_integer_arith(comp_ctx, func_ctx, arith_op, V128_i64x2_TYPE); +} + +static bool +simd_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMTypeRef type) +{ + LLVMValueRef vector, result; + + if (!(vector = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, type, "vector"))) { + return false; + } + + if (!(result = LLVMBuildNeg(comp_ctx->builder, vector, "neg"))) { + HANDLE_FAILURE("LLVMBuildNeg"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_neg(comp_ctx, func_ctx, V128_i8x16_TYPE); +} + +bool +aot_compile_simd_i16x8_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_neg(comp_ctx, func_ctx, V128_i16x8_TYPE); +} + +bool +aot_compile_simd_i32x4_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_neg(comp_ctx, func_ctx, V128_i32x4_TYPE); +} + +bool +aot_compile_simd_i64x2_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_neg(comp_ctx, func_ctx, V128_i64x2_TYPE); +} + +bool +aot_compile_simd_i8x16_popcnt(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef vector, result; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i8x16_TYPE, "vector"))) { + return false; + } + + if (!(result = aot_call_llvm_intrinsic(comp_ctx, func_ctx, + "llvm.ctpop.v16i8", V128_i8x16_TYPE, + &V128_i8x16_TYPE, 1, vector))) { + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +static bool +simd_v128_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef vector_type, V128Arithmetic arith_op, bool is_signed) +{ + LLVMValueRef lhs, rhs, result; + LLVMIntPredicate op; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + if (V128_MIN == arith_op) { + op = is_signed ? LLVMIntSLT : LLVMIntULT; + } + else { + op = is_signed ? LLVMIntSGT : LLVMIntUGT; + } + + if (!(result = LLVMBuildICmp(comp_ctx->builder, op, lhs, rhs, "cmp"))) { + HANDLE_FAILURE("LLVMBuildICmp"); + return false; + } + + if (!(result = + LLVMBuildSelect(comp_ctx->builder, result, lhs, rhs, "select"))) { + HANDLE_FAILURE("LLVMBuildSelect"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed) +{ + return simd_v128_cmp(comp_ctx, func_ctx, V128_i8x16_TYPE, arith_op, + is_signed); +} + +bool +aot_compile_simd_i16x8_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed) +{ + return simd_v128_cmp(comp_ctx, func_ctx, V128_i16x8_TYPE, arith_op, + is_signed); +} + +bool +aot_compile_simd_i32x4_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed) +{ + return simd_v128_cmp(comp_ctx, func_ctx, V128_i32x4_TYPE, arith_op, + is_signed); +} + +/* llvm.abs.* */ +static bool +simd_v128_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + char *intrinsic, LLVMTypeRef vector_type) +{ + LLVMValueRef vector, result; + LLVMTypeRef param_types[] = { vector_type, INT1_TYPE }; + + if (!(vector = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "vec"))) { + return false; + } + + if (!(result = aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsic, + vector_type, param_types, 2, vector, + /* is_int_min_poison */ + LLVM_CONST(i1_zero)))) { + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_v128_abs(comp_ctx, func_ctx, "llvm.abs.v16i8", V128_i8x16_TYPE); +} + +bool +aot_compile_simd_i16x8_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_v128_abs(comp_ctx, func_ctx, "llvm.abs.v8i16", V128_i16x8_TYPE); +} + +bool +aot_compile_simd_i32x4_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_v128_abs(comp_ctx, func_ctx, "llvm.abs.v4i32", V128_i32x4_TYPE); +} + +bool +aot_compile_simd_i64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return simd_v128_abs(comp_ctx, func_ctx, "llvm.abs.v2i64", V128_i64x2_TYPE); +} + +enum integer_avgr_u { + e_avgr_u_i8x16, + e_avgr_u_i16x8, +}; + +/* TODO: try int_x86_mmx_pavg_b and int_x86_mmx_pavg_w */ +/* (v1 + v2 + 1) / 2 */ +static bool +simd_v128_avg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + enum integer_avgr_u itype) +{ + LLVMValueRef lhs, rhs, ones, result; + LLVMTypeRef vector_ext_type; + LLVMTypeRef vector_type[] = { + V128_i8x16_TYPE, + V128_i16x8_TYPE, + }; + unsigned lanes[] = { 16, 8 }; + + if (!(rhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + vector_type[itype], "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + vector_type[itype], "lhs"))) { + return false; + } + + if (!(vector_ext_type = LLVMVectorType(I64_TYPE, lanes[itype]))) { + HANDLE_FAILURE("LLVMVectorType"); + return false; + } + + if (!(lhs = LLVMBuildZExt(comp_ctx->builder, lhs, vector_ext_type, + "zext_to_i64")) + || !(rhs = LLVMBuildZExt(comp_ctx->builder, rhs, vector_ext_type, + "zext_to_i64"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + return false; + } + + /* by default, add will do signed/unsigned overflow */ + if (!(result = LLVMBuildAdd(comp_ctx->builder, lhs, rhs, "l_add_r"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + return false; + } + + if (!(ones = simd_build_splat_const_integer_vector(comp_ctx, I64_TYPE, 1, + lanes[itype]))) { + return false; + } + + if (!(result = LLVMBuildAdd(comp_ctx->builder, result, ones, "plus_1"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + return false; + } + + if (!(result = LLVMBuildLShr(comp_ctx->builder, result, ones, "avg"))) { + HANDLE_FAILURE("LLVMBuildLShr"); + return false; + } + + if (!(result = LLVMBuildTrunc(comp_ctx->builder, result, vector_type[itype], + "to_orig_type"))) { + HANDLE_FAILURE("LLVMBuildTrunc"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_avgr_u(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_v128_avg(comp_ctx, func_ctx, e_avgr_u_i8x16); +} + +bool +aot_compile_simd_i16x8_avgr_u(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + return simd_v128_avg(comp_ctx, func_ctx, e_avgr_u_i16x8); +} + +bool +aot_compile_simd_i32x4_dot_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef vec1, vec2, even_mask, odd_mask, zero, result; + LLVMTypeRef vector_ext_type; + LLVMValueRef even_element[] = { + LLVM_CONST(i32_zero), + LLVM_CONST(i32_two), + LLVM_CONST(i32_four), + LLVM_CONST(i32_six), + }; + LLVMValueRef odd_element[] = { + LLVM_CONST(i32_one), + LLVM_CONST(i32_three), + LLVM_CONST(i32_five), + LLVM_CONST(i32_seven), + }; + + if (!(vec1 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, V128_i16x8_TYPE, + "vec1")) + || !(vec2 = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, + V128_i16x8_TYPE, "vec2"))) { + return false; + } + + if (!(vector_ext_type = LLVMVectorType(I32_TYPE, 8))) { + HANDLE_FAILURE("LLVMVectorType"); + return false; + } + + /* sext to */ + if (!(vec1 = LLVMBuildSExt(comp_ctx->builder, vec1, vector_ext_type, + "vec1_v8i32")) + || !(vec2 = LLVMBuildSExt(comp_ctx->builder, vec2, vector_ext_type, + "vec2_v8i32"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + return false; + } + + if (!(result = LLVMBuildMul(comp_ctx->builder, vec1, vec2, "product"))) { + HANDLE_FAILURE("LLVMBuildMul"); + return false; + } + + /* pick elements with even indexes and odd indexes */ + if (!(even_mask = LLVMConstVector(even_element, 4)) + || !(odd_mask = LLVMConstVector(odd_element, 4))) { + HANDLE_FAILURE("LLVMConstVector"); + return false; + } + + if (!(zero = simd_build_splat_const_integer_vector(comp_ctx, I32_TYPE, 0, + 8))) { + return false; + } + + if (!(vec1 = LLVMBuildShuffleVector(comp_ctx->builder, result, zero, + even_mask, "even_result")) + || !(vec2 = LLVMBuildShuffleVector(comp_ctx->builder, result, zero, + odd_mask, "odd_result"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + if (!(result = LLVMBuildAdd(comp_ctx->builder, vec1, vec2, "new_vec"))) { + HANDLE_FAILURE("LLVMBuildAdd"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} diff --git a/core/iwasm/compilation/simd/simd_int_arith.h b/core/iwasm/compilation/simd/simd_int_arith.h new file mode 100644 index 0000000000..49827d51d0 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_int_arith.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_INT_ARITH_H_ +#define _SIMD_INT_ARITH_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic cond); + +bool +aot_compile_simd_i16x8_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic cond); + +bool +aot_compile_simd_i32x4_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic cond); + +bool +aot_compile_simd_i64x2_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic cond); + +bool +aot_compile_simd_i8x16_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i16x8_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i32x4_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i64x2_neg(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i8x16_popcnt(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i8x16_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed); + +bool +aot_compile_simd_i16x8_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed); + +bool +aot_compile_simd_i32x4_cmp(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed); + +bool +aot_compile_simd_i8x16_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i16x8_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i32x4_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i64x2_abs(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i8x16_avgr_u(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i16x8_avgr_u(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_simd_i32x4_dot_i16x8(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_INT_ARITH_H_ */ diff --git a/core/iwasm/compilation/simd/simd_load_store.c b/core/iwasm/compilation/simd/simd_load_store.c new file mode 100644 index 0000000000..d3bbcc9650 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_load_store.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_common.h" +#include "simd_load_store.h" +#include "../aot_emit_exception.h" +#include "../aot_emit_memory.h" +#include "../../aot/aot_runtime.h" +#include "../../interpreter/wasm_opcode.h" + +/* data_length in bytes */ +static LLVMValueRef +simd_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, + mem_offset_t offset, uint32 data_length, LLVMTypeRef ptr_type, + LLVMTypeRef data_type, bool enable_segue) +{ + LLVMValueRef maddr, data; + + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, + data_length, enable_segue, NULL))) { + HANDLE_FAILURE("aot_check_memory_overflow"); + return NULL; + } + + if (!(maddr = LLVMBuildBitCast(comp_ctx->builder, maddr, ptr_type, + "data_ptr"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + return NULL; + } + + if (!(data = LLVMBuildLoad2(comp_ctx->builder, data_type, maddr, "data"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + return NULL; + } + + LLVMSetAlignment(data, 1); + + return data; +} + +bool +aot_compile_simd_v128_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset) +{ + bool enable_segue = comp_ctx->enable_segue_v128_load; + LLVMTypeRef v128_ptr_type = enable_segue ? V128_PTR_TYPE_GS : V128_PTR_TYPE; + LLVMValueRef result; + + if (!(result = simd_load(comp_ctx, func_ctx, align, offset, 16, + v128_ptr_type, V128_TYPE, enable_segue))) { + return false; + } + + PUSH_V128(result); + + return true; +fail: + return false; +} + +bool +aot_compile_simd_load_extend(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset) +{ + LLVMValueRef sub_vector, result; + uint32 opcode_index = opcode - SIMD_v128_load8x8_s; + bool signeds[] = { true, false, true, false, true, false }; + LLVMTypeRef vector_types[] = { + V128_i16x8_TYPE, V128_i16x8_TYPE, V128_i32x4_TYPE, + V128_i32x4_TYPE, V128_i64x2_TYPE, V128_i64x2_TYPE, + }; + LLVMTypeRef sub_vector_types[] = { + LLVMVectorType(INT8_TYPE, 8), LLVMVectorType(INT8_TYPE, 8), + LLVMVectorType(INT16_TYPE, 4), LLVMVectorType(INT16_TYPE, 4), + LLVMVectorType(I32_TYPE, 2), LLVMVectorType(I32_TYPE, 2), + }; + LLVMTypeRef sub_vector_type, sub_vector_ptr_type; + bool enable_segue = comp_ctx->enable_segue_v128_load; + + bh_assert(opcode_index < 6); + + sub_vector_type = sub_vector_types[opcode_index]; + + /* to vector ptr type */ + if (!sub_vector_type + || !(sub_vector_ptr_type = + LLVMPointerType(sub_vector_type, enable_segue ? 256 : 0))) { + HANDLE_FAILURE("LLVMPointerType"); + return false; + } + + if (!(sub_vector = + simd_load(comp_ctx, func_ctx, align, offset, 8, + sub_vector_ptr_type, sub_vector_type, enable_segue))) { + return false; + } + + if (signeds[opcode_index]) { + if (!(result = LLVMBuildSExt(comp_ctx->builder, sub_vector, + vector_types[opcode_index], "vector"))) { + HANDLE_FAILURE("LLVMBuildSExt"); + return false; + } + } + else { + if (!(result = LLVMBuildZExt(comp_ctx->builder, sub_vector, + vector_types[opcode_index], "vector"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + return false; + } + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_load_splat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset) +{ + uint32 opcode_index = opcode - SIMD_v128_load8_splat; + LLVMValueRef element, result; + LLVMTypeRef element_ptr_types[] = { INT8_PTR_TYPE, INT16_PTR_TYPE, + INT32_PTR_TYPE, INT64_PTR_TYPE }; + LLVMTypeRef element_ptr_types_gs[] = { INT8_PTR_TYPE_GS, INT16_PTR_TYPE_GS, + INT32_PTR_TYPE_GS, + INT64_PTR_TYPE_GS }; + LLVMTypeRef element_data_types[] = { INT8_TYPE, INT16_TYPE, I32_TYPE, + I64_TYPE }; + uint32 data_lengths[] = { 1, 2, 4, 8 }; + LLVMValueRef undefs[] = { + LLVM_CONST(i8x16_undef), + LLVM_CONST(i16x8_undef), + LLVM_CONST(i32x4_undef), + LLVM_CONST(i64x2_undef), + }; + LLVMValueRef masks[] = { + LLVM_CONST(i32x16_zero), + LLVM_CONST(i32x8_zero), + LLVM_CONST(i32x4_zero), + LLVM_CONST(i32x2_zero), + }; + bool enable_segue = comp_ctx->enable_segue_v128_load; + + bh_assert(opcode_index < 4); + + if (!(element = simd_load( + comp_ctx, func_ctx, align, offset, data_lengths[opcode_index], + comp_ctx->enable_segue_v128_load + ? element_ptr_types_gs[opcode_index] + : element_ptr_types[opcode_index], + element_data_types[opcode_index], enable_segue))) { + return false; + } + + if (!(result = + LLVMBuildInsertElement(comp_ctx->builder, undefs[opcode_index], + element, I32_ZERO, "base"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + return false; + } + + if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, result, + undefs[opcode_index], + masks[opcode_index], "vector"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_load_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset, + uint8 lane_id) +{ + LLVMValueRef element, vector; + uint32 opcode_index = opcode - SIMD_v128_load8_lane; + uint32 data_lengths[] = { 1, 2, 4, 8 }; + LLVMTypeRef element_ptr_types[] = { INT8_PTR_TYPE, INT16_PTR_TYPE, + INT32_PTR_TYPE, INT64_PTR_TYPE }; + LLVMTypeRef element_ptr_types_gs[] = { INT8_PTR_TYPE_GS, INT16_PTR_TYPE_GS, + INT32_PTR_TYPE_GS, + INT64_PTR_TYPE_GS }; + LLVMTypeRef element_data_types[] = { INT8_TYPE, INT16_TYPE, I32_TYPE, + I64_TYPE }; + LLVMTypeRef vector_types[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, + V128_i32x4_TYPE, V128_i64x2_TYPE }; + LLVMValueRef lane = simd_lane_id_to_llvm_value(comp_ctx, lane_id); + bool enable_segue = comp_ctx->enable_segue_v128_load; + + bh_assert(opcode_index < 4); + + if (!(vector = simd_pop_v128_and_bitcast( + comp_ctx, func_ctx, vector_types[opcode_index], "src"))) { + return false; + } + + if (!(element = simd_load( + comp_ctx, func_ctx, align, offset, data_lengths[opcode_index], + comp_ctx->enable_segue_v128_load + ? element_ptr_types_gs[opcode_index] + : element_ptr_types[opcode_index], + element_data_types[opcode_index], enable_segue))) { + return false; + } + + if (!(vector = LLVMBuildInsertElement(comp_ctx->builder, vector, element, + lane, "dst"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, vector, "result"); +} + +bool +aot_compile_simd_load_zero(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset) +{ + LLVMValueRef element, result, mask; + uint32 opcode_index = opcode - SIMD_v128_load32_zero; + uint32 data_lengths[] = { 4, 8 }; + LLVMTypeRef element_ptr_types[] = { INT32_PTR_TYPE, INT64_PTR_TYPE }; + LLVMTypeRef element_ptr_types_gs[] = { INT32_PTR_TYPE_GS, + INT64_PTR_TYPE_GS }; + LLVMTypeRef element_data_types[] = { I32_TYPE, I64_TYPE }; + LLVMValueRef zero[] = { + LLVM_CONST(i32x4_vec_zero), + LLVM_CONST(i64x2_vec_zero), + }; + LLVMValueRef undef[] = { + LLVM_CONST(i32x4_undef), + LLVM_CONST(i64x2_undef), + }; + uint32 mask_length[] = { 4, 2 }; + LLVMValueRef mask_element[][4] = { + { LLVM_CONST(i32_zero), LLVM_CONST(i32_four), LLVM_CONST(i32_five), + LLVM_CONST(i32_six) }, + { LLVM_CONST(i32_zero), LLVM_CONST(i32_two) }, + }; + bool enable_segue = comp_ctx->enable_segue_v128_load; + + bh_assert(opcode_index < 2); + + if (!(element = simd_load( + comp_ctx, func_ctx, align, offset, data_lengths[opcode_index], + comp_ctx->enable_segue_v128_load + ? element_ptr_types_gs[opcode_index] + : element_ptr_types[opcode_index], + element_data_types[opcode_index], enable_segue))) { + return false; + } + + if (!(result = + LLVMBuildInsertElement(comp_ctx->builder, undef[opcode_index], + element, I32_ZERO, "vector"))) { + HANDLE_FAILURE("LLVMBuildInsertElement"); + return false; + } + + /* fill in other lanes with zero */ + if (!(mask = LLVMConstVector(mask_element[opcode_index], + mask_length[opcode_index]))) { + HANDLE_FAILURE("LLConstVector"); + return false; + } + + if (!(result = LLVMBuildShuffleVector(comp_ctx->builder, result, + zero[opcode_index], mask, + "fill_in_zero"))) { + HANDLE_FAILURE("LLVMBuildShuffleVector"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +/* data_length in bytes */ +static bool +simd_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 align, + mem_offset_t offset, uint32 data_length, LLVMValueRef value, + LLVMTypeRef value_ptr_type, bool enable_segue) +{ + LLVMValueRef maddr, result; + + if (!(maddr = aot_check_memory_overflow(comp_ctx, func_ctx, offset, + data_length, enable_segue, NULL))) + return false; + + if (!(maddr = LLVMBuildBitCast(comp_ctx->builder, maddr, value_ptr_type, + "data_ptr"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + return false; + } + + if (!(result = LLVMBuildStore(comp_ctx->builder, value, maddr))) { + HANDLE_FAILURE("LLVMBuildStore"); + return false; + } + + LLVMSetAlignment(result, 1); + + return true; +} + +bool +aot_compile_simd_v128_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset) +{ + bool enable_segue = comp_ctx->enable_segue_v128_store; + LLVMTypeRef v128_ptr_type = enable_segue ? V128_PTR_TYPE_GS : V128_PTR_TYPE; + LLVMValueRef value; + + POP_V128(value); + + return simd_store(comp_ctx, func_ctx, align, offset, 16, value, + v128_ptr_type, enable_segue); +fail: + return false; +} + +bool +aot_compile_simd_store_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset, + uint8 lane_id) +{ + LLVMValueRef element, vector; + uint32 data_lengths[] = { 1, 2, 4, 8 }; + LLVMTypeRef element_ptr_types[] = { INT8_PTR_TYPE, INT16_PTR_TYPE, + INT32_PTR_TYPE, INT64_PTR_TYPE }; + LLVMTypeRef element_ptr_types_gs[] = { INT8_PTR_TYPE_GS, INT16_PTR_TYPE_GS, + INT32_PTR_TYPE_GS, + INT64_PTR_TYPE_GS }; + uint32 opcode_index = opcode - SIMD_v128_store8_lane; + LLVMTypeRef vector_types[] = { V128_i8x16_TYPE, V128_i16x8_TYPE, + V128_i32x4_TYPE, V128_i64x2_TYPE }; + LLVMValueRef lane = simd_lane_id_to_llvm_value(comp_ctx, lane_id); + bool enable_segue = comp_ctx->enable_segue_v128_store; + + bh_assert(opcode_index < 4); + + if (!(vector = simd_pop_v128_and_bitcast( + comp_ctx, func_ctx, vector_types[opcode_index], "src"))) { + return false; + } + + if (!(element = LLVMBuildExtractElement(comp_ctx->builder, vector, lane, + "element"))) { + HANDLE_FAILURE("LLVMBuildExtractElement"); + return false; + } + + return simd_store(comp_ctx, func_ctx, align, offset, + data_lengths[opcode_index], element, + enable_segue ? element_ptr_types_gs[opcode_index] + : element_ptr_types[opcode_index], + enable_segue); +} diff --git a/core/iwasm/compilation/simd/simd_load_store.h b/core/iwasm/compilation/simd/simd_load_store.h new file mode 100644 index 0000000000..7a98cbb6fe --- /dev/null +++ b/core/iwasm/compilation/simd/simd_load_store.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_LOAD_STORE_H_ +#define _SIMD_LOAD_STORE_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_v128_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset); + +bool +aot_compile_simd_load_extend(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset); + +bool +aot_compile_simd_load_splat(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset); + +bool +aot_compile_simd_load_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset, + uint8 lane_id); + +bool +aot_compile_simd_load_zero(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset); + +bool +aot_compile_simd_v128_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, mem_offset_t offset); + +bool +aot_compile_simd_store_lane(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 opcode, uint32 align, mem_offset_t offset, + uint8 lane_id); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_LOAD_STORE_H_ */ diff --git a/core/iwasm/compilation/simd/simd_sat_int_arith.c b/core/iwasm/compilation/simd/simd_sat_int_arith.c new file mode 100644 index 0000000000..ea250b7e08 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_sat_int_arith.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "simd_sat_int_arith.h" +#include "simd_common.h" +#include "../aot_emit_exception.h" +#include "../../aot/aot_runtime.h" + +static bool +simd_sat_int_arith(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef vector_type, const char *intrinsics) +{ + LLVMValueRef lhs, rhs, result; + LLVMTypeRef param_types[2]; + + if (!(rhs = + simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, "rhs")) + || !(lhs = simd_pop_v128_and_bitcast(comp_ctx, func_ctx, vector_type, + "lhs"))) { + return false; + } + + param_types[0] = vector_type; + param_types[1] = vector_type; + + if (!(result = + aot_call_llvm_intrinsic(comp_ctx, func_ctx, intrinsics, + vector_type, param_types, 2, lhs, rhs))) { + HANDLE_FAILURE("LLVMBuildCall"); + return false; + } + + return simd_bitcast_and_push_v128(comp_ctx, func_ctx, result, "result"); +} + +bool +aot_compile_simd_i8x16_saturate(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed) +{ + char *intrinsics[][2] = { + { "llvm.sadd.sat.v16i8", "llvm.uadd.sat.v16i8" }, + { "llvm.ssub.sat.v16i8", "llvm.usub.sat.v16i8" }, + }; + + return simd_sat_int_arith(comp_ctx, func_ctx, V128_i8x16_TYPE, + is_signed ? intrinsics[arith_op][0] + : intrinsics[arith_op][1]); +} + +bool +aot_compile_simd_i16x8_saturate(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed) +{ + char *intrinsics[][2] = { + { "llvm.sadd.sat.v8i16", "llvm.uadd.sat.v8i16" }, + { "llvm.ssub.sat.v8i16", "llvm.usub.sat.v8i16" }, + }; + + return simd_sat_int_arith(comp_ctx, func_ctx, V128_i16x8_TYPE, + is_signed ? intrinsics[arith_op][0] + : intrinsics[arith_op][1]); +} diff --git a/core/iwasm/compilation/simd/simd_sat_int_arith.h b/core/iwasm/compilation/simd/simd_sat_int_arith.h new file mode 100644 index 0000000000..67c602fc55 --- /dev/null +++ b/core/iwasm/compilation/simd/simd_sat_int_arith.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SIMD_SAT_INT_ARITH_H_ +#define _SIMD_SAT_INT_ARITH_H_ + +#include "../aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_simd_i8x16_saturate(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed); + +bool +aot_compile_simd_i16x8_saturate(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + V128Arithmetic arith_op, bool is_signed); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _SIMD_SAT_INT_ARITH_H_ */ diff --git a/core/iwasm/doc/classic_interpreter.MD b/core/iwasm/doc/classic_interpreter.MD new file mode 100644 index 0000000000..a607758e26 --- /dev/null +++ b/core/iwasm/doc/classic_interpreter.MD @@ -0,0 +1,5 @@ +# Classic interpreter + +## stack format + +![](./images/stack_format_ci.svg) \ No newline at end of file diff --git a/core/iwasm/doc/images/export_function.excalidraw b/core/iwasm/doc/images/export_function.excalidraw new file mode 100644 index 0000000000..b983af05b1 --- /dev/null +++ b/core/iwasm/doc/images/export_function.excalidraw @@ -0,0 +1,5695 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "type": "rectangle", + "version": 469, + "versionNonce": 587617691, + "isDeleted": false, + "id": "YQFdEhDm9LI_5UD2YjLU-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 238.99996948242188, + "y": 207.16673278808577, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 221.3333740234375, + "height": 94.629648844401, + "seed": 1964509979, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 318, + "versionNonce": 929539477, + "isDeleted": false, + "id": "pDkkkqvgrizIP6AYdFbkj", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 239.66665649414062, + "y": 161.83335876464827, + "strokeColor": "#1864ab", + "backgroundColor": "transparent", + "width": 96, + "height": 19, + "seed": 278267925, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModule", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule" + }, + { + "type": "rectangle", + "version": 592, + "versionNonce": 276752955, + "isDeleted": false, + "id": "awCpIZpFN-gQRBgh6iG1X", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 228.33334350585938, + "y": 191.50007629394514, + "strokeColor": "#1864ab", + "backgroundColor": "transparent", + "width": 247.11109754774304, + "height": 265.6667175292969, + "seed": 1908273083, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 411, + "versionNonce": 1793600245, + "isDeleted": false, + "id": "TOmX9MwwNOgbfjNJ8V8j7", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 239.4444953070747, + "y": 233.9815283881292, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 211, + "height": 56, + "seed": 86392181, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "2NQmRBF_NE2Myp3-jqLfT", + "type": "arrow" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": " WASMTable *tables;\n WASMMemory *memories;\n WASMGlobal *globals;", + "baseline": 52, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " WASMTable *tables;\n WASMMemory *memories;\n WASMGlobal *globals;" + }, + { + "type": "text", + "version": 390, + "versionNonce": 1283272731, + "isDeleted": false, + "id": "FPUGB-iP7ep91gfoTuV2d", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 289.3334045410156, + "y": 374.1667327880857, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 165, + "height": 19, + "seed": 504570933, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "OdDm5a5O_NIoZbF3u0c0H", + "type": "arrow" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMExport *exports;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMExport *exports;" + }, + { + "type": "rectangle", + "version": 371, + "versionNonce": 692171541, + "isDeleted": false, + "id": "iP-OL8X-L4CE8z9CTp9qG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 243.66677856445312, + "y": 359.5001068115232, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 216, + "height": 42.6666259765625, + "seed": 1531454875, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 336, + "versionNonce": 1261860027, + "isDeleted": false, + "id": "jkgMB1VHPK6x-xD6Wuey7", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 258.3335266113281, + "y": 370.16679382324196, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 15.33331298828125, + "height": 22.66668701171875, + "seed": 2028656021, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 286, + "versionNonce": 1775181787, + "isDeleted": false, + "id": "v4paccURicZAp-WeQNnlQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 585.7036675347218, + "y": 278.944342719184, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 156, + "height": 128.33334350585938, + "seed": 766182741, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 176, + "versionNonce": 907517781, + "isDeleted": false, + "id": "blxQLl_yf7DMD76qHC5rc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 587.9629041883677, + "y": 256.3147176106771, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 97, + "height": 19, + "seed": 1409110677, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "OdDm5a5O_NIoZbF3u0c0H", + "type": "arrow" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMExport", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMExport" + }, + { + "type": "text", + "version": 216, + "versionNonce": 271871099, + "isDeleted": false, + "id": "xQPYBI_XdfyZkh2-U_YQB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 617.3703240288627, + "y": 289.2776862250434, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 88, + "height": 19, + "seed": 1776880725, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "YidAloK-3ikBBzvuu-S22", + "type": "arrow" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "char *name;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "char *name;" + }, + { + "type": "text", + "version": 241, + "versionNonce": 217246901, + "isDeleted": false, + "id": "lB_sRHE0gMYdFdpT2Dvja", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 614.7036370171439, + "y": 321.6110602484809, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 75, + "height": 19, + "seed": 1312575355, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "uint8 kind;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "uint8 kind;" + }, + { + "type": "text", + "version": 230, + "versionNonce": 562504987, + "isDeleted": false, + "id": "SQI7khDAbJL0pLh0deiej", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 610.0369805230033, + "y": 354.944342719184, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 93, + "height": 19, + "seed": 2114894261, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "C_HvFqwDiW4wGe01QNKFg", + "type": "arrow" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "uint32 index;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "uint32 index;" + }, + { + "type": "rectangle", + "version": 188, + "versionNonce": 2042465813, + "isDeleted": false, + "id": "sqBZGoR4vKErEsSE7jSJk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 601.0370110405812, + "y": 284.9443579779731, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 120.66668701171875, + "height": 29.666671752929688, + "seed": 1817827573, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "YidAloK-3ikBBzvuu-S22", + "type": "arrow" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 255, + "versionNonce": 892979643, + "isDeleted": false, + "id": "xy3rvXCHxwYSjuQZAaa0Y", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 601.7036675347218, + "y": 320.6110602484809, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 118, + "height": 26.33331298828125, + "seed": 1394813013, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 190, + "versionNonce": 728564597, + "isDeleted": false, + "id": "noIblgsWe1zKM-bENF8Ev", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 599.0370110405812, + "y": 351.61102973090277, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 126, + "height": 29.666656494140625, + "seed": 410891355, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 174, + "versionNonce": 1447480533, + "isDeleted": false, + "id": "q8XZjMjkc5oaR7KNqOcGF", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 654.3703545464408, + "y": 385.7777167426215, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 20, + "height": 19, + "seed": 1931744507, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "[0]", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 177, + "versionNonce": 804837115, + "isDeleted": false, + "id": "cywWwhg521rh-6jpDBbTE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 586.3703240288625, + "y": 407.94437323676215, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 158, + "height": 42, + "seed": 19921979, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "8dd-iKzlb9Z4V1eqnx9a-" + } + ], + "updated": 1679555887720, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 152, + "versionNonce": 165702197, + "isDeleted": false, + "id": "8dd-iKzlb9Z4V1eqnx9a-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 655.3703240288627, + "y": 419.44437323676215, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 20, + "height": 19, + "seed": 1593993467, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887720, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "[1]", + "baseline": 15, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "cywWwhg521rh-6jpDBbTE", + "originalText": "[1]" + }, + { + "type": "rectangle", + "version": 191, + "versionNonce": 1381646235, + "isDeleted": false, + "id": "SsP3zaE3LuSyRa2Ma4ffB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 587.3703240288625, + "y": 450.94437323676215, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 158, + "height": 42, + "seed": 1216947029, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "Ux1Agae75_DS0veGHXMuH" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 169, + "versionNonce": 1193237397, + "isDeleted": false, + "id": "Ux1Agae75_DS0veGHXMuH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 654.3703240288627, + "y": 462.44437323676215, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 24, + "height": 19, + "seed": 982329467, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "[...]", + "baseline": 15, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "SsP3zaE3LuSyRa2Ma4ffB", + "originalText": "[...]" + }, + { + "type": "text", + "version": 248, + "versionNonce": 1555249749, + "isDeleted": false, + "id": "o7FaW1SjzOKpt86xZNbly", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -71.58126068115234, + "y": 146.11661834716801, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 171, + "height": 19, + "seed": 1768482683, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModuleInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance" + }, + { + "type": "rectangle", + "version": 131, + "versionNonce": 1297489444, + "isDeleted": false, + "id": "MC9rQuxIk7iVRtsas7ICs", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -73.99992370605469, + "y": 180.9666107177734, + "strokeColor": "#d9480f", + "backgroundColor": "transparent", + "width": 210.66668701171875, + "height": 314.33334350585943, + "seed": 1805099445, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558426546, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 473, + "versionNonce": 1484728732, + "isDeleted": false, + "id": "6pGqkyRY8AHL4k5LTJ0Yl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -51.000274658203125, + "y": 425.4667785644531, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174.66668701171875, + "height": 31.666625976562507, + "seed": 957429083, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558303924, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 499, + "versionNonce": 1368837668, + "isDeleted": false, + "id": "cDohxEkx3HhhoX-zcCc9Q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -45.666961669921875, + "y": 433.1334350585937, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 63676885, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558303924, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 208, + "versionNonce": 1386190364, + "isDeleted": false, + "id": "kOP_SYX2hDGyTbKOLrSY7", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -25.66693115234375, + "y": 429.9667785644531, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 115, + "height": 20, + "seed": 697815547, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "xGVX08X1n0JU0OA60_t9A", + "type": "arrow" + } + ], + "updated": 1679558303924, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "export_tables", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "export_tables" + }, + { + "type": "rectangle", + "version": 534, + "versionNonce": 1960518965, + "isDeleted": false, + "id": "FnISIa4MPRb3okZ9nUHSQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -58.66651916503906, + "y": 359.8333740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174.66668701171875, + "height": 31.666625976562507, + "seed": 1723426171, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 562, + "versionNonce": 901559451, + "isDeleted": false, + "id": "KPco9Nm_8Olmg0Uq3NVQ1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -53.33320617675781, + "y": 367.5000305175781, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 135660469, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 260, + "versionNonce": 420132501, + "isDeleted": false, + "id": "8KE83CX20gDJQwJueCKwN", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -39.33323669433594, + "y": 365.66668701171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 131, + "height": 20, + "seed": 788856347, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "v2uiV8UbBxa6_yEOLAehf", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "export_memories", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "export_memories" + }, + { + "type": "rectangle", + "version": 477, + "versionNonce": 1527867707, + "isDeleted": false, + "id": "_CbJcjswexYEsNuWct5mC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -51.99998474121094, + "y": 189.83334350585938, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174.66668701171875, + "height": 31.666625976562507, + "seed": 1344002453, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 505, + "versionNonce": 671161333, + "isDeleted": false, + "id": "hGw4Pp4LnIpkfSfU4PeRb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -46.66667175292969, + "y": 197.5, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 861402683, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 213, + "versionNonce": 690391515, + "isDeleted": false, + "id": "67MrbK1oKDTBrmUzIQlr1", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -26.666641235351562, + "y": 194.33334350585938, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 118, + "height": 20, + "seed": 1190518517, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "export_globals", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "export_globals" + }, + { + "type": "rectangle", + "version": 400, + "versionNonce": 1237570901, + "isDeleted": false, + "id": "eMcaE0yc0BCbsQ4MrLuGP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -51.33323669433594, + "y": 271.8333740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174.66668701171875, + "height": 31.666625976562507, + "seed": 1337283029, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 429, + "versionNonce": 84842107, + "isDeleted": false, + "id": "rW76UbdBzelN0CWuqQjTc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -45.99992370605469, + "y": 279.5000305175781, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 778117627, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "4eMPasZehGc58H7vVusCf", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 136, + "versionNonce": 162030261, + "isDeleted": false, + "id": "NfPonNQyhbRCCPh50Ed7I", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -25.999893188476562, + "y": 276.3333740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 136, + "height": 20, + "seed": 1026969397, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "4eMPasZehGc58H7vVusCf", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "export_functions", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "export_functions" + }, + { + "type": "text", + "version": 121, + "versionNonce": 227282715, + "isDeleted": false, + "id": "ILDnCNxtBv4qdrhMb3QoZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -365.33335876464844, + "y": 193.1666259765625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 193, + "height": 19, + "seed": 282525979, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMExportFuncInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMExportFuncInstance" + }, + { + "type": "rectangle", + "version": 201, + "versionNonce": 1817461781, + "isDeleted": false, + "id": "2agHBzF-91Fb2ws0hYObd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -354.33335876464844, + "y": 223.83334350585938, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 177.33331298828122, + "height": 121.00006103515625, + "seed": 1290367509, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "4eMPasZehGc58H7vVusCf", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 176, + "versionNonce": 1961256348, + "isDeleted": false, + "id": "aZL7IU5LEszDJmyyWcmtp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -340.6666717529297, + "y": 239.83328247070312, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 117, + "height": 31, + "seed": 1714300347, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "P3_9LebxO4r1Nud8xISGk" + }, + { + "id": "YidAloK-3ikBBzvuu-S22", + "type": "arrow" + } + ], + "updated": 1679558297714, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 194, + "versionNonce": 2136035876, + "isDeleted": false, + "id": "P3_9LebxO4r1Nud8xISGk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -334.6666717529297, + "y": 244.83328247070312, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 105, + "height": 21, + "seed": 2004641653, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297727, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "char* name ", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aZL7IU5LEszDJmyyWcmtp", + "originalText": "char* name " + }, + { + "type": "diamond", + "version": 159, + "versionNonce": 1002521691, + "isDeleted": false, + "id": "qdga26-o2AGoXkwH2yP4N", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -323.00001525878906, + "y": 284.1666259765625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 14.666656494140625, + "height": 17.666656494140625, + "seed": 444053083, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 216, + "versionNonce": 1328022229, + "isDeleted": false, + "id": "2onXRIpW3Znosk6O46QtY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -338.33335876464844, + "y": 276.49993896484375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 128, + "height": 38, + "seed": 852078805, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "Zrwh0TsrNxBSNhpJZIqbu" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 183, + "versionNonce": 1455378972, + "isDeleted": false, + "id": "Zrwh0TsrNxBSNhpJZIqbu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -305.83335876464844, + "y": 285.49993896484375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 63, + "height": 21, + "seed": 2089169659, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297728, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "function", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "2onXRIpW3Znosk6O46QtY", + "originalText": "function" + }, + { + "type": "arrow", + "version": 505, + "versionNonce": 2099961909, + "isDeleted": false, + "id": "nG4vs8WttuFFZPXDKIdNr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -320.33335876464844, + "y": 293.49993896484375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 111.00001525878906, + "height": 184.2470495648475, + "seed": 476499509, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "7JcYtZ-KQ0WF1SDFDwvcV", + "gap": 1.2531640581994319, + "focus": -0.8059388021902064 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -111.00001525878906, + 33.166717529296875 + ], + [ + -35.26115412319115, + 184.2470495648475 + ] + ] + }, + { + "type": "text", + "version": 531, + "versionNonce": 363532693, + "isDeleted": false, + "id": "7MNv-pmgV4-8yJ5lIFY1U", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 496.5186225043402, + "y": 71.31480577256946, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 235, + "height": 149, + "seed": 2074251419, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "YidAloK-3ikBBzvuu-S22", + "type": "arrow" + }, + { + "id": "2NQmRBF_NE2Myp3-jqLfT", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "name: shown to external\nindex: refer to item idx for export \n in current kind\nkind: total 4 export kinds:\n- function\n- globals\n- memory\n- table", + "baseline": 145, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "name: shown to external\nindex: refer to item idx for export \n in current kind\nkind: total 4 export kinds:\n- function\n- globals\n- memory\n- table" + }, + { + "type": "rectangle", + "version": 81, + "versionNonce": 997735995, + "isDeleted": false, + "id": "orpW0tFerpELyreVDruzO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -353.3333740234375, + "y": 343.99998474121094, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 940489147, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "KlcGzdua5BpLa_-0RGeFO" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 53, + "versionNonce": 1965780388, + "isDeleted": false, + "id": "KlcGzdua5BpLa_-0RGeFO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -274.3333740234375, + "y": 350.1666564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 22, + "height": 21, + "seed": 1399452699, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297729, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[1]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "orpW0tFerpELyreVDruzO", + "originalText": "[1]" + }, + { + "type": "text", + "version": 71, + "versionNonce": 1609219803, + "isDeleted": false, + "id": "da0XYl9R6gkV-BQ4X1JdF", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -278.166748046875, + "y": 324.33338928222656, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 848835675, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 109, + "versionNonce": 528755579, + "isDeleted": false, + "id": "oVhH9XqC6rs2uaPMH4x2B", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -353.0001220703125, + "y": 377.50006103515625, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 1099143605, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "wu6BDVXXPWLo-Z-l7TYGW" + }, + { + "id": "Eda9W8eaZom6PATRAm5EX", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 83, + "versionNonce": 1472638620, + "isDeleted": false, + "id": "wu6BDVXXPWLo-Z-l7TYGW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -278.5001220703125, + "y": 383.66673278808594, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 21, + "seed": 1029949467, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297730, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "oVhH9XqC6rs2uaPMH4x2B", + "originalText": "[...]" + }, + { + "type": "rectangle", + "version": 134, + "versionNonce": 1882593572, + "isDeleted": false, + "id": "t8xOhAVyJcx51xJsmWC6j", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -352.33331298828125, + "y": 408.5001220703125, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 31, + "seed": 883564757, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "F09zazRYIO_G3oM7DmBbr" + }, + { + "id": "Eda9W8eaZom6PATRAm5EX", + "type": "arrow" + } + ], + "updated": 1679558297730, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 111, + "versionNonce": 1756265244, + "isDeleted": false, + "id": "F09zazRYIO_G3oM7DmBbr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -274.83331298828125, + "y": 418.5001220703125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 25, + "height": 21, + "seed": 1529135867, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297738, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[n]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "t8xOhAVyJcx51xJsmWC6j", + "originalText": "[n]" + }, + { + "type": "line", + "version": 284, + "versionNonce": 350603451, + "isDeleted": false, + "id": "XphCtXmoQONIDWT15UPmf", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 169.00015258789057, + "y": 21.389623853895444, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 1.66656494140625, + "height": 838.3330154418943, + "seed": 395797243, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 1.66656494140625, + 838.3330154418943 + ] + ] + }, + { + "type": "arrow", + "version": 156, + "versionNonce": 1808036981, + "isDeleted": false, + "id": "4eMPasZehGc58H7vVusCf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -40.66667175292969, + "y": 278.5517677558588, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 133.01236173861167, + "height": 54.587889349881976, + "seed": 526956341, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "startBinding": { + "elementId": "NfPonNQyhbRCCPh50Ed7I", + "focus": -0.6897037086038879, + "gap": 14.666778564453125 + }, + "endBinding": { + "elementId": "2agHBzF-91Fb2ws0hYObd", + "focus": -1.0127197558064842, + "gap": 3.3210122848258408 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -133.01236173861167, + -54.587889349881976 + ] + ] + }, + { + "type": "rectangle", + "version": 227, + "versionNonce": 2017655131, + "isDeleted": false, + "id": "7JcYtZ-KQ0WF1SDFDwvcV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -362, + "y": 478.3334655761719, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 223.3334655761719, + "height": 33.00015258789055, + "seed": 857261429, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "nG4vs8WttuFFZPXDKIdNr", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 351, + "versionNonce": 1552424405, + "isDeleted": false, + "id": "QLOWRmBSKHUu_NrX66JPt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -355.3333740234375, + "y": 486.00006103515625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 208, + "height": 20, + "seed": 513338651, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "nG4vs8WttuFFZPXDKIdNr", + "type": "arrow" + }, + { + "id": "Vhva2LNBhtrohj4Y3bGV9", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(refer to function diagam)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(refer to function diagam)" + }, + { + "type": "text", + "version": 94, + "versionNonce": 636556795, + "isDeleted": false, + "id": "R3bNKi3-D-UlbcoafRHR5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -40.6666259765625, + "y": 316.9998779296875, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 154, + "height": 20, + "seed": 1038062939, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Eda9W8eaZom6PATRAm5EX", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "export_func_count", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "export_func_count" + }, + { + "type": "arrow", + "version": 162, + "versionNonce": 1037015861, + "isDeleted": false, + "id": "Eda9W8eaZom6PATRAm5EX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -51.354154838890395, + "y": 333.9999084472656, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 192.10502044691845, + "height": 89.91317165306509, + "seed": 518031893, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "startBinding": { + "elementId": "mZobbVjOOfzbAK_1alOcK", + "focus": 0.6974503894293331, + "gap": 1 + }, + "endBinding": { + "elementId": "oVhH9XqC6rs2uaPMH4x2B", + "focus": 0.8116332021918504, + "gap": 14.079675559315092 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -81.97918866696898, + 54.666717529296875 + ], + [ + -192.10502044691845, + 89.91317165306509 + ] + ] + }, + { + "type": "rectangle", + "version": 71, + "versionNonce": 2013293717, + "isDeleted": false, + "id": "mZobbVjOOfzbAK_1alOcK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -50.66668701171875, + "y": 311.333251953125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 172.666748046875, + "height": 31.66668701171875, + "seed": 1172380085, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Eda9W8eaZom6PATRAm5EX", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 417, + "versionNonce": 1255150069, + "isDeleted": false, + "id": "YyGWHpfM0OE4uzGAFwZ12", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 484.1481289333766, + "y": 65.53704833984384, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 287.70376925998266, + "height": 162.29637824164502, + "seed": 274906523, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "YidAloK-3ikBBzvuu-S22", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 1048, + "versionNonce": 392819675, + "isDeleted": false, + "id": "YidAloK-3ikBBzvuu-S22", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 743.579402430669, + "y": 225.04108862166348, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 21.48420572561895, + "height": 53.29342358325337, + "seed": 1380550619, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "startBinding": { + "elementId": "7MNv-pmgV4-8yJ5lIFY1U", + "focus": -0.6782971803849929, + "gap": 12.953770184814061 + }, + "endBinding": { + "elementId": "sqBZGoR4vKErEsSE7jSJk", + "focus": 0.7448990456524555, + "gap": 10.515841746169144 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 10.124343093419043, + 26.347898601643806 + ], + [ + -11.359862632199906, + 53.29342358325337 + ] + ] + }, + { + "type": "text", + "version": 264, + "versionNonce": 1665403733, + "isDeleted": false, + "id": "sRzZXOvjblsPsAM89sUXa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -705.3333892822266, + "y": 113.58320617675781, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 192, + "height": 19, + "seed": 700308213, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMExportGlobInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMExportGlobInstance" + }, + { + "type": "rectangle", + "version": 348, + "versionNonce": 1800716411, + "isDeleted": false, + "id": "xWPtNuyAqazMMUyouKnhf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -694.3333892822266, + "y": 144.2499237060547, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 177.33331298828122, + "height": 121.00006103515625, + "seed": 1037646555, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "RxhU0Qr-hmqzklRZdxsvn", + "type": "arrow" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 323, + "versionNonce": 467621028, + "isDeleted": false, + "id": "7uYVutqCDMjhS1LtgrSpl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -680.6667022705078, + "y": 160.24986267089844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 117, + "height": 31, + "seed": 328250453, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "PlRFeDiD1tywOAk_rJgHg" + } + ], + "updated": 1679558297741, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 337, + "versionNonce": 334018460, + "isDeleted": false, + "id": "PlRFeDiD1tywOAk_rJgHg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -674.6667022705078, + "y": 165.24986267089844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 105, + "height": 21, + "seed": 843261819, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297749, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "char* name ", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7uYVutqCDMjhS1LtgrSpl", + "originalText": "char* name " + }, + { + "type": "diamond", + "version": 301, + "versionNonce": 2143222293, + "isDeleted": false, + "id": "01IysLeEkWSJyxI9NbVb8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -663.0000457763672, + "y": 204.5832061767578, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 14.666656494140625, + "height": 17.666656494140625, + "seed": 1686200757, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 360, + "versionNonce": 458563003, + "isDeleted": false, + "id": "lWjh1xjt4eB1XKwJJx59D", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -678.3333892822266, + "y": 196.91651916503906, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 128, + "height": 38, + "seed": 671514651, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "VQ0ymOLfMpjFiupR01ZFb" + } + ], + "updated": 1679555887721, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 327, + "versionNonce": 97853476, + "isDeleted": false, + "id": "VQ0ymOLfMpjFiupR01ZFb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -637.3333892822266, + "y": 205.91651916503906, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 46, + "height": 21, + "seed": 565535509, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297750, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "global", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lWjh1xjt4eB1XKwJJx59D", + "originalText": "global" + }, + { + "type": "arrow", + "version": 624, + "versionNonce": 2147462747, + "isDeleted": false, + "id": "E_RGDiJbGUf1aNH6jDnvz", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -660.3333892822266, + "y": 213.91651916503906, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 111.00001525878906, + "height": 176.67764729852638, + "seed": 152421563, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "KWKN5CpCB6zVtYZd6txj9", + "focus": -0.8240578974308156, + "gap": 7.457318223231596 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -111.00001525878906, + 33.166717529296875 + ], + [ + -17.790676987880033, + 176.67764729852638 + ] + ] + }, + { + "type": "rectangle", + "version": 225, + "versionNonce": 1372149301, + "isDeleted": false, + "id": "uLMWTXI90CGAIKI4zHJML", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -693.3334045410156, + "y": 264.41656494140625, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 1208836565, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "AGXBiW0dSLHn3nGEzduCY" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 196, + "versionNonce": 1168598044, + "isDeleted": false, + "id": "AGXBiW0dSLHn3nGEzduCY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -614.3334045410156, + "y": 270.58323669433594, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 22, + "height": 21, + "seed": 324145659, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297750, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[1]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uLMWTXI90CGAIKI4zHJML", + "originalText": "[1]" + }, + { + "type": "text", + "version": 213, + "versionNonce": 2064304021, + "isDeleted": false, + "id": "QvT_bIR-th1HBXIOu8j7Y", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -618.1667785644531, + "y": 244.74996948242188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 269246261, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 253, + "versionNonce": 630646843, + "isDeleted": false, + "id": "zom5ZNn_gBWzVI1PMMYQo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -693.0001525878906, + "y": 297.91664123535156, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 215227035, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "U2e2lWVjWzkRt9x6FkfeD" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 226, + "versionNonce": 1443199908, + "isDeleted": false, + "id": "U2e2lWVjWzkRt9x6FkfeD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -618.5001525878906, + "y": 304.08331298828125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 21, + "seed": 1845801109, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297751, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "zom5ZNn_gBWzVI1PMMYQo", + "originalText": "[...]" + }, + { + "type": "rectangle", + "version": 278, + "versionNonce": 333217948, + "isDeleted": false, + "id": "IbfRch-fWorg9yld9Wih5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -692.3333435058594, + "y": 328.9167022705078, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 31, + "seed": 1716260667, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "So5vnnPiQUj6m6ZH6HSMP" + } + ], + "updated": 1679558297752, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 254, + "versionNonce": 111044388, + "isDeleted": false, + "id": "So5vnnPiQUj6m6ZH6HSMP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -614.8333435058594, + "y": 338.9167022705078, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 25, + "height": 21, + "seed": 2141967861, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297758, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[n]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IbfRch-fWorg9yld9Wih5", + "originalText": "[n]" + }, + { + "type": "rectangle", + "version": 257, + "versionNonce": 494949755, + "isDeleted": false, + "id": "KWKN5CpCB6zVtYZd6txj9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -670.666748046875, + "y": 392.7500457763672, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 196.00015258789062, + "height": 39.33346557617187, + "seed": 35418075, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "E_RGDiJbGUf1aNH6jDnvz", + "type": "arrow" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 415, + "versionNonce": 964039605, + "isDeleted": false, + "id": "uEz53YqVhDB8JRuE5hMcU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -659.3334655761719, + "y": 402.41664123535156, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 164, + "height": 20, + "seed": 268914517, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMGlobalInstance", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMGlobalInstance" + }, + { + "type": "text", + "version": 236, + "versionNonce": 2005417979, + "isDeleted": false, + "id": "FiGf5f4dzHNrO5L3NMiwT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -604.5001068115234, + "y": 504.4166564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 193, + "height": 19, + "seed": 1644818613, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "v2uiV8UbBxa6_yEOLAehf", + "type": "arrow" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMExportMemInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMExportMemInstance" + }, + { + "type": "rectangle", + "version": 316, + "versionNonce": 1410863413, + "isDeleted": false, + "id": "tgz_9er4fEh_TbaYDZF6u", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -593.5001068115234, + "y": 535.0833740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 177.33331298828122, + "height": 121.00006103515625, + "seed": 273834267, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 295, + "versionNonce": 104467740, + "isDeleted": false, + "id": "fZ94UaMm9ypc37Hwvn9SX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -579.8334197998047, + "y": 551.0833129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 117, + "height": 31, + "seed": 492048917, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "QBs13zYnt2LZX4M9cdFuH" + } + ], + "updated": 1679558297759, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 308, + "versionNonce": 470672036, + "isDeleted": false, + "id": "QBs13zYnt2LZX4M9cdFuH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -573.8334197998047, + "y": 556.0833129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 105, + "height": 21, + "seed": 524125627, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297764, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "char* name ", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fZ94UaMm9ypc37Hwvn9SX", + "originalText": "char* name " + }, + { + "type": "diamond", + "version": 271, + "versionNonce": 424590651, + "isDeleted": false, + "id": "nqJwiZ18KG3vCMfANM8aQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -562.1667633056641, + "y": 595.4166564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 14.666656494140625, + "height": 17.666656494140625, + "seed": 563283829, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 332, + "versionNonce": 1978861557, + "isDeleted": false, + "id": "HEJvi8QjWRiMWcoZPLHIl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -577.5001068115234, + "y": 587.7499694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 128, + "height": 38, + "seed": 983242331, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "HU6nOicEloKkqYD55hBWR" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 304, + "versionNonce": 1580337564, + "isDeleted": false, + "id": "HU6nOicEloKkqYD55hBWR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -540.5001068115234, + "y": 596.7499694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 54, + "height": 21, + "seed": 554455253, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297765, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "memory", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HEJvi8QjWRiMWcoZPLHIl", + "originalText": "memory" + }, + { + "type": "arrow", + "version": 546, + "versionNonce": 1519927637, + "isDeleted": false, + "id": "O2ixOO7WSexT4tbgcUDGA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -559.5001068115234, + "y": 604.7499694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 111.00001525878906, + "height": 176.67764729852638, + "seed": 20812539, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "Sn4zW4Qj5F8AgPuajBnRy", + "focus": -0.8240578974308156, + "gap": 7.457318223231596 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -111.00001525878906, + 33.166717529296875 + ], + [ + -17.790676987880033, + 176.67764729852638 + ] + ] + }, + { + "type": "text", + "version": 371, + "versionNonce": 1795615355, + "isDeleted": false, + "id": "XjbCYdp81z8HsAXal748C", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -794.1667785644531, + "y": 589.7500152587891, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 190, + "height": 20, + "seed": 1767206453, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(WASMMemoryInstance*)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(WASMMemoryInstance*)" + }, + { + "type": "rectangle", + "version": 197, + "versionNonce": 1561284277, + "isDeleted": false, + "id": "8-N4VCgO26s4M3_joLoAg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -592.5001220703125, + "y": 655.2500152587891, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 329743259, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "_zHc4H95qdMAwSfBAe4ji" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 167, + "versionNonce": 1560407588, + "isDeleted": false, + "id": "_zHc4H95qdMAwSfBAe4ji", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -513.5001220703125, + "y": 661.4166870117188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 22, + "height": 21, + "seed": 2035539861, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297766, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[1]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "8-N4VCgO26s4M3_joLoAg", + "originalText": "[1]" + }, + { + "type": "text", + "version": 183, + "versionNonce": 201175061, + "isDeleted": false, + "id": "8MK4DV6gTxwniJIipRt0r", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -517.33349609375, + "y": 635.5834197998047, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 1577799739, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 225, + "versionNonce": 313275, + "isDeleted": false, + "id": "tIAX2kAFoZpVqZ5qUAx0Z", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -592.1668701171875, + "y": 688.7500915527344, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 1463624949, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "2puZDaevwGxRcBgZ-ptc6" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 197, + "versionNonce": 1593210396, + "isDeleted": false, + "id": "2puZDaevwGxRcBgZ-ptc6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -517.6668701171875, + "y": 694.9167633056641, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 21, + "seed": 575377627, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297767, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "tIAX2kAFoZpVqZ5qUAx0Z", + "originalText": "[...]" + }, + { + "type": "rectangle", + "version": 250, + "versionNonce": 174623140, + "isDeleted": false, + "id": "N0OFLsAea9yT6OuliaHBO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -591.5000610351562, + "y": 719.7501525878906, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 31, + "seed": 647413333, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "q7ppugt7g7qcpJ5yt5OdE" + } + ], + "updated": 1679558297767, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 225, + "versionNonce": 360272540, + "isDeleted": false, + "id": "q7ppugt7g7qcpJ5yt5OdE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -514.0000610351562, + "y": 729.7501525878906, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 25, + "height": 21, + "seed": 1106951547, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297771, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[n]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "N0OFLsAea9yT6OuliaHBO", + "originalText": "[n]" + }, + { + "type": "rectangle", + "version": 228, + "versionNonce": 647779579, + "isDeleted": false, + "id": "Sn4zW4Qj5F8AgPuajBnRy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -569.8334655761719, + "y": 783.58349609375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 196.00015258789062, + "height": 39.33346557617187, + "seed": 2117479349, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "O2ixOO7WSexT4tbgcUDGA", + "type": "arrow" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 386, + "versionNonce": 1606143029, + "isDeleted": false, + "id": "ADaMm1yoqbZuEu8DJV90L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -558.5001831054688, + "y": 793.2500915527344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 170, + "height": 20, + "seed": 1213945371, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMMemoryInstance", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMMemoryInstance" + }, + { + "type": "arrow", + "version": 134, + "versionNonce": 1991153820, + "isDeleted": false, + "id": "RxhU0Qr-hmqzklRZdxsvn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -42.66673278808594, + "y": 209.66668701171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 470.2476914752049, + "height": 74.33332824707031, + "seed": 1569902293, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679558440686, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "xWPtNuyAqazMMUyouKnhf", + "focus": -0.7987290948340221, + "gap": 4.085652030654501 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -274, + -74.33332824707031 + ], + [ + -470.2476914752049, + -62.82886586813058 + ] + ] + }, + { + "type": "arrow", + "version": 214, + "versionNonce": 1329495445, + "isDeleted": false, + "id": "v2uiV8UbBxa6_yEOLAehf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -54.615555578359704, + "y": 386.91916605443436, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 362.7179252566011, + "height": 185.08100179224533, + "seed": 1266609179, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "startBinding": { + "elementId": "8KE83CX20gDJQwJueCKwN", + "focus": 1.000586020261827, + "gap": 15.33355712890625 + }, + "endBinding": { + "elementId": "FiGf5f4dzHNrO5L3NMiwT", + "focus": 0.2798025099622999, + "gap": 11.916763305664062 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -132.71792525660123, + 185.08100179224533 + ], + [ + -362.7179252566011, + 148.41425374537033 + ] + ] + }, + { + "type": "text", + "version": 48, + "versionNonce": 304349941, + "isDeleted": false, + "id": "VoO6IQkaaomgHGzvmdk9L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -107.00004577636719, + "y": 551.3334197998047, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 184, + "height": 19, + "seed": 2114679797, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMExportTabInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMExportTabInstance" + }, + { + "type": "rectangle", + "version": 353, + "versionNonce": 340366043, + "isDeleted": false, + "id": "cfV2B-j5UbmISEWVZNm_5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -108.33344268798828, + "y": 571.0000915527344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 177.33331298828122, + "height": 121.00006103515625, + "seed": 2095295067, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 335, + "versionNonce": 393196836, + "isDeleted": false, + "id": "DJ8H7VUIn868NKonqg5gA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -94.66675567626953, + "y": 587.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 117, + "height": 31, + "seed": 1950809301, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "kVEW0vY1bUiCqI5IpX5ML" + }, + { + "id": "xGVX08X1n0JU0OA60_t9A", + "type": "arrow" + } + ], + "updated": 1679558297772, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 346, + "versionNonce": 543989532, + "isDeleted": false, + "id": "kVEW0vY1bUiCqI5IpX5ML", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -88.66675567626953, + "y": 592.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 105, + "height": 21, + "seed": 357803771, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297776, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "char* name ", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "DJ8H7VUIn868NKonqg5gA", + "originalText": "char* name " + }, + { + "type": "diamond", + "version": 308, + "versionNonce": 930974133, + "isDeleted": false, + "id": "gUolYbJIMeIJ_CDZqZFxE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -77.0000991821289, + "y": 631.3333740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 14.666656494140625, + "height": 17.666656494140625, + "seed": 1482332725, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 371, + "versionNonce": 809555995, + "isDeleted": false, + "id": "KIe-ngH8dNcYj_TNLGGa1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -92.33344268798828, + "y": 623.6666870117188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 128, + "height": 38, + "seed": 1675300763, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "NtF7gKcUrgH2P2tIg7Y1I" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 343, + "versionNonce": 1636135076, + "isDeleted": false, + "id": "NtF7gKcUrgH2P2tIg7Y1I", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -49.83344268798828, + "y": 632.6666870117188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 43, + "height": 21, + "seed": 651398037, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297777, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "table", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "KIe-ngH8dNcYj_TNLGGa1", + "originalText": "table" + }, + { + "type": "rectangle", + "version": 236, + "versionNonce": 1212488891, + "isDeleted": false, + "id": "1vaZwH7ZrMW37Kus4v6s8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -107.33345794677734, + "y": 691.1667327880859, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 179070011, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "EcHCtg-lwToZUzRwQtRqU" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 205, + "versionNonce": 2017614748, + "isDeleted": false, + "id": "EcHCtg-lwToZUzRwQtRqU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -28.333457946777344, + "y": 697.3334045410156, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 22, + "height": 21, + "seed": 266817781, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297777, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[1]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1vaZwH7ZrMW37Kus4v6s8", + "originalText": "[1]" + }, + { + "type": "text", + "version": 220, + "versionNonce": 748146011, + "isDeleted": false, + "id": "xnv1ofRPmnyf7Y75joBRd", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -32.166831970214844, + "y": 671.5001373291016, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 1101669595, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 264, + "versionNonce": 1671072213, + "isDeleted": false, + "id": "WhKSAa-6xEaKbFn82X5ru", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -107.00020599365234, + "y": 724.6668090820312, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 32.333343505859375, + "seed": 555444821, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "-gxwjWzmqS5WDjuFaNKFD" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 235, + "versionNonce": 1750580260, + "isDeleted": false, + "id": "-gxwjWzmqS5WDjuFaNKFD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -32.500205993652344, + "y": 730.8334808349609, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 21, + "seed": 543609211, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297778, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "WhKSAa-6xEaKbFn82X5ru", + "originalText": "[...]" + }, + { + "type": "rectangle", + "version": 289, + "versionNonce": 925665308, + "isDeleted": false, + "id": "10EyNsT35ULiP_xM9qv8Y", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -106.3333969116211, + "y": 755.6668701171875, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 180, + "height": 31, + "seed": 457529269, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "x2msRpv4tJnWtZ_hp50wB" + } + ], + "updated": 1679558297778, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 263, + "versionNonce": 97818532, + "isDeleted": false, + "id": "x2msRpv4tJnWtZ_hp50wB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -28.833396911621094, + "y": 765.6668701171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 25, + "height": 21, + "seed": 673507867, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297782, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[n]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "10EyNsT35ULiP_xM9qv8Y", + "originalText": "[n]" + }, + { + "type": "text", + "version": 76, + "versionNonce": 375904405, + "isDeleted": false, + "id": "0XlRzavkxOV0TYo3Cru6q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -307.66673278808594, + "y": 651.0002899169922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 171, + "height": 19, + "seed": 1584858171, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "GXMgwVG5yviwCuQz9-Jdx", + "type": "arrow" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "(WASMTableInstance *)", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(WASMTableInstance *)" + }, + { + "type": "arrow", + "version": 29, + "versionNonce": 1192191803, + "isDeleted": false, + "id": "GXMgwVG5yviwCuQz9-Jdx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -67.33335876464844, + "y": 639.0001068115234, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 69.3333740234375, + "height": 18.333343505859375, + "seed": 1221178005, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "0XlRzavkxOV0TYo3Cru6q", + "focus": 0.6054948422392628, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -69.3333740234375, + 18.333343505859375 + ] + ] + }, + { + "type": "arrow", + "version": 104, + "versionNonce": 1499556260, + "isDeleted": false, + "id": "xGVX08X1n0JU0OA60_t9A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -41.0000457763672, + "y": 446.7786348431563, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 81.66662597656249, + "height": 127.22147196836715, + "seed": 1672312955, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679558303924, + "link": null, + "locked": false, + "startBinding": { + "elementId": "kOP_SYX2hDGyTbKOLrSY7", + "focus": 1.0138390872785434, + "gap": 15.333114624023438 + }, + "endBinding": { + "elementId": "DJ8H7VUIn868NKonqg5gA", + "focus": -0.9738124716848731, + "gap": 15.333290100097656 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -81.66662597656249, + 95.22147196836715 + ], + [ + -68.99999999999999, + 127.22147196836715 + ] + ] + }, + { + "type": "text", + "version": 42, + "versionNonce": 548718555, + "isDeleted": false, + "id": "HULjVKYZds5RYxESw_kpb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -343.66673278808594, + "y": 454.3334197998047, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 173, + "height": 19, + "seed": 2051995003, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887722, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMFunctionInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunctionInstance" + }, + { + "type": "rectangle", + "version": 116, + "versionNonce": 130053973, + "isDeleted": false, + "id": "kgRAri5TBI6AVeJYwZ0aO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 291.8626718521118, + "y": 615.9665649414064, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 229.3333740234375, + "height": 71.33331298828125, + "seed": 1363010869, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "GHvOda4nqju-pWyZr-_zS" + } + ], + "updated": 1679555887722, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 70, + "versionNonce": 1629299868, + "isDeleted": false, + "id": "GHvOda4nqju-pWyZr-_zS", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 393.02935886383057, + "y": 642.0332214355467, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 27, + "height": 21, + "seed": 1441133723, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679558297783, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[..]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kgRAri5TBI6AVeJYwZ0aO", + "originalText": "[..]" + }, + { + "type": "text", + "version": 339, + "versionNonce": 1673577653, + "isDeleted": false, + "id": "oxCwJMeoYhRvkKNPtKeha", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 292.13729763031006, + "y": 587.3669616699217, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 211, + "height": 20, + "seed": 1528133269, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Vhva2LNBhtrohj4Y3bGV9", + "type": "arrow" + }, + { + "id": "tV8skfel8ww2IgWRNSntj", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMFunction (per module)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunction (per module)" + }, + { + "type": "arrow", + "version": 77, + "versionNonce": 1227017499, + "isDeleted": false, + "id": "Vhva2LNBhtrohj4Y3bGV9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -140.00001525878906, + "y": 499.6667022705078, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 435.3333740234375, + "height": 121, + "seed": 975356629, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": { + "elementId": "QLOWRmBSKHUu_NrX66JPt", + "focus": -0.5172589859849054, + "gap": 7.3333587646484375 + }, + "endBinding": { + "elementId": "oxCwJMeoYhRvkKNPtKeha", + "focus": -1.2215352226224219, + "gap": 13.29974060058612 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 230, + 35.33331298828125 + ], + [ + 435.3333740234375, + 121 + ] + ] + }, + { + "type": "rectangle", + "version": 220, + "versionNonce": 1520753525, + "isDeleted": false, + "id": "kby0LOGKuGuYeMzR0L7Pt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 602.3332366943359, + "y": 559.4166564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 47414645, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 241, + "versionNonce": 12821717, + "isDeleted": false, + "id": "J-wnUXeWDAfVNvwQHeW06", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 601.9998626708984, + "y": 592.5832824707031, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 785254101, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 226, + "versionNonce": 441810683, + "isDeleted": false, + "id": "qpSAJ6K0S6TvU6i2Uflep", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 625.9998626708984, + "y": 600.2499389648438, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1914322171, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "euj5CRuv6EoS2abVhp14P", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 229, + "versionNonce": 2130854453, + "isDeleted": false, + "id": "yCRds2HRNFQhi2l_NRv1E", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 601.6665496826172, + "y": 625.9166259765625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 24142901, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "tV8skfel8ww2IgWRNSntj", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 238, + "versionNonce": 451012507, + "isDeleted": false, + "id": "XQ_n6PzQRK1pjOIbTF09C", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 600.3331756591797, + "y": 653.5832824707031, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1465836955, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 219, + "versionNonce": 1782985621, + "isDeleted": false, + "id": "dQpthmaEJfLeEUR4UsOsg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 622.9998626708984, + "y": 661.9166259765625, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 2124420501, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 231, + "versionNonce": 1796750395, + "isDeleted": false, + "id": "lRGlqO5nnxQXGumLJpnO9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 624.3332366943359, + "y": 630.8333282470703, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1262844475, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 90, + "versionNonce": 516225269, + "isDeleted": false, + "id": "bCPa9rRVzubIpa31jwl73", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 574.0000152587891, + "y": 532.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 177.33334350585938, + "seed": 294820597, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "6EZFlJGbMoYN4RDYkSEYP", + "type": "arrow" + }, + { + "id": "euj5CRuv6EoS2abVhp14P", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 534, + "versionNonce": 1457192155, + "isDeleted": false, + "id": "C_HvFqwDiW4wGe01QNKFg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 715.7036827935111, + "y": 367.61115180121527, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00552481453781, + "height": 1.0071746818260863, + "seed": 1379035003, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": { + "elementId": "SQI7khDAbJL0pLh0deiej", + "focus": 0.39512687910745115, + "gap": 12.666702270507812 + }, + "endBinding": { + "elementId": "2jRIgxhs5VlnfeDeDJXss", + "focus": 0.24394927767367422, + "gap": 2.171731894901793 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 70.00552481453781, + -1.0071746818260863 + ] + ] + }, + { + "type": "arrow", + "version": 48, + "versionNonce": 163359099, + "isDeleted": false, + "id": "tV8skfel8ww2IgWRNSntj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 622.6667327880859, + "y": 610.3333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 105.3333740234375, + "height": 3.666656494140625, + "seed": 1040768149, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": { + "elementId": "yCRds2HRNFQhi2l_NRv1E", + "focus": 1.968489954492859, + "gap": 15.583236694335938 + }, + "endBinding": { + "elementId": "oxCwJMeoYhRvkKNPtKeha", + "focus": 1.5212851912013483, + "gap": 14.196061134338379 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -105.3333740234375, + 3.666656494140625 + ] + ] + }, + { + "type": "rectangle", + "version": 138, + "versionNonce": 2094596021, + "isDeleted": false, + "id": "lbEH3IbUKE-BUvPaOCKOp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 705.7037133110892, + "y": 358.277838812934, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 13.33331298828125, + "height": 11.666671752929688, + "seed": 638517691, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 133, + "versionNonce": 419414555, + "isDeleted": false, + "id": "dTPwE9k9B6K3ksCQBb5OL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 535.0371958414713, + "y": 506.51877678765186, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 20, + "seed": 121765525, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::functions", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::functions" + }, + { + "type": "rectangle", + "version": 456, + "versionNonce": 1301726907, + "isDeleted": false, + "id": "2J2sR-bPrGrF9OxiUqIF0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 868.2591145833333, + "y": 158.1759851243761, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 1128772501, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "hFcIrFVFFLvSiAWOYE-xZ", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 284, + "versionNonce": 154931515, + "isDeleted": false, + "id": "ZZLxhvBfAsVbp2PY6VOfR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 851.2592061360676, + "y": 129.7593591478136, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 153.00003051757812, + "seed": 1437253909, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "hFcIrFVFFLvSiAWOYE-xZ", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 356, + "versionNonce": 1161098229, + "isDeleted": false, + "id": "FdRdG15cLuW_kygoS9kFB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 862.5927598741318, + "y": 332.1297662523058, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 159, + "height": 20, + "seed": 1076709051, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::globals", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::globals" + }, + { + "type": "rectangle", + "version": 458, + "versionNonce": 196882907, + "isDeleted": false, + "id": "ev_sIxuEomRo18wQcVOAi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 863.5926411946614, + "y": 195.926084306505, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 1597294293, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "hFcIrFVFFLvSiAWOYE-xZ", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 469, + "versionNonce": 1119661397, + "isDeleted": false, + "id": "2AbM31Y4eYzNJdn6ccO7X", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 870.2593892415364, + "y": 231.25939729478625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 1887403259, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "6EZFlJGbMoYN4RDYkSEYP", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 193, + "versionNonce": 499530421, + "isDeleted": false, + "id": "2jRIgxhs5VlnfeDeDJXss", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.7037285698783, + "y": 359.277838812934, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 16, + "height": 19.000091552734375, + "seed": 1552112411, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "C_HvFqwDiW4wGe01QNKFg", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 445, + "versionNonce": 1861688341, + "isDeleted": false, + "id": "hFcIrFVFFLvSiAWOYE-xZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 797.8519422743055, + "y": 365.2778930664062, + "strokeColor": "#000000", + "backgroundColor": "#7950f2", + "width": 60.463513970850386, + "height": 155.0348750393585, + "seed": 1134283957, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "ev_sIxuEomRo18wQcVOAi", + "focus": 0.8021785743382612, + "gap": 5.277184949505454 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 26.18546210394959, + -121.94443088107656 + ], + [ + 60.463513970850386, + -155.0348750393585 + ] + ] + }, + { + "type": "rectangle", + "version": 485, + "versionNonce": 1957174203, + "isDeleted": false, + "id": "qGE55eCk3NIsUQpSP-g6Y", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 888.4441562228732, + "y": 390.84254328409827, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 983648475, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 316, + "versionNonce": 507826549, + "isDeleted": false, + "id": "c3AswZE8rY-ZsD8gGAzgg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.4442477756076, + "y": 362.42591730753577, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 153.00003051757812, + "seed": 832079445, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 343, + "versionNonce": 1916988507, + "isDeleted": false, + "id": "6YL2S8HdLuD8PaiWGA-vU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 829.5925801595051, + "y": 102.42615678575305, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 156, + "height": 20, + "seed": 920221051, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::tables", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::tables" + }, + { + "type": "rectangle", + "version": 542, + "versionNonce": 2029312725, + "isDeleted": false, + "id": "50mTV_vbZA4UToji5TAhb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 882.0739339192708, + "y": 431.5186000400119, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 1438577589, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "OjoiFuv7ZOtNkq4SpgOL-", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 553, + "versionNonce": 1635428603, + "isDeleted": false, + "id": "Aa3QPT7Ps924J0klBEGMw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 888.7406819661458, + "y": 466.85191302829315, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 547556891, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 424, + "versionNonce": 1473368475, + "isDeleted": false, + "id": "WMOLdrP9c0TFV1JLcmfzK", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 233.66656494140625, + "y": 236.33336639404277, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 17.3333740234375, + "height": 13.000015258789062, + "seed": 1272865237, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 442, + "versionNonce": 606202261, + "isDeleted": false, + "id": "XVo2iBeciuaFiIrP3DUw5", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 235.66656494140625, + "y": 252.66669464111305, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 17.3333740234375, + "height": 13.000015258789062, + "seed": 823202299, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "6EZFlJGbMoYN4RDYkSEYP", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 427, + "versionNonce": 377153083, + "isDeleted": false, + "id": "InatpictpQQa0Op1qByFJ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 235.66656494140625, + "y": 268.33336639404274, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 17.3333740234375, + "height": 13.000015258789062, + "seed": 682826293, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "6EZFlJGbMoYN4RDYkSEYP", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 609, + "versionNonce": 1207751547, + "isDeleted": false, + "id": "6EZFlJGbMoYN4RDYkSEYP", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 257.1755919962603, + "y": 432.4837343704476, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 350.4818452518821, + "height": 113.7944575139108, + "seed": 1046151995, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "bCPa9rRVzubIpa31jwl73", + "focus": 0.6467798007705062, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -33.87912050646, + 40.47938005012526 + ], + [ + 316.60272474542205, + 113.7944575139108 + ] + ] + }, + { + "type": "text", + "version": 380, + "versionNonce": 1636332981, + "isDeleted": false, + "id": "qUXi_zQbiA8lmV5c_r8ta", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 875.0742831759982, + "y": 534.6482721964519, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 172, + "height": 20, + "seed": 2104268027, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::memories", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::memories" + }, + { + "type": "rectangle", + "version": 511, + "versionNonce": 833268763, + "isDeleted": false, + "id": "rfTE9QxqtEjzOzoCNZT01", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 900.9256795247394, + "y": 593.3610492282444, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 1292864565, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "4NjPz6Olqru3m83OxGXGX", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 342, + "versionNonce": 347731733, + "isDeleted": false, + "id": "RzznX4k1tfSt8AyuJuF2D", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 883.9257710774738, + "y": 564.9444232516819, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 153.00003051757812, + "seed": 1990606235, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "4NjPz6Olqru3m83OxGXGX", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 566, + "versionNonce": 626290875, + "isDeleted": false, + "id": "GEQFwoUvMYOSit62JGwLM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 894.5554572211371, + "y": 634.037105984158, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 1545223573, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 578, + "versionNonce": 1559514229, + "isDeleted": false, + "id": "IvFYTMyYUBhJe6Ob5YAGg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 901.2222052680121, + "y": 669.3704189724392, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70.00006103515625, + "height": 25.66668701171874, + "seed": 937546299, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 43, + "versionNonce": 1419551067, + "isDeleted": false, + "id": "ZMwf8VnKqa7gp45PnuQWi", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 780.3335876464843, + "y": 45.92599826388881, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 286.66666666666686, + "height": 707.0370313856337, + "seed": 876902971, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "YidAloK-3ikBBzvuu-S22", + "type": "arrow" + } + ], + "updated": 1679555887723, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 138, + "versionNonce": 1241314773, + "isDeleted": false, + "id": "2NQmRBF_NE2Myp3-jqLfT", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 418.1839375016565, + "y": 222.9630296495223, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 351.7376685654755, + "height": 202.59258694118918, + "seed": 1011740277, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": { + "elementId": "TOmX9MwwNOgbfjNJ8V8j7", + "focus": 0.48971428325717553, + "gap": 11.018498738606851 + }, + "endBinding": { + "elementId": "7MNv-pmgV4-8yJ5lIFY1U", + "focus": 1.3268339026620566, + "gap": 13.16658528645857 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 82.8903795827618, + -202.59258694118918 + ], + [ + 351.7376685654755, + -175.99734962527606 + ] + ] + }, + { + "type": "arrow", + "version": 58, + "versionNonce": 416660987, + "isDeleted": false, + "id": "OjoiFuv7ZOtNkq4SpgOL-", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 796.6298048231338, + "y": 366.6667785644529, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 82.96305338541652, + "height": 65.92593722873261, + "seed": 275987509, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "50mTV_vbZA4UToji5TAhb", + "focus": -0.4434608130384178, + "gap": 2.481075710720461 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 82.96305338541652, + 65.92593722873261 + ] + ] + }, + { + "type": "arrow", + "version": 131, + "versionNonce": 1458139957, + "isDeleted": false, + "id": "4NjPz6Olqru3m83OxGXGX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 796.6298726399739, + "y": 372.96306355794246, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.51854112413207, + "height": 227.77777777777777, + "seed": 1888354747, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "rfTE9QxqtEjzOzoCNZT01", + "focus": -0.6184957089704255, + "gap": 5.7772657606334406 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 46.66673448350696, + 191.48159450954864 + ], + [ + 98.51854112413207, + 227.77777777777777 + ] + ] + }, + { + "type": "arrow", + "version": 105, + "versionNonce": 2097301147, + "isDeleted": false, + "id": "euj5CRuv6EoS2abVhp14P", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 792.9261237250435, + "y": 375.55566745334175, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 140, + "height": 233.33346896701386, + "seed": 856817851, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887723, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "qpSAJ6K0S6TvU6i2Uflep", + "focus": 1.8857122566967692, + "gap": 14.01957438916057 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -30.37034776475707, + 147.77781168619794 + ], + [ + -140, + 233.33346896701386 + ] + ] + }, + { + "type": "text", + "version": 404, + "versionNonce": 2049173, + "isDeleted": false, + "id": "0836mka_gP8EntAw7Pvzh", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 285.4076097276477, + "y": 424.0742221408418, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 198, + "height": 19, + "seed": 1302236763, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887724, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMFunction **functions;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunction **functions;" + }, + { + "type": "rectangle", + "version": 385, + "versionNonce": 131382075, + "isDeleted": false, + "id": "Fv3xwjCOvQ9-pJ5ui2sV2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 239.7409837510852, + "y": 409.4075961642793, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 216, + "height": 42.6666259765625, + "seed": 1276926165, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887724, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 350, + "versionNonce": 424929781, + "isDeleted": false, + "id": "IibWA_jM89ndxmrceQLmE", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 254.4077317979602, + "y": 420.07428317599806, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 15.33331298828125, + "height": 22.66668701171875, + "seed": 1427245819, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887724, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 133, + "versionNonce": 1169136603, + "isDeleted": false, + "id": "OdDm5a5O_NIoZbF3u0c0H", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 442.5558098687066, + "y": 370.37052747938344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 138.34606629993374, + "height": 88.15863628949046, + "seed": 967352763, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679555887724, + "link": null, + "locked": false, + "startBinding": { + "elementId": "FPUGB-iP7ep91gfoTuV2d", + "focus": 0.6557614629211577, + "gap": 3.7962053087022696 + }, + "endBinding": { + "elementId": "blxQLl_yf7DMD76qHC5rc", + "focus": -0.24942508137542688, + "gap": 9.870619032117872 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 61.48149278428815, + -72.22222222222217 + ], + [ + 138.34606629993374, + -88.15863628949046 + ] + ] + }, + { + "type": "text", + "version": 35, + "versionNonce": 1670177621, + "isDeleted": false, + "id": "armvxVMuT0bX77T30wPV4", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -39.66648017035561, + "y": 230.37051052517361, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 165, + "height": 20, + "seed": 587722005, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887724, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "export_global_count", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "export_global_count" + }, + { + "type": "rectangle", + "version": 502, + "versionNonce": 312830075, + "isDeleted": false, + "id": "nUlAmf5jmsJoZ2lStTbBw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": -52.18503146701369, + "y": 226.01867336697043, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174.66668701171875, + "height": 25.37032402886292, + "seed": 1476753525, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679555887724, + "link": null, + "locked": false + }, + { + "id": "Mznw_08SMS746JVePQe3h", + "type": "text", + "x": -55.747947692871094, + "y": 396.450114440918, + "width": 174, + "height": 20, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 1392475036, + "version": 62, + "versionNonce": 452856732, + "isDeleted": false, + "boundElements": null, + "updated": 1679558398355, + "link": null, + "locked": false, + "text": "export_memory_count", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 14, + "containerId": null, + "originalText": "export_memory_count" + }, + { + "id": "wyGG1OIhy3X499IvtJM3Z", + "type": "rectangle", + "x": -58.081260681152344, + "y": 394.9499618530274, + "width": 182.33331298828125, + "height": 25, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 611257380, + "version": 32, + "versionNonce": 1973769116, + "isDeleted": false, + "boundElements": null, + "updated": 1679558406221, + "link": null, + "locked": false + }, + { + "id": "Gk4JYjHayAFkRDbQWId6b", + "type": "text", + "x": -42.081260681152344, + "y": 463.28327484130864, + "width": 162, + "height": 20, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 804003748, + "version": 47, + "versionNonce": 591391772, + "isDeleted": false, + "boundElements": null, + "updated": 1679558418472, + "link": null, + "locked": false, + "text": "export_table_count", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 14, + "containerId": null, + "originalText": "export_table_count" + }, + { + "id": "Xb-aggFXlG4Dh_LtxGJ7l", + "type": "rectangle", + "x": -49.414573669433594, + "y": 462.28330535888676, + "width": 174.33331298828125, + "height": 24.33343505859375, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 572093348, + "version": 30, + "versionNonce": 1840351132, + "isDeleted": false, + "boundElements": null, + "updated": 1679558423043, + "link": null, + "locked": false + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/core/iwasm/doc/images/stack_format_ci.excalidraw b/core/iwasm/doc/images/stack_format_ci.excalidraw new file mode 100644 index 0000000000..f2ade3b205 --- /dev/null +++ b/core/iwasm/doc/images/stack_format_ci.excalidraw @@ -0,0 +1,2643 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "type": "rectangle", + "version": 155, + "versionNonce": 1248561528, + "isDeleted": false, + "id": "fxSjNN3geJtdm-2MR7INN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 583.9999694824219, + "y": 276.33331298828114, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 427.66665649414057, + "height": 468.6667785644533, + "seed": 1226571556, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "zFlCnXyCuuvj85rH8yMcR", + "type": "arrow" + }, + { + "id": "KiC12zdfqG7zXRbctXGmT", + "type": "arrow" + } + ], + "updated": 1679706874848, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 31, + "versionNonce": 726044059, + "isDeleted": false, + "id": "kKMhPpSUI7zZU0hhfGfSr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652, + "y": 310.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 91, + "height": 20, + "seed": 1323166748, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "v8qOyuaPyJmHPHCk-V90A", + "type": "arrow" + } + ], + "updated": 1679639568750, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "prev_frame", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "prev_frame" + }, + { + "type": "text", + "version": 31, + "versionNonce": 213374372, + "isDeleted": false, + "id": "97vkuGwuf9dOgnbXL4nZW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652, + "y": 345.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 84, + "height": 20, + "seed": 123433892, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679617642914, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_inst ", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func_inst " + }, + { + "type": "text", + "version": 31, + "versionNonce": 34601372, + "isDeleted": false, + "id": "IaZULBLAtP-VbIpB210WT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652, + "y": 380.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 13, + "height": 20, + "seed": 1157761180, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679617674900, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "ip", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "ip" + }, + { + "type": "text", + "version": 37, + "versionNonce": 556583925, + "isDeleted": false, + "id": "H6AElOIjCOfKVDEzsEb8_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 648.3333435058594, + "y": 415.16668701171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 20, + "seed": 175464228, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bzZuzwhAslOM0VOMpIFi4", + "type": "arrow" + } + ], + "updated": 1679638914768, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "opnd_stack_bottom", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "opnd_stack_bottom" + }, + { + "type": "text", + "version": 31, + "versionNonce": 2114117339, + "isDeleted": false, + "id": "InrDJwTwyP1E7lpeGu0Tb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652, + "y": 450.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 275, + "height": 20, + "seed": 158242076, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bzZuzwhAslOM0VOMpIFi4", + "type": "arrow" + } + ], + "updated": 1679638694120, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "opnd_sp (point to opnd stack top)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "opnd_sp (point to opnd stack top)" + }, + { + "type": "text", + "version": 31, + "versionNonce": 1005029461, + "isDeleted": false, + "id": "04QcPfm0Sc1zQpYDxmPVl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652, + "y": 485.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 163, + "height": 20, + "seed": 2051398308, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679638792159, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "label_stack_bottom", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "label_stack_bottom" + }, + { + "type": "text", + "version": 31, + "versionNonce": 1565468827, + "isDeleted": false, + "id": "GD4urgWTuYKcn7FcwA5uj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 652, + "y": 520.5, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 280, + "height": 20, + "seed": 429232540, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "AhNgHym2Ox-2Je47FlwkG", + "type": "arrow" + } + ], + "updated": 1679638982669, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "label_sp (point to label stack top)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "label_sp (point to label stack top)" + }, + { + "type": "text", + "version": 123, + "versionNonce": 458495496, + "isDeleted": false, + "id": "9TW8AADnHJOkmP9yPw86T", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 626, + "y": 586.1666564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 125, + "height": 20, + "seed": 561702436, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706667761, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func arguments:", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func arguments:" + }, + { + "type": "text", + "version": 373, + "versionNonce": 180653176, + "isDeleted": false, + "id": "ZDnffzv8Hf65ecpMemz82", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 700.6666870117188, + "y": 669.1667785644531, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 131, + "height": 20, + "seed": 1157792164, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543900, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "rectangle", + "version": 108, + "versionNonce": 628186232, + "isDeleted": false, + "id": "10UtZNhGhQIza4PqPLKs5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 622.6665954589844, + "y": 299.9999694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 378.3334655761719, + "height": 33.66670227050781, + "seed": 1281579428, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706899765, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 108, + "versionNonce": 1807107317, + "isDeleted": false, + "id": "lxiJENi5HY8EPwIeH7LA0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 621.9999389648438, + "y": 343.3332977294922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 377.6668090820312, + "height": 28.33332824707031, + "seed": 523845788, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679639010923, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 104, + "versionNonce": 114288661, + "isDeleted": false, + "id": "iBGNXVvcsdhIuK4rSyr24", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 621.333251953125, + "y": 377.9999694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 380.3333129882813, + "height": 24.666656494140625, + "seed": 1117299228, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679639014926, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 141, + "versionNonce": 1756531061, + "isDeleted": false, + "id": "hwgrctAuxGaA-SGuwFgEd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 623.6665649414062, + "y": 413.66664123535156, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 190.33331298828125, + "height": 25.333358764648438, + "seed": 1885325596, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "2yVfQUtaAnW5BuEtoXRcA", + "type": "arrow" + } + ], + "updated": 1679638734395, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 135, + "versionNonce": 138906299, + "isDeleted": false, + "id": "V6DAmN9L5JPz2B1MGfa9C", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 624.6665954589844, + "y": 445.9999694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 377.6666564941406, + "height": 28.00003051757812, + "seed": 1058988452, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bzZuzwhAslOM0VOMpIFi4", + "type": "arrow" + } + ], + "updated": 1679638756269, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 171, + "versionNonce": 755362101, + "isDeleted": false, + "id": "0m59R0bqJv_x7GbZ6hfyg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 624.6665954589844, + "y": 485.33331298828125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 197.33340454101562, + "height": 23.33328247070314, + "seed": 1040920092, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bzZuzwhAslOM0VOMpIFi4", + "type": "arrow" + } + ], + "updated": 1679638908118, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 191, + "versionNonce": 944635675, + "isDeleted": false, + "id": "Itqs7rL1-S3eIrmvfrr6i", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 625.333251953125, + "y": 516.3333129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 349.66674804687506, + "height": 34.66665649414062, + "seed": 498379804, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679639029205, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 169, + "versionNonce": 1525847816, + "isDeleted": false, + "id": "Yx4QpHhmHHvf267ot4QcW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 623.333251953125, + "y": 582.6666259765625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 352.3333740234375, + "height": 31.666748046875, + "seed": 612410396, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543900, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 195, + "versionNonce": 335028795, + "isDeleted": false, + "id": "sWiDv3IP9Y4zn-pQPKT59", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 585.6668090820312, + "y": 203.99998474121094, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 424.3334045410156, + "height": 70.66668701171874, + "seed": 813710748, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "v8qOyuaPyJmHPHCk-V90A", + "type": "arrow" + } + ], + "updated": 1679639568750, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 255, + "versionNonce": 1953250680, + "isDeleted": false, + "id": "gS_VbgMS5NUoHmfxH0eEO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 752.0001525878906, + "y": 624.3333129882812, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 30.6666259765625, + "height": 19.000091552734375, + "seed": 2044515484, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543900, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 171, + "versionNonce": 227339384, + "isDeleted": false, + "id": "m89OexiP1cw5cN304gmQ-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 771.3336334228516, + "y": 587.8333282470703, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 37.999969482421875, + "height": 19.666748046875, + "seed": 2074557348, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706664521, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 174, + "versionNonce": 1205391112, + "isDeleted": false, + "id": "aAWksorEQDseQSDntTUKe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824.6669464111328, + "y": 589.1665802001953, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 35.999969482421875, + "height": 15.666778564453123, + "seed": 1851711260, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706659367, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 213, + "versionNonce": 1798283016, + "isDeleted": false, + "id": "IH4D4tHy91CnkLlP3kKJT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 878.6669464111328, + "y": 588.8333892822266, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 35.999969482421875, + "height": 16.333435058593754, + "seed": 1835107492, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706677882, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 28, + "versionNonce": 1189712156, + "isDeleted": false, + "id": "OGXe0pARYj6FFaTw_LGLs", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 916.6667785644531, + "y": 348.3333435058594, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 13.33331298828125, + "height": 15.66668701171875, + "seed": 703955236, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "zFlCnXyCuuvj85rH8yMcR", + "type": "arrow" + } + ], + "updated": 1679617654572, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 90, + "versionNonce": 1752012152, + "isDeleted": false, + "id": "zFlCnXyCuuvj85rH8yMcR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 930.9664201728779, + "y": 355.9093759200581, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 97.70041942673151, + "height": 3.156306335738577, + "seed": 1652552228, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706533823, + "link": null, + "locked": false, + "startBinding": { + "elementId": "OGXe0pARYj6FFaTw_LGLs", + "gap": 1, + "focus": -0.05520408889747474 + }, + "endBinding": { + "elementId": "fxSjNN3geJtdm-2MR7INN", + "gap": 17.000213623046875, + "focus": 0.6588609578759657 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 97.70041942673151, + 3.156306335738577 + ] + ] + }, + { + "type": "rectangle", + "version": 39, + "versionNonce": 2081828900, + "isDeleted": false, + "id": "cN9omEMJtZq2pKK3nPqeK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1033.3334655761719, + "y": 339.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 218, + "height": 37, + "seed": 1716587932, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "w-Fx4nTROv9qvnPqLigxc" + } + ], + "updated": 1679617661526, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 4, + "versionNonce": 727129976, + "isDeleted": false, + "id": "w-Fx4nTROv9qvnPqLigxc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1051.3334655761719, + "y": 347.5000305175781, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 182, + "height": 20, + "seed": 1245620124, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706461629, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(current func instance)", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "cN9omEMJtZq2pKK3nPqeK", + "originalText": "(current func instance)" + }, + { + "type": "diamond", + "version": 19, + "versionNonce": 1517445412, + "isDeleted": false, + "id": "YAWb1D32tq9r2NA9BjPC0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 917.3334655761719, + "y": 381.3333740234375, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 12.6666259765625, + "height": 17.666656494140625, + "seed": 831940124, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679617681098, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 103, + "versionNonce": 437077525, + "isDeleted": false, + "id": "EUw5VCdUqXWdPh2cmFgTu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1048.0000915527344, + "y": 390.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 224, + "height": 52, + "seed": 122854564, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "sQUYAdB_aJemE8MbtfUXU" + }, + { + "id": "7cT6qctQMJuzVHVpt9gAe", + "type": "arrow" + } + ], + "updated": 1679643416466, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 89, + "versionNonce": 818328584, + "isDeleted": false, + "id": "sQUYAdB_aJemE8MbtfUXU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1066.0000915527344, + "y": 395.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 188, + "height": 40, + "seed": 1881253020, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706461630, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": " (next bytecode in code\nfor return)", + "baseline": 34, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "EUw5VCdUqXWdPh2cmFgTu", + "originalText": " (next bytecode in code\nfor return)" + }, + { + "type": "arrow", + "version": 86, + "versionNonce": 44769845, + "isDeleted": false, + "id": "7cT6qctQMJuzVHVpt9gAe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 928.0000915527344, + "y": 389.66668701171875, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 118.6890752940651, + "height": 18.605575795885613, + "seed": 2073584156, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643418552, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "EUw5VCdUqXWdPh2cmFgTu", + "gap": 1.3109247059348494, + "focus": -0.23038166167021662 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 118.6890752940651, + 18.605575795885613 + ] + ] + }, + { + "type": "rectangle", + "version": 2, + "versionNonce": 504035291, + "isDeleted": false, + "id": "6fkbi-hv-WF274Ggup6O2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1200.0001525878906, + "y": 619.8332824707031, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 0.333251953125, + "height": 0.333343505859375, + "seed": 1881722357, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1679638527376, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 237, + "versionNonce": 103104376, + "isDeleted": false, + "id": "TBY_IOcQ1nL2SbnoH6sFO", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 627.0001373291016, + "y": 660.5000610351562, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 357.33331298828125, + "height": 30.33352661132812, + "seed": 1619080725, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "bzZuzwhAslOM0VOMpIFi4", + "type": "arrow" + }, + { + "id": "2yVfQUtaAnW5BuEtoXRcA", + "type": "arrow" + } + ], + "updated": 1679706543900, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 304, + "versionNonce": 1231721480, + "isDeleted": false, + "id": "2yVfQUtaAnW5BuEtoXRcA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1001.4951265607561, + "y": 435.4648501924554, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 69.86595546390652, + "height": 230.92727975758817, + "seed": 554350997, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Q10gr2u0Ozc3e0iafNjrU", + "focus": -0.9749635321679403, + "gap": 1 + }, + "endBinding": { + "elementId": "TBY_IOcQ1nL2SbnoH6sFO", + "gap": 11.467449077109102, + "focus": 0.9687666106437038 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 64.17172829764229, + 132.70180630168522 + ], + [ + -5.694227166264227, + 230.92727975758817 + ] + ] + }, + { + "type": "arrow", + "version": 579, + "versionNonce": 1040896776, + "isDeleted": false, + "id": "bzZuzwhAslOM0VOMpIFi4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 637.088614263612, + "y": 428.66374830753944, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 87.42185095794798, + "height": 243.67105886528202, + "seed": 1778263445, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": { + "elementId": "H6AElOIjCOfKVDEzsEb8_", + "focus": 1.022917371543073, + "gap": 11.244729242247331 + }, + "endBinding": { + "elementId": "zmGNXD3FjbK3cLzJQcTS7", + "focus": -0.9191849396644245, + "gap": 1.3880431063754486 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -87.42185095794798, + 127.50287766902306 + ], + [ + -10.80983302916718, + 243.67105886528202 + ] + ] + }, + { + "type": "rectangle", + "version": 146, + "versionNonce": 1195282552, + "isDeleted": false, + "id": "zmGNXD3FjbK3cLzJQcTS7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 627.6668243408203, + "y": 662.8332824707031, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 207.333251953125, + "height": 25.333343505859375, + "seed": 2075064181, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "bzZuzwhAslOM0VOMpIFi4", + "type": "arrow" + }, + { + "id": "PtPViQVeIOLGj_fGdcXae", + "type": "arrow" + } + ], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 252, + "versionNonce": 1380659720, + "isDeleted": false, + "id": "fdKDasbQmnSQhVWnuc6o0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 892.2284992953181, + "y": 676.9139215503409, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 46.77163803378346, + "height": 0.7472955737783877, + "seed": 2138378229, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 46.77163803378346, + -0.7472955737783877 + ] + ] + }, + { + "type": "text", + "version": 63, + "versionNonce": 2032367925, + "isDeleted": false, + "id": "ZwrESUGUwekFgJ8cot-D1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 821.6667327880859, + "y": 414.6666259765625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174, + "height": 20, + "seed": 795089589, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "2yVfQUtaAnW5BuEtoXRcA", + "type": "arrow" + } + ], + "updated": 1679638757542, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "opnd_stack_boundary", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "opnd_stack_boundary" + }, + { + "type": "rectangle", + "version": 32, + "versionNonce": 132303029, + "isDeleted": false, + "id": "VOinNwnsGZVJt9RUbM5gI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 821.3334197998047, + "y": 409.8332977294922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 184.3333740234375, + "height": 30.333328247070312, + "seed": 1069875285, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679638823023, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 255, + "versionNonce": 453362040, + "isDeleted": false, + "id": "PtPViQVeIOLGj_fGdcXae", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 979.6667327880859, + "y": 460.4999694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 140.89037965104183, + "height": 211.49970410546382, + "seed": 472939093, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "zmGNXD3FjbK3cLzJQcTS7", + "focus": 0.7843520090394333, + "gap": 6.109589831379935 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 2.33331298828125, + 145 + ], + [ + -138.55706666276058, + 211.49970410546382 + ] + ] + }, + { + "type": "diamond", + "version": 19, + "versionNonce": 987485307, + "isDeleted": false, + "id": "u7bglMSX07f5CN_SyFAmd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 967.0000457763672, + "y": 452.1666259765625, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 15, + "height": 15, + "seed": 764957403, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679638777911, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 43, + "versionNonce": 115610933, + "isDeleted": false, + "id": "shh1k5OswcF57jhAKAI6G", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 830.0000457763672, + "y": 486.8332824707031, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 20, + "seed": 1994374395, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679638809874, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "label_stack_boundary", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "label_stack_boundary" + }, + { + "type": "rectangle", + "version": 33, + "versionNonce": 1511042715, + "isDeleted": false, + "id": "H4lcjS3aIarViIGAHsLBU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 827.6667327880859, + "y": 480.83331298828125, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 181, + "height": 29.666656494140625, + "seed": 1492829339, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679638816247, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 372, + "versionNonce": 1764776968, + "isDeleted": false, + "id": "B1eH_aNspf4OoiXnvWu1V", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 630.0000915527344, + "y": 705.1667175292969, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 356.66662597656256, + "height": 31.333404541015625, + "seed": 710054491, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "X76to9wjER3VzqXb7tCwb", + "type": "arrow" + }, + { + "id": "1Urs2i0MGQSQ9XbOVdbCH", + "type": "arrow" + }, + { + "id": "KiC12zdfqG7zXRbctXGmT", + "type": "arrow" + } + ], + "updated": 1679706706153, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 332, + "versionNonce": 1981566984, + "isDeleted": false, + "id": "Tu7mkiWTjNwFrOsUa35N1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 633.3333740234375, + "y": 704.8334045410156, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 250.33328247070318, + "height": 26.66656494140625, + "seed": 1201312981, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "AhNgHym2Ox-2Je47FlwkG", + "type": "arrow" + } + ], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 250, + "versionNonce": 1696927496, + "isDeleted": false, + "id": "xQf_fS4j5XD1gPYVmnyQi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 687, + "y": 709.8335876464844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 108, + "height": 20, + "seed": 1671092892, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "" + }, + { + "type": "diamond", + "version": 19, + "versionNonce": 167262267, + "isDeleted": false, + "id": "T43dNIeAePPWy65vsVjRw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 629.0000457763672, + "y": 420.8332977294922, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 13.66668701171875, + "height": 9, + "seed": 1938681499, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679638923749, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 26, + "versionNonce": 1796989173, + "isDeleted": false, + "id": "Q10gr2u0Ozc3e0iafNjrU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 988.5000762939453, + "y": 431.9999542236328, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 13.66668701171875, + "height": 9, + "seed": 1339643477, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "2yVfQUtaAnW5BuEtoXRcA", + "type": "arrow" + } + ], + "updated": 1679639079224, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 160, + "versionNonce": 1562882312, + "isDeleted": false, + "id": "X76to9wjER3VzqXb7tCwb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 633.6667327880859, + "y": 498.1665954589844, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 95, + "height": 226.4773119076067, + "seed": 332801877, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "B1eH_aNspf4OoiXnvWu1V", + "gap": 3.7751266782821933, + "focus": -0.9692106196282059 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -95, + 119 + ], + [ + -7.441767913633839, + 226.4773119076067 + ] + ] + }, + { + "type": "arrow", + "version": 202, + "versionNonce": 791528312, + "isDeleted": false, + "id": "1Urs2i0MGQSQ9XbOVdbCH", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 993.6667327880859, + "y": 500.833251953125, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 55.58884147480535, + "height": 215.3311147859538, + "seed": 385950459, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_1a_M2hRRjsccFJTYD-i0", + "focus": -0.27052637703325505, + "gap": 2.7105616084241397 + }, + "endBinding": { + "elementId": "B1eH_aNspf4OoiXnvWu1V", + "focus": 0.9207393546998478, + "gap": 3.0779218308585996 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 51.666748046875, + 153.66668701171875 + ], + [ + -3.922093427930349, + 215.3311147859538 + ] + ] + }, + { + "type": "arrow", + "version": 252, + "versionNonce": 1150719096, + "isDeleted": false, + "id": "AhNgHym2Ox-2Je47FlwkG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 941.0000457763672, + "y": 533.833251953125, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 73.73891815660659, + "height": 177.63880524698254, + "seed": 17032283, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": { + "elementId": "GD4urgWTuYKcn7FcwA5uj", + "focus": -1.0502037458670375, + "gap": 9.000045776367188 + }, + "endBinding": { + "elementId": "Tu7mkiWTjNwFrOsUa35N1", + "focus": 0.7914007600477917, + "gap": 2.5944100904637253 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 18.99993896484375, + 133.33340454101562 + ], + [ + -54.73897919176284, + 177.63880524698254 + ] + ] + }, + { + "type": "diamond", + "version": 19, + "versionNonce": 987485307, + "isDeleted": false, + "id": "C2JNNEQlhQ4cFy2h16E4l", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 630.5000457763672, + "y": 491.333251953125, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 15, + "height": 15, + "seed": 2047712475, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679638991963, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 19, + "versionNonce": 987485307, + "isDeleted": false, + "id": "xsWhegnNTDUvjVk1Hl20d", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 931.5000457763672, + "y": 523.333251953125, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 15, + "height": 15, + "seed": 751530581, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679638994246, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 20, + "versionNonce": 2123101557, + "isDeleted": false, + "id": "_1a_M2hRRjsccFJTYD-i0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 987.5000457763672, + "y": 503.333251953125, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 15, + "height": 15, + "seed": 1838893435, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "1Urs2i0MGQSQ9XbOVdbCH", + "type": "arrow" + } + ], + "updated": 1679639046142, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 122, + "versionNonce": 918373752, + "isDeleted": false, + "id": "_FzrYgawOp32aihqUkx8N", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 937.3333587646484, + "y": 722.4999084472656, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 31, + "height": 0.333343505859375, + "seed": 125027067, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 31, + 0.333343505859375 + ] + ] + }, + { + "type": "arrow", + "version": 58, + "versionNonce": 1791510299, + "isDeleted": false, + "id": "v8qOyuaPyJmHPHCk-V90A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 640.33349609375, + "y": 318.1665954589844, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 112.66668701171875, + "height": 114.41643468378521, + "seed": 1521072891, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679639586455, + "link": null, + "locked": false, + "startBinding": { + "elementId": "kKMhPpSUI7zZU0hhfGfSr", + "focus": -0.8312315870337287, + "gap": 11.66650390625 + }, + "endBinding": { + "elementId": "sWiDv3IP9Y4zn-pQPKT59", + "focus": 1.0049483520293248, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -112.66668701171875, + -61.99995422363281 + ], + [ + -55.63497828298284, + -114.41643468378521 + ] + ] + }, + { + "type": "diamond", + "version": 23, + "versionNonce": 1760592469, + "isDeleted": false, + "id": "rizowRaNCY3sscSKdUoZ_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 637.6668090820312, + "y": 307.49993896484375, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 11.66668701171875, + "height": 17, + "seed": 599455541, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679639578366, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 241, + "versionNonce": 1517501429, + "isDeleted": false, + "id": "Mv8lernCpH2jNrVrNfqm5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 585.8334503173828, + "y": 130.833251953125, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 424.3334045410156, + "height": 70.66668701171874, + "seed": 1040624571, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "cl6Duxg7hC4_1chb5YNau", + "type": "arrow" + } + ], + "updated": 1679639602774, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 41, + "versionNonce": 926996117, + "isDeleted": false, + "id": "eQrRfUHvRypTcFfQ80nK5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 647.0000610351562, + "y": 219.33328247070312, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 91, + "height": 20, + "seed": 1884598901, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "cl6Duxg7hC4_1chb5YNau", + "type": "arrow" + } + ], + "updated": 1679639602774, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "prev_frame", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "prev_frame" + }, + { + "type": "diamond", + "version": 32, + "versionNonce": 784602165, + "isDeleted": false, + "id": "w_RQs6dyVbTAM3iaX46G2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 632.6668701171875, + "y": 216.33322143554688, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 11.66668701171875, + "height": 17, + "seed": 1918737243, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679639598842, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 135, + "versionNonce": 1953671957, + "isDeleted": false, + "id": "cl6Duxg7hC4_1chb5YNau", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 639.0001525878906, + "y": 224.16659545898438, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 80.33334350585938, + "height": 95, + "seed": 509490587, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679639608498, + "link": null, + "locked": false, + "startBinding": { + "elementId": "eQrRfUHvRypTcFfQ80nK5", + "focus": -0.7568501325114791, + "gap": 7.999908447265625 + }, + "endBinding": { + "elementId": "Mv8lernCpH2jNrVrNfqm5", + "focus": 1.0067042515978792, + "gap": 1.666656494140625 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -80.33334350585938, + -53.66668701171875 + ], + [ + -53.66668701171875, + -95 + ] + ] + }, + { + "type": "text", + "version": 196, + "versionNonce": 1763609493, + "isDeleted": false, + "id": "7pum3cjupgQ0Fz7eY_3xD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1026.33349609375, + "y": 123.83326721191406, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 109, + "height": 20, + "seed": 1300737371, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679640276534, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Stack bottom", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Stack bottom" + }, + { + "type": "text", + "version": 310, + "versionNonce": 1972102043, + "isDeleted": false, + "id": "Su_2hWwLvhW5W8cNMx6hT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1030.666748046875, + "y": 266.8333206176758, + "strokeColor": "#000000", + "backgroundColor": "#12b886", + "width": 110, + "height": 20, + "seed": 2131950293, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679640279741, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "current frame", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "current frame" + }, + { + "type": "rectangle", + "version": 181, + "versionNonce": 1099516792, + "isDeleted": false, + "id": "jCBotdnPaPF-NkuwgrLyd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 582.3333129882812, + "y": 744.8332977294922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 429.666748046875, + "height": 48.333343505859396, + "seed": 1136750491, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706699607, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 167, + "versionNonce": 647398264, + "isDeleted": false, + "id": "BRMXHqjjvaWADtilYg0NV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 784.3333740234375, + "y": 775.8333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 57, + "height": 20, + "seed": 415943189, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "KiC12zdfqG7zXRbctXGmT", + "type": "arrow" + } + ], + "updated": 1679706708714, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Unused", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Unused" + }, + { + "type": "text", + "version": 179, + "versionNonce": 1210985736, + "isDeleted": false, + "id": "6YoFb1tMeQkuEDMy5H4FV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1018.6668090820312, + "y": 778.8333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 122, + "height": 20, + "seed": 1257872437, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706711595, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "stack boundary", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "stack boundary" + }, + { + "type": "text", + "version": 174, + "versionNonce": 314437128, + "isDeleted": false, + "id": "_e0Yy4p4UD9MNJUym2U_N", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1025.6666259765625, + "y": 732.8335113525391, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 80, + "height": 20, + "seed": 521361589, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "stack top", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "stack top" + }, + { + "type": "rectangle", + "version": 126, + "versionNonce": 1927665272, + "isDeleted": false, + "id": "ATaMGJS9emfaD_vgxw4Wi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 625.5619915078412, + "y": 621.4944998254441, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 358.66670227050787, + "height": 25.999969482421875, + "seed": 1247140509, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 268, + "versionNonce": 1190911752, + "isDeleted": false, + "id": "qlKC-UwDGQErtLa5oG6C3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 625.3333740234375, + "y": 554.1666870117188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 236, + "height": 20, + "seed": 1957232156, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706639231, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "Wasm LOCALs (local.set/get):", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Wasm LOCALs (local.set/get):" + }, + { + "type": "rectangle", + "version": 264, + "versionNonce": 500451192, + "isDeleted": false, + "id": "CnBSoD9DNI5Himd9gJXwV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 787.8953006814252, + "y": 625.661102913823, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 30.6666259765625, + "height": 19.000091552734375, + "seed": 1737116886, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 259, + "versionNonce": 235932680, + "isDeleted": false, + "id": "z4Ye9_zQDUab5ewn2mujX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 827.5619876931439, + "y": 623.9944159021043, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 30.6666259765625, + "height": 19.000091552734375, + "seed": 1207868746, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 292, + "versionNonce": 600610936, + "isDeleted": false, + "id": "V3_r6rtZsp-pFDhmttdsh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 865.5620487283002, + "y": 625.3277594079636, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 30.6666259765625, + "height": 19.000091552734375, + "seed": 974809482, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 266, + "versionNonce": 1061243656, + "isDeleted": false, + "id": "Tov8P7W3W8Zmma9a3S8gP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 904.5620487283002, + "y": 626.661102913823, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 30.6666259765625, + "height": 19.000091552734375, + "seed": 1423773654, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679706543901, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 148, + "versionNonce": 1167673464, + "isDeleted": false, + "id": "KiC12zdfqG7zXRbctXGmT", + "fillStyle": "hachure", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 806.4613995527118, + "y": 748.8279119958543, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 2.161352857758061, + "height": 20.33328247070324, + "seed": 167487754, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679706706153, + "link": null, + "locked": false, + "startBinding": { + "elementId": "B1eH_aNspf4OoiXnvWu1V", + "focus": 0.026931962058821583, + "gap": 12.327789925541765 + }, + "endBinding": { + "elementId": "fxSjNN3geJtdm-2MR7INN", + "focus": -0.06989783256192499, + "gap": 24.16110291382313 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 2.161352857758061, + 20.33328247070324 + ] + ] + }, + { + "type": "text", + "version": 151, + "versionNonce": 1430888312, + "isDeleted": false, + "id": "wBnV18_fVimstqpfPY0KR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 631.4456683895414, + "y": 624.6666107177734, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 91, + "height": 20, + "seed": 1961347080, + "groupIds": [], + "roundness": null, + "boundElements": null, + "updated": 1679706671751, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func locals:", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func locals:" + }, + { + "id": "8Y0Qz7jDPtVE84-d_UHu2", + "type": "rectangle", + "x": 613.2790118954008, + "y": 575.9999542236328, + "width": 384.0000305175781, + "height": 76.66668701171875, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 3 + }, + "seed": 743618568, + "version": 80, + "versionNonce": 129563000, + "isDeleted": false, + "boundElements": null, + "updated": 1679706644458, + "link": null, + "locked": false + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/core/iwasm/doc/images/stack_format_ci.svg b/core/iwasm/doc/images/stack_format_ci.svg new file mode 100644 index 0000000000..f054c4345a --- /dev/null +++ b/core/iwasm/doc/images/stack_format_ci.svg @@ -0,0 +1,16 @@ + + + + + + + prev_framefunc_inst ipopnd_stack_bottomopnd_sp (point to opnd stack top)label_stack_bottomlabel_sp (point to label stack top)func arguments:<operand stack>(current func instance) (next bytecode in codefor return)opnd_stack_boundarylabel_stack_boundary<lable stack>prev_frameStack bottomcurrent frameUnusedstack boundarystack topWasm LOCALs (local.set/get):func locals: \ No newline at end of file diff --git a/core/iwasm/doc/images/wasm_exports.svg b/core/iwasm/doc/images/wasm_exports.svg new file mode 100644 index 0000000000..573f11c351 --- /dev/null +++ b/core/iwasm/doc/images/wasm_exports.svg @@ -0,0 +1,16 @@ + + + + + + + WASMModule WASMTable *tables; WASMMemory *memories; WASMGlobal *globals;WASMExport *exports;WASMExportchar *name;uint8 kind;uint32 index;[0][1][...]WASMModuleInstanceexport_tablesexport_memoriesexport_globalsexport_functionsWASMExportFuncInstancechar* name functionname: shown to externalindex: refer to item idx for export in current kindkind: total 4 export kinds:- function- globals- memory- table[1][0][...][n](refer to function diagam)export_func_countWASMExportGlobInstancechar* name global[1][0][...][n]WASMGlobalInstanceWASMExportMemInstancechar* name memory(WASMMemoryInstance*)[1][0][...][n]WASMMemoryInstanceWASMExportTabInstancechar* name table[1][0][...][n](WASMTableInstance *)WASMFunctionInstance[..]WASMFunction (per module)WASMModule::functionsWASMModule::globalsWASMModule::tablesWASMModule::memoriesWASMFunction **functions;export_global_countexport_memory_countexport_table_count \ No newline at end of file diff --git a/core/iwasm/doc/images/wasm_function.excalidraw b/core/iwasm/doc/images/wasm_function.excalidraw new file mode 100644 index 0000000000..8c59bdbca2 --- /dev/null +++ b/core/iwasm/doc/images/wasm_function.excalidraw @@ -0,0 +1,7754 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "type": "rectangle", + "version": 215, + "versionNonce": 1880133867, + "isDeleted": false, + "id": "4o9JHPgrKQSqK-ibc0qs_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2254.5001678466797, + "y": 1663.1666412353516, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 150.66668701171875, + "height": 43.000030517578125, + "seed": 4270136, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "PsNatrhOR918YjbmvyT9x", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 230, + "versionNonce": 1135445829, + "isDeleted": false, + "id": "94gfo2BctxYsRP6cuuIbI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1297.1946105957031, + "y": 1755.6666768391929, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 214.33333333333348, + "height": 122.66668701171875, + "seed": 305330883, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "9vnyulmvSUCDWXvSKKyJ6", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 162, + "versionNonce": 1027947403, + "isDeleted": false, + "id": "JWlL3nHzTP4pxrEVYolFx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2226.416498184204, + "y": 1001.3333435058594, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 278.6666259765625, + "height": 244.00003051757812, + "seed": 1502608995, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "70jp9eV1jV2_kUBbN055m", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 115, + "versionNonce": 291919525, + "isDeleted": false, + "id": "Cc8lshSSJ7lAY4RPjzS5A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1289.1666717529297, + "y": 1285.8333587646484, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 178, + "height": 20, + "seed": 411108936, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMFunctionInstance", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunctionInstance" + }, + { + "type": "rectangle", + "version": 100, + "versionNonce": 1607176747, + "isDeleted": false, + "id": "onh-wm6wgKrkH94fakl6O", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1291.1666107177734, + "y": 1309.1666717529297, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 259.33331298828125, + "height": 210.3333435058593, + "seed": 2073695304, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "fltFoJAyfls8KPkBw6X_P", + "type": "arrow" + }, + { + "id": "MLVyGZQLa4jU554J6bsmJ", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 113, + "versionNonce": 34486789, + "isDeleted": false, + "id": "eAtZfC7P3ZafizTDITzz-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1363.1666717529297, + "y": 1335.500015258789, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10, + "height": 12.333343505859375, + "seed": 136923720, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 139, + "versionNonce": 411964619, + "isDeleted": false, + "id": "GmBiArFp8A3Q9NaPWxp3Q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1395.8332977294922, + "y": 1332.8333587646484, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 94, + "height": 20, + "seed": 258177848, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "nUF7GyfmAGZN3iZvBfYtq", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_import", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func_import" + }, + { + "type": "text", + "version": 239, + "versionNonce": 245543269, + "isDeleted": false, + "id": "YdCXv7DFgrOOU2wowasgm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1748.499984741211, + "y": 1142.4999237060547, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 164, + "height": 20, + "seed": 1678600520, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMFunctionImport:", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunctionImport:" + }, + { + "type": "rectangle", + "version": 254, + "versionNonce": 656884587, + "isDeleted": false, + "id": "1Oi1iFkA8pBbaQpvduCq1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1738.499984741211, + "y": 1161.1666717529297, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 200.33340454101554, + "seed": 1244990536, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "nUF7GyfmAGZN3iZvBfYtq", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 701, + "versionNonce": 879836357, + "isDeleted": false, + "id": "nUF7GyfmAGZN3iZvBfYtq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1502.280048689805, + "y": 1332.5712493918486, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 234.84228393034437, + "height": 170.17912641351631, + "seed": 213606712, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "startBinding": { + "elementId": "GmBiArFp8A3Q9NaPWxp3Q", + "focus": 0.8789861743715494, + "gap": 14.558826765976846 + }, + "endBinding": { + "elementId": "w7-3leJjFtbtDhwDpkUjF", + "focus": 0.9853865103923569, + "gap": 6.774518257019281 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 157.46964422706515, + -143.2377075217314 + ], + [ + 234.84228393034437, + -170.17912641351631 + ] + ] + }, + { + "type": "text", + "version": 197, + "versionNonce": 1944165899, + "isDeleted": false, + "id": "bM6INpWVFBvURve3zdbdf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1396.1666717529297, + "y": 1355.5000457763672, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 34, + "height": 20, + "seed": 379599688, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "78xSb96N8EcRm2LhdCNjJ", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func" + }, + { + "type": "diamond", + "version": 140, + "versionNonce": 1796693029, + "isDeleted": false, + "id": "3cBYbIbQEyvFuXBMoyida", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1364.8332977294922, + "y": 1360.3333435058594, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10, + "height": 12.333343505859375, + "seed": 1837623096, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 310, + "versionNonce": 2042078379, + "isDeleted": false, + "id": "9KTm6XzaqX3iZ90_AL3RN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1900.8332977294922, + "y": 1530.166732788086, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 211, + "height": 20, + "seed": 1970637640, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Bnf72M4RGMZjNgBlzk90B", + "type": "arrow" + }, + { + "id": "78xSb96N8EcRm2LhdCNjJ", + "type": "arrow" + }, + { + "id": "S1cN82-5SoSu4e4SWfAUw", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMFunction (per module)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunction (per module)" + }, + { + "type": "rectangle", + "version": 261, + "versionNonce": 1518505861, + "isDeleted": false, + "id": "0kWlc6iPzGzhrfIVEPeOM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1902.1667938232422, + "y": 1559.6667175292969, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 225.33337402343744, + "height": 200.9999389648438, + "seed": 2020723016, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "78xSb96N8EcRm2LhdCNjJ", + "type": "arrow" + }, + { + "id": "Bnf72M4RGMZjNgBlzk90B", + "type": "arrow" + }, + { + "id": "uC3ZSm-IltHDllxDGLJ9v", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 675, + "versionNonce": 1884542795, + "isDeleted": false, + "id": "zzj3BPEivUiaW1sqpfx7J", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2266.8334197998047, + "y": 1673.8333282470703, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 129, + "height": 20, + "seed": 26708536, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "78xSb96N8EcRm2LhdCNjJ", + "type": "arrow" + }, + { + "id": "PsNatrhOR918YjbmvyT9x", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "internal function", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "internal function" + }, + { + "type": "arrow", + "version": 666, + "versionNonce": 1357928165, + "isDeleted": false, + "id": "78xSb96N8EcRm2LhdCNjJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1434.5331571443298, + "y": 1377.528759465866, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 458.6267427864543, + "height": 186.82387510648414, + "seed": 535299656, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "startBinding": { + "elementId": "evHonaxVjRvjwFP08UX9x", + "focus": -0.827785929496437, + "gap": 16.471286310501227 + }, + "endBinding": { + "elementId": "9KTm6XzaqX3iZ90_AL3RN", + "focus": -1.317866862333605, + "gap": 14.985901784264229 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 184.63351460859985, + 58.63791228706373 + ], + [ + 458.6267427864543, + 186.82387510648414 + ] + ] + }, + { + "type": "rectangle", + "version": 145, + "versionNonce": 907064811, + "isDeleted": false, + "id": "WfHCEtd4eDaOU42FFn9wo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1341.1666717529297, + "y": 1321.500015258789, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 186.00006103515625, + "height": 65, + "seed": 663703880, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "78xSb96N8EcRm2LhdCNjJ", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 166, + "versionNonce": 2028876357, + "isDeleted": false, + "id": "QbAKq7kA_EZo7yxdpqj6F", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1742.499984741211, + "y": 1166.8332977294922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 158.66668701171872, + "height": 45.33337402343752, + "seed": 757681480, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 170, + "versionNonce": 2046345355, + "isDeleted": false, + "id": "w7-3leJjFtbtDhwDpkUjF", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1715.499984741211, + "y": 1169.1666412353516, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 187, + "height": 40, + "seed": 1177365064, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "nUF7GyfmAGZN3iZvBfYtq", + "type": "arrow" + }, + { + "id": "70jp9eV1jV2_kUBbN055m", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": " char *module_name;\n char *field_name;", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " char *module_name;\n char *field_name;" + }, + { + "type": "rectangle", + "version": 190, + "versionNonce": 1331274149, + "isDeleted": false, + "id": "m6kQPfRjltByoJapP3m-h", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1744.499984741211, + "y": 1234.499984741211, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 155.33337402343747, + "height": 35.666656494140625, + "seed": 613948728, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 184, + "versionNonce": 633763627, + "isDeleted": false, + "id": "IXwlC5RgaF1iJPCtg6-Er", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1768.4999237060547, + "y": 1243.499984741211, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 125, + "height": 20, + "seed": 664043080, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "l4S1IXvHmVx_wl8DPQXk3", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_ptr_linked", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func_ptr_linked" + }, + { + "type": "rectangle", + "version": 230, + "versionNonce": 1499473157, + "isDeleted": false, + "id": "o1vzUOCoilIzaDJdTpt35", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1735.8333587646484, + "y": 932.4999542236328, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 155.33331298828125, + "height": 90.33334350585938, + "seed": 1170302520, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "l4S1IXvHmVx_wl8DPQXk3", + "type": "arrow" + }, + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 211, + "versionNonce": 132405707, + "isDeleted": false, + "id": "Kpjtivj-7LYLq1nuvC4KS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1742.5000457763672, + "y": 940.8332977294922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 123, + "height": 20, + "seed": 1541878344, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "l4S1IXvHmVx_wl8DPQXk3", + "type": "arrow" + }, + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "native function:", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "native function:" + }, + { + "type": "arrow", + "version": 755, + "versionNonce": 444546149, + "isDeleted": false, + "id": "l4S1IXvHmVx_wl8DPQXk3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1751.6334408235434, + "y": 1247.8332977294922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 121.88354763506686, + "height": 307.57912212433075, + "seed": 203809096, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "startBinding": { + "elementId": "OpV38MM8YOexWG_e-5_hX", + "focus": -0.4013706262952343, + "gap": 1.943772417380456 + }, + "endBinding": { + "elementId": "Kpjtivj-7LYLq1nuvC4KS", + "focus": 1.1040701980735919, + "gap": 6.6751580517609455 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -121.88354763506686, + -191.16677856445312 + ], + [ + -15.808553098937182, + -307.57912212433075 + ] + ] + }, + { + "type": "rectangle", + "version": 185, + "versionNonce": 1030547563, + "isDeleted": false, + "id": "5EDlNHzjVbZ3Rx8ny3SUe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1742.6665802001953, + "y": 1279.1666412353516, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 165.16677856445304, + "height": 73.666748046875, + "seed": 202955336, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 241, + "versionNonce": 1250191301, + "isDeleted": false, + "id": "wc-s6ecXMbmh2ViGrkOa4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1757.8332977294922, + "y": 1289.5000457763672, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 152, + "height": 40, + "seed": 1536408648, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "S1cN82-5SoSu4e4SWfAUw", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "import_module;\nimport_func_linked;", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "import_module;\nimport_func_linked;" + }, + { + "type": "arrow", + "version": 788, + "versionNonce": 1546298123, + "isDeleted": false, + "id": "S1cN82-5SoSu4e4SWfAUw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1741.963485458681, + "y": 1322.4954523286892, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 248.14874440629774, + "height": 238.16542426722026, + "seed": 1657735240, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-vw33v9u2Ko2ayilI0CXG", + "focus": 2.0635763351494516, + "gap": 4.200185998866319 + }, + "endBinding": { + "elementId": "9KTm6XzaqX3iZ90_AL3RN", + "focus": -1.1514950968199016, + "gap": 11.294143807823616 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -88.79684422332912, + 88.33793695353734 + ], + [ + 159.35190018296862, + 238.16542426722026 + ] + ] + }, + { + "type": "text", + "version": 234, + "versionNonce": 1267541797, + "isDeleted": false, + "id": "jF8UBxFom2hco1NronB-_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1639.2142922537669, + "y": 1510.404782976423, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 137, + "height": 20, + "seed": 548488008, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(WASMFunction *)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(WASMFunction *)" + }, + { + "type": "diamond", + "version": 76, + "versionNonce": 1553467819, + "isDeleted": false, + "id": "jJOCFAslIe3JYgWsRnIKx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1915.8334197998047, + "y": 1577.1667022705078, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 17.33331298828125, + "height": 15.666656494140625, + "seed": 1470386504, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [ + { + "id": "PsNatrhOR918YjbmvyT9x", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 88, + "versionNonce": 1611070085, + "isDeleted": false, + "id": "V1RSJPoudlOhc-GaAxPSa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1947.8334197998047, + "y": 1577.500015258789, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 37, + "height": 20, + "seed": 1209849672, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "PsNatrhOR918YjbmvyT9x", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "code", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "code" + }, + { + "type": "rectangle", + "version": 217, + "versionNonce": 1138728011, + "isDeleted": false, + "id": "tXHmR9TBkCnxMdo_pd0XG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2255.833480834961, + "y": 1603.1666412353516, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 149.3333740234375, + "height": 114.33340454101562, + "seed": 321892664, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 182, + "versionNonce": 548364773, + "isDeleted": false, + "id": "Zsg-OsrfZ2doJiTuOOpPu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2277.166793823242, + "y": 1576.1666107177734, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70, + "height": 20, + "seed": 40194872, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "bytecode", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "bytecode" + }, + { + "type": "arrow", + "version": 545, + "versionNonce": 232512235, + "isDeleted": false, + "id": "PsNatrhOR918YjbmvyT9x", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1990.5000762939453, + "y": 1589.1666717529297, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 264.9616597351928, + "height": 70.73153780068333, + "seed": 1716226360, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "startBinding": { + "elementId": "V1RSJPoudlOhc-GaAxPSa", + "focus": -0.13746287298855567, + "gap": 7.9146728515625 + }, + "endBinding": { + "elementId": "4o9JHPgrKQSqK-ibc0qs_", + "focus": 0.060950944035830956, + "gap": 3.268431681738548 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 149.9165802001953, + 22.1666259765625 + ], + [ + 191.24952507019043, + 48.83355712890625 + ], + [ + 264.9616597351928, + 70.73153780068333 + ] + ] + }, + { + "type": "diamond", + "version": 79, + "versionNonce": 848551237, + "isDeleted": false, + "id": "sWaZg1Dcn3g0Qfdc7onW_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1916.5001068115234, + "y": 1629.8333892822266, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 18.6666259765625, + "height": 15, + "seed": 238700600, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 84, + "versionNonce": 1461142923, + "isDeleted": false, + "id": "U5NUD0rdNNDuY14J1ZRMu", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1954.8333587646484, + "y": 1628.8333892822266, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 110, + "height": 20, + "seed": 744475464, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "code_compiled", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "code_compiled" + }, + { + "type": "rectangle", + "version": 431, + "versionNonce": 1024584869, + "isDeleted": false, + "id": "gk39IBXF-F8MrwhzL35C6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2462.5001068115234, + "y": 1639.5001373291016, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 114.66674804687503, + "height": 119.33334350585936, + "seed": 201839672, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 444, + "versionNonce": 1187798059, + "isDeleted": false, + "id": "ZRckuyHrC8P5etlpd8MWT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2443.166793823242, + "y": 1616.833480834961, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 161, + "height": 20, + "seed": 2016979784, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "precompiled-bytecode", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "precompiled-bytecode" + }, + { + "type": "rectangle", + "version": 437, + "versionNonce": 534188037, + "isDeleted": false, + "id": "qGjmy62MZbL7zALsNU2dL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2463.166793823242, + "y": 1699.5001373291016, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 114.00006103515625, + "height": 43.000030517578125, + "seed": 1165004088, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "iqe6U9xOE6ECb18moJnw-", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 68, + "versionNonce": 954572491, + "isDeleted": false, + "id": "SiZHwNA9YKd93iwc74wxB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1942.4999542236328, + "y": 1667.833480834961, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 175, + "height": 20, + "seed": 1708294472, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "HCvGV6j45DG_BGeI3J7ut", + "type": "arrow" + } + ], + "updated": 1679533929331, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "fast_jit_jitted_code", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "fast_jit_jitted_code" + }, + { + "type": "diamond", + "version": 85, + "versionNonce": 1759561573, + "isDeleted": false, + "id": "ODI38iO5_5uu6RobQgkVa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1912.499984741211, + "y": 1672.3333892822266, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 18.6666259765625, + "height": 15, + "seed": 297387080, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 93, + "versionNonce": 670928235, + "isDeleted": false, + "id": "gNxfqnpn5KEfZX8dL5GX3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1913.499984741211, + "y": 1717.666732788086, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 18.6666259765625, + "height": 15, + "seed": 1391035448, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 99, + "versionNonce": 175936197, + "isDeleted": false, + "id": "sYSDwbrpZl0692xpkcG4z", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1946.499984741211, + "y": 1713.166763305664, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 143, + "height": 20, + "seed": 1305637176, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "RckpORzYftIA2k4VSkTaW", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "llvm_jit_func_ptr", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "llvm_jit_func_ptr" + }, + { + "type": "text", + "version": 450, + "versionNonce": 1465821195, + "isDeleted": false, + "id": "1iUf8pP_YoM8qCPpSEahX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2383.0000762939453, + "y": 1788.8333129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 142, + "height": 20, + "seed": 68451128, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "fast jitted coode", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "fast jitted coode" + }, + { + "type": "rectangle", + "version": 501, + "versionNonce": 1363669541, + "isDeleted": false, + "id": "qVsRlSPxTl9a8XrnRMl2a", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2405.6665802001953, + "y": 1827.4999389648438, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 103.33337402343747, + "height": 29.2, + "seed": 1374549064, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "HCvGV6j45DG_BGeI3J7ut", + "type": "arrow" + }, + { + "id": "BF2h7Ub5gAf2yYxLfQSFh", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 447, + "versionNonce": 1023635115, + "isDeleted": false, + "id": "bu6GnR96DeCSFWu3DhMIJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2419.499954223633, + "y": 1890.1666870117188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 133, + "height": 20, + "seed": 713214264, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "RckpORzYftIA2k4VSkTaW", + "type": "arrow" + }, + { + "id": "kjpM2qWJqDrV-jr9XK8QR", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "llvm jitted coode", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "llvm jitted coode" + }, + { + "type": "rectangle", + "version": 395, + "versionNonce": 439379333, + "isDeleted": false, + "id": "Z9cJSNSjDvW2uPNthdOW9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2406.1666412353516, + "y": 1916.1666870117188, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 150.66668701171875, + "height": 43.000030517578125, + "seed": 1255376456, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "4E9MghnYo6hn8E82pmPMe", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 266, + "versionNonce": 731275595, + "isDeleted": false, + "id": "evHonaxVjRvjwFP08UX9x", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1335.1666259765625, + "y": 1394.0000457763672, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 187.33343505859372, + "height": 73.666748046875, + "seed": 873572680, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "78xSb96N8EcRm2LhdCNjJ", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 258, + "versionNonce": 1215524069, + "isDeleted": false, + "id": "4R5zAaFl-qG-PwNUPfyXq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1360.4999389648438, + "y": 1404.6667022705078, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 157, + "height": 40, + "seed": 237924152, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "import_module_inst;\nimport_func_inst;", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "import_module_inst;\nimport_func_inst;" + }, + { + "type": "diamond", + "version": 132, + "versionNonce": 1014104043, + "isDeleted": false, + "id": "-cXxAxf0DBRaXH85Wn82G", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1341.4998168945312, + "y": 1408.6666564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10, + "height": 12.333343505859375, + "seed": 1993562680, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "MLVyGZQLa4jU554J6bsmJ", + "type": "arrow" + }, + { + "id": "9vnyulmvSUCDWXvSKKyJ6", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 136, + "versionNonce": 2073987141, + "isDeleted": false, + "id": "V3cEII-BWPnk8MmO8v9Hw", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1342.8331909179688, + "y": 1431.6666564941406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10, + "height": 12.333343505859375, + "seed": 374375752, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "MLVyGZQLa4jU554J6bsmJ", + "type": "arrow" + }, + { + "id": "9vnyulmvSUCDWXvSKKyJ6", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 196, + "versionNonce": 2040580747, + "isDeleted": false, + "id": "jccHI4GP5zADwZpJ6_F0e", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1020.4999542236328, + "y": 1294.5002899169922, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 132.66668701171866, + "height": 34.3333740234375, + "seed": 1209325128, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "eyNKBEqdZDGI0jikxT-7-" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 28, + "versionNonce": 1577034445, + "isDeleted": false, + "id": "eyNKBEqdZDGI0jikxT-7-", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1050.8332977294922, + "y": 1302.066976928711, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 72, + "height": 20, + "seed": 13177699, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247195, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "functions", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "jccHI4GP5zADwZpJ6_F0e", + "originalText": "functions" + }, + { + "type": "diamond", + "version": 157, + "versionNonce": 1205682475, + "isDeleted": false, + "id": "DpQKmgYqIbva-oudQDKVA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1029.8332977294922, + "y": 1302.8336334228516, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1787304760, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "0dTwoTnwCJUbyz3e0bm1O", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 460, + "versionNonce": 544406277, + "isDeleted": false, + "id": "fltFoJAyfls8KPkBw6X_P", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1152.0382824623548, + "y": 1307.393701716202, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 140.3213191272912, + "height": 0.8215748529805751, + "seed": 1890946120, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "onh-wm6wgKrkH94fakl6O", + "focus": 1.0244280280971265, + "gap": 2.5945448897082315 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 140.3213191272912, + -0.8215748529805751 + ] + ] + }, + { + "type": "rectangle", + "version": 245, + "versionNonce": 277996491, + "isDeleted": false, + "id": "BpsyACMObLF20Fkti2Uqq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1020.8332061767578, + "y": 1339.000228881836, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 130.00000000000009, + "height": 31, + "seed": 1664170040, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "22kjCR2ZOQmZQXYgnarEF" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 50, + "versionNonce": 1029340291, + "isDeleted": false, + "id": "22kjCR2ZOQmZQXYgnarEF", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1058.3332061767578, + "y": 1344.900228881836, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55, + "height": 20, + "seed": 1753405955, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247196, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "globals", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "BpsyACMObLF20Fkti2Uqq", + "originalText": "globals" + }, + { + "type": "diamond", + "version": 201, + "versionNonce": 735654507, + "isDeleted": false, + "id": "66O-4o40vS3pLIeBqwFBW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1030.1665802001953, + "y": 1344.000228881836, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 231180104, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 336, + "versionNonce": 2136587717, + "isDeleted": false, + "id": "l2Sz8ohFcHQT7gWys1M9D", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 751.1665802001953, + "y": 1311.6668853759766, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 158, + "height": 32, + "seed": 1571295288, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "o-cON41NQVHvHqkgeW_6m" + }, + { + "id": "0dTwoTnwCJUbyz3e0bm1O", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 115, + "versionNonce": 956023085, + "isDeleted": false, + "id": "o-cON41NQVHvHqkgeW_6m", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 824.6665802001953, + "y": 1317.1668853759766, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 11, + "height": 20, + "seed": 1939939267, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247196, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "e", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "l2Sz8ohFcHQT7gWys1M9D", + "originalText": "e" + }, + { + "type": "diamond", + "version": 340, + "versionNonce": 659012901, + "isDeleted": false, + "id": "_eiwTSAQSXB8P2k-PDhNa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 769.8332061767578, + "y": 1323.6668548583984, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1019883336, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "0dTwoTnwCJUbyz3e0bm1O", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 238, + "versionNonce": 536466347, + "isDeleted": false, + "id": "OLHiYmF0KqdbZBgecyb7T", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1017.1665802001953, + "y": 1430.6669158935547, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 131.33337402343759, + "height": 32.33331298828125, + "seed": 1564507192, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 172, + "versionNonce": 1409208453, + "isDeleted": false, + "id": "qUAVJJGhHiEU00yvj5Xwy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1041.1665802001953, + "y": 1438.3335723876953, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1916699464, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 170, + "versionNonce": 209200715, + "isDeleted": false, + "id": "ZzKApm4TYPqV3EGJbMuQ3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1001.6664886474609, + "y": 1253.9168853759766, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 214, + "height": 20, + "seed": 1061991496, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstanceExtra", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstanceExtra" + }, + { + "type": "rectangle", + "version": 195, + "versionNonce": 859600869, + "isDeleted": false, + "id": "SWgPVXvgjtN7AVlTfBUDR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1009.2498474121094, + "y": 1279.9169082641602, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 155, + "height": 200.41667938232422, + "seed": 1419979080, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "0dTwoTnwCJUbyz3e0bm1O", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 389, + "versionNonce": 114500843, + "isDeleted": false, + "id": "3rcvtpnHrIvCrE__yw5GU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1339.5831756591797, + "y": 1031.000186920166, + "strokeColor": "#d9480f", + "backgroundColor": "transparent", + "width": 174, + "height": 40, + "seed": 1610110792, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "AuwWYqGK5XChc2C2ZDCOd", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstance::\nimport_func_ptrs", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance::\nimport_func_ptrs" + }, + { + "type": "rectangle", + "version": 137, + "versionNonce": 1594766149, + "isDeleted": false, + "id": "IK-a-uPI373j3VM1YHdKy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1390.1666870117188, + "y": 1107.625286102295, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1938455864, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "dA4sHNYw9NqC0yRSl1ByC", + "type": "arrow" + }, + { + "id": "AuwWYqGK5XChc2C2ZDCOd", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 123, + "versionNonce": 110448523, + "isDeleted": false, + "id": "xEmeSz_qg3vcIV0Kjo_4r", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1414.1666870117188, + "y": 1115.2919425964355, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1045500488, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "AuwWYqGK5XChc2C2ZDCOd", + "type": "arrow" + }, + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 158, + "versionNonce": 1406239397, + "isDeleted": false, + "id": "u6aTea5vKrjtv4E0SAr_D", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1389.8333129882812, + "y": 1140.7919120788574, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1488328248, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 144, + "versionNonce": 771302955, + "isDeleted": false, + "id": "6petv8iQ_6W4RCCyGVs86", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1413.8333129882812, + "y": 1148.458568572998, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1174899016, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 146, + "versionNonce": 640935429, + "isDeleted": false, + "id": "HWwjwJX9MC7vOni7CdfwU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1389.5, + "y": 1174.1252555847168, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 654741304, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 158, + "versionNonce": 1944660171, + "isDeleted": false, + "id": "3V6VqEBHUeHRXm4rtMN9P", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1388.1666259765625, + "y": 1201.7919120788574, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 652208184, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 138, + "versionNonce": 2033376613, + "isDeleted": false, + "id": "s3LweJMlqb3u8zFFOtRWC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1410.8333129882812, + "y": 1210.1252555847168, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 630970184, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 748, + "versionNonce": 234870635, + "isDeleted": false, + "id": "j_Tg3JOansfDRNxNBUMfi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1423.3266279235036, + "y": 1116.6584335466105, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 310.5923414580757, + "height": 177.73696684706545, + "seed": 561180216, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "startBinding": { + "elementId": "xEmeSz_qg3vcIV0Kjo_4r", + "focus": -0.6524247058306003, + "gap": 2.5695135809208747 + }, + "endBinding": { + "elementId": "Kpjtivj-7LYLq1nuvC4KS", + "focus": 1.1530360747961412, + "gap": 8.581076394787942 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 125.75645618294175, + -55.99185334641493 + ], + [ + 310.5923414580757, + -177.73696684706545 + ] + ] + }, + { + "type": "diamond", + "version": 115, + "versionNonce": 874244293, + "isDeleted": false, + "id": "SosCUEMFvN64e8eYhtj4S", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1748.0836486816406, + "y": 1289.8335762023926, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 8.333282470703125, + "height": 13.75, + "seed": 1668126536, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "S1cN82-5SoSu4e4SWfAUw", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 119, + "versionNonce": 445100555, + "isDeleted": false, + "id": "-vw33v9u2Ko2ayilI0CXG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1745.5836486816406, + "y": 1319.8335762023926, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10.833282470703125, + "height": 9.583320617675781, + "seed": 2012578120, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "S1cN82-5SoSu4e4SWfAUw", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 648, + "versionNonce": 2022475813, + "isDeleted": false, + "id": "MLVyGZQLa4jU554J6bsmJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1347.8331909179688, + "y": 1415.2017225151058, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 179.41659545898438, + "height": 304.1667256414903, + "seed": 1591654456, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "startBinding": { + "elementId": "V3cEII-BWPnk8MmO8v9Hw", + "focus": 3.669987091693772, + "gap": 10.369642504898593 + }, + "endBinding": { + "elementId": "Rcref7JZ-AhlcXLLdwY5D", + "focus": -0.4568695235814372, + "gap": 6.321478788304148 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -179.41659545898438, + 169.4649187202458 + ], + [ + -89.48586426600582, + 304.1667256414903 + ] + ] + }, + { + "type": "text", + "version": 807, + "versionNonce": 175847595, + "isDeleted": false, + "id": "tUs9waDW6sL0AbyoZYJ5Q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 867.8295186360679, + "y": 823.9724260965983, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 672, + "height": 160, + "seed": 709576760, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "this is the one actually referred during executing opcode\n\nduring model load, if import can be solved through the native api registeration,\nthe pointer of native function will be filled.\n\nc-api could change the pointer later, then it will point to a different native function\n\nNULL: means multi-module import, go to \"import_func_inst\" field for target function", + "baseline": 154, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "this is the one actually referred during executing opcode\n\nduring model load, if import can be solved through the native api registeration,\nthe pointer of native function will be filled.\n\nc-api could change the pointer later, then it will point to a different native function\n\nNULL: means multi-module import, go to \"import_func_inst\" field for target function" + }, + { + "type": "rectangle", + "version": 410, + "versionNonce": 1483881349, + "isDeleted": false, + "id": "kARKLR5va5hDwe-2JCl7d", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 855.623291015625, + "y": 824.4128579639253, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 691.1904471261159, + "height": 170.05954742431626, + "seed": 1355113288, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "AuwWYqGK5XChc2C2ZDCOd", + "type": "arrow" + }, + { + "id": "j_Tg3JOansfDRNxNBUMfi", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 1427, + "versionNonce": 1267002187, + "isDeleted": false, + "id": "AuwWYqGK5XChc2C2ZDCOd", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1132.2583561737392, + "y": 997.6315427986297, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 188.93306844281233, + "height": 54.67370578283135, + "seed": 1524992312, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "startBinding": { + "elementId": "kARKLR5va5hDwe-2JCl7d", + "gap": 3.1591374103880994, + "focus": 0.5842950344963632 + }, + "endBinding": { + "elementId": "3rcvtpnHrIvCrE__yw5GU", + "gap": 18.39175104262814, + "focus": -0.7039875993615579 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 188.93306844281233, + 54.67370578283135 + ] + ] + }, + { + "type": "text", + "version": 363, + "versionNonce": 1275757285, + "isDeleted": false, + "id": "_pYmb7H1lxRLE4Qw_MajA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1034.3336715698242, + "y": 1680.4170036315918, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 20, + "seed": 764509256, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstance*", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance*" + }, + { + "type": "text", + "version": 314, + "versionNonce": 838814187, + "isDeleted": false, + "id": "GBXnnFKM_76tjt1WAlYnV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1250.0279070536296, + "y": 1605.2504641215007, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 198, + "height": 20, + "seed": 1630119496, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "9vnyulmvSUCDWXvSKKyJ6", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(WASMFunctionInstance*)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(WASMFunctionInstance*)" + }, + { + "type": "arrow", + "version": 881, + "versionNonce": 1742921285, + "isDeleted": false, + "id": "9vnyulmvSUCDWXvSKKyJ6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1347.3662637658592, + "y": 1437.871212796838, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 109.83801974730454, + "height": 307.7636946939249, + "seed": 422577480, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-cXxAxf0DBRaXH85Wn82G", + "focus": -3.9115852992052806, + "gap": 11.29853538112821 + }, + "endBinding": { + "elementId": "94gfo2BctxYsRP6cuuIbI", + "gap": 10.033975741878294, + "focus": -0.6520703073991437 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -109.83801974730454, + 135.3790961936404 + ], + [ + -51.493209521376, + 307.7636946939249 + ] + ] + }, + { + "type": "diamond", + "version": 207, + "versionNonce": 2116029227, + "isDeleted": false, + "id": "kliwot061_yUhDMDbVbQe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1354.3614679972331, + "y": 1779.417065938314, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10, + "height": 12.333343505859375, + "seed": 82951992, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 233, + "versionNonce": 2082066693, + "isDeleted": false, + "id": "mvfNSPpLgMxaEaQuXYPrl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1387.0280939737956, + "y": 1776.7504094441733, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 94, + "height": 20, + "seed": 1266085960, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_import", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func_import" + }, + { + "type": "text", + "version": 265, + "versionNonce": 1206611403, + "isDeleted": false, + "id": "Oe13Ts1dEazuz8ilwnZiL", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1398.6947809855144, + "y": 1806.7504094441733, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 34, + "height": 20, + "seed": 2145720376, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func" + }, + { + "type": "diamond", + "version": 230, + "versionNonce": 287533157, + "isDeleted": false, + "id": "nyquujDKZGyYvDs-a_GQ-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1356.0280939737956, + "y": 1811.5837071736655, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 10, + "height": 12.333343505859375, + "seed": 784443208, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 252, + "versionNonce": 326204523, + "isDeleted": false, + "id": "cGQYK5rXelrtuGolytFol", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1332.3614679972331, + "y": 1765.417065938314, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 154.00006103515616, + "height": 79, + "seed": 1426411832, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "9vnyulmvSUCDWXvSKKyJ6", + "type": "arrow" + } + ], + "updated": 1679533929332, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 521, + "versionNonce": 868422597, + "isDeleted": false, + "id": "VRweEUgFB9qqzjdTwpHnK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1323.694574991862, + "y": 1739.194897969564, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 117, + "height": 20, + "seed": 155802936, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929332, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMFunction ", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunction " + }, + { + "type": "arrow", + "version": 621, + "versionNonce": 746370827, + "isDeleted": false, + "id": "CUEfVWpVIuIHc_5h3VskN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1459.8716446745273, + "y": 1829.1380187988277, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 394.2706533635544, + "height": 82.19527893066447, + "seed": 1854588984, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "epVvbDyPF40MaERFnDDJy", + "focus": 0.2506598246466399, + "gap": 9.849883651733307 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 153.21163779617586, + 82.19527893066447 + ], + [ + 394.2706533635544, + 27.990782200797412 + ] + ] + }, + { + "type": "text", + "version": 309, + "versionNonce": 1880511269, + "isDeleted": false, + "id": "OTP338ttAjF_rIJwNKA1S", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1298.416748046875, + "y": 1343.3333587646484, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 38, + "height": 20, + "seed": 1994699981, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "union", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "union" + }, + { + "type": "rectangle", + "version": 62, + "versionNonce": 25664939, + "isDeleted": false, + "id": "7kmKkWcfD2eeTgLmci_TA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1289.75, + "y": 1517.3333282470703, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 260.66668701171875, + "height": 61.333343505859375, + "seed": 1810842211, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "fr9-3bNKWz24759an1jPs" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 13, + "versionNonce": 914224163, + "isDeleted": false, + "id": "fr9-3bNKWz24759an1jPs", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1404.5833435058594, + "y": 1538.4, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 20, + "seed": 1884299875, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247200, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7kmKkWcfD2eeTgLmci_TA", + "originalText": "[...]" + }, + { + "type": "line", + "version": 176, + "versionNonce": 1373083019, + "isDeleted": false, + "id": "pYd8BNqe32DQ1BK15gl1X", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1607.7500610351565, + "y": 911.2331604003898, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 2.2737367544323206e-13, + "height": 1207.7780151367188, + "seed": 1889947117, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533932559, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -2.2737367544323206e-13, + 1207.7780151367188 + ] + ] + }, + { + "type": "text", + "version": 58, + "versionNonce": 2111671781, + "isDeleted": false, + "id": "-EA8TtLR5unZFdjT-k9mq", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1404.4167785644531, + "y": 1492.6665802001953, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 1174452173, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "text", + "version": 225, + "versionNonce": 49704683, + "isDeleted": false, + "id": "al3s0Ce-XZ_FiU84jmv0f", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 729.2799504597981, + "y": 1209.9554656982423, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 171, + "height": 19, + "seed": 1330045933, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModuleInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance" + }, + { + "type": "rectangle", + "version": 102, + "versionNonce": 555518277, + "isDeleted": false, + "id": "TxSoCCktw7hU7jU0cN2he", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 723.0834655761719, + "y": 1240.9998931884766, + "strokeColor": "#d9480f", + "backgroundColor": "transparent", + "width": 210.66668701171875, + "height": 305.33334350585943, + "seed": 598232163, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 113, + "versionNonce": 1941920139, + "isDeleted": false, + "id": "uyhu5MiXRFgQansSAKgbz", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1858.638671875, + "y": 1857.11110941569, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 229.3333740234375, + "height": 71.33331298828125, + "seed": 1064313101, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "4W7-B8UsG2Kp0-6eEN0-h" + }, + { + "id": "CUEfVWpVIuIHc_5h3VskN", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 67, + "versionNonce": 1873089421, + "isDeleted": false, + "id": "4W7-B8UsG2Kp0-6eEN0-h", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1959.8053588867188, + "y": 1883.1777659098307, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 27, + "height": 20, + "seed": 1206387267, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247203, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[..]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uyhu5MiXRFgQansSAKgbz", + "originalText": "[..]" + }, + { + "type": "arrow", + "version": 335, + "versionNonce": 1028780075, + "isDeleted": false, + "id": "0dTwoTnwCJUbyz3e0bm1O", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 780.7038274166374, + "y": 1329.8562414550568, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 227.1615634556406, + "height": 49.895659740248675, + "seed": 1282951939, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "startBinding": { + "elementId": "_eiwTSAQSXB8P2k-PDhNa", + "focus": -0.16162303890895813, + "gap": 1.5411549516792498 + }, + "endBinding": { + "elementId": "SWgPVXvgjtN7AVlTfBUDR", + "focus": 1.0022214255263049, + "gap": 1.3844565398313762 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 227.1615634556406, + -49.895659740248675 + ] + ] + }, + { + "type": "rectangle", + "version": 380, + "versionNonce": 1831123973, + "isDeleted": false, + "id": "EjfvS52mTJV_ntE7FAmqi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 749.41650390625, + "y": 1265.500015258789, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 163, + "height": 38.333343505859375, + "seed": 908455875, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "0dTwoTnwCJUbyz3e0bm1O", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 372, + "versionNonce": 395899749, + "isDeleted": false, + "id": "0n8DknkuWXlRynFJmYm-N", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 760.0831298828125, + "y": 1276.500015258789, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1140391779, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "dA4sHNYw9NqC0yRSl1ByC", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 131, + "versionNonce": 169804459, + "isDeleted": false, + "id": "xwVumJFL4-d-8BM7nSKzG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1406.5276896158855, + "y": 1849.9999745686848, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 25, + "height": 20, + "seed": 813534477, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[n]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[n]" + }, + { + "type": "arrow", + "version": 164, + "versionNonce": 1833439621, + "isDeleted": false, + "id": "dA4sHNYw9NqC0yRSl1ByC", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 767.0830841064453, + "y": 1278.6665496826172, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 617.3333435058594, + "height": 168.00003051757812, + "seed": 1360269091, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "startBinding": { + "elementId": "0n8DknkuWXlRynFJmYm-N", + "focus": -0.6970200254594874, + "gap": 1 + }, + "endBinding": { + "elementId": "IK-a-uPI373j3VM1YHdKy", + "focus": 0.91934006004951, + "gap": 5.7502593994140625 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 283.33331298828125, + -86.66671752929688 + ], + [ + 617.3333435058594, + -168.00003051757812 + ] + ] + }, + { + "type": "text", + "version": 370, + "versionNonce": 2015327563, + "isDeleted": false, + "id": "1uhr4l-w9t4eVo0gQ06SH", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1628.0831146240234, + "y": 1047.333236694336, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 60, + "height": 20, + "seed": 2127932547, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "70jp9eV1jV2_kUBbN055m", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(void *)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(void *)" + }, + { + "type": "diamond", + "version": 110, + "versionNonce": 867539173, + "isDeleted": false, + "id": "OpV38MM8YOexWG_e-5_hX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1751.5831604003906, + "y": 1244.7915496826172, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 8.333282470703125, + "height": 13.75, + "seed": 1876974509, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "l4S1IXvHmVx_wl8DPQXk3", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 201, + "versionNonce": 1052283883, + "isDeleted": false, + "id": "0VYDhNUTpNaSbemsP54Zy", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 982.6778793334961, + "y": 1174.3998931884767, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 68, + "height": 20, + "seed": 2080172771, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(void **)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(void **)" + }, + { + "type": "text", + "version": 52, + "versionNonce": 364590149, + "isDeleted": false, + "id": "mEKNbQ7XuwA2N2CRqO86h", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1395.7501068115234, + "y": 1178.6665954589844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 43, + "height": 20, + "seed": 95270211, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "NULL", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "NULL" + }, + { + "type": "rectangle", + "version": 348, + "versionNonce": 306207333, + "isDeleted": false, + "id": "FKonkUbaqKFXKyt5VSiGE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 743.0831654866537, + "y": 1476.5000508626301, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174.66668701171875, + "height": 31.666625976562507, + "seed": 710825357, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "0dTwoTnwCJUbyz3e0bm1O", + "type": "arrow" + } + ], + "updated": 1679533956228, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 376, + "versionNonce": 1951705707, + "isDeleted": false, + "id": "xNodTFHQFtS6jmRfbZDpo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 748.4164784749349, + "y": 1484.1667073567708, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1417026541, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533956228, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 82, + "versionNonce": 678544837, + "isDeleted": false, + "id": "8i8vmtgsTLeEQt_E_hLrk", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 768.4165089925131, + "y": 1481.0000508626301, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 149, + "height": 20, + "seed": 1944091203, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "yu3un9i0kAGrZ8bAnVMa3", + "type": "arrow" + } + ], + "updated": 1679533956228, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_type_indexes", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func_type_indexes" + }, + { + "type": "rectangle", + "version": 210, + "versionNonce": 1441855435, + "isDeleted": false, + "id": "IwrJYgHbhcHGM-MPt77v3", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 907.3989664713541, + "y": 1609.9723409016926, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 919532995, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 230, + "versionNonce": 2006154853, + "isDeleted": false, + "id": "XRMQmYFkm-FtbS8PBuF_X", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 907.0655924479166, + "y": 1643.1389668782551, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 361842019, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 218, + "versionNonce": 473286251, + "isDeleted": false, + "id": "nsMfEXFd6IeqB7fL4ngUa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 906.7322794596354, + "y": 1676.4723103841145, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1923653891, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 227, + "versionNonce": 1069574597, + "isDeleted": false, + "id": "R5ioSAhKEdUlCurXbrHuN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 905.3989054361979, + "y": 1704.1389668782551, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1469286563, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 323, + "versionNonce": 617227531, + "isDeleted": false, + "id": "yu3un9i0kAGrZ8bAnVMa3", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 753.0831654866537, + "y": 1487.6672379829788, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 200.16657758128372, + "height": 153.3329247774377, + "seed": 1439562723, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533956229, + "link": null, + "locked": false, + "startBinding": { + "elementId": "8i8vmtgsTLeEQt_E_hLrk", + "focus": 1.0014597510870404, + "gap": 15.333343505859375 + }, + "endBinding": { + "elementId": "p5TPteQC3PraRMJtt4XsT", + "focus": 0.36261877029011863, + "gap": 3.9746450546211918 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -60.000050862630246, + 26.332884087333696 + ], + [ + -40.88889567057288, + 105.11070594605758 + ], + [ + 62.88881429036462, + 153.3329247774377 + ], + [ + 140.16652671865347, + 150.58490939994954 + ] + ] + }, + { + "type": "text", + "version": 99, + "versionNonce": 2113464613, + "isDeleted": false, + "id": "a8gv4R6T3PYjKJhay9vMv", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 802.4165191650391, + "y": 1604.7780354817708, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 70, + "height": 20, + "seed": 453646061, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint32 *", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "uint32 *" + }, + { + "type": "text", + "version": 97, + "versionNonce": 692152235, + "isDeleted": false, + "id": "eNJLe1mhF_AWRUyt9lO6k", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2227.083185195923, + "y": 955.9999694824219, + "strokeColor": "#1864ab", + "backgroundColor": "transparent", + "width": 96, + "height": 19, + "seed": 1711184323, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModule", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule" + }, + { + "type": "rectangle", + "version": 247, + "versionNonce": 329940101, + "isDeleted": false, + "id": "25KRboddeZem953trnn-R", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2216.416498184204, + "y": 985.9999389648438, + "strokeColor": "#1864ab", + "backgroundColor": "transparent", + "width": 308, + "height": 568, + "seed": 847170787, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 135, + "versionNonce": 799881803, + "isDeleted": false, + "id": "NnvrV9DcfaaiOCGPUdP78", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2229.083246231079, + "y": 1003.3333129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 263, + "height": 224, + "seed": 2117157197, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": " WASMImport *import_tables;\n WASMImport *import_memories;\n WASMImport *import_globals;\n\n WASMType **types;\n WASMImport *imports;\n WASMTable *tables;\n WASMMemory *memories;\n WASMGlobal *globals;\n WASMExport *exports;\n WASMTableSeg *table_segments;\n WASMDataSeg **data_segments;", + "baseline": 220, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " WASMImport *import_tables;\n WASMImport *import_memories;\n WASMImport *import_globals;\n\n WASMType **types;\n WASMImport *imports;\n WASMTable *tables;\n WASMMemory *memories;\n WASMGlobal *globals;\n WASMExport *exports;\n WASMTableSeg *table_segments;\n WASMDataSeg **data_segments;" + }, + { + "type": "rectangle", + "version": 123, + "versionNonce": 1486005221, + "isDeleted": false, + "id": "O5y5oOsgUB5ue4vWSQB0i", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2232.4165592193604, + "y": 1301.3332824707031, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 273.3333740234375, + "height": 44, + "seed": 1988414157, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 145, + "versionNonce": 709212395, + "isDeleted": false, + "id": "6xufIeHeKpvDChXCTeKSb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2294.4165592193604, + "y": 1314.6665954589844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 198, + "height": 19, + "seed": 2026661485, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMFunction **functions;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunction **functions;" + }, + { + "type": "diamond", + "version": 108, + "versionNonce": 1354686277, + "isDeleted": false, + "id": "s4lQvHIN1eAAubp7DkNrk", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2245.083246231079, + "y": 1318, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 26.6666259765625, + "height": 19.333343505859375, + "seed": 1489582509, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Bnf72M4RGMZjNgBlzk90B", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 354, + "versionNonce": 1184195467, + "isDeleted": false, + "id": "Bnf72M4RGMZjNgBlzk90B", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2250.416437149048, + "y": 1332.0000305175781, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 163.1120623945253, + "height": 15.460472501937602, + "seed": 1550987139, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "startBinding": { + "elementId": "s4lQvHIN1eAAubp7DkNrk", + "focus": -0.36983487209914556, + "gap": 1 + }, + "endBinding": { + "elementId": "swt6lb3ztkUJAvM41XHBK", + "focus": -0.7544161254824635, + "gap": 5.555002272222737 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -163.1120623945253, + 15.460472501937602 + ] + ] + }, + { + "type": "text", + "version": 138, + "versionNonce": 579645093, + "isDeleted": false, + "id": "TfktbM2ODt0uHXCbkJtbX", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2277.4166202545166, + "y": 1269.3333435058594, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 229, + "height": 19, + "seed": 2140950979, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMImport *import_functions;", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMImport *import_functions;" + }, + { + "type": "rectangle", + "version": 99, + "versionNonce": 483834411, + "isDeleted": false, + "id": "sIxEmRk_EPaTumCGaxM6t", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2231.749994277954, + "y": 1254.6667175292969, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 276, + "height": 42.6666259765625, + "seed": 2140310499, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 86, + "versionNonce": 1286181381, + "isDeleted": false, + "id": "_er3JaiUwljITdxfog0w_", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2246.416742324829, + "y": 1265.3334045410156, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 15.33331298828125, + "height": 22.66668701171875, + "seed": 568056877, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 453, + "versionNonce": 1509178571, + "isDeleted": false, + "id": "70jp9eV1jV2_kUBbN055m", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2253.0103282895525, + "y": 1275.9391811320388, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 323.49287390894006, + "height": 187.53914675447618, + "seed": 638625069, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "startBinding": { + "elementId": "JWlL3nHzTP4pxrEVYolFx", + "focus": -1.0762119763220488, + "gap": 30.60580710860131 + }, + "endBinding": { + "elementId": "1rZk-xFL82XSp5Mpcqy4f", + "focus": -1.2708836305762516, + "gap": 14.434116596798958 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -146.59352492956714, + -83.93927268477319 + ], + [ + -323.49287390894006, + -187.53914675447618 + ] + ] + }, + { + "type": "rectangle", + "version": 145, + "versionNonce": 471009637, + "isDeleted": false, + "id": "4BwxH9WndrjsXga95YTvm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1705.7500858306885, + "y": 1086.6665954589844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 222.6666259765625, + "height": 303.33331298828125, + "seed": 803769283, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "70jp9eV1jV2_kUBbN055m", + "type": "arrow" + }, + { + "id": "nUF7GyfmAGZN3iZvBfYtq", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 270, + "versionNonce": 696806251, + "isDeleted": false, + "id": "RR6mIlX4wkxcfcv6RQ8Wa", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1708.416711807251, + "y": 1062.6665954589844, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 97, + "height": 19, + "seed": 2042686211, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 2, + "text": "WASMImport", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMImport" + }, + { + "type": "rectangle", + "version": 191, + "versionNonce": 1645647045, + "isDeleted": false, + "id": "1rZk-xFL82XSp5Mpcqy4f", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1745.749963760376, + "y": 1097.3333129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 169.3333740234375, + "height": 31, + "seed": 176484109, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "qkiSeaixB8ivmpNxhvY6l" + }, + { + "id": "70jp9eV1jV2_kUBbN055m", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 162, + "versionNonce": 395409347, + "isDeleted": false, + "id": "qkiSeaixB8ivmpNxhvY6l", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1750.749963760376, + "y": 1102.3333129882812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 79, + "height": 20, + "seed": 2146735565, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247208, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint8 kind", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "middle", + "containerId": "1rZk-xFL82XSp5Mpcqy4f", + "originalText": "uint8 kind" + }, + { + "type": "rectangle", + "version": 175, + "versionNonce": 1783739429, + "isDeleted": false, + "id": "R_f4RWhd20C8JXmaJofMm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1706.7500247955322, + "y": 1390.6665954589844, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 221, + "height": 31, + "seed": 691699363, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "jXOu2RGl7KTvcY3-hwIj2" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 56, + "versionNonce": 2117409261, + "isDeleted": false, + "id": "jXOu2RGl7KTvcY3-hwIj2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1806.2500247955322, + "y": 1396.0665954589845, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 22, + "height": 20, + "seed": 776900557, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247209, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[1]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "R_f4RWhd20C8JXmaJofMm", + "originalText": "[1]" + }, + { + "type": "text", + "version": 51, + "versionNonce": 816965509, + "isDeleted": false, + "id": "JvLqnDwgx42bo19rINn18", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1807.083490371704, + "y": 1371.9998474121094, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 2020174093, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 185, + "versionNonce": 1499856715, + "isDeleted": false, + "id": "Xj5n84LklLY7rIUbMpV30", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1708.2501163482666, + "y": 1422.3332061767578, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 221, + "height": 31, + "seed": 1499618403, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "LOWTAqc1KaVYNxnLkXHB4" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 61, + "versionNonce": 2125694819, + "isDeleted": false, + "id": "LOWTAqc1KaVYNxnLkXHB4", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1803.2501163482666, + "y": 1427.733206176758, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 20, + "seed": 701124429, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247210, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Xj5n84LklLY7rIUbMpV30", + "originalText": "[...]" + }, + { + "type": "rectangle", + "version": 50, + "versionNonce": 178118123, + "isDeleted": false, + "id": "qd-T97QOO_xa4SGTgkrAj", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1363.083200454712, + "y": 1076.0001373291016, + "strokeColor": "#d9480f", + "backgroundColor": "transparent", + "width": 96, + "height": 170.66668701171875, + "seed": 215655011, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "AuwWYqGK5XChc2C2ZDCOd", + "type": "arrow" + } + ], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 169, + "versionNonce": 1562872389, + "isDeleted": false, + "id": "EAEAQzFaB6T9-rCB0X-61", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2303.749662399292, + "y": 1418.0001983642578, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 20, + "seed": 1651418499, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "fast_jit_func_ptrs", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "fast_jit_func_ptrs" + }, + { + "type": "rectangle", + "version": 195, + "versionNonce": 312535179, + "isDeleted": false, + "id": "n3LUTZSRU6GJ4nfF98WsH", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2233.7496013641357, + "y": 1409.6669158935547, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 273.3333740234375, + "height": 44, + "seed": 1123016931, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 173, + "versionNonce": 312998309, + "isDeleted": false, + "id": "SOlRVdTv-nHYKomLm0-Lr", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2246.749662399292, + "y": 1421.000244140625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 26.6666259765625, + "height": 19.333343505859375, + "seed": 381478531, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929333, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 61, + "versionNonce": 1193910059, + "isDeleted": false, + "id": "RckpORzYftIA2k4VSkTaW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2093.082914352417, + "y": 1731.3334503173828, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 314.6666564941406, + "height": 191.3333740234375, + "seed": 1221862445, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": { + "elementId": "sYSDwbrpZl0692xpkcG4z", + "focus": -0.8132904069013918, + "gap": 6.05506706237793 + }, + "endBinding": { + "elementId": "bu6GnR96DeCSFWu3DhMIJ", + "focus": -1.5131314965155, + "gap": 13.30013732910163 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 150.66665649414062, + 128.66668701171875 + ], + [ + 314.6666564941406, + 191.3333740234375 + ] + ] + }, + { + "type": "arrow", + "version": 66, + "versionNonce": 1361280261, + "isDeleted": false, + "id": "HCvGV6j45DG_BGeI3J7ut", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2116.4162578582764, + "y": 1692.6667938232422, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 288.25032234191895, + "height": 141.39862277829707, + "seed": 298119629, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": { + "elementId": "SiZHwNA9YKd93iwc74wxB", + "focus": -0.7153529191190878, + "gap": 5.633312988281318 + }, + "endBinding": { + "elementId": "qVsRlSPxTl9a8XrnRMl2a", + "focus": -0.10195339456743455, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 125.33331298828125, + 108.66665649414062 + ], + [ + 288.25032234191895, + 141.39862277829707 + ] + ] + }, + { + "type": "arrow", + "version": 81, + "versionNonce": 1654817227, + "isDeleted": false, + "id": "iqe6U9xOE6ECb18moJnw-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2083.0828227996826, + "y": 1640.0001068115234, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 380, + "height": 114.66668701171875, + "seed": 1655019469, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "qGjmy62MZbL7zALsNU2dL", + "focus": 0.8815432234830397, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 202.66668701171875, + 114.66668701171875 + ], + [ + 380, + 64 + ] + ] + }, + { + "type": "rectangle", + "version": 196, + "versionNonce": 1880593509, + "isDeleted": false, + "id": "pWu_lwlXIT6mrZDyT6QkK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2690.0828075408936, + "y": 1522.750244140625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 162247299, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "NSz4yfxdToa5c5At8YYeR", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 181, + "versionNonce": 1847988331, + "isDeleted": false, + "id": "rBPIesLQ3_hwRCCEZDo_K", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2714.0828075408936, + "y": 1530.4169006347656, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 2113989421, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 216, + "versionNonce": 2068955077, + "isDeleted": false, + "id": "1nU3Tx2BdlitDqcIjhR6O", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2689.749433517456, + "y": 1555.9168701171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 255355427, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 202, + "versionNonce": 1803471627, + "isDeleted": false, + "id": "qPuFVkGJxscoxDIlxjxO7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2713.749433517456, + "y": 1563.5835266113281, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1853160845, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 205, + "versionNonce": 686784293, + "isDeleted": false, + "id": "ijtdWkKxrTh4Pnlp14iE0", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2689.416120529175, + "y": 1589.2502136230469, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 495791555, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "BF2h7Ub5gAf2yYxLfQSFh", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 215, + "versionNonce": 1059533227, + "isDeleted": false, + "id": "jFcNDR-eUAEW7pedWkAdo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2688.0827465057373, + "y": 1616.9168701171875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 796378093, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 196, + "versionNonce": 257839749, + "isDeleted": false, + "id": "q4AkedYi9svz1WOMHGyiP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2710.749433517456, + "y": 1625.2502136230469, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 2009628003, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 195, + "versionNonce": 1514783819, + "isDeleted": false, + "id": "NSz4yfxdToa5c5At8YYeR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2491.7495250701904, + "y": 1437.3335571289062, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 199.43811857564833, + "height": 81.5190719332436, + "seed": 909964067, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "pWu_lwlXIT6mrZDyT6QkK", + "focus": 0.37183947794184286, + "gap": 3.8976150784751553 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 199.43811857564833, + 81.5190719332436 + ] + ] + }, + { + "type": "arrow", + "version": 222, + "versionNonce": 458996197, + "isDeleted": false, + "id": "BF2h7Ub5gAf2yYxLfQSFh", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2718.826031777619, + "y": 1626.7287320369785, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 207.0765067074285, + "height": 202.09118637438291, + "seed": 358411853, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": { + "elementId": "D0065Oo1uIjEqNG6iDJjn", + "focus": -2.935287320516118, + "gap": 14.961736017351072 + }, + "endBinding": { + "elementId": "qVsRlSPxTl9a8XrnRMl2a", + "focus": 0.3455990175110858, + "gap": 2.749570846557617 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -84.40981969570976, + 140.60479457434963 + ], + [ + -207.0765067074285, + 202.09118637438291 + ] + ] + }, + { + "type": "diamond", + "version": 207, + "versionNonce": 616628971, + "isDeleted": false, + "id": "D0065Oo1uIjEqNG6iDJjn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2712.0828075408936, + "y": 1594.1669158935547, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1454945635, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "BF2h7Ub5gAf2yYxLfQSFh", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 61, + "versionNonce": 1173697861, + "isDeleted": false, + "id": "OI5mleCPF3WYtCzvosMk1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2661.7495861053467, + "y": 1495.3336181640625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 177.33334350585938, + "seed": 617283619, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 81, + "versionNonce": 655846795, + "isDeleted": false, + "id": "4Q_PdspTfAcKBYqfwEOrl", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2534.4162731170654, + "y": 1478.0003051757812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 68, + "height": 20, + "seed": 468048995, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(void **)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(void **)" + }, + { + "type": "text", + "version": 215, + "versionNonce": 176702629, + "isDeleted": false, + "id": "zEBN6RuZylHRtnzhMAsfm", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2302.082899093628, + "y": 1367.6669006347656, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 80, + "height": 20, + "seed": 1726260589, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_ptrs", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "func_ptrs" + }, + { + "type": "rectangle", + "version": 240, + "versionNonce": 1955038251, + "isDeleted": false, + "id": "Q1RbJ8FG5PuoKZ0jGW-AG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2232.0828380584717, + "y": 1359.3336181640625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 273.3333740234375, + "height": 44, + "seed": 1664381923, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 218, + "versionNonce": 482502661, + "isDeleted": false, + "id": "lKOq2jf5D0WXwfNx9mwVb", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2245.082899093628, + "y": 1370.6669464111328, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 26.6666259765625, + "height": 19.333343505859375, + "seed": 1869049805, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 197, + "versionNonce": 1425786571, + "isDeleted": false, + "id": "5_VLE0dE94H4gwxL9iUpC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2851.4160289764404, + "y": 1432.750228881836, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1330766563, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "8jHbZMq4zvKK9AxPUw9Qf", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 181, + "versionNonce": 1587617637, + "isDeleted": false, + "id": "k1p6Nabm5uz-_RQk8vKUy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2875.4160289764404, + "y": 1440.4168853759766, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1901883597, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 216, + "versionNonce": 789493099, + "isDeleted": false, + "id": "BH3ZK-RnoIcnosroWZ9_J", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2851.082654953003, + "y": 1465.9168548583984, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 897158787, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 202, + "versionNonce": 376564421, + "isDeleted": false, + "id": "cAmZfC7YbkEqrmByhjjHI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2875.082654953003, + "y": 1473.583511352539, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 596561709, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 205, + "versionNonce": 839569419, + "isDeleted": false, + "id": "yAeZ1rNDP97dzjt5edJvJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2850.7493419647217, + "y": 1499.2501983642578, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1022019107, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 215, + "versionNonce": 1691503141, + "isDeleted": false, + "id": "h_AbFbWb_N0KfOV68sJf6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2849.415967941284, + "y": 1526.9168548583984, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1889952141, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 196, + "versionNonce": 1072338603, + "isDeleted": false, + "id": "OKOuC260x6EDIVInqBKer", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2872.082654953003, + "y": 1535.2501983642578, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 479463875, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 208, + "versionNonce": 2058337669, + "isDeleted": false, + "id": "zQXps3l6_CULfrKz0sxFV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2873.4160289764404, + "y": 1504.1669006347656, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 769435629, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "4E9MghnYo6hn8E82pmPMe", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 61, + "versionNonce": 391678283, + "isDeleted": false, + "id": "4EdLS1fcNDOsz52Mf7ATN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2823.0828075408936, + "y": 1405.3336029052734, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 177.33334350585938, + "seed": 697555299, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 108, + "versionNonce": 256361701, + "isDeleted": false, + "id": "8jHbZMq4zvKK9AxPUw9Qf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2495.082899093628, + "y": 1376.0003051757812, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 355.3333740234375, + "height": 55.999969482421875, + "seed": 424364771, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "5_VLE0dE94H4gwxL9iUpC", + "focus": 0.6186308498480104, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 355.3333740234375, + 55.999969482421875 + ] + ] + }, + { + "type": "text", + "version": 44, + "versionNonce": 1034889195, + "isDeleted": false, + "id": "PamsTN-BPmxTRCvWvz1kZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2636.5496044158936, + "y": 1377.7335876464845, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 68, + "height": 20, + "seed": 508079075, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(void **)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(void **)" + }, + { + "type": "arrow", + "version": 101, + "versionNonce": 371712069, + "isDeleted": false, + "id": "4E9MghnYo6hn8E82pmPMe", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2877.7495250701904, + "y": 1546.6669921875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 317.33331298828125, + "height": 370, + "seed": 625293229, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": { + "elementId": "zQXps3l6_CULfrKz0sxFV", + "focus": -3.2298254921305025, + "gap": 13.691591814926234 + }, + "endBinding": { + "elementId": "Z9cJSNSjDvW2uPNthdOW9", + "focus": 0.3950527937625778, + "gap": 3.582883834838867 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -144, + 266 + ], + [ + -317.33331298828125, + 370 + ] + ] + }, + { + "type": "rectangle", + "version": 219, + "versionNonce": 687403659, + "isDeleted": false, + "id": "swt6lb3ztkUJAvM41XHBK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2033.7493724822998, + "y": 1348.4170532226562, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 177857187, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Bnf72M4RGMZjNgBlzk90B", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 203, + "versionNonce": 892178341, + "isDeleted": false, + "id": "MlaoCVMw-5S2Yi8P1HjDR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2057.7493724823, + "y": 1356.0837097167969, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1852132621, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 238, + "versionNonce": 703621419, + "isDeleted": false, + "id": "TiGonLf-juzwJEoH9VSZD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2033.4159984588623, + "y": 1381.5836791992188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 254514755, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 224, + "versionNonce": 2055623429, + "isDeleted": false, + "id": "3YRJV9k9ywr0i4pEZkHkN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2057.4159984588623, + "y": 1389.2503356933594, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 2075272045, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 227, + "versionNonce": 369101771, + "isDeleted": false, + "id": "33dfOgt2KTLk3Q5NBImFr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2033.082685470581, + "y": 1414.9170227050781, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1787155939, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 237, + "versionNonce": 1387244133, + "isDeleted": false, + "id": "92FFROdmhjPmxEpXFFQ5M", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2031.7493114471436, + "y": 1442.5836791992188, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1238707661, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 218, + "versionNonce": 826432107, + "isDeleted": false, + "id": "QULGDwKUKXLejiOFZCB88", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2054.4159984588623, + "y": 1450.9170227050781, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1270292867, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 230, + "versionNonce": 1047991749, + "isDeleted": false, + "id": "-dLa28cPs8d1YMU273D-q", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2055.7493724823, + "y": 1419.833724975586, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1206381613, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "uC3ZSm-IltHDllxDGLJ9v", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 83, + "versionNonce": 1484903691, + "isDeleted": false, + "id": "diotVPud8Nk4qCzgu2hJi", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2005.416151046753, + "y": 1321.0004272460938, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 177.33334350585938, + "seed": 2080241955, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 89, + "versionNonce": 1224348965, + "isDeleted": false, + "id": "uC3ZSm-IltHDllxDGLJ9v", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2061.749616622925, + "y": 1459.3337860107422, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 103.33331298828125, + "height": 104.66668701171875, + "seed": 687885357, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": { + "elementId": "-dLa28cPs8d1YMU273D-q", + "focus": 3.424460294999428, + "gap": 11.855942213284369 + }, + "endBinding": { + "elementId": "0kWlc6iPzGzhrfIVEPeOM", + "focus": 0.1363752482649436, + "gap": 1.5828227996826172 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 103.33331298828125, + 64.66668701171875 + ], + [ + 67.3333740234375, + 104.66668701171875 + ] + ] + }, + { + "type": "text", + "version": 107, + "versionNonce": 1798896555, + "isDeleted": false, + "id": "Ba0bmo-eQQnhNNi6zcCzg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2259.0828075408936, + "y": 1502.6670684814453, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 177, + "height": 40, + "seed": 1174246765, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": " uint8 *load_addr;\n uint64 load_size;", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": " uint8 *load_addr;\n uint64 load_size;" + }, + { + "type": "rectangle", + "version": 43, + "versionNonce": 1034299525, + "isDeleted": false, + "id": "SOEOh6pxx-gC9L5EGGFZb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2247.082929611206, + "y": 1502.6670684814453, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 256, + "height": 38, + "seed": 1478394307, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 17, + "versionNonce": 1972522571, + "isDeleted": false, + "id": "KcLwCwuZF-_XQTvbSOePf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2267.7495555877686, + "y": 1506.6670684814453, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 17.3333740234375, + "height": 13.333343505859375, + "seed": 1278561005, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 58, + "versionNonce": 507039717, + "isDeleted": false, + "id": "f7JIg_N3m3yBHDkQvlpUn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2275.7495555877686, + "y": 1510.6670684814453, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 63.33331298828125, + "height": 94.00003051757812, + "seed": 646153155, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -63.33331298828125, + 70 + ], + [ + -19.33331298828125, + 94.00003051757812 + ] + ] + }, + { + "type": "text", + "version": 331, + "versionNonce": 443043051, + "isDeleted": false, + "id": "epVvbDyPF40MaERFnDDJy", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1858.9132976531982, + "y": 1828.5115061442057, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 238, + "height": 20, + "seed": 2133258755, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "CUEfVWpVIuIHc_5h3VskN", + "type": "arrow" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMFunction (second module)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMFunction (second module)" + }, + { + "type": "text", + "version": 36, + "versionNonce": 1488948037, + "isDeleted": false, + "id": "iSNS4LqcpEqsBWT3JrhTG", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1985.4163494110107, + "y": 1302.0003509521484, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 176, + "height": 20, + "seed": 895067437, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::functions", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::functions" + }, + { + "type": "text", + "version": 58, + "versionNonce": 1027962763, + "isDeleted": false, + "id": "zzptLpjImiiG1vPCDIGPS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2788.5657176971436, + "y": 1386.4003204345704, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 185, + "height": 20, + "seed": 510558371, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::func_ptrs", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::func_ptrs" + }, + { + "type": "text", + "version": 84, + "versionNonce": 1033362085, + "isDeleted": false, + "id": "QPiZaCnPYGIz7ApGqrktI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 2629.160482406616, + "y": 1451.7336334228517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 40, + "seed": 1301033645, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModule::\nfast_jit_func_ptrs", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModule::\nfast_jit_func_ptrs" + }, + { + "type": "rectangle", + "version": 444, + "versionNonce": 1818512939, + "isDeleted": false, + "id": "bGS26pMiud1SV_QZasA4k", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 753.6687545776367, + "y": 1354.399984741211, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 140.66668701171875, + "height": 32.33331298828126, + "seed": 1104978987, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "E9Lf0GGwiMXE7OEKmLBD0" + } + ], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 223, + "versionNonce": 498422861, + "isDeleted": false, + "id": "E9Lf0GGwiMXE7OEKmLBD0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 784.0020980834961, + "y": 1360.0666412353517, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 80, + "height": 20, + "seed": 1389316101, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679537247214, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "func_ptrs", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "bGS26pMiud1SV_QZasA4k", + "originalText": "func_ptrs" + }, + { + "type": "diamond", + "version": 480, + "versionNonce": 179261643, + "isDeleted": false, + "id": "3V5jOnV9GBHrydrWDjdlN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 764.3353805541992, + "y": 1366.7332977294923, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 2058151627, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 64, + "versionNonce": 927151461, + "isDeleted": false, + "id": "Ej1XTEhuVY9wBpKERi-1L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 772.668815612793, + "y": 1274.0665191650392, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 140, + "height": 20, + "seed": 1144833349, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "import_func_ptrs", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "import_func_ptrs" + }, + { + "type": "rectangle", + "version": 521, + "versionNonce": 50715531, + "isDeleted": false, + "id": "OFyAqRR6a69cDApnQUNYF", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 735.2243474324545, + "y": 1412.3998321533204, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 185.3334045410156, + "height": 34.99996948242188, + "seed": 1243189643, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533960843, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 511, + "versionNonce": 2105521829, + "isDeleted": false, + "id": "Nv_mYV9MitkgQQoVIZJ9H", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 737.8910039265951, + "y": 1424.3998321533204, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1000679467, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533960843, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 85, + "versionNonce": 1685598763, + "isDeleted": false, + "id": "sB-lDN4LgjZtFuvGEHopU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 753.8910039265951, + "y": 1421.2331756591798, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 160, + "height": 20, + "seed": 967892165, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "WmAyzG_eOk5gu9ag2e4NN", + "type": "arrow" + } + ], + "updated": 1679533960843, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "fast_jit_func_ptrs", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "fast_jit_func_ptrs" + }, + { + "type": "rectangle", + "version": 247, + "versionNonce": 1175609515, + "isDeleted": false, + "id": "bEyCLX9k_ShKiQzS-ZFKc", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.6685104370117, + "y": 1862.1498245239259, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1755598347, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929334, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 231, + "versionNonce": 591500165, + "isDeleted": false, + "id": "mz6MyUFqd-cTtlX0HmAqX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 895.6685104370117, + "y": 1869.8164810180665, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 488090661, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 266, + "versionNonce": 1509364555, + "isDeleted": false, + "id": "i6UWgN6SrSs9asopho9X_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.3351364135742, + "y": 1895.3164505004884, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 581194923, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 252, + "versionNonce": 858708709, + "isDeleted": false, + "id": "OxO-Lw3MtI3B3_yxI_9BQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 895.3351364135742, + "y": 1902.983106994629, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 133750661, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 255, + "versionNonce": 2146202091, + "isDeleted": false, + "id": "Av0IXtuoDwPzsT4d4ZD9r", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.001823425293, + "y": 1928.6497940063477, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 942311243, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 265, + "versionNonce": 414987845, + "isDeleted": false, + "id": "zsHPiP1O3kL8Jo_YTK1SB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 869.6684494018555, + "y": 1956.3164505004884, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 454862565, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 246, + "versionNonce": 150797451, + "isDeleted": false, + "id": "QkKoENHz9noDS00xsmwGp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 892.3351364135742, + "y": 1964.6497940063477, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 798057963, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 258, + "versionNonce": 1321674149, + "isDeleted": false, + "id": "8h3LnYKSejC6NlmCDjN_T", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 893.6685104370117, + "y": 1933.5664962768556, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1494254149, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 127, + "versionNonce": 1469009605, + "isDeleted": false, + "id": "8FJ9nE4COwUuKYB0Fjze2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 843.3352890014648, + "y": 1851.0666336059571, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 160.99990844726562, + "seed": 1608741003, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "WmAyzG_eOk5gu9ag2e4NN", + "type": "arrow" + }, + { + "id": "Xr0h90XMpFNQFRcVNp-Qb", + "type": "arrow" + } + ], + "updated": 1679534054366, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 103, + "versionNonce": 410855685, + "isDeleted": false, + "id": "xkXUNjzkhjNlrNaWU9GPX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 826.6688613891602, + "y": 1811.3999618530274, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174, + "height": 40, + "seed": 380760485, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstance::\nfast_jit_func_ptrs", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance::\nfast_jit_func_ptrs" + }, + { + "type": "arrow", + "version": 98, + "versionNonce": 1677433349, + "isDeleted": false, + "id": "WmAyzG_eOk5gu9ag2e4NN", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 742.8910344441732, + "y": 1433.2344728128833, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 187.33331298828125, + "height": 431.9987943990309, + "seed": 471597803, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533960844, + "link": null, + "locked": false, + "startBinding": { + "elementId": "sB-lDN4LgjZtFuvGEHopU", + "focus": 1.017117824752581, + "gap": 10.999969482421875 + }, + "endBinding": { + "elementId": "8FJ9nE4COwUuKYB0Fjze2", + "focus": 0.09986159259017957, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -87.5555318196615, + 110.66535934043713 + ], + [ + -41.555531819661496, + 281.99876388145276 + ], + [ + 99.77778116861975, + 431.9987943990309 + ] + ] + }, + { + "type": "rectangle", + "version": 261, + "versionNonce": 227189867, + "isDeleted": false, + "id": "AXS2auzqFNZS35F2ixu8Z", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1039.001808166504, + "y": 1963.98309173584, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1019623493, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 245, + "versionNonce": 1630563269, + "isDeleted": false, + "id": "cjLfuQX8dFdgByjdkn0sY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1063.001808166504, + "y": 1971.6497482299806, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 67902091, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 280, + "versionNonce": 1496375051, + "isDeleted": false, + "id": "O8ko0NdUbnEnqQSXbDcTn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1038.6684341430664, + "y": 1997.1497177124024, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 641787813, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 266, + "versionNonce": 907586341, + "isDeleted": false, + "id": "MK_yFDygTjEm7c4A1RI_A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1062.6684341430664, + "y": 2004.816374206543, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 140415275, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 269, + "versionNonce": 1433847211, + "isDeleted": false, + "id": "IpQhaQLL8LX1JsLCZTqHR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1038.3351211547852, + "y": 2030.4830612182618, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 527346437, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 279, + "versionNonce": 1988230789, + "isDeleted": false, + "id": "G_B8CuUKKojKnmDg-blQS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1037.0017471313477, + "y": 2058.1497177124024, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 48, + "height": 29, + "seed": 1416180683, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 261, + "versionNonce": 696703051, + "isDeleted": false, + "id": "bubXHX2zzv6QsRGmmBlvp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1059.6684341430664, + "y": 2066.483061218262, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 1758265957, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "kjpM2qWJqDrV-jr9XK8QR", + "type": "arrow" + } + ], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 272, + "versionNonce": 959047141, + "isDeleted": false, + "id": "p2Ps7ouZR4NFea5dxTpPY", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1061.001808166504, + "y": 2035.3997634887696, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 10.66668701171875, + "height": 17.666656494140625, + "seed": 407274091, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 142, + "versionNonce": 161780453, + "isDeleted": false, + "id": "PZQU4Sc5stYlZLaGSiYZg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1010.668586730957, + "y": 1952.8999008178712, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98.66668701171875, + "height": 160.99990844726562, + "seed": 1462332869, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "eXEUtswyqJzZHgjz8P_J5", + "type": "arrow" + }, + { + "id": "BcRoQEkrTOTzUxX-Uw8S8", + "type": "arrow" + } + ], + "updated": 1679534050516, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 119, + "versionNonce": 1629348165, + "isDeleted": false, + "id": "RolknO7AhmdfdQTNiXJ8F", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 994.0021591186523, + "y": 1913.2332290649415, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 174, + "height": 40, + "seed": 482409739, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "eXEUtswyqJzZHgjz8P_J5", + "type": "arrow" + } + ], + "updated": 1679533929335, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstance::\nfunc_ptrs", + "baseline": 34, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance::\nfunc_ptrs" + }, + { + "type": "rectangle", + "version": 222, + "versionNonce": 824148363, + "isDeleted": false, + "id": "p5TPteQC3PraRMJtt4XsT", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 897.2243372599283, + "y": 1585.9554707845052, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 74.666748046875, + "height": 160.99990844726562, + "seed": 727269189, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "yu3un9i0kAGrZ8bAnVMa3", + "type": "arrow" + } + ], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 444, + "versionNonce": 841297547, + "isDeleted": false, + "id": "eXEUtswyqJzZHgjz8P_J5", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 771.3355026245117, + "y": 1374.8998474121095, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 384.88897705078125, + "height": 633.6667022705078, + "seed": 539008875, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533948630, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "PZQU4Sc5stYlZLaGSiYZg", + "focus": 0.2318272147781851, + "gap": 3.3330230712890625 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -148.888916015625, + 184.1111602783203 + ], + [ + 15.555613199869754, + 612.4444732666016 + ], + [ + 236.00006103515625, + 633.6667022705078 + ] + ] + }, + { + "type": "arrow", + "version": 83, + "versionNonce": 745347115, + "isDeleted": false, + "id": "kjpM2qWJqDrV-jr9XK8QR", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1063.3354822794595, + "y": 2044.0108703613284, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 1343.333333333333, + "height": 143.33333333333326, + "seed": 738327429, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bubXHX2zzv6QsRGmmBlvp", + "focus": -3.5383188444895577, + "gap": 13.041657453315548 + }, + "endBinding": { + "elementId": "bu6GnR96DeCSFWu3DhMIJ", + "focus": -0.1414139865892621, + "gap": 14.399755859375318 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 790, + 23.888905843098883 + ], + [ + 1343.333333333333, + -119.44442749023438 + ] + ] + }, + { + "type": "text", + "version": 258, + "versionNonce": 1857033221, + "isDeleted": false, + "id": "VCjoZw1mwV4c94ES2JChm", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 708.2243372599285, + "y": 1804.0109212239581, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 68, + "height": 20, + "seed": 1329151115, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "(void **)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "(void **)" + }, + { + "type": "text", + "version": 317, + "versionNonce": 588176075, + "isDeleted": false, + "id": "8pDOO3W9GlOqpH8OfM3H_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1274.502098083496, + "y": 1674.5109720865876, + "strokeColor": "#e67700", + "backgroundColor": "transparent", + "width": 246, + "height": 19, + "seed": 73971563, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679533929335, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModuleInstance(second)", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance(second)" + }, + { + "type": "rectangle", + "version": 271, + "versionNonce": 2113489765, + "isDeleted": false, + "id": "Rcref7JZ-AhlcXLLdwY5D", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1264.668805440267, + "y": 1697.4554453531891, + "strokeColor": "#d9480f", + "backgroundColor": "transparent", + "width": 270.66663614908845, + "height": 194.22224934895837, + "seed": 635278027, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "MLVyGZQLa4jU554J6bsmJ", + "type": "arrow" + } + ], + "updated": 1679533929335, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 226, + "versionNonce": 1289066309, + "isDeleted": false, + "id": "CZNopRssr82fjSim4TRBD", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 601.1133956909175, + "y": 2080.3445597330715, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 335, + "height": 20, + "seed": 556524395, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679534038146, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "function pointer arrays for faster access", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "function pointer arrays for faster access" + }, + { + "type": "rectangle", + "version": 45, + "versionNonce": 815465317, + "isDeleted": false, + "id": "J2L3EeElp1XhI1X3IbanK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 578.8910547892249, + "y": 2055.122269694009, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 364.4444274902344, + "height": 58.33338419596339, + "seed": 1181100939, + "groupIds": [], + "roundness": { + "type": 3 + }, + "boundElements": [ + { + "id": "BcRoQEkrTOTzUxX-Uw8S8", + "type": "arrow" + }, + { + "id": "Xr0h90XMpFNQFRcVNp-Qb", + "type": "arrow" + } + ], + "updated": 1679534054366, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 33, + "versionNonce": 57793355, + "isDeleted": false, + "id": "BcRoQEkrTOTzUxX-Uw8S8", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 944.4466272989905, + "y": 2095.1223205566394, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 63.33333333333326, + "height": 19.444478352864735, + "seed": 132937093, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679534050516, + "link": null, + "locked": false, + "startBinding": { + "elementId": "J2L3EeElp1XhI1X3IbanK", + "focus": 0.788606211312463, + "gap": 1.11114501953125 + }, + "endBinding": { + "elementId": "PZQU4Sc5stYlZLaGSiYZg", + "focus": -0.2743956700481259, + "gap": 2.8886260986332672 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 63.33333333333326, + -19.444478352864735 + ] + ] + }, + { + "type": "arrow", + "version": 24, + "versionNonce": 2098532715, + "isDeleted": false, + "id": "Xr0h90XMpFNQFRcVNp-Qb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 786.6688156127926, + "y": 2054.011175537108, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55.555572509765625, + "height": 45.00000000000023, + "seed": 1053702635, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679534054366, + "link": null, + "locked": false, + "startBinding": { + "elementId": "J2L3EeElp1XhI1X3IbanK", + "focus": -0.054183297415777855, + "gap": 1.1110941569011175 + }, + "endBinding": { + "elementId": "8FJ9nE4COwUuKYB0Fjze2", + "focus": -0.3037089268567984, + "gap": 1.110900878906591 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 55.555572509765625, + -45.00000000000023 + ] + ] + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/core/iwasm/doc/images/wasm_function.svg b/core/iwasm/doc/images/wasm_function.svg new file mode 100644 index 0000000000..86fdf78ade --- /dev/null +++ b/core/iwasm/doc/images/wasm_function.svg @@ -0,0 +1,16 @@ + + + + + + + WASMFunctionInstancefunc_importWASMFunctionImport:funcWASMFunction (per module)internal function char *module_name; char *field_name;func_ptr_linkednative function:import_module;import_func_linked;(WASMFunction *)codebytecodecode_compiledprecompiled-bytecodefast_jit_jitted_codellvm_jit_func_ptrfast jitted coodellvm jitted coodeimport_module_inst;import_func_inst;functionsglobalseWASMModuleInstanceExtraWASMModuleInstance::import_func_ptrsthis is the one actually referred during executing opcodeduring model load, if import can be solved through the native api registeration,the pointer of native function will be filled.c-api could change the pointer later, then it will point to a different native functionNULL: means multi-module import, go to "import_func_inst" field for target functionWASMModuleInstance*(WASMFunctionInstance*)func_importfuncWASMFunction union[...][0]WASMModuleInstance[..][n](void *)(void **)NULLfunc_type_indexesuint32 *WASMModule WASMImport *import_tables; WASMImport *import_memories; WASMImport *import_globals; WASMType **types; WASMImport *imports; WASMTable *tables; WASMMemory *memories; WASMGlobal *globals; WASMExport *exports; WASMTableSeg *table_segments; WASMDataSeg **data_segments;WASMFunction **functions;WASMImport *import_functions;WASMImportuint8 kind[1][0][...]fast_jit_func_ptrs(void **)func_ptrs(void **) uint8 *load_addr; uint64 load_size;WASMFunction (second module)WASMModule::functionsWASMModule::func_ptrsWASMModule::fast_jit_func_ptrsfunc_ptrsimport_func_ptrsfast_jit_func_ptrsWASMModuleInstance::fast_jit_func_ptrsWASMModuleInstance::func_ptrs(void **)WASMModuleInstance(second)function pointer arrays for faster access \ No newline at end of file diff --git a/core/iwasm/doc/images/wasm_globals.excalidraw b/core/iwasm/doc/images/wasm_globals.excalidraw new file mode 100644 index 0000000000..9471535201 --- /dev/null +++ b/core/iwasm/doc/images/wasm_globals.excalidraw @@ -0,0 +1,2313 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://marketplace.visualstudio.com/items?itemName=pomdtr.excalidraw-editor", + "elements": [ + { + "type": "rectangle", + "version": 381, + "versionNonce": 2068900405, + "isDeleted": false, + "id": "D5Ay5TxydaAe4f80l_79L", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 982.833251953125, + "y": 298.00006103515625, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 99.666748046875, + "height": 211.00007629394523, + "seed": 1123866381, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "5S0qe2-BjQRPwuEEQ_UsU", + "type": "arrow" + } + ], + "updated": 1679660991439, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 317, + "versionNonce": 1880855285, + "isDeleted": false, + "id": "4JTzPDASWmnx1PVSabO3A", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 914.833251953125, + "y": 236.3333282470703, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 103, + "height": 20, + "seed": 422161475, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661135316, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "global_data:", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "global_data:" + }, + { + "type": "text", + "version": 183, + "versionNonce": 1437992789, + "isDeleted": false, + "id": "HQjUeFzLlihuH1Lf8XZQq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 528.1666870117188, + "y": 324.00001525878906, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 274, + "height": 20, + "seed": 2018585571, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661163124, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstanceExtra::globals", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstanceExtra::globals" + }, + { + "type": "arrow", + "version": 453, + "versionNonce": 92905717, + "isDeleted": false, + "id": "I86Qg5wzdmE77J5SbA7HP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 759.0470523835444, + "y": 472.4753157819668, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 223.44867715016005, + "height": 152.25013181880155, + "seed": 661894701, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "Qm7oDN7yJzVFQrCa0EE1G" + } + ], + "updated": 1679660991440, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "Sd34CflTFN9Elv1dedCI1", + "focus": 0.9595350520837825, + "gap": 4.004209431139316 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 223.44867715016005, + -152.25013181880155 + ] + ] + }, + { + "type": "text", + "version": 4, + "versionNonce": 2089047221, + "isDeleted": false, + "id": "Qm7oDN7yJzVFQrCa0EE1G", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 844.2713909586244, + "y": 386.350249872566, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 53, + "height": 20, + "seed": 1720249052, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661201718, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "offset", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "I86Qg5wzdmE77J5SbA7HP", + "originalText": "offset" + }, + { + "type": "rectangle", + "version": 213, + "versionNonce": 1454183483, + "isDeleted": false, + "id": "Sd34CflTFN9Elv1dedCI1", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 986.4999389648438, + "y": 315.6666564941406, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 91.66668701171875, + "height": 27.999999999999996, + "seed": 357984397, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "I86Qg5wzdmE77J5SbA7HP", + "type": "arrow" + } + ], + "updated": 1679660991440, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 192, + "versionNonce": 706008283, + "isDeleted": false, + "id": "X5nXXDxRcZputNDZp2WfW", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 984.4998779296875, + "y": 350.83333587646484, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 94.3333740234375, + "height": 19.333358764648438, + "seed": 1611395779, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "PFkOKGbMOcdhcpcv4DutQ", + "type": "arrow" + } + ], + "updated": 1679660991442, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 369, + "versionNonce": 1307818581, + "isDeleted": false, + "id": "PFkOKGbMOcdhcpcv4DutQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 753.8716651262948, + "y": 690.3334503173828, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 220.3341412661283, + "height": 341.60985534904495, + "seed": 1747362403, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "MPb6tVMlSBtnXhUTpZvIC" + } + ], + "updated": 1679660991442, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "X5nXXDxRcZputNDZp2WfW", + "gap": 5.794568769140314, + "focus": 1.2182487723741706 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 220.3341412661283, + -341.60985534904495 + ] + ] + }, + { + "type": "text", + "version": 4, + "versionNonce": 1008745755, + "isDeleted": false, + "id": "MPb6tVMlSBtnXhUTpZvIC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 837.538735759359, + "y": 509.52852264286037, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 53, + "height": 20, + "seed": 1880081892, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661201719, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "offset", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PFkOKGbMOcdhcpcv4DutQ", + "originalText": "offset" + }, + { + "type": "text", + "version": 429, + "versionNonce": 147921333, + "isDeleted": false, + "id": "5GnY6Vq9qPDS0ntZW3UOE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 917.166748046875, + "y": 259.3333282470703, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 197, + "height": 20, + "seed": 1179957165, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661138960, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "hold values of GLOBALs", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "hold values of GLOBALs" + }, + { + "type": "rectangle", + "version": 234, + "versionNonce": 339097851, + "isDeleted": false, + "id": "FcG2LCAdKxw_z2SzLhPPr", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 548.3333129882812, + "y": 351.666748046875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 228.66668701171866, + "height": 299.66668701171875, + "seed": 407083364, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "_acaSZ2N5FSiuPGQjH8RA", + "type": "arrow" + } + ], + "updated": 1679660870990, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 182, + "versionNonce": 1735336804, + "isDeleted": false, + "id": "OdBdX2K4Kcn9Hq7vInwKA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 571, + "y": 387.3333740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 190, + "height": 30, + "seed": 175211612, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "OZDTfM4SDGXEVbxgCAbsx" + } + ], + "updated": 1679643912221, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 135, + "versionNonce": 1695691996, + "isDeleted": false, + "id": "OZDTfM4SDGXEVbxgCAbsx", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 621.5, + "y": 392.3333740234375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 89, + "height": 20, + "seed": 1994750564, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643912221, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint8 type;", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "OdBdX2K4Kcn9Hq7vInwKA", + "originalText": "uint8 type;" + }, + { + "type": "text", + "version": 174, + "versionNonce": 2063524661, + "isDeleted": false, + "id": "w5el7zqw5vcK9RGIcDlFZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 580.9999389648438, + "y": 357.8333435058594, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 171, + "height": 19, + "seed": 1831729636, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "_acaSZ2N5FSiuPGQjH8RA", + "type": "arrow" + } + ], + "updated": 1679660882880, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMGlobalInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMGlobalInstance" + }, + { + "type": "rectangle", + "version": 147, + "versionNonce": 1194277212, + "isDeleted": false, + "id": "gd9UxDYh5XZYBG_P0f0c2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 568.3333129882812, + "y": 424.666748046875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 195, + "height": 30, + "seed": 1018791012, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "YeoEjoPWRnxxyB2DjNi3g" + } + ], + "updated": 1679643912221, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 104, + "versionNonce": 1904253540, + "isDeleted": false, + "id": "YeoEjoPWRnxxyB2DjNi3g", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 600.8333129882812, + "y": 429.666748046875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 130, + "height": 20, + "seed": 839439588, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643912221, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "bool is_mutable;", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gd9UxDYh5XZYBG_P0f0c2", + "originalText": "bool is_mutable;" + }, + { + "type": "rectangle", + "version": 91, + "versionNonce": 1480043996, + "isDeleted": false, + "id": "MB23InQWKES3OnYie8bbP", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 569.6666870117188, + "y": 460.666748046875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 195, + "height": 30, + "seed": 1468762332, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "--vP8lM62PEnMBhgFRqTM" + } + ], + "updated": 1679643912221, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 63, + "versionNonce": 717729252, + "isDeleted": false, + "id": "--vP8lM62PEnMBhgFRqTM", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 582.6666870117188, + "y": 465.666748046875, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 169, + "height": 20, + "seed": 695551844, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643912221, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint32 data_offset;", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MB23InQWKES3OnYie8bbP", + "originalText": "uint32 data_offset;" + }, + { + "type": "rectangle", + "version": 111, + "versionNonce": 155030108, + "isDeleted": false, + "id": "pUUma4amJviNKdfn5otpb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 577.6666870117188, + "y": 497.33338928222656, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 180.33331298828125, + "height": 30, + "seed": 1550527844, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "JybtW1PyDYQa00JaSYsuo" + } + ], + "updated": 1679643912221, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 63, + "versionNonce": 1625313636, + "isDeleted": false, + "id": "JybtW1PyDYQa00JaSYsuo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 618.8333435058594, + "y": 502.33338928222656, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 98, + "height": 20, + "seed": 1952693084, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643912221, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "initial_value", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "pUUma4amJviNKdfn5otpb", + "originalText": "initial_value" + }, + { + "type": "rectangle", + "version": 91, + "versionNonce": 1494410972, + "isDeleted": false, + "id": "CewuQtNj0ZC6Ogsq68ite", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 569.6666870117188, + "y": 546.6667022705078, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 191, + "height": 30, + "seed": 10317668, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "4xSFF2vFZIoA0G7GTeC8-" + } + ], + "updated": 1679643912221, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 63, + "versionNonce": 588809444, + "isDeleted": false, + "id": "4xSFF2vFZIoA0G7GTeC8-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 588.1666870117188, + "y": 551.6667022705078, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 154, + "height": 20, + "seed": 1109403492, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643912221, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "import_module_inst", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "CewuQtNj0ZC6Ogsq68ite", + "originalText": "import_module_inst" + }, + { + "type": "rectangle", + "version": 123, + "versionNonce": 474590044, + "isDeleted": false, + "id": "AuHfz77U4KxNTCmKvW6bJ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 573, + "y": 589.3333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 184, + "height": 30, + "seed": 2058570588, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "h7MfnIO2f08rV_U7840Zp" + } + ], + "updated": 1679643912221, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 75, + "versionNonce": 1727940708, + "isDeleted": false, + "id": "h7MfnIO2f08rV_U7840Zp", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 591, + "y": 594.3333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 148, + "height": 20, + "seed": 1917882340, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643912221, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "import_global_inst", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "AuHfz77U4KxNTCmKvW6bJ", + "originalText": "import_global_inst" + }, + { + "type": "text", + "version": 134, + "versionNonce": 1361481211, + "isDeleted": false, + "id": "Clf1NqZqRXJuRl-CQgx_u", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 232.6667175292969, + "y": 454.6668395996094, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 237, + "height": 20, + "seed": 1711793252, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "_acaSZ2N5FSiuPGQjH8RA", + "type": "arrow" + } + ], + "updated": 1679661000688, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMGlobalInstance *globals;", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMGlobalInstance *globals;" + }, + { + "type": "text", + "version": 30, + "versionNonce": 1504826724, + "isDeleted": false, + "id": "Mj7l2FOHQ7NNAkTiGq7T_", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 641, + "y": 631.0001373291016, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 29, + "height": 20, + "seed": 1401608924, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643959727, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[0]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[0]" + }, + { + "type": "rectangle", + "version": 52, + "versionNonce": 894397020, + "isDeleted": false, + "id": "CGwcN3BpsJaehLlHjEn6l", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 548.6666259765625, + "y": 653.0000457763672, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 227, + "height": 87, + "seed": 253331684, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644065258, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 107, + "versionNonce": 1424372828, + "isDeleted": false, + "id": "YjTAPKLSA0k-ml5j0Ho59", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 565.1666259765625, + "y": 671.6667327880859, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 195, + "height": 30, + "seed": 192441436, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "jmlAM3nHo1CJVxGKppt1O" + } + ], + "updated": 1679643968867, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 78, + "versionNonce": 2064306020, + "isDeleted": false, + "id": "jmlAM3nHo1CJVxGKppt1O", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 578.1666259765625, + "y": 676.6667327880859, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 169, + "height": 20, + "seed": 1473779556, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643968868, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint32 data_offset;", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "YjTAPKLSA0k-ml5j0Ho59", + "originalText": "uint32 data_offset;" + }, + { + "type": "text", + "version": 32, + "versionNonce": 1512962780, + "isDeleted": false, + "id": "QnLh-KZNv_MoD4Ak5RsLk", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 636.1666259765625, + "y": 719.0000457763672, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 22, + "height": 20, + "seed": 927449564, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679643979051, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[1]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[1]" + }, + { + "type": "rectangle", + "version": 62, + "versionNonce": 626330844, + "isDeleted": false, + "id": "F26JZDrwDBDSk5o5kcV_E", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 547.3333129882812, + "y": 739.6667327880859, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 227.666748046875, + "height": 60.666656494140625, + "seed": 725179740, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644057644, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 71, + "versionNonce": 1658992100, + "isDeleted": false, + "id": "H6cL4L0PUBLB5An3KDf-i", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 636.6666259765625, + "y": 784.0003204345703, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 31, + "height": 20, + "seed": 83711068, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644496025, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "[...]", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "[...]" + }, + { + "type": "rectangle", + "version": 158, + "versionNonce": 208125412, + "isDeleted": false, + "id": "M6cl8S-GRJkkRSQrUxzJV", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 566.833251953125, + "y": 747.6667327880859, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 195, + "height": 30, + "seed": 997959652, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "DrpXGgZIT2RAhz4L6EM2n" + } + ], + "updated": 1679644019993, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 128, + "versionNonce": 493257308, + "isDeleted": false, + "id": "DrpXGgZIT2RAhz4L6EM2n", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 579.833251953125, + "y": 752.6667327880859, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 169, + "height": 20, + "seed": 157169756, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644019993, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint32 data_offset;", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "M6cl8S-GRJkkRSQrUxzJV", + "originalText": "uint32 data_offset;" + }, + { + "type": "rectangle", + "version": 223, + "versionNonce": 680765365, + "isDeleted": false, + "id": "nPxM8HePICecTvRXbdEeU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 983.333251953125, + "y": 378.1666793823242, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 97.66668701171875, + "height": 30.000015258789062, + "seed": 1516156124, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "Y_Dw9hCEvutA3MjjHfyJK", + "type": "arrow" + } + ], + "updated": 1679660991443, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 165, + "versionNonce": 430152219, + "isDeleted": false, + "id": "Y_Dw9hCEvutA3MjjHfyJK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 756.6666259765625, + "y": 766.3333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 217.14296385054604, + "height": 385.92617569738337, + "seed": 703298780, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "MraNxq38RWxCBOb3EhIue" + } + ], + "updated": 1679660991443, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "nPxM8HePICecTvRXbdEeU", + "gap": 9.523662126016394, + "focus": 1.1442737969660202 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 217.14296385054604, + -385.92617569738337 + ] + ] + }, + { + "type": "text", + "version": 4, + "versionNonce": 1122104853, + "isDeleted": false, + "id": "MraNxq38RWxCBOb3EhIue", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 838.7381079018355, + "y": 563.3703014335349, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 53, + "height": 20, + "seed": 1058889828, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661201730, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "offset", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Y_Dw9hCEvutA3MjjHfyJK", + "originalText": "offset" + }, + { + "type": "text", + "version": 239, + "versionNonce": 551950564, + "isDeleted": false, + "id": "O5mHltv55Fs4-vM6wNcf-", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 226.99996948242188, + "y": 655.0000457763672, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 164, + "height": 20, + "seed": 352849508, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "1QAmzR8Zkk8scUA3tqI0k", + "type": "arrow" + } + ], + "updated": 1679644753215, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMGlobalInstance", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMGlobalInstance" + }, + { + "type": "rectangle", + "version": 104, + "versionNonce": 219644132, + "isDeleted": false, + "id": "CpOJESdMD1E9wBe2Gkkvf", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 216.66665649414062, + "y": 681.6666412353516, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 180.33331298828125, + "height": 46.666778564453125, + "seed": 705788380, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644728661, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 153, + "versionNonce": 296383867, + "isDeleted": false, + "id": "1QAmzR8Zkk8scUA3tqI0k", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 583.9999694824219, + "y": 602.3333587646484, + "strokeColor": "#000000", + "backgroundColor": "#15aabf", + "width": 179.2173434297432, + "height": 75.12469349055664, + "seed": 869061340, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "N9fS0DKeDh1d_Ueyopb9v" + } + ], + "updated": 1679661068533, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "O5mHltv55Fs4-vM6wNcf-", + "focus": 1.1855962422681312, + "gap": 14.0001220703125 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -179.2173434297432, + 75.12469349055664 + ] + ] + }, + { + "id": "N9fS0DKeDh1d_Ueyopb9v", + "type": "text", + "x": 449.39129776755027, + "y": 629.8957055099268, + "width": 90, + "height": 20, + "angle": 0, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 339183189, + "version": 24, + "versionNonce": 270159573, + "isDeleted": false, + "boundElements": null, + "updated": 1679661092509, + "link": null, + "locked": false, + "text": "when import", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "baseline": 14, + "containerId": "1QAmzR8Zkk8scUA3tqI0k", + "originalText": "when import" + }, + { + "type": "rectangle", + "version": 59, + "versionNonce": 96174172, + "isDeleted": false, + "id": "oDs6sXYd2cCCUTlDuFyfK", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 199.66659545898438, + "y": 599.9999847412109, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 236.33343505859375, + "height": 196.3333740234375, + "seed": 1531972708, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644717838, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 72, + "versionNonce": 1711293284, + "isDeleted": false, + "id": "Gyz12ttmraGX-CaCTWl3h", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 191.99990844726562, + "y": 576.3333892822266, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 239, + "height": 20, + "seed": 683171044, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "957MNrV_zCOsf-ssBBkla", + "type": "arrow" + } + ], + "updated": 1679644717838, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WASMModuleInstance (second)", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance (second)" + }, + { + "type": "text", + "version": 185, + "versionNonce": 1644000405, + "isDeleted": false, + "id": "7TYuQ_yCiwRN4oz8zkdGb", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 38.33337402343753, + "y": 248.66676330566406, + "strokeColor": "#d9480f", + "backgroundColor": "transparent", + "width": 171, + "height": 19, + "seed": 1694546652, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModuleInstance", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstance" + }, + { + "type": "rectangle", + "version": 151, + "versionNonce": 1152705621, + "isDeleted": false, + "id": "DN3tWnhyoeDPQR_-BYeYI", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 36.666656494140625, + "y": 269.00010681152344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 202.00003051757815, + "height": 124.66665649414062, + "seed": 693300324, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "-n6vwSfIOzGpAxjpRyuNZ", + "type": "arrow" + } + ], + "updated": 1679661004485, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 150, + "versionNonce": 458299605, + "isDeleted": false, + "id": "K0JMv-qZGU4i9Or0Xz4Gg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 246.33328247070315, + "y": 399.33343505859375, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 218, + "height": 19, + "seed": 881476572, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661029361, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 3, + "text": "WASMModuleInstanceExtra", + "baseline": 15, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WASMModuleInstanceExtra" + }, + { + "type": "rectangle", + "version": 169, + "versionNonce": 769392085, + "isDeleted": false, + "id": "CERoUCjzlaXjrdr4PSxtB", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 225.33328247070312, + "y": 428.00010681152344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 256.3333740234375, + "height": 91.33331298828128, + "seed": 1750838620, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "-n6vwSfIOzGpAxjpRyuNZ", + "type": "arrow" + } + ], + "updated": 1679661027326, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 88, + "versionNonce": 1947089019, + "isDeleted": false, + "id": "H3BkjBVeDYM2F429PxOI9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 233.0000915527344, + "y": 447.00010681152344, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 243.00006103515625, + "height": 31, + "seed": 258806116, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "-n6vwSfIOzGpAxjpRyuNZ", + "type": "arrow" + } + ], + "updated": 1679661000688, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 257, + "versionNonce": 314119835, + "isDeleted": false, + "id": "_acaSZ2N5FSiuPGQjH8RA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 463.97668298272777, + "y": 453.66683959960943, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 80.59006409386518, + "height": 100.23285752369344, + "seed": 1866235612, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false, + "startBinding": { + "elementId": "Clf1NqZqRXJuRl-CQgx_u", + "focus": 0.8216444271084713, + "gap": 1 + }, + "endBinding": { + "elementId": "FcG2LCAdKxw_z2SzLhPPr", + "focus": 1.009989878742988, + "gap": 3.7665659116883603 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 80.59006409386518, + -100.23285752369344 + ] + ] + }, + { + "type": "rectangle", + "version": 63, + "versionNonce": 1164697781, + "isDeleted": false, + "id": "NIObCr2apYl6JLLzA37G2", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 232.66665649414065, + "y": 486.66676330566406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 244.33331298828125, + "height": 30, + "seed": 1813909212, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "sYEF77zbpRw-pcdX1ass6" + } + ], + "updated": 1679661000688, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 24, + "versionNonce": 4682011, + "isDeleted": false, + "id": "sYEF77zbpRw-pcdX1ass6", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 272.33331298828125, + "y": 491.66676330566406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 165, + "height": 20, + "seed": 382274908, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint32 global_count;", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "NIObCr2apYl6JLLzA37G2", + "originalText": "uint32 global_count;" + }, + { + "type": "rectangle", + "version": 142, + "versionNonce": 519569941, + "isDeleted": false, + "id": "7jX0wgP7v4HHkuITKOf89", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 94.3333435058594, + "y": 344.33351135253906, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 115, + "height": 30.333358764648438, + "seed": 1226666212, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "sK7ecOoz_3QIduVF0nHd7" + } + ], + "updated": 1679661000688, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 116, + "versionNonce": 1846580667, + "isDeleted": false, + "id": "sK7ecOoz_3QIduVF0nHd7", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 146.3333435058594, + "y": 349.5001907348633, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 11, + "height": 20, + "seed": 1178170724, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "e", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "7jX0wgP7v4HHkuITKOf89", + "originalText": "e" + }, + { + "type": "diamond", + "version": 135, + "versionNonce": 691599221, + "isDeleted": false, + "id": "yTfoj623sdm1eh4Eusmvt", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 174.66665649414065, + "y": 352.0001983642578, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 13.66668701171875, + "height": 12.333328247070312, + "seed": 559414236, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false + }, + { + "type": "arrow", + "version": 324, + "versionNonce": 1197545819, + "isDeleted": false, + "id": "-n6vwSfIOzGpAxjpRyuNZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 182.00003051757815, + "y": 356.66676330566406, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 45.12566323463764, + "height": 69.66665649414062, + "seed": 1850539876, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661027326, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "CERoUCjzlaXjrdr4PSxtB", + "gap": 1, + "focus": -0.6072997775389923 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 45.12566323463764, + 69.66665649414062 + ] + ] + }, + { + "type": "arrow", + "version": 68, + "versionNonce": 732107996, + "isDeleted": false, + "id": "957MNrV_zCOsf-ssBBkla", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 578.0000305175781, + "y": 557.6666717529297, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 144.0692777412945, + "height": 39.30743410761045, + "seed": 1605267676, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644717838, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "Gyz12ttmraGX-CaCTWl3h", + "focus": 1.0338080600507247, + "gap": 3.00006103515625 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -144.0692777412945, + 39.30743410761045 + ] + ] + }, + { + "type": "diamond", + "version": 20, + "versionNonce": 825681252, + "isDeleted": false, + "id": "pKw8fr7S6f80J93nrJtkQ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 575.8333129882812, + "y": 553.5000076293945, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 13.66668701171875, + "height": 12.333328247070312, + "seed": 222063588, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644481683, + "link": null, + "locked": false + }, + { + "type": "diamond", + "version": 20, + "versionNonce": 825681252, + "isDeleted": false, + "id": "XKwLDKrjsXcSogvJHSY-e", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 573.8333129882812, + "y": 597.5000076293945, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 13.66668701171875, + "height": 12.333328247070312, + "seed": 1140569180, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644483164, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 215, + "versionNonce": 510935253, + "isDeleted": false, + "id": "oPjebDyI5NP26BNK4PLtX", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 50.33343505859378, + "y": 290.33338928222656, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 180, + "height": 31, + "seed": 727213156, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "ZK3cjtpUxVpruHBkbgevo" + } + ], + "updated": 1679661000688, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 175, + "versionNonce": 1653383931, + "isDeleted": false, + "id": "ZK3cjtpUxVpruHBkbgevo", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 58.33343505859378, + "y": 295.83338928222656, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 164, + "height": 20, + "seed": 1130592356, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "uint8 * global_data", + "baseline": 14, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "oPjebDyI5NP26BNK4PLtX", + "originalText": "uint8 * global_data" + }, + { + "type": "arrow", + "version": 159, + "versionNonce": 345149237, + "isDeleted": false, + "id": "5S0qe2-BjQRPwuEEQ_UsU", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 228.00003051757812, + "y": 299.9999694824219, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 754.7517447227082, + "height": 17.8331298828125, + "seed": 1256331620, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679661008047, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "D5Ay5TxydaAe4f80l_79L", + "focus": 0.9720307648275319, + "gap": 1 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + 385.0834503173828, + -17.8331298828125 + ], + [ + 754.7517447227082, + -2.996583692902334 + ] + ] + }, + { + "type": "diamond", + "version": 89, + "versionNonce": 1413439029, + "isDeleted": false, + "id": "szV9RT3yAJ51dUnP5JYMS", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 220.1667175292969, + "y": 294.50000762939453, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 13.66668701171875, + "height": 12.333328247070312, + "seed": 1642184284, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679661000688, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 160, + "versionNonce": 715957468, + "isDeleted": false, + "id": "DuwilIDd-fhNUxybFRKva", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 255.16659545898438, + "y": 750.6665496826172, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 99, + "height": 20, + "seed": 121250780, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644739787, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "global_data", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "global_data" + }, + { + "type": "rectangle", + "version": 143, + "versionNonce": 2046173532, + "isDeleted": false, + "id": "6CMun9I8IOX876t85IJ2X", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 215.83328247070312, + "y": 738.6664733886719, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 180.33331298828125, + "height": 46.666778564453125, + "seed": 2113088100, + "groupIds": [], + "roundness": null, + "boundElements": [ + { + "id": "igqxFPh3AgDb1kYMsORSZ", + "type": "arrow" + } + ], + "updated": 1679644784578, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 34, + "versionNonce": 1198703204, + "isDeleted": false, + "id": "cbSo4pUG_J3a4Pnk3ODCA", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 276.6665954589844, + "y": 693.3332366943359, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 55, + "height": 20, + "seed": 328242020, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1679644771096, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "globals", + "baseline": 14, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "globals" + }, + { + "type": "arrow", + "version": 81, + "versionNonce": 311117156, + "isDeleted": false, + "id": "igqxFPh3AgDb1kYMsORSZ", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 247.33328247070312, + "y": 706.3332366943359, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 71.66665649414062, + "height": 58.33343505859375, + "seed": 1345639908, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1679644787747, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": { + "elementId": "6CMun9I8IOX876t85IJ2X", + "focus": -0.8452179527426857, + "gap": 1.499908447265625 + }, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": "arrow", + "points": [ + [ + 0, + 0 + ], + [ + -71.66665649414062, + 15.66668701171875 + ], + [ + -32.999908447265625, + 58.33343505859375 + ] + ] + }, + { + "type": "diamond", + "version": 135, + "versionNonce": 691599221, + "isDeleted": false, + "id": "koTtnTl85TIGelUbfKlR9", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 459.58338928222656, + "y": 446.0001754760742, + "strokeColor": "#000000", + "backgroundColor": "#be4bdb", + "width": 13.66668701171875, + "height": 12.333328247070312, + "seed": 626707637, + "groupIds": [], + "roundness": null, + "boundElements": null, + "updated": 1679661018823, + "link": null, + "locked": false + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file diff --git a/core/iwasm/doc/images/wasm_globals.svg b/core/iwasm/doc/images/wasm_globals.svg new file mode 100644 index 0000000000..fa3461732a --- /dev/null +++ b/core/iwasm/doc/images/wasm_globals.svg @@ -0,0 +1,16 @@ + + + + + + + global_data:WASMModuleInstanceExtra::globalsoffsetoffsethold values of GLOBALsuint8 type;WASMGlobalInstancebool is_mutable;uint32 data_offset;initial_valueimport_module_instimport_global_instWASMGlobalInstance *globals;[0]uint32 data_offset;[1][...]uint32 data_offset;offsetWASMGlobalInstancewhen importWASMModuleInstance (second)WASMModuleInstanceWASMModuleInstanceExtrauint32 global_count;euint8 * global_dataglobal_dataglobals \ No newline at end of file diff --git a/core/iwasm/doc/wasm_exports.MD b/core/iwasm/doc/wasm_exports.MD new file mode 100644 index 0000000000..70708cde31 --- /dev/null +++ b/core/iwasm/doc/wasm_exports.MD @@ -0,0 +1,22 @@ +# Wasm exports +The internal data structure for Wasm exports: +![](./images/wasm_exports.svg) + +## Setup exports for Module +The array data structure pointed by `WASMModule::exports` is setup during loading a module. Basically the runtime will load the exports sections from the module file content, and construct an array of C struct `WASMExport`. + +A `WASMExport` item contains three elements that map the Wasm file exports section structure: +- name: the name shown to external +- kind: total 4 export types: function, globals, memory, table +- index: As all the 4 export types are organized in array, this refers to index in target export type + +## Function exports +### use function exports +function exports are often used in two situations: + 1. **call by host**: runtime API `wasm_runtime_lookup_function` will walk through the array of `WASMModuleInstance::export_functions` and compare the exported name with given target symbol name in the function parameter. If any array item matches the name, it then returns the value of field `function` which points to associated function instance (WASMFunctionInstance) + 2. **import by another module**: During linking multiple modules, the runtime saves the pointer of exported WASMFunctionInstance in the local WASMFunctionInstance of importing module. + +### setup for instance +The data structure pointed by `WASMModuleInstance::export_functions` is set up during instantiating module instance. + +The runtime will walk through the `WASMModule::exports` array and find all the item with kind equal to "function". Create a node of `WASMExportFuncInstance` for each matching, find the associated `WASMFunctionInstance` object and save its address in the field `function`. diff --git a/core/iwasm/doc/wasm_function.MD b/core/iwasm/doc/wasm_function.MD new file mode 100644 index 0000000000..016e46f776 --- /dev/null +++ b/core/iwasm/doc/wasm_function.MD @@ -0,0 +1,47 @@ +# Wasm Function + +## Internal data structure + +![](./images/wasm_function.svg) + +## Module level data (function) +**WASMModule**: Data structure created for loading a module. +- `WASMImport *import_functions`: initialized from the Wasm file function section +- `WASMImport *import_functions`: initialized from the Wasm file import section. The runtime will try to solve the imports from the native API registration, refer to [Export native API to WASM application](../../../doc/export_native_api.md). + +**WASMFunction**: represent a Wasm function located in Wasm file code section. Track the links to the compiled function body. +**WASMImport**: represent a imported Wasm function which can be a solved as a native function or another Wasm module exported function. + +## Instance level data (function) +**WASMModuleInstance**: Data structure created for instantiating a module +- `WASMModuleInstanceExtra::functions`: combined the imported and internal functions into single array of structure `WASMFunctionInstance` +- `WASMModuleInstance::import_func_ptrs`: pointer array for solved function imports. This array is referred during calling imported native function. Note it is initialized with the module level solved imports, but may points to different native function later due to c-api calls. + +## Execution paths +**Interpreter**: +- Execute internal bytecode function: + ``` + WASMModuleInstance::e + -> WASMModuleInstanceExtra::functions[..] + -> WASMFunctionInstance::func + -> WASMFunction::code + ``` + +- Execute imported function from other module: + ``` + WASMModuleInstance::e + -> WASMModuleInstanceExtra::functions[..] + (WASMFunctionInstance flag indicates an import) + -> WASMFunctionInstance::import_func_inst + -> WASMModuleInstance(second)::func + -> WASMFunction (second module)::code + ``` + +- Execute imported native function: + ``` + WASMModuleInstance::e + -> WASMModuleInstanceExtra::functions[..] + (flag indicates imported native) + WASMModuleInstance::import_func_ptrs[..] + -> native function + ``` diff --git a/core/iwasm/doc/wasm_globals.MD b/core/iwasm/doc/wasm_globals.MD new file mode 100644 index 0000000000..e5019a9fc5 --- /dev/null +++ b/core/iwasm/doc/wasm_globals.MD @@ -0,0 +1,4 @@ +# Wasm globals + +![](./images/wasm_globals.svg) + diff --git a/core/iwasm/fast-jit/asmjit_sgx_patch.diff b/core/iwasm/fast-jit/asmjit_sgx_patch.diff new file mode 100644 index 0000000000..465b9de616 --- /dev/null +++ b/core/iwasm/fast-jit/asmjit_sgx_patch.diff @@ -0,0 +1,42 @@ +diff --git a/src/asmjit/core/cpuinfo.cpp b/src/asmjit/core/cpuinfo.cpp +index 7bf7407..ae2160b 100644 +--- a/src/asmjit/core/cpuinfo.cpp ++++ b/src/asmjit/core/cpuinfo.cpp +@@ -9,13 +9,13 @@ + + #if !defined(_WIN32) + #include +- #include ++ //#include + #include + #endif + + // Required by `getauxval()` on Linux. + #if defined(__linux__) +- #include ++ //#include + #endif + + //! Required to detect CPU and features on Apple platforms. +diff --git a/src/asmjit/core/globals.cpp b/src/asmjit/core/globals.cpp +index 2bbd0c0..e6b69e5 100644 +--- a/src/asmjit/core/globals.cpp ++++ b/src/asmjit/core/globals.cpp +@@ -105,6 +105,8 @@ ASMJIT_FAVOR_SIZE const char* DebugUtils::errorAsString(Error err) noexcept { + #endif + } + ++extern "C" int os_printf(const char *message, ...); ++ + // DebugUtils - Debug Output + // ========================= + +@@ -112,7 +114,7 @@ ASMJIT_FAVOR_SIZE void DebugUtils::debugOutput(const char* str) noexcept { + #if defined(_WIN32) + ::OutputDebugStringA(str); + #else +- ::fputs(str, stderr); ++ os_printf(str); + #endif + } + diff --git a/core/iwasm/fast-jit/cg/LICENSE_ASMJIT b/core/iwasm/fast-jit/cg/LICENSE_ASMJIT new file mode 100644 index 0000000000..020a569dbd --- /dev/null +++ b/core/iwasm/fast-jit/cg/LICENSE_ASMJIT @@ -0,0 +1,17 @@ +Copyright (c) 2008-2020 The AsmJit Authors + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/core/iwasm/fast-jit/cg/LICENSE_ZYDIS b/core/iwasm/fast-jit/cg/LICENSE_ZYDIS new file mode 100644 index 0000000000..11185a5a74 --- /dev/null +++ b/core/iwasm/fast-jit/cg/LICENSE_ZYDIS @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2014-2021 Florian Bernd +Copyright (c) 2014-2021 Joel Höner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp b/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp new file mode 100644 index 0000000000..967bf14b5e --- /dev/null +++ b/core/iwasm/fast-jit/cg/x86-64/jit_codegen_x86_64.cpp @@ -0,0 +1,9513 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_codegen.h" +#include "jit_codecache.h" +#include "jit_compiler.h" +#include "jit_frontend.h" +#include "jit_dump.h" + +#include +#include +#if WASM_ENABLE_FAST_JIT_DUMP != 0 +#include +#endif + +#define CODEGEN_CHECK_ARGS 1 +#define CODEGEN_DUMP 0 + +using namespace asmjit; + +static char *code_block_switch_to_jitted_from_interp = NULL; +static char *code_block_return_to_interp_from_jitted = NULL; +#if WASM_ENABLE_LAZY_JIT != 0 +static char *code_block_compile_fast_jit_and_then_call = NULL; +#endif + +typedef enum { + REG_BPL_IDX = 0, + REG_AXL_IDX, + REG_BXL_IDX, + REG_CXL_IDX, + REG_DXL_IDX, + REG_DIL_IDX, + REG_SIL_IDX, + REG_I8_FREE_IDX = REG_SIL_IDX +} RegIndexI8; + +typedef enum { + REG_BP_IDX = 0, + REG_AX_IDX, + REG_BX_IDX, + REG_CX_IDX, + REG_DX_IDX, + REG_DI_IDX, + REG_SI_IDX, + REG_I16_FREE_IDX = REG_SI_IDX +} RegIndexI16; + +typedef enum { + REG_EBP_IDX = 0, + REG_EAX_IDX, + REG_EBX_IDX, + REG_ECX_IDX, + REG_EDX_IDX, + REG_EDI_IDX, + REG_ESI_IDX, + REG_I32_FREE_IDX = REG_ESI_IDX +} RegIndexI32; + +typedef enum { + REG_RBP_IDX = 0, + REG_RAX_IDX, + REG_RBX_IDX, + REG_RCX_IDX, + REG_RDX_IDX, + REG_RDI_IDX, + REG_RSI_IDX, + REG_RSP_IDX, + REG_R8_IDX, + REG_R9_IDX, + REG_R10_IDX, + REG_R11_IDX, + REG_R12_IDX, + REG_R13_IDX, + REG_R14_IDX, + REG_R15_IDX, + REG_I64_FREE_IDX = REG_RSI_IDX +} RegIndexI64; + +/* clang-format off */ +x86::Gp regs_i8[] = { + x86::bpl, x86::al, x86::bl, x86::cl, + x86::dl, x86::dil, x86::sil, x86::spl, + x86::r8b, x86::r9b, x86::r10b, x86::r11b, + x86::r12b, x86::r13b, x86::r14b, x86::r15b +}; + +x86::Gp regs_i16[] = { + x86::bp, x86::ax, x86::bx, x86::cx, + x86::dx, x86::di, x86::si, x86::sp, + x86::r8w, x86::r9w, x86::r10w, x86::r11w, + x86::r12w, x86::r13w, x86::r14w, x86::r15w +}; + +x86::Gp regs_i32[] = { + x86::ebp, x86::eax, x86::ebx, x86::ecx, + x86::edx, x86::edi, x86::esi, x86::esp, + x86::r8d, x86::r9d, x86::r10d, x86::r11d, + x86::r12d, x86::r13d, x86::r14d, x86::r15d +}; + +x86::Gp regs_i64[] = { + x86::rbp, x86::rax, x86::rbx, x86::rcx, + x86::rdx, x86::rdi, x86::rsi, x86::rsp, + x86::r8, x86::r9, x86::r10, x86::r11, + x86::r12, x86::r13, x86::r14, x86::r15, +}; + +#define REG_F32_FREE_IDX 15 +#define REG_F64_FREE_IDX 15 + +x86::Xmm regs_float[] = { + x86::xmm0, + x86::xmm1, + x86::xmm2, + x86::xmm3, + x86::xmm4, + x86::xmm5, + x86::xmm6, + x86::xmm7, + x86::xmm8, + x86::xmm9, + x86::xmm10, + x86::xmm11, + x86::xmm12, + x86::xmm13, + x86::xmm14, + x86::xmm15, +}; +/* clang-format on */ + +int +jit_codegen_interp_jitted_glue(void *exec_env, JitInterpSwitchInfo *info, + uint32 func_idx, void *target) +{ + typedef int32 (*F)(const void *exec_env, void *info, uint32 func_idx, + const void *target); + union { + F f; + void *v; + } u; + + u.v = code_block_switch_to_jitted_from_interp; + return u.f(exec_env, info, func_idx, target); +} + +#define PRINT_LINE() LOG_VERBOSE("\n", __LINE__) + +#if CODEGEN_DUMP != 0 +#define GOTO_FAIL \ + do { \ + PRINT_LINE(); \ + goto fail; \ + } while (0) +#else +#define GOTO_FAIL goto fail +#endif + +#if CODEGEN_CHECK_ARGS == 0 + +#define CHECK_EQKIND(reg0, reg1) (void)0 +#define CHECK_CONST(reg0) (void)0 +#define CHECK_NCONST(reg0) (void)0 +#define CHECK_KIND(reg0, type) (void)0 +#define CHECK_REG_NO(no, kind) (void)0 +#else + +/* Check if two register's kind is equal */ +#define CHECK_EQKIND(reg0, reg1) \ + do { \ + if (jit_reg_kind(reg0) != jit_reg_kind(reg1)) { \ + PRINT_LINE(); \ + LOG_VERBOSE("reg type not equal:\n"); \ + jit_dump_reg(cc, reg0); \ + jit_dump_reg(cc, reg1); \ + GOTO_FAIL; \ + } \ + } while (0) + +/* Check if a register is an const */ +#define CHECK_CONST(reg0) \ + do { \ + if (!jit_reg_is_const(reg0)) { \ + PRINT_LINE(); \ + LOG_VERBOSE("reg is not const:\n"); \ + jit_dump_reg(cc, reg0); \ + GOTO_FAIL; \ + } \ + } while (0) + +/* Check if a register is not an const */ +#define CHECK_NCONST(reg0) \ + do { \ + if (jit_reg_is_const(reg0)) { \ + PRINT_LINE(); \ + LOG_VERBOSE("reg is const:\n"); \ + jit_dump_reg(cc, reg0); \ + GOTO_FAIL; \ + } \ + } while (0) + +/* Check if a register is a special type */ +#define CHECK_KIND(reg0, type) \ + do { \ + if (jit_reg_kind(reg0) != type) { \ + PRINT_LINE(); \ + LOG_VERBOSE("invalid reg type %d, expected is: %d", \ + jit_reg_kind(reg0), type); \ + jit_dump_reg(cc, reg0); \ + GOTO_FAIL; \ + } \ + } while (0) + +#define CHECK_I32_REG_NO(no) \ + do { \ + if ((uint32)no >= sizeof(regs_i32) / sizeof(regs_i32[0])) \ + GOTO_FAIL; \ + } while (0) + +#define CHECK_I64_REG_NO(no) \ + do { \ + if ((uint32)no >= sizeof(regs_i64) / sizeof(regs_i64[0])) \ + GOTO_FAIL; \ + } while (0) + +#define CHECK_F32_REG_NO(no) \ + do { \ + if ((uint32)no >= sizeof(regs_float) / sizeof(regs_float[0])) \ + GOTO_FAIL; \ + } while (0) + +#define CHECK_F64_REG_NO(no) \ + do { \ + if ((uint32)no >= sizeof(regs_float) / sizeof(regs_float[0])) \ + GOTO_FAIL; \ + } while (0) + +/* Check if a register number is valid */ +#define CHECK_REG_NO(no, kind) \ + do { \ + if (kind == JIT_REG_KIND_I32 || kind == JIT_REG_KIND_I64) { \ + CHECK_I32_REG_NO(no); \ + CHECK_I64_REG_NO(no); \ + } \ + else if (kind == JIT_REG_KIND_F32 || kind == JIT_REG_KIND_F64) { \ + CHECK_F32_REG_NO(no); \ + CHECK_F64_REG_NO(no); \ + } \ + else \ + GOTO_FAIL; \ + } while (0) + +#endif /* end of CODEGEN_CHECK_ARGS == 0 */ + +/* Load one operand from insn and check none */ +#define LOAD_1ARG() r0 = *jit_insn_opnd(insn, 0) + +/* Load two operands from insn and check if r0 is non-const */ +#define LOAD_2ARGS() \ + r0 = *jit_insn_opnd(insn, 0); \ + r1 = *jit_insn_opnd(insn, 1); \ + CHECK_NCONST(r0) + +/* Load three operands from insn and check if r0 is non-const */ +#define LOAD_3ARGS() \ + r0 = *jit_insn_opnd(insn, 0); \ + r1 = *jit_insn_opnd(insn, 1); \ + r2 = *jit_insn_opnd(insn, 2); \ + CHECK_NCONST(r0) + +/* Load three operands from insn and check none */ +#define LOAD_3ARGS_NO_ASSIGN() \ + r0 = *jit_insn_opnd(insn, 0); \ + r1 = *jit_insn_opnd(insn, 1); \ + r2 = *jit_insn_opnd(insn, 2); + +/* Load four operands from insn and check if r0 is non-const */ +#define LOAD_4ARGS() \ + r0 = *jit_insn_opnd(insn, 0); \ + r1 = *jit_insn_opnd(insn, 1); \ + r2 = *jit_insn_opnd(insn, 2); \ + r3 = *jit_insn_opnd(insn, 3); \ + CHECK_NCONST(r0) + +/* Load five operands from insn and check if r0 is non-const */ +#define LOAD_4ARGS_NO_ASSIGN() \ + r0 = *jit_insn_opnd(insn, 0); \ + r1 = *jit_insn_opnd(insn, 1); \ + r2 = *jit_insn_opnd(insn, 2); \ + r3 = *jit_insn_opnd(insn, 3); + +class JitErrorHandler : public ErrorHandler +{ + public: + Error err; + + JitErrorHandler() + : err(kErrorOk) + {} + + void handleError(Error e, const char *msg, BaseEmitter *base) override + { + (void)msg; + (void)base; + this->err = e; + } +}; + +/* Alu opcode */ +typedef enum { ADD, SUB, MUL, DIV_S, REM_S, DIV_U, REM_U, MIN, MAX } ALU_OP; +/* Bit opcode */ +typedef enum { OR, XOR, AND } BIT_OP; +/* Shift opcode */ +typedef enum { SHL, SHRS, SHRU, ROTL, ROTR } SHIFT_OP; +/* Bitcount opcode */ +typedef enum { CLZ, CTZ, POPCNT } BITCOUNT_OP; +/* Condition opcode */ +typedef enum { EQ, NE, GTS, GES, LTS, LES, GTU, GEU, LTU, LEU } COND_OP; + +typedef union _cast_float_to_integer { + float f; + uint32 i; +} cast_float_to_integer; + +typedef union _cast_double_to_integer { + double d; + uint64 i; +} cast_double_to_integer; + +static uint32 +local_log2(uint32 data) +{ + uint32 ret = 0; + while (data >>= 1) { + ret++; + } + return ret; +} + +static uint64 +local_log2l(uint64 data) +{ + uint64 ret = 0; + while (data >>= 1) { + ret++; + } + return ret; +} + +/* Jmp type */ +typedef enum JmpType { + JMP_DST_LABEL_REL, /* jmp to dst label with relative addr */ + JMP_DST_LABEL_ABS, /* jmp to dst label with absolute addr */ + JMP_END_OF_CALLBC, /* jmp to end of CALLBC */ + JMP_LOOKUPSWITCH_BASE, /* LookupSwitch table base addr */ +} JmpType; + +/** + * Jmp info, save the info on first encoding pass, + * and replace the offset with exact offset when the code cache + * has been allocated actually. + */ +typedef struct JmpInfo { + bh_list_link link; + JmpType type; + uint32 label_src; + uint32 offset; + union { + uint32 label_dst; + } dst_info; +} JmpInfo; + +static bool +label_is_neighboring(JitCompContext *cc, int32 label_prev, int32 label_succ) +{ + return (label_prev == 0 && label_succ == 2) + || (label_prev >= 2 && label_succ == label_prev + 1) + || (label_prev == (int32)jit_cc_label_num(cc) - 1 + && label_succ == 1); +} + +static bool +label_is_ahead(JitCompContext *cc, int32 label_dst, int32 label_src) +{ + return (label_dst == 0 && label_src != 0) + || (label_dst != 1 && label_src == 1) + || (2 <= label_dst && label_dst < label_src + && label_src <= (int32)jit_cc_label_num(cc) - 1); +} + +/** + * Encode jumping from one label to the other label + * + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param label_dst the index of dst label + * @param label_src the index of src label + * + * @return true if success, false if failed + */ +static bool +jmp_from_label_to_label(x86::Assembler &a, bh_list *jmp_info_list, + int32 label_dst, int32 label_src) +{ + Imm imm(INT32_MAX); + JmpInfo *node; + + node = (JmpInfo *)jit_calloc(sizeof(JmpInfo)); + if (!node) + return false; + + node->type = JMP_DST_LABEL_REL; + node->label_src = label_src; + node->dst_info.label_dst = label_dst; + node->offset = a.code()->sectionById(0)->buffer().size() + 2; + bh_list_insert(jmp_info_list, node); + + a.jmp(imm); + return true; +} + +/** + * Encode detecting compare result register according to condition code + * and then jumping to suitable label when the condition is met + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param label_src the index of src label + * @param op the opcode of condition operation + * @param r1 the label info when condition is met + * @param r2 the label info when condition is unmet, do nothing if VOID + * @param is_last_insn if current insn is the last insn of current block + * + * @return true if success, false if failed + */ +static bool +cmp_r_and_jmp_label(JitCompContext *cc, x86::Assembler &a, + bh_list *jmp_info_list, int32 label_src, COND_OP op, + JitReg r1, JitReg r2, bool is_last_insn) +{ + Imm imm(INT32_MAX); + JmpInfo *node; + + node = (JmpInfo *)jit_malloc(sizeof(JmpInfo)); + if (!node) + return false; + + node->type = JMP_DST_LABEL_REL; + node->label_src = label_src; + node->dst_info.label_dst = jit_reg_no(r1); + node->offset = a.code()->sectionById(0)->buffer().size() + 2; + bh_list_insert(jmp_info_list, node); + + bool fp_cmp = cc->last_cmp_on_fp; + + bh_assert(!fp_cmp || (fp_cmp && (op == GTS || op == GES))); + + switch (op) { + case EQ: + { + a.je(imm); + break; + } + case NE: + { + a.jne(imm); + break; + } + case GTS: + { + if (fp_cmp) + a.ja(imm); + else + a.jg(imm); + break; + } + case LES: + { + a.jng(imm); + break; + } + case GES: + { + if (fp_cmp) + a.jae(imm); + else + a.jnl(imm); + break; + } + case LTS: + { + a.jl(imm); + break; + } + case GTU: + { + a.ja(imm); + break; + } + case LEU: + { + a.jna(imm); + break; + } + case GEU: + { + a.jnb(imm); + break; + } + case LTU: + { + a.jb(imm); + break; + } + default: + { + bh_assert(0); + break; + } + } + + if (r2) { + int32 label_dst = jit_reg_no(r2); + if (!(is_last_insn && label_is_neighboring(cc, label_src, label_dst))) + if (!jmp_from_label_to_label(a, jmp_info_list, label_dst, + label_src)) + return false; + } + + return true; +} + +#if WASM_ENABLE_FAST_JIT_DUMP != 0 +static void +dump_native(char *data, uint32 length) +{ + /* Initialize decoder context */ + ZydisDecoder decoder; + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, + ZYDIS_STACK_WIDTH_64); + + /* Initialize formatter */ + ZydisFormatter formatter; + ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); + + /* Loop over the instructions in our buffer */ + ZyanU64 runtime_address = (ZyanU64)(uintptr_t)data; + ZyanUSize offset = 0; + ZydisDecodedInstruction instruction; + ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE]; + + while (ZYAN_SUCCESS(ZydisDecoderDecodeFull( + &decoder, data + offset, length - offset, &instruction, operands, + ZYDIS_MAX_OPERAND_COUNT_VISIBLE, ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY))) { + /* Print current instruction pointer */ + os_printf("%012" PRIX64 " ", runtime_address); + + /* Format & print the binary instruction structure to + human readable format */ + char buffer[256]; + ZydisFormatterFormatInstruction(&formatter, &instruction, operands, + instruction.operand_count_visible, + buffer, sizeof(buffer), + runtime_address); + puts(buffer); + + offset += instruction.length; + runtime_address += instruction.length; + } +} +#endif + +/** + * Encode extending register of byte to register of dword + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src tho no of src register + * @param is_signed the data is signed or unsigned + * + * @return true if success, false otherwise + */ +static bool +extend_r8_to_r32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src, + bool is_signed) +{ + if (is_signed) { + a.movsx(regs_i32[reg_no_dst], regs_i8[reg_no_src]); + } + else { + a.movzx(regs_i32[reg_no_dst], regs_i8[reg_no_src]); + } + return true; +} +/** + * Encode extending register of word to register of dword + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src tho no of src register + * @param is_signed the data is signed or unsigned + * + * @return true if success, false otherwise + */ +static bool +extend_r16_to_r32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src, + bool is_signed) +{ + if (is_signed) { + a.movsx(regs_i32[reg_no_dst], regs_i16[reg_no_src]); + } + else { + a.movzx(regs_i32[reg_no_dst], regs_i16[reg_no_src]); + } + return true; +} + +/** + * Encode extending register of byte to register of qword + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src tho no of src register + * @param is_signed the data is signed or unsigned + * + * @return true if success, false otherwise + */ +static bool +extend_r8_to_r64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src, + bool is_signed) +{ + if (is_signed) { + a.movsx(regs_i64[reg_no_dst], regs_i8[reg_no_src]); + } + else { + a.movzx(regs_i64[reg_no_dst], regs_i8[reg_no_src]); + } + return true; +} + +/** + * Encode extending register of word to register of qword + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src tho no of src register + * @param is_signed the data is signed or unsigned + * + * @return true if success, false otherwise + */ +static bool +extend_r16_to_r64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src, + bool is_signed) +{ + if (is_signed) { + a.movsx(regs_i64[reg_no_dst], regs_i16[reg_no_src]); + } + else { + a.movzx(regs_i64[reg_no_dst], regs_i16[reg_no_src]); + } + return true; +} + +/** + * Encode extending register of dword to register of qword + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src tho no of src register + * @param is_signed the data is signed or unsigned + * + * @return true if success, false otherwise + */ +static bool +extend_r32_to_r64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src, + bool is_signed) +{ + if (is_signed) { + a.movsxd(regs_i64[reg_no_dst], regs_i32[reg_no_src]); + } + else { + /* + * The upper 32-bit will be zero-extended, ref to Intel document, + * 3.4.1.1 General-Purpose Registers: 32-bit operands generate + * a 32-bit result, zero-extended to a 64-bit result in the + * destination general-purpose register + */ + a.mov(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + } + return true; +} + +static bool +mov_r_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src); + +static bool +mov_r_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src); + +static void +mov_r_to_r(x86::Assembler &a, uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src) +{ + if (kind_dst == JIT_REG_KIND_I32) + mov_r_to_r_i32(a, reg_no_dst, reg_no_src); + else if (kind_dst == JIT_REG_KIND_I64) + mov_r_to_r_i64(a, reg_no_dst, reg_no_src); + else if (kind_dst == JIT_REG_KIND_F32) { + /* TODO */ + bh_assert(0); + } + else if (kind_dst == JIT_REG_KIND_F64) { + /* TODO */ + bh_assert(0); + } + else { + bh_assert(0); + } +} + +/** + * Encode moving memory to a register + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * skipped by float and double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param is_signed whether the data is signed or unsigned + * @param reg_no_dst the index of dest register + * @param m_src the memory operand which contains the source data + * + * @return true if success, false otherwise + */ +static bool +mov_m_to_r(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, bool is_signed, + int32 reg_no_dst, x86::Mem &m_src) +{ + if (kind_dst == JIT_REG_KIND_I32) { + switch (bytes_dst) { + case 1: + case 2: + if (is_signed) + a.movsx(regs_i32[reg_no_dst], m_src); + else + a.movzx(regs_i32[reg_no_dst], m_src); + break; + case 4: + a.mov(regs_i32[reg_no_dst], m_src); + break; + default: + bh_assert(0); + return false; + } + } + else if (kind_dst == JIT_REG_KIND_I64) { + switch (bytes_dst) { + case 1: + case 2: + if (is_signed) + a.movsx(regs_i64[reg_no_dst], m_src); + else + a.movzx(regs_i64[reg_no_dst], m_src); + break; + case 4: + if (is_signed) + a.movsxd(regs_i64[reg_no_dst], m_src); + else + /* + * The upper 32-bit will be zero-extended, ref to Intel + * document, 3.4.1.1 General-Purpose Registers: 32-bit + * operands generate a 32-bit result, zero-extended to + * a 64-bit result in the destination general-purpose + * register + */ + a.mov(regs_i32[reg_no_dst], m_src); + break; + case 8: + a.mov(regs_i64[reg_no_dst], m_src); + break; + default: + bh_assert(0); + return false; + } + } + else if (kind_dst == JIT_REG_KIND_F32) { + a.movss(regs_float[reg_no_dst], m_src); + } + else if (kind_dst == JIT_REG_KIND_F64) { + a.movsd(regs_float[reg_no_dst], m_src); + } + return true; +} + +/** + * Encode moving register to memory + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * skipped by float and double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param is_signed whether the data is signed or unsigned + * @param m_dst the dest memory operand + * @param reg_no_src the index of dest register + * + * @return true if success, false otherwise + */ +static bool +mov_r_to_m(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + x86::Mem &m_dst, int32 reg_no_src) +{ + if (kind_dst == JIT_REG_KIND_I32) { + bh_assert(reg_no_src < 16); + switch (bytes_dst) { + case 1: + a.mov(m_dst, regs_i8[reg_no_src]); + break; + case 2: + a.mov(m_dst, regs_i16[reg_no_src]); + break; + case 4: + a.mov(m_dst, regs_i32[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + } + else if (kind_dst == JIT_REG_KIND_I64) { + bh_assert(reg_no_src < 16); + switch (bytes_dst) { + case 1: + a.mov(m_dst, regs_i8[reg_no_src]); + break; + case 2: + a.mov(m_dst, regs_i16[reg_no_src]); + break; + case 4: + a.mov(m_dst, regs_i32[reg_no_src]); + break; + case 8: + a.mov(m_dst, regs_i64[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + } + else if (kind_dst == JIT_REG_KIND_F32) { + a.movss(m_dst, regs_float[reg_no_src]); + } + else if (kind_dst == JIT_REG_KIND_F64) { + a.movsd(m_dst, regs_float[reg_no_src]); + } + return true; +} + +/** + * Encode moving immediate data to memory + * + * @param m dst memory + * @param imm src immediate data + * + * @return new stream + */ +static bool +mov_imm_to_m(x86::Assembler &a, x86::Mem &m_dst, Imm imm_src, uint32 bytes_dst) +{ + if (bytes_dst == 8) { + int64 value = imm_src.value(); + if (value >= INT32_MIN && value <= INT32_MAX) { + imm_src.setValue((int32)value); + a.mov(m_dst, imm_src); + } + else { + /* There is no instruction `MOV m64, imm64`, we use + two instructions to implement it */ + a.mov(regs_i64[REG_I64_FREE_IDX], imm_src); + a.mov(m_dst, regs_i64[REG_I64_FREE_IDX]); + } + } + else + a.mov(m_dst, imm_src); + return true; +} + +#if WASM_ENABLE_SHARED_MEMORY != 0 +/** + * Encode exchange register with memory + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * skipped by float and double + * @param kind_dst the kind of data to move, could only be I32 or I64 + * @param m_dst the dest memory operand + * @param reg_no_src the index of dest register + * + * @return true if success, false otherwise + */ +static bool +xchg_r_to_m(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + x86::Mem &m_dst, int32 reg_no_src) +{ + bh_assert((kind_dst == JIT_REG_KIND_I32 && bytes_dst <= 4) + || kind_dst == JIT_REG_KIND_I64); + bh_assert(reg_no_src < 16); + switch (bytes_dst) { + case 1: + a.xchg(m_dst, regs_i8[reg_no_src]); + break; + case 2: + a.xchg(m_dst, regs_i16[reg_no_src]); + break; + case 4: + a.xchg(m_dst, regs_i32[reg_no_src]); + break; + case 8: + a.xchg(m_dst, regs_i64[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + return true; +} +#endif +/** + * Encode loading register data from memory with imm base and imm offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param is_signed the data is signed or unsigned + * @param reg_no_dst the index of dest register + * @param base the base address of the memory + * @param offset the offset address of the memory + * + * @return true if success, false otherwise + */ +static bool +ld_r_from_base_imm_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, bool is_signed, int32 reg_no_dst, + int32 base, int32 offset) +{ + x86::Mem m((uintptr_t)(base + offset), bytes_dst); + return mov_m_to_r(a, bytes_dst, kind_dst, is_signed, reg_no_dst, m); +} + +/** + * Encode loading register data from memory with imm base and register offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param is_signed the data is signed or unsigned + * @param reg_no_dst the index of dest register + * @param base the base address of the memory + * @param reg_no_offset the no of register which stores the offset of the memory + * + * @return true if success, false otherwise + */ +static bool +ld_r_from_base_imm_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, bool is_signed, int32 reg_no_dst, + int32 base, int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_offset], base, bytes_dst); + return mov_m_to_r(a, bytes_dst, kind_dst, is_signed, reg_no_dst, m); +} + +/** + * Encode loading register data from memory with register base and imm offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param is_signed the data is signed or unsigned + * @param reg_no_dst the index of dest register + * @param reg_no_base the no of register which stores the base of the memory + * @param offset the offset address of the memory + * + * @return true if success, false otherwise + */ +static bool +ld_r_from_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, bool is_signed, int32 reg_no_dst, + int32 reg_no_base, int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return mov_m_to_r(a, bytes_dst, kind_dst, is_signed, reg_no_dst, m); +} + +/** + * Encode loading register data from memory with register base and register + * offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param is_signed the data is signed or unsigned + * @param reg_no_dst the index of dest register + * @param reg_no_base the no of register which stores the base of the memory + * @param reg_no_offset the no of register which stores the offset of the memory + * + * @return true if success, false otherwise + */ +static bool +ld_r_from_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + bool is_signed, int32 reg_no_dst, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return mov_m_to_r(a, bytes_dst, kind_dst, is_signed, reg_no_dst, m); +} + +/** + * Encode storing register data to memory with imm base and imm offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param reg_no_src the index of src register + * @param base the base address of the dst memory + * @param offset the offset address of the dst memory + * + * @return true if success, false otherwise + */ +static bool +st_r_to_base_imm_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_src, int32 base, + int32 offset, bool atomic) +{ + x86::Mem m((uintptr_t)(base + offset), bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +#endif + return mov_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +} + +/** + * Encode storing register data to memory with imm base and register offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param reg_no_src the index of src register + * @param base the base address of the dst memory + * @param reg_no_offset the no of register which stores the offset of the dst + * memory + * + * @return true if success, false otherwise + */ +static bool +st_r_to_base_imm_offset_r(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + int32 reg_no_src, int32 base, int32 reg_no_offset, + bool atomic) +{ + x86::Mem m(regs_i64[reg_no_offset], base, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +#endif + return mov_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +} + +/** + * Encode storing register data to memory with register base and imm offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param reg_no_src the index of src register + * @param reg_no_base the no of register which stores the base of the dst memory + * @param offset the offset address of the dst memory + * + * @return true if success, false otherwise + */ +static bool +st_r_to_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + int32 reg_no_src, int32 reg_no_base, int32 offset, + bool atomic) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +#endif + return mov_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +} + +/** + * Encode storing register data to memory with register base and register offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), skipped by + * float/double + * @param kind_dst the kind of data to move, could be I32, I64, F32 or F64 + * @param reg_no_src the index of src register + * @param reg_no_base the no of register which stores the base of the dst memory + * @param reg_no_offset the no of register which stores the offset of the dst + * memory + * + * @return true if success, false otherwise + */ +static bool +st_r_to_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + int32 reg_no_src, int32 reg_no_base, + int32 reg_no_offset, bool atomic) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +#endif + return mov_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src); +} + +static void +imm_set_value(Imm &imm, void *data, uint32 bytes) +{ + switch (bytes) { + case 1: + imm.setValue(*(uint8 *)data); + break; + case 2: + imm.setValue(*(uint16 *)data); + break; + case 4: + imm.setValue(*(uint32 *)data); + break; + case 8: + imm.setValue(*(uint64 *)data); + break; + default: + bh_assert(0); + } +} + +#if WASM_ENABLE_SHARED_MEMORY != 0 +static uint32 +mov_imm_to_free_reg(x86::Assembler &a, Imm &imm, uint32 bytes) +{ + uint32 reg_no; + + switch (bytes) { + case 1: + reg_no = REG_I8_FREE_IDX; + a.mov(regs_i8[reg_no], imm); + break; + case 2: + reg_no = REG_I16_FREE_IDX; + a.mov(regs_i16[reg_no], imm); + break; + case 4: + reg_no = REG_I32_FREE_IDX; + a.mov(regs_i32[reg_no], imm); + break; + case 8: + reg_no = REG_I64_FREE_IDX; + a.mov(regs_i64[reg_no], imm); + break; + default: + bh_assert(0); + } + + return reg_no; +} +#endif + +/** + * Encode storing int32 imm data to memory with imm base and imm offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64) + * @param data_src the src immediate data + * @param base the base address of dst memory + * @param offset the offset address of dst memory + * + * @return true if success, false otherwise + */ +static bool +st_imm_to_base_imm_offset_imm(x86::Assembler &a, uint32 bytes_dst, + void *data_src, int32 base, int32 offset, + bool atomic) +{ + x86::Mem m((uintptr_t)(base + offset), bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + if (atomic) { + return xchg_r_to_m(a, bytes_dst, JIT_REG_KIND_I64, m, reg_no_src); + } +#endif + return mov_imm_to_m(a, m, imm, bytes_dst); +} + +/** + * Encode storing int32 imm data to memory with imm base and reg offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64) + * @param data_src the src immediate data + * @param base the base address of dst memory + * @param reg_no_offset the no of register that stores the offset address + * of dst memory + * + * @return true if success, false otherwise + */ +static bool +st_imm_to_base_imm_offset_r(x86::Assembler &a, uint32 bytes_dst, void *data_src, + int32 base, int32 reg_no_offset, bool atomic) +{ + x86::Mem m(regs_i64[reg_no_offset], base, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + if (atomic) { + return xchg_r_to_m(a, bytes_dst, JIT_REG_KIND_I64, m, reg_no_src); + } +#endif + return mov_imm_to_m(a, m, imm, bytes_dst); +} + +/** + * Encode storing int32 imm data to memory with reg base and imm offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64) + * @param data_src the src immediate data + * @param reg_no_base the no of register that stores the base address + * of dst memory + * @param offset the offset address of dst memory + * + * @return true if success, false otherwise + */ +static bool +st_imm_to_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, void *data_src, + int32 reg_no_base, int32 offset, bool atomic) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + if (atomic) { + return xchg_r_to_m(a, bytes_dst, JIT_REG_KIND_I64, m, reg_no_src); + } +#endif + return mov_imm_to_m(a, m, imm, bytes_dst); +} + +/** + * Encode storing int32 imm data to memory with reg base and reg offset + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64) + * @param data_src the src immediate data + * @param reg_no_base the no of register that stores the base address + * of dst memory + * @param reg_no_offset the no of register that stores the offset address + * of dst memory + * + * @return true if success, false otherwise + */ +static bool +st_imm_to_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, void *data_src, + int32 reg_no_base, int32 reg_no_offset, bool atomic) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); +#if WASM_ENABLE_SHARED_MEMORY != 0 + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + if (atomic) { + return xchg_r_to_m(a, bytes_dst, JIT_REG_KIND_I64, m, reg_no_src); + } +#endif + return mov_imm_to_m(a, m, imm, bytes_dst); +} + +/** + * Encode moving immediate int32 data to register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst register + * @param data the immediate data to move + * + * @return true if success, false otherwise + */ +static bool +mov_imm_to_r_i32(x86::Assembler &a, int32 reg_no, int32 data) +{ + Imm imm(data); + a.mov(regs_i32[reg_no], imm); + return true; +} + +/** + * Encode moving int32 data from src register to dst register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +mov_r_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + if (reg_no_dst != reg_no_src) + a.mov(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + return true; +} + +/** + * Encode moving immediate int64 data to register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst register + * @param data the immediate data to move + * + * @return true if success, false otherwise + */ +static bool +mov_imm_to_r_i64(x86::Assembler &a, int32 reg_no, int64 data) +{ + Imm imm(data); + a.mov(regs_i64[reg_no], imm); + return true; +} + +/** + * Encode moving int64 data from src register to dst register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +mov_r_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + if (reg_no_dst != reg_no_src) + a.mov(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + return true; +} + +/** + * Encode moving immediate float data to register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst register + * @param data the immediate data to move + * + * @return true if success, false otherwise + */ +static bool +mov_imm_to_r_f32(x86::Assembler &a, int32 reg_no, float data) +{ + /* imm -> gp -> xmm */ + cast_float_to_integer v = { .f = data }; + Imm imm(v.i); + a.mov(regs_i32[REG_I32_FREE_IDX], imm); + a.movd(regs_float[reg_no], regs_i32[REG_I32_FREE_IDX]); + return true; +} + +/** + * Encode moving float data from src register to dst register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +mov_r_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + if (reg_no_dst != reg_no_src) { + a.movss(regs_float[reg_no_dst], regs_float[reg_no_src]); + } + return true; +} + +/** + * Encode moving immediate double data to register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst register + * @param data the immediate data to move + * + * @return true if success, false otherwise + */ +static bool +mov_imm_to_r_f64(x86::Assembler &a, int32 reg_no, double data) +{ + cast_double_to_integer v = { .d = data }; + Imm imm(v.i); + a.mov(regs_i64[REG_I32_FREE_IDX], imm); + /* REG_I32_FREE_IDX == REG_I64_FREE_IDX */ + a.movq(regs_float[reg_no], regs_i64[REG_I64_FREE_IDX]); + return true; +} + +/** + * Encode moving double data from src register to dst register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +mov_r_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + if (reg_no_dst != reg_no_src) { + a.movsd(regs_float[reg_no_dst], regs_float[reg_no_src]); + } + return true; +} + +/* Let compiler do the conversation job as much as possible */ + +/** + * Encoding convert int8 immediate data to int32 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to int32 + * @param data the src int8 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i8_to_r_i32(x86::Assembler &a, int32 reg_no, int8 data) +{ + return mov_imm_to_r_i32(a, reg_no, (int32)data); +} + +/** + * encoding convert int8 register to int32 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i8_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return extend_r8_to_r32(a, reg_no_dst, reg_no_src, true); +} + +/** + * encoding convert int8 immediate data to int64 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to int64 + * @param data the src int8 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i8_to_r_i64(x86::Assembler &a, int32 reg_no, int8 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int64)data); +} + +/** + * encoding convert int8 register to int64 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i8_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return extend_r8_to_r64(a, reg_no_dst, reg_no_src, true); +} + +/** + * Encoding convert int16 immediate data to int32 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to int32 + * @param data the src int16 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i16_to_r_i32(x86::Assembler &a, int32 reg_no, int16 data) +{ + return mov_imm_to_r_i32(a, reg_no, (int32)data); +} + +/** + * encoding convert int16 register to int32 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i16_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return extend_r16_to_r32(a, reg_no_dst, reg_no_src, true); +} + +/** + * encoding convert int16 immediate data to int64 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to int64 + * @param data the src int16 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i16_to_r_i64(x86::Assembler &a, int32 reg_no, int16 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int64)data); +} + +/** + * encoding convert int16 register to int64 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i16_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return extend_r16_to_r64(a, reg_no_dst, reg_no_src, true); +} + +/** + * Encoding convert int32 immediate data to int8 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to int8 + * @param data the src int32 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_i8(x86::Assembler &a, int32 reg_no, int32 data) +{ + /* (int32)(int8)data will do sign-extension */ + /* (int32)(uint32)(int8)data is longer */ + return mov_imm_to_r_i32(a, reg_no, data & 0x000000FF); +} + +/** + * Encoding convert int32 immediate data to int8 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register, need to be converted to int8 + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_i8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i32(a, reg_no_dst, reg_no_src); + a.and_(regs_i32[reg_no_dst], 0x000000FF); + return true; +} + +/** + * Encoding convert int32 immediate data to uint8 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to uint8 + * @param data the src int32 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_u8(x86::Assembler &a, int32 reg_no, int32 data) +{ + return mov_imm_to_r_i32(a, reg_no, (uint8)data); +} + +/** + * Encoding convert int32 immediate data to uint8 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register, need to be converted to uint8 + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_u8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return convert_r_i32_to_r_i8(a, reg_no_dst, reg_no_src); +} + +/** + * Encoding convert int32 immediate data to int16 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to int16 + * @param data the src int32 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_i16(x86::Assembler &a, int32 reg_no, int32 data) +{ + /* (int32)(int16)data will do sign-extension */ + /* (int32)(uint32)(int16)data is longer */ + return mov_imm_to_r_i32(a, reg_no, data & 0x0000FFFF); +} + +/** + * Encoding convert int32 immediate data to int16 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register, need to be converted to int16 + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_i16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i32(a, reg_no_dst, reg_no_src); + a.and_(regs_i32[reg_no_dst], 0x0000FFFF); + return true; +} + +/** + * Encoding convert int32 immediate data to uint16 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to uint16 + * @param data the src int32 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_u16(x86::Assembler &a, int32 reg_no, int32 data) +{ + return mov_imm_to_r_i32(a, reg_no, (uint16)data); +} + +/** + * Encoding convert int32 immediate data to uint16 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register, need to be converted to uint16 + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_u16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return convert_r_i32_to_r_i16(a, reg_no_dst, reg_no_src); +} + +/** + * Encoding convert int32 immediate data to int64 register + * + * @param a the assembler to emit the code + * @param reg_no the dst register, need to be converted to uint64 + * @param data the src int32 immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_i64(x86::Assembler &a, int32 reg_no, int32 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int64)data); +} + +/** + * Encoding convert int32 register data to int64 register with signed extension + * + * @param a the assembler to emit the code + * @param reg_no_dst the dst register, need to be converted to uint64 + * @param reg_no_src the src register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return extend_r32_to_r64(a, reg_no_dst, reg_no_src, true); +} + +/** + * Encode converting int32 register data to float register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst float register + * @param reg_no_src the no of src int32 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvtsi2ss(regs_float[reg_no_dst], regs_i32[reg_no_src]); + return true; +} + +/** + * Encode converting int32 immediate data to float register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst float register + * @param data the src immediate data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_f32(x86::Assembler &a, int32 reg_no, int32 data) +{ + mov_imm_to_r_i32(a, REG_I32_FREE_IDX, data); + return convert_r_i32_to_r_f32(a, reg_no, REG_I32_FREE_IDX); +} + +/** + * Encode converting int32 register data to double register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst double register + * @param reg_no_src the no of src int32 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i32_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvtsi2sd(regs_float[reg_no_dst], regs_i32[reg_no_src]); + return true; +} + +/** + * Encode converting int32 immediate data to double register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst double register + * @param data the src immediate int32 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i32_to_r_f64(x86::Assembler &a, int32 reg_no, int32 data) +{ + mov_imm_to_r_i32(a, REG_I32_FREE_IDX, data); + return convert_r_i32_to_r_f64(a, reg_no, REG_I32_FREE_IDX); +} + +/** + * Encode converting int64 immediate data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate int64 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i64_to_r_i32(x86::Assembler &a, int32 reg_no, int64 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int32)data); +} + +/** + * Encode converting int64 register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i64_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i64(a, reg_no_dst, reg_no_src); + a.and_(regs_i64[reg_no_dst], 0x00000000FFFFFFFFLL); + return true; +} + +/** + * Encode converting int64 immediate data to int8 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate int64 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i64_to_r_i8(x86::Assembler &a, int32 reg_no, int64 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int8)data); +} + +/** + * Encode converting int64 register data to int8 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int8 register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i64_to_r_i8(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i64(a, reg_no_dst, reg_no_src); + a.and_(regs_i64[reg_no_dst], 0x00000000000000FFLL); + return true; +} + +/** + * Encode converting int64 immediate data to int16 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate int64 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i64_to_r_i16(x86::Assembler &a, int32 reg_no, int64 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int16)data); +} + +/** + * Encode converting int64 register data to int16 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int16 register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i64_to_r_i16(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i64(a, reg_no_dst, reg_no_src); + a.and_(regs_i64[reg_no_dst], 0x000000000000FFFFLL); + return true; +} + +/** + * Encode converting uint32 immediate data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int64 register + * @param data the src immediate uint32 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_u32_to_r_i64(x86::Assembler &a, int32 reg_no, uint32 data) +{ + return mov_imm_to_r_i64(a, reg_no, (int64)(uint64)data); +} + +/** + * Encode converting uint32 register data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst uint32 register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_u32_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + return extend_r32_to_r64(a, reg_no_dst, reg_no_src, false); +} + +/** + * Encode converting uint32 immediate data to float register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst float register + * @param data the src immediate uint32 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_u32_to_r_f32(x86::Assembler &a, int32 reg_no, uint32 data) +{ + mov_imm_to_r_i64(a, REG_I64_FREE_IDX, (int64)(uint64)data); + a.cvtsi2ss(regs_float[reg_no], regs_i64[REG_I64_FREE_IDX]); + return true; +} + +/** + * Encode converting uint32 register data to float register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst uint32 register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +convert_r_u32_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + extend_r32_to_r64(a, REG_I64_FREE_IDX, reg_no_src, false); + a.cvtsi2ss(regs_float[reg_no_dst], regs_i64[REG_I64_FREE_IDX]); + return true; +} + +/** + * Encode converting uint32 immediate data to double register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst double register + * @param data the src immediate uint32 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_u32_to_r_f64(x86::Assembler &a, int32 reg_no, uint32 data) +{ + mov_imm_to_r_i64(a, REG_I64_FREE_IDX, (int64)(uint64)data); + a.cvtsi2sd(regs_float[reg_no], regs_i64[REG_I64_FREE_IDX]); + return true; +} + +/** + * Encode converting uint32 register data to double register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst uint32 register + * @param reg_no_src the no of src double register + * + * @return true if success, false otherwise + */ +static bool +convert_r_u32_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + extend_r32_to_r64(a, REG_I64_FREE_IDX, reg_no_src, false); + a.cvtsi2sd(regs_float[reg_no_dst], regs_i64[REG_I64_FREE_IDX]); + return true; +} + +/** + * Encode converting int64 register data to float register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst float register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i64_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvtsi2ss(regs_float[reg_no_dst], regs_i64[reg_no_src]); + return true; +} + +/** + * Encode converting int64 immediate data to float register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst float register + * @param data the src immediate int64 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i64_to_r_f32(x86::Assembler &a, int32 reg_no, int64 data) +{ + mov_imm_to_r_i64(a, REG_I64_FREE_IDX, data); + return convert_r_i64_to_r_f32(a, reg_no, REG_I64_FREE_IDX); +} + +/** + * Encode converting int64 register data to double register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst double register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +convert_r_i64_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvtsi2sd(regs_float[reg_no_dst], regs_i64[reg_no_src]); + return true; +} + +/** + * Encode converting int64 immediate data to double register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst double register + * @param data the src immediate int64 data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_i64_to_r_f64(x86::Assembler &a, int32 reg_no, int64 data) +{ + mov_imm_to_r_i64(a, REG_I64_FREE_IDX, data); + return convert_r_i64_to_r_f64(a, reg_no, REG_I64_FREE_IDX); +} + +/** + * Encode converting float immediate data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate float data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f32_to_r_i32(x86::Assembler &a, int32 reg_no, float data) +{ + return mov_imm_to_r_i32(a, reg_no, (int32)data); +} + +/** + * Encode converting float register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f32_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvttss2si(regs_i32[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting float immediate data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate float data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f32_to_r_u32(x86::Assembler &a, int32 reg_no, float data) +{ + return mov_imm_to_r_i32(a, reg_no, (uint32)data); +} + +/** + * Encode converting float register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f32_to_r_u32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvttss2si(regs_i64[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting float immediate data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int64 register + * @param data the src immediate float data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f32_to_r_i64(x86::Assembler &a, int32 reg_no, float data) +{ + return mov_imm_to_r_i64(a, reg_no, (int64)data); +} + +/** + * Encode converting float register data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int64 register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f32_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvttss2si(regs_i64[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting float immediate data to double register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst double register + * @param data the src immediate float data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f32_to_r_f64(x86::Assembler &a, int32 reg_no, float data) +{ + return mov_imm_to_r_f64(a, reg_no, (double)data); +} + +/** + * Encode converting float register data to double register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst double register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f32_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvtss2sd(regs_float[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting double immediate data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate double data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f64_to_r_i32(x86::Assembler &a, int32 reg_no, double data) +{ + return mov_imm_to_r_i32(a, reg_no, (int32)data); +} + +/** + * Encode converting double register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src double register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f64_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvttsd2si(regs_i32[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting double immediate data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int64 register + * @param data the src immediate double data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f64_to_r_i64(x86::Assembler &a, int32 reg_no, double data) +{ + return mov_imm_to_r_i64(a, reg_no, (int64)data); +} + +/** + * Encode converting double register data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int64 register + * @param reg_no_src the no of src double register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f64_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvttsd2si(regs_i64[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting double immediate data to float register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst float register + * @param data the src immediate double data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f64_to_r_f32(x86::Assembler &a, int32 reg_no, double data) +{ + return mov_imm_to_r_f32(a, reg_no, (float)data); +} + +/** + * Encode converting double register data to float register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst float register + * @param reg_no_src the no of src double register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f64_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvtsd2ss(regs_float[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode converting double immediate data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate double data + * + * @return true if success, false otherwise + */ +static bool +convert_imm_f64_to_r_u32(x86::Assembler &a, int32 reg_no, double data) +{ + return mov_imm_to_r_i32(a, reg_no, (uint32)data); +} + +/** + * Encode converting double register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src double register + * + * @return true if success, false otherwise + */ +static bool +convert_r_f64_to_r_u32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.cvttsd2si(regs_i64[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode making negative from int32 immediate data to int32 register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst register + * @param data the src int32 immediate data + * + * @return true if success, false otherwise + */ +static bool +neg_imm_to_r_i32(x86::Assembler &a, int32 reg_no, int32 data) +{ + Imm imm(-data); + a.mov(regs_i32[reg_no], imm); + return true; +} + +/** + * Encode making negative from int32 register to int32 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +neg_r_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i32(a, reg_no_dst, reg_no_src); + a.neg(regs_i32[reg_no_dst]); + return true; +} + +/** + * Encode making negative from int64 immediate data to int64 register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst register + * @param data the src int64 immediate data + * + * @return true if success, false otherwise + */ +static bool +neg_imm_to_r_i64(x86::Assembler &a, int32 reg_no, int64 data) +{ + Imm imm(-data); + a.mov(regs_i64[reg_no], imm); + return true; +} + +/** + * Encode making negative from int64 register to int64 register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +neg_r_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + mov_r_to_r_i64(a, reg_no_dst, reg_no_src); + a.neg(regs_i64[reg_no_dst]); + return true; +} + +/** + * Encode making negative from float immediate data to float register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst float register + * @param data the src float immediate data + * + * @return true if success, false otherwise + */ +static bool +neg_imm_to_r_f32(x86::Assembler &a, int32 reg_no, float data) +{ + bh_assert(0); + (void)a; + (void)reg_no; + (void)data; + return false; +} + +/** + * Encode making negative from float register to float register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst register + * @param reg_no_src the no of src register + * + * @return true if success, false otherwise + */ +static bool +neg_r_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + bh_assert(0); + (void)a; + (void)reg_no_dst; + (void)reg_no_src; + return false; +} + +/** + * Encode making negative from double immediate data to double register + * + * @param a the assembler to emit the code + * @param reg_no the no of dst double register + * @param data the src double immediate data + * + * @return true if success, false otherwise + */ +static bool +neg_imm_to_r_f64(x86::Assembler &a, int32 reg_no, double data) +{ + bh_assert(0); + (void)a; + (void)reg_no; + (void)data; + return false; +} + +/** + * Encode making negative from double register to double register + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst double register + * @param reg_no_src the no of src double register + * + * @return true if success, false otherwise + */ +static bool +neg_r_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + bh_assert(0); + (void)a; + (void)reg_no_dst; + (void)reg_no_src; + return false; +} + +static COND_OP +not_cond(COND_OP op) +{ + COND_OP not_list[] = { NE, EQ, LES, LTS, GES, GTS, LEU, LTU, GEU, GTU }; + + bh_assert(op <= LEU); + return not_list[op]; +} + +/** + * Encode int32 alu operation of reg and data, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no the no of register, as first operand, and save result + * @param data the immediate data, as the second operand + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_imm_i32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no_src, int32 data) +{ + Imm imm(data); + + switch (op) { + case ADD: + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no_src); + if (data == 1) + a.inc(regs_i32[reg_no_dst]); + else if (data == -1) + a.dec(regs_i32[reg_no_dst]); + else if (data != 0) + a.add(regs_i32[reg_no_dst], imm); + break; + case SUB: + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no_src); + if (data == -1) + a.inc(regs_i32[reg_no_dst]); + else if (data == 1) + a.dec(regs_i32[reg_no_dst]); + else if (data != 0) + a.sub(regs_i32[reg_no_dst], imm); + break; + case MUL: + if (data == 0) + a.xor_(regs_i32[reg_no_dst], regs_i32[reg_no_dst]); + else if (data == -1) { + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no_src); + a.neg(regs_i32[reg_no_dst]); + } + else if (data == 1) { + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no_src); + } + else if (data > 0 && (data & (data - 1)) == 0x0) { + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no_src); + data = (int32)local_log2(data); + imm.setValue(data); + a.shl(regs_i32[reg_no_dst], imm); + } + else { + a.imul(regs_i32[reg_no_dst], regs_i32[reg_no_src], imm); + } + break; + case DIV_S: + case REM_S: + bh_assert(reg_no_src == REG_EAX_IDX); + if (op == DIV_S) { + bh_assert(reg_no_dst == REG_EAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_EDX_IDX); + } + a.mov(regs_i32[REG_I32_FREE_IDX], imm); + /* signed extend eax to edx:eax */ + a.cdq(); + a.idiv(regs_i32[REG_I32_FREE_IDX]); + break; + case DIV_U: + case REM_U: + bh_assert(reg_no_src == REG_EAX_IDX); + if (op == DIV_U) { + bh_assert(reg_no_dst == REG_EAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_EDX_IDX); + } + a.mov(regs_i32[REG_I32_FREE_IDX], imm); + /* unsigned extend eax to edx:eax */ + a.xor_(regs_i32[REG_EDX_IDX], regs_i32[REG_EDX_IDX]); + a.div(regs_i32[REG_I32_FREE_IDX]); + break; + default: + bh_assert(0); + break; + } + + return true; +} + +/** + * Encode int32 alu operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register, as first operand, and save result + * @param reg_no_src the no of register, as the second operand + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_r_i32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, int32 reg_no1_src, + int32 reg_no2_src) +{ + switch (op) { + case ADD: + if (reg_no_dst != reg_no2_src) { + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no1_src); + a.add(regs_i32[reg_no_dst], regs_i32[reg_no2_src]); + } + else + a.add(regs_i32[reg_no2_src], regs_i32[reg_no1_src]); + break; + case SUB: + if (reg_no_dst != reg_no2_src) { + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no1_src); + a.sub(regs_i32[reg_no_dst], regs_i32[reg_no2_src]); + } + else { + a.sub(regs_i32[reg_no2_src], regs_i32[reg_no1_src]); + a.neg(regs_i32[reg_no2_src]); + } + break; + case MUL: + if (reg_no_dst != reg_no2_src) { + mov_r_to_r(a, JIT_REG_KIND_I32, reg_no_dst, reg_no1_src); + a.imul(regs_i32[reg_no_dst], regs_i32[reg_no2_src]); + } + else + a.imul(regs_i32[reg_no2_src], regs_i32[reg_no1_src]); + break; + case DIV_S: + case REM_S: + bh_assert(reg_no1_src == REG_EAX_IDX); + if (op == DIV_S) { + bh_assert(reg_no_dst == REG_EAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_EDX_IDX); + if (reg_no2_src == REG_EDX_IDX) { + /* convert `REM_S edx, eax, edx` into + `mov esi, edx` and `REM_S edx eax, rsi` to + avoid overwriting edx when a.cdq() */ + a.mov(regs_i32[REG_I32_FREE_IDX], regs_i32[REG_EDX_IDX]); + reg_no2_src = REG_I32_FREE_IDX; + } + } + /* signed extend eax to edx:eax */ + a.cdq(); + a.idiv(regs_i32[reg_no2_src]); + break; + case DIV_U: + case REM_U: + bh_assert(reg_no1_src == REG_EAX_IDX); + if (op == DIV_U) { + bh_assert(reg_no_dst == REG_EAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_EDX_IDX); + if (reg_no2_src == REG_EDX_IDX) { + /* convert `REM_U edx, eax, edx` into + `mov esi, edx` and `REM_U edx eax, rsi` to + avoid overwriting edx when unsigned extend + eax to edx:eax */ + a.mov(regs_i32[REG_I32_FREE_IDX], regs_i32[REG_EDX_IDX]); + reg_no2_src = REG_I32_FREE_IDX; + } + } + /* unsigned extend eax to edx:eax */ + a.xor_(regs_i32[REG_EDX_IDX], regs_i32[REG_EDX_IDX]); + a.div(regs_i32[reg_no2_src]); + break; + default: + bh_assert(0); + return false; + } + + return true; +} + +/** + * Encode int32 alu operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_imm_to_r_i32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 data1_src, int32 data2_src) +{ + Imm imm; + int32 data = 0; + + switch (op) { + case ADD: + data = data1_src + data2_src; + break; + case SUB: + data = data1_src - data2_src; + break; + case MUL: + data = data1_src * data2_src; + break; + case DIV_S: + data = data1_src / data2_src; + break; + case REM_S: + data = data1_src % data2_src; + break; + case DIV_U: + data = (uint32)data1_src / (uint32)data2_src; + break; + case REM_U: + data = (uint32)data1_src % (uint32)data2_src; + break; + default: + bh_assert(0); + return false; + } + + imm.setValue(data); + a.mov(regs_i32[reg_no_dst], imm); + return true; +} + +/** + * Encode int32 alu operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_r_to_r_i32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 data1_src, int32 reg_no2_src) +{ + if (op == ADD || op == MUL) + return alu_r_r_imm_i32(a, op, reg_no_dst, reg_no2_src, data1_src); + else if (op == SUB) { + if (!alu_r_r_imm_i32(a, op, reg_no_dst, reg_no2_src, data1_src)) + return false; + a.neg(regs_i32[reg_no_dst]); + return true; + } + else { + if (reg_no_dst != reg_no2_src) { + if (!mov_imm_to_r_i32(a, reg_no_dst, data1_src) + || !alu_r_r_r_i32(a, op, reg_no_dst, reg_no_dst, reg_no2_src)) + return false; + return true; + } + else { + if (!mov_imm_to_r_i32(a, REG_I32_FREE_IDX, data1_src) + || !alu_r_r_r_i32(a, op, reg_no_dst, REG_I32_FREE_IDX, + reg_no2_src)) + return false; + return true; + } + } + + return true; +} + +/** + * Encode int32 alu operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_r_imm_to_r_i32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 data2_src) +{ + return alu_r_r_imm_i32(a, op, reg_no_dst, reg_no1_src, data2_src); +} + +/** + * Encode int32 alu operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_to_r_i32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + return alu_r_r_r_i32(a, op, reg_no_dst, reg_no1_src, reg_no2_src); +} + +/** + * Encode int64 alu operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register, as first operand, and save result + * @param reg_no_src the no of register, as the second operand + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_r_i64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, int32 reg_no1_src, + int32 reg_no2_src) +{ + switch (op) { + case ADD: + if (reg_no_dst != reg_no2_src) { + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no1_src); + a.add(regs_i64[reg_no_dst], regs_i64[reg_no2_src]); + } + else + a.add(regs_i64[reg_no2_src], regs_i64[reg_no1_src]); + break; + case SUB: + if (reg_no_dst != reg_no2_src) { + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no1_src); + a.sub(regs_i64[reg_no_dst], regs_i64[reg_no2_src]); + } + else { + a.sub(regs_i64[reg_no2_src], regs_i64[reg_no1_src]); + a.neg(regs_i64[reg_no2_src]); + } + break; + case MUL: + if (reg_no_dst != reg_no2_src) { + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no1_src); + a.imul(regs_i64[reg_no_dst], regs_i64[reg_no2_src]); + } + else + a.imul(regs_i64[reg_no2_src], regs_i64[reg_no1_src]); + break; + case DIV_S: + case REM_S: + bh_assert(reg_no1_src == REG_RAX_IDX); + if (op == DIV_S) { + bh_assert(reg_no_dst == REG_RAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_RDX_IDX); + } + /* signed extend rax to rdx:rax */ + a.cqo(); + a.idiv(regs_i64[reg_no2_src]); + break; + case DIV_U: + case REM_U: + bh_assert(reg_no1_src == REG_RAX_IDX); + if (op == DIV_U) { + bh_assert(reg_no_dst == REG_RAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_RDX_IDX); + } + /* unsigned extend rax to rdx:rax */ + a.xor_(regs_i64[REG_RDX_IDX], regs_i64[REG_RDX_IDX]); + a.div(regs_i64[reg_no2_src]); + break; + default: + bh_assert(0); + break; + } + + return true; +} + +/** + * Encode int64 alu operation of reg and data, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no the no of register, as first operand, and save result + * @param data the immediate data, as the second operand + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_imm_i64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no_src, int64 data) +{ + Imm imm(data); + + switch (op) { + case ADD: + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no_src); + if (data == 1) + a.inc(regs_i64[reg_no_dst]); + else if (data == -1) + a.dec(regs_i64[reg_no_dst]); + else if (data != 0) { + if (data >= INT32_MIN && data <= INT32_MAX) { + imm.setValue((int32)data); + a.add(regs_i64[reg_no_dst], imm); + } + else { + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.add(regs_i64[reg_no_dst], regs_i64[REG_I64_FREE_IDX]); + } + } + break; + case SUB: + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no_src); + if (data == -1) + a.inc(regs_i64[reg_no_dst]); + else if (data == 1) + a.dec(regs_i64[reg_no_dst]); + else if (data != 0) { + if (data >= INT32_MIN && data <= INT32_MAX) { + imm.setValue((int32)data); + a.sub(regs_i64[reg_no_dst], imm); + } + else { + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.sub(regs_i64[reg_no_dst], regs_i64[REG_I64_FREE_IDX]); + } + } + break; + case MUL: + if (data == 0) + a.xor_(regs_i64[reg_no_dst], regs_i64[reg_no_dst]); + else if (data == -1) { + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no_src); + a.neg(regs_i64[reg_no_dst]); + } + else if (data == 1) { + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no_src); + } + else if (data > 0 && (data & (data - 1)) == 0x0) { + mov_r_to_r(a, JIT_REG_KIND_I64, reg_no_dst, reg_no_src); + data = (int64)local_log2l(data); + imm.setValue(data); + a.shl(regs_i64[reg_no_dst], imm); + } + else if (INT32_MIN <= data && data <= INT32_MAX) { + a.imul(regs_i64[reg_no_dst], regs_i64[reg_no_src], imm); + } + else { + mov_imm_to_r_i64( + a, reg_no_dst == reg_no_src ? REG_I64_FREE_IDX : reg_no_dst, + data); + alu_r_r_r_i64(a, op, reg_no_dst, + reg_no_dst == reg_no_src ? REG_I64_FREE_IDX + : reg_no_dst, + reg_no_src); + } + break; + case DIV_S: + case REM_S: + bh_assert(reg_no_src == REG_RAX_IDX); + if (op == DIV_S) { + bh_assert(reg_no_dst == REG_RAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_RDX_IDX); + } + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + /* signed extend rax to rdx:rax */ + a.cqo(); + a.idiv(regs_i64[REG_I64_FREE_IDX]); + break; + case DIV_U: + case REM_U: + bh_assert(reg_no_src == REG_RAX_IDX); + if (op == DIV_U) { + bh_assert(reg_no_dst == REG_RAX_IDX); + } + else { + bh_assert(reg_no_dst == REG_RDX_IDX); + } + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + /* unsigned extend rax to rdx:rax */ + a.xor_(regs_i64[REG_RDX_IDX], regs_i64[REG_RDX_IDX]); + a.div(regs_i64[REG_I64_FREE_IDX]); + break; + default: + bh_assert(0); + break; + } + + return true; +} + +/** + * Encode int64 alu operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_imm_to_r_i64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int64 data1_src, int64 data2_src) +{ + Imm imm; + int64 data = 0; + + switch (op) { + case ADD: + data = data1_src + data2_src; + break; + case SUB: + data = data1_src - data2_src; + break; + case MUL: + data = data1_src * data2_src; + break; + case DIV_S: + data = data1_src / data2_src; + break; + case REM_S: + data = data1_src % data2_src; + break; + case DIV_U: + data = (uint64)data1_src / (uint64)data2_src; + break; + case REM_U: + data = (uint64)data1_src % (uint64)data2_src; + break; + default: + bh_assert(0); + break; + } + + imm.setValue(data); + a.mov(regs_i64[reg_no_dst], imm); + return true; +} + +/** + * Encode int64 alu operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_r_to_r_i64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int64 data1_src, int32 reg_no2_src) +{ + if (op == ADD || op == MUL) + return alu_r_r_imm_i64(a, op, reg_no_dst, reg_no2_src, data1_src); + else if (op == SUB) { + if (!alu_r_r_imm_i64(a, op, reg_no_dst, reg_no2_src, data1_src)) + return false; + a.neg(regs_i64[reg_no_dst]); + return true; + } + else { + if (reg_no_dst != reg_no2_src) { + if (!mov_imm_to_r_i64(a, reg_no_dst, data1_src) + || !alu_r_r_r_i64(a, op, reg_no_dst, reg_no_dst, reg_no2_src)) + return false; + return true; + } + else { + if (!mov_imm_to_r_i64(a, REG_I64_FREE_IDX, data1_src) + || !alu_r_r_r_i64(a, op, reg_no_dst, REG_I64_FREE_IDX, + reg_no2_src)) + return false; + return true; + } + } + + return true; +} + +/** + * Encode int64 alu operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_r_imm_to_r_i64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, int64 data2_src) +{ + return alu_r_r_imm_i64(a, op, reg_no_dst, reg_no1_src, data2_src); +} + +/** + * Encode int64 alu operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_to_r_i64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + return alu_r_r_r_i64(a, op, reg_no_dst, reg_no1_src, reg_no2_src); +} + +/** + * Encode float alu operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_imm_to_r_f32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + float data1_src, float data2_src) +{ + Imm imm; + float data = 0; + + switch (op) { + case ADD: + { + data = data1_src + data2_src; + break; + } + case SUB: + { + data = data1_src - data2_src; + break; + } + case MUL: + { + data = data1_src * data2_src; + break; + } + case DIV_S: + { + data = data1_src / data2_src; + break; + } + case MAX: + { + data = fmaxf(data1_src, data2_src); + break; + } + case MIN: + { + data = fminf(data1_src, data2_src); + break; + } + default: + { + bh_assert(0); + return false; + } + } + + return mov_imm_to_r_f32(a, reg_no_dst, data); +} + +static bool +alu_r_m_float(x86::Assembler &a, ALU_OP op, int32 reg_no, x86::Mem &m, + bool is_f32) +{ + switch (op) { + case ADD: + { + if (is_f32) + a.addss(regs_float[reg_no], m); + else + a.addsd(regs_float[reg_no], m); + break; + } + case SUB: + { + if (is_f32) + a.subss(regs_float[reg_no], m); + else + a.subsd(regs_float[reg_no], m); + break; + } + case MUL: + { + if (is_f32) + a.mulss(regs_float[reg_no], m); + else + a.mulsd(regs_float[reg_no], m); + break; + } + case DIV_S: + { + if (is_f32) + a.divss(regs_float[reg_no], m); + else + a.divsd(regs_float[reg_no], m); + break; + } + case MAX: + { + if (is_f32) + a.maxss(regs_float[reg_no], m); + else + a.maxsd(regs_float[reg_no], m); + break; + } + case MIN: + { + if (is_f32) + a.minss(regs_float[reg_no], m); + else + a.minsd(regs_float[reg_no], m); + break; + } + default: + { + bh_assert(0); + return false; + } + } + return true; +} + +/** + * Encode float alu operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_r_to_r_f32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + float data1_src, int32 reg_no2_src) +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + /* xmm -> m128 */ + x86::Mem cache = x86::xmmword_ptr(regs_i64[hreg_info->exec_env_hreg_index], + offsetof(WASMExecEnv, jit_cache)); + a.movups(cache, regs_float[reg_no2_src]); + + /* imm -> gp -> xmm */ + mov_imm_to_r_f32(a, reg_no_dst, data1_src); + + return alu_r_m_float(a, op, reg_no_dst, cache, true); +} + +/** + * Encode float alu operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +alu_r_imm_to_r_f32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, float data2_src) +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + /* imm -> m32 */ + x86::Mem cache = x86::dword_ptr(regs_i64[hreg_info->exec_env_hreg_index], + offsetof(WASMExecEnv, jit_cache)); + cast_float_to_integer v = { .f = data2_src }; + Imm imm(v.i); + mov_imm_to_m(a, cache, imm, 4); + + mov_r_to_r_f32(a, reg_no_dst, reg_no1_src); + return alu_r_m_float(a, op, reg_no_dst, cache, true); +} + +/** + * Encode float alu operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_to_r_f32(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + bool store_result = false; + + /** + * - op r0,r0,r1. do nothing since instructions always store results in + * the first register + * + * - op r1,r0,r1. use FREE_REG to cache and replace r0, and then store + * results in r1 + * + * - op r0,r1,r2. use r0 to cache and replace r1, and accept the result + * naturally + **/ + if (reg_no_dst == reg_no2_src) { + store_result = true; + reg_no_dst = REG_F32_FREE_IDX; + } + mov_r_to_r_f32(a, reg_no_dst, reg_no1_src); + + switch (op) { + case ADD: + { + a.addss(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case SUB: + { + a.subss(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case MUL: + { + a.mulss(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case DIV_S: + { + a.divss(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case MAX: + { + a.maxss(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case MIN: + { + a.minss(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + default: + { + bh_assert(0); + return false; + } + } + + if (store_result) + mov_r_to_r_f32(a, reg_no2_src, REG_F32_FREE_IDX); + + return true; +} + +/** + * Encode double alu operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_imm_to_r_f64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + double data1_src, double data2_src) +{ + Imm imm; + double data = 0; + + switch (op) { + case ADD: + { + data = data1_src + data2_src; + break; + } + case SUB: + { + data = data1_src - data2_src; + break; + } + case MUL: + { + data = data1_src * data2_src; + break; + } + case DIV_S: + { + data = data1_src / data2_src; + break; + } + case MAX: + { + data = fmax(data1_src, data2_src); + break; + } + case MIN: + { + data = fmin(data1_src, data2_src); + break; + } + default: + { + bh_assert(0); + return false; + } + } + + return mov_imm_to_r_f64(a, reg_no_dst, data); +} + +/** + * Encode double alu operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_imm_r_to_r_f64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + double data1_src, int32 reg_no2_src) +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + /* xmm -> m128 */ + x86::Mem cache = x86::qword_ptr(regs_i64[hreg_info->exec_env_hreg_index], + offsetof(WASMExecEnv, jit_cache)); + a.movupd(cache, regs_float[reg_no2_src]); + + /* imm -> gp -> xmm */ + mov_imm_to_r_f64(a, reg_no_dst, data1_src); + + return alu_r_m_float(a, op, reg_no_dst, cache, false); +} + +/** + * Encode double alu operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +alu_r_imm_to_r_f64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, double data2_src) +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + /* imm -> m64 */ + x86::Mem cache = x86::qword_ptr(regs_i64[hreg_info->exec_env_hreg_index], + offsetof(WASMExecEnv, jit_cache)); + cast_double_to_integer v = { .d = data2_src }; + Imm imm(v.i); + mov_imm_to_m(a, cache, imm, 8); + + mov_r_to_r_f64(a, reg_no_dst, reg_no1_src); + return alu_r_m_float(a, op, reg_no_dst, cache, false); +} + +/** + * Encode double alu operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of ALU operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +alu_r_r_to_r_f64(x86::Assembler &a, ALU_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + bool store_result = false; + + /** + * - op r0,r0,r1. do nothing since instructions always store results in + * the first register + * + * - op r1,r0,r1. use FREE_REG to cache and replace r0, and then store + * results in r1 + * + * - op r0,r1,r2. use r0 to cache and replace r1, and accept the result + * naturally + **/ + if (reg_no_dst == reg_no2_src) { + store_result = true; + reg_no_dst = REG_F64_FREE_IDX; + } + mov_r_to_r_f64(a, reg_no_dst, reg_no1_src); + + switch (op) { + case ADD: + { + a.addsd(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case SUB: + { + a.subsd(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case MUL: + { + a.mulsd(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case DIV_S: + { + a.divsd(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case MAX: + { + a.maxsd(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + case MIN: + { + a.minsd(regs_float[reg_no_dst], regs_float[reg_no2_src]); + break; + } + default: + { + bh_assert(0); + return false; + } + } + + if (store_result) + mov_r_to_r_f64(a, reg_no2_src, REG_F64_FREE_IDX); + + return true; +} + +/** + * Encode int32 bit operation of reg and data, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no the no of register, as first operand, and save result + * @param data the immediate data, as the second operand + * + * @return true if success, false otherwise + */ +static bool +bit_r_imm_i32(x86::Assembler &a, BIT_OP op, int32 reg_no, int32 data) +{ + Imm imm(data); + + switch (op) { + case OR: + if (data != 0) + a.or_(regs_i32[reg_no], imm); + break; + case XOR: + if (data == -1) + a.not_(regs_i32[reg_no]); + else if (data != 0) + a.xor_(regs_i32[reg_no], imm); + break; + case AND: + if (data != -1) + a.and_(regs_i32[reg_no], imm); + break; + default: + bh_assert(0); + break; + } + return true; +} + +/** + * Encode int32 bit operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register, as first operand, and save result + * @param reg_no_src the no of register, as second operand + * + * @return true if success, false otherwise + */ +static bool +bit_r_r_i32(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, int32 reg_no_src) +{ + switch (op) { + case OR: + a.or_(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + break; + case XOR: + a.xor_(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + break; + case AND: + a.and_(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + break; + default: + bh_assert(0); + break; + } + return true; +} + +/** + * Encode int32 bit operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +bit_imm_imm_to_r_i32(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 data1_src, int32 data2_src) +{ + Imm imm; + + switch (op) { + case OR: + imm.setValue(data1_src | data2_src); + break; + case XOR: + imm.setValue(data1_src ^ data2_src); + break; + case AND: + imm.setValue(data1_src & data2_src); + break; + default: + bh_assert(0); + break; + } + + a.mov(regs_i32[reg_no_dst], imm); + return true; +} + +/** + * Encode int32 bit operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +bit_imm_r_to_r_i32(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 data1_src, int32 reg_no2_src) +{ + if (op == AND && data1_src == 0) + a.xor_(regs_i32[reg_no_dst], regs_i32[reg_no_dst]); + else if (op == OR && data1_src == -1) { + Imm imm(-1); + a.mov(regs_i32[reg_no_dst], imm); + } + else { + mov_r_to_r_i32(a, reg_no_dst, reg_no2_src); + return bit_r_imm_i32(a, op, reg_no_dst, data1_src); + } + return true; +} + +/** + * Encode int32 bit operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +bit_r_imm_to_r_i32(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 data2_src) +{ + return bit_imm_r_to_r_i32(a, op, reg_no_dst, data2_src, reg_no1_src); +} + +/** + * Encode int32 bit operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +bit_r_r_to_r_i32(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + if (reg_no_dst != reg_no2_src) { + mov_r_to_r_i32(a, reg_no_dst, reg_no1_src); + return bit_r_r_i32(a, op, reg_no_dst, reg_no2_src); + } + else + return bit_r_r_i32(a, op, reg_no_dst, reg_no1_src); + return false; +} + +/** + * Encode int64 bit operation of reg and data, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no the no of register, as first operand, and save result + * @param data the immediate data, as the second operand + * + * @return true if success, false otherwise + */ +static bool +bit_r_imm_i64(x86::Assembler &a, BIT_OP op, int32 reg_no, int64 data) +{ + Imm imm(data); + + switch (op) { + case OR: + if (data != 0) { + if (data >= INT32_MIN && data <= INT32_MAX) { + imm.setValue((int32)data); + a.or_(regs_i64[reg_no], imm); + } + else { + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.or_(regs_i64[reg_no], regs_i64[REG_I64_FREE_IDX]); + } + } + break; + case XOR: + if (data == -1LL) + a.not_(regs_i64[reg_no]); + else if (data != 0) { + if (data >= INT32_MIN && data <= INT32_MAX) { + imm.setValue((int32)data); + a.xor_(regs_i64[reg_no], imm); + } + else { + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.xor_(regs_i64[reg_no], regs_i64[REG_I64_FREE_IDX]); + } + } + break; + case AND: + if (data != -1LL) { + if (data >= INT32_MIN && data <= INT32_MAX) { + imm.setValue((int32)data); + a.and_(regs_i64[reg_no], imm); + } + else { + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.and_(regs_i64[reg_no], regs_i64[REG_I64_FREE_IDX]); + } + } + break; + default: + bh_assert(0); + break; + } + return true; +} + +/** + * Encode int64 bit operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register, as first operand, and save result + * @param reg_no_src the no of register, as second operand + * + * @return true if success, false otherwise + */ +static bool +bit_r_r_i64(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, int32 reg_no_src) +{ + switch (op) { + case OR: + a.or_(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + break; + case XOR: + a.xor_(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + break; + case AND: + a.and_(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + break; + default: + bh_assert(0); + break; + } + return true; +} + +/** + * Encode int64 bit operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +bit_imm_imm_to_r_i64(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 data1_src, int64 data2_src) +{ + Imm imm; + + switch (op) { + case OR: + imm.setValue(data1_src | data2_src); + break; + case XOR: + imm.setValue(data1_src ^ data2_src); + break; + case AND: + imm.setValue(data1_src & data2_src); + break; + default: + bh_assert(0); + break; + } + + a.mov(regs_i64[reg_no_dst], imm); + return true; +} + +/** + * Encode int64 bit operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +bit_imm_r_to_r_i64(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int64 data1_src, int32 reg_no2_src) +{ + if (op == AND && data1_src == 0) + a.xor_(regs_i64[reg_no_dst], regs_i64[reg_no_dst]); + else if (op == OR && data1_src == -1LL) { + Imm imm(-1LL); + a.mov(regs_i64[reg_no_dst], imm); + } + else { + mov_r_to_r_i64(a, reg_no_dst, reg_no2_src); + return bit_r_imm_i64(a, op, reg_no_dst, data1_src); + } + return true; +} + +/** + * Encode int64 bit operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +bit_r_imm_to_r_i64(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int64 data2_src) +{ + return bit_imm_r_to_r_i64(a, op, reg_no_dst, data2_src, reg_no1_src); +} + +/** + * Encode int64 bit operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BIT operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +bit_r_r_to_r_i64(x86::Assembler &a, BIT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + if (reg_no_dst != reg_no2_src) { + mov_r_to_r_i64(a, reg_no_dst, reg_no1_src); + return bit_r_r_i64(a, op, reg_no_dst, reg_no2_src); + } + else + return bit_r_r_i64(a, op, reg_no_dst, reg_no1_src); + return false; +} + +/** + * Encode int32 shift operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of SHIFT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +shift_imm_imm_to_r_i32(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int32 data1_src, int32 data2_src) +{ + int32 data; + switch (op) { + case SHL: + { + data = data1_src << data2_src; + break; + } + case SHRS: + { + data = data1_src >> data2_src; + break; + } + case SHRU: + { + data = ((uint32)data1_src) >> data2_src; + break; + } + case ROTL: + { + data = (data1_src << data2_src) + | (((uint32)data1_src) >> (32 - data2_src)); + break; + } + case ROTR: + { + data = (((uint32)data1_src) >> data2_src) + | (data1_src << (32 - data2_src)); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return mov_imm_to_r_i32(a, reg_no_dst, data); +fail: + return false; +} + +/** + * Encode int32 shift operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of SHIFT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +shift_imm_r_to_r_i32(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int32 data1_src, int32 reg_no2_src) +{ + /* Should have been optimized by previous lower */ + bh_assert(0); + (void)a; + (void)op; + (void)reg_no_dst; + (void)data1_src; + (void)reg_no2_src; + return false; +} + +/** + * Encode int32 shift operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of SHIFT operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +shift_r_imm_to_r_i32(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 data2_src) +{ + /* SHL/SHA/SHR r/m32, imm8 */ + Imm imm((uint8)data2_src); + + mov_r_to_r_i32(a, reg_no_dst, reg_no1_src); + switch (op) { + case SHL: + { + a.shl(regs_i32[reg_no_dst], imm); + break; + } + case SHRS: + { + a.sar(regs_i32[reg_no_dst], imm); + break; + } + case SHRU: + { + a.shr(regs_i32[reg_no_dst], imm); + break; + } + case ROTL: + { + a.rol(regs_i32[reg_no_dst], imm); + break; + } + case ROTR: + { + a.ror(regs_i32[reg_no_dst], imm); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return true; +fail: + return false; +} + +/** + * Encode int32 shift operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of shift operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +shift_r_r_to_r_i32(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + /* should be CL */ + if (reg_no2_src != REG_ECX_IDX) + return false; + + mov_r_to_r_i32(a, reg_no_dst, reg_no1_src); + + switch (op) { + case SHL: + { + a.shl(regs_i32[reg_no_dst], x86::cl); + break; + } + case SHRS: + { + a.sar(regs_i32[reg_no_dst], x86::cl); + break; + } + case SHRU: + { + a.shr(regs_i32[reg_no_dst], x86::cl); + break; + } + case ROTL: + { + a.rol(regs_i32[reg_no_dst], x86::cl); + break; + } + case ROTR: + { + a.ror(regs_i32[reg_no_dst], x86::cl); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return true; +fail: + return false; +} + +/** + * Encode int64 shift operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of SHIFT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +shift_imm_imm_to_r_i64(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int64 data1_src, int64 data2_src) +{ + int64 data; + + switch (op) { + case SHL: + { + data = data1_src << data2_src; + break; + } + case SHRS: + { + data = data1_src >> data2_src; + break; + } + case SHRU: + { + data = ((uint64)data1_src) >> data2_src; + break; + } + case ROTL: + { + data = (data1_src << data2_src) + | (((uint64)data1_src) >> (64LL - data2_src)); + break; + } + case ROTR: + { + data = (((uint64)data1_src) >> data2_src) + | (data1_src << (64LL - data2_src)); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return mov_imm_to_r_i64(a, reg_no_dst, data); +fail: + return false; +} + +/** + * Encode int64 shift operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of SHIFT operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +shift_imm_r_to_r_i64(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int64 data1_src, int32 reg_no2_src) +{ + /* Should have been optimized by previous lower */ + bh_assert(0); + (void)a; + (void)op; + (void)reg_no_dst; + (void)data1_src; + (void)reg_no2_src; + return false; +} + +/** + * Encode int64 shift operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of SHIFT operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +shift_r_imm_to_r_i64(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int64 data2_src) +{ + /* SHL/SHA/SHR r/m64, imm8 */ + Imm imm((uint8)data2_src); + + mov_r_to_r_i64(a, reg_no_dst, reg_no1_src); + switch (op) { + case SHL: + { + a.shl(regs_i64[reg_no_dst], imm); + break; + } + case SHRS: + { + a.sar(regs_i64[reg_no_dst], imm); + break; + } + case SHRU: + { + a.shr(regs_i64[reg_no_dst], imm); + break; + } + case ROTL: + { + a.rol(regs_i64[reg_no_dst], imm); + break; + } + case ROTR: + { + a.ror(regs_i64[reg_no_dst], imm); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return true; +fail: + return false; +} + +/** + * Encode int64 shift operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of shift operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +shift_r_r_to_r_i64(x86::Assembler &a, SHIFT_OP op, int32 reg_no_dst, + int32 reg_no1_src, int32 reg_no2_src) +{ + /* should be CL */ + if (reg_no2_src != REG_ECX_IDX) + return false; + + mov_r_to_r_i64(a, reg_no_dst, reg_no1_src); + + switch (op) { + case SHL: + { + a.shl(regs_i64[reg_no_dst], x86::cl); + break; + } + case SHRS: + { + a.sar(regs_i64[reg_no_dst], x86::cl); + break; + } + case SHRU: + { + a.shr(regs_i64[reg_no_dst], x86::cl); + break; + } + case ROTL: + { + a.rol(regs_i64[reg_no_dst], x86::cl); + break; + } + case ROTR: + { + a.ror(regs_i64[reg_no_dst], x86::cl); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return true; +fail: + return false; +} + +/** + * Encode int32 cmp operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_imm_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 data1_src, + int32 data2_src) +{ + Imm imm(data1_src); + a.mov(regs_i32[REG_I32_FREE_IDX], imm); + imm.setValue(data2_src); + a.cmp(regs_i32[REG_I32_FREE_IDX], imm); + (void)reg_no_dst; + return true; +} + +/** + * Encode int32 cmp operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_r_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 data1_src, + int32 reg_no2_src) +{ + Imm imm(data1_src); + a.mov(regs_i32[REG_I32_FREE_IDX], imm); + a.cmp(regs_i32[REG_I32_FREE_IDX], regs_i32[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode int32 cmp operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_imm_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + int32 data2_src) +{ + Imm imm(data2_src); + a.cmp(regs_i32[reg_no1_src], imm); + (void)reg_no_dst; + return true; +} + +/** + * Encode int32 cmp operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_r_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + int32 reg_no2_src) +{ + a.cmp(regs_i32[reg_no1_src], regs_i32[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode int64 cmp operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_imm_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int64 data1_src, + int64 data2_src) +{ + /* imm -> m64 */ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + x86::Mem mem = x86::qword_ptr(regs_i64[hreg_info->exec_env_hreg_index], + offsetof(WASMExecEnv, jit_cache)); + Imm imm(data2_src); + mov_imm_to_m(a, mem, imm, 8); + + a.mov(regs_i64[REG_I64_FREE_IDX], data1_src); + a.cmp(regs_i64[REG_I64_FREE_IDX], mem); + (void)reg_no_dst; + return true; +} + +/** + * Encode int64 cmp operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_r_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int64 data1_src, + int32 reg_no2_src) +{ + Imm imm(data1_src); + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.cmp(regs_i64[REG_I64_FREE_IDX], regs_i64[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode int64 cmp operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_imm_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + int64 data2_src) +{ + Imm imm(data2_src); + + if (data2_src >= INT32_MIN && data2_src <= INT32_MAX) { + imm.setValue((int32)data2_src); + a.cmp(regs_i64[reg_no1_src], imm); + } + else { + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.cmp(regs_i64[reg_no1_src], regs_i64[REG_I64_FREE_IDX]); + } + (void)reg_no_dst; + return true; +} + +/** + * Encode int64 cmp operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_r_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + int32 reg_no2_src) +{ + a.cmp(regs_i64[reg_no1_src], regs_i64[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode float cmp operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_r_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + int32 reg_no2_src) +{ + a.comiss(regs_float[reg_no1_src], regs_float[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode float cmp operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_imm_to_r_f32(x86::Assembler &a, int32 reg_no_dst, float data1_src, + float data2_src) +{ + /* should have been optimized in the frontend */ + bh_assert(0); + (void)a; + (void)reg_no_dst; + (void)data1_src; + (void)data2_src; + return false; +} + +/** + * Encode float cmp operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_r_to_r_f32(x86::Assembler &a, int32 reg_no_dst, float data1_src, + int32 reg_no2_src) +{ + mov_imm_to_r_f32(a, REG_F32_FREE_IDX, data1_src); + a.comiss(regs_float[REG_F32_FREE_IDX], regs_float[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode float cmp operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_imm_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + float data2_src) +{ + mov_imm_to_r_f32(a, REG_F32_FREE_IDX, data2_src); + a.comiss(regs_float[reg_no1_src], regs_float[REG_F32_FREE_IDX]); + (void)reg_no_dst; + return true; +} + +/** + * Encode double cmp operation of reg and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_r_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + int32 reg_no2_src) +{ + a.comisd(regs_float[reg_no1_src], regs_float[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode double cmp operation of imm and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_imm_to_r_f64(x86::Assembler &a, int32 reg_no_dst, double data1_src, + double data2_src) +{ + /* should have been optimized in the frontend */ + bh_assert(0); + (void)a; + (void)reg_no_dst; + (void)data1_src; + (void)data2_src; + return false; +} + +/** + * Encode double cmp operation of imm and reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param data1_src the first src immediate data + * @param reg_no2_src the reg no of second src register data + * + * @return true if success, false otherwise + */ +static bool +cmp_imm_r_to_r_f64(x86::Assembler &a, int32 reg_no_dst, double data1_src, + int32 reg_no2_src) +{ + mov_imm_to_r_f64(a, REG_F64_FREE_IDX, data1_src); + a.comisd(regs_float[REG_F64_FREE_IDX], regs_float[reg_no2_src]); + (void)reg_no_dst; + return true; +} + +/** + * Encode double cmp operation of reg and imm, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of cmp operation + * @param reg_no_dst the no of register + * @param reg_no1_src the reg no of first src register data + * @param data2_src the second src immediate data + * + * @return true if success, false otherwise + */ +static bool +cmp_r_imm_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no1_src, + double data2_src) +{ + mov_imm_to_r_f64(a, REG_F64_FREE_IDX, data2_src); + a.comisd(regs_float[reg_no1_src], regs_float[REG_F64_FREE_IDX]); + (void)reg_no_dst; + return true; +} + +/** + * Encode insn ld: LD_type r0, r1, r2 + * @param kind the data kind, such as I32, I64, F32 and F64 + * @param bytes_dst the byte number of dst data + * @param is_signed the data is signed or unsigned + */ +#define LD_R_R_R(kind, bytes_dst, is_signed) \ + do { \ + int32 reg_no_dst = 0, reg_no_base = 0, reg_no_offset = 0; \ + int32 base = 0, offset = 0; \ + bool _ret = false; \ + \ + if (jit_reg_is_const(r1)) { \ + CHECK_KIND(r1, JIT_REG_KIND_I32); \ + } \ + else { \ + CHECK_KIND(r1, JIT_REG_KIND_I64); \ + } \ + if (jit_reg_is_const(r2)) { \ + CHECK_KIND(r2, JIT_REG_KIND_I32); \ + } \ + else { \ + CHECK_KIND(r2, JIT_REG_KIND_I64); \ + } \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + if (jit_reg_is_const(r1)) \ + base = jit_cc_get_const_I32(cc, r1); \ + else { \ + reg_no_base = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_base, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r2)) \ + offset = jit_cc_get_const_I32(cc, r2); \ + else { \ + reg_no_offset = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_offset, jit_reg_kind(r2)); \ + } \ + \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = ld_r_from_base_imm_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, is_signed, reg_no_dst, \ + base, offset); \ + else \ + _ret = ld_r_from_base_imm_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, is_signed, reg_no_dst, \ + base, reg_no_offset); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = ld_r_from_base_r_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, is_signed, reg_no_dst, \ + reg_no_base, offset); \ + else \ + _ret = ld_r_from_base_r_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, is_signed, reg_no_dst, \ + reg_no_base, reg_no_offset); \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode insn sd: ST_type r0, r1, r2 + * @param kind the data kind, such as I32, I64, F32 and F64 + * @param bytes_dst the byte number of dst data + * @param atomic whether it's atomic store + */ +#define ST_R_R_R(kind, type, bytes_dst, atomic) \ + do { \ + type data_src = 0; \ + int32 reg_no_src = 0, reg_no_base = 0, reg_no_offset = 0; \ + int32 base = 0, offset = 0; \ + bool _ret = false; \ + \ + if (jit_reg_is_const(r1)) { \ + CHECK_KIND(r1, JIT_REG_KIND_I32); \ + } \ + else { \ + CHECK_KIND(r1, JIT_REG_KIND_I64); \ + } \ + if (jit_reg_is_const(r2)) { \ + CHECK_KIND(r2, JIT_REG_KIND_I32); \ + } \ + else { \ + CHECK_KIND(r2, JIT_REG_KIND_I64); \ + } \ + \ + if (jit_reg_is_const(r0)) \ + data_src = jit_cc_get_const_##kind(cc, r0); \ + else { \ + reg_no_src = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_src, jit_reg_kind(r0)); \ + } \ + if (jit_reg_is_const(r1)) \ + base = jit_cc_get_const_I32(cc, r1); \ + else { \ + reg_no_base = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_base, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r2)) \ + offset = jit_cc_get_const_I32(cc, r2); \ + else { \ + reg_no_offset = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_offset, jit_reg_kind(r2)); \ + } \ + \ + if (jit_reg_is_const(r0)) { \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = st_imm_to_base_imm_offset_imm( \ + a, bytes_dst, &data_src, base, offset, atomic); \ + else \ + _ret = st_imm_to_base_imm_offset_r( \ + a, bytes_dst, &data_src, base, reg_no_offset, atomic); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = st_imm_to_base_r_offset_imm( \ + a, bytes_dst, &data_src, reg_no_base, offset, atomic); \ + else \ + _ret = st_imm_to_base_r_offset_r(a, bytes_dst, &data_src, \ + reg_no_base, reg_no_offset, \ + atomic); \ + } \ + else if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = st_r_to_base_imm_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_src, base, \ + offset, atomic); \ + else \ + _ret = st_r_to_base_imm_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_src, base, \ + reg_no_offset, atomic); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = st_r_to_base_r_offset_imm(a, bytes_dst, \ + JIT_REG_KIND_##kind, reg_no_src, \ + reg_no_base, offset, atomic); \ + else \ + _ret = st_r_to_base_r_offset_r(a, bytes_dst, JIT_REG_KIND_##kind, \ + reg_no_src, reg_no_base, \ + reg_no_offset, atomic); \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode insn mov: MOV r0, r1 + * @param kind the data kind, such as I32, I64, F32 and F64 + * @param Type the data type, such as int32, int64, float32, and float64 + * @param type the abbreviation of data type, such as i32, i64, f32, and f64 + * @param bytes_dst the byte number of dst data + */ +#define MOV_R_R(kind, Type, type) \ + do { \ + bool _ret = false; \ + int32 reg_no_dst = 0, reg_no_src = 0; \ + CHECK_EQKIND(r0, r1); \ + \ + CHECK_NCONST(r0); \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + \ + if (jit_reg_is_const(r1)) { \ + Type data = jit_cc_get_const_##kind(cc, r1); \ + _ret = mov_imm_to_r_##type(a, reg_no_dst, data); \ + } \ + else { \ + reg_no_src = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src, jit_reg_kind(r1)); \ + _ret = mov_r_to_r_##type(a, reg_no_dst, reg_no_src); \ + } \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode mov insn, MOV r0, r1 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the src operand info + * + * @return true if success, false if failed + */ +static bool +lower_mov(JitCompContext *cc, x86::Assembler &a, JitReg r0, JitReg r1) +{ + switch (jit_reg_kind(r0)) { + case JIT_REG_KIND_I32: + MOV_R_R(I32, int32, i32); + break; + case JIT_REG_KIND_I64: + MOV_R_R(I64, int64, i64); + break; + case JIT_REG_KIND_F32: + MOV_R_R(F32, float32, f32); + break; + case JIT_REG_KIND_F64: + MOV_R_R(F64, float64, f64); + break; + default: + LOG_VERBOSE("Invalid reg type of mov: %d\n", jit_reg_kind(r0)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode insn neg: NEG r0, r1 + * @param kind the data kind, such as I32, I64, F32 and F64 + * @param Type the data type, such as int32, int64, float32, and float64 + * @param type the abbreviation of data type, such as i32, i64, f32, and f64 + */ +#define NEG_R_R(kind, Type, type) \ + do { \ + bool _ret = false; \ + int32 reg_no_dst = 0, reg_no_src = 0; \ + CHECK_EQKIND(r0, r1); \ + \ + CHECK_NCONST(r0); \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + \ + if (jit_reg_is_const(r1)) { \ + Type data = jit_cc_get_const_##kind(cc, r1); \ + _ret = neg_imm_to_r_##type(a, reg_no_dst, data); \ + } \ + else { \ + reg_no_src = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src, jit_reg_kind(r1)); \ + _ret = neg_r_to_r_##type(a, reg_no_dst, reg_no_src); \ + } \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode neg insn, NEG r0, r1 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the src operand info + * + * @return true if success, false if failed + */ +static bool +lower_neg(JitCompContext *cc, x86::Assembler &a, JitReg r0, JitReg r1) +{ + switch (jit_reg_kind(r0)) { + case JIT_REG_KIND_I32: + NEG_R_R(I32, int32, i32); + break; + case JIT_REG_KIND_I64: + NEG_R_R(I64, int64, i64); + break; + case JIT_REG_KIND_F32: + NEG_R_R(F32, float32, f32); + break; + case JIT_REG_KIND_F64: + NEG_R_R(F64, float64, f64); + break; + default: + LOG_VERBOSE("Invalid reg type of neg: %d\n", jit_reg_kind(r0)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode insn convert: I32TOI8 r0, r1, or I32TOI16, I32TOF32, F32TOF64, etc. + * @param kind0 the dst JIT_REG_KIND, such as I32, I64, F32 and F64 + * @param kind1 the src JIT_REG_KIND, such as I32, I64, F32 and F64 + * @param type0 the dst data type, such as i8, u8, i16, u16, i32, f32, i64, f32, + * f64 + * @param type1 the src data type, such as i8, u8, i16, u16, i32, f32, i64, f32, + * f64 + */ +#define CONVERT_R_R(kind0, kind1, type0, type1, Type1) \ + do { \ + bool _ret = false; \ + int32 reg_no_dst = 0, reg_no_src = 0; \ + CHECK_KIND(r0, JIT_REG_KIND_##kind0); \ + CHECK_KIND(r1, JIT_REG_KIND_##kind1); \ + \ + CHECK_NCONST(r0); \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + \ + if (jit_reg_is_const(r1)) { \ + Type1 data = jit_cc_get_const_##kind1(cc, r1); \ + _ret = convert_imm_##type1##_to_r_##type0(a, reg_no_dst, data); \ + } \ + else { \ + reg_no_src = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src, jit_reg_kind(r1)); \ + _ret = \ + convert_r_##type1##_to_r_##type0(a, reg_no_dst, reg_no_src); \ + } \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode insn alu: ADD/SUB/MUL/DIV/REM r0, r1, r2 + * @param kind the data kind, such as I32, I64, F32 and F64 + * @param Type the data type, such as int32, int64, float32, and float64 + * @param type the abbreviation of data type, such as i32, i64, f32, and f64 + * @param op the opcode of alu + */ +#define ALU_R_R_R(kind, Type, type, op) \ + do { \ + Type data1, data2; \ + int32 reg_no_dst = 0, reg_no_src1 = 0, reg_no_src2 = 0; \ + bool _ret = false; \ + \ + CHECK_EQKIND(r0, r1); \ + CHECK_EQKIND(r0, r2); \ + memset(&data1, 0, sizeof(Type)); \ + memset(&data2, 0, sizeof(Type)); \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + if (jit_reg_is_const(r1)) \ + data1 = jit_cc_get_const_##kind(cc, r1); \ + else { \ + reg_no_src1 = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src1, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r2)) \ + data2 = jit_cc_get_const_##kind(cc, r2); \ + else { \ + reg_no_src2 = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_src2, jit_reg_kind(r2)); \ + } \ + \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = \ + alu_imm_imm_to_r_##type(a, op, reg_no_dst, data1, data2); \ + else \ + _ret = alu_imm_r_to_r_##type(a, op, reg_no_dst, data1, \ + reg_no_src2); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = \ + alu_r_imm_to_r_##type(a, op, reg_no_dst, reg_no_src1, data2); \ + else \ + _ret = alu_r_r_to_r_##type(a, op, reg_no_dst, reg_no_src1, \ + reg_no_src2); \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode alu insn, ADD/SUB/MUL/DIV/REM r0, r1, r2 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param op the opcode of alu operations + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the first src operand info + * @param r2 src jit register that contains the second src operand info + * + * @return true if success, false if failed + */ +static bool +lower_alu(JitCompContext *cc, x86::Assembler &a, ALU_OP op, JitReg r0, + JitReg r1, JitReg r2) +{ + switch (jit_reg_kind(r0)) { + case JIT_REG_KIND_I32: + ALU_R_R_R(I32, int32, i32, op); + break; + case JIT_REG_KIND_I64: + ALU_R_R_R(I64, int64, i64, op); + break; + case JIT_REG_KIND_F32: + ALU_R_R_R(F32, float32, f32, op); + break; + case JIT_REG_KIND_F64: + ALU_R_R_R(F64, float64, f64, op); + break; + default: + LOG_VERBOSE("Invalid reg type of alu: %d\n", jit_reg_kind(r0)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode insn bit: AND/OR/XOR r0, r1, r2 + * @param kind the data kind, such as I32, I64 + * @param Type the data type, such as int32, int64 + * @param type the abbreviation of data type, such as i32, i64 + * @param op the opcode of bit operation + */ +#define BIT_R_R_R(kind, Type, type, op) \ + do { \ + Type data1, data2; \ + int32 reg_no_dst = 0, reg_no_src1 = 0, reg_no_src2 = 0; \ + bool _ret = false; \ + \ + CHECK_EQKIND(r0, r1); \ + CHECK_EQKIND(r0, r2); \ + memset(&data1, 0, sizeof(Type)); \ + memset(&data2, 0, sizeof(Type)); \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + if (jit_reg_is_const(r1)) \ + data1 = jit_cc_get_const_##kind(cc, r1); \ + else { \ + reg_no_src1 = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src1, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r2)) \ + data2 = jit_cc_get_const_##kind(cc, r2); \ + else { \ + reg_no_src2 = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_src2, jit_reg_kind(r2)); \ + } \ + \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = \ + bit_imm_imm_to_r_##type(a, op, reg_no_dst, data1, data2); \ + else \ + _ret = bit_imm_r_to_r_##type(a, op, reg_no_dst, data1, \ + reg_no_src2); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = \ + bit_r_imm_to_r_##type(a, op, reg_no_dst, reg_no_src1, data2); \ + else \ + _ret = bit_r_r_to_r_##type(a, op, reg_no_dst, reg_no_src1, \ + reg_no_src2); \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode bit insn, AND/OR/XOR r0, r1, r2 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param op the opcode of bit operations + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the first src operand info + * @param r2 src jit register that contains the second src operand info + * + * @return true if success, false if failed + */ +static bool +lower_bit(JitCompContext *cc, x86::Assembler &a, BIT_OP op, JitReg r0, + JitReg r1, JitReg r2) +{ + switch (jit_reg_kind(r0)) { + case JIT_REG_KIND_I32: + BIT_R_R_R(I32, int32, i32, op); + break; + case JIT_REG_KIND_I64: + BIT_R_R_R(I64, int64, i64, op); + break; + default: + LOG_VERBOSE("Invalid reg type of bit: %d\n", jit_reg_kind(r0)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode insn shift: SHL/SHRS/SHRU r0, r1, r2 + * @param kind the data kind, such as I32, I64 + * @param Type the data type, such as int32, int64 + * @param type the abbreviation of data type, such as i32, i64 + * @param op the opcode of shift operation + */ +#define SHIFT_R_R_R(kind, Type, type, op) \ + do { \ + Type data1, data2; \ + int32 reg_no_dst = 0, reg_no_src1 = 0, reg_no_src2 = 0; \ + bool _ret = false; \ + \ + CHECK_EQKIND(r0, r1); \ + CHECK_KIND(r2, JIT_REG_KIND_##kind); \ + memset(&data1, 0, sizeof(Type)); \ + memset(&data2, 0, sizeof(Type)); \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + if (jit_reg_is_const(r1)) \ + data1 = jit_cc_get_const_##kind(cc, r1); \ + else { \ + reg_no_src1 = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src1, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r2)) \ + data2 = jit_cc_get_const_##kind(cc, r2); \ + else { \ + reg_no_src2 = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_src2, jit_reg_kind(r2)); \ + } \ + \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = shift_imm_imm_to_r_##type(a, op, reg_no_dst, data1, \ + data2); \ + else \ + _ret = shift_imm_r_to_r_##type(a, op, reg_no_dst, data1, \ + reg_no_src2); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = shift_r_imm_to_r_##type(a, op, reg_no_dst, reg_no_src1, \ + data2); \ + else \ + _ret = shift_r_r_to_r_##type(a, op, reg_no_dst, reg_no_src1, \ + reg_no_src2); \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode shift insn, SHL/SHRS/SHRU r0, r1, r2 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param op the opcode of shift operations + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the first src operand info + * @param r2 src jit register that contains the second src operand info + * + * @return true if success, false if failed + */ +static bool +lower_shift(JitCompContext *cc, x86::Assembler &a, SHIFT_OP op, JitReg r0, + JitReg r1, JitReg r2) +{ + switch (jit_reg_kind(r0)) { + case JIT_REG_KIND_I32: + SHIFT_R_R_R(I32, int32, i32, op); + break; + case JIT_REG_KIND_I64: + SHIFT_R_R_R(I64, int64, i64, op); + break; + default: + LOG_VERBOSE("Invalid reg type of shift: %d\n", jit_reg_kind(r0)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode int32 bitcount operation of reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BITCOUNT operation + * @param reg_no_dst the no of register + * @param reg_no_src the reg no of first src register data + * + * @return true if success, false otherwise + */ +static bool +bitcount_r_to_r_i32(x86::Assembler &a, BITCOUNT_OP op, int32 reg_no_dst, + int32 reg_no_src) +{ + switch (op) { + case CLZ: + a.lzcnt(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + break; + case CTZ: + a.tzcnt(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + break; + case POPCNT: + a.popcnt(regs_i32[reg_no_dst], regs_i32[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + return true; +} + +/** + * Encode int64 bitcount operation of reg, and save result to reg + * + * @param a the assembler to emit the code + * @param op the opcode of BITCOUNT operation + * @param reg_no_dst the no of register + * @param reg_no_src the reg no of first src register data + * + * @return true if success, false otherwise + */ +static bool +bitcount_r_to_r_i64(x86::Assembler &a, BITCOUNT_OP op, int32 reg_no_dst, + int32 reg_no_src) +{ + switch (op) { + case CLZ: + a.lzcnt(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + break; + case CTZ: + a.tzcnt(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + break; + case POPCNT: + a.popcnt(regs_i64[reg_no_dst], regs_i64[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + return true; +} + +/** + * Encode insn bitcount: CLZ/CTZ/POPCNT r0, r1 + * @param kind the data kind, such as I32, I64 + * @param Type the data type, such as int32, int64 + * @param type the abbreviation of data type, such as i32, i64 + * @param op the opcode of bit operation + */ +#define BITCOUNT_R_R(kind, Type, type, op) \ + do { \ + int32 reg_no_dst = 0, reg_no_src = 0; \ + \ + CHECK_EQKIND(r0, r1); \ + CHECK_NCONST(r0); \ + CHECK_NCONST(r1); \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + reg_no_src = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src, jit_reg_kind(r1)); \ + if (!bitcount_r_to_r_##type(a, op, reg_no_dst, reg_no_src)) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode bitcount insn, CLZ/CTZ/POPCNT r0, r1 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param op the opcode of bitcount operations + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the src operand info + * + * @return true if success, false if failed + */ +static bool +lower_bitcount(JitCompContext *cc, x86::Assembler &a, BITCOUNT_OP op, JitReg r0, + JitReg r1) +{ + switch (jit_reg_kind(r0)) { + case JIT_REG_KIND_I32: + BITCOUNT_R_R(I32, int32, i32, op); + break; + case JIT_REG_KIND_I64: + BITCOUNT_R_R(I64, int64, i64, op); + break; + default: + LOG_VERBOSE("Invalid reg type of bit: %d\n", jit_reg_kind(r0)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode insn cmp: CMP r0, r1, r2 + * @param kind the data kind, such as I32, I64, F32 and F64 + * @param Type the data type, such as int32, int64, float32, and float64 + * @param type the abbreviation of data type, such as i32, i64, f32, and f64 + */ +#define CMP_R_R_R(kind, Type, type) \ + do { \ + Type data1, data2; \ + int32 reg_no_dst = 0, reg_no_src1 = 0, reg_no_src2 = 0; \ + bool _ret = false; \ + \ + CHECK_KIND(r0, JIT_REG_KIND_I32); \ + CHECK_KIND(r1, JIT_REG_KIND_##kind); \ + CHECK_EQKIND(r1, r2); \ + memset(&data1, 0, sizeof(Type)); \ + memset(&data2, 0, sizeof(Type)); \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + if (jit_reg_is_const(r1)) \ + data1 = jit_cc_get_const_##kind(cc, r1); \ + else { \ + reg_no_src1 = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src1, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r2)) \ + data2 = jit_cc_get_const_##kind(cc, r2); \ + else { \ + reg_no_src2 = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_src2, jit_reg_kind(r2)); \ + } \ + \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r2)) \ + _ret = cmp_imm_imm_to_r_##type(a, reg_no_dst, data1, data2); \ + else \ + _ret = \ + cmp_imm_r_to_r_##type(a, reg_no_dst, data1, reg_no_src2); \ + } \ + else if (jit_reg_is_const(r2)) \ + _ret = cmp_r_imm_to_r_##type(a, reg_no_dst, reg_no_src1, data2); \ + else \ + _ret = \ + cmp_r_r_to_r_##type(a, reg_no_dst, reg_no_src1, reg_no_src2); \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode cmp insn, CMP r0, r1, r2 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param r0 dst jit register that contains the dst operand info + * @param r1 condition jit register + * @param r2 src jit register that contains the first src operand info + * @param r3 src jit register that contains the second src operand info + * + * @return true if success, false if failed + */ +static bool +lower_cmp(JitCompContext *cc, x86::Assembler &a, JitReg r0, JitReg r1, + JitReg r2) +{ + switch (jit_reg_kind(r1)) { + case JIT_REG_KIND_I32: + CMP_R_R_R(I32, int32, i32); + cc->last_cmp_on_fp = false; + break; + case JIT_REG_KIND_I64: + CMP_R_R_R(I64, int64, i64); + cc->last_cmp_on_fp = false; + break; + case JIT_REG_KIND_F32: + CMP_R_R_R(F32, float32, f32); + cc->last_cmp_on_fp = true; + break; + case JIT_REG_KIND_F64: + CMP_R_R_R(F64, float64, f64); + cc->last_cmp_on_fp = true; + break; + default: + cc->last_cmp_on_fp = false; + LOG_VERBOSE("Invalid reg type of cmp: %d\n", jit_reg_kind(r1)); + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode detecting the cmp flags in reg, and jmp to the relative address + * according to the condition opcode + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param op the condition opcode to jmp + * @param offset the relative offset to jmp when the contidtion meeted + * + * @return return the next address of native code after encoded + */ +static bool +cmp_r_and_jmp_relative(JitCompContext *cc, x86::Assembler &a, COND_OP op, + int32 offset) +{ + Imm target(INT32_MAX); + char *stream; + bool fp_cmp = cc->last_cmp_on_fp; + + bh_assert(!fp_cmp || (fp_cmp && (op == GTS || op == GES))); + + switch (op) { + case EQ: + { + a.je(target); + break; + } + case NE: + { + a.jne(target); + break; + } + case GTS: + { + if (fp_cmp) { + a.ja(target); + } + else { + a.jg(target); + } + break; + } + case LES: + { + a.jng(target); + break; + } + case GES: + { + if (fp_cmp) { + a.jae(target); + } + else { + a.jnl(target); + } + break; + } + case LTS: + { + a.jl(target); + break; + } + case GTU: + { + a.ja(target); + break; + } + case LEU: + { + a.jna(target); + break; + } + case GEU: + { + a.jae(target); + break; + } + case LTU: + { + a.jb(target); + break; + } + default: + { + bh_assert(0); + break; + } + } + + JitErrorHandler *err_handler = (JitErrorHandler *)a.code()->errorHandler(); + + if (!err_handler->err) { + /* The offset written by asmjit is always 0, we patch it again */ + stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size() - 6; + *(int32 *)(stream + 2) = offset; + } + return true; +} + +/** + * Encode select insn, SELECT r0, r1, r2, r3 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the first src operand info + * @param r2 src jit register that contains the second src operand info + * + * @return true if success, false if failed + */ +/* TODO: optimize with setcc */ +static bool +lower_select(JitCompContext *cc, x86::Assembler &a, COND_OP op, JitReg r0, + JitReg r1, JitReg r2, JitReg r3) +{ + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code1, code2; + char *stream_mov1, *stream_mov2; + uint32 size_mov1, size_mov2; + + code1.init(env); + code1.setErrorHandler(&err_handler); + x86::Assembler a1(&code1); + + code2.init(env); + code2.setErrorHandler(&err_handler); + x86::Assembler a2(&code2); + + CHECK_NCONST(r0); + CHECK_NCONST(r1); + CHECK_KIND(r1, JIT_REG_KIND_I32); + + if (r0 == r3 && r0 != r2 && !cc->last_cmp_on_fp) { + JitReg r_tmp; + + /* For i32/i64, exchange r2 and r3 to make r0 equal to r2, + so as to decrease possible execution instructions. + For f32/f64 comparison, should not change the order as + the result of comparison with NaN may be different. */ + r_tmp = r2; + r2 = r3; + r3 = r_tmp; + op = not_cond(op); + } + + if (!lower_mov(cc, a1, r0, r2)) + GOTO_FAIL; + + if (!lower_mov(cc, a2, r0, r3)) + GOTO_FAIL; + + stream_mov1 = (char *)a1.code()->sectionById(0)->buffer().data(); + size_mov1 = a1.code()->sectionById(0)->buffer().size(); + stream_mov2 = (char *)a2.code()->sectionById(0)->buffer().data(); + size_mov2 = a2.code()->sectionById(0)->buffer().size(); + + if (r0 != r2) { + a.embedDataArray(TypeId::kInt8, stream_mov1, size_mov1); + } + + if (r3 && r0 != r3) { + if (!cmp_r_and_jmp_relative(cc, a, op, (int32)size_mov2)) + return false; + a.embedDataArray(TypeId::kInt8, stream_mov2, size_mov2); + } + + return true; +fail: + return false; +} + +/* jmp to dst label */ +#define JMP_TO_LABEL(label_dst, label_src) \ + do { \ + if (label_is_ahead(cc, label_dst, label_src)) { \ + JitErrorHandler *err_handler = \ + (JitErrorHandler *)a.code()->errorHandler(); \ + int32 _offset; \ + char *stream; \ + Imm imm(INT32_MAX); \ + a.jmp(imm); \ + if (!err_handler->err) { \ + /* The offset written by asmjit is always 0, we patch it \ + again, 6 is the size of jmp instruction */ \ + stream = (char *)a.code()->sectionById(0)->buffer().data() \ + + a.code()->sectionById(0)->buffer().size() - 6; \ + _offset = label_offsets[label_dst] \ + - a.code()->sectionById(0)->buffer().size(); \ + *(int32 *)(stream + 2) = _offset; \ + } \ + } \ + else { \ + if (!jmp_from_label_to_label(a, jmp_info_list, label_dst, \ + label_src)) \ + GOTO_FAIL; \ + } \ + } while (0) + +/** + * Encode branch insn, BEQ/BNE/../BLTU r0, r1, r2 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param r0 dst jit register that contains the dst operand info + * @param r1 src jit register that contains the first src operand info + * @param r2 src jit register that contains the second src operand info + * @param is_last_insn if current insn is the last insn of current block + * + * @return true if success, false if failed + */ +static bool +lower_branch(JitCompContext *cc, x86::Assembler &a, bh_list *jmp_info_list, + int32 label_src, COND_OP op, JitReg r0, JitReg r1, JitReg r2, + bool is_last_insn) +{ + int32 label_dst; + + CHECK_NCONST(r0); + CHECK_KIND(r0, JIT_REG_KIND_I32); + CHECK_KIND(r1, JIT_REG_KIND_L32); + + CHECK_REG_NO(jit_reg_no(r0), jit_reg_kind(r0)); + + label_dst = jit_reg_no(r1); + if (label_dst < (int32)jit_cc_label_num(cc) - 1 && is_last_insn + && label_is_neighboring(cc, label_src, label_dst) + && !cc->last_cmp_on_fp) { + JitReg r_tmp; + + r_tmp = r1; + r1 = r2; + r2 = r_tmp; + op = not_cond(op); + } + + if (!cmp_r_and_jmp_label(cc, a, jmp_info_list, label_src, op, r1, r2, + is_last_insn)) + GOTO_FAIL; + + return true; +fail: + return false; +} + +/** + * Encode lookupswitch with key of immediate data + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param label_offsets the offsets of each label + * @param label_src the index of src label + * @param key the entry key + * @param opnd the lookup switch operand + * @param is_last_insn if current insn is the last insn of current block + * + * @return true if success, false if failed + */ +static bool +lookupswitch_imm(JitCompContext *cc, x86::Assembler &a, bh_list *jmp_info_list, + uint32 *label_offsets, int32 label_src, int32 key, + const JitOpndLookupSwitch *opnd, bool is_last_insn) +{ + uint32 i; + int32 label_dst; + + for (i = 0; i < opnd->match_pairs_num; i++) + if (key == opnd->match_pairs[i].value) { + label_dst = jit_reg_no(opnd->match_pairs[i].target); + if (!(is_last_insn + && label_is_neighboring(cc, label_src, label_dst))) { + JMP_TO_LABEL(label_dst, label_src); + } + return true; + } + + if (opnd->default_target) { + label_dst = jit_reg_no(opnd->default_target); + if (!(is_last_insn && label_is_neighboring(cc, label_src, label_dst))) { + JMP_TO_LABEL(label_dst, label_src); + } + } + + return true; +fail: + return false; +} + +/** + * Encode detecting lookupswitch entry register and jumping to matched label + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param label_offsets the offsets of each label + * @param label_src the index of src label + * @param reg_no the no of entry register + * @param opnd the lookup switch operand + * @param is_last_insn if current insn is the last insn of current block + * + * @return true if success, false if failed + */ +static bool +lookupswitch_r(JitCompContext *cc, x86::Assembler &a, bh_list *jmp_info_list, + uint32 *label_offsets, int32 label_src, int32 reg_no, + const JitOpndLookupSwitch *opnd, bool is_last_insn) +{ + JmpInfo *node; + Imm imm; + x86::Mem m; + uint32 i; + int32 label_dst = 0; + char *stream; + + if (opnd->match_pairs_num < 10) { + /* For small count of branches, it is better to compare + the key with branch value and jump one by one */ + for (i = 0; i < opnd->match_pairs_num; i++) { + imm.setValue(opnd->match_pairs[i].value); + a.cmp(regs_i32[reg_no], imm); + + node = (JmpInfo *)jit_malloc(sizeof(JmpInfo)); + if (!node) + GOTO_FAIL; + + node->type = JMP_DST_LABEL_REL; + node->label_src = label_src; + node->dst_info.label_dst = jit_reg_no(opnd->match_pairs[i].target); + node->offset = a.code()->sectionById(0)->buffer().size() + 2; + bh_list_insert(jmp_info_list, node); + + imm.setValue(INT32_MAX); + a.je(imm); + } + + if (opnd->default_target) { + label_dst = jit_reg_no(opnd->default_target); + if (!(is_last_insn + && label_is_neighboring(cc, label_src, label_dst))) + JMP_TO_LABEL(label_dst, label_src); + } + } + else { + /* For bigger count of branches, use indirect jump */ + /* unsigned extend to rsi */ + a.mov(regs_i32[REG_I32_FREE_IDX], regs_i32[reg_no]); + imm.setValue(opnd->match_pairs_num); + a.cmp(regs_i64[REG_I64_FREE_IDX], imm); + + /* Jump to default label if rsi >= br_count */ + stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + imm.setValue(INT32_MAX); + a.jb(imm); + *(uint32 *)(stream + 2) = 6; + + node = (JmpInfo *)jit_calloc(sizeof(JmpInfo)); + if (!node) + goto fail; + + node->type = JMP_DST_LABEL_REL; + node->label_src = label_src; + node->dst_info.label_dst = jit_reg_no(opnd->default_target); + node->offset = a.code()->sectionById(0)->buffer().size() + 2; + bh_list_insert(jmp_info_list, node); + + imm.setValue(INT32_MAX); + a.jmp(imm); + + node = (JmpInfo *)jit_malloc(sizeof(JmpInfo)); + if (!node) + GOTO_FAIL; + + node->type = JMP_LOOKUPSWITCH_BASE; + node->offset = a.code()->sectionById(0)->buffer().size() + 2; + bh_list_insert(jmp_info_list, node); + + /* LookupSwitch table base addr */ + imm.setValue(INT64_MAX); + a.mov(regs_i64[reg_no], imm); + + /* jmp *(base_addr + rsi * 8) */ + m = x86::ptr(regs_i64[reg_no], regs_i64[REG_I64_FREE_IDX], 3); + a.jmp(m); + + /* Store each dst label absolute address */ + for (i = 0; i < opnd->match_pairs_num; i++) { + node = (JmpInfo *)jit_malloc(sizeof(JmpInfo)); + if (!node) + GOTO_FAIL; + + node->type = JMP_DST_LABEL_ABS; + node->dst_info.label_dst = jit_reg_no(opnd->match_pairs[i].target); + node->offset = a.code()->sectionById(0)->buffer().size(); + bh_list_insert(jmp_info_list, node); + + a.embedUInt64(UINT64_MAX); + } + } + + return true; +fail: + return false; +} + +/** + * Encode lookupswitch insn, LOOKUPSWITCH opnd + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param label_offsets the offsets of each label + * @param label_src the index of src label + * @param opnd the lookup switch operand + * @param is_last_insn if current insn is the last insn of current block + * + * @return true if success, false if failed + */ +static bool +lower_lookupswitch(JitCompContext *cc, x86::Assembler &a, + bh_list *jmp_info_list, uint32 *label_offsets, + int32 label_src, const JitOpndLookupSwitch *opnd, + bool is_last_insn) +{ + JitReg r0 = opnd->value; + int32 key, reg_no; + + CHECK_KIND(r0, JIT_REG_KIND_I32); + CHECK_KIND(opnd->default_target, JIT_REG_KIND_L32); + + if (jit_reg_is_const(r0)) { + key = jit_cc_get_const_I32(cc, r0); + if (!lookupswitch_imm(cc, a, jmp_info_list, label_offsets, label_src, + key, opnd, is_last_insn)) + GOTO_FAIL; + } + else { + reg_no = jit_reg_no(r0); + CHECK_I32_REG_NO(reg_no); + if (!lookupswitch_r(cc, a, jmp_info_list, label_offsets, label_src, + reg_no, opnd, is_last_insn)) + GOTO_FAIL; + } + + return true; +fail: + return false; +} + +/** + * Encode callnative insn, CALLNATIVE r0, r1, ... + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param insn current insn info + * + * @return true if success, false if failed + */ +static bool +lower_callnative(JitCompContext *cc, x86::Assembler &a, JitInsn *insn) +{ + void (*func_ptr)(void); + JitReg ret_reg, func_reg, arg_reg; + /* the index of callee saved registers in regs_i64 */ + uint8 regs_arg_idx[] = { REG_RDI_IDX, REG_RSI_IDX, REG_RDX_IDX, + REG_RCX_IDX, REG_R8_IDX, REG_R9_IDX }; + Imm imm; + uint32 i, opnd_num; + int32 integer_reg_index = 0, floatpoint_reg_index = 0; + + ret_reg = *(jit_insn_opndv(insn, 0)); + func_reg = *(jit_insn_opndv(insn, 1)); + CHECK_KIND(func_reg, JIT_REG_KIND_I64); + CHECK_CONST(func_reg); + + func_ptr = (void (*)(void))jit_cc_get_const_I64(cc, func_reg); + + opnd_num = jit_insn_opndv_num(insn); + for (i = 0; i < opnd_num - 2; i++) { + /*TODO: if arguments number is greater than 6 */ + bh_assert(integer_reg_index < 6); + bh_assert(floatpoint_reg_index < 6); + + arg_reg = *(jit_insn_opndv(insn, i + 2)); + switch (jit_reg_kind(arg_reg)) { + case JIT_REG_KIND_I32: + { + int32 reg_no = regs_arg_idx[integer_reg_index++]; + CHECK_I64_REG_NO(reg_no); + if (jit_reg_is_const(arg_reg)) { + mov_imm_to_r_i64(a, reg_no, + (int64)jit_cc_get_const_I32(cc, arg_reg)); + } + else { + int32 arg_reg_no = jit_reg_no(arg_reg); + CHECK_I32_REG_NO(arg_reg_no); + extend_r32_to_r64(a, reg_no, arg_reg_no, true); + } + break; + } + case JIT_REG_KIND_I64: + { + int32 reg_no = regs_arg_idx[integer_reg_index++]; + CHECK_I64_REG_NO(reg_no); + if (jit_reg_is_const(arg_reg)) { + mov_imm_to_r_i64(a, reg_no, + jit_cc_get_const_I64(cc, arg_reg)); + } + else { + int32 arg_reg_no = jit_reg_no(arg_reg); + CHECK_I64_REG_NO(arg_reg_no); + mov_r_to_r_i64(a, reg_no, arg_reg_no); + } + break; + } + case JIT_REG_KIND_F32: + { + CHECK_F32_REG_NO((int32)floatpoint_reg_index); + if (jit_reg_is_const(arg_reg)) { + mov_imm_to_r_f32(a, floatpoint_reg_index, + jit_cc_get_const_F32(cc, arg_reg)); + } + else { + int32 arg_reg_no = jit_reg_no(arg_reg); + CHECK_F32_REG_NO(arg_reg_no); + mov_r_to_r_f32(a, floatpoint_reg_index, arg_reg_no); + } + floatpoint_reg_index++; + break; + } + case JIT_REG_KIND_F64: + { + CHECK_F64_REG_NO((int32)floatpoint_reg_index); + if (jit_reg_is_const(arg_reg)) { + mov_imm_to_r_f64(a, floatpoint_reg_index, + jit_cc_get_const_F64(cc, arg_reg)); + } + else { + int32 arg_reg_no = jit_reg_no(arg_reg); + CHECK_F64_REG_NO(arg_reg_no); + mov_r_to_r_f64(a, floatpoint_reg_index, arg_reg_no); + } + floatpoint_reg_index++; + break; + } + default: + { + + bh_assert(0); + goto fail; + } + } + } + + imm.setValue((uint64)func_ptr); + a.mov(regs_i64[REG_RAX_IDX], imm); + a.call(regs_i64[REG_RAX_IDX]); + + if (ret_reg) { + uint32 ret_reg_no = jit_reg_no(ret_reg); + if (jit_reg_kind(ret_reg) == JIT_REG_KIND_I64) { + CHECK_I64_REG_NO(ret_reg_no); + /* mov res, rax */ + mov_r_to_r_i64(a, ret_reg_no, REG_RAX_IDX); + } + else if (jit_reg_kind(ret_reg) == JIT_REG_KIND_F64) { + CHECK_F64_REG_NO(ret_reg_no); + /* mov res, xmm0_f64 */ + mov_r_to_r_f64(a, ret_reg_no, 0); + } + else { + bh_assert((jit_reg_kind(ret_reg) == JIT_REG_KIND_I32 + && ret_reg_no == REG_EAX_IDX) + || (jit_reg_kind(ret_reg) == JIT_REG_KIND_F32 + && ret_reg_no == 0)); + } + } + + return true; +fail: + return false; +} + +/** + * Encode callbc insn, CALLBC r0, r1, r2 + * + * @param cc the compiler context + * @param a the assembler to emit the code + * @param jmp_info_list the jmp info list + * @param label_src the index of src label + * @param insn current insn info + * + * @return true if success, false if failed + */ +static bool +lower_callbc(JitCompContext *cc, x86::Assembler &a, bh_list *jmp_info_list, + int32 label_src, JitInsn *insn) +{ + JmpInfo *node; + Imm imm; + JitReg edx_hreg = jit_reg_new(JIT_REG_KIND_I32, REG_EDX_IDX); + JitReg rdx_hreg = jit_reg_new(JIT_REG_KIND_I64, REG_RDX_IDX); + JitReg xmm0_f32_hreg = jit_reg_new(JIT_REG_KIND_F32, 0); + JitReg xmm0_f64_hreg = jit_reg_new(JIT_REG_KIND_F64, 0); + JitReg ret_reg = *(jit_insn_opnd(insn, 0)); + JitReg func_reg = *(jit_insn_opnd(insn, 2)); + JitReg func_idx = *(jit_insn_opnd(insn, 3)); + JitReg src_reg; + int32 func_reg_no; + + /* Load return_jitted_addr from stack */ + x86::Mem m(x86::rbp, cc->jitted_return_address_offset); + + CHECK_KIND(func_reg, JIT_REG_KIND_I64); + func_reg_no = jit_reg_no(func_reg); + CHECK_I64_REG_NO(func_reg_no); + + CHECK_KIND(func_idx, JIT_REG_KIND_I32); + if (jit_reg_is_const(func_idx)) { + imm.setValue(jit_cc_get_const_I32(cc, func_idx)); + a.mov(regs_i64[REG_RDX_IDX], imm); + } + else { + a.movzx(regs_i64[REG_RDX_IDX], regs_i32[jit_reg_no(func_idx)]); + } + + node = (JmpInfo *)jit_malloc(sizeof(JmpInfo)); + if (!node) + GOTO_FAIL; + + node->type = JMP_END_OF_CALLBC; + node->label_src = label_src; + node->offset = a.code()->sectionById(0)->buffer().size() + 2; + bh_list_insert(jmp_info_list, node); + + /* Set next jited addr to glue_ret_jited_addr, 0 will be replaced with + actual offset after actual code cache is allocated */ + imm.setValue(INT64_MAX); + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.mov(m, regs_i64[REG_I64_FREE_IDX]); + a.jmp(regs_i64[func_reg_no]); + + if (ret_reg) { + switch (jit_reg_kind(ret_reg)) { + case JIT_REG_KIND_I32: + src_reg = edx_hreg; + break; + case JIT_REG_KIND_I64: + src_reg = rdx_hreg; + break; + case JIT_REG_KIND_F32: + src_reg = xmm0_f32_hreg; + break; + case JIT_REG_KIND_F64: + src_reg = xmm0_f64_hreg; + break; + default: + bh_assert(0); + return false; + } + + if (!lower_mov(cc, a, ret_reg, src_reg)) + return false; + } + return true; +fail: + return false; +} + +static bool +lower_returnbc(JitCompContext *cc, x86::Assembler &a, JitInsn *insn) +{ + JitReg edx_hreg = jit_reg_new(JIT_REG_KIND_I32, REG_EDX_IDX); + JitReg rdx_hreg = jit_reg_new(JIT_REG_KIND_I64, REG_RDX_IDX); + JitReg xmm0_f32_hreg = jit_reg_new(JIT_REG_KIND_F32, 0); + JitReg xmm0_f64_hreg = jit_reg_new(JIT_REG_KIND_F64, 0); + JitReg act_reg = *(jit_insn_opnd(insn, 0)); + JitReg ret_reg = *(jit_insn_opnd(insn, 1)); + JitReg dst_reg; + int32 act; + + CHECK_CONST(act_reg); + CHECK_KIND(act_reg, JIT_REG_KIND_I32); + + act = jit_cc_get_const_I32(cc, act_reg); + + if (ret_reg) { + switch (jit_reg_kind(ret_reg)) { + case JIT_REG_KIND_I32: + dst_reg = edx_hreg; + break; + case JIT_REG_KIND_I64: + dst_reg = rdx_hreg; + break; + case JIT_REG_KIND_F32: + dst_reg = xmm0_f32_hreg; + break; + case JIT_REG_KIND_F64: + dst_reg = xmm0_f64_hreg; + break; + default: + bh_assert(0); + return false; + } + if (!lower_mov(cc, a, dst_reg, ret_reg)) + return false; + } + + { + /* eax = act */ + Imm imm(act); + a.mov(x86::eax, imm); + + x86::Mem m(x86::rbp, cc->jitted_return_address_offset); + a.jmp(m); + } + return true; +fail: + return false; +} + +static bool +lower_return(JitCompContext *cc, x86::Assembler &a, JitInsn *insn) +{ + JitReg act_reg = *(jit_insn_opnd(insn, 0)); + int32 act; + + CHECK_CONST(act_reg); + CHECK_KIND(act_reg, JIT_REG_KIND_I32); + + act = jit_cc_get_const_I32(cc, act_reg); + { + /* eax = act */ + Imm imm(act); + a.mov(x86::eax, imm); + + imm.setValue((uintptr_t)code_block_return_to_interp_from_jitted); + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.jmp(regs_i64[REG_I64_FREE_IDX]); + } + return true; +fail: + return false; +} + +/** + * Replace all the jmp address pre-saved when the code cache hasn't been + * allocated with actual address after code cache allocated + * + * @param cc compiler context containing the allocated code cacha info + * @param jmp_info_list the jmp info list + */ +static void +patch_jmp_info_list(JitCompContext *cc, bh_list *jmp_info_list) +{ + JmpInfo *jmp_info, *jmp_info_next; + JitReg reg_dst; + char *stream; + + jmp_info = (JmpInfo *)bh_list_first_elem(jmp_info_list); + + while (jmp_info) { + jmp_info_next = (JmpInfo *)bh_list_elem_next(jmp_info); + + stream = (char *)cc->jitted_addr_begin + jmp_info->offset; + + if (jmp_info->type == JMP_DST_LABEL_REL) { + /* Jmp with relative address */ + reg_dst = + jit_reg_new(JIT_REG_KIND_L32, jmp_info->dst_info.label_dst); + *(int32 *)stream = + (int32)((uintptr_t)*jit_annl_jitted_addr(cc, reg_dst) + - (uintptr_t)stream) + - 4; + } + else if (jmp_info->type == JMP_DST_LABEL_ABS) { + /* Jmp with absolute address */ + reg_dst = + jit_reg_new(JIT_REG_KIND_L32, jmp_info->dst_info.label_dst); + *(uintptr_t *)stream = + (uintptr_t)*jit_annl_jitted_addr(cc, reg_dst); + } + else if (jmp_info->type == JMP_END_OF_CALLBC) { + /* 7 is the size of mov and jmp instruction */ + *(uintptr_t *)stream = (uintptr_t)stream + sizeof(uintptr_t) + 7; + } + else if (jmp_info->type == JMP_LOOKUPSWITCH_BASE) { + /* 11 is the size of 8-byte addr and 3-byte jmp instruction */ + *(uintptr_t *)stream = (uintptr_t)stream + 11; + } + + jmp_info = jmp_info_next; + } +} + +/* Free the jmp info list */ +static void +free_jmp_info_list(bh_list *jmp_info_list) +{ + void *cur_node = bh_list_first_elem(jmp_info_list); + + while (cur_node) { + void *next_node = bh_list_elem_next(cur_node); + + bh_list_remove(jmp_info_list, cur_node); + jit_free(cur_node); + cur_node = next_node; + } +} + +/** + * Encode cast int32 immediate data to float register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst float register + * @param data the src immediate data + * + * @return true if success, false otherwise + */ +static bool +cast_imm_i32_to_r_f32(x86::Assembler &a, int32 reg_no, int32 data) +{ + Imm imm(data); + a.mov(regs_i32[REG_I32_FREE_IDX], imm); + a.movd(regs_float[reg_no], regs_i32[REG_I32_FREE_IDX]); + return true; +} + +/** + * Encode cast int32 register data to float register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst float register + * @param reg_no_src the no of src int32 register + * + * @return true if success, false otherwise + */ +static bool +cast_r_i32_to_r_f32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.movd(regs_float[reg_no_dst], regs_i32[reg_no_src]); + return true; +} + +/** + * Encode cast int64 immediate data to double register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst double register + * @param data the src immediate data + * + * @return true if success, false otherwise + */ +static bool +cast_imm_i64_to_r_f64(x86::Assembler &a, int32 reg_no, int64 data) +{ + Imm imm(data); + a.mov(regs_i64[REG_I64_FREE_IDX], imm); + a.movq(regs_float[reg_no], regs_i64[REG_I64_FREE_IDX]); + return true; +} + +/** + * Encode cast int64 register data to double register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst double register + * @param reg_no_src the no of src int64 register + * + * @return true if success, false otherwise + */ +static bool +cast_r_i64_to_r_f64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.movq(regs_float[reg_no_dst], regs_i64[reg_no_src]); + return true; +} + +/** + * Encode cast float immediate data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int32 register + * @param data the src immediate data + * + * @return true if success, false otherwise + */ +static bool +cast_imm_f32_to_r_i32(x86::Assembler &a, int32 reg_no, float data) +{ + cast_float_to_integer v = { .f = data }; + return mov_imm_to_r_i32(a, reg_no, v.i); +} + +/** + * Encode cast float register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +cast_r_f32_to_r_i32(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.movd(regs_i32[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode cast double immediate data to int64 register data + * + * @param a the assembler to emit the code + * @param reg_no the no of dst int64 register + * @param data the src immediate data + * + * @return true if success, false otherwise + */ +static bool +cast_imm_f64_to_r_i64(x86::Assembler &a, int32 reg_no, double data) +{ + cast_double_to_integer v = { .d = data }; + return mov_imm_to_r_i64(a, reg_no, v.i); +} + +/** + * Encode cast float register data to int32 register data + * + * @param a the assembler to emit the code + * @param reg_no_dst the no of dst int32 register + * @param reg_no_src the no of src float register + * + * @return true if success, false otherwise + */ +static bool +cast_r_f64_to_r_i64(x86::Assembler &a, int32 reg_no_dst, int32 reg_no_src) +{ + a.movq(regs_i64[reg_no_dst], regs_float[reg_no_src]); + return true; +} + +/** + * Encode insn cast: F32CASTI32, + * @param kind0 the dst JIT_REG_KIND, such as I32, I64, F32 and F64 + * @param kind1 the src JIT_REG_KIND, such as I32, I64, F32 and F64 + * @param type0 the dst data type, such as i8, u8, i16, u16, i32, f32, i64, f32, + * f64 + * @param type1 the src data type, such as i8, u8, i16, u16, i32, f32, i64, f32, + * f64 + */ +#define CAST_R_R(kind0, kind1, type0, type1, Type1) \ + do { \ + bool _ret = false; \ + int32 reg_no_dst = 0, reg_no_src = 0; \ + CHECK_KIND(r0, JIT_REG_KIND_##kind0); \ + CHECK_KIND(r1, JIT_REG_KIND_##kind1); \ + \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, JIT_REG_KIND_##kind0); \ + if (jit_reg_is_const(r1)) { \ + Type1 data = jit_cc_get_const_##kind1(cc, r1); \ + _ret = cast_imm_##type1##_to_r_##type0(a, reg_no_dst, data); \ + } \ + else { \ + reg_no_src = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src, JIT_REG_KIND_##kind1); \ + _ret = cast_r_##type1##_to_r_##type0(a, reg_no_dst, reg_no_src); \ + } \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +#if WASM_ENABLE_SHARED_MEMORY != 0 + +/** + * Encode extend certain bytes in the src register to a I32 or I64 kind value in + * dst register + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to extend to, could be I32, I64 + * @param reg_no_src the index of register hold src value + * + * @return true if success, false otherwise + */ +static bool +extend_r_to_r(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + int32 reg_no_src, int32 reg_no_dst) +{ + if (kind_dst == JIT_REG_KIND_I32) { + bh_assert(reg_no_src < 16 && reg_no_dst < 16); + switch (bytes_dst) { + case 1: + extend_r8_to_r32(a, reg_no_dst, reg_no_src, false); + break; + case 2: + extend_r16_to_r32(a, reg_no_dst, reg_no_src, false); + break; + case 4: + mov_r_to_r_i32(a, reg_no_dst, reg_no_src); + break; + default: + bh_assert(0); + return false; + } + } + else if (kind_dst == JIT_REG_KIND_I64) { + bh_assert(reg_no_src < 16 && reg_no_dst < 16); + switch (bytes_dst) { + case 1: + extend_r8_to_r64(a, reg_no_dst, reg_no_src, false); + break; + case 2: + extend_r16_to_r64(a, reg_no_dst, reg_no_src, false); + break; + case 4: + extend_r32_to_r64(a, reg_no_dst, reg_no_src, false); + break; + case 8: + mov_r_to_r_i64(a, reg_no_dst, reg_no_src); + break; + default: + bh_assert(0); + return false; + } + } + else { + bh_assert(0); + } + return true; +} + +/** + * Encode atomic compare and exchange, when calling this function, + * value for comparison should be already moved in register + * al/ax/eax/rax + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to move, could be I32, I64 + * @param m_dst the dest memory operand + * @param reg_no_xchg the index of register hold exchange value + * + * @return true if success, false otherwise + */ +static bool +at_cmpxchg(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, + int32 reg_no_xchg, x86::Mem &m_dst) +{ + bh_assert((kind_dst == JIT_REG_KIND_I32 && bytes_dst <= 4) + || kind_dst == JIT_REG_KIND_I64); + bh_assert(reg_no_xchg < 16); + switch (bytes_dst) { + case 1: + a.lock().cmpxchg(m_dst, regs_i8[reg_no_xchg]); + break; + case 2: + a.lock().cmpxchg(m_dst, regs_i16[reg_no_xchg]); + break; + case 4: + a.lock().cmpxchg(m_dst, regs_i32[reg_no_xchg]); + break; + case 8: + a.lock().cmpxchg(m_dst, regs_i64[reg_no_xchg]); + break; + default: + bh_assert(0); + return false; + } + return true; +} + +/** + * Encode atomic compare and exchange: load value into a register from + * memory with reg base and reg offset, compare (expected) reg data with the + * loaded value, if equal, store the (replacement) reg data to the same + * memory, else, do nothing. Either way, returns the loaded value + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_xchg the no of register that stores the conditionally + * replacement value + * @param reg_no_base the no of register that stores the base address + * of src&dst memory + * @param reg_no_offset the no of register that stores the offset address + * of src&dst memory + * @return true if success, false otherwise + */ +static bool +at_cmpxchg_r_ra_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_xchg, + int32 reg_no_base, int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return at_cmpxchg(a, bytes_dst, kind_dst, reg_no_xchg, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, REG_RAX_IDX); +} + +/** + * Encode atomic compare and exchange: load value into a register from + * memory with reg base and imm offset, compare (expected) reg data with the + * loaded value, if equal, store the (replacement) reg data to the same + * memory, else, do nothing. Either way, returns the loaded value + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_xchg the no of register that stores the conditionally + * replacement value + * @param reg_no_base the no of register that stores the base address + * of src&dst memory + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_cmpxchg_r_ra_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_xchg, + int32 reg_no_base, int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return at_cmpxchg(a, bytes_dst, kind_dst, reg_no_xchg, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, REG_RAX_IDX); +} + +/** + * Encode atomic compare and exchange: load value into a register from + * memory with reg base and reg offset, compare (expected) reg data with the + * loaded value, if equal, store the (replacement) imm data to the same + * memory, else, do nothing. Either way, returns the loaded value + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param data_xchg the immediate data for exchange(conditionally replacement + * value) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory + * @param reg_no_offset the no of register that stores the offset address + * of src&dst memory + * @return true if success, false otherwise + */ +static bool +at_cmpxchg_imm_ra_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, void *data_xchg, + int32 reg_no_base, int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_xchg, bytes_dst); + uint32 reg_no_xchg = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_cmpxchg(a, bytes_dst, kind_dst, reg_no_xchg, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, REG_RAX_IDX); +} + +/** + * Encode atomic compare and exchange: load value into a register from + * memory with reg base and imm offset, compare (expected) reg data with the + * loaded value, if equal, store the (replacement) imm data to the same + * memory, else, do nothing. Either way, returns the loaded value + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param data_xchg the immediate data for exchange(conditionally replacement + * value) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_cmpxchg_imm_ra_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, void *data_xchg, + int32 reg_no_base, int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_xchg, bytes_dst); + uint32 reg_no_xchg = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_cmpxchg(a, bytes_dst, kind_dst, reg_no_xchg, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, REG_RAX_IDX); +} + +/** + * Encode insn cmpxchg: CMPXCHG_type r0, r1, r2, r3, r4 + * @param kind the data kind, can only be I32 or I64 + * @param bytes_dst the byte number of dst data + */ +#define CMPXCHG_R_R_R_R_R(kind, type, bytes_dst) \ + do { \ + type data_xchg = 0; \ + int32 reg_no_xchg = 0, reg_no_cmp = 0, reg_no_base = 0, \ + reg_no_offset = 0; \ + int32 offset = 0; \ + bool _ret = false; \ + if (jit_reg_is_const(r3)) { \ + CHECK_KIND(r3, JIT_REG_KIND_I32); \ + } \ + else { \ + CHECK_KIND(r3, JIT_REG_KIND_I64); \ + } \ + /* r1: expected value(it must in register a) \ + * r2: memory base addr can't be const */ \ + CHECK_NCONST(r1); \ + reg_no_cmp = jit_reg_no(r1); \ + bh_assert(reg_no_cmp == REG_EAX_IDX || reg_no_cmp == REG_RAX_IDX); \ + CHECK_REG_NO(reg_no_cmp, jit_reg_kind(r1)); \ + CHECK_NCONST(r2); \ + reg_no_base = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_base, jit_reg_kind(r2)); \ + /* r0: replacement value r3: offset can be const */ \ + if (jit_reg_is_const(r0)) \ + data_xchg = jit_cc_get_const_##kind(cc, r0); \ + else { \ + reg_no_xchg = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_xchg, jit_reg_kind(r0)); \ + } \ + if (jit_reg_is_const(r3)) \ + offset = jit_cc_get_const_I32(cc, r3); \ + else { \ + reg_no_offset = jit_reg_no(r3); \ + CHECK_REG_NO(reg_no_offset, jit_reg_kind(r3)); \ + } \ + \ + if (jit_reg_is_const(r0)) { \ + if (jit_reg_is_const(r3)) \ + _ret = at_cmpxchg_imm_ra_base_r_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, &data_xchg, \ + reg_no_base, offset); \ + else \ + _ret = at_cmpxchg_imm_ra_base_r_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, &data_xchg, \ + reg_no_base, reg_no_offset); \ + } \ + else { \ + if (jit_reg_is_const(r3)) \ + _ret = at_cmpxchg_r_ra_base_r_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_xchg, \ + reg_no_base, offset); \ + else \ + _ret = at_cmpxchg_r_ra_base_r_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_xchg, \ + reg_no_base, reg_no_offset); \ + } \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode negate a value in the register + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to move, could be I32, I64 + * @param reg_no_src the index of register hold src value + * + * @return true if success, false otherwise + */ +static bool +neg_r(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, int32 reg_no_src) +{ + bh_assert((kind_dst == JIT_REG_KIND_I32 && bytes_dst <= 4) + || kind_dst == JIT_REG_KIND_I64); + bh_assert(reg_no_src < 16); + switch (bytes_dst) { + case 1: + a.neg(regs_i8[reg_no_src]); + break; + case 2: + a.neg(regs_i16[reg_no_src]); + break; + case 4: + a.neg(regs_i32[reg_no_src]); + break; + case 8: + a.neg(regs_i64[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + return true; +} + +/** + * Encode atomic exchange and add + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to move, could be I32, I64 + * @param reg_no_src the index of register hold operand value of add operation + * @param m_dst the dest memory operand + * + * @return true if success, false otherwise + */ +static bool +at_xadd(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, int32 reg_no_src, + x86::Mem &m_dst) +{ + bh_assert((kind_dst == JIT_REG_KIND_I32 && bytes_dst <= 4) + || kind_dst == JIT_REG_KIND_I64); + bh_assert(reg_no_src < 16); + switch (bytes_dst) { + case 1: + a.lock().xadd(m_dst, regs_i8[reg_no_src]); + break; + case 2: + a.lock().xadd(m_dst, regs_i16[reg_no_src]); + break; + case 4: + a.lock().xadd(m_dst, regs_i32[reg_no_src]); + break; + case 8: + a.lock().xadd(m_dst, regs_i64[reg_no_src]); + break; + default: + bh_assert(0); + return false; + } + + return true; +} + +/** + * Encode atomic rmw add: load value into a register from memory + * with reg base and reg offset, add loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(first operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(second operand&store back) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_add_imm_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw add: load value into a register from memory + * with reg base and reg offset, add loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_add_imm_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw add: load value into a register from memory + * with reg base and imm offset, add loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_add_r_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw add: load value into a register from memory + * with reg base and reg offset, add loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_add_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw sub: load value into a register from memory + * with reg base and reg offset, sub loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(first operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(second operand&store back) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_sub_imm_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return neg_r(a, bytes_dst, kind_dst, reg_no_src) + && at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw sub: load value into a register from memory + * with reg base and reg offset, sub loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_sub_imm_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return neg_r(a, bytes_dst, kind_dst, reg_no_src) + && at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw sub: load value into a register from memory + * with reg base and imm offset, sub loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_sub_r_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return neg_r(a, bytes_dst, kind_dst, reg_no_src) + && at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw sub: load value into a register from memory + * with reg base and reg offset, sub loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_sub_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return neg_r(a, bytes_dst, kind_dst, reg_no_src) + && at_xadd(a, bytes_dst, kind_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw xchg: load value into a register from memory + * with reg base and reg offset, exchange loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(first operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(second operand&store back) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xchg_imm_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw xchg: load value into a register from memory + * with reg base and reg offset, exchange loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xchg_imm_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw xchg: load value into a register from memory + * with reg base and imm offset, exchange loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xchg_r_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode atomic rmw xchg: load value into a register from memory + * with reg base and reg offset, exchange loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xchg_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return xchg_r_to_m(a, bytes_dst, kind_dst, m, reg_no_src) + && extend_r_to_r(a, bytes_dst, kind_dst, reg_no_src, reg_no_dst); +} + +/** + * Encode insn rmw logical operation: generate a loop to make sure it's atomic + * @param bin_op the operation, can be and/or/xor + * @param kind the data kind, can only be I32 or I64 + * @param bytes_dst the byte number of dst data + */ +#define AT_RMW_LOGICAL_LOOP(bin_op, kind, bytes_dst) \ + do { \ + bh_assert((kind_dst == JIT_REG_KIND_I32 && bytes_dst <= 4) \ + || kind_dst == JIT_REG_KIND_I64); \ + bh_assert(reg_no_src < 16 && reg_no_dst < 16); \ + /* read original value in memory(operand 1) to rax(expected) */ \ + mov_m_to_r(a, bytes_dst, kind_dst, false, REG_RAX_IDX, m_dst); \ + Label loop = a.newLabel(); \ + /* check whether loop is valid, and bind the loop label \ + * to the current position in the code. */ \ + if (!loop.isValid() || a.bind(loop) != kErrorOk) \ + return false; \ + /* move operand 1 to temp reg rb */ \ + mov_r_to_r(a, kind_dst, REG_RBX_IDX, REG_RAX_IDX); \ + /* actual logical operation with operand 2, result save to rbx */ \ + switch (bytes_dst) { \ + case 1: \ + a.bin_op##_(regs_i8[REG_RBX_IDX], regs_i8[reg_no_src]); \ + break; \ + case 2: \ + a.bin_op##_(regs_i16[REG_RBX_IDX], regs_i16[reg_no_src]); \ + break; \ + case 4: \ + a.bin_op##_(regs_i32[REG_RBX_IDX], regs_i32[reg_no_src]); \ + break; \ + case 8: \ + a.bin_op##_(regs_i64[REG_RBX_IDX], regs_i64[reg_no_src]); \ + break; \ + default: \ + bh_assert(0); \ + return false; \ + } \ + /* cmp with read value in RAX, try to change with result value in RBX \ + * REG, if change successfully, mem data is changed and exit loop(ZF \ + * is set) if not, loop again(ZF is clear) and tries to do logical ops \ + * atomically */ \ + at_cmpxchg(a, bytes_dst, kind_dst, REG_RBX_IDX, m_dst); \ + a.jne(loop); \ + return true; \ + } while (0) + +/** + * Encode atomic logical binary operation: and + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to move, could be I32, I64 + * @param reg_no_dst the index of dest register + * @param reg_no_src the index of register hold operand value of add operation + * @param m_dst the dest memory operand + * + * @return true if success, false otherwise + */ +static bool +at_and(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, x86::Mem &m_dst) +{ + AT_RMW_LOGICAL_LOOP(and, kind_dst, bytes_dst); +} + +/** + * Encode atomic logical binary operation: or + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to move, could be I32, I64 + * @param reg_no_dst the index of dest register + * @param reg_no_src the index of register hold operand value of add operation + * @param m_dst the dest memory operand + * + * @return true if success, false otherwise + */ +static bool +at_or(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, x86::Mem &m_dst) +{ + AT_RMW_LOGICAL_LOOP(or, kind_dst, bytes_dst); +} +/** + * Encode atomic logical binary operation: xor + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data, + * could be 1(byte), 2(short), 4(int32), 8(int64), + * @param kind_dst the kind of data to move, could be I32, I64 + * @param reg_no_dst the index of dest register + * @param reg_no_src the index of register hold operand value of add operation + * @param m_dst the dest memory operand + * + * @return true if success, false otherwise + */ +static bool +at_xor(x86::Assembler &a, uint32 bytes_dst, uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, x86::Mem &m_dst) +{ + AT_RMW_LOGICAL_LOOP(xor, kind_dst, bytes_dst); +} + +/** + * Encode atomic rmw and: load value into a register from memory with reg base + * and reg offset, bitwise and loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(first operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(second operand&store back) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_and_imm_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_and(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw and: load value into a register from memory with reg base + * and reg offset, bitwise and loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_and_imm_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_and(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw and: load value into a register from memory with reg base + * and imm offset, bitwise and value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_and_r_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return at_and(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw and: load value into a register from memory with reg base + * and reg offset, bitwise and loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_and_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return at_and(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw or: load value into a register from memory with reg base + * and reg offset, bitwise or loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(first operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(second operand&store back) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_or_imm_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_or(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw or: load value into a register from memory with reg base + * and reg offset, bitwise or loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_or_imm_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, void *data_src, + int32 reg_no_base, int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_or(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw or: load value into a register from memory with reg base + * and imm offset, bitwise or loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_or_r_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return at_or(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw or: load value into a register from memory with reg base + * and reg offset, bitwise or loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_or_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, int32 reg_no_src, + int32 reg_no_base, int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return at_or(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw xor: load value into a register from memory with reg base + * and reg offset, bitwise xor loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(first operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(second operand&store back) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xor_imm_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_xor(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw xor: load value into a register from memory with reg base + * and reg offset, bitwise xor loaded value with imm data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param data_src the immediate data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xor_imm_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + void *data_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + Imm imm; + imm_set_value(imm, data_src, bytes_dst); + uint32 reg_no_src = mov_imm_to_free_reg(a, imm, bytes_dst); + return at_xor(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw xor: load value into a register from memory with reg base + * and imm offset, bitwise xor exchange loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back location) + * @param offset the offset address of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xor_r_base_r_offset_imm(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 offset) +{ + x86::Mem m(regs_i64[reg_no_base], offset, bytes_dst); + return at_xor(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode atomic rmw xor: load value into a register from memory with reg base + * and reg offset, bitwise xor loaded value with reg data, store back + * + * @param a the assembler to emit the code + * @param bytes_dst the bytes number of the data to actual operated on(load, + * compare, replacement) could be 1(byte), 2(short), 4(int32), 8(int64) + * @param reg_no_dst the no of register that stores the returned value + * @param reg_no_src the no of register store the src data(second operand) + * @param reg_no_base the no of register that stores the base address + * of src&dst memory(first operand&store back) + * @param reg_no_offset the no of register that stores the offset of the memory + * @return true if success, false otherwise + */ +static bool +at_rmw_xor_r_base_r_offset_r(x86::Assembler &a, uint32 bytes_dst, + uint32 kind_dst, int32 reg_no_dst, + int32 reg_no_src, int32 reg_no_base, + int32 reg_no_offset) +{ + x86::Mem m(regs_i64[reg_no_base], regs_i64[reg_no_offset], 0, 0, bytes_dst); + return at_xor(a, bytes_dst, kind_dst, reg_no_dst, reg_no_src, m) + && extend_r_to_r(a, bytes_dst, kind_dst, REG_RAX_IDX, reg_no_dst); +} + +/** + * Encode insn rmw RMW_type r0, r1, r2, r3 + * @param bin_op the operation, can be add/sub/xchg/and/or/xor + * @param kind the data kind, can only be I32 or I64 + * @param bytes_dst the byte number of dst data + */ +#define AT_RMW_R_R_R_R(bin_op, kind, type, bytes_dst) \ + do { \ + type data_src = 0; \ + int32 reg_no_dst = 0, reg_no_src = 0, reg_no_base = 0, \ + reg_no_offset = 0; \ + int32 offset = 0; \ + bool _ret = false; \ + if (jit_reg_is_const(r3)) { \ + CHECK_KIND(r3, JIT_REG_KIND_I32); \ + } \ + else { \ + CHECK_KIND(r3, JIT_REG_KIND_I64); \ + } \ + /* r0: read/return value r2: memory base addr can't be const */ \ + /* already check it's not const in LOAD_4ARGS() */ \ + reg_no_dst = jit_reg_no(r0); \ + CHECK_REG_NO(reg_no_dst, jit_reg_kind(r0)); \ + /* mem_data base address has to be non-const */ \ + CHECK_NCONST(r2); \ + reg_no_base = jit_reg_no(r2); \ + CHECK_REG_NO(reg_no_base, jit_reg_kind(r2)); \ + /* r1: source operand value r3: offset can be const */ \ + if (jit_reg_is_const(r1)) \ + data_src = jit_cc_get_const_##kind(cc, r1); \ + else { \ + reg_no_src = jit_reg_no(r1); \ + CHECK_REG_NO(reg_no_src, jit_reg_kind(r1)); \ + } \ + if (jit_reg_is_const(r3)) \ + offset = jit_cc_get_const_I32(cc, r3); \ + else { \ + reg_no_offset = jit_reg_no(r3); \ + CHECK_REG_NO(reg_no_offset, jit_reg_kind(r3)); \ + } \ + \ + if (jit_reg_is_const(r1)) { \ + if (jit_reg_is_const(r3)) \ + _ret = at_rmw_##bin_op##_imm_base_r_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_dst, &data_src, \ + reg_no_base, offset); \ + else \ + _ret = at_rmw_##bin_op##_imm_base_r_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_dst, &data_src, \ + reg_no_base, reg_no_offset); \ + } \ + else { \ + if (jit_reg_is_const(r3)) \ + _ret = at_rmw_##bin_op##_r_base_r_offset_imm( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_dst, reg_no_src, \ + reg_no_base, offset); \ + else \ + _ret = at_rmw_##bin_op##_r_base_r_offset_r( \ + a, bytes_dst, JIT_REG_KIND_##kind, reg_no_dst, reg_no_src, \ + reg_no_base, reg_no_offset); \ + } \ + if (!_ret) \ + GOTO_FAIL; \ + } while (0) + +/** + * Encode insn mfence + **/ +static void +fence(x86::Assembler &a) +{ + a.mfence(); +} + +/** + * Encode insn fence + */ +#define FENCE() fence(a) + +#endif + +bool +jit_codegen_gen_native(JitCompContext *cc) +{ + bool atomic; + JitBasicBlock *block; + JitInsn *insn; + JitReg r0, r1, r2, r3, r4; + JmpInfo jmp_info_head; + bh_list *jmp_info_list = (bh_list *)&jmp_info_head; + uint32 label_index, label_num, i; + uint32 *label_offsets = NULL, code_size; +#if CODEGEN_DUMP != 0 + uint32 code_offset = 0; +#endif + bool return_value = false, is_last_insn; + void **jitted_addr; + char *code_buf, *stream; + + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code; + code.init(env); + code.setErrorHandler(&err_handler); + x86::Assembler a(&code); + + if (BH_LIST_SUCCESS != bh_list_init(jmp_info_list)) { + jit_set_last_error(cc, "init jmp info list failed"); + return false; + } + + label_num = jit_cc_label_num(cc); + + if (!(label_offsets = + (uint32 *)jit_calloc(((uint32)sizeof(uint32)) * label_num))) { + jit_set_last_error(cc, "allocate memory failed"); + goto fail; + } + + for (i = 0; i < label_num; i++) { + if (i == 0) + label_index = 0; + else if (i == label_num - 1) + label_index = 1; + else + label_index = i + 1; + + label_offsets[label_index] = code.sectionById(0)->buffer().size(); + + block = *jit_annl_basic_block( + cc, jit_reg_new(JIT_REG_KIND_L32, label_index)); + +#if CODEGEN_DUMP != 0 + os_printf("\nL%d:\n\n", label_index); +#endif + + JIT_FOREACH_INSN(block, insn) + { + is_last_insn = (insn->next == block) ? true : false; + +#if CODEGEN_DUMP != 0 + os_printf("\n"); + jit_dump_insn(cc, insn); +#endif + switch (insn->opcode) { + case JIT_OP_MOV: + LOAD_2ARGS(); + if (!lower_mov(cc, a, r0, r1)) + GOTO_FAIL; + break; + + case JIT_OP_I8TOI32: + LOAD_2ARGS(); + CONVERT_R_R(I32, I32, i32, i8, int8); + break; + + case JIT_OP_I8TOI64: + LOAD_2ARGS(); + CONVERT_R_R(I64, I32, i64, i8, int8); + break; + + case JIT_OP_I16TOI32: + LOAD_2ARGS(); + CONVERT_R_R(I32, I32, i32, i16, int16); + break; + + case JIT_OP_I16TOI64: + LOAD_2ARGS(); + CONVERT_R_R(I64, I32, i64, i16, int16); + break; + + case JIT_OP_I32TOI8: + LOAD_2ARGS(); + CONVERT_R_R(I32, I32, i8, i32, int32); + break; + + case JIT_OP_I32TOU8: + LOAD_2ARGS(); + CONVERT_R_R(I32, I32, u8, i32, int32); + break; + + case JIT_OP_I32TOI16: + LOAD_2ARGS(); + CONVERT_R_R(I32, I32, i16, i32, int32); + break; + + case JIT_OP_I32TOU16: + LOAD_2ARGS(); + CONVERT_R_R(I32, I32, u16, i32, int32); + break; + + case JIT_OP_I32TOI64: + LOAD_2ARGS(); + CONVERT_R_R(I64, I32, i64, i32, int32); + break; + + case JIT_OP_U32TOI64: + LOAD_2ARGS(); + CONVERT_R_R(I64, I32, i64, u32, int32); + break; + + case JIT_OP_I32TOF32: + LOAD_2ARGS(); + CONVERT_R_R(F32, I32, f32, i32, int32); + break; + + case JIT_OP_U32TOF32: + LOAD_2ARGS(); + CONVERT_R_R(F32, I32, f32, u32, uint32); + break; + + case JIT_OP_I32TOF64: + LOAD_2ARGS(); + CONVERT_R_R(F64, I32, f64, i32, int32); + break; + + case JIT_OP_U32TOF64: + LOAD_2ARGS(); + CONVERT_R_R(F64, I32, f64, u32, uint32); + break; + + case JIT_OP_I64TOI8: + LOAD_2ARGS(); + CONVERT_R_R(I32, I64, i8, i64, int64); + break; + + case JIT_OP_I64TOI16: + LOAD_2ARGS(); + CONVERT_R_R(I32, I64, i16, i64, int64); + break; + + case JIT_OP_I64TOI32: + LOAD_2ARGS(); + CONVERT_R_R(I32, I64, i32, i64, int64); + break; + + case JIT_OP_I64TOF32: + LOAD_2ARGS(); + CONVERT_R_R(F32, I64, f32, i64, int64); + break; + + case JIT_OP_I64TOF64: + LOAD_2ARGS(); + CONVERT_R_R(F64, I64, f64, i64, int64); + break; + + case JIT_OP_F32TOI32: + LOAD_2ARGS(); + CONVERT_R_R(I32, F32, i32, f32, float32); + break; + + case JIT_OP_F32TOI64: + LOAD_2ARGS(); + CONVERT_R_R(I64, F32, i64, f32, float32); + break; + + case JIT_OP_F32TOF64: + LOAD_2ARGS(); + CONVERT_R_R(F64, F32, f64, f32, float32); + break; + + case JIT_OP_F32TOU32: + LOAD_2ARGS(); + CONVERT_R_R(I32, F32, u32, f32, float32); + break; + + case JIT_OP_F64TOI32: + LOAD_2ARGS(); + CONVERT_R_R(I32, F64, i32, f64, float64); + break; + + case JIT_OP_F64TOI64: + LOAD_2ARGS(); + CONVERT_R_R(I64, F64, i64, f64, float64); + break; + + case JIT_OP_F64TOF32: + LOAD_2ARGS(); + CONVERT_R_R(F32, F64, f32, f64, float64); + break; + + case JIT_OP_F64TOU32: + LOAD_2ARGS(); + CONVERT_R_R(I32, F64, u32, f64, float64); + break; + + case JIT_OP_NEG: + LOAD_2ARGS(); + if (!lower_neg(cc, a, r0, r1)) + GOTO_FAIL; + break; + + case JIT_OP_ADD: + case JIT_OP_SUB: + case JIT_OP_MUL: + case JIT_OP_DIV_S: + case JIT_OP_REM_S: + case JIT_OP_DIV_U: + case JIT_OP_REM_U: + LOAD_3ARGS(); + if (!lower_alu(cc, a, + (ALU_OP)(ADD + (insn->opcode - JIT_OP_ADD)), + r0, r1, r2)) + GOTO_FAIL; + break; + + case JIT_OP_SHL: + case JIT_OP_SHRS: + case JIT_OP_SHRU: + case JIT_OP_ROTL: + case JIT_OP_ROTR: + LOAD_3ARGS(); + if (!lower_shift( + cc, a, + (SHIFT_OP)(SHL + (insn->opcode - JIT_OP_SHL)), r0, + r1, r2)) + GOTO_FAIL; + break; + + case JIT_OP_OR: + case JIT_OP_XOR: + case JIT_OP_AND: + LOAD_3ARGS(); + if (!lower_bit(cc, a, + (BIT_OP)(OR + (insn->opcode - JIT_OP_OR)), + r0, r1, r2)) + GOTO_FAIL; + break; + + case JIT_OP_CLZ: + case JIT_OP_CTZ: + case JIT_OP_POPCNT: + LOAD_2ARGS(); + if (!lower_bitcount( + cc, a, + (BITCOUNT_OP)(CLZ + (insn->opcode - JIT_OP_CLZ)), + r0, r1)) + GOTO_FAIL; + break; + + case JIT_OP_CMP: + LOAD_3ARGS(); + if (!lower_cmp(cc, a, r0, r1, r2)) + GOTO_FAIL; + break; + + case JIT_OP_SELECTEQ: + case JIT_OP_SELECTNE: + case JIT_OP_SELECTGTS: + case JIT_OP_SELECTGES: + case JIT_OP_SELECTLTS: + case JIT_OP_SELECTLES: + case JIT_OP_SELECTGTU: + case JIT_OP_SELECTGEU: + case JIT_OP_SELECTLTU: + case JIT_OP_SELECTLEU: + LOAD_4ARGS(); + if (!lower_select( + cc, a, + (COND_OP)(EQ + (insn->opcode - JIT_OP_SELECTEQ)), + r0, r1, r2, r3)) + GOTO_FAIL; + break; + + case JIT_OP_LDEXECENV: + LOAD_1ARG(); + CHECK_KIND(r0, JIT_REG_KIND_I32); + /* TODO */ + break; + + case JIT_OP_LDJITINFO: + LOAD_1ARG(); + CHECK_KIND(r0, JIT_REG_KIND_I32); + /* TODO */ + break; + + case JIT_OP_LDI8: + LOAD_3ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + LD_R_R_R(I32, 1, true); + else + LD_R_R_R(I64, 1, true); + break; + + case JIT_OP_LDU8: + LOAD_3ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + LD_R_R_R(I32, 1, false); + else + LD_R_R_R(I64, 1, false); + break; + + case JIT_OP_LDI16: + LOAD_3ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + LD_R_R_R(I32, 2, true); + else + LD_R_R_R(I64, 2, true); + break; + + case JIT_OP_LDU16: + LOAD_3ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + LD_R_R_R(I32, 2, false); + else + LD_R_R_R(I64, 2, false); + break; + + case JIT_OP_LDI32: + LOAD_3ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + LD_R_R_R(I32, 4, true); + else + LD_R_R_R(I64, 4, true); + break; + + case JIT_OP_LDU32: + LOAD_3ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + LD_R_R_R(I32, 4, false); + else + LD_R_R_R(I64, 4, false); + break; + + case JIT_OP_LDI64: + case JIT_OP_LDU64: + case JIT_OP_LDPTR: + LOAD_3ARGS(); + LD_R_R_R(I64, 8, false); + break; + + case JIT_OP_LDF32: + LOAD_3ARGS(); + LD_R_R_R(F32, 4, false); + break; + + case JIT_OP_LDF64: + LOAD_3ARGS(); + LD_R_R_R(F64, 8, false); + break; + + case JIT_OP_STI8: + LOAD_3ARGS_NO_ASSIGN(); + atomic = insn->flags_u8 & 0x1; + ST_R_R_R(I32, int32, 1, atomic); + break; + + case JIT_OP_STI16: + LOAD_3ARGS_NO_ASSIGN(); + atomic = insn->flags_u8 & 0x1; + ST_R_R_R(I32, int32, 2, atomic); + break; + + case JIT_OP_STI32: + LOAD_3ARGS_NO_ASSIGN(); + atomic = insn->flags_u8 & 0x1; + ST_R_R_R(I32, int32, 4, atomic); + break; + + case JIT_OP_STI64: + LOAD_3ARGS_NO_ASSIGN(); + atomic = insn->flags_u8 & 0x1; + ST_R_R_R(I64, int64, 8, atomic); + break; + + case JIT_OP_STPTR: + LOAD_3ARGS_NO_ASSIGN(); + ST_R_R_R(I64, int64, 8, false); + break; + + case JIT_OP_STF32: + LOAD_3ARGS_NO_ASSIGN(); + ST_R_R_R(F32, float32, 4, false); + break; + + case JIT_OP_STF64: + LOAD_3ARGS_NO_ASSIGN(); + ST_R_R_R(F64, float64, 8, false); + break; + + case JIT_OP_JMP: + LOAD_1ARG(); + CHECK_KIND(r0, JIT_REG_KIND_L32); + if (!(is_last_insn + && label_is_neighboring(cc, label_index, + jit_reg_no(r0)))) + JMP_TO_LABEL(jit_reg_no(r0), label_index); + break; + + case JIT_OP_BEQ: + case JIT_OP_BNE: + case JIT_OP_BGTS: + case JIT_OP_BGES: + case JIT_OP_BLTS: + case JIT_OP_BLES: + case JIT_OP_BGTU: + case JIT_OP_BGEU: + case JIT_OP_BLTU: + case JIT_OP_BLEU: + LOAD_3ARGS(); + if (!lower_branch( + cc, a, jmp_info_list, label_index, + (COND_OP)(EQ + (insn->opcode - JIT_OP_BEQ)), r0, r1, + r2, is_last_insn)) + GOTO_FAIL; + break; + + case JIT_OP_LOOKUPSWITCH: + { + JitOpndLookupSwitch *opnd = jit_insn_opndls(insn); + if (!lower_lookupswitch(cc, a, jmp_info_list, label_offsets, + label_index, opnd, is_last_insn)) + GOTO_FAIL; + break; + } + + case JIT_OP_CALLNATIVE: + if (!lower_callnative(cc, a, insn)) + GOTO_FAIL; + break; + + case JIT_OP_CALLBC: + if (!lower_callbc(cc, a, jmp_info_list, label_index, insn)) + GOTO_FAIL; + break; + + case JIT_OP_RETURNBC: + if (!lower_returnbc(cc, a, insn)) + GOTO_FAIL; + break; + + case JIT_OP_RETURN: + if (!lower_return(cc, a, insn)) + GOTO_FAIL; + break; + + case JIT_OP_I32CASTF32: + LOAD_2ARGS(); + CAST_R_R(F32, I32, f32, i32, int32); + break; + + case JIT_OP_I64CASTF64: + LOAD_2ARGS(); + CAST_R_R(F64, I64, f64, i64, int64); + break; + + case JIT_OP_F32CASTI32: + LOAD_2ARGS(); + CAST_R_R(I32, F32, i32, f32, float); + break; + + case JIT_OP_F64CASTI64: + LOAD_2ARGS(); + CAST_R_R(I64, F64, i64, f64, double); + break; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case JIT_OP_AT_CMPXCHGU8: + LOAD_4ARGS_NO_ASSIGN(); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + CMPXCHG_R_R_R_R_R(I32, int32, 1); + else + CMPXCHG_R_R_R_R_R(I64, int64, 1); + break; + + case JIT_OP_AT_CMPXCHGU16: + LOAD_4ARGS_NO_ASSIGN(); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + CMPXCHG_R_R_R_R_R(I32, int32, 2); + else + CMPXCHG_R_R_R_R_R(I64, int64, 2); + break; + + case JIT_OP_AT_CMPXCHGI32: + LOAD_4ARGS_NO_ASSIGN(); + CMPXCHG_R_R_R_R_R(I32, int32, 4); + break; + + case JIT_OP_AT_CMPXCHGU32: + LOAD_4ARGS_NO_ASSIGN(); + CMPXCHG_R_R_R_R_R(I64, int32, 4); + break; + + case JIT_OP_AT_CMPXCHGI64: + LOAD_4ARGS_NO_ASSIGN(); + CMPXCHG_R_R_R_R_R(I64, int64, 8); + break; + + case JIT_OP_AT_ADDU8: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(add, I32, int32, 1); + else + AT_RMW_R_R_R_R(add, I64, int64, 1); + break; + + case JIT_OP_AT_ADDU16: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(add, I32, int32, 2); + else + AT_RMW_R_R_R_R(add, I64, int64, 2); + break; + + case JIT_OP_AT_ADDI32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(add, I32, int32, 4); + break; + + case JIT_OP_AT_ADDU32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(add, I64, int64, 4); + break; + + case JIT_OP_AT_ADDI64: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(add, I64, int64, 8); + break; + + case JIT_OP_AT_SUBU8: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(sub, I32, int32, 1); + else + AT_RMW_R_R_R_R(sub, I64, int64, 1); + break; + + case JIT_OP_AT_SUBU16: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(sub, I32, int32, 2); + else + AT_RMW_R_R_R_R(sub, I64, int64, 2); + break; + + case JIT_OP_AT_SUBI32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(sub, I32, int32, 4); + break; + + case JIT_OP_AT_SUBU32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(sub, I64, int64, 4); + break; + + case JIT_OP_AT_SUBI64: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(sub, I64, int64, 8); + break; + + case JIT_OP_AT_XCHGU8: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(xchg, I32, int32, 1); + else + AT_RMW_R_R_R_R(xchg, I64, int64, 1); + break; + + case JIT_OP_AT_XCHGU16: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(xchg, I32, int32, 2); + else + AT_RMW_R_R_R_R(xchg, I64, int64, 2); + break; + + case JIT_OP_AT_XCHGI32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(xchg, I32, int32, 4); + break; + + case JIT_OP_AT_XCHGU32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(xchg, I64, int64, 4); + break; + + case JIT_OP_AT_XCHGI64: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(xchg, I64, int64, 8); + break; + + case JIT_OP_AT_ANDU8: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(and, I32, int32, 1); + else + AT_RMW_R_R_R_R(and, I64, int64, 1); + break; + + case JIT_OP_AT_ANDU16: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(and, I32, int32, 2); + else + AT_RMW_R_R_R_R(and, I64, int64, 2); + break; + + case JIT_OP_AT_ANDI32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(and, I32, int32, 4); + break; + + case JIT_OP_AT_ANDU32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(and, I64, int64, 4); + break; + + case JIT_OP_AT_ANDI64: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(and, I64, int64, 8); + break; + + case JIT_OP_AT_ORU8: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(or, I32, int32, 1); + else + AT_RMW_R_R_R_R(or, I64, int64, 1); + break; + + case JIT_OP_AT_ORU16: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(or, I32, int32, 2); + else + AT_RMW_R_R_R_R(or, I64, int64, 2); + break; + + case JIT_OP_AT_ORI32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(or, I32, int32, 4); + break; + + case JIT_OP_AT_ORU32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(or, I64, int64, 4); + break; + + case JIT_OP_AT_ORI64: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(or, I64, int64, 8); + break; + + case JIT_OP_AT_XORU8: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(xor, I32, int32, 1); + else + AT_RMW_R_R_R_R(xor, I64, int64, 1); + break; + + case JIT_OP_AT_XORU16: + LOAD_4ARGS(); + bh_assert(jit_reg_kind(r0) == JIT_REG_KIND_I32 + || jit_reg_kind(r0) == JIT_REG_KIND_I64); + if (jit_reg_kind(r0) == JIT_REG_KIND_I32) + AT_RMW_R_R_R_R(xor, I32, int32, 2); + else + AT_RMW_R_R_R_R(xor, I64, int64, 2); + break; + + case JIT_OP_AT_XORI32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(xor, I32, int32, 4); + break; + + case JIT_OP_AT_XORU32: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(xor, I64, int64, 4); + break; + + case JIT_OP_AT_XORI64: + LOAD_4ARGS(); + AT_RMW_R_R_R_R(xor, I64, int64, 8); + break; + + case JIT_OP_FENCE: + FENCE(); + break; + +#endif + + default: + jit_set_last_error_v(cc, "unsupported JIT opcode 0x%2x", + insn->opcode); + GOTO_FAIL; + } + + if (err_handler.err) { + jit_set_last_error_v(cc, + "failed to generate native code for JIT " + "opcode 0x%02x, ErrorCode is %u", + insn->opcode, err_handler.err); + GOTO_FAIL; + } + +#if CODEGEN_DUMP != 0 + dump_native((char *)code.sectionById(0)->buffer().data() + + code_offset, + code.sectionById(0)->buffer().size() - code_offset); + code_offset = code.sectionById(0)->buffer().size(); +#endif + } + } + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + if (!(stream = (char *)jit_code_cache_alloc(code_size))) { + jit_set_last_error(cc, "allocate memory failed"); + goto fail; + } + + bh_memcpy_s(stream, code_size, code_buf, code_size); + cc->jitted_addr_begin = stream; + cc->jitted_addr_end = stream + code_size; + + for (i = 0; i < label_num; i++) { + if (i == 0) + label_index = 0; + else if (i == label_num - 1) + label_index = 1; + else + label_index = i + 1; + + jitted_addr = jit_annl_jitted_addr( + cc, jit_reg_new(JIT_REG_KIND_L32, label_index)); + *jitted_addr = stream + label_offsets[label_index]; + } + + patch_jmp_info_list(cc, jmp_info_list); + return_value = true; + +fail: + + jit_free(label_offsets); + free_jmp_info_list(jmp_info_list); + return return_value; +} + +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 + +#define MAX_REG_INTS 6 +#define MAX_REG_FLOATS 8 + +void * +jit_codegen_compile_call_to_llvm_jit(const WASMType *func_type) +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + x86::Gp reg_lp = x86::r10, reg_res = x86::r12; + x86::Gp reg_tmp_i64 = x86::r11, reg_tmp_i32 = x86::r11d; + /* the index of integer argument registers */ + uint8 reg_idx_of_int_args[] = { REG_RDI_IDX, REG_RSI_IDX, REG_RDX_IDX, + REG_RCX_IDX, REG_R8_IDX, REG_R9_IDX }; + uint32 n_ints = 0, n_fps = 0, n_stacks = 0, n_pushed; + uint32 int_reg_idx = 0, fp_reg_idx = 0, stack_arg_idx = 0; + uint32 off_to_lp = 0, off_to_res = 0, code_size, i; + uint32 param_count = func_type->param_count; + uint32 result_count = func_type->result_count; + uint32 ext_result_count; + char *code_buf, *stream; + Imm imm; + + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code; + code.init(env); + code.setErrorHandler(&err_handler); + x86::Assembler a(&code); + + /* Load the llvm jit function pointer */ + { + /* r11 = exec_env->module_inst */ + x86::Mem m1(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, module_inst)); + a.mov(reg_tmp_i64, m1); + /* r11 = module_inst->func_ptrs */ + x86::Mem m2(reg_tmp_i64, + (uint32)offsetof(WASMModuleInstance, func_ptrs)); + a.mov(reg_tmp_i64, m2); + /* rax = func_ptrs[func_idx] */ + x86::Mem m3(reg_tmp_i64, x86::rdx, 3, 0); + a.mov(x86::rax, m3); + } + + n_ints++; /* exec_env */ + + for (i = 0; i < param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_I64: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + if (n_ints < MAX_REG_INTS) + n_ints++; + else + n_stacks++; + break; + case VALUE_TYPE_F32: + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS) + n_fps++; + else + n_stacks++; + break; + } + } + + ext_result_count = result_count > 1 ? result_count - 1 : 0; + + if (ext_result_count > 0) { + if (n_ints + ext_result_count <= MAX_REG_INTS) { + /* extra result pointers can be stored into int registers */ + n_ints += ext_result_count; + } + else { + /* part or all extra result pointers must be stored into stack */ + n_stacks += n_ints + ext_result_count - MAX_REG_INTS; + n_ints = MAX_REG_INTS; + } + } + + n_pushed = n_stacks; + if (n_stacks & 1) { + /* Align stack on 16 bytes */ + n_pushed++; + } + if (n_pushed > 0) { + imm.setValue(n_pushed * 8); + a.sub(x86::rsp, imm); + } + + /* r10 = outs_area->lp */ + { + x86::Mem m(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, wasm_stack.top)); + a.mov(reg_lp, m); + a.add(reg_lp, (uint32)offsetof(WASMInterpFrame, lp)); + } + + /* rdi = exec_env */ + a.mov(regs_i64[reg_idx_of_int_args[int_reg_idx++]], + regs_i64[hreg_info->exec_env_hreg_index]); + + for (i = 0; i < param_count; i++) { + x86::Mem m_src(reg_lp, off_to_lp); + + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + { + if (int_reg_idx < MAX_REG_INTS) { + a.mov(regs_i32[reg_idx_of_int_args[int_reg_idx]], m_src); + int_reg_idx++; + } + else { + a.mov(reg_tmp_i32, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i32); + stack_arg_idx++; + } + off_to_lp += 4; + break; + } + case VALUE_TYPE_I64: + { + if (int_reg_idx < MAX_REG_INTS) { + a.mov(regs_i64[reg_idx_of_int_args[int_reg_idx]], m_src); + int_reg_idx++; + } + else { + a.mov(reg_tmp_i64, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i64); + stack_arg_idx++; + } + off_to_lp += 8; + break; + } + case VALUE_TYPE_F32: + { + if (fp_reg_idx < MAX_REG_FLOATS) { + a.movss(regs_float[fp_reg_idx], m_src); + fp_reg_idx++; + } + else { + a.mov(reg_tmp_i32, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i32); + stack_arg_idx++; + } + off_to_lp += 4; + break; + } + case VALUE_TYPE_F64: + { + if (fp_reg_idx < MAX_REG_FLOATS) { + a.movsd(regs_float[fp_reg_idx], m_src); + fp_reg_idx++; + } + else { + a.mov(reg_tmp_i64, m_src); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i64); + stack_arg_idx++; + } + off_to_lp += 8; + break; + } + } + } + + if (result_count > 0) { + switch (func_type->types[param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_F32: + off_to_res = 4; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + off_to_res = 8; + break; + } + + /* r12 = cur_frame->sp */ + x86::Mem m(x86::rbp, (uint32)offsetof(WASMInterpFrame, sp)); + a.mov(reg_res, m); + + for (i = 0; i < ext_result_count; i++) { + x86::Mem m(reg_res, off_to_res); + + if (int_reg_idx < MAX_REG_INTS) { + a.lea(regs_i64[reg_idx_of_int_args[int_reg_idx]], m); + int_reg_idx++; + } + else { + a.lea(reg_tmp_i64, m); + x86::Mem m_dst(x86::rsp, stack_arg_idx * 8); + a.mov(m_dst, reg_tmp_i64); + stack_arg_idx++; + } + + switch (func_type->types[param_count + 1 + i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_F32: + off_to_res += 4; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + off_to_res += 8; + break; + } + } + } + + bh_assert(int_reg_idx == n_ints); + bh_assert(fp_reg_idx == n_fps); + bh_assert(stack_arg_idx == n_stacks); + + /* Call the llvm jit function */ + a.call(x86::rax); + + /* Check if there was exception thrown */ + { + /* r11 = exec_env->module_inst */ + x86::Mem m1(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, module_inst)); + a.mov(reg_tmp_i64, m1); + /* module_inst->cur_exception */ + x86::Mem m2(reg_tmp_i64, + (uint32)offsetof(WASMModuleInstance, cur_exception)); + /* bl = module_inst->cur_exception[0] */ + a.mov(x86::bl, m2); + + /* cur_exception[0] == 0 ? */ + Imm imm((uint8)0); + a.cmp(x86::bl, imm); + /* If yes, jump to `Get function result and return` */ + imm.setValue(INT32_MAX); + a.je(imm); + + char *stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + /* If no, set eax to JIT_INTERP_ACTION_THROWN, and + jump to code_block_return_to_interp_from_jitted to + return to interpreter */ + imm.setValue(JIT_INTERP_ACTION_THROWN); + a.mov(x86::eax, imm); + imm.setValue(code_block_return_to_interp_from_jitted); + a.mov(x86::rsi, imm); + a.jmp(x86::rsi); + + char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + *(int32 *)(stream - 4) = (uint32)(stream_new - stream); + } + + /* Get function result and return */ + + if (result_count > 0 && func_type->types[param_count] != VALUE_TYPE_F32 + && func_type->types[param_count] != VALUE_TYPE_F64) { + a.mov(x86::rdx, x86::rax); + } + + if (off_to_res > 0) { + imm.setValue(off_to_res); + a.add(reg_res, imm); + /* cur_frame->sp = r12 */ + x86::Mem m(x86::rbp, (uint32)offsetof(WASMInterpFrame, sp)); + a.mov(m, reg_res); + } + + if (n_pushed > 0) { + imm.setValue(n_pushed * 8); + a.add(x86::rsp, imm); + } + + /* Return to the caller */ + { + /* eax = action = JIT_INTERP_ACTION_NORMAL */ + Imm imm(0); + a.mov(x86::eax, imm); + + uint32 jitted_return_addr_offset = + jit_frontend_get_jitted_return_addr_offset(); + x86::Mem m(x86::rbp, jitted_return_addr_offset); + a.jmp(m); + } + + if (err_handler.err) + return NULL; + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + return NULL; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + +#if 0 + dump_native(stream, code_size); +#endif + + return stream; +} + +static WASMInterpFrame * +fast_jit_alloc_frame(WASMExecEnv *exec_env, uint32 param_cell_num, + uint32 ret_cell_num) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + WASMInterpFrame *frame; + uint32 size_frame1 = wasm_interp_interp_frame_size(ret_cell_num); + uint32 size_frame2 = wasm_interp_interp_frame_size(param_cell_num); + + /** + * Check whether we can allocate two frames: the first is an implied + * frame to store the function results from jit function to call, + * the second is the frame for the jit function + */ + if ((uint8 *)exec_env->wasm_stack.top + size_frame1 + size_frame2 + > exec_env->wasm_stack.top_boundary) { + wasm_set_exception(module_inst, "wasm operand stack overflow"); + return NULL; + } + + /* Allocate the frame */ + frame = (WASMInterpFrame *)exec_env->wasm_stack.top; + exec_env->wasm_stack.top += size_frame1; + + frame->function = NULL; + frame->ip = NULL; + frame->sp = frame->lp; + frame->prev_frame = wasm_exec_env_get_cur_frame(exec_env); + frame->jitted_return_addr = + (uint8 *)code_block_return_to_interp_from_jitted; + + wasm_exec_env_set_cur_frame(exec_env, frame); + + return frame; +} + +void * +jit_codegen_compile_call_to_fast_jit(const WASMModule *module, uint32 func_idx) +{ + uint32 func_idx_non_import = func_idx - module->import_function_count; + WASMType *func_type = module->functions[func_idx_non_import]->func_type; + /* the index of integer argument registers */ + uint8 reg_idx_of_int_args[] = { REG_RDI_IDX, REG_RSI_IDX, REG_RDX_IDX, + REG_RCX_IDX, REG_R8_IDX, REG_R9_IDX }; + uint32 int_reg_idx, fp_reg_idx, stack_arg_idx; + uint32 switch_info_offset, exec_env_offset, stack_arg_offset; + uint32 int_reg_offset, frame_lp_offset; + uint32 switch_info_size, code_size, i; + uint32 param_count = func_type->param_count; + uint32 result_count = func_type->result_count; + uint32 ext_result_count = result_count > 1 ? result_count - 1 : 0; + uint32 param_cell_num = func_type->param_cell_num; + uint32 ret_cell_num = + func_type->ret_cell_num > 2 ? func_type->ret_cell_num : 2; + char *code_buf, *stream; + Imm imm; + + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code; + code.init(env); + code.setErrorHandler(&err_handler); + x86::Assembler a(&code); + + /** + * Push JitInterpSwitchInfo and make stack 16-byte aligned: + * the size pushed must be odd multiples of 8, as the stack pointer + * %rsp must be aligned to a 16-byte boundary before making a call, + * and when a function (including this llvm jit function) gets + * control, the %rsp is not 16-byte aligned (call instruction will + * push the ret address to stack). + */ + switch_info_size = align_uint((uint32)sizeof(JitInterpSwitchInfo), 16) + 8; + imm.setValue((uint64)switch_info_size); + a.sub(x86::rsp, imm); + + /* Push all integer argument registers since we will use them as + temporarily registers to load/store data */ + for (i = 0; i < MAX_REG_INTS; i++) { + a.push(regs_i64[reg_idx_of_int_args[MAX_REG_INTS - 1 - i]]); + } + + /* We don't push float/double register since we don't use them here */ + + /** + * Layout of the stack now: + * stack arguments + * ret address of the caller + * switch info + * int registers: r9, r8, rcx, rdx, rsi + * exec_env: rdi + */ + + /* offset of the first stack argument to the stack pointer, + add 8 to skip the ret address of the caller */ + stack_arg_offset = switch_info_size + 8 * MAX_REG_INTS + 8; + /* offset of jit interp switch info to the stack pointer */ + switch_info_offset = 8 * MAX_REG_INTS; + /* offset of the first int register to the stack pointer */ + int_reg_offset = 8; + /* offset of exec_env to the stack pointer */ + exec_env_offset = 0; + + /* Call fast_jit_alloc_frame to allocate the stack frame to + receive the results of the fast jit function to call */ + + /* rdi = exec_env, has been already set as exec_env is + the first argument of LLVM JIT function */ + /* rsi = param_cell_num */ + imm.setValue(param_cell_num); + a.mov(x86::rsi, imm); + /* rdx = ret_cell_num */ + imm.setValue(ret_cell_num); + a.mov(x86::rdx, imm); + /* call fast_jit_alloc_frame */ + imm.setValue((uint64)(uintptr_t)fast_jit_alloc_frame); + a.mov(x86::rax, imm); + a.call(x86::rax); + + /* Check the return value, note now rax is the allocated frame */ + { + /* Did fast_jit_alloc_frame return NULL? */ + Imm imm((uint64)0); + a.cmp(x86::rax, imm); + /* If no, jump to `Copy arguments to frame lp area` */ + imm.setValue(INT32_MAX); + a.jne(imm); + + char *stream = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + /* If yes, set eax to 0, return to caller */ + + /* Pop all integer argument registers */ + for (i = 0; i < MAX_REG_INTS; i++) { + a.pop(regs_i64[reg_idx_of_int_args[i]]); + } + /* Pop jit interp switch info */ + imm.setValue((uint64)switch_info_size); + a.add(x86::rsp, imm); + + /* Return to the caller, don't use leave as we didn't + `push rbp` and `mov rbp, rsp` */ + a.ret(); + + /* Patch the offset of jne instruction */ + char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + *(int32 *)(stream - 4) = (int32)(stream_new - stream); + } + + int_reg_idx = 1; /* skip exec_env */ + fp_reg_idx = 0; + stack_arg_idx = 0; + + /* Offset of the dest arguments to outs area */ + frame_lp_offset = wasm_interp_interp_frame_size(ret_cell_num) + + (uint32)offsetof(WASMInterpFrame, lp); + + /* Copy arguments to frame lp area */ + for (i = 0; i < func_type->param_count; i++) { + x86::Mem m_dst(x86::rax, frame_lp_offset); + switch (func_type->types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + if (int_reg_idx < MAX_REG_INTS) { + /* Copy i32 argument from int register */ + x86::Mem m_src(x86::rsp, int_reg_offset); + a.mov(x86::esi, m_src); + a.mov(m_dst, x86::esi); + int_reg_offset += 8; + int_reg_idx++; + } + else { + /* Copy i32 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::esi, m_src); + a.mov(m_dst, x86::esi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 4; + break; + case VALUE_TYPE_I64: + if (int_reg_idx < MAX_REG_INTS) { + /* Copy i64 argument from int register */ + x86::Mem m_src(x86::rsp, int_reg_offset); + a.mov(x86::rsi, m_src); + a.mov(m_dst, x86::rsi); + int_reg_offset += 8; + int_reg_idx++; + } + else { + /* Copy i64 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::rsi, m_src); + a.mov(m_dst, x86::rsi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 8; + break; + case VALUE_TYPE_F32: + if (fp_reg_idx < MAX_REG_FLOATS) { + /* Copy f32 argument from fp register */ + a.movss(m_dst, regs_float[fp_reg_idx++]); + } + else { + /* Copy f32 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::esi, m_src); + a.mov(m_dst, x86::esi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 4; + break; + case VALUE_TYPE_F64: + if (fp_reg_idx < MAX_REG_FLOATS) { + /* Copy f64 argument from fp register */ + a.movsd(m_dst, regs_float[fp_reg_idx++]); + } + else { + /* Copy f64 argument from stack */ + x86::Mem m_src(x86::rsp, stack_arg_offset); + a.mov(x86::rsi, m_src); + a.mov(m_dst, x86::rsi); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 8; + break; + default: + bh_assert(0); + } + } + + /* Call the fast jit function */ + { + /* info = rsp + switch_info_offset */ + a.lea(x86::rsi, x86::ptr(x86::rsp, switch_info_offset)); + /* info.frame = frame = rax, or return of fast_jit_alloc_frame */ + x86::Mem m1(x86::rsi, (uint32)offsetof(JitInterpSwitchInfo, frame)); + a.mov(m1, x86::rax); + + /* Call code_block_switch_to_jitted_from_interp + with argument (exec_env, info, func_idx, pc) */ + /* rdi = exec_env */ + a.mov(x86::rdi, x86::ptr(x86::rsp, exec_env_offset)); + /* rsi = info, has been set */ + /* rdx = func_idx */ + imm.setValue(func_idx); + a.mov(x86::rdx, imm); + /* module_inst = exec_env->module_inst */ + a.mov(x86::rcx, + x86::ptr(x86::rdi, (uint32)offsetof(WASMExecEnv, module_inst))); + /* fast_jit_func_ptrs = module_inst->fast_jit_func_ptrs */ + a.mov(x86::rcx, + x86::ptr(x86::rcx, (uint32)offsetof(WASMModuleInstance, + fast_jit_func_ptrs))); + imm.setValue(func_idx_non_import); + a.mov(x86::rax, imm); + x86::Mem m3(x86::rcx, x86::rax, 3, 0); + /* rcx = module_inst->fast_jit_func_ptrs[func_idx_non_import] */ + a.mov(x86::rcx, m3); + + imm.setValue( + (uint64)(uintptr_t)code_block_switch_to_jitted_from_interp); + a.mov(x86::rax, imm); + a.call(x86::rax); + } + + /* No need to check exception thrown here as it will be checked + in the caller */ + + /* Copy function results */ + if (result_count > 0) { + frame_lp_offset = offsetof(WASMInterpFrame, lp); + + switch (func_type->types[param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + a.mov(x86::eax, x86::edx); + frame_lp_offset += 4; + break; + case VALUE_TYPE_I64: + a.mov(x86::rax, x86::rdx); + frame_lp_offset += 8; + break; + case VALUE_TYPE_F32: + /* The first result has been put to xmm0 */ + frame_lp_offset += 4; + break; + case VALUE_TYPE_F64: + /* The first result has been put to xmm0 */ + frame_lp_offset += 8; + break; + default: + bh_assert(0); + } + + /* Copy extra results from exec_env->cur_frame */ + if (ext_result_count > 0) { + /* rdi = exec_env */ + a.mov(x86::rdi, x86::ptr(x86::rsp, exec_env_offset)); + /* rsi = exec_env->cur_frame */ + a.mov(x86::rsi, + x86::ptr(x86::rdi, (uint32)offsetof(WASMExecEnv, cur_frame))); + + for (i = 0; i < ext_result_count; i++) { + switch (func_type->types[param_count + 1 + i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + case VALUE_TYPE_F32: + { + /* Copy 32-bit result */ + a.mov(x86::ecx, x86::ptr(x86::rsi, frame_lp_offset)); + if (int_reg_idx < MAX_REG_INTS) { + x86::Mem m1(x86::rsp, + exec_env_offset + int_reg_idx * 8); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::ecx); + int_reg_idx++; + } + else { + x86::Mem m1(x86::rsp, stack_arg_offset); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::ecx); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 4; + break; + } + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + { + /* Copy 64-bit result */ + a.mov(x86::rcx, x86::ptr(x86::rsi, frame_lp_offset)); + if (int_reg_idx < MAX_REG_INTS) { + x86::Mem m1(x86::rsp, + exec_env_offset + int_reg_idx * 8); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::rcx); + int_reg_idx++; + } + else { + x86::Mem m1(x86::rsp, stack_arg_offset); + a.mov(x86::rdx, m1); + x86::Mem m2(x86::rdx, 0); + a.mov(m2, x86::rcx); + stack_arg_offset += 8; + stack_arg_idx++; + } + frame_lp_offset += 8; + break; + } + default: + bh_assert(0); + } + } + } + } + + /* Free the frame allocated */ + + /* rdi = exec_env */ + a.mov(x86::rdi, x86::ptr(x86::rsp, exec_env_offset)); + /* rsi = exec_env->cur_frame */ + a.mov(x86::rsi, + x86::ptr(x86::rdi, (uint32)offsetof(WASMExecEnv, cur_frame))); + /* rdx = exec_env->cur_frame->prev_frame */ + a.mov(x86::rdx, + x86::ptr(x86::rsi, (uint32)offsetof(WASMInterpFrame, prev_frame))); + /* exec_env->wasm_stack.top = cur_frame */ + { + x86::Mem m(x86::rdi, offsetof(WASMExecEnv, wasm_stack.top)); + a.mov(m, x86::rsi); + } + /* exec_env->cur_frame = prev_frame */ + { + x86::Mem m(x86::rdi, offsetof(WASMExecEnv, cur_frame)); + a.mov(m, x86::rdx); + } + + /* Pop all integer argument registers */ + for (i = 0; i < MAX_REG_INTS; i++) { + a.pop(regs_i64[reg_idx_of_int_args[i]]); + } + /* Pop jit interp switch info */ + imm.setValue((uint64)switch_info_size); + a.add(x86::rsp, imm); + + /* Return to the caller, don't use leave as we didn't + `push rbp` and `mov rbp, rsp` */ + a.ret(); + + if (err_handler.err) { + return NULL; + } + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + return NULL; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + +#if 0 + printf("Code of call to fast jit of func %u:\n", func_idx); + dump_native(stream, code_size); + printf("\n"); +#endif + + return stream; +} + +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 */ + +bool +jit_codegen_lower(JitCompContext *cc) +{ + (void)cc; + return true; +} + +void +jit_codegen_free_native(JitCompContext *cc) +{ + (void)cc; +} + +void +jit_codegen_dump_native(void *begin_addr, void *end_addr) +{ +#if WASM_ENABLE_FAST_JIT_DUMP != 0 + os_printf("\n"); + dump_native((char *)begin_addr, (char *)end_addr - (char *)begin_addr); + os_printf("\n"); +#else + (void)begin_addr; + (void)end_addr; +#endif +} + +bool +jit_codegen_init() +{ + const JitHardRegInfo *hreg_info = jit_codegen_get_hreg_info(); + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); + char *code_buf, *stream; + uint32 code_size; + + JitErrorHandler err_handler; + Environment env(Arch::kX64); + CodeHolder code; + code.init(env); + code.setErrorHandler(&err_handler); + x86::Assembler a(&code); + + /* Initialize code_block_switch_to_jitted_from_interp */ + + /* push callee-save registers */ + a.push(x86::rbp); + a.push(x86::rbx); + a.push(x86::r12); + a.push(x86::r13); + a.push(x86::r14); + a.push(x86::r15); + /* push info */ + a.push(x86::rsi); + + /* Note: the number of register pushed must be odd, as the stack pointer + %rsp must be aligned to a 16-byte boundary before making a call, so + when a function (including this function) gets control, %rsp is not + aligned. We push odd number registers here to make %rsp happy before + calling native functions. */ + + /* exec_env_reg = exec_env */ + a.mov(regs_i64[hreg_info->exec_env_hreg_index], x86::rdi); + /* fp_reg = info->frame */ + a.mov(x86::rbp, x86::ptr(x86::rsi, offsetof(JitInterpSwitchInfo, frame))); + /* rdx = func_idx, is already set in the func_idx argument of + jit_codegen_interp_jitted_glue */ + /* jmp target, rcx = pc */ + a.jmp(x86::rcx); + + if (err_handler.err) + return false; + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + return false; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + code_block_switch_to_jitted_from_interp = stream; + +#if 0 + dump_native(stream, code_size); +#endif + + /* Initialize code_block_return_to_interp_from_jitted */ + + a.setOffset(0); + + /* pop info */ + a.pop(x86::rsi); + /* info->frame = fp_reg */ + { + x86::Mem m(x86::rsi, offsetof(JitInterpSwitchInfo, frame)); + a.mov(m, x86::rbp); + } + /* info->out.ret.ival[0, 1] = rdx */ + { + x86::Mem m(x86::rsi, offsetof(JitInterpSwitchInfo, out.ret.ival)); + a.mov(m, x86::rdx); + } + /* info->out.ret.fval[0, 1] = xmm0 */ + { + x86::Mem m(x86::rsi, offsetof(JitInterpSwitchInfo, out.ret.fval)); + a.movsd(m, x86::xmm0); + } + + /* pop callee-save registers */ + a.pop(x86::r15); + a.pop(x86::r14); + a.pop(x86::r13); + a.pop(x86::r12); + a.pop(x86::rbx); + a.pop(x86::rbp); + a.ret(); + + if (err_handler.err) + goto fail1; + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + goto fail1; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + code_block_return_to_interp_from_jitted = + jit_globals->return_to_interp_from_jitted = stream; + +#if 0 + dump_native(stream, code_size); +#endif + +#if WASM_ENABLE_LAZY_JIT != 0 + /* Initialize code_block_compile_fast_jit_and_then_call */ + + a.setOffset(0); + + /* Use rbx, r12, r13 to save func_dix, module_inst and module, + as they are callee-save registers */ + + /* Backup func_idx: rbx = rdx = func_idx, note that rdx has + been prepared in the caller: + callbc or code_block_switch_to_jitted_from_interp */ + a.mov(x86::rbx, x86::rdx); + /* r12 = module_inst = exec_env->module_inst */ + { + x86::Mem m(regs_i64[hreg_info->exec_env_hreg_index], + (uint32)offsetof(WASMExecEnv, module_inst)); + a.mov(x86::r12, m); + } + /* rdi = r13 = module_inst->module */ + { + x86::Mem m(x86::r12, (uint32)offsetof(WASMModuleInstance, module)); + a.mov(x86::rdi, m); + a.mov(x86::r13, x86::rdi); + } + /* rsi = rdx = func_idx */ + a.mov(x86::rsi, x86::rdx); + /* Call jit_compiler_compile(module, func_idx) */ + { + Imm imm((uint64)(uintptr_t)jit_compiler_compile); + a.mov(x86::rax, imm); + a.call(x86::rax); + } + + /* Check if failed to compile the jit function */ + { + /* Did jit_compiler_compile return false? */ + Imm imm((uint8)0); + a.cmp(x86::al, imm); + /* If no, jump to `Load compiled func ptr and call it` */ + imm.setValue(INT32_MAX); + a.jne(imm); + + char *stream_old = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + + /* If yes, call jit_set_exception_with_id to throw exception, + and then set eax to JIT_INTERP_ACTION_THROWN, and jump to + code_block_return_to_interp_from_jitted to return */ + + /* rdi = module_inst */ + a.mov(x86::rdi, x86::r12); + /* rsi = EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC */ + imm.setValue(EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC); + a.mov(x86::rsi, imm); + /* Call jit_set_exception_with_id */ + imm.setValue((uint64)(uintptr_t)jit_set_exception_with_id); + a.mov(x86::rax, imm); + a.call(x86::rax); + /* Return to the caller */ + imm.setValue(JIT_INTERP_ACTION_THROWN); + a.mov(x86::eax, imm); + imm.setValue(code_block_return_to_interp_from_jitted); + a.mov(x86::rsi, imm); + a.jmp(x86::rsi); + + /* Patch the offset of jne instruction */ + char *stream_new = (char *)a.code()->sectionById(0)->buffer().data() + + a.code()->sectionById(0)->buffer().size(); + *(int32 *)(stream_old - 4) = (int32)(stream_new - stream_old); + } + + /* Load compiled func ptr and call it */ + { + /* rsi = module->import_function_count */ + x86::Mem m1(x86::r13, + (uint32)offsetof(WASMModule, import_function_count)); + a.movzx(x86::rsi, m1); + /* rbx = rbx - module->import_function_count */ + a.sub(x86::rbx, x86::rsi); + /* rax = module->fast_jit_func_ptrs */ + x86::Mem m2(x86::r13, (uint32)offsetof(WASMModule, fast_jit_func_ptrs)); + a.mov(x86::rax, m2); + /* rax = fast_jit_func_ptrs[rbx] */ + x86::Mem m3(x86::rax, x86::rbx, 3, 0); + a.mov(x86::rax, m3); + a.jmp(x86::rax); + } + + if (err_handler.err) + goto fail2; + + code_buf = (char *)code.sectionById(0)->buffer().data(); + code_size = code.sectionById(0)->buffer().size(); + stream = (char *)jit_code_cache_alloc(code_size); + if (!stream) + goto fail2; + + bh_memcpy_s(stream, code_size, code_buf, code_size); + code_block_compile_fast_jit_and_then_call = + jit_globals->compile_fast_jit_and_then_call = stream; + +#if 0 + dump_native(stream, code_size); +#endif +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 */ + + return true; + +#if WASM_ENABLE_LAZY_JIT != 0 +fail2: + jit_code_cache_free(code_block_return_to_interp_from_jitted); +#endif +fail1: + jit_code_cache_free(code_block_switch_to_jitted_from_interp); + return false; +} + +void +jit_codegen_destroy() +{ +#if WASM_ENABLE_LAZY_JIT != 0 + jit_code_cache_free(code_block_compile_fast_jit_and_then_call); +#endif + jit_code_cache_free(code_block_return_to_interp_from_jitted); + jit_code_cache_free(code_block_switch_to_jitted_from_interp); +} + +/* clang-format off */ +static const uint8 hreg_info_I32[3][7] = { + /* ebp, eax, ebx, ecx, edx, edi, esi */ + { 1, 0, 0, 0, 0, 0, 1 }, /* fixed, esi is freely used */ + { 0, 1, 0, 1, 1, 1, 0 }, /* caller_saved_native */ + { 0, 1, 1, 1, 1, 1, 0 } /* caller_saved_jitted */ +}; + +static const uint8 hreg_info_I64[3][16] = { + /* rbp, rax, rbx, rcx, rdx, rdi, rsi, rsp, + r8, r9, r10, r11, r12, r13, r14, r15 */ + { 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1 }, /* fixed, rsi is freely used */ + { 0, 1, 0, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 0, 0, 0, 0 }, /* caller_saved_native */ + { 0, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 0 }, /* caller_saved_jitted */ +}; + +/* System V AMD64 ABI Calling Conversion. [XYZ]MM0-7 */ +static uint8 hreg_info_F32[3][16] = { + /* xmm0 ~ xmm15 */ + { 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0 }, /* caller_saved_native */ + { 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0 }, /* caller_saved_jitted */ +}; + +/* System V AMD64 ABI Calling Conversion. [XYZ]MM0-7 */ +static uint8 hreg_info_F64[3][16] = { + /* xmm0 ~ xmm15 */ + { 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 1 }, + { 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0 }, /* caller_saved_native */ + { 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0 }, /* caller_saved_jitted */ +}; + +static const JitHardRegInfo g_hreg_info = { + { + { 0, NULL, NULL, NULL }, /* VOID */ + + { sizeof(hreg_info_I32[0]), /* I32 */ + hreg_info_I32[0], + hreg_info_I32[1], + hreg_info_I32[2] }, + + { sizeof(hreg_info_I64[0]), /* I64 */ + hreg_info_I64[0], + hreg_info_I64[1], + hreg_info_I64[2] }, + + { sizeof(hreg_info_F32[0]), /* F32 */ + hreg_info_F32[0], + hreg_info_F32[1], + hreg_info_F32[2] }, + + { sizeof(hreg_info_F64[0]), /* F64 */ + hreg_info_F64[0], + hreg_info_F64[1], + hreg_info_F64[2] }, + + { 0, NULL, NULL, NULL }, /* V8 */ + { 0, NULL, NULL, NULL }, /* V16 */ + { 0, NULL, NULL, NULL } /* V32 */ + }, + /* frame pointer hreg index: rbp */ + 0, + /* exec_env hreg index: r15 */ + 15, + /* cmp hreg index: esi */ + 6 +}; +/* clang-format on */ + +const JitHardRegInfo * +jit_codegen_get_hreg_info() +{ + return &g_hreg_info; +} + +static const char *reg_names_i32[] = { + "ebp", "eax", "ebx", "ecx", "edx", "edi", "esi", "esp", +}; + +static const char *reg_names_i64[] = { + "rbp", "rax", "rbx", "rcx", "rdx", "rdi", "rsi", "rsp", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", +}; + +static const char *reg_names_f32[] = { "xmm0", "xmm1", "xmm2", "xmm3", + "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10", "xmm11", + "xmm12", "xmm13", "xmm14", "xmm15" }; + +static const char *reg_names_f64[] = { + "xmm0_f64", "xmm1_f64", "xmm2_f64", "xmm3_f64", "xmm4_f64", "xmm5_f64", + "xmm6_f64", "xmm7_f64", "xmm8_f64", "xmm9_f64", "xmm10_f64", "xmm11_f64", + "xmm12_f64", "xmm13_f64", "xmm14_f64", "xmm15_f64" +}; + +JitReg +jit_codegen_get_hreg_by_name(const char *name) +{ + size_t i; + + if (name[0] == 'e') { + for (i = 0; i < sizeof(reg_names_i32) / sizeof(char *); i++) + if (!strcmp(reg_names_i32[i], name)) + return jit_reg_new(JIT_REG_KIND_I32, i); + } + else if (name[0] == 'r') { + for (i = 0; i < sizeof(reg_names_i64) / sizeof(char *); i++) + if (!strcmp(reg_names_i64[i], name)) + return jit_reg_new(JIT_REG_KIND_I64, i); + } + else if (!strncmp(name, "xmm", 3)) { + if (!strstr(name, "_f64")) { + for (i = 0; i < sizeof(reg_names_f32) / sizeof(char *); i++) + if (!strcmp(reg_names_f32[i], name)) + return jit_reg_new(JIT_REG_KIND_F32, i); + } + else { + for (i = 0; i < sizeof(reg_names_f64) / sizeof(char *); i++) + if (!strcmp(reg_names_f64[i], name)) + return jit_reg_new(JIT_REG_KIND_F64, i); + } + } + return 0; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_compare.c b/core/iwasm/fast-jit/fe/jit_emit_compare.c new file mode 100644 index 0000000000..02619a4d2f --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_compare.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_compare.h" +#include "jit_emit_function.h" +#include "../jit_frontend.h" +#include "../jit_codegen.h" + +static bool +jit_compile_op_compare_integer(JitCompContext *cc, IntCond cond, bool is64Bit) +{ + JitReg lhs, rhs, res, const_zero, const_one; + + if (cond < INT_EQZ || cond > INT_GE_U) { + jit_set_last_error(cc, "unsupported comparison operation"); + goto fail; + } + + res = jit_cc_new_reg_I32(cc); + const_zero = NEW_CONST(I32, 0); + const_one = NEW_CONST(I32, 1); + + if (is64Bit) { + if (INT_EQZ == cond) { + rhs = NEW_CONST(I64, 0); + } + else { + POP_I64(rhs); + } + POP_I64(lhs); + } + else { + if (INT_EQZ == cond) { + rhs = NEW_CONST(I32, 0); + } + else { + POP_I32(rhs); + } + POP_I32(lhs); + } + + GEN_INSN(CMP, cc->cmp_reg, lhs, rhs); + switch (cond) { + case INT_EQ: + case INT_EQZ: + { + GEN_INSN(SELECTEQ, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_NE: + { + GEN_INSN(SELECTNE, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_LT_S: + { + GEN_INSN(SELECTLTS, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_LT_U: + { + GEN_INSN(SELECTLTU, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_GT_S: + { + GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_GT_U: + { + GEN_INSN(SELECTGTU, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_LE_S: + { + GEN_INSN(SELECTLES, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_LE_U: + { + GEN_INSN(SELECTLEU, res, cc->cmp_reg, const_one, const_zero); + break; + } + case INT_GE_S: + { + GEN_INSN(SELECTGES, res, cc->cmp_reg, const_one, const_zero); + break; + } + default: /* INT_GE_U */ + { + GEN_INSN(SELECTGEU, res, cc->cmp_reg, const_one, const_zero); + break; + } + } + + PUSH_I32(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_compare(JitCompContext *cc, IntCond cond) +{ + return jit_compile_op_compare_integer(cc, cond, false); +} + +bool +jit_compile_op_i64_compare(JitCompContext *cc, IntCond cond) +{ + return jit_compile_op_compare_integer(cc, cond, true); +} + +static int32 +float_cmp_eq(float f1, float f2) +{ + if (isnan(f1) || isnan(f2)) + return 0; + + return f1 == f2; +} + +static int32 +float_cmp_ne(float f1, float f2) +{ + if (isnan(f1) || isnan(f2)) + return 1; + + return f1 != f2; +} + +static int32 +double_cmp_eq(double d1, double d2) +{ + if (isnan(d1) || isnan(d2)) + return 0; + + return d1 == d2; +} + +static int32 +double_cmp_ne(double d1, double d2) +{ + if (isnan(d1) || isnan(d2)) + return 1; + + return d1 != d2; +} + +static bool +jit_compile_op_compare_float_point(JitCompContext *cc, FloatCond cond, + JitReg lhs, JitReg rhs) +{ + JitReg res, args[2], const_zero, const_one; + JitRegKind kind; + void *func; + + if (cond == FLOAT_EQ || cond == FLOAT_NE) { + kind = jit_reg_kind(lhs); + if (cond == FLOAT_EQ) + func = (kind == JIT_REG_KIND_F32) ? (void *)float_cmp_eq + : (void *)double_cmp_eq; + else + func = (kind == JIT_REG_KIND_F32) ? (void *)float_cmp_ne + : (void *)double_cmp_ne; + + res = jit_cc_new_reg_I32(cc); + args[0] = lhs; + args[1] = rhs; + + if (!jit_emit_callnative(cc, func, res, args, 2)) { + goto fail; + } + } + else { + res = jit_cc_new_reg_I32(cc); + const_zero = NEW_CONST(I32, 0); + const_one = NEW_CONST(I32, 1); + switch (cond) { + case FLOAT_LT: + { + GEN_INSN(CMP, cc->cmp_reg, rhs, lhs); + GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero); + break; + } + case FLOAT_GT: + { + GEN_INSN(CMP, cc->cmp_reg, lhs, rhs); + GEN_INSN(SELECTGTS, res, cc->cmp_reg, const_one, const_zero); + break; + } + case FLOAT_LE: + { + GEN_INSN(CMP, cc->cmp_reg, rhs, lhs); + GEN_INSN(SELECTGES, res, cc->cmp_reg, const_one, const_zero); + break; + } + case FLOAT_GE: + { + GEN_INSN(CMP, cc->cmp_reg, lhs, rhs); + GEN_INSN(SELECTGES, res, cc->cmp_reg, const_one, const_zero); + break; + } + default: + { + bh_assert(!"unknown FloatCond"); + goto fail; + } + } + } + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_compare(JitCompContext *cc, FloatCond cond) +{ + JitReg res, const_zero, const_one; + JitReg lhs, rhs; + + POP_F32(rhs); + POP_F32(lhs); + + if (jit_reg_is_const(lhs) && jit_reg_is_const(rhs)) { + float32 lvalue = jit_cc_get_const_F32(cc, lhs); + float32 rvalue = jit_cc_get_const_F32(cc, rhs); + + const_zero = NEW_CONST(I32, 0); + const_one = NEW_CONST(I32, 1); + + switch (cond) { + case FLOAT_EQ: + { + res = (lvalue == rvalue) ? const_one : const_zero; + break; + } + case FLOAT_NE: + { + res = (lvalue != rvalue) ? const_one : const_zero; + break; + } + case FLOAT_LT: + { + res = (lvalue < rvalue) ? const_one : const_zero; + break; + } + case FLOAT_GT: + { + res = (lvalue > rvalue) ? const_one : const_zero; + break; + } + case FLOAT_LE: + { + res = (lvalue <= rvalue) ? const_one : const_zero; + break; + } + case FLOAT_GE: + { + res = (lvalue >= rvalue) ? const_one : const_zero; + break; + } + default: + { + bh_assert(!"unknown FloatCond"); + goto fail; + } + } + + PUSH_I32(res); + return true; + } + + return jit_compile_op_compare_float_point(cc, cond, lhs, rhs); +fail: + return false; +} + +bool +jit_compile_op_f64_compare(JitCompContext *cc, FloatCond cond) +{ + JitReg res, const_zero, const_one; + JitReg lhs, rhs; + + POP_F64(rhs); + POP_F64(lhs); + + if (jit_reg_is_const(lhs) && jit_reg_is_const(rhs)) { + float64 lvalue = jit_cc_get_const_F64(cc, lhs); + float64 rvalue = jit_cc_get_const_F64(cc, rhs); + + const_zero = NEW_CONST(I32, 0); + const_one = NEW_CONST(I32, 1); + + switch (cond) { + case FLOAT_EQ: + { + res = (lvalue == rvalue) ? const_one : const_zero; + break; + } + case FLOAT_NE: + { + res = (lvalue != rvalue) ? const_one : const_zero; + break; + } + case FLOAT_LT: + { + res = (lvalue < rvalue) ? const_one : const_zero; + break; + } + case FLOAT_GT: + { + res = (lvalue > rvalue) ? const_one : const_zero; + break; + } + case FLOAT_LE: + { + res = (lvalue <= rvalue) ? const_one : const_zero; + break; + } + case FLOAT_GE: + { + res = (lvalue >= rvalue) ? const_one : const_zero; + break; + } + default: + { + bh_assert(!"unknown FloatCond"); + goto fail; + } + } + + PUSH_I32(res); + return true; + } + + return jit_compile_op_compare_float_point(cc, cond, lhs, rhs); +fail: + return false; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_compare.h b/core/iwasm/fast-jit/fe/jit_emit_compare.h new file mode 100644 index 0000000000..db905b5508 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_compare.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_COMPARE_H_ +#define _JIT_EMIT_COMPARE_H_ + +#include "../jit_compiler.h" +#include "../jit_frontend.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_i32_compare(JitCompContext *cc, IntCond cond); + +bool +jit_compile_op_i64_compare(JitCompContext *cc, IntCond cond); + +bool +jit_compile_op_f32_compare(JitCompContext *cc, FloatCond cond); + +bool +jit_compile_op_f64_compare(JitCompContext *cc, FloatCond cond); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_COMPARE_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_const.c b/core/iwasm/fast-jit/fe/jit_emit_const.c new file mode 100644 index 0000000000..1bbc83c2f3 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_const.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_const.h" +#include "../jit_frontend.h" + +bool +jit_compile_op_i32_const(JitCompContext *cc, int32 i32_const) +{ + JitReg value = NEW_CONST(I32, i32_const); + PUSH_I32(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_const(JitCompContext *cc, int64 i64_const) +{ + JitReg value = NEW_CONST(I64, i64_const); + PUSH_I64(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_const(JitCompContext *cc, float32 f32_const) +{ + JitReg value = NEW_CONST(F32, f32_const); + PUSH_F32(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_const(JitCompContext *cc, float64 f64_const) +{ + JitReg value = NEW_CONST(F64, f64_const); + PUSH_F64(value); + return true; +fail: + return false; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_const.h b/core/iwasm/fast-jit/fe/jit_emit_const.h new file mode 100644 index 0000000000..b753141173 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_const.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_CONST_H_ +#define _JIT_EMIT_CONST_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_i32_const(JitCompContext *cc, int32 i32_const); + +bool +jit_compile_op_i64_const(JitCompContext *cc, int64 i64_const); + +bool +jit_compile_op_f32_const(JitCompContext *cc, float32 f32_const); + +bool +jit_compile_op_f64_const(JitCompContext *cc, float64 f64_const); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_CONST_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_control.c b/core/iwasm/fast-jit/fe/jit_emit_control.c new file mode 100644 index 0000000000..04140b6aeb --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_control.c @@ -0,0 +1,1318 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_control.h" +#include "jit_emit_exception.h" +#include "jit_emit_function.h" +#include "../jit_frontend.h" +#include "../interpreter/wasm_loader.h" + +#define CREATE_BASIC_BLOCK(new_basic_block) \ + do { \ + bh_assert(!new_basic_block); \ + if (!(new_basic_block = jit_cc_new_basic_block(cc, 0))) { \ + jit_set_last_error(cc, "create basic block failed"); \ + goto fail; \ + } \ + } while (0) + +#define CURR_BASIC_BLOCK() cc->cur_basic_block + +#define BUILD_BR(target_block) \ + do { \ + if (!GEN_INSN(JMP, jit_basic_block_label(target_block))) { \ + jit_set_last_error(cc, "generate jmp insn failed"); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_COND_BR(value_if, block_then, block_else) \ + do { \ + if (!GEN_INSN(CMP, cc->cmp_reg, value_if, NEW_CONST(I32, 0)) \ + || !GEN_INSN(BNE, cc->cmp_reg, jit_basic_block_label(block_then), \ + jit_basic_block_label(block_else))) { \ + jit_set_last_error(cc, "generate bne insn failed"); \ + goto fail; \ + } \ + } while (0) + +#define SET_BUILDER_POS(basic_block) \ + do { \ + cc->cur_basic_block = basic_block; \ + } while (0) + +#define SET_BB_BEGIN_BCIP(basic_block, bcip) \ + do { \ + *(jit_annl_begin_bcip(cc, jit_basic_block_label(basic_block))) = bcip; \ + } while (0) + +#define SET_BB_END_BCIP(basic_block, bcip) \ + do { \ + *(jit_annl_end_bcip(cc, jit_basic_block_label(basic_block))) = bcip; \ + } while (0) + +static JitBlock * +get_target_block(JitCompContext *cc, uint32 br_depth) +{ + uint32 i = br_depth; + JitBlock *block = jit_block_stack_top(&cc->block_stack); + + while (i-- > 0 && block) { + block = block->prev; + } + + if (!block) { + jit_set_last_error(cc, "WASM block stack underflow"); + return NULL; + } + return block; +} + +static bool +load_block_params(JitCompContext *cc, JitBlock *block) +{ + JitFrame *jit_frame = cc->jit_frame; + uint32 offset, i; + JitReg value = 0; + + /* Clear jit frame's locals and stacks */ + clear_values(jit_frame); + + /* Restore jit frame's sp to block's sp begin */ + jit_frame->sp = block->frame_sp_begin; + + /* Load params to new block */ + offset = (uint32)(jit_frame->sp - jit_frame->lp); + for (i = 0; i < block->param_count; i++) { + switch (block->param_types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + value = gen_load_i32(jit_frame, offset); + offset++; + break; + case VALUE_TYPE_I64: + value = gen_load_i64(jit_frame, offset); + offset += 2; + break; + case VALUE_TYPE_F32: + value = gen_load_f32(jit_frame, offset); + offset++; + break; + case VALUE_TYPE_F64: + value = gen_load_f64(jit_frame, offset); + offset += 2; + break; + default: + bh_assert(0); + break; + } + PUSH(value, block->param_types[i]); + } + + return true; +fail: + return false; +} + +static bool +load_block_results(JitCompContext *cc, JitBlock *block) +{ + JitFrame *jit_frame = cc->jit_frame; + uint32 offset, i; + JitReg value = 0; + + /* Restore jit frame's sp to block's sp begin */ + jit_frame->sp = block->frame_sp_begin; + + /* Load results to new block */ + offset = (uint32)(jit_frame->sp - jit_frame->lp); + for (i = 0; i < block->result_count; i++) { + switch (block->result_types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + value = gen_load_i32(jit_frame, offset); + offset++; + break; + case VALUE_TYPE_I64: + value = gen_load_i64(jit_frame, offset); + offset += 2; + break; + case VALUE_TYPE_F32: + value = gen_load_f32(jit_frame, offset); + offset++; + break; + case VALUE_TYPE_F64: + value = gen_load_f64(jit_frame, offset); + offset += 2; + break; + default: + bh_assert(0); + break; + } + PUSH(value, block->result_types[i]); + } + + return true; +fail: + return false; +} + +static bool +jit_reg_is_i32_const(JitCompContext *cc, JitReg reg, int32 val) +{ + return (jit_reg_kind(reg) == JIT_REG_KIND_I32 && jit_reg_is_const(reg) + && jit_cc_get_const_I32(cc, reg) == val) + ? true + : false; +} + +/** + * get the last two insns: + * CMP cmp_reg, r0, r1 + * SELECTcc r2, cmp_reg, 1, 0 + */ +static void +get_last_cmp_and_selectcc(JitCompContext *cc, JitReg cond, JitInsn **p_insn_cmp, + JitInsn **p_insn_select) +{ + JitInsn *insn = jit_basic_block_last_insn(cc->cur_basic_block); + + if (insn && insn->prev && insn->prev->opcode == JIT_OP_CMP + && insn->opcode >= JIT_OP_SELECTEQ && insn->opcode <= JIT_OP_SELECTLEU + && *jit_insn_opnd(insn, 0) == cond + && jit_reg_is_i32_const(cc, *jit_insn_opnd(insn, 2), 1) + && jit_reg_is_i32_const(cc, *jit_insn_opnd(insn, 3), 0)) { + *p_insn_cmp = insn->prev; + *p_insn_select = insn; + } +} + +static bool +push_jit_block_to_stack_and_pass_params(JitCompContext *cc, JitBlock *block, + JitBasicBlock *basic_block, JitReg cond, + bool merge_cmp_and_if) +{ + JitFrame *jit_frame = cc->jit_frame; + JitValue *value_list_head = NULL, *value_list_end = NULL, *jit_value; + JitInsn *insn; + JitReg value; + uint32 i, param_index, cell_num; + + if (cc->cur_basic_block == basic_block) { + /* Reuse the current basic block and no need to commit values, + we just move param values from current block's value stack to + the new block's value stack */ + for (i = 0; i < block->param_count; i++) { + jit_value = jit_value_stack_pop( + &jit_block_stack_top(&cc->block_stack)->value_stack); + if (!value_list_head) { + value_list_head = value_list_end = jit_value; + jit_value->prev = jit_value->next = NULL; + } + else { + jit_value->prev = NULL; + jit_value->next = value_list_head; + value_list_head->prev = jit_value; + value_list_head = jit_value; + } + } + block->value_stack.value_list_head = value_list_head; + block->value_stack.value_list_end = value_list_end; + + /* Save block's begin frame sp */ + cell_num = wasm_get_cell_num(block->param_types, block->param_count); + block->frame_sp_begin = jit_frame->sp - cell_num; + + /* Push the new block to block stack */ + jit_block_stack_push(&cc->block_stack, block); + + /* Continue to translate current block */ + } + else { + JitInsn *insn_select = NULL, *insn_cmp = NULL; + + if (merge_cmp_and_if) { + get_last_cmp_and_selectcc(cc, cond, &insn_cmp, &insn_select); + } + + /* Commit register values to locals and stacks */ + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + + /* Pop param values from current block's value stack */ + for (i = 0; i < block->param_count; i++) { + param_index = block->param_count - 1 - i; + POP(value, block->param_types[param_index]); + } + + /* Clear frame values */ + clear_values(jit_frame); + /* Save block's begin frame sp */ + block->frame_sp_begin = jit_frame->sp; + + /* Push the new block to block stack */ + jit_block_stack_push(&cc->block_stack, block); + + if (block->label_type == LABEL_TYPE_LOOP) { + BUILD_BR(basic_block); + } + else { + /* IF block with condition br insn */ + if (insn_select && insn_cmp) { + /* Change `CMP + SELECTcc` into `CMP + Bcc` */ + if (!(insn = GEN_INSN(BEQ, cc->cmp_reg, + jit_basic_block_label(basic_block), 0))) { + jit_set_last_error(cc, "generate cond br failed"); + goto fail; + } + insn->opcode = + JIT_OP_BEQ + (insn_select->opcode - JIT_OP_SELECTEQ); + jit_insn_unlink(insn_select); + jit_insn_delete(insn_select); + } + else { + if (!GEN_INSN(CMP, cc->cmp_reg, cond, NEW_CONST(I32, 0)) + || !(insn = + GEN_INSN(BNE, cc->cmp_reg, + jit_basic_block_label(basic_block), 0))) { + jit_set_last_error(cc, "generate cond br failed"); + goto fail; + } + } + + /* Don't create else basic block or end basic block now, just + save its incoming BNE insn, and patch the insn's else label + when the basic block is lazily created */ + if (block->wasm_code_else) { + block->incoming_insn_for_else_bb = insn; + } + else { + if (!jit_block_add_incoming_insn(block, insn, 2)) { + jit_set_last_error(cc, "add incoming insn failed"); + goto fail; + } + } + } + + /* Start to translate the block */ + SET_BUILDER_POS(basic_block); + + /* Push the block parameters */ + if (!load_block_params(cc, block)) { + goto fail; + } + } + return true; +fail: + return false; +} + +static void +copy_block_arities(JitCompContext *cc, JitReg dst_frame_sp, uint8 *dst_types, + uint32 dst_type_count, JitReg *p_first_res_reg) +{ + JitFrame *jit_frame; + uint32 offset_src, offset_dst, i; + JitReg value; + + jit_frame = cc->jit_frame; + offset_src = (uint32)(jit_frame->sp - jit_frame->lp) + - wasm_get_cell_num(dst_types, dst_type_count); + offset_dst = 0; + + /* pop values from stack and store to dest frame */ + for (i = 0; i < dst_type_count; i++) { + switch (dst_types[i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + value = gen_load_i32(jit_frame, offset_src); + if (i == 0 && p_first_res_reg) + *p_first_res_reg = value; + else + GEN_INSN(STI32, value, dst_frame_sp, + NEW_CONST(I32, offset_dst * 4)); + offset_src++; + offset_dst++; + break; + case VALUE_TYPE_I64: + value = gen_load_i64(jit_frame, offset_src); + if (i == 0 && p_first_res_reg) + *p_first_res_reg = value; + else + GEN_INSN(STI64, value, dst_frame_sp, + NEW_CONST(I32, offset_dst * 4)); + offset_src += 2; + offset_dst += 2; + break; + case VALUE_TYPE_F32: + value = gen_load_f32(jit_frame, offset_src); + if (i == 0 && p_first_res_reg) + *p_first_res_reg = value; + else + GEN_INSN(STF32, value, dst_frame_sp, + NEW_CONST(I32, offset_dst * 4)); + offset_src++; + offset_dst++; + break; + case VALUE_TYPE_F64: + value = gen_load_f64(jit_frame, offset_src); + if (i == 0 && p_first_res_reg) + *p_first_res_reg = value; + else + GEN_INSN(STF64, value, dst_frame_sp, + NEW_CONST(I32, offset_dst * 4)); + offset_src += 2; + offset_dst += 2; + break; + default: + bh_assert(0); + break; + } + } +} + +static bool +handle_func_return(JitCompContext *cc, JitBlock *block) +{ + JitReg prev_frame, prev_frame_sp; + JitReg ret_reg = 0; +#if WASM_ENABLE_PERF_PROFILING != 0 + JitReg func_inst = jit_cc_new_reg_ptr(cc); + JitReg time_start = jit_cc_new_reg_I64(cc); + JitReg time_end = jit_cc_new_reg_I64(cc); + JitReg cur_exec_time = jit_cc_new_reg_I64(cc); + JitReg total_exec_time = jit_cc_new_reg_I64(cc); + JitReg total_exec_cnt = jit_cc_new_reg_I32(cc); +#endif + +#if WASM_ENABLE_PERF_PROFILING != 0 + /* time_end = os_time_thread_cputime_us() */ + if (!jit_emit_callnative(cc, os_time_thread_cputime_us, time_end, NULL, + 0)) { + return false; + } + /* time_start = cur_frame->time_started */ + GEN_INSN(LDI64, time_start, cc->fp_reg, + NEW_CONST(I32, offsetof(WASMInterpFrame, time_started))); + /* cur_exec_time = time_end - time_start */ + GEN_INSN(SUB, cur_exec_time, time_end, time_start); + /* func_inst = cur_frame->function */ + GEN_INSN(LDPTR, func_inst, cc->fp_reg, + NEW_CONST(I32, offsetof(WASMInterpFrame, function))); + /* total_exec_time = func_inst->total_exec_time */ + GEN_INSN(LDI64, total_exec_time, func_inst, + NEW_CONST(I32, offsetof(WASMFunctionInstance, total_exec_time))); + /* total_exec_time += cur_exec_time */ + GEN_INSN(ADD, total_exec_time, total_exec_time, cur_exec_time); + /* func_inst->total_exec_time = total_exec_time */ + GEN_INSN(STI64, total_exec_time, func_inst, + NEW_CONST(I32, offsetof(WASMFunctionInstance, total_exec_time))); + /* totoal_exec_cnt = func_inst->total_exec_cnt */ + GEN_INSN(LDI32, total_exec_cnt, func_inst, + NEW_CONST(I32, offsetof(WASMFunctionInstance, total_exec_cnt))); + /* total_exec_cnt++ */ + GEN_INSN(ADD, total_exec_cnt, total_exec_cnt, NEW_CONST(I32, 1)); + /* func_inst->total_exec_cnt = total_exec_cnt */ + GEN_INSN(STI32, total_exec_cnt, func_inst, + NEW_CONST(I32, offsetof(WASMFunctionInstance, total_exec_cnt))); +#endif + + prev_frame = jit_cc_new_reg_ptr(cc); + prev_frame_sp = jit_cc_new_reg_ptr(cc); + + /* prev_frame = cur_frame->prev_frame */ + GEN_INSN(LDPTR, prev_frame, cc->fp_reg, + NEW_CONST(I32, offsetof(WASMInterpFrame, prev_frame))); + GEN_INSN(LDPTR, prev_frame_sp, prev_frame, + NEW_CONST(I32, offsetof(WASMInterpFrame, sp))); + + if (block->result_count) { + uint32 cell_num = + wasm_get_cell_num(block->result_types, block->result_count); + + copy_block_arities(cc, prev_frame_sp, block->result_types, + block->result_count, &ret_reg); + /* prev_frame->sp += cell_num */ + GEN_INSN(ADD, prev_frame_sp, prev_frame_sp, + NEW_CONST(PTR, cell_num * 4)); + GEN_INSN(STPTR, prev_frame_sp, prev_frame, + NEW_CONST(I32, offsetof(WASMInterpFrame, sp))); + } + + /* Free stack space of the current frame: + exec_env->wasm_stack.top = cur_frame */ + GEN_INSN(STPTR, cc->fp_reg, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, wasm_stack.top))); + /* Set the prev_frame as the current frame: + exec_env->cur_frame = prev_frame */ + GEN_INSN(STPTR, prev_frame, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, cur_frame))); + /* fp_reg = prev_frame */ + GEN_INSN(MOV, cc->fp_reg, prev_frame); + /* return 0 */ + GEN_INSN(RETURNBC, NEW_CONST(I32, JIT_INTERP_ACTION_NORMAL), ret_reg, 0); + + return true; +} + +/** + * is_block_polymorphic: whether current block's stack is in polymorphic state, + * if the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed + */ +static bool +handle_op_end(JitCompContext *cc, uint8 **p_frame_ip, bool is_block_polymorphic) +{ + JitFrame *jit_frame = cc->jit_frame; + JitBlock *block, *block_prev; + JitIncomingInsn *incoming_insn; + JitInsn *insn; + + /* Check block stack */ + if (!(block = jit_block_stack_top(&cc->block_stack))) { + jit_set_last_error(cc, "WASM block stack underflow"); + return false; + } + + if (!block->incoming_insns_for_end_bb) { + /* No other basic blocks jumping to this end, no need to + create the end basic block, just continue to translate + the following opcodes */ + if (block->label_type == LABEL_TYPE_FUNCTION) { + if (!handle_func_return(cc, block)) { + return false; + } + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + clear_values(jit_frame); + } + else if (block->result_count > 0) { + JitValue *value_list_head = NULL, *value_list_end = NULL; + JitValue *jit_value; + uint32 i; + + /* No need to change cc->jit_frame, just move result values + from current block's value stack to previous block's + value stack */ + block_prev = block->prev; + + for (i = 0; i < block->result_count; i++) { + jit_value = jit_value_stack_pop(&block->value_stack); + bh_assert(jit_value); + if (!value_list_head) { + value_list_head = value_list_end = jit_value; + jit_value->prev = jit_value->next = NULL; + } + else { + jit_value->prev = NULL; + jit_value->next = value_list_head; + value_list_head->prev = jit_value; + value_list_head = jit_value; + } + } + + if (!block_prev->value_stack.value_list_head) { + block_prev->value_stack.value_list_head = value_list_head; + block_prev->value_stack.value_list_end = value_list_end; + } + else { + /* Link to the end of previous block's value stack */ + block_prev->value_stack.value_list_end->next = value_list_head; + value_list_head->prev = block_prev->value_stack.value_list_end; + block_prev->value_stack.value_list_end = value_list_end; + } + } + + /* Pop block and destroy the block */ + block = jit_block_stack_pop(&cc->block_stack); + jit_block_destroy(block); + return true; + } + else { + /* Commit register values to locals and stacks */ + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + /* Clear frame values */ + clear_values(jit_frame); + + /* Create the end basic block */ + CREATE_BASIC_BLOCK(block->basic_block_end); + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + SET_BB_BEGIN_BCIP(block->basic_block_end, *p_frame_ip); + /* No need to create 'JMP' insn if block is in stack polymorphic + state, as previous br/br_table opcode has created 'JMP' insn + to this end basic block */ + if (!is_block_polymorphic) { + /* Jump to the end basic block */ + BUILD_BR(block->basic_block_end); + } + + /* Patch the INSNs which jump to this basic block */ + incoming_insn = block->incoming_insns_for_end_bb; + while (incoming_insn) { + insn = incoming_insn->insn; + + bh_assert( + insn->opcode == JIT_OP_JMP + || (insn->opcode >= JIT_OP_BEQ && insn->opcode <= JIT_OP_BLEU) + || insn->opcode == JIT_OP_LOOKUPSWITCH); + + if (insn->opcode == JIT_OP_JMP + || (insn->opcode >= JIT_OP_BEQ + && insn->opcode <= JIT_OP_BLEU)) { + *(jit_insn_opnd(insn, incoming_insn->opnd_idx)) = + jit_basic_block_label(block->basic_block_end); + } + else { + /* Patch LOOKUPSWITCH INSN */ + JitOpndLookupSwitch *opnd = jit_insn_opndls(insn); + if (incoming_insn->opnd_idx < opnd->match_pairs_num) { + opnd->match_pairs[incoming_insn->opnd_idx].target = + jit_basic_block_label(block->basic_block_end); + } + else { + opnd->default_target = + jit_basic_block_label(block->basic_block_end); + } + } + + incoming_insn = incoming_insn->next; + } + + SET_BUILDER_POS(block->basic_block_end); + + /* Pop block and load block results */ + block = jit_block_stack_pop(&cc->block_stack); + + if (block->label_type == LABEL_TYPE_FUNCTION) { + if (!handle_func_return(cc, block)) { + jit_block_destroy(block); + goto fail; + } + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + clear_values(jit_frame); + } + else { + if (!load_block_results(cc, block)) { + jit_block_destroy(block); + goto fail; + } + } + + jit_block_destroy(block); + return true; + } + return true; +fail: + return false; +} + +/** + * is_block_polymorphic: whether current block's stack is in polymorphic state, + * if the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed + */ +static bool +handle_op_else(JitCompContext *cc, uint8 **p_frame_ip, + bool is_block_polymorphic) +{ + JitBlock *block = jit_block_stack_top(&cc->block_stack); + JitFrame *jit_frame = cc->jit_frame; + JitInsn *insn; + + /* Check block */ + if (!block) { + jit_set_last_error(cc, "WASM block stack underflow"); + return false; + } + if (block->label_type != LABEL_TYPE_IF) { + jit_set_last_error(cc, "Invalid WASM block type"); + return false; + } + + if (!block->incoming_insn_for_else_bb) { + /* The if branch is handled like OP_BLOCK (cond is const and != 0), + just skip the else branch and handle OP_END */ + *p_frame_ip = block->wasm_code_end + 1; + return handle_op_end(cc, p_frame_ip, false); + } + else { + /* Has else branch and need to translate else branch */ + + /* Commit register values to locals and stacks */ + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + /* Clear frame values */ + clear_values(jit_frame); + + /* No need to create 'JMP' insn if block is in stack polymorphic + state, as previous br/br_table opcode has created 'JMP' insn + to this end basic block */ + if (!is_block_polymorphic) { + /* Jump to end basic block */ + if (!(insn = GEN_INSN(JMP, 0))) { + jit_set_last_error(cc, "generate jmp insn failed"); + return false; + } + if (!jit_block_add_incoming_insn(block, insn, 0)) { + jit_set_last_error(cc, "add incoming insn failed"); + return false; + } + } + + /* Clear value stack, restore param values and + start to translate the else branch. */ + jit_value_stack_destroy(&block->value_stack); + + /* create else basic block */ + CREATE_BASIC_BLOCK(block->basic_block_else); + SET_BB_END_BCIP(block->basic_block_entry, *p_frame_ip - 1); + SET_BB_BEGIN_BCIP(block->basic_block_else, *p_frame_ip); + + /* Patch the insn which conditionly jumps to the else basic block */ + insn = block->incoming_insn_for_else_bb; + *(jit_insn_opnd(insn, 2)) = + jit_basic_block_label(block->basic_block_else); + + SET_BUILDER_POS(block->basic_block_else); + + /* Reload block parameters */ + if (!load_block_params(cc, block)) { + return false; + } + + return true; + } + return true; +fail: + return false; +} + +static bool +handle_next_reachable_block(JitCompContext *cc, uint8 **p_frame_ip) +{ + JitBlock *block = jit_block_stack_top(&cc->block_stack); + + bh_assert(block); + + do { + if (block->label_type == LABEL_TYPE_IF + && block->incoming_insn_for_else_bb + && *p_frame_ip <= block->wasm_code_else) { + /* Else branch hasn't been translated, + start to translate the else branch */ + *p_frame_ip = block->wasm_code_else + 1; + /* Restore jit frame's sp to block's sp begin */ + cc->jit_frame->sp = block->frame_sp_begin; + return handle_op_else(cc, p_frame_ip, true); + } + else if (block->incoming_insns_for_end_bb) { + *p_frame_ip = block->wasm_code_end + 1; + /* Restore jit frame's sp to block's sp end */ + cc->jit_frame->sp = + block->frame_sp_begin + + wasm_get_cell_num(block->result_types, block->result_count); + return handle_op_end(cc, p_frame_ip, true); + } + else { + *p_frame_ip = block->wasm_code_end + 1; + jit_block_stack_pop(&cc->block_stack); + jit_block_destroy(block); + block = jit_block_stack_top(&cc->block_stack); + } + } while (block != NULL); + + return true; +} + +bool +jit_compile_op_block(JitCompContext *cc, uint8 **p_frame_ip, + uint8 *frame_ip_end, uint32 label_type, uint32 param_count, + uint8 *param_types, uint32 result_count, + uint8 *result_types, bool merge_cmp_and_if) +{ + BlockAddr block_addr_cache[BLOCK_ADDR_CACHE_SIZE][BLOCK_ADDR_CONFLICT_SIZE]; + JitBlock *block; + JitReg value; + uint8 *else_addr, *end_addr; + + /* Check block stack */ + if (!jit_block_stack_top(&cc->block_stack)) { + jit_set_last_error(cc, "WASM block stack underflow"); + return false; + } + + memset(block_addr_cache, 0, sizeof(block_addr_cache)); + + /* Get block info */ + if (!(wasm_loader_find_block_addr( + NULL, (BlockAddr *)block_addr_cache, *p_frame_ip, frame_ip_end, + (uint8)label_type, &else_addr, &end_addr))) { + jit_set_last_error(cc, "find block end addr failed"); + return false; + } + + /* Allocate memory */ + if (!(block = jit_calloc(sizeof(JitBlock)))) { + jit_set_last_error(cc, "allocate memory failed"); + return false; + } + + if (param_count && !(block->param_types = jit_calloc(param_count))) { + jit_set_last_error(cc, "allocate memory failed"); + goto fail; + } + if (result_count && !(block->result_types = jit_calloc(result_count))) { + jit_set_last_error(cc, "allocate memory failed"); + goto fail; + } + + /* Initialize block data */ + block->label_type = label_type; + block->param_count = param_count; + if (param_count) { + bh_memcpy_s(block->param_types, param_count, param_types, param_count); + } + block->result_count = result_count; + if (result_count) { + bh_memcpy_s(block->result_types, result_count, result_types, + result_count); + } + block->wasm_code_else = else_addr; + block->wasm_code_end = end_addr; + + if (label_type == LABEL_TYPE_BLOCK) { + /* Push the new jit block to block stack and continue to + translate current basic block */ + if (!push_jit_block_to_stack_and_pass_params( + cc, block, cc->cur_basic_block, 0, false)) + goto fail; + } + else if (label_type == LABEL_TYPE_LOOP) { + CREATE_BASIC_BLOCK(block->basic_block_entry); + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + SET_BB_BEGIN_BCIP(block->basic_block_entry, *p_frame_ip); + /* Push the new jit block to block stack and continue to + translate the new basic block */ + if (!push_jit_block_to_stack_and_pass_params( + cc, block, block->basic_block_entry, 0, false)) + goto fail; + } + else if (label_type == LABEL_TYPE_IF) { + POP_I32(value); + + if (!jit_reg_is_const(value)) { + /* Compare value is not constant, create condition br IR */ + + /* Create entry block */ + CREATE_BASIC_BLOCK(block->basic_block_entry); + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + SET_BB_BEGIN_BCIP(block->basic_block_entry, *p_frame_ip); + + if (!push_jit_block_to_stack_and_pass_params( + cc, block, block->basic_block_entry, value, + merge_cmp_and_if)) + goto fail; + } + else { + if (jit_cc_get_const_I32(cc, value) != 0) { + /* Compare value is not 0, condition is true, else branch of + BASIC_BLOCK if cannot be reached, we treat it same as + LABEL_TYPE_BLOCK and start to translate if branch */ + if (!push_jit_block_to_stack_and_pass_params( + cc, block, cc->cur_basic_block, 0, false)) + goto fail; + } + else { + if (else_addr) { + /* Compare value is not 0, condition is false, if branch of + BASIC_BLOCK if cannot be reached, we treat it same as + LABEL_TYPE_BLOCK and start to translate else branch */ + if (!push_jit_block_to_stack_and_pass_params( + cc, block, cc->cur_basic_block, 0, false)) + goto fail; + *p_frame_ip = else_addr + 1; + } + else { + /* The whole if block cannot be reached, skip it */ + jit_block_destroy(block); + *p_frame_ip = end_addr + 1; + } + } + } + } + else { + jit_set_last_error(cc, "Invalid block type"); + goto fail; + } + + return true; +fail: + /* Only destroy the block if it hasn't been pushed into + the block stack, or if will be destroyed again when + destroying the block stack */ + if (jit_block_stack_top(&cc->block_stack) != block) + jit_block_destroy(block); + return false; +} + +bool +jit_compile_op_else(JitCompContext *cc, uint8 **p_frame_ip) +{ + return handle_op_else(cc, p_frame_ip, false); +} + +bool +jit_compile_op_end(JitCompContext *cc, uint8 **p_frame_ip) +{ + return handle_op_end(cc, p_frame_ip, false); +} + +/* Check whether need to copy arities when jumping from current block + to the dest block */ +static bool +check_copy_arities(const JitBlock *block_dst, JitFrame *jit_frame) +{ + JitValueSlot *frame_sp_src = NULL; + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + frame_sp_src = + jit_frame->sp + - wasm_get_cell_num(block_dst->param_types, block_dst->param_count); + /* There are parameters to copy and the src/dst addr are different */ + return (block_dst->param_count > 0 + && block_dst->frame_sp_begin != frame_sp_src) + ? true + : false; + } + else { + frame_sp_src = jit_frame->sp + - wasm_get_cell_num(block_dst->result_types, + block_dst->result_count); + /* There are results to copy and the src/dst addr are different */ + return (block_dst->result_count > 0 + && block_dst->frame_sp_begin != frame_sp_src) + ? true + : false; + } +} + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +jit_check_suspend_flags(JitCompContext *cc) +{ + JitReg exec_env, suspend_flags, terminate_flag, offset; + JitBasicBlock *terminate_block, *cur_basic_block; + JitFrame *jit_frame = cc->jit_frame; + + cur_basic_block = cc->cur_basic_block; + terminate_block = jit_cc_new_basic_block(cc, 0); + if (!terminate_block) { + return false; + } + + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + exec_env = cc->exec_env_reg; + suspend_flags = jit_cc_new_reg_I32(cc); + terminate_flag = jit_cc_new_reg_I32(cc); + + offset = jit_cc_new_const_I32(cc, offsetof(WASMExecEnv, suspend_flags)); + GEN_INSN(LDI32, suspend_flags, exec_env, offset); + GEN_INSN(AND, terminate_flag, suspend_flags, NEW_CONST(I32, 1)); + + GEN_INSN(CMP, cc->cmp_reg, terminate_flag, NEW_CONST(I32, 0)); + GEN_INSN(BNE, cc->cmp_reg, jit_basic_block_label(terminate_block), 0); + + cc->cur_basic_block = terminate_block; + GEN_INSN(RETURN, NEW_CONST(I32, 0)); + + cc->cur_basic_block = cur_basic_block; + + return true; +} + +#endif + +static bool +handle_op_br(JitCompContext *cc, uint32 br_depth, uint8 **p_frame_ip) +{ + JitFrame *jit_frame; + JitBlock *block_dst, *block; + JitReg frame_sp_dst; + JitInsn *insn; + bool copy_arities; + uint32 offset; + + /* Check block stack */ + if (!(block = jit_block_stack_top(&cc->block_stack))) { + jit_set_last_error(cc, "WASM block stack underflow"); + return false; + } + + if (!(block_dst = get_target_block(cc, br_depth))) { + return false; + } + + jit_frame = cc->jit_frame; + + /* Only opy parameters or results when their count > 0 and + the src/dst addr are different */ + copy_arities = check_copy_arities(block_dst, jit_frame); + + if (copy_arities) { + frame_sp_dst = jit_cc_new_reg_ptr(cc); + offset = offsetof(WASMInterpFrame, lp) + + (block_dst->frame_sp_begin - jit_frame->lp) * 4; + GEN_INSN(ADD, frame_sp_dst, cc->fp_reg, NEW_CONST(PTR, offset)); + + /* No need to commit results as they will be copied to dest block */ + gen_commit_values(jit_frame, jit_frame->lp, block->frame_sp_begin); + } + else { + /* Commit all including results as they won't be copied */ + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + } + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + if (copy_arities) { + /* Dest block is Loop block, copy loop parameters */ + copy_block_arities(cc, frame_sp_dst, block_dst->param_types, + block_dst->param_count, NULL); + } + + clear_values(jit_frame); + + /* Jump to the begin basic block */ + BUILD_BR(block_dst->basic_block_entry); + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + } + else { + if (copy_arities) { + /* Dest block is Block/If/Function block, copy block results */ + copy_block_arities(cc, frame_sp_dst, block_dst->result_types, + block_dst->result_count, NULL); + } + + clear_values(jit_frame); + + /* Jump to the end basic block */ + if (!(insn = GEN_INSN(JMP, 0))) { + jit_set_last_error(cc, "generate jmp insn failed"); + goto fail; + } + if (!jit_block_add_incoming_insn(block_dst, insn, 0)) { + jit_set_last_error(cc, "add incoming insn failed"); + goto fail; + } + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + } + + return true; +fail: + return false; +} + +bool +jit_compile_op_br(JitCompContext *cc, uint32 br_depth, uint8 **p_frame_ip) +{ + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + return false; +#endif + + return handle_op_br(cc, br_depth, p_frame_ip) + && handle_next_reachable_block(cc, p_frame_ip); +} + +static JitFrame * +jit_frame_clone(const JitFrame *jit_frame) +{ + JitFrame *jit_frame_cloned; + uint32 max_locals = jit_frame->max_locals; + uint32 max_stacks = jit_frame->max_stacks; + uint32 total_size; + + total_size = (uint32)(offsetof(JitFrame, lp) + + sizeof(*jit_frame->lp) * (max_locals + max_stacks)); + + jit_frame_cloned = jit_calloc(total_size); + if (jit_frame_cloned) { + bh_memcpy_s(jit_frame_cloned, total_size, jit_frame, total_size); + jit_frame_cloned->sp = + jit_frame_cloned->lp + (jit_frame->sp - jit_frame->lp); + } + + return jit_frame_cloned; +} + +static void +jit_frame_copy(JitFrame *jit_frame_dst, const JitFrame *jit_frame_src) +{ + uint32 max_locals = jit_frame_src->max_locals; + uint32 max_stacks = jit_frame_src->max_stacks; + uint32 total_size; + + total_size = + (uint32)(offsetof(JitFrame, lp) + + sizeof(*jit_frame_src->lp) * (max_locals + max_stacks)); + bh_memcpy_s(jit_frame_dst, total_size, jit_frame_src, total_size); + jit_frame_dst->sp = + jit_frame_dst->lp + (jit_frame_src->sp - jit_frame_src->lp); +} + +bool +jit_compile_op_br_if(JitCompContext *cc, uint32 br_depth, + bool merge_cmp_and_br_if, uint8 **p_frame_ip) +{ + JitFrame *jit_frame, *jit_frame_cloned; + JitBlock *block_dst; + JitReg cond; + JitBasicBlock *cur_basic_block, *if_basic_block = NULL; + JitInsn *insn, *insn_select = NULL, *insn_cmp = NULL; + bool copy_arities; + + if (!(block_dst = get_target_block(cc, br_depth))) { + return false; + } + + /* append IF to current basic block */ + POP_I32(cond); + + if (merge_cmp_and_br_if) { + get_last_cmp_and_selectcc(cc, cond, &insn_cmp, &insn_select); + } + + jit_frame = cc->jit_frame; + cur_basic_block = cc->cur_basic_block; + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + + if (!(insn_select && insn_cmp)) { + if (!GEN_INSN(CMP, cc->cmp_reg, cond, NEW_CONST(I32, 0))) { + jit_set_last_error(cc, "generate cmp insn failed"); + goto fail; + } + } + + /* Only copy parameters or results when their count > 0 and + the src/dst addr are different */ + copy_arities = check_copy_arities(block_dst, jit_frame); + + if (!copy_arities) { + if (block_dst->label_type == LABEL_TYPE_LOOP) { + if (!(insn = GEN_INSN( + BNE, cc->cmp_reg, + jit_basic_block_label(block_dst->basic_block_entry), + 0))) { + jit_set_last_error(cc, "generate bne insn failed"); + goto fail; + } + } + else { + if (!(insn = GEN_INSN(BNE, cc->cmp_reg, 0, 0))) { + jit_set_last_error(cc, "generate bne insn failed"); + goto fail; + } + if (!jit_block_add_incoming_insn(block_dst, insn, 1)) { + jit_set_last_error(cc, "add incoming insn failed"); + goto fail; + } + } + if (insn_select && insn_cmp) { + /* Change `CMP + SELECTcc` into `CMP + Bcc` */ + insn->opcode = JIT_OP_BEQ + (insn_select->opcode - JIT_OP_SELECTEQ); + jit_insn_unlink(insn_select); + jit_insn_delete(insn_select); + } + return true; + } + + CREATE_BASIC_BLOCK(if_basic_block); + if (!(insn = GEN_INSN(BNE, cc->cmp_reg, + jit_basic_block_label(if_basic_block), 0))) { + jit_set_last_error(cc, "generate bne insn failed"); + goto fail; + } + if (insn_select && insn_cmp) { + /* Change `CMP + SELECTcc` into `CMP + Bcc` */ + insn->opcode = JIT_OP_BEQ + (insn_select->opcode - JIT_OP_SELECTEQ); + jit_insn_unlink(insn_select); + jit_insn_delete(insn_select); + } + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + return false; +#endif + + SET_BUILDER_POS(if_basic_block); + SET_BB_BEGIN_BCIP(if_basic_block, *p_frame_ip - 1); + + /* Clone current jit frame to a new jit fame */ + if (!(jit_frame_cloned = jit_frame_clone(jit_frame))) { + jit_set_last_error(cc, "allocate memory failed"); + goto fail; + } + + /* Clear current jit frame so that the registers + in the new basic block will be loaded again */ + clear_values(jit_frame); + if (!handle_op_br(cc, br_depth, p_frame_ip)) { + jit_free(jit_frame_cloned); + goto fail; + } + + /* Restore the jit frame so that the registers can + be used again in current basic block */ + jit_frame_copy(jit_frame, jit_frame_cloned); + jit_free(jit_frame_cloned); + + /* Continue processing opcodes after BR_IF */ + SET_BUILDER_POS(cur_basic_block); + return true; +fail: + return false; +} + +bool +jit_compile_op_br_table(JitCompContext *cc, uint32 *br_depths, uint32 br_count, + uint8 **p_frame_ip) +{ + JitBasicBlock *cur_basic_block; + JitReg value; + JitInsn *insn; + uint32 i = 0; + JitOpndLookupSwitch *opnd = NULL; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + return false; +#endif + + cur_basic_block = cc->cur_basic_block; + + POP_I32(value); + + /* append LOOKUPSWITCH to current basic block */ + gen_commit_values(cc->jit_frame, cc->jit_frame->lp, cc->jit_frame->sp); + /* Clear frame values */ + clear_values(cc->jit_frame); + SET_BB_END_BCIP(cur_basic_block, *p_frame_ip - 1); + + /* prepare basic blocks for br */ + insn = GEN_INSN(LOOKUPSWITCH, value, br_count); + if (NULL == insn) { + jit_set_last_error(cc, "generate insn LOOKUPSWITCH failed"); + goto fail; + } + + for (i = 0, opnd = jit_insn_opndls(insn); i < br_count + 1; i++) { + JitBasicBlock *basic_block = NULL; + JitBlock *block_dst; + bool copy_arities; + + if (!(block_dst = get_target_block(cc, br_depths[i]))) { + goto fail; + } + + /* Only opy parameters or results when their count > 0 and + the src/dst addr are different */ + copy_arities = check_copy_arities(block_dst, cc->jit_frame); + + if (!copy_arities) { + /* No need to create new basic block, directly jump to + the existing basic block when no need to copy arities */ + if (i == br_count) { + if (block_dst->label_type == LABEL_TYPE_LOOP) { + opnd->default_target = + jit_basic_block_label(block_dst->basic_block_entry); + } + else { + bh_assert(!block_dst->basic_block_end); + if (!jit_block_add_incoming_insn(block_dst, insn, i)) { + jit_set_last_error(cc, "add incoming insn failed"); + goto fail; + } + } + } + else { + opnd->match_pairs[i].value = i; + if (block_dst->label_type == LABEL_TYPE_LOOP) { + opnd->match_pairs[i].target = + jit_basic_block_label(block_dst->basic_block_entry); + } + else { + bh_assert(!block_dst->basic_block_end); + if (!jit_block_add_incoming_insn(block_dst, insn, i)) { + jit_set_last_error(cc, "add incoming insn failed"); + goto fail; + } + } + } + continue; + } + + /* Create new basic block when need to copy arities */ + CREATE_BASIC_BLOCK(basic_block); + SET_BB_BEGIN_BCIP(basic_block, *p_frame_ip - 1); + + if (i == br_count) { + opnd->default_target = jit_basic_block_label(basic_block); + } + else { + opnd->match_pairs[i].value = i; + opnd->match_pairs[i].target = jit_basic_block_label(basic_block); + } + + SET_BUILDER_POS(basic_block); + + if (!handle_op_br(cc, br_depths[i], p_frame_ip)) + goto fail; + } + + /* Search next available block to handle */ + return handle_next_reachable_block(cc, p_frame_ip); +fail: + return false; +} + +bool +jit_compile_op_return(JitCompContext *cc, uint8 **p_frame_ip) +{ + JitBlock *block_func = cc->block_stack.block_list_head; + + bh_assert(block_func); + + if (!handle_func_return(cc, block_func)) { + return false; + } + SET_BB_END_BCIP(cc->cur_basic_block, *p_frame_ip - 1); + clear_values(cc->jit_frame); + + return handle_next_reachable_block(cc, p_frame_ip); +} + +bool +jit_compile_op_unreachable(JitCompContext *cc, uint8 **p_frame_ip) +{ + if (!jit_emit_exception(cc, EXCE_UNREACHABLE, JIT_OP_JMP, 0, NULL)) + return false; + + return handle_next_reachable_block(cc, p_frame_ip); +} + +bool +jit_handle_next_reachable_block(JitCompContext *cc, uint8 **p_frame_ip) +{ + return handle_next_reachable_block(cc, p_frame_ip); +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_control.h b/core/iwasm/fast-jit/fe/jit_emit_control.h new file mode 100644 index 0000000000..e1bc09a0a2 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_control.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_CONTROL_H_ +#define _JIT_EMIT_CONTROL_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_block(JitCompContext *cc, uint8 **p_frame_ip, + uint8 *frame_ip_end, uint32 label_type, uint32 param_count, + uint8 *param_types, uint32 result_count, + uint8 *result_types, bool merge_cmp_and_if); + +bool +jit_compile_op_else(JitCompContext *cc, uint8 **p_frame_ip); + +bool +jit_compile_op_end(JitCompContext *cc, uint8 **p_frame_ip); + +bool +jit_compile_op_br(JitCompContext *cc, uint32 br_depth, uint8 **p_frame_ip); + +bool +jit_compile_op_br_if(JitCompContext *cc, uint32 br_depth, + bool merge_cmp_and_br_if, uint8 **p_frame_ip); + +bool +jit_compile_op_br_table(JitCompContext *cc, uint32 *br_depths, uint32 br_count, + uint8 **p_frame_ip); + +bool +jit_compile_op_return(JitCompContext *cc, uint8 **p_frame_ip); + +bool +jit_compile_op_unreachable(JitCompContext *cc, uint8 **p_frame_ip); + +bool +jit_handle_next_reachable_block(JitCompContext *cc, uint8 **p_frame_ip); + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +jit_check_suspend_flags(JitCompContext *cc); +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_CONTROL_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_conversion.c b/core/iwasm/fast-jit/fe/jit_emit_conversion.c new file mode 100644 index 0000000000..89d84f142e --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_conversion.c @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_conversion.h" +#include "jit_emit_exception.h" +#include "jit_emit_function.h" +#include "../jit_codegen.h" +#include "../jit_frontend.h" + +#define F32_I32_S_MIN (-2147483904.0f) +#define F32_I32_S_MAX (2147483648.0f) +#define F32_I32_U_MIN (-1.0f) +#define F32_I32_U_MAX (4294967296.0f) +#define F32_I64_S_MIN (-9223373136366403584.0f) +#define F32_I64_S_MAX (9223372036854775808.0f) +#define F32_I64_U_MIN (-1.0f) +#define F32_I64_U_MAX (18446744073709551616.0f) + +#define F64_I32_S_MIN (-2147483649.0) +#define F64_I32_S_MAX (2147483648.0) +#define F64_I32_U_MIN (-1.0) +#define F64_I32_U_MAX (4294967296.0) +#define F64_I64_S_MIN (-9223372036854777856.0) +#define F64_I64_S_MAX (9223372036854775808.0) +#define F64_I64_U_MIN (-1.0) +#define F64_I64_U_MAX (18446744073709551616.0) + +#define FP_TO_INT(f_ty, i_ty, f_nm, i_nm) \ + static i_ty i_nm##_trunc_##f_nm(f_ty fp) + +#define INT_TO_FP(i_ty, f_ty, i_nm, f_nm) \ + static f_ty f_nm##_convert_##i_nm(i_ty i) + +#define FP_TO_INT_SAT(f_ty, i_ty, f_nm, i_nm) \ + static i_ty i_nm##_trunc_##f_nm##_sat(f_ty fp) + +static int +local_isnan(double x) +{ + return isnan(x); +} + +static int +local_isnanf(float x) +{ + return isnan(x); +} + +#define RETURN_IF_NANF(fp) \ + if (local_isnanf(fp)) { \ + return 0; \ + } + +#define RETURN_IF_NAN(fp) \ + if (local_isnan(fp)) { \ + return 0; \ + } + +#define RETURN_IF_INF(fp, i_min, i_max) \ + if (isinf(fp)) { \ + return fp < 0 ? i_min : i_max; \ + } + +#define RETURN_IF_MIN(fp, f_min, i_min) \ + if (fp <= f_min) { \ + return i_min; \ + } + +#define RETURN_IF_MAX(fp, f_max, i_max) \ + if (fp >= f_max) { \ + return i_max; \ + } + +FP_TO_INT_SAT(float, int32, f32, i32) +{ + RETURN_IF_NANF(fp) + RETURN_IF_INF(fp, INT32_MIN, INT32_MAX) + RETURN_IF_MIN(fp, F32_I32_S_MIN, INT32_MIN) + RETURN_IF_MAX(fp, F32_I32_S_MAX, INT32_MAX) + return (int32)fp; +} + +FP_TO_INT_SAT(float, uint32, f32, u32) +{ + RETURN_IF_NANF(fp) + RETURN_IF_INF(fp, 0, UINT32_MAX) + RETURN_IF_MIN(fp, F32_I32_U_MIN, 0) + RETURN_IF_MAX(fp, F32_I32_U_MAX, UINT32_MAX) + return (uint32)fp; +} + +FP_TO_INT_SAT(double, int32, f64, i32) +{ + RETURN_IF_NAN(fp) + RETURN_IF_INF(fp, INT32_MIN, INT32_MAX) + RETURN_IF_MIN(fp, F64_I32_S_MIN, INT32_MIN) + RETURN_IF_MAX(fp, F64_I32_S_MAX, INT32_MAX) + return (int32)fp; +} + +FP_TO_INT_SAT(double, uint32, f64, u32) +{ + RETURN_IF_NAN(fp) + RETURN_IF_INF(fp, 0, UINT32_MAX) + RETURN_IF_MIN(fp, F64_I32_U_MIN, 0) + RETURN_IF_MAX(fp, F64_I32_U_MAX, UINT32_MAX) + return (uint32)fp; +} + +FP_TO_INT_SAT(float, int64, f32, i64) +{ + RETURN_IF_NANF(fp) + RETURN_IF_INF(fp, INT64_MIN, INT64_MAX) + RETURN_IF_MIN(fp, F32_I64_S_MIN, INT64_MIN) + RETURN_IF_MAX(fp, F32_I64_S_MAX, INT64_MAX) + return (int64)fp; +} + +FP_TO_INT(float, uint64, f32, u64) +{ + return (uint64)fp; +} + +FP_TO_INT_SAT(float, uint64, f32, u64) +{ + RETURN_IF_NANF(fp) + RETURN_IF_INF(fp, 0, UINT64_MAX) + RETURN_IF_MIN(fp, F32_I64_U_MIN, 0) + RETURN_IF_MAX(fp, F32_I64_U_MAX, UINT64_MAX) + return (uint64)fp; +} + +FP_TO_INT_SAT(double, int64, f64, i64) +{ + RETURN_IF_NANF(fp) + RETURN_IF_INF(fp, INT64_MIN, INT64_MAX) + RETURN_IF_MIN(fp, F64_I64_S_MIN, INT64_MIN) + RETURN_IF_MAX(fp, F64_I64_S_MAX, INT64_MAX) + return (int64)fp; +} + +FP_TO_INT(double, uint64, f64, u64) +{ + return (uint64)fp; +} + +FP_TO_INT_SAT(double, uint64, f64, u64) +{ + RETURN_IF_NANF(fp) + RETURN_IF_INF(fp, 0, UINT64_MAX) + RETURN_IF_MIN(fp, F64_I64_U_MIN, 0) + RETURN_IF_MAX(fp, F64_I64_U_MAX, UINT64_MAX) + return (uint64)fp; +} + +INT_TO_FP(uint64, float, u64, f32) +{ + return (float)i; +} + +INT_TO_FP(uint64, double, u64, f64) +{ + return (double)i; +} + +bool +jit_compile_op_i32_wrap_i64(JitCompContext *cc) +{ + JitReg num, res; + + POP_I64(num); + + res = jit_cc_new_reg_I32(cc); + GEN_INSN(I64TOI32, res, num); + + PUSH_I32(res); + + return true; +fail: + return false; +} + +static bool +jit_compile_check_value_range(JitCompContext *cc, JitReg value, JitReg min_fp, + JitReg max_fp) +{ + JitReg nan_ret = jit_cc_new_reg_I32(cc); + JitRegKind kind = jit_reg_kind(value); + bool emit_ret = false; + + bh_assert(JIT_REG_KIND_F32 == kind || JIT_REG_KIND_F64 == kind); + + if (JIT_REG_KIND_F32 == kind && jit_reg_is_const(value)) { + /* value is an f32 const */ + float value_f32_const = jit_cc_get_const_F32(cc, value); + float min_fp_f32_const = jit_cc_get_const_F32(cc, min_fp); + float max_fp_f32_const = jit_cc_get_const_F32(cc, max_fp); + + if (isnan(value_f32_const)) { + /* throw exception if value is nan */ + if (!jit_emit_exception(cc, EXCE_INVALID_CONVERSION_TO_INTEGER, + JIT_OP_JMP, 0, NULL)) + goto fail; + } + + if (value_f32_const <= min_fp_f32_const + || value_f32_const >= max_fp_f32_const) { + /* throw exception if value is out of range */ + if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_JMP, 0, + NULL)) + goto fail; + } + + /* value is in range, do nothing */ + return true; + } + else if (JIT_REG_KIND_F64 == kind && jit_reg_is_const(value)) { + /* value is an f64 const */ + double value_f64_const = jit_cc_get_const_F64(cc, value); + double min_fp_f64_const = jit_cc_get_const_F64(cc, min_fp); + double max_fp_f64_const = jit_cc_get_const_F64(cc, max_fp); + + if (isnan(value_f64_const)) { + /* throw exception if value is nan */ + if (!jit_emit_exception(cc, EXCE_INVALID_CONVERSION_TO_INTEGER, + JIT_OP_JMP, 0, NULL)) + goto fail; + } + + if (value_f64_const <= min_fp_f64_const + || value_f64_const >= max_fp_f64_const) { + /* throw exception if value is out of range */ + if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_JMP, 0, + NULL)) + goto fail; + } + + /* value is in range, do nothing */ + return true; + } + + /* If value is NaN, throw exception */ + if (JIT_REG_KIND_F32 == kind) + emit_ret = jit_emit_callnative(cc, local_isnanf, nan_ret, &value, 1); + else + emit_ret = jit_emit_callnative(cc, local_isnan, nan_ret, &value, 1); + if (!emit_ret) + goto fail; + + GEN_INSN(CMP, cc->cmp_reg, nan_ret, NEW_CONST(I32, 1)); + if (!jit_emit_exception(cc, EXCE_INVALID_CONVERSION_TO_INTEGER, JIT_OP_BEQ, + cc->cmp_reg, NULL)) + goto fail; + + /* If value is out of integer range, throw exception */ + GEN_INSN(CMP, cc->cmp_reg, min_fp, value); + if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BGES, cc->cmp_reg, + NULL)) + goto fail; + + GEN_INSN(CMP, cc->cmp_reg, value, max_fp); + if (!jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BGES, cc->cmp_reg, + NULL)) + goto fail; + + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_trunc_f32(JitCompContext *cc, bool sign, bool sat) +{ + JitReg value, res; + + POP_F32(value); + + res = jit_cc_new_reg_I32(cc); + if (!sat) { + JitReg min_fp = NEW_CONST(F32, sign ? F32_I32_S_MIN : F32_I32_U_MIN); + JitReg max_fp = NEW_CONST(F32, sign ? F32_I32_S_MAX : F32_I32_U_MAX); + + if (!jit_compile_check_value_range(cc, value, min_fp, max_fp)) + goto fail; + + if (sign) + GEN_INSN(F32TOI32, res, value); + else + GEN_INSN(F32TOU32, res, value); + } + else { + if (!jit_emit_callnative(cc, + sign ? (void *)i32_trunc_f32_sat + : (void *)u32_trunc_f32_sat, + res, &value, 1)) + goto fail; + } + + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_trunc_f64(JitCompContext *cc, bool sign, bool sat) +{ + JitReg value, res; + + POP_F64(value); + + res = jit_cc_new_reg_I32(cc); + if (!sat) { + JitReg min_fp = NEW_CONST(F64, sign ? F64_I32_S_MIN : F64_I32_U_MIN); + JitReg max_fp = NEW_CONST(F64, sign ? F64_I32_S_MAX : F64_I32_U_MAX); + + if (!jit_compile_check_value_range(cc, value, min_fp, max_fp)) + goto fail; + + if (sign) + GEN_INSN(F64TOI32, res, value); + else + GEN_INSN(F64TOU32, res, value); + } + else { + if (!jit_emit_callnative(cc, + sign ? (void *)i32_trunc_f64_sat + : (void *)u32_trunc_f64_sat, + res, &value, 1)) + goto fail; + } + + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_extend_i32(JitCompContext *cc, bool sign) +{ + JitReg num, res; + + POP_I32(num); + + res = jit_cc_new_reg_I64(cc); + if (sign) + GEN_INSN(I32TOI64, res, num); + else + GEN_INSN(U32TOI64, res, num); + + PUSH_I64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_extend_i64(JitCompContext *cc, int8 bitwidth) +{ + JitReg value, tmp, res; + + POP_I64(value); + + tmp = jit_cc_new_reg_I32(cc); + res = jit_cc_new_reg_I64(cc); + + switch (bitwidth) { + case 8: + { + GEN_INSN(I64TOI8, tmp, value); + GEN_INSN(I8TOI64, res, tmp); + break; + } + case 16: + { + GEN_INSN(I64TOI16, tmp, value); + GEN_INSN(I16TOI64, res, tmp); + break; + } + case 32: + { + GEN_INSN(I64TOI32, tmp, value); + GEN_INSN(I32TOI64, res, tmp); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + PUSH_I64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_extend_i32(JitCompContext *cc, int8 bitwidth) +{ + JitReg value, tmp, res; + + POP_I32(value); + + tmp = jit_cc_new_reg_I32(cc); + res = jit_cc_new_reg_I32(cc); + + switch (bitwidth) { + case 8: + { + GEN_INSN(I32TOI8, tmp, value); + GEN_INSN(I8TOI32, res, tmp); + break; + } + case 16: + { + GEN_INSN(I32TOI16, tmp, value); + GEN_INSN(I16TOI32, res, tmp); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_trunc_f32(JitCompContext *cc, bool sign, bool sat) +{ + JitReg value, res; + + POP_F32(value); + + res = jit_cc_new_reg_I64(cc); + if (!sat) { + JitReg min_fp = NEW_CONST(F32, sign ? F32_I64_S_MIN : F32_I64_U_MIN); + JitReg max_fp = NEW_CONST(F32, sign ? F32_I64_S_MAX : F32_I64_U_MAX); + + if (!jit_compile_check_value_range(cc, value, min_fp, max_fp)) + goto fail; + + if (sign) { + GEN_INSN(F32TOI64, res, value); + } + else { + if (!jit_emit_callnative(cc, u64_trunc_f32, res, &value, 1)) + goto fail; + } + } + else { + if (!jit_emit_callnative(cc, + sign ? (void *)i64_trunc_f32_sat + : (void *)u64_trunc_f32_sat, + res, &value, 1)) + goto fail; + } + + PUSH_I64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_trunc_f64(JitCompContext *cc, bool sign, bool sat) +{ + JitReg value, res; + + POP_F64(value); + + res = jit_cc_new_reg_I64(cc); + if (!sat) { + JitReg min_fp = NEW_CONST(F64, sign ? F64_I64_S_MIN : F64_I64_U_MIN); + JitReg max_fp = NEW_CONST(F64, sign ? F64_I64_S_MAX : F64_I64_U_MAX); + + if (!jit_compile_check_value_range(cc, value, min_fp, max_fp)) + goto fail; + + if (sign) { + GEN_INSN(F64TOI64, res, value); + } + else { + if (!jit_emit_callnative(cc, u64_trunc_f64, res, &value, 1)) + goto fail; + } + } + else { + if (!jit_emit_callnative(cc, + sign ? (void *)i64_trunc_f64_sat + : (void *)u64_trunc_f64_sat, + res, &value, 1)) + goto fail; + } + + PUSH_I64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_convert_i32(JitCompContext *cc, bool sign) +{ + JitReg value, res; + + POP_I32(value); + + res = jit_cc_new_reg_F32(cc); + if (sign) { + GEN_INSN(I32TOF32, res, value); + } + else { + GEN_INSN(U32TOF32, res, value); + } + + PUSH_F32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_convert_i64(JitCompContext *cc, bool sign) +{ + JitReg value, res; + + POP_I64(value); + + res = jit_cc_new_reg_F32(cc); + if (sign) { + GEN_INSN(I64TOF32, res, value); + } + else { + if (!jit_emit_callnative(cc, f32_convert_u64, res, &value, 1)) { + goto fail; + } + } + + PUSH_F32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_demote_f64(JitCompContext *cc) +{ + JitReg value, res; + + POP_F64(value); + + res = jit_cc_new_reg_F32(cc); + GEN_INSN(F64TOF32, res, value); + + PUSH_F32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_convert_i32(JitCompContext *cc, bool sign) +{ + JitReg value, res; + + POP_I32(value); + + res = jit_cc_new_reg_F64(cc); + if (sign) + GEN_INSN(I32TOF64, res, value); + else + GEN_INSN(U32TOF64, res, value); + + PUSH_F64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_convert_i64(JitCompContext *cc, bool sign) +{ + JitReg value, res; + + POP_I64(value); + + res = jit_cc_new_reg_F64(cc); + if (sign) { + GEN_INSN(I64TOF64, res, value); + } + else { + if (!jit_emit_callnative(cc, f64_convert_u64, res, &value, 1)) { + goto fail; + } + } + + PUSH_F64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_promote_f32(JitCompContext *cc) +{ + JitReg value, res; + + POP_F32(value); + + res = jit_cc_new_reg_F64(cc); + GEN_INSN(F32TOF64, res, value); + + PUSH_F64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_reinterpret_f64(JitCompContext *cc) +{ + JitReg value, res; + + POP_F64(value); + + res = jit_cc_new_reg_I64(cc); + GEN_INSN(F64CASTI64, res, value); + + PUSH_I64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_reinterpret_f32(JitCompContext *cc) +{ + JitReg value, res; + + POP_F32(value); + + res = jit_cc_new_reg_I32(cc); + GEN_INSN(F32CASTI32, res, value); + + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_reinterpret_i64(JitCompContext *cc) +{ + JitReg value, res; + + POP_I64(value); + + res = jit_cc_new_reg_F64(cc); + GEN_INSN(I64CASTF64, res, value); + + PUSH_F64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_reinterpret_i32(JitCompContext *cc) +{ + JitReg value, res; + + POP_I32(value); + + res = jit_cc_new_reg_F32(cc); + GEN_INSN(I32CASTF32, res, value); + + PUSH_F32(res); + + return true; +fail: + return false; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_conversion.h b/core/iwasm/fast-jit/fe/jit_emit_conversion.h new file mode 100644 index 0000000000..28952fc613 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_conversion.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_CONVERSION_H_ +#define _JIT_EMIT_CONVERSION_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_i32_wrap_i64(JitCompContext *cc); + +bool +jit_compile_op_i32_trunc_f32(JitCompContext *cc, bool sign, bool sat); + +bool +jit_compile_op_i32_trunc_f64(JitCompContext *cc, bool sign, bool sat); + +bool +jit_compile_op_i64_extend_i32(JitCompContext *comp_ctx, bool sign); + +bool +jit_compile_op_i64_extend_i64(JitCompContext *comp_ctx, int8 bitwidth); + +bool +jit_compile_op_i32_extend_i32(JitCompContext *comp_ctx, int8 bitwidth); + +bool +jit_compile_op_i64_trunc_f32(JitCompContext *cc, bool sign, bool sat); + +bool +jit_compile_op_i64_trunc_f64(JitCompContext *cc, bool sign, bool sat); + +bool +jit_compile_op_f32_convert_i32(JitCompContext *comp_ctx, bool sign); + +bool +jit_compile_op_f32_convert_i64(JitCompContext *comp_ctx, bool sign); + +bool +jit_compile_op_f32_demote_f64(JitCompContext *comp_ctx); + +bool +jit_compile_op_f64_convert_i32(JitCompContext *comp_ctx, bool sign); + +bool +jit_compile_op_f64_convert_i64(JitCompContext *comp_ctx, bool sign); + +bool +jit_compile_op_f64_promote_f32(JitCompContext *comp_ctx); + +bool +jit_compile_op_i64_reinterpret_f64(JitCompContext *comp_ctx); + +bool +jit_compile_op_i32_reinterpret_f32(JitCompContext *comp_ctx); + +bool +jit_compile_op_f64_reinterpret_i64(JitCompContext *comp_ctx); + +bool +jit_compile_op_f32_reinterpret_i32(JitCompContext *comp_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_CONVERSION_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_exception.c b/core/iwasm/fast-jit/fe/jit_emit_exception.c new file mode 100644 index 0000000000..2addb5cde2 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_exception.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_exception.h" +#include "../jit_frontend.h" + +bool +jit_emit_exception(JitCompContext *cc, int32 exception_id, uint8 jit_opcode, + JitReg cond_br_if, JitBasicBlock *cond_br_else_block) +{ + JitInsn *insn = NULL; + JitIncomingInsn *incoming_insn; + JitReg else_label; + + bh_assert(exception_id < EXCE_NUM); + + if (jit_opcode >= JIT_OP_BEQ && jit_opcode <= JIT_OP_BLEU) { + bh_assert(cond_br_if == cc->cmp_reg); + else_label = + cond_br_else_block ? jit_basic_block_label(cond_br_else_block) : 0; + switch (jit_opcode) { + case JIT_OP_BEQ: + insn = GEN_INSN(BEQ, cond_br_if, 0, else_label); + break; + case JIT_OP_BNE: + insn = GEN_INSN(BNE, cond_br_if, 0, else_label); + break; + case JIT_OP_BGTS: + insn = GEN_INSN(BGTS, cond_br_if, 0, else_label); + break; + case JIT_OP_BGES: + insn = GEN_INSN(BGES, cond_br_if, 0, else_label); + break; + case JIT_OP_BLTS: + insn = GEN_INSN(BLTS, cond_br_if, 0, else_label); + break; + case JIT_OP_BLES: + insn = GEN_INSN(BLES, cond_br_if, 0, else_label); + break; + case JIT_OP_BGTU: + insn = GEN_INSN(BGTU, cond_br_if, 0, else_label); + break; + case JIT_OP_BGEU: + insn = GEN_INSN(BGEU, cond_br_if, 0, else_label); + break; + case JIT_OP_BLTU: + insn = GEN_INSN(BLTU, cond_br_if, 0, else_label); + break; + case JIT_OP_BLEU: + insn = GEN_INSN(BLEU, cond_br_if, 0, else_label); + break; + } + if (!insn) { + jit_set_last_error(cc, "generate cond br insn failed"); + return false; + } + } + else if (jit_opcode == JIT_OP_JMP) { + insn = GEN_INSN(JMP, 0); + if (!insn) { + jit_set_last_error(cc, "generate jmp insn failed"); + return false; + } + } + + incoming_insn = jit_calloc(sizeof(JitIncomingInsn)); + if (!incoming_insn) { + jit_set_last_error(cc, "allocate memory failed"); + return false; + } + + incoming_insn->insn = insn; + incoming_insn->next = cc->incoming_insns_for_exec_bbs[exception_id]; + cc->incoming_insns_for_exec_bbs[exception_id] = incoming_insn; + return true; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_exception.h b/core/iwasm/fast-jit/fe/jit_emit_exception.h new file mode 100644 index 0000000000..7aa393b786 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_exception.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_EXCEPTION_H_ +#define _JIT_EMIT_EXCEPTION_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_emit_exception(JitCompContext *cc, int32 exception_id, uint8 jit_opcode, + JitReg cond_br_if, JitBasicBlock *cond_br_else_block); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_EXCEPTION_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_function.c b/core/iwasm/fast-jit/fe/jit_emit_function.c new file mode 100644 index 0000000000..1e41994407 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_function.c @@ -0,0 +1,984 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_function.h" +#include "jit_emit_exception.h" +#include "jit_emit_control.h" +#include "../jit_frontend.h" +#include "../jit_codegen.h" +#include "../../interpreter/wasm_runtime.h" + +static bool +emit_callnative(JitCompContext *cc, JitReg native_func_reg, JitReg res, + JitReg *params, uint32 param_count); + +/* Prepare parameters for the function to call */ +static bool +pre_call(JitCompContext *cc, const WASMType *func_type) +{ + JitReg value; + uint32 i, outs_off; + /* Prepare parameters for the function to call */ + outs_off = + cc->total_frame_size + offsetof(WASMInterpFrame, lp) + + wasm_get_cell_num(func_type->types, func_type->param_count) * 4; + + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[func_type->param_count - 1 - i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + POP_I32(value); + outs_off -= 4; + GEN_INSN(STI32, value, cc->fp_reg, NEW_CONST(I32, outs_off)); + break; + case VALUE_TYPE_I64: + POP_I64(value); + outs_off -= 8; + GEN_INSN(STI64, value, cc->fp_reg, NEW_CONST(I32, outs_off)); + break; + case VALUE_TYPE_F32: + POP_F32(value); + outs_off -= 4; + GEN_INSN(STF32, value, cc->fp_reg, NEW_CONST(I32, outs_off)); + break; + case VALUE_TYPE_F64: + POP_F64(value); + outs_off -= 8; + GEN_INSN(STF64, value, cc->fp_reg, NEW_CONST(I32, outs_off)); + break; + default: + bh_assert(0); + goto fail; + } + } + + /* Commit sp as the callee may use it to store the results */ + gen_commit_sp_ip(cc->jit_frame); + + return true; +fail: + return false; +} + +/* Push results */ +static bool +post_return(JitCompContext *cc, const WASMType *func_type, JitReg first_res, + bool update_committed_sp) +{ + uint32 i, n; + JitReg value; + + n = cc->jit_frame->sp - cc->jit_frame->lp; + for (i = 0; i < func_type->result_count; i++) { + switch (func_type->types[func_type->param_count + i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + if (i == 0 && first_res) { + bh_assert(jit_reg_kind(first_res) == JIT_REG_KIND_I32); + value = first_res; + } + else { + value = jit_cc_new_reg_I32(cc); + GEN_INSN(LDI32, value, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + PUSH_I32(value); + n++; + break; + case VALUE_TYPE_I64: + if (i == 0 && first_res) { + bh_assert(jit_reg_kind(first_res) == JIT_REG_KIND_I64); + value = first_res; + } + else { + value = jit_cc_new_reg_I64(cc); + GEN_INSN(LDI64, value, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + PUSH_I64(value); + n += 2; + break; + case VALUE_TYPE_F32: + if (i == 0 && first_res) { + bh_assert(jit_reg_kind(first_res) == JIT_REG_KIND_F32); + value = first_res; + } + else { + value = jit_cc_new_reg_F32(cc); + GEN_INSN(LDF32, value, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + PUSH_F32(value); + n++; + break; + case VALUE_TYPE_F64: + if (i == 0 && first_res) { + bh_assert(jit_reg_kind(first_res) == JIT_REG_KIND_F64); + value = first_res; + } + else { + value = jit_cc_new_reg_F64(cc); + GEN_INSN(LDF64, value, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + PUSH_F64(value); + n += 2; + break; + default: + bh_assert(0); + goto fail; + } + } + + if (update_committed_sp) + /* Update the committed_sp as the callee has updated the frame sp */ + cc->jit_frame->committed_sp = cc->jit_frame->sp; + + return true; +fail: + return false; +} + +static bool +pre_load(JitCompContext *cc, JitReg *argvs, const WASMType *func_type) +{ + JitReg value; + uint32 i; + + /* Prepare parameters for the function to call */ + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[func_type->param_count - 1 - i]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + POP_I32(value); + argvs[func_type->param_count - 1 - i] = value; + break; + case VALUE_TYPE_I64: + POP_I64(value); + argvs[func_type->param_count - 1 - i] = value; + break; + case VALUE_TYPE_F32: + POP_F32(value); + argvs[func_type->param_count - 1 - i] = value; + break; + case VALUE_TYPE_F64: + POP_F64(value); + argvs[func_type->param_count - 1 - i] = value; + break; + default: + bh_assert(0); + goto fail; + } + } + + gen_commit_sp_ip(cc->jit_frame); + + return true; +fail: + return false; +} + +static JitReg +create_first_res_reg(JitCompContext *cc, const WASMType *func_type) +{ + if (func_type->result_count) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + return jit_cc_new_reg_I32(cc); + case VALUE_TYPE_I64: + return jit_cc_new_reg_I64(cc); + case VALUE_TYPE_F32: + return jit_cc_new_reg_F32(cc); + case VALUE_TYPE_F64: + return jit_cc_new_reg_F64(cc); + default: + bh_assert(0); + return 0; + } + } + return 0; +} + +bool +jit_compile_op_call(JitCompContext *cc, uint32 func_idx, bool tail_call) +{ + WASMModule *wasm_module = cc->cur_wasm_module; + WASMFunctionImport *func_import; + WASMFunction *func; + WASMType *func_type; + JitFrame *jit_frame = cc->jit_frame; + JitReg fast_jit_func_ptrs, jitted_code = 0; + JitReg native_func, *argvs = NULL, *argvs1 = NULL, func_params[5]; + JitReg native_addr_ptr, module_inst_reg, ret, res; + uint32 jitted_func_idx, i; + uint64 total_size; + const char *signature = NULL; + /* Whether the argument is a pointer/str argument and + need to call jit_check_app_addr_and_convert */ + bool is_pointer_arg; + bool return_value = false; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + goto fail; +#endif + + if (func_idx < wasm_module->import_function_count) { + /* The function to call is an import function */ + func_import = &wasm_module->import_functions[func_idx].u.function; + func_type = func_import->func_type; + + /* Call fast_jit_invoke_native in some cases */ + if (!func_import->func_ptr_linked /* import func hasn't been linked */ + || func_import->call_conv_wasm_c_api /* linked by wasm_c_api */ + || func_import->call_conv_raw /* registered as raw mode */ + || func_type->param_count >= 5 /* registered as normal mode, but + jit_emit_callnative only supports + maximum 6 registers now + (include exec_nev) */) { + JitReg arg_regs[3]; + + if (!pre_call(cc, func_type)) { + goto fail; + } + + /* Call fast_jit_invoke_native */ + ret = jit_cc_new_reg_I32(cc); + arg_regs[0] = cc->exec_env_reg; + arg_regs[1] = NEW_CONST(I32, func_idx); + arg_regs[2] = cc->fp_reg; + if (!jit_emit_callnative(cc, fast_jit_invoke_native, ret, arg_regs, + 3)) { + goto fail; + } + + /* Convert the return value from bool to uint32 */ + GEN_INSN(AND, ret, ret, NEW_CONST(I32, 0xFF)); + + /* Check whether there is exception thrown */ + GEN_INSN(CMP, cc->cmp_reg, ret, NEW_CONST(I32, 0)); + if (!jit_emit_exception(cc, EXCE_ALREADY_THROWN, JIT_OP_BEQ, + cc->cmp_reg, NULL)) { + goto fail; + } + + if (!post_return(cc, func_type, 0, true)) { + goto fail; + } + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + goto fail; +#endif + + return true; + } + + /* Import function was registered as normal mode, and its argument count + is no more than 5, we directly call it */ + + signature = func_import->signature; + bh_assert(signature); + + /* Allocate memory for argvs*/ + total_size = sizeof(JitReg) * (uint64)(func_type->param_count); + if (total_size > 0) { + if (total_size >= UINT32_MAX + || !(argvs = jit_malloc((uint32)total_size))) { + goto fail; + } + } + + /* Pop function params from stack and store them into argvs */ + if (!pre_load(cc, argvs, func_type)) { + goto fail; + } + + ret = jit_cc_new_reg_I32(cc); + func_params[0] = module_inst_reg = get_module_inst_reg(jit_frame); + func_params[4] = native_addr_ptr = jit_cc_new_reg_ptr(cc); + GEN_INSN(ADD, native_addr_ptr, cc->exec_env_reg, + NEW_CONST(PTR, offsetof(WASMExecEnv, jit_cache))); + + /* Traverse each pointer/str argument, call + jit_check_app_addr_and_convert to check whether it is + in the range of linear memory and and convert it from + app offset into native address */ + for (i = 0; i < func_type->param_count; i++) { + + is_pointer_arg = false; + + if (signature[i + 1] == '*') { + /* param is a pointer */ + is_pointer_arg = true; + func_params[1] = NEW_CONST(I32, false); /* is_str = false */ + func_params[2] = argvs[i]; + if (signature[i + 2] == '~') { + /* TODO: Memory64 no need to convert if mem idx type i64 */ + func_params[3] = jit_cc_new_reg_I64(cc); + /* pointer with length followed */ + GEN_INSN(I32TOI64, func_params[3], argvs[i + 1]); + } + else { + /* pointer with length followed */ + func_params[3] = NEW_CONST(I64, 1); + } + } + else if (signature[i + 1] == '$') { + /* param is a string */ + is_pointer_arg = true; + func_params[1] = NEW_CONST(I32, true); /* is_str = true */ + func_params[2] = argvs[i]; + func_params[3] = NEW_CONST(I64, 1); + } + + if (is_pointer_arg) { + JitReg native_addr_64 = jit_cc_new_reg_I64(cc); + /* TODO: Memory64 no need to convert if mem idx type i64 */ + GEN_INSN(I32TOI64, native_addr_64, func_params[2]); + func_params[2] = native_addr_64; + + if (!jit_emit_callnative(cc, jit_check_app_addr_and_convert, + ret, func_params, 5)) { + goto fail; + } + + /* Convert the return value from bool to uint32 */ + GEN_INSN(AND, ret, ret, NEW_CONST(I32, 0xFF)); + /* Check whether there is exception thrown */ + GEN_INSN(CMP, cc->cmp_reg, ret, NEW_CONST(I32, 0)); + if (!jit_emit_exception(cc, EXCE_ALREADY_THROWN, JIT_OP_BEQ, + cc->cmp_reg, NULL)) { + return false; + } + + /* Load native addr from pointer of native addr, + or exec_env->jit_cache */ + argvs[i] = jit_cc_new_reg_ptr(cc); + GEN_INSN(LDPTR, argvs[i], native_addr_ptr, NEW_CONST(I32, 0)); + } + } + + res = create_first_res_reg(cc, func_type); + + /* Prepare arguments of the native function */ + if (!(argvs1 = + jit_calloc(sizeof(JitReg) * (func_type->param_count + 1)))) { + goto fail; + } + argvs1[0] = cc->exec_env_reg; + for (i = 0; i < func_type->param_count; i++) { + argvs1[i + 1] = argvs[i]; + } + + /* Call the native function */ + native_func = NEW_CONST(PTR, (uintptr_t)func_import->func_ptr_linked); + if (!emit_callnative(cc, native_func, res, argvs1, + func_type->param_count + 1)) { + jit_free(argvs1); + goto fail; + } + jit_free(argvs1); + + /* Check whether there is exception thrown */ + GEN_INSN(LDI8, ret, module_inst_reg, + NEW_CONST(I32, offsetof(WASMModuleInstance, cur_exception))); + GEN_INSN(CMP, cc->cmp_reg, ret, NEW_CONST(I32, 0)); + if (!jit_emit_exception(cc, EXCE_ALREADY_THROWN, JIT_OP_BNE, + cc->cmp_reg, NULL)) { + goto fail; + } + + if (!post_return(cc, func_type, res, false)) { + goto fail; + } + } + else { + /* The function to call is a bytecode function */ + func = wasm_module + ->functions[func_idx - wasm_module->import_function_count]; + func_type = func->func_type; + + /* jitted_code = func_ptrs[func_idx - import_function_count] */ + fast_jit_func_ptrs = get_fast_jit_func_ptrs_reg(jit_frame); + jitted_code = jit_cc_new_reg_ptr(cc); + jitted_func_idx = func_idx - wasm_module->import_function_count; + GEN_INSN(LDPTR, jitted_code, fast_jit_func_ptrs, + NEW_CONST(I32, (uint32)sizeof(void *) * jitted_func_idx)); + + if (!pre_call(cc, func_type)) { + goto fail; + } + + res = create_first_res_reg(cc, func_type); + + GEN_INSN(CALLBC, res, 0, jitted_code, NEW_CONST(I32, func_idx)); + + if (!post_return(cc, func_type, res, true)) { + goto fail; + } + } + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + goto fail; +#endif + + /* Clear part of memory regs and table regs as their values + may be changed in the function call */ + if (cc->cur_wasm_module->possible_memory_grow) + clear_memory_regs(jit_frame); + clear_table_regs(jit_frame); + + /* Ignore tail call currently */ + (void)tail_call; + + return_value = true; + +fail: + if (argvs) + jit_free(argvs); + + return return_value; +} + +static JitReg +pack_argv(JitCompContext *cc) +{ + /* reuse the stack of the next frame */ + uint32 stack_base; + JitReg argv; + + stack_base = cc->total_frame_size + offsetof(WASMInterpFrame, lp); + argv = jit_cc_new_reg_ptr(cc); + GEN_INSN(ADD, argv, cc->fp_reg, NEW_CONST(PTR, stack_base)); + if (jit_get_last_error(cc)) { + return (JitReg)0; + } + return argv; +} + +bool +jit_compile_op_call_indirect(JitCompContext *cc, uint32 type_idx, + uint32 tbl_idx) +{ + WASMModule *wasm_module = cc->cur_wasm_module; + JitBasicBlock *block_import, *block_nonimport, *func_return; + JitReg elem_idx, native_ret, argv, arg_regs[6]; + JitFrame *jit_frame = cc->jit_frame; + JitReg tbl_size, offset, offset_i32; + JitReg func_import, func_idx, tbl_elems, func_count; + JitReg func_type_indexes, func_type_idx, fast_jit_func_ptrs; + JitReg offset1_i32, offset1, func_type_idx1, res; + JitReg import_func_ptrs, jitted_code_idx, jitted_code; + WASMType *func_type; + uint32 n; + + POP_I32(elem_idx); + + /* check elem_idx */ + tbl_size = get_table_cur_size_reg(jit_frame, tbl_idx); + + GEN_INSN(CMP, cc->cmp_reg, elem_idx, tbl_size); + if (!jit_emit_exception(cc, EXCE_UNDEFINED_ELEMENT, JIT_OP_BGEU, + cc->cmp_reg, NULL)) + goto fail; + + /* check func_idx */ + if (UINTPTR_MAX == UINT64_MAX) { + offset_i32 = jit_cc_new_reg_I32(cc); + offset = jit_cc_new_reg_I64(cc); + /* Calculate offset by pointer size (elem_idx * + * sizeof(table_elem_type_t)) */ + GEN_INSN(SHL, offset_i32, elem_idx, NEW_CONST(I32, 3)); + GEN_INSN(I32TOI64, offset, offset_i32); + } + else { + offset = jit_cc_new_reg_I32(cc); + GEN_INSN(SHL, offset, elem_idx, NEW_CONST(I32, 2)); + } + func_idx = jit_cc_new_reg_I32(cc); + tbl_elems = get_table_elems_reg(jit_frame, tbl_idx); + GEN_INSN(LDI32, func_idx, tbl_elems, offset); + + GEN_INSN(CMP, cc->cmp_reg, func_idx, NEW_CONST(I32, -1)); + if (!jit_emit_exception(cc, EXCE_UNINITIALIZED_ELEMENT, JIT_OP_BEQ, + cc->cmp_reg, NULL)) + goto fail; + + func_count = NEW_CONST(I32, wasm_module->import_function_count + + wasm_module->function_count); + GEN_INSN(CMP, cc->cmp_reg, func_idx, func_count); + if (!jit_emit_exception(cc, EXCE_INVALID_FUNCTION_INDEX, JIT_OP_BGTU, + cc->cmp_reg, NULL)) + goto fail; + + /* check func_type */ + /* get func_type_idx from func_type_indexes */ + if (UINTPTR_MAX == UINT64_MAX) { + offset1_i32 = jit_cc_new_reg_I32(cc); + offset1 = jit_cc_new_reg_I64(cc); + GEN_INSN(SHL, offset1_i32, func_idx, NEW_CONST(I32, 2)); + GEN_INSN(I32TOI64, offset1, offset1_i32); + } + else { + offset1 = jit_cc_new_reg_I32(cc); + GEN_INSN(SHL, offset1, func_idx, NEW_CONST(I32, 2)); + } + + func_type_indexes = get_func_type_indexes_reg(jit_frame); + func_type_idx = jit_cc_new_reg_I32(cc); + GEN_INSN(LDI32, func_type_idx, func_type_indexes, offset1); + + type_idx = wasm_get_smallest_type_idx(wasm_module->types, + wasm_module->type_count, type_idx); + func_type_idx1 = NEW_CONST(I32, type_idx); + GEN_INSN(CMP, cc->cmp_reg, func_type_idx, func_type_idx1); + if (!jit_emit_exception(cc, EXCE_INVALID_FUNCTION_TYPE_INDEX, JIT_OP_BNE, + cc->cmp_reg, NULL)) + goto fail; + + /* pop function arguments and store it to out area of callee stack frame */ + func_type = wasm_module->types[type_idx]; + if (!pre_call(cc, func_type)) { + goto fail; + } + + /* store elem_idx and func_idx to exec_env->jit_cache */ + GEN_INSN(STI32, elem_idx, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, jit_cache))); + GEN_INSN(STI32, func_idx, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, jit_cache) + 4)); + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + goto fail; +#endif + + block_import = jit_cc_new_basic_block(cc, 0); + block_nonimport = jit_cc_new_basic_block(cc, 0); + func_return = jit_cc_new_basic_block(cc, 0); + if (!block_import || !block_nonimport || !func_return) { + goto fail; + } + + /* Commit register values to locals and stacks */ + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + /* Clear frame values */ + clear_values(jit_frame); + + /* jump to block_import or block_nonimport */ + GEN_INSN(CMP, cc->cmp_reg, func_idx, + NEW_CONST(I32, cc->cur_wasm_module->import_function_count)); + GEN_INSN(BLTU, cc->cmp_reg, jit_basic_block_label(block_import), + jit_basic_block_label(block_nonimport)); + + /* block_import */ + cc->cur_basic_block = block_import; + + elem_idx = jit_cc_new_reg_I32(cc); + GEN_INSN(LDI32, elem_idx, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, jit_cache))); + GEN_INSN(LDI32, func_idx, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, jit_cache) + 4)); + + argv = pack_argv(cc); + if (!argv) { + goto fail; + } + native_ret = jit_cc_new_reg_I32(cc); + arg_regs[0] = cc->exec_env_reg; + arg_regs[1] = NEW_CONST(I32, tbl_idx); + arg_regs[2] = elem_idx; + arg_regs[3] = NEW_CONST(I32, type_idx); + arg_regs[4] = NEW_CONST(I32, func_type->param_cell_num); + arg_regs[5] = argv; + + import_func_ptrs = get_import_func_ptrs_reg(jit_frame); + func_import = jit_cc_new_reg_ptr(cc); + if (UINTPTR_MAX == UINT64_MAX) { + JitReg func_import_offset = jit_cc_new_reg_I32(cc); + JitReg func_import_offset_i64 = jit_cc_new_reg_I64(cc); + GEN_INSN(SHL, func_import_offset, func_idx, NEW_CONST(I32, 3)); + GEN_INSN(I32TOI64, func_import_offset_i64, func_import_offset); + GEN_INSN(LDPTR, func_import, import_func_ptrs, func_import_offset_i64); + } + else { + JitReg func_import_offset = jit_cc_new_reg_I32(cc); + GEN_INSN(SHL, func_import_offset, func_idx, NEW_CONST(I32, 2)); + GEN_INSN(LDPTR, func_import, import_func_ptrs, func_import_offset); + } + if (!jit_emit_callnative(cc, fast_jit_call_indirect, native_ret, arg_regs, + 6)) { + goto fail; + } + + /* Convert bool to uint32 */ + GEN_INSN(AND, native_ret, native_ret, NEW_CONST(I32, 0xFF)); + + /* Check whether there is exception thrown */ + GEN_INSN(CMP, cc->cmp_reg, native_ret, NEW_CONST(I32, 0)); + if (!jit_emit_exception(cc, EXCE_ALREADY_THROWN, JIT_OP_BEQ, cc->cmp_reg, + NULL)) { + return false; + } + + /* Store res into current frame, so that post_return in + block func_return can get the value */ + n = cc->jit_frame->sp - cc->jit_frame->lp; + if (func_type->result_count > 0) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + res = jit_cc_new_reg_I32(cc); + GEN_INSN(LDI32, res, argv, NEW_CONST(I32, 0)); + GEN_INSN(STI32, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + case VALUE_TYPE_I64: + res = jit_cc_new_reg_I64(cc); + GEN_INSN(LDI64, res, argv, NEW_CONST(I32, 0)); + GEN_INSN(STI64, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + case VALUE_TYPE_F32: + res = jit_cc_new_reg_F32(cc); + GEN_INSN(LDF32, res, argv, NEW_CONST(I32, 0)); + GEN_INSN(STF32, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + case VALUE_TYPE_F64: + res = jit_cc_new_reg_F64(cc); + GEN_INSN(LDF64, res, argv, NEW_CONST(I32, 0)); + GEN_INSN(STF64, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + default: + bh_assert(0); + goto fail; + } + } + + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + clear_values(jit_frame); + GEN_INSN(JMP, jit_basic_block_label(func_return)); + + /* basic_block non_import */ + cc->cur_basic_block = block_nonimport; + + GEN_INSN(LDI32, func_idx, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, jit_cache) + 4)); + + /* get jitted_code */ + fast_jit_func_ptrs = get_fast_jit_func_ptrs_reg(jit_frame); + jitted_code_idx = jit_cc_new_reg_I32(cc); + jitted_code = jit_cc_new_reg_ptr(cc); + GEN_INSN(SUB, jitted_code_idx, func_idx, + NEW_CONST(I32, cc->cur_wasm_module->import_function_count)); + if (UINTPTR_MAX == UINT64_MAX) { + JitReg jitted_code_offset = jit_cc_new_reg_I32(cc); + JitReg jitted_code_offset_64 = jit_cc_new_reg_I64(cc); + GEN_INSN(SHL, jitted_code_offset, jitted_code_idx, NEW_CONST(I32, 3)); + GEN_INSN(I32TOI64, jitted_code_offset_64, jitted_code_offset); + GEN_INSN(LDPTR, jitted_code, fast_jit_func_ptrs, jitted_code_offset_64); + } + else { + JitReg jitted_code_offset = jit_cc_new_reg_I32(cc); + GEN_INSN(SHL, jitted_code_offset, jitted_code_idx, NEW_CONST(I32, 2)); + GEN_INSN(LDPTR, jitted_code, fast_jit_func_ptrs, jitted_code_offset); + } + + res = 0; + if (func_type->result_count > 0) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + res = jit_cc_new_reg_I32(cc); + break; + case VALUE_TYPE_I64: + res = jit_cc_new_reg_I64(cc); + break; + case VALUE_TYPE_F32: + res = jit_cc_new_reg_F32(cc); + break; + case VALUE_TYPE_F64: + res = jit_cc_new_reg_F64(cc); + break; + default: + bh_assert(0); + goto fail; + } + } + GEN_INSN(CALLBC, res, 0, jitted_code, func_idx); + /* Store res into current frame, so that post_return in + block func_return can get the value */ + n = cc->jit_frame->sp - cc->jit_frame->lp; + if (func_type->result_count > 0) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + GEN_INSN(STI32, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + case VALUE_TYPE_I64: + GEN_INSN(STI64, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + case VALUE_TYPE_F32: + GEN_INSN(STF32, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + case VALUE_TYPE_F64: + GEN_INSN(STF64, res, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + default: + bh_assert(0); + goto fail; + } + } + /* commit and clear jit frame, then jump to block func_ret */ + gen_commit_values(jit_frame, jit_frame->lp, jit_frame->sp); + clear_values(jit_frame); + GEN_INSN(JMP, jit_basic_block_label(func_return)); + + /* translate block func_return */ + cc->cur_basic_block = func_return; + if (!post_return(cc, func_type, 0, true)) { + goto fail; + } + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + goto fail; +#endif + + /* Clear part of memory regs and table regs as their values + may be changed in the function call */ + if (cc->cur_wasm_module->possible_memory_grow) + clear_memory_regs(cc->jit_frame); + clear_table_regs(cc->jit_frame); + return true; +fail: + return false; +} + +#if WASM_ENABLE_REF_TYPES != 0 +bool +jit_compile_op_ref_null(JitCompContext *cc, uint32 ref_type) +{ + PUSH_I32(NEW_CONST(I32, NULL_REF)); + (void)ref_type; + return true; +fail: + return false; +} + +bool +jit_compile_op_ref_is_null(JitCompContext *cc) +{ + JitReg ref, res; + + POP_I32(ref); + + GEN_INSN(CMP, cc->cmp_reg, ref, NEW_CONST(I32, NULL_REF)); + res = jit_cc_new_reg_I32(cc); + GEN_INSN(SELECTEQ, res, cc->cmp_reg, NEW_CONST(I32, 1), NEW_CONST(I32, 0)); + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_ref_func(JitCompContext *cc, uint32 func_idx) +{ + PUSH_I32(NEW_CONST(I32, func_idx)); + return true; +fail: + return false; +} +#endif + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +static bool +emit_callnative(JitCompContext *cc, JitReg native_func_reg, JitReg res, + JitReg *params, uint32 param_count) +{ + JitInsn *insn; + char *i32_arg_names[] = { "edi", "esi", "edx", "ecx" }; + char *i64_arg_names[] = { "rdi", "rsi", "rdx", "rcx", "r8", "r9" }; + char *f32_arg_names[] = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5" }; + char *f64_arg_names[] = { "xmm0_f64", "xmm1_f64", "xmm2_f64", + "xmm3_f64", "xmm4_f64", "xmm5_f64" }; + JitReg i32_arg_regs[4], i64_arg_regs[6]; + JitReg f32_arg_regs[6], f64_arg_regs[6], res_reg = 0; + JitReg eax_hreg = jit_codegen_get_hreg_by_name("eax"); + JitReg xmm0_hreg = jit_codegen_get_hreg_by_name("xmm0"); + uint32 i, i64_reg_idx, float_reg_idx, lock_i32_reg_num; + + bh_assert(param_count <= 6); + + for (i = 0; i < 4; i++) { + i32_arg_regs[i] = jit_codegen_get_hreg_by_name(i32_arg_names[i]); + } + + for (i = 0; i < 6; i++) { + i64_arg_regs[i] = jit_codegen_get_hreg_by_name(i64_arg_names[i]); + f32_arg_regs[i] = jit_codegen_get_hreg_by_name(f32_arg_names[i]); + f64_arg_regs[i] = jit_codegen_get_hreg_by_name(f64_arg_names[i]); + } + + lock_i32_reg_num = param_count < 4 ? param_count : 4; + + /* + * Lock i32 registers so that they won't be allocated for the operand + * of below I32TOI64 insn, which may have been overwritten in the + * previous MOV, for example, in the below insns: + * MOV I5, I15 + * I32TOI64 I6, i5 + * CALLNATIVE VOID, native_func, I5, I6 + * i5 is used in the second insn, but it has been overwritten in I5 + * by the first insn + */ + for (i = 0; i < lock_i32_reg_num; i++) { + GEN_INSN(MOV, i32_arg_regs[i], i32_arg_regs[i]); + } + + i64_reg_idx = float_reg_idx = 0; + for (i = 0; i < param_count; i++) { + switch (jit_reg_kind(params[i])) { + case JIT_REG_KIND_I32: + GEN_INSN(I32TOI64, i64_arg_regs[i64_reg_idx++], params[i]); + break; + case JIT_REG_KIND_I64: + GEN_INSN(MOV, i64_arg_regs[i64_reg_idx++], params[i]); + break; + case JIT_REG_KIND_F32: + GEN_INSN(MOV, f32_arg_regs[float_reg_idx++], params[i]); + break; + case JIT_REG_KIND_F64: + GEN_INSN(MOV, f64_arg_regs[float_reg_idx++], params[i]); + break; + default: + bh_assert(0); + return false; + } + } + + /* + * Announce the locked i32 registers are being used, and do necessary + * spill ASAP + */ + for (i = 0; i < lock_i32_reg_num; i++) { + GEN_INSN(MOV, i32_arg_regs[i], i32_arg_regs[i]); + } + + if (res) { + switch (jit_reg_kind(res)) { + case JIT_REG_KIND_I32: + res_reg = eax_hreg; + break; + case JIT_REG_KIND_I64: + res_reg = res; + break; + case JIT_REG_KIND_F32: + res_reg = xmm0_hreg; + break; + case JIT_REG_KIND_F64: + res_reg = res; + break; + default: + bh_assert(0); + return false; + } + } + + insn = GEN_INSN(CALLNATIVE, res_reg, native_func_reg, param_count); + if (!insn) { + return false; + } + + i64_reg_idx = float_reg_idx = 0; + for (i = 0; i < param_count; i++) { + switch (jit_reg_kind(params[i])) { + case JIT_REG_KIND_I32: + case JIT_REG_KIND_I64: + *(jit_insn_opndv(insn, i + 2)) = i64_arg_regs[i64_reg_idx++]; + break; + case JIT_REG_KIND_F32: + *(jit_insn_opndv(insn, i + 2)) = f32_arg_regs[float_reg_idx++]; + break; + case JIT_REG_KIND_F64: + *(jit_insn_opndv(insn, i + 2)) = f64_arg_regs[float_reg_idx++]; + break; + default: + bh_assert(0); + return false; + } + } + + if (res && res != res_reg) { + GEN_INSN(MOV, res, res_reg); + } + + return true; +} +#else +static bool +emit_callnative(JitCompContext *cc, JitRef native_func_reg, JitReg res, + JitReg *params, uint32 param_count) +{ + JitInsn *insn; + uint32 i; + + bh_assert(param_count <= 6); + + insn = GEN_INSN(CALLNATIVE, res, native_func_reg, param_count); + if (!insn) + return false; + + for (i = 0; i < param_count; i++) { + *(jit_insn_opndv(insn, i + 2)) = params[i]; + } + return true; +} +#endif + +bool +jit_emit_callnative(JitCompContext *cc, void *native_func, JitReg res, + JitReg *params, uint32 param_count) +{ + return emit_callnative(cc, NEW_CONST(PTR, (uintptr_t)native_func), res, + params, param_count); +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_function.h b/core/iwasm/fast-jit/fe/jit_emit_function.h new file mode 100644 index 0000000000..7405f774ce --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_function.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_FUNCTION_H_ +#define _JIT_EMIT_FUNCTION_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_call(JitCompContext *cc, uint32 func_idx, bool tail_call); + +bool +jit_compile_op_call_indirect(JitCompContext *cc, uint32 type_idx, + uint32 tbl_idx); + +bool +jit_compile_op_ref_null(JitCompContext *cc, uint32 ref_type); + +bool +jit_compile_op_ref_is_null(JitCompContext *cc); + +bool +jit_compile_op_ref_func(JitCompContext *cc, uint32 func_idx); + +bool +jit_emit_callnative(JitCompContext *cc, void *native_func, JitReg res, + JitReg *params, uint32 param_count); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_FUNCTION_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_memory.c b/core/iwasm/fast-jit/fe/jit_emit_memory.c new file mode 100644 index 0000000000..07269a6551 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_memory.c @@ -0,0 +1,1195 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_memory.h" +#include "jit_emit_exception.h" +#include "jit_emit_function.h" +#include "../jit_frontend.h" +#include "../jit_codegen.h" +#include "../../interpreter/wasm_runtime.h" +#include "jit_emit_control.h" + +#ifndef OS_ENABLE_HW_BOUND_CHECK +static JitReg +get_memory_boundary(JitCompContext *cc, uint32 mem_idx, uint32 bytes) +{ + JitReg memory_boundary; + + switch (bytes) { + case 1: + { + memory_boundary = + get_mem_bound_check_1byte_reg(cc->jit_frame, mem_idx); + break; + } + case 2: + { + memory_boundary = + get_mem_bound_check_2bytes_reg(cc->jit_frame, mem_idx); + break; + } + case 4: + { + memory_boundary = + get_mem_bound_check_4bytes_reg(cc->jit_frame, mem_idx); + break; + } + case 8: + { + memory_boundary = + get_mem_bound_check_8bytes_reg(cc->jit_frame, mem_idx); + break; + } + case 16: + { + memory_boundary = + get_mem_bound_check_16bytes_reg(cc->jit_frame, mem_idx); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + return memory_boundary; +fail: + return 0; +} +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +static void +set_load_or_store_atomic(JitInsn *load_or_store_inst) +{ + load_or_store_inst->flags_u8 |= 0x1; +} +#endif + +#if UINTPTR_MAX == UINT64_MAX +static JitReg +check_and_seek_on_64bit_platform(JitCompContext *cc, JitReg addr, JitReg offset, + JitReg memory_boundary) +{ + JitReg long_addr, offset1; + + /* long_addr = (int64_t)addr */ + long_addr = jit_cc_new_reg_I64(cc); + GEN_INSN(U32TOI64, long_addr, addr); + + /* offset1 = offset + long_addr */ + offset1 = jit_cc_new_reg_I64(cc); + GEN_INSN(ADD, offset1, offset, long_addr); + +#ifndef OS_ENABLE_HW_BOUND_CHECK + /* if (offset1 > memory_boundary) goto EXCEPTION */ + GEN_INSN(CMP, cc->cmp_reg, offset1, memory_boundary); + if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, JIT_OP_BGTU, + cc->cmp_reg, NULL)) { + goto fail; + } +#endif + + return offset1; +#ifndef OS_ENABLE_HW_BOUND_CHECK +fail: + return 0; +#endif +} +#else +static JitReg +check_and_seek_on_32bit_platform(JitCompContext *cc, JitReg addr, JitReg offset, + JitReg memory_boundary) +{ + JitReg offset1; + + /* offset1 = offset + addr */ + offset1 = jit_cc_new_reg_I32(cc); + GEN_INSN(ADD, offset1, offset, addr); + + /* if (offset1 < addr) goto EXCEPTION */ + GEN_INSN(CMP, cc->cmp_reg, offset1, addr); + if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, JIT_OP_BLTU, + cc->cmp_reg, NULL)) { + goto fail; + } + +#ifndef OS_ENABLE_HW_BOUND_CHECK + /* if (offset1 > memory_boundary) goto EXCEPTION */ + GEN_INSN(CMP, cc->cmp_reg, offset1, memory_boundary); + if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, JIT_OP_BGTU, + cc->cmp_reg, NULL)) { + goto fail; + } +#endif + + return offset1; +fail: + return 0; +} +#endif + +static JitReg +check_and_seek(JitCompContext *cc, JitReg addr, uint32 offset, uint32 bytes) +{ + JitReg memory_boundary = 0, offset1; +#ifndef OS_ENABLE_HW_BOUND_CHECK + JitReg cur_page_count; + /* the default memory */ + uint32 mem_idx = 0; +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + /* ---------- check ---------- */ + /* 1. shortcut if the memory size is 0 */ + if (cc->cur_wasm_module->memories != NULL + && 0 == cc->cur_wasm_module->memories[mem_idx].init_page_count) { + + cur_page_count = get_cur_page_count_reg(cc->jit_frame, mem_idx); + + /* if (cur_mem_page_count == 0) goto EXCEPTION */ + GEN_INSN(CMP, cc->cmp_reg, cur_page_count, NEW_CONST(I32, 0)); + if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + JIT_OP_BEQ, cc->cmp_reg, NULL)) { + goto fail; + } + } + + /* 2. a complete boundary check */ + memory_boundary = get_memory_boundary(cc, mem_idx, bytes); + if (!memory_boundary) + goto fail; +#endif + +#if UINTPTR_MAX == UINT64_MAX + offset1 = check_and_seek_on_64bit_platform(cc, addr, NEW_CONST(I64, offset), + memory_boundary); + if (!offset1) + goto fail; +#else + offset1 = check_and_seek_on_32bit_platform(cc, addr, NEW_CONST(I32, offset), + memory_boundary); + if (!offset1) + goto fail; +#endif + + return offset1; +fail: + return 0; +} + +#if UINTPTR_MAX == UINT64_MAX +#define CHECK_ALIGNMENT(offset1) \ + do { \ + JitReg align_mask = NEW_CONST(I64, ((uint64)1 << align) - 1); \ + JitReg AND_res = jit_cc_new_reg_I64(cc); \ + GEN_INSN(AND, AND_res, offset1, 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) +#else +#define CHECK_ALIGNMENT(offset1) \ + do { \ + JitReg align_mask = NEW_CONST(I32, (1 << align) - 1); \ + JitReg AND_res = jit_cc_new_reg_I32(cc); \ + GEN_INSN(AND, AND_res, offset1, align_mask); \ + GEN_INSN(CMP, cc->cmp_reg, AND_res, NEW_CONST(I32, 0)); \ + if (!jit_emit_exception(cc, EXCE_UNALIGNED_ATOMIC, JIT_OP_BNE, \ + cc->cmp_reg, NULL)) \ + goto fail; \ + } while (0) +#endif + +bool +jit_compile_op_i32_load(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool sign, bool atomic) +{ + JitReg addr, offset1, value, memory_data; + JitInsn *load_insn = NULL; + + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) { + goto fail; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + CHECK_ALIGNMENT(offset1); + } +#endif + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + value = jit_cc_new_reg_I32(cc); + switch (bytes) { + case 1: + { + if (sign) { + load_insn = GEN_INSN(LDI8, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU8, value, memory_data, offset1); + } + break; + } + case 2: + { + if (sign) { + load_insn = GEN_INSN(LDI16, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU16, value, memory_data, offset1); + } + break; + } + case 4: + { + if (sign) { + load_insn = GEN_INSN(LDI32, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU32, value, memory_data, offset1); + } + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic && load_insn) + set_load_or_store_atomic(load_insn); +#else + (void)load_insn; +#endif + + PUSH_I32(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_load(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool sign, bool atomic) +{ + JitReg addr, offset1, value, memory_data; + JitInsn *load_insn = NULL; + + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) { + goto fail; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + CHECK_ALIGNMENT(offset1); + } +#endif + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + value = jit_cc_new_reg_I64(cc); + switch (bytes) { + case 1: + { + if (sign) { + load_insn = GEN_INSN(LDI8, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU8, value, memory_data, offset1); + } + break; + } + case 2: + { + if (sign) { + load_insn = GEN_INSN(LDI16, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU16, value, memory_data, offset1); + } + break; + } + case 4: + { + if (sign) { + load_insn = GEN_INSN(LDI32, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU32, value, memory_data, offset1); + } + break; + } + case 8: + { + if (sign) { + load_insn = GEN_INSN(LDI64, value, memory_data, offset1); + } + else { + load_insn = GEN_INSN(LDU64, value, memory_data, offset1); + } + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic && load_insn) + set_load_or_store_atomic(load_insn); +#else + (void)load_insn; +#endif + + PUSH_I64(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_load(JitCompContext *cc, uint32 align, uint32 offset) +{ + JitReg addr, offset1, value, memory_data; + + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, 4); + if (!offset1) { + goto fail; + } + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + value = jit_cc_new_reg_F32(cc); + GEN_INSN(LDF32, value, memory_data, offset1); + + PUSH_F32(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_load(JitCompContext *cc, uint32 align, uint32 offset) +{ + JitReg addr, offset1, value, memory_data; + + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, 8); + if (!offset1) { + goto fail; + } + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + value = jit_cc_new_reg_F64(cc); + GEN_INSN(LDF64, value, memory_data, offset1); + + PUSH_F64(value); + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_store(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool atomic) +{ + JitReg value, addr, offset1, memory_data; + JitInsn *store_insn = NULL; + + POP_I32(value); + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) { + goto fail; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + CHECK_ALIGNMENT(offset1); + } +#endif + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + switch (bytes) { + case 1: + { + store_insn = GEN_INSN(STI8, value, memory_data, offset1); + break; + } + case 2: + { + store_insn = GEN_INSN(STI16, value, memory_data, offset1); + break; + } + case 4: + { + store_insn = GEN_INSN(STI32, value, memory_data, offset1); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic && store_insn) + set_load_or_store_atomic(store_insn); +#else + (void)store_insn; +#endif + + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_store(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool atomic) +{ + JitReg value, addr, offset1, memory_data; + JitInsn *store_insn = NULL; + + POP_I64(value); + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) { + goto fail; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + CHECK_ALIGNMENT(offset1); + } +#endif + + if (jit_reg_is_const(value) && bytes < 8) { + value = NEW_CONST(I32, (int32)jit_cc_get_const_I64(cc, value)); + } + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + switch (bytes) { + case 1: + { + store_insn = GEN_INSN(STI8, value, memory_data, offset1); + break; + } + case 2: + { + store_insn = GEN_INSN(STI16, value, memory_data, offset1); + break; + } + case 4: + { + store_insn = GEN_INSN(STI32, value, memory_data, offset1); + break; + } + case 8: + { + store_insn = GEN_INSN(STI64, value, memory_data, offset1); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic && store_insn) + set_load_or_store_atomic(store_insn); +#else + (void)store_insn; +#endif + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_store(JitCompContext *cc, uint32 align, uint32 offset) +{ + JitReg value, addr, offset1, memory_data; + + POP_F32(value); + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, 4); + if (!offset1) { + goto fail; + } + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + GEN_INSN(STF32, value, memory_data, offset1); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_store(JitCompContext *cc, uint32 align, uint32 offset) +{ + JitReg value, addr, offset1, memory_data; + + POP_F64(value); + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, 8); + if (!offset1) { + goto fail; + } + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + GEN_INSN(STF64, value, memory_data, offset1); + + return true; +fail: + return false; +} + +bool +jit_compile_op_memory_size(JitCompContext *cc, uint32 mem_idx) +{ + JitReg cur_page_count; + + cur_page_count = get_cur_page_count_reg(cc->jit_frame, mem_idx); + + PUSH_I32(cur_page_count); + + return true; +fail: + return false; +} + +bool +jit_compile_op_memory_grow(JitCompContext *cc, uint32 mem_idx) +{ + JitReg grow_res, res; + JitReg prev_page_count, inc_page_count, args[2]; + + /* Get current page count as prev_page_count */ + prev_page_count = get_cur_page_count_reg(cc->jit_frame, mem_idx); + + /* Call wasm_enlarge_memory */ + POP_I32(inc_page_count); + + grow_res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = inc_page_count; + + /* TODO: multi-memory wasm_enlarge_memory_with_idx() */ + if (!jit_emit_callnative(cc, wasm_enlarge_memory, grow_res, args, 2)) { + goto fail; + } + /* Convert bool to uint32 */ + GEN_INSN(AND, grow_res, grow_res, NEW_CONST(I32, 0xFF)); + + /* return different values according to memory.grow result */ + res = jit_cc_new_reg_I32(cc); + GEN_INSN(CMP, cc->cmp_reg, grow_res, NEW_CONST(I32, 0)); + GEN_INSN(SELECTNE, res, cc->cmp_reg, prev_page_count, + NEW_CONST(I32, (int32)-1)); + PUSH_I32(res); + + /* Ensure a refresh in next get memory related registers */ + clear_memory_regs(cc->jit_frame); + + return true; +fail: + return false; +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static int +wasm_init_memory(WASMModuleInstance *inst, uint32 mem_idx, uint32 seg_idx, + uint32 len, uint32 mem_offset, uint32 data_offset) +{ + WASMMemoryInstance *mem_inst; + WASMDataSeg *data_segment; + uint64 mem_size; + uint8 *mem_addr, *data_addr; + uint32 seg_len; + + /* if d + n > the length of mem.data */ + mem_inst = inst->memories[mem_idx]; + mem_size = mem_inst->cur_page_count * (uint64)mem_inst->num_bytes_per_page; + if (mem_size < mem_offset || mem_size - mem_offset < len) + goto out_of_bounds; + + /* if s + n > the length of data.data */ + bh_assert(seg_idx < inst->module->data_seg_count); + if (bh_bitmap_get_bit(inst->e->common.data_dropped, seg_idx)) { + seg_len = 0; + data_addr = NULL; + } + else { + data_segment = inst->module->data_segments[seg_idx]; + seg_len = data_segment->data_length; + data_addr = data_segment->data + data_offset; + } + if (seg_len < data_offset || seg_len - data_offset < len) + goto out_of_bounds; + + mem_addr = mem_inst->memory_data + mem_offset; + bh_memcpy_s(mem_addr, (uint32)(mem_size - mem_offset), data_addr, len); + + return 0; +out_of_bounds: + wasm_set_exception(inst, "out of bounds memory access"); + return -1; +} + +bool +jit_compile_op_memory_init(JitCompContext *cc, uint32 mem_idx, uint32 seg_idx) +{ + JitReg len, mem_offset, data_offset, res; + JitReg args[6] = { 0 }; + + POP_I32(len); + POP_I32(data_offset); + POP_I32(mem_offset); + + res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, mem_idx); + args[2] = NEW_CONST(I32, seg_idx); + args[3] = len; + args[4] = mem_offset; + args[5] = data_offset; + + if (!jit_emit_callnative(cc, wasm_init_memory, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + 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; + + return true; +fail: + return false; +} + +static void +wasm_data_drop(WASMModuleInstance *inst, uint32 seg_idx) +{ + bh_bitmap_set_bit(inst->e->common.data_dropped, seg_idx); +} + +bool +jit_compile_op_data_drop(JitCompContext *cc, uint32 seg_idx) +{ + JitReg args[2] = { 0 }; + + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, seg_idx); + + return jit_emit_callnative(cc, wasm_data_drop, 0, args, + sizeof(args) / sizeof(args[0])); +} +#endif + +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 +static int +wasm_copy_memory(WASMModuleInstance *inst, uint32 src_mem_idx, + uint32 dst_mem_idx, uint32 len, uint32 src_offset, + uint32 dst_offset) +{ + WASMMemoryInstance *src_mem, *dst_mem; + uint64 src_mem_size, dst_mem_size; + uint8 *src_addr, *dst_addr; + + src_mem = inst->memories[src_mem_idx]; + dst_mem = inst->memories[dst_mem_idx]; + src_mem_size = + src_mem->cur_page_count * (uint64)src_mem->num_bytes_per_page; + dst_mem_size = + dst_mem->cur_page_count * (uint64)dst_mem->num_bytes_per_page; + + /* if s + n > the length of mem.data */ + if (src_mem_size < src_offset || src_mem_size - src_offset < len) + goto out_of_bounds; + + /* if d + n > the length of mem.data */ + if (dst_mem_size < dst_offset || dst_mem_size - dst_offset < len) + goto out_of_bounds; + + src_addr = src_mem->memory_data + src_offset; + dst_addr = dst_mem->memory_data + dst_offset; + /* allowing the destination and source to overlap */ + bh_memmove_s(dst_addr, (uint32)(dst_mem_size - dst_offset), src_addr, len); + + return 0; +out_of_bounds: + wasm_set_exception(inst, "out of bounds memory access"); + return -1; +} + +bool +jit_compile_op_memory_copy(JitCompContext *cc, uint32 src_mem_idx, + uint32 dst_mem_idx) +{ + JitReg len, src, dst, res; + JitReg args[6] = { 0 }; + + POP_I32(len); + POP_I32(src); + POP_I32(dst); + + res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, src_mem_idx); + args[2] = NEW_CONST(I32, dst_mem_idx); + args[3] = len; + args[4] = src; + args[5] = dst; + + if (!jit_emit_callnative(cc, wasm_copy_memory, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + 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; + + return true; +fail: + return false; +} + +static int +wasm_fill_memory(WASMModuleInstance *inst, uint32 mem_idx, uint32 len, + uint32 val, uint32 dst) +{ + WASMMemoryInstance *mem_inst; + uint64 mem_size; + uint8 *dst_addr; + + mem_inst = inst->memories[mem_idx]; + mem_size = mem_inst->cur_page_count * (uint64)mem_inst->num_bytes_per_page; + + if (mem_size < dst || mem_size - dst < len) + goto out_of_bounds; + + dst_addr = mem_inst->memory_data + dst; + memset(dst_addr, val, len); + + return 0; +out_of_bounds: + wasm_set_exception(inst, "out of bounds memory access"); + return -1; +} + +bool +jit_compile_op_memory_fill(JitCompContext *cc, uint32 mem_idx) +{ + JitReg res, len, val, dst; + JitReg args[5] = { 0 }; + + POP_I32(len); + POP_I32(val); + POP_I32(dst); + + res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, mem_idx); + args[2] = len; + args[3] = val; + args[4] = dst; + + if (!jit_emit_callnative(cc, wasm_fill_memory, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + 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; + + return true; +fail: + return false; +} +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#define GEN_AT_RMW_INSN(op, op_type, bytes, result, value, memory_data, \ + offset1) \ + do { \ + switch (bytes) { \ + case 1: \ + { \ + insn = GEN_INSN(AT_##op##U8, result, value, memory_data, \ + offset1); \ + break; \ + } \ + case 2: \ + { \ + insn = GEN_INSN(AT_##op##U16, result, value, memory_data, \ + offset1); \ + break; \ + } \ + case 4: \ + { \ + if (op_type == VALUE_TYPE_I32) \ + insn = GEN_INSN(AT_##op##I32, result, value, memory_data, \ + offset1); \ + else \ + insn = GEN_INSN(AT_##op##U32, result, value, memory_data, \ + offset1); \ + break; \ + } \ + case 8: \ + { \ + insn = GEN_INSN(AT_##op##I64, result, value, memory_data, \ + offset1); \ + break; \ + } \ + default: \ + { \ + bh_assert(0); \ + goto fail; \ + } \ + } \ + } while (0) + +bool +jit_compile_op_atomic_rmw(JitCompContext *cc, uint8 atomic_op, uint8 op_type, + uint32 align, uint32 offset, uint32 bytes) +{ + JitReg addr, offset1, memory_data, value, result, eax_hreg, rax_hreg, + ebx_hreg, rbx_hreg; + JitInsn *insn = NULL; + bool is_i32 = op_type == VALUE_TYPE_I32; + bool is_logical_op = atomic_op == AtomicRMWBinOpAnd + || atomic_op == AtomicRMWBinOpOr + || atomic_op == AtomicRMWBinOpXor; + + /* currently we only implement atomic rmw on x86-64 target */ +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + + /* For atomic logical binary ops, it implicitly uses rax in cmpxchg + * instruction and implicitly uses rbx for storing temp value in the + * generated loop */ + eax_hreg = jit_codegen_get_hreg_by_name("eax"); + rax_hreg = jit_codegen_get_hreg_by_name("rax"); + ebx_hreg = jit_codegen_get_hreg_by_name("ebx"); + rbx_hreg = jit_codegen_get_hreg_by_name("rbx"); + + bh_assert(op_type == VALUE_TYPE_I32 || op_type == VALUE_TYPE_I64); + if (op_type == VALUE_TYPE_I32) { + POP_I32(value); + } + else { + POP_I64(value); + } + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) { + goto fail; + } + CHECK_ALIGNMENT(offset1); + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + if (op_type == VALUE_TYPE_I32) + result = jit_cc_new_reg_I32(cc); + else + result = jit_cc_new_reg_I64(cc); + + switch (atomic_op) { + case AtomicRMWBinOpAdd: + { + GEN_AT_RMW_INSN(ADD, op_type, bytes, result, value, memory_data, + offset1); + break; + } + case AtomicRMWBinOpSub: + { + GEN_AT_RMW_INSN(SUB, op_type, bytes, result, value, memory_data, + offset1); + break; + } + case AtomicRMWBinOpAnd: + { + GEN_AT_RMW_INSN(AND, op_type, bytes, result, value, memory_data, + offset1); + break; + } + case AtomicRMWBinOpOr: + { + GEN_AT_RMW_INSN(OR, op_type, bytes, result, value, memory_data, + offset1); + break; + } + case AtomicRMWBinOpXor: + { + GEN_AT_RMW_INSN(XOR, op_type, bytes, result, value, memory_data, + offset1); + break; + } + case AtomicRMWBinOpXchg: + { + GEN_AT_RMW_INSN(XCHG, op_type, bytes, result, value, memory_data, + offset1); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + if (is_logical_op + && (!insn + || !jit_lock_reg_in_insn(cc, insn, is_i32 ? eax_hreg : rax_hreg) + || !jit_lock_reg_in_insn(cc, insn, is_i32 ? ebx_hreg : rbx_hreg))) { + jit_set_last_error( + cc, "generate atomic logical insn or lock ra&rb hreg failed"); + goto fail; + } + + if (op_type == VALUE_TYPE_I32) + PUSH_I32(result); + else + PUSH_I64(result); + + return true; +#endif /* defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) */ + +fail: + return false; +} + +bool +jit_compile_op_atomic_cmpxchg(JitCompContext *cc, uint8 op_type, uint32 align, + uint32 offset, uint32 bytes) +{ + JitReg addr, offset1, memory_data, value, expect, result; + bool is_i32 = op_type == VALUE_TYPE_I32; + /* currently we only implement atomic cmpxchg on x86-64 target */ +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + /* cmpxchg will use register al/ax/eax/rax to store parameter expected + * value, and the read result will also be stored to al/ax/eax/rax */ + JitReg eax_hreg = jit_codegen_get_hreg_by_name("eax"); + JitReg rax_hreg = jit_codegen_get_hreg_by_name("rax"); + JitInsn *insn = NULL; + + bh_assert(op_type == VALUE_TYPE_I32 || op_type == VALUE_TYPE_I64); + if (is_i32) { + POP_I32(value); + POP_I32(expect); + result = jit_cc_new_reg_I32(cc); + } + else { + POP_I64(value); + POP_I64(expect); + result = jit_cc_new_reg_I64(cc); + } + POP_I32(addr); + + offset1 = check_and_seek(cc, addr, offset, bytes); + if (!offset1) { + goto fail; + } + CHECK_ALIGNMENT(offset1); + + memory_data = get_memory_data_reg(cc->jit_frame, 0); + + GEN_INSN(MOV, is_i32 ? eax_hreg : rax_hreg, expect); + switch (bytes) { + case 1: + { + insn = GEN_INSN(AT_CMPXCHGU8, value, is_i32 ? eax_hreg : rax_hreg, + memory_data, offset1); + break; + } + case 2: + { + insn = GEN_INSN(AT_CMPXCHGU16, value, is_i32 ? eax_hreg : rax_hreg, + memory_data, offset1); + break; + } + case 4: + { + if (op_type == VALUE_TYPE_I32) + insn = + GEN_INSN(AT_CMPXCHGI32, value, is_i32 ? eax_hreg : rax_hreg, + memory_data, offset1); + else + insn = + GEN_INSN(AT_CMPXCHGU32, value, is_i32 ? eax_hreg : rax_hreg, + memory_data, offset1); + break; + } + case 8: + { + insn = GEN_INSN(AT_CMPXCHGI64, value, is_i32 ? eax_hreg : rax_hreg, + memory_data, offset1); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + if (!insn + || !jit_lock_reg_in_insn(cc, insn, is_i32 ? eax_hreg : rax_hreg)) { + jit_set_last_error(cc, "generate cmpxchg insn or lock ra hreg failed"); + goto fail; + } + + GEN_INSN(MOV, result, is_i32 ? eax_hreg : rax_hreg); + + if (is_i32) + PUSH_I32(result); + else + PUSH_I64(result); + + return true; +#endif /* defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) */ + +fail: + return false; +} + +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; + CHECK_ALIGNMENT(offset1); + + JitReg maddr = jit_cc_new_reg_ptr(cc); + GEN_INSN(ADD, 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); + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (!jit_check_suspend_flags(cc)) + goto fail; +#endif + return true; +fail: + return false; +} + +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; + CHECK_ALIGNMENT(offset1); + + JitReg maddr = jit_cc_new_reg_ptr(cc); + GEN_INSN(ADD, 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; +} + +bool +jit_compiler_op_atomic_fence(JitCompContext *cc) +{ + GEN_INSN(FENCE); + return true; +} +#endif diff --git a/core/iwasm/fast-jit/fe/jit_emit_memory.h b/core/iwasm/fast-jit/fe/jit_emit_memory.h new file mode 100644 index 0000000000..2fdacccf52 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_memory.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_MEMORY_H_ +#define _JIT_EMIT_MEMORY_H_ + +#include "../jit_compiler.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../../common/wasm_shared_memory.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_i32_load(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool sign, bool atomic); + +bool +jit_compile_op_i64_load(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool sign, bool atomic); + +bool +jit_compile_op_f32_load(JitCompContext *cc, uint32 align, uint32 offset); + +bool +jit_compile_op_f64_load(JitCompContext *cc, uint32 align, uint32 offset); + +bool +jit_compile_op_i32_store(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool atomic); + +bool +jit_compile_op_i64_store(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes, bool atomic); + +bool +jit_compile_op_f32_store(JitCompContext *cc, uint32 align, uint32 offset); + +bool +jit_compile_op_f64_store(JitCompContext *cc, uint32 align, uint32 offset); + +bool +jit_compile_op_memory_size(JitCompContext *cc, uint32 mem_idx); + +bool +jit_compile_op_memory_grow(JitCompContext *cc, uint32 mem_idx); + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +jit_compile_op_memory_init(JitCompContext *cc, uint32 mem_idx, uint32 seg_idx); + +bool +jit_compile_op_data_drop(JitCompContext *cc, uint32 seg_idx); +#endif + +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 +bool +jit_compile_op_memory_copy(JitCompContext *cc, uint32 src_mem_idx, + uint32 dst_mem_idx); + +bool +jit_compile_op_memory_fill(JitCompContext *cc, uint32 mem_idx); +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +bool +jit_compile_op_atomic_rmw(JitCompContext *cc, uint8 atomic_op, uint8 op_type, + uint32 align, uint32 offset, uint32 bytes); + +bool +jit_compile_op_atomic_cmpxchg(JitCompContext *cc, uint8 op_type, uint32 align, + uint32 offset, uint32 bytes); + +bool +jit_compile_op_atomic_wait(JitCompContext *cc, uint8 op_type, uint32 align, + uint32 offset, uint32 bytes); + +bool +jit_compiler_op_atomic_notify(JitCompContext *cc, uint32 align, uint32 offset, + uint32 bytes); + +bool +jit_compiler_op_atomic_fence(JitCompContext *cc); +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_MEMORY_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_numberic.c b/core/iwasm/fast-jit/fe/jit_emit_numberic.c new file mode 100644 index 0000000000..347c21827e --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_numberic.c @@ -0,0 +1,1719 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_numberic.h" +#include "jit_emit_exception.h" +#include "jit_emit_control.h" +#include "jit_emit_function.h" +#include "../jit_frontend.h" +#include "../jit_codegen.h" + +#define PUSH_INT(v) \ + do { \ + if (is_i32) \ + PUSH_I32(v); \ + else \ + PUSH_I64(v); \ + } while (0) + +#define POP_INT(v) \ + do { \ + if (is_i32) \ + POP_I32(v); \ + else \ + POP_I64(v); \ + } while (0) + +#define PUSH_FLOAT(v) \ + do { \ + if (is_f32) \ + PUSH_F32(v); \ + else \ + PUSH_F64(v); \ + } while (0) + +#define POP_FLOAT(v) \ + do { \ + if (is_f32) \ + POP_F32(v); \ + else \ + POP_F64(v); \ + } while (0) + +#define DEF_INT_UNARY_OP(op, err) \ + do { \ + JitReg res, operand; \ + POP_INT(operand); \ + if (!(res = op)) { \ + if (err) \ + jit_set_last_error(cc, err); \ + goto fail; \ + } \ + PUSH_INT(res); \ + } while (0) + +#define DEF_INT_BINARY_OP(op, err) \ + do { \ + JitReg res, left, right; \ + POP_INT(right); \ + POP_INT(left); \ + if (!(res = op)) { \ + if (err) \ + jit_set_last_error(cc, err); \ + goto fail; \ + } \ + PUSH_INT(res); \ + } while (0) + +#define DEF_FP_UNARY_OP(op, err) \ + do { \ + JitReg res, operand; \ + POP_FLOAT(operand); \ + if (!(res = op)) { \ + if (err) \ + jit_set_last_error(cc, err); \ + goto fail; \ + } \ + PUSH_FLOAT(res); \ + } while (0) + +#define DEF_FP_BINARY_OP(op, err) \ + do { \ + JitReg res, left, right; \ + POP_FLOAT(right); \ + POP_FLOAT(left); \ + if (!(res = op)) { \ + if (err) \ + jit_set_last_error(cc, err); \ + goto fail; \ + } \ + PUSH_FLOAT(res); \ + } while (0) + +static uint32 +clz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 0x80000000)) { + num++; + type <<= 1; + } + return num; +} + +static uint64 +clz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +static uint32 +ctz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static uint64 +ctz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static uint32 +popcnt32(uint32 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static uint64 +popcnt64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +bool +jit_compile_op_i32_clz(JitCompContext *cc) +{ + JitReg value, res; + + POP_I32(value); + if (jit_reg_is_const(value)) { + uint32 i32 = jit_cc_get_const_I32(cc, value); + PUSH_I32(NEW_CONST(I32, clz32(i32))); + return true; + } + + res = jit_cc_new_reg_I32(cc); + GEN_INSN(CLZ, res, value); + PUSH_I32(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_ctz(JitCompContext *cc) +{ + JitReg value, res = jit_cc_new_reg_I32(cc); + + POP_I32(value); + if (jit_reg_is_const(value)) { + uint32 i32 = jit_cc_get_const_I32(cc, value); + PUSH_I32(NEW_CONST(I32, ctz32(i32))); + return true; + } + + res = jit_cc_new_reg_I32(cc); + GEN_INSN(CTZ, res, value); + PUSH_I32(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_popcnt(JitCompContext *cc) +{ + JitReg value, res; + + POP_I32(value); + if (jit_reg_is_const(value)) { + uint32 i32 = jit_cc_get_const_I32(cc, value); + PUSH_I32(NEW_CONST(I32, popcnt32(i32))); + return true; + } + + res = jit_cc_new_reg_I32(cc); + GEN_INSN(POPCNT, res, value); + PUSH_I32(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_clz(JitCompContext *cc) +{ + JitReg value, res; + + POP_I64(value); + if (jit_reg_is_const(value)) { + uint64 i64 = jit_cc_get_const_I64(cc, value); + PUSH_I64(NEW_CONST(I64, clz64(i64))); + return true; + } + + res = jit_cc_new_reg_I64(cc); + GEN_INSN(CLZ, res, value); + PUSH_I64(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_ctz(JitCompContext *cc) +{ + JitReg value, res; + + POP_I64(value); + if (jit_reg_is_const(value)) { + uint64 i64 = jit_cc_get_const_I64(cc, value); + PUSH_I64(NEW_CONST(I64, ctz64(i64))); + return true; + } + + res = jit_cc_new_reg_I64(cc); + GEN_INSN(CTZ, res, value); + PUSH_I64(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i64_popcnt(JitCompContext *cc) +{ + JitReg value, res; + + POP_I64(value); + if (jit_reg_is_const(value)) { + uint64 i64 = jit_cc_get_const_I64(cc, value); + PUSH_I64(NEW_CONST(I64, popcnt64(i64))); + return true; + } + + res = jit_cc_new_reg_I64(cc); + GEN_INSN(POPCNT, res, value); + PUSH_I64(res); + return true; +fail: + return false; +} + +#define IS_CONST_ALL_ONE(val, is_i32) \ + (jit_reg_is_const(val) \ + && ((is_i32 && jit_cc_get_const_I32(cc, val) == -1) \ + || (!is_i32 && jit_cc_get_const_I64(cc, val) == -1LL))) + +#define IS_CONST_ZERO(val) \ + (jit_reg_is_const(val) \ + && ((is_i32 && jit_cc_get_const_I32(cc, val) == 0) \ + || (!is_i32 && jit_cc_get_const_I64(cc, val) == 0))) + +/* macros for integer binary operations (ibinop) */ + +#if defined(__GNUC__) +#define NO_SANITIZER_INTEGER \ + __attribute__((no_sanitize("signed-integer-overflow"))) +#else +#define NO_SANITIZER_INTEGER +#endif + +#define __DEF_BI_INT_CONST_OPS(bits, opname, op) \ + NO_SANITIZER_INTEGER \ + static int##bits do_i##bits##_const_##opname(int##bits lhs, int##bits rhs) \ + { \ + return lhs op rhs; \ + } + +#define DEF_BI_INT_CONST_OPS(opname, op) \ + __DEF_BI_INT_CONST_OPS(32, opname, op) \ + __DEF_BI_INT_CONST_OPS(64, opname, op) + +#define DEF_UNI_INT_CONST_OPS(opname) \ + static JitReg compile_int_##opname##_consts( \ + JitCompContext *cc, JitReg left, JitReg right, bool is_i32) + +typedef JitReg (*uni_const_handler)(JitCompContext *, JitReg, JitReg, bool); +typedef int32 (*bin_i32_consts_handler)(int32, int32); +typedef int64 (*bin_i64_consts_handler)(int64, int64); + +/* ibinopt for integer binary operations */ +static JitReg +compile_op_ibinopt_const(JitCompContext *cc, JitReg left, JitReg right, + bool is_i32, uni_const_handler handle_one_const, + bin_i32_consts_handler handle_two_i32_const, + bin_i64_consts_handler handle_two_i64_const) +{ + JitReg res; + + if (jit_reg_is_const(left) && jit_reg_is_const(right)) { + if (is_i32) { + int32 left_val = jit_cc_get_const_I32(cc, left); + int32 right_val = jit_cc_get_const_I32(cc, right); + res = NEW_CONST(I32, handle_two_i32_const(left_val, right_val)); + } + else { + int64 left_val = jit_cc_get_const_I64(cc, left); + int64 right_val = jit_cc_get_const_I64(cc, right); + res = NEW_CONST(I64, handle_two_i64_const(left_val, right_val)); + } + goto shortcut; + } + + if (jit_reg_is_const(left) || jit_reg_is_const(right)) { + res = handle_one_const(cc, left, right, is_i32); + if (res) + goto shortcut; + } + + return 0; +shortcut: + return res; +} + +#define CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, opname) \ + compile_op_ibinopt_const(cc, left, right, is_i32, \ + compile_int_##opname##_consts, \ + do_i32_const_##opname, do_i64_const_##opname) + +DEF_UNI_INT_CONST_OPS(add) +{ + /* If one of the operands is 0, just return the other */ + if (IS_CONST_ZERO(left)) + return right; + if (IS_CONST_ZERO(right)) + return left; + + return 0; +} + +DEF_BI_INT_CONST_OPS(add, +) + +static JitReg +compile_int_add(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, add); + if (res) + goto shortcut; + + /* Build add */ + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(ADD, res, left, right); + +shortcut: + return res; +} + +DEF_UNI_INT_CONST_OPS(sub) +{ + /* If the right operand is 0, just return the left */ + if (IS_CONST_ZERO(right)) + return left; + + return 0; +} + +DEF_BI_INT_CONST_OPS(sub, -) + +static JitReg +compile_int_sub(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, sub); + if (res) + goto shortcut; + + /* Build sub */ + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(SUB, res, left, right); + +shortcut: + return res; +} + +DEF_UNI_INT_CONST_OPS(mul) +{ + /* If one of the operands is 0, just return constant 0 */ + if (IS_CONST_ZERO(left) || IS_CONST_ZERO(right)) + return is_i32 ? NEW_CONST(I32, 0) : NEW_CONST(I64, 0); + + return 0; +} + +static int32 +do_i32_const_mul(int32 lhs, int32 rhs) +{ + return (int32)((uint64)lhs * (uint64)rhs); +} + +static int64 +do_i64_const_mul(int64 lhs, int64 rhs) +{ + return (int64)((uint64)lhs * (uint64)rhs); +} + +static JitReg +compile_int_mul(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, mul); + if (res) + goto shortcut; + + /* Build mul */ + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(MUL, res, left, right); + +shortcut: + return res; +} + +static bool +compile_int_div_no_check(JitCompContext *cc, IntArithmetic arith_op, + bool is_i32, JitReg left, JitReg right, JitReg res) +{ +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + JitReg eax_hreg = jit_codegen_get_hreg_by_name("eax"); + JitReg edx_hreg = jit_codegen_get_hreg_by_name("edx"); + JitReg rax_hreg = jit_codegen_get_hreg_by_name("rax"); + JitReg rdx_hreg = jit_codegen_get_hreg_by_name("rdx"); +#endif + + if (jit_reg_is_const(right) && jit_reg_is_const(left)) { + if (INT_DIV_S == arith_op || INT_REM_S == arith_op) { + if (is_i32) { + int32 lhs = jit_cc_get_const_I32(cc, left); + int32 rhs = jit_cc_get_const_I32(cc, right); + if (INT_DIV_S == arith_op) { + res = NEW_CONST(I32, lhs / rhs); + } + else { + res = NEW_CONST(I32, lhs % rhs); + } + PUSH_I32(res); + return true; + } + else { + int64 lhs = jit_cc_get_const_I64(cc, left); + int64 rhs = jit_cc_get_const_I64(cc, right); + if (INT_DIV_S == arith_op) { + res = NEW_CONST(I64, lhs / rhs); + } + else { + res = NEW_CONST(I64, lhs % rhs); + } + PUSH_I64(res); + return true; + } + } + else { + if (is_i32) { + uint32 lhs = (uint32)jit_cc_get_const_I32(cc, left); + uint32 rhs = (uint32)jit_cc_get_const_I32(cc, right); + if (INT_DIV_U == arith_op) { + res = NEW_CONST(I32, lhs / rhs); + } + else { + res = NEW_CONST(I32, lhs % rhs); + } + PUSH_I32(res); + return true; + } + else { + uint64 lhs = (uint64)jit_cc_get_const_I64(cc, left); + uint64 rhs = (uint64)jit_cc_get_const_I64(cc, right); + if (INT_DIV_U == arith_op) { + res = NEW_CONST(I64, lhs / rhs); + } + else { + res = NEW_CONST(I64, lhs % rhs); + } + PUSH_I64(res); + return true; + } + } + } + + switch (arith_op) { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + case INT_DIV_S: + case INT_DIV_U: + { + JitInsn *insn = NULL, *insn1 = NULL; + + if (is_i32) { + GEN_INSN(MOV, eax_hreg, left); + if (arith_op == INT_DIV_S) + insn = GEN_INSN(DIV_S, eax_hreg, eax_hreg, right); + else + insn = GEN_INSN(DIV_U, eax_hreg, eax_hreg, right); + } + else { + GEN_INSN(MOV, rax_hreg, left); + if (arith_op == INT_DIV_S) + insn = GEN_INSN(DIV_S, rax_hreg, rax_hreg, right); + else + insn = GEN_INSN(DIV_U, rax_hreg, rax_hreg, right); + } + + if (!insn) { + goto fail; + } + if (!jit_lock_reg_in_insn(cc, insn, eax_hreg) + || !jit_lock_reg_in_insn(cc, insn, edx_hreg)) { + goto fail; + } + + if (is_i32) { + res = jit_cc_new_reg_I32(cc); + insn1 = jit_insn_new_MOV(res, eax_hreg); + } + else { + res = jit_cc_new_reg_I64(cc); + insn1 = jit_insn_new_MOV(res, rax_hreg); + } + + if (!insn1) { + jit_set_last_error(cc, "generate insn failed"); + goto fail; + } + + jit_insn_insert_after(insn, insn1); + break; + } + case INT_REM_S: + case INT_REM_U: + { + JitInsn *insn = NULL, *insn1 = NULL; + + if (is_i32) { + GEN_INSN(MOV, eax_hreg, left); + if (arith_op == INT_REM_S) + insn = GEN_INSN(REM_S, edx_hreg, eax_hreg, right); + else + insn = GEN_INSN(REM_U, edx_hreg, eax_hreg, right); + } + else { + GEN_INSN(MOV, rax_hreg, left); + if (arith_op == INT_REM_S) + insn = GEN_INSN(REM_S, rdx_hreg, rax_hreg, right); + else + insn = GEN_INSN(REM_U, rdx_hreg, rax_hreg, right); + } + + if (!insn) { + goto fail; + } + if (!jit_lock_reg_in_insn(cc, insn, eax_hreg) + || !jit_lock_reg_in_insn(cc, insn, edx_hreg)) { + goto fail; + } + + if (is_i32) { + res = jit_cc_new_reg_I32(cc); + insn1 = jit_insn_new_MOV(res, edx_hreg); + } + else { + res = jit_cc_new_reg_I64(cc); + insn1 = jit_insn_new_MOV(res, rdx_hreg); + } + + if (!insn1) { + jit_set_last_error(cc, "generate insn failed"); + goto fail; + } + + jit_insn_insert_after(insn, insn1); + break; + } +#else + case INT_DIV_S: + GEN_INSN(DIV_S, res, left, right); + break; + case INT_DIV_U: + GEN_INSN(DIV_U, res, left, right); + break; + case INT_REM_S: + GEN_INSN(REM_S, res, left, right); + break; + case INT_REM_U: + GEN_INSN(REM_U, res, left, right); + break; +#endif /* defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) */ + default: + bh_assert(0); + return false; + } + + if (is_i32) + PUSH_I32(res); + else + PUSH_I64(res); + return true; +fail: + return false; +} + +static bool +compile_int_div(JitCompContext *cc, IntArithmetic arith_op, bool is_i32, + uint8 **p_frame_ip) +{ + JitReg left, right, res; + + bh_assert(arith_op == INT_DIV_S || arith_op == INT_DIV_U + || arith_op == INT_REM_S || arith_op == INT_REM_U); + + if (is_i32) { + POP_I32(right); + POP_I32(left); + res = jit_cc_new_reg_I32(cc); + } + else { + POP_I64(right); + POP_I64(left); + res = jit_cc_new_reg_I64(cc); + } + + if (jit_reg_is_const(right)) { + int64 right_val = is_i32 ? (int64)jit_cc_get_const_I32(cc, right) + : jit_cc_get_const_I64(cc, right); + + switch (right_val) { + case 0: + { + /* Directly throw exception if divided by zero */ + if (!(jit_emit_exception(cc, EXCE_INTEGER_DIVIDE_BY_ZERO, + JIT_OP_JMP, 0, NULL))) + goto fail; + + return jit_handle_next_reachable_block(cc, p_frame_ip); + } + case 1: + { + if (arith_op == INT_DIV_S || arith_op == INT_DIV_U) { + if (is_i32) + PUSH_I32(left); + else + PUSH_I64(left); + } + else { + if (is_i32) + PUSH_I32(NEW_CONST(I32, 0)); + else + PUSH_I64(NEW_CONST(I64, 0)); + } + return true; + } + case -1: + { + if (arith_op == INT_DIV_S) { + if (is_i32) + GEN_INSN(CMP, cc->cmp_reg, left, + NEW_CONST(I32, INT32_MIN)); + else + GEN_INSN(CMP, cc->cmp_reg, left, + NEW_CONST(I64, INT64_MIN)); + + /* Throw integer overflow exception if left is + INT32_MIN or INT64_MIN */ + if (!(jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, + JIT_OP_BEQ, cc->cmp_reg, NULL))) + goto fail; + + /* Push -(left) to stack */ + GEN_INSN(NEG, res, left); + if (is_i32) + PUSH_I32(res); + else + PUSH_I64(res); + return true; + } + else if (arith_op == INT_REM_S) { + if (is_i32) + PUSH_I32(NEW_CONST(I32, 0)); + else + PUSH_I64(NEW_CONST(I64, 0)); + return true; + } + else { + /* Build default div and rem */ + return compile_int_div_no_check(cc, arith_op, is_i32, left, + right, res); + } + } + default: + { + /* Build default div and rem */ + return compile_int_div_no_check(cc, arith_op, is_i32, left, + right, res); + } + } + } + else { + JitReg cmp1 = jit_cc_new_reg_I32(cc); + JitReg cmp2 = jit_cc_new_reg_I32(cc); + + GEN_INSN(CMP, cc->cmp_reg, right, + is_i32 ? NEW_CONST(I32, 0) : NEW_CONST(I64, 0)); + /* Throw integer divided by zero exception if right is zero */ + if (!(jit_emit_exception(cc, EXCE_INTEGER_DIVIDE_BY_ZERO, JIT_OP_BEQ, + cc->cmp_reg, NULL))) + goto fail; + + switch (arith_op) { + case INT_DIV_S: + { + /* Check integer overflow */ + GEN_INSN(CMP, cc->cmp_reg, left, + is_i32 ? NEW_CONST(I32, INT32_MIN) + : NEW_CONST(I64, INT64_MIN)); + GEN_INSN(SELECTEQ, cmp1, cc->cmp_reg, NEW_CONST(I32, 1), + NEW_CONST(I32, 0)); + GEN_INSN(CMP, cc->cmp_reg, right, + is_i32 ? NEW_CONST(I32, -1) : NEW_CONST(I64, -1LL)); + GEN_INSN(SELECTEQ, cmp2, cc->cmp_reg, NEW_CONST(I32, 1), + NEW_CONST(I32, 0)); + GEN_INSN(AND, cmp1, cmp1, cmp2); + GEN_INSN(CMP, cc->cmp_reg, cmp1, NEW_CONST(I32, 1)); + /* Throw integer overflow exception if left is INT32_MIN or + INT64_MIN, and right is -1 */ + if (!(jit_emit_exception(cc, EXCE_INTEGER_OVERFLOW, JIT_OP_BEQ, + cc->cmp_reg, NULL))) + goto fail; + + /* Build default div and rem */ + return compile_int_div_no_check(cc, arith_op, is_i32, left, + right, res); + } + case INT_REM_S: + { + JitReg left1 = + is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + + GEN_INSN(CMP, cc->cmp_reg, right, + is_i32 ? NEW_CONST(I32, -1) : NEW_CONST(I64, -1LL)); + /* Don't generate `SELECTEQ left, cmp_reg, 0, left` since + left might be const, use left1 instead */ + if (is_i32) + GEN_INSN(SELECTEQ, left1, cc->cmp_reg, NEW_CONST(I32, 0), + left); + else + GEN_INSN(SELECTEQ, left1, cc->cmp_reg, NEW_CONST(I64, 0), + left); + /* Build default div and rem */ + return compile_int_div_no_check(cc, arith_op, is_i32, left1, + right, res); + } + default: + { + /* Build default div and rem */ + return compile_int_div_no_check(cc, arith_op, is_i32, left, + right, res); + } + } + } + +fail: + return false; +} + +static bool +compile_op_int_arithmetic(JitCompContext *cc, IntArithmetic arith_op, + bool is_i32, uint8 **p_frame_ip) +{ + switch (arith_op) { + case INT_ADD: + DEF_INT_BINARY_OP(compile_int_add(cc, left, right, is_i32), + "compile int add fail."); + return true; + case INT_SUB: + DEF_INT_BINARY_OP(compile_int_sub(cc, left, right, is_i32), + "compile int sub fail."); + return true; + case INT_MUL: + DEF_INT_BINARY_OP(compile_int_mul(cc, left, right, is_i32), + "compile int mul fail."); + return true; + case INT_DIV_S: + case INT_DIV_U: + case INT_REM_S: + case INT_REM_U: + return compile_int_div(cc, arith_op, is_i32, p_frame_ip); + default: + bh_assert(0); + return false; + } + +fail: + return false; +} + +bool +jit_compile_op_i32_arithmetic(JitCompContext *cc, IntArithmetic arith_op, + uint8 **p_frame_ip) +{ + return compile_op_int_arithmetic(cc, arith_op, true, p_frame_ip); +} + +bool +jit_compile_op_i64_arithmetic(JitCompContext *cc, IntArithmetic arith_op, + uint8 **p_frame_ip) +{ + return compile_op_int_arithmetic(cc, arith_op, false, p_frame_ip); +} + +DEF_UNI_INT_CONST_OPS(and) +{ + JitReg res; + if (IS_CONST_ZERO(left) || IS_CONST_ZERO(right)) { + res = is_i32 ? NEW_CONST(I32, 0) : NEW_CONST(I64, 0); + goto shortcut; + } + + if (IS_CONST_ALL_ONE(left, is_i32)) { + res = right; + goto shortcut; + } + + if (IS_CONST_ALL_ONE(right, is_i32)) { + res = left; + goto shortcut; + } + + return 0; +shortcut: + return res; +} + +DEF_BI_INT_CONST_OPS(and, &) + +static JitReg +compile_int_and(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; + + /* shortcuts */ + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, and); + if (res) + goto shortcut; + + /* do and */ + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(AND, res, left, right); + +shortcut: + return res; +} + +DEF_UNI_INT_CONST_OPS(or) +{ + JitReg res; + + if (IS_CONST_ZERO(left)) { + res = right; + goto shortcut; + } + + if (IS_CONST_ZERO(right)) { + res = left; + goto shortcut; + } + + if (IS_CONST_ALL_ONE(left, is_i32) || IS_CONST_ALL_ONE(right, is_i32)) { + res = is_i32 ? NEW_CONST(I32, -1) : NEW_CONST(I64, -1LL); + goto shortcut; + } + + return 0; +shortcut: + return res; +} + +DEF_BI_INT_CONST_OPS(or, |) + +static JitReg +compile_int_or(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; + + /* shortcuts */ + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, or); + if (res) + goto shortcut; + + /* do or */ + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(OR, res, left, right); + +shortcut: + return res; +} + +DEF_UNI_INT_CONST_OPS(xor) +{ + if (IS_CONST_ZERO(left)) + return right; + + if (IS_CONST_ZERO(right)) + return left; + + return 0; +} + +DEF_BI_INT_CONST_OPS(xor, ^) + +static JitReg +compile_int_xor(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; + + /* shortcuts */ + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, xor); + if (res) + goto shortcut; + + /* do xor */ + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(XOR, res, left, right); + +shortcut: + return res; +} + +static bool +compile_op_int_bitwise(JitCompContext *cc, IntBitwise arith_op, bool is_i32) +{ + JitReg left, right, res; + + POP_INT(right); + POP_INT(left); + + switch (arith_op) { + case INT_AND: + { + res = compile_int_and(cc, left, right, is_i32); + break; + } + case INT_OR: + { + res = compile_int_or(cc, left, right, is_i32); + break; + } + case INT_XOR: + { + res = compile_int_xor(cc, left, right, is_i32); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + PUSH_INT(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_bitwise(JitCompContext *cc, IntBitwise bitwise_op) +{ + return compile_op_int_bitwise(cc, bitwise_op, true); +} + +bool +jit_compile_op_i64_bitwise(JitCompContext *cc, IntBitwise bitwise_op) +{ + return compile_op_int_bitwise(cc, bitwise_op, false); +} + +DEF_UNI_INT_CONST_OPS(shl) +{ + if (IS_CONST_ZERO(right) || IS_CONST_ZERO(left)) { + return left; + } + + if (jit_reg_is_const(right)) { + JitReg res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(SHL, res, left, right); + return res; + } + return 0; +} + +DEF_UNI_INT_CONST_OPS(shrs) +{ + if (IS_CONST_ZERO(right) || IS_CONST_ZERO(left) + || IS_CONST_ALL_ONE(left, is_i32)) { + return left; + } + + if (jit_reg_is_const(right)) { + JitReg res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(SHRS, res, left, right); + return res; + } + return 0; +} + +DEF_UNI_INT_CONST_OPS(shru) +{ + if (IS_CONST_ZERO(right) || IS_CONST_ZERO(left)) { + return left; + } + + if (jit_reg_is_const(right)) { + JitReg res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(SHRU, res, left, right); + return res; + } + return 0; +} + +static int32 +do_i32_const_shl(int32 lhs, int32 rhs) +{ + rhs &= 31; + return (int32)((uint32)lhs << (uint32)rhs); +} + +static int64 +do_i64_const_shl(int64 lhs, int64 rhs) +{ + rhs &= 63LL; + return (uint64)lhs << (uint64)rhs; +} + +DEF_BI_INT_CONST_OPS(shrs, >>) + +static int32 +do_i32_const_shru(int32 lhs, int32 rhs) +{ + rhs &= 31; + return (uint32)lhs >> rhs; +} + +static int64 +do_i64_const_shru(int64 lhs, int64 rhs) +{ + rhs &= 63LL; + return (uint64)lhs >> rhs; +} + +typedef enum { SHL, SHRS, SHRU, ROTL, ROTR } SHIFT_OP; + +static JitReg +compile_int_shift_modulo(JitCompContext *cc, JitReg rhs, bool is_i32, + SHIFT_OP op) +{ + JitReg res; + + if (jit_reg_is_const(rhs)) { + if (is_i32) { + int32 val = jit_cc_get_const_I32(cc, rhs); + val = val & 0x1f; + res = NEW_CONST(I32, val); + } + else { + int64 val = jit_cc_get_const_I64(cc, rhs); + val = val & 0x3f; + res = NEW_CONST(I64, val); + } + } + else { + if (op == ROTL || op == ROTR) { + /* No need to generate AND insn as the result + is same for rotate shift */ + res = rhs; + } + else if (is_i32) { + res = jit_cc_new_reg_I32(cc); + GEN_INSN(AND, res, rhs, NEW_CONST(I32, 0x1f)); + } + else { + res = jit_cc_new_reg_I64(cc); + GEN_INSN(AND, res, rhs, NEW_CONST(I64, 0x3f)); + } + } + + return res; +} + +static JitReg +mov_left_to_reg(JitCompContext *cc, bool is_i32, JitReg left) +{ + JitReg res = left; + /* left needs to be a variable */ + if (jit_reg_is_const(left)) { + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(MOV, res, left); + } + return res; +} + +static JitReg +compile_int_shl(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + JitReg ecx_hreg = jit_codegen_get_hreg_by_name("ecx"); + JitReg rcx_hreg = jit_codegen_get_hreg_by_name("rcx"); + JitInsn *insn = NULL; +#endif + + right = compile_int_shift_modulo(cc, right, is_i32, SHL); + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, shl); + if (res) + goto shortcut; + + left = mov_left_to_reg(cc, is_i32, left); + + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + GEN_INSN(MOV, is_i32 ? ecx_hreg : rcx_hreg, right); + insn = GEN_INSN(SHL, res, left, is_i32 ? ecx_hreg : rcx_hreg); + if (jit_get_last_error(cc) || !jit_lock_reg_in_insn(cc, insn, ecx_hreg)) { + goto fail; + } +#else + GEN_INSN(SHL, res, left, right); + if (jit_get_last_error(cc)) { + goto fail; + } +#endif + +shortcut: + return res; +fail: + return (JitReg)0; +} + +static JitReg +compile_int_shrs(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + JitReg ecx_hreg = jit_codegen_get_hreg_by_name("ecx"); + JitReg rcx_hreg = jit_codegen_get_hreg_by_name("rcx"); + JitInsn *insn = NULL; +#endif + + right = compile_int_shift_modulo(cc, right, is_i32, SHRS); + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, shrs); + if (res) + goto shortcut; + + left = mov_left_to_reg(cc, is_i32, left); + + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + GEN_INSN(MOV, is_i32 ? ecx_hreg : rcx_hreg, right); + insn = GEN_INSN(SHRS, res, left, is_i32 ? ecx_hreg : rcx_hreg); + if (jit_get_last_error(cc) || !jit_lock_reg_in_insn(cc, insn, ecx_hreg)) { + goto fail; + } +#else + GEN_INSN(SHRS, res, left, right); + if (jit_get_last_error(cc)) { + goto fail; + } +#endif + +shortcut: + return res; +fail: + return (JitReg)0; +} + +static JitReg +compile_int_shru(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + JitReg ecx_hreg = jit_codegen_get_hreg_by_name("ecx"); + JitReg rcx_hreg = jit_codegen_get_hreg_by_name("rcx"); + JitInsn *insn = NULL; +#endif + + right = compile_int_shift_modulo(cc, right, is_i32, SHRU); + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, shru); + if (res) + goto shortcut; + + left = mov_left_to_reg(cc, is_i32, left); + + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + GEN_INSN(MOV, is_i32 ? ecx_hreg : rcx_hreg, right); + insn = GEN_INSN(SHRU, res, left, is_i32 ? ecx_hreg : rcx_hreg); + if (jit_get_last_error(cc) || !jit_lock_reg_in_insn(cc, insn, ecx_hreg)) { + goto fail; + } +#else + GEN_INSN(SHRU, res, left, right); + if (jit_get_last_error(cc)) { + goto fail; + } +#endif + +shortcut: + return res; +fail: + return (JitReg)0; +} + +DEF_UNI_INT_CONST_OPS(rotl) +{ + if (IS_CONST_ZERO(right) || IS_CONST_ZERO(left) + || IS_CONST_ALL_ONE(left, is_i32)) + return left; + + if (jit_reg_is_const(right)) { + JitReg res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(ROTL, res, left, right); + return res; + } + + return 0; +} + +static int32 +do_i32_const_rotl(int32 lhs, int32 rhs) +{ + uint32 n = (uint32)lhs; + uint32 d = (uint32)rhs; + return (n << d) | (n >> (32 - d)); +} + +static int64 +do_i64_const_rotl(int64 lhs, int64 rhs) +{ + uint64 n = (uint64)lhs; + uint64 d = (uint64)rhs; + return (n << d) | (n >> (64 - d)); +} + +static JitReg +compile_int_rotl(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + JitReg ecx_hreg = jit_codegen_get_hreg_by_name("ecx"); + JitReg rcx_hreg = jit_codegen_get_hreg_by_name("rcx"); + JitInsn *insn = NULL; +#endif + + right = compile_int_shift_modulo(cc, right, is_i32, ROTL); + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, rotl); + if (res) + goto shortcut; + + left = mov_left_to_reg(cc, is_i32, left); + + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + GEN_INSN(MOV, is_i32 ? ecx_hreg : rcx_hreg, right); + insn = GEN_INSN(ROTL, res, left, is_i32 ? ecx_hreg : rcx_hreg); + if (jit_get_last_error(cc) || !jit_lock_reg_in_insn(cc, insn, ecx_hreg)) { + goto fail; + } +#else + GEN_INSN(ROTL, res, left, right); + if (jit_get_last_error(cc)) { + goto fail; + } +#endif + +shortcut: + return res; +fail: + return (JitReg)0; +} + +DEF_UNI_INT_CONST_OPS(rotr) +{ + if (IS_CONST_ZERO(right) || IS_CONST_ZERO(left) + || IS_CONST_ALL_ONE(left, is_i32)) + return left; + + if (jit_reg_is_const(right)) { + JitReg res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); + GEN_INSN(ROTR, res, left, right); + return res; + } + + return 0; +} + +static int32 +do_i32_const_rotr(int32 lhs, int32 rhs) +{ + uint32 n = (uint32)lhs; + uint32 d = (uint32)rhs; + return (n >> d) | (n << (32 - d)); +} + +static int64 +do_i64_const_rotr(int64 lhs, int64 rhs) +{ + uint64 n = (uint64)lhs; + uint64 d = (uint64)rhs; + return (n >> d) | (n << (64 - d)); +} + +static JitReg +compile_int_rotr(JitCompContext *cc, JitReg left, JitReg right, bool is_i32) +{ + JitReg res; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + JitReg ecx_hreg = jit_codegen_get_hreg_by_name("ecx"); + JitReg rcx_hreg = jit_codegen_get_hreg_by_name("rcx"); + JitInsn *insn = NULL; +#endif + + right = compile_int_shift_modulo(cc, right, is_i32, ROTR); + + res = CHECK_AND_PROCESS_INT_CONSTS(cc, left, right, is_i32, rotr); + if (res) + goto shortcut; + + left = mov_left_to_reg(cc, is_i32, left); + + res = is_i32 ? jit_cc_new_reg_I32(cc) : jit_cc_new_reg_I64(cc); +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + GEN_INSN(MOV, is_i32 ? ecx_hreg : rcx_hreg, right); + insn = GEN_INSN(ROTR, res, left, is_i32 ? ecx_hreg : rcx_hreg); + if (jit_get_last_error(cc) || !jit_lock_reg_in_insn(cc, insn, ecx_hreg)) { + goto fail; + } +#else + GEN_INSN(ROTR, res, left, right); + if (jit_get_last_error(cc)) { + goto fail; + } +#endif + +shortcut: + return res; +fail: + return (JitReg)0; +} + +static bool +compile_op_int_shift(JitCompContext *cc, IntShift shift_op, bool is_i32) +{ + JitReg left, right, res; + + POP_INT(right); + POP_INT(left); + + switch (shift_op) { + case INT_SHL: + { + res = compile_int_shl(cc, left, right, is_i32); + break; + } + case INT_SHR_S: + { + res = compile_int_shrs(cc, left, right, is_i32); + break; + } + case INT_SHR_U: + { + res = compile_int_shru(cc, left, right, is_i32); + break; + } + case INT_ROTL: + { + res = compile_int_rotl(cc, left, right, is_i32); + break; + } + case INT_ROTR: + { + res = compile_int_rotr(cc, left, right, is_i32); + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + PUSH_INT(res); + return true; +fail: + return false; +} + +bool +jit_compile_op_i32_shift(JitCompContext *cc, IntShift shift_op) +{ + return compile_op_int_shift(cc, shift_op, true); +} + +bool +jit_compile_op_i64_shift(JitCompContext *cc, IntShift shift_op) +{ + return compile_op_int_shift(cc, shift_op, false); +} + +static float32 +negf(float32 f32) +{ + return -f32; +} + +static float64 +neg(float64 f64) +{ + return -f64; +} + +static bool +compile_op_float_math(JitCompContext *cc, FloatMath math_op, bool is_f32) +{ + JitReg value, res; + void *func = NULL; + + if (is_f32) + res = jit_cc_new_reg_F32(cc); + else + res = jit_cc_new_reg_F64(cc); + + if (is_f32) + POP_F32(value); + else + POP_F64(value); + + switch (math_op) { + case FLOAT_ABS: + /* TODO: andps 0x7fffffffffffffff */ + func = is_f32 ? (void *)fabsf : (void *)fabs; + break; + case FLOAT_NEG: + /* TODO: xorps 0x8000000000000000 */ + func = is_f32 ? (void *)negf : (void *)neg; + break; + case FLOAT_CEIL: + func = is_f32 ? (void *)ceilf : (void *)ceil; + break; + case FLOAT_FLOOR: + func = is_f32 ? (void *)floorf : (void *)floor; + break; + case FLOAT_TRUNC: + func = is_f32 ? (void *)truncf : (void *)trunc; + break; + case FLOAT_NEAREST: + func = is_f32 ? (void *)rintf : (void *)rint; + break; + case FLOAT_SQRT: + func = is_f32 ? (void *)sqrtf : (void *)sqrt; + break; + default: + bh_assert(0); + goto fail; + } + + if (!jit_emit_callnative(cc, func, res, &value, 1)) { + goto fail; + } + + if (is_f32) + PUSH_F32(res); + else + PUSH_F64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_math(JitCompContext *cc, FloatMath math_op) +{ + return compile_op_float_math(cc, math_op, true); +} + +bool +jit_compile_op_f64_math(JitCompContext *cc, FloatMath math_op) +{ + return compile_op_float_math(cc, math_op, false); +} + +static float32 +f32_min(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +static float32 +f32_max(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +static float64 +f64_min(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +static float64 +f64_max(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +static bool +compile_op_float_min_max(JitCompContext *cc, FloatArithmetic arith_op, + bool is_f32, JitReg lhs, JitReg rhs, JitReg *out) +{ + JitReg res, args[2]; + void *func; + + res = is_f32 ? jit_cc_new_reg_F32(cc) : jit_cc_new_reg_F64(cc); + if (arith_op == FLOAT_MIN) + func = is_f32 ? (void *)f32_min : (void *)f64_min; + else + func = is_f32 ? (void *)f32_max : (void *)f64_max; + + args[0] = lhs; + args[1] = rhs; + if (!jit_emit_callnative(cc, func, res, args, 2)) + return false; + + *out = res; + return true; +} + +static bool +compile_op_float_arithmetic(JitCompContext *cc, FloatArithmetic arith_op, + bool is_f32) +{ + JitReg lhs, rhs, res; + + if (is_f32) { + POP_F32(rhs); + POP_F32(lhs); + res = jit_cc_new_reg_F32(cc); + } + else { + POP_F64(rhs); + POP_F64(lhs); + res = jit_cc_new_reg_F64(cc); + } + + switch (arith_op) { + case FLOAT_ADD: + { + GEN_INSN(ADD, res, lhs, rhs); + break; + } + case FLOAT_SUB: + { + GEN_INSN(SUB, res, lhs, rhs); + break; + } + case FLOAT_MUL: + { + GEN_INSN(MUL, res, lhs, rhs); + break; + } + case FLOAT_DIV: + { + GEN_INSN(DIV_S, res, lhs, rhs); + break; + } + case FLOAT_MIN: + case FLOAT_MAX: + { + if (!compile_op_float_min_max(cc, arith_op, is_f32, lhs, rhs, &res)) + goto fail; + break; + } + default: + { + bh_assert(0); + goto fail; + } + } + + if (is_f32) + PUSH_F32(res); + else + PUSH_F64(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f32_arithmetic(JitCompContext *cc, FloatArithmetic arith_op) +{ + return compile_op_float_arithmetic(cc, arith_op, true); +} + +bool +jit_compile_op_f64_arithmetic(JitCompContext *cc, FloatArithmetic arith_op) +{ + return compile_op_float_arithmetic(cc, arith_op, false); +} + +bool +jit_compile_op_f32_copysign(JitCompContext *cc) +{ + JitReg res; + JitReg args[2] = { 0 }; + + POP_F32(args[1]); + POP_F32(args[0]); + + res = jit_cc_new_reg_F32(cc); + if (!jit_emit_callnative(cc, copysignf, res, args, 2)) + goto fail; + + PUSH_F32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_f64_copysign(JitCompContext *cc) +{ + JitReg res; + JitReg args[2] = { 0 }; + + POP_F64(args[1]); + POP_F64(args[0]); + + res = jit_cc_new_reg_F64(cc); + if (!jit_emit_callnative(cc, copysign, res, args, 2)) + goto fail; + + PUSH_F64(res); + + return true; +fail: + return false; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_numberic.h b/core/iwasm/fast-jit/fe/jit_emit_numberic.h new file mode 100644 index 0000000000..e73c3ebad5 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_numberic.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_NUMBERIC_H_ +#define _JIT_EMIT_NUMBERIC_H_ + +#include "../jit_compiler.h" +#include "../jit_frontend.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_i32_clz(JitCompContext *cc); + +bool +jit_compile_op_i32_ctz(JitCompContext *cc); + +bool +jit_compile_op_i32_popcnt(JitCompContext *cc); + +bool +jit_compile_op_i64_clz(JitCompContext *cc); + +bool +jit_compile_op_i64_ctz(JitCompContext *cc); + +bool +jit_compile_op_i64_popcnt(JitCompContext *cc); + +bool +jit_compile_op_i32_arithmetic(JitCompContext *cc, IntArithmetic arith_op, + uint8 **p_frame_ip); + +bool +jit_compile_op_i64_arithmetic(JitCompContext *cc, IntArithmetic arith_op, + uint8 **p_frame_ip); + +bool +jit_compile_op_i32_bitwise(JitCompContext *cc, IntBitwise bitwise_op); + +bool +jit_compile_op_i64_bitwise(JitCompContext *cc, IntBitwise bitwise_op); + +bool +jit_compile_op_i32_shift(JitCompContext *cc, IntShift shift_op); + +bool +jit_compile_op_i64_shift(JitCompContext *cc, IntShift shift_op); + +bool +jit_compile_op_f32_math(JitCompContext *cc, FloatMath math_op); + +bool +jit_compile_op_f64_math(JitCompContext *cc, FloatMath math_op); + +bool +jit_compile_op_f32_arithmetic(JitCompContext *cc, FloatArithmetic arith_op); + +bool +jit_compile_op_f64_arithmetic(JitCompContext *cc, FloatArithmetic arith_op); + +bool +jit_compile_op_f32_copysign(JitCompContext *cc); + +bool +jit_compile_op_f64_copysign(JitCompContext *cc); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_NUMBERIC_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_parametric.c b/core/iwasm/fast-jit/fe/jit_emit_parametric.c new file mode 100644 index 0000000000..df0b23a7ac --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_parametric.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_parametric.h" +#include "../jit_frontend.h" + +static bool +pop_value_from_wasm_stack(JitCompContext *cc, bool is_32bit, JitReg *p_value, + uint8 *p_type) +{ + JitValue *jit_value; + JitReg value; + uint8 type; + + if (!jit_block_stack_top(&cc->block_stack)) { + jit_set_last_error(cc, "WASM block stack underflow."); + return false; + } + if (!jit_block_stack_top(&cc->block_stack)->value_stack.value_list_end) { + jit_set_last_error(cc, "WASM data stack underflow."); + return false; + } + + jit_value = jit_value_stack_pop( + &jit_block_stack_top(&cc->block_stack)->value_stack); + type = jit_value->type; + + if (p_type != NULL) { + *p_type = jit_value->type; + } + + wasm_runtime_free(jit_value); + + /* is_32: i32, f32, ref.func, ref.extern, v128 */ + if (is_32bit + && !(type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 +#if WASM_ENABLE_REF_TYPES != 0 + || type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF +#endif + || type == VALUE_TYPE_V128)) { + jit_set_last_error(cc, "invalid WASM stack data type."); + return false; + } + /* !is_32: i64, f64 */ + if (!is_32bit && !(type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64)) { + jit_set_last_error(cc, "invalid WASM stack data type."); + return false; + } + + switch (type) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + value = pop_i32(cc->jit_frame); + break; + case VALUE_TYPE_I64: + value = pop_i64(cc->jit_frame); + break; + case VALUE_TYPE_F32: + value = pop_f32(cc->jit_frame); + break; + case VALUE_TYPE_F64: + value = pop_f64(cc->jit_frame); + break; + default: + bh_assert(0); + return false; + } + + if (p_value != NULL) { + *p_value = value; + } + return true; +} + +bool +jit_compile_op_drop(JitCompContext *cc, bool is_drop_32) +{ + if (!pop_value_from_wasm_stack(cc, is_drop_32, NULL, NULL)) + return false; + return true; +} + +bool +jit_compile_op_select(JitCompContext *cc, bool is_select_32) +{ + JitReg val1, val2, cond, selected; + uint8 val1_type, val2_type; + + POP_I32(cond); + + if (!pop_value_from_wasm_stack(cc, is_select_32, &val2, &val2_type) + || !pop_value_from_wasm_stack(cc, is_select_32, &val1, &val1_type)) { + return false; + } + + if (val1_type != val2_type) { + jit_set_last_error(cc, "invalid stack values with different type"); + return false; + } + + switch (val1_type) { + case VALUE_TYPE_I32: + selected = jit_cc_new_reg_I32(cc); + break; + case VALUE_TYPE_I64: + selected = jit_cc_new_reg_I64(cc); + break; + case VALUE_TYPE_F32: + selected = jit_cc_new_reg_F32(cc); + break; + case VALUE_TYPE_F64: + selected = jit_cc_new_reg_F64(cc); + break; + default: + bh_assert(0); + return false; + } + + GEN_INSN(CMP, cc->cmp_reg, cond, NEW_CONST(I32, 0)); + GEN_INSN(SELECTNE, selected, cc->cmp_reg, val1, val2); + PUSH(selected, val1_type); + return true; +fail: + return false; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_parametric.h b/core/iwasm/fast-jit/fe/jit_emit_parametric.h new file mode 100644 index 0000000000..40025ed214 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_parametric.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_PARAMETRIC_H_ +#define _JIT_EMIT_PARAMETRIC_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_drop(JitCompContext *cc, bool is_drop_32); + +bool +jit_compile_op_select(JitCompContext *cc, bool is_select_32); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_PARAMETRIC_H_ */ diff --git a/core/iwasm/fast-jit/fe/jit_emit_table.c b/core/iwasm/fast-jit/fe/jit_emit_table.c new file mode 100644 index 0000000000..efdf5cf145 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_table.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_table.h" +#include "jit_emit_exception.h" +#include "jit_emit_function.h" +#include "../../interpreter/wasm_runtime.h" +#include "../jit_frontend.h" + +#if WASM_ENABLE_REF_TYPES != 0 +static void +wasm_elem_drop(WASMModuleInstance *inst, uint32 tbl_seg_idx) +{ + bh_bitmap_set_bit(inst->e->common.elem_dropped, tbl_seg_idx); +} + +bool +jit_compile_op_elem_drop(JitCompContext *cc, uint32 tbl_seg_idx) +{ + JitReg args[2] = { 0 }; + + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, tbl_seg_idx); + + return jit_emit_callnative(cc, wasm_elem_drop, 0, args, + sizeof(args) / sizeof(args[0])); +} + +bool +jit_compile_op_table_get(JitCompContext *cc, uint32 tbl_idx) +{ + JitReg elem_idx, tbl_sz, tbl_elems, elem_idx_long, offset, res; + + POP_I32(elem_idx); + + /* if (elem_idx >= tbl_sz) goto exception; */ + tbl_sz = get_table_cur_size_reg(cc->jit_frame, tbl_idx); + GEN_INSN(CMP, cc->cmp_reg, elem_idx, tbl_sz); + if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, JIT_OP_BGEU, + cc->cmp_reg, NULL)) + goto fail; + + elem_idx_long = jit_cc_new_reg_I64(cc); + GEN_INSN(I32TOI64, elem_idx_long, elem_idx); + + offset = jit_cc_new_reg_I64(cc); + GEN_INSN(MUL, offset, elem_idx_long, + NEW_CONST(I64, sizeof(table_elem_type_t))); + + res = jit_cc_new_reg_I32(cc); + tbl_elems = get_table_elems_reg(cc->jit_frame, tbl_idx); + GEN_INSN(LDI32, res, tbl_elems, offset); + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_table_set(JitCompContext *cc, uint32 tbl_idx) +{ + JitReg elem_idx, elem_val, tbl_sz, tbl_elems, elem_idx_long, offset; + + POP_I32(elem_val); + POP_I32(elem_idx); + + /* if (elem_idx >= tbl_sz) goto exception; */ + tbl_sz = get_table_cur_size_reg(cc->jit_frame, tbl_idx); + GEN_INSN(CMP, cc->cmp_reg, elem_idx, tbl_sz); + if (!jit_emit_exception(cc, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, JIT_OP_BGEU, + cc->cmp_reg, NULL)) + goto fail; + + elem_idx_long = jit_cc_new_reg_I64(cc); + GEN_INSN(I32TOI64, elem_idx_long, elem_idx); + + offset = jit_cc_new_reg_I64(cc); + GEN_INSN(MUL, offset, elem_idx_long, + NEW_CONST(I64, sizeof(table_elem_type_t))); + + tbl_elems = get_table_elems_reg(cc->jit_frame, tbl_idx); + GEN_INSN(STI32, elem_val, tbl_elems, offset); + + return true; +fail: + return false; +} + +static int +wasm_init_table(WASMModuleInstance *inst, uint32 tbl_idx, uint32 seg_idx, + uint32 dst_offset, uint32 len, uint32 src_offset) +{ + WASMTableInstance *tbl; + WASMTableSeg *tbl_seg = inst->module->table_segments + seg_idx; + InitializerExpression *tbl_seg_init_values = NULL, *init_values; + uint32 tbl_sz, tbl_seg_len = 0, i; + table_elem_type_t *addr; + + if (!bh_bitmap_get_bit(inst->e->common.elem_dropped, seg_idx)) { + /* table segment isn't dropped */ + tbl_seg_init_values = tbl_seg->init_values; + tbl_seg_len = tbl_seg->value_count; + } + + if (offset_len_out_of_bounds(src_offset, len, tbl_seg_len)) + goto out_of_bounds; + + tbl = inst->tables[tbl_idx]; + tbl_sz = tbl->cur_size; + if (offset_len_out_of_bounds(dst_offset, len, tbl_sz)) + goto out_of_bounds; + + if (!len) + return 0; + + addr = + (table_elem_type_t *)((uint8 *)tbl + offsetof(WASMTableInstance, elems) + + dst_offset * sizeof(table_elem_type_t)); + init_values = tbl_seg_init_values + src_offset; + for (i = 0; i < len; i++) { + addr[i] = + (table_elem_type_t)(uintptr_t)init_values[+i].u.unary.v.ref_index; + } + + return 0; +out_of_bounds: + wasm_set_exception(inst, "out of bounds table access"); + return -1; +} + +bool +jit_compile_op_table_init(JitCompContext *cc, uint32 tbl_idx, + uint32 tbl_seg_idx) +{ + JitReg len, src, dst, res; + JitReg args[6] = { 0 }; + + POP_I32(len); + POP_I32(src); + POP_I32(dst); + + res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, tbl_idx); + args[2] = NEW_CONST(I32, tbl_seg_idx); + args[3] = dst; + args[4] = len; + args[5] = src; + + if (!jit_emit_callnative(cc, wasm_init_table, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + 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; + + return true; +fail: + return false; +} + +static int +wasm_copy_table(WASMModuleInstance *inst, uint32 src_tbl_idx, + uint32 dst_tbl_idx, uint32 dst_offset, uint32 len, + uint32 src_offset) +{ + WASMTableInstance *src_tbl, *dst_tbl; + uint32 src_tbl_sz, dst_tbl_sz; + + dst_tbl = inst->tables[dst_tbl_idx]; + dst_tbl_sz = dst_tbl->cur_size; + if (offset_len_out_of_bounds(dst_offset, len, dst_tbl_sz)) + goto out_of_bounds; + + src_tbl = inst->tables[src_tbl_idx]; + src_tbl_sz = src_tbl->cur_size; + if (offset_len_out_of_bounds(src_offset, len, src_tbl_sz)) + goto out_of_bounds; + + bh_memmove_s( + (uint8 *)dst_tbl + offsetof(WASMTableInstance, elems) + + dst_offset * sizeof(table_elem_type_t), + (uint32)((dst_tbl_sz - dst_offset) * sizeof(table_elem_type_t)), + (uint8 *)src_tbl + offsetof(WASMTableInstance, elems) + + src_offset * sizeof(table_elem_type_t), + (uint32)(len * sizeof(table_elem_type_t))); + + return 0; +out_of_bounds: + wasm_set_exception(inst, "out of bounds table access"); + return -1; +} + +bool +jit_compile_op_table_copy(JitCompContext *cc, uint32 src_tbl_idx, + uint32 dst_tbl_idx) +{ + JitReg len, src, dst, res; + JitReg args[6] = { 0 }; + + POP_I32(len); + POP_I32(src); + POP_I32(dst); + + res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, src_tbl_idx); + args[2] = NEW_CONST(I32, dst_tbl_idx); + args[3] = dst; + args[4] = len; + args[5] = src; + + if (!jit_emit_callnative(cc, wasm_copy_table, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + 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; + + return true; +fail: + return false; +} + +bool +jit_compile_op_table_size(JitCompContext *cc, uint32 tbl_idx) +{ + JitReg res; + + res = get_table_cur_size_reg(cc->jit_frame, tbl_idx); + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +jit_compile_op_table_grow(JitCompContext *cc, uint32 tbl_idx) +{ + JitReg tbl_sz, n, val, enlarge_ret, res; + JitReg args[4] = { 0 }; + + POP_I32(n); + POP_I32(val); + + tbl_sz = get_table_cur_size_reg(cc->jit_frame, tbl_idx); + + enlarge_ret = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, tbl_idx); + args[2] = n; + args[3] = val; + + if (!jit_emit_callnative(cc, wasm_enlarge_table, enlarge_ret, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + /* Convert bool to uint32 */ + GEN_INSN(AND, enlarge_ret, enlarge_ret, NEW_CONST(I32, 0xFF)); + + res = jit_cc_new_reg_I32(cc); + GEN_INSN(CMP, cc->cmp_reg, enlarge_ret, NEW_CONST(I32, 1)); + GEN_INSN(SELECTEQ, res, cc->cmp_reg, tbl_sz, NEW_CONST(I32, -1)); + PUSH_I32(res); + + /* Ensure a refresh in next get memory related registers */ + clear_table_regs(cc->jit_frame); + return true; +fail: + return false; +} + +static int +wasm_fill_table(WASMModuleInstance *inst, uint32 tbl_idx, uint32 dst_offset, + uintptr_t val, uint32 len) +{ + WASMTableInstance *tbl; + uint32 tbl_sz; + + tbl = inst->tables[tbl_idx]; + tbl_sz = tbl->cur_size; + + if (offset_len_out_of_bounds(dst_offset, len, tbl_sz)) + goto out_of_bounds; + + for (; len != 0; dst_offset++, len--) { + tbl->elems[dst_offset] = val; + } + + return 0; +out_of_bounds: + wasm_set_exception(inst, "out of bounds table access"); + return -1; +} + +bool +jit_compile_op_table_fill(JitCompContext *cc, uint32 tbl_idx) +{ + JitReg len, val, dst, res; + JitReg args[5] = { 0 }; + + POP_I32(len); + POP_I32(val); + POP_I32(dst); + + res = jit_cc_new_reg_I32(cc); + args[0] = get_module_inst_reg(cc->jit_frame); + args[1] = NEW_CONST(I32, tbl_idx); + args[2] = dst; + args[3] = val; + args[4] = len; + + if (!jit_emit_callnative(cc, wasm_fill_table, res, args, + sizeof(args) / sizeof(args[0]))) + goto fail; + + 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; + + return true; +fail: + return false; +} +#endif diff --git a/core/iwasm/fast-jit/fe/jit_emit_table.h b/core/iwasm/fast-jit/fe/jit_emit_table.h new file mode 100644 index 0000000000..acfb655f2d --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_table.h @@ -0,0 +1,47 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_TABLE_H_ +#define _JIT_EMIT_TABLE_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if WASM_ENABLE_REF_TYPES != 0 +bool +jit_compile_op_elem_drop(JitCompContext *cc, uint32 tbl_seg_idx); + +bool +jit_compile_op_table_get(JitCompContext *cc, uint32 tbl_idx); + +bool +jit_compile_op_table_set(JitCompContext *cc, uint32 tbl_idx); + +bool +jit_compile_op_table_init(JitCompContext *cc, uint32 tbl_idx, + uint32 tbl_seg_idx); + +bool +jit_compile_op_table_copy(JitCompContext *cc, uint32 src_tbl_idx, + uint32 dst_tbl_idx); + +bool +jit_compile_op_table_size(JitCompContext *cc, uint32 tbl_idx); + +bool +jit_compile_op_table_grow(JitCompContext *cc, uint32 tbl_idx); + +bool +jit_compile_op_table_fill(JitCompContext *cc, uint32 tbl_idx); +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif +#endif diff --git a/core/iwasm/fast-jit/fe/jit_emit_variable.c b/core/iwasm/fast-jit/fe/jit_emit_variable.c new file mode 100644 index 0000000000..72f040a31b --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_variable.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_emit_variable.h" +#include "jit_emit_exception.h" +#include "../jit_frontend.h" + +#define CHECK_LOCAL(idx) \ + do { \ + if (idx \ + >= wasm_func->func_type->param_count + wasm_func->local_count) { \ + jit_set_last_error(cc, "local index out of range"); \ + goto fail; \ + } \ + } while (0) + +static uint8 +get_local_type(const WASMFunction *wasm_func, uint32 local_idx) +{ + uint32 param_count = wasm_func->func_type->param_count; + return local_idx < param_count + ? wasm_func->func_type->types[local_idx] + : wasm_func->local_types[local_idx - param_count]; +} + +bool +jit_compile_op_get_local(JitCompContext *cc, uint32 local_idx) +{ + WASMFunction *wasm_func = cc->cur_wasm_func; + uint16 *local_offsets = wasm_func->local_offsets; + uint16 local_offset; + uint8 local_type; + JitReg value = 0; + + CHECK_LOCAL(local_idx); + + local_offset = local_offsets[local_idx]; + local_type = get_local_type(wasm_func, local_idx); + + switch (local_type) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + value = local_i32(cc->jit_frame, local_offset); + + break; + case VALUE_TYPE_I64: + value = local_i64(cc->jit_frame, local_offset); + break; + case VALUE_TYPE_F32: + value = local_f32(cc->jit_frame, local_offset); + break; + case VALUE_TYPE_F64: + value = local_f64(cc->jit_frame, local_offset); + break; + default: + bh_assert(0); + break; + } + + PUSH(value, local_type); + return true; +fail: + return false; +} + +bool +jit_compile_op_set_local(JitCompContext *cc, uint32 local_idx) +{ + WASMFunction *wasm_func = cc->cur_wasm_func; + uint16 *local_offsets = wasm_func->local_offsets; + uint16 local_offset; + uint8 local_type; + JitReg value; + + CHECK_LOCAL(local_idx); + + local_offset = local_offsets[local_idx]; + local_type = get_local_type(wasm_func, local_idx); + + switch (local_type) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + POP_I32(value); + set_local_i32(cc->jit_frame, local_offset, value); + break; + case VALUE_TYPE_I64: + POP_I64(value); + set_local_i64(cc->jit_frame, local_offset, value); + break; + case VALUE_TYPE_F32: + POP_F32(value); + set_local_f32(cc->jit_frame, local_offset, value); + break; + case VALUE_TYPE_F64: + POP_F64(value); + set_local_f64(cc->jit_frame, local_offset, value); + break; + default: + bh_assert(0); + break; + } + + return true; +fail: + return false; +} + +bool +jit_compile_op_tee_local(JitCompContext *cc, uint32 local_idx) +{ + WASMFunction *wasm_func = cc->cur_wasm_func; + uint16 *local_offsets = wasm_func->local_offsets; + uint16 local_offset; + uint8 local_type; + JitReg value = 0; + + CHECK_LOCAL(local_idx); + + local_offset = local_offsets[local_idx]; + local_type = get_local_type(wasm_func, local_idx); + + switch (local_type) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + POP_I32(value); + set_local_i32(cc->jit_frame, local_offset, value); + PUSH_I32(value); + break; + case VALUE_TYPE_I64: + POP_I64(value); + set_local_i64(cc->jit_frame, local_offset, value); + PUSH_I64(value); + break; + case VALUE_TYPE_F32: + POP_F32(value); + set_local_f32(cc->jit_frame, local_offset, value); + PUSH_F32(value); + break; + case VALUE_TYPE_F64: + POP_F64(value); + set_local_f64(cc->jit_frame, local_offset, value); + PUSH_F64(value); + break; + default: + bh_assert(0); + goto fail; + } + + return true; +fail: + return false; +} + +static uint8 +get_global_type(const WASMModule *module, uint32 global_idx) +{ + if (global_idx < module->import_global_count) { + const WASMGlobalImport *import_global = + &((module->import_globals + global_idx)->u.global); + return import_global->type.val_type; + } + else { + const WASMGlobal *global = + module->globals + (global_idx - module->import_global_count); + return global->type.val_type; + } +} + +bool +jit_compile_op_get_global(JitCompContext *cc, uint32 global_idx) +{ + uint32 data_offset; + uint8 global_type = 0; + JitReg value = 0; + + bh_assert(global_idx < cc->cur_wasm_module->import_global_count + + cc->cur_wasm_module->global_count); + + data_offset = + jit_frontend_get_global_data_offset(cc->cur_wasm_module, global_idx); + global_type = get_global_type(cc->cur_wasm_module, global_idx); + + switch (global_type) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + { + value = jit_cc_new_reg_I32(cc); + GEN_INSN(LDI32, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + case VALUE_TYPE_I64: + { + value = jit_cc_new_reg_I64(cc); + GEN_INSN(LDI64, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + case VALUE_TYPE_F32: + { + value = jit_cc_new_reg_F32(cc); + GEN_INSN(LDF32, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + case VALUE_TYPE_F64: + { + value = jit_cc_new_reg_F64(cc); + GEN_INSN(LDF64, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + default: + { + jit_set_last_error(cc, "unexpected global type"); + goto fail; + } + } + + PUSH(value, global_type); + + return true; +fail: + return false; +} + +bool +jit_compile_op_set_global(JitCompContext *cc, uint32 global_idx, + bool is_aux_stack) +{ + uint32 data_offset; + uint8 global_type = 0; + JitReg value = 0; + + bh_assert(global_idx < cc->cur_wasm_module->import_global_count + + cc->cur_wasm_module->global_count); + + data_offset = + jit_frontend_get_global_data_offset(cc->cur_wasm_module, global_idx); + global_type = get_global_type(cc->cur_wasm_module, global_idx); + + switch (global_type) { + case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: +#endif + { + POP_I32(value); + if (is_aux_stack) { + JitReg aux_stack_bound = get_aux_stack_bound_reg(cc->jit_frame); + JitReg aux_stack_bottom = + get_aux_stack_bottom_reg(cc->jit_frame); + GEN_INSN(CMP, cc->cmp_reg, value, aux_stack_bound); + if (!(jit_emit_exception(cc, EXCE_AUX_STACK_OVERFLOW, + JIT_OP_BLEU, cc->cmp_reg, NULL))) + goto fail; + GEN_INSN(CMP, cc->cmp_reg, value, aux_stack_bottom); + if (!(jit_emit_exception(cc, EXCE_AUX_STACK_UNDERFLOW, + JIT_OP_BGTU, cc->cmp_reg, NULL))) + goto fail; + } + GEN_INSN(STI32, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + case VALUE_TYPE_I64: + { + POP_I64(value); + GEN_INSN(STI64, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + case VALUE_TYPE_F32: + { + POP_F32(value); + GEN_INSN(STF32, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + case VALUE_TYPE_F64: + { + POP_F64(value); + GEN_INSN(STF64, value, get_module_inst_reg(cc->jit_frame), + NEW_CONST(I32, data_offset)); + break; + } + default: + { + jit_set_last_error(cc, "unexpected global type"); + goto fail; + } + } + + return true; +fail: + return false; +} diff --git a/core/iwasm/fast-jit/fe/jit_emit_variable.h b/core/iwasm/fast-jit/fe/jit_emit_variable.h new file mode 100644 index 0000000000..80a10511d8 --- /dev/null +++ b/core/iwasm/fast-jit/fe/jit_emit_variable.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_EMIT_VARIABLE_H_ +#define _JIT_EMIT_VARIABLE_H_ + +#include "../jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_compile_op_get_local(JitCompContext *cc, uint32 local_idx); + +bool +jit_compile_op_set_local(JitCompContext *cc, uint32 local_idx); + +bool +jit_compile_op_tee_local(JitCompContext *cc, uint32 local_idx); + +bool +jit_compile_op_get_global(JitCompContext *cc, uint32 global_idx); + +bool +jit_compile_op_set_global(JitCompContext *cc, uint32 global_idx, + bool is_aux_stack); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _JIT_EMIT_VARIABLE_H_ */ diff --git a/core/iwasm/fast-jit/iwasm_fast_jit.cmake b/core/iwasm/fast-jit/iwasm_fast_jit.cmake new file mode 100644 index 0000000000..619866598d --- /dev/null +++ b/core/iwasm/fast-jit/iwasm_fast_jit.cmake @@ -0,0 +1,104 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +set (IWASM_FAST_JIT_DIR ${CMAKE_CURRENT_LIST_DIR}) +add_definitions(-DWASM_ENABLE_FAST_JIT=1) +if (WAMR_BUILD_FAST_JIT_DUMP EQUAL 1) + add_definitions(-DWASM_ENABLE_FAST_JIT_DUMP=1) +endif () + +include_directories (${IWASM_FAST_JIT_DIR}) +enable_language(CXX) + +if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + include(FetchContent) + if (NOT WAMR_BUILD_PLATFORM STREQUAL "linux-sgx") + FetchContent_Declare( + asmjit + GIT_REPOSITORY https://github.com/asmjit/asmjit.git + GIT_TAG c1019f1642a588107148f64ba54584b0ae3ec8d1 + ) + else () + FetchContent_Declare( + asmjit + GIT_REPOSITORY https://github.com/asmjit/asmjit.git + GIT_TAG c1019f1642a588107148f64ba54584b0ae3ec8d1 + PATCH_COMMAND git apply ${IWASM_FAST_JIT_DIR}/asmjit_sgx_patch.diff + ) + endif () + FetchContent_GetProperties(asmjit) + if (NOT asmjit_POPULATED) + message ("-- Fetching asmjit ..") + FetchContent_Populate(asmjit) + add_definitions(-DASMJIT_STATIC) + add_definitions(-DASMJIT_NO_DEPRECATED) + add_definitions(-DASMJIT_NO_BUILDER) + add_definitions(-DASMJIT_NO_COMPILER) + add_definitions(-DASMJIT_NO_JIT) + add_definitions(-DASMJIT_NO_LOGGING) + add_definitions(-DASMJIT_NO_TEXT) + add_definitions(-DASMJIT_NO_VALIDATION) + add_definitions(-DASMJIT_NO_INTROSPECTION) + add_definitions(-DASMJIT_NO_INTRINSICS) + add_definitions(-DASMJIT_NO_AARCH64) + add_definitions(-DASMJIT_NO_AARCH32) + include_directories(SYSTEM "${asmjit_SOURCE_DIR}/src") + add_subdirectory(${asmjit_SOURCE_DIR} ${asmjit_BINARY_DIR} EXCLUDE_FROM_ALL) + file (GLOB_RECURSE cpp_source_asmjit + ${asmjit_SOURCE_DIR}/src/asmjit/core/*.cpp + ${asmjit_SOURCE_DIR}/src/asmjit/x86/*.cpp + ) + endif () + if (WAMR_BUILD_FAST_JIT_DUMP EQUAL 1) + FetchContent_Declare( + zycore + GIT_REPOSITORY https://github.com/zyantific/zycore-c.git + ) + FetchContent_GetProperties(zycore) + if (NOT zycore_POPULATED) + message ("-- Fetching zycore ..") + FetchContent_Populate(zycore) + option(ZYDIS_BUILD_TOOLS "" OFF) + option(ZYDIS_BUILD_EXAMPLES "" OFF) + include_directories(SYSTEM "${zycore_SOURCE_DIR}/include") + include_directories(SYSTEM "${zycore_BINARY_DIR}") + add_subdirectory(${zycore_SOURCE_DIR} ${zycore_BINARY_DIR} EXCLUDE_FROM_ALL) + file (GLOB_RECURSE c_source_zycore ${zycore_SOURCE_DIR}/src/*.c) + endif () + FetchContent_Declare( + zydis + GIT_REPOSITORY https://github.com/zyantific/zydis.git + GIT_TAG e14a07895136182a5b53e181eec3b1c6e0b434de + ) + FetchContent_GetProperties(zydis) + if (NOT zydis_POPULATED) + message ("-- Fetching zydis ..") + FetchContent_Populate(zydis) + option(ZYDIS_BUILD_TOOLS "" OFF) + option(ZYDIS_BUILD_EXAMPLES "" OFF) + include_directories(SYSTEM "${zydis_BINARY_DIR}") + include_directories(SYSTEM "${zydis_SOURCE_DIR}/include") + include_directories(SYSTEM "${zydis_SOURCE_DIR}/src") + add_subdirectory(${zydis_SOURCE_DIR} ${zydis_BINARY_DIR} EXCLUDE_FROM_ALL) + file (GLOB_RECURSE c_source_zydis ${zydis_SOURCE_DIR}/src/*.c) + endif () + endif () +endif () + +file (GLOB c_source_jit ${IWASM_FAST_JIT_DIR}/*.c ${IWASM_FAST_JIT_DIR}/fe/*.c) + +if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + file (GLOB_RECURSE cpp_source_jit_cg ${IWASM_FAST_JIT_DIR}/cg/x86-64/*.cpp) +else () + message (FATAL_ERROR "Fast JIT codegen for target ${WAMR_BUILD_TARGET} isn't implemented") +endif () + +set (IWASM_FAST_JIT_SOURCE ${c_source_jit} ${cpp_source_jit_cg} + ${cpp_source_asmjit} ${c_source_zycore} ${c_source_zydis}) diff --git a/core/iwasm/fast-jit/jit_codecache.c b/core/iwasm/fast-jit/jit_codecache.c new file mode 100644 index 0000000000..ef20747478 --- /dev/null +++ b/core/iwasm/fast-jit/jit_codecache.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_codecache.h" +#include "mem_alloc.h" +#include "jit_compiler.h" + +static void *code_cache_pool = NULL; +static uint32 code_cache_pool_size = 0; +static mem_allocator_t code_cache_pool_allocator = NULL; + +bool +jit_code_cache_init(uint32 code_cache_size) +{ + int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; + int map_flags = MMAP_MAP_NONE; + + if (!(code_cache_pool = os_mmap(NULL, code_cache_size, map_prot, map_flags, + os_get_invalid_handle()))) { + return false; + } + + if (!(code_cache_pool_allocator = + mem_allocator_create(code_cache_pool, code_cache_size))) { + os_munmap(code_cache_pool, code_cache_size); + code_cache_pool = NULL; + return false; + } + + code_cache_pool_size = code_cache_size; + return true; +} + +void +jit_code_cache_destroy() +{ + mem_allocator_destroy(code_cache_pool_allocator); + os_munmap(code_cache_pool, code_cache_pool_size); +} + +void * +jit_code_cache_alloc(uint32 size) +{ + return mem_allocator_malloc(code_cache_pool_allocator, size); +} + +void +jit_code_cache_free(void *ptr) +{ + if (ptr) + mem_allocator_free(code_cache_pool_allocator, ptr); +} + +bool +jit_pass_register_jitted_code(JitCompContext *cc) +{ + 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_codecache.h b/core/iwasm/fast-jit/jit_codecache.h new file mode 100644 index 0000000000..953026ad46 --- /dev/null +++ b/core/iwasm/fast-jit/jit_codecache.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_CODE_CACHE_H_ +#define _JIT_CODE_CACHE_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +jit_code_cache_init(uint32 code_cache_size); + +void +jit_code_cache_destroy(); + +void * +jit_code_cache_alloc(uint32 size); + +void +jit_code_cache_free(void *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _JIT_CODE_CACHE_H_ */ diff --git a/core/iwasm/fast-jit/jit_codegen.c b/core/iwasm/fast-jit/jit_codegen.c new file mode 100644 index 0000000000..2bd60bb413 --- /dev/null +++ b/core/iwasm/fast-jit/jit_codegen.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_compiler.h" +#include "jit_codegen.h" + +bool +jit_pass_lower_cg(JitCompContext *cc) +{ + return jit_codegen_lower(cc); +} + +bool +jit_pass_codegen(JitCompContext *cc) +{ + if (!jit_annl_enable_jitted_addr(cc)) + return false; + + return jit_codegen_gen_native(cc); +} diff --git a/core/iwasm/fast-jit/jit_codegen.h b/core/iwasm/fast-jit/jit_codegen.h new file mode 100644 index 0000000000..735cddab62 --- /dev/null +++ b/core/iwasm/fast-jit/jit_codegen.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_CODEGEN_H_ +#define _JIT_CODEGEN_H_ + +#include "bh_platform.h" +#include "jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize codegen module, such as instruction encoder. + * + * @return true if succeeded; false if failed. + */ +bool +jit_codegen_init(); + +/** + * Destroy codegen module, such as instruction encoder. + */ +void +jit_codegen_destroy(); + +/** + * Get hard register information of each kind. + * + * @return the JitHardRegInfo array of each kind + */ +const JitHardRegInfo * +jit_codegen_get_hreg_info(); + +/** + * Get hard register by name. + * + * @param name the name of the hard register + * + * @return the hard register of the name + */ +JitReg +jit_codegen_get_hreg_by_name(const char *name); + +/** + * Generate native code for the given compilation context + * + * @param cc the compilation context that is ready to do codegen + * + * @return true if succeeds, false otherwise + */ +bool +jit_codegen_gen_native(JitCompContext *cc); + +/** + * lower unsupported operations to supported ones for the target. + * + * @param cc the compilation context that is ready to do codegen + * + * @return true if succeeds, false otherwise + */ +bool +jit_codegen_lower(JitCompContext *cc); + +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 +void * +jit_codegen_compile_call_to_llvm_jit(const WASMType *func_type); + +void * +jit_codegen_compile_call_to_fast_jit(const WASMModule *module, uint32 func_idx); +#endif + +/** + * Dump native code in the given range to assembly. + * + * @param begin_addr begin address of the native code + * @param end_addr end address of the native code + */ +void +jit_codegen_dump_native(void *begin_addr, void *end_addr); + +int +jit_codegen_interp_jitted_glue(void *self, JitInterpSwitchInfo *info, + uint32 func_idx, void *pc); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _JIT_CODEGEN_H_ */ diff --git a/core/iwasm/fast-jit/jit_compiler.c b/core/iwasm/fast-jit/jit_compiler.c new file mode 100644 index 0000000000..2c686dd33b --- /dev/null +++ b/core/iwasm/fast-jit/jit_compiler.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_compiler.h" +#include "jit_ir.h" +#include "jit_codegen.h" +#include "jit_codecache.h" +#include "../interpreter/wasm.h" + +typedef struct JitCompilerPass { + /* Name of the pass */ + const char *name; + /* The entry of the compiler pass */ + bool (*run)(JitCompContext *cc); +} JitCompilerPass; + +/* clang-format off */ +static JitCompilerPass compiler_passes[] = { + { NULL, NULL }, +#define REG_PASS(name) { #name, jit_pass_##name } + REG_PASS(dump), + REG_PASS(update_cfg), + REG_PASS(frontend), + REG_PASS(lower_cg), + REG_PASS(regalloc), + REG_PASS(codegen), + REG_PASS(register_jitted_code) +#undef REG_PASS +}; + +/* Number of compiler passes */ +#define COMPILER_PASS_NUM (sizeof(compiler_passes) / sizeof(compiler_passes[0])) + +#if WASM_ENABLE_FAST_JIT_DUMP == 0 +static const uint8 compiler_passes_without_dump[] = { + 3, 4, 5, 6, 7, 0 +}; +#else +static const uint8 compiler_passes_with_dump[] = { + 3, 2, 1, 4, 1, 5, 1, 6, 1, 7, 0 +}; +#endif + +/* The exported global data of JIT compiler */ +static JitGlobals jit_globals = { +#if WASM_ENABLE_FAST_JIT_DUMP == 0 + .passes = compiler_passes_without_dump, +#else + .passes = compiler_passes_with_dump, +#endif + .return_to_interp_from_jitted = NULL, +#if WASM_ENABLE_LAZY_JIT != 0 + .compile_fast_jit_and_then_call = NULL, +#endif +}; +/* clang-format on */ + +static bool +apply_compiler_passes(JitCompContext *cc) +{ + const uint8 *p = jit_globals.passes; + + for (; *p; p++) { + /* Set the pass NO */ + cc->cur_pass_no = p - jit_globals.passes; + bh_assert(*p < COMPILER_PASS_NUM); + + if (!compiler_passes[*p].run(cc) || jit_get_last_error(cc)) { + LOG_VERBOSE("JIT: compilation failed at pass[%td] = %s\n", + p - jit_globals.passes, compiler_passes[*p].name); + return false; + } + } + + return true; +} + +bool +jit_compiler_init(const JitCompOptions *options) +{ + uint32 code_cache_size = options->code_cache_size > 0 + ? options->code_cache_size + : FAST_JIT_DEFAULT_CODE_CACHE_SIZE; + + LOG_VERBOSE("JIT: compiler init with code cache size: %u\n", + code_cache_size); + + if (!jit_code_cache_init(code_cache_size)) + return false; + + if (!jit_codegen_init()) + goto fail1; + + return true; + +fail1: + jit_code_cache_destroy(); + return false; +} + +void +jit_compiler_destroy() +{ + jit_codegen_destroy(); + + jit_code_cache_destroy(); +} + +JitGlobals * +jit_compiler_get_jit_globals() +{ + return &jit_globals; +} + +const char * +jit_compiler_get_pass_name(unsigned i) +{ + return i < COMPILER_PASS_NUM ? compiler_passes[i].name : NULL; +} + +bool +jit_compiler_compile(WASMModule *module, uint32 func_idx) +{ + JitCompContext *cc = NULL; + char *last_error; + bool ret = false; + uint32 i = func_idx - module->import_function_count; + uint32 j = i % WASM_ORC_JIT_BACKEND_THREAD_NUM; + + /* Lock to avoid duplicated compilation by other threads */ + os_mutex_lock(&module->fast_jit_thread_locks[j]); + + if (jit_compiler_is_compiled(module, func_idx)) { + /* Function has been compiled */ + os_mutex_unlock(&module->fast_jit_thread_locks[j]); + return true; + } + + /* Initialize the compilation context */ + if (!(cc = jit_calloc(sizeof(*cc)))) { + goto fail; + } + + if (!jit_cc_init(cc, 64)) { + goto fail; + } + + cc->cur_wasm_module = module; + cc->cur_wasm_func = module->functions[i]; + cc->cur_wasm_func_idx = func_idx; + cc->mem_space_unchanged = (!cc->cur_wasm_func->has_op_memory_grow + && !cc->cur_wasm_func->has_op_func_call) + || (!module->possible_memory_grow); + + /* 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; + LOG_ERROR("fast jit compilation failed: %s (function_name=%s)\n", + last_error ? last_error : "unknown error", function_name); +#else + LOG_ERROR("fast jit compilation failed: %s\n", + last_error ? last_error : "unknown error"); +#endif + + goto fail; + } + + ret = true; + +fail: + /* Destroy the compilation context */ + if (cc) + jit_cc_delete(cc); + + os_mutex_unlock(&module->fast_jit_thread_locks[j]); + + return ret; +} + +bool +jit_compiler_compile_all(WASMModule *module) +{ + uint32 i; + + for (i = 0; i < module->function_count; i++) { + if (!jit_compiler_compile(module, module->import_function_count + i)) { + return false; + } + } + + return true; +} + +bool +jit_compiler_is_compiled(const WASMModule *module, uint32 func_idx) +{ + uint32 i = func_idx - module->import_function_count; + + bh_assert(func_idx >= module->import_function_count + && func_idx + < module->import_function_count + module->function_count); + +#if WASM_ENABLE_LAZY_JIT == 0 + return module->fast_jit_func_ptrs[i] ? true : false; +#else + return module->fast_jit_func_ptrs[i] + != jit_globals.compile_fast_jit_and_then_call + ? true + : false; +#endif +} + +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 +bool +jit_compiler_set_call_to_llvm_jit(WASMModule *module, uint32 func_idx) +{ + uint32 i = func_idx - module->import_function_count; + uint32 j = i % WASM_ORC_JIT_BACKEND_THREAD_NUM; + WASMType *func_type = module->functions[i]->func_type; + uint32 k = + ((uint32)(uintptr_t)func_type >> 3) % WASM_ORC_JIT_BACKEND_THREAD_NUM; + void *func_ptr = NULL; + + /* Compile code block of call_to_llvm_jit_from_fast_jit of + this kind of function type if it hasn't been compiled */ + if (!(func_ptr = func_type->call_to_llvm_jit_from_fast_jit)) { + os_mutex_lock(&module->fast_jit_thread_locks[k]); + if (!(func_ptr = func_type->call_to_llvm_jit_from_fast_jit)) { + if (!(func_ptr = func_type->call_to_llvm_jit_from_fast_jit = + jit_codegen_compile_call_to_llvm_jit(func_type))) { + os_mutex_unlock(&module->fast_jit_thread_locks[k]); + return false; + } + } + os_mutex_unlock(&module->fast_jit_thread_locks[k]); + } + + /* Switch current fast jit func ptr to the code block */ + os_mutex_lock(&module->fast_jit_thread_locks[j]); + module->fast_jit_func_ptrs[i] = func_ptr; + os_mutex_unlock(&module->fast_jit_thread_locks[j]); + return true; +} + +bool +jit_compiler_set_call_to_fast_jit(WASMModule *module, uint32 func_idx) +{ + void *func_ptr = NULL; + + 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); + } + + return func_ptr ? true : false; +} + +void +jit_compiler_set_llvm_jit_func_ptr(WASMModule *module, uint32 func_idx, + void *func_ptr) +{ + WASMModuleInstance *instance; + uint32 i = func_idx - module->import_function_count; + + os_mutex_lock(&module->instance_list_lock); + + module->func_ptrs[i] = func_ptr; + + instance = module->instance_list; + while (instance) { + 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); +} +#endif /* end of WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 */ + +int +jit_interp_switch_to_jitted(void *exec_env, JitInterpSwitchInfo *info, + uint32 func_idx, void *pc) +{ + return jit_codegen_interp_jitted_glue(exec_env, info, func_idx, pc); +} diff --git a/core/iwasm/fast-jit/jit_compiler.h b/core/iwasm/fast-jit/jit_compiler.h new file mode 100644 index 0000000000..9a49cffdd1 --- /dev/null +++ b/core/iwasm/fast-jit/jit_compiler.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_COMPILER_H_ +#define _JIT_COMPILER_H_ + +#include "bh_platform.h" +#include "../interpreter/wasm_runtime.h" +#include "jit_ir.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct JitGlobals { + /* Compiler pass sequence, the last element must be 0 */ + const uint8 *passes; + char *return_to_interp_from_jitted; +#if WASM_ENABLE_LAZY_JIT != 0 + char *compile_fast_jit_and_then_call; +#endif +} JitGlobals; + +/** + * Actions the interpreter should do when jitted code returns to + * interpreter. + */ +typedef enum JitInterpAction { + JIT_INTERP_ACTION_NORMAL, /* normal execution */ + JIT_INTERP_ACTION_THROWN, /* exception was thrown */ + JIT_INTERP_ACTION_CALL /* call wasm function */ +} JitInterpAction; + +/** + * Information exchanged between jitted code and interpreter. + */ +typedef struct JitInterpSwitchInfo { + /* Points to the frame that is passed to jitted code and the frame + that is returned from jitted code */ + void *frame; + + /* Output values from jitted code of different actions */ + union { + /* IP and SP offsets for NORMAL */ + struct { + int32 ip; + int32 sp; + } normal; + + /* Function called from jitted code for CALL */ + struct { + void *function; + } call; + + /* Returned integer and/or floating point values for RETURN. This + is also used to pass return values from interpreter to jitted + code if the caller is in jitted code and the callee is in + interpreter. */ + struct { + uint32 ival[2]; + uint32 fval[2]; + uint32 last_return_type; + } ret; + } out; +} JitInterpSwitchInfo; + +/* Jit compiler options */ +typedef struct JitCompOptions { + uint32 code_cache_size; + uint32 opt_level; +} JitCompOptions; + +bool +jit_compiler_init(const JitCompOptions *option); + +void +jit_compiler_destroy(); + +JitGlobals * +jit_compiler_get_jit_globals(); + +const char * +jit_compiler_get_pass_name(unsigned i); + +bool +jit_compiler_compile(WASMModule *module, uint32 func_idx); + +bool +jit_compiler_compile_all(WASMModule *module); + +bool +jit_compiler_is_compiled(const WASMModule *module, uint32 func_idx); + +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_JIT != 0 +bool +jit_compiler_set_call_to_llvm_jit(WASMModule *module, uint32 func_idx); + +bool +jit_compiler_set_call_to_fast_jit(WASMModule *module, uint32 func_idx); + +void +jit_compiler_set_llvm_jit_func_ptr(WASMModule *module, uint32 func_idx, + void *func_ptr); +#endif + +int +jit_interp_switch_to_jitted(void *self, JitInterpSwitchInfo *info, + uint32 func_idx, void *pc); + +/* + * Pass declarations: + */ + +/** + * Dump the compilation context. + */ +bool +jit_pass_dump(JitCompContext *cc); + +/** + * Update CFG (usually before dump for better readability). + */ +bool +jit_pass_update_cfg(JitCompContext *cc); + +/** + * Translate profiling result into MIR. + */ +bool +jit_pass_frontend(JitCompContext *cc); + +/** + * Lower unsupported operations into supported ones. + */ +bool +jit_pass_lower_cg(JitCompContext *cc); + +/** + * Register allocation. + */ +bool +jit_pass_regalloc(JitCompContext *cc); + +/** + * Native code generation. + */ +bool +jit_pass_codegen(JitCompContext *cc); + +/** + * Register the jitted code so that it can be executed. + */ +bool +jit_pass_register_jitted_code(JitCompContext *cc); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _JIT_COMPILER_H_ */ diff --git a/core/iwasm/fast-jit/jit_dump.c b/core/iwasm/fast-jit/jit_dump.c new file mode 100644 index 0000000000..7e45dd83df --- /dev/null +++ b/core/iwasm/fast-jit/jit_dump.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_dump.h" +#include "jit_compiler.h" +#include "jit_codegen.h" + +void +jit_dump_reg(JitCompContext *cc, JitReg reg) +{ + unsigned kind = jit_reg_kind(reg); + unsigned no = jit_reg_no(reg); + + switch (kind) { + case JIT_REG_KIND_VOID: + os_printf("VOID"); + break; + + case JIT_REG_KIND_I32: + if (jit_reg_is_const(reg)) { + unsigned rel = jit_cc_get_const_I32_rel(cc, reg); + + os_printf("0x%x", jit_cc_get_const_I32(cc, reg)); + + if (rel) + os_printf("(rel: 0x%x)", rel); + } + else + os_printf("i%d", no); + break; + + case JIT_REG_KIND_I64: + if (jit_reg_is_const(reg)) + os_printf("0x%llxL", jit_cc_get_const_I64(cc, reg)); + else + os_printf("I%d", no); + break; + + case JIT_REG_KIND_F32: + if (jit_reg_is_const(reg)) + os_printf("%f", (double)jit_cc_get_const_F32(cc, reg)); + else + os_printf("f%d", no); + break; + + case JIT_REG_KIND_F64: + if (jit_reg_is_const(reg)) + os_printf("%fL", jit_cc_get_const_F64(cc, reg)); + else + os_printf("D%d", no); + break; + + case JIT_REG_KIND_L32: + os_printf("L%d", no); + break; + + default: + bh_assert(!"Unsupported register kind."); + } +} + +static void +jit_dump_insn_Reg(JitCompContext *cc, JitInsn *insn, unsigned opnd_num) +{ + unsigned i; + + for (i = 0; i < opnd_num; i++) { + os_printf(i == 0 ? " " : ", "); + jit_dump_reg(cc, *(jit_insn_opnd(insn, i))); + } + + os_printf("\n"); +} + +static void +jit_dump_insn_VReg(JitCompContext *cc, JitInsn *insn, unsigned opnd_num) +{ + unsigned i; + + opnd_num = jit_insn_opndv_num(insn); + + for (i = 0; i < opnd_num; i++) { + os_printf(i == 0 ? " " : ", "); + jit_dump_reg(cc, *(jit_insn_opndv(insn, i))); + } + + os_printf("\n"); +} + +static void +jit_dump_insn_LookupSwitch(JitCompContext *cc, JitInsn *insn, unsigned opnd_num) +{ + unsigned i; + JitOpndLookupSwitch *opnd = jit_insn_opndls(insn); + + os_printf(" "); + jit_dump_reg(cc, opnd->value); + os_printf("\n%16s: ", "default"); + jit_dump_reg(cc, opnd->default_target); + os_printf("\n"); + + for (i = 0; i < opnd->match_pairs_num; i++) { + os_printf("%18d: ", opnd->match_pairs[i].value); + jit_dump_reg(cc, opnd->match_pairs[i].target); + os_printf("\n"); + } +} + +void +jit_dump_insn(JitCompContext *cc, JitInsn *insn) +{ + switch (insn->opcode) { +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) \ + case JIT_OP_##NAME: \ + if (insn->flags_u8 & 0x1) \ + os_printf(" ATOMIC %-8s", #NAME); \ + else \ + os_printf(" %-15s", #NAME); \ + jit_dump_insn_##OPND_KIND(cc, insn, OPND_NUM); \ + break; +#include "jit_ir.def" +#undef INSN + } +} + +void +jit_dump_basic_block(JitCompContext *cc, JitBasicBlock *block) +{ + unsigned i, label_index; + void *begin_addr, *end_addr; + JitBasicBlock *block_next; + JitInsn *insn; + JitRegVec preds = jit_basic_block_preds(block); + JitRegVec succs = jit_basic_block_succs(block); + JitReg label = jit_basic_block_label(block), label_next; + JitReg *reg; + + jit_dump_reg(cc, label); + os_printf(":\n ; PREDS("); + + JIT_REG_VEC_FOREACH(preds, i, reg) + { + if (i > 0) + os_printf(" "); + jit_dump_reg(cc, *reg); + } + + os_printf(")\n ;"); + + if (jit_annl_is_enabled_begin_bcip(cc)) + os_printf(" BEGIN_BCIP=0x%04tx", + *(jit_annl_begin_bcip(cc, label)) + - (uint8 *)cc->cur_wasm_module->load_addr); + + if (jit_annl_is_enabled_end_bcip(cc)) + os_printf(" END_BCIP=0x%04tx", + *(jit_annl_end_bcip(cc, label)) + - (uint8 *)cc->cur_wasm_module->load_addr); + os_printf("\n"); + + if (jit_annl_is_enabled_jitted_addr(cc)) { + begin_addr = *(jit_annl_jitted_addr(cc, label)); + + if (label == cc->entry_label) { + block_next = cc->_ann._label_basic_block[2]; + label_next = jit_basic_block_label(block_next); + end_addr = *(jit_annl_jitted_addr(cc, label_next)); + } + else if (label == cc->exit_label) { + end_addr = cc->jitted_addr_end; + } + else { + label_index = jit_reg_no(label); + if (label_index < jit_cc_label_num(cc) - 1) + block_next = cc->_ann._label_basic_block[label_index + 1]; + else + block_next = cc->_ann._label_basic_block[1]; + label_next = jit_basic_block_label(block_next); + end_addr = *(jit_annl_jitted_addr(cc, label_next)); + } + + jit_codegen_dump_native(begin_addr, end_addr); + } + else { + /* Dump IR. */ + JIT_FOREACH_INSN(block, insn) jit_dump_insn(cc, insn); + } + + os_printf(" ; SUCCS("); + + JIT_REG_VEC_FOREACH(succs, i, reg) + { + if (i > 0) + os_printf(" "); + jit_dump_reg(cc, *reg); + } + + os_printf(")\n\n"); +} + +static void +dump_func_name(JitCompContext *cc) +{ + const char *func_name = NULL; + WASMModule *module = cc->cur_wasm_module; + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + func_name = cc->cur_wasm_func->field_name; +#endif + + /* if custom name section is not generated, + search symbols from export table */ + if (!func_name) { + uint32 i; + for (i = 0; i < module->export_count; i++) { + if (module->exports[i].kind == EXPORT_KIND_FUNC + && module->exports[i].index == cc->cur_wasm_func_idx) { + func_name = module->exports[i].name; + break; + } + } + } + + /* function name not exported, print number instead */ + if (func_name == NULL) { + os_printf("$f%d", cc->cur_wasm_func_idx); + } + else { + os_printf("%s", func_name); + } +} + +static void +dump_cc_ir(JitCompContext *cc) +{ + unsigned i, end; + JitBasicBlock *block; + JitReg label; + const char *kind_names[] = { "VOID", "I32", "I64", "F32", + "F64", "V64", "V128", "V256" }; + + os_printf("; Function: "); + dump_func_name(cc); + os_printf("\n"); + + os_printf("; Constant table sizes:"); + + for (i = 0; i < JIT_REG_KIND_L32; i++) + os_printf(" %s=%d", kind_names[i], cc->_const_val._num[i]); + + os_printf("\n; Label number: %d", jit_cc_label_num(cc)); + os_printf("\n; Instruction number: %d", jit_cc_insn_num(cc)); + os_printf("\n; Register numbers:"); + + for (i = 0; i < JIT_REG_KIND_L32; i++) + os_printf(" %s=%d", kind_names[i], jit_cc_reg_num(cc, i)); + + os_printf("\n; Label annotations:"); +#define ANN_LABEL(TYPE, NAME) \ + if (jit_annl_is_enabled_##NAME(cc)) \ + os_printf(" %s", #NAME); +#include "jit_ir.def" +#undef ANN_LABEL + + os_printf("\n; Instruction annotations:"); +#define ANN_INSN(TYPE, NAME) \ + if (jit_anni_is_enabled_##NAME(cc)) \ + os_printf(" %s", #NAME); +#include "jit_ir.def" +#undef ANN_INSN + + os_printf("\n; Register annotations:"); +#define ANN_REG(TYPE, NAME) \ + if (jit_annr_is_enabled_##NAME(cc)) \ + os_printf(" %s", #NAME); +#include "jit_ir.def" +#undef ANN_REG + + os_printf("\n\n"); + + if (jit_annl_is_enabled_next_label(cc)) { + /* Blocks have been reordered, use that order to dump. */ + for (label = cc->entry_label; label; + label = *(jit_annl_next_label(cc, label))) + jit_dump_basic_block(cc, *(jit_annl_basic_block(cc, label))); + } + else { + /* Otherwise, use the default order. */ + jit_dump_basic_block(cc, jit_cc_entry_basic_block(cc)); + + JIT_FOREACH_BLOCK(cc, i, end, block) jit_dump_basic_block(cc, block); + + jit_dump_basic_block(cc, jit_cc_exit_basic_block(cc)); + } +} + +void +jit_dump_cc(JitCompContext *cc) +{ + if (jit_cc_label_num(cc) <= 2) + return; + + dump_cc_ir(cc); +} + +bool +jit_pass_dump(JitCompContext *cc) +{ + const JitGlobals *jit_globals = jit_compiler_get_jit_globals(); + const uint8 *passes = jit_globals->passes; + uint8 pass_no = cc->cur_pass_no; + const char *pass_name = + pass_no > 0 ? jit_compiler_get_pass_name(passes[pass_no - 1]) : "NULL"; + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + if (!strcmp(pass_name, "lower_cg")) + /* Ignore lower codegen pass as it does nothing in x86-64 */ + return true; +#endif + + os_printf("JIT.COMPILER.DUMP: PASS_NO=%d PREV_PASS=%s\n\n", pass_no, + pass_name); + + jit_dump_cc(cc); + + os_printf("\n"); + return true; +} + +bool +jit_pass_update_cfg(JitCompContext *cc) +{ + return jit_cc_update_cfg(cc); +} diff --git a/core/iwasm/fast-jit/jit_dump.h b/core/iwasm/fast-jit/jit_dump.h new file mode 100644 index 0000000000..8e572b88de --- /dev/null +++ b/core/iwasm/fast-jit/jit_dump.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_DUMP_H_ +#define _JIT_DUMP_H_ + +#include "jit_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Dump a register. + * + * @param cc compilation context of the register + * @param reg register to be dumped + */ +void +jit_dump_reg(JitCompContext *cc, JitReg reg); + +/** + * Dump an instruction. + * + * @param cc compilation context of the instruction + * @param insn instruction to be dumped + */ +void +jit_dump_insn(JitCompContext *cc, JitInsn *insn); + +/** + * Dump a block. + * + * @param cc compilation context of the block + * @param block block to be dumped + */ +void +jit_dump_block(JitCompContext *cc, JitBlock *block); + +/** + * Dump a compilation context. + * + * @param cc compilation context to be dumped + */ +void +jit_dump_cc(JitCompContext *cc); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _JIT_DUMP_H_ */ diff --git a/core/iwasm/fast-jit/jit_frontend.c b/core/iwasm/fast-jit/jit_frontend.c new file mode 100644 index 0000000000..c96b5410ba --- /dev/null +++ b/core/iwasm/fast-jit/jit_frontend.c @@ -0,0 +1,2623 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_compiler.h" +#include "jit_frontend.h" +#include "fe/jit_emit_compare.h" +#include "fe/jit_emit_const.h" +#include "fe/jit_emit_control.h" +#include "fe/jit_emit_conversion.h" +#include "fe/jit_emit_exception.h" +#include "fe/jit_emit_function.h" +#include "fe/jit_emit_memory.h" +#include "fe/jit_emit_numberic.h" +#include "fe/jit_emit_parametric.h" +#include "fe/jit_emit_table.h" +#include "fe/jit_emit_variable.h" +#include "../interpreter/wasm_interp.h" +#include "../interpreter/wasm_opcode.h" +#include "../interpreter/wasm_runtime.h" +#include "../common/wasm_exec_env.h" + +static uint32 +get_global_base_offset(const WASMModule *module) +{ + uint32 module_inst_struct_size = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes); + uint32 mem_inst_size = + (uint32)sizeof(WASMMemoryInstance) + * (module->import_memory_count + module->memory_count); + +#if WASM_ENABLE_JIT != 0 + /* If the module doesn't have memory, reserve one mem_info space + with empty content to align with llvm jit compiler */ + if (mem_inst_size == 0) + mem_inst_size = (uint32)sizeof(WASMMemoryInstance); +#endif + + /* Size of module inst and memory instances */ + return module_inst_struct_size + mem_inst_size; +} + +static uint32 +get_first_table_inst_offset(const WASMModule *module) +{ + return get_global_base_offset(module) + module->global_data_size; +} + +uint32 +jit_frontend_get_global_data_offset(const WASMModule *module, uint32 global_idx) +{ + uint32 global_base_offset = get_global_base_offset(module); + + if (global_idx < module->import_global_count) { + const WASMGlobalImport *import_global = + &((module->import_globals + global_idx)->u.global); + return global_base_offset + import_global->data_offset; + } + else { + const WASMGlobal *global = + module->globals + (global_idx - module->import_global_count); + return global_base_offset + global->data_offset; + } +} + +uint32 +jit_frontend_get_table_inst_offset(const WASMModule *module, uint32 tbl_idx) +{ + uint32 offset, i = 0; + + offset = get_first_table_inst_offset(module); + + while (i < tbl_idx && i < module->import_table_count) { + WASMTableImport *import_table = &module->import_tables[i].u.table; + + offset += (uint32)offsetof(WASMTableInstance, elems); +#if WASM_ENABLE_MULTI_MODULE != 0 + offset += (uint32)sizeof(uint32) * import_table->table_type.max_size; +#else + offset += (uint32)sizeof(uint32) + * (import_table->table_type.possible_grow + ? import_table->table_type.max_size + : import_table->table_type.init_size); +#endif + + i++; + } + + if (i == tbl_idx) { + return offset; + } + + tbl_idx -= module->import_table_count; + i -= module->import_table_count; + while (i < tbl_idx && i < module->table_count) { + WASMTable *table = module->tables + i; + + offset += (uint32)offsetof(WASMTableInstance, elems); +#if WASM_ENABLE_MULTI_MODULE != 0 + offset += + (uint32)sizeof(table_elem_type_t) * table->table_type.max_size; +#else + offset += + (uint32)sizeof(table_elem_type_t) + * (table->table_type.possible_grow ? table->table_type.max_size + : table->table_type.init_size); +#endif + + i++; + } + + return offset; +} + +uint32 +jit_frontend_get_module_inst_extra_offset(const WASMModule *module) +{ + uint32 offset = jit_frontend_get_table_inst_offset( + module, module->import_table_count + module->table_count); + + return align_uint(offset, 8); +} + +JitReg +get_module_inst_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + + if (!frame->module_inst_reg) { + frame->module_inst_reg = cc->module_inst_reg; + GEN_INSN(LDPTR, frame->module_inst_reg, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, module_inst))); + } + return frame->module_inst_reg; +} + +JitReg +get_module_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg = get_module_inst_reg(frame); + + if (!frame->module_reg) { + frame->module_reg = cc->module_reg; + GEN_INSN(LDPTR, frame->module_reg, module_inst_reg, + NEW_CONST(I32, offsetof(WASMModuleInstance, module))); + } + return frame->module_reg; +} + +JitReg +get_import_func_ptrs_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg = get_module_inst_reg(frame); + + if (!frame->import_func_ptrs_reg) { + frame->import_func_ptrs_reg = cc->import_func_ptrs_reg; + GEN_INSN( + LDPTR, frame->import_func_ptrs_reg, module_inst_reg, + NEW_CONST(I32, offsetof(WASMModuleInstance, import_func_ptrs))); + } + return frame->import_func_ptrs_reg; +} + +JitReg +get_fast_jit_func_ptrs_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg = get_module_inst_reg(frame); + + if (!frame->fast_jit_func_ptrs_reg) { + frame->fast_jit_func_ptrs_reg = cc->fast_jit_func_ptrs_reg; + GEN_INSN( + LDPTR, frame->fast_jit_func_ptrs_reg, module_inst_reg, + NEW_CONST(I32, offsetof(WASMModuleInstance, fast_jit_func_ptrs))); + } + return frame->fast_jit_func_ptrs_reg; +} + +JitReg +get_func_type_indexes_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg = get_module_inst_reg(frame); + + if (!frame->func_type_indexes_reg) { + frame->func_type_indexes_reg = cc->func_type_indexes_reg; + GEN_INSN( + LDPTR, frame->func_type_indexes_reg, module_inst_reg, + NEW_CONST(I32, offsetof(WASMModuleInstance, func_type_indexes))); + } + return frame->func_type_indexes_reg; +} + +JitReg +get_aux_stack_bound_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg tmp = jit_cc_new_reg_I32(cc); + + if (!frame->aux_stack_bound_reg) { + frame->aux_stack_bound_reg = cc->aux_stack_bound_reg; + GEN_INSN(LDPTR, frame->aux_stack_bound_reg, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, aux_stack_boundary))); + /* TODO: Memory64 whether to convert depends on memory idx type */ + GEN_INSN(I64TOI32, tmp, frame->aux_stack_bound_reg); + frame->aux_stack_bound_reg = tmp; + } + return frame->aux_stack_bound_reg; +} + +JitReg +get_aux_stack_bottom_reg(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg tmp = jit_cc_new_reg_I32(cc); + + if (!frame->aux_stack_bottom_reg) { + frame->aux_stack_bottom_reg = cc->aux_stack_bottom_reg; + GEN_INSN(LDPTR, frame->aux_stack_bottom_reg, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, aux_stack_bottom))); + /* TODO: Memory64 whether to convert depends on memory idx type */ + GEN_INSN(I64TOI32, tmp, frame->aux_stack_bottom_reg); + frame->aux_stack_bottom_reg = tmp; + } + return frame->aux_stack_bottom_reg; +} + +#if WASM_ENABLE_SHARED_MEMORY != 0 +static bool +is_shared_memory(WASMModule *module, uint32 mem_idx) +{ + WASMMemory *memory; + WASMMemoryImport *memory_import; + bool is_shared; + + if (mem_idx < module->import_memory_count) { + memory_import = &(module->import_memories[mem_idx].u.memory); + is_shared = memory_import->mem_type.flags & 0x02 ? true : false; + } + else { + memory = &module->memories[mem_idx - module->import_memory_count]; + is_shared = memory->flags & 0x02 ? true : false; + } + return is_shared; +} +#endif + +JitReg +get_memory_inst_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg = get_module_inst_reg(frame); + uint32 memory_inst_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memories_addr; + uint32 memories_offset; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].memory_inst) + return frame->memory_regs[mem_idx].memory_inst; + + frame->memory_regs[mem_idx].memory_inst = + cc->memory_regs[mem_idx].memory_inst; + + bh_assert(mem_idx == 0); +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memories_addr = jit_cc_new_reg_ptr(cc); + memories_offset = (uint32)offsetof(WASMModuleInstance, memories); + /* module_inst->memories */ + GEN_INSN(LDPTR, memories_addr, module_inst_reg, + NEW_CONST(I32, memories_offset)); + /* module_inst->memories[mem_idx], mem_idx can only be 0 now */ + GEN_INSN(LDPTR, frame->memory_regs[mem_idx].memory_inst, memories_addr, + NEW_CONST(I32, mem_idx)); + } + else +#endif + { + memory_inst_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes); + GEN_INSN(LDPTR, frame->memory_regs[mem_idx].memory_inst, + module_inst_reg, NEW_CONST(I32, memory_inst_offset)); + } + + return frame->memory_regs[mem_idx].memory_inst; +} + +JitReg +get_cur_page_count_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 cur_page_count_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].cur_page_count) + return frame->memory_regs[mem_idx].cur_page_count; + + frame->memory_regs[mem_idx].cur_page_count = + cc->memory_regs[mem_idx].cur_page_count; + + /* Get current page count */ + bh_assert(mem_idx == 0); +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + cur_page_count_offset = + (uint32)offsetof(WASMMemoryInstance, cur_page_count); + /* memories[mem_idx]->cur_page_count_offset */ + GEN_INSN(LDI32, frame->memory_regs[mem_idx].cur_page_count, + memory_inst_reg, NEW_CONST(I32, cur_page_count_offset)); + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + cur_page_count_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, cur_page_count); + GEN_INSN(LDI32, frame->memory_regs[mem_idx].cur_page_count, + module_inst_reg, NEW_CONST(I32, cur_page_count_offset)); + } + + return frame->memory_regs[mem_idx].cur_page_count; +} + +JitReg +get_memory_data_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 memory_data_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].memory_data) + return frame->memory_regs[mem_idx].memory_data; + + frame->memory_regs[mem_idx].memory_data = + cc->memory_regs[mem_idx].memory_data; + + bh_assert(mem_idx == 0); +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + memory_data_offset = (uint32)offsetof(WASMMemoryInstance, memory_data); + /* memories[mem_idx]->memory_data */ + GEN_INSN(LDPTR, frame->memory_regs[mem_idx].memory_data, + memory_inst_reg, NEW_CONST(I32, memory_data_offset)); + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + memory_data_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, memory_data); + GEN_INSN(LDPTR, frame->memory_regs[mem_idx].memory_data, + module_inst_reg, NEW_CONST(I32, memory_data_offset)); + } + return frame->memory_regs[mem_idx].memory_data; +} + +JitReg +get_memory_data_end_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 memory_data_end_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].memory_data_end) + return frame->memory_regs[mem_idx].memory_data_end; + + frame->memory_regs[mem_idx].memory_data_end = + cc->memory_regs[mem_idx].memory_data_end; + + bh_assert(mem_idx == 0); +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + memory_data_end_offset = + (uint32)offsetof(WASMMemoryInstance, memory_data_end); + /* memories[mem_idx]->memory_data_end */ + GEN_INSN(LDPTR, frame->memory_regs[mem_idx].memory_data_end, + memory_inst_reg, NEW_CONST(I32, memory_data_end_offset)); + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + memory_data_end_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, memory_data_end); + GEN_INSN(LDPTR, frame->memory_regs[mem_idx].memory_data_end, + module_inst_reg, NEW_CONST(I32, memory_data_end_offset)); + } + return frame->memory_regs[mem_idx].memory_data_end; +} + +JitReg +get_mem_bound_check_1byte_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 mem_bound_check_1byte_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].mem_bound_check_1byte) + return frame->memory_regs[mem_idx].mem_bound_check_1byte; + + frame->memory_regs[mem_idx].mem_bound_check_1byte = + cc->memory_regs[mem_idx].mem_bound_check_1byte; + + bh_assert(mem_idx == 0); + +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + mem_bound_check_1byte_offset = + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_1byte); + /* memories[mem_idx]->mem_bound_check_1byte */ +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_1byte, + memory_inst_reg, NEW_CONST(I32, mem_bound_check_1byte_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_1byte, + memory_inst_reg, NEW_CONST(I32, mem_bound_check_1byte_offset)); +#endif + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + mem_bound_check_1byte_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_1byte); +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_1byte, + module_inst_reg, NEW_CONST(I32, mem_bound_check_1byte_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_1byte, + module_inst_reg, NEW_CONST(I32, mem_bound_check_1byte_offset)); +#endif + } + return frame->memory_regs[mem_idx].mem_bound_check_1byte; +} + +JitReg +get_mem_bound_check_2bytes_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 mem_bound_check_2bytes_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].mem_bound_check_2bytes) + return frame->memory_regs[mem_idx].mem_bound_check_2bytes; + + frame->memory_regs[mem_idx].mem_bound_check_2bytes = + cc->memory_regs[mem_idx].mem_bound_check_2bytes; + + bh_assert(mem_idx == 0); + +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + mem_bound_check_2bytes_offset = + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_2bytes); + /* memories[mem_idx]->mem_bound_check_2bytes */ +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_2bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_2bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_2bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_2bytes_offset)); +#endif + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + mem_bound_check_2bytes_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_2bytes); +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_2bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_2bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_2bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_2bytes_offset)); +#endif + } + return frame->memory_regs[mem_idx].mem_bound_check_2bytes; +} + +JitReg +get_mem_bound_check_4bytes_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 mem_bound_check_4bytes_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].mem_bound_check_4bytes) + return frame->memory_regs[mem_idx].mem_bound_check_4bytes; + + frame->memory_regs[mem_idx].mem_bound_check_4bytes = + cc->memory_regs[mem_idx].mem_bound_check_4bytes; + + bh_assert(mem_idx == 0); + +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + mem_bound_check_4bytes_offset = + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_4bytes); + /* memories[mem_idx]->mem_bound_check_4bytes */ +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_4bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_4bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_4bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_4bytes_offset)); +#endif + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + mem_bound_check_4bytes_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_4bytes); +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_4bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_4bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_4bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_4bytes_offset)); +#endif + } + return frame->memory_regs[mem_idx].mem_bound_check_4bytes; +} + +JitReg +get_mem_bound_check_8bytes_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 mem_bound_check_8bytes_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].mem_bound_check_8bytes) + return frame->memory_regs[mem_idx].mem_bound_check_8bytes; + + frame->memory_regs[mem_idx].mem_bound_check_8bytes = + cc->memory_regs[mem_idx].mem_bound_check_8bytes; + + bh_assert(mem_idx == 0); + +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + mem_bound_check_8bytes_offset = + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_8bytes); + /* memories[mem_idx]->mem_bound_check_8bytes */ +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_8bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_8bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_8bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_8bytes_offset)); +#endif + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + mem_bound_check_8bytes_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_8bytes); +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_8bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_8bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_8bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_8bytes_offset)); +#endif + } + return frame->memory_regs[mem_idx].mem_bound_check_8bytes; +} + +JitReg +get_mem_bound_check_16bytes_reg(JitFrame *frame, uint32 mem_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst_reg; + uint32 mem_bound_check_16bytes_offset; +#if WASM_ENABLE_SHARED_MEMORY != 0 + JitReg memory_inst_reg; + bool is_shared; +#endif + + if (frame->memory_regs[mem_idx].mem_bound_check_16bytes) + return frame->memory_regs[mem_idx].mem_bound_check_16bytes; + + frame->memory_regs[mem_idx].mem_bound_check_16bytes = + cc->memory_regs[mem_idx].mem_bound_check_16bytes; + + bh_assert(mem_idx == 0); + +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared = is_shared_memory(cc->cur_wasm_module, mem_idx); + if (is_shared) { + memory_inst_reg = get_memory_inst_reg(frame, mem_idx); + mem_bound_check_16bytes_offset = + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_16bytes); + /* memories[mem_idx]->mem_bound_check_16bytes */ +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_16bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_16bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_16bytes, + memory_inst_reg, + NEW_CONST(I32, mem_bound_check_16bytes_offset)); +#endif + } + else +#endif + { + module_inst_reg = get_module_inst_reg(frame); + mem_bound_check_16bytes_offset = + (uint32)offsetof(WASMModuleInstance, global_table_data.bytes) + + (uint32)offsetof(WASMMemoryInstance, mem_bound_check_16bytes); +#if UINTPTR_MAX == UINT64_MAX + GEN_INSN(LDI64, frame->memory_regs[mem_idx].mem_bound_check_16bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_16bytes_offset)); +#else + GEN_INSN(LDI32, frame->memory_regs[mem_idx].mem_bound_check_16bytes, + module_inst_reg, + NEW_CONST(I32, mem_bound_check_16bytes_offset)); +#endif + } + return frame->memory_regs[mem_idx].mem_bound_check_16bytes; +} + +JitReg +get_table_elems_reg(JitFrame *frame, uint32 tbl_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst = get_module_inst_reg(frame); + uint32 offset = + jit_frontend_get_table_inst_offset(cc->cur_wasm_module, tbl_idx) + + (uint32)offsetof(WASMTableInstance, elems); + + if (!frame->table_regs[tbl_idx].table_elems) { + frame->table_regs[tbl_idx].table_elems = + cc->table_regs[tbl_idx].table_elems; + GEN_INSN(ADD, frame->table_regs[tbl_idx].table_elems, module_inst, + NEW_CONST(PTR, offset)); + } + return frame->table_regs[tbl_idx].table_elems; +} + +JitReg +get_table_cur_size_reg(JitFrame *frame, uint32 tbl_idx) +{ + JitCompContext *cc = frame->cc; + JitReg module_inst = get_module_inst_reg(frame); + uint32 offset = + jit_frontend_get_table_inst_offset(cc->cur_wasm_module, tbl_idx) + + (uint32)offsetof(WASMTableInstance, cur_size); + + if (!frame->table_regs[tbl_idx].table_cur_size) { + frame->table_regs[tbl_idx].table_cur_size = + cc->table_regs[tbl_idx].table_cur_size; + GEN_INSN(LDI32, frame->table_regs[tbl_idx].table_cur_size, module_inst, + NEW_CONST(I32, offset)); + } + return frame->table_regs[tbl_idx].table_cur_size; +} + +void +clear_fixed_virtual_regs(JitFrame *frame) +{ + WASMModule *module = frame->cc->cur_wasm_module; + uint32 count, i; + + frame->module_inst_reg = 0; + frame->module_reg = 0; + frame->import_func_ptrs_reg = 0; + frame->fast_jit_func_ptrs_reg = 0; + frame->func_type_indexes_reg = 0; + frame->aux_stack_bound_reg = 0; + frame->aux_stack_bottom_reg = 0; + + count = module->import_memory_count + module->memory_count; + for (i = 0; i < count; i++) { + frame->memory_regs[i].memory_inst = 0; + frame->memory_regs[i].cur_page_count = 0; + frame->memory_regs[i].memory_data = 0; + frame->memory_regs[i].memory_data_end = 0; + frame->memory_regs[i].mem_bound_check_1byte = 0; + frame->memory_regs[i].mem_bound_check_2bytes = 0; + frame->memory_regs[i].mem_bound_check_4bytes = 0; + frame->memory_regs[i].mem_bound_check_8bytes = 0; + frame->memory_regs[i].mem_bound_check_16bytes = 0; + } + + count = module->import_table_count + module->table_count; + for (i = 0; i < count; i++) { + frame->table_regs[i].table_elems = 0; + frame->table_regs[i].table_cur_size = 0; + } +} + +void +clear_memory_regs(JitFrame *frame) +{ + WASMModule *module = frame->cc->cur_wasm_module; + uint32 count, i; + + count = module->import_memory_count + module->memory_count; + for (i = 0; i < count; i++) { + frame->memory_regs[i].cur_page_count = 0; + frame->memory_regs[i].memory_data = 0; + frame->memory_regs[i].memory_data_end = 0; + frame->memory_regs[i].mem_bound_check_1byte = 0; + frame->memory_regs[i].mem_bound_check_2bytes = 0; + frame->memory_regs[i].mem_bound_check_4bytes = 0; + frame->memory_regs[i].mem_bound_check_8bytes = 0; + frame->memory_regs[i].mem_bound_check_16bytes = 0; + } +} + +void +clear_table_regs(JitFrame *frame) +{ + WASMModule *module = frame->cc->cur_wasm_module; + uint32 count, i; + + count = module->import_table_count + module->table_count; + for (i = 0; i < count; i++) { + frame->table_regs[i].table_cur_size = 0; + } +} + +JitReg +gen_load_i32(JitFrame *frame, unsigned n) +{ + if (!frame->lp[n].reg) { + JitCompContext *cc = frame->cc; + frame->lp[n].reg = jit_cc_new_reg_I32(cc); + GEN_INSN(LDI32, frame->lp[n].reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + + return frame->lp[n].reg; +} + +JitReg +gen_load_i64(JitFrame *frame, unsigned n) +{ + if (!frame->lp[n].reg) { + JitCompContext *cc = frame->cc; + frame->lp[n].reg = frame->lp[n + 1].reg = jit_cc_new_reg_I64(cc); + GEN_INSN(LDI64, frame->lp[n].reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + + return frame->lp[n].reg; +} + +JitReg +gen_load_f32(JitFrame *frame, unsigned n) +{ + if (!frame->lp[n].reg) { + JitCompContext *cc = frame->cc; + frame->lp[n].reg = jit_cc_new_reg_F32(cc); + GEN_INSN(LDF32, frame->lp[n].reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + + return frame->lp[n].reg; +} + +JitReg +gen_load_f64(JitFrame *frame, unsigned n) +{ + if (!frame->lp[n].reg) { + JitCompContext *cc = frame->cc; + frame->lp[n].reg = frame->lp[n + 1].reg = jit_cc_new_reg_F64(cc); + GEN_INSN(LDF64, frame->lp[n].reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + } + + return frame->lp[n].reg; +} + +void +gen_commit_values(JitFrame *frame, JitValueSlot *begin, JitValueSlot *end) +{ + JitCompContext *cc = frame->cc; + JitValueSlot *p; + int n; + + for (p = begin; p < end; p++) { + if (!p->dirty) + continue; + + p->dirty = 0; + n = p - frame->lp; + + switch (jit_reg_kind(p->reg)) { + case JIT_REG_KIND_I32: + GEN_INSN(STI32, p->reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + + case JIT_REG_KIND_I64: + GEN_INSN(STI64, p->reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + (++p)->dirty = 0; + break; + + case JIT_REG_KIND_F32: + GEN_INSN(STF32, p->reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + break; + + case JIT_REG_KIND_F64: + GEN_INSN(STF64, p->reg, cc->fp_reg, + NEW_CONST(I32, offset_of_local(n))); + (++p)->dirty = 0; + break; + } + } +} + +/** + * Generate instructions to commit SP and IP pointers to the frame. + * + * @param frame the frame information + */ +void +gen_commit_sp_ip(JitFrame *frame) +{ + JitCompContext *cc = frame->cc; + JitReg sp; + + if (frame->sp != frame->committed_sp) { + sp = jit_cc_new_reg_ptr(cc); + GEN_INSN(ADD, sp, cc->fp_reg, + NEW_CONST(PTR, offset_of_local(frame->sp - frame->lp))); + GEN_INSN(STPTR, sp, cc->fp_reg, + NEW_CONST(I32, offsetof(WASMInterpFrame, sp))); + frame->committed_sp = frame->sp; + } + +#if 0 /* Disable committing ip currently */ + if (frame->ip != frame->committed_ip) { + GEN_INSN(STPTR, NEW_CONST(PTR, (uintptr_t)frame->ip), cc->fp_reg, + NEW_CONST(I32, offsetof(WASMInterpFrame, ip))); + frame->committed_ip = frame->ip; + } +#endif +} + +static bool +create_fixed_virtual_regs(JitCompContext *cc) +{ + WASMModule *module = cc->cur_wasm_module; + uint64 total_size; + uint32 i, count; + + cc->module_inst_reg = jit_cc_new_reg_ptr(cc); + cc->module_reg = jit_cc_new_reg_ptr(cc); + cc->import_func_ptrs_reg = jit_cc_new_reg_ptr(cc); + cc->fast_jit_func_ptrs_reg = jit_cc_new_reg_ptr(cc); + cc->func_type_indexes_reg = jit_cc_new_reg_ptr(cc); + cc->aux_stack_bound_reg = jit_cc_new_reg_ptr(cc); + cc->aux_stack_bottom_reg = jit_cc_new_reg_ptr(cc); + + count = module->import_memory_count + module->memory_count; + if (count > 0) { + total_size = (uint64)sizeof(JitMemRegs) * count; + if (total_size > UINT32_MAX + || !(cc->memory_regs = jit_calloc((uint32)total_size))) { + jit_set_last_error(cc, "allocate memory failed"); + return false; + } + + for (i = 0; i < count; i++) { + cc->memory_regs[i].memory_inst = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].cur_page_count = jit_cc_new_reg_I32(cc); + cc->memory_regs[i].memory_data = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].memory_data_end = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].mem_bound_check_1byte = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].mem_bound_check_2bytes = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].mem_bound_check_4bytes = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].mem_bound_check_8bytes = jit_cc_new_reg_ptr(cc); + cc->memory_regs[i].mem_bound_check_16bytes = jit_cc_new_reg_ptr(cc); + } + } + + count = module->import_table_count + module->table_count; + if (count > 0) { + total_size = (uint64)sizeof(JitTableRegs) * count; + if (total_size > UINT32_MAX + || !(cc->table_regs = jit_calloc((uint32)total_size))) { + jit_set_last_error(cc, "allocate memory failed"); + return false; + } + + for (i = 0; i < count; i++) { + cc->table_regs[i].table_elems = jit_cc_new_reg_ptr(cc); + cc->table_regs[i].table_cur_size = jit_cc_new_reg_I32(cc); + } + } + + return true; +} + +static bool +form_and_translate_func(JitCompContext *cc) +{ + JitBasicBlock *func_entry_basic_block; + JitReg func_entry_label; + JitInsn *insn; + JitIncomingInsn *incoming_insn, *incoming_insn_next; + uint32 i; + + if (!create_fixed_virtual_regs(cc)) + return false; + + if (!(func_entry_basic_block = jit_frontend_translate_func(cc))) + return false; + + jit_cc_reset_insn_hash(cc); + + /* The label of the func entry basic block. */ + func_entry_label = jit_basic_block_label(func_entry_basic_block); + + /* Create a JMP instruction jumping to the func entry. */ + if (!(insn = jit_cc_new_insn(cc, JMP, func_entry_label))) + return false; + + /* Insert the instruction into the cc entry block. */ + jit_basic_block_append_insn(jit_cc_entry_basic_block(cc), insn); + + /* Patch INSNs jumping to exception basic blocks. */ + for (i = 0; i < EXCE_NUM; i++) { + incoming_insn = cc->incoming_insns_for_exec_bbs[i]; + if (incoming_insn) { + if (!(cc->exce_basic_blocks[i] = jit_cc_new_basic_block(cc, 0))) { + jit_set_last_error(cc, "create basic block failed"); + return false; + } + while (incoming_insn) { + incoming_insn_next = incoming_insn->next; + insn = incoming_insn->insn; + if (insn->opcode == JIT_OP_JMP) { + *(jit_insn_opnd(insn, 0)) = + jit_basic_block_label(cc->exce_basic_blocks[i]); + } + else if (insn->opcode >= JIT_OP_BEQ + && insn->opcode <= JIT_OP_BLEU) { + *(jit_insn_opnd(insn, 1)) = + jit_basic_block_label(cc->exce_basic_blocks[i]); + } + incoming_insn = incoming_insn_next; + } + cc->cur_basic_block = cc->exce_basic_blocks[i]; + if (i != EXCE_ALREADY_THROWN) { + JitReg module_inst_reg = jit_cc_new_reg_ptr(cc); + GEN_INSN(LDPTR, module_inst_reg, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, module_inst))); + insn = GEN_INSN( + CALLNATIVE, 0, + NEW_CONST(PTR, (uintptr_t)jit_set_exception_with_id), 2); + if (insn) { + *(jit_insn_opndv(insn, 2)) = module_inst_reg; + *(jit_insn_opndv(insn, 3)) = NEW_CONST(I32, i); + } + } + GEN_INSN(RETURN, NEW_CONST(I32, JIT_INTERP_ACTION_THROWN)); + + *(jit_annl_begin_bcip(cc, + jit_basic_block_label(cc->cur_basic_block))) = + *(jit_annl_end_bcip( + cc, jit_basic_block_label(cc->cur_basic_block))) = + cc->cur_wasm_module->load_addr; + } + } + + *(jit_annl_begin_bcip(cc, cc->entry_label)) = + *(jit_annl_end_bcip(cc, cc->entry_label)) = + *(jit_annl_begin_bcip(cc, cc->exit_label)) = + *(jit_annl_end_bcip(cc, cc->exit_label)) = + cc->cur_wasm_module->load_addr; + + if (jit_get_last_error(cc)) { + return false; + } + return true; +} + +bool +jit_pass_frontend(JitCompContext *cc) +{ + /* Enable necessary annotations required at the current stage. */ + if (!jit_annl_enable_begin_bcip(cc) || !jit_annl_enable_end_bcip(cc) + || !jit_annl_enable_end_sp(cc) || !jit_annr_enable_def_insn(cc) + || !jit_cc_enable_insn_hash(cc, 127)) + return false; + + if (!(form_and_translate_func(cc))) + return false; + + /* Release the annotations after local CSE and translation. */ + jit_cc_disable_insn_hash(cc); + jit_annl_disable_end_sp(cc); + + return true; +} + +static JitFrame * +init_func_translation(JitCompContext *cc) +{ + JitFrame *jit_frame; + JitReg top, top_boundary, new_top, frame_boundary, frame_sp; + WASMModule *cur_wasm_module = cc->cur_wasm_module; + WASMFunction *cur_wasm_func = cc->cur_wasm_func; + uint32 cur_wasm_func_idx = cc->cur_wasm_func_idx; + uint32 max_locals = + cur_wasm_func->param_cell_num + cur_wasm_func->local_cell_num; + uint32 max_stacks = cur_wasm_func->max_stack_cell_num; + uint64 total_cell_num = + (uint64)cur_wasm_func->param_cell_num + + (uint64)cur_wasm_func->local_cell_num + + (uint64)cur_wasm_func->max_stack_cell_num + + ((uint64)cur_wasm_func->max_block_num) * sizeof(WASMBranchBlock) / 4; + uint32 frame_size, outs_size, local_size, count; + uint32 i, local_off; + uint64 total_size; +#if WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_PERF_PROFILING != 0 + JitReg module_inst, func_inst; + uint32 func_insts_offset; +#if WASM_ENABLE_PERF_PROFILING != 0 + JitReg time_started; +#endif +#endif + + if ((uint64)max_locals + (uint64)max_stacks >= UINT32_MAX + || total_cell_num >= UINT32_MAX + || !(jit_frame = jit_calloc(offsetof(JitFrame, lp) + + sizeof(*jit_frame->lp) + * (max_locals + max_stacks)))) { + LOG_ERROR("allocate jit frame failed\n"); + return NULL; + } + + count = + cur_wasm_module->import_memory_count + cur_wasm_module->memory_count; + if (count > 0) { + total_size = (uint64)sizeof(JitMemRegs) * count; + if (total_size > UINT32_MAX + || !(jit_frame->memory_regs = jit_calloc((uint32)total_size))) { + jit_set_last_error(cc, "allocate memory failed"); + jit_free(jit_frame); + return NULL; + } + } + + count = cur_wasm_module->import_table_count + cur_wasm_module->table_count; + if (count > 0) { + total_size = (uint64)sizeof(JitTableRegs) * count; + if (total_size > UINT32_MAX + || !(jit_frame->table_regs = jit_calloc((uint32)total_size))) { + jit_set_last_error(cc, "allocate memory failed"); + if (jit_frame->memory_regs) + jit_free(jit_frame->memory_regs); + jit_free(jit_frame); + return NULL; + } + } + + jit_frame->cur_wasm_module = cur_wasm_module; + jit_frame->cur_wasm_func = cur_wasm_func; + jit_frame->cur_wasm_func_idx = cur_wasm_func_idx; + jit_frame->cc = cc; + jit_frame->max_locals = max_locals; + jit_frame->max_stacks = max_stacks; + jit_frame->sp = jit_frame->lp + max_locals; + jit_frame->ip = cur_wasm_func->code; + + cc->jit_frame = jit_frame; + cc->cur_basic_block = jit_cc_entry_basic_block(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 *) * 16; + cc->total_frame_size = cc->spill_cache_offset + cc->spill_cache_size; + cc->jitted_return_address_offset = + offsetof(WASMInterpFrame, jitted_return_addr); + cc->cur_basic_block = jit_cc_entry_basic_block(cc); + + frame_size = outs_size = cc->total_frame_size; + local_size = + (cur_wasm_func->param_cell_num + cur_wasm_func->local_cell_num) * 4; + + top = jit_cc_new_reg_ptr(cc); + top_boundary = jit_cc_new_reg_ptr(cc); + new_top = jit_cc_new_reg_ptr(cc); + frame_boundary = jit_cc_new_reg_ptr(cc); + frame_sp = jit_cc_new_reg_ptr(cc); + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_PERF_PROFILING != 0 + module_inst = jit_cc_new_reg_ptr(cc); + func_inst = jit_cc_new_reg_ptr(cc); +#if WASM_ENABLE_PERF_PROFILING != 0 + time_started = jit_cc_new_reg_I64(cc); + /* Call os_time_thread_cputime_us() to get time_started firstly + as there is stack frame switching below, calling native in them + may cause register spilling work improperly */ + if (!jit_emit_callnative(cc, os_time_thread_cputime_us, time_started, NULL, + 0)) { + return NULL; + } +#endif +#endif + + /* top = exec_env->wasm_stack.top */ + GEN_INSN(LDPTR, top, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, wasm_stack.top))); + /* top_boundary = exec_env->wasm_stack.top_boundary */ + GEN_INSN(LDPTR, top_boundary, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, wasm_stack.top_boundary))); + /* frame_boundary = top + frame_size + outs_size */ + GEN_INSN(ADD, frame_boundary, top, NEW_CONST(PTR, frame_size + outs_size)); + /* if frame_boundary > top_boundary, throw stack overflow exception */ + GEN_INSN(CMP, cc->cmp_reg, frame_boundary, top_boundary); + if (!jit_emit_exception(cc, EXCE_OPERAND_STACK_OVERFLOW, JIT_OP_BGTU, + cc->cmp_reg, NULL)) { + return NULL; + } + + /* Add first and then sub to reduce one used register */ + /* new_top = frame_boundary - outs_size = top + frame_size */ + GEN_INSN(SUB, new_top, frame_boundary, NEW_CONST(PTR, outs_size)); + /* exec_env->wasm_stack.top = new_top */ + GEN_INSN(STPTR, new_top, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, wasm_stack.top))); + /* frame_sp = frame->lp + local_size */ + GEN_INSN(ADD, frame_sp, top, + NEW_CONST(PTR, offsetof(WASMInterpFrame, lp) + local_size)); + /* frame->sp = frame_sp */ + GEN_INSN(STPTR, frame_sp, top, + NEW_CONST(I32, offsetof(WASMInterpFrame, sp))); + /* frame->prev_frame = fp_reg */ + GEN_INSN(STPTR, cc->fp_reg, top, + NEW_CONST(I32, offsetof(WASMInterpFrame, prev_frame))); +#if WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_PERF_PROFILING != 0 + /* module_inst = exec_env->module_inst */ + GEN_INSN(LDPTR, module_inst, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, module_inst))); + func_insts_offset = + jit_frontend_get_module_inst_extra_offset(cur_wasm_module) + + (uint32)offsetof(WASMModuleInstanceExtra, functions); + /* func_inst = module_inst->e->functions */ + GEN_INSN(LDPTR, func_inst, module_inst, NEW_CONST(I32, func_insts_offset)); + /* func_inst = func_inst + cur_wasm_func_idx */ + GEN_INSN(ADD, func_inst, func_inst, + NEW_CONST(PTR, (uint32)sizeof(WASMFunctionInstance) + * cur_wasm_func_idx)); + /* frame->function = func_inst */ + GEN_INSN(STPTR, func_inst, top, + NEW_CONST(I32, offsetof(WASMInterpFrame, function))); +#if WASM_ENABLE_PERF_PROFILING != 0 + /* frame->time_started = time_started */ + GEN_INSN(STI64, time_started, top, + NEW_CONST(I32, offsetof(WASMInterpFrame, time_started))); +#endif +#endif + /* exec_env->cur_frame = top */ + GEN_INSN(STPTR, top, cc->exec_env_reg, + NEW_CONST(I32, offsetof(WASMExecEnv, cur_frame))); + /* fp_reg = top */ + GEN_INSN(MOV, cc->fp_reg, top); + + /* Initialize local variables, set them to 0 */ + local_off = (uint32)offsetof(WASMInterpFrame, lp) + + cur_wasm_func->param_cell_num * 4; + for (i = 0; i < cur_wasm_func->local_cell_num / 2; i++, local_off += 8) { + GEN_INSN(STI64, NEW_CONST(I64, 0), cc->fp_reg, + NEW_CONST(I32, local_off)); + } + if (cur_wasm_func->local_cell_num & 1) { + GEN_INSN(STI32, NEW_CONST(I32, 0), cc->fp_reg, + NEW_CONST(I32, local_off)); + } + +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + /* externref/funcref should be NULL_REF rather than 0 */ + local_off = (uint32)offsetof(WASMInterpFrame, lp) + + cur_wasm_func->param_cell_num * 4; + for (i = 0; i < cur_wasm_func->local_count; i++) { + if (cur_wasm_func->local_types[i] == VALUE_TYPE_EXTERNREF + || cur_wasm_func->local_types[i] == VALUE_TYPE_FUNCREF) { + GEN_INSN(STI32, NEW_CONST(I32, NULL_REF), cc->fp_reg, + NEW_CONST(I32, local_off)); + } + local_off += + 4 * wasm_value_type_cell_num(cur_wasm_func->local_types[i]); + } +#endif + + return jit_frame; +} + +static void +free_block_memory(JitBlock *block) +{ + if (block->param_types) + jit_free(block->param_types); + if (block->result_types) + jit_free(block->result_types); + jit_free(block); +} + +static JitBasicBlock * +create_func_block(JitCompContext *cc) +{ + JitBlock *jit_block; + WASMFunction *cur_func = cc->cur_wasm_func; + WASMType *func_type = cur_func->func_type; + uint32 param_count = func_type->param_count; + uint32 result_count = func_type->result_count; + + if (!(jit_block = jit_calloc(sizeof(JitBlock)))) { + return NULL; + } + + if (param_count && !(jit_block->param_types = jit_calloc(param_count))) { + goto fail; + } + if (result_count && !(jit_block->result_types = jit_calloc(result_count))) { + goto fail; + } + + /* Set block data */ + jit_block->label_type = LABEL_TYPE_FUNCTION; + jit_block->param_count = param_count; + if (param_count) { + bh_memcpy_s(jit_block->param_types, param_count, func_type->types, + param_count); + } + jit_block->result_count = result_count; + if (result_count) { + bh_memcpy_s(jit_block->result_types, result_count, + func_type->types + param_count, result_count); + } + jit_block->wasm_code_end = cur_func->code + cur_func->code_size; + jit_block->frame_sp_begin = cc->jit_frame->sp; + + /* Add function entry block */ + if (!(jit_block->basic_block_entry = jit_cc_new_basic_block(cc, 0))) { + goto fail; + } + *(jit_annl_begin_bcip( + cc, jit_basic_block_label(jit_block->basic_block_entry))) = + cur_func->code; + jit_block_stack_push(&cc->block_stack, jit_block); + cc->cur_basic_block = jit_block->basic_block_entry; + + return jit_block->basic_block_entry; + +fail: + free_block_memory(jit_block); + return NULL; +} + +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + if (buf + length > buf_end) { \ + jit_set_last_error(cc, "read leb failed: unexpected end."); \ + return false; \ + } \ + } while (0) + +static bool +read_leb(JitCompContext *cc, const uint8 *buf, const uint8 *buf_end, + uint32 *p_offset, uint32 maxbits, bool sign, uint64 *p_result) +{ + uint64 result = 0; + uint32 shift = 0; + uint32 bcnt = 0; + uint64 byte; + + while (true) { + CHECK_BUF(buf, buf_end, 1); + byte = buf[*p_offset]; + *p_offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + bcnt += 1; + } + if (bcnt > (maxbits + 6) / 7) { + jit_set_last_error(cc, "read leb failed: " + "integer representation too long"); + return false; + } + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= (~((uint64)0)) << shift; + } + *p_result = result; + return true; +} + +#define read_leb_uint32(p, p_end, res) \ + do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(cc, p, p_end, &off, 32, false, &res64)) \ + return false; \ + p += off; \ + res = (uint32)res64; \ + } while (0) + +#define read_leb_int32(p, p_end, res) \ + do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(cc, p, p_end, &off, 32, true, &res64)) \ + return false; \ + p += off; \ + res = (int32)res64; \ + } while (0) + +#define read_leb_int64(p, p_end, res) \ + do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(cc, p, p_end, &off, 64, true, &res64)) \ + return false; \ + p += off; \ + res = (int64)res64; \ + } while (0) + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#define COMPILE_ATOMIC_RMW(OP, NAME) \ + case WASM_OP_ATOMIC_RMW_I32_##NAME: \ + bytes = 4; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME: \ + bytes = 8; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I32_##NAME##8_U: \ + bytes = 1; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I32_##NAME##16_U: \ + bytes = 2; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##8_U: \ + bytes = 1; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##16_U: \ + bytes = 2; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##32_U: \ + bytes = 4; \ + op_type = VALUE_TYPE_I64; \ + OP_ATOMIC_##OP : bin_op = AtomicRMWBinOp##OP; \ + goto build_atomic_rmw; +#endif + +static bool +jit_compile_func(JitCompContext *cc) +{ + WASMFunction *cur_func = cc->cur_wasm_func; + WASMType *func_type = NULL; + uint8 *frame_ip = cur_func->code, opcode, *p_f32, *p_f64; + uint8 *frame_ip_end = frame_ip + cur_func->code_size; + uint8 *param_types = NULL, *result_types = NULL, value_type; + uint16 param_count, result_count; + uint32 br_depth, *br_depths, br_count; + uint32 func_idx, type_idx, mem_idx, local_idx, global_idx, i; + uint32 bytes = 4, align, offset; + bool merge_cmp_and_if = false, merge_cmp_and_br_if = false; + bool sign = true; + int32 i32_const; + int64 i64_const; + float32 f32_const; + float64 f64_const; + + while (frame_ip < frame_ip_end) { + cc->jit_frame->ip = frame_ip; + opcode = *frame_ip++; + +#if 0 /* TODO */ +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (cc->enable_thread_mgr) { + if (!check_suspend_flags(cc, func_ctx)) + return false; + } +#endif +#endif + + switch (opcode) { + case WASM_OP_UNREACHABLE: + if (!jit_compile_op_unreachable(cc, &frame_ip)) + return false; + break; + + case WASM_OP_NOP: + break; + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + { + value_type = *frame_ip++; + if (value_type == VALUE_TYPE_I32 || value_type == VALUE_TYPE_I64 + || value_type == VALUE_TYPE_F32 + || value_type == VALUE_TYPE_F64 + || value_type == VALUE_TYPE_V128 + || value_type == VALUE_TYPE_VOID + || value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF) { + param_count = 0; + param_types = NULL; + if (value_type == VALUE_TYPE_VOID) { + result_count = 0; + result_types = NULL; + } + else { + result_count = 1; + result_types = &value_type; + } + } + else { + jit_set_last_error(cc, "unsupported value type"); + return false; + } + if (!jit_compile_op_block( + cc, &frame_ip, frame_ip_end, + (uint32)(LABEL_TYPE_BLOCK + opcode - WASM_OP_BLOCK), + param_count, param_types, result_count, result_types, + merge_cmp_and_if)) + return false; + /* Clear flag */ + merge_cmp_and_if = false; + break; + } + case EXT_OP_BLOCK: + case EXT_OP_LOOP: + case EXT_OP_IF: + { + read_leb_int32(frame_ip, frame_ip_end, type_idx); + /* type index was checked in wasm loader */ + bh_assert(type_idx < cc->cur_wasm_module->type_count); + func_type = cc->cur_wasm_module->types[type_idx]; + param_count = func_type->param_count; + param_types = func_type->types; + result_count = func_type->result_count; + result_types = func_type->types + param_count; + if (!jit_compile_op_block( + cc, &frame_ip, frame_ip_end, + (uint32)(LABEL_TYPE_BLOCK + opcode - EXT_OP_BLOCK), + param_count, param_types, result_count, result_types, + merge_cmp_and_if)) + return false; + /* Clear flag */ + merge_cmp_and_if = false; + break; + } + + case WASM_OP_ELSE: + if (!jit_compile_op_else(cc, &frame_ip)) + return false; + break; + + case WASM_OP_END: + if (!jit_compile_op_end(cc, &frame_ip)) + return false; + break; + + case WASM_OP_BR: + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!jit_compile_op_br(cc, br_depth, &frame_ip)) + return false; + break; + + case WASM_OP_BR_IF: + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!jit_compile_op_br_if(cc, br_depth, merge_cmp_and_br_if, + &frame_ip)) + return false; + /* Clear flag */ + merge_cmp_and_br_if = false; + break; + + case WASM_OP_BR_TABLE: + read_leb_uint32(frame_ip, frame_ip_end, br_count); + if (!(br_depths = jit_calloc((uint32)sizeof(uint32) + * (br_count + 1)))) { + jit_set_last_error(cc, "allocate memory failed."); + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + for (i = 0; i <= br_count; i++) + read_leb_uint32(frame_ip, frame_ip_end, br_depths[i]); +#else + for (i = 0; i <= br_count; i++) + br_depths[i] = *frame_ip++; +#endif + + if (!jit_compile_op_br_table(cc, br_depths, br_count, + &frame_ip)) { + jit_free(br_depths); + return false; + } + + jit_free(br_depths); + break; + +#if WASM_ENABLE_FAST_INTERP == 0 + case EXT_OP_BR_TABLE_CACHE: + { + BrTableCache *node = bh_list_first_elem( + cc->cur_wasm_module->br_table_cache_list); + BrTableCache *node_next; + uint8 *p_opcode = frame_ip - 1; + + read_leb_uint32(frame_ip, frame_ip_end, br_count); + + while (node) { + node_next = bh_list_elem_next(node); + if (node->br_table_op_addr == p_opcode) { + br_depths = node->br_depths; + if (!jit_compile_op_br_table(cc, br_depths, br_count, + &frame_ip)) { + return false; + } + break; + } + node = node_next; + } + bh_assert(node); + + break; + } +#endif + + case WASM_OP_RETURN: + if (!jit_compile_op_return(cc, &frame_ip)) + return false; + break; + + case WASM_OP_CALL: + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!jit_compile_op_call(cc, func_idx, false)) + return false; + break; + + case WASM_OP_CALL_INDIRECT: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, type_idx); + +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); +#else + frame_ip++; + tbl_idx = 0; +#endif + + if (!jit_compile_op_call_indirect(cc, type_idx, tbl_idx)) + return false; + break; + } + +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + + if (!jit_compile_op_call(cc, func_idx, true)) + return false; + if (!jit_compile_op_return(cc, &frame_ip)) + return false; + break; + + case WASM_OP_RETURN_CALL_INDIRECT: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, type_idx); +#if WASM_ENABLE_REF_TYPES != 0 + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); +#else + frame_ip++; + tbl_idx = 0; +#endif + + if (!jit_compile_op_call_indirect(cc, type_idx, tbl_idx)) + return false; + if (!jit_compile_op_return(cc, &frame_ip)) + return false; + break; + } +#endif /* end of WASM_ENABLE_TAIL_CALL */ + + case WASM_OP_DROP: + if (!jit_compile_op_drop(cc, true)) + return false; + break; + + case WASM_OP_DROP_64: + if (!jit_compile_op_drop(cc, false)) + return false; + break; + + case WASM_OP_SELECT: + if (!jit_compile_op_select(cc, true)) + return false; + break; + + case WASM_OP_SELECT_64: + if (!jit_compile_op_select(cc, false)) + return false; + break; + +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + { + uint32 vec_len; + + read_leb_uint32(frame_ip, frame_ip_end, vec_len); + bh_assert(vec_len == 1); + (void)vec_len; + + type_idx = *frame_ip++; + if (!jit_compile_op_select(cc, + (type_idx != VALUE_TYPE_I64) + && (type_idx != VALUE_TYPE_F64))) + return false; + break; + } + case WASM_OP_TABLE_GET: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!jit_compile_op_table_get(cc, tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_SET: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!jit_compile_op_table_set(cc, tbl_idx)) + return false; + break; + } + case WASM_OP_REF_NULL: + { + uint32 ref_type; + read_leb_uint32(frame_ip, frame_ip_end, ref_type); + if (!jit_compile_op_ref_null(cc, ref_type)) + return false; + break; + } + case WASM_OP_REF_IS_NULL: + { + if (!jit_compile_op_ref_is_null(cc)) + return false; + break; + } + case WASM_OP_REF_FUNC: + { + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!jit_compile_op_ref_func(cc, func_idx)) + return false; + break; + } +#endif + + case WASM_OP_GET_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!jit_compile_op_get_local(cc, local_idx)) + return false; + break; + + case WASM_OP_SET_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!jit_compile_op_set_local(cc, local_idx)) + return false; + break; + + case WASM_OP_TEE_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!jit_compile_op_tee_local(cc, local_idx)) + return false; + break; + + case WASM_OP_GET_GLOBAL: + case WASM_OP_GET_GLOBAL_64: + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + if (!jit_compile_op_get_global(cc, global_idx)) + return false; + break; + + case WASM_OP_SET_GLOBAL: + case WASM_OP_SET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_AUX_STACK: + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + if (!jit_compile_op_set_global( + cc, global_idx, + opcode == WASM_OP_SET_GLOBAL_AUX_STACK ? true : false)) + return false; + break; + + case WASM_OP_I32_LOAD: + bytes = 4; + sign = true; + goto op_i32_load; + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + bytes = 1; + sign = (opcode == WASM_OP_I32_LOAD8_S) ? true : false; + goto op_i32_load; + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + bytes = 2; + sign = (opcode == WASM_OP_I32_LOAD16_S) ? true : false; + op_i32_load: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_i32_load(cc, align, offset, bytes, sign, + false)) + return false; + break; + + case WASM_OP_I64_LOAD: + bytes = 8; + sign = true; + goto op_i64_load; + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + bytes = 1; + sign = (opcode == WASM_OP_I64_LOAD8_S) ? true : false; + goto op_i64_load; + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + bytes = 2; + sign = (opcode == WASM_OP_I64_LOAD16_S) ? true : false; + goto op_i64_load; + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + bytes = 4; + sign = (opcode == WASM_OP_I64_LOAD32_S) ? true : false; + op_i64_load: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_i64_load(cc, align, offset, bytes, sign, + false)) + return false; + break; + + case WASM_OP_F32_LOAD: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_f32_load(cc, align, offset)) + return false; + break; + + case WASM_OP_F64_LOAD: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_f64_load(cc, align, offset)) + return false; + break; + + case WASM_OP_I32_STORE: + bytes = 4; + goto op_i32_store; + case WASM_OP_I32_STORE8: + bytes = 1; + goto op_i32_store; + case WASM_OP_I32_STORE16: + bytes = 2; + op_i32_store: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_i32_store(cc, align, offset, bytes, false)) + return false; + break; + + case WASM_OP_I64_STORE: + bytes = 8; + goto op_i64_store; + case WASM_OP_I64_STORE8: + bytes = 1; + goto op_i64_store; + case WASM_OP_I64_STORE16: + bytes = 2; + goto op_i64_store; + case WASM_OP_I64_STORE32: + bytes = 4; + op_i64_store: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_i64_store(cc, align, offset, bytes, false)) + return false; + break; + + case WASM_OP_F32_STORE: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_f32_store(cc, align, offset)) + return false; + break; + + case WASM_OP_F64_STORE: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!jit_compile_op_f64_store(cc, align, offset)) + return false; + break; + + case WASM_OP_MEMORY_SIZE: + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!jit_compile_op_memory_size(cc, mem_idx)) + return false; + break; + + case WASM_OP_MEMORY_GROW: + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!jit_compile_op_memory_grow(cc, mem_idx)) + return false; + break; + + case WASM_OP_I32_CONST: + read_leb_int32(frame_ip, frame_ip_end, i32_const); + if (!jit_compile_op_i32_const(cc, i32_const)) + return false; + break; + + case WASM_OP_I64_CONST: + read_leb_int64(frame_ip, frame_ip_end, i64_const); + if (!jit_compile_op_i64_const(cc, i64_const)) + return false; + break; + + case WASM_OP_F32_CONST: + p_f32 = (uint8 *)&f32_const; + for (i = 0; i < sizeof(float32); i++) + *p_f32++ = *frame_ip++; + if (!jit_compile_op_f32_const(cc, f32_const)) + return false; + break; + + case WASM_OP_F64_CONST: + p_f64 = (uint8 *)&f64_const; + for (i = 0; i < sizeof(float64); i++) + *p_f64++ = *frame_ip++; + if (!jit_compile_op_f64_const(cc, f64_const)) + return false; + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + if (!jit_compile_op_i32_compare(cc, INT_EQZ + opcode + - WASM_OP_I32_EQZ)) + return false; + if (frame_ip < frame_ip_end) { + /* Merge `CMP, SELECTcc, CMP, BNE` insns into `CMP, Bcc` */ + if (*frame_ip == WASM_OP_IF || *frame_ip == EXT_OP_IF) + merge_cmp_and_if = true; + if (*frame_ip == WASM_OP_BR_IF) + merge_cmp_and_br_if = true; + } + break; + + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + if (!jit_compile_op_i64_compare(cc, INT_EQZ + opcode + - WASM_OP_I64_EQZ)) + return false; + if (frame_ip < frame_ip_end) { + /* Merge `CMP, SELECTcc, CMP, BNE` insns into `CMP, Bcc` */ + if (*frame_ip == WASM_OP_IF || *frame_ip == EXT_OP_IF) + merge_cmp_and_if = true; + if (*frame_ip == WASM_OP_BR_IF) + merge_cmp_and_br_if = true; + } + break; + + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + if (!jit_compile_op_f32_compare(cc, FLOAT_EQ + opcode + - WASM_OP_F32_EQ)) + return false; + if (frame_ip < frame_ip_end) { + /* Merge `CMP, SELECTcc, CMP, BNE` insns into `CMP, Bcc` */ + if (*frame_ip == WASM_OP_IF || *frame_ip == EXT_OP_IF) + merge_cmp_and_if = true; + if (*frame_ip == WASM_OP_BR_IF) + merge_cmp_and_br_if = true; + } + break; + + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + if (!jit_compile_op_f64_compare(cc, FLOAT_EQ + opcode + - WASM_OP_F64_EQ)) + return false; + if (frame_ip < frame_ip_end) { + /* Merge `CMP, SELECTcc, CMP, BNE` insns into `CMP, Bcc` */ + if (*frame_ip == WASM_OP_IF || *frame_ip == EXT_OP_IF) + merge_cmp_and_if = true; + if (*frame_ip == WASM_OP_BR_IF) + merge_cmp_and_br_if = true; + } + break; + + case WASM_OP_I32_CLZ: + if (!jit_compile_op_i32_clz(cc)) + return false; + break; + + case WASM_OP_I32_CTZ: + if (!jit_compile_op_i32_ctz(cc)) + return false; + break; + + case WASM_OP_I32_POPCNT: + if (!jit_compile_op_i32_popcnt(cc)) + return false; + break; + + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + if (!jit_compile_op_i32_arithmetic( + cc, INT_ADD + opcode - WASM_OP_I32_ADD, &frame_ip)) + return false; + break; + + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + if (!jit_compile_op_i32_bitwise(cc, INT_SHL + opcode + - WASM_OP_I32_AND)) + return false; + break; + + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + if (!jit_compile_op_i32_shift(cc, INT_SHL + opcode + - WASM_OP_I32_SHL)) + return false; + break; + + case WASM_OP_I64_CLZ: + if (!jit_compile_op_i64_clz(cc)) + return false; + break; + + case WASM_OP_I64_CTZ: + if (!jit_compile_op_i64_ctz(cc)) + return false; + break; + + case WASM_OP_I64_POPCNT: + if (!jit_compile_op_i64_popcnt(cc)) + return false; + break; + + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + if (!jit_compile_op_i64_arithmetic( + cc, INT_ADD + opcode - WASM_OP_I64_ADD, &frame_ip)) + return false; + break; + + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + if (!jit_compile_op_i64_bitwise(cc, INT_SHL + opcode + - WASM_OP_I64_AND)) + return false; + break; + + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + if (!jit_compile_op_i64_shift(cc, INT_SHL + opcode + - WASM_OP_I64_SHL)) + return false; + break; + + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + if (!jit_compile_op_f32_math(cc, FLOAT_ABS + opcode + - WASM_OP_F32_ABS)) + return false; + break; + + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + if (!jit_compile_op_f32_arithmetic(cc, FLOAT_ADD + opcode + - WASM_OP_F32_ADD)) + return false; + break; + + case WASM_OP_F32_COPYSIGN: + if (!jit_compile_op_f32_copysign(cc)) + return false; + break; + + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + if (!jit_compile_op_f64_math(cc, FLOAT_ABS + opcode + - WASM_OP_F64_ABS)) + return false; + break; + + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + if (!jit_compile_op_f64_arithmetic(cc, FLOAT_ADD + opcode + - WASM_OP_F64_ADD)) + return false; + break; + + case WASM_OP_F64_COPYSIGN: + if (!jit_compile_op_f64_copysign(cc)) + return false; + break; + + case WASM_OP_I32_WRAP_I64: + if (!jit_compile_op_i32_wrap_i64(cc)) + return false; + break; + + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + sign = (opcode == WASM_OP_I32_TRUNC_S_F32) ? true : false; + if (!jit_compile_op_i32_trunc_f32(cc, sign, false)) + return false; + break; + + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + sign = (opcode == WASM_OP_I32_TRUNC_S_F64) ? true : false; + if (!jit_compile_op_i32_trunc_f64(cc, sign, false)) + return false; + break; + + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + sign = (opcode == WASM_OP_I64_EXTEND_S_I32) ? true : false; + if (!jit_compile_op_i64_extend_i32(cc, sign)) + return false; + break; + + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + sign = (opcode == WASM_OP_I64_TRUNC_S_F32) ? true : false; + if (!jit_compile_op_i64_trunc_f32(cc, sign, false)) + return false; + break; + + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + sign = (opcode == WASM_OP_I64_TRUNC_S_F64) ? true : false; + if (!jit_compile_op_i64_trunc_f64(cc, sign, false)) + return false; + break; + + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + sign = (opcode == WASM_OP_F32_CONVERT_S_I32) ? true : false; + if (!jit_compile_op_f32_convert_i32(cc, sign)) + return false; + break; + + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + sign = (opcode == WASM_OP_F32_CONVERT_S_I64) ? true : false; + if (!jit_compile_op_f32_convert_i64(cc, sign)) + return false; + break; + + case WASM_OP_F32_DEMOTE_F64: + if (!jit_compile_op_f32_demote_f64(cc)) + return false; + break; + + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + sign = (opcode == WASM_OP_F64_CONVERT_S_I32) ? true : false; + if (!jit_compile_op_f64_convert_i32(cc, sign)) + return false; + break; + + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + sign = (opcode == WASM_OP_F64_CONVERT_S_I64) ? true : false; + if (!jit_compile_op_f64_convert_i64(cc, sign)) + return false; + break; + + case WASM_OP_F64_PROMOTE_F32: + if (!jit_compile_op_f64_promote_f32(cc)) + return false; + break; + + case WASM_OP_I32_REINTERPRET_F32: + if (!jit_compile_op_i32_reinterpret_f32(cc)) + return false; + break; + + case WASM_OP_I64_REINTERPRET_F64: + if (!jit_compile_op_i64_reinterpret_f64(cc)) + return false; + break; + + case WASM_OP_F32_REINTERPRET_I32: + if (!jit_compile_op_f32_reinterpret_i32(cc)) + return false; + break; + + case WASM_OP_F64_REINTERPRET_I64: + if (!jit_compile_op_f64_reinterpret_i64(cc)) + return false; + break; + + case WASM_OP_I32_EXTEND8_S: + if (!jit_compile_op_i32_extend_i32(cc, 8)) + return false; + break; + + case WASM_OP_I32_EXTEND16_S: + if (!jit_compile_op_i32_extend_i32(cc, 16)) + return false; + break; + + case WASM_OP_I64_EXTEND8_S: + if (!jit_compile_op_i64_extend_i64(cc, 8)) + return false; + break; + + case WASM_OP_I64_EXTEND16_S: + if (!jit_compile_op_i64_extend_i64(cc, 16)) + return false; + break; + + case WASM_OP_I64_EXTEND32_S: + if (!jit_compile_op_i64_extend_i64(cc, 32)) + return false; + break; + + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + sign = (opcode == WASM_OP_I32_TRUNC_SAT_S_F32) ? true + : false; + if (!jit_compile_op_i32_trunc_f32(cc, sign, true)) + return false; + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + sign = (opcode == WASM_OP_I32_TRUNC_SAT_S_F64) ? true + : false; + if (!jit_compile_op_i32_trunc_f64(cc, sign, true)) + return false; + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + sign = (opcode == WASM_OP_I64_TRUNC_SAT_S_F32) ? true + : false; + if (!jit_compile_op_i64_trunc_f32(cc, sign, true)) + return false; + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + sign = (opcode == WASM_OP_I64_TRUNC_SAT_S_F64) ? true + : false; + if (!jit_compile_op_i64_trunc_f64(cc, sign, true)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 seg_idx = 0; + read_leb_uint32(frame_ip, frame_ip_end, seg_idx); + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!jit_compile_op_memory_init(cc, mem_idx, seg_idx)) + return false; + break; + } + case WASM_OP_DATA_DROP: + { + uint32 seg_idx; + read_leb_uint32(frame_ip, frame_ip_end, seg_idx); + if (!jit_compile_op_data_drop(cc, seg_idx)) + return false; + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + { + uint32 src_mem_idx, dst_mem_idx; + read_leb_uint32(frame_ip, frame_ip_end, src_mem_idx); + read_leb_uint32(frame_ip, frame_ip_end, dst_mem_idx); + if (!jit_compile_op_memory_copy(cc, src_mem_idx, + dst_mem_idx)) + return false; + break; + } + case WASM_OP_MEMORY_FILL: + { + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!jit_compile_op_memory_fill(cc, mem_idx)) + return false; + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx, tbl_seg_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_seg_idx); + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!jit_compile_op_table_init(cc, tbl_idx, + tbl_seg_idx)) + return false; + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 tbl_seg_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_seg_idx); + if (!jit_compile_op_elem_drop(cc, tbl_seg_idx)) + return false; + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, dst_tbl_idx); + read_leb_uint32(frame_ip, frame_ip_end, src_tbl_idx); + if (!jit_compile_op_table_copy(cc, src_tbl_idx, + dst_tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_GROW: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!jit_compile_op_table_grow(cc, tbl_idx)) + return false; + break; + } + + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!jit_compile_op_table_size(cc, tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!jit_compile_op_table_fill(cc, tbl_idx)) + return false; + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + default: + jit_set_last_error(cc, "unsupported opcode"); + return false; + } + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + uint8 bin_op, op_type; + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + if (opcode != WASM_OP_ATOMIC_FENCE) { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + } + switch (opcode) { + case WASM_OP_ATOMIC_WAIT32: + if (!jit_compile_op_atomic_wait(cc, VALUE_TYPE_I32, + align, offset, 4)) + return false; + break; + case WASM_OP_ATOMIC_WAIT64: + if (!jit_compile_op_atomic_wait(cc, VALUE_TYPE_I64, + align, offset, 8)) + return false; + break; + case WASM_OP_ATOMIC_NOTIFY: + if (!jit_compiler_op_atomic_notify(cc, align, offset, + bytes)) + return false; + break; + case WASM_OP_ATOMIC_FENCE: + /* Skip memory index */ + frame_ip++; + if (!jit_compiler_op_atomic_fence(cc)) + return false; + break; + case WASM_OP_ATOMIC_I32_LOAD: + bytes = 4; + goto op_atomic_i32_load; + case WASM_OP_ATOMIC_I32_LOAD8_U: + bytes = 1; + goto op_atomic_i32_load; + case WASM_OP_ATOMIC_I32_LOAD16_U: + bytes = 2; + op_atomic_i32_load: + if (!jit_compile_op_i32_load(cc, align, offset, bytes, + sign, true)) + return false; + break; + + case WASM_OP_ATOMIC_I64_LOAD: + bytes = 8; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD8_U: + bytes = 1; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD16_U: + bytes = 2; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD32_U: + bytes = 4; + op_atomic_i64_load: + if (!jit_compile_op_i64_load(cc, align, offset, bytes, + sign, true)) + return false; + break; + + case WASM_OP_ATOMIC_I32_STORE: + bytes = 4; + goto op_atomic_i32_store; + case WASM_OP_ATOMIC_I32_STORE8: + bytes = 1; + goto op_atomic_i32_store; + case WASM_OP_ATOMIC_I32_STORE16: + bytes = 2; + op_atomic_i32_store: + if (!jit_compile_op_i32_store(cc, align, offset, bytes, + true)) + return false; + break; + + case WASM_OP_ATOMIC_I64_STORE: + bytes = 8; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE8: + bytes = 1; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE16: + bytes = 2; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE32: + bytes = 4; + op_atomic_i64_store: + if (!jit_compile_op_i64_store(cc, align, offset, bytes, + true)) + return false; + break; + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + bytes = 4; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + bytes = 8; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + bytes = 1; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + bytes = 2; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + bytes = 1; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + bytes = 2; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + bytes = 4; + op_type = VALUE_TYPE_I64; + op_atomic_cmpxchg: + if (!jit_compile_op_atomic_cmpxchg(cc, op_type, align, + offset, bytes)) + return false; + break; + + COMPILE_ATOMIC_RMW(Add, ADD); + COMPILE_ATOMIC_RMW(Sub, SUB); + COMPILE_ATOMIC_RMW(And, AND); + COMPILE_ATOMIC_RMW(Or, OR); + COMPILE_ATOMIC_RMW(Xor, XOR); + COMPILE_ATOMIC_RMW(Xchg, XCHG); + + build_atomic_rmw: + if (!jit_compile_op_atomic_rmw(cc, bin_op, op_type, + align, offset, bytes)) + return false; + break; + + default: + jit_set_last_error(cc, "unsupported opcode"); + return false; + } + break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + + default: + jit_set_last_error(cc, "unsupported opcode"); + return false; + } + /* Error may occur when creating registers, basic blocks, insns, + consts and labels, in which the return value may be unchecked, + here we check again */ + if (jit_get_last_error(cc)) { + return false; + } + } + + (void)func_idx; + return true; +fail: + return false; +} + +JitBasicBlock * +jit_frontend_translate_func(JitCompContext *cc) +{ + JitFrame *jit_frame; + JitBasicBlock *basic_block_entry; + + if (!(jit_frame = init_func_translation(cc))) { + return NULL; + } + + if (!(basic_block_entry = create_func_block(cc))) { + return NULL; + } + + if (!jit_compile_func(cc)) { + return NULL; + } + + return basic_block_entry; +} + +uint32 +jit_frontend_get_jitted_return_addr_offset() +{ + return (uint32)offsetof(WASMInterpFrame, jitted_return_addr); +} diff --git a/core/iwasm/fast-jit/jit_frontend.h b/core/iwasm/fast-jit/jit_frontend.h new file mode 100644 index 0000000000..9065d23ec8 --- /dev/null +++ b/core/iwasm/fast-jit/jit_frontend.h @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_FRONTEND_H_ +#define _JIT_FRONTEND_H_ + +#include "jit_utils.h" +#include "jit_ir.h" +#include "../interpreter/wasm_interp.h" +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if WASM_ENABLE_AOT == 0 +typedef enum IntCond { + INT_EQZ = 0, + INT_EQ, + INT_NE, + INT_LT_S, + INT_LT_U, + INT_GT_S, + INT_GT_U, + INT_LE_S, + INT_LE_U, + INT_GE_S, + INT_GE_U +} IntCond; + +typedef enum FloatCond { + FLOAT_EQ = 0, + FLOAT_NE, + FLOAT_LT, + FLOAT_GT, + FLOAT_LE, + FLOAT_GE, + FLOAT_UNO +} FloatCond; +#else +#define IntCond AOTIntCond +#define FloatCond AOTFloatCond +#endif + +typedef enum IntArithmetic { + INT_ADD = 0, + INT_SUB, + INT_MUL, + INT_DIV_S, + INT_DIV_U, + INT_REM_S, + INT_REM_U +} IntArithmetic; + +typedef enum V128Arithmetic { + V128_ADD = 0, + V128_SUB, + V128_MUL, + V128_DIV, + V128_NEG, + V128_MIN, + V128_MAX, +} V128Arithmetic; + +typedef enum IntBitwise { + INT_AND = 0, + INT_OR, + INT_XOR, +} IntBitwise; + +typedef enum V128Bitwise { + V128_NOT, + V128_AND, + V128_ANDNOT, + V128_OR, + V128_XOR, + V128_BITSELECT, +} V128Bitwise; + +typedef enum IntShift { + INT_SHL = 0, + INT_SHR_S, + INT_SHR_U, + INT_ROTL, + INT_ROTR +} IntShift; + +typedef enum FloatMath { + FLOAT_ABS = 0, + FLOAT_NEG, + FLOAT_CEIL, + FLOAT_FLOOR, + FLOAT_TRUNC, + FLOAT_NEAREST, + FLOAT_SQRT +} FloatMath; + +typedef enum FloatArithmetic { + FLOAT_ADD = 0, + FLOAT_SUB, + FLOAT_MUL, + FLOAT_DIV, + FLOAT_MIN, + FLOAT_MAX, +} FloatArithmetic; + +#if WASM_ENABLE_SHARED_MEMORY != 0 +typedef enum AtomicRMWBinOp { + AtomicRMWBinOpAdd, + AtomicRMWBinOpSub, + AtomicRMWBinOpAnd, + AtomicRMWBinOpOr, + AtomicRMWBinOpXor, + AtomicRMWBinOpXchg +} AtomicRMWBinOp; +#endif + +/** + * Translate instructions in a function. The translated block must + * end with a branch instruction whose targets are offsets relating to + * the end bcip of the translated block, which are integral constants. + * If a target of a branch is really a constant value (which should be + * rare), put it into a register and then jump to the register instead + * of using the constant value directly in the target. In the + * translation process, don't create any new labels. The code bcip of + * the begin and end of the translated block is stored in the + * jit_annl_begin_bcip and jit_annl_end_bcip annotations of the label + * of the block, which must be the same as the bcips used in + * profiling. + * + * NOTE: the function must explicitly set SP to correct value when the + * entry's bcip is the function's entry address. + * + * @param cc containing compilation context of generated IR + * @param entry entry of the basic block to be translated. If its + * value is NULL, the function will clean up any pass local data that + * might be created previously. + * @param is_reached a bitmap recording which bytecode has been + * reached as a block entry + * + * @return IR block containing translated instructions if succeeds, + * NULL otherwise + */ +JitBasicBlock * +jit_frontend_translate_func(JitCompContext *cc); + +/** + * Lower the IR of the given compilation context. + * + * @param cc the compilation context + * + * @return true if succeeds, false otherwise + */ +bool +jit_frontend_lower(JitCompContext *cc); + +uint32 +jit_frontend_get_jitted_return_addr_offset(); + +uint32 +jit_frontend_get_global_data_offset(const WASMModule *module, + uint32 global_idx); + +uint32 +jit_frontend_get_table_inst_offset(const WASMModule *module, uint32 tbl_idx); + +uint32 +jit_frontend_get_module_inst_extra_offset(const WASMModule *module); + +JitReg +get_module_inst_reg(JitFrame *frame); + +JitReg +get_module_reg(JitFrame *frame); + +JitReg +get_import_func_ptrs_reg(JitFrame *frame); + +JitReg +get_fast_jit_func_ptrs_reg(JitFrame *frame); + +JitReg +get_func_type_indexes_reg(JitFrame *frame); + +JitReg +get_aux_stack_bound_reg(JitFrame *frame); + +JitReg +get_aux_stack_bottom_reg(JitFrame *frame); + +JitReg +get_memory_inst_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_cur_page_count_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_memory_data_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_memory_data_end_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_mem_bound_check_1byte_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_mem_bound_check_2bytes_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_mem_bound_check_4bytes_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_mem_bound_check_8bytes_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_mem_bound_check_16bytes_reg(JitFrame *frame, uint32 mem_idx); + +JitReg +get_table_elems_reg(JitFrame *frame, uint32 table_idx); + +JitReg +get_table_cur_size_reg(JitFrame *frame, uint32 table_idx); + +void +clear_fixed_virtual_regs(JitFrame *frame); + +void +clear_memory_regs(JitFrame *frame); + +void +clear_table_regs(JitFrame *frame); + +/** + * Get the offset from frame pointer to the n-th local variable slot. + * + * @param n the index to the local variable array + * + * @return the offset from frame pointer to the local variable slot + */ +static inline unsigned +offset_of_local(unsigned n) +{ + return offsetof(WASMInterpFrame, lp) + n * 4; +} + +/** + * Generate instruction to load an integer from the frame. + * + * This and the below gen_load_X functions generate instructions to + * load values from the frame into registers if the values have not + * been loaded yet. + * + * @param frame the frame information + * @param n slot index to the local variable array + * + * @return register holding the loaded value + */ +JitReg +gen_load_i32(JitFrame *frame, unsigned n); + +/** + * Generate instruction to load a i64 integer from the frame. + * + * @param frame the frame information + * @param n slot index to the local variable array + * + * @return register holding the loaded value + */ +JitReg +gen_load_i64(JitFrame *frame, unsigned n); + +/** + * Generate instruction to load a floating point value from the frame. + * + * @param frame the frame information + * @param n slot index to the local variable array + * + * @return register holding the loaded value + */ +JitReg +gen_load_f32(JitFrame *frame, unsigned n); + +/** + * Generate instruction to load a double value from the frame. + * + * @param frame the frame information + * @param n slot index to the local variable array + * + * @return register holding the loaded value + */ +JitReg +gen_load_f64(JitFrame *frame, unsigned n); + +/** + * Generate instructions to commit computation result to the frame. + * The general principle is to only commit values that will be used + * through the frame. + * + * @param frame the frame information + * @param begin the begin value slot to commit + * @param end the end value slot to commit + */ +void +gen_commit_values(JitFrame *frame, JitValueSlot *begin, JitValueSlot *end); + +/** + * Generate instructions to commit SP and IP pointers to the frame. + * + * @param frame the frame information + */ +void +gen_commit_sp_ip(JitFrame *frame); + +/** + * Generate commit instructions for the block end. + * + * @param frame the frame information + */ +static inline void +gen_commit_for_branch(JitFrame *frame) +{ + gen_commit_values(frame, frame->lp, frame->sp); +} + +/** + * Generate commit instructions for exception checks. + * + * @param frame the frame information + */ +static inline void +gen_commit_for_exception(JitFrame *frame) +{ + gen_commit_values(frame, frame->lp, frame->lp + frame->max_locals); + gen_commit_sp_ip(frame); +} + +/** + * Generate commit instructions to commit all status. + * + * @param frame the frame information + */ +static inline void +gen_commit_for_all(JitFrame *frame) +{ + gen_commit_values(frame, frame->lp, frame->sp); + gen_commit_sp_ip(frame); +} + +static inline void +clear_values(JitFrame *frame) +{ + size_t total_size = + sizeof(JitValueSlot) * (frame->max_locals + frame->max_stacks); + memset(frame->lp, 0, total_size); + frame->committed_sp = NULL; + frame->committed_ip = NULL; + clear_fixed_virtual_regs(frame); +} + +static inline void +push_i32(JitFrame *frame, JitReg value) +{ + frame->sp->reg = value; + frame->sp->dirty = 1; + frame->sp++; +} + +static inline void +push_i64(JitFrame *frame, JitReg value) +{ + frame->sp->reg = value; + frame->sp->dirty = 1; + frame->sp++; + frame->sp->reg = value; + frame->sp->dirty = 1; + frame->sp++; +} + +static inline void +push_f32(JitFrame *frame, JitReg value) +{ + push_i32(frame, value); +} + +static inline void +push_f64(JitFrame *frame, JitReg value) +{ + push_i64(frame, value); +} + +static inline JitReg +pop_i32(JitFrame *frame) +{ + frame->sp--; + return gen_load_i32(frame, frame->sp - frame->lp); +} + +static inline JitReg +pop_i64(JitFrame *frame) +{ + frame->sp -= 2; + return gen_load_i64(frame, frame->sp - frame->lp); +} + +static inline JitReg +pop_f32(JitFrame *frame) +{ + frame->sp--; + return gen_load_f32(frame, frame->sp - frame->lp); +} + +static inline JitReg +pop_f64(JitFrame *frame) +{ + frame->sp -= 2; + return gen_load_f64(frame, frame->sp - frame->lp); +} + +static inline void +pop(JitFrame *frame, int n) +{ + frame->sp -= n; + memset(frame->sp, 0, n * sizeof(*frame->sp)); +} + +static inline JitReg +local_i32(JitFrame *frame, int n) +{ + return gen_load_i32(frame, n); +} + +static inline JitReg +local_i64(JitFrame *frame, int n) +{ + return gen_load_i64(frame, n); +} + +static inline JitReg +local_f32(JitFrame *frame, int n) +{ + return gen_load_f32(frame, n); +} + +static inline JitReg +local_f64(JitFrame *frame, int n) +{ + return gen_load_f64(frame, n); +} + +static void +set_local_i32(JitFrame *frame, int n, JitReg val) +{ + frame->lp[n].reg = val; + frame->lp[n].dirty = 1; +} + +static void +set_local_i64(JitFrame *frame, int n, JitReg val) +{ + frame->lp[n].reg = val; + frame->lp[n].dirty = 1; + frame->lp[n + 1].reg = val; + frame->lp[n + 1].dirty = 1; +} + +static inline void +set_local_f32(JitFrame *frame, int n, JitReg val) +{ + set_local_i32(frame, n, val); +} + +static inline void +set_local_f64(JitFrame *frame, int n, JitReg val) +{ + set_local_i64(frame, n, val); +} + +#define POP(jit_value, value_type) \ + do { \ + if (!jit_cc_pop_value(cc, value_type, &jit_value)) \ + goto fail; \ + } while (0) + +#define POP_I32(v) POP(v, VALUE_TYPE_I32) +#define POP_I64(v) POP(v, VALUE_TYPE_I64) +#define POP_F32(v) POP(v, VALUE_TYPE_F32) +#define POP_F64(v) POP(v, VALUE_TYPE_F64) +#define POP_FUNCREF(v) POP(v, VALUE_TYPE_FUNCREF) +#define POP_EXTERNREF(v) POP(v, VALUE_TYPE_EXTERNREF) + +#define PUSH(jit_value, value_type) \ + do { \ + if (!jit_value) \ + goto fail; \ + if (!jit_cc_push_value(cc, value_type, jit_value)) \ + goto fail; \ + } while (0) + +#define PUSH_I32(v) PUSH(v, VALUE_TYPE_I32) +#define PUSH_I64(v) PUSH(v, VALUE_TYPE_I64) +#define PUSH_F32(v) PUSH(v, VALUE_TYPE_F32) +#define PUSH_F64(v) PUSH(v, VALUE_TYPE_F64) +#define PUSH_FUNCREF(v) PUSH(v, VALUE_TYPE_FUNCREF) +#define PUSH_EXTERNREF(v) PUSH(v, VALUE_TYPE_EXTERNREF) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/fast-jit/jit_ir.c b/core/iwasm/fast-jit/jit_ir.c new file mode 100644 index 0000000000..68503e3f5f --- /dev/null +++ b/core/iwasm/fast-jit/jit_ir.c @@ -0,0 +1,1427 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_ir.h" +#include "jit_codegen.h" +#include "jit_frontend.h" + +/** + * Operand kinds of instructions. + */ +enum { + JIT_OPND_KIND_Reg, + JIT_OPND_KIND_VReg, + JIT_OPND_KIND_LookupSwitch, +}; + +/** + * Operand kind of each instruction. + */ +static const uint8 insn_opnd_kind[] = { +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) JIT_OPND_KIND_##OPND_KIND, +#include "jit_ir.def" +#undef INSN +}; + +/** + * Operand number of each instruction. + */ +static const uint8 insn_opnd_num[] = { +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) OPND_NUM, +#include "jit_ir.def" +#undef INSN +}; + +/** + * Operand number of each instruction. + */ +static const uint8 insn_opnd_first_use[] = { +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) FIRST_USE, +#include "jit_ir.def" +#undef INSN +}; + +#define JIT_INSN_NEW_Reg(OPND_NUM) \ + jit_calloc(offsetof(JitInsn, _opnd) + sizeof(JitReg) * (OPND_NUM)) +#define JIT_INSN_NEW_VReg(OPND_NUM) \ + jit_calloc(offsetof(JitInsn, _opnd._opnd_VReg._reg) \ + + sizeof(JitReg) * (OPND_NUM)) + +JitInsn * +_jit_insn_new_Reg_0(JitOpcode opc) +{ + JitInsn *insn = JIT_INSN_NEW_Reg(0); + + if (insn) { + insn->opcode = opc; + } + + return insn; +} + +JitInsn * +_jit_insn_new_Reg_1(JitOpcode opc, JitReg r0) +{ + JitInsn *insn = JIT_INSN_NEW_Reg(1); + + if (insn) { + insn->opcode = opc; + *jit_insn_opnd(insn, 0) = r0; + } + + return insn; +} + +JitInsn * +_jit_insn_new_Reg_2(JitOpcode opc, JitReg r0, JitReg r1) +{ + JitInsn *insn = JIT_INSN_NEW_Reg(2); + + if (insn) { + insn->opcode = opc; + *jit_insn_opnd(insn, 0) = r0; + *jit_insn_opnd(insn, 1) = r1; + } + + return insn; +} + +JitInsn * +_jit_insn_new_Reg_3(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2) +{ + JitInsn *insn = JIT_INSN_NEW_Reg(3); + + if (insn) { + insn->opcode = opc; + *jit_insn_opnd(insn, 0) = r0; + *jit_insn_opnd(insn, 1) = r1; + *jit_insn_opnd(insn, 2) = r2; + } + + return insn; +} + +JitInsn * +_jit_insn_new_Reg_4(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2, JitReg r3) +{ + JitInsn *insn = JIT_INSN_NEW_Reg(4); + + if (insn) { + insn->opcode = opc; + *jit_insn_opnd(insn, 0) = r0; + *jit_insn_opnd(insn, 1) = r1; + *jit_insn_opnd(insn, 2) = r2; + *jit_insn_opnd(insn, 3) = r3; + } + + return insn; +} + +JitInsn * +_jit_insn_new_Reg_5(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2, JitReg r3, + JitReg r4) +{ + JitInsn *insn = JIT_INSN_NEW_Reg(5); + + if (insn) { + insn->opcode = opc; + *jit_insn_opnd(insn, 0) = r0; + *jit_insn_opnd(insn, 1) = r1; + *jit_insn_opnd(insn, 2) = r2; + *jit_insn_opnd(insn, 3) = r3; + *jit_insn_opnd(insn, 4) = r4; + } + + return insn; +} + +JitInsn * +_jit_insn_new_VReg_1(JitOpcode opc, JitReg r0, int n) +{ + JitInsn *insn = JIT_INSN_NEW_VReg(1 + n); + + if (insn) { + insn->opcode = opc; + insn->_opnd._opnd_VReg._reg_num = 1 + n; + *(jit_insn_opndv(insn, 0)) = r0; + } + + return insn; +} + +JitInsn * +_jit_insn_new_VReg_2(JitOpcode opc, JitReg r0, JitReg r1, int n) +{ + JitInsn *insn = JIT_INSN_NEW_VReg(2 + n); + + if (insn) { + insn->opcode = opc; + insn->_opnd._opnd_VReg._reg_num = 2 + n; + *(jit_insn_opndv(insn, 0)) = r0; + *(jit_insn_opndv(insn, 1)) = r1; + } + + return insn; +} + +JitInsn * +_jit_insn_new_LookupSwitch_1(JitOpcode opc, JitReg value, uint32 num) +{ + JitOpndLookupSwitch *opnd = NULL; + JitInsn *insn = + jit_calloc(offsetof(JitInsn, _opnd._opnd_LookupSwitch.match_pairs) + + sizeof(opnd->match_pairs[0]) * num); + + if (insn) { + insn->opcode = opc; + opnd = jit_insn_opndls(insn); + opnd->value = value; + opnd->match_pairs_num = num; + } + + return insn; +} + +#undef JIT_INSN_NEW_Reg +#undef JIT_INSN_NEW_VReg + +void +jit_insn_insert_before(JitInsn *insn1, JitInsn *insn2) +{ + bh_assert(insn1->prev); + insn1->prev->next = insn2; + insn2->prev = insn1->prev; + insn2->next = insn1; + insn1->prev = insn2; +} + +void +jit_insn_insert_after(JitInsn *insn1, JitInsn *insn2) +{ + bh_assert(insn1->next); + insn1->next->prev = insn2; + insn2->next = insn1->next; + insn2->prev = insn1; + insn1->next = insn2; +} + +void +jit_insn_unlink(JitInsn *insn) +{ + bh_assert(insn->prev); + insn->prev->next = insn->next; + bh_assert(insn->next); + insn->next->prev = insn->prev; + insn->prev = insn->next = NULL; +} + +unsigned +jit_insn_hash(JitInsn *insn) +{ + const uint8 opcode = insn->opcode; + unsigned hash = opcode, i; + + /* Currently, only instructions with Reg kind operand require + hashing. For others, simply use opcode as the hash value. */ + if (insn_opnd_kind[opcode] != JIT_OPND_KIND_Reg + || insn_opnd_num[opcode] < 1) + return hash; + + /* All the instructions with hashing support must be in the + assignment format, i.e. the first operand is the result (hence + being ignored) and all the others are operands. This is also + true for CHK instructions, whose first operand is the instruction + pointer. */ + for (i = 1; i < insn_opnd_num[opcode]; i++) + hash = ((hash << 5) - hash) + *(jit_insn_opnd(insn, i)); + + return hash; +} + +bool +jit_insn_equal(JitInsn *insn1, JitInsn *insn2) +{ + const uint8 opcode = insn1->opcode; + unsigned i; + + if (insn2->opcode != opcode) + return false; + + if (insn_opnd_kind[opcode] != JIT_OPND_KIND_Reg + || insn_opnd_num[opcode] < 1) + return false; + + for (i = 1; i < insn_opnd_num[opcode]; i++) + if (*(jit_insn_opnd(insn1, i)) != *(jit_insn_opnd(insn2, i))) + return false; + + return true; +} + +JitRegVec +jit_insn_opnd_regs(JitInsn *insn) +{ + JitRegVec vec = { 0 }; + JitOpndLookupSwitch *ls; + + vec._stride = 1; + + switch (insn_opnd_kind[insn->opcode]) { + case JIT_OPND_KIND_Reg: + vec.num = insn_opnd_num[insn->opcode]; + vec._base = jit_insn_opnd(insn, 0); + break; + + case JIT_OPND_KIND_VReg: + vec.num = jit_insn_opndv_num(insn); + vec._base = jit_insn_opndv(insn, 0); + break; + + case JIT_OPND_KIND_LookupSwitch: + ls = jit_insn_opndls(insn); + vec.num = ls->match_pairs_num + 2; + vec._base = &ls->value; + vec._stride = sizeof(ls->match_pairs[0]) / sizeof(*vec._base); + break; + } + + return vec; +} + +unsigned +jit_insn_opnd_first_use(JitInsn *insn) +{ + return insn_opnd_first_use[insn->opcode]; +} + +JitBasicBlock * +jit_basic_block_new(JitReg label, int n) +{ + JitBasicBlock *block = jit_insn_new_PHI(label, n); + if (!block) + return NULL; + + block->prev = block->next = block; + return block; +} + +void +jit_basic_block_delete(JitBasicBlock *block) +{ + JitInsn *insn, *next_insn, *end; + + if (!block) + return; + + insn = jit_basic_block_first_insn(block); + end = jit_basic_block_end_insn(block); + + for (; insn != end; insn = next_insn) { + next_insn = insn->next; + jit_insn_delete(insn); + } + + jit_insn_delete(block); +} + +JitRegVec +jit_basic_block_preds(JitBasicBlock *block) +{ + JitRegVec vec; + + vec.num = jit_insn_opndv_num(block) - 1; + vec._base = vec.num > 0 ? jit_insn_opndv(block, 1) : NULL; + vec._stride = 1; + + return vec; +} + +JitRegVec +jit_basic_block_succs(JitBasicBlock *block) +{ + JitInsn *last_insn = jit_basic_block_last_insn(block); + JitRegVec vec; + + vec.num = 0; + vec._base = NULL; + vec._stride = 1; + + switch (last_insn->opcode) { + case JIT_OP_JMP: + vec.num = 1; + vec._base = jit_insn_opnd(last_insn, 0); + break; + + case JIT_OP_BEQ: + case JIT_OP_BNE: + case JIT_OP_BGTS: + case JIT_OP_BGES: + case JIT_OP_BLTS: + case JIT_OP_BLES: + case JIT_OP_BGTU: + case JIT_OP_BGEU: + case JIT_OP_BLTU: + case JIT_OP_BLEU: + vec.num = 2; + vec._base = jit_insn_opnd(last_insn, 1); + break; + + case JIT_OP_LOOKUPSWITCH: + { + JitOpndLookupSwitch *opnd = jit_insn_opndls(last_insn); + vec.num = opnd->match_pairs_num + 1; + vec._base = &opnd->default_target; + vec._stride = sizeof(opnd->match_pairs[0]) / sizeof(*vec._base); + break; + } + + default: + vec._stride = 0; + } + + return vec; +} + +JitCompContext * +jit_cc_init(JitCompContext *cc, unsigned htab_size) +{ + JitBasicBlock *entry_block, *exit_block; + unsigned i, num; + + memset(cc, 0, sizeof(*cc)); + cc->_reference_count = 1; + jit_annl_enable_basic_block(cc); + + /* Create entry and exit blocks. They must be the first two + blocks respectively. */ + if (!(entry_block = jit_cc_new_basic_block(cc, 0))) + goto fail; + + if (!(exit_block = jit_cc_new_basic_block(cc, 0))) { + jit_basic_block_delete(entry_block); + goto fail; + } + + /* Record the entry and exit labels, whose indexes must be 0 and 1 + respectively. */ + cc->entry_label = jit_basic_block_label(entry_block); + cc->exit_label = jit_basic_block_label(exit_block); + bh_assert(jit_reg_no(cc->entry_label) == 0 + && jit_reg_no(cc->exit_label) == 1); + + if (!(cc->exce_basic_blocks = + jit_calloc(sizeof(JitBasicBlock *) * EXCE_NUM))) + goto fail; + + if (!(cc->incoming_insns_for_exec_bbs = + jit_calloc(sizeof(JitIncomingInsnList) * EXCE_NUM))) + goto fail; + + cc->hreg_info = jit_codegen_get_hreg_info(); + bh_assert(cc->hreg_info->info[JIT_REG_KIND_I32].num > 3); + + /* Initialize virtual registers for hard registers. */ + for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) { + if ((num = cc->hreg_info->info[i].num)) { + /* Initialize the capacity to be large enough. */ + jit_cc_new_reg(cc, i); + bh_assert(cc->_ann._reg_capacity[i] > num); + cc->_ann._reg_num[i] = num; + } + } + + /* Create registers for frame pointer, exec_env and cmp. */ + cc->fp_reg = jit_reg_new(JIT_REG_KIND_PTR, cc->hreg_info->fp_hreg_index); + cc->exec_env_reg = + jit_reg_new(JIT_REG_KIND_PTR, cc->hreg_info->exec_env_hreg_index); + cc->cmp_reg = jit_reg_new(JIT_REG_KIND_I32, cc->hreg_info->cmp_hreg_index); + + cc->_const_val._hash_table_size = htab_size; + + if (!(cc->_const_val._hash_table = + jit_calloc(htab_size * sizeof(*cc->_const_val._hash_table)))) + goto fail; + + return cc; + +fail: + jit_cc_destroy(cc); + return NULL; +} + +void +jit_cc_destroy(JitCompContext *cc) +{ + unsigned i, end; + JitBasicBlock *block; + JitIncomingInsn *incoming_insn, *incoming_insn_next; + + jit_block_stack_destroy(&cc->block_stack); + + if (cc->jit_frame) { + if (cc->jit_frame->memory_regs) + jit_free(cc->jit_frame->memory_regs); + if (cc->jit_frame->table_regs) + jit_free(cc->jit_frame->table_regs); + jit_free(cc->jit_frame); + } + + if (cc->memory_regs) + jit_free(cc->memory_regs); + + if (cc->table_regs) + jit_free(cc->table_regs); + + jit_free(cc->_const_val._hash_table); + + /* Release the instruction hash table. */ + jit_cc_disable_insn_hash(cc); + + jit_free(cc->exce_basic_blocks); + + if (cc->incoming_insns_for_exec_bbs) { + for (i = 0; i < EXCE_NUM; i++) { + incoming_insn = cc->incoming_insns_for_exec_bbs[i]; + while (incoming_insn) { + incoming_insn_next = incoming_insn->next; + jit_free(incoming_insn); + incoming_insn = incoming_insn_next; + } + } + jit_free(cc->incoming_insns_for_exec_bbs); + } + + /* Release entry and exit blocks. */ + if (0 != cc->entry_label) + jit_basic_block_delete(jit_cc_entry_basic_block(cc)); + if (0 != cc->exit_label) + jit_basic_block_delete(jit_cc_exit_basic_block(cc)); + + /* clang-format off */ + /* Release blocks and instructions. */ + JIT_FOREACH_BLOCK(cc, i, end, block) + { + jit_basic_block_delete(block); + } + /* clang-format on */ + + /* Release constant values. */ + for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) { + jit_free(cc->_const_val._value[i]); + jit_free(cc->_const_val._next[i]); + } + + /* Release storage of annotations. */ +#define ANN_LABEL(TYPE, NAME) jit_annl_disable_##NAME(cc); +#define ANN_INSN(TYPE, NAME) jit_anni_disable_##NAME(cc); +#define ANN_REG(TYPE, NAME) jit_annr_disable_##NAME(cc); +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG +} + +void +jit_cc_delete(JitCompContext *cc) +{ + if (cc && --cc->_reference_count == 0) { + jit_cc_destroy(cc); + jit_free(cc); + } +} + +/* + * Reallocate a memory block with the new_size. + * TODO: replace this with imported jit_realloc when it's available. + */ +static void * +_jit_realloc(void *ptr, unsigned new_size, unsigned old_size) +{ + void *new_ptr = jit_malloc(new_size); + + if (new_ptr) { + bh_assert(new_size > old_size); + + if (ptr) { + memcpy(new_ptr, ptr, old_size); + memset((uint8 *)new_ptr + old_size, 0, new_size - old_size); + jit_free(ptr); + } + else + memset(new_ptr, 0, new_size); + } + + return new_ptr; +} + +static unsigned +hash_of_const(unsigned kind, unsigned size, void *val) +{ + uint8 *p = (uint8 *)val, *end = p + size; + unsigned hash = kind; + + do + hash = ((hash << 5) - hash) + *p++; + while (p != end); + + return hash; +} + +static inline void * +address_of_const(JitCompContext *cc, JitReg reg, unsigned size) +{ + int kind = jit_reg_kind(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._value[kind] + size * idx; +} + +static inline JitReg +next_of_const(JitCompContext *cc, JitReg reg) +{ + int kind = jit_reg_kind(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]; +} + +/** + * Put a constant value into the compilation context. + * + * @param cc compilation context + * @param kind register kind + * @param size size of the value + * @param val pointer to value which must be aligned + * + * @return a constant register containing the value + */ +static JitReg +_jit_cc_new_const(JitCompContext *cc, int kind, unsigned size, void *val) +{ + unsigned num = cc->_const_val._num[kind], slot; + unsigned capacity = cc->_const_val._capacity[kind]; + uint8 *new_value; + JitReg r, *new_next; + + bh_assert(num <= capacity); + + /* Find the existing value first. */ + slot = hash_of_const(kind, size, val) % cc->_const_val._hash_table_size; + r = cc->_const_val._hash_table[slot]; + + for (; r; r = next_of_const(cc, r)) + if (jit_reg_kind(r) == kind + && !memcmp(val, address_of_const(cc, r, size), size)) + return r; + + if (num == capacity) { + /* Increase the space of value and next. */ + capacity = capacity > 0 ? (capacity + capacity / 2) : 16; + new_value = _jit_realloc(cc->_const_val._value[kind], size * capacity, + size * num); + new_next = + _jit_realloc(cc->_const_val._next[kind], + sizeof(*new_next) * capacity, sizeof(*new_next) * num); + + if (new_value && new_next) { + cc->_const_val._value[kind] = new_value; + cc->_const_val._next[kind] = new_next; + } + else { + jit_set_last_error(cc, "create const register failed"); + jit_free(new_value); + jit_free(new_next); + return 0; + } + + cc->_const_val._capacity[kind] = capacity; + } + + bh_assert(num + 1 < (uint32)_JIT_REG_CONST_IDX_FLAG); + r = jit_reg_new(kind, _JIT_REG_CONST_IDX_FLAG | num); + memcpy(cc->_const_val._value[kind] + size * num, val, size); + cc->_const_val._next[kind][num] = cc->_const_val._hash_table[slot]; + cc->_const_val._hash_table[slot] = r; + cc->_const_val._num[kind] = num + 1; + + return r; +} + +static inline int32 +get_const_val_in_reg(JitReg reg) +{ + int shift = 8 * sizeof(reg) - _JIT_REG_KIND_SHIFT + 1; + return ((int32)(reg << shift)) >> shift; +} + +#define _JIT_CC_NEW_CONST_HELPER(KIND, TYPE, val) \ + do { \ + JitReg reg = jit_reg_new( \ + JIT_REG_KIND_##KIND, \ + (_JIT_REG_CONST_VAL_FLAG | ((JitReg)val & ~_JIT_REG_KIND_MASK))); \ + \ + if ((TYPE)get_const_val_in_reg(reg) == val) \ + return reg; \ + return _jit_cc_new_const(cc, JIT_REG_KIND_##KIND, sizeof(val), &val); \ + } while (0) + +JitReg +jit_cc_new_const_I32_rel(JitCompContext *cc, int32 val, uint32 rel) +{ + uint64 val64 = (uint64)(uint32)val | ((uint64)rel << 32); + _JIT_CC_NEW_CONST_HELPER(I32, uint64, val64); +} + +JitReg +jit_cc_new_const_I64(JitCompContext *cc, int64 val) +{ + _JIT_CC_NEW_CONST_HELPER(I64, int64, val); +} + +JitReg +jit_cc_new_const_F32(JitCompContext *cc, float val) +{ + int32 float_neg_zero = 0x80000000; + + if (!memcmp(&val, &float_neg_zero, sizeof(float))) + /* Create const -0.0f */ + return _jit_cc_new_const(cc, JIT_REG_KIND_F32, sizeof(float), &val); + + _JIT_CC_NEW_CONST_HELPER(F32, float, val); +} + +JitReg +jit_cc_new_const_F64(JitCompContext *cc, double val) +{ + int64 double_neg_zero = 0x8000000000000000ll; + + if (!memcmp(&val, &double_neg_zero, sizeof(double))) + /* Create const -0.0d */ + return _jit_cc_new_const(cc, JIT_REG_KIND_F64, sizeof(double), &val); + + _JIT_CC_NEW_CONST_HELPER(F64, double, val); +} + +#undef _JIT_CC_NEW_CONST_HELPER + +#define _JIT_CC_GET_CONST_HELPER(KIND, TYPE) \ + do { \ + bh_assert(jit_reg_kind(reg) == JIT_REG_KIND_##KIND); \ + bh_assert(jit_reg_is_const(reg)); \ + \ + return (jit_reg_is_const_val(reg) \ + ? (TYPE)get_const_val_in_reg(reg) \ + : *(TYPE *)(address_of_const(cc, reg, sizeof(TYPE)))); \ + } while (0) + +static uint64 +jit_cc_get_const_I32_helper(JitCompContext *cc, JitReg reg) +{ + _JIT_CC_GET_CONST_HELPER(I32, uint64); +} + +uint32 +jit_cc_get_const_I32_rel(JitCompContext *cc, JitReg reg) +{ + return (uint32)(jit_cc_get_const_I32_helper(cc, reg) >> 32); +} + +int32 +jit_cc_get_const_I32(JitCompContext *cc, JitReg reg) +{ + return (int32)(jit_cc_get_const_I32_helper(cc, reg)); +} + +int64 +jit_cc_get_const_I64(JitCompContext *cc, JitReg reg) +{ + _JIT_CC_GET_CONST_HELPER(I64, int64); +} + +float +jit_cc_get_const_F32(JitCompContext *cc, JitReg reg) +{ + _JIT_CC_GET_CONST_HELPER(F32, float); +} + +double +jit_cc_get_const_F64(JitCompContext *cc, JitReg reg) +{ + _JIT_CC_GET_CONST_HELPER(F64, double); +} + +#undef _JIT_CC_GET_CONST_HELPER + +#define _JIT_REALLOC_ANN(TYPE, NAME, ANN, POSTFIX) \ + if (successful && cc->_ann._##ANN##_##NAME##_enabled) { \ + TYPE *ptr = _jit_realloc(cc->_ann._##ANN##_##NAME POSTFIX, \ + sizeof(TYPE) * capacity, sizeof(TYPE) * num); \ + if (ptr) \ + cc->_ann._##ANN##_##NAME POSTFIX = ptr; \ + else \ + successful = false; \ + } + +JitReg +jit_cc_new_label(JitCompContext *cc) +{ + unsigned num = cc->_ann._label_num; + unsigned capacity = cc->_ann._label_capacity; + bool successful = true; + + bh_assert(num <= capacity); + + if (num == capacity) { + capacity = capacity > 0 ? (capacity + capacity / 2) : 16; + +#define EMPTY_POSTFIX +#define ANN_LABEL(TYPE, NAME) _JIT_REALLOC_ANN(TYPE, NAME, label, EMPTY_POSTFIX) +#include "jit_ir.def" +#undef ANN_LABEL +#undef EMPTY_POSTFIX + + if (!successful) { + jit_set_last_error(cc, "create label register failed"); + return 0; + } + + cc->_ann._label_capacity = capacity; + } + + cc->_ann._label_num = num + 1; + + return jit_reg_new(JIT_REG_KIND_L32, num); +} + +JitBasicBlock * +jit_cc_new_basic_block(JitCompContext *cc, int n) +{ + JitReg label = jit_cc_new_label(cc); + JitBasicBlock *block = NULL; + + if (label && (block = jit_basic_block_new(label, n))) + /* Void 0 register indicates error in creation. */ + *(jit_annl_basic_block(cc, label)) = block; + else + jit_set_last_error(cc, "create basic block failed"); + + return block; +} + +JitBasicBlock * +jit_cc_resize_basic_block(JitCompContext *cc, JitBasicBlock *block, int n) +{ + JitReg label = jit_basic_block_label(block); + JitInsn *insn = jit_basic_block_first_insn(block); + JitBasicBlock *new_block = jit_basic_block_new(label, n); + + if (!new_block) { + jit_set_last_error(cc, "resize basic block failed"); + return NULL; + } + + jit_insn_unlink(block); + + if (insn != block) + jit_insn_insert_before(insn, new_block); + + bh_assert(*(jit_annl_basic_block(cc, label)) == block); + *(jit_annl_basic_block(cc, label)) = new_block; + jit_insn_delete(block); + + return new_block; +} + +bool +jit_cc_enable_insn_hash(JitCompContext *cc, unsigned n) +{ + if (jit_anni_is_enabled__hash_link(cc)) + return true; + + if (!jit_anni_enable__hash_link(cc)) + return false; + + /* The table must not exist. */ + bh_assert(!cc->_insn_hash_table._table); + + /* Integer overflow cannot happen because n << 4G (at most several + times of 64K in the most extreme case). */ + if (!(cc->_insn_hash_table._table = + jit_calloc(n * sizeof(*cc->_insn_hash_table._table)))) { + jit_anni_disable__hash_link(cc); + return false; + } + + cc->_insn_hash_table._size = n; + return true; +} + +void +jit_cc_disable_insn_hash(JitCompContext *cc) +{ + jit_anni_disable__hash_link(cc); + jit_free(cc->_insn_hash_table._table); + cc->_insn_hash_table._table = NULL; + cc->_insn_hash_table._size = 0; +} + +void +jit_cc_reset_insn_hash(JitCompContext *cc) +{ + if (jit_anni_is_enabled__hash_link(cc)) + memset(cc->_insn_hash_table._table, 0, + cc->_insn_hash_table._size + * sizeof(*cc->_insn_hash_table._table)); +} + +JitInsn * +jit_cc_set_insn_uid(JitCompContext *cc, JitInsn *insn) +{ + if (insn) { + unsigned num = cc->_ann._insn_num; + unsigned capacity = cc->_ann._insn_capacity; + bool successful = true; + + bh_assert(num <= capacity); + + if (num == capacity) { + capacity = capacity > 0 ? (capacity + capacity / 2) : 64; + +#define EMPTY_POSTFIX +#define ANN_INSN(TYPE, NAME) _JIT_REALLOC_ANN(TYPE, NAME, insn, EMPTY_POSTFIX) +#include "jit_ir.def" +#undef ANN_INSN +#undef EMPTY_POSTFIX + + if (!successful) { + jit_set_last_error(cc, "set insn uid failed"); + return NULL; + } + + cc->_ann._insn_capacity = capacity; + } + + cc->_ann._insn_num = num + 1; + insn->uid = num; + } + + return insn; +} + +JitInsn * +_jit_cc_set_insn_uid_for_new_insn(JitCompContext *cc, JitInsn *insn) +{ + if (jit_cc_set_insn_uid(cc, insn)) + return insn; + + jit_insn_delete(insn); + return NULL; +} + +JitReg +jit_cc_new_reg(JitCompContext *cc, unsigned kind) +{ + unsigned num = jit_cc_reg_num(cc, kind); + unsigned capacity = cc->_ann._reg_capacity[kind]; + bool successful = true; + + bh_assert(num <= capacity); + + if (num == capacity) { + capacity = (capacity == 0 + /* Initialize the capacity to be larger than hard + register number. */ + ? cc->hreg_info->info[kind].num + 16 + : capacity + capacity / 2); + +#define ANN_REG(TYPE, NAME) _JIT_REALLOC_ANN(TYPE, NAME, reg, [kind]) +#include "jit_ir.def" +#undef ANN_REG + + if (!successful) { + jit_set_last_error(cc, "create register failed"); + return 0; + } + + cc->_ann._reg_capacity[kind] = capacity; + } + + cc->_ann._reg_num[kind] = num + 1; + + return jit_reg_new(kind, num); +} + +#undef _JIT_REALLOC_ANN + +#define ANN_LABEL(TYPE, NAME) \ + bool jit_annl_enable_##NAME(JitCompContext *cc) \ + { \ + if (cc->_ann._label_##NAME##_enabled) \ + return true; \ + \ + if (cc->_ann._label_capacity > 0 \ + && !(cc->_ann._label_##NAME = \ + jit_calloc(cc->_ann._label_capacity * sizeof(TYPE)))) { \ + jit_set_last_error(cc, "annl enable " #NAME "failed"); \ + return false; \ + } \ + \ + cc->_ann._label_##NAME##_enabled = 1; \ + return true; \ + } +#define ANN_INSN(TYPE, NAME) \ + bool jit_anni_enable_##NAME(JitCompContext *cc) \ + { \ + if (cc->_ann._insn_##NAME##_enabled) \ + return true; \ + \ + if (cc->_ann._insn_capacity > 0 \ + && !(cc->_ann._insn_##NAME = \ + jit_calloc(cc->_ann._insn_capacity * sizeof(TYPE)))) { \ + jit_set_last_error(cc, "anni enable " #NAME "failed"); \ + return false; \ + } \ + \ + cc->_ann._insn_##NAME##_enabled = 1; \ + return true; \ + } +#define ANN_REG(TYPE, NAME) \ + bool jit_annr_enable_##NAME(JitCompContext *cc) \ + { \ + unsigned k; \ + \ + if (cc->_ann._reg_##NAME##_enabled) \ + return true; \ + \ + for (k = JIT_REG_KIND_VOID; k < JIT_REG_KIND_L32; k++) \ + if (cc->_ann._reg_capacity[k] > 0 \ + && !(cc->_ann._reg_##NAME[k] = jit_calloc( \ + cc->_ann._reg_capacity[k] * sizeof(TYPE)))) { \ + jit_set_last_error(cc, "annr enable " #NAME "failed"); \ + jit_annr_disable_##NAME(cc); \ + return false; \ + } \ + \ + cc->_ann._reg_##NAME##_enabled = 1; \ + return true; \ + } +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + +#define ANN_LABEL(TYPE, NAME) \ + void jit_annl_disable_##NAME(JitCompContext *cc) \ + { \ + jit_free(cc->_ann._label_##NAME); \ + cc->_ann._label_##NAME = NULL; \ + cc->_ann._label_##NAME##_enabled = 0; \ + } +#define ANN_INSN(TYPE, NAME) \ + void jit_anni_disable_##NAME(JitCompContext *cc) \ + { \ + jit_free(cc->_ann._insn_##NAME); \ + cc->_ann._insn_##NAME = NULL; \ + cc->_ann._insn_##NAME##_enabled = 0; \ + } +#define ANN_REG(TYPE, NAME) \ + void jit_annr_disable_##NAME(JitCompContext *cc) \ + { \ + unsigned k; \ + \ + for (k = JIT_REG_KIND_VOID; k < JIT_REG_KIND_L32; k++) { \ + jit_free(cc->_ann._reg_##NAME[k]); \ + cc->_ann._reg_##NAME[k] = NULL; \ + } \ + \ + cc->_ann._reg_##NAME##_enabled = 0; \ + } +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + +char * +jit_get_last_error(JitCompContext *cc) +{ + return cc->last_error[0] == '\0' ? NULL : cc->last_error; +} + +void +jit_set_last_error_v(JitCompContext *cc, const char *format, ...) +{ + va_list args; + va_start(args, format); + vsnprintf(cc->last_error, sizeof(cc->last_error), format, args); + va_end(args); +} + +void +jit_set_last_error(JitCompContext *cc, const char *error) +{ + if (error) + snprintf(cc->last_error, sizeof(cc->last_error), "Error: %s", error); + else + cc->last_error[0] = '\0'; +} + +bool +jit_cc_update_cfg(JitCompContext *cc) +{ + JitBasicBlock *block; + unsigned block_index, end, succ_index, idx; + JitReg *target; + bool retval = false; + + if (!jit_annl_enable_pred_num(cc)) + return false; + + /* Update pred_num of all blocks. */ + JIT_FOREACH_BLOCK_ENTRY_EXIT(cc, block_index, end, block) + { + JitRegVec succs = jit_basic_block_succs(block); + + JIT_REG_VEC_FOREACH(succs, succ_index, target) + if (jit_reg_is_kind(L32, *target)) + *(jit_annl_pred_num(cc, *target)) += 1; + } + + /* Resize predecessor vectors of body blocks. */ + JIT_FOREACH_BLOCK(cc, block_index, end, block) + { + if (!jit_cc_resize_basic_block( + cc, block, + *(jit_annl_pred_num(cc, jit_basic_block_label(block))))) + goto cleanup_and_return; + } + + /* Fill in predecessor vectors all blocks. */ + JIT_FOREACH_BLOCK_REVERSE_ENTRY_EXIT(cc, block_index, block) + { + JitRegVec succs = jit_basic_block_succs(block), preds; + + JIT_REG_VEC_FOREACH(succs, succ_index, target) + if (jit_reg_is_kind(L32, *target)) { + preds = jit_basic_block_preds(*(jit_annl_basic_block(cc, *target))); + bh_assert(*(jit_annl_pred_num(cc, *target)) > 0); + idx = *(jit_annl_pred_num(cc, *target)) - 1; + *(jit_annl_pred_num(cc, *target)) = idx; + *(jit_reg_vec_at(&preds, idx)) = jit_basic_block_label(block); + } + } + + retval = true; + +cleanup_and_return: + jit_annl_disable_pred_num(cc); + return retval; +} + +void +jit_value_stack_push(JitValueStack *stack, JitValue *value) +{ + if (!stack->value_list_head) + stack->value_list_head = stack->value_list_end = value; + else { + stack->value_list_end->next = value; + value->prev = stack->value_list_end; + stack->value_list_end = value; + } +} + +JitValue * +jit_value_stack_pop(JitValueStack *stack) +{ + JitValue *value = stack->value_list_end; + + bh_assert(stack->value_list_end); + + if (stack->value_list_head == stack->value_list_end) + stack->value_list_head = stack->value_list_end = NULL; + else { + stack->value_list_end = stack->value_list_end->prev; + stack->value_list_end->next = NULL; + value->prev = NULL; + } + + return value; +} + +void +jit_value_stack_destroy(JitValueStack *stack) +{ + JitValue *value = stack->value_list_head, *p; + + while (value) { + p = value->next; + jit_free(value); + value = p; + } + + stack->value_list_head = NULL; + stack->value_list_end = NULL; +} + +void +jit_block_stack_push(JitBlockStack *stack, JitBlock *block) +{ + if (!stack->block_list_head) + stack->block_list_head = stack->block_list_end = block; + else { + stack->block_list_end->next = block; + block->prev = stack->block_list_end; + stack->block_list_end = block; + } +} + +JitBlock * +jit_block_stack_top(JitBlockStack *stack) +{ + return stack->block_list_end; +} + +JitBlock * +jit_block_stack_pop(JitBlockStack *stack) +{ + JitBlock *block = stack->block_list_end; + + bh_assert(stack->block_list_end); + + if (stack->block_list_head == stack->block_list_end) + stack->block_list_head = stack->block_list_end = NULL; + else { + stack->block_list_end = stack->block_list_end->prev; + stack->block_list_end->next = NULL; + block->prev = NULL; + } + + return block; +} + +void +jit_block_stack_destroy(JitBlockStack *stack) +{ + JitBlock *block = stack->block_list_head, *p; + + while (block) { + p = block->next; + jit_value_stack_destroy(&block->value_stack); + jit_block_destroy(block); + block = p; + } + + stack->block_list_head = NULL; + stack->block_list_end = NULL; +} + +bool +jit_block_add_incoming_insn(JitBlock *block, JitInsn *insn, uint32 opnd_idx) +{ + JitIncomingInsn *incoming_insn; + + if (!(incoming_insn = jit_calloc((uint32)sizeof(JitIncomingInsn)))) + return false; + + incoming_insn->insn = insn; + incoming_insn->opnd_idx = opnd_idx; + incoming_insn->next = block->incoming_insns_for_end_bb; + block->incoming_insns_for_end_bb = incoming_insn; + return true; +} + +void +jit_block_destroy(JitBlock *block) +{ + JitIncomingInsn *incoming_insn, *incoming_insn_next; + + jit_value_stack_destroy(&block->value_stack); + if (block->param_types) + jit_free(block->param_types); + if (block->result_types) + jit_free(block->result_types); + + incoming_insn = block->incoming_insns_for_end_bb; + while (incoming_insn) { + incoming_insn_next = incoming_insn->next; + jit_free(incoming_insn); + incoming_insn = incoming_insn_next; + } + + jit_free(block); +} + +static inline uint8 +to_stack_value_type(uint8 type) +{ +#if WASM_ENABLE_REF_TYPES != 0 + if (type == VALUE_TYPE_EXTERNREF || type == VALUE_TYPE_FUNCREF) + return VALUE_TYPE_I32; +#endif + return type; +} + +bool +jit_cc_pop_value(JitCompContext *cc, uint8 type, JitReg *p_value) +{ + JitValue *jit_value = NULL; + JitReg value = 0; + + if (!jit_block_stack_top(&cc->block_stack)) { + jit_set_last_error(cc, "WASM block stack underflow"); + return false; + } + if (!jit_block_stack_top(&cc->block_stack)->value_stack.value_list_end) { + jit_set_last_error(cc, "WASM data stack underflow"); + return false; + } + + jit_value = jit_value_stack_pop( + &jit_block_stack_top(&cc->block_stack)->value_stack); + bh_assert(jit_value); + + if (jit_value->type != to_stack_value_type(type)) { + jit_set_last_error(cc, "invalid WASM stack data type"); + jit_free(jit_value); + return false; + } + + switch (jit_value->type) { + case VALUE_TYPE_I32: + value = pop_i32(cc->jit_frame); + break; + case VALUE_TYPE_I64: + value = pop_i64(cc->jit_frame); + break; + case VALUE_TYPE_F32: + value = pop_f32(cc->jit_frame); + break; + case VALUE_TYPE_F64: + value = pop_f64(cc->jit_frame); + break; + default: + bh_assert(0); + break; + } + + bh_assert(cc->jit_frame->sp == jit_value->value); + bh_assert(value == jit_value->value->reg); + *p_value = value; + jit_free(jit_value); + return true; +} + +bool +jit_cc_push_value(JitCompContext *cc, uint8 type, JitReg value) +{ + JitValue *jit_value; + + if (!jit_block_stack_top(&cc->block_stack)) { + jit_set_last_error(cc, "WASM block stack underflow"); + return false; + } + + if (!(jit_value = jit_calloc(sizeof(JitValue)))) { + jit_set_last_error(cc, "allocate memory failed"); + return false; + } + + bh_assert(value); + + jit_value->type = to_stack_value_type(type); + jit_value->value = cc->jit_frame->sp; + jit_value_stack_push(&jit_block_stack_top(&cc->block_stack)->value_stack, + jit_value); + + switch (jit_value->type) { + case VALUE_TYPE_I32: + push_i32(cc->jit_frame, value); + break; + case VALUE_TYPE_I64: + push_i64(cc->jit_frame, value); + break; + case VALUE_TYPE_F32: + push_f32(cc->jit_frame, value); + break; + case VALUE_TYPE_F64: + push_f64(cc->jit_frame, value); + break; + } + + return true; +} + +bool +_jit_insn_check_opnd_access_Reg(const JitInsn *insn, unsigned n) +{ + unsigned opcode = insn->opcode; + return (insn_opnd_kind[opcode] == JIT_OPND_KIND_Reg + && n < insn_opnd_num[opcode]); +} + +bool +_jit_insn_check_opnd_access_VReg(const JitInsn *insn, unsigned n) +{ + unsigned opcode = insn->opcode; + return (insn_opnd_kind[opcode] == JIT_OPND_KIND_VReg + && n < insn->_opnd._opnd_VReg._reg_num); +} + +bool +_jit_insn_check_opnd_access_LookupSwitch(const JitInsn *insn) +{ + unsigned opcode = insn->opcode; + return (insn_opnd_kind[opcode] == JIT_OPND_KIND_LookupSwitch); +} + +bool +jit_lock_reg_in_insn(JitCompContext *cc, JitInsn *the_insn, JitReg reg_to_lock) +{ + bool ret = false; + JitInsn *prevent_spill = NULL; + JitInsn *indicate_using = NULL; + + if (!the_insn) + goto just_return; + + if (jit_cc_is_hreg_fixed(cc, reg_to_lock)) { + ret = true; + goto just_return; + } + + /** + * give the virtual register of the locked hard register a minimum, non-zero + * distance, * so as to prevent it from being spilled out + */ + prevent_spill = jit_insn_new_MOV(reg_to_lock, reg_to_lock); + if (!prevent_spill) + goto just_return; + + jit_insn_insert_before(the_insn, prevent_spill); + + /** + * announce the locked hard register is being used, and do necessary spill + * ASAP + */ + indicate_using = jit_insn_new_MOV(reg_to_lock, reg_to_lock); + if (!indicate_using) + goto just_return; + + jit_insn_insert_after(the_insn, indicate_using); + + ret = true; + +just_return: + if (!ret) + jit_set_last_error(cc, "generate insn failed"); + return ret; +} diff --git a/core/iwasm/fast-jit/jit_ir.def b/core/iwasm/fast-jit/jit_ir.def new file mode 100644 index 0000000000..046bea1ff5 --- /dev/null +++ b/core/iwasm/fast-jit/jit_ir.def @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * @file jit-ir.def + * + * @brief Definition of JIT IR instructions and annotations. + */ + +/** + * @def INSN (NAME, OPND_KIND, OPND_NUM, FIRST_USE) + * + * Definition of IR instructions + * + * @param NAME name of the opcode + * @param OPND_KIND kind of the operand(s) + * @param OPND_NUM number of the operand(s) + * @param FIRST_USE index of the first use register + * + * @p OPND_KIND and @p OPND_NUM together determine the format of an + * instruction. There are four kinds of formats: + * + * 1) Reg: fixed-number register operands, @p OPND_NUM specifies the + * number of operands; + * + * 2) VReg: variable-number register operands, @p OPND_NUM specifies + * the number of fixed register operands; + * + * 3) TableSwitch: tableswitch instruction's format, @p OPND_NUM must + * be 1; + * + * 4) LookupSwitch: lookupswitch instruction's format, @p OPND_NUM + * must be 1. + * + * Instruction operands are all registers and they are organized in an + * order that all registers defined by the instruction, if any, appear + * before the registers used by the instruction. The @p FIRST_USE is + * the index of the first use register in the register vector sorted + * in this order. Use @c jit_insn_opnd_regs to get the register + * vector in this order and use @c jit_insn_opnd_first_use to get the + * index of the first use register. + * + * Every instruction with name @p NAME has the following definitions: + * + * @c JEFF_OP_NAME: the enum opcode of insn NAME + * @c jit_insn_new_NAME (...): creates a new instance of insn NAME + * + * An instruction is deleted by function: + * + * @c jit_insn_delete (@p insn) + * + * In the scope of this IR's terminology, operand and argument have + * different meanings. The operand is a general notation, which + * denotes every raw operand of an instruction, while the argument + * only denotes the variable part of operands of instructions of VReg + * kind. For example, a VReg instruction phi node "r0 = phi(r1, r2)" + * has three operands opnd[0]: r0, opnd[1]: r1 and opnd[2]: r2, but + * only two arguments arg[0]: r1 and arg[1]: r2. Operands or + * arguments of instructions with various formats can be access + * through the following APIs: + * + * @c jit_insn_opnd (@p insn, @p n): for Reg_N formats + * @c jit_insn_opndv (@p insn, @p n): for VReg_N formats + * @c jit_insn_opndv_num (@p insn): for VReg_N formats + * @c jit_insn_opndts (@p insn): for TableSwitch_1 format + * @c jit_insn_opndls (@p insn): for LookupSwitch_1 format + */ + +#ifndef INSN +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) +#endif + +/* Move and conversion instructions that transfer values among + registers of the same kind (move) or different kinds (convert) */ +INSN(MOV, Reg, 2, 1) +INSN(PHI, VReg, 1, 1) + +/* conversion. will extend or truncate */ +INSN(I8TOI32, Reg, 2, 1) +INSN(I8TOI64, Reg, 2, 1) +INSN(I16TOI32, Reg, 2, 1) +INSN(I16TOI64, Reg, 2, 1) +INSN(I32TOI8, Reg, 2, 1) +INSN(I32TOU8, Reg, 2, 1) +INSN(I32TOI16, Reg, 2, 1) +INSN(I32TOU16, Reg, 2, 1) +INSN(I32TOI64, Reg, 2, 1) +INSN(I32TOF32, Reg, 2, 1) +INSN(I32TOF64, Reg, 2, 1) +INSN(U32TOI64, Reg, 2, 1) +INSN(U32TOF32, Reg, 2, 1) +INSN(U32TOF64, Reg, 2, 1) +INSN(I64TOI8, Reg, 2, 1) +INSN(I64TOI16, Reg, 2, 1) +INSN(I64TOI32, Reg, 2, 1) +INSN(I64TOF32, Reg, 2, 1) +INSN(I64TOF64, Reg, 2, 1) +INSN(F32TOI32, Reg, 2, 1) +INSN(F32TOI64, Reg, 2, 1) +INSN(F32TOF64, Reg, 2, 1) +INSN(F32TOU32, Reg, 2, 1) +INSN(F64TOI32, Reg, 2, 1) +INSN(F64TOI64, Reg, 2, 1) +INSN(F64TOF32, Reg, 2, 1) +INSN(F64TOU32, Reg, 2, 1) + +/** + * Re-interpret binary presentations: + * *(i32 *)&f32, *(i64 *)&f64, *(f32 *)&i32, *(f64 *)&i64 + */ +INSN(I32CASTF32, Reg, 2, 1) +INSN(I64CASTF64, Reg, 2, 1) +INSN(F32CASTI32, Reg, 2, 1) +INSN(F64CASTI64, Reg, 2, 1) + +/* Arithmetic and bitwise instructions: */ +INSN(NEG, Reg, 2, 1) +INSN(NOT, Reg, 2, 1) +INSN(ADD, Reg, 3, 1) +INSN(SUB, Reg, 3, 1) +INSN(MUL, Reg, 3, 1) +INSN(DIV_S, Reg, 3, 1) +INSN(REM_S, Reg, 3, 1) +INSN(DIV_U, Reg, 3, 1) +INSN(REM_U, Reg, 3, 1) +INSN(SHL, Reg, 3, 1) +INSN(SHRS, Reg, 3, 1) +INSN(SHRU, Reg, 3, 1) +INSN(ROTL, Reg, 3, 1) +INSN(ROTR, Reg, 3, 1) +INSN(OR, Reg, 3, 1) +INSN(XOR, Reg, 3, 1) +INSN(AND, Reg, 3, 1) +INSN(CMP, Reg, 3, 1) +INSN(MAX, Reg, 3, 1) +INSN(MIN, Reg, 3, 1) +INSN(CLZ, Reg, 2, 1) +INSN(CTZ, Reg, 2, 1) +INSN(POPCNT, Reg, 2, 1) + +/* Select instruction: */ +INSN(SELECTEQ, Reg, 4, 1) +INSN(SELECTNE, Reg, 4, 1) +INSN(SELECTGTS, Reg, 4, 1) +INSN(SELECTGES, Reg, 4, 1) +INSN(SELECTLTS, Reg, 4, 1) +INSN(SELECTLES, Reg, 4, 1) +INSN(SELECTGTU, Reg, 4, 1) +INSN(SELECTGEU, Reg, 4, 1) +INSN(SELECTLTU, Reg, 4, 1) +INSN(SELECTLEU, Reg, 4, 1) + +/* Memory access instructions: */ +INSN(LDEXECENV, Reg, 1, 1) +INSN(LDJITINFO, Reg, 1, 1) +INSN(LDI8, Reg, 3, 1) +INSN(LDU8, Reg, 3, 1) +INSN(LDI16, Reg, 3, 1) +INSN(LDU16, Reg, 3, 1) +INSN(LDI32, Reg, 3, 1) +INSN(LDU32, Reg, 3, 1) +INSN(LDI64, Reg, 3, 1) +INSN(LDU64, Reg, 3, 1) +INSN(LDF32, Reg, 3, 1) +INSN(LDF64, Reg, 3, 1) +INSN(LDPTR, Reg, 3, 1) +INSN(LDV64, Reg, 3, 1) +INSN(LDV128, Reg, 3, 1) +INSN(LDV256, Reg, 3, 1) +INSN(STI8, Reg, 3, 0) +INSN(STI16, Reg, 3, 0) +INSN(STI32, Reg, 3, 0) +INSN(STI64, Reg, 3, 0) +INSN(STF32, Reg, 3, 0) +INSN(STF64, Reg, 3, 0) +INSN(STPTR, Reg, 3, 0) +INSN(STV64, Reg, 3, 1) +INSN(STV128, Reg, 3, 1) +INSN(STV256, Reg, 3, 1) + +/* Control instructions */ +INSN(JMP, Reg, 1, 0) +INSN(BEQ, Reg, 3, 0) +INSN(BNE, Reg, 3, 0) +INSN(BGTS, Reg, 3, 0) +INSN(BGES, Reg, 3, 0) +INSN(BLTS, Reg, 3, 0) +INSN(BLES, Reg, 3, 0) +INSN(BGTU, Reg, 3, 0) +INSN(BGEU, Reg, 3, 0) +INSN(BLTU, Reg, 3, 0) +INSN(BLEU, Reg, 3, 0) +INSN(LOOKUPSWITCH, LookupSwitch, 1, 0) + +/* Call and return instructions */ +INSN(CALLNATIVE, VReg, 2, 1) +INSN(CALLBC, Reg, 4, 2) +INSN(RETURNBC, Reg, 3, 0) +INSN(RETURN, Reg, 1, 0) + +#if WASM_ENABLE_SHARED_MEMORY != 0 +/* Atomic Memory Accesses */ +/* op1(replacement val) op2(expected val) op3(mem data) op4(offset) + * and in x86, the result is stored in register al/ax/eax/rax */ +INSN(AT_CMPXCHGU8, Reg, 4, 0) +INSN(AT_CMPXCHGU16, Reg, 4, 0) +INSN(AT_CMPXCHGI32, Reg, 4, 0) +INSN(AT_CMPXCHGU32, Reg, 4, 0) +INSN(AT_CMPXCHGI64, Reg, 4, 0) +/* rmw operations: + * op1(read value) op2(operand value) op3(mem data) op4(offset) */ +INSN(AT_ADDU8, Reg, 4, 1) +INSN(AT_ADDU16, Reg, 4, 1) +INSN(AT_ADDI32, Reg, 4, 1) +INSN(AT_ADDU32, Reg, 4, 1) +INSN(AT_ADDI64, Reg, 4, 1) +INSN(AT_SUBU8, Reg, 4, 1) +INSN(AT_SUBU16, Reg, 4, 1) +INSN(AT_SUBI32, Reg, 4, 1) +INSN(AT_SUBU32, Reg, 4, 1) +INSN(AT_SUBI64, Reg, 4, 1) +INSN(AT_ANDU8, Reg, 4, 1) +INSN(AT_ANDU16, Reg, 4, 1) +INSN(AT_ANDI32, Reg, 4, 1) +INSN(AT_ANDU32, Reg, 4, 1) +INSN(AT_ANDI64, Reg, 4, 1) +INSN(AT_ORU8, Reg, 4, 1) +INSN(AT_ORU16, Reg, 4, 1) +INSN(AT_ORI32, Reg, 4, 1) +INSN(AT_ORU32, Reg, 4, 1) +INSN(AT_ORI64, Reg, 4, 1) +INSN(AT_XORU8, Reg, 4, 1) +INSN(AT_XORU16, Reg, 4, 1) +INSN(AT_XORI32, Reg, 4, 1) +INSN(AT_XORU32, Reg, 4, 1) +INSN(AT_XORI64, Reg, 4, 1) +INSN(AT_XCHGU8, Reg, 4, 1) +INSN(AT_XCHGU16, Reg, 4, 1) +INSN(AT_XCHGI32, Reg, 4, 1) +INSN(AT_XCHGU32, Reg, 4, 1) +INSN(AT_XCHGI64, Reg, 4, 1) +INSN(FENCE, Reg, 0, 0) +#endif + +#undef INSN + +/** + * @def ANN_LABEL (TYPE, NAME) + * + * Definition of label annotations. + * + * @param TYPE type of the annotation + * @param NAME name of the annotation + * + * Each defined annotation with name NAME has the following APIs: + * + * @c jit_annl_NAME (cc, label): accesses the annotation NAME of + * label @p label + * @c jit_annl_enable_NAME (cc): enables the annotation NAME + * @c jit_annl_disable_NAME (cc): disables the annotation NAME + * @c jit_annl_is_enabled_NAME (cc): check whether the annotation NAME + * is enabled + */ + +#ifndef ANN_LABEL +#define ANN_LABEL(TYPE, NAME) +#endif + +/* Basic Block of a label. */ +ANN_LABEL(JitBasicBlock *, basic_block) +/* Predecessor number of the block that is only used in + jit_cc_update_cfg for updating the CFG. */ +ANN_LABEL(uint16, pred_num) +/* Execution frequency of a block. We can split critical edges with + empty blocks so we don't need to store frequencies of edges. */ +ANN_LABEL(uint16, freq) +/* Begin bytecode instruction pointer of the block. */ +ANN_LABEL(uint8 *, begin_bcip) +/* End bytecode instruction pointer of the block. */ +ANN_LABEL(uint8 *, end_bcip) +/* Stack pointer offset at the end of the block. */ +ANN_LABEL(uint16, end_sp) +/* The label of the next physically adjacent block. */ +ANN_LABEL(JitReg, next_label) +/* Compiled code address of the block. */ +ANN_LABEL(void *, jitted_addr) + +#undef ANN_LABEL + +/** + * @def ANN_INSN (TYPE, NAME) + * + * Definition of instruction annotations. + * + * @param TYPE type of the annotation + * @param NAME name of the annotation + * + * Each defined annotation with name NAME has the following APIs: + * + * @c jit_anni_NAME (cc, insn): accesses the annotation NAME of + * instruction @p insn + * @c jit_anni_enable_NAME (cc): enables the annotation NAME + * @c jit_anni_disable_NAME (cc): disables the annotation NAME + * @c jit_anni_is_enabled_NAME (cc): check whether the annotation NAME + * is enabled + */ + +#ifndef ANN_INSN +#define ANN_INSN(TYPE, NAME) +#endif + +/* A private annotation for linking instructions with the same hash + value, which is only used by the compilation context's hash table + of instructions. */ +ANN_INSN(JitInsn *, _hash_link) + +#undef ANN_INSN + +/** + * @def ANN_REG (TYPE, NAME) + * + * Definition of register annotations. + * + * @param TYPE type of the annotation + * @param NAME name of the annotation + * + * Each defined annotation with name NAME has the following APIs: + * + * @c jit_annr_NAME (cc, reg): accesses the annotation NAME of + * register @p reg + * @c jit_annr_enable_NAME (cc): enables the annotation NAME + * @c jit_annr_disable_NAME (cc): disables the annotation NAME + * @c jit_annr_is_enabled_NAME (cc): check whether the annotation NAME + * is enabled + */ + +#ifndef ANN_REG +#define ANN_REG(TYPE, NAME) +#endif + +/* Defining instruction of registers satisfying SSA property. */ +ANN_REG(JitInsn *, def_insn) + +#undef ANN_REG diff --git a/core/iwasm/fast-jit/jit_ir.h b/core/iwasm/fast-jit/jit_ir.h new file mode 100644 index 0000000000..6b3acfa0b4 --- /dev/null +++ b/core/iwasm/fast-jit/jit_ir.h @@ -0,0 +1,1882 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_IR_H_ +#define _JIT_IR_H_ + +#include "bh_platform.h" +#include "../interpreter/wasm.h" +#include "jit_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Register (operand) representation of JIT IR. + * + * Encoding: [4-bit: kind, 28-bit register no.] + * + * Registers in JIT IR are classified into different kinds according + * to types of values they can hold. The classification is based on + * most processors' hardware register classifications, which include + * various sets of integer, floating point and vector registers with + * different sizes. These registers can be mapped onto corresponding + * kinds of hardware registers by register allocator. Instructions + * can only operate on allowed kinds of registers. For example, an + * integer instruction cannot operate on floating point or vector + * registers. Some encodings of these kinds of registers also + * represent immediate constant values and indexes to constant tables + * (see below). In that case, those registers are read-only. Writing + * to them is illegal. Reading from an immediate constant value + * register always returns the constant value encoded in the register + * no. Reading from a constant table index register always returns + * the constant value stored at the encoded index of the constant + * table of the register's kind. Immediate constant values and values + * indexed by constant table indexes can only be loaded into the + * corresponding kinds of registers if they must be loaded into + * registers. Besides these common kinds of registers, labels of + * basic blocks are also treated as registers of a special kind, which + * hold code addresses of basic block labels and are read-only. Each + * basic block is assigned one unique label register. With this + * unification, we can use the same set of load instructions to load + * values either from addresses stored in normal registers or from + * addresses of labels. Besides these register kinds, the void kind + * is a special kind of registers to denote some error occurs when a + * normal register is expected. Or it can be used as result operand + * of call and invoke instructions to denote no return values. The + * variable registers are classified into two sets: the hard registers + * whose register numbers are less than the hard register numbers of + * their kinds and the virtual registers whose register numbers are + * greater than or equal to the hard register numbers. Before + * register allocation is done, hard registers may appear in the IR + * due to special usages of passes frontend (e.g. fp_reg and exec_env_reg) + * or lower_cg. In the mean time (including during register + * allocation), those hard registers are treated same as virtual + * registers except that they may not be SSA and they can only be + * allocated to the hard registers of themselves. + * + * Classification of registers: + * + void register (kind == JIT_REG_KIND_VOID, no. must be 0) + * + label registers (kind == JIT_REG_KIND_L32) + * + value registers (kind == JIT_REG_KIND_I32/I64/F32/F64/V64/V128/V256) + * | + constants (_JIT_REG_CONST_VAL_FLAG | _JIT_REG_CONST_IDX_FLAG) + * | | + constant values (_JIT_REG_CONST_VAL_FLAG) + * | | + constant indexes (_JIT_REG_CONST_IDX_FLAG) + * | + variables (!(_JIT_REG_CONST_VAL_FLAG | _JIT_REG_CONST_IDX_FLAG)) + * | | + hard registers (no. < hard register number) + * | | + virtual registers (no. >= hard register number) + */ +typedef uint32 JitReg; + +/* + * Mask and shift bits of register kind. + */ +#define _JIT_REG_KIND_MASK 0xf0000000 +#define _JIT_REG_KIND_SHIFT 28 + +/* + * Mask of register no. which must be the least significant bits. + */ +#define _JIT_REG_NO_MASK (~_JIT_REG_KIND_MASK) + +/* + * Constant value flag (the most significant bit) of register + * no. field of integer, floating point and vector registers. If this + * flag is set in the register no., the rest bits of register + * no. represent a signed (27-bit) integer constant value of the + * corresponding type of the register and the register is read-only. + */ +#define _JIT_REG_CONST_VAL_FLAG ((_JIT_REG_NO_MASK >> 1) + 1) + +/* + * Constant index flag of non-constant-value (constant value flag is + * not set in register no. field) integer, floating point and vector + * registers. If this flag is set, the rest bits of the register + * no. represent an index to the constant value table of the + * corresponding type of the register and the register is read-only. + */ +#define _JIT_REG_CONST_IDX_FLAG (_JIT_REG_CONST_VAL_FLAG >> 1) + +/** + * Register kinds. Don't change the order of the defined values. The + * L32 kind must be after all normal kinds (see _const_val and _reg_ann + * of JitCompContext). + */ +typedef enum JitRegKind { + JIT_REG_KIND_VOID = 0x00, /* void type */ + JIT_REG_KIND_I32 = 0x01, /* 32-bit signed or unsigned integer */ + JIT_REG_KIND_I64 = 0x02, /* 64-bit signed or unsigned integer */ + JIT_REG_KIND_F32 = 0x03, /* 32-bit floating point */ + JIT_REG_KIND_F64 = 0x04, /* 64-bit floating point */ + JIT_REG_KIND_V64 = 0x05, /* 64-bit vector */ + JIT_REG_KIND_V128 = 0x06, /* 128-bit vector */ + JIT_REG_KIND_V256 = 0x07, /* 256-bit vector */ + JIT_REG_KIND_L32 = 0x08, /* 32-bit label address */ + JIT_REG_KIND_NUM /* number of register kinds */ +} JitRegKind; + +#if UINTPTR_MAX == UINT64_MAX +#define JIT_REG_KIND_PTR JIT_REG_KIND_I64 +#else +#define JIT_REG_KIND_PTR JIT_REG_KIND_I32 +#endif + +/** + * Construct a new JIT IR register from the kind and no. + * + * @param reg_kind register kind + * @param reg_no register no. + * + * @return the new register with the given kind and no. + */ +static inline JitReg +jit_reg_new(unsigned reg_kind, unsigned reg_no) +{ + return (JitReg)((reg_kind << _JIT_REG_KIND_SHIFT) | reg_no); +} + +/** + * Get the register kind of the given register. + * + * @param r a JIT IR register + * + * @return the register kind of register r + */ +static inline int +jit_reg_kind(JitReg r) +{ + return (r & _JIT_REG_KIND_MASK) >> _JIT_REG_KIND_SHIFT; +} + +/** + * Get the register no. of the given JIT IR register. + * + * @param r a JIT IR register + * + * @return the register no. of register r + */ +static inline int +jit_reg_no(JitReg r) +{ + return r & _JIT_REG_NO_MASK; +} + +/** + * Check whether the given register is a normal value register. + * + * @param r a JIT IR register + * + * @return true iff the register is a normal value register + */ +static inline bool +jit_reg_is_value(JitReg r) +{ + unsigned kind = jit_reg_kind(r); + return kind > JIT_REG_KIND_VOID && kind < JIT_REG_KIND_L32; +} + +/** + * Check whether the given register is a constant value. + * + * @param r a JIT IR register + * + * @return true iff register r is a constant value + */ +static inline bool +jit_reg_is_const_val(JitReg r) +{ + return jit_reg_is_value(r) && (r & _JIT_REG_CONST_VAL_FLAG); +} + +/** + * Check whether the given register is a constant table index. + * + * @param r a JIT IR register + * + * @return true iff register r is a constant table index + */ +static inline bool +jit_reg_is_const_idx(JitReg r) +{ + return (jit_reg_is_value(r) && !jit_reg_is_const_val(r) + && (r & _JIT_REG_CONST_IDX_FLAG)); +} + +/** + * Check whether the given register is a constant. + * + * @param r a JIT IR register + * + * @return true iff register r is a constant + */ +static inline bool +jit_reg_is_const(JitReg r) +{ + return (jit_reg_is_value(r) + && (r & (_JIT_REG_CONST_VAL_FLAG | _JIT_REG_CONST_IDX_FLAG))); +} + +/** + * Check whether the given register is a normal variable register. + * + * @param r a JIT IR register + * + * @return true iff the register is a normal variable register + */ +static inline bool +jit_reg_is_variable(JitReg r) +{ + return (jit_reg_is_value(r) + && !(r & (_JIT_REG_CONST_VAL_FLAG | _JIT_REG_CONST_IDX_FLAG))); +} + +/** + * Test whether the register is the given kind. + * + * @param KIND register kind name + * @param R register + * + * @return true if the register is the given kind + */ +#define jit_reg_is_kind(KIND, R) (jit_reg_kind(R) == JIT_REG_KIND_##KIND) + +/** + * Construct a zero IR register with given the kind. + * + * @param kind the kind of the value + * + * @return a constant register of zero + */ +static inline JitReg +jit_reg_new_zero(unsigned kind) +{ + bh_assert(kind != JIT_REG_KIND_VOID && kind < JIT_REG_KIND_L32); + return jit_reg_new(kind, _JIT_REG_CONST_VAL_FLAG); +} + +/** + * Test whether the register is a zero constant value. + * + * @param reg an IR register + * + * @return true iff the register is a constant zero + */ +static inline JitReg +jit_reg_is_zero(JitReg reg) +{ + return (jit_reg_is_value(reg) + && jit_reg_no(reg) == _JIT_REG_CONST_VAL_FLAG); +} + +/** + * Operand of instructions with fixed-number register operand(s). + */ +typedef JitReg JitOpndReg; + +/** + * Operand of instructions with variable-number register operand(s). + */ +typedef struct JitOpndVReg { + uint32 _reg_num; + JitReg _reg[1]; +} JitOpndVReg; + +/** + * Operand of lookupswitch instruction. + */ +typedef struct JitOpndLookupSwitch { + /* NOTE: distance between JitReg operands must be the same (see + jit_insn_opnd_regs). */ + JitReg value; /* the value to be compared */ + uint32 match_pairs_num; /* match pairs number */ + /* NOTE: offset between adjacent targets must be sizeof + (match_pairs[0]) (see implementation of jit_basic_block_succs), + so the default_target field must be here. */ + JitReg default_target; /* default target BB */ + struct { + int32 value; /* match value of the match pair */ + JitReg target; /* target BB of the match pair */ + } match_pairs[1]; /* match pairs of the instruction */ +} JitOpndLookupSwitch; + +/** + * Instruction of JIT IR. + */ +typedef struct JitInsn { + /* Pointers to the previous and next instructions. */ + struct JitInsn *prev; + struct JitInsn *next; + + /* Opcode of the instruction. */ + uint16 opcode; + + /* Reserved field that may be used by optimizations locally. + * bit_0(Least Significant Bit) is atomic flag for load/store */ + uint8 flags_u8; + + /* The unique ID of the instruction. */ + uint16 uid; + + /* Operands for different kinds of instructions. */ + union { + /* For instructions with fixed-number register operand(s). */ + JitOpndReg _opnd_Reg[1]; + + /* For instructions with variable-number register operand(s). */ + JitOpndVReg _opnd_VReg; + + /* For lookupswitch instruction. */ + JitOpndLookupSwitch _opnd_LookupSwitch; + } _opnd; +} JitInsn; + +/** + * Opcodes of IR instructions. + */ +typedef enum JitOpcode { +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) JIT_OP_##NAME, +#include "jit_ir.def" +#undef INSN + JIT_OP_OPCODE_NUMBER +} JitOpcode; + +/* + * Helper functions for creating new instructions. Don't call them + * directly. Use jit_insn_new_NAME, such as jit_insn_new_MOV instead. + */ + +JitInsn * +_jit_insn_new_Reg_0(JitOpcode opc); +JitInsn * +_jit_insn_new_Reg_1(JitOpcode opc, JitReg r0); +JitInsn * +_jit_insn_new_Reg_2(JitOpcode opc, JitReg r0, JitReg r1); +JitInsn * +_jit_insn_new_Reg_3(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2); +JitInsn * +_jit_insn_new_Reg_4(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2, JitReg r3); +JitInsn * +_jit_insn_new_Reg_5(JitOpcode opc, JitReg r0, JitReg r1, JitReg r2, JitReg r3, + JitReg r4); +JitInsn * +_jit_insn_new_VReg_1(JitOpcode opc, JitReg r0, int n); +JitInsn * +_jit_insn_new_VReg_2(JitOpcode opc, JitReg r0, JitReg r1, int n); +JitInsn * +_jit_insn_new_LookupSwitch_1(JitOpcode opc, JitReg value, uint32 num); + +/* + * Instruction creation functions jit_insn_new_NAME, where NAME is the + * name of the instruction defined in jit_ir.def. + */ +#define ARG_DECL_Reg_0 +#define ARG_LIST_Reg_0 +#define ARG_DECL_Reg_1 JitReg r0 +#define ARG_LIST_Reg_1 , r0 +#define ARG_DECL_Reg_2 JitReg r0, JitReg r1 +#define ARG_LIST_Reg_2 , r0, r1 +#define ARG_DECL_Reg_3 JitReg r0, JitReg r1, JitReg r2 +#define ARG_LIST_Reg_3 , r0, r1, r2 +#define ARG_DECL_Reg_4 JitReg r0, JitReg r1, JitReg r2, JitReg r3 +#define ARG_LIST_Reg_4 , r0, r1, r2, r3 +#define ARG_DECL_Reg_5 JitReg r0, JitReg r1, JitReg r2, JitReg r3, JitReg r4 +#define ARG_LIST_Reg_5 , r0, r1, r2, r3, r4 +#define ARG_DECL_VReg_1 JitReg r0, int n +#define ARG_LIST_VReg_1 , r0, n +#define ARG_DECL_VReg_2 JitReg r0, JitReg r1, int n +#define ARG_LIST_VReg_2 , r0, r1, n +#define ARG_DECL_LookupSwitch_1 JitReg value, uint32 num +#define ARG_LIST_LookupSwitch_1 , value, num +#define INSN(NAME, OPND_KIND, OPND_NUM, FIRST_USE) \ + static inline JitInsn *jit_insn_new_##NAME( \ + ARG_DECL_##OPND_KIND##_##OPND_NUM) \ + { \ + return _jit_insn_new_##OPND_KIND##_##OPND_NUM( \ + JIT_OP_##NAME ARG_LIST_##OPND_KIND##_##OPND_NUM); \ + } +#include "jit_ir.def" +#undef INSN +#undef ARG_DECL_Reg_0 +#undef ARG_LIST_Reg_0 +#undef ARG_DECL_Reg_1 +#undef ARG_LIST_Reg_1 +#undef ARG_DECL_Reg_2 +#undef ARG_LIST_Reg_2 +#undef ARG_DECL_Reg_3 +#undef ARG_LIST_Reg_3 +#undef ARG_DECL_Reg_4 +#undef ARG_LIST_Reg_4 +#undef ARG_DECL_Reg_5 +#undef ARG_LIST_Reg_5 +#undef ARG_DECL_VReg_1 +#undef ARG_LIST_VReg_1 +#undef ARG_DECL_VReg_2 +#undef ARG_LIST_VReg_2 +#undef ARG_DECL_LookupSwitch_1 +#undef ARG_LIST_LookupSwitch_1 + +/** + * Delete an instruction + * + * @param insn an instruction to be deleted + */ +static inline void +jit_insn_delete(JitInsn *insn) +{ + jit_free(insn); +} + +/* + * Runtime type check functions that check whether accessing the n-th + * operand is legal. They are only used for in self-verification + * mode. + * + * @param insn any JIT IR instruction + * @param n index of the operand to access + * + * @return true if the access is legal + */ +bool +_jit_insn_check_opnd_access_Reg(const JitInsn *insn, unsigned n); +bool +_jit_insn_check_opnd_access_VReg(const JitInsn *insn, unsigned n); +bool +_jit_insn_check_opnd_access_LookupSwitch(const JitInsn *insn); + +/** + * Get the pointer to the n-th register operand of the given + * instruction. The instruction format must be Reg. + * + * @param insn a Reg format instruction + * @param n index of the operand to get + * + * @return pointer to the n-th operand + */ +static inline JitReg * +jit_insn_opnd(JitInsn *insn, int n) +{ + bh_assert(_jit_insn_check_opnd_access_Reg(insn, n)); + return &insn->_opnd._opnd_Reg[n]; +} + +/** + * Get the pointer to the n-th register operand of the given + * instruction. The instruction format must be VReg. + * + * @param insn a VReg format instruction + * @param n index of the operand to get + * + * @return pointer to the n-th operand + */ +static inline JitReg * +jit_insn_opndv(JitInsn *insn, int n) +{ + bh_assert(_jit_insn_check_opnd_access_VReg(insn, n)); + return &insn->_opnd._opnd_VReg._reg[n]; +} + +/** + * Get the operand number of the given instruction. The instruction + * format must be VReg. + * + * @param insn a VReg format instruction + * + * @return operand number of the instruction + */ +static inline unsigned +jit_insn_opndv_num(const JitInsn *insn) +{ + bh_assert(_jit_insn_check_opnd_access_VReg(insn, 0)); + return insn->_opnd._opnd_VReg._reg_num; +} + +/** + * Get the pointer to the LookupSwitch operand of the given + * instruction. The instruction format must be LookupSwitch. + * + * @param insn a LookupSwitch format instruction + * + * @return pointer to the operand + */ +static inline JitOpndLookupSwitch * +jit_insn_opndls(JitInsn *insn) +{ + bh_assert(_jit_insn_check_opnd_access_LookupSwitch(insn)); + return &insn->_opnd._opnd_LookupSwitch; +} + +/** + * Insert instruction @p insn2 before instruction @p insn1. + * + * @param insn1 any instruction + * @param insn2 any instruction + */ +void +jit_insn_insert_before(JitInsn *insn1, JitInsn *insn2); + +/** + * Insert instruction @p insn2 after instruction @p insn1. + * + * @param insn1 any instruction + * @param insn2 any instruction + */ +void +jit_insn_insert_after(JitInsn *insn1, JitInsn *insn2); + +/** + * Unlink the instruction @p insn from the containing list. + * + * @param insn an instruction + */ +void +jit_insn_unlink(JitInsn *insn); + +/** + * Get the hash value of the comparable instruction (pure functions + * and exception check instructions). + * + * @param insn an instruction + * + * @return hash value of the instruction + */ +unsigned +jit_insn_hash(JitInsn *insn); + +/** + * Compare whether the two comparable instructions are the same. + * + * @param insn1 the first instruction + * @param insn2 the second instruction + * + * @return true if the two instructions are the same + */ +bool +jit_insn_equal(JitInsn *insn1, JitInsn *insn2); + +/** + * Register vector for accessing predecessors and successors of a + * basic block. + */ +typedef struct JitRegVec { + JitReg *_base; /* points to the first register */ + int32 _stride; /* stride to the next register */ + uint32 num; /* number of registers */ +} JitRegVec; + +/** + * Get the address of the i-th register in the register vector. + * + * @param vec a register vector + * @param i index to the register vector + * + * @return the address of the i-th register in the vector + */ +static inline JitReg * +jit_reg_vec_at(const JitRegVec *vec, unsigned i) +{ + bh_assert(i < vec->num); + return vec->_base + vec->_stride * i; +} + +/** + * Visit each element in a register vector. + * + * @param V (JitRegVec) the register vector + * @param I (unsigned) index variable in the vector + * @param R (JitReg *) resiger pointer variable + */ +#define JIT_REG_VEC_FOREACH(V, I, R) \ + for ((I) = 0, (R) = (V)._base; (I) < (V).num; (I)++, (R) += (V)._stride) + +/** + * Visit each register defined by an instruction. + * + * @param V (JitRegVec) register vector of the instruction + * @param I (unsigned) index variable in the vector + * @param R (JitReg *) resiger pointer variable + * @param F index of the first used register + */ +#define JIT_REG_VEC_FOREACH_DEF(V, I, R, F) \ + for ((I) = 0, (R) = (V)._base; (I) < (F); (I)++, (R) += (V)._stride) + +/** + * Visit each register used by an instruction. + * + * @param V (JitRegVec) register vector of the instruction + * @param I (unsigned) index variable in the vector + * @param R (JitReg *) resiger pointer variable + * @param F index of the first used register + */ +#define JIT_REG_VEC_FOREACH_USE(V, I, R, F) \ + for ((I) = (F), (R) = (V)._base + (F) * (V)._stride; (I) < (V).num; \ + (I)++, (R) += (V)._stride) + +/** + * Get a generic register vector that contains all register operands. + * The registers defined by the instruction, if any, appear before the + * registers used by the instruction. + * + * @param insn an instruction + * + * @return a register vector containing register operands + */ +JitRegVec +jit_insn_opnd_regs(JitInsn *insn); + +/** + * Get the index of the first use register in the register vector + * returned by jit_insn_opnd_regs. + * + * @param insn an instruction + * + * @return the index of the first use register in the register vector + */ +unsigned +jit_insn_opnd_first_use(JitInsn *insn); + +/** + * Basic Block of JIT IR. It is a basic block only if the IR is not in + * non-BB form. The block is represented by a special phi node, whose + * result and arguments are label registers. The result label is the + * containing block's label. The arguments are labels of predecessors + * of the block. Successor labels are stored in the last instruction, + * which must be a control flow instruction. Instructions of a block + * are linked in a circular linked list with the block phi node as the + * end of the list. The next and prev field of the block phi node + * point to the first and last instructions of the block. + */ +typedef JitInsn JitBasicBlock; + +/** + * Create a new basic block instance. + * + * @param label the label of the new basic block + * @param n number of predecessors + * + * @return the created new basic block instance + */ +JitBasicBlock * +jit_basic_block_new(JitReg label, int n); + +/** + * Delete a basic block instance and all instructions init. + * + * @param block the basic block to be deleted + */ +void +jit_basic_block_delete(JitBasicBlock *block); + +/** + * Get the label of the basic block. + * + * @param block a basic block instance + * + * @return the label of the basic block + */ +static inline JitReg +jit_basic_block_label(JitBasicBlock *block) +{ + return *(jit_insn_opndv(block, 0)); +} + +/** + * Get the first instruction of the basic block. + * + * @param block a basic block instance + * + * @return the first instruction of the basic block + */ +static inline JitInsn * +jit_basic_block_first_insn(JitBasicBlock *block) +{ + return block->next; +} + +/** + * Get the last instruction of the basic block. + * + * @param block a basic block instance + * + * @return the last instruction of the basic block + */ +static inline JitInsn * +jit_basic_block_last_insn(JitBasicBlock *block) +{ + return block->prev; +} + +/** + * Get the end of instruction list of the basic block (which is always + * the block itself). + * + * @param block a basic block instance + * + * @return the end of instruction list of the basic block + */ +static inline JitInsn * +jit_basic_block_end_insn(JitBasicBlock *block) +{ + return block; +} + +/** + * Visit each instruction in the block from the first to the last. In + * the code block, the instruction pointer @p I must be a valid + * pointer to an instruction in the block. That means if the + * instruction may be deleted, @p I must point to the previous or next + * valid instruction before the next iteration. + * + * @param B (JitBasicBlock *) the block + * @param I (JitInsn *) instruction visited + */ +#define JIT_FOREACH_INSN(B, I) \ + for (I = jit_basic_block_first_insn(B); I != jit_basic_block_end_insn(B); \ + I = I->next) + +/** + * Visit each instruction in the block from the last to the first. In + * the code block, the instruction pointer @p I must be a valid + * pointer to an instruction in the block. That means if the + * instruction may be deleted, @p I must point to the previous or next + * valid instruction before the next iteration. + * + * @param B (JitBasicBlock *) the block + * @param I (JitInsn *) instruction visited + */ +#define JIT_FOREACH_INSN_REVERSE(B, I) \ + for (I = jit_basic_block_last_insn(B); I != jit_basic_block_end_insn(B); \ + I = I->prev) + +/** + * Prepend an instruction in the front of the block. The position is + * just after the block phi node (the block instance itself). + * + * @param block a block + * @param insn an instruction to be prepended + */ +static inline void +jit_basic_block_prepend_insn(JitBasicBlock *block, JitInsn *insn) +{ + jit_insn_insert_after(block, insn); +} + +/** + * Append an instruction to the end of the basic block. + * + * @param block a basic block + * @param insn an instruction to be appended + */ +static inline void +jit_basic_block_append_insn(JitBasicBlock *block, JitInsn *insn) +{ + jit_insn_insert_before(block, insn); +} + +/** + * Get the register vector of predecessors of a basic block. + * + * @param block a JIT IR block + * + * @return register vector of the predecessors + */ +JitRegVec +jit_basic_block_preds(JitBasicBlock *block); + +/** + * Get the register vector of successors of a basic block. + * + * @param block a JIT IR basic block + * + * @return register vector of the successors + */ +JitRegVec +jit_basic_block_succs(JitBasicBlock *block); + +/** + * Hard register information of one kind. + */ +typedef struct JitHardRegInfo { + struct { + /* Hard register number of this kind. */ + uint32 num; + + /* Whether each register is fixed. */ + const uint8 *fixed; + + /* Whether each register is caller-saved in the native ABI. */ + const uint8 *caller_saved_native; + + /* Whether each register is caller-saved in the JITed ABI. */ + const uint8 *caller_saved_jitted; + } info[JIT_REG_KIND_L32]; + + /* The indexes of hard registers of frame pointer, exec_env and cmp. */ + uint32 fp_hreg_index; + uint32 exec_env_hreg_index; + uint32 cmp_hreg_index; +} JitHardRegInfo; + +struct JitBlock; +struct JitCompContext; +struct JitValueSlot; + +/** + * Value in the WASM operation stack, each stack element + * is a Jit register + */ +typedef struct JitValue { + struct JitValue *next; + struct JitValue *prev; + struct JitValueSlot *value; + /* VALUE_TYPE_I32/I64/F32/F64/VOID */ + uint8 type; +} JitValue; + +/** + * Value stack, represents stack elements in a WASM block + */ +typedef struct JitValueStack { + JitValue *value_list_head; + JitValue *value_list_end; +} JitValueStack; + +/* Record information of a value slot of local variable or stack + during translation. */ +typedef struct JitValueSlot { + /* The virtual register that holds the value of the slot if the + value of the slot is in register. */ + JitReg reg; + + /* The dirty bit of the value slot. It's set if the value in + register is newer than the value in memory. */ + uint32 dirty : 1; + + /* Whether the new value in register is a reference, which is valid + only when the dirty bit is set. */ + uint32 ref : 1; + + /* Committed reference flag. 0: unknown, 1: not-reference, 2: + reference. */ + uint32 committed_ref : 2; +} JitValueSlot; + +typedef struct JitMemRegs { + /* The following registers should be re-loaded after + memory.grow, callbc and callnative */ + JitReg memory_inst; + JitReg cur_page_count; + JitReg memory_data; + JitReg memory_data_end; + JitReg mem_bound_check_1byte; + JitReg mem_bound_check_2bytes; + JitReg mem_bound_check_4bytes; + JitReg mem_bound_check_8bytes; + JitReg mem_bound_check_16bytes; +} JitMemRegs; + +typedef struct JitTableRegs { + JitReg table_elems; + /* Should be re-loaded after table.grow, + callbc and callnative */ + JitReg table_cur_size; +} JitTableRegs; + +/* Frame information for translation */ +typedef struct JitFrame { + /* The current wasm module */ + WASMModule *cur_wasm_module; + /* The current wasm function */ + WASMFunction *cur_wasm_func; + /* The current wasm function index */ + uint32 cur_wasm_func_idx; + /* The current compilation context */ + struct JitCompContext *cc; + + /* Max local slot number. */ + uint32 max_locals; + + /* Max operand stack slot number. */ + uint32 max_stacks; + + /* Instruction pointer */ + uint8 *ip; + + /* Stack top pointer */ + JitValueSlot *sp; + + /* Committed instruction pointer */ + uint8 *committed_ip; + + /* Committed stack top pointer */ + JitValueSlot *committed_sp; + + /* WASM module instance */ + JitReg module_inst_reg; + /* WASM module */ + JitReg module_reg; + /* module_inst->import_func_ptrs */ + JitReg import_func_ptrs_reg; + /* module_inst->fast_jit_func_ptrs */ + JitReg fast_jit_func_ptrs_reg; + /* module_inst->func_type_indexes */ + JitReg func_type_indexes_reg; + /* Boundary of auxiliary stack */ + JitReg aux_stack_bound_reg; + /* Bottom of auxiliary stack */ + JitReg aux_stack_bottom_reg; + /* Data of memory instances */ + JitMemRegs *memory_regs; + /* Data of table instances */ + JitTableRegs *table_regs; + + /* Local variables */ + JitValueSlot lp[1]; +} JitFrame; + +typedef struct JitIncomingInsn { + struct JitIncomingInsn *next; + JitInsn *insn; + uint32 opnd_idx; +} JitIncomingInsn, *JitIncomingInsnList; + +typedef struct JitBlock { + struct JitBlock *next; + struct JitBlock *prev; + + /* The current Jit Block */ + struct JitCompContext *cc; + + /* LABEL_TYPE_BLOCK/LOOP/IF/FUNCTION */ + uint32 label_type; + + /* code of else opcode of this block, if it is a IF block */ + uint8 *wasm_code_else; + /* code of end opcode of this block */ + uint8 *wasm_code_end; + + /* JIT label points to code begin */ + JitBasicBlock *basic_block_entry; + /* JIT label points to code else */ + JitBasicBlock *basic_block_else; + /* JIT label points to code end */ + JitBasicBlock *basic_block_end; + + /* Incoming INSN for basic_block_else */ + JitInsn *incoming_insn_for_else_bb; + /* Incoming INSNs for basic_block_end */ + JitIncomingInsnList incoming_insns_for_end_bb; + + /* WASM operation stack */ + JitValueStack value_stack; + + /* Param count/types/PHIs of this block */ + uint32 param_count; + uint8 *param_types; + + /* Result count/types/PHIs of this block */ + uint32 result_count; + uint8 *result_types; + + /* The begin frame stack pointer of this block */ + JitValueSlot *frame_sp_begin; +} JitBlock; + +/** + * Block stack, represents WASM block stack elements + */ +typedef struct JitBlockStack { + JitBlock *block_list_head; + JitBlock *block_list_end; +} JitBlockStack; + +/** + * The JIT compilation context for one compilation process of a + * compilation unit. + */ +typedef struct JitCompContext { + /* Hard register information of each kind. */ + const JitHardRegInfo *hreg_info; + + /* No. of the pass to be applied. */ + uint8 cur_pass_no; + + /* The current wasm module */ + WASMModule *cur_wasm_module; + /* The current wasm function */ + WASMFunction *cur_wasm_func; + /* The current wasm function index */ + uint32 cur_wasm_func_idx; + /* The block stack */ + JitBlockStack block_stack; + + bool mem_space_unchanged; + + /* Entry and exit labels of the compilation unit, whose numbers must + be 0 and 1 respectively (see JIT_FOREACH_BLOCK). */ + JitReg entry_label; + JitReg exit_label; + JitBasicBlock **exce_basic_blocks; + JitIncomingInsnList *incoming_insns_for_exec_bbs; + + /* The current basic block to generate instructions */ + JitBasicBlock *cur_basic_block; + + /* Registers of frame pointer, exec_env and CMP result. */ + JitReg fp_reg; + JitReg exec_env_reg; + JitReg cmp_reg; + + /* WASM module instance */ + JitReg module_inst_reg; + /* WASM module */ + JitReg module_reg; + /* module_inst->import_func_ptrs */ + JitReg import_func_ptrs_reg; + /* module_inst->fast_jit_func_ptrs */ + JitReg fast_jit_func_ptrs_reg; + /* module_inst->func_type_indexes */ + JitReg func_type_indexes_reg; + /* Boundary of auxiliary stack */ + JitReg aux_stack_bound_reg; + /* Bottom of auxiliary stack */ + JitReg aux_stack_bottom_reg; + /* Data of memory instances */ + JitMemRegs *memory_regs; + /* Data of table instances */ + JitTableRegs *table_regs; + + /* Current frame information for translation */ + JitFrame *jit_frame; + + /* The total frame size of current function */ + uint32 total_frame_size; + + /* The spill cache offset to the interp frame */ + uint32 spill_cache_offset; + /* The spill cache size */ + uint32 spill_cache_size; + + /* The offset of jitted_return_address in the frame, which is set by + the pass frontend and used by the pass codegen. */ + uint32 jitted_return_address_offset; + + /* Begin and end addresses of the jitted code produced by the pass + codegen and consumed by the region registration after codegen and + the pass dump. */ + void *jitted_addr_begin; + void *jitted_addr_end; + + char last_error[128]; + + /* Below fields are all private. Don't access them directly. */ + + /* Reference count of the compilation context. */ + uint16 _reference_count; + + /* Constant values. */ + struct { + /* Number of constant values of each kind. */ + uint32 _num[JIT_REG_KIND_L32]; + + /* Capacity of register annotations of each kind. */ + uint32 _capacity[JIT_REG_KIND_L32]; + + /* Constant values of each kind. */ + uint8 *_value[JIT_REG_KIND_L32]; + + /* Next element on the list of values with the same hash code. */ + JitReg *_next[JIT_REG_KIND_L32]; + + /* Size of the hash table. */ + uint32 _hash_table_size; + + /* Map values to JIT register. */ + JitReg *_hash_table; + } _const_val; + + /* Annotations of labels, registers and instructions. */ + struct { + /* Number of all ever created labels. */ + uint32 _label_num; + + /* Capacity of label annotations. */ + uint32 _label_capacity; + + /* Number of all ever created instructions. */ + uint32 _insn_num; + + /* Capacity of instruction annotations. */ + uint32 _insn_capacity; + + /* Number of ever created registers of each kind. */ + uint32 _reg_num[JIT_REG_KIND_L32]; + + /* Capacity of register annotations of each kind. */ + uint32 _reg_capacity[JIT_REG_KIND_L32]; + + /* Storage of annotations. */ +#define ANN_LABEL(TYPE, NAME) TYPE *_label_##NAME; +#define ANN_INSN(TYPE, NAME) TYPE *_insn_##NAME; +#define ANN_REG(TYPE, NAME) TYPE *_reg_##NAME[JIT_REG_KIND_L32]; +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + + /* Flags of annotations. */ +#define ANN_LABEL(TYPE, NAME) uint32 _label_##NAME##_enabled : 1; +#define ANN_INSN(TYPE, NAME) uint32 _insn_##NAME##_enabled : 1; +#define ANN_REG(TYPE, NAME) uint32 _reg_##NAME##_enabled : 1; +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + } _ann; + + /* Instruction hash table. */ + struct { + /* Size of the hash table. */ + uint32 _size; + + /* The hash table. */ + JitInsn **_table; + } _insn_hash_table; + + /* indicate if the last comparison is about floating-point numbers or not + */ + bool last_cmp_on_fp; +} JitCompContext; + +/* + * Annotation accessing functions jit_annl_NAME, jit_anni_NAME and + * jit_annr_NAME. + */ +#define ANN_LABEL(TYPE, NAME) \ + static inline TYPE *jit_annl_##NAME(JitCompContext *cc, JitReg label) \ + { \ + unsigned idx = jit_reg_no(label); \ + bh_assert(jit_reg_kind(label) == JIT_REG_KIND_L32); \ + bh_assert(idx < cc->_ann._label_num); \ + bh_assert(cc->_ann._label_##NAME##_enabled); \ + return &cc->_ann._label_##NAME[idx]; \ + } +#define ANN_INSN(TYPE, NAME) \ + static inline TYPE *jit_anni_##NAME(JitCompContext *cc, JitInsn *insn) \ + { \ + unsigned uid = insn->uid; \ + bh_assert(uid < cc->_ann._insn_num); \ + bh_assert(cc->_ann._insn_##NAME##_enabled); \ + return &cc->_ann._insn_##NAME[uid]; \ + } +#define ANN_REG(TYPE, NAME) \ + static inline TYPE *jit_annr_##NAME(JitCompContext *cc, JitReg reg) \ + { \ + unsigned kind = jit_reg_kind(reg); \ + unsigned no = jit_reg_no(reg); \ + bh_assert(kind < JIT_REG_KIND_L32); \ + bh_assert(no < cc->_ann._reg_num[kind]); \ + bh_assert(cc->_ann._reg_##NAME##_enabled); \ + return &cc->_ann._reg_##NAME[kind][no]; \ + } +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + +/* + * Annotation enabling functions jit_annl_enable_NAME, + * jit_anni_enable_NAME and jit_annr_enable_NAME, which allocate + * sufficient memory for the annotations. + */ +#define ANN_LABEL(TYPE, NAME) bool jit_annl_enable_##NAME(JitCompContext *cc); +#define ANN_INSN(TYPE, NAME) bool jit_anni_enable_##NAME(JitCompContext *cc); +#define ANN_REG(TYPE, NAME) bool jit_annr_enable_##NAME(JitCompContext *cc); +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + +/* + * Annotation disabling functions jit_annl_disable_NAME, + * jit_anni_disable_NAME and jit_annr_disable_NAME, which release + * memory of the annotations. Before calling these functions, + * resources owned by the annotations must be explicitly released. + */ +#define ANN_LABEL(TYPE, NAME) void jit_annl_disable_##NAME(JitCompContext *cc); +#define ANN_INSN(TYPE, NAME) void jit_anni_disable_##NAME(JitCompContext *cc); +#define ANN_REG(TYPE, NAME) void jit_annr_disable_##NAME(JitCompContext *cc); +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + +/* + * Functions jit_annl_is_enabled_NAME, jit_anni_is_enabled_NAME and + * jit_annr_is_enabled_NAME for checking whether an annotation is + * enabled. + */ +#define ANN_LABEL(TYPE, NAME) \ + static inline bool jit_annl_is_enabled_##NAME(JitCompContext *cc) \ + { \ + return !!cc->_ann._label_##NAME##_enabled; \ + } +#define ANN_INSN(TYPE, NAME) \ + static inline bool jit_anni_is_enabled_##NAME(JitCompContext *cc) \ + { \ + return !!cc->_ann._insn_##NAME##_enabled; \ + } +#define ANN_REG(TYPE, NAME) \ + static inline bool jit_annr_is_enabled_##NAME(JitCompContext *cc) \ + { \ + return !!cc->_ann._reg_##NAME##_enabled; \ + } +#include "jit_ir.def" +#undef ANN_LABEL +#undef ANN_INSN +#undef ANN_REG + +/** + * Initialize a compilation context. + * + * @param cc the compilation context + * @param htab_size the initial hash table size of constant pool + * + * @return cc if succeeds, NULL otherwise + */ +JitCompContext * +jit_cc_init(JitCompContext *cc, unsigned htab_size); + +/** + * Release all resources of a compilation context, which doesn't + * include the compilation context itself. + * + * @param cc the compilation context + */ +void +jit_cc_destroy(JitCompContext *cc); + +/** + * Increase the reference count of the compilation context. + * + * @param cc the compilation context + */ +static inline void +jit_cc_inc_ref(JitCompContext *cc) +{ + cc->_reference_count++; +} + +/** + * Decrease the reference_count and destroy and free the compilation + * context if the reference_count is decreased to zero. + * + * @param cc the compilation context + */ +void +jit_cc_delete(JitCompContext *cc); + +char * +jit_get_last_error(JitCompContext *cc); + +void +jit_set_last_error(JitCompContext *cc, const char *error); + +void +jit_set_last_error_v(JitCompContext *cc, const char *format, ...); + +/** + * Create a I32 constant value with relocatable into the compilation + * context. A constant value that has relocation info cannot be + * constant-folded as normal constants because its value depends on + * runtime context and may be different in different executions. + * + * @param cc compilation context + * @param val a I32 value + * @param rel relocation information + * + * @return a constant register containing the value + */ +JitReg +jit_cc_new_const_I32_rel(JitCompContext *cc, int32 val, uint32 rel); + +/** + * Create a I32 constant value without relocation info (0) into the + * compilation context. + * + * @param cc compilation context + * @param val a I32 value + * + * @return a constant register containing the value + */ +static inline JitReg +jit_cc_new_const_I32(JitCompContext *cc, int32 val) +{ + return jit_cc_new_const_I32_rel(cc, val, 0); +} + +/** + * Create a I64 constant value into the compilation context. + * + * @param cc compilation context + * @param val a I64 value + * + * @return a constant register containing the value + */ +JitReg +jit_cc_new_const_I64(JitCompContext *cc, int64 val); + +#if UINTPTR_MAX == UINT64_MAX +#define jit_cc_new_const_PTR jit_cc_new_const_I64 +#else +#define jit_cc_new_const_PTR jit_cc_new_const_I32 +#endif + +/** + * Create a F32 constant value into the compilation context. + * + * @param cc compilation context + * @param val a F32 value + * + * @return a constant register containing the value + */ +JitReg +jit_cc_new_const_F32(JitCompContext *cc, float val); + +/** + * Create a F64 constant value into the compilation context. + * + * @param cc compilation context + * @param val a F64 value + * + * @return a constant register containing the value + */ +JitReg +jit_cc_new_const_F64(JitCompContext *cc, double val); + +/** + * Get the relocation info of a I32 constant register. + * + * @param cc compilation context + * @param reg constant register + * + * @return the relocation info of the constant + */ +uint32 +jit_cc_get_const_I32_rel(JitCompContext *cc, JitReg reg); + +/** + * Get the constant value of a I32 constant register. + * + * @param cc compilation context + * @param reg constant register + * + * @return the constant value + */ +int32 +jit_cc_get_const_I32(JitCompContext *cc, JitReg reg); + +/** + * Get the constant value of a I64 constant register. + * + * @param cc compilation context + * @param reg constant register + * + * @return the constant value + */ +int64 +jit_cc_get_const_I64(JitCompContext *cc, JitReg reg); + +/** + * Get the constant value of a F32 constant register. + * + * @param cc compilation context + * @param reg constant register + * + * @return the constant value + */ +float +jit_cc_get_const_F32(JitCompContext *cc, JitReg reg); + +/** + * Get the constant value of a F64 constant register. + * + * @param cc compilation context + * @param reg constant register + * + * @return the constant value + */ +double +jit_cc_get_const_F64(JitCompContext *cc, JitReg reg); + +/** + * Get the number of total created labels. + * + * @param cc the compilation context + * + * @return the number of total created labels + */ +static inline unsigned +jit_cc_label_num(JitCompContext *cc) +{ + return cc->_ann._label_num; +} + +/** + * Get the number of total created instructions. + * + * @param cc the compilation context + * + * @return the number of total created instructions + */ +static inline unsigned +jit_cc_insn_num(JitCompContext *cc) +{ + return cc->_ann._insn_num; +} + +/** + * Get the number of total created registers. + * + * @param cc the compilation context + * @param kind the register kind + * + * @return the number of total created registers + */ +static inline unsigned +jit_cc_reg_num(JitCompContext *cc, unsigned kind) +{ + bh_assert(kind < JIT_REG_KIND_L32); + return cc->_ann._reg_num[kind]; +} + +/** + * Create a new label in the compilation context. + * + * @param cc the compilation context + * + * @return a new label in the compilation context + */ +JitReg +jit_cc_new_label(JitCompContext *cc); + +/** + * Create a new block with a new label in the compilation context. + * + * @param cc the compilation context + * @param n number of predecessors + * + * @return a new block with a new label in the compilation context + */ +JitBasicBlock * +jit_cc_new_basic_block(JitCompContext *cc, int n); + +/** + * Resize the predecessor number of a block. + * + * @param cc the containing compilation context + * @param block block to be resized + * @param n new number of predecessors + * + * @return the new block if succeeds, NULL otherwise + */ +JitBasicBlock * +jit_cc_resize_basic_block(JitCompContext *cc, JitBasicBlock *block, int n); + +/** + * Initialize the instruction hash table to the given size and enable + * the instruction's _hash_link annotation. + * + * @param cc the containing compilation context + * @param n size of the hash table + * + * @return true if succeeds, false otherwise + */ +bool +jit_cc_enable_insn_hash(JitCompContext *cc, unsigned n); + +/** + * Destroy the instruction hash table and disable the instruction's + * _hash_link annotation. + * + * @param cc the containing compilation context + */ +void +jit_cc_disable_insn_hash(JitCompContext *cc); + +/** + * Reset the hash table entries. + * + * @param cc the containing compilation context + */ +void +jit_cc_reset_insn_hash(JitCompContext *cc); + +/** + * Allocate a new instruction ID in the compilation context and set it + * to the given instruction. + * + * @param cc the compilation context + * @param insn IR instruction + * + * @return the insn with uid being set + */ +JitInsn * +jit_cc_set_insn_uid(JitCompContext *cc, JitInsn *insn); + +/* + * Similar to jit_cc_set_insn_uid except that if setting uid failed, + * delete the insn. Only used by jit_cc_new_insn + */ +JitInsn * +_jit_cc_set_insn_uid_for_new_insn(JitCompContext *cc, JitInsn *insn); + +/** + * Create a new instruction in the compilation context. + * + * @param cc the compilationo context + * @param NAME instruction name + * + * @return a new instruction in the compilation context + */ +#define jit_cc_new_insn(cc, NAME, ...) \ + _jit_cc_set_insn_uid_for_new_insn(cc, jit_insn_new_##NAME(__VA_ARGS__)) + +/* + * Helper function for jit_cc_new_insn_norm. + */ +JitInsn * +_jit_cc_new_insn_norm(JitCompContext *cc, JitReg *result, JitInsn *insn); + +/** + * Create a new instruction in the compilation context and normalize + * the instruction (constant folding and simplification etc.). If the + * instruction hashing is enabled (anni__hash_link is enabled), try to + * find the existing equivalent insruction first before adding a new + * one to the compilation contest. + * + * @param cc the compilationo context + * @param result returned result of the instruction. If the value is + * non-zero, it is the result of the constant-folding or an existing + * equivalent instruction, in which case no instruction is added into + * the compilation context. Otherwise, a new normalized instruction + * has been added into the compilation context. + * @param NAME instruction name + * + * @return a new or existing instruction in the compilation context + */ +#define jit_cc_new_insn_norm(cc, result, NAME, ...) \ + _jit_cc_new_insn_norm(cc, result, jit_insn_new_##NAME(__VA_ARGS__)) + +/** + * Helper function for GEN_INSN + * + * @param cc compilation context + * @param block the current block + * @param insn the new instruction + * + * @return the new instruction if inserted, NULL otherwise + */ +static inline JitInsn * +_gen_insn(JitCompContext *cc, JitInsn *insn) +{ + if (insn) + jit_basic_block_append_insn(cc->cur_basic_block, insn); + else + jit_set_last_error(cc, "generate insn failed"); + + return insn; +} + +/** + * Generate and append an instruction to the current block. + */ +#define GEN_INSN(...) _gen_insn(cc, jit_cc_new_insn(cc, __VA_ARGS__)) + +/** + * Create a constant register without relocation info. + * + * @param Type type of the register + * @param val the constant value + * + * @return the constant register if succeeds, 0 otherwise + */ +#define NEW_CONST(Type, val) jit_cc_new_const_##Type(cc, val) + +/** + * Create a new virtual register in the compilation context. + * + * @param cc the compilation context + * @param kind kind of the register + * + * @return a new label in the compilation context + */ +JitReg +jit_cc_new_reg(JitCompContext *cc, unsigned kind); + +/* + * Create virtual registers with specific types in the compilation + * context. They are more convenient than the above one. + */ + +static inline JitReg +jit_cc_new_reg_I32(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_I32); +} + +static inline JitReg +jit_cc_new_reg_I64(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_I64); +} + +#if UINTPTR_MAX == UINT64_MAX +#define jit_cc_new_reg_ptr jit_cc_new_reg_I64 +#else +#define jit_cc_new_reg_ptr jit_cc_new_reg_I32 +#endif + +static inline JitReg +jit_cc_new_reg_F32(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_F32); +} + +static inline JitReg +jit_cc_new_reg_F64(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_F64); +} + +static inline JitReg +jit_cc_new_reg_V64(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_V64); +} + +static inline JitReg +jit_cc_new_reg_V128(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_V128); +} + +static inline JitReg +jit_cc_new_reg_V256(JitCompContext *cc) +{ + return jit_cc_new_reg(cc, JIT_REG_KIND_V256); +} + +/** + * Get the hard register numbe of the given kind + * + * @param cc the compilation context + * @param kind the register kind + * + * @return number of hard registers of the given kind + */ +static inline unsigned +jit_cc_hreg_num(JitCompContext *cc, unsigned kind) +{ + bh_assert(kind < JIT_REG_KIND_L32); + return cc->hreg_info->info[kind].num; +} + +/** + * Check whether a given register is a hard register. + * + * @param cc the compilation context + * @param reg the register which must be a variable + * + * @return true if the register is a hard register + */ +static inline bool +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; +} + +/** + * Check whether the given hard register is fixed. + * + * @param cc the compilation context + * @param reg the hard register + * + * @return true if the hard register is fixed + */ +static inline bool +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]; +} + +/** + * Check whether the given hard register is caller-saved-native. + * + * @param cc the compilation context + * @param reg the hard register + * + * @return true if the hard register is caller-saved-native + */ +static inline bool +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]; +} + +/** + * Check whether the given hard register is caller-saved-jitted. + * + * @param cc the compilation context + * @param reg the hard register + * + * @return true if the hard register is caller-saved-jitted + */ +static inline bool +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]; +} + +/** + * Return the entry block of the compilation context. + * + * @param cc the compilation context + * + * @return the entry block of the compilation context + */ +static inline JitBasicBlock * +jit_cc_entry_basic_block(JitCompContext *cc) +{ + return *(jit_annl_basic_block(cc, cc->entry_label)); +} + +/** + * Return the exit block of the compilation context. + * + * @param cc the compilation context + * + * @return the exit block of the compilation context + */ +static inline JitBasicBlock * +jit_cc_exit_basic_block(JitCompContext *cc) +{ + return *(jit_annl_basic_block(cc, cc->exit_label)); +} + +void +jit_value_stack_push(JitValueStack *stack, JitValue *value); + +JitValue * +jit_value_stack_pop(JitValueStack *stack); + +void +jit_value_stack_destroy(JitValueStack *stack); + +JitBlock * +jit_block_stack_top(JitBlockStack *stack); + +void +jit_block_stack_push(JitBlockStack *stack, JitBlock *block); + +JitBlock * +jit_block_stack_pop(JitBlockStack *stack); + +void +jit_block_stack_destroy(JitBlockStack *stack); + +bool +jit_block_add_incoming_insn(JitBlock *block, JitInsn *insn, uint32 opnd_idx); + +void +jit_block_destroy(JitBlock *block); + +bool +jit_cc_push_value(JitCompContext *cc, uint8 type, JitReg value); + +bool +jit_cc_pop_value(JitCompContext *cc, uint8 type, JitReg *p_value); + +bool +jit_lock_reg_in_insn(JitCompContext *cc, JitInsn *the_insn, JitReg reg_to_lock); + +/** + * Update the control flow graph after successors of blocks are + * changed so that the predecessor vector of each block represents the + * updated status. The predecessors may not be required by all + * passes, so we don't need to keep them always being updated. + * + * @param cc the compilation context + * + * @return true if succeeds, false otherwise + */ +bool +jit_cc_update_cfg(JitCompContext *cc); + +/** + * Visit each normal block (which is not entry nor exit block) in a + * compilation context. New blocks can be added in the loop body, but + * they won't be visited. Blocks can also be removed safely (by + * setting the label's block annotation to NULL) in the loop body. + * + * @param CC (JitCompContext *) the compilation context + * @param I (unsigned) index variable of the block (label no) + * @param E (unsigned) end index variable of block (last index + 1) + * @param B (JitBasicBlock *) block pointer variable + */ +#define JIT_FOREACH_BLOCK(CC, I, E, B) \ + for ((I) = 2, (E) = (CC)->_ann._label_num; (I) < (E); (I)++) \ + if (((B) = (CC)->_ann._label_basic_block[(I)])) + +/** + * The version that includes entry and exit block. + */ +#define JIT_FOREACH_BLOCK_ENTRY_EXIT(CC, I, E, B) \ + for ((I) = 0, (E) = (CC)->_ann._label_num; (I) < (E); (I)++) \ + if (((B) = (CC)->_ann._label_basic_block[(I)])) + +/** + * Visit each normal block (which is not entry nor exit block) in a + * compilation context in reverse order. New blocks can be added in + * the loop body, but they won't be visited. Blocks can also be + * removed safely (by setting the label's block annotation to NULL) in + * the loop body. + * + * @param CC (JitCompContext *) the compilation context + * @param I (unsigned) index of the block (label no) + * @param B (JitBasicBlock *) block pointer + */ +#define JIT_FOREACH_BLOCK_REVERSE(CC, I, B) \ + for ((I) = (CC)->_ann._label_num; (I) > 2; (I)--) \ + if (((B) = (CC)->_ann._label_basic_block[(I)-1])) + +/** + * The version that includes entry and exit block. + */ +#define JIT_FOREACH_BLOCK_REVERSE_ENTRY_EXIT(CC, I, B) \ + for ((I) = (CC)->_ann._label_num; (I) > 0; (I)--) \ + if (((B) = (CC)->_ann._label_basic_block[(I)-1])) + +#ifdef __cplusplus +} +#endif + +#endif /* end of _JIT_IR_H_ */ diff --git a/core/iwasm/fast-jit/jit_regalloc.c b/core/iwasm/fast-jit/jit_regalloc.c new file mode 100644 index 0000000000..96e5a57cfb --- /dev/null +++ b/core/iwasm/fast-jit/jit_regalloc.c @@ -0,0 +1,862 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "jit_utils.h" +#include "jit_compiler.h" + +#if BH_DEBUG != 0 +#define VREG_DEF_SANITIZER +#endif + +/** + * A uint16 stack for storing distances of occurrences of virtual + * registers. + */ +typedef struct UintStack { + /* Capacity of the stack. */ + uint32 capacity; + + /* Top index of the stack. */ + uint32 top; + + /* Elements of the vector. */ + uint32 elem[1]; +} UintStack; + +static bool +uint_stack_push(UintStack **stack, unsigned val) +{ + unsigned capacity = *stack ? (*stack)->capacity : 0; + unsigned top = *stack ? (*stack)->top : 0; + + bh_assert(top <= capacity); + + if (top == capacity) { + const unsigned elem_size = sizeof((*stack)->elem[0]); + unsigned new_capacity = capacity ? capacity + capacity / 2 : 4; + UintStack *new_stack = + jit_malloc(offsetof(UintStack, elem) + elem_size * new_capacity); + + if (!new_stack) + return false; + + new_stack->capacity = new_capacity; + new_stack->top = top; + + if (*stack) + memcpy(new_stack->elem, (*stack)->elem, elem_size * top); + + jit_free(*stack); + *stack = new_stack; + } + + (*stack)->elem[(*stack)->top++] = val; + + return true; +} + +static int +uint_stack_top(UintStack *stack) +{ + return stack->elem[stack->top - 1]; +} + +static void +uint_stack_delete(UintStack **stack) +{ + jit_free(*stack); + *stack = NULL; +} + +static void +uint_stack_pop(UintStack **stack) +{ + bh_assert((*stack)->top > 0); + + /** + * TODO: the fact of empty distances stack means there is no instruction + * using current JitReg anymore. so shall we release the HardReg and clean + * VirtualReg information? + */ + if (--(*stack)->top == 0) + uint_stack_delete(stack); +} + +/** + * Information of a virtual register. + */ +typedef struct VirtualReg { + /* The hard register allocated to this virtual register. */ + JitReg hreg; + + /* The spill slot allocated to this virtual register. */ + JitReg slot; + + /* The hard register allocated to global virtual registers. It is 0 + for local registers, whose lifetime is within one basic block. */ + JitReg global_hreg; + + /* Distances from the beginning of basic block of all occurrences of the + virtual register in the basic block. */ + UintStack *distances; +} VirtualReg; + +/** + * Information of a hard register. + */ +typedef struct HardReg { + /* The virtual register this hard register is allocated to. */ + JitReg vreg; +} HardReg; + +/** + * Information of a spill slot. + */ +typedef struct SpillSlot { + /* The virtual register this spill slot is allocated to. */ + JitReg vreg; +} SpillSlot; + +typedef struct RegallocContext { + /* The compiler context. */ + JitCompContext *cc; + + /* Information of virtual registers. The register allocation must + not increase the virtual register number during the allocation + process. */ + VirtualReg *vregs[JIT_REG_KIND_L32]; + + /* Information of hard registers. */ + HardReg *hregs[JIT_REG_KIND_L32]; + + /* Number of elements in the spill_slots array. */ + uint32 spill_slot_num; + + /* Information of spill slots. */ + SpillSlot *spill_slots; + + /* The last define-released hard register. */ + JitReg last_def_released_hreg; +} RegallocContext; + +/** + * Get the VirtualReg structure of the given virtual register. + * + * @param rc the regalloc context + * @param vreg the virtual register + * + * @return the VirtualReg structure of the given virtual register + */ +static VirtualReg * +rc_get_vr(RegallocContext *rc, JitReg vreg) +{ + unsigned kind = jit_reg_kind(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]; +} + +/** + * Get the HardReg structure of the given hard register. + * + * @param rc the regalloc context + * @param hreg the hard register + * + * @return the HardReg structure of the given hard register + */ +static HardReg * +rc_get_hr(RegallocContext *rc, JitReg hreg) +{ + unsigned kind = jit_reg_kind(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]; +} + +/** + * Get the SpillSlot structure of the given slot. + * + * @param rc the regalloc context + * @param slot the constant register representing the slot index + * + * @return the SpillSlot of the given slot + */ +static SpillSlot * +rc_get_spill_slot(RegallocContext *rc, JitReg slot) +{ + unsigned index = jit_cc_get_const_I32(rc->cc, slot); + + bh_assert(index < rc->spill_slot_num); + + return &rc->spill_slots[index]; +} + +/** + * Get the stride in the spill slots of the register. + * + * @param reg a virtual register + * + * @return stride in the spill slots + */ +static unsigned +get_reg_stride(JitReg reg) +{ + static const uint8 strides[] = { 0, 1, 2, 1, 2, 2, 4, 8, 0 }; + uint32 kind = jit_reg_kind(reg); + bh_assert(kind <= JIT_REG_KIND_L32); + return strides[kind]; +} + +/** + * Allocate a spill slot for the given virtual register. + * + * @param rc the regalloc context + * @param vreg the virtual register + * + * @return the spill slot encoded in a constant register + */ +static JitReg +rc_alloc_spill_slot(RegallocContext *rc, JitReg vreg) +{ + const unsigned stride = get_reg_stride(vreg); + unsigned mask, new_num, i, j; + SpillSlot *slots; + + bh_assert(stride > 0); + + for (i = 0; i < rc->spill_slot_num; i += stride) + for (j = i;; j++) { + if (j == i + stride) + /* Found a free slot for vreg. */ + goto found; + + if (rc->spill_slots[j].vreg) + break; + } + + /* No free slot, increase the slot number. */ + mask = stride - 1; + /* Align the slot index. */ + i = (rc->spill_slot_num + mask) & ~mask; + new_num = i == 0 ? 32 : i + i / 2; + + if (!(slots = jit_calloc(sizeof(*slots) * new_num))) + return 0; + + if (rc->spill_slots) + memcpy(slots, rc->spill_slots, sizeof(*slots) * rc->spill_slot_num); + + jit_free(rc->spill_slots); + rc->spill_slots = slots; + rc->spill_slot_num = new_num; + +found: + /* Now, i is the first slot for vreg. */ + if ((i + stride) * 4 > rc->cc->spill_cache_size) + /* No frame space for the spill area. */ + return 0; + + /* Allocate the slot(s) to vreg. */ + for (j = i; j < i + stride; j++) + rc->spill_slots[j].vreg = vreg; + + return jit_cc_new_const_I32(rc->cc, i); +} + +/** + * Free a spill slot. + * + * @param rc the regalloc context + * @param slot_reg the constant register representing the slot index + */ +static void +rc_free_spill_slot(RegallocContext *rc, JitReg slot_reg) +{ + if (slot_reg) { + SpillSlot *slot = rc_get_spill_slot(rc, slot_reg); + const JitReg vreg = slot->vreg; + const unsigned stride = get_reg_stride(vreg); + unsigned i; + + for (i = 0; i < stride; i++) + slot[i].vreg = 0; + } +} + +static void +rc_destroy(RegallocContext *rc) +{ + unsigned i, j; + + for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) { + const unsigned vreg_num = jit_cc_reg_num(rc->cc, i); + + if (rc->vregs[i]) + for (j = 0; j < vreg_num; j++) + uint_stack_delete(&rc->vregs[i][j].distances); + + jit_free(rc->vregs[i]); + jit_free(rc->hregs[i]); + } + + jit_free(rc->spill_slots); +} + +static bool +rc_init(RegallocContext *rc, JitCompContext *cc) +{ + unsigned i, j; + + memset(rc, 0, sizeof(*rc)); + rc->cc = cc; + + for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) { + const unsigned vreg_num = jit_cc_reg_num(cc, i); + const unsigned hreg_num = jit_cc_hreg_num(cc, i); + + if (vreg_num > 0 + && !(rc->vregs[i] = jit_calloc(sizeof(VirtualReg) * vreg_num))) + goto fail; + if (hreg_num > 0 + && !(rc->hregs[i] = jit_calloc(sizeof(HardReg) * hreg_num))) + goto fail; + + /* Hard registers can only be allocated to themselves. */ + for (j = 0; j < hreg_num; j++) + rc->vregs[i][j].global_hreg = jit_reg_new(i, j); + } + + return true; + +fail: + rc_destroy(rc); + + return false; +} + +/** + * Check whether the given register is an allocation candidate, which + * must be a variable register that is not fixed hard register. + * + * @param cc the compilation context + * @param reg the register + * + * @return true if the register is an allocation candidate + */ +static bool +is_alloc_candidate(JitCompContext *cc, JitReg reg) +{ + return (jit_reg_is_variable(reg) + && (!jit_cc_is_hreg(cc, reg) || !jit_cc_is_hreg_fixed(cc, reg))); +} + +#ifdef VREG_DEF_SANITIZER +static void +check_vreg_definition(RegallocContext *rc, JitInsn *insn) +{ + JitRegVec regvec = jit_insn_opnd_regs(insn); + JitReg *regp, reg_defined = 0; + unsigned i, first_use = jit_insn_opnd_first_use(insn); + + /* check if there is the definition of an vr before its references */ + JIT_REG_VEC_FOREACH(regvec, i, regp) + { + VirtualReg *vr = NULL; + + if (!is_alloc_candidate(rc->cc, *regp)) + continue; + + /* a strong assumption that there is only one defined reg */ + if (i < first_use) { + reg_defined = *regp; + continue; + } + + /** + * both definition and references are in one instruction, + * like MOV i3, i3 + */ + if (reg_defined == *regp) + continue; + + vr = rc_get_vr(rc, *regp); + bh_assert(vr->distances); + } +} +#endif + +/** + * Collect distances from the beginning of basic block of all occurrences of + * each virtual register. + * + * @param rc the regalloc context + * @param basic_block the basic block + * + * @return distance of the end instruction if succeeds, -1 otherwise + */ +static int +collect_distances(RegallocContext *rc, JitBasicBlock *basic_block) +{ + JitInsn *insn; + int distance = 1; + + JIT_FOREACH_INSN(basic_block, insn) + { +#if WASM_ENABLE_SHARED_MEMORY != 0 + /* fence insn doesn't have any operand, hence, no regs involved */ + if (insn->opcode == JIT_OP_FENCE) { + continue; + } +#endif + + JitRegVec regvec = jit_insn_opnd_regs(insn); + unsigned i; + JitReg *regp; + +#ifdef VREG_DEF_SANITIZER + check_vreg_definition(rc, insn); +#endif + + /* NOTE: the distance may be pushed more than once if the + virtual register occurs multiple times in the + instruction. */ + JIT_REG_VEC_FOREACH(regvec, i, regp) + if (is_alloc_candidate(rc->cc, *regp)) + if (!uint_stack_push(&(rc_get_vr(rc, *regp))->distances, distance)) + return -1; + + /* Integer overflow check, normally it won't happen, but + we had better add the check here */ + if (distance >= INT32_MAX) + return -1; + + distance++; + } + + return distance; +} + +static JitReg +offset_of_spill_slot(JitCompContext *cc, JitReg slot) +{ + return jit_cc_new_const_I32(cc, cc->spill_cache_offset + + jit_cc_get_const_I32(cc, slot) * 4); +} + +/** + * Reload the virtual register from memory. Reload instruction will + * be inserted after the given instruction. + * + * @param rc the regalloc context + * @param vreg the virtual register to be reloaded + * @param cur_insn the current instruction after which the reload + * insertion will be inserted + * + * @return the reload instruction if succeeds, NULL otherwise + */ +static JitInsn * +reload_vreg(RegallocContext *rc, JitReg vreg, JitInsn *cur_insn) +{ + VirtualReg *vr = rc_get_vr(rc, vreg); + HardReg *hr = rc_get_hr(rc, vr->hreg); + JitInsn *insn = NULL; + + if (vreg == rc->cc->exec_env_reg) + /* Reload exec_env_reg with LDEXECENV. */ + insn = jit_cc_new_insn(rc->cc, LDEXECENV, vr->hreg); + else + /* Allocate spill slot if not yet and reload from there. */ + { + JitReg fp_reg = rc->cc->fp_reg, offset; + + if (!vr->slot && !(vr->slot = rc_alloc_spill_slot(rc, vreg))) + /* Cannot allocate spill slot (due to OOM or frame size limit). */ + return NULL; + + offset = offset_of_spill_slot(rc->cc, vr->slot); + + switch (jit_reg_kind(vreg)) { + case JIT_REG_KIND_I32: + insn = jit_cc_new_insn(rc->cc, LDI32, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_I64: + insn = jit_cc_new_insn(rc->cc, LDI64, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_F32: + insn = jit_cc_new_insn(rc->cc, LDF32, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_F64: + insn = jit_cc_new_insn(rc->cc, LDF64, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_V64: + insn = jit_cc_new_insn(rc->cc, LDV64, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_V128: + insn = + jit_cc_new_insn(rc->cc, LDV128, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_V256: + insn = + jit_cc_new_insn(rc->cc, LDV256, vr->hreg, fp_reg, offset); + break; + default: + bh_assert(0); + } + } + + if (insn) + jit_insn_insert_after(cur_insn, insn); + + bh_assert(hr->vreg == vreg); + hr->vreg = vr->hreg = 0; + + return insn; +} + +/** + * Spill the virtual register (which cannot be exec_env_reg) to memory. + * Spill instruction will be inserted after the given instruction. + * + * @param rc the regalloc context + * @param vreg the virtual register to be reloaded + * @param cur_insn the current instruction after which the reload + * insertion will be inserted + * + * @return the spill instruction if succeeds, NULL otherwise + */ +static JitInsn * +spill_vreg(RegallocContext *rc, JitReg vreg, JitInsn *cur_insn) +{ + VirtualReg *vr = rc_get_vr(rc, vreg); + JitReg fp_reg = rc->cc->fp_reg, offset; + JitInsn *insn; + + /* There is no chance to spill exec_env_reg. */ + bh_assert(vreg != rc->cc->exec_env_reg); + bh_assert(vr->hreg && vr->slot); + offset = offset_of_spill_slot(rc->cc, vr->slot); + + switch (jit_reg_kind(vreg)) { + case JIT_REG_KIND_I32: + insn = jit_cc_new_insn(rc->cc, STI32, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_I64: + insn = jit_cc_new_insn(rc->cc, STI64, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_F32: + insn = jit_cc_new_insn(rc->cc, STF32, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_F64: + insn = jit_cc_new_insn(rc->cc, STF64, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_V64: + insn = jit_cc_new_insn(rc->cc, STV64, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_V128: + insn = jit_cc_new_insn(rc->cc, STV128, vr->hreg, fp_reg, offset); + break; + case JIT_REG_KIND_V256: + insn = jit_cc_new_insn(rc->cc, STV256, vr->hreg, fp_reg, offset); + break; + default: + bh_assert(0); + return NULL; + } + + if (insn) + jit_insn_insert_after(cur_insn, insn); + + return insn; +} + +/** + * Allocate a hard register for the virtual register. Necessary + * reload instruction will be inserted after the given instruction. + * + * @param rc the regalloc context + * @param vreg the virtual register + * @param insn the instruction after which the reload insertion will + * be inserted + * @param distance the distance of the current instruction + * + * @return the hard register allocated if succeeds, 0 otherwise + */ +static JitReg +allocate_hreg(RegallocContext *rc, JitReg vreg, JitInsn *insn, int distance) +{ + const int kind = jit_reg_kind(vreg); + 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. */ + { + jit_set_last_error(rc->cc, "unsupported hard register kind"); + return 0; + } + + if (vr->global_hreg) + /* It has globally allocated register, we can only use it. */ + { + if ((vreg_to_reload = (rc_get_hr(rc, vr->global_hreg))->vreg)) + if (!reload_vreg(rc, vreg_to_reload, insn)) + return 0; + + return vr->global_hreg; + } + + /* Use the last define-released register if its kind is correct and + it's free so as to optimize for two-operand instructions. */ + if (jit_reg_kind(rc->last_def_released_hreg) == kind + && (rc_get_hr(rc, rc->last_def_released_hreg))->vreg == 0) + return rc->last_def_released_hreg; + + /* No hint given, just try to pick any free register. */ + for (i = 0; i < hreg_num; i++) { + hreg = jit_reg_new(kind, i); + + if (jit_cc_is_hreg_fixed(rc->cc, hreg)) + continue; + + if (hregs[i].vreg == 0) + /* Found a free one, return it. */ + return hreg; + } + + /* No free registers, need to spill and reload one. */ + for (i = 0; i < hreg_num; i++) { + if (jit_cc_is_hreg_fixed(rc->cc, jit_reg_new(kind, i))) + continue; + + vr = rc_get_vr(rc, hregs[i].vreg); + /* TODO: since the hregs[i] is in use, its distances should be valid */ + vr_distance = vr->distances ? uint_stack_top(vr->distances) : 0; + + if (vr_distance < min_distance) { + min_distance = vr_distance; + vreg_to_reload = hregs[i].vreg; + hreg = jit_reg_new(kind, i); + } + } + + bh_assert(min_distance < distance); + + if (!reload_vreg(rc, vreg_to_reload, insn)) + return 0; + + return hreg; +} + +/** + * Allocate a hard register for the virtual register if not allocated + * yet. Necessary spill and reload instructions will be inserted + * before/after and after the given instruction. This operation will + * convert the virtual register's state from 1 or 3 to 2. + * + * @param rc the regalloc context + * @param vreg the virtual register + * @param insn the instruction after which the spill and reload + * insertions will be inserted + * @param distance the distance of the current instruction + * + * @return the hard register allocated to the virtual register if + * succeeds, 0 otherwise + */ +static JitReg +allocate_for_vreg(RegallocContext *rc, JitReg vreg, JitInsn *insn, int distance) +{ + VirtualReg *vr = rc_get_vr(rc, vreg); + + if (vr->hreg) + /* It has had a hard register, reuse it. */ + return vr->hreg; + + /* Not allocated yet. */ + if ((vr->hreg = allocate_hreg(rc, vreg, insn, distance))) + (rc_get_hr(rc, vr->hreg))->vreg = vreg; + + return vr->hreg; +} + +/** + * Clobber live registers. + * + * @param rc the regalloc context + * @param is_native whether it's native ABI or JITed ABI + * @param insn the instruction after which the reload insertion will + * be inserted + * + * @return true if succeeds, false otherwise + */ +static bool +clobber_live_regs(RegallocContext *rc, bool is_native, JitInsn *insn) +{ + unsigned i, j; + + for (i = JIT_REG_KIND_VOID; i < JIT_REG_KIND_L32; i++) { + const unsigned hreg_num = jit_cc_hreg_num(rc->cc, i); + + for (j = 0; j < hreg_num; j++) { + JitReg hreg = jit_reg_new(i, j); + bool caller_saved = + (is_native ? jit_cc_is_hreg_caller_saved_native(rc->cc, hreg) + : jit_cc_is_hreg_caller_saved_jitted(rc->cc, hreg)); + + if (caller_saved && rc->hregs[i][j].vreg) + if (!reload_vreg(rc, rc->hregs[i][j].vreg, insn)) + return false; + } + } + + return true; +} + +/** + * Do local register allocation for the given basic block + * + * @param rc the regalloc context + * @param basic_block the basic block + * @param distance the distance of the last instruction of the basic block + * + * @return true if succeeds, false otherwise + */ +static bool +allocate_for_basic_block(RegallocContext *rc, JitBasicBlock *basic_block, + int distance) +{ + JitInsn *insn; + + JIT_FOREACH_INSN_REVERSE(basic_block, insn) + { +#if WASM_ENABLE_SHARED_MEMORY != 0 + /* fence insn doesn't have any operand, hence, no regs involved */ + if (insn->opcode == JIT_OP_FENCE) { + continue; + } +#endif + + JitRegVec regvec = jit_insn_opnd_regs(insn); + unsigned first_use = jit_insn_opnd_first_use(insn); + unsigned i; + JitReg *regp; + + distance--; + + JIT_REG_VEC_FOREACH_DEF(regvec, i, regp, first_use) + if (is_alloc_candidate(rc->cc, *regp)) { + const JitReg vreg = *regp; + VirtualReg *vr = rc_get_vr(rc, vreg); + + if (!(*regp = allocate_for_vreg(rc, vreg, insn, distance))) + return false; + + /* Spill the register if required. */ + if (vr->slot && !spill_vreg(rc, vreg, insn)) + return false; + + bh_assert(uint_stack_top(vr->distances) == distance); + uint_stack_pop(&vr->distances); + /* Record the define-released hard register. */ + rc->last_def_released_hreg = vr->hreg; + /* Release the hreg and spill slot. */ + rc_free_spill_slot(rc, vr->slot); + (rc_get_hr(rc, vr->hreg))->vreg = 0; + vr->hreg = vr->slot = 0; + } + + if (insn->opcode == JIT_OP_CALLBC) { + if (!clobber_live_regs(rc, false, insn)) + return false; + + /* The exec_env_reg is implicitly used by the callee. */ + if (!allocate_for_vreg(rc, rc->cc->exec_env_reg, insn, distance)) + return false; + } + else if (insn->opcode == JIT_OP_CALLNATIVE) { + if (!clobber_live_regs(rc, true, insn)) + return false; + } + + JIT_REG_VEC_FOREACH_USE(regvec, i, regp, first_use) + if (is_alloc_candidate(rc->cc, *regp)) { + if (!allocate_for_vreg(rc, *regp, insn, distance)) + return false; + } + + JIT_REG_VEC_FOREACH_USE(regvec, i, regp, first_use) + if (is_alloc_candidate(rc->cc, *regp)) { + VirtualReg *vr = rc_get_vr(rc, *regp); + bh_assert(uint_stack_top(vr->distances) == distance); + uint_stack_pop(&vr->distances); + /* be sure that the hreg exists and hasn't been spilled out */ + bh_assert(vr->hreg != 0); + *regp = vr->hreg; + } + } + + return true; +} + +bool +jit_pass_regalloc(JitCompContext *cc) +{ + RegallocContext rc = { 0 }; + unsigned label_index, end_label_index; + JitBasicBlock *basic_block; + VirtualReg *self_vr; + bool retval = false; + + if (!rc_init(&rc, cc)) + return false; + + /* NOTE: don't allocate new virtual registers during allocation + because the rc->vregs array is fixed size. */ + + /* TODO: allocate hard registers for global virtual registers here. + Currently, exec_env_reg is the only global virtual register. */ + self_vr = rc_get_vr(&rc, cc->exec_env_reg); + + JIT_FOREACH_BLOCK_ENTRY_EXIT(cc, label_index, end_label_index, basic_block) + { + int distance; + + /* TODO: initialize hreg for live-out registers. */ + self_vr->hreg = self_vr->global_hreg; + (rc_get_hr(&rc, cc->exec_env_reg))->vreg = cc->exec_env_reg; + + /** + * TODO: the allocation of a basic block keeps using vregs[] + * and hregs[] from previous basic block + */ + if ((distance = collect_distances(&rc, basic_block)) < 0) + goto cleanup_and_return; + + if (!allocate_for_basic_block(&rc, basic_block, distance)) + goto cleanup_and_return; + + /* TODO: generate necessary spills for live-in registers. */ + } + + retval = true; + +cleanup_and_return: + rc_destroy(&rc); + + return retval; +} diff --git a/core/iwasm/fast-jit/jit_utils.h b/core/iwasm/fast-jit/jit_utils.h new file mode 100644 index 0000000000..a533c70bc6 --- /dev/null +++ b/core/iwasm/fast-jit/jit_utils.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _JIT_UTILS_H_ +#define _JIT_UTILS_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline void * +jit_malloc(unsigned int size) +{ + return wasm_runtime_malloc(size); +} + +static inline void * +jit_calloc(unsigned int size) +{ + void *ret = wasm_runtime_malloc(size); + if (ret) { + memset(ret, 0, size); + } + return ret; +} + +static inline void +jit_free(void *ptr) +{ + if (ptr) + wasm_runtime_free(ptr); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/include/aot_comp_option.h b/core/iwasm/include/aot_comp_option.h new file mode 100644 index 0000000000..9a9023ee2e --- /dev/null +++ b/core/iwasm/include/aot_comp_option.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __AOT_COMP_OPTION_H__ +#define __AOT_COMP_OPTION_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* Enables or disables bounds checks for stack frames. When enabled, the AOT + * compiler generates code to check if the stack pointer is within the + * bounds of the current stack frame (and if not, traps). */ + bool bounds_checks; + + /* Enables or disables instruction pointer (IP) tracking. */ + bool ip; + + /* Enables or disables function index in the stack trace. Please note that + * function index can be recovered from the instruction pointer using + * ip2function.py script, so enabling this feature along with `ip` might + * often be redundant. + * This option will automatically be enabled for GC and Perf Profiling mode. + */ + bool func_idx; + + /* Enables or disables tracking instruction pointer of a trap. Only takes + * effect when `ip` is enabled. */ + bool trap_ip; + + /* Enables or disables parameters, locals and stack operands. */ + bool values; + + /* If enabled, stack frame is generated at the beginning of each + * function (frame-per-function mode). Otherwise, stack frame is + * generated before each call of a function (frame-per-call mode). */ + bool frame_per_function; +} AOTCallStackFeatures; + +void +aot_call_stack_features_init_default(AOTCallStackFeatures *features); + +typedef enum { + AOT_STACK_FRAME_OFF = 0, + /* Use a small stack frame data structure (AOTTinyFrame) */ + AOT_STACK_FRAME_TYPE_TINY, + /* Use a regular stack frame data structure (AOTFrame) */ + AOT_STACK_FRAME_TYPE_STANDARD, +} AOTStackFrameType; + +typedef struct AOTCompOption { + bool is_jit_mode; + bool is_indirect_mode; + char *target_arch; + char *target_abi; + char *target_cpu; + char *cpu_features; + bool is_sgx_platform; + bool enable_bulk_memory; + bool enable_bulk_memory_opt; + bool enable_thread_mgr; + bool enable_tail_call; + bool enable_simd; + bool enable_ref_types; + bool enable_call_indirect_overlong; + bool enable_gc; + bool enable_aux_stack_check; + bool enable_extended_const; + bool enable_lime1; + AOTStackFrameType aux_stack_frame_type; + AOTCallStackFeatures call_stack_features; + bool enable_perf_profiling; + bool enable_memory_profiling; + bool disable_llvm_intrinsics; + bool disable_llvm_jump_tables; + bool disable_llvm_lto; + bool enable_llvm_pgo; + bool enable_stack_estimation; + bool quick_invoke_c_api_import; + bool enable_shared_heap; + bool enable_shared_chain; + char *use_prof_file; + uint32_t opt_level; + uint32_t size_level; + uint32_t output_format; + uint32_t bounds_checks; + uint32_t stack_bounds_checks; + uint32_t segue_flags; + char **custom_sections; + uint32_t custom_sections_count; + const char *stack_usage_file; + const char *llvm_passes; + const char *builtin_intrinsics; +} AOTCompOption, *aot_comp_option_t; + +#ifdef __cplusplus +} +#endif + +#endif /* end of __AOT_COMP_OPTION_H__ */ diff --git a/core/iwasm/include/aot_export.h b/core/iwasm/include/aot_export.h index 4338465cfa..e4072ab729 100644 --- a/core/iwasm/include/aot_export.h +++ b/core/iwasm/include/aot_export.h @@ -3,12 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file aot_export.h + * + * @brief This file defines the exported AOT compilation APIs + */ + #ifndef _AOT_EXPORT_H #define _AOT_EXPORT_H -#include +#include #include +#include "aot_comp_option.h" #ifdef __cplusplus extern "C" { @@ -20,12 +27,22 @@ typedef struct AOTCompData *aot_comp_data_t; struct AOTCompContext; typedef struct AOTCompContext *aot_comp_context_t; +struct AOTObjectData; +typedef struct AOTObjectData *aot_obj_data_t; + aot_comp_data_t -aot_create_comp_data(void *wasm_module); +aot_create_comp_data(void *wasm_module, const char *target_arch, + bool gc_enabled); void aot_destroy_comp_data(aot_comp_data_t comp_data); +#if WASM_ENABLE_DEBUG_AOT != 0 +typedef void *dwarf_extractor_handle_t; +dwarf_extractor_handle_t +create_dwarf_extractor(aot_comp_data_t comp_data, char *file_name); +#endif + enum { AOT_FORMAT_FILE, AOT_OBJECT_FILE, @@ -33,20 +50,14 @@ enum { AOT_LLVMIR_OPT_FILE, }; -typedef struct AOTCompOption{ - bool is_jit_mode; - char *target_arch; - char *target_abi; - char *target_cpu; - char *cpu_features; - uint32_t opt_level; - uint32_t size_level; - uint32_t output_format; -} AOTCompOption, *aot_comp_option_t; +bool +aot_compiler_init(void); + +void +aot_compiler_destroy(void); aot_comp_context_t -aot_create_comp_context(aot_comp_data_t comp_data, - aot_comp_option_t option); +aot_create_comp_context(aot_comp_data_t comp_data, aot_comp_option_t option); void aot_destroy_comp_context(aot_comp_context_t comp_ctx); @@ -54,6 +65,25 @@ aot_destroy_comp_context(aot_comp_context_t comp_ctx); bool aot_compile_wasm(aot_comp_context_t comp_ctx); +aot_obj_data_t +aot_obj_data_create(aot_comp_context_t comp_ctx); + +void +aot_obj_data_destroy(aot_obj_data_t obj_data); + +uint32_t +aot_get_aot_file_size(aot_comp_context_t comp_ctx, aot_comp_data_t comp_data, + aot_obj_data_t obj_data); + +uint8_t * +aot_emit_aot_file_buf(aot_comp_context_t comp_ctx, aot_comp_data_t comp_data, + uint32_t *p_aot_file_size); + +bool +aot_emit_aot_file_buf_ex(aot_comp_context_t comp_ctx, aot_comp_data_t comp_data, + aot_obj_data_t obj_data, uint8_t *aot_file_buf, + uint32_t aot_file_size); + bool aot_emit_llvm_file(aot_comp_context_t comp_ctx, const char *file_name); @@ -61,18 +91,17 @@ bool aot_emit_object_file(aot_comp_context_t comp_ctx, const char *file_name); bool -aot_emit_aot_file(aot_comp_context_t comp_ctx, - aot_comp_data_t comp_data, +aot_emit_aot_file(aot_comp_context_t comp_ctx, aot_comp_data_t comp_data, const char *file_name); void aot_destroy_aot_file(uint8_t *aot_file); -char* -aot_get_last_error(); +char * +aot_get_last_error(void); uint32_t -aot_get_plt_table_size(); +aot_get_plt_table_size(void); #ifdef __cplusplus } diff --git a/core/iwasm/include/bh_memory.h b/core/iwasm/include/bh_memory.h deleted file mode 100644 index 4b3aa86db5..0000000000 --- a/core/iwasm/include/bh_memory.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_MEMORY_H -#define _BH_MEMORY_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define BH_KB (1024) -#define BH_MB ((BH_KB)*1024) -#define BH_GB ((BH_MB)*1024) - -/** - * Initialize memory allocator with a pool, the bh_malloc/bh_free function - * will malloc/free memory from the pool - * - * @param mem the pool buffer - * @param bytes the size bytes of the buffer - * - * @return 0 if success, -1 otherwise - */ -int bh_memory_init_with_pool(void *mem, unsigned int bytes); - -/** - * Initialize memory allocator with memory allocator, the bh_malloc/bh_free - * function will malloc/free memory with the allocator passed - * - * @param malloc_func the malloc function - * @param free_func the free function - * - * @return 0 if success, -1 otherwise - */ -int bh_memory_init_with_allocator(void *malloc_func, void *free_func); - -/** - * Destroy memory - */ -void bh_memory_destroy(); - -/** - * Get the pool size of memory, if memory is initialized with allocator, - * return 1GB by default. - */ -unsigned bh_memory_pool_size(); - -#if BEIHAI_ENABLE_MEMORY_PROFILING == 0 - -/** - * This function allocates a memory chunk from system - * - * @param size bytes need allocate - * - * @return the pointer to memory allocated - */ -void* bh_malloc(unsigned int size); - -/** - * This function frees memory chunk - * - * @param ptr the pointer to memory need free - */ -void bh_free(void *ptr); - -#else - -void* bh_malloc_profile(const char *file, int line, const char *func, unsigned int size); -void bh_free_profile(const char *file, int line, const char *func, void *ptr); - -#define bh_malloc(size) bh_malloc_profile(__FILE__, __LINE__, __func__, size) -#define bh_free(ptr) bh_free_profile(__FILE__, __LINE__, __func__, ptr) - -/** - * Print current memory profiling data - * - * @param file file name of the caller - * @param line line of the file of the caller - * @param func function name of the caller - */ -void memory_profile_print(const char *file, int line, const char *func, int alloc); - -/** - * Summarize memory usage and print it out - * Can use awk to analyze the output like below: - * awk -F: '{print $2,$4,$6,$8,$9}' OFS="\t" ./out.txt | sort -n -r -k 1 - */ -void memory_usage_summarize(); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef _BH_MEMORY_H */ - diff --git a/core/iwasm/include/ext_lib_export.h b/core/iwasm/include/ext_lib_export.h deleted file mode 100644 index fbf8d17d41..0000000000 --- a/core/iwasm/include/ext_lib_export.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _EXT_LIB_EXPORT_H_ -#define _EXT_LIB_EXPORT_H_ - -#include "lib_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int -get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis) -{ - *p_ext_lib_apis = extended_native_symbol_defs; - return sizeof(extended_native_symbol_defs) / sizeof(NativeSymbol); -} - -#ifdef __cplusplus -} -#endif - -#endif /* end of _EXT_LIB_EXPORT_H_ */ - diff --git a/core/iwasm/include/gc_export.h b/core/iwasm/include/gc_export.h new file mode 100644 index 0000000000..bf2c36d604 --- /dev/null +++ b/core/iwasm/include/gc_export.h @@ -0,0 +1,955 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * @file gc_export.h + * + * @brief This file defines the exported GC APIs + */ + +#ifndef _GC_EXPORT_H +#define _GC_EXPORT_H + +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint8_t wasm_value_type_t; + +typedef enum wasm_value_type_enum { + VALUE_TYPE_I32 = 0x7F, + VALUE_TYPE_I64 = 0x7E, + VALUE_TYPE_F32 = 0x7D, + VALUE_TYPE_F64 = 0x7C, + VALUE_TYPE_V128 = 0x7B, + /* GC Types */ + VALUE_TYPE_I8 = 0x78, + VALUE_TYPE_I16 = 0x77, + VALUE_TYPE_NULLFUNCREF = 0x73, + VALUE_TYPE_NULLEXTERNREF = 0x72, + VALUE_TYPE_NULLREF = 0x71, + VALUE_TYPE_FUNCREF = 0x70, + VALUE_TYPE_EXTERNREF = 0x6F, + VALUE_TYPE_ANYREF = 0x6E, + VALUE_TYPE_EQREF = 0x6D, + VALUE_TYPE_I31REF = 0x6C, + VALUE_TYPE_STRUCTREF = 0x6B, + VALUE_TYPE_ARRAYREF = 0x6A, + VALUE_TYPE_HT_NON_NULLABLE_REF = 0x64, + VALUE_TYPE_HT_NULLABLE_REF = 0x63, + /* Stringref Types */ + VALUE_TYPE_STRINGREF = 0X67, + VALUE_TYPE_STRINGVIEWWTF8 = 0x66, + VALUE_TYPE_STRINGVIEWWTF16 = 0x62, + VALUE_TYPE_STRINGVIEWITER = 0x61 +} wasm_value_type_enum; + +typedef int32_t wasm_heap_type_t; + +typedef enum wasm_heap_type_enum { + HEAP_TYPE_NOFUNC = -0x0D, + HEAP_TYPE_NOEXTERN = -0x0E, + HEAP_TYPE_NONE = -0x0F, + HEAP_TYPE_FUNC = -0x10, + HEAP_TYPE_EXTERN = -0x11, + HEAP_TYPE_ANY = -0x12, + HEAP_TYPE_EQ = -0x13, + HEAP_TYPE_I31 = -0x14, + HEAP_TYPE_STRUCT = -0x15, + HEAP_TYPE_ARRAY = -0x16, + /* Stringref Types */ + HEAP_TYPE_STRINGREF = -0x19, + HEAP_TYPE_STRINGVIEWWTF8 = -0x1A, + HEAP_TYPE_STRINGVIEWWTF16 = -0x1E, + HEAP_TYPE_STRINGVIEWITER = -0x1F +} wasm_heap_type_enum; + +struct WASMObject; +typedef struct WASMObject *wasm_obj_t; + +#ifndef WASM_VALUE_DEFINED +#define WASM_VALUE_DEFINED +typedef union V128 { + int8_t i8x16[16]; + int16_t i16x8[8]; + int32_t i32x4[4]; + int64_t i64x2[2]; + float f32x4[4]; + double f64x2[2]; +} V128; + +typedef union WASMValue { + int32_t i32; + uint32_t u32; + uint32_t global_index; + uint32_t ref_index; + int64_t i64; + uint64_t u64; + float f32; + double f64; + V128 v128; + wasm_obj_t gc_obj; + uint32_t type_index; + struct { + uint32_t type_index; + uint32_t length; + } array_new_default; + /* pointer to a memory space holding more data, current usage: + * struct.new init value: WASMStructNewInitValues * + * array.new init value: WASMArrayNewInitValues * + */ + void *data; +} WASMValue; +#endif /* end of WASM_VALUE_DEFINED */ + +typedef union WASMValue wasm_value_t; + +/* Reference type, the layout is same as WasmRefType in wasm.h + * use wasm_ref_type_set_type_idx to initialize as concrete ref type + * use wasm_ref_type_set_heap_type to initialize as abstract ref type + */ +typedef struct wasm_ref_type_t { + wasm_value_type_t value_type; + bool nullable; + int32_t heap_type; +} wasm_ref_type_t; + +/** + * Local object reference that can be traced when GC occurs. All + * native functions that need to hold WASM objects which may not be + * referenced from other elements of GC root set may be hold with + * this type of variable so that they can be traced when GC occurs. + * Before using such a variable, it must be pushed onto the stack + * (implemented as a chain) of such variables, and before leaving the + * frame of the variables, they must be popped from the stack. + */ +typedef struct WASMLocalObjectRef { + /* Previous local object reference variable on the stack */ + struct WASMLocalObjectRef *prev; + /* The reference of WASM object hold by this variable */ + wasm_obj_t val; +} WASMLocalObjectRef, wasm_local_obj_ref_t; + +struct WASMType; +struct WASMFuncType; +struct WASMStructType; +struct WASMArrayType; + +typedef struct WASMType *wasm_defined_type_t; +typedef struct WASMFuncType *wasm_func_type_t; +typedef struct WASMStructType *wasm_struct_type_t; +typedef struct WASMArrayType *wasm_array_type_t; + +struct WASMExternrefObject; +struct WASMAnyrefObject; +struct WASMStructObject; +struct WASMArrayObject; +struct WASMFuncObject; + +typedef struct WASMExternrefObject *wasm_externref_obj_t; +typedef struct WASMAnyrefObject *wasm_anyref_obj_t; +typedef struct WASMStructObject *wasm_struct_obj_t; +typedef struct WASMArrayObject *wasm_array_obj_t; +typedef struct WASMFuncObject *wasm_func_obj_t; +typedef struct WASMStringrefObject *wasm_stringref_obj_t; +typedef uintptr_t wasm_i31_obj_t; + +typedef void (*wasm_obj_finalizer_t)(const wasm_obj_t obj, void *data); + +/* Defined type related operations */ + +/** + * Get number of defined types in the given wasm module + * + * @param module the wasm module + * + * @return defined type count + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_get_defined_type_count(const wasm_module_t module); + +/** + * Get defined type by type index + * + * @param module the wasm module + * @param index the type index + * + * @return defined type + */ +WASM_RUNTIME_API_EXTERN wasm_defined_type_t +wasm_get_defined_type(const wasm_module_t module, uint32_t index); + +/** + * Get defined type of the GC managed object, the object must be struct, + * array or func. + * + * @param obj the object + * + * @return defined type of the object. + */ +WASM_RUNTIME_API_EXTERN wasm_defined_type_t +wasm_obj_get_defined_type(const wasm_obj_t obj); + +/** + * Get defined type index of the GC managed object, the object must be struct, + * array or func. + * + * @param obj the object + * + * @return defined type index of the object. + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_obj_get_defined_type_idx(const wasm_module_t module, const wasm_obj_t obj); + +/** + * Check whether a defined type is a function type + * + * @param def_type the defined type to be checked + * + * @return true if the defined type is function type, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_defined_type_is_func_type(const wasm_defined_type_t def_type); + +/** + * Check whether a defined type is a struct type + * + * @param def_type the defined type to be checked + * + * @return true if the defined type is struct type, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_defined_type_is_struct_type(const wasm_defined_type_t def_type); + +/** + * Check whether a defined type is an array type + * + * @param def_type the defined type to be checked + * + * @return true if the defined type is array type, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_defined_type_is_array_type(const wasm_defined_type_t def_type); + +/** + * Get type of a specified parameter of a function type + * + * @param func_type the specified function type + * @param param_idx the specified param index + * + * @return the param type at the specified param index of the specified func + * type + */ +WASM_RUNTIME_API_EXTERN wasm_ref_type_t +wasm_func_type_get_param_type(const wasm_func_type_t func_type, + uint32_t param_idx); + +/** + * Get type of a specified result of a function type + * + * @param func_type the specified function type + * @param param_idx the specified result index + * + * @return the result type at the specified result index of the specified func + * type + */ +WASM_RUNTIME_API_EXTERN wasm_ref_type_t +wasm_func_type_get_result_type(const wasm_func_type_t func_type, + uint32_t result_idx); + +/** + * Get field count of a struct type + * + * @param struct_type the specified struct type + * + * @return the field count of the specified struct type + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_struct_type_get_field_count(const wasm_struct_type_t struct_type); + +/** + * Get type of a specified field of a struct type + * + * @param struct_type the specified struct type + * @param field_idx index of the specified field + * @param p_is_mutable if not NULL, output the mutability of the field + * + * @return the result type at the specified field index of the specified struct + */ +WASM_RUNTIME_API_EXTERN wasm_ref_type_t +wasm_struct_type_get_field_type(const wasm_struct_type_t struct_type, + uint32_t field_idx, bool *p_is_mutable); + +/** + * Get element type of an array type + * + * @param array_type the specified array type + * @param p_is_mutable if not NULL, output the mutability of the element type + * + * @return the ref type of array's elem type + */ +WASM_RUNTIME_API_EXTERN wasm_ref_type_t +wasm_array_type_get_elem_type(const wasm_array_type_t array_type, + bool *p_is_mutable); + +/** + * Check whether two defined types are equal + * + * @param def_type1 the specified defined type1 + * @param def_type2 the specified defined type2 + * @param module current wasm module + * + * @return true if the defined type1 is equal to the defined type2, + * false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_defined_type_equal(const wasm_defined_type_t def_type1, + const wasm_defined_type_t def_type2, + const wasm_module_t module); + +/** + * Check whether def_type1 is subtype of def_type2 + * + * @param def_type1 the specified defined type1 + * @param def_type2 the specified defined type2 + * @param module current wasm module + * + * @return true if the defined type1 is subtype of the defined type2, + * false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_defined_type_is_subtype_of(const wasm_defined_type_t def_type1, + const wasm_defined_type_t def_type2, + const wasm_module_t module); + +/* ref type related operations */ + +/** + * Set the ref_type to be (ref null? type_idx) + * + * @param ref_type the ref_type to be set + * @param nullable whether the ref_type is nullable + * @param type_idx the type index + */ +WASM_RUNTIME_API_EXTERN void +wasm_ref_type_set_type_idx(wasm_ref_type_t *ref_type, bool nullable, + int32_t type_idx); + +/** + * Set the ref_type to be (ref null? func/extern/any/eq/i31/struct/array/..) + * + * @param ref_type the ref_type to be set + * @param nullable whether the ref_type is nullable + * @param heap_type the heap type + */ +WASM_RUNTIME_API_EXTERN void +wasm_ref_type_set_heap_type(wasm_ref_type_t *ref_type, bool nullable, + int32_t heap_type); + +/** + * Check whether two ref types are equal + * + * @param ref_type1 the specified ref type1 + * @param ref_type2 the specified ref type2 + * @param module current wasm module + * + * @return true if the ref type1 is equal to the ref type2, + * false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_ref_type_equal(const wasm_ref_type_t *ref_type1, + const wasm_ref_type_t *ref_type2, + const wasm_module_t module); + +/** + * Check whether ref_type1 is subtype of ref_type2 + * + * @param ref_type1 the specified ref type1 + * @param ref_type2 the specified ref type2 + * @param module current wasm module + * + * @return true if the ref type1 is subtype of the ref type2, + * false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_ref_type_is_subtype_of(const wasm_ref_type_t *ref_type1, + const wasm_ref_type_t *ref_type2, + const wasm_module_t module); + +/* wasm object related operations */ + +/** + * Create a struct object with the index of defined type + * + * @param exec_env the execution environment + * @param type_idx index of the struct type + * + * @return wasm_struct_obj_t if create success, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_struct_obj_t +wasm_struct_obj_new_with_typeidx(wasm_exec_env_t exec_env, uint32_t type_idx); + +/** + * Create a struct object with the struct type + * + * @param exec_env the execution environment + * @param type defined struct type + * + * @return wasm_struct_obj_t if create success, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_struct_obj_t +wasm_struct_obj_new_with_type(wasm_exec_env_t exec_env, + const wasm_struct_type_t type); + +/** + * Set the field value of a struct object + * + * @param obj the struct object to set field + * @param field_idx the specified field index + * @param value wasm value to be set + */ +WASM_RUNTIME_API_EXTERN void +wasm_struct_obj_set_field(wasm_struct_obj_t obj, uint32_t field_idx, + const wasm_value_t *value); + +/** + * Get the field value of a struct object + * + * @param obj the struct object to get field + * @param field_idx the specified field index + * @param sign_extend whether to sign extend for i8 and i16 element types + * @param value output the wasm value + */ +WASM_RUNTIME_API_EXTERN void +wasm_struct_obj_get_field(const wasm_struct_obj_t obj, uint32_t field_idx, + bool sign_extend, wasm_value_t *value); + +/** + * Get the field count of the a struct object. + * + * @param obj the WASM struct object + * + * @return the field count of the a struct object + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_struct_obj_get_field_count(const wasm_struct_obj_t obj); + +/** + * Create an array object with the index of defined type, the obj's length is + * length, init value is init_value + * + * @param exec_env the execution environment + * @param type_idx the index of the specified type + * @param length the array's length + * @param init_value the array's init value + * + * @return the created array object + */ +WASM_RUNTIME_API_EXTERN wasm_array_obj_t +wasm_array_obj_new_with_typeidx(wasm_exec_env_t exec_env, uint32_t type_idx, + uint32_t length, wasm_value_t *init_value); + +/** + * Create an array object with the array type, the obj's length is length, init + * value is init_value + * + * @param exec_env the execution environment + * @param type the array's specified type + * @param length the array's length + * @param init_value the array's init value + * + * @return the created array object + */ +WASM_RUNTIME_API_EXTERN wasm_array_obj_t +wasm_array_obj_new_with_type(wasm_exec_env_t exec_env, + const wasm_array_type_t type, uint32_t length, + wasm_value_t *init_value); + +/** + * Set the specified element's value of an array object + * + * @param array_obj the array object to set element value + * @param elem_idx the specified element index + * @param value wasm value to be set + */ +WASM_RUNTIME_API_EXTERN void +wasm_array_obj_set_elem(wasm_array_obj_t array_obj, uint32_t elem_idx, + const wasm_value_t *value); + +/** + * Get the specified element's value of an array object + * + * @param array_obj the array object to get element value + * @param elem_idx the specified element index + * @param sign_extend whether to sign extend for i8 and i16 element types + * @param value output the wasm value + */ +WASM_RUNTIME_API_EXTERN void +wasm_array_obj_get_elem(const wasm_array_obj_t array_obj, uint32_t elem_idx, + bool sign_extend, wasm_value_t *value); + +/** + * Copy elements from one array to another + * + * @param dst_obj destination array object + * @param dst_idx target index in destination + * @param src_obj source array object + * @param src_idx start index in source + * @param len length of elements to copy + */ +WASM_RUNTIME_API_EXTERN void +wasm_array_obj_copy(wasm_array_obj_t dst_obj, uint32_t dst_idx, + const wasm_array_obj_t src_obj, uint32_t src_idx, + uint32_t len); + +/** + * Return the length of an array object + * + * @param array_obj the array object to get length + * + * @return length of the array object + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_array_obj_length(const wasm_array_obj_t array_obj); + +/** + * Get the address of the first element of an array object + * + * @param array_obj the array object to get element address + * + * @return address of the first element + */ +WASM_RUNTIME_API_EXTERN void * +wasm_array_obj_first_elem_addr(const wasm_array_obj_t array_obj); + +/** + * Get the address of the i-th element of an array object + * + * @param array_obj the array object to get element address + * @param elem_idx the specified element index + * + * @return address of the specified element + */ +WASM_RUNTIME_API_EXTERN void * +wasm_array_obj_elem_addr(const wasm_array_obj_t array_obj, uint32_t elem_idx); + +/** + * Create a function object with the index of defined type and the index of the + * function + * + * @param exec_env the execution environment + * @param type_idx the index of the specified type + * @param func_idx_bound the index of the function + * + * @return the created function object + */ +WASM_RUNTIME_API_EXTERN wasm_func_obj_t +wasm_func_obj_new_with_typeidx(wasm_exec_env_t exec_env, uint32_t type_idx, + uint32_t func_idx_bound); + +/** + * Create a function object with the function type and the index of the function + * + * @param exec_env the execution environment + * @param type the specified type + * @param func_idx_bound the index of the function + * + * @return the created function object + */ +WASM_RUNTIME_API_EXTERN wasm_func_obj_t +wasm_func_obj_new_with_type(wasm_exec_env_t exec_env, wasm_func_type_t type, + uint32_t func_idx_bound); + +/** + * Get the function index bound of a function object + * + * @param func_obj the function object + * + * @return the bound function index + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_func_obj_get_func_idx_bound(const wasm_func_obj_t func_obj); + +/** + * Get the function type of a function object + * + * @param func_obj the function object + * + * @return defined function type + */ +WASM_RUNTIME_API_EXTERN wasm_func_type_t +wasm_func_obj_get_func_type(const wasm_func_obj_t func_obj); + +/** + * Call the given WASM function object with arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param func_obj the function object to call + * @param argc total cell number that the function parameters occupy, + * a cell is a slot of the uint32 array argv[], e.g. i32/f32 argument + * occupies one cell, i64/f64 argument occupies two cells, note that + * it might be different from the parameter number of the function + * @param argv the arguments. If the function 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 the exception + * info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_func_ref(wasm_exec_env_t exec_env, + const wasm_func_obj_t func_obj, uint32_t argc, + uint32_t argv[]); + +/** + * Call the given WASM function object with provided results space + * and arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param func_obj the function object to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param args the arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_func_ref_a(wasm_exec_env_t exec_env, + const wasm_func_obj_t func_obj, + uint32_t num_results, wasm_val_t results[], + uint32_t num_args, wasm_val_t *args); + +/** + * Call the given WASM function object with provided results space and + * variant arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param func_obj the function object to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param ... the variant arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_func_ref_v(wasm_exec_env_t exec_env, + const wasm_func_obj_t func_obj, + uint32_t num_results, wasm_val_t results[], + uint32_t num_args, ...); + +/** + * Create an externref object with host object + * + * @param exec_env the execution environment + * @param host_obj host object pointer + * + * @return wasm_externref_obj_t if success, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_externref_obj_t +wasm_externref_obj_new(wasm_exec_env_t exec_env, const void *host_obj); + +/** + * Get the host value of an externref object + * + * @param externref_obj the externref object + * + * @return the stored host object pointer + */ +WASM_RUNTIME_API_EXTERN const void * +wasm_externref_obj_get_value(const wasm_externref_obj_t externref_obj); + +/** + * Create an anyref object with host object + * + * @param exec_env the execution environment + * @param host_obj host object pointer + * + * @return wasm_anyref_obj_t if success, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_anyref_obj_t +wasm_anyref_obj_new(wasm_exec_env_t exec_env, const void *host_obj); + +/** + * Get the host object value of an anyref object + * + * @param anyref_obj the anyref object + * + * @return the stored host object pointer + */ +WASM_RUNTIME_API_EXTERN const void * +wasm_anyref_obj_get_value(const wasm_anyref_obj_t anyref_obj); + +/** + * Get the internal object inside the externref object, same as + * the operation of opcode extern.internalize + * + * @param externref_obj the externref object + * + * @return internalized wasm_obj_t + */ +WASM_RUNTIME_API_EXTERN wasm_obj_t +wasm_externref_obj_to_internal_obj(const wasm_externref_obj_t externref_obj); + +/** + * Create an externref object from an internal object, same as + * the operation of opcode extern.externalize + * + * @param exec_env the execution environment + * @param internal_obj the internal object + * + * @return wasm_externref_obj_t if create success, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_externref_obj_t +wasm_internal_obj_to_externref_obj(wasm_exec_env_t exec_env, + const wasm_obj_t internal_obj); + +/** + * Create an i31 object + * + * @param i31_value the scalar value + * + * @return wasm_i31_obj_t + */ +WASM_RUNTIME_API_EXTERN wasm_i31_obj_t +wasm_i31_obj_new(uint32_t i31_value); + +/** + * Get value from an i31 object + * + * @param i31_obj the i31 object + * @param sign_extend whether to sign extend the value + * + * @return wasm_i31_obj_t + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_i31_obj_get_value(wasm_i31_obj_t i31_obj, bool sign_extend); + +/** + * Pin an object to make it traced during GC + * + * @param exec_env the execution environment + * @param obj the object to pin + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_pin_object(wasm_exec_env_t exec_env, wasm_obj_t obj); + +/** + * Unpin an object + * + * @param exec_env the execution environment + * @param obj the object to unpin + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_unpin_object(wasm_exec_env_t exec_env, wasm_obj_t obj); + +/** + * Check whether an object is a struct object + * + * @param obj the object to check + * + * @return true if the object is a struct, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_struct_obj(const wasm_obj_t obj); + +/** + * Check whether an object is an array object + * + * @param obj the object to check + * + * @return true if the object is a array, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_array_obj(const wasm_obj_t obj); + +/** + * Check whether an object is a function object + * + * @param obj the object to check + * + * @return true if the object is a function, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_func_obj(const wasm_obj_t obj); + +/** + * Check whether an object is an i31 object + * + * @param obj the object to check + * + * @return true if the object is an i32, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_i31_obj(const wasm_obj_t obj); + +/** + * Check whether an object is an externref object + * + * @param obj the object to check + * + * @return true if the object is an externref, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_externref_obj(const wasm_obj_t obj); + +/** + * Check whether an object is an anyref object + * + * @param obj the object to check + * + * @return true if the object is an anyref, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_anyref_obj(const wasm_obj_t obj); + +/** + * Check whether an object is a struct object, or, an i31/struct/array object + * + * @param obj the object to check + * + * @return true if the object is an internal object, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_internal_obj(const wasm_obj_t obj); + +/** + * Check whether an object is an eq object + * + * @param obj the object to check + * + * @return true if the object is an eq object, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_eq_obj(const wasm_obj_t obj); + +/** + * Check whether an object is an instance of a defined type + * + * @param obj the object to check + * @param defined_type the defined type + * @param module current wasm module + * + * @return true if the object is instance of the defined type, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_instance_of_defined_type(const wasm_obj_t obj, + const wasm_defined_type_t defined_type, + const wasm_module_t module); + +/** + * Check whether an object is an instance of a defined type with + * index type_idx + * + * @param obj the object to check + * @param type_idx the type index + * @param module current wasm module + * + * @return true if the object is instance of the defined type specified by + * type_idx, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_instance_of_type_idx(const wasm_obj_t obj, uint32_t type_idx, + const wasm_module_t module); + +/** + * Check whether an object is an instance of a ref type + * + * @param obj the object to check + * @param ref_type the ref type + * + * @return true if the object is instance of the ref type, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_obj_is_instance_of_ref_type(const wasm_obj_t obj, + const wasm_ref_type_t *ref_type); + +/** + * Push a local object ref into stack, note that we should set its value + * after pushing to retain it during GC, and should pop it from stack + * before returning from the current function + * + * @param exec_env the execution environment + * @param local_obj_ref the local object ref to push + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_push_local_obj_ref(wasm_exec_env_t exec_env, + wasm_local_obj_ref_t *local_obj_ref); + +/** + * Pop a local object ref from stack + * + * @param exec_env the execution environment + * + * @return the popped wasm_local_obj_ref_t + */ +WASM_RUNTIME_API_EXTERN wasm_local_obj_ref_t * +wasm_runtime_pop_local_obj_ref(wasm_exec_env_t exec_env); + +/** + * Pop n local object refs from stack + * + * @param exec_env the execution environment + * @param n number to pop + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_pop_local_obj_refs(wasm_exec_env_t exec_env, uint32_t n); + +/** + * Get current local object ref from stack + * + * @param exec_env the execution environment + * + * @return the wasm_local_obj_ref_t obj from the top of the stack, not change + * the state of the stack + */ +WASM_RUNTIME_API_EXTERN wasm_local_obj_ref_t * +wasm_runtime_get_cur_local_obj_ref(wasm_exec_env_t exec_env); + +/** + * Set finalizer to the given object, if another finalizer is set to the same + * object, the previous one will be cancelled + * + * @param exec_env the execution environment + * @param obj object to set finalizer + * @param cb finalizer function to be called before this object is freed + * @param data custom data to be passed to finalizer function + * + * @return true if success, false otherwise + */ +bool +wasm_obj_set_gc_finalizer(wasm_exec_env_t exec_env, const wasm_obj_t obj, + wasm_obj_finalizer_t cb, void *data); + +/** + * Unset finalizer to the given object + * + * @param exec_env the execution environment + * @param obj object to unset finalizer + */ +void +wasm_obj_unset_gc_finalizer(wasm_exec_env_t exec_env, void *obj); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _GC_EXPORT_H */ diff --git a/core/iwasm/include/lib_export.h b/core/iwasm/include/lib_export.h index 2d3de17226..0ca668f52e 100644 --- a/core/iwasm/include/lib_export.h +++ b/core/iwasm/include/lib_export.h @@ -3,9 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file lib_export.h + * + */ + #ifndef _LIB_EXPORT_H_ #define _LIB_EXPORT_H_ +#include + #ifdef __cplusplus extern "C" { #endif @@ -13,10 +20,28 @@ extern "C" { typedef struct NativeSymbol { const char *symbol; void *func_ptr; + const char *signature; + /* attachment which can be retrieved in native API by + calling wasm_runtime_get_function_attachment(exec_env) */ + void *attachment; } NativeSymbol; -#define EXPORT_WASM_API(symbol) {#symbol, symbol} -#define EXPORT_WASM_API2(symbol) {#symbol, symbol##_wrapper} +/* clang-format off */ +#define EXPORT_WASM_API(symbol) \ + { #symbol, (void *)symbol, NULL, NULL } +#define EXPORT_WASM_API2(symbol) \ + { #symbol, (void *)symbol##_wrapper, NULL, NULL } + +#define EXPORT_WASM_API_WITH_SIG(symbol, signature) \ + { #symbol, (void *)symbol, signature, NULL } +#define EXPORT_WASM_API_WITH_SIG2(symbol, signature) \ + { #symbol, (void *)symbol##_wrapper, signature, NULL } + +#define EXPORT_WASM_API_WITH_ATT(symbol, signature, attachment) \ + { #symbol, (void *)symbol, signature, attachment } +#define EXPORT_WASM_API_WITH_ATT2(symbol, signature, attachment) \ + { #symbol, (void *)symbol##_wrapper, signature, attachment } +/* clang-format on */ /** * Get the exported APIs of base lib @@ -25,28 +50,11 @@ typedef struct NativeSymbol { * * @return the number of the exported API */ -int +uint32_t get_base_lib_export_apis(NativeSymbol **p_base_lib_apis); -/** - * Get the exported APIs of extended lib, this API isn't provided by WASM VM, - * it must be provided by developer to register the extended native APIs, - * for example, developer can register his native APIs to extended_native_symbol_defs, - * array, and include file ext_lib_export.h which implements this API. - * And if developer hasn't any native API to register, he can define an empty - * extended_native_symbol_defs array, and then include file ext_lib_export.h to - * implements this API. - * - * @param p_base_lib_apis return the exported API array of extend lib - * - * @return the number of the exported API - */ -int -get_extend_lib_export_apis(NativeSymbol **p_base_lib_apis); - #ifdef __cplusplus } #endif -#endif - +#endif /* end of _LIB_EXPORT_H_ */ diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h new file mode 100644 index 0000000000..00920adcb7 --- /dev/null +++ b/core/iwasm/include/wasm_c_api.h @@ -0,0 +1,912 @@ +// WebAssembly C API + +/** + * @file wasm_c_api.h + * + * @brief This file defines the WebAssembly C APIs + */ + +#ifndef _WASM_C_API_H_ +#define _WASM_C_API_H_ + +#include +#include +#include +#include +#include + +#ifndef WASM_API_EXTERN +#if defined(_MSC_BUILD) +#if defined(COMPILING_WASM_RUNTIME_API) +#define WASM_API_EXTERN __declspec(dllexport) +#else +#define WASM_API_EXTERN __declspec(dllimport) +#endif +#elif defined(__GNUC__) || defined(__clang__) +#define WASM_API_EXTERN __attribute__((visibility("default"))) +#else +#define WASM_API_EXTERN +#endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define WASM_API_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define WASM_API_DEPRECATED __declspec(deprecated) +#else +#pragma message("WARNING: You need to implement DEPRECATED for this compiler") +#define WASM_API_DEPRECATED +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* clang-format off */ + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +// Machine types +#if defined(__STDC_VERSION__) && (__STDC_VERSION__) > 199901L +inline void assertions(void) { + static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type"); + static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type"); + static_assert(sizeof(intptr_t) == sizeof(uint32_t) || + sizeof(intptr_t) == sizeof(uint64_t), + "incompatible pointer type"); +} +#endif + +typedef char byte_t; +typedef float float32_t; +typedef double float64_t; + + +// Ownership + +#define own + +// The qualifier `own` is used to indicate ownership of data in this API. +// It is intended to be interpreted similar to a `const` qualifier: +// +// - `own wasm_xxx_t*` owns the pointed-to data +// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx` +// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!) +// - an `own` function parameter passes ownership from caller to callee +// - an `own` function result passes ownership from callee to caller +// - an exception are `own` pointer parameters named `out`, which are copy-back +// output parameters passing back ownership from callee to caller +// +// Own data is created by `wasm_xxx_new` functions and some others. +// It must be released with the corresponding `wasm_xxx_delete` function. +// +// Deleting a reference does not necessarily delete the underlying object, +// it merely indicates that this owner no longer uses it. +// +// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that +// neither the vector nor its elements should be modified. +// TODO: introduce proper `wasm_xxx_const_vec_t`? + + +#define WASM_DECLARE_OWN(name) \ + typedef struct wasm_##name##_t wasm_##name##_t; \ + \ + WASM_API_EXTERN void wasm_##name##_delete(own wasm_##name##_t*); + + +// Vectors +// size: capacity +// num_elems: current number of elements +// size_of_elem: size of one element +#define WASM_DECLARE_VEC(name, ptr_or_none) \ + typedef struct wasm_##name##_vec_t { \ + size_t size; \ + wasm_##name##_t ptr_or_none* data; \ + size_t num_elems; \ + size_t size_of_elem; \ + void *lock; \ + } wasm_##name##_vec_t; \ + \ + WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \ + WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \ + own wasm_##name##_vec_t* out, size_t); \ + WASM_API_EXTERN void wasm_##name##_vec_new( \ + own wasm_##name##_vec_t* out, \ + size_t, own wasm_##name##_t ptr_or_none const[]); \ + WASM_API_EXTERN void wasm_##name##_vec_copy( \ + own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \ + WASM_API_EXTERN void wasm_##name##_vec_delete(own wasm_##name##_vec_t*); + + +// Byte vectors + +typedef byte_t wasm_byte_t; +WASM_DECLARE_VEC(byte, ) + +typedef wasm_byte_vec_t wasm_name_t; + +#define wasm_name wasm_byte_vec +#define wasm_name_new wasm_byte_vec_new +#define wasm_name_new_empty wasm_byte_vec_new_empty +#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized +#define wasm_name_copy wasm_byte_vec_copy +#define wasm_name_delete wasm_byte_vec_delete + +static inline void wasm_name_new_from_string( + own wasm_name_t* out, const char* s +) { + wasm_name_new(out, strlen(s), s); +} + +static inline void wasm_name_new_from_string_nt( + own wasm_name_t* out, const char* s +) { + wasm_name_new(out, strlen(s) + 1, s); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Configuration + +WASM_DECLARE_OWN(config) + +#ifndef MEM_ALLOC_OPTION_DEFINED +#define MEM_ALLOC_OPTION_DEFINED +/* same definition from wasm_export.h */ +/* Memory allocator type */ +typedef enum { + /* pool mode, allocate memory from user defined heap buffer */ + Alloc_With_Pool = 0, + /* user allocator mode, allocate memory from user defined + malloc function */ + Alloc_With_Allocator, + /* system allocator mode, allocate memory from system allocator, + or, platform's os_malloc function */ + Alloc_With_System_Allocator, +} mem_alloc_type_t; + +/* Memory allocator option */ +typedef union MemAllocOption { + struct { + void *heap_buf; + uint32_t heap_size; + } pool; + struct { + void *malloc_func; + void *realloc_func; + void *free_func; + /* allocator user data, only used when + WASM_MEM_ALLOC_WITH_USER_DATA is defined */ + void *user_data; + } allocator; +} MemAllocOption; +#endif /* MEM_ALLOC_OPTION_DEFINED */ + +/* Runtime configuration */ +struct wasm_config_t { + mem_alloc_type_t mem_alloc_type; + MemAllocOption mem_alloc_option; + uint32_t segue_flags; + bool enable_linux_perf; + /*TODO: wasi args*/ +}; + +#ifndef INSTANTIATION_ARGS_OPTION_DEFINED +#define INSTANTIATION_ARGS_OPTION_DEFINED +/* WASM module instantiation arguments */ +typedef struct InstantiationArgs { + uint32_t default_stack_size; + uint32_t host_managed_heap_size; + uint32_t max_memory_pages; +} InstantiationArgs; +#endif /* INSTANTIATION_ARGS_OPTION_DEFINED */ + +/* + * by default: + * - mem_alloc_type is Alloc_With_System_Allocator + * - mem_alloc_option is all 0 + * - enable_linux_perf is false + */ +WASM_API_EXTERN own wasm_config_t* wasm_config_new(void); + +// Embedders may provide custom functions for manipulating configs. +WASM_API_EXTERN own wasm_config_t* +wasm_config_set_mem_alloc_opt(wasm_config_t *, mem_alloc_type_t, MemAllocOption *); + +WASM_API_EXTERN own wasm_config_t* +wasm_config_set_linux_perf_opt(wasm_config_t *, bool); + +/** + * Enable using GS register as the base address of linear memory in linux x86_64, + * which may speedup the linear memory access for LLVM AOT/JIT: + * bit0 to bit4 denotes i32.load, i64.load, f32.load, f64.load, v128.load + * bit8 to bit12 denotes i32.store, i64.store, f32.store, f64.store, v128.store + * For example, 0x01 enables i32.load, 0x0100 enables i32.store. + * To enable all load/store operations, use 0x1F1F + */ +WASM_API_EXTERN wasm_config_t* +wasm_config_set_segue_flags(wasm_config_t *config, uint32_t segue_flags); + +// Engine + +WASM_DECLARE_OWN(engine) + +/** + * Create a new engine + * + * Note: for the engine new/delete operations, including this, + * wasm_engine_new_with_config, wasm_engine_new_with_args, and + * wasm_engine_delete, if the platform has mutex initializer, + * then they are thread-safe: we use a global lock to lock the + * operations of the engine. Otherwise they are not thread-safe: + * when there are engine new/delete operations happening + * simultaneously in multiple threads, developer must create + * the lock by himself, and add the lock when calling these + * functions. + */ +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(void); +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new_with_config(wasm_config_t*); +WASM_API_DEPRECATED WASM_API_EXTERN own wasm_engine_t * +wasm_engine_new_with_args(mem_alloc_type_t type, const MemAllocOption *opts); + +// Store + +WASM_DECLARE_OWN(store) + +WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Type attributes + +typedef uint8_t wasm_mutability_t; +enum wasm_mutability_enum { + WASM_CONST, + WASM_VAR, +}; + +typedef struct wasm_limits_t { + uint32_t min; + uint32_t max; +} wasm_limits_t; + +static const uint32_t wasm_limits_max_default = 0xffffffff; + + +// Generic + +#define WASM_DECLARE_TYPE(name) \ + WASM_DECLARE_OWN(name) \ + WASM_DECLARE_VEC(name, *) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); + + +// Value Types + +WASM_DECLARE_TYPE(valtype) + +#ifndef WASM_VALKIND_T_DEFINED +#define WASM_VALKIND_T_DEFINED +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_V128, + WASM_EXTERNREF = 128, + WASM_FUNCREF, +}; +#endif + +WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); + +WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*); + +static inline bool wasm_valkind_is_num(wasm_valkind_t k) { + return k < WASM_EXTERNREF; +} +static inline bool wasm_valkind_is_ref(wasm_valkind_t k) { + return k >= WASM_EXTERNREF; +} + +static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) { + return wasm_valkind_is_num(wasm_valtype_kind(t)); +} +static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) { + return wasm_valkind_is_ref(wasm_valtype_kind(t)); +} + + +// Function Types + +WASM_DECLARE_TYPE(functype) + +WASM_API_EXTERN own wasm_functype_t* wasm_functype_new( + own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results); + +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*); +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*); + + +// Global Types + +WASM_DECLARE_TYPE(globaltype) + +WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new( + own wasm_valtype_t*, wasm_mutability_t); + +WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*); +WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*); + + +// Table Types + +WASM_DECLARE_TYPE(tabletype) + +WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new( + own wasm_valtype_t*, const wasm_limits_t*); + +WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*); + + +// Memory Types + +WASM_DECLARE_TYPE(memorytype) + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*); + +WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*); + + +// Extern Types + +WASM_DECLARE_TYPE(externtype) + +typedef uint8_t wasm_externkind_t; +enum wasm_externkind_enum { + WASM_EXTERN_FUNC, + WASM_EXTERN_GLOBAL, + WASM_EXTERN_TABLE, + WASM_EXTERN_MEMORY, +}; + +WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*); + +WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*); + +WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*); +WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*); +WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*); +WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*); + +WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*); + +WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*); + + +// Import Types + +WASM_DECLARE_TYPE(importtype) + +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); + +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 + +WASM_DECLARE_TYPE(exporttype) + +WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new( + own wasm_name_t*, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// Values + +#ifndef WASM_VAL_T_DEFINED +#define WASM_VAL_T_DEFINED +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + uint8_t _paddings[7]; + union { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + struct wasm_ref_t* ref; + } of; +} wasm_val_t; +#endif + +WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v); +WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*); + +WASM_DECLARE_VEC(val, ) + + +// References + +#define WASM_DECLARE_REF_BASE(name) \ + WASM_DECLARE_OWN(name) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \ + WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); \ + \ + WASM_API_EXTERN void* wasm_##name##_get_host_info(const wasm_##name##_t*); \ + WASM_API_EXTERN void wasm_##name##_set_host_info(wasm_##name##_t*, void*); \ + WASM_API_EXTERN void wasm_##name##_set_host_info_with_finalizer( \ + wasm_##name##_t*, void*, void (*)(void*)); + +#define WASM_DECLARE_REF(name) \ + WASM_DECLARE_REF_BASE(name) \ + \ + WASM_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \ + WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \ + WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \ + WASM_API_EXTERN const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*); + +#define WASM_DECLARE_SHARABLE_REF(name) \ + WASM_DECLARE_REF(name) \ + WASM_DECLARE_OWN(shared_##name) \ + \ + WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*); + + +WASM_DECLARE_REF_BASE(ref) + + +// Frames + +WASM_DECLARE_OWN(frame) +WASM_DECLARE_VEC(frame, *) +WASM_API_EXTERN own wasm_frame_t* wasm_frame_copy(const wasm_frame_t*); + +WASM_API_EXTERN struct wasm_instance_t* wasm_frame_instance(const wasm_frame_t*); +WASM_API_EXTERN uint32_t wasm_frame_func_index(const wasm_frame_t*); +WASM_API_EXTERN size_t wasm_frame_func_offset(const wasm_frame_t*); +WASM_API_EXTERN size_t wasm_frame_module_offset(const wasm_frame_t*); + + +// Traps + +typedef wasm_name_t wasm_message_t; // null terminated + +WASM_DECLARE_REF(trap) + +WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*); + +WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out); +WASM_API_EXTERN own wasm_frame_t* wasm_trap_origin(const wasm_trap_t*); +WASM_API_EXTERN void wasm_trap_trace(const wasm_trap_t*, own wasm_frame_vec_t* out); + + +// Foreign Objects + +WASM_DECLARE_REF(foreign) + +WASM_API_EXTERN own wasm_foreign_t* wasm_foreign_new(wasm_store_t*); + + +// Modules +// WASM_DECLARE_SHARABLE_REF(module) + +#ifndef WASM_MODULE_T_DEFINED +#define WASM_MODULE_T_DEFINED +struct WASMModuleCommon; +typedef struct WASMModuleCommon *wasm_module_t; +#endif + +#ifndef LOAD_ARGS_OPTION_DEFINED +#define LOAD_ARGS_OPTION_DEFINED +typedef struct LoadArgs { + char *name; + /* True by default, used by wasm-c-api only. + If false, the wasm input buffer (wasm_byte_vec_t) is referenced by the + module instead of being cloned. Hence, it can be freed after module loading. */ + bool clone_wasm_binary; + /* This option is only used by the AOT/wasm loader (see wasm_export.h) */ + bool wasm_binary_freeable; + /* false by default, if true, don't resolve the symbols yet. The + wasm_runtime_load_ex has to be followed by a wasm_runtime_resolve_symbols + call */ + bool no_resolve; + /* TODO: more fields? */ +} LoadArgs; +#endif /* LOAD_ARGS_OPTION_DEFINED */ + +WASM_API_EXTERN own wasm_module_t* wasm_module_new( + wasm_store_t*, const wasm_byte_vec_t* binary); + +// please refer to wasm_runtime_load_ex(...) in core/iwasm/include/wasm_export.h +WASM_API_EXTERN own wasm_module_t* wasm_module_new_ex( + wasm_store_t*, wasm_byte_vec_t* binary, LoadArgs *args); + +WASM_API_EXTERN void wasm_module_delete(own wasm_module_t*); + +WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); +WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); + +WASM_API_EXTERN void wasm_module_serialize(wasm_module_t*, own wasm_byte_vec_t* out); +WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*); + +typedef wasm_module_t wasm_shared_module_t; +WASM_API_EXTERN own wasm_shared_module_t* wasm_module_share(wasm_module_t*); +WASM_API_EXTERN own wasm_module_t* wasm_module_obtain(wasm_store_t*, wasm_shared_module_t*); +WASM_API_EXTERN void wasm_shared_module_delete(own wasm_shared_module_t*); + +WASM_API_EXTERN bool wasm_module_set_name(wasm_module_t*, const char* name); +WASM_API_EXTERN const char *wasm_module_get_name(wasm_module_t*); + +WASM_API_EXTERN bool wasm_module_is_underlying_binary_freeable(const wasm_module_t *module); + + +// Function Instances + +WASM_DECLARE_REF(func) + +typedef own wasm_trap_t* (*wasm_func_callback_t)( + const wasm_val_vec_t* args, own wasm_val_vec_t *results); +typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)( + void* env, const wasm_val_vec_t *args, wasm_val_vec_t *results); + +WASM_API_EXTERN own wasm_func_t* wasm_func_new( + wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t); +WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env( + wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t, + void* env, void (*finalizer)(void*)); + +WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*); + +WASM_API_EXTERN own wasm_trap_t* wasm_func_call( + const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results); + + +// Global Instances + +WASM_DECLARE_REF(global) + +WASM_API_EXTERN own wasm_global_t* wasm_global_new( + wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*); + +WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*); + +WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out); +WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*); + + +// Table Instances + +WASM_DECLARE_REF(table) + +typedef uint32_t wasm_table_size_t; + +WASM_API_EXTERN own wasm_table_t* wasm_table_new( + wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init); + +WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*); + +WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index); +WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*); + +WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*); +WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init); + + +// Memory Instances + +WASM_DECLARE_REF(memory) + +typedef uint32_t wasm_memory_pages_t; + +static const size_t MEMORY_PAGE_SIZE = 0x10000; + +WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*); + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*); + +WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*); +WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*); + +WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*); +WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta); + + +// Externals + +WASM_DECLARE_REF(extern) +WASM_DECLARE_VEC(extern, *) + +WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*); +WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*); + +WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*); +WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*); +WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*); +WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*); + +WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*); +WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*); +WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*); +WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*); + +WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*); + +WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*); + + +// Module Instances + +WASM_DECLARE_REF(instance) + +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 +); + +// please refer to wasm_runtime_instantiate(...) in core/iwasm/include/wasm_export.h +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new_with_args( + wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t *imports, + own wasm_trap_t** trap, const uint32_t stack_size, const uint32_t heap_size +); + +// please refer to wasm_runtime_instantiate_ex(...) in core/iwasm/include/wasm_export.h +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new_with_args_ex( + wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t *imports, + own wasm_trap_t** trap, const InstantiationArgs *inst_args +); + +WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out); + +// Return total wasm functions' execution time in ms +WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*); +// Return execution time in ms of a given wasm function with +// func_name. If the function is not found, return 0. +WASM_API_EXTERN double wasm_instance_get_wasm_func_exec_time(const wasm_instance_t*, const char *); + +/////////////////////////////////////////////////////////////////////////////// +// Convenience + +// Vectors + +#define WASM_EMPTY_VEC {0, NULL, 0, 0, NULL} +#define WASM_ARRAY_VEC(array) {sizeof(array)/sizeof(*(array)), array, sizeof(array)/sizeof(*(array)), sizeof(*(array)), NULL} + + +// Value Type construction short-hands + +static inline own wasm_valtype_t* wasm_valtype_new_i32(void) { + return wasm_valtype_new(WASM_I32); +} +static inline own wasm_valtype_t* wasm_valtype_new_i64(void) { + return wasm_valtype_new(WASM_I64); +} +static inline own wasm_valtype_t* wasm_valtype_new_f32(void) { + return wasm_valtype_new(WASM_F32); +} +static inline own wasm_valtype_t* wasm_valtype_new_f64(void) { + return wasm_valtype_new(WASM_F64); +} +static inline own wasm_valtype_t* wasm_valtype_new_v128(void) { + return wasm_valtype_new(WASM_V128); +} + +static inline own wasm_valtype_t* wasm_valtype_new_anyref(void) { + return wasm_valtype_new(WASM_EXTERNREF); +} +static inline own wasm_valtype_t* wasm_valtype_new_funcref(void) { + return wasm_valtype_new(WASM_FUNCREF); +} + + +// Function Types construction short-hands + +static inline own wasm_functype_t* wasm_functype_new_0_0(void) { + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_0( + own wasm_valtype_t* p +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_1( + own wasm_valtype_t* r +) { + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_1( + own wasm_valtype_t* p, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_2( + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_2( + own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + + +// Value construction short-hands + +static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) { +#if UINTPTR_MAX == UINT32_MAX + out->kind = WASM_I32; + out->of.i32 = (intptr_t)p; +#elif UINTPTR_MAX == UINT64_MAX + out->kind = WASM_I64; + out->of.i64 = (intptr_t)p; +#endif +} + +static inline void* wasm_val_ptr(const wasm_val_t* val) { +#if UINTPTR_MAX == UINT32_MAX + return (void*)(intptr_t)val->of.i32; +#elif UINTPTR_MAX == UINT64_MAX + return (void*)(intptr_t)val->of.i64; +#endif +} + +#define WASM_I32_VAL(i) {.kind = WASM_I32, ._paddings = {0}, .of = {.i32 = i}} +#define WASM_I64_VAL(i) {.kind = WASM_I64, ._paddings = {0}, .of = {.i64 = i}} +#define WASM_F32_VAL(z) {.kind = WASM_F32, ._paddings = {0}, .of = {.f32 = z}} +#define WASM_F64_VAL(z) {.kind = WASM_F64, ._paddings = {0}, .of = {.f64 = z}} +#define WASM_REF_VAL(r) {.kind = WASM_EXTERNREF, ._paddings = {0}, .of = {.ref = r}} +#define WASM_INIT_VAL {.kind = WASM_EXTERNREF, ._paddings = {0}, .of = {.ref = NULL}} + +#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 + +/* clang-format on */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef _WASM_C_API_H_ diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 92bdece484..830c5c030c 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -3,47 +3,160 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +/** + * @file wasm_export.h + * + * @brief This file defines the exported common runtime APIs + */ + #ifndef _WASM_EXPORT_H #define _WASM_EXPORT_H -#include +#include #include +#include "lib_export.h" +#ifndef WASM_RUNTIME_API_EXTERN +#if defined(_MSC_BUILD) +#if defined(COMPILING_WASM_RUNTIME_API) +#define WASM_RUNTIME_API_EXTERN __declspec(dllexport) +#else +#define WASM_RUNTIME_API_EXTERN __declspec(dllimport) +#endif +#elif defined(__GNUC__) || defined(__clang__) +#define WASM_RUNTIME_API_EXTERN __attribute__((visibility("default"))) +#else +#define WASM_RUNTIME_API_EXTERN +#endif +#endif #ifdef __cplusplus extern "C" { #endif +#define get_module_inst(exec_env) wasm_runtime_get_module_inst(exec_env) + +#define validate_app_addr(offset, size) \ + wasm_runtime_validate_app_addr(module_inst, offset, size) + +#define validate_app_str_addr(offset) \ + wasm_runtime_validate_app_str_addr(module_inst, offset) + +#define addr_app_to_native(offset) \ + wasm_runtime_addr_app_to_native(module_inst, offset) + +#define addr_native_to_app(ptr) \ + wasm_runtime_addr_native_to_app(module_inst, ptr) + +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) + +#define module_free(offset) wasm_runtime_module_free(module_inst, offset) + +#define native_raw_return_type(type, args) type *raw_ret = (type *)(args) + +#define native_raw_get_arg(type, name, args) type name = *((type *)(args++)) + +#define native_raw_set_return(val) *raw_ret = (val) + +#ifndef WASM_MODULE_T_DEFINED +#define WASM_MODULE_T_DEFINED /* Uninstantiated WASM module loaded from WASM binary file or AoT binary file*/ struct WASMModuleCommon; typedef struct WASMModuleCommon *wasm_module_t; +#endif + +typedef enum { + WASM_IMPORT_EXPORT_KIND_FUNC, + WASM_IMPORT_EXPORT_KIND_TABLE, + WASM_IMPORT_EXPORT_KIND_MEMORY, + WASM_IMPORT_EXPORT_KIND_GLOBAL +} wasm_import_export_kind_t; + +struct WASMFuncType; +typedef struct WASMFuncType *wasm_func_type_t; + +struct WASMTableType; +typedef struct WASMTableType *wasm_table_type_t; + +struct WASMGlobalType; +typedef struct WASMGlobalType *wasm_global_type_t; + +#ifndef WASM_MEMORY_T_DEFINED +#define WASM_MEMORY_T_DEFINED +struct WASMMemory; +typedef struct WASMMemory WASMMemoryType; +#endif +typedef WASMMemoryType *wasm_memory_type_t; + +typedef struct wasm_import_t { + const char *module_name; + const char *name; + wasm_import_export_kind_t kind; + bool linked; + union { + wasm_func_type_t func_type; + wasm_table_type_t table_type; + wasm_global_type_t global_type; + wasm_memory_type_t memory_type; + } u; +} wasm_import_t; + +typedef struct wasm_export_t { + const char *name; + wasm_import_export_kind_t kind; + union { + wasm_func_type_t func_type; + wasm_table_type_t table_type; + wasm_global_type_t global_type; + wasm_memory_type_t memory_type; + } u; +} wasm_export_t; /* Instantiated WASM module */ struct WASMModuleInstanceCommon; typedef struct WASMModuleInstanceCommon *wasm_module_inst_t; /* Function instance */ -struct WASMFunctionInstanceCommon; -typedef struct WASMFunctionInstanceCommon *wasm_function_inst_t; +typedef void WASMFunctionInstanceCommon; +typedef WASMFunctionInstanceCommon *wasm_function_inst_t; + +/* Memory instance */ +struct WASMMemoryInstance; +typedef struct WASMMemoryInstance *wasm_memory_inst_t; + +typedef struct wasm_frame_t { + /* wasm_instance_t */ + void *instance; + uint32_t module_offset; + uint32_t func_index; + uint32_t func_offset; + const char *func_name_wp; + + uint32_t *sp; + uint8_t *frame_ref; + uint32_t *lp; +} WASMCApiFrame; /* WASM section */ -typedef struct wasm_section { - struct wasm_section *next; +typedef struct wasm_section_t { + struct wasm_section_t *next; /* section type */ int section_type; /* section body, not include type and size */ uint8_t *section_body; /* section body size */ uint32_t section_body_size; -} wasm_section_t, *wasm_section_list_t; - -typedef wasm_section_t aot_section_t, *aot_section_list_t; +} wasm_section_t, aot_section_t, *wasm_section_list_t, *aot_section_list_t; /* Execution environment, e.g. stack info */ struct WASMExecEnv; typedef struct WASMExecEnv *wasm_exec_env_t; +struct WASMSharedHeap; +typedef struct WASMSharedHeap *wasm_shared_heap_t; + /* Package Type */ typedef enum { Wasm_Module_Bytecode = 0, @@ -51,386 +164,2353 @@ typedef enum { Package_Type_Unknown = 0xFFFF } package_type_t; +#ifndef MEM_ALLOC_OPTION_DEFINED +#define MEM_ALLOC_OPTION_DEFINED +/* Memory allocator type */ +typedef enum { + /* pool mode, allocate memory from user defined heap buffer */ + Alloc_With_Pool = 0, + /* user allocator mode, allocate memory from user defined + malloc function */ + Alloc_With_Allocator, + /* system allocator mode, allocate memory from system allocator, + or, platform's os_malloc function */ + Alloc_With_System_Allocator, +} mem_alloc_type_t; + +typedef enum { Alloc_For_Runtime, Alloc_For_LinearMemory } mem_alloc_usage_t; + +/* Memory allocator option */ +typedef union MemAllocOption { + struct { + void *heap_buf; + uint32_t heap_size; + } pool; + struct { + /* the function signature is varied when + WASM_MEM_ALLOC_WITH_USER_DATA and + WASM_MEM_ALLOC_WITH_USAGE are defined */ + void *malloc_func; + void *realloc_func; + void *free_func; + /* allocator user data, only used when + WASM_MEM_ALLOC_WITH_USER_DATA is defined */ + void *user_data; + } allocator; +} MemAllocOption; +#endif + +/* Memory pool info */ +typedef struct mem_alloc_info_t { + uint32_t total_size; + uint32_t total_free_size; + 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; + MemAllocOption mem_alloc_option; + + const char *native_module_name; + NativeSymbol *native_symbols; + uint32_t n_native_symbols; + + /* maximum thread number, only used when + WASM_ENABLE_THREAD_MGR is defined */ + uint32_t max_thread_num; + + /* Debug settings, only used when + WASM_ENABLE_DEBUG_INTERP != 0 */ + char ip_addr[128]; + int unused; /* was platform_port */ + int instance_port; + + /* Fast JIT code cache size */ + uint32_t fast_jit_code_cache_size; + + /* Default GC heap size */ + uint32_t gc_heap_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; + /* Segue optimization flags for LLVM JIT */ + uint32_t segue_flags; + /** + * If enabled + * - llvm-jit will output a jitdump file for `perf inject` + * - aot will output a perf-${pid}.map for `perf record` + * - fast-jit. TBD + * - multi-tier-jit. TBD + * - interpreter. TBD + */ + bool enable_linux_perf; +} RuntimeInitArgs; + +#ifndef LOAD_ARGS_OPTION_DEFINED +#define LOAD_ARGS_OPTION_DEFINED +typedef struct LoadArgs { + char *name; + /* This option is only used by the Wasm C API (see wasm_c_api.h) */ + bool clone_wasm_binary; + /* False by default, used by AOT/wasm loader only. + If true, the AOT/wasm loader creates a copy of some module fields (e.g. + const strings), making it possible to free the wasm binary buffer after + loading. */ + bool wasm_binary_freeable; + + /* false by default, if true, don't resolve the symbols yet. The + wasm_runtime_load_ex has to be followed by a wasm_runtime_resolve_symbols + call */ + bool no_resolve; + /* TODO: more fields? */ +} LoadArgs; +#endif /* LOAD_ARGS_OPTION_DEFINED */ + +#ifndef INSTANTIATION_ARGS_OPTION_DEFINED +#define INSTANTIATION_ARGS_OPTION_DEFINED +/* WASM module instantiation arguments */ +typedef struct InstantiationArgs { + uint32_t default_stack_size; + uint32_t host_managed_heap_size; + uint32_t max_memory_pages; +} InstantiationArgs; +#endif /* INSTANTIATION_ARGS_OPTION_DEFINED */ + +struct InstantiationArgs2; + +#ifndef WASM_VALKIND_T_DEFINED +#define WASM_VALKIND_T_DEFINED +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_V128, + WASM_EXTERNREF = 128, + WASM_FUNCREF, +}; +#endif + +#ifndef WASM_VAL_T_DEFINED +#define WASM_VAL_T_DEFINED +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + uint8_t _paddings[7]; + union { + /* also represent a function index */ + int32_t i32; + int64_t i64; + float f32; + double f64; + /* represent a foreign object, aka externref in .wat */ + uintptr_t foreign; + struct wasm_ref_t *ref; + } of; +} wasm_val_t; +#endif + +/* Global instance*/ +typedef struct wasm_global_inst_t { + wasm_valkind_t kind; + bool is_mutable; + void *global_data; +} wasm_global_inst_t; + +/* Table instance*/ +typedef struct wasm_table_inst_t { + wasm_valkind_t elem_kind; + uint32_t cur_size; + uint32_t max_size; + /* represents the elements of the table, for internal use only */ + void *elems; +} wasm_table_inst_t; + +typedef enum { + WASM_LOG_LEVEL_FATAL = 0, + WASM_LOG_LEVEL_ERROR = 1, + WASM_LOG_LEVEL_WARNING = 2, + WASM_LOG_LEVEL_DEBUG = 3, + WASM_LOG_LEVEL_VERBOSE = 4 +} log_level_t; + +typedef struct SharedHeapInitArgs { + uint32_t size; + void *pre_allocated_addr; +} SharedHeapInitArgs; + /** - * Initialize the WASM runtime environment. + * Initialize the WASM runtime environment, and also initialize + * the memory allocator with system allocator, which calls os_malloc + * to allocate memory * * @return true if success, false otherwise */ -bool -wasm_runtime_init(); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_init(void); /** - * Destroy the WASM runtime environment. + * 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 + * + * @return return true if success, false otherwise */ -void -wasm_runtime_destroy(); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_full_init(RuntimeInitArgs *init_args); /** - * Get the package type of a buffer. - * - * @param buf the package buffer - * @param size the package buffer size + * Set the log level. To be called after the runtime is initialized. * - * @return the package type, return Package_Type_Unknown if the type is unknown + * @param level the log level to set */ -package_type_t -get_package_type(const uint8_t *buf, uint32_t size); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_log_level(log_level_t level); /** - * Load a WASM module from a specified byte buffer. + * Query whether a certain running mode is supported for the runtime * - * @param buf the byte buffer which contains the WASM binary data - * @param size the size of the buffer - * @param error_buf output of the exception info - * @param error_buf_size the size of the exception string + * @param running_mode the running mode to query * - * @return return WASM module loaded, NULL if failed + * @return true if this running mode is supported, false otherwise */ -wasm_module_t -wasm_runtime_load(const uint8_t *buf, uint32_t size, - char *error_buf, uint32_t error_buf_size); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_running_mode_supported(RunningMode running_mode); /** - * Load a WASM module from a specified WASM or AOT section list. + * 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 section_list the section list which contains each section data - * @param is_aot whether the section list is AOT section list - * @param error_buf output of the exception info - * @param error_buf_size the size of the exception string + * @param running_mode the running mode to set * - * @return return WASM module loaded, NULL if failed + * @return true if success, false otherwise */ -wasm_module_t -wasm_runtime_load_from_sections(wasm_section_list_t section_list, bool is_aot, - char *error_buf, uint32_t error_buf_size); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_set_default_running_mode(RunningMode running_mode); /** - * Unload a WASM module. - * - * @param module the module to be unloaded + * Destroy the WASM runtime environment. */ -void -wasm_runtime_unload(wasm_module_t module); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_destroy(void); -void -wasm_runtime_set_wasi_args(wasm_module_t module, - const char *dir_list[], uint32_t dir_count, - const char *map_dir_list[], uint32_t map_dir_count, - const char *env[], uint32_t env_count, - char *argv[], int argc); +/** + * Allocate memory from runtime memory environment. + * + * @param size bytes need to allocate + * + * @return the pointer to memory allocated + */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_malloc(unsigned int size); /** - * Instantiate a WASM module. + * Allocate memory with specified alignment from runtime memory environment. + * This function mimics aligned_alloc() behavior in WebAssembly context. * - * @param module the WASM module to instantiate - * @param stack_size the default stack size of the module instance, a stack - * will be created when function wasm_runtime_call_wasm() is called - * to run WASM function and the exec_env argument passed to - * wasm_runtime_call_wasm() is NULL. That means this parameter is - * ignored if exec_env is not NULL. - * @param heap_size the default heap size of the module instance, a heap will - * be created besides the app memory space. Both wasm app and native - * function can allocate memory from the heap. If heap_size is 0, the - * default heap size will be used. - * @param error_buf buffer to output the error info if failed - * @param error_buf_size the size of the error buffer + * Note: Only supported in POOL memory mode. Other modes will return NULL. + * Note: Allocated memory cannot be reallocated with wasm_runtime_realloc(). * - * @return return the instantiated WASM module instance, NULL if failed + * @param size bytes need to allocate (must be multiple of alignment) + * @param alignment alignment requirement (must be power of 2, >= 8, <= page + * size) + * + * @return the pointer to aligned memory allocated, or NULL on failure */ -wasm_module_inst_t -wasm_runtime_instantiate(const wasm_module_t module, - uint32_t stack_size, uint32_t heap_size, - char *error_buf, uint32_t error_buf_size); +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_aligned_alloc(unsigned int size, unsigned int alignment); /** - * Deinstantiate a WASM module instance, destroy the resources. + * Reallocate memory from runtime memory environment * - * @param module_inst the WASM module instance to destroy + * @param ptr the original memory + * @param size bytes need to reallocate + * + * @return the pointer to memory reallocated */ -void -wasm_runtime_deinstantiate(wasm_module_inst_t module_inst); +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_realloc(void *ptr, unsigned int size); -bool -wasm_runtime_is_wasi_mode(wasm_module_inst_t module_inst); +/* + * Free memory to runtime memory environment. + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_free(void *ptr); -wasm_function_inst_t -wasm_runtime_lookup_wasi_start_function(wasm_module_inst_t module_inst); +/* + * Get memory info, only pool mode is supported now. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_get_mem_alloc_info(mem_alloc_info_t *mem_alloc_info); /** - * Lookup an exported function in the WASM module instance. + * Get the package type of a buffer. * - * @param module_inst the module instance - * @param name the name of the function - * @param signature the signature of the function, use "i32"/"i64"/"f32"/"f64" - * to represent the type of i32/i64/f32/f64, e.g. "(i32i64)" "(i32)f32" + * @param buf the package buffer + * @param size the package buffer size * - * @return the function instance found + * @return the package type, return Package_Type_Unknown if the type is unknown */ -wasm_function_inst_t -wasm_runtime_lookup_function(const wasm_module_inst_t module_inst, - const char *name, const char *signature); +WASM_RUNTIME_API_EXTERN package_type_t +get_package_type(const uint8_t *buf, uint32_t size); /** - * Create execution environment for a WASM module instance. + * Get the package type of a buffer (same as get_package_type). * - * @param module_inst the module instance - * @param stack_size the stack size to execute a WASM function + * @param buf the package buffer + * @param size the package buffer size * - * @return the execution environment + * @return the package type, return Package_Type_Unknown if the type is unknown */ -wasm_exec_env_t -wasm_runtime_create_exec_env(wasm_module_inst_t module_inst, - uint32_t stack_size); +WASM_RUNTIME_API_EXTERN package_type_t +wasm_runtime_get_file_package_type(const uint8_t *buf, uint32_t size); /** - * Destroy the execution environment. + * Get the package type of a module. + * + * @param module the module * - * @param env the execution environment to destroy + * @return the package type, return Package_Type_Unknown if the type is + * unknown */ -void -wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env); +WASM_RUNTIME_API_EXTERN package_type_t +wasm_runtime_get_module_package_type(const wasm_module_t module); /** - * Get WASM module instance from execution environment + * Get the package version of a buffer. * - * @param exec_env the execution environment to retrieve + * @param buf the package buffer + * @param size the package buffer size * - * @return the WASM module instance + * @return the package version, return zero if the version is unknown */ -wasm_module_inst_t -wasm_runtime_get_module_inst(wasm_exec_env_t exec_env); +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_file_package_version(const uint8_t *buf, uint32_t size); /** - * Call the given WASM function of a WASM module instance with - * arguments (bytecode and AoT). + * Get the package version of a module * - * @param exec_env the execution environment to call the function - * which must be created from wasm_create_exec_env() - * @param function the function to be called - * @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. + * @param module the module * - * @return true if success, false otherwise and exception will be thrown, - * the caller can call wasm_runtime_get_exception to get exception info. + * @return the package version, or zero if version is unknown */ -bool -wasm_runtime_call_wasm(wasm_exec_env_t exec_env, - wasm_function_inst_t function, - uint32_t argc, uint32_t argv[]); +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_module_package_version(const wasm_module_t module); /** - * Find the unique main function from a WASM module instance - * and execute that function. + * Get the currently supported version of the package type * - * @param module_inst the WASM module instance - * @param argc the number of arguments - * @param argv the arguments array + * @param package_type the package type * - * @return true if the main function is called, false otherwise and exception will be thrown, - * the caller can call wasm_runtime_get_exception to get exception info. + * @return the currently supported version, or zero if package type is unknown */ -bool -wasm_application_execute_main(wasm_module_inst_t module_inst, - int32_t argc, char *argv[]); +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_current_package_version(package_type_t package_type); /** - * Find the specified function in argv[0] from a WASM module instance - * and execute that function. + * Check whether a file is an AOT XIP (Execution In Place) file * - * @param module_inst the WASM module instance - * @param name the name of the function to execute - * @param argc the number of arguments - * @param argv the arguments array + * @param buf the package buffer + * @param size the package buffer size * - * @return true if the specified function is called, false otherwise and exception will be thrown, - * the caller can call wasm_runtime_get_exception to get exception info. + * @return true if success, false otherwise */ -bool -wasm_application_execute_func(wasm_module_inst_t module_inst, - const char *name, int32_t argc, char *argv[]); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_xip_file(const uint8_t *buf, uint32_t size); + /** - * Get exception info of the WASM module instance. - * - * @param module_inst the WASM module instance - * - * @return the exception string + * Callback to load a module file into a buffer in multi-module feature */ -const char * -wasm_runtime_get_exception(wasm_module_inst_t module_inst); +typedef bool (*module_reader)(package_type_t module_type, + const char *module_name, uint8_t **p_buffer, + uint32_t *p_size); /** - * Clear exception info of the WASM module instance. - * - * @param module_inst the WASM module instance + * Callback to release the buffer loaded by module_reader callback */ -void -wasm_runtime_clear_exception(wasm_module_inst_t module_inst); +typedef void (*module_destroyer)(uint8_t *buffer, uint32_t size); /** - * Set custom data to WASM module instance. + * Setup callbacks for reading and releasing a buffer about a module file * - * @param module_inst the WASM module instance - * @param custom_data the custom data to be set + * @param reader a callback to read a module file into a buffer + * @param destroyer a callback to release above buffer */ -void -wasm_runtime_set_custom_data(wasm_module_inst_t module_inst, - void *custom_data); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); /** - * Get the custom data within a WASM module instance. + * Give the "module" a name "module_name". + * Can not assign a new name to a module if it already has a name * - * @param module_inst the WASM module instance + * @param module_name indicate a name + * @param module the target module + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string * - * @return the custom data (NULL if not set yet) + * @return true means success, false means failed */ -void * -wasm_runtime_get_custom_data(wasm_module_inst_t module_inst); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_register_module(const char *module_name, wasm_module_t module, + char *error_buf, uint32_t error_buf_size); /** - * Allocate memory from the heap of WASM module instance + * Check if there is already a loaded module named module_name in the + * runtime. Repeatedly loading a module with the same name is not allowed. * - * @param module_inst the WASM module instance which contains heap - * @param size the size bytes to allocate + * @param module_name indicate a name * - * @return the allocated memory address, which is a relative offset to the - * base address of the module instance's memory space, the value range - * is (-heap_size, 0). Note that it is not an absolute address. - * Return non-zero if success, zero if failed. + * @return return WASM module loaded, NULL if failed */ -int32_t -wasm_runtime_module_malloc(wasm_module_inst_t module_inst, uint32_t size); +WASM_RUNTIME_API_EXTERN wasm_module_t +wasm_runtime_find_module_registered(const char *module_name); /** - * Free memory to the heap of WASM module instance + * Load a WASM module from a specified byte buffer. The byte buffer can be + * WASM binary data when interpreter or JIT is enabled, or AOT binary data + * when AOT is enabled. If it is AOT binary data, it must be 4-byte aligned. * - * @param module_inst the WASM module instance which contains heap - * @param ptr the pointer to free + * Note: In case of AOT XIP modules, the runtime doesn't make modifications + * to the buffer. (Except the "Known issues" mentioned in doc/xip.md.) + * Otherwise, the runtime can make modifications to the buffer for its + * internal purposes. Thus, in general, it isn't safe to create multiple + * modules from a single buffer. + * + * @param buf the byte buffer which contains the WASM/AOT binary data, + * note that the byte buffer must be writable since runtime may + * change its content for footprint and performance purpose, and + * it must be referenceable until wasm_runtime_unload is called + * @param size the size of the buffer + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return return WASM module loaded, NULL if failed */ -void -wasm_runtime_module_free(wasm_module_inst_t module_inst, int32_t ptr); +WASM_RUNTIME_API_EXTERN wasm_module_t +wasm_runtime_load(uint8_t *buf, uint32_t size, char *error_buf, + uint32_t error_buf_size); /** - * Allocate memory from the heap of WASM module instance and initialize - * the memory with src - * - * @param module_inst the WASM module instance which contains heap - * @param src the source data to copy - * @param size the size of the source data - * - * @return the allocated memory address, which is a relative offset to the - * base address of the module instance's memory space, the value range - * is (-heap_size, 0). Note that it is not an absolute address. - * Return non-zero if success, zero if failed. + * Load a WASM module with specified load argument. */ -int32_t -wasm_runtime_module_dup_data(wasm_module_inst_t module_inst, - const char *src, uint32_t size); +WASM_RUNTIME_API_EXTERN wasm_module_t +wasm_runtime_load_ex(uint8_t *buf, uint32_t size, const LoadArgs *args, + char *error_buf, uint32_t error_buf_size); /** - * Validate the app address, check whether it belongs to WASM module - * instance's address space, or in its heap space or memory space. + * Resolve symbols for a previously loaded WASM module. Only useful when the + * module was loaded with LoadArgs::no_resolve set to true + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_resolve_symbols(wasm_module_t module); +/** + * Load a WASM module from a specified WASM or AOT section list. * - * @param module_inst the WASM module instance - * @param app_offset the app address to validate, which is a relative address - * @param size the size bytes of the app address + * @param section_list the section list which contains each section data + * @param is_aot whether the section list is AOT section list + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string * - * @return true if success, false otherwise. If failed, an exception will - * be thrown. + * @return return WASM module loaded, NULL if failed */ -bool -wasm_runtime_validate_app_addr(wasm_module_inst_t module_inst, - int32_t app_offset, uint32_t size); +WASM_RUNTIME_API_EXTERN wasm_module_t +wasm_runtime_load_from_sections(wasm_section_list_t section_list, bool is_aot, + char *error_buf, uint32_t error_buf_size); /** - * Similar to wasm_runtime_validate_app_addr(), except that the size parameter - * is not provided. This function validates the app string address, check whether it - * belongs to WASM module instance's address space, or in its heap space or - * memory space. Moreover, it checks whether it is the offset of a string that - * is end with '\0'. - * @param module_inst the WASM module instance - * @param app_str_offset the app address of the string to validate, which is a - * relative address + * Unload a WASM module. * - * @return true if success, false otherwise. If failed, an exception will - * be thrown. + * @param module the module to be unloaded */ -bool -wasm_runtime_validate_app_str_addr(wasm_module_inst_t module_inst, - int32_t app_str_offset); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_unload(wasm_module_t module); /** - * Validate the native address, check whether it belongs to WASM module - * instance's address space, or in its heap space or memory space. + * Get the module hash of a WASM module, currently only available on + * linux-sgx platform when the remote attestation feature is enabled * - * @param module_inst the WASM module instance - * @param native_ptr the native address to validate, which is an absolute - * address - * @param size the size bytes of the app address + * @param module the WASM module to retrieve * - * @return true if success, false otherwise. If failed, an exception will - * be thrown. + * @return the module hash of the WASM module */ -bool -wasm_runtime_validate_native_addr(wasm_module_inst_t module_inst, - void *native_ptr, uint32_t size); +char * +wasm_runtime_get_module_hash(wasm_module_t module); /** - * Convert app address(relative address) to native address(absolute address) + * Set WASI parameters. * - * @param module_inst the WASM module instance - * @param app_offset the app adress + * While this API operates on a module, these parameters will be used + * only when the module is instantiated. That is, you can consider these + * as extra parameters for wasm_runtime_instantiate(). * - * @return the native address converted + * @param module The module to set WASI parameters. + * @param dir_list The list of directories to preopen. (real path) + * @param dir_count The number of elements in dir_list. + * @param map_dir_list The list of directories to preopen. (mapped path) + * Format for each map entry: :: + * @param map_dir_count The number of elements in map_dir_list. + * If map_dir_count is smaller than dir_count, + * mapped path is assumed to be same as the + * corresponding real path for the rest of entries. + * @param env The list of environment variables. + * @param env_count The number of elements in env. + * @param argv The list of command line arguments. + * @param argc The number of elements in argv. + * @param stdin_handle The raw host handle to back WASI STDIN_FILENO. + * If an invalid handle is specified (e.g. -1 on POSIX, + * INVALID_HANDLE_VALUE on Windows), the platform default + * for STDIN is used. + * @param stdoutfd The raw host handle to back WASI STDOUT_FILENO. + * If an invalid handle is specified (e.g. -1 on POSIX, + * INVALID_HANDLE_VALUE on Windows), the platform default + * for STDOUT is used. + * @param stderrfd The raw host handle to back WASI STDERR_FILENO. + * If an invalid handle is specified (e.g. -1 on POSIX, + * INVALID_HANDLE_VALUE on Windows), the platform default + * for STDERR is used. */ -void* -wasm_runtime_addr_app_to_native(wasm_module_inst_t module_inst, - int32_t app_offset); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_args_ex(wasm_module_t module, const char *dir_list[], + uint32_t dir_count, const char *map_dir_list[], + uint32_t map_dir_count, const char *env[], + uint32_t env_count, char *argv[], int argc, + int64_t stdinfd, int64_t stdoutfd, + int64_t stderrfd); /** - * Convert native address(absolute address) to app address(relative address) - * - * @param module_inst the WASM module instance - * @param native_ptr the native address + * Set WASI parameters. * - * @return the app address converted + * Same as wasm_runtime_set_wasi_args_ex but with default stdio handles */ -int32_t -wasm_runtime_addr_native_to_app(wasm_module_inst_t module_inst, - void *native_ptr); +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_args(wasm_module_t module, const char *dir_list[], + uint32_t dir_count, const char *map_dir_list[], + uint32_t map_dir_count, const char *env[], + uint32_t env_count, char *argv[], int argc); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_addr_pool(wasm_module_t module, const char *addr_pool[], + uint32_t addr_pool_size); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_wasi_ns_lookup_pool(wasm_module_t module, + const char *ns_lookup_pool[], + uint32_t ns_lookup_pool_size); /** - * Get the app address range (relative address) that a app address belongs to + * Instantiate a WASM module. + * + * @param module the WASM module to instantiate + * @param default_stack_size the default stack size of the module instance when + * the exec env's operation stack isn't created by user, e.g. API + * wasm_application_execute_main() and wasm_application_execute_func() + * create the operation stack internally with the stack size specified + * here. And API wasm_runtime_create_exec_env() creates the operation + * stack with stack size specified by its parameter, the stack size + * specified here is ignored. + * @param host_managed_heap_size the default heap size of the module instance, + * a heap will be created besides the app memory space. Both wasm app + * and native function can allocate memory from the heap. + * @param error_buf buffer to output the error info if failed + * @param error_buf_size the size of the error buffer + * + * @return return the instantiated WASM module instance, NULL if failed + */ +WASM_RUNTIME_API_EXTERN wasm_module_inst_t +wasm_runtime_instantiate(const wasm_module_t module, + uint32_t default_stack_size, + uint32_t host_managed_heap_size, char *error_buf, + uint32_t error_buf_size); + +/** + * Instantiate a WASM module, with specified instantiation arguments + * + * Same as wasm_runtime_instantiate, but it also allows overwriting maximum + * memory + */ +WASM_RUNTIME_API_EXTERN wasm_module_inst_t +wasm_runtime_instantiate_ex(const wasm_module_t module, + const InstantiationArgs *args, char *error_buf, + uint32_t error_buf_size); + +/** + * Create an InstantiationArgs2 object with default parameters. + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_instantiation_args_create(struct InstantiationArgs2 **p); + +/** + * Dispose an InstantiationArgs2 object. + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_destroy(struct InstantiationArgs2 *p); + +/** + * Setter functions for the InstantiationArgs2 object. + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_default_stack_size( + struct InstantiationArgs2 *p, uint32_t v); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_host_managed_heap_size( + struct InstantiationArgs2 *p, uint32_t v); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_max_memory_pages( + struct InstantiationArgs2 *p, uint32_t v); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_arg(struct InstantiationArgs2 *p, + char *argv[], int argc); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_env(struct InstantiationArgs2 *p, + const char *env[], + uint32_t env_count); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_dir(struct InstantiationArgs2 *p, + const char *dir_list[], + uint32_t dir_count, + const char *map_dir_list[], + uint32_t map_dir_count); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_stdio(struct InstantiationArgs2 *p, + int64_t stdinfd, + int64_t stdoutfd, + int64_t stderrfd); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_addr_pool(struct InstantiationArgs2 *p, + const char *addr_pool[], + uint32_t addr_pool_size); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_instantiation_args_set_wasi_ns_lookup_pool( + struct InstantiationArgs2 *p, const char *ns_lookup_pool[], + uint32_t ns_lookup_pool_size); + +/** + * Instantiate a WASM module, with specified instantiation arguments + * + * Same as wasm_runtime_instantiate_ex, but this version takes + * InstantiationArgs2, which can be extended without breaking the ABI. + */ +WASM_RUNTIME_API_EXTERN wasm_module_inst_t +wasm_runtime_instantiate_ex2(const wasm_module_t module, + const struct InstantiationArgs2 *args, + 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. + * + * @param module_inst the WASM module instance to destroy + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_deinstantiate(wasm_module_inst_t module_inst); + +/** + * Get WASM module from WASM module instance + * + * @param module_inst the WASM module instance to retrieve + * + * @return the WASM module + */ +WASM_RUNTIME_API_EXTERN wasm_module_t +wasm_runtime_get_module(wasm_module_inst_t module_inst); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_wasi_mode(wasm_module_inst_t module_inst); + +WASM_RUNTIME_API_EXTERN wasm_function_inst_t +wasm_runtime_lookup_wasi_start_function(wasm_module_inst_t module_inst); + +/** + * Get WASI exit code. + * + * After a WASI command completed its execution, an embedder can + * call this function to get its exit code. (that is, the value given + * to proc_exit.) + * + * @param module_inst the module instance + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_wasi_exit_code(wasm_module_inst_t module_inst); + +/** + * Lookup an exported function in the WASM module instance. + * + * @param module_inst the module instance + * @param name the name of the function + * + * @return the function instance found, NULL if not found + */ +WASM_RUNTIME_API_EXTERN wasm_function_inst_t +wasm_runtime_lookup_function(const wasm_module_inst_t module_inst, + const char *name); + +/** + * Get parameter count of the function instance + * + * @param func_inst the function instance + * @param module_inst the module instance the function instance belongs to + * + * @return the parameter count of the function instance + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_func_get_param_count(const wasm_function_inst_t func_inst, + const wasm_module_inst_t module_inst); + +/** + * Get result count of the function instance + * + * @param func_inst the function instance + * @param module_inst the module instance the function instance belongs to + * + * @return the result count of the function instance + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_func_get_result_count(const wasm_function_inst_t func_inst, + const wasm_module_inst_t module_inst); + +/** + * Get parameter types of the function instance + * + * @param func_inst the function instance + * @param module_inst the module instance the function instance belongs to + * @param param_types the parameter types returned + */ +WASM_RUNTIME_API_EXTERN void +wasm_func_get_param_types(const wasm_function_inst_t func_inst, + const wasm_module_inst_t module_inst, + wasm_valkind_t *param_types); + +/** + * Get result types of the function instance + * + * @param func_inst the function instance + * @param module_inst the module instance the function instance belongs to + * @param result_types the result types returned + */ +WASM_RUNTIME_API_EXTERN void +wasm_func_get_result_types(const wasm_function_inst_t func_inst, + const wasm_module_inst_t module_inst, + wasm_valkind_t *result_types); + +/** + * Create execution environment for a WASM module instance. + * + * @param module_inst the module instance + * @param stack_size the stack size to execute a WASM function + * + * @return the execution environment, NULL if failed, e.g. invalid + * stack size is passed + */ +WASM_RUNTIME_API_EXTERN wasm_exec_env_t +wasm_runtime_create_exec_env(wasm_module_inst_t module_inst, + uint32_t stack_size); + +/** + * Destroy the execution environment. + * + * @param exec_env the execution environment to destroy + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env); + +/** + * @brief Copy callstack frames. + * + * Caution: This is not a thread-safe function. Ensure the exec_env + * is suspended before calling it from another thread. + * + * Usage: In the callback to read frames fields use APIs + * for wasm_frame_t from wasm_c_api.h + * + * Note: The function is async-signal-safe if called with verified arguments. + * Meaning it's safe to call it from a signal handler even on a signal + * interruption from another thread if next variables hold valid pointers + * - exec_env + * - exec_env->module_inst + * - exec_env->module_inst->module + * + * @param exec_env the execution environment that containes frames + * @param buffer the buffer of size equal length * sizeof(wasm_frame_t) to copy + * frames to + * @param length the number of frames to copy + * @param skip_n the number of frames to skip from the top of the stack + * + * @return number of copied frames + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_copy_callstack(const wasm_exec_env_t exec_env, WASMCApiFrame *buffer, + const uint32_t length, const uint32_t skip_n, + char *error_buf, uint32_t error_buf_size); + +/** + * Get the singleton execution environment for the instance. + * + * Note: The singleton execution environment is the execution + * environment used internally by the runtime for the API functions + * like wasm_application_execute_main, which don't take explicit + * execution environment. It's associated to the corresponding + * module instance and managed by the runtime. The API user should + * not destroy it with wasm_runtime_destroy_exec_env. + * + * @param module_inst the module instance + * + * @return exec_env the execution environment to destroy + */ +WASM_RUNTIME_API_EXTERN wasm_exec_env_t +wasm_runtime_get_exec_env_singleton(wasm_module_inst_t module_inst); + +/** + * Start debug instance based on given execution environment. + * Note: + * The debug instance will be destroyed during destroying the + * execution environment, developers don't need to destroy it + * manually. + * If the cluster of this execution environment has already + * been bound to a debug instance, this function will return true + * directly. + * If developer spawns some exec_env by wasm_runtime_spawn_exec_env, + * don't need to call this function for every spawned exec_env as + * they are sharing the same cluster with the main exec_env. + * + * @param exec_env the execution environment to start debug instance + * @param port the port for the debug server to listen on. + * 0 means automatic assignment. + * -1 means to use the global setting in RuntimeInitArgs. + * + * @return debug port if success, 0 otherwise. + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_start_debug_instance_with_port(wasm_exec_env_t exec_env, + int32_t port); + +/** + * Same as wasm_runtime_start_debug_instance_with_port(env, -1). + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_start_debug_instance(wasm_exec_env_t exec_env); + +/** + * Initialize the thread environment. + * Note: + * If developer creates a child thread by himself to call the + * the wasm function in that thread, he should call this API + * firstly before calling the wasm function and then call + * wasm_runtime_destroy_thread_env() after calling the wasm + * function. If the thread is created from the runtime API, + * it is unnecessary to call these two APIs. + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_init_thread_env(void); + +/** + * Destroy the thread environment + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_destroy_thread_env(void); + +/** + * Whether the thread environment is initialized + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_thread_env_inited(void); + +/** + * Get WASM module instance from execution environment + * + * @param exec_env the execution environment to retrieve + * + * @return the WASM module instance + */ +WASM_RUNTIME_API_EXTERN wasm_module_inst_t +wasm_runtime_get_module_inst(wasm_exec_env_t exec_env); + +/** + * Set WASM module instance of execution environment + * Caution: + * normally the module instance is bound with the execution + * environment one by one, if multiple module instances want + * to share to the same execution environment, developer should + * be responsible for the backup and restore of module instance + * + * @param exec_env the execution environment + * @param module_inst the WASM module instance to set + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_module_inst(wasm_exec_env_t exec_env, + const wasm_module_inst_t module_inst); + +/** + * @brief Lookup a memory instance by name + * + * @param module_inst The module instance + * @param name The name of the memory instance + * + * @return The memory instance if found, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_memory_inst_t +wasm_runtime_lookup_memory(const wasm_module_inst_t module_inst, + const char *name); + +/** + * @brief Get the default memory instance + * + * @param module_inst The module instance + * + * @return The memory instance if found, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_memory_inst_t +wasm_runtime_get_default_memory(const wasm_module_inst_t module_inst); + +/** + * @brief Get a memory instance by index + * + * @param module_inst The module instance + * @param index The index of the memory instance + * + * @return The memory instance if found, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_memory_inst_t +wasm_runtime_get_memory(const wasm_module_inst_t module_inst, uint32_t index); + +/** + * @brief Get the current number of pages for a memory instance + * + * @param memory_inst The memory instance + * + * @return The current number of pages + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_memory_get_cur_page_count(const wasm_memory_inst_t memory_inst); + +/** + * @brief Get the maximum number of pages for a memory instance + * + * @param memory_inst The memory instance + * + * @return The maximum number of pages + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_memory_get_max_page_count(const wasm_memory_inst_t memory_inst); + +/** + * @brief Get the number of bytes per page for a memory instance + * + * @param memory_inst The memory instance + * + * @return The number of bytes per page + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_memory_get_bytes_per_page(const wasm_memory_inst_t memory_inst); + +/** + * @brief Get the shared status for a memory instance + * + * @param memory_inst The memory instance + * + * @return True if shared, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_memory_get_shared(const wasm_memory_inst_t memory_inst); + +/** + * @brief Get the base address for a memory instance + * + * @param memory_inst The memory instance + * + * @return The base address on success, false otherwise + */ +WASM_RUNTIME_API_EXTERN void * +wasm_memory_get_base_address(const wasm_memory_inst_t memory_inst); + +/** + * @brief Enlarge a memory instance by a number of pages + * + * @param memory_inst The memory instance + * @param inc_page_count The number of pages to add + * + * @return True if successful, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_memory_enlarge(wasm_memory_inst_t memory_inst, uint64_t inc_page_count); + +/** + * Call the given WASM function of a WASM module instance with + * arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param argc total cell number that the function parameters occupy, + * a cell is a slot of the uint32 array argv[], e.g. i32/f32 argument + * occupies one cell, i64/f64 argument occupies two cells, note that + * it might be different from the parameter number of the function + * @param argv the arguments. If the function 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 the exception + * info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_wasm(wasm_exec_env_t exec_env, wasm_function_inst_t function, + uint32_t argc, uint32_t argv[]); + +/** + * Call the given WASM function of a WASM module instance with + * provided results space and arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param args the arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_wasm_a(wasm_exec_env_t exec_env, + wasm_function_inst_t function, uint32_t num_results, + wasm_val_t results[], uint32_t num_args, + wasm_val_t *args); + +/** + * Call the given WASM function of a WASM module instance with + * provided results space and variant arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param ... the variant arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_call_wasm_v(wasm_exec_env_t exec_env, + wasm_function_inst_t function, 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 + * provided by the caller of a registered 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. + * + * @param module_inst the WASM module instance + * @param argc the number of arguments + * @param argv the arguments array, if the main function has return value, + * *(int*)argv stores the return value of the called main function after + * this function returns. + * + * @return true if the main function is called, false otherwise and exception + * will be thrown, the caller can call wasm_runtime_get_exception to get + * the exception info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_application_execute_main(wasm_module_inst_t module_inst, int32_t argc, + char *argv[]); + +/** + * Find the specified function from a WASM module instance and execute + * that function. + * + * @param module_inst the WASM module instance + * @param name the name of the function to execute. + * to indicate the module name via: $module_name$function_name + * or just a function name: function_name + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the specified function is called, false otherwise and + * exception will be thrown, the caller can call wasm_runtime_get_exception + * to get the exception info. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_application_execute_func(wasm_module_inst_t module_inst, const char *name, + int32_t argc, char *argv[]); + +/** + * Get exception info of the WASM module instance. + * + * @param module_inst the WASM module instance + * + * @return the exception string + */ +WASM_RUNTIME_API_EXTERN const char * +wasm_runtime_get_exception(wasm_module_inst_t module_inst); + +/** + * Set exception info of the WASM module instance. + * + * @param module_inst the WASM module instance + * + * @param exception the exception string + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_exception(wasm_module_inst_t module_inst, + const char *exception); + +/** + * Clear exception info of the WASM module instance. + * + * @param module_inst the WASM module instance + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_clear_exception(wasm_module_inst_t module_inst); + +/** + * Terminate the WASM module instance. + * + * This function causes the module instance fail as if it raised a trap. + * + * This is intended to be used in situations like: + * + * - A thread is executing the WASM module instance + * (eg. it's in the middle of `wasm_application_execute_main`) + * + * - Another thread has a copy of `wasm_module_inst_t` of + * the module instance and wants to terminate it asynchronously. + * + * @param module_inst the WASM module instance + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_terminate(wasm_module_inst_t module_inst); + +/** + * Set custom data to WASM module instance. + * Note: + * If WAMR_BUILD_LIB_PTHREAD is enabled, this API + * will spread the custom data to all threads + * + * @param module_inst the WASM module instance + * @param custom_data the custom data to be set + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_custom_data(wasm_module_inst_t module_inst, void *custom_data); + +/** + * Get the custom data within a WASM module instance. + * + * @param module_inst the WASM module instance + * + * @return the custom data (NULL if not set yet) + */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_custom_data(wasm_module_inst_t module_inst); + +/** + * Set the memory bounds checks flag of a WASM module instance. + * + * @param module_inst the WASM module instance + * @param enable the flag to enable/disable the memory bounds checks + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_bounds_checks(wasm_module_inst_t module_inst, bool enable); + +/** + * Check if the memory bounds checks flag is enabled for a WASM module instance. + * + * @param module_inst the WASM module instance + * @return true if the memory bounds checks flag is enabled, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_bounds_checks_enabled(wasm_module_inst_t module_inst); + +/** + * Allocate memory from the heap of WASM module instance + * + * Note: wasm_runtime_module_malloc can call heap functions inside + * the module instance and thus cause a memory growth. + * This API needs to be used very carefully when you have a native + * pointers to the module instance memory obtained with + * wasm_runtime_addr_app_to_native or similar APIs. + * + * @param module_inst the WASM module instance which contains heap + * @param size the size bytes to allocate + * @param p_native_addr return native address of the allocated memory + * if it is not NULL, and return NULL if memory malloc failed + * + * @return the allocated memory address, which is a relative offset to the + * base address of the module instance's memory space. Note that + * it is not an absolute address. + * Return non-zero if success, zero if failed. + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_runtime_module_malloc(wasm_module_inst_t module_inst, uint64_t size, + void **p_native_addr); + +/** + * Free memory to the heap of WASM module instance + * + * @param module_inst the WASM module instance which contains heap + * @param ptr the pointer to free + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_module_free(wasm_module_inst_t module_inst, uint64_t ptr); + +/** + * Allocate memory from the heap of WASM module instance and initialize + * the memory with src + * + * @param module_inst the WASM module instance which contains heap + * @param src the source data to copy + * @param size the size of the source data + * + * @return the allocated memory address, which is a relative offset to the + * base address of the module instance's memory space. Note that + * it is not an absolute address. + * Return non-zero if success, zero if failed. + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_runtime_module_dup_data(wasm_module_inst_t module_inst, const char *src, + uint64_t size); + +/** + * Validate the app address, check whether it belongs to WASM module + * instance's address space, or in its heap space or memory space. + * + * @param module_inst the WASM module instance + * @param app_offset the app address to validate, which is a relative address + * @param size the size bytes of the app address + * + * @return true if success, false otherwise. If failed, an exception will + * be thrown. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_validate_app_addr(wasm_module_inst_t module_inst, + uint64_t app_offset, uint64_t size); + +/** + * Similar to wasm_runtime_validate_app_addr(), except that the size parameter + * is not provided. This function validates the app string address, check + * whether it belongs to WASM module instance's address space, or in its heap + * space or memory space. Moreover, it checks whether it is the offset of a + * string that is end with '\0'. + * + * Note: The validation result, especially the NUL termination check, + * is not reliable for a module instance with multiple threads because + * other threads can modify the heap behind us. + * + * @param module_inst the WASM module instance + * @param app_str_offset the app address of the string to validate, which is a + * relative address + * + * @return true if success, false otherwise. If failed, an exception will + * be thrown. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_validate_app_str_addr(wasm_module_inst_t module_inst, + uint64_t app_str_offset); + +/** + * Validate the native address, check whether it belongs to WASM module + * instance's address space, or in its heap space or memory space. + * + * @param module_inst the WASM module instance + * @param native_ptr the native address to validate, which is an absolute + * address + * @param size the size bytes of the app address + * + * @return true if success, false otherwise. If failed, an exception will + * be thrown. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_validate_native_addr(wasm_module_inst_t module_inst, + void *native_ptr, uint64_t size); + +/** + * Convert app address (relative address) to native address (absolute address) + * + * Note that native addresses to module instance memory can be invalidated + * on a memory growth. (Except shared memory, whose native addresses are + * stable.) + * + * @param module_inst the WASM module instance + * @param app_offset the app address + * + * @return the native address converted + */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_addr_app_to_native(wasm_module_inst_t module_inst, + uint64_t app_offset); + +/** + * Convert native address (absolute address) to app address (relative address) + * + * @param module_inst the WASM module instance + * @param native_ptr the native address + * + * @return the app address converted + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_runtime_addr_native_to_app(wasm_module_inst_t module_inst, + void *native_ptr); + +/** + * Get the app address range (relative address) that a app address belongs to + * + * @param module_inst the WASM module instance + * @param app_offset the app address to retrieve + * @param p_app_start_offset buffer to output the app start offset if not NULL + * @param p_app_end_offset buffer to output the app end offset if not NULL + * + * @return true if success, false otherwise. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_get_app_addr_range(wasm_module_inst_t module_inst, + uint64_t app_offset, + uint64_t *p_app_start_offset, + uint64_t *p_app_end_offset); + +/** + * Get the native address range (absolute address) that a native address + * belongs to + * + * @param module_inst the WASM module instance + * @param native_ptr the native address to retrieve + * @param p_native_start_addr buffer to output the native start address + * if not NULL + * @param p_native_end_addr buffer to output the native end address + * if not NULL + * + * @return true if success, false otherwise. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst, + uint8_t *native_ptr, + uint8_t **p_native_start_addr, + uint8_t **p_native_end_addr); + +/** + * Get the number of import items for a WASM module + * + * Typical usage scenario: + * Combine this function with wasm_runtime_get_import_count() to traverse + * all import items in a module. Use import_type.kind to filter and identify + * different types of import items. + * + * Example usage (as wasm_runtime_for_each_import_func() in + * samples/import-func-callback) + * + * @param module the WASM module + * + * @return the number of imports (zero for none), or -1 for failure + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_runtime_get_import_count(const wasm_module_t module); + +/** + * Get information about a specific WASM module import + * + * Typical usage scenario: + * Combine this function with wasm_runtime_get_import_count() to traverse + * all import items in a module. Use import_type.kind to filter and identify + * different types of import items. + * + * Example usage (as wasm_runtime_for_each_import_func() in + * samples/import-func-callback) + * + * @param module the WASM module + * @param import_index the desired import index + * @param import_type the location to store information about the import + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_get_import_type(const wasm_module_t module, int32_t import_index, + wasm_import_t *import_type); + +/** + * Get the number of export items for a WASM module + * + * @param module the WASM module + * + * @return the number of exports (zero for none), or -1 for failure + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_runtime_get_export_count(const wasm_module_t module); + +/** + * Get information about a specific WASM module export + * + * @param module the WASM module + * @param export_index the desired export index + * @param export_type the location to store information about the export + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_get_export_type(const wasm_module_t module, int32_t export_index, + wasm_export_t *export_type); + +/** + * Get the number of parameters for a function type + * + * @param func_type the function type + * + * @return the number of parameters for the function type + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_func_type_get_param_count(const wasm_func_type_t func_type); + +/** + * Get the kind of a parameter for a function type + * + * @param func_type the function type + * @param param_index the index of the parameter to get + * + * @return the kind of the parameter if successful, -1 otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_valkind_t +wasm_func_type_get_param_valkind(const wasm_func_type_t func_type, + uint32_t param_index); + +/** + * Get the number of results for a function type + * + * @param func_type the function type + * + * @return the number of results for the function type + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_func_type_get_result_count(const wasm_func_type_t func_type); + +/** + * Get the kind of a result for a function type + * + * @param func_type the function type + * @param result_index the index of the result to get + * + * @return the kind of the result if successful, -1 otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_valkind_t +wasm_func_type_get_result_valkind(const wasm_func_type_t func_type, + uint32_t result_index); + +/** + * Get the kind for a global type + * + * @param global_type the global type + * + * @return the kind of the global + */ +WASM_RUNTIME_API_EXTERN wasm_valkind_t +wasm_global_type_get_valkind(const wasm_global_type_t global_type); + +/** + * Get the mutability for a global type + * + * @param global_type the global type + * + * @return true if mutable, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_global_type_get_mutable(const wasm_global_type_t global_type); + +/** + * Get the shared setting for a memory type + * + * @param memory_type the memory type + * + * @return true if shared, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_memory_type_get_shared(const wasm_memory_type_t memory_type); + +/** + * Get the initial page count for a memory type + * + * @param memory_type the memory type + * + * @return the initial memory page count + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_memory_type_get_init_page_count(const wasm_memory_type_t memory_type); + +/** + * Get the maximum page count for a memory type + * + * @param memory_type the memory type + * + * @return the maximum memory page count + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_memory_type_get_max_page_count(const wasm_memory_type_t memory_type); + +/** + * Get the element kind for a table type + * + * @param table_type the table type + * + * @return the element kind + */ +WASM_RUNTIME_API_EXTERN wasm_valkind_t +wasm_table_type_get_elem_kind(const wasm_table_type_t table_type); + +/** + * Get the sharing setting for a table type + * + * @param table_type the table type + * + * @return true if shared, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_table_type_get_shared(const wasm_table_type_t table_type); + +/** + * Get the initial size for a table type + * + * @param table_type the table type + * + * @return the initial table size + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_table_type_get_init_size(const wasm_table_type_t table_type); + +/** + * Get the maximum size for a table type + * + * @param table_type the table type + * + * @return the maximum table size + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_table_type_get_max_size(const wasm_table_type_t table_type); + +/** + * Register native functions with same module name + * + * Note: The array `native_symbols` should not be read-only because the + * library can modify it in-place. + * + * Note: After successful call of this function, the array `native_symbols` + * is owned by the library. + * + * @param module_name the module name of the native functions + * @param native_symbols specifies an array of NativeSymbol structures which + * contain the names, function pointers and signatures + * Note: WASM runtime will not allocate memory to clone the data, so + * user must ensure the array can be used forever + * Meanings of letters in function signature: + * 'i': the parameter is i32 type + * 'I': the parameter is i64 type + * 'f': the parameter is f32 type + * 'F': the parameter is f64 type + * 'r': the parameter is externref type, it should be a uintptr_t + * in host + * '*': the parameter is a pointer (i32 in WASM), and runtime will + * auto check its boundary before calling the native function. + * If it is followed by '~', the checked length of the pointer + * is gotten from the following parameter, if not, the checked + * length of the pointer is 1. The runtime will also convert + * the app pointer to a native pointer, thus there is no need + * to manually call `wasm_runtime_addr_app_to_native`. + * '~': the parameter is the pointer's length with i32 type, and must + * follow after '*' + * '$': the parameter is a string (i32 in WASM), and runtime will + * auto check its boundary before calling the native function. + * Like '*', the runtime will also convert the app pointer to a + * native pointer. + * @param n_native_symbols specifies the number of native symbols in the array + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32_t n_native_symbols); + +/** + * Register native functions with same module name, similar to + * wasm_runtime_register_natives, the difference is that runtime passes raw + * arguments to native API, which means that the native API should be defined as + * void foo(wasm_exec_env_t exec_env, uint64 *args); + * and native API should extract arguments one by one from args array with macro + * native_raw_get_arg + * and write the return value back to args[0] with macro + * native_raw_return_type and native_raw_set_return + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32_t n_native_symbols); + +/** + * Undo wasm_runtime_register_natives or wasm_runtime_register_natives_raw + * + * @param module_name Should be the same as the corresponding + * wasm_runtime_register_natives. + * (Same in term of strcmp.) + * + * @param native_symbols Should be the same as the corresponding + * wasm_runtime_register_natives. + * (Same in term of pointer comparison.) + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_unregister_natives(const char *module_name, + NativeSymbol *native_symbols); + +/** + * Get an export global instance + * + * @param module_inst the module instance + * @param name the export global name + * @param global_inst location to store the global instance + * + * @return true if success, false otherwise + * + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_get_export_global_inst(const wasm_module_inst_t module_inst, + const char *name, + wasm_global_inst_t *global_inst); + +/** + * Get an export table instance + * + * @param module_inst the module instance + * @param name the export table name + * @param table_inst location to store the table instance + * + * @return true if success, false otherwise + * + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_get_export_table_inst(const wasm_module_inst_t module_inst, + const char *name, + wasm_table_inst_t *table_inst); + +/** + * Get a function instance from a table. + * + * @param module_inst the module instance + * @param table_inst the table instance + * @param idx the index in the table + * + * @return the function instance if successful, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_function_inst_t +wasm_table_get_func_inst(const wasm_module_inst_t module_inst, + const wasm_table_inst_t *table_inst, uint32_t idx); + +/** + * Get attachment of native function from execution environment + * + * @param exec_env the execution environment to retrieve + * + * @return the attachment of native function + */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_function_attachment(wasm_exec_env_t exec_env); + +/** + * Set user data to execution environment. + * + * @param exec_env the execution environment + * @param user_data the user data to be set + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_user_data(wasm_exec_env_t exec_env, void *user_data); + +/** + * Get the user data within execution environment. + * + * @param exec_env the execution environment + * + * @return the user data (NULL if not set yet) + */ +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_user_data(wasm_exec_env_t exec_env); + +/** + * Set native stack boundary to execution environment, if it is set, + * it will be used instead of getting the boundary with the platform + * layer API when calling wasm functions. This is useful for some + * fiber cases. + * + * Note: unlike setting the boundary by runtime, this API doesn't add + * the WASM_STACK_GUARD_SIZE(see comments in core/config.h) to the + * exec_env's native_stack_boundary to reserve bytes to the native + * thread stack boundary, which is used to throw native stack overflow + * exception if the guard boundary is reached. Developer should ensure + * that enough guard bytes are kept. + * + * @param exec_env the execution environment + * @param native_stack_boundary the user data to be set + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env, + uint8_t *native_stack_boundary); + +/** + * Set the instruction count limit to the execution environment. + * By default the instruction count limit is -1, which means no limit. + * However, if the instruction count limit is set to a positive value, + * the execution will be terminated when the instruction count reaches + * the limit. + * + * @param exec_env the execution environment + * @param instruction_count the instruction count limit + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env, + int instruction_count); + +/** + * Dump runtime memory consumption, including: + * Exec env memory consumption + * WASM module memory consumption + * WASM module instance memory consumption + * stack and app heap used info + * + * @param exec_env the execution environment + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_dump_mem_consumption(wasm_exec_env_t exec_env); + +/** + * Dump runtime performance profiler data of each function + * + * @param module_inst the WASM module instance to profile + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_dump_perf_profiling(wasm_module_inst_t module_inst); + +/** + * Return total wasm functions' execution time in ms + * + * @param module_inst the WASM module instance to profile + */ +WASM_RUNTIME_API_EXTERN double +wasm_runtime_sum_wasm_exec_time(wasm_module_inst_t module_inst); + +/** + * Return execution time in ms of a given wasm function with + * func_name. If the function is not found, return 0. + * + * @param module_inst the WASM module instance to profile + * @param func_name could be an export name or a name in the + * name section + */ +WASM_RUNTIME_API_EXTERN double +wasm_runtime_get_wasm_func_exec_time(wasm_module_inst_t inst, + const char *func_name); + +/* wasm thread callback function type */ +typedef void *(*wasm_thread_callback_t)(wasm_exec_env_t, void *); +/* wasm thread type */ +typedef uintptr_t wasm_thread_t; + +/** + * Set the max thread num per cluster. + * + * @param num maximum thread num + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_max_thread_num(uint32_t num); + +/** + * Spawn a new exec_env, the spawned exec_env + * can be used in other threads + * + * @param num the original exec_env + * + * @return the spawned exec_env if success, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN wasm_exec_env_t +wasm_runtime_spawn_exec_env(wasm_exec_env_t exec_env); + +/** + * Destroy the spawned exec_env + * + * @param exec_env the spawned exec_env + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_destroy_spawned_exec_env(wasm_exec_env_t exec_env); + +/** + * Spawn a thread from the given exec_env + * + * @param exec_env the original exec_env + * @param tid thread id to be returned to the caller + * @param callback the callback function provided by the user + * @param arg the arguments passed to the callback + * + * @return 0 if success, -1 otherwise + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_runtime_spawn_thread(wasm_exec_env_t exec_env, wasm_thread_t *tid, + wasm_thread_callback_t callback, void *arg); + +/** + * Wait a spawned thread to terminate + * + * @param tid thread id + * @param retval if not NULL, output the return value of the thread + * + * @return 0 if success, error number otherwise + */ +WASM_RUNTIME_API_EXTERN int32_t +wasm_runtime_join_thread(wasm_thread_t tid, void **retval); + +/** + * Map external object to an internal externref index: if the index + * has been created, return it, otherwise create the index. + * + * @param module_inst the WASM module instance that the extern object + * belongs to + * @param extern_obj the external object to be mapped + * @param p_externref_idx return externref index of the external object + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_obj2ref(wasm_module_inst_t module_inst, void *extern_obj, + uint32_t *p_externref_idx); + +/** + * Delete external object registered by `wasm_externref_obj2ref`. + * + * @param module_inst the WASM module instance that the extern object + * belongs to + * @param extern_obj the external object to be deleted + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_objdel(wasm_module_inst_t module_inst, void *extern_obj); + +/** + * Set cleanup callback to release external object. + * + * @param module_inst the WASM module instance that the extern object + * belongs to + * @param extern_obj the external object to which to set the + * `extern_obj_cleanup` cleanup callback. + * @param extern_obj_cleanup a callback to release `extern_obj` + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_set_cleanup(wasm_module_inst_t module_inst, void *extern_obj, + void (*extern_obj_cleanup)(void *)); + +/** + * Retrieve the external object from an internal externref index + * + * @param externref_idx the externref index to retrieve + * @param p_extern_obj return the mapped external object of + * the externref index + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_ref2obj(uint32_t externref_idx, void **p_extern_obj); + +/** + * Retain an extern object which is mapped to the internal externref + * so that the object won't be cleaned during extern object reclaim + * if it isn't used. + * + * @param externref_idx the externref index of an external object + * to retain + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_retain(uint32_t externref_idx); + +/** + * Dump the call stack to stdout + * + * @param exec_env the execution environment + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_dump_call_stack(wasm_exec_env_t exec_env); + +/** + * Get the size required to store the call stack contents, including + * the space for terminating null byte ('\0') + * + * @param exec_env the execution environment + * + * @return size required to store the contents, 0 means error + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_call_stack_buf_size(wasm_exec_env_t exec_env); + +/** + * Dump the call stack to buffer. + * + * @note this function is not thread-safe, please only use this API + * when the exec_env is not executing + * + * @param exec_env the execution environment + * @param buf buffer to store the dumped content + * @param len length of the buffer + * + * @return bytes dumped to the buffer, including the terminating null + * byte ('\0'), 0 means error and data in buf may be invalid + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_dump_call_stack_to_buf(wasm_exec_env_t exec_env, char *buf, + uint32_t len); + +/** + * Get the size required to store the LLVM PGO profile data * * @param module_inst the WASM module instance - * @param app_offset the app address to retrieve - * @param p_app_start_offset buffer to output the app start offset if not NULL - * @param p_app_end_offset buffer to output the app end offset if not NULL * - * @return true if success, false otherwise. + * @return size required to store the contents, 0 means error */ -bool -wasm_runtime_get_app_addr_range(wasm_module_inst_t module_inst, - int32_t app_offset, - int32_t *p_app_start_offset, - int32_t *p_app_end_offset); +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_get_pgo_prof_data_size(wasm_module_inst_t module_inst); /** - * Get the native address range (absolute address) that a native address belongs to + * Dump the LLVM PGO profile data to buffer * * @param module_inst the WASM module instance - * @param native_ptr the native address to retrieve - * @param p_native_start_addr buffer to output the native start address if not NULL - * @param p_native_end_addr buffer to output the native end address if not NULL + * @param buf buffer to store the dumped content + * @param len length of the buffer + * + * @return bytes dumped to the buffer, 0 means error and data in buf + * may be invalid + */ +WASM_RUNTIME_API_EXTERN uint32_t +wasm_runtime_dump_pgo_prof_data_to_buf(wasm_module_inst_t module_inst, + char *buf, uint32_t len); + +/** + * Get a custom section by name + * + * @param module_comm the module to find + * @param name name of the custom section + * @param len return the length of the content if found + * + * @return Custom section content (not including the name length + * and name string) if found, NULL otherwise + */ +WASM_RUNTIME_API_EXTERN const uint8_t * +wasm_runtime_get_custom_section(const wasm_module_t module_comm, + const char *name, uint32_t *len); + +/** + * Get WAMR semantic version + */ +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 native 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 native globals + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name); + +/** + * Enlarge the memory region for a module instance + * + * @param module_inst the module instance + * @param inc_page_count the number of pages to add + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_enlarge_memory(wasm_module_inst_t module_inst, + uint64_t inc_page_count); + +typedef enum { + INTERNAL_ERROR, + MAX_SIZE_REACHED, +} enlarge_memory_error_reason_t; + +typedef void (*enlarge_memory_error_callback_t)( + uint32_t inc_page_count, uint64_t current_memory_size, + uint32_t memory_index, enlarge_memory_error_reason_t failure_reason, + wasm_module_inst_t instance, wasm_exec_env_t exec_env, void *user_data); + +/** + * Setup callback invoked when memory.grow fails + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_enlarge_mem_error_callback( + const enlarge_memory_error_callback_t callback, void *user_data); + +/* + * module instance context APIs + * wasm_runtime_create_context_key + * wasm_runtime_destroy_context_key + * wasm_runtime_set_context + * wasm_runtime_set_context_spread + * wasm_runtime_get_context + * + * This set of APIs is intended to be used by an embedder which provides + * extra sets of native functions, which need per module instance state + * and are maintained outside of the WAMR tree. + * + * It's modelled after the pthread specific API. + * + * wasm_runtime_set_context_spread is similar to + * wasm_runtime_set_context, except that + * wasm_runtime_set_context_spread applies the change + * to all threads in the cluster. + * It's an undefined behavior if multiple threads in a cluster call + * wasm_runtime_set_context_spread on the same key + * simultaneously. It's a caller's responsibility to perform necessary + * serialization if necessary. For example: + * + * if (wasm_runtime_get_context(inst, key) == NULL) { + * newctx = alloc_and_init(...); + * lock(some_lock); + * if (wasm_runtime_get_context(inst, key) == NULL) { + * // this thread won the race + * wasm_runtime_set_context_spread(inst, key, newctx); + * newctx = NULL; + * } + * unlock(some_lock); + * if (newctx != NULL) { + * // this thread lost the race, free it + * cleanup_and_free(newctx); + * } + * } + * + * Note: dynamic key create/destroy while instances are live is not + * implemented as of writing this. + * it's caller's responsibility to ensure destroying all module instances + * before calling wasm_runtime_create_context_key or + * wasm_runtime_destroy_context_key. + * otherwise, it's an undefined behavior. + * + * Note about threads: + * - When spawning a thread, the contexts (the pointers given to + * wasm_runtime_set_context) are copied from the parent + * instance. + * - The destructor is called only on the main instance. + */ + +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_create_context_key(void (*dtor)(wasm_module_inst_t inst, + void *ctx)); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_destroy_context_key(void *key); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_context(wasm_module_inst_t inst, void *key, void *ctx); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_set_context_spread(wasm_module_inst_t inst, void *key, void *ctx); + +WASM_RUNTIME_API_EXTERN void * +wasm_runtime_get_context(wasm_module_inst_t inst, void *key); + +/* + * wasm_runtime_begin_blocking_op/wasm_runtime_end_blocking_op + * + * These APIs are intended to be used by the implementations of + * host functions. It wraps an operation which possibly blocks for long + * to prepare for async termination. + * + * For simplicity, we recommend to wrap only the very minimum piece of + * the code with this. Ideally, just a single system call. + * + * eg. + * + * if (!wasm_runtime_begin_blocking_op(exec_env)) { + * return EINTR; + * } + * ret = possibly_blocking_op(); + * wasm_runtime_end_blocking_op(exec_env); + * return ret; + * + * If threading support (WASM_ENABLE_THREAD_MGR) is not enabled, + * these functions are no-op. + * + * If the underlying platform support (OS_ENABLE_WAKEUP_BLOCKING_OP) is + * not available, these functions are no-op. In that case, the runtime + * might not terminate a blocking thread in a timely manner. * + * If the underlying platform support is available, it's used to wake up + * the thread for async termination. The expectation here is that a + * `os_wakeup_blocking_op` call makes the blocking operation + * (`possibly_blocking_op` in the above example) return in a timely manner. + * + * The actual wake up mechanism used by `os_wakeup_blocking_op` is + * platform-dependent. It might impose some platform-dependent restrictions + * on the implementation of the blocking operation. + * + * For example, on POSIX-like platforms, a signal (by default SIGUSR1) is + * used. The signal delivery configurations (eg. signal handler, signal mask, + * etc) for the signal are set up by the runtime. You can change the signal + * to use for this purpose by calling os_set_signal_number_for_blocking_op + * before the runtime initialization. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_begin_blocking_op(wasm_exec_env_t exec_env); + +WASM_RUNTIME_API_EXTERN void +wasm_runtime_end_blocking_op(wasm_exec_env_t exec_env); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_set_module_name(wasm_module_t module, const char *name, + char *error_buf, uint32_t error_buf_size); + +/* return the most recently set module name or "" if never set before */ +WASM_RUNTIME_API_EXTERN const char * +wasm_runtime_get_module_name(wasm_module_t module); + +/* + * wasm_runtime_detect_native_stack_overflow + * + * Detect native stack shortage. + * Ensure that the calling thread still has a reasonable amount of + * native stack (WASM_STACK_GUARD_SIZE bytes) available. + * + * If enough stack is left, this function returns true. + * Otherwise, this function raises a "native stack overflow" trap and + * returns false. + * + * Note: please do not expect a very strict detection. it's a good idea + * to give some margins. wasm_runtime_detect_native_stack_overflow itself + * requires a small amount of stack to run. + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow(wasm_exec_env_t exec_env); + +/* + * wasm_runtime_detect_native_stack_overflow_size + * + * Similar to wasm_runtime_detect_native_stack_overflow, + * but use the caller-specified size instead of WASM_STACK_GUARD_SIZE. + * + * An expected usage: + * ```c + * __attribute__((noinline)) // inlining can break the stack check + * void stack_hog(void) + * { + * // consume a lot of stack here + * } + * + * void + * stack_hog_wrapper(exec_env) { + * // the amount of stack stack_hog would consume, + * // plus a small margin + * uint32_t size = 10000000; + * + * if (!wasm_runtime_detect_native_stack_overflow_size(exec_env, size)) { + * // wasm_runtime_detect_native_stack_overflow_size has raised + * // a trap. + * return; + * } + * stack_hog(); + * } + * ``` + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_detect_native_stack_overflow_size(wasm_exec_env_t exec_env, + uint32_t required_size); + +/** + * Query whether the wasm binary buffer used to create the module can be freed + * + * @param module the target module + * @return true if the wasm binary buffer can be freed + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_underlying_binary_freeable(const wasm_module_t module); + +/** + * Create a shared heap + * + * @param init_args the initialization arguments + * @return the shared heap created + */ +WASM_RUNTIME_API_EXTERN wasm_shared_heap_t +wasm_runtime_create_shared_heap(SharedHeapInitArgs *init_args); + +/** + * This function links two shared heap(lists), `head` and `body` in to a single + * shared heap list, where `head` becomes the new shared heap list head. The + * shared heap list remains one continuous shared heap in wasm app's point of + * view. At most one shared heap in shared heap list can be dynamically + * allocated, the rest have to be the pre-allocated shared heap. * + * + * @param head The head of the shared heap chain. + * @param body The body of the shared heap chain to be appended. + * @return The new head of the shared heap chain. NULL if failed. + */ +WASM_RUNTIME_API_EXTERN wasm_shared_heap_t +wasm_runtime_chain_shared_heaps(wasm_shared_heap_t head, + wasm_shared_heap_t body); + +/** + * This function unchains the shared heaps from the given head. If + * `entire_chain` is true, it will unchain the entire chain of shared heaps. + * Otherwise, it will unchain only the first shared heap in the chain. + * + * @param head The head of the shared heap chain. + * @param entire_chain A boolean flag indicating whether to unchain the entire + * chain. + * @return The new head of the shared heap chain. Or the last shared heap in the + * chain if `entire_chain` is true. + */ +wasm_shared_heap_t +wasm_runtime_unchain_shared_heaps(wasm_shared_heap_t head, bool entire_chain); + +/** + * Reset shared heap chain. For each shared heap in the chain, if it is a + * pre-allocated shared heap, its memory region will be zeroed. For a + * WAMR-managed shared heap, it will be destroyed and reinitialized. + * + * @param shared_heap The head of the shared heap chain. * @return true if success, false otherwise. */ -bool -wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst, - uint8_t *native_ptr, - uint8_t **p_native_start_addr, - uint8_t **p_native_end_addr); +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_reset_shared_heap_chain(wasm_shared_heap_t shared_heap); + +/** + * Attach a shared heap, it can be the head of shared heap chain, in that case, + * attach the shared heap chain, to a module instance + * + * @param module_inst the module instance + * @param shared_heap the shared heap + * @return true if success, false if failed + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_attach_shared_heap(wasm_module_inst_t module_inst, + wasm_shared_heap_t shared_heap); + +/** + * Detach a shared heap from a module instance + * + * @param module_inst the module instance + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_detach_shared_heap(wasm_module_inst_t module_inst); + +/** + * Allocate memory from a shared heap, or the non-preallocated shared heap from + * the shared heap chain + * + * @param module_inst the module instance + * @param size required memory size + * @param p_native_addr native address of allocated memory + * + * @return return the allocated memory address, which reuses part of the wasm + * address space and is in the range of [UINT32 - shared_heap_size + 1, UINT32] + * (when the wasm memory is 32-bit) or [UINT64 - shared_heap_size + 1, UINT64] + * (when the wasm memory is 64-bit). Note that it is not an absolute address. + * Return non-zero if success, zero if failed. + */ +WASM_RUNTIME_API_EXTERN uint64_t +wasm_runtime_shared_heap_malloc(wasm_module_inst_t module_inst, uint64_t size, + void **p_native_addr); + +/** + * Free the memory allocated from shared heap, or the non-preallocated shared + * heap from the shared heap chain + * + * @param module_inst the module instance + * @param ptr the offset in wasm app + */ +WASM_RUNTIME_API_EXTERN void +wasm_runtime_shared_heap_free(wasm_module_inst_t module_inst, uint64_t ptr); #ifdef __cplusplus } diff --git a/core/iwasm/interpreter/SConscript b/core/iwasm/interpreter/SConscript new file mode 100644 index 0000000000..7c0605ee94 --- /dev/null +++ b/core/iwasm/interpreter/SConscript @@ -0,0 +1,30 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() + +src = Split(''' +wasm_runtime.c +''') + +if GetDepend(['WAMR_BUILD_FAST_INTERP']): + src += ["wasm_interp_fast.c"] +else: + src += ["wasm_interp_classic.c"] + +if GetDepend(['WAMR_BUILD_MINI_LOADER']): + src += ["wasm_mini_loader.c"] +else: + src += ["wasm_loader.c"] + + +CPPPATH = [cwd] + +group = DefineGroup('iwasm_interpreter', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/iwasm/interpreter/iwasm_interp.cmake b/core/iwasm/interpreter/iwasm_interp.cmake index 9ac035b1cb..e6e52e42c8 100644 --- a/core/iwasm/interpreter/iwasm_interp.cmake +++ b/core/iwasm/interpreter/iwasm_interp.cmake @@ -7,7 +7,23 @@ add_definitions (-DWASM_ENABLE_INTERP=1) include_directories(${IWASM_INTERP_DIR}) -file (GLOB_RECURSE source_all ${IWASM_INTERP_DIR}/*.c) +if (WAMR_BUILD_FAST_INTERP EQUAL 1) + set (INTERPRETER "wasm_interp_fast.c") +else () + set (INTERPRETER "wasm_interp_classic.c") +endif () + +if (WAMR_BUILD_MINI_LOADER EQUAL 1) + set (LOADER "wasm_mini_loader.c") +else () + set (LOADER "wasm_loader.c") +endif () + +file (GLOB_RECURSE source_all + ${IWASM_INTERP_DIR}/${LOADER} + ${IWASM_INTERP_DIR}/wasm_runtime.c + ${IWASM_INTERP_DIR}/${INTERPRETER} +) set (IWASM_INTERP_SOURCE ${source_all}) diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 27918ac5fb..879bdc64b1 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -9,31 +9,148 @@ #include "bh_platform.h" #include "bh_hashmap.h" #include "bh_assert.h" +#if WASM_ENABLE_GC != 0 +#include "gc_export.h" +#endif #ifdef __cplusplus extern "C" { #endif -/** Value Type */ +/* Value Type */ #define VALUE_TYPE_I32 0x7F #define VALUE_TYPE_I64 0X7E #define VALUE_TYPE_F32 0x7D #define VALUE_TYPE_F64 0x7C +#define VALUE_TYPE_V128 0x7B +#define VALUE_TYPE_FUNCREF 0x70 +#define VALUE_TYPE_EXTERNREF 0x6F #define VALUE_TYPE_VOID 0x40 + +/* Packed Types */ +#define PACKED_TYPE_I8 0x78 +#define PACKED_TYPE_I16 0x77 + +/* Reference Types */ +#define REF_TYPE_NULLFUNCREF 0x73 +#define REF_TYPE_NULLEXTERNREF 0x72 +#define REF_TYPE_NULLREF 0x71 +#define REF_TYPE_FUNCREF VALUE_TYPE_FUNCREF /* 0x70 */ +#define REF_TYPE_EXTERNREF VALUE_TYPE_EXTERNREF /* 0x6F */ +#define REF_TYPE_ANYREF 0x6E +#define REF_TYPE_EQREF 0x6D +#define REF_TYPE_I31REF 0x6C +#define REF_TYPE_STRUCTREF 0x6B +#define REF_TYPE_ARRAYREF 0x6A +#define REF_TYPE_HT_NON_NULLABLE 0x64 +#define REF_TYPE_HT_NULLABLE 0x63 +#define REF_TYPE_STRINGREF VALUE_TYPE_STRINGREF /* 0x67 */ +#define REF_TYPE_STRINGVIEWWTF8 VALUE_TYPE_STRINGVIEWWTF8 /* 0x66 */ +#define REF_TYPE_STRINGVIEWWTF16 VALUE_TYPE_STRINGVIEWWTF16 /* 0x62 */ +#define REF_TYPE_STRINGVIEWITER VALUE_TYPE_STRINGVIEWITER /* 0x61 */ + +/* Heap Types */ +#define HEAP_TYPE_NOFUNC (-0x0D) +#define HEAP_TYPE_NOEXTERN (-0x0E) +#define HEAP_TYPE_NONE (-0x0F) +#define HEAP_TYPE_FUNC (-0x10) +#define HEAP_TYPE_EXTERN (-0x11) +#define HEAP_TYPE_ANY (-0x12) +#define HEAP_TYPE_EQ (-0x13) +#define HEAP_TYPE_I31 (-0x14) +#define HEAP_TYPE_STRUCT (-0x15) +#define HEAP_TYPE_ARRAY (-0x16) +#define HEAP_TYPE_STRINGREF (-0x19) +#define HEAP_TYPE_STRINGVIEWWTF8 (-0x1A) +#define HEAP_TYPE_STRINGVIEWWTF16 (-0x1E) +#define HEAP_TYPE_STRINGVIEWITER (-0x1F) + +/* Defined Types */ +#define DEFINED_TYPE_FUNC 0x60 +#define DEFINED_TYPE_STRUCT 0x5F +#define DEFINED_TYPE_ARRAY 0x5E +#define DEFINED_TYPE_SUB 0x50 +#define DEFINED_TYPE_SUB_FINAL 0x4F +#define DEFINED_TYPE_REC 0x4E + /* Used by AOT */ -#define VALUE_TYPE_I1 0x41 +#define VALUE_TYPE_I1 0x41 +/** + * Used by loader to represent any type of i32/i64/f32/f64/v128 + * and ref types, including funcref, externref, anyref, eqref, + * (ref null $ht), (ref $ht), i31ref, structref, arrayref, + * nullfuncref, nullexternref, nullref and stringref + */ +#define VALUE_TYPE_ANY 0x42 +/** + * Used by wamr compiler to represent object ref types, + * including func object ref, externref object ref, + * internal object ref, eq object ref, i31 object ref, + * struct object ref, array object ref + */ +#define VALUE_TYPE_GC_REF 0x43 -/* Table Element Type */ -#define TABLE_ELEM_TYPE_ANY_FUNC 0x70 +#define MAX_PAGE_COUNT_FLAG 0x01 +#define SHARED_MEMORY_FLAG 0x02 +#define MEMORY64_FLAG 0x04 +#define MAX_TABLE_SIZE_FLAG 0x01 +/* the shared flag for table is not actual used now */ +#define SHARED_TABLE_FLAG 0x02 +#define TABLE64_FLAG 0x04 + +/** + * In the multi-memory proposal, the memarg in loads and stores are + * reinterpreted as a bitfield, bit 6 serves as a flag indicating the presence + * of the optional memory index, if it is set, then an i32 memory index follows + * after the alignment bitfield + */ +#define OPT_MEMIDX_FLAG 0x40 #define DEFAULT_NUM_BYTES_PER_PAGE 65536 +#define DEFAULT_MAX_PAGES 65536 +#define DEFAULT_MEM64_MAX_PAGES UINT32_MAX +/* Max size of linear memory */ +#define MAX_LINEAR_MEMORY_SIZE (4 * (uint64)BH_GB) +/* Roughly 274 TB */ +#define MAX_LINEAR_MEM64_MEMORY_SIZE \ + (DEFAULT_MEM64_MAX_PAGES * (uint64)64 * (uint64)BH_KB) +/* Macro to check memory flag and return appropriate memory size */ +#define GET_MAX_LINEAR_MEMORY_SIZE(is_memory64) \ + (is_memory64 ? MAX_LINEAR_MEM64_MEMORY_SIZE : MAX_LINEAR_MEMORY_SIZE) + +#if WASM_ENABLE_GC == 0 +typedef uintptr_t table_elem_type_t; +#define NULL_REF (0xFFFFFFFF) +#else +typedef void *table_elem_type_t; +#define NULL_REF (NULL) +#define REF_CELL_NUM ((uint32)sizeof(uintptr_t) / sizeof(uint32)) +#endif + +#define INIT_EXPR_NONE 0x00 #define INIT_EXPR_TYPE_I32_CONST 0x41 #define INIT_EXPR_TYPE_I64_CONST 0x42 #define INIT_EXPR_TYPE_F32_CONST 0x43 #define INIT_EXPR_TYPE_F64_CONST 0x44 +#define INIT_EXPR_TYPE_V128_CONST 0xFD #define INIT_EXPR_TYPE_GET_GLOBAL 0x23 -#define INIT_EXPR_TYPE_ERROR 0xff +#define INIT_EXPR_TYPE_I32_ADD 0x6A +#define INIT_EXPR_TYPE_I32_SUB 0x6B +#define INIT_EXPR_TYPE_I32_MUL 0x6C +#define INIT_EXPR_TYPE_I64_ADD 0x7C +#define INIT_EXPR_TYPE_I64_SUB 0x7D +#define INIT_EXPR_TYPE_I64_MUL 0x7E +#define INIT_EXPR_TYPE_REFNULL_CONST 0xD0 +#define INIT_EXPR_TYPE_FUNCREF_CONST 0xD2 +#define INIT_EXPR_TYPE_STRUCT_NEW 0xD3 +#define INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT 0xD4 +#define INIT_EXPR_TYPE_ARRAY_NEW 0xD5 +#define INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT 0xD6 +#define INIT_EXPR_TYPE_ARRAY_NEW_FIXED 0xD7 +#define INIT_EXPR_TYPE_I31_NEW 0xD8 +#define INIT_EXPR_TYPE_ANY_CONVERT_EXTERN 0xD9 +#define INIT_EXPR_TYPE_EXTERN_CONVERT_ANY 0xDA #define WASM_MAGIC_NUMBER 0x6d736100 #define WASM_CURRENT_VERSION 1 @@ -50,102 +167,501 @@ extern "C" { #define SECTION_TYPE_ELEM 9 #define SECTION_TYPE_CODE 10 #define SECTION_TYPE_DATA 11 +#if WASM_ENABLE_BULK_MEMORY != 0 +#define SECTION_TYPE_DATACOUNT 12 +#endif +#if WASM_ENABLE_TAGS != 0 +#define SECTION_TYPE_TAG 13 +#endif +#if WASM_ENABLE_STRINGREF != 0 +#define SECTION_TYPE_STRINGREF 14 +#endif + +#define SUB_SECTION_TYPE_MODULE 0 +#define SUB_SECTION_TYPE_FUNC 1 +#define SUB_SECTION_TYPE_LOCAL 2 #define IMPORT_KIND_FUNC 0 #define IMPORT_KIND_TABLE 1 #define IMPORT_KIND_MEMORY 2 #define IMPORT_KIND_GLOBAL 3 +#if WASM_ENABLE_TAGS != 0 +#define IMPORT_KIND_TAG 4 +#endif #define EXPORT_KIND_FUNC 0 #define EXPORT_KIND_TABLE 1 #define EXPORT_KIND_MEMORY 2 #define EXPORT_KIND_GLOBAL 3 +#if WASM_ENABLE_TAGS != 0 +#define EXPORT_KIND_TAG 4 +#endif + +#define LABEL_TYPE_BLOCK 0 +#define LABEL_TYPE_LOOP 1 +#define LABEL_TYPE_IF 2 +#define LABEL_TYPE_FUNCTION 3 +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define LABEL_TYPE_TRY 4 +#define LABEL_TYPE_CATCH 5 +#define LABEL_TYPE_CATCH_ALL 6 +#endif -#define BLOCK_TYPE_BLOCK 0 -#define BLOCK_TYPE_LOOP 1 -#define BLOCK_TYPE_IF 2 -#define BLOCK_TYPE_FUNCTION 3 +#define WASM_TYPE_FUNC 0 +#define WASM_TYPE_STRUCT 1 +#define WASM_TYPE_ARRAY 2 + +#if WASM_ENABLE_STRINGREF != 0 +#define WASM_TYPE_STRINGREF 3 +#define WASM_TYPE_STRINGVIEWWTF8 4 +#define WASM_TYPE_STRINGVIEWWTF16 5 +#define WASM_TYPE_STRINGVIEWITER 6 +#endif + +/* In WasmGC, a table can start with [0x40 0x00] to indicate it has an + * initializer */ +#define TABLE_INIT_EXPR_FLAG 0x40 + +typedef struct WASMModule WASMModule; +typedef struct WASMFunction WASMFunction; +typedef struct WASMGlobal WASMGlobal; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTag WASMTag; +#endif + +#ifndef WASM_VALUE_DEFINED +#define WASM_VALUE_DEFINED + +typedef union V128 { + int8 i8x16[16]; + int16 i16x8[8]; + int32 i32x4[4]; + int64 i64x2[2]; + float32 f32x4[4]; + float64 f64x2[2]; +} V128; typedef union WASMValue { int32 i32; uint32 u32; + uint32 global_index; + uint32 ref_index; int64 i64; uint64 u64; float32 f32; float64 f64; - uintptr_t addr; + V128 v128; +#if WASM_ENABLE_GC != 0 + wasm_obj_t gc_obj; + uint32 type_index; + struct { + uint32 type_index; + uint32 length; + } array_new_default; + /* pointer to a memory space holding more data, current usage: + * struct.new init value: WASMStructNewInitValues * + * array.new init value: WASMArrayNewInitValues * + */ + void *data; +#endif } WASMValue; +#endif /* end of WASM_VALUE_DEFINED */ + +typedef struct WASMStructNewInitValues { + uint32 type_idx; + uint32 count; + WASMValue fields[1]; +} WASMStructNewInitValues; + +typedef struct WASMArrayNewInitValues { + uint32 type_idx; + uint32 length; + WASMValue elem_data[1]; +} WASMArrayNewInitValues; typedef struct InitializerExpression { - /* type of INIT_EXPR_TYPE_XXX */ + /* type of INIT_EXPR_TYPE_XXX, which is an instruction of + constant expression */ uint8 init_expr_type; union { - int32 i32; - int64 i64; - float32 f32; - float64 f64; - uint32 global_index; + struct { + WASMValue v; + } unary; + struct { + struct InitializerExpression *l_expr; + struct InitializerExpression *r_expr; + } binary; } u; } InitializerExpression; +static inline bool +is_expr_binary_op(uint8 flag) +{ + return flag == INIT_EXPR_TYPE_I32_ADD || flag == INIT_EXPR_TYPE_I32_SUB + || flag == INIT_EXPR_TYPE_I32_MUL || flag == INIT_EXPR_TYPE_I64_ADD + || flag == INIT_EXPR_TYPE_I64_SUB || flag == INIT_EXPR_TYPE_I64_MUL; +} + +/* check if table or data offset is valid for i32 offset */ +static inline bool +is_valid_i32_offset(uint8 flag) +{ + return flag == INIT_EXPR_TYPE_I32_CONST || flag == INIT_EXPR_TYPE_I32_ADD + || flag == INIT_EXPR_TYPE_I32_SUB || flag == INIT_EXPR_TYPE_I32_MUL; +} + +/* check if table or data offset is valid for i64 offset */ +static inline bool +is_valid_i64_offset(uint8 flag) +{ + return flag == INIT_EXPR_TYPE_I64_CONST || flag == INIT_EXPR_TYPE_I64_ADD + || flag == INIT_EXPR_TYPE_I64_SUB || flag == INIT_EXPR_TYPE_I64_MUL; +} + +#if WASM_ENABLE_GC != 0 +/** + * Reference type of (ref null ht) or (ref ht), + * and heap type is defined type (type i), i >= 0 + */ +typedef struct RefHeapType_TypeIdx { + /* ref_type is REF_TYPE_HT_NULLABLE or + REF_TYPE_HT_NON_NULLABLE, (0x63 or 0x64) */ + uint8 ref_type; + /* true if ref_type is REF_TYPE_HT_NULLABLE */ + bool nullable; + /* heap type is defined type: type_index >= 0 */ + int32 type_idx; +} RefHeapType_TypeIdx; + +/** + * Reference type of (ref null ht) or (ref ht), + * and heap type is non-defined type + */ +typedef struct RefHeapType_Common { + /* ref_type is REF_TYPE_HT_NULLABLE or + REF_TYPE_HT_NON_NULLABLE (0x63 or 0x64) */ + uint8 ref_type; + /* true if ref_type is REF_TYPE_HT_NULLABLE */ + bool nullable; + /* Common heap type (not defined type): + -0x10 (func), -0x11 (extern), -0x12 (any), -0x13 (eq), + -0x16 (i31), -0x17 (nofunc), -0x18 (noextern), + -0x19 (struct), -0x20 (array), -0x21 (none) */ + int32 heap_type; +} RefHeapType_Common; + +/** + * Reference type + */ +typedef union WASMRefType { + uint8 ref_type; + RefHeapType_TypeIdx ref_ht_typeidx; + RefHeapType_Common ref_ht_common; +} WASMRefType; + +typedef struct WASMRefTypeMap { + /** + * The type index of a type array, which only stores + * the first byte of the type, e.g. WASMFuncType.types, + * WASMStructType.fields + */ + uint16 index; + /* The full type info if the type cannot be described + with one byte */ + WASMRefType *ref_type; +} WASMRefTypeMap; +#endif /* end of WASM_ENABLE_GC */ + +#if WASM_ENABLE_GC == 0 +typedef struct WASMFuncType WASMType; +typedef WASMType *WASMTypePtr; +#else +/** + * Common type, store the same fields of + * WASMFuncType, WASMStructType and WASMArrayType + */ typedef struct WASMType { - uint32 param_count; - /* only one result is supported currently */ - uint32 result_count; - /* types of params and results */ + /** + * type_flag must be WASM_TYPE_FUNC/STRUCT/ARRAY to + * denote that it is a WASMFuncType, WASMStructType or + * WASMArrayType + */ + uint16 type_flag; + + bool is_sub_final; + /* How many types are referring to this type */ + uint16 ref_count; + /* The inheritance depth */ + uint16 inherit_depth; + /* The root type */ + struct WASMType *root_type; + /* The parent type */ + struct WASMType *parent_type; + uint32 parent_type_idx; + + /* The number of internal types in the current rec group, and if + the type is not in a recursive group, rec_count is 1 since a + single type definition is reinterpreted as a short-hand for a + recursive group containing just one type */ + uint16 rec_count; + uint16 rec_idx; + /* The index of the begin type of this group */ + uint32 rec_begin_type_idx; +} WASMType, *WASMTypePtr; +#endif /* end of WASM_ENABLE_GC */ + +/* Function type */ +typedef struct WASMFuncType { +#if WASM_ENABLE_GC != 0 + WASMType base_type; +#endif + + uint16 param_count; + uint16 result_count; + uint16 param_cell_num; + uint16 ret_cell_num; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* Code block to call llvm jit functions of this + kind of function type from fast jit jitted code */ + void *call_to_llvm_jit_from_fast_jit; +#endif + +#if WASM_ENABLE_GC != 0 + uint16 ref_type_map_count; + WASMRefTypeMap *ref_type_maps; + WASMRefTypeMap *result_ref_type_maps; +#else + uint16 ref_count; +#endif + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + /* Quick AOT/JIT entry of this func type */ + void *quick_aot_entry; +#endif + + /* types of params and results, only store the first byte + * of the type, if it cannot be described with one byte, + * then the full type info is stored in ref_type_maps */ uint8 types[1]; -} WASMType; +} WASMFuncType; -typedef struct WASMTable { +#if WASM_ENABLE_GC != 0 +typedef struct WASMStructFieldType { + uint16 field_flags; + uint8 field_type; + uint8 field_size; + uint32 field_offset; +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 + /* + * The field size and field offset of a wasm struct may vary + * in 32-bit target and 64-bit target, e.g., the size of a + * GC reference is 4 bytes in the former and 8 bytes in the + * latter, the AOT compiler needs to use the correct field + * offset according to the target info. + */ + uint8 field_size_64bit; + uint8 field_size_32bit; + uint32 field_offset_64bit; + uint32 field_offset_32bit; +#endif +} WASMStructFieldType; + +typedef struct WASMStructType { + WASMType base_type; + + /* total size of this struct object */ + uint32 total_size; + uint16 field_count; + + uint16 ref_type_map_count; + WASMRefTypeMap *ref_type_maps; + + /* Offsets of reference fields that need to be traced during GC. + The first element of the table is the number of such offsets. */ + uint16 *reference_table; + + /* Field info, note that fields[i]->field_type only stores + * the first byte of the field type, if it cannot be described + * with one byte, then the full field type info is stored in + * ref_type_maps */ + WASMStructFieldType fields[1]; +} WASMStructType; + +typedef struct WASMArrayType { + WASMType base_type; + + uint16 elem_flags; uint8 elem_type; - uint32 flags; + /* The full elem type info if the elem type cannot be + described with one byte */ + WASMRefType *elem_ref_type; +} WASMArrayType; + +#if WASM_ENABLE_STRINGREF != 0 +/* stringref representation, we define it as a void * pointer here, the + * stringref implementation can use any structure */ +/* + WasmGC heap + +-----------------------+ + | | + | stringref | + | +----------+ | external string representation + | | host_ptr |--------o------+----->+------------+ + | +----------+ | | | | + | | | +------------+ + | stringview_wtf8/16 | | + | +----------+ | | + | | host_ptr |--------o------+ + | +----------+ | | + | | | + | stringview_iter | | + | +----------+ | | + | | host_ptr |--------o------+ + | +----------+ | + | | pos | | + | +----------+ | + | | + +-----------------------+ +*/ +typedef void *WASMString; + +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ +#endif /* end of WASM_ENABLE_GC != 0 */ + +typedef struct WASMTableType { + uint8 elem_type; + /** + * 0: no max size and not shared + * 1: has max size + * 2: shared + * 4: table64 + */ + uint8 flags; + bool possible_grow; uint32 init_size; /* specified if (flags & 1), else it is 0x10000 */ uint32 max_size; +#if WASM_ENABLE_GC != 0 + WASMRefType *elem_ref_type; +#endif +} WASMTableType; + +typedef struct WASMTable { + WASMTableType table_type; +#if WASM_ENABLE_GC != 0 + /* init expr for the whole table */ + InitializerExpression init_expr; +#endif } WASMTable; +#if WASM_ENABLE_MEMORY64 != 0 +typedef uint64 mem_offset_t; +#define PR_MEM_OFFSET PRIu64 +#else +typedef uint32 mem_offset_t; +#define PR_MEM_OFFSET PRIu32 +#endif +typedef mem_offset_t tbl_elem_idx_t; + typedef struct WASMMemory { uint32 flags; uint32 num_bytes_per_page; uint32 init_page_count; uint32 max_page_count; } WASMMemory; +#ifndef WASM_MEMORY_T_DEFINED +#define WASM_MEMORY_T_DEFINED +typedef struct WASMMemory WASMMemoryType; +#endif typedef struct WASMTableImport { char *module_name; char *field_name; - uint8 elem_type; - uint32 flags; - uint32 init_size; - /* specified if (flags & 1), else it is 0x10000 */ - uint32 max_size; + WASMTableType table_type; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMTable *import_table_linked; +#endif } WASMTableImport; typedef struct WASMMemoryImport { char *module_name; char *field_name; - uint32 flags; - uint32 num_bytes_per_page; - uint32 init_page_count; - uint32 max_page_count; + WASMMemoryType mem_type; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMMemory *import_memory_linked; +#endif } WASMMemoryImport; typedef struct WASMFunctionImport { char *module_name; char *field_name; /* function type */ - WASMType *func_type; - /* function pointer after linked */ + WASMFuncType *func_type; + /* native function pointer after linked */ void *func_ptr_linked; + /* signature from registered native symbols */ + const char *signature; + /* attachment */ + void *attachment; +#if WASM_ENABLE_GC != 0 + /* the type index of this function's func_type */ + uint32 type_idx; +#endif + bool call_conv_raw; + bool call_conv_wasm_c_api; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMFunction *import_func_linked; +#endif } WASMFunctionImport; -typedef struct WASMGlobalImport { +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTagImport { char *module_name; char *field_name; - uint8 type; + uint8 attribute; /* the type of the tag (numerical) */ + uint32 type; /* the type of the catch function (numerical)*/ + WASMFuncType *tag_type; + void *tag_ptr_linked; + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* imported tag pointer after linked */ + WASMModule *import_module; + WASMTag *import_tag_linked; + uint32 import_tag_index_linked; +#endif +} WASMTagImport; +#endif + +typedef struct WASMGlobalType { + uint8 val_type; bool is_mutable; +} WASMGlobalType; + +typedef struct WASMGlobalImport { + char *module_name; + char *field_name; + WASMGlobalType type; + bool is_linked; /* global data after linked */ WASMValue global_data_linked; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + /* imported function pointer after linked */ + /* TODO: remove if not needed */ + WASMModule *import_module; + WASMGlobal *import_global_linked; +#endif +#if WASM_ENABLE_FAST_JIT != 0 + /* The data offset of current global in global data */ + uint32 data_offset; +#endif } WASMGlobalImport; typedef struct WASMImport { @@ -154,6 +670,9 @@ typedef struct WASMImport { WASMFunctionImport function; WASMTableImport table; WASMMemoryImport memory; +#if WASM_ENABLE_TAGS != 0 + WASMTagImport tag; +#endif WASMGlobalImport global; struct { char *module_name; @@ -162,11 +681,66 @@ typedef struct WASMImport { } u; } WASMImport; -typedef struct WASMFunction { +#if WASM_ENABLE_EXCE_HANDLING != 0 && WASM_ENABLE_FAST_INTERP != 0 +/* One typed `catch N` clause inside a single try-region. The handler_pc + * points at the first opcode of the catch body in the rewritten fast- + * interp IR; the loader patches it in pass 2 of the preprocess pass. */ +typedef struct WASMFastEHCatch { + uint32 tag_index; + uint8 *handler_pc; + /* Tag-with-params payload routing (same-function dispatch only). + * When this catch matches, the throw walker copies `param_cell_num` + * 32-bit cells from the throw site's *source* slots (encoded as + * `int16` immediates after the THROW opcode in the rewritten IR) + * into these *destination* slots in the catch body's `frame_lp`, + * then sets `frame_ip = handler_pc`. The destination slots are + * allocated by the CATCH loader at preprocess time, mirroring how + * block-with-params allocate fresh `dynamic_offset` slots via + * `PUSH_OFFSET_TYPE`. NULL iff `param_cell_num == 0` (the typical + * tag-without-params shape, e.g. Porffor's empty-payload tags). + * + * Cross-function dispatch (caller's catch fires for a callee's + * throw) does NOT copy the payload: the callee's source slots + * sit in a frame that's about to be torn down by return_func. + * That gap is documented as an ignored integration test — + * `cross_function_tag_with_params` in + * crates/benchmark-core/tests/eh_correctness.rs. */ + uint32 param_cell_num; + int16 *param_dst_offsets; +} WASMFastEHCatch; + +/* One entry per same-function try-region, indexed by the uint32 immediate + * emitted after the rewritten TRY opcode. Allocated once per function at + * load time, sized by `func->exception_handler_count`. At runtime the + * dispatch loop carries one stack-allocated handle per *active* try- + * region (see frame->eh_stack); hot ops (CALL / LOAD / STORE) never + * touch this table. */ +typedef struct WASMFastEHEntry { + uint32 catch_count; + WASMFastEHCatch *catches; /* may be NULL when catch_count == 0 */ + uint8 *catch_all_pc; /* NULL if no `catch_all` clause */ + /* UINT32_MAX iff the try-region closes with `end`; otherwise the + * LEB depth from `delegate N`. */ + uint32 delegate_target_depth; + /* Rewritten-IR pc of the op immediately after the try-region's `end` + * (or `delegate`). CATCH / CATCH_ALL handlers branch here when their + * body completes; the loader patches it when the `end` is seen. */ + uint8 *end_of_region_pc; +} WASMFastEHEntry; +#endif /* WASM_ENABLE_EXCE_HANDLING && WASM_ENABLE_FAST_INTERP */ + +struct WASMFunction { +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + char *field_name; +#endif /* the type of function */ - WASMType *func_type; + WASMFuncType *func_type; uint32 local_count; uint8 *local_types; +#if WASM_ENABLE_GC != 0 + uint16 local_ref_type_map_count; + WASMRefTypeMap *local_ref_type_maps; +#endif /* cell num of parameters */ uint16 param_cell_num; @@ -174,26 +748,111 @@ typedef struct WASMFunction { uint16 ret_cell_num; /* cell num of local variables */ uint16 local_cell_num; - /* offset of each local, including function paramameters + /* offset of each local, including function parameters and local variables */ uint16 *local_offsets; uint32 max_stack_cell_num; uint32 max_block_num; + uint32 code_size; + uint8 *code; +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 code_compiled_size; + uint8 *code_compiled; + uint8 *consts; + uint32 const_cell_num; +#endif + +#if WASM_ENABLE_GC != 0 + /* the type index of this function's func_type */ + uint32 type_idx; +#endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* Number of `try` opcodes in this function. Populated by the loader + * during the preprocess pass (classic-interp uses this to size the + * runtime handler-pointer array stored on the value stack; fast- + * interp uses it to size `exception_handlers[]` below). */ + uint32 exception_handler_count; +#if WASM_ENABLE_FAST_INTERP != 0 + /* Per-function table of try-regions in source order, length + * `exception_handler_count`. Allocated and populated in pass 2 of + * the fast-interp preprocess pass; the uint32 immediate emitted + * after the rewritten TRY opcode is the index into this array. + * NULL iff `exception_handler_count == 0`. */ + WASMFastEHEntry *exception_handlers; +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 /* Whether function has opcode memory.grow */ bool has_op_memory_grow; - /* Whether function has opcode call or - call_indirect */ + /* Whether function has opcode call or call_indirect */ bool has_op_func_call; - uint32 code_size; - uint8 *code; -} WASMFunction; +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + /* Whether function has memory operation opcodes */ + bool has_memory_operations; + /* Whether function has opcode call_indirect */ + bool has_op_call_indirect; + /* Whether function has opcode set_global_aux_stack */ + bool has_op_set_global_aux_stack; +#endif -typedef struct WASMGlobal { - uint8 type; - bool is_mutable; +#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 + +#if WASM_ENABLE_BRANCH_HINTS != 0 + uint8 *code_body_begin; +#endif +}; + +#if WASM_ENABLE_TAGS != 0 +struct WASMTag { + uint8 attribute; /* the attribute property of the tag (expected to be 0) */ + uint32 type; /* the type of the tag (expected valid inden in type table) */ + WASMFuncType *tag_type; +}; +#endif + +#if WASM_ENABLE_BRANCH_HINTS != 0 +enum WASMCompilationHintType { + DUMMY = 0, + WASM_COMPILATION_BRANCH_HINT = 0, +}; +struct WASMCompilationHint { + struct WASMCompilationHint *next; + enum WASMCompilationHintType type; +}; +struct WASMCompilationHintBranchHint { + struct WASMCompilationHint *next; + enum WASMCompilationHintType type; + uint32 offset; + bool is_likely; +}; +#endif + +struct WASMGlobal { + WASMGlobalType type; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; +#endif InitializerExpression init_expr; -} WASMGlobal; +#if WASM_ENABLE_FAST_JIT != 0 + /* The data offset of current global in global data */ + uint32 data_offset; +#endif +}; typedef struct WASMExport { char *name; @@ -202,17 +861,29 @@ typedef struct WASMExport { } WASMExport; typedef struct WASMTableSeg { + /* 0 to 7 */ + uint32 mode; + /* funcref or externref, elemkind will be considered as funcref */ + uint32 elem_type; +#if WASM_ENABLE_GC != 0 + WASMRefType *elem_ref_type; +#endif + /* optional, only for active */ uint32 table_index; InitializerExpression base_offset; - uint32 function_count; - uint32 *func_indexes; + uint32 value_count; + InitializerExpression *init_values; } WASMTableSeg; typedef struct WASMDataSeg { uint32 memory_index; InitializerExpression base_offset; uint32 data_length; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive; +#endif uint8 *data; + bool is_data_cloned; } WASMDataSeg; typedef struct BlockAddr { @@ -221,9 +892,6 @@ typedef struct BlockAddr { uint8 *end_addr; } BlockAddr; -#define BLOCK_ADDR_CACHE_SIZE 64 -#define BLOCK_ADDR_CONFLICT_SIZE 4 - #if WASM_ENABLE_LIBC_WASI != 0 typedef struct WASIArguments { const char **dir_list; @@ -232,8 +900,15 @@ typedef struct WASIArguments { uint32 map_dir_count; const char **env; uint32 env_count; - const char **argv; + /* in CIDR notation */ + const char **addr_pool; + uint32 addr_count; + const char **ns_lookup_pool; + uint32 ns_lookup_count; + char **argv; uint32 argc; + os_raw_file_handle stdio[3]; + bool set_by_user; } WASIArguments; #endif @@ -242,7 +917,52 @@ typedef struct StringNode { char *str; } StringNode, *StringList; -typedef struct WASMModule { +typedef struct BrTableCache { + struct BrTableCache *next; + /* Address of br_table opcode */ + uint8 *br_table_op_addr; + uint32 br_count; + uint32 br_depths[1]; +} BrTableCache; + +#if WASM_ENABLE_DEBUG_INTERP != 0 +typedef struct WASMFastOPCodeNode { + struct WASMFastOPCodeNode *next; + uint64 offset; + uint8 orig_op; +} WASMFastOPCodeNode; +#endif + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +typedef struct WASMCustomSection { + struct WASMCustomSection *next; + /* Start address of the section name */ + char *name_addr; + /* Length of the section name decoded from leb */ + uint32 name_len; + /* Start address of the content (name len and name skipped) */ + uint8 *content_addr; + uint32 content_len; +} WASMCustomSection; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 +struct AOTCompData; +struct AOTCompContext; + +/* Orc JIT thread arguments */ +typedef struct OrcJitThreadArg { +#if WASM_ENABLE_JIT != 0 + struct AOTCompContext *comp_ctx; +#endif + struct WASMModule *module; + uint32 group_idx; +} OrcJitThreadArg; +#endif + +struct WASMModuleInstance; + +struct WASMModule { /* Module type, for module loaded from WASM bytecode binary, this field is Wasm_Module_Bytecode; for module loaded from AOT file, this field is @@ -250,24 +970,48 @@ typedef struct WASMModule { AOTModule structure. */ uint32 module_type; + /* the package version read from the WASM file */ + uint32 package_version; + uint32 type_count; uint32 import_count; uint32 function_count; uint32 table_count; uint32 memory_count; +#if WASM_ENABLE_TAGS != 0 + uint32 tag_count; +#endif uint32 global_count; uint32 export_count; uint32 table_seg_count; + /* data seg count read from data segment section */ uint32 data_seg_count; +#if WASM_ENABLE_BULK_MEMORY != 0 + /* data count read from datacount section */ + uint32 data_seg_count1; +#endif +#if WASM_ENABLE_GC != 0 +#if WASM_ENABLE_STRINGREF != 0 + uint32 string_literal_count; + uint32 *string_literal_lengths; + const uint8 **string_literal_ptrs; +#endif +#endif uint32 import_function_count; uint32 import_table_count; uint32 import_memory_count; +#if WASM_ENABLE_TAGS != 0 + uint32 import_tag_count; +#endif uint32 import_global_count; WASMImport *import_functions; WASMImport *import_tables; WASMImport *import_memories; +#if WASM_ENABLE_TAGS != 0 + WASMImport *import_tags; +#endif WASMImport *import_globals; WASMType **types; @@ -275,44 +1019,239 @@ typedef struct WASMModule { WASMFunction **functions; WASMTable *tables; WASMMemory *memories; +#if WASM_ENABLE_TAGS != 0 + WASMTag **tags; +#endif WASMGlobal *globals; WASMExport *exports; WASMTableSeg *table_segments; WASMDataSeg **data_segments; uint32 start_function; - /* __data_end global exported by llvm */ - uint32 llvm_aux_data_end; - /* auxiliary stack bottom, or __heap_base global exported by llvm */ - uint32 llvm_aux_stack_bottom; - /* auxiliary stack size */ - uint32 llvm_aux_stack_size; - /* the index of a global exported by llvm, which is - auxiliary stack top pointer */ - uint32 llvm_aux_stack_global_index; - - /* Whether there is possible memory grow, e.g. - memory.grow opcode or call enlargeMemory */ + /* total global variable size */ + uint32 global_data_size; + + /* the index of auxiliary __data_end global, + -1 means unexported */ + uint32 aux_data_end_global_index; + /* auxiliary __data_end exported by wasm app */ + uint64 aux_data_end; + + /* the index of auxiliary __heap_base global, + -1 means unexported */ + uint32 aux_heap_base_global_index; + /* auxiliary __heap_base exported by wasm app */ + uint64 aux_heap_base; + + /* the index of auxiliary stack top global, + -1 means unexported */ + uint32 aux_stack_top_global_index; + /* auxiliary stack bottom resolved */ + uint64 aux_stack_bottom; + /* auxiliary stack size resolved */ + uint32 aux_stack_size; + + /* the index of malloc/free function, + -1 means unexported */ + uint32 malloc_function; + uint32 free_function; + + /* the index of __retain function, + -1 means unexported */ + uint32 retain_function; + + /* Whether there is possible memory grow, e.g. memory.grow opcode */ bool possible_memory_grow; StringList const_str_list; - - BlockAddr block_addr_cache[BLOCK_ADDR_CACHE_SIZE][BLOCK_ADDR_CONFLICT_SIZE]; +#if WASM_ENABLE_FAST_INTERP == 0 + bh_list br_table_cache_list_head; + bh_list *br_table_cache_list; +#endif #if WASM_ENABLE_LIBC_WASI != 0 WASIArguments wasi_args; - bool is_wasi_module; + bool import_wasi_api; +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* TODO: add mutex for mutli-thread? */ + bh_list import_module_list_head; + bh_list *import_module_list; +#endif + +#if WASM_ENABLE_GC != 0 + /* Ref types hash set */ + HashMap *ref_type_set; + struct WASMRttType **rtt_types; + korp_mutex rtt_type_lock; +#if WASM_ENABLE_STRINGREF != 0 + /* special rtts for stringref types + - stringref + - stringview_wtf8 + - stringview_wtf16 + - stringview_iter + */ + struct WASMRttType *stringref_rtts[4]; +#endif +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_DEBUG_AOT != 0 + bh_list fast_opcode_list; + uint8 *buf_code; + uint64 buf_code_size; +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_FAST_JIT != 0 \ + || WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + uint8 *load_addr; + uint64 load_size; +#endif + +#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. + * + * 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 + * in the instance. + */ + struct WASMModuleInstance *instance_list; + korp_mutex instance_list_lock; +#endif + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + const uint8 *name_section_buf; + const uint8 *name_section_buf_end; +#endif + +#if WASM_ENABLE_BRANCH_HINTS != 0 + struct WASMCompilationHint **function_hints; +#endif + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + WASMCustomSection *custom_section_list; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 + /** + * 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]; + bool fast_jit_thread_locks_inited[WASM_ORC_JIT_BACKEND_THREAD_NUM]; +#endif + +#if WASM_ENABLE_JIT != 0 + struct AOTCompData *comp_data; + struct AOTCompContext *comp_ctx; + /** + * func pointers of LLVM JITed (un-imported) functions + * for non Multi-Tier JIT mode: + * each pointer is set to the looked up 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; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* backend compilation threads */ + korp_tid orcjit_threads[WASM_ORC_JIT_BACKEND_THREAD_NUM]; + /* backend thread arguments */ + OrcJitThreadArg orcjit_thread_args[WASM_ORC_JIT_BACKEND_THREAD_NUM]; + /* whether to stop the compilation of backend threads */ + bool orcjit_stop_compiling; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* wait lock/cond for the synchronization of + the llvm jit initialization */ + korp_mutex tierup_wait_lock; + korp_cond tierup_wait_cond; + bool tierup_wait_lock_inited; + 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; + /* The count of groups which finish compiling the fast jit + functions in that group */ + uint32 fast_jit_ready_groups; +#endif + +#if WASM_ENABLE_WAMR_COMPILER != 0 + bool is_simd_used; + bool is_ref_types_used; + bool is_bulk_memory_used; #endif -} WASMModule; + + /* user defined name */ + char *name; + + /* Whether the underlying wasm binary buffer can be freed */ + bool is_binary_freeable; +}; + +typedef struct BlockType { + /* Block type may be expressed in one of two forms: + * either by the type of the single return value or + * by a type index of module. + */ + union { + struct { + uint8 type; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap ref_type_map; +#endif + } value_type; + WASMFuncType *type; + } u; + bool is_value_type; +} BlockType; typedef struct WASMBranchBlock { - uint8 block_type; - uint8 return_type; + uint8 *begin_addr; uint8 *target_addr; uint32 *frame_sp; + uint32 cell_num; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* in exception handling, label_type needs to be stored to lookup exception + * handlers */ + uint8 label_type; +#endif } WASMBranchBlock; -/* Execution environment, e.g. stack info */ /** * Align an unsigned value on a alignment boundary. * @@ -322,12 +1261,45 @@ typedef struct WASMBranchBlock { * @return the aligned value */ inline static unsigned -align_uint (unsigned v, unsigned b) +align_uint(unsigned v, unsigned b) { unsigned m = b - 1; return (v + m) & ~m; } +/** + * Align an 64 bit unsigned value on a alignment boundary. + * + * @param v the value to be aligned + * @param b the alignment boundary (2, 4, 8, ...) + * + * @return the aligned value + */ +inline static uint64 +align_uint64(uint64 v, uint64 b) +{ + uint64 m = b - 1; + return (v + m) & ~m; +} + +/** + * Check whether a piece of data is out of range + * + * @param offset the offset that the data starts + * @param len the length of the data + * @param max_size the maximum size of the data range + * + * @return true if out of range, false otherwise + */ +inline static bool +offset_len_out_of_bounds(uint32 offset, uint32 len, uint32 max_size) +{ + if (offset + len < offset /* integer overflow */ + || offset + len > max_size) + return true; + return false; +} + /** * Return the hash value of c string. */ @@ -335,7 +1307,7 @@ inline static uint32 wasm_string_hash(const char *str) { unsigned h = (unsigned)strlen(str); - const uint8 *p = (uint8*)str; + const uint8 *p = (uint8 *)str; const uint8 *end = p + h; while (p != end) @@ -353,72 +1325,250 @@ wasm_string_equal(const char *s1, const char *s2) } /** - * Return the byte size of value type. + * Return the byte size of value type with specific pointer size. * + * Note: Please use wasm_value_type_size for interpreter, only aot compiler + * can use this API directly to calculate type size for different target */ inline static uint32 -wasm_value_type_size(uint8 value_type) +wasm_value_type_size_internal(uint8 value_type, uint8 pointer_size) { - switch (value_type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - return sizeof(int32); - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - return sizeof(int64); - default: - bh_assert(0); + if (value_type == VALUE_TYPE_VOID) + return 0; + else if (value_type == VALUE_TYPE_I32 || value_type == VALUE_TYPE_F32 + || value_type == VALUE_TYPE_ANY) + return sizeof(int32); + else if (value_type == VALUE_TYPE_I64 || value_type == VALUE_TYPE_F64) + return sizeof(int64); +#if WASM_ENABLE_SIMD != 0 + else if (value_type == VALUE_TYPE_V128) + return sizeof(int64) * 2; +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + else if (value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF) + return sizeof(uint32); +#elif WASM_ENABLE_GC != 0 + else if ((value_type >= (uint8)REF_TYPE_ARRAYREF /* 0x6A */ + && value_type <= (uint8)REF_TYPE_NULLFUNCREF) /* 0x73 */ + || (value_type >= (uint8)REF_TYPE_HT_NULLABLE /* 0x63 */ + && value_type <= (uint8)REF_TYPE_HT_NON_NULLABLE) /* 0x64 */ +#if WASM_ENABLE_STRINGREF != 0 + || (value_type >= (uint8)REF_TYPE_STRINGVIEWWTF8 /* 0x66 */ + && value_type <= (uint8)REF_TYPE_STRINGREF) /* 0x67 */ + || (value_type >= (uint8)REF_TYPE_STRINGVIEWITER /* 0x61 */ + && value_type <= (uint8)REF_TYPE_STRINGVIEWWTF16) /* 0x62 */ +#endif + ) + return pointer_size; + else if (value_type == PACKED_TYPE_I8) + return sizeof(int8); + else if (value_type == PACKED_TYPE_I16) + return sizeof(int16); +#endif + else { + bh_assert(0 && "Unknown value type. It should be handled ahead."); } +#if WASM_ENABLE_GC == 0 + (void)pointer_size; +#endif return 0; } +/** + * Return the cell num of value type with specific pointer size. + * + * Note: Please use wasm_value_type_cell_num for interpreter, only aot compiler + * can use this API directly to calculate type cell num for different target + */ inline static uint16 -wasm_value_type_cell_num(uint8 value_type) +wasm_value_type_cell_num_internal(uint8 value_type, uint8 pointer_size) { - switch (value_type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - return 1; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - return 2; - default: - bh_assert(0); - } - return 0; + return wasm_value_type_size_internal(value_type, pointer_size) / 4; +} + +/** + * Return the byte size of value type. + */ +inline static uint32 +wasm_value_type_size(uint8 value_type) +{ + return wasm_value_type_size_internal(value_type, sizeof(uintptr_t)); } inline static uint16 +wasm_value_type_cell_num(uint8 value_type) +{ + return wasm_value_type_size(value_type) / 4; +} + +inline static uint32 wasm_get_cell_num(const uint8 *types, uint32 type_count) { uint32 cell_num = 0; uint32 i; for (i = 0; i < type_count; i++) cell_num += wasm_value_type_cell_num(types[i]); - return (uint16)cell_num; + return cell_num; } +#if WASM_ENABLE_REF_TYPES != 0 inline static uint16 -wasm_type_param_cell_num(const WASMType *type) +wasm_value_type_cell_num_outside(uint8 value_type) { - return wasm_get_cell_num(type->types, type->param_count); + if (VALUE_TYPE_EXTERNREF == value_type) { + return sizeof(uintptr_t) / sizeof(uint32); + } + else { + return wasm_value_type_cell_num(value_type); + } } +#endif -inline static uint16 -wasm_type_return_cell_num(const WASMType *type) +#if WASM_ENABLE_GC == 0 +inline static bool +wasm_type_equal(const WASMType *type1, const WASMType *type2, + const WASMTypePtr *types, uint32 type_count) { - return wasm_get_cell_num(type->types + type->param_count, - type->result_count); + const WASMFuncType *func_type1 = (const WASMFuncType *)type1; + const WASMFuncType *func_type2 = (const WASMFuncType *)type2; + + if (type1 == type2) { + return true; + } + + return (func_type1->param_count == func_type2->param_count + && func_type1->result_count == func_type2->result_count + && memcmp( + func_type1->types, func_type2->types, + (uint32)(func_type1->param_count + func_type1->result_count)) + == 0) + ? true + : false; + (void)types; + (void)type_count; } +#else +/* implemented in gc_type.c */ +bool +wasm_type_equal(const WASMType *type1, const WASMType *type2, + const WASMTypePtr *types, uint32 type_count); +#endif -inline static bool -wasm_type_equal(const WASMType *type1, const WASMType *type2) +inline static uint32 +wasm_get_smallest_type_idx(const WASMTypePtr *types, uint32 type_count, + uint32 cur_type_idx) +{ + uint32 i; + + for (i = 0; i < cur_type_idx; i++) { + if (wasm_type_equal(types[cur_type_idx], types[i], types, type_count)) + return i; + } + return cur_type_idx; +} + +#if WASM_ENABLE_GC == 0 +static inline uint32 +block_type_get_param_types(BlockType *block_type, uint8 **p_param_types) +#else +static inline uint32 +block_type_get_param_types(BlockType *block_type, uint8 **p_param_types, + WASMRefTypeMap **p_param_reftype_maps, + uint32 *p_param_reftype_map_count) +#endif +{ + uint32 param_count = 0; + if (!block_type->is_value_type) { + WASMFuncType *func_type = block_type->u.type; + *p_param_types = func_type->types; + param_count = func_type->param_count; +#if WASM_ENABLE_GC != 0 + *p_param_reftype_maps = func_type->ref_type_maps; + *p_param_reftype_map_count = (uint32)(func_type->result_ref_type_maps + - func_type->ref_type_maps); +#endif + } + else { + *p_param_types = NULL; + param_count = 0; +#if WASM_ENABLE_GC != 0 + *p_param_reftype_maps = NULL; + *p_param_reftype_map_count = 0; +#endif + } + + return param_count; +} + +#if WASM_ENABLE_GC == 0 +static inline uint32 +block_type_get_result_types(BlockType *block_type, uint8 **p_result_types) +#else +static inline uint32 +block_type_get_result_types(BlockType *block_type, uint8 **p_result_types, + WASMRefTypeMap **p_result_reftype_maps, + uint32 *p_result_reftype_map_count) +#endif { - return (type1->param_count == type2->param_count - && type1->result_count == type2->result_count - && memcmp(type1->types, type2->types, - type1->param_count + type1->result_count) == 0) - ? true : false; + uint32 result_count = 0; + uint8 *result_types = NULL; +#if WASM_ENABLE_GC != 0 + uint8 type; + uint32 result_reftype_map_count = 0; + WASMRefTypeMap *result_reftype_maps = NULL; +#endif + + if (block_type->is_value_type) { + if (block_type->u.value_type.type != VALUE_TYPE_VOID) { + result_types = &block_type->u.value_type.type; + result_count = 1; +#if WASM_ENABLE_GC != 0 + type = block_type->u.value_type.type; + if (type == (uint8)REF_TYPE_HT_NULLABLE + || type == (uint8)REF_TYPE_HT_NON_NULLABLE) { + result_reftype_maps = &block_type->u.value_type.ref_type_map; + result_reftype_map_count = 1; + } +#endif + } + } + else { + WASMFuncType *func_type = block_type->u.type; + result_types = func_type->types + func_type->param_count; + result_count = func_type->result_count; +#if WASM_ENABLE_GC != 0 + result_reftype_maps = func_type->result_ref_type_maps; + result_reftype_map_count = (uint32)(func_type->ref_type_map_count + - (func_type->result_ref_type_maps + - func_type->ref_type_maps)); +#endif + } + *p_result_types = result_types; +#if WASM_ENABLE_GC != 0 + *p_result_reftype_maps = result_reftype_maps; + *p_result_reftype_map_count = result_reftype_map_count; +#endif + return result_count; +} + +static inline uint32 +block_type_get_arity(const BlockType *block_type, uint8 label_type) +{ + if (label_type == LABEL_TYPE_LOOP) { + if (block_type->is_value_type) + return 0; + else + return block_type->u.type->param_count; + } + else { + if (block_type->is_value_type) { + return block_type->u.value_type.type != VALUE_TYPE_VOID ? 1 : 0; + } + else + return block_type->u.type->result_count; + } + return 0; } #ifdef __cplusplus @@ -426,4 +1576,3 @@ wasm_type_equal(const WASMType *type1, const WASMType *type2) #endif #endif /* end of _WASM_H_ */ - diff --git a/core/iwasm/interpreter/wasm_interp.c b/core/iwasm/interpreter/wasm_interp.c deleted file mode 100644 index 8b1f5c8baa..0000000000 --- a/core/iwasm/interpreter/wasm_interp.c +++ /dev/null @@ -1,2399 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_interp.h" -#include "bh_memory.h" -#include "bh_log.h" -#include "wasm_runtime.h" -#include "wasm_opcode.h" -#include "wasm_loader.h" -#include "../common/wasm_exec_env.h" - -typedef int32 CellType_I32; -typedef int64 CellType_I64; -typedef float32 CellType_F32; -typedef float64 CellType_F64; - -#define BR_TABLE_TMP_BUF_LEN 32 - -/* 64-bit Memory accessors. */ -#if WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 -#define PUT_I64_TO_ADDR(addr, value) do { \ - *(int64*)(addr) = (int64)(value); \ - } while (0) -#define PUT_F64_TO_ADDR(addr, value) do { \ - *(float64*)(addr) = (float64)(value); \ - } while (0) - -#define GET_I64_FROM_ADDR(addr) (*(int64*)(addr)) -#define GET_F64_FROM_ADDR(addr) (*(float64*)(addr)) - -/* For STORE opcodes */ -#define STORE_I64 PUT_I64_TO_ADDR -#define STORE_U32(addr, value) do { \ - *(uint32*)(addr) = (uint32)(value); \ - } while (0) -#define STORE_U16(addr, value) do { \ - *(uint16*)(addr) = (uint16)(value); \ - } while (0) - -/* For LOAD opcodes */ -#define LOAD_I64(addr) (*(int64*)(addr)) -#define LOAD_F64(addr) (*(float64*)(addr)) -#define LOAD_I32(addr) (*(int32*)(addr)) -#define LOAD_U32(addr) (*(uint32*)(addr)) -#define LOAD_I16(addr) (*(int16*)(addr)) -#define LOAD_U16(addr) (*(uint16*)(addr)) - -#else /* WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 */ -#define PUT_I64_TO_ADDR(addr, value) do { \ - union { int64 val; uint32 parts[2]; } u; \ - u.val = (int64)(value); \ - (addr)[0] = u.parts[0]; \ - (addr)[1] = u.parts[1]; \ - } while (0) -#define PUT_F64_TO_ADDR(addr, value) do { \ - union { float64 val; uint32 parts[2]; } u; \ - u.val = (value); \ - (addr)[0] = u.parts[0]; \ - (addr)[1] = u.parts[1]; \ - } while (0) - -static inline int64 -GET_I64_FROM_ADDR(uint32 *addr) -{ - union { int64 val; uint32 parts[2]; } u; - u.parts[0] = addr[0]; - u.parts[1] = addr[1]; - return u.val; -} - -static inline float64 -GET_F64_FROM_ADDR (uint32 *addr) -{ - union { float64 val; uint32 parts[2]; } u; - u.parts[0] = addr[0]; - u.parts[1] = addr[1]; - return u.val; -} - -/* For STORE opcodes */ -#define STORE_I64(addr, value) do { \ - uintptr_t addr1 = (uintptr_t)(addr); \ - union { int64 val; uint32 u32[2]; \ - uint16 u16[4]; uint8 u8[8]; } u; \ - if ((addr1 & (uintptr_t)7) == 0) \ - *(int64*)(addr) = (int64)(value); \ - else { \ - u.val = (int64)(value); \ - if ((addr1 & (uintptr_t)3) == 0) { \ - ((uint32*)(addr))[0] = u.u32[0]; \ - ((uint32*)(addr))[1] = u.u32[1]; \ - } \ - else if ((addr1 & (uintptr_t)1) == 0) { \ - ((uint16*)(addr))[0] = u.u16[0]; \ - ((uint16*)(addr))[1] = u.u16[1]; \ - ((uint16*)(addr))[2] = u.u16[2]; \ - ((uint16*)(addr))[3] = u.u16[3]; \ - } \ - else { \ - int32 t; \ - for (t = 0; t < 8; t++) \ - ((uint8*)(addr))[t] = u.u8[t]; \ - } \ - } \ - } while (0) - -#define STORE_U32(addr, value) do { \ - uintptr_t addr1 = (uintptr_t)(addr); \ - union { uint32 val; \ - uint16 u16[2]; uint8 u8[4]; } u; \ - if ((addr1 & (uintptr_t)3) == 0) \ - *(uint32*)(addr) = (uint32)(value); \ - else { \ - u.val = (uint32)(value); \ - if ((addr1 & (uintptr_t)1) == 0) { \ - ((uint16*)(addr))[0] = u.u16[0]; \ - ((uint16*)(addr))[1] = u.u16[1]; \ - } \ - else { \ - ((uint8*)(addr))[0] = u.u8[0]; \ - ((uint8*)(addr))[1] = u.u8[1]; \ - ((uint8*)(addr))[2] = u.u8[2]; \ - ((uint8*)(addr))[3] = u.u8[3]; \ - } \ - } \ - } while (0) - -#define STORE_U16(addr, value) do { \ - union { uint16 val; uint8 u8[2]; } u; \ - u.val = (uint16)(value); \ - ((uint8*)(addr))[0] = u.u8[0]; \ - ((uint8*)(addr))[1] = u.u8[1]; \ - } while (0) - -/* For LOAD opcodes */ -static inline int64 -LOAD_I64(void *addr) -{ - uintptr_t addr1 = (uintptr_t)addr; - union { int64 val; uint32 u32[2]; - uint16 u16[4]; uint8 u8[8]; } u; - if ((addr1 & (uintptr_t)7) == 0) - return *(int64*)addr; - - if ((addr1 & (uintptr_t)3) == 0) { - u.u32[0] = ((uint32*)addr)[0]; - u.u32[1] = ((uint32*)addr)[1]; - } - else if ((addr1 & (uintptr_t)1) == 0) { - u.u16[0] = ((uint16*)addr)[0]; - u.u16[1] = ((uint16*)addr)[1]; - u.u16[2] = ((uint16*)addr)[2]; - u.u16[3] = ((uint16*)addr)[3]; - } - else { - int32 t; - for (t = 0; t < 8; t++) - u.u8[t] = ((uint8*)addr)[t]; - } - return u.val; -} - -static inline float64 -LOAD_F64(void *addr) -{ - uintptr_t addr1 = (uintptr_t)addr; - union { float64 val; uint32 u32[2]; - uint16 u16[4]; uint8 u8[8]; } u; - if ((addr1 & (uintptr_t)7) == 0) - return *(float64*)addr; - - if ((addr1 & (uintptr_t)3) == 0) { - u.u32[0] = ((uint32*)addr)[0]; - u.u32[1] = ((uint32*)addr)[1]; - } - else if ((addr1 & (uintptr_t)1) == 0) { - u.u16[0] = ((uint16*)addr)[0]; - u.u16[1] = ((uint16*)addr)[1]; - u.u16[2] = ((uint16*)addr)[2]; - u.u16[3] = ((uint16*)addr)[3]; - } - else { - int32 t; - for (t = 0; t < 8; t++) - u.u8[t] = ((uint8*)addr)[t]; - } - return u.val; -} - -static inline int32 -LOAD_I32(void *addr) -{ - uintptr_t addr1 = (uintptr_t)addr; - union { int32 val; uint16 u16[2]; uint8 u8[4]; } u; - if ((addr1 & (uintptr_t)3) == 0) - return *(int32*)addr; - - if ((addr1 & (uintptr_t)1) == 0) { - u.u16[0] = ((uint16*)addr)[0]; - u.u16[1] = ((uint16*)addr)[1]; - } - else { - u.u8[0] = ((uint8*)addr)[0]; - u.u8[1] = ((uint8*)addr)[1]; - u.u8[2] = ((uint8*)addr)[2]; - u.u8[3] = ((uint8*)addr)[3]; - } - return u.val; -} - -static inline int16 -LOAD_I16(void *addr) -{ - union { int16 val; uint8 u8[2]; } u; - u.u8[0] = ((uint8*)addr)[0]; - u.u8[1] = ((uint8*)addr)[1]; - return u.val; -} - -#define LOAD_U32(addr) ((uint32)LOAD_I32(addr)) -#define LOAD_U16(addr) ((uint16)LOAD_I16(addr)) - -#endif /* WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 */ - -#define CHECK_MEMORY_OVERFLOW() do { \ - uint32 offset1 = offset + addr; \ - /* if (flags != 2) \ - LOG_VERBOSE("unaligned load/store in wasm interp, flag: %d.\n", flags); */\ - /* The WASM spec doesn't require that the dynamic address operand must be \ - unsigned, so we don't check whether integer overflow or not here. */ \ - /* if (offset1 < offset) \ - goto out_of_bounds; */ \ - if (offset1 < memory_data_size) { \ - /* If offset1 is in valid range, maddr must also be in valid range, \ - no need to check it again. */ \ - maddr = memory->memory_data + offset1; \ - if (maddr + LOAD_SIZE[opcode - WASM_OP_I32_LOAD] > memory->end_addr) \ - goto out_of_bounds; \ - } \ - else if (offset1 > heap_base_offset \ - && offset1 < heap_base_offset + heap_data_size) { \ - /* If offset1 is in valid range, maddr must also be in valid range, \ - no need to check it again. */ \ - maddr = memory->heap_data + offset1 - memory->heap_base_offset; \ - if (maddr + LOAD_SIZE[opcode - WASM_OP_I32_LOAD] > memory->heap_data_end) \ - goto out_of_bounds; \ - } \ - else \ - goto out_of_bounds; \ - } while (0) - -static inline uint32 -rotl32(uint32 n, uint32 c) -{ - const uint32 mask = (31); - c = c % 32; - c &= mask; - return (n<>( (-c)&mask )); -} - -static inline uint32 -rotr32(uint32 n, uint32 c) -{ - const uint32 mask = (31); - c = c % 32; - c &= mask; - return (n>>c) | (n<<( (-c)&mask )); -} - -static inline uint64 -rotl64(uint64 n, uint64 c) -{ - const uint64 mask = (63); - c = c % 64; - c &= mask; - return (n<>( (-c)&mask )); -} - -static inline uint64 -rotr64(uint64 n, uint64 c) -{ - const uint64 mask = (63); - c = c % 64; - c &= mask; - return (n>>c) | (n<<( (-c)&mask )); -} - -static inline double -wa_fmax(double a, double b) -{ - double c = fmax(a, b); - if (c==0 && a==b) - return signbit(a) ? b : a; - return c; -} - -static inline double -wa_fmin(double a, double b) -{ - double c = fmin(a, b); - if (c==0 && a==b) - return signbit(a) ? a : b; - return c; -} - -static inline uint32 -clz32(uint32 type) -{ - uint32 num = 0; - if (type == 0) - return 32; - while (!(type & 0x80000000)) { - num++; - type <<= 1; - } - return num; -} - -static inline uint32 -clz64(uint64 type) -{ - uint32 num = 0; - if (type == 0) - return 64; - while (!(type & 0x8000000000000000LL)) { - num++; - type <<= 1; - } - return num; -} - -static inline uint32 -ctz32(uint32 type) -{ - uint32 num = 0; - if (type == 0) - return 32; - while (!(type & 1)) { - num++; - type >>= 1; - } - return num; -} - -static inline uint32 -ctz64(uint64 type) -{ - uint32 num = 0; - if (type == 0) - return 64; - while (!(type & 1)) { - num++; - type >>= 1; - } - return num; -} - -static inline uint32 -popcount32(uint32 u) -{ - uint32 ret = 0; - while (u) { - u = (u & (u - 1)); - ret++; - } - return ret; -} - -static inline uint32 -popcount64(uint64 u) -{ - uint32 ret = 0; - while (u) { - u = (u & (u - 1)); - ret++; - } - return ret; -} - -static uint64 -read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) -{ - uint64 result = 0; - uint32 shift = 0; - uint32 bcnt = 0; - uint64 byte; - - while (true) { - byte = buf[*p_offset]; - *p_offset += 1; - result |= ((byte & 0x7f) << shift); - shift += 7; - if ((byte & 0x80) == 0) { - break; - } - bcnt += 1; - } - if (sign && (shift < maxbits) && (byte & 0x40)) { - /* Sign extend */ - result |= - ((uint64)1 << shift); - } - return result; -} - -#define PUSH_I32(value) do { \ - *(int32*)frame_sp++ = (int32)(value); \ - } while (0) - -#define PUSH_F32(value) do { \ - *(float32*)frame_sp++ = (float32)(value); \ - } while (0) - -#define PUSH_I64(value) do { \ - PUT_I64_TO_ADDR(frame_sp, value); \ - frame_sp += 2; \ - } while (0) - -#define PUSH_F64(value) do { \ - PUT_F64_TO_ADDR(frame_sp, value); \ - frame_sp += 2; \ - } while (0) - -#define PUSH_CSP(type, ret_type, _target_addr) do { \ - bh_assert(frame_csp < frame->csp_boundary); \ - frame_csp->block_type = type; \ - frame_csp->return_type = ret_type; \ - frame_csp->target_addr = _target_addr; \ - frame_csp->frame_sp = frame_sp; \ - frame_csp++; \ - } while (0) - -#define POP_I32() (--frame_sp, *(int32*)frame_sp) - -#define POP_F32() (--frame_sp, *(float32*)frame_sp) - -#define POP_I64() (frame_sp -= 2, GET_I64_FROM_ADDR(frame_sp)) - -#define POP_F64() (frame_sp -= 2, GET_F64_FROM_ADDR(frame_sp)) - -#define POP_CSP_CHECK_OVERFLOW(n) do { \ - bh_assert(frame_csp - n >= frame->csp_bottom); \ - } while (0) - -#define POP_CSP() do { \ - POP_CSP_CHECK_OVERFLOW(1); \ - --frame_csp; \ - } while (0) - -#define POP_CSP_N(n) do { \ - uint32 *frame_sp_old = frame_sp; \ - POP_CSP_CHECK_OVERFLOW(n + 1); \ - frame_csp -= n; \ - frame_ip = (frame_csp - 1)->target_addr; \ - /* copy return value of block */ \ - frame_sp = (frame_csp - 1)->frame_sp; \ - switch ((frame_csp - 1)->return_type) { \ - case VALUE_TYPE_I32: \ - case VALUE_TYPE_F32: \ - PUSH_I32(*(frame_sp_old - 1)); \ - break; \ - case VALUE_TYPE_I64: \ - case VALUE_TYPE_F64: \ - PUSH_I64(GET_I64_FROM_ADDR(frame_sp_old - 2)); \ - break; \ - } \ - } while (0) - -/* Pop the given number of elements from the given frame's stack. */ -#define POP(N) do { \ - int n = (N); \ - frame_sp -= n; \ - } while (0) - -#define SYNC_ALL_TO_FRAME() do { \ - frame->sp = frame_sp; \ - frame->ip = frame_ip; \ - frame->csp = frame_csp; \ - } while (0) - -#define UPDATE_ALL_FROM_FRAME() do { \ - frame_sp = frame->sp; \ - frame_ip = frame->ip; \ - frame_csp = frame->csp; \ - } while (0) - -#define read_leb_int64(p, p_end, res) do { \ - uint8 _val = *p; \ - if (!(_val & 0x80)) { \ - res = (int64)_val; \ - if (_val & 0x40) \ - /* sign extend */ \ - res |= 0xFFFFFFFFFFFFFF80LL; \ - p++; \ - break; \ - } \ - uint32 _off = 0; \ - res = (int64)read_leb(p, &_off, 64, true); \ - p += _off; \ -} while (0) - -#define read_leb_uint32(p, p_end, res) do { \ - uint8 _val = *p; \ - if (!(_val & 0x80)) { \ - res = _val; \ - p++; \ - break; \ - } \ - uint32 _off = 0; \ - res = (uint32)read_leb(p, &_off, 32, false); \ - p += _off; \ -} while (0) - -#define read_leb_int32(p, p_end, res) do { \ - uint8 _val = *p; \ - if (!(_val & 0x80)) { \ - res = (int32)_val; \ - if (_val & 0x40) \ - /* sign extend */ \ - res |= 0xFFFFFF80; \ - p++; \ - break; \ - } \ - uint32 _off = 0; \ - res = (int32)read_leb(p, &_off, 32, true); \ - p += _off; \ -} while (0) - -#if WASM_ENABLE_LABELS_AS_VALUES == 0 -#define RECOVER_FRAME_IP_END() \ - frame_ip_end = wasm_get_func_code_end(cur_func) -#else -#define RECOVER_FRAME_IP_END() (void)0 -#endif - -#define RECOVER_CONTEXT(new_frame) do { \ - frame = (new_frame); \ - cur_func = frame->function; \ - prev_frame = frame->prev_frame; \ - frame_ip = frame->ip; \ - RECOVER_FRAME_IP_END(); \ - frame_lp = frame->lp; \ - frame_sp = frame->sp; \ - frame_csp = frame->csp; \ - } while (0) - -#if WASM_ENABLE_LABELS_AS_VALUES != 0 -#define GET_OPCODE() opcode = *(frame_ip - 1); -#else -#define GET_OPCODE() (void)0 -#endif - -#define DEF_OP_I_CONST(ctype, src_op_type) do { \ - ctype cval; \ - read_leb_##ctype(frame_ip, frame_ip_end, cval); \ - PUSH_##src_op_type(cval); \ - } while (0) - -#define DEF_OP_EQZ(src_op_type) do { \ - int32 val; \ - val = POP_##src_op_type() == 0; \ - PUSH_I32(val); \ - } while (0) - -#define DEF_OP_CMP(src_type, src_op_type, cond) do { \ - uint32 res; \ - src_type val1, val2; \ - val2 = (src_type)POP_##src_op_type(); \ - val1 = (src_type)POP_##src_op_type(); \ - res = val1 cond val2; \ - PUSH_I32(res); \ - } while (0) - -#define DEF_OP_BIT_COUNT(src_type, src_op_type, operation) do { \ - src_type val1, val2; \ - val1 = (src_type)POP_##src_op_type(); \ - val2 = (src_type)operation(val1); \ - PUSH_##src_op_type(val2); \ - } while (0) - -#define DEF_OP_NUMERIC(src_type1, src_type2, src_op_type, operation) do { \ - frame_sp -= sizeof(src_type2)/sizeof(uint32); \ - *(src_type1*)(frame_sp - sizeof(src_type1)/sizeof(uint32)) operation##= \ - *(src_type2*)(frame_sp); \ - } while (0) - -#if WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 -#define DEF_OP_NUMERIC_64 DEF_OP_NUMERIC -#else -#define DEF_OP_NUMERIC_64(src_type1, src_type2, src_op_type, operation) do {\ - src_type1 val1; \ - src_type2 val2; \ - frame_sp -= 2; \ - val1 = (src_type1)GET_##src_op_type##_FROM_ADDR(frame_sp - 2); \ - val2 = (src_type2)GET_##src_op_type##_FROM_ADDR(frame_sp); \ - val1 operation##= val2; \ - PUT_##src_op_type##_TO_ADDR(frame_sp - 2, val1); \ - } while (0) -#endif - -#define DEF_OP_NUMERIC2(src_type1, src_type2, src_op_type, operation) do { \ - frame_sp -= sizeof(src_type2)/sizeof(uint32); \ - *(src_type1*)(frame_sp - sizeof(src_type1)/sizeof(uint32)) operation##= \ - (*(src_type2*)(frame_sp) % 32); \ - } while (0) - -#define DEF_OP_NUMERIC2_64(src_type1, src_type2, src_op_type, operation) do { \ - src_type1 val1; \ - src_type2 val2; \ - frame_sp -= 2; \ - val1 = (src_type1)GET_##src_op_type##_FROM_ADDR(frame_sp - 2); \ - val2 = (src_type2)GET_##src_op_type##_FROM_ADDR(frame_sp); \ - val1 operation##= (val2 % 64); \ - PUT_##src_op_type##_TO_ADDR(frame_sp - 2, val1); \ - } while (0) - -#define DEF_OP_MATH(src_type, src_op_type, method) do { \ - src_type val; \ - val = POP_##src_op_type(); \ - PUSH_##src_op_type(method(val)); \ - } while (0) - -#define DEF_OP_TRUNC(dst_type, dst_op_type, src_type, src_op_type, \ - min_cond, max_cond) do { \ - src_type value = POP_##src_op_type(); \ - if (isnan(value)) { \ - wasm_set_exception(module, "invalid conversion to integer"); \ - goto got_exception; \ - } \ - else if (value min_cond || value max_cond) { \ - wasm_set_exception(module, "integer overflow"); \ - goto got_exception; \ - } \ - PUSH_##dst_op_type(((dst_type)value)); \ - } while (0) - -#define DEF_OP_CONVERT(dst_type, dst_op_type, \ - src_type, src_op_type) do { \ - dst_type value = (dst_type)(src_type)POP_##src_op_type(); \ - PUSH_##dst_op_type(value); \ - } while (0) - -#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() do { \ - uint32 param_count = cur_func->param_count; \ - read_leb_uint32(frame_ip, frame_ip_end, local_idx); \ - bh_assert(local_idx < param_count + cur_func->local_count); \ - local_offset = cur_func->local_offsets[local_idx]; \ - if (local_idx < param_count) \ - local_type = cur_func->param_types[local_idx]; \ - else \ - local_type = cur_func->local_types[local_idx - param_count]; \ - } while (0) - -static inline int32 -sign_ext_8_32(int8 val) -{ - if (val & 0x80) - return (int32)val | (int32)0xffffff00; - return val; -} - -static inline int32 -sign_ext_16_32(int16 val) -{ - if (val & 0x8000) - return (int32)val | (int32)0xffff0000; - return val; -} - -static inline int64 -sign_ext_8_64(int8 val) -{ - if (val & 0x80) - return (int64)val | (int64)0xffffffffffffff00; - return val; -} - -static inline int64 -sign_ext_16_64(int16 val) -{ - if (val & 0x8000) - return (int64)val | (int64)0xffffffffffff0000; - return val; -} - -static inline int64 -sign_ext_32_64(int32 val) -{ - if (val & (int32)0x80000000) - return (int64)val | (int64)0xffffffff00000000; - return val; -} - -static inline void -word_copy(uint32 *dest, uint32 *src, unsigned num) -{ - for (; num > 0; num--) - *dest++ = *src++; -} - -static inline WASMInterpFrame* -ALLOC_FRAME(WASMExecEnv *exec_env, uint32 size, WASMInterpFrame *prev_frame) -{ - WASMInterpFrame *frame = wasm_exec_env_alloc_wasm_frame(exec_env, size); - - if (frame) - frame->prev_frame = prev_frame; - else { - wasm_set_exception((WASMModuleInstance*)exec_env->module_inst, - "WASM interp failed: stack overflow."); - } - - return frame; -} - -static inline void -FREE_FRAME(WASMExecEnv *exec_env, WASMInterpFrame *frame) -{ - wasm_exec_env_free_wasm_frame(exec_env, frame); -} - -static void -wasm_interp_call_func_native(WASMModuleInstance *module_inst, - WASMExecEnv *exec_env, - WASMFunctionInstance *cur_func, - WASMInterpFrame *prev_frame) -{ - unsigned local_cell_num = 2; - WASMInterpFrame *frame; - uint32 argv_ret[2]; - bool ret; - - if (!(frame = ALLOC_FRAME(exec_env, - wasm_interp_interp_frame_size(local_cell_num), - prev_frame))) - return; - - frame->function = cur_func; - frame->ip = NULL; - frame->sp = frame->lp + local_cell_num; - - wasm_exec_env_set_cur_frame(exec_env, frame); - - if (!cur_func->u.func_import->func_ptr_linked) { - char buf[128]; - snprintf(buf, - sizeof(buf), "fail to call unlinked import function (%s, %s)", - cur_func->u.func_import->module_name, - cur_func->u.func_import->field_name); - wasm_set_exception((WASMModuleInstance*)module_inst, buf); - return; - } - - ret = wasm_runtime_invoke_native(cur_func->u.func_import->func_ptr_linked, - cur_func->u.func_import->func_type, - exec_env, - frame->lp, cur_func->param_cell_num, argv_ret); - - if (!ret) - return; - - if (cur_func->ret_cell_num == 1) { - prev_frame->sp[0] = argv_ret[0]; - prev_frame->sp++; - } - else if (cur_func->ret_cell_num == 2) { - prev_frame->sp[0] = argv_ret[0]; - prev_frame->sp[1] = argv_ret[1]; - prev_frame->sp += 2; - } - - FREE_FRAME(exec_env, frame); - wasm_exec_env_set_cur_frame(exec_env, prev_frame); -} - -#if WASM_ENABLE_LABELS_AS_VALUES != 0 - -#define HANDLE_OP(opcode) HANDLE_##opcode -#define FETCH_OPCODE_AND_DISPATCH() goto *handle_table[*frame_ip++] -#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() - -#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ - -#define HANDLE_OP(opcode) case opcode -#define HANDLE_OP_END() continue - -#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ - -typedef struct BlockAddrCache { - uint8 *frame_ip; - uint8 *else_addr; - uint8 *end_addr; -} BlockAddrCache; - -static void -wasm_interp_call_func_bytecode(WASMModuleInstance *module, - WASMExecEnv *exec_env, - WASMFunctionInstance *cur_func, - WASMInterpFrame *prev_frame) -{ - WASMMemoryInstance *memory = module->default_memory; - uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; - uint32 memory_data_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; - uint32 heap_base_offset = memory ? (uint32)memory->heap_base_offset : 0; - uint32 heap_data_size = memory ? (uint32)(memory->heap_data_end - memory->heap_data) : 0; - uint8 *global_data = memory ? memory->global_data : NULL; - WASMTableInstance *table = module->default_table; - WASMGlobalInstance *globals = module->globals; - uint8 opcode_IMPDEP = WASM_OP_IMPDEP; - WASMInterpFrame *frame = NULL; - /* Points to this special opcode so as to jump to the call_method_from_entry. */ - register uint8 *frame_ip = &opcode_IMPDEP; /* cache of frame->ip */ - register uint32 *frame_lp = NULL; /* cache of frame->lp */ - register uint32 *frame_sp = NULL; /* cache of frame->sp */ - WASMBranchBlock *frame_csp = NULL; - WASMGlobalInstance *global; - uint8 *frame_ip_end = frame_ip + 1; - uint8 opcode, block_ret_type; - uint32 *depths = NULL; - uint32 depth_buf[BR_TABLE_TMP_BUF_LEN]; - uint32 i, depth, cond, count, fidx, tidx, frame_size = 0; - uint64 all_cell_num = 0; - int32 didx, val; - uint8 *else_addr, *end_addr, *maddr = NULL; - uint32 local_idx, local_offset, global_idx; - uint8 local_type, *global_addr; - BlockAddrCache block_addr_cache[32] = { 0 }; - uint32 cache_index, block_addr_cache_size = 32; - -#if WASM_ENABLE_LABELS_AS_VALUES != 0 - #define HANDLE_OPCODE(op) &&HANDLE_##op - DEFINE_GOTO_TABLE (handle_table); - #undef HANDLE_OPCODE -#endif - - /* Size of memory load. - This starts with the first memory load operator at opcode 0x28 */ - uint32 LOAD_SIZE[] = { - 4, 8, 4, 8, 1, 1, 2, 2, 1, 1, 2, 2, 4, 4, /* loads */ - 4, 8, 4, 8, 1, 2, 1, 2, 4 }; /* stores */ - -#if WASM_ENABLE_LABELS_AS_VALUES == 0 - while (frame_ip < frame_ip_end) { - opcode = *frame_ip++; - switch (opcode) { -#else - FETCH_OPCODE_AND_DISPATCH (); -#endif - /* control instructions */ - HANDLE_OP (WASM_OP_UNREACHABLE): - wasm_set_exception(module, "unreachable"); - goto got_exception; - - HANDLE_OP (WASM_OP_NOP): - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_BLOCK): - block_ret_type = *frame_ip++; - - cache_index = ((uintptr_t)frame_ip) & (uintptr_t)(block_addr_cache_size - 1); - if (block_addr_cache[cache_index].frame_ip == frame_ip) { - end_addr = block_addr_cache[cache_index].end_addr; - } - else { - if (!wasm_loader_find_block_addr(module->module, - frame_ip, (uint8*)-1, - BLOCK_TYPE_BLOCK, - &else_addr, &end_addr, - NULL, 0)) { - wasm_set_exception(module, "find block address failed"); - goto got_exception; - } - block_addr_cache[cache_index].frame_ip = frame_ip; - block_addr_cache[cache_index].end_addr = end_addr; - } - - PUSH_CSP(BLOCK_TYPE_BLOCK, block_ret_type, end_addr); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_LOOP): - block_ret_type = *frame_ip++; - PUSH_CSP(BLOCK_TYPE_LOOP, block_ret_type, frame_ip); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_IF): - block_ret_type = *frame_ip++; - - cache_index = ((uintptr_t)frame_ip) & (uintptr_t)(block_addr_cache_size - 1); - if (block_addr_cache[cache_index].frame_ip == frame_ip) { - else_addr = block_addr_cache[cache_index].else_addr; - end_addr = block_addr_cache[cache_index].end_addr; - } - else { - if (!wasm_loader_find_block_addr(module->module, - frame_ip, (uint8*)-1, - BLOCK_TYPE_IF, - &else_addr, &end_addr, - NULL, 0)) { - wasm_set_exception(module, "find block address failed"); - goto got_exception; - } - - block_addr_cache[cache_index].frame_ip = frame_ip; - block_addr_cache[cache_index].else_addr = else_addr; - block_addr_cache[cache_index].end_addr = end_addr; - } - - cond = (uint32)POP_I32(); - - PUSH_CSP(BLOCK_TYPE_IF, block_ret_type, end_addr); - - /* condition of the if branch is false, else condition is met */ - if (cond == 0) { - /* if there is no else branch, go to the end addr */ - if (else_addr == NULL) { - POP_CSP(); - frame_ip = end_addr + 1; - } - /* if there is an else branch, go to the else addr */ - else - frame_ip = else_addr + 1; - } - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_ELSE): - /* comes from the if branch in WASM_OP_IF */ - frame_ip = (frame_csp - 1)->target_addr; - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_END): - if (frame_csp > frame->csp_bottom + 1) { - POP_CSP(); - } - else { /* end of function, treat as WASM_OP_RETURN */ - frame_sp -= cur_func->ret_cell_num; - for (i = 0; i < cur_func->ret_cell_num; i++) { - *prev_frame->sp++ = frame_sp[i]; - } - goto return_func; - } - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_BR): - read_leb_uint32(frame_ip, frame_ip_end, depth); - POP_CSP_N(depth); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_BR_IF): - read_leb_uint32(frame_ip, frame_ip_end, depth); - cond = (uint32)POP_I32(); - if (cond) - POP_CSP_N(depth); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_BR_TABLE): - read_leb_uint32(frame_ip, frame_ip_end, count); - if (count <= BR_TABLE_TMP_BUF_LEN) - depths = depth_buf; - else { - uint64 total_size = sizeof(uint32) * (uint64)count; - if (total_size >= UINT32_MAX - || !(depths = wasm_malloc((uint32)total_size))) { - wasm_set_exception(module, - "WASM interp failed: allocate memory failed."); - goto got_exception; - } - } - for (i = 0; i < count; i++) { - read_leb_uint32(frame_ip, frame_ip_end, depths[i]); - } - read_leb_uint32(frame_ip, frame_ip_end, depth); - didx = POP_I32(); - if (didx >= 0 && (uint32)didx < count) { - depth = depths[didx]; - } - if (depths != depth_buf) { - wasm_free(depths); - depths = NULL; - } - POP_CSP_N(depth); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_RETURN): - frame_sp -= cur_func->ret_cell_num; - for (i = 0; i < cur_func->ret_cell_num; i++) { - *prev_frame->sp++ = frame_sp[i]; - } - goto return_func; - - HANDLE_OP (WASM_OP_CALL): - read_leb_uint32(frame_ip, frame_ip_end, fidx); - bh_assert(fidx < module->function_count); - cur_func = module->functions + fidx; - goto call_func_from_interp; - - HANDLE_OP (WASM_OP_CALL_INDIRECT): - { - WASMType *cur_type, *cur_func_type; - - read_leb_uint32(frame_ip, frame_ip_end, tidx); - if (tidx >= module->module->type_count) { - wasm_set_exception(module, "type index is overflow"); - goto got_exception; - } - cur_type = module->module->types[tidx]; - - /* to skip 0x00 here */ - frame_ip++; - val = POP_I32(); - - if (val < 0 || val >= (int32)table->cur_size) { - wasm_set_exception(module, "undefined element"); - goto got_exception; - } - - fidx = ((uint32*)table->base_addr)[val]; - if (fidx == (uint32)-1) { - wasm_set_exception(module, "uninitialized element"); - goto got_exception; - } - - cur_func = module->functions + fidx; - - if (cur_func->is_import_func) - cur_func_type = cur_func->u.func_import->func_type; - else - cur_func_type = cur_func->u.func->func_type; - if (!wasm_type_equal(cur_type, cur_func_type)) { - wasm_set_exception(module, "indirect call type mismatch"); - goto got_exception; - } - goto call_func_from_interp; - } - - /* parametric instructions */ - HANDLE_OP (WASM_OP_DROP): - { - frame_sp--; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_DROP_64): - { - frame_sp -= 2; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_SELECT): - { - cond = (uint32)POP_I32(); - frame_sp--; - if (!cond) - *(frame_sp - 1) = *frame_sp; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_SELECT_64): - { - cond = (uint32)POP_I32(); - frame_sp -= 2; - if (!cond) { - *(frame_sp - 2) = *frame_sp; - *(frame_sp - 1) = *(frame_sp + 1); - } - HANDLE_OP_END (); - } - - /* variable instructions */ - HANDLE_OP (WASM_OP_GET_LOCAL): - { - GET_LOCAL_INDEX_TYPE_AND_OFFSET(); - - switch (local_type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - PUSH_I32(*(int32*)(frame_lp + local_offset)); - break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - PUSH_I64(GET_I64_FROM_ADDR(frame_lp + local_offset)); - break; - default: - wasm_set_exception(module, "invalid local type"); - goto got_exception; - } - - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_GET_LOCAL_FAST): - { - local_offset = *frame_ip++; - if (local_offset & 0x80) - PUSH_I64(GET_I64_FROM_ADDR(frame_lp + (local_offset & 0x7F))); - else - PUSH_I32(*(int32*)(frame_lp + local_offset)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_SET_LOCAL): - { - GET_LOCAL_INDEX_TYPE_AND_OFFSET(); - - switch (local_type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - *(int32*)(frame_lp + local_offset) = POP_I32(); - break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - PUT_I64_TO_ADDR((uint32*)(frame_lp + local_offset), POP_I64()); - break; - default: - wasm_set_exception(module, "invalid local type"); - goto got_exception; - } - - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_SET_LOCAL_FAST): - { - local_offset = *frame_ip++; - if (local_offset & 0x80) - PUT_I64_TO_ADDR((uint32*)(frame_lp + (local_offset & 0x7F)), POP_I64()); - else - *(int32*)(frame_lp + local_offset) = POP_I32(); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_TEE_LOCAL): - { - GET_LOCAL_INDEX_TYPE_AND_OFFSET(); - - switch (local_type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - *(int32*)(frame_lp + local_offset) = *(int32*)(frame_sp - 1); - break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - PUT_I64_TO_ADDR((uint32*)(frame_lp + local_offset), - GET_I64_FROM_ADDR(frame_sp - 2)); - break; - default: - wasm_set_exception(module, "invalid local type"); - goto got_exception; - } - - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_TEE_LOCAL_FAST): - { - local_offset = *frame_ip++; - if (local_offset & 0x80) - PUT_I64_TO_ADDR((uint32*)(frame_lp + (local_offset & 0x7F)), - GET_I64_FROM_ADDR(frame_sp - 2)); - else - *(int32*)(frame_lp + local_offset) = *(int32*)(frame_sp - 1); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_GET_GLOBAL): - { - read_leb_uint32(frame_ip, frame_ip_end, global_idx); - - bh_assert(global_idx < module->global_count); - global = globals + global_idx; - global_addr = global_data + global->data_offset; - - switch (global->type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - PUSH_I32(*(uint32*)global_addr); - break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - PUSH_I64(GET_I64_FROM_ADDR((uint32*)global_addr)); - break; - default: - wasm_set_exception(module, "invalid global type"); - goto got_exception; - } - - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_SET_GLOBAL): - { - read_leb_uint32(frame_ip, frame_ip_end, global_idx); - - bh_assert(global_idx < module->global_count); - global = globals + global_idx; - global_addr = global_data + global->data_offset; - - switch (global->type) { - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - *(int32*)global_addr = POP_I32(); - break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - PUT_I64_TO_ADDR((uint32*)global_addr, POP_I64()); - break; - default: - wasm_set_exception(module, "invalid global type"); - goto got_exception; - } - - HANDLE_OP_END (); - } - - /* memory load instructions */ - HANDLE_OP (WASM_OP_I32_LOAD): - HANDLE_OP (WASM_OP_I64_LOAD): - HANDLE_OP (WASM_OP_F32_LOAD): - HANDLE_OP (WASM_OP_F64_LOAD): - HANDLE_OP (WASM_OP_I32_LOAD8_S): - HANDLE_OP (WASM_OP_I32_LOAD8_U): - HANDLE_OP (WASM_OP_I32_LOAD16_S): - HANDLE_OP (WASM_OP_I32_LOAD16_U): - HANDLE_OP (WASM_OP_I64_LOAD8_S): - HANDLE_OP (WASM_OP_I64_LOAD8_U): - HANDLE_OP (WASM_OP_I64_LOAD16_S): - HANDLE_OP (WASM_OP_I64_LOAD16_U): - HANDLE_OP (WASM_OP_I64_LOAD32_S): - HANDLE_OP (WASM_OP_I64_LOAD32_U): - { - uint32 offset, flags, addr; - GET_OPCODE(); - read_leb_uint32(frame_ip, frame_ip_end, flags); - read_leb_uint32(frame_ip, frame_ip_end, offset); - addr = (uint32)POP_I32(); - CHECK_MEMORY_OVERFLOW(); -#if WASM_ENABLE_LABELS_AS_VALUES != 0 - static const void *handle_load_table[] = { - &&HANDLE_LOAD_WASM_OP_I32_LOAD, - &&HANDLE_LOAD_WASM_OP_I64_LOAD, - &&HANDLE_LOAD_WASM_OP_F32_LOAD, - &&HANDLE_LOAD_WASM_OP_F64_LOAD, - &&HANDLE_LOAD_WASM_OP_I32_LOAD8_S, - &&HANDLE_LOAD_WASM_OP_I32_LOAD8_U, - &&HANDLE_LOAD_WASM_OP_I32_LOAD16_S, - &&HANDLE_LOAD_WASM_OP_I32_LOAD16_U, - &&HANDLE_LOAD_WASM_OP_I64_LOAD8_S, - &&HANDLE_LOAD_WASM_OP_I64_LOAD8_U, - &&HANDLE_LOAD_WASM_OP_I64_LOAD16_S, - &&HANDLE_LOAD_WASM_OP_I64_LOAD16_U, - &&HANDLE_LOAD_WASM_OP_I64_LOAD32_S, - &&HANDLE_LOAD_WASM_OP_I64_LOAD32_U - }; - #define HANDLE_OP_LOAD(opcode) HANDLE_LOAD_##opcode - goto *handle_load_table[opcode - WASM_OP_I32_LOAD]; -#else - #define HANDLE_OP_LOAD(opcode) case opcode - switch (opcode) -#endif - { - HANDLE_OP_LOAD(WASM_OP_I32_LOAD): - PUSH_I32(LOAD_I32(maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD): - PUSH_I64(LOAD_I64(maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_F32_LOAD): - PUSH_I32(LOAD_I32(maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_F64_LOAD): - PUSH_F64(LOAD_F64(maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I32_LOAD8_S): - PUSH_I32(sign_ext_8_32(*(int8*)maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I32_LOAD8_U): - PUSH_I32((uint32)(*(uint8*)maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I32_LOAD16_S): - PUSH_I32(sign_ext_16_32(LOAD_I16(maddr))); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I32_LOAD16_U): - PUSH_I32((uint32)(LOAD_U16(maddr))); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD8_S): - PUSH_I64(sign_ext_8_64(*(int8*)maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD8_U): - PUSH_I64((uint64)(*(uint8*)maddr)); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD16_S): - PUSH_I64(sign_ext_16_64(LOAD_I16(maddr))); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD16_U): - PUSH_I64((uint64)(LOAD_U16(maddr))); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD32_S): - PUSH_I64(sign_ext_32_64(LOAD_I32(maddr))); - HANDLE_OP_END(); - HANDLE_OP_LOAD(WASM_OP_I64_LOAD32_U): - PUSH_I64((uint64)(LOAD_U32(maddr))); - HANDLE_OP_END(); - } - (void)flags; - HANDLE_OP_END (); - } - - /* memory store instructions */ - HANDLE_OP (WASM_OP_F32_STORE): - { - uint32 offset, flags, addr; - GET_OPCODE(); - read_leb_uint32(frame_ip, frame_ip_end, flags); - read_leb_uint32(frame_ip, frame_ip_end, offset); - frame_sp--; - addr = (uint32)POP_I32(); - CHECK_MEMORY_OVERFLOW(); - STORE_U32(maddr, frame_sp[1]); - (void)flags; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F64_STORE): - { - uint32 offset, flags, addr; - GET_OPCODE(); - read_leb_uint32(frame_ip, frame_ip_end, flags); - read_leb_uint32(frame_ip, frame_ip_end, offset); - frame_sp -= 2; - addr = (uint32)POP_I32(); - CHECK_MEMORY_OVERFLOW(); - STORE_U32(maddr, frame_sp[1]); - STORE_U32(maddr + 4, frame_sp[2]); - (void)flags; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_STORE): - HANDLE_OP (WASM_OP_I32_STORE8): - HANDLE_OP (WASM_OP_I32_STORE16): - { - uint32 offset, flags, addr; - uint32 sval; - GET_OPCODE(); - read_leb_uint32(frame_ip, frame_ip_end, flags); - read_leb_uint32(frame_ip, frame_ip_end, offset); - sval = (uint32)POP_I32(); - addr = (uint32)POP_I32(); - CHECK_MEMORY_OVERFLOW(); - switch (opcode) { - case WASM_OP_I32_STORE: - STORE_U32(maddr, sval); - break; - case WASM_OP_I32_STORE8: - *(uint8*)maddr = (uint8)sval; - break; - case WASM_OP_I32_STORE16: - STORE_U16(maddr, (uint16)sval); - break; - } - (void)flags; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_STORE): - HANDLE_OP (WASM_OP_I64_STORE8): - HANDLE_OP (WASM_OP_I64_STORE16): - HANDLE_OP (WASM_OP_I64_STORE32): - { - uint32 offset, flags, addr; - uint64 sval; - GET_OPCODE(); - read_leb_uint32(frame_ip, frame_ip_end, flags); - read_leb_uint32(frame_ip, frame_ip_end, offset); - sval = (uint64)POP_I64(); - addr = (uint32)POP_I32(); - CHECK_MEMORY_OVERFLOW(); - switch (opcode) { - case WASM_OP_I64_STORE: - STORE_I64(maddr, sval); - break; - case WASM_OP_I64_STORE8: - *(uint8*)maddr = (uint8)sval; - break; - case WASM_OP_I64_STORE16: - STORE_U16(maddr, (uint16)sval); - break; - case WASM_OP_I64_STORE32: - STORE_U32(maddr, (uint32)sval); - break; - } - (void)flags; - HANDLE_OP_END (); - } - - /* memory size and memory grow instructions */ - HANDLE_OP (WASM_OP_MEMORY_SIZE): - { - uint32 reserved; - read_leb_uint32(frame_ip, frame_ip_end, reserved); - PUSH_I32(memory->cur_page_count); - (void)reserved; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_MEMORY_GROW): - { - uint32 reserved, delta, prev_page_count = memory->cur_page_count; - - read_leb_uint32(frame_ip, frame_ip_end, reserved); - delta = (uint32)POP_I32(); - - if (!wasm_enlarge_memory(module, delta)) { - /* fail to memory.grow, return -1 */ - PUSH_I32(-1); - if (wasm_get_exception(module)) { - bh_printf("%s\n", wasm_get_exception(module)); - wasm_set_exception(module, NULL); - } - } - else { - /* success, return previous page count */ - PUSH_I32(prev_page_count); - /* update the memory instance ptr */ - memory = module->default_memory; - memory_data_size = num_bytes_per_page * memory->cur_page_count; - global_data = memory->global_data; - } - - (void)reserved; - HANDLE_OP_END (); - } - - /* constant instructions */ - HANDLE_OP (WASM_OP_I32_CONST): - DEF_OP_I_CONST(int32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_CONST): - DEF_OP_I_CONST(int64, I64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_CONST): - { - uint8 *p_float = (uint8*)frame_sp++; - for (i = 0; i < sizeof(float32); i++) - *p_float++ = *frame_ip++; - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F64_CONST): - { - uint8 *p_float = (uint8*)frame_sp++; - frame_sp++; - for (i = 0; i < sizeof(float64); i++) - *p_float++ = *frame_ip++; - HANDLE_OP_END (); - } - - /* comparison instructions of i32 */ - HANDLE_OP (WASM_OP_I32_EQZ): - DEF_OP_EQZ(I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_EQ): - DEF_OP_CMP(uint32, I32, ==); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_NE): - DEF_OP_CMP(uint32, I32, !=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_LT_S): - DEF_OP_CMP(int32, I32, <); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_LT_U): - DEF_OP_CMP(uint32, I32, <); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_GT_S): - DEF_OP_CMP(int32, I32, >); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_GT_U): - DEF_OP_CMP(uint32, I32, >); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_LE_S): - DEF_OP_CMP(int32, I32, <=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_LE_U): - DEF_OP_CMP(uint32, I32, <=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_GE_S): - DEF_OP_CMP(int32, I32, >=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_GE_U): - DEF_OP_CMP(uint32, I32, >=); - HANDLE_OP_END (); - - /* comparison instructions of i64 */ - HANDLE_OP (WASM_OP_I64_EQZ): - DEF_OP_EQZ(I64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_EQ): - DEF_OP_CMP(uint64, I64, ==); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_NE): - DEF_OP_CMP(uint64, I64, !=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_LT_S): - DEF_OP_CMP(int64, I64, <); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_LT_U): - DEF_OP_CMP(uint64, I64, <); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_GT_S): - DEF_OP_CMP(int64, I64, >); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_GT_U): - DEF_OP_CMP(uint64, I64, >); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_LE_S): - DEF_OP_CMP(int64, I64, <=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_LE_U): - DEF_OP_CMP(uint64, I64, <=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_GE_S): - DEF_OP_CMP(int64, I64, >=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_GE_U): - DEF_OP_CMP(uint64, I64, >=); - HANDLE_OP_END (); - - /* comparison instructions of f32 */ - HANDLE_OP (WASM_OP_F32_EQ): - DEF_OP_CMP(float32, F32, ==); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_NE): - DEF_OP_CMP(float32, F32, !=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_LT): - DEF_OP_CMP(float32, F32, <); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_GT): - DEF_OP_CMP(float32, F32, >); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_LE): - DEF_OP_CMP(float32, F32, <=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_GE): - DEF_OP_CMP(float32, F32, >=); - HANDLE_OP_END (); - - /* comparison instructions of f64 */ - HANDLE_OP (WASM_OP_F64_EQ): - DEF_OP_CMP(float64, F64, ==); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_NE): - DEF_OP_CMP(float64, F64, !=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_LT): - DEF_OP_CMP(float64, F64, <); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_GT): - DEF_OP_CMP(float64, F64, >); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_LE): - DEF_OP_CMP(float64, F64, <=); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_GE): - DEF_OP_CMP(float64, F64, >=); - HANDLE_OP_END (); - - /* numberic instructions of i32 */ - HANDLE_OP (WASM_OP_I32_CLZ): - DEF_OP_BIT_COUNT(uint32, I32, clz32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_CTZ): - DEF_OP_BIT_COUNT(uint32, I32, ctz32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_POPCNT): - DEF_OP_BIT_COUNT(uint32, I32, popcount32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_ADD): - DEF_OP_NUMERIC(uint32, uint32, I32, +); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_SUB): - DEF_OP_NUMERIC(uint32, uint32, I32, -); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_MUL): - DEF_OP_NUMERIC(uint32, uint32, I32, *); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_DIV_S): - { - int32 a, b; - - b = POP_I32(); - a = POP_I32(); - if (a == (int32)0x80000000 && b == -1) { - wasm_set_exception(module, "integer overflow"); - goto got_exception; - } - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I32(a / b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_DIV_U): - { - uint32 a, b; - - b = (uint32)POP_I32(); - a = (uint32)POP_I32(); - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I32(a / b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_REM_S): - { - int32 a, b; - - b = POP_I32(); - a = POP_I32(); - if (a == (int32)0x80000000 && b == -1) { - PUSH_I32(0); - HANDLE_OP_END (); - } - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I32(a % b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_REM_U): - { - uint32 a, b; - - b = (uint32)POP_I32(); - a = (uint32)POP_I32(); - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I32(a % b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_AND): - DEF_OP_NUMERIC(uint32, uint32, I32, &); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_OR): - DEF_OP_NUMERIC(uint32, uint32, I32, |); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_XOR): - DEF_OP_NUMERIC(uint32, uint32, I32, ^); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_SHL): - { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) - DEF_OP_NUMERIC(uint32, uint32, I32, <<); -#else - DEF_OP_NUMERIC2(uint32, uint32, I32, <<); -#endif - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_SHR_S): - { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) - DEF_OP_NUMERIC(int32, uint32, I32, >>); -#else - DEF_OP_NUMERIC2(int32, uint32, I32, >>); -#endif - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_SHR_U): - { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) - DEF_OP_NUMERIC(uint32, uint32, I32, >>); -#else - DEF_OP_NUMERIC2(uint32, uint32, I32, >>); -#endif - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_ROTL): - { - uint32 a, b; - - b = (uint32)POP_I32(); - a = (uint32)POP_I32(); - PUSH_I32(rotl32(a, b)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_ROTR): - { - uint32 a, b; - - b = (uint32)POP_I32(); - a = (uint32)POP_I32(); - PUSH_I32(rotr32(a, b)); - HANDLE_OP_END (); - } - - /* numberic instructions of i64 */ - HANDLE_OP (WASM_OP_I64_CLZ): - DEF_OP_BIT_COUNT(uint64, I64, clz64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_CTZ): - DEF_OP_BIT_COUNT(uint64, I64, ctz64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_POPCNT): - DEF_OP_BIT_COUNT(uint64, I64, popcount64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_ADD): - DEF_OP_NUMERIC_64(uint64, uint64, I64, +); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_SUB): - DEF_OP_NUMERIC_64(uint64, uint64, I64, -); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_MUL): - DEF_OP_NUMERIC_64(uint64, uint64, I64, *); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_DIV_S): - { - int64 a, b; - - b = POP_I64(); - a = POP_I64(); - if (a == (int64)0x8000000000000000LL && b == -1) { - wasm_set_exception(module, "integer overflow"); - goto got_exception; - } - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I64(a / b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_DIV_U): - { - uint64 a, b; - - b = (uint64)POP_I64(); - a = (uint64)POP_I64(); - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I64(a / b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_REM_S): - { - int64 a, b; - - b = POP_I64(); - a = POP_I64(); - if (a == (int64)0x8000000000000000LL && b == -1) { - PUSH_I64(0); - HANDLE_OP_END (); - } - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I64(a % b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_REM_U): - { - uint64 a, b; - - b = (uint64)POP_I64(); - a = (uint64)POP_I64(); - if (b == 0) { - wasm_set_exception(module, "integer divide by zero"); - goto got_exception; - } - PUSH_I64(a % b); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_AND): - DEF_OP_NUMERIC_64(uint64, uint64, I64, &); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_OR): - DEF_OP_NUMERIC_64(uint64, uint64, I64, |); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_XOR): - DEF_OP_NUMERIC_64(uint64, uint64, I64, ^); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_SHL): - { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) - DEF_OP_NUMERIC_64(uint64, uint64, I64, <<); -#else - DEF_OP_NUMERIC2_64(uint64, uint64, I64, <<); -#endif - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_SHR_S): - { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) - DEF_OP_NUMERIC_64(int64, uint64, I64, >>); -#else - DEF_OP_NUMERIC2_64(int64, uint64, I64, >>); -#endif - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_SHR_U): - { -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) - DEF_OP_NUMERIC_64(uint64, uint64, I64, >>); -#else - DEF_OP_NUMERIC2_64(uint64, uint64, I64, >>); -#endif - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_ROTL): - { - uint64 a, b; - - b = (uint64)POP_I64(); - a = (uint64)POP_I64(); - PUSH_I64(rotl64(a, b)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I64_ROTR): - { - uint64 a, b; - - b = (uint64)POP_I64(); - a = (uint64)POP_I64(); - PUSH_I64(rotr64(a, b)); - HANDLE_OP_END (); - } - - /* numberic instructions of f32 */ - HANDLE_OP (WASM_OP_F32_ABS): - DEF_OP_MATH(float32, F32, fabs); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_NEG): - { - int32 i32 = (int32)frame_sp[-1]; - int32 sign_bit = i32 & (1 << 31); - if (sign_bit) - frame_sp[-1] = i32 & ~(1 << 31); - else - frame_sp[-1] = (uint32)(i32 | (1 << 31)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F32_CEIL): - DEF_OP_MATH(float32, F32, ceil); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_FLOOR): - DEF_OP_MATH(float32, F32, floor); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_TRUNC): - DEF_OP_MATH(float32, F32, trunc); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_NEAREST): - DEF_OP_MATH(float32, F32, rint); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_SQRT): - DEF_OP_MATH(float32, F32, sqrt); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_ADD): - DEF_OP_NUMERIC(float32, float32, F32, +); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_SUB): - DEF_OP_NUMERIC(float32, float32, F32, -); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_MUL): - DEF_OP_NUMERIC(float32, float32, F32, *); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_DIV): - DEF_OP_NUMERIC(float32, float32, F32, /); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_MIN): - { - float32 a, b; - - b = POP_F32(); - a = POP_F32(); - - if (isnan(a)) - PUSH_F32(a); - else if (isnan(b)) - PUSH_F32(b); - else - PUSH_F32(wa_fmin(a, b)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F32_MAX): - { - float32 a, b; - - b = POP_F32(); - a = POP_F32(); - - if (isnan(a)) - PUSH_F32(a); - else if (isnan(b)) - PUSH_F32(b); - else - PUSH_F32(wa_fmax(a, b)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F32_COPYSIGN): - { - float32 a, b; - - b = POP_F32(); - a = POP_F32(); - PUSH_F32(signbit(b) ? -fabs(a) : fabs(a)); - HANDLE_OP_END (); - } - - /* numberic instructions of f64 */ - HANDLE_OP (WASM_OP_F64_ABS): - DEF_OP_MATH(float64, F64, fabs); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_NEG): - { - int64 i64 = GET_I64_FROM_ADDR(frame_sp - 2); - int64 sign_bit = i64 & (((int64)1) << 63); - if (sign_bit) - PUT_I64_TO_ADDR(frame_sp - 2, ((uint64)i64 & ~(((uint64)1) << 63))); - else - PUT_I64_TO_ADDR(frame_sp - 2, ((uint64)i64 | (((uint64)1) << 63))); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F64_CEIL): - DEF_OP_MATH(float64, F64, ceil); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_FLOOR): - DEF_OP_MATH(float64, F64, floor); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_TRUNC): - DEF_OP_MATH(float64, F64, trunc); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_NEAREST): - DEF_OP_MATH(float64, F64, rint); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_SQRT): - DEF_OP_MATH(float64, F64, sqrt); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_ADD): - DEF_OP_NUMERIC_64(float64, float64, F64, +); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_SUB): - DEF_OP_NUMERIC_64(float64, float64, F64, -); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_MUL): - DEF_OP_NUMERIC_64(float64, float64, F64, *); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_DIV): - DEF_OP_NUMERIC_64(float64, float64, F64, /); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_MIN): - { - float64 a, b; - - b = POP_F64(); - a = POP_F64(); - - if (isnan(a)) - PUSH_F64(a); - else if (isnan(b)) - PUSH_F64(b); - else - PUSH_F64(wa_fmin(a, b)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F64_MAX): - { - float64 a, b; - - b = POP_F64(); - a = POP_F64(); - - if (isnan(a)) - PUSH_F64(a); - else if (isnan(b)) - PUSH_F64(b); - else - PUSH_F64(wa_fmax(a, b)); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_F64_COPYSIGN): - { - float64 a, b; - - b = POP_F64(); - a = POP_F64(); - PUSH_F64(signbit(b) ? -fabs(a) : fabs(a)); - HANDLE_OP_END (); - } - - /* conversions of i32 */ - HANDLE_OP (WASM_OP_I32_WRAP_I64): - { - int32 value = (int32)(POP_I64() & 0xFFFFFFFFLL); - PUSH_I32(value); - HANDLE_OP_END (); - } - - HANDLE_OP (WASM_OP_I32_TRUNC_S_F32): - /* We don't use INT32_MIN/INT32_MAX/UINT32_MIN/UINT32_MAX, - since float/double values of ieee754 cannot precisely represent - all int32/uint32/int64/uint64 values, e.g.: - UINT32_MAX is 4294967295, but (float32)4294967295 is 4294967296.0f, - but not 4294967295.0f. */ - DEF_OP_TRUNC(int32, I32, float32, F32, <= -2147483904.0f, - >= 2147483648.0f); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_TRUNC_U_F32): - DEF_OP_TRUNC(uint32, I32, float32, F32, <= -1.0f, - >= 4294967296.0f); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_TRUNC_S_F64): - DEF_OP_TRUNC(int32, I32, float64, F64, <= -2147483649.0, - >= 2147483648.0); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I32_TRUNC_U_F64): - DEF_OP_TRUNC(uint32, I32, float64, F64, <= -1.0 , - >= 4294967296.0); - HANDLE_OP_END (); - - /* conversions of i64 */ - HANDLE_OP (WASM_OP_I64_EXTEND_S_I32): - DEF_OP_CONVERT(int64, I64, int32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_EXTEND_U_I32): - DEF_OP_CONVERT(int64, I64, uint32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_TRUNC_S_F32): - DEF_OP_TRUNC(int64, I64, float32, F32, <= -9223373136366403584.0f, - >= 9223372036854775808.0f); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_TRUNC_U_F32): - DEF_OP_TRUNC(uint64, I64, float32, F32, <= -1.0f, - >= 18446744073709551616.0f); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_TRUNC_S_F64): - DEF_OP_TRUNC(int64, I64, float64, F64, <= -9223372036854777856.0, - >= 9223372036854775808.0); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_I64_TRUNC_U_F64): - DEF_OP_TRUNC(uint64, I64, float64, F64, <= -1.0, - >= 18446744073709551616.0); - HANDLE_OP_END (); - - /* conversions of f32 */ - HANDLE_OP (WASM_OP_F32_CONVERT_S_I32): - DEF_OP_CONVERT(float32, F32, int32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_CONVERT_U_I32): - DEF_OP_CONVERT(float32, F32, uint32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_CONVERT_S_I64): - DEF_OP_CONVERT(float32, F32, int64, I64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_CONVERT_U_I64): - DEF_OP_CONVERT(float32, F32, uint64, I64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F32_DEMOTE_F64): - DEF_OP_CONVERT(float32, F32, float64, F64); - HANDLE_OP_END (); - - /* conversions of f64 */ - HANDLE_OP (WASM_OP_F64_CONVERT_S_I32): - DEF_OP_CONVERT(float64, F64, int32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_CONVERT_U_I32): - DEF_OP_CONVERT(float64, F64, uint32, I32); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_CONVERT_S_I64): - DEF_OP_CONVERT(float64, F64, int64, I64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_CONVERT_U_I64): - DEF_OP_CONVERT(float64, F64, uint64, I64); - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_F64_PROMOTE_F32): - DEF_OP_CONVERT(float64, F64, float32, F32); - HANDLE_OP_END (); - - /* reinterpretations */ - HANDLE_OP (WASM_OP_I32_REINTERPRET_F32): - HANDLE_OP (WASM_OP_I64_REINTERPRET_F64): - HANDLE_OP (WASM_OP_F32_REINTERPRET_I32): - HANDLE_OP (WASM_OP_F64_REINTERPRET_I64): - HANDLE_OP_END (); - - HANDLE_OP (WASM_OP_IMPDEP): - frame = prev_frame; - frame_ip = frame->ip; - frame_sp = frame->sp; - frame_csp = frame->csp; - goto call_func_from_entry; - -#if WASM_ENABLE_LABELS_AS_VALUES == 0 - default: - wasm_set_exception(module, "WASM interp failed: unsupported opcode."); - goto got_exception; - } -#endif - -#if WASM_ENABLE_LABELS_AS_VALUES != 0 - HANDLE_OP (WASM_OP_UNUSED_0x06): - HANDLE_OP (WASM_OP_UNUSED_0x07): - HANDLE_OP (WASM_OP_UNUSED_0x08): - HANDLE_OP (WASM_OP_UNUSED_0x09): - HANDLE_OP (WASM_OP_UNUSED_0x0a): - HANDLE_OP (WASM_OP_UNUSED_0x12): - HANDLE_OP (WASM_OP_UNUSED_0x13): - HANDLE_OP (WASM_OP_UNUSED_0x14): - HANDLE_OP (WASM_OP_UNUSED_0x15): - HANDLE_OP (WASM_OP_UNUSED_0x16): - HANDLE_OP (WASM_OP_UNUSED_0x17): - HANDLE_OP (WASM_OP_UNUSED_0x18): - HANDLE_OP (WASM_OP_UNUSED_0x19): - HANDLE_OP (WASM_OP_UNUSED_0x1c): - HANDLE_OP (WASM_OP_UNUSED_0x1d): - HANDLE_OP (WASM_OP_UNUSED_0x1e): - HANDLE_OP (WASM_OP_UNUSED_0x1f): - HANDLE_OP (WASM_OP_UNUSED_0x25): - HANDLE_OP (WASM_OP_UNUSED_0x26): - HANDLE_OP (WASM_OP_UNUSED_0x27): - { - wasm_set_exception(module, "WASM interp failed: unsupported opcode."); - goto got_exception; - } -#endif - -#if WASM_ENABLE_LABELS_AS_VALUES == 0 - continue; -#else - FETCH_OPCODE_AND_DISPATCH (); -#endif - - call_func_from_interp: - /* Only do the copy when it's called from interpreter. */ - { - WASMInterpFrame *outs_area = wasm_exec_env_wasm_stack_top(exec_env); - POP(cur_func->param_cell_num); - SYNC_ALL_TO_FRAME(); - word_copy(outs_area->lp, frame_sp, cur_func->param_cell_num); - prev_frame = frame; - } - - call_func_from_entry: - { - if (cur_func->is_import_func) { - wasm_interp_call_func_native(module, exec_env, cur_func, prev_frame); - prev_frame = frame->prev_frame; - cur_func = frame->function; - UPDATE_ALL_FROM_FRAME(); - - memory = module->default_memory; - if (wasm_get_exception(module)) - goto got_exception; - } - else { - WASMFunction *cur_wasm_func = cur_func->u.func; - WASMType *func_type; - uint8 ret_type; - - func_type = cur_wasm_func->func_type; - - all_cell_num = (uint64)cur_func->param_cell_num - + (uint64)cur_func->local_cell_num - + (uint64)cur_wasm_func->max_stack_cell_num - + ((uint64)cur_wasm_func->max_block_num) * sizeof(WASMBranchBlock) / 4; - if (all_cell_num >= UINT32_MAX) { - wasm_set_exception(module, "WASM interp failed: stack overflow."); - goto got_exception; - } - - frame_size = wasm_interp_interp_frame_size((uint32)all_cell_num); - if (!(frame = ALLOC_FRAME(exec_env, frame_size, prev_frame))) { - frame = prev_frame; - goto got_exception; - } - - /* Initialize the interpreter context. */ - frame->function = cur_func; - frame_ip = wasm_get_func_code(cur_func); - frame_ip_end = wasm_get_func_code_end(cur_func); - frame_lp = frame->lp; - - frame_sp = frame->sp_bottom = frame_lp + cur_func->param_cell_num - + cur_func->local_cell_num; - frame->sp_boundary = frame->sp_bottom + cur_wasm_func->max_stack_cell_num; - - frame_csp = frame->csp_bottom = (WASMBranchBlock*)frame->sp_boundary; - frame->csp_boundary = frame->csp_bottom + cur_wasm_func->max_block_num; - - /* Initialize the local varialbes */ - memset(frame_lp + cur_func->param_cell_num, 0, - (uint32)(cur_func->local_cell_num * 4)); - - /* Push function block as first block */ - ret_type = func_type->result_count - ? cur_func->param_types[func_type->param_count] - : VALUE_TYPE_VOID; - PUSH_CSP(BLOCK_TYPE_FUNCTION, ret_type, frame_ip_end - 1); - - wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)frame); - } - HANDLE_OP_END (); - } - - return_func: - { - FREE_FRAME(exec_env, frame); - wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)prev_frame); - - if (!prev_frame->ip) - /* Called from native. */ - return; - - RECOVER_CONTEXT(prev_frame); - HANDLE_OP_END (); - } - - out_of_bounds: - wasm_set_exception(module, "out of bounds memory access"); - - got_exception: - return; - -#if WASM_ENABLE_LABELS_AS_VALUES == 0 - } -#else - FETCH_OPCODE_AND_DISPATCH (); -#endif -} - -void -wasm_interp_call_wasm(WASMModuleInstance *module_inst, - WASMExecEnv *exec_env, - WASMFunctionInstance *function, - uint32 argc, uint32 argv[]) -{ - WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); - WASMInterpFrame *frame, *outs_area; - - /* Allocate sufficient cells for all kinds of return values. */ - unsigned all_cell_num = 2, i; - /* 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); - - if (argc != function->param_cell_num) { - char buf[128]; - snprintf(buf, sizeof(buf), - "invalid argument count %d, expected %d", - argc, function->param_cell_num); - wasm_set_exception(module_inst, buf); - return; - } - - /* TODO: check stack overflow. */ - - if (!(frame = ALLOC_FRAME(exec_env, frame_size, (WASMInterpFrame*)prev_frame))) - return; - - outs_area = wasm_exec_env_wasm_stack_top(exec_env); - frame->function = NULL; - frame->ip = NULL; - /* There is no local variable. */ - frame->sp = frame->lp + 0; - - if (argc > 0) - word_copy(outs_area->lp, argv, argc); - - wasm_exec_env_set_cur_frame(exec_env, frame); - - if (function->is_import_func) - wasm_interp_call_func_native(module_inst, exec_env, function, frame); - else - wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); - - /* Output the return value to the caller */ - if (!wasm_get_exception(module_inst)) { - for (i = 0; i < function->ret_cell_num; i++) - argv[i] = *(frame->sp + i - function->ret_cell_num); - } - - wasm_exec_env_set_cur_frame(exec_env, prev_frame); - FREE_FRAME(exec_env, frame); -} diff --git a/core/iwasm/interpreter/wasm_interp.h b/core/iwasm/interpreter/wasm_interp.h index 3d78440652..8ca6fe5f23 100644 --- a/core/iwasm/interpreter/wasm_interp.h +++ b/core/iwasm/interpreter/wasm_interp.h @@ -17,32 +17,73 @@ struct WASMFunctionInstance; struct WASMExecEnv; typedef struct WASMInterpFrame { - /* The frame of the caller that are calling the current function. */ - struct WASMInterpFrame *prev_frame; - - /* The current WASM function. */ - struct WASMFunctionInstance *function; - - /* Instruction pointer of the bytecode array. */ - uint8 *ip; - - /* Operand stack top pointer of the current frame. The bottom of - the stack is the next cell after the last local variable. */ - uint32 *sp_bottom; - uint32 *sp_boundary; - uint32 *sp; - - WASMBranchBlock *csp_bottom; - WASMBranchBlock *csp_boundary; - WASMBranchBlock *csp; - - /* Frame data, the layout is: - lp: param_cell_count + local_cell_count - sp_bottom to sp_boundary: stack of data - csp_bottom to csp_boundary: stack of block - ref to frame end: data types of local vairables and stack data + /* The frame of the caller that are calling the current function. */ + struct WASMInterpFrame *prev_frame; + + /* The current WASM function. */ + struct WASMFunctionInstance *function; + + /* Instruction pointer of the bytecode array. */ + uint8 *ip; + +#if WASM_ENABLE_FAST_JIT != 0 + uint8 *jitted_return_addr; +#endif + +#if WASM_ENABLE_PERF_PROFILING != 0 + uint64 time_started; +#endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* set to true if the callee returns an exception rather than + * result values on the stack + */ + bool exception_raised; + uint32 tag_index; +#if WASM_ENABLE_FAST_INTERP != 0 + /* Number of *currently-active* try-regions on this frame's eh- + * stack. The stack itself lives in the trailing cells of the + * frame's operand[] block — see call_func_from_entry in + * wasm_interp_fast.c where all_cell_num is grown by + * `exception_handler_count` cells per frame. Read+written only by + * the WASM_OP_TRY / CATCH / CATCH_ALL / END / THROW handlers; the + * hot ops (CALL / LOAD / STORE) never touch it, so this field + * stays cold and clusters with exception_raised/tag_index above. */ + uint32 eh_count; +#endif +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Return offset of the first return value of current frame, + the callee will put return values here continuously */ + uint32 ret_offset; + uint32 *lp; +#if WASM_ENABLE_GC != 0 + uint8 *frame_ref; +#endif + uint32 operand[1]; +#else /* else of WASM_ENABLE_FAST_INTERP != 0 */ + /* Operand stack top pointer of the current frame. The bottom of + the stack is the next cell after the last local variable. */ + uint32 *sp_bottom; + uint32 *sp_boundary; + uint32 *sp; + + WASMBranchBlock *csp_bottom; + WASMBranchBlock *csp_boundary; + WASMBranchBlock *csp; + + /** + * Frame data, the layout is: + * lp: parameters and local variables + * sp_bottom to sp_boundary: wasm operand stack + * csp_bottom to csp_boundary: wasm label stack + * frame ref flags: only available for GC + * whether each cell in local and stack area is a GC obj + * jit spill cache: only available for fast jit */ - uint32 lp[1]; + uint32 lp[1]; +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ } WASMInterpFrame; /** @@ -56,15 +97,34 @@ typedef struct WASMInterpFrame { static inline unsigned wasm_interp_interp_frame_size(unsigned all_cell_num) { - return align_uint((uint32)offsetof(WASMInterpFrame, lp) - + all_cell_num * 5, 4); + unsigned frame_size; + +#if WASM_ENABLE_FAST_INTERP == 0 +#if WASM_ENABLE_GC == 0 + frame_size = (uint32)offsetof(WASMInterpFrame, lp) + all_cell_num * 4; +#else + frame_size = + (uint32)offsetof(WASMInterpFrame, lp) + align_uint(all_cell_num * 5, 4); +#endif +#else + frame_size = (uint32)offsetof(WASMInterpFrame, operand) + all_cell_num * 4; +#endif + return align_uint(frame_size, 4); } void wasm_interp_call_wasm(struct WASMModuleInstance *module_inst, struct WASMExecEnv *exec_env, - struct WASMFunctionInstance *function, - uint32 argc, uint32 argv[]); + struct WASMFunctionInstance *function, uint32 argc, + uint32 argv[]); + +#if WASM_ENABLE_GC != 0 +bool +wasm_interp_traverse_gc_rootset(struct WASMExecEnv *exec_env, void *heap); + +uint8 * +wasm_interp_get_frame_ref(WASMInterpFrame *frame); +#endif #ifdef __cplusplus } diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c new file mode 100644 index 0000000000..abde189ebc --- /dev/null +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -0,0 +1,7578 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_interp.h" +#include "bh_log.h" +#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_GC != 0 +#include "../common/gc/gc_object.h" +#include "mem_alloc.h" +#if WASM_ENABLE_STRINGREF != 0 +#include "string_object.h" +#endif +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#include "../libraries/debug-engine/debug_engine.h" +#endif +#if WASM_ENABLE_FAST_JIT != 0 +#include "../fast-jit/jit_compiler.h" +#endif + +typedef int32 CellType_I32; +typedef int64 CellType_I64; +typedef float32 CellType_F32; +typedef float64 CellType_F64; + +#define BR_TABLE_TMP_BUF_LEN 32 + +#if WASM_ENABLE_THREAD_MGR == 0 +#define get_linear_mem_size() linear_mem_size +#else +/** + * Load memory data size in each time boundary check in + * multi-threading mode since it may be changed by other + * threads in memory.grow + */ +#define get_linear_mem_size() GET_LINEAR_MEMORY_SIZE(memory) +#endif + +#if WASM_ENABLE_MEMORY64 == 0 + +#if (!defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0) +#define CHECK_MEMORY_OVERFLOW(bytes) \ + do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + if (disable_bounds_checks || offset1 + bytes <= get_linear_mem_size()) \ + /* If offset1 is in valid range, maddr must also \ + be in valid range, no need to check it again. */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) \ + do { \ + uint64 offset1 = (uint32)(start); \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + if (disable_bounds_checks || offset1 + bytes <= get_linear_mem_size()) \ + /* App heap space is not valid space for \ + bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#else /* else of !defined(OS_ENABLE_HW_BOUND_CHECK) || \ + WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 */ + +#define CHECK_MEMORY_OVERFLOW(bytes) \ + do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + maddr = memory->memory_data + offset1; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) \ + do { \ + uint64 offset1 = (uint32)(start); \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + maddr = memory->memory_data + offset1; \ + } while (0) + +#endif /* end of !defined(OS_ENABLE_HW_BOUND_CHECK) || \ + WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 */ + +#else /* else of WASM_ENABLE_MEMORY64 == 0 */ + +#define CHECK_MEMORY_OVERFLOW(bytes) \ + do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + /* If memory64 is enabled, offset1, offset1 + bytes can overflow */ \ + if (disable_bounds_checks \ + || (offset1 >= offset && offset1 + bytes >= offset1 \ + && offset1 + bytes <= get_linear_mem_size())) \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) \ + do { \ + uint64 offset1 = (uint64)(start); \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + /* If memory64 is enabled, offset1 + bytes can overflow */ \ + if (disable_bounds_checks \ + || (offset1 + bytes >= offset1 \ + && offset1 + bytes <= get_linear_mem_size())) \ + /* App heap space is not valid space for \ + bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#endif /* end of WASM_ENABLE_MEMORY64 == 0 */ + +#define CHECK_ATOMIC_MEMORY_ACCESS() \ + do { \ + if (((uintptr_t)maddr & (((uintptr_t)1 << align) - 1)) != 0) \ + goto unaligned_atomic; \ + } while (0) + +#if WASM_ENABLE_DEBUG_INTERP != 0 +#define TRIGGER_WATCHPOINT_SIGTRAP() \ + do { \ + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP); \ + CHECK_SUSPEND_FLAGS(); \ + } while (0) + +#define CHECK_WATCHPOINT(list, current_addr) \ + do { \ + WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list); \ + while (watchpoint) { \ + WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint); \ + if (watchpoint->addr <= current_addr \ + && watchpoint->addr + watchpoint->length > current_addr) { \ + TRIGGER_WATCHPOINT_SIGTRAP(); \ + } \ + watchpoint = next; \ + } \ + } while (0) + +#define CHECK_READ_WATCHPOINT(addr, offset) \ + CHECK_WATCHPOINT(watch_point_list_read, WASM_ADDR_OFFSET(addr + offset)) +#define CHECK_WRITE_WATCHPOINT(addr, offset) \ + CHECK_WATCHPOINT(watch_point_list_write, WASM_ADDR_OFFSET(addr + offset)) +#else +#define CHECK_READ_WATCHPOINT(addr, offset) (void)0 +#define CHECK_WRITE_WATCHPOINT(addr, offset) (void)0 +#endif + +static inline uint32 +rotl32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n << c) | (n >> ((0 - c) & mask)); +} + +static inline uint32 +rotr32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n >> c) | (n << ((0 - c) & mask)); +} + +static inline uint64 +rotl64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n << c) | (n >> ((0 - c) & mask)); +} + +static inline uint64 +rotr64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n >> c) | (n << ((0 - c) & mask)); +} + +static inline float32 +f32_min(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +static inline float32 +f32_max(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +static inline float64 +f64_min(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +static inline float64 +f64_max(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +static inline uint32 +clz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 0x80000000)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +clz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +ctz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +ctz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +popcount32(uint32 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static inline uint32 +popcount64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static float +local_copysignf(float x, float y) +{ + union { + float f; + uint32 i; + } ux = { x }, uy = { y }; + ux.i &= 0x7fffffff; + ux.i |= uy.i & 0x80000000; + return ux.f; +} + +static double +local_copysign(double x, double y) +{ + union { + double f; + uint64 i; + } ux = { x }, uy = { y }; + ux.i &= UINT64_MAX / 2; + ux.i |= uy.i & 1ULL << 63; + return ux.f; +} + +static uint64 +read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) +{ + uint64 result = 0, byte; + uint32 offset = *p_offset; + uint32 shift = 0; + + while (true) { + byte = buf[offset++]; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + } + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= (~((uint64)0)) << shift; + } + *p_offset = offset; + return result; +} + +#if WASM_ENABLE_GC != 0 +static uint8 * +get_frame_ref(WASMInterpFrame *frame) +{ + WASMFunctionInstance *cur_func = frame->function; + uint32 all_cell_num; + + if (!cur_func) { + /* it's a glue frame created in wasm_interp_call_wasm, + no GC object will be traversed */ + return (uint8 *)frame->lp; + } + else if (!frame->ip) { + /* it's a native method frame created in + wasm_interp_call_func_native */ + all_cell_num = + cur_func->param_cell_num > 2 ? cur_func->param_cell_num : 2; + return (uint8 *)(frame->lp + all_cell_num); + } + else { +#if WASM_ENABLE_JIT == 0 + /* it's a wasm bytecode function frame */ + return (uint8 *)frame->csp_boundary; +#else + return (uint8 *)(frame->lp + cur_func->param_cell_num + + cur_func->local_cell_num + + cur_func->u.func->max_stack_cell_num); +#endif + } +} + +static void +init_frame_refs(uint8 *frame_ref, uint32 cell_num, WASMFunctionInstance *func) +{ + uint32 i, j; + + memset(frame_ref, 0, cell_num); + + for (i = 0, j = 0; i < func->param_count; i++) { + if (wasm_is_type_reftype(func->param_types[i]) + && !wasm_is_reftype_i31ref(func->param_types[i])) { + frame_ref[j++] = 1; +#if UINTPTR_MAX == UINT64_MAX + frame_ref[j++] = 1; +#endif + } + else { + j += wasm_value_type_cell_num(func->param_types[i]); + } + } + + for (i = 0; i < func->local_count; i++) { + if (wasm_is_type_reftype(func->local_types[i]) + && !wasm_is_reftype_i31ref(func->local_types[i])) { + frame_ref[j++] = 1; +#if UINTPTR_MAX == UINT64_MAX + frame_ref[j++] = 1; +#endif + } + else { + j += wasm_value_type_cell_num(func->local_types[i]); + } + } +} + +uint8 * +wasm_interp_get_frame_ref(WASMInterpFrame *frame) +{ + return get_frame_ref(frame); +} + +/* Return the corresponding ref slot of the given address of local + variable or stack pointer. */ + +#define COMPUTE_FRAME_REF(ref, lp, p) (ref + (unsigned)((uint32 *)p - lp)) + +#define FRAME_REF(p) COMPUTE_FRAME_REF(frame_ref, frame_lp, p) + +#define FRAME_REF_FOR(frame, p) \ + COMPUTE_FRAME_REF(get_frame_ref(frame), frame->lp, p) + +#define CLEAR_FRAME_REF(p, n) \ + do { \ + int32 ref_i, ref_n = (int32)(n); \ + uint8 *ref = FRAME_REF(p); \ + for (ref_i = 0; ref_i < ref_n; ref_i++) \ + ref[ref_i] = 0; \ + } while (0) +#else +#define CLEAR_FRAME_REF(p, n) (void)0 +#endif /* end of WASM_ENABLE_GC != 0 */ + +#define skip_leb(p) while (*p++ & 0x80) + +#define PUSH_I32(value) \ + do { \ + *(int32 *)frame_sp++ = (int32)(value); \ + } while (0) + +#define PUSH_F32(value) \ + do { \ + *(float32 *)frame_sp++ = (float32)(value); \ + } while (0) + +#define PUSH_I64(value) \ + do { \ + PUT_I64_TO_ADDR(frame_sp, value); \ + frame_sp += 2; \ + } while (0) + +#define PUSH_F64(value) \ + do { \ + PUT_F64_TO_ADDR(frame_sp, value); \ + frame_sp += 2; \ + } while (0) + +#if UINTPTR_MAX == UINT64_MAX +#define PUSH_REF(value) \ + do { \ + PUT_REF_TO_ADDR(frame_sp, value); \ + frame_ref_tmp = FRAME_REF(frame_sp); \ + *frame_ref_tmp = *(frame_ref_tmp + 1) = 1; \ + frame_sp += 2; \ + } while (0) +#define PUSH_I31REF(value) \ + do { \ + PUT_REF_TO_ADDR(frame_sp, value); \ + frame_sp += 2; \ + } while (0) +#else +#define PUSH_REF(value) \ + do { \ + PUT_REF_TO_ADDR(frame_sp, value); \ + frame_ref_tmp = FRAME_REF(frame_sp); \ + *frame_ref_tmp = 1; \ + frame_sp++; \ + } while (0) +#define PUSH_I31REF(value) \ + do { \ + PUT_REF_TO_ADDR(frame_sp, value); \ + frame_sp++; \ + } while (0) +#endif + +#if UINTPTR_MAX == UINT64_MAX +#define PUSH_PTR(value) PUSH_I64(value) +#else +#define PUSH_PTR(value) PUSH_I32(value) +#endif + +/* in exception handling, label_type needs to be stored to lookup exception + * handlers */ + +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define SET_LABEL_TYPE(_label_type) frame_csp->label_type = _label_type +#else +#define SET_LABEL_TYPE(_label_type) (void)0 +#endif + +#if WASM_ENABLE_MEMORY64 != 0 +#define COND_PUSH_TEMPLATE(cond, value) \ + do { \ + if (cond) { \ + PUT_I64_TO_ADDR(frame_sp, value); \ + frame_sp += 2; \ + } \ + else { \ + *(int32 *)frame_sp++ = (int32)(value); \ + } \ + } while (0) +#define PUSH_MEM_OFFSET(value) COND_PUSH_TEMPLATE(is_memory64, value) +#define PUSH_TBL_ELEM_IDX(value) COND_PUSH_TEMPLATE(is_table64, value) +#else +#define PUSH_MEM_OFFSET(value) PUSH_I32(value) +#define PUSH_TBL_ELEM_IDX(value) PUSH_I32(value) +#endif + +#define PUSH_PAGE_COUNT(value) PUSH_MEM_OFFSET(value) + +#define PUSH_CSP(_label_type, param_cell_num, cell_num, _target_addr) \ + do { \ + bh_assert(frame_csp < frame->csp_boundary); \ + SET_LABEL_TYPE(_label_type); \ + frame_csp->cell_num = cell_num; \ + frame_csp->begin_addr = frame_ip; \ + frame_csp->target_addr = _target_addr; \ + frame_csp->frame_sp = frame_sp - param_cell_num; \ + frame_csp++; \ + } while (0) + +#define POP_I32() (--frame_sp, *(int32 *)frame_sp) + +#define POP_F32() (--frame_sp, *(float32 *)frame_sp) + +#define POP_I64() (frame_sp -= 2, GET_I64_FROM_ADDR(frame_sp)) + +#define POP_F64() (frame_sp -= 2, GET_F64_FROM_ADDR(frame_sp)) + +#if UINTPTR_MAX == UINT64_MAX +#define POP_REF() \ + (frame_sp -= 2, frame_ref_tmp = FRAME_REF(frame_sp), \ + *frame_ref_tmp = *(frame_ref_tmp + 1) = 0, GET_REF_FROM_ADDR(frame_sp)) +#else +#define POP_REF() \ + (frame_sp--, frame_ref_tmp = FRAME_REF(frame_sp), *frame_ref_tmp = 0, \ + GET_REF_FROM_ADDR(frame_sp)) +#endif + +#if WASM_ENABLE_MEMORY64 != 0 +#define POP_MEM_OFFSET() (is_memory64 ? POP_I64() : (uint32)POP_I32()) +#define POP_TBL_ELEM_IDX() (is_table64 ? POP_I64() : (uint32)POP_I32()) +#else +#define POP_MEM_OFFSET() POP_I32() +#define POP_TBL_ELEM_IDX() POP_I32() +#endif + +#define POP_PAGE_COUNT() POP_MEM_OFFSET() + +#define POP_CSP_CHECK_OVERFLOW(n) \ + do { \ + bh_assert(frame_csp - n >= frame->csp_bottom); \ + } while (0) + +#define POP_CSP() \ + do { \ + POP_CSP_CHECK_OVERFLOW(1); \ + --frame_csp; \ + } while (0) + +#define POP_CSP_N(n) \ + do { \ + uint32 *frame_sp_old = frame_sp; \ + uint32 cell_num_to_copy; \ + POP_CSP_CHECK_OVERFLOW(n + 1); \ + frame_csp -= n; \ + frame_ip = (frame_csp - 1)->target_addr; \ + /* copy arity values of block */ \ + frame_sp = (frame_csp - 1)->frame_sp; \ + cell_num_to_copy = (frame_csp - 1)->cell_num; \ + if (cell_num_to_copy > 0) { \ + word_copy(frame_sp, frame_sp_old - cell_num_to_copy, \ + cell_num_to_copy); \ + frame_ref_copy(FRAME_REF(frame_sp), \ + FRAME_REF(frame_sp_old - cell_num_to_copy), \ + cell_num_to_copy); \ + } \ + frame_sp += cell_num_to_copy; \ + CLEAR_FRAME_REF(frame_sp, frame_sp_old - frame_sp); \ + } while (0) + +/* Pop the given number of elements from the given frame's stack. */ +#define POP(N) \ + do { \ + int n = (N); \ + frame_sp -= n; \ + CLEAR_FRAME_REF(frame_sp, n); \ + } while (0) + +#if WASM_ENABLE_EXCE_HANDLING != 0 +/* unwind the CSP to a given label and optionally modify the labeltype */ +#define UNWIND_CSP(N, T) \ + do { \ + /* unwind to function frame */ \ + frame_csp -= N; \ + /* drop handlers and values pushd in try block */ \ + frame_sp = (frame_csp - 1)->frame_sp; \ + (frame_csp - 1)->label_type = T ? T : (frame_csp - 1)->label_type; \ + } while (0) +#endif + +#define SYNC_ALL_TO_FRAME() \ + do { \ + frame->sp = frame_sp; \ + frame->ip = frame_ip; \ + frame->csp = frame_csp; \ + } while (0) + +#define UPDATE_ALL_FROM_FRAME() \ + do { \ + frame_sp = frame->sp; \ + frame_ip = frame->ip; \ + frame_csp = frame->csp; \ + } while (0) + +#define read_leb_int64(p, p_end, res) \ + do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (int64)_val; \ + if (_val & 0x40) \ + /* sign extend */ \ + res |= 0xFFFFFFFFFFFFFF80LL; \ + p++; \ + } \ + else { \ + uint32 _off = 0; \ + res = (int64)read_leb(p, &_off, 64, true); \ + p += _off; \ + } \ + } while (0) + +#define read_leb_uint32(p, p_end, res) \ + do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = _val; \ + p++; \ + } \ + else { \ + uint32 _off = 0; \ + res = (uint32)read_leb(p, &_off, 32, false); \ + p += _off; \ + } \ + } while (0) + +#define read_leb_int32(p, p_end, res) \ + do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (int32)_val; \ + if (_val & 0x40) \ + /* sign extend */ \ + res |= 0xFFFFFF80; \ + p++; \ + } \ + else { \ + uint32 _off = 0; \ + res = (int32)read_leb(p, &_off, 32, true); \ + p += _off; \ + } \ + } while (0) + +#if WASM_ENABLE_MEMORY64 != 0 +#define read_leb_mem_offset(p, p_end, res) \ + do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (mem_offset_t)_val; \ + p++; \ + } \ + else { \ + uint32 _off = 0; \ + res = (mem_offset_t)read_leb(p, &_off, is_memory64 ? 64 : 32, \ + false); \ + p += _off; \ + } \ + } while (0) +#else +#define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif + +#if WASM_ENABLE_MULTI_MEMORY != 0 +/* If the current memidx differs than the last cached one, + * update memory related information */ +#define read_leb_memidx(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (res != memidx_cached) { \ + memory = wasm_get_memory_with_idx(module, res); \ + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); \ + memidx_cached = res; \ + } \ + } while (0) +/* First read the alignment, then if it has flag indicating following memidx, + * read and update memory related information, if it differs than the + * last(cached) one. If it doesn't have flag reset the + * memory instance to the default memories[0] */ +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (!(res & OPT_MEMIDX_FLAG)) \ + memidx = 0; \ + else \ + read_leb_uint32(p, p_end, memidx); \ + if (memidx != memidx_cached) { \ + memory = wasm_get_memory_with_idx(module, memidx); \ + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); \ + memidx_cached = memidx; \ + } \ + } while (0) +#else +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + (void)res; \ + } while (0) +#define read_leb_memidx(p, p_end, res) read_leb_memarg(p, p_end, res) +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 +#define RECOVER_FRAME_IP_END() frame_ip_end = wasm_get_func_code_end(cur_func) +#else +#define RECOVER_FRAME_IP_END() (void)0 +#endif + +#if WASM_ENABLE_GC != 0 +#define RECOVER_FRAME_REF() frame_ref = (uint8 *)frame->csp_boundary +#else +#define RECOVER_FRAME_REF() (void)0 +#endif + +#define RECOVER_CONTEXT(new_frame) \ + do { \ + frame = (new_frame); \ + cur_func = frame->function; \ + prev_frame = frame->prev_frame; \ + frame_ip = frame->ip; \ + RECOVER_FRAME_IP_END(); \ + frame_lp = frame->lp; \ + frame_sp = frame->sp; \ + frame_csp = frame->csp; \ + RECOVER_FRAME_REF(); \ + } while (0) + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#define GET_OPCODE() opcode = *(frame_ip - 1); +#else +#define GET_OPCODE() (void)0 +#endif + +#define DEF_OP_I_CONST(ctype, src_op_type) \ + do { \ + ctype cval; \ + read_leb_##ctype(frame_ip, frame_ip_end, cval); \ + PUSH_##src_op_type(cval); \ + } while (0) + +#define DEF_OP_EQZ(src_op_type) \ + do { \ + int32 pop_val; \ + pop_val = POP_##src_op_type() == 0; \ + PUSH_I32(pop_val); \ + } while (0) + +#define DEF_OP_CMP(src_type, src_op_type, cond) \ + do { \ + uint32 res; \ + src_type val1, val2; \ + val2 = (src_type)POP_##src_op_type(); \ + val1 = (src_type)POP_##src_op_type(); \ + res = val1 cond val2; \ + PUSH_I32(res); \ + } while (0) + +#define DEF_OP_BIT_COUNT(src_type, src_op_type, operation) \ + do { \ + src_type val1, val2; \ + val1 = (src_type)POP_##src_op_type(); \ + val2 = (src_type)operation(val1); \ + PUSH_##src_op_type(val2); \ + } while (0) + +#define DEF_OP_NUMERIC(src_type1, src_type2, src_op_type, operation) \ + do { \ + frame_sp -= sizeof(src_type2) / sizeof(uint32); \ + *(src_type1 *)(frame_sp - sizeof(src_type1) / sizeof(uint32)) \ + operation## = *(src_type2 *)(frame_sp); \ + } while (0) + +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define DEF_OP_NUMERIC_64 DEF_OP_NUMERIC +#else +#define DEF_OP_NUMERIC_64(src_type1, src_type2, src_op_type, operation) \ + do { \ + src_type1 val1; \ + src_type2 val2; \ + frame_sp -= 2; \ + val1 = (src_type1)GET_##src_op_type##_FROM_ADDR(frame_sp - 2); \ + val2 = (src_type2)GET_##src_op_type##_FROM_ADDR(frame_sp); \ + val1 operation## = val2; \ + PUT_##src_op_type##_TO_ADDR(frame_sp - 2, val1); \ + } while (0) +#endif + +#define DEF_OP_NUMERIC2(src_type1, src_type2, src_op_type, operation) \ + do { \ + frame_sp -= sizeof(src_type2) / sizeof(uint32); \ + *(src_type1 *)(frame_sp - sizeof(src_type1) / sizeof(uint32)) \ + operation## = (*(src_type2 *)(frame_sp) % 32); \ + } while (0) + +#define DEF_OP_NUMERIC2_64(src_type1, src_type2, src_op_type, operation) \ + do { \ + src_type1 val1; \ + src_type2 val2; \ + frame_sp -= 2; \ + val1 = (src_type1)GET_##src_op_type##_FROM_ADDR(frame_sp - 2); \ + val2 = (src_type2)GET_##src_op_type##_FROM_ADDR(frame_sp); \ + val1 operation## = (val2 % 64); \ + PUT_##src_op_type##_TO_ADDR(frame_sp - 2, val1); \ + } while (0) + +#define DEF_OP_MATH(src_type, src_op_type, method) \ + do { \ + src_type src_val; \ + src_val = POP_##src_op_type(); \ + PUSH_##src_op_type(method(src_val)); \ + } while (0) + +#define TRUNC_FUNCTION(func_name, src_type, dst_type, signed_type) \ + static dst_type func_name(src_type src_value, src_type src_min, \ + src_type src_max, dst_type dst_min, \ + dst_type dst_max, bool is_sign) \ + { \ + dst_type dst_value = 0; \ + if (!isnan(src_value)) { \ + if (src_value <= src_min) \ + dst_value = dst_min; \ + else if (src_value >= src_max) \ + dst_value = dst_max; \ + else { \ + if (is_sign) \ + dst_value = (dst_type)(signed_type)src_value; \ + else \ + dst_value = (dst_type)src_value; \ + } \ + } \ + return dst_value; \ + } + +TRUNC_FUNCTION(trunc_f32_to_i32, float32, uint32, int32) +TRUNC_FUNCTION(trunc_f32_to_i64, float32, uint64, int64) +TRUNC_FUNCTION(trunc_f64_to_i32, float64, uint32, int32) +TRUNC_FUNCTION(trunc_f64_to_i64, float64, uint64, int64) + +static bool +trunc_f32_to_int(WASMModuleInstance *module, uint32 *frame_sp, float32 src_min, + float32 src_max, bool saturating, bool is_i32, bool is_sign) +{ + float32 src_value = POP_F32(); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return false; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return false; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f32_to_i32(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + PUSH_I32(dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f32_to_i64(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + PUSH_I64(dst_value_i64); + } + return true; +} + +static bool +trunc_f64_to_int(WASMModuleInstance *module, uint32 *frame_sp, float64 src_min, + float64 src_max, bool saturating, bool is_i32, bool is_sign) +{ + float64 src_value = POP_F64(); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return false; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return false; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f64_to_i32(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + PUSH_I32(dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f64_to_i64(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + PUSH_I64(dst_value_i64); + } + return true; +} + +#define DEF_OP_TRUNC_F32(min, max, is_i32, is_sign) \ + do { \ + if (!trunc_f32_to_int(module, frame_sp, min, max, false, is_i32, \ + is_sign)) \ + goto got_exception; \ + } while (0) + +#define DEF_OP_TRUNC_F64(min, max, is_i32, is_sign) \ + do { \ + if (!trunc_f64_to_int(module, frame_sp, min, max, false, is_i32, \ + is_sign)) \ + goto got_exception; \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F32(min, max, is_i32, is_sign) \ + do { \ + (void)trunc_f32_to_int(module, frame_sp, min, max, true, is_i32, \ + is_sign); \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F64(min, max, is_i32, is_sign) \ + do { \ + (void)trunc_f64_to_int(module, frame_sp, min, max, true, is_i32, \ + is_sign); \ + } while (0) + +#define DEF_OP_CONVERT(dst_type, dst_op_type, src_type, src_op_type) \ + do { \ + dst_type value = (dst_type)(src_type)POP_##src_op_type(); \ + PUSH_##dst_op_type(value); \ + } while (0) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() \ + do { \ + uint32 param_count = cur_func->param_count; \ + read_leb_uint32(frame_ip, frame_ip_end, local_idx); \ + bh_assert(local_idx < param_count + cur_func->local_count); \ + local_offset = cur_func->local_offsets[local_idx]; \ + if (local_idx < param_count) \ + local_type = cur_func->param_types[local_idx]; \ + else \ + local_type = cur_func->local_types[local_idx - param_count]; \ + } while (0) + +#define DEF_ATOMIC_RMW_OPCODE(OP_NAME, op) \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U: \ + { \ + uint32 readv, sval; \ + \ + sval = POP_I32(); \ + addr = POP_MEM_OFFSET(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U) { \ + CHECK_MEMORY_OVERFLOW(1); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = (uint32)(*(uint8 *)maddr); \ + *(uint8 *)maddr = (uint8)(readv op sval); \ + shared_memory_unlock(memory); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U) { \ + CHECK_MEMORY_OVERFLOW(2); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = (uint32)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + shared_memory_unlock(memory); \ + } \ + else { \ + CHECK_MEMORY_OVERFLOW(4); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = LOAD_I32(maddr); \ + STORE_U32(maddr, readv op sval); \ + shared_memory_unlock(memory); \ + } \ + PUSH_I32(readv); \ + break; \ + } \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U: \ + { \ + uint64 readv, sval; \ + \ + sval = (uint64)POP_I64(); \ + addr = POP_MEM_OFFSET(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U) { \ + CHECK_MEMORY_OVERFLOW(1); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)(*(uint8 *)maddr); \ + *(uint8 *)maddr = (uint8)(readv op sval); \ + shared_memory_unlock(memory); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U) { \ + CHECK_MEMORY_OVERFLOW(2); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + shared_memory_unlock(memory); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U) { \ + CHECK_MEMORY_OVERFLOW(4); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)LOAD_U32(maddr); \ + STORE_U32(maddr, (uint32)(readv op sval)); \ + shared_memory_unlock(memory); \ + } \ + else { \ + uint64 op_result; \ + CHECK_MEMORY_OVERFLOW(8); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)LOAD_I64(maddr); \ + op_result = readv op sval; \ + STORE_I64(maddr, op_result); \ + shared_memory_unlock(memory); \ + } \ + PUSH_I64(readv); \ + break; \ + } + +static inline int32 +sign_ext_8_32(int8 val) +{ + if (val & 0x80) + return (int32)val | (int32)0xffffff00; + return val; +} + +static inline int32 +sign_ext_16_32(int16 val) +{ + if (val & 0x8000) + return (int32)val | (int32)0xffff0000; + return val; +} + +static inline int64 +sign_ext_8_64(int8 val) +{ + if (val & 0x80) + return (int64)val | (int64)0xffffffffffffff00LL; + return val; +} + +static inline int64 +sign_ext_16_64(int16 val) +{ + if (val & 0x8000) + return (int64)val | (int64)0xffffffffffff0000LL; + return val; +} + +static inline int64 +sign_ext_32_64(int32 val) +{ + if (val & (int32)0x80000000) + return (int64)val | (int64)0xffffffff00000000LL; + return val; +} + +static inline void +word_copy(uint32 *dest, uint32 *src, unsigned num) +{ + bh_assert(dest != NULL); + bh_assert(src != NULL); + bh_assert(num > 0); + if (dest != src) { + /* No overlap buffer */ + bh_assert(!((src < dest) && (dest < src + num))); + for (; num > 0; num--) + *dest++ = *src++; + } +} + +#if WASM_ENABLE_GC != 0 +static inline void +frame_ref_copy(uint8 *frame_ref_dest, uint8 *frame_ref_src, unsigned num) +{ + if (frame_ref_dest != frame_ref_src) + for (; num > 0; num--) + *frame_ref_dest++ = *frame_ref_src++; +} +#else +#define frame_ref_copy(frame_ref_dst, frame_ref_src, num) (void)0 +#endif + +static inline WASMInterpFrame * +ALLOC_FRAME(WASMExecEnv *exec_env, uint32 size, WASMInterpFrame *prev_frame) +{ + WASMInterpFrame *frame = wasm_exec_env_alloc_wasm_frame(exec_env, size); + + if (frame) { + frame->prev_frame = prev_frame; +#if WASM_ENABLE_PERF_PROFILING != 0 + frame->time_started = os_time_thread_cputime_us(); +#endif + } + else { + wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, + "wasm operand stack overflow"); + } + + return frame; +} + +static inline void +FREE_FRAME(WASMExecEnv *exec_env, WASMInterpFrame *frame) +{ +#if WASM_ENABLE_PERF_PROFILING != 0 + if (frame->function) { + WASMInterpFrame *prev_frame = frame->prev_frame; + uint64 time_elapsed = os_time_thread_cputime_us() - frame->time_started; + + frame->function->total_exec_time += time_elapsed; + frame->function->total_exec_cnt++; + + if (prev_frame && prev_frame->function) + prev_frame->function->children_exec_time += time_elapsed; + } +#endif + wasm_exec_env_free_wasm_frame(exec_env, frame); +} + +static void +wasm_interp_call_func_native(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMFunctionImport *func_import = cur_func->u.func_import; + CApiFuncImport *c_api_func_import = NULL; + unsigned local_cell_num = + cur_func->param_cell_num > 2 ? cur_func->param_cell_num : 2; + unsigned all_cell_num; + WASMInterpFrame *frame; + uint32 argv_ret[2], cur_func_index; + void *native_func_pointer = NULL; + char buf[128]; + bool ret; +#if WASM_ENABLE_GC != 0 + WASMFuncType *func_type; + uint8 *frame_ref; +#endif + + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + + all_cell_num = local_cell_num; +#if WASM_ENABLE_GC != 0 + all_cell_num += (local_cell_num + 3) / 4; +#endif + + if (!(frame = + ALLOC_FRAME(exec_env, wasm_interp_interp_frame_size(all_cell_num), + prev_frame))) + return; + + frame->function = cur_func; + frame->ip = NULL; + frame->sp = frame->lp + local_cell_num; +#if WASM_ENABLE_GC != 0 + /* native function doesn't have operand stack and label stack */ + frame_ref = (uint8 *)frame->sp; + init_frame_refs(frame_ref, local_cell_num, cur_func); +#endif + + wasm_exec_env_set_cur_frame(exec_env, frame); + + cur_func_index = (uint32)(cur_func - module_inst->e->functions); + bh_assert(cur_func_index < module_inst->module->import_function_count); + if (!func_import->call_conv_wasm_c_api) { + native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; + } + else if (module_inst->c_api_func_imports) { + c_api_func_import = module_inst->c_api_func_imports + cur_func_index; + native_func_pointer = c_api_func_import->func_ptr_linked; + } + + if (!native_func_pointer) { + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + if (func_import->call_conv_wasm_c_api) { + ret = wasm_runtime_invoke_c_api_native( + (WASMModuleInstanceCommon *)module_inst, native_func_pointer, + func_import->func_type, cur_func->param_cell_num, frame->lp, + c_api_func_import->with_env_arg, c_api_func_import->env_arg); + if (ret) { + argv_ret[0] = frame->lp[0]; + argv_ret[1] = frame->lp[1]; + } + } + else if (!func_import->call_conv_raw) { + ret = wasm_runtime_invoke_native( + exec_env, native_func_pointer, func_import->func_type, + func_import->signature, func_import->attachment, frame->lp, + cur_func->param_cell_num, argv_ret); + } + else { + ret = wasm_runtime_invoke_native_raw( + exec_env, native_func_pointer, func_import->func_type, + func_import->signature, func_import->attachment, frame->lp, + cur_func->param_cell_num, argv_ret); + } + + if (!ret) + return; + +#if WASM_ENABLE_GC != 0 + func_type = cur_func->u.func_import->func_type; + if (func_type->result_count + && wasm_is_type_reftype(func_type->types[cur_func->param_count])) { + frame_ref = (uint8 *)prev_frame->csp_boundary + + (unsigned)(uintptr_t)(prev_frame->sp - prev_frame->lp); + if (!wasm_is_reftype_i31ref(func_type->types[cur_func->param_count])) { +#if UINTPTR_MAX == UINT64_MAX + *frame_ref = *(frame_ref + 1) = 1; +#else + *frame_ref = 1; +#endif + } + } +#endif + + if (cur_func->ret_cell_num == 1) { + prev_frame->sp[0] = argv_ret[0]; + prev_frame->sp++; + } + else if (cur_func->ret_cell_num == 2) { + prev_frame->sp[0] = argv_ret[0]; + prev_frame->sp[1] = argv_ret[1]; + prev_frame->sp += 2; + } + + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); +} + +#if WASM_ENABLE_FAST_JIT != 0 +bool +fast_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + WASMFunctionInstance *cur_func = module_inst->e->functions + func_idx; + + wasm_interp_call_func_native(module_inst, exec_env, cur_func, prev_frame); + return wasm_copy_exception(module_inst, NULL) ? false : true; +} +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame); + +static void +wasm_interp_call_func_import(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *sub_module_inst = cur_func->import_module_inst; + WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst; + WASMFunctionImport *func_import = cur_func->u.func_import; + uint8 *ip = prev_frame->ip; + char buf[128]; + WASMExecEnv *sub_module_exec_env = NULL; + uintptr_t aux_stack_origin_boundary = 0; + uintptr_t aux_stack_origin_bottom = 0; + + /* + * perform stack overflow check before calling + * wasm_interp_call_func_bytecode recursively. + */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + + if (!sub_func_inst) { + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + /* Switch exec_env but keep using the same one by replacing necessary + * variables */ + sub_module_exec_env = wasm_runtime_get_exec_env_singleton( + (WASMModuleInstanceCommon *)sub_module_inst); + if (!sub_module_exec_env) { + wasm_set_exception(module_inst, "create singleton exec_env failed"); + return; + } + + /* - module_inst */ + wasm_exec_env_set_module_inst(exec_env, + (WASMModuleInstanceCommon *)sub_module_inst); + /* - aux_stack_boundary */ + aux_stack_origin_boundary = exec_env->aux_stack_boundary; + exec_env->aux_stack_boundary = sub_module_exec_env->aux_stack_boundary; + /* - aux_stack_bottom */ + aux_stack_origin_bottom = exec_env->aux_stack_bottom; + exec_env->aux_stack_bottom = sub_module_exec_env->aux_stack_bottom; + + /* set ip NULL to make call_func_bytecode return after executing + this function */ + prev_frame->ip = NULL; + + /* call function of sub-module*/ + wasm_interp_call_func_bytecode(sub_module_inst, exec_env, sub_func_inst, + prev_frame); + + /* restore ip and other replaced */ + prev_frame->ip = ip; + exec_env->aux_stack_boundary = aux_stack_origin_boundary; + exec_env->aux_stack_bottom = aux_stack_origin_bottom; + wasm_exec_env_restore_module_inst(exec_env, + (WASMModuleInstanceCommon *)module_inst); +} +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +#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_waiting_run(exec_env); \ + } \ + os_mutex_unlock(&exec_env->wait_lock); \ + } while (0) +#else +#if WASM_SUSPEND_FLAGS_IS_ATOMIC != 0 +/* The lock is only needed when the suspend_flags is atomic; otherwise + the lock is already taken at the time when SUSPENSION_LOCK() is called. */ +#define SUSPENSION_LOCK() os_mutex_lock(&exec_env->wait_lock); +#define SUSPENSION_UNLOCK() os_mutex_unlock(&exec_env->wait_lock); +#else +#define SUSPENSION_LOCK() +#define SUSPENSION_UNLOCK() +#endif + +#define CHECK_SUSPEND_FLAGS() \ + do { \ + WASM_SUSPEND_FLAGS_LOCK(exec_env->wait_lock); \ + if (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) \ + & WASM_SUSPEND_FLAG_TERMINATE) { \ + /* terminate current thread */ \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ + return; \ + } \ + while (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) \ + & WASM_SUSPEND_FLAG_SUSPEND) { \ + /* suspend current thread */ \ + SUSPENSION_LOCK() \ + os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock); \ + SUSPENSION_UNLOCK() \ + } \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ + } while (0) +#endif /* WASM_ENABLE_DEBUG_INTERP */ +#endif /* WASM_ENABLE_THREAD_MGR */ + +#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 +#if BH_ATOMIC_32_IS_ATOMIC != 0 +#define GET_SIGNAL_FLAG() \ + do { \ + signal_flag = \ + BH_ATOMIC_32_LOAD(exec_env->current_status->signal_flag); \ + } while (0) +#else +#define GET_SIGNAL_FLAG() \ + do { \ + os_mutex_lock(&exec_env->wait_lock); \ + signal_flag = exec_env->current_status->signal_flag; \ + os_mutex_unlock(&exec_env->wait_lock); \ + } while (0) +#endif +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + +#define HANDLE_OP(opcode) HANDLE_##opcode: +#define FETCH_OPCODE_AND_DISPATCH() goto *handle_table[*frame_ip++] + +#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 +#define HANDLE_OP_END() \ + do { \ + /* Record the current frame_ip, so when exception occurs, \ + debugger can know the exact opcode who caused the exception */ \ + frame_ip_orig = frame_ip; \ + /* Atomic load the exec_env's signal_flag first, and then handle \ + more with lock if it is WAMR_SIG_SINGSTEP */ \ + GET_SIGNAL_FLAG(); \ + if (signal_flag == WAMR_SIG_SINGSTEP) { \ + os_mutex_lock(&exec_env->wait_lock); \ + while (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP \ + && exec_env->current_status->step_count++ == 1) { \ + exec_env->current_status->step_count = 0; \ + SYNC_ALL_TO_FRAME(); \ + wasm_cluster_thread_waiting_run(exec_env); \ + } \ + os_mutex_unlock(&exec_env->wait_lock); \ + } \ + CHECK_INSTRUCTION_LIMIT(); \ + goto *handle_table[*frame_ip++]; \ + } while (0) +#else +#define HANDLE_OP_END() \ + CHECK_INSTRUCTION_LIMIT(); \ + FETCH_OPCODE_AND_DISPATCH() +#endif + +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#define HANDLE_OP(opcode) case opcode: +#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0 +#define HANDLE_OP_END() \ + /* Record the current frame_ip, so when exception occurs, \ + debugger can know the exact opcode who caused the exception */ \ + frame_ip_orig = frame_ip; \ + /* Atomic load the exec_env's signal_flag first, and then handle \ + more with lock if it is WAMR_SIG_SINGSTEP */ \ + GET_SIGNAL_FLAG(); \ + if (signal_flag == WAMR_SIG_SINGSTEP) { \ + os_mutex_lock(&exec_env->wait_lock); \ + while (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP \ + && exec_env->current_status->step_count++ == 1) { \ + exec_env->current_status->step_count = 0; \ + SYNC_ALL_TO_FRAME(); \ + wasm_cluster_thread_waiting_run(exec_env); \ + } \ + os_mutex_unlock(&exec_env->wait_lock); \ + } \ + CHECK_INSTRUCTION_LIMIT(); \ + continue; +#else +#define HANDLE_OP_END() \ + CHECK_INSTRUCTION_LIMIT(); \ + continue; +#endif + +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +static inline uint8 * +get_global_addr(uint8 *global_data, WASMGlobalInstance *global) +{ +#if WASM_ENABLE_MULTI_MODULE == 0 + return global_data + global->data_offset; +#else + return global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif +} + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +#define CHECK_INSTRUCTION_LIMIT() \ + if (instructions_left == 0) { \ + wasm_set_exception(module, "instruction limit exceeded"); \ + goto got_exception; \ + } \ + else if (instructions_left > 0) \ + instructions_left--; +#else +#define CHECK_INSTRUCTION_LIMIT() (void)0 +#endif + +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + 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_OPT != 0 + uint64 linear_mem_size = 0; + if (memory) +#if WASM_ENABLE_THREAD_MGR == 0 + linear_mem_size = memory->memory_data_size; +#else + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); +#endif +#endif + WASMFuncType **wasm_types = (WASMFuncType **)module->module->types; + WASMGlobalInstance *globals = module->e->globals, *global; + uint8 *global_data = module->global_data; + uint8 opcode_IMPDEP = WASM_OP_IMPDEP; + WASMInterpFrame *frame = NULL; + /* Points to this special opcode so as to jump to the + * call_method_from_entry. */ + register uint8 *frame_ip = &opcode_IMPDEP; /* cache of frame->ip */ + register uint32 *frame_lp = NULL; /* cache of frame->lp */ + register uint32 *frame_sp = NULL; /* cache of frame->sp */ +#if WASM_ENABLE_GC != 0 + register uint8 *frame_ref = NULL; /* cache of frame->ref */ + uint8 *frame_ref_tmp; +#endif + WASMBranchBlock *frame_csp = NULL; + BlockAddr *cache_items; + uint8 *frame_ip_end = frame_ip + 1; + uint8 opcode; + uint32 i, depth, cond, count, fidx, tidx, lidx, frame_size = 0; + uint32 all_cell_num = 0; + tbl_elem_idx_t val; + uint8 *else_addr, *end_addr, *maddr = NULL; + uint32 local_idx, local_offset, global_idx; + uint8 local_type, *global_addr; + uint32 cache_index, type_index, param_cell_num, cell_num; + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + int instructions_left = -1; + if (exec_env) { + instructions_left = exec_env->instructions_to_execute; + } +#endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 + int32_t exception_tag_index; +#endif + uint8 value_type; +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + bool disable_bounds_checks = !wasm_runtime_is_bounds_checks_enabled( + (WASMModuleInstanceCommon *)module); +#else + bool disable_bounds_checks = false; +#endif +#endif +#if WASM_ENABLE_GC != 0 + WASMObjectRef gc_obj; + WASMStructObjectRef struct_obj; + WASMArrayObjectRef array_obj; + WASMFuncObjectRef func_obj; + WASMI31ObjectRef i31_obj; + WASMExternrefObjectRef externref_obj; +#if WASM_ENABLE_STRINGREF != 0 + WASMString str_obj = NULL; + WASMStringrefObjectRef stringref_obj; + WASMStringviewWTF8ObjectRef stringview_wtf8_obj; + WASMStringviewWTF16ObjectRef stringview_wtf16_obj; + WASMStringviewIterObjectRef stringview_iter_obj; +#endif +#endif +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + bool is_return_call = false; +#endif +#if WASM_ENABLE_MEMORY64 != 0 + /* TODO: multi-memories for now assuming the memory idx type is consistent + * across multi-memories */ + bool is_memory64 = false; + bool is_table64 = false; + if (memory) + is_memory64 = memory->is_memory64; +#endif +#if WASM_ENABLE_MULTI_MEMORY != 0 + uint32 memidx = 0; + uint32 memidx_cached = (uint32)-1; +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + uint8 *frame_ip_orig = NULL; + WASMDebugInstance *debug_instance = wasm_exec_env_get_instance(exec_env); + bh_list *watch_point_list_read = + debug_instance ? &debug_instance->watch_point_list_read : NULL; + bh_list *watch_point_list_write = + debug_instance ? &debug_instance->watch_point_list_write : NULL; +#if WASM_ENABLE_THREAD_MGR != 0 + uint32 signal_flag; +#endif +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#define HANDLE_OPCODE(op) &&HANDLE_##op + DEFINE_GOTO_TABLE(const void *, handle_table); +#undef HANDLE_OPCODE +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + while (frame_ip < frame_ip_end) { + opcode = *frame_ip++; + switch (opcode) { +#else + FETCH_OPCODE_AND_DISPATCH(); +#endif + /* control instructions */ + HANDLE_OP(WASM_OP_UNREACHABLE) + { + wasm_set_exception(module, "unreachable"); + goto got_exception; + } + + HANDLE_OP(WASM_OP_NOP) + { + HANDLE_OP_END(); + } + +#if WASM_ENABLE_EXCE_HANDLING != 0 + HANDLE_OP(WASM_OP_RETHROW) + { + int32_t relative_depth; + read_leb_int32(frame_ip, frame_ip_end, relative_depth); + + /* No frame found with exception handler; validation should + * catch it */ + bh_assert(frame_csp >= frame->csp_bottom + relative_depth); + + /* go up the frame stack */ + WASMBranchBlock *tgtframe = (frame_csp - 1) - relative_depth; + + bh_assert(tgtframe->label_type == LABEL_TYPE_CATCH + || tgtframe->label_type == LABEL_TYPE_CATCH_ALL); + + /* tgtframe points to the frame containing a thrown + * exception */ + + uint32 *tgtframe_sp = tgtframe->frame_sp; + + /* frame sp of tgtframe points to caught exception */ + exception_tag_index = *((uint32 *)tgtframe_sp); + tgtframe_sp++; + + uint32 cell_num_to_copy = 0; + + if (IS_INVALID_TAGINDEX(exception_tag_index)) { + /* + * Cross-module exception with unknown tag. + * No parameters to copy - just re-throw with + * the invalid tag index so find_a_catch_handler + * routes it to CATCH_ALL. + */ + cell_num_to_copy = 0; + } + else { + /* get tag type */ + uint8 tag_type_index = + module->module->tags[exception_tag_index]->type; + cell_num_to_copy = + wasm_types[tag_type_index]->param_cell_num; + } + + /* move exception parameters (if there are any) onto top + * of stack */ + if (cell_num_to_copy > 0) { + word_copy(frame_sp, tgtframe_sp - cell_num_to_copy, + cell_num_to_copy); + } + + frame_sp += cell_num_to_copy; + goto find_a_catch_handler; + } + + HANDLE_OP(WASM_OP_THROW) + { + read_leb_int32(frame_ip, frame_ip_end, exception_tag_index); + + /* landing pad for the rethrow ? */ + find_a_catch_handler: + { + WASMFuncType *tag_type = NULL; + uint32 cell_num_to_copy = 0; + if (IS_INVALID_TAGINDEX(exception_tag_index)) { + /* + * invalid exception index, + * generated if a submodule throws an exception + * that has not been imported here + * + * This should result in a branch to the CATCH_ALL block, + * if there is one + */ + tag_type = NULL; + cell_num_to_copy = 0; + } + else { + if (module->e->tags[exception_tag_index].is_import_tag) { + tag_type = module->e->tags[exception_tag_index] + .u.tag_import->tag_type; + } + else { + tag_type = module->e->tags[exception_tag_index] + .u.tag->tag_type; + } + cell_num_to_copy = tag_type->param_cell_num; + } + + /* browse through frame stack */ + uint32 relative_depth = 0; + do { + POP_CSP_CHECK_OVERFLOW(relative_depth - 1); + WASMBranchBlock *tgtframe = frame_csp - relative_depth - 1; + + switch (tgtframe->label_type) { + case LABEL_TYPE_BLOCK: + case LABEL_TYPE_IF: + case LABEL_TYPE_LOOP: + case LABEL_TYPE_CATCH: + case LABEL_TYPE_CATCH_ALL: + /* + * skip that blocks in search + * BLOCK, IF and LOOP do not contain handlers and + * cannot catch exceptions. + * blocks marked as CATCH or + * CATCH_ALL did already caught an exception and can + * only be a target for RETHROW, but cannot catch an + * exception again + */ + break; + case LABEL_TYPE_TRY: + { + uint32 handler_number = 0; + uint8 **handlers = (uint8 **)tgtframe->frame_sp; + uint8 *handler = NULL; + while ((handler = handlers[handler_number]) != 0) { + uint8 handler_opcode = *handler; + uint8 *target_addr = + handler + + 1; /* first instruction or leb-immediate + behind the handler opcode */ + switch (handler_opcode) { + case WASM_OP_CATCH: + { + int32 lookup_index = 0; + /* read the tag_index and advance + * target_addr to the first instruction + * in the block */ + read_leb_int32(target_addr, 0, + lookup_index); + + if (exception_tag_index + == lookup_index) { + /* set ip */ + frame_ip = target_addr; + /* save frame_sp (points to + * exception values) */ + uint32 *frame_sp_old = frame_sp; + + UNWIND_CSP(relative_depth, + LABEL_TYPE_CATCH); + + /* push exception_tag_index and + * exception values for rethrow */ + PUSH_I32(exception_tag_index); + if (cell_num_to_copy > 0) { + word_copy( + frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + /* push exception values for + * catch + */ + word_copy( + frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + } + + /* advance to handler */ + HANDLE_OP_END(); + } + break; + } + case WASM_OP_DELEGATE: + { + int32 lookup_depth = 0; + /* read the depth */ + read_leb_int32(target_addr, 0, + lookup_depth); + + /* save frame_sp (points to exception + * values) */ + uint32 *frame_sp_old = frame_sp; + + UNWIND_CSP(relative_depth, + LABEL_TYPE_CATCH); + + /* leave the block (the delegate is + * technically not inside the frame) */ + frame_csp--; + + /* unwind to delegated frame */ + frame_csp -= lookup_depth; + + /* push exception values for catch */ + if (cell_num_to_copy > 0) { + word_copy(frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + } + + /* tag_index is already stored in + * exception_tag_index */ + goto find_a_catch_handler; + } + case WASM_OP_CATCH_ALL: + { + /* no immediate */ + /* save frame_sp (points to exception + * values) */ + uint32 *frame_sp_old = frame_sp; + /* set ip */ + frame_ip = target_addr; + + UNWIND_CSP(relative_depth, + LABEL_TYPE_CATCH_ALL); + + /* push exception_tag_index and + * exception values for rethrow */ + PUSH_I32(exception_tag_index); + if (cell_num_to_copy > 0) { + word_copy(frame_sp, + frame_sp_old + - cell_num_to_copy, + cell_num_to_copy); + frame_sp += cell_num_to_copy; + } + /* catch_all has no exception values */ + + /* advance to handler */ + HANDLE_OP_END(); + } + default: + wasm_set_exception( + module, "WASM_OP_THROW found " + "unexpected handler type"); + goto got_exception; + } + handler_number++; + } + /* exception not caught in this frame */ + break; + } + case LABEL_TYPE_FUNCTION: + { + /* save frame_sp (points to exception values) */ + uint32 *frame_sp_old = frame_sp; + + UNWIND_CSP(relative_depth, LABEL_TYPE_FUNCTION); + /* push exception values for catch + * The values are copied to the CALLER FRAME + * (prev_frame->sp) same behavior ad WASM_OP_RETURN + */ + if (cell_num_to_copy > 0) { + word_copy(prev_frame->sp, + frame_sp_old - cell_num_to_copy, + cell_num_to_copy); + prev_frame->sp += cell_num_to_copy; + } + *((int32 *)(prev_frame->sp)) = exception_tag_index; + prev_frame->sp++; + + /* mark frame as raised exception */ + wasm_set_exception(module, + "uncaught wasm exception"); + + /* end of function, treat as WASM_OP_RETURN */ + goto return_func; + } + default: + wasm_set_exception( + module, + "unexpected or invalid label in THROW or " + "RETHROW when searching a catch handler"); + goto got_exception; + } + + relative_depth++; + + } while (1); + } + + /* something went wrong. normally, we should always find the + * func label. if not, stop the interpreter */ + wasm_set_exception( + module, "WASM_OP_THROW hit the bottom of the frame stack"); + goto got_exception; + } + + HANDLE_OP(EXT_OP_TRY) + { + /* read the blocktype */ + read_leb_uint32(frame_ip, frame_ip_end, type_index); + param_cell_num = wasm_types[type_index]->param_cell_num; + cell_num = wasm_types[type_index]->ret_cell_num; + goto handle_op_try; + } + + HANDLE_OP(WASM_OP_TRY) + { + value_type = *frame_ip++; + param_cell_num = 0; + cell_num = wasm_value_type_cell_num(value_type); + + handle_op_try: + + cache_index = ((uintptr_t)frame_ip) + & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + cache_items = exec_env->block_addr_cache[cache_index]; + if (cache_items[0].start_addr == frame_ip) { + cache_items[0].start_addr = 0; + } + if (cache_items[1].start_addr == frame_ip) { + cache_items[1].start_addr = 0; + } + + /* start at the first opcode following the try and its blocktype + */ + uint8 *lookup_cursor = frame_ip; + uint8 handler_opcode = WASM_OP_UNREACHABLE; + + /* target_addr filled in when END or DELEGATE is found */ + PUSH_CSP(LABEL_TYPE_TRY, param_cell_num, cell_num, 0); + + /* reset to begin of block */ + lookup_cursor = frame_ip; + do { + /* lookup the next CATCH, CATCH_ALL or END for this TRY */ + if (!wasm_loader_find_block_addr( + exec_env, (BlockAddr *)exec_env->block_addr_cache, + lookup_cursor, (uint8 *)-1, LABEL_TYPE_TRY, + &else_addr, &end_addr)) { + /* something went wrong */ + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + + /* place cursor for continuation past opcode */ + lookup_cursor = end_addr + 1; + + /* end_addr points to CATCH, CATCH_ALL, DELEGATE or END */ + handler_opcode = *end_addr; + switch (handler_opcode) { + case WASM_OP_CATCH: + skip_leb(lookup_cursor); /* skip tag_index */ + PUSH_PTR(end_addr); + break; + case WASM_OP_CATCH_ALL: + PUSH_PTR(end_addr); + break; + case WASM_OP_DELEGATE: + skip_leb(lookup_cursor); /* skip depth */ + PUSH_PTR(end_addr); + /* patch target_addr */ + (frame_csp - 1)->target_addr = lookup_cursor; + break; + case WASM_OP_END: + PUSH_PTR(0); + /* patch target_addr */ + (frame_csp - 1)->target_addr = end_addr; + break; + default: + /* something went wrong */ + wasm_set_exception(module, + "find block address returned an " + "unexpected opcode"); + goto got_exception; + } + /* ... search until the returned address is the END of the + * TRY block */ + } while (handler_opcode != WASM_OP_END + && handler_opcode != WASM_OP_DELEGATE); + /* handler setup on stack complete */ + + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_CATCH) + { + /* skip the tag_index */ + skip_leb(frame_ip); + /* leave the frame */ + POP_CSP_N(0); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_CATCH_ALL) + { + /* leave the frame */ + POP_CSP_N(0); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_DELEGATE) + { + /* skip the delegate depth */ + skip_leb(frame_ip); + /* leave the frame like WASM_OP_END */ + POP_CSP(); + HANDLE_OP_END(); + } +#endif /* end of WASM_ENABLE_EXCE_HANDLING != 0 */ + HANDLE_OP(EXT_OP_BLOCK) + { + read_leb_uint32(frame_ip, frame_ip_end, type_index); + param_cell_num = + ((WASMFuncType *)wasm_types[type_index])->param_cell_num; + cell_num = + ((WASMFuncType *)wasm_types[type_index])->ret_cell_num; + goto handle_op_block; + } + + HANDLE_OP(WASM_OP_BLOCK) + { + value_type = *frame_ip++; + param_cell_num = 0; + cell_num = wasm_value_type_cell_num(value_type); + handle_op_block: + cache_index = ((uintptr_t)frame_ip) + & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + cache_items = exec_env->block_addr_cache[cache_index]; + if (cache_items[0].start_addr == frame_ip) { + end_addr = cache_items[0].end_addr; + } + else if (cache_items[1].start_addr == frame_ip) { + end_addr = cache_items[1].end_addr; + } +#if WASM_ENABLE_DEBUG_INTERP != 0 + else if (!wasm_loader_find_block_addr( + exec_env, (BlockAddr *)exec_env->block_addr_cache, + frame_ip, (uint8 *)-1, LABEL_TYPE_BLOCK, + &else_addr, &end_addr)) { + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } +#endif + else { + end_addr = NULL; + } + PUSH_CSP(LABEL_TYPE_BLOCK, param_cell_num, cell_num, end_addr); + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_LOOP) + { + read_leb_uint32(frame_ip, frame_ip_end, type_index); + param_cell_num = + ((WASMFuncType *)wasm_types[type_index])->param_cell_num; + cell_num = + ((WASMFuncType *)wasm_types[type_index])->param_cell_num; + goto handle_op_loop; + } + + HANDLE_OP(WASM_OP_LOOP) + { + value_type = *frame_ip++; + param_cell_num = 0; + cell_num = 0; + handle_op_loop: + PUSH_CSP(LABEL_TYPE_LOOP, param_cell_num, cell_num, frame_ip); + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_IF) + { + read_leb_uint32(frame_ip, frame_ip_end, type_index); + param_cell_num = + ((WASMFuncType *)wasm_types[type_index])->param_cell_num; + cell_num = + ((WASMFuncType *)wasm_types[type_index])->ret_cell_num; + goto handle_op_if; + } + + HANDLE_OP(WASM_OP_IF) + { + value_type = *frame_ip++; + param_cell_num = 0; + cell_num = wasm_value_type_cell_num(value_type); + handle_op_if: + cache_index = ((uintptr_t)frame_ip) + & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + cache_items = exec_env->block_addr_cache[cache_index]; + if (cache_items[0].start_addr == frame_ip) { + else_addr = cache_items[0].else_addr; + end_addr = cache_items[0].end_addr; + } + else if (cache_items[1].start_addr == frame_ip) { + else_addr = cache_items[1].else_addr; + end_addr = cache_items[1].end_addr; + } + else if (!wasm_loader_find_block_addr( + exec_env, (BlockAddr *)exec_env->block_addr_cache, + frame_ip, (uint8 *)-1, LABEL_TYPE_IF, &else_addr, + &end_addr)) { + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + + cond = (uint32)POP_I32(); + + if (cond) { /* if branch is met */ + PUSH_CSP(LABEL_TYPE_IF, param_cell_num, cell_num, end_addr); + } + else { /* if branch is not met */ + /* if there is no else branch, go to the end addr */ + if (else_addr == NULL) { + frame_ip = end_addr + 1; + } + /* if there is an else branch, go to the else addr */ + else { + PUSH_CSP(LABEL_TYPE_IF, param_cell_num, cell_num, + end_addr); + frame_ip = else_addr + 1; + } + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_ELSE) + { + /* comes from the if branch in WASM_OP_IF */ + frame_ip = (frame_csp - 1)->target_addr; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_END) + { + if (frame_csp > frame->csp_bottom + 1) { + POP_CSP(); + } + else { /* end of function, treat as WASM_OP_RETURN */ + frame_sp -= cur_func->ret_cell_num; + for (i = 0; i < cur_func->ret_cell_num; i++) { +#if WASM_ENABLE_GC != 0 + if (prev_frame->ip) { + /* prev frame is not a glue frame and has + the frame ref area */ + *FRAME_REF_FOR(prev_frame, prev_frame->sp) = + *FRAME_REF(frame_sp + i); + } +#endif + *prev_frame->sp++ = frame_sp[i]; + } + goto return_func; + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, depth); + label_pop_csp_n: + POP_CSP_N(depth); + if (!frame_ip) { /* must be label pushed by WASM_OP_BLOCK */ + if (!wasm_loader_find_block_addr( + exec_env, (BlockAddr *)exec_env->block_addr_cache, + (frame_csp - 1)->begin_addr, (uint8 *)-1, + LABEL_TYPE_BLOCK, &else_addr, &end_addr)) { + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + frame_ip = end_addr; + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR_IF) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, depth); + cond = (uint32)POP_I32(); + if (cond) + goto label_pop_csp_n; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR_TABLE) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, count); + lidx = POP_I32(); + if (lidx > count) + lidx = count; + depth = frame_ip[lidx]; + goto label_pop_csp_n; + } + + HANDLE_OP(EXT_OP_BR_TABLE_CACHE) + { + BrTableCache *node_cache = + bh_list_first_elem(module->module->br_table_cache_list); + BrTableCache *node_next; + +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + lidx = POP_I32(); + + while (node_cache) { + node_next = bh_list_elem_next(node_cache); + if (node_cache->br_table_op_addr == frame_ip - 1) { + if (lidx > node_cache->br_count) + lidx = node_cache->br_count; + depth = node_cache->br_depths[lidx]; + goto label_pop_csp_n; + } + node_cache = node_next; + } + bh_assert(0); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_RETURN) + { + frame_sp -= cur_func->ret_cell_num; + for (i = 0; i < cur_func->ret_cell_num; i++) { +#if WASM_ENABLE_GC != 0 + if (prev_frame->ip) { + /* prev frame is not a glue frame and has + the frame ref area */ + *FRAME_REF_FOR(prev_frame, prev_frame->sp) = + *FRAME_REF(frame_sp + i); + } +#endif + *prev_frame->sp++ = frame_sp[i]; + } + goto return_func; + } + + HANDLE_OP(WASM_OP_CALL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, fidx); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->e->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + + cur_func = module->e->functions + fidx; + goto call_func_from_interp; + } + +#if WASM_ENABLE_TAIL_CALL != 0 + HANDLE_OP(WASM_OP_RETURN_CALL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, fidx); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->e->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + cur_func = module->e->functions + fidx; + + goto call_func_from_return_call; + } +#endif /* WASM_ENABLE_TAIL_CALL */ + + HANDLE_OP(WASM_OP_CALL_INDIRECT) +#if WASM_ENABLE_TAIL_CALL != 0 + HANDLE_OP(WASM_OP_RETURN_CALL_INDIRECT) +#endif + { + WASMFuncType *cur_type, *cur_func_type; + WASMTableInstance *tbl_inst; + uint32 tbl_idx; + +#if WASM_ENABLE_TAIL_CALL != 0 + opcode = *(frame_ip - 1); +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + + /** + * type check. compiler will make sure all like + * (call_indirect (type $x) (it.const 1)) + * the function type has to be defined in the module also + * no matter it is used or not + */ + read_leb_uint32(frame_ip, frame_ip_end, tidx); + bh_assert(tidx < module->module->type_count); + cur_type = wasm_types[tidx]; + + /* clang-format off */ +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 || WASM_ENABLE_GC != 0 + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); +#else + frame_ip++; + tbl_idx = 0; +#endif + bh_assert(tbl_idx < module->table_count); + /* clang-format on */ + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + + val = POP_TBL_ELEM_IDX(); + if (val >= tbl_inst->cur_size) { + wasm_set_exception(module, "undefined element"); + goto got_exception; + } + + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + fidx = tbl_inst->elems[val]; + if (fidx == (uint32)-1) { + wasm_set_exception(module, "uninitialized element"); + goto got_exception; + } +#else + func_obj = (WASMFuncObjectRef)tbl_inst->elems[val]; + if (!func_obj) { + wasm_set_exception(module, "uninitialized element"); + goto got_exception; + } + fidx = wasm_func_obj_get_func_idx_bound(func_obj); +#endif + /* clang-format on */ + + /* + * we might be using a table injected by host or + * another module. In that case, we don't validate + * the elem value while loading + */ + if (fidx >= module->e->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } + + /* always call module own functions */ + cur_func = module->e->functions + fidx; + + if (cur_func->is_import_func) + cur_func_type = cur_func->u.func_import->func_type; + else + cur_func_type = cur_func->u.func->func_type; + + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + if (cur_type != cur_func_type) { + wasm_set_exception(module, "indirect call type mismatch"); + goto got_exception; + } +#else + if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) { + wasm_set_exception(module, "indirect call type mismatch"); + goto got_exception; + } +#endif + /* clang-format on */ + +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_RETURN_CALL_INDIRECT) + goto call_func_from_return_call; +#endif + goto call_func_from_interp; + } + + /* parametric instructions */ + HANDLE_OP(WASM_OP_DROP) + { + frame_sp--; + +#if WASM_ENABLE_GC != 0 + frame_ref_tmp = FRAME_REF(frame_sp); + *frame_ref_tmp = 0; +#endif + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_DROP_64) + { + frame_sp -= 2; + +#if WASM_ENABLE_GC != 0 + frame_ref_tmp = FRAME_REF(frame_sp); + *frame_ref_tmp = 0; + *(frame_ref_tmp + 1) = 0; +#endif + + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SELECT) + { + cond = (uint32)POP_I32(); + frame_sp--; + if (!cond) + *(frame_sp - 1) = *frame_sp; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SELECT_64) + { + cond = (uint32)POP_I32(); + frame_sp -= 2; + if (!cond) { + *(frame_sp - 2) = *frame_sp; + *(frame_sp - 1) = *(frame_sp + 1); + } + HANDLE_OP_END(); + } + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + HANDLE_OP(WASM_OP_SELECT_T) + { + uint32 vec_len; + uint8 type; + + read_leb_uint32(frame_ip, frame_ip_end, vec_len); + type = *frame_ip++; + + cond = (uint32)POP_I32(); + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64 +#if WASM_ENABLE_GC != 0 && UINTPTR_MAX == UINT64_MAX + || wasm_is_type_reftype(type) +#endif + ) { + frame_sp -= 2; + if (!cond) { + *(frame_sp - 2) = *frame_sp; + *(frame_sp - 1) = *(frame_sp + 1); + } + } + else { + frame_sp--; + if (!cond) + *(frame_sp - 1) = *frame_sp; + } + +#if WASM_ENABLE_GC != 0 + frame_ref_tmp = FRAME_REF(frame_sp); + *frame_ref_tmp = 0; +#if UINTPTR_MAX == UINT64_MAX + *(frame_ref_tmp + 1) = 0; +#endif +#endif + (void)vec_len; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_TABLE_GET) + { + uint32 tbl_idx; + tbl_elem_idx_t elem_idx; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + + elem_idx = POP_TBL_ELEM_IDX(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + +#if WASM_ENABLE_GC == 0 + PUSH_I32(tbl_inst->elems[elem_idx]); +#else + PUSH_REF(tbl_inst->elems[elem_idx]); +#endif + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_TABLE_SET) + { + WASMTableInstance *tbl_inst; + uint32 tbl_idx; + tbl_elem_idx_t elem_idx; + table_elem_type_t elem_val; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + +#if WASM_ENABLE_GC == 0 + elem_val = POP_I32(); +#else + elem_val = POP_REF(); +#endif + elem_idx = POP_TBL_ELEM_IDX(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + tbl_inst->elems[elem_idx] = elem_val; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_NULL) + { + uint32 ref_type; + read_leb_uint32(frame_ip, frame_ip_end, ref_type); +#if WASM_ENABLE_GC == 0 + PUSH_I32(NULL_REF); +#else + PUSH_REF(NULL_REF); +#endif + (void)ref_type; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_IS_NULL) + { +#if WASM_ENABLE_GC == 0 + uint32 ref_val; + ref_val = POP_I32(); +#else + void *ref_val; + ref_val = POP_REF(); +#endif + PUSH_I32(ref_val == NULL_REF ? 1 : 0); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_FUNC) + { + uint32 func_idx; + read_leb_uint32(frame_ip, frame_ip_end, func_idx); +#if WASM_ENABLE_GC == 0 + PUSH_I32(func_idx); +#else + SYNC_ALL_TO_FRAME(); + if (!(gc_obj = wasm_create_func_obj(module, func_idx, true, + NULL, 0))) { + goto got_exception; + } + PUSH_REF(gc_obj); +#endif + HANDLE_OP_END(); + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_GC != 0 + HANDLE_OP(WASM_OP_CALL_REF) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, type_index); + func_obj = POP_REF(); + if (!func_obj) { + wasm_set_exception(module, "null function reference"); + goto got_exception; + } + + fidx = wasm_func_obj_get_func_idx_bound(func_obj); + cur_func = module->e->functions + fidx; + goto call_func_from_interp; + } + + HANDLE_OP(WASM_OP_RETURN_CALL_REF) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, type_index); + func_obj = POP_REF(); + if (!func_obj) { + wasm_set_exception(module, "null function reference"); + goto got_exception; + } + + fidx = wasm_func_obj_get_func_idx_bound(func_obj); + cur_func = module->e->functions + fidx; + goto call_func_from_return_call; + } + + HANDLE_OP(WASM_OP_REF_EQ) + { + WASMObjectRef gc_obj1, gc_obj2; + gc_obj2 = POP_REF(); + gc_obj1 = POP_REF(); + val = wasm_obj_equal(gc_obj1, gc_obj2); + PUSH_I32(val); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_AS_NON_NULL) + { + gc_obj = POP_REF(); + if (gc_obj == NULL_REF) { + wasm_set_exception(module, "null reference"); + goto got_exception; + } + PUSH_REF(gc_obj); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR_ON_NULL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, depth); + gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM); + if (gc_obj == NULL_REF) { + frame_sp -= REF_CELL_NUM; + CLEAR_FRAME_REF(frame_sp, REF_CELL_NUM); + goto label_pop_csp_n; + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR_ON_NON_NULL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, depth); + gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM); + if (gc_obj != NULL_REF) { + goto label_pop_csp_n; + } + else { + frame_sp -= REF_CELL_NUM; + CLEAR_FRAME_REF(frame_sp, REF_CELL_NUM); + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_GC_PREFIX) + { + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_STRUCT_NEW: + case WASM_OP_STRUCT_NEW_DEFAULT: + { + WASMModule *wasm_module = module->module; + WASMStructType *struct_type; + WASMRttType *rtt_type; + WASMValue field_value = { 0 }; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + struct_type = + (WASMStructType *)module->module->types[type_index]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)struct_type, type_index, + wasm_module->rtt_types, + wasm_module->type_count, + &wasm_module->rtt_type_lock))) { + wasm_set_exception(module, + "create rtt type failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + struct_obj = wasm_struct_obj_new(exec_env, rtt_type); + if (!struct_obj) { + wasm_set_exception(module, + "create struct object failed"); + goto got_exception; + } + + if (opcode == WASM_OP_STRUCT_NEW) { + WASMStructFieldType *fields = struct_type->fields; + int32 field_count = (int32)struct_type->field_count; + int32 field_idx; + uint8 field_type; + + for (field_idx = field_count - 1; field_idx >= 0; + field_idx--) { + field_type = fields[field_idx].field_type; + if (wasm_is_type_reftype(field_type)) { + field_value.gc_obj = POP_REF(); + } + else if (field_type == VALUE_TYPE_I32 + || field_type == VALUE_TYPE_F32 + || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + field_value.i32 = POP_I32(); + } + else { + field_value.i64 = POP_I64(); + } + wasm_struct_obj_set_field(struct_obj, field_idx, + &field_value); + } + } + PUSH_REF(struct_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRUCT_GET: + case WASM_OP_STRUCT_GET_S: + case WASM_OP_STRUCT_GET_U: + { + WASMStructType *struct_type; + WASMValue field_value = { 0 }; + uint32 field_idx; + uint8 field_type; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, field_idx); + struct_type = + (WASMStructType *)module->module->types[type_index]; + + struct_obj = POP_REF(); + + if (!struct_obj) { + wasm_set_exception(module, + "null structure reference"); + goto got_exception; + } + + wasm_struct_obj_get_field( + struct_obj, field_idx, + opcode == WASM_OP_STRUCT_GET_S ? true : false, + &field_value); + + field_type = struct_type->fields[field_idx].field_type; + if (wasm_is_reftype_i31ref(field_type)) { + PUSH_I31REF(field_value.gc_obj); + } + else if (wasm_is_type_reftype(field_type)) { + PUSH_REF(field_value.gc_obj); + } + else if (field_type == VALUE_TYPE_I32 + || field_type == VALUE_TYPE_F32 + || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + PUSH_I32(field_value.i32); + } + else { + PUSH_I64(field_value.i64); + } + HANDLE_OP_END(); + } + case WASM_OP_STRUCT_SET: + { + WASMStructType *struct_type; + WASMValue field_value = { 0 }; + uint32 field_idx; + uint8 field_type; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, field_idx); + + struct_type = + (WASMStructType *)module->module->types[type_index]; + field_type = struct_type->fields[field_idx].field_type; + + if (wasm_is_type_reftype(field_type)) { + field_value.gc_obj = POP_REF(); + } + else if (field_type == VALUE_TYPE_I32 + || field_type == VALUE_TYPE_F32 + || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + field_value.i32 = POP_I32(); + } + else { + field_value.i64 = POP_I64(); + } + + struct_obj = POP_REF(); + if (!struct_obj) { + wasm_set_exception(module, + "null structure reference"); + goto got_exception; + } + + wasm_struct_obj_set_field(struct_obj, field_idx, + &field_value); + HANDLE_OP_END(); + } + + case WASM_OP_ARRAY_NEW: + case WASM_OP_ARRAY_NEW_DEFAULT: + case WASM_OP_ARRAY_NEW_FIXED: + { + WASMModule *wasm_module = module->module; + WASMArrayType *array_type; + WASMRttType *rtt_type; + WASMValue array_elem = { 0 }; + uint32 array_len; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + array_type = + (WASMArrayType *)wasm_module->types[type_index]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_index, + wasm_module->rtt_types, + wasm_module->type_count, + &wasm_module->rtt_type_lock))) { + wasm_set_exception(module, + "create rtt type failed"); + goto got_exception; + } + + if (opcode != WASM_OP_ARRAY_NEW_FIXED) + array_len = POP_I32(); + else + read_leb_uint32(frame_ip, frame_ip_end, array_len); + + if (opcode == WASM_OP_ARRAY_NEW) { + if (wasm_is_type_reftype(array_type->elem_type)) { + array_elem.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32 + || array_type->elem_type == PACKED_TYPE_I8 + || array_type->elem_type + == PACKED_TYPE_I16) { + array_elem.i32 = POP_I32(); + } + else { + array_elem.i64 = POP_I64(); + } + } + + SYNC_ALL_TO_FRAME(); + array_obj = wasm_array_obj_new(exec_env, rtt_type, + array_len, &array_elem); + if (!array_obj) { + wasm_set_exception(module, + "create array object failed"); + goto got_exception; + } + + if (opcode == WASM_OP_ARRAY_NEW_FIXED) { + for (i = 0; i < array_len; i++) { + if (wasm_is_type_reftype( + array_type->elem_type)) { + array_elem.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type + == VALUE_TYPE_F32 + || array_type->elem_type + == PACKED_TYPE_I8 + || array_type->elem_type + == PACKED_TYPE_I16) { + array_elem.i32 = POP_I32(); + } + else { + array_elem.i64 = POP_I64(); + } + wasm_array_obj_set_elem( + array_obj, array_len - 1 - i, &array_elem); + } + } + + PUSH_REF(array_obj); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_NEW_DATA: + { + WASMModule *wasm_module = module->module; + WASMArrayType *array_type; + WASMRttType *rtt_type; + WASMValue array_elem = { 0 }; + WASMDataSeg *data_seg; + uint8 *array_elem_base; + uint32 array_len, data_seg_idx, data_seg_offset; + uint32 elem_size = 0; + uint64 total_size; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, data_seg_idx); + data_seg = wasm_module->data_segments[data_seg_idx]; + + array_type = + (WASMArrayType *)wasm_module->types[type_index]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_index, + wasm_module->rtt_types, + wasm_module->type_count, + &wasm_module->rtt_type_lock))) { + wasm_set_exception(module, + "create rtt type failed"); + goto got_exception; + } + + array_len = POP_I32(); + data_seg_offset = POP_I32(); + + switch (array_type->elem_type) { + case PACKED_TYPE_I8: + elem_size = 1; + break; + case PACKED_TYPE_I16: + elem_size = 2; + break; + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + elem_size = 4; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + elem_size = 8; + break; + default: + bh_assert(0); + } + + total_size = (uint64)elem_size * array_len; + if (data_seg_offset >= data_seg->data_length + || total_size + > data_seg->data_length - data_seg_offset) { + wasm_set_exception(module, + "data segment out of bounds"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + array_obj = wasm_array_obj_new(exec_env, rtt_type, + array_len, &array_elem); + if (!array_obj) { + wasm_set_exception(module, + "create array object failed"); + goto got_exception; + } + + array_elem_base = + (uint8 *)wasm_array_obj_first_elem_addr(array_obj); + bh_memcpy_s(array_elem_base, (uint32)total_size, + data_seg->data + data_seg_offset, + (uint32)total_size); + + PUSH_REF(array_obj); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_NEW_ELEM: + { + /* TODO */ + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } + case WASM_OP_ARRAY_GET: + case WASM_OP_ARRAY_GET_S: + case WASM_OP_ARRAY_GET_U: + { + WASMArrayType *array_type; + WASMValue array_elem = { 0 }; + uint32 elem_idx, elem_size_log; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + array_type = + (WASMArrayType *)module->module->types[type_index]; + + elem_idx = POP_I32(); + array_obj = POP_REF(); + + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + if (elem_idx >= wasm_array_obj_length(array_obj)) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_get_elem( + array_obj, elem_idx, + opcode == WASM_OP_ARRAY_GET_S ? true : false, + &array_elem); + elem_size_log = wasm_array_obj_elem_size_log(array_obj); + + if (wasm_is_reftype_i31ref(array_type->elem_type)) { + PUSH_I31REF(array_elem.gc_obj); + } + else if (wasm_is_type_reftype(array_type->elem_type)) { + PUSH_REF(array_elem.gc_obj); + } + else if (elem_size_log < 3) { + PUSH_I32(array_elem.i32); + } + else { + PUSH_I64(array_elem.i64); + } + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_SET: + { + WASMArrayType *array_type; + WASMValue array_elem = { 0 }; + uint32 elem_idx; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + array_type = + (WASMArrayType *)module->module->types[type_index]; + if (wasm_is_type_reftype(array_type->elem_type)) { + array_elem.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32 + || array_type->elem_type == PACKED_TYPE_I8 + || array_type->elem_type == PACKED_TYPE_I16) { + array_elem.i32 = POP_I32(); + } + else { + array_elem.i64 = POP_I64(); + } + + elem_idx = POP_I32(); + array_obj = POP_REF(); + + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + if (elem_idx >= wasm_array_obj_length(array_obj)) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_set_elem(array_obj, elem_idx, + &array_elem); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_LEN: + { + uint32 array_len; + + array_obj = POP_REF(); + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + array_len = wasm_array_obj_length(array_obj); + PUSH_I32(array_len); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_FILL: + { + WASMArrayType *array_type; + WASMValue fill_value = { 0 }; + uint32 start_offset, len; + read_leb_uint32(frame_ip, frame_ip_end, type_index); + + array_type = + (WASMArrayType *)module->module->types[type_index]; + + len = POP_I32(); + if (wasm_is_type_reftype(array_type->elem_type)) { + fill_value.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32 + || array_type->elem_type == PACKED_TYPE_I8 + || array_type->elem_type == PACKED_TYPE_I16) { + fill_value.i32 = POP_I32(); + } + else { + fill_value.i64 = POP_I64(); + } + start_offset = POP_I32(); + array_obj = POP_REF(); + + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + + if (len > 0) { + if ((uint64)start_offset + len + > wasm_array_obj_length(array_obj)) { + wasm_set_exception( + module, "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_fill(array_obj, start_offset, len, + &fill_value); + } + + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_COPY: + { + uint32 dst_offset, src_offset, len, src_type_index; + WASMArrayObjectRef src_obj, dst_obj; + + read_leb_uint32(frame_ip, frame_ip_end, type_index); + read_leb_uint32(frame_ip, frame_ip_end, src_type_index); + + len = POP_I32(); + src_offset = POP_I32(); + src_obj = POP_REF(); + dst_offset = POP_I32(); + dst_obj = POP_REF(); + + if (!src_obj || !dst_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + + if (len > 0) { + if ((dst_offset > UINT32_MAX - len) + || (dst_offset + len + > wasm_array_obj_length(dst_obj)) + || (src_offset > UINT32_MAX - len) + || (src_offset + len + > wasm_array_obj_length(src_obj))) { + wasm_set_exception( + module, "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_copy(dst_obj, dst_offset, src_obj, + src_offset, len); + } + + (void)src_type_index; + HANDLE_OP_END(); + } + + case WASM_OP_REF_I31: + { + uint32 i31_val; + + i31_val = POP_I32(); + i31_obj = wasm_i31_obj_new(i31_val); + PUSH_I31REF(i31_obj); + HANDLE_OP_END(); + } + case WASM_OP_I31_GET_S: + case WASM_OP_I31_GET_U: + { + uint32 i31_val; + + i31_obj = (WASMI31ObjectRef)POP_REF(); + if (!i31_obj) { + wasm_set_exception(module, "null i31 reference"); + goto got_exception; + } + i31_val = (uint32)(((uintptr_t)i31_obj) >> 1); + if (opcode == WASM_OP_I31_GET_S + && (i31_val & 0x40000000) /* bit 30 is 1 */) + /* set bit 31 to 1 */ + i31_val |= 0x80000000; + PUSH_I32(i31_val); + HANDLE_OP_END(); + } + + case WASM_OP_REF_TEST: + case WASM_OP_REF_CAST: + case WASM_OP_REF_TEST_NULLABLE: + case WASM_OP_REF_CAST_NULLABLE: + { + int32 heap_type; + + read_leb_int32(frame_ip, frame_ip_end, heap_type); + + gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM); + if (!gc_obj) { + if (opcode == WASM_OP_REF_TEST + || opcode == WASM_OP_REF_TEST_NULLABLE) { + (void)POP_REF(); + if (opcode == WASM_OP_REF_TEST) + PUSH_I32(0); + else + PUSH_I32(1); + } + else if (opcode == WASM_OP_REF_CAST) { + wasm_set_exception(module, "cast failure"); + goto got_exception; + } + else { + /* Do nothing for WASM_OP_REF_CAST_NULLABLE */ + } + } + else { + bool castable = false; + + if (heap_type >= 0) { + WASMModule *wasm_module = module->module; + castable = wasm_obj_is_instance_of( + gc_obj, (uint32)heap_type, + wasm_module->types, + wasm_module->type_count); + } + else { + castable = + wasm_obj_is_type_of(gc_obj, heap_type); + } + + if (opcode == WASM_OP_REF_TEST + || opcode == WASM_OP_REF_TEST_NULLABLE) { + (void)POP_REF(); + if (castable) + PUSH_I32(1); + else + PUSH_I32(0); + } + else if (!castable) { + wasm_set_exception(module, "cast failure"); + goto got_exception; + } + } + HANDLE_OP_END(); + } + + case WASM_OP_BR_ON_CAST: + case WASM_OP_BR_ON_CAST_FAIL: + { + int32 heap_type, heap_type_dst; + uint8 castflags; + +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + castflags = *frame_ip++; + read_leb_uint32(frame_ip, frame_ip_end, depth); + read_leb_int32(frame_ip, frame_ip_end, heap_type); + read_leb_int32(frame_ip, frame_ip_end, heap_type_dst); + + gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM); + if (!gc_obj) { + /* + * castflags should be 0~3: + * 0: (non-null, non-null) + * 1: (null, non-null) + * 2: (non-null, null) + * 3: (null, null) + */ + if ( + /* op is BR_ON_CAST and dst reftype is nullable + */ + ((opcode1 == WASM_OP_BR_ON_CAST) + && ((castflags == 2) || (castflags == 3))) + /* op is BR_ON_CAST_FAIL and dst reftype is + non-nullable */ + || ((opcode1 == WASM_OP_BR_ON_CAST_FAIL) + && ((castflags == 0) || (castflags == 1)))) + goto label_pop_csp_n; + } + else { + bool castable = false; + + if (heap_type_dst >= 0) { + WASMModule *wasm_module = module->module; + castable = wasm_obj_is_instance_of( + gc_obj, (uint32)heap_type_dst, + wasm_module->types, + wasm_module->type_count); + } + else { + castable = + wasm_obj_is_type_of(gc_obj, heap_type_dst); + } + + if ((castable && (opcode == WASM_OP_BR_ON_CAST)) + || (!castable + && (opcode == WASM_OP_BR_ON_CAST_FAIL))) { + goto label_pop_csp_n; + } + } + + (void)heap_type; + HANDLE_OP_END(); + } + + case WASM_OP_ANY_CONVERT_EXTERN: + { + externref_obj = POP_REF(); + if (externref_obj == NULL_REF) + PUSH_REF(NULL_REF); + else { + gc_obj = wasm_externref_obj_to_internal_obj( + externref_obj); + PUSH_REF(gc_obj); + } + HANDLE_OP_END(); + } + case WASM_OP_EXTERN_CONVERT_ANY: + { + gc_obj = POP_REF(); + if (gc_obj == NULL_REF) + PUSH_REF(NULL_REF); + else { + if (!(externref_obj = + wasm_internal_obj_to_externref_obj( + exec_env, gc_obj))) { + wasm_set_exception( + module, "create externref object failed"); + goto got_exception; + } + PUSH_REF(externref_obj); + } + HANDLE_OP_END(); + } + +#if WASM_ENABLE_STRINGREF != 0 + case WASM_OP_STRING_NEW_UTF8: + case WASM_OP_STRING_NEW_WTF16: + case WASM_OP_STRING_NEW_LOSSY_UTF8: + case WASM_OP_STRING_NEW_WTF8: + { + uint32 mem_idx, addr, bytes_length, offset = 0; + EncodingFlag flag = WTF8; + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + bytes_length = POP_I32(); + addr = POP_I32(); + + CHECK_MEMORY_OVERFLOW(bytes_length); + + if (opcode == WASM_OP_STRING_NEW_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_NEW_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_WTF8) { + flag = WTF8; + } + + str_obj = wasm_string_new_with_encoding( + maddr, bytes_length, flag); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + + (void)mem_idx; + HANDLE_OP_END(); + } + case WASM_OP_STRING_CONST: + { + WASMModule *wasm_module = module->module; + uint32 contents; + + read_leb_uint32(frame_ip, frame_ip_end, contents); + + str_obj = wasm_string_new_const( + (const char *) + wasm_module->string_literal_ptrs[contents], + wasm_module->string_literal_lengths[contents]); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_MEASURE_UTF8: + case WASM_OP_STRING_MEASURE_WTF8: + case WASM_OP_STRING_MEASURE_WTF16: + { + int32 target_bytes_length; + EncodingFlag flag = WTF8; + + stringref_obj = POP_REF(); + + if (opcode == WASM_OP_STRING_MEASURE_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_MEASURE_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_MEASURE_WTF8) { + flag = LOSSY_UTF8; + } + target_bytes_length = wasm_string_measure( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + flag); + + PUSH_I32(target_bytes_length); + HANDLE_OP_END(); + } + case WASM_OP_STRING_ENCODE_UTF8: + case WASM_OP_STRING_ENCODE_WTF16: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8: + case WASM_OP_STRING_ENCODE_WTF8: + { + uint32 mem_idx, addr; + int32 target_bytes_length; + WASMMemoryInstance *memory_inst; + EncodingFlag flag = WTF8; + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + addr = POP_I32(); + stringref_obj = POP_REF(); + + str_obj = (WASMString)wasm_stringref_obj_get_value( + stringref_obj); + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)addr, 1)) + shared_heap_addr_app_to_native((uint64)addr, maddr); + else +#endif + { + memory_inst = module->memories[mem_idx]; + maddr = memory_inst->memory_data + addr; + } + + if (opcode == WASM_OP_STRING_ENCODE_WTF16) { + flag = WTF16; + count = wasm_string_measure(str_obj, flag); + target_bytes_length = wasm_string_encode( + str_obj, 0, count, maddr, NULL, flag); + } + else { + if (opcode == WASM_OP_STRING_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRING_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_ENCODE_WTF8) { + flag = WTF8; + } + count = wasm_string_measure(str_obj, flag); + target_bytes_length = wasm_string_encode( + str_obj, 0, count, maddr, NULL, flag); + + if (target_bytes_length == -1) { + wasm_set_exception( + module, "isolated surrogate is seen"); + goto got_exception; + } + } + if (target_bytes_length < 0) { + wasm_set_exception(module, + "stringref encode failed"); + goto got_exception; + } + + PUSH_I32(target_bytes_length); + HANDLE_OP_END(); + } + case WASM_OP_STRING_CONCAT: + { + WASMStringrefObjectRef stringref_obj1, stringref_obj2; + + stringref_obj2 = POP_REF(); + stringref_obj1 = POP_REF(); + + str_obj = wasm_string_concat( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj1), + (WASMString)wasm_stringref_obj_get_value( + stringref_obj2)); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_EQ: + { + WASMStringrefObjectRef stringref_obj1, stringref_obj2; + int32 is_eq; + + stringref_obj2 = POP_REF(); + stringref_obj1 = POP_REF(); + + is_eq = wasm_string_eq( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj1), + (WASMString)wasm_stringref_obj_get_value( + stringref_obj2)); + + PUSH_I32(is_eq); + HANDLE_OP_END(); + } + case WASM_OP_STRING_IS_USV_SEQUENCE: + { + int32 is_usv_sequence; + + stringref_obj = POP_REF(); + + is_usv_sequence = wasm_string_is_usv_sequence( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj)); + + PUSH_I32(is_usv_sequence); + HANDLE_OP_END(); + } + case WASM_OP_STRING_AS_WTF8: + { + stringref_obj = POP_REF(); + + str_obj = wasm_string_create_view( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + STRING_VIEW_WTF8); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringview_wtf8_obj = + wasm_stringview_wtf8_obj_new(exec_env, str_obj); + if (!stringview_wtf8_obj) { + wasm_set_exception(module, + "create stringview wtf8 failed"); + goto got_exception; + } + + PUSH_REF(stringview_wtf8_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF8_ADVANCE: + { + uint32 next_pos, bytes, pos; + + bytes = POP_I32(); + pos = POP_I32(); + stringview_wtf8_obj = POP_REF(); + + next_pos = wasm_string_advance( + (WASMString)wasm_stringview_wtf8_obj_get_value( + stringview_wtf8_obj), + pos, bytes, NULL); + + PUSH_I32(next_pos); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8: + { + uint32 mem_idx, addr, pos, bytes, next_pos; + int32 bytes_written; + WASMMemoryInstance *memory_inst; + EncodingFlag flag = WTF8; + + if (opcode == WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode + == WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8) { + flag = WTF8; + } + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + bytes = POP_I32(); + pos = POP_I32(); + addr = POP_I32(); + stringview_wtf8_obj = POP_REF(); + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)addr, 1)) + shared_heap_addr_app_to_native((uint64)addr, maddr); + else +#endif + { + memory_inst = module->memories[mem_idx]; + maddr = memory_inst->memory_data + addr; + } + + bytes_written = wasm_string_encode( + (WASMString)wasm_stringview_wtf8_obj_get_value( + stringview_wtf8_obj), + pos, bytes, maddr, &next_pos, flag); + + if (bytes_written < 0) { + if (bytes_written == Isolated_Surrogate) { + wasm_set_exception( + module, "isolated surrogate is seen"); + } + else { + wasm_set_exception(module, "encode failed"); + } + + goto got_exception; + } + + PUSH_I32(next_pos); + PUSH_I32(bytes_written); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF8_SLICE: + { + uint32 start, end; + + end = POP_I32(); + start = POP_I32(); + stringview_wtf8_obj = POP_REF(); + + str_obj = wasm_string_slice( + (WASMString)wasm_stringview_wtf8_obj_get_value( + stringview_wtf8_obj), + start, end, STRING_VIEW_WTF8); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_AS_WTF16: + { + stringref_obj = POP_REF(); + + str_obj = wasm_string_create_view( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + STRING_VIEW_WTF16); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringview_wtf16_obj = + wasm_stringview_wtf16_obj_new(exec_env, str_obj); + if (!stringview_wtf16_obj) { + wasm_set_exception( + module, "create stringview wtf16 failed"); + goto got_exception; + } + + PUSH_REF(stringview_wtf16_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_LENGTH: + { + int32 code_units_length; + + stringview_wtf16_obj = POP_REF(); + + code_units_length = wasm_string_wtf16_get_length( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj)); + + PUSH_I32(code_units_length); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_GET_CODEUNIT: + { + int32 pos; + uint32 code_unit; + + pos = POP_I32(); + stringview_wtf16_obj = POP_REF(); + + code_unit = (uint32)wasm_string_get_wtf16_codeunit( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj), + pos); + + PUSH_I32(code_unit); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_ENCODE: + { + uint32 mem_idx, addr, pos, len, offset = 0; + int32 written_code_units = 0; + + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + len = POP_I32(); + pos = POP_I32(); + addr = POP_I32(); + stringview_wtf16_obj = POP_REF(); + + CHECK_MEMORY_OVERFLOW(len * sizeof(uint16)); + + /* check 2-byte alignment */ + if (((uintptr_t)maddr & (((uintptr_t)1 << 2) - 1)) + != 0) { + wasm_set_exception(module, + "unaligned memory access"); + goto got_exception; + } + + written_code_units = wasm_string_encode( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj), + pos, len, maddr, NULL, WTF16); + if (written_code_units < 0) { + wasm_set_exception(module, "encode failed"); + goto got_exception; + } + + PUSH_I32(written_code_units); + (void)mem_idx; + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_SLICE: + { + uint32 start, end; + + end = POP_I32(); + start = POP_I32(); + stringview_wtf16_obj = POP_REF(); + + str_obj = wasm_string_slice( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj), + start, end, STRING_VIEW_WTF16); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_AS_ITER: + { + stringref_obj = POP_REF(); + + str_obj = wasm_string_create_view( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + STRING_VIEW_ITER); + + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringview_iter_obj = + wasm_stringview_iter_obj_new(exec_env, str_obj, 0); + if (!stringview_iter_obj) { + wasm_set_exception(module, + "create stringview iter failed"); + goto got_exception; + } + + PUSH_REF(stringview_iter_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_ITER_NEXT: + { + uint32 code_point; + + stringview_iter_obj = POP_REF(); + + code_point = wasm_string_next_codepoint( + (WASMString)wasm_stringview_iter_obj_get_value( + stringview_iter_obj), + wasm_stringview_iter_obj_get_pos( + stringview_iter_obj)); + + PUSH_I32(code_point); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_ITER_ADVANCE: + case WASM_OP_STRINGVIEW_ITER_REWIND: + { + uint32 code_points_count, code_points_consumed = 0, + cur_pos, next_pos = 0; + + code_points_count = POP_I32(); + stringview_iter_obj = POP_REF(); + + str_obj = + (WASMString)wasm_stringview_iter_obj_get_value( + stringview_iter_obj); + cur_pos = wasm_stringview_iter_obj_get_pos( + stringview_iter_obj); + + if (opcode == WASM_OP_STRINGVIEW_ITER_ADVANCE) { + next_pos = wasm_string_advance( + str_obj, cur_pos, code_points_count, + &code_points_consumed); + } + else if (opcode == WASM_OP_STRINGVIEW_ITER_REWIND) { + next_pos = wasm_string_rewind( + str_obj, cur_pos, code_points_count, + &code_points_consumed); + } + + wasm_stringview_iter_obj_update_pos(stringview_iter_obj, + next_pos); + + PUSH_I32(code_points_consumed); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_ITER_SLICE: + { + uint32 code_points_count, cur_pos; + + code_points_count = POP_I32(); + stringview_iter_obj = POP_REF(); + + cur_pos = wasm_stringview_iter_obj_get_pos( + stringview_iter_obj); + + str_obj = wasm_string_slice( + (WASMString)wasm_stringview_iter_obj_get_value( + stringview_iter_obj), + cur_pos, cur_pos + code_points_count, + STRING_VIEW_ITER); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_NEW_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF16_ARRAY: + case WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF8_ARRAY: + { + uint32 start, end, array_len; + EncodingFlag flag = WTF8; + WASMArrayType *array_type; + void *arr_start_addr; + + end = POP_I32(); + start = POP_I32(); + array_obj = POP_REF(); + + array_type = (WASMArrayType *)wasm_obj_get_defined_type( + (WASMObjectRef)array_obj); + arr_start_addr = + wasm_array_obj_elem_addr(array_obj, start); + array_len = wasm_array_obj_length(array_obj); + + if (start > end || end > array_len) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + if (opcode == WASM_OP_STRING_NEW_WTF16_ARRAY) { + if (array_type->elem_type != VALUE_TYPE_I16) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + flag = WTF16; + } + else { + if (array_type->elem_type != VALUE_TYPE_I8) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + if (opcode == WASM_OP_STRING_NEW_UTF8_ARRAY) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_WTF8_ARRAY) { + flag = WTF8; + } + else if (opcode + == WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY) { + flag = LOSSY_UTF8; + } + } + + str_obj = wasm_string_new_with_encoding( + arr_start_addr, (end - start), flag); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_ENCODE_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF16_ARRAY: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF8_ARRAY: + { + uint32 start, array_len; + int32 bytes_written; + EncodingFlag flag = WTF8; + WASMArrayType *array_type; + void *arr_start_addr; + + start = POP_I32(); + array_obj = POP_REF(); + stringref_obj = POP_REF(); + + str_obj = (WASMString)wasm_stringref_obj_get_value( + stringref_obj); + + array_type = (WASMArrayType *)wasm_obj_get_defined_type( + (WASMObjectRef)array_obj); + arr_start_addr = + wasm_array_obj_elem_addr(array_obj, start); + array_len = wasm_array_obj_length(array_obj); + + if (start > array_len) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + if (opcode == WASM_OP_STRING_ENCODE_WTF16_ARRAY) { + if (array_type->elem_type != VALUE_TYPE_I16) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + flag = WTF16; + } + else { + if (array_type->elem_type != VALUE_TYPE_I8) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + if (opcode == WASM_OP_STRING_ENCODE_UTF8_ARRAY) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRING_ENCODE_WTF8_ARRAY) { + flag = WTF8; + } + else if ( + opcode + == WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY) { + flag = LOSSY_UTF8; + } + } + + count = wasm_string_measure(str_obj, flag); + + bytes_written = wasm_string_encode( + str_obj, 0, count, arr_start_addr, NULL, flag); + + if (bytes_written < 0) { + if (bytes_written == Isolated_Surrogate) { + wasm_set_exception( + module, "isolated surrogate is seen"); + } + else if (bytes_written == Insufficient_Space) { + wasm_set_exception( + module, "array space is insufficient"); + } + else { + wasm_set_exception(module, "encode failed"); + } + + goto got_exception; + } + + PUSH_I32(bytes_written); + HANDLE_OP_END(); + } +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + default: + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + /* variable instructions */ + HANDLE_OP(WASM_OP_GET_LOCAL) + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + PUSH_I32(*(int32 *)(frame_lp + local_offset)); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + PUSH_I64(GET_I64_FROM_ADDR(frame_lp + local_offset)); + break; + default: +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_reftype(local_type)) { + if (wasm_is_reftype_i31ref(local_type)) { + PUSH_I31REF( + GET_REF_FROM_ADDR(frame_lp + local_offset)); + } + else { + PUSH_REF( + GET_REF_FROM_ADDR(frame_lp + local_offset)); + } + } + else +#endif + { + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + } + + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_GET_LOCAL_FAST) + { + local_offset = *frame_ip++; + if (local_offset & 0x80) + PUSH_I64( + GET_I64_FROM_ADDR(frame_lp + (local_offset & 0x7F))); + else + PUSH_I32(*(int32 *)(frame_lp + local_offset)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_LOCAL) + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + *(int32 *)(frame_lp + local_offset) = POP_I32(); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + PUT_I64_TO_ADDR((uint32 *)(frame_lp + local_offset), + POP_I64()); + break; + default: +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_reftype(local_type)) { + PUT_REF_TO_ADDR(frame_lp + local_offset, POP_REF()); + } + else +#endif + { + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + } + + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_SET_LOCAL_FAST) + { + local_offset = *frame_ip++; + if (local_offset & 0x80) + PUT_I64_TO_ADDR( + (uint32 *)(frame_lp + (local_offset & 0x7F)), + POP_I64()); + else + *(int32 *)(frame_lp + local_offset) = POP_I32(); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_TEE_LOCAL) + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + *(int32 *)(frame_lp + local_offset) = + *(int32 *)(frame_sp - 1); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + PUT_I64_TO_ADDR((uint32 *)(frame_lp + local_offset), + GET_I64_FROM_ADDR(frame_sp - 2)); + break; + default: +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_reftype(local_type)) { + PUT_REF_TO_ADDR( + frame_lp + local_offset, + GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM)); + } + else +#endif + { + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + } + + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_TEE_LOCAL_FAST) + { + local_offset = *frame_ip++; + if (local_offset & 0x80) + PUT_I64_TO_ADDR( + (uint32 *)(frame_lp + (local_offset & 0x7F)), + GET_I64_FROM_ADDR(frame_sp - 2)); + else + *(int32 *)(frame_lp + local_offset) = + *(int32 *)(frame_sp - 1); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_GET_GLOBAL) + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + PUSH_I32(*(uint32 *)global_addr); +#else + if (!wasm_is_type_reftype(global->type)) { + PUSH_I32(*(uint32 *)global_addr); + } + else if (wasm_is_reftype_i31ref(global->type)) { + PUSH_I31REF(GET_REF_FROM_ADDR((uint32 *)global_addr)); + } + else { + PUSH_REF(GET_REF_FROM_ADDR((uint32 *)global_addr)); + } +#endif + /* clang-format on */ + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_GET_GLOBAL_64) + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + PUSH_I64(GET_I64_FROM_ADDR((uint32 *)global_addr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_GLOBAL) + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + *(int32 *)global_addr = POP_I32(); +#else + if (!wasm_is_type_reftype(global->type)) + *(int32 *)global_addr = POP_I32(); + else + PUT_REF_TO_ADDR((uint32 *)global_addr, POP_REF()); +#endif + /* clang-format on */ + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_GLOBAL_AUX_STACK) + { + uint64 aux_stack_top; + + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + aux_stack_top = *(uint64 *)(frame_sp - 2); + } + else +#endif + { + aux_stack_top = (uint64)(*(uint32 *)(frame_sp - 1)); + } + if (aux_stack_top <= (uint64)exec_env->aux_stack_boundary) { + wasm_set_exception(module, "wasm auxiliary stack overflow"); + goto got_exception; + } + if (aux_stack_top > (uint64)exec_env->aux_stack_bottom) { + wasm_set_exception(module, + "wasm auxiliary stack underflow"); + goto got_exception; + } +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + *(uint64 *)global_addr = aux_stack_top; + frame_sp -= 2; + } + else +#endif + { + *(uint32 *)global_addr = (uint32)aux_stack_top; + frame_sp--; + } +#if WASM_ENABLE_MEMORY_PROFILING != 0 + if (module->module->aux_stack_top_global_index != (uint32)-1) { + uint32 aux_stack_used = + (uint32)(module->module->aux_stack_bottom + - *(uint32 *)global_addr); + if (aux_stack_used > module->e->max_aux_stack_used) + module->e->max_aux_stack_used = aux_stack_used; + } +#endif + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_GLOBAL_64) + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + PUT_I64_TO_ADDR((uint32 *)global_addr, POP_I64()); + HANDLE_OP_END(); + } + + /* memory load instructions */ + HANDLE_OP(WASM_OP_I32_LOAD) + HANDLE_OP(WASM_OP_F32_LOAD) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + PUSH_I32(LOAD_I32(maddr)); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD) + HANDLE_OP(WASM_OP_F64_LOAD) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(8); + PUSH_I64(LOAD_I64(maddr)); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD8_S) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I32(sign_ext_8_32(*(int8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD8_U) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I32((uint32)(*(uint8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD16_S) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I32(sign_ext_16_32(LOAD_I16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD16_U) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I32((uint32)(LOAD_U16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD8_S) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I64(sign_ext_8_64(*(int8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD8_U) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I64((uint64)(*(uint8 *)maddr)); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD16_S) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I64(sign_ext_16_64(LOAD_I16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD16_U) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I64((uint64)(LOAD_U16(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD32_S) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + PUSH_I64(sign_ext_32_64(LOAD_I32(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD32_U) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + PUSH_I64((uint64)(LOAD_U32(maddr))); + CHECK_READ_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + /* memory store instructions */ + HANDLE_OP(WASM_OP_I32_STORE) + HANDLE_OP(WASM_OP_F32_STORE) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + frame_sp--; + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + STORE_U32(maddr, frame_sp[2]); + } + else +#endif + { + STORE_U32(maddr, frame_sp[1]); + } + CHECK_WRITE_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_STORE) + HANDLE_OP(WASM_OP_F64_STORE) + { + uint32 flags; + mem_offset_t offset, addr; + + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + frame_sp -= 2; + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(8); + +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) { + PUT_I64_TO_ADDR((mem_offset_t *)maddr, + GET_I64_FROM_ADDR(frame_sp + 2)); + } + else +#endif + { + PUT_I64_TO_ADDR((uint32 *)maddr, + GET_I64_FROM_ADDR(frame_sp + 1)); + } + CHECK_WRITE_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_STORE8) + HANDLE_OP(WASM_OP_I32_STORE16) + { + uint32 flags; + mem_offset_t offset, addr; + uint32 sval; + + opcode = *(frame_ip - 1); + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + sval = (uint32)POP_I32(); + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_I32_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + *(uint8 *)maddr = (uint8)sval; + } + else { + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + } + CHECK_WRITE_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_STORE8) + HANDLE_OP(WASM_OP_I64_STORE16) + HANDLE_OP(WASM_OP_I64_STORE32) + { + uint32 flags; + mem_offset_t offset, addr; + uint64 sval; + + opcode = *(frame_ip - 1); + read_leb_memarg(frame_ip, frame_ip_end, flags); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + sval = (uint64)POP_I64(); + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_I64_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + *(uint8 *)maddr = (uint8)sval; + } + else if (opcode == WASM_OP_I64_STORE16) { + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + } + else { + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, (uint32)sval); + } + CHECK_WRITE_WATCHPOINT(addr, offset); + HANDLE_OP_END(); + } + + /* memory size and memory grow instructions */ + HANDLE_OP(WASM_OP_MEMORY_SIZE) + { + uint32 mem_idx; + read_leb_memidx(frame_ip, frame_ip_end, mem_idx); + PUSH_PAGE_COUNT(memory->cur_page_count); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_MEMORY_GROW) + { + uint32 mem_idx, prev_page_count; + mem_offset_t delta; + + read_leb_memidx(frame_ip, frame_ip_end, mem_idx); + prev_page_count = memory->cur_page_count; + delta = POP_PAGE_COUNT(); + + if ( +#if WASM_ENABLE_MEMORY64 != 0 + delta > UINT32_MAX || +#endif + !wasm_enlarge_memory_with_idx(module, (uint32)delta, + mem_idx)) { + /* failed to memory.grow, return -1 */ + PUSH_PAGE_COUNT(-1); + } + else { + /* success, return previous page count */ + PUSH_PAGE_COUNT(prev_page_count); + /* update memory size, no need to update memory ptr as + it isn't changed in wasm_enlarge_memory */ +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY != 0 + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); +#endif + } + + HANDLE_OP_END(); + } + + /* constant instructions */ + HANDLE_OP(WASM_OP_I32_CONST) + DEF_OP_I_CONST(int32, I32); + HANDLE_OP_END(); + + HANDLE_OP(WASM_OP_I64_CONST) + DEF_OP_I_CONST(int64, I64); + HANDLE_OP_END(); + + HANDLE_OP(WASM_OP_F32_CONST) + { + uint8 *p_float = (uint8 *)frame_sp++; + for (i = 0; i < sizeof(float32); i++) + *p_float++ = *frame_ip++; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONST) + { + uint8 *p_float = (uint8 *)frame_sp++; + frame_sp++; + for (i = 0; i < sizeof(float64); i++) + *p_float++ = *frame_ip++; + HANDLE_OP_END(); + } + + /* comparison instructions of i32 */ + HANDLE_OP(WASM_OP_I32_EQZ) + { + DEF_OP_EQZ(I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_EQ) + { + DEF_OP_CMP(uint32, I32, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_NE) + { + DEF_OP_CMP(uint32, I32, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LT_S) + { + DEF_OP_CMP(int32, I32, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LT_U) + { + DEF_OP_CMP(uint32, I32, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GT_S) + { + DEF_OP_CMP(int32, I32, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GT_U) + { + DEF_OP_CMP(uint32, I32, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LE_S) + { + DEF_OP_CMP(int32, I32, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LE_U) + { + DEF_OP_CMP(uint32, I32, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GE_S) + { + DEF_OP_CMP(int32, I32, >=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GE_U) + { + DEF_OP_CMP(uint32, I32, >=); + HANDLE_OP_END(); + } + + /* comparison instructions of i64 */ + HANDLE_OP(WASM_OP_I64_EQZ) + { + DEF_OP_EQZ(I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EQ) + { + DEF_OP_CMP(uint64, I64, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_NE) + { + DEF_OP_CMP(uint64, I64, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LT_S) + { + DEF_OP_CMP(int64, I64, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LT_U) + { + DEF_OP_CMP(uint64, I64, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GT_S) + { + DEF_OP_CMP(int64, I64, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GT_U) + { + DEF_OP_CMP(uint64, I64, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LE_S) + { + DEF_OP_CMP(int64, I64, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LE_U) + { + DEF_OP_CMP(uint64, I64, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GE_S) + { + DEF_OP_CMP(int64, I64, >=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GE_U) + { + DEF_OP_CMP(uint64, I64, >=); + HANDLE_OP_END(); + } + + /* comparison instructions of f32 */ + HANDLE_OP(WASM_OP_F32_EQ) + { + DEF_OP_CMP(float32, F32, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_NE) + { + DEF_OP_CMP(float32, F32, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_LT) + { + DEF_OP_CMP(float32, F32, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_GT) + { + DEF_OP_CMP(float32, F32, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_LE) + { + DEF_OP_CMP(float32, F32, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_GE) + { + DEF_OP_CMP(float32, F32, >=); + HANDLE_OP_END(); + } + + /* comparison instructions of f64 */ + HANDLE_OP(WASM_OP_F64_EQ) + { + DEF_OP_CMP(float64, F64, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_NE) + { + DEF_OP_CMP(float64, F64, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_LT) + { + DEF_OP_CMP(float64, F64, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_GT) + { + DEF_OP_CMP(float64, F64, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_LE) + { + DEF_OP_CMP(float64, F64, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_GE) + { + DEF_OP_CMP(float64, F64, >=); + HANDLE_OP_END(); + } + + /* numeric instructions of i32 */ + HANDLE_OP(WASM_OP_I32_CLZ) + { + DEF_OP_BIT_COUNT(uint32, I32, clz32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_CTZ) + { + DEF_OP_BIT_COUNT(uint32, I32, ctz32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_POPCNT) + { + DEF_OP_BIT_COUNT(uint32, I32, popcount32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_ADD) + { + DEF_OP_NUMERIC(uint32, uint32, I32, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SUB) + { + DEF_OP_NUMERIC(uint32, uint32, I32, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_MUL) + { + DEF_OP_NUMERIC(uint32, uint32, I32, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_DIV_S) + { + int32 a, b; + + b = POP_I32(); + a = POP_I32(); + if (a == (int32)0x80000000 && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_DIV_U) + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_REM_S) + { + int32 a, b; + + b = POP_I32(); + a = POP_I32(); + if (a == (int32)0x80000000 && b == -1) { + PUSH_I32(0); + HANDLE_OP_END(); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_REM_U) + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_AND) + { + DEF_OP_NUMERIC(uint32, uint32, I32, &); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_OR) + { + DEF_OP_NUMERIC(uint32, uint32, I32, |); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_XOR) + { + DEF_OP_NUMERIC(uint32, uint32, I32, ^); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SHL) + { + DEF_OP_NUMERIC2(uint32, uint32, I32, <<); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SHR_S) + { + DEF_OP_NUMERIC2(int32, uint32, I32, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SHR_U) + { + DEF_OP_NUMERIC2(uint32, uint32, I32, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_ROTL) + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + PUSH_I32(rotl32(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_ROTR) + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + PUSH_I32(rotr32(a, b)); + HANDLE_OP_END(); + } + + /* numeric instructions of i64 */ + HANDLE_OP(WASM_OP_I64_CLZ) + { + DEF_OP_BIT_COUNT(uint64, I64, clz64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_CTZ) + { + DEF_OP_BIT_COUNT(uint64, I64, ctz64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_POPCNT) + { + DEF_OP_BIT_COUNT(uint64, I64, popcount64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_ADD) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SUB) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_MUL) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_DIV_S) + { + int64 a, b; + + b = POP_I64(); + a = POP_I64(); + if (a == (int64)0x8000000000000000LL && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_DIV_U) + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_REM_S) + { + int64 a, b; + + b = POP_I64(); + a = POP_I64(); + if (a == (int64)0x8000000000000000LL && b == -1) { + PUSH_I64(0); + HANDLE_OP_END(); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_REM_U) + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_AND) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, &); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_OR) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, |); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_XOR) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, ^); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SHL) + { + DEF_OP_NUMERIC2_64(uint64, uint64, I64, <<); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SHR_S) + { + DEF_OP_NUMERIC2_64(int64, uint64, I64, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SHR_U) + { + DEF_OP_NUMERIC2_64(uint64, uint64, I64, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_ROTL) + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + PUSH_I64(rotl64(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_ROTR) + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + PUSH_I64(rotr64(a, b)); + HANDLE_OP_END(); + } + + /* numeric instructions of f32 */ + HANDLE_OP(WASM_OP_F32_ABS) + { + DEF_OP_MATH(float32, F32, fabsf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_NEG) + { + uint32 u32 = frame_sp[-1]; + uint32 sign_bit = u32 & ((uint32)1 << 31); + if (sign_bit) + frame_sp[-1] = u32 & ~((uint32)1 << 31); + else + frame_sp[-1] = u32 | ((uint32)1 << 31); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CEIL) + { + DEF_OP_MATH(float32, F32, ceilf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_FLOOR) + { + DEF_OP_MATH(float32, F32, floorf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_TRUNC) + { + DEF_OP_MATH(float32, F32, truncf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_NEAREST) + { + DEF_OP_MATH(float32, F32, rintf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_SQRT) + { + DEF_OP_MATH(float32, F32, sqrtf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_ADD) + { + DEF_OP_NUMERIC(float32, float32, F32, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_SUB) + { + DEF_OP_NUMERIC(float32, float32, F32, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_MUL) + { + DEF_OP_NUMERIC(float32, float32, F32, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_DIV) + { + DEF_OP_NUMERIC(float32, float32, F32, /); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_MIN) + { + float32 a, b; + + b = POP_F32(); + a = POP_F32(); + + PUSH_F32(f32_min(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_MAX) + { + float32 a, b; + + b = POP_F32(); + a = POP_F32(); + + PUSH_F32(f32_max(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_COPYSIGN) + { + float32 a, b; + + b = POP_F32(); + a = POP_F32(); + PUSH_F32(local_copysignf(a, b)); + HANDLE_OP_END(); + } + + /* numeric instructions of f64 */ + HANDLE_OP(WASM_OP_F64_ABS) + { + DEF_OP_MATH(float64, F64, fabs); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_NEG) + { + uint64 u64 = GET_I64_FROM_ADDR(frame_sp - 2); + uint64 sign_bit = u64 & (((uint64)1) << 63); + if (sign_bit) + PUT_I64_TO_ADDR(frame_sp - 2, (u64 & ~(((uint64)1) << 63))); + else + PUT_I64_TO_ADDR(frame_sp - 2, (u64 | (((uint64)1) << 63))); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CEIL) + { + DEF_OP_MATH(float64, F64, ceil); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_FLOOR) + { + DEF_OP_MATH(float64, F64, floor); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_TRUNC) + { + DEF_OP_MATH(float64, F64, trunc); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_NEAREST) + { + DEF_OP_MATH(float64, F64, rint); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_SQRT) + { + DEF_OP_MATH(float64, F64, sqrt); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_ADD) + { + DEF_OP_NUMERIC_64(float64, float64, F64, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_SUB) + { + DEF_OP_NUMERIC_64(float64, float64, F64, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_MUL) + { + DEF_OP_NUMERIC_64(float64, float64, F64, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_DIV) + { + DEF_OP_NUMERIC_64(float64, float64, F64, /); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_MIN) + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + PUSH_F64(f64_min(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_MAX) + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + PUSH_F64(f64_max(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_COPYSIGN) + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + PUSH_F64(local_copysign(a, b)); + HANDLE_OP_END(); + } + + /* conversions of i32 */ + HANDLE_OP(WASM_OP_I32_WRAP_I64) + { + int32 value = (int32)(POP_I64() & 0xFFFFFFFFLL); + PUSH_I32(value); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_S_F32) + { + /* We don't use INT32_MIN/INT32_MAX/UINT32_MIN/UINT32_MAX, + since float/double values of ieee754 cannot precisely + represent all int32/uint32/int64/uint64 values, e.g. + UINT32_MAX is 4294967295, but (float32)4294967295 is + 4294967296.0f, but not 4294967295.0f. */ + DEF_OP_TRUNC_F32(-2147483904.0f, 2147483648.0f, true, true); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_U_F32) + { + DEF_OP_TRUNC_F32(-1.0f, 4294967296.0f, true, false); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_S_F64) + { + DEF_OP_TRUNC_F64(-2147483649.0, 2147483648.0, true, true); + /* frame_sp can't be moved in trunc function, we need to + manually adjust it if src and dst op's cell num is + different */ + frame_sp--; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_U_F64) + { + DEF_OP_TRUNC_F64(-1.0, 4294967296.0, true, false); + frame_sp--; + HANDLE_OP_END(); + } + + /* conversions of i64 */ + HANDLE_OP(WASM_OP_I64_EXTEND_S_I32) + { + DEF_OP_CONVERT(int64, I64, int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND_U_I32) + { + DEF_OP_CONVERT(int64, I64, uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_S_F32) + { + DEF_OP_TRUNC_F32(-9223373136366403584.0f, + 9223372036854775808.0f, false, true); + frame_sp++; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_U_F32) + { + DEF_OP_TRUNC_F32(-1.0f, 18446744073709551616.0f, false, false); + frame_sp++; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_S_F64) + { + DEF_OP_TRUNC_F64(-9223372036854777856.0, 9223372036854775808.0, + false, true); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_U_F64) + { + DEF_OP_TRUNC_F64(-1.0, 18446744073709551616.0, false, false); + HANDLE_OP_END(); + } + + /* conversions of f32 */ + HANDLE_OP(WASM_OP_F32_CONVERT_S_I32) + { + DEF_OP_CONVERT(float32, F32, int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONVERT_U_I32) + { + DEF_OP_CONVERT(float32, F32, uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONVERT_S_I64) + { + DEF_OP_CONVERT(float32, F32, int64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONVERT_U_I64) + { + DEF_OP_CONVERT(float32, F32, uint64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_DEMOTE_F64) + { + DEF_OP_CONVERT(float32, F32, float64, F64); + HANDLE_OP_END(); + } + + /* conversions of f64 */ + HANDLE_OP(WASM_OP_F64_CONVERT_S_I32) + { + DEF_OP_CONVERT(float64, F64, int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONVERT_U_I32) + { + DEF_OP_CONVERT(float64, F64, uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONVERT_S_I64) + { + DEF_OP_CONVERT(float64, F64, int64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONVERT_U_I64) + { + DEF_OP_CONVERT(float64, F64, uint64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_PROMOTE_F32) + { + DEF_OP_CONVERT(float64, F64, float32, F32); + HANDLE_OP_END(); + } + + /* reinterpretations */ + HANDLE_OP(WASM_OP_I32_REINTERPRET_F32) + HANDLE_OP(WASM_OP_I64_REINTERPRET_F64) + HANDLE_OP(WASM_OP_F32_REINTERPRET_I32) + HANDLE_OP(WASM_OP_F64_REINTERPRET_I64) + { + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_EXTEND8_S) + { + DEF_OP_CONVERT(int32, I32, int8, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_EXTEND16_S) + { + DEF_OP_CONVERT(int32, I32, int16, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND8_S) + { + DEF_OP_CONVERT(int64, I64, int8, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND16_S) + { + DEF_OP_CONVERT(int64, I64, int16, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND32_S) + { + DEF_OP_CONVERT(int64, I64, int32, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_MISC_PREFIX) + { + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-2147483904.0f, 2147483648.0f, + true, true); + break; + case WASM_OP_I32_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 4294967296.0f, true, false); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-2147483649.0, 2147483648.0, true, + true); + frame_sp--; + break; + case WASM_OP_I32_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 4294967296.0, true, false); + frame_sp--; + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-9223373136366403584.0f, + 9223372036854775808.0f, false, + true); + frame_sp++; + break; + case WASM_OP_I64_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 18446744073709551616.0f, + false, false); + frame_sp++; + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-9223372036854777856.0, + 9223372036854775808.0, false, + true); + break; + case WASM_OP_I64_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 18446744073709551616.0, + false, false); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 segment; + mem_offset_t addr; + uint64 bytes, offset, seg_len; + uint8 *data; + + read_leb_uint32(frame_ip, frame_ip_end, segment); +#if WASM_ENABLE_MULTI_MEMORY != 0 + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip memory index */ + frame_ip++; +#endif + + bytes = (uint64)(uint32)POP_I32(); + offset = (uint64)(uint32)POP_I32(); + addr = (mem_offset_t)POP_MEM_OFFSET(); + +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr); +#else +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)(uint32)addr, + bytes)) + shared_heap_addr_app_to_native((uint64)(uint32)addr, + maddr); + else +#endif + { + if ((uint64)(uint32)addr + bytes > linear_mem_size) + goto out_of_bounds; + maddr = memory->memory_data + (uint32)addr; + } +#endif + + if (bh_bitmap_get_bit(module->e->common.data_dropped, + segment)) { + seg_len = 0; + data = NULL; + } + else { + seg_len = + (uint64)module->module->data_segments[segment] + ->data_length; + data = module->module->data_segments[segment]->data; + } + if (offset + bytes > seg_len) + goto out_of_bounds; + + bh_memcpy_s(maddr, (uint32)(linear_mem_size - addr), + data + offset, (uint32)bytes); + break; + } + case WASM_OP_DATA_DROP: + { + uint32 segment; + + read_leb_uint32(frame_ip, frame_ip_end, segment); + bh_bitmap_set_bit(module->e->common.data_dropped, + segment); + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + { + mem_offset_t dst, src, len; + uint8 *mdst, *msrc; + + len = POP_MEM_OFFSET(); + src = POP_MEM_OFFSET(); + dst = POP_MEM_OFFSET(); + +#if WASM_ENABLE_MULTI_MEMORY != 0 + /* dst memidx */ + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip dst memidx */ + frame_ip += 1; +#endif + // TODO: apply memidx +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + /* dst boundary check */ +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); +#else /* else of OS_ENABLE_HW_BOUND_CHECK */ +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)dst, len)) { + shared_heap_addr_app_to_native((uint64)dst, mdst); + } + else +#endif + { + if ((uint64)dst + len > linear_mem_size) + goto out_of_bounds; + mdst = memory->memory_data + dst; + } +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + +#if WASM_ENABLE_MULTI_MEMORY != 0 + /* src memidx */ + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip src memidx */ + frame_ip += 1; +#endif + // TODO: apply memidx +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + /* src boundary check */ +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); +#else +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)src, len)) + shared_heap_addr_app_to_native((uint64)src, msrc); + else +#endif + { + if ((uint64)src + len > linear_mem_size) + goto out_of_bounds; + msrc = memory->memory_data + src; + } +#endif + + /* + * avoid unnecessary operations + * + * since dst and src both are valid indexes in the + * linear memory, mdst and msrc can't be NULL + * + * The spec. converts memory.copy into i32.load8 and + * i32.store8; the following are runtime-specific + * optimizations. + * + */ + if (len && mdst != msrc) { + /* allowing the destination and source to overlap */ + memmove(mdst, msrc, len); + } + break; + } + case WASM_OP_MEMORY_FILL: + { + mem_offset_t dst, len; + uint8 fill_val, *mdst; + +#if WASM_ENABLE_MULTI_MEMORY != 0 + read_leb_memidx(frame_ip, frame_ip_end, memidx); +#else + /* skip memory index */ + frame_ip++; +#endif + + len = POP_MEM_OFFSET(); + fill_val = POP_I32(); + dst = POP_MEM_OFFSET(); + +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); +#else +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)(uint32)dst, len)) + shared_heap_addr_app_to_native((uint64)(uint32)dst, + mdst); + else +#endif + { + if ((uint64)(uint32)dst + len > linear_mem_size) + goto out_of_bounds; + mdst = memory->memory_data + (uint32)dst; + } +#endif + + memset(mdst, fill_val, len); + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx; + tbl_elem_idx_t elem_idx, d; + uint32 n, s; + WASMTableInstance *tbl_inst; + table_elem_type_t *table_elems; + InitializerExpression *tbl_seg_init_values = NULL, + *init_values; + uint32 tbl_seg_len = 0; + + read_leb_uint32(frame_ip, frame_ip_end, elem_idx); + bh_assert(elem_idx < module->module->table_seg_count); + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (tbl_elem_idx_t)POP_TBL_ELEM_IDX(); + + if (!bh_bitmap_get_bit(module->e->common.elem_dropped, + elem_idx)) { + /* table segment isn't dropped */ + tbl_seg_init_values = + module->module->table_segments[elem_idx] + .init_values; + tbl_seg_len = + module->module->table_segments[elem_idx] + .value_count; + } + + /* TODO: memory64 current implementation of table64 + * still assumes the max table size UINT32_MAX + */ + if ( +#if WASM_ENABLE_MEMORY64 != 0 + d > UINT32_MAX || +#endif + offset_len_out_of_bounds(s, n, tbl_seg_len) + || offset_len_out_of_bounds((uint32)d, n, + tbl_inst->cur_size)) { + wasm_set_exception(module, + "out of bounds table access"); + goto got_exception; + } + + if (!n) { + break; + } + + table_elems = tbl_inst->elems + d; + init_values = tbl_seg_init_values + s; +#if WASM_ENABLE_GC != 0 + SYNC_ALL_TO_FRAME(); +#endif + for (i = 0; i < n; i++) { + /* UINT32_MAX indicates that it is a null ref */ + bh_assert(init_values[i].init_expr_type + == INIT_EXPR_TYPE_REFNULL_CONST + || init_values[i].init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST); +#if WASM_ENABLE_GC == 0 + table_elems[i] = (table_elem_type_t)init_values[i] + .u.unary.v.ref_index; +#else + if (init_values[i].u.unary.v.ref_index + != UINT32_MAX) { + if (!(func_obj = wasm_create_func_obj( + module, + init_values[i].u.unary.v.ref_index, + true, NULL, 0))) { + goto got_exception; + } + table_elems[i] = func_obj; + } + else { + table_elems[i] = NULL_REF; + } +#endif + } + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 elem_idx; + read_leb_uint32(frame_ip, frame_ip_end, elem_idx); + bh_assert(elem_idx < module->module->table_seg_count); + + bh_bitmap_set_bit(module->e->common.elem_dropped, + elem_idx); + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + tbl_elem_idx_t n, s, d; + WASMTableInstance *src_tbl_inst, *dst_tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, dst_tbl_idx); + bh_assert(dst_tbl_idx < module->table_count); + + dst_tbl_inst = wasm_get_table_inst(module, dst_tbl_idx); + + read_leb_uint32(frame_ip, frame_ip_end, src_tbl_idx); + bh_assert(src_tbl_idx < module->table_count); + + src_tbl_inst = wasm_get_table_inst(module, src_tbl_idx); + +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = src_tbl_inst->is_table64 + && dst_tbl_inst->is_table64; +#endif + n = (tbl_elem_idx_t)POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = src_tbl_inst->is_table64; +#endif + s = (tbl_elem_idx_t)POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = dst_tbl_inst->is_table64; +#endif + d = (tbl_elem_idx_t)POP_TBL_ELEM_IDX(); + + if ( +#if WASM_ENABLE_MEMORY64 != 0 + n > UINT32_MAX || s > UINT32_MAX || d > UINT32_MAX + || +#endif + offset_len_out_of_bounds((uint32)d, (uint32)n, + dst_tbl_inst->cur_size) + || offset_len_out_of_bounds( + (uint32)s, (uint32)n, src_tbl_inst->cur_size)) { + wasm_set_exception(module, + "out of bounds table access"); + goto got_exception; + } + + /* if s >= d, copy from front to back */ + /* if s < d, copy from back to front */ + /* merge all together */ + bh_memmove_s((uint8 *)dst_tbl_inst + + offsetof(WASMTableInstance, elems) + + d * sizeof(table_elem_type_t), + (uint32)((dst_tbl_inst->cur_size - d) + * sizeof(table_elem_type_t)), + (uint8 *)src_tbl_inst + + offsetof(WASMTableInstance, elems) + + s * sizeof(table_elem_type_t), + (uint32)(n * sizeof(table_elem_type_t))); + break; + } + case WASM_OP_TABLE_GROW: + { + WASMTableInstance *tbl_inst; + uint32 tbl_idx, orig_tbl_sz; + tbl_elem_idx_t n; + table_elem_type_t init_val; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + + orig_tbl_sz = tbl_inst->cur_size; + + n = POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_GC == 0 + init_val = POP_I32(); +#else + init_val = POP_REF(); +#endif + + if ( +#if WASM_ENABLE_MEMORY64 != 0 + n > UINT32_MAX || +#endif + !wasm_enlarge_table(module, tbl_idx, (uint32)n, + init_val)) { + PUSH_TBL_ELEM_IDX(-1); + } + else { + PUSH_TBL_ELEM_IDX(orig_tbl_sz); + } + break; + } + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + + PUSH_TBL_ELEM_IDX(tbl_inst->cur_size); + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx; + tbl_elem_idx_t n, elem_idx; + WASMTableInstance *tbl_inst; + table_elem_type_t fill_val; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); +#if WASM_ENABLE_MEMORY64 != 0 + is_table64 = tbl_inst->is_table64; +#endif + + n = POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_GC == 0 + fill_val = POP_I32(); +#else + fill_val = POP_REF(); +#endif + elem_idx = POP_TBL_ELEM_IDX(); + + if ( +#if WASM_ENABLE_MEMORY64 != 0 + n > UINT32_MAX || elem_idx > UINT32_MAX || +#endif + offset_len_out_of_bounds((uint32)elem_idx, + (uint32)n, + tbl_inst->cur_size)) { + wasm_set_exception(module, + "out of bounds table access"); + goto got_exception; + } + + for (; n != 0; elem_idx++, n--) { + tbl_inst->elems[elem_idx] = fill_val; + } + break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } + HANDLE_OP_END(); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + HANDLE_OP(WASM_OP_ATOMIC_PREFIX) + { + mem_offset_t offset = 0, addr; + uint32 align = 0; + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + /* opcode1 was checked in loader and is no larger than + UINT8_MAX */ + opcode = (uint8)opcode1; + + if (opcode != WASM_OP_ATOMIC_FENCE) { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_mem_offset(frame_ip, frame_ip_end, offset); + } + + switch (opcode) { + case WASM_OP_ATOMIC_NOTIFY: + { + uint32 notify_count, ret; + + notify_count = POP_I32(); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + + ret = wasm_runtime_atomic_notify( + (WASMModuleInstanceCommon *)module, maddr, + notify_count); + if (ret == (uint32)-1) + goto got_exception; + + PUSH_I32(ret); + break; + } + case WASM_OP_ATOMIC_WAIT32: + { + uint64 timeout; + uint32 expect, ret; + + timeout = POP_I64(); + expect = POP_I32(); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + + ret = wasm_runtime_atomic_wait( + (WASMModuleInstanceCommon *)module, maddr, + (uint64)expect, timeout, false); + 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_WAIT64: + { + uint64 timeout, expect; + uint32 ret; + + timeout = POP_I64(); + expect = POP_I64(); + addr = POP_MEM_OFFSET(); + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(); + + ret = wasm_runtime_atomic_wait( + (WASMModuleInstanceCommon *)module, maddr, expect, + timeout, true); + 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: + { + /* Skip the memory index */ + frame_ip++; + os_atomic_thread_fence(os_memory_order_seq_cst); + break; + } + + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + { + uint32 readv; + + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_ATOMIC_I32_LOAD8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = (uint32)(*(uint8 *)maddr); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I32_LOAD16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = (uint32)LOAD_U16(maddr); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = LOAD_I32(maddr); + shared_memory_unlock(memory); + } + + PUSH_I32(readv); + break; + } + + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + { + uint64 readv; + + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_ATOMIC_I64_LOAD8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = (uint64)(*(uint8 *)maddr); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = (uint64)LOAD_U16(maddr); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD32_U) { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = (uint64)LOAD_U32(maddr); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + readv = LOAD_I64(maddr); + shared_memory_unlock(memory); + } + + PUSH_I64(readv); + break; + } + + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + { + uint32 sval; + + sval = (uint32)POP_I32(); + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_ATOMIC_I32_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + *(uint8 *)maddr = (uint8)sval; + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I32_STORE16) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + STORE_U16(maddr, (uint16)sval); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + STORE_U32(maddr, sval); + shared_memory_unlock(memory); + } + break; + } + + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + { + uint64 sval; + + sval = (uint64)POP_I64(); + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_ATOMIC_I64_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + *(uint8 *)maddr = (uint8)sval; + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_STORE16) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + STORE_U16(maddr, (uint16)sval); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_STORE32) { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + STORE_U32(maddr, (uint32)sval); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(); + shared_memory_lock(memory); + STORE_I64(maddr, sval); + shared_memory_unlock(memory); + } + break; + } + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + { + uint32 readv, sval, expect; + + sval = POP_I32(); + expect = POP_I32(); + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(); + + expect = (uint8)expect; + shared_memory_lock(memory); + readv = (uint32)(*(uint8 *)maddr); + if (readv == expect) + *(uint8 *)maddr = (uint8)(sval); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(); + + expect = (uint16)expect; + shared_memory_lock(memory); + readv = (uint32)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + + shared_memory_lock(memory); + readv = LOAD_I32(maddr); + if (readv == expect) + STORE_U32(maddr, sval); + shared_memory_unlock(memory); + } + PUSH_I32(readv); + break; + } + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + { + uint64 readv, sval, expect; + + sval = (uint64)POP_I64(); + expect = (uint64)POP_I64(); + addr = POP_MEM_OFFSET(); + + if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(); + + expect = (uint8)expect; + shared_memory_lock(memory); + readv = (uint64)(*(uint8 *)maddr); + if (readv == expect) + *(uint8 *)maddr = (uint8)(sval); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(); + + expect = (uint16)expect; + shared_memory_lock(memory); + readv = (uint64)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U) { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(); + + expect = (uint32)expect; + shared_memory_lock(memory); + readv = (uint64)LOAD_U32(maddr); + if (readv == expect) + STORE_U32(maddr, (uint32)(sval)); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(); + + shared_memory_lock(memory); + readv = (uint64)LOAD_I64(maddr); + if (readv == expect) + STORE_I64(maddr, sval); + shared_memory_unlock(memory); + } + PUSH_I64(readv); + break; + } + + DEF_ATOMIC_RMW_OPCODE(ADD, +); + DEF_ATOMIC_RMW_OPCODE(SUB, -); + DEF_ATOMIC_RMW_OPCODE(AND, &); + DEF_ATOMIC_RMW_OPCODE(OR, |); + DEF_ATOMIC_RMW_OPCODE(XOR, ^); + /* xchg, ignore the read value, and store the given + value: readv * 0 + sval */ + DEF_ATOMIC_RMW_OPCODE(XCHG, *0 +); + } + + HANDLE_OP_END(); + } +#endif + + HANDLE_OP(WASM_OP_IMPDEP) + { + frame = prev_frame; + frame_ip = frame->ip; + frame_sp = frame->sp; + frame_csp = frame->csp; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif + goto call_func_from_entry; + } + +#if WASM_ENABLE_DEBUG_INTERP != 0 + HANDLE_OP(DEBUG_OP_BREAK) + { + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP); + WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, + WASM_SUSPEND_FLAG_SUSPEND); + frame_ip--; + SYNC_ALL_TO_FRAME(); + CHECK_SUSPEND_FLAGS(); + HANDLE_OP_END(); + } +#endif +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + HANDLE_OP(WASM_OP_UNUSED_0x0a) +#if WASM_ENABLE_TAIL_CALL == 0 + HANDLE_OP(WASM_OP_RETURN_CALL) + HANDLE_OP(WASM_OP_RETURN_CALL_INDIRECT) +#endif +#if WASM_ENABLE_SHARED_MEMORY == 0 + HANDLE_OP(WASM_OP_ATOMIC_PREFIX) +#endif +#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0 + HANDLE_OP(WASM_OP_SELECT_T) + HANDLE_OP(WASM_OP_TABLE_GET) + HANDLE_OP(WASM_OP_TABLE_SET) + HANDLE_OP(WASM_OP_REF_NULL) + HANDLE_OP(WASM_OP_REF_IS_NULL) + HANDLE_OP(WASM_OP_REF_FUNC) +#endif +#if WASM_ENABLE_GC == 0 + HANDLE_OP(WASM_OP_CALL_REF) + HANDLE_OP(WASM_OP_RETURN_CALL_REF) + HANDLE_OP(WASM_OP_REF_EQ) + HANDLE_OP(WASM_OP_REF_AS_NON_NULL) + HANDLE_OP(WASM_OP_BR_ON_NULL) + HANDLE_OP(WASM_OP_BR_ON_NON_NULL) + HANDLE_OP(WASM_OP_GC_PREFIX) +#endif +#if WASM_ENABLE_EXCE_HANDLING == 0 + HANDLE_OP(WASM_OP_TRY) + HANDLE_OP(WASM_OP_CATCH) + HANDLE_OP(WASM_OP_THROW) + HANDLE_OP(WASM_OP_RETHROW) + HANDLE_OP(WASM_OP_DELEGATE) + HANDLE_OP(WASM_OP_CATCH_ALL) + HANDLE_OP(EXT_OP_TRY) +#endif + /* SIMD isn't supported by interpreter, but when JIT is + enabled, `iwasm --interp ` may be run to + trigger the SIMD opcode in interpreter */ + HANDLE_OP(WASM_OP_SIMD_PREFIX) + HANDLE_OP(WASM_OP_UNUSED_0x16) + HANDLE_OP(WASM_OP_UNUSED_0x17) + HANDLE_OP(WASM_OP_UNUSED_0x27) + /* Used by fast interpreter */ + HANDLE_OP(EXT_OP_SET_LOCAL_FAST_I64) + HANDLE_OP(EXT_OP_TEE_LOCAL_FAST_I64) + HANDLE_OP(EXT_OP_COPY_STACK_TOP) + HANDLE_OP(EXT_OP_COPY_STACK_TOP_I64) + HANDLE_OP(EXT_OP_COPY_STACK_VALUES) + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES != 0 */ + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + continue; +#else + FETCH_OPCODE_AND_DISPATCH(); +#endif + +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + call_func_from_return_call: + { + POP(cur_func->param_cell_num); + if (cur_func->param_cell_num > 0) { + word_copy(frame->lp, frame_sp, cur_func->param_cell_num); + } + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + is_return_call = true; + goto call_func_from_entry; + } +#endif + call_func_from_interp: + { + /* Only do the copy when it's called from interpreter. */ + WASMInterpFrame *outs_area = wasm_exec_env_wasm_stack_top(exec_env); + if (cur_func->param_cell_num > 0) { + POP(cur_func->param_cell_num); + word_copy(outs_area->lp, frame_sp, cur_func->param_cell_num); + } + SYNC_ALL_TO_FRAME(); + prev_frame = frame; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif + } + + call_func_from_entry: + { + if (cur_func->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->import_func_inst) { + wasm_interp_call_func_import(module, exec_env, cur_func, + prev_frame); +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (is_return_call) { + /* the frame was freed before tail calling and + the prev_frame was set as exec_env's cur_frame, + so here we recover context from prev_frame */ + RECOVER_CONTEXT(prev_frame); + } + else +#endif + { + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + } + +#if WASM_ENABLE_EXCE_HANDLING != 0 + char uncaught_exception[128] = { 0 }; + bool has_exception = + wasm_copy_exception(module, uncaught_exception); + if (has_exception + && strstr(uncaught_exception, "uncaught wasm exception")) { + uint32 import_exception; + /* initialize imported exception index to be invalid */ + SET_INVALID_TAGINDEX(import_exception); + + /* pull external exception */ + uint32 ext_exception = POP_I32(); + + /* external function came back with an exception or trap */ + /* lookup exception in import tags */ + WASMTagInstance *tag = module->e->tags; + for (uint32 t = 0; t < module->module->import_tag_count; + tag++, t++) { + + /* compare the module and the external index with the + * import tag data */ + if ((cur_func->u.func_import->import_module + == tag->u.tag_import->import_module) + && (ext_exception + == tag->u.tag_import + ->import_tag_index_linked)) { + /* set the import_exception to the import tag */ + import_exception = t; + break; + } + } + /* + * exchange the thrown exception (index valid in submodule) + * with the imported exception index (valid in this module) + * if the module did not import the exception, + * that results in a "INVALID_TAGINDEX", that triggers + * an CATCH_ALL block, if there is one. + */ + PUSH_I32(import_exception); + } +#endif /* end of WASM_ENABLE_EXCE_HANDLING != 0 */ + } + else +#endif /* end of WASM_ENABLE_MULTI_MODULE != 0 */ + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (is_return_call) { + /* the frame was freed before tail calling and + the prev_frame was set as exec_env's cur_frame, + so here we recover context from prev_frame */ + RECOVER_CONTEXT(prev_frame); + } + else +#endif + { + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + } + } + + /* update memory size, no need to update memory ptr as + it isn't changed in wasm_enlarge_memory */ +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY != 0 + if (memory) + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); +#endif + if (wasm_copy_exception(module, NULL)) { +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* the caller raised an exception */ + char uncaught_exception[128] = { 0 }; + bool has_exception = + wasm_copy_exception(module, uncaught_exception); + + /* libc_builtin signaled a "exception thrown by stdc++" trap */ + if (has_exception + && strstr(uncaught_exception, + "exception thrown by stdc++")) { + wasm_set_exception(module, NULL); + + /* setup internal c++ rethrow */ + exception_tag_index = 0; + goto find_a_catch_handler; + } + + /* when throw hits the end of a function it signals with a + * "uncaught wasm exception" trap */ + if (has_exception + && strstr(uncaught_exception, "uncaught wasm exception")) { + wasm_set_exception(module, NULL); + exception_tag_index = POP_I32(); + + /* rethrow the exception into that frame */ + goto find_a_catch_handler; + } +#endif /* WASM_ENABLE_EXCE_HANDLING != 0 */ + goto got_exception; + } + } + else { + WASMFunction *cur_wasm_func = cur_func->u.func; + WASMFuncType *func_type = cur_wasm_func->func_type; + uint32 max_stack_cell_num = cur_wasm_func->max_stack_cell_num; + uint32 cell_num_of_local_stack; +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + uint32 local_cell_idx; +#endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* account for exception handlers, bundle them here */ + uint32 eh_size = + cur_wasm_func->exception_handler_count * sizeof(uint8 *); + max_stack_cell_num += eh_size; +#endif + + cell_num_of_local_stack = cur_func->param_cell_num + + cur_func->local_cell_num + + max_stack_cell_num; + all_cell_num = cell_num_of_local_stack + + cur_wasm_func->max_block_num + * (uint32)sizeof(WASMBranchBlock) / 4; +#if WASM_ENABLE_GC != 0 + /* area of frame_ref */ + all_cell_num += (cell_num_of_local_stack + 3) / 4; +#endif + + /* param_cell_num, local_cell_num, max_stack_cell_num and + max_block_num are all no larger than UINT16_MAX (checked + in loader), all_cell_num must be smaller than 1MB */ + bh_assert(all_cell_num < 1 * BH_MB); + + frame_size = wasm_interp_interp_frame_size(all_cell_num); + if (!(frame = ALLOC_FRAME(exec_env, frame_size, prev_frame))) { + frame = prev_frame; + goto got_exception; + } + + /* Initialize the interpreter context. */ + frame->function = cur_func; + frame_ip = wasm_get_func_code(cur_func); + frame_ip_end = wasm_get_func_code_end(cur_func); + frame_lp = frame->lp; + + frame_sp = frame->sp_bottom = + frame_lp + cur_func->param_cell_num + cur_func->local_cell_num; + frame->sp_boundary = frame->sp_bottom + max_stack_cell_num; + + frame_csp = frame->csp_bottom = + (WASMBranchBlock *)frame->sp_boundary; + frame->csp_boundary = + frame->csp_bottom + cur_wasm_func->max_block_num; + +#if WASM_ENABLE_GC != 0 + /* frame->sp and frame->ip are used during GC root set enumeration, + * so we must initialized these fields here */ + frame->sp = frame_sp; + frame->ip = frame_ip; + frame_ref = (uint8 *)frame->csp_boundary; + init_frame_refs(frame_ref, (uint32)cell_num_of_local_stack, + cur_func); +#endif + + /* Initialize the local variables */ + memset(frame_lp + cur_func->param_cell_num, 0, + (uint32)(cur_func->local_cell_num * 4)); + +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + /* externref/funcref should be NULL_REF rather than 0 */ + local_cell_idx = cur_func->param_cell_num; + for (i = 0; i < cur_wasm_func->local_count; i++) { + if (cur_wasm_func->local_types[i] == VALUE_TYPE_EXTERNREF + || cur_wasm_func->local_types[i] == VALUE_TYPE_FUNCREF) { + *(frame_lp + local_cell_idx) = NULL_REF; + } + local_cell_idx += + wasm_value_type_cell_num(cur_wasm_func->local_types[i]); + } +#endif + + /* Push function block as first block */ + cell_num = func_type->ret_cell_num; + 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 + HANDLE_OP_END(); + } + + return_func: + { + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + + if (!prev_frame->ip) { + /* Called from native. */ + return; + } + + RECOVER_CONTEXT(prev_frame); +#if WASM_ENABLE_EXCE_HANDLING != 0 + if (wasm_get_exception(module)) { + wasm_set_exception(module, NULL); + exception_tag_index = POP_I32(); + goto find_a_catch_handler; + } +#endif + HANDLE_OP_END(); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + unaligned_atomic: + wasm_set_exception(module, "unaligned atomic"); + goto got_exception; +#endif + +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY_OPT != 0 + out_of_bounds: + wasm_set_exception(module, "out of bounds memory access"); +#endif + + got_exception: +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (wasm_exec_env_get_instance(exec_env) != NULL) { + uint8 *frame_ip_temp = frame_ip; + frame_ip = frame_ip_orig; + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP); + CHECK_SUSPEND_FLAGS(); + frame_ip = frame_ip_temp; + } +#endif + SYNC_ALL_TO_FRAME(); + return; + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + } +#else + FETCH_OPCODE_AND_DISPATCH(); +#endif +} + +#if WASM_ENABLE_GC != 0 +bool +wasm_interp_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) +{ + WASMInterpFrame *frame; + WASMObjectRef gc_obj; + int i; + + frame = wasm_exec_env_get_cur_frame(exec_env); + for (; frame; frame = frame->prev_frame) { + uint8 *frame_ref = get_frame_ref(frame); + for (i = 0; i < frame->sp - frame->lp; i++) { + if (frame_ref[i]) { + gc_obj = GET_REF_FROM_ADDR(frame->lp + i); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) { + return false; + } + } +#if UINTPTR_MAX == UINT64_MAX + bh_assert(frame_ref[i + 1]); + i++; +#endif + } + } + } + return true; +} +#endif + +#if WASM_ENABLE_FAST_JIT != 0 +/* + * ASAN is not designed to work with custom stack unwind or other low-level + * things. Ignore a function that does some low-level magic. (e.g. walking + * through the thread's stack bypassing the frame boundaries) + */ +#if defined(__GNUC__) || defined(__clang__) +__attribute__((no_sanitize_address)) +#endif +static void +fast_jit_call_func_bytecode(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *function, + WASMInterpFrame *frame) +{ + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); + JitInterpSwitchInfo info; + WASMModule *module = module_inst->module; + WASMFuncType *func_type = function->u.func->func_type; + uint8 type = func_type->result_count + ? func_type->types[func_type->param_count] + : VALUE_TYPE_VOID; + uint32 func_idx = (uint32)(function - module_inst->e->functions); + uint32 func_idx_non_import = func_idx - module->import_function_count; + int32 action; + +#if WASM_ENABLE_REF_TYPES != 0 + if (type == VALUE_TYPE_EXTERNREF || type == VALUE_TYPE_FUNCREF) + type = VALUE_TYPE_I32; +#endif + +#if WASM_ENABLE_LAZY_JIT != 0 + if (!jit_compiler_compile(module, func_idx)) { + wasm_set_exception(module_inst, "failed to compile fast jit function"); + return; + } +#endif + bh_assert(jit_compiler_is_compiled(module, func_idx)); + + /* Switch to jitted code to call the jit function */ + info.out.ret.last_return_type = type; + info.frame = frame; + frame->jitted_return_addr = + (uint8 *)jit_globals->return_to_interp_from_jitted; + action = jit_interp_switch_to_jitted( + exec_env, &info, func_idx, + module_inst->fast_jit_func_ptrs[func_idx_non_import]); + bh_assert(action == JIT_INTERP_ACTION_NORMAL + || (action == JIT_INTERP_ACTION_THROWN + && wasm_copy_exception( + (WASMModuleInstance *)exec_env->module_inst, NULL))); + + /* Get the return values form info.out.ret */ + if (func_type->result_count) { + switch (type) { + case VALUE_TYPE_I32: + *(frame->sp - function->ret_cell_num) = info.out.ret.ival[0]; + break; + case VALUE_TYPE_I64: + *(frame->sp - function->ret_cell_num) = info.out.ret.ival[0]; + *(frame->sp - function->ret_cell_num + 1) = + info.out.ret.ival[1]; + break; + case VALUE_TYPE_F32: + *(frame->sp - function->ret_cell_num) = info.out.ret.fval[0]; + break; + case VALUE_TYPE_F64: + *(frame->sp - function->ret_cell_num) = info.out.ret.fval[0]; + *(frame->sp - function->ret_cell_num + 1) = + info.out.ret.fval[1]; + break; + default: + bh_assert(0); + break; + } + } + (void)action; + (void)func_idx; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ + +#if WASM_ENABLE_JIT != 0 +#if WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_PERF_PROFILING != 0 \ + || WASM_ENABLE_AOT_STACK_FRAME != 0 +#if WASM_ENABLE_GC == 0 +bool +llvm_jit_alloc_frame(WASMExecEnv *exec_env, uint32 func_index) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + WASMInterpFrame *cur_frame, *frame; + uint32 size = (uint32)offsetof(WASMInterpFrame, lp); + + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); + + cur_frame = exec_env->cur_frame; + if (!cur_frame) + frame = (WASMInterpFrame *)exec_env->wasm_stack.bottom; + else + frame = (WASMInterpFrame *)((uint8 *)cur_frame + size); + + if ((uint8 *)frame + size > exec_env->wasm_stack.top_boundary) { + wasm_set_exception(module_inst, "wasm operand stack overflow"); + return false; + } + + frame->function = module_inst->e->functions + func_index; + /* No need to initialize ip, it will be committed in jitted code + when needed */ + /* frame->ip = NULL; */ + frame->prev_frame = cur_frame; + +#if WASM_ENABLE_PERF_PROFILING != 0 + frame->time_started = os_time_thread_cputime_us(); +#endif +#if WASM_ENABLE_MEMORY_PROFILING != 0 + { + uint32 wasm_stack_used = + (uint8 *)frame + size - exec_env->wasm_stack.bottom; + if (wasm_stack_used > exec_env->max_wasm_stack_used) + exec_env->max_wasm_stack_used = wasm_stack_used; + } +#endif + + exec_env->cur_frame = frame; + + return true; +} + +static inline void +llvm_jit_free_frame_internal(WASMExecEnv *exec_env) +{ + WASMInterpFrame *frame = exec_env->cur_frame; + WASMInterpFrame *prev_frame = frame->prev_frame; + + bh_assert(exec_env->module_inst->module_type == Wasm_Module_Bytecode); + +#if WASM_ENABLE_PERF_PROFILING != 0 + if (frame->function) { + uint64 time_elapsed = os_time_thread_cputime_us() - frame->time_started; + + frame->function->total_exec_time += time_elapsed; + frame->function->total_exec_cnt++; + + /* parent function */ + if (prev_frame) + prev_frame->function->children_exec_time += time_elapsed; + } +#endif + exec_env->cur_frame = prev_frame; +} + +void +llvm_jit_free_frame(WASMExecEnv *exec_env) +{ + llvm_jit_free_frame_internal(exec_env); +} + +#else /* else of WASM_ENABLE_GC == 0 */ + +bool +llvm_jit_alloc_frame(WASMExecEnv *exec_env, uint32 func_index) +{ + WASMModuleInstance *module_inst; + WASMModule *module; + WASMInterpFrame *frame; + uint32 size, max_local_cell_num, max_stack_cell_num; + + bh_assert(exec_env->module_inst->module_type == Wasm_Module_Bytecode); + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + module = module_inst->module; + + if (func_index >= func_index - module->import_function_count) { + WASMFunction *func = + module->functions[func_index - module->import_function_count]; + + max_local_cell_num = func->param_cell_num + func->local_cell_num; + max_stack_cell_num = func->max_stack_cell_num; + } + else { + WASMFunctionImport *func = + &((module->import_functions + func_index)->u.function); + + max_local_cell_num = func->func_type->param_cell_num > 2 + ? func->func_type->param_cell_num + : 2; + max_stack_cell_num = 0; + } + + size = + wasm_interp_interp_frame_size(max_local_cell_num + max_stack_cell_num); + + frame = wasm_exec_env_alloc_wasm_frame(exec_env, size); + if (!frame) { + wasm_set_exception(module_inst, "wasm operand stack overflow"); + return false; + } + + frame->function = module_inst->e->functions + func_index; +#if WASM_ENABLE_PERF_PROFILING != 0 + frame->time_started = os_time_thread_cputime_us(); +#endif + frame->prev_frame = wasm_exec_env_get_cur_frame(exec_env); + + /* No need to initialize ip, it will be committed in jitted code + when needed */ + /* frame->ip = NULL; */ + +#if WASM_ENABLE_GC != 0 + frame->sp = frame->lp + max_local_cell_num; + + /* Initialize frame ref flags for import function */ + if (func_index < module->import_function_count) { + WASMFunctionImport *func = + &((module->import_functions + func_index)->u.function); + WASMFuncType *func_type = func->func_type; + /* native function doesn't have operand stack and label stack */ + uint8 *frame_ref = (uint8 *)frame->sp; + uint32 i, j, k, value_type_cell_num; + + for (i = 0, j = 0; i < func_type->param_count; i++) { + if (wasm_is_type_reftype(func_type->types[i]) + && !wasm_is_reftype_i31ref(func_type->types[i])) { + frame_ref[j++] = 1; +#if UINTPTR_MAX == UINT64_MAX + frame_ref[j++] = 1; +#endif + } + else { + value_type_cell_num = + wasm_value_type_cell_num(func_type->types[i]); + for (k = 0; k < value_type_cell_num; k++) + frame_ref[j++] = 0; + } + } + } +#endif + + wasm_exec_env_set_cur_frame(exec_env, frame); + + return true; +} + +static inline void +llvm_jit_free_frame_internal(WASMExecEnv *exec_env) +{ + WASMInterpFrame *frame; + WASMInterpFrame *prev_frame; + + bh_assert(exec_env->module_inst->module_type == Wasm_Module_Bytecode); + + frame = wasm_exec_env_get_cur_frame(exec_env); + prev_frame = frame->prev_frame; + +#if WASM_ENABLE_PERF_PROFILING != 0 + if (frame->function) { + uint64 time_elapsed = os_time_thread_cputime_us() - frame->time_started; + + frame->function->total_exec_time += time_elapsed; + frame->function->total_exec_cnt++; + + /* parent function */ + if (prev_frame) + prev_frame->function->children_exec_time += time_elapsed; + } +#endif + wasm_exec_env_free_wasm_frame(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); +} + +void +llvm_jit_free_frame(WASMExecEnv *exec_env) +{ + llvm_jit_free_frame_internal(exec_env); +} +#endif /* end of WASM_ENABLE_GC == 0 */ + +void +llvm_jit_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame) +{ +#if WASM_ENABLE_PERF_PROFILING != 0 + WASMInterpFrame *cur_frame = exec_env->cur_frame; + + if (alloc_frame) { + cur_frame->time_started = os_time_thread_cputime_us(); + } + else { + if (cur_frame->function) { + WASMInterpFrame *prev_frame = cur_frame->prev_frame; + uint64 time_elapsed = + os_time_thread_cputime_us() - cur_frame->time_started; + + cur_frame->function->total_exec_time += time_elapsed; + cur_frame->function->total_exec_cnt++; + + /* parent function */ + if (prev_frame) + prev_frame->function->children_exec_time += time_elapsed; + } + } +#endif + +#if WASM_ENABLE_MEMORY_PROFILING != 0 + if (alloc_frame) { +#if WASM_ENABLE_GC == 0 + uint32 wasm_stack_used = (uint8 *)exec_env->cur_frame + + (uint32)offsetof(WASMInterpFrame, lp) + - exec_env->wasm_stack.bottom; +#else + uint32 wasm_stack_used = + exec_env->wasm_stack.top - exec_env->wasm_stack.bottom; +#endif + if (wasm_stack_used > exec_env->max_wasm_stack_used) + exec_env->max_wasm_stack_used = wasm_stack_used; + } +#endif +} +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK != 0 \ + || WASM_ENABLE_PERF_PROFILING != 0 \ + || WASM_ENABLE_AOT_STACK_FRAME != 0 */ + +static bool +llvm_jit_call_func_bytecode(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *function, uint32 argc, + uint32 argv[]) +{ + WASMFuncType *func_type = function->u.func->func_type; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + uint32 func_idx = (uint32)(function - module_inst->e->functions); + bool ret = false; + +#if (WASM_ENABLE_DUMP_CALL_STACK != 0) || (WASM_ENABLE_PERF_PROFILING != 0) \ + || (WASM_ENABLE_AOT_STACK_FRAME != 0) + if (!llvm_jit_alloc_frame(exec_env, function - module_inst->e->functions)) { + /* wasm operand stack overflow has been thrown, + no need to throw again */ + return false; + } +#endif + + if (ext_ret_count > 0) { + uint32 cell_num = 0, i; + uint8 *ext_ret_types = func_type->types + func_type->param_count + 1; + uint32 argv1_buf[32], *argv1 = argv1_buf, *ext_rets = NULL; + uint32 *argv_ret = argv; + uint32 ext_ret_cell = wasm_get_cell_num(ext_ret_types, ext_ret_count); + uint64 size; + + /* Allocate memory all arguments */ + size = + sizeof(uint32) * (uint64)argc /* original arguments */ + + sizeof(void *) + * (uint64)ext_ret_count /* extra result values' addr */ + + sizeof(uint32) * (uint64)ext_ret_cell; /* extra result values */ + if (size > sizeof(argv1_buf)) { + if (size > UINT32_MAX + || !(argv1 = wasm_runtime_malloc((uint32)size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + ret = false; + goto fail; + } + } + + /* Copy original arguments */ + bh_memcpy_s(argv1, (uint32)size, argv, sizeof(uint32) * argc); + + /* Get the extra result value's address */ + ext_rets = + argv1 + argc + sizeof(void *) / sizeof(uint32) * ext_ret_count; + + /* Append each extra result value's address to original arguments */ + for (i = 0; i < ext_ret_count; i++) { + *(uintptr_t *)(argv1 + argc + sizeof(void *) / sizeof(uint32) * i) = + (uintptr_t)(ext_rets + cell_num); + cell_num += wasm_value_type_cell_num(ext_ret_types[i]); + } + + ret = wasm_runtime_invoke_native( + exec_env, module_inst->func_ptrs[func_idx], func_type, NULL, NULL, + argv1, argc, argv); + if (!ret) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + goto fail; + } + + /* Get extra result values */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + argv_ret++; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + argv_ret += 2; + break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + argv_ret += 4; + break; +#endif + default: + bh_assert(0); + break; + } + + ext_rets = + argv1 + argc + sizeof(void *) / sizeof(uint32) * ext_ret_count; + bh_memcpy_s(argv_ret, sizeof(uint32) * cell_num, ext_rets, + sizeof(uint32) * cell_num); + + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + ret = true; + } + else { +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + /* Quick call if the quick jit entry is registered */ + if (func_type->quick_aot_entry) { + void (*invoke_native)(void *func_ptr, void *exec_env, uint32 *argv, + uint32 *argv_ret) = + func_type->quick_aot_entry; + invoke_native(module_inst->func_ptrs[func_idx], exec_env, argv, + argv); + ret = !wasm_copy_exception(module_inst, NULL); + } + else +#endif + { + ret = wasm_runtime_invoke_native( + exec_env, module_inst->func_ptrs[func_idx], func_type, NULL, + NULL, argv, argc, argv); + + if (ret) + ret = !wasm_copy_exception(module_inst, NULL); + } + } + +fail: + +#if (WASM_ENABLE_DUMP_CALL_STACK != 0) || (WASM_ENABLE_PERF_PROFILING != 0) \ + || (WASM_ENABLE_AOT_STACK_FRAME != 0) + llvm_jit_free_frame_internal(exec_env); +#endif + + return ret; +} +#endif /* end of WASM_ENABLE_JIT != 0 */ + +void +wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, + WASMFunctionInstance *function, uint32 argc, + uint32 argv[]) +{ + WASMRuntimeFrame *frame = NULL, *prev_frame, *outs_area; + RunningMode running_mode = + wasm_runtime_get_running_mode((WASMModuleInstanceCommon *)module_inst); + /* Allocate sufficient cells for all kinds of return values. */ + bool alloc_frame = true; + + if (argc < function->param_cell_num) { + char buf[128]; + snprintf(buf, sizeof(buf), + "invalid argument count %" PRIu32 + ", must be no smaller than %u", + argc, function->param_cell_num); + wasm_set_exception(module_inst, buf); + return; + } + argc = function->param_cell_num; + +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + /* + * wasm_runtime_detect_native_stack_overflow is done by + * call_wasm_with_hw_bound_check. + */ +#else + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } +#endif + + if (!function->is_import_func) { + /* No need to alloc frame when calling LLVM JIT function */ +#if WASM_ENABLE_JIT != 0 + if (running_mode == Mode_LLVM_JIT) { + alloc_frame = false; + } +#if WASM_ENABLE_LAZY_JIT != 0 && WASM_ENABLE_FAST_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]) { + alloc_frame = false; + } + } +#endif +#endif + } + + if (alloc_frame) { + unsigned all_cell_num = + function->ret_cell_num > 2 ? function->ret_cell_num : 2; + unsigned frame_size; + + prev_frame = wasm_exec_env_get_cur_frame(exec_env); + /* This frame won't be used by JITed code, so only allocate interp + frame here. */ + frame_size = wasm_interp_interp_frame_size(all_cell_num); + + if (!(frame = ALLOC_FRAME(exec_env, frame_size, prev_frame))) + return; + + outs_area = wasm_exec_env_wasm_stack_top(exec_env); + frame->function = NULL; + frame->ip = NULL; + /* There is no local variable. */ + frame->sp = frame->lp + 0; + + if ((uint8 *)(outs_area->lp + function->param_cell_num) + > exec_env->wasm_stack.top_boundary) { + wasm_set_exception(module_inst, "wasm operand stack overflow"); + return; + } + + if (argc > 0) + word_copy(outs_area->lp, argv, argc); + + wasm_exec_env_set_cur_frame(exec_env, frame); + } + +#if defined(os_writegsbase) + { + WASMMemoryInstance *memory_inst = wasm_get_default_memory(module_inst); + if (memory_inst) + /* write base addr of linear memory to GS segment register */ + os_writegsbase(memory_inst->memory_data); + } +#endif + + if (function->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (function->import_module_inst) { + wasm_interp_call_func_import(module_inst, exec_env, function, + frame); + } + else +#endif + { + /* it is a native function */ + wasm_interp_call_func_native(module_inst, exec_env, function, + frame); + } + } + else { + 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); + } +#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); + } + else { + fast_jit_call_func_bytecode(module_inst, exec_env, function, + frame); + } + } +#endif + 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 + (void)fast_jit_call_func_bytecode; +#endif + } + + /* Output the return value to the caller */ + if (!wasm_copy_exception(module_inst, NULL)) { + if (alloc_frame) { + uint32 i; + for (i = 0; i < function->ret_cell_num; i++) { + argv[i] = *(frame->sp + i - function->ret_cell_num); + } + } + } + else { +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (wasm_interp_create_call_stack(exec_env)) { + wasm_interp_dump_call_stack(exec_env, true, NULL, 0); + } +#endif + } + + if (alloc_frame) { + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + FREE_FRAME(exec_env, frame); + } +} diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c new file mode 100644 index 0000000000..42fb8fb6da --- /dev/null +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -0,0 +1,8583 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_interp.h" +#include "bh_log.h" +#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_GC != 0 +#include "../common/gc/gc_object.h" +#include "mem_alloc.h" +#if WASM_ENABLE_STRINGREF != 0 +#include "string_object.h" +#endif +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + +#if WASM_ENABLE_SIMDE != 0 +#include "simde/wasm/simd128.h" +#endif + +/* MSVC has no `__builtin_expect`; the cold-path hints below are + * GCC/Clang only. Provide a no-op fallback so the loop still + * compiles on the Windows MSVC build. Branch-predictor hints are + * an optimization, not correctness, so dropping them on MSVC is + * fine. */ +#if !defined(__GNUC__) && !defined(__clang__) +#define __builtin_expect(expr, expected) (expr) +#endif + +typedef int32 CellType_I32; +typedef int64 CellType_I64; +typedef float32 CellType_F32; +typedef float64 CellType_F64; + +#if WASM_ENABLE_THREAD_MGR == 0 +#define get_linear_mem_size() linear_mem_size +#else +/** + * Load memory data size in each time boundary check in + * multi-threading mode since it may be changed by other + * threads in memory.grow + */ +#define get_linear_mem_size() GET_LINEAR_MEMORY_SIZE(memory) +#endif + +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 +#define CHECK_MEMORY_OVERFLOW(bytes) \ + do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + if (disable_bounds_checks || offset1 + bytes <= get_linear_mem_size()) \ + /* If offset1 is in valid range, maddr must also \ + be in valid range, no need to check it again. */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) \ + do { \ + uint64 offset1 = (uint32)(start); \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + if (disable_bounds_checks || offset1 + bytes <= get_linear_mem_size()) \ + /* App heap space is not valid space for \ + bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) +#else +#define CHECK_MEMORY_OVERFLOW(bytes) \ + do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + maddr = memory->memory_data + offset1; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) \ + do { \ + uint64 offset1 = (uint32)(start); \ + CHECK_SHARED_HEAP_OVERFLOW(offset1, bytes, maddr) \ + maddr = memory->memory_data + offset1; \ + } while (0) +#endif /* !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 */ + +#define CHECK_ATOMIC_MEMORY_ACCESS(align) \ + do { \ + if (((uintptr_t)maddr & (align - 1)) != 0) \ + goto unaligned_atomic; \ + } while (0) + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 +#define CHECK_INSTRUCTION_LIMIT() \ + if (instructions_left == 0) { \ + wasm_set_exception(module, "instruction limit exceeded"); \ + goto got_exception; \ + } \ + else if (instructions_left > 0) \ + instructions_left--; + +#else +#define CHECK_INSTRUCTION_LIMIT() (void)0 +#endif + +#if WASM_ENABLE_EXCE_HANDLING != 0 +/* Per-frame eh-stack entries are 2 cells wide. Cell 0 packs the index + * into `func->exception_handlers[]` (low 31 bits) and a state bit + * (top bit): clear when the try-region's handler is *in scope* (TRY + * state — a throw matching one of its catches will dispatch into the + * handler), set once the throw walker has selected one of its + * handlers (CATCH state — further throws raised from inside that + * handler skip the entry and propagate outward). Cell 1 holds the + * wasm tag index of the exception currently being handled (written + * by the throw walker on dispatch; read by RETHROW). The tag is + * undefined while the entry is in TRY state. */ +#define EH_TRY_CATCH_STATE_BIT 0x80000000u +#define EH_ENTRY_CELLS 2 + +/* Base of the per-frame eh-stack, in cells from frame_lp. + * + * Frame setup (see the call-into-wasm-function path) reserves the + * eh-stack region *after* the locals + value stack and, in GC builds, + * *after* the frame_ref root bitmap as well. The accumulation order in + * `all_cell_num` is: + * + * locals + value stack : cell_num_of_local_stack cells + * [GC only] frame_ref : (cell_num_of_local_stack + 3) / 4 cells + * eh-stack : exception_handler_count * EH_ENTRY_CELLS + * + * where cell_num_of_local_stack = param_cell_num + local_cell_num + * + max_stack_cell_num. The runtime pointer must skip the same regions. + * In non-GC builds the frame_ref bitmap doesn't exist, so the offset + * collapses to cell_num_of_local_stack — leaving non-GC behavior byte- + * for-byte identical. In GC builds, omitting the bitmap term made the + * eh-stack alias frame->frame_ref (both start at + * frame_lp + cell_num_of_local_stack), so WASM_OP_TRY corrupted GC + * roots; adding it lands the eh-stack in its reserved trailing cells. */ +#if WASM_ENABLE_GC != 0 +#define EH_FRAME_REF_CELLS(cur_func, cur_wasm_func) \ + ((((cur_func)->param_cell_num + (cur_func)->local_cell_num \ + + (cur_wasm_func)->max_stack_cell_num) \ + + 3) \ + / 4) +#else +#define EH_FRAME_REF_CELLS(cur_func, cur_wasm_func) 0 +#endif + +#define EH_STACK_BASE(frame_lp, cur_func, cur_wasm_func) \ + ((frame_lp) + (cur_func)->param_cell_num + (cur_func)->local_cell_num \ + + (cur_wasm_func)->max_stack_cell_num \ + + EH_FRAME_REF_CELLS(cur_func, cur_wasm_func)) +#endif + +static inline uint32 +rotl32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n << c) | (n >> ((0 - c) & mask)); +} + +static inline uint32 +rotr32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n >> c) | (n << ((0 - c) & mask)); +} + +static inline uint64 +rotl64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n << c) | (n >> ((0 - c) & mask)); +} + +static inline uint64 +rotr64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n >> c) | (n << ((0 - c) & mask)); +} + +static inline float32 +f32_min(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +static inline float32 +f32_max(float32 a, float32 b) +{ + if (isnan(a) || isnan(b)) + return NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +static inline float64 +f64_min(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? a : b; + else + return a > b ? b : a; +} + +static inline float64 +f64_max(float64 a, float64 b) +{ + if (isnan(a) || isnan(b)) + return (float64)NAN; + else if (a == 0 && a == b) + return signbit(a) ? b : a; + else + return a > b ? a : b; +} + +static inline uint32 +clz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 0x80000000)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +clz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +ctz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +ctz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +popcount32(uint32 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static inline uint32 +popcount64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static float +local_copysignf(float x, float y) +{ + union { + float f; + uint32 i; + } ux = { x }, uy = { y }; + ux.i &= 0x7fffffff; + ux.i |= uy.i & 0x80000000; + return ux.f; +} + +static double +local_copysign(double x, double y) +{ + union { + double f; + uint64 i; + } ux = { x }, uy = { y }; + ux.i &= UINT64_MAX / 2; + ux.i |= uy.i & 1ULL << 63; + return ux.f; +} + +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define LOAD_U32_WITH_2U16S(addr) (*(uint32 *)(addr)) +#define LOAD_PTR(addr) (*(void **)(addr)) +#else /* else of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +static inline uint32 +LOAD_U32_WITH_2U16S(void *addr) +{ + union { + uint32 val; + uint16 u16[2]; + } u; + + bh_assert(((uintptr_t)addr & 1) == 0); + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + return u.val; +} +#if UINTPTR_MAX == UINT32_MAX +#define LOAD_PTR(addr) ((void *)LOAD_U32_WITH_2U16S(addr)) +#elif UINTPTR_MAX == UINT64_MAX +static inline void * +LOAD_PTR(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { + void *val; + uint32 u32[2]; + uint16 u16[4]; + } u; + + bh_assert(((uintptr_t)addr & 1) == 0); + if ((addr1 & (uintptr_t)7) == 0) + return *(void **)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32 *)addr)[0]; + u.u32[1] = ((uint32 *)addr)[1]; + } + else { + u.u16[0] = ((uint16 *)addr)[0]; + u.u16[1] = ((uint16 *)addr)[1]; + u.u16[2] = ((uint16 *)addr)[2]; + u.u16[3] = ((uint16 *)addr)[3]; + } + return u.val; +} +#endif /* end of UINTPTR_MAX */ +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ + +#if WASM_ENABLE_GC != 0 +static void +init_frame_refs(uint8 *frame_ref, uint32 cell_num, WASMFunctionInstance *func) +{ + uint32 i, j; + + memset(frame_ref, 0, cell_num); + + for (i = 0, j = 0; i < func->param_count; i++) { + if (wasm_is_type_reftype(func->param_types[i]) + && !wasm_is_reftype_i31ref(func->param_types[i])) { + frame_ref[j++] = 1; +#if UINTPTR_MAX == UINT64_MAX + frame_ref[j++] = 1; +#endif + } + else { + j += wasm_value_type_cell_num(func->param_types[i]); + } + } + + for (i = 0; i < func->local_count; i++) { + if (wasm_is_type_reftype(func->local_types[i]) + && !wasm_is_reftype_i31ref(func->local_types[i])) { + frame_ref[j++] = 1; +#if UINTPTR_MAX == UINT64_MAX + frame_ref[j++] = 1; +#endif + } + else { + j += wasm_value_type_cell_num(func->local_types[i]); + } + } +} + +uint8 * +wasm_interp_get_frame_ref(WASMInterpFrame *frame) +{ + return frame->frame_ref; +} + +/* Return the corresponding ref slot of the given slot of local + variable or stack pointer. */ + +#define COMPUTE_FRAME_REF(ref, off) (ref + (unsigned)(off)) + +#define FRAME_REF(off) COMPUTE_FRAME_REF(frame_ref, off) + +#if UINTPTR_MAX == UINT64_MAX +#define SET_FRAME_REF(off) *FRAME_REF(off) = *FRAME_REF(off + 1) = 1 +#define CLEAR_FRAME_REF(off) \ + (unsigned)off >= local_cell_num \ + ? (*FRAME_REF(off) = *FRAME_REF(off + 1) = 0) \ + : (void)0 +#else +#define SET_FRAME_REF(off) *FRAME_REF(off) = 1 +#define CLEAR_FRAME_REF(off) \ + (unsigned)off >= local_cell_num ? (*FRAME_REF(off) = 0) : (void)0 +#endif + +#define FRAME_REF_FOR(frame, p) \ + COMPUTE_FRAME_REF(frame->frame_ref, p - frame->lp) + +#define CLEAR_FRAME_REF_FOR(p, n) \ + do { \ + int32 ref_i, ref_n = (int32)(n); \ + uint8 *ref = FRAME_REF(p - frame_lp); \ + for (ref_i = 0; ref_i < ref_n; ref_i++) \ + ref[ref_i] = 0; \ + } while (0) +#endif /* end of WASM_ENABLE_GC != 0 */ + +#define read_uint32(p) \ + (p += sizeof(uint32), LOAD_U32_WITH_2U16S(p - sizeof(uint32))) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() \ + do { \ + uint32 param_count = cur_func->param_count; \ + local_idx = read_uint32(frame_ip); \ + bh_assert(local_idx < param_count + cur_func->local_count); \ + local_offset = cur_func->local_offsets[local_idx]; \ + if (local_idx < param_count) \ + local_type = cur_func->param_types[local_idx]; \ + else \ + local_type = cur_func->local_types[local_idx - param_count]; \ + } while (0) + +#define GET_OFFSET() (frame_ip += 2, *(int16 *)(frame_ip - 2)) + +#define SET_OPERAND_I32(off, value) \ + do { \ + *(uint32 *)(frame_lp + *(int16 *)(frame_ip + off)) = value; \ + } while (0) +#define SET_OPERAND_F32(off, value) \ + do { \ + *(float32 *)(frame_lp + *(int16 *)(frame_ip + off)) = value; \ + } while (0) +#define SET_OPERAND_I64(off, value) \ + do { \ + uint32 *addr_tmp = frame_lp + *(int16 *)(frame_ip + off); \ + PUT_I64_TO_ADDR(addr_tmp, value); \ + } while (0) +#define SET_OPERAND_F64(off, value) \ + do { \ + uint32 *addr_tmp = frame_lp + *(int16 *)(frame_ip + off); \ + PUT_F64_TO_ADDR(addr_tmp, value); \ + } while (0) +#define SET_OPERAND_REF(off, value) \ + do { \ + uint32 *addr_tmp; \ + opnd_off = *(int16 *)(frame_ip + off); \ + addr_tmp = frame_lp + opnd_off; \ + PUT_REF_TO_ADDR(addr_tmp, value); \ + SET_FRAME_REF(opnd_off); \ + } while (0) + +#define SET_OPERAND(op_type, off, value) SET_OPERAND_##op_type(off, value) + +#define GET_OPERAND_I32(type, off) \ + *(type *)(frame_lp + *(int16 *)(frame_ip + off)) +#define GET_OPERAND_F32(type, off) \ + *(type *)(frame_lp + *(int16 *)(frame_ip + off)) +#define GET_OPERAND_I64(type, off) \ + (type) GET_I64_FROM_ADDR(frame_lp + *(int16 *)(frame_ip + off)) +#define GET_OPERAND_F64(type, off) \ + (type) GET_F64_FROM_ADDR(frame_lp + *(int16 *)(frame_ip + off)) +#define GET_OPERAND_V128(off) \ + GET_V128_FROM_ADDR(frame_lp + *(int16 *)(frame_ip + off)) +#define GET_OPERAND_REF(type, off) \ + (type) GET_REF_FROM_ADDR(frame_lp + *(int16 *)(frame_ip + off)) + +#define GET_OPERAND(type, op_type, off) GET_OPERAND_##op_type(type, off) + +#define PUSH_I32(value) \ + do { \ + *(int32 *)(frame_lp + GET_OFFSET()) = value; \ + } while (0) + +#define PUSH_F32(value) \ + do { \ + *(float32 *)(frame_lp + GET_OFFSET()) = value; \ + } while (0) + +#define PUSH_I64(value) \ + do { \ + uint32 *addr_tmp = frame_lp + GET_OFFSET(); \ + PUT_I64_TO_ADDR(addr_tmp, value); \ + } while (0) + +#define PUSH_F64(value) \ + do { \ + uint32 *addr_tmp = frame_lp + GET_OFFSET(); \ + PUT_F64_TO_ADDR(addr_tmp, value); \ + } while (0) + +#define PUSH_REF(value) \ + do { \ + uint32 *addr_tmp; \ + opnd_off = GET_OFFSET(); \ + addr_tmp = frame_lp + opnd_off; \ + PUT_REF_TO_ADDR(addr_tmp, value); \ + SET_FRAME_REF(opnd_off); \ + } while (0) + +#define PUSH_I31REF(value) \ + do { \ + uint32 *addr_tmp; \ + opnd_off = GET_OFFSET(); \ + addr_tmp = frame_lp + opnd_off; \ + PUT_REF_TO_ADDR(addr_tmp, value); \ + } while (0) + +#define POP_I32() (*(int32 *)(frame_lp + GET_OFFSET())) + +#define POP_F32() (*(float32 *)(frame_lp + GET_OFFSET())) + +#define POP_I64() (GET_I64_FROM_ADDR(frame_lp + GET_OFFSET())) + +#define POP_V128() (GET_V128_FROM_ADDR(frame_lp + GET_OFFSET())) + +#define POP_F64() (GET_F64_FROM_ADDR(frame_lp + GET_OFFSET())) + +#define POP_REF() \ + (opnd_off = GET_OFFSET(), CLEAR_FRAME_REF((unsigned)(opnd_off)), \ + GET_REF_FROM_ADDR(frame_lp + opnd_off)) + +#if WASM_ENABLE_GC != 0 +#define SYNC_FRAME_REF() frame->frame_ref = frame_ref +#define UPDATE_FRAME_REF() frame_ref = frame->frame_ref +#else +#define SYNC_FRAME_REF() (void)0 +#define UPDATE_FRAME_REF() (void)0 +#endif + +#define SYNC_ALL_TO_FRAME() \ + do { \ + frame->ip = frame_ip; \ + SYNC_FRAME_REF(); \ + } while (0) + +#define UPDATE_ALL_FROM_FRAME() \ + do { \ + frame_ip = frame->ip; \ + UPDATE_FRAME_REF(); \ + } while (0) + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#define UPDATE_FRAME_IP_END() (void)0 +#else +#define UPDATE_FRAME_IP_END() frame_ip_end = wasm_get_func_code_end(cur_func) +#endif + +#if WASM_ENABLE_GC != 0 +#define RECOVER_FRAME_REF() frame_ref = frame->frame_ref +#else +#define RECOVER_FRAME_REF() (void)0 +#endif + +#define RECOVER_CONTEXT(new_frame) \ + do { \ + frame = (new_frame); \ + cur_func = frame->function; \ + prev_frame = frame->prev_frame; \ + frame_ip = frame->ip; \ + UPDATE_FRAME_IP_END(); \ + frame_lp = frame->lp; \ + RECOVER_FRAME_REF(); \ + } while (0) + +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define GET_OPCODE() opcode = *frame_ip++; +#else +#define GET_OPCODE() \ + opcode = *frame_ip; \ + frame_ip += 2; +#endif + +#define DEF_OP_EQZ(ctype, src_op_type) \ + do { \ + SET_OPERAND(I32, 2, (GET_OPERAND(ctype, src_op_type, 0) == 0)); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_CMP(src_type, src_op_type, cond) \ + do { \ + SET_OPERAND(I32, 4, \ + GET_OPERAND(src_type, src_op_type, 2) \ + cond GET_OPERAND(src_type, src_op_type, 0)); \ + frame_ip += 6; \ + } while (0) + +#define DEF_OP_BIT_COUNT(src_type, src_op_type, operation) \ + do { \ + SET_OPERAND( \ + src_op_type, 2, \ + (src_type)operation(GET_OPERAND(src_type, src_op_type, 0))); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_NUMERIC(src_type1, src_type2, src_op_type, operation) \ + do { \ + SET_OPERAND(src_op_type, 4, \ + GET_OPERAND(src_type1, src_op_type, 2) \ + operation GET_OPERAND(src_type2, src_op_type, 0)); \ + frame_ip += 6; \ + } while (0) + +#define DEF_OP_REINTERPRET(src_type, src_op_type) \ + do { \ + SET_OPERAND(src_op_type, 2, GET_OPERAND(src_type, src_op_type, 0)); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_NUMERIC_64 DEF_OP_NUMERIC + +#define DEF_OP_NUMERIC2(src_type1, src_type2, src_op_type, operation) \ + do { \ + SET_OPERAND(src_op_type, 4, \ + GET_OPERAND(src_type1, src_op_type, 2) operation( \ + GET_OPERAND(src_type2, src_op_type, 0) % 32)); \ + frame_ip += 6; \ + } while (0) + +#define DEF_OP_NUMERIC2_64(src_type1, src_type2, src_op_type, operation) \ + do { \ + SET_OPERAND(src_op_type, 4, \ + GET_OPERAND(src_type1, src_op_type, 2) operation( \ + GET_OPERAND(src_type2, src_op_type, 0) % 64)); \ + frame_ip += 6; \ + } while (0) + +#define DEF_ATOMIC_RMW_OPCODE(OP_NAME, op) \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U: \ + { \ + uint32 readv, sval; \ + \ + sval = POP_I32(); \ + addr = POP_I32(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U) { \ + CHECK_MEMORY_OVERFLOW(1); \ + CHECK_ATOMIC_MEMORY_ACCESS(1); \ + \ + shared_memory_lock(memory); \ + readv = (uint32)(*(uint8 *)maddr); \ + *(uint8 *)maddr = (uint8)(readv op sval); \ + shared_memory_unlock(memory); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U) { \ + CHECK_MEMORY_OVERFLOW(2); \ + CHECK_ATOMIC_MEMORY_ACCESS(2); \ + \ + shared_memory_lock(memory); \ + readv = (uint32)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + shared_memory_unlock(memory); \ + } \ + else { \ + CHECK_MEMORY_OVERFLOW(4); \ + CHECK_ATOMIC_MEMORY_ACCESS(4); \ + \ + shared_memory_lock(memory); \ + readv = LOAD_I32(maddr); \ + STORE_U32(maddr, readv op sval); \ + shared_memory_unlock(memory); \ + } \ + PUSH_I32(readv); \ + break; \ + } \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U: \ + { \ + uint64 readv, sval; \ + \ + sval = (uint64)POP_I64(); \ + addr = POP_I32(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U) { \ + CHECK_MEMORY_OVERFLOW(1); \ + CHECK_ATOMIC_MEMORY_ACCESS(1); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)(*(uint8 *)maddr); \ + *(uint8 *)maddr = (uint8)(readv op sval); \ + shared_memory_unlock(memory); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U) { \ + CHECK_MEMORY_OVERFLOW(2); \ + CHECK_ATOMIC_MEMORY_ACCESS(2); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + shared_memory_unlock(memory); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U) { \ + CHECK_MEMORY_OVERFLOW(4); \ + CHECK_ATOMIC_MEMORY_ACCESS(4); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)LOAD_U32(maddr); \ + STORE_U32(maddr, (uint32)(readv op sval)); \ + shared_memory_unlock(memory); \ + } \ + else { \ + uint64 op_result; \ + CHECK_MEMORY_OVERFLOW(8); \ + CHECK_ATOMIC_MEMORY_ACCESS(8); \ + \ + shared_memory_lock(memory); \ + readv = (uint64)LOAD_I64(maddr); \ + op_result = readv op sval; \ + STORE_I64(maddr, op_result); \ + shared_memory_unlock(memory); \ + } \ + PUSH_I64(readv); \ + break; \ + } + +#define DEF_OP_MATH(src_type, src_op_type, method) \ + do { \ + SET_OPERAND(src_op_type, 2, \ + (src_type)method(GET_OPERAND(src_type, src_op_type, 0))); \ + frame_ip += 4; \ + } while (0) + +#define TRUNC_FUNCTION(func_name, src_type, dst_type, signed_type) \ + static dst_type func_name(src_type src_value, src_type src_min, \ + src_type src_max, dst_type dst_min, \ + dst_type dst_max, bool is_sign) \ + { \ + dst_type dst_value = 0; \ + if (!isnan(src_value)) { \ + if (src_value <= src_min) \ + dst_value = dst_min; \ + else if (src_value >= src_max) \ + dst_value = dst_max; \ + else { \ + if (is_sign) \ + dst_value = (dst_type)(signed_type)src_value; \ + else \ + dst_value = (dst_type)src_value; \ + } \ + } \ + return dst_value; \ + } + +TRUNC_FUNCTION(trunc_f32_to_i32, float32, uint32, int32) +TRUNC_FUNCTION(trunc_f32_to_i64, float32, uint64, int64) +TRUNC_FUNCTION(trunc_f64_to_i32, float64, uint32, int32) +TRUNC_FUNCTION(trunc_f64_to_i64, float64, uint64, int64) + +static bool +trunc_f32_to_int(WASMModuleInstance *module, uint8 *frame_ip, uint32 *frame_lp, + float32 src_min, float32 src_max, bool saturating, bool is_i32, + bool is_sign) +{ + float32 src_value = GET_OPERAND(float32, F32, 0); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return false; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return false; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f32_to_i32(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + SET_OPERAND(I32, 2, dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f32_to_i64(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + SET_OPERAND(I64, 2, dst_value_i64); + } + return true; +} + +static bool +trunc_f64_to_int(WASMModuleInstance *module, uint8 *frame_ip, uint32 *frame_lp, + float64 src_min, float64 src_max, bool saturating, bool is_i32, + bool is_sign) +{ + float64 src_value = GET_OPERAND(float64, F64, 0); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return false; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return false; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f64_to_i32(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + SET_OPERAND(I32, 2, dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f64_to_i64(src_value, src_min, src_max, dst_min, + dst_max, is_sign); + SET_OPERAND(I64, 2, dst_value_i64); + } + return true; +} + +#define DEF_OP_TRUNC_F32(min, max, is_i32, is_sign) \ + do { \ + if (!trunc_f32_to_int(module, frame_ip, frame_lp, min, max, false, \ + is_i32, is_sign)) \ + goto got_exception; \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_TRUNC_F64(min, max, is_i32, is_sign) \ + do { \ + if (!trunc_f64_to_int(module, frame_ip, frame_lp, min, max, false, \ + is_i32, is_sign)) \ + goto got_exception; \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F32(min, max, is_i32, is_sign) \ + do { \ + (void)trunc_f32_to_int(module, frame_ip, frame_lp, min, max, true, \ + is_i32, is_sign); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F64(min, max, is_i32, is_sign) \ + do { \ + (void)trunc_f64_to_int(module, frame_ip, frame_lp, min, max, true, \ + is_i32, is_sign); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_CONVERT(dst_type, dst_op_type, src_type, src_op_type) \ + do { \ + dst_type value = (dst_type)(src_type)POP_##src_op_type(); \ + PUSH_##dst_op_type(value); \ + } while (0) + +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define CELL_SIZE sizeof(uint8) +#else +#define CELL_SIZE (sizeof(uint8) * 2) +#endif + +static bool +copy_stack_values(WASMModuleInstance *module, uint32 *frame_lp, uint32 arity, +#if WASM_ENABLE_GC != 0 + uint8 *frame_ref, +#endif + uint32 total_cell_num, const uint8 *cells, + const int16 *src_offsets, const uint16 *dst_offsets) +{ + /* To avoid the overlap issue between src offsets and dst offset, + * we use 2 steps to do the copy. First step, copy the src values + * to a tmp buf. Second step, copy the values from tmp buf to dst. + */ + bool ret = false; + uint32 buf[16] = { 0 }, i; + uint32 *tmp_buf = buf; + uint8 cell; + int16 src, buf_index = 0; + uint16 dst; +#if WASM_ENABLE_GC != 0 + uint8 ref_buf[4]; + uint8 *tmp_ref_buf = ref_buf; +#endif + + /* Allocate memory if the buf is not large enough */ + if (total_cell_num > sizeof(buf) / sizeof(uint32)) { + uint64 total_size = sizeof(uint32) * (uint64)total_cell_num; + if (total_size >= UINT32_MAX + || !(tmp_buf = wasm_runtime_malloc((uint32)total_size))) { + wasm_set_exception(module, "allocate memory failed"); + goto fail; + } + } + +#if WASM_ENABLE_GC != 0 + if (total_cell_num > sizeof(ref_buf) / sizeof(uint8)) { + uint64 total_size = sizeof(uint8) * (uint64)total_cell_num; + if (total_size >= UINT32_MAX + || !(tmp_ref_buf = wasm_runtime_malloc((uint32)total_size))) { + wasm_set_exception(module, "allocate memory failed"); + goto fail; + } + } +#endif + + /* 1) Copy values from src to tmp buf */ + for (i = 0; i < arity; i++) { + cell = cells[i * CELL_SIZE]; + src = src_offsets[i]; + if (cell == 1) { + tmp_buf[buf_index] = frame_lp[src]; +#if WASM_ENABLE_GC != 0 + tmp_ref_buf[buf_index] = frame_ref[src]; + frame_ref[src] = 0; +#endif + } + else { + tmp_buf[buf_index] = frame_lp[src]; + tmp_buf[buf_index + 1] = frame_lp[src + 1]; +#if WASM_ENABLE_GC != 0 + tmp_ref_buf[buf_index] = frame_ref[src]; + tmp_ref_buf[buf_index + 1] = frame_ref[src + 1]; + frame_ref[src] = 0; + frame_ref[src + 1] = 0; +#endif + } + buf_index += cell; + } + + /* 2) Copy values from tmp buf to dest */ + buf_index = 0; + for (i = 0; i < arity; i++) { + cell = cells[i * CELL_SIZE]; + dst = dst_offsets[i]; + if (cell == 1) { + frame_lp[dst] = tmp_buf[buf_index]; +#if WASM_ENABLE_GC != 0 + frame_ref[dst] = tmp_ref_buf[buf_index]; +#endif + } + else { + frame_lp[dst] = tmp_buf[buf_index]; + frame_lp[dst + 1] = tmp_buf[buf_index + 1]; +#if WASM_ENABLE_GC != 0 + frame_ref[dst] = tmp_ref_buf[buf_index]; + frame_ref[dst + 1] = tmp_ref_buf[buf_index + 1]; +#endif + } + buf_index += cell; + } + + ret = true; + +fail: + if (tmp_buf != buf) { + wasm_runtime_free(tmp_buf); + } + +#if WASM_ENABLE_GC != 0 + if (tmp_ref_buf != ref_buf) { + wasm_runtime_free(tmp_ref_buf); + } +#endif + + return ret; +} + +#if WASM_ENABLE_GC != 0 +#define RECOVER_BR_INFO() \ + do { \ + uint32 arity; \ + /* read arity */ \ + arity = read_uint32(frame_ip); \ + if (arity) { \ + uint32 total_cell; \ + uint16 *dst_offsets = NULL; \ + uint8 *cells; \ + int16 *src_offsets = NULL; \ + /* read total cell num */ \ + total_cell = read_uint32(frame_ip); \ + /* cells */ \ + cells = (uint8 *)frame_ip; \ + frame_ip += arity * CELL_SIZE; \ + /* src offsets */ \ + src_offsets = (int16 *)frame_ip; \ + frame_ip += arity * sizeof(int16); \ + /* dst offsets */ \ + dst_offsets = (uint16 *)frame_ip; \ + frame_ip += arity * sizeof(uint16); \ + if (arity == 1) { \ + if (cells[0] == 1) { \ + frame_lp[dst_offsets[0]] = frame_lp[src_offsets[0]]; \ + /* Ignore constants because they are not reference */ \ + if (src_offsets[0] >= 0) { \ + CLEAR_FRAME_REF((unsigned)(src_offsets[0])); \ + SET_FRAME_REF(dst_offsets[0]); \ + } \ + } \ + else if (cells[0] == 2) { \ + int64 tmp_i64 = \ + GET_I64_FROM_ADDR(frame_lp + src_offsets[0]); \ + PUT_I64_TO_ADDR(frame_lp + dst_offsets[0], tmp_i64); \ + /* Ignore constants because they are not reference */ \ + if (src_offsets[0] >= 0) { \ + CLEAR_FRAME_REF((unsigned)src_offsets[0]); \ + CLEAR_FRAME_REF((unsigned)(src_offsets[0] + 1)); \ + SET_FRAME_REF((unsigned)dst_offsets[0]); \ + SET_FRAME_REF((unsigned)(dst_offsets[0] + 1)); \ + } \ + } \ + else if (cells[0] == 4) { \ + V128 tmp_v128 = \ + GET_V128_FROM_ADDR(frame_lp + src_offsets[0]); \ + PUT_V128_TO_ADDR(frame_lp + dst_offsets[0], tmp_v128); \ + /* Ignore constants because they are not reference */ \ + if (src_offsets[0] >= 0) { \ + CLEAR_FRAME_REF((unsigned)src_offsets[0]); \ + CLEAR_FRAME_REF((unsigned)(src_offsets[0] + 1)); \ + CLEAR_FRAME_REF((unsigned)(src_offsets[0] + 2)); \ + CLEAR_FRAME_REF((unsigned)(src_offsets[0] + 3)); \ + SET_FRAME_REF((unsigned)dst_offsets[0]); \ + SET_FRAME_REF((unsigned)(dst_offsets[0] + 1)); \ + SET_FRAME_REF((unsigned)(dst_offsets[0] + 2)); \ + SET_FRAME_REF((unsigned)(dst_offsets[0] + 3)); \ + } \ + } \ + } \ + else { \ + if (!copy_stack_values(module, frame_lp, arity, frame_ref, \ + total_cell, cells, src_offsets, \ + dst_offsets)) \ + goto got_exception; \ + } \ + } \ + frame_ip = (uint8 *)LOAD_PTR(frame_ip); \ + } while (0) +#else +#define RECOVER_BR_INFO() \ + do { \ + uint32 arity; \ + /* read arity */ \ + arity = read_uint32(frame_ip); \ + if (arity) { \ + uint32 total_cell; \ + uint16 *dst_offsets = NULL; \ + uint8 *cells; \ + int16 *src_offsets = NULL; \ + /* read total cell num */ \ + total_cell = read_uint32(frame_ip); \ + /* cells */ \ + cells = (uint8 *)frame_ip; \ + frame_ip += arity * CELL_SIZE; \ + /* src offsets */ \ + src_offsets = (int16 *)frame_ip; \ + frame_ip += arity * sizeof(int16); \ + /* dst offsets */ \ + dst_offsets = (uint16 *)frame_ip; \ + frame_ip += arity * sizeof(uint16); \ + if (arity == 1) { \ + if (cells[0] == 1) \ + frame_lp[dst_offsets[0]] = frame_lp[src_offsets[0]]; \ + else if (cells[0] == 2) { \ + int64 tmp_i64 = \ + GET_I64_FROM_ADDR(frame_lp + src_offsets[0]); \ + PUT_I64_TO_ADDR(frame_lp + dst_offsets[0], tmp_i64); \ + } \ + else if (cells[0] == 4) { \ + V128 tmp_v128 = \ + GET_V128_FROM_ADDR(frame_lp + src_offsets[0]); \ + PUT_V128_TO_ADDR(frame_lp + dst_offsets[0], tmp_v128); \ + } \ + } \ + else { \ + if (!copy_stack_values(module, frame_lp, arity, total_cell, \ + cells, src_offsets, dst_offsets)) \ + goto got_exception; \ + } \ + } \ + frame_ip = (uint8 *)LOAD_PTR(frame_ip); \ + } while (0) +#endif + +#define SKIP_BR_INFO() \ + do { \ + uint32 arity; \ + /* read and skip arity */ \ + arity = read_uint32(frame_ip); \ + if (arity) { \ + /* skip total cell num */ \ + frame_ip += sizeof(uint32); \ + /* skip cells, src offsets and dst offsets */ \ + frame_ip += (CELL_SIZE + sizeof(int16) + sizeof(uint16)) * arity; \ + } \ + /* skip target address */ \ + frame_ip += sizeof(uint8 *); \ + } while (0) + +static inline int32 +sign_ext_8_32(int8 val) +{ + if (val & 0x80) + return (int32)val | (int32)0xffffff00; + return val; +} + +static inline int32 +sign_ext_16_32(int16 val) +{ + if (val & 0x8000) + return (int32)val | (int32)0xffff0000; + return val; +} + +static inline int64 +sign_ext_8_64(int8 val) +{ + if (val & 0x80) + return (int64)val | (int64)0xffffffffffffff00LL; + return val; +} + +static inline int64 +sign_ext_16_64(int16 val) +{ + if (val & 0x8000) + return (int64)val | (int64)0xffffffffffff0000LL; + return val; +} + +static inline int64 +sign_ext_32_64(int32 val) +{ + if (val & (int32)0x80000000) + return (int64)val | (int64)0xffffffff00000000LL; + return val; +} + +static inline void +word_copy(uint32 *dest, uint32 *src, unsigned num) +{ + bh_assert(dest != NULL); + bh_assert(src != NULL); + bh_assert(num > 0); + if (dest != src) { + /* No overlap buffer */ + bh_assert(!((src < dest) && (dest < src + num))); + for (; num > 0; num--) + *dest++ = *src++; + } +} + +static inline WASMInterpFrame * +ALLOC_FRAME(WASMExecEnv *exec_env, uint32 size, WASMInterpFrame *prev_frame) +{ + WASMInterpFrame *frame = wasm_exec_env_alloc_wasm_frame(exec_env, size); + + if (frame) { + frame->prev_frame = prev_frame; +#if WASM_ENABLE_PERF_PROFILING != 0 + frame->time_started = os_time_thread_cputime_us(); +#endif + } + else { + wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, + "wasm operand stack overflow"); + } + + return frame; +} + +static inline void +FREE_FRAME(WASMExecEnv *exec_env, WASMInterpFrame *frame) +{ +#if WASM_ENABLE_PERF_PROFILING != 0 + if (frame->function) { + WASMInterpFrame *prev_frame = frame->prev_frame; + uint64 time_elapsed = os_time_thread_cputime_us() - frame->time_started; + + frame->function->total_exec_time += time_elapsed; + frame->function->total_exec_cnt++; + + /* parent function */ + if (prev_frame && prev_frame->function) + prev_frame->function->children_exec_time += time_elapsed; + } +#endif + wasm_exec_env_free_wasm_frame(exec_env, frame); +} + +static void +wasm_interp_call_func_native(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMFunctionImport *func_import = cur_func->u.func_import; + CApiFuncImport *c_api_func_import = NULL; + unsigned local_cell_num = + cur_func->param_cell_num > 2 ? cur_func->param_cell_num : 2; + unsigned all_cell_num; + WASMInterpFrame *frame; + uint32 argv_ret[2], cur_func_index; + void *native_func_pointer = NULL; + bool ret; +#if WASM_ENABLE_GC != 0 + WASMFuncType *func_type; + uint8 *frame_ref; +#endif + + all_cell_num = local_cell_num; +#if WASM_ENABLE_GC != 0 + all_cell_num += (local_cell_num + 3) / 4; +#endif + + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + + if (!(frame = + ALLOC_FRAME(exec_env, wasm_interp_interp_frame_size(all_cell_num), + prev_frame))) + return; + + frame->function = cur_func; + frame->ip = NULL; + frame->lp = frame->operand; +#if WASM_ENABLE_GC != 0 + frame->frame_ref = (uint8 *)(frame->lp + local_cell_num); + init_frame_refs(frame->frame_ref, local_cell_num, cur_func); +#endif + + wasm_exec_env_set_cur_frame(exec_env, frame); + + cur_func_index = (uint32)(cur_func - module_inst->e->functions); + bh_assert(cur_func_index < module_inst->module->import_function_count); + if (!func_import->call_conv_wasm_c_api) { + native_func_pointer = module_inst->import_func_ptrs[cur_func_index]; + } + else if (module_inst->c_api_func_imports) { + c_api_func_import = module_inst->c_api_func_imports + cur_func_index; + native_func_pointer = c_api_func_import->func_ptr_linked; + } + + if (!native_func_pointer) { + char buf[128]; + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception((WASMModuleInstance *)module_inst, buf); + return; + } + + if (func_import->call_conv_wasm_c_api) { + ret = wasm_runtime_invoke_c_api_native( + (WASMModuleInstanceCommon *)module_inst, native_func_pointer, + func_import->func_type, cur_func->param_cell_num, frame->lp, + c_api_func_import->with_env_arg, c_api_func_import->env_arg); + if (ret) { + argv_ret[0] = frame->lp[0]; + argv_ret[1] = frame->lp[1]; + } + } + else if (!func_import->call_conv_raw) { + ret = wasm_runtime_invoke_native( + exec_env, native_func_pointer, func_import->func_type, + func_import->signature, func_import->attachment, frame->lp, + cur_func->param_cell_num, argv_ret); + } + else { + ret = wasm_runtime_invoke_native_raw( + exec_env, native_func_pointer, func_import->func_type, + func_import->signature, func_import->attachment, frame->lp, + cur_func->param_cell_num, argv_ret); + } + + if (!ret) + return; + +#if WASM_ENABLE_GC != 0 + func_type = cur_func->u.func_import->func_type; + if (func_type->result_count + && wasm_is_type_reftype(func_type->types[cur_func->param_count]) + && !wasm_is_reftype_i31ref(func_type->types[cur_func->param_count])) { + frame_ref = prev_frame->frame_ref + prev_frame->ret_offset; +#if UINTPTR_MAX == UINT64_MAX + *frame_ref = *(frame_ref + 1) = 1; +#else + *frame_ref = 1; +#endif + } +#endif + + if (cur_func->ret_cell_num == 1) { + prev_frame->lp[prev_frame->ret_offset] = argv_ret[0]; + } + else if (cur_func->ret_cell_num == 2) { + prev_frame->lp[prev_frame->ret_offset] = argv_ret[0]; + prev_frame->lp[prev_frame->ret_offset + 1] = argv_ret[1]; + } + + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame); + +static void +wasm_interp_call_func_import(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *sub_module_inst = cur_func->import_module_inst; + WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst; + WASMFunctionImport *func_import = cur_func->u.func_import; + uint8 *ip = prev_frame->ip; + char buf[128]; + WASMExecEnv *sub_module_exec_env = NULL; + uintptr_t aux_stack_origin_boundary = 0; + uintptr_t aux_stack_origin_bottom = 0; + + /* + * perform stack overflow check before calling + * wasm_interp_call_func_bytecode recursively. + */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } + + if (!sub_func_inst) { + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + /* Switch exec_env but keep using the same one by replacing necessary + * variables */ + sub_module_exec_env = wasm_runtime_get_exec_env_singleton( + (WASMModuleInstanceCommon *)sub_module_inst); + if (!sub_module_exec_env) { + wasm_set_exception(module_inst, "create singleton exec_env failed"); + return; + } + + /* - module_inst */ + wasm_exec_env_set_module_inst(exec_env, + (WASMModuleInstanceCommon *)sub_module_inst); + /* - aux_stack_boundary */ + aux_stack_origin_boundary = exec_env->aux_stack_boundary; + exec_env->aux_stack_boundary = sub_module_exec_env->aux_stack_boundary; + /* - aux_stack_bottom */ + aux_stack_origin_bottom = exec_env->aux_stack_bottom; + exec_env->aux_stack_bottom = sub_module_exec_env->aux_stack_bottom; + + /* set ip NULL to make call_func_bytecode return after executing + this function */ + prev_frame->ip = NULL; + + /* call function of sub-module*/ + wasm_interp_call_func_bytecode(sub_module_inst, exec_env, sub_func_inst, + prev_frame); + + /* restore ip and other replaced */ + prev_frame->ip = ip; + exec_env->aux_stack_boundary = aux_stack_origin_boundary; + exec_env->aux_stack_bottom = aux_stack_origin_bottom; + wasm_exec_env_restore_module_inst(exec_env, + (WASMModuleInstanceCommon *)module_inst); +} +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +#define CHECK_SUSPEND_FLAGS() \ + do { \ + WASM_SUSPEND_FLAGS_LOCK(exec_env->wait_lock); \ + if (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) \ + & WASM_SUSPEND_FLAG_TERMINATE) { \ + /* terminate current thread */ \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ + return; \ + } \ + /* TODO: support suspend and breakpoint */ \ + WASM_SUSPEND_FLAGS_UNLOCK(exec_env->wait_lock); \ + } while (0) +#endif + +#if WASM_ENABLE_OPCODE_COUNTER != 0 +typedef struct OpcodeInfo { + char *name; + uint64 count; +} OpcodeInfo; + +/* clang-format off */ +#define HANDLE_OPCODE(op) \ + { \ + #op, 0 \ + } +DEFINE_GOTO_TABLE(OpcodeInfo, opcode_table); +#undef HANDLE_OPCODE +/* clang-format on */ + +static void +wasm_interp_dump_op_count() +{ + uint32 i; + uint64 total_count = 0; + for (i = 0; i < WASM_OP_IMPDEP; i++) + total_count += opcode_table[i].count; + + os_printf("total opcode count: %ld\n", total_count); + for (i = 0; i < WASM_OP_IMPDEP; i++) + if (opcode_table[i].count > 0) + os_printf("\t\t%s count:\t\t%ld,\t\t%.2f%%\n", opcode_table[i].name, + opcode_table[i].count, + opcode_table[i].count * 100.0f / total_count); +} +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + +/* #define HANDLE_OP(opcode) HANDLE_##opcode:printf(#opcode"\n"); */ +#if WASM_ENABLE_OPCODE_COUNTER != 0 +#define HANDLE_OP(opcode) HANDLE_##opcode : opcode_table[opcode].count++; +#else +#define HANDLE_OP(opcode) HANDLE_##opcode: +#endif +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define FETCH_OPCODE_AND_DISPATCH() \ + do { \ + const void *p_label_addr = *(void **)frame_ip; \ + frame_ip += sizeof(void *); \ + CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ + } while (0) +#else +#if UINTPTR_MAX == UINT64_MAX +#define FETCH_OPCODE_AND_DISPATCH() \ + do { \ + const void *p_label_addr; \ + bh_assert(((uintptr_t)frame_ip & 1) == 0); \ + /* int32 relative offset was emitted in 64-bit target */ \ + p_label_addr = label_base + (int32)LOAD_U32_WITH_2U16S(frame_ip); \ + frame_ip += sizeof(int32); \ + CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ + } while (0) +#else +#define FETCH_OPCODE_AND_DISPATCH() \ + do { \ + const void *p_label_addr; \ + bh_assert(((uintptr_t)frame_ip & 1) == 0); \ + /* uint32 label address was emitted in 32-bit target */ \ + p_label_addr = (void *)(uintptr_t)LOAD_U32_WITH_2U16S(frame_ip); \ + frame_ip += sizeof(int32); \ + CHECK_INSTRUCTION_LIMIT(); \ + goto *p_label_addr; \ + } while (0) +#endif +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() + +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ + +#define HANDLE_OP(opcode) case opcode: +#define HANDLE_OP_END() continue + +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +static void **global_handle_table; +#endif + +static inline uint8 * +get_global_addr(uint8 *global_data, WASMGlobalInstance *global) +{ +#if WASM_ENABLE_MULTI_MODULE == 0 + return global_data + global->data_offset; +#else + return global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif +} + +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + 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_OPT != 0 + uint64 linear_mem_size = 0; + if (memory) +#if WASM_ENABLE_THREAD_MGR == 0 + linear_mem_size = memory->memory_data_size; +#else + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); +#endif +#endif + WASMGlobalInstance *globals = module->e ? module->e->globals : NULL; + WASMGlobalInstance *global; + uint8 *global_data = module->global_data; + uint8 opcode_IMPDEP = WASM_OP_IMPDEP; + WASMInterpFrame *frame = NULL; + /* Points to this special opcode so as to jump to the + * call_method_from_entry. */ + register uint8 *frame_ip = &opcode_IMPDEP; /* cache of frame->ip */ + register uint32 *frame_lp = NULL; /* cache of frame->lp */ +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 && UINTPTR_MAX == UINT64_MAX + /* cache of label base addr */ + register uint8 *label_base = &&HANDLE_WASM_OP_UNREACHABLE; +#endif +#endif +#if WASM_ENABLE_GC != 0 + register uint8 *frame_ref = NULL; /* cache of frame->ref */ + uint32 local_cell_num = 0; + int16 opnd_off; +#endif + uint8 *frame_ip_end = frame_ip + 1; + uint32 cond, count, fidx, tidx, frame_size = 0; + uint32 all_cell_num = 0; + int16 addr1, addr2, addr_ret = 0; + int32 didx, val; + uint8 *maddr = NULL; + uint32 local_idx, local_offset, global_idx; + uint8 opcode = 0, local_type, *global_addr; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* Carries the wasm tag index from WASM_OP_THROW to the + * find_a_catch_handler label, and from a callee's return through + * frame->tag_index back to a caller-side find_a_catch_handler. + * Cold path only — the dispatch loop's hot ops never reference + * this variable, so the compiler is free to spill it. */ + uint32 exception_tag_index = 0; + /* Tag-with-params payload routing for same-function dispatch. + * Read off the IR after THROW's tag_index immediate; + * `throw_src_offsets` points at the first src-slot int16 in the + * rewritten IR, and `throw_param_cell_num` is the total cell + * count across all of the tag's params. find_a_catch_handler + * uses these to copy frame_lp[src[i]] into the matched catch's + * pre-allocated dst slots. Both are cold-path-only — like + * exception_tag_index, the dispatch loop's hot ops never + * reference them. RETHROW re-points throw_src_offsets at the + * still-alive catch's `param_dst_offsets` (the original + * payload values, unchanged by the catch body since they live + * in a different slot range from locals) so the re-raised + * exception carries the same payload across outer try-regions + * in this frame. */ + uint32 throw_param_cell_num = 0; + int16 *throw_src_offsets = NULL; +#endif + +#if WASM_ENABLE_INSTRUCTION_METERING != 0 + int instructions_left = -1; + if (exec_env) { + instructions_left = exec_env->instructions_to_execute; + } +#endif +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + bool disable_bounds_checks = !wasm_runtime_is_bounds_checks_enabled( + (WASMModuleInstanceCommon *)module); +#else + bool disable_bounds_checks = false; +#endif +#endif +#if WASM_ENABLE_GC != 0 + WASMObjectRef gc_obj; + WASMStructObjectRef struct_obj; + WASMArrayObjectRef array_obj; + WASMFuncObjectRef func_obj; + WASMI31ObjectRef i31_obj; + WASMExternrefObjectRef externref_obj; + uint32 type_idx; +#if WASM_ENABLE_STRINGREF != 0 + WASMString str_obj; + WASMStringrefObjectRef stringref_obj; + WASMStringviewWTF8ObjectRef stringview_wtf8_obj; + WASMStringviewWTF16ObjectRef stringview_wtf16_obj; + WASMStringviewIterObjectRef stringview_iter_obj; +#endif +#endif +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + bool is_return_call = false; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + /* TODO: currently flowing two variables are only dummy for shared heap + * boundary check, need to be updated when multi-memory or memory64 + * proposals are to be implemented */ + bool is_memory64 = false; + uint32 memidx = 0; + (void)is_memory64; + (void)memidx; +/* #endif */ +#endif /* end of WASM_ENABLE_SHARED_HEAP != 0 */ + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#define HANDLE_OPCODE(op) &&HANDLE_##op + DEFINE_GOTO_TABLE(const void *, handle_table); +#undef HANDLE_OPCODE + if (exec_env == NULL) { + global_handle_table = (void **)handle_table; + return; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + while (frame_ip < frame_ip_end) { + opcode = *frame_ip++; +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + frame_ip++; +#endif + switch (opcode) { +#else + goto *handle_table[WASM_OP_IMPDEP]; +#endif + /* control instructions */ + HANDLE_OP(WASM_OP_UNREACHABLE) + { + wasm_set_exception(module, "unreachable"); + goto got_exception; + } + + HANDLE_OP(WASM_OP_IF) + { + cond = (uint32)POP_I32(); + + if (cond == 0) { + uint8 *else_addr = (uint8 *)LOAD_PTR(frame_ip); + if (else_addr == NULL) { + frame_ip = + (uint8 *)LOAD_PTR(frame_ip + sizeof(uint8 *)); + } + else { + frame_ip = else_addr; + } + } + else { + frame_ip += sizeof(uint8 *) * 2; + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_ELSE) + { + frame_ip = (uint8 *)LOAD_PTR(frame_ip); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + recover_br_info: + RECOVER_BR_INFO(); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR_IF) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + cond = frame_lp[GET_OFFSET()]; + + if (cond) + goto recover_br_info; + else + SKIP_BR_INFO(); + + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BR_TABLE) + { + uint32 arity, br_item_size; + +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + count = read_uint32(frame_ip); + didx = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + + if (!(didx >= 0 && (uint32)didx < count)) + didx = count; + + /* all br items must have the same arity and item size, + so we only calculate the first item size */ + arity = LOAD_U32_WITH_2U16S(frame_ip); + br_item_size = sizeof(uint32); /* arity */ + if (arity) { + /* total cell num */ + br_item_size += sizeof(uint32); + /* cells, src offsets and dst offsets */ + br_item_size += + (CELL_SIZE + sizeof(int16) + sizeof(uint16)) * arity; + } + /* target address */ + br_item_size += sizeof(uint8 *); + + frame_ip += br_item_size * didx; + goto recover_br_info; + } + + HANDLE_OP(WASM_OP_RETURN) + { + uint32 ret_idx; + WASMFuncType *func_type; + int32 off; + uint32 ret_offset; + uint8 *ret_types; + if (cur_func->is_import_func) + func_type = cur_func->u.func_import->func_type; + else + func_type = cur_func->u.func->func_type; + + /* types of each return value */ + ret_types = func_type->types + func_type->param_count; + ret_offset = prev_frame->ret_offset; + + for (ret_idx = 0, + off = (int32)sizeof(int16) * (func_type->result_count - 1); + ret_idx < func_type->result_count; + ret_idx++, off -= (int32)sizeof(int16)) { + if (ret_types[ret_idx] == VALUE_TYPE_I64 + || ret_types[ret_idx] == VALUE_TYPE_F64) { + PUT_I64_TO_ADDR(prev_frame->lp + ret_offset, + GET_OPERAND(uint64, I64, off)); + ret_offset += 2; + } + else if (ret_types[ret_idx] == VALUE_TYPE_V128) { + PUT_V128_TO_ADDR(prev_frame->lp + ret_offset, + GET_OPERAND_V128(off)); + ret_offset += 4; + } +#if WASM_ENABLE_GC != 0 + else if (wasm_is_type_reftype(ret_types[ret_idx])) { + PUT_REF_TO_ADDR(prev_frame->lp + ret_offset, + GET_OPERAND(void *, REF, off)); + if (!wasm_is_reftype_i31ref(ret_types[ret_idx])) { + *(prev_frame->frame_ref + ret_offset) = 1; +#if UINTPTR_MAX == UINT64_MAX + *(prev_frame->frame_ref + ret_offset + 1) = 1; +#endif + } + ret_offset += REF_CELL_NUM; + } +#endif + else { + prev_frame->lp[ret_offset] = + GET_OPERAND(uint32, I32, off); + ret_offset++; + } + } + goto return_func; + } + + HANDLE_OP(WASM_OP_CALL_INDIRECT) +#if WASM_ENABLE_TAIL_CALL != 0 + HANDLE_OP(WASM_OP_RETURN_CALL_INDIRECT) +#endif + { + WASMFuncType *cur_type, *cur_func_type; + WASMTableInstance *tbl_inst; + uint32 tbl_idx; + +#if WASM_ENABLE_TAIL_CALL != 0 + GET_OPCODE(); +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + + tidx = read_uint32(frame_ip); + cur_type = (WASMFuncType *)module->module->types[tidx]; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + val = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + + /* Bounds / null / type-mismatch checks below are + * structurally cold paths — well-formed wasm modules + * pass them on every dispatched CALL_INDIRECT. Marking + * them `__builtin_expect(cond, 0)` lets the compiler + * (a) hint the branch predictor with a static-bias + * fallback for unseen call sites, and (b) lay out the + * error-handling tail away from the hot path so each + * fall-through case stays in one straight-line I-cache + * line. Apple Silicon E-cores (Icestorm, iPhone 12) + * showed ~27 % `Discarded` (bad-spec / mispredict) + * on the AS variant of graphql-validation under + * fast-interp, where megamorphic vtable dispatch + * hits CALL_INDIRECT thousands of times; the layout + * hint matters more than the branch hint on Apple's + * sophisticated predictor. PMU bucket shares stay + * within run-to-run noise on both Porffor and AS + * graphql-validation workloads, so the change is + * documentation-as-code more than a speedup — + * keep it because the cold-path semantic is real + * and the cost is zero. */ + if (__builtin_expect((uint32)val >= tbl_inst->cur_size, 0)) { + wasm_set_exception(module, "undefined element"); + goto got_exception; + } + + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + fidx = (uint32)tbl_inst->elems[val]; + if (__builtin_expect(fidx == (uint32)-1, 0)) { + wasm_set_exception(module, "uninitialized element"); + goto got_exception; + } +#else + func_obj = (WASMFuncObjectRef)tbl_inst->elems[val]; + if (__builtin_expect(!func_obj, 0)) { + wasm_set_exception(module, "uninitialized element"); + goto got_exception; + } + fidx = wasm_func_obj_get_func_idx_bound(func_obj); +#endif + /* clang-format on */ + + /* + * we might be using a table injected by host or + * another module. in that case, we don't validate + * the elem value while loading + */ + if (__builtin_expect(fidx >= module->e->function_count, 0)) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } + + /* always call module own functions */ + cur_func = module->e->functions + fidx; + + if (cur_func->is_import_func) + cur_func_type = cur_func->u.func_import->func_type; + else + cur_func_type = cur_func->u.func->func_type; + + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + if (__builtin_expect(cur_type != cur_func_type, 0)) { + wasm_set_exception(module, "indirect call type mismatch"); + goto got_exception; + } +#else + if (__builtin_expect( + !wasm_func_type_is_super_of(cur_type, cur_func_type), + 0)) { + wasm_set_exception(module, "indirect call type mismatch"); + goto got_exception; + } +#endif + /* clang-format on */ + +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_RETURN_CALL_INDIRECT) + goto call_func_from_return_call; +#endif + goto call_func_from_interp; + } + +#if WASM_ENABLE_EXCE_HANDLING != 0 + HANDLE_OP(WASM_OP_THROW) + { + /* Loader emits + * `WASM_OP_THROW + * + * ... + * `. Read the tag plus payload-source + * metadata, then walk the eh-stack in find_a_catch_handler to + * find a matching catch — first in this frame, then via + * return_func's hook in caller frames, and finally + * falling out to the host via got_exception when no + * match is found anywhere. + * + * Payload routing: when a same-function catch matches, + * find_a_catch_handler copies frame_lp[src[i]] into + * the catch's pre-allocated dst slots (recorded on + * `WASMFastEHCatch.param_dst_offsets` at load time). + * For tag-without-params (the typical Porffor shape), + * `throw_param_cell_num == 0` makes the copy a no-op. + * For cross-function dispatch the source frame is + * torn down before the caller's walker runs, so the + * payload is dropped — this gap is documented in + * AGENTS.md and exercised as + * `cross_function_tag_with_params` (#[ignore]). */ + exception_tag_index = read_uint32(frame_ip); + throw_param_cell_num = read_uint32(frame_ip); + throw_src_offsets = (int16 *)frame_ip; + frame_ip += sizeof(int16) * throw_param_cell_num; + goto find_a_catch_handler; + } + + find_a_catch_handler: + { + /* The eh-stack lives in the trailing cells of + * frame->operand[] (see call_func_from_entry and the + * runtime push from WASM_OP_TRY). Each entry packs the + * eh-table index into the low 31 bits; the top bit + * (EH_TRY_CATCH_STATE_BIT) is set on entries whose + * catch handler is *already running* — those are + * skipped here so a throw raised from inside a catch + * body propagates outward rather than re-entering the + * same handler. + * + * Cost shape: the walk runs only on the throw path + * (cold). CALL / LOAD / STORE handlers are untouched, + * and the eh-stack cells share a cache line with the + * value stack they're allocated next to, so the walk + * hits warm memory. + * + * Known limitation in this patch: try-regions with a + * non-void result-type are *not yet supported* by the + * normal-flow path. The fix is a loader-side + * try-body→block-dynamic-offset COPY emit at CATCH + * processing time (mirrors how WASM_OP_ELSE aligns + * the if-body's result via reserve_block_ret). See the + * AGENTS.md "Open follow-up — WAMR fast-interp legacy + * exception handling" section for the architectural + * note. The throw → catch dispatch implemented here + * still works correctly for void-result try-regions + * (which is what graphql-validation-porf-accurate's + * single try-block is). */ + WASMFunction *cur_wasm_func = cur_func->u.func; + uint32 *eh_stack = EH_STACK_BASE(frame_lp, cur_func, cur_wasm_func); + uint32 i; + for (i = frame->eh_count; i > 0; i--) { + uint32 *cells = eh_stack + (i - 1) * EH_ENTRY_CELLS; + uint32 packed = cells[0]; + uint32 eh_idx; + WASMFastEHEntry *entry; + uint32 j; + if (packed & EH_TRY_CATCH_STATE_BIT) + continue; /* in-progress catch — skip */ + eh_idx = packed & ~EH_TRY_CATCH_STATE_BIT; + bh_assert(eh_idx < cur_wasm_func->exception_handler_count); + entry = &cur_wasm_func->exception_handlers[eh_idx]; + if (entry->delegate_target_depth != UINT32_MAX) { + /* This try-region was closed by `delegate N`, + * not `end`. The spec says the exception is + * re-raised at the location of the target + * block — i.e. it propagates past every try + * whose body the delegate's try sits inside + * (but the target is also inside). The loader + * already counted those tries as + * `delegate_target_depth = delta`. Marking + * THIS entry as consumed and decrementing `i` + * by `delta` makes the for-loop's natural + * i-- land on the first eh-stack entry + * strictly *outside* the target block — which + * is exactly where the spec wants the throw + * to resume matching. + * + * If `delta + 1 >= i`, the target block is + * outside this function's eh-stack entirely + * (e.g. `delegate `): + * break out to the "no handler in this + * frame" path and let return_func forward the + * exception to the caller. + * + * Cost: cold path; only THROW reaches here. + * Hot ops untouched. */ + uint32 delta = entry->delegate_target_depth; + cells[0] = packed | EH_TRY_CATCH_STATE_BIT; + if (delta + 1 >= i) { + /* Underflow guard + escape signal: any + * `delta` that would skip past the start + * of the eh-stack means the target lies + * past this function's try-blocks. */ + break; + } + i -= delta; + continue; + } + for (j = 0; j < entry->catch_count; j++) { + if (entry->catches[j].tag_index == exception_tag_index) { + /* Mark the entry as in-progress catch and + * stash the tag that's being handled so a + * RETHROW from this catch body can re- + * raise it. */ + cells[0] = packed | EH_TRY_CATCH_STATE_BIT; + cells[1] = exception_tag_index; + /* Payload copy (same-function dispatch). + * The loader guaranteed + * `entry->catches[j].param_cell_num == + * throw_param_cell_num` by checking the + * tag type at both THROW and CATCH; the + * runtime just executes the cell-wise + * frame_lp move. Tag-without-params makes + * the loop trivial. */ + if (throw_param_cell_num > 0 + && entry->catches[j].param_dst_offsets) { + uint32 c; + int16 *dst = entry->catches[j].param_dst_offsets; + for (c = 0; c < throw_param_cell_num; c++) { + frame_lp[dst[c]] = + frame_lp[throw_src_offsets[c]]; + } + } + /* Pop the inner eh-stack entries that the + * throw is jumping past. When the match is + * at the topmost entry this is a no-op + * (i == frame->eh_count). When the match is + * an outer entry, the nested-try entries + * above it (indices i .. eh_count-1) are + * out of scope after the catch-dispatch; + * leaving them counted would let a + * subsequent throw inside the catch body + * see stale in-scope entries (and a tight + * loop of throw → outer-catch → throw + * would eventually overflow the fixed + * reservation). The matched entry stays + * at index i-1 with its state bit set; the + * catch body's END pops it when it + * completes. Cost: one indexed store on + * the cold throw path; CALL / LOAD / STORE + * untouched. */ + frame->eh_count = i; + frame_ip = entry->catches[j].handler_pc; + HANDLE_OP_END(); + } + } + if (entry->catch_all_pc) { + /* catch_all binds no payload (spec: catch_all + * has no exception values), so we drop the + * src cells here. RETHROW from inside a + * catch_all body cannot re-emit a payload — + * documented as a known limitation. */ + cells[0] = packed | EH_TRY_CATCH_STATE_BIT; + cells[1] = exception_tag_index; + /* Same unwind as the typed-catch path above — + * pop any nested-try entries the throw is + * jumping past so a subsequent throw inside + * this catch_all body doesn't dispatch + * against stale inner entries. */ + frame->eh_count = i; + frame_ip = entry->catch_all_pc; + HANDLE_OP_END(); + } + } + /* No handler in this frame. Hand the exception off to + * the caller via return_func, which checks + * frame->exception_raised after RECOVER_CONTEXT and + * re-enters this label with the caller's frame in + * scope. If we're already at the top of the wasm + * stack, the existing got_exception path lets the + * host observe the trap via wasm_runtime_get_exception. + * + * Tag-with-params payload is intentionally NOT + * preserved across the frame boundary: the source + * cells (throw_src_offsets) live in *this* frame's + * frame_lp, which return_func is about to tear down. + * A caller-side typed catch would then bind + * uninitialized destination slots, producing wrong + * results in the catch body (or, if the typed catch + * uses the slots as a struct-of-pointers, memory + * corruption). The safe action when a payload- + * bearing throw escapes its callee is to trap to the + * host with a clear diagnostic. Same-function + * payload routing (the common Porffor / AS shape) + * is unaffected — it dispatches via the loop above + * before this branch runs. catch_all in the caller + * would technically tolerate a zero-payload bind, + * but the typed-vs-catch_all choice happens in the + * caller's walker, which we can't peek into here + * without coupling the frames; trap unconditionally + * for payload-bearing throws and let the test + * `cross_function_tag_with_params` document the + * shape. */ + if (prev_frame && prev_frame->ip) { + if (throw_param_cell_num > 0) { + wasm_set_exception(module, + "cross-function exception payload " + "not supported by fast-interp"); + goto got_exception; + } + prev_frame->tag_index = exception_tag_index; + prev_frame->exception_raised = true; + goto return_func; + } + { + char exception_buf[64]; + snprintf(exception_buf, sizeof(exception_buf), + "wasm exception thrown (tag %u)", exception_tag_index); + wasm_set_exception(module, exception_buf); + } + goto got_exception; + } + + HANDLE_OP(WASM_OP_TRY) + { + /* Loader emits `WASM_OP_TRY `. Push one + * entry onto the per-frame eh-stack so subsequent + * THROW / RETHROW handlers can find the in-scope + * catches by walking it. + * + * The eh-stack lives in the trailing cells of + * frame->operand[] — EH_ENTRY_CELLS cells per try- + * region, sized by + * cur_wasm_func->exception_handler_count * + * EH_ENTRY_CELLS at frame setup. Cell 1 (caught_tag) + * is unspecified while the entry is in TRY state and + * gets written by the throw walker on catch dispatch. + * Cost: one indexed store + one increment, both on a + * cold path; CALL / LOAD / STORE are untouched. */ + uint32 eh_idx = read_uint32(frame_ip); + WASMFunction *cur_wasm_func = cur_func->u.func; + uint32 *eh_stack = + EH_STACK_BASE(frame_lp, cur_func, cur_wasm_func); + bh_assert(frame->eh_count + < cur_wasm_func->exception_handler_count); + eh_stack[frame->eh_count * EH_ENTRY_CELLS + 0] = eh_idx; + frame->eh_count++; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_CATCH) + HANDLE_OP(WASM_OP_CATCH_ALL) + { + /* Loader emits ` ` (commit 1's + * exception_handlers table records each catch body's + * pc and the region's end_of_region_pc). + * + * Reached via *normal flow* — execution either ran the + * try body to completion (CATCH is the first opcode + * after the try body) or fell through from a previous + * catch body. Either way: pop one eh-stack entry and + * branch past the try-region's end. The THROW dispatch + * (follow-up commit) jumps directly to a catch body's + * first opcode, *skipping* the CATCH opcode itself, so + * this handler never runs as a result of a caught + * throw — only as a fall-through exit. */ + uint32 eh_idx = read_uint32(frame_ip); + WASMFunction *cur_wasm_func = cur_func->u.func; + bh_assert(eh_idx < cur_wasm_func->exception_handler_count); + bh_assert(frame->eh_count > 0); + frame->eh_count--; + frame_ip = + cur_wasm_func->exception_handlers[eh_idx].end_of_region_pc; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_RETHROW) + { + /* Loader emits `WASM_OP_RETHROW `. Re-raise + * the exception currently being handled by an + * enclosing catch (the (depth+1)-th `state=CATCH` + * entry from the top of the eh-stack at this point — + * each in-progress catch we're nested in contributes + * one such entry, in source order). RETHROW is a + * cold op (only fires inside catch bodies); the walk + * runs across at most the number of catches nested + * around the rethrow site. CALL / LOAD / STORE are + * untouched. */ + uint32 depth = read_uint32(frame_ip); + WASMFunction *cur_wasm_func = cur_func->u.func; + uint32 *eh_stack = + EH_STACK_BASE(frame_lp, cur_func, cur_wasm_func); + uint32 i; + uint32 catch_seen = 0; + for (i = frame->eh_count; i > 0; i--) { + uint32 *cells = eh_stack + (i - 1) * EH_ENTRY_CELLS; + if (!(cells[0] & EH_TRY_CATCH_STATE_BIT)) + continue; + if (catch_seen == depth) { + /* Re-raise the caught tag against the *outer* + * try-regions. find_a_catch_handler iterates + * top-down and skips state=CATCH entries, so + * this same entry won't re-match. + * + * Payload routing: the original throw's + * payload values were copied into THIS + * catch's dst slots by the previous + * find_a_catch_handler dispatch. The wasm + * spec says the catch body can't mutate + * those exception values directly (they're + * not addressable as locals, and the only + * way to read them is to pop off the + * operand stack at catch entry — which + * advances past the dst slots without + * writing them back). So at RETHROW time + * the dst slots still hold the original + * payload, and we can point throw_src_offsets + * at them so the outer catch's copy lands + * on a fresh set of dst slots with the + * same values. + * + * If the original match was via catch_all + * (no typed catch matched cells[1]), + * `match->param_dst_offsets == NULL` and the + * payload was already dropped at the + * catch_all dispatch. RETHROW from + * catch_all then re-raises with no payload + * — documented as a known limitation. */ + uint32 ent_eh_idx = cells[0] & ~EH_TRY_CATCH_STATE_BIT; + WASMFastEHEntry *ent = + &cur_wasm_func->exception_handlers[ent_eh_idx]; + WASMFastEHCatch *match = NULL; + uint32 mj; + for (mj = 0; mj < ent->catch_count; mj++) { + if (ent->catches[mj].tag_index == cells[1]) { + match = &ent->catches[mj]; + break; + } + } + if (match && match->param_dst_offsets) { + throw_param_cell_num = match->param_cell_num; + throw_src_offsets = match->param_dst_offsets; + } + else { + throw_param_cell_num = 0; + throw_src_offsets = NULL; + } + exception_tag_index = cells[1]; + goto find_a_catch_handler; + } + catch_seen++; + } + /* Loader validated rethrow's depth at compile time; + * if we got here the eh-stack is inconsistent with + * the IR (typically a runtime bug in the loader's + * eh-table population). */ + wasm_set_exception(module, "rethrow depth out of range"); + goto got_exception; + } + + HANDLE_OP(WASM_OP_DELEGATE) + { + /* Normal-flow exit from a `try ... delegate N` region: + * the try body completed without throwing, so the + * runtime just pops the eh-stack entry that + * HANDLE_OP(WASM_OP_TRY) pushed and falls through to + * the next op in the rewritten IR (which is whatever + * came after the `delegate N` in source). + * + * The forwarding semantics ("if the try body throws, + * re-raise at the target block") are handled by the + * find_a_catch_handler walker reading the eh-table + * entry's `delegate_target_depth` and skipping that + * many nested-try eh-stack entries — DELEGATE itself + * doesn't run in the throw path, only on fall-through. + * + * No immediate to read: the loader skipped emit_br_info + * so the depth lives in the per-function eh-table + * indexed by the eh_idx of *this* try-region (which is + * the eh-stack's top). Cost: one decrement on a cold + * path; CALL / LOAD / STORE untouched. */ + bh_assert(frame->eh_count > 0); + frame->eh_count--; + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_TRY) + { + /* The fast-interp loader doesn't emit EXT_OP_TRY yet + * (the eh-table records CATCH / CATCH_ALL / DELEGATE + * indices directly on the per-function table; TRY's + * uint32 immediate is the eh_idx, not a type-index + * blocktype). Reaching this handler means a future + * loader change started emitting EXT_OP_TRY without + * the runtime catching up — surface that as an + * explicit trap. */ + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + + /* parametric instructions */ + HANDLE_OP(WASM_OP_SELECT) + { + cond = frame_lp[GET_OFFSET()]; + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + if (!cond) { + if (addr_ret != addr1) + frame_lp[addr_ret] = frame_lp[addr1]; + } + else { + if (addr_ret != addr2) + frame_lp[addr_ret] = frame_lp[addr2]; + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SELECT_64) + { + cond = frame_lp[GET_OFFSET()]; + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + if (!cond) { + if (addr_ret != addr1) + PUT_I64_TO_ADDR(frame_lp + addr_ret, + GET_I64_FROM_ADDR(frame_lp + addr1)); + } + else { + if (addr_ret != addr2) + PUT_I64_TO_ADDR(frame_lp + addr_ret, + GET_I64_FROM_ADDR(frame_lp + addr2)); + } + HANDLE_OP_END(); + } +#if WASM_ENABLE_SIMDE != 0 + HANDLE_OP(WASM_OP_SELECT_128) + { + cond = frame_lp[GET_OFFSET()]; + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + if (!cond) { + if (addr_ret != addr1) + PUT_V128_TO_ADDR(frame_lp + addr_ret, + GET_V128_FROM_ADDR(frame_lp + addr1)); + } + else { + if (addr_ret != addr2) + PUT_V128_TO_ADDR(frame_lp + addr_ret, + GET_V128_FROM_ADDR(frame_lp + addr2)); + } + HANDLE_OP_END(); + } +#endif + +#if WASM_ENABLE_GC != 0 + HANDLE_OP(WASM_OP_SELECT_T) + { + cond = frame_lp[GET_OFFSET()]; + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + if (!cond) { + if (addr_ret != addr1) + PUT_REF_TO_ADDR(frame_lp + addr_ret, + GET_REF_FROM_ADDR(frame_lp + addr1)); + } + else { + if (addr_ret != addr2) + PUT_REF_TO_ADDR(frame_lp + addr_ret, + GET_REF_FROM_ADDR(frame_lp + addr2)); + } + { + uint8 orig_ref = 0; + /* Ignore constants because they are not reference */ + if (addr1 >= 0) { + orig_ref = *FRAME_REF(addr1); + CLEAR_FRAME_REF(addr1); + } + if (addr2 >= 0) { + CLEAR_FRAME_REF(addr2); + } + if (orig_ref) { + SET_FRAME_REF(addr_ret); + } + } + + HANDLE_OP_END(); + } +#endif + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + HANDLE_OP(WASM_OP_TABLE_GET) + { + uint32 tbl_idx, elem_idx; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + elem_idx = POP_I32(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + +#if WASM_ENABLE_GC == 0 + PUSH_I32(tbl_inst->elems[elem_idx]); +#else + PUSH_REF(tbl_inst->elems[elem_idx]); +#endif + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_TABLE_SET) + { + uint32 tbl_idx, elem_idx; + WASMTableInstance *tbl_inst; + table_elem_type_t elem_val; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + +#if WASM_ENABLE_GC == 0 + elem_val = POP_I32(); +#else + elem_val = POP_REF(); +#endif + elem_idx = POP_I32(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + tbl_inst->elems[elem_idx] = elem_val; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_NULL) + { +#if WASM_ENABLE_GC == 0 + PUSH_I32(NULL_REF); +#else + PUSH_REF(NULL_REF); +#endif + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_IS_NULL) + { +#if WASM_ENABLE_GC == 0 + uint32 ref_val; + ref_val = POP_I32(); +#else + void *ref_val; + ref_val = POP_REF(); +#endif + PUSH_I32(ref_val == NULL_REF ? 1 : 0); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_REF_FUNC) + { + uint32 func_idx = read_uint32(frame_ip); + +#if WASM_ENABLE_GC == 0 + PUSH_I32(func_idx); +#else + SYNC_ALL_TO_FRAME(); + if (!(gc_obj = wasm_create_func_obj(module, func_idx, true, + NULL, 0))) { + goto got_exception; + } + PUSH_REF(gc_obj); +#endif + HANDLE_OP_END(); + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_GC != 0 + HANDLE_OP(WASM_OP_CALL_REF) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + func_obj = POP_REF(); + if (!func_obj) { + wasm_set_exception(module, "null function reference"); + goto got_exception; + } + + fidx = wasm_func_obj_get_func_idx_bound(func_obj); + cur_func = module->e->functions + fidx; + goto call_func_from_interp; + } + HANDLE_OP(WASM_OP_RETURN_CALL_REF) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + func_obj = POP_REF(); + if (!func_obj) { + wasm_set_exception(module, "null function reference"); + goto got_exception; + } + + fidx = wasm_func_obj_get_func_idx_bound(func_obj); + cur_func = module->e->functions + fidx; + goto call_func_from_return_call; + } + HANDLE_OP(WASM_OP_REF_AS_NON_NULL) + { + gc_obj = POP_REF(); + if (gc_obj == NULL_REF) { + wasm_set_exception(module, "null reference"); + goto got_exception; + } + PUSH_REF(gc_obj); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_REF_EQ) + { + WASMObjectRef gc_obj1, gc_obj2; + gc_obj2 = POP_REF(); + gc_obj1 = POP_REF(); + val = wasm_obj_equal(gc_obj1, gc_obj2); + PUSH_I32(val); + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_BR_ON_NULL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + opnd_off = GET_OFFSET(); + gc_obj = GET_REF_FROM_ADDR(frame_lp + opnd_off); + if (gc_obj == NULL_REF) { + CLEAR_FRAME_REF(opnd_off); + goto recover_br_info; + } + else { + SKIP_BR_INFO(); + } + HANDLE_OP_END(); + } + HANDLE_OP(WASM_OP_BR_ON_NON_NULL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + opnd_off = GET_OFFSET(); + gc_obj = GET_REF_FROM_ADDR(frame_lp + opnd_off); + if (gc_obj != NULL_REF) { + goto recover_br_info; + } + else { + CLEAR_FRAME_REF(opnd_off); + SKIP_BR_INFO(); + } + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_GC_PREFIX) + { + GET_OPCODE(); + + switch (opcode) { + case WASM_OP_STRUCT_NEW: + case WASM_OP_STRUCT_NEW_DEFAULT: + { + WASMModule *wasm_module = module->module; + WASMStructType *struct_type; + WASMRttType *rtt_type; + WASMValue field_value = { 0 }; + + type_idx = read_uint32(frame_ip); + struct_type = + (WASMStructType *)module->module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)struct_type, type_idx, + wasm_module->rtt_types, + wasm_module->type_count, + &wasm_module->rtt_type_lock))) { + wasm_set_exception(module, + "create rtt type failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + struct_obj = wasm_struct_obj_new(exec_env, rtt_type); + if (!struct_obj) { + wasm_set_exception(module, + "create struct object failed"); + goto got_exception; + } + + if (opcode == WASM_OP_STRUCT_NEW) { + WASMStructFieldType *fields = struct_type->fields; + int32 field_count = (int32)struct_type->field_count; + int32 field_idx; + uint8 field_type; + + for (field_idx = field_count - 1; field_idx >= 0; + field_idx--) { + field_type = fields[field_idx].field_type; + if (wasm_is_type_reftype(field_type)) { + field_value.gc_obj = POP_REF(); + } + else if (field_type == VALUE_TYPE_I32 + || field_type == VALUE_TYPE_F32 + || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + field_value.i32 = POP_I32(); + } + else { + field_value.i64 = POP_I64(); + } + wasm_struct_obj_set_field(struct_obj, field_idx, + &field_value); + } + } + PUSH_REF(struct_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRUCT_GET: + case WASM_OP_STRUCT_GET_S: + case WASM_OP_STRUCT_GET_U: + { + WASMStructType *struct_type; + WASMValue field_value = { 0 }; + uint32 field_idx; + uint8 field_type; + + type_idx = read_uint32(frame_ip); + field_idx = read_uint32(frame_ip); + + struct_type = + (WASMStructType *)module->module->types[type_idx]; + + struct_obj = POP_REF(); + + if (!struct_obj) { + wasm_set_exception(module, + "null structure reference"); + goto got_exception; + } + + wasm_struct_obj_get_field( + struct_obj, field_idx, + opcode == WASM_OP_STRUCT_GET_S ? true : false, + &field_value); + + field_type = struct_type->fields[field_idx].field_type; + if (wasm_is_reftype_i31ref(field_type)) { + PUSH_I31REF(field_value.gc_obj); + } + else if (wasm_is_type_reftype(field_type)) { + PUSH_REF(field_value.gc_obj); + } + else if (field_type == VALUE_TYPE_I32 + || field_type == VALUE_TYPE_F32 + || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + PUSH_I32(field_value.i32); + } + else { + PUSH_I64(field_value.i64); + } + HANDLE_OP_END(); + } + case WASM_OP_STRUCT_SET: + { + WASMStructType *struct_type; + WASMValue field_value = { 0 }; + uint32 field_idx; + uint8 field_type; + + type_idx = read_uint32(frame_ip); + field_idx = read_uint32(frame_ip); + + struct_type = + (WASMStructType *)module->module->types[type_idx]; + field_type = struct_type->fields[field_idx].field_type; + + if (wasm_is_type_reftype(field_type)) { + field_value.gc_obj = POP_REF(); + } + else if (field_type == VALUE_TYPE_I32 + || field_type == VALUE_TYPE_F32 + || field_type == PACKED_TYPE_I8 + || field_type == PACKED_TYPE_I16) { + field_value.i32 = POP_I32(); + } + else { + field_value.i64 = POP_I64(); + } + + struct_obj = POP_REF(); + if (!struct_obj) { + wasm_set_exception(module, + "null structure reference"); + goto got_exception; + } + + wasm_struct_obj_set_field(struct_obj, field_idx, + &field_value); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_NEW: + case WASM_OP_ARRAY_NEW_DEFAULT: + case WASM_OP_ARRAY_NEW_FIXED: + { + WASMModule *wasm_module = module->module; + WASMArrayType *array_type; + WASMRttType *rtt_type; + WASMValue array_elem = { 0 }; + uint32 array_len, i; + + type_idx = read_uint32(frame_ip); + array_type = + (WASMArrayType *)wasm_module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_idx, + wasm_module->rtt_types, + wasm_module->type_count, + &wasm_module->rtt_type_lock))) { + wasm_set_exception(module, + "create rtt type failed"); + goto got_exception; + } + + if (opcode != WASM_OP_ARRAY_NEW_FIXED) + array_len = POP_I32(); + else + array_len = read_uint32(frame_ip); + + if (opcode == WASM_OP_ARRAY_NEW) { + if (wasm_is_type_reftype(array_type->elem_type)) { + array_elem.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32 + || array_type->elem_type == PACKED_TYPE_I8 + || array_type->elem_type + == PACKED_TYPE_I16) { + array_elem.i32 = POP_I32(); + } + else { + array_elem.i64 = POP_I64(); + } + } + + SYNC_ALL_TO_FRAME(); + array_obj = wasm_array_obj_new(exec_env, rtt_type, + array_len, &array_elem); + if (!array_obj) { + wasm_set_exception(module, + "create array object failed"); + goto got_exception; + } + + if (opcode == WASM_OP_ARRAY_NEW_FIXED) { + for (i = 0; i < array_len; i++) { + if (wasm_is_type_reftype( + array_type->elem_type)) { + array_elem.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type + == VALUE_TYPE_F32 + || array_type->elem_type + == PACKED_TYPE_I8 + || array_type->elem_type + == PACKED_TYPE_I16) { + array_elem.i32 = POP_I32(); + } + else { + array_elem.i64 = POP_I64(); + } + wasm_array_obj_set_elem( + array_obj, array_len - 1 - i, &array_elem); + } + } + + PUSH_REF(array_obj); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_NEW_DATA: + { + WASMModule *wasm_module = module->module; + WASMArrayType *array_type; + WASMRttType *rtt_type; + WASMValue array_elem = { 0 }; + WASMDataSeg *data_seg; + uint8 *array_elem_base; + uint32 array_len, data_seg_idx, data_seg_offset; + uint32 elem_size = 0; + uint64 total_size; + + type_idx = read_uint32(frame_ip); + data_seg_idx = read_uint32(frame_ip); + data_seg = wasm_module->data_segments[data_seg_idx]; + + array_type = + (WASMArrayType *)wasm_module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_idx, + wasm_module->rtt_types, + wasm_module->type_count, + &wasm_module->rtt_type_lock))) { + wasm_set_exception(module, + "create rtt type failed"); + goto got_exception; + } + + array_len = POP_I32(); + data_seg_offset = POP_I32(); + + switch (array_type->elem_type) { + case PACKED_TYPE_I8: + elem_size = 1; + break; + case PACKED_TYPE_I16: + elem_size = 2; + break; + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + elem_size = 4; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + elem_size = 8; + break; + default: + bh_assert(0); + } + + total_size = (uint64)elem_size * array_len; + if (data_seg_offset >= data_seg->data_length + || total_size + > data_seg->data_length - data_seg_offset) { + wasm_set_exception(module, + "data segment out of bounds"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + array_obj = wasm_array_obj_new(exec_env, rtt_type, + array_len, &array_elem); + if (!array_obj) { + wasm_set_exception(module, + "create array object failed"); + goto got_exception; + } + + array_elem_base = + (uint8 *)wasm_array_obj_first_elem_addr(array_obj); + bh_memcpy_s(array_elem_base, (uint32)total_size, + data_seg->data + data_seg_offset, + (uint32)total_size); + + PUSH_REF(array_obj); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_NEW_ELEM: + { + /* TODO */ + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } + case WASM_OP_ARRAY_GET: + case WASM_OP_ARRAY_GET_S: + case WASM_OP_ARRAY_GET_U: + { + WASMArrayType *array_type; + WASMValue array_elem = { 0 }; + uint32 elem_idx, elem_size_log; + + type_idx = read_uint32(frame_ip); + array_type = + (WASMArrayType *)module->module->types[type_idx]; + + elem_idx = POP_I32(); + array_obj = POP_REF(); + + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + if (elem_idx >= wasm_array_obj_length(array_obj)) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_get_elem( + array_obj, elem_idx, + opcode == WASM_OP_ARRAY_GET_S ? true : false, + &array_elem); + elem_size_log = wasm_array_obj_elem_size_log(array_obj); + + if (wasm_is_reftype_i31ref(array_type->elem_type)) { + PUSH_I31REF(array_elem.gc_obj); + } + else if (wasm_is_type_reftype(array_type->elem_type)) { + PUSH_REF(array_elem.gc_obj); + } + else if (elem_size_log < 3) { + PUSH_I32(array_elem.i32); + } + else { + PUSH_I64(array_elem.i64); + } + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_SET: + { + WASMArrayType *array_type; + WASMValue array_elem = { 0 }; + uint32 elem_idx; + + type_idx = read_uint32(frame_ip); + array_type = + (WASMArrayType *)module->module->types[type_idx]; + if (wasm_is_type_reftype(array_type->elem_type)) { + array_elem.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32 + || array_type->elem_type == PACKED_TYPE_I8 + || array_type->elem_type == PACKED_TYPE_I16) { + array_elem.i32 = POP_I32(); + } + else { + array_elem.i64 = POP_I64(); + } + + elem_idx = POP_I32(); + array_obj = POP_REF(); + + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + if (elem_idx >= wasm_array_obj_length(array_obj)) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_set_elem(array_obj, elem_idx, + &array_elem); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_LEN: + { + uint32 array_len; + array_obj = POP_REF(); + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + array_len = wasm_array_obj_length(array_obj); + PUSH_I32(array_len); + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_FILL: + { + WASMArrayType *array_type; + WASMValue fill_value = { 0 }; + uint32 start_offset, len; + + type_idx = read_uint32(frame_ip); + + array_type = + (WASMArrayType *)module->module->types[type_idx]; + + len = POP_I32(); + if (wasm_is_type_reftype(array_type->elem_type)) { + fill_value.gc_obj = POP_REF(); + } + else if (array_type->elem_type == VALUE_TYPE_I32 + || array_type->elem_type == VALUE_TYPE_F32 + || array_type->elem_type == PACKED_TYPE_I8 + || array_type->elem_type == PACKED_TYPE_I16) { + fill_value.i32 = POP_I32(); + } + else { + fill_value.i64 = POP_I64(); + } + start_offset = POP_I32(); + array_obj = POP_REF(); + + if (!array_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + + if (len > 0) { + if ((uint64)start_offset + len + > wasm_array_obj_length(array_obj)) { + wasm_set_exception( + module, "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_fill(array_obj, start_offset, len, + &fill_value); + } + + HANDLE_OP_END(); + } + case WASM_OP_ARRAY_COPY: + { + uint32 dst_offset, src_offset, len, src_type_index; + WASMArrayObjectRef src_obj, dst_obj; + + type_idx = read_uint32(frame_ip); + src_type_index = read_uint32(frame_ip); + + len = POP_I32(); + src_offset = POP_I32(); + src_obj = POP_REF(); + dst_offset = POP_I32(); + dst_obj = POP_REF(); + + if (!src_obj || !dst_obj) { + wasm_set_exception(module, "null array reference"); + goto got_exception; + } + + if (len > 0) { + if ((dst_offset > UINT32_MAX - len) + || (dst_offset + len + > wasm_array_obj_length(dst_obj)) + || (src_offset > UINT32_MAX - len) + || (src_offset + len + > wasm_array_obj_length(src_obj))) { + wasm_set_exception( + module, "out of bounds array access"); + goto got_exception; + } + + wasm_array_obj_copy(dst_obj, dst_offset, src_obj, + src_offset, len); + } + + (void)src_type_index; + HANDLE_OP_END(); + } + + case WASM_OP_REF_I31: + { + uint32 i31_val; + + i31_val = POP_I32(); + i31_obj = wasm_i31_obj_new(i31_val); + PUSH_I31REF(i31_obj); + HANDLE_OP_END(); + } + case WASM_OP_I31_GET_S: + case WASM_OP_I31_GET_U: + { + uint32 i31_val; + + i31_obj = (WASMI31ObjectRef)POP_REF(); + if (!i31_obj) { + wasm_set_exception(module, "null i31 reference"); + goto got_exception; + } + i31_val = (uint32)(((uintptr_t)i31_obj) >> 1); + if (opcode == WASM_OP_I31_GET_S + && (i31_val & 0x40000000) /* bit 30 is 1 */) + /* set bit 31 to 1 */ + i31_val |= 0x80000000; + PUSH_I32(i31_val); + HANDLE_OP_END(); + } + + case WASM_OP_REF_TEST: + case WASM_OP_REF_CAST: + case WASM_OP_REF_TEST_NULLABLE: + case WASM_OP_REF_CAST_NULLABLE: + { + int32 heap_type; + + heap_type = (int32)read_uint32(frame_ip); + + gc_obj = POP_REF(); + if (!gc_obj) { + if (opcode == WASM_OP_REF_TEST + || opcode == WASM_OP_REF_TEST_NULLABLE) { + if (opcode == WASM_OP_REF_TEST) + PUSH_I32(0); + else + PUSH_I32(1); + } + else if (opcode == WASM_OP_REF_CAST) { + wasm_set_exception(module, "cast failure"); + goto got_exception; + } + else { + PUSH_REF(gc_obj); + } + } + else { + bool castable = false; + + if (heap_type >= 0) { + WASMModule *wasm_module = module->module; + castable = wasm_obj_is_instance_of( + gc_obj, (uint32)heap_type, + wasm_module->types, + wasm_module->type_count); + } + else { + castable = + wasm_obj_is_type_of(gc_obj, heap_type); + } + + if (opcode == WASM_OP_REF_TEST + || opcode == WASM_OP_REF_TEST_NULLABLE) { + if (castable) + PUSH_I32(1); + else + PUSH_I32(0); + } + else if (!castable) { + wasm_set_exception(module, "cast failure"); + goto got_exception; + } + else { + PUSH_REF(gc_obj); + } + } + HANDLE_OP_END(); + } + + case WASM_OP_BR_ON_CAST: + case WASM_OP_BR_ON_CAST_FAIL: + { + int32 heap_type, heap_type_dst; + uint8 castflags; + uint16 opnd_off_br; + +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + castflags = *frame_ip++; + heap_type = (int32)read_uint32(frame_ip); + heap_type_dst = (int32)read_uint32(frame_ip); + + opnd_off = GET_OFFSET(); + opnd_off_br = GET_OFFSET(); + gc_obj = GET_REF_FROM_ADDR(frame_lp + opnd_off); + PUT_REF_TO_ADDR(frame_lp + opnd_off_br, gc_obj); + + if (!gc_obj) { + /* + * castflags should be 0~3: + * 0: (non-null, non-null) + * 1: (null, non-null) + * 2: (non-null, null) + * 3: (null, null) + */ + if ( + /* op is BR_ON_CAST and dst reftype is nullable + */ + ((opcode == WASM_OP_BR_ON_CAST) + && ((castflags == 2) || (castflags == 3))) + /* op is BR_ON_CAST_FAIL and dst reftype is + non-nullable */ + || ((opcode == WASM_OP_BR_ON_CAST_FAIL) + && ((castflags == 0) + || (castflags == 1)))) { + CLEAR_FRAME_REF(opnd_off); + if (!wasm_is_reftype_i31ref(heap_type)) { + SET_FRAME_REF(opnd_off_br); + } + goto recover_br_info; + } + } + else { + bool castable = false; + + if (heap_type_dst >= 0) { + WASMModule *wasm_module = module->module; + castable = wasm_obj_is_instance_of( + gc_obj, (uint32)heap_type_dst, + wasm_module->types, + wasm_module->type_count); + } + else { + castable = + wasm_obj_is_type_of(gc_obj, heap_type_dst); + } + + if ((castable && (opcode == WASM_OP_BR_ON_CAST)) + || (!castable + && (opcode == WASM_OP_BR_ON_CAST_FAIL))) { + CLEAR_FRAME_REF(opnd_off); + if (!wasm_is_reftype_i31ref(heap_type)) { + SET_FRAME_REF(opnd_off_br); + } + goto recover_br_info; + } + } + SKIP_BR_INFO(); + + (void)heap_type_dst; + HANDLE_OP_END(); + } + + case WASM_OP_ANY_CONVERT_EXTERN: + { + externref_obj = POP_REF(); + if (externref_obj == NULL_REF) + PUSH_REF(NULL_REF); + else { + gc_obj = wasm_externref_obj_to_internal_obj( + externref_obj); + PUSH_REF(gc_obj); + } + HANDLE_OP_END(); + } + case WASM_OP_EXTERN_CONVERT_ANY: + { + gc_obj = POP_REF(); + if (gc_obj == NULL_REF) + PUSH_REF(NULL_REF); + else { + if (!(externref_obj = + wasm_internal_obj_to_externref_obj( + exec_env, gc_obj))) { + wasm_set_exception( + module, "create externref object failed"); + goto got_exception; + } + PUSH_REF(externref_obj); + } + HANDLE_OP_END(); + } + +#if WASM_ENABLE_STRINGREF != 0 + case WASM_OP_STRING_NEW_UTF8: + case WASM_OP_STRING_NEW_WTF16: + case WASM_OP_STRING_NEW_LOSSY_UTF8: + case WASM_OP_STRING_NEW_WTF8: + { + uint32 mem_idx, addr, bytes_length, offset = 0; + EncodingFlag flag = WTF8; + + mem_idx = (uint32)read_uint32(frame_ip); + bytes_length = POP_I32(); + addr = POP_I32(); + + CHECK_MEMORY_OVERFLOW(bytes_length); + + if (opcode == WASM_OP_STRING_NEW_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_NEW_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_WTF8) { + flag = WTF8; + } + + str_obj = wasm_string_new_with_encoding( + maddr, bytes_length, flag); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + + (void)mem_idx; + HANDLE_OP_END(); + } + case WASM_OP_STRING_CONST: + { + WASMModule *wasm_module = module->module; + uint32 contents; + + contents = (uint32)read_uint32(frame_ip); + + str_obj = wasm_string_new_const( + (const char *) + wasm_module->string_literal_ptrs[contents], + wasm_module->string_literal_lengths[contents]); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!str_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_MEASURE_UTF8: + case WASM_OP_STRING_MEASURE_WTF8: + case WASM_OP_STRING_MEASURE_WTF16: + { + int32 target_bytes_length; + EncodingFlag flag = WTF8; + + stringref_obj = POP_REF(); + + if (opcode == WASM_OP_STRING_MEASURE_WTF16) { + flag = WTF16; + } + else if (opcode == WASM_OP_STRING_MEASURE_UTF8) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_MEASURE_WTF8) { + flag = LOSSY_UTF8; + } + target_bytes_length = wasm_string_measure( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + flag); + + PUSH_I32(target_bytes_length); + HANDLE_OP_END(); + } + case WASM_OP_STRING_ENCODE_UTF8: + case WASM_OP_STRING_ENCODE_WTF16: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8: + case WASM_OP_STRING_ENCODE_WTF8: + { + uint32 mem_idx, addr; + int32 target_bytes_length; + WASMMemoryInstance *memory_inst; + EncodingFlag flag = WTF8; + + mem_idx = (uint32)read_uint32(frame_ip); + addr = POP_I32(); + stringref_obj = POP_REF(); + + str_obj = (WASMString)wasm_stringref_obj_get_value( + stringref_obj); + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)addr, 1)) + shared_heap_addr_app_to_native((uint64)addr, maddr); + else +#endif + { + memory_inst = module->memories[mem_idx]; + maddr = memory_inst->memory_data + addr; + } + + if (opcode == WASM_OP_STRING_ENCODE_WTF16) { + flag = WTF16; + count = wasm_string_measure(str_obj, flag); + target_bytes_length = wasm_string_encode( + str_obj, 0, count, maddr, NULL, flag); + } + else { + if (opcode == WASM_OP_STRING_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRING_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode == WASM_OP_STRING_ENCODE_WTF8) { + flag = WTF8; + } + count = wasm_string_measure(str_obj, flag); + target_bytes_length = wasm_string_encode( + str_obj, 0, count, maddr, NULL, flag); + + if (target_bytes_length == -1) { + wasm_set_exception( + module, "isolated surrogate is seen"); + goto got_exception; + } + } + if (target_bytes_length < 0) { + wasm_set_exception(module, + "stringref encode failed"); + goto got_exception; + } + + PUSH_I32(target_bytes_length); + HANDLE_OP_END(); + } + case WASM_OP_STRING_CONCAT: + { + WASMStringrefObjectRef stringref_obj1, stringref_obj2; + + stringref_obj2 = POP_REF(); + stringref_obj1 = POP_REF(); + + str_obj = wasm_string_concat( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj1), + (WASMString)wasm_stringref_obj_get_value( + stringref_obj2)); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_EQ: + { + WASMStringrefObjectRef stringref_obj1, stringref_obj2; + int32 is_eq; + + stringref_obj2 = POP_REF(); + stringref_obj1 = POP_REF(); + + is_eq = wasm_string_eq( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj1), + (WASMString)wasm_stringref_obj_get_value( + stringref_obj2)); + + PUSH_I32(is_eq); + HANDLE_OP_END(); + } + case WASM_OP_STRING_IS_USV_SEQUENCE: + { + int32 is_usv_sequence; + + stringref_obj = POP_REF(); + + is_usv_sequence = wasm_string_is_usv_sequence( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj)); + + PUSH_I32(is_usv_sequence); + HANDLE_OP_END(); + } + case WASM_OP_STRING_AS_WTF8: + { + stringref_obj = POP_REF(); + + str_obj = wasm_string_create_view( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + STRING_VIEW_WTF8); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringview_wtf8_obj = + wasm_stringview_wtf8_obj_new(exec_env, str_obj); + if (!stringview_wtf8_obj) { + wasm_set_exception(module, + "create stringview wtf8 failed"); + goto got_exception; + } + + PUSH_REF(stringview_wtf8_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF8_ADVANCE: + { + uint32 next_pos, bytes, pos; + + bytes = POP_I32(); + pos = POP_I32(); + stringview_wtf8_obj = POP_REF(); + + next_pos = wasm_string_advance( + (WASMString)wasm_stringview_wtf8_obj_get_value( + stringview_wtf8_obj), + pos, bytes, NULL); + + PUSH_I32(next_pos); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8: + { + uint32 mem_idx, addr, pos, bytes, next_pos; + int32 bytes_written; + WASMMemoryInstance *memory_inst; + EncodingFlag flag = WTF8; + + if (opcode == WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8) { + flag = LOSSY_UTF8; + } + else if (opcode + == WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8) { + flag = WTF8; + } + + mem_idx = (uint32)read_uint32(frame_ip); + bytes = POP_I32(); + pos = POP_I32(); + addr = POP_I32(); + stringview_wtf8_obj = POP_REF(); + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)addr, 1)) + shared_heap_addr_app_to_native((uint64)addr, maddr); + else +#endif + { + memory_inst = module->memories[mem_idx]; + maddr = memory_inst->memory_data + addr; + } + + bytes_written = wasm_string_encode( + (WASMString)wasm_stringview_wtf8_obj_get_value( + stringview_wtf8_obj), + pos, bytes, maddr, &next_pos, flag); + + if (bytes_written < 0) { + if (bytes_written == Isolated_Surrogate) { + wasm_set_exception( + module, "isolated surrogate is seen"); + } + else { + wasm_set_exception(module, "encode failed"); + } + + goto got_exception; + } + + PUSH_I32(next_pos); + PUSH_I32(bytes_written); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF8_SLICE: + { + uint32 start, end; + + end = POP_I32(); + start = POP_I32(); + stringview_wtf8_obj = POP_REF(); + + str_obj = wasm_string_slice( + (WASMString)wasm_stringview_wtf8_obj_get_value( + stringview_wtf8_obj), + start, end, STRING_VIEW_WTF8); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_AS_WTF16: + { + stringref_obj = POP_REF(); + + str_obj = wasm_string_create_view( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + STRING_VIEW_WTF16); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringview_wtf16_obj = + wasm_stringview_wtf16_obj_new(exec_env, str_obj); + if (!stringview_wtf16_obj) { + wasm_set_exception( + module, "create stringview wtf16 failed"); + goto got_exception; + } + + PUSH_REF(stringview_wtf16_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_LENGTH: + { + int32 code_units_length; + + stringview_wtf16_obj = POP_REF(); + + code_units_length = wasm_string_wtf16_get_length( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj)); + + PUSH_I32(code_units_length); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_GET_CODEUNIT: + { + int32 pos; + uint32 code_unit; + + pos = POP_I32(); + stringview_wtf16_obj = POP_REF(); + + code_unit = (uint32)wasm_string_get_wtf16_codeunit( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj), + pos); + + PUSH_I32(code_unit); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_ENCODE: + { + uint32 mem_idx, addr, pos, len, offset = 0; + int32 written_code_units = 0; + + mem_idx = (uint32)read_uint32(frame_ip); + len = POP_I32(); + pos = POP_I32(); + addr = POP_I32(); + stringview_wtf16_obj = POP_REF(); + + CHECK_MEMORY_OVERFLOW(len * sizeof(uint16)); + + /* check 2-byte alignment */ + if (((uintptr_t)maddr & (((uintptr_t)1 << 2) - 1)) + != 0) { + wasm_set_exception(module, + "unaligned memory access"); + goto got_exception; + } + + written_code_units = wasm_string_encode( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj), + pos, len, maddr, NULL, WTF16); + + PUSH_I32(written_code_units); + (void)mem_idx; + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_WTF16_SLICE: + { + uint32 start, end; + + end = POP_I32(); + start = POP_I32(); + stringview_wtf16_obj = POP_REF(); + + str_obj = wasm_string_slice( + (WASMString)wasm_stringview_wtf16_obj_get_value( + stringview_wtf16_obj), + start, end, STRING_VIEW_WTF16); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_AS_ITER: + { + stringref_obj = POP_REF(); + + str_obj = wasm_string_create_view( + (WASMString)wasm_stringref_obj_get_value( + stringref_obj), + STRING_VIEW_ITER); + + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringview_iter_obj = + wasm_stringview_iter_obj_new(exec_env, str_obj, 0); + if (!stringview_iter_obj) { + wasm_set_exception(module, + "create stringview iter failed"); + goto got_exception; + } + + PUSH_REF(stringview_iter_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_ITER_NEXT: + { + uint32 code_point; + + stringview_iter_obj = POP_REF(); + + code_point = wasm_string_next_codepoint( + (WASMString)wasm_stringview_iter_obj_get_value( + stringview_iter_obj), + wasm_stringview_iter_obj_get_pos( + stringview_iter_obj)); + + PUSH_I32(code_point); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_ITER_ADVANCE: + case WASM_OP_STRINGVIEW_ITER_REWIND: + { + uint32 code_points_count, code_points_consumed = 0, + cur_pos, next_pos = 0; + + code_points_count = POP_I32(); + stringview_iter_obj = POP_REF(); + + str_obj = + (WASMString)wasm_stringview_iter_obj_get_value( + stringview_iter_obj); + cur_pos = wasm_stringview_iter_obj_get_pos( + stringview_iter_obj); + + if (opcode == WASM_OP_STRINGVIEW_ITER_ADVANCE) { + next_pos = wasm_string_advance( + str_obj, cur_pos, code_points_count, + &code_points_consumed); + } + else if (opcode == WASM_OP_STRINGVIEW_ITER_REWIND) { + next_pos = wasm_string_rewind( + str_obj, cur_pos, code_points_count, + &code_points_consumed); + } + + wasm_stringview_iter_obj_update_pos(stringview_iter_obj, + next_pos); + + PUSH_I32(code_points_consumed); + HANDLE_OP_END(); + } + case WASM_OP_STRINGVIEW_ITER_SLICE: + { + uint32 code_points_count, cur_pos; + + code_points_count = POP_I32(); + stringview_iter_obj = POP_REF(); + + cur_pos = wasm_stringview_iter_obj_get_pos( + stringview_iter_obj); + + str_obj = wasm_string_slice( + (WASMString)wasm_stringview_iter_obj_get_value( + stringview_iter_obj), + cur_pos, cur_pos + code_points_count, + STRING_VIEW_ITER); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_NEW_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF16_ARRAY: + case WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF8_ARRAY: + { + uint32 start, end, array_len; + EncodingFlag flag = WTF8; + WASMArrayType *array_type; + void *arr_start_addr; + + end = POP_I32(); + start = POP_I32(); + array_obj = POP_REF(); + + array_type = (WASMArrayType *)wasm_obj_get_defined_type( + (WASMObjectRef)array_obj); + arr_start_addr = + wasm_array_obj_elem_addr(array_obj, start); + array_len = wasm_array_obj_length(array_obj); + + if (start > end || end > array_len) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + if (opcode == WASM_OP_STRING_NEW_WTF16_ARRAY) { + if (array_type->elem_type != VALUE_TYPE_I16) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + flag = WTF16; + } + else { + if (array_type->elem_type != VALUE_TYPE_I8) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + if (opcode == WASM_OP_STRING_NEW_UTF8_ARRAY) { + flag = UTF8; + } + else if (opcode == WASM_OP_STRING_NEW_WTF8_ARRAY) { + flag = WTF8; + } + else if (opcode + == WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY) { + flag = LOSSY_UTF8; + } + } + + str_obj = wasm_string_new_with_encoding( + arr_start_addr, (end - start), flag); + if (!str_obj) { + wasm_set_exception(module, + "create string object failed"); + goto got_exception; + } + + SYNC_ALL_TO_FRAME(); + stringref_obj = + wasm_stringref_obj_new(exec_env, str_obj); + if (!stringref_obj) { + wasm_set_exception(module, + "create stringref failed"); + goto got_exception; + } + + PUSH_REF(stringref_obj); + HANDLE_OP_END(); + } + case WASM_OP_STRING_ENCODE_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF16_ARRAY: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF8_ARRAY: + { + uint32 start, array_len, count; + int32 bytes_written; + EncodingFlag flag = WTF8; + WASMArrayType *array_type; + void *arr_start_addr; + + start = POP_I32(); + array_obj = POP_REF(); + stringref_obj = POP_REF(); + + str_obj = (WASMString)wasm_stringref_obj_get_value( + stringref_obj); + + array_type = (WASMArrayType *)wasm_obj_get_defined_type( + (WASMObjectRef)array_obj); + arr_start_addr = + wasm_array_obj_elem_addr(array_obj, start); + array_len = wasm_array_obj_length(array_obj); + + if (start > array_len) { + wasm_set_exception(module, + "out of bounds array access"); + goto got_exception; + } + + if (opcode == WASM_OP_STRING_ENCODE_WTF16_ARRAY) { + if (array_type->elem_type != VALUE_TYPE_I16) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + flag = WTF16; + } + else { + if (array_type->elem_type != VALUE_TYPE_I8) { + wasm_set_exception(module, + "array type mismatch"); + goto got_exception; + } + if (opcode == WASM_OP_STRING_ENCODE_UTF8_ARRAY) { + flag = UTF8; + } + else if (opcode + == WASM_OP_STRING_ENCODE_WTF8_ARRAY) { + flag = WTF8; + } + else if ( + opcode + == WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY) { + flag = LOSSY_UTF8; + } + } + + count = wasm_string_measure(str_obj, flag); + + bytes_written = wasm_string_encode( + str_obj, 0, count, arr_start_addr, NULL, flag); + + if (bytes_written < 0) { + if (bytes_written == Isolated_Surrogate) { + wasm_set_exception( + module, "isolated surrogate is seen"); + } + else if (bytes_written == Insufficient_Space) { + wasm_set_exception( + module, "array space is insufficient"); + } + else { + wasm_set_exception(module, "encode failed"); + } + + goto got_exception; + } + + PUSH_I32(bytes_written); + HANDLE_OP_END(); + } +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + + default: + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + /* variable instructions */ + HANDLE_OP(EXT_OP_SET_LOCAL_FAST) + HANDLE_OP(EXT_OP_TEE_LOCAL_FAST) + { + /* clang-format off */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + local_offset = *frame_ip++; +#else + local_offset = *frame_ip; + frame_ip += 2; +#endif + /* clang-format on */ + *(uint32 *)(frame_lp + local_offset) = + GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_SET_LOCAL_FAST_I64) + HANDLE_OP(EXT_OP_TEE_LOCAL_FAST_I64) + { + /* clang-format off */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + local_offset = *frame_ip++; +#else + local_offset = *frame_ip; + frame_ip += 2; +#endif + /* clang-format on */ + PUT_I64_TO_ADDR((uint32 *)(frame_lp + local_offset), + GET_OPERAND(uint64, I64, 0)); + frame_ip += 2; + HANDLE_OP_END(); + } + +#if WASM_ENABLE_SIMDE != 0 + HANDLE_OP(EXT_OP_SET_LOCAL_FAST_V128) + HANDLE_OP(EXT_OP_TEE_LOCAL_FAST_V128) + { + /* clang-format off */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + local_offset = *frame_ip++; +#else + local_offset = *frame_ip; + frame_ip += 2; +#endif + /* clang-format on */ + PUT_V128_TO_ADDR((uint32 *)(frame_lp + local_offset), + GET_OPERAND_V128(0)); + frame_ip += 2; + HANDLE_OP_END(); + } +#endif + HANDLE_OP(WASM_OP_GET_GLOBAL) + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + addr_ret = GET_OFFSET(); + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + frame_lp[addr_ret] = *(uint32 *)global_addr; +#else + if (!wasm_is_type_reftype(global->type)) + frame_lp[addr_ret] = *(uint32 *)global_addr; + else { + PUT_REF_TO_ADDR(frame_lp + addr_ret, + GET_REF_FROM_ADDR((uint32 *)global_addr)); + if (!wasm_is_reftype_i31ref(global->type)) { + SET_FRAME_REF(addr_ret); + } + } +#endif + /* clang-format on */ + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_GET_GLOBAL_64) + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + addr_ret = GET_OFFSET(); + PUT_I64_TO_ADDR(frame_lp + addr_ret, + GET_I64_FROM_ADDR((uint32 *)global_addr)); + HANDLE_OP_END(); + } +#if WASM_ENABLE_SIMDE != 0 + HANDLE_OP(WASM_OP_GET_GLOBAL_V128) + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + addr_ret = GET_OFFSET(); + PUT_V128_TO_ADDR(frame_lp + addr_ret, + GET_V128_FROM_ADDR((uint32 *)global_addr)); + HANDLE_OP_END(); + } +#endif + HANDLE_OP(WASM_OP_SET_GLOBAL) + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + addr1 = GET_OFFSET(); + /* clang-format off */ +#if WASM_ENABLE_GC == 0 + *(int32 *)global_addr = frame_lp[addr1]; +#else + if (!wasm_is_type_reftype(global->type)) + *(int32 *)global_addr = frame_lp[addr1]; + else { + PUT_REF_TO_ADDR((uint32 *)global_addr, + GET_REF_FROM_ADDR(frame_lp + addr1)); + CLEAR_FRAME_REF(addr1); + } +#endif + /* clang-format on */ + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_GLOBAL_AUX_STACK) + { + uint64 aux_stack_top; + + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + /* TODO: Memory64 the data type depends on mem idx type */ + aux_stack_top = (uint64)frame_lp[GET_OFFSET()]; + if (aux_stack_top <= (uint64)exec_env->aux_stack_boundary) { + wasm_set_exception(module, "wasm auxiliary stack overflow"); + goto got_exception; + } + if (aux_stack_top > (uint64)exec_env->aux_stack_bottom) { + wasm_set_exception(module, + "wasm auxiliary stack underflow"); + goto got_exception; + } + *(int32 *)global_addr = (uint32)aux_stack_top; +#if WASM_ENABLE_MEMORY_PROFILING != 0 + if (module->module->aux_stack_top_global_index != (uint32)-1) { + uint32 aux_stack_used = + (uint32)(module->module->aux_stack_bottom + - *(uint32 *)global_addr); + if (aux_stack_used > module->e->max_aux_stack_used) + module->e->max_aux_stack_used = aux_stack_used; + } +#endif + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_GLOBAL_64) + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + addr1 = GET_OFFSET(); + PUT_I64_TO_ADDR((uint32 *)global_addr, + GET_I64_FROM_ADDR(frame_lp + addr1)); + HANDLE_OP_END(); + } +#if WASM_ENABLE_SIMDE != 0 + HANDLE_OP(WASM_OP_SET_GLOBAL_V128) + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->e->global_count); + global = globals + global_idx; + global_addr = get_global_addr(global_data, global); + addr1 = GET_OFFSET(); + PUT_V128_TO_ADDR((uint32 *)global_addr, + GET_V128_FROM_ADDR(frame_lp + addr1)); + HANDLE_OP_END(); + } +#endif + + /* memory load instructions */ + HANDLE_OP(WASM_OP_I32_LOAD) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + frame_lp[addr_ret] = LOAD_I32(maddr); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(8); + PUT_I64_TO_ADDR(frame_lp + addr_ret, LOAD_I64(maddr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD8_S) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + frame_lp[addr_ret] = sign_ext_8_32(*(int8 *)maddr); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD8_U) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + frame_lp[addr_ret] = (uint32)(*(uint8 *)(maddr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD16_S) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + frame_lp[addr_ret] = sign_ext_16_32(LOAD_I16(maddr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LOAD16_U) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + frame_lp[addr_ret] = (uint32)(LOAD_U16(maddr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD8_S) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + PUT_I64_TO_ADDR(frame_lp + addr_ret, + sign_ext_8_64(*(int8 *)maddr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD8_U) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + PUT_I64_TO_ADDR(frame_lp + addr_ret, (uint64)(*(uint8 *)maddr)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD16_S) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + PUT_I64_TO_ADDR(frame_lp + addr_ret, + sign_ext_16_64(LOAD_I16(maddr))); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD16_U) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + PUT_I64_TO_ADDR(frame_lp + addr_ret, (uint64)(LOAD_U16(maddr))); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD32_S) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + PUT_I64_TO_ADDR(frame_lp + addr_ret, + sign_ext_32_64(LOAD_I32(maddr))); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LOAD32_U) + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, I32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + PUT_I64_TO_ADDR(frame_lp + addr_ret, (uint64)(LOAD_U32(maddr))); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_STORE) + { + uint32 offset, addr; + uint32 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint32, I32, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, sval); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_STORE8) + { + uint32 offset, addr; + uint32 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint32, I32, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(1); + STORE_U8(maddr, (uint8_t)sval); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_STORE16) + { + uint32 offset, addr; + uint32 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint32, I32, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_STORE) + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, I64, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(8); + STORE_I64(maddr, sval); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_STORE8) + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, I64, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(1); + *(uint8 *)maddr = (uint8)sval; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_STORE16) + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, I64, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_STORE32) + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, I64, 0); + addr = GET_OPERAND(uint32, I32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, (uint32)sval); + HANDLE_OP_END(); + } + + /* memory size and memory grow instructions */ + HANDLE_OP(WASM_OP_MEMORY_SIZE) + { + uint32 reserved; + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = memory->cur_page_count; + (void)reserved; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_MEMORY_GROW) + { + uint32 reserved, delta, + prev_page_count = memory->cur_page_count; + + addr1 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + delta = (uint32)frame_lp[addr1]; + + /* TODO: multi-memory wasm_enlarge_memory_with_idx() */ + if (!wasm_enlarge_memory(module, delta)) { + /* failed to memory.grow, return -1 */ + frame_lp[addr_ret] = -1; + } + else { + /* success, return previous page count */ + frame_lp[addr_ret] = prev_page_count; + /* update memory size, no need to update memory ptr as + it isn't changed in wasm_enlarge_memory */ +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY != 0 + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); +#endif + } + + (void)reserved; + HANDLE_OP_END(); + } + + /* constant instructions */ + HANDLE_OP(WASM_OP_F64_CONST) + HANDLE_OP(WASM_OP_I64_CONST) + { + uint8 *orig_ip = frame_ip; + + frame_ip += sizeof(uint64); + addr_ret = GET_OFFSET(); + + bh_memcpy_s(frame_lp + addr_ret, sizeof(uint64), orig_ip, + sizeof(uint64)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONST) + HANDLE_OP(WASM_OP_I32_CONST) + { + uint8 *orig_ip = frame_ip; + + frame_ip += sizeof(uint32); + addr_ret = GET_OFFSET(); + + bh_memcpy_s(frame_lp + addr_ret, sizeof(uint32), orig_ip, + sizeof(uint32)); + HANDLE_OP_END(); + } + + /* comparison instructions of i32 */ + HANDLE_OP(WASM_OP_I32_EQZ) + { + DEF_OP_EQZ(int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_EQ) + { + DEF_OP_CMP(uint32, I32, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_NE) + { + DEF_OP_CMP(uint32, I32, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LT_S) + { + DEF_OP_CMP(int32, I32, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LT_U) + { + DEF_OP_CMP(uint32, I32, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GT_S) + { + DEF_OP_CMP(int32, I32, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GT_U) + { + DEF_OP_CMP(uint32, I32, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LE_S) + { + DEF_OP_CMP(int32, I32, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_LE_U) + { + DEF_OP_CMP(uint32, I32, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GE_S) + { + DEF_OP_CMP(int32, I32, >=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_GE_U) + { + DEF_OP_CMP(uint32, I32, >=); + HANDLE_OP_END(); + } + + /* comparison instructions of i64 */ + HANDLE_OP(WASM_OP_I64_EQZ) + { + DEF_OP_EQZ(int64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EQ) + { + DEF_OP_CMP(uint64, I64, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_NE) + { + DEF_OP_CMP(uint64, I64, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LT_S) + { + DEF_OP_CMP(int64, I64, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LT_U) + { + DEF_OP_CMP(uint64, I64, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GT_S) + { + DEF_OP_CMP(int64, I64, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GT_U) + { + DEF_OP_CMP(uint64, I64, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LE_S) + { + DEF_OP_CMP(int64, I64, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_LE_U) + { + DEF_OP_CMP(uint64, I64, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GE_S) + { + DEF_OP_CMP(int64, I64, >=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_GE_U) + { + DEF_OP_CMP(uint64, I64, >=); + HANDLE_OP_END(); + } + + /* comparison instructions of f32 */ + HANDLE_OP(WASM_OP_F32_EQ) + { + DEF_OP_CMP(float32, F32, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_NE) + { + DEF_OP_CMP(float32, F32, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_LT) + { + DEF_OP_CMP(float32, F32, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_GT) + { + DEF_OP_CMP(float32, F32, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_LE) + { + DEF_OP_CMP(float32, F32, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_GE) + { + DEF_OP_CMP(float32, F32, >=); + HANDLE_OP_END(); + } + + /* comparison instructions of f64 */ + HANDLE_OP(WASM_OP_F64_EQ) + { + DEF_OP_CMP(float64, F64, ==); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_NE) + { + DEF_OP_CMP(float64, F64, !=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_LT) + { + DEF_OP_CMP(float64, F64, <); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_GT) + { + DEF_OP_CMP(float64, F64, >); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_LE) + { + DEF_OP_CMP(float64, F64, <=); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_GE) + { + DEF_OP_CMP(float64, F64, >=); + HANDLE_OP_END(); + } + + /* numeric instructions of i32 */ + HANDLE_OP(WASM_OP_I32_CLZ) + { + DEF_OP_BIT_COUNT(uint32, I32, clz32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_CTZ) + { + DEF_OP_BIT_COUNT(uint32, I32, ctz32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_POPCNT) + { + DEF_OP_BIT_COUNT(uint32, I32, popcount32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_ADD) + { + DEF_OP_NUMERIC(uint32, uint32, I32, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SUB) + { + DEF_OP_NUMERIC(uint32, uint32, I32, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_MUL) + { + DEF_OP_NUMERIC(uint32, uint32, I32, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_DIV_S) + { + int32 a, b; + + b = frame_lp[GET_OFFSET()]; + a = frame_lp[GET_OFFSET()]; + addr_ret = GET_OFFSET(); + if (a == (int32)0x80000000 && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_DIV_U) + { + uint32 a, b; + + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + b = (uint32)frame_lp[addr1]; + a = (uint32)frame_lp[addr2]; + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_REM_S) + { + int32 a, b; + + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + b = frame_lp[addr1]; + a = frame_lp[addr2]; + if (a == (int32)0x80000000 && b == -1) { + frame_lp[addr_ret] = 0; + HANDLE_OP_END(); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_REM_U) + { + uint32 a, b; + + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + b = (uint32)frame_lp[addr1]; + a = (uint32)frame_lp[addr2]; + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_AND) + { + DEF_OP_NUMERIC(uint32, uint32, I32, &); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_OR) + { + DEF_OP_NUMERIC(uint32, uint32, I32, |); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_XOR) + { + DEF_OP_NUMERIC(uint32, uint32, I32, ^); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SHL) + { + DEF_OP_NUMERIC2(uint32, uint32, I32, <<); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SHR_S) + { + DEF_OP_NUMERIC2(int32, uint32, I32, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_SHR_U) + { + DEF_OP_NUMERIC2(uint32, uint32, I32, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_ROTL) + { + uint32 a, b; + + b = (uint32)frame_lp[GET_OFFSET()]; + a = (uint32)frame_lp[GET_OFFSET()]; + frame_lp[GET_OFFSET()] = rotl32(a, b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_ROTR) + { + uint32 a, b; + + b = (uint32)frame_lp[GET_OFFSET()]; + a = (uint32)frame_lp[GET_OFFSET()]; + frame_lp[GET_OFFSET()] = rotr32(a, b); + HANDLE_OP_END(); + } + + /* numeric instructions of i64 */ + HANDLE_OP(WASM_OP_I64_CLZ) + { + DEF_OP_BIT_COUNT(uint64, I64, clz64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_CTZ) + { + DEF_OP_BIT_COUNT(uint64, I64, ctz64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_POPCNT) + { + DEF_OP_BIT_COUNT(uint64, I64, popcount64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_ADD) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SUB) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_MUL) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_DIV_S) + { + int64 a, b; + + b = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + a = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + if (a == (int64)0x8000000000000000LL && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_DIV_U) + { + uint64 a, b; + + b = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + a = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), a / b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_REM_S) + { + int64 a, b; + + b = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + a = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + if (a == (int64)0x8000000000000000LL && b == -1) { + *(int64 *)(frame_lp + GET_OFFSET()) = 0; + HANDLE_OP_END(); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_REM_U) + { + uint64 a, b; + + b = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + a = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), a % b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_AND) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, &); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_OR) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, |); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_XOR) + { + DEF_OP_NUMERIC_64(uint64, uint64, I64, ^); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SHL) + { + DEF_OP_NUMERIC2_64(uint64, uint64, I64, <<); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SHR_S) + { + DEF_OP_NUMERIC2_64(int64, uint64, I64, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_SHR_U) + { + DEF_OP_NUMERIC2_64(uint64, uint64, I64, >>); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_ROTL) + { + uint64 a, b; + + b = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + a = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), rotl64(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_ROTR) + { + uint64 a, b; + + b = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + a = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), rotr64(a, b)); + HANDLE_OP_END(); + } + + /* numeric instructions of f32 */ + HANDLE_OP(WASM_OP_F32_ABS) + { + DEF_OP_MATH(float32, F32, fabsf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_NEG) + { + uint32 u32 = frame_lp[GET_OFFSET()]; + uint32 sign_bit = u32 & ((uint32)1 << 31); + addr_ret = GET_OFFSET(); + if (sign_bit) + frame_lp[addr_ret] = u32 & ~((uint32)1 << 31); + else + frame_lp[addr_ret] = u32 | ((uint32)1 << 31); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CEIL) + { + DEF_OP_MATH(float32, F32, ceilf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_FLOOR) + { + DEF_OP_MATH(float32, F32, floorf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_TRUNC) + { + DEF_OP_MATH(float32, F32, truncf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_NEAREST) + { + DEF_OP_MATH(float32, F32, rintf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_SQRT) + { + DEF_OP_MATH(float32, F32, sqrtf); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_ADD) + { + DEF_OP_NUMERIC(float32, float32, F32, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_SUB) + { + DEF_OP_NUMERIC(float32, float32, F32, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_MUL) + { + DEF_OP_NUMERIC(float32, float32, F32, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_DIV) + { + DEF_OP_NUMERIC(float32, float32, F32, /); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_MIN) + { + float32 a, b; + + b = *(float32 *)(frame_lp + GET_OFFSET()); + a = *(float32 *)(frame_lp + GET_OFFSET()); + + *(float32 *)(frame_lp + GET_OFFSET()) = f32_min(a, b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_MAX) + { + float32 a, b; + + b = *(float32 *)(frame_lp + GET_OFFSET()); + a = *(float32 *)(frame_lp + GET_OFFSET()); + + *(float32 *)(frame_lp + GET_OFFSET()) = f32_max(a, b); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_COPYSIGN) + { + float32 a, b; + + b = *(float32 *)(frame_lp + GET_OFFSET()); + a = *(float32 *)(frame_lp + GET_OFFSET()); + *(float32 *)(frame_lp + GET_OFFSET()) = local_copysignf(a, b); + HANDLE_OP_END(); + } + + /* numeric instructions of f64 */ + HANDLE_OP(WASM_OP_F64_ABS) + { + DEF_OP_MATH(float64, F64, fabs); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_NEG) + { + uint64 u64 = GET_I64_FROM_ADDR(frame_lp + GET_OFFSET()); + uint64 sign_bit = u64 & (((uint64)1) << 63); + if (sign_bit) + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), + (u64 & ~(((uint64)1) << 63))); + else + PUT_I64_TO_ADDR(frame_lp + GET_OFFSET(), + (u64 | (((uint64)1) << 63))); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CEIL) + { + DEF_OP_MATH(float64, F64, ceil); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_FLOOR) + { + DEF_OP_MATH(float64, F64, floor); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_TRUNC) + { + DEF_OP_MATH(float64, F64, trunc); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_NEAREST) + { + DEF_OP_MATH(float64, F64, rint); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_SQRT) + { + DEF_OP_MATH(float64, F64, sqrt); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_ADD) + { + DEF_OP_NUMERIC_64(float64, float64, F64, +); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_SUB) + { + DEF_OP_NUMERIC_64(float64, float64, F64, -); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_MUL) + { + DEF_OP_NUMERIC_64(float64, float64, F64, *); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_DIV) + { + DEF_OP_NUMERIC_64(float64, float64, F64, /); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_MIN) + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + PUSH_F64(f64_min(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_MAX) + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + PUSH_F64(f64_max(a, b)); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_COPYSIGN) + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + PUSH_F64(local_copysign(a, b)); + HANDLE_OP_END(); + } + + /* conversions of i32 */ + HANDLE_OP(WASM_OP_I32_WRAP_I64) + { + int32 value = (int32)(POP_I64() & 0xFFFFFFFFLL); + PUSH_I32(value); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_S_F32) + { + /* We don't use INT32_MIN/INT32_MAX/UINT32_MIN/UINT32_MAX, + since float/double values of ieee754 cannot precisely + represent all int32/uint32/int64/uint64 values, e.g.: + UINT32_MAX is 4294967295, but (float32)4294967295 is + 4294967296.0f, but not 4294967295.0f. */ + DEF_OP_TRUNC_F32(-2147483904.0f, 2147483648.0f, true, true); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_U_F32) + { + DEF_OP_TRUNC_F32(-1.0f, 4294967296.0f, true, false); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_S_F64) + { + DEF_OP_TRUNC_F64(-2147483649.0, 2147483648.0, true, true); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_TRUNC_U_F64) + { + DEF_OP_TRUNC_F64(-1.0, 4294967296.0, true, false); + HANDLE_OP_END(); + } + + /* conversions of i64 */ + HANDLE_OP(WASM_OP_I64_EXTEND_S_I32) + { + DEF_OP_CONVERT(int64, I64, int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND_U_I32) + { + DEF_OP_CONVERT(int64, I64, uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_S_F32) + { + DEF_OP_TRUNC_F32(-9223373136366403584.0f, + 9223372036854775808.0f, false, true); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_U_F32) + { + DEF_OP_TRUNC_F32(-1.0f, 18446744073709551616.0f, false, false); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_S_F64) + { + DEF_OP_TRUNC_F64(-9223372036854777856.0, 9223372036854775808.0, + false, true); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_TRUNC_U_F64) + { + DEF_OP_TRUNC_F64(-1.0, 18446744073709551616.0, false, false); + HANDLE_OP_END(); + } + + /* conversions of f32 */ + HANDLE_OP(WASM_OP_F32_CONVERT_S_I32) + { + DEF_OP_CONVERT(float32, F32, int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONVERT_U_I32) + { + DEF_OP_CONVERT(float32, F32, uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONVERT_S_I64) + { + DEF_OP_CONVERT(float32, F32, int64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_CONVERT_U_I64) + { + DEF_OP_CONVERT(float32, F32, uint64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F32_DEMOTE_F64) + { + DEF_OP_CONVERT(float32, F32, float64, F64); + HANDLE_OP_END(); + } + + /* conversions of f64 */ + HANDLE_OP(WASM_OP_F64_CONVERT_S_I32) + { + DEF_OP_CONVERT(float64, F64, int32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONVERT_U_I32) + { + DEF_OP_CONVERT(float64, F64, uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONVERT_S_I64) + { + DEF_OP_CONVERT(float64, F64, int64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_CONVERT_U_I64) + { + DEF_OP_CONVERT(float64, F64, uint64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_F64_PROMOTE_F32) + { + DEF_OP_CONVERT(float64, F64, float32, F32); + HANDLE_OP_END(); + } + + /* reinterpretations */ + HANDLE_OP(WASM_OP_I32_REINTERPRET_F32) + HANDLE_OP(WASM_OP_F32_REINTERPRET_I32) + { + DEF_OP_REINTERPRET(uint32, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_REINTERPRET_F64) + HANDLE_OP(WASM_OP_F64_REINTERPRET_I64) + { + DEF_OP_REINTERPRET(int64, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_COPY_STACK_TOP) + { + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + frame_lp[addr2] = frame_lp[addr1]; + +#if WASM_ENABLE_GC != 0 + /* Ignore constants because they are not reference */ + if (addr1 >= 0) { + if (*FRAME_REF(addr1)) { + CLEAR_FRAME_REF(addr1); + SET_FRAME_REF(addr2); + } + } +#endif + + HANDLE_OP_END(); + } + + HANDLE_OP(EXT_OP_COPY_STACK_TOP_I64) + { + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + + PUT_I64_TO_ADDR(frame_lp + addr2, + GET_I64_FROM_ADDR(frame_lp + addr1)); + +#if WASM_ENABLE_GC != 0 + /* Ignore constants because they are not reference */ + if (addr1 >= 0) { + if (*FRAME_REF(addr1)) { + CLEAR_FRAME_REF(addr1); + SET_FRAME_REF(addr2); + } + } +#endif + + HANDLE_OP_END(); + } +#if WASM_ENABLE_SIMDE != 0 + HANDLE_OP(EXT_OP_COPY_STACK_TOP_V128) + { + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + + PUT_V128_TO_ADDR(frame_lp + addr2, + GET_V128_FROM_ADDR(frame_lp + addr1)); + +#if WASM_ENABLE_GC != 0 + /* Ignore constants because they are not reference */ + if (addr1 >= 0) { + if (*FRAME_REF(addr1)) { + CLEAR_FRAME_REF(addr1); + SET_FRAME_REF(addr2); + } + } +#endif + + HANDLE_OP_END(); + } +#endif + + HANDLE_OP(EXT_OP_COPY_STACK_VALUES) + { + uint32 values_count, total_cell; + uint8 *cells; + int16 *src_offsets = NULL; + uint16 *dst_offsets = NULL; + + /* read values_count */ + values_count = read_uint32(frame_ip); + /* read total cell num */ + total_cell = read_uint32(frame_ip); + /* cells */ + cells = (uint8 *)frame_ip; + frame_ip += values_count * CELL_SIZE; + /* src offsets */ + src_offsets = (int16 *)frame_ip; + frame_ip += values_count * sizeof(int16); + /* dst offsets */ + dst_offsets = (uint16 *)frame_ip; + frame_ip += values_count * sizeof(uint16); + + if (!copy_stack_values(module, frame_lp, values_count, +#if WASM_ENABLE_GC != 0 + frame_ref, +#endif + total_cell, cells, src_offsets, + dst_offsets)) + goto got_exception; + + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_SET_LOCAL) + { + opcode = WASM_OP_SET_LOCAL; + goto handle_op_set_tee_local; + } + HANDLE_OP(WASM_OP_TEE_LOCAL) + { + opcode = WASM_OP_TEE_LOCAL; + handle_op_set_tee_local: + + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + addr1 = GET_OFFSET(); + + if (local_type == VALUE_TYPE_I32 || local_type == VALUE_TYPE_F32 +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + || local_type == VALUE_TYPE_FUNCREF + || local_type == VALUE_TYPE_EXTERNREF +#endif + ) { + *(int32 *)(frame_lp + local_offset) = frame_lp[addr1]; + } + else if (local_type == VALUE_TYPE_I64 + || local_type == VALUE_TYPE_F64) { + PUT_I64_TO_ADDR((uint32 *)(frame_lp + local_offset), + GET_I64_FROM_ADDR(frame_lp + addr1)); + } + else if (local_type == VALUE_TYPE_V128) { + PUT_V128_TO_ADDR((frame_lp + local_offset), + GET_V128_FROM_ADDR(frame_lp + addr1)); + } +#if WASM_ENABLE_GC != 0 + else if (wasm_is_type_reftype(local_type)) { + PUT_REF_TO_ADDR((uint32 *)(frame_lp + local_offset), + GET_REF_FROM_ADDR(frame_lp + addr1)); + if (opcode == WASM_OP_SET_LOCAL) { + CLEAR_FRAME_REF(addr1); + } + } +#endif + else { + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_EXTEND8_S) + { + DEF_OP_CONVERT(int32, I32, int8, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I32_EXTEND16_S) + { + DEF_OP_CONVERT(int32, I32, int16, I32); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND8_S) + { + DEF_OP_CONVERT(int64, I64, int8, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND16_S) + { + DEF_OP_CONVERT(int64, I64, int16, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_I64_EXTEND32_S) + { + DEF_OP_CONVERT(int64, I64, int32, I64); + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_MISC_PREFIX) + { + GET_OPCODE(); + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-2147483904.0f, 2147483648.0f, + true, true); + break; + case WASM_OP_I32_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 4294967296.0f, true, false); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-2147483649.0, 2147483648.0, true, + true); + break; + case WASM_OP_I32_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 4294967296.0, true, false); + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-9223373136366403584.0f, + 9223372036854775808.0f, false, + true); + break; + case WASM_OP_I64_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 18446744073709551616.0f, + false, false); + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-9223372036854777856.0, + 9223372036854775808.0, false, + true); + break; + case WASM_OP_I64_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 18446744073709551616.0, + false, false); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 addr, segment; + uint64 bytes, offset, seg_len; + uint8 *data; + + segment = read_uint32(frame_ip); + + bytes = (uint64)(uint32)POP_I32(); + offset = (uint64)(uint32)POP_I32(); + addr = POP_I32(); + +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr); +#else +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)(uint32)addr, + bytes)) + shared_heap_addr_app_to_native((uint64)(uint32)addr, + maddr); + else +#endif + { + if ((uint64)(uint32)addr + bytes > linear_mem_size) + goto out_of_bounds; + maddr = memory->memory_data + (uint32)addr; + } +#endif + if (bh_bitmap_get_bit(module->e->common.data_dropped, + segment)) { + seg_len = 0; + data = NULL; + } + else { + seg_len = + (uint64)module->module->data_segments[segment] + ->data_length; + data = module->module->data_segments[segment]->data; + } + if (offset + bytes > seg_len) + goto out_of_bounds; + + bh_memcpy_s(maddr, (uint32)(linear_mem_size - addr), + data + offset, (uint32)bytes); + break; + } + case WASM_OP_DATA_DROP: + { + uint32 segment; + + segment = read_uint32(frame_ip); + bh_bitmap_set_bit(module->e->common.data_dropped, + segment); + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + { + uint32 dst, src, len; + uint8 *mdst, *msrc; + + len = POP_I32(); + src = POP_I32(); + dst = POP_I32(); + +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); +#else /* else of OS_ENABLE_HW_BOUND_CHECK */ +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)src, len)) + shared_heap_addr_app_to_native((uint64)src, msrc); + else +#endif + { + if ((uint64)(uint32)src + len > linear_mem_size) + goto out_of_bounds; + msrc = memory->memory_data + (uint32)src; + } + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)dst, len)) { + shared_heap_addr_app_to_native((uint64)dst, mdst); + } + else +#endif + { + if ((uint64)(uint32)dst + len > linear_mem_size) + goto out_of_bounds; + mdst = memory->memory_data + (uint32)dst; + } +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + + /* + * avoid unnecessary operations + * + * since dst and src both are valid indexes in the + * linear memory, mdst and msrc can't be NULL + * + * The spec. converts memory.copy into i32.load8 and + * i32.store8; the following are runtime-specific + * optimizations. + * + */ + if (len && mdst != msrc) { + /* allowing the destination and source to overlap */ + memmove(mdst, msrc, len); + } + break; + } + case WASM_OP_MEMORY_FILL: + { + uint32 dst, len; + uint8 fill_val, *mdst; + + len = POP_I32(); + fill_val = POP_I32(); + dst = POP_I32(); + +#if WASM_ENABLE_THREAD_MGR != 0 + linear_mem_size = get_linear_mem_size(); +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); +#else +#if WASM_ENABLE_SHARED_HEAP != 0 + if (app_addr_in_shared_heap((uint64)(uint32)dst, len)) + shared_heap_addr_app_to_native((uint64)(uint32)dst, + mdst); + else +#endif + { + if ((uint64)(uint32)dst + len > linear_mem_size) + goto out_of_bounds; + mdst = memory->memory_data + (uint32)dst; + } +#endif + + memset(mdst, fill_val, len); + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx, elem_idx; + uint32 n, s, d; + WASMTableInstance *tbl_inst; + table_elem_type_t *table_elems; + InitializerExpression *tbl_seg_init_values = NULL, + *init_values; + uint64 i; + uint32 tbl_seg_len = 0; + + elem_idx = read_uint32(frame_ip); + bh_assert(elem_idx < module->module->table_seg_count); + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (uint32)POP_I32(); + + if (!bh_bitmap_get_bit(module->e->common.elem_dropped, + elem_idx)) { + /* table segment isn't dropped */ + tbl_seg_init_values = + module->module->table_segments[elem_idx] + .init_values; + tbl_seg_len = + module->module->table_segments[elem_idx] + .value_count; + } + + if (offset_len_out_of_bounds(s, n, tbl_seg_len) + || offset_len_out_of_bounds(d, n, + tbl_inst->cur_size)) { + wasm_set_exception(module, + "out of bounds table access"); + goto got_exception; + } + + if (!n) { + break; + } + + table_elems = tbl_inst->elems + d; + init_values = tbl_seg_init_values + s; +#if WASM_ENABLE_GC != 0 + SYNC_ALL_TO_FRAME(); +#endif + for (i = 0; i < n; i++) { + /* UINT32_MAX indicates that it is a null ref */ + bh_assert(init_values[i].init_expr_type + == INIT_EXPR_TYPE_REFNULL_CONST + || init_values[i].init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST); +#if WASM_ENABLE_GC == 0 + table_elems[i] = (table_elem_type_t)init_values[i] + .u.unary.v.ref_index; +#else + if (init_values[i].u.unary.v.ref_index + != UINT32_MAX) { + if (!(func_obj = wasm_create_func_obj( + module, + init_values[i].u.unary.v.ref_index, + true, NULL, 0))) { + goto got_exception; + } + table_elems[i] = func_obj; + } + else { + table_elems[i] = NULL_REF; + } +#endif + } + + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 elem_idx = read_uint32(frame_ip); + bh_assert(elem_idx < module->module->table_seg_count); + bh_bitmap_set_bit(module->e->common.elem_dropped, + elem_idx); + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + uint32 n, s, d; + WASMTableInstance *src_tbl_inst, *dst_tbl_inst; + + dst_tbl_idx = read_uint32(frame_ip); + bh_assert(dst_tbl_idx < module->table_count); + + dst_tbl_inst = wasm_get_table_inst(module, dst_tbl_idx); + + src_tbl_idx = read_uint32(frame_ip); + bh_assert(src_tbl_idx < module->table_count); + + src_tbl_inst = wasm_get_table_inst(module, src_tbl_idx); + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (uint32)POP_I32(); + + if (offset_len_out_of_bounds(d, n, + dst_tbl_inst->cur_size) + || offset_len_out_of_bounds( + s, n, src_tbl_inst->cur_size)) { + wasm_set_exception(module, + "out of bounds table access"); + goto got_exception; + } + + /* if s >= d, copy from front to back */ + /* if s < d, copy from back to front */ + /* merge all together */ + bh_memmove_s((uint8 *)dst_tbl_inst + + offsetof(WASMTableInstance, elems) + + d * sizeof(table_elem_type_t), + (uint32)((dst_tbl_inst->cur_size - d) + * sizeof(table_elem_type_t)), + (uint8 *)src_tbl_inst + + offsetof(WASMTableInstance, elems) + + s * sizeof(table_elem_type_t), + (uint32)(n * sizeof(table_elem_type_t))); + break; + } + case WASM_OP_TABLE_GROW: + { + uint32 tbl_idx, n, orig_tbl_sz; + WASMTableInstance *tbl_inst; + table_elem_type_t init_val; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + orig_tbl_sz = tbl_inst->cur_size; + + n = POP_I32(); +#if WASM_ENABLE_GC == 0 + init_val = POP_I32(); +#else + init_val = POP_REF(); +#endif + + if (!wasm_enlarge_table(module, tbl_idx, n, init_val)) { + PUSH_I32(-1); + } + else { + PUSH_I32(orig_tbl_sz); + } + + break; + } + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + PUSH_I32(tbl_inst->cur_size); + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx, n, i; + WASMTableInstance *tbl_inst; + table_elem_type_t fill_val; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + n = POP_I32(); +#if WASM_ENABLE_GC == 0 + fill_val = POP_I32(); +#else + fill_val = POP_REF(); +#endif + i = POP_I32(); + + if (offset_len_out_of_bounds(i, n, + tbl_inst->cur_size)) { + wasm_set_exception(module, + "out of bounds table access"); + goto got_exception; + } + + for (; n != 0; i++, n--) { + tbl_inst->elems[i] = fill_val; + } + + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } + HANDLE_OP_END(); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + HANDLE_OP(WASM_OP_ATOMIC_PREFIX) + { + uint32 offset = 0, addr; + + GET_OPCODE(); + + if (opcode != WASM_OP_ATOMIC_FENCE) { + offset = read_uint32(frame_ip); + } + + switch (opcode) { + case WASM_OP_ATOMIC_NOTIFY: + { + uint32 notify_count, ret; + + notify_count = POP_I32(); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + ret = wasm_runtime_atomic_notify( + (WASMModuleInstanceCommon *)module, maddr, + notify_count); + if (ret == (uint32)-1) + goto got_exception; + + PUSH_I32(ret); + break; + } + case WASM_OP_ATOMIC_WAIT32: + { + uint64 timeout; + uint32 expect, ret; + + timeout = POP_I64(); + expect = POP_I32(); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + ret = wasm_runtime_atomic_wait( + (WASMModuleInstanceCommon *)module, maddr, + (uint64)expect, timeout, false); + 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_WAIT64: + { + uint64 timeout, expect; + uint32 ret; + + timeout = POP_I64(); + expect = POP_I64(); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(8); + + ret = wasm_runtime_atomic_wait( + (WASMModuleInstanceCommon *)module, maddr, expect, + timeout, true); + 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_seq_cst); + break; + } + + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + { + uint32 readv; + + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I32_LOAD8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(1); + shared_memory_lock(memory); + readv = (uint32)(*(uint8 *)maddr); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I32_LOAD16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(2); + shared_memory_lock(memory); + readv = (uint32)LOAD_U16(maddr); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + shared_memory_lock(memory); + readv = LOAD_I32(maddr); + shared_memory_unlock(memory); + } + + PUSH_I32(readv); + break; + } + + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + { + uint64 readv; + + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I64_LOAD8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(1); + shared_memory_lock(memory); + readv = (uint64)(*(uint8 *)maddr); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(2); + shared_memory_lock(memory); + readv = (uint64)LOAD_U16(maddr); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD32_U) { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + shared_memory_lock(memory); + readv = (uint64)LOAD_U32(maddr); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(8); + shared_memory_lock(memory); + readv = LOAD_I64(maddr); + shared_memory_unlock(memory); + } + + PUSH_I64(readv); + break; + } + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + { + uint32 sval; + + sval = (uint32)POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I32_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(1); + shared_memory_lock(memory); + *(uint8 *)maddr = (uint8)sval; + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I32_STORE16) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(2); + shared_memory_lock(memory); + STORE_U16(maddr, (uint16)sval); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + shared_memory_lock(memory); + STORE_U32(maddr, sval); + shared_memory_unlock(memory); + } + break; + } + + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + { + uint64 sval; + + sval = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I64_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(1); + shared_memory_lock(memory); + *(uint8 *)maddr = (uint8)sval; + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_STORE16) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(2); + shared_memory_lock(memory); + STORE_U16(maddr, (uint16)sval); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_I64_STORE32) { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + shared_memory_lock(memory); + STORE_U32(maddr, (uint32)sval); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(8); + shared_memory_lock(memory); + STORE_I64(maddr, sval); + shared_memory_unlock(memory); + } + break; + } + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + { + uint32 readv, sval, expect; + + sval = POP_I32(); + expect = POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(1); + + expect = (uint8)expect; + shared_memory_lock(memory); + readv = (uint32)(*(uint8 *)maddr); + if (readv == expect) + *(uint8 *)maddr = (uint8)(sval); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(2); + + expect = (uint16)expect; + shared_memory_lock(memory); + readv = (uint32)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + shared_memory_lock(memory); + readv = LOAD_I32(maddr); + if (readv == expect) + STORE_U32(maddr, sval); + shared_memory_unlock(memory); + } + PUSH_I32(readv); + break; + } + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + { + uint64 readv, sval, expect; + + sval = (uint64)POP_I64(); + expect = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U) { + CHECK_MEMORY_OVERFLOW(1); + CHECK_ATOMIC_MEMORY_ACCESS(1); + + expect = (uint8)expect; + shared_memory_lock(memory); + readv = (uint64)(*(uint8 *)maddr); + if (readv == expect) + *(uint8 *)maddr = (uint8)(sval); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U) { + CHECK_MEMORY_OVERFLOW(2); + CHECK_ATOMIC_MEMORY_ACCESS(2); + + expect = (uint16)expect; + shared_memory_lock(memory); + readv = (uint64)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + shared_memory_unlock(memory); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U) { + CHECK_MEMORY_OVERFLOW(4); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + expect = (uint32)expect; + shared_memory_lock(memory); + readv = (uint64)LOAD_U32(maddr); + if (readv == expect) + STORE_U32(maddr, (uint32)(sval)); + shared_memory_unlock(memory); + } + else { + CHECK_MEMORY_OVERFLOW(8); + CHECK_ATOMIC_MEMORY_ACCESS(8); + + shared_memory_lock(memory); + readv = (uint64)LOAD_I64(maddr); + if (readv == expect) + STORE_I64(maddr, sval); + shared_memory_unlock(memory); + } + PUSH_I64(readv); + break; + } + + DEF_ATOMIC_RMW_OPCODE(ADD, +); + DEF_ATOMIC_RMW_OPCODE(SUB, -); + DEF_ATOMIC_RMW_OPCODE(AND, &); + DEF_ATOMIC_RMW_OPCODE(OR, |); + DEF_ATOMIC_RMW_OPCODE(XOR, ^); + /* xchg, ignore the read value, and store the given + value: readv * 0 + sval */ + DEF_ATOMIC_RMW_OPCODE(XCHG, *0 +); + } + + HANDLE_OP_END(); + } +#endif + + HANDLE_OP(WASM_OP_IMPDEP) + { + frame = prev_frame; + frame_ip = frame->ip; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif + goto call_func_from_entry; + } +#if WASM_ENABLE_SIMDE != 0 +#define SIMD_V128_TO_SIMDE_V128(s_v) \ + ({ \ + bh_assert(sizeof(V128) == sizeof(simde_v128_t)); \ + simde_v128_t se_v; \ + bh_memcpy_s(&se_v, sizeof(simde_v128_t), &(s_v), sizeof(V128)); \ + se_v; \ + }) + +#define SIMDE_V128_TO_SIMD_V128(sv, v) \ + do { \ + bh_assert(sizeof(V128) == sizeof(simde_v128_t)); \ + bh_memcpy_s(&(v), sizeof(V128), &(sv), sizeof(simde_v128_t)); \ + } while (0) + + HANDLE_OP(WASM_OP_SIMD_PREFIX) + { + GET_OPCODE(); + + switch (opcode) { + /* Memory */ + case SIMD_v128_load: + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = POP_I32(); + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(16); + PUT_V128_TO_ADDR(frame_lp + addr_ret, LOAD_V128(maddr)); + break; + } +#define SIMD_LOAD_OP(simde_func) \ + do { \ + uint32 offset, addr; \ + offset = read_uint32(frame_ip); \ + addr = POP_I32(); \ + addr_ret = GET_OFFSET(); \ + CHECK_MEMORY_OVERFLOW(8); \ + \ + simde_v128_t simde_result = simde_func(maddr); \ + \ + V128 result; \ + SIMDE_V128_TO_SIMD_V128(simde_result, result); \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + \ + } while (0) + case SIMD_v128_load8x8_s: + { + SIMD_LOAD_OP(simde_wasm_i16x8_load8x8); + break; + } + case SIMD_v128_load8x8_u: + { + SIMD_LOAD_OP(simde_wasm_u16x8_load8x8); + break; + } + case SIMD_v128_load16x4_s: + { + SIMD_LOAD_OP(simde_wasm_i32x4_load16x4); + break; + } + case SIMD_v128_load16x4_u: + { + SIMD_LOAD_OP(simde_wasm_u32x4_load16x4); + break; + } + case SIMD_v128_load32x2_s: + { + SIMD_LOAD_OP(simde_wasm_i64x2_load32x2); + break; + } + case SIMD_v128_load32x2_u: + { + SIMD_LOAD_OP(simde_wasm_u64x2_load32x2); + break; + } +#define SIMD_LOAD_SPLAT_OP(simde_func, width) \ + do { \ + uint32 offset, addr; \ + offset = read_uint32(frame_ip); \ + addr = POP_I32(); \ + addr_ret = GET_OFFSET(); \ + CHECK_MEMORY_OVERFLOW(width / 8); \ + \ + simde_v128_t simde_result = simde_func(maddr); \ + \ + V128 result; \ + SIMDE_V128_TO_SIMD_V128(simde_result, result); \ + \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + } while (0) + + case SIMD_v128_load8_splat: + { + SIMD_LOAD_SPLAT_OP(simde_wasm_v128_load8_splat, 8); + break; + } + case SIMD_v128_load16_splat: + { + SIMD_LOAD_SPLAT_OP(simde_wasm_v128_load16_splat, 16); + break; + } + case SIMD_v128_load32_splat: + { + SIMD_LOAD_SPLAT_OP(simde_wasm_v128_load32_splat, 32); + break; + } + case SIMD_v128_load64_splat: + { + SIMD_LOAD_SPLAT_OP(simde_wasm_v128_load64_splat, 64); + break; + } + case SIMD_v128_store: + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + V128 data = POP_V128(); + addr = POP_I32(); + + CHECK_MEMORY_OVERFLOW(16); + STORE_V128(maddr, data); + break; + } + + /* Basic */ + case SIMD_v128_const: + { + uint8 *orig_ip = frame_ip; + + frame_ip += sizeof(V128); + addr_ret = GET_OFFSET(); + + PUT_V128_TO_ADDR(frame_lp + addr_ret, *(V128 *)orig_ip); + break; + } + /* TODO: Add a faster SIMD implementation */ + case SIMD_v8x16_shuffle: + { + V128 indices; + bh_memcpy_s(&indices, sizeof(V128), frame_ip, + sizeof(V128)); + frame_ip += sizeof(V128); + + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + addr_ret = GET_OFFSET(); + + V128 result; + for (int i = 0; i < 16; i++) { + uint8_t index = indices.i8x16[i]; + if (index < 16) { + result.i8x16[i] = v1.i8x16[index]; + } + else { + result.i8x16[i] = v2.i8x16[index - 16]; + } + } + + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); + break; + } + case SIMD_v8x16_swizzle: + { + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + addr_ret = GET_OFFSET(); + simde_v128_t simde_result = simde_wasm_i8x16_swizzle( + SIMD_V128_TO_SIMDE_V128(v1), + SIMD_V128_TO_SIMDE_V128(v2)); + + V128 result; + SIMDE_V128_TO_SIMD_V128(simde_result, result); + + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); + break; + } + + /* Splat */ +#define SIMD_SPLAT_OP(simde_func, pop_func, val_type) \ + do { \ + val_type v = pop_func(); \ + addr_ret = GET_OFFSET(); \ + \ + simde_v128_t simde_result = simde_func(v); \ + \ + V128 result; \ + SIMDE_V128_TO_SIMD_V128(simde_result, result); \ + \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + } while (0) + +#define SIMD_SPLAT_OP_I32(simde_func) SIMD_SPLAT_OP(simde_func, POP_I32, uint32) +#define SIMD_SPLAT_OP_I64(simde_func) SIMD_SPLAT_OP(simde_func, POP_I64, uint64) +#define SIMD_SPLAT_OP_F32(simde_func) \ + SIMD_SPLAT_OP(simde_func, POP_F32, float32) +#define SIMD_SPLAT_OP_F64(simde_func) \ + SIMD_SPLAT_OP(simde_func, POP_F64, float64) + + case SIMD_i8x16_splat: + { + val = POP_I32(); + addr_ret = GET_OFFSET(); + + simde_v128_t simde_result = simde_wasm_i8x16_splat(val); + + V128 result; + SIMDE_V128_TO_SIMD_V128(simde_result, result); + + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); + break; + } + case SIMD_i16x8_splat: + { + SIMD_SPLAT_OP_I32(simde_wasm_i16x8_splat); + break; + } + case SIMD_i32x4_splat: + { + SIMD_SPLAT_OP_I32(simde_wasm_i32x4_splat); + break; + } + case SIMD_i64x2_splat: + { + SIMD_SPLAT_OP_I64(simde_wasm_i64x2_splat); + break; + } + case SIMD_f32x4_splat: + { + SIMD_SPLAT_OP_F32(simde_wasm_f32x4_splat); + break; + } + case SIMD_f64x2_splat: + { + SIMD_SPLAT_OP_F64(simde_wasm_f64x2_splat); + break; + } +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define SIMD_LANE_HANDLE_UNALIGNED_ACCESS() +#else +#define SIMD_LANE_HANDLE_UNALIGNED_ACCESS() (void)*frame_ip++ +#endif /* WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 */ + +#define SIMD_EXTRACT_LANE_OP(register, return_type, push_elem) \ + do { \ + uint8 lane = *frame_ip++; \ + SIMD_LANE_HANDLE_UNALIGNED_ACCESS(); \ + V128 v = POP_V128(); \ + push_elem((return_type)(v.register[lane])); \ + } while (0) +#define SIMD_REPLACE_LANE_OP(register, return_type, pop_elem) \ + do { \ + uint8 lane = *frame_ip++; \ + SIMD_LANE_HANDLE_UNALIGNED_ACCESS(); \ + return_type replacement = pop_elem(); \ + V128 v = POP_V128(); \ + v.register[lane] = replacement; \ + addr_ret = GET_OFFSET(); \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, v); \ + } while (0) + case SIMD_i8x16_extract_lane_s: + { + SIMD_EXTRACT_LANE_OP(i8x16, int8, PUSH_I32); + break; + } + case SIMD_i8x16_extract_lane_u: + { + SIMD_EXTRACT_LANE_OP(i8x16, uint8, PUSH_I32); + break; + } + case SIMD_i8x16_replace_lane: + { + SIMD_REPLACE_LANE_OP(i8x16, int8, POP_I32); + break; + } + case SIMD_i16x8_extract_lane_s: + { + SIMD_EXTRACT_LANE_OP(i16x8, int16, PUSH_I32); + break; + } + case SIMD_i16x8_extract_lane_u: + { + SIMD_EXTRACT_LANE_OP(i16x8, uint16, PUSH_I32); + break; + } + case SIMD_i16x8_replace_lane: + { + SIMD_REPLACE_LANE_OP(i16x8, int16, POP_I32); + break; + } + case SIMD_i32x4_extract_lane: + { + SIMD_EXTRACT_LANE_OP(i32x4, int32, PUSH_I32); + break; + } + case SIMD_i32x4_replace_lane: + { + SIMD_REPLACE_LANE_OP(i32x4, int32, POP_I32); + break; + } + case SIMD_i64x2_extract_lane: + { + SIMD_EXTRACT_LANE_OP(i64x2, int64, PUSH_I64); + break; + } + case SIMD_i64x2_replace_lane: + { + SIMD_REPLACE_LANE_OP(i64x2, int64, POP_I64); + break; + } + case SIMD_f32x4_extract_lane: + { + SIMD_EXTRACT_LANE_OP(f32x4, float32, PUSH_F32); + break; + } + case SIMD_f32x4_replace_lane: + { + SIMD_REPLACE_LANE_OP(f32x4, float32, POP_F32); + break; + } + case SIMD_f64x2_extract_lane: + { + SIMD_EXTRACT_LANE_OP(f64x2, float64, PUSH_F64); + break; + } + case SIMD_f64x2_replace_lane: + { + SIMD_REPLACE_LANE_OP(f64x2, float64, POP_F64); + break; + } + +#define SIMD_DOUBLE_OP(simde_func) \ + do { \ + V128 v2 = POP_V128(); \ + V128 v1 = POP_V128(); \ + addr_ret = GET_OFFSET(); \ + \ + simde_v128_t simde_result = simde_func(SIMD_V128_TO_SIMDE_V128(v1), \ + SIMD_V128_TO_SIMDE_V128(v2)); \ + \ + V128 result; \ + SIMDE_V128_TO_SIMD_V128(simde_result, result); \ + \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + } while (0) + + /* i8x16 comparison operations */ + case SIMD_i8x16_eq: + { + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + addr_ret = GET_OFFSET(); + + simde_v128_t simde_result = + simde_wasm_i8x16_eq(SIMD_V128_TO_SIMDE_V128(v1), + SIMD_V128_TO_SIMDE_V128(v2)); + + V128 result; + SIMDE_V128_TO_SIMD_V128(simde_result, result); + + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); + break; + } + case SIMD_i8x16_ne: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_ne); + break; + } + case SIMD_i8x16_lt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_lt); + break; + } + case SIMD_i8x16_lt_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_lt); + break; + } + case SIMD_i8x16_gt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_gt); + break; + } + case SIMD_i8x16_gt_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_gt); + break; + } + case SIMD_i8x16_le_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_le); + break; + } + case SIMD_i8x16_le_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_le); + break; + } + case SIMD_i8x16_ge_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_ge); + break; + } + case SIMD_i8x16_ge_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_ge); + break; + } + + /* i16x8 comparison operations */ + case SIMD_i16x8_eq: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_eq); + break; + } + case SIMD_i16x8_ne: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_ne); + break; + } + case SIMD_i16x8_lt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_lt); + break; + } + case SIMD_i16x8_lt_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_lt); + break; + } + case SIMD_i16x8_gt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_gt); + break; + } + case SIMD_i16x8_gt_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_gt); + break; + } + case SIMD_i16x8_le_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_le); + break; + } + case SIMD_i16x8_le_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_le); + break; + } + case SIMD_i16x8_ge_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_ge); + break; + } + case SIMD_i16x8_ge_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_ge); + break; + } + + /* i32x4 comparison operations */ + case SIMD_i32x4_eq: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_eq); + break; + } + case SIMD_i32x4_ne: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_ne); + break; + } + case SIMD_i32x4_lt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_lt); + break; + } + case SIMD_i32x4_lt_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_lt); + break; + } + case SIMD_i32x4_gt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_gt); + break; + } + case SIMD_i32x4_gt_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_gt); + break; + } + case SIMD_i32x4_le_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_le); + break; + } + case SIMD_i32x4_le_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_le); + break; + } + case SIMD_i32x4_ge_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_ge); + break; + } + case SIMD_i32x4_ge_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_ge); + break; + } + + /* f32x4 comparison operations */ + case SIMD_f32x4_eq: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_eq); + break; + } + case SIMD_f32x4_ne: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_ne); + break; + } + case SIMD_f32x4_lt: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_lt); + break; + } + case SIMD_f32x4_gt: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_gt); + break; + } + case SIMD_f32x4_le: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_le); + break; + } + case SIMD_f32x4_ge: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_ge); + break; + } + + /* f64x2 comparison operations */ + case SIMD_f64x2_eq: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_eq); + break; + } + case SIMD_f64x2_ne: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_ne); + break; + } + case SIMD_f64x2_lt: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_lt); + break; + } + case SIMD_f64x2_gt: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_gt); + break; + } + case SIMD_f64x2_le: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_le); + break; + } + case SIMD_f64x2_ge: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_ge); + break; + } + + /* v128 bitwise operations */ +#define SIMD_V128_BITWISE_OP_COMMON(result_expr_0, result_expr_1) \ + do { \ + V128 result; \ + result.i64x2[0] = (result_expr_0); \ + result.i64x2[1] = (result_expr_1); \ + addr_ret = GET_OFFSET(); \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + } while (0) + + case SIMD_v128_not: + { + V128 value = POP_V128(); + SIMD_V128_BITWISE_OP_COMMON(~value.i64x2[0], + ~value.i64x2[1]); + break; + } + case SIMD_v128_and: + { + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + SIMD_V128_BITWISE_OP_COMMON(v1.i64x2[0] & v2.i64x2[0], + v1.i64x2[1] & v2.i64x2[1]); + break; + } + case SIMD_v128_andnot: + { + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + SIMD_V128_BITWISE_OP_COMMON( + v1.i64x2[0] & (~v2.i64x2[0]), + v1.i64x2[1] & (~v2.i64x2[1])); + break; + } + case SIMD_v128_or: + { + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + SIMD_V128_BITWISE_OP_COMMON(v1.i64x2[0] | v2.i64x2[0], + v1.i64x2[1] | v2.i64x2[1]); + break; + } + case SIMD_v128_xor: + { + V128 v2 = POP_V128(); + V128 v1 = POP_V128(); + SIMD_V128_BITWISE_OP_COMMON(v1.i64x2[0] ^ v2.i64x2[0], + v1.i64x2[1] ^ v2.i64x2[1]); + break; + } + case SIMD_v128_bitselect: + { + V128 v1 = POP_V128(); + V128 v2 = POP_V128(); + V128 v3 = POP_V128(); + addr_ret = GET_OFFSET(); + + simde_v128_t simde_result = simde_wasm_v128_bitselect( + SIMD_V128_TO_SIMDE_V128(v3), + SIMD_V128_TO_SIMDE_V128(v2), + SIMD_V128_TO_SIMDE_V128(v1)); + + V128 result; + SIMDE_V128_TO_SIMD_V128(simde_result, result); + + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); + break; + } + case SIMD_v128_any_true: + { + V128 value = POP_V128(); + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = + value.i64x2[0] != 0 || value.i64x2[1] != 0; + break; + } + +#define SIMD_LOAD_LANE_COMMON(vec, register, lane, width) \ + do { \ + addr_ret = GET_OFFSET(); \ + CHECK_MEMORY_OVERFLOW(width / 8); \ + if (width == 64) { \ + vec.register[lane] = GET_I64_FROM_ADDR((uint32 *)maddr); \ + } \ + else { \ + vec.register[lane] = *(uint##width *)(maddr); \ + } \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, vec); \ + } while (0) + +#define SIMD_LOAD_LANE_OP(register, width) \ + do { \ + uint32 offset, addr; \ + offset = read_uint32(frame_ip); \ + V128 vec = POP_V128(); \ + addr = POP_I32(); \ + int lane = *frame_ip++; \ + SIMD_LANE_HANDLE_UNALIGNED_ACCESS(); \ + SIMD_LOAD_LANE_COMMON(vec, register, lane, width); \ + } while (0) + + case SIMD_v128_load8_lane: + { + SIMD_LOAD_LANE_OP(i8x16, 8); + break; + } + case SIMD_v128_load16_lane: + { + SIMD_LOAD_LANE_OP(i16x8, 16); + break; + } + case SIMD_v128_load32_lane: + { + SIMD_LOAD_LANE_OP(i32x4, 32); + break; + } + case SIMD_v128_load64_lane: + { + SIMD_LOAD_LANE_OP(i64x2, 64); + break; + } +#define SIMD_STORE_LANE_OP(register, width) \ + do { \ + uint32 offset, addr; \ + offset = read_uint32(frame_ip); \ + V128 vec = POP_V128(); \ + addr = POP_I32(); \ + int lane = *frame_ip++; \ + SIMD_LANE_HANDLE_UNALIGNED_ACCESS(); \ + CHECK_MEMORY_OVERFLOW(width / 8); \ + if (width == 64) { \ + STORE_I64(maddr, vec.register[lane]); \ + } \ + else { \ + *(uint##width *)(maddr) = vec.register[lane]; \ + } \ + } while (0) + + case SIMD_v128_store8_lane: + { + SIMD_STORE_LANE_OP(i8x16, 8); + break; + } + + case SIMD_v128_store16_lane: + { + SIMD_STORE_LANE_OP(i16x8, 16); + break; + } + + case SIMD_v128_store32_lane: + { + SIMD_STORE_LANE_OP(i32x4, 32); + break; + } + + case SIMD_v128_store64_lane: + { + SIMD_STORE_LANE_OP(i64x2, 64); + break; + } +#define SIMD_LOAD_ZERO_OP(register, width) \ + do { \ + uint32 offset, addr; \ + offset = read_uint32(frame_ip); \ + addr = POP_I32(); \ + int32 lane = 0; \ + V128 vec = { 0 }; \ + SIMD_LOAD_LANE_COMMON(vec, register, lane, width); \ + } while (0) + + case SIMD_v128_load32_zero: + { + SIMD_LOAD_ZERO_OP(i32x4, 32); + break; + } + case SIMD_v128_load64_zero: + { + SIMD_LOAD_ZERO_OP(i64x2, 64); + break; + } + +#define SIMD_SINGLE_OP(simde_func) \ + do { \ + V128 v1 = POP_V128(); \ + addr_ret = GET_OFFSET(); \ + \ + simde_v128_t simde_result = simde_func(SIMD_V128_TO_SIMDE_V128(v1)); \ + \ + V128 result; \ + SIMDE_V128_TO_SIMD_V128(simde_result, result); \ + \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + } while (0) + + /* Float conversion */ + case SIMD_f32x4_demote_f64x2_zero: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_demote_f64x2_zero); + break; + } + case SIMD_f64x2_promote_low_f32x4_zero: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_promote_low_f32x4); + break; + } + + /* i8x16 operations */ + case SIMD_i8x16_abs: + { + SIMD_SINGLE_OP(simde_wasm_i8x16_abs); + break; + } + case SIMD_i8x16_neg: + { + SIMD_SINGLE_OP(simde_wasm_i8x16_neg); + break; + } + case SIMD_i8x16_popcnt: + { + SIMD_SINGLE_OP(simde_wasm_i8x16_popcnt); + break; + } + case SIMD_i8x16_all_true: + { + V128 v1 = POP_V128(); + + bool result = simde_wasm_i8x16_all_true( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + + case SIMD_i8x16_bitmask: + { + V128 v1 = POP_V128(); + + uint32_t result = simde_wasm_i8x16_bitmask( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i8x16_narrow_i16x8_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_narrow_i16x8); + break; + } + case SIMD_i8x16_narrow_i16x8_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_narrow_i16x8); + break; + } + case SIMD_f32x4_ceil: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_ceil); + break; + } + case SIMD_f32x4_floor: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_floor); + break; + } + case SIMD_f32x4_trunc: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_trunc); + break; + } + case SIMD_f32x4_nearest: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_nearest); + break; + } +#define SIMD_LANE_SHIFT(simde_func) \ + do { \ + int32 c = POP_I32(); \ + V128 v1 = POP_V128(); \ + addr_ret = GET_OFFSET(); \ + \ + simde_v128_t simde_result = \ + simde_func(SIMD_V128_TO_SIMDE_V128(v1), c); \ + \ + V128 result; \ + SIMDE_V128_TO_SIMD_V128(simde_result, result); \ + \ + PUT_V128_TO_ADDR(frame_lp + addr_ret, result); \ + } while (0) + case SIMD_i8x16_shl: + { + SIMD_LANE_SHIFT(simde_wasm_i8x16_shl); + break; + } + case SIMD_i8x16_shr_s: + { + SIMD_LANE_SHIFT(simde_wasm_i8x16_shr); + break; + } + case SIMD_i8x16_shr_u: + { + SIMD_LANE_SHIFT(simde_wasm_u8x16_shr); + break; + } + case SIMD_i8x16_add: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_add); + break; + } + case SIMD_i8x16_add_sat_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_add_sat); + break; + } + case SIMD_i8x16_add_sat_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_add_sat); + break; + } + case SIMD_i8x16_sub: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_sub); + break; + } + case SIMD_i8x16_sub_sat_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_sub_sat); + break; + } + case SIMD_i8x16_sub_sat_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_sub_sat); + break; + } + case SIMD_f64x2_ceil: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_ceil); + break; + } + case SIMD_f64x2_floor: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_floor); + break; + } + case SIMD_i8x16_min_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_min); + break; + } + case SIMD_i8x16_min_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_min); + break; + } + case SIMD_i8x16_max_s: + { + SIMD_DOUBLE_OP(simde_wasm_i8x16_max); + break; + } + case SIMD_i8x16_max_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_max); + break; + } + case SIMD_f64x2_trunc: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_trunc); + break; + } + case SIMD_i8x16_avgr_u: + { + SIMD_DOUBLE_OP(simde_wasm_u8x16_avgr); + break; + } + case SIMD_i16x8_extadd_pairwise_i8x16_s: + { + SIMD_SINGLE_OP(simde_wasm_i16x8_extadd_pairwise_i8x16); + break; + } + case SIMD_i16x8_extadd_pairwise_i8x16_u: + { + SIMD_SINGLE_OP(simde_wasm_u16x8_extadd_pairwise_u8x16); + break; + } + case SIMD_i32x4_extadd_pairwise_i16x8_s: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_extadd_pairwise_i16x8); + break; + } + case SIMD_i32x4_extadd_pairwise_i16x8_u: + { + SIMD_SINGLE_OP(simde_wasm_u32x4_extadd_pairwise_u16x8); + break; + } + + /* i16x8 operations */ + case SIMD_i16x8_abs: + { + SIMD_SINGLE_OP(simde_wasm_i16x8_abs); + break; + } + case SIMD_i16x8_neg: + { + SIMD_SINGLE_OP(simde_wasm_i16x8_neg); + break; + } + case SIMD_i16x8_q15mulr_sat_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_q15mulr_sat); + break; + } + case SIMD_i16x8_all_true: + { + V128 v1 = POP_V128(); + + bool result = simde_wasm_i16x8_all_true( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i16x8_bitmask: + { + V128 v1 = POP_V128(); + + uint32_t result = simde_wasm_i16x8_bitmask( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i16x8_narrow_i32x4_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_narrow_i32x4); + break; + } + case SIMD_i16x8_narrow_i32x4_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_narrow_i32x4); + break; + } + case SIMD_i16x8_extend_low_i8x16_s: + { + SIMD_SINGLE_OP(simde_wasm_i16x8_extend_low_i8x16); + break; + } + case SIMD_i16x8_extend_high_i8x16_s: + { + SIMD_SINGLE_OP(simde_wasm_i16x8_extend_high_i8x16); + break; + } + case SIMD_i16x8_extend_low_i8x16_u: + { + SIMD_SINGLE_OP(simde_wasm_u16x8_extend_low_u8x16); + break; + } + case SIMD_i16x8_extend_high_i8x16_u: + { + SIMD_SINGLE_OP(simde_wasm_u16x8_extend_high_u8x16); + break; + } + case SIMD_i16x8_shl: + { + SIMD_LANE_SHIFT(simde_wasm_i16x8_shl); + break; + } + case SIMD_i16x8_shr_s: + { + SIMD_LANE_SHIFT(simde_wasm_i16x8_shr); + break; + } + case SIMD_i16x8_shr_u: + { + SIMD_LANE_SHIFT(simde_wasm_u16x8_shr); + break; + } + case SIMD_i16x8_add: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_add); + break; + } + case SIMD_i16x8_add_sat_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_add_sat); + break; + } + case SIMD_i16x8_add_sat_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_add_sat); + break; + } + case SIMD_i16x8_sub: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_sub); + break; + } + case SIMD_i16x8_sub_sat_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_sub_sat); + break; + } + case SIMD_i16x8_sub_sat_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_sub_sat); + break; + } + case SIMD_f64x2_nearest: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_nearest); + break; + } + case SIMD_i16x8_mul: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_mul); + break; + } + case SIMD_i16x8_min_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_min); + break; + } + case SIMD_i16x8_min_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_min); + break; + } + case SIMD_i16x8_max_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_max); + break; + } + case SIMD_i16x8_max_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_max); + break; + } + case SIMD_i16x8_avgr_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_avgr); + break; + } + case SIMD_i16x8_extmul_low_i8x16_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_extmul_low_i8x16); + break; + } + case SIMD_i16x8_extmul_high_i8x16_s: + { + SIMD_DOUBLE_OP(simde_wasm_i16x8_extmul_high_i8x16); + break; + } + case SIMD_i16x8_extmul_low_i8x16_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_extmul_low_u8x16); + break; + } + case SIMD_i16x8_extmul_high_i8x16_u: + { + SIMD_DOUBLE_OP(simde_wasm_u16x8_extmul_high_u8x16); + break; + } + + /* i32x4 operations */ + case SIMD_i32x4_abs: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_abs); + break; + } + case SIMD_i32x4_neg: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_neg); + break; + } + case SIMD_i32x4_all_true: + { + V128 v1 = POP_V128(); + + bool result = simde_wasm_i32x4_all_true( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i32x4_bitmask: + { + V128 v1 = POP_V128(); + + uint32_t result = simde_wasm_i32x4_bitmask( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i32x4_extend_low_i16x8_s: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_extend_low_i16x8); + break; + } + case SIMD_i32x4_extend_high_i16x8_s: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_extend_high_i16x8); + break; + } + case SIMD_i32x4_extend_low_i16x8_u: + { + SIMD_SINGLE_OP(simde_wasm_u32x4_extend_low_u16x8); + break; + } + case SIMD_i32x4_extend_high_i16x8_u: + { + SIMD_SINGLE_OP(simde_wasm_u32x4_extend_high_u16x8); + break; + } + case SIMD_i32x4_shl: + { + SIMD_LANE_SHIFT(simde_wasm_i32x4_shl); + break; + } + case SIMD_i32x4_shr_s: + { + SIMD_LANE_SHIFT(simde_wasm_i32x4_shr); + break; + } + case SIMD_i32x4_shr_u: + { + SIMD_LANE_SHIFT(simde_wasm_u32x4_shr); + break; + } + case SIMD_i32x4_add: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_add); + break; + } + case SIMD_i32x4_sub: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_sub); + break; + } + case SIMD_i32x4_mul: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_mul); + break; + } + case SIMD_i32x4_min_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_min); + break; + } + case SIMD_i32x4_min_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_min); + break; + } + case SIMD_i32x4_max_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_max); + break; + } + case SIMD_i32x4_max_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_max); + break; + } + case SIMD_i32x4_dot_i16x8_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_dot_i16x8); + break; + } + case SIMD_i32x4_extmul_low_i16x8_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_extmul_low_i16x8); + break; + } + case SIMD_i32x4_extmul_high_i16x8_s: + { + SIMD_DOUBLE_OP(simde_wasm_i32x4_extmul_high_i16x8); + break; + } + case SIMD_i32x4_extmul_low_i16x8_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_extmul_low_u16x8); + break; + } + case SIMD_i32x4_extmul_high_i16x8_u: + { + SIMD_DOUBLE_OP(simde_wasm_u32x4_extmul_high_u16x8); + break; + } + + /* i64x2 operations */ + case SIMD_i64x2_abs: + { + SIMD_SINGLE_OP(simde_wasm_i64x2_abs); + break; + } + case SIMD_i64x2_neg: + { + SIMD_SINGLE_OP(simde_wasm_i64x2_neg); + break; + } + case SIMD_i64x2_all_true: + { + V128 v1 = POP_V128(); + + bool result = simde_wasm_i64x2_all_true( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i64x2_bitmask: + { + V128 v1 = POP_V128(); + + uint32_t result = simde_wasm_i64x2_bitmask( + SIMD_V128_TO_SIMDE_V128(v1)); + + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = result; + break; + } + case SIMD_i64x2_extend_low_i32x4_s: + { + SIMD_SINGLE_OP(simde_wasm_i64x2_extend_low_i32x4); + break; + } + case SIMD_i64x2_extend_high_i32x4_s: + { + SIMD_SINGLE_OP(simde_wasm_i64x2_extend_high_i32x4); + break; + } + case SIMD_i64x2_extend_low_i32x4_u: + { + SIMD_SINGLE_OP(simde_wasm_u64x2_extend_low_u32x4); + break; + } + case SIMD_i64x2_extend_high_i32x4_u: + { + SIMD_SINGLE_OP(simde_wasm_u64x2_extend_high_u32x4); + break; + } + case SIMD_i64x2_shl: + { + SIMD_LANE_SHIFT(simde_wasm_i64x2_shl); + break; + } + case SIMD_i64x2_shr_s: + { + SIMD_LANE_SHIFT(simde_wasm_i64x2_shr); + break; + } + case SIMD_i64x2_shr_u: + { + SIMD_LANE_SHIFT(simde_wasm_u64x2_shr); + break; + } + case SIMD_i64x2_add: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_add); + break; + } + case SIMD_i64x2_sub: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_sub); + break; + } + case SIMD_i64x2_mul: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_mul); + break; + } + case SIMD_i64x2_eq: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_eq); + break; + } + case SIMD_i64x2_ne: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_ne); + break; + } + case SIMD_i64x2_lt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_lt); + break; + } + case SIMD_i64x2_gt_s: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_gt); + break; + } + case SIMD_i64x2_le_s: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_le); + break; + } + case SIMD_i64x2_ge_s: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_ge); + break; + } + case SIMD_i64x2_extmul_low_i32x4_s: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_extmul_low_i32x4); + break; + } + case SIMD_i64x2_extmul_high_i32x4_s: + { + SIMD_DOUBLE_OP(simde_wasm_i64x2_extmul_high_i32x4); + break; + } + case SIMD_i64x2_extmul_low_i32x4_u: + { + SIMD_DOUBLE_OP(simde_wasm_u64x2_extmul_low_u32x4); + break; + } + case SIMD_i64x2_extmul_high_i32x4_u: + { + SIMD_DOUBLE_OP(simde_wasm_u64x2_extmul_high_u32x4); + break; + } + + /* f32x4 opertions */ + case SIMD_f32x4_abs: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_abs); + break; + } + case SIMD_f32x4_neg: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_neg); + break; + } + case SIMD_f32x4_sqrt: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_sqrt); + break; + } + case SIMD_f32x4_add: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_add); + break; + } + case SIMD_f32x4_sub: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_sub); + break; + } + case SIMD_f32x4_mul: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_mul); + break; + } + case SIMD_f32x4_div: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_div); + break; + } + case SIMD_f32x4_min: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_min); + break; + } + case SIMD_f32x4_max: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_max); + break; + } + case SIMD_f32x4_pmin: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_pmin); + break; + } + case SIMD_f32x4_pmax: + { + SIMD_DOUBLE_OP(simde_wasm_f32x4_pmax); + break; + } + + /* f64x2 operations */ + case SIMD_f64x2_abs: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_abs); + break; + } + case SIMD_f64x2_neg: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_neg); + break; + } + case SIMD_f64x2_sqrt: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_sqrt); + break; + } + case SIMD_f64x2_add: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_add); + break; + } + case SIMD_f64x2_sub: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_sub); + break; + } + case SIMD_f64x2_mul: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_mul); + break; + } + case SIMD_f64x2_div: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_div); + break; + } + case SIMD_f64x2_min: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_min); + break; + } + case SIMD_f64x2_max: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_max); + break; + } + case SIMD_f64x2_pmin: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_pmin); + break; + } + case SIMD_f64x2_pmax: + { + SIMD_DOUBLE_OP(simde_wasm_f64x2_pmax); + break; + } + + /* Conversion operations */ + case SIMD_i32x4_trunc_sat_f32x4_s: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_trunc_sat_f32x4); + break; + } + case SIMD_i32x4_trunc_sat_f32x4_u: + { + SIMD_SINGLE_OP(simde_wasm_u32x4_trunc_sat_f32x4); + break; + } + case SIMD_f32x4_convert_i32x4_s: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_convert_i32x4); + break; + } + case SIMD_f32x4_convert_i32x4_u: + { + SIMD_SINGLE_OP(simde_wasm_f32x4_convert_u32x4); + break; + } + case SIMD_i32x4_trunc_sat_f64x2_s_zero: + { + SIMD_SINGLE_OP(simde_wasm_i32x4_trunc_sat_f64x2_zero); + break; + } + case SIMD_i32x4_trunc_sat_f64x2_u_zero: + { + SIMD_SINGLE_OP(simde_wasm_u32x4_trunc_sat_f64x2_zero); + break; + } + case SIMD_f64x2_convert_low_i32x4_s: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_convert_low_i32x4); + break; + } + case SIMD_f64x2_convert_low_i32x4_u: + { + SIMD_SINGLE_OP(simde_wasm_f64x2_convert_low_u32x4); + break; + } + + default: + wasm_set_exception(module, "unsupported SIMD opcode"); + } + HANDLE_OP_END(); + } +#endif + + HANDLE_OP(WASM_OP_CALL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + fidx = read_uint32(frame_ip); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->e->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + cur_func = module->e->functions + fidx; + goto call_func_from_interp; + } + +#if WASM_ENABLE_TAIL_CALL != 0 + HANDLE_OP(WASM_OP_RETURN_CALL) + { +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + fidx = read_uint32(frame_ip); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->e->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + cur_func = module->e->functions + fidx; + goto call_func_from_return_call; + } +#endif /* WASM_ENABLE_TAIL_CALL */ + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + HANDLE_OP(WASM_OP_UNUSED_0x0a) +#if WASM_ENABLE_TAIL_CALL == 0 + HANDLE_OP(WASM_OP_RETURN_CALL) + HANDLE_OP(WASM_OP_RETURN_CALL_INDIRECT) +#endif +#if WASM_ENABLE_SHARED_MEMORY == 0 + HANDLE_OP(WASM_OP_ATOMIC_PREFIX) +#endif +#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0 + HANDLE_OP(WASM_OP_TABLE_GET) + HANDLE_OP(WASM_OP_TABLE_SET) + HANDLE_OP(WASM_OP_REF_NULL) + HANDLE_OP(WASM_OP_REF_IS_NULL) + HANDLE_OP(WASM_OP_REF_FUNC) +#endif +#if WASM_ENABLE_GC == 0 + /* SELECT_T is converted to SELECT or SELECT_64 */ + HANDLE_OP(WASM_OP_SELECT_T) + HANDLE_OP(WASM_OP_CALL_REF) + HANDLE_OP(WASM_OP_RETURN_CALL_REF) + HANDLE_OP(WASM_OP_REF_EQ) + HANDLE_OP(WASM_OP_REF_AS_NON_NULL) + HANDLE_OP(WASM_OP_BR_ON_NULL) + HANDLE_OP(WASM_OP_BR_ON_NON_NULL) + HANDLE_OP(WASM_OP_GC_PREFIX) +#endif +#if WASM_ENABLE_EXCE_HANDLING == 0 + /* if exception handling is disabled, these opcodes issue a trap */ + HANDLE_OP(WASM_OP_TRY) + HANDLE_OP(WASM_OP_CATCH) + HANDLE_OP(WASM_OP_THROW) + HANDLE_OP(WASM_OP_RETHROW) + HANDLE_OP(WASM_OP_DELEGATE) + HANDLE_OP(WASM_OP_CATCH_ALL) + HANDLE_OP(EXT_OP_TRY) +#endif + HANDLE_OP(WASM_OP_UNUSED_0x16) + HANDLE_OP(WASM_OP_UNUSED_0x17) + HANDLE_OP(WASM_OP_UNUSED_0x27) + /* optimized op code */ + HANDLE_OP(WASM_OP_F32_STORE) + HANDLE_OP(WASM_OP_F64_STORE) + HANDLE_OP(WASM_OP_F32_LOAD) + HANDLE_OP(WASM_OP_F64_LOAD) + HANDLE_OP(EXT_OP_GET_LOCAL_FAST) + HANDLE_OP(WASM_OP_GET_LOCAL) + HANDLE_OP(WASM_OP_DROP) + HANDLE_OP(WASM_OP_DROP_64) +#if WASM_ENABLE_EXCE_HANDLING != 0 + HANDLE_OP(WASM_OP_END) + { + /* Block / loop / if / function-level `end` is stripped from + * the IR at load time (skip_label in the END case of + * wasm_loader_prepare_bytecode). Only try-region `end`s + * survive — the loader keeps them so the runtime can pop + * the matching eh-stack entry here when control falls + * through the bottom of a catch body (or runs the body of + * a catchless `try ... end`). + * + * Cost: one decrement on a cold path. CALL / LOAD / STORE + * are untouched. */ + bh_assert(frame->eh_count > 0); + frame->eh_count--; + HANDLE_OP_END(); + } + + HANDLE_OP(WASM_OP_BLOCK) + HANDLE_OP(WASM_OP_LOOP) +#else + HANDLE_OP(WASM_OP_BLOCK) + HANDLE_OP(WASM_OP_LOOP) + HANDLE_OP(WASM_OP_END) +#endif + HANDLE_OP(WASM_OP_NOP) + HANDLE_OP(EXT_OP_BLOCK) + HANDLE_OP(EXT_OP_LOOP) + HANDLE_OP(EXT_OP_IF) + HANDLE_OP(EXT_OP_BR_TABLE_CACHE) +#if WASM_ENABLE_SIMDE == 0 + HANDLE_OP(WASM_OP_SIMD_PREFIX) +#endif + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + continue; +#else + FETCH_OPCODE_AND_DISPATCH(); +#endif + +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + call_func_from_return_call: + { + uint32 *lp_base = NULL, *lp = NULL; + int i; + + if (cur_func->param_cell_num > 0 + && !(lp_base = lp = wasm_runtime_malloc(cur_func->param_cell_num + * sizeof(uint32)))) { + wasm_set_exception(module, "allocate memory failed"); + goto got_exception; + } + for (i = 0; i < cur_func->param_count; i++) { + if (cur_func->param_types[i] == VALUE_TYPE_I64 + || cur_func->param_types[i] == VALUE_TYPE_F64) { + PUT_I64_TO_ADDR( + lp, GET_OPERAND(uint64, I64, + 2 * (cur_func->param_count - i - 1))); + lp += 2; + } + else { + *lp = GET_OPERAND(uint32, I32, + (2 * (cur_func->param_count - i - 1))); + lp++; + } + } + frame->lp = frame->operand + cur_func->const_cell_num; + if ((uint8 *)(frame->lp + cur_func->param_cell_num) + > exec_env->wasm_stack.top_boundary) { + if (lp_base) + wasm_runtime_free(lp_base); + wasm_set_exception(module, "wasm operand stack overflow"); + goto got_exception; + } + if (lp - lp_base > 0) { + word_copy(frame->lp, lp_base, lp - lp_base); + } + if (lp_base) + wasm_runtime_free(lp_base); + FREE_FRAME(exec_env, frame); + frame_ip += cur_func->param_count * sizeof(int16); + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame); + is_return_call = true; + goto call_func_from_entry; + } +#endif /* WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 */ + + call_func_from_interp: + { + /* Only do the copy when it's called from interpreter. */ + WASMInterpFrame *outs_area = wasm_exec_env_wasm_stack_top(exec_env); + int i; + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->is_import_func) { + outs_area->lp = outs_area->operand + + (cur_func->import_func_inst + ? cur_func->import_func_inst->const_cell_num + : 0); + } + else +#endif + { + outs_area->lp = outs_area->operand + cur_func->const_cell_num; + } + + if ((uint8 *)(outs_area->lp + cur_func->param_cell_num) + > exec_env->wasm_stack.top_boundary) { + wasm_set_exception(module, "wasm operand stack overflow"); + goto got_exception; + } + + for (i = 0; i < cur_func->param_count; i++) { + if (cur_func->param_types[i] == VALUE_TYPE_V128) { + PUT_V128_TO_ADDR( + outs_area->lp, + GET_OPERAND_V128(2 * (cur_func->param_count - i - 1))); + outs_area->lp += 4; + } + else if (cur_func->param_types[i] == VALUE_TYPE_I64 + || cur_func->param_types[i] == VALUE_TYPE_F64) { + PUT_I64_TO_ADDR( + outs_area->lp, + GET_OPERAND(uint64, I64, + 2 * (cur_func->param_count - i - 1))); + outs_area->lp += 2; + } +#if WASM_ENABLE_GC != 0 + else if (wasm_is_type_reftype(cur_func->param_types[i])) { + PUT_REF_TO_ADDR( + outs_area->lp, + GET_OPERAND(void *, REF, + 2 * (cur_func->param_count - i - 1))); + CLEAR_FRAME_REF( + *(uint16 *)(frame_ip + + (2 * (cur_func->param_count - i - 1)))); + outs_area->lp += REF_CELL_NUM; + } +#endif + else { + *outs_area->lp = GET_OPERAND( + uint32, I32, (2 * (cur_func->param_count - i - 1))); + outs_area->lp++; + } + } + frame_ip += cur_func->param_count * sizeof(int16); + if (cur_func->ret_cell_num != 0) { + /* Get the first return value's offset. Since loader emit + * all return values' offset so we must skip remain return + * values' offsets. + */ + WASMFuncType *func_type; + if (cur_func->is_import_func) + func_type = cur_func->u.func_import->func_type; + else + func_type = cur_func->u.func->func_type; + frame->ret_offset = GET_OFFSET(); + frame_ip += 2 * (func_type->result_count - 1); + } + SYNC_ALL_TO_FRAME(); + prev_frame = frame; +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + is_return_call = false; +#endif + } + + call_func_from_entry: + { + if (cur_func->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->import_func_inst) { + wasm_interp_call_func_import(module, exec_env, cur_func, + prev_frame); + } + else +#endif + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); + } + +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (is_return_call) { + /* the frame was freed before tail calling and + the prev_frame was set as exec_env's cur_frame, + so here we recover context from prev_frame */ + RECOVER_CONTEXT(prev_frame); + } + else +#endif + { + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + } + + /* update memory size, no need to update memory ptr as + it isn't changed in wasm_enlarge_memory */ +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY != 0 + if (memory) + linear_mem_size = GET_LINEAR_MEMORY_SIZE(memory); +#endif + if (wasm_copy_exception(module, NULL)) + goto got_exception; + } + else { + WASMFunction *cur_wasm_func = cur_func->u.func; + uint32 cell_num_of_local_stack; +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + uint32 i, local_cell_idx; +#endif + + cell_num_of_local_stack = cur_func->param_cell_num + + cur_func->local_cell_num + + cur_wasm_func->max_stack_cell_num; + all_cell_num = cur_func->const_cell_num + cell_num_of_local_stack; +#if WASM_ENABLE_GC != 0 + /* area of frame_ref */ + all_cell_num += (cell_num_of_local_stack + 3) / 4; + /* cells occupied by locals, POP_REF should not clear frame_ref for + * these cells */ + local_cell_num = + cur_func->param_cell_num + cur_func->local_cell_num; +#endif +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* EH_ENTRY_CELLS cells per try-region in the function, + * appended past the value stack — cell 0 holds the + * packed eh_idx | state_bit, cell 1 holds the caught tag + * for RETHROW. Functions without try blocks pay zero + * cells. Mirrors classic-interp's eh_size accounting at + * wasm_interp_classic.c:6786 (which also stores per- + * handler pointers on the value stack). */ + all_cell_num += + cur_wasm_func->exception_handler_count * EH_ENTRY_CELLS; +#endif + /* param_cell_num, local_cell_num, const_cell_num and + max_stack_cell_num are all no larger than UINT16_MAX (checked + in loader), all_cell_num must be smaller than 1MB */ + bh_assert(all_cell_num < 1 * BH_MB); + + frame_size = wasm_interp_interp_frame_size(all_cell_num); + if (!(frame = ALLOC_FRAME(exec_env, frame_size, prev_frame))) { + frame = prev_frame; + goto got_exception; + } + + /* Initialize the interpreter context. */ + frame->function = cur_func; + frame_ip = wasm_get_func_code(cur_func); + frame_ip_end = wasm_get_func_code_end(cur_func); + +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* eh-stack starts empty; WASM_OP_TRY appends entries. */ + frame->eh_count = 0; + /* exception_raised is the marker `return_func` reads on + * every wasm-to-wasm call return; if a callee's throw + * found no in-frame handler it stashes the tag on the + * caller's frame->tag_index and sets this flag, then + * goes to return_func. ALLOC_FRAME doesn't zero-init + * the frame header, so leaving the slot uninitialized + * trips the return_func hook on every call return with + * stale memory contents — turning a non-throwing run + * into "wasm exception thrown (tag N)" for random N. */ + frame->exception_raised = false; +#endif + + frame_lp = frame->lp = + frame->operand + cur_wasm_func->const_cell_num; + + /* Initialize the consts */ + if (cur_wasm_func->const_cell_num > 0) { + word_copy(frame->operand, (uint32 *)cur_wasm_func->consts, + cur_wasm_func->const_cell_num); + } + + /* Initialize the local variables */ + memset(frame_lp + cur_func->param_cell_num, 0, + (uint32)(cur_func->local_cell_num * 4)); + +#if WASM_ENABLE_REF_TYPES != 0 && WASM_ENABLE_GC == 0 + /* externref/funcref should be NULL_REF rather than 0 */ + local_cell_idx = cur_func->param_cell_num; + for (i = 0; i < cur_wasm_func->local_count; i++) { + if (cur_wasm_func->local_types[i] == VALUE_TYPE_EXTERNREF + || cur_wasm_func->local_types[i] == VALUE_TYPE_FUNCREF) { + *(frame_lp + local_cell_idx) = NULL_REF; + } + local_cell_idx += + wasm_value_type_cell_num(cur_wasm_func->local_types[i]); + } +#endif + +#if WASM_ENABLE_GC != 0 + /* frame->ip is used during GC root set enumeration, so we must + * initialized this field here */ + frame->ip = frame_ip; + frame_ref = frame->frame_ref = + (uint8 *)(frame->lp + (uint32)cell_num_of_local_stack); + init_frame_refs(frame_ref, (uint32)cell_num_of_local_stack, + cur_func); +#endif + + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)frame); + } +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + HANDLE_OP_END(); + } + + return_func: + { + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame); + + if (!prev_frame->ip) + /* Called from native. */ + return; + + RECOVER_CONTEXT(prev_frame); +#if WASM_ENABLE_GC != 0 + local_cell_num = cur_func->param_cell_num + cur_func->local_cell_num; +#endif +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* Inter-function unwind: the callee stashed a wasm tag on + * this frame (now the active one after RECOVER_CONTEXT) + * when its eh-stack walk found no in-frame match. Re-enter + * find_a_catch_handler so the caller's eh-stack gets a + * chance to catch. Predicted strongly not-taken — + * exceptions are rare, this single check is the entire + * CALL-return-side cost of EH; the success path takes the + * HANDLE_OP_END() below. + * + * Cross-frame payload routing: the callee's throw site's + * source slots lived in the callee's frame_lp, which has + * already been freed by the time we get here. We zero out + * the throw_param_cell_num / throw_src_offsets pair so the + * caller's find_a_catch_handler doesn't try to dereference + * freed memory — the catch (if any matches) will fire with + * a zero-cell payload. This is the same gap documented at + * the WASM_OP_THROW handler and surfaced as + * `cross_function_tag_with_params` in the integration + * suite. */ + if (frame->exception_raised) { + exception_tag_index = frame->tag_index; + throw_param_cell_num = 0; + throw_src_offsets = NULL; + frame->exception_raised = false; + goto find_a_catch_handler; + } +#endif + HANDLE_OP_END(); + } + + (void)frame_ip_end; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + unaligned_atomic: + wasm_set_exception(module, "unaligned atomic"); + goto got_exception; +#endif + +#if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 \ + || WASM_ENABLE_BULK_MEMORY_OPT != 0 + out_of_bounds: + wasm_set_exception(module, "out of bounds memory access"); +#endif + + got_exception: + SYNC_ALL_TO_FRAME(); + return; + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + } +#else + FETCH_OPCODE_AND_DISPATCH(); +#endif +} + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +void ** +wasm_interp_get_handle_table(void) +{ + WASMModuleInstance module; + memset(&module, 0, sizeof(WASMModuleInstance)); + wasm_interp_call_func_bytecode(&module, NULL, NULL, NULL); + return global_handle_table; +} +#endif + +#if WASM_ENABLE_GC != 0 +bool +wasm_interp_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) +{ + WASMInterpFrame *frame; + WASMObjectRef gc_obj; + WASMFunctionInstance *cur_func; + uint8 *frame_ref; + uint32 local_cell_num, i; + + frame = wasm_exec_env_get_cur_frame(exec_env); + for (; frame; frame = frame->prev_frame) { + frame_ref = frame->frame_ref; + cur_func = frame->function; + + if (!cur_func) + continue; + + local_cell_num = cur_func->param_cell_num; + if (frame->ip) + local_cell_num += + cur_func->local_cell_num + cur_func->u.func->max_stack_cell_num; + + for (i = 0; i < local_cell_num; i++) { + if (frame_ref[i]) { + gc_obj = GET_REF_FROM_ADDR(frame->lp + i); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) { + return false; + } + } +#if UINTPTR_MAX == UINT64_MAX + bh_assert(frame_ref[i + 1]); + i++; +#endif + } + } + } + return true; +} +#endif + +void +wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, + WASMFunctionInstance *function, uint32 argc, + uint32 argv[]) +{ + WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); + WASMInterpFrame *frame, *outs_area; + + /* Allocate sufficient cells for all kinds of return values. */ + unsigned all_cell_num = + function->ret_cell_num > 2 ? function->ret_cell_num : 2, + i; + /* This frame won't be used by JITed code, so only allocate interp + frame here. */ + unsigned frame_size; + +#if WASM_ENABLE_GC != 0 + all_cell_num += (all_cell_num + 3) / 4; +#endif + + frame_size = wasm_interp_interp_frame_size(all_cell_num); + + if (argc < function->param_cell_num) { + char buf[128]; + snprintf(buf, sizeof(buf), + "invalid argument count %" PRIu32 + ", must be no smaller than %" PRIu32, + argc, (uint32)function->param_cell_num); + wasm_set_exception(module_inst, buf); + return; + } + argc = function->param_cell_num; + +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + /* + * wasm_runtime_detect_native_stack_overflow is done by + * call_wasm_with_hw_bound_check. + */ +#else + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return; + } +#endif + + if (!(frame = + ALLOC_FRAME(exec_env, frame_size, (WASMInterpFrame *)prev_frame))) + return; + + outs_area = wasm_exec_env_wasm_stack_top(exec_env); + frame->function = NULL; + frame->ip = NULL; + /* There is no local variable. */ + frame->lp = frame->operand + 0; +#if WASM_ENABLE_GC != 0 + frame->frame_ref = + (uint8 *)(frame->lp + + (function->ret_cell_num > 2 ? function->ret_cell_num : 2)); +#endif + frame->ret_offset = 0; + + if ((uint8 *)(outs_area->operand + function->const_cell_num + argc) + > exec_env->wasm_stack.top_boundary) { + wasm_set_exception((WASMModuleInstance *)exec_env->module_inst, + "wasm operand stack overflow"); + return; + } + + if (argc > 0) + word_copy(outs_area->operand + function->const_cell_num, argv, argc); + + wasm_exec_env_set_cur_frame(exec_env, frame); + +#if defined(os_writegsbase) + { + WASMMemoryInstance *memory_inst = wasm_get_default_memory(module_inst); + if (memory_inst) + /* write base addr of linear memory to GS segment register */ + os_writegsbase(memory_inst->memory_data); + } +#endif + + if (function->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (function->import_module_inst) { + LOG_DEBUG("it is a function of a sub module"); + wasm_interp_call_func_import(module_inst, exec_env, function, + frame); + } + else +#endif + { + LOG_DEBUG("it is an native function"); + wasm_interp_call_func_native(module_inst, exec_env, function, + frame); + } + } + else { + wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); + } + + /* Output the return value to the caller */ + if (!wasm_copy_exception(module_inst, NULL)) { + for (i = 0; i < function->ret_cell_num; i++) + argv[i] = *(frame->lp + i); + } + else { +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (wasm_interp_create_call_stack(exec_env)) { + wasm_interp_dump_call_stack(exec_env, true, NULL, 0); + } +#endif + } + + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + FREE_FRAME(exec_env, frame); +#if WASM_ENABLE_OPCODE_COUNTER != 0 + wasm_interp_dump_op_count(); +#endif +} diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 5a1491710c..758db43140 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -4,2177 +4,15394 @@ */ #include "wasm_loader.h" -#include "bh_common.h" -#include "bh_memory.h" -#include "bh_log.h" +#include "bh_platform.h" #include "wasm.h" #include "wasm_opcode.h" #include "wasm_runtime.h" +#include "wasm_loader_common.h" #include "../common/wasm_native.h" +#include "../common/wasm_memory.h" +#if WASM_ENABLE_GC != 0 +#include "../common/gc/gc_type.h" +#include "../common/gc/gc_object.h" +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 +#include "../libraries/debug-engine/debug_engine.h" +#endif +#if WASM_ENABLE_FAST_JIT != 0 +#include "../fast-jit/jit_compiler.h" +#include "../fast-jit/jit_codecache.h" +#endif +#if WASM_ENABLE_JIT != 0 +#include "../compilation/aot_llvm.h" +#endif + +#ifndef TRACE_WASM_LOADER +#define TRACE_WASM_LOADER 0 +#endif /* Read a value of given type from the address pointed to by the given pointer and increase the pointer to the position just after the value being read. */ -#define TEMPLATE_READ_VALUE(Type, p) \ +#define TEMPLATE_READ_VALUE(Type, p) \ (p += sizeof(Type), *(Type *)(p - sizeof(Type))) -static void -set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +#if WASM_ENABLE_MEMORY64 != 0 +static bool +has_module_memory64(WASMModule *module) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, "%s", string); + /* TODO: multi-memories for now assuming the memory idx type is consistent + * across multi-memories */ + if (module->import_memory_count > 0) + return !!(module->import_memories[0].u.memory.mem_type.flags + & MEMORY64_FLAG); + else if (module->memory_count > 0) + return !!(module->memories[0].flags & MEMORY64_FLAG); + + return false; } -#define CHECK_BUF(buf, buf_end, length) do { \ - if (buf + length > buf_end) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM module load failed: " \ - "unexpected end of section or function"); \ - return false; \ - } \ -} while (0) +static bool +is_table_64bit(WASMModule *module, uint32 table_idx) +{ + if (table_idx < module->import_table_count) + return !!(module->import_tables[table_idx].u.table.table_type.flags + & TABLE64_FLAG); + else + return !!(module->tables[table_idx - module->import_table_count] + .table_type.flags + & TABLE64_FLAG); -#define CHECK_BUF1(buf, buf_end, length) do { \ - if (buf + length > buf_end) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM module load failed: unexpected end");\ - return false; \ - } \ -} while (0) + return false; +} +#endif -static bool -skip_leb(const uint8 *buf, const uint8 *buf_end, - uint32 *p_offset, uint32 maxbits, - char* error_buf, uint32 error_buf_size) +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { - uint32 bcnt = 0; - uint64 byte; + wasm_loader_set_error_buf(error_buf, error_buf_size, string, false); +} - while (true) { - if (bcnt + 1 > (maxbits + 6) / 7) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "integer representation too long"); - return false; - } +#if WASM_ENABLE_MEMORY64 != 0 +static void +set_error_buf_mem_offset_out_of_range(char *error_buf, uint32 error_buf_size) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, "offset out of range"); + } +} +#endif - CHECK_BUF(buf, buf_end, *p_offset + 1); - byte = buf[*p_offset]; - *p_offset += 1; - bcnt += 1; - if ((byte & 0x80) == 0) { - break; - } +static void +set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, ...) +{ + va_list args; + char buf[128]; + + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + snprintf(error_buf, error_buf_size, "WASM module load failed: %s", buf); } +} +static bool +check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length, + char *error_buf, uint32 error_buf_size) +{ + if ((uintptr_t)buf + length < (uintptr_t)buf + || (uintptr_t)buf + length > (uintptr_t)buf_end) { + set_error_buf(error_buf, error_buf_size, + "unexpected end of section or function"); + return false; + } return true; } -#define skip_leb_int64(p, p_end) do { \ - uint32 off = 0; \ - if (!skip_leb(p, p_end, &off, 64, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ -} while (0) - -#define skip_leb_uint32(p, p_end) do { \ - uint32 off = 0; \ - if (!skip_leb(p, p_end, &off, 32, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ -} while (0) - -#define skip_leb_int32(p, p_end) do { \ - uint32 off = 0; \ - if (!skip_leb(p, p_end, &off, 32, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ -} while (0) - static bool -read_leb(const uint8 *buf, const uint8 *buf_end, - uint32 *p_offset, uint32 maxbits, - bool sign, uint64 *p_result, - char* error_buf, uint32 error_buf_size) +check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length, + char *error_buf, uint32 error_buf_size) { - uint64 result = 0; - uint32 shift = 0; - uint32 bcnt = 0; - uint64 byte; + if ((uintptr_t)buf + length < (uintptr_t)buf + || (uintptr_t)buf + length > (uintptr_t)buf_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + return true; +} - while (true) { - if (bcnt + 1 > (maxbits + 6) / 7) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "integer representation too long"); - return false; - } +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + if (!check_buf(buf, buf_end, length, error_buf, error_buf_size)) { \ + goto fail; \ + } \ + } while (0) + +#define CHECK_BUF1(buf, buf_end, length) \ + do { \ + if (!check_buf1(buf, buf_end, length, error_buf, error_buf_size)) { \ + goto fail; \ + } \ + } while (0) + +#define skip_leb(p) while (*p++ & 0x80) +#define skip_leb_int64(p, p_end) skip_leb(p) +#define skip_leb_uint32(p, p_end) skip_leb(p) +#define skip_leb_int32(p, p_end) skip_leb(p) +#define skip_leb_mem_offset(p, p_end) skip_leb(p) +#define skip_leb_memidx(p, p_end) skip_leb(p) +#if WASM_ENABLE_MULTI_MEMORY == 0 +#define skip_leb_align(p, p_end) skip_leb(p) +#else +/* Skip the following memidx if applicable */ +#define skip_leb_align(p, p_end) \ + do { \ + if (*p++ & OPT_MEMIDX_FLAG) \ + skip_leb_uint32(p, p_end); \ + } while (0) +#endif - CHECK_BUF(buf, buf_end, *p_offset + 1); - byte = buf[*p_offset]; - *p_offset += 1; - result |= ((byte & 0x7f) << shift); - shift += 7; - bcnt += 1; - if ((byte & 0x80) == 0) { - break; - } - } +#define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p) +#define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) - if (!sign && maxbits == 32 && shift >= maxbits) { - /* The top bits set represent values > 32 bits */ - if (((uint8)byte) & 0xf0) - goto fail_integer_too_large; - } - else if (sign && maxbits == 32) { - if (shift < maxbits) { - /* Sign extend */ - result = (((int32)result) << (maxbits - shift)) - >> (maxbits - shift); - } - else { - /* The top bits should be a sign-extension of the sign bit */ - bool sign_bit_set = ((uint8)byte) & 0x8; - int top_bits = ((uint8)byte) & 0xf0; - if ((sign_bit_set && top_bits != 0x70) - || (!sign_bit_set && top_bits != 0)) - goto fail_integer_too_large; - } - } - else if (sign && maxbits == 64) { - if (shift < maxbits) { - /* Sign extend */ - result = (((int64)result) << (maxbits - shift)) - >> (maxbits - shift); - } - else { - /* The top bits should be a sign-extension of the sign bit */ - bool sign_bit_set = ((uint8)byte) & 0x1; - int top_bits = ((uint8)byte) & 0xfe; +#define read_leb_int64(p, p_end, res) \ + do { \ + uint64 res64; \ + if (!read_leb((uint8 **)&p, p_end, 64, true, &res64, error_buf, \ + error_buf_size)) \ + goto fail; \ + res = (int64)res64; \ + } while (0) + +#if WASM_ENABLE_MEMORY64 != 0 +#define read_leb_mem_offset(p, p_end, res) \ + do { \ + uint64 res64; \ + if (!read_leb((uint8 **)&p, p_end, is_memory64 ? 64 : 32, false, \ + &res64, error_buf, error_buf_size)) { \ + set_error_buf_mem_offset_out_of_range(error_buf, error_buf_size); \ + goto fail; \ + } \ + res = (mem_offset_t)res64; \ + } while (0) +#else +#define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif - if ((sign_bit_set && top_bits != 0x7e) - || (!sign_bit_set && top_bits != 0)) - goto fail_integer_too_large; - } - } +#define read_leb_uint32(p, p_end, res) \ + do { \ + uint64 res64; \ + if (!read_leb((uint8 **)&p, p_end, 32, false, &res64, error_buf, \ + error_buf_size)) \ + goto fail; \ + res = (uint32)res64; \ + } while (0) + +#define read_leb_int32(p, p_end, res) \ + do { \ + uint64 res64; \ + if (!read_leb((uint8 **)&p, p_end, 32, true, &res64, error_buf, \ + error_buf_size)) \ + goto fail; \ + res = (int32)res64; \ + } while (0) + +#if WASM_ENABLE_MULTI_MEMORY != 0 +#define check_memidx(module, memidx) \ + do { \ + if (memidx >= module->import_memory_count + module->memory_count) { \ + set_error_buf_v(error_buf, error_buf_size, "unknown memory %d", \ + memidx); \ + goto fail; \ + } \ + } while (0) +/* Bit 6(0x40) indicating the optional memidx, and reset bit 6 for + * alignment check */ +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (res & OPT_MEMIDX_FLAG) { \ + res &= ~OPT_MEMIDX_FLAG; \ + read_leb_uint32(p, p_end, memidx); /* memidx */ \ + check_memidx(module, memidx); \ + } \ + } while (0) +#else +/* reserved byte 0x00 */ +#define check_memidx(module, memidx) \ + do { \ + (void)module; \ + if (memidx != 0) { \ + set_error_buf(error_buf, error_buf_size, "zero byte expected"); \ + goto fail; \ + } \ + } while (0) +#define read_leb_memarg(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif - *p_result = result; - return true; +static char * +type2str(uint8 type) +{ + char *type_str[] = { "v128", "f64", "f32", "i64", "i32" }; +#if WASM_ENABLE_GC != 0 + char *type_str_ref[] = { "stringview_iter", + "stringview_wtf16", + "(ref null ht)", + "(ref ht)", + "", /* reserved */ + "stringview_wtf8", + "stringref", + "", /* reserved */ + "", /* reserved */ + "arrayref", + "structref", + "i31ref", + "eqref", + "anyref", + "externref", + "funcref", + "nullref", + "nullexternref", + "nullfuncref" }; +#endif -fail_integer_too_large: - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: integer too large"); + if (type >= VALUE_TYPE_V128 && type <= VALUE_TYPE_I32) + return type_str[type - VALUE_TYPE_V128]; +#if WASM_ENABLE_GC != 0 + else if (wasm_is_type_reftype(type)) + return type_str_ref[type - REF_TYPE_STRINGVIEWITER]; +#endif + else if (type == VALUE_TYPE_FUNCREF) + return "funcref"; + else if (type == VALUE_TYPE_EXTERNREF) + return "externref"; + else + return "unknown type"; +} + +static bool +is_32bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + /* the operand stack is in polymorphic state */ + || type == VALUE_TYPE_ANY +#if WASM_ENABLE_GC != 0 + || (sizeof(uintptr_t) == 4 && wasm_is_type_reftype(type)) +#elif WASM_ENABLE_REF_TYPES != 0 + /* For reference types, we use uint32 index to represent + the funcref and externref */ + || type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF +#endif + ) + return true; return false; } -#define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p) -#define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) -#define read_bool(p) TEMPLATE_READ_VALUE(bool, p) - -#define read_leb_int64(p, p_end, res) do { \ - if (p < p_end) { \ - uint8 _val = *p; \ - if (!(_val & 0x80)) { \ - res = (int64)_val; \ - if (_val & 0x40) \ - /* sign extend */ \ - res |= 0xFFFFFFFFFFFFFF80LL; \ - p++; \ - break; \ - } \ - } \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 64, true, &res64, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ - res = (int64)res64; \ -} while (0) - -#define read_leb_uint32(p, p_end, res) do { \ - if (p < p_end) { \ - uint8 _val = *p; \ - if (!(_val & 0x80)) { \ - res = _val; \ - p++; \ - break; \ - } \ - } \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 32, false, &res64, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ - res = (uint32)res64; \ -} while (0) - -#define read_leb_int32(p, p_end, res) do { \ - if (p < p_end) { \ - uint8 _val = *p; \ - if (!(_val & 0x80)) { \ - res = (int32)_val; \ - if (_val & 0x40) \ - /* sign extend */ \ - res |= 0xFFFFFF80; \ - p++; \ - break; \ - } \ - } \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 32, true, &res64, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ - res = (int32)res64; \ -} while (0) - -static bool -check_utf8_str(const uint8* str, uint32 len) -{ - const uint8 *p = str, *p_end = str + len, *p_end1; - uint8 chr, n_bytes; +static bool +is_64bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64 +#if WASM_ENABLE_GC != 0 + || (sizeof(uintptr_t) == 8 && wasm_is_type_reftype(type)) +#endif + ) + return true; + return false; +} - while (p < p_end) { - chr = *p++; - if (chr >= 0x80) { - /* Calculate the byte count: the first byte must be - 110XXXXX, 1110XXXX, 11110XXX, 111110XX, or 1111110X, - the count of leading '1' denotes the total byte count */ - n_bytes = 0; - while ((chr & 0x80) != 0) { - chr = (uint8)(chr << 1); - n_bytes++; - } - - /* Check byte count */ - if (n_bytes < 2 || n_bytes > 6 - || p + n_bytes - 1 > p_end) - return false; +#if WASM_ENABLE_GC != 0 +static bool +is_packed_type(uint8 type) +{ + return (type == PACKED_TYPE_I8 || type == PACKED_TYPE_I16) ? true : false; +} +#endif - /* Check the following bytes, which must be 10XXXXXX */ - p_end1 = p + n_bytes - 1; - while (p < p_end1) { - if (!(*p & 0x80) || (*p | 0x40)) - return false; - p++; - } - } - } - return true; +static bool +is_byte_a_type(uint8 type) +{ + return (is_valid_value_type_for_interpreter(type) + || (type == VALUE_TYPE_VOID)) + ? true + : false; } -static char* -const_str_list_insert(const uint8 *str, uint32 len, WASMModule *module, - char* error_buf, uint32 error_buf_size) +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) +static V128 +read_i8x16(uint8 *p_buf, char *error_buf, uint32 error_buf_size) { - StringNode *node, *node_next; + V128 result; + uint8 i; - if (!check_utf8_str(str, len)) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "invalid UTF-8 encoding"); - return NULL; + for (i = 0; i != 16; ++i) { + result.i8x16[i] = read_uint8(p_buf); } - /* Search const str list */ - node = module->const_str_list; - while (node) { - node_next = node->next; - if (strlen(node->str) == len - && !memcmp(node->str, str, len)) - break; - node = node_next; - } + return result; +} +#endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || \ + (WASM_ENABLE_FAST_INTERP != 0) */ +#endif /* end of WASM_ENABLE_SIMD */ - if (node) - return node->str; +static void * +loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; - if (!(node = wasm_malloc(sizeof(StringNode) + len + 1))) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "allocate memory failed."); + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); return NULL; } - node->str = ((char*)node) + sizeof(StringNode); - bh_memcpy_s(node->str, len + 1, str, len); - node->str[len] = '\0'; + memset(mem, 0, (uint32)size); + return mem; +} - if (!module->const_str_list) { - /* set as head */ - module->const_str_list = node; - node->next = NULL; - } - else { - /* insert it */ - node->next = module->const_str_list; - module->const_str_list = node; +static void * +memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, char *error_buf, + uint32 error_buf_size) +{ + uint8 *mem_new; + bh_assert(size_new > size_old); + + if ((mem_new = wasm_runtime_realloc(mem_old, size_new))) { + memset(mem_new + size_old, 0, size_new - size_old); + return mem_new; } - return node->str; + if ((mem_new = loader_malloc(size_new, error_buf, error_buf_size))) { + bh_memcpy_s(mem_new, size_new, mem_old, size_old); + wasm_runtime_free(mem_old); + } + return mem_new; } +#define MEM_REALLOC(mem, size_old, size_new) \ + do { \ + void *mem_new = memory_realloc(mem, size_old, size_new, error_buf, \ + error_buf_size); \ + if (!mem_new) \ + goto fail; \ + mem = mem_new; \ + } while (0) + static bool -load_init_expr(const uint8 **p_buf, const uint8 *buf_end, - InitializerExpression *init_expr, - char *error_buf, uint32 error_buf_size) +check_type_index(const WASMModule *module, uint32 type_count, uint32 type_index, + char *error_buf, uint32 error_buf_size) { - const uint8 *p = *p_buf, *p_end = buf_end; - uint8 flag, end_byte, *p_float; - uint32 i; - - CHECK_BUF(p, p_end, 1); - init_expr->init_expr_type = read_uint8(p); - flag = init_expr->init_expr_type; + if (type_index >= type_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown type %d", + type_index); + return false; + } + return true; +} - switch (flag) { - /* i32.const */ - case INIT_EXPR_TYPE_I32_CONST: - read_leb_int32(p, p_end, init_expr->u.i32); - break; - /* i64.const */ - case INIT_EXPR_TYPE_I64_CONST: - read_leb_int64(p, p_end, init_expr->u.i64); - break; - /* f32.const */ - case INIT_EXPR_TYPE_F32_CONST: - CHECK_BUF(p, p_end, 4); - p_float = (uint8*)&init_expr->u.f32; - for (i = 0; i < sizeof(float32); i++) - *p_float++ = *p++; - break; - /* f64.const */ - case INIT_EXPR_TYPE_F64_CONST: - CHECK_BUF(p, p_end, 8); - p_float = (uint8*)&init_expr->u.f64; - for (i = 0; i < sizeof(float64); i++) - *p_float++ = *p++; - break; - /* get_global */ - case INIT_EXPR_TYPE_GET_GLOBAL: - read_leb_uint32(p, p_end, init_expr->u.global_index); - break; - default: - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: type mismatch"); - return false; +#if WASM_ENABLE_GC != 0 +static bool +check_array_type(const WASMModule *module, uint32 type_index, char *error_buf, + uint32 error_buf_size) +{ + if (!check_type_index(module, module->type_count, type_index, error_buf, + error_buf_size)) { + return false; } - CHECK_BUF(p, p_end, 1); - end_byte = read_uint8(p); - if (end_byte != 0x0b) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "unexpected end of section or function"); + if (module->types[type_index]->type_flag != WASM_TYPE_ARRAY) { + set_error_buf(error_buf, error_buf_size, "unknown array type"); return false; } - *p_buf = p; return true; } +#endif +/* + * if no GC is enabled, an valid type is always a function type. + * but if GC is enabled, we need to check the type flag + */ static bool -load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +check_function_type(const WASMModule *module, uint32 type_index, + char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end, *p_org; - uint32 type_count, param_count, result_count, i, j; - uint64 total_size; - uint8 flag; - WASMType *type; + if (!check_type_index(module, module->type_count, type_index, error_buf, + error_buf_size)) { + return false; + } - read_leb_uint32(p, p_end, type_count); +#if WASM_ENABLE_GC != 0 + if (module->types[type_index]->type_flag != WASM_TYPE_FUNC) { + set_error_buf(error_buf, error_buf_size, "unknown function type"); + return false; + } +#endif - if (type_count) { - module->type_count = type_count; - total_size = sizeof(WASMType*) * (uint64)type_count; - if (total_size >= UINT32_MAX - || !(module->types = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load type section failed: allocate memory failed."); - return false; - } + return true; +} - memset(module->types, 0, (uint32)total_size); +static bool +check_function_index(const WASMModule *module, uint32 function_index, + char *error_buf, uint32 error_buf_size) +{ + if (function_index + >= module->import_function_count + module->function_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown function %u", + function_index); + return false; + } + return true; +} - for (i = 0; i < type_count; i++) { - CHECK_BUF(p, p_end, 1); - flag = read_uint8(p); - if (flag != 0x60) { - set_error_buf(error_buf, error_buf_size, - "Load type section failed: invalid type flag."); - return false; - } +typedef struct InitValue { + uint8 type; + uint8 flag; +#if WASM_ENABLE_GC != 0 + uint8 gc_opcode; + WASMRefType ref_type; +#endif + WASMValue value; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression *expr; +#endif +} InitValue; - read_leb_uint32(p, p_end, param_count); +typedef struct ConstExprContext { + uint32 sp; + uint32 size; + WASMModule *module; + InitValue *stack; + InitValue data[WASM_CONST_EXPR_STACK_SIZE]; +} ConstExprContext; - /* Resolve param count and result count firstly */ - p_org = p; - CHECK_BUF(p, p_end, param_count); - p += param_count; - read_leb_uint32(p, p_end, result_count); - if (result_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load type section failed: invalid result count."); - return false; - } - CHECK_BUF(p, p_end, result_count); - p = p_org; +static void +init_const_expr_stack(ConstExprContext *ctx, WASMModule *module) +{ + ctx->sp = 0; + ctx->module = module; + ctx->stack = ctx->data; + ctx->size = WASM_CONST_EXPR_STACK_SIZE; +} - total_size = offsetof(WASMType, types) + - sizeof(uint8) * (uint64)(param_count + result_count); - if (total_size >= UINT32_MAX - || !(type = module->types[i] = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load type section failed: allocate memory failed."); - return false; - } +static bool +push_const_expr_stack(ConstExprContext *ctx, uint8 flag, uint8 type, +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type, uint8 gc_opcode, +#endif + WASMValue *value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression *expr, +#endif + char *error_buf, uint32 error_buf_size) +{ + InitValue *cur_value; - /* Resolve param types and result types */ - type->param_count = param_count; - type->result_count = result_count; - for (j = 0; j < param_count; j++) { - CHECK_BUF(p, p_end, 1); - type->types[j] = read_uint8(p); - } - read_leb_uint32(p, p_end, result_count); - for (j = 0; j < result_count; j++) { - CHECK_BUF(p, p_end, 1); - type->types[param_count + j] = read_uint8(p); + if (ctx->sp >= ctx->size) { + if (ctx->stack != ctx->data) { + MEM_REALLOC(ctx->stack, ctx->size * sizeof(InitValue), + (ctx->size + 4) * sizeof(InitValue)); + } + else { + if (!(ctx->stack = + loader_malloc((ctx->size + 4) * (uint64)sizeof(InitValue), + error_buf, error_buf_size))) { + goto fail; } + bh_memcpy_s(ctx->stack, (ctx->size + 4) * (uint32)sizeof(InitValue), + ctx->data, ctx->size * (uint32)sizeof(InitValue)); } + ctx->size += 4; } - if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load type section failed: section size mismatch"); - return false; + cur_value = &ctx->stack[ctx->sp++]; + cur_value->type = type; + cur_value->flag = flag; + cur_value->value = *value; + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + cur_value->expr = expr; +#endif + +#if WASM_ENABLE_GC != 0 + cur_value->gc_opcode = gc_opcode; + if (wasm_is_type_multi_byte_type(type)) { + bh_memcpy_s(&cur_value->ref_type, wasm_reftype_struct_size(ref_type), + ref_type, wasm_reftype_struct_size(ref_type)); } +#endif - LOG_VERBOSE("Load type section success.\n"); return true; +fail: + return false; } -static bool -load_table_import(const uint8 **p_buf, const uint8 *buf_end, - WASMTableImport *table, - char *error_buf, uint32 error_buf_size) +#if WASM_ENABLE_GC != 0 +static void +destroy_init_expr_data_recursive(WASMModule *module, void *data) { - const uint8 *p = *p_buf, *p_end = buf_end; + WASMStructNewInitValues *struct_init_values = + (WASMStructNewInitValues *)data; + WASMArrayNewInitValues *array_init_values = (WASMArrayNewInitValues *)data; + WASMType *wasm_type; + uint32 i; - CHECK_BUF(p, p_end, 1); - /* 0x70 */ - table->elem_type = read_uint8(p); - bh_assert(table->elem_type == TABLE_ELEM_TYPE_ANY_FUNC); - read_leb_uint32(p, p_end, table->flags); - read_leb_uint32(p, p_end, table->init_size); - if (table->flags & 1) - read_leb_uint32(p, p_end, table->max_size); - else - table->max_size = 0x10000; + if (!data) + return; - *p_buf = p; - return true; + wasm_type = module->types[struct_init_values->type_idx]; + + /* The data can only be type of `WASMStructNewInitValues *` + or `WASMArrayNewInitValues *` */ + bh_assert(wasm_type->type_flag == WASM_TYPE_STRUCT + || wasm_type->type_flag == WASM_TYPE_ARRAY); + + if (wasm_type->type_flag == WASM_TYPE_STRUCT) { + WASMStructType *struct_type = (WASMStructType *)wasm_type; + WASMRefType *ref_type; + uint8 field_type; + + uint16 ref_type_map_index = 0; + for (i = 0; i < struct_init_values->count; i++) { + field_type = struct_type->fields[i].field_type; + if (wasm_is_type_multi_byte_type(field_type)) + ref_type = + struct_type->ref_type_maps[ref_type_map_index++].ref_type; + else + ref_type = NULL; + if (wasm_reftype_is_subtype_of(field_type, ref_type, + REF_TYPE_STRUCTREF, NULL, + module->types, module->type_count) + || wasm_reftype_is_subtype_of( + field_type, ref_type, REF_TYPE_ARRAYREF, NULL, + module->types, module->type_count)) { + destroy_init_expr_data_recursive( + module, struct_init_values->fields[i].data); + } + } + } + else if (wasm_type->type_flag == WASM_TYPE_ARRAY) { + WASMArrayType *array_type = (WASMArrayType *)wasm_type; + WASMRefType *elem_ref_type = array_type->elem_ref_type; + uint8 elem_type = array_type->elem_type; + + for (i = 0; i < array_init_values->length; i++) { + if (wasm_reftype_is_subtype_of(elem_type, elem_ref_type, + REF_TYPE_STRUCTREF, NULL, + module->types, module->type_count) + || wasm_reftype_is_subtype_of( + elem_type, elem_ref_type, REF_TYPE_ARRAYREF, NULL, + module->types, module->type_count)) { + destroy_init_expr_data_recursive( + module, array_init_values->elem_data[i].data); + } + } + } + + wasm_runtime_free(data); } +#endif static bool -load_memory_import(const uint8 **p_buf, const uint8 *buf_end, - WASMMemoryImport *memory, - char *error_buf, uint32 error_buf_size) -{ - const uint8 *p = *p_buf, *p_end = buf_end; - uint32 pool_size = bh_memory_pool_size(); - uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT - / DEFAULT_NUM_BYTES_PER_PAGE; +pop_const_expr_stack(ConstExprContext *ctx, uint8 *p_flag, uint8 type, +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type, uint8 *p_gc_opcode, +#endif + WASMValue *p_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression **p_expr, +#endif + char *error_buf, uint32 error_buf_size) +{ + InitValue *cur_value; - read_leb_uint32(p, p_end, memory->flags); - read_leb_uint32(p, p_end, memory->init_page_count); - if (memory->flags & 1) { - read_leb_uint32(p, p_end, memory->max_page_count); - if (memory->max_page_count > max_page_count) - memory->max_page_count = max_page_count; + if (ctx->sp == 0) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: const expr stack underflow"); + return false; } - else - /* Limit the maximum memory size to max_page_count */ - memory->max_page_count = max_page_count; - - memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; - - *p_buf = p; - return true; -} -static bool -load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, - char *error_buf, uint32 error_buf_size) -{ - const uint8 *p = *p_buf, *p_end = buf_end; + cur_value = &ctx->stack[--ctx->sp]; - CHECK_BUF(p, p_end, 1); - /* 0x70 */ - table->elem_type = read_uint8(p); - bh_assert(table->elem_type == TABLE_ELEM_TYPE_ANY_FUNC); - read_leb_uint32(p, p_end, table->flags); - read_leb_uint32(p, p_end, table->init_size); - if (table->flags & 1) - read_leb_uint32(p, p_end, table->max_size); - else - table->max_size = 0x10000; +#if WASM_ENABLE_GC == 0 + if (cur_value->type != type) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; + } +#else + if (!wasm_reftype_is_subtype_of(cur_value->type, &cur_value->ref_type, type, + ref_type, ctx->module->types, + ctx->module->type_count)) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", type2str(type), + " but got other"); + goto fail; + } +#endif - *p_buf = p; + if (p_flag) + *p_flag = cur_value->flag; + if (p_value) + *p_value = cur_value->value; +#if WASM_ENABLE_GC != 0 + if (p_gc_opcode) + *p_gc_opcode = cur_value->gc_opcode; +#endif +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + if (p_expr) + *p_expr = cur_value->expr; +#endif return true; + +#if WASM_ENABLE_GC != 0 +fail: + if ((cur_value->flag == WASM_OP_GC_PREFIX) + && (cur_value->gc_opcode == WASM_OP_STRUCT_NEW + || cur_value->gc_opcode == WASM_OP_ARRAY_NEW + || cur_value->gc_opcode == WASM_OP_ARRAY_NEW_FIXED)) { + destroy_init_expr_data_recursive(ctx->module, cur_value->value.data); + } + return false; +#endif } -static bool -load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, - char *error_buf, uint32 error_buf_size) +static void +destroy_const_expr_stack(ConstExprContext *ctx, bool free_exprs) { - const uint8 *p = *p_buf, *p_end = buf_end; - uint32 pool_size = bh_memory_pool_size(); - uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT - / DEFAULT_NUM_BYTES_PER_PAGE; +#if WASM_ENABLE_GC != 0 + uint32 i; - read_leb_uint32(p, p_end, memory->flags); - read_leb_uint32(p, p_end, memory->init_page_count); - if (memory->flags & 1) { - read_leb_uint32(p, p_end, memory->max_page_count); - if (memory->max_page_count > max_page_count) - memory->max_page_count = max_page_count; + for (i = 0; i < ctx->sp; i++) { + if ((ctx->stack[i].flag == WASM_OP_GC_PREFIX) + && (ctx->stack[i].gc_opcode == WASM_OP_STRUCT_NEW + || ctx->stack[i].gc_opcode == WASM_OP_ARRAY_NEW + || ctx->stack[i].gc_opcode == WASM_OP_ARRAY_NEW_FIXED)) { + destroy_init_expr_data_recursive(ctx->module, + ctx->stack[i].value.data); + } } - else - /* Limit the maximum memory size to max_page_count */ - memory->max_page_count = max_page_count; - - memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; +#endif +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + if (free_exprs) { + for (uint32 j = 0; j < ctx->sp; j++) { + if (is_expr_binary_op(ctx->stack[j].expr->init_expr_type)) { + destroy_init_expr_recursive(ctx->stack[j].expr); + ctx->stack[j].expr = NULL; + } + } + } +#endif - *p_buf = p; - return true; + if (ctx->stack != ctx->data) { + wasm_runtime_free(ctx->stack); + } } -static void* -resolve_sym(const char *module_name, const char *field_name) +#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0 +static void +destroy_init_expr(WASMModule *module, InitializerExpression *expr) { - void *sym; - -#if WASM_ENABLE_LIBC_BUILTIN != 0 - if ((sym = wasm_native_lookup_libc_builtin_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_LIBC_WASI != 0 - if ((sym = wasm_native_lookup_libc_wasi_func(module_name, - field_name))) - return sym; +#if WASM_ENABLE_GC != 0 + if (expr->init_expr_type == INIT_EXPR_TYPE_STRUCT_NEW + || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW + || expr->init_expr_type == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + destroy_init_expr_data_recursive(module, expr->u.unary.v.data); + } #endif -#if WASM_ENABLE_BASE_LIB != 0 - if ((sym = wasm_native_lookup_base_lib_func(module_name, - field_name))) - return sym; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + /* free left expr and right exprs for binary operand */ + if (!is_expr_binary_op(expr->init_expr_type)) { + return; + } + if (expr->u.binary.l_expr) { + destroy_init_expr_recursive(expr->u.binary.l_expr); + } + if (expr->u.binary.r_expr) { + destroy_init_expr_recursive(expr->u.binary.r_expr); + } + expr->u.binary.l_expr = expr->u.binary.r_expr = NULL; #endif - - if ((sym = wasm_native_lookup_extension_lib_func(module_name, - field_name))) - return sym; - - return NULL; } +#endif +/* for init expr + * (data (i32.add (i32.const 0) (i32.sub (i32.const 1) (i32.const 2)))), + * the binary format is + * 0x11: 41 00 ; i32.const 0 + * 0x13: 41 01 ; i32.const 1 + * 0x15: 41 02 ; i32.const 2 + * 0x17: 6b ; i32.sub + * 0x18: 6a ; i32.add + * for traversal: read opcodes and push them onto the stack. When encountering + * a binary opcode, pop two values from the stack which become the left and + * right child nodes of this binary operation node. + */ static bool -load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, + InitializerExpression *init_expr, uint8 type, void *ref_type, + char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end, *p_old; - uint32 import_count, name_len, type_index, i, u32, flags; - uint64 total_size; - WASMImport *import; - WASMImport *import_functions = NULL, *import_tables = NULL; - WASMImport *import_memories = NULL, *import_globals = NULL; - char *module_name, *field_name; - uint8 mutable, u8, kind; + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 flag, *p_float; + uint32 i; + ConstExprContext const_expr_ctx = { 0 }; + WASMValue cur_value; +#if WASM_ENABLE_GC != 0 + uint32 opcode1, type_idx; + uint8 opcode; + WASMRefType cur_ref_type = { 0 }; +#endif +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression *cur_expr = NULL; +#endif - read_leb_uint32(p, p_end, import_count); + init_const_expr_stack(&const_expr_ctx, module); - if (import_count) { - module->import_count = import_count; - total_size = sizeof(WASMImport) * (uint64)import_count; - if (total_size >= UINT32_MAX - || !(module->imports = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: allocate memory failed."); - return false; - } + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + + while (flag != WASM_OP_END) { + switch (flag) { + /* i32.const */ + case INIT_EXPR_TYPE_I32_CONST: + read_leb_int32(p, p_end, cur_value.i32); + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_I32, +#if WASM_ENABLE_GC != 0 + NULL, 0, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + break; + /* i64.const */ + case INIT_EXPR_TYPE_I64_CONST: + read_leb_int64(p, p_end, cur_value.i64); + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_I64, +#if WASM_ENABLE_GC != 0 + NULL, 0, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + break; + /* f32.const */ + case INIT_EXPR_TYPE_F32_CONST: + CHECK_BUF(p, p_end, 4); + p_float = (uint8 *)&cur_value.f32; + for (i = 0; i < sizeof(float32); i++) + *p_float++ = *p++; + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_F32, +#if WASM_ENABLE_GC != 0 + NULL, 0, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + break; + /* f64.const */ + case INIT_EXPR_TYPE_F64_CONST: + CHECK_BUF(p, p_end, 8); + p_float = (uint8 *)&cur_value.f64; + for (i = 0; i < sizeof(float64); i++) + *p_float++ = *p++; + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_F64, +#if WASM_ENABLE_GC != 0 + NULL, 0, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + break; +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) + /* v128.const */ + case INIT_EXPR_TYPE_V128_CONST: + { + uint64 high, low; - memset(module->imports, 0, (uint32)total_size); + CHECK_BUF(p, p_end, 1); + (void)read_uint8(p); - p_old = p; + CHECK_BUF(p, p_end, 16); + wasm_runtime_read_v128(p, &high, &low); + p += 16; - /* Scan firstly to get import count of each type */ - for (i = 0; i < import_count; i++) { - /* module name */ - read_leb_uint32(p, p_end, name_len); - CHECK_BUF(p, p_end, name_len); - p += name_len; + cur_value.v128.i64x2[0] = high; + cur_value.v128.i64x2[1] = low; - /* field name */ - read_leb_uint32(p, p_end, name_len); - CHECK_BUF(p, p_end, name_len); - p += name_len; + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_V128, +#if WASM_ENABLE_GC != 0 + NULL, 0, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; +#if WASM_ENABLE_WAMR_COMPILER != 0 + /* If any init_expr is v128.const, mark SIMD used */ + module->is_simd_used = true; +#endif + break; + } +#endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || \ + (WASM_ENABLE_FAST_INTERP != 0) */ +#endif /* end of WASM_ENABLE_SIMD */ +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: + { - CHECK_BUF(p, p_end, 1); - /* 0x00/0x01/0x02/0x03 */ - kind = read_uint8(p); + InitializerExpression *l_expr, *r_expr; + WASMValue l_value, r_value; + uint8 l_flag, r_flag; + uint8 value_type; - switch (kind) { - case IMPORT_KIND_FUNC: /* import function */ - read_leb_uint32(p, p_end, type_index); - module->import_function_count++; - break; + if (flag == INIT_EXPR_TYPE_I32_ADD + || flag == INIT_EXPR_TYPE_I32_SUB + || flag == INIT_EXPR_TYPE_I32_MUL) { + value_type = VALUE_TYPE_I32; + } + else { + value_type = VALUE_TYPE_I64; + } - case IMPORT_KIND_TABLE: /* import table */ - CHECK_BUF(p, p_end, 1); - /* 0x70 */ - u8 = read_uint8(p); - read_leb_uint32(p, p_end, flags); - read_leb_uint32(p, p_end, u32); - if (flags & 1) - read_leb_uint32(p, p_end, u32); - module->import_table_count++; - if (module->import_table_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: multiple tables"); - return false; + /* If right flag indicates a binary operation, right expr will + * be popped from stack. Otherwise, allocate a new expr for + * right expr. Same for left expr. + */ + if (!(pop_const_expr_stack(&const_expr_ctx, &r_flag, value_type, +#if WASM_ENABLE_GC != 0 + NULL, NULL, +#endif + &r_value, &r_expr, error_buf, + error_buf_size))) { + goto fail; + } + if (!is_expr_binary_op(r_flag)) { + if (!(r_expr = loader_malloc(sizeof(InitializerExpression), + error_buf, error_buf_size))) { + goto fail; } - break; + r_expr->init_expr_type = r_flag; + r_expr->u.unary.v = r_value; + } - case IMPORT_KIND_MEMORY: /* import memory */ - read_leb_uint32(p, p_end, flags); - read_leb_uint32(p, p_end, u32); - if (flags & 1) - read_leb_uint32(p, p_end, u32); - module->import_memory_count++; - if (module->import_memory_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: multiple memories"); - return false; + if (!(pop_const_expr_stack(&const_expr_ctx, &l_flag, value_type, +#if WASM_ENABLE_GC != 0 + NULL, NULL, +#endif + &l_value, &l_expr, error_buf, + error_buf_size))) { + destroy_init_expr_recursive(r_expr); + goto fail; + } + if (!is_expr_binary_op(l_flag)) { + if (!(l_expr = loader_malloc(sizeof(InitializerExpression), + error_buf, error_buf_size))) { + destroy_init_expr_recursive(r_expr); + goto fail; } - break; - - case IMPORT_KIND_GLOBAL: /* import global */ - CHECK_BUF(p, p_end, 2); - p += 2; - module->import_global_count++; - break; - - default: - set_error_buf(error_buf, error_buf_size, - "Load import section failed: invalid import type."); - return false; - } - } - - if (module->import_function_count) - import_functions = module->import_functions = module->imports; - if (module->import_table_count) - import_tables = module->import_tables = - module->imports + module->import_function_count; - if (module->import_memory_count) - import_memories = module->import_memories = - module->imports + module->import_function_count + module->import_table_count; - if (module->import_global_count) - import_globals = module->import_globals = - module->imports + module->import_function_count + module->import_table_count - + module->import_memory_count; + l_expr->init_expr_type = l_flag; + l_expr->u.unary.v = l_value; + } - p = p_old; + if (!(cur_expr = loader_malloc(sizeof(InitializerExpression), + error_buf, error_buf_size))) { + destroy_init_expr_recursive(l_expr); + destroy_init_expr_recursive(r_expr); + goto fail; + } + cur_expr->init_expr_type = flag; + cur_expr->u.binary.l_expr = l_expr; + cur_expr->u.binary.r_expr = r_expr; - /* insert "env" and "wasi_unstable" to const str list */ - if (!const_str_list_insert((uint8*)"env", 3, module, error_buf, error_buf_size) - || !const_str_list_insert((uint8*)"wasi_unstable", 13, module, - error_buf, error_buf_size)) { - return false; - } + if (!push_const_expr_stack(&const_expr_ctx, flag, value_type, +#if WASM_ENABLE_GC != 0 + NULL, 0, +#endif + &cur_value, cur_expr, error_buf, + error_buf_size)) { + destroy_init_expr_recursive(cur_expr); + goto fail; + } - /* Scan again to read the data */ - for (i = 0; i < import_count; i++) { - /* load module name */ - read_leb_uint32(p, p_end, name_len); - CHECK_BUF(p, p_end, name_len); - if (!(module_name = const_str_list_insert - (p, name_len, module, error_buf, error_buf_size))) { - return false; + break; } - p += name_len; +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR */ +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + /* ref.func */ + case INIT_EXPR_TYPE_FUNCREF_CONST: + { + uint32 func_idx; + read_leb_uint32(p, p_end, func_idx); + cur_value.ref_index = func_idx; + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } - /* load field name */ - read_leb_uint32(p, p_end, name_len); - CHECK_BUF(p, p_end, name_len); - if (!(field_name = const_str_list_insert - (p, name_len, module, error_buf, error_buf_size))) { - return false; +#if WASM_ENABLE_GC == 0 + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_FUNCREF, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; +#else + if (func_idx < module->import_function_count) { + type_idx = + module->import_functions[func_idx].u.function.type_idx; + } + else { + type_idx = module + ->functions[func_idx + - module->import_function_count] + ->type_idx; + } + wasm_set_refheaptype_typeidx(&cur_ref_type.ref_ht_typeidx, + false, type_idx); + if (!push_const_expr_stack(&const_expr_ctx, flag, + cur_ref_type.ref_type, &cur_ref_type, + 0, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; +#endif +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; } - p += name_len; - - CHECK_BUF(p, p_end, 1); - /* 0x00/0x01/0x02/0x03 */ - kind = read_uint8(p); - switch (kind) { - case IMPORT_KIND_FUNC: /* import function */ - bh_assert(import_functions); - import = import_functions++; - read_leb_uint32(p, p_end, type_index); - if (type_index >= module->type_count) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: " - "function type index out of range."); - return false; - } - import->u.function.func_type = module->types[type_index]; - if (!module->possible_memory_grow - && !strcmp(module_name, "env") - && !(strcmp(field_name, "enlargeMemory"))) - module->possible_memory_grow = true; + /* ref.null */ + case INIT_EXPR_TYPE_REFNULL_CONST: + { +#if WASM_ENABLE_GC == 0 + uint8 type1; + CHECK_BUF(p, p_end, 1); + type1 = read_uint8(p); + cur_value.ref_index = NULL_REF; + if (!push_const_expr_stack(&const_expr_ctx, flag, type1, + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; +#else + /* + * According to the current GC SPEC rules, the heap_type must be + * validated when ref.null is used. It can be an absheaptype, + * or the type C.types[type_idx] must be defined in the context. + */ + int32 heap_type; + read_leb_int32(p, p_end, heap_type); + cur_value.gc_obj = NULL_REF; + + /* + * The current check of heap_type can deterministically infer + * the result of the previous condition + * `(!is_byte_a_type(type1) || + * wasm_is_type_multi_byte_type(type1))`. Therefore, the + * original condition is redundant and has been removed. + * + * This logic is consistent with the implementation of the + * `WASM_OP_REF_NULL` case in the `wasm_loader_prepare_bytecode` + * function. + */ + + if (heap_type >= 0) { + if (!check_type_index(module, module->type_count, heap_type, + error_buf, error_buf_size)) { + goto fail; + } + wasm_set_refheaptype_typeidx(&cur_ref_type.ref_ht_typeidx, + true, heap_type); + if (!push_const_expr_stack(&const_expr_ctx, flag, + cur_ref_type.ref_type, + &cur_ref_type, 0, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + } + else { + if (!wasm_is_valid_heap_type(heap_type)) { + set_error_buf_v(error_buf, error_buf_size, + "unknown type %d", heap_type); + goto fail; + } + cur_ref_type.ref_ht_common.ref_type = + (uint8)((int32)0x80 + heap_type); + if (!push_const_expr_stack(&const_expr_ctx, flag, + cur_ref_type.ref_type, NULL, 0, + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + } +#endif +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + + /* get_global */ + case INIT_EXPR_TYPE_GET_GLOBAL: + { + uint32 global_idx; + uint8 global_type; + + read_leb_uint32(p, p_end, cur_value.global_index); + global_idx = cur_value.global_index; + + /* + * Currently, constant expressions occurring as initializers + * of globals are further constrained in that contained + * global.get instructions are + * only allowed to refer to imported globals. + * + * https://webassembly.github.io/spec/core/valid/instructions.html#constant-expressions + */ + if (global_idx >= module->import_global_count + /* make spec test happy */ +#if WASM_ENABLE_GC != 0 + + module->global_count +#endif + ) { + set_error_buf_v(error_buf, error_buf_size, + "unknown global %u", global_idx); + goto fail; + } + if ( + /* make spec test happy */ +#if WASM_ENABLE_GC != 0 + global_idx < module->import_global_count && +#endif + module->import_globals[global_idx] + .u.global.type.is_mutable) { + set_error_buf_v(error_buf, error_buf_size, + "constant expression required"); + goto fail; + } - if (!(import->u.function.func_ptr_linked = - resolve_sym(module_name, field_name))) { -#if WASM_ENABLE_WAMR_COMPILER == 0 /* Output warning except running aot compiler */ - LOG_WARNING("warning: fail to link import function (%s, %s)\n", - module_name, field_name); + if (global_idx < module->import_global_count) { + global_type = module->import_globals[global_idx] + .u.global.type.val_type; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(global_type)) { + WASMRefType *global_ref_type = + module->import_globals[global_idx] + .u.global.ref_type; + bh_memcpy_s(&cur_ref_type, + wasm_reftype_struct_size(global_ref_type), + global_ref_type, + wasm_reftype_struct_size(global_ref_type)); + } #endif + } + else { + global_type = + module + ->globals[global_idx - module->import_global_count] + .type.val_type; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(global_type)) { + WASMRefType *global_ref_type = + module + ->globals[global_idx + - module->import_global_count] + .ref_type; + bh_memcpy_s(&cur_ref_type, + wasm_reftype_struct_size(global_ref_type), + global_ref_type, + wasm_reftype_struct_size(global_ref_type)); } - break; +#endif + } - case IMPORT_KIND_TABLE: /* import table */ - bh_assert(import_tables); - import = import_tables++; - if (!load_table_import(&p, p_end, &import->u.table, - error_buf, error_buf_size)) - return false; - if (module->import_table_count > 1) { - set_error_buf(error_buf, error_buf_size, "multiple tables"); - return false; + if (!push_const_expr_stack(&const_expr_ctx, flag, global_type, +#if WASM_ENABLE_GC != 0 + &cur_ref_type, 0, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + + break; + } + +#if WASM_ENABLE_GC != 0 + /* struct.new and array.new */ + case WASM_OP_GC_PREFIX: + { + read_leb_uint32(p, p_end, opcode1); + + switch (opcode1) { + case WASM_OP_STRUCT_NEW: + { + WASMStructType *struct_type; + WASMStructNewInitValues *struct_init_values = NULL; + uint32 field_count; + read_leb_uint32(p, p_end, type_idx); + + if (!check_type_index(module, module->type_count, + type_idx, error_buf, + error_buf_size)) { + goto fail; + } + + struct_type = (WASMStructType *)module->types[type_idx]; + if (struct_type->base_type.type_flag + != WASM_TYPE_STRUCT) { + set_error_buf(error_buf, error_buf_size, + "unknown struct type"); + goto fail; + } + field_count = struct_type->field_count; + + if (!(struct_init_values = loader_malloc( + offsetof(WASMStructNewInitValues, fields) + + (uint64)field_count * sizeof(WASMValue), + error_buf, error_buf_size))) { + goto fail; + } + struct_init_values->type_idx = type_idx; + struct_init_values->count = field_count; + + for (i = field_count; i > 0; i--) { + WASMRefType *field_ref_type = NULL; + uint32 field_idx = i - 1; + uint8 field_type = + struct_type->fields[field_idx].field_type; + if (wasm_is_type_multi_byte_type(field_type)) { + field_ref_type = wasm_reftype_map_find( + struct_type->ref_type_maps, + struct_type->ref_type_map_count, field_idx); + } + + if (is_packed_type(field_type)) { + field_type = VALUE_TYPE_I32; + } + + if (!pop_const_expr_stack( + &const_expr_ctx, NULL, field_type, + field_ref_type, NULL, + &struct_init_values->fields[field_idx], +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + destroy_init_expr_data_recursive( + module, struct_init_values); + goto fail; + } + } + + cur_value.data = struct_init_values; + wasm_set_refheaptype_typeidx( + &cur_ref_type.ref_ht_typeidx, false, type_idx); + if (!push_const_expr_stack( + &const_expr_ctx, flag, cur_ref_type.ref_type, + &cur_ref_type, (uint8)opcode1, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + destroy_init_expr_data_recursive( + module, struct_init_values); + goto fail; + } + break; } - break; + case WASM_OP_STRUCT_NEW_DEFAULT: + { + read_leb_uint32(p, p_end, cur_value.type_index); + type_idx = cur_value.type_index; + + if (!check_type_index(module, module->type_count, + type_idx, error_buf, + error_buf_size)) { + goto fail; + } + if (module->types[type_idx]->type_flag + != WASM_TYPE_STRUCT) { + set_error_buf(error_buf, error_buf_size, + "unknown struct type"); + goto fail; + } - case IMPORT_KIND_MEMORY: /* import memory */ - bh_assert(import_memories); - import = import_memories++; - if (!load_memory_import(&p, p_end, &import->u.memory, - error_buf, error_buf_size)) - return false; - if (module->import_memory_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: multiple memories"); - return false; + cur_value.type_index = type_idx; + cur_value.data = NULL; + wasm_set_refheaptype_typeidx( + &cur_ref_type.ref_ht_typeidx, false, type_idx); + if (!push_const_expr_stack( + &const_expr_ctx, flag, cur_ref_type.ref_type, + &cur_ref_type, (uint8)opcode1, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; } - break; + case WASM_OP_ARRAY_NEW: + case WASM_OP_ARRAY_NEW_DEFAULT: + case WASM_OP_ARRAY_NEW_FIXED: + { + WASMArrayNewInitValues *array_init_values = NULL; + WASMArrayType *array_type = NULL; + WASMRefType *elem_ref_type = NULL; + uint64 total_size; + uint8 elem_type; + + read_leb_uint32(p, p_end, cur_value.type_index); + type_idx = cur_value.type_index; + + if (!check_type_index(module, module->type_count, + type_idx, error_buf, + error_buf_size)) { + goto fail; + } - case IMPORT_KIND_GLOBAL: /* import global */ - bh_assert(import_globals); - import = import_globals++; - CHECK_BUF(p, p_end, 2); - import->u.global.type = read_uint8(p); - mutable = read_uint8(p); - if (mutable >= 2) { + array_type = (WASMArrayType *)module->types[type_idx]; + if (array_type->base_type.type_flag + != WASM_TYPE_ARRAY) { + set_error_buf(error_buf, error_buf_size, + "unknown array type"); + goto fail; + } + + if (opcode1 != WASM_OP_ARRAY_NEW_DEFAULT) { + elem_type = array_type->elem_type; + if (wasm_is_type_multi_byte_type(elem_type)) { + elem_ref_type = array_type->elem_ref_type; + } + + if (is_packed_type(elem_type)) { + elem_type = VALUE_TYPE_I32; + } + + if (opcode1 == WASM_OP_ARRAY_NEW) { + WASMValue len_val = { 0 }; + uint64 size = 0; + + if (!pop_const_expr_stack( + &const_expr_ctx, NULL, VALUE_TYPE_I32, + NULL, NULL, &len_val, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + + size = + sizeof(WASMArrayNewInitValues) + + sizeof(WASMValue) * (uint64)len_val.i32; + if (!(array_init_values = loader_malloc( + size, error_buf, error_buf_size))) { + goto fail; + } + + array_init_values->type_idx = type_idx; + array_init_values->length = len_val.i32; + + if (!pop_const_expr_stack( + &const_expr_ctx, NULL, elem_type, + elem_ref_type, NULL, + &array_init_values->elem_data[0], +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + destroy_init_expr_data_recursive( + module, array_init_values); + goto fail; + } + + cur_value.data = array_init_values; + } + else { + /* WASM_OP_ARRAY_NEW_FIXED */ + uint32 len; + read_leb_uint32(p, p_end, len); + + total_size = + (uint64)offsetof(WASMArrayNewInitValues, + elem_data) + + (uint64)sizeof(WASMValue) * len; + if (!(array_init_values = + loader_malloc(total_size, error_buf, + error_buf_size))) { + goto fail; + } + + array_init_values->type_idx = type_idx; + array_init_values->length = len; + + for (i = len; i > 0; i--) { + if (!pop_const_expr_stack( + &const_expr_ctx, NULL, elem_type, + elem_ref_type, NULL, + &array_init_values + ->elem_data[i - 1], +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + destroy_init_expr_data_recursive( + module, array_init_values); + goto fail; + } + } + + cur_value.data = array_init_values; + } + } + else { + /* WASM_OP_ARRAY_NEW_DEFAULT */ + WASMValue len_val; + uint32 len; + + /* POP(i32) */ + if (!pop_const_expr_stack( + &const_expr_ctx, NULL, VALUE_TYPE_I32, NULL, + NULL, &len_val, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + len = len_val.i32; + + cur_value.array_new_default.type_index = type_idx; + cur_value.array_new_default.length = len; + } + + wasm_set_refheaptype_typeidx( + &cur_ref_type.ref_ht_typeidx, false, type_idx); + if (!push_const_expr_stack( + &const_expr_ctx, flag, cur_ref_type.ref_type, + &cur_ref_type, (uint8)opcode1, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + if (array_init_values) { + destroy_init_expr_data_recursive( + module, array_init_values); + } + goto fail; + } + break; + } + case WASM_OP_ANY_CONVERT_EXTERN: + { set_error_buf(error_buf, error_buf_size, - "Load import section failed: " - "invalid mutability"); - return false; + "unsupported constant expression of " + "extern.internalize"); + goto fail; } - import->u.global.is_mutable = mutable & 1 ? true : false; -#if WASM_ENABLE_LIBC_BUILTIN != 0 - if (!(wasm_native_lookup_libc_builtin_global( - module_name, field_name, - &import->u.global))) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "Load import section failed: " - "resolve import global (%s, %s) failed.", - module_name, field_name); - return false; + case WASM_OP_EXTERN_CONVERT_ANY: + { + set_error_buf(error_buf, error_buf_size, + "unsupported constant expression of " + "extern.externalize"); + goto fail; } + case WASM_OP_REF_I31: + { + /* POP(i32) */ + if (!pop_const_expr_stack(&const_expr_ctx, NULL, + VALUE_TYPE_I32, NULL, NULL, + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, #endif - break; + error_buf, error_buf_size)) { + goto fail; + } - default: - set_error_buf(error_buf, error_buf_size, - "Load import section failed: " - "invalid import type."); - return false; - } - import->kind = kind; - import->u.names.module_name = module_name; - import->u.names.field_name = field_name; - } + wasm_set_refheaptype_common(&cur_ref_type.ref_ht_common, + false, HEAP_TYPE_I31); + if (!push_const_expr_stack( + &const_expr_ctx, flag, cur_ref_type.ref_type, + &cur_ref_type, (uint8)opcode1, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + } + default: + set_error_buf( + error_buf, error_buf_size, + "type mismatch or constant expression required"); + goto fail; + } -#if WASM_ENABLE_LIBC_WASI != 0 - import = module->import_functions; - for (i = 0; i < module->import_function_count; i++, import++) { - if (!strcmp(import->u.names.module_name, "wasi_unstable")) { - module->is_wasi_module = true; break; } +#endif /* end of WASM_ENABLE_GC != 0 */ + default: + { + set_error_buf(error_buf, error_buf_size, + "illegal opcode " + "or constant expression required " + "or type mismatch"); + goto fail; + } } + + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + } + + /* There should be only one value left on the init value stack */ + if (!pop_const_expr_stack(&const_expr_ctx, &flag, type, +#if WASM_ENABLE_GC != 0 + ref_type, &opcode, +#endif + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + &cur_expr, #endif + error_buf, error_buf_size)) { + goto fail; } - if (p != p_end) { + if (const_expr_ctx.sp != 0) { set_error_buf(error_buf, error_buf_size, - "Load import section failed: section size mismatch"); - return false; + "type mismatch: illegal constant opcode sequence"); + goto fail; } - LOG_VERBOSE("Load import section success.\n"); - (void)u8; - (void)u32; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + if (cur_expr != NULL) { + bh_memcpy_s(init_expr, sizeof(InitializerExpression), cur_expr, + sizeof(InitializerExpression)); + wasm_runtime_free(cur_expr); + } + else { + init_expr->init_expr_type = flag; + init_expr->u.unary.v = cur_value; + } + +#else + init_expr->init_expr_type = flag; + init_expr->u.unary.v = cur_value; +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */ + +#if WASM_ENABLE_GC != 0 + if (init_expr->init_expr_type == WASM_OP_GC_PREFIX) { + switch (opcode) { + case WASM_OP_STRUCT_NEW: + init_expr->init_expr_type = INIT_EXPR_TYPE_STRUCT_NEW; + break; + case WASM_OP_STRUCT_NEW_DEFAULT: + init_expr->init_expr_type = INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT; + break; + case WASM_OP_ARRAY_NEW: + init_expr->init_expr_type = INIT_EXPR_TYPE_ARRAY_NEW; + break; + case WASM_OP_ARRAY_NEW_DEFAULT: + init_expr->init_expr_type = INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT; + break; + case WASM_OP_ARRAY_NEW_FIXED: + init_expr->init_expr_type = INIT_EXPR_TYPE_ARRAY_NEW_FIXED; + break; + case WASM_OP_REF_I31: + init_expr->init_expr_type = INIT_EXPR_TYPE_I31_NEW; + break; + default: + bh_assert(0); + break; + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + *p_buf = p; + destroy_const_expr_stack(&const_expr_ctx, false); return true; + +fail: + destroy_const_expr_stack(&const_expr_ctx, true); + return false; } static bool -init_function_local_offsets(WASMFunction *func, - char *error_buf, uint32 error_buf_size) +check_mutability(uint8 mutable, char *error_buf, uint32 error_buf_size) { - WASMType *param_type = func->func_type; - uint32 param_count = param_type->param_count; - uint8 *param_types = param_type->types; - uint32 local_count = func->local_count; - uint8 *local_types = func->local_types; - uint32 i, local_offset = 0; - uint64 total_size = sizeof(uint16) * ((uint64)param_count + local_count); - - if (total_size >= UINT32_MAX - || !(func->local_offsets = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: allocate memory failed."); + if (mutable >= 2) { + set_error_buf(error_buf, error_buf_size, "invalid mutability"); return false; } + return true; +} - for (i = 0; i < param_count; i++) { - func->local_offsets[i] = (uint16)local_offset; - local_offset += wasm_value_type_cell_num(param_types[i]); +#if WASM_ENABLE_GC != 0 +static void +destroy_func_type(WASMFuncType *type) +{ + /* Destroy the reference type hash set */ + if (type->ref_type_maps) + wasm_runtime_free(type->ref_type_maps); + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + if (type->call_to_llvm_jit_from_fast_jit) + jit_code_cache_free(type->call_to_llvm_jit_from_fast_jit); +#endif + /* Free the type */ + wasm_runtime_free(type); +} + +static void +destroy_struct_type(WASMStructType *type) +{ + if (type->ref_type_maps) + wasm_runtime_free(type->ref_type_maps); + + wasm_runtime_free(type); +} + +static void +destroy_array_type(WASMArrayType *type) +{ + wasm_runtime_free(type); +} + +static void +destroy_wasm_type(WASMType *type) +{ + if (type->ref_count > 1) { + /* The type is referenced by other types + of current wasm module */ + type->ref_count--; + return; } - for (i = 0; i < local_count; i++) { - func->local_offsets[param_count + i] = (uint16)local_offset; - local_offset += wasm_value_type_cell_num(local_types[i]); + if (type->type_flag == WASM_TYPE_FUNC) + destroy_func_type((WASMFuncType *)type); + else if (type->type_flag == WASM_TYPE_STRUCT) + destroy_struct_type((WASMStructType *)type); + else if (type->type_flag == WASM_TYPE_ARRAY) + destroy_array_type((WASMArrayType *)type); + else { + bh_assert(0); } +} - bh_assert(local_offset == func->param_cell_num + func->local_cell_num); +/* Resolve (ref null ht) or (ref ht) */ +static bool +resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, uint32 type_count, bool nullable, + WASMRefType *ref_type, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + + ref_type->ref_type = + nullable ? REF_TYPE_HT_NULLABLE : REF_TYPE_HT_NON_NULLABLE; + ref_type->ref_ht_common.nullable = nullable; + read_leb_int32(p, p_end, ref_type->ref_ht_common.heap_type); + + if (wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common)) { + /* heap type is (type i), i : typeidx, >= 0 */ + if (!check_type_index(module, type_count, + ref_type->ref_ht_typeidx.type_idx, error_buf, + error_buf_size)) { + return false; + } + } + else if (!wasm_is_refheaptype_common(&ref_type->ref_ht_common)) { + /* heap type is func, extern, any, eq, i31 or data */ + set_error_buf(error_buf, error_buf_size, "unknown heap type"); + return false; + } + + *p_buf = p; return true; +fail: + return false; } static bool -load_function_section(const uint8 *buf, const uint8 *buf_end, - const uint8 *buf_code, const uint8 *buf_code_end, - WASMModule *module, - char *error_buf, uint32 error_buf_size) +resolve_value_type(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, uint32 type_count, + bool *p_need_ref_type_map, WASMRefType *ref_type, + bool allow_packed_type, char *error_buf, + uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - const uint8 *p_code = buf_code, *p_code_end, *p_code_save; - uint32 func_count; - uint64 total_size; - uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; - uint32 local_count, local_set_count, sub_local_count; + const uint8 *p = *p_buf, *p_end = buf_end; uint8 type; - WASMFunction *func; - read_leb_uint32(p, p_end, func_count); + memset(ref_type, 0, sizeof(WASMRefType)); - if (buf_code) - read_leb_uint32(p_code, buf_code_end, code_count); + CHECK_BUF(p, p_end, 1); + type = read_uint8(p); - if (func_count != code_count) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "function and code section have inconsistent lengths"); - return false; + if (wasm_is_reftype_htref_nullable(type)) { + /* (ref null ht) */ + if (!resolve_reftype_htref(&p, p_end, module, type_count, true, + ref_type, error_buf, error_buf_size)) + return false; + if (!wasm_is_refheaptype_common(&ref_type->ref_ht_common)) + *p_need_ref_type_map = true; + else { + /* For (ref null func/extern/any/eq/i31/data), they are same as + funcref/externref/anyref/eqref/i31ref/dataref, we convert the + multi-byte type to one-byte type to reduce the footprint and + the complexity of type equal/subtype checking */ + ref_type->ref_type = + (uint8)((int32)0x80 + ref_type->ref_ht_common.heap_type); + *p_need_ref_type_map = false; + } } - - if (func_count) { - module->function_count = func_count; - total_size = sizeof(WASMFunction*) * (uint64)func_count; - if (total_size >= UINT32_MAX - || !(module->functions = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: allocate memory failed."); + else if (wasm_is_reftype_htref_non_nullable(type)) { + /* (ref ht) */ + if (!resolve_reftype_htref(&p, p_end, module, type_count, false, + ref_type, error_buf, error_buf_size)) return false; + *p_need_ref_type_map = true; +#if WASM_ENABLE_STRINGREF != 0 + /* covert (ref string) to stringref */ + if (wasm_is_refheaptype_stringrefs(&ref_type->ref_ht_common)) { + ref_type->ref_type = + (uint8)((int32)0x80 + ref_type->ref_ht_common.heap_type); + *p_need_ref_type_map = false; } - - memset(module->functions, 0, (uint32)total_size); - - for (i = 0; i < func_count; i++) { - /* Resolve function type */ - read_leb_uint32(p, p_end, type_index); - if (type_index >= module->type_count) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "function type index out of range."); - return false; - } - - read_leb_uint32(p_code, buf_code_end, code_size); - if (code_size == 0 - || p_code + code_size > buf_code_end) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "invalid function code size."); - return false; - } - - /* Resolve local set count */ - p_code_end = p_code + code_size; - local_count = 0; - read_leb_uint32(p_code, buf_code_end, local_set_count); - p_code_save = p_code; - - /* Calculate total local count */ - for (j = 0; j < local_set_count; j++) { - read_leb_uint32(p_code, buf_code_end, sub_local_count); - if (sub_local_count > UINT32_MAX - local_count) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "too many locals"); - return false; - } - CHECK_BUF(p_code, buf_code_end, 1); - /* 0x7F/0x7E/0x7D/0x7C */ - type = read_uint8(p_code); - local_count += sub_local_count; - } - - /* Alloc memory, layout: function structure + local types */ - code_size = (uint32)(p_code_end - p_code); - - total_size = sizeof(WASMFunction) + (uint64)local_count; - if (total_size >= UINT32_MAX - || !(func = module->functions[i] = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "allocate memory failed."); - return false; - } - - /* Set function type, local count, code size and code body */ - memset(func, 0, (uint32)total_size); - func->func_type = module->types[type_index]; - func->local_count = local_count; - if (local_count > 0) - func->local_types = (uint8*)func + sizeof(WASMFunction); - func->code_size = code_size; - func->code = (uint8*)p_code; - - /* Load each local type */ - p_code = p_code_save; - local_type_index = 0; - for (j = 0; j < local_set_count; j++) { - read_leb_uint32(p_code, buf_code_end, sub_local_count); - if (local_type_index + sub_local_count <= local_type_index - || local_type_index + sub_local_count > local_count) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "invalid local count."); - return false; - } - CHECK_BUF(p_code, buf_code_end, 1); - /* 0x7F/0x7E/0x7D/0x7C */ - type = read_uint8(p_code); - if (type < VALUE_TYPE_F64 || type > VALUE_TYPE_I32) { - set_error_buf(error_buf, error_buf_size, - "Load function section failed: " - "invalid local type."); - return false; - } - for (k = 0; k < sub_local_count; k++) { - func->local_types[local_type_index++] = type; - } - } - - func->param_cell_num = wasm_type_param_cell_num(func->func_type); - func->ret_cell_num = wasm_type_return_cell_num(func->func_type); - func->local_cell_num = - wasm_get_cell_num(func->local_types, func->local_count); - - if (!init_function_local_offsets(func, error_buf, error_buf_size)) - return false; - - p_code = p_code_end; +#endif + } + else { + /* type which can be represented by one byte */ + if (!is_valid_value_type_for_interpreter(type) + && !(allow_packed_type && is_packed_type(type))) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; } + ref_type->ref_type = type; + *p_need_ref_type_map = false; +#if WASM_ENABLE_WAMR_COMPILER != 0 + /* If any value's type is v128, mark the module as SIMD used */ + if (type == VALUE_TYPE_V128) + module->is_simd_used = true; +#endif } - if (p != p_end) { + *p_buf = p; + return true; +fail: + return false; +} + +static WASMRefType * +reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type, + char *error_buf, uint32 error_buf_size) +{ + WASMRefType *ret = wasm_reftype_set_insert(ref_type_set, ref_type); + + if (!ret) { set_error_buf(error_buf, error_buf_size, - "Load function section failed: section size mismatch"); - return false; + "insert ref type to hash set failed"); } - - LOG_VERBOSE("Load function section success.\n"); - return true; + return ret; } static bool -load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, + uint32 type_count, uint32 type_idx, char *error_buf, + uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - uint32 table_count, i; + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + uint32 param_count, result_count, i, j = 0; + uint32 param_cell_num, ret_cell_num; + uint32 ref_type_map_count = 0, result_ref_type_map_count = 0; uint64 total_size; - WASMTable *table; - - read_leb_uint32(p, p_end, table_count); - bh_assert(table_count == 1); + bool need_ref_type_map; + WASMRefType ref_type; + WASMFuncType *type = NULL; + + /* Parse first time to resolve param count, result count and + ref type map count */ + read_leb_uint32(p, p_end, param_count); + p_org = p; + for (i = 0; i < param_count; i++) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { + return false; + } + if (need_ref_type_map) + ref_type_map_count++; + } - if (table_count) { - if (table_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load table section failed: multiple memories"); + read_leb_uint32(p, p_end, result_count); + for (i = 0; i < result_count; i++) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { return false; } - module->table_count = table_count; - total_size = sizeof(WASMTable) * (uint64)table_count; - if (total_size >= UINT32_MAX - || !(module->tables = wasm_malloc((uint32)total_size))) { + if (need_ref_type_map) { + ref_type_map_count++; + result_ref_type_map_count++; + } + } + + LOG_VERBOSE("type %u: func, param count: %d, result count: %d, " + "ref type map count: %d", + type_idx, param_count, result_count, ref_type_map_count); + + /* Parse second time to resolve param types, result types and + ref type map info */ + p = p_org; + + total_size = offsetof(WASMFuncType, types) + + sizeof(uint8) * (uint64)(param_count + result_count); + if (!(type = loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + if (ref_type_map_count > 0) { + if (ref_type_map_count > UINT16_MAX) { set_error_buf(error_buf, error_buf_size, - "Load table section failed: allocate memory failed."); + "ref type count too large"); return false; } + total_size = sizeof(WASMRefTypeMap) * (uint64)ref_type_map_count; + if (!(type->ref_type_maps = + loader_malloc(total_size, error_buf, error_buf_size))) { + goto fail; + } + } - memset(module->tables, 0, (uint32)total_size); + type->base_type.type_flag = WASM_TYPE_FUNC; + type->param_count = param_count; + type->result_count = result_count; + type->ref_type_map_count = ref_type_map_count; + if (ref_type_map_count > 0) { + type->result_ref_type_maps = type->ref_type_maps + ref_type_map_count + - result_ref_type_map_count; + } - /* load each table */ - table = module->tables; - for (i = 0; i < table_count; i++, table++) - if (!load_table(&p, p_end, table, error_buf, error_buf_size)) - return false; + for (i = 0; i < param_count; i++) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { + goto fail; + } + type->types[i] = ref_type.ref_type; + if (need_ref_type_map) { + type->ref_type_maps[j].index = i; + if (!(type->ref_type_maps[j++].ref_type = + reftype_set_insert(module->ref_type_set, &ref_type, + error_buf, error_buf_size))) { + goto fail; + } + } } - if (p != p_end) { + read_leb_uint32(p, p_end, result_count); + for (i = 0; i < result_count; i++) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { + goto fail; + } + type->types[param_count + i] = ref_type.ref_type; + if (need_ref_type_map) { + type->ref_type_maps[j].index = param_count + i; + if (!(type->ref_type_maps[j++].ref_type = + reftype_set_insert(module->ref_type_set, &ref_type, + error_buf, error_buf_size))) { + goto fail; + } + } + } + + bh_assert(j == type->ref_type_map_count); +#if TRACE_WASM_LOADER != 0 + os_printf("type %d = ", type_idx); + wasm_dump_func_type(type); +#endif + + param_cell_num = wasm_get_cell_num(type->types, param_count); + ret_cell_num = wasm_get_cell_num(type->types + param_count, result_count); + if (param_cell_num > UINT16_MAX || ret_cell_num > UINT16_MAX) { set_error_buf(error_buf, error_buf_size, - "Load table section failed: section size mismatch"); - return false; + "param count or result count too large"); + goto fail; } + type->param_cell_num = (uint16)param_cell_num; + type->ret_cell_num = (uint16)ret_cell_num; - LOG_VERBOSE("Load table section success.\n"); +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + type->quick_aot_entry = wasm_native_lookup_quick_aot_entry(type); +#endif + +#if WASM_ENABLE_WAMR_COMPILER != 0 + for (i = 0; i < (uint32)(type->param_count + type->result_count); i++) { + if (type->types[i] == VALUE_TYPE_V128) + module->is_simd_used = true; + } +#endif + + *p_buf = p; + + module->types[type_idx] = (WASMType *)type; return true; + +fail: + if (type) + destroy_func_type(type); + return false; } static bool -load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, +resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, uint32 type_count, uint32 type_idx, char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - uint32 memory_count, i; + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + uint32 field_count, ref_type_map_count = 0, ref_field_count = 0; + uint32 i, j = 0, offset; + uint16 *reference_table; uint64 total_size; - WASMMemory *memory; + uint8 mutable; + bool need_ref_type_map; + WASMRefType ref_type; + WASMStructType *type = NULL; + + /* Parse first time to resolve field count and ref type map count */ + read_leb_uint32(p, p_end, field_count); + p_org = p; + for (i = 0; i < field_count; i++) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, true, error_buf, + error_buf_size)) { + return false; + } + if (need_ref_type_map) + ref_type_map_count++; - read_leb_uint32(p, p_end, memory_count); - bh_assert(memory_count == 1); + if (wasm_is_reftype_anyref(ref_type.ref_type)) { + LOG_ERROR("Not support using anyref in struct fields"); + return false; + } - if (memory_count) { - if (memory_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load memory section failed: multiple memories"); + if (wasm_is_type_reftype(ref_type.ref_type)) + ref_field_count++; + + CHECK_BUF(p, p_end, 1); + mutable = read_uint8(p); + if (!check_mutability(mutable, error_buf, error_buf_size)) { return false; } - module->memory_count = memory_count; - total_size = sizeof(WASMMemory) * (uint64)memory_count; - if (total_size >= UINT32_MAX - || !(module->memories = wasm_malloc((uint32)total_size))) { + } + + LOG_VERBOSE("type %u: struct, field count: %d, ref type map count: %d", + type_idx, field_count, ref_type_map_count); + + /* Parse second time to resolve field types and ref type map info */ + p = p_org; + + total_size = offsetof(WASMStructType, fields) + + sizeof(WASMStructFieldType) * (uint64)field_count + + sizeof(uint16) * (uint64)(ref_field_count + 1); + if (!(type = loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + if (ref_type_map_count > 0) { + if (ref_type_map_count > UINT16_MAX) { set_error_buf(error_buf, error_buf_size, - "Load memory section failed: allocate memory failed."); + "ref type count too large"); return false; } + total_size = sizeof(WASMRefTypeMap) * (uint64)ref_type_map_count; + if (!(type->ref_type_maps = + loader_malloc(total_size, error_buf, error_buf_size))) { + goto fail; + } + } - memset(module->memories, 0, (uint32)total_size); + type->reference_table = reference_table = + (uint16 *)((uint8 *)type + offsetof(WASMStructType, fields) + + sizeof(WASMStructFieldType) * field_count); + *reference_table++ = ref_field_count; + + type->base_type.type_flag = WASM_TYPE_STRUCT; + type->field_count = field_count; + type->ref_type_map_count = ref_type_map_count; + + offset = (uint32)offsetof(WASMStructObject, field_data); + for (i = 0; i < field_count; i++) { + if (!resolve_value_type(&p, p_end, module, type_count, + &need_ref_type_map, &ref_type, true, error_buf, + error_buf_size)) { + goto fail; + } + if (!is_valid_field_type(ref_type.ref_type)) { + set_error_buf(error_buf, error_buf_size, "invalid field type"); + goto fail; + } + type->fields[i].field_type = ref_type.ref_type; + if (need_ref_type_map) { + type->ref_type_maps[j].index = i; + if (!(type->ref_type_maps[j++].ref_type = + reftype_set_insert(module->ref_type_set, &ref_type, + error_buf, error_buf_size))) { + goto fail; + } + } - /* load each memory */ - memory = module->memories; - for (i = 0; i < memory_count; i++, memory++) - if (!load_memory(&p, p_end, memory, error_buf, error_buf_size)) - return false; - } + CHECK_BUF(p, p_end, 1); + type->fields[i].field_flags = read_uint8(p); + type->fields[i].field_size = + (uint8)wasm_reftype_size(ref_type.ref_type); +#if !(defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_X86_32)) + if (type->fields[i].field_size == 2) + offset = align_uint(offset, 2); + else if (type->fields[i].field_size >= 4) /* field size is 4 or 8 */ + offset = align_uint(offset, 4); +#endif + type->fields[i].field_offset = offset; + if (wasm_is_type_reftype(ref_type.ref_type)) + *reference_table++ = offset; + offset += type->fields[i].field_size; - if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load memory section failed: section size mismatch"); - return false; + LOG_VERBOSE(" field: %d, flags: %d, type: %d", i, + type->fields[i].field_flags, type->fields[i].field_type); } + type->total_size = offset; - LOG_VERBOSE("Load memory section success.\n"); + bh_assert(j == type->ref_type_map_count); +#if TRACE_WASM_LOADER != 0 + os_printf("type %d = ", type_idx); + wasm_dump_struct_type(type); +#endif + + *p_buf = p; + + module->types[type_idx] = (WASMType *)type; return true; + +fail: + if (type) + destroy_struct_type(type); + return false; } static bool -load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +resolve_array_type(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, uint32 type_count, uint32 type_idx, + char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - uint32 global_count, i; - uint64 total_size; - WASMGlobal *global; + const uint8 *p = *p_buf, *p_end = buf_end; uint8 mutable; + bool need_ref_type_map; + WASMRefType ref_type; + WASMArrayType *type = NULL; - read_leb_uint32(p, p_end, global_count); + if (!resolve_value_type(&p, p_end, module, type_count, &need_ref_type_map, + &ref_type, true, error_buf, error_buf_size)) { + return false; + } - if (global_count) { - module->global_count = global_count; - total_size = sizeof(WASMGlobal) * (uint64)global_count; - if (total_size >= UINT32_MAX - || !(module->globals = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load global section failed: " - "allocate memory failed."); - return false; - } + if (wasm_is_reftype_anyref(ref_type.ref_type)) { + LOG_ERROR("Not support using anyref in array element type"); + return false; + } - memset(module->globals, 0, (uint32)total_size); + CHECK_BUF(p, p_end, 1); + mutable = read_uint8(p); + if (!check_mutability(mutable, error_buf, error_buf_size)) { + return false; + } - global = module->globals; + LOG_VERBOSE("type %u: array", type_idx); - for(i = 0; i < global_count; i++, global++) { - CHECK_BUF(p, p_end, 2); - global->type = read_uint8(p); - mutable = read_uint8(p); - if (mutable >= 2) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: " - "invalid mutability"); - return false; - } - global->is_mutable = mutable ? true : false; + if (!(type = loader_malloc(sizeof(WASMArrayType), error_buf, + error_buf_size))) { + return false; + } - /* initialize expression */ - if (!load_init_expr(&p, p_end, &(global->init_expr), error_buf, error_buf_size)) - return false; + type->base_type.type_flag = WASM_TYPE_ARRAY; + type->elem_flags = mutable; + type->elem_type = ref_type.ref_type; + if (need_ref_type_map) { + if (!(type->elem_ref_type = + reftype_set_insert(module->ref_type_set, &ref_type, error_buf, + error_buf_size))) { + goto fail; } } - if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load global section failed: section size mismatch"); - return false; - } +#if TRACE_WASM_LOADER != 0 + os_printf("type %d = ", type_idx); + wasm_dump_array_type(type); +#endif - LOG_VERBOSE("Load global section success.\n"); + *p_buf = p; + + module->types[type_idx] = (WASMType *)type; return true; + +fail: + if (type) + destroy_array_type(type); + return false; } static bool -load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +init_ref_type(WASMModule *module, WASMRefType *ref_type, bool nullable, + int32 heap_type, char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - uint32 export_count, i, index; - uint64 total_size; - uint32 str_len; - WASMExport *export; - - read_leb_uint32(p, p_end, export_count); - - if (export_count) { - module->export_count = export_count; - total_size = sizeof(WASMExport) * (uint64)export_count; - if (total_size >= UINT32_MAX - || !(module->exports = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load export section failed: " - "allocate memory failed."); + if (heap_type >= 0) { + if (!check_type_index(module, module->type_count, heap_type, error_buf, + error_buf_size)) { return false; } - - memset(module->exports, 0, (uint32)total_size); - - export = module->exports; - for (i = 0; i < export_count; i++, export++) { - read_leb_uint32(p, p_end, str_len); - CHECK_BUF(p, p_end, str_len); - if (!(export->name = const_str_list_insert(p, str_len, module, - error_buf, error_buf_size))) { - return false; - } - p += str_len; - CHECK_BUF(p, p_end, 1); - export->kind = read_uint8(p); - read_leb_uint32(p, p_end, index); - export->index = index; - - switch(export->kind) { - /*function index*/ - case EXPORT_KIND_FUNC: - if (index >= module->function_count + module->import_function_count) { - set_error_buf(error_buf, error_buf_size, - "Load export section failed: " - "function index out of range."); - return false; - } - break; - /*table index*/ - case EXPORT_KIND_TABLE: - if (index >= module->table_count + module->import_table_count) { - set_error_buf(error_buf, error_buf_size, - "Load export section failed: " - "table index out of range."); - return false; - } - break; - /*memory index*/ - case EXPORT_KIND_MEMORY: - if (index >= module->memory_count + module->import_memory_count) { - set_error_buf(error_buf, error_buf_size, - "Load export section failed: " - "memory index out of range."); - return false; - } - break; - /*global index*/ - case EXPORT_KIND_GLOBAL: - if (index >= module->global_count + module->import_global_count) { - set_error_buf(error_buf, error_buf_size, - "Load export section failed: " - "global index out of range."); - return false; - } - break; - default: - set_error_buf(error_buf, error_buf_size, - "Load export section failed: " - "invalid export kind."); - return false; - } + wasm_set_refheaptype_typeidx(&ref_type->ref_ht_typeidx, nullable, + heap_type); + } + else { + if (!wasm_is_valid_heap_type(heap_type)) { + set_error_buf(error_buf, error_buf_size, "unknown type"); + return false; + } + wasm_set_refheaptype_common(&ref_type->ref_ht_common, nullable, + heap_type); + if (nullable) { + /* For (ref null func/extern/any/eq/i31/data), + they are same as + funcref/externref/anyref/eqref/i31ref/dataref, + we convert the multi-byte type to one-byte + type to reduce the footprint and the + complexity of type equal/subtype checking */ + ref_type->ref_type = + (uint8)((int32)0x80 + ref_type->ref_ht_common.heap_type); } } + return true; +} - if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load export section failed: section size mismatch"); - return false; +static void +calculate_reftype_diff(WASMRefType *ref_type_diff, WASMRefType *ref_type1, + WASMRefType *ref_type2) +{ + /** + * The difference rt1 ∖ rt2 between two reference types is defined as + * follows: + * (ref null?1 ht1) ∖ (ref null ht2) = (ref ht1) (ref null?1 ht1) ∖ + * (ref ht2) = (ref null?1 ht1) + */ + if (wasm_is_type_multi_byte_type(ref_type1->ref_type)) { + bh_memcpy_s(ref_type_diff, wasm_reftype_struct_size(ref_type1), + ref_type1, wasm_reftype_struct_size(ref_type1)); + } + else { + ref_type_diff->ref_type = ref_type1->ref_type; } - LOG_VERBOSE("Load export section success.\n"); - return true; + if (ref_type2->ref_ht_common.nullable) { + if (wasm_is_type_reftype(ref_type_diff->ref_type) + && !(wasm_is_type_multi_byte_type(ref_type_diff->ref_type))) { + wasm_set_refheaptype_typeidx(&ref_type_diff->ref_ht_typeidx, false, + (int32)ref_type_diff->ref_type - 0x80); + } + else { + ref_type_diff->ref_ht_typeidx.nullable = false; + } + } +} +#else /* else of WASM_ENABLE_GC != 0 */ +static void +destroy_wasm_type(WASMType *type) +{ + if (type->ref_count > 1) { + /* The type is referenced by other types + of current wasm module */ + type->ref_count--; + return; + } + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + if (type->call_to_llvm_jit_from_fast_jit) + jit_code_cache_free(type->call_to_llvm_jit_from_fast_jit); +#endif + + wasm_runtime_free(type); } +#endif /* end of WASM_ENABLE_GC != 0 */ static bool -load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) { const uint8 *p = buf, *p_end = buf_end; - uint32 table_segment_count, i, j, table_index, function_count, function_index; + uint32 type_count, i; uint64 total_size; - WASMTableSeg *table_segment; + uint8 flag; +#if WASM_ENABLE_GC != 0 + uint32 processed_type_count = 0; +#endif - read_leb_uint32(p, p_end, table_segment_count); + read_leb_uint32(p, p_end, type_count); - if (table_segment_count) { - module->table_seg_count = table_segment_count; - total_size = sizeof(WASMTableSeg) * (uint64)table_segment_count; - if (total_size >= UINT32_MAX - || !(module->table_segments = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load table segment section failed: " - "allocate memory failed."); + if (type_count) { + module->type_count = type_count; + total_size = sizeof(WASMType *) * (uint64)type_count; + if (!(module->types = + loader_malloc(total_size, error_buf, error_buf_size))) { return false; } - memset(module->table_segments, 0, (uint32)total_size); +#if WASM_ENABLE_GC == 0 + for (i = 0; i < type_count; i++) { + WASMFuncType *type; + const uint8 *p_org; + uint32 param_count, result_count, j; + uint32 param_cell_num, ret_cell_num; - table_segment = module->table_segments; - for (i = 0; i < table_segment_count; i++, table_segment++) { - if (p >= p_end) { + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + if (flag != 0x60) { + set_error_buf(error_buf, error_buf_size, "invalid type flag"); + return false; + } + + read_leb_uint32(p, p_end, param_count); + + /* Resolve param count and result count firstly */ + p_org = p; + CHECK_BUF(p, p_end, param_count); + p += param_count; + read_leb_uint32(p, p_end, result_count); + CHECK_BUF(p, p_end, result_count); + p = p_org; + + if (param_count > UINT16_MAX || result_count > UINT16_MAX) { set_error_buf(error_buf, error_buf_size, - "Load table segment section failed: " - "invalid value type"); + "param count or result count too large"); return false; } - read_leb_uint32(p, p_end, table_index); - table_segment->table_index = table_index; - /* initialize expression */ - if (!load_init_expr(&p, p_end, &(table_segment->base_offset), - error_buf, error_buf_size)) + total_size = offsetof(WASMFuncType, types) + + sizeof(uint8) * (uint64)(param_count + result_count); + if (!(type = module->types[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { return false; + } + + /* Resolve param types and result types */ + type->ref_count = 1; + type->param_count = (uint16)param_count; + type->result_count = (uint16)result_count; + for (j = 0; j < param_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[j] = read_uint8(p); + } + read_leb_uint32(p, p_end, result_count); + for (j = 0; j < result_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[param_count + j] = read_uint8(p); + } + for (j = 0; j < param_count + result_count; j++) { + if (!is_valid_value_type_for_interpreter(type->types[j])) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + return false; + } + } - read_leb_uint32(p, p_end, function_count); - table_segment->function_count = function_count; - total_size = sizeof(uint32) * (uint64)function_count; - if (total_size >= UINT32_MAX - || !(table_segment->func_indexes = (uint32 *) - wasm_malloc((uint32)total_size))) { + param_cell_num = wasm_get_cell_num(type->types, param_count); + ret_cell_num = + wasm_get_cell_num(type->types + param_count, result_count); + if (param_cell_num > UINT16_MAX || ret_cell_num > UINT16_MAX) { set_error_buf(error_buf, error_buf_size, - "Load table segment section failed: " - "allocate memory failed."); + "param count or result count too large"); return false; } - for (j = 0; j < function_count; j++) { - read_leb_uint32(p, p_end, function_index); - table_segment->func_indexes[j] = function_index; + type->param_cell_num = (uint16)param_cell_num; + type->ret_cell_num = (uint16)ret_cell_num; + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + type->quick_aot_entry = wasm_native_lookup_quick_aot_entry(type); +#endif + +#if WASM_ENABLE_WAMR_COMPILER != 0 + for (j = 0; j < type->param_count + type->result_count; j++) { + if (type->types[j] == VALUE_TYPE_V128) + module->is_simd_used = true; + else if (type->types[j] == VALUE_TYPE_FUNCREF + || type->types[j] == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; + } +#endif + + /* If there is already a same type created, use it instead */ + for (j = 0; j < i; j++) { + if (wasm_type_equal(type, module->types[j], module->types, i)) { + if (module->types[j]->ref_count == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "wasm type's ref count too large"); + return false; + } + destroy_wasm_type(type); + module->types[i] = module->types[j]; + module->types[j]->ref_count++; + break; + } } } - } +#else /* else of WASM_ENABLE_GC == 0 */ + for (i = 0; i < type_count; i++) { + uint32 super_type_count = 0, parent_type_idx = (uint32)-1; + uint32 rec_count = 1, j; + bool is_sub_final = true; - if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load table segment section failed: section size mismatch"); - return false; - } + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); - LOG_VERBOSE("Load table segment section success.\n"); - return true; -} + if (flag == DEFINED_TYPE_REC) { + read_leb_uint32(p, p_end, rec_count); -static bool -load_data_segment_section(const uint8 *buf, const uint8 *buf_end, - WASMModule *module, - char *error_buf, uint32 error_buf_size) -{ - const uint8 *p = buf, *p_end = buf_end; - uint32 data_seg_count, i, mem_index, data_seg_len; - uint64 total_size; - WASMDataSeg *dataseg; - InitializerExpression init_expr; + if (rec_count > 1) { + uint64 new_total_size; - read_leb_uint32(p, p_end, data_seg_count); + /* integer overflow */ + if (rec_count - 1 > UINT32_MAX - module->type_count) { + set_error_buf(error_buf, error_buf_size, + "recursive type count too large"); + return false; + } + new_total_size = + sizeof(WASMFuncType *) + * (uint64)(module->type_count + rec_count - 1); + if (new_total_size > UINT32_MAX) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return false; + } + MEM_REALLOC(module->types, (uint32)total_size, + (uint32)new_total_size); + module->type_count += rec_count - 1; + total_size = new_total_size; + } - if (data_seg_count) { - module->data_seg_count = data_seg_count; - total_size = sizeof(WASMDataSeg*) * (uint64)data_seg_count; - if (total_size >= UINT32_MAX - || !(module->data_segments = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Load data segment section failed: " - "allocate memory failed."); - return false; - } + if (rec_count < 1) { + LOG_VERBOSE("Processing 0-entry rec group"); + } + else { + LOG_VERBOSE("Processing rec group [%d-%d]", + processed_type_count, + processed_type_count + rec_count - 1); + } + } + else { + p--; + } - memset(module->data_segments, 0, (uint32)total_size); + for (j = 0; j < rec_count; j++) { + WASMType *cur_type = NULL; - for (i = 0; i < data_seg_count; i++) { - read_leb_uint32(p, p_end, mem_index); + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); - if (!load_init_expr(&p, p_end, &init_expr, error_buf, error_buf_size)) - return false; + parent_type_idx = -1; - read_leb_uint32(p, p_end, data_seg_len); + if (flag == DEFINED_TYPE_SUB + || flag == DEFINED_TYPE_SUB_FINAL) { + read_leb_uint32(p, p_end, super_type_count); + if (super_type_count > 1) { + set_error_buf(error_buf, error_buf_size, + "super type count too large"); + return false; + } - if (!(dataseg = module->data_segments[i] = - wasm_malloc((uint32)sizeof(WASMDataSeg)))) { - set_error_buf(error_buf, error_buf_size, - "Load data segment section failed: " - "allocate memory failed."); - return false; + if (super_type_count > 0) { + read_leb_uint32(p, p_end, parent_type_idx); + if (parent_type_idx >= processed_type_count + j) { + set_error_buf_v(error_buf, error_buf_size, + "unknown type %d", parent_type_idx); + return false; + } + if (module->types[parent_type_idx]->is_sub_final) { + set_error_buf(error_buf, error_buf_size, + "sub type can not inherit from " + "a final super type"); + return false; + } + } + + if (flag == DEFINED_TYPE_SUB) + is_sub_final = false; + + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + } + + if (flag == DEFINED_TYPE_FUNC) { + if (!resolve_func_type(&p, buf_end, module, + processed_type_count + rec_count, + processed_type_count + j, error_buf, + error_buf_size)) { + return false; + } + } + else if (flag == DEFINED_TYPE_STRUCT) { + if (!resolve_struct_type(&p, buf_end, module, + processed_type_count + rec_count, + processed_type_count + j, + error_buf, error_buf_size)) { + return false; + } + } + else if (flag == DEFINED_TYPE_ARRAY) { + if (!resolve_array_type(&p, buf_end, module, + processed_type_count + rec_count, + processed_type_count + j, error_buf, + error_buf_size)) { + return false; + } + } + else { + set_error_buf(error_buf, error_buf_size, + "invalid type flag"); + return false; + } + + cur_type = module->types[processed_type_count + j]; + + cur_type->ref_count = 1; + cur_type->parent_type_idx = parent_type_idx; + cur_type->is_sub_final = is_sub_final; + + cur_type->rec_count = rec_count; + cur_type->rec_idx = j; + cur_type->rec_begin_type_idx = processed_type_count; } - bh_memcpy_s(&dataseg->base_offset, sizeof(InitializerExpression), - &init_expr, sizeof(InitializerExpression)); + /* resolve subtyping relationship in current rec group */ + for (j = 0; j < rec_count; j++) { + WASMType *cur_type = module->types[processed_type_count + j]; - dataseg->memory_index = mem_index; - dataseg->data_length = data_seg_len; - CHECK_BUF(p, p_end, data_seg_len); - dataseg->data = (uint8*)p; - p += data_seg_len; + if (cur_type->parent_type_idx != (uint32)-1) { /* has parent */ + WASMType *parent_type = + module->types[cur_type->parent_type_idx]; + cur_type->parent_type = parent_type; + cur_type->root_type = parent_type->root_type; + if (parent_type->inherit_depth == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "parent type's inherit depth too large"); + return false; + } + cur_type->inherit_depth = parent_type->inherit_depth + 1; + } + else { + cur_type->parent_type = NULL; + cur_type->root_type = cur_type; + cur_type->inherit_depth = 0; + } + } + + for (j = 0; j < rec_count; j++) { + WASMType *cur_type = module->types[processed_type_count + j]; + + if (cur_type->parent_type_idx != (uint32)-1) { /* has parent */ + WASMType *parent_type = + module->types[cur_type->parent_type_idx]; + if (!wasm_type_is_subtype_of(cur_type, parent_type, + module->types, + module->type_count)) { + set_error_buf_v(error_buf, error_buf_size, + "sub type %u does not match super type", + processed_type_count + j); + return false; + } + } + } + + /* If there is already an equivalence type or a group of equivalence + recursive types created, use it or them instead */ + for (j = 0; j < processed_type_count;) { + WASMType *src_type = module->types[j]; + WASMType *cur_type = module->types[processed_type_count]; + uint32 k, src_rec_count; + + src_rec_count = src_type->rec_count; + if (src_rec_count != rec_count) { + /* no type equivalence */ + j += src_rec_count; + continue; + } + + for (k = 0; k < rec_count; k++) { + src_type = module->types[j + k]; + cur_type = module->types[processed_type_count + k]; + if (!wasm_type_equal(src_type, cur_type, module->types, + module->type_count)) { + break; + } + } + if (k < rec_count) { + /* no type equivalence */ + j += src_rec_count; + continue; + } + + /* type equivalence */ + for (k = 0; k < rec_count; k++) { + if (module->types[j + k]->ref_count == UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "wasm type's ref count too large"); + return false; + } + destroy_wasm_type(module->types[processed_type_count + k]); + module->types[processed_type_count + k] = + module->types[j + k]; + module->types[j + k]->ref_count++; + } + break; + } + + if (rec_count > 1) { + LOG_VERBOSE("Finished processing rec group [%d-%d]", + processed_type_count, + processed_type_count + rec_count - 1); + } + + processed_type_count += rec_count; + } + + if (!(module->rtt_types = loader_malloc((uint64)sizeof(WASMRttType *) + * module->type_count, + error_buf, error_buf_size))) { + return false; + } +#endif /* end of WASM_ENABLE_GC == 0 */ + } + + for (i = 0; i < module->type_count; i++) { + if (module->types[i] == NULL) { + set_error_buf_v(error_buf, error_buf_size, "unknown type %d", i); + return false; } } if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load data segment section failed: section size mismatch"); + set_error_buf(error_buf, error_buf_size, "section size mismatch"); return false; } - LOG_VERBOSE("Load data segment section success.\n"); + LOG_VERBOSE("Load type section success.\n"); return true; +fail: + return false; } -static bool -load_code_section(const uint8 *buf, const uint8 *buf_end, - const uint8 *buf_func, - const uint8 *buf_func_end, - WASMModule *module, - char *error_buf, uint32 error_buf_size) +static void +adjust_table_max_size(bool is_table64, uint32 init_size, uint32 max_size_flag, + uint32 *max_size) { - const uint8 *p = buf, *p_end = buf_end; - const uint8 *p_func = buf_func; - uint32 func_count = 0, code_count; + uint32 default_max_size; - /* code has been loaded in function section, so pass it here, just check - * whether function and code section have inconsistent lengths */ - read_leb_uint32(p, p_end, code_count); + /* TODO: current still use UINT32_MAX as upper limit for table size to keep + * ABI unchanged */ + (void)is_table64; + if (UINT32_MAX / 2 > init_size) + default_max_size = init_size * 2; + else + default_max_size = UINT32_MAX; - if (buf_func) - read_leb_uint32(p_func, buf_func_end, func_count); + if (default_max_size < WASM_TABLE_MAX_SIZE) + default_max_size = WASM_TABLE_MAX_SIZE; - if (func_count != code_count) { - set_error_buf(error_buf, error_buf_size, - "Load code section failed: " - "function and code section have inconsistent lengths"); - return false; - } + if (max_size_flag) { + /* module defines the table limitation */ + bh_assert(init_size <= *max_size); - LOG_VERBOSE("Load code segment section success.\n"); - return true; + if (init_size < *max_size) { + *max_size = + *max_size < default_max_size ? *max_size : default_max_size; + } + } + else { + /* partial defined table limitation, gives a default value */ + *max_size = default_max_size; + } } -static bool -load_start_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +#if WASM_ENABLE_LIBC_WASI != 0 || WASM_ENABLE_MULTI_MODULE != 0 +/** + * Find export item of a module with export info: + * module name, field name and export kind + */ +static WASMExport * +wasm_loader_find_export(const WASMModule *module, const char *module_name, + const char *field_name, uint8 export_kind, + char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - uint32 start_function; + WASMExport *export = + loader_find_export((WASMModuleCommon *)module, module_name, field_name, + export_kind, error_buf, error_buf_size); + return export; +} +#endif - read_leb_uint32(p, p_end, start_function); +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMTable * +wasm_loader_resolve_table(const char *module_name, const char *table_name, + uint32 init_size, uint32 max_size, char *error_buf, + uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMTable *table = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for table", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } - if (start_function) { - if (start_function >= module->function_count + module->import_function_count) { - set_error_buf(error_buf, error_buf_size, - "Load start section failed: " - "function index out of range."); - return false; - } - module->start_function = start_function; + module = (WASMModule *)module_reg; + export = + wasm_loader_find_export(module, module_name, table_name, + EXPORT_KIND_TABLE, error_buf, error_buf_size); + if (!export) { + return NULL; } - if (p != p_end) { - set_error_buf(error_buf, error_buf_size, - "Load start section failed: section size mismatch"); - return false; + /* resolve table and check the init/max size */ + if (export->index < module->import_table_count) { + table = + module->import_tables[export->index].u.table.import_table_linked; + } + else { + table = &(module->tables[export->index - module->import_table_count]); + } + if (table->table_type.init_size < init_size + || table->table_type.max_size > max_size) { + LOG_DEBUG("%s,%s failed type check(%d-%d), expected(%d-%d)", + module_name, table_name, table->table_type.init_size, + table->table_type.max_size, init_size, max_size); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; } - LOG_VERBOSE("Load start section success.\n"); - return true; + return table; } -static bool -load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, - char *error_buf, uint32 error_buf_size) +static WASMMemory * +wasm_loader_resolve_memory(const char *module_name, const char *memory_name, + uint32 init_page_count, uint32 max_page_count, + char *error_buf, uint32 error_buf_size) { - const uint8 *p = buf, *p_end = buf_end; - uint32 name_len; + WASMModuleCommon *module_reg; + WASMMemory *memory = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for memory", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } - if (p >= p_end) { - set_error_buf(error_buf, error_buf_size, - "Load custom section failed: unexpected end"); - return false; + module = (WASMModule *)module_reg; + export = + wasm_loader_find_export(module, module_name, memory_name, + EXPORT_KIND_MEMORY, error_buf, error_buf_size); + if (!export) { + return NULL; } - read_leb_uint32(p, p_end, name_len); + /* resolve memory and check the init/max page count */ + if (export->index < module->import_memory_count) { + memory = module->import_memories[export->index] + .u.memory.import_memory_linked; + } + else { + memory = + &(module->memories[export->index - module->import_memory_count]); + } + if (memory->init_page_count < init_page_count + || memory->max_page_count > max_page_count) { + LOG_DEBUG("%s,%s failed type check(%d-%d), expected(%d-%d)", + module_name, memory_name, memory->init_page_count, + memory->max_page_count, init_page_count, max_page_count); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return memory; +} - if (name_len == 0 - || p + name_len > p_end) { - set_error_buf(error_buf, error_buf_size, - "Load custom section failed: unexpected end"); - return false; +static WASMGlobal * +wasm_loader_resolve_global(const char *module_name, const char *global_name, + uint8 type, bool is_mutable, char *error_buf, + uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMGlobal *global = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for global", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; } - if (!check_utf8_str(p, name_len)) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "invalid UTF-8 encoding"); - return false; + module = (WASMModule *)module_reg; + export = + wasm_loader_find_export(module, module_name, global_name, + EXPORT_KIND_GLOBAL, error_buf, error_buf_size); + if (!export) { + return NULL; } - LOG_VERBOSE("Load custom section success.\n"); - return true; + /* resolve and check the global */ + if (export->index < module->import_global_count) { + global = + module->import_globals[export->index].u.global.import_global_linked; + } + else { + global = + &(module->globals[export->index - module->import_global_count]); + } + if (global->type.val_type != type + || global->type.is_mutable != is_mutable) { + LOG_DEBUG("%s,%s failed type check(%d, %d), expected(%d, %d)", + module_name, global_name, global->type.val_type, + global->type.is_mutable, type, is_mutable); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return global; } - -static bool -wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, - char *error_buf, uint32 error_buf_size); - -static bool -load_from_sections(WASMModule *module, WASMSection *sections, - char *error_buf, uint32 error_buf_size) +#if WASM_ENABLE_TAGS != 0 +static WASMTag * +wasm_loader_resolve_tag(const char *module_name, const char *tag_name, + const WASMType *expected_tag_type, + uint32 *linked_tag_index, char *error_buf, + uint32 error_buf_size) { - WASMExport *export; - WASMSection *section = sections; - const uint8 *buf, *buf_end, *buf_code = NULL, *buf_code_end = NULL, - *buf_func = NULL, *buf_func_end = NULL; - WASMGlobal *llvm_data_end_global = NULL, *llvm_heap_base_global = NULL; - WASMGlobal *llvm_stack_top_global = NULL, *global; - uint32 llvm_data_end = UINT32_MAX, llvm_heap_base = UINT32_MAX; - uint32 llvm_stack_top = UINT32_MAX, global_index, i; - uint32 data_end_global_index = UINT32_MAX; - uint32 heap_base_global_index = UINT32_MAX; - uint32 stack_top_global_index = UINT32_MAX; + WASMModuleCommon *module_reg; + WASMTag *tag = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for tag %s", module_name, + tag_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } - /* Find code and function sections if have */ - while (section) { - if (section->section_type == SECTION_TYPE_CODE) { - buf_code = section->section_body; - buf_code_end = buf_code + section->section_body_size; - } - else if (section->section_type == SECTION_TYPE_FUNC) { - buf_func = section->section_body; - buf_func_end = buf_func + section->section_body_size; - } - section = section->next; + module = (WASMModule *)module_reg; + export = + wasm_loader_find_export(module, module_name, tag_name, EXPORT_KIND_TAG, + error_buf, error_buf_size); + if (!export) { + return NULL; } - section = sections; - while (section) { - buf = section->section_body; - buf_end = buf + section->section_body_size; - switch (section->section_type) { - case SECTION_TYPE_USER: - /* unsupported user section, ignore it. */ - if (!load_user_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_TYPE: - if (!load_type_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_IMPORT: - if (!load_import_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_FUNC: - if (!load_function_section(buf, buf_end, buf_code, buf_code_end, - module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_TABLE: - if (!load_table_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_MEMORY: - if (!load_memory_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_GLOBAL: - if (!load_global_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_EXPORT: - if (!load_export_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_START: - if (!load_start_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_ELEM: - if (!load_table_segment_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_CODE: - if (!load_code_section(buf, buf_end, buf_func, buf_func_end, - module, error_buf, error_buf_size)) - return false; - break; - case SECTION_TYPE_DATA: - if (!load_data_segment_section(buf, buf_end, module, error_buf, error_buf_size)) - return false; - break; - default: - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: invalid section id"); - return false; - } + /* resolve tag type and tag */ + if (export->index < module->import_tag_count) { + /* importing an imported tag from the submodule */ + tag = module->import_tags[export->index].u.tag.import_tag_linked; + } + else { + /* importing an section tag from the submodule */ + tag = module->tags[export->index - module->import_tag_count]; + } - section = section->next; + /* check function type */ + if (!wasm_type_equal(expected_tag_type, tag->tag_type, module->types, + module->type_count)) { + LOG_DEBUG("%s.%s failed the type check", module_name, tag_name); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; } - for (i = 0; i < module->function_count; i++) { - WASMFunction *func = module->functions[i]; - if (!wasm_loader_prepare_bytecode(module, func, error_buf, error_buf_size)) - return false; + if (linked_tag_index != NULL) { + *linked_tag_index = export->index; } - /* Resolve llvm auxiliary data/stack/heap info and reset memory info */ - if (!module->possible_memory_grow) { - export = module->exports; - for (i = 0; i < module->export_count; i++, export++) { - if (export->kind == EXPORT_KIND_GLOBAL) { - if (!strcmp(export->name, "__heap_base")) { - global_index = export->index - module->import_global_count; - global = module->globals + global_index; - if (global->type == VALUE_TYPE_I32 - && !global->is_mutable - && global->init_expr.init_expr_type == - INIT_EXPR_TYPE_I32_CONST) { - heap_base_global_index = global_index; - llvm_heap_base_global = global; - llvm_heap_base = global->init_expr.u.i32; - LOG_VERBOSE("found llvm __heap_base global, value: %d\n", - llvm_heap_base); - } - } - else if (!strcmp(export->name, "__data_end")) { - global_index = export->index - module->import_global_count; - global = module->globals + global_index; - if (global->type == VALUE_TYPE_I32 - && !global->is_mutable - && global->init_expr.init_expr_type == - INIT_EXPR_TYPE_I32_CONST) { - data_end_global_index = global_index; - llvm_data_end_global = global; - llvm_data_end = global->init_expr.u.i32; - LOG_VERBOSE("found llvm __data_end global, value: %d\n", - llvm_data_end); - - llvm_data_end = align_uint(llvm_data_end, 16); - } - } - - if (llvm_data_end_global && llvm_heap_base_global) { - if ((data_end_global_index == heap_base_global_index + 1 - && data_end_global_index > 0) - || (heap_base_global_index == data_end_global_index + 1 - && heap_base_global_index > 0)) { - global_index = - data_end_global_index < heap_base_global_index - ? data_end_global_index - 1 : heap_base_global_index - 1; - global = module->globals + global_index; - if (global->type == VALUE_TYPE_I32 - && global->is_mutable - && global->init_expr.init_expr_type == - INIT_EXPR_TYPE_I32_CONST) { - llvm_stack_top_global = global; - llvm_stack_top = global->init_expr.u.i32; - stack_top_global_index = global_index; - LOG_VERBOSE("found llvm stack top global, " - "value: %d, global index: %d\n", - llvm_stack_top, global_index); - } - } - break; - } - } - } + return tag; +} +#endif /* end of WASM_ENABLE_TAGS != 0 */ +#endif /* end of WASM_ENABLE_MULTI_MODULE */ - if (llvm_data_end_global - && llvm_heap_base_global - && llvm_stack_top_global - && llvm_stack_top <= llvm_heap_base) { - WASMMemoryImport *memory_import; - WASMMemory *memory; - uint64 init_memory_size; - uint32 shrunk_memory_size = llvm_heap_base > llvm_data_end - ? llvm_heap_base : llvm_data_end; - if (module->import_memory_count) { - memory_import = &module->import_memories[0].u.memory; - init_memory_size = (uint64)memory_import->num_bytes_per_page * - memory_import->init_page_count; - if (llvm_heap_base <= init_memory_size - && llvm_data_end <= init_memory_size) { - /* Reset memory info to decrease memory usage */ - memory_import->num_bytes_per_page = shrunk_memory_size; - memory_import->init_page_count = 1; - LOG_VERBOSE("reset import memory size to %d\n", - shrunk_memory_size); - } - } - if (module->memory_count) { - memory = &module->memories[0]; - init_memory_size = (uint64)memory->num_bytes_per_page * - memory->init_page_count; - if (llvm_heap_base <= init_memory_size - && llvm_data_end <= init_memory_size) { - /* Reset memory info to decrease memory usage */ - memory->num_bytes_per_page = shrunk_memory_size; - memory->init_page_count = 1; - LOG_VERBOSE("reset memory size to %d\n", shrunk_memory_size); - } - } +static bool +load_function_import(const uint8 **p_buf, const uint8 *buf_end, + const WASMModule *parent_module, + const char *sub_module_name, const char *function_name, + WASMFunctionImport *function, bool no_resolve, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_type_index = 0; - module->llvm_aux_data_end = llvm_data_end; - module->llvm_aux_stack_bottom = llvm_stack_top; - module->llvm_aux_stack_size = llvm_stack_top > llvm_data_end - ? llvm_stack_top - llvm_data_end - : llvm_stack_top; - module->llvm_aux_stack_global_index = stack_top_global_index; - LOG_VERBOSE("aux stack bottom: %d, size: %d\n", - module->llvm_aux_stack_bottom, - module->llvm_aux_stack_size); - } + read_leb_uint32(p, p_end, declare_type_index); + *p_buf = p; + + if (!check_function_type(parent_module, declare_type_index, error_buf, + error_buf_size)) { + return false; } +#if WASM_ENABLE_GC != 0 + function->type_idx = declare_type_index; +#endif + +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) + declare_type_index = wasm_get_smallest_type_idx( + parent_module->types, parent_module->type_count, declare_type_index); +#endif + + function->func_type = + (WASMFuncType *)parent_module->types[declare_type_index]; + + function->module_name = (char *)sub_module_name; + function->field_name = (char *)function_name; + function->attachment = NULL; + function->signature = NULL; + function->call_conv_raw = false; + + /* lookup registered native symbols first */ + if (!no_resolve) { + wasm_resolve_import_func(parent_module, function); + } return true; +fail: + return false; } -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 -static void wasm_loader_free(void *ptr) +static bool +check_table_max_size(uint32 init_size, uint32 max_size, char *error_buf, + uint32 error_buf_size) { - wasm_free(ptr); + if (max_size < init_size) { + set_error_buf(error_buf, error_buf_size, + "size minimum must not be greater than maximum"); + return false; + } + return true; } -#else -#define wasm_loader_free wasm_free -#endif -static WASMModule* -create_module(char *error_buf, uint32 error_buf_size) +static bool +load_table_import(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *parent_module, const char *sub_module_name, + const char *table_name, WASMTableImport *table, + char *error_buf, uint32 error_buf_size) { - WASMModule *module = wasm_malloc(sizeof(WASMModule)); + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + uint32 declare_elem_type = 0, table_flag = 0, declare_init_size = 0, + declare_max_size = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *sub_module = NULL; + WASMTable *linked_table = NULL; +#endif +#if WASM_ENABLE_GC != 0 + WASMRefType ref_type; + bool need_ref_type_map; +#endif + bool is_table64 = false; - if (!module) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "allocate memory failed."); - return NULL; +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p, p_end, 1); + /* 0x70 or 0x6F */ + declare_elem_type = read_uint8(p); + if (VALUE_TYPE_FUNCREF != declare_elem_type +#if WASM_ENABLE_REF_TYPES != 0 + && VALUE_TYPE_EXTERNREF != declare_elem_type +#endif + ) { + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return false; + } +#else /* else of WASM_ENABLE_GC == 0 */ + if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { + return false; + } + if (!wasm_is_type_reftype(ref_type.ref_type) + || wasm_is_reftype_htref_non_nullable(ref_type.ref_type)) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; + } + declare_elem_type = ref_type.ref_type; + if (need_ref_type_map) { + if (!(table->table_type.elem_ref_type = + reftype_set_insert(parent_module->ref_type_set, &ref_type, + error_buf, error_buf_size))) { + return false; + } + } +#if TRACE_WASM_LOADER != 0 + os_printf("import table type: "); + wasm_dump_value_type(declare_elem_type, table->table_type.elem_ref_type); + os_printf("\n"); +#endif +#endif /* end of WASM_ENABLE_GC == 0 */ + + p_org = p; + read_leb_uint32(p, p_end, table_flag); + is_table64 = table_flag & TABLE64_FLAG; + if (p - p_org > 1) { + LOG_VERBOSE("integer representation too long(import table)"); + set_error_buf(error_buf, error_buf_size, "invalid limits flags"); + return false; } - memset(module, 0, sizeof(WASMModule)); + if (!wasm_table_check_flags(table_flag, error_buf, error_buf_size, false)) { + return false; + } - module->module_type = Wasm_Module_Bytecode; + read_leb_uint32(p, p_end, declare_init_size); + if (table_flag & MAX_TABLE_SIZE_FLAG) { + read_leb_uint32(p, p_end, declare_max_size); + if (!check_table_max_size(declare_init_size, declare_max_size, + error_buf, error_buf_size)) + return false; + } - /* Set start_function to -1, means no start function */ - module->start_function = (uint32)-1; + adjust_table_max_size(is_table64, declare_init_size, + table_flag & MAX_TABLE_SIZE_FLAG, &declare_max_size); - return module; -} + *p_buf = p; -WASMModule * -wasm_loader_load_from_sections(WASMSection *section_list, - char *error_buf, uint32 error_buf_size) -{ - WASMModule *module = create_module(error_buf, error_buf_size); - if (!module) - return NULL; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + sub_module = (WASMModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)parent_module, sub_module_name, error_buf, + error_buf_size); + if (sub_module) { + linked_table = wasm_loader_resolve_table( + sub_module_name, table_name, declare_init_size, + declare_max_size, error_buf, error_buf_size); + if (linked_table) { + /* reset with linked table limit */ + declare_elem_type = linked_table->table_type.elem_type; + declare_init_size = linked_table->table_type.init_size; + declare_max_size = linked_table->table_type.max_size; + table_flag = linked_table->table_type.flags; + table->import_table_linked = linked_table; + table->import_module = sub_module; + } + } + } +#endif /* WASM_ENABLE_MULTI_MODULE != 0 */ + + /* (table (export "table") 10 20 funcref) */ + /* (table (export "table64") 10 20 funcref) */ + /* we need this section working in wamrc */ + if (!strcmp("spectest", sub_module_name)) { + const uint32 spectest_table_init_size = 10; + const uint32 spectest_table_max_size = 20; + + if (strcmp("table", table_name) +#if WASM_ENABLE_MEMORY64 != 0 + && strcmp("table64", table_name) +#endif + ) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } - if (!load_from_sections(module, section_list, error_buf, error_buf_size)) { - wasm_loader_unload(module); - return NULL; + if (declare_init_size > spectest_table_init_size + || declare_max_size < spectest_table_max_size) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + + declare_init_size = spectest_table_init_size; + declare_max_size = spectest_table_max_size; } - LOG_VERBOSE("Load module from sections success.\n"); - return module; + /* now we believe all declaration are ok */ + table->table_type.elem_type = declare_elem_type; + table->table_type.init_size = declare_init_size; + table->table_type.flags = table_flag; + table->table_type.max_size = declare_max_size; + +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (table->table_type.elem_type == VALUE_TYPE_EXTERNREF) + parent_module->is_ref_types_used = true; +#endif + (void)parent_module; + return true; +fail: + return false; } -static void -destroy_sections(WASMSection *section_list) +static bool +check_memory_init_size(bool is_memory64, uint32 init_size, char *error_buf, + uint32 error_buf_size) { - WASMSection *section = section_list, *next; - while (section) { - next = section->next; - wasm_free(section); - section = next; + uint32 default_max_size = + is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; + + if (!is_memory64 && init_size > default_max_size) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return false; + } +#if WASM_ENABLE_MEMORY64 != 0 + else if (is_memory64 && init_size > default_max_size) { + set_error_buf( + error_buf, error_buf_size, + "memory size must be at most 4,294,967,295 pages (274 Terabyte)"); + return false; } +#endif + return true; } static bool -create_sections(const uint8 *buf, uint32 size, - WASMSection **p_section_list, - char *error_buf, uint32 error_buf_size) +check_memory_max_size(bool is_memory64, uint32 init_size, uint32 max_size, + char *error_buf, uint32 error_buf_size) { - WASMSection *section_list_end = NULL, *section; - const uint8 *p = buf, *p_end = buf + size/*, *section_body*/; - uint8 section_type, last_section_type = (uint8)-1; - uint32 section_size; - - bh_assert(!*p_section_list); - - p += 8; - while (p < p_end) { - CHECK_BUF(p, p_end, 1); - section_type = read_uint8(p); - if (section_type <= SECTION_TYPE_DATA) { - if (section_type != SECTION_TYPE_USER) { - /* Custom sections may be inserted at any place, - while other sections must occur at most once - and in prescribed order. */ - if (last_section_type != (uint8)-1 - && section_type <= last_section_type) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "junk after last section"); - return false; - } - last_section_type = section_type; - } - CHECK_BUF1(p, p_end, 1); - read_leb_uint32(p, p_end, section_size); - CHECK_BUF1(p, p_end, section_size); - - if (!(section = wasm_malloc(sizeof(WASMSection)))) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "allocate memory failed."); - return false; - } - - memset(section, 0, sizeof(WASMSection)); - section->section_type = section_type; - section->section_body = p; - section->section_body_size = section_size; + uint32 default_max_size = + is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; - if (!*p_section_list) - *p_section_list = section_list_end = section; - else { - section_list_end->next = section; - section_list_end = section; - } + if (max_size < init_size) { + set_error_buf(error_buf, error_buf_size, + "size minimum must not be greater than maximum"); + return false; + } - p += section_size; - } - else { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: invalid section id"); - return false; - } + if (!is_memory64 && max_size > default_max_size) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return false; } +#if WASM_ENABLE_MEMORY64 != 0 + else if (is_memory64 && max_size > default_max_size) { + set_error_buf( + error_buf, error_buf_size, + "memory size must be at most 4,294,967,295 pages (274 Terabyte)"); + return false; + } +#endif return true; } -static void -exchange32(uint8* p_data) -{ - uint8 value = *p_data; - *p_data = *(p_data + 3); - *(p_data + 3) = value; - - value = *(p_data + 1); - *(p_data + 1) = *(p_data + 2); - *(p_data + 2) = value; -} - -static union { - int a; - char b; -} __ue = { .a = 1 }; - -#define is_little_endian() (__ue.b == 1) - static bool -load(const uint8 *buf, uint32 size, WASMModule *module, - char *error_buf, uint32 error_buf_size) +load_memory_import(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *parent_module, const char *sub_module_name, + const char *memory_name, WASMMemoryImport *memory, + char *error_buf, uint32 error_buf_size) { - const uint8 *buf_end = buf + size; - const uint8 *p = buf, *p_end = buf_end; - uint32 magic_number, version; - WASMSection *section_list = NULL; - - CHECK_BUF1(p, p_end, sizeof(uint32)); - magic_number = read_uint32(p); - if (!is_little_endian()) - exchange32((uint8*)&magic_number); + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 pool_size = wasm_runtime_memory_pool_size(); + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count; +#endif /* WASM_ENABLE_APP_FRAMEWORK */ + uint32 mem_flag = 0; + bool is_memory64 = false; + uint32 declare_init_page_count = 0; + uint32 declare_max_page_count = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *sub_module = NULL; + WASMMemory *linked_memory = NULL; +#endif - if (magic_number != WASM_MAGIC_NUMBER) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: magic header not detected"); + p_org = p; + read_leb_uint32(p, p_end, mem_flag); + is_memory64 = mem_flag & MEMORY64_FLAG; + if (p - p_org > 1) { + LOG_VERBOSE("integer representation too long(import memory)"); + set_error_buf(error_buf, error_buf_size, "invalid limits flags"); return false; } - CHECK_BUF1(p, p_end, sizeof(uint32)); - version = read_uint32(p); - if (!is_little_endian()) - exchange32((uint8*)&version); - - if (version != WASM_CURRENT_VERSION) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: unknown binary version"); + if (!wasm_memory_check_flags(mem_flag, error_buf, error_buf_size, false)) { return false; } - if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) - || !load_from_sections(module, section_list, error_buf, error_buf_size)) { - destroy_sections(section_list); + read_leb_uint32(p, p_end, declare_init_page_count); + if (!check_memory_init_size(is_memory64, declare_init_page_count, error_buf, + error_buf_size)) { return false; } - destroy_sections(section_list); - return true; -} +#if WASM_ENABLE_APP_FRAMEWORK == 0 + max_page_count = is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; +#endif + if (mem_flag & MAX_PAGE_COUNT_FLAG) { + read_leb_uint32(p, p_end, declare_max_page_count); + if (!check_memory_max_size(is_memory64, declare_init_page_count, + declare_max_page_count, error_buf, + error_buf_size)) { + return false; + } + if (declare_max_page_count > max_page_count) { + declare_max_page_count = max_page_count; + } + } + else { + /* Limit the maximum memory size to max_page_count */ + declare_max_page_count = max_page_count; + } -const uint8* wasm_file; -WASMModule* -wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size) -{ - wasm_file = buf; - WASMModule *module = wasm_malloc(sizeof(WASMModule)); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + sub_module = (WASMModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)parent_module, sub_module_name, error_buf, + error_buf_size); + if (sub_module) { + linked_memory = wasm_loader_resolve_memory( + sub_module_name, memory_name, declare_init_page_count, + declare_max_page_count, error_buf, error_buf_size); + if (linked_memory) { + /** + * reset with linked memory limit + */ + memory->import_module = sub_module; + memory->import_memory_linked = linked_memory; + declare_init_page_count = linked_memory->init_page_count; + declare_max_page_count = linked_memory->max_page_count; + } + } + } +#endif - if (!module) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: allocate memory failed."); - return NULL; + /* (memory (export "memory") 1 2) */ + if (!strcmp("spectest", sub_module_name)) { + uint32 spectest_memory_init_page = 1; + uint32 spectest_memory_max_page = 2; + + if (strcmp("memory", memory_name)) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } + + if (declare_init_page_count > spectest_memory_init_page + || declare_max_page_count < spectest_memory_max_page) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + + declare_init_page_count = spectest_memory_init_page; + declare_max_page_count = spectest_memory_max_page; } +#if WASM_ENABLE_WASI_TEST != 0 + /* a case in wasi-testsuite which imports ("foo" "bar") */ + else if (!strcmp("foo", sub_module_name)) { + uint32 spectest_memory_init_page = 1; + uint32 spectest_memory_max_page = 1; - memset(module, 0, sizeof(WASMModule)); + if (strcmp("bar", memory_name)) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } - module->module_type = Wasm_Module_Bytecode; + if (declare_init_page_count > spectest_memory_init_page + || declare_max_page_count < spectest_memory_max_page) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } - /* Set start_function to -1, means no start function */ - module->start_function = (uint32)-1; + declare_init_page_count = spectest_memory_init_page; + declare_max_page_count = spectest_memory_max_page; + } +#endif - if (!load(buf, size, module, error_buf, error_buf_size)) - goto fail; + /* now we believe all declaration are ok */ + memory->mem_type.flags = mem_flag; + memory->mem_type.init_page_count = declare_init_page_count; + memory->mem_type.max_page_count = declare_max_page_count; + memory->mem_type.num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; - LOG_VERBOSE("Load module success.\n"); - return module; + *p_buf = p; + (void)parent_module; + return true; fail: - wasm_loader_unload(module); - return NULL; + return false; } -void -wasm_loader_unload(WASMModule *module) +#if WASM_ENABLE_TAGS != 0 +static bool +load_tag_import(const uint8 **p_buf, const uint8 *buf_end, + const WASMModule *parent_module, /* this module ! */ + const char *sub_module_name, const char *tag_name, + WASMTagImport *tag, /* structure to fill */ + char *error_buf, uint32 error_buf_size) { - uint32 i; + /* attribute and type of the import statement */ + uint8 declare_tag_attribute; + uint32 declare_type_index; + const uint8 *p = *p_buf, *p_end = buf_end; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *sub_module = NULL; +#endif - if (!module) - return; + /* get the one byte attribute */ + CHECK_BUF(p, p_end, 1); + declare_tag_attribute = read_uint8(p); + if (declare_tag_attribute != 0) { + set_error_buf(error_buf, error_buf_size, "unknown tag attribute"); + goto fail; + } - if (module->types) { - for (i = 0; i < module->type_count; i++) { - if (module->types[i]) - wasm_free(module->types[i]); - } - wasm_free(module->types); + /* get type */ + read_leb_uint32(p, p_end, declare_type_index); + /* compare against module->types */ + if (!check_function_type(parent_module, declare_type_index, error_buf, + error_buf_size)) { + goto fail; } - if (module->imports) - wasm_free(module->imports); + WASMFuncType *declare_tag_type = + (WASMFuncType *)parent_module->types[declare_type_index]; - if (module->functions) { - for (i = 0; i < module->function_count; i++) { - if (module->functions[i]) { - if (module->functions[i]->local_offsets) - wasm_free(module->functions[i]->local_offsets); - wasm_free(module->functions[i]); + /* check, that the type of the declared tag returns void */ + if (declare_tag_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + + goto fail; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + sub_module = (WASMModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)parent_module, sub_module_name, error_buf, + error_buf_size); + if (sub_module) { + /* wasm_loader_resolve_tag checks, that the imported tag + * and the declared tag have the same type + */ + uint32 linked_tag_index = 0; + WASMTag *linked_tag = wasm_loader_resolve_tag( + sub_module_name, tag_name, declare_tag_type, + &linked_tag_index /* out */, error_buf, error_buf_size); + if (linked_tag) { + tag->import_module = sub_module; + tag->import_tag_linked = linked_tag; + tag->import_tag_index_linked = linked_tag_index; } } - wasm_free(module->functions); } +#endif + /* store to module tag declarations */ + tag->attribute = declare_tag_attribute; + tag->type = declare_type_index; - if (module->tables) - wasm_free(module->tables); + tag->module_name = (char *)sub_module_name; + tag->field_name = (char *)tag_name; + tag->tag_type = declare_tag_type; - if (module->memories) - wasm_free(module->memories); + *p_buf = p; + (void)parent_module; - if (module->globals) - wasm_free(module->globals); + LOG_VERBOSE("Load tag import success\n"); - if (module->exports) - wasm_free(module->exports); + return true; +fail: + return false; +} +#endif /* end of WASM_ENABLE_TAGS != 0 */ - if (module->table_segments) { - for (i = 0; i < module->table_seg_count; i++) { - if (module->table_segments[i].func_indexes) - wasm_free(module->table_segments[i].func_indexes); +static bool +load_global_import(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *parent_module, char *sub_module_name, + char *global_name, WASMGlobalImport *global, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 declare_type = 0; + uint8 declare_mutable = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *sub_module = NULL; + WASMGlobal *linked_global = NULL; +#endif +#if WASM_ENABLE_GC != 0 + WASMRefType ref_type; + bool need_ref_type_map; +#endif + bool ret = false; + +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p, p_end, 2); + /* global type */ + declare_type = read_uint8(p); + if (!is_valid_value_type_for_interpreter(declare_type)) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; + } + declare_mutable = read_uint8(p); +#else + if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { + return false; + } + declare_type = ref_type.ref_type; + if (need_ref_type_map) { + if (!(global->ref_type = + reftype_set_insert(parent_module->ref_type_set, &ref_type, + error_buf, error_buf_size))) { + return false; } - wasm_free(module->table_segments); } +#if TRACE_WASM_LOADER != 0 + os_printf("import global type: "); + wasm_dump_value_type(declare_type, global->ref_type); + os_printf("\n"); +#endif + CHECK_BUF(p, p_end, 1); + declare_mutable = read_uint8(p); +#endif /* end of WASM_ENABLE_GC == 0 */ - if (module->data_segments) { - for (i = 0; i < module->data_seg_count; i++) { - if (module->data_segments[i]) - wasm_free(module->data_segments[i]); - } - wasm_free(module->data_segments); + *p_buf = p; + + if (!check_mutability(declare_mutable, error_buf, error_buf_size)) { + return false; } - if (module->const_str_list) { - StringNode *node = module->const_str_list, *node_next; - while (node) { - node_next = node->next; - wasm_free(node); - node = node_next; +#if WASM_ENABLE_LIBC_BUILTIN != 0 + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, global_name, + global); + if (ret) { + if (global->type.val_type != declare_type + || global->type.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 + if (!global->is_linked + && !wasm_runtime_is_built_in_module(sub_module_name)) { + sub_module = (WASMModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)parent_module, sub_module_name, error_buf, + error_buf_size); + if (sub_module) { + /* check sub modules */ + linked_global = wasm_loader_resolve_global( + sub_module_name, global_name, declare_type, declare_mutable, + error_buf, error_buf_size); + if (linked_global) { + global->import_module = sub_module; + global->import_global_linked = linked_global; + global->is_linked = true; + } } } +#endif - wasm_free(module); + global->module_name = sub_module_name; + global->field_name = global_name; + global->type.val_type = declare_type; + global->type.is_mutable = (declare_mutable == 1); + +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (global->type.val_type == VALUE_TYPE_V128) + parent_module->is_simd_used = true; + else if (global->type.val_type == VALUE_TYPE_EXTERNREF) + parent_module->is_ref_types_used = true; +#endif + (void)parent_module; + (void)ret; + return true; +fail: + return false; } -bool -wasm_loader_find_block_addr(WASMModule *module, - const uint8 *start_addr, - const uint8 *code_end_addr, - uint8 block_type, - uint8 **p_else_addr, - uint8 **p_end_addr, - char *error_buf, - uint32 error_buf_size) +static bool +load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, + WASMTable *table, char *error_buf, uint32 error_buf_size) { - const uint8 *p = start_addr, *p_end = code_end_addr; - uint8 *else_addr = NULL; - uint32 block_nested_depth = 1, count, i; - uint8 opcode, u8; - - BlockAddr block_stack[16] = { 0 }, *block; - uint32 j, t; + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; +#if WASM_ENABLE_GC != 0 + WASMRefType ref_type; + bool need_ref_type_map; +#endif + bool is_table64 = false; - i = (uint32)(((uintptr_t)start_addr) ^ ((uintptr_t)start_addr >> 16)); - i = i % BLOCK_ADDR_CACHE_SIZE; - block = module->block_addr_cache[i]; - for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) { - if (block[j].start_addr == start_addr) { - /* Cache hit */ - *p_else_addr = block[j].else_addr; - *p_end_addr = block[j].end_addr; - return true; +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p, p_end, 1); + /* 0x70 or 0x6F */ + table->table_type.elem_type = read_uint8(p); + if (VALUE_TYPE_FUNCREF != table->table_type.elem_type +#if WASM_ENABLE_REF_TYPES != 0 + && VALUE_TYPE_EXTERNREF != table->table_type.elem_type +#endif + ) { + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return false; + } +#else /* else of WASM_ENABLE_GC == 0 */ + if (!resolve_value_type(&p, p_end, module, module->type_count, + &need_ref_type_map, &ref_type, false, error_buf, + error_buf_size)) { + return false; + } + /* + * TODO: add this validator + * `wasm_is_reftype_htref_non_nullable(ref_type.ref_type)` + * after sync up with the latest GC spec + */ + if (!wasm_is_type_reftype(ref_type.ref_type)) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; + } + table->table_type.elem_type = ref_type.ref_type; + if (need_ref_type_map) { + if (!(table->table_type.elem_ref_type = + reftype_set_insert(module->ref_type_set, &ref_type, error_buf, + error_buf_size))) { + return false; } } +#if TRACE_WASM_LOADER != 0 + os_printf("table type: "); + wasm_dump_value_type(table->table_type.elem_type, + table->table_type.elem_ref_type); + os_printf("\n"); +#endif +#endif /* end of WASM_ENABLE_GC == 0 */ + + p_org = p; + read_leb_uint32(p, p_end, table->table_type.flags); + is_table64 = table->table_type.flags & TABLE64_FLAG; + if (p - p_org > 1) { + LOG_VERBOSE("integer representation too long(table)"); + set_error_buf(error_buf, error_buf_size, "invalid limits flags"); + return false; + } - /* Cache unhit */ - block_stack[0].start_addr = start_addr; + if (!wasm_table_check_flags(table->table_type.flags, error_buf, + error_buf_size, false)) { + return false; + } - while (p < code_end_addr) { - opcode = *p++; + read_leb_uint32(p, p_end, table->table_type.init_size); + if (table->table_type.flags & MAX_TABLE_SIZE_FLAG) { + read_leb_uint32(p, p_end, table->table_type.max_size); + if (!check_table_max_size(table->table_type.init_size, + table->table_type.max_size, error_buf, + error_buf_size)) + return false; + } - switch (opcode) { - case WASM_OP_UNREACHABLE: - case WASM_OP_NOP: - break; + adjust_table_max_size(is_table64, table->table_type.init_size, + table->table_type.flags & MAX_TABLE_SIZE_FLAG, + &table->table_type.max_size); - case WASM_OP_BLOCK: - case WASM_OP_LOOP: - case WASM_OP_IF: - CHECK_BUF(p, p_end, 1); - /* block result type: 0x40/0x7F/0x7E/0x7D/0x7C */ - u8 = read_uint8(p); - if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) { - block_stack[block_nested_depth].start_addr = p; +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (table->table_type.elem_type == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; +#endif + + *p_buf = p; + return true; +fail: + return false; +} + +static bool +load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 pool_size = wasm_runtime_memory_pool_size(); + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count; +#endif + bool is_memory64 = false; + + p_org = p; + read_leb_uint32(p, p_end, memory->flags); + is_memory64 = memory->flags & MEMORY64_FLAG; + if (p - p_org > 1) { + LOG_VERBOSE("integer representation too long(memory)"); + set_error_buf(error_buf, error_buf_size, "invalid limits flags"); + return false; + } + + if (!wasm_memory_check_flags(memory->flags, error_buf, error_buf_size, + false)) { + return false; + } + + read_leb_uint32(p, p_end, memory->init_page_count); + if (!check_memory_init_size(is_memory64, memory->init_page_count, error_buf, + error_buf_size)) + return false; + +#if WASM_ENABLE_APP_FRAMEWORK == 0 + max_page_count = is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; +#endif + if (memory->flags & 1) { + read_leb_uint32(p, p_end, memory->max_page_count); + if (!check_memory_max_size(is_memory64, memory->init_page_count, + memory->max_page_count, error_buf, + error_buf_size)) + return false; + if (memory->max_page_count > max_page_count) + memory->max_page_count = max_page_count; + } + else { + /* Limit the maximum memory size to max_page_count */ + memory->max_page_count = max_page_count; + } + + memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +fail: + return false; +} + +static int +cmp_export_name(const void *a, const void *b) +{ + return strcmp(*(char **)a, *(char **)b); +} + +static bool +load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, bool no_resolve, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_old; + uint32 import_count, name_len, type_index, i, u32, flags; + uint64 total_size; + WASMImport *import; + WASMImport *import_functions = NULL, *import_tables = NULL; + WASMImport *import_memories = NULL, *import_globals = NULL; +#if WASM_ENABLE_TAGS != 0 + WASMImport *import_tags = NULL; +#endif + char *sub_module_name, *field_name; + uint8 u8, kind, global_type; + + read_leb_uint32(p, p_end, import_count); + + if (import_count) { + module->import_count = import_count; + total_size = sizeof(WASMImport) * (uint64)import_count; + if (!(module->imports = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + p_old = p; + + /* Scan firstly to get import count of each type */ + for (i = 0; i < import_count; i++) { + /* module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + /* field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03/0x04 */ + kind = read_uint8(p); + + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + read_leb_uint32(p, p_end, type_index); + module->import_function_count++; + break; + + case IMPORT_KIND_TABLE: /* import table */ + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + u8 = read_uint8(p); +#if WASM_ENABLE_GC != 0 + if (wasm_is_reftype_htref_nullable(u8)) { + int32 heap_type; + read_leb_int32(p, p_end, heap_type); + (void)heap_type; + } +#endif + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_table_count++; + + if (module->import_table_count > 1) { +#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0 + set_error_buf(error_buf, error_buf_size, + "multiple tables"); + return false; +#elif WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + } + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_memory_count++; +#if WASM_ENABLE_MULTI_MEMORY == 0 + if (module->import_memory_count > 1) { + set_error_buf(error_buf, error_buf_size, + "multiple memories"); + return false; + } +#endif + break; + +#if WASM_ENABLE_TAGS != 0 + case IMPORT_KIND_TAG: /* import tags */ + /* it only counts the number of tags to import */ + module->import_tag_count++; + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); + read_leb_uint32(p, p_end, type_index); + break; +#endif + + case IMPORT_KIND_GLOBAL: /* import global */ +#if WASM_ENABLE_GC != 0 + /* valtype */ + CHECK_BUF(p, p_end, 1); + global_type = read_uint8(p); + if (wasm_is_reftype_htref_nullable(global_type) + || wasm_is_reftype_htref_non_nullable(global_type)) { + int32 heap_type; + read_leb_int32(p, p_end, heap_type); + (void)heap_type; + } + + /* mutability */ + CHECK_BUF(p, p_end, 1); + p += 1; +#else + CHECK_BUF(p, p_end, 2); + p += 2; +#endif + + (void)global_type; + module->import_global_count++; + break; + + default: + set_error_buf(error_buf, error_buf_size, + "invalid import kind"); + return false; + } + } + + if (module->import_function_count) + import_functions = module->import_functions = module->imports; + if (module->import_table_count) + import_tables = module->import_tables = + module->imports + module->import_function_count; + if (module->import_memory_count) + import_memories = module->import_memories = + module->imports + module->import_function_count + + module->import_table_count; + +#if WASM_ENABLE_TAGS != 0 + if (module->import_tag_count) + import_tags = module->import_tags = + module->imports + module->import_function_count + + module->import_table_count + module->import_memory_count; + if (module->import_global_count) + import_globals = module->import_globals = + module->imports + module->import_function_count + + module->import_table_count + module->import_memory_count + + module->import_tag_count; +#else + if (module->import_global_count) + import_globals = module->import_globals = + module->imports + module->import_function_count + + module->import_table_count + module->import_memory_count; +#endif + + p = p_old; + + /* Scan again to resolve the data */ + for (i = 0; i < import_count; i++) { + /* load module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(sub_module_name = wasm_const_str_list_insert( + p, name_len, module, is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + p += name_len; + + /* load field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(field_name = wasm_const_str_list_insert( + p, name_len, module, is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + p += name_len; + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03/0x4 */ + kind = read_uint8(p); + + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + bh_assert(import_functions); + import = import_functions++; + if (!load_function_import(&p, p_end, module, + sub_module_name, field_name, + &import->u.function, no_resolve, + error_buf, error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_TABLE: /* import table */ + bh_assert(import_tables); + import = import_tables++; + if (!load_table_import(&p, p_end, module, sub_module_name, + field_name, &import->u.table, + error_buf, error_buf_size)) { + LOG_DEBUG("can not import such a table (%s,%s)", + sub_module_name, field_name); + return false; + } + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + bh_assert(import_memories); + import = import_memories++; + if (!load_memory_import(&p, p_end, module, sub_module_name, + field_name, &import->u.memory, + error_buf, error_buf_size)) { + return false; + } + break; + +#if WASM_ENABLE_TAGS != 0 + case IMPORT_KIND_TAG: + bh_assert(import_tags); + import = import_tags++; + if (!load_tag_import(&p, p_end, module, sub_module_name, + field_name, &import->u.tag, error_buf, + error_buf_size)) { + return false; + } + break; +#endif + + case IMPORT_KIND_GLOBAL: /* import global */ + bh_assert(import_globals); + import = import_globals++; + if (!load_global_import(&p, p_end, module, sub_module_name, + field_name, &import->u.global, + error_buf, error_buf_size)) { + return false; + } + break; + + default: + set_error_buf(error_buf, error_buf_size, + "invalid import kind"); + return false; + } + import->kind = kind; + import->u.names.module_name = sub_module_name; + import->u.names.field_name = field_name; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + import = module->import_functions; + for (i = 0; i < module->import_function_count; i++, import++) { + if (!strcmp(import->u.names.module_name, "wasi_unstable") + || !strcmp(import->u.names.module_name, + "wasi_snapshot_preview1")) { + module->import_wasi_api = true; + break; + } + } +#endif + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load import section success.\n"); + (void)u8; + (void)u32; + (void)type_index; + return true; +fail: + return false; +} + +static bool +init_function_local_offsets(WASMFunction *func, char *error_buf, + uint32 error_buf_size) +{ + WASMFuncType *param_type = func->func_type; + uint32 param_count = param_type->param_count; + uint8 *param_types = param_type->types; + uint32 local_count = func->local_count; + uint8 *local_types = func->local_types; + uint32 i, local_offset = 0; + uint64 total_size = sizeof(uint16) * ((uint64)param_count + local_count); + + /* + * Only allocate memory when total_size is not 0, + * or the return value of malloc(0) might be NULL on some platforms, + * which causes wasm loader return false. + */ + if (total_size > 0 + && !(func->local_offsets = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < param_count; i++) { + func->local_offsets[i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(param_types[i]); + } + + for (i = 0; i < local_count; i++) { + func->local_offsets[param_count + i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(local_types[i]); + } + + bh_assert(local_offset == func->param_cell_num + func->local_cell_num); + return true; +} + +static bool +load_function_section(const uint8 *buf, const uint8 *buf_end, + const uint8 *buf_code, const uint8 *buf_code_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_code = buf_code, *p_code_end, *p_code_save; + uint32 func_count; + uint64 total_size; + uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; + uint32 local_count, local_set_count, sub_local_count, local_cell_num; + uint8 type; + WASMFunction *func; +#if WASM_ENABLE_GC != 0 + bool need_ref_type_map; + WASMRefType ref_type; + uint32 ref_type_map_count = 0, t = 0, type_index_org; +#endif + + read_leb_uint32(p, p_end, func_count); + + if (buf_code) + read_leb_uint32(p_code, buf_code_end, code_count); + + if (func_count != code_count) { + set_error_buf(error_buf, error_buf_size, + "function and code section have inconsistent lengths or " + "unexpected end"); + return false; + } + + if (is_indices_overflow(module->import_function_count, func_count, + error_buf, error_buf_size)) + return false; + + if (func_count) { + module->function_count = func_count; + total_size = sizeof(WASMFunction *) * (uint64)func_count; + if (!(module->functions = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < func_count; i++) { + /* Resolve function type */ + read_leb_uint32(p, p_end, type_index); + + if (!check_function_type(module, type_index, error_buf, + error_buf_size)) { + return false; + } + +#if WASM_ENABLE_GC != 0 + type_index_org = type_index; +#endif + +#if (WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0) \ + && WASM_ENABLE_GC == 0 + type_index = wasm_get_smallest_type_idx( + module->types, module->type_count, type_index); +#endif + + read_leb_uint32(p_code, buf_code_end, code_size); + if (code_size == 0 || p_code + code_size > buf_code_end) { + set_error_buf(error_buf, error_buf_size, + "invalid function code size"); + return false; + } + + /* Resolve local set count */ + p_code_end = p_code + code_size; +#if WASM_ENABLE_BRANCH_HINTS != 0 + uint8 *p_body_start = (uint8 *)p_code; +#endif + local_count = 0; + read_leb_uint32(p_code, buf_code_end, local_set_count); + p_code_save = p_code; + +#if WASM_ENABLE_GC != 0 + ref_type_map_count = 0; +#endif + + /* Calculate total local count */ + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + if (sub_local_count > UINT32_MAX - local_count) { + set_error_buf(error_buf, error_buf_size, "too many locals"); + return false; + } +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + local_count += sub_local_count; +#if WASM_ENABLE_WAMR_COMPILER != 0 + /* If any value's type is v128, mark the module as SIMD used */ + if (type == VALUE_TYPE_V128) + module->is_simd_used = true; +#endif +#else + if (!resolve_value_type(&p_code, buf_code_end, module, + module->type_count, &need_ref_type_map, + &ref_type, false, error_buf, + error_buf_size)) { + return false; + } + local_count += sub_local_count; + if (need_ref_type_map) + ref_type_map_count += sub_local_count; +#endif + } + + /* Code size in code entry can't be smaller than size of vec(locals) + * + expr(at least 1 for opcode end). And expressions are encoded by + * their instruction sequence terminated with an explicit 0x0B + * opcode for end. */ + if (p_code_end <= p_code || *(p_code_end - 1) != WASM_OP_END) { + set_error_buf( + error_buf, error_buf_size, + "section size mismatch: function body END opcode expected"); + return false; + } + + /* Alloc memory, layout: function structure + local types */ + code_size = (uint32)(p_code_end - p_code); + + total_size = sizeof(WASMFunction) + (uint64)local_count; + if (!(func = module->functions[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } +#if WASM_ENABLE_GC != 0 + if (ref_type_map_count > 0) { + if (ref_type_map_count > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "ref type count too large"); + return false; + } + total_size = + sizeof(WASMRefTypeMap) * (uint64)ref_type_map_count; + if (!(func->local_ref_type_maps = loader_malloc( + total_size, error_buf, error_buf_size))) { + return false; + } + func->local_ref_type_map_count = ref_type_map_count; + } +#endif + + /* Set function type, local count, code size and code body */ + func->func_type = (WASMFuncType *)module->types[type_index]; + func->local_count = local_count; + if (local_count > 0) + func->local_types = (uint8 *)func + sizeof(WASMFunction); + func->code_size = code_size; +#if WASM_ENABLE_BRANCH_HINTS != 0 + func->code_body_begin = p_body_start; +#endif + /* + * we shall make a copy of code body [p_code, p_code + code_size] + * when we are worrying about inappropriate releasing behaviour. + * all code bodies are actually in a buffer which user allocates in + * their embedding environment and we don't have power over them. + * it will be like: + * code_body_cp = malloc(code_size); + * memcpy(code_body_cp, p_code, code_size); + * func->code = code_body_cp; + */ + func->code = (uint8 *)p_code; +#if WASM_ENABLE_GC != 0 + func->type_idx = type_index_org; +#endif + +#if WASM_ENABLE_GC != 0 + t = 0; +#endif + + /* Load each local type */ + p_code = p_code_save; + local_type_index = 0; + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + /* Note: sub_local_count is allowed to be 0 */ + if (local_type_index > UINT32_MAX - sub_local_count + || local_type_index + sub_local_count > local_count) { + set_error_buf(error_buf, error_buf_size, + "invalid local count"); + return false; + } +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + if (!is_valid_value_type_for_interpreter(type)) { + if (type == VALUE_TYPE_V128) + set_error_buf(error_buf, error_buf_size, + "v128 value type requires simd feature"); + else if (type == VALUE_TYPE_FUNCREF + || type == VALUE_TYPE_EXTERNREF) + set_error_buf(error_buf, error_buf_size, + "ref value type requires " + "reference types feature"); + else + set_error_buf_v(error_buf, error_buf_size, + "invalid local type 0x%02X", type); + return false; + } +#else + if (!resolve_value_type(&p_code, buf_code_end, module, + module->type_count, &need_ref_type_map, + &ref_type, false, error_buf, + error_buf_size)) { + return false; + } + if (need_ref_type_map) { + WASMRefType *ref_type_tmp; + if (!(ref_type_tmp = reftype_set_insert( + module->ref_type_set, &ref_type, error_buf, + error_buf_size))) { + return false; + } + for (k = 0; k < sub_local_count; k++) { + func->local_ref_type_maps[t + k].ref_type = + ref_type_tmp; + func->local_ref_type_maps[t + k].index = + local_type_index + k; + } + t += sub_local_count; + } + type = ref_type.ref_type; +#endif + for (k = 0; k < sub_local_count; k++) { + func->local_types[local_type_index++] = type; + } +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (type == VALUE_TYPE_V128) + module->is_simd_used = true; + else if (type == VALUE_TYPE_FUNCREF + || type == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; +#endif + } + + bh_assert(local_type_index == func->local_count); +#if WASM_ENABLE_GC != 0 + bh_assert(t == func->local_ref_type_map_count); +#if TRACE_WASM_LOADER != 0 + os_printf("func %u, local types: [", i); + k = 0; + for (j = 0; j < func->local_count; j++) { + WASMRefType *ref_type_tmp = NULL; + if (wasm_is_type_multi_byte_type(func->local_types[j])) { + bh_assert(j == func->local_ref_type_maps[k].index); + ref_type_tmp = func->local_ref_type_maps[k++].ref_type; + } + wasm_dump_value_type(func->local_types[j], ref_type_tmp); + if (j < func->local_count - 1) + os_printf(" "); + } + os_printf("]\n"); +#endif +#endif + + func->param_cell_num = func->func_type->param_cell_num; + func->ret_cell_num = func->func_type->ret_cell_num; + local_cell_num = + wasm_get_cell_num(func->local_types, func->local_count); + + if (local_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "local count too large"); + return false; + } + + func->local_cell_num = (uint16)local_cell_num; + + if (!init_function_local_offsets(func, error_buf, error_buf_size)) + return false; + + p_code = p_code_end; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load function section success.\n"); + return true; +fail: + return false; +} + +static bool +load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 table_count, i; + uint64 total_size; + WASMTable *table; + + read_leb_uint32(p, p_end, table_count); + if (module->import_table_count + table_count > 1) { +#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0 + /* a total of one table is allowed */ + set_error_buf(error_buf, error_buf_size, "multiple tables"); + return false; +#elif WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + } + + if (table_count) { + module->table_count = table_count; + total_size = sizeof(WASMTable) * (uint64)table_count; + if (!(module->tables = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each table */ + table = module->tables; + for (i = 0; i < table_count; i++, table++) { +#if WASM_ENABLE_GC != 0 + uint8 flag; + bool has_init = false; + + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + + if (flag == TABLE_INIT_EXPR_FLAG) { + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + + if (flag != 0x00) { + set_error_buf(error_buf, error_buf_size, + "invalid leading bytes for table"); + return false; + } + has_init = true; + } + else { + p--; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + if (!load_table(&p, p_end, module, table, error_buf, + error_buf_size)) + return false; + +#if WASM_ENABLE_GC != 0 + if (has_init) { + if (!load_init_expr(module, &p, p_end, &table->init_expr, + table->table_type.elem_type, + table->table_type.elem_ref_type, error_buf, + error_buf_size)) + return false; + if (table->init_expr.init_expr_type >= INIT_EXPR_TYPE_STRUCT_NEW + && table->init_expr.init_expr_type + <= INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + set_error_buf( + error_buf, error_buf_size, + "unsupported initializer expression for table"); + return false; + } + } + else { + if (wasm_is_reftype_htref_non_nullable( + table->table_type.elem_type)) { + set_error_buf( + error_buf, error_buf_size, + "type mismatch: non-nullable table without init expr"); + return false; + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (table->table_type.elem_type == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; +#endif + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load table section success.\n"); + return true; +fail: + return false; +} + +static bool +load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 memory_count, i; + uint64 total_size; + WASMMemory *memory; + + read_leb_uint32(p, p_end, memory_count); + +#if WASM_ENABLE_MULTI_MEMORY == 0 + /* a total of one memory is allowed */ + if (module->import_memory_count + memory_count > 1) { + set_error_buf(error_buf, error_buf_size, "multiple memories"); + return false; + } +#endif + + if (memory_count) { + module->memory_count = memory_count; + total_size = sizeof(WASMMemory) * (uint64)memory_count; + if (!(module->memories = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each memory */ + memory = module->memories; + for (i = 0; i < memory_count; i++, memory++) + if (!load_memory(&p, p_end, memory, error_buf, error_buf_size)) + return false; + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load memory section success.\n"); + return true; +fail: + return false; +} + +static bool +load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 global_count, i; + uint64 total_size; + WASMGlobal *global; + uint8 mutable; +#if WASM_ENABLE_GC != 0 + bool need_ref_type_map; + WASMRefType ref_type; +#endif + + read_leb_uint32(p, p_end, global_count); + if (is_indices_overflow(module->import_global_count, global_count, + error_buf, error_buf_size)) + return false; + + module->global_count = 0; + if (global_count) { + total_size = sizeof(WASMGlobal) * (uint64)global_count; + if (!(module->globals = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + global = module->globals; + + for (i = 0; i < global_count; i++, global++) { +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p, p_end, 2); + /* global type */ + global->type.val_type = read_uint8(p); + if (!is_valid_value_type_for_interpreter(global->type.val_type)) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; + } + mutable = read_uint8(p); +#else + if (!resolve_value_type(&p, p_end, module, module->type_count, + &need_ref_type_map, &ref_type, false, + error_buf, error_buf_size)) { + return false; + } + global->type.val_type = ref_type.ref_type; + CHECK_BUF(p, p_end, 1); + mutable = read_uint8(p); +#endif /* end of WASM_ENABLE_GC */ + +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (global->type.val_type == VALUE_TYPE_V128) + module->is_simd_used = true; + else if (global->type.val_type == VALUE_TYPE_FUNCREF + || global->type.val_type == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; +#endif + + if (!check_mutability(mutable, error_buf, error_buf_size)) { + return false; + } + global->type.is_mutable = mutable ? true : false; + + /* initialize expression */ + if (!load_init_expr(module, &p, p_end, &(global->init_expr), + global->type.val_type, +#if WASM_ENABLE_GC == 0 + NULL, +#else + &ref_type, +#endif + error_buf, error_buf_size)) + return false; + +#if WASM_ENABLE_GC != 0 + if (global->init_expr.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + uint8 global_type; + WASMRefType *global_ref_type; + uint32 global_idx = global->init_expr.u.unary.v.global_index; + + if (global->init_expr.u.unary.v.global_index + >= module->import_global_count + i) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return false; + } + + if (global_idx < module->import_global_count) { + global_type = module->import_globals[global_idx] + .u.global.type.val_type; + global_ref_type = + module->import_globals[global_idx].u.global.ref_type; + } + else { + global_type = + module + ->globals[global_idx - module->import_global_count] + .type.val_type; + global_ref_type = + module + ->globals[global_idx - module->import_global_count] + .ref_type; + } + if (!wasm_reftype_is_subtype_of( + global_type, global_ref_type, global->type.val_type, + global->ref_type, module->types, module->type_count)) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; + } + } + + if (need_ref_type_map) { + if (!(global->ref_type = + reftype_set_insert(module->ref_type_set, &ref_type, + error_buf, error_buf_size))) { + return false; + } + } +#if TRACE_WASM_LOADER != 0 + os_printf("global type: "); + wasm_dump_value_type(global->type, global->ref_type); + os_printf("\n"); +#endif +#endif + module->global_count++; + } + bh_assert(module->global_count == global_count); + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load global section success.\n"); + return true; +fail: + return false; +} + +static bool +check_duplicate_exports(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + uint32 i; + bool result = false; + char *names_buf[32], **names = names_buf; + + if (module->export_count > 32) { + names = loader_malloc(module->export_count * sizeof(char *), error_buf, + error_buf_size); + if (!names) { + return result; + } + } + + for (i = 0; i < module->export_count; i++) { + names[i] = module->exports[i].name; + } + + qsort(names, module->export_count, sizeof(char *), cmp_export_name); + + for (i = 1; i < module->export_count; i++) { + if (!strcmp(names[i], names[i - 1])) { + set_error_buf(error_buf, error_buf_size, "duplicate export name"); + goto cleanup; + } + } + + result = true; +cleanup: + if (module->export_count > 32) { + wasm_runtime_free(names); + } + return result; +} + +static bool +load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 export_count, i, index; + uint64 total_size; + uint32 str_len; + WASMExport *export; + + read_leb_uint32(p, p_end, export_count); + + if (export_count) { + module->export_count = export_count; + total_size = sizeof(WASMExport) * (uint64)export_count; + if (!(module->exports = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + export = module->exports; + for (i = 0; i < export_count; i++, export ++) { +#if WASM_ENABLE_THREAD_MGR == 0 + if (p == p_end) { + /* export section with inconsistent count: + n export declared, but less than n given */ + set_error_buf(error_buf, error_buf_size, + "length out of bounds"); + return false; + } +#endif + read_leb_uint32(p, p_end, str_len); + CHECK_BUF(p, p_end, str_len); + + if (!(export->name = wasm_const_str_list_insert( + p, str_len, module, is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + + p += str_len; + CHECK_BUF(p, p_end, 1); + export->kind = read_uint8(p); + read_leb_uint32(p, p_end, index); + export->index = index; + + switch (export->kind) { + /* function index */ + case EXPORT_KIND_FUNC: + if (index >= module->function_count + + module->import_function_count) { + set_error_buf(error_buf, error_buf_size, + "unknown function"); + return false; + } +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) + /* TODO: check func type, if it has v128 param or result, + report error */ +#endif +#endif + break; + /* table index */ + case EXPORT_KIND_TABLE: + if (index + >= module->table_count + module->import_table_count) { + set_error_buf(error_buf, error_buf_size, + "unknown table"); + return false; + } + break; + /* memory index */ + case EXPORT_KIND_MEMORY: + if (index + >= module->memory_count + module->import_memory_count) { + set_error_buf(error_buf, error_buf_size, + "unknown memory"); + return false; + } + break; +#if WASM_ENABLE_TAGS != 0 + /* export tag */ + case EXPORT_KIND_TAG: + if (index >= module->tag_count + module->import_tag_count) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + return false; + } + break; +#endif + + /* global index */ + case EXPORT_KIND_GLOBAL: + if (index + >= module->global_count + module->import_global_count) { + set_error_buf(error_buf, error_buf_size, + "unknown global"); + return false; + } + break; + + default: + set_error_buf(error_buf, error_buf_size, + "invalid export kind"); + return false; + } + } + + if (!check_duplicate_exports(module, error_buf, error_buf_size)) { + return false; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load export section success.\n"); + return true; +fail: + return false; +} + +static bool +check_table_index(const WASMModule *module, uint32 table_index, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_REF_TYPES == 0 && WASM_ENABLE_GC == 0 + if (table_index != 0) { + set_error_buf( + error_buf, error_buf_size, + "zero byte expected. The module uses reference types feature " + "which is disabled in the runtime."); + return false; + } +#endif + + if (table_index >= module->import_table_count + module->table_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown table %d", + table_index); + return false; + } + return true; +} + +static bool +load_table_index(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, + uint32 *p_table_index, char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 table_index; + + read_leb_uint32(p, p_end, table_index); + if (!check_table_index(module, table_index, error_buf, error_buf_size)) { + return false; + } + + *p_table_index = table_index; + *p_buf = p; + return true; +fail: + return false; +} + +/* Element segments must match element type of table */ +static bool +check_table_elem_type(WASMModule *module, uint32 table_index, + uint32 type_from_elem_seg, char *error_buf, + uint32 error_buf_size) +{ + uint32 table_declared_elem_type; + + if (table_index < module->import_table_count) + table_declared_elem_type = + module->import_tables[table_index].u.table.table_type.elem_type; + else + table_declared_elem_type = + module->tables[table_index - module->import_table_count] + .table_type.elem_type; + + if (table_declared_elem_type == type_from_elem_seg) + return true; + +#if WASM_ENABLE_GC != 0 + /* + * balance in: anyref, funcref, (ref.null func) and (ref.func) + */ + if (table_declared_elem_type == REF_TYPE_ANYREF) + return true; + + if (table_declared_elem_type == VALUE_TYPE_FUNCREF + && type_from_elem_seg == REF_TYPE_HT_NON_NULLABLE) + return true; + + if (table_declared_elem_type == REF_TYPE_HT_NULLABLE + && type_from_elem_seg == REF_TYPE_HT_NON_NULLABLE) + return true; +#endif + + set_error_buf(error_buf, error_buf_size, "type mismatch"); + return false; +} + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +static bool +load_elem_type(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, + uint32 *p_elem_type, +#if WASM_ENABLE_GC != 0 + WASMRefType **p_elem_ref_type, +#endif + bool elemkind_zero, char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 elem_type; +#if WASM_ENABLE_GC != 0 + WASMRefType elem_ref_type; + bool need_ref_type_map; +#endif + + CHECK_BUF(p, p_end, 1); + elem_type = read_uint8(p); + if (elemkind_zero) { + if (elem_type != 0) { + set_error_buf(error_buf, error_buf_size, + "invalid reference type or unknown type"); + return false; + } + else { + *p_elem_type = VALUE_TYPE_FUNCREF; + *p_buf = p; + return true; + } + } + +#if WASM_ENABLE_GC == 0 + if (elem_type != VALUE_TYPE_FUNCREF && elem_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, + "invalid reference type or unknown type"); + return false; + } + *p_elem_type = elem_type; +#else + p--; + if (!resolve_value_type((const uint8 **)&p, p_end, module, + module->type_count, &need_ref_type_map, + &elem_ref_type, false, error_buf, error_buf_size)) { + return false; + } + if (!wasm_is_type_reftype(elem_ref_type.ref_type)) { + set_error_buf(error_buf, error_buf_size, + "invalid reference type or unknown type"); + return false; + } + *p_elem_type = elem_ref_type.ref_type; + if (need_ref_type_map) { + if (!(*p_elem_ref_type = + reftype_set_insert(module->ref_type_set, &elem_ref_type, + error_buf, error_buf_size))) { + return false; + } + } +#endif + + *p_buf = p; + return true; +fail: + return false; +} +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +static bool +load_func_index_vec(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, WASMTableSeg *table_segment, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 function_count, function_index = 0, i; + uint64 total_size; + + read_leb_uint32(p, p_end, function_count); + table_segment->value_count = function_count; + total_size = sizeof(InitializerExpression) * (uint64)function_count; + if (total_size > 0 + && !(table_segment->init_values = + (InitializerExpression *)loader_malloc(total_size, error_buf, + error_buf_size))) { + return false; + } + + for (i = 0; i < function_count; i++) { + InitializerExpression *init_expr = &table_segment->init_values[i]; + + read_leb_uint32(p, p_end, function_index); + if (!check_function_index(module, function_index, error_buf, + error_buf_size)) { + return false; + } + + init_expr->init_expr_type = INIT_EXPR_TYPE_FUNCREF_CONST; + init_expr->u.unary.v.ref_index = function_index; + } + + *p_buf = p; + return true; +fail: + return false; +} + +#if (WASM_ENABLE_GC != 0) || (WASM_ENABLE_REF_TYPES != 0) +static bool +load_init_expr_vec(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, WASMTableSeg *table_segment, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 ref_count, i; + uint64 total_size; + + read_leb_uint32(p, p_end, ref_count); + table_segment->value_count = ref_count; + total_size = sizeof(InitializerExpression) * (uint64)ref_count; + if (total_size > 0 + && !(table_segment->init_values = + (InitializerExpression *)loader_malloc(total_size, error_buf, + error_buf_size))) { + return false; + } + + for (i = 0; i < ref_count; i++) { + InitializerExpression *init_expr = &table_segment->init_values[i]; + + if (!load_init_expr(module, &p, p_end, init_expr, + table_segment->elem_type, +#if WASM_ENABLE_GC == 0 + NULL, +#else + table_segment->elem_ref_type, +#endif + error_buf, error_buf_size)) + return false; + + bh_assert((init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) + || (init_expr->init_expr_type == INIT_EXPR_TYPE_REFNULL_CONST) + || (init_expr->init_expr_type >= INIT_EXPR_TYPE_FUNCREF_CONST + && init_expr->init_expr_type + <= INIT_EXPR_TYPE_ARRAY_NEW_FIXED)); + } + + *p_buf = p; + return true; +fail: + return false; +} +#endif /* end of (WASM_ENABLE_GC != 0) || (WASM_ENABLE_REF_TYPES != 0) */ + +static bool +load_table_segment_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint8 table_elem_idx_type; + uint32 table_segment_count, i; + uint64 total_size; + WASMTableSeg *table_segment; + + read_leb_uint32(p, p_end, table_segment_count); + + if (table_segment_count) { + module->table_seg_count = table_segment_count; + total_size = sizeof(WASMTableSeg) * (uint64)table_segment_count; + if (!(module->table_segments = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + table_segment = module->table_segments; + for (i = 0; i < table_segment_count; i++, table_segment++) { + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, + "invalid value type or " + "invalid elements segment kind"); + return false; + } + table_elem_idx_type = VALUE_TYPE_I32; + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + read_leb_uint32(p, p_end, table_segment->mode); + /* last three bits */ + table_segment->mode = table_segment->mode & 0x07; + switch (table_segment->mode) { + /* elemkind/elemtype + active */ + case 0: + case 4: + { +#if WASM_ENABLE_GC != 0 + if (table_segment->mode == 0) { + /* vec(funcidx), set elem type to (ref func) */ + WASMRefType elem_ref_type = { 0 }; + table_segment->elem_type = REF_TYPE_HT_NON_NULLABLE; + wasm_set_refheaptype_common( + &elem_ref_type.ref_ht_common, false, + HEAP_TYPE_FUNC); + if (!(table_segment->elem_ref_type = reftype_set_insert( + module->ref_type_set, &elem_ref_type, + error_buf, error_buf_size))) + return false; + } + else { + /* vec(expr), set elem type to funcref */ + table_segment->elem_type = VALUE_TYPE_FUNCREF; + } +#else + table_segment->elem_type = VALUE_TYPE_FUNCREF; +#endif + table_segment->table_index = 0; + + if (!check_table_index(module, table_segment->table_index, + error_buf, error_buf_size)) + return false; + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = + is_table_64bit(module, table_segment->table_index) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (!load_init_expr(module, &p, p_end, + &table_segment->base_offset, + table_elem_idx_type, NULL, error_buf, + error_buf_size)) + return false; + + if (table_segment->mode == 0) { + /* vec(funcidx) */ + if (!load_func_index_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + else { + /* vec(expr) */ + if (!load_init_expr_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + + if (!check_table_elem_type(module, + table_segment->table_index, + table_segment->elem_type, + error_buf, error_buf_size)) + return false; + + break; + } + /* elemkind + passive/declarative */ + case 1: + case 3: + if (!load_elem_type(module, &p, p_end, + &table_segment->elem_type, +#if WASM_ENABLE_GC != 0 + &table_segment->elem_ref_type, +#endif + true, error_buf, error_buf_size)) + return false; + /* vec(funcidx) */ + if (!load_func_index_vec(&p, p_end, module, table_segment, + error_buf, error_buf_size)) + return false; + break; + /* elemkind/elemtype + table_idx + active */ + case 2: + case 6: + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, + error_buf, error_buf_size)) + return false; +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = + is_table_64bit(module, table_segment->table_index) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (!load_init_expr(module, &p, p_end, + &table_segment->base_offset, + table_elem_idx_type, NULL, error_buf, + error_buf_size)) + return false; + if (!load_elem_type(module, &p, p_end, + &table_segment->elem_type, +#if WASM_ENABLE_GC != 0 + &table_segment->elem_ref_type, +#endif + table_segment->mode == 2 ? true : false, + error_buf, error_buf_size)) + return false; + + if (table_segment->mode == 2) { + /* vec(funcidx) */ + if (!load_func_index_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + else { + /* vec(expr) */ + if (!load_init_expr_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + + if (!check_table_elem_type(module, + table_segment->table_index, + table_segment->elem_type, + error_buf, error_buf_size)) + return false; + + break; + case 5: + case 7: + if (!load_elem_type(module, &p, p_end, + &table_segment->elem_type, +#if WASM_ENABLE_GC != 0 + &table_segment->elem_ref_type, +#endif + false, error_buf, error_buf_size)) + return false; + /* vec(expr) */ + if (!load_init_expr_vec(&p, p_end, module, table_segment, + error_buf, error_buf_size)) + return false; + break; + default: + set_error_buf(error_buf, error_buf_size, + "unknown element segment kind"); + return false; + } +#else /* else of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + /* + * like: 00 41 05 0b 04 00 01 00 01 + * for: (elem 0 (offset (i32.const 5)) $f1 $f2 $f1 $f2) + */ + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, error_buf, + error_buf_size)) + return false; +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = + is_table_64bit(module, table_segment->table_index) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (!load_init_expr(module, &p, p_end, &table_segment->base_offset, + table_elem_idx_type, NULL, error_buf, + error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, table_segment, + error_buf, error_buf_size)) + return false; + + table_segment->elem_type = VALUE_TYPE_FUNCREF; + + if (!check_table_elem_type(module, table_segment->table_index, + table_segment->elem_type, error_buf, + error_buf_size)) + return false; +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_MEMORY64 != 0 + if (table_elem_idx_type == VALUE_TYPE_I64 + && table_segment->base_offset.u.unary.v.u64 > UINT32_MAX) { + set_error_buf(error_buf, error_buf_size, + "In table64, table base offset can't be " + "larger than UINT32_MAX"); + return false; + } +#endif + +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (table_segment->elem_type == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; +#endif + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load table segment section success.\n"); + return true; +fail: + return false; +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +check_data_count_consistency(bool has_datacount_section, int datacount_len, + int data_seg_len, char *error_buf, + uint32 error_buf_size) +{ + if (has_datacount_section && datacount_len != data_seg_len) { + set_error_buf(error_buf, error_buf_size, + "data count and data section have inconsistent lengths"); + return false; + } + return true; +} +#endif + +static bool +load_data_segment_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, +#if WASM_ENABLE_BULK_MEMORY != 0 + bool has_datacount_section, +#endif + bool clone_data_seg, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count, i, mem_index, data_seg_len; + uint64 total_size; + WASMDataSeg *dataseg; + InitializerExpression init_expr; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive = false; + uint32 mem_flag; +#endif + uint8 mem_offset_type = VALUE_TYPE_I32; + + read_leb_uint32(p, p_end, data_seg_count); + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!check_data_count_consistency(has_datacount_section, + module->data_seg_count1, data_seg_count, + error_buf, error_buf_size)) { + return false; + } +#endif + + if (data_seg_count) { + module->data_seg_count = data_seg_count; + total_size = sizeof(WASMDataSeg *) * (uint64)data_seg_count; + if (!(module->data_segments = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < data_seg_count; i++) { + read_leb_uint32(p, p_end, mem_index); +#if WASM_ENABLE_BULK_MEMORY != 0 + is_passive = false; + mem_flag = mem_index & 0x03; + switch (mem_flag) { + case 0x01: + is_passive = true; +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + break; + case 0x00: + /* no memory index, treat index as 0 */ + mem_index = 0; + goto check_mem_index; + case 0x02: + /* read following memory index */ + read_leb_uint32(p, p_end, mem_index); +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + check_mem_index: + if (mem_index + >= module->import_memory_count + module->memory_count) { + set_error_buf_v(error_buf, error_buf_size, + "unknown memory %d", mem_index); + return false; + } + break; + case 0x03: + default: + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + break; + } +#else + if (mem_index + >= module->import_memory_count + module->memory_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown memory %d", + mem_index); + return false; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif + { +#if WASM_ENABLE_MEMORY64 != 0 + /* This memory_flag is from memory instead of data segment */ + uint8 memory_flag; + if (module->import_memory_count > 0) { + memory_flag = module->import_memories[mem_index] + .u.memory.mem_type.flags; + } + else { + memory_flag = + module + ->memories[mem_index - module->import_memory_count] + .flags; + } + mem_offset_type = memory_flag & MEMORY64_FLAG ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#else + mem_offset_type = VALUE_TYPE_I32; +#endif + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif + if (!load_init_expr(module, &p, p_end, &init_expr, + mem_offset_type, NULL, error_buf, + error_buf_size)) + return false; + + read_leb_uint32(p, p_end, data_seg_len); + + if (!(dataseg = module->data_segments[i] = loader_malloc( + sizeof(WASMDataSeg), error_buf, error_buf_size))) { +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(module, &init_expr); +#endif + return false; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + dataseg->is_passive = is_passive; + if (!is_passive) +#endif + { + bh_memcpy_s(&dataseg->base_offset, + sizeof(InitializerExpression), &init_expr, + sizeof(InitializerExpression)); + + dataseg->memory_index = mem_index; + } + + dataseg->data_length = data_seg_len; + CHECK_BUF(p, p_end, data_seg_len); + if (clone_data_seg) { + if (!(dataseg->data = loader_malloc( + dataseg->data_length, error_buf, error_buf_size))) { + return false; + } + + bh_memcpy_s(dataseg->data, dataseg->data_length, p, + data_seg_len); + } + else { + dataseg->data = (uint8 *)p; + } + dataseg->is_data_cloned = clone_data_seg; + p += data_seg_len; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load data segment section success.\n"); + return true; +fail: + return false; +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +load_datacount_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count1 = 0; + + read_leb_uint32(p, p_end, data_seg_count1); + module->data_seg_count1 = data_seg_count1; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + LOG_VERBOSE("Load datacount section success.\n"); + return true; +fail: + return false; +} +#endif + +#if WASM_ENABLE_TAGS != 0 +static bool +load_tag_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_code, + const uint8 *buf_code_end, WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + (void)buf_code; + (void)buf_code_end; + + const uint8 *p = buf, *p_end = buf_end; + size_t total_size = 0; + /* number of tags defined in the section */ + uint32 section_tag_count = 0; + uint8 tag_attribute; + uint32 tag_type; + WASMTag *tag = NULL; + + /* get tag count */ + read_leb_uint32(p, p_end, section_tag_count); + if (is_indices_overflow(module->import_tag_count, section_tag_count, + error_buf, error_buf_size)) + return false; + + module->tag_count = section_tag_count; + + if (section_tag_count) { + total_size = sizeof(WASMTag *) * module->tag_count; + if (!(module->tags = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + /* load each tag, imported tags precede the tags */ + uint32 tag_index; + for (tag_index = 0; tag_index < section_tag_count; tag_index++) { + + /* get the one byte attribute */ + CHECK_BUF(p, p_end, 1); + tag_attribute = read_uint8(p); + + /* get type */ + read_leb_uint32(p, p_end, tag_type); + /* compare against module->types */ + if (!check_function_type(module, tag_type, error_buf, + error_buf_size)) { + return false; + } + + /* get return type (must be 0) */ + /* check, that the type of the referred tag returns void */ + WASMFuncType *func_type = (WASMFuncType *)module->types[tag_type]; + if (func_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "non-empty tag result type"); + + goto fail; + } + + if (!(tag = module->tags[tag_index] = loader_malloc( + sizeof(WASMTag), error_buf, error_buf_size))) { + return false; + } + + /* store to module tag declarations */ + tag->attribute = tag_attribute; + tag->type = tag_type; + tag->tag_type = func_type; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load tag section success.\n"); + return true; +fail: + return false; +} +#endif /* end of WASM_ENABLE_TAGS != 0 */ + +static bool +load_code_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_func, + const uint8 *buf_func_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_func = buf_func; + uint32 func_count = 0, code_count; + + /* code has been loaded in function section, so pass it here, just check + * whether function and code section have inconsistent lengths */ + read_leb_uint32(p, p_end, code_count); + + if (buf_func) + read_leb_uint32(p_func, buf_func_end, func_count); + + if (func_count != code_count) { + set_error_buf(error_buf, error_buf_size, + "function and code section have inconsistent lengths"); + return false; + } + + LOG_VERBOSE("Load code segment section success.\n"); + (void)module; + return true; +fail: + return false; +} + +static bool +load_start_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + WASMFuncType *type; + uint32 start_function; + + read_leb_uint32(p, p_end, start_function); + + if (start_function + >= module->function_count + module->import_function_count) { + set_error_buf(error_buf, error_buf_size, "unknown function"); + return false; + } + + if (start_function < module->import_function_count) + type = module->import_functions[start_function].u.function.func_type; + else + type = module->functions[start_function - module->import_function_count] + ->func_type; + if (type->param_count != 0 || type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, "invalid start function"); + return false; + } + + module->start_function = start_function; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load start section success.\n"); + return true; +fail: + return false; +} + +#if WASM_ENABLE_STRINGREF != 0 +static bool +load_stringref_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, bool is_load_from_file_buf, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + int32 deferred_count, immediate_count, string_length, i; + uint64 total_size; + + read_leb_uint32(p, p_end, deferred_count); + read_leb_uint32(p, p_end, immediate_count); + + /* proposal set deferred_count for future extension */ + if (deferred_count != 0) { + goto fail; + } + + if (immediate_count > 0) { + total_size = sizeof(char *) * (uint64)immediate_count; + if (!(module->string_literal_ptrs = + loader_malloc(total_size, error_buf, error_buf_size))) { + goto fail; + } + module->string_literal_count = immediate_count; + + total_size = sizeof(uint32) * (uint64)immediate_count; + if (!(module->string_literal_lengths = + loader_malloc(total_size, error_buf, error_buf_size))) { + goto fail; + } + + for (i = 0; i < immediate_count; i++) { + read_leb_uint32(p, p_end, string_length); + + CHECK_BUF(p, p_end, string_length); + module->string_literal_ptrs[i] = p; + module->string_literal_lengths[i] = string_length; + p += string_length; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + goto fail; + } + + LOG_VERBOSE("Load stringref section success.\n"); + return true; + +fail: + return false; +} +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 +static bool +handle_name_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 name_type, subsection_size; + uint32 previous_name_type = 0; + uint32 num_func_name; + uint32 func_index; + uint32 previous_func_index = ~0U; + uint32 func_name_len; + uint32 name_index; + int i = 0; + + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + + while (p < p_end) { + read_leb_uint32(p, p_end, name_type); + if (i != 0) { + if (name_type == previous_name_type) { + set_error_buf(error_buf, error_buf_size, + "duplicate sub-section"); + return false; + } + if (name_type < previous_name_type) { + set_error_buf(error_buf, error_buf_size, + "out-of-order sub-section"); + return false; + } + } + previous_name_type = name_type; + read_leb_uint32(p, p_end, subsection_size); + CHECK_BUF(p, p_end, subsection_size); + switch (name_type) { + case SUB_SECTION_TYPE_FUNC: + if (subsection_size) { + read_leb_uint32(p, p_end, num_func_name); + for (name_index = 0; name_index < num_func_name; + name_index++) { + read_leb_uint32(p, p_end, func_index); + if (func_index == previous_func_index) { + set_error_buf(error_buf, error_buf_size, + "duplicate function name"); + return false; + } + if (func_index < previous_func_index + && previous_func_index != ~0U) { + set_error_buf(error_buf, error_buf_size, + "out-of-order function index "); + return false; + } + previous_func_index = func_index; + read_leb_uint32(p, p_end, func_name_len); + CHECK_BUF(p, p_end, func_name_len); + /* Skip the import functions */ + if (func_index >= module->import_function_count) { + func_index -= module->import_function_count; + if (func_index >= module->function_count) { + set_error_buf(error_buf, error_buf_size, + "out-of-range function index"); + return false; + } + if (!(module->functions[func_index]->field_name = + wasm_const_str_list_insert( + p, func_name_len, module, +#if WASM_ENABLE_WAMR_COMPILER != 0 + false, +#else + is_load_from_file_buf, +#endif + error_buf, error_buf_size))) { + return false; + } + } + p += func_name_len; + } + } + break; + case SUB_SECTION_TYPE_MODULE: /* TODO: Parse for module subsection + */ + case SUB_SECTION_TYPE_LOCAL: /* TODO: Parse for local subsection */ + default: + p = p + subsection_size; + break; + } + i++; + } + + return true; +fail: + return false; +} +#endif + +#if WASM_ENABLE_BRANCH_HINTS != 0 +/** + * Count the number of branch instructions for the specified function. + */ +static uint32 +calculate_num_branch_instructions(const WASMFunction *func) +{ + const uint8 *code = func->code; + const uint8 *code_end = code + func->code_size; + uint32 max_hints = 0; + + while (code < code_end) { + uint8 opcode = *code++; + + if (opcode == WASM_OP_IF || opcode == WASM_OP_BR_IF) { + max_hints++; + } + } + + return max_hints; +} + +static bool +handle_branch_hint_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + if (module->function_hints == NULL) { + module->function_hints = loader_malloc( + sizeof(struct WASMCompilationHint) * module->function_count, + error_buf, error_buf_size); + } + uint32 numFunctionHints = 0; + read_leb_uint32(buf, buf_end, numFunctionHints); + for (uint32 i = 0; i < numFunctionHints; ++i) { + uint32 func_idx; + read_leb_uint32(buf, buf_end, func_idx); + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + if (func_idx < module->import_function_count) { + set_error_buf(error_buf, error_buf_size, + "branch hint for imported function is not allowed"); + goto fail; + } + + struct WASMCompilationHint *current_hint = + (struct WASMCompilationHint *)&module + ->function_hints[func_idx - module->import_function_count]; + while (current_hint->next != NULL) { + current_hint = current_hint->next; + } + + uint32 num_hints; + read_leb_uint32(buf, buf_end, num_hints); + + /* Ensure that num_hints doesn't exceed the actual number of branch + * instructions */ + WASMFunction *func = + module->functions[func_idx - module->import_function_count]; + uint32 max_branch_instructions = + calculate_num_branch_instructions(func); + if (num_hints > max_branch_instructions) { + set_error_buf_v( + error_buf, error_buf_size, + "invalid number of branch hints: expected at most %u, got %u", + max_branch_instructions, num_hints); + goto fail; + } + + struct WASMCompilationHintBranchHint *new_hints = loader_malloc( + sizeof(struct WASMCompilationHintBranchHint) * num_hints, error_buf, + error_buf_size); + if (!new_hints) { + goto fail; + } + for (uint32 j = 0; j < num_hints; ++j) { + struct WASMCompilationHintBranchHint *new_hint = &new_hints[j]; + new_hint->next = NULL; + new_hint->type = WASM_COMPILATION_BRANCH_HINT; + read_leb_uint32(buf, buf_end, new_hint->offset); + + /* Validate offset is within the function's code bounds */ + if (new_hint->offset >= func->code_size) { + set_error_buf_v( + error_buf, error_buf_size, + "invalid branch hint offset: %u exceeds function " + "code size %u", + new_hint->offset, func->code_size); + goto fail; + } + + uint32 size; + read_leb_uint32(buf, buf_end, size); + if (size != 1) { + set_error_buf_v(error_buf, error_buf_size, + "invalid branch hint size, expected 1, got %d.", + size); + /* Do not free new_hints here - any hints already linked into + * the module structure will be freed during module cleanup. + * Freeing here would cause a double-free. */ + goto fail; + } + + uint8 data = *buf++; + if (data == 0x00) + new_hint->is_likely = false; + else if (data == 0x01) + new_hint->is_likely = true; + else { + set_error_buf_v(error_buf, error_buf_size, + "invalid branch hint, expected 0 or 1, got %d", + data); + /* Do not free new_hints here - any hints already linked into + * the module structure will be freed during module cleanup. + * Freeing here would cause a double-free. */ + goto fail; + } + + current_hint->next = (struct WASMCompilationHint *)new_hint; + current_hint = (struct WASMCompilationHint *)new_hint; + } + } + if (buf != buf_end) { + set_error_buf(error_buf, error_buf_size, + "invalid branch hint section, not filled until end"); + goto fail; + } + return true; +fail: + return false; +} +#endif + +static bool +load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + char section_name[32]; + uint32 name_len, buffer_len; + + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + + read_leb_uint32(p, p_end, name_len); + + if (p + name_len > p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + + if (!wasm_check_utf8_str(p, name_len)) { + set_error_buf(error_buf, error_buf_size, "invalid UTF-8 encoding"); + return false; + } + + buffer_len = sizeof(section_name); + memset(section_name, 0, buffer_len); + if (name_len < buffer_len) { + bh_memcpy_s(section_name, buffer_len, p, name_len); + } + else { + bh_memcpy_s(section_name, buffer_len, p, buffer_len - 4); + memset(section_name + buffer_len - 4, '.', 3); + } + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + if (name_len == 4 && memcmp(p, "name", 4) == 0) { + module->name_section_buf = buf; + module->name_section_buf_end = buf_end; + p += name_len; + if (!handle_name_section(p, p_end, module, is_load_from_file_buf, + error_buf, error_buf_size)) { + return false; + } + LOG_VERBOSE("Load custom name section success."); + } +#endif + +#if WASM_ENABLE_BRANCH_HINTS != 0 + if (name_len == 25 + && memcmp((const char *)p, "metadata.code.branch_hint", 25) == 0) { + p += name_len; + if (!handle_branch_hint_section(p, p_end, module, error_buf, + error_buf_size)) { + return false; + } + LOG_VERBOSE("Load branch hint section success."); + } +#else + if (name_len == 25 + && memcmp((const char *)p, "metadata.code.branch_hint", 25) == 0) { + LOG_WARNING("Found branch hint section, but branch hints are disabled " + "in this build, skipping."); + } +#endif + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + { + WASMCustomSection *section = + loader_malloc(sizeof(WASMCustomSection), error_buf, error_buf_size); + + if (!section) { + return false; + } + + section->name_addr = (char *)p; + section->name_len = name_len; + section->content_addr = (uint8 *)(p + name_len); + section->content_len = (uint32)(p_end - p - name_len); + + section->next = module->custom_section_list; + module->custom_section_list = section; + LOG_VERBOSE("Load custom section [%s] success.", section_name); + return true; + } +#endif + + LOG_VERBOSE("Ignore custom section [%s].", section_name); + + (void)is_load_from_file_buf; + (void)module; + return true; +fail: + return false; +} + +static void +calculate_global_data_offset(WASMModule *module) +{ + uint32 i, data_offset; + + data_offset = 0; + for (i = 0; i < module->import_global_count; i++) { + WASMGlobalImport *import_global = + &((module->import_globals + i)->u.global); +#if WASM_ENABLE_FAST_JIT != 0 + import_global->data_offset = data_offset; +#endif + data_offset += wasm_value_type_size(import_global->type.val_type); + } + + for (i = 0; i < module->global_count; i++) { + WASMGlobal *global = module->globals + i; +#if WASM_ENABLE_FAST_JIT != 0 + global->data_offset = data_offset; +#endif + data_offset += wasm_value_type_size(global->type.val_type); + } + + module->global_data_size = data_offset; +} + +#if WASM_ENABLE_FAST_JIT != 0 +static bool +init_fast_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_LAZY_JIT != 0 + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); +#endif + uint32 i; + + if (!module->function_count) + return true; + + if (!(module->fast_jit_func_ptrs = + loader_malloc(sizeof(void *) * module->function_count, error_buf, + error_buf_size))) { + return false; + } + +#if WASM_ENABLE_LAZY_JIT != 0 + for (i = 0; i < module->function_count; i++) { + module->fast_jit_func_ptrs[i] = + jit_globals->compile_fast_jit_and_then_call; + } +#endif + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (os_mutex_init(&module->fast_jit_thread_locks[i]) != 0) { + set_error_buf(error_buf, error_buf_size, + "init fast jit thread lock failed"); + return false; + } + module->fast_jit_thread_locks_inited[i] = true; + } + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ + +#if WASM_ENABLE_JIT != 0 +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; +#if WASM_ENABLE_GC != 0 + bool gc_enabled = true; +#else + bool gc_enabled = false; +#endif + + if (module->function_count == 0) + return true; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (os_mutex_init(&module->tierup_wait_lock) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup lock failed"); + return false; + } + if (os_cond_init(&module->tierup_wait_cond) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup cond failed"); + os_mutex_destroy(&module->tierup_wait_lock); + return false; + } + module->tierup_wait_lock_inited = true; +#endif + + size = sizeof(void *) * (uint64)module->function_count + + sizeof(bool) * (uint64)module->function_count; + if (!(module->func_ptrs = loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + module->func_ptrs_compiled = + (bool *)((uint8 *)module->func_ptrs + + sizeof(void *) * module->function_count); + + module->comp_data = aot_create_comp_data(module, NULL, gc_enabled); + if (!module->comp_data) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return false; + } + + option.is_jit_mode = true; + + option.opt_level = llvm_jit_options->opt_level; + option.size_level = llvm_jit_options->size_level; + option.segue_flags = llvm_jit_options->segue_flags; + option.quick_invoke_c_api_import = + llvm_jit_options->quick_invoke_c_api_import; + +#if WASM_ENABLE_BULK_MEMORY != 0 + option.enable_bulk_memory = true; +#endif +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + option.enable_bulk_memory_opt = true; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + option.enable_thread_mgr = true; +#endif +#if WASM_ENABLE_TAIL_CALL != 0 + option.enable_tail_call = true; +#endif +#if WASM_ENABLE_SIMD != 0 + option.enable_simd = true; +#endif +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + option.enable_ref_types = true; +#elif WASM_ENABLE_GC != 0 + option.enable_gc = true; +#endif +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 + option.enable_call_indirect_overlong = true; +#endif + option.enable_aux_stack_check = true; +#if WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0 \ + || WASM_ENABLE_AOT_STACK_FRAME != 0 + option.aux_stack_frame_type = AOT_STACK_FRAME_TYPE_STANDARD; + aot_call_stack_features_init_default(&option.call_stack_features); +#endif +#if WASM_ENABLE_PERF_PROFILING != 0 + option.enable_perf_profiling = true; +#endif +#if WASM_ENABLE_MEMORY_PROFILING != 0 + option.enable_memory_profiling = true; + option.enable_stack_estimation = true; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + option.enable_shared_heap = true; +#endif + + module->comp_ctx = aot_create_comp_context(module->comp_data, &option); + if (!module->comp_ctx) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return false; + } + + return true; +} + +static bool +init_llvm_jit_functions_stage2(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + char *aot_last_error; + uint32 i; + + if (module->function_count == 0) + return true; + + if (!aot_compile_wasm(module->comp_ctx)) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return false; + } + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->orcjit_stop_compiling) + return false; +#endif + + bh_print_time("Begin to lookup llvm jit functions"); + + for (i = 0; i < module->function_count; i++) { + LLVMOrcJITTargetAddress func_addr = 0; + LLVMErrorRef error; + char func_name[48]; + + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, i); + error = LLVMOrcLLLazyJITLookup(module->comp_ctx->orc_jit, &func_addr, + func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + set_error_buf_v(error_buf, error_buf_size, + "failed to compile llvm jit function: %s", err_msg); + LLVMDisposeErrorMessage(err_msg); + return false; + } + + /** + * No need to lock the func_ptr[func_idx] here as it is basic + * data type, the load/store for it can be finished by one cpu + * instruction, and there can be only one cpu instruction + * loading/storing at the same time. + */ + 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 + } + + bh_print_time("End lookup llvm jit functions"); + + return true; +} +#endif /* end of WASM_ENABLE_JIT != 0 */ + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 +static void * +init_llvm_jit_functions_stage2_callback(void *arg) +{ + WASMModule *module = (WASMModule *)arg; + char error_buf[128]; + uint32 error_buf_size = (uint32)sizeof(error_buf); + + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + module->orcjit_stop_compiling = true; + return NULL; + } + + os_mutex_lock(&module->tierup_wait_lock); + module->llvm_jit_inited = true; + os_cond_broadcast(&module->tierup_wait_cond); + os_mutex_unlock(&module->tierup_wait_lock); + + return NULL; +} +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 +/* The callback function to compile jit functions */ +static void * +orcjit_thread_callback(void *arg) +{ + OrcJitThreadArg *thread_arg = (OrcJitThreadArg *)arg; +#if WASM_ENABLE_JIT != 0 + AOTCompContext *comp_ctx = thread_arg->comp_ctx; +#endif + WASMModule *module = thread_arg->module; + uint32 group_idx = thread_arg->group_idx; + uint32 group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; + uint32 func_count = module->function_count; + uint32 i; + +#if WASM_ENABLE_FAST_JIT != 0 + /* Compile fast jit functions of this group */ + for (i = group_idx; i < func_count; i += group_stride) { + if (!jit_compiler_compile(module, i + module->import_function_count)) { + LOG_ERROR("failed to compile fast jit function %u\n", i); + break; + } + + if (module->orcjit_stop_compiling) { + return NULL; + } + } +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + os_mutex_lock(&module->tierup_wait_lock); + module->fast_jit_ready_groups++; + os_mutex_unlock(&module->tierup_wait_lock); +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* For JIT tier-up, set each llvm jit func to call_to_fast_jit */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + uint32 j; + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + if (!jit_compiler_set_call_to_fast_jit( + module, + i + j * group_stride + module->import_function_count)) { + LOG_ERROR( + "failed to compile call_to_fast_jit for func %u\n", + i + j * group_stride + module->import_function_count); + module->orcjit_stop_compiling = true; + return NULL; + } + } + if (module->orcjit_stop_compiling) { + return NULL; + } + } + } + + /* Wait until init_llvm_jit_functions_stage2 finishes and all + fast jit functions are compiled */ + os_mutex_lock(&module->tierup_wait_lock); + while (!(module->llvm_jit_inited && module->enable_llvm_jit_compilation + && module->fast_jit_ready_groups >= group_stride)) { + os_cond_reltimedwait(&module->tierup_wait_cond, + &module->tierup_wait_lock, 10000); + if (module->orcjit_stop_compiling) { + /* init_llvm_jit_functions_stage2 failed */ + os_mutex_unlock(&module->tierup_wait_lock); + return NULL; + } + } + os_mutex_unlock(&module->tierup_wait_lock); +#endif + +#if WASM_ENABLE_JIT != 0 + /* Compile llvm jit functions of this group */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + LLVMOrcJITTargetAddress func_addr = 0; + LLVMErrorRef error; + char func_name[48]; + typedef void (*F)(void); + union { + F f; + void *v; + } u; + uint32 j; + + snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, i, + "_wrapper"); + LOG_DEBUG("compile llvm jit func %s", func_name); + error = + LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + LOG_ERROR("failed to compile llvm jit function %u: %s", i, err_msg); + LLVMDisposeErrorMessage(err_msg); + break; + } + + /* Call the jit wrapper function to trigger its compilation, so as + to compile the actual jit functions, since we add the latter to + function list in the PartitionFunction callback */ + u.v = (void *)func_addr; + u.f(); + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + module->func_ptrs_compiled[i + j * group_stride] = true; +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, + i + j * group_stride); + error = LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, + func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + LOG_ERROR("failed to compile llvm jit function %u: %s", i, + err_msg); + LLVMDisposeErrorMessage(err_msg); + /* Ignore current llvm jit func, as its func ptr is + previous set to call_to_fast_jit, which also works */ + continue; + } + + jit_compiler_set_llvm_jit_func_ptr( + module, + i + j * group_stride + module->import_function_count, + (void *)func_addr); + + /* Try to switch to call this llvm jit function instead of + fast jit function from fast jit jitted code */ + jit_compiler_set_call_to_llvm_jit( + module, + i + j * group_stride + module->import_function_count); +#endif + } + } + + if (module->orcjit_stop_compiling) { + break; + } + } +#endif + + return NULL; +} + +static void +orcjit_stop_compile_threads(WASMModule *module) +{ +#if WASM_ENABLE_LAZY_JIT != 0 + uint32 i, thread_num = (uint32)(sizeof(module->orcjit_thread_args) + / sizeof(OrcJitThreadArg)); + + module->orcjit_stop_compiling = true; + for (i = 0; i < thread_num; i++) { + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } +#endif +} + +static bool +compile_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + uint32 thread_num = + (uint32)(sizeof(module->orcjit_thread_args) / sizeof(OrcJitThreadArg)); + uint32 i, j; + + bh_print_time("Begin to compile jit functions"); + + /* Create threads to compile the jit functions */ + for (i = 0; i < thread_num && i < module->function_count; i++) { +#if WASM_ENABLE_JIT != 0 + module->orcjit_thread_args[i].comp_ctx = module->comp_ctx; +#endif + module->orcjit_thread_args[i].module = module; + module->orcjit_thread_args[i].group_idx = i; + + if (os_thread_create(&module->orcjit_threads[i], orcjit_thread_callback, + (void *)&module->orcjit_thread_args[i], + APP_THREAD_STACK_SIZE_DEFAULT) + != 0) { + set_error_buf(error_buf, error_buf_size, + "create orcjit compile thread failed"); + /* Terminate the threads created */ + module->orcjit_stop_compiling = true; + for (j = 0; j < i; j++) { + os_thread_join(module->orcjit_threads[j], NULL); + } + return false; + } + } + +#if WASM_ENABLE_LAZY_JIT == 0 + /* Wait until all jit functions are compiled for eager mode */ + for (i = 0; i < thread_num; i++) { + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } + +#if WASM_ENABLE_FAST_JIT != 0 + /* Ensure all the fast-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!jit_compiler_is_compiled(module, + i + module->import_function_count)) { + set_error_buf(error_buf, error_buf_size, + "failed to compile fast jit function"); + return false; + } + } +#endif + +#if WASM_ENABLE_JIT != 0 + /* Ensure all the llvm-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!module->func_ptrs_compiled[i]) { + set_error_buf(error_buf, error_buf_size, + "failed to compile llvm jit function"); + return false; + } + } +#endif +#endif /* end of WASM_ENABLE_LAZY_JIT == 0 */ + + bh_print_time("End compile jit functions"); + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 */ + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + uint32 cur_func_idx, char *error_buf, + uint32 error_buf_size); + +#if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_LABELS_AS_VALUES != 0 +void ** +wasm_interp_get_handle_table(void); + +static void **handle_table; +#endif + +static bool +load_from_sections(WASMModule *module, WASMSection *sections, + bool is_load_from_file_buf, bool wasm_binary_freeable, + bool no_resolve, char *error_buf, uint32 error_buf_size) +{ + WASMExport *export; + WASMSection *section = sections; + const uint8 *buf, *buf_end, *buf_code = NULL, *buf_code_end = NULL, + *buf_func = NULL, *buf_func_end = NULL; + WASMGlobal *aux_data_end_global = NULL, *aux_heap_base_global = NULL; + WASMGlobal *aux_stack_top_global = NULL, *global; + uint64 aux_data_end = (uint64)-1LL, aux_heap_base = (uint64)-1LL, + aux_stack_top = (uint64)-1LL; + uint32 global_index, func_index, i; + uint32 aux_data_end_global_index = (uint32)-1; + uint32 aux_heap_base_global_index = (uint32)-1; + WASMFuncType *func_type; + uint8 malloc_free_io_type = VALUE_TYPE_I32; + bool reuse_const_strings = is_load_from_file_buf && !wasm_binary_freeable; + bool clone_data_seg = is_load_from_file_buf && wasm_binary_freeable; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool has_datacount_section = false; +#endif + + /* Find code and function sections if have */ + while (section) { + if (section->section_type == SECTION_TYPE_CODE) { + buf_code = section->section_body; + buf_code_end = buf_code + section->section_body_size; +#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_DEBUG_AOT != 0 + module->buf_code = (uint8 *)buf_code; + module->buf_code_size = section->section_body_size; +#endif + } + else if (section->section_type == SECTION_TYPE_FUNC) { + buf_func = section->section_body; + buf_func_end = buf_func + section->section_body_size; + } + section = section->next; + } + + section = sections; + while (section) { + buf = section->section_body; + buf_end = buf + section->section_body_size; + switch (section->section_type) { + case SECTION_TYPE_USER: + /* unsupported user section, ignore it. */ + if (!load_user_section(buf, buf_end, module, + reuse_const_strings, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_TYPE: + if (!load_type_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_IMPORT: + if (!load_import_section(buf, buf_end, module, + reuse_const_strings, no_resolve, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_FUNC: + if (!load_function_section(buf, buf_end, buf_code, buf_code_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_TABLE: + if (!load_table_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_MEMORY: + if (!load_memory_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; +#if WASM_ENABLE_TAGS != 0 + case SECTION_TYPE_TAG: + /* load tag declaration section */ + if (!load_tag_section(buf, buf_end, buf_code, buf_code_end, + module, error_buf, error_buf_size)) + return false; + break; +#endif + case SECTION_TYPE_GLOBAL: + if (!load_global_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_EXPORT: + if (!load_export_section(buf, buf_end, module, + reuse_const_strings, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_START: + if (!load_start_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_ELEM: + if (!load_table_segment_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_CODE: + if (!load_code_section(buf, buf_end, buf_func, buf_func_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_DATA: + if (!load_data_segment_section(buf, buf_end, module, +#if WASM_ENABLE_BULK_MEMORY != 0 + has_datacount_section, +#endif + clone_data_seg, error_buf, + error_buf_size)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case SECTION_TYPE_DATACOUNT: + if (!load_datacount_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + has_datacount_section = true; + break; +#endif +#if WASM_ENABLE_STRINGREF != 0 + case SECTION_TYPE_STRINGREF: + if (!load_stringref_section(buf, buf_end, module, + reuse_const_strings, error_buf, + error_buf_size)) + return false; + break; +#endif + default: + set_error_buf(error_buf, error_buf_size, "invalid section id"); + return false; + } + + section = section->next; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!check_data_count_consistency( + has_datacount_section, module->data_seg_count1, + module->data_seg_count, error_buf, error_buf_size)) { + return false; + } +#endif + + module->aux_data_end_global_index = (uint32)-1; + module->aux_heap_base_global_index = (uint32)-1; + module->aux_stack_top_global_index = (uint32)-1; + + /* Resolve auxiliary data/stack/heap info and reset memory info */ + export = module->exports; + for (i = 0; i < module->export_count; i++, export ++) { + if (export->kind == EXPORT_KIND_GLOBAL) { + if (!strcmp(export->name, "__heap_base")) { + if (export->index < module->import_global_count) { + LOG_DEBUG("Skip the process if __heap_base is imported " + "instead of being a local global"); + continue; + } + + /* only process linker-generated symbols */ + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type.val_type == VALUE_TYPE_I32 + && !global->type.is_mutable + && global->init_expr.init_expr_type + == INIT_EXPR_TYPE_I32_CONST) { + aux_heap_base_global = global; + aux_heap_base = + (uint64)(uint32)global->init_expr.u.unary.v.i32; + aux_heap_base_global_index = export->index; + LOG_VERBOSE("Found aux __heap_base global, value: %" PRIu64, + aux_heap_base); + } + } + else if (!strcmp(export->name, "__data_end")) { + if (export->index < module->import_global_count) { + LOG_DEBUG("Skip the process if __data_end is imported " + "instead of being a local global"); + continue; + } + + /* only process linker-generated symbols */ + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type.val_type == VALUE_TYPE_I32 + && !global->type.is_mutable + && global->init_expr.init_expr_type + == INIT_EXPR_TYPE_I32_CONST) { + aux_data_end_global = global; + aux_data_end = + (uint64)(uint32)global->init_expr.u.unary.v.i32; + aux_data_end_global_index = export->index; + LOG_VERBOSE("Found aux __data_end global, value: %" PRIu64, + aux_data_end); + + aux_data_end = align_uint64(aux_data_end, 16); + } + } + + /* For module compiled with -pthread option, the global is: + [0] stack_top <-- 0 + [1] tls_pointer + [2] tls_size + [3] data_end <-- 3 + [4] global_base + [5] heap_base <-- 5 + [6] dso_handle + + For module compiled without -pthread option: + [0] stack_top <-- 0 + [1] data_end <-- 1 + [2] global_base + [3] heap_base <-- 3 + [4] dso_handle + */ + if (aux_data_end_global && aux_heap_base_global + && aux_data_end <= aux_heap_base) { + module->aux_data_end_global_index = aux_data_end_global_index; + module->aux_data_end = aux_data_end; + module->aux_heap_base_global_index = aux_heap_base_global_index; + module->aux_heap_base = aux_heap_base; + + /* Resolve aux stack top global */ + for (global_index = 0; global_index < module->global_count; + global_index++) { + global = module->globals + global_index; + if (global->type.is_mutable /* heap_base and data_end is + not mutable */ + && global->type.val_type == VALUE_TYPE_I32 + && global->init_expr.init_expr_type + == INIT_EXPR_TYPE_I32_CONST + && (uint64)(uint32)global->init_expr.u.unary.v.i32 + <= aux_heap_base) { + aux_stack_top_global = global; + aux_stack_top = + (uint64)(uint32)global->init_expr.u.unary.v.i32; + module->aux_stack_top_global_index = + module->import_global_count + global_index; + module->aux_stack_bottom = aux_stack_top; + module->aux_stack_size = + aux_stack_top > aux_data_end + ? (uint32)(aux_stack_top - aux_data_end) + : (uint32)aux_stack_top; + LOG_VERBOSE( + "Found aux stack top global, value: %" PRIu64 ", " + "global index: %d, stack size: %d", + aux_stack_top, global_index, + module->aux_stack_size); + break; + } + } + if (!aux_stack_top_global) { + /* Auxiliary stack global isn't found, it must be unused + in the wasm app, as if it is used, the global must be + defined. Here we set it to __heap_base global and set + its size to 0. */ + aux_stack_top_global = aux_heap_base_global; + aux_stack_top = aux_heap_base; + module->aux_stack_top_global_index = + module->aux_heap_base_global_index; + module->aux_stack_bottom = aux_stack_top; + module->aux_stack_size = 0; + } + break; + } + } + } + + module->malloc_function = (uint32)-1; + module->free_function = (uint32)-1; + module->retain_function = (uint32)-1; + + /* Resolve malloc/free function exported by wasm module */ +#if WASM_ENABLE_MEMORY64 != 0 + if (has_module_memory64(module)) + malloc_free_io_type = VALUE_TYPE_I64; +#endif + export = module->exports; + for (i = 0; i < module->export_count; i++, export ++) { + if (export->kind == EXPORT_KIND_FUNC) { + if (!strcmp(export->name, "malloc") + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { + bh_assert(module->malloc_function == (uint32)-1); + module->malloc_function = export->index; + LOG_VERBOSE("Found malloc function, name: %s, index: %u", + export->name, export->index); + } + } + else if (!strcmp(export->name, "__new") + && export->index >= module->import_function_count) { + /* __new && __pin for AssemblyScript */ + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 2 && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == VALUE_TYPE_I32 + && func_type->types[2] == malloc_free_io_type) { + uint32 j; + WASMExport *export_tmp; + + bh_assert(module->malloc_function == (uint32)-1); + module->malloc_function = export->index; + LOG_VERBOSE("Found malloc function, name: %s, index: %u", + export->name, export->index); + + /* resolve retain function. + If not found, reset malloc function index */ + export_tmp = module->exports; + for (j = 0; j < module->export_count; j++, export_tmp++) { + if ((export_tmp->kind == EXPORT_KIND_FUNC) + && (!strcmp(export_tmp->name, "__retain") + || (!strcmp(export_tmp->name, "__pin"))) + && (export_tmp->index + >= module->import_function_count)) { + func_index = export_tmp->index + - module->import_function_count; + func_type = + module->functions[func_index]->func_type; + if (func_type->param_count == 1 + && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { + bh_assert(module->retain_function + == (uint32)-1); + module->retain_function = export_tmp->index; + LOG_VERBOSE("Found retain function, name: %s, " + "index: %u", + export_tmp->name, + export_tmp->index); + break; + } + } + } + if (j == module->export_count) { + module->malloc_function = (uint32)-1; + LOG_VERBOSE("Can't find retain function," + "reset malloc function index to -1"); + } + } + } + else if (((!strcmp(export->name, "free")) + || (!strcmp(export->name, "__release")) + || (!strcmp(export->name, "__unpin"))) + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 && func_type->result_count == 0 + && func_type->types[0] == malloc_free_io_type) { + bh_assert(module->free_function == (uint32)-1); + module->free_function = export->index; + LOG_VERBOSE("Found free function, name: %s, index: %u", + export->name, export->index); + } + } + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_LABELS_AS_VALUES != 0 + handle_table = wasm_interp_get_handle_table(); +#endif + + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + if (!wasm_loader_prepare_bytecode(module, func, i, error_buf, + error_buf_size)) { + return false; + } + + if (i == module->function_count - 1 + && func->code + func->code_size != buf_code_end) { + set_error_buf(error_buf, error_buf_size, + "code section size mismatch"); + return false; + } + } + + if (!module->possible_memory_grow) { +#if WASM_ENABLE_SHRUNK_MEMORY != 0 + if (aux_data_end_global && aux_heap_base_global + && aux_stack_top_global) { + uint64 init_memory_size; + uint64 shrunk_memory_size = align_uint64(aux_heap_base, 8); + + /* Only resize(shrunk) the memory size if num_bytes_per_page is in + * valid range of uint32 */ + if (shrunk_memory_size <= UINT32_MAX) { + if (module->import_memory_count) { + WASMMemoryImport *memory_import = + &module->import_memories[0].u.memory; + init_memory_size = + (uint64)memory_import->mem_type.num_bytes_per_page + * memory_import->mem_type.init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory_import->mem_type.num_bytes_per_page = + (uint32)shrunk_memory_size; + memory_import->mem_type.init_page_count = 1; + LOG_VERBOSE("Shrink import memory size to %" PRIu64, + shrunk_memory_size); + } + } + + if (module->memory_count) { + WASMMemory *memory = &module->memories[0]; + init_memory_size = (uint64)memory->num_bytes_per_page + * memory->init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory->num_bytes_per_page = (uint32)shrunk_memory_size; + memory->init_page_count = 1; + LOG_VERBOSE("Shrink memory size to %" PRIu64, + shrunk_memory_size); + } + } + } + } +#endif /* WASM_ENABLE_SHRUNK_MEMORY != 0 */ + +#if WASM_ENABLE_MULTI_MODULE == 0 + if (module->import_memory_count) { + WASMMemoryImport *memory_import = + &module->import_memories[0].u.memory; + /* Only resize the memory to one big page if num_bytes_per_page is + * in valid range of uint32 */ + if (memory_import->mem_type.init_page_count < DEFAULT_MAX_PAGES) { + memory_import->mem_type.num_bytes_per_page *= + memory_import->mem_type.init_page_count; + + if (memory_import->mem_type.init_page_count > 0) + memory_import->mem_type.init_page_count = + memory_import->mem_type.max_page_count = 1; + else + memory_import->mem_type.init_page_count = + memory_import->mem_type.max_page_count = 0; + } + } + if (module->memory_count) { + WASMMemory *memory = &module->memories[0]; + /* Only resize(shrunk) the memory size if num_bytes_per_page is in + * valid range of uint32 */ + if (memory->init_page_count < DEFAULT_MAX_PAGES) { + memory->num_bytes_per_page *= memory->init_page_count; + if (memory->init_page_count > 0) + memory->init_page_count = memory->max_page_count = 1; + else + memory->init_page_count = memory->max_page_count = 0; + } + } +#endif + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (!check_memory64_flags_consistency(module, error_buf, error_buf_size, + false)) + return false; +#endif + + calculate_global_data_offset(module); + +#if WASM_ENABLE_FAST_JIT != 0 + if (!init_fast_jit_functions(module, error_buf, error_buf_size)) { + return false; + } +#endif + +#if WASM_ENABLE_JIT != 0 + if (!init_llvm_jit_functions_stage1(module, error_buf, error_buf_size)) { + return false; + } +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + return false; + } +#else + /* Run aot_compile_wasm in a backend thread, so as not to block the main + thread fast jit execution, since applying llvm optimizations in + aot_compile_wasm may cost a lot of time. + Create thread with enough native stack to apply llvm optimizations */ + if (os_thread_create(&module->llvm_jit_init_thread, + init_llvm_jit_functions_stage2_callback, + (void *)module, APP_THREAD_STACK_SIZE_DEFAULT * 8) + != 0) { + set_error_buf(error_buf, error_buf_size, + "create orcjit compile thread failed"); + return false; + } +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Create threads to compile the jit functions */ + if (!compile_jit_functions(module, error_buf, error_buf_size)) { + return false; + } +#endif + +#if WASM_ENABLE_MEMORY_TRACING != 0 + wasm_runtime_dump_module_mem_consumption((WASMModuleCommon *)module); +#endif + return true; +} + +static WASMModule * +create_module(char *name, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = + loader_malloc(sizeof(WASMModule), error_buf, error_buf_size); + bh_list_status ret; + + if (!module) { + return NULL; + } + + module->module_type = Wasm_Module_Bytecode; + + /* Set start_function to -1, means no start function */ + module->start_function = (uint32)-1; + + module->name = name; + module->is_binary_freeable = false; + +#if WASM_ENABLE_FAST_INTERP == 0 + module->br_table_cache_list = &module->br_table_cache_list_head; + ret = bh_list_init(module->br_table_cache_list); + bh_assert(ret == BH_LIST_SUCCESS); +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + module->import_module_list = &module->import_module_list_head; + ret = bh_list_init(module->import_module_list); + bh_assert(ret == BH_LIST_SUCCESS); +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + ret = bh_list_init(&module->fast_opcode_list); + bh_assert(ret == BH_LIST_SUCCESS); +#endif + +#if WASM_ENABLE_GC != 0 + if (!(module->ref_type_set = + wasm_reftype_set_create(GC_REFTYPE_MAP_SIZE_DEFAULT))) { + set_error_buf(error_buf, error_buf_size, "create reftype map failed"); + goto fail1; + } + + if (os_mutex_init(&module->rtt_type_lock)) { + set_error_buf(error_buf, error_buf_size, "init rtt type lock failed"); + goto fail2; + } +#endif + +#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, + "init instance list lock failed"); + goto fail3; + } +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + wasi_args_set_defaults(&module->wasi_args); +#endif /* WASM_ENABLE_LIBC_WASI != 0 */ + + (void)ret; + return module; + +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT \ + && WASM_ENABLE_LAZY_JIT != 0) +fail3: +#endif +#if WASM_ENABLE_GC != 0 + os_mutex_destroy(&module->rtt_type_lock); +fail2: + bh_hash_map_destroy(module->ref_type_set); +fail1: +#endif + wasm_runtime_free(module); + return NULL; +} + +#if WASM_ENABLE_DEBUG_INTERP != 0 +static bool +record_fast_op(WASMModule *module, uint8 *pos, uint8 orig_op, char *error_buf, + uint32 error_buf_size) +{ + WASMFastOPCodeNode *fast_op = + loader_malloc(sizeof(WASMFastOPCodeNode), error_buf, error_buf_size); + if (fast_op) { + fast_op->offset = pos - module->load_addr; + fast_op->orig_op = orig_op; + bh_list_insert(&module->fast_opcode_list, fast_op); + } + return fast_op ? true : false; +} +#endif + +WASMModule * +wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf, + uint32 error_buf_size) +{ + WASMModule *module = create_module("", error_buf, error_buf_size); + if (!module) + return NULL; + + if (!load_from_sections(module, section_list, false, true, false, error_buf, + error_buf_size)) { + wasm_loader_unload(module); + return NULL; + } + + LOG_VERBOSE("Load module from sections success.\n"); + return module; +} + +static void +destroy_sections(WASMSection *section_list) +{ + WASMSection *section = section_list, *next; + while (section) { + next = section->next; + wasm_runtime_free(section); + section = next; + } +} + +/* clang-format off */ +static uint8 section_ids[] = { + SECTION_TYPE_USER, + SECTION_TYPE_TYPE, + SECTION_TYPE_IMPORT, + SECTION_TYPE_FUNC, + SECTION_TYPE_TABLE, + SECTION_TYPE_MEMORY, +#if WASM_ENABLE_TAGS != 0 + SECTION_TYPE_TAG, +#endif +#if WASM_ENABLE_STRINGREF != 0 + /* must immediately precede the global section, + or where the global section would be */ + SECTION_TYPE_STRINGREF, +#endif + SECTION_TYPE_GLOBAL, + SECTION_TYPE_EXPORT, + SECTION_TYPE_START, + SECTION_TYPE_ELEM, +#if WASM_ENABLE_BULK_MEMORY != 0 + SECTION_TYPE_DATACOUNT, +#endif + SECTION_TYPE_CODE, + SECTION_TYPE_DATA +}; +/* clang-format on */ + +static uint8 +get_section_index(uint8 section_type) +{ + uint8 max_id = sizeof(section_ids) / sizeof(uint8); + + for (uint8 i = 0; i < max_id; i++) { + if (section_type == section_ids[i]) + return i; + } + + return (uint8)-1; +} + +static bool +create_sections(const uint8 *buf, uint32 size, WASMSection **p_section_list, + char *error_buf, uint32 error_buf_size) +{ + WASMSection *section_list_end = NULL, *section; + const uint8 *p = buf, *p_end = buf + size; + uint8 section_type, section_index, last_section_index = (uint8)-1; + uint32 section_size; + + bh_assert(!*p_section_list); + + p += 8; + while (p < p_end) { + CHECK_BUF(p, p_end, 1); + section_type = read_uint8(p); + section_index = get_section_index(section_type); + if (section_index != (uint8)-1) { + if (section_type != SECTION_TYPE_USER) { + /* Custom sections may be inserted at any place, + while other sections must occur at most once + and in prescribed order. */ + if (last_section_index != (uint8)-1 + && (section_index <= last_section_index)) { + set_error_buf(error_buf, error_buf_size, + "unexpected content after last section or " + "junk after last section"); + return false; + } + last_section_index = section_index; + } + read_leb_uint32(p, p_end, section_size); + CHECK_BUF1(p, p_end, section_size); + + if (!(section = loader_malloc(sizeof(WASMSection), error_buf, + error_buf_size))) { + return false; + } + + section->section_type = section_type; + section->section_body = (uint8 *)p; + section->section_body_size = section_size; + + if (!section_list_end) + *p_section_list = section_list_end = section; + else { + section_list_end->next = section; + section_list_end = section; + } + + p += section_size; + } + else { + set_error_buf(error_buf, error_buf_size, "invalid section id"); + return false; + } + } + + return true; +fail: + return false; +} + +static void +exchange32(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +static bool +load(const uint8 *buf, uint32 size, WASMModule *module, + bool wasm_binary_freeable, bool no_resolve, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *buf_end = buf + size; + const uint8 *p = buf, *p_end = buf_end; + uint32 magic_number, version; + WASMSection *section_list = NULL; + + CHECK_BUF1(p, p_end, sizeof(uint32)); + magic_number = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8 *)&magic_number); + + if (magic_number != WASM_MAGIC_NUMBER) { + set_error_buf(error_buf, error_buf_size, "magic header not detected"); + return false; + } + + CHECK_BUF1(p, p_end, sizeof(uint32)); + version = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8 *)&version); + + if (version != WASM_CURRENT_VERSION) { + set_error_buf(error_buf, error_buf_size, "unknown binary version"); + return false; + } + + module->package_version = version; + + if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) + || !load_from_sections(module, section_list, true, wasm_binary_freeable, + no_resolve, error_buf, error_buf_size)) { + destroy_sections(section_list); + return false; + } + + destroy_sections(section_list); + return true; +fail: + return false; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +/** + * refer to + * https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md + */ +static bool +check_wasi_abi_compatibility(const WASMModule *module, +#if WASM_ENABLE_MULTI_MODULE != 0 + bool main_module, +#endif + char *error_buf, uint32 error_buf_size) +{ + /** + * be careful with: + * wasi compatible modules(command/reactor) which don't import any wasi + * APIs. Usually, a command has to import a "prox_exit" at least, but a + * reactor can depend on nothing. At the same time, each has its own entry + * point. + * + * observations: + * - clang always injects `_start` into a command + * - clang always injects `_initialize` into a reactor + * - `iwasm -f` allows to run a function in the reactor + * + * strong assumptions: + * - no one will define either `_start` or `_initialize` on purpose + * - `_start` should always be `void _start(void)` + * - `_initialize` should always be `void _initialize(void)` + * + */ + + /* clang-format off */ + /** + * + * | | import_wasi_api True | | import_wasi_api False | | + * | ----------- | -------------------- | ---------------- | --------------------- | ---------------- | + * | | \_initialize() Y | \_initialize() N | \_initialize() Y | \_initialize() N | + * | \_start() Y | N | COMMANDER | N | COMMANDER | + * | \_start() N | REACTOR | N | REACTOR | OTHERS | + */ + /* clang-format on */ + + WASMExport *initialize = NULL, *memory = NULL, *start = NULL; + uint32 import_function_count = module->import_function_count; + WASMFuncType *func_type; + + /* (func (export "_start") (...) */ + start = wasm_loader_find_export(module, "", "_start", EXPORT_KIND_FUNC, + error_buf, error_buf_size); + if (start) { + if (start->index < import_function_count) { + set_error_buf( + error_buf, error_buf_size, + "the builtin _start function can not be an import function"); + return false; + } + + func_type = + module->functions[start->index - import_function_count]->func_type; + if (func_type->param_count || func_type->result_count) { + set_error_buf(error_buf, error_buf_size, + "the signature of builtin _start function is wrong"); + return false; + } + } + else { + /* (func (export "_initialize") (...) */ + initialize = + wasm_loader_find_export(module, "", "_initialize", EXPORT_KIND_FUNC, + error_buf, error_buf_size); + + if (initialize) { + if (initialize->index < import_function_count) { + set_error_buf(error_buf, error_buf_size, + "the builtin _initialize function can not be an " + "import function"); + return false; + } + + func_type = + module->functions[initialize->index - import_function_count] + ->func_type; + if (func_type->param_count || func_type->result_count) { + set_error_buf( + error_buf, error_buf_size, + "the signature of builtin _initialize function is wrong"); + return false; + } + } + } + + /* filter out non-wasi compatible modules */ + if (!module->import_wasi_api && !start && !initialize) { + return true; + } + + /* should have one at least */ + if (module->import_wasi_api && !start && !initialize) { + LOG_WARNING("warning: a module with WASI apis should be either " + "a command or a reactor"); + } + + /* + * there is at least one of `_start` and `_initialize` in below cases. + * according to the assumption, they should be all wasi compatible + */ + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* filter out commands (with `_start`) cases */ + if (start && !main_module) { + set_error_buf( + error_buf, error_buf_size, + "a command (with _start function) can not be a sub-module"); + return false; + } +#endif + + /* + * it is ok a reactor acts as a main module, + * so skip the check about (with `_initialize`) + */ + + memory = wasm_loader_find_export(module, "", "memory", EXPORT_KIND_MEMORY, + error_buf, error_buf_size); + 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; + } + + return true; +} +#endif + +WASMModule * +wasm_loader_load(uint8 *buf, uint32 size, +#if WASM_ENABLE_MULTI_MODULE != 0 + bool main_module, +#endif + const LoadArgs *args, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = create_module(args->name, error_buf, error_buf_size); + if (!module) { + return NULL; + } + +#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_FAST_JIT != 0 \ + || WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_JIT != 0 + module->load_addr = (uint8 *)buf; + module->load_size = size; +#endif + + if (!load(buf, size, module, args->wasm_binary_freeable, args->no_resolve, + error_buf, error_buf_size)) { + goto fail; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + /* Check the WASI application ABI */ + if (!check_wasi_abi_compatibility(module, +#if WASM_ENABLE_MULTI_MODULE != 0 + main_module, +#endif + error_buf, error_buf_size)) { + goto fail; + } +#endif + + LOG_VERBOSE("Load module success.\n"); + return module; + +fail: + wasm_loader_unload(module); + return NULL; +} + +void +wasm_loader_unload(WASMModule *module) +{ + uint32 i; + + if (!module) + return; + +#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); +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Stop Fast/LLVM JIT compilation firstly to avoid accessing + module internal data after they were freed */ + orcjit_stop_compile_threads(module); +#endif + +#if WASM_ENABLE_JIT != 0 + if (module->func_ptrs) + wasm_runtime_free(module->func_ptrs); + if (module->comp_ctx) + aot_destroy_comp_context(module->comp_ctx); + if (module->comp_data) + aot_destroy_comp_data(module->comp_data); +#endif + +#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); + } +#endif + + if (module->imports) + wasm_runtime_free(module->imports); + + if (module->functions) { + for (i = 0; i < module->function_count; i++) { + if (module->functions[i]) { + if (module->functions[i]->local_offsets) + wasm_runtime_free(module->functions[i]->local_offsets); +#if WASM_ENABLE_FAST_INTERP != 0 + if (module->functions[i]->code_compiled) + wasm_runtime_free(module->functions[i]->code_compiled); + if (module->functions[i]->consts) + wasm_runtime_free(module->functions[i]->consts); +#if WASM_ENABLE_EXCE_HANDLING != 0 + if (module->functions[i]->exception_handlers) { + uint32 eh_idx; + for (eh_idx = 0; + eh_idx < module->functions[i]->exception_handler_count; + eh_idx++) { + WASMFastEHEntry *eh_entry = + &module->functions[i]->exception_handlers[eh_idx]; + if (eh_entry->catches) { + uint32 cj; + /* Free each catch's tag-with-params dst + * slot array. param_dst_offsets is NULL + * for the (common) tag-without-params + * case, in which case the free is a + * no-op. */ + for (cj = 0; cj < eh_entry->catch_count; cj++) { + if (eh_entry->catches[cj].param_dst_offsets) { + wasm_runtime_free(eh_entry->catches[cj] + .param_dst_offsets); + } + } + wasm_runtime_free(eh_entry->catches); + } + } + wasm_runtime_free(module->functions[i]->exception_handlers); + } +#endif /* end of WASM_ENABLE_EXCE_HANDLING */ +#endif +#if WASM_ENABLE_FAST_JIT != 0 + if (module->functions[i]->fast_jit_jitted_code) { + jit_code_cache_free( + module->functions[i]->fast_jit_jitted_code); + } +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->functions[i]->call_to_fast_jit_from_llvm_jit) { + jit_code_cache_free( + module->functions[i]->call_to_fast_jit_from_llvm_jit); + } +#endif +#endif +#if WASM_ENABLE_GC != 0 + if (module->functions[i]->local_ref_type_maps) { + wasm_runtime_free( + module->functions[i]->local_ref_type_maps); + } +#endif + wasm_runtime_free(module->functions[i]); + } + } + wasm_runtime_free(module->functions); + } + + if (module->tables) { +#if WASM_ENABLE_GC != 0 + for (i = 0; i < module->table_count; i++) { + destroy_init_expr(module, &module->tables[i].init_expr); + } +#endif + wasm_runtime_free(module->tables); + } + + if (module->memories) + wasm_runtime_free(module->memories); + + if (module->globals) { +#if WASM_ENABLE_GC != 0 || WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + for (i = 0; i < module->global_count; i++) { + destroy_init_expr(module, &module->globals[i].init_expr); + } +#endif + wasm_runtime_free(module->globals); + } + +#if WASM_ENABLE_TAGS != 0 + if (module->tags) { + for (i = 0; i < module->tag_count; i++) { + if (module->tags[i]) + wasm_runtime_free(module->tags[i]); + } + wasm_runtime_free(module->tags); + } +#endif + + if (module->exports) + wasm_runtime_free(module->exports); + + if (module->table_segments) { + for (i = 0; i < module->table_seg_count; i++) { + if (module->table_segments[i].init_values) { +#if WASM_ENABLE_GC != 0 + uint32 j; + for (j = 0; j < module->table_segments[i].value_count; j++) { + destroy_init_expr( + module, &module->table_segments[i].init_values[j]); + } +#endif + wasm_runtime_free(module->table_segments[i].init_values); + } +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(module, &module->table_segments[i].base_offset); +#endif + } + wasm_runtime_free(module->table_segments); + } + + if (module->data_segments) { + for (i = 0; i < module->data_seg_count; i++) { + if (module->data_segments[i]) { + if (module->data_segments[i]->is_data_cloned) + wasm_runtime_free(module->data_segments[i]->data); +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(module, + &(module->data_segments[i]->base_offset)); +#endif + wasm_runtime_free(module->data_segments[i]); + } + } + wasm_runtime_free(module->data_segments); + } + + if (module->types) { + for (i = 0; i < module->type_count; i++) { + if (module->types[i]) + destroy_wasm_type(module->types[i]); + } + wasm_runtime_free(module->types); + } + + if (module->const_str_list) { + StringNode *node = module->const_str_list, *node_next; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + } + +#if WASM_ENABLE_STRINGREF != 0 + if (module->string_literal_ptrs) { + wasm_runtime_free((void *)module->string_literal_ptrs); + } + if (module->string_literal_lengths) { + wasm_runtime_free(module->string_literal_lengths); + } +#endif + +#if WASM_ENABLE_FAST_INTERP == 0 + if (module->br_table_cache_list) { + BrTableCache *node = bh_list_first_elem(module->br_table_cache_list); + BrTableCache *node_next; + while (node) { + node_next = bh_list_elem_next(node); + wasm_runtime_free(node); + node = node_next; + } + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just release the sub module list */ + if (module->import_module_list) { + WASMRegisteredModule *node = + bh_list_first_elem(module->import_module_list); + while (node) { + WASMRegisteredModule *next = bh_list_elem_next(node); + bh_list_remove(module->import_module_list, node); + /* + * unload(sub_module) will be triggered during runtime_destroy(). + * every module in the global module list will be unloaded one by + * one. so don't worry. + */ + wasm_runtime_free(node); + /* + * the module file reading buffer will be released + * in runtime_destroy() + */ + node = next; + } + } +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + WASMFastOPCodeNode *fast_opcode = + bh_list_first_elem(&module->fast_opcode_list); + while (fast_opcode) { + WASMFastOPCodeNode *next = bh_list_elem_next(fast_opcode); + wasm_runtime_free(fast_opcode); + fast_opcode = next; + } +#endif + +#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 + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + wasm_runtime_destroy_custom_sections(module->custom_section_list); +#endif + +#if WASM_ENABLE_FAST_JIT != 0 + if (module->fast_jit_func_ptrs) { + wasm_runtime_free(module->fast_jit_func_ptrs); + } + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (module->fast_jit_thread_locks_inited[i]) { + os_mutex_destroy(&module->fast_jit_thread_locks[i]); + } + } +#endif + +#if WASM_ENABLE_GC != 0 + os_mutex_destroy(&module->rtt_type_lock); + bh_hash_map_destroy(module->ref_type_set); + if (module->rtt_types) { + for (i = 0; i < module->type_count; i++) { + if (module->rtt_types[i]) + wasm_runtime_free(module->rtt_types[i]); + } + wasm_runtime_free(module->rtt_types); + } +#if WASM_ENABLE_STRINGREF != 0 + for (i = 0; i < WASM_TYPE_STRINGVIEWITER - WASM_TYPE_STRINGREF + 1; i++) { + if (module->stringref_rtts[i]) + wasm_runtime_free(module->stringref_rtts[i]); + } +#endif +#endif +#if WASM_ENABLE_BRANCH_HINTS != 0 + for (i = 0; i < module->function_count; i++) { + // be carefull when adding more hints. This only works as long as + // the hint structs have been allocated all at once as an array. + // With only branch-hints at the moment, this is the case. + if (module->function_hints != NULL && module->function_hints[i] != NULL) + wasm_runtime_free(module->function_hints[i]); + } + if (module->function_hints != NULL) + wasm_runtime_free(module->function_hints); +#endif + wasm_runtime_free(module); +} + +bool +wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, + const uint8 *start_addr, const uint8 *code_end_addr, + uint8 label_type, uint8 **p_else_addr, + uint8 **p_end_addr) +{ + const uint8 *p = start_addr, *p_end = code_end_addr; + uint8 *else_addr = NULL; + char error_buf[128]; + uint32 block_nested_depth = 1, count, i, j, t; + uint32 error_buf_size = sizeof(error_buf); + uint8 opcode, u8; + BlockAddr block_stack[16] = { { 0 } }, *block; + + i = ((uintptr_t)start_addr) & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) { + if (block[j].start_addr == start_addr) { + /* Cache hit */ + *p_else_addr = block[j].else_addr; + *p_end_addr = block[j].end_addr; + return true; + } + } + + /* Cache unhit */ + block_stack[0].start_addr = start_addr; + + while (p < code_end_addr) { + opcode = *p++; +#if WASM_ENABLE_DEBUG_INTERP != 0 + op_break_retry: +#endif + switch (opcode) { + case WASM_OP_UNREACHABLE: + case WASM_OP_NOP: + break; + +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + u8 = read_uint8(p); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + case EXT_OP_TRY: + skip_leb_uint32(p, p_end); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + case WASM_OP_CATCH: + if (block_nested_depth == 1) { + *p_end_addr = (uint8 *)(p - 1); + /* stop search and return the address of the catch block */ + return true; + } + break; + case WASM_OP_CATCH_ALL: + if (block_nested_depth == 1) { + *p_end_addr = (uint8 *)(p - 1); + /* stop search and return the address of the catch_all block + */ + return true; + } + break; + case WASM_OP_THROW: + /* skip tag_index */ + skip_leb(p); + break; + case WASM_OP_RETHROW: + /* skip depth */ + skip_leb(p); + break; + case WASM_OP_DELEGATE: + if (block_nested_depth == 1) { + *p_end_addr = (uint8 *)(p - 1); + return true; + } + else { + skip_leb(p); + /* the DELEGATE opcode ends the tryblock, */ + block_nested_depth--; + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) + block_stack[block_nested_depth].end_addr = + (uint8 *)(p - 1); + } + break; +#endif /* end of WASM_ENABLE_EXCE_HANDLING != 0 */ + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + { + /* block result type: 0x40/0x7F/0x7E/0x7D/0x7C */ + u8 = read_uint8(p); + if (is_byte_a_type(u8)) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(u8)) { + /* the possible extra bytes of GC ref type have been + modified to OP_NOP, no need to resolve them again */ + } +#endif + } + else { + p--; + /* block type */ + skip_leb_int32(p, p_end); + } + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + } + + case EXT_OP_BLOCK: + case EXT_OP_LOOP: + case EXT_OP_IF: + /* block type */ + skip_leb_int32(p, p_end); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; block_stack[block_nested_depth].else_addr = NULL; } - block_nested_depth++; - break; + block_nested_depth++; + break; + + case WASM_OP_ELSE: + if (label_type == LABEL_TYPE_IF && block_nested_depth == 1) + else_addr = (uint8 *)(p - 1); + if (block_nested_depth - 1 + < sizeof(block_stack) / sizeof(BlockAddr)) + block_stack[block_nested_depth - 1].else_addr = + (uint8 *)(p - 1); + break; + + case WASM_OP_END: + if (block_nested_depth == 1) { + if (label_type == LABEL_TYPE_IF) + *p_else_addr = else_addr; + *p_end_addr = (uint8 *)(p - 1); + + block_stack[0].end_addr = (uint8 *)(p - 1); + for (t = 0; t < sizeof(block_stack) / sizeof(BlockAddr); + t++) { + start_addr = block_stack[t].start_addr; + if (start_addr) { + i = ((uintptr_t)start_addr) + & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + block = + block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) + if (!block[j].start_addr) + break; + + if (j == BLOCK_ADDR_CONFLICT_SIZE) { + memmove(block + 1, block, + (BLOCK_ADDR_CONFLICT_SIZE - 1) + * sizeof(BlockAddr)); + j = 0; + } + block[j].start_addr = block_stack[t].start_addr; + block[j].else_addr = block_stack[t].else_addr; + block[j].end_addr = block_stack[t].end_addr; + } + else + break; + } + return true; + } + else { + block_nested_depth--; + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) + block_stack[block_nested_depth].end_addr = + (uint8 *)(p - 1); + } + break; + + case WASM_OP_BR: + case WASM_OP_BR_IF: + skip_leb_uint32(p, p_end); /* labelidx */ + break; + + case WASM_OP_BR_TABLE: + read_leb_uint32(p, p_end, count); /* label num */ +#if WASM_ENABLE_FAST_INTERP != 0 + for (i = 0; i <= count; i++) /* labelidxs */ + skip_leb_uint32(p, p_end); +#else + p += count + 1; + while (*p == WASM_OP_NOP) + p++; +#endif + break; + +#if WASM_ENABLE_FAST_INTERP == 0 + case EXT_OP_BR_TABLE_CACHE: + read_leb_uint32(p, p_end, count); /* label num */ + while (*p == WASM_OP_NOP) + p++; + break; +#endif + + case WASM_OP_RETURN: + break; + + case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif + skip_leb_uint32(p, p_end); /* funcidx */ + break; + + case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif + skip_leb_uint32(p, p_end); /* typeidx */ +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 || WASM_ENABLE_GC != 0 + skip_leb_uint32(p, p_end); /* tableidx */ +#else + u8 = read_uint8(p); /* 0x00 */ +#endif + break; + +#if WASM_ENABLE_GC != 0 + case WASM_OP_CALL_REF: + case WASM_OP_RETURN_CALL_REF: + skip_leb_uint32(p, p_end); /* typeidx */ + break; +#endif + + case WASM_OP_DROP: + case WASM_OP_SELECT: + case WASM_OP_DROP_64: + case WASM_OP_SELECT_64: +#if WASM_ENABLE_SIMDE != 0 + case WASM_OP_SELECT_128: +#endif + break; + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_SELECT_T: + { + skip_leb_uint32(p, p_end); /* vec length */ + u8 = read_uint8(p); /* typeidx */ + /* the possible extra bytes of GC ref type have been + modified to OP_NOP, no need to resolve them again */ + break; + } + + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + skip_leb_uint32(p, p_end); /* table index */ + break; + case WASM_OP_REF_NULL: + { + u8 = read_uint8(p); /* type */ + if (is_byte_a_type(u8)) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(u8)) { + /* the possible extra bytes of GC ref type have been + modified to OP_NOP, no need to resolve them again */ + } +#endif + } + else { + p--; + skip_leb_uint32(p, p_end); + } + break; + } + case WASM_OP_REF_IS_NULL: + break; + case WASM_OP_REF_FUNC: + skip_leb_uint32(p, p_end); /* func index */ + break; +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_GC != 0 + case WASM_OP_REF_AS_NON_NULL: + case WASM_OP_REF_EQ: + break; + case WASM_OP_BR_ON_NULL: + case WASM_OP_BR_ON_NON_NULL: + skip_leb_uint32(p, p_end); /* label index */ + break; +#endif /* end of WASM_ENABLE_GC != 0 */ + + case WASM_OP_GET_LOCAL: + case WASM_OP_SET_LOCAL: + case WASM_OP_TEE_LOCAL: + case WASM_OP_GET_GLOBAL: + case WASM_OP_SET_GLOBAL: + case WASM_OP_GET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_64: +#if WASM_ENABLE_SIMDE != 0 + case WASM_OP_GET_GLOBAL_V128: + case WASM_OP_SET_GLOBAL_V128: +#endif + case WASM_OP_SET_GLOBAL_AUX_STACK: + skip_leb_uint32(p, p_end); /* local index */ + break; + + case EXT_OP_GET_LOCAL_FAST: + case EXT_OP_SET_LOCAL_FAST: + case EXT_OP_TEE_LOCAL_FAST: + CHECK_BUF(p, p_end, 1); + p++; + break; + + case WASM_OP_I32_LOAD: + case WASM_OP_I64_LOAD: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_I32_STORE: + case WASM_OP_I64_STORE: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + skip_leb_align(p, p_end); /* align */ + skip_leb_mem_offset(p, p_end); /* offset */ + break; + + case WASM_OP_MEMORY_SIZE: + case WASM_OP_MEMORY_GROW: + skip_leb_memidx(p, p_end); /* memidx */ + break; + + case WASM_OP_I32_CONST: + skip_leb_int32(p, p_end); + break; + case WASM_OP_I64_CONST: + skip_leb_int64(p, p_end); + break; + case WASM_OP_F32_CONST: + p += sizeof(float32); + break; + case WASM_OP_F64_CONST: + p += sizeof(float64); + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + case WASM_OP_I32_WRAP_I64: + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + case WASM_OP_F32_DEMOTE_F64: + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + case WASM_OP_F64_PROMOTE_F32: + case WASM_OP_I32_REINTERPRET_F32: + case WASM_OP_I64_REINTERPRET_F64: + case WASM_OP_F32_REINTERPRET_I32: + case WASM_OP_F64_REINTERPRET_I64: + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + break; + +#if WASM_ENABLE_GC != 0 + case WASM_OP_GC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); + /* opcode1 was checked in wasm_loader_prepare_bytecode and + is no larger than UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_STRUCT_NEW: + case WASM_OP_STRUCT_NEW_DEFAULT: + skip_leb_uint32(p, p_end); /* typeidx */ + break; + case WASM_OP_STRUCT_GET: + case WASM_OP_STRUCT_GET_S: + case WASM_OP_STRUCT_GET_U: + case WASM_OP_STRUCT_SET: + skip_leb_uint32(p, p_end); /* typeidx */ + skip_leb_uint32(p, p_end); /* fieldidx */ + break; + + case WASM_OP_ARRAY_NEW: + case WASM_OP_ARRAY_NEW_DEFAULT: + case WASM_OP_ARRAY_GET: + case WASM_OP_ARRAY_GET_S: + case WASM_OP_ARRAY_GET_U: + case WASM_OP_ARRAY_SET: + case WASM_OP_ARRAY_FILL: + skip_leb_uint32(p, p_end); /* typeidx */ + break; + case WASM_OP_ARRAY_COPY: + skip_leb_uint32(p, p_end); /* typeidx1 */ + skip_leb_uint32(p, p_end); /* typeidx2 */ + break; + case WASM_OP_ARRAY_LEN: + break; + case WASM_OP_ARRAY_NEW_FIXED: + case WASM_OP_ARRAY_NEW_DATA: + case WASM_OP_ARRAY_NEW_ELEM: + skip_leb_uint32(p, p_end); /* typeidx */ + skip_leb_uint32(p, p_end); /* N/dataidx/elemidx */ + break; + + case WASM_OP_REF_I31: + case WASM_OP_I31_GET_S: + case WASM_OP_I31_GET_U: + break; + + case WASM_OP_REF_TEST: + case WASM_OP_REF_CAST: + case WASM_OP_REF_TEST_NULLABLE: + case WASM_OP_REF_CAST_NULLABLE: + skip_leb_int32(p, p_end); /* heaptype */ + break; + case WASM_OP_BR_ON_CAST: + case WASM_OP_BR_ON_CAST_FAIL: + p += sizeof(uint8); /* castflag */ + skip_leb_uint32(p, p_end); /* labelidx */ + skip_leb_int32(p, p_end); /* heaptype */ + skip_leb_int32(p, p_end); /* heaptype2 */ + break; + + case WASM_OP_ANY_CONVERT_EXTERN: + case WASM_OP_EXTERN_CONVERT_ANY: + break; + +#if WASM_ENABLE_STRINGREF != 0 + case WASM_OP_STRING_NEW_UTF8: + case WASM_OP_STRING_NEW_WTF16: + case WASM_OP_STRING_NEW_LOSSY_UTF8: + case WASM_OP_STRING_NEW_WTF8: + skip_leb_uint32(p, p_end); /* memory index 0x00 */ + break; + case WASM_OP_STRING_CONST: + skip_leb_int32(p, p_end); /* contents */ + break; + case WASM_OP_STRING_MEASURE_UTF8: + case WASM_OP_STRING_MEASURE_WTF8: + case WASM_OP_STRING_MEASURE_WTF16: + break; + case WASM_OP_STRING_ENCODE_UTF8: + case WASM_OP_STRING_ENCODE_WTF16: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8: + case WASM_OP_STRING_ENCODE_WTF8: + skip_leb_uint32(p, p_end); /* memory index 0x00 */ + break; + case WASM_OP_STRING_CONCAT: + case WASM_OP_STRING_EQ: + case WASM_OP_STRING_IS_USV_SEQUENCE: + case WASM_OP_STRING_AS_WTF8: + case WASM_OP_STRINGVIEW_WTF8_ADVANCE: + break; + case WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8: + skip_leb_uint32(p, p_end); /* memory index 0x00 */ + break; + case WASM_OP_STRINGVIEW_WTF8_SLICE: + case WASM_OP_STRING_AS_WTF16: + case WASM_OP_STRINGVIEW_WTF16_LENGTH: + case WASM_OP_STRINGVIEW_WTF16_GET_CODEUNIT: + break; + case WASM_OP_STRINGVIEW_WTF16_ENCODE: + skip_leb_uint32(p, p_end); /* memory index 0x00 */ + break; + case WASM_OP_STRINGVIEW_WTF16_SLICE: + case WASM_OP_STRING_AS_ITER: + case WASM_OP_STRINGVIEW_ITER_NEXT: + case WASM_OP_STRINGVIEW_ITER_ADVANCE: + case WASM_OP_STRINGVIEW_ITER_REWIND: + case WASM_OP_STRINGVIEW_ITER_SLICE: + case WASM_OP_STRING_NEW_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF16_ARRAY: + case WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF8_ARRAY: + case WASM_OP_STRING_ENCODE_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF16_ARRAY: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF8_ARRAY: + break; +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + default: + return false; + } + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); + /* opcode1 was checked in wasm_loader_prepare_bytecode and + is no larger than UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + skip_leb_uint32(p, p_end); + skip_leb_memidx(p, p_end); + break; + case WASM_OP_DATA_DROP: + skip_leb_uint32(p, p_end); + break; +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + skip_leb_memidx(p, p_end); + skip_leb_memidx(p, p_end); + break; + case WASM_OP_MEMORY_FILL: + skip_leb_memidx(p, p_end); + break; +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + case WASM_OP_TABLE_COPY: + /* tableidx */ + skip_leb_uint32(p, p_end); + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_ELEM_DROP: + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_TABLE_SIZE: + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + skip_leb_uint32(p, p_end); /* table idx */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ + default: + return false; + } + break; + } + +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) + case WASM_OP_SIMD_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); + /* opcode1 was checked in wasm_loader_prepare_bytecode and + is no larger than UINT8_MAX */ + opcode = (uint8)opcode1; + + /* follow the order of enum WASMSimdEXTOpcode in wasm_opcode.h + */ + switch (opcode) { + case SIMD_v128_load: + case SIMD_v128_load8x8_s: + case SIMD_v128_load8x8_u: + case SIMD_v128_load16x4_s: + case SIMD_v128_load16x4_u: + case SIMD_v128_load32x2_s: + case SIMD_v128_load32x2_u: + case SIMD_v128_load8_splat: + case SIMD_v128_load16_splat: + case SIMD_v128_load32_splat: + case SIMD_v128_load64_splat: + case SIMD_v128_store: + /* memarg align */ + skip_leb_uint32(p, p_end); + /* memarg offset */ + skip_leb_mem_offset(p, p_end); + break; + + case SIMD_v128_const: + case SIMD_v8x16_shuffle: + /* immByte[16] immLaneId[16] */ + CHECK_BUF1(p, p_end, 16); + p += 16; + break; + + case SIMD_i8x16_extract_lane_s: + case SIMD_i8x16_extract_lane_u: + case SIMD_i8x16_replace_lane: + case SIMD_i16x8_extract_lane_s: + case SIMD_i16x8_extract_lane_u: + case SIMD_i16x8_replace_lane: + case SIMD_i32x4_extract_lane: + case SIMD_i32x4_replace_lane: + case SIMD_i64x2_extract_lane: + case SIMD_i64x2_replace_lane: + case SIMD_f32x4_extract_lane: + case SIMD_f32x4_replace_lane: + case SIMD_f64x2_extract_lane: + case SIMD_f64x2_replace_lane: + /* ImmLaneId */ + CHECK_BUF(p, p_end, 1); + p++; + break; + + case SIMD_v128_load8_lane: + case SIMD_v128_load16_lane: + case SIMD_v128_load32_lane: + case SIMD_v128_load64_lane: + case SIMD_v128_store8_lane: + case SIMD_v128_store16_lane: + case SIMD_v128_store32_lane: + case SIMD_v128_store64_lane: + /* memarg align */ + skip_leb_uint32(p, p_end); + /* memarg offset */ + skip_leb_mem_offset(p, p_end); + /* ImmLaneId */ + CHECK_BUF(p, p_end, 1); + p++; + break; + + case SIMD_v128_load32_zero: + case SIMD_v128_load64_zero: + /* memarg align */ + skip_leb_uint32(p, p_end); + /* memarg offset */ + skip_leb_mem_offset(p, p_end); + break; + + default: + /* + * since latest SIMD specific used almost every value + * from 0x00 to 0xff, the default branch will present + * all opcodes without imm + * https://github.com/WebAssembly/simd/blob/main/proposals/simd/NewOpcodes.md + */ + break; + } + break; + } +#endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || \ + (WASM_ENABLE_FAST_INTERP != 0) */ +#endif /* end of WASM_ENABLE_SIMD */ + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + uint32 opcode1; + + /* atomic_op (u32_leb) + memarg (2 u32_leb) */ + read_leb_uint32(p, p_end, opcode1); + /* opcode1 was checked in wasm_loader_prepare_bytecode and + is no larger than UINT8_MAX */ + opcode = (uint8)opcode1; + + if (opcode != WASM_OP_ATOMIC_FENCE) { + skip_leb_uint32(p, p_end); /* align */ + skip_leb_mem_offset(p, p_end); /* offset */ + } + else { + /* atomic.fence doesn't have memarg */ + p++; + } + break; + } +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 + case DEBUG_OP_BREAK: + { + WASMDebugInstance *debug_instance = + wasm_exec_env_get_instance(exec_env); + char original_opcode[1]; + uint64 size = 1; + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + uint64 offset = (p - 1) >= module_inst->module->load_addr + ? (p - 1) - module_inst->module->load_addr + : ~0; + if (debug_instance) { + if (wasm_debug_instance_get_obj_mem(debug_instance, offset, + original_opcode, &size) + && size == 1) { + LOG_VERBOSE("WASM loader find OP_BREAK , recover it " + "with %02x: ", + original_opcode[0]); + opcode = original_opcode[0]; + goto op_break_retry; + } + } + break; + } +#endif + + default: + return false; + } + } + + (void)u8; + (void)exec_env; + return false; +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_DEBUG_PREPROCESSOR != 0 +#define LOG_OP(...) os_printf(__VA_ARGS__) +#else +#define LOG_OP(...) (void)0 +#endif + +#define PATCH_ELSE 0 +#define PATCH_END 1 +typedef struct BranchBlockPatch { + struct BranchBlockPatch *next; + uint8 patch_type; + uint8 *code_compiled; +} BranchBlockPatch; +#endif + +typedef struct BranchBlock { + uint8 label_type; + BlockType block_type; + uint8 *start_addr; + uint8 *else_addr; + uint8 *end_addr; + uint32 stack_cell_num; +#if WASM_ENABLE_GC != 0 + uint32 reftype_map_num; + /* Indicate which local is used inside current block, used to validate + * local.get with non-nullable ref types */ + uint8 *local_use_mask; + uint32 local_use_mask_size; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + uint16 dynamic_offset; + uint8 *code_compiled; + BranchBlockPatch *patch_list; + /* This is used to save params frame_offset of of if block */ + int16 *param_frame_offsets; + /* This is used to recover the dynamic offset for else branch, + * and also to remember the start offset of dynamic space which + * stores the block arguments for loop block, so we can use it + * to copy the stack operands to the loop block's arguments in + * wasm_loader_emit_br_info for opcode br. */ + uint16 start_dynamic_offset; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* For LABEL_TYPE_TRY/CATCH/CATCH_ALL: index into + * func->exception_handlers (the same index across the whole try- + * catch-end region — a CATCH clause inherits its parent TRY's + * index when the loader rewrites the block label). UINT32_MAX + * for non-EH label types. */ + uint32 eh_entry_idx; +#endif +#endif + + /* Indicate the operand stack is in polymorphic state. + * If the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed. + * If stack is in polymorphic state and stack is empty, instruction can + * pop any type of value directly without decreasing stack top pointer + * and stack cell num. */ + bool is_stack_polymorphic; +} BranchBlock; + +typedef struct WASMLoaderContext { + /* frame ref stack */ + uint8 *frame_ref; + uint8 *frame_ref_bottom; + uint8 *frame_ref_boundary; + uint32 frame_ref_size; + uint32 stack_cell_num; + uint32 max_stack_cell_num; + +#if WASM_ENABLE_GC != 0 + /* frame reftype map stack */ + WASMRefTypeMap *frame_reftype_map; + WASMRefTypeMap *frame_reftype_map_bottom; + WASMRefTypeMap *frame_reftype_map_boundary; + uint32 frame_reftype_map_size; + uint32 reftype_map_num; + uint32 max_reftype_map_num; + /* Current module */ + WASMModule *module; + /* Current module's ref_type_set */ + HashMap *ref_type_set; + /* Always point to local variable ref_type of + wasm_loader_prepare_bytecode */ + WASMRefType *ref_type_tmp; +#endif + + /* frame csp stack */ + BranchBlock *frame_csp; + BranchBlock *frame_csp_bottom; + BranchBlock *frame_csp_boundary; + uint32 frame_csp_size; + uint32 csp_num; + uint32 max_csp_num; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* frame offset stack */ + int16 *frame_offset; + int16 *frame_offset_bottom; + int16 *frame_offset_boundary; + uint32 frame_offset_size; + int16 dynamic_offset; + int16 start_dynamic_offset; + int16 max_dynamic_offset; + + /* preserved local offset */ + int16 preserved_local_offset; + + /* const buffer for i64 and f64 consts, note that the raw bytes + * of i64 and f64 are the same, so we read an i64 value from an + * f64 const with its raw bytes, something like `*(int64 *)&f64 */ + int64 *i64_consts; + uint32 i64_const_max_num; + uint32 i64_const_num; + /* const buffer for i32 and f32 consts */ + int32 *i32_consts; + uint32 i32_const_max_num; + uint32 i32_const_num; + /* const buffer for V128 */ + V128 *v128_consts; + uint32 v128_const_max_num; + uint32 v128_const_num; + + /* processed code */ + uint8 *p_code_compiled; + uint8 *p_code_compiled_end; + uint32 code_compiled_size; + /* If the last opcode will be dropped, the peak memory usage will be larger + * than the final code_compiled_size, we record the peak size to ensure + * there will not be invalid memory access during second traverse */ + uint32 code_compiled_peak_size; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* Index of the next entry to claim in func->exception_handlers, + * during the second traverse only (the first traverse merely counts + * try-blocks into func->exception_handler_count to size the array). + * Reset to 0 in wasm_loader_ctx_reinit. */ + uint32 cur_eh_entry_idx; +#endif +#endif +} WASMLoaderContext; + +#define CHECK_CSP_PUSH() \ + do { \ + if (ctx->frame_csp >= ctx->frame_csp_boundary) { \ + MEM_REALLOC( \ + ctx->frame_csp_bottom, ctx->frame_csp_size, \ + (uint32)(ctx->frame_csp_size + 8 * sizeof(BranchBlock))); \ + ctx->frame_csp_size += (uint32)(8 * sizeof(BranchBlock)); \ + ctx->frame_csp_boundary = \ + ctx->frame_csp_bottom \ + + ctx->frame_csp_size / sizeof(BranchBlock); \ + ctx->frame_csp = ctx->frame_csp_bottom + ctx->csp_num; \ + } \ + } while (0) + +#define CHECK_CSP_POP() \ + do { \ + if (ctx->csp_num < 1) { \ + set_error_buf(error_buf, error_buf_size, \ + "type mismatch: " \ + "expect data but block stack was empty"); \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_FAST_INTERP != 0 +static bool +check_offset_push(WASMLoaderContext *ctx, char *error_buf, + uint32 error_buf_size) +{ + uint32 cell_num = (uint32)(ctx->frame_offset - ctx->frame_offset_bottom); + if (ctx->frame_offset >= ctx->frame_offset_boundary) { + MEM_REALLOC(ctx->frame_offset_bottom, ctx->frame_offset_size, + ctx->frame_offset_size + 16); + ctx->frame_offset_size += 16; + ctx->frame_offset_boundary = + ctx->frame_offset_bottom + ctx->frame_offset_size / sizeof(int16); + ctx->frame_offset = ctx->frame_offset_bottom + cell_num; + } + return true; +fail: + return false; +} + +static bool +check_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->frame_offset - cells < ctx->frame_offset_bottom) + return false; + return true; +} + +static bool +check_dynamic_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->dynamic_offset < 0 || (uint32)ctx->dynamic_offset < cells) + return false; + return true; +} + +static void +free_label_patch_list(BranchBlock *frame_csp) +{ + BranchBlockPatch *label_patch = frame_csp->patch_list; + BranchBlockPatch *next; + while (label_patch != NULL) { + next = label_patch->next; + wasm_runtime_free(label_patch); + label_patch = next; + } + frame_csp->patch_list = NULL; +} + +static void +free_all_label_patch_lists(BranchBlock *frame_csp, uint32 csp_num) +{ + BranchBlock *tmp_csp = frame_csp; + uint32 i; + + for (i = 0; i < csp_num; i++) { + free_label_patch_list(tmp_csp); + tmp_csp++; + } +} + +static void +free_all_label_param_frame_offsets(BranchBlock *frame_csp, uint32 csp_num) +{ + BranchBlock *tmp_csp = frame_csp; + uint32 i; + + for (i = 0; i < csp_num; i++) { + if (tmp_csp->param_frame_offsets) + wasm_runtime_free(tmp_csp->param_frame_offsets); + tmp_csp++; + } +} +#endif /* end of WASM_ENABLE_FAST_INTERP */ + +#if WASM_ENABLE_GC != 0 +static bool +wasm_loader_init_local_use_masks(WASMLoaderContext *ctx, uint32 local_count, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *current_csp = ctx->frame_csp - 1; + uint32 local_mask_size; + + if (local_count == 0) { + current_csp->local_use_mask_size = 0; + return true; + } + + /* if current_csp->local_use_mask is not NULL, then it is re-init masks for + * else branch, we don't need to allocate memory again */ + if (!current_csp->local_use_mask) { + local_mask_size = (local_count + 7) / sizeof(uint8); + if (!(current_csp->local_use_mask = + loader_malloc(local_mask_size, error_buf, error_buf_size))) { + return false; + } + current_csp->local_use_mask_size = local_mask_size; + } + else { + local_mask_size = current_csp->local_use_mask_size; + bh_assert(current_csp->label_type == LABEL_TYPE_IF); + } + + if (current_csp->label_type != LABEL_TYPE_FUNCTION) { + /* For non-function blocks, inherit the use status from parent block */ + BranchBlock *parent_csp = current_csp - 1; + + bh_assert(parent_csp >= ctx->frame_csp_bottom); + bh_assert(parent_csp->local_use_mask); + + bh_memcpy_s(current_csp->local_use_mask, local_mask_size, + parent_csp->local_use_mask, local_mask_size); + } + + return true; +} + +static void +wasm_loader_destroy_curr_local_use_masks(WASMLoaderContext *ctx) +{ + BranchBlock *current_csp = ctx->frame_csp - 1; + + bh_assert(current_csp->local_use_mask + || current_csp->local_use_mask_size == 0); + + if (current_csp->local_use_mask) { + wasm_runtime_free(current_csp->local_use_mask); + } + + current_csp->local_use_mask = NULL; + current_csp->local_use_mask_size = 0; +} + +static void +wasm_loader_clean_all_local_use_masks(WASMLoaderContext *ctx) +{ + BranchBlock *tmp_csp = ctx->frame_csp_bottom; + uint32 i; + + for (i = 0; i < ctx->csp_num; i++) { + if (tmp_csp->local_use_mask) { + wasm_runtime_free(tmp_csp->local_use_mask); + tmp_csp->local_use_mask = NULL; + tmp_csp->local_use_mask_size = 0; + } + tmp_csp++; + } +} + +static void +wasm_loader_mask_local(WASMLoaderContext *ctx, uint32 index) +{ + BranchBlock *current_csp = ctx->frame_csp - 1; + uint32 byte_offset = index / sizeof(uint8); + uint32 bit_offset = index % sizeof(uint8); + + bh_assert(byte_offset < current_csp->local_use_mask_size); + bh_assert(current_csp->local_use_mask); + + current_csp->local_use_mask[byte_offset] |= (1 << bit_offset); +} + +static bool +wasm_loader_get_local_status(WASMLoaderContext *ctx, uint32 index) +{ + BranchBlock *current_csp = ctx->frame_csp - 1; + uint32 byte_offset = index / sizeof(uint8); + uint32 bit_offset = index % sizeof(uint8); + + bh_assert(byte_offset < current_csp->local_use_mask_size); + bh_assert(current_csp->local_use_mask); + + return (current_csp->local_use_mask[byte_offset] & (1 << bit_offset)) + ? true + : false; +} +#endif /* end of WASM_ENABLE_GC != 0 */ + +static void +wasm_loader_ctx_destroy(WASMLoaderContext *ctx) +{ + if (ctx) { + if (ctx->frame_ref_bottom) + wasm_runtime_free(ctx->frame_ref_bottom); +#if WASM_ENABLE_GC != 0 + if (ctx->frame_reftype_map_bottom) + wasm_runtime_free(ctx->frame_reftype_map_bottom); +#endif + if (ctx->frame_csp_bottom) { +#if WASM_ENABLE_FAST_INTERP != 0 + free_all_label_patch_lists(ctx->frame_csp_bottom, ctx->csp_num); + free_all_label_param_frame_offsets(ctx->frame_csp_bottom, + ctx->csp_num); +#endif +#if WASM_ENABLE_GC != 0 + wasm_loader_clean_all_local_use_masks(ctx); +#endif + wasm_runtime_free(ctx->frame_csp_bottom); + } +#if WASM_ENABLE_FAST_INTERP != 0 + if (ctx->frame_offset_bottom) + wasm_runtime_free(ctx->frame_offset_bottom); + if (ctx->i64_consts) + wasm_runtime_free(ctx->i64_consts); + if (ctx->i32_consts) + wasm_runtime_free(ctx->i32_consts); + if (ctx->v128_consts) + wasm_runtime_free(ctx->v128_consts); +#endif + wasm_runtime_free(ctx); + } +} + +static WASMLoaderContext * +wasm_loader_ctx_init(WASMFunction *func, char *error_buf, uint32 error_buf_size) +{ + WASMLoaderContext *loader_ctx = + loader_malloc(sizeof(WASMLoaderContext), error_buf, error_buf_size); + if (!loader_ctx) + return NULL; + + loader_ctx->frame_ref_size = 32; + if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = loader_malloc( + loader_ctx->frame_ref_size, error_buf, error_buf_size))) + goto fail; + loader_ctx->frame_ref_boundary = loader_ctx->frame_ref_bottom + 32; + +#if WASM_ENABLE_GC != 0 + loader_ctx->frame_reftype_map_size = sizeof(WASMRefTypeMap) * 16; + if (!(loader_ctx->frame_reftype_map_bottom = loader_ctx->frame_reftype_map = + loader_malloc(loader_ctx->frame_reftype_map_size, error_buf, + error_buf_size))) + goto fail; + loader_ctx->frame_reftype_map_boundary = + loader_ctx->frame_reftype_map_bottom + 16; +#endif + + loader_ctx->frame_csp_size = sizeof(BranchBlock) * 8; + if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = loader_malloc( + loader_ctx->frame_csp_size, error_buf, error_buf_size))) + goto fail; + loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; + +#if WASM_ENABLE_EXCE_HANDLING != 0 + func->exception_handler_count = 0; +#if WASM_ENABLE_FAST_INTERP != 0 + /* Allocated at the start of the second traverse, once + * exception_handler_count is known from the first traverse. */ + func->exception_handlers = NULL; +#endif +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + loader_ctx->frame_offset_size = sizeof(int16) * 32; + if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = + loader_malloc(loader_ctx->frame_offset_size, error_buf, + error_buf_size))) + goto fail; + loader_ctx->frame_offset_boundary = loader_ctx->frame_offset_bottom + 32; + + loader_ctx->i64_const_max_num = 8; + if (!(loader_ctx->i64_consts = + loader_malloc(sizeof(int64) * loader_ctx->i64_const_max_num, + error_buf, error_buf_size))) + goto fail; + loader_ctx->i32_const_max_num = 8; + if (!(loader_ctx->i32_consts = + loader_malloc(sizeof(int32) * loader_ctx->i32_const_max_num, + error_buf, error_buf_size))) + goto fail; + loader_ctx->v128_const_max_num = 8; + if (!(loader_ctx->v128_consts = + loader_malloc(sizeof(V128) * loader_ctx->v128_const_max_num, + error_buf, error_buf_size))) + goto fail; + + if (func->param_cell_num >= (int32)INT16_MAX - func->local_cell_num) { + set_error_buf(error_buf, error_buf_size, + "fast interpreter offset overflow"); + goto fail; + } + + loader_ctx->start_dynamic_offset = loader_ctx->dynamic_offset = + loader_ctx->max_dynamic_offset = + func->param_cell_num + func->local_cell_num; +#endif + return loader_ctx; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + return NULL; +} + +static bool +check_stack_push(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + uint32 cell_num_needed = wasm_value_type_cell_num(type); + + if (ctx->frame_ref + cell_num_needed > ctx->frame_ref_boundary) { + /* Increase the frame ref stack */ + MEM_REALLOC(ctx->frame_ref_bottom, ctx->frame_ref_size, + ctx->frame_ref_size + 16); + ctx->frame_ref_size += 16; + ctx->frame_ref_boundary = ctx->frame_ref_bottom + ctx->frame_ref_size; + ctx->frame_ref = ctx->frame_ref_bottom + ctx->stack_cell_num; + } + +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(type) + && ctx->frame_reftype_map >= ctx->frame_reftype_map_boundary) { + /* Increase the frame reftype map stack */ + bh_assert( + (uint32)((ctx->frame_reftype_map - ctx->frame_reftype_map_bottom) + * sizeof(WASMRefTypeMap)) + == ctx->frame_reftype_map_size); + MEM_REALLOC(ctx->frame_reftype_map_bottom, ctx->frame_reftype_map_size, + ctx->frame_reftype_map_size + + (uint32)sizeof(WASMRefTypeMap) * 8); + ctx->frame_reftype_map = + ctx->frame_reftype_map_bottom + + ctx->frame_reftype_map_size / ((uint32)sizeof(WASMRefTypeMap)); + ctx->frame_reftype_map_size += (uint32)sizeof(WASMRefTypeMap) * 8; + ctx->frame_reftype_map_boundary = + ctx->frame_reftype_map_bottom + + ctx->frame_reftype_map_size / ((uint32)sizeof(WASMRefTypeMap)); + } +#endif + return true; +fail: + return false; +} + +static bool +wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + uint32 type_cell_num = wasm_value_type_cell_num(type); + uint32 i; + + if (!check_stack_push(ctx, type, error_buf, error_buf_size)) + return false; + +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(type)) { + WASMRefType *ref_type; + if (!(ref_type = + reftype_set_insert(ctx->ref_type_set, ctx->ref_type_tmp, + error_buf, error_buf_size))) { + return false; + } + + if (ctx->frame_reftype_map >= ctx->frame_reftype_map_boundary) { + /* Increase the frame reftype map stack */ + bh_assert((uint32)((ctx->frame_reftype_map + - ctx->frame_reftype_map_bottom) + * sizeof(WASMRefTypeMap)) + == ctx->frame_reftype_map_size); + MEM_REALLOC(ctx->frame_reftype_map_bottom, + ctx->frame_reftype_map_size, + ctx->frame_reftype_map_size + + (uint32)sizeof(WASMRefTypeMap) * 8); + ctx->frame_reftype_map = ctx->frame_reftype_map_bottom + + ctx->frame_reftype_map_size + / ((uint32)sizeof(WASMRefTypeMap)); + ctx->frame_reftype_map_size += (uint32)sizeof(WASMRefTypeMap) * 8; + ctx->frame_reftype_map_boundary = + ctx->frame_reftype_map_bottom + + ctx->frame_reftype_map_size + / ((uint32)sizeof(WASMRefTypeMap)); + } + + ctx->frame_reftype_map->index = ctx->stack_cell_num; + ctx->frame_reftype_map->ref_type = ref_type; + ctx->frame_reftype_map++; + ctx->reftype_map_num++; + if (ctx->reftype_map_num > ctx->max_reftype_map_num) + ctx->max_reftype_map_num = ctx->reftype_map_num; + } +#endif + + for (i = 0; i < type_cell_num; i++) + *ctx->frame_ref++ = type; + ctx->stack_cell_num += type_cell_num; + + if (ctx->stack_cell_num > ctx->max_stack_cell_num) { + ctx->max_stack_cell_num = ctx->stack_cell_num; + if (ctx->max_stack_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "operand stack depth limit exceeded"); + return false; + } + } + return true; +#if WASM_ENABLE_GC != 0 +fail: + return false; +#endif +} + +static bool +check_stack_top_values(WASMLoaderContext *ctx, uint8 *frame_ref, + int32 stack_cell_num, +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *frame_reftype_map, int32 reftype_map_num, +#endif + uint8 type, +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type, +#endif + char *error_buf, uint32 error_buf_size) +{ + int32 type_cell_num = (int32)wasm_value_type_cell_num(type), i; +#if WASM_ENABLE_GC != 0 + WASMRefType *frame_reftype = NULL; +#endif + + if (stack_cell_num < type_cell_num) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: expect data but stack was empty"); + return false; + } + +#if WASM_ENABLE_GC == 0 + for (i = 0; i < type_cell_num; i++) { + if (*(frame_ref - 1 - i) != type) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", type2str(type), + " but got other"); + return false; + } + } +#else + if (wasm_is_type_multi_byte_type(*(frame_ref - 1))) { + bh_assert(reftype_map_num > 0); + frame_reftype = (frame_reftype_map - 1)->ref_type; + } + if (!wasm_reftype_is_subtype_of(*(frame_ref - 1), frame_reftype, type, + ref_type, ctx->module->types, + ctx->module->type_count)) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", type2str(type), + " but got other"); + return false; + } + for (i = 0; i < type_cell_num - 1; i++) { + if (*(frame_ref - 2 - i) != *(frame_ref - 1)) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", type2str(type), + " but got other"); + return false; + } + } +#endif + + return true; +} + +static bool +check_stack_pop(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + int32 block_stack_cell_num = + (int32)(ctx->stack_cell_num - (ctx->frame_csp - 1)->stack_cell_num); +#if WASM_ENABLE_GC != 0 + int32 reftype_map_num = + (int32)(ctx->reftype_map_num - (ctx->frame_csp - 1)->reftype_map_num); +#endif + + if (block_stack_cell_num > 0) { + if (*(ctx->frame_ref - 1) == VALUE_TYPE_ANY) + /* the stack top is a value of any type, return success */ + return true; + } + +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_reftype(type) && block_stack_cell_num > 0) { + uint8 stack_top_type = *(ctx->frame_ref - 1); + WASMRefType *stack_top_ref_type = NULL; + + if (wasm_is_type_multi_byte_type(stack_top_type)) { + bh_assert(reftype_map_num > 0); + stack_top_ref_type = (*(ctx->frame_reftype_map - 1)).ref_type; + } + + if (wasm_reftype_is_subtype_of(stack_top_type, stack_top_ref_type, type, + ctx->ref_type_tmp, ctx->module->types, + ctx->module->type_count)) { + if (wasm_is_type_multi_byte_type(stack_top_type)) { + uint32 ref_type_struct_size = + wasm_reftype_struct_size(stack_top_ref_type); + bh_memcpy_s(ctx->ref_type_tmp, (uint32)sizeof(WASMRefType), + stack_top_ref_type, ref_type_struct_size); + } + return true; + } + } +#endif + + if (!check_stack_top_values(ctx, ctx->frame_ref, block_stack_cell_num, +#if WASM_ENABLE_GC != 0 + ctx->frame_reftype_map, reftype_map_num, +#endif + type, +#if WASM_ENABLE_GC != 0 + ctx->ref_type_tmp, +#endif + error_buf, error_buf_size)) { + return false; + } + + return true; +} + +static bool +wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + BranchBlock *cur_block = ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(ctx->stack_cell_num - cur_block->stack_cell_num); + uint32 cell_num_to_pop = wasm_value_type_cell_num(type); + + /* Directly return success if current block is in stack + polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_pop(ctx, type, error_buf, error_buf_size)) + return false; + + bh_assert(available_stack_cell > 0); + if (*(ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + type = VALUE_TYPE_ANY; + cell_num_to_pop = 1; + } + + ctx->frame_ref -= cell_num_to_pop; + ctx->stack_cell_num -= cell_num_to_pop; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(*ctx->frame_ref)) { + ctx->frame_reftype_map--; + ctx->reftype_map_num--; + } +#endif + + return true; +} + +#if WASM_ENABLE_GC != 0 +/* Get the stack top element of current block */ +static bool +wasm_loader_get_frame_ref_top(WASMLoaderContext *ctx, uint8 *p_type, + WASMRefType **p_ref_type, char *error_buf, + uint32 error_buf_size) +{ + BranchBlock *cur_block = ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(ctx->stack_cell_num - cur_block->stack_cell_num); + + if (available_stack_cell <= 0) { + /* Directly return success if current block is in stack + polymorphic state while stack is empty. */ + if (cur_block->is_stack_polymorphic) { + *p_type = VALUE_TYPE_ANY; + return true; + } + else { + set_error_buf( + error_buf, error_buf_size, + "type mismatch: expect data but block stack was empty"); + return false; + } + } + + *p_type = *(ctx->frame_ref - 1); + if (wasm_is_type_multi_byte_type(*p_type)) { + int32 available_reftype_map = + (int32)(ctx->reftype_map_num + - (ctx->frame_csp - 1)->reftype_map_num); + bh_assert(available_reftype_map > 0); + (void)available_reftype_map; + *p_ref_type = (ctx->frame_reftype_map - 1)->ref_type; + } + + return true; +} + +#if WASM_ENABLE_FAST_INTERP != 0 +static bool +wasm_loader_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size); +#endif + +/* Check whether the stack top elem is a heap object, and if yes, + pop and return it */ +static bool +wasm_loader_pop_heap_obj(WASMLoaderContext *ctx, uint8 *p_type, + WASMRefType *ref_ht_ret, char *error_buf, + uint32 error_buf_size) +{ + uint8 type = 0; + WASMRefType *ref_type = NULL; + + /* Get stack top element */ + if (!wasm_loader_get_frame_ref_top(ctx, &type, &ref_type, error_buf, + error_buf_size)) { + return false; + } + + if (type != VALUE_TYPE_ANY /* block isn't in stack polymorphic state */ + /* stack top isn't a ref type */ + && !wasm_is_type_reftype(type)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: expect heap object but got others"); + return false; + } + + /* POP stack top */ + if (wasm_is_type_multi_byte_type(type)) { + bh_assert(ref_type); + bh_memcpy_s(ctx->ref_type_tmp, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!wasm_loader_pop_frame_ref_offset(ctx, type, error_buf, + error_buf_size)) { + return false; + } +#else + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) { + return false; + } +#endif + + if (p_type) + *p_type = type; + if (wasm_is_type_multi_byte_type(type) && ref_ht_ret) { + bh_memcpy_s(ref_ht_ret, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } + return true; +} + +/* Check whether the stack top elem is subtype of (ref null ht), + and if yes, pop it and return the converted (ref ht) */ +static bool +wasm_loader_pop_nullable_ht(WASMLoaderContext *ctx, uint8 *p_type, + WASMRefType *ref_ht_ret, char *error_buf, + uint32 error_buf_size) +{ + uint8 type = 0; + WASMRefType ref_type = { 0 }; + + if (!wasm_loader_pop_heap_obj(ctx, &type, &ref_type, error_buf, + error_buf_size)) { + return false; + } + + /* Convert to related (ref ht) and return */ + if (type >= REF_TYPE_ARRAYREF && type <= REF_TYPE_NULLFUNCREF) { + /* Return (ref array/struct/i31/eq/any/extern/func/none/noextern/nofunc) + */ + wasm_set_refheaptype_common(&ref_ht_ret->ref_ht_common, false, + HEAP_TYPE_ARRAY + + (type - REF_TYPE_ARRAYREF)); + type = ref_ht_ret->ref_type; + } + else if (wasm_is_reftype_htref_nullable(type) + || wasm_is_reftype_htref_non_nullable(type)) { + bh_memcpy_s(ref_ht_ret, (uint32)sizeof(WASMRefType), &ref_type, + wasm_reftype_struct_size(&ref_type)); + /* Convert to (ref ht) */ + ref_ht_ret->ref_ht_common.ref_type = REF_TYPE_HT_NON_NULLABLE; + ref_ht_ret->ref_ht_common.nullable = false; + type = ref_ht_ret->ref_type; + } + *p_type = type; + + return true; +} + +/* Check whether the stack top elem is (ref null $t) or (ref $t), + and if yes, pop it and return the type_idx */ +static bool +wasm_loader_pop_nullable_typeidx(WASMLoaderContext *ctx, uint8 *p_type, + uint32 *p_type_idx, char *error_buf, + uint32 error_buf_size) +{ + uint8 type = 0; + int32 type_idx = -1; + WASMRefType *ref_type = NULL; + + /* Get stack top element */ + if (!wasm_loader_get_frame_ref_top(ctx, &type, &ref_type, error_buf, + error_buf_size)) { + return false; + } + + if (type != VALUE_TYPE_ANY) { + /* stack top isn't (ref null $t) */ + if (!((wasm_is_reftype_htref_nullable(type) + || wasm_is_reftype_htref_non_nullable(type)) + && wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common))) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: expect (ref null $t) but got others"); + return false; + } + type_idx = ref_type->ref_ht_typeidx.type_idx; + + bh_memcpy_s(ctx->ref_type_tmp, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } + + /* POP stack top */ +#if WASM_ENABLE_FAST_INTERP != 0 + if (!wasm_loader_pop_frame_ref_offset(ctx, type, error_buf, + error_buf_size)) { + return false; + } +#else + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) { + return false; + } +#endif + + /* Convert to type_idx and return */ + *p_type = type; + if (type != VALUE_TYPE_ANY) + *p_type_idx = (uint32)type_idx; + return true; +} +#endif /* WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_FAST_INTERP == 0 +static bool +wasm_loader_push_pop_frame_ref(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, char *error_buf, + uint32 error_buf_size) +{ + for (int i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_ref(ctx, type_pop, error_buf, + error_buf_size)) + return false; + } + if (!wasm_loader_push_frame_ref(ctx, type_push, error_buf, error_buf_size)) + return false; + return true; +} +#endif + +static bool +wasm_loader_push_frame_csp(WASMLoaderContext *ctx, uint8 label_type, + BlockType block_type, uint8 *start_addr, + char *error_buf, uint32 error_buf_size) +{ + CHECK_CSP_PUSH(); + memset(ctx->frame_csp, 0, sizeof(BranchBlock)); + ctx->frame_csp->label_type = label_type; + ctx->frame_csp->block_type = block_type; + ctx->frame_csp->start_addr = start_addr; + ctx->frame_csp->stack_cell_num = ctx->stack_cell_num; +#if WASM_ENABLE_GC != 0 + ctx->frame_csp->reftype_map_num = ctx->reftype_map_num; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + ctx->frame_csp->dynamic_offset = ctx->dynamic_offset; + ctx->frame_csp->patch_list = NULL; +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* Default sentinel; the WASM_OP_TRY handler patches this on entry + * and the CATCH/CATCH_ALL handlers propagate it onto the rewritten + * label. */ + ctx->frame_csp->eh_entry_idx = UINT32_MAX; +#endif +#endif + ctx->frame_csp++; + ctx->csp_num++; + if (ctx->csp_num > ctx->max_csp_num) { + ctx->max_csp_num = ctx->csp_num; + if (ctx->max_csp_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "label stack depth limit exceeded"); + return false; + } + } + return true; +fail: + return false; +} + +static bool +wasm_loader_pop_frame_csp(WASMLoaderContext *ctx, char *error_buf, + uint32 error_buf_size) +{ + CHECK_CSP_POP(); +#if WASM_ENABLE_FAST_INTERP != 0 + if ((ctx->frame_csp - 1)->param_frame_offsets) + wasm_runtime_free((ctx->frame_csp - 1)->param_frame_offsets); +#endif + ctx->frame_csp--; + ctx->csp_num--; + + return true; +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define emit_label(opcode) \ + do { \ + wasm_loader_emit_ptr(loader_ctx, handle_table[opcode]); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#define skip_label() \ + do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(void *)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) +#else /* else of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#if UINTPTR_MAX == UINT64_MAX +#define emit_label(opcode) \ + do { \ + int32 offset = \ + (int32)((uint8 *)handle_table[opcode] - (uint8 *)handle_table[0]); \ + /* emit int32 relative offset in 64-bit target */ \ + wasm_loader_emit_uint32(loader_ctx, offset); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#else +#define emit_label(opcode) \ + do { \ + uint32 label_addr = (uint32)(uintptr_t)handle_table[opcode]; \ + /* emit uint32 label address in 32-bit target */ \ + wasm_loader_emit_uint32(loader_ctx, label_addr); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#endif +#define skip_label() \ + do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(int32)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#define emit_label(opcode) \ + do { \ + wasm_loader_emit_uint8(loader_ctx, opcode); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#define skip_label() \ + do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(uint8)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +#define emit_empty_label_addr_and_frame_ip(type) \ + do { \ + if (!add_label_patch_to_list(loader_ctx->frame_csp - 1, type, \ + loader_ctx->p_code_compiled, error_buf, \ + error_buf_size)) \ + goto fail; \ + /* label address, to be patched */ \ + wasm_loader_emit_ptr(loader_ctx, NULL); \ + } while (0) + +#define emit_br_info(frame_csp, is_br) \ + do { \ + if (!wasm_loader_emit_br_info(loader_ctx, frame_csp, is_br, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define LAST_OP_OUTPUT_I32() \ + (last_op >= WASM_OP_I32_EQZ && last_op <= WASM_OP_I32_ROTR) \ + || (last_op == WASM_OP_I32_LOAD || last_op == WASM_OP_F32_LOAD) \ + || (last_op >= WASM_OP_I32_LOAD8_S && last_op <= WASM_OP_I32_LOAD16_U) \ + || (last_op >= WASM_OP_F32_ABS && last_op <= WASM_OP_F32_COPYSIGN) \ + || (last_op >= WASM_OP_I32_WRAP_I64 \ + && last_op <= WASM_OP_I32_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F32_CONVERT_S_I32 \ + && last_op <= WASM_OP_F32_DEMOTE_F64) \ + || (last_op == WASM_OP_I32_REINTERPRET_F32) \ + || (last_op == WASM_OP_F32_REINTERPRET_I32) \ + || (last_op == EXT_OP_COPY_STACK_TOP) + +#define LAST_OP_OUTPUT_I64() \ + (last_op >= WASM_OP_I64_CLZ && last_op <= WASM_OP_I64_ROTR) \ + || (last_op >= WASM_OP_F64_ABS && last_op <= WASM_OP_F64_COPYSIGN) \ + || (last_op == WASM_OP_I64_LOAD || last_op == WASM_OP_F64_LOAD) \ + || (last_op >= WASM_OP_I64_LOAD8_S && last_op <= WASM_OP_I64_LOAD32_U) \ + || (last_op >= WASM_OP_I64_EXTEND_S_I32 \ + && last_op <= WASM_OP_I64_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F64_CONVERT_S_I32 \ + && last_op <= WASM_OP_F64_PROMOTE_F32) \ + || (last_op == WASM_OP_I64_REINTERPRET_F64) \ + || (last_op == WASM_OP_F64_REINTERPRET_I64) \ + || (last_op == EXT_OP_COPY_STACK_TOP_I64) + +#define GET_CONST_OFFSET(type, val) \ + do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, &val, \ + &operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F32_OFFSET(type, fval) \ + do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, &fval, \ + &operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F64_OFFSET(type, fval) \ + do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, &fval, \ + &operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define emit_operand(ctx, offset) \ + do { \ + wasm_loader_emit_int16(ctx, offset); \ + LOG_OP("%d\t", offset); \ + } while (0) + +#define emit_byte(ctx, byte) \ + do { \ + wasm_loader_emit_uint8(ctx, byte); \ + LOG_OP("%d\t", byte); \ + } while (0) + +#define emit_uint32(ctx, value) \ + do { \ + wasm_loader_emit_uint32(ctx, value); \ + LOG_OP("%d\t", value); \ + } while (0) + +#define emit_uint64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%lld\t", value); \ + } while (0) + +#define emit_float32(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, true); \ + LOG_OP("%f\t", value); \ + } while (0) + +#define emit_float64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%f\t", value); \ + } while (0) + +static bool +wasm_loader_ctx_reinit(WASMLoaderContext *ctx) +{ + if (!(ctx->p_code_compiled = + loader_malloc(ctx->code_compiled_peak_size, NULL, 0))) + return false; + ctx->p_code_compiled_end = + ctx->p_code_compiled + ctx->code_compiled_peak_size; + + /* clean up frame ref */ + memset(ctx->frame_ref_bottom, 0, ctx->frame_ref_size); + ctx->frame_ref = ctx->frame_ref_bottom; + ctx->stack_cell_num = 0; + +#if WASM_ENABLE_GC != 0 + /* clean up reftype map */ + memset(ctx->frame_reftype_map_bottom, 0, ctx->frame_reftype_map_size); + ctx->frame_reftype_map = ctx->frame_reftype_map_bottom; + ctx->reftype_map_num = 0; +#endif + + /* clean up frame csp */ + memset(ctx->frame_csp_bottom, 0, ctx->frame_csp_size); + ctx->frame_csp = ctx->frame_csp_bottom; + ctx->csp_num = 0; + ctx->max_csp_num = 0; + + /* clean up frame offset */ + memset(ctx->frame_offset_bottom, 0, ctx->frame_offset_size); + ctx->frame_offset = ctx->frame_offset_bottom; + ctx->dynamic_offset = ctx->start_dynamic_offset; + + /* init preserved local offsets */ + ctx->preserved_local_offset = ctx->max_dynamic_offset; + +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* Start of the second traverse — reset the per-function try-block + * cursor so it tracks the same source-order index as the first + * traverse used to size func->exception_handlers. */ + ctx->cur_eh_entry_idx = 0; +#endif + + /* const buf is reserved */ + return true; +} + +static void +increase_compiled_code_space(WASMLoaderContext *ctx, int32 size) +{ + ctx->code_compiled_size += size; + if (ctx->code_compiled_size >= ctx->code_compiled_peak_size) { + ctx->code_compiled_peak_size = ctx->code_compiled_size; + } +} + +static void +wasm_loader_emit_const(WASMLoaderContext *ctx, void *value, bool is_32_bit) +{ + uint32 size = is_32_bit ? sizeof(uint32) : sizeof(uint64); + + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + bh_memcpy_s(ctx->p_code_compiled, + (uint32)(ctx->p_code_compiled_end - ctx->p_code_compiled), + value, size); + ctx->p_code_compiled += size; + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, size); + } +} + +static void +wasm_loader_emit_uint32(WASMLoaderContext *ctx, uint32 value) +{ + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + STORE_U32(ctx->p_code_compiled, value); + ctx->p_code_compiled += sizeof(uint32); + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, sizeof(uint32)); + } +} + +static void +wasm_loader_emit_int16(WASMLoaderContext *ctx, int16 value) +{ + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + STORE_U16(ctx->p_code_compiled, (uint16)value); + ctx->p_code_compiled += sizeof(int16); + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, sizeof(uint16)); + } +} + +static void +wasm_loader_emit_uint8(WASMLoaderContext *ctx, uint8 value) +{ + if (ctx->p_code_compiled) { + *(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(uint8); +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + ctx->p_code_compiled++; + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + } + else { + increase_compiled_code_space(ctx, sizeof(uint8)); +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + increase_compiled_code_space(ctx, sizeof(uint8)); + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + } +} + +static void +wasm_loader_emit_ptr(WASMLoaderContext *ctx, void *value) +{ + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + STORE_PTR(ctx->p_code_compiled, value); + ctx->p_code_compiled += sizeof(void *); + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, sizeof(void *)); + } +} + +static void +wasm_loader_emit_backspace(WASMLoaderContext *ctx, uint32 size) +{ + if (ctx->p_code_compiled) { + ctx->p_code_compiled -= size; +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + if (size == sizeof(uint8)) { + ctx->p_code_compiled--; + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); + } +#endif + } + else { + ctx->code_compiled_size -= size; +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + if (size == sizeof(uint8)) { + ctx->code_compiled_size--; + bh_assert((ctx->code_compiled_size & 1) == 0); + } +#endif + } +} + +static bool +preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, + uint32 local_index, uint32 local_type, + bool *preserved, char *error_buf, + uint32 error_buf_size) +{ + + uint32 i = 0; + int16 preserved_offset = (int16)local_index; + + *preserved = false; + while (i < loader_ctx->stack_cell_num) { + uint8 cur_type = loader_ctx->frame_ref_bottom[i]; + + /* move previous local into dynamic space before a set/tee_local opcode + */ + if (loader_ctx->frame_offset_bottom[i] == (int16)local_index) { + if (!(*preserved)) { + *preserved = true; + skip_label(); + preserved_offset = loader_ctx->preserved_local_offset; + if (loader_ctx->p_code_compiled) { + bh_assert(preserved_offset != (int16)local_index); + } + if (is_32bit_type(local_type)) { + /* Only increase preserve offset in the second traversal */ + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset++; + emit_label(EXT_OP_COPY_STACK_TOP); + } +#if WASM_ENABLE_SIMDE != 0 + else if (local_type == VALUE_TYPE_V128) { + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset += 4; + emit_label(EXT_OP_COPY_STACK_TOP_V128); + } +#endif + else { + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset += 2; + emit_label(EXT_OP_COPY_STACK_TOP_I64); + } + + /* overflow */ + if (preserved_offset > loader_ctx->preserved_local_offset) { + set_error_buf_v(error_buf, error_buf_size, + "too much local cells 0x%x", + loader_ctx->preserved_local_offset); + return false; + } + + emit_operand(loader_ctx, local_index); + emit_operand(loader_ctx, preserved_offset); + emit_label(opcode); + } + loader_ctx->frame_offset_bottom[i] = preserved_offset; + } + + if (cur_type == VALUE_TYPE_V128) { + i += 4; + } + else if (is_32bit_type(cur_type)) { + i++; + } + else { + i += 2; + } + } + + (void)error_buf; + (void)error_buf_size; + return true; +} + +static bool +preserve_local_for_block(WASMLoaderContext *loader_ctx, uint8 opcode, + char *error_buf, uint32 error_buf_size) +{ + uint32 i = 0; + bool preserve_local; + + /* preserve locals before blocks to ensure that "tee/set_local" inside + blocks will not influence the value of these locals */ + uint32 frame_offset_cell = + (uint32)(loader_ctx->frame_offset - loader_ctx->frame_offset_bottom); + uint32 frame_ref_cell = + (uint32)(loader_ctx->frame_ref - loader_ctx->frame_ref_bottom); + if (frame_offset_cell < loader_ctx->stack_cell_num + || frame_ref_cell < loader_ctx->stack_cell_num) { + set_error_buf(error_buf, error_buf_size, "stack cell num error"); + return false; + } + + while (i < loader_ctx->stack_cell_num) { + int16 cur_offset = loader_ctx->frame_offset_bottom[i]; + uint8 cur_type = loader_ctx->frame_ref_bottom[i]; + + if ((cur_offset < loader_ctx->start_dynamic_offset) + && (cur_offset >= 0)) { + if (!(preserve_referenced_local(loader_ctx, opcode, cur_offset, + cur_type, &preserve_local, + error_buf, error_buf_size))) + return false; + } + + if (cur_type == VALUE_TYPE_V128) { + i += 4; + } + else if (is_32bit_type(cur_type)) { + i++; + } + else { + i += 2; + } + } + + return true; +} + +static bool +add_label_patch_to_list(BranchBlock *frame_csp, uint8 patch_type, + uint8 *p_code_compiled, char *error_buf, + uint32 error_buf_size) +{ + BranchBlockPatch *patch = + loader_malloc(sizeof(BranchBlockPatch), error_buf, error_buf_size); + if (!patch) { + return false; + } + patch->patch_type = patch_type; + patch->code_compiled = p_code_compiled; + if (!frame_csp->patch_list) { + frame_csp->patch_list = patch; + patch->next = NULL; + } + else { + patch->next = frame_csp->patch_list; + frame_csp->patch_list = patch; + } + return true; +} + +static void +apply_label_patch(WASMLoaderContext *ctx, uint8 depth, uint8 patch_type) +{ + BranchBlock *frame_csp = ctx->frame_csp - depth; + BranchBlockPatch *node = frame_csp->patch_list; + BranchBlockPatch *node_prev = NULL, *node_next; + + if (!ctx->p_code_compiled) + return; + + while (node) { + node_next = node->next; + if (node->patch_type == patch_type) { + STORE_PTR(node->code_compiled, ctx->p_code_compiled); + if (node_prev == NULL) { + frame_csp->patch_list = node_next; + } + else { + node_prev->next = node_next; + } + wasm_runtime_free(node); + } + else { + node_prev = node; + } + node = node_next; + } +} + +static bool +wasm_loader_emit_br_info(WASMLoaderContext *ctx, BranchBlock *frame_csp, + bool is_br, char *error_buf, uint32 error_buf_size) +{ + /* br info layout: + * a) arity of target block + * b) total cell num of arity values + * c) each arity value's cell num + * d) each arity value's src frame offset + * e) each arity values's dst dynamic offset + * f) branch target address + * + * Note: b-e are omitted when arity is 0 so that + * interpreter can recover the br info quickly. + */ + BlockType *block_type = &frame_csp->block_type; + uint8 *types = NULL, cell; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *reftype_maps; + uint32 reftype_map_count; +#endif + uint32 arity = 0; + int32 i; + int16 *frame_offset = ctx->frame_offset; + uint16 dynamic_offset; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ +#if WASM_ENABLE_GC == 0 + if (frame_csp->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(block_type, &types); + else + arity = block_type_get_result_types(block_type, &types); +#else + if (frame_csp->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(block_type, &types, &reftype_maps, + &reftype_map_count); + else + arity = block_type_get_result_types(block_type, &types, &reftype_maps, + &reftype_map_count); +#endif + + /* Part a */ + emit_uint32(ctx, arity); + + if (arity) { + /* Part b */ + emit_uint32(ctx, wasm_get_cell_num(types, arity)); + /* Part c */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = (uint8)wasm_value_type_cell_num(types[i]); + emit_byte(ctx, cell); + } + /* Part d */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = (uint8)wasm_value_type_cell_num(types[i]); + frame_offset -= cell; + emit_operand(ctx, *(int16 *)(frame_offset)); + } + /* Part e */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) + /* Use start_dynamic_offset which was set in + copy_params_to_dynamic_space */ + dynamic_offset = frame_csp->start_dynamic_offset + + wasm_get_cell_num(types, arity); + else + dynamic_offset = + frame_csp->dynamic_offset + wasm_get_cell_num(types, arity); + if (is_br) + ctx->dynamic_offset = dynamic_offset; + for (i = (int32)arity - 1; i >= 0; i--) { + cell = (uint8)wasm_value_type_cell_num(types[i]); + dynamic_offset -= cell; + emit_operand(ctx, dynamic_offset); + } + } + + /* Part f */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) { + wasm_loader_emit_ptr(ctx, frame_csp->code_compiled); + } + else { + if (!add_label_patch_to_list(frame_csp, PATCH_END, ctx->p_code_compiled, + error_buf, error_buf_size)) + return false; + /* label address, to be patched */ + wasm_loader_emit_ptr(ctx, NULL); + } + + return true; +} + +static bool +wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + uint32 cell_num_to_push, i; + + if (type == VALUE_TYPE_VOID) + return true; + + /* only check memory overflow in first traverse */ + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + if (disable_emit) + *(ctx->frame_offset)++ = operand_offset; + else { + emit_operand(ctx, ctx->dynamic_offset); + *(ctx->frame_offset)++ = ctx->dynamic_offset; + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { + ctx->max_dynamic_offset = ctx->dynamic_offset; + if (ctx->max_dynamic_offset >= INT16_MAX) { + goto fail; + } + } + } + + if (is_32bit_type(type)) + return true; + + cell_num_to_push = wasm_value_type_cell_num(type) - 1; + for (i = 0; i < cell_num_to_push; i++) { + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + ctx->frame_offset++; + if (!disable_emit) { + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { + ctx->max_dynamic_offset = ctx->dynamic_offset; + if (ctx->max_dynamic_offset >= INT16_MAX) + goto fail; + } + } + } + + return true; + +fail: + set_error_buf(error_buf, error_buf_size, + "fast interpreter offset overflow"); + return false; +} + +/* This function should be in front of wasm_loader_pop_frame_ref + as they both use ctx->stack_cell_num, and ctx->stack_cell_num + will be modified by wasm_loader_pop_frame_ref */ +static bool +wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* if ctx->frame_csp equals ctx->frame_csp_bottom, + then current block is the function block */ + uint32 depth = ctx->frame_csp > ctx->frame_csp_bottom ? 1 : 0; + BranchBlock *cur_block = ctx->frame_csp - depth; + int32 available_stack_cell = + (int32)(ctx->stack_cell_num - cur_block->stack_cell_num); + uint32 cell_num_to_pop; + + /* Directly return success if current block is in stack + polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + /* Change type to ANY when the stack top is ANY, so as to avoid + popping unneeded offsets, e.g. if type is I64/F64, we may pop + two offsets */ + if (available_stack_cell > 0 && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) + type = VALUE_TYPE_ANY; + + cell_num_to_pop = wasm_value_type_cell_num(type); + + /* Check the offset stack bottom to ensure the frame offset + stack will not go underflow. But we don't thrown error + and return true here, because the error msg should be + given in wasm_loader_pop_frame_ref */ + if (!check_offset_pop(ctx, cell_num_to_pop)) + return true; + + ctx->frame_offset -= cell_num_to_pop; + if (check_dynamic_offset_pop(ctx, cell_num_to_pop) + && (*(ctx->frame_offset) > ctx->start_dynamic_offset) + && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) + ctx->dynamic_offset -= cell_num_to_pop; + + emit_operand(ctx, *(ctx->frame_offset)); + + (void)error_buf; + (void)error_buf_size; + return true; +} + +static bool +wasm_loader_push_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (!(wasm_loader_push_frame_offset(ctx, type, disable_emit, operand_offset, + error_buf, error_buf_size))) + return false; + if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size))) + return false; + + return true; +} + +static bool +wasm_loader_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* put wasm_loader_pop_frame_offset in front of wasm_loader_pop_frame_ref */ + if (!wasm_loader_pop_frame_offset(ctx, type, error_buf, error_buf_size)) + return false; + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_push_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + uint8 i; + + for (i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_offset(ctx, type_pop, error_buf, + error_buf_size)) + return false; + + if (!wasm_loader_pop_frame_ref(ctx, type_pop, error_buf, + error_buf_size)) + return false; + } + + if (!wasm_loader_push_frame_offset(ctx, type_push, disable_emit, + operand_offset, error_buf, + error_buf_size)) + return false; + + if (!wasm_loader_push_frame_ref(ctx, type_push, error_buf, error_buf_size)) + return false; + + return true; +} + +static int +cmp_i64_const(const void *p_i64_const1, const void *p_i64_const2) +{ + int64 i64_const1 = *(int64 *)p_i64_const1; + int64 i64_const2 = *(int64 *)p_i64_const2; + + return (i64_const1 < i64_const2) ? -1 : (i64_const1 > i64_const2) ? 1 : 0; +} + +static int +cmp_i32_const(const void *p_i32_const1, const void *p_i32_const2) +{ + int32 i32_const1 = *(int32 *)p_i32_const1; + int32 i32_const2 = *(int32 *)p_i32_const2; + + return (i32_const1 < i32_const2) ? -1 : (i32_const1 > i32_const2) ? 1 : 0; +} + +static int +cmp_v128_const(const void *p_v128_const1, const void *p_v128_const2) +{ + V128 v128_const1 = *(V128 *)p_v128_const1; + V128 v128_const2 = *(V128 *)p_v128_const2; + + return memcmp(&v128_const1, &v128_const2, sizeof(V128)); +} + +static bool +wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, void *value, + int16 *offset, char *error_buf, + uint32 error_buf_size) +{ + if (!ctx->p_code_compiled) { + /* Treat i64 and f64 as the same by reading i64 value from + the raw bytes */ + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) { + /* No slot left, emit const instead */ + if (ctx->i64_const_num * 2 + ctx->i32_const_num > INT16_MAX - 2) { + *offset = 0; + return true; + } + + /* Traverse the list if the const num is small */ + if (ctx->i64_const_num < 10) { + for (uint32 i = 0; i < ctx->i64_const_num; i++) { + if (ctx->i64_consts[i] == *(int64 *)value) { + *offset = -1; + return true; + } + } + } + + if (ctx->i64_const_num >= ctx->i64_const_max_num) { + MEM_REALLOC(ctx->i64_consts, + sizeof(int64) * ctx->i64_const_max_num, + sizeof(int64) * (ctx->i64_const_max_num * 2)); + ctx->i64_const_max_num *= 2; + } + ctx->i64_consts[ctx->i64_const_num++] = *(int64 *)value; + } + else if (type == VALUE_TYPE_V128) { + /* No slot left, emit const instead */ + if (ctx->v128_const_num * 4 > INT16_MAX - 2) { + *offset = 0; + return true; + } + + /* Traverse the list if the const num is small */ + if (ctx->v128_const_num < 10) { + for (uint32 i = 0; i < ctx->v128_const_num; i++) { + if (memcmp(&ctx->v128_consts[i], value, sizeof(V128)) + == 0) { + *offset = -1; + return true; + } + } + } + + if (ctx->v128_const_num >= ctx->v128_const_max_num) { + MEM_REALLOC(ctx->v128_consts, + sizeof(V128) * ctx->v128_const_max_num, + sizeof(V128) * (ctx->v128_const_max_num * 2)); + ctx->v128_const_max_num *= 2; + } + ctx->v128_consts[ctx->v128_const_num++] = *(V128 *)value; + } + else { + /* Treat i32 and f32 as the same by reading i32 value from + the raw bytes */ + bh_assert(type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32); + + /* No slot left, emit const instead */ + if (ctx->i64_const_num * 2 + ctx->i32_const_num > INT16_MAX - 1) { + *offset = 0; + return true; + } + + /* Traverse the list if the const num is small */ + if (ctx->i32_const_num < 10) { + for (uint32 i = 0; i < ctx->i32_const_num; i++) { + if (ctx->i32_consts[i] == *(int32 *)value) { + *offset = -1; + return true; + } + } + } + + if (ctx->i32_const_num >= ctx->i32_const_max_num) { + MEM_REALLOC(ctx->i32_consts, + sizeof(int32) * ctx->i32_const_max_num, + sizeof(int32) * (ctx->i32_const_max_num * 2)); + ctx->i32_const_max_num *= 2; + } + ctx->i32_consts[ctx->i32_const_num++] = *(int32 *)value; + } + + *offset = -1; + return true; + } + else { + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) { + int64 key = *(int64 *)value, *i64_const; + i64_const = bsearch(&key, ctx->i64_consts, ctx->i64_const_num, + sizeof(int64), cmp_i64_const); + if (!i64_const) { /* not found, emit const instead */ + *offset = 0; + return true; + } + + /* constant index is encoded as negative value */ + *offset = -(int32)(ctx->i64_const_num * 2 + ctx->i32_const_num) + + (int32)(i64_const - ctx->i64_consts) * 2; + } + else if (type == VALUE_TYPE_V128) { + V128 key = *(V128 *)value, *v128_const; + v128_const = bsearch(&key, ctx->v128_consts, ctx->v128_const_num, + sizeof(V128), cmp_v128_const); + if (!v128_const) { /* not found, emit const instead */ + *offset = 0; + return true; + } + + /* constant index is encoded as negative value */ + *offset = -(int32)(ctx->v128_const_num) + + (int32)(v128_const - ctx->v128_consts); + } + + else { + int32 key = *(int32 *)value, *i32_const; + i32_const = bsearch(&key, ctx->i32_consts, ctx->i32_const_num, + sizeof(int32), cmp_i32_const); + if (!i32_const) { /* not found, emit const instead */ + *offset = 0; + return true; + } + + /* constant index is encoded as negative value */ + *offset = -(int32)(ctx->i32_const_num) + + (int32)(i32_const - ctx->i32_consts); + } + + return true; + } +fail: + return false; +} + +/* + PUSH(POP)_XXX = push(pop) frame_ref + push(pop) frame_offset + -- Mostly used for the binary / compare operation + PUSH(POP)_OFFSET_TYPE only push(pop) the frame_offset stack + -- Mostly used in block / control instructions + + The POP will always emit the offset on the top of the frame_offset stack + PUSH can be used in two ways: + 1. directly PUSH: + PUSH_XXX(); + will allocate a dynamic space and emit + 2. silent PUSH: + operand_offset = xxx; disable_emit = true; + PUSH_XXX(); + only push the frame_offset stack, no emit +*/ + +#define TEMPLATE_PUSH(Type) \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_##Type, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define TEMPLATE_PUSH_REF(Type) \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, Type, disable_emit, \ + operand_offset, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define TEMPLATE_POP(Type) \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_##Type, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define TEMPLATE_POP_REF(Type) \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, Type, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_OFFSET_TYPE(type) \ + do { \ + if (!(wasm_loader_push_frame_offset(loader_ctx, type, disable_emit, \ + operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_OFFSET_TYPE(type) \ + do { \ + if (!(wasm_loader_pop_frame_offset(loader_ctx, type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref_offset( \ + loader_ctx, 1, type_push, type_pop, disable_emit, \ + operand_offset, error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref_offset( \ + loader_ctx, 2, type_push, type_pop, disable_emit, \ + operand_offset, error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#else /* WASM_ENABLE_FAST_INTERP */ + +#define TEMPLATE_PUSH(Type) \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_##Type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define TEMPLATE_PUSH_REF(Type) \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, Type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define TEMPLATE_POP(Type) \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_##Type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define TEMPLATE_POP_REF(Type) \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, Type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 1, type_push, \ + type_pop, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 2, type_push, \ + type_pop, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) +#endif /* WASM_ENABLE_FAST_INTERP */ + +#define PUSH_I32() TEMPLATE_PUSH(I32) +#define PUSH_F32() TEMPLATE_PUSH(F32) +#define PUSH_I64() TEMPLATE_PUSH(I64) +#define PUSH_F64() TEMPLATE_PUSH(F64) +#define PUSH_V128() TEMPLATE_PUSH(V128) +#define PUSH_FUNCREF() TEMPLATE_PUSH(FUNCREF) +#define PUSH_EXTERNREF() TEMPLATE_PUSH(EXTERNREF) +#define PUSH_REF(Type) TEMPLATE_PUSH_REF(Type) +#define POP_REF(Type) TEMPLATE_POP_REF(Type) +#define PUSH_MEM_OFFSET() TEMPLATE_PUSH_REF(mem_offset_type) +#define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET() +#define PUSH_TBL_ELEM_IDX() TEMPLATE_PUSH_REF(table_elem_idx_type) + +#define POP_I32() TEMPLATE_POP(I32) +#define POP_F32() TEMPLATE_POP(F32) +#define POP_I64() TEMPLATE_POP(I64) +#define POP_F64() TEMPLATE_POP(F64) +#define POP_V128() TEMPLATE_POP(V128) +#define POP_FUNCREF() TEMPLATE_POP(FUNCREF) +#define POP_EXTERNREF() TEMPLATE_POP(EXTERNREF) +#define POP_STRINGREF() TEMPLATE_POP(STRINGREF) +#define POP_MEM_OFFSET() TEMPLATE_POP_REF(mem_offset_type) +#define POP_TBL_ELEM_IDX() TEMPLATE_POP_REF(table_elem_idx_type) + +#if WASM_ENABLE_FAST_INTERP != 0 + +static bool +reserve_block_ret(WASMLoaderContext *loader_ctx, uint8 opcode, + bool disable_emit, char *error_buf, uint32 error_buf_size) +{ + int16 operand_offset = 0; + BranchBlock *block = (opcode == WASM_OP_ELSE) ? loader_ctx->frame_csp - 1 + : loader_ctx->frame_csp; + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *reftype_maps = NULL; + uint32 reftype_map_count; +#endif + uint32 return_count = 0, value_count = 0, total_cel_num = 0; + int32 i = 0; + int16 dynamic_offset, dynamic_offset_org, *frame_offset = NULL, + *frame_offset_org = NULL; + +#if WASM_ENABLE_GC == 0 + return_count = block_type_get_result_types(block_type, &return_types); +#else + return_count = block_type_get_result_types( + block_type, &return_types, &reftype_maps, &reftype_map_count); +#endif + + /* If there is only one return value, use EXT_OP_COPY_STACK_TOP/_I64/V128 + * instead of EXT_OP_COPY_STACK_VALUES for interpreter performance. */ + if (return_count == 1) { + uint8 cell = (uint8)wasm_value_type_cell_num(return_types[0]); + if (block->dynamic_offset != *(loader_ctx->frame_offset - cell)) { + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); +#if WASM_ENABLE_SIMDE != 0 + if (cell == 4) { + emit_label(EXT_OP_COPY_STACK_TOP_V128); + } +#endif + if (cell <= 2) { + emit_label(cell == 1 ? EXT_OP_COPY_STACK_TOP + : EXT_OP_COPY_STACK_TOP_I64); + } + emit_operand(loader_ctx, *(loader_ctx->frame_offset - cell)); + emit_operand(loader_ctx, block->dynamic_offset); + + if (opcode == WASM_OP_ELSE) { + *(loader_ctx->frame_offset - cell) = block->dynamic_offset; + } + else { + loader_ctx->frame_offset -= cell; + loader_ctx->dynamic_offset = block->dynamic_offset; + PUSH_OFFSET_TYPE(return_types[0]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + } + return true; + } + + /* Copy stack top values to block's results which are in dynamic space. + * The instruction format: + * Part a: values count + * Part b: all values total cell num + * Part c: each value's cell_num, src offset and dst offset + * Part d: each value's src offset and dst offset + * Part e: each value's dst offset + */ + frame_offset = frame_offset_org = loader_ctx->frame_offset; + dynamic_offset = dynamic_offset_org = + block->dynamic_offset + wasm_get_cell_num(return_types, return_count); + + /* First traversal to get the count of values needed to be copied. */ + for (i = (int32)return_count - 1; i >= 0; i--) { + uint8 cells = (uint8)wasm_value_type_cell_num(return_types[i]); + + if (frame_offset - cells < loader_ctx->frame_offset_bottom) { + set_error_buf(error_buf, error_buf_size, "frame offset underflow"); + goto fail; + } + + if (cells == 4) { + bool needs_copy = false; + int16 v128_dynamic = dynamic_offset - cells; + + for (int j = 0; j < 4; j++) { + if (*(frame_offset - j - 1) != (v128_dynamic + j)) { + needs_copy = true; + break; + } + } + + if (needs_copy) { + value_count++; + total_cel_num += cells; + } + + frame_offset -= cells; + dynamic_offset = v128_dynamic; + } + else { + frame_offset -= cells; + dynamic_offset -= cells; + if (dynamic_offset != *frame_offset) { + value_count++; + total_cel_num += cells; + } + } + } + + if (value_count) { + uint32 j = 0; + uint8 *emit_data = NULL, *cells = NULL; + int16 *src_offsets = NULL; + uint16 *dst_offsets = NULL; + uint64 size = + (uint64)value_count + * (sizeof(*cells) + sizeof(*src_offsets) + sizeof(*dst_offsets)); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + value_count); + dst_offsets = (uint16 *)(src_offsets + value_count); + + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, value_count); + /* Part b) */ + emit_uint32(loader_ctx, total_cel_num); + + /* Second traversal to get each value's cell num, src offset and dst + * offset. */ + frame_offset = frame_offset_org; + dynamic_offset = dynamic_offset_org; + for (i = (int32)return_count - 1, j = 0; i >= 0; i--) { + uint8 cell = (uint8)wasm_value_type_cell_num(return_types[i]); + + if (cell == 4) { + bool needs_copy = false; + int16 v128_dynamic = dynamic_offset - cell; + + for (int k = 0; k < 4; k++) { + if (*(frame_offset - k - 1) != (v128_dynamic + k)) { + needs_copy = true; + break; + } + } + + if (needs_copy) { + cells[j] = cell; + src_offsets[j] = *(frame_offset - cell); + dst_offsets[j] = v128_dynamic; + j++; + } + + frame_offset -= cell; + dynamic_offset = v128_dynamic; + } + else { + frame_offset -= cell; + dynamic_offset -= cell; + if (dynamic_offset != *frame_offset) { + cells[j] = cell; + /* src offset */ + src_offsets[j] = *frame_offset; + /* dst offset */ + dst_offsets[j] = dynamic_offset; + j++; + } + } + + if (opcode == WASM_OP_ELSE) { + if (cell == 4) { + for (int k = 0; k < cell; k++) { + *(frame_offset + k) = dynamic_offset + k; + } + } + else { + *frame_offset = dynamic_offset; + } + } + else { + loader_ctx->frame_offset = frame_offset; + loader_ctx->dynamic_offset = dynamic_offset; + if (!(wasm_loader_push_frame_offset( + loader_ctx, return_types[i], disable_emit, + operand_offset, error_buf, error_buf_size))) { + wasm_runtime_free(emit_data); + goto fail; + } + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + loader_ctx->frame_offset = frame_offset_org; + loader_ctx->dynamic_offset = dynamic_offset_org; + } + } + + bh_assert(j == value_count); + + /* Emit the cells, src_offsets and dst_offsets */ + for (j = 0; j < value_count; j++) + emit_byte(loader_ctx, cells[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, src_offsets[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, dst_offsets[j]); + + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + + wasm_runtime_free(emit_data); + } + + return true; + +fail: + return false; +} +#endif /* WASM_ENABLE_FAST_INTERP */ + +#define PUSH_TYPE(type) \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_TYPE(type) \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#if WASM_ENABLE_GC == 0 +#define PUSH_CSP(label_type, block_type, _start_addr) \ + do { \ + if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \ + _start_addr, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_CSP() \ + do { \ + if (!wasm_loader_pop_frame_csp(loader_ctx, error_buf, error_buf_size)) \ + goto fail; \ + } while (0) +#else +#define PUSH_CSP(label_type, block_type, _start_addr) \ + do { \ + if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \ + _start_addr, error_buf, \ + error_buf_size)) \ + goto fail; \ + if (!wasm_loader_init_local_use_masks(loader_ctx, local_count, \ + error_buf, error_buf_size)) { \ + goto fail; \ + } \ + } while (0) + +#define POP_CSP() \ + do { \ + wasm_loader_destroy_curr_local_use_masks(loader_ctx); \ + if (!wasm_loader_pop_frame_csp(loader_ctx, error_buf, error_buf_size)) \ + goto fail; \ + } while (0) +#endif /* end of WASM_ENABLE_GC == 0 */ + +#if WASM_ENABLE_GC == 0 +#define GET_LOCAL_REFTYPE() (void)0 +#else +#define GET_LOCAL_REFTYPE() \ + do { \ + if (wasm_is_type_multi_byte_type(local_type)) { \ + WASMRefType *_ref_type; \ + if (local_idx < param_count) \ + _ref_type = wasm_reftype_map_find( \ + param_reftype_maps, param_reftype_map_count, local_idx); \ + else \ + _ref_type = wasm_reftype_map_find(local_reftype_maps, \ + local_reftype_map_count, \ + local_idx - param_count); \ + bh_assert(_ref_type); \ + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), _ref_type, \ + wasm_reftype_struct_size(_ref_type)); \ + } \ + } while (0) +#endif /* end of WASM_ENABLE_GC == 0 */ + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() \ + do { \ + read_leb_uint32(p, p_end, local_idx); \ + if (local_idx >= param_count + local_count) { \ + set_error_buf(error_buf, error_buf_size, "unknown local"); \ + goto fail; \ + } \ + local_type = local_idx < param_count \ + ? param_types[local_idx] \ + : local_types[local_idx - param_count]; \ + local_offset = local_offsets[local_idx]; \ + GET_LOCAL_REFTYPE(); \ + } while (0) + +static bool +check_memory(WASMModule *module, char *error_buf, uint32 error_buf_size) +{ + if (module->memory_count == 0 && module->import_memory_count == 0) { + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + } + return true; +} + +#define CHECK_MEMORY() \ + do { \ + if (!check_memory(module, error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +static bool +check_memory_access_align(uint8 opcode, uint32 align, char *error_buf, + uint32 error_buf_size) +{ + uint8 mem_access_aligns[] = { + 2, 3, 2, 3, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, /* loads */ + 2, 3, 2, 3, 0, 1, 0, 1, 2 /* stores */ + }; + bh_assert(opcode >= WASM_OP_I32_LOAD && opcode <= WASM_OP_I64_STORE32); + if (align > mem_access_aligns[opcode - WASM_OP_I32_LOAD]) { + set_error_buf(error_buf, error_buf_size, + "invalid memop flags: alignment must not be larger " + "than natural"); + return false; + } + return true; +} + +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) +static bool +check_simd_memory_access_align(uint8 opcode, uint32 align, char *error_buf, + uint32 error_buf_size) +{ + uint8 mem_access_aligns[] = { + 4, /* load */ + 3, 3, 3, 3, 3, 3, /* load and extend */ + 0, 1, 2, 3, /* load and splat */ + 4, /* store */ + }; + + uint8 mem_access_aligns_load_lane[] = { + 0, 1, 2, 3, /* load lane */ + 0, 1, 2, 3, /* store lane */ + 2, 3 /* store zero */ + }; + + if (!((opcode <= SIMD_v128_store) + || (SIMD_v128_load8_lane <= opcode + && opcode <= SIMD_v128_load64_zero))) { + set_error_buf(error_buf, error_buf_size, + "the opcode doesn't include memarg"); + return false; + } + + if ((opcode <= SIMD_v128_store + && align > mem_access_aligns[opcode - SIMD_v128_load]) + || (SIMD_v128_load8_lane <= opcode && opcode <= SIMD_v128_load64_zero + && align > mem_access_aligns_load_lane[opcode + - SIMD_v128_load8_lane])) { + set_error_buf(error_buf, error_buf_size, + "invalid memop flags: alignment must not be larger " + "than natural"); + return false; + } + + return true; +} + +static bool +check_simd_access_lane(uint8 opcode, uint8 lane, char *error_buf, + uint32 error_buf_size) +{ + switch (opcode) { + case SIMD_i8x16_extract_lane_s: + case SIMD_i8x16_extract_lane_u: + case SIMD_i8x16_replace_lane: + if (lane >= 16) { + goto fail; + } + break; + case SIMD_i16x8_extract_lane_s: + case SIMD_i16x8_extract_lane_u: + case SIMD_i16x8_replace_lane: + if (lane >= 8) { + goto fail; + } + break; + case SIMD_i32x4_extract_lane: + case SIMD_i32x4_replace_lane: + case SIMD_f32x4_extract_lane: + case SIMD_f32x4_replace_lane: + if (lane >= 4) { + goto fail; + } + break; + case SIMD_i64x2_extract_lane: + case SIMD_i64x2_replace_lane: + case SIMD_f64x2_extract_lane: + case SIMD_f64x2_replace_lane: + if (lane >= 2) { + goto fail; + } + break; + + case SIMD_v128_load8_lane: + case SIMD_v128_load16_lane: + case SIMD_v128_load32_lane: + case SIMD_v128_load64_lane: + case SIMD_v128_store8_lane: + case SIMD_v128_store16_lane: + case SIMD_v128_store32_lane: + case SIMD_v128_store64_lane: + case SIMD_v128_load32_zero: + case SIMD_v128_load64_zero: + { + uint8 max_lanes[] = { 16, 8, 4, 2, 16, 8, 4, 2, 4, 2 }; + if (lane >= max_lanes[opcode - SIMD_v128_load8_lane]) { + goto fail; + } + break; + } + default: + goto fail; + } + + return true; +fail: + set_error_buf(error_buf, error_buf_size, "invalid lane index"); + return false; +} + +static bool +check_simd_shuffle_mask(V128 mask, char *error_buf, uint32 error_buf_size) +{ + uint8 i; + for (i = 0; i != 16; ++i) { + if (mask.i8x16[i] < 0 || mask.i8x16[i] >= 32) { + set_error_buf(error_buf, error_buf_size, "invalid lane index"); + return false; + } + } + return true; +} +#endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) */ +#endif /* end of WASM_ENABLE_SIMD */ + +#if WASM_ENABLE_SHARED_MEMORY != 0 +static bool +check_memory_align_equal(uint8 opcode, uint32 align, char *error_buf, + uint32 error_buf_size) +{ + uint8 wait_notify_aligns[] = { 2, 2, 3 }; + uint8 mem_access_aligns[] = { + 2, 3, 0, 1, 0, 1, 2, + }; + uint8 expect; + + bh_assert((opcode <= WASM_OP_ATOMIC_WAIT64) + || (opcode >= WASM_OP_ATOMIC_I32_LOAD + && opcode <= WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U)); + if (opcode <= WASM_OP_ATOMIC_WAIT64) { + expect = wait_notify_aligns[opcode - WASM_OP_ATOMIC_NOTIFY]; + } + else { + /* 7 opcodes in every group */ + expect = mem_access_aligns[(opcode - WASM_OP_ATOMIC_I32_LOAD) % 7]; + } + if (align != expect) { + set_error_buf(error_buf, error_buf_size, + "alignment isn't equal to natural"); + return false; + } + return true; +} +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + +static bool +wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, uint8 opcode, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *target_block, *cur_block; + BlockType *target_block_type; + uint8 type, *types = NULL, *frame_ref; + uint32 arity = 0; + int32 i, available_stack_cell; + uint16 cell_num; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *frame_reftype_map; + WASMRefTypeMap *reftype_maps = NULL, *reftype_map = NULL; + WASMRefType *ref_type; + uint32 reftype_map_count = 0; + int32 available_reftype_map; + bool is_type_multi_byte; +#endif + + uint8 *frame_ref_old = loader_ctx->frame_ref; + uint8 *frame_ref_after_popped = NULL; + uint8 frame_ref_tmp[4] = { 0 }; + uint8 *frame_ref_buf = frame_ref_tmp; + uint32 stack_cell_num_old = loader_ctx->stack_cell_num; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *frame_reftype_map_old = loader_ctx->frame_reftype_map; + WASMRefTypeMap *frame_reftype_map_after_popped = NULL; + WASMRefTypeMap frame_reftype_map_tmp[4] = { 0 }; + WASMRefTypeMap *frame_reftype_map_buf = frame_reftype_map_tmp; + uint32 reftype_map_num_old = loader_ctx->reftype_map_num; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + int16 *frame_offset_old = loader_ctx->frame_offset; + int16 *frame_offset_after_popped = NULL; + int16 frame_offset_tmp[4] = { 0 }; + int16 *frame_offset_buf = frame_offset_tmp; + uint16 dynamic_offset_old = (loader_ctx->frame_csp - 1)->dynamic_offset; +#endif + bool ret = false; + + bh_assert(loader_ctx->csp_num > 0); + if (loader_ctx->csp_num - 1 < depth) { + set_error_buf(error_buf, error_buf_size, + "unknown label, " + "unexpected end of section or function"); + return false; + } + + cur_block = loader_ctx->frame_csp - 1; + target_block = loader_ctx->frame_csp - (depth + 1); + target_block_type = &target_block->block_type; + frame_ref = loader_ctx->frame_ref; +#if WASM_ENABLE_GC != 0 + frame_reftype_map = loader_ctx->frame_reftype_map; +#endif + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ +#if WASM_ENABLE_GC == 0 + if (target_block->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(target_block_type, &types); + else + arity = block_type_get_result_types(target_block_type, &types); +#else + if (target_block->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(target_block_type, &types, + &reftype_maps, &reftype_map_count); + else + arity = block_type_get_result_types(target_block_type, &types, + &reftype_maps, &reftype_map_count); +#endif + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (cur_block->is_stack_polymorphic) { +#if WASM_ENABLE_GC != 0 + int32 j = (int32)reftype_map_count - 1; +#endif + for (i = (int32)arity - 1; i >= 0; i--) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(types[i])) { + bh_assert(reftype_maps[j].index == i); + bh_memcpy_s(loader_ctx->ref_type_tmp, sizeof(WASMRefType), + reftype_maps[j].ref_type, + wasm_reftype_struct_size(reftype_maps[j].ref_type)); + j--; + } +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(types[i]); +#endif + POP_TYPE(types[i]); + } + + /* Backup stack data since it may be changed in the below + push operations, and the stack data may be used when + checking other target blocks of opcode br_table */ + if (opcode == WASM_OP_BR_TABLE) { + uint64 total_size; + + frame_ref_after_popped = loader_ctx->frame_ref; + total_size = (uint64)sizeof(uint8) + * (frame_ref_old - frame_ref_after_popped); + if (total_size > sizeof(frame_ref_tmp) + && !(frame_ref_buf = loader_malloc(total_size, error_buf, + error_buf_size))) { + goto fail; + } + bh_memcpy_s(frame_ref_buf, (uint32)total_size, + frame_ref_after_popped, (uint32)total_size); + +#if WASM_ENABLE_GC != 0 + frame_reftype_map_after_popped = loader_ctx->frame_reftype_map; + total_size = + (uint64)sizeof(WASMRefTypeMap) + * (frame_reftype_map_old - frame_reftype_map_after_popped); + if (total_size > sizeof(frame_reftype_map_tmp) + && !(frame_reftype_map_buf = loader_malloc( + total_size, error_buf, error_buf_size))) { + goto fail; + } + bh_memcpy_s(frame_reftype_map_buf, (uint32)total_size, + frame_reftype_map_after_popped, (uint32)total_size); +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + frame_offset_after_popped = loader_ctx->frame_offset; + total_size = (uint64)sizeof(int16) + * (frame_offset_old - frame_offset_after_popped); + if (total_size > sizeof(frame_offset_tmp) + && !(frame_offset_buf = loader_malloc(total_size, error_buf, + error_buf_size))) { + goto fail; + } + bh_memcpy_s(frame_offset_buf, (uint32)total_size, + frame_offset_after_popped, (uint32)total_size); +#endif + } + +#if WASM_ENABLE_GC != 0 + j = 0; +#endif + for (i = 0; i < (int32)arity; i++) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(types[i])) { + bh_assert(reftype_maps[j].index == i); + bh_memcpy_s(loader_ctx->ref_type_tmp, sizeof(WASMRefType), + reftype_maps[j].ref_type, + wasm_reftype_struct_size(reftype_maps[j].ref_type)); + j++; + } +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(types[i]); +#endif + PUSH_TYPE(types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(target_block, opcode == WASM_OP_BR); +#endif + + /* Restore the stack data, note that frame_ref_bottom, + frame_reftype_map_bottom, frame_offset_bottom may be + re-allocated in the above push operations */ + if (opcode == WASM_OP_BR_TABLE) { + uint32 total_size; + + /* The stack operand num should not be smaller than before + after pop and push operations */ + bh_assert(loader_ctx->stack_cell_num >= stack_cell_num_old); + loader_ctx->stack_cell_num = stack_cell_num_old; + loader_ctx->frame_ref = + loader_ctx->frame_ref_bottom + stack_cell_num_old; + total_size = (uint32)(sizeof(uint8) + * (frame_ref_old - frame_ref_after_popped)); + bh_memcpy_s((uint8 *)loader_ctx->frame_ref - total_size, total_size, + frame_ref_buf, total_size); + +#if WASM_ENABLE_GC != 0 + /* The stack operand num should not be smaller than before + after pop and push operations */ + bh_assert(loader_ctx->reftype_map_num >= reftype_map_num_old); + loader_ctx->reftype_map_num = reftype_map_num_old; + loader_ctx->frame_reftype_map = + loader_ctx->frame_reftype_map_bottom + reftype_map_num_old; + total_size = (uint32)(sizeof(WASMRefTypeMap) + * (frame_reftype_map_old + - frame_reftype_map_after_popped)); + bh_memcpy_s((uint8 *)loader_ctx->frame_reftype_map - total_size, + total_size, frame_reftype_map_buf, total_size); +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + loader_ctx->frame_offset = + loader_ctx->frame_offset_bottom + stack_cell_num_old; + total_size = + (uint32)(sizeof(int16) + * (frame_offset_old - frame_offset_after_popped)); + bh_memcpy_s((uint8 *)loader_ctx->frame_offset - total_size, + total_size, frame_offset_buf, total_size); + (loader_ctx->frame_csp - 1)->dynamic_offset = dynamic_offset_old; +#endif + } + + ret = true; + goto cleanup_and_return; + } + + available_stack_cell = + (int32)(loader_ctx->stack_cell_num - cur_block->stack_cell_num); +#if WASM_ENABLE_GC != 0 + available_reftype_map = + (int32)(loader_ctx->reftype_map_num + - (loader_ctx->frame_csp - 1)->reftype_map_num); + reftype_map = reftype_maps ? reftype_maps + reftype_map_count - 1 : NULL; +#endif + + /* Check stack top values match target block type */ + for (i = (int32)arity - 1; i >= 0; i--) { + type = types[i]; +#if WASM_ENABLE_GC != 0 + ref_type = NULL; + is_type_multi_byte = wasm_is_type_multi_byte_type(type); + if (is_type_multi_byte) { + bh_assert(reftype_map); + ref_type = reftype_map->ref_type; + } +#endif + + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + break; + + if (!check_stack_top_values(loader_ctx, frame_ref, available_stack_cell, +#if WASM_ENABLE_GC != 0 + frame_reftype_map, available_reftype_map, +#endif + type, +#if WASM_ENABLE_GC != 0 + ref_type, +#endif + error_buf, error_buf_size)) { + goto fail; + } + cell_num = wasm_value_type_cell_num(types[i]); + frame_ref -= cell_num; + available_stack_cell -= cell_num; +#if WASM_ENABLE_GC != 0 + if (is_type_multi_byte) { + frame_reftype_map--; + available_reftype_map--; + reftype_map--; + } +#endif + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(target_block, opcode == WASM_OP_BR); +#endif + + ret = true; + +cleanup_and_return: +fail: + if (frame_ref_buf && frame_ref_buf != frame_ref_tmp) + wasm_runtime_free(frame_ref_buf); +#if WASM_ENABLE_GC != 0 + if (frame_reftype_map_buf && frame_reftype_map_buf != frame_reftype_map_tmp) + wasm_runtime_free(frame_reftype_map_buf); +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + if (frame_offset_buf && frame_offset_buf != frame_offset_tmp) + wasm_runtime_free(frame_offset_buf); +#endif + + return ret; +} + +static BranchBlock * +check_branch_block(WASMLoaderContext *loader_ctx, uint8 **p_buf, uint8 *buf_end, + uint8 opcode, char *error_buf, uint32 error_buf_size) +{ + uint8 *p = *p_buf, *p_end = buf_end; + BranchBlock *frame_csp_tmp; + uint32 depth; + + read_leb_uint32(p, p_end, depth); + if (!wasm_loader_check_br(loader_ctx, depth, opcode, error_buf, + error_buf_size)) { + goto fail; + } + + frame_csp_tmp = loader_ctx->frame_csp - depth - 1; + + *p_buf = p; + return frame_csp_tmp; +fail: + return NULL; +} + +#if WASM_ENABLE_EXCE_HANDLING != 0 +/* Returns the number of LABEL_TYPE_TRY / _CATCH / _CATCH_ALL + * frames whose END the runtime br will SKIP — i.e. the count of + * such frames at csp positions `cur_block` down to `target_block` + * inclusive (target_block included because br to a non-LOOP + * target lands AFTER target's end, skipping it; LOOP targets + * aren't try-typed so the inclusive vs exclusive distinction + * doesn't matter for them). The runtime br jumps directly to the + * target's resolved pc without decrementing `frame->eh_count`, + * so each such frame represents one stale eh-stack entry that + * survives the br. A single leaked entry is benign — frame + * allocation reserves `exception_handler_count * EH_ENTRY_CELLS` + * cells, the walker iterates top-down so sibling-try throws + * still match correctly, and the stale entry dies at frame + * teardown. But a br to a surrounding LOOP re-pushes one entry + * every iteration, eventually overflowing the static reservation; + * the resulting out-of-bounds writes go through silently in + * release builds (`bh_assert` is a no-op without `BH_DEBUG`). + * Caller logs a warning so the shape shows up in load-time + * diagnostics. */ +static uint32 +count_try_blocks_crossed(BranchBlock *cur_block, BranchBlock *target_block) +{ + BranchBlock *b; + uint32 count = 0; + for (b = cur_block; b >= target_block; b--) { + if (b->label_type == LABEL_TYPE_TRY || b->label_type == LABEL_TYPE_CATCH + || b->label_type == LABEL_TYPE_CATCH_ALL) { + count++; + } + } + return count; +} + +/* A try-crossing br only leaks one eh-stack entry harmlessly when it + * executes once. If the br (or its target) is enclosed by a LOOP that + * can re-execute it, every iteration re-pushes a TRY entry and the + * leaked entries eventually overrun the static + * `exception_handler_count * EH_ENTRY_CELLS` reservation. + * + * The direct case (br target IS a loop entry) is handled separately by + * the caller. This walks the control frames the br does NOT exit — the + * target frame `target_block` and everything below it down to the + * function frame — looking for an enclosing LOOP. Such a loop keeps the + * br live after it lands, so the per-iteration leak applies. + * + * Returns true if a try-crossing br landing at `target_block` would + * leak per loop iteration via an enclosing loop. */ +static bool +br_try_leak_in_enclosing_loop(BranchBlock *frame_csp_bottom, + BranchBlock *target_block) +{ + BranchBlock *b; + for (b = target_block; b >= frame_csp_bottom; b--) { + if (b->label_type == LABEL_TYPE_LOOP) { + return true; + } + } + return false; +} + +static BranchBlock * +check_branch_block_for_delegate(WASMLoaderContext *loader_ctx, uint8 **p_buf, + uint8 *buf_end, char *error_buf, + uint32 error_buf_size) +{ + uint8 *p = *p_buf, *p_end = buf_end; + BranchBlock *frame_csp_tmp; + uint32 depth; + + read_leb_uint32(p, p_end, depth); + /* + * Note: "delegate 0" means the surrounding block, not the + * try-delegate block itself. + * + * Note: the caller hasn't popped the try-delegate frame yet. + */ + bh_assert(loader_ctx->csp_num > 0); + if (loader_ctx->csp_num - 1 <= depth) { +#if WASM_ENABLE_SPEC_TEST == 0 + set_error_buf(error_buf, error_buf_size, "unknown delegate label"); +#else + set_error_buf(error_buf, error_buf_size, "unknown label"); +#endif + goto fail; + } + frame_csp_tmp = loader_ctx->frame_csp - depth - 2; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(frame_csp_tmp, false); +#endif + + *p_buf = p; + return frame_csp_tmp; +fail: + return NULL; +} +#endif /* end of WASM_ENABLE_EXCE_HANDLING != 0 */ + +static bool +check_block_stack(WASMLoaderContext *loader_ctx, BranchBlock *block, + char *error_buf, uint32 error_buf_size) +{ + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0; + int32 available_stack_cell, return_cell_num, i; + uint8 *frame_ref = NULL; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *frame_reftype_map; + WASMRefTypeMap *return_reftype_maps = NULL, *return_reftype_map; + WASMRefType *ref_type; + uint32 param_count, return_reftype_map_count = 0; + int32 available_reftype_map = + (int32)(loader_ctx->reftype_map_num - block->reftype_map_num); +#endif + + available_stack_cell = + (int32)(loader_ctx->stack_cell_num - block->stack_cell_num); + +#if WASM_ENABLE_GC == 0 + return_count = block_type_get_result_types(block_type, &return_types); +#else + return_count = block_type_get_result_types(block_type, &return_types, + &return_reftype_maps, + &return_reftype_map_count); + param_count = + block_type->is_value_type ? 0 : block_type->u.type->param_count; + (void)param_count; +#endif + return_cell_num = + return_count > 0 ? wasm_get_cell_num(return_types, return_count) : 0; + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (block->is_stack_polymorphic) { +#if WASM_ENABLE_GC != 0 + int32 j = (int32)return_reftype_map_count - 1; +#endif + for (i = (int32)return_count - 1; i >= 0; i--) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(return_types[i])) { + bh_assert(return_reftype_maps[j].index == i + param_count); + bh_memcpy_s( + loader_ctx->ref_type_tmp, sizeof(WASMRefType), + return_reftype_maps[j].ref_type, + wasm_reftype_struct_size(return_reftype_maps[j].ref_type)); + j--; + } +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(return_types[i]); +#endif + POP_TYPE(return_types[i]); + } + + /* Check stack is empty */ + if (loader_ctx->stack_cell_num != block->stack_cell_num) { + set_error_buf( + error_buf, error_buf_size, + "type mismatch: stack size does not match block type"); + goto fail; + } + +#if WASM_ENABLE_GC != 0 + j = 0; +#endif + for (i = 0; i < (int32)return_count; i++) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(return_types[i])) { + bh_assert(return_reftype_maps[j].index == i + param_count); + bh_memcpy_s( + loader_ctx->ref_type_tmp, sizeof(WASMRefType), + return_reftype_maps[j].ref_type, + wasm_reftype_struct_size(return_reftype_maps[j].ref_type)); + j++; + } +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(return_types[i]); +#endif + PUSH_TYPE(return_types[i]); + } + return true; + } + + if (available_stack_cell != return_cell_num) { +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* testspec: this error message format is expected by try_catch.wast */ + snprintf( + error_buf, error_buf_size, "type mismatch: %s requires [%s]%s[%s]", + block->label_type == LABEL_TYPE_TRY + || (block->label_type == LABEL_TYPE_CATCH + && return_cell_num > 0) + ? "instruction" + : "block", + return_cell_num > 0 ? type2str(return_types[0]) : "", + " but stack has ", + available_stack_cell > 0 ? type2str(*(loader_ctx->frame_ref - 1)) + : ""); + goto fail; +#else + set_error_buf(error_buf, error_buf_size, + "type mismatch: stack size does not match block type"); + goto fail; +#endif + } + + /* Check stack values match return types */ + frame_ref = loader_ctx->frame_ref; +#if WASM_ENABLE_GC != 0 + frame_reftype_map = loader_ctx->frame_reftype_map; + return_reftype_map = + return_reftype_map_count + ? return_reftype_maps + return_reftype_map_count - 1 + : NULL; +#endif + for (i = (int32)return_count - 1; i >= 0; i--) { + uint8 type = return_types[i]; +#if WASM_ENABLE_GC != 0 + bool is_type_multi_byte = wasm_is_type_multi_byte_type(type); + ref_type = NULL; + if (is_type_multi_byte) { + bh_assert(return_reftype_map); + ref_type = return_reftype_map->ref_type; + } +#endif + if (!check_stack_top_values(loader_ctx, frame_ref, available_stack_cell, +#if WASM_ENABLE_GC != 0 + frame_reftype_map, available_reftype_map, +#endif + type, +#if WASM_ENABLE_GC != 0 + ref_type, +#endif + error_buf, error_buf_size)) + return false; + frame_ref -= wasm_value_type_cell_num(return_types[i]); + available_stack_cell -= wasm_value_type_cell_num(return_types[i]); +#if WASM_ENABLE_GC != 0 + if (is_type_multi_byte) { + frame_reftype_map--; + available_reftype_map--; + return_reftype_map--; + } +#endif + } + + return true; + +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 +/* Copy parameters to dynamic space. + * 1) POP original parameter out; + * 2) Push and copy original values to dynamic space. + * The copy instruction format: + * Part a: param count + * Part b: all param total cell num + * Part c: each param's cell_num, src offset and dst offset + * Part d: each param's src offset + * Part e: each param's dst offset + */ +static bool +copy_params_to_dynamic_space(WASMLoaderContext *loader_ctx, char *error_buf, + uint32 error_buf_size) +{ + bool ret = false; + int16 *frame_offset = NULL; + uint8 *cells = NULL, cell; + int16 *src_offsets = NULL; + uint8 *emit_data = NULL; + uint32 i; + BranchBlock *block = loader_ctx->frame_csp - 1; + BlockType *block_type = &block->block_type; + WASMFuncType *wasm_type = block_type->u.type; + uint32 param_count = block_type->u.type->param_count; + int16 condition_offset = 0; + bool disable_emit = false; + bool is_if_block = (block->label_type == LABEL_TYPE_IF ? true : false); + int16 operand_offset = 0; + + uint64 size = (uint64)param_count * (sizeof(*cells) + sizeof(*src_offsets)); + bh_assert(size > 0); + + /* For if block, we also need copy the condition operand offset. */ + if (is_if_block) + size += sizeof(*cells) + sizeof(*src_offsets); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + param_count); + + if (is_if_block) + condition_offset = *loader_ctx->frame_offset; + + /* POP original parameter out */ + for (i = 0; i < param_count; i++) { + POP_OFFSET_TYPE(wasm_type->types[param_count - i - 1]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + frame_offset = loader_ctx->frame_offset; + + /* Get each param's cell num and src offset */ + for (i = 0; i < param_count; i++) { + cell = (uint8)wasm_value_type_cell_num(wasm_type->types[i]); + cells[i] = cell; + src_offsets[i] = *frame_offset; + frame_offset += cell; + } + /* emit copy instruction */ + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, is_if_block ? param_count + 1 : param_count); + /* Part b) */ + emit_uint32(loader_ctx, is_if_block ? wasm_type->param_cell_num + 1 + : wasm_type->param_cell_num); + /* Part c) */ + for (i = 0; i < param_count; i++) + emit_byte(loader_ctx, cells[i]); + if (is_if_block) + emit_byte(loader_ctx, 1); + + /* Part d) */ + for (i = 0; i < param_count; i++) + emit_operand(loader_ctx, src_offsets[i]); + if (is_if_block) + emit_operand(loader_ctx, condition_offset); + + /* Since the start offset to save the block's params and + * the start offset to save the block's results may be + * different, we remember the dynamic offset for loop block + * so that we can use it to copy the stack operands to the + * loop block's params in wasm_loader_emit_br_info. */ + if (block->label_type == LABEL_TYPE_LOOP) + block->start_dynamic_offset = loader_ctx->dynamic_offset; + + /* Part e) */ + /* Push to dynamic space. The push will emit the dst offset. */ + for (i = 0; i < param_count; i++) + PUSH_OFFSET_TYPE(wasm_type->types[i]); + if (is_if_block) + PUSH_OFFSET_TYPE(VALUE_TYPE_I32); + + ret = true; + +fail: + /* Free the emit data */ + wasm_runtime_free(emit_data); + + return ret; +} +#endif + +#if WASM_ENABLE_GC == 0 +#define RESET_REFTYPE_MAP_STACK() (void)0 +#else +#define RESET_REFTYPE_MAP_STACK() \ + do { \ + loader_ctx->reftype_map_num = \ + (loader_ctx->frame_csp - 1)->reftype_map_num; \ + loader_ctx->frame_reftype_map = loader_ctx->frame_reftype_map_bottom \ + + loader_ctx->reftype_map_num; \ + } while (0) +#endif + +/* reset the stack to the state of before entering the last block */ +#if WASM_ENABLE_FAST_INTERP != 0 +#define RESET_STACK() \ + do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + loader_ctx->frame_offset = \ + loader_ctx->frame_offset_bottom + loader_ctx->stack_cell_num; \ + RESET_REFTYPE_MAP_STACK(); \ + } while (0) +#else +#define RESET_STACK() \ + do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + RESET_REFTYPE_MAP_STACK(); \ + } while (0) +#endif + +/* set current block's stack polymorphic state */ +#define SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(flag) \ + do { \ + BranchBlock *_cur_block = loader_ctx->frame_csp - 1; \ + _cur_block->is_stack_polymorphic = flag; \ + } while (0) + +#define BLOCK_HAS_PARAM(block_type) \ + (!block_type.is_value_type && block_type.u.type->param_count > 0) + +#define PRESERVE_LOCAL_FOR_BLOCK() \ + do { \ + if (!(preserve_local_for_block(loader_ctx, opcode, error_buf, \ + error_buf_size))) { \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +static bool +get_table_elem_type(const WASMModule *module, uint32 table_idx, + uint8 *p_elem_type, void **p_ref_type, char *error_buf, + uint32 error_buf_size) +{ + if (!check_table_index(module, table_idx, error_buf, error_buf_size)) { + return false; + } + + if (table_idx < module->import_table_count) { + if (p_elem_type) + *p_elem_type = + module->import_tables[table_idx].u.table.table_type.elem_type; +#if WASM_ENABLE_GC != 0 + if (p_ref_type) + *((WASMRefType **)p_ref_type) = + module->import_tables[table_idx] + .u.table.table_type.elem_ref_type; +#endif + } + else { + if (p_elem_type) + *p_elem_type = + module->tables[table_idx - module->import_table_count] + .table_type.elem_type; +#if WASM_ENABLE_GC != 0 + if (p_ref_type) + *((WASMRefType **)p_ref_type) = + module->tables[table_idx - module->import_table_count] + .table_type.elem_ref_type; +#endif + } + return true; +} + +static bool +get_table_seg_elem_type(const WASMModule *module, uint32 table_seg_idx, + uint8 *p_elem_type, void **p_elem_ref_type, + char *error_buf, uint32 error_buf_size) +{ + if (table_seg_idx >= module->table_seg_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown elem segment %u", + table_seg_idx); + return false; + } + + if (p_elem_type) { + *p_elem_type = module->table_segments[table_seg_idx].elem_type; + } +#if WASM_ENABLE_GC != 0 + if (p_elem_ref_type) + *((WASMRefType **)p_elem_ref_type) = + module->table_segments[table_seg_idx].elem_ref_type; +#endif + return true; +} +#endif + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 +const uint8 * +wasm_loader_get_custom_section(WASMModule *module, const char *name, + uint32 *len) +{ + WASMCustomSection *section = module->custom_section_list; + + while (section) { + if ((section->name_len == strlen(name)) + && (memcmp(section->name_addr, name, section->name_len) == 0)) { + if (len) { + *len = section->content_len; + } + return section->content_addr; + } + + section = section->next; + } + + return NULL; +} +#endif + +#if 0 +#define HANDLE_OPCODE(opcode) #opcode +DEFINE_GOTO_TABLE(const char *, op_mnemonics); +#undef HANDLE_OPCODE +#endif + +#if WASM_ENABLE_FAST_INTERP == 0 + +#define pb_read_leb_uint32 read_leb_uint32 +#define pb_read_leb_int32 read_leb_int32 +#define pb_read_leb_int64 read_leb_int64 +#define pb_read_leb_memarg read_leb_memarg +#define pb_read_leb_mem_offset read_leb_mem_offset + +#else + +/* Read leb without malformed format check */ +static uint64 +read_leb_quick(uint8 **p_buf, uint32 maxbits, bool sign) +{ + uint8 *buf = *p_buf; + uint64 result = 0, byte = 0; + uint32 shift = 0; + + do { + byte = *buf++; + result |= ((byte & 0x7f) << shift); + shift += 7; + } while (byte & 0x80); + + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= (~((uint64)0)) << shift; + } + + *p_buf = buf; + return result; +} + +#define pb_read_leb_uint32(p, p_end, res) \ + do { \ + if (!loader_ctx->p_code_compiled) \ + /* Enable format check in the first scan */ \ + read_leb_uint32(p, p_end, res); \ + else \ + /* Disable format check in the second scan */ \ + res = (uint32)read_leb_quick(&p, 32, false); \ + } while (0) + +#define pb_read_leb_int32(p, p_end, res) \ + do { \ + if (!loader_ctx->p_code_compiled) \ + /* Enable format check in the first scan */ \ + read_leb_int32(p, p_end, res); \ + else \ + /* Disable format check in the second scan */ \ + res = (int32)read_leb_quick(&p, 32, true); \ + } while (0) + +#define pb_read_leb_int64(p, p_end, res) \ + do { \ + if (!loader_ctx->p_code_compiled) \ + /* Enable format check in the first scan */ \ + read_leb_int64(p, p_end, res); \ + else \ + /* Disable format check in the second scan */ \ + res = (int64)read_leb_quick(&p, 64, true); \ + } while (0) + +#if WASM_ENABLE_MULTI_MEMORY != 0 +#define pb_read_leb_memarg read_leb_memarg +#else +#define pb_read_leb_memarg pb_read_leb_uint32 +#endif + +#if WASM_ENABLE_MEMORY64 != 0 +#define pb_read_leb_mem_offset read_leb_mem_offset +#else +#define pb_read_leb_mem_offset pb_read_leb_uint32 +#endif + +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + uint32 cur_func_idx, char *error_buf, + uint32 error_buf_size) +{ + uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; + uint32 param_count, local_count, global_count; + uint8 *param_types, *local_types, local_type, global_type, mem_offset_type, + table_elem_idx_type; + BlockType func_block_type; + uint16 *local_offsets, local_offset; + uint32 type_idx, func_idx, local_idx, global_idx, table_idx; + uint32 table_seg_idx, data_seg_idx, count, align, i; + mem_offset_t mem_offset; + int32 i32_const = 0; + int64 i64_const; + uint8 opcode; + bool return_value = false; + WASMLoaderContext *loader_ctx; + BranchBlock *frame_csp_tmp; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *param_reftype_maps, *local_reftype_maps; + uint32 param_reftype_map_count, local_reftype_map_count; + int32 heap_type; + WASMRefType wasm_ref_type = { 0 }; + bool need_ref_type_map; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + int16 operand_offset = 0; + uint8 last_op = 0; + bool disable_emit, preserve_local = false, if_condition_available = true; + float32 f32_const; + float64 f64_const; + /* + * It means that the fast interpreter detected an exception while preparing, + * typically near the block opcode, but it did not immediately trigger + * the exception. The loader should be capable of identifying it near + * the end opcode and then raising the exception. + */ + bool pending_exception = false; + + LOG_OP("\nProcessing func | [%d] params | [%d] locals | [%d] return\n", + func->param_cell_num, func->local_cell_num, func->ret_cell_num); +#endif +#if WASM_ENABLE_MEMORY64 != 0 + bool is_memory64 = has_module_memory64(module); + mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32; +#else + mem_offset_type = VALUE_TYPE_I32; + table_elem_idx_type = VALUE_TYPE_I32; +#endif + uint32 memidx; + + global_count = module->import_global_count + module->global_count; + + param_count = func->func_type->param_count; + param_types = func->func_type->types; + + func_block_type.is_value_type = false; + func_block_type.u.type = func->func_type; + + local_count = func->local_count; + local_types = func->local_types; + local_offsets = func->local_offsets; + +#if WASM_ENABLE_GC != 0 + param_reftype_maps = func->func_type->ref_type_maps; + param_reftype_map_count = func->func_type->ref_type_map_count; + local_reftype_maps = func->local_ref_type_maps; + local_reftype_map_count = func->local_ref_type_map_count; +#endif + + if (!(loader_ctx = wasm_loader_ctx_init(func, error_buf, error_buf_size))) { + goto fail; + } +#if WASM_ENABLE_GC != 0 + loader_ctx->module = module; + loader_ctx->ref_type_set = module->ref_type_set; + loader_ctx->ref_type_tmp = &wasm_ref_type; +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + /* For the first traverse, the initial value of preserved_local_offset has + * not been determined, we use the INT16_MAX to represent that a slot has + * been copied to preserve space. For second traverse, this field will be + * set to the appropriate value in wasm_loader_ctx_reinit. + * This is for Issue #1230, + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/1230, the + * drop opcodes need to know which slots are preserved, so those slots will + * not be treated as dynamically allocated slots */ + loader_ctx->preserved_local_offset = INT16_MAX; + +re_scan: + if (loader_ctx->code_compiled_size > 0) { + if (!wasm_loader_ctx_reinit(loader_ctx)) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + goto fail; + } + p = func->code; + func->code_compiled = loader_ctx->p_code_compiled; + func->code_compiled_size = loader_ctx->code_compiled_size; + + if (loader_ctx->i64_const_num > 0) { + int64 *i64_consts_old = loader_ctx->i64_consts; + + /* Sort the i64 consts */ + qsort(i64_consts_old, loader_ctx->i64_const_num, sizeof(int64), + cmp_i64_const); + + /* Remove the duplicated i64 consts */ + uint32 k = 1; + for (i = 1; i < loader_ctx->i64_const_num; i++) { + if (i64_consts_old[i] != i64_consts_old[i - 1]) { + i64_consts_old[k++] = i64_consts_old[i]; + } + } + + if (k < loader_ctx->i64_const_num) { + int64 *i64_consts_new; + /* Try to reallocate memory with a smaller size */ + if ((i64_consts_new = + wasm_runtime_malloc((uint32)sizeof(int64) * k))) { + bh_memcpy_s(i64_consts_new, (uint32)sizeof(int64) * k, + i64_consts_old, (uint32)sizeof(int64) * k); + /* Free the old memory */ + wasm_runtime_free(i64_consts_old); + loader_ctx->i64_consts = i64_consts_new; + loader_ctx->i64_const_max_num = k; + } + loader_ctx->i64_const_num = k; + } + } + + if (loader_ctx->v128_const_num > 0) { + V128 *v128_consts_old = loader_ctx->v128_consts; + + /* Sort the v128 consts */ + qsort(v128_consts_old, loader_ctx->v128_const_num, sizeof(V128), + cmp_v128_const); + + /* Remove the duplicated v128 consts */ + uint32 k = 1; + for (i = 1; i < loader_ctx->v128_const_num; i++) { + if (!(memcmp(&v128_consts_old[i], &v128_consts_old[i - 1], + sizeof(V128)) + == 0)) { + v128_consts_old[k++] = v128_consts_old[i]; + } + } + + if (k < loader_ctx->v128_const_num) { + V128 *v128_consts_new; + /* Try to reallocate memory with a smaller size */ + if ((v128_consts_new = + wasm_runtime_malloc((uint32)sizeof(V128) * k))) { + bh_memcpy_s(v128_consts_new, (uint32)sizeof(V128) * k, + v128_consts_old, (uint32)sizeof(V128) * k); + /* Free the old memory */ + wasm_runtime_free(v128_consts_old); + loader_ctx->v128_consts = v128_consts_new; + loader_ctx->v128_const_max_num = k; + } + loader_ctx->v128_const_num = k; + } + } + + if (loader_ctx->i32_const_num > 0) { + int32 *i32_consts_old = loader_ctx->i32_consts; + + /* Sort the i32 consts */ + qsort(i32_consts_old, loader_ctx->i32_const_num, sizeof(int32), + cmp_i32_const); + + /* Remove the duplicated i32 consts */ + uint32 k = 1; + for (i = 1; i < loader_ctx->i32_const_num; i++) { + if (i32_consts_old[i] != i32_consts_old[i - 1]) { + i32_consts_old[k++] = i32_consts_old[i]; + } + } + + if (k < loader_ctx->i32_const_num) { + int32 *i32_consts_new; + /* Try to reallocate memory with a smaller size */ + if ((i32_consts_new = + wasm_runtime_malloc((uint32)sizeof(int32) * k))) { + bh_memcpy_s(i32_consts_new, (uint32)sizeof(int32) * k, + i32_consts_old, (uint32)sizeof(int32) * k); + /* Free the old memory */ + wasm_runtime_free(i32_consts_old); + loader_ctx->i32_consts = i32_consts_new; + loader_ctx->i32_const_max_num = k; + } + loader_ctx->i32_const_num = k; + } + } + +#if WASM_ENABLE_EXCE_HANDLING != 0 + /* The first traverse counted `func->exception_handler_count` + * try-blocks; the second traverse is about to populate one + * entry per try-block in source order. Allocate the array now + * (zero-initialized) and reset delegate_target_depth to the + * "no delegate" sentinel on every entry. */ + if (func->exception_handler_count > 0) { + uint64 eh_size = + (uint64)sizeof(WASMFastEHEntry) * func->exception_handler_count; + uint32 eh_i; + if (!(func->exception_handlers = + loader_malloc(eh_size, error_buf, error_buf_size))) { + goto fail; + } + for (eh_i = 0; eh_i < func->exception_handler_count; eh_i++) { + func->exception_handlers[eh_i].delegate_target_depth = + UINT32_MAX; + } + } +#endif + } +#endif + + PUSH_CSP(LABEL_TYPE_FUNCTION, func_block_type, p); + + while (p < p_end) { + opcode = *p++; +#if WASM_ENABLE_FAST_INTERP != 0 + p_org = p; + disable_emit = false; + emit_label(opcode); +#endif + switch (opcode) { + case WASM_OP_UNREACHABLE: + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + + case WASM_OP_NOP: +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + break; + + case WASM_OP_IF: + { +#if WASM_ENABLE_FAST_INTERP != 0 + BranchBlock *parent_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - parent_block->stack_cell_num); + + if (available_stack_cell <= 0 + && parent_block->is_stack_polymorphic) + if_condition_available = false; + else + if_condition_available = true; + + PRESERVE_LOCAL_FOR_BLOCK(); +#endif +#if WASM_ENABLE_GC == 0 + POP_I32(); +#endif + goto handle_op_block_and_loop; + } + case WASM_OP_BLOCK: + case WASM_OP_LOOP: +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + if (opcode == WASM_OP_TRY) { +#if WASM_ENABLE_FAST_INTERP != 0 + /* Two-traverse loader: the first traverse counts + * try-blocks into func->exception_handler_count so + * the second traverse can allocate the per-function + * exception_handlers[] table (see re_scan block). */ + if (loader_ctx->p_code_compiled == NULL) + func->exception_handler_count++; +#else + /* Single-traverse classic-interp / shared loader. */ + func->exception_handler_count++; +#endif + + /* + * try is a block + * do nothing special, but execution continues to + * to handle_op_block_and_loop, + * and that be pushes the csp + */ + } + +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + PRESERVE_LOCAL_FOR_BLOCK(); +#endif + handle_op_block_and_loop: + { + uint8 value_type; + BlockType block_type; +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 available_params = 0; +#endif + + CHECK_BUF(p, p_end, 1); + value_type = read_uint8(p); + if (is_byte_a_type(value_type)) { + /* If the first byte is one of these special values: + * 0x40/0x7F/0x7E/0x7D/0x7C, take it as the type of + * the single return value. */ + block_type.is_value_type = true; + block_type.u.value_type.type = value_type; +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (value_type == VALUE_TYPE_V128) + module->is_simd_used = true; + else if (value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF) + module->is_ref_types_used = true; +#endif +#if WASM_ENABLE_GC != 0 + if (value_type != VALUE_TYPE_VOID) { + p_org = p; + p--; + if (!resolve_value_type((const uint8 **)&p, p_end, + module, module->type_count, + &need_ref_type_map, + &wasm_ref_type, false, + error_buf, error_buf_size)) { + goto fail; + } + if (need_ref_type_map) { + block_type.u.value_type.ref_type_map.index = 0; + if (!(block_type.u.value_type.ref_type_map + .ref_type = reftype_set_insert( + module->ref_type_set, &wasm_ref_type, + error_buf, error_buf_size))) { + goto fail; + } + } + /* Set again as the type might be changed, e.g. + (ref null any) to anyref */ + block_type.u.value_type.type = wasm_ref_type.ref_type; +#if WASM_ENABLE_FAST_INTERP == 0 + while (p_org < p) { +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_org, *p_org, + error_buf, error_buf_size)) { + goto fail; + } +#endif + /* Ignore extra bytes for interpreter */ + *p_org++ = WASM_OP_NOP; + } +#endif + } +#endif /* end of WASM_ENABLE_GC != 0 */ + } + else { + int32 type_index; + + /* Resolve the leb128 encoded type index as block type */ + p--; + p_org = p - 1; + pb_read_leb_int32(p, p_end, type_index); + + if (!check_function_type(module, type_index, error_buf, + error_buf_size)) { + goto fail; + } + + block_type.is_value_type = false; + block_type.u.type = + (WASMFuncType *)module->types[type_index]; +#if WASM_ENABLE_FAST_INTERP == 0 + /* If block use type index as block type, change the opcode + * to new extended opcode so that interpreter can resolve + * the block quickly. + */ +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_org, *p_org, error_buf, + error_buf_size)) { + goto fail; + } +#endif + *p_org = EXT_OP_BLOCK + (opcode - WASM_OP_BLOCK); +#endif + } + +#if WASM_ENABLE_GC != 0 + if (opcode == WASM_OP_IF) { + POP_I32(); + } +#endif + + /* Pop block parameters from stack */ + if (BLOCK_HAS_PARAM(block_type)) { + WASMFuncType *wasm_type = block_type.u.type; + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; + uint32 j = 0; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 cell_num; + available_params = block_type.u.type->param_count; +#endif +#if WASM_ENABLE_GC != 0 + /* find the index of the last param + * in wasm_type->ref_type_maps as j */ + for (i = 0; i < block_type.u.type->param_count; i++) { + if (wasm_is_type_multi_byte_type(wasm_type->types[i])) { + j += 1; + } + } + if (j > 0) { + j -= 1; + } +#endif + for (i = 0; i < block_type.u.type->param_count; i++) { + + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + if (available_stack_cell <= 0 + && cur_block->is_stack_polymorphic) { +#if WASM_ENABLE_FAST_INTERP != 0 + available_params = i; +#endif + break; + } +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type( + wasm_type + ->types[wasm_type->param_count - i - 1])) { + bh_assert(wasm_type->ref_type_maps[j].index + == wasm_type->param_count - i - 1); + ref_type = wasm_type->ref_type_maps[j].ref_type; + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + j--; + } +#endif + + uint8 *frame_ref_before_pop = loader_ctx->frame_ref; + POP_TYPE( + wasm_type->types[wasm_type->param_count - i - 1]); +#if WASM_ENABLE_FAST_INTERP != 0 + /* decrease the frame_offset pointer accordingly to keep + * consistent with frame_ref stack. Use the actual + * popped cell count instead of + * wasm_value_type_cell_num() because when the stack top + * is VALUE_TYPE_ANY, wasm_loader_pop_frame_ref always + * pops exactly 1 cell regardless of the expected type + */ + cell_num = (uint32)(frame_ref_before_pop + - loader_ctx->frame_ref); + loader_ctx->frame_offset -= cell_num; + + if (loader_ctx->frame_offset + < loader_ctx->frame_offset_bottom) { + LOG_DEBUG( + "frame_offset underflow, roll back and " + "let following stack checker report it\n"); + loader_ctx->frame_offset += cell_num; + pending_exception = true; + break; + } +#endif + } + } + PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK), + block_type, p); + + /* Pass parameters to block */ + if (BLOCK_HAS_PARAM(block_type)) { + WASMFuncType *func_type = block_type.u.type; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; + uint32 j = 0; +#endif + for (i = 0; i < func_type->param_count; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 cell_num = + wasm_value_type_cell_num(func_type->types[i]); + if (i >= available_params) { + /* make sure enough space */ + if (loader_ctx->p_code_compiled == NULL) { + loader_ctx->frame_offset += cell_num; + if (!check_offset_push(loader_ctx, error_buf, + error_buf_size)) + goto fail; + /* for following dummy value assignment */ + loader_ctx->frame_offset -= cell_num; + } + + /* If there isn't enough data on stack, push a dummy + * offset to keep the stack consistent with + * frame_ref. + * Since the stack is already in polymorphic state, + * the opcode will not be executed, so the dummy + * offset won't cause any error */ + for (uint32 n = 0; n < cell_num; n++) { + *loader_ctx->frame_offset++ = 0; + } + } + else { + loader_ctx->frame_offset += cell_num; + } +#endif +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(func_type->types[i])) { + bh_assert(func_type->ref_type_maps[j].index == i); + ref_type = func_type->ref_type_maps[j].ref_type; + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + j++; + } +#endif + PUSH_TYPE(func_type->types[i]); + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (opcode == WASM_OP_BLOCK || opcode == WASM_OP_LOOP) { + skip_label(); + + if (BLOCK_HAS_PARAM(block_type)) { + /* Make sure params are in dynamic space */ + if (!copy_params_to_dynamic_space(loader_ctx, error_buf, + error_buf_size)) + goto fail; + } + + if (opcode == WASM_OP_LOOP) { + (loader_ctx->frame_csp - 1)->code_compiled = + loader_ctx->p_code_compiled; + } + } +#if WASM_ENABLE_EXCE_HANDLING != 0 + else if (opcode == WASM_OP_TRY) { + /* The auto-emit_label at the top of the dispatch + * loop already wrote the WASM_OP_TRY byte into the + * rewritten IR; the runtime handler for that + * opcode (HANDLE_OP(WASM_OP_TRY) in + * wasm_interp_fast.c) reads the uint32 eh_idx + * immediate we emit below and pushes one entry + * onto the per-frame eh-stack. Unlike BLOCK / LOOP, + * we keep the opcode in the IR — its runtime + * effect (push) is what makes throws find the + * right catches. */ + bh_assert(loader_ctx->cur_eh_entry_idx + < func->exception_handler_count); + (loader_ctx->frame_csp - 1)->eh_entry_idx = + loader_ctx->cur_eh_entry_idx; + emit_uint32(loader_ctx, loader_ctx->cur_eh_entry_idx); + loader_ctx->cur_eh_entry_idx++; + } +#endif + else if (opcode == WASM_OP_IF) { + BranchBlock *block = loader_ctx->frame_csp - 1; + /* If block has parameters, we should make sure they are in + * dynamic space. Otherwise, when else branch is missing, + * the later opcode may consume incorrect operand offset. + * Spec case: + * (func (export "params-id") (param i32) (result i32) + * (i32.const 1) + * (i32.const 2) + * (if (param i32 i32) (result i32 i32) (local.get 0) + * (then)) (i32.add) + * ) + * + * So we should emit a copy instruction before the if. + * + * And we also need to save the parameter offsets and + * recover them before entering else branch. + * + */ + if (BLOCK_HAS_PARAM(block_type)) { + uint64 size; + + /* In polymorphic state, there may be no if condition on + * the stack, so the offset may not emitted */ + if (if_condition_available) { + /* skip the if condition operand offset */ + wasm_loader_emit_backspace(loader_ctx, + sizeof(int16)); + } + /* skip the if label */ + skip_label(); + /* Emit a copy instruction */ + if (!copy_params_to_dynamic_space(loader_ctx, error_buf, + error_buf_size)) + goto fail; + + /* Emit the if instruction */ + emit_label(opcode); + /* Emit the new condition operand offset */ + POP_OFFSET_TYPE(VALUE_TYPE_I32); + + /* Save top param_count values of frame_offset stack, so + * that we can recover it before executing else branch + */ + size = sizeof(int16) + * (uint64)block_type.u.type->param_cell_num; + if (!(block->param_frame_offsets = loader_malloc( + size, error_buf, error_buf_size))) + goto fail; + bh_memcpy_s(block->param_frame_offsets, (uint32)size, + loader_ctx->frame_offset + - size / sizeof(int16), + (uint32)size); + } + + block->start_dynamic_offset = loader_ctx->dynamic_offset; + + emit_empty_label_addr_and_frame_ip(PATCH_ELSE); + emit_empty_label_addr_and_frame_ip(PATCH_END); + } +#endif + break; + } +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_THROW: + { + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + uint8 label_type = cur_block->label_type; + uint32 tag_index = 0; + pb_read_leb_int32(p, p_end, tag_index); + + /* check validity of tag_index against module->tag_count */ + /* check tag index is within the tag index space */ + if (tag_index >= module->import_tag_count + module->tag_count) { + snprintf(error_buf, error_buf_size, "unknown tag %d", + tag_index); + goto fail; + } + + /* the tag_type is stored in either the WASMTag (section tags) + * or WASMTagImport (import tag) */ + WASMFuncType *tag_type = NULL; + if (tag_index < module->import_tag_count) { + tag_type = module->import_tags[tag_index].u.tag.tag_type; + } + else { + tag_type = + module->tags[tag_index - module->import_tag_count] + ->tag_type; + } + + if (tag_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Fast-interp THROW IR shape (emitted in BOTH traverses + * so pass-1 / pass-2 size accounting stays balanced): + * + * + * + * + * ... + * + * Where `param_cell_num` is the sum across all params' + * cell widths (i32 = 1, i64 = 2, v128 = 4, etc.) and + * src_offset_i is the throw-site's frame_lp slot for + * the i-th payload cell, read directly off the top of + * `loader_ctx->frame_offset[]`. The validation loop + * below pops frame_ref / available_stack_cell but + * doesn't touch frame_offset, so the src offsets are + * stable to read here. They get consumed at runtime + * by find_a_catch_handler when a *same-function* + * catch matches: it copies `param_cell_num` cells + * from frame_lp[src_offset_i] into the catch body's + * `param_dst_offsets[i]` slots before jumping to + * handler_pc. + * + * Cross-function dispatch (callee throws, caller's + * catch fires after return_func unwinds) does NOT + * preserve the payload — the source slots live in a + * frame that's about to be torn down. That gap is + * documented as an ignored integration test, in line + * with the cost-model rule that EH must not tax hot + * ops: a per-thread payload buffer would force every + * CALL / RETURN handler to spill scratch state across + * the boundary, which we explicitly refuse. + * + * Tag-without-params is the common case (Porffor + * emits empty payloads; many spec tests use bare + * tags too). param_cell_num=0 makes the for-loop + * trivial and the resulting IR is just the tag_index + * + a single zero — same hot-path cost as the + * pre-tag-with-params shape, since the runtime + * read_uint32 of param_cell_num happens on the cold + * THROW handler. */ + emit_uint32(loader_ctx, tag_index); + emit_uint32(loader_ctx, tag_type->param_cell_num); + { + /* Multi-cell types (i64, f64, v128) only have a + * meaningful first-cell offset in + * `frame_offset[]` — subsequent cells of the + * same value are left uninitialized by + * `wasm_loader_push_frame_offset` (it just + * advances the pointer without writing). For + * each param walk the per-param first cell out + * of frame_offset and synthesize consecutive + * cell offsets `(first, first+1, ...)`; that + * matches the runtime invariant that an n-cell + * value occupies n consecutive frame_lp cells. + * + * In stack-polymorphic code (after an + * `unreachable`, e.g. `unreachable; throw $tag`) + * the offset stack does NOT actually hold the + * tag's params, so reading + * `frame_offset - param_cell_num` would underflow + * the offset stack. The validation loop below + * tolerates the missing values via the same + * polymorphic short-circuit used by the + * block-param path; mirror it here by emitting + * zeroed placeholder source offsets, which keeps + * pass-1 / pass-2 size accounting balanced while + * avoiding the underflow (this THROW is + * unreachable at runtime anyway). */ + uint32 pi, c, cell_so_far = 0; + int32 throw_avail_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + bool stack_has_params = + !(throw_avail_cell < (int32)tag_type->param_cell_num + && cur_block->is_stack_polymorphic); + int16 *base = + loader_ctx->frame_offset - tag_type->param_cell_num; + for (pi = 0; pi < tag_type->param_count; pi++) { + uint32 this_cells = + wasm_value_type_cell_num(tag_type->types[pi]); + int16 first_slot = + stack_has_params ? base[cell_so_far] : 0; + for (c = 0; c < this_cells; c++) { + emit_operand( + loader_ctx, + stack_has_params ? (int16)(first_slot + c) : 0); + } + cell_so_far += this_cells; + } + } +#endif + + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + int32 tti; + + /* Check stack values match return types by comparing tag param + * types with stack cells */ + uint8 *frame_ref = loader_ctx->frame_ref; +#if WASM_ENABLE_GC != 0 + WASMRefTypeMap *frame_reftype_map = + loader_ctx->frame_reftype_map; + uint32 frame_reftype_map_num = loader_ctx->reftype_map_num; + + /* Temporarily set these values since they may be used in + GET_LOCAL_REFTYPE(), remember they must be restored later */ + param_reftype_maps = tag_type->ref_type_maps; + /* For tag_type function, it shouldn't have result_count = 0 */ + param_reftype_map_count = tag_type->ref_type_map_count; + param_count = tag_type->param_count; +#endif + + for (tti = (int32)tag_type->param_count - 1; tti >= 0; tti--) { +#if WASM_ENABLE_GC != 0 + local_type = tag_type->types[tti]; + local_idx = tti; + /* Get the wasm_ref_type if the local_type is multibyte + type */ + GET_LOCAL_REFTYPE(); +#endif + + if (!check_stack_top_values( + loader_ctx, frame_ref, available_stack_cell, +#if WASM_ENABLE_GC != 0 + frame_reftype_map, frame_reftype_map_num, +#endif + tag_type->types[tti], +#if WASM_ENABLE_GC != 0 + &wasm_ref_type, +#endif + error_buf, error_buf_size)) { + snprintf(error_buf, error_buf_size, + "type mismatch: instruction requires [%s] but " + "stack has [%s]", + tag_type->param_count > 0 + ? type2str(tag_type->types[tti]) + : "", + available_stack_cell > 0 + ? type2str(*(loader_ctx->frame_ref - 1)) + : ""); + goto fail; + } + frame_ref -= wasm_value_type_cell_num(tag_type->types[tti]); + available_stack_cell -= + wasm_value_type_cell_num(tag_type->types[tti]); + } + +#if WASM_ENABLE_GC != 0 + /* Restore the values */ + param_reftype_maps = func->func_type->ref_type_maps; + param_reftype_map_count = func->func_type->ref_type_map_count; + param_count = func->func_type->param_count; +#endif + + /* throw is stack polymorphic */ + (void)label_type; + RESET_STACK(); + + break; + } + case WASM_OP_RETHROW: + { + /* must be done before reading the depth */ + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + /* Manual depth + label-type validation. We deliberately + * skip the shared `check_branch_block` here because + * RETHROW doesn't *branch* to its target — it walks + * the eh-stack at runtime and re-raises — so the + * branch-info bytes that check_branch_block / + * emit_br_info would write between the auto-emitted + * opcode label and our depth immediate are dead + * weight (4 bytes arity + 8 bytes target ptr + + * arity-dependent operand-offsets, all unread by the + * runtime walker). Worse, leaving them in the IR + * shifts our depth immediate past where the runtime + * read_uint32(frame_ip) looks for it. */ + { + uint32 rethrow_depth = 0; + BranchBlock *target_block; + pb_read_leb_uint32(p, p_end, rethrow_depth); + if (rethrow_depth + 1 > loader_ctx->csp_num) { +#if WASM_ENABLE_SPEC_TEST == 0 + set_error_buf(error_buf, error_buf_size, + "unknown rethrow label"); +#else + set_error_buf(error_buf, error_buf_size, + "unknown label"); +#endif + goto fail; + } + target_block = loader_ctx->frame_csp - rethrow_depth - 1; + if (target_block->label_type != LABEL_TYPE_CATCH + && target_block->label_type != LABEL_TYPE_CATCH_ALL) { + /* trap according to spectest (rethrow.wast) */ + set_error_buf(error_buf, error_buf_size, + "invalid rethrow label"); + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + /* Emit the depth as a uint32 immediate after the + * auto-emitted RETHROW opcode. Pass 1's size + * accounting must match pass 2's actual emit so + * we run this branch in both traverses. */ + emit_uint32(loader_ctx, rethrow_depth); +#endif + } + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + uint8 label_type = cur_block->label_type; + (void)label_type; + /* rethrow is stack polymorphic */ + RESET_STACK(); + break; + } + case WASM_OP_DELEGATE: + { + /* Manual depth + label-type validation. Like RETHROW + * (above), we deliberately skip the shared + * `check_branch_block_for_delegate` here because: + * (1) DELEGATE doesn't *branch* to its target at + * runtime — when the try-body throws, the + * find_a_catch_handler walker reads the precomputed + * `delegate_target_depth` off the eh-table entry + * and skips the right number of nested-try entries + * on the per-frame eh-stack. The branch-info bytes + * that `emit_br_info` would write between the + * auto-emitted DELEGATE label and any subsequent + * operand are dead weight (4 bytes arity + 8 bytes + * target ptr, all unread by either the runtime + * DELEGATE handler or the throw walker). + * (2) Worse, leaving them in the IR shifts any + * immediate we *do* want to emit past where the + * runtime reads it — same gotcha that bit + * RETHROW. + * + * `delegate N` targets the (N+1)-th block out from the + * current try-delegate frame. The try-delegate itself + * still sits on the loader's csp stack at this point + * (POP_CSP is called below), so the target is at + * frame_csp - N - 2 + * and the spec rejects `delegate N` whose N+1 would + * climb past the function frame. */ + uint32 delegate_depth = 0; + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + BranchBlock *target_block; + uint8 label_type = cur_block->label_type; + (void)label_type; + + pb_read_leb_uint32(p, p_end, delegate_depth); + bh_assert(loader_ctx->csp_num > 0); + if (loader_ctx->csp_num - 1 <= delegate_depth) { +#if WASM_ENABLE_SPEC_TEST == 0 + set_error_buf(error_buf, error_buf_size, + "unknown delegate label"); +#else + set_error_buf(error_buf, error_buf_size, "unknown label"); +#endif + goto fail; + } + target_block = loader_ctx->frame_csp - delegate_depth - 2; + (void)target_block; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Second traverse only: populate the eh-table entry so + * the runtime walker can dispatch through it. + * + * delegate_target_depth = (count of try / catch / + * catch_all blocks STRICTLY between cur_block and + * target_block on the loader's csp stack) + * + * At runtime those `delta` blocks are exactly the + * eh-stack entries immediately below the delegate's own + * entry that the throw walker must SKIP — the spec + * re-raises the exception "at the target block's + * location", so any try whose body the delegate's try + * is nested inside (but the target is also inside) + * doesn't get to catch it. + * + * end_of_region_pc still gets set to the IR pc just + * after the auto-emitted DELEGATE label. The walker + * never reads it for delegate entries (it forwards via + * delta instead), but a future DELEGATE-end runtime + * handler that wanted to advance frame_ip past the + * region could use it; recording it keeps the + * shape identical to the END(try) capture and the + * field semantics easy to reason about. */ + if (loader_ctx->p_code_compiled != NULL) { + uint32 eh_idx = cur_block->eh_entry_idx; + uint32 delta = 0; + BranchBlock *b; + bh_assert(eh_idx < func->exception_handler_count); + bh_assert(func->exception_handlers != NULL); + for (b = cur_block - 1; b > target_block; b--) { + if (b->label_type == LABEL_TYPE_TRY + || b->label_type == LABEL_TYPE_CATCH + || b->label_type == LABEL_TYPE_CATCH_ALL) { + delta++; + } + } + func->exception_handlers[eh_idx].delegate_target_depth = + delta; + func->exception_handlers[eh_idx].end_of_region_pc = + loader_ctx->p_code_compiled; + } +#endif + /* DELEGATE ends the block */ + POP_CSP(); + break; + } + case WASM_OP_CATCH: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + uint8 label_type = cur_block->label_type; + uint32 tag_index = 0; + pb_read_leb_int32(p, p_end, tag_index); + + /* check validity of tag_index against module->tag_count */ + /* check tag index is within the tag index space */ + if (tag_index >= module->import_tag_count + module->tag_count) { + LOG_VERBOSE("In %s, unknown tag at WASM_OP_CATCH\n", + __FUNCTION__); + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } + + /* the tag_type is stored in either the WASMTag (section tags) + * or WASMTagImport (import tag) */ + WASMFuncType *func_type = NULL; + if (tag_index < module->import_tag_count) { + func_type = module->import_tags[tag_index].u.tag.tag_type; + } + else { + func_type = + module->tags[tag_index - module->import_tag_count] + ->tag_type; + } + + if (func_type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, + "tag type signature does not return void"); + goto fail; + } + + /* check validity of current label (expect LABEL_TYPE_TRY or + * LABEL_TYPE_CATCH) */ + if ((LABEL_TYPE_CATCH != label_type) + && (LABEL_TYPE_TRY != label_type)) { + set_error_buf(error_buf, error_buf_size, + "Unexpected block sequence encountered."); + goto fail; + } + + /* Validate previous body's stack (try body on first + * CATCH, previous catch body on subsequent CATCH) + * matches the block's result type. Without this the + * loader would silently accept stack-shape mismatches + * between the try body and the catch bodies and the + * next op would read garbage. Same pattern as ELSE + * runs `check_block_stack` on the if-body before the + * else body's PUSH_TYPE sequence. */ + if (!check_block_stack(loader_ctx, cur_block, error_buf, + error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* For result-typed try-regions, inject a COPY of the + * previous body's last value(s) into the block's + * `dynamic_offset` slot BEFORE the auto-emitted CATCH + * label. The normal-flow CATCH dispatch jumps from + * here to `end_of_region_pc` — the body's value would + * otherwise be lost. Mirrors how `reserve_block_ret` + * + `case WASM_OP_ELSE` align the if-body's result + * for the else-body's END to read. Layout becomes: + * + * [previous body ops...] + * [EXT_OP_COPY_STACK_TOP src=prev_top dst=dyn_off] + * [CATCH label][eh_idx][dst-slots from PUSH...] + * [catch body ops...] + * + * The `src != dst` check runs in BOTH traverses so + * pass-1 size accounting matches pass-2 writes: + * `dynamic_offset` evolves identically in both + * passes, and although const-pool slots get + * renumbered between passes by the qsort/dedup at + * the start of pass 2, they stay strictly negative + * (offsets `-(count)..-1`) while `dynamic_offset` is + * strictly non-negative (`>= start_dynamic_offset = + * param_cell_num + local_cell_num`). So the + * predicate is sign-stable across passes. + * + * Multi-return-value try-regions need + * `EXT_OP_COPY_STACK_VALUES`; we error out + * explicitly until a follow-up commit lifts that + * restriction. Single-return covers every shape + * Porffor / AS / our integration tests emit. */ + { + uint8 *return_types = NULL; +#if WASM_ENABLE_GC == 0 + uint32 return_count = block_type_get_result_types( + &cur_block->block_type, &return_types); +#else + WASMRefTypeMap *return_reftype_maps = NULL; + uint32 return_reftype_map_count = 0; + uint32 return_count = block_type_get_result_types( + &cur_block->block_type, &return_types, + &return_reftype_maps, &return_reftype_map_count); +#endif + if (return_count == 1) { + uint8 cell = + (uint8)wasm_value_type_cell_num(return_types[0]); + int16 src = *(loader_ctx->frame_offset - cell); + int16 dst = cur_block->dynamic_offset; + if (src != dst) { + skip_label(); +#if WASM_ENABLE_SIMDE != 0 + if (cell == 4) { + emit_label(EXT_OP_COPY_STACK_TOP_V128); + } + else +#endif + if (cell == 2) { + emit_label(EXT_OP_COPY_STACK_TOP_I64); + } + else { + emit_label(EXT_OP_COPY_STACK_TOP); + } + emit_operand(loader_ctx, src); + emit_operand(loader_ctx, dst); + emit_label(opcode); + } + } + else if (return_count > 1) { + set_error_buf(error_buf, error_buf_size, + "multi-return try-region not " + "supported in fast interpreter"); + goto fail; + } + } + + /* Emit `` after the auto-emitted CATCH + * opcode. The runtime CATCH handler reads it to find + * end_of_region_pc when the catch is reached via + * normal flow. Emitted in BOTH traverses so pass 1's + * size measurement and pass 2's actual writes match; + * if this were inside the populate guard below, + * pass 2 would overrun the code_compiled buffer by + * sizeof(uint32) bytes per catch, corrupting whatever + * loader allocation the heap placed immediately after + * (typically func->exception_handlers itself). */ + emit_uint32(loader_ctx, cur_block->eh_entry_idx); +#endif + + /* + * replace frame_csp by LABEL_TYPE_CATCH + */ + cur_block->label_type = LABEL_TYPE_CATCH; + + /* RESET_STACK removes the values pushed in TRY or previous + * CATCH Blocks */ + RESET_STACK(); + /* Reset the polymorphic flag the way `WASM_OP_ELSE` + * does: the catch body is a freshly-reachable region, + * not a continuation of the (dead) try body after a + * throw. Without this reset, the catch body's END + * runs `check_block_stack` in polymorphic mode, which + * emits a `POP_OFFSET_TYPE` operand byte for each + * return-cell — those bytes land between the auto- + * emitted END label and the case body's + * `skip_label()`, shifting the re-emitted END label + * forward by `2 * return_cell_num` bytes and leaving + * a corrupt handler-ptr at the originally-recorded + * `handler_pc`. (The same bug latent in non-EH + * polymorphic blocks doesn't bite because their END + * gets stripped from the IR entirely; the EH path's + * runtime needs the END opcode to actually exist for + * the eh-stack pop.) */ + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; + uint32 j = 0; +#endif + + /* Push the tag's params onto the catch body's operand + * stack. Classic-interp uses PUSH_TYPE (which only + * touches the value-type stack used by validation); + * fast-interp also needs `PUSH_OFFSET_TYPE`, which + * allocates fresh `dynamic_offset` slots for each cell + * (and emits the slot offsets as `int16` operands in + * the IR right after the eh_idx). The catch body's + * downstream ops then `POP_OFFSET_TYPE` to consume + * these slots — same shape the loader uses for + * block-with-params (see `copy_params_to_dynamic_ + * space`). + * + * Note: the emitted dst slots are *unused* by the + * runtime CATCH normal-flow handler (it only reads + * eh_idx and branches to end_of_region_pc) — they + * sit in the IR as dead bytes on the fall-through + * path. The throw walker doesn't read them either; + * it consults the pre-decoded copy on + * `WASMFastEHCatch.param_dst_offsets` (populated + * below). They're emitted only so PUSH_OFFSET_TYPE's + * pass-1 / pass-2 size accounting stays balanced and + * the catch body's POP_OFFSET_TYPEs find the right + * slot offsets in `frame_offset[]`. */ + for (i = 0; i < func_type->param_count; i++) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(func_type->types[i])) { + bh_assert(func_type->ref_type_maps[j].index == i); + ref_type = func_type->ref_type_maps[j].ref_type; + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + j++; + } +#endif + /* Allocate a fresh `dynamic_offset` slot for the + * catch param AND push its type onto `frame_ref` + * (so `stack_cell_num` stays balanced). One + * without the other doesn't work: a bare + * `PUSH_OFFSET_TYPE` leaves the offset side + * ahead of the ref side, so the catch body's + * first consumer (e.g. `global.set $g`) hits + * `wasm_loader_pop_frame_offset`'s polymorphic + * short-circuit — the CATCH block inherits the + * polymorphic flag from THROW's + * `SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE`, and + * with `available_stack_cell == 0` the pop + * silently returns without emitting the source + * slot. The consumer's runtime read then lands + * on heap garbage and crashes with SIGBUS / + * SIGSEGV. PUSH_TYPE rebalances and avoids + * the short-circuit so the catch body's pops + * emit real source-slot operand bytes. */ +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(func_type->types[i]); +#endif + PUSH_TYPE(func_type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Second traverse only: append a fully-populated + * `WASMFastEHCatch` entry to the parent try-region's + * catches[]. handler_pc is captured *after* the + * PUSH_OFFSET_TYPE emits above so it points at the + * first rewritten-IR byte of the catch body proper + * (skipping the dead dst-slot bytes). param_cell_num + * is the sum of cells across all tag params (i32 = 1 + * cell, i64 = 2, v128 = 4); param_dst_offsets is a + * loader-owned copy of the int16 slot offsets just + * pushed onto frame_offset[]. NULL when the tag has + * no params (the typical Porffor shape). */ + if (loader_ctx->p_code_compiled != NULL) { + uint32 eh_idx = cur_block->eh_entry_idx; + WASMFastEHEntry *entry; + WASMFastEHCatch *new_catches; + uint64 new_size; + bh_assert(eh_idx < func->exception_handler_count); + bh_assert(func->exception_handlers != NULL); + entry = &func->exception_handlers[eh_idx]; + new_size = (uint64)sizeof(WASMFastEHCatch) + * (entry->catch_count + 1); + if (!(new_catches = loader_malloc(new_size, error_buf, + error_buf_size))) { + goto fail; + } + if (entry->catches) { + bh_memcpy_s(new_catches, (uint32)new_size, + entry->catches, + (uint32)sizeof(WASMFastEHCatch) + * entry->catch_count); + wasm_runtime_free(entry->catches); + } + new_catches[entry->catch_count].tag_index = tag_index; + new_catches[entry->catch_count].handler_pc = + loader_ctx->p_code_compiled; + new_catches[entry->catch_count].param_cell_num = + func_type->param_cell_num; + new_catches[entry->catch_count].param_dst_offsets = NULL; + if (func_type->param_cell_num > 0) { + uint64 dst_size = + (uint64)sizeof(int16) * func_type->param_cell_num; + int16 *dst; + uint32 pi, c, cell_so_far = 0; + int16 *base; + if (!(dst = loader_malloc(dst_size, error_buf, + error_buf_size))) { + wasm_runtime_free(new_catches); + goto fail; + } + /* Synthesize per-cell dst offsets from each + * param's first cell. Same multi-cell shape + * concern as the THROW src emit: + * `wasm_loader_push_frame_offset` writes a + * meaningful int16 only for the first cell + * of a multi-cell value (i64 / f64 / v128); + * subsequent cells of the same value have + * unspecified frame_offset entries. The + * runtime walker copies one frame_lp cell + * per iteration, so its `param_cell_num` + * loop needs an offset array indexed by + * absolute cell number, not by frame_offset + * position. Build that here by walking + * params and synthesizing `(first, first+1, + * ..., first+param_cells-1)` for each one. */ + base = loader_ctx->frame_offset + - func_type->param_cell_num; + for (pi = 0; pi < func_type->param_count; pi++) { + uint32 this_cells = + wasm_value_type_cell_num(func_type->types[pi]); + int16 first_slot = base[cell_so_far]; + for (c = 0; c < this_cells; c++) { + dst[cell_so_far + c] = (int16)(first_slot + c); + } + cell_so_far += this_cells; + } + new_catches[entry->catch_count].param_dst_offsets = dst; + } + entry->catches = new_catches; + entry->catch_count++; + } +#endif + break; + } + case WASM_OP_CATCH_ALL: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + /* expecting a TRY or CATCH, anything else will be considered an + * error */ + if ((LABEL_TYPE_CATCH != cur_block->label_type) + && (LABEL_TYPE_TRY != cur_block->label_type)) { + set_error_buf(error_buf, error_buf_size, + "Unexpected block sequence encountered."); + goto fail; + } + + /* Same previous-body-stack validation as in CATCH. */ + if (!check_block_stack(loader_ctx, cur_block, error_buf, + error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Same COPY-to-block-dynamic_offset shape as CATCH + * (see the long comment in the CATCH case for the + * rationale and pass-1/pass-2 alignment argument). + * catch_all is the only place the body-COPY can run + * for a try with a result-type and only a catch_all, + * so without this emit a result-typed + * `try (result T) ... catch_all` would lose the try + * body's value on the normal-flow path. */ + { + uint8 *return_types = NULL; +#if WASM_ENABLE_GC == 0 + uint32 return_count = block_type_get_result_types( + &cur_block->block_type, &return_types); +#else + WASMRefTypeMap *return_reftype_maps = NULL; + uint32 return_reftype_map_count = 0; + uint32 return_count = block_type_get_result_types( + &cur_block->block_type, &return_types, + &return_reftype_maps, &return_reftype_map_count); +#endif + if (return_count == 1) { + uint8 cell = + (uint8)wasm_value_type_cell_num(return_types[0]); + int16 src = *(loader_ctx->frame_offset - cell); + int16 dst = cur_block->dynamic_offset; + if (src != dst) { + skip_label(); +#if WASM_ENABLE_SIMDE != 0 + if (cell == 4) { + emit_label(EXT_OP_COPY_STACK_TOP_V128); + } + else +#endif + if (cell == 2) { + emit_label(EXT_OP_COPY_STACK_TOP_I64); + } + else { + emit_label(EXT_OP_COPY_STACK_TOP); + } + emit_operand(loader_ctx, src); + emit_operand(loader_ctx, dst); + emit_label(opcode); + } + } + else if (return_count > 1) { + set_error_buf(error_buf, error_buf_size, + "multi-return try-region not " + "supported in fast interpreter"); + goto fail; + } + } + + /* Emit `` after the auto-emitted CATCH_ALL + * opcode in BOTH traverses (pass 1's size accounting + * must include this or pass 2 overruns + * code_compiled). Pass 2 additionally records + * catch_all_pc on the parent try-region — set exactly + * once per region (spec allows at most one catch_all + * per try). */ + emit_uint32(loader_ctx, cur_block->eh_entry_idx); + if (loader_ctx->p_code_compiled != NULL) { + uint32 eh_idx = cur_block->eh_entry_idx; + bh_assert(eh_idx < func->exception_handler_count); + bh_assert(func->exception_handlers != NULL); + bh_assert(func->exception_handlers[eh_idx].catch_all_pc + == NULL); + func->exception_handlers[eh_idx].catch_all_pc = + loader_ctx->p_code_compiled; + } +#endif + + /* no immediates */ + /* replace frame_csp by LABEL_TYPE_CATCH_ALL */ + cur_block->label_type = LABEL_TYPE_CATCH_ALL; + + /* RESET_STACK removes the values pushed in TRY or previous + * CATCH Blocks */ + RESET_STACK(); + /* Same polymorphic reset as `WASM_OP_CATCH` — see the + * matching comment there for the rationale. */ + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + + /* catch_all has no tagtype and therefore no parameters */ + break; + } +#endif /* end of WASM_ENABLE_EXCE_HANDLING != 0 */ + case WASM_OP_ELSE: + handle_op_else: + { + BranchBlock *block = NULL; + BlockType block_type; + + if (loader_ctx->csp_num < 2 + /* the matched if isn't found */ + || (loader_ctx->frame_csp - 1)->label_type != LABEL_TYPE_IF + /* duplicated else is found */ + || (loader_ctx->frame_csp - 1)->else_addr) { + set_error_buf( + error_buf, error_buf_size, + "opcode else found without matched opcode if"); + goto fail; + } + block = loader_ctx->frame_csp - 1; + + /* check whether if branch's stack matches its result type */ + if (!check_block_stack(loader_ctx, block, error_buf, + error_buf_size)) + goto fail; + + block->else_addr = p - 1; + block_type = block->block_type; + +#if WASM_ENABLE_GC != 0 + if (!wasm_loader_init_local_use_masks( + loader_ctx, local_count, error_buf, error_buf_size)) { + goto fail; + } +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + /* if the result of if branch is in local or const area, add a + * copy op */ + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, + error_buf, error_buf_size)) { + goto fail; + } + + emit_empty_label_addr_and_frame_ip(PATCH_END); + apply_label_patch(loader_ctx, 1, PATCH_ELSE); +#endif + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + + /* Pass parameters to if-false branch */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) + PUSH_TYPE(block_type.u.type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Recover top param_count values of frame_offset stack */ + if (BLOCK_HAS_PARAM((block_type))) { + uint32 size; + size = sizeof(int16) * block_type.u.type->param_cell_num; + bh_memcpy_s(loader_ctx->frame_offset, size, + block->param_frame_offsets, size); + loader_ctx->frame_offset += (size / sizeof(int16)); + } + loader_ctx->dynamic_offset = block->start_dynamic_offset; +#endif + + break; + } + + case WASM_OP_END: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; +#if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_EXCE_HANDLING != 0 + /* If this END closes a try-region (LABEL_TYPE_TRY when + * the region has only a try-body and no catch, or + * LABEL_TYPE_CATCH / CATCH_ALL when at least one catch + * clause is present), we need to remember the entry's + * index and label type now — POP_CSP and the subsequent + * skip_label / reserve_block_ret happen first, but the + * end_of_region_pc capture has to wait until after + * those advance loader_ctx->p_code_compiled. */ + uint32 ending_eh_idx = cur_block->eh_entry_idx; + bool ending_was_eh = + (ending_eh_idx != UINT32_MAX) + && (cur_block->label_type == LABEL_TYPE_TRY + || cur_block->label_type == LABEL_TYPE_CATCH + || cur_block->label_type == LABEL_TYPE_CATCH_ALL); +#endif + + /* check whether block stack matches its result type */ + if (!check_block_stack(loader_ctx, cur_block, error_buf, + error_buf_size)) + goto fail; + + /* if there is no else branch, make a virtual else opcode for + easier integrity check and to copy the correct results to + the block return address for fast-interp mode: + change if block from `if ... end` to `if ... else end` */ + if (cur_block->label_type == LABEL_TYPE_IF + && !cur_block->else_addr) { + opcode = WASM_OP_ELSE; + p--; +#if WASM_ENABLE_FAST_INTERP != 0 + p_org = p; + skip_label(); + disable_emit = false; + emit_label(opcode); +#endif + goto handle_op_else; + } + + POP_CSP(); + +#if WASM_ENABLE_FAST_INTERP != 0 +#if WASM_ENABLE_EXCE_HANDLING != 0 + if (ending_was_eh) { + /* try-region END must execute the eh-stack pop in + * the runtime END handler — including when reached + * via `br N` (whose target was registered into + * this block's PATCH_END list by emit_br_info). + * + * Rewind the auto-emitted END byte, point all + * PATCH_END entries at the rewound position, then + * re-emit the END byte so both branches and fall- + * through dispatch the pop. reserve_block_ret's + * COPY (if any) lands *after* the END byte: the + * pop only adjusts eh_count and doesn't touch the + * operand stack the COPY moves from. */ + skip_label(); + apply_label_patch(loader_ctx, 0, PATCH_END); + emit_label(WASM_OP_END); + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, + error_buf, error_buf_size)) { + free_label_patch_list(loader_ctx->frame_csp); + goto fail; + } + free_label_patch_list(loader_ctx->frame_csp); + /* A try-region's END can never coincide with + * LABEL_TYPE_FUNCTION (the implicit function block + * is not a try); no WASM_OP_RETURN emit needed. */ + } + else +#endif /* WASM_ENABLE_EXCE_HANDLING */ + { + skip_label(); + /* copy the result to the block return address */ + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, + error_buf, error_buf_size)) { + /* it could be tmp frame_csp allocated from opcode like + * OP_BR and not counted in loader_ctx->csp_num, it + * won't be freed in wasm_loader_ctx_destroy(loader_ctx) + * so need to free the loader_ctx->frame_csp if fails */ + free_label_patch_list(loader_ctx->frame_csp); + goto fail; + } + + apply_label_patch(loader_ctx, 0, PATCH_END); + free_label_patch_list(loader_ctx->frame_csp); + if (loader_ctx->frame_csp->label_type + == LABEL_TYPE_FUNCTION) { + int32 idx; + uint8 ret_type; + + emit_label(WASM_OP_RETURN); + for (idx = (int32)func->func_type->result_count - 1; + idx >= 0; idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); + POP_OFFSET_TYPE(ret_type); + } + } + } +#endif + if (loader_ctx->csp_num > 0) { + loader_ctx->frame_csp->end_addr = p - 1; + } + else { + /* end of function block, function will return */ + if (p < p_end) { + set_error_buf(error_buf, error_buf_size, + "section size mismatch"); + goto fail; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (pending_exception) { + set_error_buf( + error_buf, error_buf_size, + "There is a pending exception needs to be handled"); + goto fail; + } +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_EXCE_HANDLING != 0 + /* Second-traverse-only: if this END closed a try- + * region, record where the rewritten IR continues so a + * runtime catch-handler body can branch past the + * region after running. The captured pc lands *after* + * the END's own skip_label and reserve_block_ret, so + * the next dispatched op is whatever follows the + * source-level END byte. */ + if (loader_ctx->p_code_compiled != NULL && ending_was_eh) { + bh_assert(ending_eh_idx < func->exception_handler_count); + bh_assert(func->exception_handlers != NULL); + func->exception_handlers[ending_eh_idx].end_of_region_pc = + loader_ctx->p_code_compiled; + } +#endif + + break; + } + + case WASM_OP_BR: + { + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) + goto fail; + +#if WASM_ENABLE_EXCE_HANDLING != 0 && WASM_ENABLE_FAST_INTERP != 0 + /* When a br skips over a try-region's END, the + * runtime br doesn't pop eh-stack entries. For a + * one-shot br to a block / function-end / catch, + * the leaked entry is absorbed by the static + * `exception_handler_count * EH_ENTRY_CELLS` + * reservation and dies at frame teardown — log + * a warning so the shape shows up in load-time + * diagnostics, but accept the module. + * + * If the br target is a LOOP entry, however, + * every iteration's TRY push adds one more entry + * to the eh-stack and eventually overwrites past + * the static reservation (silently in release + * builds since `bh_assert` is a no-op without + * `BH_DEBUG`). Reject those modules at load time + * — emitting cleanup at the br site would be the + * other fix, but it complicates the hot dispatch + * loop and the shape is rare in practice. */ + { + uint32 leaked = count_try_blocks_crossed( + loader_ctx->frame_csp - 1, frame_csp_tmp); + if (leaked > 0 + && br_try_leak_in_enclosing_loop( + loader_ctx->frame_csp_bottom, frame_csp_tmp)) { + set_error_buf(error_buf, error_buf_size, + "br out of try-region from inside a " + "loop not supported in fast " + "interpreter (would leak eh-stack " + "entries per iteration)"); + goto fail; + } + if (leaked > 0 && loader_ctx->p_code_compiled == NULL) { + LOG_WARNING("wasm fast-interp: br at func[%u] crosses " + "%u try-region(s); each leaks one " + "eh-stack entry until frame teardown", + cur_func_idx, leaked); + } + } +#endif + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + } + + case WASM_OP_BR_IF: + { + POP_I32(); + + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) + goto fail; + +#if WASM_ENABLE_EXCE_HANDLING != 0 && WASM_ENABLE_FAST_INTERP != 0 + { + uint32 leaked = count_try_blocks_crossed( + loader_ctx->frame_csp - 1, frame_csp_tmp); + if (leaked > 0 + && br_try_leak_in_enclosing_loop( + loader_ctx->frame_csp_bottom, frame_csp_tmp)) { + set_error_buf(error_buf, error_buf_size, + "br_if out of try-region from inside a " + "loop not supported in fast " + "interpreter (would leak eh-stack " + "entries per iteration)"); + goto fail; + } + if (leaked > 0 && loader_ctx->p_code_compiled == NULL) { + LOG_WARNING( + "wasm fast-interp: br_if at func[%u] crosses " + "%u try-region(s); each leaks one " + "eh-stack entry until frame teardown", + cur_func_idx, leaked); + } + } +#endif + + break; + } + + case WASM_OP_BR_TABLE: + { + uint32 depth = 0, default_arity, arity = 0; + BranchBlock *target_block; + BlockType *target_block_type; +#if WASM_ENABLE_FAST_INTERP == 0 + BrTableCache *br_table_cache = NULL; + uint8 *p_depth_begin, *p_depth, *p_opcode = p - 1; + uint32 j; +#endif + + pb_read_leb_uint32(p, p_end, count); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, count); +#endif + POP_I32(); + + /* Get each depth and check it */ + p_org = p; + for (i = 0; i <= count; i++) { + pb_read_leb_uint32(p, p_end, depth); + bh_assert(loader_ctx->csp_num > 0); + if (loader_ctx->csp_num - 1 < depth) { + set_error_buf(error_buf, error_buf_size, + "unknown label, " + "unexpected end of section or function"); + goto fail; + } + } + p = p_org; + + /* Get the default block's arity */ + target_block = loader_ctx->frame_csp - (depth + 1); + target_block_type = &target_block->block_type; + default_arity = block_type_get_arity(target_block_type, + target_block->label_type); + +#if WASM_ENABLE_FAST_INTERP == 0 + p_depth_begin = p_depth = p; +#endif + for (i = 0; i <= count; i++) { + p_org = p; + pb_read_leb_uint32(p, p_end, depth); + p = p_org; + + /* Get the target block's arity and check it */ + target_block = loader_ctx->frame_csp - (depth + 1); + target_block_type = &target_block->block_type; + arity = block_type_get_arity(target_block_type, + target_block->label_type); + if (arity != default_arity) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: br_table targets must " + "all use same result type"); + goto fail; + } + + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) { + goto fail; + } + +#if WASM_ENABLE_EXCE_HANDLING != 0 && WASM_ENABLE_FAST_INTERP != 0 + { + uint32 leaked = count_try_blocks_crossed( + loader_ctx->frame_csp - 1, frame_csp_tmp); + if (leaked > 0 + && br_try_leak_in_enclosing_loop( + loader_ctx->frame_csp_bottom, frame_csp_tmp)) { + set_error_buf(error_buf, error_buf_size, + "br_table out of try-region from " + "inside a loop not supported in fast " + "interpreter (would leak eh-stack " + "entries per iteration)"); + goto fail; + } + if (leaked > 0 && loader_ctx->p_code_compiled == NULL) { + LOG_WARNING( + "wasm fast-interp: br_table[%u] at " + "func[%u] crosses %u try-region(s); each " + "leaks one eh-stack entry until frame " + "teardown", + i, cur_func_idx, leaked); + } + } +#endif + +#if WASM_ENABLE_FAST_INTERP == 0 + if (br_table_cache) { + br_table_cache->br_depths[i] = depth; + } + else { + if (depth > 255) { + /* The depth cannot be stored in one byte, + create br_table cache to store each depth */ +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_opcode, *p_opcode, + error_buf, error_buf_size)) { + goto fail; + } +#endif + if (!(br_table_cache = loader_malloc( + offsetof(BrTableCache, br_depths) + + sizeof(uint32) + * (uint64)(count + 1), + error_buf, error_buf_size))) { + goto fail; + } + *p_opcode = EXT_OP_BR_TABLE_CACHE; + br_table_cache->br_table_op_addr = p_opcode; + br_table_cache->br_count = count; + /* Copy previous depths which are one byte */ + for (j = 0; j < i; j++) { + br_table_cache->br_depths[j] = p_depth_begin[j]; + } + br_table_cache->br_depths[i] = depth; + bh_list_insert(module->br_table_cache_list, + br_table_cache); + } + else { + /* The depth can be stored in one byte, use the + byte of the leb to store it */ + *p_depth++ = (uint8)depth; + } + } +#endif + } + +#if WASM_ENABLE_FAST_INTERP == 0 + /* Set the tailing bytes to nop */ + if (br_table_cache) + p_depth = p_depth_begin; + while (p_depth < p) + *p_depth++ = WASM_OP_NOP; +#endif + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + } + + case WASM_OP_RETURN: + { + WASMFuncType *func_type = func->func_type; + int32 idx; + uint8 ret_type; + +#if WASM_ENABLE_GC != 0 + uint32 j = func_type->ref_type_map_count - 1; +#endif + for (idx = (int32)func_type->result_count - 1; idx >= 0; + idx--) { + ret_type = + *(func_type->types + func_type->param_count + idx); +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(ret_type)) { + WASMRefType *ref_type = + func_type->ref_type_maps[j].ref_type; + bh_assert(func_type->ref_type_maps[j].index + == func_type->param_count + idx); + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + j--; + } +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + /* emit the offset after return opcode */ + POP_OFFSET_TYPE(ret_type); +#endif + POP_TYPE(ret_type); + } + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + break; + } + + case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif +#if WASM_ENABLE_GC != 0 + case WASM_OP_CALL_REF: + case WASM_OP_RETURN_CALL_REF: +#endif + { + WASMFuncType *func_type; + uint8 type; + int32 idx; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; + uint32 type_idx1; + int32 j; +#endif + +#if WASM_ENABLE_GC != 0 + if (opcode == WASM_OP_CALL_REF + || opcode == WASM_OP_RETURN_CALL_REF) { + pb_read_leb_uint32(p, p_end, type_idx1); + if (!check_type_index(module, module->type_count, type_idx1, + error_buf, error_buf_size)) { + goto fail; + } + if (module->types[type_idx1]->type_flag != WASM_TYPE_FUNC) { + set_error_buf(error_buf, error_buf_size, + "unknown function type"); + goto fail; + } + if (!wasm_loader_pop_nullable_typeidx(loader_ctx, &type, + &type_idx, error_buf, + error_buf_size)) { + goto fail; + } + if (type == VALUE_TYPE_ANY) { + type_idx = type_idx1; + } + if (!check_type_index(module, module->type_count, type_idx, + error_buf, error_buf_size)) { + goto fail; + } + if (module->types[type_idx]->type_flag != WASM_TYPE_FUNC) { + set_error_buf(error_buf, error_buf_size, + "unknown function type"); + goto fail; + } + if (!wasm_func_type_is_super_of( + (WASMFuncType *)module->types[type_idx1], + (WASMFuncType *)module->types[type_idx])) { + set_error_buf(error_buf, error_buf_size, + "function type mismatch"); + goto fail; + } + func_type = (WASMFuncType *)module->types[type_idx]; + } + else +#endif + { + pb_read_leb_uint32(p, p_end, func_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + /* we need to emit func_idx before arguments */ + emit_uint32(loader_ctx, func_idx); +#endif + + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + + if (func_idx < module->import_function_count) + func_type = module->import_functions[func_idx] + .u.function.func_type; + else + func_type = + module + ->functions[func_idx + - module->import_function_count] + ->func_type; + } + + if (func_type->param_count > 0) { +#if WASM_ENABLE_GC != 0 + j = (int32)(func_type->result_ref_type_maps + - func_type->ref_type_maps - 1); +#endif + for (idx = (int32)(func_type->param_count - 1); idx >= 0; + idx--) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type( + func_type->types[idx])) { + ref_type = func_type->ref_type_maps[j].ref_type; + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + j--; + } +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + POP_TYPE(func_type->types[idx]); + } + } + +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + if (opcode == WASM_OP_CALL || opcode == WASM_OP_CALL_REF) { +#endif +#if WASM_ENABLE_GC != 0 + j = (int32)(func_type->result_ref_type_maps + - func_type->ref_type_maps); +#endif + for (i = 0; i < func_type->result_count; i++) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type( + func_type->types[func_type->param_count + i])) { + ref_type = func_type->ref_type_maps[j].ref_type; + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + j++; + } +#endif + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Here we emit each return value's dynamic_offset. But + * in fact these offsets are continuous, so interpreter + * only need to get the first return value's offset. + */ + PUSH_OFFSET_TYPE( + func_type->types[func_type->param_count + i]); +#endif + } +#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0 + } + else { +#if WASM_ENABLE_GC == 0 + if (func_type->result_count + != func->func_type->result_count) { + set_error_buf_v(error_buf, error_buf_size, "%s%u%s", + "type mismatch: expect ", + func->func_type->result_count, + " return values but got other"); + goto fail; + } + for (i = 0; i < func_type->result_count; i++) { + type = func->func_type + ->types[func->func_type->param_count + i]; + if (func_type->types[func_type->param_count + i] + != type) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", + type2str(type), " but got other"); + goto fail; + } + } +#else + if (!wasm_func_type_result_is_subtype_of( + func_type, func->func_type, module->types, + module->type_count)) { + set_error_buf( + error_buf, error_buf_size, + "type mismatch: invalid func result types"); + goto fail; + } +#endif + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + } +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_func_call = true; +#endif + (void)type; + break; + } + + /* + * if disable reference type: call_indirect typeidx, 0x00 + * if enable reference type: call_indirect typeidx, tableidx + */ + case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif + { + int32 idx; + WASMFuncType *func_type; + uint32 tbl_elem_type; +#if WASM_ENABLE_GC != 0 + WASMRefType *elem_ref_type = NULL; +#endif + + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 || WASM_ENABLE_GC != 0 +#if WASM_ENABLE_WAMR_COMPILER != 0 + if (p + 1 < p_end && *p != 0x00) { + /* + * Any non-0x00 byte requires the ref types proposal. + * This is different from checking the table_idx value + * since `0x80 0x00` etc. are all valid encodings of zero. + */ + module->is_ref_types_used = true; + } +#endif + pb_read_leb_uint32(p, p_end, table_idx); +#else + CHECK_BUF(p, p_end, 1); + table_idx = read_uint8(p); +#endif + if (!check_table_index(module, table_idx, error_buf, + error_buf_size)) { + goto fail; + } + tbl_elem_type = + table_idx < module->import_table_count + ? module->import_tables[table_idx] + .u.table.table_type.elem_type + : module->tables[table_idx - module->import_table_count] + .table_type.elem_type; + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + if (tbl_elem_type != VALUE_TYPE_FUNCREF) { + set_error_buf_v(error_buf, error_buf_size, + "type mismatch: instruction requires table " + "of functions but table %u has externref", + table_idx); + goto fail; + } +#elif WASM_ENABLE_GC != 0 + /* Table element must match type ref null func */ + elem_ref_type = + table_idx < module->import_table_count + ? module->import_tables[table_idx] + .u.table.table_type.elem_ref_type + : module->tables[table_idx - module->import_table_count] + .table_type.elem_ref_type; + + if (!wasm_reftype_is_subtype_of( + tbl_elem_type, elem_ref_type, REF_TYPE_FUNCREF, NULL, + module->types, module->type_count)) { + set_error_buf_v(error_buf, error_buf_size, + "type mismatch: instruction requires " + "reference type t match type ref null func" + "in table %u", + table_idx); + goto fail; + } +#else + (void)tbl_elem_type; +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + /* we need to emit before arguments */ +#if WASM_ENABLE_TAIL_CALL != 0 + emit_byte(loader_ctx, opcode); +#endif + emit_uint32(loader_ctx, type_idx); + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + /* skip elem idx */ + POP_TBL_ELEM_IDX(); + + if (!check_function_type(module, type_idx, error_buf, + error_buf_size)) { + goto fail; + } + + func_type = (WASMFuncType *)module->types[type_idx]; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; + idx--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + POP_TYPE(func_type->types[idx]); + } + } + +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL_INDIRECT) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE( + func_type->types[func_type->param_count + i]); +#endif + } +#if WASM_ENABLE_TAIL_CALL != 0 + } + else { + uint8 type; + if (func_type->result_count + != func->func_type->result_count) { + set_error_buf_v(error_buf, error_buf_size, "%s%u%s", + "type mismatch: expect ", + func->func_type->result_count, + " return values but got other"); + goto fail; + } + for (i = 0; i < func_type->result_count; i++) { + type = func->func_type + ->types[func->func_type->param_count + i]; + if (func_type->types[func_type->param_count + i] + != type) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", + type2str(type), " but got other"); + goto fail; + } + } + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_func_call = true; +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_call_indirect = true; +#endif + break; + } + + case WASM_OP_DROP: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic) { + set_error_buf(error_buf, error_buf_size, + "type mismatch, opcode drop was found " + "but stack was empty"); + goto fail; + } + + if (available_stack_cell > 0) { +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type( + *(loader_ctx->frame_ref - 1))) { + bh_assert((int32)(loader_ctx->reftype_map_num + - cur_block->reftype_map_num) + > 0); + loader_ctx->frame_reftype_map--; + loader_ctx->reftype_map_num--; + } +#endif + if (is_32bit_type(*(loader_ctx->frame_ref - 1))) { + loader_ctx->frame_ref--; + loader_ctx->stack_cell_num--; +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset--; + if ((*(loader_ctx->frame_offset) + > loader_ctx->start_dynamic_offset) + && (*(loader_ctx->frame_offset) + < loader_ctx->max_dynamic_offset)) + loader_ctx->dynamic_offset--; +#endif + } + else if (is_64bit_type(*(loader_ctx->frame_ref - 1))) { + loader_ctx->frame_ref -= 2; + loader_ctx->stack_cell_num -= 2; +#if WASM_ENABLE_FAST_INTERP == 0 + *(p - 1) = WASM_OP_DROP_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset -= 2; + if ((*(loader_ctx->frame_offset) + > loader_ctx->start_dynamic_offset) + && (*(loader_ctx->frame_offset) + < loader_ctx->max_dynamic_offset)) + loader_ctx->dynamic_offset -= 2; +#endif + } +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) + else if (*(loader_ctx->frame_ref - 1) == VALUE_TYPE_V128) { + loader_ctx->frame_ref -= 4; + loader_ctx->stack_cell_num -= 4; +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset -= 4; + if ((*(loader_ctx->frame_offset) + > loader_ctx->start_dynamic_offset) + && (*(loader_ctx->frame_offset) + < loader_ctx->max_dynamic_offset)) + loader_ctx->dynamic_offset -= 4; +#endif + } +#endif +#endif + else { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + } + break; + } + + case WASM_OP_SELECT: + { + uint8 ref_type; + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell; +#if WASM_ENABLE_FAST_INTERP != 0 + uint8 *p_code_compiled_tmp = loader_ctx->p_code_compiled; +#endif + + POP_I32(); + + available_stack_cell = (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic) { + set_error_buf(error_buf, error_buf_size, + "type mismatch or invalid result arity, " + "opcode select was found " + "but stack was empty"); + goto fail; + } + + if (available_stack_cell > 0) { + switch (*(loader_ctx->frame_ref - 1)) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + case VALUE_TYPE_ANY: + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: +#if WASM_ENABLE_FAST_INTERP == 0 + *(p - 1) = WASM_OP_SELECT_64; +#else + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT_64; +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void **)(p_code_compiled_tmp + - sizeof(void *)) = + handle_table[opcode_tmp]; +#elif UINTPTR_MAX == UINT64_MAX + /* emit int32 relative offset in 64-bit target + */ + int32 offset = + (int32)((uint8 *)handle_table[opcode_tmp] + - (uint8 *)handle_table[0]); + *(int32 *)(p_code_compiled_tmp + - sizeof(int32)) = offset; +#else + /* emit uint32 label address in 32-bit target */ + *(uint32 *)(p_code_compiled_tmp + - sizeof(uint32)) = + (uint32)(uintptr_t)handle_table[opcode_tmp]; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } +#endif /* end of WASM_ENABLE_FAST_INTERP */ + break; +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) + case VALUE_TYPE_V128: +#if WASM_ENABLE_SIMDE != 0 + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT_128; +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void **)(p_code_compiled_tmp + - sizeof(void *)) = + handle_table[opcode_tmp]; +#elif UINTPTR_MAX == UINT64_MAX + /* emit int32 relative offset in 64-bit target + */ + int32 offset = + (int32)((uint8 *)handle_table[opcode_tmp] + - (uint8 *)handle_table[0]); + *(int32 *)(p_code_compiled_tmp + - sizeof(int32)) = offset; +#else + /* emit uint32 label address in 32-bit target */ + *(uint32 *)(p_code_compiled_tmp + - sizeof(uint32)) = + (uint32)(uintptr_t)handle_table[opcode_tmp]; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } +#endif /* end of WASM_ENABLE_FAST_INTERP */ + break; +#endif /* (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || \ + (WASM_ENABLE_FAST_INTERP != 0) */ +#endif /* WASM_ENABLE_SIMD != 0 */ + default: + { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + } + + ref_type = *(loader_ctx->frame_ref - 1); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + PUSH_OFFSET_TYPE(ref_type); + PUSH_TYPE(ref_type); +#else + POP2_AND_PUSH(ref_type, ref_type); +#endif + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(VALUE_TYPE_ANY); +#endif + PUSH_TYPE(VALUE_TYPE_ANY); + } + break; + } + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_SELECT_T: + { + uint8 vec_len, type; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type = NULL; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + uint8 *p_code_compiled_tmp = loader_ctx->p_code_compiled; +#endif + + pb_read_leb_uint32(p, p_end, vec_len); + if (vec_len != 1) { + /* typed select must have exactly one result */ + set_error_buf(error_buf, error_buf_size, + "invalid result arity"); + goto fail; + } + +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p, p_end, 1); + type = read_uint8(p); + if (!is_valid_value_type_for_interpreter(type)) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } +#else + p_org = p + 1; + if (!resolve_value_type((const uint8 **)&p, p_end, module, + module->type_count, &need_ref_type_map, + &wasm_ref_type, false, error_buf, + error_buf_size)) { + goto fail; + } + type = wasm_ref_type.ref_type; + if (need_ref_type_map) { + if (!(ref_type = reftype_set_insert( + module->ref_type_set, &wasm_ref_type, error_buf, + error_buf_size))) { + goto fail; + } + } +#if WASM_ENABLE_FAST_INTERP == 0 + while (p_org < p) { +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_org, *p_org, error_buf, + error_buf_size)) { + goto fail; + } +#endif + /* Ignore extra bytes for interpreter */ + *p_org++ = WASM_OP_NOP; + } +#endif +#endif /* end of WASM_ENABLE_GC == 0 */ + + POP_I32(); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT; + + if (type == VALUE_TYPE_V128) { +#if WASM_ENABLE_SIMDE != 0 + opcode_tmp = WASM_OP_SELECT_128; +#else + set_error_buf(error_buf, error_buf_size, + "v128 value type requires simd feature"); +#endif + } + else { + if (type == VALUE_TYPE_F64 || type == VALUE_TYPE_I64) + opcode_tmp = WASM_OP_SELECT_64; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_reftype(type)) + opcode_tmp = WASM_OP_SELECT_T; +#endif +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void **)(p_code_compiled_tmp - sizeof(void *)) = + handle_table[opcode_tmp]; +#else +#if UINTPTR_MAX == UINT64_MAX + /* emit int32 relative offset in 64-bit target */ + int32 offset = (int32)((uint8 *)handle_table[opcode_tmp] + - (uint8 *)handle_table[0]); + *(int32 *)(p_code_compiled_tmp - sizeof(int32)) = + offset; +#else + /* emit uint32 label address in 32-bit target */ + *(uint32 *)(p_code_compiled_tmp - sizeof(uint32)) = + (uint32)(uintptr_t)handle_table[opcode_tmp]; +#endif +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } + } +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + + POP_REF(type); + +#if WASM_ENABLE_GC != 0 + if (need_ref_type_map) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } +#endif + POP_REF(type); + +#if WASM_ENABLE_GC != 0 + if (need_ref_type_map) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } +#endif + PUSH_REF(type); + +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + (void)vec_len; + break; + } + + /* table.get x. tables[x]. [it] -> [t] */ + /* table.set x. tables[x]. [it t] -> [] */ + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + { + uint8 decl_ref_type; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; +#endif + + pb_read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, &decl_ref_type, +#if WASM_ENABLE_GC != 0 + (void **)&ref_type, +#else + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(decl_ref_type)) { + bh_assert(ref_type); + bh_memcpy_s(&wasm_ref_type, (uint32)sizeof(WASMRefType), + ref_type, wasm_reftype_struct_size(ref_type)); + } +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (opcode == WASM_OP_TABLE_GET) { + POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(decl_ref_type); +#endif + PUSH_TYPE(decl_ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + POP_TBL_ELEM_IDX(); + } + +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } + case WASM_OP_REF_NULL: + { + uint8 ref_type; + +#if WASM_ENABLE_GC == 0 + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + + if (ref_type != VALUE_TYPE_FUNCREF + && ref_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, "type mismatch"); + goto fail; + } +#else + pb_read_leb_int32(p, p_end, heap_type); + if (heap_type >= 0) { + if (!check_type_index(module, module->type_count, heap_type, + error_buf, error_buf_size)) { + goto fail; + } + wasm_set_refheaptype_typeidx(&wasm_ref_type.ref_ht_typeidx, + true, heap_type); + ref_type = wasm_ref_type.ref_type; + } + else { + if (!wasm_is_valid_heap_type(heap_type)) { + set_error_buf(error_buf, error_buf_size, + "unknown type"); + goto fail; + } + ref_type = (uint8)((int32)0x80 + heap_type); + } +#endif /* end of WASM_ENABLE_GC == 0 */ + +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } + case WASM_OP_REF_IS_NULL: + { +#if WASM_ENABLE_GC == 0 +#if WASM_ENABLE_FAST_INTERP != 0 + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 block_stack_cell_num = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + if (block_stack_cell_num <= 0) { + if (!cur_block->is_stack_polymorphic) { + set_error_buf( + error_buf, error_buf_size, + "type mismatch: expect data but stack was empty"); + goto fail; + } + } + else { + if (*(loader_ctx->frame_ref - 1) == VALUE_TYPE_FUNCREF + || *(loader_ctx->frame_ref - 1) == VALUE_TYPE_EXTERNREF + || *(loader_ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + if (!wasm_loader_pop_frame_ref_offset( + loader_ctx, *(loader_ctx->frame_ref - 1), + error_buf, error_buf_size)) { + goto fail; + } + } + else { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + } +#else + if (!wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_FUNCREF, + error_buf, error_buf_size) + && !wasm_loader_pop_frame_ref(loader_ctx, + VALUE_TYPE_EXTERNREF, + error_buf, error_buf_size)) { + goto fail; + } +#endif +#else /* else of WASM_ENABLE_GC == 0 */ + uint8 type; + if (!wasm_loader_pop_heap_obj(loader_ctx, &type, &wasm_ref_type, + error_buf, error_buf_size)) { + goto fail; + } +#endif + PUSH_I32(); + +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } + case WASM_OP_REF_FUNC: + { + pb_read_leb_uint32(p, p_end, func_idx); + + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + + /* Refer to a forward-declared function: + the function must be an import, exported, or present in + a table elem segment or global initializer to be used as + the operand to ref.func */ + if (func_idx >= module->import_function_count) { + WASMTableSeg *table_seg = module->table_segments; + bool func_declared = false; + uint32 j; + + for (i = 0; i < module->global_count; i++) { + if (module->globals[i].type.val_type + == VALUE_TYPE_FUNCREF + && module->globals[i].init_expr.init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST + && module->globals[i].init_expr.u.unary.v.u32 + == func_idx) { + func_declared = true; + break; + } + } + + if (!func_declared) { + /* Check whether the function is declared in table segs, + note that it doesn't matter whether the table seg's + mode is passive, active or declarative. */ + for (i = 0; i < module->table_seg_count; + i++, table_seg++) { + if (table_seg->elem_type == VALUE_TYPE_FUNCREF +#if WASM_ENABLE_GC != 0 + /* elem type is (ref null? func) or + (ref null? $t) */ + || ((table_seg->elem_type + == REF_TYPE_HT_NON_NULLABLE + || table_seg->elem_type + == REF_TYPE_HT_NULLABLE) + && (table_seg->elem_ref_type->ref_ht_common + .heap_type + == HEAP_TYPE_FUNC + || table_seg->elem_ref_type + ->ref_ht_common.heap_type + > 0)) +#endif + ) { + for (j = 0; j < table_seg->value_count; j++) { + if (table_seg->init_values[j] + .u.unary.v.ref_index + == func_idx) { + func_declared = true; + break; + } + } + } + } + } + + if (!func_declared) { + /* Check whether the function is exported */ + for (i = 0; i < module->export_count; i++) { + if (module->exports[i].kind == EXPORT_KIND_FUNC + && module->exports[i].index == func_idx) { + func_declared = true; + break; + } + } + } + + if (!func_declared) { + set_error_buf(error_buf, error_buf_size, + "undeclared function reference"); + goto fail; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, func_idx); +#endif +#if WASM_ENABLE_GC == 0 + PUSH_FUNCREF(); +#else + if (func_idx < module->import_function_count) + type_idx = + module->import_functions[func_idx].u.function.type_idx; + else + type_idx = module + ->functions[func_idx + - module->import_function_count] + ->type_idx; + wasm_set_refheaptype_typeidx(&wasm_ref_type.ref_ht_typeidx, + false, type_idx); + PUSH_REF(wasm_ref_type.ref_type); +#endif + +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_GC != 0 + case WASM_OP_REF_AS_NON_NULL: + case WASM_OP_BR_ON_NULL: + { + uint8 type; + WASMRefType ref_type; + + /* POP (ref null ht) and get the converted (ref ht) */ + if (!wasm_loader_pop_nullable_ht(loader_ctx, &type, &ref_type, + error_buf, error_buf_size)) { + goto fail; + } + + if (opcode == WASM_OP_BR_ON_NULL) { + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + disable_emit = true; +#endif + } + + /* PUSH the converted (ref ht) */ + if (type != VALUE_TYPE_ANY) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), &ref_type, + sizeof(WASMRefType)); + } + PUSH_REF(type); + break; + } + + case WASM_OP_BR_ON_NON_NULL: + { + uint8 type; + WASMRefType ref_type; + uint32 available_stack_cell = + loader_ctx->stack_cell_num + - (loader_ctx->frame_csp - 1)->stack_cell_num; + + /* POP (ref null ht) and get the converted (ref ht) */ + if (!wasm_loader_pop_nullable_ht(loader_ctx, &type, &ref_type, + error_buf, error_buf_size)) { + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + disable_emit = true; +#endif + + /* Temporarily PUSH back (ref ht), check brach block and + then POP it */ + if (available_stack_cell + > 0) { /* stack isn't in polymorphic state */ + if (type != VALUE_TYPE_ANY) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + &ref_type, sizeof(WASMRefType)); + } + PUSH_REF(type); + } + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) { + goto fail; + } + if (available_stack_cell + > 0) { /* stack isn't in polymorphic state */ + POP_REF(type); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Erase the opnd offset emitted by POP_REF() */ + wasm_loader_emit_backspace(loader_ctx, sizeof(uint16)); +#endif + } + break; + } + + case WASM_OP_REF_EQ: + POP_REF(REF_TYPE_EQREF); + POP_REF(REF_TYPE_EQREF); + PUSH_I32(); + break; +#endif /* end of WASM_ENABLE_GC != 0 */ + + case WASM_OP_GET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_GC != 0 + /* Cannot get a non-nullable and unset local */ + if (local_idx >= param_count + && wasm_is_reftype_htref_non_nullable(local_type) + && !wasm_loader_get_local_status(loader_ctx, + local_idx - param_count)) { + set_error_buf(error_buf, error_buf_size, + "uninitialized local"); + goto fail; + } +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Get Local is optimized out */ + skip_label(); + disable_emit = true; + operand_offset = local_offset; + PUSH_OFFSET_TYPE(local_type); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_FAST_JIT == 0) && (WASM_ENABLE_DEBUG_INTERP == 0) + if (local_offset < 0x80 +#if WASM_ENABLE_GC != 0 + && !wasm_is_type_reftype(local_type) +#endif + ) { + *p_org++ = EXT_OP_GET_LOCAL_FAST; + if (is_32bit_type(local_type)) { + *p_org++ = (uint8)local_offset; + } + else { + *p_org++ = (uint8)(local_offset | 0x80); + } + while (p_org < p) { + *p_org++ = WASM_OP_NOP; + } + } +#endif +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ + break; + } + + case WASM_OP_SET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local( + loader_ctx, opcode, local_offset, local_type, + &preserve_local, error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256 +#if WASM_ENABLE_GC != 0 + && !wasm_is_type_reftype(local_type) +#endif + ) { + skip_label(); + if ((!preserve_local) && (LAST_OP_OUTPUT_I32())) { + if (loader_ctx->p_code_compiled) + STORE_U16(loader_ctx->p_code_compiled - 2, + local_offset); + loader_ctx->frame_offset--; + loader_ctx->dynamic_offset--; + } + else if ((!preserve_local) && (LAST_OP_OUTPUT_I64())) { + if (loader_ctx->p_code_compiled) + STORE_U16(loader_ctx->p_code_compiled - 2, + local_offset); + loader_ctx->frame_offset -= 2; + loader_ctx->dynamic_offset -= 2; + } + else { + if (is_32bit_type(local_type)) { + emit_label(EXT_OP_SET_LOCAL_FAST); + emit_byte(loader_ctx, (uint8)local_offset); + } + else if (is_64bit_type(local_type)) { + emit_label(EXT_OP_SET_LOCAL_FAST_I64); + emit_byte(loader_ctx, (uint8)local_offset); + } +#if WASM_ENABLE_SIMDE != 0 + else if (local_type == VALUE_TYPE_V128) { + emit_label(EXT_OP_SET_LOCAL_FAST_V128); + emit_byte(loader_ctx, (uint8)local_offset); + } +#endif + else { + set_error_buf(error_buf, error_buf_size, + "unknown local type"); + goto fail; + } + POP_OFFSET_TYPE(local_type); + } + } + else { /* local index larger than 255, reserve leb */ + emit_uint32(loader_ctx, local_idx); + POP_OFFSET_TYPE(local_type); + } +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_FAST_JIT == 0) && (WASM_ENABLE_DEBUG_INTERP == 0) + if (local_offset < 0x80 +#if WASM_ENABLE_GC != 0 + && !wasm_is_type_reftype(local_type) +#endif + ) { + *p_org++ = EXT_OP_SET_LOCAL_FAST; + if (is_32bit_type(local_type)) { + *p_org++ = (uint8)local_offset; + } + else { + *p_org++ = (uint8)(local_offset | 0x80); + } + while (p_org < p) { + *p_org++ = WASM_OP_NOP; + } + } +#endif +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ - case WASM_OP_ELSE: - if (block_type == BLOCK_TYPE_IF && block_nested_depth == 1) - else_addr = (uint8*)(p - 1); - if (block_nested_depth - 1 < sizeof(block_stack)/sizeof(BlockAddr)) - block_stack[block_nested_depth - 1].else_addr = (uint8*)(p - 1); - break; +#if WASM_ENABLE_GC != 0 + if (local_idx >= param_count) { + wasm_loader_mask_local(loader_ctx, local_idx - param_count); + } +#endif - case WASM_OP_END: - if (block_nested_depth == 1) { - if (block_type == BLOCK_TYPE_IF) - *p_else_addr = else_addr; - *p_end_addr = (uint8*)(p - 1); + POP_TYPE(local_type); + break; + } - block_stack[0].end_addr = (uint8*)(p - 1); - for (t = 0; t < sizeof(block_stack)/sizeof(BlockAddr); t++) { - start_addr = block_stack[t].start_addr; - if (start_addr) { - i = (uint32)(((uintptr_t)start_addr) ^ ((uintptr_t)start_addr >> 16)); - i = i % BLOCK_ADDR_CACHE_SIZE; - block = module->block_addr_cache[i]; - for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) - if (!block[j].start_addr) - break; + case WASM_OP_TEE_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); +#if WASM_ENABLE_FAST_INTERP != 0 + /* If the stack is in polymorphic state, do fake pop and push on + offset stack to keep the depth of offset stack to be the + same with ref stack */ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + if (cur_block->is_stack_polymorphic) { + POP_OFFSET_TYPE(local_type); + PUSH_OFFSET_TYPE(local_type); + } +#endif + POP_TYPE(local_type); + PUSH_TYPE(local_type); - if (j == BLOCK_ADDR_CONFLICT_SIZE) { - memmove(block + 1, block, (BLOCK_ADDR_CONFLICT_SIZE - 1) * - sizeof(BlockAddr)); - j = 0; +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local( + loader_ctx, opcode, local_offset, local_type, + &preserve_local, error_buf, error_buf_size))) + goto fail; - } - block[j].start_addr = block_stack[t].start_addr; - block[j].else_addr = block_stack[t].else_addr; - block[j].end_addr = block_stack[t].end_addr; - } - else - break; + if (local_offset < 256 +#if WASM_ENABLE_GC != 0 + && !wasm_is_type_reftype(local_type) +#endif + ) { + skip_label(); + if (is_32bit_type(local_type)) { + emit_label(EXT_OP_TEE_LOCAL_FAST); + emit_byte(loader_ctx, (uint8)local_offset); + } +#if WASM_ENABLE_SIMDE != 0 + else if (local_type == VALUE_TYPE_V128) { + emit_label(EXT_OP_TEE_LOCAL_FAST_V128); + emit_byte(loader_ctx, (uint8)local_offset); + } +#endif + else { + emit_label(EXT_OP_TEE_LOCAL_FAST_I64); + emit_byte(loader_ctx, (uint8)local_offset); } - return true; } - else { - block_nested_depth--; - if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) - block_stack[block_nested_depth].end_addr = (uint8*)(p - 1); + else { /* local index larger than 255, reserve leb */ + emit_uint32(loader_ctx, local_idx); } - break; + emit_operand(loader_ctx, + *(loader_ctx->frame_offset + - wasm_value_type_cell_num(local_type))); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_FAST_JIT == 0) && (WASM_ENABLE_DEBUG_INTERP == 0) + if (local_offset < 0x80 +#if WASM_ENABLE_GC != 0 + && !wasm_is_type_reftype(local_type) +#endif + ) { + *p_org++ = EXT_OP_TEE_LOCAL_FAST; + if (is_32bit_type(local_type)) { + *p_org++ = (uint8)local_offset; + } + else { + *p_org++ = (uint8)(local_offset | 0x80); + } + while (p_org < p) { + *p_org++ = WASM_OP_NOP; + } + } +#endif +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ - case WASM_OP_BR: - case WASM_OP_BR_IF: - skip_leb_uint32(p, p_end); /* labelidx */ +#if WASM_ENABLE_GC != 0 + if (local_idx >= param_count) { + wasm_loader_mask_local(loader_ctx, local_idx - param_count); + } +#endif break; + } - case WASM_OP_BR_TABLE: - read_leb_uint32(p, p_end, count); /* lable num */ - for (i = 0; i <= count; i++) /* lableidxs */ - skip_leb_uint32(p, p_end); - break; + case WASM_OP_GET_GLOBAL: + { +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; +#endif - case WASM_OP_RETURN: - break; + p_org = p - 1; + pb_read_leb_uint32(p, p_end, global_idx); + if (global_idx >= global_count) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + goto fail; + } - case WASM_OP_CALL: - skip_leb_uint32(p, p_end); /* funcidx */ - break; + global_type = global_idx < module->import_global_count + ? module->import_globals[global_idx] + .u.global.type.val_type + : module + ->globals[global_idx + - module->import_global_count] + .type.val_type; +#if WASM_ENABLE_GC != 0 + ref_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.ref_type + : module + ->globals[global_idx + - module->import_global_count] + .ref_type; + if (wasm_is_type_multi_byte_type(global_type)) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } +#endif - case WASM_OP_CALL_INDIRECT: - skip_leb_uint32(p, p_end); /* typeidx */ - CHECK_BUF(p, p_end, 1); - u8 = read_uint8(p); /* 0x00 */ - break; + PUSH_TYPE(global_type); - case WASM_OP_DROP: - case WASM_OP_SELECT: - case WASM_OP_DROP_64: - case WASM_OP_SELECT_64: +#if WASM_ENABLE_FAST_INTERP == 0 + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_org, *p_org, error_buf, + error_buf_size)) { + goto fail; + } +#endif + *p_org = WASM_OP_GET_GLOBAL_64; + } +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + skip_label(); + emit_label(WASM_OP_GET_GLOBAL_64); + } +#if WASM_ENABLE_SIMDE != 0 + if (global_type == VALUE_TYPE_V128) { + skip_label(); + emit_label(WASM_OP_GET_GLOBAL_V128); + } +#endif /* end of WASM_ENABLE_SIMDE */ + emit_uint32(loader_ctx, global_idx); + PUSH_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ break; + } - case WASM_OP_GET_LOCAL: - case WASM_OP_SET_LOCAL: - case WASM_OP_TEE_LOCAL: - case WASM_OP_GET_GLOBAL: case WASM_OP_SET_GLOBAL: - skip_leb_uint32(p, p_end); /* localidx */ - break; + { + bool is_mutable = false; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; +#endif + + p_org = p - 1; + pb_read_leb_uint32(p, p_end, global_idx); + if (global_idx >= global_count) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + goto fail; + } + + is_mutable = global_idx < module->import_global_count + ? module->import_globals[global_idx] + .u.global.type.is_mutable + : module + ->globals[global_idx + - module->import_global_count] + .type.is_mutable; + if (!is_mutable) { +#if WASM_ENABLE_GC == 0 + set_error_buf(error_buf, error_buf_size, + "global is immutable"); +#else + set_error_buf(error_buf, error_buf_size, + "immutable global"); +#endif + goto fail; + } + + global_type = global_idx < module->import_global_count + ? module->import_globals[global_idx] + .u.global.type.val_type + : module + ->globals[global_idx + - module->import_global_count] + .type.val_type; +#if WASM_ENABLE_GC != 0 + ref_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.ref_type + : module + ->globals[global_idx + - module->import_global_count] + .ref_type; + if (wasm_is_type_multi_byte_type(global_type)) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), ref_type, + wasm_reftype_struct_size(ref_type)); + } +#endif + +#if WASM_ENABLE_FAST_INTERP == 0 + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_org, *p_org, error_buf, + error_buf_size)) { + goto fail; + } +#endif + *p_org = WASM_OP_SET_GLOBAL_64; + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (!record_fast_op(module, p_org, *p_org, error_buf, + error_buf_size)) { + goto fail; + } +#endif + *p_org = WASM_OP_SET_GLOBAL_AUX_STACK; +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_set_global_aux_stack = true; +#endif + } +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_64); + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_AUX_STACK); + } +#if WASM_ENABLE_SIMDE != 0 + else if (global_type == VALUE_TYPE_V128) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_V128); + } +#endif /* end of WASM_ENABLE_SIMDE */ + emit_uint32(loader_ctx, global_idx); + POP_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + + POP_TYPE(global_type); - case WASM_OP_GET_LOCAL_FAST: - case WASM_OP_SET_LOCAL_FAST: - case WASM_OP_TEE_LOCAL_FAST: - CHECK_BUF(p, p_end, 1); - p++; break; + } + /* load */ case WASM_OP_I32_LOAD: - case WASM_OP_I64_LOAD: - case WASM_OP_F32_LOAD: - case WASM_OP_F64_LOAD: case WASM_OP_I32_LOAD8_S: case WASM_OP_I32_LOAD8_U: case WASM_OP_I32_LOAD16_S: case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD: case WASM_OP_I64_LOAD8_S: case WASM_OP_I64_LOAD8_U: case WASM_OP_I64_LOAD16_S: case WASM_OP_I64_LOAD16_U: case WASM_OP_I64_LOAD32_S: case WASM_OP_I64_LOAD32_U: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + /* store */ case WASM_OP_I32_STORE: - case WASM_OP_I64_STORE: - case WASM_OP_F32_STORE: - case WASM_OP_F64_STORE: case WASM_OP_I32_STORE8: case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE: case WASM_OP_I64_STORE8: case WASM_OP_I64_STORE16: case WASM_OP_I64_STORE32: - skip_leb_uint32(p, p_end); /* align */ - skip_leb_uint32(p, p_end); /* offset */ + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + { +#if WASM_ENABLE_FAST_INTERP != 0 + /* change F32/F64 into I32/I64 */ + if (opcode == WASM_OP_F32_LOAD) { + skip_label(); + emit_label(WASM_OP_I32_LOAD); + } + else if (opcode == WASM_OP_F64_LOAD) { + skip_label(); + emit_label(WASM_OP_I64_LOAD); + } + else if (opcode == WASM_OP_F32_STORE) { + skip_label(); + emit_label(WASM_OP_I32_STORE); + } + else if (opcode == WASM_OP_F64_STORE) { + skip_label(); + emit_label(WASM_OP_I64_STORE); + } +#endif + CHECK_MEMORY(); + pb_read_leb_memarg(p, p_end, align); /* align */ + pb_read_leb_mem_offset(p, p_end, mem_offset); /* offset */ + if (!check_memory_access_align(opcode, align, error_buf, + error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + switch (opcode) { + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I32); + break; + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I64); + break; + case WASM_OP_F32_LOAD: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_F32); + break; + case WASM_OP_F64_LOAD: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_F64); + break; + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + POP_I32(); + POP_MEM_OFFSET(); + break; + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + POP_I64(); + POP_MEM_OFFSET(); + break; + case WASM_OP_F32_STORE: + POP_F32(); + POP_MEM_OFFSET(); + break; + case WASM_OP_F64_STORE: + POP_F64(); + POP_MEM_OFFSET(); + break; + default: + break; + } break; + } case WASM_OP_MEMORY_SIZE: + CHECK_MEMORY(); + pb_read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); + PUSH_PAGE_COUNT(); + + module->possible_memory_grow = true; +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + case WASM_OP_MEMORY_GROW: - skip_leb_uint32(p, p_end); /* 0x00 */ + CHECK_MEMORY(); + pb_read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); + POP_AND_PUSH(mem_offset_type, mem_offset_type); + + module->possible_memory_grow = true; +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_memory_grow = true; +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif break; case WASM_OP_I32_CONST: - skip_leb_int32(p, p_end); + pb_read_leb_int32(p, p_end, i32_const); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I32_CONST); + emit_uint32(loader_ctx, i32_const); + } +#else + (void)i32_const; +#endif + PUSH_I32(); break; + case WASM_OP_I64_CONST: - skip_leb_int64(p, p_end); + pb_read_leb_int64(p, p_end, i64_const); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I64, i64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I64_CONST); + emit_uint64(loader_ctx, i64_const); + } +#endif + PUSH_I64(); break; + case WASM_OP_F32_CONST: + CHECK_BUF(p, p_end, sizeof(float32)); p += sizeof(float32); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + bh_memcpy_s((uint8 *)&f32_const, sizeof(float32), p_org, + sizeof(float32)); + GET_CONST_F32_OFFSET(VALUE_TYPE_F32, f32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F32_CONST); + emit_float32(loader_ctx, f32_const); + } +#endif + PUSH_F32(); break; + case WASM_OP_F64_CONST: + CHECK_BUF(p, p_end, sizeof(float64)); p += sizeof(float64); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + /* Some MCU may require 8-byte align */ + bh_memcpy_s((uint8 *)&f64_const, sizeof(float64), p_org, + sizeof(float64)); + GET_CONST_F64_OFFSET(VALUE_TYPE_F64, f64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F64_CONST); + emit_float64(loader_ctx, f64_const); + } +#endif + PUSH_F64(); break; case WASM_OP_I32_EQZ: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_EQ: case WASM_OP_I32_NE: case WASM_OP_I32_LT_S: @@ -2185,7 +15402,13 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_I32_LE_U: case WASM_OP_I32_GE_S: case WASM_OP_I32_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_I64_EQZ: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + case WASM_OP_I64_EQ: case WASM_OP_I64_NE: case WASM_OP_I64_LT_S: @@ -2196,21 +15419,33 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_I64_LE_U: case WASM_OP_I64_GE_S: case WASM_OP_I64_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + case WASM_OP_F32_EQ: case WASM_OP_F32_NE: case WASM_OP_F32_LT: case WASM_OP_F32_GT: case WASM_OP_F32_LE: case WASM_OP_F32_GE: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_F64_EQ: case WASM_OP_F64_NE: case WASM_OP_F64_LT: case WASM_OP_F64_GT: case WASM_OP_F64_LE: case WASM_OP_F64_GE: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + case WASM_OP_I32_CLZ: case WASM_OP_I32_CTZ: case WASM_OP_I32_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_ADD: case WASM_OP_I32_SUB: case WASM_OP_I32_MUL: @@ -2226,9 +15461,15 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_I32_SHR_U: case WASM_OP_I32_ROTL: case WASM_OP_I32_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_I64_CLZ: case WASM_OP_I64_CTZ: case WASM_OP_I64_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + case WASM_OP_I64_ADD: case WASM_OP_I64_SUB: case WASM_OP_I64_MUL: @@ -2244,6 +15485,9 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_I64_SHR_U: case WASM_OP_I64_ROTL: case WASM_OP_I64_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + case WASM_OP_F32_ABS: case WASM_OP_F32_NEG: case WASM_OP_F32_CEIL: @@ -2251,6 +15495,9 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_F32_TRUNC: case WASM_OP_F32_NEAREST: case WASM_OP_F32_SQRT: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + case WASM_OP_F32_ADD: case WASM_OP_F32_SUB: case WASM_OP_F32_MUL: @@ -2258,6 +15505,9 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_F32_MIN: case WASM_OP_F32_MAX: case WASM_OP_F32_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + case WASM_OP_F64_ABS: case WASM_OP_F64_NEG: case WASM_OP_F64_CEIL: @@ -2265,6 +15515,9 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_F64_TRUNC: case WASM_OP_F64_NEAREST: case WASM_OP_F64_SQRT: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + case WASM_OP_F64_ADD: case WASM_OP_F64_SUB: case WASM_OP_F64_MUL: @@ -2272,1291 +15525,2416 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_F64_MIN: case WASM_OP_F64_MAX: case WASM_OP_F64_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + case WASM_OP_I32_WRAP_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + case WASM_OP_I32_TRUNC_S_F32: case WASM_OP_I32_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_TRUNC_S_F64: case WASM_OP_I32_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + case WASM_OP_I64_EXTEND_S_I32: case WASM_OP_I64_EXTEND_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + case WASM_OP_I64_TRUNC_S_F32: case WASM_OP_I64_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + case WASM_OP_I64_TRUNC_S_F64: case WASM_OP_I64_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + case WASM_OP_F32_CONVERT_S_I32: case WASM_OP_F32_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + case WASM_OP_F32_CONVERT_S_I64: case WASM_OP_F32_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F32); + break; + case WASM_OP_F32_DEMOTE_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F32); + break; + case WASM_OP_F64_CONVERT_S_I32: case WASM_OP_F64_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F64); + break; + case WASM_OP_F64_CONVERT_S_I64: case WASM_OP_F64_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + case WASM_OP_F64_PROMOTE_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F64); + break; + case WASM_OP_I32_REINTERPRET_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_I64_REINTERPRET_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + case WASM_OP_F32_REINTERPRET_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + case WASM_OP_F64_REINTERPRET_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); break; - default: - if (error_buf) - snprintf(error_buf, error_buf_size, - "WASM loader find block addr failed: " - "invalid opcode %02x.", opcode); - return false; - } - } + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; - (void)u8; - return false; -} + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; -#define REF_I32 VALUE_TYPE_I32 -#define REF_F32 VALUE_TYPE_F32 -#define REF_I64_1 VALUE_TYPE_I64 -#define REF_I64_2 VALUE_TYPE_I64 -#define REF_F64_1 VALUE_TYPE_F64 -#define REF_F64_2 VALUE_TYPE_F64 +#if WASM_ENABLE_GC != 0 + case WASM_OP_GC_PREFIX: + { + uint32 opcode1; -typedef struct BranchBlock { - uint8 block_type; - uint8 return_type; - bool is_block_reachable; - uint8 *start_addr; - uint8 *else_addr; - uint8 *end_addr; - uint32 stack_cell_num; -} BranchBlock; + pb_read_leb_uint32(p, p_end, opcode1); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, ((uint8)opcode1)); +#endif -static void* -memory_realloc(void *mem_old, uint32 size_old, uint32 size_new) -{ - uint8 *mem_new; - bh_assert(size_new > size_old); - if ((mem_new = wasm_malloc(size_new))) { - bh_memcpy_s(mem_new, size_new, mem_old, size_old); - memset(mem_new + size_old, 0, size_new - size_old); - wasm_free(mem_old); - } - return mem_new; -} + switch (opcode1) { + case WASM_OP_STRUCT_NEW: + case WASM_OP_STRUCT_NEW_DEFAULT: + { + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, type_idx); +#endif + if (!check_type_index(module, module->type_count, + type_idx, error_buf, + error_buf_size)) { + goto fail; + } + if (module->types[type_idx]->type_flag + != WASM_TYPE_STRUCT) { + set_error_buf(error_buf, error_buf_size, + "unknown struct type"); + goto fail; + } -#define MEM_REALLOC(mem, size_old, size_new) do { \ - void *mem_new = memory_realloc(mem, size_old, size_new);\ - if (!mem_new) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM loader prepare bytecode failed: " \ - "allocate memory failed."); \ - goto fail; \ - } \ - mem = mem_new; \ - } while (0) + if (opcode1 == WASM_OP_STRUCT_NEW) { + int32 j, k; + uint8 value_type; + uint32 ref_type_struct_size; + WASMStructType *struct_type = + (WASMStructType *)module->types[type_idx]; + + k = struct_type->ref_type_map_count - 1; + for (j = struct_type->field_count - 1; j >= 0; + j--) { + value_type = struct_type->fields[j].field_type; + if (wasm_is_type_reftype(value_type)) { + if (wasm_is_type_multi_byte_type( + value_type)) { + ref_type_struct_size = + wasm_reftype_struct_size( + struct_type->ref_type_maps[k] + .ref_type); + bh_memcpy_s( + &wasm_ref_type, + (uint32)sizeof(WASMRefType), + struct_type->ref_type_maps[k] + .ref_type, + ref_type_struct_size); + k--; + } + POP_REF(value_type); + } + else { + switch (value_type) { + case VALUE_TYPE_I32: + case PACKED_TYPE_I8: + case PACKED_TYPE_I16: + POP_I32(); + break; + case VALUE_TYPE_I64: + POP_I64(); + break; + case VALUE_TYPE_F32: + POP_F32(); + break; + case VALUE_TYPE_F64: + POP_F64(); + break; + default: + set_error_buf(error_buf, + error_buf_size, + "unknown type"); + goto fail; + } + } + } + } -static bool -check_stack_push(uint8 **p_frame_ref_bottom, uint8 **p_frame_ref_boundary, - uint8 **p_frame_ref, uint32 *p_frame_ref_size, - uint32 stack_cell_num, - char *error_buf, uint32 error_buf_size) -{ - if (*p_frame_ref >= *p_frame_ref_boundary) { - MEM_REALLOC(*p_frame_ref_bottom, *p_frame_ref_size, - *p_frame_ref_size + 16); - *p_frame_ref_size += 16; - *p_frame_ref_boundary = *p_frame_ref_bottom + *p_frame_ref_size; - *p_frame_ref = *p_frame_ref_bottom + stack_cell_num; - } - return true; -fail: - return false; -} + /* PUSH struct obj, (ref $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, false, type_idx); + PUSH_REF(wasm_ref_type.ref_type); + break; + } -#define CHECK_STACK_PUSH() do { \ - if (!check_stack_push(&frame_ref_bottom, &frame_ref_boundary,\ - &frame_ref, &frame_ref_size, \ - stack_cell_num, \ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) + case WASM_OP_STRUCT_GET: + case WASM_OP_STRUCT_GET_S: + case WASM_OP_STRUCT_GET_U: + case WASM_OP_STRUCT_SET: + { + WASMStructType *struct_type; + WASMRefType *ref_type = NULL; + uint32 field_idx; + uint8 field_type; + + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, type_idx); +#endif + if (!check_type_index(module, module->type_count, + type_idx, error_buf, + error_buf_size)) { + goto fail; + } + if (module->types[type_idx]->type_flag + != WASM_TYPE_STRUCT) { + set_error_buf(error_buf, error_buf_size, + "unknown struct type"); + goto fail; + } + struct_type = (WASMStructType *)module->types[type_idx]; -static bool -check_stack_pop(uint8 type, uint8 *frame_ref, uint32 stack_cell_num, - char *error_buf, uint32 error_buf_size, - const char *type_str) -{ - if (((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) - && stack_cell_num < 1) - || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) - && stack_cell_num < 2)) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "type mismatch: expect data but stack was empty"); - return false; - } + pb_read_leb_uint32(p, p_end, field_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, field_idx); +#endif + if (field_idx >= struct_type->field_count) { + set_error_buf(error_buf, error_buf_size, + "unknown struct field"); + goto fail; + } - if ((type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) - || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) - || (type == VALUE_TYPE_I64 - && (*(frame_ref - 2) != REF_I64_1 || *(frame_ref - 1) != REF_I64_2)) - || (type == VALUE_TYPE_F64 - && (*(frame_ref - 2) != REF_F64_1 || *(frame_ref - 1) != REF_F64_2))) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, "%s%s%s", - "WASM module load failed: type mismatch: expect ", - type_str, " but got other"); - return false; - } - return true; -} - -#define CHECK_STACK_POP(TYPE, type) do { \ - if (!check_stack_pop(VALUE_TYPE_##TYPE, \ - frame_ref, stack_cell_num, \ - error_buf, error_buf_size, #type)) \ - goto fail; \ - } while (0) - -#define PUSH_I32() do { \ - CHECK_STACK_PUSH(); \ - *frame_ref++ = REF_I32; \ - stack_cell_num++; \ - if (stack_cell_num > max_stack_cell_num) \ - max_stack_cell_num = stack_cell_num; \ - } while (0) - -#define PUSH_F32() do { \ - CHECK_STACK_PUSH(); \ - *frame_ref++ = REF_F32; \ - stack_cell_num++; \ - if (stack_cell_num > max_stack_cell_num) \ - max_stack_cell_num = stack_cell_num; \ - } while (0) - -#define PUSH_I64() do { \ - CHECK_STACK_PUSH(); \ - *frame_ref++ = REF_I64_1; \ - stack_cell_num++; \ - CHECK_STACK_PUSH(); \ - *frame_ref++ = REF_I64_2; \ - stack_cell_num++; \ - if (stack_cell_num > max_stack_cell_num) \ - max_stack_cell_num = stack_cell_num; \ - } while (0) - -#define PUSH_F64() do { \ - CHECK_STACK_PUSH(); \ - *frame_ref++ = REF_F64_1; \ - stack_cell_num++; \ - CHECK_STACK_PUSH(); \ - *frame_ref++ = REF_F64_2; \ - stack_cell_num++; \ - if (stack_cell_num > max_stack_cell_num) \ - max_stack_cell_num = stack_cell_num; \ - } while (0) - -#define POP_I32() do { \ - CHECK_STACK_POP(I32, i32); \ - stack_cell_num--; \ - frame_ref--; \ - } while (0) - -#define POP_I64() do { \ - CHECK_STACK_POP(I64, i64); \ - stack_cell_num -= 2; \ - frame_ref -= 2; \ - } while (0) - -#define POP_F32() do { \ - CHECK_STACK_POP(F32, f32); \ - stack_cell_num--; \ - frame_ref--; \ - } while (0) - -#define POP_F64() do { \ - CHECK_STACK_POP(F64, f64); \ - stack_cell_num -= 2; \ - frame_ref -= 2; \ - } while (0) - -static bool -push_type(uint8 type, uint8 **p_frame_ref_bottom, - uint8 **p_frame_ref_boundary, - uint8 **p_frame_ref, uint32 *p_frame_ref_size, - uint32 *p_stack_cell_num, uint32 *p_max_stack_cell_num, - char *error_buf, uint32 error_buf_size) -{ - uint8 *frame_ref = *p_frame_ref; - uint32 frame_ref_size = *p_frame_ref_size; - uint32 max_stack_cell_num = *p_max_stack_cell_num; - uint32 stack_cell_num = *p_stack_cell_num; - - switch (type) { - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - if (!check_stack_push(p_frame_ref_bottom, p_frame_ref_boundary, - &frame_ref, &frame_ref_size, - stack_cell_num, - error_buf, error_buf_size)) - goto fail; - *frame_ref++ = type; - stack_cell_num++; - if (stack_cell_num > max_stack_cell_num) - max_stack_cell_num = stack_cell_num; - goto handle_i32_f32; -handle_i32_f32: - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - if (!check_stack_push(p_frame_ref_bottom, p_frame_ref_boundary, - &frame_ref, &frame_ref_size, - stack_cell_num, - error_buf, error_buf_size)) - goto fail; - *frame_ref++ = type; - stack_cell_num++; - if (stack_cell_num > max_stack_cell_num) - max_stack_cell_num = stack_cell_num; - break; - } + if (opcode1 == WASM_OP_STRUCT_SET + && !(struct_type->fields[field_idx].field_flags + & 1)) { + set_error_buf(error_buf, error_buf_size, + "field is immutable"); + goto fail; + } - *p_frame_ref = frame_ref; - *p_frame_ref_size = frame_ref_size; - *p_max_stack_cell_num = max_stack_cell_num; - *p_stack_cell_num = stack_cell_num; - return true; -fail: - return false; -} + field_type = struct_type->fields[field_idx].field_type; + if (is_packed_type(field_type)) { + if (opcode1 == WASM_OP_STRUCT_GET) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + else { + field_type = VALUE_TYPE_I32; + } + } + if (wasm_is_type_multi_byte_type(field_type)) { + ref_type = wasm_reftype_map_find( + struct_type->ref_type_maps, + struct_type->ref_type_map_count, field_idx); + bh_assert(ref_type); + } + if (opcode1 == WASM_OP_STRUCT_SET) { + /* POP field */ + if (wasm_is_type_multi_byte_type(field_type)) { + bh_memcpy_s(&wasm_ref_type, + (uint32)sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + } + POP_REF(field_type); + /* POP struct obj, (ref null $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, true, type_idx); + POP_REF(wasm_ref_type.ref_type); + } + else { + /* POP struct obj, (ref null $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, true, type_idx); + POP_REF(wasm_ref_type.ref_type); + /* PUSH field */ + if (wasm_is_type_multi_byte_type(field_type)) { + bh_memcpy_s(&wasm_ref_type, + (uint32)sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + } + PUSH_REF(field_type); + } + break; + } -#define PUSH_TYPE(type) do { \ - if (!push_type(type, &frame_ref_bottom, \ - &frame_ref_boundary, \ - &frame_ref, &frame_ref_size, \ - &stack_cell_num, &max_stack_cell_num, \ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) + case WASM_OP_ARRAY_NEW: + case WASM_OP_ARRAY_NEW_DEFAULT: + case WASM_OP_ARRAY_NEW_FIXED: + case WASM_OP_ARRAY_NEW_DATA: + case WASM_OP_ARRAY_NEW_ELEM: + { + WASMArrayType *array_type; + uint8 elem_type; + uint32 u32 = 0; + + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, type_idx); +#endif + if (opcode1 == WASM_OP_ARRAY_NEW_FIXED + || opcode1 == WASM_OP_ARRAY_NEW_DATA + || opcode1 == WASM_OP_ARRAY_NEW_ELEM) { + pb_read_leb_uint32(p, p_end, u32); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, u32); +#endif + } -static bool -pop_type(uint8 type, uint8 **p_frame_ref, uint32 *p_stack_cell_num, - char *error_buf, uint32 error_buf_size) -{ - char *type_str[] = { "f64", "f32", "i64", "i32" }; - switch (type) { - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - if (!check_stack_pop(type, *p_frame_ref, *p_stack_cell_num, - error_buf, error_buf_size, - type_str[type - VALUE_TYPE_F64])) - return false; - *p_frame_ref -= 2; - *p_stack_cell_num -= 2; - break; - case VALUE_TYPE_I32: - case VALUE_TYPE_F32: - if (!check_stack_pop(type, *p_frame_ref, *p_stack_cell_num, - error_buf, error_buf_size, - type_str[type - VALUE_TYPE_F64])) - return false; - *p_frame_ref -= 1; - *p_stack_cell_num -= 1; - break; - } - return true; -} + if (!check_array_type(module, type_idx, error_buf, + error_buf_size)) { + goto fail; + } -#define POP_TYPE(type) do { \ - if (!pop_type(type, &frame_ref, &stack_cell_num,\ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define CHECK_CSP_PUSH() do { \ - if (frame_csp >= frame_csp_boundary) { \ - MEM_REALLOC(frame_csp_bottom, frame_csp_size, \ - (uint32)(frame_csp_size \ - + 8 * sizeof(BranchBlock))); \ - frame_csp_size += (uint32)(8 * sizeof(BranchBlock)); \ - frame_csp_boundary = frame_csp_bottom + \ - frame_csp_size / sizeof(BranchBlock); \ - frame_csp = frame_csp_bottom + csp_num; \ - } \ - } while (0) - -#define CHECK_CSP_POP() do { \ - if (csp_num < 1) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM module load failed: type mismatch: "\ - "expect data but block stack was empty"); \ - goto fail; \ - } \ - } while (0) - -#define PUSH_CSP(type, ret_type, _start_addr) do { \ - CHECK_CSP_PUSH(); \ - frame_csp->block_type = type; \ - frame_csp->is_block_reachable = false; \ - frame_csp->return_type = ret_type; \ - frame_csp->start_addr = _start_addr; \ - frame_csp->else_addr = NULL; \ - frame_csp->end_addr = NULL; \ - frame_csp->stack_cell_num = stack_cell_num; \ - frame_csp++; \ - csp_num++; \ - if (csp_num > max_csp_num) \ - max_csp_num = csp_num; \ - } while (0) - -#define POP_CSP() do { \ - CHECK_CSP_POP(); \ - frame_csp--; \ - csp_num--; \ - } while (0) - -#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() do { \ - read_leb_uint32(p, p_end, local_idx); \ - if (local_idx >= param_count + local_count) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM module load failed: " \ - "local index out of range"); \ - goto fail; \ - } \ - local_type = local_idx < param_count \ - ? param_types[local_idx] \ - : local_types[local_idx - param_count]; \ - local_offset = local_offsets[local_idx]; \ - } while (0) - -#define CHECK_BR(depth) do { \ - if (csp_num < depth + 1) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM module load failed: type mismatch: " \ - "unexpected end of section or function"); \ - goto fail; \ - } \ - if ((frame_csp - (depth + 1))->block_type != BLOCK_TYPE_LOOP) { \ - uint8 tmp_ret_type = (frame_csp - (depth + 1))->return_type; \ - if ((tmp_ret_type == VALUE_TYPE_I32 \ - && (stack_cell_num < 1 || *(frame_ref - 1) != REF_I32)) \ - || (tmp_ret_type == VALUE_TYPE_F32 \ - && (stack_cell_num < 1 || *(frame_ref - 1) != REF_F32))\ - || (tmp_ret_type == VALUE_TYPE_I64 \ - && (stack_cell_num < 2 \ - || *(frame_ref - 2) != REF_I64_1 \ - || *(frame_ref - 1) != REF_I64_2)) \ - || (tmp_ret_type == VALUE_TYPE_F64 \ - && (stack_cell_num < 2 \ - || *(frame_ref - 2) != REF_F64_1 \ - || *(frame_ref - 1) != REF_F64_2))) { \ - set_error_buf(error_buf, error_buf_size, \ - "WASM module load failed: type mismatch: " \ - "expect data but stack was empty or other type"); \ - goto fail; \ - } \ - (frame_csp - (depth + 1))->is_block_reachable = true; \ - } \ - } while (0) - -static bool -check_memory(WASMModule *module, - char *error_buf, uint32 error_buf_size) -{ - if (module->memory_count == 0 - && module->import_memory_count == 0) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "load or store in module without default memory"); - return false; - } - return true; -} + if (opcode1 != WASM_OP_ARRAY_NEW_FIXED) { + /* length */ + POP_I32(); + } -#define CHECK_MEMORY() do { \ - if (!check_memory(module, error_buf, error_buf_size)) \ - goto fail; \ - } while (0) + array_type = (WASMArrayType *)module->types[type_idx]; + elem_type = array_type->elem_type; -static bool -wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, - char *error_buf, uint32 error_buf_size) -{ - uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; - uint8 *frame_ref_bottom = NULL, *frame_ref_boundary, *frame_ref; - BranchBlock *frame_csp_bottom = NULL, *frame_csp_boundary, *frame_csp; - uint32 param_count, local_count, global_count; - uint32 max_stack_cell_num = 0, max_csp_num = 0; - uint32 stack_cell_num = 0, csp_num = 0; - uint32 frame_ref_size, frame_csp_size; - uint8 *param_types, ret_type, *local_types, local_type, global_type; - uint16 *local_offsets, local_offset; - uint32 count, i, local_idx, global_idx, depth, u32; - int32 i32, i32_const = 0; - int64 i64; - uint8 opcode, u8, block_return_type; - bool return_value = false, is_i32_const = false; + if (opcode1 == WASM_OP_ARRAY_NEW + || opcode1 == WASM_OP_ARRAY_NEW_FIXED) { + if (wasm_is_type_multi_byte_type(elem_type)) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + array_type->elem_ref_type, + wasm_reftype_struct_size( + array_type->elem_ref_type)); + } + if (is_packed_type(elem_type)) { + elem_type = VALUE_TYPE_I32; + } - global_count = module->import_global_count + module->global_count; + if (opcode1 == WASM_OP_ARRAY_NEW_FIXED) { + uint32 N = u32; + for (i = 0; i < N; i++) { + if (wasm_is_type_multi_byte_type( + elem_type)) { + bh_memcpy_s( + &wasm_ref_type, sizeof(WASMRefType), + array_type->elem_ref_type, + wasm_reftype_struct_size( + array_type->elem_ref_type)); + } + POP_REF(elem_type); + } + } + else + POP_REF(elem_type); + } + else if (opcode1 == WASM_OP_ARRAY_NEW_DATA) { + /* offset of data segment */ + POP_I32(); + + if (u32 >= module->data_seg_count) { + set_error_buf(error_buf, error_buf_size, + "unknown data segment"); + goto fail; + } - param_count = func->func_type->param_count; - param_types = func->func_type->types; - ret_type = func->func_type->result_count - ? param_types[param_count] : VALUE_TYPE_VOID; + if (wasm_is_type_reftype(elem_type)) { + set_error_buf(error_buf, error_buf_size, + "array elem type mismatch"); + goto fail; + } + } + else if (opcode1 == WASM_OP_ARRAY_NEW_ELEM) { + WASMTableSeg *table_seg = + module->table_segments + u32; - local_count = func->local_count; - local_types = func->local_types; - local_offsets = func->local_offsets; + /* offset of element segment */ + POP_I32(); - frame_ref_size = 32; - if (!(frame_ref_bottom = frame_ref = wasm_malloc(frame_ref_size))) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "allocate memory failed"); - goto fail; - } - memset(frame_ref_bottom, 0, frame_ref_size); - frame_ref_boundary = frame_ref_bottom + frame_ref_size; + if (u32 >= module->table_seg_count) { + set_error_buf(error_buf, error_buf_size, + "unknown element segment"); + goto fail; + } + if (!wasm_reftype_is_subtype_of( + table_seg->elem_type, + table_seg->elem_ref_type, elem_type, + array_type->elem_ref_type, module->types, + module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "array elem type mismatch"); + goto fail; + } + } - frame_csp_size = sizeof(BranchBlock) * 8; - if (!(frame_csp_bottom = frame_csp = wasm_malloc(frame_csp_size))) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "allocate memory failed"); - goto fail; - } + /* PUSH array obj, (ref $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, false, type_idx); + PUSH_REF(wasm_ref_type.ref_type); + break; + } - memset(frame_csp_bottom, 0, frame_csp_size); - frame_csp_boundary = frame_csp_bottom + 8; + case WASM_OP_ARRAY_GET: + case WASM_OP_ARRAY_GET_S: + case WASM_OP_ARRAY_GET_U: + case WASM_OP_ARRAY_SET: + { + uint8 elem_type; + WASMArrayType *array_type; + WASMRefType *ref_type = NULL; + + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, type_idx); +#endif + if (!check_array_type(module, type_idx, error_buf, + error_buf_size)) { + goto fail; + } + array_type = (WASMArrayType *)module->types[type_idx]; - PUSH_CSP(BLOCK_TYPE_FUNCTION, ret_type, p); - (frame_csp - 1)->is_block_reachable = true; + if (opcode1 == WASM_OP_ARRAY_SET + && !(array_type->elem_flags & 1)) { + set_error_buf(error_buf, error_buf_size, + "array is immutable"); + goto fail; + } - while (p < p_end) { - opcode = *p++; + elem_type = array_type->elem_type; + if (is_packed_type(elem_type)) { + if (opcode1 != WASM_OP_ARRAY_GET_S + && opcode1 != WASM_OP_ARRAY_GET_U + && opcode1 != WASM_OP_ARRAY_SET) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + else { + elem_type = VALUE_TYPE_I32; + } + } + ref_type = array_type->elem_ref_type; + + if (opcode1 == WASM_OP_ARRAY_SET) { + /* POP elem to set */ + if (wasm_is_type_multi_byte_type(elem_type)) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + } + POP_REF(elem_type); + } + /* elem idx */ + POP_I32(); + /* POP array obj, (ref null $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, true, type_idx); + POP_REF(wasm_ref_type.ref_type); + if (opcode1 != WASM_OP_ARRAY_SET) { + /* PUSH elem */ + if (wasm_is_type_multi_byte_type(elem_type)) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + } + PUSH_REF(elem_type); + } + break; + } - switch (opcode) { - case WASM_OP_UNREACHABLE: - goto handle_next_reachable_block; + case WASM_OP_ARRAY_LEN: + { + POP_REF(REF_TYPE_ARRAYREF); + /* length */ + PUSH_I32(); + break; + } - case WASM_OP_NOP: - break; + case WASM_OP_ARRAY_FILL: + { + WASMArrayType *array_type; + uint8 elem_type; + /* typeidx */ + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, type_idx); +#endif + if (!check_array_type(module, type_idx, error_buf, + error_buf_size)) { + goto fail; + } - case WASM_OP_BLOCK: - /* 0x40/0x7F/0x7E/0x7D/0x7C */ - block_return_type = read_uint8(p); - PUSH_CSP(BLOCK_TYPE_BLOCK, block_return_type, p); - break; + array_type = (WASMArrayType *)module->types[type_idx]; + if (!(array_type->elem_flags & 1)) { + set_error_buf(error_buf, error_buf_size, + "array is immutable"); + goto fail; + } - case WASM_OP_LOOP: - /* 0x40/0x7F/0x7E/0x7D/0x7C */ - block_return_type = read_uint8(p); - PUSH_CSP(BLOCK_TYPE_LOOP, block_return_type, p); - break; + elem_type = array_type->elem_type; + if (is_packed_type(elem_type)) { + elem_type = VALUE_TYPE_I32; + } - case WASM_OP_IF: - POP_I32(); - /* 0x40/0x7F/0x7E/0x7D/0x7C */ - block_return_type = read_uint8(p); - PUSH_CSP(BLOCK_TYPE_IF, block_return_type, p); - if (!is_i32_const) - (frame_csp - 1)->is_block_reachable = true; - else { - if (!i32_const) { - if(!wasm_loader_find_block_addr(module, - (frame_csp - 1)->start_addr, - p_end, - (frame_csp - 1)->block_type, - &(frame_csp - 1)->else_addr, - &(frame_csp - 1)->end_addr, - error_buf, error_buf_size)) - goto fail; - - if ((frame_csp - 1)->else_addr) - p = (frame_csp - 1)->else_addr; - else - p = (frame_csp - 1)->end_addr; + POP_I32(); /* length */ +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(elem_type); +#endif + POP_TYPE(elem_type); + POP_I32(); /* start */ + /* POP array obj, (ref null $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, true, type_idx); + POP_REF(wasm_ref_type.ref_type); + + break; } - } - break; - case WASM_OP_ELSE: - if (csp_num < 2 - || (frame_csp - 1)->block_type != BLOCK_TYPE_IF) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "opcode else found without matched opcode if"); - goto fail; - } + case WASM_OP_ARRAY_COPY: + { + uint32 src_type_idx; + uint8 src_elem_type, dst_elem_type; + WASMRefType src_ref_type = { 0 }, + *src_elem_ref_type = NULL; + WASMRefType dst_ref_type = { 0 }, + *dst_elem_ref_type = NULL; + WASMArrayType *array_type; + + /* typeidx1 */ + pb_read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, type_idx); +#endif + /* typeidx2 */ + pb_read_leb_uint32(p, p_end, src_type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, src_type_idx); +#endif + if (!check_array_type(module, type_idx, error_buf, + error_buf_size)) { + goto fail; + } + + if (!check_array_type(module, src_type_idx, error_buf, + error_buf_size)) { + goto fail; + } + + POP_I32(); + POP_I32(); + /* POP array obj, (ref null $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, true, src_type_idx); + POP_REF(wasm_ref_type.ref_type); + bh_memcpy_s(&src_ref_type, (uint32)sizeof(WASMRefType), + &wasm_ref_type, + wasm_reftype_struct_size(&wasm_ref_type)); + POP_I32(); + /* POP array obj, (ref null $t) */ + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, true, type_idx); + POP_REF(wasm_ref_type.ref_type); + bh_memcpy_s(&dst_ref_type, (uint32)sizeof(WASMRefType), + &wasm_ref_type, + wasm_reftype_struct_size(&wasm_ref_type)); + + array_type = (WASMArrayType *)module->types[type_idx]; + if (!(array_type->elem_flags & 1)) { + set_error_buf(error_buf, error_buf_size, + "destination array is immutable"); + goto fail; + } + + dst_elem_type = array_type->elem_type; + if (wasm_is_type_multi_byte_type(dst_elem_type)) { + dst_elem_ref_type = array_type->elem_ref_type; + } + + array_type = + (WASMArrayType *)module->types[src_type_idx]; + src_elem_type = array_type->elem_type; + if (wasm_is_type_multi_byte_type(src_elem_type)) { + src_elem_ref_type = array_type->elem_ref_type; + } + + if (!wasm_reftype_is_subtype_of( + src_elem_type, src_elem_ref_type, dst_elem_type, + dst_elem_ref_type, module->types, + module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "array types do not match"); + goto fail; + } + + break; + } + + case WASM_OP_REF_I31: + { + POP_I32(); + wasm_set_refheaptype_common( + &wasm_ref_type.ref_ht_common, false, HEAP_TYPE_I31); + PUSH_REF(wasm_ref_type.ref_type); + break; + } + + case WASM_OP_I31_GET_S: + case WASM_OP_I31_GET_U: + { + POP_REF(REF_TYPE_I31REF); + PUSH_I32(); + break; + } + + case WASM_OP_REF_TEST: + case WASM_OP_REF_CAST: + case WASM_OP_REF_TEST_NULLABLE: + case WASM_OP_REF_CAST_NULLABLE: + { + uint8 type; + + pb_read_leb_int32(p, p_end, heap_type); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, (uint32)heap_type); +#endif + if (heap_type >= 0) { + if (!check_type_index(module, module->type_count, + heap_type, error_buf, + error_buf_size)) { + goto fail; + } + } + else { + if (!wasm_is_valid_heap_type(heap_type)) { + set_error_buf(error_buf, error_buf_size, + "unknown type"); + goto fail; + } + } + if (!wasm_loader_pop_heap_obj(loader_ctx, &type, + &wasm_ref_type, error_buf, + error_buf_size)) { + goto fail; + } + if (opcode1 == WASM_OP_REF_TEST + || opcode1 == WASM_OP_REF_TEST_NULLABLE) + PUSH_I32(); + else { + bool nullable = + (opcode1 == WASM_OP_REF_CAST_NULLABLE) ? true + : false; + if (heap_type >= 0 || !nullable) { + wasm_set_refheaptype_typeidx( + &wasm_ref_type.ref_ht_typeidx, nullable, + heap_type); + PUSH_REF(wasm_ref_type.ref_type); + } + else { + PUSH_REF((uint8)((int32)0x80 + heap_type)); + } + } + break; + } + + case WASM_OP_BR_ON_CAST: + case WASM_OP_BR_ON_CAST_FAIL: + { + WASMRefType ref_type_tmp = { 0 }, ref_type1 = { 0 }, + ref_type2 = { 0 }, ref_type_diff = { 0 }; + uint8 type_tmp, castflags; + uint32 depth; + int32 heap_type_dst; + bool src_nullable, dst_nullable; + + CHECK_BUF(p, p_end, 1); + castflags = read_uint8(p); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Emit heap_type firstly */ + emit_byte(loader_ctx, castflags); +#endif + + p_org = p; + pb_read_leb_uint32(p, p_end, depth); + pb_read_leb_int32(p, p_end, heap_type); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Emit heap_type firstly */ + emit_uint32(loader_ctx, (uint32)heap_type); +#endif + pb_read_leb_int32(p, p_end, heap_type_dst); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Emit heap_type firstly */ + emit_uint32(loader_ctx, (uint32)heap_type_dst); +#endif + (void)depth; + + /* + * castflags should be 0~3: + * 0: (non-null, non-null) + * 1: (null, non-null) + * 2: (non-null, null) + * 3: (null, null) + */ + if (castflags > 3) { + set_error_buf(error_buf, error_buf_size, + "invalid castflags"); + break; + } + src_nullable = + (castflags == 1) || (castflags == 3) ? true : false; + dst_nullable = + (castflags == 2) || (castflags == 3) ? true : false; + + /* Pop and backup the stack top's ref type */ + if (!wasm_loader_pop_heap_obj(loader_ctx, &type_tmp, + &ref_type_tmp, error_buf, + error_buf_size)) { + goto fail; + } + + /* The reference type rt1 must be valid */ + if (!init_ref_type(module, &ref_type1, src_nullable, + heap_type, error_buf, + error_buf_size)) { + goto fail; + } + + /* The reference type rt2 must be valid. */ + if (!init_ref_type(module, &ref_type2, dst_nullable, + heap_type_dst, error_buf, + error_buf_size)) { + goto fail; + } + + calculate_reftype_diff(&ref_type_diff, &ref_type1, + &ref_type2); + + /* The reference type rt2 must match rt1. */ + if (!wasm_reftype_is_subtype_of( + ref_type2.ref_type, &ref_type2, + ref_type1.ref_type, &ref_type1, module->types, + module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + + p = p_org; + /* Push ref type casted for branch block check */ + if (opcode1 == WASM_OP_BR_ON_CAST) { + /* The reference type rt2 must match rt′. */ + type_tmp = ref_type2.ref_type; + if (wasm_is_type_multi_byte_type(type_tmp)) { + bh_memcpy_s( + &wasm_ref_type, + wasm_reftype_struct_size(&ref_type2), + &ref_type2, + wasm_reftype_struct_size(&ref_type2)); + } + } + else { + /* The reference type rt′1 must match rt′. */ + type_tmp = ref_type_diff.ref_type; + if (wasm_is_type_multi_byte_type(type_tmp)) { + bh_memcpy_s( + &wasm_ref_type, + wasm_reftype_struct_size(&ref_type_diff), + &ref_type_diff, + wasm_reftype_struct_size(&ref_type_diff)); + } + } + PUSH_REF(type_tmp); + if (!(frame_csp_tmp = check_branch_block( + loader_ctx, &p, p_end, opcode, error_buf, + error_buf_size))) { + goto fail; + } + /* Ignore heap_types */ + skip_leb_uint32(p, p_end); + skip_leb_uint32(p, p_end); + + /* Restore the original stack top's ref type */ + POP_REF(type_tmp); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Erase the opnd offset emitted by POP_REF() */ + wasm_loader_emit_backspace(loader_ctx, sizeof(uint16)); +#endif + if (opcode1 == WASM_OP_BR_ON_CAST) { + type_tmp = ref_type_diff.ref_type; + if (wasm_is_type_multi_byte_type(type_tmp)) { + bh_memcpy_s( + &wasm_ref_type, + wasm_reftype_struct_size(&ref_type_diff), + &ref_type_diff, + wasm_reftype_struct_size(&ref_type_diff)); + } + } + else { + type_tmp = ref_type2.ref_type; + if (wasm_is_type_multi_byte_type(type_tmp)) { + bh_memcpy_s( + &wasm_ref_type, + wasm_reftype_struct_size(&ref_type2), + &ref_type2, + wasm_reftype_struct_size(&ref_type2)); + } + } + PUSH_REF(type_tmp); + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Erase the opnd offset emitted by PUSH_REF() */ + wasm_loader_emit_backspace(loader_ctx, sizeof(uint16)); +#endif + break; + } - (frame_csp - 1)->else_addr = p - 1; - stack_cell_num = (frame_csp - 1)->stack_cell_num; - frame_ref = frame_ref_bottom + stack_cell_num; - break; + case WASM_OP_ANY_CONVERT_EXTERN: + { + uint8 type; - case WASM_OP_END: - { - POP_CSP(); + if (!wasm_loader_pop_heap_obj(loader_ctx, &type, + &wasm_ref_type, error_buf, + error_buf_size)) { + goto fail; + } + if (!(type == REF_TYPE_EXTERNREF + || (type == REF_TYPE_HT_NON_NULLABLE + && wasm_ref_type.ref_ht_common.heap_type + == HEAP_TYPE_EXTERN) + || type == VALUE_TYPE_ANY)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } - POP_TYPE(frame_csp->return_type); - PUSH_TYPE(frame_csp->return_type); + if (type == REF_TYPE_EXTERNREF) + type = REF_TYPE_ANYREF; + else { + wasm_ref_type.ref_ht_common.heap_type = + HEAP_TYPE_ANY; + } + PUSH_REF(type); + break; + } - if (csp_num > 0) { - frame_csp->end_addr = p - 1; - } - else { - /* end of function block, function will return, - ignore the following bytecodes */ - p = p_end; - } - break; - } + case WASM_OP_EXTERN_CONVERT_ANY: + { + uint8 type; - case WASM_OP_BR: - { - read_leb_uint32(p, p_end, depth); - CHECK_BR(depth); + if (!wasm_loader_pop_heap_obj(loader_ctx, &type, + &wasm_ref_type, error_buf, + error_buf_size)) { + goto fail; + } + if (type == REF_TYPE_EXTERNREF + || ((type == REF_TYPE_HT_NULLABLE + || type == REF_TYPE_HT_NON_NULLABLE) + && wasm_ref_type.ref_ht_common.heap_type + == HEAP_TYPE_EXTERN)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } -handle_next_reachable_block: - for (i = 1; i <= csp_num; i++) - if ((frame_csp - i)->is_block_reachable) + if (type != REF_TYPE_HT_NON_NULLABLE) { + /* push (ref null extern) */ + type = REF_TYPE_EXTERNREF; + } + else { + /* push (ref extern) */ + type = REF_TYPE_HT_NON_NULLABLE; + wasm_set_refheaptype_common( + &wasm_ref_type.ref_ht_common, false, + HEAP_TYPE_EXTERN); + } + PUSH_REF(type); break; + } - block_return_type = (frame_csp - i)->return_type; +#if WASM_ENABLE_STRINGREF != 0 + case WASM_OP_STRING_NEW_UTF8: + case WASM_OP_STRING_NEW_WTF16: + case WASM_OP_STRING_NEW_LOSSY_UTF8: + case WASM_OP_STRING_NEW_WTF8: + { +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif - if(!wasm_loader_find_block_addr(module, - (frame_csp - i)->start_addr, - p_end, - (frame_csp - i)->block_type, - &(frame_csp - i)->else_addr, - &(frame_csp - i)->end_addr, - error_buf, error_buf_size)) - goto fail; + pb_read_leb_uint32(p, p_end, memidx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, (uint32)memidx); +#endif + POP_I32(); + POP_I32(); + PUSH_REF(REF_TYPE_STRINGREF); + break; + } + case WASM_OP_STRING_CONST: + { + uint32 contents; - stack_cell_num = (frame_csp - i)->stack_cell_num; - frame_ref = frame_ref_bottom + stack_cell_num; - csp_num -= i - 1; - frame_csp -= i - 1; + pb_read_leb_uint32(p, p_end, contents); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, (uint32)contents); +#endif + PUSH_REF(REF_TYPE_STRINGREF); + (void)contents; + break; + } + case WASM_OP_STRING_MEASURE_UTF8: + case WASM_OP_STRING_MEASURE_WTF8: + case WASM_OP_STRING_MEASURE_WTF16: + { + POP_STRINGREF(); + PUSH_I32(); + break; + } + case WASM_OP_STRING_ENCODE_UTF8: + case WASM_OP_STRING_ENCODE_WTF16: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8: + case WASM_OP_STRING_ENCODE_WTF8: + { +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif - if ((frame_csp - 1)->block_type == BLOCK_TYPE_IF - && (frame_csp - 1)->else_addr != NULL - && p <= (frame_csp - 1)->else_addr) - p = (frame_csp - 1)->else_addr; - else { - p = (frame_csp - 1)->end_addr; - PUSH_TYPE(block_return_type); - } + pb_read_leb_uint32(p, p_end, memidx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, (uint32)memidx); +#endif + POP_I32(); + POP_STRINGREF(); + PUSH_I32(); + break; + } + case WASM_OP_STRING_CONCAT: + { + POP_STRINGREF(); + POP_STRINGREF(); + PUSH_REF(REF_TYPE_STRINGREF); + break; + } + case WASM_OP_STRING_EQ: + { + POP_STRINGREF(); + POP_STRINGREF(); + PUSH_I32(); + break; + } + case WASM_OP_STRING_IS_USV_SEQUENCE: + { + POP_STRINGREF(); + PUSH_I32(); + break; + } + case WASM_OP_STRING_AS_WTF8: + { + POP_STRINGREF(); + PUSH_REF(REF_TYPE_STRINGVIEWWTF8); + break; + } + case WASM_OP_STRINGVIEW_WTF8_ADVANCE: + { + POP_I32(); + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWWTF8); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8: + case WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8: + { +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif - break; - } + pb_read_leb_uint32(p, p_end, memidx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, (uint32)memidx); +#endif + POP_I32(); + POP_I32(); + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWWTF8); + PUSH_I32(); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_WTF8_SLICE: + { + POP_I32(); + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWWTF8); + PUSH_REF(REF_TYPE_STRINGREF); + break; + } + case WASM_OP_STRING_AS_WTF16: + { + POP_STRINGREF(); + PUSH_REF(REF_TYPE_STRINGVIEWWTF16); + break; + } + case WASM_OP_STRINGVIEW_WTF16_LENGTH: + { + POP_REF(REF_TYPE_STRINGVIEWWTF16); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_WTF16_GET_CODEUNIT: + { + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWWTF16); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_WTF16_ENCODE: + { +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif - case WASM_OP_BR_IF: - read_leb_uint32(p, p_end, depth); - POP_I32(); - CHECK_BR(depth); - if (!is_i32_const) - (frame_csp - (depth + 1))->is_block_reachable = true; - else { - if (i32_const) - goto handle_next_reachable_block; + pb_read_leb_uint32(p, p_end, memidx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, (uint32)memidx); +#endif + POP_I32(); + POP_I32(); + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWWTF16); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_WTF16_SLICE: + { + POP_I32(); + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWWTF16); + PUSH_REF(REF_TYPE_STRINGREF); + break; + } + case WASM_OP_STRING_AS_ITER: + { + POP_STRINGREF(); + PUSH_REF(REF_TYPE_STRINGVIEWITER); + break; + } + case WASM_OP_STRINGVIEW_ITER_NEXT: + { + POP_REF(REF_TYPE_STRINGVIEWITER); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_ITER_ADVANCE: + case WASM_OP_STRINGVIEW_ITER_REWIND: + { + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWITER); + PUSH_I32(); + break; + } + case WASM_OP_STRINGVIEW_ITER_SLICE: + { + POP_I32(); + POP_REF(REF_TYPE_STRINGVIEWITER); + PUSH_REF(REF_TYPE_STRINGREF); + break; + } + case WASM_OP_STRING_NEW_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF16_ARRAY: + case WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_NEW_WTF8_ARRAY: + { + POP_I32(); + POP_I32(); + POP_REF(REF_TYPE_ARRAYREF); + PUSH_REF(REF_TYPE_STRINGREF); + break; + } + case WASM_OP_STRING_ENCODE_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF16_ARRAY: + case WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY: + case WASM_OP_STRING_ENCODE_WTF8_ARRAY: + { + POP_I32(); + POP_REF(REF_TYPE_ARRAYREF); + POP_STRINGREF(); + PUSH_I32(); + break; + } +#endif /* end of WASM_ENABLE_STRINGREF != 0 */ + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x %02x", "unsupported opcode", + 0xfb, opcode1); + goto fail; } break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ - case WASM_OP_BR_TABLE: + case WASM_OP_MISC_PREFIX: { - read_leb_uint32(p, p_end, count); - POP_I32(); + uint32 opcode1; - /* TODO: check the const */ - for (i = 0; i <= count; i++) { - read_leb_uint32(p, p_end, depth); - CHECK_BR(depth); - } - goto handle_next_reachable_block; - } + pb_read_leb_uint32(p, p_end, opcode1); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, ((uint8)opcode1)); +#endif + switch (opcode1) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + pb_read_leb_uint32(p, p_end, data_seg_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, data_seg_idx); +#endif + if (module->import_memory_count == 0 + && module->memory_count == 0) { + set_error_buf(error_buf, error_buf_size, + "unknown memory 0"); + goto fail; + } - case WASM_OP_RETURN: - { - POP_TYPE(ret_type); - PUSH_TYPE(ret_type); - - if(!wasm_loader_find_block_addr(module, - (frame_csp - 1)->start_addr, - p_end, - (frame_csp - 1)->block_type, - &(frame_csp - 1)->else_addr, - &(frame_csp - 1)->end_addr, - error_buf, error_buf_size)) - goto fail; + pb_read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); - stack_cell_num = (frame_csp - 1)->stack_cell_num; - frame_ref = frame_ref_bottom + stack_cell_num; - if ((frame_csp - 1)->block_type == BLOCK_TYPE_IF - && p <= (frame_csp - 1)->else_addr) { - p = (frame_csp - 1)->else_addr; - } - else { - p = (frame_csp - 1)->end_addr; - PUSH_TYPE((frame_csp - 1)->return_type); - } - break; - } + if (data_seg_idx >= module->data_seg_count) { + set_error_buf_v(error_buf, error_buf_size, + "unknown data segment %d", + data_seg_idx); + goto fail; + } - case WASM_OP_CALL: - { - WASMType *func_type; - uint32 func_idx; - int32 idx; + if (module->data_seg_count1 == 0) + goto fail_data_cnt_sec_require; - read_leb_uint32(p, p_end, func_idx); + POP_I32(); + POP_I32(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + break; + } + case WASM_OP_DATA_DROP: + { + pb_read_leb_uint32(p, p_end, data_seg_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, data_seg_idx); +#endif + if (data_seg_idx >= module->data_seg_count) { + set_error_buf(error_buf, error_buf_size, + "unknown data segment"); + goto fail; + } - if (func_idx >= module->import_function_count + module->function_count) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "function index out of range"); - goto fail; - } + if (module->data_seg_count1 == 0) + goto fail_data_cnt_sec_require; - if (func_idx < module->import_function_count) - func_type = module->import_functions[func_idx].u.function.func_type; - else - func_type = - module->functions[func_idx - module->import_function_count]->func_type; +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + break; + } + fail_data_cnt_sec_require: + set_error_buf(error_buf, error_buf_size, + "data count section required"); + goto fail; +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + { + CHECK_BUF(p, p_end, sizeof(int16)); + /* check both src and dst memory index */ + pb_read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); + pb_read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); + + if (module->import_memory_count == 0 + && module->memory_count == 0) { + set_error_buf(error_buf, error_buf_size, + "unknown memory 0"); + goto fail; + } - if (func_type->param_count > 0) { - for (idx = (int32)(func_type->param_count - 1); idx >= 0; idx--) - POP_TYPE(func_type->types[idx]); - } + POP_MEM_OFFSET(); + POP_MEM_OFFSET(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + break; + } + case WASM_OP_MEMORY_FILL: + { + pb_read_leb_uint32(p, p_end, memidx); + check_memidx(module, memidx); + if (module->import_memory_count == 0 + && module->memory_count == 0) { + set_error_buf(error_buf, error_buf_size, + "unknown memory 0"); + goto fail; + } + POP_MEM_OFFSET(); + POP_I32(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_bulk_memory_used = true; +#endif + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + case WASM_OP_TABLE_INIT: + { + uint8 seg_type = 0, tbl_type = 0; +#if WASM_ENABLE_GC != 0 + WASMRefType *seg_ref_type = NULL, *tbl_ref_type = NULL; +#endif - if (func_type->result_count) - PUSH_TYPE(func_type->types[func_type->param_count]); + pb_read_leb_uint32(p, p_end, table_seg_idx); + pb_read_leb_uint32(p, p_end, table_idx); - func->has_op_func_call = true; - break; - } + if (!get_table_elem_type(module, table_idx, &tbl_type, +#if WASM_ENABLE_GC != 0 + (void **)&tbl_ref_type, +#else + NULL, +#endif + error_buf, error_buf_size)) + goto fail; - case WASM_OP_CALL_INDIRECT: - { - int32 idx; - WASMType *func_type; - uint32 type_idx; + if (!get_table_seg_elem_type(module, table_seg_idx, + &seg_type, +#if WASM_ENABLE_GC != 0 + (void **)&seg_ref_type, +#else + NULL, +#endif + error_buf, error_buf_size)) + goto fail; - if (module->table_count == 0 - && module->import_table_count == 0) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "call indirect without default table"); - goto fail; - } +#if WASM_ENABLE_GC == 0 + if (seg_type != tbl_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } +#else + if (!wasm_reftype_is_subtype_of( + seg_type, seg_ref_type, tbl_type, tbl_ref_type, + module->types, module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } +#endif - read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); + emit_uint32(loader_ctx, table_idx); +#endif + POP_I32(); + POP_I32(); +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + POP_TBL_ELEM_IDX(); - /* reserved byte 0x00 */ - if (*p++ != 0x00) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "zero flag expected"); - goto fail; - } +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } + case WASM_OP_ELEM_DROP: + { + pb_read_leb_uint32(p, p_end, table_seg_idx); + if (!get_table_seg_elem_type(module, table_seg_idx, + NULL, NULL, error_buf, + error_buf_size)) + goto fail; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); +#endif - POP_I32(); +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } + case WASM_OP_TABLE_COPY: + { + uint8 src_type, dst_type, src_tbl_idx_type, + dst_tbl_idx_type, min_tbl_idx_type; +#if WASM_ENABLE_GC != 0 + WASMRefType *src_ref_type = NULL, *dst_ref_type = NULL; +#endif + uint32 src_tbl_idx, dst_tbl_idx; - if (type_idx >= module->type_count) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "function index out of range"); - goto fail; - } + pb_read_leb_uint32(p, p_end, dst_tbl_idx); + if (!get_table_elem_type(module, dst_tbl_idx, &dst_type, +#if WASM_ENABLE_GC != 0 + (void **)&dst_ref_type, +#else + NULL, +#endif + error_buf, error_buf_size)) + goto fail; - func_type = module->types[type_idx]; + pb_read_leb_uint32(p, p_end, src_tbl_idx); + if (!get_table_elem_type(module, src_tbl_idx, &src_type, +#if WASM_ENABLE_GC != 0 + (void **)&src_ref_type, +#else + NULL, +#endif + error_buf, error_buf_size)) + goto fail; - if (func_type->param_count > 0) { - for (idx = (int32)(func_type->param_count - 1); idx >= 0; idx--) - POP_TYPE(func_type->types[idx]); - } +#if WASM_ENABLE_GC == 0 + if (src_type != dst_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } +#else + if (!wasm_reftype_is_subtype_of( + src_type, src_ref_type, dst_type, dst_ref_type, + module->types, module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } +#endif - PUSH_TYPE(func_type->types[func_type->param_count]); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, dst_tbl_idx); + emit_uint32(loader_ctx, src_tbl_idx); +#endif - func->has_op_func_call = true; - break; - } +#if WASM_ENABLE_MEMORY64 != 0 + src_tbl_idx_type = is_table_64bit(module, src_tbl_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; + dst_tbl_idx_type = is_table_64bit(module, dst_tbl_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; + min_tbl_idx_type = + (src_tbl_idx_type == VALUE_TYPE_I32 + || dst_tbl_idx_type == VALUE_TYPE_I32) + ? VALUE_TYPE_I32 + : VALUE_TYPE_I64; +#else + src_tbl_idx_type = VALUE_TYPE_I32; + dst_tbl_idx_type = VALUE_TYPE_I32; + min_tbl_idx_type = VALUE_TYPE_I32; +#endif - case WASM_OP_DROP: - { - if (stack_cell_num <= 0) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "opcode drop was found but stack was empty"); - goto fail; - } + table_elem_idx_type = min_tbl_idx_type; + POP_TBL_ELEM_IDX(); + table_elem_idx_type = src_tbl_idx_type; + POP_TBL_ELEM_IDX(); + table_elem_idx_type = dst_tbl_idx_type; + POP_TBL_ELEM_IDX(); - if (*(frame_ref - 1) == REF_I32 - || *(frame_ref - 1) == REF_F32) { - frame_ref--; - stack_cell_num--; - } - else { - if (stack_cell_num <= 1) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "opcode drop was found but stack was empty"); - goto fail; +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; } - frame_ref -= 2; - stack_cell_num -= 2; - *(p - 1) = WASM_OP_DROP_64; - } - break; - } + case WASM_OP_TABLE_SIZE: + { + pb_read_leb_uint32(p, p_end, table_idx); + /* TODO: shall we create a new function to check + table idx instead of using below function? */ + if (!get_table_elem_type(module, table_idx, NULL, NULL, + error_buf, error_buf_size)) + goto fail; - case WASM_OP_SELECT: - { - uint8 ref_type; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif - POP_I32(); +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + PUSH_TBL_ELEM_IDX(); - if (stack_cell_num <= 0) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "opcode select was found but stack was empty"); - goto fail; - } +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif + break; + } + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + { + uint8 decl_type; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type = NULL; +#endif + + pb_read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, &decl_type, +#if WASM_ENABLE_GC != 0 + (void **)&ref_type, +#else + NULL, +#endif + error_buf, error_buf_size)) + goto fail; +#if WASM_ENABLE_GC != 0 + if (wasm_is_type_multi_byte_type(decl_type)) { + bh_memcpy_s(&wasm_ref_type, sizeof(WASMRefType), + ref_type, + wasm_reftype_struct_size(ref_type)); + } +#endif + + if (opcode1 == WASM_OP_TABLE_GROW) { + if (table_idx < module->import_table_count) { + module->import_tables[table_idx] + .u.table.table_type.possible_grow = true; + } + else { + module + ->tables[table_idx + - module->import_table_count] + .table_type.possible_grow = true; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_type); +#endif + POP_TYPE(decl_type); + if (opcode1 == WASM_OP_TABLE_GROW) + PUSH_TBL_ELEM_IDX(); + else + POP_TBL_ELEM_IDX(); - switch (*(frame_ref - 1)) { - case REF_I32: - case REF_F32: - break; - case REF_I64_2: - case REF_F64_2: - *(p - 1) = WASM_OP_SELECT_64; +#if WASM_ENABLE_WAMR_COMPILER != 0 + module->is_ref_types_used = true; +#endif break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x %02x", "unsupported opcode", + 0xfc, opcode1); + goto fail; } - - ref_type = *(frame_ref - 1); - POP_TYPE(ref_type); - POP_TYPE(ref_type); - PUSH_TYPE(ref_type); break; } - case WASM_OP_GET_LOCAL: +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) \ + || (WASM_ENABLE_FAST_INTERP != 0) + case WASM_OP_SIMD_PREFIX: { - p_org = p - 1; + uint32 opcode1; - GET_LOCAL_INDEX_TYPE_AND_OFFSET(); - PUSH_TYPE(local_type); +#if WASM_ENABLE_WAMR_COMPILER != 0 + /* Mark the SIMD instruction is used in this module */ + module->is_simd_used = true; +#endif -#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) - if (local_offset < 0x80) { - *p_org++ = WASM_OP_GET_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) - *p_org++ = (uint8)local_offset; - else - *p_org++ = (uint8)(local_offset | 0x80); - while (p_org < p) - *p_org++ = WASM_OP_NOP; - } + pb_read_leb_uint32(p, p_end, opcode1); + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, opcode1); #endif - break; - } - case WASM_OP_SET_LOCAL: - { - p_org = p - 1; + /* follow the order of enum WASMSimdEXTOpcode in wasm_opcode.h + */ + switch (opcode1) { + /* memory instruction */ + case SIMD_v128_load: + case SIMD_v128_load8x8_s: + case SIMD_v128_load8x8_u: + case SIMD_v128_load16x4_s: + case SIMD_v128_load16x4_u: + case SIMD_v128_load32x2_s: + case SIMD_v128_load32x2_u: + case SIMD_v128_load8_splat: + case SIMD_v128_load16_splat: + case SIMD_v128_load32_splat: + case SIMD_v128_load64_splat: + { + CHECK_MEMORY(); + + pb_read_leb_uint32(p, p_end, align); /* align */ + if (!check_simd_memory_access_align( + opcode1, align, error_buf, error_buf_size)) { + goto fail; + } - GET_LOCAL_INDEX_TYPE_AND_OFFSET(); - POP_TYPE(local_type); + pb_read_leb_mem_offset(p, p_end, + mem_offset); /* offset */ -#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) - if (local_offset < 0x80) { - *p_org++ = WASM_OP_SET_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) - *p_org++ = (uint8)local_offset; - else - *p_org++ = (uint8)(local_offset | 0x80); - while (p_org < p) - *p_org++ = WASM_OP_NOP; - } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); #endif - break; - } + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_V128); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } - case WASM_OP_TEE_LOCAL: - { - p_org = p - 1; + case SIMD_v128_store: + { + CHECK_MEMORY(); - GET_LOCAL_INDEX_TYPE_AND_OFFSET(); - POP_TYPE(local_type); - PUSH_TYPE(local_type); + pb_read_leb_uint32(p, p_end, align); /* align */ + if (!check_simd_memory_access_align( + opcode1, align, error_buf, error_buf_size)) { + goto fail; + } -#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) - if (local_offset < 0x80) { - *p_org++ = WASM_OP_TEE_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) - *p_org++ = (uint8)local_offset; - else - *p_org++ = (uint8)(local_offset | 0x80); - while (p_org < p) - *p_org++ = WASM_OP_NOP; - } + pb_read_leb_mem_offset(p, p_end, + mem_offset); /* offset */ + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); #endif - break; - } + POP_V128(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } - case WASM_OP_GET_GLOBAL: - { - read_leb_uint32(p, p_end, global_idx); - if (global_idx >= global_count) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "global index out of range"); - goto fail; - } + /* basic operation */ + case SIMD_v128_const: + { +#if WASM_ENABLE_FAST_INTERP != 0 + uint64 high, low; +#endif + CHECK_BUF1(p, p_end, 16); +#if WASM_ENABLE_FAST_INTERP != 0 + wasm_runtime_read_v128(p, &high, &low); + emit_uint64(loader_ctx, high); + emit_uint64(loader_ctx, low); +#endif + p += 16; + PUSH_V128(); + break; + } - global_type = global_idx < module->import_global_count - ? module->import_globals[global_idx].u.global.type - :module->globals[global_idx - module->import_global_count].type; + case SIMD_v8x16_shuffle: + { + V128 mask; - PUSH_TYPE(global_type); - break; - } + CHECK_BUF1(p, p_end, 16); + mask = read_i8x16(p, error_buf, error_buf_size); + if (!check_simd_shuffle_mask(mask, error_buf, + error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + uint64 high, low; + wasm_runtime_read_v128(p, &high, &low); + emit_uint64(loader_ctx, high); + emit_uint64(loader_ctx, low); +#endif + p += 16; + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_SET_GLOBAL: - { - read_leb_uint32(p, p_end, global_idx); - if (global_idx >= global_count) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "global index out of range"); - goto fail; - } + case SIMD_v8x16_swizzle: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - global_type = global_idx < module->import_global_count - ? module->import_globals[global_idx].u.global.type - : module->globals[global_idx - module->import_global_count].type; + /* splat operation */ + case SIMD_i8x16_splat: + case SIMD_i16x8_splat: + case SIMD_i32x4_splat: + case SIMD_i64x2_splat: + case SIMD_f32x4_splat: + case SIMD_f64x2_splat: + { + uint8 pop_type[] = { VALUE_TYPE_I32, VALUE_TYPE_I32, + VALUE_TYPE_I32, VALUE_TYPE_I64, + VALUE_TYPE_F32, VALUE_TYPE_F64 }; + POP_AND_PUSH(pop_type[opcode1 - SIMD_i8x16_splat], + VALUE_TYPE_V128); + break; + } - POP_TYPE(global_type); - break; - } + /* lane operation */ + case SIMD_i8x16_extract_lane_s: + case SIMD_i8x16_extract_lane_u: + case SIMD_i8x16_replace_lane: + case SIMD_i16x8_extract_lane_s: + case SIMD_i16x8_extract_lane_u: + case SIMD_i16x8_replace_lane: + case SIMD_i32x4_extract_lane: + case SIMD_i32x4_replace_lane: + case SIMD_i64x2_extract_lane: + case SIMD_i64x2_replace_lane: + case SIMD_f32x4_extract_lane: + case SIMD_f32x4_replace_lane: + case SIMD_f64x2_extract_lane: + case SIMD_f64x2_replace_lane: + { + uint8 lane; + /* clang-format off */ + uint8 replace[] = { + /*i8x16*/ 0x0, 0x0, VALUE_TYPE_I32, + /*i16x8*/ 0x0, 0x0, VALUE_TYPE_I32, + /*i32x4*/ 0x0, VALUE_TYPE_I32, + /*i64x2*/ 0x0, VALUE_TYPE_I64, + /*f32x4*/ 0x0, VALUE_TYPE_F32, + /*f64x2*/ 0x0, VALUE_TYPE_F64, + }; + uint8 push_type[] = { + /*i8x16*/ VALUE_TYPE_I32, VALUE_TYPE_I32, + VALUE_TYPE_V128, + /*i16x8*/ VALUE_TYPE_I32, VALUE_TYPE_I32, + VALUE_TYPE_V128, + /*i32x4*/ VALUE_TYPE_I32, VALUE_TYPE_V128, + /*i64x2*/ VALUE_TYPE_I64, VALUE_TYPE_V128, + /*f32x4*/ VALUE_TYPE_F32, VALUE_TYPE_V128, + /*f64x2*/ VALUE_TYPE_F64, VALUE_TYPE_V128, + }; + /* clang-format on */ + + CHECK_BUF(p, p_end, 1); + lane = read_uint8(p); + if (!check_simd_access_lane(opcode1, lane, error_buf, + error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, lane); +#endif + if (replace[opcode1 - SIMD_i8x16_extract_lane_s]) { +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(wasm_loader_pop_frame_ref_offset( + loader_ctx, + replace[opcode1 + - SIMD_i8x16_extract_lane_s], + error_buf, error_buf_size))) + goto fail; +#else + if (!(wasm_loader_pop_frame_ref( + loader_ctx, + replace[opcode1 + - SIMD_i8x16_extract_lane_s], + error_buf, error_buf_size))) + goto fail; +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ + } - case WASM_OP_I32_LOAD: - case WASM_OP_I32_LOAD8_S: - case WASM_OP_I32_LOAD8_U: - case WASM_OP_I32_LOAD16_S: - case WASM_OP_I32_LOAD16_U: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_I32(); - PUSH_I32(); - break; + POP_AND_PUSH( + VALUE_TYPE_V128, + push_type[opcode1 - SIMD_i8x16_extract_lane_s]); + break; + } - case WASM_OP_I64_LOAD: - case WASM_OP_I64_LOAD8_S: - case WASM_OP_I64_LOAD8_U: - case WASM_OP_I64_LOAD16_S: - case WASM_OP_I64_LOAD16_U: - case WASM_OP_I64_LOAD32_S: - case WASM_OP_I64_LOAD32_U: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_I32(); - PUSH_I64(); - break; + /* i8x16 compare operation */ + case SIMD_i8x16_eq: + case SIMD_i8x16_ne: + case SIMD_i8x16_lt_s: + case SIMD_i8x16_lt_u: + case SIMD_i8x16_gt_s: + case SIMD_i8x16_gt_u: + case SIMD_i8x16_le_s: + case SIMD_i8x16_le_u: + case SIMD_i8x16_ge_s: + case SIMD_i8x16_ge_u: + /* i16x8 compare operation */ + case SIMD_i16x8_eq: + case SIMD_i16x8_ne: + case SIMD_i16x8_lt_s: + case SIMD_i16x8_lt_u: + case SIMD_i16x8_gt_s: + case SIMD_i16x8_gt_u: + case SIMD_i16x8_le_s: + case SIMD_i16x8_le_u: + case SIMD_i16x8_ge_s: + case SIMD_i16x8_ge_u: + /* i32x4 compare operation */ + case SIMD_i32x4_eq: + case SIMD_i32x4_ne: + case SIMD_i32x4_lt_s: + case SIMD_i32x4_lt_u: + case SIMD_i32x4_gt_s: + case SIMD_i32x4_gt_u: + case SIMD_i32x4_le_s: + case SIMD_i32x4_le_u: + case SIMD_i32x4_ge_s: + case SIMD_i32x4_ge_u: + /* f32x4 compare operation */ + case SIMD_f32x4_eq: + case SIMD_f32x4_ne: + case SIMD_f32x4_lt: + case SIMD_f32x4_gt: + case SIMD_f32x4_le: + case SIMD_f32x4_ge: + /* f64x2 compare operation */ + case SIMD_f64x2_eq: + case SIMD_f64x2_ne: + case SIMD_f64x2_lt: + case SIMD_f64x2_gt: + case SIMD_f64x2_le: + case SIMD_f64x2_ge: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_LOAD: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_I32(); - PUSH_F32(); - break; + /* v128 operation */ + case SIMD_v128_not: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_LOAD: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_I32(); - PUSH_F64(); - break; + case SIMD_v128_and: + case SIMD_v128_andnot: + case SIMD_v128_or: + case SIMD_v128_xor: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_STORE: - case WASM_OP_I32_STORE8: - case WASM_OP_I32_STORE16: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_I32(); - POP_I32(); - break; + case SIMD_v128_bitselect: + { + POP_V128(); + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_STORE: - case WASM_OP_I64_STORE8: - case WASM_OP_I64_STORE16: - case WASM_OP_I64_STORE32: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_I64(); - POP_I32(); - break; + case SIMD_v128_any_true: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_I32); + break; + } - case WASM_OP_F32_STORE: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_F32(); - POP_I32(); - break; + /* Load Lane Operation */ + case SIMD_v128_load8_lane: + case SIMD_v128_load16_lane: + case SIMD_v128_load32_lane: + case SIMD_v128_load64_lane: + case SIMD_v128_store8_lane: + case SIMD_v128_store16_lane: + case SIMD_v128_store32_lane: + case SIMD_v128_store64_lane: + { + uint8 lane; + + CHECK_MEMORY(); + + pb_read_leb_uint32(p, p_end, align); /* align */ + if (!check_simd_memory_access_align( + opcode1, align, error_buf, error_buf_size)) { + goto fail; + } - case WASM_OP_F64_STORE: - CHECK_MEMORY(); - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ - POP_F64(); - POP_I32(); - break; + pb_read_leb_mem_offset(p, p_end, + mem_offset); /* offset */ - case WASM_OP_MEMORY_SIZE: - CHECK_MEMORY(); - /* reserved byte 0x00 */ - if (*p++ != 0x00) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "zero flag expected"); - goto fail; - } - PUSH_I32(); - break; + CHECK_BUF(p, p_end, 1); + lane = read_uint8(p); + if (!check_simd_access_lane(opcode1, lane, error_buf, + error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + POP_V128(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, lane); +#endif + if (opcode1 < SIMD_v128_store8_lane) { + PUSH_V128(); + } +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } - case WASM_OP_MEMORY_GROW: - CHECK_MEMORY(); - /* reserved byte 0x00 */ - if (*p++ != 0x00) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "zero flag expected"); - goto fail; - } - POP_I32(); - PUSH_I32(); + case SIMD_v128_load32_zero: + case SIMD_v128_load64_zero: + { + CHECK_MEMORY(); - func->has_op_memory_grow = true; - module->possible_memory_grow = true; - break; + pb_read_leb_uint32(p, p_end, align); /* align */ + if (!check_simd_memory_access_align( + opcode1, align, error_buf, error_buf_size)) { + goto fail; + } - case WASM_OP_I32_CONST: - read_leb_int32(p, p_end, i32_const); - /* Currently we only track simple I32_CONST opcode. */ - is_i32_const = true; - PUSH_I32(); - break; + pb_read_leb_mem_offset(p, p_end, + mem_offset); /* offset */ +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_V128); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } - case WASM_OP_I64_CONST: - read_leb_int64(p, p_end, i64); - PUSH_I64(); - break; + /* Float conversion */ + case SIMD_f32x4_demote_f64x2_zero: + case SIMD_f64x2_promote_low_f32x4_zero: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_CONST: - p += sizeof(float32); - PUSH_F32(); - break; + /* i8x16 Operation */ + case SIMD_i8x16_abs: + case SIMD_i8x16_neg: + case SIMD_i8x16_popcnt: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_CONST: - p += sizeof(float64); - PUSH_F64(); - break; + case SIMD_i8x16_all_true: + case SIMD_i8x16_bitmask: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_I32); + break; + } - case WASM_OP_I32_EQZ: - POP_I32(); - PUSH_I32(); - break; + case SIMD_i8x16_narrow_i16x8_s: + case SIMD_i8x16_narrow_i16x8_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_EQ: - case WASM_OP_I32_NE: - case WASM_OP_I32_LT_S: - case WASM_OP_I32_LT_U: - case WASM_OP_I32_GT_S: - case WASM_OP_I32_GT_U: - case WASM_OP_I32_LE_S: - case WASM_OP_I32_LE_U: - case WASM_OP_I32_GE_S: - case WASM_OP_I32_GE_U: - POP_I32(); - POP_I32(); - PUSH_I32(); - break; + case SIMD_f32x4_ceil: + case SIMD_f32x4_floor: + case SIMD_f32x4_trunc: + case SIMD_f32x4_nearest: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_EQZ: - POP_I64(); - PUSH_I32(); - break; + case SIMD_i8x16_shl: + case SIMD_i8x16_shr_s: + case SIMD_i8x16_shr_u: + { + POP_I32(); + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_EQ: - case WASM_OP_I64_NE: - case WASM_OP_I64_LT_S: - case WASM_OP_I64_LT_U: - case WASM_OP_I64_GT_S: - case WASM_OP_I64_GT_U: - case WASM_OP_I64_LE_S: - case WASM_OP_I64_LE_U: - case WASM_OP_I64_GE_S: - case WASM_OP_I64_GE_U: - POP_I64(); - POP_I64(); - PUSH_I32(); - break; + case SIMD_i8x16_add: + case SIMD_i8x16_add_sat_s: + case SIMD_i8x16_add_sat_u: + case SIMD_i8x16_sub: + case SIMD_i8x16_sub_sat_s: + case SIMD_i8x16_sub_sat_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } + + case SIMD_f64x2_ceil: + case SIMD_f64x2_floor: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } + + case SIMD_i8x16_min_s: + case SIMD_i8x16_min_u: + case SIMD_i8x16_max_s: + case SIMD_i8x16_max_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_EQ: - case WASM_OP_F32_NE: - case WASM_OP_F32_LT: - case WASM_OP_F32_GT: - case WASM_OP_F32_LE: - case WASM_OP_F32_GE: - POP_F32(); - POP_F32(); - PUSH_I32(); - break; + case SIMD_f64x2_trunc: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_EQ: - case WASM_OP_F64_NE: - case WASM_OP_F64_LT: - case WASM_OP_F64_GT: - case WASM_OP_F64_LE: - case WASM_OP_F64_GE: - POP_F64(); - POP_F64(); - PUSH_I32(); - break; + case SIMD_i8x16_avgr_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - break; + case SIMD_i16x8_extadd_pairwise_i8x16_s: + case SIMD_i16x8_extadd_pairwise_i8x16_u: + case SIMD_i32x4_extadd_pairwise_i16x8_s: + case SIMD_i32x4_extadd_pairwise_i16x8_u: + /* i16x8 operation */ + case SIMD_i16x8_abs: + case SIMD_i16x8_neg: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_CLZ: - case WASM_OP_I32_CTZ: - case WASM_OP_I32_POPCNT: - POP_I32(); - PUSH_I32(); - break; + case SIMD_i16x8_q15mulr_sat_s: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_ADD: - case WASM_OP_I32_SUB: - case WASM_OP_I32_MUL: - case WASM_OP_I32_DIV_S: - case WASM_OP_I32_DIV_U: - case WASM_OP_I32_REM_S: - case WASM_OP_I32_REM_U: - case WASM_OP_I32_AND: - case WASM_OP_I32_OR: - case WASM_OP_I32_XOR: - case WASM_OP_I32_SHL: - case WASM_OP_I32_SHR_S: - case WASM_OP_I32_SHR_U: - case WASM_OP_I32_ROTL: - case WASM_OP_I32_ROTR: - POP_I32(); - POP_I32(); - PUSH_I32(); - break; + case SIMD_i16x8_all_true: + case SIMD_i16x8_bitmask: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_I32); + break; + } - case WASM_OP_I64_CLZ: - case WASM_OP_I64_CTZ: - case WASM_OP_I64_POPCNT: - POP_I64(); - PUSH_I64(); - break; + case SIMD_i16x8_narrow_i32x4_s: + case SIMD_i16x8_narrow_i32x4_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_ADD: - case WASM_OP_I64_SUB: - case WASM_OP_I64_MUL: - case WASM_OP_I64_DIV_S: - case WASM_OP_I64_DIV_U: - case WASM_OP_I64_REM_S: - case WASM_OP_I64_REM_U: - case WASM_OP_I64_AND: - case WASM_OP_I64_OR: - case WASM_OP_I64_XOR: - case WASM_OP_I64_SHL: - case WASM_OP_I64_SHR_S: - case WASM_OP_I64_SHR_U: - case WASM_OP_I64_ROTL: - case WASM_OP_I64_ROTR: - POP_I64(); - POP_I64(); - PUSH_I64(); - break; + case SIMD_i16x8_extend_low_i8x16_s: + case SIMD_i16x8_extend_high_i8x16_s: + case SIMD_i16x8_extend_low_i8x16_u: + case SIMD_i16x8_extend_high_i8x16_u: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_ABS: - case WASM_OP_F32_NEG: - case WASM_OP_F32_CEIL: - case WASM_OP_F32_FLOOR: - case WASM_OP_F32_TRUNC: - case WASM_OP_F32_NEAREST: - case WASM_OP_F32_SQRT: - POP_F32(); - PUSH_F32(); - break; + case SIMD_i16x8_shl: + case SIMD_i16x8_shr_s: + case SIMD_i16x8_shr_u: + { + POP_I32(); + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_ADD: - case WASM_OP_F32_SUB: - case WASM_OP_F32_MUL: - case WASM_OP_F32_DIV: - case WASM_OP_F32_MIN: - case WASM_OP_F32_MAX: - case WASM_OP_F32_COPYSIGN: - POP_F32(); - POP_F32(); - PUSH_F32(); - break; + case SIMD_i16x8_add: + case SIMD_i16x8_add_sat_s: + case SIMD_i16x8_add_sat_u: + case SIMD_i16x8_sub: + case SIMD_i16x8_sub_sat_s: + case SIMD_i16x8_sub_sat_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_ABS: - case WASM_OP_F64_NEG: - case WASM_OP_F64_CEIL: - case WASM_OP_F64_FLOOR: - case WASM_OP_F64_TRUNC: - case WASM_OP_F64_NEAREST: - case WASM_OP_F64_SQRT: - POP_F64(); - PUSH_F64(); - break; + case SIMD_f64x2_nearest: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_ADD: - case WASM_OP_F64_SUB: - case WASM_OP_F64_MUL: - case WASM_OP_F64_DIV: - case WASM_OP_F64_MIN: - case WASM_OP_F64_MAX: - case WASM_OP_F64_COPYSIGN: - POP_F64(); - POP_F64(); - PUSH_F64(); - break; + case SIMD_i16x8_mul: + case SIMD_i16x8_min_s: + case SIMD_i16x8_min_u: + case SIMD_i16x8_max_s: + case SIMD_i16x8_max_u: + case SIMD_i16x8_avgr_u: + case SIMD_i16x8_extmul_low_i8x16_s: + case SIMD_i16x8_extmul_high_i8x16_s: + case SIMD_i16x8_extmul_low_i8x16_u: + case SIMD_i16x8_extmul_high_i8x16_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_WRAP_I64: - POP_I64(); - PUSH_I32(); - break; + /* i32x4 operation */ + case SIMD_i32x4_abs: + case SIMD_i32x4_neg: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_TRUNC_S_F32: - case WASM_OP_I32_TRUNC_U_F32: - POP_F32(); - PUSH_I32(); - break; + case SIMD_i32x4_all_true: + case SIMD_i32x4_bitmask: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_I32); + break; + } - case WASM_OP_I32_TRUNC_S_F64: - case WASM_OP_I32_TRUNC_U_F64: - POP_F64(); - PUSH_I32(); - break; + case SIMD_i32x4_extend_low_i16x8_s: + case SIMD_i32x4_extend_high_i16x8_s: + case SIMD_i32x4_extend_low_i16x8_u: + case SIMD_i32x4_extend_high_i16x8_u: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_EXTEND_S_I32: - case WASM_OP_I64_EXTEND_U_I32: - POP_I32(); - PUSH_I64(); - break; + case SIMD_i32x4_shl: + case SIMD_i32x4_shr_s: + case SIMD_i32x4_shr_u: + { + POP_I32(); + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_TRUNC_S_F32: - case WASM_OP_I64_TRUNC_U_F32: - POP_F32(); - PUSH_I64(); - break; + case SIMD_i32x4_add: + case SIMD_i32x4_sub: + case SIMD_i32x4_mul: + case SIMD_i32x4_min_s: + case SIMD_i32x4_min_u: + case SIMD_i32x4_max_s: + case SIMD_i32x4_max_u: + case SIMD_i32x4_dot_i16x8_s: + case SIMD_i32x4_extmul_low_i16x8_s: + case SIMD_i32x4_extmul_high_i16x8_s: + case SIMD_i32x4_extmul_low_i16x8_u: + case SIMD_i32x4_extmul_high_i16x8_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_TRUNC_S_F64: - case WASM_OP_I64_TRUNC_U_F64: - POP_F64(); - PUSH_I64(); - break; + /* i64x2 operation */ + case SIMD_i64x2_abs: + case SIMD_i64x2_neg: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_CONVERT_S_I32: - case WASM_OP_F32_CONVERT_U_I32: - POP_I32(); - PUSH_F32(); - break; + case SIMD_i64x2_all_true: + case SIMD_i64x2_bitmask: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_I32); + break; + } - case WASM_OP_F32_CONVERT_S_I64: - case WASM_OP_F32_CONVERT_U_I64: - POP_I64(); - PUSH_F32(); - break; + case SIMD_i64x2_extend_low_i32x4_s: + case SIMD_i64x2_extend_high_i32x4_s: + case SIMD_i64x2_extend_low_i32x4_u: + case SIMD_i64x2_extend_high_i32x4_u: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_DEMOTE_F64: - POP_F64(); - PUSH_F32(); - break; + case SIMD_i64x2_shl: + case SIMD_i64x2_shr_s: + case SIMD_i64x2_shr_u: + { + POP_I32(); + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_CONVERT_S_I32: - case WASM_OP_F64_CONVERT_U_I32: - POP_I32(); - PUSH_F64(); - break; + case SIMD_i64x2_add: + case SIMD_i64x2_sub: + case SIMD_i64x2_mul: + case SIMD_i64x2_eq: + case SIMD_i64x2_ne: + case SIMD_i64x2_lt_s: + case SIMD_i64x2_gt_s: + case SIMD_i64x2_le_s: + case SIMD_i64x2_ge_s: + case SIMD_i64x2_extmul_low_i32x4_s: + case SIMD_i64x2_extmul_high_i32x4_s: + case SIMD_i64x2_extmul_low_i32x4_u: + case SIMD_i64x2_extmul_high_i32x4_u: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_CONVERT_S_I64: - case WASM_OP_F64_CONVERT_U_I64: - POP_I64(); - PUSH_F64(); - break; + /* f32x4 operation */ + case SIMD_f32x4_abs: + case SIMD_f32x4_neg: + case SIMD_f32x4_sqrt: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F64_PROMOTE_F32: - POP_F32(); - PUSH_F64(); - break; + case SIMD_f32x4_add: + case SIMD_f32x4_sub: + case SIMD_f32x4_mul: + case SIMD_f32x4_div: + case SIMD_f32x4_min: + case SIMD_f32x4_max: + case SIMD_f32x4_pmin: + case SIMD_f32x4_pmax: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I32_REINTERPRET_F32: - POP_F32(); - PUSH_I32(); - break; + /* f64x2 operation */ + case SIMD_f64x2_abs: + case SIMD_f64x2_neg: + case SIMD_f64x2_sqrt: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_I64_REINTERPRET_F64: - POP_F64(); - PUSH_I64(); - break; + case SIMD_f64x2_add: + case SIMD_f64x2_sub: + case SIMD_f64x2_mul: + case SIMD_f64x2_div: + case SIMD_f64x2_min: + case SIMD_f64x2_max: + case SIMD_f64x2_pmin: + case SIMD_f64x2_pmax: + { + POP2_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } - case WASM_OP_F32_REINTERPRET_I32: - POP_I32(); - PUSH_F32(); + case SIMD_i32x4_trunc_sat_f32x4_s: + case SIMD_i32x4_trunc_sat_f32x4_u: + case SIMD_f32x4_convert_i32x4_s: + case SIMD_f32x4_convert_i32x4_u: + case SIMD_i32x4_trunc_sat_f64x2_s_zero: + case SIMD_i32x4_trunc_sat_f64x2_u_zero: + case SIMD_f64x2_convert_low_i32x4_s: + case SIMD_f64x2_convert_low_i32x4_u: + { + POP_AND_PUSH(VALUE_TYPE_V128, VALUE_TYPE_V128); + break; + } + + default: + { + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "WASM module load failed: " + "invalid opcode 0xfd %02x.", + opcode1); + } + goto fail; + } + } break; + } +#endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || \ + (WASM_ENABLE_FAST_INTERP != 0) */ +#endif /* end of WASM_ENABLE_SIMD */ - case WASM_OP_F64_REINTERPRET_I64: - POP_I64(); - PUSH_F64(); +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + uint32 opcode1; + + pb_read_leb_uint32(p, p_end, opcode1); + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, opcode1); +#endif + if (opcode1 != WASM_OP_ATOMIC_FENCE) { + CHECK_MEMORY(); + pb_read_leb_uint32(p, p_end, align); /* align */ + pb_read_leb_mem_offset(p, p_end, mem_offset); /* offset */ + if (!check_memory_align_equal(opcode1, align, error_buf, + error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + } +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + switch (opcode1) { + case WASM_OP_ATOMIC_NOTIFY: + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_WAIT32: + POP_I64(); + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_WAIT64: + POP_I64(); + POP_I64(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_FENCE: + /* reserved byte 0x00 */ + if (*p++ != 0x00) { + set_error_buf(error_buf, error_buf_size, + "zero byte expected"); + goto fail; + } + break; + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + POP_I32(); + POP_MEM_OFFSET(); + break; + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I64); + break; + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + POP_I64(); + POP_MEM_OFFSET(); + break; + case WASM_OP_ATOMIC_RMW_I32_ADD: + case WASM_OP_ATOMIC_RMW_I32_ADD8_U: + case WASM_OP_ATOMIC_RMW_I32_ADD16_U: + case WASM_OP_ATOMIC_RMW_I32_SUB: + case WASM_OP_ATOMIC_RMW_I32_SUB8_U: + case WASM_OP_ATOMIC_RMW_I32_SUB16_U: + case WASM_OP_ATOMIC_RMW_I32_AND: + case WASM_OP_ATOMIC_RMW_I32_AND8_U: + case WASM_OP_ATOMIC_RMW_I32_AND16_U: + case WASM_OP_ATOMIC_RMW_I32_OR: + case WASM_OP_ATOMIC_RMW_I32_OR8_U: + case WASM_OP_ATOMIC_RMW_I32_OR16_U: + case WASM_OP_ATOMIC_RMW_I32_XOR: + case WASM_OP_ATOMIC_RMW_I32_XOR8_U: + case WASM_OP_ATOMIC_RMW_I32_XOR16_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG: + case WASM_OP_ATOMIC_RMW_I32_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG16_U: + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_RMW_I64_ADD: + case WASM_OP_ATOMIC_RMW_I64_ADD8_U: + case WASM_OP_ATOMIC_RMW_I64_ADD16_U: + case WASM_OP_ATOMIC_RMW_I64_ADD32_U: + case WASM_OP_ATOMIC_RMW_I64_SUB: + case WASM_OP_ATOMIC_RMW_I64_SUB8_U: + case WASM_OP_ATOMIC_RMW_I64_SUB16_U: + case WASM_OP_ATOMIC_RMW_I64_SUB32_U: + case WASM_OP_ATOMIC_RMW_I64_AND: + case WASM_OP_ATOMIC_RMW_I64_AND8_U: + case WASM_OP_ATOMIC_RMW_I64_AND16_U: + case WASM_OP_ATOMIC_RMW_I64_AND32_U: + case WASM_OP_ATOMIC_RMW_I64_OR: + case WASM_OP_ATOMIC_RMW_I64_OR8_U: + case WASM_OP_ATOMIC_RMW_I64_OR16_U: + case WASM_OP_ATOMIC_RMW_I64_OR32_U: + case WASM_OP_ATOMIC_RMW_I64_XOR: + case WASM_OP_ATOMIC_RMW_I64_XOR8_U: + case WASM_OP_ATOMIC_RMW_I64_XOR16_U: + case WASM_OP_ATOMIC_RMW_I64_XOR32_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG: + case WASM_OP_ATOMIC_RMW_I64_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG32_U: + POP_I64(); + POP_MEM_OFFSET(); + PUSH_I64(); + break; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + POP_I32(); + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + POP_I64(); + POP_I64(); + POP_MEM_OFFSET(); + PUSH_I64(); + break; + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x %02x", "unsupported opcode", + 0xfe, opcode1); + goto fail; + } break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ default: - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "WASM module load failed: " - "invalid opcode %02x.", opcode); + set_error_buf_v(error_buf, error_buf_size, "%s %02x", + "unsupported opcode", opcode); goto fail; } - if (opcode != WASM_OP_I32_CONST) - is_i32_const = false; +#if WASM_ENABLE_FAST_INTERP != 0 + last_op = opcode; +#endif } - if (csp_num > 0) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: " - "function body must end with END opcode."); + if (loader_ctx->csp_num > 0) { + /* unmatched end opcodes result from unbalanced control flow structures, + * for example, br_table with inconsistent target count (1 declared, 2 + * given), or simply superfluous end opcodes */ + set_error_buf( + error_buf, error_buf_size, + "unexpected end opcodes from unbalanced control flow structures"); goto fail; } - func->max_stack_cell_num = max_stack_cell_num; - func->max_block_num = max_csp_num; +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled == NULL) + goto re_scan; + + func->const_cell_num = loader_ctx->i64_const_num * 2 + + loader_ctx->v128_const_num * 4 + + loader_ctx->i32_const_num; + if (func->const_cell_num > 0) { + if (!(func->consts = + loader_malloc((uint64)sizeof(uint32) * func->const_cell_num, + error_buf, error_buf_size))) + goto fail; + if (loader_ctx->i64_const_num > 0) { + bh_memcpy_s(func->consts, + (uint32)sizeof(int64) * loader_ctx->i64_const_num, + loader_ctx->i64_consts, + (uint32)sizeof(int64) * loader_ctx->i64_const_num); + } + if (loader_ctx->i32_const_num > 0) { + bh_memcpy_s(func->consts + + sizeof(int64) * loader_ctx->i64_const_num, + (uint32)sizeof(int32) * loader_ctx->i32_const_num, + loader_ctx->i32_consts, + (uint32)sizeof(int32) * loader_ctx->i32_const_num); + } + if (loader_ctx->v128_const_num > 0) { + bh_memcpy_s(func->consts, + (uint32)sizeof(V128) * loader_ctx->v128_const_num, + loader_ctx->v128_consts, + (uint32)sizeof(V128) * loader_ctx->v128_const_num); + } + } + + func->max_stack_cell_num = loader_ctx->preserved_local_offset + - loader_ctx->start_dynamic_offset + 1; +#else + func->max_stack_cell_num = loader_ctx->max_stack_cell_num; +#endif + func->max_block_num = loader_ctx->max_csp_num; return_value = true; fail: - if (frame_ref_bottom) - wasm_free(frame_ref_bottom); - if (frame_csp_bottom) - wasm_free(frame_csp_bottom); + wasm_loader_ctx_destroy(loader_ctx); - (void)u8; - (void)u32; - (void)i32; - (void)i64; + (void)table_idx; + (void)table_seg_idx; + (void)data_seg_idx; + (void)i64_const; (void)local_offset; (void)p_org; + (void)mem_offset; + (void)align; return return_value; } diff --git a/core/iwasm/interpreter/wasm_loader.h b/core/iwasm/interpreter/wasm_loader.h index ec3c55776d..676770ee22 100644 --- a/core/iwasm/interpreter/wasm_loader.h +++ b/core/iwasm/interpreter/wasm_loader.h @@ -23,8 +23,12 @@ extern "C" { * * @return return module loaded, NULL if failed */ -WASMModule* -wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size); +WASMModule * +wasm_loader_load(uint8 *buf, uint32 size, +#if WASM_ENABLE_MULTI_MODULE != 0 + bool main_module, +#endif + const LoadArgs *args, char *error_buf, uint32 error_buf_size); /** * Load a WASM module from a specified WASM section list. @@ -35,9 +39,9 @@ wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_bu * * @return return WASM module loaded, NULL if failed */ -WASMModule* -wasm_loader_load_from_sections(WASMSection *section_list, - char *error_buf, uint32 error_buf_size); +WASMModule * +wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf, + uint32 error_buf_size); /** * Unload a WASM module. @@ -62,15 +66,12 @@ wasm_loader_unload(WASMModule *module); * * @return true if success, false otherwise */ + bool -wasm_loader_find_block_addr(WASMModule *module, - const uint8 *start_addr, - const uint8 *code_end_addr, - uint8 block_type, - uint8 **p_else_addr, - uint8 **p_end_addr, - char *error_buf, - uint32 error_buf_size); +wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, + const uint8 *start_addr, const uint8 *code_end_addr, + uint8 block_type, uint8 **p_else_addr, + uint8 **p_end_addr); #ifdef __cplusplus } diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c new file mode 100644 index 0000000000..1e2aa08c62 --- /dev/null +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -0,0 +1,8722 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_loader.h" +#include "bh_common.h" +#include "bh_log.h" +#include "wasm.h" +#include "wasm_opcode.h" +#include "wasm_runtime.h" +#include "../common/wasm_native.h" +#include "../common/wasm_memory.h" +#include "wasm_loader_common.h" +#if WASM_ENABLE_FAST_JIT != 0 +#include "../fast-jit/jit_compiler.h" +#include "../fast-jit/jit_codecache.h" +#endif +#if WASM_ENABLE_JIT != 0 +#include "../compilation/aot_llvm.h" +#endif + +/* Read a value of given type from the address pointed to by the given + pointer and increase the pointer to the position just after the + value being read. */ +#define TEMPLATE_READ_VALUE(Type, p) \ + (p += sizeof(Type), *(Type *)(p - sizeof(Type))) + +#if WASM_ENABLE_MEMORY64 != 0 +static bool +has_module_memory64(WASMModule *module) +{ + /* TODO: multi-memories for now assuming the memory idx type is consistent + * across multi-memories */ + if (module->import_memory_count > 0) + return !!(module->import_memories[0].u.memory.mem_type.flags + & MEMORY64_FLAG); + else if (module->memory_count > 0) + return !!(module->memories[0].flags & MEMORY64_FLAG); + + return false; +} + +static bool +is_table_64bit(WASMModule *module, uint32 table_idx) +{ + if (table_idx < module->import_table_count) + return !!(module->import_tables[table_idx].u.table.table_type.flags + & TABLE64_FLAG); + else + return !!(module->tables[table_idx - module->import_table_count] + .table_type.flags + & TABLE64_FLAG); + + return false; +} +#endif + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + wasm_loader_set_error_buf(error_buf, error_buf_size, string, false); +} + +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + bh_assert(buf + length >= buf && buf + length <= buf_end); \ + } while (0) + +#define CHECK_BUF1(buf, buf_end, length) \ + do { \ + bh_assert(buf + length >= buf && buf + length <= buf_end); \ + } while (0) + +#define skip_leb(p) while (*p++ & 0x80) +#define skip_leb_int64(p, p_end) skip_leb(p) +#define skip_leb_uint32(p, p_end) skip_leb(p) +#define skip_leb_int32(p, p_end) skip_leb(p) +#define skip_leb_mem_offset(p, p_end) skip_leb(p) +#define skip_leb_memidx(p, p_end) skip_leb(p) +#if WASM_ENABLE_MULTI_MEMORY == 0 +#define skip_leb_align(p, p_end) skip_leb(p) +#else +/* Skip the following memidx if applicable */ +#define skip_leb_align(p, p_end) \ + do { \ + if (*p++ & OPT_MEMIDX_FLAG) \ + skip_leb_uint32(p, p_end); \ + } while (0) +#endif + +static bool +is_32bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + /* the operand stack is in polymorphic state */ + || type == VALUE_TYPE_ANY +#if WASM_ENABLE_REF_TYPES != 0 + || type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF +#endif + ) + return true; + return false; +} + +static bool +is_64bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + return true; + return false; +} + +static bool +is_byte_a_type(uint8 type) +{ + return is_valid_value_type_for_interpreter(type) + || (type == VALUE_TYPE_VOID); +} + +#define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p) +#define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) +#define read_bool(p) TEMPLATE_READ_VALUE(bool, p) + +#define read_leb_int64(p, p_end, res) \ + do { \ + uint64 res64; \ + read_leb((uint8 **)&p, p_end, 64, true, &res64, error_buf, \ + error_buf_size); \ + res = (int64)res64; \ + } while (0) + +#define read_leb_uint32(p, p_end, res) \ + do { \ + uint64 res64; \ + read_leb((uint8 **)&p, p_end, 32, false, &res64, error_buf, \ + error_buf_size); \ + res = (uint32)res64; \ + } while (0) + +#define read_leb_int32(p, p_end, res) \ + do { \ + uint64 res64; \ + read_leb((uint8 **)&p, p_end, 32, true, &res64, error_buf, \ + error_buf_size); \ + res = (int32)res64; \ + } while (0) + +#if WASM_ENABLE_MEMORY64 != 0 +#define read_leb_mem_offset(p, p_end, res) \ + do { \ + uint64 res64; \ + read_leb((uint8 **)&p, p_end, is_memory64 ? 64 : 32, false, &res64, \ + error_buf, error_buf_size); \ + res = (mem_offset_t)res64; \ + } while (0) +#else +#define read_leb_mem_offset(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif +#define read_leb_memidx(p, p_end, res) read_leb_uint32(p, p_end, res) +#if WASM_ENABLE_MULTI_MEMORY != 0 +#define check_memidx(module, memidx) \ + do { \ + bh_assert(memidx \ + < module->import_memory_count + module->memory_count); \ + (void)memidx; \ + } while (0) +/* Bit 6 indicating the optional memidx, and reset bit 6 for + * alignment check */ +#define read_leb_memarg(p, p_end, res) \ + do { \ + read_leb_uint32(p, p_end, res); \ + if (res & OPT_MEMIDX_FLAG) { \ + res &= ~OPT_MEMIDX_FLAG; \ + read_leb_uint32(p, p_end, memidx); /* memidx */ \ + check_memidx(module, memidx); \ + } \ + } while (0) +#else +/* reserved byte 0x00 */ +#define check_memidx(module, memidx) \ + do { \ + (void)module; \ + bh_assert(memidx == 0); \ + (void)memidx; \ + } while (0) +#define read_leb_memarg(p, p_end, res) read_leb_uint32(p, p_end, res) +#endif + +static void * +loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static void * +memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, char *error_buf, + uint32 error_buf_size) +{ + uint8 *mem_new; + bh_assert(size_new > size_old); + + if ((mem_new = wasm_runtime_realloc(mem_old, size_new))) { + memset(mem_new + size_old, 0, size_new - size_old); + return mem_new; + } + + if ((mem_new = loader_malloc(size_new, error_buf, error_buf_size))) { + bh_memcpy_s(mem_new, size_new, mem_old, size_old); + wasm_runtime_free(mem_old); + } + return mem_new; +} + +#define MEM_REALLOC(mem, size_old, size_new) \ + do { \ + void *mem_new = memory_realloc(mem, size_old, size_new, error_buf, \ + error_buf_size); \ + if (!mem_new) \ + goto fail; \ + mem = mem_new; \ + } while (0) + +static void +destroy_wasm_type(WASMFuncType *type) +{ + if (type->ref_count > 1) { + /* The type is referenced by other types + of current wasm module */ + type->ref_count--; + return; + } + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + if (type->call_to_llvm_jit_from_fast_jit) + jit_code_cache_free(type->call_to_llvm_jit_from_fast_jit); +#endif + + wasm_runtime_free(type); +} + +static bool +check_function_index(const WASMModule *module, uint32 function_index, + char *error_buf, uint32 error_buf_size) +{ + return (function_index + < module->import_function_count + module->function_count); +} + +typedef struct InitValue { + uint8 type; + uint8 flag; + WASMValue value; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression *expr; +#endif +} InitValue; + +typedef struct ConstExprContext { + uint32 sp; + uint32 size; + WASMModule *module; + InitValue *stack; + InitValue data[WASM_CONST_EXPR_STACK_SIZE]; +} ConstExprContext; + +static void +init_const_expr_stack(ConstExprContext *ctx, WASMModule *module) +{ + ctx->sp = 0; + ctx->module = module; + ctx->stack = ctx->data; + ctx->size = WASM_CONST_EXPR_STACK_SIZE; +} + +static bool +push_const_expr_stack(ConstExprContext *ctx, uint8 flag, uint8 type, + WASMValue *value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression *expr, +#endif + char *error_buf, uint32 error_buf_size) +{ + InitValue *cur_value; + + if (ctx->sp >= ctx->size) { + if (ctx->stack != ctx->data) { + MEM_REALLOC(ctx->stack, ctx->size * sizeof(InitValue), + (ctx->size + 4) * sizeof(InitValue)); + } + else { + if (!(ctx->stack = + loader_malloc((ctx->size + 4) * (uint64)sizeof(InitValue), + error_buf, error_buf_size))) { + return false; + } + } + ctx->size += 4; + } + + cur_value = &ctx->stack[ctx->sp++]; + cur_value->type = type; + cur_value->flag = flag; + cur_value->value = *value; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + cur_value->expr = expr; +#endif + + return true; +fail: + return false; +} + +static bool +pop_const_expr_stack(ConstExprContext *ctx, uint8 *p_flag, uint8 type, + WASMValue *p_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression **p_expr, +#endif + char *error_buf, uint32 error_buf_size) +{ + InitValue *cur_value; + + if (ctx->sp == 0) { + return false; + } + + cur_value = &ctx->stack[--ctx->sp]; + + if (cur_value->type != type) { + return false; + } + + if (p_flag) + *p_flag = cur_value->flag; + if (p_value) + *p_value = cur_value->value; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + if (p_expr) + *p_expr = cur_value->expr; +#endif + + return true; +} + +static void +destroy_const_expr_stack(ConstExprContext *ctx, bool free_exprs) +{ +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + if (free_exprs) { + for (uint32 j = 0; j < ctx->sp; j++) { + if (is_expr_binary_op(ctx->stack[j].expr->init_expr_type)) { + destroy_init_expr_recursive(ctx->stack[j].expr); + ctx->stack[j].expr = NULL; + } + } + } +#endif + if (ctx->stack != ctx->data) { + wasm_runtime_free(ctx->stack); + } +} + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 +static void +destroy_init_expr(InitializerExpression *expr) +{ + /* free left expr and right exprs for binary operand */ + if (!is_expr_binary_op(expr->init_expr_type)) { + return; + } + if (expr->u.binary.l_expr) { + destroy_init_expr_recursive(expr->u.binary.l_expr); + } + if (expr->u.binary.r_expr) { + destroy_init_expr_recursive(expr->u.binary.r_expr); + } + expr->u.binary.l_expr = expr->u.binary.r_expr = NULL; +} +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */ + +static bool +load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end, + InitializerExpression *init_expr, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 flag, *p_float; + uint32 i; + ConstExprContext const_expr_ctx = { 0 }; + WASMValue cur_value = { 0 }; +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + InitializerExpression *cur_expr = NULL; +#endif + + init_const_expr_stack(&const_expr_ctx, module); + + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + + while (flag != WASM_OP_END) { + switch (flag) { + /* i32.const */ + case INIT_EXPR_TYPE_I32_CONST: + read_leb_int32(p, p_end, cur_value.i32); + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_I32, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + /* i64.const */ + case INIT_EXPR_TYPE_I64_CONST: + read_leb_int64(p, p_end, cur_value.i64); + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_I64, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + /* f32.const */ + case INIT_EXPR_TYPE_F32_CONST: + CHECK_BUF(p, p_end, 4); + p_float = (uint8 *)&cur_value.f32; + for (i = 0; i < sizeof(float32); i++) + *p_float++ = *p++; + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_F32, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + /* f64.const */ + case INIT_EXPR_TYPE_F64_CONST: + CHECK_BUF(p, p_end, 8); + p_float = (uint8 *)&cur_value.f64; + for (i = 0; i < sizeof(float64); i++) + *p_float++ = *p++; + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_F64, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + +#if WASM_ENABLE_REF_TYPES != 0 + /* ref.func */ + case INIT_EXPR_TYPE_FUNCREF_CONST: + { + uint32 func_idx; + read_leb_uint32(p, p_end, func_idx); + cur_value.ref_index = func_idx; + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + + if (!push_const_expr_stack(&const_expr_ctx, flag, + VALUE_TYPE_FUNCREF, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + } + + /* ref.null */ + case INIT_EXPR_TYPE_REFNULL_CONST: + { + uint8 type1; + + CHECK_BUF(p, p_end, 1); + type1 = read_uint8(p); + + cur_value.ref_index = UINT32_MAX; + if (!push_const_expr_stack(&const_expr_ctx, flag, type1, + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) { + goto fail; + } + break; + } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 */ + + /* get_global */ + case INIT_EXPR_TYPE_GET_GLOBAL: + { + uint32 global_idx; + uint8 global_type; + + read_leb_uint32(p, p_end, cur_value.global_index); + global_idx = cur_value.global_index; + + bh_assert(global_idx < module->import_global_count); + bh_assert(!module->import_globals[global_idx] + .u.global.type.is_mutable); + + if (global_idx < module->import_global_count) { + global_type = module->import_globals[global_idx] + .u.global.type.val_type; + } + else { + global_type = + module + ->globals[global_idx - module->import_global_count] + .type.val_type; + } + + if (!push_const_expr_stack(&const_expr_ctx, flag, global_type, + &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + NULL, +#endif + error_buf, error_buf_size)) + goto fail; + + break; + } +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_MUL: + { + InitializerExpression *l_expr, *r_expr; + WASMValue l_value, r_value; + uint8 l_flag, r_flag; + uint8 value_type; + + if (flag == INIT_EXPR_TYPE_I32_ADD + || flag == INIT_EXPR_TYPE_I32_SUB + || flag == INIT_EXPR_TYPE_I32_MUL) { + value_type = VALUE_TYPE_I32; + } + else { + value_type = VALUE_TYPE_I64; + } + + /* If right flag indicates a binary operation, right expr will + * be popped from stack. Otherwise, allocate a new expr for + * right expr. Same for left expr. + */ + if (!(pop_const_expr_stack(&const_expr_ctx, &r_flag, value_type, + &r_value, &r_expr, error_buf, + error_buf_size))) { + goto fail; + } + if (!is_expr_binary_op(r_flag)) { + if (!(r_expr = loader_malloc(sizeof(InitializerExpression), + error_buf, error_buf_size))) { + goto fail; + } + r_expr->init_expr_type = r_flag; + r_expr->u.unary.v = r_value; + } + + if (!(pop_const_expr_stack(&const_expr_ctx, &l_flag, value_type, + &l_value, &l_expr, error_buf, + error_buf_size))) { + destroy_init_expr_recursive(r_expr); + goto fail; + } + if (!is_expr_binary_op(l_flag)) { + if (!(l_expr = loader_malloc(sizeof(InitializerExpression), + error_buf, error_buf_size))) { + destroy_init_expr_recursive(r_expr); + goto fail; + } + l_expr->init_expr_type = l_flag; + l_expr->u.unary.v = l_value; + } + + if (!(cur_expr = loader_malloc(sizeof(InitializerExpression), + error_buf, error_buf_size))) { + destroy_init_expr_recursive(l_expr); + destroy_init_expr_recursive(r_expr); + goto fail; + } + cur_expr->init_expr_type = flag; + cur_expr->u.binary.l_expr = l_expr; + cur_expr->u.binary.r_expr = r_expr; + + if (!push_const_expr_stack(&const_expr_ctx, flag, value_type, + &cur_value, cur_expr, error_buf, + error_buf_size)) { + destroy_init_expr_recursive(cur_expr); + goto fail; + } + break; + } +#endif + default: + { + goto fail; + } + } + + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + } + + /* There should be only one value left on the init value stack */ + if (!pop_const_expr_stack(&const_expr_ctx, &flag, type, &cur_value, +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + &cur_expr, +#endif + error_buf, error_buf_size)) { + goto fail; + } + + if (const_expr_ctx.sp != 0) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: illegal constant opcode sequence"); + goto fail; + } + +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + if (cur_expr != NULL) { + bh_memcpy_s(init_expr, sizeof(InitializerExpression), cur_expr, + sizeof(InitializerExpression)); + wasm_runtime_free(cur_expr); + } + else { + init_expr->init_expr_type = flag; + init_expr->u.unary.v = cur_value; + } + +#else + init_expr->init_expr_type = flag; + init_expr->u.unary.v = cur_value; +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */ + + *p_buf = p; + destroy_const_expr_stack(&const_expr_ctx, false); + return true; + +fail: + destroy_const_expr_stack(&const_expr_ctx, true); + return false; +} + +static bool +load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_org; + uint32 type_count, param_count, result_count, i, j; + uint32 param_cell_num, ret_cell_num; + uint64 total_size; + uint8 flag; + WASMFuncType *type; + + read_leb_uint32(p, p_end, type_count); + + if (type_count) { + module->type_count = type_count; + total_size = sizeof(WASMFuncType *) * (uint64)type_count; + if (!(module->types = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < type_count; i++) { + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + bh_assert(flag == 0x60); + + read_leb_uint32(p, p_end, param_count); + + /* Resolve param count and result count firstly */ + p_org = p; + CHECK_BUF(p, p_end, param_count); + p += param_count; + read_leb_uint32(p, p_end, result_count); + CHECK_BUF(p, p_end, result_count); + p = p_org; + + bh_assert(param_count <= UINT16_MAX && result_count <= UINT16_MAX); + + total_size = offsetof(WASMFuncType, types) + + sizeof(uint8) * (uint64)(param_count + result_count); + if (!(type = module->types[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Resolve param types and result types */ + type->ref_count = 1; + type->param_count = (uint16)param_count; + type->result_count = (uint16)result_count; + for (j = 0; j < param_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[j] = read_uint8(p); + } + read_leb_uint32(p, p_end, result_count); + for (j = 0; j < result_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[param_count + j] = read_uint8(p); + } + for (j = 0; j < param_count + result_count; j++) { + bh_assert(is_valid_value_type_for_interpreter(type->types[j])); + } + + param_cell_num = wasm_get_cell_num(type->types, param_count); + ret_cell_num = + wasm_get_cell_num(type->types + param_count, result_count); + bh_assert(param_cell_num <= UINT16_MAX + && ret_cell_num <= UINT16_MAX); + type->param_cell_num = (uint16)param_cell_num; + type->ret_cell_num = (uint16)ret_cell_num; + +#if WASM_ENABLE_QUICK_AOT_ENTRY != 0 + type->quick_aot_entry = wasm_native_lookup_quick_aot_entry(type); +#endif + + /* If there is already a same type created, use it instead */ + for (j = 0; j < i; ++j) { + if (wasm_type_equal(type, module->types[j], module->types, i)) { + bh_assert(module->types[j]->ref_count != UINT16_MAX); + destroy_wasm_type(type); + module->types[i] = module->types[j]; + module->types[j]->ref_count++; + break; + } + } + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load type section success.\n"); + (void)flag; + return true; +} + +static void +adjust_table_max_size(bool is_table64, uint32 init_size, uint32 max_size_flag, + uint32 *max_size) +{ + uint32 default_max_size = init_size * 2 > WASM_TABLE_MAX_SIZE + ? init_size * 2 + : WASM_TABLE_MAX_SIZE; + /* TODO: current still use UINT32_MAX as upper limit for table size to keep + * ABI unchanged */ + (void)is_table64; + + if (max_size_flag) { + /* module defines the table limitation */ + bh_assert(init_size <= *max_size); + + if (init_size < *max_size) { + *max_size = + *max_size < default_max_size ? *max_size : default_max_size; + } + } + else { + /* partial defined table limitation, gives a default value */ + *max_size = default_max_size; + } +} + +static bool +load_function_import(const uint8 **p_buf, const uint8 *buf_end, + const WASMModule *parent_module, + const char *sub_module_name, const char *function_name, + WASMFunctionImport *function, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_type_index = 0; + WASMFuncType *declare_func_type = NULL; + WASMFunction *linked_func = NULL; + const char *linked_signature = NULL; + void *linked_attachment = NULL; + bool linked_call_conv_raw = false; + + read_leb_uint32(p, p_end, declare_type_index); + *p_buf = p; + + bh_assert(declare_type_index < parent_module->type_count); + + declare_func_type = parent_module->types[declare_type_index]; + + /* check built-in modules */ + linked_func = wasm_native_resolve_symbol( + sub_module_name, function_name, declare_func_type, &linked_signature, + &linked_attachment, &linked_call_conv_raw); + + function->module_name = (char *)sub_module_name; + function->field_name = (char *)function_name; + function->func_type = declare_func_type; + function->func_ptr_linked = linked_func; + function->signature = linked_signature; + function->attachment = linked_attachment; + function->call_conv_raw = linked_call_conv_raw; + return true; +} + +static bool +load_table_import(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *parent_module, const char *sub_module_name, + const char *table_name, WASMTableImport *table, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + uint32 declare_elem_type = 0, table_flag = 0, declare_init_size = 0, + declare_max_size = 0; + + CHECK_BUF(p, p_end, 1); + /* 0x70 or 0x6F */ + declare_elem_type = read_uint8(p); + bh_assert(VALUE_TYPE_FUNCREF == declare_elem_type +#if WASM_ENABLE_REF_TYPES != 0 + || VALUE_TYPE_EXTERNREF == declare_elem_type +#endif + ); + + /* the table flag can't exceed one byte, only check in debug build given + * the nature of mini-loader */ + p_org = p; + read_leb_uint32(p, p_end, table_flag); + bh_assert(p - p_org <= 1); + (void)p_org; + + if (!wasm_table_check_flags(table_flag, error_buf, error_buf_size, false)) { + return false; + } + + read_leb_uint32(p, p_end, declare_init_size); + if (table_flag & MAX_TABLE_SIZE_FLAG) { + read_leb_uint32(p, p_end, declare_max_size); + bh_assert(table->table_type.init_size <= table->table_type.max_size); + } + + adjust_table_max_size(table_flag & TABLE64_FLAG, declare_init_size, + table_flag & MAX_TABLE_SIZE_FLAG, &declare_max_size); + *p_buf = p; + + bh_assert(!((table_flag & MAX_TABLE_SIZE_FLAG) + && declare_init_size > declare_max_size)); + + /* now we believe all declaration are ok */ + table->table_type.elem_type = declare_elem_type; + table->table_type.init_size = declare_init_size; + table->table_type.flags = table_flag; + table->table_type.max_size = declare_max_size; + return true; +} + +static bool +load_memory_import(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *parent_module, const char *sub_module_name, + const char *memory_name, WASMMemoryImport *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 pool_size = wasm_runtime_memory_pool_size(); + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count; +#endif /* WASM_ENABLE_APP_FRAMEWORK */ + uint32 mem_flag = 0; + bool is_memory64 = false; + uint32 declare_init_page_count = 0; + uint32 declare_max_page_count = 0; + + /* the memory flag can't exceed one byte, only check in debug build given + * the nature of mini-loader */ + p_org = p; + read_leb_uint32(p, p_end, mem_flag); + bh_assert(p - p_org <= 1); + (void)p_org; + + if (!wasm_memory_check_flags(mem_flag, error_buf, error_buf_size, false)) { + return false; + } + +#if WASM_ENABLE_APP_FRAMEWORK == 0 + is_memory64 = mem_flag & MEMORY64_FLAG; + max_page_count = is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; +#endif + + read_leb_uint32(p, p_end, declare_init_page_count); + bh_assert(declare_init_page_count <= max_page_count); + + if (mem_flag & MAX_PAGE_COUNT_FLAG) { + read_leb_uint32(p, p_end, declare_max_page_count); + bh_assert(declare_init_page_count <= declare_max_page_count); + bh_assert(declare_max_page_count <= max_page_count); + if (declare_max_page_count > max_page_count) { + declare_max_page_count = max_page_count; + } + } + else { + /* Limit the maximum memory size to max_page_count */ + declare_max_page_count = max_page_count; + } + + /* now we believe all declaration are ok */ + memory->mem_type.flags = mem_flag; + memory->mem_type.init_page_count = declare_init_page_count; + memory->mem_type.max_page_count = declare_max_page_count; + memory->mem_type.num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +} + +static bool +load_global_import(const uint8 **p_buf, const uint8 *buf_end, + const WASMModule *parent_module, char *sub_module_name, + char *global_name, WASMGlobalImport *global, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 declare_type = 0; + uint8 declare_mutable = 0; + bool is_mutable = false; + bool ret = false; + + CHECK_BUF(p, p_end, 2); + declare_type = read_uint8(p); + declare_mutable = read_uint8(p); + *p_buf = p; + + bh_assert(declare_mutable < 2); + + is_mutable = declare_mutable & 1 ? true : false; + +#if WASM_ENABLE_LIBC_BUILTIN != 0 + /* check built-in modules */ + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, global_name, + global); + if (ret) { + bh_assert(global->type.val_type == declare_type + && global->type.is_mutable != declare_mutable); + } +#endif /* WASM_ENABLE_LIBC_BUILTIN */ + + global->is_linked = ret; + global->module_name = sub_module_name; + global->field_name = global_name; + global->type.val_type = declare_type; + global->type.is_mutable = is_mutable; + (void)p_end; + return true; +} + +static bool +load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + + CHECK_BUF(p, p_end, 1); + /* 0x70 or 0x6F */ + table->table_type.elem_type = read_uint8(p); + bh_assert((VALUE_TYPE_FUNCREF == table->table_type.elem_type) +#if WASM_ENABLE_REF_TYPES != 0 + || VALUE_TYPE_EXTERNREF == table->table_type.elem_type +#endif + ); + + /* the table flag can't exceed one byte, only check in debug build given + * the nature of mini-loader */ + p_org = p; + read_leb_uint32(p, p_end, table->table_type.flags); + bh_assert(p - p_org <= 1); + (void)p_org; + + if (!wasm_table_check_flags(table->table_type.flags, error_buf, + error_buf_size, false)) { + return false; + } + + read_leb_uint32(p, p_end, table->table_type.init_size); + if (table->table_type.flags == MAX_TABLE_SIZE_FLAG) { + read_leb_uint32(p, p_end, table->table_type.max_size); + bh_assert(table->table_type.init_size <= table->table_type.max_size); + } + + adjust_table_max_size(table->table_type.flags & TABLE64_FLAG, + table->table_type.init_size, + table->table_type.flags & MAX_TABLE_SIZE_FLAG, + &table->table_type.max_size); + + *p_buf = p; + return true; +} + +static bool +load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 pool_size = wasm_runtime_memory_pool_size(); + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count; + bool is_memory64 = false; +#endif + + /* the memory flag can't exceed one byte, only check in debug build given + * the nature of mini-loader */ + p_org = p; + read_leb_uint32(p, p_end, memory->flags); + bh_assert(p - p_org <= 1); + (void)p_org; + if (!wasm_memory_check_flags(memory->flags, error_buf, error_buf_size, + false)) { + return false; + } + +#if WASM_ENABLE_APP_FRAMEWORK == 0 + is_memory64 = memory->flags & MEMORY64_FLAG; + max_page_count = is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; +#endif + + read_leb_uint32(p, p_end, memory->init_page_count); + bh_assert(memory->init_page_count <= max_page_count); + + if (memory->flags & 1) { + read_leb_uint32(p, p_end, memory->max_page_count); + bh_assert(memory->init_page_count <= memory->max_page_count); + bh_assert(memory->max_page_count <= max_page_count); + if (memory->max_page_count > max_page_count) + memory->max_page_count = max_page_count; + } + else { + /* Limit the maximum memory size to max_page_count */ + memory->max_page_count = max_page_count; + } + + memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +} + +static bool +load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_old; + uint32 import_count, name_len, type_index, i, u32, flags; + uint64 total_size; + WASMImport *import; + WASMImport *import_functions = NULL, *import_tables = NULL; + WASMImport *import_memories = NULL, *import_globals = NULL; + char *sub_module_name, *field_name; + uint8 u8, kind; + + read_leb_uint32(p, p_end, import_count); + + if (import_count) { + module->import_count = import_count; + total_size = sizeof(WASMImport) * (uint64)import_count; + if (!(module->imports = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + p_old = p; + + /* Scan firstly to get import count of each type */ + for (i = 0; i < import_count; i++) { + /* module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + /* field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03 */ + kind = read_uint8(p); + + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + read_leb_uint32(p, p_end, type_index); + module->import_function_count++; + break; + + case IMPORT_KIND_TABLE: /* import table */ + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + u8 = read_uint8(p); + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_table_count++; +#if WASM_ENABLE_REF_TYPES == 0 + bh_assert(module->import_table_count <= 1); +#endif + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_memory_count++; +#if WASM_ENABLE_MULTI_MEMORY != 0 + bh_assert(module->import_memory_count <= 1); +#endif + break; + + case IMPORT_KIND_GLOBAL: /* import global */ + CHECK_BUF(p, p_end, 2); + p += 2; + module->import_global_count++; + break; + + default: + bh_assert(0); + break; + } + } + + if (module->import_function_count) + import_functions = module->import_functions = module->imports; + if (module->import_table_count) + import_tables = module->import_tables = + module->imports + module->import_function_count; + if (module->import_memory_count) + import_memories = module->import_memories = + module->imports + module->import_function_count + + module->import_table_count; + if (module->import_global_count) + import_globals = module->import_globals = + module->imports + module->import_function_count + + module->import_table_count + module->import_memory_count; + + p = p_old; + + /* Scan again to resolve the data */ + for (i = 0; i < import_count; i++) { + WASMModule *sub_module = NULL; + + /* load module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(sub_module_name = wasm_const_str_list_insert( + p, name_len, module, is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + p += name_len; + + /* load field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(field_name = wasm_const_str_list_insert( + p, name_len, module, is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + p += name_len; + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03 */ + kind = read_uint8(p); + + LOG_DEBUG("import #%d: (%s, %s), kind: %d", i, sub_module_name, + field_name, kind); + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + bh_assert(import_functions); + import = import_functions++; + if (!load_function_import( + &p, p_end, module, sub_module_name, field_name, + &import->u.function, error_buf, error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_TABLE: /* import table */ + bh_assert(import_tables); + import = import_tables++; + if (!load_table_import(&p, p_end, module, sub_module_name, + field_name, &import->u.table, + error_buf, error_buf_size)) { + LOG_DEBUG("can not import such a table (%s,%s)", + sub_module_name, field_name); + return false; + } + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + bh_assert(import_memories); + import = import_memories++; + if (!load_memory_import(&p, p_end, module, sub_module_name, + field_name, &import->u.memory, + error_buf, error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_GLOBAL: /* import global */ + bh_assert(import_globals); + import = import_globals++; + if (!load_global_import(&p, p_end, module, sub_module_name, + field_name, &import->u.global, + error_buf, error_buf_size)) { + return false; + } + break; + + default: + bh_assert(0); + import = NULL; + break; + } + import->kind = kind; + import->u.names.module_name = sub_module_name; + import->u.names.field_name = field_name; + (void)sub_module; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + import = module->import_functions; + for (i = 0; i < module->import_function_count; i++, import++) { + if (!strcmp(import->u.names.module_name, "wasi_unstable") + || !strcmp(import->u.names.module_name, + "wasi_snapshot_preview1")) { + module->import_wasi_api = true; + break; + } + } +#endif + } + + bh_assert(p == p_end); + + LOG_VERBOSE("Load import section success.\n"); + (void)u8; + (void)u32; + (void)type_index; + return true; +} + +static bool +init_function_local_offsets(WASMFunction *func, char *error_buf, + uint32 error_buf_size) +{ + WASMFuncType *param_type = func->func_type; + uint32 param_count = param_type->param_count; + uint8 *param_types = param_type->types; + uint32 local_count = func->local_count; + uint8 *local_types = func->local_types; + uint32 i, local_offset = 0; + uint64 total_size = sizeof(uint16) * ((uint64)param_count + local_count); + + if (total_size > 0 + && !(func->local_offsets = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < param_count; i++) { + func->local_offsets[i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(param_types[i]); + } + + for (i = 0; i < local_count; i++) { + func->local_offsets[param_count + i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(local_types[i]); + } + + bh_assert(local_offset == func->param_cell_num + func->local_cell_num); + return true; +} + +static bool +load_function_section(const uint8 *buf, const uint8 *buf_end, + const uint8 *buf_code, const uint8 *buf_code_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_code = buf_code, *p_code_end, *p_code_save; + uint32 func_count; + uint64 total_size; + uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; + uint32 local_count, local_set_count, sub_local_count, local_cell_num; + uint8 type; + WASMFunction *func; + + read_leb_uint32(p, p_end, func_count); + + if (buf_code) + read_leb_uint32(p_code, buf_code_end, code_count); + + bh_assert(func_count == code_count); + + bh_assert(module->import_function_count <= UINT32_MAX - func_count); + + if (func_count) { + module->function_count = func_count; + total_size = sizeof(WASMFunction *) * (uint64)func_count; + if (!(module->functions = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < func_count; i++) { + /* Resolve function type */ + read_leb_uint32(p, p_end, type_index); + bh_assert(type_index < module->type_count); + +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) + type_index = wasm_get_smallest_type_idx( + module->types, module->type_count, type_index); +#endif + + read_leb_uint32(p_code, buf_code_end, code_size); + bh_assert(code_size > 0 && p_code + code_size <= buf_code_end); + + /* Resolve local set count */ + p_code_end = p_code + code_size; + local_count = 0; + read_leb_uint32(p_code, buf_code_end, local_set_count); + p_code_save = p_code; + + /* Calculate total local count */ + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + bh_assert(sub_local_count <= UINT32_MAX - local_count); + + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + local_count += sub_local_count; + } + + bh_assert(p_code_end > p_code && *(p_code_end - 1) == WASM_OP_END); + + /* Alloc memory, layout: function structure + local types */ + code_size = (uint32)(p_code_end - p_code); + + total_size = sizeof(WASMFunction) + (uint64)local_count; + if (!(func = module->functions[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set function type, local count, code size and code body */ + func->func_type = module->types[type_index]; + func->local_count = local_count; + if (local_count > 0) + func->local_types = (uint8 *)func + sizeof(WASMFunction); + func->code_size = code_size; + /* + * we shall make a copy of code body [p_code, p_code + code_size] + * when we are worrying about inappropriate releasing behaviour. + * all code bodies are actually in a buffer which user allocates in + * their embedding environment and we don't have power over them. + * it will be like: + * code_body_cp = malloc(code_size); + * memcpy(code_body_cp, p_code, code_size); + * func->code = code_body_cp; + */ + func->code = (uint8 *)p_code; + + /* Load each local type */ + p_code = p_code_save; + local_type_index = 0; + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + /* Note: sub_local_count is allowed to be 0 */ + bh_assert(local_type_index <= UINT32_MAX - sub_local_count + && local_type_index + sub_local_count <= local_count); + + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + bh_assert(is_valid_value_type_for_interpreter(type)); + for (k = 0; k < sub_local_count; k++) { + func->local_types[local_type_index++] = type; + } + } + + func->param_cell_num = func->func_type->param_cell_num; + func->ret_cell_num = func->func_type->ret_cell_num; + local_cell_num = + wasm_get_cell_num(func->local_types, func->local_count); + bh_assert(local_cell_num <= UINT16_MAX); + + func->local_cell_num = (uint16)local_cell_num; + + if (!init_function_local_offsets(func, error_buf, error_buf_size)) + return false; + + p_code = p_code_end; + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load function section success.\n"); + (void)code_count; + return true; +} + +static bool +load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 table_count, i; + uint64 total_size; + WASMTable *table; + + read_leb_uint32(p, p_end, table_count); +#if WASM_ENABLE_REF_TYPES == 0 + bh_assert(module->import_table_count + table_count <= 1); +#endif + + if (table_count) { + module->table_count = table_count; + total_size = sizeof(WASMTable) * (uint64)table_count; + if (!(module->tables = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each table */ + table = module->tables; + for (i = 0; i < table_count; i++, table++) + if (!load_table(&p, p_end, table, error_buf, error_buf_size)) + return false; + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load table section success.\n"); + return true; +} + +static bool +load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 memory_count, i; + uint64 total_size; + WASMMemory *memory; + + read_leb_uint32(p, p_end, memory_count); +#if WASM_ENABLE_MULTI_MEMORY != 0 + bh_assert(module->import_memory_count + memory_count <= 1); +#endif + + if (memory_count) { + module->memory_count = memory_count; + total_size = sizeof(WASMMemory) * (uint64)memory_count; + if (!(module->memories = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each memory */ + memory = module->memories; + for (i = 0; i < memory_count; i++, memory++) + if (!load_memory(&p, p_end, memory, error_buf, error_buf_size)) + return false; + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load memory section success.\n"); + return true; +} + +static bool +load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 global_count, i; + uint64 total_size; + WASMGlobal *global; + uint8 mutable; + + read_leb_uint32(p, p_end, global_count); + + bh_assert(module->import_global_count <= UINT32_MAX - global_count); + + module->global_count = 0; + if (global_count) { + total_size = sizeof(WASMGlobal) * (uint64)global_count; + if (!(module->globals = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + global = module->globals; + + for (i = 0; i < global_count; i++, global++) { + CHECK_BUF(p, p_end, 2); + global->type.val_type = read_uint8(p); + mutable = read_uint8(p); + bh_assert(mutable < 2); + global->type.is_mutable = mutable ? true : false; + + /* initialize expression */ + if (!load_init_expr(module, &p, p_end, &(global->init_expr), + global->type.val_type, error_buf, + error_buf_size)) + return false; + + if (INIT_EXPR_TYPE_GET_GLOBAL == global->init_expr.init_expr_type) { + /** + * Currently, constant expressions occurring as initializers + * of globals are further constrained in that contained + * global.get instructions are + * only allowed to refer to imported globals. + */ + uint32 target_global_index = + global->init_expr.u.unary.v.global_index; + bh_assert(target_global_index < module->import_global_count); + (void)target_global_index; + } + else if (INIT_EXPR_TYPE_FUNCREF_CONST + == global->init_expr.init_expr_type) { + bh_assert(global->init_expr.u.unary.v.ref_index + < module->import_function_count + + module->function_count); + } + + module->global_count++; + } + bh_assert(module->global_count == global_count); + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load global section success.\n"); + return true; +} + +static bool +load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 export_count, i, j, index; + uint64 total_size; + uint32 str_len; + WASMExport *export; + const char *name; + + read_leb_uint32(p, p_end, export_count); + + if (export_count) { + module->export_count = export_count; + total_size = sizeof(WASMExport) * (uint64)export_count; + if (!(module->exports = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + export = module->exports; + for (i = 0; i < export_count; i++, export ++) { + read_leb_uint32(p, p_end, str_len); + CHECK_BUF(p, p_end, str_len); + + for (j = 0; j < i; j++) { + name = module->exports[j].name; + bh_assert(!(strlen(name) == str_len + && memcmp(name, p, str_len) == 0)); + } + + if (!(export->name = wasm_const_str_list_insert( + p, str_len, module, is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + + p += str_len; + CHECK_BUF(p, p_end, 1); + export->kind = read_uint8(p); + read_leb_uint32(p, p_end, index); + export->index = index; + + switch (export->kind) { + /* function index */ + case EXPORT_KIND_FUNC: + bh_assert(index < module->function_count + + module->import_function_count); + break; + /* table index */ + case EXPORT_KIND_TABLE: + bh_assert(index < module->table_count + + module->import_table_count); + break; + /* memory index */ + case EXPORT_KIND_MEMORY: + bh_assert(index < module->memory_count + + module->import_memory_count); + break; + /* global index */ + case EXPORT_KIND_GLOBAL: + bh_assert(index < module->global_count + + module->import_global_count); + break; + default: + bh_assert(0); + break; + } + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load export section success.\n"); + (void)name; + return true; +} + +static bool +check_table_index(const WASMModule *module, uint32 table_index, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_REF_TYPES == 0 + if (table_index != 0) { + return false; + } +#endif + + if (table_index >= module->import_table_count + module->table_count) { + return false; + } + return true; +} + +static bool +load_table_index(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module, + uint32 *p_table_index, char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 table_index; + + read_leb_uint32(p, p_end, table_index); + if (!check_table_index(module, table_index, error_buf, error_buf_size)) { + return false; + } + + *p_table_index = table_index; + *p_buf = p; + return true; +} + +#if WASM_ENABLE_REF_TYPES != 0 +static bool +load_elem_type(const uint8 **p_buf, const uint8 *buf_end, uint32 *p_elem_type, + bool elemkind_zero, char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 elem_type; + + CHECK_BUF(p, p_end, 1); + elem_type = read_uint8(p); + if ((elemkind_zero && elem_type != 0) + || (!elemkind_zero && elem_type != VALUE_TYPE_FUNCREF + && elem_type != VALUE_TYPE_EXTERNREF)) { + set_error_buf(error_buf, error_buf_size, "invalid reference type"); + return false; + } + + if (elemkind_zero) + *p_elem_type = VALUE_TYPE_FUNCREF; + else + *p_elem_type = elem_type; + *p_buf = p; + + (void)p_end; + return true; +} +#endif /* WASM_ENABLE_REF_TYPES != 0*/ + +static bool +load_func_index_vec(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, WASMTableSeg *table_segment, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 function_count, function_index = 0, i; + uint64 total_size; + + read_leb_uint32(p, p_end, function_count); + table_segment->value_count = function_count; + total_size = sizeof(InitializerExpression) * (uint64)function_count; + if (total_size > 0 + && !(table_segment->init_values = + (InitializerExpression *)loader_malloc(total_size, error_buf, + error_buf_size))) { + return false; + } + + for (i = 0; i < function_count; i++) { + InitializerExpression *init_expr = &table_segment->init_values[i]; + + read_leb_uint32(p, p_end, function_index); + if (!check_function_index(module, function_index, error_buf, + error_buf_size)) { + return false; + } + + init_expr->init_expr_type = INIT_EXPR_TYPE_FUNCREF_CONST; + init_expr->u.unary.v.ref_index = function_index; + } + + *p_buf = p; + return true; +} + +#if WASM_ENABLE_REF_TYPES != 0 +static bool +load_init_expr_vec(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, WASMTableSeg *table_segment, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 ref_count, i; + uint64 total_size; + + read_leb_uint32(p, p_end, ref_count); + table_segment->value_count = ref_count; + total_size = sizeof(InitializerExpression) * (uint64)ref_count; + if (total_size > 0 + && !(table_segment->init_values = + (InitializerExpression *)loader_malloc(total_size, error_buf, + error_buf_size))) { + return false; + } + + for (i = 0; i < ref_count; i++) { + InitializerExpression *init_expr = &table_segment->init_values[i]; + + if (!load_init_expr(module, &p, p_end, init_expr, + table_segment->elem_type, error_buf, + error_buf_size)) + return false; + + bh_assert( + (init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) + || (init_expr->init_expr_type == INIT_EXPR_TYPE_REFNULL_CONST) + || (init_expr->init_expr_type == INIT_EXPR_TYPE_FUNCREF_CONST)); + } + + *p_buf = p; + return true; +} +#endif /* end of WASM_ENABLE_REF_TYPES != 0 */ + +static bool +load_table_segment_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint8 table_elem_idx_type; + uint32 table_segment_count, i, table_index, function_count; + uint64 total_size; + WASMTableSeg *table_segment; + + read_leb_uint32(p, p_end, table_segment_count); + + if (table_segment_count) { + module->table_seg_count = table_segment_count; + total_size = sizeof(WASMTableSeg) * (uint64)table_segment_count; + if (!(module->table_segments = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + table_segment = module->table_segments; + for (i = 0; i < table_segment_count; i++, table_segment++) { + bh_assert(p < p_end); + table_elem_idx_type = VALUE_TYPE_I32; + +#if WASM_ENABLE_REF_TYPES != 0 + read_leb_uint32(p, p_end, table_segment->mode); + /* last three bits */ + table_segment->mode = table_segment->mode & 0x07; + switch (table_segment->mode) { + /* elemkind/elemtype + active */ + case 0: + case 4: + table_segment->elem_type = VALUE_TYPE_FUNCREF; + table_segment->table_index = 0; + + if (!check_table_index(module, table_segment->table_index, + error_buf, error_buf_size)) + return false; + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = + is_table_64bit(module, table_segment->table_index) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (!load_init_expr( + module, &p, p_end, &table_segment->base_offset, + table_elem_idx_type, error_buf, error_buf_size)) + return false; + + if (table_segment->mode == 0) { + /* vec(funcidx) */ + if (!load_func_index_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + else { + /* vec(expr) */ + if (!load_init_expr_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + break; + /* elemkind + passive/declarative */ + case 1: + case 3: + if (!load_elem_type(&p, p_end, &table_segment->elem_type, + true, error_buf, error_buf_size)) + return false; + /* vec(funcidx) */ + if (!load_func_index_vec(&p, p_end, module, table_segment, + error_buf, error_buf_size)) + return false; + break; + /* elemkind/elemtype + table_idx + active */ + case 2: + case 6: + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, + error_buf, error_buf_size)) + return false; +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = + is_table_64bit(module, table_segment->table_index) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (!load_init_expr( + module, &p, p_end, &table_segment->base_offset, + table_elem_idx_type, error_buf, error_buf_size)) + return false; + if (!load_elem_type(&p, p_end, &table_segment->elem_type, + table_segment->mode == 2 ? true : false, + error_buf, error_buf_size)) + return false; + if (table_segment->mode == 2) { + /* vec(funcidx) */ + if (!load_func_index_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + else { + /* vec(expr) */ + if (!load_init_expr_vec(&p, p_end, module, + table_segment, error_buf, + error_buf_size)) + return false; + } + break; + case 5: + case 7: + if (!load_elem_type(&p, p_end, &table_segment->elem_type, + false, error_buf, error_buf_size)) + return false; + /* vec(expr) */ + if (!load_init_expr_vec(&p, p_end, module, table_segment, + error_buf, error_buf_size)) + return false; + break; + default: + return false; + } +#else + /* + * like: 00 41 05 0b 04 00 01 00 01 + * for: (elem 0 (offset (i32.const 5)) $f1 $f2 $f1 $f2) + */ + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, error_buf, + error_buf_size)) + return false; +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = + is_table_64bit(module, table_segment->table_index) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (!load_init_expr(module, &p, p_end, &table_segment->base_offset, + table_elem_idx_type, error_buf, error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, table_segment, + error_buf, error_buf_size)) + return false; +#endif /* WASM_ENABLE_REF_TYPES != 0 */ + +#if WASM_ENABLE_MEMORY64 != 0 + if (table_elem_idx_type == VALUE_TYPE_I64 + && table_segment->base_offset.u.u64 > UINT32_MAX) { + set_error_buf(error_buf, error_buf_size, + "In table64, table base offset can't be " + "larger than UINT32_MAX"); + return false; + } +#endif + } + } + + (void)table_index; + (void)function_count; + bh_assert(p == p_end); + LOG_VERBOSE("Load table segment section success.\n"); + return true; +} + +static bool +load_data_segment_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, +#if WASM_ENABLE_BULK_MEMORY != 0 + bool has_datacount_section, +#endif + bool clone_data_seg, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count, i, mem_index, data_seg_len; + uint64 total_size; + WASMDataSeg *dataseg; + InitializerExpression init_expr; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive = false; + uint32 mem_flag; +#endif + uint8 mem_offset_type = VALUE_TYPE_I32; + + read_leb_uint32(p, p_end, data_seg_count); + +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_assert(!has_datacount_section + || data_seg_count == module->data_seg_count1); +#endif + + if (data_seg_count) { + module->data_seg_count = data_seg_count; + total_size = sizeof(WASMDataSeg *) * (uint64)data_seg_count; + if (!(module->data_segments = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < data_seg_count; i++) { + read_leb_uint32(p, p_end, mem_index); +#if WASM_ENABLE_BULK_MEMORY != 0 + is_passive = false; + mem_flag = mem_index & 0x03; + switch (mem_flag) { + case 0x01: + is_passive = true; + break; + case 0x00: + /* no memory index, treat index as 0 */ + mem_index = 0; + goto check_mem_index; + case 0x02: + /* read following memory index */ + read_leb_uint32(p, p_end, mem_index); + check_mem_index: + bh_assert(mem_index < module->import_memory_count + + module->memory_count); + break; + case 0x03: + default: + bh_assert(0); + break; + } +#else + bh_assert(mem_index + < module->import_memory_count + module->memory_count); +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif /* WASM_ENABLE_BULK_MEMORY */ + { +#if WASM_ENABLE_MEMORY64 != 0 + /* This memory_flag is from memory instead of data segment */ + uint8 memory_flag; + if (module->import_memory_count > 0) { + memory_flag = module->import_memories[mem_index] + .u.memory.mem_type.flags; + } + else { + memory_flag = + module + ->memories[mem_index - module->import_memory_count] + .flags; + } + mem_offset_type = memory_flag & MEMORY64_FLAG ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#else + mem_offset_type = VALUE_TYPE_I32; +#endif /* WASM_ENABLE_MEMORY64 */ + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif + if (!load_init_expr(module, &p, p_end, &init_expr, + mem_offset_type, error_buf, error_buf_size)) + return false; + + read_leb_uint32(p, p_end, data_seg_len); + + if (!(dataseg = module->data_segments[i] = loader_malloc( + sizeof(WASMDataSeg), error_buf, error_buf_size))) { +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(&init_expr); +#endif + return false; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + dataseg->is_passive = is_passive; + if (!is_passive) +#endif + { + bh_memcpy_s(&dataseg->base_offset, + sizeof(InitializerExpression), &init_expr, + sizeof(InitializerExpression)); + + dataseg->memory_index = mem_index; + } + + dataseg->data_length = data_seg_len; + CHECK_BUF(p, p_end, data_seg_len); + if (clone_data_seg) { + if (!(dataseg->data = loader_malloc( + dataseg->data_length, error_buf, error_buf_size))) { + return false; + } + + bh_memcpy_s(dataseg->data, dataseg->data_length, p, + data_seg_len); + } + else { + dataseg->data = (uint8 *)p; + } + dataseg->is_data_cloned = clone_data_seg; + p += data_seg_len; + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load data segment section success.\n"); + return true; +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +load_datacount_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count1 = 0; + + read_leb_uint32(p, p_end, data_seg_count1); + module->data_seg_count1 = data_seg_count1; + + bh_assert(p == p_end); + LOG_VERBOSE("Load datacount section success.\n"); + return true; +} +#endif + +static bool +load_code_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_func, + const uint8 *buf_func_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_func = buf_func; + uint32 func_count = 0, code_count; + + /* code has been loaded in function section, so pass it here, just check + * whether function and code section have inconsistent lengths */ + read_leb_uint32(p, p_end, code_count); + + if (buf_func) + read_leb_uint32(p_func, buf_func_end, func_count); + + bh_assert(func_count == code_count); + LOG_VERBOSE("Load code segment section success.\n"); + (void)code_count; + (void)func_count; + return true; +} + +static bool +load_start_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + WASMFuncType *type; + uint32 start_function; + + read_leb_uint32(p, p_end, start_function); + + bh_assert(start_function + < module->function_count + module->import_function_count); + + if (start_function < module->import_function_count) + type = module->import_functions[start_function].u.function.func_type; + else + type = module->functions[start_function - module->import_function_count] + ->func_type; + + bh_assert(type->param_count == 0 && type->result_count == 0); + + module->start_function = start_function; + + bh_assert(p == p_end); + LOG_VERBOSE("Load start section success.\n"); + (void)type; + return true; +} + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 +static bool +handle_name_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 name_type, subsection_size; + uint32 previous_name_type = 0; + uint32 num_func_name; + uint32 func_index; + uint32 previous_func_index = ~0U; + uint32 func_name_len; + uint32 name_index; + int i = 0; + + bh_assert(p < p_end); + + while (p < p_end) { + read_leb_uint32(p, p_end, name_type); + if (i != 0) { + bh_assert(name_type > previous_name_type); + } + previous_name_type = name_type; + read_leb_uint32(p, p_end, subsection_size); + CHECK_BUF(p, p_end, subsection_size); + switch (name_type) { + case SUB_SECTION_TYPE_FUNC: + if (subsection_size) { + read_leb_uint32(p, p_end, num_func_name); + for (name_index = 0; name_index < num_func_name; + name_index++) { + read_leb_uint32(p, p_end, func_index); + bh_assert(func_index > previous_func_index); + previous_func_index = func_index; + read_leb_uint32(p, p_end, func_name_len); + CHECK_BUF(p, p_end, func_name_len); + /* Skip the import functions */ + if (func_index >= module->import_function_count) { + func_index -= module->import_function_count; + bh_assert(func_index < module->function_count); + if (!(module->functions[func_index]->field_name = + wasm_const_str_list_insert( + p, func_name_len, module, + is_load_from_file_buf, error_buf, + error_buf_size))) { + return false; + } + } + p += func_name_len; + } + } + break; + case SUB_SECTION_TYPE_MODULE: /* TODO: Parse for module subsection + */ + case SUB_SECTION_TYPE_LOCAL: /* TODO: Parse for local subsection */ + default: + p = p + subsection_size; + break; + } + i++; + } + + (void)previous_name_type; + (void)previous_func_index; + return true; +} +#endif + +static bool +load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 name_len; + + bh_assert(p < p_end); + + read_leb_uint32(p, p_end, name_len); + + bh_assert(name_len > 0 && p + name_len <= p_end); + +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + if (name_len == 4 && memcmp(p, "name", 4) == 0) { + p += name_len; + if (!handle_name_section(p, p_end, module, is_load_from_file_buf, + error_buf, error_buf_size)) { + return false; + } + } +#endif + LOG_VERBOSE("Load custom section success.\n"); + (void)name_len; + return true; +} + +static void +calculate_global_data_offset(WASMModule *module) +{ + uint32 i, data_offset; + + data_offset = 0; + for (i = 0; i < module->import_global_count; i++) { + WASMGlobalImport *import_global = + &((module->import_globals + i)->u.global); +#if WASM_ENABLE_FAST_JIT != 0 + import_global->data_offset = data_offset; +#endif + data_offset += wasm_value_type_size(import_global->type.val_type); + } + + for (i = 0; i < module->global_count; i++) { + WASMGlobal *global = module->globals + i; +#if WASM_ENABLE_FAST_JIT != 0 + global->data_offset = data_offset; +#endif + data_offset += wasm_value_type_size(global->type.val_type); + } + + module->global_data_size = data_offset; +} + +#if WASM_ENABLE_FAST_JIT != 0 +static bool +init_fast_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ +#if WASM_ENABLE_LAZY_JIT != 0 + JitGlobals *jit_globals = jit_compiler_get_jit_globals(); +#endif + uint32 i; + + if (!module->function_count) + return true; + + if (!(module->fast_jit_func_ptrs = + loader_malloc(sizeof(void *) * module->function_count, error_buf, + error_buf_size))) { + return false; + } + +#if WASM_ENABLE_LAZY_JIT != 0 + for (i = 0; i < module->function_count; i++) { + module->fast_jit_func_ptrs[i] = + jit_globals->compile_fast_jit_and_then_call; + } +#endif + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (os_mutex_init(&module->fast_jit_thread_locks[i]) != 0) { + set_error_buf(error_buf, error_buf_size, + "init fast jit thread lock failed"); + return false; + } + module->fast_jit_thread_locks_inited[i] = true; + } + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ + +#if WASM_ENABLE_JIT != 0 +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; + bool gc_enabled = false; /* GC hasn't been enabled in mini loader */ + + if (module->function_count == 0) + return true; + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (os_mutex_init(&module->tierup_wait_lock) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup lock failed"); + return false; + } + if (os_cond_init(&module->tierup_wait_cond) != 0) { + set_error_buf(error_buf, error_buf_size, "init jit tierup cond failed"); + os_mutex_destroy(&module->tierup_wait_lock); + return false; + } + module->tierup_wait_lock_inited = true; +#endif + + size = sizeof(void *) * (uint64)module->function_count + + sizeof(bool) * (uint64)module->function_count; + if (!(module->func_ptrs = loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + module->func_ptrs_compiled = + (bool *)((uint8 *)module->func_ptrs + + sizeof(void *) * module->function_count); + + module->comp_data = aot_create_comp_data(module, NULL, gc_enabled); + if (!module->comp_data) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return false; + } + + option.is_jit_mode = true; + option.opt_level = llvm_jit_options->opt_level; + option.size_level = llvm_jit_options->size_level; + option.segue_flags = llvm_jit_options->segue_flags; + option.quick_invoke_c_api_import = + llvm_jit_options->quick_invoke_c_api_import; + +#if WASM_ENABLE_BULK_MEMORY != 0 + option.enable_bulk_memory = true; +#endif +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + option.enable_bulk_memory_opt = true; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + option.enable_thread_mgr = true; +#endif +#if WASM_ENABLE_TAIL_CALL != 0 + option.enable_tail_call = true; +#endif +#if WASM_ENABLE_SIMD != 0 + option.enable_simd = true; +#endif +#if WASM_ENABLE_REF_TYPES != 0 + option.enable_ref_types = true; +#endif +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 + option.enable_call_indirect_overlong = true; +#endif + option.enable_aux_stack_check = true; +#if WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0 \ + || WASM_ENABLE_AOT_STACK_FRAME != 0 + option.aux_stack_frame_type = AOT_STACK_FRAME_TYPE_STANDARD; + aot_call_stack_features_init_default(&option.call_stack_features); +#endif +#if WASM_ENABLE_PERF_PROFILING != 0 + option.enable_perf_profiling = true; +#endif +#if WASM_ENABLE_MEMORY_PROFILING != 0 + option.enable_memory_profiling = true; + option.enable_stack_estimation = true; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + option.enable_shared_heap = true; +#endif + + module->comp_ctx = aot_create_comp_context(module->comp_data, &option); + if (!module->comp_ctx) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return false; + } + + return true; +} + +static bool +init_llvm_jit_functions_stage2(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + char *aot_last_error; + uint32 i; + + if (module->function_count == 0) + return true; + + if (!aot_compile_wasm(module->comp_ctx)) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return false; + } + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->orcjit_stop_compiling) + return false; +#endif + + bh_print_time("Begin to lookup llvm jit functions"); + + for (i = 0; i < module->function_count; i++) { + LLVMOrcJITTargetAddress func_addr = 0; + LLVMErrorRef error; + char func_name[48]; + + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, i); + error = LLVMOrcLLLazyJITLookup(module->comp_ctx->orc_jit, &func_addr, + func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + char buf[96]; + snprintf(buf, sizeof(buf), + "failed to compile llvm jit function: %s", err_msg); + set_error_buf(error_buf, error_buf_size, buf); + LLVMDisposeErrorMessage(err_msg); + return false; + } + + /** + * No need to lock the func_ptr[func_idx] here as it is basic + * data type, the load/store for it can be finished by one cpu + * instruction, and there can be only one cpu instruction + * loading/storing at the same time. + */ + 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 + } + + bh_print_time("End lookup llvm jit functions"); + + return true; +} +#endif /* end of WASM_ENABLE_JIT != 0 */ + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 +static void * +init_llvm_jit_functions_stage2_callback(void *arg) +{ + WASMModule *module = (WASMModule *)arg; + char error_buf[128]; + uint32 error_buf_size = (uint32)sizeof(error_buf); + + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + module->orcjit_stop_compiling = true; + return NULL; + } + + os_mutex_lock(&module->tierup_wait_lock); + module->llvm_jit_inited = true; + os_cond_broadcast(&module->tierup_wait_cond); + os_mutex_unlock(&module->tierup_wait_lock); + + return NULL; +} +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 +/* The callback function to compile jit functions */ +static void * +orcjit_thread_callback(void *arg) +{ + OrcJitThreadArg *thread_arg = (OrcJitThreadArg *)arg; +#if WASM_ENABLE_JIT != 0 + AOTCompContext *comp_ctx = thread_arg->comp_ctx; +#endif + WASMModule *module = thread_arg->module; + uint32 group_idx = thread_arg->group_idx; + uint32 group_stride = WASM_ORC_JIT_BACKEND_THREAD_NUM; + uint32 func_count = module->function_count; + uint32 i; + +#if WASM_ENABLE_FAST_JIT != 0 + /* Compile fast jit functions of this group */ + for (i = group_idx; i < func_count; i += group_stride) { + if (!jit_compiler_compile(module, i + module->import_function_count)) { + LOG_ERROR("failed to compile fast jit function %u\n", i); + break; + } + + if (module->orcjit_stop_compiling) { + return NULL; + } + } +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + os_mutex_lock(&module->tierup_wait_lock); + module->fast_jit_ready_groups++; + os_mutex_unlock(&module->tierup_wait_lock); +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + /* For JIT tier-up, set each llvm jit func to call_to_fast_jit */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + uint32 j; + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + if (!jit_compiler_set_call_to_fast_jit( + module, + i + j * group_stride + module->import_function_count)) { + LOG_ERROR( + "failed to compile call_to_fast_jit for func %u\n", + i + j * group_stride + module->import_function_count); + module->orcjit_stop_compiling = true; + return NULL; + } + } + if (module->orcjit_stop_compiling) { + return NULL; + } + } + } + + /* Wait until init_llvm_jit_functions_stage2 finishes and all + fast jit functions are compiled */ + os_mutex_lock(&module->tierup_wait_lock); + while (!(module->llvm_jit_inited && module->enable_llvm_jit_compilation + && module->fast_jit_ready_groups >= group_stride)) { + os_cond_reltimedwait(&module->tierup_wait_cond, + &module->tierup_wait_lock, 10000); + if (module->orcjit_stop_compiling) { + /* init_llvm_jit_functions_stage2 failed */ + os_mutex_unlock(&module->tierup_wait_lock); + return NULL; + } + } + os_mutex_unlock(&module->tierup_wait_lock); +#endif + +#if WASM_ENABLE_JIT != 0 + /* Compile llvm jit functions of this group */ + for (i = group_idx; i < func_count; + i += group_stride * WASM_ORC_JIT_COMPILE_THREAD_NUM) { + LLVMOrcJITTargetAddress func_addr = 0; + LLVMErrorRef error; + char func_name[48]; + typedef void (*F)(void); + union { + F f; + void *v; + } u; + uint32 j; + + snprintf(func_name, sizeof(func_name), "%s%d%s", AOT_FUNC_PREFIX, i, + "_wrapper"); + LOG_DEBUG("compile llvm jit func %s", func_name); + error = + LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + LOG_ERROR("failed to compile llvm jit function %u: %s", i, err_msg); + LLVMDisposeErrorMessage(err_msg); + break; + } + + /* Call the jit wrapper function to trigger its compilation, so as + to compile the actual jit functions, since we add the latter to + function list in the PartitionFunction callback */ + u.v = (void *)func_addr; + u.f(); + + for (j = 0; j < WASM_ORC_JIT_COMPILE_THREAD_NUM; j++) { + if (i + j * group_stride < func_count) { + module->func_ptrs_compiled[i + j * group_stride] = true; +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, + i + j * group_stride); + error = LLVMOrcLLLazyJITLookup(comp_ctx->orc_jit, &func_addr, + func_name); + if (error != LLVMErrorSuccess) { + char *err_msg = LLVMGetErrorMessage(error); + LOG_ERROR("failed to compile llvm jit function %u: %s", i, + err_msg); + LLVMDisposeErrorMessage(err_msg); + /* Ignore current llvm jit func, as its func ptr is + previous set to call_to_fast_jit, which also works */ + continue; + } + + jit_compiler_set_llvm_jit_func_ptr( + module, + i + j * group_stride + module->import_function_count, + (void *)func_addr); + + /* Try to switch to call this llvm jit function instead of + fast jit function from fast jit jitted code */ + jit_compiler_set_call_to_llvm_jit( + module, + i + j * group_stride + module->import_function_count); +#endif + } + } + + if (module->orcjit_stop_compiling) { + break; + } + } +#endif + + return NULL; +} + +static void +orcjit_stop_compile_threads(WASMModule *module) +{ +#if WASM_ENABLE_LAZY_JIT != 0 + uint32 i, thread_num = (uint32)(sizeof(module->orcjit_thread_args) + / sizeof(OrcJitThreadArg)); + + module->orcjit_stop_compiling = true; + for (i = 0; i < thread_num; i++) { + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } +#endif +} + +static bool +compile_jit_functions(WASMModule *module, char *error_buf, + uint32 error_buf_size) +{ + uint32 thread_num = + (uint32)(sizeof(module->orcjit_thread_args) / sizeof(OrcJitThreadArg)); + uint32 i, j; + + bh_print_time("Begin to compile jit functions"); + + /* Create threads to compile the jit functions */ + for (i = 0; i < thread_num && i < module->function_count; i++) { +#if WASM_ENABLE_JIT != 0 + module->orcjit_thread_args[i].comp_ctx = module->comp_ctx; +#endif + module->orcjit_thread_args[i].module = module; + module->orcjit_thread_args[i].group_idx = i; + + if (os_thread_create(&module->orcjit_threads[i], orcjit_thread_callback, + (void *)&module->orcjit_thread_args[i], + APP_THREAD_STACK_SIZE_DEFAULT) + != 0) { + set_error_buf(error_buf, error_buf_size, + "create orcjit compile thread failed"); + /* Terminate the threads created */ + module->orcjit_stop_compiling = true; + for (j = 0; j < i; j++) { + os_thread_join(module->orcjit_threads[j], NULL); + } + return false; + } + } + +#if WASM_ENABLE_LAZY_JIT == 0 + /* Wait until all jit functions are compiled for eager mode */ + for (i = 0; i < thread_num; i++) { + if (module->orcjit_threads[i]) + os_thread_join(module->orcjit_threads[i], NULL); + } + +#if WASM_ENABLE_FAST_JIT != 0 + /* Ensure all the fast-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!jit_compiler_is_compiled(module, + i + module->import_function_count)) { + set_error_buf(error_buf, error_buf_size, + "failed to compile fast jit function"); + return false; + } + } +#endif + +#if WASM_ENABLE_JIT != 0 + /* Ensure all the llvm-jit functions are compiled */ + for (i = 0; i < module->function_count; i++) { + if (!module->func_ptrs_compiled[i]) { + set_error_buf(error_buf, error_buf_size, + "failed to compile llvm jit function"); + return false; + } + } +#endif +#endif /* end of WASM_ENABLE_LAZY_JIT == 0 */ + + bh_print_time("End compile jit functions"); + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 */ + +#if WASM_ENABLE_REF_TYPES != 0 +static bool +get_table_elem_type(const WASMModule *module, uint32 table_idx, + uint8 *p_elem_type, char *error_buf, uint32 error_buf_size) +{ + if (!check_table_index(module, table_idx, error_buf, error_buf_size)) { + return false; + } + + if (p_elem_type) { + if (table_idx < module->import_table_count) + *p_elem_type = + module->import_tables[table_idx].u.table.table_type.elem_type; + else + *p_elem_type = + module->tables[table_idx - module->import_table_count] + .table_type.elem_type; + } + return true; +} + +static bool +get_table_seg_elem_type(const WASMModule *module, uint32 table_seg_idx, + uint8 *p_elem_type, char *error_buf, + uint32 error_buf_size) +{ + if (table_seg_idx >= module->table_seg_count) { + return false; + } + + if (p_elem_type) { + *p_elem_type = module->table_segments[table_seg_idx].elem_type; + } + return true; +} +#endif + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + uint32 cur_func_idx, char *error_buf, + uint32 error_buf_size); + +#if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_LABELS_AS_VALUES != 0 +void ** +wasm_interp_get_handle_table(void); + +static void **handle_table; +#endif + +static bool +load_from_sections(WASMModule *module, WASMSection *sections, + bool is_load_from_file_buf, bool wasm_binary_freeable, + char *error_buf, uint32 error_buf_size) +{ + WASMExport *export; + WASMSection *section = sections; + const uint8 *buf, *buf_end, *buf_code = NULL, *buf_code_end = NULL, + *buf_func = NULL, *buf_func_end = NULL; + WASMGlobal *aux_data_end_global = NULL, *aux_heap_base_global = NULL; + WASMGlobal *aux_stack_top_global = NULL, *global; + uint64 aux_data_end = (uint64)-1LL, aux_heap_base = (uint64)-1LL, + aux_stack_top = (uint64)-1LL; + uint32 global_index, func_index, i; + uint32 aux_data_end_global_index = (uint32)-1; + uint32 aux_heap_base_global_index = (uint32)-1; + WASMFuncType *func_type; + uint8 malloc_free_io_type = VALUE_TYPE_I32; + bool reuse_const_strings = is_load_from_file_buf && !wasm_binary_freeable; + bool clone_data_seg = is_load_from_file_buf && wasm_binary_freeable; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool has_datacount_section = false; +#endif + + /* Find code and function sections if have */ + while (section) { + if (section->section_type == SECTION_TYPE_CODE) { + buf_code = section->section_body; + buf_code_end = buf_code + section->section_body_size; + } + else if (section->section_type == SECTION_TYPE_FUNC) { + buf_func = section->section_body; + buf_func_end = buf_func + section->section_body_size; + } + section = section->next; + } + + section = sections; + while (section) { + buf = section->section_body; + buf_end = buf + section->section_body_size; + LOG_DEBUG("load section, type: %d", section->section_type); + switch (section->section_type) { + case SECTION_TYPE_USER: + /* unsupported user section, ignore it. */ + if (!load_user_section(buf, buf_end, module, + reuse_const_strings, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_TYPE: + if (!load_type_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_IMPORT: + if (!load_import_section(buf, buf_end, module, + reuse_const_strings, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_FUNC: + if (!load_function_section(buf, buf_end, buf_code, buf_code_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_TABLE: + if (!load_table_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_MEMORY: + if (!load_memory_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_GLOBAL: + if (!load_global_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_EXPORT: + if (!load_export_section(buf, buf_end, module, + reuse_const_strings, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_START: + if (!load_start_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_ELEM: + if (!load_table_segment_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + break; + case SECTION_TYPE_CODE: + if (!load_code_section(buf, buf_end, buf_func, buf_func_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_DATA: + if (!load_data_segment_section(buf, buf_end, module, +#if WASM_ENABLE_BULK_MEMORY != 0 + has_datacount_section, +#endif + clone_data_seg, error_buf, + error_buf_size)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case SECTION_TYPE_DATACOUNT: + if (!load_datacount_section(buf, buf_end, module, error_buf, + error_buf_size)) + return false; + has_datacount_section = true; + break; +#endif + default: + set_error_buf(error_buf, error_buf_size, "invalid section id"); + return false; + } + + section = section->next; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_assert(!has_datacount_section + || module->data_seg_count == module->data_seg_count1); +#endif + + module->aux_data_end_global_index = (uint32)-1; + module->aux_heap_base_global_index = (uint32)-1; + module->aux_stack_top_global_index = (uint32)-1; + + /* Resolve auxiliary data/stack/heap info and reset memory info */ + export = module->exports; + for (i = 0; i < module->export_count; i++, export ++) { + if (export->kind == EXPORT_KIND_GLOBAL) { + if (!strcmp(export->name, "__heap_base")) { + if (export->index < module->import_global_count) { + LOG_DEBUG("Skip the process if __heap_base is imported " + "instead of being a local global"); + continue; + } + + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type.val_type == VALUE_TYPE_I32 + && !global->type.is_mutable + && global->init_expr.init_expr_type + == INIT_EXPR_TYPE_I32_CONST) { + aux_heap_base_global = global; + aux_heap_base = + (uint64)(uint32)global->init_expr.u.unary.v.i32; + aux_heap_base_global_index = export->index; + LOG_VERBOSE("Found aux __heap_base global, value: %" PRIu64, + aux_heap_base); + } + } + else if (!strcmp(export->name, "__data_end")) { + if (export->index < module->import_global_count) { + LOG_DEBUG("Skip the process if __data_end is imported " + "instead of being a local global"); + continue; + } + + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type.val_type == VALUE_TYPE_I32 + && !global->type.is_mutable + && global->init_expr.init_expr_type + == INIT_EXPR_TYPE_I32_CONST) { + aux_data_end_global = global; + aux_data_end = + (uint64)(uint32)global->init_expr.u.unary.v.i32; + aux_data_end_global_index = export->index; + LOG_VERBOSE("Found aux __data_end global, value: %" PRIu64, + aux_data_end); + aux_data_end = align_uint64(aux_data_end, 16); + } + } + + /* For module compiled with -pthread option, the global is: + [0] stack_top <-- 0 + [1] tls_pointer + [2] tls_size + [3] data_end <-- 3 + [4] global_base + [5] heap_base <-- 5 + [6] dso_handle + + For module compiled without -pthread option: + [0] stack_top <-- 0 + [1] data_end <-- 1 + [2] global_base + [3] heap_base <-- 3 + [4] dso_handle + */ + if (aux_data_end_global && aux_heap_base_global + && aux_data_end <= aux_heap_base) { + module->aux_data_end_global_index = aux_data_end_global_index; + module->aux_data_end = aux_data_end; + module->aux_heap_base_global_index = aux_heap_base_global_index; + module->aux_heap_base = aux_heap_base; + + /* Resolve aux stack top global */ + for (global_index = 0; global_index < module->global_count; + global_index++) { + global = module->globals + global_index; + if (global->type.is_mutable /* heap_base and data_end is + not mutable */ + && global->type.val_type == VALUE_TYPE_I32 + && global->init_expr.init_expr_type + == INIT_EXPR_TYPE_I32_CONST + && (uint64)(uint32)global->init_expr.u.unary.v.i32 + <= aux_heap_base) { + aux_stack_top_global = global; + aux_stack_top = + (uint64)(uint32)global->init_expr.u.unary.v.i32; + module->aux_stack_top_global_index = + module->import_global_count + global_index; + module->aux_stack_bottom = aux_stack_top; + module->aux_stack_size = + aux_stack_top > aux_data_end + ? (uint32)(aux_stack_top - aux_data_end) + : (uint32)aux_stack_top; + LOG_VERBOSE( + "Found aux stack top global, value: %" PRIu64 ", " + "global index: %d, stack size: %d", + aux_stack_top, global_index, + module->aux_stack_size); + break; + } + } + if (!aux_stack_top_global) { + /* Auxiliary stack global isn't found, it must be unused + in the wasm app, as if it is used, the global must be + defined. Here we set it to __heap_base global and set + its size to 0. */ + aux_stack_top_global = aux_heap_base_global; + aux_stack_top = aux_heap_base; + module->aux_stack_top_global_index = + module->aux_heap_base_global_index; + module->aux_stack_bottom = aux_stack_top; + module->aux_stack_size = 0; + } + break; + } + } + } + + module->malloc_function = (uint32)-1; + module->free_function = (uint32)-1; + module->retain_function = (uint32)-1; + + /* Resolve malloc/free function exported by wasm module */ +#if WASM_ENABLE_MEMORY64 != 0 + if (has_module_memory64(module)) + malloc_free_io_type = VALUE_TYPE_I64; +#endif + export = module->exports; + for (i = 0; i < module->export_count; i++, export ++) { + if (export->kind == EXPORT_KIND_FUNC) { + if (!strcmp(export->name, "malloc") + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { + bh_assert(module->malloc_function == (uint32)-1); + module->malloc_function = export->index; + LOG_VERBOSE("Found malloc function, name: %s, index: %u", + export->name, export->index); + } + } + else if (!strcmp(export->name, "__new") + && export->index >= module->import_function_count) { + /* __new && __pin for AssemblyScript */ + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 2 && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == VALUE_TYPE_I32 + && func_type->types[2] == malloc_free_io_type) { + uint32 j; + WASMExport *export_tmp; + + bh_assert(module->malloc_function == (uint32)-1); + module->malloc_function = export->index; + LOG_VERBOSE("Found malloc function, name: %s, index: %u", + export->name, export->index); + + /* resolve retain function. + If not found, reset malloc function index */ + export_tmp = module->exports; + for (j = 0; j < module->export_count; j++, export_tmp++) { + if ((export_tmp->kind == EXPORT_KIND_FUNC) + && (!strcmp(export_tmp->name, "__retain") + || !strcmp(export_tmp->name, "__pin")) + && (export_tmp->index + >= module->import_function_count)) { + func_index = export_tmp->index + - module->import_function_count; + func_type = + module->functions[func_index]->func_type; + if (func_type->param_count == 1 + && func_type->result_count == 1 + && func_type->types[0] == malloc_free_io_type + && func_type->types[1] == malloc_free_io_type) { + bh_assert(module->retain_function + == (uint32)-1); + module->retain_function = export_tmp->index; + LOG_VERBOSE("Found retain function, name: %s, " + "index: %u", + export_tmp->name, + export_tmp->index); + break; + } + } + } + if (j == module->export_count) { + module->malloc_function = (uint32)-1; + LOG_VERBOSE("Can't find retain function," + "reset malloc function index to -1"); + } + } + } + else if (((!strcmp(export->name, "free")) + || (!strcmp(export->name, "__release")) + || (!strcmp(export->name, "__unpin"))) + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 && func_type->result_count == 0 + && func_type->types[0] == malloc_free_io_type) { + bh_assert(module->free_function == (uint32)-1); + module->free_function = export->index; + LOG_VERBOSE("Found free function, name: %s, index: %u", + export->name, export->index); + } + } + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_LABELS_AS_VALUES != 0 + handle_table = wasm_interp_get_handle_table(); +#endif + + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + if (!wasm_loader_prepare_bytecode(module, func, i, error_buf, + error_buf_size)) { + return false; + } + + if (i == module->function_count - 1) { + bh_assert(func->code + func->code_size == buf_code_end); + } + } + + if (!module->possible_memory_grow) { +#if WASM_ENABLE_SHRUNK_MEMORY != 0 + if (aux_data_end_global && aux_heap_base_global + && aux_stack_top_global) { + uint64 init_memory_size; + uint64 shrunk_memory_size = align_uint64(aux_heap_base, 8); + + /* Only resize(shrunk) the memory size if num_bytes_per_page is in + * valid range of uint32 */ + if (shrunk_memory_size <= UINT32_MAX) { + if (module->import_memory_count) { + WASMMemoryImport *memory_import = + &module->import_memories[0].u.memory; + init_memory_size = + (uint64)memory_import->mem_type.num_bytes_per_page + * memory_import->mem_type.init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory_import->mem_type.num_bytes_per_page = + shrunk_memory_size; + memory_import->mem_type.init_page_count = 1; + LOG_VERBOSE("Shrink import memory size to %" PRIu64, + shrunk_memory_size); + } + } + + if (module->memory_count) { + WASMMemory *memory = &module->memories[0]; + init_memory_size = (uint64)memory->num_bytes_per_page + * memory->init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory->num_bytes_per_page = shrunk_memory_size; + memory->init_page_count = 1; + LOG_VERBOSE("Shrink memory size to %" PRIu64, + shrunk_memory_size); + } + } + } + } +#endif /* WASM_ENABLE_SHRUNK_MEMORY != 0 */ + + if (module->import_memory_count) { + WASMMemoryImport *memory_import = + &module->import_memories[0].u.memory; + if (memory_import->mem_type.init_page_count < DEFAULT_MAX_PAGES) { + memory_import->mem_type.num_bytes_per_page *= + memory_import->mem_type.init_page_count; + if (memory_import->mem_type.init_page_count > 0) + memory_import->mem_type.init_page_count = + memory_import->mem_type.max_page_count = 1; + else + memory_import->mem_type.init_page_count = + memory_import->mem_type.max_page_count = 0; + } + } + + if (module->memory_count) { + WASMMemory *memory = &module->memories[0]; + if (memory->init_page_count < DEFAULT_MAX_PAGES) { + memory->num_bytes_per_page *= memory->init_page_count; + if (memory->init_page_count > 0) + memory->init_page_count = memory->max_page_count = 1; + else + memory->init_page_count = memory->max_page_count = 0; + } + } + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (!check_memory64_flags_consistency(module, error_buf, error_buf_size, + false)) + return false; +#endif + + calculate_global_data_offset(module); + +#if WASM_ENABLE_FAST_JIT != 0 + if (!init_fast_jit_functions(module, error_buf, error_buf_size)) { + return false; + } +#endif + +#if WASM_ENABLE_JIT != 0 + if (!init_llvm_jit_functions_stage1(module, error_buf, error_buf_size)) { + return false; + } +#if !(WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0) + if (!init_llvm_jit_functions_stage2(module, error_buf, error_buf_size)) { + return false; + } +#else + /* Run aot_compile_wasm in a backend thread, so as not to block the main + thread fast jit execution, since applying llvm optimizations in + aot_compile_wasm may cost a lot of time. + Create thread with enough native stack to apply llvm optimizations */ + if (os_thread_create(&module->llvm_jit_init_thread, + init_llvm_jit_functions_stage2_callback, + (void *)module, APP_THREAD_STACK_SIZE_DEFAULT * 8) + != 0) { + set_error_buf(error_buf, error_buf_size, + "create orcjit compile thread failed"); + return false; + } +#endif +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Create threads to compile the jit functions */ + if (!compile_jit_functions(module, error_buf, error_buf_size)) { + return false; + } +#endif + +#if WASM_ENABLE_MEMORY_TRACING != 0 + wasm_runtime_dump_module_mem_consumption(module); +#endif + return true; +} + +static WASMModule * +create_module(char *name, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = + loader_malloc(sizeof(WASMModule), error_buf, error_buf_size); + bh_list_status ret; + + if (!module) { + return NULL; + } + + module->module_type = Wasm_Module_Bytecode; + + /* Set start_function to -1, means no start function */ + module->start_function = (uint32)-1; + + module->name = name; + module->is_binary_freeable = false; + +#if WASM_ENABLE_FAST_INTERP == 0 + module->br_table_cache_list = &module->br_table_cache_list_head; + ret = bh_list_init(module->br_table_cache_list); + bh_assert(ret == BH_LIST_SUCCESS); +#endif + +#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"); + wasm_runtime_free(module); + return NULL; + } +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + wasi_args_set_defaults(&module->wasi_args); +#endif /* WASM_ENABLE_LIBC_WASI != 0 */ + + (void)ret; + return module; +} + +WASMModule * +wasm_loader_load_from_sections(WASMSection *section_list, char *error_buf, + uint32 error_buf_size) +{ + WASMModule *module = create_module("", error_buf, error_buf_size); + if (!module) + return NULL; + + if (!load_from_sections(module, section_list, false, true, error_buf, + error_buf_size)) { + wasm_loader_unload(module); + return NULL; + } + + LOG_VERBOSE("Load module from sections success.\n"); + return module; +} + +static void +destroy_sections(WASMSection *section_list) +{ + WASMSection *section = section_list, *next; + while (section) { + next = section->next; + wasm_runtime_free(section); + section = next; + } +} + +/* clang-format off */ +static uint8 section_ids[] = { + SECTION_TYPE_USER, + SECTION_TYPE_TYPE, + SECTION_TYPE_IMPORT, + SECTION_TYPE_FUNC, + SECTION_TYPE_TABLE, + SECTION_TYPE_MEMORY, + SECTION_TYPE_GLOBAL, + SECTION_TYPE_EXPORT, + SECTION_TYPE_START, + SECTION_TYPE_ELEM, +#if WASM_ENABLE_BULK_MEMORY != 0 + SECTION_TYPE_DATACOUNT, +#endif + SECTION_TYPE_CODE, + SECTION_TYPE_DATA +}; +/* clang-format on */ + +static uint8 +get_section_index(uint8 section_type) +{ + uint8 max_id = sizeof(section_ids) / sizeof(uint8); + + for (uint8 i = 0; i < max_id; i++) { + if (section_type == section_ids[i]) + return i; + } + + return (uint8)-1; +} + +static bool +create_sections(const uint8 *buf, uint32 size, WASMSection **p_section_list, + char *error_buf, uint32 error_buf_size) +{ + WASMSection *section_list_end = NULL, *section; + const uint8 *p = buf, *p_end = buf + size /*, *section_body*/; + uint8 section_type, section_index, last_section_index = (uint8)-1; + uint32 section_size; + + bh_assert(!*p_section_list); + + p += 8; + while (p < p_end) { + CHECK_BUF(p, p_end, 1); + section_type = read_uint8(p); + section_index = get_section_index(section_type); + if (section_index != (uint8)-1) { + if (section_type != SECTION_TYPE_USER) { + /* Custom sections may be inserted at any place, + while other sections must occur at most once + and in prescribed order. */ + bh_assert(last_section_index == (uint8)-1 + || last_section_index < section_index); + last_section_index = section_index; + } + read_leb_uint32(p, p_end, section_size); + CHECK_BUF1(p, p_end, section_size); + + if (!(section = loader_malloc(sizeof(WASMSection), error_buf, + error_buf_size))) { + return false; + } + + section->section_type = section_type; + section->section_body = (uint8 *)p; + section->section_body_size = section_size; + + if (!*p_section_list) + *p_section_list = section_list_end = section; + else { + section_list_end->next = section; + section_list_end = section; + } + + p += section_size; + } + else { + bh_assert(0); + } + } + + (void)last_section_index; + return true; +} + +static void +exchange32(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +static bool +load(const uint8 *buf, uint32 size, WASMModule *module, + bool wasm_binary_freeable, char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf_end = buf + size; + const uint8 *p = buf, *p_end = buf_end; + uint32 magic_number, version; + WASMSection *section_list = NULL; + + CHECK_BUF1(p, p_end, sizeof(uint32)); + magic_number = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8 *)&magic_number); + + bh_assert(magic_number == WASM_MAGIC_NUMBER); + + CHECK_BUF1(p, p_end, sizeof(uint32)); + version = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8 *)&version); + + if (version != WASM_CURRENT_VERSION) { + set_error_buf(error_buf, error_buf_size, "unknown binary version"); + return false; + } + + if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) + || !load_from_sections(module, section_list, true, wasm_binary_freeable, + error_buf, error_buf_size)) { + destroy_sections(section_list); + return false; + } + + destroy_sections(section_list); + (void)p_end; + return true; +} + +WASMModule * +wasm_loader_load(uint8 *buf, uint32 size, +#if WASM_ENABLE_MULTI_MODULE != 0 + bool main_module, +#endif + const LoadArgs *args, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = create_module(args->name, error_buf, error_buf_size); + if (!module) { + return NULL; + } + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0 \ + || WASM_ENABLE_JIT != 0 + module->load_addr = (uint8 *)buf; + module->load_size = size; +#endif + + if (!load(buf, size, module, args->wasm_binary_freeable, error_buf, + error_buf_size)) { + goto fail; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + (void)main_module; +#endif + + LOG_VERBOSE("Load module success.\n"); + return module; + +fail: + wasm_loader_unload(module); + return NULL; +} + +void +wasm_loader_unload(WASMModule *module) +{ + uint32 i; + + if (!module) + return; + +#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); +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + /* Stop Fast/LLVM JIT compilation firstly to avoid accessing + module internal data after they were freed */ + orcjit_stop_compile_threads(module); +#endif + +#if WASM_ENABLE_JIT != 0 + if (module->func_ptrs) + wasm_runtime_free(module->func_ptrs); + if (module->comp_ctx) + aot_destroy_comp_context(module->comp_ctx); + if (module->comp_data) + aot_destroy_comp_data(module->comp_data); +#endif + +#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); + } +#endif + + if (module->types) { + for (i = 0; i < module->type_count; i++) { + if (module->types[i]) + destroy_wasm_type(module->types[i]); + } + wasm_runtime_free(module->types); + } + + if (module->imports) + wasm_runtime_free(module->imports); + + if (module->functions) { + for (i = 0; i < module->function_count; i++) { + if (module->functions[i]) { + if (module->functions[i]->local_offsets) + wasm_runtime_free(module->functions[i]->local_offsets); +#if WASM_ENABLE_FAST_INTERP != 0 + if (module->functions[i]->code_compiled) + wasm_runtime_free(module->functions[i]->code_compiled); + if (module->functions[i]->consts) + wasm_runtime_free(module->functions[i]->consts); +#endif +#if WASM_ENABLE_FAST_JIT != 0 + if (module->functions[i]->fast_jit_jitted_code) { + jit_code_cache_free( + module->functions[i]->fast_jit_jitted_code); + } +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_LAZY_JIT != 0 + if (module->functions[i]->call_to_fast_jit_from_llvm_jit) { + jit_code_cache_free( + module->functions[i]->call_to_fast_jit_from_llvm_jit); + } +#endif +#endif + wasm_runtime_free(module->functions[i]); + } + } + wasm_runtime_free(module->functions); + } + + if (module->tables) + wasm_runtime_free(module->tables); + + if (module->memories) + wasm_runtime_free(module->memories); + + if (module->globals) { +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + for (i = 0; i < module->global_count; i++) { + destroy_init_expr(&module->globals[i].init_expr); + } +#endif + wasm_runtime_free(module->globals); + } + + if (module->exports) + wasm_runtime_free(module->exports); + + if (module->table_segments) { + for (i = 0; i < module->table_seg_count; i++) { + if (module->table_segments[i].init_values) + wasm_runtime_free(module->table_segments[i].init_values); +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(&module->table_segments[i].base_offset); +#endif + } + wasm_runtime_free(module->table_segments); + } + + if (module->data_segments) { + for (i = 0; i < module->data_seg_count; i++) { + if (module->data_segments[i]) { + if (module->data_segments[i]->is_data_cloned) + wasm_runtime_free(module->data_segments[i]->data); +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + destroy_init_expr(&module->data_segments[i]->base_offset); +#endif + wasm_runtime_free(module->data_segments[i]); + } + } + wasm_runtime_free(module->data_segments); + } + + if (module->const_str_list) { + StringNode *node = module->const_str_list, *node_next; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + } + +#if WASM_ENABLE_FAST_INTERP == 0 + if (module->br_table_cache_list) { + BrTableCache *node = bh_list_first_elem(module->br_table_cache_list); + BrTableCache *node_next; + while (node) { + node_next = bh_list_elem_next(node); + wasm_runtime_free(node); + node = node_next; + } + } +#endif + +#if WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0 + os_mutex_destroy(&module->instance_list_lock); +#endif + +#if WASM_ENABLE_FAST_JIT != 0 + if (module->fast_jit_func_ptrs) { + wasm_runtime_free(module->fast_jit_func_ptrs); + } + + for (i = 0; i < WASM_ORC_JIT_BACKEND_THREAD_NUM; i++) { + if (module->fast_jit_thread_locks_inited[i]) { + os_mutex_destroy(&module->fast_jit_thread_locks[i]); + } + } +#endif + + wasm_runtime_free(module); +} + +bool +wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache, + const uint8 *start_addr, const uint8 *code_end_addr, + uint8 label_type, uint8 **p_else_addr, + uint8 **p_end_addr) +{ + const uint8 *p = start_addr, *p_end = code_end_addr; + uint8 *else_addr = NULL; + char error_buf[128]; + uint32 block_nested_depth = 1, count, i, j, t; + uint32 error_buf_size = sizeof(error_buf); + uint8 opcode, u8; + BlockAddr block_stack[16] = { 0 }, *block; + + i = ((uintptr_t)start_addr) & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) { + if (block[j].start_addr == start_addr) { + /* Cache hit */ + *p_else_addr = block[j].else_addr; + *p_end_addr = block[j].end_addr; + return true; + } + } + + /* Cache unhit */ + block_stack[0].start_addr = start_addr; + + while (p < code_end_addr) { + opcode = *p++; + + switch (opcode) { + case WASM_OP_UNREACHABLE: + case WASM_OP_NOP: + break; + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + /* block result type: 0x40/0x7F/0x7E/0x7D/0x7C */ + u8 = read_uint8(p); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + + case EXT_OP_BLOCK: + case EXT_OP_LOOP: + case EXT_OP_IF: + /* block type */ + skip_leb_int32(p, p_end); + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + + case WASM_OP_ELSE: + if (label_type == LABEL_TYPE_IF && block_nested_depth == 1) + else_addr = (uint8 *)(p - 1); + if (block_nested_depth - 1 + < sizeof(block_stack) / sizeof(BlockAddr)) + block_stack[block_nested_depth - 1].else_addr = + (uint8 *)(p - 1); + break; + + case WASM_OP_END: + if (block_nested_depth == 1) { + if (label_type == LABEL_TYPE_IF) + *p_else_addr = else_addr; + *p_end_addr = (uint8 *)(p - 1); + + block_stack[0].end_addr = (uint8 *)(p - 1); + for (t = 0; t < sizeof(block_stack) / sizeof(BlockAddr); + t++) { + start_addr = block_stack[t].start_addr; + if (start_addr) { + i = ((uintptr_t)start_addr) + & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + block = + block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) + if (!block[j].start_addr) + break; + + if (j == BLOCK_ADDR_CONFLICT_SIZE) { + memmove(block + 1, block, + (BLOCK_ADDR_CONFLICT_SIZE - 1) + * sizeof(BlockAddr)); + j = 0; + } + block[j].start_addr = block_stack[t].start_addr; + block[j].else_addr = block_stack[t].else_addr; + block[j].end_addr = block_stack[t].end_addr; + } + else + break; + } + return true; + } + else { + block_nested_depth--; + if (block_nested_depth + < sizeof(block_stack) / sizeof(BlockAddr)) + block_stack[block_nested_depth].end_addr = + (uint8 *)(p - 1); + } + break; + + case WASM_OP_BR: + case WASM_OP_BR_IF: + skip_leb_uint32(p, p_end); /* labelidx */ + break; + + case WASM_OP_BR_TABLE: + read_leb_uint32(p, p_end, count); /* lable num */ +#if WASM_ENABLE_FAST_INTERP != 0 + for (i = 0; i <= count; i++) /* lableidxs */ + skip_leb_uint32(p, p_end); +#else + p += count + 1; + while (*p == WASM_OP_NOP) + p++; +#endif + break; + +#if WASM_ENABLE_FAST_INTERP == 0 + case EXT_OP_BR_TABLE_CACHE: + read_leb_uint32(p, p_end, count); /* lable num */ + while (*p == WASM_OP_NOP) + p++; + break; +#endif + + case WASM_OP_RETURN: + break; + + case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif + skip_leb_uint32(p, p_end); /* funcidx */ + break; + + case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif + skip_leb_uint32(p, p_end); /* typeidx */ +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 + skip_leb_uint32(p, p_end); /* tableidx */ +#else + u8 = read_uint8(p); /* 0x00 */ +#endif + break; + +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + case WASM_OP_CATCH: + case WASM_OP_THROW: + case WASM_OP_RETHROW: + case WASM_OP_DELEGATE: + case WASM_OP_CATCH_ALL: + /* TODO */ + return false; +#endif + + case WASM_OP_DROP: + case WASM_OP_SELECT: + case WASM_OP_DROP_64: + case WASM_OP_SELECT_64: + break; +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + skip_leb_uint32(p, p_end); /* vec length */ + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* typeidx */ + break; + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + skip_leb_uint32(p, p_end); /* table index */ + break; + case WASM_OP_REF_NULL: + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* type */ + break; + case WASM_OP_REF_IS_NULL: + break; + case WASM_OP_REF_FUNC: + skip_leb_uint32(p, p_end); /* func index */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ + case WASM_OP_GET_LOCAL: + case WASM_OP_SET_LOCAL: + case WASM_OP_TEE_LOCAL: + case WASM_OP_GET_GLOBAL: + case WASM_OP_SET_GLOBAL: + case WASM_OP_GET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_AUX_STACK: + skip_leb_uint32(p, p_end); /* localidx */ + break; + + case EXT_OP_GET_LOCAL_FAST: + case EXT_OP_SET_LOCAL_FAST: + case EXT_OP_TEE_LOCAL_FAST: + CHECK_BUF(p, p_end, 1); + p++; + break; + + case WASM_OP_I32_LOAD: + case WASM_OP_I64_LOAD: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_I32_STORE: + case WASM_OP_I64_STORE: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + skip_leb_align(p, p_end); /* align */ + skip_leb_mem_offset(p, p_end); /* offset */ + break; + + case WASM_OP_MEMORY_SIZE: + case WASM_OP_MEMORY_GROW: + skip_leb_memidx(p, p_end); /* memidx */ + break; + + case WASM_OP_I32_CONST: + skip_leb_int32(p, p_end); + break; + case WASM_OP_I64_CONST: + skip_leb_int64(p, p_end); + break; + case WASM_OP_F32_CONST: + p += sizeof(float32); + break; + case WASM_OP_F64_CONST: + p += sizeof(float64); + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + case WASM_OP_I32_WRAP_I64: + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + case WASM_OP_F32_DEMOTE_F64: + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + case WASM_OP_F64_PROMOTE_F32: + case WASM_OP_I32_REINTERPRET_F32: + case WASM_OP_I64_REINTERPRET_F64: + case WASM_OP_F32_REINTERPRET_I32: + case WASM_OP_F64_REINTERPRET_I64: + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + break; + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); + /* opcode1 was checked in wasm_loader_prepare_bytecode and + is no larger than UINT8_MAX */ + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + skip_leb_uint32(p, p_end); + skip_leb_memidx(p, p_end); + break; + case WASM_OP_DATA_DROP: + skip_leb_uint32(p, p_end); + break; +#endif +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + skip_leb_memidx(p, p_end); + skip_leb_memidx(p, p_end); + break; + case WASM_OP_MEMORY_FILL: + skip_leb_memidx(p, p_end); + break; +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + case WASM_OP_TABLE_COPY: + /* tableidx */ + skip_leb_uint32(p, p_end); + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_ELEM_DROP: + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_TABLE_SIZE: + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + skip_leb_uint32(p, p_end); /* table idx */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ + default: + bh_assert(0); + break; + } + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + /* TODO: memory64 offset type changes */ + uint32 opcode1; + + /* atomic_op (u32_leb) + memarg (2 u32_leb) */ + read_leb_uint32(p, p_end, opcode1); + /* opcode1 was checked in wasm_loader_prepare_bytecode and + is no larger than UINT8_MAX */ + opcode = (uint8)opcode1; + + if (opcode != WASM_OP_ATOMIC_FENCE) { + skip_leb_uint32(p, p_end); /* align */ + skip_leb_mem_offset(p, p_end); /* offset */ + } + else { + /* atomic.fence doesn't have memarg */ + p++; + } + break; + } +#endif + + default: + bh_assert(0); + break; + } + } + + (void)u8; + return false; +} + +#define REF_I32 VALUE_TYPE_I32 +#define REF_F32 VALUE_TYPE_F32 +#define REF_I64_1 VALUE_TYPE_I64 +#define REF_I64_2 VALUE_TYPE_I64 +#define REF_F64_1 VALUE_TYPE_F64 +#define REF_F64_2 VALUE_TYPE_F64 +#define REF_ANY VALUE_TYPE_ANY + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_DEBUG_PREPROCESSOR != 0 +#define LOG_OP(...) os_printf(__VA_ARGS__) +#else +#define LOG_OP(...) (void)0 +#endif + +#define PATCH_ELSE 0 +#define PATCH_END 1 +typedef struct BranchBlockPatch { + struct BranchBlockPatch *next; + uint8 patch_type; + uint8 *code_compiled; +} BranchBlockPatch; +#endif + +typedef struct BranchBlock { + uint8 label_type; + BlockType block_type; + uint8 *start_addr; + uint8 *else_addr; + uint8 *end_addr; + uint32 stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + uint16 dynamic_offset; + uint8 *code_compiled; + BranchBlockPatch *patch_list; + /* This is used to save params frame_offset of of if block */ + int16 *param_frame_offsets; + /* This is used to store available param num for if/else branch, so the else + * opcode can know how many parameters should be copied to the stack */ + uint32 available_param_num; + /* This is used to recover the dynamic offset for else branch, + * and also to remember the start offset of dynamic space which + * stores the block arguments for loop block, so we can use it + * to copy the stack operands to the loop block's arguments in + * wasm_loader_emit_br_info for opcode br. */ + uint16 start_dynamic_offset; +#endif + + /* Indicate the operand stack is in polymorphic state. + * If the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed. + * If stack is in polymorphic state and stack is empty, instruction can + * pop any type of value directly without decreasing stack top pointer + * and stack cell num. */ + bool is_stack_polymorphic; +} BranchBlock; + +typedef struct WASMLoaderContext { + /* frame ref stack */ + uint8 *frame_ref; + uint8 *frame_ref_bottom; + uint8 *frame_ref_boundary; + uint32 frame_ref_size; + uint32 stack_cell_num; + uint32 max_stack_cell_num; + + /* frame csp stack */ + BranchBlock *frame_csp; + BranchBlock *frame_csp_bottom; + BranchBlock *frame_csp_boundary; + uint32 frame_csp_size; + uint32 csp_num; + uint32 max_csp_num; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* frame offset stack */ + int16 *frame_offset; + int16 *frame_offset_bottom; + int16 *frame_offset_boundary; + uint32 frame_offset_size; + int16 dynamic_offset; + int16 start_dynamic_offset; + int16 max_dynamic_offset; + + /* preserved local offset */ + int16 preserved_local_offset; + + /* const buffer for i64 and f64 consts, note that the raw bytes + * of i64 and f64 are the same, so we read an i64 value from an + * f64 const with its raw bytes, something like `*(int64 *)&f64 */ + int64 *i64_consts; + uint32 i64_const_max_num; + uint32 i64_const_num; + /* const buffer for i32 and f32 consts */ + int32 *i32_consts; + uint32 i32_const_max_num; + uint32 i32_const_num; + + /* processed code */ + uint8 *p_code_compiled; + uint8 *p_code_compiled_end; + uint32 code_compiled_size; + /* If the last opcode will be dropped, the peak memory usage will be larger + * than the final code_compiled_size, we record the peak size to ensure + * there will not be invalid memory access during second traverse */ + uint32 code_compiled_peak_size; +#endif +} WASMLoaderContext; + +#define CHECK_CSP_PUSH() \ + do { \ + if (ctx->frame_csp >= ctx->frame_csp_boundary) { \ + MEM_REALLOC( \ + ctx->frame_csp_bottom, ctx->frame_csp_size, \ + (uint32)(ctx->frame_csp_size + 8 * sizeof(BranchBlock))); \ + ctx->frame_csp_size += (uint32)(8 * sizeof(BranchBlock)); \ + ctx->frame_csp_boundary = \ + ctx->frame_csp_bottom \ + + ctx->frame_csp_size / sizeof(BranchBlock); \ + ctx->frame_csp = ctx->frame_csp_bottom + ctx->csp_num; \ + } \ + } while (0) + +#define CHECK_CSP_POP() \ + do { \ + bh_assert(ctx->csp_num >= 1); \ + } while (0) + +#if WASM_ENABLE_FAST_INTERP != 0 +static bool +check_offset_push(WASMLoaderContext *ctx, char *error_buf, + uint32 error_buf_size) +{ + uint32 cell_num = (uint32)(ctx->frame_offset - ctx->frame_offset_bottom); + if (ctx->frame_offset >= ctx->frame_offset_boundary) { + MEM_REALLOC(ctx->frame_offset_bottom, ctx->frame_offset_size, + ctx->frame_offset_size + 16); + ctx->frame_offset_size += 16; + ctx->frame_offset_boundary = + ctx->frame_offset_bottom + ctx->frame_offset_size / sizeof(int16); + ctx->frame_offset = ctx->frame_offset_bottom + cell_num; + } + return true; +fail: + return false; +} + +static bool +check_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->frame_offset - cells < ctx->frame_offset_bottom) + return false; + return true; +} + +static bool +check_dynamic_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->dynamic_offset < 0 || (uint32)ctx->dynamic_offset < cells) + return false; + return true; +} + +static void +free_label_patch_list(BranchBlock *frame_csp) +{ + BranchBlockPatch *label_patch = frame_csp->patch_list; + BranchBlockPatch *next; + while (label_patch != NULL) { + next = label_patch->next; + wasm_runtime_free(label_patch); + label_patch = next; + } + frame_csp->patch_list = NULL; +} + +static void +free_all_label_patch_lists(BranchBlock *frame_csp, uint32 csp_num) +{ + BranchBlock *tmp_csp = frame_csp; + uint32 i; + + for (i = 0; i < csp_num; i++) { + free_label_patch_list(tmp_csp); + tmp_csp++; + } +} + +static void +free_all_label_param_frame_offsets(BranchBlock *frame_csp, uint32 csp_num) +{ + BranchBlock *tmp_csp = frame_csp; + uint32 i; + + for (i = 0; i < csp_num; i++) { + if (tmp_csp->param_frame_offsets) + wasm_runtime_free(tmp_csp->param_frame_offsets); + tmp_csp++; + } +} +#endif + +static bool +check_stack_push(WASMLoaderContext *ctx, char *error_buf, uint32 error_buf_size) +{ + if (ctx->frame_ref >= ctx->frame_ref_boundary) { + MEM_REALLOC(ctx->frame_ref_bottom, ctx->frame_ref_size, + ctx->frame_ref_size + 16); + ctx->frame_ref_size += 16; + ctx->frame_ref_boundary = ctx->frame_ref_bottom + ctx->frame_ref_size; + ctx->frame_ref = ctx->frame_ref_bottom + ctx->stack_cell_num; + } + return true; +fail: + return false; +} + +static bool +check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + bh_assert(!((is_32bit_type(type) && stack_cell_num < 1) + || (is_64bit_type(type) && stack_cell_num < 2))); + + bh_assert(!( + (type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) + || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) + || (type == VALUE_TYPE_I64 + && (*(frame_ref - 2) != REF_I64_1 || *(frame_ref - 1) != REF_I64_2)) + || (type == VALUE_TYPE_F64 + && (*(frame_ref - 2) != REF_F64_1 + || *(frame_ref - 1) != REF_F64_2)))); + return true; +} + +static bool +check_stack_pop(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + int32 block_stack_cell_num = + (int32)(ctx->stack_cell_num - (ctx->frame_csp - 1)->stack_cell_num); + + if (block_stack_cell_num > 0 && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + /* the stack top is a value of any type, return success */ + return true; + } + + if (!check_stack_top_values(ctx->frame_ref, block_stack_cell_num, type, + error_buf, error_buf_size)) + return false; + + return true; +} + +static void +wasm_loader_ctx_destroy(WASMLoaderContext *ctx) +{ + if (ctx) { + if (ctx->frame_ref_bottom) + wasm_runtime_free(ctx->frame_ref_bottom); + if (ctx->frame_csp_bottom) { +#if WASM_ENABLE_FAST_INTERP != 0 + free_all_label_patch_lists(ctx->frame_csp_bottom, ctx->csp_num); + free_all_label_param_frame_offsets(ctx->frame_csp_bottom, + ctx->csp_num); +#endif + wasm_runtime_free(ctx->frame_csp_bottom); + } +#if WASM_ENABLE_FAST_INTERP != 0 + if (ctx->frame_offset_bottom) + wasm_runtime_free(ctx->frame_offset_bottom); + if (ctx->i64_consts) + wasm_runtime_free(ctx->i64_consts); + if (ctx->i32_consts) + wasm_runtime_free(ctx->i32_consts); +#endif + wasm_runtime_free(ctx); + } +} + +static WASMLoaderContext * +wasm_loader_ctx_init(WASMFunction *func, char *error_buf, uint32 error_buf_size) +{ + WASMLoaderContext *loader_ctx = + loader_malloc(sizeof(WASMLoaderContext), error_buf, error_buf_size); + if (!loader_ctx) + return NULL; + + loader_ctx->frame_ref_size = 32; + if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = loader_malloc( + loader_ctx->frame_ref_size, error_buf, error_buf_size))) + goto fail; + loader_ctx->frame_ref_boundary = loader_ctx->frame_ref_bottom + 32; + + loader_ctx->frame_csp_size = sizeof(BranchBlock) * 8; + if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = loader_malloc( + loader_ctx->frame_csp_size, error_buf, error_buf_size))) + goto fail; + loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; + +#if WASM_ENABLE_FAST_INTERP != 0 + loader_ctx->frame_offset_size = sizeof(int16) * 32; + if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = + loader_malloc(loader_ctx->frame_offset_size, error_buf, + error_buf_size))) + goto fail; + loader_ctx->frame_offset_boundary = loader_ctx->frame_offset_bottom + 32; + + loader_ctx->i64_const_max_num = 8; + if (!(loader_ctx->i64_consts = + loader_malloc(sizeof(int64) * loader_ctx->i64_const_max_num, + error_buf, error_buf_size))) + goto fail; + loader_ctx->i32_const_max_num = 8; + if (!(loader_ctx->i32_consts = + loader_malloc(sizeof(int32) * loader_ctx->i32_const_max_num, + error_buf, error_buf_size))) + goto fail; + + if (func->param_cell_num >= (int32)INT16_MAX - func->local_cell_num) { + set_error_buf(error_buf, error_buf_size, + "fast interpreter offset overflow"); + goto fail; + } + + loader_ctx->start_dynamic_offset = loader_ctx->dynamic_offset = + loader_ctx->max_dynamic_offset = + func->param_cell_num + func->local_cell_num; +#endif + return loader_ctx; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + return NULL; +} + +static bool +wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_push(ctx, error_buf, error_buf_size)) + return false; + + *ctx->frame_ref++ = type; + ctx->stack_cell_num++; + if (ctx->stack_cell_num > ctx->max_stack_cell_num) + ctx->max_stack_cell_num = ctx->stack_cell_num; + + if (is_32bit_type(type)) + return true; + + if (!check_stack_push(ctx, error_buf, error_buf_size)) + return false; + *ctx->frame_ref++ = type; + ctx->stack_cell_num++; + if (ctx->stack_cell_num > ctx->max_stack_cell_num) { + ctx->max_stack_cell_num = ctx->stack_cell_num; + bh_assert(ctx->max_stack_cell_num <= UINT16_MAX); + } + return true; +} + +static bool +wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, char *error_buf, + uint32 error_buf_size) +{ + BranchBlock *cur_block = ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_pop(ctx, type, error_buf, error_buf_size)) + return false; + + ctx->frame_ref--; + ctx->stack_cell_num--; + + if (is_32bit_type(type) || *ctx->frame_ref == VALUE_TYPE_ANY) + return true; + + ctx->frame_ref--; + ctx->stack_cell_num--; + return true; +} + +#if WASM_ENABLE_FAST_INTERP == 0 +static bool +wasm_loader_push_pop_frame_ref(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, char *error_buf, + uint32 error_buf_size) +{ + for (int i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_ref(ctx, type_pop, error_buf, + error_buf_size)) + return false; + } + if (!wasm_loader_push_frame_ref(ctx, type_push, error_buf, error_buf_size)) + return false; + return true; +} +#endif + +static bool +wasm_loader_push_frame_csp(WASMLoaderContext *ctx, uint8 label_type, + BlockType block_type, uint8 *start_addr, + char *error_buf, uint32 error_buf_size) +{ + CHECK_CSP_PUSH(); + memset(ctx->frame_csp, 0, sizeof(BranchBlock)); + ctx->frame_csp->label_type = label_type; + ctx->frame_csp->block_type = block_type; + ctx->frame_csp->start_addr = start_addr; + ctx->frame_csp->stack_cell_num = ctx->stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + ctx->frame_csp->dynamic_offset = ctx->dynamic_offset; + ctx->frame_csp->patch_list = NULL; +#endif + ctx->frame_csp++; + ctx->csp_num++; + if (ctx->csp_num > ctx->max_csp_num) { + ctx->max_csp_num = ctx->csp_num; + bh_assert(ctx->max_csp_num <= UINT16_MAX); + } + return true; +fail: + return false; +} + +static bool +wasm_loader_pop_frame_csp(WASMLoaderContext *ctx, char *error_buf, + uint32 error_buf_size) +{ + CHECK_CSP_POP(); +#if WASM_ENABLE_FAST_INTERP != 0 + if ((ctx->frame_csp - 1)->param_frame_offsets) + wasm_runtime_free((ctx->frame_csp - 1)->param_frame_offsets); +#endif + ctx->frame_csp--; + ctx->csp_num--; + return true; +} + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 +#define emit_label(opcode) \ + do { \ + wasm_loader_emit_ptr(loader_ctx, handle_table[opcode]); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#define skip_label() \ + do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(void *)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) +#else /* else of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#if UINTPTR_MAX == UINT64_MAX +#define emit_label(opcode) \ + do { \ + int32 offset = \ + (int32)((uint8 *)handle_table[opcode] - (uint8 *)handle_table[0]); \ + /* emit int32 relative offset in 64-bit target */ \ + wasm_loader_emit_uint32(loader_ctx, offset); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#else +#define emit_label(opcode) \ + do { \ + uint32 label_addr = (uint32)(uintptr_t)handle_table[opcode]; \ + /* emit uint32 label address in 32-bit target */ \ + wasm_loader_emit_uint32(loader_ctx, label_addr); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#endif +#define skip_label() \ + do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(int32)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#define emit_label(opcode) \ + do { \ + wasm_loader_emit_uint8(loader_ctx, opcode); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) +#define skip_label() \ + do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(uint8)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +#define emit_empty_label_addr_and_frame_ip(type) \ + do { \ + if (!add_label_patch_to_list(loader_ctx->frame_csp - 1, type, \ + loader_ctx->p_code_compiled, error_buf, \ + error_buf_size)) \ + goto fail; \ + /* label address, to be patched */ \ + wasm_loader_emit_ptr(loader_ctx, NULL); \ + } while (0) + +#define emit_br_info(frame_csp, is_br) \ + do { \ + if (!wasm_loader_emit_br_info(loader_ctx, frame_csp, is_br, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define LAST_OP_OUTPUT_I32() \ + (last_op >= WASM_OP_I32_EQZ && last_op <= WASM_OP_I32_ROTR) \ + || (last_op == WASM_OP_I32_LOAD || last_op == WASM_OP_F32_LOAD) \ + || (last_op >= WASM_OP_I32_LOAD8_S && last_op <= WASM_OP_I32_LOAD16_U) \ + || (last_op >= WASM_OP_F32_ABS && last_op <= WASM_OP_F32_COPYSIGN) \ + || (last_op >= WASM_OP_I32_WRAP_I64 \ + && last_op <= WASM_OP_I32_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F32_CONVERT_S_I32 \ + && last_op <= WASM_OP_F32_DEMOTE_F64) \ + || (last_op == WASM_OP_I32_REINTERPRET_F32) \ + || (last_op == WASM_OP_F32_REINTERPRET_I32) \ + || (last_op == EXT_OP_COPY_STACK_TOP) + +#define LAST_OP_OUTPUT_I64() \ + (last_op >= WASM_OP_I64_CLZ && last_op <= WASM_OP_I64_ROTR) \ + || (last_op >= WASM_OP_F64_ABS && last_op <= WASM_OP_F64_COPYSIGN) \ + || (last_op == WASM_OP_I64_LOAD || last_op == WASM_OP_F64_LOAD) \ + || (last_op >= WASM_OP_I64_LOAD8_S && last_op <= WASM_OP_I64_LOAD32_U) \ + || (last_op >= WASM_OP_I64_EXTEND_S_I32 \ + && last_op <= WASM_OP_I64_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F64_CONVERT_S_I32 \ + && last_op <= WASM_OP_F64_PROMOTE_F32) \ + || (last_op == WASM_OP_I64_REINTERPRET_F64) \ + || (last_op == WASM_OP_F64_REINTERPRET_I64) \ + || (last_op == EXT_OP_COPY_STACK_TOP_I64) + +#define GET_CONST_OFFSET(type, val) \ + do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, &val, \ + &operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F32_OFFSET(type, fval) \ + do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, &fval, \ + &operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F64_OFFSET(type, fval) \ + do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, &fval, \ + &operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define emit_operand(ctx, offset) \ + do { \ + wasm_loader_emit_int16(ctx, offset); \ + LOG_OP("%d\t", offset); \ + } while (0) + +#define emit_byte(ctx, byte) \ + do { \ + wasm_loader_emit_uint8(ctx, byte); \ + LOG_OP("%d\t", byte); \ + } while (0) + +#define emit_uint32(ctx, value) \ + do { \ + wasm_loader_emit_uint32(ctx, value); \ + LOG_OP("%d\t", value); \ + } while (0) + +#define emit_uint64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%lld\t", value); \ + } while (0) + +#define emit_float32(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, true); \ + LOG_OP("%f\t", value); \ + } while (0) + +#define emit_float64(ctx, value) \ + do { \ + wasm_loader_emit_const(ctx, &value, false); \ + LOG_OP("%f\t", value); \ + } while (0) + +static bool +wasm_loader_ctx_reinit(WASMLoaderContext *ctx) +{ + if (!(ctx->p_code_compiled = + loader_malloc(ctx->code_compiled_peak_size, NULL, 0))) + return false; + ctx->p_code_compiled_end = + ctx->p_code_compiled + ctx->code_compiled_peak_size; + + /* clean up frame ref */ + memset(ctx->frame_ref_bottom, 0, ctx->frame_ref_size); + ctx->frame_ref = ctx->frame_ref_bottom; + ctx->stack_cell_num = 0; + + /* clean up frame csp */ + memset(ctx->frame_csp_bottom, 0, ctx->frame_csp_size); + ctx->frame_csp = ctx->frame_csp_bottom; + ctx->csp_num = 0; + ctx->max_csp_num = 0; + + /* clean up frame offset */ + memset(ctx->frame_offset_bottom, 0, ctx->frame_offset_size); + ctx->frame_offset = ctx->frame_offset_bottom; + ctx->dynamic_offset = ctx->start_dynamic_offset; + + /* init preserved local offsets */ + ctx->preserved_local_offset = ctx->max_dynamic_offset; + + /* const buf is reserved */ + return true; +} + +static void +increase_compiled_code_space(WASMLoaderContext *ctx, int32 size) +{ + ctx->code_compiled_size += size; + if (ctx->code_compiled_size >= ctx->code_compiled_peak_size) { + ctx->code_compiled_peak_size = ctx->code_compiled_size; + } +} + +static void +wasm_loader_emit_const(WASMLoaderContext *ctx, void *value, bool is_32_bit) +{ + uint32 size = is_32_bit ? sizeof(uint32) : sizeof(uint64); + + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + bh_memcpy_s(ctx->p_code_compiled, + (uint32)(ctx->p_code_compiled_end - ctx->p_code_compiled), + value, size); + ctx->p_code_compiled += size; + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, size); + } +} + +static void +wasm_loader_emit_uint32(WASMLoaderContext *ctx, uint32 value) +{ + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + STORE_U32(ctx->p_code_compiled, value); + ctx->p_code_compiled += sizeof(uint32); + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, sizeof(uint32)); + } +} + +static void +wasm_loader_emit_int16(WASMLoaderContext *ctx, int16 value) +{ + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + STORE_U16(ctx->p_code_compiled, (uint16)value); + ctx->p_code_compiled += sizeof(int16); + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, sizeof(uint16)); + } +} + +static void +wasm_loader_emit_uint8(WASMLoaderContext *ctx, uint8 value) +{ + if (ctx->p_code_compiled) { + *(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(uint8); +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + ctx->p_code_compiled++; + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + } + else { + increase_compiled_code_space(ctx, sizeof(uint8)); +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + increase_compiled_code_space(ctx, sizeof(uint8)); + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + } +} + +static void +wasm_loader_emit_ptr(WASMLoaderContext *ctx, void *value) +{ + if (ctx->p_code_compiled) { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); +#endif + STORE_PTR(ctx->p_code_compiled, value); + ctx->p_code_compiled += sizeof(void *); + } + else { +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + bh_assert((ctx->code_compiled_size & 1) == 0); +#endif + increase_compiled_code_space(ctx, sizeof(void *)); + } +} + +static void +wasm_loader_emit_backspace(WASMLoaderContext *ctx, uint32 size) +{ + if (ctx->p_code_compiled) { + ctx->p_code_compiled -= size; +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + if (size == sizeof(uint8)) { + ctx->p_code_compiled--; + bh_assert(((uintptr_t)ctx->p_code_compiled & 1) == 0); + } +#endif + } + else { + ctx->code_compiled_size -= size; +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + if (size == sizeof(uint8)) { + ctx->code_compiled_size--; + bh_assert((ctx->code_compiled_size & 1) == 0); + } +#endif + } +} + +static bool +preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, + uint32 local_index, uint32 local_type, + bool *preserved, char *error_buf, + uint32 error_buf_size) +{ + uint32 i = 0; + int16 preserved_offset = (int16)local_index; + + *preserved = false; + while (i < loader_ctx->stack_cell_num) { + uint8 cur_type = loader_ctx->frame_ref_bottom[i]; + + /* move previous local into dynamic space before a set/tee_local opcode + */ + if (loader_ctx->frame_offset_bottom[i] == (int16)local_index) { + if (!(*preserved)) { + *preserved = true; + skip_label(); + preserved_offset = loader_ctx->preserved_local_offset; + if (loader_ctx->p_code_compiled) { + bh_assert(preserved_offset != (int16)local_index); + } + if (is_32bit_type(local_type)) { + /* Only increase preserve offset in the second traversal */ + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset++; + emit_label(EXT_OP_COPY_STACK_TOP); + } + else { + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset += 2; + emit_label(EXT_OP_COPY_STACK_TOP_I64); + } + + /* overflow */ + bh_assert(preserved_offset + <= loader_ctx->preserved_local_offset); + + emit_operand(loader_ctx, local_index); + emit_operand(loader_ctx, preserved_offset); + emit_label(opcode); + } + loader_ctx->frame_offset_bottom[i] = preserved_offset; + } + + if (is_32bit_type(cur_type)) + i++; + else + i += 2; + } + + return true; +} + +static bool +preserve_local_for_block(WASMLoaderContext *loader_ctx, uint8 opcode, + char *error_buf, uint32 error_buf_size) +{ + uint32 i = 0; + bool preserve_local; + + /* preserve locals before blocks to ensure that "tee/set_local" inside + blocks will not influence the value of these locals */ + while (i < loader_ctx->stack_cell_num) { + int16 cur_offset = loader_ctx->frame_offset_bottom[i]; + uint8 cur_type = loader_ctx->frame_ref_bottom[i]; + + if ((cur_offset < loader_ctx->start_dynamic_offset) + && (cur_offset >= 0)) { + if (!(preserve_referenced_local(loader_ctx, opcode, cur_offset, + cur_type, &preserve_local, + error_buf, error_buf_size))) + return false; + } + + if (is_32bit_type(cur_type == VALUE_TYPE_I32)) { + i++; + } + else { + i += 2; + } + } + + return true; +} + +static bool +add_label_patch_to_list(BranchBlock *frame_csp, uint8 patch_type, + uint8 *p_code_compiled, char *error_buf, + uint32 error_buf_size) +{ + BranchBlockPatch *patch = + loader_malloc(sizeof(BranchBlockPatch), error_buf, error_buf_size); + if (!patch) { + return false; + } + patch->patch_type = patch_type; + patch->code_compiled = p_code_compiled; + if (!frame_csp->patch_list) { + frame_csp->patch_list = patch; + patch->next = NULL; + } + else { + patch->next = frame_csp->patch_list; + frame_csp->patch_list = patch; + } + return true; +} + +static void +apply_label_patch(WASMLoaderContext *ctx, uint8 depth, uint8 patch_type) +{ + BranchBlock *frame_csp = ctx->frame_csp - depth; + BranchBlockPatch *node = frame_csp->patch_list; + BranchBlockPatch *node_prev = NULL, *node_next; + + if (!ctx->p_code_compiled) + return; + + while (node) { + node_next = node->next; + if (node->patch_type == patch_type) { + STORE_PTR(node->code_compiled, ctx->p_code_compiled); + if (node_prev == NULL) { + frame_csp->patch_list = node_next; + } + else { + node_prev->next = node_next; + } + wasm_runtime_free(node); + } + else { + node_prev = node; + } + node = node_next; + } +} + +static bool +wasm_loader_emit_br_info(WASMLoaderContext *ctx, BranchBlock *frame_csp, + bool is_br, char *error_buf, uint32 error_buf_size) +{ + /* br info layout: + * a) arity of target block + * b) total cell num of arity values + * c) each arity value's cell num + * d) each arity value's src frame offset + * e) each arity values's dst dynamic offset + * f) branch target address + * + * Note: b-e are omitted when arity is 0 so that + * interpreter can recover the br info quickly. + */ + BlockType *block_type = &frame_csp->block_type; + uint8 *types = NULL, cell; + uint32 arity = 0; + int32 i; + int16 *frame_offset = ctx->frame_offset; + uint16 dynamic_offset; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(block_type, &types); + else + arity = block_type_get_result_types(block_type, &types); + + /* Part a */ + emit_uint32(ctx, arity); + + if (arity) { + /* Part b */ + emit_uint32(ctx, wasm_get_cell_num(types, arity)); + + /* Part c */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = (uint8)wasm_value_type_cell_num(types[i]); + emit_byte(ctx, cell); + } + /* Part d */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = (uint8)wasm_value_type_cell_num(types[i]); + frame_offset -= cell; + emit_operand(ctx, *(int16 *)(frame_offset)); + } + /* Part e */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) + /* Use start_dynamic_offset which was set in + copy_params_to_dynamic_space */ + dynamic_offset = frame_csp->start_dynamic_offset + + wasm_get_cell_num(types, arity); + else + dynamic_offset = + frame_csp->dynamic_offset + wasm_get_cell_num(types, arity); + if (is_br) + ctx->dynamic_offset = dynamic_offset; + for (i = (int32)arity - 1; i >= 0; i--) { + cell = (uint8)wasm_value_type_cell_num(types[i]); + dynamic_offset -= cell; + emit_operand(ctx, dynamic_offset); + } + } + + /* Part f */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) { + wasm_loader_emit_ptr(ctx, frame_csp->code_compiled); + } + else { + if (!add_label_patch_to_list(frame_csp, PATCH_END, ctx->p_code_compiled, + error_buf, error_buf_size)) + return false; + /* label address, to be patched */ + wasm_loader_emit_ptr(ctx, NULL); + } + + return true; +} + +static bool +wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + uint32 cell_num_to_push, i; + + if (type == VALUE_TYPE_VOID) + return true; + + /* only check memory overflow in first traverse */ + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + if (disable_emit) + *(ctx->frame_offset)++ = operand_offset; + else { + emit_operand(ctx, ctx->dynamic_offset); + *(ctx->frame_offset)++ = ctx->dynamic_offset; + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { + ctx->max_dynamic_offset = ctx->dynamic_offset; + bh_assert(ctx->max_dynamic_offset < INT16_MAX); + } + } + + if (is_32bit_type(type)) + return true; + + cell_num_to_push = wasm_value_type_cell_num(type) - 1; + for (i = 0; i < cell_num_to_push; i++) { + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + ctx->frame_offset++; + if (!disable_emit) { + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) { + ctx->max_dynamic_offset = ctx->dynamic_offset; + bh_assert(ctx->max_dynamic_offset < INT16_MAX); + } + } + } + + return true; +} + +/* This function should be in front of wasm_loader_pop_frame_ref + as they both use ctx->stack_cell_num, and ctx->stack_cell_num + will be modified by wasm_loader_pop_frame_ref */ +static bool +wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* if ctx->frame_csp equals ctx->frame_csp_bottom, + then current block is the function block */ + uint32 depth = ctx->frame_csp > ctx->frame_csp_bottom ? 1 : 0; + BranchBlock *cur_block = ctx->frame_csp - depth; + int32 available_stack_cell = + (int32)(ctx->stack_cell_num - cur_block->stack_cell_num); + uint32 cell_num_to_pop; + + /* Directly return success if current block is in stack + polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + /* Change type to ANY when the stack top is ANY, so as to avoid + popping unneeded offsets, e.g. if type is I64/F64, we may pop + two offsets */ + if (available_stack_cell > 0 && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) + type = VALUE_TYPE_ANY; + + cell_num_to_pop = wasm_value_type_cell_num(type); + + /* Check the offset stack bottom to ensure the frame offset + stack will not go underflow. But we don't thrown error + and return true here, because the error msg should be + given in wasm_loader_pop_frame_ref */ + if (!check_offset_pop(ctx, cell_num_to_pop)) + return true; + + ctx->frame_offset -= cell_num_to_pop; + if (check_dynamic_offset_pop(ctx, cell_num_to_pop) + && (*(ctx->frame_offset) > ctx->start_dynamic_offset) + && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) + ctx->dynamic_offset -= cell_num_to_pop; + + emit_operand(ctx, *(ctx->frame_offset)); + + (void)error_buf; + (void)error_buf_size; + return true; +} + +static bool +wasm_loader_push_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (!(wasm_loader_push_frame_offset(ctx, type, disable_emit, operand_offset, + error_buf, error_buf_size))) + return false; + if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size))) + return false; + + return true; +} + +static bool +wasm_loader_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* put wasm_loader_pop_frame_offset in front of wasm_loader_pop_frame_ref */ + if (!wasm_loader_pop_frame_offset(ctx, type, error_buf, error_buf_size)) + return false; + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_push_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + uint8 i; + + for (i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_offset(ctx, type_pop, error_buf, + error_buf_size)) + return false; + + if (!wasm_loader_pop_frame_ref(ctx, type_pop, error_buf, + error_buf_size)) + return false; + } + + if (!wasm_loader_push_frame_offset(ctx, type_push, disable_emit, + operand_offset, error_buf, + error_buf_size)) + return false; + + if (!wasm_loader_push_frame_ref(ctx, type_push, error_buf, error_buf_size)) + return false; + + return true; +} + +static int +cmp_i64_const(const void *p_i64_const1, const void *p_i64_const2) +{ + int64 i64_const1 = *(int64 *)p_i64_const1; + int64 i64_const2 = *(int64 *)p_i64_const2; + + return (i64_const1 < i64_const2) ? -1 : (i64_const1 > i64_const2) ? 1 : 0; +} + +static int +cmp_i32_const(const void *p_i32_const1, const void *p_i32_const2) +{ + int32 i32_const1 = *(int32 *)p_i32_const1; + int32 i32_const2 = *(int32 *)p_i32_const2; + + return (i32_const1 < i32_const2) ? -1 : (i32_const1 > i32_const2) ? 1 : 0; +} + +static bool +wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, void *value, + int16 *offset, char *error_buf, + uint32 error_buf_size) +{ + if (!ctx->p_code_compiled) { + /* Treat i64 and f64 as the same by reading i64 value from + the raw bytes */ + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) { + /* No slot left, emit const instead */ + if (ctx->i64_const_num * 2 + ctx->i32_const_num > INT16_MAX - 2) { + *offset = 0; + return true; + } + + /* Traverse the list if the const num is small */ + if (ctx->i64_const_num < 10) { + for (uint32 i = 0; i < ctx->i64_const_num; i++) { + if (ctx->i64_consts[i] == *(int64 *)value) { + *offset = -1; + return true; + } + } + } + + if (ctx->i64_const_num >= ctx->i64_const_max_num) { + MEM_REALLOC(ctx->i64_consts, + sizeof(int64) * ctx->i64_const_max_num, + sizeof(int64) * (ctx->i64_const_max_num * 2)); + ctx->i64_const_max_num *= 2; + } + ctx->i64_consts[ctx->i64_const_num++] = *(int64 *)value; + } + else { + /* Treat i32 and f32 as the same by reading i32 value from + the raw bytes */ + bh_assert(type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32); + + /* No slot left, emit const instead */ + if (ctx->i64_const_num * 2 + ctx->i32_const_num > INT16_MAX - 1) { + *offset = 0; + return true; + } + + /* Traverse the list if the const num is small */ + if (ctx->i32_const_num < 10) { + for (uint32 i = 0; i < ctx->i32_const_num; i++) { + if (ctx->i32_consts[i] == *(int32 *)value) { + *offset = -1; + return true; + } + } + } + + if (ctx->i32_const_num >= ctx->i32_const_max_num) { + MEM_REALLOC(ctx->i32_consts, + sizeof(int32) * ctx->i32_const_max_num, + sizeof(int32) * (ctx->i32_const_max_num * 2)); + ctx->i32_const_max_num *= 2; + } + ctx->i32_consts[ctx->i32_const_num++] = *(int32 *)value; + } + + *offset = -1; + return true; + } + else { + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) { + int64 key = *(int64 *)value, *i64_const; + i64_const = bsearch(&key, ctx->i64_consts, ctx->i64_const_num, + sizeof(int64), cmp_i64_const); + if (!i64_const) { /* not found, emit const instead */ + *offset = 0; + return true; + } + *offset = -(uint32)(ctx->i64_const_num * 2 + ctx->i32_const_num) + + (uint32)(i64_const - ctx->i64_consts) * 2; + } + else { + int32 key = *(int32 *)value, *i32_const; + i32_const = bsearch(&key, ctx->i32_consts, ctx->i32_const_num, + sizeof(int32), cmp_i32_const); + if (!i32_const) { /* not found, emit const instead */ + *offset = 0; + return true; + } + *offset = -(uint32)(ctx->i32_const_num) + + (uint32)(i32_const - ctx->i32_consts); + } + + return true; + } +fail: + return false; +} + +/* + PUSH(POP)_XXX = push(pop) frame_ref + push(pop) frame_offset + -- Mostly used for the binary / compare operation + PUSH(POP)_OFFSET_TYPE only push(pop) the frame_offset stack + -- Mostly used in block / control instructions + + The POP will always emit the offset on the top of the frame_offset stack + PUSH can be used in two ways: + 1. directly PUSH: + PUSH_XXX(); + will allocate a dynamic space and emit + 2. silent PUSH: + operand_offset = xxx; disable_emit = true; + PUSH_XXX(); + only push the frame_offset stack, no emit +*/ +#define PUSH_I32() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_F32() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_I64() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_F64() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_FUNCREF() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_FUNCREF, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_I32() \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_F32() \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_I64() \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_F64() \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_OFFSET_TYPE(type) \ + do { \ + if (!(wasm_loader_push_frame_offset(loader_ctx, type, disable_emit, \ + operand_offset, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_OFFSET_TYPE(type) \ + do { \ + if (!(wasm_loader_pop_frame_offset(loader_ctx, type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_MEM_OFFSET() \ + do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, mem_offset_type, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) +#define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET() + +#define PUSH_TBL_ELEM_IDX() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, table_elem_idx_type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_MEM_OFFSET() \ + do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, mem_offset_type, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_TBL_ELEM_IDX() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, table_elem_idx_type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref_offset( \ + loader_ctx, 1, type_push, type_pop, disable_emit, \ + operand_offset, error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref_offset( \ + loader_ctx, 2, type_push, type_pop, disable_emit, \ + operand_offset, error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#else /* WASM_ENABLE_FAST_INTERP */ + +#define PUSH_I32() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_F32() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_I64() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_F64() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_FUNCREF() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_FUNCREF, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_MEM_OFFSET() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, mem_offset_type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_PAGE_COUNT() PUSH_MEM_OFFSET() + +#define PUSH_TBL_ELEM_IDX() \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, table_elem_idx_type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_I32() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I32, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_F32() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F32, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_I64() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I64, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_F64() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F64, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_FUNCREF() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_FUNCREF, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_MEM_OFFSET() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, mem_offset_type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_TBL_ELEM_IDX() \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, table_elem_idx_type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 1, type_push, \ + type_pop, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) \ + do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 2, type_push, \ + type_pop, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) +#endif /* WASM_ENABLE_FAST_INTERP */ + +#if WASM_ENABLE_FAST_INTERP != 0 + +static bool +reserve_block_ret(WASMLoaderContext *loader_ctx, uint8 opcode, + bool disable_emit, char *error_buf, uint32 error_buf_size) +{ + int16 operand_offset = 0; + BranchBlock *block = (opcode == WASM_OP_ELSE) ? loader_ctx->frame_csp - 1 + : loader_ctx->frame_csp; + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0, value_count = 0, total_cel_num = 0; + int32 i = 0; + int16 dynamic_offset, dynamic_offset_org, *frame_offset = NULL, + *frame_offset_org = NULL; + + return_count = block_type_get_result_types(block_type, &return_types); + + /* If there is only one return value, use EXT_OP_COPY_STACK_TOP/_I64 instead + * of EXT_OP_COPY_STACK_VALUES for interpreter performance. */ + if (return_count == 1) { + uint8 cell = (uint8)wasm_value_type_cell_num(return_types[0]); + if (cell <= 2 /* V128 isn't supported whose cell num is 4 */ + && block->dynamic_offset != *(loader_ctx->frame_offset - cell)) { + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(cell == 1 ? EXT_OP_COPY_STACK_TOP + : EXT_OP_COPY_STACK_TOP_I64); + emit_operand(loader_ctx, *(loader_ctx->frame_offset - cell)); + emit_operand(loader_ctx, block->dynamic_offset); + + if (opcode == WASM_OP_ELSE) { + *(loader_ctx->frame_offset - cell) = block->dynamic_offset; + } + else { + loader_ctx->frame_offset -= cell; + loader_ctx->dynamic_offset = block->dynamic_offset; + PUSH_OFFSET_TYPE(return_types[0]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + } + return true; + } + + /* Copy stack top values to block's results which are in dynamic space. + * The instruction format: + * Part a: values count + * Part b: all values total cell num + * Part c: each value's cell_num, src offset and dst offset + * Part d: each value's src offset and dst offset + * Part e: each value's dst offset + */ + frame_offset = frame_offset_org = loader_ctx->frame_offset; + dynamic_offset = dynamic_offset_org = + block->dynamic_offset + wasm_get_cell_num(return_types, return_count); + + /* First traversal to get the count of values needed to be copied. */ + for (i = (int32)return_count - 1; i >= 0; i--) { + uint8 cells = (uint8)wasm_value_type_cell_num(return_types[i]); + + frame_offset -= cells; + dynamic_offset -= cells; + if (dynamic_offset != *frame_offset) { + value_count++; + total_cel_num += cells; + } + } + + if (value_count) { + uint32 j = 0; + uint8 *emit_data = NULL, *cells = NULL; + int16 *src_offsets = NULL; + uint16 *dst_offsets = NULL; + uint64 size = + (uint64)value_count + * (sizeof(*cells) + sizeof(*src_offsets) + sizeof(*dst_offsets)); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + value_count); + dst_offsets = (uint16 *)(src_offsets + value_count); + + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, value_count); + /* Part b) */ + emit_uint32(loader_ctx, total_cel_num); + + /* Second traversal to get each value's cell num, src offset and dst + * offset. */ + frame_offset = frame_offset_org; + dynamic_offset = dynamic_offset_org; + for (i = (int32)return_count - 1, j = 0; i >= 0; i--) { + uint8 cell = (uint8)wasm_value_type_cell_num(return_types[i]); + frame_offset -= cell; + dynamic_offset -= cell; + if (dynamic_offset != *frame_offset) { + /* cell num */ + cells[j] = cell; + /* src offset */ + src_offsets[j] = *frame_offset; + /* dst offset */ + dst_offsets[j] = dynamic_offset; + j++; + } + if (opcode == WASM_OP_ELSE) { + *frame_offset = dynamic_offset; + } + else { + loader_ctx->frame_offset = frame_offset; + loader_ctx->dynamic_offset = dynamic_offset; + if (!(wasm_loader_push_frame_offset( + loader_ctx, return_types[i], disable_emit, + operand_offset, error_buf, error_buf_size))) { + wasm_runtime_free(emit_data); + goto fail; + } + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + loader_ctx->frame_offset = frame_offset_org; + loader_ctx->dynamic_offset = dynamic_offset_org; + } + } + + bh_assert(j == value_count); + + /* Emit the cells, src_offsets and dst_offsets */ + for (j = 0; j < value_count; j++) + emit_byte(loader_ctx, cells[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, src_offsets[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, dst_offsets[j]); + + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + + wasm_runtime_free(emit_data); + } + + return true; + +fail: + return false; +} + +#endif /* WASM_ENABLE_FAST_INTERP */ + +#define PUSH_TYPE(type) \ + do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_TYPE(type) \ + do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, type, error_buf, \ + error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_CSP(label_type, block_type, _start_addr) \ + do { \ + if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \ + _start_addr, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_CSP() \ + do { \ + if (!wasm_loader_pop_frame_csp(loader_ctx, error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() \ + do { \ + read_leb_uint32(p, p_end, local_idx); \ + bh_assert(local_idx < param_count + local_count); \ + local_type = local_idx < param_count \ + ? param_types[local_idx] \ + : local_types[local_idx - param_count]; \ + local_offset = local_offsets[local_idx]; \ + } while (0) + +#define CHECK_MEMORY() \ + do { \ + bh_assert(module->import_memory_count + module->memory_count > 0); \ + } while (0) + +static bool +wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, uint8 opcode, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *target_block, *cur_block; + BlockType *target_block_type; + uint8 *types = NULL, *frame_ref; + uint32 arity = 0; + int32 i, available_stack_cell; + uint16 cell_num; + + uint8 *frame_ref_old = loader_ctx->frame_ref; + uint8 *frame_ref_after_popped = NULL; + uint8 frame_ref_tmp[4] = { 0 }; + uint8 *frame_ref_buf = frame_ref_tmp; + uint32 stack_cell_num_old = loader_ctx->stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + int16 *frame_offset_old = loader_ctx->frame_offset; + int16 *frame_offset_after_popped = NULL; + int16 frame_offset_tmp[4] = { 0 }; + int16 *frame_offset_buf = frame_offset_tmp; + uint16 dynamic_offset_old = (loader_ctx->frame_csp - 1)->dynamic_offset; +#endif + bool ret = false; + + bh_assert(loader_ctx->csp_num > 0); + if (loader_ctx->csp_num - 1 < depth) { + set_error_buf(error_buf, error_buf_size, + "unknown label, " + "unexpected end of section or function"); + return false; + } + + cur_block = loader_ctx->frame_csp - 1; + target_block = loader_ctx->frame_csp - (depth + 1); + target_block_type = &target_block->block_type; + frame_ref = loader_ctx->frame_ref; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ + if (target_block->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(target_block_type, &types); + else + arity = block_type_get_result_types(target_block_type, &types); + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (cur_block->is_stack_polymorphic) { + for (i = (int32)arity - 1; i >= 0; i--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(types[i]); +#endif + POP_TYPE(types[i]); + } + + /* Backup stack data since it may be changed in the below + push operations, and the stack data may be used when + checking other target blocks of opcode br_table */ + if (opcode == WASM_OP_BR_TABLE) { + uint64 total_size; + + frame_ref_after_popped = loader_ctx->frame_ref; + total_size = (uint64)sizeof(uint8) + * (frame_ref_old - frame_ref_after_popped); + if (total_size > sizeof(frame_ref_tmp) + && !(frame_ref_buf = loader_malloc(total_size, error_buf, + error_buf_size))) { + goto fail; + } + bh_memcpy_s(frame_ref_buf, (uint32)total_size, + frame_ref_after_popped, (uint32)total_size); + +#if WASM_ENABLE_FAST_INTERP != 0 + frame_offset_after_popped = loader_ctx->frame_offset; + total_size = (uint64)sizeof(int16) + * (frame_offset_old - frame_offset_after_popped); + if (total_size > sizeof(frame_offset_tmp) + && !(frame_offset_buf = loader_malloc(total_size, error_buf, + error_buf_size))) { + goto fail; + } + bh_memcpy_s(frame_offset_buf, (uint32)total_size, + frame_offset_after_popped, (uint32)total_size); +#endif + } + + for (i = 0; i < (int32)arity; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(types[i]); +#endif + PUSH_TYPE(types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(target_block, opcode == WASM_OP_BR); +#endif + + /* Restore the stack data, note that frame_ref_bottom, + frame_reftype_map_bottom, frame_offset_bottom may be + re-allocated in the above push operations */ + if (opcode == WASM_OP_BR_TABLE) { + uint32 total_size; + + /* The stack operand num should not be smaller than before + after pop and push operations */ + bh_assert(loader_ctx->stack_cell_num >= stack_cell_num_old); + loader_ctx->stack_cell_num = stack_cell_num_old; + loader_ctx->frame_ref = + loader_ctx->frame_ref_bottom + stack_cell_num_old; + total_size = (uint32)sizeof(uint8) + * (frame_ref_old - frame_ref_after_popped); + bh_memcpy_s((uint8 *)loader_ctx->frame_ref - total_size, total_size, + frame_ref_buf, total_size); + +#if WASM_ENABLE_FAST_INTERP != 0 + loader_ctx->frame_offset = + loader_ctx->frame_offset_bottom + stack_cell_num_old; + total_size = (uint32)sizeof(int16) + * (frame_offset_old - frame_offset_after_popped); + bh_memcpy_s((uint8 *)loader_ctx->frame_offset - total_size, + total_size, frame_offset_buf, total_size); + (loader_ctx->frame_csp - 1)->dynamic_offset = dynamic_offset_old; +#endif + } + + ret = true; + goto cleanup_and_return; + } + + available_stack_cell = + (int32)(loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Check stack top values match target block type */ + for (i = (int32)arity - 1; i >= 0; i--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, types[i], + error_buf, error_buf_size)) { + goto fail; + } + cell_num = wasm_value_type_cell_num(types[i]); + frame_ref -= cell_num; + available_stack_cell -= cell_num; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(target_block, opcode == WASM_OP_BR); +#endif + + ret = true; + +cleanup_and_return: +fail: + if (frame_ref_buf && frame_ref_buf != frame_ref_tmp) + wasm_runtime_free(frame_ref_buf); +#if WASM_ENABLE_FAST_INTERP != 0 + if (frame_offset_buf && frame_offset_buf != frame_offset_tmp) + wasm_runtime_free(frame_offset_buf); +#endif + + return ret; +} + +static BranchBlock * +check_branch_block(WASMLoaderContext *loader_ctx, uint8 **p_buf, uint8 *buf_end, + uint8 opcode, char *error_buf, uint32 error_buf_size) +{ + uint8 *p = *p_buf, *p_end = buf_end; + BranchBlock *frame_csp_tmp; + uint32 depth; + + read_leb_uint32(p, p_end, depth); + if (!wasm_loader_check_br(loader_ctx, depth, opcode, error_buf, + error_buf_size)) { + goto fail; + } + + frame_csp_tmp = loader_ctx->frame_csp - depth - 1; + + *p_buf = p; + return frame_csp_tmp; +fail: + return NULL; +} + +static bool +check_block_stack(WASMLoaderContext *loader_ctx, BranchBlock *block, + char *error_buf, uint32 error_buf_size) +{ + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0; + int32 available_stack_cell, return_cell_num, i; + uint8 *frame_ref = NULL; + + available_stack_cell = + (int32)(loader_ctx->stack_cell_num - block->stack_cell_num); + + return_count = block_type_get_result_types(block_type, &return_types); + return_cell_num = + return_count > 0 ? wasm_get_cell_num(return_types, return_count) : 0; + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (block->is_stack_polymorphic) { + for (i = (int32)return_count - 1; i >= 0; i--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(return_types[i]); +#endif + POP_TYPE(return_types[i]); + } + + /* Check stack is empty */ + bh_assert(loader_ctx->stack_cell_num == block->stack_cell_num); + + for (i = 0; i < (int32)return_count; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(return_types[i]); +#endif + PUSH_TYPE(return_types[i]); + } + return true; + } + + /* Check stack cell num equals return cell num */ + bh_assert(available_stack_cell == return_cell_num); + + /* Check stack values match return types */ + frame_ref = loader_ctx->frame_ref; + for (i = (int32)return_count - 1; i >= 0; i--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, + return_types[i], error_buf, error_buf_size)) + return false; + frame_ref -= wasm_value_type_cell_num(return_types[i]); + available_stack_cell -= wasm_value_type_cell_num(return_types[i]); + } + + (void)return_cell_num; + return true; + +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 +/* Copy parameters to dynamic space. + * 1) POP original parameter out; + * 2) Push and copy original values to dynamic space. + * The copy instruction format: + * Part a: param count + * Part b: all param total cell num + * Part c: each param's cell_num, src offset and dst offset + * Part d: each param's src offset + * Part e: each param's dst offset + */ +static bool +copy_params_to_dynamic_space(WASMLoaderContext *loader_ctx, char *error_buf, + uint32 error_buf_size) +{ + bool ret = false; + int16 *frame_offset = NULL; + uint8 *cells = NULL, cell; + int16 *src_offsets = NULL; + uint8 *emit_data = NULL; + uint32 i; + BranchBlock *block = loader_ctx->frame_csp - 1; + BlockType *block_type = &block->block_type; + WASMFuncType *wasm_type = block_type->u.type; + uint32 param_count = block_type->u.type->param_count; + int16 condition_offset = 0; + bool disable_emit = false; + bool is_if_block = (block->label_type == LABEL_TYPE_IF ? true : false); + int16 operand_offset = 0; + + uint64 size = (uint64)param_count * (sizeof(*cells) + sizeof(*src_offsets)); + bh_assert(size > 0); + + /* For if block, we also need copy the condition operand offset. */ + if (is_if_block) + size += sizeof(*cells) + sizeof(*src_offsets); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + param_count); + + if (is_if_block) + condition_offset = *loader_ctx->frame_offset; + + /* POP original parameter out */ + for (i = 0; i < param_count; i++) { + POP_OFFSET_TYPE(wasm_type->types[param_count - i - 1]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + frame_offset = loader_ctx->frame_offset; + + /* Get each param's cell num and src offset */ + for (i = 0; i < param_count; i++) { + cell = (uint8)wasm_value_type_cell_num(wasm_type->types[i]); + cells[i] = cell; + src_offsets[i] = *frame_offset; + frame_offset += cell; + } + /* emit copy instruction */ + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, is_if_block ? param_count + 1 : param_count); + /* Part b) */ + emit_uint32(loader_ctx, is_if_block ? wasm_type->param_cell_num + 1 + : wasm_type->param_cell_num); + /* Part c) */ + for (i = 0; i < param_count; i++) + emit_byte(loader_ctx, cells[i]); + if (is_if_block) + emit_byte(loader_ctx, 1); + + /* Part d) */ + for (i = 0; i < param_count; i++) + emit_operand(loader_ctx, src_offsets[i]); + if (is_if_block) + emit_operand(loader_ctx, condition_offset); + + /* Since the start offset to save the block's params and + * the start offset to save the block's results may be + * different, we remember the dynamic offset for loop block + * so that we can use it to copy the stack operands to the + * loop block's params in wasm_loader_emit_br_info. */ + if (block->label_type == LABEL_TYPE_LOOP) + block->start_dynamic_offset = loader_ctx->dynamic_offset; + + /* Part e) */ + /* Push to dynamic space. The push will emit the dst offset. */ + for (i = 0; i < param_count; i++) + PUSH_OFFSET_TYPE(wasm_type->types[i]); + if (is_if_block) + PUSH_OFFSET_TYPE(VALUE_TYPE_I32); + + ret = true; + +fail: + /* Free the emit data */ + wasm_runtime_free(emit_data); + + return ret; +} +#endif + +/* reset the stack to the state of before entering the last block */ +#if WASM_ENABLE_FAST_INTERP != 0 +#define RESET_STACK() \ + do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + loader_ctx->frame_offset = \ + loader_ctx->frame_offset_bottom + loader_ctx->stack_cell_num; \ + } while (0) +#else +#define RESET_STACK() \ + do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + } while (0) +#endif + +/* set current block's stack polymorphic state */ +#define SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(flag) \ + do { \ + BranchBlock *_cur_block = loader_ctx->frame_csp - 1; \ + _cur_block->is_stack_polymorphic = flag; \ + } while (0) + +#define BLOCK_HAS_PARAM(block_type) \ + (!block_type.is_value_type && block_type.u.type->param_count > 0) + +#define PRESERVE_LOCAL_FOR_BLOCK() \ + do { \ + if (!(preserve_local_for_block(loader_ctx, opcode, error_buf, \ + error_buf_size))) { \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_FAST_INTERP == 0 + +#define pb_read_leb_uint32 read_leb_uint32 +#define pb_read_leb_int32 read_leb_int32 +#define pb_read_leb_int64 read_leb_int64 +#define pb_read_leb_memarg read_leb_memarg +#define pb_read_leb_mem_offset read_leb_mem_offset +#define pb_read_leb_memidx read_leb_memidx + +#else + +/* Read leb without malformed format check */ +static uint64 +read_leb_quick(uint8 **p_buf, uint32 maxbits, bool sign) +{ + uint8 *buf = *p_buf; + uint64 result = 0, byte = 0; + uint32 shift = 0; + + do { + byte = *buf++; + result |= ((byte & 0x7f) << shift); + shift += 7; + } while (byte & 0x80); + + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= (~((uint64)0)) << shift; + } + + *p_buf = buf; + return result; +} + +#define pb_read_leb_uint32(p, p_end, res) \ + do { \ + if (!loader_ctx->p_code_compiled) \ + /* Enable format check in the first scan */ \ + read_leb_uint32(p, p_end, res); \ + else \ + /* Disable format check in the second scan */ \ + res = (uint32)read_leb_quick(&p, 32, false); \ + } while (0) + +#define pb_read_leb_int32(p, p_end, res) \ + do { \ + if (!loader_ctx->p_code_compiled) \ + /* Enable format check in the first scan */ \ + read_leb_int32(p, p_end, res); \ + else \ + /* Disable format check in the second scan */ \ + res = (int32)read_leb_quick(&p, 32, true); \ + } while (0) + +#define pb_read_leb_int64(p, p_end, res) \ + do { \ + if (!loader_ctx->p_code_compiled) \ + /* Enable format check in the first scan */ \ + read_leb_int64(p, p_end, res); \ + else \ + /* Disable format check in the second scan */ \ + res = (int64)read_leb_quick(&p, 64, true); \ + } while (0) + +#if WASM_ENABLE_MULTI_MEMORY != 0 +#define pb_read_leb_memarg read_leb_memarg +#else +#define pb_read_leb_memarg pb_read_leb_uint32 +#endif + +#if WASM_ENABLE_MEMORY64 != 0 +#define pb_read_leb_mem_offset read_leb_mem_offset +#else +#define pb_read_leb_mem_offset pb_read_leb_uint32 +#endif + +#define pb_read_leb_memidx pb_read_leb_uint32 + +#endif /* end of WASM_ENABLE_FAST_INTERP != 0 */ + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + uint32 cur_func_idx, char *error_buf, + uint32 error_buf_size) +{ + uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; + uint32 param_count, local_count, global_count; + uint8 *param_types, *local_types, local_type, global_type, mem_offset_type, + table_elem_idx_type; + BlockType func_block_type; + uint16 *local_offsets, local_offset; + uint32 count, local_idx, global_idx, u32, align, i, memidx; + mem_offset_t mem_offset; + int32 i32, i32_const = 0; + int64 i64_const; + uint8 opcode, u8; + bool return_value = false; + WASMLoaderContext *loader_ctx; + BranchBlock *frame_csp_tmp; +#if WASM_ENABLE_BULK_MEMORY != 0 + uint32 segment_index; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + int16 operand_offset = 0; + uint8 last_op = 0; + bool disable_emit, preserve_local = false, if_condition_available = true; + float32 f32_const; + float64 f64_const; + + LOG_OP("\nProcessing func | [%d] params | [%d] locals | [%d] return\n", + func->param_cell_num, func->local_cell_num, func->ret_cell_num); +#endif +#if WASM_ENABLE_MEMORY64 != 0 + bool is_memory64 = has_module_memory64(module); + mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32; +#else + mem_offset_type = VALUE_TYPE_I32; + table_elem_idx_type = VALUE_TYPE_I32; +#endif + + global_count = module->import_global_count + module->global_count; + + param_count = func->func_type->param_count; + param_types = func->func_type->types; + + func_block_type.is_value_type = false; + func_block_type.u.type = func->func_type; + + local_count = func->local_count; + local_types = func->local_types; + local_offsets = func->local_offsets; + + if (!(loader_ctx = wasm_loader_ctx_init(func, error_buf, error_buf_size))) { + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* For the first traverse, the initial value of preserved_local_offset has + * not been determined, we use the INT16_MAX to represent that a slot has + * been copied to preserve space. For second traverse, this field will be + * set to the appropriate value in wasm_loader_ctx_reinit. + * This is for Issue #1230, + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/1230, the + * drop opcodes need to know which slots are preserved, so those slots will + * not be treated as dynamically allocated slots */ + loader_ctx->preserved_local_offset = INT16_MAX; + +re_scan: + if (loader_ctx->code_compiled_size > 0) { + if (!wasm_loader_ctx_reinit(loader_ctx)) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + goto fail; + } + p = func->code; + func->code_compiled = loader_ctx->p_code_compiled; + func->code_compiled_size = loader_ctx->code_compiled_size; + + if (loader_ctx->i64_const_num > 0) { + int64 *i64_consts_old = loader_ctx->i64_consts; + + /* Sort the i64 consts */ + qsort(i64_consts_old, loader_ctx->i64_const_num, sizeof(int64), + cmp_i64_const); + + /* Remove the duplicated i64 consts */ + uint32 k = 1; + for (i = 1; i < loader_ctx->i64_const_num; i++) { + if (i64_consts_old[i] != i64_consts_old[i - 1]) { + i64_consts_old[k++] = i64_consts_old[i]; + } + } + + if (k < loader_ctx->i64_const_num) { + int64 *i64_consts_new; + /* Try to reallocate memory with a smaller size */ + if ((i64_consts_new = + wasm_runtime_malloc((uint32)sizeof(int64) * k))) { + bh_memcpy_s(i64_consts_new, (uint32)sizeof(int64) * k, + i64_consts_old, (uint32)sizeof(int64) * k); + /* Free the old memory */ + wasm_runtime_free(i64_consts_old); + loader_ctx->i64_consts = i64_consts_new; + loader_ctx->i64_const_max_num = k; + } + loader_ctx->i64_const_num = k; + } + } + + if (loader_ctx->i32_const_num > 0) { + int32 *i32_consts_old = loader_ctx->i32_consts; + + /* Sort the i32 consts */ + qsort(i32_consts_old, loader_ctx->i32_const_num, sizeof(int32), + cmp_i32_const); + + /* Remove the duplicated i32 consts */ + uint32 k = 1; + for (i = 1; i < loader_ctx->i32_const_num; i++) { + if (i32_consts_old[i] != i32_consts_old[i - 1]) { + i32_consts_old[k++] = i32_consts_old[i]; + } + } + + if (k < loader_ctx->i32_const_num) { + int32 *i32_consts_new; + /* Try to reallocate memory with a smaller size */ + if ((i32_consts_new = + wasm_runtime_malloc((uint32)sizeof(int32) * k))) { + bh_memcpy_s(i32_consts_new, (uint32)sizeof(int32) * k, + i32_consts_old, (uint32)sizeof(int32) * k); + /* Free the old memory */ + wasm_runtime_free(i32_consts_old); + loader_ctx->i32_consts = i32_consts_new; + loader_ctx->i32_const_max_num = k; + } + loader_ctx->i32_const_num = k; + } + } + } +#endif + + PUSH_CSP(LABEL_TYPE_FUNCTION, func_block_type, p); + + while (p < p_end) { + opcode = *p++; +#if WASM_ENABLE_FAST_INTERP != 0 + p_org = p; + disable_emit = false; + emit_label(opcode); +#endif + + switch (opcode) { + case WASM_OP_UNREACHABLE: + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + + case WASM_OP_NOP: +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + break; + + case WASM_OP_IF: + { +#if WASM_ENABLE_FAST_INTERP != 0 + BranchBlock *parent_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - parent_block->stack_cell_num); + + if (available_stack_cell <= 0 + && parent_block->is_stack_polymorphic) + if_condition_available = false; + else + if_condition_available = true; + PRESERVE_LOCAL_FOR_BLOCK(); +#endif + POP_I32(); + goto handle_op_block_and_loop; + } + case WASM_OP_BLOCK: + case WASM_OP_LOOP: +#if WASM_ENABLE_FAST_INTERP != 0 + PRESERVE_LOCAL_FOR_BLOCK(); +#endif + handle_op_block_and_loop: + { + uint8 value_type; + BlockType block_type; +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 available_params = 0; +#endif + + p_org = p - 1; + value_type = read_uint8(p); + if (is_byte_a_type(value_type)) { + /* If the first byte is one of these special values: + * 0x40/0x7F/0x7E/0x7D/0x7C, take it as the type of + * the single return value. */ + block_type.is_value_type = true; + block_type.u.value_type.type = value_type; + } + else { + int32 type_index; + /* Resolve the leb128 encoded type index as block type */ + p--; + pb_read_leb_int32(p, p_end, type_index); + bh_assert((uint32)type_index < module->type_count); + block_type.is_value_type = false; + block_type.u.type = module->types[type_index]; +#if WASM_ENABLE_FAST_INTERP == 0 + /* If block use type index as block type, change the opcode + * to new extended opcode so that interpreter can resolve + * the block quickly. + */ + *p_org = EXT_OP_BLOCK + (opcode - WASM_OP_BLOCK); +#endif + } + + /* Pop block parameters from stack */ + if (BLOCK_HAS_PARAM(block_type)) { + WASMFuncType *wasm_type = block_type.u.type; + + BranchBlock *cur_block = loader_ctx->frame_csp - 1; +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 cell_num; + available_params = block_type.u.type->param_count; +#endif + for (i = 0; i < block_type.u.type->param_count; i++) { + + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + if (available_stack_cell <= 0 + && cur_block->is_stack_polymorphic) { +#if WASM_ENABLE_FAST_INTERP != 0 + available_params = i; +#endif + break; + } + + POP_TYPE( + wasm_type->types[wasm_type->param_count - i - 1]); +#if WASM_ENABLE_FAST_INTERP != 0 + /* decrease the frame_offset pointer accordingly to keep + * consistent with frame_ref stack */ + cell_num = wasm_value_type_cell_num( + wasm_type->types[wasm_type->param_count - i - 1]); + loader_ctx->frame_offset -= cell_num; +#endif + } + } + + PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK), + block_type, p); + + /* Pass parameters to block */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 cell_num = wasm_value_type_cell_num( + block_type.u.type->types[i]); + if (i >= available_params) { + /* If there isn't enough data on stack, push a dummy + * offset to keep the stack consistent with + * frame_ref. + * Since the stack is already in polymorphic state, + * the opcode will not be executed, so the dummy + * offset won't cause any error */ + uint32 n; + + for (n = 0; n < cell_num; n++) { + if (loader_ctx->p_code_compiled == NULL) { + if (!check_offset_push(loader_ctx, + error_buf, + error_buf_size)) + goto fail; + } + *loader_ctx->frame_offset++ = 0; + } + } + else { + loader_ctx->frame_offset += cell_num; + } +#endif + PUSH_TYPE(block_type.u.type->types[i]); + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (opcode == WASM_OP_BLOCK || opcode == WASM_OP_LOOP) { + skip_label(); + if (BLOCK_HAS_PARAM(block_type)) { + /* Make sure params are in dynamic space */ + if (!copy_params_to_dynamic_space(loader_ctx, error_buf, + error_buf_size)) + goto fail; + } + if (opcode == WASM_OP_LOOP) { + (loader_ctx->frame_csp - 1)->code_compiled = + loader_ctx->p_code_compiled; + } + } + else if (opcode == WASM_OP_IF) { + BranchBlock *block = loader_ctx->frame_csp - 1; + /* If block has parameters, we should make sure they are in + * dynamic space. Otherwise, when else branch is missing, + * the later opcode may consume incorrect operand offset. + * Spec case: + * (func (export "params-id") (param i32) (result i32) + * (i32.const 1) + * (i32.const 2) + * (if (param i32 i32) (result i32 i32) (local.get 0) + * (then)) (i32.add) + * ) + * + * So we should emit a copy instruction before the if. + * + * And we also need to save the parameter offsets and + * recover them before entering else branch. + * + */ + if (BLOCK_HAS_PARAM(block_type)) { + uint64 size; + + /* In polymorphic state, there may be no if condition on + * the stack, so the offset may not emitted */ + if (if_condition_available) { + /* skip the if condition operand offset */ + wasm_loader_emit_backspace(loader_ctx, + sizeof(int16)); + } + /* skip the if label */ + skip_label(); + /* Emit a copy instruction */ + if (!copy_params_to_dynamic_space(loader_ctx, error_buf, + error_buf_size)) + goto fail; + + /* Emit the if instruction */ + emit_label(opcode); + /* Emit the new condition operand offset */ + POP_OFFSET_TYPE(VALUE_TYPE_I32); + + /* Save top param_count values of frame_offset stack, so + * that we can recover it before executing else branch + */ + size = sizeof(int16) + * (uint64)block_type.u.type->param_cell_num; + if (!(block->param_frame_offsets = loader_malloc( + size, error_buf, error_buf_size))) + goto fail; + bh_memcpy_s(block->param_frame_offsets, (uint32)size, + loader_ctx->frame_offset + - size / sizeof(int16), + (uint32)size); + } + + block->start_dynamic_offset = loader_ctx->dynamic_offset; + + emit_empty_label_addr_and_frame_ip(PATCH_ELSE); + emit_empty_label_addr_and_frame_ip(PATCH_END); + } +#endif + break; + } + + case WASM_OP_ELSE: + handle_op_else: + { + BranchBlock *block = NULL; + BlockType block_type = (loader_ctx->frame_csp - 1)->block_type; + bh_assert(loader_ctx->csp_num >= 2 + /* the matched if is found */ + && (loader_ctx->frame_csp - 1)->label_type + == LABEL_TYPE_IF + /* duplicated else isn't found */ + && !(loader_ctx->frame_csp - 1)->else_addr); + block = loader_ctx->frame_csp - 1; + + /* check whether if branch's stack matches its result type */ + if (!check_block_stack(loader_ctx, block, error_buf, + error_buf_size)) + goto fail; + + block->else_addr = p - 1; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* if the result of if branch is in local or const area, add a + * copy op */ + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, + error_buf, error_buf_size)) { + goto fail; + } + + emit_empty_label_addr_and_frame_ip(PATCH_END); + apply_label_patch(loader_ctx, 1, PATCH_ELSE); +#endif + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + + /* Pass parameters to if-false branch */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) + PUSH_TYPE(block_type.u.type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Recover top param_count values of frame_offset stack */ + if (BLOCK_HAS_PARAM((block_type))) { + uint32 size; + size = sizeof(int16) * block_type.u.type->param_cell_num; + bh_memcpy_s(loader_ctx->frame_offset, size, + block->param_frame_offsets, size); + loader_ctx->frame_offset += (size / sizeof(int16)); + } + loader_ctx->dynamic_offset = block->start_dynamic_offset; +#endif + + break; + } + + case WASM_OP_END: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + /* check whether block stack matches its result type */ + if (!check_block_stack(loader_ctx, cur_block, error_buf, + error_buf_size)) + goto fail; + + /* if there is no else branch, make a virtual else opcode for + easier integrity check and to copy the correct results to + the block return address for fast-interp mode: + change if block from `if ... end` to `if ... else end` */ + if (cur_block->label_type == LABEL_TYPE_IF + && !cur_block->else_addr) { + opcode = WASM_OP_ELSE; + p--; +#if WASM_ENABLE_FAST_INTERP != 0 + p_org = p; + skip_label(); + disable_emit = false; + emit_label(opcode); +#endif + goto handle_op_else; + } + + POP_CSP(); + +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + /* copy the result to the block return address */ + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, + error_buf, error_buf_size)) { + free_label_patch_list(loader_ctx->frame_csp); + goto fail; + } + + apply_label_patch(loader_ctx, 0, PATCH_END); + free_label_patch_list(loader_ctx->frame_csp); + if (loader_ctx->frame_csp->label_type == LABEL_TYPE_FUNCTION) { + int32 idx; + uint8 ret_type; + + emit_label(WASM_OP_RETURN); + for (idx = (int32)func->func_type->result_count - 1; + idx >= 0; idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); + POP_OFFSET_TYPE(ret_type); + } + } +#endif + if (loader_ctx->csp_num > 0) { + loader_ctx->frame_csp->end_addr = p - 1; + } + else { + /* end of function block, function will return */ + bh_assert(p == p_end); + } + + break; + } + + case WASM_OP_BR: + { + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) + goto fail; + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + } + + case WASM_OP_BR_IF: + { + POP_I32(); + + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) + goto fail; + + break; + } + + case WASM_OP_BR_TABLE: + { + uint8 *ret_types = NULL; + uint32 ret_count = 0, depth = 0; +#if WASM_ENABLE_FAST_INTERP == 0 + BrTableCache *br_table_cache = NULL; + uint8 *p_depth_begin, *p_depth, *p_opcode = p - 1; + uint32 j; +#endif + + pb_read_leb_uint32(p, p_end, count); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, count); +#endif + POP_I32(); + + /* Get each depth and check it */ + p_org = p; + for (i = 0; i <= count; i++) { + pb_read_leb_uint32(p, p_end, depth); + bh_assert(loader_ctx->csp_num > 0); + bh_assert(loader_ctx->csp_num - 1 >= depth); + (void)depth; + } + p = p_org; + +#if WASM_ENABLE_FAST_INTERP == 0 + p_depth_begin = p_depth = p; +#endif + for (i = 0; i <= count; i++) { + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, opcode, + error_buf, error_buf_size))) + goto fail; + +#if WASM_ENABLE_FAST_INTERP == 0 + depth = (uint32)(loader_ctx->frame_csp - 1 - frame_csp_tmp); + if (br_table_cache) { + br_table_cache->br_depths[i] = depth; + } + else { + if (depth > 255) { + /* The depth cannot be stored in one byte, + create br_table cache to store each depth */ + if (!(br_table_cache = loader_malloc( + offsetof(BrTableCache, br_depths) + + sizeof(uint32) + * (uint64)(count + 1), + error_buf, error_buf_size))) { + goto fail; + } + *p_opcode = EXT_OP_BR_TABLE_CACHE; + br_table_cache->br_table_op_addr = p_opcode; + br_table_cache->br_count = count; + /* Copy previous depths which are one byte */ + for (j = 0; j < i; j++) { + br_table_cache->br_depths[j] = p_depth_begin[j]; + } + br_table_cache->br_depths[i] = depth; + bh_list_insert(module->br_table_cache_list, + br_table_cache); + } + else { + /* The depth can be stored in one byte, use the + byte of the leb to store it */ + *p_depth++ = (uint8)depth; + } + } +#endif + } + +#if WASM_ENABLE_FAST_INTERP == 0 + /* Set the tailing bytes to nop */ + if (br_table_cache) + p_depth = p_depth_begin; + while (p_depth < p) + *p_depth++ = WASM_OP_NOP; +#endif + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + (void)ret_count; + (void)ret_types; + break; + } + + case WASM_OP_RETURN: + { + int32 idx; + uint8 ret_type; + for (idx = (int32)func->func_type->result_count - 1; idx >= 0; + idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); +#if WASM_ENABLE_FAST_INTERP != 0 + /* emit the offset after return opcode */ + POP_OFFSET_TYPE(ret_type); +#endif + POP_TYPE(ret_type); + } + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + break; + } + + case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif + { + WASMFuncType *func_type; + uint32 func_idx; + int32 idx; + + pb_read_leb_uint32(p, p_end, func_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + /* we need to emit func_idx before arguments */ + emit_uint32(loader_ctx, func_idx); +#endif + + bh_assert(func_idx < module->import_function_count + + module->function_count); + + if (func_idx < module->import_function_count) + func_type = + module->import_functions[func_idx].u.function.func_type; + else + func_type = module + ->functions[func_idx + - module->import_function_count] + ->func_type; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; + idx--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + POP_TYPE(func_type->types[idx]); + } + } + +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Here we emit each return value's dynamic_offset. But + * in fact these offsets are continuous, so interpreter + * only need to get the first return value's offset. + */ + PUSH_OFFSET_TYPE( + func_type->types[func_type->param_count + i]); +#endif + } +#if WASM_ENABLE_TAIL_CALL != 0 + } + else { + bh_assert(func_type->result_count + == func->func_type->result_count); + for (i = 0; i < func_type->result_count; i++) { + bh_assert( + func_type->types[func_type->param_count + i] + == func->func_type + ->types[func->func_type->param_count + i]); + } + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_func_call = true; +#endif + break; + } + + case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif + { + int32 idx; + WASMFuncType *func_type; + uint32 type_idx, table_idx; + + bh_assert(module->import_table_count + module->table_count > 0); + + pb_read_leb_uint32(p, p_end, type_idx); + +#if WASM_ENABLE_CALL_INDIRECT_OVERLONG != 0 + pb_read_leb_uint32(p, p_end, table_idx); +#else + CHECK_BUF(p, p_end, 1); + table_idx = read_uint8(p); +#endif + if (!check_table_index(module, table_idx, error_buf, + error_buf_size)) { + goto fail; + } + + bh_assert( + (table_idx < module->import_table_count + ? module->import_tables[table_idx] + .u.table.table_type.elem_type + : module + ->tables[table_idx - module->import_table_count] + .table_type.elem_type) + == VALUE_TYPE_FUNCREF); + +#if WASM_ENABLE_FAST_INTERP != 0 + /* we need to emit before arguments */ + emit_uint32(loader_ctx, type_idx); + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + /* skip elem idx */ + POP_TBL_ELEM_IDX(); + + bh_assert(type_idx < module->type_count); + + func_type = module->types[type_idx]; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; + idx--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + POP_TYPE(func_type->types[idx]); + } + } + +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE( + func_type->types[func_type->param_count + i]); +#endif + } +#if WASM_ENABLE_TAIL_CALL != 0 + } + else { + bh_assert(func_type->result_count + == func->func_type->result_count); + for (i = 0; i < func_type->result_count; i++) { + bh_assert( + func_type->types[func_type->param_count + i] + == func->func_type + ->types[func->func_type->param_count + i]); + } + } +#endif + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_func_call = true; +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_call_indirect = true; +#endif + break; + } + +#if WASM_ENABLE_EXCE_HANDLING != 0 + case WASM_OP_TRY: + case WASM_OP_CATCH: + case WASM_OP_THROW: + case WASM_OP_RETHROW: + case WASM_OP_DELEGATE: + case WASM_OP_CATCH_ALL: + /* TODO */ + set_error_buf(error_buf, error_buf_size, "unsupported opcode"); + goto fail; +#endif + + case WASM_OP_DROP: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + + bh_assert(!(available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic)); + + if (available_stack_cell > 0) { + if (is_32bit_type(*(loader_ctx->frame_ref - 1))) { + loader_ctx->frame_ref--; + loader_ctx->stack_cell_num--; +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset--; + if ((*(loader_ctx->frame_offset) + > loader_ctx->start_dynamic_offset) + && (*(loader_ctx->frame_offset) + < loader_ctx->max_dynamic_offset)) + loader_ctx->dynamic_offset--; +#endif + } + else if (is_64bit_type(*(loader_ctx->frame_ref - 1))) { + loader_ctx->frame_ref -= 2; + loader_ctx->stack_cell_num -= 2; +#if WASM_ENABLE_FAST_INTERP == 0 + *(p - 1) = WASM_OP_DROP_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset -= 2; + if ((*(loader_ctx->frame_offset) + > loader_ctx->start_dynamic_offset) + && (*(loader_ctx->frame_offset) + < loader_ctx->max_dynamic_offset)) + loader_ctx->dynamic_offset -= 2; +#endif + } + else { + bh_assert(0); + } + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + } + break; + } + + case WASM_OP_SELECT: + { + uint8 ref_type; + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell; +#if WASM_ENABLE_FAST_INTERP != 0 + uint8 *p_code_compiled_tmp = loader_ctx->p_code_compiled; +#endif + + POP_I32(); + + available_stack_cell = (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + + bh_assert(!(available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic)); + + if (available_stack_cell > 0) { + switch (*(loader_ctx->frame_ref - 1)) { + case REF_I32: + case REF_F32: + case REF_ANY: + break; + case REF_I64_2: + case REF_F64_2: +#if WASM_ENABLE_FAST_INTERP == 0 + *(p - 1) = WASM_OP_SELECT_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT_64; +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void **)(p_code_compiled_tmp + - sizeof(void *)) = + handle_table[opcode_tmp]; +#else +#if UINTPTR_MAX == UINT64_MAX + /* emit int32 relative offset in 64-bit target + */ + int32 offset = + (int32)((uint8 *)handle_table[opcode_tmp] + - (uint8 *)handle_table[0]); + *(int32 *)(p_code_compiled_tmp + - sizeof(int32)) = offset; +#else + /* emit uint32 label address in 32-bit target */ + *(uint32 *)(p_code_compiled_tmp + - sizeof(uint32)) = + (uint32)(uintptr_t)handle_table[opcode_tmp]; +#endif +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } +#endif + break; + } + + ref_type = *(loader_ctx->frame_ref - 1); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(VALUE_TYPE_ANY); +#endif + PUSH_TYPE(VALUE_TYPE_ANY); + } + break; + } + +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + { + uint8 vec_len, ref_type; +#if WASM_ENABLE_FAST_INTERP != 0 + uint8 *p_code_compiled_tmp = loader_ctx->p_code_compiled; +#endif + + pb_read_leb_uint32(p, p_end, vec_len); + if (vec_len != 1) { + /* typed select must have exactly one result */ + set_error_buf(error_buf, error_buf_size, + "invalid result arity"); + goto fail; + } + + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + if (!is_valid_value_type_for_interpreter(ref_type)) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } + + POP_I32(); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT; + + if (ref_type == VALUE_TYPE_F64 + || ref_type == VALUE_TYPE_I64) + opcode_tmp = WASM_OP_SELECT_64; + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void **)(p_code_compiled_tmp - sizeof(void *)) = + handle_table[opcode_tmp]; +#else +#if UINTPTR_MAX == UINT64_MAX + /* emit int32 relative offset in 64-bit target */ + int32 offset = (int32)((uint8 *)handle_table[opcode_tmp] + - (uint8 *)handle_table[0]); + *(int32 *)(p_code_compiled_tmp - sizeof(int32)) = offset; +#else + /* emit uint32 label address in 32-bit target */ + *(uint32 *)(p_code_compiled_tmp - sizeof(uint32)) = + (uint32)(uintptr_t)handle_table[opcode_tmp]; +#endif +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + PUSH_OFFSET_TYPE(ref_type); + PUSH_TYPE(ref_type); +#else + POP2_AND_PUSH(ref_type, ref_type); +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + + (void)vec_len; + break; + } + + /* table.get x. tables[x]. [it] -> [t] */ + /* table.set x. tables[x]. [it t] -> [] */ + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + { + uint8 decl_ref_type; + uint32 table_idx; + + pb_read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, &decl_ref_type, + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + if (opcode == WASM_OP_TABLE_GET) { + POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(decl_ref_type); +#endif + PUSH_TYPE(decl_ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + POP_TBL_ELEM_IDX(); + } + break; + } + case WASM_OP_REF_NULL: + { + uint8 ref_type; + + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + if (ref_type != VALUE_TYPE_FUNCREF + && ref_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + break; + } + case WASM_OP_REF_IS_NULL: + { +#if WASM_ENABLE_FAST_INTERP != 0 + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 block_stack_cell_num = + (int32)(loader_ctx->stack_cell_num + - cur_block->stack_cell_num); + if (block_stack_cell_num <= 0) { + if (!cur_block->is_stack_polymorphic) { + set_error_buf( + error_buf, error_buf_size, + "type mismatch: expect data but stack was empty"); + goto fail; + } + } + else { + if (*(loader_ctx->frame_ref - 1) == VALUE_TYPE_FUNCREF + || *(loader_ctx->frame_ref - 1) == VALUE_TYPE_EXTERNREF + || *(loader_ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + if (!wasm_loader_pop_frame_ref_offset( + loader_ctx, *(loader_ctx->frame_ref - 1), + error_buf, error_buf_size)) { + goto fail; + } + } + else { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + } +#else + if (!wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_FUNCREF, + error_buf, error_buf_size) + && !wasm_loader_pop_frame_ref(loader_ctx, + VALUE_TYPE_EXTERNREF, + error_buf, error_buf_size)) { + goto fail; + } +#endif + PUSH_I32(); + break; + } + case WASM_OP_REF_FUNC: + { + uint32 func_idx = 0; + pb_read_leb_uint32(p, p_end, func_idx); + + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + + /* Refer to a forward-declared function: + the function must be an import, exported, or present in + a table elem segment or global initializer to be used as + the operand to ref.func */ + if (func_idx >= module->import_function_count) { + WASMTableSeg *table_seg = module->table_segments; + bool func_declared = false; + uint32 j; + + for (i = 0; i < module->global_count; i++) { + if (module->globals[i].type.val_type + == VALUE_TYPE_FUNCREF + && module->globals[i].init_expr.init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST + && module->globals[i].init_expr.u.unary.v.ref_index + == func_idx) { + func_declared = true; + break; + } + } + + if (!func_declared) { + /* Check whether the function is declared in table segs, + note that it doesn't matter whether the table seg's + mode is passive, active or declarative. */ + for (i = 0; i < module->table_seg_count; + i++, table_seg++) { + if (table_seg->elem_type == VALUE_TYPE_FUNCREF) { + for (j = 0; j < table_seg->value_count; j++) { + if (table_seg->init_values[j] + .u.unary.v.ref_index + == func_idx) { + func_declared = true; + break; + } + } + } + } + } + + if (!func_declared) { + /* Check whether the function is exported */ + for (i = 0; i < module->export_count; i++) { + if (module->exports[i].kind == EXPORT_KIND_FUNC + && module->exports[i].index == func_idx) { + func_declared = true; + break; + } + } + } + bh_assert(func_declared); + (void)func_declared; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, func_idx); +#endif + PUSH_FUNCREF(); + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + + case WASM_OP_GET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Get Local is optimized out */ + skip_label(); + disable_emit = true; + operand_offset = local_offset; + PUSH_OFFSET_TYPE(local_type); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_FAST_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_GET_LOCAL_FAST; + if (is_32bit_type(local_type)) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_SET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local( + loader_ctx, opcode, local_offset, local_type, + &preserve_local, error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256) { + skip_label(); + if ((!preserve_local) && (LAST_OP_OUTPUT_I32())) { + if (loader_ctx->p_code_compiled) + STORE_U16(loader_ctx->p_code_compiled - 2, + local_offset); + loader_ctx->frame_offset--; + loader_ctx->dynamic_offset--; + } + else if ((!preserve_local) && (LAST_OP_OUTPUT_I64())) { + if (loader_ctx->p_code_compiled) + STORE_U16(loader_ctx->p_code_compiled - 2, + local_offset); + loader_ctx->frame_offset -= 2; + loader_ctx->dynamic_offset -= 2; + } + else { + if (is_32bit_type(local_type)) { + emit_label(EXT_OP_SET_LOCAL_FAST); + emit_byte(loader_ctx, (uint8)local_offset); + } + else { + emit_label(EXT_OP_SET_LOCAL_FAST_I64); + emit_byte(loader_ctx, (uint8)local_offset); + } + POP_OFFSET_TYPE(local_type); + } + } + else { /* local index larger than 255, reserve leb */ + emit_uint32(loader_ctx, local_idx); + POP_OFFSET_TYPE(local_type); + } +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_FAST_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_SET_LOCAL_FAST; + if (is_32bit_type(local_type)) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + POP_TYPE(local_type); + break; + } + + case WASM_OP_TEE_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); +#if WASM_ENABLE_FAST_INTERP != 0 + /* If the stack is in polymorphic state, do fake pop and push on + offset stack to keep the depth of offset stack to be the + same with ref stack */ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + if (cur_block->is_stack_polymorphic) { + POP_OFFSET_TYPE(local_type); + PUSH_OFFSET_TYPE(local_type); + } +#endif + POP_TYPE(local_type); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local( + loader_ctx, opcode, local_offset, local_type, + &preserve_local, error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256) { + skip_label(); + if (is_32bit_type(local_type)) { + emit_label(EXT_OP_TEE_LOCAL_FAST); + emit_byte(loader_ctx, (uint8)local_offset); + } + else { + emit_label(EXT_OP_TEE_LOCAL_FAST_I64); + emit_byte(loader_ctx, (uint8)local_offset); + } + } + else { /* local index larger than 255, reserve leb */ + emit_uint32(loader_ctx, local_idx); + } + emit_operand(loader_ctx, + *(loader_ctx->frame_offset + - wasm_value_type_cell_num(local_type))); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) \ + && (WASM_ENABLE_FAST_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_TEE_LOCAL_FAST; + if (is_32bit_type(local_type)) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_GET_GLOBAL: + { + p_org = p - 1; + pb_read_leb_uint32(p, p_end, global_idx); + bh_assert(global_idx < global_count); + + global_type = global_idx < module->import_global_count + ? module->import_globals[global_idx] + .u.global.type.val_type + : module + ->globals[global_idx + - module->import_global_count] + .type.val_type; + + PUSH_TYPE(global_type); + +#if WASM_ENABLE_FAST_INTERP == 0 + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + *p_org = WASM_OP_GET_GLOBAL_64; + } +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (is_64bit_type(global_type)) { + skip_label(); + emit_label(WASM_OP_GET_GLOBAL_64); + } + emit_uint32(loader_ctx, global_idx); + PUSH_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + break; + } + + case WASM_OP_SET_GLOBAL: + { + bool is_mutable = false; + + p_org = p - 1; + pb_read_leb_uint32(p, p_end, global_idx); + bh_assert(global_idx < global_count); + + is_mutable = global_idx < module->import_global_count + ? module->import_globals[global_idx] + .u.global.type.is_mutable + : module + ->globals[global_idx + - module->import_global_count] + .type.is_mutable; + bh_assert(is_mutable); + + global_type = global_idx < module->import_global_count + ? module->import_globals[global_idx] + .u.global.type.val_type + : module + ->globals[global_idx + - module->import_global_count] + .type.val_type; + +#if WASM_ENABLE_FAST_INTERP == 0 + if (is_64bit_type(global_type)) { + *p_org = WASM_OP_SET_GLOBAL_64; + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + *p_org = WASM_OP_SET_GLOBAL_AUX_STACK; +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_set_global_aux_stack = true; +#endif + } +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (is_64bit_type(global_type)) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_64); + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_AUX_STACK); + } + emit_uint32(loader_ctx, global_idx); + POP_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + + POP_TYPE(global_type); + + (void)is_mutable; + break; + } + + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + { +#if WASM_ENABLE_FAST_INTERP != 0 + /* change F32/F64 into I32/I64 */ + if (opcode == WASM_OP_F32_LOAD) { + skip_label(); + emit_label(WASM_OP_I32_LOAD); + } + else if (opcode == WASM_OP_F64_LOAD) { + skip_label(); + emit_label(WASM_OP_I64_LOAD); + } + else if (opcode == WASM_OP_F32_STORE) { + skip_label(); + emit_label(WASM_OP_I32_STORE); + } + else if (opcode == WASM_OP_F64_STORE) { + skip_label(); + emit_label(WASM_OP_I64_STORE); + } +#endif + CHECK_MEMORY(); + pb_read_leb_memarg(p, p_end, align); /* align */ + pb_read_leb_mem_offset(p, p_end, mem_offset); /* offset */ +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + switch (opcode) { + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I32); + break; + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I64); + break; + case WASM_OP_F32_LOAD: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_F32); + break; + case WASM_OP_F64_LOAD: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_F64); + break; + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + POP_I32(); + POP_MEM_OFFSET(); + break; + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + POP_I64(); + POP_MEM_OFFSET(); + break; + case WASM_OP_F32_STORE: + POP_F32(); + POP_MEM_OFFSET(); + break; + case WASM_OP_F64_STORE: + POP_F64(); + POP_MEM_OFFSET(); + break; + default: + break; + } + break; + } + + case WASM_OP_MEMORY_SIZE: + CHECK_MEMORY(); + pb_read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + PUSH_PAGE_COUNT(); + + module->possible_memory_grow = true; +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + + case WASM_OP_MEMORY_GROW: + CHECK_MEMORY(); + pb_read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + POP_AND_PUSH(mem_offset_type, mem_offset_type); + + module->possible_memory_grow = true; +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_op_memory_grow = true; +#endif +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + + case WASM_OP_I32_CONST: + pb_read_leb_int32(p, p_end, i32_const); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I32_CONST); + emit_uint32(loader_ctx, i32_const); + } +#else + (void)i32_const; +#endif + PUSH_I32(); + break; + + case WASM_OP_I64_CONST: + pb_read_leb_int64(p, p_end, i64_const); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I64, i64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_I64_CONST); + emit_uint64(loader_ctx, i64_const); + } +#endif + PUSH_I64(); + break; + + case WASM_OP_F32_CONST: + CHECK_BUF(p, p_end, sizeof(float32)); + p += sizeof(float32); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + bh_memcpy_s((uint8 *)&f32_const, sizeof(float32), p_org, + sizeof(float32)); + GET_CONST_F32_OFFSET(VALUE_TYPE_F32, f32_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F32_CONST); + emit_float32(loader_ctx, f32_const); + } +#endif + PUSH_F32(); + break; + + case WASM_OP_F64_CONST: + CHECK_BUF(p, p_end, sizeof(float64)); + p += sizeof(float64); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + /* Some MCU may require 8-byte align */ + bh_memcpy_s((uint8 *)&f64_const, sizeof(float64), p_org, + sizeof(float64)); + GET_CONST_F64_OFFSET(VALUE_TYPE_F64, f64_const); + + if (operand_offset == 0) { + disable_emit = false; + emit_label(WASM_OP_F64_CONST); + emit_float64(loader_ctx, f64_const); + } +#endif + PUSH_F64(); + break; + + case WASM_OP_I32_EQZ: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EQZ: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_WRAP_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_DEMOTE_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_PROMOTE_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_REINTERPRET_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_REINTERPRET_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_REINTERPRET_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_REINTERPRET_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + pb_read_leb_uint32(p, p_end, opcode1); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, ((uint8)opcode1)); +#endif + switch (opcode1) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + CHECK_MEMORY(); + pb_read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, segment_index); +#endif + pb_read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + + bh_assert(segment_index < module->data_seg_count); + bh_assert(module->data_seg_count1 > 0); + + POP_I32(); + POP_I32(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } + case WASM_OP_DATA_DROP: + { + pb_read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, segment_index); +#endif + bh_assert(segment_index < module->data_seg_count); + bh_assert(module->data_seg_count1 > 0); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_BULK_MEMORY_OPT != 0 + case WASM_OP_MEMORY_COPY: + { + CHECK_MEMORY(); + CHECK_BUF(p, p_end, sizeof(int16)); + /* check both src and dst memory index */ + pb_read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + pb_read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + + POP_MEM_OFFSET(); + POP_MEM_OFFSET(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } + case WASM_OP_MEMORY_FILL: + { + CHECK_MEMORY(); + pb_read_leb_memidx(p, p_end, memidx); + check_memidx(module, memidx); + + POP_MEM_OFFSET(); + POP_I32(); + POP_MEM_OFFSET(); +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY_OPT */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint8 seg_ref_type, tbl_ref_type; + uint32 table_seg_idx, table_idx; + + pb_read_leb_uint32(p, p_end, table_seg_idx); + pb_read_leb_uint32(p, p_end, table_idx); + + if (!get_table_elem_type(module, table_idx, + &tbl_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (!get_table_seg_elem_type(module, table_seg_idx, + &seg_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (seg_ref_type != tbl_ref_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); + emit_uint32(loader_ctx, table_idx); +#endif + POP_I32(); + POP_I32(); +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + POP_TBL_ELEM_IDX(); + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 table_seg_idx; + pb_read_leb_uint32(p, p_end, table_seg_idx); + if (!get_table_seg_elem_type(module, table_seg_idx, + NULL, error_buf, + error_buf_size)) + goto fail; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); +#endif + break; + } + case WASM_OP_TABLE_COPY: + { + uint8 src_ref_type, dst_ref_type; + uint32 src_tbl_idx, dst_tbl_idx, src_tbl_idx_type, + dst_tbl_idx_type, min_tbl_idx_type; + + pb_read_leb_uint32(p, p_end, src_tbl_idx); + if (!get_table_elem_type(module, src_tbl_idx, + &src_ref_type, error_buf, + error_buf_size)) + goto fail; + + pb_read_leb_uint32(p, p_end, dst_tbl_idx); + if (!get_table_elem_type(module, dst_tbl_idx, + &dst_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (src_ref_type != dst_ref_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, src_tbl_idx); + emit_uint32(loader_ctx, dst_tbl_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + src_tbl_idx_type = is_table_64bit(module, src_tbl_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; + dst_tbl_idx_type = is_table_64bit(module, dst_tbl_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; + min_tbl_idx_type = + (src_tbl_idx_type == VALUE_TYPE_I32 + || dst_tbl_idx_type == VALUE_TYPE_I32) + ? VALUE_TYPE_I32 + : VALUE_TYPE_I64; +#else + src_tbl_idx_type = VALUE_TYPE_I32; + dst_tbl_idx_type = VALUE_TYPE_I32; + min_tbl_idx_type = VALUE_TYPE_I32; +#endif + + table_elem_idx_type = min_tbl_idx_type; + POP_TBL_ELEM_IDX(); + table_elem_idx_type = src_tbl_idx_type; + POP_TBL_ELEM_IDX(); + table_elem_idx_type = dst_tbl_idx_type; + POP_TBL_ELEM_IDX(); + break; + } + case WASM_OP_TABLE_SIZE: + { + uint32 table_idx; + + pb_read_leb_uint32(p, p_end, table_idx); + /* TODO: shall we create a new function to check + table idx instead of using below function? */ + if (!get_table_elem_type(module, table_idx, NULL, + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + PUSH_TBL_ELEM_IDX(); + break; + } + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + { + uint8 decl_ref_type; + uint32 table_idx; + + pb_read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, + &decl_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (opcode1 == WASM_OP_TABLE_GROW) { + if (table_idx < module->import_table_count) { + module->import_tables[table_idx] + .u.table.table_type.possible_grow = true; + } + else { + module + ->tables[table_idx + - module->import_table_count] + .table_type.possible_grow = true; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + +#if WASM_ENABLE_MEMORY64 != 0 + table_elem_idx_type = is_table_64bit(module, table_idx) + ? VALUE_TYPE_I64 + : VALUE_TYPE_I32; +#endif + POP_TBL_ELEM_IDX(); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + if (opcode1 == WASM_OP_TABLE_GROW) + PUSH_TBL_ELEM_IDX(); + else + PUSH_TBL_ELEM_IDX(); + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + default: + bh_assert(0); + break; + } + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + uint32 opcode1; + + pb_read_leb_uint32(p, p_end, opcode1); + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, opcode1); +#endif + if (opcode1 != WASM_OP_ATOMIC_FENCE) { + CHECK_MEMORY(); + pb_read_leb_uint32(p, p_end, align); /* align */ + pb_read_leb_mem_offset(p, p_end, mem_offset); /* offset */ +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + } +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + func->has_memory_operations = true; +#endif + switch (opcode1) { + case WASM_OP_ATOMIC_NOTIFY: + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_WAIT32: + POP_I64(); + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_WAIT64: + POP_I64(); + POP_I64(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_FENCE: + /* reserved byte 0x00 */ + bh_assert(*p == 0x00); + p++; + break; + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + POP_I32(); + POP_MEM_OFFSET(); + break; + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + POP_AND_PUSH(mem_offset_type, VALUE_TYPE_I64); + break; + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + POP_I64(); + POP_MEM_OFFSET(); + break; + case WASM_OP_ATOMIC_RMW_I32_ADD: + case WASM_OP_ATOMIC_RMW_I32_ADD8_U: + case WASM_OP_ATOMIC_RMW_I32_ADD16_U: + case WASM_OP_ATOMIC_RMW_I32_SUB: + case WASM_OP_ATOMIC_RMW_I32_SUB8_U: + case WASM_OP_ATOMIC_RMW_I32_SUB16_U: + case WASM_OP_ATOMIC_RMW_I32_AND: + case WASM_OP_ATOMIC_RMW_I32_AND8_U: + case WASM_OP_ATOMIC_RMW_I32_AND16_U: + case WASM_OP_ATOMIC_RMW_I32_OR: + case WASM_OP_ATOMIC_RMW_I32_OR8_U: + case WASM_OP_ATOMIC_RMW_I32_OR16_U: + case WASM_OP_ATOMIC_RMW_I32_XOR: + case WASM_OP_ATOMIC_RMW_I32_XOR8_U: + case WASM_OP_ATOMIC_RMW_I32_XOR16_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG: + case WASM_OP_ATOMIC_RMW_I32_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG16_U: + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_RMW_I64_ADD: + case WASM_OP_ATOMIC_RMW_I64_ADD8_U: + case WASM_OP_ATOMIC_RMW_I64_ADD16_U: + case WASM_OP_ATOMIC_RMW_I64_ADD32_U: + case WASM_OP_ATOMIC_RMW_I64_SUB: + case WASM_OP_ATOMIC_RMW_I64_SUB8_U: + case WASM_OP_ATOMIC_RMW_I64_SUB16_U: + case WASM_OP_ATOMIC_RMW_I64_SUB32_U: + case WASM_OP_ATOMIC_RMW_I64_AND: + case WASM_OP_ATOMIC_RMW_I64_AND8_U: + case WASM_OP_ATOMIC_RMW_I64_AND16_U: + case WASM_OP_ATOMIC_RMW_I64_AND32_U: + case WASM_OP_ATOMIC_RMW_I64_OR: + case WASM_OP_ATOMIC_RMW_I64_OR8_U: + case WASM_OP_ATOMIC_RMW_I64_OR16_U: + case WASM_OP_ATOMIC_RMW_I64_OR32_U: + case WASM_OP_ATOMIC_RMW_I64_XOR: + case WASM_OP_ATOMIC_RMW_I64_XOR8_U: + case WASM_OP_ATOMIC_RMW_I64_XOR16_U: + case WASM_OP_ATOMIC_RMW_I64_XOR32_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG: + case WASM_OP_ATOMIC_RMW_I64_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG32_U: + POP_I64(); + POP_MEM_OFFSET(); + PUSH_I64(); + break; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + POP_I32(); + POP_I32(); + POP_MEM_OFFSET(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + POP_I64(); + POP_I64(); + POP_MEM_OFFSET(); + PUSH_I64(); + break; + default: + bh_assert(0); + break; + } + break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + + default: + bh_assert(0); + break; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + last_op = opcode; +#endif + } + + if (loader_ctx->csp_num > 0) { + set_error_buf(error_buf, error_buf_size, + "function body must end with END opcode"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled == NULL) + goto re_scan; + + func->const_cell_num = + loader_ctx->i64_const_num * 2 + loader_ctx->i32_const_num; + if (func->const_cell_num > 0) { + if (!(func->consts = + loader_malloc((uint64)sizeof(uint32) * func->const_cell_num, + error_buf, error_buf_size))) + goto fail; + if (loader_ctx->i64_const_num > 0) { + bh_memcpy_s(func->consts, + (uint32)sizeof(int64) * loader_ctx->i64_const_num, + loader_ctx->i64_consts, + (uint32)sizeof(int64) * loader_ctx->i64_const_num); + } + if (loader_ctx->i32_const_num > 0) { + bh_memcpy_s(func->consts + + sizeof(int64) * loader_ctx->i64_const_num, + (uint32)sizeof(int32) * loader_ctx->i32_const_num, + loader_ctx->i32_consts, + (uint32)sizeof(int32) * loader_ctx->i32_const_num); + } + } + + func->max_stack_cell_num = loader_ctx->preserved_local_offset + - loader_ctx->start_dynamic_offset + 1; +#else + func->max_stack_cell_num = loader_ctx->max_stack_cell_num; +#endif + func->max_block_num = loader_ctx->max_csp_num; + return_value = true; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + + (void)u8; + (void)u32; + (void)i32; + (void)i64_const; + (void)global_count; + (void)local_count; + (void)local_offset; + (void)p_org; + (void)mem_offset; + (void)align; +#if WASM_ENABLE_BULK_MEMORY != 0 + (void)segment_index; +#endif + return return_value; +} diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index e96dc78692..1147384131 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -14,448 +14,1030 @@ extern "C" { typedef enum WASMOpcode { /* control instructions */ - WASM_OP_UNREACHABLE = 0x00, /* unreachable */ - WASM_OP_NOP = 0x01, /* nop */ - WASM_OP_BLOCK = 0x02, /* block */ - WASM_OP_LOOP = 0x03, /* loop */ - WASM_OP_IF = 0x04, /* if */ - WASM_OP_ELSE = 0x05, /* else */ - - WASM_OP_UNUSED_0x06 = 0x06, - WASM_OP_UNUSED_0x07 = 0x07, - WASM_OP_UNUSED_0x08 = 0x08, - WASM_OP_UNUSED_0x09 = 0x09, - WASM_OP_UNUSED_0x0a = 0x0a, - - WASM_OP_END = 0x0b, /* end */ - WASM_OP_BR = 0x0c, /* br */ - WASM_OP_BR_IF = 0x0d, /* br if */ - WASM_OP_BR_TABLE = 0x0e, /* br table */ - WASM_OP_RETURN = 0x0f, /* return */ - WASM_OP_CALL = 0x10, /* call */ - WASM_OP_CALL_INDIRECT = 0x11, /* call_indirect */ - - WASM_OP_UNUSED_0x12 = 0x12, - WASM_OP_UNUSED_0x13 = 0x13, - WASM_OP_UNUSED_0x14 = 0x14, - WASM_OP_UNUSED_0x15 = 0x15, - WASM_OP_UNUSED_0x16 = 0x16, - WASM_OP_UNUSED_0x17 = 0x17, - WASM_OP_UNUSED_0x18 = 0x18, - WASM_OP_UNUSED_0x19 = 0x19, + WASM_OP_UNREACHABLE = 0x00, /* unreachable */ + WASM_OP_NOP = 0x01, /* nop */ + WASM_OP_BLOCK = 0x02, /* block */ + WASM_OP_LOOP = 0x03, /* loop */ + WASM_OP_IF = 0x04, /* if */ + WASM_OP_ELSE = 0x05, /* else */ + WASM_OP_TRY = 0x06, /* try */ + WASM_OP_CATCH = 0x07, /* catch* */ + WASM_OP_THROW = 0x08, /* throw of a try catch */ + WASM_OP_RETHROW = 0x09, /* rethrow of a try catch */ + WASM_OP_UNUSED_0x0a = 0x0a, + + WASM_OP_END = 0x0b, /* end */ + WASM_OP_BR = 0x0c, /* br */ + WASM_OP_BR_IF = 0x0d, /* br if */ + WASM_OP_BR_TABLE = 0x0e, /* br table */ + WASM_OP_RETURN = 0x0f, /* return */ + WASM_OP_CALL = 0x10, /* call */ + WASM_OP_CALL_INDIRECT = 0x11, /* call_indirect */ + WASM_OP_RETURN_CALL = 0x12, /* return_call */ + WASM_OP_RETURN_CALL_INDIRECT = 0x13, /* return_call_indirect */ + WASM_OP_CALL_REF = 0x14, /* call_ref */ + WASM_OP_RETURN_CALL_REF = 0x15, /* return_call_ref */ + + WASM_OP_UNUSED_0x16 = 0x16, + WASM_OP_UNUSED_0x17 = 0x17, + + WASM_OP_DELEGATE = 0x18, /* delegate block of the try catch*/ + WASM_OP_CATCH_ALL = 0x19, /* a catch_all handler in a try block */ /* parametric instructions */ - WASM_OP_DROP = 0x1a, /* drop */ - WASM_OP_SELECT = 0x1b, /* select */ + WASM_OP_DROP = 0x1a, /* drop */ + WASM_OP_SELECT = 0x1b, /* select */ + WASM_OP_SELECT_T = 0x1c, /* select t */ - WASM_OP_UNUSED_0x1c = 0x1c, - WASM_OP_UNUSED_0x1d = 0x1d, - WASM_OP_UNUSED_0x1e = 0x1e, - WASM_OP_UNUSED_0x1f = 0x1f, + WASM_OP_GET_GLOBAL_64 = 0x1d, + WASM_OP_SET_GLOBAL_64 = 0x1e, + WASM_OP_SET_GLOBAL_AUX_STACK = 0x1f, /* variable instructions */ - WASM_OP_GET_LOCAL = 0x20, /* get_local */ - WASM_OP_SET_LOCAL = 0x21, /* set_local */ - WASM_OP_TEE_LOCAL = 0x22, /* tee_local */ - WASM_OP_GET_GLOBAL = 0x23, /* get_global */ - WASM_OP_SET_GLOBAL = 0x24, /* set_global */ + WASM_OP_GET_LOCAL = 0x20, /* get_local */ + WASM_OP_SET_LOCAL = 0x21, /* set_local */ + WASM_OP_TEE_LOCAL = 0x22, /* tee_local */ + WASM_OP_GET_GLOBAL = 0x23, /* get_global */ + WASM_OP_SET_GLOBAL = 0x24, /* set_global */ - WASM_OP_UNUSED_0x25 = 0x25, - WASM_OP_UNUSED_0x26 = 0x26, - WASM_OP_UNUSED_0x27 = 0x27, + WASM_OP_TABLE_GET = 0x25, /* table.get */ + WASM_OP_TABLE_SET = 0x26, /* table.set */ + WASM_OP_UNUSED_0x27 = 0x27, /* memory instructions */ - WASM_OP_I32_LOAD = 0x28, /* i32.load */ - WASM_OP_I64_LOAD = 0x29, /* i64.load */ - WASM_OP_F32_LOAD = 0x2a, /* f32.load */ - WASM_OP_F64_LOAD = 0x2b, /* f64.load */ - WASM_OP_I32_LOAD8_S = 0x2c, /* i32.load8_s */ - WASM_OP_I32_LOAD8_U = 0x2d, /* i32.load8_u */ - WASM_OP_I32_LOAD16_S = 0x2e, /* i32.load16_s */ - WASM_OP_I32_LOAD16_U = 0x2f, /* i32.load16_u */ - WASM_OP_I64_LOAD8_S = 0x30, /* i64.load8_s */ - WASM_OP_I64_LOAD8_U = 0x31, /* i64.load8_u */ - WASM_OP_I64_LOAD16_S = 0x32, /* i64.load16_s */ - WASM_OP_I64_LOAD16_U = 0x33, /* i64.load16_u */ - WASM_OP_I64_LOAD32_S = 0x34, /* i32.load32_s */ - WASM_OP_I64_LOAD32_U = 0x35, /* i32.load32_u */ - WASM_OP_I32_STORE = 0x36, /* i32.store */ - WASM_OP_I64_STORE = 0x37, /* i64.store */ - WASM_OP_F32_STORE = 0x38, /* f32.store */ - WASM_OP_F64_STORE = 0x39, /* f64.store */ - WASM_OP_I32_STORE8 = 0x3a, /* i32.store8 */ - WASM_OP_I32_STORE16 = 0x3b, /* i32.store16 */ - WASM_OP_I64_STORE8 = 0x3c, /* i64.store8 */ - WASM_OP_I64_STORE16 = 0x3d, /* i64.sotre16 */ - WASM_OP_I64_STORE32 = 0x3e, /* i64.store32 */ - WASM_OP_MEMORY_SIZE = 0x3f, /* memory.size */ - WASM_OP_MEMORY_GROW = 0x40, /* memory.grow */ + WASM_OP_I32_LOAD = 0x28, /* i32.load */ + WASM_OP_I64_LOAD = 0x29, /* i64.load */ + WASM_OP_F32_LOAD = 0x2a, /* f32.load */ + WASM_OP_F64_LOAD = 0x2b, /* f64.load */ + WASM_OP_I32_LOAD8_S = 0x2c, /* i32.load8_s */ + WASM_OP_I32_LOAD8_U = 0x2d, /* i32.load8_u */ + WASM_OP_I32_LOAD16_S = 0x2e, /* i32.load16_s */ + WASM_OP_I32_LOAD16_U = 0x2f, /* i32.load16_u */ + WASM_OP_I64_LOAD8_S = 0x30, /* i64.load8_s */ + WASM_OP_I64_LOAD8_U = 0x31, /* i64.load8_u */ + WASM_OP_I64_LOAD16_S = 0x32, /* i64.load16_s */ + WASM_OP_I64_LOAD16_U = 0x33, /* i64.load16_u */ + WASM_OP_I64_LOAD32_S = 0x34, /* i32.load32_s */ + WASM_OP_I64_LOAD32_U = 0x35, /* i32.load32_u */ + WASM_OP_I32_STORE = 0x36, /* i32.store */ + WASM_OP_I64_STORE = 0x37, /* i64.store */ + WASM_OP_F32_STORE = 0x38, /* f32.store */ + WASM_OP_F64_STORE = 0x39, /* f64.store */ + WASM_OP_I32_STORE8 = 0x3a, /* i32.store8 */ + WASM_OP_I32_STORE16 = 0x3b, /* i32.store16 */ + WASM_OP_I64_STORE8 = 0x3c, /* i64.store8 */ + WASM_OP_I64_STORE16 = 0x3d, /* i64.store16 */ + WASM_OP_I64_STORE32 = 0x3e, /* i64.store32 */ + WASM_OP_MEMORY_SIZE = 0x3f, /* memory.size */ + WASM_OP_MEMORY_GROW = 0x40, /* memory.grow */ /* constant instructions */ - WASM_OP_I32_CONST = 0x41, /* i32.const */ - WASM_OP_I64_CONST = 0x42, /* i64.const */ - WASM_OP_F32_CONST = 0x43, /* f32.const */ - WASM_OP_F64_CONST = 0x44, /* f64.const */ + WASM_OP_I32_CONST = 0x41, /* i32.const */ + WASM_OP_I64_CONST = 0x42, /* i64.const */ + WASM_OP_F32_CONST = 0x43, /* f32.const */ + WASM_OP_F64_CONST = 0x44, /* f64.const */ /* comparison instructions */ - WASM_OP_I32_EQZ = 0x45, /* i32.eqz */ - WASM_OP_I32_EQ = 0x46, /* i32.eq */ - WASM_OP_I32_NE = 0x47, /* i32.ne */ - WASM_OP_I32_LT_S = 0x48, /* i32.lt_s */ - WASM_OP_I32_LT_U = 0x49, /* i32.lt_u */ - WASM_OP_I32_GT_S = 0x4a, /* i32.gt_s */ - WASM_OP_I32_GT_U = 0x4b, /* i32.gt_u */ - WASM_OP_I32_LE_S = 0x4c, /* i32.le_s */ - WASM_OP_I32_LE_U = 0x4d, /* i32.le_u */ - WASM_OP_I32_GE_S = 0x4e, /* i32.ge_s */ - WASM_OP_I32_GE_U = 0x4f, /* i32.ge_u */ - - WASM_OP_I64_EQZ = 0x50, /* i64.eqz */ - WASM_OP_I64_EQ = 0x51, /* i64.eq */ - WASM_OP_I64_NE = 0x52, /* i64.ne */ - WASM_OP_I64_LT_S = 0x53, /* i64.lt_s */ - WASM_OP_I64_LT_U = 0x54, /* i64.lt_u */ - WASM_OP_I64_GT_S = 0x55, /* i64.gt_s */ - WASM_OP_I64_GT_U = 0x56, /* i64.gt_u */ - WASM_OP_I64_LE_S = 0x57, /* i64.le_s */ - WASM_OP_I64_LE_U = 0x58, /* i64.le_u */ - WASM_OP_I64_GE_S = 0x59, /* i64.ge_s */ - WASM_OP_I64_GE_U = 0x5a, /* i64.ge_u */ - - WASM_OP_F32_EQ = 0x5b, /* f32.eq */ - WASM_OP_F32_NE = 0x5c, /* f32.ne */ - WASM_OP_F32_LT = 0x5d, /* f32.lt */ - WASM_OP_F32_GT = 0x5e, /* f32.gt */ - WASM_OP_F32_LE = 0x5f, /* f32.le */ - WASM_OP_F32_GE = 0x60, /* f32.ge */ - - WASM_OP_F64_EQ = 0x61, /* f64.eq */ - WASM_OP_F64_NE = 0x62, /* f64.ne */ - WASM_OP_F64_LT = 0x63, /* f64.lt */ - WASM_OP_F64_GT = 0x64, /* f64.gt */ - WASM_OP_F64_LE = 0x65, /* f64.le */ - WASM_OP_F64_GE = 0x66, /* f64.ge */ + WASM_OP_I32_EQZ = 0x45, /* i32.eqz */ + WASM_OP_I32_EQ = 0x46, /* i32.eq */ + WASM_OP_I32_NE = 0x47, /* i32.ne */ + WASM_OP_I32_LT_S = 0x48, /* i32.lt_s */ + WASM_OP_I32_LT_U = 0x49, /* i32.lt_u */ + WASM_OP_I32_GT_S = 0x4a, /* i32.gt_s */ + WASM_OP_I32_GT_U = 0x4b, /* i32.gt_u */ + WASM_OP_I32_LE_S = 0x4c, /* i32.le_s */ + WASM_OP_I32_LE_U = 0x4d, /* i32.le_u */ + WASM_OP_I32_GE_S = 0x4e, /* i32.ge_s */ + WASM_OP_I32_GE_U = 0x4f, /* i32.ge_u */ + + WASM_OP_I64_EQZ = 0x50, /* i64.eqz */ + WASM_OP_I64_EQ = 0x51, /* i64.eq */ + WASM_OP_I64_NE = 0x52, /* i64.ne */ + WASM_OP_I64_LT_S = 0x53, /* i64.lt_s */ + WASM_OP_I64_LT_U = 0x54, /* i64.lt_u */ + WASM_OP_I64_GT_S = 0x55, /* i64.gt_s */ + WASM_OP_I64_GT_U = 0x56, /* i64.gt_u */ + WASM_OP_I64_LE_S = 0x57, /* i64.le_s */ + WASM_OP_I64_LE_U = 0x58, /* i64.le_u */ + WASM_OP_I64_GE_S = 0x59, /* i64.ge_s */ + WASM_OP_I64_GE_U = 0x5a, /* i64.ge_u */ + + WASM_OP_F32_EQ = 0x5b, /* f32.eq */ + WASM_OP_F32_NE = 0x5c, /* f32.ne */ + WASM_OP_F32_LT = 0x5d, /* f32.lt */ + WASM_OP_F32_GT = 0x5e, /* f32.gt */ + WASM_OP_F32_LE = 0x5f, /* f32.le */ + WASM_OP_F32_GE = 0x60, /* f32.ge */ + + WASM_OP_F64_EQ = 0x61, /* f64.eq */ + WASM_OP_F64_NE = 0x62, /* f64.ne */ + WASM_OP_F64_LT = 0x63, /* f64.lt */ + WASM_OP_F64_GT = 0x64, /* f64.gt */ + WASM_OP_F64_LE = 0x65, /* f64.le */ + WASM_OP_F64_GE = 0x66, /* f64.ge */ /* numeric operators */ - WASM_OP_I32_CLZ = 0x67, /* i32.clz */ - WASM_OP_I32_CTZ = 0x68, /* i32.ctz */ - WASM_OP_I32_POPCNT = 0x69, /* i32.popcnt */ - WASM_OP_I32_ADD = 0x6a, /* i32.add */ - WASM_OP_I32_SUB = 0x6b, /* i32.sub */ - WASM_OP_I32_MUL = 0x6c, /* i32.mul */ - WASM_OP_I32_DIV_S = 0x6d, /* i32.div_s */ - WASM_OP_I32_DIV_U = 0x6e, /* i32.div_u */ - WASM_OP_I32_REM_S = 0x6f, /* i32.rem_s */ - WASM_OP_I32_REM_U = 0x70, /* i32.rem_u */ - WASM_OP_I32_AND = 0x71, /* i32.and */ - WASM_OP_I32_OR = 0x72, /* i32.or */ - WASM_OP_I32_XOR = 0x73, /* i32.xor */ - WASM_OP_I32_SHL = 0x74, /* i32.shl */ - WASM_OP_I32_SHR_S = 0x75, /* i32.shr_s */ - WASM_OP_I32_SHR_U = 0x76, /* i32.shr_u */ - WASM_OP_I32_ROTL = 0x77, /* i32.rotl */ - WASM_OP_I32_ROTR = 0x78, /* i32.rotr */ - - WASM_OP_I64_CLZ = 0x79, /* i64.clz */ - WASM_OP_I64_CTZ = 0x7a, /* i64.ctz */ - WASM_OP_I64_POPCNT = 0x7b, /* i64.popcnt */ - WASM_OP_I64_ADD = 0x7c, /* i64.add */ - WASM_OP_I64_SUB = 0x7d, /* i64.sub */ - WASM_OP_I64_MUL = 0x7e, /* i64.mul */ - WASM_OP_I64_DIV_S = 0x7f, /* i64.div_s */ - WASM_OP_I64_DIV_U = 0x80, /* i64.div_u */ - WASM_OP_I64_REM_S = 0x81, /* i64.rem_s */ - WASM_OP_I64_REM_U = 0x82, /* i64.rem_u */ - WASM_OP_I64_AND = 0x83, /* i64.and */ - WASM_OP_I64_OR = 0x84, /* i64.or */ - WASM_OP_I64_XOR = 0x85, /* i64.xor */ - WASM_OP_I64_SHL = 0x86, /* i64.shl */ - WASM_OP_I64_SHR_S = 0x87, /* i64.shr_s */ - WASM_OP_I64_SHR_U = 0x88, /* i64.shr_u */ - WASM_OP_I64_ROTL = 0x89, /* i64.rotl */ - WASM_OP_I64_ROTR = 0x8a, /* i64.rotr */ - - WASM_OP_F32_ABS = 0x8b, /* f32.abs */ - WASM_OP_F32_NEG = 0x8c, /* f32.neg */ - WASM_OP_F32_CEIL = 0x8d, /* f32.ceil */ - WASM_OP_F32_FLOOR = 0x8e, /* f32.floor */ - WASM_OP_F32_TRUNC = 0x8f, /* f32.trunc */ - WASM_OP_F32_NEAREST = 0x90, /* f32.nearest */ - WASM_OP_F32_SQRT = 0x91, /* f32.sqrt */ - WASM_OP_F32_ADD = 0x92, /* f32.add */ - WASM_OP_F32_SUB = 0x93, /* f32.sub */ - WASM_OP_F32_MUL = 0x94, /* f32.mul */ - WASM_OP_F32_DIV = 0x95, /* f32.div */ - WASM_OP_F32_MIN = 0x96, /* f32.min */ - WASM_OP_F32_MAX = 0x97, /* f32.max */ - WASM_OP_F32_COPYSIGN = 0x98, /* f32.copysign */ - - WASM_OP_F64_ABS = 0x99, /* f64.abs */ - WASM_OP_F64_NEG = 0x9a, /* f64.neg */ - WASM_OP_F64_CEIL = 0x9b, /* f64.ceil */ - WASM_OP_F64_FLOOR = 0x9c, /* f64.floor */ - WASM_OP_F64_TRUNC = 0x9d, /* f64.trunc */ - WASM_OP_F64_NEAREST = 0x9e, /* f64.nearest */ - WASM_OP_F64_SQRT = 0x9f, /* f64.sqrt */ - WASM_OP_F64_ADD = 0xa0, /* f64.add */ - WASM_OP_F64_SUB = 0xa1, /* f64.sub */ - WASM_OP_F64_MUL = 0xa2, /* f64.mul */ - WASM_OP_F64_DIV = 0xa3, /* f64.div */ - WASM_OP_F64_MIN = 0xa4, /* f64.min */ - WASM_OP_F64_MAX = 0xa5, /* f64.max */ - WASM_OP_F64_COPYSIGN = 0xa6, /* f64.copysign */ + WASM_OP_I32_CLZ = 0x67, /* i32.clz */ + WASM_OP_I32_CTZ = 0x68, /* i32.ctz */ + WASM_OP_I32_POPCNT = 0x69, /* i32.popcnt */ + WASM_OP_I32_ADD = 0x6a, /* i32.add */ + WASM_OP_I32_SUB = 0x6b, /* i32.sub */ + WASM_OP_I32_MUL = 0x6c, /* i32.mul */ + WASM_OP_I32_DIV_S = 0x6d, /* i32.div_s */ + WASM_OP_I32_DIV_U = 0x6e, /* i32.div_u */ + WASM_OP_I32_REM_S = 0x6f, /* i32.rem_s */ + WASM_OP_I32_REM_U = 0x70, /* i32.rem_u */ + WASM_OP_I32_AND = 0x71, /* i32.and */ + WASM_OP_I32_OR = 0x72, /* i32.or */ + WASM_OP_I32_XOR = 0x73, /* i32.xor */ + WASM_OP_I32_SHL = 0x74, /* i32.shl */ + WASM_OP_I32_SHR_S = 0x75, /* i32.shr_s */ + WASM_OP_I32_SHR_U = 0x76, /* i32.shr_u */ + WASM_OP_I32_ROTL = 0x77, /* i32.rotl */ + WASM_OP_I32_ROTR = 0x78, /* i32.rotr */ + + WASM_OP_I64_CLZ = 0x79, /* i64.clz */ + WASM_OP_I64_CTZ = 0x7a, /* i64.ctz */ + WASM_OP_I64_POPCNT = 0x7b, /* i64.popcnt */ + WASM_OP_I64_ADD = 0x7c, /* i64.add */ + WASM_OP_I64_SUB = 0x7d, /* i64.sub */ + WASM_OP_I64_MUL = 0x7e, /* i64.mul */ + WASM_OP_I64_DIV_S = 0x7f, /* i64.div_s */ + WASM_OP_I64_DIV_U = 0x80, /* i64.div_u */ + WASM_OP_I64_REM_S = 0x81, /* i64.rem_s */ + WASM_OP_I64_REM_U = 0x82, /* i64.rem_u */ + WASM_OP_I64_AND = 0x83, /* i64.and */ + WASM_OP_I64_OR = 0x84, /* i64.or */ + WASM_OP_I64_XOR = 0x85, /* i64.xor */ + WASM_OP_I64_SHL = 0x86, /* i64.shl */ + WASM_OP_I64_SHR_S = 0x87, /* i64.shr_s */ + WASM_OP_I64_SHR_U = 0x88, /* i64.shr_u */ + WASM_OP_I64_ROTL = 0x89, /* i64.rotl */ + WASM_OP_I64_ROTR = 0x8a, /* i64.rotr */ + + WASM_OP_F32_ABS = 0x8b, /* f32.abs */ + WASM_OP_F32_NEG = 0x8c, /* f32.neg */ + WASM_OP_F32_CEIL = 0x8d, /* f32.ceil */ + WASM_OP_F32_FLOOR = 0x8e, /* f32.floor */ + WASM_OP_F32_TRUNC = 0x8f, /* f32.trunc */ + WASM_OP_F32_NEAREST = 0x90, /* f32.nearest */ + WASM_OP_F32_SQRT = 0x91, /* f32.sqrt */ + WASM_OP_F32_ADD = 0x92, /* f32.add */ + WASM_OP_F32_SUB = 0x93, /* f32.sub */ + WASM_OP_F32_MUL = 0x94, /* f32.mul */ + WASM_OP_F32_DIV = 0x95, /* f32.div */ + WASM_OP_F32_MIN = 0x96, /* f32.min */ + WASM_OP_F32_MAX = 0x97, /* f32.max */ + WASM_OP_F32_COPYSIGN = 0x98, /* f32.copysign */ + + WASM_OP_F64_ABS = 0x99, /* f64.abs */ + WASM_OP_F64_NEG = 0x9a, /* f64.neg */ + WASM_OP_F64_CEIL = 0x9b, /* f64.ceil */ + WASM_OP_F64_FLOOR = 0x9c, /* f64.floor */ + WASM_OP_F64_TRUNC = 0x9d, /* f64.trunc */ + WASM_OP_F64_NEAREST = 0x9e, /* f64.nearest */ + WASM_OP_F64_SQRT = 0x9f, /* f64.sqrt */ + WASM_OP_F64_ADD = 0xa0, /* f64.add */ + WASM_OP_F64_SUB = 0xa1, /* f64.sub */ + WASM_OP_F64_MUL = 0xa2, /* f64.mul */ + WASM_OP_F64_DIV = 0xa3, /* f64.div */ + WASM_OP_F64_MIN = 0xa4, /* f64.min */ + WASM_OP_F64_MAX = 0xa5, /* f64.max */ + WASM_OP_F64_COPYSIGN = 0xa6, /* f64.copysign */ /* conversions */ - WASM_OP_I32_WRAP_I64 = 0xa7, /* i32.wrap/i64 */ - WASM_OP_I32_TRUNC_S_F32 = 0xa8, /* i32.trunc_s/f32 */ - WASM_OP_I32_TRUNC_U_F32 = 0xa9, /* i32.trunc_u/f32 */ - WASM_OP_I32_TRUNC_S_F64 = 0xaa, /* i32.trunc_s/f64 */ - WASM_OP_I32_TRUNC_U_F64 = 0xab, /* i32.trunc_u/f64 */ - - WASM_OP_I64_EXTEND_S_I32 = 0xac, /* i64.extend_s/i32 */ - WASM_OP_I64_EXTEND_U_I32 = 0xad, /* i64.extend_u/i32 */ - WASM_OP_I64_TRUNC_S_F32 = 0xae, /* i64.trunc_s/f32 */ - WASM_OP_I64_TRUNC_U_F32 = 0xaf, /* i64.trunc_u/f32 */ - WASM_OP_I64_TRUNC_S_F64 = 0xb0, /* i64.trunc_s/f64 */ - WASM_OP_I64_TRUNC_U_F64 = 0xb1, /* i64.trunc_u/f64 */ + WASM_OP_I32_WRAP_I64 = 0xa7, /* i32.wrap/i64 */ + WASM_OP_I32_TRUNC_S_F32 = 0xa8, /* i32.trunc_s/f32 */ + WASM_OP_I32_TRUNC_U_F32 = 0xa9, /* i32.trunc_u/f32 */ + WASM_OP_I32_TRUNC_S_F64 = 0xaa, /* i32.trunc_s/f64 */ + WASM_OP_I32_TRUNC_U_F64 = 0xab, /* i32.trunc_u/f64 */ + + WASM_OP_I64_EXTEND_S_I32 = 0xac, /* i64.extend_s/i32 */ + WASM_OP_I64_EXTEND_U_I32 = 0xad, /* i64.extend_u/i32 */ + WASM_OP_I64_TRUNC_S_F32 = 0xae, /* i64.trunc_s/f32 */ + WASM_OP_I64_TRUNC_U_F32 = 0xaf, /* i64.trunc_u/f32 */ + WASM_OP_I64_TRUNC_S_F64 = 0xb0, /* i64.trunc_s/f64 */ + WASM_OP_I64_TRUNC_U_F64 = 0xb1, /* i64.trunc_u/f64 */ WASM_OP_F32_CONVERT_S_I32 = 0xb2, /* f32.convert_s/i32 */ WASM_OP_F32_CONVERT_U_I32 = 0xb3, /* f32.convert_u/i32 */ WASM_OP_F32_CONVERT_S_I64 = 0xb4, /* f32.convert_s/i64 */ WASM_OP_F32_CONVERT_U_I64 = 0xb5, /* f32.convert_u/i64 */ - WASM_OP_F32_DEMOTE_F64 = 0xb6, /* f32.demote/f64 */ + WASM_OP_F32_DEMOTE_F64 = 0xb6, /* f32.demote/f64 */ WASM_OP_F64_CONVERT_S_I32 = 0xb7, /* f64.convert_s/i32 */ WASM_OP_F64_CONVERT_U_I32 = 0xb8, /* f64.convert_u/i32 */ WASM_OP_F64_CONVERT_S_I64 = 0xb9, /* f64.convert_s/i64 */ WASM_OP_F64_CONVERT_U_I64 = 0xba, /* f64.convert_u/i64 */ - WASM_OP_F64_PROMOTE_F32 = 0xbb, /* f64.promote/f32 */ + WASM_OP_F64_PROMOTE_F32 = 0xbb, /* f64.promote/f32 */ /* reinterpretations */ - WASM_OP_I32_REINTERPRET_F32 = 0xbc, /* i32.reinterpret/f32 */ - WASM_OP_I64_REINTERPRET_F64 = 0xbd, /* i64.reinterpret/f64 */ - WASM_OP_F32_REINTERPRET_I32 = 0xbe, /* f32.reinterpret/i32 */ - WASM_OP_F64_REINTERPRET_I64 = 0xbf, /* f64.reinterpret/i64 */ + WASM_OP_I32_REINTERPRET_F32 = 0xbc, /* i32.reinterpret/f32 */ + WASM_OP_I64_REINTERPRET_F64 = 0xbd, /* i64.reinterpret/f64 */ + WASM_OP_F32_REINTERPRET_I32 = 0xbe, /* f32.reinterpret/i32 */ + WASM_OP_F64_REINTERPRET_I64 = 0xbf, /* f64.reinterpret/i64 */ + + WASM_OP_I32_EXTEND8_S = 0xc0, /* i32.extend8_s */ + WASM_OP_I32_EXTEND16_S = 0xc1, /* i32.extend16_s */ + WASM_OP_I64_EXTEND8_S = 0xc2, /* i64.extend8_s */ + WASM_OP_I64_EXTEND16_S = 0xc3, /* i64.extend16_s */ + WASM_OP_I64_EXTEND32_S = 0xc4, /* i64.extend32_s */ /* drop/select specified types*/ - WASM_OP_DROP_64 = 0xc0, - WASM_OP_SELECT_64 = 0xc1, - WASM_OP_GET_LOCAL_FAST = 0xc2, - WASM_OP_SET_LOCAL_FAST = 0xc3, - WASM_OP_TEE_LOCAL_FAST = 0xc4, + WASM_OP_DROP_64 = 0xc5, + WASM_OP_SELECT_64 = 0xc6, + + /* extend op code */ + EXT_OP_GET_LOCAL_FAST = 0xc7, + EXT_OP_SET_LOCAL_FAST_I64 = 0xc8, + EXT_OP_SET_LOCAL_FAST = 0xc9, + EXT_OP_TEE_LOCAL_FAST = 0xca, + EXT_OP_TEE_LOCAL_FAST_I64 = 0xcb, + EXT_OP_COPY_STACK_TOP = 0xcc, + EXT_OP_COPY_STACK_TOP_I64 = 0xcd, + EXT_OP_COPY_STACK_VALUES = 0xce, + + WASM_OP_IMPDEP = 0xcf, + + WASM_OP_REF_NULL = 0xd0, /* ref.null */ + WASM_OP_REF_IS_NULL = 0xd1, /* ref.is_null */ + WASM_OP_REF_FUNC = 0xd2, /* ref.func */ + WASM_OP_REF_EQ = 0xd3, /* ref.eq */ + WASM_OP_REF_AS_NON_NULL = 0xd4, /* ref.as_non_null */ + WASM_OP_BR_ON_NULL = 0xd5, /* br_on_null */ + WASM_OP_BR_ON_NON_NULL = 0xd6, /* br_on_non_null */ + + EXT_OP_BLOCK = 0xd7, /* block with blocktype */ + EXT_OP_LOOP = 0xd8, /* loop with blocktype */ + EXT_OP_IF = 0xd9, /* if with blocktype */ + EXT_OP_BR_TABLE_CACHE = 0xda, /* br_table from cache */ - WASM_OP_IMPDEP = 0xc5 + EXT_OP_TRY = 0xdb, /* try block with blocktype */ + +#if WASM_ENABLE_DEBUG_INTERP != 0 + DEBUG_OP_BREAK = 0xdc, /* debug break point */ +#endif + +#if WASM_ENABLE_SIMDE != 0 + EXT_OP_SET_LOCAL_FAST_V128 = 0xdd, + EXT_OP_TEE_LOCAL_FAST_V128 = 0xde, + EXT_OP_COPY_STACK_TOP_V128 = 0xdf, + WASM_OP_GET_GLOBAL_V128 = 0xe0, + WASM_OP_SET_GLOBAL_V128 = 0xe1, + WASM_OP_SELECT_128 = 0xe2, +#endif + + /* Post-MVP extend op prefix */ + WASM_OP_GC_PREFIX = 0xfb, + WASM_OP_MISC_PREFIX = 0xfc, + WASM_OP_SIMD_PREFIX = 0xfd, + WASM_OP_ATOMIC_PREFIX = 0xfe, } WASMOpcode; -#ifdef __cplusplus -} +typedef enum WASMGCEXTOpcode { + WASM_OP_STRUCT_NEW = 0x00, /* struct.new */ + WASM_OP_STRUCT_NEW_DEFAULT = 0x01, /* struct.new_default */ + WASM_OP_STRUCT_GET = 0x02, /* struct.get */ + WASM_OP_STRUCT_GET_S = 0x03, /* struct.get_s */ + WASM_OP_STRUCT_GET_U = 0x04, /* struct.get_u */ + WASM_OP_STRUCT_SET = 0x05, /* struct.set */ + + WASM_OP_ARRAY_NEW = 0x06, /* array.new */ + WASM_OP_ARRAY_NEW_DEFAULT = 0x07, /* array.new_default */ + WASM_OP_ARRAY_NEW_FIXED = 0x08, /* array.new_fixed */ + WASM_OP_ARRAY_NEW_DATA = 0x09, /* array.new_data */ + WASM_OP_ARRAY_NEW_ELEM = 0x0A, /* array.new_elem */ + WASM_OP_ARRAY_GET = 0x0B, /* array.get */ + WASM_OP_ARRAY_GET_S = 0x0C, /* array.get_s */ + WASM_OP_ARRAY_GET_U = 0x0D, /* array.get_u */ + WASM_OP_ARRAY_SET = 0x0E, /* array.set */ + WASM_OP_ARRAY_LEN = 0x0F, /* array.len */ + WASM_OP_ARRAY_FILL = 0x10, /* array.fill */ + WASM_OP_ARRAY_COPY = 0x11, /* array.copy */ + WASM_OP_ARRAY_INIT_DATA = 0x12, + /* array.init_data */ /* TODO */ + WASM_OP_ARRAY_INIT_ELEM = 0x13, + /* array.init_elem */ /* TODO */ + + WASM_OP_REF_TEST = 0x14, /* ref.test */ + WASM_OP_REF_TEST_NULLABLE = 0x15, /* ref.test_nullable */ + WASM_OP_REF_CAST = 0x16, /* ref.cast */ + WASM_OP_REF_CAST_NULLABLE = 0x17, /* ref.cast_nullable */ + + WASM_OP_BR_ON_CAST = 0x18, /* br_on_cast */ + WASM_OP_BR_ON_CAST_FAIL = 0x19, /* br_on_cast_fail */ + + WASM_OP_ANY_CONVERT_EXTERN = 0x1A, /* any.convert_extern */ + WASM_OP_EXTERN_CONVERT_ANY = 0x1B, /* extern.covert_any */ + + WASM_OP_REF_I31 = 0x1C, /* ref.i31 */ + WASM_OP_I31_GET_S = 0x1D, /* i31.get_s */ + WASM_OP_I31_GET_U = 0x1E, /* i31.get_u */ + + /* stringref related opcodes */ + WASM_OP_STRING_NEW_UTF8 = 0x80, /* string.new_utf8 */ + WASM_OP_STRING_NEW_WTF16 = 0x81, /* string.new_wtf16 */ + WASM_OP_STRING_CONST = 0x82, /* string.const */ + WASM_OP_STRING_MEASURE_UTF8 = 0x83, /* string.measure_utf8 */ + WASM_OP_STRING_MEASURE_WTF8 = 0x84, /* string.measure_wtf8 */ + WASM_OP_STRING_MEASURE_WTF16 = 0x85, /* string.measure_wtf16 */ + WASM_OP_STRING_ENCODE_UTF8 = 0x86, /* string.encode_utf8 */ + WASM_OP_STRING_ENCODE_WTF16 = 0x87, /* string.encode_wtf16 */ + WASM_OP_STRING_CONCAT = 0x88, /* string.concat */ + WASM_OP_STRING_EQ = 0x89, /* string.eq */ + WASM_OP_STRING_IS_USV_SEQUENCE = 0x8a, /* string.is_usv_sequence */ + WASM_OP_STRING_NEW_LOSSY_UTF8 = 0x8b, /* string.new_lossy_utf8 */ + WASM_OP_STRING_NEW_WTF8 = 0x8c, /* string.new_wtf8 */ + WASM_OP_STRING_ENCODE_LOSSY_UTF8 = 0x8d, /* string.encode_lossy_utf8 */ + WASM_OP_STRING_ENCODE_WTF8 = 0x8e, /* string.encode_wtf8 */ + + WASM_OP_STRING_AS_WTF8 = 0x90, /* string.as_wtf8 */ + WASM_OP_STRINGVIEW_WTF8_ADVANCE = 0x91, /* stringview_wtf8.advance */ + WASM_OP_STRINGVIEW_WTF8_ENCODE_UTF8 = + 0x92, /* stringview_wtf8.encode_utf8 */ + WASM_OP_STRINGVIEW_WTF8_SLICE = 0x93, /* stringview_wtf8.slice */ + WASM_OP_STRINGVIEW_WTF8_ENCODE_LOSSY_UTF8 = + 0x94, /* stringview_wtf8.encode_lossy_utf8 */ + WASM_OP_STRINGVIEW_WTF8_ENCODE_WTF8 = + 0x95, /* stringview_wtf8.encode_wtf8 */ + + WASM_OP_STRING_AS_WTF16 = 0x98, /* string.as_wtf16 */ + WASM_OP_STRINGVIEW_WTF16_LENGTH = 0x99, /* stringview_wtf16.length */ + WASM_OP_STRINGVIEW_WTF16_GET_CODEUNIT = + 0x9a, /* stringview_wtf16.get_codeunit */ + WASM_OP_STRINGVIEW_WTF16_ENCODE = 0x9b, /* stringview_wtf16.encode */ + WASM_OP_STRINGVIEW_WTF16_SLICE = 0x9c, /* stringview_wtf16.slice */ + + WASM_OP_STRING_AS_ITER = 0xa0, /* string.as_iter */ + WASM_OP_STRINGVIEW_ITER_NEXT = 0xa1, /* stringview_iter.next */ + WASM_OP_STRINGVIEW_ITER_ADVANCE = 0xa2, /* stringview_iter.advance */ + WASM_OP_STRINGVIEW_ITER_REWIND = 0xa3, /* stringview_iter.rewind */ + WASM_OP_STRINGVIEW_ITER_SLICE = 0xa4, /* stringview_iter.slice */ + + WASM_OP_STRING_NEW_UTF8_ARRAY = 0xb0, /* string.new_utf8_array */ + WASM_OP_STRING_NEW_WTF16_ARRAY = 0xb1, /* string.new_wtf16_array */ + WASM_OP_STRING_ENCODE_UTF8_ARRAY = 0xb2, /* string.encode_utf8_array */ + WASM_OP_STRING_ENCODE_WTF16_ARRAY = 0xb3, /* string.encode_wtf16_array */ + WASM_OP_STRING_NEW_LOSSY_UTF8_ARRAY = + 0xb4, /* string.new_lossy_utf8_array */ + WASM_OP_STRING_NEW_WTF8_ARRAY = 0xb5, /* string.new_wtf8_array */ + WASM_OP_STRING_ENCODE_LOSSY_UTF8_ARRAY = + 0xb6, /* string.encode_lossy_utf8_array */ + WASM_OP_STRING_ENCODE_WTF8_ARRAY = 0xb7, /* string.encode_wtf8_array */ +} WASMGCEXTOpcode; + +typedef enum WASMMiscEXTOpcode { + WASM_OP_I32_TRUNC_SAT_S_F32 = 0x00, + WASM_OP_I32_TRUNC_SAT_U_F32 = 0x01, + WASM_OP_I32_TRUNC_SAT_S_F64 = 0x02, + WASM_OP_I32_TRUNC_SAT_U_F64 = 0x03, + WASM_OP_I64_TRUNC_SAT_S_F32 = 0x04, + WASM_OP_I64_TRUNC_SAT_U_F32 = 0x05, + WASM_OP_I64_TRUNC_SAT_S_F64 = 0x06, + WASM_OP_I64_TRUNC_SAT_U_F64 = 0x07, + WASM_OP_MEMORY_INIT = 0x08, + WASM_OP_DATA_DROP = 0x09, + WASM_OP_MEMORY_COPY = 0x0a, + WASM_OP_MEMORY_FILL = 0x0b, + WASM_OP_TABLE_INIT = 0x0c, + WASM_OP_ELEM_DROP = 0x0d, + WASM_OP_TABLE_COPY = 0x0e, + WASM_OP_TABLE_GROW = 0x0f, + WASM_OP_TABLE_SIZE = 0x10, + WASM_OP_TABLE_FILL = 0x11, +} WASMMiscEXTOpcode; + +typedef enum WASMSimdEXTOpcode { + /* memory instruction */ + SIMD_v128_load = 0x00, + SIMD_v128_load8x8_s = 0x01, + SIMD_v128_load8x8_u = 0x02, + SIMD_v128_load16x4_s = 0x03, + SIMD_v128_load16x4_u = 0x04, + SIMD_v128_load32x2_s = 0x05, + SIMD_v128_load32x2_u = 0x06, + SIMD_v128_load8_splat = 0x07, + SIMD_v128_load16_splat = 0x08, + SIMD_v128_load32_splat = 0x09, + SIMD_v128_load64_splat = 0x0a, + SIMD_v128_store = 0x0b, + + /* basic operation */ + SIMD_v128_const = 0x0c, + SIMD_v8x16_shuffle = 0x0d, + SIMD_v8x16_swizzle = 0x0e, + + /* splat operation */ + SIMD_i8x16_splat = 0x0f, + SIMD_i16x8_splat = 0x10, + SIMD_i32x4_splat = 0x11, + SIMD_i64x2_splat = 0x12, + SIMD_f32x4_splat = 0x13, + SIMD_f64x2_splat = 0x14, + + /* lane operation */ + SIMD_i8x16_extract_lane_s = 0x15, + SIMD_i8x16_extract_lane_u = 0x16, + SIMD_i8x16_replace_lane = 0x17, + SIMD_i16x8_extract_lane_s = 0x18, + SIMD_i16x8_extract_lane_u = 0x19, + SIMD_i16x8_replace_lane = 0x1a, + SIMD_i32x4_extract_lane = 0x1b, + SIMD_i32x4_replace_lane = 0x1c, + SIMD_i64x2_extract_lane = 0x1d, + SIMD_i64x2_replace_lane = 0x1e, + SIMD_f32x4_extract_lane = 0x1f, + SIMD_f32x4_replace_lane = 0x20, + SIMD_f64x2_extract_lane = 0x21, + SIMD_f64x2_replace_lane = 0x22, + + /* i8x16 compare operation */ + SIMD_i8x16_eq = 0x23, + SIMD_i8x16_ne = 0x24, + SIMD_i8x16_lt_s = 0x25, + SIMD_i8x16_lt_u = 0x26, + SIMD_i8x16_gt_s = 0x27, + SIMD_i8x16_gt_u = 0x28, + SIMD_i8x16_le_s = 0x29, + SIMD_i8x16_le_u = 0x2a, + SIMD_i8x16_ge_s = 0x2b, + SIMD_i8x16_ge_u = 0x2c, + + /* i16x8 compare operation */ + SIMD_i16x8_eq = 0x2d, + SIMD_i16x8_ne = 0x2e, + SIMD_i16x8_lt_s = 0x2f, + SIMD_i16x8_lt_u = 0x30, + SIMD_i16x8_gt_s = 0x31, + SIMD_i16x8_gt_u = 0x32, + SIMD_i16x8_le_s = 0x33, + SIMD_i16x8_le_u = 0x34, + SIMD_i16x8_ge_s = 0x35, + SIMD_i16x8_ge_u = 0x36, + + /* i32x4 compare operation */ + SIMD_i32x4_eq = 0x37, + SIMD_i32x4_ne = 0x38, + SIMD_i32x4_lt_s = 0x39, + SIMD_i32x4_lt_u = 0x3a, + SIMD_i32x4_gt_s = 0x3b, + SIMD_i32x4_gt_u = 0x3c, + SIMD_i32x4_le_s = 0x3d, + SIMD_i32x4_le_u = 0x3e, + SIMD_i32x4_ge_s = 0x3f, + SIMD_i32x4_ge_u = 0x40, + + /* f32x4 compare operation */ + SIMD_f32x4_eq = 0x41, + SIMD_f32x4_ne = 0x42, + SIMD_f32x4_lt = 0x43, + SIMD_f32x4_gt = 0x44, + SIMD_f32x4_le = 0x45, + SIMD_f32x4_ge = 0x46, + + /* f64x2 compare operation */ + SIMD_f64x2_eq = 0x47, + SIMD_f64x2_ne = 0x48, + SIMD_f64x2_lt = 0x49, + SIMD_f64x2_gt = 0x4a, + SIMD_f64x2_le = 0x4b, + SIMD_f64x2_ge = 0x4c, + + /* v128 operation */ + SIMD_v128_not = 0x4d, + SIMD_v128_and = 0x4e, + SIMD_v128_andnot = 0x4f, + SIMD_v128_or = 0x50, + SIMD_v128_xor = 0x51, + SIMD_v128_bitselect = 0x52, + SIMD_v128_any_true = 0x53, + + /* Load Lane Operation */ + SIMD_v128_load8_lane = 0x54, + SIMD_v128_load16_lane = 0x55, + SIMD_v128_load32_lane = 0x56, + SIMD_v128_load64_lane = 0x57, + SIMD_v128_store8_lane = 0x58, + SIMD_v128_store16_lane = 0x59, + SIMD_v128_store32_lane = 0x5a, + SIMD_v128_store64_lane = 0x5b, + SIMD_v128_load32_zero = 0x5c, + SIMD_v128_load64_zero = 0x5d, + + /* Float conversion */ + SIMD_f32x4_demote_f64x2_zero = 0x5e, + SIMD_f64x2_promote_low_f32x4_zero = 0x5f, + + /* i8x16 Operation */ + SIMD_i8x16_abs = 0x60, + SIMD_i8x16_neg = 0x61, + SIMD_i8x16_popcnt = 0x62, + SIMD_i8x16_all_true = 0x63, + SIMD_i8x16_bitmask = 0x64, + SIMD_i8x16_narrow_i16x8_s = 0x65, + SIMD_i8x16_narrow_i16x8_u = 0x66, + SIMD_f32x4_ceil = 0x67, + SIMD_f32x4_floor = 0x68, + SIMD_f32x4_trunc = 0x69, + SIMD_f32x4_nearest = 0x6a, + SIMD_i8x16_shl = 0x6b, + SIMD_i8x16_shr_s = 0x6c, + SIMD_i8x16_shr_u = 0x6d, + SIMD_i8x16_add = 0x6e, + SIMD_i8x16_add_sat_s = 0x6f, + SIMD_i8x16_add_sat_u = 0x70, + SIMD_i8x16_sub = 0x71, + SIMD_i8x16_sub_sat_s = 0x72, + SIMD_i8x16_sub_sat_u = 0x73, + SIMD_f64x2_ceil = 0x74, + SIMD_f64x2_floor = 0x75, + SIMD_i8x16_min_s = 0x76, + SIMD_i8x16_min_u = 0x77, + SIMD_i8x16_max_s = 0x78, + SIMD_i8x16_max_u = 0x79, + SIMD_f64x2_trunc = 0x7a, + SIMD_i8x16_avgr_u = 0x7b, + SIMD_i16x8_extadd_pairwise_i8x16_s = 0x7c, + SIMD_i16x8_extadd_pairwise_i8x16_u = 0x7d, + SIMD_i32x4_extadd_pairwise_i16x8_s = 0x7e, + SIMD_i32x4_extadd_pairwise_i16x8_u = 0x7f, + + /* i16x8 operation */ + SIMD_i16x8_abs = 0x80, + SIMD_i16x8_neg = 0x81, + SIMD_i16x8_q15mulr_sat_s = 0x82, + SIMD_i16x8_all_true = 0x83, + SIMD_i16x8_bitmask = 0x84, + SIMD_i16x8_narrow_i32x4_s = 0x85, + SIMD_i16x8_narrow_i32x4_u = 0x86, + SIMD_i16x8_extend_low_i8x16_s = 0x87, + SIMD_i16x8_extend_high_i8x16_s = 0x88, + SIMD_i16x8_extend_low_i8x16_u = 0x89, + SIMD_i16x8_extend_high_i8x16_u = 0x8a, + SIMD_i16x8_shl = 0x8b, + SIMD_i16x8_shr_s = 0x8c, + SIMD_i16x8_shr_u = 0x8d, + SIMD_i16x8_add = 0x8e, + SIMD_i16x8_add_sat_s = 0x8f, + SIMD_i16x8_add_sat_u = 0x90, + SIMD_i16x8_sub = 0x91, + SIMD_i16x8_sub_sat_s = 0x92, + SIMD_i16x8_sub_sat_u = 0x93, + SIMD_f64x2_nearest = 0x94, + SIMD_i16x8_mul = 0x95, + SIMD_i16x8_min_s = 0x96, + SIMD_i16x8_min_u = 0x97, + SIMD_i16x8_max_s = 0x98, + SIMD_i16x8_max_u = 0x99, + /* placeholder = 0x9a */ + SIMD_i16x8_avgr_u = 0x9b, + SIMD_i16x8_extmul_low_i8x16_s = 0x9c, + SIMD_i16x8_extmul_high_i8x16_s = 0x9d, + SIMD_i16x8_extmul_low_i8x16_u = 0x9e, + SIMD_i16x8_extmul_high_i8x16_u = 0x9f, + + /* i32x4 operation */ + SIMD_i32x4_abs = 0xa0, + SIMD_i32x4_neg = 0xa1, + /* placeholder = 0xa2 */ + SIMD_i32x4_all_true = 0xa3, + SIMD_i32x4_bitmask = 0xa4, + /* placeholder = 0xa5 */ + /* placeholder = 0xa6 */ + SIMD_i32x4_extend_low_i16x8_s = 0xa7, + SIMD_i32x4_extend_high_i16x8_s = 0xa8, + SIMD_i32x4_extend_low_i16x8_u = 0xa9, + SIMD_i32x4_extend_high_i16x8_u = 0xaa, + SIMD_i32x4_shl = 0xab, + SIMD_i32x4_shr_s = 0xac, + SIMD_i32x4_shr_u = 0xad, + SIMD_i32x4_add = 0xae, + /* placeholder = 0xaf */ + /* placeholder = 0xb0 */ + SIMD_i32x4_sub = 0xb1, + /* placeholder = 0xb2 */ + /* placeholder = 0xb3 */ + /* placeholder = 0xb4 */ + SIMD_i32x4_mul = 0xb5, + SIMD_i32x4_min_s = 0xb6, + SIMD_i32x4_min_u = 0xb7, + SIMD_i32x4_max_s = 0xb8, + SIMD_i32x4_max_u = 0xb9, + SIMD_i32x4_dot_i16x8_s = 0xba, + /* placeholder = 0xbb */ + SIMD_i32x4_extmul_low_i16x8_s = 0xbc, + SIMD_i32x4_extmul_high_i16x8_s = 0xbd, + SIMD_i32x4_extmul_low_i16x8_u = 0xbe, + SIMD_i32x4_extmul_high_i16x8_u = 0xbf, + + /* i64x2 operation */ + SIMD_i64x2_abs = 0xc0, + SIMD_i64x2_neg = 0xc1, + /* placeholder = 0xc2 */ + SIMD_i64x2_all_true = 0xc3, + SIMD_i64x2_bitmask = 0xc4, + /* placeholder = 0xc5 */ + /* placeholder = 0xc6 */ + SIMD_i64x2_extend_low_i32x4_s = 0xc7, + SIMD_i64x2_extend_high_i32x4_s = 0xc8, + SIMD_i64x2_extend_low_i32x4_u = 0xc9, + SIMD_i64x2_extend_high_i32x4_u = 0xca, + SIMD_i64x2_shl = 0xcb, + SIMD_i64x2_shr_s = 0xcc, + SIMD_i64x2_shr_u = 0xcd, + SIMD_i64x2_add = 0xce, + /* placeholder = 0xcf */ + /* placeholder = 0xd0 */ + SIMD_i64x2_sub = 0xd1, + /* placeholder = 0xd2 */ + /* placeholder = 0xd3 */ + /* placeholder = 0xd4 */ + SIMD_i64x2_mul = 0xd5, + SIMD_i64x2_eq = 0xd6, + SIMD_i64x2_ne = 0xd7, + SIMD_i64x2_lt_s = 0xd8, + SIMD_i64x2_gt_s = 0xd9, + SIMD_i64x2_le_s = 0xda, + SIMD_i64x2_ge_s = 0xdb, + SIMD_i64x2_extmul_low_i32x4_s = 0xdc, + SIMD_i64x2_extmul_high_i32x4_s = 0xdd, + SIMD_i64x2_extmul_low_i32x4_u = 0xde, + SIMD_i64x2_extmul_high_i32x4_u = 0xdf, + + /* f32x4 operation */ + SIMD_f32x4_abs = 0xe0, + SIMD_f32x4_neg = 0xe1, + /* placeholder = 0xe2 */ + SIMD_f32x4_sqrt = 0xe3, + SIMD_f32x4_add = 0xe4, + SIMD_f32x4_sub = 0xe5, + SIMD_f32x4_mul = 0xe6, + SIMD_f32x4_div = 0xe7, + SIMD_f32x4_min = 0xe8, + SIMD_f32x4_max = 0xe9, + SIMD_f32x4_pmin = 0xea, + SIMD_f32x4_pmax = 0xeb, + + /* f64x2 operation */ + SIMD_f64x2_abs = 0xec, + SIMD_f64x2_neg = 0xed, + /* placeholder = 0xee */ + SIMD_f64x2_sqrt = 0xef, + SIMD_f64x2_add = 0xf0, + SIMD_f64x2_sub = 0xf1, + SIMD_f64x2_mul = 0xf2, + SIMD_f64x2_div = 0xf3, + SIMD_f64x2_min = 0xf4, + SIMD_f64x2_max = 0xf5, + SIMD_f64x2_pmin = 0xf6, + SIMD_f64x2_pmax = 0xf7, + + /* conversion operation */ + SIMD_i32x4_trunc_sat_f32x4_s = 0xf8, + SIMD_i32x4_trunc_sat_f32x4_u = 0xf9, + SIMD_f32x4_convert_i32x4_s = 0xfa, + SIMD_f32x4_convert_i32x4_u = 0xfb, + SIMD_i32x4_trunc_sat_f64x2_s_zero = 0xfc, + SIMD_i32x4_trunc_sat_f64x2_u_zero = 0xfd, + SIMD_f64x2_convert_low_i32x4_s = 0xfe, + SIMD_f64x2_convert_low_i32x4_u = 0xff, +} WASMSimdEXTOpcode; + +typedef enum WASMAtomicEXTOpcode { + /* atomic wait and notify */ + WASM_OP_ATOMIC_NOTIFY = 0x00, + WASM_OP_ATOMIC_WAIT32 = 0x01, + WASM_OP_ATOMIC_WAIT64 = 0x02, + WASM_OP_ATOMIC_FENCE = 0x03, + /* atomic load and store */ + WASM_OP_ATOMIC_I32_LOAD = 0x10, + WASM_OP_ATOMIC_I64_LOAD = 0x11, + WASM_OP_ATOMIC_I32_LOAD8_U = 0x12, + WASM_OP_ATOMIC_I32_LOAD16_U = 0x13, + WASM_OP_ATOMIC_I64_LOAD8_U = 0x14, + WASM_OP_ATOMIC_I64_LOAD16_U = 0x15, + WASM_OP_ATOMIC_I64_LOAD32_U = 0x16, + WASM_OP_ATOMIC_I32_STORE = 0x17, + WASM_OP_ATOMIC_I64_STORE = 0x18, + WASM_OP_ATOMIC_I32_STORE8 = 0x19, + WASM_OP_ATOMIC_I32_STORE16 = 0x1a, + WASM_OP_ATOMIC_I64_STORE8 = 0x1b, + WASM_OP_ATOMIC_I64_STORE16 = 0x1c, + WASM_OP_ATOMIC_I64_STORE32 = 0x1d, + /* atomic add */ + WASM_OP_ATOMIC_RMW_I32_ADD = 0x1e, + WASM_OP_ATOMIC_RMW_I64_ADD = 0x1f, + WASM_OP_ATOMIC_RMW_I32_ADD8_U = 0x20, + WASM_OP_ATOMIC_RMW_I32_ADD16_U = 0x21, + WASM_OP_ATOMIC_RMW_I64_ADD8_U = 0x22, + WASM_OP_ATOMIC_RMW_I64_ADD16_U = 0x23, + WASM_OP_ATOMIC_RMW_I64_ADD32_U = 0x24, + /* atomic sub */ + WASM_OP_ATOMIC_RMW_I32_SUB = 0x25, + WASM_OP_ATOMIC_RMW_I64_SUB = 0x26, + WASM_OP_ATOMIC_RMW_I32_SUB8_U = 0x27, + WASM_OP_ATOMIC_RMW_I32_SUB16_U = 0x28, + WASM_OP_ATOMIC_RMW_I64_SUB8_U = 0x29, + WASM_OP_ATOMIC_RMW_I64_SUB16_U = 0x2a, + WASM_OP_ATOMIC_RMW_I64_SUB32_U = 0x2b, + /* atomic and */ + WASM_OP_ATOMIC_RMW_I32_AND = 0x2c, + WASM_OP_ATOMIC_RMW_I64_AND = 0x2d, + WASM_OP_ATOMIC_RMW_I32_AND8_U = 0x2e, + WASM_OP_ATOMIC_RMW_I32_AND16_U = 0x2f, + WASM_OP_ATOMIC_RMW_I64_AND8_U = 0x30, + WASM_OP_ATOMIC_RMW_I64_AND16_U = 0x31, + WASM_OP_ATOMIC_RMW_I64_AND32_U = 0x32, + /* atomic or */ + WASM_OP_ATOMIC_RMW_I32_OR = 0x33, + WASM_OP_ATOMIC_RMW_I64_OR = 0x34, + WASM_OP_ATOMIC_RMW_I32_OR8_U = 0x35, + WASM_OP_ATOMIC_RMW_I32_OR16_U = 0x36, + WASM_OP_ATOMIC_RMW_I64_OR8_U = 0x37, + WASM_OP_ATOMIC_RMW_I64_OR16_U = 0x38, + WASM_OP_ATOMIC_RMW_I64_OR32_U = 0x39, + /* atomic xor */ + WASM_OP_ATOMIC_RMW_I32_XOR = 0x3a, + WASM_OP_ATOMIC_RMW_I64_XOR = 0x3b, + WASM_OP_ATOMIC_RMW_I32_XOR8_U = 0x3c, + WASM_OP_ATOMIC_RMW_I32_XOR16_U = 0x3d, + WASM_OP_ATOMIC_RMW_I64_XOR8_U = 0x3e, + WASM_OP_ATOMIC_RMW_I64_XOR16_U = 0x3f, + WASM_OP_ATOMIC_RMW_I64_XOR32_U = 0x40, + /* atomic xchg */ + WASM_OP_ATOMIC_RMW_I32_XCHG = 0x41, + WASM_OP_ATOMIC_RMW_I64_XCHG = 0x42, + WASM_OP_ATOMIC_RMW_I32_XCHG8_U = 0x43, + WASM_OP_ATOMIC_RMW_I32_XCHG16_U = 0x44, + WASM_OP_ATOMIC_RMW_I64_XCHG8_U = 0x45, + WASM_OP_ATOMIC_RMW_I64_XCHG16_U = 0x46, + WASM_OP_ATOMIC_RMW_I64_XCHG32_U = 0x47, + /* atomic cmpxchg */ + WASM_OP_ATOMIC_RMW_I32_CMPXCHG = 0x48, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG = 0x49, + WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U = 0x4a, + WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U = 0x4b, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U = 0x4c, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U = 0x4d, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U = 0x4e, +} WASMAtomicEXTOpcode; + +#if WASM_ENABLE_DEBUG_INTERP != 0 +#define DEF_DEBUG_BREAK_HANDLE() \ + [DEBUG_OP_BREAK] = HANDLE_OPCODE(DEBUG_OP_BREAK), /* 0xdb */ +#else +#define DEF_DEBUG_BREAK_HANDLE() #endif +#define SET_GOTO_TABLE_ELEM(opcode) [opcode] = HANDLE_OPCODE(opcode) +#if WASM_ENABLE_SIMDE != 0 +#define DEF_EXT_V128_HANDLE() \ + SET_GOTO_TABLE_ELEM(EXT_OP_SET_LOCAL_FAST_V128), /* 0xdd */ \ + SET_GOTO_TABLE_ELEM(EXT_OP_TEE_LOCAL_FAST_V128), /* 0xde */ \ + SET_GOTO_TABLE_ELEM(EXT_OP_COPY_STACK_TOP_V128), /* 0xdf */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_GET_GLOBAL_V128), /* 0xe0 */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_SET_GLOBAL_V128), /* 0xe1 */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_SELECT_128), /* 0xe2 */ + +#else +#define DEF_EXT_V128_HANDLE() +#endif /* * Macro used to generate computed goto tables for the C interpreter. */ #define WASM_INSTRUCTION_NUM 256 -#define DEFINE_GOTO_TABLE(_name) \ -static const void *_name[WASM_INSTRUCTION_NUM] = { \ - HANDLE_OPCODE (WASM_OP_UNREACHABLE), /* 0x00 */ \ - HANDLE_OPCODE (WASM_OP_NOP), /* 0x01 */ \ - HANDLE_OPCODE (WASM_OP_BLOCK), /* 0x02 */ \ - HANDLE_OPCODE (WASM_OP_LOOP), /* 0x03 */ \ - HANDLE_OPCODE (WASM_OP_IF), /* 0x04 */ \ - HANDLE_OPCODE (WASM_OP_ELSE), /* 0x05 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x06), /* 0x06 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x07), /* 0x07 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x08), /* 0x08 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x09), /* 0x09 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x0a), /* 0x0a */ \ - HANDLE_OPCODE (WASM_OP_END), /* 0x0b */ \ - HANDLE_OPCODE (WASM_OP_BR), /* 0x0c */ \ - HANDLE_OPCODE (WASM_OP_BR_IF), /* 0x0d */ \ - HANDLE_OPCODE (WASM_OP_BR_TABLE), /* 0x0e */ \ - HANDLE_OPCODE (WASM_OP_RETURN), /* 0x0f */ \ - HANDLE_OPCODE (WASM_OP_CALL), /* 0x10 */ \ - HANDLE_OPCODE (WASM_OP_CALL_INDIRECT), /* 0x11 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x12), /* 0x12 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x13), /* 0x13 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x14), /* 0x14 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x15), /* 0x15 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x16), /* 0x16 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x17), /* 0x17 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x18), /* 0x18 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x19), /* 0x19 */ \ - HANDLE_OPCODE (WASM_OP_DROP), /* 0x1a */ \ - HANDLE_OPCODE (WASM_OP_SELECT), /* 0x1b */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1c), /* 0x1c */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1d), /* 0x1d */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1e), /* 0x1e */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1f), /* 0x1f */ \ - HANDLE_OPCODE (WASM_OP_GET_LOCAL), /* 0x20 */ \ - HANDLE_OPCODE (WASM_OP_SET_LOCAL), /* 0x21 */ \ - HANDLE_OPCODE (WASM_OP_TEE_LOCAL), /* 0x22 */ \ - HANDLE_OPCODE (WASM_OP_GET_GLOBAL), /* 0x23 */ \ - HANDLE_OPCODE (WASM_OP_SET_GLOBAL), /* 0x24 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x25), /* 0x25 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x26), /* 0x26 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x27), /* 0x27 */ \ - HANDLE_OPCODE (WASM_OP_I32_LOAD), /* 0x28 */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD), /* 0x29 */ \ - HANDLE_OPCODE (WASM_OP_F32_LOAD), /* 0x2a */ \ - HANDLE_OPCODE (WASM_OP_F64_LOAD), /* 0x2b */ \ - HANDLE_OPCODE (WASM_OP_I32_LOAD8_S), /* 0x2c */ \ - HANDLE_OPCODE (WASM_OP_I32_LOAD8_U), /* 0x2d */ \ - HANDLE_OPCODE (WASM_OP_I32_LOAD16_S), /* 0x2e */ \ - HANDLE_OPCODE (WASM_OP_I32_LOAD16_U), /* 0x2f */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD8_S), /* 0x30 */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD8_U), /* 0x31 */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD16_S), /* 0x32 */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD16_U), /* 0x33 */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD32_S), /* 0x34 */ \ - HANDLE_OPCODE (WASM_OP_I64_LOAD32_U), /* 0x35 */ \ - HANDLE_OPCODE (WASM_OP_I32_STORE), /* 0x36 */ \ - HANDLE_OPCODE (WASM_OP_I64_STORE), /* 0x37 */ \ - HANDLE_OPCODE (WASM_OP_F32_STORE), /* 0x38 */ \ - HANDLE_OPCODE (WASM_OP_F64_STORE), /* 0x39 */ \ - HANDLE_OPCODE (WASM_OP_I32_STORE8), /* 0x3a */ \ - HANDLE_OPCODE (WASM_OP_I32_STORE16), /* 0x3b */ \ - HANDLE_OPCODE (WASM_OP_I64_STORE8), /* 0x3c */ \ - HANDLE_OPCODE (WASM_OP_I64_STORE16), /* 0x3d */ \ - HANDLE_OPCODE (WASM_OP_I64_STORE32), /* 0x3e */ \ - HANDLE_OPCODE (WASM_OP_MEMORY_SIZE), /* 0x3f */ \ - HANDLE_OPCODE (WASM_OP_MEMORY_GROW), /* 0x40 */ \ - HANDLE_OPCODE (WASM_OP_I32_CONST), /* 0x41 */ \ - HANDLE_OPCODE (WASM_OP_I64_CONST), /* 0x42 */ \ - HANDLE_OPCODE (WASM_OP_F32_CONST), /* 0x43 */ \ - HANDLE_OPCODE (WASM_OP_F64_CONST), /* 0x44 */ \ - HANDLE_OPCODE (WASM_OP_I32_EQZ), /* 0x45 */ \ - HANDLE_OPCODE (WASM_OP_I32_EQ), /* 0x46 */ \ - HANDLE_OPCODE (WASM_OP_I32_NE), /* 0x47 */ \ - HANDLE_OPCODE (WASM_OP_I32_LT_S), /* 0x48 */ \ - HANDLE_OPCODE (WASM_OP_I32_LT_U), /* 0x49 */ \ - HANDLE_OPCODE (WASM_OP_I32_GT_S), /* 0x4a */ \ - HANDLE_OPCODE (WASM_OP_I32_GT_U), /* 0x4b */ \ - HANDLE_OPCODE (WASM_OP_I32_LE_S), /* 0x4c */ \ - HANDLE_OPCODE (WASM_OP_I32_LE_U), /* 0x4d */ \ - HANDLE_OPCODE (WASM_OP_I32_GE_S), /* 0x4e */ \ - HANDLE_OPCODE (WASM_OP_I32_GE_U), /* 0x4f */ \ - HANDLE_OPCODE (WASM_OP_I64_EQZ), /* 0x50 */ \ - HANDLE_OPCODE (WASM_OP_I64_EQ), /* 0x51 */ \ - HANDLE_OPCODE (WASM_OP_I64_NE), /* 0x52 */ \ - HANDLE_OPCODE (WASM_OP_I64_LT_S), /* 0x53 */ \ - HANDLE_OPCODE (WASM_OP_I64_LT_U), /* 0x54 */ \ - HANDLE_OPCODE (WASM_OP_I64_GT_S), /* 0x55 */ \ - HANDLE_OPCODE (WASM_OP_I64_GT_U), /* 0x56 */ \ - HANDLE_OPCODE (WASM_OP_I64_LE_S), /* 0x57 */ \ - HANDLE_OPCODE (WASM_OP_I64_LE_U), /* 0x58 */ \ - HANDLE_OPCODE (WASM_OP_I64_GE_S), /* 0x59 */ \ - HANDLE_OPCODE (WASM_OP_I64_GE_U), /* 0x5a */ \ - HANDLE_OPCODE (WASM_OP_F32_EQ), /* 0x5b */ \ - HANDLE_OPCODE (WASM_OP_F32_NE), /* 0x5c */ \ - HANDLE_OPCODE (WASM_OP_F32_LT), /* 0x5d */ \ - HANDLE_OPCODE (WASM_OP_F32_GT), /* 0x5e */ \ - HANDLE_OPCODE (WASM_OP_F32_LE), /* 0x5f */ \ - HANDLE_OPCODE (WASM_OP_F32_GE), /* 0x60 */ \ - HANDLE_OPCODE (WASM_OP_F64_EQ), /* 0x61 */ \ - HANDLE_OPCODE (WASM_OP_F64_NE), /* 0x62 */ \ - HANDLE_OPCODE (WASM_OP_F64_LT), /* 0x63 */ \ - HANDLE_OPCODE (WASM_OP_F64_GT), /* 0x64 */ \ - HANDLE_OPCODE (WASM_OP_F64_LE), /* 0x65 */ \ - HANDLE_OPCODE (WASM_OP_F64_GE), /* 0x66 */ \ - HANDLE_OPCODE (WASM_OP_I32_CLZ), /* 0x67 */ \ - HANDLE_OPCODE (WASM_OP_I32_CTZ), /* 0x68 */ \ - HANDLE_OPCODE (WASM_OP_I32_POPCNT), /* 0x69 */ \ - HANDLE_OPCODE (WASM_OP_I32_ADD), /* 0x6a */ \ - HANDLE_OPCODE (WASM_OP_I32_SUB), /* 0x6b */ \ - HANDLE_OPCODE (WASM_OP_I32_MUL), /* 0x6c */ \ - HANDLE_OPCODE (WASM_OP_I32_DIV_S), /* 0x6d */ \ - HANDLE_OPCODE (WASM_OP_I32_DIV_U), /* 0x6e */ \ - HANDLE_OPCODE (WASM_OP_I32_REM_S), /* 0x6f */ \ - HANDLE_OPCODE (WASM_OP_I32_REM_U), /* 0x70 */ \ - HANDLE_OPCODE (WASM_OP_I32_AND), /* 0x71 */ \ - HANDLE_OPCODE (WASM_OP_I32_OR), /* 0x72 */ \ - HANDLE_OPCODE (WASM_OP_I32_XOR), /* 0x73 */ \ - HANDLE_OPCODE (WASM_OP_I32_SHL), /* 0x74 */ \ - HANDLE_OPCODE (WASM_OP_I32_SHR_S), /* 0x75 */ \ - HANDLE_OPCODE (WASM_OP_I32_SHR_U), /* 0x76 */ \ - HANDLE_OPCODE (WASM_OP_I32_ROTL), /* 0x77 */ \ - HANDLE_OPCODE (WASM_OP_I32_ROTR), /* 0x78 */ \ - HANDLE_OPCODE (WASM_OP_I64_CLZ), /* 0x79 */ \ - HANDLE_OPCODE (WASM_OP_I64_CTZ), /* 0x7a */ \ - HANDLE_OPCODE (WASM_OP_I64_POPCNT), /* 0x7b */ \ - HANDLE_OPCODE (WASM_OP_I64_ADD), /* 0x7c */ \ - HANDLE_OPCODE (WASM_OP_I64_SUB), /* 0x7d */ \ - HANDLE_OPCODE (WASM_OP_I64_MUL), /* 0x7e */ \ - HANDLE_OPCODE (WASM_OP_I64_DIV_S), /* 0x7f */ \ - HANDLE_OPCODE (WASM_OP_I64_DIV_U), /* 0x80 */ \ - HANDLE_OPCODE (WASM_OP_I64_REM_S), /* 0x81 */ \ - HANDLE_OPCODE (WASM_OP_I64_REM_U), /* 0x82 */ \ - HANDLE_OPCODE (WASM_OP_I64_AND), /* 0x83 */ \ - HANDLE_OPCODE (WASM_OP_I64_OR), /* 0x84 */ \ - HANDLE_OPCODE (WASM_OP_I64_XOR), /* 0x85 */ \ - HANDLE_OPCODE (WASM_OP_I64_SHL), /* 0x86 */ \ - HANDLE_OPCODE (WASM_OP_I64_SHR_S), /* 0x87 */ \ - HANDLE_OPCODE (WASM_OP_I64_SHR_U), /* 0x88 */ \ - HANDLE_OPCODE (WASM_OP_I64_ROTL), /* 0x89 */ \ - HANDLE_OPCODE (WASM_OP_I64_ROTR), /* 0x8a */ \ - HANDLE_OPCODE (WASM_OP_F32_ABS), /* 0x8b */ \ - HANDLE_OPCODE (WASM_OP_F32_NEG), /* 0x8c */ \ - HANDLE_OPCODE (WASM_OP_F32_CEIL), /* 0x8d */ \ - HANDLE_OPCODE (WASM_OP_F32_FLOOR), /* 0x8e */ \ - HANDLE_OPCODE (WASM_OP_F32_TRUNC), /* 0x8f */ \ - HANDLE_OPCODE (WASM_OP_F32_NEAREST), /* 0x90 */ \ - HANDLE_OPCODE (WASM_OP_F32_SQRT), /* 0x91 */ \ - HANDLE_OPCODE (WASM_OP_F32_ADD), /* 0x92 */ \ - HANDLE_OPCODE (WASM_OP_F32_SUB), /* 0x93 */ \ - HANDLE_OPCODE (WASM_OP_F32_MUL), /* 0x94 */ \ - HANDLE_OPCODE (WASM_OP_F32_DIV), /* 0x95 */ \ - HANDLE_OPCODE (WASM_OP_F32_MIN), /* 0x96 */ \ - HANDLE_OPCODE (WASM_OP_F32_MAX), /* 0x97 */ \ - HANDLE_OPCODE (WASM_OP_F32_COPYSIGN), /* 0x98 */ \ - HANDLE_OPCODE (WASM_OP_F64_ABS), /* 0x99 */ \ - HANDLE_OPCODE (WASM_OP_F64_NEG), /* 0x9a */ \ - HANDLE_OPCODE (WASM_OP_F64_CEIL), /* 0x9b */ \ - HANDLE_OPCODE (WASM_OP_F64_FLOOR), /* 0x9c */ \ - HANDLE_OPCODE (WASM_OP_F64_TRUNC), /* 0x9d */ \ - HANDLE_OPCODE (WASM_OP_F64_NEAREST), /* 0x9e */ \ - HANDLE_OPCODE (WASM_OP_F64_SQRT), /* 0x9f */ \ - HANDLE_OPCODE (WASM_OP_F64_ADD), /* 0xa0 */ \ - HANDLE_OPCODE (WASM_OP_F64_SUB), /* 0xa1 */ \ - HANDLE_OPCODE (WASM_OP_F64_MUL), /* 0xa2 */ \ - HANDLE_OPCODE (WASM_OP_F64_DIV), /* 0xa3 */ \ - HANDLE_OPCODE (WASM_OP_F64_MIN), /* 0xa4 */ \ - HANDLE_OPCODE (WASM_OP_F64_MAX), /* 0xa5 */ \ - HANDLE_OPCODE (WASM_OP_F64_COPYSIGN), /* 0xa6 */ \ - HANDLE_OPCODE (WASM_OP_I32_WRAP_I64), /* 0xa7 */ \ - HANDLE_OPCODE (WASM_OP_I32_TRUNC_S_F32), /* 0xa8 */ \ - HANDLE_OPCODE (WASM_OP_I32_TRUNC_U_F32), /* 0xa9 */ \ - HANDLE_OPCODE (WASM_OP_I32_TRUNC_S_F64), /* 0xaa */ \ - HANDLE_OPCODE (WASM_OP_I32_TRUNC_U_F64), /* 0xab */ \ - HANDLE_OPCODE (WASM_OP_I64_EXTEND_S_I32), /* 0xac */ \ - HANDLE_OPCODE (WASM_OP_I64_EXTEND_U_I32), /* 0xad */ \ - HANDLE_OPCODE (WASM_OP_I64_TRUNC_S_F32), /* 0xae */ \ - HANDLE_OPCODE (WASM_OP_I64_TRUNC_U_F32), /* 0xaf */ \ - HANDLE_OPCODE (WASM_OP_I64_TRUNC_S_F64), /* 0xb0 */ \ - HANDLE_OPCODE (WASM_OP_I64_TRUNC_U_F64), /* 0xb1 */ \ - HANDLE_OPCODE (WASM_OP_F32_CONVERT_S_I32), /* 0xb2 */ \ - HANDLE_OPCODE (WASM_OP_F32_CONVERT_U_I32), /* 0xb3 */ \ - HANDLE_OPCODE (WASM_OP_F32_CONVERT_S_I64), /* 0xb4 */ \ - HANDLE_OPCODE (WASM_OP_F32_CONVERT_U_I64), /* 0xb5 */ \ - HANDLE_OPCODE (WASM_OP_F32_DEMOTE_F64), /* 0xb6 */ \ - HANDLE_OPCODE (WASM_OP_F64_CONVERT_S_I32), /* 0xb7 */ \ - HANDLE_OPCODE (WASM_OP_F64_CONVERT_U_I32), /* 0xb8 */ \ - HANDLE_OPCODE (WASM_OP_F64_CONVERT_S_I64), /* 0xb9 */ \ - HANDLE_OPCODE (WASM_OP_F64_CONVERT_U_I64), /* 0xba */ \ - HANDLE_OPCODE (WASM_OP_F64_PROMOTE_F32), /* 0xbb */ \ - HANDLE_OPCODE (WASM_OP_I32_REINTERPRET_F32), /* 0xbc */ \ - HANDLE_OPCODE (WASM_OP_I64_REINTERPRET_F64), /* 0xbd */ \ - HANDLE_OPCODE (WASM_OP_F32_REINTERPRET_I32), /* 0xbe */ \ - HANDLE_OPCODE (WASM_OP_F64_REINTERPRET_I64), /* 0xbf */ \ - HANDLE_OPCODE (WASM_OP_DROP_64), /* 0xc0 */ \ - HANDLE_OPCODE (WASM_OP_SELECT_64), /* 0xc1 */ \ - HANDLE_OPCODE (WASM_OP_GET_LOCAL_FAST),/* 0xc2 */ \ - HANDLE_OPCODE (WASM_OP_SET_LOCAL_FAST),/* 0xc3 */ \ - HANDLE_OPCODE (WASM_OP_TEE_LOCAL_FAST),/* 0xc4 */ \ - HANDLE_OPCODE (WASM_OP_IMPDEP), /* 0xc5 */ \ +#define DEFINE_GOTO_TABLE(type, _name) \ + static type _name[WASM_INSTRUCTION_NUM] = { \ + HANDLE_OPCODE(WASM_OP_UNREACHABLE), /* 0x00 */ \ + HANDLE_OPCODE(WASM_OP_NOP), /* 0x01 */ \ + HANDLE_OPCODE(WASM_OP_BLOCK), /* 0x02 */ \ + HANDLE_OPCODE(WASM_OP_LOOP), /* 0x03 */ \ + HANDLE_OPCODE(WASM_OP_IF), /* 0x04 */ \ + HANDLE_OPCODE(WASM_OP_ELSE), /* 0x05 */ \ + HANDLE_OPCODE(WASM_OP_TRY), /* 0x06 */ \ + HANDLE_OPCODE(WASM_OP_CATCH), /* 0x07 */ \ + HANDLE_OPCODE(WASM_OP_THROW), /* 0x08 */ \ + HANDLE_OPCODE(WASM_OP_RETHROW), /* 0x09 */ \ + HANDLE_OPCODE(WASM_OP_UNUSED_0x0a), /* 0x0a */ \ + HANDLE_OPCODE(WASM_OP_END), /* 0x0b */ \ + HANDLE_OPCODE(WASM_OP_BR), /* 0x0c */ \ + HANDLE_OPCODE(WASM_OP_BR_IF), /* 0x0d */ \ + HANDLE_OPCODE(WASM_OP_BR_TABLE), /* 0x0e */ \ + HANDLE_OPCODE(WASM_OP_RETURN), /* 0x0f */ \ + HANDLE_OPCODE(WASM_OP_CALL), /* 0x10 */ \ + HANDLE_OPCODE(WASM_OP_CALL_INDIRECT), /* 0x11 */ \ + HANDLE_OPCODE(WASM_OP_RETURN_CALL), /* 0x12 */ \ + HANDLE_OPCODE(WASM_OP_RETURN_CALL_INDIRECT), /* 0x13 */ \ + HANDLE_OPCODE(WASM_OP_CALL_REF), /* 0x14 */ \ + HANDLE_OPCODE(WASM_OP_RETURN_CALL_REF), /* 0x15 */ \ + HANDLE_OPCODE(WASM_OP_UNUSED_0x16), /* 0x16 */ \ + HANDLE_OPCODE(WASM_OP_UNUSED_0x17), /* 0x17 */ \ + HANDLE_OPCODE(WASM_OP_DELEGATE), /* 0x18 */ \ + HANDLE_OPCODE(WASM_OP_CATCH_ALL), /* 0x19 */ \ + HANDLE_OPCODE(WASM_OP_DROP), /* 0x1a */ \ + HANDLE_OPCODE(WASM_OP_SELECT), /* 0x1b */ \ + HANDLE_OPCODE(WASM_OP_SELECT_T), /* 0x1c */ \ + HANDLE_OPCODE(WASM_OP_GET_GLOBAL_64), /* 0x1d */ \ + HANDLE_OPCODE(WASM_OP_SET_GLOBAL_64), /* 0x1e */ \ + HANDLE_OPCODE(WASM_OP_SET_GLOBAL_AUX_STACK), /* 0x1f */ \ + HANDLE_OPCODE(WASM_OP_GET_LOCAL), /* 0x20 */ \ + HANDLE_OPCODE(WASM_OP_SET_LOCAL), /* 0x21 */ \ + HANDLE_OPCODE(WASM_OP_TEE_LOCAL), /* 0x22 */ \ + HANDLE_OPCODE(WASM_OP_GET_GLOBAL), /* 0x23 */ \ + HANDLE_OPCODE(WASM_OP_SET_GLOBAL), /* 0x24 */ \ + HANDLE_OPCODE(WASM_OP_TABLE_GET), /* 0x25 */ \ + HANDLE_OPCODE(WASM_OP_TABLE_SET), /* 0x26 */ \ + HANDLE_OPCODE(WASM_OP_UNUSED_0x27), /* 0x27 */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD), /* 0x28 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD), /* 0x29 */ \ + HANDLE_OPCODE(WASM_OP_F32_LOAD), /* 0x2a */ \ + HANDLE_OPCODE(WASM_OP_F64_LOAD), /* 0x2b */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD8_S), /* 0x2c */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD8_U), /* 0x2d */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD16_S), /* 0x2e */ \ + HANDLE_OPCODE(WASM_OP_I32_LOAD16_U), /* 0x2f */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD8_S), /* 0x30 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD8_U), /* 0x31 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD16_S), /* 0x32 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD16_U), /* 0x33 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD32_S), /* 0x34 */ \ + HANDLE_OPCODE(WASM_OP_I64_LOAD32_U), /* 0x35 */ \ + HANDLE_OPCODE(WASM_OP_I32_STORE), /* 0x36 */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE), /* 0x37 */ \ + HANDLE_OPCODE(WASM_OP_F32_STORE), /* 0x38 */ \ + HANDLE_OPCODE(WASM_OP_F64_STORE), /* 0x39 */ \ + HANDLE_OPCODE(WASM_OP_I32_STORE8), /* 0x3a */ \ + HANDLE_OPCODE(WASM_OP_I32_STORE16), /* 0x3b */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE8), /* 0x3c */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE16), /* 0x3d */ \ + HANDLE_OPCODE(WASM_OP_I64_STORE32), /* 0x3e */ \ + HANDLE_OPCODE(WASM_OP_MEMORY_SIZE), /* 0x3f */ \ + HANDLE_OPCODE(WASM_OP_MEMORY_GROW), /* 0x40 */ \ + HANDLE_OPCODE(WASM_OP_I32_CONST), /* 0x41 */ \ + HANDLE_OPCODE(WASM_OP_I64_CONST), /* 0x42 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONST), /* 0x43 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONST), /* 0x44 */ \ + HANDLE_OPCODE(WASM_OP_I32_EQZ), /* 0x45 */ \ + HANDLE_OPCODE(WASM_OP_I32_EQ), /* 0x46 */ \ + HANDLE_OPCODE(WASM_OP_I32_NE), /* 0x47 */ \ + HANDLE_OPCODE(WASM_OP_I32_LT_S), /* 0x48 */ \ + HANDLE_OPCODE(WASM_OP_I32_LT_U), /* 0x49 */ \ + HANDLE_OPCODE(WASM_OP_I32_GT_S), /* 0x4a */ \ + HANDLE_OPCODE(WASM_OP_I32_GT_U), /* 0x4b */ \ + HANDLE_OPCODE(WASM_OP_I32_LE_S), /* 0x4c */ \ + HANDLE_OPCODE(WASM_OP_I32_LE_U), /* 0x4d */ \ + HANDLE_OPCODE(WASM_OP_I32_GE_S), /* 0x4e */ \ + HANDLE_OPCODE(WASM_OP_I32_GE_U), /* 0x4f */ \ + HANDLE_OPCODE(WASM_OP_I64_EQZ), /* 0x50 */ \ + HANDLE_OPCODE(WASM_OP_I64_EQ), /* 0x51 */ \ + HANDLE_OPCODE(WASM_OP_I64_NE), /* 0x52 */ \ + HANDLE_OPCODE(WASM_OP_I64_LT_S), /* 0x53 */ \ + HANDLE_OPCODE(WASM_OP_I64_LT_U), /* 0x54 */ \ + HANDLE_OPCODE(WASM_OP_I64_GT_S), /* 0x55 */ \ + HANDLE_OPCODE(WASM_OP_I64_GT_U), /* 0x56 */ \ + HANDLE_OPCODE(WASM_OP_I64_LE_S), /* 0x57 */ \ + HANDLE_OPCODE(WASM_OP_I64_LE_U), /* 0x58 */ \ + HANDLE_OPCODE(WASM_OP_I64_GE_S), /* 0x59 */ \ + HANDLE_OPCODE(WASM_OP_I64_GE_U), /* 0x5a */ \ + HANDLE_OPCODE(WASM_OP_F32_EQ), /* 0x5b */ \ + HANDLE_OPCODE(WASM_OP_F32_NE), /* 0x5c */ \ + HANDLE_OPCODE(WASM_OP_F32_LT), /* 0x5d */ \ + HANDLE_OPCODE(WASM_OP_F32_GT), /* 0x5e */ \ + HANDLE_OPCODE(WASM_OP_F32_LE), /* 0x5f */ \ + HANDLE_OPCODE(WASM_OP_F32_GE), /* 0x60 */ \ + HANDLE_OPCODE(WASM_OP_F64_EQ), /* 0x61 */ \ + HANDLE_OPCODE(WASM_OP_F64_NE), /* 0x62 */ \ + HANDLE_OPCODE(WASM_OP_F64_LT), /* 0x63 */ \ + HANDLE_OPCODE(WASM_OP_F64_GT), /* 0x64 */ \ + HANDLE_OPCODE(WASM_OP_F64_LE), /* 0x65 */ \ + HANDLE_OPCODE(WASM_OP_F64_GE), /* 0x66 */ \ + HANDLE_OPCODE(WASM_OP_I32_CLZ), /* 0x67 */ \ + HANDLE_OPCODE(WASM_OP_I32_CTZ), /* 0x68 */ \ + HANDLE_OPCODE(WASM_OP_I32_POPCNT), /* 0x69 */ \ + HANDLE_OPCODE(WASM_OP_I32_ADD), /* 0x6a */ \ + HANDLE_OPCODE(WASM_OP_I32_SUB), /* 0x6b */ \ + HANDLE_OPCODE(WASM_OP_I32_MUL), /* 0x6c */ \ + HANDLE_OPCODE(WASM_OP_I32_DIV_S), /* 0x6d */ \ + HANDLE_OPCODE(WASM_OP_I32_DIV_U), /* 0x6e */ \ + HANDLE_OPCODE(WASM_OP_I32_REM_S), /* 0x6f */ \ + HANDLE_OPCODE(WASM_OP_I32_REM_U), /* 0x70 */ \ + HANDLE_OPCODE(WASM_OP_I32_AND), /* 0x71 */ \ + HANDLE_OPCODE(WASM_OP_I32_OR), /* 0x72 */ \ + HANDLE_OPCODE(WASM_OP_I32_XOR), /* 0x73 */ \ + HANDLE_OPCODE(WASM_OP_I32_SHL), /* 0x74 */ \ + HANDLE_OPCODE(WASM_OP_I32_SHR_S), /* 0x75 */ \ + HANDLE_OPCODE(WASM_OP_I32_SHR_U), /* 0x76 */ \ + HANDLE_OPCODE(WASM_OP_I32_ROTL), /* 0x77 */ \ + HANDLE_OPCODE(WASM_OP_I32_ROTR), /* 0x78 */ \ + HANDLE_OPCODE(WASM_OP_I64_CLZ), /* 0x79 */ \ + HANDLE_OPCODE(WASM_OP_I64_CTZ), /* 0x7a */ \ + HANDLE_OPCODE(WASM_OP_I64_POPCNT), /* 0x7b */ \ + HANDLE_OPCODE(WASM_OP_I64_ADD), /* 0x7c */ \ + HANDLE_OPCODE(WASM_OP_I64_SUB), /* 0x7d */ \ + HANDLE_OPCODE(WASM_OP_I64_MUL), /* 0x7e */ \ + HANDLE_OPCODE(WASM_OP_I64_DIV_S), /* 0x7f */ \ + HANDLE_OPCODE(WASM_OP_I64_DIV_U), /* 0x80 */ \ + HANDLE_OPCODE(WASM_OP_I64_REM_S), /* 0x81 */ \ + HANDLE_OPCODE(WASM_OP_I64_REM_U), /* 0x82 */ \ + HANDLE_OPCODE(WASM_OP_I64_AND), /* 0x83 */ \ + HANDLE_OPCODE(WASM_OP_I64_OR), /* 0x84 */ \ + HANDLE_OPCODE(WASM_OP_I64_XOR), /* 0x85 */ \ + HANDLE_OPCODE(WASM_OP_I64_SHL), /* 0x86 */ \ + HANDLE_OPCODE(WASM_OP_I64_SHR_S), /* 0x87 */ \ + HANDLE_OPCODE(WASM_OP_I64_SHR_U), /* 0x88 */ \ + HANDLE_OPCODE(WASM_OP_I64_ROTL), /* 0x89 */ \ + HANDLE_OPCODE(WASM_OP_I64_ROTR), /* 0x8a */ \ + HANDLE_OPCODE(WASM_OP_F32_ABS), /* 0x8b */ \ + HANDLE_OPCODE(WASM_OP_F32_NEG), /* 0x8c */ \ + HANDLE_OPCODE(WASM_OP_F32_CEIL), /* 0x8d */ \ + HANDLE_OPCODE(WASM_OP_F32_FLOOR), /* 0x8e */ \ + HANDLE_OPCODE(WASM_OP_F32_TRUNC), /* 0x8f */ \ + HANDLE_OPCODE(WASM_OP_F32_NEAREST), /* 0x90 */ \ + HANDLE_OPCODE(WASM_OP_F32_SQRT), /* 0x91 */ \ + HANDLE_OPCODE(WASM_OP_F32_ADD), /* 0x92 */ \ + HANDLE_OPCODE(WASM_OP_F32_SUB), /* 0x93 */ \ + HANDLE_OPCODE(WASM_OP_F32_MUL), /* 0x94 */ \ + HANDLE_OPCODE(WASM_OP_F32_DIV), /* 0x95 */ \ + HANDLE_OPCODE(WASM_OP_F32_MIN), /* 0x96 */ \ + HANDLE_OPCODE(WASM_OP_F32_MAX), /* 0x97 */ \ + HANDLE_OPCODE(WASM_OP_F32_COPYSIGN), /* 0x98 */ \ + HANDLE_OPCODE(WASM_OP_F64_ABS), /* 0x99 */ \ + HANDLE_OPCODE(WASM_OP_F64_NEG), /* 0x9a */ \ + HANDLE_OPCODE(WASM_OP_F64_CEIL), /* 0x9b */ \ + HANDLE_OPCODE(WASM_OP_F64_FLOOR), /* 0x9c */ \ + HANDLE_OPCODE(WASM_OP_F64_TRUNC), /* 0x9d */ \ + HANDLE_OPCODE(WASM_OP_F64_NEAREST), /* 0x9e */ \ + HANDLE_OPCODE(WASM_OP_F64_SQRT), /* 0x9f */ \ + HANDLE_OPCODE(WASM_OP_F64_ADD), /* 0xa0 */ \ + HANDLE_OPCODE(WASM_OP_F64_SUB), /* 0xa1 */ \ + HANDLE_OPCODE(WASM_OP_F64_MUL), /* 0xa2 */ \ + HANDLE_OPCODE(WASM_OP_F64_DIV), /* 0xa3 */ \ + HANDLE_OPCODE(WASM_OP_F64_MIN), /* 0xa4 */ \ + HANDLE_OPCODE(WASM_OP_F64_MAX), /* 0xa5 */ \ + HANDLE_OPCODE(WASM_OP_F64_COPYSIGN), /* 0xa6 */ \ + HANDLE_OPCODE(WASM_OP_I32_WRAP_I64), /* 0xa7 */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_S_F32), /* 0xa8 */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_U_F32), /* 0xa9 */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_S_F64), /* 0xaa */ \ + HANDLE_OPCODE(WASM_OP_I32_TRUNC_U_F64), /* 0xab */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND_S_I32), /* 0xac */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND_U_I32), /* 0xad */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_S_F32), /* 0xae */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_U_F32), /* 0xaf */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_S_F64), /* 0xb0 */ \ + HANDLE_OPCODE(WASM_OP_I64_TRUNC_U_F64), /* 0xb1 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_S_I32), /* 0xb2 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_U_I32), /* 0xb3 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_S_I64), /* 0xb4 */ \ + HANDLE_OPCODE(WASM_OP_F32_CONVERT_U_I64), /* 0xb5 */ \ + HANDLE_OPCODE(WASM_OP_F32_DEMOTE_F64), /* 0xb6 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_S_I32), /* 0xb7 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_U_I32), /* 0xb8 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_S_I64), /* 0xb9 */ \ + HANDLE_OPCODE(WASM_OP_F64_CONVERT_U_I64), /* 0xba */ \ + HANDLE_OPCODE(WASM_OP_F64_PROMOTE_F32), /* 0xbb */ \ + HANDLE_OPCODE(WASM_OP_I32_REINTERPRET_F32), /* 0xbc */ \ + HANDLE_OPCODE(WASM_OP_I64_REINTERPRET_F64), /* 0xbd */ \ + HANDLE_OPCODE(WASM_OP_F32_REINTERPRET_I32), /* 0xbe */ \ + HANDLE_OPCODE(WASM_OP_F64_REINTERPRET_I64), /* 0xbf */ \ + HANDLE_OPCODE(WASM_OP_I32_EXTEND8_S), /* 0xc0 */ \ + HANDLE_OPCODE(WASM_OP_I32_EXTEND16_S), /* 0xc1 */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND8_S), /* 0xc2 */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND16_S), /* 0xc3 */ \ + HANDLE_OPCODE(WASM_OP_I64_EXTEND32_S), /* 0xc4 */ \ + HANDLE_OPCODE(WASM_OP_DROP_64), /* 0xc5 */ \ + HANDLE_OPCODE(WASM_OP_SELECT_64), /* 0xc6 */ \ + HANDLE_OPCODE(EXT_OP_GET_LOCAL_FAST), /* 0xc7 */ \ + HANDLE_OPCODE(EXT_OP_SET_LOCAL_FAST_I64), /* 0xc8 */ \ + HANDLE_OPCODE(EXT_OP_SET_LOCAL_FAST), /* 0xc9 */ \ + HANDLE_OPCODE(EXT_OP_TEE_LOCAL_FAST), /* 0xca */ \ + HANDLE_OPCODE(EXT_OP_TEE_LOCAL_FAST_I64), /* 0xcb */ \ + HANDLE_OPCODE(EXT_OP_COPY_STACK_TOP), /* 0xcc */ \ + HANDLE_OPCODE(EXT_OP_COPY_STACK_TOP_I64), /* 0xcd */ \ + HANDLE_OPCODE(EXT_OP_COPY_STACK_VALUES), /* 0xce */ \ + HANDLE_OPCODE(WASM_OP_IMPDEP), /* 0xcf */ \ + HANDLE_OPCODE(WASM_OP_REF_NULL), /* 0xd0 */ \ + HANDLE_OPCODE(WASM_OP_REF_IS_NULL), /* 0xd1 */ \ + HANDLE_OPCODE(WASM_OP_REF_FUNC), /* 0xd2 */ \ + HANDLE_OPCODE(WASM_OP_REF_EQ), /* 0xd3 */ \ + HANDLE_OPCODE(WASM_OP_REF_AS_NON_NULL), /* 0xd4 */ \ + HANDLE_OPCODE(WASM_OP_BR_ON_NULL), /* 0xd5 */ \ + HANDLE_OPCODE(WASM_OP_BR_ON_NON_NULL), /* 0xd6 */ \ + HANDLE_OPCODE(EXT_OP_BLOCK), /* 0xd7 */ \ + HANDLE_OPCODE(EXT_OP_LOOP), /* 0xd8 */ \ + HANDLE_OPCODE(EXT_OP_IF), /* 0xd9 */ \ + HANDLE_OPCODE(EXT_OP_BR_TABLE_CACHE), /* 0xda */ \ + HANDLE_OPCODE(EXT_OP_TRY), /* 0xdb */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_GC_PREFIX), /* 0xfb */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_MISC_PREFIX), /* 0xfc */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_SIMD_PREFIX), /* 0xfd */ \ + SET_GOTO_TABLE_ELEM(WASM_OP_ATOMIC_PREFIX), /* 0xfe */ \ + DEF_DEBUG_BREAK_HANDLE() DEF_EXT_V128_HANDLE() \ + }; + +#ifdef __cplusplus } +#endif #endif /* end of _WASM_OPCODE_H */ diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 5f78bfa626..e83eae4bba 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -4,33 +4,77 @@ */ #include "wasm_runtime.h" +#include "wasm.h" #include "wasm_loader.h" #include "wasm_interp.h" #include "bh_common.h" #include "bh_log.h" #include "mem_alloc.h" #include "../common/wasm_runtime_common.h" +#include "../common/wasm_memory.h" +#if WASM_ENABLE_GC != 0 +#include "../common/gc/gc_object.h" +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 +#include "../libraries/debug-engine/debug_engine.h" +#endif +#if WASM_ENABLE_FAST_JIT != 0 +#include "../fast-jit/jit_compiler.h" +#endif +#if WASM_ENABLE_JIT != 0 +#include "../aot/aot_runtime.h" +#endif static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, "%s", string); + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "WASM module instantiate failed: %s", string); + } +} + +static void +set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, ...) +{ + va_list args; + char buf[128]; + + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + snprintf(error_buf, error_buf_size, + "WASM module instantiate failed: %s", buf); + } } -WASMModule* -wasm_load(const uint8 *buf, uint32 size, - char *error_buf, uint32 error_buf_size) +WASMModule * +wasm_load(uint8 *buf, uint32 size, +#if WASM_ENABLE_MULTI_MODULE != 0 + bool main_module, +#endif + const LoadArgs *name, char *error_buf, uint32 error_buf_size) { - return wasm_loader_load(buf, size, error_buf, error_buf_size); + return wasm_loader_load(buf, size, +#if WASM_ENABLE_MULTI_MODULE != 0 + main_module, +#endif + name, error_buf, error_buf_size); } -WASMModule* -wasm_load_from_sections(WASMSection *section_list, - char *error_buf, uint32_t error_buf_size) +WASMModule * +wasm_load_from_sections(WASMSection *section_list, char *error_buf, + uint32 error_buf_size) { - return wasm_loader_load_from_sections(section_list, - error_buf, error_buf_size); + return wasm_loader_load_from_sections(section_list, error_buf, + error_buf_size); } void @@ -39,164 +83,504 @@ wasm_unload(WASMModule *module) wasm_loader_unload(module); } +bool +wasm_resolve_symbols(WASMModule *module) +{ + bool ret = true; + uint32 idx; + for (idx = 0; idx < module->import_function_count; ++idx) { + WASMFunctionImport *import = &module->import_functions[idx].u.function; + bool linked = import->func_ptr_linked; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->import_func_linked) { + linked = true; + } +#endif + if (!linked && !wasm_resolve_import_func(module, import)) { + ret = false; + } + } + return ret; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMFunction * +wasm_resolve_function(const char *module_name, const char *function_name, + const WASMFuncType *expected_function_type, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMFunction *function = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + WASMFuncType *target_function_type = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for function %s", module_name, + function_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + module = (WASMModule *)module_reg; + export = loader_find_export((WASMModuleCommon *)module, module_name, + function_name, EXPORT_KIND_FUNC, error_buf, + error_buf_size); + if (!export) { + return NULL; + } + + /* resolve function type and function */ + if (export->index < module->import_function_count) { + target_function_type = + module->import_functions[export->index].u.function.func_type; + function = module->import_functions[export->index] + .u.function.import_func_linked; + } + else { + target_function_type = + module->functions[export->index - module->import_function_count] + ->func_type; + function = + module->functions[export->index - module->import_function_count]; + } + + /* check function type */ + if (!wasm_type_equal((WASMType *)expected_function_type, + (WASMType *)target_function_type, module->types, + module->type_count)) { + LOG_DEBUG("%s.%s failed the type check", module_name, function_name); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return function; +} +#endif + +bool +wasm_resolve_import_func(const WASMModule *module, WASMFunctionImport *function) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + char error_buf[128]; + WASMModule *sub_module = NULL; +#endif + function->func_ptr_linked = wasm_native_resolve_symbol( + function->module_name, function->field_name, function->func_type, + &function->signature, &function->attachment, &function->call_conv_raw); + + if (function->func_ptr_linked) { + return true; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(function->module_name)) { + sub_module = (WASMModule *)wasm_runtime_load_depended_module( + (WASMModuleCommon *)module, function->module_name, error_buf, + sizeof(error_buf)); + if (!sub_module) { + LOG_WARNING("failed to load sub module: %s", error_buf); + return false; + } + } + function->import_func_linked = wasm_resolve_function( + function->module_name, function->field_name, function->func_type, + error_buf, sizeof(error_buf)); + + if (function->import_func_linked) { + function->import_module = sub_module; + return true; + } + else { + LOG_WARNING("failed to link function (%s, %s): %s", + function->module_name, function->field_name, error_buf); + } +#endif + + return false; +} + +static void * +runtime_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModuleInstance * +get_sub_module_inst(const WASMModuleInstance *parent_module_inst, + const WASMModule *sub_module) +{ + bh_list *sub_module_inst_list = parent_module_inst->e->sub_module_inst_list; + WASMSubModInstNode *node = bh_list_first_elem(sub_module_inst_list); + + while (node && sub_module != node->module_inst->module) { + node = bh_list_elem_next(node); + } + return node ? node->module_inst : NULL; +} +#endif + /** * Destroy memory instances. */ static void -memories_deinstantiate(WASMMemoryInstance **memories, uint32 count) +memories_deinstantiate(WASMModuleInstance *module_inst, + WASMMemoryInstance **memories, uint32 count) { uint32 i; if (memories) { - for (i = 0; i < count; i++) + for (i = 0; i < count; i++) { if (memories[i]) { - if (memories[i]->heap_handle) +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *module = module_inst->module; + if (i < module->import_memory_count + && module->import_memories[i].u.memory.import_module) { + continue; + } +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (shared_memory_is_shared(memories[i])) { + uint32 ref_count = shared_memory_dec_reference(memories[i]); + /* if the reference count is not zero, + don't free the memory */ + if (ref_count > 0) + continue; + } +#endif + if (memories[i]->heap_handle) { mem_allocator_destroy(memories[i]->heap_handle); - wasm_free(memories[i]->heap_data); - wasm_free(memories[i]); + wasm_runtime_free(memories[i]->heap_handle); + memories[i]->heap_handle = NULL; + } + if (memories[i]->memory_data) { + wasm_deallocate_linear_memory(memories[i]); + } } - wasm_free(memories); - } + } + wasm_runtime_free(memories); + } + (void)module_inst; } -static WASMMemoryInstance* -memory_instantiate(uint32 num_bytes_per_page, - uint32 init_page_count, uint32 max_page_count, - uint32 global_data_size, - uint32 heap_size, +static WASMMemoryInstance * +memory_instantiate(WASMModuleInstance *module_inst, WASMModuleInstance *parent, + WASMMemoryInstance *memory, uint32 memory_idx, + uint32 num_bytes_per_page, uint32 init_page_count, + uint32 max_page_count, uint32 heap_size, uint32 flags, char *error_buf, uint32 error_buf_size) { - WASMMemoryInstance *memory; - uint64 total_size = offsetof(WASMMemoryInstance, base_addr) + - num_bytes_per_page * (uint64)init_page_count + - global_data_size; + WASMModule *module = module_inst->module; + uint32 inc_page_count, global_idx, default_max_page; + uint32 bytes_of_last_page, bytes_to_page_end; + uint64 aux_heap_base, + heap_offset = (uint64)num_bytes_per_page * init_page_count; + uint64 memory_data_size, max_memory_data_size; + uint8 *global_addr; + + bool is_shared_memory = false; +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared_memory = flags & SHARED_MEMORY_FLAG ? true : false; + + /* shared memory */ + if (is_shared_memory && parent != NULL) { + bh_assert(parent->memory_count > memory_idx); + memory = parent->memories[memory_idx]; + shared_memory_inc_reference(memory); + return memory; + } +#else + (void)parent; + (void)memory_idx; + (void)flags; +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + +#if WASM_ENABLE_MEMORY64 != 0 + if (flags & MEMORY64_FLAG) { + memory->is_memory64 = 1; + } +#endif + default_max_page = + memory->is_memory64 ? DEFAULT_MEM64_MAX_PAGES : DEFAULT_MAX_PAGES; + + /* The app heap should be in the default memory */ + if (memory_idx == 0) { + if (heap_size > 0 && module_inst->module->malloc_function != (uint32)-1 + && module_inst->module->free_function != (uint32)-1) { + /* Disable app heap, use malloc/free function exported + by wasm app to allocate/free memory instead */ + heap_size = 0; + } + + /* If initial memory is the largest size allowed, disallowing insert + * host managed heap */ + if (heap_size > 0 + && heap_offset == GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64)) { + set_error_buf(error_buf, error_buf_size, + "failed to insert app heap into linear memory, " + "try using `--heap-size=0` option"); + return NULL; + } + + if (init_page_count == max_page_count && init_page_count == 1) { + /* If only one page and at most one page, we just append + the app heap to the end of linear memory, enlarge the + num_bytes_per_page, and don't change the page count */ + if (heap_size > UINT32_MAX - num_bytes_per_page) { + set_error_buf(error_buf, error_buf_size, + "failed to insert app heap into linear memory, " + "try using `--heap-size=0` option"); + return NULL; + } + heap_offset = num_bytes_per_page; + num_bytes_per_page += heap_size; + } + else if (heap_size > 0) { + if (init_page_count == max_page_count && init_page_count == 0) { + /* If the memory data size is always 0, we resize it to + one page for app heap */ + num_bytes_per_page = heap_size; + heap_offset = 0; + inc_page_count = 1; + } + else if (module->aux_heap_base_global_index != (uint32)-1 + && module->aux_heap_base + < (uint64)num_bytes_per_page * init_page_count) { + /* Insert app heap before __heap_base */ + aux_heap_base = module->aux_heap_base; + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + inc_page_count = + (heap_size - bytes_to_page_end + num_bytes_per_page - 1) + / num_bytes_per_page; + heap_offset = aux_heap_base; + aux_heap_base += heap_size; + + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + if (bytes_to_page_end < 1 * BH_KB) { + aux_heap_base += 1 * BH_KB; + inc_page_count++; + } + + /* Adjust __heap_base global value */ + global_idx = module->aux_heap_base_global_index; + bh_assert(module_inst->e->globals + && global_idx < module_inst->e->global_count); + global_addr = module_inst->global_data + + module_inst->e->globals[global_idx].data_offset; +#if WASM_ENABLE_MEMORY64 != 0 + if (memory->is_memory64) { + /* For memory64, the global value should be i64 */ + *(uint64 *)global_addr = aux_heap_base; + } + else +#endif + { + /* For memory32, the global value should be i32 */ + *(uint32 *)global_addr = (uint32)aux_heap_base; + } + LOG_VERBOSE("Reset __heap_base global to %" PRIu64, + aux_heap_base); + } + else { + /* Insert app heap before new page */ + inc_page_count = + (heap_size + num_bytes_per_page - 1) / num_bytes_per_page; + heap_offset = (uint64)num_bytes_per_page * init_page_count; + heap_size = (uint64)num_bytes_per_page * inc_page_count; + if (heap_size > 0) + heap_size -= 1 * BH_KB; + } + init_page_count += inc_page_count; + max_page_count += inc_page_count; + if (init_page_count > default_max_page) { + set_error_buf(error_buf, error_buf_size, + "failed to insert app heap into linear memory, " + "try using `--heap-size=0` option"); + return NULL; + } + + if (max_page_count > default_max_page) + max_page_count = default_max_page; + } + } - /* Allocate memory space, addr data and global data */ - if (total_size >= UINT32_MAX - || !(memory = wasm_malloc((uint32)total_size))) { + LOG_VERBOSE("Memory instantiate:"); + LOG_VERBOSE(" page bytes: %u, init pages: %u, max pages: %u", + num_bytes_per_page, init_page_count, max_page_count); + if (memory_idx == 0) + LOG_VERBOSE(" heap offset: %" PRIu64 ", heap size: %u\n", heap_offset, + heap_size); + + max_memory_data_size = (uint64)num_bytes_per_page * max_page_count; + bh_assert(max_memory_data_size + <= GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64)); + (void)max_memory_data_size; + + bh_assert(memory != NULL); + + if (wasm_allocate_linear_memory(&memory->memory_data, is_shared_memory, + memory->is_memory64, num_bytes_per_page, + init_page_count, max_page_count, + &memory_data_size) + != BHT_OK) { set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: allocate memory failed."); + "allocate linear memory failed"); return NULL; } - memset(memory, 0, (uint32)total_size); + memory->module_type = Wasm_Module_Bytecode; memory->num_bytes_per_page = num_bytes_per_page; memory->cur_page_count = init_page_count; memory->max_page_count = max_page_count; + memory->memory_data_size = memory_data_size; - memory->memory_data = memory->base_addr; - - memory->global_data = memory->memory_data + - num_bytes_per_page * memory->cur_page_count; - memory->global_data_size = global_data_size; + if (memory_idx == 0) { + memory->heap_data = memory->memory_data + heap_offset; + memory->heap_data_end = memory->heap_data + heap_size; + memory->memory_data_end = memory->memory_data + memory_data_size; + } - memory->end_addr = memory->global_data + global_data_size; + /* Initialize heap */ + if (memory_idx == 0 && heap_size > 0) { + uint32 heap_struct_size = mem_allocator_get_heap_struct_size(); - /* Allocate heap space */ - if (!(memory->heap_data = wasm_malloc(heap_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: allocate memory failed."); - goto fail1; + if (!(memory->heap_handle = runtime_malloc( + (uint64)heap_struct_size, error_buf, error_buf_size))) { + goto fail1; + } + if (!mem_allocator_create_with_struct_and_pool( + memory->heap_handle, heap_struct_size, memory->heap_data, + heap_size)) { + set_error_buf(error_buf, error_buf_size, "init app heap failed"); + goto fail2; + } } - memory->heap_data_end = memory->heap_data + heap_size; - /* Initialize heap */ - if (!(memory->heap_handle = mem_allocator_create - (memory->heap_data, heap_size))) { - goto fail2; + if (memory_data_size > 0) { + wasm_runtime_set_mem_bound_check_bytes(memory, memory_data_size); } -#if WASM_ENABLE_MEMORY_GROW != 0 - memory->heap_base_offset = DEFAULT_APP_HEAP_BASE_OFFSET; -#else - memory->heap_base_offset = memory->end_addr - memory->memory_data; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (is_shared_memory) { + memory->is_shared_memory = 1; + memory->ref_count = 1; + } #endif + LOG_VERBOSE("Memory instantiate success."); return memory; fail2: - wasm_free(memory->heap_data); - + if (memory_idx == 0 && heap_size > 0) + wasm_runtime_free(memory->heap_handle); fail1: - wasm_free(memory); + if (memory->memory_data) + wasm_deallocate_linear_memory(memory); + return NULL; } /** * Instantiate memories in a module. */ -static WASMMemoryInstance** -memories_instantiate(const WASMModule *module, - uint32 global_data_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size) +static WASMMemoryInstance ** +memories_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, + WASMModuleInstance *parent, uint32 heap_size, + uint32 max_memory_pages, char *error_buf, + uint32 error_buf_size) { WASMImport *import; - uint32 mem_index = 0, i, memory_count = - module->import_memory_count + module->memory_count; + uint32 mem_index = 0, i, + memory_count = module->import_memory_count + module->memory_count; uint64 total_size; WASMMemoryInstance **memories, *memory; - if (memory_count == 0 && global_data_size > 0) - memory_count = 1; + total_size = sizeof(WASMMemoryInstance *) * (uint64)memory_count; - total_size = sizeof(WASMMemoryInstance*) * (uint64)memory_count; - - if (total_size >= UINT32_MAX - || !(memories = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: " - "allocate memory failed."); + if (!(memories = runtime_malloc(total_size, error_buf, error_buf_size))) { return NULL; } - memset(memories, 0, (uint32)total_size); + memory = module_inst->global_table_data.memory_instances; /* instantiate memories from import section */ import = module->import_memories; - for (i = 0; i < module->import_memory_count; i++, import++) { - if (!(memory = memories[mem_index++] = - memory_instantiate(import->u.memory.num_bytes_per_page, - import->u.memory.init_page_count, - import->u.memory. max_page_count, - global_data_size, - heap_size, error_buf, error_buf_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: " - "allocate memory failed."); - memories_deinstantiate(memories, memory_count); - return NULL; - } - } + for (i = 0; i < module->import_memory_count; i++, import++, memory++) { + uint32 num_bytes_per_page = + import->u.memory.mem_type.num_bytes_per_page; + uint32 init_page_count = import->u.memory.mem_type.init_page_count; + uint32 max_page_count = wasm_runtime_get_max_mem( + max_memory_pages, import->u.memory.mem_type.init_page_count, + import->u.memory.mem_type.max_page_count); + uint32 flags = import->u.memory.mem_type.flags; + uint32 actual_heap_size = heap_size; + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->u.memory.import_module != NULL) { + WASMModuleInstance *module_inst_linked; + + if (!(module_inst_linked = get_sub_module_inst( + module_inst, import->u.memory.import_module))) { + set_error_buf(error_buf, error_buf_size, "unknown memory"); + memories_deinstantiate(module_inst, memories, memory_count); + return NULL; + } - /* instantiate memories from memory section */ - for (i = 0; i < module->memory_count; i++) { - if (!(memory = memories[mem_index++] = - memory_instantiate(module->memories[i].num_bytes_per_page, - module->memories[i].init_page_count, - module->memories[i].max_page_count, - global_data_size, - heap_size, error_buf, error_buf_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: " - "allocate memory failed."); - memories_deinstantiate(memories, memory_count); - return NULL; + if (!(memories[mem_index++] = wasm_lookup_memory( + module_inst_linked, import->u.memory.field_name))) { + set_error_buf(error_buf, error_buf_size, "unknown memory"); + memories_deinstantiate(module_inst, memories, memory_count); + return NULL; + } + } + else +#endif + { + if (!(memories[mem_index] = memory_instantiate( + module_inst, parent, memory, mem_index, + num_bytes_per_page, init_page_count, max_page_count, + actual_heap_size, flags, error_buf, error_buf_size))) { + memories_deinstantiate(module_inst, memories, memory_count); + return NULL; + } + mem_index++; } } - if (mem_index == 0) { - /* no import memory and define memory, but has global variables */ - if (!(memory = memories[mem_index++] = - memory_instantiate(0, 0, 0, global_data_size, - heap_size, error_buf, error_buf_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: " - "allocate memory failed.\n"); - memories_deinstantiate(memories, memory_count); + /* instantiate memories from memory section */ + for (i = 0; i < module->memory_count; i++, memory++) { + uint32 max_page_count = wasm_runtime_get_max_mem( + max_memory_pages, module->memories[i].init_page_count, + module->memories[i].max_page_count); + if (!(memories[mem_index] = memory_instantiate( + module_inst, parent, memory, mem_index, + module->memories[i].num_bytes_per_page, + module->memories[i].init_page_count, max_page_count, + heap_size, module->memories[i].flags, error_buf, + error_buf_size))) { + memories_deinstantiate(module_inst, memories, memory_count); return NULL; } + mem_index++; } bh_assert(mem_index == memory_count); + (void)module_inst; return memories; } @@ -204,138 +588,246 @@ memories_instantiate(const WASMModule *module, * Destroy table instances. */ static void -tables_deinstantiate(WASMTableInstance **tables, uint32 count) +tables_deinstantiate(WASMModuleInstance *module_inst) { - uint32 i; - if (tables) { - for (i = 0; i < count; i++) - if (tables[i]) - wasm_free(tables[i]); - wasm_free(tables); + if (module_inst->tables) { + wasm_runtime_free(module_inst->tables); + } +#if WASM_ENABLE_MULTI_MODULE != 0 + if (module_inst->e->table_insts_linked) { + wasm_runtime_free(module_inst->e->table_insts_linked); } +#endif } /** * Instantiate tables in a module. */ -static WASMTableInstance** -tables_instantiate(const WASMModule *module, - char *error_buf, uint32 error_buf_size) +static WASMTableInstance ** +tables_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, + WASMTableInstance *first_table, char *error_buf, + uint32 error_buf_size) { WASMImport *import; - uint32 table_index = 0, i, table_count = - module->import_table_count + module->table_count; - uint64 total_size = sizeof(WASMTableInstance*) * (uint64)table_count; - WASMTableInstance **tables, *table; + uint32 table_index = 0, i; + uint32 table_count = module->import_table_count + module->table_count; + uint64 total_size = (uint64)sizeof(WASMTableInstance *) * table_count; + WASMTableInstance **tables, *table = first_table; +#if WASM_ENABLE_MULTI_MODULE != 0 + uint64 total_size_of_tables_linked = + (uint64)sizeof(WASMTableInstance *) * module->import_table_count; + WASMTableInstance **table_linked = NULL; +#endif - if (total_size >= UINT32_MAX - || !(tables = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate table failed: " - "allocate memory failed."); + if (!(tables = runtime_malloc(total_size, error_buf, error_buf_size))) { return NULL; } - memset(tables, 0, (uint32)total_size); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (module->import_table_count > 0 + && !(module_inst->e->table_insts_linked = table_linked = runtime_malloc( + total_size_of_tables_linked, error_buf, error_buf_size))) { + goto fail; + } +#endif /* instantiate tables from import section */ import = module->import_tables; for (i = 0; i < module->import_table_count; i++, import++) { - total_size = offsetof(WASMTableInstance, base_addr) + - sizeof(uint32) * (uint64)import->u.table.init_size; - if (total_size >= UINT32_MAX - || !(table = tables[table_index++] = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate table failed: " - "allocate memory failed."); - tables_deinstantiate(tables, table_count); - return NULL; + uint32 max_size_fixed = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMTableInstance *table_inst_linked = NULL; + WASMModuleInstance *module_inst_linked = NULL; + + if (import->u.table.import_module) { + if (!(module_inst_linked = get_sub_module_inst( + module_inst, import->u.table.import_module))) { + set_error_buf(error_buf, error_buf_size, "unknown table"); + goto fail; + } + + if (!(table_inst_linked = wasm_lookup_table( + module_inst_linked, import->u.table.field_name))) { + set_error_buf(error_buf, error_buf_size, "unknown table"); + goto fail; + } + + total_size = offsetof(WASMTableInstance, elems); + } + else +#endif + { + /* in order to save memory, alloc resource as few as possible */ + max_size_fixed = import->u.table.table_type.possible_grow + ? import->u.table.table_type.max_size + : import->u.table.table_type.init_size; + + /* it is a built-in table, every module has its own */ + total_size = offsetof(WASMTableInstance, elems); + /* store function indexes for non-gc, object pointers for gc */ + total_size += (uint64)sizeof(table_elem_type_t) * max_size_fixed; } + tables[table_index++] = table; + +#if WASM_ENABLE_GC == 0 /* Set all elements to -1 to mark them as uninitialized elements */ memset(table, -1, (uint32)total_size); - table->elem_type = import->u.table.elem_type; - table->cur_size = import->u.table.init_size; - table->max_size = import->u.table.max_size; +#else + /* For GC, all elements have already been set to NULL_REF (0) as + uninitialized elements */ +#endif + + table->is_table64 = import->u.table.table_type.flags & TABLE64_FLAG; + +#if WASM_ENABLE_MULTI_MODULE != 0 + *table_linked = table_inst_linked; + if (table_inst_linked != NULL) { + table->elem_type = table_inst_linked->elem_type; +#if WASM_ENABLE_GC != 0 + table->elem_ref_type = table_inst_linked->elem_ref_type; +#endif + table->cur_size = table_inst_linked->cur_size; + table->max_size = table_inst_linked->max_size; + } + else +#endif + { + table->elem_type = import->u.table.table_type.elem_type; +#if WASM_ENABLE_GC != 0 + table->elem_ref_type.elem_ref_type = + import->u.table.table_type.elem_ref_type; +#endif + table->cur_size = import->u.table.table_type.init_size; + table->max_size = max_size_fixed; + } + + table = (WASMTableInstance *)((uint8 *)table + (uint32)total_size); +#if WASM_ENABLE_MULTI_MODULE != 0 + table_linked++; +#endif } /* instantiate tables from table section */ for (i = 0; i < module->table_count; i++) { - total_size = offsetof(WASMTableInstance, base_addr) + - sizeof(uint32) * (uint64)module->tables[i].init_size; - if (total_size >= UINT32_MAX - || !(table = tables[table_index++] = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate table failed: " - "allocate memory failed."); - tables_deinstantiate(tables, table_count); - return NULL; - } + uint32 max_size_fixed = 0; + + total_size = offsetof(WASMTableInstance, elems); +#if WASM_ENABLE_MULTI_MODULE != 0 + /* in case, a module which imports this table will grow it */ + max_size_fixed = module->tables[i].table_type.max_size; +#else + max_size_fixed = module->tables[i].table_type.possible_grow + ? module->tables[i].table_type.max_size + : module->tables[i].table_type.init_size; +#endif +#if WASM_ENABLE_GC == 0 + /* Store function indexes */ + total_size += sizeof(uintptr_t) * (uint64)max_size_fixed; +#else + /* Store object pointers */ + total_size += sizeof(uintptr_t) * (uint64)max_size_fixed; +#endif + + tables[table_index++] = table; +#if WASM_ENABLE_GC == 0 /* Set all elements to -1 to mark them as uninitialized elements */ memset(table, -1, (uint32)total_size); - table->elem_type = module->tables[i].elem_type; - table->cur_size = module->tables[i].init_size; - table->max_size = module->tables[i].max_size; +#else + /* For GC, all elements have already been set to NULL_REF (0) as + uninitialized elements */ +#endif + table->is_table64 = module->tables[i].table_type.flags & TABLE64_FLAG; + table->elem_type = module->tables[i].table_type.elem_type; +#if WASM_ENABLE_GC != 0 + table->elem_ref_type.elem_ref_type = + module->tables[i].table_type.elem_ref_type; +#endif + table->cur_size = module->tables[i].table_type.init_size; + table->max_size = max_size_fixed; + + table = (WASMTableInstance *)((uint8 *)table + (uint32)total_size); } bh_assert(table_index == table_count); + (void)module_inst; return tables; +#if WASM_ENABLE_MULTI_MODULE != 0 +fail: + wasm_runtime_free(tables); + return NULL; +#endif } /** * Destroy function instances. */ static void -functions_deinstantiate(WASMFunctionInstance *functions, uint32 count) +functions_deinstantiate(WASMFunctionInstance *functions) { if (functions) { - wasm_free(functions); + wasm_runtime_free(functions); } } /** * Instantiate functions in a module. */ -static WASMFunctionInstance* -functions_instantiate(const WASMModule *module, +static WASMFunctionInstance * +functions_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, char *error_buf, uint32 error_buf_size) { WASMImport *import; - uint32 i, function_count = - module->import_function_count + module->function_count; + uint32 i, + function_count = module->import_function_count + module->function_count; uint64 total_size = sizeof(WASMFunctionInstance) * (uint64)function_count; WASMFunctionInstance *functions, *function; - if (total_size >= UINT32_MAX - || !(functions = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate function failed: " - "allocate memory failed."); + if (!(functions = runtime_malloc(total_size, error_buf, error_buf_size))) { return NULL; } - memset(functions, 0, (uint32)total_size); + total_size = sizeof(void *) * (uint64)module->import_function_count; + if (total_size > 0 + && !(module_inst->import_func_ptrs = + runtime_malloc(total_size, error_buf, error_buf_size))) { + wasm_runtime_free(functions); + return NULL; + } /* instantiate functions from import section */ function = functions; import = module->import_functions; for (i = 0; i < module->import_function_count; i++, import++) { function->is_import_func = true; - function->u.func_import = &import->u.function; - function->param_cell_num = - wasm_type_param_cell_num(import->u.function.func_type); - function->ret_cell_num = - wasm_type_return_cell_num(import->u.function.func_type); - function->local_cell_num = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->u.function.import_module) { + function->import_module_inst = get_sub_module_inst( + module_inst, import->u.function.import_module); + if (function->import_module_inst) { + function->import_func_inst = + wasm_lookup_function(function->import_module_inst, + import->u.function.field_name); + } + } +#endif /* WASM_ENABLE_MULTI_MODULE */ + function->u.func_import = &import->u.function; + function->param_cell_num = import->u.function.func_type->param_cell_num; + function->ret_cell_num = import->u.function.func_type->ret_cell_num; function->param_count = (uint16)function->u.func_import->func_type->param_count; - function->local_count = 0; function->param_types = function->u.func_import->func_type->types; + function->local_cell_num = 0; + function->local_count = 0; function->local_types = NULL; + /* Copy the function pointer to current instance */ + module_inst->import_func_ptrs[i] = + function->u.func_import->func_ptr_linked; + function++; } @@ -348,147 +840,600 @@ functions_instantiate(const WASMModule *module, function->ret_cell_num = function->u.func->ret_cell_num; function->local_cell_num = function->u.func->local_cell_num; - function->param_count = (uint16)function->u.func->func_type->param_count; + function->param_count = + (uint16)function->u.func->func_type->param_count; function->local_count = (uint16)function->u.func->local_count; function->param_types = function->u.func->func_type->types; function->local_types = function->u.func->local_types; function->local_offsets = function->u.func->local_offsets; +#if WASM_ENABLE_FAST_INTERP != 0 + function->const_cell_num = function->u.func->const_cell_num; +#endif + 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 + return functions; } +#if WASM_ENABLE_TAGS != 0 /** - * Destroy global instances. + * Destroy tags instances. */ static void -globals_deinstantiate(WASMGlobalInstance *globals) +tags_deinstantiate(WASMTagInstance *tags, void **import_tag_ptrs) { - if (globals) - wasm_free(globals); + if (tags) { + wasm_runtime_free(tags); + } + if (import_tag_ptrs) { + wasm_runtime_free(import_tag_ptrs); + } } /** - * Instantiate globals in a module. + * Instantiate tags in a module. */ -static WASMGlobalInstance* -globals_instantiate(const WASMModule *module, - uint32 *p_global_data_size, - char *error_buf, uint32 error_buf_size) +static WASMTagInstance * +tags_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, + char *error_buf, uint32 error_buf_size) { WASMImport *import; - uint32 global_data_offset = 0; - uint32 i, global_count = - module->import_global_count + module->global_count; - uint64 total_size = sizeof(WASMGlobalInstance) * (uint64)global_count; - WASMGlobalInstance *globals, *global; + uint32 i, tag_count = module->import_tag_count + module->tag_count; + uint64 total_size = sizeof(WASMTagInstance) * (uint64)tag_count; + WASMTagInstance *tags, *tag; - if (total_size >= UINT32_MAX - || !(globals = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate global failed: " - "allocate memory failed."); + if (!(tags = runtime_malloc(total_size, error_buf, error_buf_size))) { return NULL; } - memset(globals, 0, (uint32)total_size); - - /* instantiate globals from import section */ - global = globals; - import = module->import_globals; - for (i = 0; i < module->import_global_count; i++, import++) { - WASMGlobalImport *global_import = &import->u.global; - global->type = global_import->type; - global->is_mutable = global_import->is_mutable; - global->initial_value = global_import->global_data_linked; - global->data_offset = global_data_offset; - global_data_offset += wasm_value_type_size(global->type); - - global++; + total_size = sizeof(void *) * (uint64)module->import_tag_count; + if (total_size > 0 + && !(module_inst->e->import_tag_ptrs = + runtime_malloc(total_size, error_buf, error_buf_size))) { + wasm_runtime_free(tags); + return NULL; } - /* instantiate globals from global section */ - for (i = 0; i < module->global_count; i++) { - global->type = module->globals[i].type; - global->is_mutable = module->globals[i].is_mutable; + /* instantiate tags from import section */ + tag = tags; + import = module->import_tags; + for (i = 0; i < module->import_tag_count; i++, import++) { + tag->is_import_tag = true; + tag->u.tag_import = &import->u.tag; + tag->type = import->u.tag.type; + tag->attribute = import->u.tag.attribute; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->u.tag.import_module) { + if (!(tag->import_module_inst = get_sub_module_inst( + module_inst, import->u.tag.import_module))) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } - global->data_offset = global_data_offset; - global_data_offset += wasm_value_type_size(global->type); + if (!(tag->import_tag_inst = + wasm_lookup_tag(tag->import_module_inst, + import->u.tag.field_name, NULL))) { + set_error_buf(error_buf, error_buf_size, "unknown tag"); + goto fail; + } - global++; + /* Copy the imported tag to current instance */ + module_inst->e->import_tag_ptrs[i] = + tag->u.tag_import->import_tag_linked; + } +#endif + tag++; } - bh_assert((uint32)(global - globals) == global_count); - *p_global_data_size = global_data_offset; - return globals; -} - -static void -globals_instantiate_fix(WASMGlobalInstance *globals, - const WASMModule *module, - WASMModuleInstance *module_inst) -{ - WASMGlobalInstance *global = globals; - WASMImport *import = module->import_globals; - uint32 i; + /* instantiate tags from tag section */ + for (i = 0; i < module->tag_count; i++) { + tag->is_import_tag = false; + tag->type = module->tags[i]->type; + tag->u.tag = module->tags[i]; - /* Fix globals from import section */ - for (i = 0; i < module->import_global_count; i++, import++, global++) { - if (!strcmp(import->u.names.module_name, "env")) { - if (!strcmp(import->u.names.field_name, "memoryBase") - || !strcmp(import->u.names.field_name, "__memory_base")) { - global->initial_value.addr = 0; - } - else if (!strcmp(import->u.names.field_name, "tableBase") - || !strcmp(import->u.names.field_name, "__table_base")) { - global->initial_value.addr = 0; - } - else if (!strcmp(import->u.names.field_name, "DYNAMICTOP_PTR")) { - global->initial_value.i32 = (int32) - (module_inst->default_memory->num_bytes_per_page - * module_inst->default_memory->cur_page_count); - module_inst->DYNAMICTOP_PTR_offset = global->data_offset; - } - else if (!strcmp(import->u.names.field_name, "STACKTOP")) { - global->initial_value.i32 = 0; - } - else if (!strcmp(import->u.names.field_name, "STACK_MAX")) { - /* Unused in emcc wasm bin actually. */ - global->initial_value.i32 = 0; - } - } +#if WASM_ENABLE_FAST_INTERP != 0 + /* tag->const_cell_num = function->u.func->const_cell_num; */ +#endif + tag++; } + bh_assert((uint32)(tag - tags) == tag_count); - for (i = 0; i < module->global_count; i++) { - InitializerExpression *init_expr = &module->globals[i].init_expr; + return tags; - if (init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - bh_assert(init_expr->u.global_index < module->import_global_count); - global->initial_value = globals[init_expr->u.global_index].initial_value; - } - else { - bh_memcpy_s(&global->initial_value, sizeof(WASMValue), - &init_expr->u, sizeof(init_expr->u)); - } - global++; - } +#if WASM_ENABLE_MULTI_MODULE != 0 +fail: + tags_deinstantiate(tags, module_inst->e->import_tag_ptrs); + /* clean up */ + module_inst->e->import_tag_ptrs = NULL; + return NULL; +#endif } +#endif /* end of WASM_ENABLE_TAGS != 0 */ /** - * Return export function count in module export section. + * Destroy global instances. */ -static uint32 -get_export_function_count(const WASMModule *module) -{ +static void +globals_deinstantiate(WASMGlobalInstance *globals) +{ + if (globals) + wasm_runtime_free(globals); +} + +static bool +check_global_init_expr(const WASMModule *module, uint32 global_index, + char *error_buf, uint32 error_buf_size) +{ + if (global_index >= module->import_global_count + module->global_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown global %d", + global_index); + return false; + } + +#if WASM_ENABLE_GC == 0 + /** + * Currently, constant expressions occurring as initializers of + * globals are further constrained in that contained global.get + * instructions are only allowed to refer to imported globals. + * + * And initializer expression cannot reference a mutable global. + */ + if (global_index >= module->import_global_count + || (module->import_globals + global_index)->u.global.type.is_mutable) { + set_error_buf(error_buf, error_buf_size, + "constant expression required"); + return false; + } +#endif + + return true; +} + +#if WASM_ENABLE_GC != 0 +/* Instantiate struct global variable recursively */ +static WASMStructObjectRef +instantiate_struct_global_recursive(WASMModule *module, + WASMModuleInstance *module_inst, + uint32 type_idx, uint8 flag, + WASMStructNewInitValues *init_values, + char *error_buf, uint32 error_buf_size) +{ + WASMRttType *rtt_type; + WASMStructObjectRef struct_obj; + WASMStructType *struct_type; + + struct_type = (WASMStructType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new((WASMType *)struct_type, type_idx, + module->rtt_types, module->type_count, + &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, "create rtt object failed"); + return NULL; + } + + if (!(struct_obj = wasm_struct_obj_new_internal( + module_inst->e->common.gc_heap_handle, rtt_type))) { + set_error_buf(error_buf, error_buf_size, "create struct object failed"); + return NULL; + } + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + uint32 field_idx; + WASMRefTypeMap *ref_type_map = struct_type->ref_type_maps; + + bh_assert(init_values->count == struct_type->field_count); + + for (field_idx = 0; field_idx < init_values->count; field_idx++) { + uint8 field_type = struct_type->fields[field_idx].field_type; + WASMRefType *field_ref_type = NULL; + if (wasm_is_type_multi_byte_type(field_type)) { + field_ref_type = ref_type_map->ref_type; + } + + if (wasm_reftype_is_subtype_of(field_type, field_ref_type, + REF_TYPE_STRUCTREF, NULL, + module->types, module->type_count) + || wasm_reftype_is_subtype_of(field_type, field_ref_type, + REF_TYPE_ARRAYREF, NULL, + module->types, module->type_count) + || wasm_reftype_is_subtype_of( + field_type, field_ref_type, REF_TYPE_FUNCREF, NULL, + module->types, module->type_count)) { + WASMType *wasm_type; + int32 heap_type = + ref_type_map->ref_type->ref_ht_common.heap_type; + WASMValue *wasm_value = &init_values->fields[field_idx]; + WASMValue field_value = { 0 }; + + bh_assert(heap_type >= 0); + wasm_type = module->types[heap_type]; + + bh_assert(wasm_type->type_flag == WASM_TYPE_STRUCT + || wasm_type->type_flag == WASM_TYPE_ARRAY + || wasm_type->type_flag == WASM_TYPE_FUNC); + + if (wasm_type->type_flag == WASM_TYPE_STRUCT) { + WASMStructNewInitValues *init_values1 = + (WASMStructNewInitValues *)wasm_value->data; + WASMStructObjectRef field = + instantiate_struct_global_recursive( + module, module_inst, heap_type, + init_values1 ? INIT_EXPR_TYPE_STRUCT_NEW + : INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT, + init_values1, error_buf, error_buf_size); + field_value.gc_obj = (WASMObjectRef)field; + wasm_struct_obj_set_field(struct_obj, field_idx, + &field_value); + } + else if (wasm_type->type_flag == WASM_TYPE_ARRAY) { + /* struct object's field is an array obj */ + set_error_buf(error_buf, error_buf_size, + "array as a field in struct object is " + "not supported in constant init expr"); + return NULL; + } + else if (wasm_type->type_flag == WASM_TYPE_FUNC) { + WASMFuncObjectRef func_obj = NULL; + /* UINT32_MAX indicates that it is a null reference */ + if (wasm_value->u32 != UINT32_MAX) { + if (!(func_obj = wasm_create_func_obj( + module_inst, wasm_value->u32, false, + error_buf, error_buf_size))) { + return NULL; + } + } + field_value.gc_obj = (WASMObjectRef)func_obj; + wasm_struct_obj_set_field(struct_obj, field_idx, + &field_value); + } + } + else { + wasm_struct_obj_set_field(struct_obj, field_idx, + &init_values->fields[field_idx]); + } + if (wasm_is_type_multi_byte_type(field_type)) { + ref_type_map++; + } + } + } + + return struct_obj; +} + +static WASMArrayObjectRef +instantiate_array_global_recursive(WASMModule *module, + WASMModuleInstance *module_inst, + uint32 type_idx, uint8 flag, uint32 len, + WASMValue *array_init_value, + WASMArrayNewInitValues *init_values, + char *error_buf, uint32 error_buf_size) +{ + WASMRttType *rtt_type; + WASMArrayObjectRef array_obj; + WASMArrayType *array_type; + + array_type = (WASMArrayType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new((WASMType *)array_type, type_idx, + module->rtt_types, module->type_count, + &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, "create rtt object failed"); + return NULL; + } + + if (!(array_obj = + wasm_array_obj_new_internal(module_inst->e->common.gc_heap_handle, + rtt_type, len, array_init_value))) { + set_error_buf(error_buf, error_buf_size, "create array object failed"); + return NULL; + } + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + uint32 elem_idx; + uint8 elem_type = array_type->elem_type; + WASMRefType *elem_ref_type = array_type->elem_ref_type; + + bh_assert(init_values); + + if (wasm_reftype_is_subtype_of(elem_type, elem_ref_type, + REF_TYPE_STRUCTREF, NULL, module->types, + module->type_count) + || wasm_reftype_is_subtype_of(elem_type, elem_ref_type, + REF_TYPE_ARRAYREF, NULL, + module->types, module->type_count) + || wasm_reftype_is_subtype_of(elem_type, elem_ref_type, + REF_TYPE_FUNCREF, NULL, module->types, + module->type_count)) { + /* TODO */ + } + + for (elem_idx = 0; elem_idx < len; elem_idx++) { + wasm_array_obj_set_elem(array_obj, elem_idx, + &init_values->elem_data[elem_idx]); + } + } + + return array_obj; +} +#endif + +static bool +get_init_value_recursive(WASMModule *module, InitializerExpression *expr, + WASMGlobalInstance *globals, WASMValue *value, + char *error_buf, uint32 error_buf_size) +{ + uint8 flag = expr->init_expr_type; + switch (flag) { + case INIT_EXPR_TYPE_GET_GLOBAL: + { + if (!check_global_init_expr(module, expr->u.unary.v.global_index, + error_buf, error_buf_size)) { + goto fail; + } + + *value = globals[expr->u.unary.v.global_index].initial_value; + break; + } + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_I64_CONST: + { + *value = expr->u.unary.v; + break; + } +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: + { + WASMValue l_value, r_value; + if (!expr->u.binary.l_expr || !expr->u.binary.r_expr) { + goto fail; + } + if (!get_init_value_recursive(module, expr->u.binary.l_expr, + globals, &l_value, error_buf, + error_buf_size)) { + goto fail; + } + if (!get_init_value_recursive(module, expr->u.binary.r_expr, + globals, &r_value, error_buf, + error_buf_size)) { + goto fail; + } + + if (flag == INIT_EXPR_TYPE_I32_ADD) { + value->i32 = l_value.i32 + r_value.i32; + } + else if (flag == INIT_EXPR_TYPE_I32_SUB) { + value->i32 = l_value.i32 - r_value.i32; + } + else if (flag == INIT_EXPR_TYPE_I32_MUL) { + value->i32 = l_value.i32 * r_value.i32; + } + else if (flag == INIT_EXPR_TYPE_I64_ADD) { + value->i64 = l_value.i64 + r_value.i64; + } + else if (flag == INIT_EXPR_TYPE_I64_SUB) { + value->i64 = l_value.i64 - r_value.i64; + } + else if (flag == INIT_EXPR_TYPE_I64_MUL) { + value->i64 = l_value.i64 * r_value.i64; + } + break; + } +#endif /* end of WASM_ENABLE_EXTENDED_CONST_EXPR != 0 */ + default: + goto fail; + } + return true; +fail: + return false; +} + +/** + * Instantiate globals in a module. + */ +static WASMGlobalInstance * +globals_instantiate(WASMModule *module, WASMModuleInstance *module_inst, + char *error_buf, uint32 error_buf_size) +{ + WASMImport *import; + uint32 global_data_offset = 0; + uint32 i, global_count = module->import_global_count + module->global_count; + uint64 total_size = sizeof(WASMGlobalInstance) * (uint64)global_count; + WASMGlobalInstance *globals, *global; + + if (!(globals = runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + /* instantiate globals from import section */ + global = globals; + import = module->import_globals; + for (i = 0; i < module->import_global_count; i++, import++) { + WASMGlobalImport *global_import = &import->u.global; + global->type = global_import->type.val_type; + global->is_mutable = global_import->type.is_mutable; +#if WASM_ENABLE_GC != 0 + global->ref_type = global_import->ref_type; +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + if (global_import->import_module) { + if (!(global->import_module_inst = get_sub_module_inst( + module_inst, global_import->import_module))) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + goto fail; + } + + if (!(global->import_global_inst = wasm_lookup_global( + global->import_module_inst, global_import->field_name))) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + goto fail; + } + + /* The linked global instance has been initialized, we + just need to copy the value. */ + global->initial_value = + global_import->import_global_linked->init_expr.u.unary.v; + } + else +#endif + { + /* native globals share their initial_values in one module */ + bh_memcpy_s(&(global->initial_value), sizeof(WASMValue), + &(global_import->global_data_linked), + sizeof(WASMValue)); + } +#if WASM_ENABLE_FAST_JIT != 0 + bh_assert(global_data_offset == global_import->data_offset); +#endif + global->data_offset = global_data_offset; + global_data_offset += wasm_value_type_size(global->type); + + global++; + } + + /* instantiate globals from global section */ + for (i = 0; i < module->global_count; i++) { + InitializerExpression *init_expr = &(module->globals[i].init_expr); + uint8 flag = init_expr->init_expr_type; + + global->type = module->globals[i].type.val_type; + global->is_mutable = module->globals[i].type.is_mutable; +#if WASM_ENABLE_FAST_JIT != 0 + bh_assert(global_data_offset == module->globals[i].data_offset); +#endif + global->data_offset = global_data_offset; + global_data_offset += wasm_value_type_size(global->type); +#if WASM_ENABLE_GC != 0 + global->ref_type = module->globals[i].ref_type; +#endif + + switch (flag) { + case INIT_EXPR_TYPE_I32_CONST: + case INIT_EXPR_TYPE_I64_CONST: + case INIT_EXPR_TYPE_GET_GLOBAL: +#if WASM_ENABLE_EXTENDED_CONST_EXPR != 0 + case INIT_EXPR_TYPE_I32_ADD: + case INIT_EXPR_TYPE_I32_SUB: + case INIT_EXPR_TYPE_I32_MUL: + case INIT_EXPR_TYPE_I64_ADD: + case INIT_EXPR_TYPE_I64_SUB: + case INIT_EXPR_TYPE_I64_MUL: +#endif + { + if (!get_init_value_recursive(module, init_expr, globals, + &global->initial_value, error_buf, + error_buf_size)) { + goto fail; + } + break; + } +#if WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_STRUCT_NEW: + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + { + WASMStructObjectRef struct_obj; + WASMStructNewInitValues *init_values = NULL; + uint32 type_idx; + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + init_values = + (WASMStructNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + } + else { + type_idx = init_expr->u.unary.v.type_index; + } + + struct_obj = instantiate_struct_global_recursive( + module, module_inst, type_idx, flag, init_values, error_buf, + error_buf_size); + if (!struct_obj) { + goto fail; + } + + global->initial_value.gc_obj = (void *)struct_obj; + break; + } + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + WASMArrayObjectRef array_obj; + WASMArrayNewInitValues *init_values = NULL; + WASMValue *array_init_value = NULL, empty_value = { 0 }; + uint32 type_idx, len; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) { + type_idx = + init_expr->u.unary.v.array_new_default.type_index; + len = init_expr->u.unary.v.array_new_default.length; + array_init_value = &empty_value; + } + else { + init_values = + (WASMArrayNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + len = init_values->length; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW) { + array_init_value = init_values->elem_data; + } + } + + array_obj = instantiate_array_global_recursive( + module, module_inst, type_idx, flag, len, array_init_value, + init_values, error_buf, error_buf_size); + + global->initial_value.gc_obj = (void *)array_obj; + break; + } + case INIT_EXPR_TYPE_I31_NEW: + { + global->initial_value.gc_obj = + (wasm_obj_t)wasm_i31_obj_new(init_expr->u.unary.v.i32); + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + default: + global->initial_value = init_expr->u.unary.v; + break; + } + + global++; + } + + bh_assert((uint32)(global - globals) == global_count); + bh_assert(global_data_offset == module->global_data_size); + (void)module_inst; + return globals; +fail: + wasm_runtime_free(globals); + return NULL; +} + +/** + * Return export function count in module export section. + */ +static uint32 +get_export_count(const WASMModule *module, uint8 kind) +{ WASMExport *export = module->exports; uint32 count = 0, i; - for (i = 0; i < module->export_count; i++, export++) - if (export->kind == EXPORT_KIND_FUNC) + for (i = 0; i < module->export_count; i++, export ++) + if (export->kind == kind) count++; return count; @@ -501,720 +1446,3735 @@ static void export_functions_deinstantiate(WASMExportFuncInstance *functions) { if (functions) - wasm_free(functions); + wasm_runtime_free(functions); +} + +static int +cmp_export_func_inst(const void *a, const void *b) +{ + const WASMExportFuncInstance *export_func1 = + (const WASMExportFuncInstance *)a; + const WASMExportFuncInstance *export_func2 = + (const WASMExportFuncInstance *)b; + + return strcmp(export_func1->name, export_func2->name); } /** * Instantiate export functions in a module. */ -static WASMExportFuncInstance* +static WASMExportFuncInstance * export_functions_instantiate(const WASMModule *module, WASMModuleInstance *module_inst, - uint32 export_func_count, - char *error_buf, uint32 error_buf_size) + uint32 export_func_count, char *error_buf, + uint32 error_buf_size) { WASMExportFuncInstance *export_funcs, *export_func; WASMExport *export = module->exports; uint32 i; - uint64 total_size = sizeof(WASMExportFuncInstance) * (uint64)export_func_count; + uint64 total_size = + sizeof(WASMExportFuncInstance) * (uint64)export_func_count; - if (total_size >= UINT32_MAX - || !(export_func = export_funcs = wasm_malloc((uint32)total_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate export function failed: " - "allocate memory failed."); + if (!(export_func = export_funcs = + runtime_malloc(total_size, error_buf, error_buf_size))) { return NULL; } - memset(export_funcs, 0, (uint32)total_size); - - for (i = 0; i < module->export_count; i++, export++) + for (i = 0; i < module->export_count; i++, export ++) if (export->kind == EXPORT_KIND_FUNC) { export_func->name = export->name; - export_func->function = &module_inst->functions[export->index]; + export_func->function = &module_inst->e->functions[export->index]; export_func++; } bh_assert((uint32)(export_func - export_funcs) == export_func_count); + + qsort(export_funcs, export_func_count, sizeof(WASMExportFuncInstance), + cmp_export_func_inst); return export_funcs; } -static bool -execute_post_inst_function(WASMModuleInstance *module_inst) +#if WASM_ENABLE_TAGS != 0 +/** + * Destroy export function instances. + */ +static void +export_tags_deinstantiate(WASMExportTagInstance *tags) { - WASMFunctionInstance *post_inst_func = NULL; - WASMType *post_inst_func_type; + if (tags) + wasm_runtime_free(tags); +} + +/** + * Instantiate export functions in a module. + */ +static WASMExportTagInstance * +export_tags_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_tag_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportTagInstance *export_tags, *export_tag; + WASMExport *export = module->exports; uint32 i; + uint64 total_size = + sizeof(WASMExportTagInstance) * (uint64)export_tag_count; - 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 (!(export_tag = export_tags = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } - if (!post_inst_func) - /* Not found */ - return true; + for (i = 0; i < module->export_count; i++, export ++) + if (export->kind == EXPORT_KIND_TAG) { + export_tag->name = export->name; - 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; + bh_assert(module_inst->e->tags); + + export_tag->tag = &module_inst->e->tags[export->index]; + export_tag++; + } - return wasm_create_exec_env_and_call_function(module_inst, post_inst_func, - 0, NULL); + bh_assert((uint32)(export_tag - export_tags) == export_tag_count); + return export_tags; } +#endif /* end of WASM_ENABLE_TAGS != 0 */ -static bool -execute_start_function(WASMModuleInstance *module_inst) +#if WASM_ENABLE_MULTI_MEMORY != 0 +static void +export_memories_deinstantiate(WASMExportMemInstance *memories) { - WASMFunctionInstance *func = module_inst->start_function; + if (memories) + wasm_runtime_free(memories); +} - if (!func) - return true; +static WASMExportMemInstance * +export_memories_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_mem_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportMemInstance *export_memories, *export_memory; + WASMExport *export = module->exports; + uint32 i; + uint64 total_size = + sizeof(WASMExportMemInstance) * (uint64)export_mem_count; - bh_assert(!func->is_import_func && func->param_cell_num == 0 - && func->ret_cell_num == 0); + if (!(export_memory = export_memories = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + for (i = 0; i < module->export_count; i++, export ++) + if (export->kind == EXPORT_KIND_MEMORY) { + export_memory->name = export->name; + export_memory->memory = module_inst->memories[export->index]; + export_memory++; + } - return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL); + bh_assert((uint32)(export_memory - export_memories) == export_mem_count); + return export_memories; } +#endif /* end of if WASM_ENABLE_MULTI_MEMORY != 0 */ -/** - * Instantiate module - */ -WASMModuleInstance* -wasm_instantiate(WASMModule *module, - uint32 stack_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size) +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +export_globals_deinstantiate(WASMExportGlobInstance *globals) { - WASMModuleInstance *module_inst; - WASMTableSeg *table_seg; - WASMDataSeg *data_seg; - WASMGlobalInstance *globals = NULL, *global; - uint32 global_count, global_data_size = 0, i, j; - uint32 base_offset, length, memory_size; - uint8 *global_data, *global_data_end; - uint8 *memory_data; - uint32 *table_data; + if (globals) + wasm_runtime_free(globals); +} - if (!module) +static WASMExportGlobInstance * +export_globals_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_glob_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportGlobInstance *export_globals, *export_global; + WASMExport *export = module->exports; + uint32 i; + uint64 total_size = + sizeof(WASMExportGlobInstance) * (uint64)export_glob_count; + + if (!(export_global = export_globals = + runtime_malloc(total_size, error_buf, error_buf_size))) { return NULL; + } - /* Check heap size */ - heap_size = align_uint(heap_size, 8); - if (heap_size == 0) - heap_size = APP_HEAP_SIZE_DEFAULT; - if (heap_size < APP_HEAP_SIZE_MIN) - heap_size = APP_HEAP_SIZE_MIN; - if (heap_size > APP_HEAP_SIZE_MAX) - heap_size = APP_HEAP_SIZE_MAX; + for (i = 0; i < module->export_count; i++, export ++) + if (export->kind == EXPORT_KIND_GLOBAL) { + export_global->name = export->name; + export_global->global = &module_inst->e->globals[export->index]; + export_global++; + } - /* Instantiate global firstly to get the mutable data size */ - global_count = module->import_global_count + module->global_count; - if (global_count && - !(globals = globals_instantiate(module, - &global_data_size, - error_buf, error_buf_size))) - return NULL; + bh_assert((uint32)(export_global - export_globals) == export_glob_count); + return export_globals; +} - /* Allocate the memory */ - if (!(module_inst = wasm_malloc((uint32)sizeof(WASMModuleInstance)))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate module failed: allocate memory failed."); - globals_deinstantiate(globals); - return NULL; - } +#endif /* end of if WASM_ENABLE_MULTI_MODULE != 0 */ - memset(module_inst, 0, (uint32)sizeof(WASMModuleInstance)); - module_inst->global_count = global_count; - module_inst->globals = globals; +static WASMFunctionInstance * +lookup_post_instantiate_func(WASMModuleInstance *module_inst, + const char *func_name) +{ + WASMFunctionInstance *func; + WASMFuncType *func_type; - module_inst->memory_count = - module->import_memory_count + module->memory_count; - module_inst->table_count = - module->import_table_count + module->table_count; - module_inst->function_count = - module->import_function_count + module->function_count; - module_inst->export_func_count = get_export_function_count(module); + if (!(func = wasm_lookup_function(module_inst, func_name))) + /* Not found */ + return NULL; - /* Instantiate memories/tables/functions */ - if (((module_inst->memory_count > 0 || global_count > 0) - && !(module_inst->memories = - memories_instantiate(module, global_data_size, - heap_size, error_buf, error_buf_size))) - || (module_inst->table_count > 0 - && !(module_inst->tables = tables_instantiate(module, - error_buf, - error_buf_size))) - || (module_inst->function_count > 0 - && !(module_inst->functions = functions_instantiate(module, - error_buf, - error_buf_size))) - || (module_inst->export_func_count > 0 - && !(module_inst->export_functions = export_functions_instantiate( - module, module_inst, module_inst->export_func_count, - error_buf, error_buf_size)))) { - wasm_deinstantiate(module_inst); + 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; - } - if (module_inst->memory_count || global_count > 0) { - WASMMemoryInstance *memory; + return func; +} - memory = module_inst->default_memory = module_inst->memories[0]; - memory_data = module_inst->default_memory->memory_data; +static bool +execute_post_instantiate_functions(WASMModuleInstance *module_inst, + bool is_sub_inst, WASMExecEnv *exec_env_main) +{ + 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 + WASMModuleInstanceCommon *module_inst_main = NULL; +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + WASMExecEnv *exec_env = NULL, *exec_env_created = NULL; + bool ret = false; - /* fix import memoryBase */ - globals_instantiate_fix(globals, module, module_inst); +#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 - /* Initialize the global data */ - global_data = memory->global_data; - global_data_end = global_data + global_data_size; - global = globals; + /* 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 (!start_func && !initialize_func && !post_inst_func + && !call_ctors_func) { + /* No post instantiation functions to call */ + return true; + } + + if (is_sub_inst) { + bh_assert(exec_env_main); +#ifdef OS_ENABLE_HW_BOUND_CHECK + /* May come from pthread_create_wrapper, thread_spawn_wrapper and + wasm_cluster_spawn_exec_env. If it comes from the former two, + the exec_env_tls must be not NULL and equal to exec_env_main, + else if it comes from the last one, it may be NULL. */ + if (exec_env_tls) + bh_assert(exec_env_tls == exec_env_main); +#endif + exec_env = exec_env_main; + + /* Temporarily replace parent exec_env'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_main->module_inst; + wasm_exec_env_set_module_inst(exec_env, + (WASMModuleInstanceCommon *)module_inst); + } + else { + /* Try using the existing exec_env */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = exec_env_tls; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) + exec_env = wasm_clusters_search_exec_env( + (WASMModuleInstanceCommon *)module_inst); +#endif + if (!exec_env) { + if (!(exec_env = exec_env_created = wasm_exec_env_create( + (WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } + } + else { + /* Temporarily replace exec_env's module inst with current + module inst to ensure that the exec_env's module inst + is the correct one. */ + module_inst_main = exec_env->module_inst; + wasm_exec_env_set_module_inst( + exec_env, (WASMModuleInstanceCommon *)module_inst); + } + } + + /* Execute start function for both main instance and sub instance */ + if (start_func && !wasm_call_function(exec_env, start_func, 0, NULL)) { + goto fail; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + if (initialize_func + && !wasm_call_function(exec_env, initialize_func, 0, NULL)) { + goto fail; + } +#else + (void)initialize_func; +#endif + + 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: + if (is_sub_inst) { + /* Restore the parent exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env_main, module_inst_main); + } + else { + if (module_inst_main) + /* Restore the existing exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env, module_inst_main); + if (exec_env_created) + wasm_exec_env_destroy(exec_env_created); + } + + return ret; +} + +static bool +execute_malloc_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, + WASMFunctionInstance *malloc_func, + WASMFunctionInstance *retain_func, uint64 size, + uint64 *p_result) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + WASMExecEnv *exec_env_created = NULL; + WASMModuleInstanceCommon *module_inst_old = NULL; + union { + uint32 u32[3]; + uint64 u64; + } argv; + uint32 argc; + bool ret; +#if WASM_ENABLE_MEMORY64 != 0 + bool is_memory64 = module_inst->memories[0]->is_memory64; + if (is_memory64) { + argc = 2; + PUT_I64_TO_ADDR(&argv.u64, size); + } + else +#endif + { + argc = 1; + argv.u32[0] = (uint32)size; + } + + /* if __retain is exported, then this module is compiled by + assemblyscript, the memory should be managed by as's runtime, + in this case we need to call the retain function after malloc + the memory */ + if (retain_func) { + /* the malloc function from assemblyscript is: + function __new(size: usize, id: u32) + id = 0 means this is an ArrayBuffer object */ + argv.u32[argc] = 0; + argc++; + } + + if (exec_env) { +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (exec_env_tls) { + bh_assert(exec_env_tls == exec_env); + } +#endif + bh_assert(exec_env->module_inst + == (WASMModuleInstanceCommon *)module_inst); + } + else { + /* Try using the existing exec_env */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = exec_env_tls; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) + exec_env = wasm_clusters_search_exec_env( + (WASMModuleInstanceCommon *)module_inst); +#endif + if (!exec_env) { + if (!(exec_env = exec_env_created = wasm_exec_env_create( + (WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } + } + else { + /* Temporarily replace exec_env's module inst with current + module inst to ensure that the exec_env's module inst + is the correct one. */ + module_inst_old = exec_env->module_inst; + wasm_exec_env_set_module_inst( + exec_env, (WASMModuleInstanceCommon *)module_inst); + } + } + + ret = wasm_call_function(exec_env, malloc_func, argc, argv.u32); + + if (retain_func && ret) + ret = wasm_call_function(exec_env, retain_func, 1, argv.u32); + + if (module_inst_old) + /* Restore the existing exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env, module_inst_old); + + if (exec_env_created) + wasm_exec_env_destroy(exec_env_created); + + if (ret) { +#if WASM_ENABLE_MEMORY64 != 0 + if (is_memory64) + *p_result = argv.u64; + else +#endif + { + *p_result = argv.u32[0]; + } + } + return ret; +} + +static bool +execute_free_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env, + WASMFunctionInstance *free_func, uint64 offset) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + WASMExecEnv *exec_env_created = NULL; + WASMModuleInstanceCommon *module_inst_old = NULL; + union { + uint32 u32[2]; + uint64 u64; + } argv; + uint32 argc; + bool ret; + +#if WASM_ENABLE_MEMORY64 != 0 + if (module_inst->memories[0]->is_memory64) { + PUT_I64_TO_ADDR(&argv.u64, offset); + argc = 2; + } + else +#endif + { + argv.u32[0] = (uint32)offset; + argc = 1; + } + + if (exec_env) { +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (exec_env_tls) { + bh_assert(exec_env_tls == exec_env); + } +#endif + bh_assert(exec_env->module_inst + == (WASMModuleInstanceCommon *)module_inst); + } + else { + /* Try using the existing exec_env */ +#ifdef OS_ENABLE_HW_BOUND_CHECK + exec_env = exec_env_tls; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + if (!exec_env) + exec_env = wasm_clusters_search_exec_env( + (WASMModuleInstanceCommon *)module_inst); +#endif + if (!exec_env) { + if (!(exec_env = exec_env_created = wasm_exec_env_create( + (WASMModuleInstanceCommon *)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } + } + else { + /* Temporarily replace exec_env's module inst with current + module inst to ensure that the exec_env's module inst + is the correct one. */ + module_inst_old = exec_env->module_inst; + wasm_exec_env_set_module_inst( + exec_env, (WASMModuleInstanceCommon *)module_inst); + } + } + + ret = wasm_call_function(exec_env, free_func, argc, argv.u32); + + if (module_inst_old) + /* Restore the existing exec_env's module inst */ + wasm_exec_env_restore_module_inst(exec_env, module_inst_old); + + if (exec_env_created) + wasm_exec_env_destroy(exec_env_created); + + return ret; +} + +static bool +check_linked_symbol(WASMModuleInstance *module_inst, char *error_buf, + uint32 error_buf_size) +{ + WASMModule *module = module_inst->module; + uint32 i; + + for (i = 0; i < module->import_function_count; i++) { + WASMFunctionImport *func = + &((module->import_functions + i)->u.function); + if (!func->func_ptr_linked +#if WASM_ENABLE_MULTI_MODULE != 0 + && !func->import_func_linked +#endif + ) { + LOG_WARNING("warning: failed to link import function (%s, %s)", + func->module_name, func->field_name); + } + } + + for (i = 0; i < module->import_global_count; i++) { + WASMGlobalImport *global = &((module->import_globals + i)->u.global); + + if (!global->is_linked) { +#if WASM_ENABLE_SPEC_TEST != 0 + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return false; +#else + set_error_buf_v(error_buf, error_buf_size, + "failed to link import global (%s, %s)", + global->module_name, global->field_name); + return false; +#endif /* WASM_ENABLE_SPEC_TEST != 0 */ + } + } + + for (i = 0; i < module->import_table_count; i++) { + WASMTableImport *table = &((module->import_tables + i)->u.table); + + if (!wasm_runtime_is_built_in_module(table->module_name) +#if WASM_ENABLE_MULTI_MODULE != 0 + && !table->import_table_linked +#endif + ) { + set_error_buf_v(error_buf, error_buf_size, + "failed to link import table (%s, %s)", + table->module_name, table->field_name); + return false; + } + } + + for (i = 0; i < module->import_memory_count; i++) { + WASMMemoryImport *memory = &((module->import_memories + i)->u.memory); + + if (!wasm_runtime_is_built_in_module(memory->module_name) +#if WASM_ENABLE_MULTI_MODULE != 0 + && !memory->import_memory_linked +#endif + ) { + set_error_buf_v(error_buf, error_buf_size, + "failed to link import memory (%s, %s)", + memory->module_name, memory->field_name); + return false; + } + } + +#if WASM_ENABLE_MULTI_MODULE != 0 +#if WASM_ENABLE_TAGS != 0 + for (i = 0; i < module->import_tag_count; i++) { + WASMTagImport *tag = &((module->import_tags + i)->u.tag); + + if (!tag->import_tag_linked) { + set_error_buf_v(error_buf, error_buf_size, + "failed to link import tag (%s, %s)", + tag->module_name, tag->field_name); + return false; + } + } +#endif /* WASM_ENABLE_TAGS != 0 */ +#endif + + return true; +} + +#if WASM_ENABLE_JIT != 0 +static bool +init_func_ptrs(WASMModuleInstance *module_inst, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + void **func_ptrs; + uint64 total_size = (uint64)sizeof(void *) * module_inst->e->function_count; + + /* Allocate memory */ + if (!(func_ptrs = module_inst->func_ptrs = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set import function pointers */ + for (i = 0; i < module->import_function_count; i++, func_ptrs++) { + WASMFunctionImport *import_func = + &module->import_functions[i].u.function; + /* TODO: handle multi module */ + *func_ptrs = import_func->func_ptr_linked; + } + + /* 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 */ + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 +static uint32 +get_smallest_type_idx(WASMModule *module, WASMFuncType *func_type) +{ + uint32 i; + + for (i = 0; i < module->type_count; i++) { + if (func_type == (WASMFuncType *)module->types[i]) + return i; + } + + bh_assert(0); + return -1; +} + +static bool +init_func_type_indexes(WASMModuleInstance *module_inst, char *error_buf, + uint32 error_buf_size) +{ + uint32 i; + uint64 total_size = (uint64)sizeof(uint32) * module_inst->e->function_count; + + /* Allocate memory */ + if (!(module_inst->func_type_indexes = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module_inst->e->function_count; i++) { + WASMFunctionInstance *func_inst = module_inst->e->functions + i; + WASMFuncType *func_type = func_inst->is_import_func + ? func_inst->u.func_import->func_type + : func_inst->u.func->func_type; + module_inst->func_type_indexes[i] = + get_smallest_type_idx(module_inst->module, func_type); + } + + return true; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 */ + +#if WASM_ENABLE_GC != 0 +void * +wasm_create_func_obj(WASMModuleInstance *module_inst, uint32 func_idx, + bool throw_exce, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = module_inst->module; + WASMRttTypeRef rtt_type; + WASMFuncObjectRef func_obj; + WASMFuncType *func_type; + uint32 type_idx; + + if (throw_exce) { + error_buf = module_inst->cur_exception; + error_buf_size = sizeof(module_inst->cur_exception); + } + + if (func_idx >= module->import_function_count + module->function_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown function %d", + func_idx); + return NULL; + } + + if (func_idx < module->import_function_count) { + func_type = module->import_functions[func_idx].u.function.func_type; + type_idx = module->import_functions[func_idx].u.function.type_idx; + } + else { + func_type = module->functions[func_idx - module->import_function_count] + ->func_type; + type_idx = module->functions[func_idx - module->import_function_count] + ->type_idx; + } + + if (!(rtt_type = wasm_rtt_type_new((WASMType *)func_type, type_idx, + module->rtt_types, module->type_count, + &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, "create rtt object failed"); + return NULL; + } + + if (!(func_obj = wasm_func_obj_new_internal( + module_inst->e->common.gc_heap_handle, rtt_type, func_idx))) { + set_error_buf(error_buf, error_buf_size, "create func object failed"); + return NULL; + } + + return func_obj; +} + +static bool +wasm_global_traverse_gc_rootset(WASMModuleInstance *module_inst, void *heap) +{ + WASMGlobalInstance *global = module_inst->e->globals; + WASMGlobalInstance *global_end = global + module_inst->e->global_count; + uint8 *global_data = module_inst->global_data; + WASMObjectRef gc_obj; + + while (global < global_end) { + if (wasm_is_type_reftype(global->type)) { + gc_obj = GET_REF_FROM_ADDR( + (uint32 *)(global_data + global->data_offset)); + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } + global++; + } + return true; +} + +static bool +wasm_table_traverse_gc_rootset(WASMModuleInstance *module_inst, void *heap) +{ + WASMTableInstance **tables = module_inst->tables, *table; + uint32 table_count = module_inst->table_count, i, j; + WASMObjectRef gc_obj, *table_elems; + + for (i = 0; i < table_count; i++) { + table = tables[i]; + table_elems = (WASMObjectRef *)table->elems; + for (j = 0; j < table->cur_size; j++) { + gc_obj = table_elems[j]; + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } + } + + return true; +} + +static bool +local_object_refs_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) +{ + WASMLocalObjectRef *r; + WASMObjectRef gc_obj; + + for (r = exec_env->cur_local_object_ref; r; r = r->prev) { + gc_obj = r->val; + if (wasm_obj_is_created_from_heap(gc_obj)) { + if (0 != mem_allocator_add_root((mem_allocator_t)heap, gc_obj)) + return false; + } + } + return true; +} + +bool +wasm_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + bool ret; + + ret = wasm_global_traverse_gc_rootset(module_inst, heap); + if (!ret) + return ret; + + ret = wasm_table_traverse_gc_rootset(module_inst, heap); + if (!ret) + return ret; + + ret = local_object_refs_traverse_gc_rootset(exec_env, heap); + if (!ret) + return ret; + + return wasm_interp_traverse_gc_rootset(exec_env, heap); +} +#endif /* end of WASM_ENABLE_GC != 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 */ + (void)first_time_set; + 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, 10000); + 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 + */ +WASMModuleInstance * +wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, + WASMExecEnv *exec_env_main, + const struct InstantiationArgs2 *args, char *error_buf, + uint32 error_buf_size) +{ + WASMModuleInstance *module_inst; + WASMGlobalInstance *globals = NULL, *global; + WASMTableInstance *first_table; + uint32 global_count, i; + uint32 length, extra_info_offset; + mem_offset_t base_offset; + uint32 module_inst_struct_size = + offsetof(WASMModuleInstance, global_table_data.bytes); + uint64 module_inst_mem_inst_size; + uint64 total_size, table_size = 0; + uint8 *global_data, *global_data_end; +#if WASM_ENABLE_MULTI_MODULE != 0 + bool ret = false; +#endif + const bool is_sub_inst = parent != NULL; + uint32 stack_size = args->v1.default_stack_size; + uint32 heap_size = args->v1.host_managed_heap_size; + uint32 max_memory_pages = args->v1.max_memory_pages; + + if (!module) + return NULL; + + /* Check the heap size */ + heap_size = align_uint(heap_size, 8); + if (heap_size > APP_HEAP_SIZE_MAX) + heap_size = APP_HEAP_SIZE_MAX; + + module_inst_mem_inst_size = + sizeof(WASMMemoryInstance) + * ((uint64)module->import_memory_count + module->memory_count); + +#if WASM_ENABLE_JIT != 0 + /* If the module doesn't have memory, reserve one mem_info space + with empty content to align with llvm jit compiler */ + if (module_inst_mem_inst_size == 0) + module_inst_mem_inst_size = (uint64)sizeof(WASMMemoryInstance); +#endif + + /* Size of module inst, memory instances and global data */ + total_size = (uint64)module_inst_struct_size + module_inst_mem_inst_size + + module->global_data_size; + + /* Calculate the size of table data */ + for (i = 0; i < module->import_table_count; i++) { + WASMTableImport *import_table = &module->import_tables[i].u.table; + table_size += offsetof(WASMTableInstance, elems); +#if WASM_ENABLE_MULTI_MODULE != 0 + table_size += (uint64)sizeof(table_elem_type_t) + * import_table->table_type.max_size; +#else + table_size += (uint64)sizeof(table_elem_type_t) + * (import_table->table_type.possible_grow + ? import_table->table_type.max_size + : import_table->table_type.init_size); +#endif + } + for (i = 0; i < module->table_count; i++) { + WASMTable *table = module->tables + i; + table_size += offsetof(WASMTableInstance, elems); +#if WASM_ENABLE_MULTI_MODULE != 0 + table_size += + (uint64)sizeof(table_elem_type_t) * table->table_type.max_size; +#else + table_size += + (uint64)sizeof(table_elem_type_t) + * (table->table_type.possible_grow ? table->table_type.max_size + : table->table_type.init_size); +#endif + } + total_size += table_size; + + /* The offset of WASMModuleInstanceExtra, make it 8-byte aligned */ + total_size = (total_size + 7LL) & ~7LL; + extra_info_offset = (uint32)total_size; + total_size += sizeof(WASMModuleInstanceExtra); + + /* Allocate the memory for module instance with memory instances, + global data, table data appended at the end */ + if (!(module_inst = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } + + module_inst->module_type = Wasm_Module_Bytecode; + module_inst->module = module; + module_inst->e = + (WASMModuleInstanceExtra *)((uint8 *)module_inst + extra_info_offset); +#if WASM_ENABLE_THREAD_MGR != 0 + if (os_mutex_init(&module_inst->e->common.exception_lock) != 0) { + wasm_runtime_free(module_inst); + return NULL; + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + module_inst->e->sub_module_inst_list = + &module_inst->e->sub_module_inst_list_head; + ret = wasm_runtime_sub_module_instantiate( + (WASMModuleCommon *)module, (WASMModuleInstanceCommon *)module_inst, + args, error_buf, error_buf_size); + if (!ret) { + LOG_DEBUG("build a sub module list failed"); + goto fail; + } +#endif + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (module->data_seg_count > 0) { + module_inst->e->common.data_dropped = + bh_bitmap_new(0, module->data_seg_count); + if (module_inst->e->common.data_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + for (i = 0; i < module->data_seg_count; i++) { + if (!module->data_segments[i]->is_passive) + bh_bitmap_set_bit(module_inst->e->common.data_dropped, i); + } + } +#endif +#if WASM_ENABLE_REF_TYPES != 0 + if (module->table_seg_count > 0) { + module_inst->e->common.elem_dropped = + bh_bitmap_new(0, module->table_seg_count); + if (module_inst->e->common.elem_dropped == NULL) { + LOG_DEBUG("failed to allocate bitmaps"); + set_error_buf(error_buf, error_buf_size, + "failed to allocate bitmaps"); + goto fail; + } + for (i = 0; i < module->table_seg_count; i++) { + if (wasm_elem_is_active(module->table_segments[i].mode) + || wasm_elem_is_declarative(module->table_segments[i].mode)) + bh_bitmap_set_bit(module_inst->e->common.elem_dropped, i); + } + } +#endif + +#if WASM_ENABLE_GC != 0 + if (!is_sub_inst) { + uint32 gc_heap_size = wasm_runtime_get_gc_heap_size_default(); + + if (gc_heap_size < GC_HEAP_SIZE_MIN) + gc_heap_size = GC_HEAP_SIZE_MIN; + if (gc_heap_size > GC_HEAP_SIZE_MAX) + gc_heap_size = GC_HEAP_SIZE_MAX; + + module_inst->e->common.gc_heap_pool = + runtime_malloc(gc_heap_size, error_buf, error_buf_size); + if (!module_inst->e->common.gc_heap_pool) + goto fail; + + module_inst->e->common.gc_heap_handle = mem_allocator_create( + module_inst->e->common.gc_heap_pool, gc_heap_size); + if (!module_inst->e->common.gc_heap_handle) + goto fail; + } +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (!(module_inst->frames = runtime_malloc((uint64)sizeof(Vector), + error_buf, error_buf_size))) { + goto fail; + } +#endif + + /* Instantiate global firstly to get the mutable data size */ + global_count = module->import_global_count + module->global_count; + if (global_count + && !(globals = globals_instantiate(module, module_inst, error_buf, + error_buf_size))) { + goto fail; + } + module_inst->e->global_count = global_count; + module_inst->e->globals = globals; + module_inst->global_data = (uint8 *)module_inst + module_inst_struct_size + + module_inst_mem_inst_size; + module_inst->global_data_size = module->global_data_size; + first_table = (WASMTableInstance *)(module_inst->global_data + + module->global_data_size); + + module_inst->memory_count = + module->import_memory_count + module->memory_count; + module_inst->table_count = module->import_table_count + module->table_count; + module_inst->e->function_count = + module->import_function_count + module->function_count; +#if WASM_ENABLE_TAGS != 0 + module_inst->e->tag_count = module->import_tag_count + module->tag_count; +#endif + + /* export */ + module_inst->export_func_count = get_export_count(module, EXPORT_KIND_FUNC); +#if WASM_ENABLE_MULTI_MEMORY != 0 + module_inst->export_memory_count = + get_export_count(module, EXPORT_KIND_MEMORY); +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + module_inst->export_table_count = + get_export_count(module, EXPORT_KIND_TABLE); +#if WASM_ENABLE_TAGS != 0 + module_inst->e->export_tag_count = + get_export_count(module, EXPORT_KIND_TAG); +#endif + module_inst->export_global_count = + get_export_count(module, EXPORT_KIND_GLOBAL); +#endif + + /* Instantiate memories/tables/functions/tags */ + if ((module_inst->memory_count > 0 + && !(module_inst->memories = memories_instantiate( + module, module_inst, parent, heap_size, max_memory_pages, + error_buf, error_buf_size))) + || (module_inst->table_count > 0 + && !(module_inst->tables = + tables_instantiate(module, module_inst, first_table, + error_buf, error_buf_size))) + || (module_inst->e->function_count > 0 + && !(module_inst->e->functions = functions_instantiate( + module, module_inst, error_buf, error_buf_size))) + || (module_inst->export_func_count > 0 + && !(module_inst->export_functions = export_functions_instantiate( + module, module_inst, module_inst->export_func_count, + error_buf, error_buf_size))) +#if WASM_ENABLE_TAGS != 0 + || (module_inst->e->tag_count > 0 + && !(module_inst->e->tags = tags_instantiate( + module, module_inst, error_buf, error_buf_size))) + || (module_inst->e->export_tag_count > 0 + && !(module_inst->e->export_tags = export_tags_instantiate( + module, module_inst, module_inst->e->export_tag_count, + error_buf, error_buf_size))) +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + || (module_inst->export_global_count > 0 + && !(module_inst->export_globals = export_globals_instantiate( + module, module_inst, module_inst->export_global_count, + error_buf, error_buf_size))) +#endif +#if WASM_ENABLE_MULTI_MEMORY != 0 + || (module_inst->export_memory_count > 0 + && !(module_inst->export_memories = export_memories_instantiate( + module, module_inst, module_inst->export_memory_count, + error_buf, error_buf_size))) +#endif +#if WASM_ENABLE_JIT != 0 + || (module_inst->e->function_count > 0 + && !init_func_ptrs(module_inst, module, error_buf, error_buf_size)) +#endif +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 + || (module_inst->e->function_count > 0 + && !init_func_type_indexes(module_inst, error_buf, error_buf_size)) +#endif + ) { + goto fail; + } + if (global_count > 0) { + /* Initialize the global data */ + global_data = module_inst->global_data; + global_data_end = global_data + module->global_data_size; + global = globals; for (i = 0; i < global_count; i++, global++) { switch (global->type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: - *(int32*)global_data = global->initial_value.i32; +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif + *(int32 *)global_data = global->initial_value.i32; global_data += sizeof(int32); break; - case VALUE_TYPE_I64: - case VALUE_TYPE_F64: - bh_memcpy_s(global_data, (uint32)(global_data_end - global_data), - &global->initial_value.i64, sizeof(int64)); - global_data += sizeof(int64); + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_memcpy_s(global_data, + (uint32)(global_data_end - global_data), + &global->initial_value.i64, sizeof(int64)); + global_data += sizeof(int64); + break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + bh_memcpy_s(global_data, (uint32)sizeof(V128), + &global->initial_value.v128, sizeof(V128)); + global_data += sizeof(V128); + break; +#endif +#if WASM_ENABLE_GC != 0 + case VALUE_TYPE_EXTERNREF: + /* the initial value should be a null reference */ + bh_assert(global->initial_value.gc_obj == NULL_REF); + STORE_PTR((void **)global_data, NULL_REF); + global_data += sizeof(void *); + break; +#endif + default: + { +#if WASM_ENABLE_GC != 0 + InitializerExpression *global_init = NULL; + bh_assert(wasm_is_type_reftype(global->type)); + + if (i >= module->import_global_count) { + global_init = + &module->globals[i - module->import_global_count] + .init_expr; + } + + if (global->type == REF_TYPE_NULLFUNCREF + || global->type == REF_TYPE_NULLEXTERNREF + || global->type == REF_TYPE_NULLREF) { + STORE_PTR((void **)global_data, NULL_REF); + global_data += sizeof(void *); + break; + } + + /* We can't create funcref obj during global instantiation + * since the functions are not instantiated yet, so we need + * to defer the initialization here */ + if (global_init + && (global_init->init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST) + && wasm_reftype_is_subtype_of( + global->type, global->ref_type, REF_TYPE_FUNCREF, + NULL, module_inst->module->types, + module_inst->module->type_count)) { + WASMFuncObjectRef func_obj = NULL; + /* UINT32_MAX indicates that it is a null reference */ + if ((uint32)global->initial_value.i32 != UINT32_MAX) { + if (!(func_obj = wasm_create_func_obj( + module_inst, global->initial_value.i32, + false, error_buf, error_buf_size))) + goto fail; + } + STORE_PTR((void **)global_data, func_obj); + global_data += sizeof(void *); + /* Also update the initial_value since other globals may + * refer to this */ + global->initial_value.gc_obj = (wasm_obj_t)func_obj; + break; + } + else { + STORE_PTR((void **)global_data, + global->initial_value.gc_obj); + global_data += sizeof(void *); + break; + } +#endif + bh_assert(0); + break; + } + } + } + bh_assert(global_data == global_data_end); + } + + if (!check_linked_symbol(module_inst, error_buf, error_buf_size)) { + goto fail; + } + + /* Initialize the memory data with data segment section */ + for (i = 0; i < module->data_seg_count; i++) { + WASMMemoryInstance *memory = NULL; + uint8 *memory_data = NULL; + uint64 memory_size = 0; + WASMDataSeg *data_seg = module->data_segments[i]; + WASMValue offset_value; + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (data_seg->is_passive) + continue; +#endif + if (is_sub_inst) + /* Ignore setting memory init data if the memory has been + initialized */ + continue; + + /* has check it in loader */ + memory = module_inst->memories[data_seg->memory_index]; + bh_assert(memory); + + memory_data = memory->memory_data; + memory_size = + (uint64)memory->num_bytes_per_page * memory->cur_page_count; + bh_assert(memory_data || memory_size == 0); + + uint8 offset_flag = data_seg->base_offset.init_expr_type; + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || (memory->is_memory64 ? is_valid_i64_offset(offset_flag) + : is_valid_i32_offset(offset_flag))); + + if (!get_init_value_recursive(module, &data_seg->base_offset, globals, + &offset_value, error_buf, + error_buf_size)) { + goto fail; + } + + if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) { + if (!globals + || globals[data_seg->base_offset.u.unary.v.global_index].type + != (memory->is_memory64 ? VALUE_TYPE_I64 + : VALUE_TYPE_I32)) { + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + goto fail; + } + } + +#if WASM_ENABLE_MEMORY64 != 0 + if (memory->is_memory64) { + base_offset = (uint64)offset_value.i64; + } + else +#endif + { + base_offset = (uint32)offset_value.i32; + } + /* check offset */ + if (base_offset > memory_size) { +#if WASM_ENABLE_MEMORY64 != 0 + LOG_DEBUG("base_offset(%" PRIu64 ") > memory_size(%" PRIu64 ")", + base_offset, memory_size); +#else + LOG_DEBUG("base_offset(%u) > memory_size(%" PRIu64 ")", base_offset, + memory_size); +#endif +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); +#endif + goto fail; + } + + /* check offset + length(could be zero) */ + length = data_seg->data_length; + if ((uint64)base_offset + length > memory_size) { +#if WASM_ENABLE_MEMORY64 != 0 + LOG_DEBUG("base_offset(%" PRIu64 + ") + length(%d) > memory_size(%" PRIu64 ")", + base_offset, length, memory_size); +#else + LOG_DEBUG("base_offset(%u) + length(%d) > memory_size(%" PRIu64 ")", + base_offset, length, memory_size); +#endif +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); +#endif + goto fail; + } + + if (memory_data) { + bh_memcpy_s(memory_data + base_offset, + (uint32)(memory_size - base_offset), data_seg->data, + length); + } + } + +#if WASM_ENABLE_JIT != 0 && WASM_ENABLE_SHARED_HEAP != 0 +#if UINTPTR_MAX == UINT64_MAX + module_inst->e->shared_heap_start_off.u64 = UINT64_MAX; +#else + module_inst->e->shared_heap_start_off.u32[0] = UINT32_MAX; +#endif + module_inst->e->shared_heap = NULL; +#endif + +#if WASM_ENABLE_GC != 0 + /* Initialize the table data with init expr */ + for (i = 0; i < module->table_count; i++) { + WASMTable *table = module->tables + i; + WASMTableInstance *table_inst = module_inst->tables[i]; + table_elem_type_t *table_data; + uint32 j; + + if (table->init_expr.init_expr_type == 0) { + /* No table initializer */ + continue; + } + + table_data = table_inst->elems; + + bh_assert( + table->init_expr.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL + || table->init_expr.init_expr_type == INIT_EXPR_TYPE_FUNCREF_CONST + || table->init_expr.init_expr_type == INIT_EXPR_TYPE_REFNULL_CONST); + + if (table->init_expr.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + if (!check_global_init_expr(module, + table->init_expr.u.unary.v.global_index, + error_buf, error_buf_size)) { + goto fail; + } + + table->init_expr.u.unary.v.gc_obj = + globals[table->init_expr.u.unary.v.global_index] + .initial_value.gc_obj; + } + else if (table->init_expr.init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST) { + uint32 func_idx = table->init_expr.u.unary.v.ref_index; + if (func_idx != UINT32_MAX) { + if (!(table->init_expr.u.unary.v.gc_obj = + wasm_create_func_obj(module_inst, func_idx, false, + error_buf, error_buf_size))) + goto fail; + } + else { + table->init_expr.u.unary.v.gc_obj = NULL_REF; + } + } + else if (table->init_expr.init_expr_type + == INIT_EXPR_TYPE_REFNULL_CONST) { + table->init_expr.u.unary.v.gc_obj = NULL_REF; + } + + LOG_DEBUG("Init table [%d] elements from [%d] to [%d] as: %p", i, 0, + table_inst->cur_size, + (void *)table->init_expr.u.unary.v.gc_obj); + for (j = 0; j < table_inst->cur_size; j++) { + *(table_data + j) = table->init_expr.u.unary.v.gc_obj; + } + } +#endif /* end of WASM_ENABLE_GC != 0 */ + + /* Initialize the table data with table segment section */ + for (i = 0; module_inst->table_count > 0 && i < module->table_seg_count; + i++) { + WASMTableSeg *table_seg = module->table_segments + i; + /* has check it in loader */ + WASMTableInstance *table = module_inst->tables[table_seg->table_index]; + table_elem_type_t *table_data; + WASMValue offset_value; + uint32 j; +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + uint8 tbl_elem_type; + uint32 tbl_init_size, tbl_max_size; +#endif +#if WASM_ENABLE_GC != 0 + WASMRefType *tbl_elem_ref_type; +#endif + + bh_assert(table); + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + (void)wasm_runtime_get_table_inst_elem_type( + (WASMModuleInstanceCommon *)module_inst, table_seg->table_index, + &tbl_elem_type, +#if WASM_ENABLE_GC != 0 + &tbl_elem_ref_type, +#endif + &tbl_init_size, &tbl_max_size); + +#if WASM_ENABLE_GC == 0 + if (tbl_elem_type != VALUE_TYPE_FUNCREF + && tbl_elem_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); + goto fail; + } +#elif WASM_ENABLE_GC != 0 + if (!wasm_elem_is_declarative(table_seg->mode) + && !wasm_reftype_is_subtype_of( + table_seg->elem_type, table_seg->elem_ref_type, + table->elem_type, table->elem_ref_type.elem_ref_type, + module->types, module->type_count)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); + goto fail; + } +#endif + (void)tbl_init_size; + (void)tbl_max_size; +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + + table_data = table->elems; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (table_seg->table_index < module->import_table_count + && module_inst->e->table_insts_linked[table_seg->table_index]) { + table_data = + module_inst->e->table_insts_linked[table_seg->table_index] + ->elems; + } +#endif + bh_assert(table_data); + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + if (!wasm_elem_is_active(table_seg->mode)) + continue; +#endif + + uint8 offset_flag = table_seg->base_offset.init_expr_type; +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || offset_flag == INIT_EXPR_TYPE_FUNCREF_CONST + || offset_flag == INIT_EXPR_TYPE_REFNULL_CONST + || is_valid_i32_offset(offset_flag)); +#else + bh_assert(offset_flag == INIT_EXPR_TYPE_GET_GLOBAL + || is_valid_i32_offset(offset_flag)); +#endif + + if (!get_init_value_recursive(module, &table_seg->base_offset, globals, + &offset_value, error_buf, + error_buf_size)) { + goto fail; + } + + if (offset_flag == INIT_EXPR_TYPE_GET_GLOBAL) { + if (!globals + || globals[table_seg->base_offset.u.unary.v.global_index].type + != VALUE_TYPE_I32) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); + goto fail; + } + } + + /* check offset since length might negative */ + if ((uint32)offset_value.i32 > table->cur_size) { + LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", offset_value.i32, + table->cur_size); +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); +#endif + goto fail; + } + + /* check offset + length(could be zero) */ + length = table_seg->value_count; + if ((uint32)offset_value.i32 + length > table->cur_size) { + LOG_DEBUG("base_offset(%d) + length(%d)> table->cur_size(%d)", + offset_value.i32, length, table->cur_size); +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else + set_error_buf(error_buf, error_buf_size, + "type mismatch: elements segment does not fit"); +#endif + goto fail; + } + + for (j = 0; j < length; j++) { + InitializerExpression *init_expr = &table_seg->init_values[j]; + uint8 flag = init_expr->init_expr_type; + void *ref = NULL; + + /* const and get global init values should be resolved during + * loading */ + bh_assert((flag == INIT_EXPR_TYPE_GET_GLOBAL) + || (flag == INIT_EXPR_TYPE_REFNULL_CONST) + || ((flag >= INIT_EXPR_TYPE_FUNCREF_CONST) + && (flag <= INIT_EXPR_TYPE_EXTERN_CONVERT_ANY))); + + switch (flag) { + case INIT_EXPR_TYPE_REFNULL_CONST: + ref = NULL; + break; + case INIT_EXPR_TYPE_FUNCREF_CONST: + { +#if WASM_ENABLE_GC == 0 + ref = (void *)(uintptr_t)init_expr->u.unary.v.ref_index; +#else + WASMFuncObjectRef func_obj; + uint32 func_idx = init_expr->u.unary.v.ref_index; + /* UINT32_MAX indicates that it is a null reference */ + if (func_idx != UINT32_MAX) { + if (!(func_obj = wasm_create_func_obj( + module_inst, func_idx, false, error_buf, + error_buf_size))) { + goto fail; + } + ref = func_obj; + } + else { + ref = NULL_REF; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + break; + } +#if WASM_ENABLE_GC != 0 + case INIT_EXPR_TYPE_GET_GLOBAL: + { + if (!check_global_init_expr( + module, init_expr->u.unary.v.global_index, + error_buf, error_buf_size)) { + goto fail; + } + + ref = globals[init_expr->u.unary.v.global_index] + .initial_value.gc_obj; + break; + } + case INIT_EXPR_TYPE_STRUCT_NEW: + case INIT_EXPR_TYPE_STRUCT_NEW_DEFAULT: + { + WASMRttType *rtt_type; + WASMStructObjectRef struct_obj; + WASMStructType *struct_type; + WASMStructNewInitValues *init_values = NULL; + uint32 type_idx; + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + init_values = (WASMStructNewInitValues *) + init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + } + else { + type_idx = init_expr->u.unary.v.type_index; + } + + struct_type = (WASMStructType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)struct_type, type_idx, + module->rtt_types, module->type_count, + &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, + "create rtt object failed"); + goto fail; + } + + if (!(struct_obj = wasm_struct_obj_new_internal( + module_inst->e->common.gc_heap_handle, + rtt_type))) { + set_error_buf(error_buf, error_buf_size, + "create struct object failed"); + goto fail; + } + + if (flag == INIT_EXPR_TYPE_STRUCT_NEW) { + uint32 field_idx; + + bh_assert(init_values->count + == struct_type->field_count); + + for (field_idx = 0; field_idx < init_values->count; + field_idx++) { + wasm_struct_obj_set_field( + struct_obj, field_idx, + &init_values->fields[field_idx]); + } + } + + ref = struct_obj; + break; + } + case INIT_EXPR_TYPE_ARRAY_NEW: + case INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT: + case INIT_EXPR_TYPE_ARRAY_NEW_FIXED: + { + WASMRttType *rtt_type; + WASMArrayObjectRef array_obj; + WASMArrayType *array_type; + WASMArrayNewInitValues *init_values = NULL; + WASMValue *arr_init_val = NULL, empty_val = { 0 }; + uint32 type_idx, len; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_DEFAULT) { + type_idx = + init_expr->u.unary.v.array_new_default.type_index; + len = init_expr->u.unary.v.array_new_default.length; + arr_init_val = &empty_val; + } + else { + init_values = + (WASMArrayNewInitValues *)init_expr->u.unary.v.data; + type_idx = init_values->type_idx; + len = init_values->length; + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + arr_init_val = init_values->elem_data; + } + } + + array_type = (WASMArrayType *)module->types[type_idx]; + + if (!(rtt_type = wasm_rtt_type_new( + (WASMType *)array_type, type_idx, + module->rtt_types, module->type_count, + &module->rtt_type_lock))) { + set_error_buf(error_buf, error_buf_size, + "create rtt object failed"); + goto fail; + } + + if (!(array_obj = wasm_array_obj_new_internal( + module_inst->e->common.gc_heap_handle, rtt_type, + len, arr_init_val))) { + set_error_buf(error_buf, error_buf_size, + "create array object failed"); + goto fail; + } + + if (flag == INIT_EXPR_TYPE_ARRAY_NEW_FIXED) { + uint32 elem_idx; + + bh_assert(init_values); + + for (elem_idx = 0; elem_idx < len; elem_idx++) { + wasm_array_obj_set_elem( + array_obj, elem_idx, + &init_values->elem_data[elem_idx]); + } + } + + ref = array_obj; + + break; + } + case INIT_EXPR_TYPE_I31_NEW: + { + ref = + (wasm_obj_t)wasm_i31_obj_new(init_expr->u.unary.v.i32); + break; + } +#endif /* end of WASM_ENABLE_GC != 0 */ + } + + *(table_data + offset_value.i32 + j) = (table_elem_type_t)ref; + } + } + + /* Initialize the thread related data */ + if (stack_size == 0) + stack_size = DEFAULT_WASM_STACK_SIZE; + + module_inst->default_wasm_stack_size = stack_size; + + if (module->malloc_function != (uint32)-1) { + module_inst->e->malloc_function = + &module_inst->e->functions[module->malloc_function]; + } + + if (module->free_function != (uint32)-1) { + module_inst->e->free_function = + &module_inst->e->functions[module->free_function]; + } + + if (module->retain_function != (uint32)-1) { + module_inst->e->retain_function = + &module_inst->e->functions[module->retain_function]; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + /* The sub-instance will get the wasi_ctx from main-instance */ + if (!is_sub_inst) { + const WASIArguments *wasi_args = &args->wasi; + if (module->wasi_args.set_by_user) { + if (wasi_args->set_by_user) { + set_error_buf(error_buf, error_buf_size, + "WASI configuration was given via both of module " + "and InstantiationArgs2"); + goto fail; + } + wasi_args = &module->wasi_args; + } + if (!wasm_runtime_init_wasi( + (WASMModuleInstanceCommon *)module_inst, wasi_args->dir_list, + wasi_args->dir_count, wasi_args->map_dir_list, + wasi_args->map_dir_count, wasi_args->env, wasi_args->env_count, + wasi_args->addr_pool, wasi_args->addr_count, + wasi_args->ns_lookup_pool, wasi_args->ns_lookup_count, + wasi_args->argv, wasi_args->argc, wasi_args->stdio[0], + wasi_args->stdio[1], wasi_args->stdio[2], error_buf, + error_buf_size)) { + 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 (module->instance_list) { + LOG_WARNING( + "warning: multiple instances referencing to the same module " + "may cause unexpected behaviour during debugging"); + } + 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) + module_inst->e->start_function = + &module_inst->e->functions[module->start_function]; + } + + if (!execute_post_instantiate_functions(module_inst, is_sub_inst, + exec_env_main)) { + set_error_buf(error_buf, error_buf_size, module_inst->cur_exception); + goto fail; + } + +#if WASM_ENABLE_MEMORY_TRACING != 0 + wasm_runtime_dump_module_inst_mem_consumption( + (WASMModuleInstanceCommon *)module_inst); +#endif + + (void)global_data_end; + return module_inst; + +fail: + wasm_deinstantiate(module_inst, false); + return NULL; +} + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +static void +destroy_c_api_frames(Vector *frames) +{ + WASMCApiFrame frame = { 0 }; + uint32 i, total_frames, ret; + + total_frames = (uint32)bh_vector_size(frames); + + for (i = 0; i < total_frames; i++) { + ret = bh_vector_get(frames, i, &frame); + bh_assert(ret); + + if (frame.lp) + wasm_runtime_free(frame.lp); + } + + ret = bh_vector_destroy(frames); + bh_assert(ret); + (void)ret; +} +#endif + +void +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 */ + { + 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); +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_sub_module_deinstantiate( + (WASMModuleInstanceCommon *)module_inst); +#endif + + if (module_inst->memory_count > 0) + memories_deinstantiate(module_inst, module_inst->memories, + module_inst->memory_count); + + if (module_inst->import_func_ptrs) { + wasm_runtime_free(module_inst->import_func_ptrs); + } + + tables_deinstantiate(module_inst); + functions_deinstantiate(module_inst->e->functions); +#if WASM_ENABLE_TAGS != 0 + tags_deinstantiate(module_inst->e->tags, module_inst->e->import_tag_ptrs); +#endif + globals_deinstantiate(module_inst->e->globals); + export_functions_deinstantiate(module_inst->export_functions); +#if WASM_ENABLE_TAGS != 0 + export_tags_deinstantiate(module_inst->e->export_tags); +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + export_globals_deinstantiate(module_inst->export_globals); +#endif + +#if WASM_ENABLE_MULTI_MEMORY != 0 + export_memories_deinstantiate(module_inst->export_memories); +#endif + +#if WASM_ENABLE_GC == 0 && WASM_ENABLE_REF_TYPES != 0 + wasm_externref_cleanup((WASMModuleInstanceCommon *)module_inst); +#endif + +#if WASM_ENABLE_GC != 0 + if (!is_sub_inst) { + if (module_inst->e->common.gc_heap_handle) + mem_allocator_destroy(module_inst->e->common.gc_heap_handle); + if (module_inst->e->common.gc_heap_pool) + wasm_runtime_free(module_inst->e->common.gc_heap_pool); + } +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (module_inst->frames) { + destroy_c_api_frames(module_inst->frames); + wasm_runtime_free(module_inst->frames); + module_inst->frames = NULL; + } +#endif + + if (module_inst->c_api_func_imports) + wasm_runtime_free(module_inst->c_api_func_imports); + + if (!is_sub_inst) { + wasm_native_call_context_dtors((WASMModuleInstanceCommon *)module_inst); + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_bitmap_delete(module_inst->e->common.data_dropped); +#endif +#if WASM_ENABLE_REF_TYPES != 0 + bh_bitmap_delete(module_inst->e->common.elem_dropped); +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + os_mutex_destroy(&module_inst->e->common.exception_lock); +#endif + wasm_runtime_free(module_inst); +} + +WASMFunctionInstance * +wasm_lookup_function(const WASMModuleInstance *module_inst, const char *name) +{ + WASMExportFuncInstance key = { .name = (char *)name }; + WASMExportFuncInstance *export_func_inst; + + if (!module_inst->export_functions) + return NULL; + + export_func_inst = bsearch( + &key, module_inst->export_functions, module_inst->export_func_count, + sizeof(WASMExportFuncInstance), cmp_export_func_inst); + + if (!export_func_inst) + return NULL; + + return export_func_inst->function; +} + +WASMMemoryInstance * +wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name) +{ +#if WASM_ENABLE_MULTI_MEMORY != 0 + uint32 i; + for (i = 0; i < module_inst->export_memory_count; i++) + if (!strcmp(module_inst->export_memories[i].name, name)) + return module_inst->export_memories[i].memory; + return NULL; +#else + (void)module_inst->export_memories; + if (!module_inst->memories) + return NULL; + return module_inst->memories[0]; +#endif +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMGlobalInstance * +wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name) +{ + uint32 i; + for (i = 0; i < module_inst->export_global_count; i++) + if (!strcmp(module_inst->export_globals[i].name, name)) + return module_inst->export_globals[i].global; + return NULL; +} + +WASMTableInstance * +wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name) +{ + /** + * using a strong assumption that one module instance only has + * one table instance + */ + (void)module_inst->export_tables; + return module_inst->tables[0]; +} + +#if WASM_ENABLE_TAGS != 0 +WASMTagInstance * +wasm_lookup_tag(const WASMModuleInstance *module_inst, const char *name, + const char *signature) +{ + uint32 i; + for (i = 0; i < module_inst->e->export_tag_count; i++) + if (!strcmp(module_inst->e->export_tags[i].name, name)) + return module_inst->e->export_tags[i].tag; + (void)signature; + return NULL; +} +#endif + +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +static void +call_wasm_with_hw_bound_check(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *function, unsigned argc, + uint32 argv[]) +{ + WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls(); + WASMJmpBuf jmpbuf_node = { 0 }, *jmpbuf_node_pop; + WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); + uint8 *prev_top = exec_env->wasm_stack.top; +#ifdef BH_PLATFORM_WINDOWS + int result; + bool has_exception; + char exception[EXCEPTION_BUF_LEN]; +#endif + bool ret = true; + + if (!exec_env_tls) { + if (!os_thread_signal_inited()) { + wasm_set_exception(module_inst, "thread signal env not inited"); + return; + } + + /* Set thread handle and stack boundary if they haven't been set */ + wasm_exec_env_set_thread_info(exec_env); + + wasm_runtime_set_exec_env_tls(exec_env); + } + else { + if (exec_env_tls != exec_env) { + wasm_set_exception(module_inst, "invalid exec env"); + return; + } + } + + /* 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. */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + wasm_runtime_set_exec_env_tls(NULL); + return; + } + + wasm_exec_env_push_jmpbuf(exec_env, &jmpbuf_node); + + if (os_setjmp(jmpbuf_node.jmpbuf) == 0) { +#ifndef BH_PLATFORM_WINDOWS + wasm_interp_call_wasm(module_inst, exec_env, function, argc, argv); +#else + __try { + wasm_interp_call_wasm(module_inst, exec_env, function, argc, argv); + } __except (wasm_copy_exception(module_inst, NULL) + ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_SEARCH) { + /* Exception was thrown in wasm_exception_handler */ + ret = false; + } + 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(); + bh_assert(result != 0); + } +#endif + } + else { + /* Exception has been set in signal handler before calling longjmp */ + ret = false; + } + + /* Note: can't check wasm_get_exception(module_inst) here, there may be + * exception which is not caught by hardware (e.g. uninitialized elements), + * then the stack-frame is already freed inside wasm_interp_call_wasm */ + if (!ret) { +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (wasm_interp_create_call_stack(exec_env)) { + wasm_interp_dump_call_stack(exec_env, true, NULL, 0); + } +#endif + /* Restore operand frames */ + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + exec_env->wasm_stack.top = prev_top; + } + + jmpbuf_node_pop = wasm_exec_env_pop_jmpbuf(exec_env); + bh_assert(&jmpbuf_node == jmpbuf_node_pop); + if (!exec_env->jmpbuf_stack_top) { + wasm_runtime_set_exec_env_tls(NULL); + } + if (!ret) { + os_sigreturn(); + os_signal_unmask(); + } + (void)jmpbuf_node_pop; +} +#define interp_call_wasm call_wasm_with_hw_bound_check +#else +#define interp_call_wasm wasm_interp_call_wasm +#endif + +bool +wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function, + unsigned argc, uint32 argv[]) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + +#ifndef OS_ENABLE_HW_BOUND_CHECK + /* Set thread handle and stack boundary */ + wasm_exec_env_set_thread_info(exec_env); +#else + /* Set thread info in call_wasm_with_hw_bound_check when + hw bound check is enabled */ +#endif + + /* Set exec env, so it can be later retrieved from instance */ + module_inst->cur_exec_env = exec_env; + + interp_call_wasm(module_inst, exec_env, function, argc, argv); + return !wasm_copy_exception(module_inst, NULL); +} + +#if WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0 +/* look for the function name */ +static char * +get_func_name_from_index(const WASMModuleInstance *inst, uint32 func_index) +{ + char *func_name = NULL; + WASMFunctionInstance *func_inst = inst->e->functions + func_index; + + if (func_inst->is_import_func) { + func_name = func_inst->u.func_import->field_name; + } + else { +#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0 + func_name = func_inst->u.func->field_name; +#endif + /* if custom name section is not generated, + search symbols from export table */ + if (!func_name) { + unsigned j; + for (j = 0; j < inst->export_func_count; j++) { + WASMExportFuncInstance *export_func = + inst->export_functions + j; + if (export_func->function == func_inst) { + func_name = export_func->name; break; - default: - bh_assert(0); + } } } - bh_assert(global_data == global_data_end); + } - /* Initialize the memory data with data segment section */ - if (module_inst->default_memory->cur_page_count > 0) { - for (i = 0; i < module->data_seg_count; i++) { - data_seg = module->data_segments[i]; - bh_assert(data_seg->memory_index == 0); - bh_assert(data_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || data_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); - - if (data_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - bh_assert(data_seg->base_offset.u.global_index < global_count - && globals[data_seg->base_offset.u.global_index].type == - VALUE_TYPE_I32); - data_seg->base_offset.u.i32 = - globals[data_seg->base_offset.u.global_index].initial_value.i32; - } + return func_name; +} +#endif /*WASM_ENABLE_PERF_PROFILING != 0 || WASM_ENABLE_DUMP_CALL_STACK != 0*/ - base_offset = (uint32)data_seg->base_offset.u.i32; - length = data_seg->data_length; - memory_size = module_inst->default_memory->num_bytes_per_page - * module_inst->default_memory->cur_page_count; +#if WASM_ENABLE_PERF_PROFILING != 0 +void +wasm_dump_perf_profiling(const WASMModuleInstance *module_inst) +{ + WASMFunctionInstance *func_inst; + char *func_name; + uint32 i; - if (length > 0 - && (base_offset >= memory_size - || base_offset + length > memory_size)) { - set_error_buf(error_buf, error_buf_size, - "Instantiate module failed: data segment out of range."); - wasm_deinstantiate(module_inst); - return NULL; - } + os_printf("Performance profiler data:\n"); + for (i = 0; i < module_inst->e->function_count; i++) { + func_inst = module_inst->e->functions + i; + + if (func_inst->total_exec_cnt == 0) + continue; + + func_name = get_func_name_from_index(module_inst, i); + if (func_name) + os_printf( + " func %s, execution time: %.3f ms, execution count: %" PRIu32 + " times, children execution time: %.3f ms\n", + func_name, func_inst->total_exec_time / 1000.0, + func_inst->total_exec_cnt, + func_inst->children_exec_time / 1000.0); + else + os_printf(" func %" PRIu32 + ", execution time: %.3f ms, execution count: %" PRIu32 + " times, children execution time: %.3f ms\n", + i, func_inst->total_exec_time / 1000.0, + func_inst->total_exec_cnt, + func_inst->children_exec_time / 1000.0); + } +} - bh_memcpy_s(memory_data + base_offset, memory_size - base_offset, - data_seg->data, length); - } - } - } - - if (module_inst->table_count) { - module_inst->default_table = module_inst->tables[0]; - - /* Initialize the table data with table segment section */ - table_data = (uint32*)module_inst->default_table->base_addr; - table_seg = module->table_segments; - for (i = 0; i < module->table_seg_count; i++, table_seg++) { - bh_assert(table_seg->table_index == 0); - bh_assert(table_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || table_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); - - if (table_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL) { - bh_assert(table_seg->base_offset.u.global_index < global_count - && globals[table_seg->base_offset.u.global_index].type == - VALUE_TYPE_I32); - table_seg->base_offset.u.i32 = - globals[table_seg->base_offset.u.global_index].initial_value.i32; - } - if ((uint32)table_seg->base_offset.u.i32 < - module_inst->default_table->cur_size) { - length = table_seg->function_count; - if ((uint32)table_seg->base_offset.u.i32 + length > - module_inst->default_table->cur_size) - length = module_inst->default_table->cur_size - - (uint32)table_seg->base_offset.u.i32; - /* Check function index */ - for (j = 0; j < length; j++) { - if (table_seg->func_indexes[j] >= module_inst->function_count) { - set_error_buf(error_buf, error_buf_size, - "function index is overflow"); - wasm_deinstantiate(module_inst); - return NULL; - } - } - bh_memcpy_s(table_data + table_seg->base_offset.u.i32, - (uint32)((module_inst->default_table->cur_size - - (uint32)table_seg->base_offset.u.i32) - * sizeof(uint32)), - table_seg->func_indexes, (uint32)(length * sizeof(uint32))); +double +wasm_summarize_wasm_execute_time(const WASMModuleInstance *inst) +{ + double ret = 0; + + unsigned i; + for (i = 0; i < inst->e->function_count; i++) { + WASMFunctionInstance *func = inst->e->functions + i; + ret += (func->total_exec_time - func->children_exec_time) / 1000.0; + } + + return ret; +} + +double +wasm_get_wasm_func_exec_time(const WASMModuleInstance *inst, + const char *func_name) +{ + unsigned i; + for (i = 0; i < inst->e->function_count; i++) { + char *name_in_wasm = get_func_name_from_index(inst, i); + if (name_in_wasm && strcmp(name_in_wasm, func_name) == 0) { + WASMFunctionInstance *func = inst->e->functions + i; + return (func->total_exec_time - func->children_exec_time) / 1000.0; + } + } + + return -1.0; +} +#endif /*WASM_ENABLE_PERF_PROFILING != 0*/ + +uint64 +wasm_module_malloc_internal(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 size, + void **p_native_addr) +{ + WASMMemoryInstance *memory = wasm_get_default_memory(module_inst); + uint8 *addr = NULL; + uint64 offset = 0; + + /* TODO: Memory64 size check based on memory idx type */ + bh_assert(size <= UINT32_MAX); + + if (!memory) { + wasm_set_exception(module_inst, "uninitialized memory"); + return 0; + } + + if (memory->heap_handle) { + addr = mem_allocator_malloc(memory->heap_handle, (uint32)size); + } + else if (module_inst->e->malloc_function && module_inst->e->free_function) { + if (!execute_malloc_function( + module_inst, exec_env, module_inst->e->malloc_function, + module_inst->e->retain_function, size, &offset)) { + return 0; + } + /* If we use app's malloc function, + the default memory may be changed while memory growing */ + memory = wasm_get_default_memory(module_inst); + addr = offset ? memory->memory_data + offset : NULL; + } + + if (!addr) { + if (memory->heap_handle + && mem_allocator_is_heap_corrupted(memory->heap_handle)) { + wasm_runtime_show_app_heap_corrupted_prompt(); + wasm_set_exception(module_inst, "app heap corrupted"); + } + else { + LOG_WARNING("warning: allocate %" PRIu64 " bytes memory failed", + size); + } + return 0; + } + if (p_native_addr) + *p_native_addr = addr; + + return (uint64)(addr - memory->memory_data); +} + +uint64 +wasm_module_realloc_internal(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 ptr, uint64 size, + void **p_native_addr) +{ + WASMMemoryInstance *memory = wasm_get_default_memory(module_inst); + uint8 *addr = NULL; + + /* TODO: Memory64 ptr and size check based on memory idx type */ + bh_assert(ptr <= UINT32_MAX); + bh_assert(size <= UINT32_MAX); + + if (!memory) { + wasm_set_exception(module_inst, "uninitialized memory"); + return 0; + } + + if (memory->heap_handle) { + addr = mem_allocator_realloc( + memory->heap_handle, + (uint32)ptr ? memory->memory_data + (uint32)ptr : NULL, + (uint32)size); + } + + /* Only support realloc in WAMR's app heap */ + (void)exec_env; + + if (!addr) { + if (memory->heap_handle + && mem_allocator_is_heap_corrupted(memory->heap_handle)) { + wasm_set_exception(module_inst, "app heap corrupted"); + } + else { + wasm_set_exception(module_inst, "out of memory"); + } + return 0; + } + if (p_native_addr) + *p_native_addr = addr; + + return (uint64)(addr - memory->memory_data); +} + +void +wasm_module_free_internal(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 ptr) +{ + WASMMemoryInstance *memory = wasm_get_default_memory(module_inst); + + /* TODO: Memory64 ptr and size check based on memory idx type */ + bh_assert(ptr <= UINT32_MAX); + + if (!memory) { + return; + } + + if (ptr) { + uint8 *addr = memory->memory_data + (uint32)ptr; + uint8 *memory_data_end; + + /* memory->memory_data_end may be changed in memory grow */ + SHARED_MEMORY_LOCK(memory); + memory_data_end = memory->memory_data_end; + SHARED_MEMORY_UNLOCK(memory); + + if (memory->heap_handle && memory->heap_data <= addr + && addr < memory->heap_data_end) { + mem_allocator_free(memory->heap_handle, addr); + } + else if (module_inst->e->malloc_function + && module_inst->e->free_function && memory->memory_data <= addr + && addr < memory_data_end) { + execute_free_function(module_inst, exec_env, + module_inst->e->free_function, ptr); + } + } +} + +uint64 +wasm_module_malloc(WASMModuleInstance *module_inst, uint64 size, + void **p_native_addr) +{ + return wasm_module_malloc_internal(module_inst, NULL, size, p_native_addr); +} + +uint64 +wasm_module_realloc(WASMModuleInstance *module_inst, uint64 ptr, uint64 size, + void **p_native_addr) +{ + return wasm_module_realloc_internal(module_inst, NULL, ptr, size, + p_native_addr); +} + +void +wasm_module_free(WASMModuleInstance *module_inst, uint64 ptr) +{ + wasm_module_free_internal(module_inst, NULL, ptr); +} + +uint64 +wasm_module_dup_data(WASMModuleInstance *module_inst, const char *src, + uint64 size) +{ + char *buffer; + uint64 buffer_offset; + + /* TODO: Memory64 size check based on memory idx type */ + bh_assert(size <= UINT32_MAX); + + buffer_offset = wasm_module_malloc(module_inst, size, (void **)&buffer); + + if (buffer_offset != 0) { + buffer = wasm_runtime_addr_app_to_native( + (WASMModuleInstanceCommon *)module_inst, buffer_offset); + bh_memcpy_s(buffer, (uint32)size, src, (uint32)size); + } + return buffer_offset; +} + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +bool +wasm_enlarge_table(WASMModuleInstance *module_inst, uint32 table_idx, + uint32 inc_size, table_elem_type_t init_val) +{ + uint32 total_size, i; + table_elem_type_t *new_table_data_start; + WASMTableInstance *table_inst; + + if (!inc_size) { + return true; + } + + bh_assert(table_idx < module_inst->table_count); + table_inst = wasm_get_table_inst(module_inst, table_idx); + if (!table_inst) { + return false; + } + + if (inc_size > UINT32_MAX - table_inst->cur_size) { + return false; + } + + total_size = table_inst->cur_size + inc_size; + if (total_size > table_inst->max_size) { + return false; + } + + /* fill in */ + new_table_data_start = table_inst->elems + table_inst->cur_size; + for (i = 0; i < inc_size; ++i) { + new_table_data_start[i] = init_val; + } + + table_inst->cur_size = total_size; + return true; +} +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +static bool +call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 tbl_elem_idx, + uint32 argc, uint32 argv[], bool check_type_idx, uint32 type_idx) +{ + WASMModuleInstance *module_inst = NULL; + WASMTableInstance *table_inst = NULL; + table_elem_type_t tbl_elem_val = NULL_REF; + uint32 func_idx = 0; + WASMFunctionInstance *func_inst = NULL; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + bh_assert(module_inst); + + table_inst = module_inst->tables[tbl_idx]; + if (!table_inst) { + wasm_set_exception(module_inst, "unknown table"); + goto got_exception; + } + + if (tbl_elem_idx >= table_inst->cur_size) { + wasm_set_exception(module_inst, "undefined element"); + goto got_exception; + } + + tbl_elem_val = ((table_elem_type_t *)table_inst->elems)[tbl_elem_idx]; + if (tbl_elem_val == NULL_REF) { + wasm_set_exception(module_inst, "uninitialized element"); + goto got_exception; + } + +#if WASM_ENABLE_GC == 0 + func_idx = (uint32)tbl_elem_val; +#else + func_idx = + wasm_func_obj_get_func_idx_bound((WASMFuncObjectRef)tbl_elem_val); +#endif + + /** + * we insist to call functions owned by the module itself + **/ + if (func_idx >= module_inst->e->function_count) { + wasm_set_exception(module_inst, "unknown function"); + goto got_exception; + } + + func_inst = module_inst->e->functions + func_idx; + + if (check_type_idx) { + WASMType *cur_type = module_inst->module->types[type_idx]; + WASMType *cur_func_type; + + if (func_inst->is_import_func) + cur_func_type = (WASMType *)func_inst->u.func_import->func_type; + else + cur_func_type = (WASMType *)func_inst->u.func->func_type; + + if (cur_type != cur_func_type) { + wasm_set_exception(module_inst, "indirect call type mismatch"); + goto got_exception; + } + } + + interp_call_wasm(module_inst, exec_env, func_inst, argc, argv); + + return !wasm_copy_exception(module_inst, NULL); + +got_exception: + return false; +} + +bool +wasm_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, + uint32 argc, uint32 argv[]) +{ + return call_indirect(exec_env, tbl_idx, elem_idx, argc, argv, false, 0); +} + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_set_aux_stack(WASMExecEnv *exec_env, uint64 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 */ + uint64 data_end = module_inst->module->aux_data_end; + uint64 stack_bottom = module_inst->module->aux_stack_bottom; + bool is_stack_before_data = stack_bottom < data_end ? true : false; + 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, + set the initial value for the global */ + uint8 *global_addr = + module_inst->global_data + + module_inst->e->globals[stack_top_idx].data_offset; + *(int32 *)global_addr = (uint32)start_offset; + /* The aux stack boundary is a constant value, + set the value to exec_env */ + exec_env->aux_stack_boundary = (uintptr_t)start_offset - size; + exec_env->aux_stack_bottom = (uintptr_t)start_offset; + return true; + } + + return false; +} + +bool +wasm_get_aux_stack(WASMExecEnv *exec_env, uint64 *start_offset, uint32 *size) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + + /* The aux stack information is resolved in loader + and store in module */ + uint64 stack_bottom = module_inst->module->aux_stack_bottom; + uint32 total_aux_stack_size = module_inst->module->aux_stack_size; + + if (stack_bottom != 0 && total_aux_stack_size != 0) { + if (start_offset) + *start_offset = stack_bottom; + if (size) + *size = total_aux_stack_size; + return true; + } + return false; +} +#endif + +#if (WASM_ENABLE_MEMORY_PROFILING != 0) || (WASM_ENABLE_MEMORY_TRACING != 0) +void +wasm_get_module_mem_consumption(const WASMModule *module, + WASMModuleMemConsumption *mem_conspn) +{ + uint32 i, size; + + memset(mem_conspn, 0, sizeof(*mem_conspn)); + + mem_conspn->module_struct_size = sizeof(WASMModule); + + mem_conspn->types_size = sizeof(WASMFuncType *) * module->type_count; + for (i = 0; i < module->type_count; i++) { + WASMFuncType *type = module->types[i]; + size = offsetof(WASMFuncType, types) + + sizeof(uint8) * (type->param_count + type->result_count); + mem_conspn->types_size += size; + } + + mem_conspn->imports_size = sizeof(WASMImport) * module->import_count; + + mem_conspn->functions_size = + sizeof(WASMFunction *) * module->function_count; + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + WASMFuncType *type = func->func_type; + size = sizeof(WASMFunction) + func->local_count + + sizeof(uint16) * (type->param_count + func->local_count); +#if WASM_ENABLE_FAST_INTERP != 0 + size += + func->code_compiled_size + sizeof(uint32) * func->const_cell_num; +#endif + mem_conspn->functions_size += size; + } + + mem_conspn->tables_size = sizeof(WASMTable) * module->table_count; + mem_conspn->memories_size = sizeof(WASMMemory) * module->memory_count; + mem_conspn->globals_size = sizeof(WASMGlobal) * module->global_count; + mem_conspn->exports_size = sizeof(WASMExport) * module->export_count; + + mem_conspn->table_segs_size = + sizeof(WASMTableSeg) * module->table_seg_count; + for (i = 0; i < module->table_seg_count; i++) { + WASMTableSeg *table_seg = &module->table_segments[i]; + mem_conspn->tables_size += + sizeof(InitializerExpression *) * table_seg->value_count; + } + + mem_conspn->data_segs_size = sizeof(WASMDataSeg *) * module->data_seg_count; + for (i = 0; i < module->data_seg_count; i++) { + mem_conspn->data_segs_size += sizeof(WASMDataSeg); + } + + if (module->const_str_list) { + StringNode *node = module->const_str_list, *node_next; + while (node) { + node_next = node->next; + mem_conspn->const_strs_size += + sizeof(StringNode) + strlen(node->str) + 1; + node = node_next; + } + } + +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + { + WASMCustomSection *section = module->custom_section_list; + while (section) { + mem_conspn->custom_sections_size += + sizeof(WASMCustomSection) + section->content_len; + section = section->next; + } + } +#endif + + mem_conspn->total_size += mem_conspn->module_struct_size; + mem_conspn->total_size += mem_conspn->types_size; + mem_conspn->total_size += mem_conspn->imports_size; + mem_conspn->total_size += mem_conspn->functions_size; + mem_conspn->total_size += mem_conspn->tables_size; + mem_conspn->total_size += mem_conspn->memories_size; + mem_conspn->total_size += mem_conspn->globals_size; + mem_conspn->total_size += mem_conspn->exports_size; + mem_conspn->total_size += mem_conspn->table_segs_size; + mem_conspn->total_size += mem_conspn->data_segs_size; + mem_conspn->total_size += mem_conspn->const_strs_size; +#if WASM_ENABLE_LOAD_CUSTOM_SECTION != 0 + mem_conspn->total_size += mem_conspn->custom_sections_size; +#endif +} + +void +wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module_inst, + WASMModuleInstMemConsumption *mem_conspn) +{ + uint32 i; + uint64 size; + + memset(mem_conspn, 0, sizeof(*mem_conspn)); + + mem_conspn->module_inst_struct_size = (uint8 *)module_inst->e + - (uint8 *)module_inst + + sizeof(WASMModuleInstanceExtra); + + mem_conspn->memories_size = + sizeof(WASMMemoryInstance *) * module_inst->memory_count; + for (i = 0; i < module_inst->memory_count; i++) { + WASMMemoryInstance *memory = module_inst->memories[i]; + size = (uint64)memory->num_bytes_per_page * memory->cur_page_count; + mem_conspn->memories_size += size; + mem_conspn->app_heap_size += memory->heap_data_end - memory->heap_data; + /* size of app heap structure */ + mem_conspn->memories_size += mem_allocator_get_heap_struct_size(); + /* Module instance structures have been appended into the end of + module instance */ + } + + mem_conspn->tables_size = + sizeof(WASMTableInstance *) * module_inst->table_count; + /* Table instance structures and table elements have been appended into + the end of module instance */ + + mem_conspn->functions_size = + sizeof(WASMFunctionInstance) * module_inst->e->function_count; + + mem_conspn->globals_size = + sizeof(WASMGlobalInstance) * module_inst->e->global_count; + /* Global data has been appended into the end of module instance */ + + mem_conspn->exports_size = + sizeof(WASMExportFuncInstance) * module_inst->export_func_count; + + mem_conspn->total_size += mem_conspn->module_inst_struct_size; + mem_conspn->total_size += mem_conspn->memories_size; + mem_conspn->total_size += mem_conspn->functions_size; + mem_conspn->total_size += mem_conspn->tables_size; + mem_conspn->total_size += mem_conspn->globals_size; + mem_conspn->total_size += mem_conspn->exports_size; +} +#endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \ + || (WASM_ENABLE_MEMORY_TRACING != 0) */ + +#if WASM_ENABLE_COPY_CALL_STACK != 0 +uint32 +wasm_interp_copy_callstack(WASMExecEnv *exec_env, WASMCApiFrame *buffer, + uint32 length, uint32 skip_n, char *error_buf, + uint32_t error_buf_size) +{ + /* + * Note for devs: please refrain from such modifications inside of + * wasm_interp_copy_callstack + * - any allocations/freeing memory + * - dereferencing any pointers other than: exec_env, exec_env->module_inst, + * exec_env->module_inst->module, pointers between stack's bottom and + * top_boundary For more details check wasm_copy_callstack in + * wasm_export.h + */ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env); + WASMInterpFrame *cur_frame = wasm_exec_env_get_cur_frame(exec_env); + uint8 *top_boundary = exec_env->wasm_stack.top_boundary; + uint8 *bottom = exec_env->wasm_stack.bottom; + uint32 count = 0; + + WASMCApiFrame record_frame; + while (cur_frame && (uint8_t *)cur_frame >= bottom + && (uint8_t *)cur_frame + sizeof(WASMInterpFrame) <= top_boundary + && count < (skip_n + length)) { + if (!cur_frame->function) { + cur_frame = cur_frame->prev_frame; + continue; + } + if (count < skip_n) { + ++count; + cur_frame = cur_frame->prev_frame; + continue; + } + record_frame.instance = module_inst; + record_frame.module_offset = 0; + // It's safe to dereference module_inst->e because "e" is asigned only + // once in wasm_instantiate + record_frame.func_index = + (uint32)(cur_frame->function - module_inst->e->functions); + buffer[count - skip_n] = record_frame; + cur_frame = cur_frame->prev_frame; + ++count; + } + return count >= skip_n ? count - skip_n : 0; +} +#endif // WASM_ENABLE_COPY_CALL_STACK + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 +bool +wasm_interp_create_call_stack(struct WASMExecEnv *exec_env) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env); + WASMModule *module = module_inst->module; + WASMInterpFrame *first_frame, + *cur_frame = wasm_exec_env_get_cur_frame(exec_env); + uint32 n = 0; + + /* count frames includes a function */ + first_frame = cur_frame; + while (cur_frame) { + if (cur_frame->function) { + n++; + } + cur_frame = cur_frame->prev_frame; + } + + /* release previous stack frames and create new ones */ + destroy_c_api_frames(module_inst->frames); + if (!bh_vector_init(module_inst->frames, n, sizeof(WASMCApiFrame), false)) { + return false; + } + + cur_frame = first_frame; + n = 0; + + while (cur_frame) { + WASMCApiFrame frame = { 0 }; + WASMFunctionInstance *func_inst = cur_frame->function; + const char *func_name = NULL; + const uint8 *func_code_base = NULL; + uint32 max_local_cell_num, max_stack_cell_num; + uint32 all_cell_num, lp_size; + + if (!func_inst) { + cur_frame = cur_frame->prev_frame; + continue; + } + + /* place holder, will overwrite it in wasm_c_api */ + frame.instance = module_inst; + frame.module_offset = 0; + frame.func_index = (uint32)(func_inst - module_inst->e->functions); + + func_code_base = wasm_get_func_code(func_inst); + if (!cur_frame->ip || !func_code_base) { + frame.func_offset = 0; + } + else { +#if WASM_ENABLE_FAST_INTERP == 0 + frame.func_offset = (uint32)(cur_frame->ip - module->load_addr); +#else + frame.func_offset = (uint32)(cur_frame->ip - func_code_base); +#endif + } + + func_name = get_func_name_from_index(module_inst, frame.func_index); + frame.func_name_wp = func_name; + + if (frame.func_index >= module->import_function_count) { + uint32 wasm_func_idx = + frame.func_index - module->import_function_count; + max_local_cell_num = + module->functions[wasm_func_idx]->param_cell_num + + module->functions[wasm_func_idx]->local_cell_num; + max_stack_cell_num = + module->functions[wasm_func_idx]->max_stack_cell_num; + all_cell_num = max_local_cell_num + max_stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + all_cell_num += module->functions[wasm_func_idx]->const_cell_num; +#endif + } + else { + WASMFuncType *func_type = + module->import_functions[frame.func_index].u.function.func_type; + max_local_cell_num = + func_type->param_cell_num > 2 ? func_type->param_cell_num : 2; + max_stack_cell_num = 0; + all_cell_num = max_local_cell_num + max_stack_cell_num; + } + +#if WASM_ENABLE_GC == 0 + lp_size = all_cell_num * 4; +#else + lp_size = align_uint(all_cell_num * 5, 4); +#endif + if (lp_size > 0) { + if (!(frame.lp = wasm_runtime_malloc(lp_size))) { + destroy_c_api_frames(module_inst->frames); + return false; } + bh_memcpy_s(frame.lp, lp_size, cur_frame->lp, lp_size); + +#if WASM_ENABLE_GC != 0 +#if WASM_ENABLE_FAST_INTERP == 0 + frame.sp = frame.lp + (cur_frame->sp - cur_frame->lp); +#else + /* for fast-interp, let frame sp point to the end of the frame */ + frame.sp = frame.lp + all_cell_num; +#endif + frame.frame_ref = (uint8 *)frame.lp + + (wasm_interp_get_frame_ref(cur_frame) + - (uint8 *)cur_frame->lp); +#endif + } + + if (!bh_vector_append(module_inst->frames, &frame)) { + if (frame.lp) + wasm_runtime_free(frame.lp); + destroy_c_api_frames(module_inst->frames); + return false; } + + cur_frame = cur_frame->prev_frame; + n++; } -#if WASM_ENABLE_LIBC_WASI != 0 - if (!wasm_runtime_init_wasi((WASMModuleInstanceCommon*)module_inst, - module->wasi_args.dir_list, - module->wasi_args.dir_count, - module->wasi_args.map_dir_list, - module->wasi_args.map_dir_count, - module->wasi_args.env, - module->wasi_args.env_count, - module->wasi_args.argv, - module->wasi_args.argc, - error_buf, error_buf_size)) { - wasm_deinstantiate(module_inst); - return NULL; + return true; +} + +#define PRINT_OR_DUMP() \ + do { \ + total_len += \ + wasm_runtime_dump_line_buf_impl(line_buf, print, &buf, &len); \ + if ((!print) && buf && (len == 0)) { \ + exception_unlock(module_inst); \ + return total_len; \ + } \ + } while (0) + +uint32 +wasm_interp_dump_call_stack(struct WASMExecEnv *exec_env, bool print, char *buf, + uint32 len) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env); + uint32 n = 0, total_len = 0, total_frames; + /* reserve 256 bytes for line buffer, any line longer than 256 bytes + * will be truncated */ + char line_buf[256]; + + if (!module_inst->frames) { + return 0; } -#endif - if (module->start_function != (uint32)-1) { - bh_assert(module->start_function >= module->import_function_count); - module_inst->start_function = - &module_inst->functions[module->start_function]; + total_frames = (uint32)bh_vector_size(module_inst->frames); + if (total_frames == 0) { + return 0; } - module_inst->module = module; + exception_lock(module_inst); + snprintf(line_buf, sizeof(line_buf), "\n"); + PRINT_OR_DUMP(); - /* module instance type */ - module_inst->module_type = Wasm_Module_Bytecode; + while (n < total_frames) { + WASMCApiFrame frame = { 0 }; + uint32 line_length, i; - /* Initialize the thread related data */ - if (stack_size == 0) - stack_size = DEFAULT_WASM_STACK_SIZE; - module_inst->default_wasm_stack_size = stack_size; + if (!bh_vector_get(module_inst->frames, n, &frame)) { + exception_unlock(module_inst); + return 0; + } - /* Execute __post_instantiate function */ - if (!execute_post_inst_function(module_inst) - || !execute_start_function(module_inst)) { - set_error_buf(error_buf, error_buf_size, - module_inst->cur_exception); - wasm_deinstantiate(module_inst); - return NULL; +#if WASM_ENABLE_FAST_JIT != 0 + /* Fast JIT doesn't support committing ip (instruction pointer) yet */ + if (module_inst->e->running_mode == Mode_Fast_JIT + || module_inst->e->running_mode == Mode_Multi_Tier_JIT) { + /* function name not exported, print number instead */ + if (frame.func_name_wp == NULL) { + line_length = snprintf(line_buf, sizeof(line_buf), + "#%02" PRIu32 " $f%" PRIu32 "\n", n, + frame.func_index); + } + else { + line_length = + snprintf(line_buf, sizeof(line_buf), "#%02" PRIu32 " %s\n", + n, frame.func_name_wp); + } + } + else +#endif + { + /* function name not exported, print number instead */ + if (frame.func_name_wp == NULL) { + line_length = + snprintf(line_buf, sizeof(line_buf), + "#%02" PRIu32 ": 0x%04x - $f%" PRIu32 "\n", n, + frame.func_offset, frame.func_index); + } + else { + line_length = snprintf(line_buf, sizeof(line_buf), + "#%02" PRIu32 ": 0x%04x - %s\n", n, + frame.func_offset, frame.func_name_wp); + } + } + + if (line_length >= sizeof(line_buf)) { + uint32 line_buffer_len = sizeof(line_buf); + /* If line too long, ensure the last character is '\n' */ + for (i = line_buffer_len - 5; i < line_buffer_len - 2; i++) { + line_buf[i] = '.'; + } + line_buf[line_buffer_len - 2] = '\n'; + } + + PRINT_OR_DUMP(); + + n++; } + snprintf(line_buf, sizeof(line_buf), "\n"); + PRINT_OR_DUMP(); + exception_unlock(module_inst); - (void)global_data_end; - return module_inst; + return total_len + 1; } +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK */ +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 void -wasm_deinstantiate(WASMModuleInstance *module_inst) +jit_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id) { - if (!module_inst) - return; + if (id != EXCE_ALREADY_THROWN) + wasm_set_exception_with_id(module_inst, id); +#ifdef OS_ENABLE_HW_BOUND_CHECK + wasm_runtime_access_exce_check_guard_page(); +#endif +} -#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, - these fields will be set to NULL, we cannot free their internal data - which may allocated from global heap. */ - wasm_runtime_destroy_wasi((WASMModuleInstanceCommon*)module_inst); +bool +jit_check_app_addr_and_convert(WASMModuleInstance *module_inst, bool is_str, + uint64 app_buf_addr, uint64 app_buf_size, + void **p_native_addr) +{ + bool ret = wasm_check_app_addr_and_convert( + module_inst, is_str, app_buf_addr, app_buf_size, p_native_addr); + +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!ret) + wasm_runtime_access_exce_check_guard_page(); #endif - if (module_inst->memory_count > 0) - memories_deinstantiate(module_inst->memories, module_inst->memory_count); - else if (module_inst->memories != NULL && module_inst->global_count > 0) - /* No imported memory and defined memory, the memory is created when - global count > 0. */ - memories_deinstantiate(module_inst->memories, 1); - - tables_deinstantiate(module_inst->tables, module_inst->table_count); - functions_deinstantiate(module_inst->functions, module_inst->function_count); - globals_deinstantiate(module_inst->globals); - export_functions_deinstantiate(module_inst->export_functions); + return ret; +} +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 */ - wasm_free(module_inst); +#if WASM_ENABLE_FAST_JIT != 0 +bool +fast_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, + uint32 type_idx, uint32 argc, uint32 *argv) +{ + return call_indirect(exec_env, tbl_idx, elem_idx, argc, argv, true, + type_idx); } +#endif /* end of WASM_ENABLE_FAST_JIT != 0 */ -static bool -check_type(uint8 type, const char *p) +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 + +bool +llvm_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, + uint32 argc, uint32 *argv) { - const char *str = "i32"; + bool ret; - if (strlen(p) < 3) - return false; + bh_assert(exec_env->module_inst->module_type == Wasm_Module_Bytecode); - switch (type) { - case VALUE_TYPE_I32: - str = "i32"; - break; - case VALUE_TYPE_I64: - str = "i64"; - break; - case VALUE_TYPE_F32: - str = "f32"; - break; - case VALUE_TYPE_F64: - str = "f64"; - break; + ret = call_indirect(exec_env, tbl_idx, elem_idx, argc, argv, false, 0); +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!ret) + wasm_runtime_access_exce_check_guard_page(); +#endif + return ret; +} + +bool +llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, + uint32 *argv) +{ + WASMModuleInstance *module_inst; + WASMModule *module; + uint32 *func_type_indexes; + uint32 func_type_idx; + WASMFuncType *func_type; + void *func_ptr; + WASMFunctionImport *import_func; + CApiFuncImport *c_api_func_import = NULL; + const char *signature; + void *attachment; + char buf[96]; + bool ret = false; + + bh_assert(exec_env->module_inst->module_type == Wasm_Module_Bytecode); + + module_inst = (WASMModuleInstance *)wasm_runtime_get_module_inst(exec_env); + module = module_inst->module; + func_type_indexes = module_inst->func_type_indexes; + func_type_idx = func_type_indexes[func_idx]; + func_type = (WASMFuncType *)module->types[func_type_idx]; + func_ptr = module_inst->func_ptrs[func_idx]; + + bh_assert(func_idx < module->import_function_count); + + import_func = &module->import_functions[func_idx].u.function; + if (import_func->call_conv_wasm_c_api) { + if (module_inst->c_api_func_imports) { + c_api_func_import = module_inst->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 (strncmp(p, str, 3)) - return false; - return true; + if (!func_ptr) { + snprintf(buf, sizeof(buf), + "failed to call unlinked import function (%s, %s)", + import_func->module_name, import_func->field_name); + wasm_set_exception(module_inst, buf); + goto fail; + } + + attachment = import_func->attachment; + if (import_func->call_conv_wasm_c_api) { + ret = wasm_runtime_invoke_c_api_native( + (WASMModuleInstanceCommon *)module_inst, func_ptr, func_type, argc, + argv, c_api_func_import->with_env_arg, c_api_func_import->env_arg); + } + else if (!import_func->call_conv_raw) { + signature = import_func->signature; + ret = + wasm_runtime_invoke_native(exec_env, func_ptr, func_type, signature, + attachment, argv, argc, argv); + } + else { + signature = import_func->signature; + ret = wasm_runtime_invoke_native_raw(exec_env, func_ptr, func_type, + signature, attachment, argv, argc, + argv); + } + +fail: +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!ret) + wasm_runtime_access_exce_check_guard_page(); +#endif + return ret; } -static bool -check_function_type(const WASMType *type, const char *signature) +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +llvm_jit_memory_init(WASMModuleInstance *module_inst, uint32 seg_index, + uint32 offset, uint32 len, size_t dst) { - uint32 i; - const char *p = signature; + WASMMemoryInstance *memory_inst; + WASMModule *module; + uint8 *data; + uint8 *maddr; + uint64 seg_len; - if (!p || *p++ != '(') - return false; + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); - for (i = 0; i < type->param_count; i++) { - if (!check_type(type->types[i], p)) - return false; - p += 3; + memory_inst = wasm_get_default_memory(module_inst); + + if (bh_bitmap_get_bit(module_inst->e->common.data_dropped, seg_index)) { + seg_len = 0; + data = NULL; + } + else { + module = module_inst->module; + seg_len = module->data_segments[seg_index]->data_length; + data = module->data_segments[seg_index]->data; } - if (*p++ != ')') + if (!wasm_runtime_validate_app_addr((WASMModuleInstanceCommon *)module_inst, + (uint64)dst, (uint64)len)) return false; - if (type->result_count) { - if (!check_type(type->types[type->param_count], p)) - return false; - p += 3; + if ((uint64)offset + (uint64)len > seg_len) { + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; } - if (*p != '\0') - return false; + maddr = wasm_runtime_addr_app_to_native( + (WASMModuleInstanceCommon *)module_inst, (uint64)dst); + SHARED_MEMORY_LOCK(memory_inst); + bh_memcpy_s(maddr, CLAMP_U64_TO_U32(memory_inst->memory_data_size - dst), + data + offset, len); + SHARED_MEMORY_UNLOCK(memory_inst); return true; } -WASMFunctionInstance* -wasm_lookup_function(const WASMModuleInstance *module_inst, - const char *name, const char *signature) +bool +llvm_jit_data_drop(WASMModuleInstance *module_inst, uint32 seg_index) { - uint32 i; - for (i = 0; i < module_inst->export_func_count; i++) - if (!strcmp(module_inst->export_functions[i].name, name) - && check_function_type( - module_inst->export_functions[i].function->u.func->func_type, - signature)) - return module_inst->export_functions[i].function; - return NULL; + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); + + bh_bitmap_set_bit(module_inst->e->common.data_dropped, seg_index); + /* Currently we can't free the dropped data segment + as they are stored in wasm bytecode */ + return true; } +#endif /* end of WASM_ENABLE_BULK_MEMORY != 0 */ -bool -wasm_call_function(WASMExecEnv *exec_env, - WASMFunctionInstance *function, - unsigned argc, uint32 argv[]) +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +void +llvm_jit_drop_table_seg(WASMModuleInstance *module_inst, uint32 tbl_seg_idx) { - WASMModuleInstance *module_inst = (WASMModuleInstance*)exec_env->module_inst; - wasm_interp_call_wasm(module_inst, exec_env, function, argc, argv); - return !wasm_get_exception(module_inst) ? true : false; + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); + bh_bitmap_set_bit(module_inst->e->common.elem_dropped, tbl_seg_idx); } -bool -wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst, - WASMFunctionInstance *func, - unsigned argc, uint32 argv[]) +void +llvm_jit_table_init(WASMModuleInstance *module_inst, uint32 tbl_idx, + uint32 tbl_seg_idx, uint32 length, uint32 src_offset, + uint32 dst_offset) { - WASMExecEnv *exec_env; - bool ret; + WASMTableInstance *tbl_inst; + WASMTableSeg *tbl_seg; + table_elem_type_t *table_elems; + InitializerExpression *tbl_seg_init_values = NULL, *init_values; + uint32 i, tbl_seg_len = 0; +#if WASM_ENABLE_GC != 0 + void *func_obj; +#endif - if (!(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; + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); + + tbl_inst = wasm_get_table_inst(module_inst, tbl_idx); + tbl_seg = module_inst->module->table_segments + tbl_seg_idx; + + bh_assert(tbl_inst); + bh_assert(tbl_seg); + + if (!bh_bitmap_get_bit(module_inst->e->common.elem_dropped, tbl_seg_idx)) { + /* table segment isn't dropped */ + tbl_seg_init_values = tbl_seg->init_values; + tbl_seg_len = tbl_seg->value_count; } - ret = wasm_call_function(exec_env, func, argc, argv); - wasm_exec_env_destroy(exec_env); - return ret; + if (offset_len_out_of_bounds(src_offset, length, tbl_seg_len) + || offset_len_out_of_bounds(dst_offset, length, tbl_inst->cur_size)) { + jit_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + if (!length) { + return; + } + + table_elems = tbl_inst->elems + dst_offset; + init_values = tbl_seg_init_values + src_offset; + + for (i = 0; i < length; i++) { +#if WASM_ENABLE_GC != 0 + /* UINT32_MAX indicates that it is a null ref */ + if (init_values[i].u.unary.v.ref_index != UINT32_MAX) { + if (!(func_obj = wasm_create_func_obj( + module_inst, init_values[i].u.unary.v.ref_index, true, + NULL, 0))) { + wasm_set_exception(module_inst, "null function reference"); + return; + } + table_elems[i] = func_obj; + } + else { + table_elems[i] = NULL_REF; + } +#else + table_elems[i] = init_values[i].u.unary.v.ref_index; +#endif + } } void -wasm_set_exception(WASMModuleInstance *module_inst, - const char *exception) +llvm_jit_table_copy(WASMModuleInstance *module_inst, uint32 src_tbl_idx, + uint32 dst_tbl_idx, uint32 length, uint32 src_offset, + uint32 dst_offset) { - if (exception) - snprintf(module_inst->cur_exception, - sizeof(module_inst->cur_exception), - "Exception: %s", exception); - else - module_inst->cur_exception[0] = '\0'; -} + WASMTableInstance *src_tbl_inst; + WASMTableInstance *dst_tbl_inst; -const char* -wasm_get_exception(WASMModuleInstance *module_inst) -{ - if (module_inst->cur_exception[0] == '\0') - return NULL; - else - return module_inst->cur_exception; -} + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); -int32 -wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size) -{ - WASMMemoryInstance *memory = module_inst->default_memory; - uint8 *addr = mem_allocator_malloc(memory->heap_handle, size); - if (!addr) { - wasm_set_exception(module_inst, "out of memory"); - return 0; + src_tbl_inst = wasm_get_table_inst(module_inst, src_tbl_idx); + dst_tbl_inst = wasm_get_table_inst(module_inst, dst_tbl_idx); + bh_assert(src_tbl_inst); + bh_assert(dst_tbl_inst); + + if (offset_len_out_of_bounds(dst_offset, length, dst_tbl_inst->cur_size) + || offset_len_out_of_bounds(src_offset, length, + src_tbl_inst->cur_size)) { + jit_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; } - return memory->heap_base_offset + (int32)(addr - memory->heap_data); + + /* if src_offset >= dst_offset, copy from front to back */ + /* if src_offset < dst_offset, copy from back to front */ + /* merge all together */ + bh_memmove_s((uint8 *)dst_tbl_inst + offsetof(WASMTableInstance, elems) + + sizeof(table_elem_type_t) * dst_offset, + (uint32)sizeof(table_elem_type_t) + * (dst_tbl_inst->cur_size - dst_offset), + (uint8 *)src_tbl_inst + offsetof(WASMTableInstance, elems) + + sizeof(table_elem_type_t) * src_offset, + (uint32)sizeof(table_elem_type_t) * length); } void -wasm_module_free(WASMModuleInstance *module_inst, int32 ptr) +llvm_jit_table_fill(WASMModuleInstance *module_inst, uint32 tbl_idx, + uint32 length, uintptr_t val, uint32 data_offset) { - if (ptr) { - WASMMemoryInstance *memory = module_inst->default_memory; - uint8 *addr = memory->heap_data + (ptr - memory->heap_base_offset); - if (memory->heap_data < addr && addr < memory->heap_data_end) - mem_allocator_free(memory->heap_handle, addr); + WASMTableInstance *tbl_inst; + + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); + + tbl_inst = wasm_get_table_inst(module_inst, tbl_idx); + bh_assert(tbl_inst); + + if (offset_len_out_of_bounds(data_offset, length, tbl_inst->cur_size)) { + jit_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; } -} -int32 -wasm_module_dup_data(WASMModuleInstance *module_inst, - const char *src, uint32 size) -{ - int32 buffer_offset = wasm_module_malloc(module_inst, size); - if (buffer_offset != 0) { - char *buffer; - buffer = wasm_addr_app_to_native(module_inst, buffer_offset); - bh_memcpy_s(buffer, size, src, size); + for (; length != 0; data_offset++, length--) { + tbl_inst->elems[data_offset] = (table_elem_type_t)val; } - return buffer_offset; } -bool -wasm_validate_app_addr(WASMModuleInstance *module_inst, - int32 app_offset, uint32 size) +uint32 +llvm_jit_table_grow(WASMModuleInstance *module_inst, uint32 tbl_idx, + uint32 inc_size, uintptr_t init_val) { - WASMMemoryInstance *memory; - uint8 *addr; + WASMTableInstance *tbl_inst; + uint32 i, orig_size, total_size; - /* integer overflow check */ - if(app_offset + (int32)size < app_offset) { - goto fail; + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); + + tbl_inst = wasm_get_table_inst(module_inst, tbl_idx); + if (!tbl_inst) { + return (uint32)-1; } - memory = module_inst->default_memory; - if (0 <= app_offset - && app_offset < memory->heap_base_offset) { - addr = memory->memory_data + app_offset; - if (!(memory->base_addr <= addr && addr + size <= memory->end_addr)) - goto fail; - return true; + orig_size = tbl_inst->cur_size; + + if (!inc_size) { + return orig_size; } - else if (memory->heap_base_offset < app_offset - && app_offset < memory->heap_base_offset - + (memory->heap_data_end - memory->heap_data)) { - addr = memory->heap_data + (app_offset - memory->heap_base_offset); - if (!(memory->heap_data <= addr && addr + size <= memory->heap_data_end)) - goto fail; - return true; + + if (tbl_inst->cur_size > UINT32_MAX - inc_size) { /* integer overflow */ +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of integer overflow", + tbl_inst->cur_size, inc_size); +#endif + return (uint32)-1; } -fail: - wasm_set_exception(module_inst, "out of bounds memory access"); - return false; + total_size = tbl_inst->cur_size + inc_size; + if (total_size > tbl_inst->max_size) { +#if WASM_ENABLE_SPEC_TEST == 0 + LOG_WARNING("table grow (%" PRIu32 "-> %" PRIu32 + ") failed because of over max size", + tbl_inst->cur_size, inc_size); +#endif + return (uint32)-1; + } + + /* fill in */ + for (i = 0; i < inc_size; ++i) { + tbl_inst->elems[tbl_inst->cur_size + i] = (table_elem_type_t)init_val; + } + + tbl_inst->cur_size = total_size; + return orig_size; } +#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ -bool -wasm_validate_native_addr(WASMModuleInstance *module_inst, - void *native_ptr, uint32 size) +#if WASM_ENABLE_GC != 0 +void * +llvm_jit_create_func_obj(WASMModuleInstance *module_inst, uint32 func_idx, + bool throw_exce, char *error_buf, + uint32 error_buf_size) { - uint8 *addr = native_ptr; - WASMMemoryInstance *memory = module_inst->default_memory; + bh_assert(module_inst->module_type == Wasm_Module_Bytecode); - if (addr + size < addr) { - goto fail; - } + return wasm_create_func_obj(module_inst, func_idx, throw_exce, error_buf, + error_buf_size); +} - if ((memory->base_addr <= addr && addr + size <= memory->end_addr) - || (memory->heap_data <= addr && addr + size <= memory->heap_data_end) - ) - return true; +bool +llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst, + WASMObjectRef gc_obj, uint32 type_index) +{ + WASMModule *module = module_inst->module; + WASMType **types = module->types; + uint32 type_count = module->type_count; -fail: - wasm_set_exception(module_inst, "out of bounds memory access"); - return false; + return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count); } -void * -wasm_addr_app_to_native(WASMModuleInstance *module_inst, - int32 app_offset) -{ - WASMMemoryInstance *memory = module_inst->default_memory; - if (0 <= app_offset && app_offset < memory->heap_base_offset) - return memory->memory_data + app_offset; - else if (memory->heap_base_offset < app_offset - && app_offset < memory->heap_base_offset - + (memory->heap_data_end - memory->heap_data)) - return memory->heap_data + (app_offset - memory->heap_base_offset); - else - return NULL; +bool +llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst, + uint32 type_idx1, uint32 type_idx2) +{ + WASMModule *module = module_inst->module; + WASMType **types = module->types; + + if (type_idx1 == type_idx2) + return true; + + bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC); + bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC); + return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1], + (WASMFuncType *)types[type_idx2]); } -int32 -wasm_addr_native_to_app(WASMModuleInstance *module_inst, - void *native_ptr) +WASMRttTypeRef +llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index) { - WASMMemoryInstance *memory = module_inst->default_memory; - if (memory->base_addr <= (uint8*)native_ptr - && (uint8*)native_ptr < memory->end_addr) - return (int32)((uint8*)native_ptr - memory->memory_data); - else if (memory->heap_data <= (uint8*)native_ptr - && (uint8*)native_ptr < memory->heap_data_end) - return memory->heap_base_offset - + (int32)((uint8*)native_ptr - memory->heap_data); - else - return 0; + WASMModule *module = module_inst->module; + WASMType *defined_type = module->types[type_index]; + WASMRttType **rtt_types = module->rtt_types; + uint32 rtt_type_count = module->type_count; + korp_mutex *rtt_type_lock = &module->rtt_type_lock; + + return wasm_rtt_type_new(defined_type, type_index, rtt_types, + rtt_type_count, rtt_type_lock); } bool -wasm_get_app_addr_range(WASMModuleInstance *module_inst, - int32 app_offset, - int32 *p_app_start_offset, - int32 *p_app_end_offset) +llvm_array_init_with_data(WASMModuleInstance *module_inst, uint32 seg_index, + uint32 data_seg_offset, WASMArrayObjectRef array_obj, + uint32 elem_size, uint32 array_len) { - int32 app_start_offset, app_end_offset; - WASMMemoryInstance *memory = module_inst->default_memory; + WASMModule *wasm_module = module_inst->module; + WASMDataSeg *data_seg; + uint8 *array_elem_base; + uint64 total_size; - if (0 <= app_offset && app_offset < memory->heap_base_offset) { - app_start_offset = 0; - app_end_offset = (int32)(memory->num_bytes_per_page * memory->cur_page_count); - } - else if (memory->heap_base_offset < app_offset - && app_offset < memory->heap_base_offset - + (memory->heap_data_end - memory->heap_data)) { - app_start_offset = memory->heap_base_offset; - app_end_offset = memory->heap_base_offset - + (int32)(memory->heap_data_end - memory->heap_data); - } - else + data_seg = wasm_module->data_segments[seg_index]; + total_size = (int64)elem_size * array_len; + + if (data_seg_offset >= data_seg->data_length + || total_size > data_seg->data_length - data_seg_offset) { + wasm_set_exception(module_inst, "out of bounds memory access"); return false; + } + + array_elem_base = (uint8 *)wasm_array_obj_first_elem_addr(array_obj); + bh_memcpy_s(array_elem_base, (uint32)total_size, + data_seg->data + data_seg_offset, (uint32)total_size); - if (p_app_start_offset) - *p_app_start_offset = app_start_offset; - if (p_app_end_offset) - *p_app_end_offset = app_end_offset; return true; } +#endif /* end of WASM_ENABLE_GC != 0 */ -bool -wasm_get_native_addr_range(WASMModuleInstance *module_inst, - uint8 *native_ptr, - uint8 **p_native_start_addr, - uint8 **p_native_end_addr) +#endif /* end of WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 */ + +#if WASM_ENABLE_LIBC_WASI != 0 && WASM_ENABLE_MULTI_MODULE != 0 +void +wasm_propagate_wasi_args(WASMModule *module) { - uint8 *native_start_addr, *native_end_addr; - WASMMemoryInstance *memory = module_inst->default_memory; + if (!module->import_count) + return; - if (memory->base_addr <= (uint8*)native_ptr - && (uint8*)native_ptr < memory->end_addr) { - native_start_addr = memory->memory_data; - native_end_addr = memory->memory_data - + memory->num_bytes_per_page * memory->cur_page_count; - } - else if (memory->heap_data <= (uint8*)native_ptr - && (uint8*)native_ptr < memory->heap_data_end) { - native_start_addr = memory->heap_data; - native_end_addr = memory->heap_data_end; - } - else - return false; + bh_assert(&module->import_module_list_head); - if (p_native_start_addr) - *p_native_start_addr = native_start_addr; - if (p_native_end_addr) - *p_native_end_addr = native_end_addr; - return true; + WASMRegisteredModule *node = + bh_list_first_elem(&module->import_module_list_head); + while (node) { + WASIArguments *wasi_args_impt_mod = + &((WASMModule *)(node->module))->wasi_args; + bh_assert(wasi_args_impt_mod); + + bh_memcpy_s(wasi_args_impt_mod, sizeof(WASIArguments), + &module->wasi_args, sizeof(WASIArguments)); + node = bh_list_elem_next(node); + } } +#endif bool -wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) -{ -#if WASM_ENABLE_MEMORY_GROW != 0 - WASMMemoryInstance *memory = module->default_memory; - WASMMemoryInstance *new_memory; - uint32 total_page_count = inc_page_count + memory->cur_page_count; - uint64 total_size = offsetof(WASMMemoryInstance, base_addr) + - memory->num_bytes_per_page * (uint64)total_page_count + - memory->global_data_size; - - if (inc_page_count <= 0) - /* No need to enlarge memory */ - return true; +wasm_check_utf8_str(const uint8 *str, uint32 len) +{ + /* The valid ranges are taken from page 125, below link + https://www.unicode.org/versions/Unicode9.0.0/ch03.pdf */ + const uint8 *p = str, *p_end = str + len; + uint8 chr; - if (total_page_count < memory->cur_page_count /* integer overflow */ - || total_page_count > memory->max_page_count) { - wasm_set_exception(module, "fail to enlarge memory."); - return false; + while (p < p_end) { + chr = *p; + + if (chr == 0) { + LOG_WARNING( + "LIMITATION: a string which contains '\\00' is unsupported"); + return false; + } + else if (chr < 0x80) { + p++; + } + else if (chr >= 0xC2 && chr <= 0xDF && p + 1 < p_end) { + if (p[1] < 0x80 || p[1] > 0xBF) { + return false; + } + p += 2; + } + else if (chr >= 0xE0 && chr <= 0xEF && p + 2 < p_end) { + if (chr == 0xE0) { + if (p[1] < 0xA0 || p[1] > 0xBF || p[2] < 0x80 || p[2] > 0xBF) { + return false; + } + } + else if (chr == 0xED) { + if (p[1] < 0x80 || p[1] > 0x9F || p[2] < 0x80 || p[2] > 0xBF) { + return false; + } + } + else { /* chr >= 0xE1 && chr <= 0xEF */ + if (p[1] < 0x80 || p[1] > 0xBF || p[2] < 0x80 || p[2] > 0xBF) { + return false; + } + } + p += 3; + } + else if (chr >= 0xF0 && chr <= 0xF4 && p + 3 < p_end) { + if (chr == 0xF0) { + if (p[1] < 0x90 || p[1] > 0xBF || p[2] < 0x80 || p[2] > 0xBF + || p[3] < 0x80 || p[3] > 0xBF) { + return false; + } + } + else if (chr <= 0xF3) { /* and also chr >= 0xF1 */ + if (p[1] < 0x80 || p[1] > 0xBF || p[2] < 0x80 || p[2] > 0xBF + || p[3] < 0x80 || p[3] > 0xBF) { + return false; + } + } + else { /* chr == 0xF4 */ + if (p[1] < 0x80 || p[1] > 0x8F || p[2] < 0x80 || p[2] > 0xBF + || p[3] < 0x80 || p[3] > 0xBF) { + return false; + } + } + p += 4; + } + else { + return false; + } } + return (p == p_end); +} - if (total_size >= UINT32_MAX - || !(new_memory = wasm_malloc((uint32)total_size))) { - wasm_set_exception(module, "fail to enlarge memory."); - return false; +char * +wasm_const_str_list_insert(const uint8 *str, uint32 len, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size) +{ + StringNode *node, *node_next; + + if (!wasm_check_utf8_str(str, len)) { + set_error_buf(error_buf, error_buf_size, "invalid UTF-8 encoding"); + return NULL; + } + + if (len == 0) { + return ""; + } + else if (is_load_from_file_buf) { + /* As the file buffer can be referred to after loading, we use + the previous byte of leb encoded size to adjust the string: + move string 1 byte backward and then append '\0' */ + char *c_str = (char *)str - 1; + bh_memmove_s(c_str, len + 1, c_str + 1, len); + c_str[len] = '\0'; + return c_str; } - new_memory->num_bytes_per_page = memory->num_bytes_per_page; - new_memory->cur_page_count = total_page_count; - new_memory->max_page_count = memory->max_page_count; + /* Search const str list */ + node = module->const_str_list; + while (node) { + node_next = node->next; + if (strlen(node->str) == len && !memcmp(node->str, str, len)) + break; + node = node_next; + } + + if (node) { + return node->str; + } - new_memory->memory_data = new_memory->base_addr; + if (!(node = runtime_malloc(sizeof(StringNode) + len + 1, error_buf, + error_buf_size))) { + return NULL; + } - new_memory->global_data = new_memory->memory_data + - memory->num_bytes_per_page * total_page_count; - new_memory->global_data_size = memory->global_data_size; + node->str = ((char *)node) + sizeof(StringNode); + bh_memcpy_s(node->str, len + 1, str, len); + node->str[len] = '\0'; - new_memory->end_addr = new_memory->global_data + memory->global_data_size; + if (!module->const_str_list) { + /* set as head */ + module->const_str_list = node; + node->next = NULL; + } + else { + /* insert it */ + node->next = module->const_str_list; + module->const_str_list = node; + } - /* Copy memory data */ - bh_memcpy_s(new_memory->memory_data, - (uint32)(memory->global_data - memory->memory_data), - memory->memory_data, - (uint32)(memory->global_data - memory->memory_data)); - /* Copy global data */ - bh_memcpy_s(new_memory->global_data, new_memory->global_data_size, - memory->global_data, memory->global_data_size); - /* Init free space of new memory */ - memset(new_memory->memory_data + memory->num_bytes_per_page * memory->cur_page_count, - 0, memory->num_bytes_per_page * (total_page_count - memory->cur_page_count)); + return node->str; +} - new_memory->heap_data = memory->heap_data; - new_memory->heap_data_end = memory->heap_data_end; - new_memory->heap_handle = memory->heap_handle; - new_memory->heap_base_offset = memory->heap_base_offset; +bool +wasm_set_module_name(WASMModule *module, const char *name, char *error_buf, + uint32_t error_buf_size) +{ + if (!name) + return false; - module->memories[0] = module->default_memory = new_memory; - wasm_free(memory); - return true; -#else /* else of WASM_ENABLE_MEMORY_GROW */ - wasm_set_exception(module, "unsupported operation: enlarge memory."); - return false; -#endif /* end of WASM_ENABLE_MEMORY_GROW */ + module->name = + wasm_const_str_list_insert((const uint8 *)name, (uint32)strlen(name), + module, false, error_buf, error_buf_size); + return module->name != NULL; } +const char * +wasm_get_module_name(WASMModule *module) +{ + return module->name; +} diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 85f5d72426..98913e9fee 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -7,6 +7,8 @@ #define _WASM_RUNTIME_H #include "wasm.h" +#include "bh_atomic.h" +#include "bh_bitmap.h" #include "bh_hashmap.h" #include "../common/wasm_runtime_common.h" #include "../common/wasm_exec_env.h" @@ -15,66 +17,201 @@ extern "C" { #endif -typedef struct WASMMemoryInstance { +#define EXCEPTION_BUF_LEN 128 + +typedef struct WASMModuleInstance WASMModuleInstance; +typedef struct WASMFunctionInstance WASMFunctionInstance; +typedef struct WASMMemoryInstance WASMMemoryInstance; +typedef struct WASMTableInstance WASMTableInstance; +typedef struct WASMGlobalInstance WASMGlobalInstance; +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMTagInstance WASMTagInstance; +#endif + +/** + * When LLVM JIT, WAMR compiler or AOT is enabled, we should ensure that + * some offsets of the same field in the interpreter module instance and + * aot module instance are the same, so that the LLVM JITed/AOTed code + * can smoothly access the interpreter module instance. + * Same for the memory instance and table instance. + * We use the macro DefPointer to define some related pointer fields. + */ +#if (WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 \ + || WASM_ENABLE_AOT != 0) \ + && UINTPTR_MAX == UINT32_MAX +/* Add u32 padding if LLVM JIT, WAMR compiler or AOT is enabled on + 32-bit platform */ +#define DefPointer(type, field) \ + type field; \ + uint32 field##_padding +#else +#define DefPointer(type, field) type field +#endif + +typedef enum WASMExceptionID { + EXCE_UNREACHABLE = 0, + EXCE_OUT_OF_MEMORY, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + EXCE_INTEGER_OVERFLOW, + EXCE_INTEGER_DIVIDE_BY_ZERO, + EXCE_INVALID_CONVERSION_TO_INTEGER, + EXCE_INVALID_FUNCTION_TYPE_INDEX, + EXCE_INVALID_FUNCTION_INDEX, + EXCE_UNDEFINED_ELEMENT, + EXCE_UNINITIALIZED_ELEMENT, + EXCE_CALL_UNLINKED_IMPORT_FUNC, + EXCE_NATIVE_STACK_OVERFLOW, + EXCE_UNALIGNED_ATOMIC, + EXCE_AUX_STACK_OVERFLOW, + EXCE_AUX_STACK_UNDERFLOW, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, + EXCE_OPERAND_STACK_OVERFLOW, + EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC, + /* GC related exceptions */ + EXCE_NULL_FUNC_OBJ, + EXCE_NULL_STRUCT_OBJ, + EXCE_NULL_ARRAY_OBJ, + EXCE_NULL_I31_OBJ, + EXCE_NULL_REFERENCE, + EXCE_FAILED_TO_CREATE_RTT_TYPE, + EXCE_FAILED_TO_CREATE_STRUCT_OBJ, + EXCE_FAILED_TO_CREATE_ARRAY_OBJ, + EXCE_FAILED_TO_CREATE_EXTERNREF_OBJ, + EXCE_CAST_FAILURE, + EXCE_ARRAY_IDX_OOB, + EXCE_FAILED_TO_CREATE_STRING, + EXCE_FAILED_TO_CREATE_STRINGREF, + EXCE_FAILED_TO_CREATE_STRINGVIEW, + EXCE_FAILED_TO_ENCODE_STRING, + EXCE_ALREADY_THROWN, + EXCE_NUM, +} WASMExceptionID; + +typedef union { + uint64 u64; + uint32 u32[2]; +} MemBound; + +typedef struct WASMSharedHeap { + /* The global shared heap list maintained in runtime, used for runtime + * destroy */ + DefPointer(struct WASMSharedHeap *, next); + /* The logical shared heap chain the shared heap in */ + DefPointer(struct WASMSharedHeap *, chain_next); + /* Will be null if shared heap is created from pre allocated memory chunk + * and don't need to dynamic malloc and free */ + DefPointer(void *, heap_handle); + DefPointer(uint8 *, base_addr); + uint64 size; + uint64 start_off_mem64; + uint64 start_off_mem32; + /* The number of wasm apps it attached to, for a shared heap chain, only the + * list head need to maintain the valid attached_count */ + uint8 attached_count; +} WASMSharedHeap; + +struct WASMMemoryInstance { + /* Module type */ + uint32 module_type; + + /* Whether the memory is shared */ + uint8 is_shared_memory; + + /* Whether the memory has 64-bit memory addresses */ + uint8 is_memory64; + + /* Reference count of the memory instance: + 0: non-shared memory, > 0: shared memory */ + bh_atomic_16_t ref_count; + + /* Four-byte paddings to ensure the layout of WASMMemoryInstance is the same + * in both 64-bit and 32-bit */ + uint8 _paddings[4]; + /* Number bytes per page */ uint32 num_bytes_per_page; /* Current page count */ uint32 cur_page_count; /* Maximum page count */ uint32 max_page_count; + /* Memory data size */ + uint64 memory_data_size; + /** + * Memory data begin address, Note: + * the app-heap might be inserted in to the linear memory, + * when memory is re-allocated, the heap data and memory data + * must be copied to new memory also + */ + DefPointer(uint8 *, memory_data); + /* Memory data end address */ + DefPointer(uint8 *, memory_data_end); /* Heap data base address */ - uint8 *heap_data; + DefPointer(uint8 *, heap_data); /* Heap data end address */ - uint8 *heap_data_end; + DefPointer(uint8 *, heap_data_end); /* The heap created */ - void *heap_handle; - /* Heap base offset of wasm app */ - int32 heap_base_offset; - - /* Memory data */ - uint8 *memory_data; - /* Global data of global instances */ - uint8 *global_data; - uint32 global_data_size; - - /* End address of memory */ - uint8 *end_addr; - - /* Base address, the layout is: - thunk_argv data + thunk arg offsets + - memory data + global data - memory data init size is: num_bytes_per_page * cur_page_count - global data size is calculated in module instantiating - Note: when memory is re-allocated, the thunk argv data, thunk - argv offsets and memory data must be copied to new memory also. - */ - uint8 base_addr[1]; -} WASMMemoryInstance; - -typedef struct WASMTableInstance { - /* The element type, TABLE_ELEM_TYPE_ANY_FUNC currently */ + DefPointer(void *, heap_handle); + /* TODO: use it to replace the g_shared_memory_lock */ + DefPointer(korp_mutex *, memory_lock); + +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_AOT != 0 + MemBound mem_bound_check_1byte; + MemBound mem_bound_check_2bytes; + MemBound mem_bound_check_4bytes; + MemBound mem_bound_check_8bytes; + MemBound mem_bound_check_16bytes; +#endif +}; + +/* WASMTableInstance is used to represent table instance in + * runtime, to compute the table element address with index + * we need to know the element type and the element ref type. + * For pointer type, it's 32-bit or 64-bit, align up to 8 bytes + * to simplify the computation. + * And each struct member should be 4-byte or 8-byte aligned. + */ +struct WASMTableInstance { + /* The element type */ uint8 elem_type; + uint8 is_table64; + uint8 __padding__[6]; + union { +#if WASM_ENABLE_GC != 0 + WASMRefType *elem_ref_type; +#endif + uint64 __padding__; + } elem_ref_type; /* Current size */ uint32 cur_size; /* Maximum size */ uint32 max_size; - /* Base address */ - uint8 base_addr[1]; -} WASMTableInstance; + /* Table elements */ + table_elem_type_t elems[1]; +}; -typedef struct WASMGlobalInstance { +struct WASMGlobalInstance { /* value type, VALUE_TYPE_I32/I64/F32/F64 */ uint8 type; /* mutable or constant */ bool is_mutable; - /* data offset to base_addr of WASMMemoryInstance */ + /* data offset to the address of initial_value, started from the end of + * WASMMemoryInstance(start of WASMGlobalInstance)*/ uint32 data_offset; /* initial value */ WASMValue initial_value; -} WASMGlobalInstance; +#if WASM_ENABLE_GC != 0 + WASMRefType *ref_type; +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just for import, keep the reference here */ + WASMModuleInstance *import_module_inst; + WASMGlobalInstance *import_global_inst; +#endif +}; -typedef struct WASMFunctionInstance { +struct WASMFunctionInstance { /* whether it is import function or WASM function */ bool is_import_func; /* parameter count */ @@ -87,6 +224,10 @@ typedef struct WASMFunctionInstance { uint16 ret_cell_num; /* cell num of local variables, 0 for import function */ uint16 local_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + /* cell num of consts */ + uint16 const_cell_num; +#endif uint16 *local_offsets; /* parameter types */ uint8 *param_types; @@ -96,65 +237,265 @@ typedef struct WASMFunctionInstance { WASMFunctionImport *func_import; WASMFunction *func; } u; -} WASMFunctionInstance; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *import_module_inst; + WASMFunctionInstance *import_func_inst; +#endif +#if WASM_ENABLE_PERF_PROFILING != 0 + /* total execution time */ + uint64 total_exec_time; + /* total execution count */ + uint32 total_exec_cnt; + /* children execution time */ + uint64 children_exec_time; +#endif +}; + +#if WASM_ENABLE_TAGS != 0 +struct WASMTagInstance { + bool is_import_tag; + /* tag attribute */ + uint8 attribute; + /* tag type index */ + uint32 type; + union { + WASMTagImport *tag_import; + WASMTag *tag; + } u; + +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *import_module_inst; + WASMTagInstance *import_tag_inst; +#endif +}; +#endif +#if WASM_ENABLE_EXCE_HANDLING != 0 +#define INVALID_TAGINDEX ((uint32)0xFFFFFFFF) +#define SET_INVALID_TAGINDEX(tag) (tag = INVALID_TAGINDEX) +#define IS_INVALID_TAGINDEX(tag) ((tag & INVALID_TAGINDEX) == INVALID_TAGINDEX) +#endif typedef struct WASMExportFuncInstance { char *name; WASMFunctionInstance *function; } WASMExportFuncInstance; -typedef struct WASMModuleInstance { - /* Module instance type, for module instance loaded from - WASM bytecode binary, this field is Wasm_Module_Bytecode; - for module instance loaded from AOT file, this field is - Wasm_Module_AoT, and this structure should be treated as - AOTModuleInstance structure. */ - uint32 module_type; +typedef struct WASMExportGlobInstance { + char *name; + WASMGlobalInstance *global; +} WASMExportGlobInstance; - uint32 memory_count; - uint32 table_count; - uint32 global_count; - uint32 function_count; - uint32 export_func_count; +typedef struct WASMExportTabInstance { + char *name; + WASMTableInstance *table; +} WASMExportTabInstance; + +typedef struct WASMExportMemInstance { + char *name; + WASMMemoryInstance *memory; +} WASMExportMemInstance; + +#if WASM_ENABLE_TAGS != 0 +typedef struct WASMExportTagInstance { + char *name; + WASMTagInstance *tag; +} WASMExportTagInstance; +#endif + +/* wasm-c-api import function info */ +typedef struct CApiFuncImport { + /* host func pointer after linked */ + void *func_ptr_linked; + /* whether the host func has env argument */ + bool with_env_arg; + /* the env argument of the host func */ + void *env_arg; +} CApiFuncImport; + +/* The common part of WASMModuleInstanceExtra and AOTModuleInstanceExtra */ +typedef struct WASMModuleInstanceExtraCommon { +#if WASM_ENABLE_MODULE_INST_CONTEXT != 0 + void *contexts[WASM_MAX_INSTANCE_CONTEXTS]; +#endif +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + /* Disable bounds checks or not */ + bool disable_bounds_checks; +#endif +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_bitmap *data_dropped; +#endif +#if WASM_ENABLE_REF_TYPES != 0 + bh_bitmap *elem_dropped; +#endif + +#if WASM_ENABLE_GC != 0 + /* The gc heap memory pool */ + uint8 *gc_heap_pool; + /* The gc heap created */ + void *gc_heap_handle; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + korp_mutex exception_lock; +#endif +} WASMModuleInstanceExtraCommon; + +/* Extra info of WASM module instance for interpreter/jit mode */ +typedef struct WASMModuleInstanceExtra { + WASMModuleInstanceExtraCommon common; - WASMMemoryInstance **memories; - WASMTableInstance **tables; WASMGlobalInstance *globals; WASMFunctionInstance *functions; - WASMExportFuncInstance *export_functions; - WASMMemoryInstance *default_memory; - WASMTableInstance *default_table; + uint32 global_count; + uint32 function_count; WASMFunctionInstance *start_function; + WASMFunctionInstance *malloc_function; + WASMFunctionInstance *free_function; + WASMFunctionInstance *retain_function; + + RunningMode running_mode; - WASMModule *module; +#if WASM_ENABLE_MULTI_MODULE != 0 + bh_list sub_module_inst_list_head; + bh_list *sub_module_inst_list; + /* linked table instances of import table instances */ + WASMTableInstance **table_insts_linked; +#endif -#if WASM_ENABLE_LIBC_WASI != 0 - WASIContext *wasi_ctx; +#if WASM_ENABLE_TAGS != 0 + uint32 tag_count; + uint32 export_tag_count; + WASMTagInstance *tags; + WASMExportTagInstance *export_tags; + void **import_tag_ptrs; #endif - uint32 DYNAMICTOP_PTR_offset; - uint32 temp_ret; - uint32 llvm_stack; +#if WASM_ENABLE_MEMORY_PROFILING != 0 + uint32 max_aux_stack_used; +#endif - /* Default WASM stack size of threads of this Module instance. */ - uint32 default_wasm_stack_size; +#if WASM_ENABLE_SHARED_HEAP != 0 + /* + * Adjusted shared heap based addr to simple the calculation + * in the aot code. The value is: + * shared_heap->base_addr - shared_heap->start_off + */ + uint8 *shared_heap_base_addr_adj; + MemBound shared_heap_start_off; + MemBound shared_heap_end_off; + WASMSharedHeap *shared_heap; +#endif - /* The exception buffer of wasm interpreter for current thread. */ - char cur_exception[128]; +#if WASM_ENABLE_DEBUG_INTERP != 0 \ + || (WASM_ENABLE_FAST_JIT != 0 && WASM_ENABLE_JIT != 0 \ + && WASM_ENABLE_LAZY_JIT != 0) + WASMModuleInstance *next; +#endif +} WASMModuleInstanceExtra; + +struct AOTFuncPerfProfInfo; + +struct WASMModuleInstance { + /* Module instance type, for module instance loaded from + WASM bytecode binary, this field is Wasm_Module_Bytecode; + for module instance loaded from AOT file, this field is + Wasm_Module_AoT, and this structure should be treated as + AOTModuleInstance structure. */ + uint32 module_type; + + uint32 memory_count; + DefPointer(WASMMemoryInstance **, memories); + + /* global and table info */ + uint32 global_data_size; + uint32 table_count; + DefPointer(uint8 *, global_data); + /* For AOTModuleInstance, it denotes `AOTTableInstance *` */ + DefPointer(WASMTableInstance **, tables); + + /* import func ptrs + llvm jit func ptrs */ + DefPointer(void **, func_ptrs); - /* The custom data that can be set/get by - * wasm_set_custom_data/wasm_get_custom_data */ - void *custom_data; + /* function type indexes */ + DefPointer(uint32 *, func_type_indexes); - /* Main exec env */ - WASMExecEnv *main_exec_env; -} WASMModuleInstance; + uint32 export_func_count; + uint32 export_global_count; + uint32 export_memory_count; + uint32 export_table_count; + /* For AOTModuleInstance, it denotes `AOTFunctionInstance *` */ + DefPointer(WASMExportFuncInstance *, export_functions); + DefPointer(WASMExportGlobInstance *, export_globals); + DefPointer(WASMExportMemInstance *, export_memories); + DefPointer(WASMExportTabInstance *, export_tables); + + /* The exception buffer of wasm interpreter for current thread. */ + char cur_exception[EXCEPTION_BUF_LEN]; + + /* The WASM module or AOT module, for AOTModuleInstance, + it denotes `AOTModule *` */ + DefPointer(WASMModule *, module); + + DefPointer(WASMExecEnv *, exec_env_singleton); + /* Array of function pointers to import functions, + not available in AOTModuleInstance */ + DefPointer(void **, import_func_ptrs); + /* Array of function pointers to fast jit functions, + 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); + /* Stack frames, used in call stack dump and perf profiling */ + DefPointer(Vector *, frames); + /* Function performance profiling info list, only available + in AOTModuleInstance */ + DefPointer(struct AOTFuncPerfProfInfo *, func_perf_profilings); + DefPointer(CApiFuncImport *, c_api_func_imports); + /* Pointer to the exec env currently used */ + DefPointer(WASMExecEnv *, cur_exec_env); + /* WASM/AOT module extra info, for AOTModuleInstance, + it denotes `AOTModuleInstanceExtra *` */ + DefPointer(WASMModuleInstanceExtra *, e); + + /* Default WASM operand stack size */ + uint32 default_wasm_stack_size; + uint32 reserved[7]; + + /* + * +------------------------------+ <-- memories + * | WASMMemoryInstance[mem_count], mem_count is always 1 for LLVM JIT/AOT + * +------------------------------+ <-- global_data + * | global data + * +------------------------------+ <-- tables + * | WASMTableInstance[table_count] + * +------------------------------+ <-- e + * | WASMModuleInstanceExtra + * +------------------------------+ + */ + union { + uint64 _make_it_8_byte_aligned_; + WASMMemoryInstance memory_instances[1]; + uint8 bytes[1]; + } global_table_data; +}; struct WASMInterpFrame; typedef struct WASMInterpFrame WASMRuntimeFrame; +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMSubModInstNode { + bh_list_link l; + /* point to a string pool */ + const char *module_name; + WASMModuleInstance *module_inst; +} WASMSubModInstNode; +#endif + /** * Return the code block of a function. * @@ -162,10 +503,14 @@ typedef struct WASMInterpFrame WASMRuntimeFrame; * * @return the code block of the function */ -static inline uint8* +static inline uint8 * wasm_get_func_code(WASMFunctionInstance *func) { +#if WASM_ENABLE_FAST_INTERP == 0 return func->is_import_func ? NULL : func->u.func->code; +#else + return func->is_import_func ? NULL : func->u.func->code_compiled; +#endif } /** @@ -175,100 +520,396 @@ wasm_get_func_code(WASMFunctionInstance *func) * * @return the code block end of the function */ -static inline uint8* +static inline uint8 * wasm_get_func_code_end(WASMFunctionInstance *func) { +#if WASM_ENABLE_FAST_INTERP == 0 + return func->is_import_func ? NULL + : func->u.func->code + func->u.func->code_size; +#else return func->is_import_func - ? NULL : func->u.func->code + func->u.func->code_size; + ? NULL + : func->u.func->code_compiled + func->u.func->code_compiled_size; +#endif } WASMModule * -wasm_load(const uint8 *buf, uint32 size, - char *error_buf, uint32 error_buf_size); +wasm_load(uint8 *buf, uint32 size, +#if WASM_ENABLE_MULTI_MODULE != 0 + bool main_module, +#endif + const LoadArgs *args, char *error_buf, uint32 error_buf_size); WASMModule * -wasm_load_from_sections(WASMSection *section_list, - char *error_buf, uint32_t error_buf_size); +wasm_load_from_sections(WASMSection *section_list, char *error_buf, + uint32 error_buf_size); void wasm_unload(WASMModule *module); +bool +wasm_resolve_symbols(WASMModule *module); + +bool +wasm_resolve_import_func(const WASMModule *module, + WASMFunctionImport *function); + WASMModuleInstance * -wasm_instantiate(WASMModule *module, - uint32 stack_size, uint32 heap_size, - char *error_buf, uint32 error_buf_size); +wasm_instantiate(WASMModule *module, WASMModuleInstance *parent, + WASMExecEnv *exec_env_main, + const struct InstantiationArgs2 *args, char *error_buf, + uint32 error_buf_size); void -wasm_deinstantiate(WASMModuleInstance *module_inst); +wasm_dump_perf_profiling(const WASMModuleInstance *module_inst); -WASMFunctionInstance * -wasm_lookup_function(const WASMModuleInstance *module_inst, - const char *name, const char *signature); +double +wasm_summarize_wasm_execute_time(const WASMModuleInstance *inst); + +double +wasm_get_wasm_func_exec_time(const WASMModuleInstance *inst, + const char *func_name); + +void +wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst); bool -wasm_call_function(WASMExecEnv *exec_env, - WASMFunctionInstance *function, - unsigned argc, uint32 argv[]); +wasm_set_running_mode(WASMModuleInstance *module_inst, + RunningMode running_mode); + +WASMFunctionInstance * +wasm_lookup_function(const WASMModuleInstance *module_inst, const char *name); + +WASMMemoryInstance * +wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name); + +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMGlobalInstance * +wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name); + +WASMTableInstance * +wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name); + +#if WASM_ENABLE_TAGS != 0 +WASMTagInstance * +wasm_lookup_tag(const WASMModuleInstance *module_inst, const char *name, + const char *signature); +#endif + +#endif bool -wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst, - WASMFunctionInstance *function, - unsigned argc, uint32 argv[]); +wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function, + unsigned argc, uint32 argv[]); void wasm_set_exception(WASMModuleInstance *module, const char *exception); -const char* +void +wasm_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id); + +const char * wasm_get_exception(WASMModuleInstance *module); -int32 -wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size); +/** + * @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); + +uint64 +wasm_module_malloc_internal(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 size, + void **p_native_addr); + +uint64 +wasm_module_realloc_internal(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 ptr, uint64 size, + void **p_native_addr); + +void +wasm_module_free_internal(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, uint64 ptr); + +uint64 +wasm_module_malloc(WASMModuleInstance *module_inst, uint64 size, + void **p_native_addr); + +uint64 +wasm_module_realloc(WASMModuleInstance *module_inst, uint64 ptr, uint64 size, + void **p_native_addr); + +void +wasm_module_free(WASMModuleInstance *module_inst, uint64 ptr); + +uint64 +wasm_module_dup_data(WASMModuleInstance *module_inst, const char *src, + uint64 size); + +/** + * Check whether the app address and the buf is inside the linear memory, + * and convert the app address into native address + */ +bool +wasm_check_app_addr_and_convert(WASMModuleInstance *module_inst, bool is_str, + uint64 app_buf_addr, uint64 app_buf_size, + void **p_native_addr); + +WASMMemoryInstance * +wasm_get_default_memory(WASMModuleInstance *module_inst); + +WASMMemoryInstance * +wasm_get_memory_with_idx(WASMModuleInstance *module_inst, uint32 index); + +bool +wasm_enlarge_memory(WASMModuleInstance *module_inst, uint32 inc_page_count); + +bool +wasm_enlarge_memory_with_idx(WASMModuleInstance *module_inst, + uint32 inc_page_count, uint32 memidx); + +bool +wasm_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, + uint32 argc, uint32 argv[]); + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_set_aux_stack(WASMExecEnv *exec_env, uint64 start_offset, uint32 size); + +bool +wasm_get_aux_stack(WASMExecEnv *exec_env, uint64 *start_offset, uint32 *size); +#endif + +void +wasm_get_module_mem_consumption(const WASMModule *module, + WASMModuleMemConsumption *mem_conspn); + +void +wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module, + WASMModuleInstMemConsumption *mem_conspn); + +#if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 +static inline bool +wasm_elem_is_active(uint32 mode) +{ + return (mode & 0x1) == 0x0; +} + +static inline bool +wasm_elem_is_passive(uint32 mode) +{ + return (mode & 0x1) == 0x1; +} + +static inline bool +wasm_elem_is_declarative(uint32 mode) +{ + return (mode & 0x3) == 0x3; +} + +bool +wasm_enlarge_table(WASMModuleInstance *module_inst, uint32 table_idx, + uint32 inc_entries, table_elem_type_t init_val); +#endif /* WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */ + +#if WASM_ENABLE_GC != 0 +void * +wasm_create_func_obj(WASMModuleInstance *module_inst, uint32 func_idx, + bool throw_exce, char *error_buf, uint32 error_buf_size); + +bool +wasm_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap); + +#endif + +static inline WASMTableInstance * +wasm_get_table_inst(const WASMModuleInstance *module_inst, uint32 tbl_idx) +{ + /* careful, it might be a table in another module */ + WASMTableInstance *tbl_inst = module_inst->tables[tbl_idx]; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (tbl_idx < module_inst->module->import_table_count + && module_inst->e->table_insts_linked[tbl_idx]) { + tbl_inst = module_inst->e->table_insts_linked[tbl_idx]; + } +#endif + bh_assert(tbl_inst); + return tbl_inst; +} + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + +#if WASM_ENABLE_COPY_CALL_STACK != 0 +uint32 +wasm_interp_copy_callstack(WASMExecEnv *exec_env, WASMCApiFrame *buffer, + uint32 length, uint32 skip_n, char *error_buf, + uint32_t error_buf_size); +#endif // WASM_ENABLE_COPY_CALL_STACK + +bool +wasm_interp_create_call_stack(struct WASMExecEnv *exec_env); + +/** + * @brief Dump wasm call stack or get the size + * + * @param exec_env the execution environment + * @param print whether to print to stdout or not + * @param buf buffer to store the dumped content + * @param len length of the buffer + * + * @return when print is true, return the bytes printed out to stdout; when + * print is false and buf is NULL, return the size required to store the + * callstack content; when print is false and buf is not NULL, return the size + * dumped to the buffer, 0 means error and data in buf may be invalid + */ +uint32 +wasm_interp_dump_call_stack(struct WASMExecEnv *exec_env, bool print, char *buf, + uint32 len); +#endif + +const uint8 * +wasm_loader_get_custom_section(WASMModule *module, const char *name, + uint32 *len); +#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 void -wasm_module_free(WASMModuleInstance *module_inst, int32 ptr); +jit_set_exception_with_id(WASMModuleInstance *module_inst, uint32 id); + +/** + * Check whether the app address and the buf is inside the linear memory, + * and convert the app address into native address + */ +bool +jit_check_app_addr_and_convert(WASMModuleInstance *module_inst, bool is_str, + uint64 app_buf_addr, uint64 app_buf_size, + void **p_native_addr); +#endif /* end of WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \ + || WASM_ENABLE_WAMR_COMPILER != 0 */ + +#if WASM_ENABLE_FAST_JIT != 0 +bool +fast_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, + uint32 type_idx, uint32 argc, uint32 *argv); + +bool +fast_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, + struct WASMInterpFrame *prev_frame); +#endif + +#if WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 +bool +llvm_jit_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 elem_idx, + uint32 argc, uint32 *argv); -int32 -wasm_module_dup_data(WASMModuleInstance *module_inst, - const char *src, uint32 size); +bool +llvm_jit_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, uint32 argc, + uint32 *argv); +#if WASM_ENABLE_BULK_MEMORY != 0 bool -wasm_validate_app_addr(WASMModuleInstance *module_inst, - int32 app_offset, uint32 size); +llvm_jit_memory_init(WASMModuleInstance *module_inst, uint32 seg_index, + uint32 offset, uint32 len, size_t dst); bool -wasm_validate_app_str_addr(WASMModuleInstance *module_inst, - int32 app_offset); +llvm_jit_data_drop(WASMModuleInstance *module_inst, uint32 seg_index); +#endif + +#if WASM_ENABLE_REF_TYPES != 0 +void +llvm_jit_drop_table_seg(WASMModuleInstance *module_inst, uint32 tbl_seg_idx); +void +llvm_jit_table_init(WASMModuleInstance *module_inst, uint32 tbl_idx, + uint32 tbl_seg_idx, uint32 length, uint32 src_offset, + uint32 dst_offset); + +void +llvm_jit_table_copy(WASMModuleInstance *module_inst, uint32 src_tbl_idx, + uint32 dst_tbl_idx, uint32 length, uint32 src_offset, + uint32 dst_offset); + +void +llvm_jit_table_fill(WASMModuleInstance *module_inst, uint32 tbl_idx, + uint32 length, uintptr_t val, uint32 data_offset); + +uint32 +llvm_jit_table_grow(WASMModuleInstance *module_inst, uint32 tbl_idx, + uint32 inc_entries, uintptr_t init_val); +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 || WASM_ENABLE_PERF_PROFILING != 0 \ + || WASM_ENABLE_AOT_STACK_FRAME != 0 bool -wasm_validate_native_addr(WASMModuleInstance *module_inst, - void *native_ptr, uint32 size); +llvm_jit_alloc_frame(WASMExecEnv *exec_env, uint32 func_index); + +void +llvm_jit_free_frame(WASMExecEnv *exec_env); +void +llvm_jit_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame); +#endif + +#if WASM_ENABLE_GC != 0 void * -wasm_addr_app_to_native(WASMModuleInstance *module_inst, - int32 app_offset); +llvm_jit_create_func_obj(WASMModuleInstance *module_inst, uint32 func_idx, + bool throw_exce, char *error_buf, + uint32 error_buf_size); + +bool +llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst, + WASMObjectRef gc_obj, uint32 type_index); + +/* Whether func type1 is one of super types of func type2 */ +bool +llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst, + uint32 type_idx1, uint32 type_idx2); -int32 -wasm_addr_native_to_app(WASMModuleInstance *module_inst, - void *native_ptr); +WASMRttTypeRef +llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index); bool -wasm_get_app_addr_range(WASMModuleInstance *module_inst, - int32 app_offset, - int32 *p_app_start_offset, - int32 *p_app_end_offset); +llvm_array_init_with_data(WASMModuleInstance *module_inst, uint32 seg_index, + uint32 data_seg_offset, WASMArrayObjectRef array_obj, + uint32 elem_size, uint32 array_len); +#endif +#endif /* end of WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 */ + +#if WASM_ENABLE_LIBC_WASI != 0 && WASM_ENABLE_MULTI_MODULE != 0 +void +wasm_propagate_wasi_args(WASMModule *module); +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +void +exception_lock(WASMModuleInstance *module_inst); +void +exception_unlock(WASMModuleInstance *module_inst); +#else +#define exception_lock(module_inst) (void)(module_inst) +#define exception_unlock(module_inst) (void)(module_inst) +#endif bool -wasm_get_native_addr_range(WASMModuleInstance *module_inst, - uint8_t *native_ptr, - uint8_t **p_native_start_addr, - uint8_t **p_native_end_addr); +wasm_check_utf8_str(const uint8 *str, uint32 len); + +char * +wasm_const_str_list_insert(const uint8 *str, uint32 len, WASMModule *module, + bool is_load_from_file_buf, char *error_buf, + uint32 error_buf_size); bool -wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count); +wasm_set_module_name(WASMModule *module, const char *name, char *error_buf, + uint32_t error_buf_size); + +const char * +wasm_get_module_name(WASMModule *module); #ifdef __cplusplus } #endif #endif /* end of _WASM_RUNTIME_H */ - diff --git a/core/iwasm/libraries/debug-engine/debug_engine.c b/core/iwasm/libraries/debug-engine/debug_engine.c new file mode 100644 index 0000000000..24d57d7068 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/debug_engine.c @@ -0,0 +1,1433 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "debug_engine.h" +#include "gdbserver.h" +#include "handler.h" +#include "bh_platform.h" +#include "wasm_interp.h" +#include "wasm_opcode.h" +#include "wasm_runtime.h" + +static const uint8 break_instr[] = { DEBUG_OP_BREAK }; + +typedef struct WASMDebugEngine { + struct WASMDebugEngine *next; + WASMDebugControlThread *control_thread; + char ip_addr[128]; + int32 process_base_port; + bh_list debug_instance_list; + korp_mutex instance_list_lock; +} WASMDebugEngine; + +void +on_thread_stop_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env) +{ + os_mutex_lock(&debug_inst->wait_lock); + debug_inst->stopped_thread = exec_env; + + if (debug_inst->current_state == DBG_LAUNCHING) { + /* In launching phase, send a signal so that handle_threadstop_request + * can be woken up */ + os_cond_signal(&debug_inst->wait_cond); + } + os_mutex_unlock(&debug_inst->wait_lock); +} + +void +on_thread_exit_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env) +{ + os_mutex_lock(&debug_inst->wait_lock); + + /* DBG_LAUNCHING: exit when debugger detached, + * DBG_ERROR: exit when debugger error */ + if (debug_inst->current_state != DBG_LAUNCHING + && debug_inst->current_state != DBG_ERROR) { + /* only when exit normally the debugger thread will participate in + * teardown phase */ + debug_inst->stopped_thread = exec_env; + } + + os_mutex_unlock(&debug_inst->wait_lock); +} + +static WASMDebugEngine *g_debug_engine; + +static uint32 current_instance_id = 1; + +static uint32 +allocate_instance_id(void) +{ + uint32 id; + + bh_assert(g_debug_engine); + + os_mutex_lock(&g_debug_engine->instance_list_lock); + id = current_instance_id++; + os_mutex_unlock(&g_debug_engine->instance_list_lock); + + return id; +} + +static bool +is_thread_running(WASMDebugControlThread *control_thread) +{ + return control_thread->status == RUNNING; +} + +static bool +is_thread_stopped(WASMDebugControlThread *control_thread) +{ + return control_thread->status == STOPPED; +} + +static bool +is_thread_detached(WASMDebugControlThread *control_thread) +{ + return control_thread->status == DETACHED; +} + +static void * +control_thread_routine(void *arg) +{ + WASMDebugInstance *debug_inst = (WASMDebugInstance *)arg; + WASMDebugControlThread *control_thread = NULL; + + control_thread = debug_inst->control_thread; + bh_assert(control_thread); + + os_mutex_lock(&debug_inst->wait_lock); + + control_thread->status = RUNNING; + + debug_inst->id = allocate_instance_id(); + + control_thread->debug_engine = g_debug_engine; + control_thread->debug_instance = debug_inst; + bh_strcpy_s(control_thread->ip_addr, sizeof(control_thread->ip_addr), + g_debug_engine->ip_addr); + if (control_thread->port == -1) { + control_thread->port = + (g_debug_engine->process_base_port == 0) + ? 0 + : g_debug_engine->process_base_port + debug_inst->id - 1; + } + + LOG_WARNING("control thread of debug object %p start\n", debug_inst); + + control_thread->server = + wasm_create_gdbserver(control_thread->ip_addr, &control_thread->port); + + if (!control_thread->server) { + LOG_ERROR("Failed to create debug server\n"); + control_thread->port = 0; + os_cond_signal(&debug_inst->wait_cond); + os_mutex_unlock(&debug_inst->wait_lock); + return NULL; + } + + control_thread->server->thread = control_thread; + + /* + * wasm gdbserver created, the execution thread + * doesn't need to wait for the debugger connection, + * so we wake up the execution thread before listen + */ + os_cond_signal(&debug_inst->wait_cond); + os_mutex_unlock(&debug_inst->wait_lock); + + if (!wasm_gdbserver_listen(control_thread->server)) { + LOG_ERROR("Failed while listening for debugger\n"); + goto fail; + } + + /* outer infinite loop: try to connect with the debugger */ + while (true) { + /* wait lldb client to connect */ + if (!wasm_gdbserver_accept(control_thread->server)) { + LOG_ERROR("Failed while accepting debugger connection\n"); + goto fail; + } + + control_thread->status = RUNNING; + /* when reattached, send signal */ + wasm_cluster_send_signal_all(debug_inst->cluster, WAMR_SIG_SINGSTEP); + + /* inner infinite loop: keep serving until detach */ + while (true) { + os_mutex_lock(&control_thread->wait_lock); + if (is_thread_running(control_thread)) { + /* send thread stop reply */ + if (debug_inst->stopped_thread + && debug_inst->current_state == APP_RUNNING) { + uint32 status; + korp_tid tid; + + status = (uint32)debug_inst->stopped_thread->current_status + ->signal_flag; + tid = debug_inst->stopped_thread->handle; + + if (debug_inst->stopped_thread->current_status + ->running_status + == STATUS_EXIT) { + /* If the thread exits, report "W00" if it's the last + * thread in the cluster, otherwise ignore this event */ + status = 0; + + /* By design, all the other threads should have been + * stopped at this moment, so it is safe to access the + * exec_env_list.len without lock */ + if (debug_inst->cluster->exec_env_list.len != 1) { + debug_inst->stopped_thread = NULL; + /* The exiting thread may wait for the signal */ + os_cond_signal(&debug_inst->wait_cond); + os_mutex_unlock(&control_thread->wait_lock); + continue; + } + } + + wasm_debug_instance_set_cur_thread( + debug_inst, debug_inst->stopped_thread->handle); + + send_thread_stop_status(control_thread->server, status, + tid); + + debug_inst->current_state = APP_STOPPED; + debug_inst->stopped_thread = NULL; + + if (status == 0) { + /* The exiting thread may wait for the signal */ + os_cond_signal(&debug_inst->wait_cond); + } + } + + /* Processing incoming requests */ + if (!wasm_gdbserver_handle_packet(control_thread->server)) { + control_thread->status = STOPPED; + LOG_ERROR("An error occurs when handling a packet\n"); + os_mutex_unlock(&control_thread->wait_lock); + goto fail; + } + } + else if (is_thread_detached(control_thread)) { + os_mutex_unlock(&control_thread->wait_lock); + break; + } + else if (is_thread_stopped(control_thread)) { + os_mutex_unlock(&control_thread->wait_lock); + return NULL; + } + os_mutex_unlock(&control_thread->wait_lock); + } + } +fail: + wasm_debug_instance_on_failure(debug_inst); + LOG_VERBOSE("control thread of debug object [%p] stopped with failure\n", + debug_inst); + return NULL; +} + +static WASMDebugControlThread * +wasm_debug_control_thread_create(WASMDebugInstance *debug_instance, int32 port) +{ + WASMDebugControlThread *control_thread; + + if (!(control_thread = + wasm_runtime_malloc(sizeof(WASMDebugControlThread)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory"); + return NULL; + } + memset(control_thread, 0, sizeof(WASMDebugControlThread)); + control_thread->port = port; + + if (os_mutex_init(&control_thread->wait_lock) != 0) + goto fail; + + debug_instance->control_thread = control_thread; + + os_mutex_lock(&debug_instance->wait_lock); + + if (0 + != os_thread_create(&control_thread->tid, control_thread_routine, + debug_instance, APP_THREAD_STACK_SIZE_DEFAULT)) { + os_mutex_unlock(&debug_instance->wait_lock); + goto fail1; + } + + /* wait until the debug control thread ready */ + os_cond_wait(&debug_instance->wait_cond, &debug_instance->wait_lock); + os_mutex_unlock(&debug_instance->wait_lock); + if (!control_thread->server) { + os_thread_join(control_thread->tid, NULL); + goto fail1; + } + + os_mutex_lock(&g_debug_engine->instance_list_lock); + /* create control thread success, append debug instance to debug engine */ + bh_list_insert(&g_debug_engine->debug_instance_list, debug_instance); + os_mutex_unlock(&g_debug_engine->instance_list_lock); + + /* If we set WAMR_SIG_STOP here, the VSCode debugger adaptor will raise an + * exception in the UI. We use WAMR_SIG_SINGSTEP to avoid this exception for + * better user experience */ + wasm_cluster_send_signal_all(debug_instance->cluster, WAMR_SIG_SINGSTEP); + + return control_thread; + +fail1: + os_mutex_destroy(&control_thread->wait_lock); +fail: + wasm_runtime_free(control_thread); + return NULL; +} + +static void +wasm_debug_control_thread_destroy(WASMDebugInstance *debug_instance) +{ + WASMDebugControlThread *control_thread = debug_instance->control_thread; + + LOG_VERBOSE("stopping control thread of debug object [%p]\n", + debug_instance); + control_thread->status = STOPPED; + os_mutex_lock(&control_thread->wait_lock); + wasm_close_gdbserver(control_thread->server); + os_mutex_unlock(&control_thread->wait_lock); + os_thread_join(control_thread->tid, NULL); + wasm_runtime_free(control_thread->server); + + os_mutex_destroy(&control_thread->wait_lock); + wasm_runtime_free(control_thread); +} + +static WASMDebugEngine * +wasm_debug_engine_create(void) +{ + WASMDebugEngine *engine; + + if (!(engine = wasm_runtime_malloc(sizeof(WASMDebugEngine)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory"); + return NULL; + } + memset(engine, 0, sizeof(WASMDebugEngine)); + + if (os_mutex_init(&engine->instance_list_lock) != 0) { + wasm_runtime_free(engine); + LOG_ERROR("WASM Debug Engine error: failed to init mutex"); + return NULL; + } + + /* reset current instance id */ + current_instance_id = 1; + + bh_list_init(&engine->debug_instance_list); + return engine; +} + +void +wasm_debug_engine_destroy(void) +{ + if (g_debug_engine) { + wasm_debug_handler_deinit(); + os_mutex_destroy(&g_debug_engine->instance_list_lock); + wasm_runtime_free(g_debug_engine); + g_debug_engine = NULL; + } +} + +bool +wasm_debug_engine_init(char *ip_addr, int32 process_port) +{ + if (wasm_debug_handler_init() != 0) { + return false; + } + + if (g_debug_engine == NULL) { + g_debug_engine = wasm_debug_engine_create(); + } + + if (g_debug_engine) { + g_debug_engine->process_base_port = + (process_port > 0) ? process_port : 0; + if (ip_addr) + snprintf(g_debug_engine->ip_addr, sizeof(g_debug_engine->ip_addr), + "%s", ip_addr); + else + snprintf(g_debug_engine->ip_addr, sizeof(g_debug_engine->ip_addr), + "%s", "127.0.0.1"); + } + else { + wasm_debug_handler_deinit(); + } + + return g_debug_engine != NULL ? true : false; +} + +/* A debug Instance is a debug "process" in gdb remote protocol + and bound to a runtime cluster */ +WASMDebugInstance * +wasm_debug_instance_create(WASMCluster *cluster, int32 port) +{ + WASMDebugInstance *instance; + WASMExecEnv *exec_env = NULL; + wasm_module_inst_t module_inst = NULL; + + if (!g_debug_engine) { + return NULL; + } + + if (!(instance = wasm_runtime_malloc(sizeof(WASMDebugInstance)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory"); + return NULL; + } + memset(instance, 0, sizeof(WASMDebugInstance)); + + if (os_mutex_init(&instance->wait_lock) != 0) { + goto fail1; + } + + if (os_cond_init(&instance->wait_cond) != 0) { + goto fail2; + } + + bh_list_init(&instance->break_point_list); + bh_list_init(&instance->watch_point_list_read); + bh_list_init(&instance->watch_point_list_write); + + instance->cluster = cluster; + exec_env = bh_list_first_elem(&cluster->exec_env_list); + bh_assert(exec_env); + + instance->current_tid = exec_env->handle; + + module_inst = wasm_runtime_get_module_inst(exec_env); + bh_assert(module_inst); + + /* Allocate linear memory for evaluating expressions during debugging. If + * the allocation failed, the debugger will not be able to evaluate + * expressions */ + instance->exec_mem_info.size = DEBUG_EXECUTION_MEMORY_SIZE; + instance->exec_mem_info.start_offset = wasm_runtime_module_malloc( + module_inst, (uint64)instance->exec_mem_info.size, NULL); + if (instance->exec_mem_info.start_offset == 0) { + LOG_WARNING( + "WASM Debug Engine warning: failed to allocate linear memory for " + "execution. \n" + "Will not be able to evaluate expressions during " + "debugging"); + } + instance->exec_mem_info.current_pos = instance->exec_mem_info.start_offset; + + if (!wasm_debug_control_thread_create(instance, port)) { + LOG_ERROR("WASM Debug Engine error: failed to create control thread"); + goto fail3; + } + + wasm_cluster_set_debug_inst(cluster, instance); + + return instance; + +fail3: + os_cond_destroy(&instance->wait_cond); +fail2: + os_mutex_destroy(&instance->wait_lock); +fail1: + wasm_runtime_free(instance); + + return NULL; +} + +static void +wasm_debug_instance_destroy_breakpoints(WASMDebugInstance *instance) +{ + WASMDebugBreakPoint *breakpoint, *next_bp; + + breakpoint = bh_list_first_elem(&instance->break_point_list); + while (breakpoint) { + next_bp = bh_list_elem_next(breakpoint); + + bh_list_remove(&instance->break_point_list, breakpoint); + wasm_runtime_free(breakpoint); + + breakpoint = next_bp; + } +} + +static void +wasm_debug_instance_destroy_watchpoints(WASMDebugInstance *instance, + bh_list *watchpoints) +{ + WASMDebugWatchPoint *watchpoint, *next; + + watchpoint = bh_list_first_elem(watchpoints); + while (watchpoint) { + next = bh_list_elem_next(watchpoint); + + bh_list_remove(watchpoints, watchpoint); + wasm_runtime_free(watchpoint); + + watchpoint = next; + } +} + +void +wasm_debug_instance_destroy(WASMCluster *cluster) +{ + WASMDebugInstance *instance = NULL; + + if (!g_debug_engine) { + return; + } + + instance = cluster->debug_inst; + if (instance) { + /* destroy control thread */ + wasm_debug_control_thread_destroy(instance); + + os_mutex_lock(&g_debug_engine->instance_list_lock); + bh_list_remove(&g_debug_engine->debug_instance_list, instance); + os_mutex_unlock(&g_debug_engine->instance_list_lock); + + /* destroy all breakpoints */ + wasm_debug_instance_destroy_breakpoints(instance); + wasm_debug_instance_destroy_watchpoints( + instance, &instance->watch_point_list_read); + wasm_debug_instance_destroy_watchpoints( + instance, &instance->watch_point_list_write); + + os_mutex_destroy(&instance->wait_lock); + os_cond_destroy(&instance->wait_cond); + + wasm_runtime_free(instance); + cluster->debug_inst = NULL; + } +} + +WASMExecEnv * +wasm_debug_instance_get_current_env(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env = NULL; + + if (instance) { + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + while (exec_env) { + if (exec_env->handle == instance->current_tid) + break; + exec_env = bh_list_elem_next(exec_env); + } + } + return exec_env; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +bool +wasm_debug_instance_get_current_object_name(WASMDebugInstance *instance, + char name_buffer[], uint32 len) +{ + WASMExecEnv *exec_env; + WASIArguments *wasi_args; + WASMModuleInstance *module_inst; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + wasi_args = &module_inst->module->wasi_args; + if (wasi_args && wasi_args->argc > 0) { + char *argv_name = wasi_args->argv[0]; + uint32 name_len = (uint32)strlen(argv_name); + + printf("the module name is %s\n", argv_name); + if (len - 1 >= name_len) + bh_strcpy_s(name_buffer, len, argv_name); + else + bh_strcpy_s(name_buffer, len, argv_name + (name_len + 1 - len)); + return true; + } + return false; +} +#endif + +uint64 +wasm_debug_instance_get_pid(WASMDebugInstance *instance) +{ + if (instance != NULL) { + return (uint64)instance->id; + } + return (uint64)0; +} + +korp_tid +wasm_debug_instance_get_tid(WASMDebugInstance *instance) +{ + if (instance != NULL) { + return instance->current_tid; + } + return (korp_tid)(uintptr_t)0; +} + +uint32 +wasm_debug_instance_get_tids(WASMDebugInstance *instance, korp_tid tids[], + uint32 len) +{ + WASMExecEnv *exec_env; + uint32 i = 0, threads_num = 0; + + if (!instance) + return 0; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + while (exec_env && i < len) { + /* Some threads may not be ready */ + if (exec_env->handle != 0) { + tids[i++] = exec_env->handle; + threads_num++; + } + exec_env = bh_list_elem_next(exec_env); + } + LOG_VERBOSE("find %d tids\n", threads_num); + return threads_num; +} + +uint32 +wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, korp_tid tid) +{ + WASMExecEnv *exec_env = NULL; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + while (exec_env) { + if (exec_env->handle == tid) { + return (uint32)exec_env->current_status->signal_flag; + } + exec_env = bh_list_elem_next(exec_env); + } + + return 0; +} + +void +wasm_debug_instance_set_cur_thread(WASMDebugInstance *instance, korp_tid tid) +{ + instance->current_tid = tid; +} + +uint64 +wasm_debug_instance_get_pc(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return 0; + + exec_env = wasm_debug_instance_get_current_env(instance); + if ((exec_env != NULL) && (exec_env->cur_frame != NULL) + && (exec_env->cur_frame->ip != NULL)) { + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + return WASM_ADDR( + WasmObj, instance->id, + (exec_env->cur_frame->ip - module_inst->module->load_addr)); + } + return 0; +} + +uint64 +wasm_debug_instance_get_load_addr(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return WASM_ADDR(WasmInvalid, 0, 0); + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (exec_env) { + return WASM_ADDR(WasmObj, instance->id, 0); + } + + return WASM_ADDR(WasmInvalid, 0, 0); +} + +WASMDebugMemoryInfo * +wasm_debug_instance_get_memregion(WASMDebugInstance *instance, uint64 addr) +{ + WASMDebugMemoryInfo *mem_info; + WASMExecEnv *exec_env; + WASMModuleInstance *module_inst; + WASMMemoryInstance *memory; + uint32 num_bytes_per_page; + uint32 linear_mem_size = 0; + + if (!instance) + return NULL; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return NULL; + + if (!(mem_info = wasm_runtime_malloc(sizeof(WASMDebugMemoryInfo)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory"); + return NULL; + } + memset(mem_info, 0, sizeof(WASMDebugMemoryInfo)); + mem_info->start = WASM_ADDR(WasmInvalid, 0, 0); + mem_info->size = 0; + mem_info->name[0] = '\0'; + mem_info->permisson[0] = '\0'; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + + switch (WASM_ADDR_TYPE(addr)) { + case WasmObj: + if (WASM_ADDR_OFFSET(addr) < module_inst->module->load_size) { + mem_info->start = WASM_ADDR(WasmObj, instance->id, 0); + mem_info->size = module_inst->module->load_size; + snprintf(mem_info->name, sizeof(mem_info->name), "%s", + "module"); + snprintf(mem_info->permisson, sizeof(mem_info->permisson), "%s", + "rx"); + } + break; + case WasmMemory: + { + memory = wasm_get_default_memory(module_inst); + + if (memory) { + num_bytes_per_page = memory->num_bytes_per_page; + linear_mem_size = num_bytes_per_page * memory->cur_page_count; + } + if (WASM_ADDR_OFFSET(addr) < linear_mem_size) { + mem_info->start = WASM_ADDR(WasmMemory, instance->id, 0); + mem_info->size = linear_mem_size; + snprintf(mem_info->name, sizeof(mem_info->name), "%s", + "memory"); + snprintf(mem_info->permisson, sizeof(mem_info->permisson), "%s", + "rw"); + } + break; + } + default: + mem_info->start = WASM_ADDR(WasmInvalid, 0, 0); + mem_info->size = 0; + } + return mem_info; +} + +void +wasm_debug_instance_destroy_memregion(WASMDebugInstance *instance, + WASMDebugMemoryInfo *mem_info) +{ + wasm_runtime_free(mem_info); +} + +bool +wasm_debug_instance_get_obj_mem(WASMDebugInstance *instance, uint64 offset, + char *buf, uint64 *size) +{ + WASMExecEnv *exec_env; + WASMModuleInstance *module_inst; + WASMDebugBreakPoint *breakpoint; + WASMFastOPCodeNode *fast_opcode; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + + if (offset + *size > module_inst->module->load_size) { + LOG_VERBOSE("wasm_debug_instance_get_data_mem size overflow!\n"); + *size = module_inst->module->load_size >= offset + ? module_inst->module->load_size - offset + : 0; + } + + bh_memcpy_s(buf, (uint32)*size, module_inst->module->load_addr + offset, + (uint32)*size); + + breakpoint = bh_list_first_elem(&instance->break_point_list); + while (breakpoint) { + if (offset <= breakpoint->addr && breakpoint->addr < offset + *size) { + bh_memcpy_s(buf + (breakpoint->addr - offset), sizeof(break_instr), + &breakpoint->orignal_data, sizeof(break_instr)); + } + breakpoint = bh_list_elem_next(breakpoint); + } + + fast_opcode = bh_list_first_elem(&module_inst->module->fast_opcode_list); + while (fast_opcode) { + if (offset <= fast_opcode->offset + && fast_opcode->offset < offset + *size) { + *(uint8 *)(buf + (fast_opcode->offset - offset)) = + fast_opcode->orig_op; + } + fast_opcode = bh_list_elem_next(fast_opcode); + } + + return true; +} + +bool +wasm_debug_instance_get_linear_mem(WASMDebugInstance *instance, uint64 offset, + char *buf, uint64 *size) +{ + WASMExecEnv *exec_env; + WASMModuleInstance *module_inst; + WASMMemoryInstance *memory; + uint32 num_bytes_per_page; + uint32 linear_mem_size; + + if (!instance) + return false; + + exec_env = wasm_debug_instance_get_current_env(instance); + if (!exec_env) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + memory = wasm_get_default_memory(module_inst); + if (memory) { + num_bytes_per_page = memory->num_bytes_per_page; + linear_mem_size = num_bytes_per_page * memory->cur_page_count; + if (offset + *size > linear_mem_size) { + LOG_VERBOSE("wasm_debug_instance_get_linear_mem size overflow!\n"); + *size = linear_mem_size >= offset ? linear_mem_size - offset : 0; + } + bh_memcpy_s(buf, (uint32)*size, memory->memory_data + offset, + (uint32)*size); + return true; + } + return false; +} + +bool +wasm_debug_instance_set_linear_mem(WASMDebugInstance *instance, uint64 offset, + char *buf, uint64 *size) +{ + WASMExecEnv *exec_env; + WASMModuleInstance *module_inst; + WASMMemoryInstance *memory; + uint32 num_bytes_per_page; + uint32 linear_mem_size; + + if (!instance) + return false; + + exec_env = wasm_debug_instance_get_current_env(instance); + if (!exec_env) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + memory = wasm_get_default_memory(module_inst); + if (memory) { + num_bytes_per_page = memory->num_bytes_per_page; + linear_mem_size = num_bytes_per_page * memory->cur_page_count; + if (offset + *size > linear_mem_size) { + LOG_VERBOSE("wasm_debug_instance_get_linear_mem size overflow!\n"); + *size = linear_mem_size >= offset ? linear_mem_size - offset : 0; + } + bh_memcpy_s(memory->memory_data + offset, (uint32)*size, buf, + (uint32)*size); + return true; + } + return false; +} + +bool +wasm_debug_instance_get_mem(WASMDebugInstance *instance, uint64 addr, char *buf, + uint64 *size) +{ + switch (WASM_ADDR_TYPE(addr)) { + case WasmMemory: + return wasm_debug_instance_get_linear_mem( + instance, WASM_ADDR_OFFSET(addr), buf, size); + break; + case WasmObj: + return wasm_debug_instance_get_obj_mem( + instance, WASM_ADDR_OFFSET(addr), buf, size); + break; + default: + return false; + } +} + +bool +wasm_debug_instance_set_mem(WASMDebugInstance *instance, uint64 addr, char *buf, + uint64 *size) +{ + switch (WASM_ADDR_TYPE(addr)) { + case WasmMemory: + return wasm_debug_instance_set_linear_mem( + instance, WASM_ADDR_OFFSET(addr), buf, size); + break; + case WasmObj: + default: + return false; + } +} + +WASMDebugInstance * +wasm_exec_env_get_instance(WASMExecEnv *exec_env) +{ + WASMDebugInstance *instance = NULL; + + if (!g_debug_engine) { + return NULL; + } + + os_mutex_lock(&g_debug_engine->instance_list_lock); + instance = bh_list_first_elem(&g_debug_engine->debug_instance_list); + while (instance) { + if (instance->cluster == exec_env->cluster) + break; + instance = bh_list_elem_next(instance); + } + + os_mutex_unlock(&g_debug_engine->instance_list_lock); + return instance; +} + +uint32 +wasm_debug_instance_get_call_stack_pcs(WASMDebugInstance *instance, + korp_tid tid, uint64 buf[], uint64 size) +{ + WASMExecEnv *exec_env; + struct WASMInterpFrame *frame; + uint32 i = 0; + + if (!instance) + return 0; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + while (exec_env) { + if (exec_env->handle == tid) { + WASMModuleInstance *module_inst = + (WASMModuleInstance *)exec_env->module_inst; + frame = exec_env->cur_frame; + while (frame && i < size) { + if (frame->ip != NULL) { + buf[i++] = + WASM_ADDR(WasmObj, instance->id, + (frame->ip - module_inst->module->load_addr)); + } + frame = frame->prev_frame; + } + return i; + } + exec_env = bh_list_elem_next(exec_env); + } + return 0; +} + +bool +wasm_debug_instance_add_breakpoint(WASMDebugInstance *instance, uint64 addr, + uint64 length) +{ + WASMExecEnv *exec_env; + WASMModuleInstance *module_inst; + uint64 offset; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + if (WASM_ADDR_TYPE(addr) != WasmObj) + return false; + + offset = WASM_ADDR_OFFSET(addr); + + if (length >= sizeof(break_instr)) { + if (offset + sizeof(break_instr) <= module_inst->module->load_size) { + WASMDebugBreakPoint *breakpoint; + if (!(breakpoint = + wasm_runtime_malloc(sizeof(WASMDebugBreakPoint)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory"); + return false; + } + memset(breakpoint, 0, sizeof(WASMDebugBreakPoint)); + breakpoint->addr = offset; + /* TODO: how to if more than one breakpoints are set + at the same addr? */ + bh_memcpy_s(&breakpoint->orignal_data, (uint32)sizeof(break_instr), + module_inst->module->load_addr + offset, + (uint32)sizeof(break_instr)); + + bh_memcpy_s(module_inst->module->load_addr + offset, + (uint32)sizeof(break_instr), break_instr, + (uint32)sizeof(break_instr)); + + bh_list_insert(&instance->break_point_list, breakpoint); + return true; + } + } + return false; +} + +bool +wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr, + uint64 length) +{ + WASMExecEnv *exec_env; + WASMModuleInstance *module_inst; + uint64 offset; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + + if (WASM_ADDR_TYPE(addr) != WasmObj) + return false; + offset = WASM_ADDR_OFFSET(addr); + + if (length >= sizeof(break_instr)) { + if (offset + sizeof(break_instr) <= module_inst->module->load_size) { + WASMDebugBreakPoint *breakpoint = + bh_list_first_elem(&instance->break_point_list); + while (breakpoint) { + WASMDebugBreakPoint *next_break = bh_list_elem_next(breakpoint); + if (breakpoint->addr == offset) { + /* TODO: how to if more than one breakpoints are set + at the same addr? */ + bh_memcpy_s(module_inst->module->load_addr + offset, + (uint32)sizeof(break_instr), + &breakpoint->orignal_data, + (uint32)sizeof(break_instr)); + bh_list_remove(&instance->break_point_list, breakpoint); + wasm_runtime_free(breakpoint); + } + breakpoint = next_break; + } + } + } + return true; +} + +static bool +add_watchpoint(bh_list *list, uint64 addr, uint64 length) +{ + WASMDebugWatchPoint *watchpoint; + if (!(watchpoint = wasm_runtime_malloc(sizeof(WASMDebugWatchPoint)))) { + LOG_ERROR("WASM Debug Engine error: failed to allocate memory for " + "watchpoint"); + return false; + } + memset(watchpoint, 0, sizeof(WASMDebugWatchPoint)); + watchpoint->addr = addr; + watchpoint->length = length; + bh_list_insert(list, watchpoint); + return true; +} + +static bool +remove_watchpoint(bh_list *list, uint64 addr, uint64 length) +{ + WASMDebugWatchPoint *watchpoint = bh_list_first_elem(list); + while (watchpoint) { + WASMDebugWatchPoint *next = bh_list_elem_next(watchpoint); + if (watchpoint->addr == addr && watchpoint->length == length) { + bh_list_remove(list, watchpoint); + wasm_runtime_free(watchpoint); + } + watchpoint = next; + } + return true; +} + +bool +wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return add_watchpoint(&instance->watch_point_list_write, addr, length); +} + +bool +wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return remove_watchpoint(&instance->watch_point_list_write, addr, length); +} + +bool +wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return add_watchpoint(&instance->watch_point_list_read, addr, length); +} + +bool +wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length) +{ + return remove_watchpoint(&instance->watch_point_list_read, addr, length); +} + +bool +wasm_debug_instance_on_failure(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + os_mutex_lock(&instance->wait_lock); + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) { + os_mutex_unlock(&instance->wait_lock); + return false; + } + + if (instance->stopped_thread == NULL + && instance->current_state == DBG_LAUNCHING) { + /* if fail in start stage: may need wait for main thread to notify it */ + os_cond_wait(&instance->wait_cond, &instance->wait_lock); + } + instance->current_state = DBG_ERROR; + instance->stopped_thread = NULL; + + /* terminate the wasm execution thread */ + while (exec_env) { + /* Resume all threads so they can receive the TERM signal */ + os_mutex_lock(&exec_env->wait_lock); + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM); + exec_env->current_status->running_status = STATUS_RUNNING; + os_cond_signal(&exec_env->wait_cond); + os_mutex_unlock(&exec_env->wait_lock); + exec_env = bh_list_elem_next(exec_env); + } + os_mutex_unlock(&instance->wait_lock); + + return true; +} + +bool +wasm_debug_instance_continue(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + if (instance->current_state == APP_RUNNING) { + LOG_VERBOSE("Already in running state, ignore continue request"); + return false; + } + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + while (exec_env) { + wasm_cluster_thread_continue(exec_env); + exec_env = bh_list_elem_next(exec_env); + } + + instance->current_state = APP_RUNNING; + + return true; +} + +bool +wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + while (exec_env) { + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP); + exec_env = bh_list_elem_next(exec_env); + } + return true; +} + +bool +wasm_debug_instance_detach(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + wasm_gdbserver_detach(instance->control_thread->server); + + while (exec_env) { + if (instance->current_state == APP_STOPPED) { + /* Resume all threads since remote debugger detached*/ + wasm_cluster_thread_continue(exec_env); + } + exec_env = bh_list_elem_next(exec_env); + } + + /* relaunch, accept new debug connection */ + instance->current_state = DBG_LAUNCHING; + instance->control_thread->status = DETACHED; + instance->stopped_thread = NULL; + + return true; +} + +bool +wasm_debug_instance_kill(WASMDebugInstance *instance) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + while (exec_env) { + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM); + if (instance->current_state == APP_STOPPED) { + /* Resume all threads so they can receive the TERM signal */ + os_mutex_lock(&exec_env->wait_lock); + exec_env->current_status->running_status = STATUS_RUNNING; + os_cond_signal(&exec_env->wait_cond); + os_mutex_unlock(&exec_env->wait_lock); + } + exec_env = bh_list_elem_next(exec_env); + } + + instance->current_state = APP_RUNNING; + return true; +} + +bool +wasm_debug_instance_singlestep(WASMDebugInstance *instance, korp_tid tid) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + if (instance->current_state == APP_RUNNING) { + LOG_VERBOSE("Already in running state, ignore step request"); + return false; + } + + exec_env = bh_list_first_elem(&instance->cluster->exec_env_list); + if (!exec_env) + return false; + + while (exec_env) { + if (exec_env->handle == tid || tid == (korp_tid)(uintptr_t)~0LL) { + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_SINGSTEP); + wasm_cluster_thread_step(exec_env); + } + exec_env = bh_list_elem_next(exec_env); + } + + instance->current_state = APP_RUNNING; + + return true; +} + +bool +wasm_debug_instance_get_local(WASMDebugInstance *instance, int32 frame_index, + int32 local_index, char buf[], int32 *size) +{ + WASMExecEnv *exec_env; + struct WASMInterpFrame *frame; + WASMFunctionInstance *cur_func; + uint8 local_type = 0xFF; + uint32 local_offset; + int32 param_count; + int32 fi = 0; + + if (!instance) + return false; + + exec_env = wasm_debug_instance_get_current_env(instance); + if (!exec_env) + return false; + + frame = exec_env->cur_frame; + while (frame && fi++ != frame_index) { + frame = frame->prev_frame; + } + + if (!frame) + return false; + cur_func = frame->function; + if (!cur_func) + return false; + + param_count = cur_func->param_count; + + if (local_index >= param_count + cur_func->local_count) + return false; + + local_offset = cur_func->local_offsets[local_index]; + if (local_index < param_count) + local_type = cur_func->param_types[local_index]; + else if (local_index < cur_func->local_count + param_count) + local_type = cur_func->local_types[local_index - param_count]; + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + *size = 4; + bh_memcpy_s(buf, 4, (char *)(frame->lp + local_offset), 4); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + *size = 8; + bh_memcpy_s(buf, 8, (char *)(frame->lp + local_offset), 8); + break; + default: + *size = 0; + break; + } + return true; +} + +bool +wasm_debug_instance_get_global(WASMDebugInstance *instance, int32 frame_index, + int32 global_index, char buf[], int32 *size) +{ + WASMExecEnv *exec_env; + struct WASMInterpFrame *frame; + WASMModuleInstance *module_inst; + WASMGlobalInstance *globals, *global; + uint8 *global_addr; + uint8 global_type = 0xFF; + uint8 *global_data; + int32 fi = 0; + + if (!instance) + return false; + + exec_env = wasm_debug_instance_get_current_env(instance); + if (!exec_env) + return false; + + frame = exec_env->cur_frame; + while (frame && fi++ != frame_index) { + frame = frame->prev_frame; + } + + if (!frame) + return false; + + module_inst = (WASMModuleInstance *)exec_env->module_inst; + global_data = module_inst->global_data; + globals = module_inst->e->globals; + + if ((global_index < 0) + || ((uint32)global_index >= module_inst->e->global_count)) { + return false; + } + global = globals + global_index; + +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + global_type = global->type; + + switch (global_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + *size = 4; + bh_memcpy_s(buf, 4, (char *)(global_addr), 4); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + *size = 8; + bh_memcpy_s(buf, 8, (char *)(global_addr), 8); + break; + default: + *size = 0; + break; + } + return true; +} + +uint64 +wasm_debug_instance_mmap(WASMDebugInstance *instance, uint32 size, + int32 map_prot) +{ + WASMExecEnv *exec_env; + uint32 offset = 0; + (void)map_prot; + + if (!instance) + return 0; + + exec_env = wasm_debug_instance_get_current_env(instance); + if (!exec_env) + return 0; + + if (instance->exec_mem_info.start_offset == 0) { + return 0; + } + + if (instance->exec_mem_info.current_pos + - instance->exec_mem_info.start_offset + size + <= (uint64)instance->exec_mem_info.size) { + offset = instance->exec_mem_info.current_pos; + instance->exec_mem_info.current_pos += size; + } + + if (offset == 0) { + LOG_WARNING("the memory may be not enough for debug, try use larger " + "--heap-size"); + return 0; + } + + return WASM_ADDR(WasmMemory, 0, offset); +} + +bool +wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr) +{ + WASMExecEnv *exec_env; + + if (!instance) + return false; + + exec_env = wasm_debug_instance_get_current_env(instance); + if (!exec_env) + return false; + + if (instance->exec_mem_info.start_offset == 0) { + return false; + } + + (void)addr; + + /* Currently we don't support to free the execution memory, simply return + * true here */ + return true; +} diff --git a/core/iwasm/libraries/debug-engine/debug_engine.cmake b/core/iwasm/libraries/debug-engine/debug_engine.cmake new file mode 100644 index 0000000000..914ddd6396 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/debug_engine.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2021 Ant Group. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (DEBUG_ENGINE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_DEBUG_INTERP=1) + +include_directories(${DEBUG_ENGINE_DIR}) + +file (GLOB source_all ${DEBUG_ENGINE_DIR}/*.c) + +set (DEBUG_ENGINE_SOURCE ${source_all}) diff --git a/core/iwasm/libraries/debug-engine/debug_engine.h b/core/iwasm/libraries/debug-engine/debug_engine.h new file mode 100644 index 0000000000..275eeaad13 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/debug_engine.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _DEBUG_ENGINE_H +#define _DEBUG_ENGINE_H + +#include "bh_list.h" +#include "gdbserver.h" +#include "thread_manager.h" + +typedef enum WASMDebugControlThreadStatus { + RUNNING, + DETACHED, + STOPPED, +} WASMDebugControlThreadStatus; + +struct WASMDebugEngine; +struct WASMDebugInstance; + +typedef struct WASMDebugControlThread { + WASMGDBServer *server; + korp_tid tid; + korp_mutex wait_lock; + char ip_addr[128]; + int port; + WASMDebugControlThreadStatus status; + struct WASMDebugEngine *debug_engine; + struct WASMDebugInstance *debug_instance; +} WASMDebugControlThread; + +typedef struct WASMDebugBreakPoint { + struct WASMDebugBreakPoint *next; + uint64 addr; + uint64 orignal_data; +} WASMDebugBreakPoint; + +typedef struct WASMDebugWatchPoint { + bh_list_link next; + uint64 addr; + uint64 length; +} WASMDebugWatchPoint; + +typedef enum debug_state_t { + /* Debugger state conversion sequence: + * DBG_LAUNCHING ---> APP_STOPPED <---> APP_RUNNING + */ + DBG_LAUNCHING, + APP_RUNNING, + APP_STOPPED, + DBG_ERROR +} debug_state_t; + +typedef struct WASMDebugExecutionMemory { + uint64 start_offset; + uint64 current_pos; + uint32 size; +} WASMDebugExecutionMemory; + +struct WASMDebugInstance { + struct WASMDebugInstance *next; + WASMDebugControlThread *control_thread; + bh_list break_point_list; + bh_list watch_point_list_read; + bh_list watch_point_list_write; + WASMCluster *cluster; + uint32 id; + korp_tid current_tid; + korp_mutex wait_lock; + korp_cond wait_cond; + /* Last stopped thread, it should be set to NULL when sending + * out the thread stop reply */ + WASMExecEnv *volatile stopped_thread; + /* Currently status of the debug instance, it will be set to + * RUNNING when receiving STEP/CONTINUE commands, and set to + * STOPPED when any thread stopped */ + volatile debug_state_t current_state; + /* Execution memory info. During debugging, the debug client may request to + * malloc a memory space to evaluate user expressions. We preserve a buffer + * during creating debug instance, and use a simple bump pointer allocator + * to serve lldb's memory request */ + WASMDebugExecutionMemory exec_mem_info; +}; + +typedef enum WASMDebugEventKind { + BREAK_POINT_ADD, + BREAK_POINT_REMOVE +} WASMDebugEventKind; + +typedef struct WASMDebugEvent { + WASMDebugEventKind kind; + unsigned char metadata[0]; +} WASMDebugEvent; + +typedef struct WASMDebugMemoryInfo { + uint64 start; + uint64 size; + char name[128]; + char permisson[4]; +} WASMDebugMemoryInfo; + +typedef enum WasmAddressType { + WasmMemory = 0x00, + WasmObj = 0x01, + WasmInvalid = 0x03 +} WasmAddressType; + +#define WASM_ADDR(type, id, offset) \ + (((uint64)type << 62) | ((uint64)0 << 32) | ((uint64)offset << 0)) + +#define WASM_ADDR_TYPE(addr) (((addr)&0xC000000000000000) >> 62) +#define WASM_ADDR_OFFSET(addr) (((addr)&0x00000000FFFFFFFF)) + +#define INVALIED_ADDR (0xFFFFFFFFFFFFFFFF) + +void +on_thread_stop_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env); + +void +on_thread_exit_event(WASMDebugInstance *debug_inst, WASMExecEnv *exec_env); + +WASMDebugInstance * +wasm_debug_instance_create(WASMCluster *cluster, int32 port); + +void +wasm_debug_instance_destroy(WASMCluster *cluster); + +WASMDebugInstance * +wasm_exec_env_get_instance(WASMExecEnv *exec_env); + +bool +wasm_debug_engine_init(char *ip_addr, int32 process_port); + +void +wasm_debug_engine_destroy(void); + +WASMExecEnv * +wasm_debug_instance_get_current_env(WASMDebugInstance *instance); + +uint64 +wasm_debug_instance_get_pid(WASMDebugInstance *instance); + +korp_tid +wasm_debug_instance_get_tid(WASMDebugInstance *instance); + +uint32 +wasm_debug_instance_get_tids(WASMDebugInstance *instance, korp_tid tids[], + uint32 len); + +void +wasm_debug_instance_set_cur_thread(WASMDebugInstance *instance, korp_tid tid); + +uint64 +wasm_debug_instance_get_pc(WASMDebugInstance *instance); + +uint64 +wasm_debug_instance_get_load_addr(WASMDebugInstance *instance); + +WASMDebugMemoryInfo * +wasm_debug_instance_get_memregion(WASMDebugInstance *instance, uint64 addr); + +void +wasm_debug_instance_destroy_memregion(WASMDebugInstance *instance, + WASMDebugMemoryInfo *mem_info); + +bool +wasm_debug_instance_get_obj_mem(WASMDebugInstance *instance, uint64 addr, + char *buf, uint64 *size); + +bool +wasm_debug_instance_get_linear_mem(WASMDebugInstance *instance, uint64 addr, + char *buf, uint64 *size); + +bool +wasm_debug_instance_get_mem(WASMDebugInstance *instance, uint64 addr, char *buf, + uint64 *size); + +bool +wasm_debug_instance_set_mem(WASMDebugInstance *instance, uint64 addr, char *buf, + uint64 *size); + +uint32 +wasm_debug_instance_get_call_stack_pcs(WASMDebugInstance *instance, + korp_tid tid, uint64 buf[], uint64 size); + +bool +wasm_debug_instance_add_breakpoint(WASMDebugInstance *instance, uint64 addr, + uint64 length); + +bool +wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance, uint64 addr, + uint64 length); + +bool +wasm_debug_instance_watchpoint_write_add(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_watchpoint_write_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_watchpoint_read_add(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_watchpoint_read_remove(WASMDebugInstance *instance, + uint64 addr, uint64 length); + +bool +wasm_debug_instance_on_failure(WASMDebugInstance *instance); + +bool +wasm_debug_instance_interrupt_all_threads(WASMDebugInstance *instance); + +bool +wasm_debug_instance_continue(WASMDebugInstance *instance); + +bool +wasm_debug_instance_detach(WASMDebugInstance *instance); + +bool +wasm_debug_instance_kill(WASMDebugInstance *instance); + +uint32 +wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, + korp_tid tid); + +bool +wasm_debug_instance_singlestep(WASMDebugInstance *instance, korp_tid tid); + +bool +wasm_debug_instance_get_local(WASMDebugInstance *instance, int32 frame_index, + int32 local_index, char buf[], int32 *size); + +bool +wasm_debug_instance_get_global(WASMDebugInstance *instance, int32 frame_index, + int32 global_index, char buf[], int32 *size); + +#if WASM_ENABLE_LIBC_WASI != 0 +bool +wasm_debug_instance_get_current_object_name(WASMDebugInstance *instance, + char name_buffer[], uint32 len); +#endif + +uint64 +wasm_debug_instance_mmap(WASMDebugInstance *instance, uint32 size, + int32 map_prot); + +bool +wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr); +#endif diff --git a/core/iwasm/libraries/debug-engine/gdbserver.c b/core/iwasm/libraries/debug-engine/gdbserver.c new file mode 100644 index 0000000000..2cf1e686a3 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/gdbserver.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "gdbserver.h" +#include "handler.h" +#include "packets.h" +#include "utils.h" + +typedef void (*PacketHandler)(WASMGDBServer *server, char *payload); + +struct packet_handler_elem { + char request; + PacketHandler handler; +}; + +#define DEL_HANDLER(r, h) [r] = { .request = r, .handler = h } + +static const struct packet_handler_elem packet_handler_table[255] = { + DEL_HANDLER('Q', handle_general_set), + DEL_HANDLER('q', handle_general_query), + DEL_HANDLER('v', handle_v_packet), + DEL_HANDLER('?', handle_threadstop_request), + DEL_HANDLER('H', handle_set_current_thread), + DEL_HANDLER('p', handle_get_register), + DEL_HANDLER('j', handle_get_json_request), + DEL_HANDLER('m', handle_get_read_memory), + DEL_HANDLER('M', handle_get_write_memory), + DEL_HANDLER('x', handle_get_read_binary_memory), + DEL_HANDLER('Z', handle_add_break), + DEL_HANDLER('z', handle_remove_break), + DEL_HANDLER('c', handle_continue_request), + DEL_HANDLER('k', handle_kill_request), + DEL_HANDLER('_', handle____request), + DEL_HANDLER('D', handle_detach_request), +}; + +WASMGDBServer * +wasm_create_gdbserver(const char *host, int *port) +{ + bh_socket_t listen_fd = (bh_socket_t)-1; + WASMGDBServer *server; + + bh_assert(port); + + if (!(server = wasm_runtime_malloc(sizeof(WASMGDBServer)))) { + LOG_ERROR("wasm gdb server error: failed to allocate memory"); + return NULL; + } + + memset(server, 0, sizeof(WASMGDBServer)); + + if (!(server->receive_ctx = + wasm_runtime_malloc(sizeof(rsp_recv_context_t)))) { + LOG_ERROR("wasm gdb server error: failed to allocate memory"); + goto fail; + } + + memset(server->receive_ctx, 0, sizeof(rsp_recv_context_t)); + + if (0 != os_socket_create(&listen_fd, true, true)) { + LOG_ERROR("wasm gdb server error: create socket failed"); + goto fail; + } + + if (0 != os_socket_bind(listen_fd, host, port)) { + LOG_ERROR("wasm gdb server error: socket bind failed"); + goto fail; + } + + LOG_WARNING("Debug server listening on %s:%" PRIu32 "\n", host, *port); + server->listen_fd = listen_fd; + + return server; + +fail: + if (listen_fd >= 0) { + os_socket_shutdown(listen_fd); + os_socket_close(listen_fd); + } + if (server->receive_ctx) + wasm_runtime_free(server->receive_ctx); + if (server) + wasm_runtime_free(server); + return NULL; +} + +bool +wasm_gdbserver_listen(WASMGDBServer *server) +{ + int32 ret; + + ret = os_socket_listen(server->listen_fd, 1); + if (ret != 0) { + LOG_ERROR("wasm gdb server error: socket listen failed"); + goto fail; + } + + LOG_VERBOSE("listen for gdb client"); + return true; + +fail: + os_socket_shutdown(server->listen_fd); + os_socket_close(server->listen_fd); + return false; +} + +bool +wasm_gdbserver_accept(WASMGDBServer *server) +{ + + bh_socket_t sockt_fd = (bh_socket_t)-1; + + LOG_VERBOSE("waiting for gdb client to connect..."); + + os_socket_accept(server->listen_fd, &sockt_fd, NULL, NULL); + if (sockt_fd < 0) { + LOG_ERROR("wasm gdb server error: socket accept failed"); + goto fail; + } + + LOG_VERBOSE("accept gdb client"); + server->socket_fd = sockt_fd; + server->noack = false; + return true; + +fail: + os_socket_shutdown(server->listen_fd); + os_socket_close(server->listen_fd); + return false; +} + +void +wasm_gdbserver_detach(WASMGDBServer *server) +{ + if (server->socket_fd > 0) { + os_socket_shutdown(server->socket_fd); + os_socket_close(server->socket_fd); + } +} + +void +wasm_close_gdbserver(WASMGDBServer *server) +{ + if (server->receive_ctx) { + wasm_runtime_free(server->receive_ctx); + } + if (server->socket_fd > 0) { + os_socket_shutdown(server->socket_fd); + os_socket_close(server->socket_fd); + } + if (server->listen_fd > 0) { + os_socket_shutdown(server->listen_fd); + os_socket_close(server->listen_fd); + } +} + +static inline void +handle_packet(WASMGDBServer *server, char request, char *payload) +{ + if (packet_handler_table[(int)request].handler != NULL) + packet_handler_table[(int)request].handler(server, payload); +} + +static void +process_packet(WASMGDBServer *server) +{ + uint8 *inbuf = (uint8 *)server->receive_ctx->receive_buffer; + char request; + char *payload = NULL; + + request = inbuf[0]; + + if (request == '\0') { + LOG_VERBOSE("ignore empty request"); + return; + } + + payload = (char *)&inbuf[1]; + + LOG_VERBOSE("receive request:%c %s\n", request, payload); + handle_packet(server, request, payload); +} + +static inline void +push_byte(rsp_recv_context_t *ctx, unsigned char ch, bool checksum) +{ + if (ctx->receive_index >= sizeof(ctx->receive_buffer)) { + LOG_ERROR("RSP message buffer overflow"); + bh_assert(false); + return; + } + + ctx->receive_buffer[ctx->receive_index++] = ch; + + if (checksum) { + ctx->check_sum += ch; + } +} + +/** + * The packet layout is: + * 1. Normal packet: + * '$' + payload + '#' + checksum(2bytes) + * ^ + * packetend + * 2. Interrupt: + * 0x03 + */ + +/* return: + * 0: incomplete message received + * 1: complete message received + * 2: interrupt message received + */ +static int +on_rsp_byte_arrive(unsigned char ch, rsp_recv_context_t *ctx) +{ + if (ctx->phase == Phase_Idle) { + ctx->receive_index = 0; + ctx->check_sum = 0; + + if (ch == 0x03) { + LOG_VERBOSE("Receive interrupt package"); + return 2; + } + else if (ch == '$') { + ctx->phase = Phase_Payload; + } + + return 0; + } + else if (ctx->phase == Phase_Payload) { + if (ch == '#') { + ctx->phase = Phase_Checksum; + push_byte(ctx, ch, false); + } + else { + push_byte(ctx, ch, true); + } + + return 0; + } + else if (ctx->phase == Phase_Checksum) { + ctx->size_in_phase++; + push_byte(ctx, ch, false); + + if (ctx->size_in_phase == 2) { + ctx->size_in_phase = 0; + + bh_assert(ctx->receive_index >= 3); + + if ((hex(ctx->receive_buffer[ctx->receive_index - 2]) << 4 + | hex(ctx->receive_buffer[ctx->receive_index - 1])) + != ctx->check_sum) { + LOG_WARNING("RSP package checksum error, ignore it"); + ctx->phase = Phase_Idle; + return 0; + } + else { + /* Change # to \0 */ + ctx->receive_buffer[ctx->receive_index - 3] = '\0'; + ctx->phase = Phase_Idle; + return 1; + } + } + + return 0; + } + + /* Should never reach here */ + bh_assert(false); + return 0; +} + +bool +wasm_gdbserver_handle_packet(WASMGDBServer *server) +{ + int32 n; + char buf[1024]; + + if (os_socket_settimeout(server->socket_fd, 1000) != 0) { + LOG_ERROR("Set socket recv timeout failed"); + return false; + } + + n = os_socket_recv(server->socket_fd, buf, sizeof(buf)); + + if (n == 0) { + handle_detach_request(server, NULL); + LOG_VERBOSE("Debugger disconnected, waiting for debugger reconnection"); + return true; + } + else if (n < 0) { +#if defined(BH_PLATFORM_WINDOWS) + if (WSAGetLastError() == WSAETIMEDOUT) +#else + if (errno == EAGAIN || errno == EWOULDBLOCK) +#endif + { + /* No bytes arrived */ + return true; + } + else { + LOG_ERROR("Socket receive error"); + return false; + } + } + else { + int32 i, ret; + + for (i = 0; i < n; i++) { + ret = on_rsp_byte_arrive(buf[i], server->receive_ctx); + + if (ret == 1) { + if (!server->noack) + write_data_raw(server, (uint8 *)"+", 1); + + process_packet(server); + } + else if (ret == 2) { + handle_interrupt(server); + } + } + } + + return true; +} diff --git a/core/iwasm/libraries/debug-engine/gdbserver.h b/core/iwasm/libraries/debug-engine/gdbserver.h new file mode 100644 index 0000000000..41ed94dec1 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/gdbserver.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GDB_SERVER_H +#define _GDB_SERVER_H + +#include "bh_platform.h" + +#define PACKET_BUF_SIZE 0x8000 + +enum GDBStoppointType { + eStoppointInvalid = -1, + eBreakpointSoftware = 0, + eBreakpointHardware, + eWatchpointWrite, + eWatchpointRead, + eWatchpointReadWrite +}; + +typedef enum rsp_recv_phase_t { + Phase_Idle, + Phase_Payload, + Phase_Checksum +} rsp_recv_phase_t; + +/* Remote Serial Protocol Receive Context */ +typedef struct rsp_recv_context_t { + rsp_recv_phase_t phase; + uint16 receive_index; + uint16 size_in_phase; + uint8 check_sum; + /* RSP packet should not be too long */ + char receive_buffer[1024]; +} rsp_recv_context_t; + +typedef struct WasmDebugPacket { + unsigned char buf[PACKET_BUF_SIZE]; + uint32 size; +} WasmDebugPacket; + +struct WASMDebugControlThread; +typedef struct WASMGDBServer { + bh_socket_t listen_fd; + bh_socket_t socket_fd; + WasmDebugPacket pkt; + bool noack; + struct WASMDebugControlThread *thread; + rsp_recv_context_t *receive_ctx; +} WASMGDBServer; + +WASMGDBServer * +wasm_create_gdbserver(const char *host, int *port); + +bool +wasm_gdbserver_listen(WASMGDBServer *server); + +bool +wasm_gdbserver_accept(WASMGDBServer *server); + +void +wasm_gdbserver_detach(WASMGDBServer *server); + +void +wasm_close_gdbserver(WASMGDBServer *server); + +bool +wasm_gdbserver_handle_packet(WASMGDBServer *server); +#endif diff --git a/core/iwasm/libraries/debug-engine/handler.c b/core/iwasm/libraries/debug-engine/handler.c new file mode 100644 index 0000000000..14c7fae6eb --- /dev/null +++ b/core/iwasm/libraries/debug-engine/handler.c @@ -0,0 +1,932 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "handler.h" +#include "debug_engine.h" +#include "packets.h" +#include "utils.h" +#include "wasm_runtime.h" + +/* + * Note: A moderate MAX_PACKET_SIZE is ok because + * LLDB queries our buffer size (via qSupported PacketSize) + * and limits packet sizes accordingly. + */ + +#if defined(DEBUG_MAX_PACKET_SIZE) +#define MAX_PACKET_SIZE DEBUG_MAX_PACKET_SIZE +#else +#define MAX_PACKET_SIZE (4096) +#endif + +/* + * Note: It's assumed that MAX_PACKET_SIZE is reasonably large. + * See GetWorkingDir, WasmCallStack, etc. + */ +#if MAX_PACKET_SIZE < PATH_MAX || MAX_PACKET_SIZE < (2048 + 1) +#error MAX_PACKET_SIZE is too small +#endif + +static char *tmpbuf; +static korp_mutex tmpbuf_lock; + +int +wasm_debug_handler_init(void) +{ + int ret; + tmpbuf = wasm_runtime_malloc(MAX_PACKET_SIZE); + if (tmpbuf == NULL) { + LOG_ERROR("debug-engine: Packet buffer allocation failure"); + return BHT_ERROR; + } + ret = os_mutex_init(&tmpbuf_lock); + if (ret != BHT_OK) { + wasm_runtime_free(tmpbuf); + tmpbuf = NULL; + } + return ret; +} + +void +wasm_debug_handler_deinit(void) +{ + wasm_runtime_free(tmpbuf); + tmpbuf = NULL; + os_mutex_destroy(&tmpbuf_lock); +} + +void +handle_interrupt(WASMGDBServer *server) +{ + wasm_debug_instance_interrupt_all_threads(server->thread->debug_instance); +} + +void +handle_general_set(WASMGDBServer *server, char *payload) +{ + const char *name; + char *args; + + args = strchr(payload, ':'); + if (args) + *args++ = '\0'; + + name = payload; + LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload); + + if (!strcmp(name, "StartNoAckMode")) { + server->noack = true; + write_packet(server, "OK"); + } + if (!strcmp(name, "ThreadSuffixSupported")) { + write_packet(server, ""); + } + if (!strcmp(name, "ListThreadsInStopReply")) { + write_packet(server, ""); + } + if (!strcmp(name, "EnableErrorStrings")) { + write_packet(server, "OK"); + } +} + +static void +process_xfer(WASMGDBServer *server, const char *name, char *args) +{ + const char *mode = args; + + args = strchr(args, ':'); + if (args) + *args++ = '\0'; + + if (!strcmp(name, "libraries") && !strcmp(mode, "read")) { + // TODO: how to get current wasm file name? + uint64 addr = wasm_debug_instance_get_load_addr( + (WASMDebugInstance *)server->thread->debug_instance); + os_mutex_lock(&tmpbuf_lock); +#if WASM_ENABLE_LIBC_WASI != 0 + char objname[128]; + if (!wasm_debug_instance_get_current_object_name( + (WASMDebugInstance *)server->thread->debug_instance, objname, + 128)) { + objname[0] = 0; /* use an empty string */ + } + snprintf(tmpbuf, MAX_PACKET_SIZE, + "l
", + objname, addr); +#else + snprintf(tmpbuf, MAX_PACKET_SIZE, + "l
", + "nobody.wasm", addr); +#endif + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } +} + +void +process_wasm_local(WASMGDBServer *server, char *args) +{ + int32 frame_index; + int32 local_index; + char buf[16]; + int32 size = 16; + bool ret; + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "E01"); + if (sscanf(args, "%" PRId32 ";%" PRId32, &frame_index, &local_index) == 2) { + ret = wasm_debug_instance_get_local( + (WASMDebugInstance *)server->thread->debug_instance, frame_index, + local_index, buf, &size); + if (ret && size > 0) { + mem2hex(buf, tmpbuf, size); + } + } + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +void +process_wasm_global(WASMGDBServer *server, char *args) +{ + int32 frame_index; + int32 global_index; + char buf[16]; + int32 size = 16; + bool ret; + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "E01"); + if (sscanf(args, "%" PRId32 ";%" PRId32, &frame_index, &global_index) + == 2) { + ret = wasm_debug_instance_get_global( + (WASMDebugInstance *)server->thread->debug_instance, frame_index, + global_index, buf, &size); + if (ret && size > 0) { + mem2hex(buf, tmpbuf, size); + } + } + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +/* TODO: let server send an empty/error reply. + Original issue: 4265 + Not tested yet, but it should work. + */ +static void +send_reply(WASMGDBServer *server, const char *err) +{ + if (!err || !*err) + write_packet(server, ""); + else + write_packet(server, err); +} + +void +handle_general_query(WASMGDBServer *server, char *payload) +{ + const char *name; + char *args; + char triple[256]; + + args = strchr(payload, ':'); + if (args) + *args++ = '\0'; + name = payload; + LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload); + + if (!strcmp(name, "C")) { + uint64 pid, tid; + pid = wasm_debug_instance_get_pid( + (WASMDebugInstance *)server->thread->debug_instance); + tid = (uint64)(uintptr_t)wasm_debug_instance_get_tid( + (WASMDebugInstance *)server->thread->debug_instance); + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "QCp%" PRIx64 ".%" PRIx64 "", pid, + tid); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + if (!strcmp(name, "Supported")) { + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, + "qXfer:libraries:read+;PacketSize=%x;", MAX_PACKET_SIZE); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + + if (!strcmp(name, "Xfer")) { + name = args; + + if (!args) { + LOG_ERROR("payload parse error during handle_general_query"); + send_reply(server, ""); + return; + } + + args = strchr(args, ':'); + + if (args) { + *args++ = '\0'; + process_xfer(server, name, args); + } + } + + if (!strcmp(name, "HostInfo")) { + mem2hex("wasm32-wamr-wasi-wasm", triple, + strlen("wasm32-wamr-wasi-wasm")); + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, + "vendor:wamr;ostype:wasi;arch:wasm32;" + "triple:%s;endian:little;ptrsize:4;", + triple); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + if (!strcmp(name, "ModuleInfo")) { + write_packet(server, ""); + } + if (!strcmp(name, "GetWorkingDir")) { + os_mutex_lock(&tmpbuf_lock); + if (getcwd(tmpbuf, PATH_MAX)) + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + if (!strcmp(name, "QueryGDBServer")) { + write_packet(server, ""); + } + if (!strcmp(name, "VAttachOrWaitSupported")) { + write_packet(server, ""); + } + if (!strcmp(name, "ProcessInfo")) { + // Todo: process id parent-pid + uint64 pid; + pid = wasm_debug_instance_get_pid( + (WASMDebugInstance *)server->thread->debug_instance); + mem2hex("wasm32-wamr-wasi-wasm", triple, + strlen("wasm32-wamr-wasi-wasm")); + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, + "pid:%" PRIx64 ";parent-pid:%" PRIx64 + ";vendor:wamr;ostype:wasi;arch:wasm32;" + "triple:%s;endian:little;ptrsize:4;", + pid, pid, triple); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + if (!strcmp(name, "RegisterInfo0")) { + os_mutex_lock(&tmpbuf_lock); + snprintf( + tmpbuf, MAX_PACKET_SIZE, + "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;" + "set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;"); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + else if (!strncmp(name, "RegisterInfo", strlen("RegisterInfo"))) { + write_packet(server, "E45"); + } + if (!strcmp(name, "StructuredDataPlugins")) { + write_packet(server, ""); + } + + if (args && (!strcmp(name, "MemoryRegionInfo"))) { + uint64 addr = strtoll(args, NULL, 16); + WASMDebugMemoryInfo *mem_info = wasm_debug_instance_get_memregion( + (WASMDebugInstance *)server->thread->debug_instance, addr); + if (mem_info) { + char name_buf[256]; + mem2hex(mem_info->name, name_buf, strlen(mem_info->name)); + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, + "start:%" PRIx64 ";size:%" PRIx64 + ";permissions:%s;name:%s;", + (uint64)mem_info->start, mem_info->size, + mem_info->permisson, name_buf); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + + wasm_debug_instance_destroy_memregion( + (WASMDebugInstance *)server->thread->debug_instance, mem_info); + } + } + + if (!strcmp(name, "WasmData")) { + write_packet(server, ""); + } + + if (!strcmp(name, "WasmMem")) { + write_packet(server, ""); + } + + if (!strcmp(name, "Symbol")) { + write_packet(server, ""); + } + + if (args && (!strcmp(name, "WasmCallStack"))) { + uint64 tid = strtoll(args, NULL, 16); + uint64 buf[1024 / sizeof(uint64)]; + uint32 count = wasm_debug_instance_get_call_stack_pcs( + (WASMDebugInstance *)server->thread->debug_instance, + (korp_tid)(uintptr_t)tid, buf, 1024 / sizeof(uint64)); + + if (count > 0) { + os_mutex_lock(&tmpbuf_lock); + mem2hex((char *)buf, tmpbuf, count * sizeof(uint64)); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } + else + write_packet(server, ""); + } + + if (args && (!strcmp(name, "WasmLocal"))) { + process_wasm_local(server, args); + } + + if (args && (!strcmp(name, "WasmGlobal"))) { + process_wasm_global(server, args); + } + + if (!strcmp(name, "Offsets")) { + write_packet(server, ""); + } + + if (!strncmp(name, "ThreadStopInfo", strlen("ThreadStopInfo"))) { + int32 prefix_len = strlen("ThreadStopInfo"); + uint64 tid_number = strtoll(name + prefix_len, NULL, 16); + korp_tid tid = (korp_tid)(uintptr_t)tid_number; + uint32 status; + + status = wasm_debug_instance_get_thread_status( + server->thread->debug_instance, tid); + + send_thread_stop_status(server, status, tid); + } + + if (!strcmp(name, "WatchpointSupportInfo")) { + os_mutex_lock(&tmpbuf_lock); + // Any uint32 is OK for the watchpoint support + snprintf(tmpbuf, MAX_PACKET_SIZE, "num:32;"); + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + } +} + +void +send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid) +{ + int32 len = 0; + uint64 pc; + korp_tid tids[20]; + char pc_string[17]; + uint32 tids_count, i = 0; + uint32 gdb_status = status; + WASMExecEnv *exec_env; + const char *exception; + + if (status == 0) { + os_mutex_lock(&tmpbuf_lock); + (void)snprintf(tmpbuf, MAX_PACKET_SIZE, "W%02" PRIx32, status); + send_reply(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); + return; + } + tids_count = wasm_debug_instance_get_tids( + (WASMDebugInstance *)server->thread->debug_instance, tids, 20); + pc = wasm_debug_instance_get_pc( + (WASMDebugInstance *)server->thread->debug_instance); + + if (status == WAMR_SIG_SINGSTEP) { + gdb_status = WAMR_SIG_TRAP; + } + + os_mutex_lock(&tmpbuf_lock); + // TODO: how name a wasm thread? + len = snprintf(tmpbuf, MAX_PACKET_SIZE, + "T%02" PRIx32 "thread:%" PRIx64 ";name:%s;", gdb_status, + (uint64)(uintptr_t)tid, "nobody"); + if (len < 0 || len >= MAX_PACKET_SIZE) { + send_reply(server, "E01"); + os_mutex_unlock(&tmpbuf_lock); + return; + } + + if (tids_count > 0) { + int n = snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, "threads:"); + if (n < 0 || n >= MAX_PACKET_SIZE - len) { + send_reply(server, "E01"); + os_mutex_unlock(&tmpbuf_lock); + return; + } + + len += n; + while (i < tids_count) { + if (i == tids_count - 1) { + n = snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, + "%" PRIx64 ";", (uint64)(uintptr_t)tids[i]); + } + else { + n = snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, + "%" PRIx64 ",", (uint64)(uintptr_t)tids[i]); + } + + if (n < 0 || n >= MAX_PACKET_SIZE - len) { + send_reply(server, "E01"); + os_mutex_unlock(&tmpbuf_lock); + return; + } + + len += n; + i++; + } + } + mem2hex((void *)&pc, pc_string, 8); + pc_string[8 * 2] = '\0'; + + exec_env = wasm_debug_instance_get_current_env( + (WASMDebugInstance *)server->thread->debug_instance); + bh_assert(exec_env); + + exception = + wasm_runtime_get_exception(wasm_runtime_get_module_inst(exec_env)); + if (exception) { + /* When exception occurs, use reason:exception so the description can be + * correctly processed by LLDB */ + uint32 exception_len = strlen(exception); + int n = + snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, + "thread-pcs:%" PRIx64 ";00:%s;reason:%s;description:", pc, + pc_string, "exception"); + if (n < 0 || n >= MAX_PACKET_SIZE - len) { + send_reply(server, "E01"); + os_mutex_unlock(&tmpbuf_lock); + return; + } + + len += n; + /* The description should be encoded as HEX */ + for (i = 0; i < exception_len; i++) { + n = snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, "%02x", + exception[i]); + if (n < 0 || n >= MAX_PACKET_SIZE - len) { + send_reply(server, "E01"); + os_mutex_unlock(&tmpbuf_lock); + return; + } + + len += n; + } + + (void)snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, ";"); + } + else { + if (status == WAMR_SIG_TRAP) { + (void)snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, + "thread-pcs:%" PRIx64 ";00:%s;reason:%s;", pc, + pc_string, "breakpoint"); + } + else if (status == WAMR_SIG_SINGSTEP) { + (void)snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, + "thread-pcs:%" PRIx64 ";00:%s;reason:%s;", pc, + pc_string, "trace"); + } + else { /* status > 0 (== 0 is checked at the function beginning) */ + (void)snprintf(tmpbuf + len, MAX_PACKET_SIZE - len, + "thread-pcs:%" PRIx64 ";00:%s;reason:%s;", pc, + pc_string, "signal"); + } + } + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +void +handle_v_packet(WASMGDBServer *server, char *payload) +{ + const char *name; + char *args; + + args = strchr(payload, ';'); + if (args) + *args++ = '\0'; + name = payload; + LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload); + + if (!strcmp("Cont?", name)) + write_packet(server, "vCont;c;C;s;S;"); + + if (!strcmp("Cont", name)) { + if (args) { + if (args[0] == 's' || args[0] == 'c') { + char *numstring = strchr(args, ':'); + if (numstring) { + uint64 tid_number; + korp_tid tid; + + *numstring++ = '\0'; + tid_number = strtoll(numstring, NULL, 16); + tid = (korp_tid)(uintptr_t)tid_number; + wasm_debug_instance_set_cur_thread( + (WASMDebugInstance *)server->thread->debug_instance, + tid); + + if (args[0] == 's') { + wasm_debug_instance_singlestep( + (WASMDebugInstance *)server->thread->debug_instance, + tid); + } + else { + wasm_debug_instance_continue( + (WASMDebugInstance *) + server->thread->debug_instance); + } + } + } + } + } +} + +void +handle_threadstop_request(WASMGDBServer *server, char *payload) +{ + korp_tid tid; + uint32 status; + WASMDebugInstance *debug_inst = + (WASMDebugInstance *)server->thread->debug_instance; + bh_assert(debug_inst); + + /* According to + https://sourceware.org/gdb/onlinedocs/gdb/Packets.html#Packets, the "?" + package should be sent when connection is first established to query the + reason the target halted */ + bh_assert(debug_inst->current_state == DBG_LAUNCHING); + + /* Waiting for the stop event */ + os_mutex_lock(&debug_inst->wait_lock); + while (!debug_inst->stopped_thread) { + os_cond_wait(&debug_inst->wait_cond, &debug_inst->wait_lock); + } + os_mutex_unlock(&debug_inst->wait_lock); + + tid = debug_inst->stopped_thread->handle; + status = (uint32)debug_inst->stopped_thread->current_status->signal_flag; + + wasm_debug_instance_set_cur_thread(debug_inst, tid); + + send_thread_stop_status(server, status, tid); + + debug_inst->current_state = APP_STOPPED; + debug_inst->stopped_thread = NULL; +} + +void +handle_set_current_thread(WASMGDBServer *server, char *payload) +{ + LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload); + if ('g' == *payload++) { + uint64 tid = strtoll(payload, NULL, 16); + if (tid > 0) + wasm_debug_instance_set_cur_thread( + (WASMDebugInstance *)server->thread->debug_instance, + (korp_tid)(uintptr_t)tid); + } + write_packet(server, "OK"); +} + +void +handle_get_register(WASMGDBServer *server, char *payload) +{ + uint64 regdata; + int32 i = strtol(payload, NULL, 16); + + if (i != 0) { + send_reply(server, "E01"); + return; + } + regdata = wasm_debug_instance_get_pc( + (WASMDebugInstance *)server->thread->debug_instance); + + os_mutex_lock(&tmpbuf_lock); + mem2hex((void *)®data, tmpbuf, 8); + tmpbuf[8 * 2] = '\0'; + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +void +handle_get_json_request(WASMGDBServer *server, char *payload) +{ + char *args; + + args = strchr(payload, ':'); + if (args) + *args++ = '\0'; + write_packet(server, ""); +} + +void +handle_get_read_binary_memory(WASMGDBServer *server, char *payload) +{ + write_packet(server, ""); +} + +void +handle_get_read_memory(WASMGDBServer *server, char *payload) +{ + uint64 maddr, mlen; + bool ret; + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "%s", ""); + if (sscanf(payload, "%" SCNx64 ",%" SCNx64, &maddr, &mlen) == 2) { + char *buff; + + if (mlen * 2 > MAX_PACKET_SIZE) { + LOG_ERROR("Buffer overflow!"); + mlen = MAX_PACKET_SIZE / 2; + } + + buff = wasm_runtime_malloc(mlen); + if (buff) { + ret = wasm_debug_instance_get_mem( + (WASMDebugInstance *)server->thread->debug_instance, maddr, + buff, &mlen); + if (ret) { + mem2hex(buff, tmpbuf, mlen); + } + wasm_runtime_free(buff); + } + } + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +void +handle_get_write_memory(WASMGDBServer *server, char *payload) +{ + size_t hex_len; + int offset; + int32 act_len; + uint64 maddr, mlen; + char *buff; + bool ret; + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "%s", ""); + if (sscanf(payload, "%" SCNx64 ",%" SCNx64 ":%n", &maddr, &mlen, &offset) + == 2) { + payload += offset; + hex_len = strlen(payload); + act_len = hex_len / 2 < mlen ? hex_len / 2 : mlen; + + buff = wasm_runtime_malloc(act_len); + if (buff) { + hex2mem(payload, buff, act_len); + ret = wasm_debug_instance_set_mem( + (WASMDebugInstance *)server->thread->debug_instance, maddr, + buff, &mlen); + if (ret) { + snprintf(tmpbuf, MAX_PACKET_SIZE, "%s", "OK"); + } + wasm_runtime_free(buff); + } + } + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +void +handle_breakpoint_software_add(WASMGDBServer *server, uint64 addr, + size_t length) +{ + bool ret = wasm_debug_instance_add_breakpoint( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_breakpoint_software_remove(WASMGDBServer *server, uint64 addr, + size_t length) +{ + bool ret = wasm_debug_instance_remove_breakpoint( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_write_add(WASMGDBServer *server, uint64 addr, size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_write_add( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_write_remove(WASMGDBServer *server, uint64 addr, + size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_write_remove( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_read_add(WASMGDBServer *server, uint64 addr, size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_read_add( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_watchpoint_read_remove(WASMGDBServer *server, uint64 addr, size_t length) +{ + bool ret = wasm_debug_instance_watchpoint_read_remove( + (WASMDebugInstance *)server->thread->debug_instance, addr, length); + write_packet(server, ret ? "OK" : "EO1"); +} + +void +handle_add_break(WASMGDBServer *server, char *payload) +{ + int arg_c; + size_t type, length; + uint64 addr; + + if ((arg_c = sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length)) + != 3) { + LOG_ERROR("Unsupported number of add break arguments %d", arg_c); + send_reply(server, ""); + return; + } + + switch (type) { + case eBreakpointSoftware: + handle_breakpoint_software_add(server, addr, length); + break; + case eWatchpointWrite: + handle_watchpoint_write_add(server, addr, length); + break; + case eWatchpointRead: + handle_watchpoint_read_add(server, addr, length); + break; + case eWatchpointReadWrite: + handle_watchpoint_write_add(server, addr, length); + handle_watchpoint_read_add(server, addr, length); + break; + default: + LOG_ERROR("Unsupported breakpoint type %zu", type); + write_packet(server, ""); + break; + } +} + +void +handle_remove_break(WASMGDBServer *server, char *payload) +{ + int arg_c; + size_t type, length; + uint64 addr; + + if ((arg_c = sscanf(payload, "%zx,%" SCNx64 ",%zx", &type, &addr, &length)) + != 3) { + LOG_ERROR("Unsupported number of remove break arguments %d", arg_c); + send_reply(server, ""); + return; + } + + switch (type) { + case eBreakpointSoftware: + handle_breakpoint_software_remove(server, addr, length); + break; + case eWatchpointWrite: + handle_watchpoint_write_remove(server, addr, length); + break; + case eWatchpointRead: + handle_watchpoint_read_remove(server, addr, length); + break; + case eWatchpointReadWrite: + handle_watchpoint_write_remove(server, addr, length); + handle_watchpoint_read_remove(server, addr, length); + break; + default: + LOG_ERROR("Unsupported breakpoint type %zu", type); + write_packet(server, ""); + break; + } +} + +void +handle_continue_request(WASMGDBServer *server, char *payload) +{ + wasm_debug_instance_continue( + (WASMDebugInstance *)server->thread->debug_instance); +} + +void +handle_kill_request(WASMGDBServer *server, char *payload) +{ + wasm_debug_instance_kill( + (WASMDebugInstance *)server->thread->debug_instance); +} + +static void +handle_malloc(WASMGDBServer *server, char *payload) +{ + char *args; + uint64 addr, size; + int32 map_prot = MMAP_PROT_NONE; + + args = strstr(payload, ","); + if (args) { + *args++ = '\0'; + } + else { + LOG_ERROR("Payload parse error during handle malloc"); + send_reply(server, ""); + return; + } + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "%s", "E03"); + + size = strtoll(payload, NULL, 16); + if (size > 0) { + while (*args) { + if (*args == 'r') { + map_prot |= MMAP_PROT_READ; + } + if (*args == 'w') { + map_prot |= MMAP_PROT_WRITE; + } + if (*args == 'x') { + map_prot |= MMAP_PROT_EXEC; + } + args++; + } + addr = wasm_debug_instance_mmap( + (WASMDebugInstance *)server->thread->debug_instance, size, + map_prot); + if (addr) { + snprintf(tmpbuf, MAX_PACKET_SIZE, "%" PRIx64, addr); + } + } + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +static void +handle_free(WASMGDBServer *server, char *payload) +{ + uint64 addr; + bool ret; + + os_mutex_lock(&tmpbuf_lock); + snprintf(tmpbuf, MAX_PACKET_SIZE, "%s", "E03"); + addr = strtoll(payload, NULL, 16); + + ret = wasm_debug_instance_ummap( + (WASMDebugInstance *)server->thread->debug_instance, addr); + if (ret) { + snprintf(tmpbuf, MAX_PACKET_SIZE, "%s", "OK"); + } + + write_packet(server, tmpbuf); + os_mutex_unlock(&tmpbuf_lock); +} + +void +handle____request(WASMGDBServer *server, char *payload) +{ + char *args; + + if (payload[0] == 'M') { + args = payload + 1; + handle_malloc(server, args); + } + if (payload[0] == 'm') { + args = payload + 1; + handle_free(server, args); + } +} + +void +handle_detach_request(WASMGDBServer *server, char *payload) +{ + if (payload != NULL) { + write_packet(server, "OK"); + } + wasm_debug_instance_detach( + (WASMDebugInstance *)server->thread->debug_instance); +} diff --git a/core/iwasm/libraries/debug-engine/handler.h b/core/iwasm/libraries/debug-engine/handler.h new file mode 100644 index 0000000000..698663c7b2 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/handler.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef HANDLER_H +#define HANDLER_H + +#include "gdbserver.h" + +int +wasm_debug_handler_init(void); + +void +wasm_debug_handler_deinit(void); + +void +handle_interrupt(WASMGDBServer *server); + +void +handle_general_set(WASMGDBServer *server, char *payload); + +void +handle_general_query(WASMGDBServer *server, char *payload); + +void +handle_v_packet(WASMGDBServer *server, char *payload); + +void +handle_threadstop_request(WASMGDBServer *server, char *payload); + +void +handle_set_current_thread(WASMGDBServer *server, char *payload); + +void +handle_get_register(WASMGDBServer *server, char *payload); + +void +handle_get_json_request(WASMGDBServer *server, char *payload); + +void +handle_get_read_binary_memory(WASMGDBServer *server, char *payload); + +void +handle_get_read_memory(WASMGDBServer *server, char *payload); + +void +handle_get_write_memory(WASMGDBServer *server, char *payload); + +void +handle_add_break(WASMGDBServer *server, char *payload); + +void +handle_remove_break(WASMGDBServer *server, char *payload); + +void +handle_continue_request(WASMGDBServer *server, char *payload); + +void +handle_kill_request(WASMGDBServer *server, char *payload); + +void +handle____request(WASMGDBServer *server, char *payload); + +void +handle_detach_request(WASMGDBServer *server, char *payload); + +void +send_thread_stop_status(WASMGDBServer *server, uint32 status, korp_tid tid); +#endif diff --git a/core/iwasm/libraries/debug-engine/packets.c b/core/iwasm/libraries/debug-engine/packets.c new file mode 100644 index 0000000000..1bdb3d2ce8 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/packets.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "packets.h" +#include "gdbserver.h" + +void +write_data_raw(WASMGDBServer *gdbserver, const uint8 *data, ssize_t len) +{ + ssize_t nwritten; + + nwritten = os_socket_send(gdbserver->socket_fd, data, len); + if (nwritten < 0) { + LOG_ERROR("Write error\n"); + exit(-2); + } +} + +void +write_hex(WASMGDBServer *gdbserver, unsigned long hex) +{ + char buf[32]; + size_t len; + + len = snprintf(buf, sizeof(buf) - 1, "%02lx", hex); + write_data_raw(gdbserver, (uint8 *)buf, len); +} + +void +write_packet_bytes(WASMGDBServer *gdbserver, const uint8 *data, + size_t num_bytes) +{ + uint8 checksum; + size_t i; + + write_data_raw(gdbserver, (uint8 *)"$", 1); + for (i = 0, checksum = 0; i < num_bytes; ++i) + checksum += data[i]; + write_data_raw(gdbserver, (uint8 *)data, num_bytes); + write_data_raw(gdbserver, (uint8 *)"#", 1); + write_hex(gdbserver, checksum); +} + +void +write_packet(WASMGDBServer *gdbserver, const char *data) +{ + LOG_VERBOSE("send replay:%s", data); + write_packet_bytes(gdbserver, (const uint8 *)data, strlen(data)); +} + +void +write_binary_packet(WASMGDBServer *gdbserver, const char *pfx, + const uint8 *data, ssize_t num_bytes) +{ + uint8 *buf; + ssize_t pfx_num_chars = strlen(pfx); + ssize_t buf_num_bytes = 0, total_size; + int32 i; + + total_size = 2 * num_bytes + pfx_num_chars; + buf = wasm_runtime_malloc(total_size); + if (!buf) { + LOG_ERROR("Failed to allocate memory for binary packet"); + return; + } + + memset(buf, 0, total_size); + memcpy(buf, pfx, pfx_num_chars); + buf_num_bytes += pfx_num_chars; + + for (i = 0; i < num_bytes; ++i) { + uint8 b = data[i]; + switch (b) { + case '#': + case '$': + case '}': + case '*': + buf[buf_num_bytes++] = '}'; + buf[buf_num_bytes++] = b ^ 0x20; + break; + default: + buf[buf_num_bytes++] = b; + break; + } + } + write_packet_bytes(gdbserver, buf, buf_num_bytes); + wasm_runtime_free(buf); +} diff --git a/core/iwasm/libraries/debug-engine/packets.h b/core/iwasm/libraries/debug-engine/packets.h new file mode 100644 index 0000000000..b35889394f --- /dev/null +++ b/core/iwasm/libraries/debug-engine/packets.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef PACKETS_H +#define PACKETS_H + +#include "gdbserver.h" + +void +write_data_raw(WASMGDBServer *gdbserver, const uint8 *data, ssize_t len); + +void +write_packet(WASMGDBServer *gdbserver, const char *data); + +#endif diff --git a/core/iwasm/libraries/debug-engine/utils.c b/core/iwasm/libraries/debug-engine/utils.c new file mode 100644 index 0000000000..4d9299c1b9 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/utils.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "utils.h" + +static const char hexchars[] = "0123456789abcdef"; + +int32 +hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +char * +mem2hex(char *mem, char *buf, int32 count) +{ + uint8 ch; + + for (int i = 0; i < count; i++) { + ch = *(mem++); + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + return (buf); +} + +char * +hex2mem(char *buf, char *mem, int32 count) +{ + uint8 ch; + + for (int i = 0; i < count; i++) { + ch = hex(*buf++) << 4; + ch = ch + hex(*buf++); + *(mem++) = ch; + } + return (mem); +} diff --git a/core/iwasm/libraries/debug-engine/utils.h b/core/iwasm/libraries/debug-engine/utils.h new file mode 100644 index 0000000000..1c75808596 --- /dev/null +++ b/core/iwasm/libraries/debug-engine/utils.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2021 Ant Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef UTILS_H +#define UTILS_H + +#include "bh_platform.h" + +int32 +hex(char ch); + +char * +mem2hex(char *mem, char *buf, int32 count); + +char * +hex2mem(char *buf, char *mem, int32 count); + +int32 +unescape(char *msg, int32 len); + +#endif /* UTILS_H */ diff --git a/core/iwasm/libraries/lib-pthread/SConscript b/core/iwasm/libraries/lib-pthread/SConscript new file mode 100644 index 0000000000..1eb1cc24c1 --- /dev/null +++ b/core/iwasm/libraries/lib-pthread/SConscript @@ -0,0 +1,20 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() + +src = Split(''' +lib_pthread_wrapper.c +''') + +CPPPATH = [cwd] + + +group = DefineGroup('iwasm_lib_pthread', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread.cmake b/core/iwasm/libraries/lib-pthread/lib_pthread.cmake new file mode 100644 index 0000000000..a1d183ee1e --- /dev/null +++ b/core/iwasm/libraries/lib-pthread/lib_pthread.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_PTHREAD_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIB_PTHREAD=1) + +if (WAMR_BUILD_LIB_PTHREAD_SEMAPHORE EQUAL 1) + add_definitions (-DWASM_ENABLE_LIB_PTHREAD_SEMAPHORE=1) +endif() + +include_directories(${LIB_PTHREAD_DIR}) + +file (GLOB source_all ${LIB_PTHREAD_DIR}/*.c) + +set (LIB_PTHREAD_SOURCE ${source_all}) + diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c new file mode 100644 index 0000000000..d7dca2f1df --- /dev/null +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -0,0 +1,1361 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" +#include "../common/wasm_runtime_common.h" +#include "thread_manager.h" + +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif + +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +#define WAMR_PTHREAD_KEYS_MAX 32 + +/* clang-format off */ +#define get_module(exec_env) \ + wasm_exec_env_get_module(exec_env) + +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define get_thread_arg(exec_env) \ + wasm_exec_env_get_thread_arg(exec_env) + +#define get_wasi_ctx(module_inst) \ + wasm_runtime_get_wasi_ctx(module_inst) + +#define validate_app_addr(offset, size) \ + wasm_runtime_validate_app_addr(module_inst, offset, size) + +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + +#define addr_app_to_native(offset) \ + wasm_runtime_addr_app_to_native(module_inst, offset) + +#define addr_native_to_app(ptr) \ + wasm_runtime_addr_native_to_app(module_inst, ptr) +/* clang-format on */ + +enum { + T_THREAD, + T_MUTEX, + T_COND, + T_SEM, +}; + +enum thread_status_t { + THREAD_INIT, + THREAD_RUNNING, + THREAD_CANCELLED, + THREAD_EXIT, +}; + +enum mutex_status_t { + MUTEX_CREATED, + MUTEX_DESTROYED, +}; + +enum cond_status_t { + COND_CREATED, + COND_DESTROYED, +}; + +enum sem_status_t { + SEM_CREATED, + SEM_CLOSED, + SEM_DESTROYED, +}; + +typedef struct ThreadKeyValueNode { + bh_list_link l; + wasm_exec_env_t exec_env; + int32 thread_key_values[WAMR_PTHREAD_KEYS_MAX]; +} ThreadKeyValueNode; + +typedef struct KeyData { + int32 destructor_func; + bool is_created; +} KeyData; + +typedef struct ClusterInfoNode { + bh_list_link l; + WASMCluster *cluster; + HashMap *thread_info_map; + /* Key data list */ + KeyData key_data_list[WAMR_PTHREAD_KEYS_MAX]; + korp_mutex key_data_list_lock; + /* Every node contains the key value list for a thread */ + bh_list thread_list_head; + bh_list *thread_list; +} ClusterInfoNode; + +typedef struct ThreadInfoNode { + wasm_exec_env_t parent_exec_env; + wasm_exec_env_t exec_env; + /* the id returned to app */ + uint32 handle; + /* type can be [THREAD | MUTEX | CONDITION] */ + uint32 type; + /* Thread status, this variable should be volatile + as its value may be changed in different threads */ + volatile uint32 status; + bool joinable; + union { + korp_tid thread; + korp_mutex *mutex; + korp_cond *cond; +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 + korp_sem *sem; +#endif + /* A copy of the thread return value */ + void *ret; + } u; +} ThreadInfoNode; + +typedef struct { + ThreadInfoNode *info_node; + /* table elem index of the app's entry function */ + uint32 elem_index; + /* arg of the app's entry function */ + uint32 arg; + wasm_module_inst_t module_inst; +} ThreadRoutineArgs; + +typedef struct { + uint32 handle; + ThreadInfoNode *node; +} SemCallbackArgs; + +static bh_list cluster_info_list; +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 +static HashMap *sem_info_map; +#endif +static korp_mutex thread_global_lock; +static uint32 handle_id = 1; + +static void +lib_pthread_destroy_callback(WASMCluster *cluster); + +static uint32 +thread_handle_hash(void *handle) +{ + return (uint32)(uintptr_t)handle; +} + +static bool +thread_handle_equal(void *h1, void *h2) +{ + return (uint32)(uintptr_t)h1 == (uint32)(uintptr_t)h2 ? true : false; +} + +static void +thread_info_destroy(void *node) +{ + ThreadInfoNode *info_node = (ThreadInfoNode *)node; + + os_mutex_lock(&thread_global_lock); + if (info_node->type == T_MUTEX) { + if (info_node->status != MUTEX_DESTROYED) + os_mutex_destroy(info_node->u.mutex); + wasm_runtime_free(info_node->u.mutex); + } + else if (info_node->type == T_COND) { + if (info_node->status != COND_DESTROYED) + os_cond_destroy(info_node->u.cond); + wasm_runtime_free(info_node->u.cond); + } +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 + else if (info_node->type == T_SEM) { + if (info_node->status != SEM_DESTROYED) + os_sem_close(info_node->u.sem); + } +#endif + wasm_runtime_free(info_node); + os_mutex_unlock(&thread_global_lock); +} + +bool +lib_pthread_init() +{ + if (0 != os_mutex_init(&thread_global_lock)) + return false; + bh_list_init(&cluster_info_list); + if (!wasm_cluster_register_destroy_callback(lib_pthread_destroy_callback)) { + os_mutex_destroy(&thread_global_lock); + return false; + } +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 + if (!(sem_info_map = bh_hash_map_create( + 32, true, (HashFunc)wasm_string_hash, + (KeyEqualFunc)wasm_string_equal, NULL, thread_info_destroy))) { + os_mutex_destroy(&thread_global_lock); + return false; + } +#endif + return true; +} + +void +lib_pthread_destroy() +{ +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 + bh_hash_map_destroy(sem_info_map); +#endif + os_mutex_destroy(&thread_global_lock); +} + +static ClusterInfoNode * +get_cluster_info(WASMCluster *cluster) +{ + ClusterInfoNode *node; + + os_mutex_lock(&thread_global_lock); + node = bh_list_first_elem(&cluster_info_list); + + while (node) { + if (cluster == node->cluster) { + os_mutex_unlock(&thread_global_lock); + return node; + } + node = bh_list_elem_next(node); + } + os_mutex_unlock(&thread_global_lock); + + return NULL; +} + +static KeyData * +key_data_list_lookup(wasm_exec_env_t exec_env, int32 key) +{ + ClusterInfoNode *node; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + + if ((node = get_cluster_info(cluster))) { + return (key >= 0 && key < WAMR_PTHREAD_KEYS_MAX + && node->key_data_list[key].is_created) + ? &(node->key_data_list[key]) + : NULL; + } + + return NULL; +} + +/** + * Lookup the thread key value node for a thread, create a new one if failed + * This design will reduce the memory usage. If the thread doesn't use the + * local storage, it will not occupy memory space. + */ +static int32 * +key_value_list_lookup_or_create(wasm_exec_env_t exec_env, ClusterInfoNode *info, + int32 key) +{ + KeyData *key_node; + ThreadKeyValueNode *data; + + /* Check if the key is valid */ + key_node = key_data_list_lookup(exec_env, key); + if (!key_node) { + return NULL; + } + + /* Find key values node */ + data = bh_list_first_elem(info->thread_list); + while (data) { + if (data->exec_env == exec_env) + return data->thread_key_values; + data = bh_list_elem_next(data); + } + + /* If not found, create a new node for this thread */ + if (!(data = wasm_runtime_malloc(sizeof(ThreadKeyValueNode)))) + return NULL; + memset(data, 0, sizeof(ThreadKeyValueNode)); + data->exec_env = exec_env; + + if (bh_list_insert(info->thread_list, data) != 0) { + wasm_runtime_free(data); + return NULL; + } + + return data->thread_key_values; +} + +static void +call_key_destructor(wasm_exec_env_t exec_env) +{ + int32 i; + uint32 destructor_index; + KeyData *key_node; + ThreadKeyValueNode *value_node; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + if (!info) { + return; + } + + value_node = bh_list_first_elem(info->thread_list); + while (value_node) { + if (value_node->exec_env == exec_env) + break; + value_node = bh_list_elem_next(value_node); + } + + /* This thread hasn't created key value node */ + if (!value_node) + return; + + /* Destroy key values */ + for (i = 0; i < WAMR_PTHREAD_KEYS_MAX; i++) { + if (value_node->thread_key_values[i] != 0) { + int32 value = value_node->thread_key_values[i]; + os_mutex_lock(&info->key_data_list_lock); + + if ((key_node = key_data_list_lookup(exec_env, i))) + destructor_index = key_node->destructor_func; + else + destructor_index = 0; + os_mutex_unlock(&info->key_data_list_lock); + + /* reset key value */ + value_node->thread_key_values[i] = 0; + + /* Call the destructor func provided by app */ + if (destructor_index) { + uint32 argv[1]; + + argv[0] = value; + wasm_runtime_call_indirect(exec_env, destructor_index, 1, argv); + } + } + } + + bh_list_remove(info->thread_list, value_node); + wasm_runtime_free(value_node); +} + +static void +destroy_thread_key_value_list(bh_list *list) +{ + ThreadKeyValueNode *node, *next; + + /* There should be only one node for main thread */ + bh_assert(list->len <= 1); + + if (list->len) { + node = bh_list_first_elem(list); + while (node) { + next = bh_list_elem_next(node); + call_key_destructor(node->exec_env); + node = next; + } + } +} + +static ClusterInfoNode * +create_cluster_info(WASMCluster *cluster) +{ + ClusterInfoNode *node; + bh_list_status ret; + + if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) { + return NULL; + } + memset(node, 0, sizeof(ClusterInfoNode)); + + node->thread_list = &node->thread_list_head; + ret = bh_list_init(node->thread_list); + bh_assert(ret == BH_LIST_SUCCESS); + + if (os_mutex_init(&node->key_data_list_lock) != 0) { + wasm_runtime_free(node); + return NULL; + } + + node->cluster = cluster; + if (!(node->thread_info_map = bh_hash_map_create( + 32, true, (HashFunc)thread_handle_hash, + (KeyEqualFunc)thread_handle_equal, NULL, thread_info_destroy))) { + os_mutex_destroy(&node->key_data_list_lock); + wasm_runtime_free(node); + return NULL; + } + os_mutex_lock(&thread_global_lock); + ret = bh_list_insert(&cluster_info_list, node); + bh_assert(ret == BH_LIST_SUCCESS); + os_mutex_unlock(&thread_global_lock); + + (void)ret; + return node; +} + +static bool +destroy_cluster_info(WASMCluster *cluster) +{ + ClusterInfoNode *node = get_cluster_info(cluster); + if (node) { + bh_hash_map_destroy(node->thread_info_map); + destroy_thread_key_value_list(node->thread_list); + os_mutex_destroy(&node->key_data_list_lock); + + /* Remove from the cluster info list */ + os_mutex_lock(&thread_global_lock); + bh_list_remove(&cluster_info_list, node); + wasm_runtime_free(node); + os_mutex_unlock(&thread_global_lock); + return true; + } + return false; +} + +static void +lib_pthread_destroy_callback(WASMCluster *cluster) +{ + destroy_cluster_info(cluster); +} + +static void +delete_thread_info_node(ThreadInfoNode *thread_info) +{ + ClusterInfoNode *node; + bool ret; + WASMCluster *cluster = wasm_exec_env_get_cluster(thread_info->exec_env); + + if ((node = get_cluster_info(cluster))) { + ret = bh_hash_map_remove(node->thread_info_map, + (void *)(uintptr_t)thread_info->handle, NULL, + NULL); + (void)ret; + } + + thread_info_destroy(thread_info); +} + +static bool +append_thread_info_node(ThreadInfoNode *thread_info) +{ + ClusterInfoNode *node; + WASMCluster *cluster = wasm_exec_env_get_cluster(thread_info->exec_env); + + if (!(node = get_cluster_info(cluster))) { + if (!(node = create_cluster_info(cluster))) { + return false; + } + } + + if (!bh_hash_map_insert(node->thread_info_map, + (void *)(uintptr_t)thread_info->handle, + thread_info)) { + return false; + } + + return true; +} + +static ThreadInfoNode * +get_thread_info(wasm_exec_env_t exec_env, uint32 handle) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + if (!info || !handle) { + return NULL; + } + + return bh_hash_map_find(info->thread_info_map, (void *)(uintptr_t)handle); +} + +static uint32 +allocate_handle() +{ + uint32 id; + os_mutex_lock(&thread_global_lock); + id = handle_id++; + os_mutex_unlock(&thread_global_lock); + return id; +} + +static void * +pthread_start_routine(void *arg) +{ + wasm_exec_env_t exec_env = (wasm_exec_env_t)arg; + wasm_exec_env_t parent_exec_env; + ThreadRoutineArgs *routine_args = exec_env->thread_arg; + ThreadInfoNode *info_node = routine_args->info_node; + uint32 argv[1]; + + parent_exec_env = info_node->parent_exec_env; + os_mutex_lock(&parent_exec_env->wait_lock); + info_node->exec_env = exec_env; + info_node->u.thread = exec_env->handle; + if (!append_thread_info_node(info_node)) { + delete_thread_info_node(info_node); + os_cond_signal(&parent_exec_env->wait_cond); + os_mutex_unlock(&parent_exec_env->wait_lock); + return NULL; + } + + info_node->status = THREAD_RUNNING; + os_cond_signal(&parent_exec_env->wait_cond); + os_mutex_unlock(&parent_exec_env->wait_lock); + + wasm_exec_env_set_thread_info(exec_env); + argv[0] = routine_args->arg; + + if (!wasm_runtime_call_indirect(exec_env, routine_args->elem_index, 1, + argv)) { + /* Exception has already been spread during throwing */ + } + + /* destroy pthread key values */ + call_key_destructor(exec_env); + + wasm_runtime_free(routine_args); + + /* if the thread is joinable, store the result in its info node, + if the other threads join this thread after exited, then we + can return the stored result */ + if (!info_node->joinable) { + delete_thread_info_node(info_node); + } + else { + info_node->u.ret = (void *)(uintptr_t)argv[0]; +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) + & WASM_SUSPEND_FLAG_EXIT) + /* argv[0] isn't set after longjmp(1) to + invoke_native_with_hw_bound_check */ + info_node->u.ret = exec_env->thread_ret_value; +#endif + /* Update node status after ret value was set */ + info_node->status = THREAD_EXIT; + } + + return (void *)(uintptr_t)argv[0]; +} + +static int +pthread_create_wrapper(wasm_exec_env_t exec_env, + uint32 *thread, /* thread_handle */ + const void *attr, /* not supported */ + uint32 elem_index, /* entry function */ + uint32 arg) /* arguments buffer */ +{ + wasm_module_t module = get_module(exec_env); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_module_inst_t new_module_inst = NULL; + ThreadInfoNode *info_node = NULL; + ThreadRoutineArgs *routine_args = NULL; + uint32 thread_handle; + uint32 stack_size = 8192; + uint32 aux_stack_size; + uint64 aux_stack_start = 0; + int32 ret = -1; + struct InstantiationArgs2 args; + + bh_assert(module); + bh_assert(module_inst); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + stack_size = + ((WASMModuleInstance *)module_inst)->default_wasm_stack_size; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + stack_size = + ((AOTModuleInstance *)module_inst)->default_wasm_stack_size; + } +#endif + + wasm_runtime_instantiation_args_set_defaults(&args); + wasm_runtime_instantiation_args_set_default_stack_size(&args, stack_size); + if (!(new_module_inst = wasm_runtime_instantiate_internal( + module, module_inst, exec_env, &args, NULL, 0))) + return -1; + + /* Set custom_data to new module instance */ + wasm_runtime_set_custom_data_internal( + new_module_inst, wasm_runtime_get_custom_data(module_inst)); + + wasm_native_inherit_contexts(new_module_inst, module_inst); + + if (!(wasm_cluster_dup_c_api_imports(new_module_inst, module_inst))) + goto fail; + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + thread_handle = allocate_handle(); + info_node->parent_exec_env = exec_env; + info_node->handle = thread_handle; + info_node->type = T_THREAD; + info_node->status = THREAD_INIT; + info_node->joinable = true; + + if (!(routine_args = wasm_runtime_malloc(sizeof(ThreadRoutineArgs)))) + goto fail; + + routine_args->arg = arg; + routine_args->elem_index = elem_index; + routine_args->info_node = info_node; + routine_args->module_inst = new_module_inst; + + /* Allocate aux stack previously since exec_env->wait_lock is acquired + below, and if the stack is allocated in wasm_cluster_create_thread, + runtime may call the exported malloc function to allocate the stack, + which acquires exec_env->wait again in wasm_exec_env_set_thread_info, + and recursive lock (or hang) occurs */ + if (!wasm_cluster_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 fail; + } + + os_mutex_lock(&exec_env->wait_lock); + ret = wasm_cluster_create_thread( + exec_env, new_module_inst, true, aux_stack_start, aux_stack_size, + pthread_start_routine, (void *)routine_args); + if (ret != 0) { + os_mutex_unlock(&exec_env->wait_lock); + goto fail; + } + + /* Wait for the thread routine to assign the exec_env to + thread_info_node, otherwise the exec_env in the thread + info node may be NULL in the next pthread API call */ + os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock); + os_mutex_unlock(&exec_env->wait_lock); + + if (thread) + *thread = thread_handle; + + return 0; + +fail: + if (new_module_inst) + wasm_runtime_deinstantiate_internal(new_module_inst, true); + if (info_node) + wasm_runtime_free(info_node); + if (routine_args) + wasm_runtime_free(routine_args); + if (aux_stack_start) + wasm_cluster_free_aux_stack(exec_env, aux_stack_start); + return ret; +} + +static int32 +pthread_join_wrapper(wasm_exec_env_t exec_env, uint32 thread, + int32 retval_offset) /* void **retval */ +{ + uint32 *ret; + int32 join_ret; + void **retval; + ThreadInfoNode *node; + wasm_module_inst_t module_inst; + wasm_exec_env_t target_exec_env; + + module_inst = get_module_inst(exec_env); + + /* validate addr, we can use current thread's + module instance here as the memory is shared */ + if (!validate_app_addr((uint64)retval_offset, (uint64)sizeof(int32))) { + /* Join failed, but we don't want to terminate all threads, + do not spread exception here */ + wasm_runtime_set_exception(module_inst, NULL); + return -1; + } + + retval = (void **)addr_app_to_native((uint64)retval_offset); + + node = get_thread_info(exec_env, thread); + if (!node) { + /* The thread has exited and not joinable, return 0 to app */ + return 0; + } + + target_exec_env = node->exec_env; + bh_assert(target_exec_env); + + if (node->status != THREAD_EXIT) { + /* if the thread is still running, call the platforms join API */ + join_ret = wasm_cluster_join_thread(target_exec_env, (void **)&ret); + } + else { + /* if the thread has exited, return stored results */ + + /* this thread must be joinable, otherwise the + info_node should be destroyed once exit */ + bh_assert(node->joinable); + join_ret = 0; + ret = node->u.ret; + + /* The target thread changes the node's status before calling + wasm_cluster_exit_thread to exit, so here its resources may + haven't been destroyed yet, we wait enough time to ensure that + they are actually destroyed to avoid unexpected behavior. */ + os_mutex_lock(&exec_env->wait_lock); + os_cond_reltimedwait(&exec_env->wait_cond, &exec_env->wait_lock, 1000); + os_mutex_unlock(&exec_env->wait_lock); + } + + if (retval_offset != 0) + *(uint32 *)retval = (uint32)(uintptr_t)ret; + + return join_ret; +} + +static int32 +pthread_detach_wrapper(wasm_exec_env_t exec_env, uint32 thread) +{ + ThreadInfoNode *node; + wasm_exec_env_t target_exec_env; + + node = get_thread_info(exec_env, thread); + if (!node) + return 0; + + node->joinable = false; + + target_exec_env = node->exec_env; + bh_assert(target_exec_env != NULL); + + return wasm_cluster_detach_thread(target_exec_env); +} + +static int32 +pthread_cancel_wrapper(wasm_exec_env_t exec_env, uint32 thread) +{ + ThreadInfoNode *node; + wasm_exec_env_t target_exec_env; + + node = get_thread_info(exec_env, thread); + if (!node) + return 0; + + node->status = THREAD_CANCELLED; + node->joinable = false; + + target_exec_env = node->exec_env; + bh_assert(target_exec_env != NULL); + + return wasm_cluster_cancel_thread(target_exec_env); +} + +static int32 +pthread_self_wrapper(wasm_exec_env_t exec_env) +{ + ThreadRoutineArgs *args = get_thread_arg(exec_env); + /* If thread_arg is NULL, it's the exec_env of the main thread, + return id 0 to app */ + if (!args) + return 0; + + return args->info_node->handle; +} + +/* emcc use __pthread_self rather than pthread_self */ +static int32 +__pthread_self_wrapper(wasm_exec_env_t exec_env) +{ + return pthread_self_wrapper(exec_env); +} + +static void +pthread_exit_wrapper(wasm_exec_env_t exec_env, int32 retval_offset) +{ + ThreadRoutineArgs *args = get_thread_arg(exec_env); + /* Currently exit main thread is not allowed */ + if (!args) + return; + +#if defined(OS_ENABLE_HW_BOUND_CHECK) && !defined(BH_PLATFORM_WINDOWS) + /* If hardware bound check enabled, don't deinstantiate module inst + and thread info node here for AoT module, as they will be freed + in pthread_start_routine */ + if (exec_env->jmpbuf_stack_top) { + wasm_cluster_exit_thread(exec_env, (void *)(uintptr_t)retval_offset); + } +#endif + + /* destroy pthread key values */ + call_key_destructor(exec_env); + + if (!args->info_node->joinable) { + delete_thread_info_node(args->info_node); + } + else { + args->info_node->u.ret = (void *)(uintptr_t)retval_offset; + /* Update node status after ret value was set */ + args->info_node->status = THREAD_EXIT; + } + + wasm_runtime_free(args); + + /* Don't destroy exec_env->module_inst in this functuntion since + it will be destroyed in wasm_cluster_exit_thread */ + wasm_cluster_exit_thread(exec_env, (void *)(uintptr_t)retval_offset); +} + +static int32 +pthread_mutex_init_wrapper(wasm_exec_env_t exec_env, uint32 *mutex, void *attr) +{ + korp_mutex *pmutex; + ThreadInfoNode *info_node; + + if (!(pmutex = wasm_runtime_malloc(sizeof(korp_mutex)))) { + return -1; + } + + if (os_mutex_init(pmutex) != 0) { + goto fail1; + } + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail2; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + info_node->exec_env = exec_env; + info_node->handle = allocate_handle(); + info_node->type = T_MUTEX; + info_node->u.mutex = pmutex; + info_node->status = MUTEX_CREATED; + + if (!append_thread_info_node(info_node)) + goto fail3; + + /* Return the mutex handle to app */ + if (mutex) + *(uint32 *)mutex = info_node->handle; + + return 0; + +fail3: + delete_thread_info_node(info_node); +fail2: + os_mutex_destroy(pmutex); +fail1: + wasm_runtime_free(pmutex); + + return -1; +} + +static int32 +pthread_mutex_lock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex) +{ + ThreadInfoNode *info_node = get_thread_info(exec_env, *mutex); + if (!info_node || info_node->type != T_MUTEX) + return -1; + + return os_mutex_lock(info_node->u.mutex); +} + +static int32 +pthread_mutex_unlock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex) +{ + ThreadInfoNode *info_node = get_thread_info(exec_env, *mutex); + if (!info_node || info_node->type != T_MUTEX) + return -1; + + return os_mutex_unlock(info_node->u.mutex); +} + +static int32 +pthread_mutex_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *mutex) +{ + int32 ret_val; + ThreadInfoNode *info_node = get_thread_info(exec_env, *mutex); + if (!info_node || info_node->type != T_MUTEX) + return -1; + + ret_val = os_mutex_destroy(info_node->u.mutex); + + info_node->status = MUTEX_DESTROYED; + delete_thread_info_node(info_node); + + return ret_val; +} + +static int32 +pthread_cond_init_wrapper(wasm_exec_env_t exec_env, uint32 *cond, void *attr) +{ + korp_cond *pcond; + ThreadInfoNode *info_node; + + if (!(pcond = wasm_runtime_malloc(sizeof(korp_cond)))) { + return -1; + } + + if (os_cond_init(pcond) != 0) { + goto fail1; + } + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail2; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + info_node->exec_env = exec_env; + info_node->handle = allocate_handle(); + info_node->type = T_COND; + info_node->u.cond = pcond; + info_node->status = COND_CREATED; + + if (!append_thread_info_node(info_node)) + goto fail3; + + /* Return the cond handle to app */ + if (cond) + *(uint32 *)cond = info_node->handle; + + return 0; + +fail3: + delete_thread_info_node(info_node); +fail2: + os_cond_destroy(pcond); +fail1: + wasm_runtime_free(pcond); + + return -1; +} + +static int32 +pthread_cond_wait_wrapper(wasm_exec_env_t exec_env, uint32 *cond, uint32 *mutex) +{ + ThreadInfoNode *cond_info_node, *mutex_info_node; + + cond_info_node = get_thread_info(exec_env, *cond); + if (!cond_info_node || cond_info_node->type != T_COND) + return -1; + + mutex_info_node = get_thread_info(exec_env, *mutex); + if (!mutex_info_node || mutex_info_node->type != T_MUTEX) + return -1; + + return os_cond_wait(cond_info_node->u.cond, mutex_info_node->u.mutex); +} + +/** + * Currently we don't support struct timespec in built-in libc, + * so the pthread_cond_timedwait use useconds instead + */ +static int32 +pthread_cond_timedwait_wrapper(wasm_exec_env_t exec_env, uint32 *cond, + uint32 *mutex, uint64 useconds) +{ + ThreadInfoNode *cond_info_node, *mutex_info_node; + + cond_info_node = get_thread_info(exec_env, *cond); + if (!cond_info_node || cond_info_node->type != T_COND) + return -1; + + mutex_info_node = get_thread_info(exec_env, *mutex); + if (!mutex_info_node || mutex_info_node->type != T_MUTEX) + return -1; + + return os_cond_reltimedwait(cond_info_node->u.cond, + mutex_info_node->u.mutex, useconds); +} + +static int32 +pthread_cond_signal_wrapper(wasm_exec_env_t exec_env, uint32 *cond) +{ + ThreadInfoNode *info_node = get_thread_info(exec_env, *cond); + if (!info_node || info_node->type != T_COND) + return -1; + + return os_cond_signal(info_node->u.cond); +} + +static int32 +pthread_cond_broadcast_wrapper(wasm_exec_env_t exec_env, uint32 *cond) +{ + ThreadInfoNode *info_node = get_thread_info(exec_env, *cond); + if (!info_node || info_node->type != T_COND) + return -1; + + return os_cond_broadcast(info_node->u.cond); +} + +static int32 +pthread_cond_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *cond) +{ + int32 ret_val; + ThreadInfoNode *info_node = get_thread_info(exec_env, *cond); + if (!info_node || info_node->type != T_COND) + return -1; + + ret_val = os_cond_destroy(info_node->u.cond); + + info_node->status = COND_DESTROYED; + delete_thread_info_node(info_node); + + return ret_val; +} + +static int32 +pthread_key_create_wrapper(wasm_exec_env_t exec_env, int32 *key, + int32 destructor_elem_index) +{ + uint32 i; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + if (!info) { + /* The user may call pthread_key_create in main thread, + in this case the cluster info hasn't been created */ + if (!(info = create_cluster_info(cluster))) { + return -1; + } + } + + os_mutex_lock(&info->key_data_list_lock); + for (i = 0; i < WAMR_PTHREAD_KEYS_MAX; i++) { + if (!info->key_data_list[i].is_created) { + break; + } + } + + if (i == WAMR_PTHREAD_KEYS_MAX) { + os_mutex_unlock(&info->key_data_list_lock); + return -1; + } + + info->key_data_list[i].destructor_func = destructor_elem_index; + info->key_data_list[i].is_created = true; + *key = i; + os_mutex_unlock(&info->key_data_list_lock); + + return 0; +} + +static int32 +pthread_setspecific_wrapper(wasm_exec_env_t exec_env, int32 key, + int32 value_offset) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + int32 *key_values; + + if (!info) + return -1; + + os_mutex_lock(&info->key_data_list_lock); + + key_values = key_value_list_lookup_or_create(exec_env, info, key); + if (!key_values) { + os_mutex_unlock(&info->key_data_list_lock); + return -1; + } + + key_values[key] = value_offset; + os_mutex_unlock(&info->key_data_list_lock); + + return 0; +} + +static int32 +pthread_getspecific_wrapper(wasm_exec_env_t exec_env, int32 key) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + int32 ret, *key_values; + + if (!info) + return 0; + + os_mutex_lock(&info->key_data_list_lock); + + key_values = key_value_list_lookup_or_create(exec_env, info, key); + if (!key_values) { + os_mutex_unlock(&info->key_data_list_lock); + return 0; + } + + ret = key_values[key]; + os_mutex_unlock(&info->key_data_list_lock); + + return ret; +} + +static int32 +pthread_key_delete_wrapper(wasm_exec_env_t exec_env, int32 key) +{ + KeyData *data; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + if (!info) + return -1; + + os_mutex_lock(&info->key_data_list_lock); + data = key_data_list_lookup(exec_env, key); + if (!data) { + os_mutex_unlock(&info->key_data_list_lock); + return -1; + } + + memset(data, 0, sizeof(KeyData)); + os_mutex_unlock(&info->key_data_list_lock); + + return 0; +} + +/** + * Currently the memory allocator doesn't support alloc specific aligned + * space, we wrap posix_memalign to simply malloc memory + */ +static int32 +posix_memalign_wrapper(wasm_exec_env_t exec_env, void **memptr, int32 align, + int32 size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + void *p = NULL; + + /* TODO: for memory 64, module_malloc may return uint64 offset */ + *((uint32 *)memptr) = (uint32)module_malloc(size, (void **)&p); + if (!p) + return -1; + + return 0; +} + +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 + +static int32 +sem_open_wrapper(wasm_exec_env_t exec_env, const char *name, int32 oflags, + int32 mode, int32 val) +{ + korp_sem *psem = NULL; + ThreadInfoNode *info_node = NULL; + + /** + * For RTOS, global semaphore map is safe for share the same semaphore + * between task/pthread. + * For Unix like system, it's dedicated for multiple processes. + */ + + if (!name) { /* avoid passing NULL to bh_hash_map_find and os_sem_open */ + return -1; + } + + if ((info_node = bh_hash_map_find(sem_info_map, (void *)name))) { + return info_node->handle; + } + + if (!(psem = os_sem_open(name, oflags, mode, val))) { + goto fail1; + } + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail2; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + info_node->exec_env = exec_env; + info_node->handle = allocate_handle(); + info_node->type = T_SEM; + info_node->u.sem = psem; + info_node->status = SEM_CREATED; + + if (!bh_hash_map_insert(sem_info_map, (void *)name, info_node)) + goto fail3; + + return info_node->handle; + +fail3: + wasm_runtime_free(info_node); +fail2: + os_sem_close(psem); +fail1: + return -1; +} + +void +sem_fetch_cb(void *key, void *value, void *user_data) +{ + (void)key; + SemCallbackArgs *args = user_data; + ThreadInfoNode *info_node = value; + if (args->handle == info_node->handle && info_node->status == SEM_CREATED) { + args->node = info_node; + } +} + +static int32 +sem_close_wrapper(wasm_exec_env_t exec_env, uint32 sem) +{ + (void)exec_env; + int ret = -1; + SemCallbackArgs args = { sem, NULL }; + + bh_hash_map_traverse(sem_info_map, sem_fetch_cb, &args); + + if (args.node) { + ret = os_sem_close(args.node->u.sem); + if (ret == 0) { + args.node->status = SEM_CLOSED; + } + } + + return ret; +} + +static int32 +sem_wait_wrapper(wasm_exec_env_t exec_env, uint32 sem) +{ + (void)exec_env; + SemCallbackArgs args = { sem, NULL }; + + bh_hash_map_traverse(sem_info_map, sem_fetch_cb, &args); + + if (args.node) { + return os_sem_wait(args.node->u.sem); + } + + return -1; +} + +static int32 +sem_trywait_wrapper(wasm_exec_env_t exec_env, uint32 sem) +{ + (void)exec_env; + SemCallbackArgs args = { sem, NULL }; + + bh_hash_map_traverse(sem_info_map, sem_fetch_cb, &args); + + if (args.node) { + return os_sem_trywait(args.node->u.sem); + } + + return -1; +} + +static int32 +sem_post_wrapper(wasm_exec_env_t exec_env, uint32 sem) +{ + (void)exec_env; + SemCallbackArgs args = { sem, NULL }; + + bh_hash_map_traverse(sem_info_map, sem_fetch_cb, &args); + + if (args.node) { + return os_sem_post(args.node->u.sem); + } + + return -1; +} + +static int32 +sem_getvalue_wrapper(wasm_exec_env_t exec_env, uint32 sem, int32 *sval) +{ + int32 ret = -1; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + (void)exec_env; + SemCallbackArgs args = { sem, NULL }; + + if (validate_native_addr(sval, (uint64)sizeof(int32))) { + + bh_hash_map_traverse(sem_info_map, sem_fetch_cb, &args); + + if (args.node) { + ret = os_sem_getvalue(args.node->u.sem, sval); + } + } + return ret; +} + +static int32 +sem_unlink_wrapper(wasm_exec_env_t exec_env, const char *name) +{ + (void)exec_env; + int32 ret_val; + + ThreadInfoNode *info_node; + + if (!name) { /* avoid passing NULL to bh_hash_map_find */ + return -1; + } + + info_node = bh_hash_map_find(sem_info_map, (void *)name); + if (!info_node || info_node->type != T_SEM) + return -1; + + if (info_node->status != SEM_CLOSED) { + ret_val = os_sem_close(info_node->u.sem); + if (ret_val != 0) { + return ret_val; + } + } + + ret_val = os_sem_unlink(name); + + if (ret_val == 0) { + bh_hash_map_remove(sem_info_map, (void *)name, NULL, NULL); + info_node->status = SEM_DESTROYED; + thread_info_destroy(info_node); + } + return ret_val; +} + +#endif + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_lib_pthread[] = { + REG_NATIVE_FUNC(pthread_create, "(**ii)i"), + REG_NATIVE_FUNC(pthread_join, "(ii)i"), + REG_NATIVE_FUNC(pthread_detach, "(i)i"), + REG_NATIVE_FUNC(pthread_cancel, "(i)i"), + REG_NATIVE_FUNC(pthread_self, "()i"), + REG_NATIVE_FUNC(__pthread_self, "()i"), + REG_NATIVE_FUNC(pthread_exit, "(i)"), + REG_NATIVE_FUNC(pthread_mutex_init, "(**)i"), + REG_NATIVE_FUNC(pthread_mutex_lock, "(*)i"), + REG_NATIVE_FUNC(pthread_mutex_unlock, "(*)i"), + REG_NATIVE_FUNC(pthread_mutex_destroy, "(*)i"), + REG_NATIVE_FUNC(pthread_cond_init, "(**)i"), + REG_NATIVE_FUNC(pthread_cond_wait, "(**)i"), + REG_NATIVE_FUNC(pthread_cond_timedwait, "(**I)i"), + REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"), + REG_NATIVE_FUNC(pthread_cond_broadcast, "(*)i"), + REG_NATIVE_FUNC(pthread_cond_destroy, "(*)i"), + REG_NATIVE_FUNC(pthread_key_create, "(*i)i"), + REG_NATIVE_FUNC(pthread_setspecific, "(ii)i"), + REG_NATIVE_FUNC(pthread_getspecific, "(i)i"), + REG_NATIVE_FUNC(pthread_key_delete, "(i)i"), + REG_NATIVE_FUNC(posix_memalign, "(*ii)i"), +#if WASM_ENABLE_LIB_PTHREAD_SEMAPHORE != 0 + REG_NATIVE_FUNC(sem_open, "($iii)i"), + REG_NATIVE_FUNC(sem_close, "(i)i"), + REG_NATIVE_FUNC(sem_wait, "(i)i"), + REG_NATIVE_FUNC(sem_trywait, "(i)i"), + REG_NATIVE_FUNC(sem_post, "(i)i"), + REG_NATIVE_FUNC(sem_getvalue, "(i*)i"), + REG_NATIVE_FUNC(sem_unlink, "($)i"), +#endif +}; + +uint32 +get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis) +{ + *p_lib_pthread_apis = native_symbols_lib_pthread; + return sizeof(native_symbols_lib_pthread) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/lib-rats/lib_rats.cmake b/core/iwasm/libraries/lib-rats/lib_rats.cmake new file mode 100644 index 0000000000..d9c63b6df6 --- /dev/null +++ b/core/iwasm/libraries/lib-rats/lib_rats.cmake @@ -0,0 +1,60 @@ +# Copyright (c) 2022 Intel Corporation +# Copyright (c) 2020-2021 Alibaba Cloud +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +set (LIB_RATS_DIR ${CMAKE_CURRENT_LIST_DIR}) + +if ("$ENV{SGX_SSL_DIR}" STREQUAL "") + set (SGX_SSL_DIR "/opt/intel/sgxssl") +else() + set (SGX_SSL_DIR $ENV{SGX_SSL_DIR}) +endif() + +if (NOT EXISTS ${SGX_SSL_DIR}) + message(FATAL_ERROR "Can not find SGX_SSL, please install it first") +endif() + +add_definitions (-DWASM_ENABLE_LIB_RATS=1) + +include_directories(SYSTEM ${LIB_RATS_DIR} ${SGX_SSL_DIR}/include) + +include(FetchContent) + +set(RATS_BUILD_MODE "sgx" + CACHE INTERNAL "Select build mode for librats(host|occlum|sgx|wasm)") +set(RATS_INSTALL_PATH "${CMAKE_BINARY_DIR}/librats" CACHE INTERNAL "") +set(BUILD_SAMPLES OFF CACHE BOOL "Disable de compilation of the librats samples" FORCE) + +FetchContent_Declare( + librats + GIT_REPOSITORY https://github.com/inclavare-containers/librats + GIT_TAG master +) +FetchContent_GetProperties(librats) +if (NOT librats_POPULATED) + message("-- Fetching librats ..") + FetchContent_Populate(librats) + include_directories(SYSTEM "${librats_SOURCE_DIR}/include") + + # Prevent the propagation of the CMAKE_C_FLAGS of WAMR into librats + set(SAVED_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) + set(CMAKE_C_FLAGS "") + + # Import the building scripts of librats + add_subdirectory(${librats_SOURCE_DIR} ${librats_BINARY_DIR} EXCLUDE_FROM_ALL) + + # Restore the CMAKE_C_FLAGS of WAMR + set(CMAKE_C_FLAGS ${SAVED_CMAKE_C_FLAGS}) + +endif() + +file (GLOB source_all ${LIB_RATS_DIR}/*.c) + +set (LIB_RATS_SOURCE ${source_all}) \ No newline at end of file diff --git a/core/iwasm/libraries/lib-rats/lib_rats_common.h b/core/iwasm/libraries/lib-rats/lib_rats_common.h new file mode 100644 index 0000000000..434c2abfd5 --- /dev/null +++ b/core/iwasm/libraries/lib-rats/lib_rats_common.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2020-2021 Alibaba Cloud + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _RATS_WAMR_COMMON_H +#define _RATS_WAMR_COMMON_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enclave Flags Bit Masks */ +/* If set, then the enclave is initialized */ +#define SGX_FLAGS_INITTED 0x001ULL +/* If set, then the enclave is debug */ +#define SGX_FLAGS_DEBUG 0x002ULL +/* If set, then the enclave is 64 bit */ +#define SGX_FLAGS_MODE64BIT 0x004ULL +/* If set, then the enclave has access to provision key */ +#define SGX_FLAGS_PROVISION_KEY 0x010ULL +/* If set, then the enclave has access to EINITTOKEN key */ +#define SGX_FLAGS_EINITTOKEN_KEY 0x020ULL +/* If set, then the enclave uses KSS */ +#define SGX_FLAGS_KSS 0x080ULL +/* If set, then the enclave enables AEX Notify */ +#define SGX_FLAGS_AEX_NOTIFY 0x400ULL + +#define SGX_QUOTE_MAX_SIZE 8192 +#define SGX_USER_DATA_SIZE 64 +#define SGX_MEASUREMENT_SIZE 32 + +/* clang-format off */ +typedef struct rats_sgx_evidence { + uint8_t quote[SGX_QUOTE_MAX_SIZE]; /* The quote of the Enclave */ + uint32_t quote_size; /* The size of the quote */ + uint8_t user_data[SGX_USER_DATA_SIZE]; /* The custom data in the quote */ + uint32_t product_id; /* Product ID of the Enclave */ + uint8_t mr_enclave[SGX_MEASUREMENT_SIZE]; /* The MRENCLAVE of the Enclave */ + uint32_t security_version; /* Security Version of the Enclave */ + uint8_t mr_signer[SGX_MEASUREMENT_SIZE]; /* The MRSIGNER of the Enclave */ + uint64_t att_flags; /* Flags of the Enclave in attributes */ + uint64_t att_xfrm; /* XSAVE Feature Request Mask */ +} rats_sgx_evidence_t; +/* clang-format on */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/libraries/lib-rats/lib_rats_wrapper.c b/core/iwasm/libraries/lib-rats/lib_rats_wrapper.c new file mode 100644 index 0000000000..bdacc259ce --- /dev/null +++ b/core/iwasm/libraries/lib-rats/lib_rats_wrapper.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2020-2021 Alibaba Cloud + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +#include "sgx_quote_3.h" +#include "wasm_export.h" +#include "bh_common.h" +#include "lib_rats_common.h" + +static int +librats_collect_wrapper(wasm_exec_env_t exec_env, uint32_t *evidence_json, + const char *buffer, uint32_t buffer_size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_module_t module = wasm_runtime_get_module(module_inst); + char *wasm_module_hash = wasm_runtime_get_module_hash(module); + + char *json, *str_ret; + uint32_t str_ret_offset; + uint8_t final_hash[SHA256_DIGEST_LENGTH]; + + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, wasm_module_hash, SHA256_DIGEST_LENGTH); + if (buffer != NULL) + SHA256_Update(&sha256, buffer, buffer_size); + SHA256_Final(final_hash, &sha256); + + int ret_code = librats_collect_evidence_to_json(final_hash, &json); + if (ret_code != 0) { + return ret_code; + } + + uint32_t json_size = strlen(json) + 1; + str_ret_offset = module_malloc(json_size, (void **)&str_ret); + if (!str_ret_offset) { + free(json); + return (int)RATS_ATTESTER_ERR_NO_MEM; + } + bh_memcpy_s(str_ret, json_size, json, json_size); + *evidence_json = str_ret_offset; + free(json); + + return 0; +} + +static int +librats_verify_wrapper(wasm_exec_env_t exec_env, const char *evidence_json, + uint32_t evidence_size, const uint8_t *hash, + uint32_t hash_size) +{ + return librats_verify_evidence_from_json(evidence_json, hash); +} + +static int +librats_parse_evidence_wrapper(wasm_exec_env_t exec_env, + const char *evidence_json, uint32_t json_size, + rats_sgx_evidence_t *evidence, + uint32_t evidence_size) +{ + attestation_evidence_t att_ev; + + if (get_evidence_from_json(evidence_json, &att_ev) != 0) { + return -1; + } + + // Only supports parsing sgx evidence currently + if (strcmp(att_ev.type, "sgx_ecdsa") != 0) { + return -1; + } + + sgx_quote3_t *quote_ptr = (sgx_quote3_t *)att_ev.ecdsa.quote; + bh_memcpy_s(evidence->quote, att_ev.ecdsa.quote_len, att_ev.ecdsa.quote, + att_ev.ecdsa.quote_len); + evidence->quote_size = att_ev.ecdsa.quote_len; + bh_memcpy_s(evidence->user_data, SGX_REPORT_DATA_SIZE, + quote_ptr->report_body.report_data.d, SGX_REPORT_DATA_SIZE); + bh_memcpy_s(evidence->mr_enclave, sizeof(sgx_measurement_t), + quote_ptr->report_body.mr_enclave.m, sizeof(sgx_measurement_t)); + bh_memcpy_s(evidence->mr_signer, sizeof(sgx_measurement_t), + quote_ptr->report_body.mr_signer.m, sizeof(sgx_measurement_t)); + evidence->product_id = quote_ptr->report_body.isv_prod_id; + evidence->security_version = quote_ptr->report_body.isv_svn; + evidence->att_flags = quote_ptr->report_body.attributes.flags; + evidence->att_xfrm = quote_ptr->report_body.attributes.flags; + + return 0; +} + +static void +librats_dispose_evidence_json_wrapper(wasm_exec_env_t exec_env, + uint32_t evidence_json) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + module_free(evidence_json); +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_lib_rats[] = { + REG_NATIVE_FUNC(librats_collect, "(**~)i"), + REG_NATIVE_FUNC(librats_verify, "(*~*~)i"), + REG_NATIVE_FUNC(librats_parse_evidence, "(*~*~)i"), + REG_NATIVE_FUNC(librats_dispose_evidence_json, "(i)") +}; + +uint32_t +get_lib_rats_export_apis(NativeSymbol **p_lib_rats_apis) +{ + *p_lib_rats_apis = native_symbols_lib_rats; + return sizeof(native_symbols_lib_rats) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/lib-rats/lib_rats_wrapper.h b/core/iwasm/libraries/lib-rats/lib_rats_wrapper.h new file mode 100644 index 0000000000..9286451080 --- /dev/null +++ b/core/iwasm/libraries/lib-rats/lib_rats_wrapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2020-2021 Alibaba Cloud + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _RATS_WAMR_API_H +#define _RATS_WAMR_API_H + +#include +#include + +#include "lib_rats_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int +librats_collect(char **evidence_json, const char *buffer, uint32_t buffer_size); + +int +librats_verify(const char *evidence_json, uint32_t evidence_size, + const uint8_t *hash, uint32_t hash_size); + +int +librats_parse_evidence(const char *evidence_json, uint32_t json_size, + rats_sgx_evidence_t *evidence, uint32_t evidence_size); + +#define librats_collect(evidence_json, buffer) \ + librats_collect(evidence_json, buffer, buffer ? strlen(buffer) + 1 : 0) + +#define librats_verify(evidence_json, hash) \ + librats_verify(evidence_json, \ + evidence_json ? strlen(evidence_json) + 1 : 0, hash, \ + hash ? strlen((const char *)hash) + 1 : 0) + +#define librats_parse_evidence(evidence_json, evidence) \ + librats_parse_evidence(evidence_json, \ + evidence_json ? strlen(evidence_json) + 1 : 0, \ + evidence, sizeof(rats_sgx_evidence_t)) + +void +librats_dispose_evidence_json(char *evidence_json); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h new file mode 100644 index 0000000000..2bad1ebed9 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h @@ -0,0 +1,1026 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASI_SOCKET_EXT_H_ +#define _WASI_SOCKET_EXT_H_ + +#include +#include +#include + +/*Be a part of */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + /* Used only for sock_addr_resolve hints */ + SOCKET_ANY = -1, + SOCKET_DGRAM = 0, + SOCKET_STREAM, +} __wasi_sock_type_t; + +typedef uint16_t __wasi_ip_port_t; + +typedef enum { IPv4 = 0, IPv6 } __wasi_addr_type_t; + +/* + n0.n1.n2.n3 + Example: + IP Address: 127.0.0.1 + Structure: {n0: 127, n1: 0, n2: 0, n3: 1} +*/ +typedef struct __wasi_addr_ip4_t { + uint8_t n0; + uint8_t n1; + uint8_t n2; + uint8_t n3; +} __wasi_addr_ip4_t; + +typedef struct __wasi_addr_ip4_port_t { + __wasi_addr_ip4_t addr; + __wasi_ip_port_t port; /* host byte order */ +} __wasi_addr_ip4_port_t; + +/* + n0:n1:n2:n3:h0:h1:h2:h3, each 16bit value uses host byte order + Example (little-endian system) + IP Address fe80::3ba2:893b:4be0:e3dd + Structure: { + n0: 0xfe80, n1:0x0, n2: 0x0, n3: 0x0, + h0: 0x3ba2, h1: 0x893b, h2: 0x4be0, h3: 0xe3dd + } +*/ +typedef struct __wasi_addr_ip6_t { + uint16_t n0; + uint16_t n1; + uint16_t n2; + uint16_t n3; + uint16_t h0; + uint16_t h1; + uint16_t h2; + uint16_t h3; +} __wasi_addr_ip6_t; + +typedef struct __wasi_addr_ip6_port_t { + __wasi_addr_ip6_t addr; + __wasi_ip_port_t port; /* host byte order */ +} __wasi_addr_ip6_port_t; + +typedef struct __wasi_addr_ip_t { + __wasi_addr_type_t kind; + union { + __wasi_addr_ip4_t ip4; + __wasi_addr_ip6_t ip6; + } addr; +} __wasi_addr_ip_t; + +typedef struct __wasi_addr_t { + __wasi_addr_type_t kind; + union { + __wasi_addr_ip4_port_t ip4; + __wasi_addr_ip6_port_t ip6; + } addr; +} __wasi_addr_t; + +typedef enum { INET4 = 0, INET6, INET_UNSPEC } __wasi_address_family_t; + +typedef struct __wasi_addr_info_t { + __wasi_addr_t addr; + __wasi_sock_type_t type; +} __wasi_addr_info_t; + +typedef struct __wasi_addr_info_hints_t { + __wasi_sock_type_t type; + __wasi_address_family_t family; + // this is to workaround lack of optional parameters + uint8_t hints_enabled; +} __wasi_addr_info_hints_t; + +#ifdef __wasi__ +/** + * Reimplement below POSIX APIs with __wasi_sock_XXX functions. + * + * Keep sync with + * + * + */ +#define SO_REUSEADDR 2 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_KEEPALIVE 9 +#define SO_LINGER 13 +#define SO_REUSEPORT 15 +#define SO_RCVTIMEO 20 +#define SO_SNDTIMEO 21 + +#define TCP_NODELAY 1 +#define TCP_KEEPIDLE 4 +#define TCP_KEEPINTVL 5 +#define TCP_QUICKACK 12 +#define TCP_FASTOPEN_CONNECT 30 + +#define IP_TTL 2 +#define IP_MULTICAST_TTL 33 +#define IP_MULTICAST_LOOP 34 +#define IP_ADD_MEMBERSHIP 35 +#define IP_DROP_MEMBERSHIP 36 + +#define IPV6_MULTICAST_LOOP 19 +#define IPV6_JOIN_GROUP 20 +#define IPV6_LEAVE_GROUP 21 +#define IPV6_V6ONLY 26 + +/* getaddrinfo error codes. + * + * we use values compatible with wasi-libc/musl netdb.h. + * https://github.com/WebAssembly/wasi-libc/blob/4ea6fdfa288e15a57c02fe31dda78e5ddc87c3c7/libc-top-half/musl/include/netdb.h#L43-L53 + * + * for now, non-posix error codes are excluded: + * EAI_PROTOCOL and EAI_BADHINTS (BSDs) + * EAI_ADDRFAMILY, EAI_NODATA + * https://github.com/WebAssembly/wasi-libc/blob/4ea6fdfa288e15a57c02fe31dda78e5ddc87c3c7/libc-top-half/musl/include/netdb.h#L145-L152 + */ + +#define EAI_AGAIN -3 +#define EAI_BADFLAGS -1 +#define EAI_FAIL -4 +#define EAI_FAMILY -6 +#define EAI_MEMORY -10 +#define EAI_NONAME -2 +#define EAI_OVERFLOW -12 +#define EAI_SERVICE -8 +#define EAI_SOCKTYPE -7 +#define EAI_SYSTEM -11 + +struct addrinfo { + int ai_flags; /* Input flags. */ + int ai_family; /* Protocol family for socket. */ + int ai_socktype; /* Socket type. */ + int ai_protocol; /* Protocol for socket. */ + socklen_t ai_addrlen; /* Length of socket address. */ + struct sockaddr *ai_addr; /* Socket address for socket. */ + char *ai_canonname; /* Canonical name for service location. */ + struct addrinfo *ai_next; /* Pointer to next in list. */ +}; + +#ifndef __WASI_RIGHTS_SOCK_ACCEPT +int +accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +#endif + +int +bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + +int +connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + +int +listen(int sockfd, int backlog); + +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags); + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags); + +ssize_t +sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen); + +ssize_t +recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen); + +int +socket(int domain, int type, int protocol); + +int +getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + +int +getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + +int +getsockopt(int sockfd, int level, int optname, void *__restrict optval, + socklen_t *__restrict optlen); + +int +setsockopt(int sockfd, int level, int optname, const void *optval, + socklen_t optlen); + +int +getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, + struct addrinfo **res); + +void +freeaddrinfo(struct addrinfo *res); + +const char * +gai_strerror(int code); +#endif + +/** + * __wasi_sock_accept was introduced in wasi-sdk v15. To + * temporarily maintain backward compatibility with the old + * wasi-sdk, we explicitly add that implementation here so it works + * with older versions of the SDK. + */ +#ifndef __WASI_RIGHTS_SOCK_ACCEPT +/** + * Accept a connection on a socket + * Note: This is similar to `accept` + */ +int32_t +__imported_wasi_snapshot_preview1_sock_accept(int32_t arg0, int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_accept"))); + +static inline __wasi_errno_t +__wasi_sock_accept(__wasi_fd_t fd, __wasi_fdflags_t flags, __wasi_fd_t *fd_new) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_accept( + (int32_t)fd, (int32_t)flags, (int32_t)fd_new); +} +#endif + +/** + * Returns the local address to which the socket is bound. + * + * Note: This is similar to `getsockname` in POSIX + * + * When successful, the contents of the output buffer consist of an IP address, + * either IP4 or IP6. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_addr_local(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_addr_local"))); + +static inline __wasi_errno_t +__wasi_sock_addr_local(__wasi_fd_t fd, __wasi_addr_t *addr) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_local( + (int32_t)fd, (int32_t)addr); +} + +/** + * Returns the remote address to which the socket is connected to. + * + * Note: This is similar to `getpeername` in POSIX + * + * When successful, the contents of the output buffer consist of an IP address, + * either IP4 or IP6. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_addr_remote(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_addr_remote"))); + +static inline __wasi_errno_t +__wasi_sock_addr_remote(__wasi_fd_t fd, __wasi_addr_t *addr) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_remote( + (int32_t)fd, (int32_t)addr); +} + +/** + * Resolve a hostname and a service to one or more IP addresses. Service is + * optional and you can pass empty string in most cases, it is used as a hint + * for protocol. + * + * Note: This is similar to `getaddrinfo` in POSIX + * + * When successful, the contents of the output buffer consist of a sequence of + * IPv4 and/or IPv6 addresses. Each address entry consists of a wasi_addr_t + * object. + * + * This function fills the output buffer as much as possible, truncating the + * entries that didn't fit into the buffer. A number of available addresses + * will be returned through the last parameter. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_addr_resolve(int32_t arg0, int32_t arg1, + int32_t arg2, int32_t arg3, + int32_t arg4, int32_t arg5) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_addr_resolve"))); + +static inline __wasi_errno_t +__wasi_sock_addr_resolve(const char *host, const char *service, + __wasi_addr_info_hints_t *hints, + __wasi_addr_info_t *addr_info, + __wasi_size_t addr_info_size, + __wasi_size_t *max_info_size) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_addr_resolve( + (int32_t)host, (int32_t)service, (int32_t)hints, (int32_t)addr_info, + (int32_t)addr_info_size, (int32_t)max_info_size); +} + +/** + * Bind a socket + * Note: This is similar to `bind` in POSIX using PF_INET + */ +int32_t +__imported_wasi_snapshot_preview1_sock_bind(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_bind"))); + +static inline __wasi_errno_t +__wasi_sock_bind(__wasi_fd_t fd, __wasi_addr_t *addr) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_bind( + (int32_t)fd, (int32_t)addr); +} + +/** + * Send data to a specific target + * Note: This is similar to `sendto` in POSIX + */ +int32_t +__imported_wasi_snapshot_preview1_sock_send_to(int32_t arg0, int32_t arg1, + int32_t arg2, int32_t arg3, + int32_t arg4, int32_t arg5) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_send_to"))); + +static inline __wasi_errno_t +__wasi_sock_send_to(__wasi_fd_t fd, const __wasi_ciovec_t *si_data, + uint32_t si_data_len, __wasi_siflags_t si_flags, + const __wasi_addr_t *dest_addr, uint32_t *so_data_len) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_send_to( + (int32_t)fd, (int32_t)si_data, (int32_t)si_data_len, (int32_t)si_flags, + (uint32_t)dest_addr, (uint32_t)so_data_len); +} + +/** + * Receives data from a socket + * Note: This is similar to `recvfrom` in POSIX + */ +int32_t +__imported_wasi_snapshot_preview1_sock_recv_from(int32_t arg0, int32_t arg1, + int32_t arg2, int32_t arg3, + int32_t arg4, int32_t arg5) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_recv_from"))); + +static inline __wasi_errno_t +__wasi_sock_recv_from(__wasi_fd_t fd, __wasi_ciovec_t *ri_data, + uint32_t ri_data_len, __wasi_riflags_t ri_flags, + __wasi_addr_t *src_addr, uint32_t *ro_data_len) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_recv_from( + (int32_t)fd, (int32_t)ri_data, (int32_t)ri_data_len, (int32_t)ri_flags, + (uint32_t)src_addr, (uint32_t)ro_data_len); +} + +/** + * Close a socket (this is an alias for `fd_close`) + * Note: This is similar to `close` in POSIX. + */ +int32_t +__imported_wasi_snapshot_preview1_sock_close(int32_t arg0) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_close"))); + +static inline __wasi_errno_t +__wasi_sock_close(__wasi_fd_t fd) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_close( + (int32_t)fd); +} + +/** + * Initiate a connection on a socket to the specified address + * Note: This is similar to `connect` in POSIX + */ + +int32_t +__imported_wasi_snapshot_preview1_sock_connect(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_connect"))); + +static inline __wasi_errno_t +__wasi_sock_connect(__wasi_fd_t fd, __wasi_addr_t *addr) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_connect( + (int32_t)fd, (int32_t)addr); +} +/** + * Retrieve the size of the receive buffer + * Note: This is similar to `getsockopt` in POSIX for SO_RCVBUF + */ + +int32_t +__imported_wasi_snapshot_preview1_sock_get_recv_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_recv_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_get_recv_buf_size(__wasi_fd_t fd, __wasi_size_t *size) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_recv_buf_size((int32_t)fd, + (int32_t)size); +} +/** + * Retrieve status of address reuse on a socket + * Note: This is similar to `getsockopt` in POSIX for SO_REUSEADDR + */ +int32_t +__imported_wasi_snapshot_preview1_sock_get_reuse_addr(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_reuse_addr"))); + +static inline __wasi_errno_t +__wasi_sock_get_reuse_addr(__wasi_fd_t fd, bool *reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_reuse_addr((int32_t)fd, + (int32_t)reuse); +} + +/** + * Retrieve status of port reuse on a socket + * Note: This is similar to `getsockopt` in POSIX for SO_REUSEPORT + */ +int32_t +__imported_wasi_snapshot_preview1_sock_get_reuse_port(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_reuse_port"))); + +static inline __wasi_errno_t +__wasi_sock_get_reuse_port(__wasi_fd_t fd, bool *reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_reuse_port((int32_t)fd, + (int32_t)reuse); +} + +/** + * Retrieve the size of the send buffer + * Note: This is similar to `getsockopt` in POSIX for SO_SNDBUF + */ +int32_t +__imported_wasi_snapshot_preview1_sock_get_send_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_send_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_get_send_buf_size(__wasi_fd_t fd, __wasi_size_t *size) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_send_buf_size((int32_t)fd, + (int32_t)size); +} + +/** + * Listen for connections on a socket + * Note: This is similar to `listen` + */ +int32_t +__imported_wasi_snapshot_preview1_sock_listen(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_listen"))); + +static inline __wasi_errno_t +__wasi_sock_listen(__wasi_fd_t fd, __wasi_size_t backlog) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_listen( + (int32_t)fd, (int32_t)backlog); +} + +/** + * Open a socket + + * The first argument to this function is a handle to an + * address pool. The address pool determines what actions can + * be performed and at which addresses they can be performed to. + + * The address pool cannot be re-assigned. You will need to close + * the socket and open a new one to use a different address pool. + + * Note: This is similar to `socket` in POSIX using PF_INET + */ + +int32_t +__imported_wasi_snapshot_preview1_sock_open(int32_t arg0, int32_t arg1, + int32_t arg2, int32_t arg3) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_open"))); + +static inline __wasi_errno_t +__wasi_sock_open(__wasi_fd_t fd, __wasi_address_family_t af, + __wasi_sock_type_t socktype, __wasi_fd_t *sockfd) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_open( + (int32_t)fd, (int32_t)af, (int32_t)socktype, (int32_t)sockfd); +} + +/** + * Set size of receive buffer + * Note: This is similar to `setsockopt` in POSIX for SO_RCVBUF + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_recv_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_recv_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_set_recv_buf_size(__wasi_fd_t fd, __wasi_size_t size) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_recv_buf_size((int32_t)fd, + (int32_t)size); +} + +/** + * Enable/disable address reuse on a socket + * Note: This is similar to `setsockopt` in POSIX for SO_REUSEADDR + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_reuse_addr(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_reuse_addr"))); + +static inline __wasi_errno_t +__wasi_sock_set_reuse_addr(__wasi_fd_t fd, bool reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_reuse_addr((int32_t)fd, + (int32_t)reuse); +} + +/** + * Enable port reuse on a socket + * Note: This is similar to `setsockopt` in POSIX for SO_REUSEPORT + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_reuse_port(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_reuse_port"))); + +static inline __wasi_errno_t +__wasi_sock_set_reuse_port(__wasi_fd_t fd, bool reuse) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_reuse_port((int32_t)fd, + (int32_t)reuse); +} + +/** + * Set size of send buffer + * Note: This is similar to `setsockopt` in POSIX for SO_SNDBUF + */ +int32_t +__imported_wasi_snapshot_preview1_sock_set_send_buf_size(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_send_buf_size"))); + +static inline __wasi_errno_t +__wasi_sock_set_send_buf_size(__wasi_fd_t fd, __wasi_size_t buf_len) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_send_buf_size( + (int32_t)fd, (int32_t)buf_len); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_recv_timeout(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_recv_timeout"))); + +static inline __wasi_errno_t +__wasi_sock_get_recv_timeout(__wasi_fd_t fd, uint64_t *timeout_us) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_recv_timeout( + (int32_t)fd, (int32_t)timeout_us); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_recv_timeout(int32_t arg0, + int64_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_recv_timeout"))); + +static inline __wasi_errno_t +__wasi_sock_set_recv_timeout(__wasi_fd_t fd, uint64_t timeout_us) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_recv_timeout( + (int32_t)fd, (int64_t)timeout_us); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_send_timeout(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_send_timeout"))); + +static inline __wasi_errno_t +__wasi_sock_get_send_timeout(__wasi_fd_t fd, uint64_t *timeout_us) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_send_timeout( + (int32_t)fd, (int32_t)timeout_us); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_send_timeout(int32_t arg0, + int64_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_send_timeout"))); + +static inline __wasi_errno_t +__wasi_sock_set_send_timeout(__wasi_fd_t fd, uint64_t timeout_us) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_send_timeout( + (int32_t)fd, (int64_t)timeout_us); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_keep_alive(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_keep_alive"))); + +static inline __wasi_errno_t +__wasi_sock_set_keep_alive(__wasi_fd_t fd, bool option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_keep_alive((int32_t)fd, + (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_keep_alive(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_keep_alive"))); + +static inline __wasi_errno_t +__wasi_sock_get_keep_alive(__wasi_fd_t fd, bool *option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_keep_alive((int32_t)fd, + (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_linger(int32_t arg0, int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_linger"))); + +static inline __wasi_errno_t +__wasi_sock_set_linger(__wasi_fd_t fd, bool is_enabled, int linger_s) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_set_linger( + (int32_t)fd, (int32_t)is_enabled, (int32_t)linger_s); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_linger(int32_t arg0, int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_linger"))); + +static inline __wasi_errno_t +__wasi_sock_get_linger(__wasi_fd_t fd, bool *is_enabled, int *linger_s) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_get_linger( + (int32_t)fd, (int32_t)is_enabled, (int32_t)linger_s); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_tcp_keep_idle(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_tcp_keep_idle"))); + +static inline __wasi_errno_t +__wasi_sock_set_tcp_keep_idle(__wasi_fd_t fd, uint32_t time_s) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_tcp_keep_idle( + (int32_t)fd, (int32_t)time_s); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_tcp_keep_idle(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_tcp_keep_idle"))); + +static inline __wasi_errno_t +__wasi_sock_get_tcp_keep_idle(__wasi_fd_t fd, uint32_t *time_s) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_tcp_keep_idle( + (int32_t)fd, (int32_t)time_s); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_tcp_keep_intvl(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_tcp_keep_intvl"))); + +static inline __wasi_errno_t +__wasi_sock_set_tcp_keep_intvl(__wasi_fd_t fd, uint32_t time_s) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_tcp_keep_intvl( + (int32_t)fd, (int32_t)time_s); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_tcp_keep_intvl(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_tcp_keep_intvl"))); + +static inline __wasi_errno_t +__wasi_sock_get_tcp_keep_intvl(__wasi_fd_t fd, uint32_t *time_s) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_tcp_keep_intvl( + (int32_t)fd, (int32_t)time_s); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_tcp_fastopen_connect(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_tcp_fastopen_connect"))); + +static inline __wasi_errno_t +__wasi_sock_set_tcp_fastopen_connect(__wasi_fd_t fd, bool option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_tcp_fastopen_connect( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_tcp_fastopen_connect(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_tcp_fastopen_connect"))); + +static inline __wasi_errno_t +__wasi_sock_get_tcp_fastopen_connect(__wasi_fd_t fd, bool *option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_tcp_fastopen_connect( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_ip_multicast_loop(int32_t arg0, + int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_ip_multicast_loop"))); + +static inline __wasi_errno_t +__wasi_sock_set_ip_multicast_loop(__wasi_fd_t fd, bool ipv6, bool option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_ip_multicast_loop( + (int32_t)fd, (int32_t)ipv6, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_ip_multicast_loop(int32_t arg0, + int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_ip_multicast_loop"))); + +static inline __wasi_errno_t +__wasi_sock_get_ip_multicast_loop(__wasi_fd_t fd, bool ipv6, bool *option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_ip_multicast_loop( + (int32_t)fd, (int32_t)ipv6, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_ip_multicast_ttl(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_ip_multicast_ttl"))); + +static inline __wasi_errno_t +__wasi_sock_set_ip_multicast_ttl(__wasi_fd_t fd, uint8_t option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_ip_multicast_ttl( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_ip_multicast_ttl(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_ip_multicast_ttl"))); + +static inline __wasi_errno_t +__wasi_sock_get_ip_multicast_ttl(__wasi_fd_t fd, uint8_t *option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_ip_multicast_ttl( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_ip_add_membership(int32_t arg0, + int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_ip_add_membership"))); + +static inline __wasi_errno_t +__wasi_sock_set_ip_add_membership(__wasi_fd_t fd, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_ip_add_membership( + (int32_t)fd, (int32_t)imr_multiaddr, (int32_t)imr_interface); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_ip_drop_membership(int32_t arg0, + int32_t arg1, + int32_t arg2) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_ip_drop_membership"))); + +static inline __wasi_errno_t +__wasi_sock_set_ip_drop_membership(__wasi_fd_t fd, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_ip_drop_membership( + (int32_t)fd, (int32_t)imr_multiaddr, (int32_t)imr_interface); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_broadcast(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_broadcast"))); + +static inline __wasi_errno_t +__wasi_sock_set_broadcast(__wasi_fd_t fd, bool option) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_set_broadcast( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_broadcast(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_broadcast"))); + +static inline __wasi_errno_t +__wasi_sock_get_broadcast(__wasi_fd_t fd, bool *option) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_get_broadcast( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_tcp_no_delay(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_tcp_no_delay"))); + +static inline __wasi_errno_t +__wasi_sock_set_tcp_no_delay(__wasi_fd_t fd, bool option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_tcp_no_delay( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_tcp_no_delay(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_tcp_no_delay"))); + +static inline __wasi_errno_t +__wasi_sock_get_tcp_no_delay(__wasi_fd_t fd, bool *option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_tcp_no_delay( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_tcp_quick_ack(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_tcp_quick_ack"))); + +static inline __wasi_errno_t +__wasi_sock_set_tcp_quick_ack(__wasi_fd_t fd, bool option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_set_tcp_quick_ack( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_tcp_quick_ack(int32_t arg0, + int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_tcp_quick_ack"))); + +static inline __wasi_errno_t +__wasi_sock_get_tcp_quick_ack(__wasi_fd_t fd, bool *option) +{ + return (__wasi_errno_t) + __imported_wasi_snapshot_preview1_sock_get_tcp_quick_ack( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_ip_ttl(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_ip_ttl"))); + +static inline __wasi_errno_t +__wasi_sock_set_ip_ttl(__wasi_fd_t fd, uint8_t option) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_set_ip_ttl( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_ip_ttl(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_ip_ttl"))); + +static inline __wasi_errno_t +__wasi_sock_get_ip_ttl(__wasi_fd_t fd, uint8_t *option) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_get_ip_ttl( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_set_ipv6_only(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_set_ipv6_only"))); + +static inline __wasi_errno_t +__wasi_sock_set_ipv6_only(__wasi_fd_t fd, bool option) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_set_ipv6_only( + (int32_t)fd, (int32_t)option); +} + +int32_t +__imported_wasi_snapshot_preview1_sock_get_ipv6_only(int32_t arg0, int32_t arg1) + __attribute__((__import_module__("wasi_snapshot_preview1"), + __import_name__("sock_get_ipv6_only"))); + +static inline __wasi_errno_t +__wasi_sock_get_ipv6_only(__wasi_fd_t fd, bool *option) +{ + return (__wasi_errno_t)__imported_wasi_snapshot_preview1_sock_get_ipv6_only( + (int32_t)fd, (int32_t)option); +} +/** + * TODO: modify recv() and send() + * since don't want to re-compile the wasi-libc, + * we tend to keep original implementations of recv() and send(). + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake b/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake new file mode 100644 index 0000000000..c6a4430f7f --- /dev/null +++ b/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake @@ -0,0 +1,9 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(socket_wasi_ext LANGUAGES C) + +add_library(${PROJECT_NAME} STATIC ${CMAKE_CURRENT_LIST_DIR}/src/wasi/wasi_socket_ext.c) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/inc/) diff --git a/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c new file mode 100644 index 0000000000..2a4b4a7214 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c @@ -0,0 +1,1076 @@ +/* + * 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 + +/* + * Avoid direct TLS access to allow a single library to be + * linked to both of threaded and non-threaded applications. + * + * wasi-libc's errno is a TLS variable, exposed directly via + * errno.h. if we use it here, LLVM may lower it differently, + * depending on enabled features like atomics and bulk-memory. + * we tweak the way to access errno here in order to make us + * compatible with both of threaded and non-threaded applications. + * __errno_location() should be reasonably stable because + * it was introduced as an alternative ABI for non-C software. + * https://github.com/WebAssembly/wasi-libc/pull/347 + */ +#if defined(errno) +#undef errno +#endif +int * +__errno_location(void); +#define errno (*__errno_location()) + +#define HANDLE_ERROR(error) \ + if (error != __WASI_ERRNO_SUCCESS) { \ + errno = error; \ + return -1; \ + } + +/* REVISIT: in many cases, EAI_SYSTEM may not be an ideal error code */ +#define GAI_HANDLE_ERROR(error) \ + if (error != __WASI_ERRNO_SUCCESS) { \ + errno = error; \ + return EAI_SYSTEM; \ + } + +static void +ipv4_addr_to_wasi_ip4_addr(uint32_t addr_num, __wasi_addr_ip4_t *out) +{ + addr_num = ntohl(addr_num); + out->n0 = (addr_num & 0xFF000000) >> 24; + out->n1 = (addr_num & 0x00FF0000) >> 16; + out->n2 = (addr_num & 0x0000FF00) >> 8; + out->n3 = (addr_num & 0x000000FF); +} + +/* addr_num and port are in network order */ +static void +ipv4_addr_to_wasi_addr(uint32_t addr_num, uint16_t port, __wasi_addr_t *out) +{ + out->kind = IPv4; + out->addr.ip4.port = ntohs(port); + ipv4_addr_to_wasi_ip4_addr(addr_num, &(out->addr.ip4.addr)); +} + +static void +ipv6_addr_to_wasi_ipv6_addr(uint16_t *addr, __wasi_addr_ip6_t *out) +{ + out->n0 = ntohs(addr[0]); + out->n1 = ntohs(addr[1]); + out->n2 = ntohs(addr[2]); + out->n3 = ntohs(addr[3]); + out->h0 = ntohs(addr[4]); + out->h1 = ntohs(addr[5]); + out->h2 = ntohs(addr[6]); + out->h3 = ntohs(addr[7]); +} + +static void +ipv6_addr_to_wasi_addr(uint16_t *addr, uint16_t port, __wasi_addr_t *out) +{ + out->kind = IPv6; + out->addr.ip6.port = ntohs(port); + ipv6_addr_to_wasi_ipv6_addr(addr, &(out->addr.ip6.addr)); +} + +static __wasi_errno_t +sockaddr_to_wasi_addr(const struct sockaddr *sock_addr, socklen_t addrlen, + __wasi_addr_t *wasi_addr) +{ + __wasi_errno_t ret = __WASI_ERRNO_SUCCESS; + if (AF_INET == sock_addr->sa_family) { + assert(sizeof(struct sockaddr_in) <= addrlen); + + ipv4_addr_to_wasi_addr( + ((struct sockaddr_in *)sock_addr)->sin_addr.s_addr, + ((struct sockaddr_in *)sock_addr)->sin_port, wasi_addr); + } + else if (AF_INET6 == sock_addr->sa_family) { + assert(sizeof(struct sockaddr_in6) <= addrlen); + ipv6_addr_to_wasi_addr( + (uint16_t *)((struct sockaddr_in6 *)sock_addr)->sin6_addr.s6_addr, + ((struct sockaddr_in6 *)sock_addr)->sin6_port, wasi_addr); + } + else { + ret = __WASI_ERRNO_AFNOSUPPORT; + } + + return ret; +} + +static __wasi_errno_t +wasi_addr_to_sockaddr(const __wasi_addr_t *wasi_addr, + struct sockaddr *sock_addr, socklen_t *addrlen) +{ + switch (wasi_addr->kind) { + case IPv4: + { + struct sockaddr_in sock_addr_in; + uint32_t s_addr; + + memset(&sock_addr_in, 0, sizeof(sock_addr_in)); + + s_addr = (wasi_addr->addr.ip4.addr.n0 << 24) + | (wasi_addr->addr.ip4.addr.n1 << 16) + | (wasi_addr->addr.ip4.addr.n2 << 8) + | wasi_addr->addr.ip4.addr.n3; + + sock_addr_in.sin_family = AF_INET; + sock_addr_in.sin_addr.s_addr = htonl(s_addr); + sock_addr_in.sin_port = htons(wasi_addr->addr.ip4.port); + memcpy(sock_addr, &sock_addr_in, sizeof(sock_addr_in)); + + *addrlen = sizeof(sock_addr_in); + break; + } + case IPv6: + { + struct sockaddr_in6 sock_addr_in6; + + memset(&sock_addr_in6, 0, sizeof(sock_addr_in6)); + + uint16_t *addr_buf = (uint16_t *)sock_addr_in6.sin6_addr.s6_addr; + + addr_buf[0] = htons(wasi_addr->addr.ip6.addr.n0); + addr_buf[1] = htons(wasi_addr->addr.ip6.addr.n1); + addr_buf[2] = htons(wasi_addr->addr.ip6.addr.n2); + addr_buf[3] = htons(wasi_addr->addr.ip6.addr.n3); + addr_buf[4] = htons(wasi_addr->addr.ip6.addr.h0); + addr_buf[5] = htons(wasi_addr->addr.ip6.addr.h1); + addr_buf[6] = htons(wasi_addr->addr.ip6.addr.h2); + addr_buf[7] = htons(wasi_addr->addr.ip6.addr.h3); + + sock_addr_in6.sin6_family = AF_INET6; + sock_addr_in6.sin6_port = htons(wasi_addr->addr.ip6.port); + memcpy(sock_addr, &sock_addr_in6, sizeof(sock_addr_in6)); + + *addrlen = sizeof(sock_addr_in6); + break; + } + default: + return __WASI_ERRNO_AFNOSUPPORT; + } + return __WASI_ERRNO_SUCCESS; +} + +int +accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + __wasi_addr_t wasi_addr; + __wasi_fd_t new_sockfd; + __wasi_errno_t error; + + memset(&wasi_addr, 0, sizeof(wasi_addr)); + + error = __wasi_sock_accept(sockfd, 0, &new_sockfd); + HANDLE_ERROR(error) + + if (getpeername(new_sockfd, addr, addrlen) == -1) { + return -1; + } + + return new_sockfd; +} + +int +bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + __wasi_addr_t wasi_addr; + __wasi_errno_t error; + + memset(&wasi_addr, 0, sizeof(wasi_addr)); + + error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); + HANDLE_ERROR(error) + + error = __wasi_sock_bind(sockfd, &wasi_addr); + HANDLE_ERROR(error) + + return 0; +} + +int +connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + __wasi_addr_t wasi_addr; + __wasi_errno_t error; + + memset(&wasi_addr, 0, sizeof(wasi_addr)); + + if (NULL == addr) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + error = sockaddr_to_wasi_addr(addr, addrlen, &wasi_addr); + HANDLE_ERROR(error) + + error = __wasi_sock_connect(sockfd, &wasi_addr); + HANDLE_ERROR(error) + + return 0; +} + +int +listen(int sockfd, int backlog) +{ + __wasi_errno_t error = __wasi_sock_listen(sockfd, backlog); + HANDLE_ERROR(error) + return 0; +} + +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + // Prepare input parameters. + __wasi_iovec_t *ri_data = NULL; + size_t i = 0; + size_t ro_datalen = 0; + __wasi_roflags_t ro_flags = 0; + + if (NULL == msg) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + // Validate flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + // __wasi_ciovec_t -> struct iovec + if (!(ri_data = (__wasi_iovec_t *)malloc(sizeof(__wasi_iovec_t) + * msg->msg_iovlen))) { + HANDLE_ERROR(__WASI_ERRNO_NOMEM) + } + + for (i = 0; i < msg->msg_iovlen; i++) { + ri_data[i].buf = (uint8_t *)msg->msg_iov[i].iov_base; + ri_data[i].buf_len = msg->msg_iov[i].iov_len; + } + + // Perform system call. + __wasi_errno_t error = __wasi_sock_recv(sockfd, ri_data, msg->msg_iovlen, 0, + &ro_datalen, &ro_flags); + free(ri_data); + HANDLE_ERROR(error) + + return ro_datalen; +} + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags) +{ + // Prepare input parameters. + __wasi_ciovec_t *si_data = NULL; + size_t so_datalen = 0; + size_t i = 0; + + if (NULL == msg) { + HANDLE_ERROR(__WASI_ERRNO_INVAL) + } + + // This implementation does not support any flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + // struct iovec -> __wasi_ciovec_t + if (!(si_data = (__wasi_ciovec_t *)malloc(sizeof(__wasi_ciovec_t) + * msg->msg_iovlen))) { + HANDLE_ERROR(__WASI_ERRNO_NOMEM) + } + + for (i = 0; i < msg->msg_iovlen; i++) { + si_data[i].buf = (uint8_t *)msg->msg_iov[i].iov_base; + si_data[i].buf_len = msg->msg_iov[i].iov_len; + } + + // Perform system call. + __wasi_errno_t error = + __wasi_sock_send(sockfd, si_data, msg->msg_iovlen, 0, &so_datalen); + free(si_data); + HANDLE_ERROR(error) + + return so_datalen; +} + +ssize_t +sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) +{ + // Prepare input parameters. + __wasi_ciovec_t iov = { .buf = (uint8_t *)buf, .buf_len = len }; + uint32_t so_datalen = 0; + __wasi_addr_t wasi_addr; + __wasi_errno_t error; + size_t si_data_len = 1; + __wasi_siflags_t si_flags = 0; + + // This implementation does not support any flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + error = sockaddr_to_wasi_addr(dest_addr, addrlen, &wasi_addr); + HANDLE_ERROR(error); + + // Perform system call. + error = __wasi_sock_send_to(sockfd, &iov, si_data_len, si_flags, &wasi_addr, + &so_datalen); + HANDLE_ERROR(error) + + return so_datalen; +} + +ssize_t +recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + // Prepare input parameters. + __wasi_ciovec_t iov = { .buf = (uint8_t *)buf, .buf_len = len }; + uint32_t so_datalen = 0; + __wasi_addr_t wasi_addr; + __wasi_errno_t error; + size_t si_data_len = 1; + __wasi_siflags_t si_flags = 0; + + // This implementation does not support any flags. + if (flags != 0) { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + if (!src_addr) { + return recv(sockfd, buf, len, flags); + } + + // Perform system call. + error = __wasi_sock_recv_from(sockfd, &iov, si_data_len, si_flags, + &wasi_addr, &so_datalen); + HANDLE_ERROR(error); + + error = wasi_addr_to_sockaddr(&wasi_addr, src_addr, addrlen); + HANDLE_ERROR(error); + + return so_datalen; +} + +int +socket(int domain, int type, int protocol) +{ + // the stub of address pool fd + __wasi_fd_t poolfd = -1; + __wasi_fd_t sockfd; + __wasi_errno_t error; + __wasi_address_family_t af; + __wasi_sock_type_t socktype; + + if (AF_INET == domain) { + af = INET4; + } + else if (AF_INET6 == domain) { + af = INET6; + } + else { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + if (SOCK_DGRAM == type) { + socktype = SOCKET_DGRAM; + } + else if (SOCK_STREAM == type) { + socktype = SOCKET_STREAM; + } + else { + HANDLE_ERROR(__WASI_ERRNO_NOPROTOOPT) + } + + error = __wasi_sock_open(poolfd, af, socktype, &sockfd); + HANDLE_ERROR(error) + + return sockfd; +} + +int +getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + __wasi_addr_t wasi_addr; + __wasi_errno_t error; + + memset(&wasi_addr, 0, sizeof(wasi_addr)); + + error = __wasi_sock_addr_local(sockfd, &wasi_addr); + HANDLE_ERROR(error) + + error = wasi_addr_to_sockaddr(&wasi_addr, addr, addrlen); + HANDLE_ERROR(error) + + return 0; +} + +int +getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) +{ + __wasi_addr_t wasi_addr; + __wasi_errno_t error; + + memset(&wasi_addr, 0, sizeof(wasi_addr)); + + error = __wasi_sock_addr_remote(sockfd, &wasi_addr); + HANDLE_ERROR(error) + + error = wasi_addr_to_sockaddr(&wasi_addr, addr, addrlen); + HANDLE_ERROR(error) + + return 0; +} + +struct aibuf { + struct addrinfo ai; + union sa { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa; +}; + +static __wasi_errno_t +addrinfo_hints_to_wasi_hints(const struct addrinfo *hints, + __wasi_addr_info_hints_t *wasi_hints) +{ + if (hints) { + wasi_hints->hints_enabled = 1; + + switch (hints->ai_family) { + case AF_INET: + wasi_hints->family = INET4; + break; + case AF_INET6: + wasi_hints->family = INET6; + break; + case AF_UNSPEC: + wasi_hints->family = INET_UNSPEC; + break; + default: + return __WASI_ERRNO_AFNOSUPPORT; + } + switch (hints->ai_socktype) { + case SOCK_STREAM: + wasi_hints->type = SOCKET_STREAM; + break; + case SOCK_DGRAM: + wasi_hints->type = SOCKET_DGRAM; + break; + case 0: + wasi_hints->type = SOCKET_ANY; + default: + return __WASI_ERRNO_NOTSUP; + } + + if (hints->ai_protocol != 0) { + return __WASI_ERRNO_NOTSUP; + } + + if (hints->ai_flags != 0) { + return __WASI_ERRNO_NOTSUP; + } + } + else { + wasi_hints->hints_enabled = 0; + } + + return __WASI_ERRNO_SUCCESS; +} + +static __wasi_errno_t +wasi_addr_info_to_addr_info(const __wasi_addr_info_t *addr_info, + struct addrinfo *ai) +{ + ai->ai_socktype = + addr_info->type == SOCKET_DGRAM ? SOCK_DGRAM : SOCK_STREAM; + ai->ai_protocol = 0; + ai->ai_canonname = NULL; + + if (addr_info->addr.kind == IPv4) { + ai->ai_family = AF_INET; + ai->ai_addrlen = sizeof(struct sockaddr_in); + } + else { + ai->ai_family = AF_INET6; + ai->ai_addrlen = sizeof(struct sockaddr_in6); + } + + return wasi_addr_to_sockaddr(&addr_info->addr, ai->ai_addr, + &ai->ai_addrlen); // TODO err handling +} + +int +getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, + struct addrinfo **res) +{ + __wasi_addr_info_hints_t wasi_hints; + __wasi_addr_info_t *addr_info = NULL; + __wasi_size_t addr_info_size, i; + __wasi_size_t max_info_size = 16; + __wasi_errno_t error; + struct aibuf *aibuf_res; + + error = addrinfo_hints_to_wasi_hints(hints, &wasi_hints); + GAI_HANDLE_ERROR(error) + + do { + if (addr_info) + free(addr_info); + + addr_info_size = max_info_size; + addr_info = (__wasi_addr_info_t *)malloc(addr_info_size + * sizeof(__wasi_addr_info_t)); + + if (!addr_info) { + return EAI_MEMORY; + } + + error = __wasi_sock_addr_resolve(node, service == NULL ? "" : service, + &wasi_hints, addr_info, addr_info_size, + &max_info_size); + if (error != __WASI_ERRNO_SUCCESS) { + free(addr_info); + GAI_HANDLE_ERROR(error); + } + } while (max_info_size > addr_info_size); + + addr_info_size = max_info_size; + if (addr_info_size == 0) { + free(addr_info); + return EAI_NONAME; + } + + aibuf_res = + (struct aibuf *)calloc(1, addr_info_size * sizeof(struct aibuf)); + if (!aibuf_res) { + free(addr_info); + return EAI_MEMORY; + } + + *res = &aibuf_res[0].ai; + + for (i = 0; i < addr_info_size; i++) { + struct addrinfo *ai = &aibuf_res[i].ai; + ai->ai_addr = (struct sockaddr *)&aibuf_res[i].sa; + + error = wasi_addr_info_to_addr_info(&addr_info[i], ai); + if (error != __WASI_ERRNO_SUCCESS) { + free(addr_info); + free(aibuf_res); + GAI_HANDLE_ERROR(error) + } + ai->ai_next = i == addr_info_size - 1 ? NULL : &aibuf_res[i + 1].ai; + } + + free(addr_info); + + return 0; +} + +void +freeaddrinfo(struct addrinfo *res) +{ + /* res is a pointer to a first field in the first element + * of aibuf array allocated in getaddrinfo, therefore this call + * frees the memory of the entire array. */ + free(res); +} + +const char * +gai_strerror(int code) +{ + switch (code) { +#define ERR(a) \ + case a: \ + return #a + ERR(EAI_AGAIN); + ERR(EAI_BADFLAGS); + ERR(EAI_FAIL); + ERR(EAI_FAMILY); + ERR(EAI_MEMORY); + ERR(EAI_NONAME); + ERR(EAI_OVERFLOW); + ERR(EAI_SERVICE); + ERR(EAI_SOCKTYPE); + ERR(EAI_SYSTEM); +#undef ERR + } + return "Unknown error"; +} + +static struct timeval +time_us_to_timeval(uint64_t time_us) +{ + struct timeval tv; + tv.tv_sec = time_us / 1000000UL; + tv.tv_usec = time_us % 1000000UL; + return tv; +} + +static uint64_t +timeval_to_time_us(struct timeval tv) +{ + return (tv.tv_sec * 1000000UL) + tv.tv_usec; +} + +static int +get_sol_socket_option(int sockfd, int optname, void *__restrict optval, + socklen_t *__restrict optlen) +{ + __wasi_errno_t error; + uint64_t timeout_us; + bool is_linger_enabled; + int linger_s; + __wasi_fdstat_t sb; + + switch (optname) { + case SO_RCVTIMEO: + assert(*optlen == sizeof(struct timeval)); + error = __wasi_sock_get_recv_timeout(sockfd, &timeout_us); + HANDLE_ERROR(error); + *(struct timeval *)optval = time_us_to_timeval(timeout_us); + return 0; + case SO_SNDTIMEO: + assert(*optlen == sizeof(struct timeval)); + error = __wasi_sock_get_send_timeout(sockfd, &timeout_us); + HANDLE_ERROR(error); + *(struct timeval *)optval = time_us_to_timeval(timeout_us); + return 0; + case SO_SNDBUF: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_send_buf_size(sockfd, (size_t *)optval); + HANDLE_ERROR(error); + return 0; + case SO_RCVBUF: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_recv_buf_size(sockfd, (size_t *)optval); + HANDLE_ERROR(error); + return 0; + case SO_KEEPALIVE: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_keep_alive(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case SO_REUSEADDR: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_reuse_addr(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case SO_REUSEPORT: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_reuse_port(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case SO_LINGER: + assert(*optlen == sizeof(struct linger)); + error = + __wasi_sock_get_linger(sockfd, &is_linger_enabled, &linger_s); + HANDLE_ERROR(error); + ((struct linger *)optval)->l_onoff = (int)is_linger_enabled; + ((struct linger *)optval)->l_linger = linger_s; + return 0; + case SO_BROADCAST: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_broadcast(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case SO_TYPE: + assert(*optlen == sizeof(int)); + error = __wasi_fd_fdstat_get(sockfd, &sb); + HANDLE_ERROR(error); + switch (sb.fs_filetype) { + case __WASI_FILETYPE_SOCKET_DGRAM: + *(int *)optval = SOCK_DGRAM; + break; + case __WASI_FILETYPE_SOCKET_STREAM: + *(int *)optval = SOCK_STREAM; + break; + default: + errno = __WASI_ERRNO_NOTSOCK; + return -1; + } + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +static int +get_ipproto_tcp_option(int sockfd, int optname, void *__restrict optval, + socklen_t *__restrict optlen) +{ + __wasi_errno_t error; + switch (optname) { + case TCP_KEEPIDLE: + assert(*optlen == sizeof(uint32_t)); + error = __wasi_sock_get_tcp_keep_idle(sockfd, (uint32_t *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_KEEPINTVL: + assert(*optlen == sizeof(uint32_t)); + error = __wasi_sock_get_tcp_keep_intvl(sockfd, (uint32_t *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_FASTOPEN_CONNECT: + assert(*optlen == sizeof(int)); + error = + __wasi_sock_get_tcp_fastopen_connect(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_NODELAY: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_tcp_no_delay(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_QUICKACK: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_tcp_quick_ack(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +static int +get_ipproto_ip_option(int sockfd, int optname, void *__restrict optval, + socklen_t *__restrict optlen) +{ + __wasi_errno_t error; + + switch (optname) { + case IP_MULTICAST_LOOP: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_ip_multicast_loop(sockfd, false, + (bool *)optval); + HANDLE_ERROR(error); + return 0; + case IP_TTL: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_ip_ttl(sockfd, (uint8_t *)optval); + HANDLE_ERROR(error); + return 0; + case IP_MULTICAST_TTL: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_ip_multicast_ttl(sockfd, (uint8_t *)optval); + HANDLE_ERROR(error); + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +static int +get_ipproto_ipv6_option(int sockfd, int optname, void *__restrict optval, + socklen_t *__restrict optlen) +{ + __wasi_errno_t error; + + switch (optname) { + case IPV6_V6ONLY: + assert(*optlen == sizeof(int)); + error = __wasi_sock_get_ipv6_only(sockfd, (bool *)optval); + HANDLE_ERROR(error); + return 0; + case IPV6_MULTICAST_LOOP: + assert(*optlen == sizeof(int)); + error = + __wasi_sock_get_ip_multicast_loop(sockfd, true, (bool *)optval); + HANDLE_ERROR(error); + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +int +getsockopt(int sockfd, int level, int optname, void *__restrict optval, + socklen_t *__restrict optlen) +{ + __wasi_errno_t error; + + switch (level) { + case SOL_SOCKET: + return get_sol_socket_option(sockfd, optname, optval, optlen); + case IPPROTO_TCP: + return get_ipproto_tcp_option(sockfd, optname, optval, optlen); + case IPPROTO_IP: + return get_ipproto_ip_option(sockfd, optname, optval, optlen); + case IPPROTO_IPV6: + return get_ipproto_ipv6_option(sockfd, optname, optval, optlen); + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +static int +set_sol_socket_option(int sockfd, int optname, const void *optval, + socklen_t optlen) +{ + __wasi_errno_t error; + uint64_t timeout_us; + + switch (optname) { + case SO_RCVTIMEO: + { + assert(optlen == sizeof(struct timeval)); + timeout_us = timeval_to_time_us(*(struct timeval *)optval); + error = __wasi_sock_set_recv_timeout(sockfd, timeout_us); + HANDLE_ERROR(error); + return 0; + } + case SO_SNDTIMEO: + { + assert(optlen == sizeof(struct timeval)); + timeout_us = timeval_to_time_us(*(struct timeval *)optval); + error = __wasi_sock_set_send_timeout(sockfd, timeout_us); + HANDLE_ERROR(error); + return 0; + } + case SO_SNDBUF: + { + assert(optlen == sizeof(int)); + error = __wasi_sock_set_send_buf_size(sockfd, *(size_t *)optval); + HANDLE_ERROR(error); + return 0; + } + case SO_RCVBUF: + { + assert(optlen == sizeof(int)); + error = __wasi_sock_set_recv_buf_size(sockfd, *(size_t *)optval); + HANDLE_ERROR(error); + return 0; + } + case SO_KEEPALIVE: + { + assert(optlen == sizeof(int)); + error = __wasi_sock_set_keep_alive(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + } + case SO_REUSEADDR: + { + assert(optlen == sizeof(int)); + error = __wasi_sock_set_reuse_addr(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + } + case SO_REUSEPORT: + { + assert(optlen == sizeof(int)); + error = __wasi_sock_set_reuse_port(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + } + case SO_LINGER: + { + assert(optlen == sizeof(struct linger)); + struct linger *linger_opt = ((struct linger *)optval); + error = __wasi_sock_set_linger(sockfd, (bool)linger_opt->l_onoff, + linger_opt->l_linger); + HANDLE_ERROR(error); + return 0; + } + case SO_BROADCAST: + { + assert(optlen == sizeof(int)); + error = __wasi_sock_set_broadcast(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + } + default: + { + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } + } +} + +static int +set_ipproto_tcp_option(int sockfd, int optname, const void *optval, + socklen_t optlen) +{ + __wasi_errno_t error; + + switch (optname) { + case TCP_NODELAY: + assert(optlen == sizeof(int)); + error = __wasi_sock_set_tcp_no_delay(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_KEEPIDLE: + assert(optlen == sizeof(uint32_t)); + error = __wasi_sock_set_tcp_keep_idle(sockfd, *(uint32_t *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_KEEPINTVL: + assert(optlen == sizeof(uint32_t)); + error = __wasi_sock_set_tcp_keep_intvl(sockfd, *(uint32_t *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_FASTOPEN_CONNECT: + assert(optlen == sizeof(int)); + error = + __wasi_sock_set_tcp_fastopen_connect(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + case TCP_QUICKACK: + assert(optlen == sizeof(int)); + error = __wasi_sock_set_tcp_quick_ack(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +static int +set_ipproto_ip_option(int sockfd, int optname, const void *optval, + socklen_t optlen) +{ + __wasi_errno_t error; + __wasi_addr_ip_t imr_multiaddr; + struct ip_mreq *ip_mreq_opt; + + switch (optname) { + case IP_MULTICAST_LOOP: + assert(optlen == sizeof(int)); + error = __wasi_sock_set_ip_multicast_loop(sockfd, false, + *(bool *)optval); + HANDLE_ERROR(error); + return 0; + case IP_ADD_MEMBERSHIP: + assert(optlen == sizeof(struct ip_mreq)); + ip_mreq_opt = (struct ip_mreq *)optval; + imr_multiaddr.kind = IPv4; + ipv4_addr_to_wasi_ip4_addr(ip_mreq_opt->imr_multiaddr.s_addr, + &imr_multiaddr.addr.ip4); + error = __wasi_sock_set_ip_add_membership( + sockfd, &imr_multiaddr, ip_mreq_opt->imr_interface.s_addr); + HANDLE_ERROR(error); + return 0; + case IP_DROP_MEMBERSHIP: + assert(optlen == sizeof(struct ip_mreq)); + ip_mreq_opt = (struct ip_mreq *)optval; + imr_multiaddr.kind = IPv4; + ipv4_addr_to_wasi_ip4_addr(ip_mreq_opt->imr_multiaddr.s_addr, + &imr_multiaddr.addr.ip4); + error = __wasi_sock_set_ip_drop_membership( + sockfd, &imr_multiaddr, ip_mreq_opt->imr_interface.s_addr); + HANDLE_ERROR(error); + return 0; + case IP_TTL: + assert(optlen == sizeof(int)); + error = __wasi_sock_set_ip_ttl(sockfd, *(uint8_t *)optval); + HANDLE_ERROR(error); + return 0; + case IP_MULTICAST_TTL: + assert(optlen == sizeof(int)); + error = + __wasi_sock_set_ip_multicast_ttl(sockfd, *(uint8_t *)optval); + HANDLE_ERROR(error); + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +static int +set_ipproto_ipv6_option(int sockfd, int optname, const void *optval, + socklen_t optlen) +{ + __wasi_errno_t error; + struct ipv6_mreq *ipv6_mreq_opt; + __wasi_addr_ip_t imr_multiaddr; + + switch (optname) { + case IPV6_V6ONLY: + assert(optlen == sizeof(int)); + error = __wasi_sock_set_ipv6_only(sockfd, *(bool *)optval); + HANDLE_ERROR(error); + return 0; + case IPV6_MULTICAST_LOOP: + assert(optlen == sizeof(int)); + error = __wasi_sock_set_ip_multicast_loop(sockfd, true, + *(bool *)optval); + HANDLE_ERROR(error); + return 0; + case IPV6_JOIN_GROUP: + assert(optlen == sizeof(struct ipv6_mreq)); + ipv6_mreq_opt = (struct ipv6_mreq *)optval; + imr_multiaddr.kind = IPv6; + ipv6_addr_to_wasi_ipv6_addr( + (uint16_t *)ipv6_mreq_opt->ipv6mr_multiaddr.s6_addr, + &imr_multiaddr.addr.ip6); + error = __wasi_sock_set_ip_add_membership( + sockfd, &imr_multiaddr, ipv6_mreq_opt->ipv6mr_interface); + HANDLE_ERROR(error); + return 0; + case IPV6_LEAVE_GROUP: + assert(optlen == sizeof(struct ipv6_mreq)); + ipv6_mreq_opt = (struct ipv6_mreq *)optval; + imr_multiaddr.kind = IPv6; + ipv6_addr_to_wasi_ipv6_addr( + (uint16_t *)ipv6_mreq_opt->ipv6mr_multiaddr.s6_addr, + &imr_multiaddr.addr.ip6); + error = __wasi_sock_set_ip_drop_membership( + sockfd, &imr_multiaddr, ipv6_mreq_opt->ipv6mr_interface); + HANDLE_ERROR(error); + return 0; + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} + +int +setsockopt(int sockfd, int level, int optname, const void *optval, + socklen_t optlen) +{ + __wasi_errno_t error; + + switch (level) { + case SOL_SOCKET: + return set_sol_socket_option(sockfd, optname, optval, optlen); + case IPPROTO_TCP: + return set_ipproto_tcp_option(sockfd, optname, optval, optlen); + case IPPROTO_IP: + return set_ipproto_ip_option(sockfd, optname, optval, optlen); + case IPPROTO_IPV6: + return set_ipproto_ipv6_option(sockfd, optname, optval, optlen); + default: + error = __WASI_ERRNO_NOTSUP; + HANDLE_ERROR(error); + return 0; + } +} 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 0000000000..24f5ee6763 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set -eo pipefail +CC="${CC:=/opt/wasi-sdk/bin/clang}" +files=("tcp_udp.c" "nslookup.c") + +for file in "${files[@]}" +do + echo $file + $CC \ + --target=wasm32-wasi-threads \ + -I../inc \ + ../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/manifest.json b/core/iwasm/libraries/lib-socket/test/manifest.json new file mode 100644 index 0000000000..b0afd1d6b6 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/manifest.json @@ -0,0 +1,3 @@ +{ + "name": "WAMR lib-socket tests" +} \ 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 0000000000..2e42ef8458 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/nslookup.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 + */ + +#include +#include +#include +#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); + if (ret != 0) { + if (ret == EAI_SYSTEM) { + fprintf(stderr, "getaddrinfo failed: %s (%s)\n", gai_strerror(ret), + strerror(errno)); + } + else { + fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(ret)); + } + } + 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); +} + +void * +test_nslookup_mt(void *params) +{ + int *af = (int *)params; + test_nslookup(*af); + return NULL; +} + +int +main() +{ + int afs[] = { AF_INET, AF_INET6 }; + + for (int i = 0; i < sizeof(afs) / sizeof(afs[0]); i++) { + pthread_t th; + + printf("Testing %d in main thread...\n", afs[i]); + test_nslookup(afs[i]); + printf("Testing %d in a new thread...\n", afs[i]); + pthread_create(&th, NULL, test_nslookup_mt, &afs[i]); + pthread_join(th, NULL); + } + + 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 0000000000..0ed0312507 --- /dev/null +++ b/core/iwasm/libraries/lib-socket/test/tcp_udp.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#include +#include +#endif +#include +#include +#include + +#define SERVER_MSG "Message from server." +#define PORT 8989 + +pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + +int server_init_complete = 0; + +typedef struct { + struct sockaddr_storage addr; + socklen_t addr_len; + int sock; + int protocol; +} socket_info_t; + +void +wait_for_server(int wait_time_seconds) +{ + int res = 0; + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += wait_time_seconds; + + pthread_mutex_lock(&mut); + while (server_init_complete == 0) { + res = pthread_cond_timedwait(&cond, &mut, &ts); + if (res == ETIMEDOUT) + break; + } + pthread_mutex_unlock(&mut); + + assert(res == 0); +} + +void +notify_server_started() +{ + pthread_mutex_lock(&mut); + server_init_complete = 1; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mut); +} + +socket_info_t +init_socket_addr(int family, int protocol) +{ + socket_info_t info; + + info.sock = socket(family, protocol, 0); + assert(info.sock != -1); + info.protocol = protocol; + + memset(&info.addr, 0, sizeof(info.addr)); + + if (family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)&info.addr; + addr->sin_family = AF_INET; + addr->sin_port = htons(PORT); + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + info.addr_len = sizeof(struct sockaddr_in); + } + else if (family == AF_INET6) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&info.addr; + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(PORT); + addr->sin6_addr = in6addr_loopback; + info.addr_len = sizeof(struct sockaddr_in6); + } + + return info; +} + +void * +server(void *arg) +{ + char buffer[sizeof(SERVER_MSG) + 1] = { 0 }; + struct sockaddr_storage client_addr; + socket_info_t *info = (socket_info_t *)arg; + struct sockaddr *server_addr = (struct sockaddr *)&info->addr; + int server_sock = info->sock; + + int optval = 1; + assert(setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &optval, + sizeof(optval)) + == 0); + + assert(bind(server_sock, server_addr, info->addr_len) == 0); + + if (info->protocol == SOCK_STREAM) + listen(server_sock, 1); + notify_server_started(); + + socklen_t addr_size = info->addr_len; + if (info->protocol == SOCK_STREAM) { + int client_sock = + accept(server_sock, (struct sockaddr *)&client_addr, &addr_size); + assert(client_sock >= 0); + assert(recv(client_sock, buffer, sizeof(buffer), 0) > 0); + strcpy(buffer, SERVER_MSG); + assert(send(client_sock, buffer, sizeof(buffer), 0) > 0); + assert(recv(client_sock, buffer, sizeof(buffer), 0) > 0); + } + else { + assert(recvfrom(server_sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&client_addr, &addr_size) + > 0); + strcpy(buffer, SERVER_MSG); + assert(sendto(server_sock, buffer, strlen(buffer), 0, + (struct sockaddr *)&client_addr, addr_size) + > 0); + assert(recvfrom(server_sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&client_addr, &addr_size) + > 0); + } + assert(close(server_sock) == 0); + + return NULL; +} + +void * +client(void *arg) +{ + char buffer[sizeof(SERVER_MSG) + 1]; + socket_info_t *info = (socket_info_t *)arg; + int sock = info->sock; + struct sockaddr *addr = (struct sockaddr *)&info->addr; + + wait_for_server(1); + + if (info->protocol == SOCK_STREAM) { + assert(connect(sock, addr, info->addr_len) != -1); + } + + assert(sendto(sock, "open", strlen("open"), 0, addr, info->addr_len) > 0); + assert(recv(sock, buffer, sizeof(buffer), 0) > 0); + assert(strncmp(buffer, SERVER_MSG, strlen(SERVER_MSG)) == 0); + assert(sendto(sock, "close", sizeof("close"), 0, addr, info->addr_len) > 0); + assert(close(sock) == 0); + + return NULL; +} + +void +test_protocol(int family, int protocol) +{ + pthread_t server_thread, client_thread; + socket_info_t server_info = init_socket_addr(family, protocol); + socket_info_t client_info = init_socket_addr(family, protocol); + + printf("Testing address family: %d protocol: %d\n", family, protocol); + + server_init_complete = 0; + + assert(pthread_create(&server_thread, NULL, server, (void *)&server_info) + == 0); + assert(pthread_create(&client_thread, NULL, client, (void *)&client_info) + == 0); + assert(pthread_join(server_thread, NULL) == 0); + assert(pthread_join(client_thread, NULL) == 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; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/SConscript b/core/iwasm/libraries/lib-wasi-threads/SConscript new file mode 100755 index 0000000000..c4d62e3db3 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/SConscript @@ -0,0 +1,15 @@ +# +# Copyright 2024 Sony Semiconductor Solutions Corporation. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('iwasm_lib_wasi_threads', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') 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 0000000000..54d2ba902c --- /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 0000000000..65b75ac28e --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/lib_wasi_threads_wrapper.c @@ -0,0 +1,180 @@ +/* + * 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(void) +{ + 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; + struct InstantiationArgs2 args; + + bh_assert(module); + bh_assert(module_inst); + + stack_size = ((WASMModuleInstance *)module_inst)->default_wasm_stack_size; + + wasm_runtime_instantiation_args_set_defaults(&args); + wasm_runtime_instantiation_args_set_default_stack_size(&args, stack_size); + if (!(new_module_inst = wasm_runtime_instantiate_internal( + module, module_inst, exec_env, &args, NULL, 0))) + return -1; + + wasm_runtime_set_custom_data_internal( + new_module_inst, wasm_runtime_get_custom_data(module_inst)); + + if (!(wasm_cluster_dup_c_api_imports(new_module_inst, module_inst))) + goto thread_preparation_fail; + + wasm_native_inherit_contexts(new_module_inst, module_inst); + + start_func = + wasm_runtime_lookup_function(new_module_inst, THREAD_START_FUNCTION); + 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, 0, 0, + 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/stress-test/build.sh b/core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh new file mode 100755 index 0000000000..1ea95cb97c --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/build.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# +# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +set -eo pipefail +CC=${CC:=/opt/wasi-sdk/bin/clang} +WAMR_DIR=../../../../.. + +show_usage() { + echo "Usage: $0 [--sysroot PATH_TO_SYSROOT]" + echo "--sysroot PATH_TO_SYSROOT specify to build with custom sysroot for wasi-libc" +} + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + --sysroot) + sysroot_path="$2" + shift + shift + ;; + --help) + show_usage + exit + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +rm -rf *.wasm +rm -rf *.aot + +for test_c in *.c; do + test_wasm="$(basename $test_c .c).wasm" + + if [[ -n "$sysroot_path" ]]; then + if [ ! -d "$sysroot_path" ]; then + echo "Directory $sysroot_path doesn't exist. Aborting" + exit 1 + fi + sysroot_command="--sysroot $sysroot_path" + fi + + echo "Compiling $test_c to $test_wasm" + $CC \ + -target wasm32-wasi-threads \ + -O2 \ + -Wall \ + -pthread \ + -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 \ + -Wl,--export=test \ + $sysroot_command \ + $test_c -o $test_wasm +done diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/errorcheck_mutex_stress_test.c b/core/iwasm/libraries/lib-wasi-threads/stress-test/errorcheck_mutex_stress_test.c new file mode 100644 index 0000000000..946e8bd634 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/errorcheck_mutex_stress_test.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "mutex_common.h" + +void +test() +{ + pthread_mutex_t mutex; + + // Set mutex type to errorcheck. This type provides some additional checks + // (for example returns EDEADLK instead of deadlocking in some cases) + pthread_mutexattr_t mutex_attr; + pthread_mutexattr_init(&mutex_attr); + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_ERRORCHECK); + + pthread_mutex_init(&mutex, &mutex_attr); + pthread_mutexattr_destroy(&mutex_attr); + + run_common_tests(&mutex); + fprintf(stderr, "Errorcheck mutex test is completed\n"); + pthread_mutex_destroy(&mutex); +} + +int +main() +{ + test(); + return 0; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/manifest.json b/core/iwasm/libraries/lib-wasi-threads/stress-test/manifest.json new file mode 100644 index 0000000000..bb91ad083e --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/manifest.json @@ -0,0 +1,3 @@ +{ + "name": "lib-wasi-threads stress tests" +} diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h b/core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h new file mode 100644 index 0000000000..d57ff7d50e --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/mutex_common.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef MUTEX_COMMON_H +#define MUTEX_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum Constants { + NUM_ITER = 250000, + NUM_THREADS = 12, + NUM_RETRY = 8, + RETRY_SLEEP_TIME_US = 1000, +}; + +// We're counting how many times each thread was called using this array +// Main thread is also counted here so we need to make arrays bigger +typedef struct { + int tids[NUM_THREADS + 1]; + int calls[NUM_THREADS + 1]; +} StatCollector; + +typedef struct { + pthread_mutex_t *mutex; + StatCollector stat; + int counter; + bool is_sleeping; +} MutexCounter; + +// This enum defines whether thread should sleep to increase contention +enum SleepState { + NON_SLEEP = 0, + SLEEP = 1, +}; + +void +mutex_counter_init(MutexCounter *mutex_counter, pthread_mutex_t *mutex, + enum SleepState is_sleeping) +{ + memset(mutex_counter, 0, sizeof(*mutex_counter)); + mutex_counter->mutex = mutex; + mutex_counter->is_sleeping = is_sleeping; +} + +// This function spawns the thread using exponential retries if it receives +// EAGAIN +static inline void +spawn_thread(pthread_t *tid, void *func, void *arg) +{ + int status_code = -1; + int timeout_us = RETRY_SLEEP_TIME_US; + for (int tries = 0; status_code != 0 && tries < NUM_RETRY; ++tries) { + status_code = pthread_create(tid, NULL, (void *(*)(void *))func, arg); + assert(status_code == 0 || status_code == EAGAIN); + if (status_code == EAGAIN) { + usleep(timeout_us); + timeout_us *= 2; + } + } + + assert(status_code == 0 && "Thread creation should succeed"); +} + +// This function adds tid to our stat +static inline void +add_to_stat(StatCollector *stat, int tid) +{ + int tid_num = 0; + for (; tid_num < NUM_THREADS + 1 && stat->tids[tid_num] != 0; ++tid_num) { + if (stat->tids[tid_num] == tid) { + stat->calls[tid_num]++; + return; + } + } + + assert(tid_num < NUM_THREADS + 1); + stat->tids[tid_num] = tid; + stat->calls[tid_num] = 1; +} + +// This function prints number of calls by TID +static inline void +print_stat(StatCollector *stat) +{ + fprintf(stderr, "Thread calls count by TID\n"); + for (int i = 0; i < NUM_THREADS + 1; ++i) { + if (stat->tids[i] != 0) { + fprintf(stderr, "TID: %d; Calls: %d\n", stat->tids[i], + stat->calls[i]); + } + } +} + +// This function is run by the threads, it increases counter in a loop and then +// sleeps after unlocking the mutex to provide better contention +static inline void * +inc_shared_variable(void *arg) +{ + MutexCounter *mutex_counter = (MutexCounter *)(arg); + int sleep_us = 0; + while (!pthread_mutex_lock(mutex_counter->mutex) + && mutex_counter->counter < NUM_ITER) { + mutex_counter->counter++; + add_to_stat(&mutex_counter->stat, (int)(pthread_self())); + if (mutex_counter->is_sleeping) { + sleep_us = rand() % 1000; + } + + assert(pthread_mutex_unlock(mutex_counter->mutex) == 0 + && "Should be able to unlock a mutex"); + if (mutex_counter->is_sleeping) { + usleep(sleep_us); + } + } + + assert(mutex_counter->counter == NUM_ITER); + assert(pthread_mutex_unlock(mutex_counter->mutex) == 0 + && "Should be able to unlock the mutex after test execution"); + + return NULL; +} + +// Locking and unlocking a mutex in a single thread. +static inline void * +same_thread_lock_unlock_test(void *mutex) +{ + for (int i = 0; i < NUM_ITER; ++i) { + assert(pthread_mutex_lock(mutex) == 0 + && "Main thread should be able to lock a mutex"); + assert(pthread_mutex_unlock(mutex) == 0 + && "Main thread should be able to unlock a mutex"); + } + + return NULL; +} + +// This function spawns a thread that locks and unlocks a mutex `NUM_ITER` times +// in a row +static inline void +same_non_main_thread_lock_unlock_test(pthread_mutex_t *mutex) +{ + pthread_t tid = 0; + spawn_thread(&tid, same_thread_lock_unlock_test, mutex); + + assert(tid != 0 && "TID can't be 0 after successful thread creation"); + assert(pthread_join(tid, NULL) == 0 + && "Thread should be joined successfully"); +} + +// This function checks basic contention between main and non-main thread +// increasing the shared variable +static inline void +two_threads_inc_test(pthread_mutex_t *mutex) +{ + MutexCounter mutex_counter; + mutex_counter_init(&mutex_counter, mutex, false); + + pthread_t tid = 0; + spawn_thread(&tid, inc_shared_variable, &mutex_counter); + + assert(tid != 0 && "TID can't be 0 after successful thread creation"); + inc_shared_variable(&mutex_counter); + assert(pthread_join(tid, NULL) == 0 + && "Thread should be joined without errors"); + assert(mutex_counter.counter == NUM_ITER); +} + +// This function creates number of threads specified by NUM_THREADS and run +// concurrent increasing of shared variable +static inline void +max_threads_inc_test(pthread_mutex_t *mutex, int threads_num, + enum SleepState is_sleeping) +{ + MutexCounter mutex_counter; + mutex_counter_init(&mutex_counter, mutex, is_sleeping); + + pthread_t tids[threads_num]; + for (int i = 0; i < threads_num; ++i) { + spawn_thread(&tids[i], inc_shared_variable, &mutex_counter); + } + + inc_shared_variable(&mutex_counter); + + for (int i = 0; i < threads_num; ++i) { + assert(pthread_join(tids[i], NULL) == 0 + && "Thread should be joined without errors"); + } + + print_stat(&mutex_counter.stat); +} + +// This function just runs all the tests described above +static inline void +run_common_tests(pthread_mutex_t *mutex) +{ + srand(time(NULL)); + + fprintf(stderr, "Starting same_thread_lock_unlock_test test\n"); + same_thread_lock_unlock_test(mutex); + fprintf(stderr, "Finished same_thread_lock_unlock_test test\n"); + + fprintf(stderr, "Starting same_non_main_thread_lock_unlock_test test\n"); + same_non_main_thread_lock_unlock_test(mutex); + fprintf(stderr, "Finished same_non_main_thread_lock_unlock_test test\n"); + + fprintf(stderr, "Starting two_threads_inc_test test\n"); + two_threads_inc_test(mutex); + fprintf(stderr, "Finished two_threads_inc_test test\n"); + + fprintf(stderr, "Starting max_threads_inc_test_sleep test\n"); + max_threads_inc_test(mutex, NUM_THREADS, SLEEP); + fprintf(stderr, "Finished concurrent_inc sleep test\n"); + + fprintf(stderr, "Starting max_threads_inc_test_non_sleep test\n"); + max_threads_inc_test(mutex, NUM_THREADS, NON_SLEEP); + fprintf(stderr, "Finished max_threads_inc_test test\n"); +} + +#endif // MUTEX_COMMON_H diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/normal_mutex_stress_test.c b/core/iwasm/libraries/lib-wasi-threads/stress-test/normal_mutex_stress_test.c new file mode 100644 index 0000000000..c7ffae279a --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/normal_mutex_stress_test.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "mutex_common.h" + +void +test() +{ + pthread_mutex_t mutex; + pthread_mutex_init(&mutex, NULL); + + run_common_tests(&mutex); + + fprintf(stderr, "Normal mutex test is completed\n"); + pthread_mutex_destroy(&mutex); +} + +int +main() +{ + test(); + return 0; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/recursive_mutex_stress_test.c b/core/iwasm/libraries/lib-wasi-threads/stress-test/recursive_mutex_stress_test.c new file mode 100644 index 0000000000..5874372cb3 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/recursive_mutex_stress_test.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 + */ + +#include +#include +#include "mutex_common.h" + +void +multiple_same_thread_lock(void *mutex) +{ + for (int i = 0; i < 100; ++i) { + assert(pthread_mutex_lock(mutex) == 0 + && "Recursive mutex should allow multiple locking"); + } + + for (int i = 0; i < 100; ++i) { + assert(pthread_mutex_unlock(mutex) == 0 + && "Recursive mutex should allow multiple unlocking"); + } +} + +void * +same_thread_multiple_rec_mutex_lock(void *mutex) +{ + for (int i = 0; i < NUM_ITER; ++i) { + multiple_same_thread_lock(mutex); + } + + return NULL; +} + +void +test() +{ + pthread_mutex_t mutex; + + // Set mutex type to recursive. This type allows multiple locking and + // unlocking within the same thread + pthread_mutexattr_t mutex_attr; + pthread_mutexattr_init(&mutex_attr); + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&mutex, &mutex_attr); + pthread_mutexattr_destroy(&mutex_attr); + + run_common_tests(&mutex); + + fprintf(stderr, "Starting same_thread_multiple_rec_mutex_lock test\n"); + same_thread_multiple_rec_mutex_lock(&mutex); + fprintf(stderr, "Finished same_thread_multiple_rec_mutex_lock test\n"); + + fprintf(stderr, "Starting same_thread_multiple_rec_mutex_lock test in " + "non-main thread\n"); + pthread_t tid; + spawn_thread(&tid, same_thread_multiple_rec_mutex_lock, &mutex); + assert(pthread_join(tid, NULL) == 0 + && "Non-main thread should be joined successfully"); + fprintf(stderr, "Finished same_thread_multiple_rec_mutex_lock test in " + "non-main thread\n"); + + fprintf(stderr, "Recursive mutex test is completed\n"); + pthread_mutex_destroy(&mutex); +} + +int +main() +{ + test(); + return 0; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/spawn_stress_test.c b/core/iwasm/libraries/lib-wasi-threads/stress-test/spawn_stress_test.c new file mode 100644 index 0000000000..8cb61a2a69 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/spawn_stress_test.c @@ -0,0 +1,125 @@ +/* + * 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 +#include +#include + +unsigned prime_numbers_count = 0; + +bool +is_prime(unsigned int num) +{ + for (unsigned int i = 2; i <= (unsigned int)(sqrt(num)); ++i) { + if (num % i == 0) { + return false; + } + } + + return true; +} + +void * +check_if_prime(void *value) +{ + unsigned int *num = (unsigned int *)(value); + usleep(10000); + if (is_prime(*num)) { + __atomic_fetch_add(&prime_numbers_count, 1, __ATOMIC_SEQ_CST); + } + return NULL; +} + +unsigned int +validate(int iter_num) +{ + unsigned int counter = 0; + for (unsigned int i = 2; i <= iter_num; ++i) { + counter += is_prime(i); + } + + return counter; +} + +void +spawn_thread(pthread_t *thread, int retry_time_us, int retry_num, + unsigned int *arg) +{ + int status_code = -1; + int timeout_us = retry_time_us; + for (int tries = 0; status_code != 0 && tries < retry_num; ++tries) { + status_code = pthread_create(thread, NULL, &check_if_prime, arg); + assert(status_code == 0 || status_code == EAGAIN); + if (status_code == EAGAIN) { + usleep(timeout_us); + timeout_us *= 2; + } + } + + assert(status_code == 0 && "Thread creation should succeed"); +} + +void +test(int iter_num, int retry_num, int max_threads_num, int retry_time_us) +{ + pthread_t threads[max_threads_num]; + unsigned int args[max_threads_num]; + double percentage = 0.1; + + for (unsigned int factorised_number = 2; factorised_number < iter_num; + ++factorised_number) { + if (factorised_number > iter_num * percentage) { + fprintf(stderr, "Stress test is %d%% finished\n", + (unsigned int)(percentage * 100)); + percentage += 0.1; + } + + unsigned int thread_num = factorised_number % max_threads_num; + if (threads[thread_num] != 0) { + assert(pthread_join(threads[thread_num], NULL) == 0); + } + + args[thread_num] = factorised_number; + + usleep(retry_time_us); + spawn_thread(&threads[thread_num], retry_time_us, retry_num, + &args[thread_num]); + assert(threads[thread_num] != 0); + } + + for (int i = 0; i < max_threads_num; ++i) { + assert(threads[i] == 0 || pthread_join(threads[i], NULL) == 0); + } + + // Check the test results + assert( + prime_numbers_count == validate(iter_num) + && "Answer mismatch between tested code and reference implementation"); + + fprintf(stderr, "Stress test finished successfully\n"); +} + +enum DEFAULT_PARAMETERS { + ITER_NUM = 20000, + RETRY_NUM = 8, + MAX_THREADS_NUM = 12, + RETRY_SLEEP_TIME_US = 2000, +}; + +int +main(int argc, char **argv) +{ + test(ITER_NUM, RETRY_NUM, MAX_THREADS_NUM, RETRY_SLEEP_TIME_US); + return 0; +} diff --git a/core/iwasm/libraries/lib-wasi-threads/stress-test/stress_test_threads_creation.c b/core/iwasm/libraries/lib-wasi-threads/stress-test/stress_test_threads_creation.c new file mode 100644 index 0000000000..79baebfa43 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/stress-test/stress_test_threads_creation.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +int threads_executed = 0; +unsigned int threads_creation_tried = 0; +unsigned int threads_in_use = 0; + +void * +thread_func(void *arg) +{ + (void)(arg); + __atomic_fetch_add(&threads_executed, 1, __ATOMIC_RELAXED); + __atomic_fetch_sub(&threads_in_use, 1, __ATOMIC_SEQ_CST); + return NULL; +} + +void +spawn_thread(pthread_t *thread, int retry_time, int iter_num) +{ + int status_code = -1; + int timeout_us = retry_time; + for (int tries = 0; status_code != 0 && tries < iter_num; ++tries) { + status_code = pthread_create(thread, NULL, &thread_func, NULL); + __atomic_fetch_add(&threads_creation_tried, 1, __ATOMIC_RELAXED); + + assert(status_code == 0 || status_code == EAGAIN); + if (status_code == EAGAIN) { + usleep(timeout_us); + timeout_us *= 2; + } + } + + assert(status_code == 0 && "Thread creation should succeed"); +} + +void +test(int iter_num, int max_threads_num, int retry_num, int retry_time_us) +{ + double percentage = 0.1; + int second_us = 1000 * 1000 * 1000; // 1 second in us + + for (int iter = 0; iter < iter_num; ++iter) { + if (iter > iter_num * percentage) { + fprintf(stderr, "Spawning stress test is %d%% finished\n", + (unsigned int)(percentage * 100)); + percentage += 0.1; + } + while (__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) + == max_threads_num) { + usleep(100); + } + + __atomic_fetch_add(&threads_in_use, 1, __ATOMIC_SEQ_CST); + pthread_t tmp; + spawn_thread(&tmp, retry_time_us, iter_num); + pthread_detach(tmp); + } + + while ((__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) != 0)) { + // Casting to int* to suppress compiler warning + __builtin_wasm_memory_atomic_wait32((int *)(&threads_in_use), 0, + second_us); + } + + assert(__atomic_load_n(&threads_in_use, __ATOMIC_SEQ_CST) == 0); + + // Validation + assert(threads_creation_tried >= threads_executed + && "Test executed more threads than were created"); + assert((1. * threads_creation_tried) / threads_executed < 2.5 + && "Ensuring that we're retrying thread creation less than 2.5 " + "times on average "); + + fprintf(stderr, + "Spawning stress test finished successfully executed %d threads " + "with retry ratio %f\n", + threads_creation_tried, + (1. * threads_creation_tried) / threads_executed); +} + +enum DEFAULT_PARAMETERS { + ITER_NUM = 50000, + RETRY_NUM = 8, + MAX_NUM_THREADS = 12, + RETRY_SLEEP_TIME_US = 4000, +}; + +int +main(int argc, char **argv) +{ + test(ITER_NUM, MAX_NUM_THREADS, RETRY_NUM, RETRY_SLEEP_TIME_US); + return 0; +} 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 100755 index 0000000000..608dd22608 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/build.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# +# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +set -eo pipefail +CC=${CC:=/opt/wasi-sdk/bin/clang} +WAMR_DIR=../../../../.. + +show_usage() { + echo "Usage: $0 [--sysroot PATH_TO_SYSROOT]" + echo "--sysroot PATH_TO_SYSROOT specify to build with custom sysroot for wasi-libc" +} + +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + --sysroot) + sysroot_path="$2" + shift + shift + ;; + --help) + show_usage + exit + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Stress tests names +thread_start_file_exclusions=("linear_memory_size_update.wasm") + +rm -rf *.wasm +rm -rf *.aot + +for test_c in *.c; do + test_wasm="$(basename $test_c .c).wasm" + + if [[ " ${thread_start_file_exclusions[@]} " =~ " ${test_wasm} " ]] ; then + thread_start_file="" + else + thread_start_file=$WAMR_DIR/samples/wasi-threads/wasm-apps/wasi_thread_start.S + fi + + if [[ -n "$sysroot_path" ]]; then + if [ ! -d "$sysroot_path" ]; then + echo "Directory $sysroot_path doesn't exist. Aborting" + exit 1 + fi + sysroot_command="--sysroot $sysroot_path" + fi + + echo "Compiling $test_c to $test_wasm" + $CC \ + -target wasm32-wasi-threads \ + -O2 \ + -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 \ + $sysroot_command \ + $thread_start_file \ + $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 0000000000..01ca932c5e --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/common.h @@ -0,0 +1,126 @@ +/* + * 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 + +#if USE_CUSTOM_SYNC_PRIMITIVES != 0 +#include "sync_primitives.h" +#else +#include +#endif + +#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 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 (;;) { + } + } + else if (blocking_task_type == BLOCKING_TASK_ATOMIC_WAIT) { + __builtin_wasm_memory_atomic_wait32(0, 0, -1); + } + else { + sleep(UINT_MAX); + } +} + +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 0000000000..94ffa0f67e --- /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_VALID_TID(thread_ids[i]); + + 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 0000000000..7e1e8e0812 --- /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_VALID_TID(thread_ids[i]); + } + + 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 0000000000..fb33802fce --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/global_lock.c @@ -0,0 +1,83 @@ +/* + * 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 + +#if USE_CUSTOM_SYNC_PRIMITIVES != 0 +#include "sync_primitives.h" +#else +#include +#endif + +#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_VALID_TID(thread_ids[i]); + } + + 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/linear_memory_size_update.c b/core/iwasm/libraries/lib-wasi-threads/test/linear_memory_size_update.c new file mode 100644 index 0000000000..9dcb34a6cb --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/linear_memory_size_update.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include + +typedef enum { + APP_STARTED, + THREAD_STARTED, + MEMORY_ALLOCATED, +} app_state_t; +typedef struct { + + pthread_cond_t cond; + pthread_mutex_t mutex; + app_state_t state; + char *data; +} context_t; + +void +context_init(context_t *ctx) +{ + pthread_cond_init(&ctx->cond, NULL); + pthread_mutex_init(&ctx->mutex, NULL); + ctx->state = APP_STARTED; + ctx->data = NULL; +} + +void +context_destroy(context_t *ctx) +{ + pthread_cond_destroy(&ctx->cond); + pthread_mutex_destroy(&ctx->mutex); + if (ctx->data) { + free(ctx->data); + } +} + +void +context_set_state(context_t *ctx, app_state_t state) +{ + pthread_mutex_lock(&ctx->mutex); + ctx->state = state; + pthread_mutex_unlock(&ctx->mutex); + pthread_cond_signal(&ctx->cond); +} + +void +context_wait_for_state(context_t *ctx, app_state_t state) +{ + pthread_mutex_lock(&ctx->mutex); + while (ctx->state != state) { + pthread_cond_wait(&ctx->cond, &ctx->mutex); + } + pthread_mutex_unlock(&ctx->mutex); +} + +void * +fnc(void *p) +{ + context_t *ctx = (context_t *)p; + context_set_state(ctx, THREAD_STARTED); + + context_wait_for_state(ctx, MEMORY_ALLOCATED); + + // trigger memory.copy + __builtin_memcpy(ctx->data + 512 * 1024, ctx->data + 1024, 1024); + + return NULL; +} + +int +main() +{ + context_t ctx; + context_init(&ctx); + + pthread_t th; + pthread_create(&th, NULL, fnc, &ctx); + + context_wait_for_state(&ctx, THREAD_STARTED); + + // trigger memory.grow + ctx.data = calloc(1024 * 1024, 1); + + context_set_state(&ctx, MEMORY_ALLOCATED); + + pthread_join(th, NULL); + + context_destroy(&ctx); + + return 0; +} 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 0000000000..19d3ec2561 --- /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 0000000000..5370f66701 --- /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 0000000000..a667e91227 --- /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 0000000000..5370f66701 --- /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 0000000000..dc8615adb0 --- /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 0000000000..5370f66701 --- /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 0000000000..bb0ac8fa00 --- /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 0000000000..07689a1055 --- /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 0000000000..a2c248882f --- /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 0000000000..07689a1055 --- /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 0000000000..0904f34bb0 --- /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 0000000000..07689a1055 --- /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/manifest.json b/core/iwasm/libraries/lib-wasi-threads/test/manifest.json new file mode 100644 index 0000000000..cd2cc7636c --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/manifest.json @@ -0,0 +1,3 @@ +{ + "name": "lib-wasi-threads tests" +} 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 0000000000..71fdcb817c --- /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 0000000000..5370f66701 --- /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 0000000000..14352cf417 --- /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 0000000000..5370f66701 --- /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 0000000000..0963aa02fa --- /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 0000000000..5370f66701 --- /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 0000000000..b3e3af7dc0 --- /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 0000000000..07689a1055 --- /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 0000000000..a68ae8be50 --- /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 0000000000..07689a1055 --- /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 0000000000..52c684a518 --- /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 0000000000..07689a1055 --- /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 0000000000..24664c4705 --- /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/sync_primitives.h b/core/iwasm/libraries/lib-wasi-threads/test/sync_primitives.h new file mode 100644 index 0000000000..4b7dac8ec9 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/sync_primitives.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +/* Mutex */ + +typedef int pthread_mutex_t; + +int +pthread_mutex_init(pthread_mutex_t *mutex, void *unused) +{ + *mutex = 0; + return 0; +} + +int +pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + return 0; +} + +static bool +try_pthread_mutex_lock(pthread_mutex_t *mutex) +{ + int expected = 0; + return __atomic_compare_exchange_n(mutex, &expected, 1, false, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +int +pthread_mutex_lock(pthread_mutex_t *mutex) +{ + while (!try_pthread_mutex_lock(mutex)) + __builtin_wasm_memory_atomic_wait32(mutex, 1, -1); + return 0; +} + +int +pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + __atomic_store_n(mutex, 0, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(mutex, 1); + return 0; +} + +/* Barrier */ + +typedef struct { + int count; + int num_threads; + int mutex; + int ready; +} pthread_barrier_t; + +int +pthread_barrier_init(pthread_barrier_t *barrier, void *unused, int num_threads) +{ + barrier->count = 0; + barrier->num_threads = num_threads; + barrier->ready = 0; + pthread_mutex_init(&barrier->mutex, NULL); + + return 0; +} + +int +pthread_barrier_wait(pthread_barrier_t *barrier) +{ + bool no_wait = false; + int count; + + pthread_mutex_lock(&barrier->mutex); + count = barrier->count++; + if (barrier->count >= barrier->num_threads) { + no_wait = true; + barrier->count = 0; + } + pthread_mutex_unlock(&barrier->mutex); + + if (no_wait) { + __atomic_store_n(&barrier->ready, 1, __ATOMIC_SEQ_CST); + __builtin_wasm_memory_atomic_notify(&barrier->ready, count); + return 0; + } + + __builtin_wasm_memory_atomic_wait32(&barrier->ready, 0, -1); + return 0; +} \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/test/trap_after_main_thread_finishes.c b/core/iwasm/libraries/lib-wasi-threads/test/trap_after_main_thread_finishes.c new file mode 100644 index 0000000000..5cf61338f0 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/trap_after_main_thread_finishes.c @@ -0,0 +1,44 @@ +/* + * 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 "wasi_thread_start.h" + +enum CONSTANTS { + SECOND = 1000 * 1000 * 1000, /* 1 second */ + TIMEOUT = 1LL * SECOND +}; + +typedef struct { + start_args_t base; +} shared_t; + +void +__wasi_thread_start_C(int thread_id, int *start_arg) +{ + /* Wait so that the exception is raised after the main thread has finished + * already */ + __builtin_wasm_memory_atomic_wait32(NULL, 0, TIMEOUT); + __builtin_trap(); +} + +int +main(int argc, char **argv) +{ + shared_t data = { 0 }; + + assert(start_args_init(&data.base)); + int thread_id = __wasi_thread_spawn(&data); + ASSERT_VALID_TID(thread_id); + + 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 0000000000..d6e34539be --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/test/update_shared_data_and_alloc_heap.c @@ -0,0 +1,97 @@ +/* + * 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 = 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; + +pthread_mutex_t mutex; +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] = 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)); + + assert(count != NULL && "Failed to call calloc"); + assert(pthread_mutex_init(&mutex, NULL) == 0 && "Failed to init mutex"); + + for (int i = 0; i < NUM_THREADS; i++) { + vals[i] = malloc(sizeof(int)); + assert(vals[i] != NULL && "Failed to call calloc"); + } + + 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_VALID_TID(thread_ids[i]); + } + + 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); + assert(pthread_mutex_destroy(&mutex) == 0 && "Failed to destroy mutex"); + + 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 0000000000..d7f6c74548 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.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 + */ + +#include "tid_allocator.h" +#include "wasm_export.h" +#include "bh_log.h" + +bh_static_assert(TID_MIN <= TID_MAX); +/* Some platforms, like Zephyr, have already defined MIN at this point */ +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +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] = + (uint32)(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, (uint32)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] = + (uint32)(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; +} 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 0000000000..6e25f7749d --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/tid_allocator.h @@ -0,0 +1,46 @@ +/* + * 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" + +#ifdef __cplusplus +extern "C" { +#endif + +#define TID_ALLOCATOR_INIT_SIZE CLUSTER_MAX_THREAD_NUM +enum { + /* Keep it in sync with + https://github.com/WebAssembly/wasi-threads#design-choice-thread-ids */ + 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); + +#ifdef __cplusplus +} +#endif + +#endif /* _TID_ALLOCATOR_H */ \ No newline at end of file diff --git a/core/iwasm/libraries/lib-wasi-threads/unit-test/lib_wasi_threads_unit_tests.cmake b/core/iwasm/libraries/lib-wasi-threads/unit-test/lib_wasi_threads_unit_tests.cmake new file mode 100644 index 0000000000..75d8f4e0e6 --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/unit-test/lib_wasi_threads_unit_tests.cmake @@ -0,0 +1,6 @@ +# Copyright (C) 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +create_wamr_unit_test(wasi_threads + ${CMAKE_CURRENT_LIST_DIR}/test_tid_allocator.cpp +) diff --git a/core/iwasm/libraries/lib-wasi-threads/unit-test/test_tid_allocator.cpp b/core/iwasm/libraries/lib-wasi-threads/unit-test/test_tid_allocator.cpp new file mode 100644 index 0000000000..6fa7300f6c --- /dev/null +++ b/core/iwasm/libraries/lib-wasi-threads/unit-test/test_tid_allocator.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "tid_allocator.h" + +#include + +class TidAllocatorTest : public ::testing::Test +{ + protected: + void SetUp() override { ASSERT_TRUE(tid_allocator_init(&_allocator)); } + + void TearDown() override { tid_allocator_deinit(&_allocator); } + + TidAllocator _allocator; +}; + +static bool +is_tid_valid(int32 tid) +{ + /* See: https://github.com/WebAssembly/wasi-threads#design-choice-thread-ids + */ + return tid >= TID_MIN && tid <= TID_MAX; +} + +TEST_F(TidAllocatorTest, BasicTest) +{ + int32 tid = tid_allocator_get_tid(&_allocator); + + ASSERT_TRUE(is_tid_valid(tid)); +} + +TEST_F(TidAllocatorTest, ShouldFailOnAllocatingMoreThanAllowedThreadIDs) +{ + int32 last_tid = 0; + for (int32 i = 0; i < TID_MAX + 1; i++) { + last_tid = tid_allocator_get_tid(&_allocator); + if (last_tid < 0) { + break; + } + ASSERT_TRUE(is_tid_valid(last_tid)); + } + + ASSERT_LT(last_tid, 0); +} + +TEST_F(TidAllocatorTest, ShouldAllocateMoreThanAllowedTIDsIfOldTIDsAreReleased) +{ + int32 last_tid = 0; + for (int32 i = 0; i < TID_MAX + 1; i++) { + if (last_tid != 0) { + tid_allocator_release_tid(&_allocator, last_tid); + } + + last_tid = tid_allocator_get_tid(&_allocator); + ASSERT_TRUE(is_tid_valid(last_tid)); + } +} diff --git a/core/iwasm/libraries/libc-builtin/SConscript b/core/iwasm/libraries/libc-builtin/SConscript new file mode 100644 index 0000000000..8dd0043823 --- /dev/null +++ b/core/iwasm/libraries/libc-builtin/SConscript @@ -0,0 +1,24 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() + +#src = Split(''' +#libc_builtin_wrapper.c +#''') + +src = Glob('*.c') + +CPPDEFINES = ['WASM_ENABLE_LIBC_BUILTIN=1'] + +CPPPATH = [cwd] + + +group = DefineGroup('iwasm_libc_builtin', src, depend = [''], CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES) + +Return('group') diff --git a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c index 28aba59dbc..6c89b85370 100644 --- a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c +++ b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c @@ -6,25 +6,21 @@ #include "bh_common.h" #include "bh_log.h" #include "wasm_export.h" -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 #include "../interpreter/wasm.h" + +#if defined(_WIN32) || defined(_WIN32_) +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #endif void wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); -uint32 -wasm_runtime_get_temp_ret(wasm_module_inst_t module); - -void -wasm_runtime_set_temp_ret(wasm_module_inst_t module, uint32 temp_ret); - -uint32 -wasm_runtime_get_llvm_stack(wasm_module_inst_t module); - -void -wasm_runtime_set_llvm_stack(wasm_module_inst_t module, uint32 llvm_stack); +uint64 +wasm_runtime_module_realloc(wasm_module_inst_t module, uint64 ptr, uint64 size, + void **p_native_addr); +/* clang-format off */ #define get_module_inst(exec_env) \ wasm_runtime_get_module_inst(exec_env) @@ -34,136 +30,73 @@ wasm_runtime_set_llvm_stack(wasm_module_inst_t module, uint32 llvm_stack); #define validate_app_str_addr(offset) \ wasm_runtime_validate_app_str_addr(module_inst, offset) +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + #define addr_app_to_native(offset) \ wasm_runtime_addr_app_to_native(module_inst, offset) #define addr_native_to_app(ptr) \ wasm_runtime_addr_native_to_app(module_inst, ptr) -#define module_malloc(size) \ - wasm_runtime_module_malloc(module_inst, size) +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) #define module_free(offset) \ wasm_runtime_module_free(module_inst, offset) +/* clang-format on */ typedef int (*out_func_t)(int c, void *ctx); -enum pad_type { - PAD_NONE, - PAD_ZERO_BEFORE, - PAD_SPACE_BEFORE, - PAD_SPACE_AFTER, -}; - typedef char *_va_list; -#define _INTSIZEOF(n) \ - (((uint32)sizeof(n) + 3) & (uint32)~3) -#define _va_arg(ap, t) \ - (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) - -#define CHECK_VA_ARG(ap, t) do { \ - if ((uint8*)ap + _INTSIZEOF(t) > native_end_addr) \ - goto fail; \ -} while (0) - -/** - * @brief Output an unsigned int in hex format - * - * Output an unsigned int on output installed by platform at init time. Should - * be able to handle an unsigned int of any size, 32 or 64 bit. - * @param num Number to output - * - * @return N/A - */ -static void -_printf_hex_uint(out_func_t out, void *ctx, - const uint64 num, bool is_u64, - enum pad_type padding, - int min_width) -{ - int shift = sizeof(num) * 8; - int found_largest_digit = 0; - int remaining = 16; /* 16 digits max */ - int digits = 0; - char nibble; - - while (shift >= 4) { - shift -= 4; - nibble = (num >> shift) & 0xf; - - if (nibble || found_largest_digit || shift == 0) { - found_largest_digit = 1; - nibble = (char)(nibble + (nibble > 9 ? 87 : 48)); - out((int) nibble, ctx); - digits++; - continue; - } - - if (remaining-- <= min_width) { - if (padding == PAD_ZERO_BEFORE) { - out('0', ctx); - } else if (padding == PAD_SPACE_BEFORE) { - out(' ', ctx); - } - } - } - - if (padding == PAD_SPACE_AFTER) { - remaining = min_width * 2 - digits; - while (remaining-- > 0) { - out(' ', ctx); - } - } -} - -/** - * @brief Output an unsigned int in decimal format - * - * Output an unsigned int on output installed by platform at init time. Only - * works with 32-bit values. - * @param num Number to output - * - * @return N/A - */ -static void -_printf_dec_uint(out_func_t out, void *ctx, - const uint32 num, - enum pad_type padding, - int min_width) -{ - uint32 pos = 999999999; - uint32 remainder = num; - int found_largest_digit = 0; - int remaining = 10; /* 10 digits max */ - int digits = 1; - - /* make sure we don't skip if value is zero */ - if (min_width <= 0) { - min_width = 1; - } - - while (pos >= 9) { - if (found_largest_digit || remainder > pos) { - found_largest_digit = 1; - out((int) ((remainder / (pos + 1)) + 48), ctx); - digits++; - } else if (remaining <= min_width && padding < PAD_SPACE_AFTER) { - out((int) (padding == PAD_ZERO_BEFORE ? '0' : ' '), ctx); - digits++; - } - remaining--; - remainder %= (pos + 1); - pos /= 10; - } - out((int) (remainder + 48), ctx); - - if (padding == PAD_SPACE_AFTER) { - remaining = min_width - digits; - while (remaining-- > 0) { - out(' ', ctx); - } - } -} +#define _INTSIZEOF(n) (((uint32)sizeof(n) + 3) & (uint32)~3) +#define _va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) + +#define CHECK_VA_ARG(ap, t) \ + do { \ + if ((uint8 *)ap + _INTSIZEOF(t) > native_end_addr) { \ + if (fmt_buf != temp_fmt) { \ + wasm_runtime_free(fmt_buf); \ + } \ + goto fail; \ + } \ + } while (0) + +/* clang-format off */ +#define PREPARE_TEMP_FORMAT() \ + char temp_fmt[32], *s, *fmt_buf = temp_fmt; \ + uint32 fmt_buf_len = (uint32)sizeof(temp_fmt); \ + int32 n; \ + \ + /* additional 2 bytes: one is the format char, \ + the other is `\0` */ \ + if ((uint32)(fmt - fmt_start_addr + 2) >= fmt_buf_len) { \ + bh_assert((uint32)(fmt - fmt_start_addr) <= \ + UINT32_MAX - 2); \ + fmt_buf_len = (uint32)(fmt - fmt_start_addr + 2); \ + if (!(fmt_buf = wasm_runtime_malloc(fmt_buf_len))) { \ + print_err(out, ctx); \ + break; \ + } \ + } \ + \ + memset(fmt_buf, 0, fmt_buf_len); \ + bh_memcpy_s(fmt_buf, fmt_buf_len, fmt_start_addr, \ + (uint32)(fmt - fmt_start_addr + 1)); +/* clang-format on */ + +#define OUTPUT_TEMP_FORMAT() \ + do { \ + if (n > 0) { \ + s = buf; \ + while (*s) \ + out((int)(*s++), ctx); \ + } \ + \ + if (fmt_buf != temp_fmt) { \ + wasm_runtime_free(fmt_buf); \ + } \ + } while (0) static void print_err(out_func_t out, void *ctx) @@ -178,13 +111,12 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, wasm_module_inst_t module_inst) { int might_format = 0; /* 1 if encountered a '%' */ - enum pad_type padding = PAD_NONE; - int min_width = -1; int long_ctr = 0; uint8 *native_end_addr; + const char *fmt_start_addr = NULL; - if (!wasm_runtime_get_native_addr_range(module_inst, (uint8*)ap, - NULL, &native_end_addr)) + if (!wasm_runtime_get_native_addr_range(module_inst, (uint8 *)ap, NULL, + &native_end_addr)) goto fail; /* fmt has already been adjusted if needed */ @@ -192,166 +124,194 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, while (*fmt) { if (!might_format) { if (*fmt != '%') { - out((int) *fmt, ctx); + out((int)*fmt, ctx); } else { might_format = 1; - min_width = -1; - padding = PAD_NONE; long_ctr = 0; + fmt_start_addr = fmt; } } else { switch (*fmt) { - case '-': - padding = PAD_SPACE_AFTER; - goto still_might_format; + case '.': + case '+': + case '-': + case ' ': + case '#': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + goto still_might_format; - case '0': - if (min_width < 0 && padding == PAD_NONE) { - padding = PAD_ZERO_BEFORE; + case 't': /* ptrdiff_t */ + case 'z': /* size_t (32bit on wasm) */ + long_ctr = 1; goto still_might_format; - } - /* Fall through */ - case '1' ... '9': - if (min_width < 0) { - min_width = *fmt - '0'; - } else { - min_width = 10 * min_width + *fmt - '0'; - } - if (padding == PAD_NONE) { - padding = PAD_SPACE_BEFORE; + case 'j': + /* intmax_t/uintmax_t */ + long_ctr = 2; + goto still_might_format; + + case 'l': + long_ctr++; + /* Fall through */ + case 'h': + /* FIXME: do nothing for these modifiers */ + goto still_might_format; + + case 'o': + case 'd': + case 'i': + case 'u': + case 'p': + case 'x': + case 'X': + case 'c': + { + char buf[64]; + PREPARE_TEMP_FORMAT(); + + if (long_ctr < 2) { + int32 d; + + CHECK_VA_ARG(ap, uint32); + d = _va_arg(ap, int32); + + if (long_ctr == 1) { + uint32 fmt_end_idx = (uint32)(fmt - fmt_start_addr); + + if (fmt_buf[fmt_end_idx - 1] == 'l' + || fmt_buf[fmt_end_idx - 1] == 'z' + || fmt_buf[fmt_end_idx - 1] == 't') { + /* The %ld, %zd and %td should be treated as + * 32bit integer in wasm */ + fmt_buf[fmt_end_idx - 1] = fmt_buf[fmt_end_idx]; + fmt_buf[fmt_end_idx] = '\0'; + } + } + + n = snprintf(buf, sizeof(buf), fmt_buf, d); + } + else { + int64 lld; + + /* Make 8-byte aligned */ + ap = (_va_list)(((uintptr_t)ap + 7) & ~(uintptr_t)7); + CHECK_VA_ARG(ap, uint64); + lld = _va_arg(ap, int64); + n = snprintf(buf, sizeof(buf), fmt_buf, lld); + } + + OUTPUT_TEMP_FORMAT(); + break; } - goto still_might_format; - case 'l': - long_ctr++; - /* Fall through */ - case 'z': - case 'h': - /* FIXME: do nothing for these modifiers */ - goto still_might_format; + case 's': + { + char buf_tmp[128], *buf = buf_tmp; + char *start; + uint32 s_offset, str_len, buf_len; - case 'd': - case 'i': { - int32 d; + PREPARE_TEMP_FORMAT(); - if (long_ctr < 2) { CHECK_VA_ARG(ap, int32); - d = _va_arg(ap, int32); - } - else { - int64 lld; - CHECK_VA_ARG(ap, int64); - lld = _va_arg(ap, int64); - if (lld > INT32_MAX || lld < INT32_MIN) { - print_err(out, ctx); - break; + s_offset = _va_arg(ap, uint32); + + if (!validate_app_str_addr(s_offset)) { + if (fmt_buf != temp_fmt) { + wasm_runtime_free(fmt_buf); + } + return false; } - d = (int32)lld; - } - if (d < 0) { - out((int)'-', ctx); - d = -d; - min_width--; - } - _printf_dec_uint(out, ctx, (uint32)d, padding, min_width); - break; - } - case 'u': { - uint32 u; + s = start = addr_app_to_native((uint64)s_offset); - if (long_ctr < 2) { - CHECK_VA_ARG(ap, uint32); - u = _va_arg(ap, uint32); - } - else { - uint64 llu; - CHECK_VA_ARG(ap, uint64); - llu = _va_arg(ap, uint64); - if (llu > INT32_MAX) { + str_len = (uint32)strlen(start); + if (str_len >= UINT32_MAX - 64) { print_err(out, ctx); + if (fmt_buf != temp_fmt) { + wasm_runtime_free(fmt_buf); + } break; } - u = (uint32)llu; - } - _printf_dec_uint(out, ctx, u, padding, min_width); - break; - } - case 'p': - out('0', ctx); - out('x', ctx); - /* left-pad pointers with zeros */ - padding = PAD_ZERO_BEFORE; - min_width = 8; - /* Fall through */ - case 'x': - case 'X': { - uint64 x; - bool is_ptr = (*fmt == 'p') ? true : false; - - if (long_ctr < 2) { - CHECK_VA_ARG(ap, uint32); - x = _va_arg(ap, uint32); - } else { - CHECK_VA_ARG(ap, uint64); - x = _va_arg(ap, uint64); - } - _printf_hex_uint(out, ctx, x, !is_ptr, padding, min_width); - break; - } - case 's': { - char *s; - char *start; - int32 s_offset; + /* reserve 64 more bytes as there may be width description + * in the fmt */ + buf_len = str_len + 64; + + if (buf_len > (uint32)sizeof(buf_tmp)) { + if (!(buf = wasm_runtime_malloc(buf_len))) { + print_err(out, ctx); + if (fmt_buf != temp_fmt) { + wasm_runtime_free(fmt_buf); + } + break; + } + } - CHECK_VA_ARG(ap, int32); - s_offset = _va_arg(ap, int32); + n = snprintf(buf, buf_len, fmt_buf, + (s_offset == 0 && str_len == 0) ? NULL + : start); - if (!validate_app_str_addr(s_offset)) { - return false; - } + OUTPUT_TEMP_FORMAT(); - s = start = addr_app_to_native(s_offset); + if (buf != buf_tmp) { + wasm_runtime_free(buf); + } - while (*s) - out((int) (*s++), ctx); + break; + } - if (padding == PAD_SPACE_AFTER) { - int remaining = min_width - (int32)(s - start); - while (remaining-- > 0) { - out(' ', ctx); - } + case '%': + { + out((int)'%', ctx); + break; } - break; - } - case 'c': { - int c; - CHECK_VA_ARG(ap, int); - c = _va_arg(ap, int); - out(c, ctx); - break; - } + case 'e': + case 'E': + case 'g': + case 'G': + case 'f': + case 'F': + { + float64 f64; + char buf[64]; + PREPARE_TEMP_FORMAT(); + + /* Make 8-byte aligned */ + ap = (_va_list)(((uintptr_t)ap + 7) & ~(uintptr_t)7); + CHECK_VA_ARG(ap, float64); + f64 = _va_arg(ap, float64); + n = snprintf(buf, sizeof(buf), fmt_buf, f64); + + OUTPUT_TEMP_FORMAT(); + break; + } - case '%': { - out((int) '%', ctx); - break; - } + case 'n': + /* print nothing */ + break; - default: - out((int) '%', ctx); - out((int) *fmt, ctx); - break; + default: + out((int)'%', ctx); + out((int)*fmt, ctx); + break; } might_format = 0; } -still_might_format: + still_might_format: ++fmt; } return true; @@ -361,10 +321,22 @@ _vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, return false; } +#ifndef BUILTIN_LIBC_BUFFERED_PRINTF +#define BUILTIN_LIBC_BUFFERED_PRINTF 0 +#endif + +#ifndef BUILTIN_LIBC_BUFFERED_PRINT_SIZE +#define BUILTIN_LIBC_BUFFERED_PRINT_SIZE 128 +#endif + struct str_context { char *str; uint32 max; uint32 count; +#if BUILTIN_LIBC_BUFFERED_PRINTF != 0 + char print_buf[BUILTIN_LIBC_BUFFERED_PRINT_SIZE]; + uint32 print_buf_size; +#endif }; static int @@ -377,88 +349,93 @@ sprintf_out(int c, struct str_context *ctx) if (ctx->count == ctx->max - 1) { ctx->str[ctx->count++] = '\0'; - } else { + } + else { ctx->str[ctx->count++] = (char)c; } return c; } +#if BUILTIN_LIBC_BUFFERED_PRINTF != 0 static int printf_out(int c, struct str_context *ctx) { - bh_printf("%c", c); + if (c == '\n') { + ctx->print_buf[ctx->print_buf_size] = '\0'; + os_printf("%s\n", ctx->print_buf); + ctx->print_buf_size = 0; + } + else if (ctx->print_buf_size >= sizeof(ctx->print_buf) - 2) { + ctx->print_buf[ctx->print_buf_size++] = (char)c; + ctx->print_buf[ctx->print_buf_size] = '\0'; + os_printf("%s\n", ctx->print_buf); + ctx->print_buf_size = 0; + } + else { + ctx->print_buf[ctx->print_buf_size++] = (char)c; + } ctx->count++; return c; } - -static bool -parse_printf_args(wasm_module_inst_t module_inst, int32 fmt_offset, - int32 va_list_offset, const char **p_fmt, - _va_list *p_va_args) -{ - const char *fmt; - union { - uintptr_t u; - _va_list v; - } u; - - if (!validate_app_str_addr(fmt_offset) - || !validate_app_addr(va_list_offset, sizeof(int32))) - return false; - - fmt = (const char*) addr_app_to_native(fmt_offset); - u.u = (uintptr_t) addr_app_to_native(va_list_offset); - - *p_fmt = fmt; - *p_va_args = u.v; - return true; +#else +static int +printf_out(int c, struct str_context *ctx) +{ + os_printf("%c", c); + ctx->count++; + return c; } +#endif static int -_printf_wrapper(wasm_exec_env_t exec_env, - int32 fmt_offset, int32 va_list_offset) +printf_wrapper(wasm_exec_env_t exec_env, const char *format, _va_list va_args) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - struct str_context ctx = { NULL, 0, 0 }; - const char *fmt; - _va_list va_args; + struct str_context ctx = { 0 }; + + memset(&ctx, 0, sizeof(ctx)); - if (!parse_printf_args(module_inst, fmt_offset, va_list_offset, &fmt, &va_args)) + /* format has been checked by runtime */ + if (!validate_native_addr(va_args, (uint64)sizeof(int32))) return 0; - if (!_vprintf_wa((out_func_t)printf_out, &ctx, fmt, va_args, module_inst)) + if (!_vprintf_wa((out_func_t)printf_out, &ctx, format, va_args, + module_inst)) return 0; + +#if BUILTIN_LIBC_BUFFERED_PRINTF != 0 + if (ctx.print_buf_size > 0) + os_printf("%s", ctx.print_buf); +#endif + return (int)ctx.count; } static int -_sprintf_wrapper(wasm_exec_env_t exec_env, - int32 str_offset, int32 fmt_offset, int32 va_list_offset) +sprintf_wrapper(wasm_exec_env_t exec_env, char *str, const char *format, + _va_list va_args) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - int32 app_end_offset; + uint8 *native_end_offset; struct str_context ctx; - char *str; - const char *fmt; - _va_list va_args; - if (!wasm_runtime_get_app_addr_range(module_inst, str_offset, - NULL, &app_end_offset)) { - wasm_runtime_set_exception(module_inst, "out of bounds memory access"); - return false; - } - - str = addr_app_to_native(str_offset); + /* str and format have been checked by runtime */ + if (!validate_native_addr(va_args, (uint64)sizeof(uint32))) + return 0; - if (!parse_printf_args(module_inst, fmt_offset, va_list_offset, &fmt, &va_args)) + if (!wasm_runtime_get_native_addr_range(module_inst, (uint8 *)str, NULL, + &native_end_offset)) { + wasm_runtime_set_exception(module_inst, "out of bounds memory access"); return 0; + } ctx.str = str; - ctx.max = (uint32)(app_end_offset - str_offset); + ctx.max = (uint32)(native_end_offset - (uint8 *)str); ctx.count = 0; - if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, fmt, va_args, module_inst)) + if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, + module_inst)) return 0; if (ctx.count < ctx.max) { @@ -469,29 +446,22 @@ _sprintf_wrapper(wasm_exec_env_t exec_env, } static int -_snprintf_wrapper(wasm_exec_env_t exec_env, - int32 str_offset, uint32 size, int32 fmt_offset, - int32 va_list_offset) +snprintf_wrapper(wasm_exec_env_t exec_env, char *str, uint32 size, + const char *format, _va_list va_args) { wasm_module_inst_t module_inst = get_module_inst(exec_env); struct str_context ctx; - char *str; - const char *fmt; - _va_list va_args; - if (!validate_app_addr(str_offset, size)) - return 0; - - str = addr_app_to_native(str_offset); - - if (!parse_printf_args(module_inst, fmt_offset, va_list_offset, &fmt, &va_args)) + /* str and format have been checked by runtime */ + if (!validate_native_addr(va_args, (uint64)sizeof(uint32))) return 0; ctx.str = str; ctx.max = size; ctx.count = 0; - if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, fmt, va_args, module_inst)) + if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, + module_inst)) return 0; if (ctx.count < ctx.max) { @@ -502,46 +472,36 @@ _snprintf_wrapper(wasm_exec_env_t exec_env, } static int -_puts_wrapper(wasm_exec_env_t exec_env, - int32 str_offset) +puts_wrapper(wasm_exec_env_t exec_env, const char *str) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - const char *str; + (void)exec_env; - if (!validate_app_str_addr(str_offset)) - return 0; - - str = addr_app_to_native(str_offset); - return bh_printf("%s\n", str); + return os_printf("%s\n", str); } static int -_putchar_wrapper(wasm_exec_env_t exec_env, int c) +putchar_wrapper(wasm_exec_env_t exec_env, int c) { - bh_printf("%c", c); + (void)exec_env; + + os_printf("%c", c); return 1; } -static int32 -_strdup_wrapper(wasm_exec_env_t exec_env, - int32 str_offset) +static uint32 +strdup_wrapper(wasm_exec_env_t exec_env, const char *str) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *str, *str_ret; + char *str_ret; uint32 len; - int32 str_ret_offset = 0; - - if (!validate_app_str_addr(str_offset)) - return 0; - - str = addr_app_to_native(str_offset); + uint32 str_ret_offset = 0; + /* str has been checked by runtime */ if (str) { len = (uint32)strlen(str) + 1; - str_ret_offset = module_malloc(len); + str_ret_offset = (uint32)module_malloc((uint64)len, (void **)&str_ret); if (str_ret_offset) { - str_ret = addr_app_to_native(str_ret_offset); bh_memcpy_s(str_ret, len, str, len); } } @@ -549,740 +509,622 @@ _strdup_wrapper(wasm_exec_env_t exec_env, return str_ret_offset; } -static int32 -__strdup_wrapper(wasm_exec_env_t exec_env, - int32 str_offset) +static uint32 +_strdup_wrapper(wasm_exec_env_t exec_env, const char *str) { - return _strdup_wrapper(exec_env, str_offset); + return strdup_wrapper(exec_env, str); } static int32 -_memcmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, int32 s2_offset, uint32 size) +memcmp_wrapper(wasm_exec_env_t exec_env, const void *s1, const void *s2, + uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s1, *s2; - if (!validate_app_addr(s1_offset, size) - || !validate_app_addr(s2_offset, size)) + /* s2 has been checked by runtime */ + if (!validate_native_addr((void *)s1, (uint64)size)) return 0; - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); return memcmp(s1, s2, size); } -static int32 -_memcpy_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, uint32 size) +static uint32 +memcpy_wrapper(wasm_exec_env_t exec_env, void *dst, const void *src, + uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *dst, *src; + uint32 dst_offset = (uint32)addr_native_to_app(dst); if (size == 0) return dst_offset; - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, (uint64)size)) return dst_offset; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); bh_memcpy_s(dst, size, src, size); return dst_offset; } -static int32 -_memmove_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, uint32 size) +static uint32 +memmove_wrapper(wasm_exec_env_t exec_env, void *dst, void *src, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *dst, *src; + uint32 dst_offset = (uint32)addr_native_to_app(dst); - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + if (size == 0) + return dst_offset; + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, (uint64)size)) return dst_offset; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); memmove(dst, src, size); return dst_offset; } -static int32 -_memset_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, int32 c, uint32 size) +static uint32 +memset_wrapper(wasm_exec_env_t exec_env, void *s, int32 c, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s; + uint32 s_offset = (uint32)addr_native_to_app(s); - if (!validate_app_addr(s_offset, size)) + if (!validate_native_addr(s, (uint64)size)) return s_offset; - s = addr_app_to_native(s_offset); memset(s, c, size); return s_offset; } -static int32 -_strchr_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, int32 c) +static uint32 +strchr_wrapper(wasm_exec_env_t exec_env, const char *s, int32 c) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - const char *s; char *ret; - if (!validate_app_str_addr(s_offset)) - return s_offset; - - s = addr_app_to_native(s_offset); + /* s has been checked by runtime */ ret = strchr(s, c); - return ret ? addr_native_to_app(ret) : 0; + return ret ? (uint32)addr_native_to_app(ret) : 0; } static int32 -_strcmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, int32 s2_offset) +strcmp_wrapper(wasm_exec_env_t exec_env, const char *s1, const char *s2) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s1, *s2; - - if (!validate_app_str_addr(s1_offset) - || !validate_app_str_addr(s2_offset)) - return 0; + (void)exec_env; - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); + /* s1 and s2 have been checked by runtime */ return strcmp(s1, s2); } static int32 -_strncmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, int32 s2_offset, uint32 size) +strncmp_wrapper(wasm_exec_env_t exec_env, const char *s1, const char *s2, + uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s1, *s2; - if (!validate_app_addr(s1_offset, size) - || !validate_app_addr(s2_offset, size)) + /* s2 has been checked by runtime */ + if (!validate_native_addr((void *)s1, (uint64)size)) return 0; - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); return strncmp(s1, s2, size); } -static int32 -_strcpy_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset) +static uint32 +strcpy_wrapper(wasm_exec_env_t exec_env, char *dst, const char *src) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *dst, *src; - uint32 len; + uint32 len = (uint32)strlen(src) + 1; - if (!validate_app_str_addr(src_offset)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, (uint64)len)) return 0; - src = addr_app_to_native(src_offset); - len = (uint32)strlen(src); - - if (!validate_app_addr(dst_offset, len + 1)) - return 0; - - dst = addr_app_to_native(dst_offset); - strncpy(dst, src, len + 1); - return dst_offset; +#ifndef BH_PLATFORM_WINDOWS + strncpy(dst, src, len); +#else + strncpy_s(dst, len, src, len); +#endif + return (uint32)addr_native_to_app(dst); } -static int32 -_strncpy_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, uint32 size) +static uint32 +strncpy_wrapper(wasm_exec_env_t exec_env, char *dst, const char *src, + uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *dst, *src; - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, (uint64)size)) return 0; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); +#ifndef BH_PLATFORM_WINDOWS strncpy(dst, src, size); - return dst_offset; +#else + strncpy_s(dst, size, src, size); +#endif + return (uint32)addr_native_to_app(dst); } static uint32 -_strlen_wrapper(wasm_exec_env_t exec_env, - int32 s_offset) +strlen_wrapper(wasm_exec_env_t exec_env, const char *s) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s; + (void)exec_env; - if (!validate_app_str_addr(s_offset)) - return 0; - - s = addr_app_to_native(s_offset); + /* s has been checked by runtime */ return (uint32)strlen(s); } -static int32 -_malloc_wrapper(wasm_exec_env_t exec_env, - uint32 size) +static uint32 +malloc_wrapper(wasm_exec_env_t exec_env, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - return module_malloc(size); + return (uint32)module_malloc((uint64)size, NULL); } -static int32 -_calloc_wrapper(wasm_exec_env_t exec_env, - uint32 nmemb, uint32 size) +static uint32 +calloc_wrapper(wasm_exec_env_t exec_env, uint32 nmemb, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - uint64 total_size = (uint64) nmemb * (uint64) size; - int32 ret_offset = 0; + uint64 total_size = (uint64)nmemb * (uint64)size; + uint32 ret_offset = 0; uint8 *ret_ptr; if (total_size >= UINT32_MAX) return 0; - ret_offset = module_malloc((uint32)total_size); + ret_offset = (uint32)module_malloc(total_size, (void **)&ret_ptr); if (ret_offset) { - ret_ptr = addr_app_to_native(ret_offset); - memset(ret_ptr, 0, (uint32) total_size); + memset(ret_ptr, 0, (uint32)total_size); } return ret_offset; } -static void -_free_wrapper(wasm_exec_env_t exec_env, - int32 ptr_offset) +static uint32 +realloc_wrapper(wasm_exec_env_t exec_env, uint32 ptr, uint32 new_size) { + uint64 ret_offset = 0; wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!validate_app_addr(ptr_offset, 4)) - return; - return module_free(ptr_offset); + + ret_offset = wasm_runtime_module_realloc(module_inst, ptr, new_size, NULL); + bh_assert(ret_offset < UINT32_MAX); + return (uint32)ret_offset; } -static int32 -_atoi_wrapper(wasm_exec_env_t exec_env, - int32 s_offset) +static void +free_wrapper(wasm_exec_env_t exec_env, void *ptr) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *str; - if (!validate_app_str_addr(s_offset)) - return 0; - - str = addr_app_to_native(s_offset); + if (!validate_native_addr(ptr, (uint64)sizeof(uint32))) + return; - return atoi(str); + module_free(addr_native_to_app(ptr)); } static int32 -_bsearch_wrapper(wasm_exec_env_t exec_env, - int32 key_offset, /* const void * */ - int32 array_offset, /* const void * */ - uint32 count, - uint32 size, - int32 cmp_index) +atoi_wrapper(wasm_exec_env_t exec_env, const char *s) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasm_runtime_set_exception(module_inst, "bsearch not implemented."); - - return 0; + (void)exec_env; + /* s has been checked by runtime */ + return atoi(s); } static void -_exit_wrapper(wasm_exec_env_t exec_env, - int32 status) +exit_wrapper(wasm_exec_env_t exec_env, int32 status) { wasm_module_inst_t module_inst = get_module_inst(exec_env); char buf[32]; - snprintf(buf, sizeof(buf), "env.exit(%i)", status); + snprintf(buf, sizeof(buf), "env.exit(%" PRId32 ")", status); wasm_runtime_set_exception(module_inst, buf); } static int32 -_strtol_wrapper(wasm_exec_env_t exec_env, - int32 nptr_offset, /* const char * */ - int32 endptr_offset, /* char ** */ - int32 base) +strtol_wrapper(wasm_exec_env_t exec_env, const char *nptr, char **endptr, + int32 base) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *nptr, **endptr; int32 num = 0; - if (!validate_app_str_addr(nptr_offset) - || !validate_app_addr(endptr_offset, sizeof(int32))) + /* nptr has been checked by runtime */ + if (!validate_native_addr(endptr, (uint64)sizeof(uint32))) return 0; - nptr = addr_app_to_native(nptr_offset); - endptr = addr_app_to_native(endptr_offset); - num = (int32)strtol(nptr, endptr, base); - *(int32 *)endptr = addr_native_to_app(*endptr); + *(uint32 *)endptr = (uint32)addr_native_to_app(*endptr); return num; } static uint32 -_strtoul_wrapper(wasm_exec_env_t exec_env, - int32 nptr_offset, /* const char * */ - int32 endptr_offset, /* char ** */ - int32 base) +strtoul_wrapper(wasm_exec_env_t exec_env, const char *nptr, char **endptr, + int32 base) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *nptr, **endptr; uint32 num = 0; - if (!validate_app_str_addr(nptr_offset) - || !validate_app_addr(endptr_offset, sizeof(int32))) + /* nptr has been checked by runtime */ + if (!validate_native_addr(endptr, (uint64)sizeof(uint32))) return 0; - nptr = addr_app_to_native(nptr_offset); - endptr = addr_app_to_native(endptr_offset); - num = (uint32)strtoul(nptr, endptr, base); - *(int32 *)endptr = addr_native_to_app(*endptr); + *(uint32 *)endptr = (uint32)addr_native_to_app(*endptr); return num; } -static int32 -_memchr_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const void * */ - int32 c, - uint32 n) +static uint32 +memchr_wrapper(wasm_exec_env_t exec_env, const void *s, int32 c, uint32 n) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s, *res; + void *res; - if (!validate_app_addr(s_offset, n)) + if (!validate_native_addr((void *)s, (uint64)n)) return 0; - s = (void*)addr_app_to_native(s_offset); - res = memchr(s, c, n); - - return addr_native_to_app(res); + return (uint32)addr_native_to_app(res); } static int32 -_strncasecmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, /* const char * */ - int32 s2_offset, /* const char * */ - uint32 n) +strncasecmp_wrapper(wasm_exec_env_t exec_env, const char *s1, const char *s2, + uint32 n) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s1, *s2; - - if (!validate_app_str_addr(s1_offset) - || !validate_app_str_addr(s2_offset)) - return 0; - - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); + (void)exec_env; + /* s1 and s2 have been checked by runtime */ return strncasecmp(s1, s2, n); } static uint32 -_strspn_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const char * */ - int32 accept_offset) /* const char * */ +strspn_wrapper(wasm_exec_env_t exec_env, const char *s, const char *accept) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s, *accept; - - if (!validate_app_str_addr(s_offset) - || !validate_app_str_addr(accept_offset)) - return 0; - - s = addr_app_to_native(s_offset); - accept = addr_app_to_native(accept_offset); + (void)exec_env; + /* s and accept have been checked by runtime */ return (uint32)strspn(s, accept); } static uint32 -_strcspn_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const char * */ - int32 reject_offset) /* const char * */ +strcspn_wrapper(wasm_exec_env_t exec_env, const char *s, const char *reject) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s, *reject; - - if (!validate_app_str_addr(s_offset) - || !validate_app_str_addr(reject_offset)) - return 0; - - s = addr_app_to_native(s_offset); - reject = addr_app_to_native(reject_offset); + (void)exec_env; + /* s and reject have been checked by runtime */ return (uint32)strcspn(s, reject); } -static int32 -_strstr_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const char * */ - int32 find_offset) /* const char * */ +static uint32 +strstr_wrapper(wasm_exec_env_t exec_env, const char *s, const char *find) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s, *find, *res; - - if (!validate_app_str_addr(s_offset) - || !validate_app_str_addr(find_offset)) - return 0; - - s = addr_app_to_native(s_offset); - find = addr_app_to_native(find_offset); - - res = strstr(s, find); - - return addr_native_to_app(res); + /* s and find have been checked by runtime */ + char *res = strstr(s, find); + return (uint32)addr_native_to_app(res); } static int32 -_isupper_wrapper(wasm_exec_env_t exec_env, int32 c) +isupper_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isupper(c); } static int32 -_isalpha_wrapper(wasm_exec_env_t exec_env, int32 c) +isalpha_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isalpha(c); } static int32 -_isspace_wrapper(wasm_exec_env_t exec_env, int32 c) +isspace_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isspace(c); } static int32 -_isgraph_wrapper(wasm_exec_env_t exec_env, int32 c) +isgraph_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isgraph(c); } static int32 -_isprint_wrapper(wasm_exec_env_t exec_env, int32 c) +isprint_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isprint(c); } static int32 -_isdigit_wrapper(wasm_exec_env_t exec_env, int32 c) +isdigit_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isdigit(c); } static int32 -_isxdigit_wrapper(wasm_exec_env_t exec_env, int32 c) +isxdigit_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isxdigit(c); } static int32 -_tolower_wrapper(wasm_exec_env_t exec_env, int32 c) +tolower_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return tolower(c); } static int32 -_toupper_wrapper(wasm_exec_env_t exec_env, int32 c) +toupper_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return toupper(c); } static int32 -_isalnum_wrapper(wasm_exec_env_t exec_env, int32 c) +isalnum_wrapper(wasm_exec_env_t exec_env, int32 c) { + (void)exec_env; + return isalnum(c); } -static void -setTempRet0_wrapper(wasm_exec_env_t exec_env, - uint32 temp_ret) +static uint32 +emscripten_memcpy_big_wrapper(wasm_exec_env_t exec_env, void *dst, + const void *src, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasm_runtime_set_temp_ret(module_inst, temp_ret); + uint32 dst_offset = (uint32)addr_native_to_app(dst); + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, (uint64)size)) + return dst_offset; + + bh_memcpy_s(dst, size, src, size); + return dst_offset; } -static uint32 -getTempRet0_wrapper(wasm_exec_env_t exec_env) +static void +abort_wrapper(wasm_exec_env_t exec_env, int32 code) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - return wasm_runtime_get_temp_ret(module_inst); + char buf[32]; + snprintf(buf, sizeof(buf), "env.abort(%" PRId32 ")", code); + wasm_runtime_set_exception(module_inst, buf); } -static uint32 -_llvm_bswap_i16_wrapper(wasm_exec_env_t exec_env, - uint32 data) +static void +abortStackOverflow_wrapper(wasm_exec_env_t exec_env, int32 code) { - return (data & 0xFFFF0000) - | ((data & 0xFF) << 8) - | ((data & 0xFF00) >> 8); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + snprintf(buf, sizeof(buf), "env.abortStackOverflow(%" PRId32 ")", code); + wasm_runtime_set_exception(module_inst, buf); } -static uint32 -_llvm_bswap_i32_wrapper(wasm_exec_env_t exec_env, - uint32 data) +static void +nullFunc_X_wrapper(wasm_exec_env_t exec_env, int32 code) { - return ((data & 0xFF) << 24) - | ((data & 0xFF00) << 8) - | ((data & 0xFF0000) >> 8) - | ((data & 0xFF000000) >> 24); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + snprintf(buf, sizeof(buf), "env.nullFunc_X(%" PRId32 ")", code); + wasm_runtime_set_exception(module_inst, buf); } static uint32 -_bitshift64Lshr_wrapper(wasm_exec_env_t exec_env, - uint32 uint64_part0, uint32 uint64_part1, - uint32 bits) +__cxa_allocate_exception_wrapper(wasm_exec_env_t exec_env, uint32 thrown_size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - union { - uint64 value; - uint32 parts[2]; - } u; - - u.parts[0] = uint64_part0; - u.parts[1] = uint64_part1; + uint32 exception = (uint32)module_malloc((uint64)thrown_size, NULL); + if (!exception) + return 0; - u.value >>= bits; - /* return low 32bit and save high 32bit to temp ret */ - wasm_runtime_set_temp_ret(module_inst, (uint32) (u.value >> 32)); - return (uint32) u.value; + return exception; } -static uint32 -_bitshift64Shl_wrapper(wasm_exec_env_t exec_env, - uint32 int64_part0, uint32 int64_part1, - uint32 bits) +static void +__cxa_begin_catch_wrapper(wasm_exec_env_t exec_env, void *exception_object) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - union { - int64 value; - uint32 parts[2]; - } u; - - u.parts[0] = int64_part0; - u.parts[1] = int64_part1; - - u.value <<= bits; - /* return low 32bit and save high 32bit to temp ret */ - wasm_runtime_set_temp_ret(module_inst, (uint32) (u.value >> 32)); - return (uint32) u.value; + (void)exec_env; + (void)exception_object; } static void -_llvm_stackrestore_wrapper(wasm_exec_env_t exec_env, - uint32 llvm_stack) +__cxa_throw_wrapper(wasm_exec_env_t exec_env, void *thrown_exception, + void *tinfo, uint32 table_elem_idx) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - bh_printf("_llvm_stackrestore called!\n"); - wasm_runtime_set_llvm_stack(module_inst, llvm_stack); + char buf[32]; + + (void)thrown_exception; + (void)tinfo; + (void)table_elem_idx; + + snprintf(buf, sizeof(buf), "%s", "exception thrown by stdc++"); + wasm_runtime_set_exception(module_inst, buf); } +struct timespec_app { + int64 tv_sec; + int32 tv_nsec; +}; + static uint32 -_llvm_stacksave_wrapper(wasm_exec_env_t exec_env) +clock_gettime_wrapper(wasm_exec_env_t exec_env, uint32 clk_id, + struct timespec_app *ts_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - bh_printf("_llvm_stacksave called!\n"); - return wasm_runtime_get_llvm_stack(module_inst); -} + uint64 time; -static int32 -_emscripten_memcpy_big_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, - uint32 size) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *dst, *src; + (void)clk_id; - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) - return dst_offset; + if (!validate_native_addr(ts_app, (uint64)sizeof(struct timespec_app))) + return (uint32)-1; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); + time = os_time_get_boot_us(); + ts_app->tv_sec = time / 1000000; + ts_app->tv_nsec = (time % 1000000) * 1000; - bh_memcpy_s(dst, size, src, size); - return dst_offset; + return (uint32)0; } -static void -abort_wrapper(wasm_exec_env_t exec_env, - int32 code) +static uint64 +clock_wrapper(wasm_exec_env_t exec_env) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char buf[32]; - snprintf(buf, sizeof(buf), "env.abort(%i)", code); - wasm_runtime_set_exception(module_inst, buf); + (void)exec_env; + + /* Convert to nano seconds as CLOCKS_PER_SEC in wasi-sdk */ + + return os_time_get_boot_us() * 1000; } +#if WASM_ENABLE_SPEC_TEST != 0 static void -abortStackOverflow_wrapper(wasm_exec_env_t exec_env, - int32 code) +print_wrapper(wasm_exec_env_t exec_env) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char buf[32]; - snprintf(buf, sizeof(buf), "env.abortStackOverflow(%i)", code); - wasm_runtime_set_exception(module_inst, buf); + os_printf("in specttest.print()\n"); } static void -nullFunc_X_wrapper(wasm_exec_env_t exec_env, - int32 code) +print_i32_wrapper(wasm_exec_env_t exec_env, int32 i32) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char buf[32]; - snprintf(buf, sizeof(buf), "env.nullFunc_X(%i)", code); - wasm_runtime_set_exception(module_inst, buf); + os_printf("in specttest.print_i32(%" PRId32 ")\n", i32); } -static int32 -__cxa_allocate_exception_wrapper(wasm_exec_env_t exec_env, - uint32 thrown_size) +static void +print_i64_wrapper(wasm_exec_env_t exec_env, int64 i64) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - int32 exception = module_malloc(thrown_size); - if (!exception) - return 0; - - return exception; + os_printf("in specttest.print_i64(%" PRId64 ")\n", i64); } static void -__cxa_begin_catch_wrapper(wasm_exec_env_t exec_env, - int32 exception_object_offset) +print_i32_f32_wrapper(wasm_exec_env_t exec_env, int32 i32, float f32) { - + os_printf("in specttest.print_i32_f32(%" PRId32 ", %f)\n", i32, + (double)f32); } static void -__cxa_throw_wrapper(wasm_exec_env_t exec_env, - int32 thrown_exception_offset, - int32 tinfo_offset, - uint32 table_elem_idx) +print_f64_f64_wrapper(wasm_exec_env_t exec_env, double f64_1, double f64_2) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char buf[32]; - - snprintf(buf, sizeof(buf), "%s", "exception thrown by stdc++"); - wasm_runtime_set_exception(module_inst, buf); + os_printf("in specttest.print_f64_f64(%f, %f)\n", f64_1, f64_2); } -#ifndef ENABLE_SPEC_TEST -#define ENABLE_SPEC_TEST 0 -#endif - -#if ENABLE_SPEC_TEST != 0 static void -print_i32_wrapper(wasm_exec_env_t exec_env, int32 i32) +print_f32_wrapper(wasm_exec_env_t exec_env, float f32) { - bh_printf("%d\n", i32); + os_printf("in specttest.print_f32(%f)\n", (double)f32); } -#endif - -/* TODO: add function parameter/result types check */ -#define REG_NATIVE_FUNC(module_name, func_name) \ - { #module_name, #func_name, func_name##_wrapper } - -typedef struct WASMNativeFuncDef { - const char *module_name; - const char *func_name; - void *func_ptr; -} WASMNativeFuncDef; -static WASMNativeFuncDef native_func_defs[] = { -#if ENABLE_SPEC_TEST != 0 - REG_NATIVE_FUNC(spectest, print_i32), -#endif - REG_NATIVE_FUNC(env, _printf), - REG_NATIVE_FUNC(env, _sprintf), - REG_NATIVE_FUNC(env, _snprintf), - REG_NATIVE_FUNC(env, _puts), - REG_NATIVE_FUNC(env, _putchar), - REG_NATIVE_FUNC(env, _memcmp), - REG_NATIVE_FUNC(env, _memcpy), - REG_NATIVE_FUNC(env, _memmove), - REG_NATIVE_FUNC(env, _memset), - REG_NATIVE_FUNC(env, _strchr), - REG_NATIVE_FUNC(env, _strcmp), - REG_NATIVE_FUNC(env, _strcpy), - REG_NATIVE_FUNC(env, _strlen), - REG_NATIVE_FUNC(env, _strncmp), - REG_NATIVE_FUNC(env, _strncpy), - REG_NATIVE_FUNC(env, _malloc), - REG_NATIVE_FUNC(env, _calloc), - REG_NATIVE_FUNC(env, _strdup), +static void +print_f64_wrapper(wasm_exec_env_t exec_env, double f64) +{ + os_printf("in specttest.print_f64(%f)\n", f64); +} +#endif /* WASM_ENABLE_SPEC_TEST */ + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_libc_builtin[] = { + REG_NATIVE_FUNC(printf, "($*)i"), + REG_NATIVE_FUNC(sprintf, "($$*)i"), + REG_NATIVE_FUNC(snprintf, "(*~$*)i"), + { "vprintf", printf_wrapper, "($*)i", NULL }, + { "vsprintf", sprintf_wrapper, "($$*)i", NULL }, + { "vsnprintf", snprintf_wrapper, "(*~$*)i", NULL }, + REG_NATIVE_FUNC(puts, "($)i"), + REG_NATIVE_FUNC(putchar, "(i)i"), + REG_NATIVE_FUNC(memcmp, "(**~)i"), + REG_NATIVE_FUNC(memcpy, "(**~)i"), + REG_NATIVE_FUNC(memmove, "(**~)i"), + REG_NATIVE_FUNC(memset, "(*ii)i"), + REG_NATIVE_FUNC(strchr, "($i)i"), + REG_NATIVE_FUNC(strcmp, "($$)i"), + REG_NATIVE_FUNC(strcpy, "(*$)i"), + REG_NATIVE_FUNC(strlen, "($)i"), + REG_NATIVE_FUNC(strncmp, "(**~)i"), + REG_NATIVE_FUNC(strncpy, "(**~)i"), + REG_NATIVE_FUNC(malloc, "(i)i"), + REG_NATIVE_FUNC(realloc, "(ii)i"), + REG_NATIVE_FUNC(calloc, "(ii)i"), + REG_NATIVE_FUNC(strdup, "($)i"), /* clang may introduce __strdup */ - REG_NATIVE_FUNC(env, __strdup), - REG_NATIVE_FUNC(env, _free), - REG_NATIVE_FUNC(env, _atoi), - REG_NATIVE_FUNC(env, _bsearch), - REG_NATIVE_FUNC(env, _exit), - REG_NATIVE_FUNC(env, _strtol), - REG_NATIVE_FUNC(env, _strtoul), - REG_NATIVE_FUNC(env, _memchr), - REG_NATIVE_FUNC(env, _strncasecmp), - REG_NATIVE_FUNC(env, _strspn), - REG_NATIVE_FUNC(env, _strcspn), - REG_NATIVE_FUNC(env, _strstr), - REG_NATIVE_FUNC(env, _isupper), - REG_NATIVE_FUNC(env, _isalpha), - REG_NATIVE_FUNC(env, _isspace), - REG_NATIVE_FUNC(env, _isgraph), - REG_NATIVE_FUNC(env, _isprint), - REG_NATIVE_FUNC(env, _isdigit), - REG_NATIVE_FUNC(env, _isxdigit), - REG_NATIVE_FUNC(env, _tolower), - REG_NATIVE_FUNC(env, _toupper), - REG_NATIVE_FUNC(env, _isalnum), - REG_NATIVE_FUNC(env, setTempRet0), - REG_NATIVE_FUNC(env, getTempRet0), - REG_NATIVE_FUNC(env, _llvm_bswap_i16), - REG_NATIVE_FUNC(env, _llvm_bswap_i32), - REG_NATIVE_FUNC(env, _bitshift64Lshr), - REG_NATIVE_FUNC(env, _bitshift64Shl), - REG_NATIVE_FUNC(env, _llvm_stackrestore), - REG_NATIVE_FUNC(env, _llvm_stacksave), - REG_NATIVE_FUNC(env, _emscripten_memcpy_big), - REG_NATIVE_FUNC(env, abort), - REG_NATIVE_FUNC(env, abortStackOverflow), - REG_NATIVE_FUNC(env, nullFunc_X), - REG_NATIVE_FUNC(env, __cxa_allocate_exception), - REG_NATIVE_FUNC(env, __cxa_begin_catch), - REG_NATIVE_FUNC(env, __cxa_throw) + REG_NATIVE_FUNC(_strdup, "($)i"), + REG_NATIVE_FUNC(free, "(*)"), + REG_NATIVE_FUNC(atoi, "($)i"), + REG_NATIVE_FUNC(exit, "(i)"), + REG_NATIVE_FUNC(strtol, "($*i)i"), + REG_NATIVE_FUNC(strtoul, "($*i)i"), + REG_NATIVE_FUNC(memchr, "(*ii)i"), + REG_NATIVE_FUNC(strncasecmp, "($$i)i"), + REG_NATIVE_FUNC(strspn, "($$)i"), + REG_NATIVE_FUNC(strcspn, "($$)i"), + REG_NATIVE_FUNC(strstr, "($$)i"), + REG_NATIVE_FUNC(isupper, "(i)i"), + REG_NATIVE_FUNC(isalpha, "(i)i"), + REG_NATIVE_FUNC(isspace, "(i)i"), + REG_NATIVE_FUNC(isgraph, "(i)i"), + REG_NATIVE_FUNC(isprint, "(i)i"), + REG_NATIVE_FUNC(isdigit, "(i)i"), + REG_NATIVE_FUNC(isxdigit, "(i)i"), + REG_NATIVE_FUNC(tolower, "(i)i"), + REG_NATIVE_FUNC(toupper, "(i)i"), + REG_NATIVE_FUNC(isalnum, "(i)i"), + REG_NATIVE_FUNC(emscripten_memcpy_big, "(**~)i"), + REG_NATIVE_FUNC(abort, "(i)"), + REG_NATIVE_FUNC(abortStackOverflow, "(i)"), + REG_NATIVE_FUNC(nullFunc_X, "(i)"), + REG_NATIVE_FUNC(__cxa_allocate_exception, "(i)i"), + REG_NATIVE_FUNC(__cxa_begin_catch, "(*)"), + REG_NATIVE_FUNC(__cxa_throw, "(**i)"), + REG_NATIVE_FUNC(clock_gettime, "(i*)i"), + REG_NATIVE_FUNC(clock, "()I"), }; -void * -wasm_native_lookup_libc_builtin_func(const char *module_name, - const char *func_name) -{ - uint32 size = sizeof(native_func_defs) / sizeof(WASMNativeFuncDef); - WASMNativeFuncDef *func_def = native_func_defs; - WASMNativeFuncDef *func_def_end = func_def + size; - - if (!module_name || !func_name) - return NULL; - - while (func_def < func_def_end) { - if (!strcmp(func_def->module_name, module_name) - && (!strcmp(func_def->func_name, func_name) - || (func_def->func_name[0] == '_' - && !strcmp(func_def->func_name + 1, func_name)))) - return (void*) (uintptr_t) func_def->func_ptr; - func_def++; - } +#if WASM_ENABLE_SPEC_TEST != 0 +static NativeSymbol native_symbols_spectest[] = { + REG_NATIVE_FUNC(print, "()"), + REG_NATIVE_FUNC(print_i32, "(i)"), + REG_NATIVE_FUNC(print_i64, "(I)"), + REG_NATIVE_FUNC(print_i32_f32, "(if)"), + REG_NATIVE_FUNC(print_f64_f64, "(FF)"), + REG_NATIVE_FUNC(print_f32, "(f)"), + REG_NATIVE_FUNC(print_f64, "(F)") +}; +#endif - return NULL; +uint32 +get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis) +{ + *p_libc_builtin_apis = native_symbols_libc_builtin; + return sizeof(native_symbols_libc_builtin) / sizeof(NativeSymbol); } -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 +#if WASM_ENABLE_SPEC_TEST != 0 +uint32 +get_spectest_export_apis(NativeSymbol **p_libc_builtin_apis) +{ + *p_libc_builtin_apis = native_symbols_spectest; + return sizeof(native_symbols_spectest) / sizeof(NativeSymbol); +} +#endif /************************************* * Global Variables * @@ -1291,28 +1133,29 @@ wasm_native_lookup_libc_builtin_func(const char *module_name, typedef struct WASMNativeGlobalDef { const char *module_name; const char *global_name; - WASMValue global_data; + uint8 type; + bool is_mutable; + WASMValue value; } WASMNativeGlobalDef; static WASMNativeGlobalDef native_global_defs[] = { -#if ENABLE_SPEC_TEST != 0 - { "spectest", "global_i32", .global_data.i32 = 666 }, - { "spectest", "global_f32", .global_data.f32 = 0 }, - { "spectest", "global_f64", .global_data.f64 = 0 }, - { "test", "global-i32", .global_data.i32 = 0 }, - { "test", "global-f32", .global_data.f32 = 0 }, +#if WASM_ENABLE_SPEC_TEST != 0 + { "spectest", "global_i32", VALUE_TYPE_I32, false, .value.i32 = 666 }, + { "spectest", "global_i64", VALUE_TYPE_I64, false, .value.i64 = 666 }, + { "spectest", "global_f32", VALUE_TYPE_F32, false, .value.f32 = 666.6 }, + { "spectest", "global_f64", VALUE_TYPE_F64, false, .value.f64 = 666.6 }, + { "test", "global-i32", VALUE_TYPE_I32, false, .value.i32 = 0 }, + { "test", "global-f32", VALUE_TYPE_F32, false, .value.f32 = 0 }, + { "test", "global-mut-i32", VALUE_TYPE_I32, true, .value.i32 = 0 }, + { "test", "global-mut-i64", VALUE_TYPE_I64, true, .value.i64 = 0 }, + { "test", "g", VALUE_TYPE_I32, true, .value.i32 = 0 }, +#if WASM_ENABLE_GC != 0 + { "G", "g", VALUE_TYPE_I32, false, .value.i32 = 4 }, + { "M", "g", REF_TYPE_HT_NON_NULLABLE, false, .value.gc_obj = 0 }, +#endif #endif - { "env", "STACKTOP", .global_data.u32 = 0 }, - { "env", "STACK_MAX", .global_data.u32 = 0 }, - { "env", "ABORT", .global_data.u32 = 0 }, - { "env", "memoryBase", .global_data.u32 = 0 }, - { "env", "__memory_base", .global_data.u32 = 0 }, - { "env", "tableBase", .global_data.u32 = 0 }, - { "env", "__table_base", .global_data.u32 = 0 }, - { "env", "DYNAMICTOP_PTR", .global_data.addr = 0 }, - { "env", "tempDoublePtr", .global_data.addr = 0 }, - { "global", "NaN", .global_data.u64 = 0x7FF8000000000000LL }, - { "global", "Infinity", .global_data.u64 = 0x7FF0000000000000LL } + { "global", "NaN", VALUE_TYPE_F64, .value.u64 = 0x7FF8000000000000LL }, + { "global", "Infinity", VALUE_TYPE_F64, .value.u64 = 0x7FF0000000000000LL } }; bool @@ -1331,7 +1174,9 @@ wasm_native_lookup_libc_builtin_global(const char *module_name, while (global_def < global_def_end) { if (!strcmp(global_def->module_name, module_name) && !strcmp(global_def->global_name, global_name)) { - global->global_data_linked = global_def->global_data; + global->type.val_type = global_def->type; + global->type.is_mutable = global_def->is_mutable; + global->global_data_linked = global_def->value; return true; } global_def++; @@ -1339,6 +1184,3 @@ wasm_native_lookup_libc_builtin_global(const char *module_name, return false; } - -#endif /* end of WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 */ - diff --git a/core/iwasm/libraries/libc-emcc/SConscript b/core/iwasm/libraries/libc-emcc/SConscript new file mode 100644 index 0000000000..432ed4a058 --- /dev/null +++ b/core/iwasm/libraries/libc-emcc/SConscript @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() + +src = Split(''' +libc_emcc_wrapper.c +''') + +CPPPATH = [cwd] + +group = DefineGroup('iwasm_libc_emcc', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/iwasm/libraries/libc-emcc/libc_emcc.cmake b/core/iwasm/libraries/libc-emcc/libc_emcc.cmake new file mode 100644 index 0000000000..d237a16eea --- /dev/null +++ b/core/iwasm/libraries/libc-emcc/libc_emcc.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIBC_EMCC_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIBC_EMCC=1) + +include_directories(${LIBC_EMCC_DIR}) + +file (GLOB source_all ${LIBC_EMCC_DIR}/*.c) + +set (LIBC_EMCC_SOURCE ${source_all}) \ 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 new file mode 100644 index 0000000000..89ffeaaa4f --- /dev/null +++ b/core/iwasm/libraries/libc-emcc/libc_emcc_wrapper.c @@ -0,0 +1,596 @@ +/* + * Copyright (C) 2020 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" + +#if defined(__linux__) +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +#define HAVE_SYSCALL_GETRANDOM +#include +#endif +#endif + +/* clang-format off */ +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define validate_app_addr(offset, size) \ + wasm_runtime_validate_app_addr(module_inst, offset, size) + +#define validate_app_str_addr(offset) \ + wasm_runtime_validate_app_str_addr(module_inst, offset) + +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + +#define addr_app_to_native(offset) \ + wasm_runtime_addr_app_to_native(module_inst, offset) + +#define addr_native_to_app(ptr) \ + wasm_runtime_addr_native_to_app(module_inst, ptr) + +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) + +#define module_free(offset) \ + wasm_runtime_module_free(module_inst, offset) +/* clang-format on */ + +static void +invoke_viiii_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0, + int arg1, int arg2, int arg3) +{ + uint32 argv[4]; + bool ret; + + argv[0] = arg0; + argv[1] = arg1; + argv[2] = arg2; + argv[3] = arg3; + ret = wasm_runtime_call_indirect(exec_env, elem_idx, 4, argv); + (void)ret; +} + +static void +invoke_viii_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0, + int arg1, int arg2) +{ + uint32 argv[4]; + bool ret; + + argv[0] = arg0; + argv[1] = arg1; + argv[2] = arg2; + ret = wasm_runtime_call_indirect(exec_env, elem_idx, 3, argv); + (void)ret; +} + +static void +invoke_vii_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0, + int arg1) +{ + uint32 argv[4]; + bool ret; + + argv[0] = arg0; + argv[1] = arg1; + ret = wasm_runtime_call_indirect(exec_env, elem_idx, 2, argv); + (void)ret; +} + +static void +invoke_vi_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0) +{ + uint32 argv[4]; + bool ret; + + argv[0] = arg0; + ret = wasm_runtime_call_indirect(exec_env, elem_idx, 1, argv); + (void)ret; +} + +static int +invoke_iii_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0, + int arg1) +{ + uint32 argv[4]; + bool ret; + + argv[0] = arg0; + argv[1] = arg1; + ret = wasm_runtime_call_indirect(exec_env, elem_idx, 2, argv); + return ret ? argv[0] : 0; +} + +static int +invoke_ii_wrapper(wasm_exec_env_t exec_env, uint32 elem_idx, int arg0) +{ + uint32 argv[4]; + bool ret; + + argv[0] = arg0; + ret = wasm_runtime_call_indirect(exec_env, elem_idx, 1, argv); + return ret ? argv[0] : 0; +} + +struct timespec_emcc { + int tv_sec; + int tv_nsec; +}; + +struct stat_emcc { + unsigned st_dev; + int __st_dev_padding; + unsigned __st_ino_truncated; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + int __st_rdev_padding; + int64 st_size; + int st_blksize; + int st_blocks; + struct timespec_emcc st_atim; + struct timespec_emcc st_mtim; + struct timespec_emcc st_ctim; + int64 st_ino; +}; + +static int +open_wrapper(wasm_exec_env_t exec_env, const char *pathname, int flags, + int mode) +{ + if (pathname == NULL) + return -1; + return open(pathname, flags, mode); +} + +static int +__sys_read_wrapper(wasm_exec_env_t exec_env, int fd, void *buf, uint32 count) +{ + return read(fd, buf, count); +} + +static void +statbuf_native2app(const struct stat *statbuf_native, + struct stat_emcc *statbuf_app) +{ + statbuf_app->st_dev = (unsigned)statbuf_native->st_dev; + statbuf_app->__st_ino_truncated = (unsigned)statbuf_native->st_ino; + statbuf_app->st_mode = (unsigned)statbuf_native->st_mode; + statbuf_app->st_nlink = (unsigned)statbuf_native->st_nlink; + statbuf_app->st_uid = (unsigned)statbuf_native->st_uid; + statbuf_app->st_gid = (unsigned)statbuf_native->st_gid; + statbuf_app->st_rdev = (unsigned)statbuf_native->st_rdev; + statbuf_app->st_size = (int64)statbuf_native->st_size; + statbuf_app->st_blksize = (unsigned)statbuf_native->st_blksize; + statbuf_app->st_blocks = (unsigned)statbuf_native->st_blocks; + statbuf_app->st_ino = (int64)statbuf_native->st_ino; +#if defined(__APPLE__) + statbuf_app->st_atim.tv_sec = (int)statbuf_native->st_atimespec.tv_sec; + statbuf_app->st_atim.tv_nsec = (int)statbuf_native->st_atimespec.tv_nsec; + statbuf_app->st_mtim.tv_sec = (int)statbuf_native->st_mtimespec.tv_sec; + statbuf_app->st_mtim.tv_nsec = (int)statbuf_native->st_mtimespec.tv_nsec; + statbuf_app->st_ctim.tv_sec = (int)statbuf_native->st_ctimespec.tv_sec; + statbuf_app->st_ctim.tv_nsec = (int)statbuf_native->st_ctimespec.tv_nsec; +#else + statbuf_app->st_atim.tv_sec = (int)statbuf_native->st_atim.tv_sec; + statbuf_app->st_atim.tv_nsec = (int)statbuf_native->st_atim.tv_nsec; + statbuf_app->st_mtim.tv_sec = (int)statbuf_native->st_mtim.tv_sec; + statbuf_app->st_mtim.tv_nsec = (int)statbuf_native->st_mtim.tv_nsec; + statbuf_app->st_ctim.tv_sec = (int)statbuf_native->st_ctim.tv_sec; + statbuf_app->st_ctim.tv_nsec = (int)statbuf_native->st_ctim.tv_nsec; +#endif +} + +static int +__sys_stat64_wrapper(wasm_exec_env_t exec_env, const char *pathname, + struct stat_emcc *statbuf_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + int ret; + struct stat statbuf; + + if (!validate_native_addr((void *)statbuf_app, + (uint64)sizeof(struct stat_emcc))) + return -1; + + if (pathname == NULL) + return -1; + + ret = stat(pathname, &statbuf); + if (ret == 0) + statbuf_native2app(&statbuf, statbuf_app); + return ret; +} + +static int +__sys_fstat64_wrapper(wasm_exec_env_t exec_env, int fd, + struct stat_emcc *statbuf_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + int ret; + struct stat statbuf; + + if (!validate_native_addr((void *)statbuf_app, + (uint64)sizeof(struct stat_emcc))) + return -1; + + if (fd <= 0) + return -1; + + ret = fstat(fd, &statbuf); + if (ret == 0) + statbuf_native2app(&statbuf, statbuf_app); + return ret; +} + +static int +mmap_wrapper(wasm_exec_env_t exec_env, void *addr, int length, int prot, + int flags, int fd, int64 offset) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uint32 buf_offset; + char *buf; + int size_read; + + buf_offset = module_malloc((uint64)length, (void **)&buf); + if (buf_offset == 0) + return -1; + + if (fd <= 0) { + module_free((uint64)buf_offset); + return -1; + } + + if (lseek(fd, offset, SEEK_SET) == -1) { + module_free((uint64)buf_offset); + return -1; + } + + size_read = read(fd, buf, length); + if (size_read < 0) { + module_free((uint64)buf_offset); + return -1; + } + return buf_offset; +} + +static int +munmap_wrapper(wasm_exec_env_t exec_env, uint32 buf_offset, int length) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + module_free((uint64)buf_offset); + return 0; +} + +static int +__munmap_wrapper(wasm_exec_env_t exec_env, uint32 buf_offset, int length) +{ + return munmap_wrapper(exec_env, buf_offset, length); +} + +static int +getentropy_wrapper(wasm_exec_env_t exec_env, void *buffer, uint32 length) +{ + if (buffer == NULL) + return -1; +#if defined(HAVE_SYSCALL_GETRANDOM) + return syscall(SYS_getrandom, buffer, length, 0); +#else + return getentropy(buffer, length); +#endif +} + +static int +setjmp_wrapper(wasm_exec_env_t exec_env, void *jmp_buf) +{ + LOG_DEBUG("setjmp() called\n"); + return 0; +} + +static void +longjmp_wrapper(wasm_exec_env_t exec_env, void *jmp_buf, int val) +{ + LOG_DEBUG("longjmp() called\n"); +} + +#if !defined(BH_PLATFORM_LINUX_SGX) +static FILE *file_list[32] = { 0 }; + +static int +get_free_file_slot() +{ + unsigned int i; + + for (i = 0; i < sizeof(file_list) / sizeof(FILE *); i++) { + if (file_list[i] == NULL) + return (int)i; + } + return -1; +} + +static int +fopen_wrapper(wasm_exec_env_t exec_env, const char *pathname, const char *mode) +{ + FILE *file; + int file_id; + + if (pathname == NULL || mode == NULL) + return 0; + + if ((file_id = get_free_file_slot()) == -1) + return 0; + + file = fopen(pathname, mode); + if (!file) + return 0; + + file_list[file_id] = file; + return file_id + 1; +} + +static uint32 +fread_wrapper(wasm_exec_env_t exec_env, void *ptr, uint32 size, uint32 nmemb, + int file_id) +{ + FILE *file; + + file_id = file_id - 1; + if ((unsigned)file_id >= sizeof(file_list) / sizeof(FILE *)) { + return 0; + } + if ((file = file_list[file_id]) == NULL) { + return 0; + } + return (uint32)fread(ptr, size, nmemb, file); +} + +static int +fseeko_wrapper(wasm_exec_env_t exec_env, int file_id, int64 offset, int whence) +{ + FILE *file; + + file_id = file_id - 1; + if ((unsigned)file_id >= sizeof(file_list) / sizeof(FILE *)) { + return -1; + } + if ((file = file_list[file_id]) == NULL) { + return -1; + } + return (uint32)fseek(file, offset, whence); +} + +static uint32 +emcc_fwrite_wrapper(wasm_exec_env_t exec_env, const void *ptr, uint32 size, + uint32 nmemb, int file_id) +{ + FILE *file; + + file_id = file_id - 1; + if ((unsigned)file_id >= sizeof(file_list) / sizeof(FILE *)) { + return 0; + } + if ((file = file_list[file_id]) == NULL) { + return 0; + } + return (uint32)fwrite(ptr, size, nmemb, file); +} + +static int +feof_wrapper(wasm_exec_env_t exec_env, int file_id) +{ + FILE *file; + + file_id = file_id - 1; + if ((unsigned)file_id >= sizeof(file_list) / sizeof(FILE *)) + return 1; + if ((file = file_list[file_id]) == NULL) + return 1; + return feof(file); +} + +static int +fclose_wrapper(wasm_exec_env_t exec_env, int file_id) +{ + FILE *file; + + file_id = file_id - 1; + if ((unsigned)file_id >= sizeof(file_list) / sizeof(FILE *)) + return -1; + if ((file = file_list[file_id]) == NULL) + return -1; + file_list[file_id] = NULL; + return fclose(file); +} + +static int +__sys_mkdir_wrapper(wasm_exec_env_t exec_env, const char *pathname, int mode) +{ + if (!pathname) + return -1; + return mkdir(pathname, mode); +} + +static int +__sys_rmdir_wrapper(wasm_exec_env_t exec_env, const char *pathname) +{ + if (!pathname) + return -1; + return rmdir(pathname); +} + +static int +__sys_unlink_wrapper(wasm_exec_env_t exec_env, const char *pathname) +{ + if (!pathname) + return -1; + return unlink(pathname); +} + +static uint32 +__sys_getcwd_wrapper(wasm_exec_env_t exec_env, char *buf, uint32 size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char *ret; + + if (!buf) + return -1; + + ret = getcwd(buf, size); + return ret ? (uint32)addr_native_to_app(ret) : 0; +} + +#include + +struct utsname_app { + char sysname[64]; + char nodename[64]; + char release[64]; + char version[64]; + char machine[64]; + char domainname[64]; +}; + +static int +__sys_uname_wrapper(wasm_exec_env_t exec_env, struct utsname_app *uname_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + struct utsname uname_native = { 0 }; + uint32 length; + + if (!validate_native_addr(uname_app, (uint64)sizeof(struct utsname_app))) + return -1; + + if (uname(&uname_native) != 0) { + return -1; + } + + memset(uname_app, 0, sizeof(struct utsname_app)); + + length = strlen(uname_native.sysname); + if (length > sizeof(uname_app->sysname) - 1) + length = sizeof(uname_app->sysname) - 1; + bh_memcpy_s(uname_app->sysname, sizeof(uname_app->sysname), + uname_native.sysname, length); + + length = strlen(uname_native.nodename); + if (length > sizeof(uname_app->nodename) - 1) + length = sizeof(uname_app->nodename) - 1; + bh_memcpy_s(uname_app->nodename, sizeof(uname_app->nodename), + uname_native.nodename, length); + + length = strlen(uname_native.release); + if (length > sizeof(uname_app->release) - 1) + length = sizeof(uname_app->release) - 1; + bh_memcpy_s(uname_app->release, sizeof(uname_app->release), + uname_native.release, length); + + length = strlen(uname_native.version); + if (length > sizeof(uname_app->version) - 1) + length = sizeof(uname_app->version) - 1; + bh_memcpy_s(uname_app->version, sizeof(uname_app->version), + uname_native.version, length); + +#ifdef _GNU_SOURCE + length = strlen(uname_native.domainname); + if (length > sizeof(uname_app->domainname) - 1) + length = sizeof(uname_app->domainname) - 1; + bh_memcpy_s(uname_app->domainname, sizeof(uname_app->domainname), + uname_native.domainname, length); +#endif + + return 0; +} + +static void +emscripten_notify_memory_growth_wrapper(wasm_exec_env_t exec_env, int i) +{ + (void)i; +} + +static void +emscripten_sleep_wrapper(wasm_exec_env_t exec_env, int timeout_ms) +{ + unsigned int sec; + useconds_t us; + + if (timeout_ms <= 0) + return; + + sec = timeout_ms / 1000; + us = (timeout_ms % 1000) * 1000; + + if (sec > 0) + sleep(sec); + if (us > 0) + usleep(us); +} + +static void +emscripten_thread_sleep_wrapper(wasm_exec_env_t exec_env, double timeout_ms) +{ + uint64 ms = (uint64)timeout_ms; + uint64 sec = ms / 1000, us = (ms % 1000) * 1000; + + if (sec > 0) + sleep(sec); + if (us > 0) + usleep(us); +} + +#endif /* end of BH_PLATFORM_LINUX_SGX */ + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format off */ + +static NativeSymbol native_symbols_libc_emcc[] = { + REG_NATIVE_FUNC(invoke_viiii, "(iiiii)"), + REG_NATIVE_FUNC(invoke_viii, "(iiii)"), + REG_NATIVE_FUNC(invoke_vii, "(iii)"), + REG_NATIVE_FUNC(invoke_vi, "(ii)"), + REG_NATIVE_FUNC(invoke_iii, "(iii)i"), + REG_NATIVE_FUNC(invoke_ii, "(ii)i"), + REG_NATIVE_FUNC(open, "($ii)i"), + REG_NATIVE_FUNC(__sys_read, "(i*~)i"), + REG_NATIVE_FUNC(__sys_stat64, "($*)i"), + REG_NATIVE_FUNC(__sys_fstat64, "(i*)i"), + REG_NATIVE_FUNC(mmap, "(*iiiiI)i"), + REG_NATIVE_FUNC(munmap, "(ii)i"), + REG_NATIVE_FUNC(__munmap, "(ii)i"), + REG_NATIVE_FUNC(getentropy, "(*~)i"), + REG_NATIVE_FUNC(setjmp, "(*)i"), + REG_NATIVE_FUNC(longjmp, "(*i)"), +#if !defined(BH_PLATFORM_LINUX_SGX) + REG_NATIVE_FUNC(fopen, "($$)i"), + REG_NATIVE_FUNC(fread, "(*iii)i"), + REG_NATIVE_FUNC(fseeko, "(iIi)i"), + REG_NATIVE_FUNC(emcc_fwrite, "(*iii)i"), + REG_NATIVE_FUNC(feof, "(i)i"), + REG_NATIVE_FUNC(fclose, "(i)i"), + REG_NATIVE_FUNC(__sys_mkdir, "($i)i"), + REG_NATIVE_FUNC(__sys_rmdir, "($)i"), + REG_NATIVE_FUNC(__sys_unlink, "($)i"), + REG_NATIVE_FUNC(__sys_getcwd, "(*~)i"), + REG_NATIVE_FUNC(__sys_uname, "(*)i"), + REG_NATIVE_FUNC(emscripten_notify_memory_growth, "(i)"), + REG_NATIVE_FUNC(emscripten_sleep, "(i)"), + REG_NATIVE_FUNC(emscripten_thread_sleep, "(F)"), +#endif /* end of BH_PLATFORM_LINUX_SGX */ +}; + +uint32 +get_libc_emcc_export_apis(NativeSymbol **p_libc_emcc_apis) +{ + *p_libc_emcc_apis = native_symbols_libc_emcc; + return sizeof(native_symbols_libc_emcc) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/libc-uvwasi/FindLIBUV.cmake b/core/iwasm/libraries/libc-uvwasi/FindLIBUV.cmake new file mode 100644 index 0000000000..7949f06f38 --- /dev/null +++ b/core/iwasm/libraries/libc-uvwasi/FindLIBUV.cmake @@ -0,0 +1,28 @@ +# Copyright (C) 2023 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Find libuv library +# This module defines +# LIBUV_FOUND, if false, do not try to link to libuv +# LIBUV_LIBRARIES +# LIBUV_INCLUDE_DIR, where to find uv.h + +find_path(LIBUV_INCLUDE_DIR NAMES uv.h) +find_library(LIBUV_LIBRARIES NAMES uv libuv) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + LIBUV + FOUND_VAR LIBUV_FOUND + REQUIRED_VARS + LIBUV_LIBRARIES + LIBUV_INCLUDE_DIR +) + +if(WIN32) + list(APPEND LIBUV_LIBRARIES iphlpapi) + list(APPEND LIBUV_LIBRARIES psapi) + list(APPEND LIBUV_LIBRARIES userenv) + list(APPEND LIBUV_LIBRARIES ws2_32) +endif() diff --git a/core/iwasm/libraries/libc-uvwasi/FindUVWASI.cmake b/core/iwasm/libraries/libc-uvwasi/FindUVWASI.cmake new file mode 100644 index 0000000000..88499eaa64 --- /dev/null +++ b/core/iwasm/libraries/libc-uvwasi/FindUVWASI.cmake @@ -0,0 +1,25 @@ +# Copyright (C) 2023 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Find libuvwasi library +# This module defines +# UVWASI_FOUND, if false, do not try to link to libuvwasi +# UVWASI_LIBRARIES +# UVWASI_INCLUDE_DIR, where to find headers + +find_path(UVWASI_INCLUDE_DIR NAMES uvwasi.h wasi_serdes.h wasi_types.h PATH_SUFFIXES uvwasi) +find_library(UVWASI_LIBRARIES NAMES uvwasi_a) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + UVWASI + FOUND_VAR UVWASI_FOUND + REQUIRED_VARS + UVWASI_LIBRARIES + UVWASI_INCLUDE_DIR +) + +if(UVWASI_FOUND) + set(UVWASI_INCLUDE_DIR ${UVWASI_INCLUDE_DIR}/uvwasi) +endif() diff --git a/core/iwasm/libraries/libc-uvwasi/LICENSE_LIBUV b/core/iwasm/libraries/libc-uvwasi/LICENSE_LIBUV new file mode 100644 index 0000000000..eb126dab3a --- /dev/null +++ b/core/iwasm/libraries/libc-uvwasi/LICENSE_LIBUV @@ -0,0 +1,66 @@ +libuv is licensed for use as follows: + +==== +Copyright (c) 2015-present libuv project contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +==== + +This license applies to parts of libuv originating from the +https://github.com/joyent/libuv repository: + +==== + +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +==== + +This license applies to all parts of libuv that are not externally +maintained libraries. + +The externally maintained libraries used by libuv are: + + - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. + + - inet_pton and inet_ntop implementations, contained in src/inet.c, are + copyright the Internet Systems Consortium, Inc., and licensed under the ISC + license. + + - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three + clause BSD license. + + - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. + Three clause BSD license. diff --git a/core/iwasm/libraries/libc-uvwasi/LICENSE_UVWASI b/core/iwasm/libraries/libc-uvwasi/LICENSE_UVWASI new file mode 100644 index 0000000000..dfb8546af5 --- /dev/null +++ b/core/iwasm/libraries/libc-uvwasi/LICENSE_UVWASI @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Colin Ihrig and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/core/iwasm/libraries/libc-uvwasi/libc_uvwasi.cmake b/core/iwasm/libraries/libc-uvwasi/libc_uvwasi.cmake new file mode 100644 index 0000000000..bbcb84d392 --- /dev/null +++ b/core/iwasm/libraries/libc-uvwasi/libc_uvwasi.cmake @@ -0,0 +1,58 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +set (LIBC_WASI_DIR ${CMAKE_CURRENT_LIST_DIR}) + +set (LIBUV_VERSION v1.51.0) + +add_definitions (-DWASM_ENABLE_LIBC_WASI=1 -DWASM_ENABLE_UVWASI=1) + +include(FetchContent) + +# Point CMake at the custom modules to find libuv and uvwasi +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +## libuv +find_package(LIBUV QUIET) +if (LIBUV_FOUND) + include_directories(SYSTEM ${LIBUV_INCLUDE_DIR}) +else() + FetchContent_Declare( + libuv + GIT_REPOSITORY https://github.com/libuv/libuv.git + GIT_TAG ${LIBUV_VERSION} + ) + FetchContent_MakeAvailable(libuv) + include_directories(SYSTEM "${libuv_SOURCE_DIR}/include") + set (LIBUV_LIBRARIES uv_a) + set_target_properties(uv_a PROPERTIES POSITION_INDEPENDENT_CODE 1) +endif() + +## uvwasi +find_package(UVWASI QUIET) +if (UVWASI_FOUND) + include_directories(SYSTEM ${UVWASI_INCLUDE_DIR}) +else() + FetchContent_Declare( + uvwasi + GIT_REPOSITORY https://github.com/nodejs/uvwasi.git + GIT_TAG 392e1f1c1c8a2d2102c9f2e0b9f35959a149d133 + ) + FetchContent_MakeAvailable(uvwasi) + include_directories(SYSTEM "${uvwasi_SOURCE_DIR}/include") + set (UVWASI_LIBRARIES uvwasi_a) + set_target_properties(uvwasi_a PROPERTIES POSITION_INDEPENDENT_CODE 1) +endif() + +set (UV_A_LIBS ${LIBUV_LIBRARIES} ${UVWASI_LIBRARIES}) + +file (GLOB_RECURSE source_all ${LIBC_WASI_DIR}/*.c) + +set (LIBC_WASI_SOURCE ${source_all}) diff --git a/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c b/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c new file mode 100644 index 0000000000..35d091e78d --- /dev/null +++ b/core/iwasm/libraries/libc-uvwasi/libc_uvwasi_wrapper.c @@ -0,0 +1,1165 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "uvwasi.h" +#include "bh_platform.h" +#include "wasm_export.h" + +/* clang-format off */ +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define validate_app_addr(offset, size) \ + wasm_runtime_validate_app_addr(module_inst, offset, size) + +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + +#define addr_app_to_native(offset) \ + wasm_runtime_addr_app_to_native(module_inst, offset) + +#define addr_native_to_app(ptr) \ + wasm_runtime_addr_native_to_app(module_inst, ptr) + +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) + +#define module_free(offset) \ + wasm_runtime_module_free(module_inst, offset) +/* clang-format on */ + +// uvwasi_errno_t is typedef'd to uint16 which is correct according to the ABI +// specification. However, in WASM, the smallest integer type is int32. If we +// return uint16, we would rely on language SDKs to implement the correct +// behaviour of casting to uint16 before checking the value or using it any way. +// Failure to do so can cause tricky bugs as the upper 16 bits of the error +// result are not guaranteed to be zero'ed by us so the result essentially +// contains garbage from the WASM app perspective. To prevent this, we return +// uint32 directly instead so as not to be reliant on the correct behaviour of +// any current/future SDK implementations. +#define wasi_errno_t uint32_t +#define wasi_fd_t uvwasi_fd_t +#define wasi_clockid_t uvwasi_clockid_t +#define wasi_timestamp_t uvwasi_timestamp_t +#define wasi_filesize_t uvwasi_filesize_t +#define wasi_prestat_app_t uvwasi_prestat_app_t +#define wasi_filedelta_t uvwasi_filedelta_t +#define wasi_whence_t uvwasi_whence_t +#define wasi_fdflags_t uvwasi_fdflags_t +#define wasi_rights_t uvwasi_rights_t +#define wasi_advice_t uvwasi_advice_t +#define wasi_lookupflags_t uvwasi_lookupflags_t +#define wasi_preopentype_t uvwasi_preopentype_t +#define wasi_fdstat_t uvwasi_fdstat_t +#define wasi_oflags_t uvwasi_oflags_t +#define wasi_dircookie_t uvwasi_dircookie_t +#define wasi_filestat_t uvwasi_filestat_t +#define wasi_fstflags_t uvwasi_fstflags_t +#define wasi_subscription_t uvwasi_subscription_t +#define wasi_event_t uvwasi_event_t +#define wasi_exitcode_t uvwasi_exitcode_t +#define wasi_signal_t uvwasi_signal_t +#define wasi_riflags_t uvwasi_riflags_t +#define wasi_roflags_t uvwasi_roflags_t +#define wasi_siflags_t uvwasi_siflags_t +#define wasi_sdflags_t uvwasi_sdflags_t +#define wasi_iovec_t uvwasi_iovec_t +#define wasi_ciovec_t uvwasi_ciovec_t + +typedef struct wasi_prestat_app { + wasi_preopentype_t pr_type; + uint32 pr_name_len; +} wasi_prestat_app_t; + +typedef struct iovec_app { + uint32 buf_offset; + uint32 buf_len; +} iovec_app_t; + +typedef struct WASIContext { + uvwasi_t uvwasi; + uint32_t exit_code; +} WASIContext; + +void * +wasm_runtime_get_wasi_ctx(wasm_module_inst_t module_inst); + +static uvwasi_t * +get_wasi_ctx(wasm_module_inst_t module_inst) +{ + WASIContext *ctx = wasm_runtime_get_wasi_ctx(module_inst); + if (ctx == NULL) { + return NULL; + } + return &ctx->uvwasi; +} + +static wasi_errno_t +wasi_args_get(wasm_exec_env_t exec_env, uint32 *argv_offsets, char *argv_buf) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t argc, argv_buf_size, i; + char **argv; + uint64 total_size; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + err = uvwasi_args_sizes_get(uvwasi, &argc, &argv_buf_size); + if (err) + return err; + + total_size = sizeof(int32) * ((uint64)argc + 1); + if (total_size >= UINT32_MAX + || !validate_native_addr(argv_offsets, total_size) + || argv_buf_size >= UINT32_MAX + || !validate_native_addr(argv_buf, (uint64)argv_buf_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(char *) * ((uint64)argc + 1); + if (total_size >= UINT32_MAX + || !(argv = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + err = uvwasi_args_get(uvwasi, argv, argv_buf); + if (err) { + wasm_runtime_free(argv); + return err; + } + + for (i = 0; i < argc; i++) + argv_offsets[i] = (uint32)addr_native_to_app(argv[i]); + + wasm_runtime_free(argv); + return 0; +} + +static wasi_errno_t +wasi_args_sizes_get(wasm_exec_env_t exec_env, uint32 *argc_app, + uint32 *argv_buf_size_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t argc, argv_buf_size; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(argc_app, (uint64)sizeof(uint32)) + || !validate_native_addr(argv_buf_size_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; + + err = uvwasi_args_sizes_get(uvwasi, &argc, &argv_buf_size); + if (err) + return err; + + *argc_app = (uint32)argc; + *argv_buf_size_app = (uint32)argv_buf_size; + return 0; +} + +static wasi_errno_t +wasi_clock_res_get(wasm_exec_env_t exec_env, wasi_clockid_t clock_id, + wasi_timestamp_t *resolution) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!validate_native_addr(resolution, (uint64)sizeof(wasi_timestamp_t))) + return (wasi_errno_t)-1; + + return uvwasi_clock_res_get(uvwasi, clock_id, resolution); +} + +static wasi_errno_t +wasi_clock_time_get(wasm_exec_env_t exec_env, wasi_clockid_t clock_id, + wasi_timestamp_t precision, wasi_timestamp_t *time) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!validate_native_addr(time, (uint64)sizeof(wasi_timestamp_t))) + return (wasi_errno_t)-1; + + return uvwasi_clock_time_get(uvwasi, clock_id, precision, time); +} + +static wasi_errno_t +wasi_environ_get(wasm_exec_env_t exec_env, uint32 *environ_offsets, + char *environ_buf) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t environ_count, environ_buf_size, i; + uint64 total_size; + char **environs; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + err = uvwasi_environ_sizes_get(uvwasi, &environ_count, &environ_buf_size); + if (err) + return err; + + if (environ_count == 0) + return 0; + + total_size = sizeof(int32) * ((uint64)environ_count + 1); + if (total_size >= UINT32_MAX + || !validate_native_addr(environ_offsets, total_size) + || environ_buf_size >= UINT32_MAX + || !validate_native_addr(environ_buf, (uint64)environ_buf_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(char *) * (((uint64)environ_count + 1)); + + if (total_size >= UINT32_MAX + || !(environs = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + err = uvwasi_environ_get(uvwasi, environs, environ_buf); + if (err) { + wasm_runtime_free(environs); + return err; + } + + for (i = 0; i < environ_count; i++) + environ_offsets[i] = (uint32)addr_native_to_app(environs[i]); + + wasm_runtime_free(environs); + return 0; +} + +static wasi_errno_t +wasi_environ_sizes_get(wasm_exec_env_t exec_env, uint32 *environ_count_app, + uint32 *environ_buf_size_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t environ_count, environ_buf_size; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(environ_count_app, (uint64)sizeof(uint32)) + || !validate_native_addr(environ_buf_size_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; + + err = uvwasi_environ_sizes_get(uvwasi, &environ_count, &environ_buf_size); + if (err) + return err; + + *environ_count_app = (uint32)environ_count; + *environ_buf_size_app = (uint32)environ_buf_size; + return 0; +} + +static wasi_errno_t +wasi_fd_prestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_prestat_app_t *prestat_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_prestat_t prestat; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(prestat_app, (uint64)sizeof(wasi_prestat_app_t))) + return (wasi_errno_t)-1; + + err = uvwasi_fd_prestat_get(uvwasi, fd, &prestat); + if (err) + return err; + + prestat_app->pr_type = prestat.pr_type; + prestat_app->pr_name_len = (uint32)prestat.u.dir.pr_name_len; + return 0; +} + +static wasi_errno_t +wasi_fd_prestat_dir_name(wasm_exec_env_t exec_env, wasi_fd_t fd, char *path, + uint32 path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_prestat_dir_name(uvwasi, fd, path, path_len); +} + +static wasi_errno_t +wasi_fd_close(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_close(uvwasi, fd); +} + +static wasi_errno_t +wasi_fd_datasync(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_datasync(uvwasi, fd); +} + +static wasi_errno_t +wasi_fd_pread(wasm_exec_env_t exec_env, wasi_fd_t fd, iovec_app_t *iovec_app, + uint32 iovs_len, wasi_filesize_t offset, uint32 *nread_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_iovec_t *iovec, *iovec_begin; + uint64 total_size; + uvwasi_size_t nread; + uint32 i; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nread_app, (uint64)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr(iovec_app, total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_iovec_t) * (uint64)iovs_len; + if (total_size >= UINT32_MAX + || !(iovec_begin = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + iovec = iovec_begin; + for (i = 0; i < iovs_len; i++, iovec_app++, iovec++) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { + err = (wasi_errno_t)-1; + goto fail; + } + iovec->buf = (void *)addr_app_to_native((uint64)iovec_app->buf_offset); + iovec->buf_len = iovec_app->buf_len; + } + + err = uvwasi_fd_pread(uvwasi, fd, iovec_begin, iovs_len, offset, &nread); + if (err) + goto fail; + + *nread_app = (uint32)nread; + + /* success */ + err = 0; + +fail: + wasm_runtime_free(iovec_begin); + return err; +} + +static wasi_errno_t +wasi_fd_pwrite(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, + wasi_filesize_t offset, uint32 *nwritten_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_ciovec_t *ciovec, *ciovec_begin; + uint64 total_size; + uvwasi_size_t nwritten; + uint32 i; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nwritten_app, (uint64)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void *)iovec_app, total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; + if (total_size >= UINT32_MAX + || !(ciovec_begin = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + ciovec = ciovec_begin; + for (i = 0; i < iovs_len; i++, iovec_app++, ciovec++) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { + err = (wasi_errno_t)-1; + goto fail; + } + ciovec->buf = (char *)addr_app_to_native((uint64)iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; + } + + err = + uvwasi_fd_pwrite(uvwasi, fd, ciovec_begin, iovs_len, offset, &nwritten); + if (err) + goto fail; + + *nwritten_app = (uint32)nwritten; + + /* success */ + err = 0; + +fail: + wasm_runtime_free(ciovec_begin); + return err; +} + +static wasi_errno_t +wasi_fd_read(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, uint32 *nread_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_iovec_t *iovec, *iovec_begin; + uint64 total_size; + uvwasi_size_t nread; + uint32 i; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nread_app, (uint64)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void *)iovec_app, total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_iovec_t) * (uint64)iovs_len; + if (total_size >= UINT32_MAX + || !(iovec_begin = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + iovec = iovec_begin; + for (i = 0; i < iovs_len; i++, iovec_app++, iovec++) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { + err = (wasi_errno_t)-1; + goto fail; + } + iovec->buf = (void *)addr_app_to_native((uint64)iovec_app->buf_offset); + iovec->buf_len = iovec_app->buf_len; + } + + err = uvwasi_fd_read(uvwasi, fd, iovec_begin, iovs_len, &nread); + if (err) + goto fail; + + *nread_app = (uint32)nread; + + /* success */ + err = 0; + +fail: + wasm_runtime_free(iovec_begin); + return err; +} + +static wasi_errno_t +wasi_fd_renumber(wasm_exec_env_t exec_env, wasi_fd_t from, wasi_fd_t to) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_renumber(uvwasi, from, to); +} + +static wasi_errno_t +wasi_fd_seek(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filedelta_t offset, + wasi_whence_t whence, wasi_filesize_t *newoffset) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(newoffset, (uint64)sizeof(wasi_filesize_t))) + return (wasi_errno_t)-1; + + return uvwasi_fd_seek(uvwasi, fd, offset, whence, newoffset); +} + +static wasi_errno_t +wasi_fd_tell(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t *newoffset) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(newoffset, (uint64)sizeof(wasi_filesize_t))) + return (wasi_errno_t)-1; + + return uvwasi_fd_tell(uvwasi, fd, newoffset); +} + +static wasi_errno_t +wasi_fd_fdstat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_fdstat_t *fdstat_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_fdstat_t fdstat; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(fdstat_app, (uint64)sizeof(wasi_fdstat_t))) + return (wasi_errno_t)-1; + + err = uvwasi_fd_fdstat_get(uvwasi, fd, &fdstat); + if (err) + return err; + + memcpy(fdstat_app, &fdstat, sizeof(wasi_fdstat_t)); + return 0; +} + +static wasi_errno_t +wasi_fd_fdstat_set_flags(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_fdflags_t flags) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_fdstat_set_flags(uvwasi, fd, flags); +} + +static wasi_errno_t +wasi_fd_fdstat_set_rights(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_rights_t fs_rights_base, + wasi_rights_t fs_rights_inheriting) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_fdstat_set_rights(uvwasi, fd, fs_rights_base, + fs_rights_inheriting); +} + +static wasi_errno_t +wasi_fd_sync(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_sync(uvwasi, fd); +} + +static wasi_errno_t +wasi_fd_write(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, + uint32 *nwritten_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_ciovec_t *ciovec, *ciovec_begin; + uint64 total_size; + uvwasi_size_t nwritten; + uint32 i; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nwritten_app, (uint64)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void *)iovec_app, total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; + if (total_size >= UINT32_MAX + || !(ciovec_begin = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + ciovec = ciovec_begin; + for (i = 0; i < iovs_len; i++, iovec_app++, ciovec++) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { + err = (wasi_errno_t)-1; + goto fail; + } + ciovec->buf = (char *)addr_app_to_native((uint64)iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; + } + +#ifndef BH_VPRINTF + err = uvwasi_fd_write(uvwasi, fd, ciovec_begin, iovs_len, &nwritten); +#else + /* redirect stdout/stderr output to BH_VPRINTF function */ + if (fd == 1 || fd == 2) { + int i; + const struct iovec *iov1 = (const struct iovec *)ciovec_begin; + + nwritten = 0; + for (i = 0; i < (int)iovs_len; i++, iov1++) { + if (iov1->iov_len > 0 && iov1->iov_base) { + char format[16]; + + /* make up format string "%.ns" */ + snprintf(format, sizeof(format), "%%.%ds", (int)iov1->iov_len); + nwritten += (uvwasi_size_t)os_printf(format, iov1->iov_base); + } + } + err = 0; + } + else { + err = uvwasi_fd_write(uvwasi, fd, ciovec_begin, iovs_len, &nwritten); + } +#endif /* end of BH_VPRINTF */ + + if (err) + goto fail; + + *nwritten_app = (uint32)nwritten; + + /* success */ + err = 0; + +fail: + wasm_runtime_free(ciovec_begin); + return err; +} + +static wasi_errno_t +wasi_fd_advise(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t offset, + wasi_filesize_t len, wasi_advice_t advice) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_advise(uvwasi, fd, offset, len, advice); +} + +static wasi_errno_t +wasi_fd_allocate(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t offset, + wasi_filesize_t len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_allocate(uvwasi, fd, offset, len); +} + +static wasi_errno_t +wasi_path_create_directory(wasm_exec_env_t exec_env, wasi_fd_t fd, + const char *path, uint32 path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_create_directory(uvwasi, fd, path, path_len); +} + +static wasi_errno_t +wasi_path_link(wasm_exec_env_t exec_env, wasi_fd_t old_fd, + wasi_lookupflags_t old_flags, const char *old_path, + uint32 old_path_len, wasi_fd_t new_fd, const char *new_path, + uint32 new_path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_link(uvwasi, old_fd, old_flags, old_path, old_path_len, + new_fd, new_path, new_path_len); +} + +static wasi_errno_t +wasi_path_open(wasm_exec_env_t exec_env, wasi_fd_t dirfd, + wasi_lookupflags_t dirflags, const char *path, uint32 path_len, + wasi_oflags_t oflags, wasi_rights_t fs_rights_base, + wasi_rights_t fs_rights_inheriting, wasi_fdflags_t fs_flags, + wasi_fd_t *fd_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_fd_t fd = (wasi_fd_t)-1; /* set fd_app -1 if path open failed */ + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(fd_app, (uint64)sizeof(wasi_fd_t))) + return (wasi_errno_t)-1; + + err = uvwasi_path_open(uvwasi, dirfd, dirflags, path, path_len, oflags, + fs_rights_base, fs_rights_inheriting, fs_flags, &fd); + + *fd_app = fd; + return err; +} + +static wasi_errno_t +wasi_fd_readdir(wasm_exec_env_t exec_env, wasi_fd_t fd, void *buf, + uint32 buf_len, wasi_dircookie_t cookie, uint32 *bufused_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t bufused; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(bufused_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; + + err = uvwasi_fd_readdir(uvwasi, fd, buf, buf_len, cookie, &bufused); + if (err) + return err; + + *bufused_app = (uint32)bufused; + return 0; +} + +static wasi_errno_t +wasi_path_readlink(wasm_exec_env_t exec_env, wasi_fd_t fd, const char *path, + uint32 path_len, char *buf, uint32 buf_len, + uint32 *bufused_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t bufused; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(bufused_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; + + err = uvwasi_path_readlink(uvwasi, fd, path, path_len, buf, buf_len, + &bufused); + if (err) + return err; + + *bufused_app = (uint32)bufused; + return 0; +} + +static wasi_errno_t +wasi_path_rename(wasm_exec_env_t exec_env, wasi_fd_t old_fd, + const char *old_path, uint32 old_path_len, wasi_fd_t new_fd, + const char *new_path, uint32 new_path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_rename(uvwasi, old_fd, old_path, old_path_len, new_fd, + new_path, new_path_len); +} + +static wasi_errno_t +wasi_fd_filestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_filestat_t *filestat) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(filestat, (uint64)sizeof(wasi_filestat_t))) + return (wasi_errno_t)-1; + + return uvwasi_fd_filestat_get(uvwasi, fd, filestat); +} + +static wasi_errno_t +wasi_fd_filestat_set_times(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_timestamp_t st_atim, wasi_timestamp_t st_mtim, + wasi_fstflags_t fstflags) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_filestat_set_times(uvwasi, fd, st_atim, st_mtim, fstflags); +} + +static wasi_errno_t +wasi_fd_filestat_set_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_filesize_t st_size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_fd_filestat_set_size(uvwasi, fd, st_size); +} + +static wasi_errno_t +wasi_path_filestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_lookupflags_t flags, const char *path, + uint32 path_len, wasi_filestat_t *filestat) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr(filestat, (uint64)sizeof(wasi_filestat_t))) + return (wasi_errno_t)-1; + + return uvwasi_path_filestat_get(uvwasi, fd, flags, path, path_len, + filestat); +} + +static wasi_errno_t +wasi_path_filestat_set_times(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_lookupflags_t flags, const char *path, + uint32 path_len, wasi_timestamp_t st_atim, + wasi_timestamp_t st_mtim, wasi_fstflags_t fstflags) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_filestat_set_times(uvwasi, fd, flags, path, path_len, + st_atim, st_mtim, fstflags); +} + +static wasi_errno_t +wasi_path_symlink(wasm_exec_env_t exec_env, const char *old_path, + uint32 old_path_len, wasi_fd_t fd, const char *new_path, + uint32 new_path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_symlink(uvwasi, old_path, old_path_len, fd, new_path, + new_path_len); +} + +static wasi_errno_t +wasi_path_unlink_file(wasm_exec_env_t exec_env, wasi_fd_t fd, const char *path, + uint32 path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_unlink_file(uvwasi, fd, path, path_len); +} + +static wasi_errno_t +wasi_path_remove_directory(wasm_exec_env_t exec_env, wasi_fd_t fd, + const char *path, uint32 path_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_path_remove_directory(uvwasi, fd, path, path_len); +} + +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) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + uvwasi_size_t nevents; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + if (!validate_native_addr((void *)in, (uint64)sizeof(wasi_subscription_t)) + || !validate_native_addr(out, (uint64)sizeof(wasi_event_t)) + || !validate_native_addr(nevents_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; + + err = uvwasi_poll_oneoff(uvwasi, in, out, nsubscriptions, &nevents); + if (err) + return err; + + *nevents_app = (uint32)nevents; + return 0; +} + +static void +wasi_proc_exit(wasm_exec_env_t exec_env, wasi_exitcode_t rval) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + WASIContext *wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + /* Here throwing exception is just to let wasm app exit, + the upper layer should clear the exception and return + as normal */ + wasm_runtime_set_exception(module_inst, "wasi proc exit"); + wasi_ctx->exit_code = rval; +} + +static wasi_errno_t +wasi_proc_raise(wasm_exec_env_t exec_env, wasi_signal_t sig) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + + snprintf(buf, sizeof(buf), "%s%d", "wasi proc raise ", sig); + wasm_runtime_set_exception(module_inst, buf); + return 0; +} + +static wasi_errno_t +wasi_random_get(wasm_exec_env_t exec_env, void *buf, uint32 buf_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + return uvwasi_random_get(uvwasi, buf, buf_len); +} + +static wasi_errno_t +wasi_sock_recv(wasm_exec_env_t exec_env, wasi_fd_t sock, iovec_app_t *ri_data, + uint32 ri_data_len, wasi_riflags_t ri_flags, + uint32 *ro_datalen_app, wasi_roflags_t *ro_flags) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_iovec_t *iovec, *iovec_begin; + uint64 total_size; + uvwasi_size_t ro_datalen; + uint32 i; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)ri_data_len; + if (!validate_native_addr(ro_datalen_app, (uint32)sizeof(uint32)) + || !validate_native_addr(ro_flags, (uint32)sizeof(wasi_roflags_t)) + || total_size >= UINT32_MAX + || !validate_native_addr(ri_data, (uint32)total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_iovec_t) * (uint64)ri_data_len; + if (total_size >= UINT32_MAX + || !(iovec_begin = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + iovec = iovec_begin; + for (i = 0; i < ri_data_len; i++, ri_data++, iovec++) { + if (!validate_app_addr((uint64)ri_data->buf_offset, + (uint64)ri_data->buf_len)) { + err = (wasi_errno_t)-1; + goto fail; + } + iovec->buf = (void *)addr_app_to_native((uint64)ri_data->buf_offset); + iovec->buf_len = ri_data->buf_len; + } + + err = uvwasi_sock_recv(uvwasi, sock, iovec_begin, ri_data_len, ri_flags, + &ro_datalen, ro_flags); + if (err) + goto fail; + + *(uint32 *)ro_datalen_app = (uint32)ro_datalen; + + /* success */ + err = 0; + +fail: + wasm_runtime_free(iovec_begin); + return err; +} + +static wasi_errno_t +wasi_sock_send(wasm_exec_env_t exec_env, wasi_fd_t sock, + const iovec_app_t *si_data, uint32 si_data_len, + wasi_siflags_t si_flags, uint32 *so_datalen_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + wasi_ciovec_t *ciovec, *ciovec_begin; + uint64 total_size; + uvwasi_size_t so_datalen; + uint32 i; + wasi_errno_t err; + + if (!uvwasi) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)si_data_len; + if (!validate_native_addr(so_datalen_app, (uint64)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void *)si_data, total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_ciovec_t) * (uint64)si_data_len; + if (total_size >= UINT32_MAX + || !(ciovec_begin = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + ciovec = ciovec_begin; + for (i = 0; i < si_data_len; i++, si_data++, ciovec++) { + if (!validate_app_addr((uint64)si_data->buf_offset, + (uint64)si_data->buf_len)) { + err = (wasi_errno_t)-1; + goto fail; + } + ciovec->buf = (char *)addr_app_to_native((uint64)si_data->buf_offset); + ciovec->buf_len = si_data->buf_len; + } + + err = uvwasi_sock_send(uvwasi, sock, ciovec_begin, si_data_len, si_flags, + &so_datalen); + if (err) + goto fail; + + *so_datalen_app = (uint32)so_datalen; + + /* success */ + err = 0; + +fail: + wasm_runtime_free(ciovec_begin); + return err; +} + +static wasi_errno_t +wasi_sock_shutdown(wasm_exec_env_t exec_env, wasi_fd_t sock, wasi_sdflags_t how) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + if (!uvwasi) + return (wasi_errno_t)-1; + + return uvwasi_sock_shutdown(uvwasi, sock, how); +} + +static wasi_errno_t +wasi_sched_yield(wasm_exec_env_t exec_env) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uvwasi_t *uvwasi = get_wasi_ctx(module_inst); + + return uvwasi_sched_yield(uvwasi); +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, wasi_##func_name, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_libc_wasi[] = { + REG_NATIVE_FUNC(args_get, "(**)i"), + REG_NATIVE_FUNC(args_sizes_get, "(**)i"), + REG_NATIVE_FUNC(clock_res_get, "(i*)i"), + REG_NATIVE_FUNC(clock_time_get, "(iI*)i"), + REG_NATIVE_FUNC(environ_get, "(**)i"), + REG_NATIVE_FUNC(environ_sizes_get, "(**)i"), + REG_NATIVE_FUNC(fd_prestat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_prestat_dir_name, "(i*~)i"), + REG_NATIVE_FUNC(fd_close, "(i)i"), + REG_NATIVE_FUNC(fd_datasync, "(i)i"), + REG_NATIVE_FUNC(fd_pread, "(i*iI*)i"), + REG_NATIVE_FUNC(fd_pwrite, "(i*iI*)i"), + REG_NATIVE_FUNC(fd_read, "(i*i*)i"), + REG_NATIVE_FUNC(fd_renumber, "(ii)i"), + REG_NATIVE_FUNC(fd_seek, "(iIi*)i"), + REG_NATIVE_FUNC(fd_tell, "(i*)i"), + REG_NATIVE_FUNC(fd_fdstat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_fdstat_set_flags, "(ii)i"), + REG_NATIVE_FUNC(fd_fdstat_set_rights, "(iII)i"), + REG_NATIVE_FUNC(fd_sync, "(i)i"), + REG_NATIVE_FUNC(fd_write, "(i*i*)i"), + REG_NATIVE_FUNC(fd_advise, "(iIIi)i"), + REG_NATIVE_FUNC(fd_allocate, "(iII)i"), + REG_NATIVE_FUNC(path_create_directory, "(i*~)i"), + REG_NATIVE_FUNC(path_link, "(ii*~i*~)i"), + REG_NATIVE_FUNC(path_open, "(ii*~iIIi*)i"), + REG_NATIVE_FUNC(fd_readdir, "(i*~I*)i"), + REG_NATIVE_FUNC(path_readlink, "(i*~*~*)i"), + REG_NATIVE_FUNC(path_rename, "(i*~i*~)i"), + REG_NATIVE_FUNC(fd_filestat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_filestat_set_times, "(iIIi)i"), + REG_NATIVE_FUNC(fd_filestat_set_size, "(iI)i"), + REG_NATIVE_FUNC(path_filestat_get, "(ii*~*)i"), + REG_NATIVE_FUNC(path_filestat_set_times, "(ii*~IIi)i"), + REG_NATIVE_FUNC(path_symlink, "(*~i*~)i"), + REG_NATIVE_FUNC(path_unlink_file, "(i*~)i"), + REG_NATIVE_FUNC(path_remove_directory, "(i*~)i"), + REG_NATIVE_FUNC(poll_oneoff, "(**i*)i"), + REG_NATIVE_FUNC(proc_exit, "(i)"), + REG_NATIVE_FUNC(proc_raise, "(i)i"), + REG_NATIVE_FUNC(random_get, "(*~)i"), + REG_NATIVE_FUNC(sock_recv, "(i*ii**)i"), + REG_NATIVE_FUNC(sock_send, "(i*ii*)i"), + REG_NATIVE_FUNC(sock_shutdown, "(ii)i"), + REG_NATIVE_FUNC(sched_yield, "()i"), +}; + +uint32 +get_libc_wasi_export_apis(NativeSymbol **p_libc_wasi_apis) +{ + *p_libc_wasi_apis = native_symbols_libc_wasi; + return sizeof(native_symbols_libc_wasi) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/libc-wasi/SConscript b/core/iwasm/libraries/libc-wasi/SConscript new file mode 100644 index 0000000000..6ed3799e0d --- /dev/null +++ b/core/iwasm/libraries/libc-wasi/SConscript @@ -0,0 +1,34 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import os + +cwd = GetCurrentDir() + +src = Split(''' +''') + +CPPPATH = [cwd, + cwd+'/sandboxed-system-primitives/include', + cwd+'/sandboxed-system-primitives/src'] + +def addSrcFiles(arr, path): + for f in os.listdir(path): + fpath = os.path.join(path, f); + if os.path.isfile(fpath): + ext = os.path.splitext(fpath)[-1] + if ext == '.c' or ext == '.cpp': + arr += [fpath] + elif os.path.isdir(fpath): + addSrcFiles(arr, fpath) + + +addSrcFiles(src, cwd) + +group = DefineGroup('iwasm_libc_wasi', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index 59ee56baf4..5ab189e71d 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -4,13 +4,19 @@ */ #include "libc_wasi_wrapper.h" -#include "bh_common.h" -#include "bh_log.h" +#include "bh_platform.h" #include "wasm_export.h" +#include "wasm_runtime_common.h" +#include "wasmtime_ssp.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); +/* clang-format off */ #define get_module_inst(exec_env) \ wasm_runtime_get_module_inst(exec_env) @@ -20,23 +26,21 @@ wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); #define validate_app_addr(offset, size) \ wasm_runtime_validate_app_addr(module_inst, offset, size) +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + #define addr_app_to_native(offset) \ wasm_runtime_addr_app_to_native(module_inst, offset) #define addr_native_to_app(ptr) \ wasm_runtime_addr_native_to_app(module_inst, ptr) -#define module_malloc(size) \ - wasm_runtime_module_malloc(module_inst, size) +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) #define module_free(offset) \ wasm_runtime_module_free(module_inst, offset) - -#define WASI_CHECK_ERR() do { \ - if (err) { \ - return err; \ - } \ - } while (0) +/* clang-format on */ typedef struct wasi_prestat_app { wasi_preopentype_t pr_type; @@ -44,261 +48,281 @@ typedef struct wasi_prestat_app { } wasi_prestat_app_t; typedef struct iovec_app { - int32 buf_offset; + uint32 buf_offset; uint32 buf_len; } iovec_app_t; -typedef struct WASIContext { - void *curfds; - void *prestats; - void *argv_environ; -} *wasi_ctx_t; +typedef struct WASIContext *wasi_ctx_t; wasi_ctx_t wasm_runtime_get_wasi_ctx(wasm_module_inst_t module_inst); +#if WASM_ENABLE_THREAD_MGR != 0 +static inline uint64_t +min_uint64(uint64_t a, uint64_t b) +{ + return a > b ? b : a; +} +#endif + +static inline uint32_t +min_uint32(uint32_t a, uint32_t b) +{ + return a > b ? b : a; +} + +static inline struct fd_table * +wasi_ctx_get_curfds(wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return wasi_ctx->curfds; +} + +static inline struct argv_environ_values * +wasi_ctx_get_argv_environ(wasm_module_inst_t module_inst, wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return wasi_ctx->argv_environ; +} + +static inline struct fd_prestats * +wasi_ctx_get_prestats(wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return wasi_ctx->prestats; +} + +static inline struct addr_pool * +wasi_ctx_get_addr_pool(wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return wasi_ctx->addr_pool; +} + +static inline char ** +wasi_ctx_get_ns_lookup_list(wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return wasi_ctx->ns_lookup_list; +} + static wasi_errno_t -wasi_args_get(wasm_exec_env_t exec_env, - int32 argv_offset /* char ** */, - int32 argv_buf_offset /* char * */) +wasi_args_get(wasm_exec_env_t exec_env, uint32 *argv_offsets, char *argv_buf) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct argv_environ_values *argv_environ = + wasi_ctx_get_argv_environ(module_inst, wasi_ctx); size_t argc, argv_buf_size, i; - uint64 total_size1, total_size2; - int32 *argv_app; - char *argv_buf_app; - wasi_errno_t err; char **argv; + uint64 total_size; + wasi_errno_t err; - err = wasmtime_ssp_args_sizes_get(wasi_ctx->argv_environ, - &argc, &argv_buf_size); - WASI_CHECK_ERR(); - - total_size1 = sizeof(char *) * ((uint64)argc + 1); - total_size2 = sizeof(char) * (uint64)argv_buf_size; - if (total_size1 >= UINT32_MAX - || !validate_app_addr(argv_offset, (uint32)total_size1) - || total_size2 >= UINT32_MAX - || !validate_app_addr(argv_buf_offset, (uint32)total_size2)) + if (!wasi_ctx) return (wasi_errno_t)-1; - argv = bh_malloc((uint32)total_size1); - if (!argv) + err = wasmtime_ssp_args_sizes_get(argv_environ, &argc, &argv_buf_size); + if (err) + return err; + + total_size = sizeof(int32) * ((uint64)argc + 1); + if (total_size >= UINT32_MAX + || !validate_native_addr(argv_offsets, total_size) + || argv_buf_size >= UINT32_MAX + || !validate_native_addr(argv_buf, (uint64)argv_buf_size)) return (wasi_errno_t)-1; - argv_app = (int32*)addr_app_to_native(argv_offset); - argv_buf_app= (char*)addr_app_to_native(argv_buf_offset); + total_size = sizeof(char *) * ((uint64)argc + 1); + if (total_size >= UINT32_MAX + || !(argv = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; - err = wasmtime_ssp_args_get(wasi_ctx->argv_environ, - argv, argv_buf_app); - if (err) - goto fail; + err = wasmtime_ssp_args_get(argv_environ, argv, argv_buf); + if (err) { + wasm_runtime_free(argv); + return err; + } for (i = 0; i < argc; i++) - argv_app[i] = addr_native_to_app(argv[i]); - argv_app[argc] = 0; - - /* success */ - err = 0; + argv_offsets[i] = (uint32)addr_native_to_app(argv[i]); -fail: - bh_free(argv); - return err; + wasm_runtime_free(argv); + return 0; } static wasi_errno_t -wasi_args_sizes_get(wasm_exec_env_t exec_env, - int32 argc_offset /* size_t * */, - int32 argv_buf_size_offset /* size_t * */) +wasi_args_sizes_get(wasm_exec_env_t exec_env, uint32 *argc_app, + uint32 *argv_buf_size_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct argv_environ_values *argv_environ; size_t argc, argv_buf_size; - uint32 *argc_app, *argv_buf_size_app; wasi_errno_t err; - if (!validate_app_addr(argc_offset, sizeof(uint32)) - || !validate_app_addr(argv_buf_size_offset, sizeof(uint32))) + if (!wasi_ctx) return (wasi_errno_t)-1; - argc_app = (uint32*)addr_app_to_native(argc_offset); - argv_buf_size_app = (uint32*)addr_app_to_native(argv_buf_size_offset); + if (!validate_native_addr(argc_app, (uint64)sizeof(uint32)) + || !validate_native_addr(argv_buf_size_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; - err = wasmtime_ssp_args_sizes_get(wasi_ctx->argv_environ, - &argc, &argv_buf_size); - WASI_CHECK_ERR(); + argv_environ = wasi_ctx->argv_environ; - *(uint32*)argc_app = (uint32)argc; - *(uint32*)argv_buf_size_app = (uint32)argv_buf_size; + err = wasmtime_ssp_args_sizes_get(argv_environ, &argc, &argv_buf_size); + if (err) + return err; + *argc_app = (uint32)argc; + *argv_buf_size_app = (uint32)argv_buf_size; return 0; } static wasi_errno_t wasi_clock_res_get(wasm_exec_env_t exec_env, - wasi_clockid_t clock_id, - int32 resolution_offset /* wasi_timestamp_t * */) + wasi_clockid_t clock_id, /* uint32 clock_id */ + wasi_timestamp_t *resolution /* uint64 *resolution */) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasi_timestamp_t resolution; - uint32 *resolution_app; - wasi_errno_t err; - if (!validate_app_addr(resolution_offset, sizeof(wasi_timestamp_t))) + if (!validate_native_addr(resolution, (uint64)sizeof(wasi_timestamp_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_clock_res_get(clock_id, &resolution); - WASI_CHECK_ERR(); - - resolution_app = addr_app_to_native(resolution_offset); - - memcpy(resolution_app, &resolution, sizeof(wasi_timestamp_t)); - - return 0; + return os_clock_res_get(clock_id, resolution); } static wasi_errno_t wasi_clock_time_get(wasm_exec_env_t exec_env, - wasi_clockid_t clock_id, - wasi_timestamp_t precision, - int32 time_offset /*wasi_timestamp_t * */) + wasi_clockid_t clock_id, /* uint32 clock_id */ + wasi_timestamp_t precision, /* uint64 precision */ + wasi_timestamp_t *time /* uint64 *time */) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasi_timestamp_t time; - uint32 *time_app; - wasi_errno_t err; - if (!validate_app_addr(time_offset, sizeof(wasi_timestamp_t))) + if (!validate_native_addr(time, (uint64)sizeof(wasi_timestamp_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_clock_time_get(clock_id, precision, &time); - WASI_CHECK_ERR(); - - time_app = addr_app_to_native(time_offset); - - memcpy(time_app, &time, sizeof(wasi_timestamp_t)); - - return 0; + return os_clock_time_get(clock_id, precision, time); } static wasi_errno_t -wasi_environ_get(wasm_exec_env_t exec_env, - int32 environ_offset /* char ** */, - int32 environ_buf_offset /* char */) +wasi_environ_get(wasm_exec_env_t exec_env, uint32 *environ_offsets, + char *environ_buf) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct argv_environ_values *argv_environ = + wasi_ctx_get_argv_environ(module_inst, wasi_ctx); size_t environ_count, environ_buf_size, i; uint64 total_size; - int32 *environ_app; - char *environ_buff_app; - char **environ; + char **environs; wasi_errno_t err; - err = wasmtime_ssp_environ_sizes_get(wasi_ctx->argv_environ, - &environ_count, &environ_buf_size); - WASI_CHECK_ERR(); + if (!wasi_ctx) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_environ_sizes_get(argv_environ, &environ_count, + &environ_buf_size); + if (err) + return err; - total_size = sizeof(char*) * ((uint64)environ_count + 1); + total_size = sizeof(int32) * ((uint64)environ_count + 1); if (total_size >= UINT32_MAX - || !validate_app_addr(environ_offset, (uint32)total_size) + || !validate_native_addr(environ_offsets, total_size) || environ_buf_size >= UINT32_MAX - || !validate_app_addr(environ_buf_offset, (uint32)environ_buf_size)) + || !validate_native_addr(environ_buf, (uint64)environ_buf_size)) return (wasi_errno_t)-1; - environ_app = (int32*)addr_app_to_native(environ_offset); - environ_buff_app = (char*)addr_app_to_native(environ_buf_offset); + total_size = sizeof(char *) * (((uint64)environ_count + 1)); - environ = bh_malloc((uint32)total_size); - if (!environ) + if (total_size >= UINT32_MAX + || !(environs = wasm_runtime_malloc((uint32)total_size))) return (wasi_errno_t)-1; - err = wasmtime_ssp_environ_get(wasi_ctx->argv_environ, - environ, environ_buff_app); - if (err) - goto fail; + err = wasmtime_ssp_environ_get(argv_environ, environs, environ_buf); + if (err) { + wasm_runtime_free(environs); + return err; + } for (i = 0; i < environ_count; i++) - environ_app[i] = addr_native_to_app(environ[i]); - environ_app[environ_count] = 0; - - /* success */ - err = 0; + environ_offsets[i] = (uint32)addr_native_to_app(environs[i]); -fail: - bh_free(environ); - return err; + wasm_runtime_free(environs); + return 0; } static wasi_errno_t -wasi_environ_sizes_get(wasm_exec_env_t exec_env, - int32 environ_count_offset /* size_t * */, - int32 environ_buf_size_offset /* size_t * */) +wasi_environ_sizes_get(wasm_exec_env_t exec_env, uint32 *environ_count_app, + uint32 *environ_buf_size_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct argv_environ_values *argv_environ = + wasi_ctx_get_argv_environ(module_inst, wasi_ctx); size_t environ_count, environ_buf_size; - uint32 *environ_count_app, *environ_buf_size_app; wasi_errno_t err; - if (!validate_app_addr(environ_count_offset, sizeof(uint32)) - || !validate_app_addr(environ_buf_size_offset, sizeof(uint32))) + if (!wasi_ctx) return (wasi_errno_t)-1; - err = wasmtime_ssp_environ_sizes_get(wasi_ctx->argv_environ, - &environ_count, &environ_buf_size); - WASI_CHECK_ERR(); + if (!validate_native_addr(environ_count_app, (uint64)sizeof(uint32)) + || !validate_native_addr(environ_buf_size_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; - environ_count_app = (uint32*)addr_app_to_native(environ_count_offset); - environ_buf_size_app = (uint32*)addr_app_to_native(environ_buf_size_offset); + err = wasmtime_ssp_environ_sizes_get(argv_environ, &environ_count, + &environ_buf_size); + if (err) + return err; - *(uint32*)environ_count_app = (uint32)environ_count; - *(uint32*)environ_buf_size_app = (uint32)environ_buf_size; + *environ_count_app = (uint32)environ_count; + *environ_buf_size_app = (uint32)environ_buf_size; return 0; } static wasi_errno_t -wasi_fd_prestat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* wasi_prestat_t * */) +wasi_fd_prestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_prestat_app_t *prestat_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_prestat_app_t *prestat_app; + struct fd_prestats *prestats = wasi_ctx_get_prestats(wasi_ctx); wasi_prestat_t prestat; wasi_errno_t err; - if (!validate_app_addr(buf_offset, sizeof(wasi_prestat_app_t))) + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(prestat_app, (uint64)sizeof(wasi_prestat_app_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_prestat_get(wasi_ctx->prestats, fd, &prestat); - WASI_CHECK_ERR(); + err = wasmtime_ssp_fd_prestat_get(prestats, fd, &prestat); + if (err) + return err; - prestat_app = (wasi_prestat_app_t*)addr_app_to_native(buf_offset); prestat_app->pr_type = prestat.pr_type; prestat_app->pr_name_len = (uint32)prestat.u.dir.pr_name_len; - return 0; } static wasi_errno_t -wasi_fd_prestat_dir_name(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* char * */, +wasi_fd_prestat_dir_name(wasm_exec_env_t exec_env, wasi_fd_t fd, char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path_app; + struct fd_prestats *prestats = wasi_ctx_get_prestats(wasi_ctx); - if (!validate_app_addr(path_offset, path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - path_app = (char*)addr_app_to_native(path_offset); - return wasmtime_ssp_fd_prestat_dir_name(wasi_ctx->prestats, fd, - path_app, path_len); + return wasmtime_ssp_fd_prestat_dir_name(prestats, fd, path, path_len); } static wasi_errno_t @@ -306,8 +330,13 @@ wasi_fd_close(wasm_exec_env_t exec_env, wasi_fd_t fd) { 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(wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; - return wasmtime_ssp_fd_close(wasi_ctx->curfds, wasi_ctx->prestats, fd); + return wasmtime_ssp_fd_close(exec_env, curfds, prestats, fd); } static wasi_errno_t @@ -315,290 +344,286 @@ wasi_fd_datasync(wasm_exec_env_t exec_env, wasi_fd_t fd) { 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(wasi_ctx); - return wasmtime_ssp_fd_datasync(wasi_ctx->curfds, fd); + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_datasync(exec_env, curfds, fd); } static wasi_errno_t -wasi_fd_pread(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_iovec_t * */, - uint32 iovs_len, - wasi_filesize_t offset, - int32 nread_offset /* size_t * */) +wasi_fd_pread(wasm_exec_env_t exec_env, wasi_fd_t fd, iovec_app_t *iovec_app, + uint32 iovs_len, wasi_filesize_t offset, uint32 *nread_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); wasi_iovec_t *iovec, *iovec_begin; - iovec_app_t *iovec_app; - uint32 i; - size_t nread; - uint32 *nread_app; uint64 total_size; + size_t nread; + uint32 i; wasi_errno_t err; - total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nread_offset, (uint32)sizeof(uint32)) - || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + if (!wasi_ctx) return (wasi_errno_t)-1; - iovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - if (!iovec_app) + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nread_app, (uint64)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr(iovec_app, total_size)) return (wasi_errno_t)-1; total_size = sizeof(wasi_iovec_t) * (uint64)iovs_len; + if (total_size == 0) { + total_size = 1; /* avoid user-triggered 0-sized allocation */ + } if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(iovec_begin = wasm_runtime_malloc((uint32)total_size))) return (wasi_errno_t)-1; - iovec = iovec_begin = (wasi_iovec_t*)addr_app_to_native(mem); + iovec = iovec_begin; for (i = 0; i < iovs_len; i++, iovec_app++, iovec++) { - if (!validate_app_addr(iovec_app->buf_offset, iovec_app->buf_len)) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - iovec->buf = (void*)addr_app_to_native(iovec_app->buf_offset); + iovec->buf = (void *)addr_app_to_native((uint64)iovec_app->buf_offset); iovec->buf_len = iovec_app->buf_len; } - err = wasmtime_ssp_fd_pread(wasi_ctx->curfds, fd, iovec_begin, - iovs_len, offset, &nread); + err = wasmtime_ssp_fd_pread(exec_env, curfds, fd, iovec_begin, iovs_len, + offset, &nread); if (err) goto fail; - nread_app = (uint32*)addr_app_to_native(nread_offset); - *(uint32*)nread_app = (uint32)nread; + *nread_app = (uint32)nread; /* success */ err = 0; fail: - module_free(mem); + wasm_runtime_free(iovec_begin); return err; } static wasi_errno_t -wasi_fd_pwrite(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_ciovec_t * */, - uint32 iovs_len, - wasi_filesize_t offset, - int32 nwritten_offset /* size_t * */) +wasi_fd_pwrite(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, + wasi_filesize_t offset, uint32 *nwritten_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); wasi_ciovec_t *ciovec, *ciovec_begin; - iovec_app_t *ciovec_app; - uint32 i; - size_t nwritten; - uint32 *nwritten_app; uint64 total_size; + size_t nwritten; + uint32 i; wasi_errno_t err; + if (!wasi_ctx) + return (wasi_errno_t)-1; + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nwritten_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nwritten_app, (uint64)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + || !validate_native_addr((void *)iovec_app, total_size)) return (wasi_errno_t)-1; - ciovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; + if (total_size == 0) { + total_size = 1; /* avoid user-triggered 0-sized allocation */ + } if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(ciovec_begin = wasm_runtime_malloc((uint32)total_size))) return (wasi_errno_t)-1; - ciovec_begin = ciovec = (wasi_ciovec_t*)addr_app_to_native(mem); - for (i = 0; i < iovs_len; i++, ciovec_app++, ciovec++) { - if (!validate_app_addr(ciovec_app->buf_offset, ciovec_app->buf_len)) { + ciovec = ciovec_begin; + + for (i = 0; i < iovs_len; i++, iovec_app++, ciovec++) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - ciovec->buf = (char*)addr_app_to_native(ciovec_app->buf_offset); - ciovec->buf_len = ciovec_app->buf_len; + ciovec->buf = (char *)addr_app_to_native((uint64)iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; } - err = wasmtime_ssp_fd_pwrite(wasi_ctx->curfds, fd, ciovec_begin, - iovs_len, offset, &nwritten); + err = wasmtime_ssp_fd_pwrite(exec_env, curfds, fd, ciovec_begin, iovs_len, + offset, &nwritten); if (err) goto fail; - nwritten_app = (uint32*)addr_app_to_native(nwritten_offset); - *(uint32*)nwritten_app = (uint32)nwritten; + *nwritten_app = (uint32)nwritten; /* success */ err = 0; fail: - module_free(mem); + wasm_runtime_free(ciovec_begin); return err; } static wasi_errno_t -wasi_fd_read(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_iovec_t * */, - uint32 iovs_len, - int32 nread_offset /* size_t * */) +wasi_fd_read(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, uint32 *nread_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); wasi_iovec_t *iovec, *iovec_begin; - iovec_app_t *iovec_app; - uint32 i; - size_t nread; - uint32 *nread_app; uint64 total_size; + size_t nread; + uint32 i; wasi_errno_t err; + if (!wasi_ctx) + return (wasi_errno_t)-1; + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nread_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nread_app, (uint64)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + || !validate_native_addr((void *)iovec_app, total_size)) return (wasi_errno_t)-1; - iovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - total_size = sizeof(wasi_iovec_t) * (uint64)iovs_len; + if (total_size == 0) { + total_size = 1; /* avoid user-triggered 0-sized allocation */ + } if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(iovec_begin = wasm_runtime_malloc((uint32)total_size))) return (wasi_errno_t)-1; - iovec = iovec_begin = (wasi_iovec_t*)addr_app_to_native(mem); + iovec = iovec_begin; for (i = 0; i < iovs_len; i++, iovec_app++, iovec++) { - if (!validate_app_addr(iovec_app->buf_offset, iovec_app->buf_len)) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - iovec->buf = (void*)addr_app_to_native(iovec_app->buf_offset); + iovec->buf = (void *)addr_app_to_native((uint64)iovec_app->buf_offset); iovec->buf_len = iovec_app->buf_len; } - err = wasmtime_ssp_fd_read(wasi_ctx->curfds, fd, - iovec_begin, iovs_len, &nread); + err = wasmtime_ssp_fd_read(exec_env, curfds, fd, iovec_begin, iovs_len, + &nread); if (err) goto fail; - nread_app = (uint32*)addr_app_to_native(nread_offset); - *(uint32*)nread_app = (uint32)nread; + *nread_app = (uint32)nread; /* success */ err = 0; fail: - module_free(mem); + wasm_runtime_free(iovec_begin); return err; } static wasi_errno_t -wasi_fd_renumber(wasm_exec_env_t exec_env, - wasi_fd_t from, wasi_fd_t to) +wasi_fd_renumber(wasm_exec_env_t exec_env, wasi_fd_t from, wasi_fd_t to) { 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(wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(wasi_ctx); - return wasmtime_ssp_fd_renumber(wasi_ctx->curfds, - wasi_ctx->prestats, from, to); + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_renumber(exec_env, curfds, prestats, from, to); } static wasi_errno_t -wasi_fd_seek(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_filedelta_t offset, - wasi_whence_t whence, - int32 newoffset_offset /* wasi_filesize_t * */) +wasi_fd_seek(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filedelta_t offset, + wasi_whence_t whence, wasi_filesize_t *newoffset) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_filesize_t newoffset, *newoffset_app; - wasi_errno_t err; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(newoffset_offset, sizeof(wasi_filesize_t))) + if (!wasi_ctx) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_seek(wasi_ctx->curfds, fd, - offset, whence, &newoffset); - WASI_CHECK_ERR(); - - newoffset_app = (wasi_filesize_t*)addr_app_to_native(newoffset_offset); - *newoffset_app = newoffset; + if (!validate_native_addr(newoffset, (uint64)sizeof(wasi_filesize_t))) + return (wasi_errno_t)-1; - return 0; + return wasmtime_ssp_fd_seek(exec_env, curfds, fd, offset, whence, + newoffset); } static wasi_errno_t -wasi_fd_tell(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 newoffset_offset /* wasi_filesize_t * */) +wasi_fd_tell(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t *newoffset) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_filesize_t newoffset, *newoffset_app; - wasi_errno_t err; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(newoffset_offset, sizeof(wasi_filesize_t))) + if (!wasi_ctx) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_tell(wasi_ctx->curfds, fd, &newoffset); - WASI_CHECK_ERR(); - - newoffset_app = (wasi_filesize_t*)addr_app_to_native(newoffset_offset); - *newoffset_app = newoffset; + if (!validate_native_addr(newoffset, (uint64)sizeof(wasi_filesize_t))) + return (wasi_errno_t)-1; - return 0; + return wasmtime_ssp_fd_tell(exec_env, curfds, fd, newoffset); } static wasi_errno_t -wasi_fd_fdstat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* wasi_fdstat_t * */) +wasi_fd_fdstat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_fdstat_t *fdstat_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_fdstat_t fdstat, *fdstat_app; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); + wasi_fdstat_t fdstat; wasi_errno_t err; - if (!validate_app_addr(buf_offset, sizeof(wasi_fdstat_t))) + if (!wasi_ctx) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_fdstat_get(wasi_ctx->curfds, fd, &fdstat); - WASI_CHECK_ERR(); + if (!validate_native_addr(fdstat_app, (uint64)sizeof(wasi_fdstat_t))) + return (wasi_errno_t)-1; - fdstat_app = (wasi_fdstat_t*)addr_app_to_native(buf_offset); - memcpy(fdstat_app, &fdstat, sizeof(wasi_fdstat_t)); + err = wasmtime_ssp_fd_fdstat_get(exec_env, curfds, fd, &fdstat); + if (err) + return err; + memcpy(fdstat_app, &fdstat, sizeof(wasi_fdstat_t)); return 0; } static wasi_errno_t -wasi_fd_fdstat_set_flags(wasm_exec_env_t exec_env, - wasi_fd_t fd, +wasi_fd_fdstat_set_flags(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_fdflags_t flags) { 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(wasi_ctx); - return wasmtime_ssp_fd_fdstat_set_flags(wasi_ctx->curfds, fd, flags); + if (!wasi_ctx) + return (wasi_errno_t)-1; + return wasmtime_ssp_fd_fdstat_set_flags(exec_env, curfds, fd, flags); } static wasi_errno_t -wasi_fd_fdstat_set_rights(wasm_exec_env_t exec_env, - wasi_fd_t fd, +wasi_fd_fdstat_set_rights(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_rights_t fs_rights_base, wasi_rights_t fs_rights_inheriting) { 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(wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; - return wasmtime_ssp_fd_fdstat_set_rights(wasi_ctx->curfds, fd, - fs_rights_base, fs_rights_inheriting); + return wasmtime_ssp_fd_fdstat_set_rights( + exec_env, curfds, fd, fs_rights_base, fs_rights_inheriting); } static wasi_errno_t @@ -606,468 +631,502 @@ wasi_fd_sync(wasm_exec_env_t exec_env, wasi_fd_t fd) { 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(wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; - return wasmtime_ssp_fd_sync(wasi_ctx->curfds, fd); + return wasmtime_ssp_fd_sync(exec_env, curfds, fd); } static wasi_errno_t -wasi_fd_write(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_ciovec_t * */, - uint32 iovs_len, - int32 nwritten_offset /* size_t * */) +wasi_fd_write(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, + uint32 *nwritten_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); wasi_ciovec_t *ciovec, *ciovec_begin; - iovec_app_t *ciovec_app; - uint32 i; - size_t nwritten; - uint32 *nwritten_app; uint64 total_size; + size_t nwritten; + uint32 i; wasi_errno_t err; + if (!wasi_ctx) + return (wasi_errno_t)-1; + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nwritten_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nwritten_app, (uint64)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + || !validate_native_addr((void *)iovec_app, total_size)) return (wasi_errno_t)-1; - ciovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; + if (total_size == 0) { + total_size = 1; /* avoid user-triggered 0-sized allocation */ + } if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(ciovec_begin = wasm_runtime_malloc((uint32)total_size))) return (wasi_errno_t)-1; - ciovec_begin = ciovec = (wasi_ciovec_t*)addr_app_to_native(mem); - for (i = 0; i < iovs_len; i++, ciovec_app++, ciovec++) { - if (!validate_app_addr(ciovec_app->buf_offset, ciovec_app->buf_len)) { + ciovec = ciovec_begin; + + for (i = 0; i < iovs_len; i++, iovec_app++, ciovec++) { + if (!validate_app_addr((uint64)iovec_app->buf_offset, + (uint64)iovec_app->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - ciovec->buf = (char*)addr_app_to_native(ciovec_app->buf_offset); - ciovec->buf_len = ciovec_app->buf_len; + ciovec->buf = (char *)addr_app_to_native((uint64)iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; } - err = wasmtime_ssp_fd_write(wasi_ctx->curfds, fd, - ciovec_begin, iovs_len, &nwritten); + err = wasmtime_ssp_fd_write(exec_env, curfds, fd, ciovec_begin, iovs_len, + &nwritten); if (err) goto fail; - nwritten_app = (uint32*)addr_app_to_native(nwritten_offset); - *(uint32*)nwritten_app = (uint32)nwritten; + *nwritten_app = (uint32)nwritten; /* success */ err = 0; fail: - module_free(mem); + wasm_runtime_free(ciovec_begin); return err; } static wasi_errno_t -wasi_fd_advise(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_filesize_t offset, - wasi_filesize_t len, - wasi_advice_t advice) +wasi_fd_advise(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t offset, + wasi_filesize_t len, wasi_advice_t advice) { 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(wasi_ctx); - return wasmtime_ssp_fd_advise(wasi_ctx->curfds, fd, offset, len, advice); + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_advise(exec_env, curfds, fd, offset, len, advice); } static wasi_errno_t -wasi_fd_allocate(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_filesize_t offset, +wasi_fd_allocate(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t offset, wasi_filesize_t len) { 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(wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; - return wasmtime_ssp_fd_allocate(wasi_ctx->curfds, fd, offset, len); + return wasmtime_ssp_fd_allocate(exec_env, curfds, fd, offset, len); } static wasi_errno_t -wasi_path_create_directory(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len) +wasi_path_create_directory(wasm_exec_env_t exec_env, wasi_fd_t fd, + const char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(path_offset, path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - - return wasmtime_ssp_path_create_directory(wasi_ctx->curfds, fd, - path, path_len); + return wasmtime_ssp_path_create_directory(exec_env, curfds, fd, path, + path_len); } static wasi_errno_t -wasi_path_link(wasm_exec_env_t exec_env, - wasi_fd_t old_fd, - wasi_lookupflags_t old_flags, - int32 old_path_offset /* const char * */, - uint32 old_path_len, - wasi_fd_t new_fd, - int32 new_path_offset /* const char * */, +wasi_path_link(wasm_exec_env_t exec_env, wasi_fd_t old_fd, + wasi_lookupflags_t old_flags, const char *old_path, + uint32 old_path_len, wasi_fd_t new_fd, const char *new_path, uint32 new_path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *old_path, *new_path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(wasi_ctx); - if (!validate_app_addr(old_path_offset, old_path_len) - || !validate_app_addr(new_path_offset, new_path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - old_path = (char*)addr_app_to_native(old_path_offset); - new_path = (char*)addr_app_to_native(new_path_offset); - - return wasmtime_ssp_path_link(wasi_ctx->curfds, wasi_ctx->prestats, - old_fd, old_flags, old_path, old_path_len, - new_fd, new_path, new_path_len); + return wasmtime_ssp_path_link(exec_env, curfds, prestats, old_fd, old_flags, + old_path, old_path_len, new_fd, new_path, + new_path_len); } static wasi_errno_t -wasi_path_open(wasm_exec_env_t exec_env, - wasi_fd_t dirfd, - wasi_lookupflags_t dirflags, - int32 path_offset /* const char * */, - uint32 path_len, - wasi_oflags_t oflags, - wasi_rights_t fs_rights_base, - wasi_rights_t fs_rights_inheriting, - wasi_fdflags_t fs_flags, - int32 fd_offset /* wasi_fd_t * */) +wasi_path_open(wasm_exec_env_t exec_env, wasi_fd_t dirfd, + wasi_lookupflags_t dirflags, const char *path, uint32 path_len, + wasi_oflags_t oflags, wasi_rights_t fs_rights_base, + wasi_rights_t fs_rights_inheriting, wasi_fdflags_t fs_flags, + wasi_fd_t *fd_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - wasi_fd_t fd = (wasi_fd_t)-1; - uint32 *fd_app; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); + wasi_fd_t fd = (wasi_fd_t)-1; /* set fd_app -1 if path open failed */ wasi_errno_t err; - if (!validate_app_addr(path_offset, path_len) - || !validate_app_addr(fd_offset, 1)) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - - err = wasmtime_ssp_path_open(wasi_ctx->curfds, - dirfd, dirflags, - path, path_len, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - &fd); - - fd_app = (wasi_fd_t*)addr_app_to_native(fd_offset); - *fd_app = (uint32)fd; + if (!validate_native_addr(fd_app, (uint64)sizeof(wasi_fd_t))) + return (wasi_errno_t)-1; - WASI_CHECK_ERR(); + err = wasmtime_ssp_path_open(exec_env, curfds, dirfd, dirflags, path, + path_len, oflags, fs_rights_base, + fs_rights_inheriting, fs_flags, &fd); - return 0; + *fd_app = fd; + return err; } static wasi_errno_t -wasi_fd_readdir(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* void *buf */, - uint32 buf_len, - wasi_dircookie_t cookie, - int32 bufused_offset /* size_t * */) +wasi_fd_readdir(wasm_exec_env_t exec_env, wasi_fd_t fd, void *buf, + uint32 buf_len, wasi_dircookie_t cookie, uint32 *bufused_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - void *buf; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); size_t bufused; - uint32 *bufused_app; wasi_errno_t err; - if (!validate_app_addr(buf_offset, buf_len) - || !validate_app_addr(bufused_offset, sizeof(uint32))) + if (!wasi_ctx) return (wasi_errno_t)-1; - buf = (void*)addr_app_to_native(buf_offset); - - err = wasmtime_ssp_fd_readdir(wasi_ctx->curfds, fd, - buf, buf_len, cookie, &bufused); - WASI_CHECK_ERR(); + if (!validate_native_addr(bufused_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; - bufused_app = (uint32*)addr_app_to_native(bufused_offset); - *(uint32*)bufused_app = (uint32)bufused; + err = wasmtime_ssp_fd_readdir(exec_env, curfds, fd, buf, buf_len, cookie, + &bufused); + if (err) + return err; + *bufused_app = (uint32)bufused; return 0; } static wasi_errno_t -wasi_path_readlink(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len, - int32 buf_offset /* char * */, - uint32 buf_len, - int32 bufused_offset /* size_t * */) +wasi_path_readlink(wasm_exec_env_t exec_env, wasi_fd_t fd, const char *path, + uint32 path_len, char *buf, uint32 buf_len, + uint32 *bufused_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path, *buf; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); size_t bufused; - uint32 *bufused_app; wasi_errno_t err; - if (!validate_app_addr(path_offset, path_len) - || !validate_app_addr(buf_offset, buf_len) - || !validate_app_addr(bufused_offset, sizeof(uint32))) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - buf = (char*)addr_app_to_native(buf_offset); + if (!validate_native_addr(bufused_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; - err = wasmtime_ssp_path_readlink(wasi_ctx->curfds, fd, - path, path_len, buf, + err = wasmtime_ssp_path_readlink(exec_env, curfds, fd, path, path_len, buf, buf_len, &bufused); - WASI_CHECK_ERR(); - - bufused_app = (uint32*)addr_app_to_native(bufused_offset); - *(uint32*)bufused_app = (uint32)bufused; + if (err) + return err; + *bufused_app = (uint32)bufused; return 0; } static wasi_errno_t -wasi_path_rename(wasm_exec_env_t exec_env, - wasi_fd_t old_fd, - int32 old_path_offset /* const char * */, - uint32 old_path_len, - wasi_fd_t new_fd, - int32 new_path_offset /* const char * */, - uint32 new_path_len) +wasi_path_rename(wasm_exec_env_t exec_env, wasi_fd_t old_fd, + const char *old_path, uint32 old_path_len, wasi_fd_t new_fd, + const char *new_path, uint32 new_path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *old_path, *new_path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(old_path_offset, old_path_len) - || !validate_app_addr(new_path_offset, new_path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - old_path = (char*)addr_app_to_native(old_path_offset); - new_path = (char*)addr_app_to_native(new_path_offset); - - return wasmtime_ssp_path_rename(wasi_ctx->curfds, old_fd, - old_path, old_path_len, - new_fd, new_path, + return wasmtime_ssp_path_rename(exec_env, curfds, old_fd, old_path, + old_path_len, new_fd, new_path, new_path_len); } static wasi_errno_t -wasi_fd_filestat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* wasi_filestat_t * */) +wasi_fd_filestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_filestat_t *filestat) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_filestat_t filestat, *filestat_app; - wasi_errno_t err; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(buf_offset, sizeof(wasi_filestat_t))) + if (!wasi_ctx) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_filestat_get(wasi_ctx->curfds, fd, &filestat); - WASI_CHECK_ERR(); - - filestat_app = (wasi_filestat_t*)addr_app_to_native(buf_offset); - memcpy(filestat_app, &filestat, sizeof(wasi_filestat_t)); + if (!validate_native_addr(filestat, (uint64)sizeof(wasi_filestat_t))) + return (wasi_errno_t)-1; - return 0; + return wasmtime_ssp_fd_filestat_get(exec_env, curfds, fd, filestat); } static wasi_errno_t -wasi_fd_filestat_set_times(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_timestamp_t st_atim, - wasi_timestamp_t st_mtim, +wasi_fd_filestat_set_times(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_timestamp_t st_atim, wasi_timestamp_t st_mtim, wasi_fstflags_t fstflags) { 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(wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; - return wasmtime_ssp_fd_filestat_set_times(wasi_ctx->curfds, fd, - st_atim, st_mtim, fstflags); + return wasmtime_ssp_fd_filestat_set_times(exec_env, curfds, fd, st_atim, + st_mtim, fstflags); } static wasi_errno_t -wasi_fd_filestat_set_size(wasm_exec_env_t exec_env, - wasi_fd_t fd, +wasi_fd_filestat_set_size(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_filesize_t st_size) { 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(wasi_ctx); - return wasmtime_ssp_fd_filestat_set_size(wasi_ctx->curfds, fd, st_size); + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_filestat_set_size(exec_env, curfds, fd, st_size); } static wasi_errno_t -wasi_path_filestat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_lookupflags_t flags, - int32 path_offset /* const char * */, - uint32 path_len, - int32 buf_offset /* wasi_filestat_t * */) +wasi_path_filestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_lookupflags_t flags, const char *path, + uint32 path_len, wasi_filestat_t *filestat) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - wasi_filestat_t filestat, *filestat_app; - wasi_errno_t err; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(path_offset, path_len) - || !validate_app_addr(buf_offset, sizeof(wasi_filestat_t))) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - - err = wasmtime_ssp_path_filestat_get(wasi_ctx->curfds, fd, - flags, path, path_len, &filestat); - WASI_CHECK_ERR(); - - filestat_app = (wasi_filestat_t*)addr_app_to_native(buf_offset); - memcpy(filestat_app, &filestat, sizeof(wasi_filestat_t)); + if (!validate_native_addr(filestat, (uint64)sizeof(wasi_filestat_t))) + return (wasi_errno_t)-1; - return 0; + return wasmtime_ssp_path_filestat_get(exec_env, curfds, fd, flags, path, + path_len, filestat); } static wasi_errno_t -wasi_path_filestat_set_times(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_lookupflags_t flags, - int32 path_offset /* const char * */, - uint32 path_len, - wasi_timestamp_t st_atim, - wasi_timestamp_t st_mtim, - wasi_fstflags_t fstflags) +wasi_path_filestat_set_times(wasm_exec_env_t exec_env, wasi_fd_t fd, + wasi_lookupflags_t flags, const char *path, + uint32 path_len, wasi_timestamp_t st_atim, + wasi_timestamp_t st_mtim, wasi_fstflags_t fstflags) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(path_offset, path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - - return wasmtime_ssp_path_filestat_set_times(wasi_ctx->curfds, fd, - flags, path, path_len, - st_atim, st_mtim, fstflags); + return wasmtime_ssp_path_filestat_set_times(exec_env, curfds, fd, flags, + path, path_len, st_atim, + st_mtim, fstflags); } static wasi_errno_t -wasi_path_symlink(wasm_exec_env_t exec_env, - int32 old_path_offset /* const char * */, - uint32 old_path_len, - wasi_fd_t fd, - int32 new_path_offset /* const char * */, +wasi_path_symlink(wasm_exec_env_t exec_env, const char *old_path, + uint32 old_path_len, wasi_fd_t fd, const char *new_path, uint32 new_path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *old_path, *new_path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(wasi_ctx); - if (!validate_app_addr(old_path_offset, old_path_len) - || !validate_app_addr(new_path_offset, new_path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - old_path = (char*)addr_app_to_native(old_path_offset); - new_path = (char*)addr_app_to_native(new_path_offset); - - return wasmtime_ssp_path_symlink(wasi_ctx->curfds, wasi_ctx->prestats, - old_path, old_path_len, fd, new_path, - new_path_len); + return wasmtime_ssp_path_symlink(exec_env, curfds, prestats, old_path, + old_path_len, fd, new_path, new_path_len); } static wasi_errno_t -wasi_path_unlink_file(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, +wasi_path_unlink_file(wasm_exec_env_t exec_env, wasi_fd_t fd, const char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(path_offset, path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - - return wasmtime_ssp_path_unlink_file(wasi_ctx->curfds, fd, path, path_len); + return wasmtime_ssp_path_unlink_file(exec_env, curfds, fd, path, path_len); } static wasi_errno_t -wasi_path_remove_directory(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len) +wasi_path_remove_directory(wasm_exec_env_t exec_env, wasi_fd_t fd, + const char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); - if (!validate_app_addr(path_offset, path_len)) + if (!wasi_ctx) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); + return wasmtime_ssp_path_remove_directory(exec_env, 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_uint64(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(struct fd_table *curfds, + 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, (uint32)nsubscriptions), + time_quant = (__wasi_timestamp_t)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, (uint32)size_to_copy, in, (uint32)size_to_copy); + + while (timeout == (__wasi_timestamp_t)-1 || elapsed <= timeout) { + /* update timeout for clock subscription events */ + update_clock_subscription_data( + in_copy, (uint32)nsubscriptions, + min_uint64(time_quant, timeout - elapsed)); + err = wasmtime_ssp_poll_oneoff(exec_env, curfds, in_copy, out, + nsubscriptions, nevents); + elapsed += time_quant; - return wasmtime_ssp_path_remove_directory(wasi_ctx->curfds, fd, path, path_len); + if (err) { + wasm_runtime_free(in_copy); + return err; + } + + if (wasm_cluster_is_thread_terminated(exec_env)) { + wasm_runtime_free(in_copy); + return __WASI_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, - int32 in_offset /* const wasi_subscription_t * */, - int32 out_offset /* wasi_event_t * */, - uint32 nsubscriptions, - int32 nevents_offset /* size_t * */) +wasi_poll_oneoff(wasm_exec_env_t exec_env, const wasi_subscription_t *in, + wasi_event_t *out, uint32 nsubscriptions, uint32 *nevents_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_subscription_t *in; - wasi_event_t *out; - size_t nevents; - uint32 *nevents_app; + struct fd_table *curfds = wasi_ctx_get_curfds(wasi_ctx); + size_t nevents = 0; wasi_errno_t err; - if (!validate_app_addr(in_offset, sizeof(wasi_subscription_t)) - || !validate_app_addr(out_offset, sizeof(wasi_event_t)) - || !validate_app_addr(nevents_offset, sizeof(uint32))) + if (!wasi_ctx) return (wasi_errno_t)-1; - in = (wasi_subscription_t*)addr_app_to_native(in_offset); - out = (wasi_event_t*)addr_app_to_native(out_offset); - - err = wasmtime_ssp_poll_oneoff(wasi_ctx->curfds, in, out, nsubscriptions, &nevents); - WASI_CHECK_ERR(); + if (!validate_native_addr((void *)in, (uint64)sizeof(wasi_subscription_t)) + || !validate_native_addr(out, (uint64)sizeof(wasi_event_t)) + || !validate_native_addr(nevents_app, (uint64)sizeof(uint32))) + return (wasi_errno_t)-1; - nevents_app = (uint32*)addr_app_to_native(nevents_offset); - *(uint32*)nevents_app = (uint32)nevents; +#if WASM_ENABLE_THREAD_MGR == 0 + err = wasmtime_ssp_poll_oneoff(exec_env, curfds, in, out, nsubscriptions, + &nevents); +#else + err = execute_interruptible_poll_oneoff(curfds, in, out, nsubscriptions, + &nevents, exec_env); +#endif + if (err) + return err; + *nevents_app = (uint32)nevents; return 0; } -void wasi_proc_exit(wasm_exec_env_t exec_env, wasi_exitcode_t rval) +static void +wasi_proc_exit(wasm_exec_env_t exec_env, wasi_exitcode_t rval) { wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + /* Here throwing exception is just to let wasm app exit, + the upper layer should clear the exception and return + as normal */ wasm_runtime_set_exception(module_inst, "wasi proc exit"); + wasi_ctx->exit_code = rval; } static wasi_errno_t @@ -1082,239 +1141,1271 @@ wasi_proc_raise(wasm_exec_env_t exec_env, wasi_signal_t sig) } static wasi_errno_t -wasi_random_get(wasm_exec_env_t exec_env, - int32 buf_offset /* void * */, - uint32 buf_len) +wasi_random_get(wasm_exec_env_t exec_env, void *buf, uint32 buf_len) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *buf; - - if (!validate_app_addr(buf_offset, buf_len)) - return (wasi_errno_t)-1; - - buf = (void*)addr_app_to_native(buf_offset); + (void)exec_env; return wasmtime_ssp_random_get(buf, buf_len); } static wasi_errno_t -wasi_sock_recv(wasm_exec_env_t exec_env, - wasi_fd_t sock, - int32 ri_data_offset /* const wasi_iovec_t * */, - uint32 ri_data_len, - wasi_riflags_t ri_flags, - int32 ro_datalen_offset /* size_t * */, - int32 ro_flags_offset /* wasi_roflags_t * */) +wasi_sock_accept(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_fdflags_t flags, + wasi_fd_t *fd_new) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_iovec_t *iovec, *iovec_begin; - int32 mem; - iovec_app_t *ri_data_app; - uint32 i; - size_t ro_datalen; - wasi_roflags_t ro_flags; - uint32 *ro_datalen_app, *ro_flags_app; - uint64 total_size; - wasi_errno_t err; - - total_size = sizeof(iovec_app_t) * (uint64)ri_data_len; - if (!validate_app_addr(ro_datalen_offset, (uint32)sizeof(uint32)) - || !validate_app_addr(ro_flags_offset, (uint32)sizeof(uint32)) - || total_size >= UINT32_MAX - || !validate_app_addr(ri_data_offset, (uint32)total_size)) - return (wasi_errno_t)-1; + struct fd_table *curfds = NULL; - ri_data_app = (iovec_app_t*)addr_app_to_native(ri_data_offset); + if (!wasi_ctx) + return __WASI_EACCES; - total_size = sizeof(wasi_iovec_t) * (uint64)ri_data_len; - if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) - return (wasi_errno_t)-1; + if (!validate_native_addr(fd_new, sizeof(*fd_new))) + return __WASI_EINVAL; - iovec = iovec_begin = (wasi_iovec_t*)addr_app_to_native(mem); + curfds = wasi_ctx_get_curfds(wasi_ctx); - for (i = 0; i < ri_data_len; i++, ri_data_app++, iovec++) { - if (!validate_app_addr(ri_data_app->buf_offset, ri_data_app->buf_len)) { - err = (wasi_errno_t)-1; - goto fail; - } - iovec->buf = (void*)addr_app_to_native(ri_data_app->buf_offset); - iovec->buf_len = ri_data_app->buf_len; - } + return wasi_ssp_sock_accept(exec_env, curfds, fd, flags, fd_new); +} - err = wasmtime_ssp_sock_recv(wasi_ctx->curfds, sock, - iovec_begin, ri_data_len, - ri_flags, &ro_datalen, - &ro_flags); - if (err) - goto fail; +static wasi_errno_t +wasi_sock_addr_local(wasm_exec_env_t exec_env, wasi_fd_t fd, + __wasi_addr_t *addr) +{ + 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 = NULL; - ro_datalen_app = (uint32*)addr_app_to_native(ro_datalen_offset); - ro_flags_app = (uint32*)addr_app_to_native(ro_flags_offset); + if (!wasi_ctx) + return __WASI_EACCES; - *(uint32*)ro_datalen_app = (uint32)ro_datalen; - *(uint32*)ro_flags_app = (uint32)ro_flags; + if (!validate_native_addr(addr, (uint64)sizeof(__wasi_addr_t))) + return __WASI_EINVAL; - /* success */ - err = 0; + curfds = wasi_ctx_get_curfds(wasi_ctx); -fail: - module_free(mem); - return err; + return wasi_ssp_sock_addr_local(exec_env, curfds, fd, addr); } static wasi_errno_t -wasi_sock_send(wasm_exec_env_t exec_env, - wasi_fd_t sock, - int32 si_data_offset /* const wasi_ciovec_t * */, - uint32 si_data_len, - wasi_siflags_t si_flags, - int32 so_datalen_offset /* size_t * */) +wasi_sock_addr_remote(wasm_exec_env_t exec_env, wasi_fd_t fd, + __wasi_addr_t *addr) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; - wasi_ciovec_t *ciovec, *ciovec_begin; - iovec_app_t *si_data_app; - uint32 i; - size_t so_datalen; - uint32 *so_datalen_app; - uint64 total_size; - wasi_errno_t err; + struct fd_table *curfds = NULL; - total_size = sizeof(iovec_app_t) * (uint64)si_data_len; - if (!validate_app_addr(so_datalen_offset, sizeof(uint32)) - || total_size >= UINT32_MAX - || !validate_app_addr(si_data_offset, (uint32)total_size)) - return (wasi_errno_t)-1; + if (!wasi_ctx) + return __WASI_EACCES; - si_data_app = (iovec_app_t*)addr_app_to_native(si_data_offset); + if (!validate_native_addr(addr, (uint64)sizeof(__wasi_addr_t))) + return __WASI_EINVAL; - total_size = sizeof(wasi_ciovec_t) * (uint64)si_data_len; - if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) - return (wasi_errno_t)-1; + curfds = wasi_ctx_get_curfds(wasi_ctx); - ciovec_begin = ciovec = (wasi_ciovec_t*)addr_app_to_native(mem); + return wasi_ssp_sock_addr_remote(exec_env, curfds, fd, addr); +} - for (i = 0; i < si_data_len; i++, si_data_app++, ciovec++) { - if (!validate_app_addr(si_data_app->buf_offset, si_data_app->buf_len)) { - err = (wasi_errno_t)-1; - goto fail; - } - ciovec->buf = (char*)addr_app_to_native(si_data_app->buf_offset); - ciovec->buf_len = si_data_app->buf_len; - } +static wasi_errno_t +wasi_sock_addr_resolve(wasm_exec_env_t exec_env, const char *host, + const char *service, __wasi_addr_info_hints_t *hints, + __wasi_addr_info_t *addr_info, + __wasi_size_t addr_info_size, + __wasi_size_t *max_info_size) +{ + 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 = NULL; + char **ns_lookup_list = NULL; - err = wasmtime_ssp_sock_send(wasi_ctx->curfds, sock, - ciovec_begin, si_data_len, - si_flags, &so_datalen); - if (err) - goto fail; + if (!wasi_ctx) + return __WASI_EACCES; - so_datalen_app = (uint32*)addr_app_to_native(so_datalen_offset); - *(uint32*)so_datalen_app = (uint32)so_datalen; + if (!validate_native_addr(hints, sizeof(*hints))) + return __WASI_EINVAL; - /* success */ - err = 0; + uint64_t addr_info_byte_size = sizeof(*addr_info) * addr_info_size; + if (addr_info_byte_size / addr_info_size != sizeof(*addr_info)) + return __WASI_EINVAL; -fail: - module_free(mem); - return err; + if (!validate_native_addr(addr_info, addr_info_byte_size)) + return __WASI_EINVAL; + + if (!validate_native_addr(max_info_size, sizeof(*max_info_size))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + ns_lookup_list = wasi_ctx_get_ns_lookup_list(wasi_ctx); + + return wasi_ssp_sock_addr_resolve(exec_env, curfds, ns_lookup_list, host, + service, hints, addr_info, addr_info_size, + max_info_size); } static wasi_errno_t -wasi_sock_shutdown(wasm_exec_env_t exec_env, - wasi_fd_t sock, wasi_sdflags_t how) +wasi_sock_bind(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_addr_t *addr) { 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 = NULL; + struct addr_pool *addr_pool = NULL; - return wasmtime_ssp_sock_shutdown(wasi_ctx->curfds, sock, how); + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(addr, sizeof(*addr))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + addr_pool = wasi_ctx_get_addr_pool(wasi_ctx); + + return wasi_ssp_sock_bind(exec_env, curfds, addr_pool, fd, addr); +} + +static wasi_errno_t +wasi_sock_close(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + (void)exec_env; + (void)fd; + + return __WASI_ENOSYS; +} + +static wasi_errno_t +wasi_sock_connect(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_addr_t *addr) +{ + 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 = NULL; + struct addr_pool *addr_pool = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(addr, sizeof(*addr))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + addr_pool = wasi_ctx_get_addr_pool(wasi_ctx); + + return wasi_ssp_sock_connect(exec_env, curfds, addr_pool, fd, addr); +} + +static wasi_errno_t +wasi_sock_get_broadcast(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_broadcast(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_get_keep_alive(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_keep_alive(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_get_linger(wasm_exec_env_t exec_env, wasi_fd_t fd, bool *is_enabled, + int *linger_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool)) + || !validate_native_addr(linger_s, (uint64)sizeof(int))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_linger(exec_env, curfds, fd, is_enabled, + linger_s); +} + +static wasi_errno_t +wasi_sock_get_recv_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + size_t *size) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(size, (uint64)sizeof(wasi_size_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_recv_buf_size(exec_env, curfds, fd, size); +} + +static wasi_errno_t +wasi_sock_get_recv_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint64_t *timeout_us) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(timeout_us, (uint64)sizeof(uint64_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_recv_timeout(exec_env, curfds, fd, timeout_us); +} + +static wasi_errno_t +wasi_sock_get_reuse_addr(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_reuse_addr(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_get_reuse_port(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_reuse_port(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_get_send_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, + size_t *size) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(size, (uint64)sizeof(__wasi_size_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_send_buf_size(exec_env, curfds, fd, size); +} + +static wasi_errno_t +wasi_sock_get_send_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint64_t *timeout_us) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(timeout_us, (uint64)sizeof(uint64_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_send_timeout(exec_env, curfds, fd, timeout_us); +} + +static wasi_errno_t +wasi_sock_get_tcp_fastopen_connect(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_tcp_fastopen_connect(exec_env, curfds, fd, + is_enabled); +} + +static wasi_errno_t +wasi_sock_get_tcp_no_delay(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_tcp_no_delay(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_get_tcp_quick_ack(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_tcp_quick_ack(exec_env, curfds, fd, + is_enabled); +} + +static wasi_errno_t +wasi_sock_get_tcp_keep_idle(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint32_t *time_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(time_s, (uint64)sizeof(uint32_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_tcp_keep_idle(exec_env, curfds, fd, time_s); +} + +static wasi_errno_t +wasi_sock_get_tcp_keep_intvl(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint32_t *time_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(time_s, (uint64)sizeof(uint32_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_tcp_keep_intvl(exec_env, curfds, fd, time_s); +} + +static wasi_errno_t +wasi_sock_get_ip_multicast_loop(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool ipv6, bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_ip_multicast_loop(exec_env, curfds, fd, ipv6, + is_enabled); +} + +static wasi_errno_t +wasi_sock_get_ip_ttl(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8_t *ttl_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(ttl_s, (uint64)sizeof(uint8_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_ip_ttl(exec_env, curfds, fd, ttl_s); +} + +static wasi_errno_t +wasi_sock_get_ip_multicast_ttl(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint8_t *ttl_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(ttl_s, (uint64)sizeof(uint8_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_ip_multicast_ttl(exec_env, curfds, fd, ttl_s); +} + +static wasi_errno_t +wasi_sock_get_ipv6_only(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool *is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(is_enabled, (uint64)sizeof(bool))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_get_ipv6_only(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_listen(wasm_exec_env_t exec_env, wasi_fd_t fd, uint32 backlog) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasi_ssp_sock_listen(exec_env, curfds, fd, backlog); +} + +static wasi_errno_t +wasi_sock_open(wasm_exec_env_t exec_env, wasi_fd_t poolfd, + wasi_address_family_t af, wasi_sock_type_t socktype, + wasi_fd_t *sockfd) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(sockfd, sizeof(*sockfd))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasi_ssp_sock_open(exec_env, curfds, poolfd, af, socktype, sockfd); +} + +static wasi_errno_t +wasi_sock_set_broadcast(wasm_exec_env_t exec_env, wasi_fd_t fd, bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_broadcast(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_set_keep_alive(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_keep_alive(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_set_linger(wasm_exec_env_t exec_env, wasi_fd_t fd, bool is_enabled, + int linger_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_linger(exec_env, curfds, fd, is_enabled, + linger_s); +} + +static wasi_errno_t +wasi_sock_set_recv_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, size_t size) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_recv_buf_size(exec_env, curfds, fd, size); +} + +static wasi_errno_t +wasi_sock_set_recv_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint64_t timeout_us) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_recv_timeout(exec_env, curfds, fd, timeout_us); +} + +static wasi_errno_t +wasi_sock_set_reuse_addr(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_reuse_addr(exec_env, curfds, fd, is_enabled); } static wasi_errno_t -wasi_sched_yield(wasm_exec_env_t exec_env) +wasi_sock_set_reuse_port(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool is_enabled) { - return wasmtime_ssp_sched_yield(); + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_reuse_port(exec_env, curfds, fd, is_enabled); } -#define REG_NATIVE_FUNC(func_name) \ - { "wasi_unstable", #func_name, wasi_##func_name } - -typedef struct WASMNativeFuncDef { - const char *module_name; - const char *func_name; - void *func_ptr; -} WASMNativeFuncDef; - -static WASMNativeFuncDef native_func_defs[] = { - REG_NATIVE_FUNC(args_get), - REG_NATIVE_FUNC(args_sizes_get), - REG_NATIVE_FUNC(clock_res_get), - REG_NATIVE_FUNC(clock_time_get), - REG_NATIVE_FUNC(environ_get), - REG_NATIVE_FUNC(environ_sizes_get), - REG_NATIVE_FUNC(fd_prestat_get), - REG_NATIVE_FUNC(fd_prestat_dir_name), - REG_NATIVE_FUNC(fd_close), - REG_NATIVE_FUNC(fd_datasync), - REG_NATIVE_FUNC(fd_pread), - REG_NATIVE_FUNC(fd_pwrite), - REG_NATIVE_FUNC(fd_read), - REG_NATIVE_FUNC(fd_renumber), - REG_NATIVE_FUNC(fd_seek), - REG_NATIVE_FUNC(fd_tell), - REG_NATIVE_FUNC(fd_fdstat_get), - REG_NATIVE_FUNC(fd_fdstat_set_flags), - REG_NATIVE_FUNC(fd_fdstat_set_rights), - REG_NATIVE_FUNC(fd_sync), - REG_NATIVE_FUNC(fd_write), - REG_NATIVE_FUNC(fd_advise), - REG_NATIVE_FUNC(fd_allocate), - REG_NATIVE_FUNC(path_create_directory), - REG_NATIVE_FUNC(path_link), - REG_NATIVE_FUNC(path_open), - REG_NATIVE_FUNC(fd_readdir), - REG_NATIVE_FUNC(path_readlink), - REG_NATIVE_FUNC(path_rename), - REG_NATIVE_FUNC(fd_filestat_get), - REG_NATIVE_FUNC(fd_filestat_set_times), - REG_NATIVE_FUNC(fd_filestat_set_size), - REG_NATIVE_FUNC(path_filestat_get), - REG_NATIVE_FUNC(path_filestat_set_times), - REG_NATIVE_FUNC(path_symlink), - REG_NATIVE_FUNC(path_unlink_file), - REG_NATIVE_FUNC(path_remove_directory), - REG_NATIVE_FUNC(poll_oneoff), - REG_NATIVE_FUNC(proc_exit), - REG_NATIVE_FUNC(proc_raise), - REG_NATIVE_FUNC(random_get), - REG_NATIVE_FUNC(sock_recv), - REG_NATIVE_FUNC(sock_send), - REG_NATIVE_FUNC(sock_shutdown), - REG_NATIVE_FUNC(sched_yield), -}; +static wasi_errno_t +wasi_sock_set_send_buf_size(wasm_exec_env_t exec_env, wasi_fd_t fd, size_t size) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_send_buf_size(exec_env, curfds, fd, size); +} -void * -wasm_native_lookup_libc_wasi_func(const char *module_name, - const char *func_name) +static wasi_errno_t +wasi_sock_set_send_timeout(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint64_t timeout_us) { - uint32 size = sizeof(native_func_defs) / sizeof(WASMNativeFuncDef); - WASMNativeFuncDef *func_def = native_func_defs; - WASMNativeFuncDef *func_def_end = func_def + size; + 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 = NULL; - if (!module_name || !func_name) - return NULL; + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_send_timeout(exec_env, curfds, fd, timeout_us); +} + +static wasi_errno_t +wasi_sock_set_tcp_fastopen_connect(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_tcp_fastopen_connect(exec_env, curfds, fd, + is_enabled); +} + +static wasi_errno_t +wasi_sock_set_tcp_no_delay(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_tcp_no_delay(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +wasi_sock_set_tcp_quick_ack(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_tcp_quick_ack(exec_env, curfds, fd, + is_enabled); +} + +static wasi_errno_t +wasi_sock_set_tcp_keep_idle(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint32_t time_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_tcp_keep_idle(exec_env, curfds, fd, time_s); +} + +static wasi_errno_t +wasi_sock_set_tcp_keep_intvl(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint32_t time_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_tcp_keep_intvl(exec_env, curfds, fd, time_s); +} + +static wasi_errno_t +wasi_sock_set_ip_multicast_loop(wasm_exec_env_t exec_env, wasi_fd_t fd, + bool ipv6, bool is_enabled) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_ip_multicast_loop(exec_env, curfds, fd, ipv6, + is_enabled); +} + +static wasi_errno_t +wasi_sock_set_ip_add_membership(wasm_exec_env_t exec_env, wasi_fd_t fd, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(imr_multiaddr, (uint64)sizeof(__wasi_addr_ip_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_ip_add_membership( + exec_env, curfds, fd, imr_multiaddr, imr_interface); +} + +static wasi_errno_t +wasi_sock_set_ip_drop_membership(wasm_exec_env_t exec_env, wasi_fd_t fd, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + if (!validate_native_addr(imr_multiaddr, (uint64)sizeof(__wasi_addr_ip_t))) + return __WASI_EINVAL; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_ip_drop_membership( + exec_env, curfds, fd, imr_multiaddr, imr_interface); +} + +static wasi_errno_t +wasi_sock_set_ip_ttl(wasm_exec_env_t exec_env, wasi_fd_t fd, uint8_t ttl_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_ip_ttl(exec_env, curfds, fd, ttl_s); +} + +static wasi_errno_t +wasi_sock_set_ip_multicast_ttl(wasm_exec_env_t exec_env, wasi_fd_t fd, + uint8_t ttl_s) +{ + 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 = NULL; + + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_ip_multicast_ttl(exec_env, curfds, fd, ttl_s); +} + +static wasi_errno_t +wasi_sock_set_ipv6_only(wasm_exec_env_t exec_env, wasi_fd_t fd, bool is_enabled) +{ + 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 = NULL; - while (func_def < func_def_end) { - if (!strcmp(func_def->module_name, module_name) - && !strcmp(func_def->func_name, func_name)) - return (void*) (uintptr_t) func_def->func_ptr; - func_def++; + if (!wasi_ctx) + return __WASI_EACCES; + + curfds = wasi_ctx_get_curfds(wasi_ctx); + + return wasmtime_ssp_sock_set_ipv6_only(exec_env, curfds, fd, is_enabled); +} + +static wasi_errno_t +allocate_iovec_app_buffer(wasm_module_inst_t module_inst, + const iovec_app_t *data, uint32 data_len, + uint8 **buf_ptr, uint64 *buf_len) +{ + uint64 total_size = 0; + uint32 i; + uint8 *buf_begin = NULL; + + if (data_len == 0) { + return __WASI_EINVAL; + } + + total_size = sizeof(iovec_app_t) * (uint64)data_len; + if (total_size >= UINT32_MAX + || !validate_native_addr((void *)data, total_size)) + return __WASI_EINVAL; + + for (total_size = 0, i = 0; i < data_len; i++, data++) { + total_size += data->buf_len; + } + + if (total_size == 0) { + return __WASI_EINVAL; + } + + if (total_size >= UINT32_MAX + || !(buf_begin = wasm_runtime_malloc((uint32)total_size))) { + return __WASI_ENOMEM; + } + + *buf_len = total_size; + *buf_ptr = buf_begin; + + return __WASI_ESUCCESS; +} + +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, + uint32 size_to_copy) +{ + uint8 *buf = buf_begin; + uint32 i; + uint32 size_to_copy_into_iovec; + + if (buf_size < size_to_copy) { + return __WASI_EINVAL; + } + + for (i = 0; i < data_len; data++, i++) { + char *native_addr; + + if (!validate_app_addr((uint64)data->buf_offset, + (uint64)data->buf_len)) { + return __WASI_EINVAL; + } + + if (buf >= buf_begin + buf_size + /* integer overflow */ + || data->buf_len > UINTPTR_MAX - (uintptr_t)buf + || buf + data->buf_len > buf_begin + buf_size + || size_to_copy == 0) { + break; + } + + /** + * If our app buffer size is smaller than the amount to be copied, + * only copy the amount in the app buffer. Otherwise, we fill the iovec + * buffer and reduce size to copy on the next iteration + */ + size_to_copy_into_iovec = min_uint32(data->buf_len, size_to_copy); + + native_addr = (void *)addr_app_to_native((uint64)data->buf_offset); + bh_memcpy_s(native_addr, size_to_copy_into_iovec, buf, + size_to_copy_into_iovec); + buf += size_to_copy_into_iovec; + size_to_copy -= size_to_copy_into_iovec; + } + + return __WASI_ESUCCESS; +} + +static wasi_errno_t +wasi_sock_recv_from(wasm_exec_env_t exec_env, wasi_fd_t sock, + iovec_app_t *ri_data, uint32 ri_data_len, + wasi_riflags_t ri_flags, __wasi_addr_t *src_addr, + uint32 *ro_data_len) +{ + /** + * ri_data_len is the length of a list of iovec_app_t, which head is + * ri_data. ro_data_len is the number of bytes received + **/ + 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(wasi_ctx); + uint64 total_size; + uint8 *buf_begin = NULL; + wasi_errno_t err; + size_t recv_bytes = 0; + + if (!wasi_ctx) { + return __WASI_EINVAL; + } + + /* note: src_addr is NULL when called by wasi_sock_recv */ + if (src_addr != NULL && !validate_native_addr(src_addr, sizeof(*src_addr))) + return __WASI_EINVAL; + + if (!validate_native_addr(ro_data_len, (uint64)sizeof(uint32))) + return __WASI_EINVAL; + + err = allocate_iovec_app_buffer(module_inst, ri_data, ri_data_len, + &buf_begin, &total_size); + if (err != __WASI_ESUCCESS) { + goto fail; + } + + memset(buf_begin, 0, total_size); + + *ro_data_len = 0; + err = wasmtime_ssp_sock_recv_from(exec_env, curfds, sock, buf_begin, + total_size, ri_flags, src_addr, + &recv_bytes); + if (err != __WASI_ESUCCESS) { + goto fail; + } + *ro_data_len = (uint32)recv_bytes; + + err = copy_buffer_to_iovec_app(module_inst, buf_begin, (uint32)total_size, + ri_data, ri_data_len, (uint32)recv_bytes); + +fail: + if (buf_begin) { + wasm_runtime_free(buf_begin); + } + return err; +} + +static wasi_errno_t +wasi_sock_recv(wasm_exec_env_t exec_env, wasi_fd_t sock, iovec_app_t *ri_data, + uint32 ri_data_len, wasi_riflags_t ri_flags, uint32 *ro_data_len, + wasi_roflags_t *ro_flags) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_errno_t error; + + if (!validate_native_addr(ro_data_len, sizeof(*ro_data_len))) + return __WASI_EINVAL; + + if (!validate_native_addr(ro_flags, (uint64)sizeof(wasi_roflags_t))) + return __WASI_EINVAL; + + // We call `recvfrom` with NULL source address as `recv` doesn't + // return the source address and this parameter is not used. + *ro_data_len = 0; + error = wasi_sock_recv_from(exec_env, sock, ri_data, ri_data_len, ri_flags, + NULL, ro_data_len); + + return error; +} + +static wasi_errno_t +convert_iovec_app_to_buffer(wasm_module_inst_t module_inst, + const iovec_app_t *si_data, uint32 si_data_len, + uint8 **buf_ptr, uint64 *buf_len) +{ + uint32 i; + const iovec_app_t *si_data_orig = si_data; + uint8 *buf = NULL; + wasi_errno_t error; + + error = allocate_iovec_app_buffer(module_inst, si_data, si_data_len, + buf_ptr, buf_len); + if (error != __WASI_ESUCCESS) { + return error; + } + + buf = *buf_ptr; + si_data = si_data_orig; + for (i = 0; i < si_data_len; i++, si_data++) { + char *native_addr; + + if (!validate_app_addr((uint64)si_data->buf_offset, + (uint64)si_data->buf_len)) { + wasm_runtime_free(*buf_ptr); + return __WASI_EINVAL; + } + + native_addr = (char *)addr_app_to_native((uint64)si_data->buf_offset); + bh_memcpy_s(buf, si_data->buf_len, native_addr, si_data->buf_len); + buf += si_data->buf_len; + } + + return __WASI_ESUCCESS; +} + +static wasi_errno_t +wasi_sock_send(wasm_exec_env_t exec_env, wasi_fd_t sock, + const iovec_app_t *si_data, uint32 si_data_len, + wasi_siflags_t si_flags, uint32 *so_data_len) +{ + /** + * si_data_len is the length of a list of iovec_app_t, which head is + * si_data. so_data_len is the number of bytes sent + **/ + 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(wasi_ctx); + uint64 buf_size = 0; + uint8 *buf = NULL; + wasi_errno_t err; + size_t send_bytes = 0; + + if (!wasi_ctx) { + return __WASI_EINVAL; + } + + if (!validate_native_addr(so_data_len, (uint64)sizeof(uint32))) + return __WASI_EINVAL; + + err = convert_iovec_app_to_buffer(module_inst, si_data, si_data_len, &buf, + &buf_size); + if (err != __WASI_ESUCCESS) + return err; + + *so_data_len = 0; + err = wasmtime_ssp_sock_send(exec_env, curfds, sock, buf, buf_size, + &send_bytes); + *so_data_len = (uint32)send_bytes; + + wasm_runtime_free(buf); + + return err; +} + +static wasi_errno_t +wasi_sock_send_to(wasm_exec_env_t exec_env, wasi_fd_t sock, + const iovec_app_t *si_data, uint32 si_data_len, + wasi_siflags_t si_flags, const __wasi_addr_t *dest_addr, + uint32 *so_data_len) +{ + /** + * si_data_len is the length of a list of iovec_app_t, which head is + * si_data. so_data_len is the number of bytes sent + **/ + 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(wasi_ctx); + uint64 buf_size = 0; + uint8 *buf = NULL; + wasi_errno_t err; + size_t send_bytes = 0; + struct addr_pool *addr_pool = wasi_ctx_get_addr_pool(wasi_ctx); + + if (!wasi_ctx) { + return __WASI_EINVAL; } - return NULL; + if (!validate_native_addr((void *)dest_addr, sizeof(*dest_addr))) + return __WASI_EINVAL; + + if (!validate_native_addr(so_data_len, (uint64)sizeof(uint32))) + return __WASI_EINVAL; + + err = convert_iovec_app_to_buffer(module_inst, si_data, si_data_len, &buf, + &buf_size); + if (err != __WASI_ESUCCESS) + return err; + + *so_data_len = 0; + err = wasmtime_ssp_sock_send_to(exec_env, curfds, addr_pool, sock, buf, + buf_size, si_flags, dest_addr, &send_bytes); + *so_data_len = (uint32)send_bytes; + + wasm_runtime_free(buf); + + return err; +} + +static wasi_errno_t +wasi_sock_shutdown(wasm_exec_env_t exec_env, wasi_fd_t sock, wasi_sdflags_t how) +{ + 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(wasi_ctx); + + if (!wasi_ctx) + return __WASI_EINVAL; + + return wasmtime_ssp_sock_shutdown(exec_env, curfds, sock); +} + +static wasi_errno_t +wasi_sched_yield(wasm_exec_env_t exec_env) +{ + (void)exec_env; + + return wasmtime_ssp_sched_yield(); } +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, wasi_##func_name, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_libc_wasi[] = { + REG_NATIVE_FUNC(args_get, "(**)i"), + REG_NATIVE_FUNC(args_sizes_get, "(**)i"), + REG_NATIVE_FUNC(clock_res_get, "(i*)i"), + REG_NATIVE_FUNC(clock_time_get, "(iI*)i"), + REG_NATIVE_FUNC(environ_get, "(**)i"), + REG_NATIVE_FUNC(environ_sizes_get, "(**)i"), + REG_NATIVE_FUNC(fd_prestat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_prestat_dir_name, "(i*~)i"), + REG_NATIVE_FUNC(fd_close, "(i)i"), + REG_NATIVE_FUNC(fd_datasync, "(i)i"), + REG_NATIVE_FUNC(fd_pread, "(i*iI*)i"), + REG_NATIVE_FUNC(fd_pwrite, "(i*iI*)i"), + REG_NATIVE_FUNC(fd_read, "(i*i*)i"), + REG_NATIVE_FUNC(fd_renumber, "(ii)i"), + REG_NATIVE_FUNC(fd_seek, "(iIi*)i"), + REG_NATIVE_FUNC(fd_tell, "(i*)i"), + REG_NATIVE_FUNC(fd_fdstat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_fdstat_set_flags, "(ii)i"), + REG_NATIVE_FUNC(fd_fdstat_set_rights, "(iII)i"), + REG_NATIVE_FUNC(fd_sync, "(i)i"), + REG_NATIVE_FUNC(fd_write, "(i*i*)i"), + REG_NATIVE_FUNC(fd_advise, "(iIIi)i"), + REG_NATIVE_FUNC(fd_allocate, "(iII)i"), + REG_NATIVE_FUNC(path_create_directory, "(i*~)i"), + REG_NATIVE_FUNC(path_link, "(ii*~i*~)i"), + REG_NATIVE_FUNC(path_open, "(ii*~iIIi*)i"), + REG_NATIVE_FUNC(fd_readdir, "(i*~I*)i"), + REG_NATIVE_FUNC(path_readlink, "(i*~*~*)i"), + REG_NATIVE_FUNC(path_rename, "(i*~i*~)i"), + REG_NATIVE_FUNC(fd_filestat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_filestat_set_times, "(iIIi)i"), + REG_NATIVE_FUNC(fd_filestat_set_size, "(iI)i"), + REG_NATIVE_FUNC(path_filestat_get, "(ii*~*)i"), + REG_NATIVE_FUNC(path_filestat_set_times, "(ii*~IIi)i"), + REG_NATIVE_FUNC(path_symlink, "(*~i*~)i"), + REG_NATIVE_FUNC(path_unlink_file, "(i*~)i"), + REG_NATIVE_FUNC(path_remove_directory, "(i*~)i"), + REG_NATIVE_FUNC(poll_oneoff, "(**i*)i"), + REG_NATIVE_FUNC(proc_exit, "(i)"), + REG_NATIVE_FUNC(proc_raise, "(i)i"), + REG_NATIVE_FUNC(random_get, "(*~)i"), + REG_NATIVE_FUNC(sock_accept, "(ii*)i"), + REG_NATIVE_FUNC(sock_addr_local, "(i*)i"), + REG_NATIVE_FUNC(sock_addr_remote, "(i*)i"), + REG_NATIVE_FUNC(sock_addr_resolve, "($$**i*)i"), + REG_NATIVE_FUNC(sock_bind, "(i*)i"), + REG_NATIVE_FUNC(sock_close, "(i)i"), + REG_NATIVE_FUNC(sock_connect, "(i*)i"), + REG_NATIVE_FUNC(sock_get_broadcast, "(i*)i"), + REG_NATIVE_FUNC(sock_get_keep_alive, "(i*)i"), + REG_NATIVE_FUNC(sock_get_linger, "(i**)i"), + REG_NATIVE_FUNC(sock_get_recv_buf_size, "(i*)i"), + REG_NATIVE_FUNC(sock_get_recv_timeout, "(i*)i"), + REG_NATIVE_FUNC(sock_get_reuse_addr, "(i*)i"), + REG_NATIVE_FUNC(sock_get_reuse_port, "(i*)i"), + REG_NATIVE_FUNC(sock_get_send_buf_size, "(i*)i"), + REG_NATIVE_FUNC(sock_get_send_timeout, "(i*)i"), + REG_NATIVE_FUNC(sock_get_tcp_fastopen_connect, "(i*)i"), + REG_NATIVE_FUNC(sock_get_tcp_keep_idle, "(i*)i"), + REG_NATIVE_FUNC(sock_get_tcp_keep_intvl, "(i*)i"), + REG_NATIVE_FUNC(sock_get_tcp_no_delay, "(i*)i"), + REG_NATIVE_FUNC(sock_get_tcp_quick_ack, "(i*)i"), + REG_NATIVE_FUNC(sock_get_ip_multicast_loop, "(ii*)i"), + REG_NATIVE_FUNC(sock_get_ip_multicast_ttl, "(i*)i"), + REG_NATIVE_FUNC(sock_get_ip_ttl, "(i*)i"), + REG_NATIVE_FUNC(sock_get_ipv6_only, "(i*)i"), + REG_NATIVE_FUNC(sock_listen, "(ii)i"), + REG_NATIVE_FUNC(sock_open, "(iii*)i"), + REG_NATIVE_FUNC(sock_recv, "(i*ii**)i"), + REG_NATIVE_FUNC(sock_recv_from, "(i*ii**)i"), + REG_NATIVE_FUNC(sock_send, "(i*ii*)i"), + REG_NATIVE_FUNC(sock_send_to, "(i*ii**)i"), + REG_NATIVE_FUNC(sock_set_broadcast, "(ii)i"), + REG_NATIVE_FUNC(sock_set_keep_alive, "(ii)i"), + REG_NATIVE_FUNC(sock_set_linger, "(iii)i"), + REG_NATIVE_FUNC(sock_set_recv_buf_size, "(ii)i"), + REG_NATIVE_FUNC(sock_set_recv_timeout, "(iI)i"), + REG_NATIVE_FUNC(sock_set_reuse_addr, "(ii)i"), + REG_NATIVE_FUNC(sock_set_reuse_port, "(ii)i"), + REG_NATIVE_FUNC(sock_set_send_buf_size, "(ii)i"), + REG_NATIVE_FUNC(sock_set_send_timeout, "(iI)i"), + REG_NATIVE_FUNC(sock_set_tcp_fastopen_connect, "(ii)i"), + REG_NATIVE_FUNC(sock_set_tcp_keep_idle, "(ii)i"), + REG_NATIVE_FUNC(sock_set_tcp_keep_intvl, "(ii)i"), + REG_NATIVE_FUNC(sock_set_tcp_no_delay, "(ii)i"), + REG_NATIVE_FUNC(sock_set_tcp_quick_ack, "(ii)i"), + REG_NATIVE_FUNC(sock_set_ip_multicast_loop, "(iii)i"), + REG_NATIVE_FUNC(sock_set_ip_multicast_ttl, "(ii)i"), + REG_NATIVE_FUNC(sock_set_ip_add_membership, "(i*i)i"), + REG_NATIVE_FUNC(sock_set_ip_drop_membership, "(i*i)i"), + REG_NATIVE_FUNC(sock_set_ip_ttl, "(ii)i"), + REG_NATIVE_FUNC(sock_set_ipv6_only, "(ii)i"), + REG_NATIVE_FUNC(sock_shutdown, "(ii)i"), + REG_NATIVE_FUNC(sched_yield, "()i"), +}; + +uint32 +get_libc_wasi_export_apis(NativeSymbol **p_libc_wasi_apis) +{ + *p_libc_wasi_apis = native_symbols_libc_wasi; + return sizeof(native_symbols_libc_wasi) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h index 53219f438e..580ce79871 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h @@ -6,46 +6,58 @@ #ifndef _LIBC_WASI_WRAPPER_H #define _LIBC_WASI_WRAPPER_H -#include "wasmtime_ssp.h" #include "posix.h" #ifdef __cplusplus extern "C" { #endif -typedef __wasi_errno_t wasi_errno_t; -typedef __wasi_fd_t wasi_fd_t; -typedef __wasi_clockid_t wasi_clockid_t; -typedef __wasi_timestamp_t wasi_timestamp_t; -typedef __wasi_prestat_t wasi_prestat_t; -typedef __wasi_iovec_t wasi_iovec_t; -typedef __wasi_ciovec_t wasi_ciovec_t; -typedef __wasi_filetype_t wasi_filetype_t; -typedef __wasi_filesize_t wasi_filesize_t; -typedef __wasi_filedelta_t wasi_filedelta_t; -typedef __wasi_whence_t wasi_whence_t; -typedef __wasi_fdstat_t wasi_fdstat_t; -typedef __wasi_fdflags_t wasi_fdflags_t; -typedef __wasi_rights_t wasi_rights_t; +typedef __wasi_address_family_t wasi_address_family_t; +typedef __wasi_addr_t wasi_addr_t; typedef __wasi_advice_t wasi_advice_t; -typedef __wasi_lookupflags_t wasi_lookupflags_t; -typedef __wasi_oflags_t wasi_oflags_t; +typedef __wasi_ciovec_t wasi_ciovec_t; +typedef __wasi_clockid_t wasi_clockid_t; typedef __wasi_dircookie_t wasi_dircookie_t; -typedef __wasi_filestat_t wasi_filestat_t; -typedef __wasi_fstflags_t wasi_fstflags_t; -typedef __wasi_subscription_t wasi_subscription_t; +// __wasi_errno_t is typedef'd to uint16 which is correct according to the ABI +// specification. However, in WASM, the smallest integer type is int32. If we +// return uint16, we would rely on language SDKs to implement the correct +// behaviour of casting to uint16 before checking the value or using it any way. +// Failure to do so can cause tricky bugs as the upper 16 bits of the error +// result are not guaranteed to be zero'ed by us so the result essentially +// contains garbage from the WASM app perspective. To prevent this, we return +// uint32 directly instead so as not to be reliant on the correct behaviour of +// any current/future WASI SDK implementations. +typedef uint32_t wasi_errno_t; typedef __wasi_event_t wasi_event_t; typedef __wasi_exitcode_t wasi_exitcode_t; -typedef __wasi_signal_t wasi_signal_t; +typedef __wasi_fdflags_t wasi_fdflags_t; +typedef __wasi_fdstat_t wasi_fdstat_t; +typedef __wasi_fd_t wasi_fd_t; +typedef __wasi_filedelta_t wasi_filedelta_t; +typedef __wasi_filesize_t wasi_filesize_t; +typedef __wasi_filestat_t wasi_filestat_t; +typedef __wasi_filetype_t wasi_filetype_t; +typedef __wasi_fstflags_t wasi_fstflags_t; +typedef __wasi_iovec_t wasi_iovec_t; +typedef __wasi_ip_port_t wasi_ip_port_t; +typedef __wasi_lookupflags_t wasi_lookupflags_t; +typedef __wasi_oflags_t wasi_oflags_t; +typedef __wasi_preopentype_t wasi_preopentype_t; +typedef __wasi_prestat_t wasi_prestat_t; typedef __wasi_riflags_t wasi_riflags_t; +typedef __wasi_rights_t wasi_rights_t; typedef __wasi_roflags_t wasi_roflags_t; -typedef __wasi_siflags_t wasi_siflags_t; typedef __wasi_sdflags_t wasi_sdflags_t; -typedef __wasi_dircookie_t wasi_dircookie_t; -typedef __wasi_preopentype_t wasi_preopentype_t; +typedef __wasi_siflags_t wasi_siflags_t; +typedef __wasi_signal_t wasi_signal_t; +typedef __wasi_size_t wasi_size_t; +typedef __wasi_sock_type_t wasi_sock_type_t; +typedef __wasi_subscription_t wasi_subscription_t; +typedef __wasi_timestamp_t wasi_timestamp_t; +typedef __wasi_whence_t wasi_whence_t; #ifdef __cplusplus } #endif -#endif /* end of _LIBC_WASI_WRAPPER_H */ +#endif /* end of _LIBC_WASI_WRAPPER_H */ \ No newline at end of file diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h index 75aac38926..3972c76ed6 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h @@ -1,871 +1,601 @@ /* - * Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. - * See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. - * - * This file declares an interface similar to WASI, but augmented to expose - * some implementation details such as the curfds arguments that we pass - * around to avoid storing them in TLS. + * 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. + */ + +/** + * The definitions of type, macro and structure in this file should be + * consistent with those in wasi-libc: + * https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/wasi/api.h */ #ifndef WASMTIME_SSP_H #define WASMTIME_SSP_H +#include #include #include -_Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout"); -_Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout"); -_Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout"); -_Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout"); -_Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout"); -_Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout"); -#if 0 -_Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout"); -_Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout"); -#endif +#include "bh_platform.h" +#include "wasm_export.h" #ifdef __cplusplus extern "C" { #endif -typedef uint8_t __wasi_advice_t; -#define __WASI_ADVICE_NORMAL (0) -#define __WASI_ADVICE_SEQUENTIAL (1) -#define __WASI_ADVICE_RANDOM (2) -#define __WASI_ADVICE_WILLNEED (3) -#define __WASI_ADVICE_DONTNEED (4) -#define __WASI_ADVICE_NOREUSE (5) - -typedef uint32_t __wasi_clockid_t; -#define __WASI_CLOCK_REALTIME (0) -#define __WASI_CLOCK_MONOTONIC (1) -#define __WASI_CLOCK_PROCESS_CPUTIME_ID (2) -#define __WASI_CLOCK_THREAD_CPUTIME_ID (3) - -typedef uint64_t __wasi_device_t; - -typedef uint64_t __wasi_dircookie_t; -#define __WASI_DIRCOOKIE_START (0) - -typedef uint16_t __wasi_errno_t; -#define __WASI_ESUCCESS (0) -#define __WASI_E2BIG (1) -#define __WASI_EACCES (2) -#define __WASI_EADDRINUSE (3) -#define __WASI_EADDRNOTAVAIL (4) -#define __WASI_EAFNOSUPPORT (5) -#define __WASI_EAGAIN (6) -#define __WASI_EALREADY (7) -#define __WASI_EBADF (8) -#define __WASI_EBADMSG (9) -#define __WASI_EBUSY (10) -#define __WASI_ECANCELED (11) -#define __WASI_ECHILD (12) -#define __WASI_ECONNABORTED (13) -#define __WASI_ECONNREFUSED (14) -#define __WASI_ECONNRESET (15) -#define __WASI_EDEADLK (16) -#define __WASI_EDESTADDRREQ (17) -#define __WASI_EDOM (18) -#define __WASI_EDQUOT (19) -#define __WASI_EEXIST (20) -#define __WASI_EFAULT (21) -#define __WASI_EFBIG (22) -#define __WASI_EHOSTUNREACH (23) -#define __WASI_EIDRM (24) -#define __WASI_EILSEQ (25) -#define __WASI_EINPROGRESS (26) -#define __WASI_EINTR (27) -#define __WASI_EINVAL (28) -#define __WASI_EIO (29) -#define __WASI_EISCONN (30) -#define __WASI_EISDIR (31) -#define __WASI_ELOOP (32) -#define __WASI_EMFILE (33) -#define __WASI_EMLINK (34) -#define __WASI_EMSGSIZE (35) -#define __WASI_EMULTIHOP (36) -#define __WASI_ENAMETOOLONG (37) -#define __WASI_ENETDOWN (38) -#define __WASI_ENETRESET (39) -#define __WASI_ENETUNREACH (40) -#define __WASI_ENFILE (41) -#define __WASI_ENOBUFS (42) -#define __WASI_ENODEV (43) -#define __WASI_ENOENT (44) -#define __WASI_ENOEXEC (45) -#define __WASI_ENOLCK (46) -#define __WASI_ENOLINK (47) -#define __WASI_ENOMEM (48) -#define __WASI_ENOMSG (49) -#define __WASI_ENOPROTOOPT (50) -#define __WASI_ENOSPC (51) -#define __WASI_ENOSYS (52) -#define __WASI_ENOTCONN (53) -#define __WASI_ENOTDIR (54) -#define __WASI_ENOTEMPTY (55) -#define __WASI_ENOTRECOVERABLE (56) -#define __WASI_ENOTSOCK (57) -#define __WASI_ENOTSUP (58) -#define __WASI_ENOTTY (59) -#define __WASI_ENXIO (60) -#define __WASI_EOVERFLOW (61) -#define __WASI_EOWNERDEAD (62) -#define __WASI_EPERM (63) -#define __WASI_EPIPE (64) -#define __WASI_EPROTO (65) -#define __WASI_EPROTONOSUPPORT (66) -#define __WASI_EPROTOTYPE (67) -#define __WASI_ERANGE (68) -#define __WASI_EROFS (69) -#define __WASI_ESPIPE (70) -#define __WASI_ESRCH (71) -#define __WASI_ESTALE (72) -#define __WASI_ETIMEDOUT (73) -#define __WASI_ETXTBSY (74) -#define __WASI_EXDEV (75) -#define __WASI_ENOTCAPABLE (76) - -typedef uint16_t __wasi_eventrwflags_t; -#define __WASI_EVENT_FD_READWRITE_HANGUP (0x0001) - -typedef uint8_t __wasi_eventtype_t; -#define __WASI_EVENTTYPE_CLOCK (0) -#define __WASI_EVENTTYPE_FD_READ (1) -#define __WASI_EVENTTYPE_FD_WRITE (2) - -typedef uint32_t __wasi_exitcode_t; - -typedef uint32_t __wasi_fd_t; - -typedef uint16_t __wasi_fdflags_t; -#define __WASI_FDFLAG_APPEND (0x0001) -#define __WASI_FDFLAG_DSYNC (0x0002) -#define __WASI_FDFLAG_NONBLOCK (0x0004) -#define __WASI_FDFLAG_RSYNC (0x0008) -#define __WASI_FDFLAG_SYNC (0x0010) - -typedef int64_t __wasi_filedelta_t; - -typedef uint64_t __wasi_filesize_t; - -typedef uint8_t __wasi_filetype_t; -#define __WASI_FILETYPE_UNKNOWN (0) -#define __WASI_FILETYPE_BLOCK_DEVICE (1) -#define __WASI_FILETYPE_CHARACTER_DEVICE (2) -#define __WASI_FILETYPE_DIRECTORY (3) -#define __WASI_FILETYPE_REGULAR_FILE (4) -#define __WASI_FILETYPE_SOCKET_DGRAM (5) -#define __WASI_FILETYPE_SOCKET_STREAM (6) -#define __WASI_FILETYPE_SYMBOLIC_LINK (7) - -typedef uint16_t __wasi_fstflags_t; -#define __WASI_FILESTAT_SET_ATIM (0x0001) -#define __WASI_FILESTAT_SET_ATIM_NOW (0x0002) -#define __WASI_FILESTAT_SET_MTIM (0x0004) -#define __WASI_FILESTAT_SET_MTIM_NOW (0x0008) - -typedef uint64_t __wasi_inode_t; - -typedef uint32_t __wasi_linkcount_t; - -typedef uint32_t __wasi_lookupflags_t; -#define __WASI_LOOKUP_SYMLINK_FOLLOW (0x00000001) - -typedef uint16_t __wasi_oflags_t; -#define __WASI_O_CREAT (0x0001) -#define __WASI_O_DIRECTORY (0x0002) -#define __WASI_O_EXCL (0x0004) -#define __WASI_O_TRUNC (0x0008) - -typedef uint16_t __wasi_riflags_t; -#define __WASI_SOCK_RECV_PEEK (0x0001) -#define __WASI_SOCK_RECV_WAITALL (0x0002) - -typedef uint64_t __wasi_rights_t; -#define __WASI_RIGHT_FD_DATASYNC (0x0000000000000001) -#define __WASI_RIGHT_FD_READ (0x0000000000000002) -#define __WASI_RIGHT_FD_SEEK (0x0000000000000004) -#define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS (0x0000000000000008) -#define __WASI_RIGHT_FD_SYNC (0x0000000000000010) -#define __WASI_RIGHT_FD_TELL (0x0000000000000020) -#define __WASI_RIGHT_FD_WRITE (0x0000000000000040) -#define __WASI_RIGHT_FD_ADVISE (0x0000000000000080) -#define __WASI_RIGHT_FD_ALLOCATE (0x0000000000000100) -#define __WASI_RIGHT_PATH_CREATE_DIRECTORY (0x0000000000000200) -#define __WASI_RIGHT_PATH_CREATE_FILE (0x0000000000000400) -#define __WASI_RIGHT_PATH_LINK_SOURCE (0x0000000000000800) -#define __WASI_RIGHT_PATH_LINK_TARGET (0x0000000000001000) -#define __WASI_RIGHT_PATH_OPEN (0x0000000000002000) -#define __WASI_RIGHT_FD_READDIR (0x0000000000004000) -#define __WASI_RIGHT_PATH_READLINK (0x0000000000008000) -#define __WASI_RIGHT_PATH_RENAME_SOURCE (0x0000000000010000) -#define __WASI_RIGHT_PATH_RENAME_TARGET (0x0000000000020000) -#define __WASI_RIGHT_PATH_FILESTAT_GET (0x0000000000040000) -#define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE (0x0000000000080000) -#define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES (0x0000000000100000) -#define __WASI_RIGHT_FD_FILESTAT_GET (0x0000000000200000) -#define __WASI_RIGHT_FD_FILESTAT_SET_SIZE (0x0000000000400000) -#define __WASI_RIGHT_FD_FILESTAT_SET_TIMES (0x0000000000800000) -#define __WASI_RIGHT_PATH_SYMLINK (0x0000000001000000) -#define __WASI_RIGHT_PATH_REMOVE_DIRECTORY (0x0000000002000000) -#define __WASI_RIGHT_PATH_UNLINK_FILE (0x0000000004000000) -#define __WASI_RIGHT_POLL_FD_READWRITE (0x0000000008000000) -#define __WASI_RIGHT_SOCK_SHUTDOWN (0x0000000010000000) - -typedef uint16_t __wasi_roflags_t; -#define __WASI_SOCK_RECV_DATA_TRUNCATED (0x0001) - -typedef uint8_t __wasi_sdflags_t; -#define __WASI_SHUT_RD (0x01) -#define __WASI_SHUT_WR (0x02) - -typedef uint16_t __wasi_siflags_t; - -typedef uint8_t __wasi_signal_t; -// 0 is reserved; POSIX has special semantics for kill(pid, 0). -#define __WASI_SIGHUP (1) -#define __WASI_SIGINT (2) -#define __WASI_SIGQUIT (3) -#define __WASI_SIGILL (4) -#define __WASI_SIGTRAP (5) -#define __WASI_SIGABRT (6) -#define __WASI_SIGBUS (7) -#define __WASI_SIGFPE (8) -#define __WASI_SIGKILL (9) -#define __WASI_SIGUSR1 (10) -#define __WASI_SIGSEGV (11) -#define __WASI_SIGUSR2 (12) -#define __WASI_SIGPIPE (13) -#define __WASI_SIGALRM (14) -#define __WASI_SIGTERM (15) -#define __WASI_SIGCHLD (16) -#define __WASI_SIGCONT (17) -#define __WASI_SIGSTOP (18) -#define __WASI_SIGTSTP (19) -#define __WASI_SIGTTIN (20) -#define __WASI_SIGTTOU (21) -#define __WASI_SIGURG (22) -#define __WASI_SIGXCPU (23) -#define __WASI_SIGXFSZ (24) -#define __WASI_SIGVTALRM (25) -#define __WASI_SIGPROF (26) -#define __WASI_SIGWINCH (27) -#define __WASI_SIGPOLL (28) -#define __WASI_SIGPWR (29) -#define __WASI_SIGSYS (30) - -typedef uint16_t __wasi_subclockflags_t; -#define __WASI_SUBSCRIPTION_CLOCK_ABSTIME (0x0001) - -typedef uint64_t __wasi_timestamp_t; - -typedef uint64_t __wasi_userdata_t; - -typedef uint8_t __wasi_whence_t; -#define __WASI_WHENCE_CUR (0) -#define __WASI_WHENCE_END (1) -#define __WASI_WHENCE_SET (2) - -typedef uint8_t __wasi_preopentype_t; -#define __WASI_PREOPENTYPE_DIR (0) - -struct fd_table; -struct fd_prestats; -struct argv_environ_values; - -typedef struct __wasi_dirent_t { - __wasi_dircookie_t d_next; - __wasi_inode_t d_ino; - uint32_t d_namlen; - __wasi_filetype_t d_type; -} __wasi_dirent_t __attribute__((aligned(8))); -_Static_assert(offsetof(__wasi_dirent_t, d_next) == 0, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_dirent_t, d_ino) == 8, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_dirent_t, d_namlen) == 16, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_dirent_t, d_type) == 20, "non-wasi data layout"); -_Static_assert(sizeof(__wasi_dirent_t) == 24, "non-wasi data layout"); -_Static_assert(_Alignof(__wasi_dirent_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_event_t { - __wasi_userdata_t userdata; - __wasi_errno_t error; - __wasi_eventtype_t type; - uint8_t __paddings[5]; - union __wasi_event_u { - struct __wasi_event_u_fd_readwrite_t { - __wasi_filesize_t nbytes; - __wasi_eventrwflags_t flags; - uint8_t __paddings[6]; - } fd_readwrite; - } u; -} __wasi_event_t __attribute__((aligned(8))); -_Static_assert(offsetof(__wasi_event_t, userdata) == 0, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_event_t, error) == 8, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_event_t, type) == 10, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_event_t, u.fd_readwrite.nbytes) == 16, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_event_t, u.fd_readwrite.flags) == 24, "non-wasi data layout"); -_Static_assert(sizeof(__wasi_event_t) == 32, "non-wasi data layout"); -_Static_assert(_Alignof(__wasi_event_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_prestat_t { - __wasi_preopentype_t pr_type; - union __wasi_prestat_u { - struct __wasi_prestat_u_dir_t { - size_t pr_name_len; - } dir; - } u; -} __wasi_prestat_t; -_Static_assert(offsetof(__wasi_prestat_t, pr_type) == 0, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 4, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 8, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - sizeof(__wasi_prestat_t) == 8, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - sizeof(__wasi_prestat_t) == 16, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - _Alignof(__wasi_prestat_t) == 4, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - _Alignof(__wasi_prestat_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_fdstat_t { - __wasi_filetype_t fs_filetype; - __wasi_fdflags_t fs_flags; - uint8_t __paddings[4]; - __wasi_rights_t fs_rights_base; - __wasi_rights_t fs_rights_inheriting; -} __wasi_fdstat_t __attribute__((aligned(8))); -_Static_assert( - offsetof(__wasi_fdstat_t, fs_filetype) == 0, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_fdstat_t, fs_rights_base) == 8, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, - "non-wasi data layout"); -_Static_assert(sizeof(__wasi_fdstat_t) == 24, "non-wasi data layout"); -_Static_assert(_Alignof(__wasi_fdstat_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_filestat_t { - __wasi_device_t st_dev; - __wasi_inode_t st_ino; - __wasi_filetype_t st_filetype; - __wasi_linkcount_t st_nlink; - __wasi_filesize_t st_size; - __wasi_timestamp_t st_atim; - __wasi_timestamp_t st_mtim; - __wasi_timestamp_t st_ctim; -} __wasi_filestat_t __attribute__((aligned(8))); -_Static_assert(offsetof(__wasi_filestat_t, st_dev) == 0, "non-wasi data layout"); -_Static_assert(offsetof(__wasi_filestat_t, st_ino) == 8, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_filestat_t, st_filetype) == 16, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_filestat_t, st_nlink) == 20, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_filestat_t, st_size) == 24, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_filestat_t, st_atim) == 32, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_filestat_t, st_mtim) == 40, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_filestat_t, st_ctim) == 48, "non-wasi data layout"); -_Static_assert(sizeof(__wasi_filestat_t) == 56, "non-wasi data layout"); -_Static_assert(_Alignof(__wasi_filestat_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_ciovec_t { - const void *buf; - size_t buf_len; -} __wasi_ciovec_t; -_Static_assert(offsetof(__wasi_ciovec_t, buf) == 0, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - offsetof(__wasi_ciovec_t, buf_len) == 4, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - offsetof(__wasi_ciovec_t, buf_len) == 8, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - sizeof(__wasi_ciovec_t) == 8, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - sizeof(__wasi_ciovec_t) == 16, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - _Alignof(__wasi_ciovec_t) == 4, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - _Alignof(__wasi_ciovec_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_iovec_t { - void *buf; - size_t buf_len; -} __wasi_iovec_t; -_Static_assert(offsetof(__wasi_iovec_t, buf) == 0, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - offsetof(__wasi_iovec_t, buf_len) == 4, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - offsetof(__wasi_iovec_t, buf_len) == 8, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - sizeof(__wasi_iovec_t) == 8, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - sizeof(__wasi_iovec_t) == 16, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 4 || - _Alignof(__wasi_iovec_t) == 4, "non-wasi data layout"); -_Static_assert(sizeof(void *) != 8 || - _Alignof(__wasi_iovec_t) == 8, "non-wasi data layout"); - -typedef struct __wasi_subscription_t { - __wasi_userdata_t userdata; - __wasi_eventtype_t type; - uint8_t __paddings[7]; - union __wasi_subscription_u { - struct __wasi_subscription_u_clock_t { - __wasi_userdata_t identifier; - __wasi_clockid_t clock_id; - uint8_t __paddings1[4]; - __wasi_timestamp_t timeout; - __wasi_timestamp_t precision; - __wasi_subclockflags_t flags; - uint8_t __paddings2[6]; - } clock; - struct __wasi_subscription_u_fd_readwrite_t { - __wasi_fd_t fd; - } fd_readwrite; - } u; -} __wasi_subscription_t __attribute__((aligned(8))); -_Static_assert( - offsetof(__wasi_subscription_t, userdata) == 0, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, type) == 8, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, u.clock.identifier) == 16, - "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, u.clock.clock_id) == 24, - "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, u.clock.timeout) == 32, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, u.clock.precision) == 40, - "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, u.clock.flags) == 48, "non-wasi data layout"); -_Static_assert( - offsetof(__wasi_subscription_t, u.fd_readwrite.fd) == 16, - "non-wasi data layout"); -_Static_assert(sizeof(__wasi_subscription_t) == 56, "non-wasi data layout"); -_Static_assert(_Alignof(__wasi_subscription_t) == 8, "non-wasi data layout"); - #if defined(WASMTIME_SSP_WASI_API) -#define WASMTIME_SSP_SYSCALL_NAME(name) \ - asm("__wasi_" #name) +#define WASMTIME_SSP_SYSCALL_NAME(name) asm("__wasi_" #name) #else #define WASMTIME_SSP_SYSCALL_NAME(name) #endif -__wasi_errno_t wasmtime_ssp_args_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *arg_environ, -#endif - char **argv, - char *argv_buf -) WASMTIME_SSP_SYSCALL_NAME(args_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_args_sizes_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *arg_environ, -#endif - size_t *argc, - size_t *argv_buf_size -) WASMTIME_SSP_SYSCALL_NAME(args_sizes_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_clock_res_get( - __wasi_clockid_t clock_id, - __wasi_timestamp_t *resolution -) WASMTIME_SSP_SYSCALL_NAME(clock_res_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_clock_time_get( - __wasi_clockid_t clock_id, - __wasi_timestamp_t precision, - __wasi_timestamp_t *time -) WASMTIME_SSP_SYSCALL_NAME(clock_time_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_environ_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *arg_environ, -#endif - char **environ, - char *environ_buf -) WASMTIME_SSP_SYSCALL_NAME(environ_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_environ_sizes_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *arg_environ, -#endif - size_t *environ_count, - size_t *environ_buf_size -) WASMTIME_SSP_SYSCALL_NAME(environ_sizes_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_prestat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_prestats *prestats, -#endif - __wasi_fd_t fd, - __wasi_prestat_t *buf -) WASMTIME_SSP_SYSCALL_NAME(fd_prestat_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_prestat_dir_name( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_prestats *prestats, -#endif - __wasi_fd_t fd, - char *path, - size_t path_len -) WASMTIME_SSP_SYSCALL_NAME(fd_prestat_dir_name) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_close( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - __wasi_fd_t fd -) WASMTIME_SSP_SYSCALL_NAME(fd_close) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_datasync( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd -) WASMTIME_SSP_SYSCALL_NAME(fd_datasync) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_pread( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_iovec_t *iovs, - size_t iovs_len, - __wasi_filesize_t offset, - size_t *nread -) WASMTIME_SSP_SYSCALL_NAME(fd_pread) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_pwrite( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_ciovec_t *iovs, - size_t iovs_len, - __wasi_filesize_t offset, - size_t *nwritten -) WASMTIME_SSP_SYSCALL_NAME(fd_pwrite) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_read( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_iovec_t *iovs, - size_t iovs_len, - size_t *nread -) WASMTIME_SSP_SYSCALL_NAME(fd_read) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_renumber( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - __wasi_fd_t from, - __wasi_fd_t to -) WASMTIME_SSP_SYSCALL_NAME(fd_renumber) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_seek( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filedelta_t offset, - __wasi_whence_t whence, - __wasi_filesize_t *newoffset -) WASMTIME_SSP_SYSCALL_NAME(fd_seek) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_tell( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t *newoffset -) WASMTIME_SSP_SYSCALL_NAME(fd_tell) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_fdstat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_fdstat_t *buf -) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_fdstat_set_flags( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_fdflags_t flags -) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_flags) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_fdstat_set_rights( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_rights_t fs_rights_base, - __wasi_rights_t fs_rights_inheriting -) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_rights) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_sync( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd -) WASMTIME_SSP_SYSCALL_NAME(fd_sync) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_write( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_ciovec_t *iovs, - size_t iovs_len, - size_t *nwritten -) WASMTIME_SSP_SYSCALL_NAME(fd_write) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_advise( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t offset, - __wasi_filesize_t len, - __wasi_advice_t advice -) WASMTIME_SSP_SYSCALL_NAME(fd_advise) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_allocate( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t offset, - __wasi_filesize_t len -) WASMTIME_SSP_SYSCALL_NAME(fd_allocate) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_create_directory( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t path_len -) WASMTIME_SSP_SYSCALL_NAME(path_create_directory) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_link( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - __wasi_fd_t old_fd, - __wasi_lookupflags_t old_flags, - const char *old_path, - size_t old_path_len, - __wasi_fd_t new_fd, - const char *new_path, - size_t new_path_len -) WASMTIME_SSP_SYSCALL_NAME(path_link) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_open( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t dirfd, - __wasi_lookupflags_t dirflags, - const char *path, - size_t path_len, - __wasi_oflags_t oflags, - __wasi_rights_t fs_rights_base, - __wasi_rights_t fs_rights_inheriting, - __wasi_fdflags_t fs_flags, - __wasi_fd_t *fd -) WASMTIME_SSP_SYSCALL_NAME(path_open) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_readdir( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - void *buf, - size_t buf_len, - __wasi_dircookie_t cookie, - size_t *bufused -) WASMTIME_SSP_SYSCALL_NAME(fd_readdir) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_readlink( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t path_len, - char *buf, - size_t buf_len, - size_t *bufused -) WASMTIME_SSP_SYSCALL_NAME(path_readlink) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_rename( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t old_fd, - const char *old_path, - size_t old_path_len, - __wasi_fd_t new_fd, - const char *new_path, - size_t new_path_len -) WASMTIME_SSP_SYSCALL_NAME(path_rename) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_filestat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filestat_t *buf -) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_filestat_set_times( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_timestamp_t st_atim, - __wasi_timestamp_t st_mtim, - __wasi_fstflags_t fstflags -) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_times) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_fd_filestat_set_size( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t st_size -) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_size) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_filestat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_lookupflags_t flags, - const char *path, - size_t path_len, - __wasi_filestat_t *buf -) WASMTIME_SSP_SYSCALL_NAME(path_filestat_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_filestat_set_times( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_lookupflags_t flags, - const char *path, - size_t path_len, - __wasi_timestamp_t st_atim, - __wasi_timestamp_t st_mtim, - __wasi_fstflags_t fstflags -) WASMTIME_SSP_SYSCALL_NAME(path_filestat_set_times) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_symlink( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - const char *old_path, - size_t old_path_len, - __wasi_fd_t fd, - const char *new_path, - size_t new_path_len -) WASMTIME_SSP_SYSCALL_NAME(path_symlink) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_unlink_file( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t path_len -) WASMTIME_SSP_SYSCALL_NAME(path_unlink_file) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_path_remove_directory( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t path_len -) WASMTIME_SSP_SYSCALL_NAME(path_remove_directory) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_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 -) WASMTIME_SSP_SYSCALL_NAME(poll_oneoff) __attribute__((__warn_unused_result__)); - -_Noreturn void wasmtime_ssp_proc_exit( - __wasi_exitcode_t rval -) WASMTIME_SSP_SYSCALL_NAME(proc_exit); - -__wasi_errno_t wasmtime_ssp_proc_raise( - __wasi_signal_t sig -) WASMTIME_SSP_SYSCALL_NAME(proc_raise) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_random_get( - void *buf, - size_t buf_len -) WASMTIME_SSP_SYSCALL_NAME(random_get) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_sock_recv( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t sock, - const __wasi_iovec_t *ri_data, - size_t ri_data_len, - __wasi_riflags_t ri_flags, - size_t *ro_datalen, - __wasi_roflags_t *ro_flags -) WASMTIME_SSP_SYSCALL_NAME(sock_recv) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_sock_send( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t sock, - const __wasi_ciovec_t *si_data, - size_t si_data_len, - __wasi_siflags_t si_flags, - size_t *so_datalen -) WASMTIME_SSP_SYSCALL_NAME(sock_send) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_sock_shutdown( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t sock, - __wasi_sdflags_t how -) WASMTIME_SSP_SYSCALL_NAME(sock_shutdown) __attribute__((__warn_unused_result__)); - -__wasi_errno_t wasmtime_ssp_sched_yield(void) - WASMTIME_SSP_SYSCALL_NAME(sched_yield) __attribute__((__warn_unused_result__)); +__wasi_errno_t +wasmtime_ssp_args_get(struct argv_environ_values *arg_environ, char **argv, + char *argv_buf) + WASMTIME_SSP_SYSCALL_NAME(args_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_args_sizes_get(struct argv_environ_values *arg_environ, + size_t *argc, size_t *argv_buf_size) + WASMTIME_SSP_SYSCALL_NAME(args_sizes_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_environ_get(struct argv_environ_values *arg_environ, + char **environs, char *environ_buf) + WASMTIME_SSP_SYSCALL_NAME(environ_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_environ_sizes_get(struct argv_environ_values *arg_environ, + size_t *environ_count, size_t *environ_buf_size) + WASMTIME_SSP_SYSCALL_NAME(environ_sizes_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_prestat_get(struct fd_prestats *prestats, __wasi_fd_t fd, + __wasi_prestat_t *buf) + WASMTIME_SSP_SYSCALL_NAME(fd_prestat_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_prestat_dir_name(struct fd_prestats *prestats, __wasi_fd_t fd, + char *path, size_t path_len) + WASMTIME_SSP_SYSCALL_NAME(fd_prestat_dir_name) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_close(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, __wasi_fd_t fd) + WASMTIME_SSP_SYSCALL_NAME(fd_close) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_datasync(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd) + WASMTIME_SSP_SYSCALL_NAME(fd_datasync) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_pread(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_iovec_t *iovs, + size_t iovs_len, __wasi_filesize_t offset, size_t *nread) + WASMTIME_SSP_SYSCALL_NAME(fd_pread) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_pwrite(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_ciovec_t *iovs, + size_t iovs_len, __wasi_filesize_t offset, + size_t *nwritten) + WASMTIME_SSP_SYSCALL_NAME(fd_pwrite) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_read(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_iovec_t *iovs, + size_t iovs_len, size_t *nread) + WASMTIME_SSP_SYSCALL_NAME(fd_read) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_renumber(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, __wasi_fd_t from, + __wasi_fd_t to) + WASMTIME_SSP_SYSCALL_NAME(fd_renumber) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_seek(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *newoffset) + WASMTIME_SSP_SYSCALL_NAME(fd_seek) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_tell(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filesize_t *newoffset) + WASMTIME_SSP_SYSCALL_NAME(fd_tell) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_fdstat_get(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_fdstat_t *buf) + WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_fdstat_set_flags(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_fdflags_t flags) + WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_flags) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_fdstat_set_rights(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting) + WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_rights) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_sync(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd) + WASMTIME_SSP_SYSCALL_NAME(fd_sync) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_write(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_ciovec_t *iovs, + size_t iovs_len, size_t *nwritten) + WASMTIME_SSP_SYSCALL_NAME(fd_write) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_advise(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filesize_t offset, + __wasi_filesize_t len, __wasi_advice_t advice) + WASMTIME_SSP_SYSCALL_NAME(fd_advise) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_allocate(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filesize_t offset, + __wasi_filesize_t len) + WASMTIME_SSP_SYSCALL_NAME(fd_allocate) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_create_directory(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + const char *path, size_t path_len) + WASMTIME_SSP_SYSCALL_NAME(path_create_directory) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_link(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, __wasi_fd_t old_fd, + __wasi_lookupflags_t old_flags, const char *old_path, + size_t old_path_len, __wasi_fd_t new_fd, + const char *new_path, size_t new_path_len) + WASMTIME_SSP_SYSCALL_NAME(path_link) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_open(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t dirfd, __wasi_lookupflags_t dirflags, + const char *path, size_t path_len, + __wasi_oflags_t oflags, __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting, + __wasi_fdflags_t fs_flags, __wasi_fd_t *fd) + WASMTIME_SSP_SYSCALL_NAME(path_open) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_readdir(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, void *buf, size_t buf_len, + __wasi_dircookie_t cookie, size_t *bufused) + WASMTIME_SSP_SYSCALL_NAME(fd_readdir) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_readlink(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const char *path, size_t path_len, + char *buf, size_t buf_len, size_t *bufused) + WASMTIME_SSP_SYSCALL_NAME(path_readlink) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_rename(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t old_fd, const char *old_path, + size_t old_path_len, __wasi_fd_t new_fd, + const char *new_path, size_t new_path_len) + WASMTIME_SSP_SYSCALL_NAME(path_rename) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_filestat_get(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filestat_t *buf) + WASMTIME_SSP_SYSCALL_NAME(fd_filestat_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_filestat_set_times(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags) + WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_times) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_fd_filestat_set_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_filesize_t st_size) + WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_size) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_filestat_get(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_lookupflags_t flags, const char *path, + size_t path_len, __wasi_filestat_t *buf) + WASMTIME_SSP_SYSCALL_NAME(path_filestat_get) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_filestat_set_times(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, size_t path_len, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags) + WASMTIME_SSP_SYSCALL_NAME(path_filestat_set_times) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_symlink(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, const char *old_path, + size_t old_path_len, __wasi_fd_t fd, + const char *new_path, size_t new_path_len) + WASMTIME_SSP_SYSCALL_NAME(path_symlink) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_unlink_file(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const char *path, size_t path_len) + WASMTIME_SSP_SYSCALL_NAME(path_unlink_file) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_path_remove_directory(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + const char *path, size_t path_len) + WASMTIME_SSP_SYSCALL_NAME(path_remove_directory) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds, + const __wasi_subscription_t *in, __wasi_event_t *out, + size_t nsubscriptions, size_t *nevents) + WASMTIME_SSP_SYSCALL_NAME(poll_oneoff) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_random_get(void *buf, size_t buf_len) + WASMTIME_SSP_SYSCALL_NAME(random_get) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_accept(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_fdflags_t flags, + __wasi_fd_t *fd_new) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_addr_local(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_addr_t *addr) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_addr_remote(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_addr_t *addr) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_open(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t poolfd, __wasi_address_family_t af, + __wasi_sock_type_t socktype, + __wasi_fd_t *sockfd) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_bind(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct addr_pool *addr_pool, __wasi_fd_t fd, + __wasi_addr_t *addr) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_addr_resolve(wasm_exec_env_t exec_env, struct fd_table *curfds, + char **ns_lookup_list, const char *host, + const char *service, __wasi_addr_info_hints_t *hints, + __wasi_addr_info_t *addr_info, + __wasi_size_t addr_info_size, + __wasi_size_t *max_info_size) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_connect(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct addr_pool *addr_pool, __wasi_fd_t fd, + __wasi_addr_t *addr) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_get_recv_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t *size) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_get_reuse_addr(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t *reuse) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_get_reuse_port(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t *reuse) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_get_send_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t *size) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_set_recv_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t size) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_set_reuse_addr(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t reuse) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_set_reuse_port(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t reuse) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_set_send_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t size) WARN_UNUSED; + +__wasi_errno_t +wasi_ssp_sock_listen(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_size_t backlog) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_recv(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, void *buf, size_t buf_len, + size_t *recv_len) + WASMTIME_SSP_SYSCALL_NAME(sock_recv) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_recv_from(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, void *buf, size_t buf_len, + __wasi_riflags_t ri_flags, __wasi_addr_t *src_addr, + size_t *recv_len) + WASMTIME_SSP_SYSCALL_NAME(sock_recv_from) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_send(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, const void *buf, size_t buf_len, + size_t *sent_len) + WASMTIME_SSP_SYSCALL_NAME(sock_send) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_send_to(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct addr_pool *addr_pool, __wasi_fd_t sock, + const void *buf, size_t buf_len, + __wasi_siflags_t si_flags, + const __wasi_addr_t *dest_addr, size_t *sent_len) + WASMTIME_SSP_SYSCALL_NAME(sock_send_to) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_shutdown(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock) + WASMTIME_SSP_SYSCALL_NAME(sock_shutdown) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_recv_timeout(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint64_t timeout_us) + WASMTIME_SSP_SYSCALL_NAME(sock_set_recv_timeout) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_recv_timeout(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint64_t *timeout_us) + WASMTIME_SSP_SYSCALL_NAME(sock_get_recv_timeout) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_send_timeout(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint64_t timeout_us) + WASMTIME_SSP_SYSCALL_NAME(sock_set_send_timeout) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_send_timeout(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint64_t *timeout_us) + WASMTIME_SSP_SYSCALL_NAME(sock_get_send_timeout) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_send_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + size_t bufsiz) + WASMTIME_SSP_SYSCALL_NAME(sock_set_send_buf_size) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_send_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + size_t *bufsiz) + WASMTIME_SSP_SYSCALL_NAME(sock_get_send_buf_size) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_recv_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + size_t bufsiz) + WASMTIME_SSP_SYSCALL_NAME(sock_set_recv_buf_size) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_recv_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + size_t *bufsiz) + WASMTIME_SSP_SYSCALL_NAME(sock_get_recv_buf_size) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_keep_alive(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_keep_alive) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_keep_alive(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_keep_alive) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_reuse_addr(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_reuse_addr) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_reuse_addr(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_reuse_addr) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_reuse_port(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_reuse_port) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_reuse_port(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_reuse_port) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_linger(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, bool is_enabled, int linger_s) + WASMTIME_SSP_SYSCALL_NAME(sock_set_linger) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_linger(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, bool *is_enabled, int *linger_s) + WASMTIME_SSP_SYSCALL_NAME(sock_get_linger) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_broadcast(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_broadcast) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_broadcast(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_broadcast) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_tcp_no_delay(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_tcp_no_delay) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_tcp_no_delay(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_tcp_no_delay) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_tcp_quick_ack(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_tcp_quick_ack) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_tcp_quick_ack(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_tcp_quick_ack) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_tcp_keep_idle(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint32_t time_s) + WASMTIME_SSP_SYSCALL_NAME(sock_set_tcp_keep_idle) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_tcp_keep_idle(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint32_t *time_s) + WASMTIME_SSP_SYSCALL_NAME(sock_get_tcp_keep_idle) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_tcp_keep_intvl(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint32_t time_s) + WASMTIME_SSP_SYSCALL_NAME(sock_set_tcp_keep_intvl) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_tcp_keep_intvl(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + uint32_t *time_s) + WASMTIME_SSP_SYSCALL_NAME(sock_get_tcp_keep_intvl) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_tcp_fastopen_connect(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_tcp_fastopen_connect) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_tcp_fastopen_connect(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_tcp_fastopen_connect) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_multicast_loop(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, bool ipv6, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_ip_multicast_loop) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_ip_multicast_loop(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, bool ipv6, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_ip_multicast_loop) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_add_membership(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) + WASMTIME_SSP_SYSCALL_NAME(sock_set_ip_add_membership) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_drop_membership(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) + WASMTIME_SSP_SYSCALL_NAME(sock_set_ip_drop_membership) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_ttl(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, uint8_t ttl_s) + WASMTIME_SSP_SYSCALL_NAME(sock_set_ip_ttl) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_ip_ttl(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, uint8_t *ttl_s) + WASMTIME_SSP_SYSCALL_NAME(sock_get_ip_ttl) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_multicast_ttl(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, uint8_t ttl_s) + WASMTIME_SSP_SYSCALL_NAME(sock_set_ip_multicast_ttl) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_ip_multicast_ttl(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, uint8_t *ttl_s) + WASMTIME_SSP_SYSCALL_NAME(sock_get_ip_multicast_ttl) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_set_ipv6_only(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_set_ipv6_only) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sock_get_ipv6_only(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t sock, + bool *is_enabled) + WASMTIME_SSP_SYSCALL_NAME(sock_get_ipv6_only) WARN_UNUSED; + +__wasi_errno_t +wasmtime_ssp_sched_yield(void) + WASMTIME_SSP_SYSCALL_NAME(sched_yield) WARN_UNUSED; #ifdef __cplusplus } @@ -873,4 +603,4 @@ __wasi_errno_t wasmtime_ssp_sched_yield(void) #undef WASMTIME_SSP_SYSCALL_NAME -#endif +#endif /* end of WASMTIME_SSP_H */ diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md index fe73057f55..b4d55d8033 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md @@ -1,4 +1,4 @@ -This directory consists of selected files copied from the [libemulator] +This directory consists of selected files copied from the [src/libemulator] directory in the [cloudabi-utils] repository, with minor modifications, along with the accompanying LICENSE file from that repository. @@ -10,5 +10,5 @@ which is dated Tue Jun 25 17:22:07 2019 -0700 . -[libemulator]: https://github.com/NuxiNL/cloudabi-utils/tree/master/src/libemulator +[libemulator]: https://github.com/NuxiNL/cloudabi-utils/tree/223dadc53248552db43e012c67ed08cf416a2b12/src/libemulator [cloudabi-utils]: https://github.com/NuxiNL/cloudabi-utils diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.c b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.c new file mode 100644 index 0000000000..cf3acf3556 --- /dev/null +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "ssp_config.h" +#include "blocking_op.h" +#include "libc_errno.h" + +__wasi_errno_t +blocking_op_close(wasm_exec_env_t exec_env, os_file_handle handle, + bool is_stdio) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + __wasi_errno_t error = os_close(handle, is_stdio); + wasm_runtime_end_blocking_op(exec_env); + return error; +} + +__wasi_errno_t +blocking_op_readv(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_iovec_t *iov, int iovcnt, size_t *nread) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + __wasi_errno_t error = os_readv(handle, iov, iovcnt, nread); + wasm_runtime_end_blocking_op(exec_env); + return error; +} + +__wasi_errno_t +blocking_op_preadv(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + __wasi_errno_t ret = os_preadv(handle, iov, iovcnt, offset, nread); + wasm_runtime_end_blocking_op(exec_env); + return ret; +} + +__wasi_errno_t +blocking_op_writev(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + __wasi_errno_t error = os_writev(handle, iov, iovcnt, nwritten); + wasm_runtime_end_blocking_op(exec_env); + return error; +} + +__wasi_errno_t +blocking_op_pwritev(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + __wasi_errno_t error = os_pwritev(handle, iov, iovcnt, offset, nwritten); + wasm_runtime_end_blocking_op(exec_env); + return error; +} + +int +blocking_op_socket_accept(wasm_exec_env_t exec_env, bh_socket_t server_sock, + bh_socket_t *sockp, void *addr, + unsigned int *addrlenp) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + errno = EINTR; + return -1; + } + int ret = os_socket_accept(server_sock, sockp, addr, addrlenp); + wasm_runtime_end_blocking_op(exec_env); + return ret; +} + +int +blocking_op_socket_connect(wasm_exec_env_t exec_env, bh_socket_t sock, + const char *addr, int port) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + errno = EINTR; + return -1; + } + int ret = os_socket_connect(sock, addr, port); + wasm_runtime_end_blocking_op(exec_env); + return ret; +} + +int +blocking_op_socket_recv_from(wasm_exec_env_t exec_env, bh_socket_t sock, + void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + errno = EINTR; + return -1; + } + int ret = os_socket_recv_from(sock, buf, len, flags, src_addr); + wasm_runtime_end_blocking_op(exec_env); + return ret; +} + +int +blocking_op_socket_send_to(wasm_exec_env_t exec_env, bh_socket_t sock, + const void *buf, unsigned int len, int flags, + const bh_sockaddr_t *dest_addr) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + errno = EINTR; + return -1; + } + int ret = os_socket_send_to(sock, buf, len, flags, dest_addr); + wasm_runtime_end_blocking_op(exec_env); + return ret; +} + +int +blocking_op_socket_addr_resolve(wasm_exec_env_t exec_env, const char *host, + const char *service, uint8_t *hint_is_tcp, + uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, + size_t addr_info_size, size_t *max_info_size) +{ + /* + * Note: Unlike others, os_socket_addr_resolve() is not a simple system + * call. It's likely backed by a complex libc function, getaddrinfo(). + * Depending on the implementation of getaddrinfo() and underlying + * DNS resolver, it might or might not be possible to make it return + * with os_wakeup_blocking_op(). + * + * Unfortunately, many of ISC/bind based resolvers just keep going on + * interrupted system calls. It includes macOS and glibc. + * + * On the other hand, NuttX as of writing this returns EAI_AGAIN + * on EINTR. + */ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + errno = EINTR; + return -1; + } + int ret = os_socket_addr_resolve(host, service, hint_is_tcp, hint_is_ipv4, + addr_info, addr_info_size, max_info_size); + wasm_runtime_end_blocking_op(exec_env); + return ret; +} + +__wasi_errno_t +blocking_op_openat(wasm_exec_env_t exec_env, os_file_handle handle, + const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fd_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode access_mode, os_file_handle *out) +{ + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + __wasi_errno_t error = os_openat(handle, path, oflags, fd_flags, + lookup_flags, access_mode, out); + wasm_runtime_end_blocking_op(exec_env); + return error; +} + +#ifndef BH_PLATFORM_WINDOWS +/* REVISIT: apply the os_file_handle style abstraction for pollfd? */ +__wasi_errno_t +blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds, + os_nfds_t nfds, int timeout_ms, int *retp) +{ + int ret; + if (!wasm_runtime_begin_blocking_op(exec_env)) { + return __WASI_EINTR; + } + ret = os_poll(pfds, nfds, timeout_ms); + wasm_runtime_end_blocking_op(exec_env); + if (ret == -1) { + return convert_errno(errno); + } + *retp = ret; + return 0; +} +#endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h new file mode 100644 index 0000000000..310d6bd2c8 --- /dev/null +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/blocking_op.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BLOCKING_OP_H_ +#define _BLOCKING_OP_H_ + +#include "bh_platform.h" +#include "wasm_export.h" + +__wasi_errno_t +blocking_op_close(wasm_exec_env_t exec_env, os_file_handle handle, + bool is_stdio); +__wasi_errno_t +blocking_op_readv(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_iovec_t *iov, int iovcnt, size_t *nread); +__wasi_errno_t +blocking_op_preadv(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread); +__wasi_errno_t +blocking_op_writev(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten); +__wasi_errno_t +blocking_op_pwritev(wasm_exec_env_t exec_env, os_file_handle handle, + const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten); +int +blocking_op_socket_accept(wasm_exec_env_t exec_env, bh_socket_t server_sock, + bh_socket_t *sockp, void *addr, + unsigned int *addrlenp); +int +blocking_op_socket_connect(wasm_exec_env_t exec_env, bh_socket_t sock, + const char *addr, int port); +int +blocking_op_socket_recv_from(wasm_exec_env_t exec_env, bh_socket_t sock, + void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr); +int +blocking_op_socket_send_to(wasm_exec_env_t exec_env, bh_socket_t sock, + const void *buf, unsigned int len, int flags, + const bh_sockaddr_t *dest_addr); +int +blocking_op_socket_addr_resolve(wasm_exec_env_t exec_env, const char *host, + const char *service, uint8_t *hint_is_tcp, + uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, + size_t addr_info_size, size_t *max_info_size); + +__wasi_errno_t +blocking_op_openat(wasm_exec_env_t exec_env, os_file_handle handle, + const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fd_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode access_mode, os_file_handle *out); + +#ifndef BH_PLATFORM_WINDOWS +__wasi_errno_t +blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds, + os_nfds_t nfds, int timeout, int *retp); +#endif + +#endif /* end of _BLOCKING_OP_H_ */ diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h index 97d11bef19..b273ba3a9f 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -14,12 +16,6 @@ #include "ssp_config.h" -#include -#include -#include -#include -#include - #ifndef __has_extension #define __has_extension(x) 0 #endif @@ -30,7 +26,7 @@ #define LOCK_ANNOTATE(x) #endif -// Lock annotation macros. +/* Lock annotation macros. */ #define LOCKABLE LOCK_ANNOTATE(lockable) @@ -38,178 +34,245 @@ #define LOCKS_SHARED(...) LOCK_ANNOTATE(shared_lock_function(__VA_ARGS__)) #define TRYLOCKS_EXCLUSIVE(...) \ - LOCK_ANNOTATE(exclusive_trylock_function(__VA_ARGS__)) + LOCK_ANNOTATE(exclusive_trylock_function(__VA_ARGS__)) #define TRYLOCKS_SHARED(...) LOCK_ANNOTATE(shared_trylock_function(__VA_ARGS__)) #define UNLOCKS(...) LOCK_ANNOTATE(unlock_function(__VA_ARGS__)) #define REQUIRES_EXCLUSIVE(...) \ - LOCK_ANNOTATE(exclusive_locks_required(__VA_ARGS__)) + LOCK_ANNOTATE(exclusive_locks_required(__VA_ARGS__)) #define REQUIRES_SHARED(...) LOCK_ANNOTATE(shared_locks_required(__VA_ARGS__)) #define REQUIRES_UNLOCKED(...) LOCK_ANNOTATE(locks_excluded(__VA_ARGS__)) #define NO_LOCK_ANALYSIS LOCK_ANNOTATE(no_thread_safety_analysis) -// Mutex that uses the lock annotations. +/* Mutex that uses the lock annotations. */ struct LOCKABLE mutex { - pthread_mutex_t object; + korp_mutex object; }; +/* clang-format off */ #define MUTEX_INITIALIZER \ - { PTHREAD_MUTEX_INITIALIZER } + { PTHREAD_MUTEX_INITIALIZER } +/* clang-format on */ -static inline void mutex_init(struct mutex *lock) REQUIRES_UNLOCKED(*lock) { - pthread_mutex_init(&lock->object, NULL); +static inline bool +mutex_init(struct mutex *lock) REQUIRES_UNLOCKED(*lock) +{ + return os_mutex_init(&lock->object) == BHT_OK ? true : false; } -static inline void mutex_destroy(struct mutex *lock) REQUIRES_UNLOCKED(*lock) { - pthread_mutex_destroy(&lock->object); +static inline void +mutex_destroy(struct mutex *lock) REQUIRES_UNLOCKED(*lock) +{ + os_mutex_destroy(&lock->object); } -static inline void mutex_lock(struct mutex *lock) - LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { - pthread_mutex_lock(&lock->object); +static inline void +mutex_lock(struct mutex *lock) LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + os_mutex_lock(&lock->object); } -static inline void mutex_unlock(struct mutex *lock) - UNLOCKS(*lock) NO_LOCK_ANALYSIS { - pthread_mutex_unlock(&lock->object); +static inline void +mutex_unlock(struct mutex *lock) UNLOCKS(*lock) NO_LOCK_ANALYSIS +{ + os_mutex_unlock(&lock->object); } -// Read-write lock that uses the lock annotations. +/* Read-write lock that uses the lock annotations. */ struct LOCKABLE rwlock { - pthread_rwlock_t object; + korp_rwlock object; }; -static inline void rwlock_init(struct rwlock *lock) REQUIRES_UNLOCKED(*lock) { - pthread_rwlock_init(&lock->object, NULL); +static inline bool +rwlock_initialize(struct rwlock *lock) REQUIRES_UNLOCKED(*lock) +{ + return os_rwlock_init(&lock->object) == 0 ? true : false; +} + +static inline void +rwlock_rdlock(struct rwlock *lock) LOCKS_SHARED(*lock) NO_LOCK_ANALYSIS +{ + os_rwlock_rdlock(&lock->object); } -static inline void rwlock_rdlock(struct rwlock *lock) - LOCKS_SHARED(*lock) NO_LOCK_ANALYSIS { - pthread_rwlock_rdlock(&lock->object); +static inline void +rwlock_wrlock(struct rwlock *lock) LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + os_rwlock_wrlock(&lock->object); } -static inline void rwlock_wrlock(struct rwlock *lock) - LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { - pthread_rwlock_wrlock(&lock->object); +static inline void +rwlock_unlock(struct rwlock *lock) UNLOCKS(*lock) NO_LOCK_ANALYSIS +{ + os_rwlock_unlock(&lock->object); } -static inline void rwlock_unlock(struct rwlock *lock) - UNLOCKS(*lock) NO_LOCK_ANALYSIS { - pthread_rwlock_unlock(&lock->object); +static inline void +rwlock_destroy(struct rwlock *lock) UNLOCKS(*lock) NO_LOCK_ANALYSIS +{ + os_rwlock_destroy(&lock->object); } -// Condition variable that uses the lock annotations. +/* Condition variable that uses the lock annotations. */ struct LOCKABLE cond { - pthread_cond_t object; -#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ - !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP - clockid_t clock; + korp_cond object; + +#if !CONFIG_HAS_CLOCK_NANOSLEEP \ + && (!CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK \ + || !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP) + clockid_t clock; #endif }; -static inline void cond_init_monotonic(struct cond *cond) { +static inline bool +cond_init_monotonic(struct cond *cond) +{ + bool ret = false; #if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK - pthread_condattr_t attr; - pthread_condattr_init(&attr); - pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); - pthread_cond_init(&cond->object, &attr); - pthread_condattr_destroy(&attr); + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr) != 0) + return false; + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) + goto fail; + + if (pthread_cond_init(&cond->object, &attr) != 0) + goto fail; + + ret = true; +fail: + pthread_condattr_destroy(&attr); #else - pthread_cond_init(&cond->object, NULL); + if (os_cond_init(&cond->object) != 0) + return false; + ret = true; #endif -#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ - !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP - cond->clock = CLOCK_MONOTONIC; + +#if !CONFIG_HAS_CLOCK_NANOSLEEP \ + && (!CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK \ + || !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP) + cond->clock = CLOCK_MONOTONIC; #endif + + return ret; } -static inline void cond_init_realtime(struct cond *cond) { - pthread_cond_init(&cond->object, NULL); -#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ - !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP - cond->clock = CLOCK_REALTIME; +static inline bool +cond_init_realtime(struct cond *cond) +{ + if (os_cond_init(&cond->object) != 0) + return false; + +#if !CONFIG_HAS_CLOCK_NANOSLEEP \ + && (!CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK \ + || !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP) + cond->clock = CLOCK_REALTIME; #endif + + return true; } -static inline void cond_destroy(struct cond *cond) { - pthread_cond_destroy(&cond->object); +static inline void +cond_destroy(struct cond *cond) +{ + os_cond_destroy(&cond->object); } -static inline void cond_signal(struct cond *cond) { - pthread_cond_signal(&cond->object); +static inline void +cond_signal(struct cond *cond) +{ + os_cond_signal(&cond->object); } -static inline bool cond_timedwait(struct cond *cond, struct mutex *lock, - uint64_t timeout, bool abstime) - REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { - struct timespec ts = { - .tv_sec = (time_t)(timeout / 1000000000), - .tv_nsec = (long)(timeout % 1000000000), - }; +#if !CONFIG_HAS_CLOCK_NANOSLEEP - if (abstime) { +static inline bool +cond_timedwait(struct cond *cond, struct mutex *lock, uint64_t timeout, + bool abstime) REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ +#if defined(BH_PLATFORM_ZEPHYR) + // TODO: Implement this for Zephyr + return false; +#else + int ret; + os_timespec ts = { + .tv_sec = (time_t)(timeout / 1000000000), + .tv_nsec = (long)(timeout % 1000000000), + }; + + if (abstime) { #if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK - // No native support for sleeping on monotonic clocks. Convert the - // timeout to a relative value and then to an absolute value for the - // realtime clock. - if (cond->clock != CLOCK_REALTIME) { - struct timespec ts_monotonic; - clock_gettime(cond->clock, &ts_monotonic); - ts.tv_sec -= ts_monotonic.tv_sec; - ts.tv_nsec -= ts_monotonic.tv_nsec; - if (ts.tv_nsec < 0) { - ts.tv_nsec += 1000000000; - --ts.tv_sec; - } - - struct timespec ts_realtime; - clock_gettime(CLOCK_REALTIME, &ts_realtime); - ts.tv_sec += ts_realtime.tv_sec; - ts.tv_nsec += ts_realtime.tv_nsec; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ++ts.tv_sec; - } + /** + * No native support for sleeping on monotonic clocks. Convert the + * timeout to a relative value and then to an absolute value for the + * realtime clock. + */ + if (cond->clock != CLOCK_REALTIME) { + os_timespec ts_monotonic; + os_timespec ts_realtime; + + clock_gettime(cond->clock, &ts_monotonic); + ts.tv_sec -= ts_monotonic.tv_sec; + ts.tv_nsec -= ts_monotonic.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1000000000; + --ts.tv_sec; + } + + clock_gettime(CLOCK_REALTIME, &ts_realtime); + ts.tv_sec += ts_realtime.tv_sec; + ts.tv_nsec += ts_realtime.tv_nsec; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } + } +#endif /* !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK */ } -#endif - } else { + else { #if CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP - // Implementation supports relative timeouts. - int ret = - pthread_cond_timedwait_relative_np(&cond->object, &lock->object, &ts); - assert((ret == 0 || ret == ETIMEDOUT) && - "pthread_cond_timedwait_relative_np() failed"); - return ret == ETIMEDOUT; + /* Implementation supports relative timeouts. */ + ret = pthread_cond_timedwait_relative_np(&cond->object, &lock->object, + &ts); + bh_assert((ret == 0 || ret == ETIMEDOUT) + && "pthread_cond_timedwait_relative_np() failed"); + return ret == ETIMEDOUT; #else - // Convert to absolute timeout. - struct timespec ts_now; + /* Convert to absolute timeout. */ + os_timespec ts_now; #if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK - clock_gettime(cond->clock, &ts_now); + clock_gettime(cond->clock, &ts_now); #else - clock_gettime(CLOCK_REALTIME, &ts_now); + clock_gettime(CLOCK_REALTIME, &ts_now); #endif - ts.tv_sec += ts_now.tv_sec; - ts.tv_nsec += ts_now.tv_nsec; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ++ts.tv_sec; + ts.tv_sec += ts_now.tv_sec; + ts.tv_nsec += ts_now.tv_nsec; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } +#endif /* CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP */ } -#endif - } - int ret = pthread_cond_timedwait(&cond->object, &lock->object, &ts); - assert((ret == 0 || ret == ETIMEDOUT) && "pthread_cond_timedwait() failed"); - return ret == ETIMEDOUT; + ret = pthread_cond_timedwait(&cond->object, &lock->object, &ts); + bh_assert((ret == 0 || ret == ETIMEDOUT) + && "pthread_cond_timedwait() failed"); + return ret == ETIMEDOUT; +#endif /* BH_PLATFORM_ZEPHYR */ } +#endif -static inline void cond_wait(struct cond *cond, struct mutex *lock) - REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS { - pthread_cond_wait(&cond->object, &lock->object); +static inline void +cond_wait(struct cond *cond, struct mutex *lock) + REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + os_cond_wait(&cond->object, &lock->object); } #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 30dddd19aa..0000000000 --- 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/CraneStation/wasmtime/blob/master/LICENSE for license information. -// -// Significant parts of this file are derived from cloudabi-utils. See -// https://github.com/CraneStation/wasmtime/blob/master/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 - -#include - -#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 cf302f8346..7b087e44ce 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 @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -10,44 +12,28 @@ // Copyright (c) 2016-2018 Nuxi, https://nuxi.nl/ #include "ssp_config.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - +#include "bh_platform.h" +#include "blocking_op.h" +#include "wasmtime_ssp.h" +#include "libc_errno.h" #include "locking.h" -#include "numeric_limits.h" #include "posix.h" #include "random.h" #include "refcount.h" #include "rights.h" #include "str.h" -#include "bh_common.h" -#include "bh_memory.h" +/* Some platforms (e.g. Windows) already define `min()` macro. + We're undefing it here to make sure the `min` call does exactly + what we want it to do. */ +#ifdef min +#undef min +#endif +static inline size_t +min(size_t a, size_t b) +{ + return a > b ? b : a; +} #if 0 /* TODO: -std=gnu99 causes compile error, comment them first */ // struct iovec must have the same layout as __wasi_iovec_t. @@ -83,1327 +69,1218 @@ static_assert(sizeof(struct iovec) == sizeof(__wasi_ciovec_t), "Size mismatch"); #endif -#if defined(WASMTIME_SSP_STATIC_CURFDS) -static __thread struct fd_table *curfds; -static __thread struct fd_prestats *prestats; -static __thread struct argv_environ_values *argv_environ; -#endif +static bool +ns_lookup_list_search(char **list, const char *host) +{ + size_t host_len = strlen(host), suffix_len; + + while (*list) { + if (*list[0] == '*') { + suffix_len = strlen(*list) - 1; + if (suffix_len <= host_len + && strncmp(host + host_len - suffix_len, *list + 1, suffix_len) + == 0) { + return true; + } + } + else { + if (strcmp(*list, host) == 0) { + return true; + } + } + list++; + } -// Converts a POSIX error code to a CloudABI error code. -static __wasi_errno_t convert_errno(int error) { - static const __wasi_errno_t errors[] = { -#define X(v) [v] = __WASI_##v - X(E2BIG), - X(EACCES), - X(EADDRINUSE), - X(EADDRNOTAVAIL), - X(EAFNOSUPPORT), - X(EAGAIN), - X(EALREADY), - X(EBADF), - X(EBADMSG), - X(EBUSY), - X(ECANCELED), - X(ECHILD), - X(ECONNABORTED), - X(ECONNREFUSED), - X(ECONNRESET), - X(EDEADLK), - X(EDESTADDRREQ), - X(EDOM), - X(EDQUOT), - X(EEXIST), - X(EFAULT), - X(EFBIG), - X(EHOSTUNREACH), - X(EIDRM), - X(EILSEQ), - X(EINPROGRESS), - X(EINTR), - X(EINVAL), - X(EIO), - X(EISCONN), - X(EISDIR), - X(ELOOP), - X(EMFILE), - X(EMLINK), - X(EMSGSIZE), - X(EMULTIHOP), - X(ENAMETOOLONG), - X(ENETDOWN), - X(ENETRESET), - X(ENETUNREACH), - X(ENFILE), - X(ENOBUFS), - X(ENODEV), - X(ENOENT), - X(ENOEXEC), - X(ENOLCK), - X(ENOLINK), - X(ENOMEM), - X(ENOMSG), - X(ENOPROTOOPT), - X(ENOSPC), - X(ENOSYS), -#ifdef ENOTCAPABLE - X(ENOTCAPABLE), -#endif - X(ENOTCONN), - X(ENOTDIR), - X(ENOTEMPTY), - X(ENOTRECOVERABLE), - X(ENOTSOCK), - X(ENOTSUP), - X(ENOTTY), - X(ENXIO), - X(EOVERFLOW), - X(EOWNERDEAD), - X(EPERM), - X(EPIPE), - X(EPROTO), - X(EPROTONOSUPPORT), - X(EPROTOTYPE), - X(ERANGE), - X(EROFS), - X(ESPIPE), - X(ESRCH), - X(ESTALE), - X(ETIMEDOUT), - X(ETXTBSY), - X(EXDEV), -#undef X -#if EOPNOTSUPP != ENOTSUP - [EOPNOTSUPP] = __WASI_ENOTSUP, -#endif -#if EWOULDBLOCK != EAGAIN - [EWOULDBLOCK] = __WASI_EAGAIN, + return false; +} + +#if !defined(BH_PLATFORM_WINDOWS) && CONFIG_HAS_CLOCK_NANOSLEEP +static bool +wasi_clockid_to_clockid(__wasi_clockid_t in, clockid_t *out) +{ + switch (in) { + case __WASI_CLOCK_MONOTONIC: + *out = CLOCK_MONOTONIC; + return true; +#if defined(CLOCK_PROCESS_CPUTIME_ID) + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + *out = CLOCK_PROCESS_CPUTIME_ID; + return true; +#endif + case __WASI_CLOCK_REALTIME: + *out = CLOCK_REALTIME; + return true; +#if defined(CLOCK_THREAD_CPUTIME_ID) + case __WASI_CLOCK_THREAD_CPUTIME_ID: + *out = CLOCK_THREAD_CPUTIME_ID; + return true; +#endif + default: + return false; + } +} #endif - }; - if (error < 0 || (size_t)error >= sizeof(errors) / sizeof(errors[0]) || - errors[error] == 0) - return __WASI_ENOSYS; - return errors[error]; + +static void +wasi_addr_to_bh_sockaddr(const __wasi_addr_t *wasi_addr, + bh_sockaddr_t *sockaddr) +{ + if (wasi_addr->kind == IPv4) { + sockaddr->addr_buffer.ipv4 = (wasi_addr->addr.ip4.addr.n0 << 24) + | (wasi_addr->addr.ip4.addr.n1 << 16) + | (wasi_addr->addr.ip4.addr.n2 << 8) + | wasi_addr->addr.ip4.addr.n3; + sockaddr->is_ipv4 = true; + sockaddr->port = wasi_addr->addr.ip4.port; + } + else { + sockaddr->addr_buffer.ipv6[0] = wasi_addr->addr.ip6.addr.n0; + sockaddr->addr_buffer.ipv6[1] = wasi_addr->addr.ip6.addr.n1; + sockaddr->addr_buffer.ipv6[2] = wasi_addr->addr.ip6.addr.n2; + sockaddr->addr_buffer.ipv6[3] = wasi_addr->addr.ip6.addr.n3; + sockaddr->addr_buffer.ipv6[4] = wasi_addr->addr.ip6.addr.h0; + sockaddr->addr_buffer.ipv6[5] = wasi_addr->addr.ip6.addr.h1; + sockaddr->addr_buffer.ipv6[6] = wasi_addr->addr.ip6.addr.h2; + sockaddr->addr_buffer.ipv6[7] = wasi_addr->addr.ip6.addr.h3; + sockaddr->is_ipv4 = false; + sockaddr->port = wasi_addr->addr.ip6.port; + } } -// Converts a POSIX timespec to a CloudABI timestamp. -static __wasi_timestamp_t convert_timespec( - const struct timespec *ts -) { - if (ts->tv_sec < 0) - return 0; - if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) - return UINT64_MAX; - return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + (__wasi_timestamp_t)ts->tv_nsec; -} - -// Converts a CloudABI clock identifier to a POSIX clock identifier. -static bool convert_clockid( - __wasi_clockid_t in, - clockid_t *out -) { - switch (in) { - case __WASI_CLOCK_MONOTONIC: - *out = CLOCK_MONOTONIC; - return true; - case __WASI_CLOCK_PROCESS_CPUTIME_ID: - *out = CLOCK_PROCESS_CPUTIME_ID; - return true; - case __WASI_CLOCK_REALTIME: - *out = CLOCK_REALTIME; - return true; - case __WASI_CLOCK_THREAD_CPUTIME_ID: - *out = CLOCK_THREAD_CPUTIME_ID; - return true; - default: - return false; - } -} - -__wasi_errno_t wasmtime_ssp_clock_res_get( - __wasi_clockid_t clock_id, - __wasi_timestamp_t *resolution -) { - clockid_t nclock_id; - if (!convert_clockid(clock_id, &nclock_id)) - return __WASI_EINVAL; - struct timespec ts; - if (clock_getres(nclock_id, &ts) < 0) - return convert_errno(errno); - *resolution = convert_timespec(&ts); - return 0; -} - -__wasi_errno_t wasmtime_ssp_clock_time_get( - __wasi_clockid_t clock_id, - __wasi_timestamp_t precision, - __wasi_timestamp_t *time -) { - clockid_t nclock_id; - if (!convert_clockid(clock_id, &nclock_id)) - return __WASI_EINVAL; - struct timespec ts; - if (clock_gettime(nclock_id, &ts) < 0) - return convert_errno(errno); - *time = convert_timespec(&ts); - return 0; +// Converts an IPv6 binary address object to WASI address object. +static void +bh_sockaddr_to_wasi_addr(const bh_sockaddr_t *sockaddr, + __wasi_addr_t *wasi_addr) +{ + if (sockaddr->is_ipv4) { + wasi_addr->kind = IPv4; + wasi_addr->addr.ip4.port = sockaddr->port; + wasi_addr->addr.ip4.addr.n0 = + (sockaddr->addr_buffer.ipv4 & 0xFF000000) >> 24; + wasi_addr->addr.ip4.addr.n1 = + (sockaddr->addr_buffer.ipv4 & 0x00FF0000) >> 16; + wasi_addr->addr.ip4.addr.n2 = + (sockaddr->addr_buffer.ipv4 & 0x0000FF00) >> 8; + wasi_addr->addr.ip4.addr.n3 = (sockaddr->addr_buffer.ipv4 & 0x000000FF); + } + else { + wasi_addr->kind = IPv6; + wasi_addr->addr.ip6.port = sockaddr->port; + wasi_addr->addr.ip6.addr.n0 = sockaddr->addr_buffer.ipv6[0]; + wasi_addr->addr.ip6.addr.n1 = sockaddr->addr_buffer.ipv6[1]; + wasi_addr->addr.ip6.addr.n2 = sockaddr->addr_buffer.ipv6[2]; + wasi_addr->addr.ip6.addr.n3 = sockaddr->addr_buffer.ipv6[3]; + wasi_addr->addr.ip6.addr.h0 = sockaddr->addr_buffer.ipv6[4]; + wasi_addr->addr.ip6.addr.h1 = sockaddr->addr_buffer.ipv6[5]; + wasi_addr->addr.ip6.addr.h2 = sockaddr->addr_buffer.ipv6[6]; + wasi_addr->addr.ip6.addr.h3 = sockaddr->addr_buffer.ipv6[7]; + } +} + +static void +wasi_addr_ip_to_bh_ip_addr_buffer(__wasi_addr_ip_t *addr, + bh_ip_addr_buffer_t *out) +{ + if (addr->kind == IPv4) { + out->ipv4 = htonl((addr->addr.ip4.n0 << 24) | (addr->addr.ip4.n1 << 16) + | (addr->addr.ip4.n2 << 8) | addr->addr.ip4.n3); + } + else { + out->ipv6[0] = htons(addr->addr.ip6.n0); + out->ipv6[1] = htons(addr->addr.ip6.n1); + out->ipv6[2] = htons(addr->addr.ip6.n2); + out->ipv6[3] = htons(addr->addr.ip6.n3); + out->ipv6[4] = htons(addr->addr.ip6.h0); + out->ipv6[5] = htons(addr->addr.ip6.h1); + out->ipv6[6] = htons(addr->addr.ip6.h2); + out->ipv6[7] = htons(addr->addr.ip6.h3); + } } struct fd_prestat { - const char *dir; + const char *dir; }; -void fd_prestats_init( - struct fd_prestats *pt -) { - rwlock_init(&pt->lock); - pt->prestats = NULL; - pt->size = 0; - pt->used = 0; -#if defined(WASMTIME_SSP_STATIC_CURFDS) - prestats = pt; -#endif +bool +fd_prestats_init(struct fd_prestats *pt) +{ + if (!rwlock_initialize(&pt->lock)) + return false; + pt->prestats = NULL; + pt->size = 0; + pt->used = 0; + return true; } // Grows the preopened resource table to a required lower bound and a // minimum number of free preopened resource table entries. -static bool fd_prestats_grow( - struct fd_prestats *pt, - size_t min, - size_t incr -) REQUIRES_EXCLUSIVE(pt->lock) { - if (pt->size <= min || pt->size < (pt->used + incr) * 2) { - // Keep on doubling the table size until we've met our constraints. - size_t size = pt->size == 0 ? 1 : pt->size; - while (size <= min || size < (pt->used + incr) * 2) - size *= 2; - - // Grow the file descriptor table's allocation. - struct fd_prestat *prestats = bh_malloc((uint32)(sizeof(*prestats) * size)); - if (prestats == NULL) - return false; - - if (pt->prestats && pt->size > 0) { - bh_memcpy_s(prestats, (uint32)(sizeof(*prestats) * size), - pt->prestats, (uint32)(sizeof(*prestats) * pt->size)); - } - - if (pt->prestats) - bh_free(pt->prestats); - - // Mark all new file descriptors as unused. - for (size_t i = pt->size; i < size; ++i) - prestats[i].dir = NULL; - pt->prestats = prestats; - pt->size = size; - } - return true; +static __wasi_errno_t +fd_prestats_grow(struct fd_prestats *pt, size_t min, size_t incr) + REQUIRES_EXCLUSIVE(pt->lock) +{ + if (pt->size <= min || pt->size < (pt->used + incr) * 2) { + // Keep on doubling the table size until we've met our constraints. + size_t size = pt->size == 0 ? 1 : pt->size; + while (size <= min || size < (pt->used + incr) * 2) + size *= 2; + + // Grow the file descriptor table's allocation. + struct fd_prestat *prestats = + wasm_runtime_malloc((uint32)(sizeof(*prestats) * size)); + if (prestats == NULL) + return __WASI_ENOMEM; + + if (pt->prestats && pt->size > 0) { + bh_memcpy_s(prestats, (uint32)(sizeof(*prestats) * size), + pt->prestats, (uint32)(sizeof(*prestats) * pt->size)); + } + + if (pt->prestats) + wasm_runtime_free(pt->prestats); + + // Mark all new file descriptors as unused. + for (size_t i = pt->size; i < size; ++i) + prestats[i].dir = NULL; + pt->prestats = prestats; + pt->size = size; + } + return __WASI_ESUCCESS; +} + +static __wasi_errno_t +fd_prestats_insert_locked(struct fd_prestats *pt, const char *dir, + __wasi_fd_t fd) +{ + // Grow the preopened resource table if needed. + __wasi_errno_t error = fd_prestats_grow(pt, fd, 1); + + if (error != __WASI_ESUCCESS) { + return error; + } + + pt->prestats[fd].dir = bh_strdup(dir); + + if (pt->prestats[fd].dir == NULL) + return __WASI_ENOMEM; + + return __WASI_ESUCCESS; } // Inserts a preopened resource record into the preopened resource table. -bool fd_prestats_insert( - struct fd_prestats *pt, - const char *dir, - __wasi_fd_t fd -) { - // Grow the preopened resource table if needed. - rwlock_wrlock(&pt->lock); - if (!fd_prestats_grow(pt, fd, 1)) { - rwlock_unlock(&pt->lock); - return false; - } +bool +fd_prestats_insert(struct fd_prestats *pt, const char *dir, __wasi_fd_t fd) +{ + rwlock_wrlock(&pt->lock); - pt->prestats[fd].dir = bh_strdup(dir); - rwlock_unlock(&pt->lock); + __wasi_errno_t error = fd_prestats_insert_locked(pt, dir, fd); - if (pt->prestats[fd].dir == NULL) - return false; + rwlock_unlock(&pt->lock); - return true; + return error == __WASI_ESUCCESS; } // Looks up a preopened resource table entry by number. -static __wasi_errno_t fd_prestats_get_entry( - struct fd_prestats *pt, - __wasi_fd_t fd, - struct fd_prestat **ret -) REQUIRES_SHARED(pt->lock) { - // Test for file descriptor existence. - if (fd >= pt->size) - return __WASI_EBADF; - struct fd_prestat *prestat = &pt->prestats[fd]; - if (prestat->dir == NULL) - return __WASI_EBADF; - - *ret = prestat; - return 0; +static __wasi_errno_t +fd_prestats_get_entry(struct fd_prestats *pt, __wasi_fd_t fd, + struct fd_prestat **ret) REQUIRES_SHARED(pt->lock) +{ + // Test for file descriptor existence. + if ((size_t)fd >= pt->size) + return __WASI_EBADF; + struct fd_prestat *prestat = &pt->prestats[fd]; + if (prestat->dir == NULL) + return __WASI_EBADF; + + *ret = prestat; + return 0; +} + +// Remove a preopened resource record from the preopened resource table by +// number +static __wasi_errno_t +fd_prestats_remove_entry(struct fd_prestats *pt, __wasi_fd_t fd) +{ + // Test for file descriptor existence. + if ((size_t)fd >= pt->size) + return __WASI_EBADF; + struct fd_prestat *prestat = &pt->prestats[fd]; + + if (prestat->dir != NULL) { + wasm_runtime_free((void *)prestat->dir); + prestat->dir = NULL; + } + + return __WASI_ESUCCESS; } struct fd_object { - struct refcount refcount; - __wasi_filetype_t type; - int number; - - union { - // Data associated with directory file descriptors. - struct { - struct mutex lock; // Lock to protect members below. - DIR *handle; // Directory handle. - __wasi_dircookie_t offset; // Offset of the directory. - } directory; - }; + struct refcount refcount; + __wasi_filetype_t type; + os_file_handle file_handle; + + // Keep track of whether this fd object refers to a stdio stream so we know + // whether to close the underlying file handle when releasing the object. + bool is_stdio; + union { + // Data associated with directory file descriptors. + struct { + struct mutex lock; // Lock to protect members below. + os_dir_stream handle; // Directory handle. + __wasi_dircookie_t offset; // Offset of the directory. + } directory; + }; }; struct fd_entry { - struct fd_object *object; - __wasi_rights_t rights_base; - __wasi_rights_t rights_inheriting; + struct fd_object *object; + __wasi_rights_t rights_base; + __wasi_rights_t rights_inheriting; }; -void fd_table_init( - struct fd_table *ft -) { - rwlock_init(&ft->lock); - ft->entries = NULL; - ft->size = 0; - ft->used = 0; -#if defined(WASMTIME_SSP_STATIC_CURFDS) - curfds = ft; -#endif +bool +fd_table_init(struct fd_table *ft) +{ + if (!rwlock_initialize(&ft->lock)) + return false; + ft->entries = NULL; + ft->size = 0; + ft->used = 0; + return true; } // Looks up a file descriptor table entry by number and required rights. -static __wasi_errno_t fd_table_get_entry( - struct fd_table *ft, - __wasi_fd_t fd, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting, - struct fd_entry **ret -) REQUIRES_SHARED(ft->lock) { - // Test for file descriptor existence. - if (fd >= ft->size) - return __WASI_EBADF; - struct fd_entry *fe = &ft->entries[fd]; - if (fe->object == NULL) - return __WASI_EBADF; - - // Validate rights. - if ((~fe->rights_base & rights_base) != 0 || - (~fe->rights_inheriting & rights_inheriting) != 0) - return __WASI_ENOTCAPABLE; - *ret = fe; - return 0; +static __wasi_errno_t +fd_table_get_entry(struct fd_table *ft, __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, struct fd_entry **ret) + REQUIRES_SHARED(ft->lock) +{ + // Test for file descriptor existence. + if ((size_t)fd >= ft->size) { + return __WASI_EBADF; + } + + struct fd_entry *fe = &ft->entries[fd]; + if (fe->object == NULL) { + return __WASI_EBADF; + } + + // Validate rights. + if ((~fe->rights_base & rights_base) != 0 + || (~fe->rights_inheriting & rights_inheriting) != 0) { + return __WASI_ENOTCAPABLE; + } + *ret = fe; + return 0; } // Grows the file descriptor table to a required lower bound and a // minimum number of free file descriptor table entries. -static bool fd_table_grow( - struct fd_table *ft, - size_t min, - size_t incr -) REQUIRES_EXCLUSIVE(ft->lock) { - if (ft->size <= min || ft->size < (ft->used + incr) * 2) { - // Keep on doubling the table size until we've met our constraints. - size_t size = ft->size == 0 ? 1 : ft->size; - while (size <= min || size < (ft->used + incr) * 2) - size *= 2; - - // Grow the file descriptor table's allocation. - struct fd_entry *entries = bh_malloc((uint32)(sizeof(*entries) * size)); - if (entries == NULL) - return false; - - if (ft->entries && ft->size > 0) { - bh_memcpy_s(entries, (uint32)(sizeof(*entries) * size), - ft->entries, (uint32)(sizeof(*entries) * ft->size)); - } - - if (ft->entries) - bh_free(ft->entries); - - // Mark all new file descriptors as unused. - for (size_t i = ft->size; i < size; ++i) - entries[i].object = NULL; - ft->entries = entries; - ft->size = size; - } - return true; +static bool +fd_table_grow(struct fd_table *ft, size_t min, size_t incr) + REQUIRES_EXCLUSIVE(ft->lock) +{ + if (ft->size <= min || ft->size < (ft->used + incr) * 2) { + // Keep on doubling the table size until we've met our constraints. + size_t size = ft->size == 0 ? 1 : ft->size; + while (size <= min || size < (ft->used + incr) * 2) + size *= 2; + + // Grow the file descriptor table's allocation. + struct fd_entry *entries = + wasm_runtime_malloc((uint32)(sizeof(*entries) * size)); + if (entries == NULL) + return false; + + if (ft->entries && ft->size > 0) { + bh_memcpy_s(entries, (uint32)(sizeof(*entries) * size), ft->entries, + (uint32)(sizeof(*entries) * ft->size)); + } + + if (ft->entries) + wasm_runtime_free(ft->entries); + + // Mark all new file descriptors as unused. + for (size_t i = ft->size; i < size; ++i) + entries[i].object = NULL; + ft->entries = entries; + ft->size = size; + } + return true; } // Allocates a new file descriptor object. -static __wasi_errno_t fd_object_new( - __wasi_filetype_t type, - struct fd_object **fo -) TRYLOCKS_SHARED(0, (*fo)->refcount) { - *fo = bh_malloc(sizeof(**fo)); - if (*fo == NULL) - return __WASI_ENOMEM; - refcount_init(&(*fo)->refcount, 1); - (*fo)->type = type; - (*fo)->number = -1; - return 0; +static __wasi_errno_t +fd_object_new(__wasi_filetype_t type, bool is_stdio, struct fd_object **fo) + TRYLOCKS_SHARED(0, (*fo)->refcount) +{ + *fo = wasm_runtime_malloc(sizeof(**fo)); + if (*fo == NULL) + return __WASI_ENOMEM; + refcount_init(&(*fo)->refcount, 1); + (*fo)->type = type; + (*fo)->file_handle = os_get_invalid_handle(); + (*fo)->is_stdio = is_stdio; + return 0; } // Attaches a file descriptor to the file descriptor table. -static void fd_table_attach( - struct fd_table *ft, - __wasi_fd_t fd, - struct fd_object *fo, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting -) REQUIRES_EXCLUSIVE(ft->lock) CONSUMES(fo->refcount) { - assert(ft->size > fd && "File descriptor table too small"); - struct fd_entry *fe = &ft->entries[fd]; - assert(fe->object == NULL && "Attempted to overwrite an existing descriptor"); - fe->object = fo; - fe->rights_base = rights_base; - fe->rights_inheriting = rights_inheriting; - ++ft->used; - assert(ft->size >= ft->used * 2 && "File descriptor too full"); +static void +fd_table_attach(struct fd_table *ft, __wasi_fd_t fd, struct fd_object *fo, + __wasi_rights_t rights_base, __wasi_rights_t rights_inheriting) + REQUIRES_EXCLUSIVE(ft->lock) CONSUMES(fo->refcount) +{ + bh_assert(ft->size <= INT_MAX + && "Unsigned value is out of signed int range"); + bh_assert((int32_t)ft->size > fd && "File descriptor table too small"); + struct fd_entry *fe = &ft->entries[fd]; + bh_assert(fe->object == NULL + && "Attempted to overwrite an existing descriptor"); + fe->object = fo; + fe->rights_base = rights_base; + fe->rights_inheriting = rights_inheriting; + ++ft->used; + bh_assert(ft->size >= ft->used * 2 && "File descriptor too full"); } // Detaches a file descriptor from the file descriptor table. -static void fd_table_detach( - struct fd_table *ft, - __wasi_fd_t fd, - struct fd_object **fo -) REQUIRES_EXCLUSIVE(ft->lock) PRODUCES((*fo)->refcount) { - assert(ft->size > fd && "File descriptor table too small"); - struct fd_entry *fe = &ft->entries[fd]; - *fo = fe->object; - assert(*fo != NULL && "Attempted to detach nonexistent descriptor"); - fe->object = NULL; - assert(ft->used > 0 && "Reference count mismatch"); - --ft->used; +static void +fd_table_detach(struct fd_table *ft, __wasi_fd_t fd, struct fd_object **fo) + REQUIRES_EXCLUSIVE(ft->lock) PRODUCES((*fo)->refcount) +{ + bh_assert(ft->size <= INT_MAX + && "Unsigned value is out of signed int range"); + bh_assert((int32_t)ft->size > fd && "File descriptor table too small"); + struct fd_entry *fe = &ft->entries[fd]; + *fo = fe->object; + bh_assert(*fo != NULL && "Attempted to detach nonexistent descriptor"); + fe->object = NULL; + bh_assert(ft->used > 0 && "Reference count mismatch"); + --ft->used; } // Determines the type of a file descriptor and its maximum set of // rights that should be attached to it. -static __wasi_errno_t fd_determine_type_rights( - int fd, - __wasi_filetype_t *type, - __wasi_rights_t *rights_base, - __wasi_rights_t *rights_inheriting -) { - struct stat sb; - if (fstat(fd, &sb) < 0) - return convert_errno(errno); - if (S_ISBLK(sb.st_mode)) { - *type = __WASI_FILETYPE_BLOCK_DEVICE; - *rights_base = RIGHTS_BLOCK_DEVICE_BASE; - *rights_inheriting = RIGHTS_BLOCK_DEVICE_INHERITING; - } else if (S_ISCHR(sb.st_mode)) { - *type = __WASI_FILETYPE_CHARACTER_DEVICE; -#if CONFIG_HAS_ISATTY - if (isatty(fd)) { - *rights_base = RIGHTS_TTY_BASE; - *rights_inheriting = RIGHTS_TTY_INHERITING; - } else -#endif - { - *rights_base = RIGHTS_CHARACTER_DEVICE_BASE; - *rights_inheriting = RIGHTS_CHARACTER_DEVICE_INHERITING; - } - } else if (S_ISDIR(sb.st_mode)) { - *type = __WASI_FILETYPE_DIRECTORY; - *rights_base = RIGHTS_DIRECTORY_BASE; - *rights_inheriting = RIGHTS_DIRECTORY_INHERITING; - } else if (S_ISREG(sb.st_mode)) { - *type = __WASI_FILETYPE_REGULAR_FILE; - *rights_base = RIGHTS_REGULAR_FILE_BASE; - *rights_inheriting = RIGHTS_REGULAR_FILE_INHERITING; - } else if (S_ISSOCK(sb.st_mode)) { - int socktype; - socklen_t socktypelen = sizeof(socktype); - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) < 0) - return convert_errno(errno); - switch (socktype) { - case SOCK_DGRAM: - *type = __WASI_FILETYPE_SOCKET_DGRAM; - break; - case SOCK_STREAM: - *type = __WASI_FILETYPE_SOCKET_STREAM; - break; - default: - return __WASI_EINVAL; +static __wasi_errno_t +fd_determine_type_rights(os_file_handle fd, __wasi_filetype_t *type, + __wasi_rights_t *rights_base, + __wasi_rights_t *rights_inheriting) +{ + struct __wasi_filestat_t buf; + __wasi_errno_t error; + + if (os_is_stdin_handle(fd)) { + *rights_base = RIGHTS_STDIN; + *rights_inheriting = RIGHTS_STDIN; + return __WASI_ESUCCESS; + } + + if (os_is_stdout_handle(fd)) { + *rights_base = RIGHTS_STDOUT; + *rights_inheriting = RIGHTS_STDOUT; + return __WASI_ESUCCESS; } - *rights_base = RIGHTS_SOCKET_BASE; - *rights_inheriting = RIGHTS_SOCKET_INHERITING; - } else if (S_ISFIFO(sb.st_mode)) { - *type = __WASI_FILETYPE_SOCKET_STREAM; - *rights_base = RIGHTS_SOCKET_BASE; - *rights_inheriting = RIGHTS_SOCKET_INHERITING; - } else { - return __WASI_EINVAL; - } - - // Strip off read/write bits based on the access mode. - switch (fcntl(fd, F_GETFL) & O_ACCMODE) { - case O_RDONLY: - *rights_base &= ~(__wasi_rights_t)__WASI_RIGHT_FD_WRITE; - break; - case O_WRONLY: - *rights_base &= ~(__wasi_rights_t)__WASI_RIGHT_FD_READ; - break; - } - return 0; -} - -// Returns the underlying file descriptor number of a file descriptor -// object. This function can only be applied to objects that have an -// underlying file descriptor number. -static int fd_number( - const struct fd_object *fo -) { - int number = fo->number; - assert(number >= 0 && "fd_number() called on virtual file descriptor"); - return number; -} - -#define CLOSE_NON_STD_FD(fd) do { \ - if (fd > 2) \ - close(fd); \ - } while (0) + + if (os_is_stderr_handle(fd)) { + *rights_base = RIGHTS_STDERR; + *rights_inheriting = RIGHTS_STDERR; + return __WASI_ESUCCESS; + } + + error = os_fstat(fd, &buf); + if (error != __WASI_ESUCCESS) + return error; + + *type = buf.st_filetype; + + switch (buf.st_filetype) { + case __WASI_FILETYPE_BLOCK_DEVICE: + *rights_base = RIGHTS_BLOCK_DEVICE_BASE; + *rights_inheriting = RIGHTS_BLOCK_DEVICE_INHERITING; + break; + case __WASI_FILETYPE_CHARACTER_DEVICE: + error = os_isatty(fd); + + if (error == __WASI_ESUCCESS) { + *rights_base = RIGHTS_TTY_BASE; + *rights_inheriting = RIGHTS_TTY_INHERITING; + } + else { + *rights_base = RIGHTS_CHARACTER_DEVICE_BASE; + *rights_inheriting = RIGHTS_CHARACTER_DEVICE_INHERITING; + } + break; + case __WASI_FILETYPE_DIRECTORY: + *rights_base = RIGHTS_DIRECTORY_BASE; + *rights_inheriting = RIGHTS_DIRECTORY_INHERITING; + break; + case __WASI_FILETYPE_REGULAR_FILE: + *rights_base = RIGHTS_REGULAR_FILE_BASE; + *rights_inheriting = RIGHTS_REGULAR_FILE_INHERITING; + break; + case __WASI_FILETYPE_SOCKET_DGRAM: + case __WASI_FILETYPE_SOCKET_STREAM: + *rights_base = RIGHTS_SOCKET_BASE; + *rights_inheriting = RIGHTS_SOCKET_INHERITING; + break; + case __WASI_FILETYPE_SYMBOLIC_LINK: + case __WASI_FILETYPE_UNKNOWN: + // If we don't know the type, allow for the maximum set of + // rights + *rights_base = RIGHTS_ALL; + *rights_inheriting = RIGHTS_ALL; + break; + default: + return __WASI_EINVAL; + } + + wasi_libc_file_access_mode access_mode; + error = os_file_get_access_mode(fd, &access_mode); + + if (error != __WASI_ESUCCESS) + return error; + + // Strip off read/write bits based on the access mode. + switch (access_mode) { + case WASI_LIBC_ACCESS_MODE_READ_ONLY: + *rights_base &= ~(__wasi_rights_t)__WASI_RIGHT_FD_WRITE; + break; + case WASI_LIBC_ACCESS_MODE_WRITE_ONLY: + *rights_base &= ~(__wasi_rights_t)__WASI_RIGHT_FD_READ; + break; + } + + return error; +} // Lowers the reference count on a file descriptor object. When the // reference count reaches zero, its resources are cleaned up. -static void fd_object_release( - struct fd_object *fo -) UNLOCKS(fo->refcount) { - if (refcount_release(&fo->refcount)) { - switch (fo->type) { - case __WASI_FILETYPE_DIRECTORY: - // For directories we may keep track of a DIR object. Calling - // closedir() on it also closes the underlying file descriptor. - mutex_destroy(&fo->directory.lock); - if (fo->directory.handle == NULL) { - CLOSE_NON_STD_FD(fd_number(fo)); - } else { - closedir(fo->directory.handle); +static __wasi_errno_t +fd_object_release(wasm_exec_env_t env, struct fd_object *fo) + UNLOCKS(fo->refcount) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + + if (refcount_release(&fo->refcount)) { + int saved_errno = errno; + switch (fo->type) { + case __WASI_FILETYPE_DIRECTORY: + // For directories we may keep track of a DIR object. + // Calling os_closedir() on it also closes the underlying file + // descriptor. + mutex_destroy(&fo->directory.lock); + if (os_is_dir_stream_valid(&fo->directory.handle)) { + error = os_closedir(fo->directory.handle); + break; + } + // Fallthrough. + default: + // The env == NULL case is for + // fd_table_destroy, path_get, path_put, + // fd_table_insert_existing + error = (env == NULL) ? os_close(fo->file_handle, fo->is_stdio) + : blocking_op_close(env, fo->file_handle, + fo->is_stdio); + break; } - break; - default: - CLOSE_NON_STD_FD(fd_number(fo)); - break; + wasm_runtime_free(fo); + errno = saved_errno; } - bh_free(fo); - } + return error; } // Inserts an already existing file descriptor into the file descriptor // table. -bool fd_table_insert_existing( - struct fd_table *ft, - __wasi_fd_t in, - int out -) { - __wasi_filetype_t type; - __wasi_rights_t rights_base, rights_inheriting; - if (fd_determine_type_rights(out, &type, &rights_base, &rights_inheriting) != - 0) - return false; +bool +fd_table_insert_existing(struct fd_table *ft, __wasi_fd_t in, + os_file_handle out, bool is_stdio) +{ + __wasi_filetype_t type = __WASI_FILETYPE_UNKNOWN; + __wasi_rights_t rights_base = 0, rights_inheriting = 0; + struct fd_object *fo; + __wasi_errno_t error; - struct fd_object *fo; - __wasi_errno_t error = fd_object_new(type, &fo); - if (error != 0) - return false; - fo->number = out; - if (type == __WASI_FILETYPE_DIRECTORY) { - mutex_init(&fo->directory.lock); - fo->directory.handle = NULL; - } - - // Grow the file descriptor table if needed. - rwlock_wrlock(&ft->lock); - if (!fd_table_grow(ft, in, 1)) { - rwlock_unlock(&ft->lock); - fd_object_release(fo); - return false; - } + 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, is_stdio, &fo); + if (error != 0) + return false; + fo->file_handle = out; + if (type == __WASI_FILETYPE_DIRECTORY) { + if (!mutex_init(&fo->directory.lock)) { + fd_object_release(NULL, fo); + return false; + } + fo->directory.handle = os_get_invalid_dir_stream(); + } - fd_table_attach(ft, in, fo, rights_base, rights_inheriting); - rwlock_unlock(&ft->lock); - return true; + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, in, 1)) { + rwlock_unlock(&ft->lock); + fd_object_release(NULL, fo); + return false; + } + + fd_table_attach(ft, in, fo, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return true; } // Picks an unused slot from the file descriptor table. -static __wasi_fd_t fd_table_unused( - struct fd_table *ft -) REQUIRES_SHARED(ft->lock) { - assert(ft->size > ft->used && "File descriptor table has no free slots"); - for (;;) { - __wasi_fd_t fd = (__wasi_fd_t)random_uniform(ft->size); - if (ft->entries[fd].object == NULL) - return fd; - } +static __wasi_errno_t +fd_table_unused(struct fd_table *ft, __wasi_fd_t *out) REQUIRES_SHARED(ft->lock) +{ + bh_assert(ft->size > ft->used && "File descriptor table has no free slots"); + for (;;) { + uintmax_t random_fd = 0; + __wasi_errno_t error = random_uniform(ft->size, &random_fd); + + if (error != __WASI_ESUCCESS) + return error; + + if (ft->entries[(__wasi_fd_t)random_fd].object == NULL) { + *out = (__wasi_fd_t)random_fd; + return error; + } + } } // Inserts a file descriptor object into an unused slot of the file // descriptor table. -static __wasi_errno_t fd_table_insert( - struct fd_table *ft, - struct fd_object *fo, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting, - __wasi_fd_t *out -) REQUIRES_UNLOCKED(ft->lock) UNLOCKS(fo->refcount) { - // Grow the file descriptor table if needed. - rwlock_wrlock(&ft->lock); - if (!fd_table_grow(ft, 0, 1)) { - rwlock_unlock(&ft->lock); - fd_object_release(fo); - return convert_errno(errno); - } +static __wasi_errno_t +fd_table_insert(wasm_exec_env_t exec_env, struct fd_table *ft, + struct fd_object *fo, __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, __wasi_fd_t *out) + REQUIRES_UNLOCKED(ft->lock) UNLOCKS(fo->refcount) +{ + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, 0, 1)) { + rwlock_unlock(&ft->lock); + fd_object_release(exec_env, fo); + return convert_errno(errno); + } - *out = fd_table_unused(ft); - fd_table_attach(ft, *out, fo, rights_base, rights_inheriting); - rwlock_unlock(&ft->lock); - return 0; -} + __wasi_errno_t error = fd_table_unused(ft, out); -// Inserts a numerical file descriptor into the file descriptor table. -static __wasi_errno_t fd_table_insert_fd( - struct fd_table *ft, - int in, - __wasi_filetype_t type, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting, - __wasi_fd_t *out -) REQUIRES_UNLOCKED(ft->lock) { - struct fd_object *fo; - __wasi_errno_t error = fd_object_new(type, &fo); - if (error != 0) { - close(in); - return error; - } - fo->number = in; - if (type == __WASI_FILETYPE_DIRECTORY) { - mutex_init(&fo->directory.lock); - fo->directory.handle = NULL; - } - return fd_table_insert(ft, fo, rights_base, rights_inheriting, out); -} - -__wasi_errno_t wasmtime_ssp_fd_prestat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_prestats *prestats, -#endif - __wasi_fd_t fd, - __wasi_prestat_t *buf -) { - rwlock_rdlock(&prestats->lock); - struct fd_prestat *prestat; - __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); - if (error != 0) { - rwlock_unlock(&prestats->lock); - return error; - } + if (error != __WASI_ESUCCESS) { + rwlock_unlock(&ft->lock); + return error; + } - *buf = (__wasi_prestat_t) { - .pr_type = __WASI_PREOPENTYPE_DIR, - }; + fd_table_attach(ft, *out, fo, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return error; +} - buf->u.dir.pr_name_len = strlen(prestat->dir); +// Inserts a numerical file descriptor into the file descriptor table. +static __wasi_errno_t +fd_table_insert_fd(wasm_exec_env_t exec_env, struct fd_table *ft, + os_file_handle in, __wasi_filetype_t type, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, __wasi_fd_t *out) + REQUIRES_UNLOCKED(ft->lock) +{ + struct fd_object *fo; - rwlock_unlock(&prestats->lock); + __wasi_errno_t error = fd_object_new(type, false, &fo); + if (error != 0) { + os_close(in, false); + return error; + } - return 0; + fo->file_handle = in; + if (type == __WASI_FILETYPE_DIRECTORY) { + if (!mutex_init(&fo->directory.lock)) { + fd_object_release(exec_env, fo); + return (__wasi_errno_t)-1; + } + fo->directory.handle = os_get_invalid_dir_stream(); + } + return fd_table_insert(exec_env, ft, fo, rights_base, rights_inheriting, + out); } -__wasi_errno_t wasmtime_ssp_fd_prestat_dir_name( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_prestats *prestats, -#endif - __wasi_fd_t fd, - char *path, - size_t path_len -) { - rwlock_rdlock(&prestats->lock); - struct fd_prestat *prestat; - __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); - if (error != 0) { - rwlock_unlock(&prestats->lock); - return error; - } - if (path_len != strlen(prestat->dir)) { - rwlock_unlock(&prestats->lock); - return EINVAL; - } +__wasi_errno_t +wasmtime_ssp_fd_prestat_get(struct fd_prestats *prestats, __wasi_fd_t fd, + __wasi_prestat_t *buf) +{ + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + if (error != 0) { + rwlock_unlock(&prestats->lock); + return error; + } + + *buf = (__wasi_prestat_t){ + .pr_type = __WASI_PREOPENTYPE_DIR, + }; - bh_memcpy_s(path, (uint32)path_len, prestat->dir, (uint32)path_len); + buf->u.dir.pr_name_len = strlen(prestat->dir); - rwlock_unlock(&prestats->lock); + rwlock_unlock(&prestats->lock); - return 0; + return 0; } -__wasi_errno_t wasmtime_ssp_fd_close( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - __wasi_fd_t fd -) { - // Don't allow closing a pre-opened resource. - // TODO: Eventually, we do want to permit this, once libpreopen in - // userspace is capable of removing entries from its tables as well. - { +__wasi_errno_t +wasmtime_ssp_fd_prestat_dir_name(struct fd_prestats *prestats, __wasi_fd_t fd, + char *path, size_t path_len) +{ rwlock_rdlock(&prestats->lock); struct fd_prestat *prestat; __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + if (error != 0) { + rwlock_unlock(&prestats->lock); + return error; + } + + const size_t prestat_dir_len = strlen(prestat->dir); + if (path_len < prestat_dir_len) { + rwlock_unlock(&prestats->lock); + return __WASI_EINVAL; + } + + bh_memcpy_s(path, (uint32)path_len, prestat->dir, (uint32)prestat_dir_len); + rwlock_unlock(&prestats->lock); - if (error == 0) { - return __WASI_ENOTSUP; + + return 0; +} + +__wasi_errno_t +wasmtime_ssp_fd_close(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, __wasi_fd_t fd) +{ + // Validate the file descriptor. + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + rwlock_wrlock(&prestats->lock); + + struct fd_entry *fe; + __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); + if (error != 0) { + rwlock_unlock(&prestats->lock); + rwlock_unlock(&ft->lock); + return error; } - } - // Validate the file descriptor. - struct fd_table *ft = curfds; - rwlock_wrlock(&ft->lock); - struct fd_entry *fe; - __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); - if (error != 0) { + // Remove it from the file descriptor table. + struct fd_object *fo; + fd_table_detach(ft, fd, &fo); + + // Remove it from the preopened resource table if it exists + error = fd_prestats_remove_entry(prestats, fd); + + rwlock_unlock(&prestats->lock); rwlock_unlock(&ft->lock); - return error; - } + fd_object_release(exec_env, fo); + + // Ignore the error if there is no preopen associated with this fd + if (error == __WASI_EBADF) { + return __WASI_ESUCCESS; + } - // Remove it from the file descriptor table. - struct fd_object *fo; - fd_table_detach(ft, fd, &fo); - rwlock_unlock(&ft->lock); - fd_object_release(fo); - return 0; + return error; } // Look up a file descriptor object in a locked file descriptor table // and increases its reference count. -static __wasi_errno_t fd_object_get_locked( - struct fd_object **fo, - struct fd_table *ft, - __wasi_fd_t fd, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting -) TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) REQUIRES_EXCLUSIVE(ft->lock) { - // Test whether the file descriptor number is valid. - struct fd_entry *fe; - __wasi_errno_t error = - fd_table_get_entry(ft, fd, rights_base, rights_inheriting, &fe); - if (error != 0) - return error; - - // Increase the reference count on the file descriptor object. A copy - // of the rights are also stored, so callers can still access those if - // needed. - *fo = fe->object; - refcount_acquire(&(*fo)->refcount); - return 0; +static __wasi_errno_t +fd_object_get_locked(struct fd_object **fo, struct fd_table *ft, __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting) + TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) REQUIRES_EXCLUSIVE(ft->lock) +{ + // Test whether the file descriptor number is valid. + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, rights_base, rights_inheriting, &fe); + if (error != 0) + return error; + + // Increase the reference count on the file descriptor object. A copy + // of the rights are also stored, so callers can still access those if + // needed. + *fo = fe->object; + refcount_acquire(&(*fo)->refcount); + return 0; } // Temporarily locks the file descriptor table to look up a file // descriptor object, increases its reference count and drops the lock. -static __wasi_errno_t fd_object_get( - struct fd_table *curfds, - struct fd_object **fo, - __wasi_fd_t fd, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting -) TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) { - struct fd_table *ft = curfds; - rwlock_rdlock(&ft->lock); - __wasi_errno_t error = - fd_object_get_locked(fo, ft, fd, rights_base, rights_inheriting); - rwlock_unlock(&ft->lock); - return error; -} - -__wasi_errno_t wasmtime_ssp_fd_datasync( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd -) { - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_DATASYNC, 0); - if (error != 0) +static __wasi_errno_t +fd_object_get(struct fd_table *curfds, struct fd_object **fo, __wasi_fd_t fd, + __wasi_rights_t rights_base, __wasi_rights_t rights_inheriting) + TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) +{ + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + __wasi_errno_t error = + fd_object_get_locked(fo, ft, fd, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); return error; - -#if CONFIG_HAS_FDATASYNC - int ret = fdatasync(fd_number(fo)); -#else - int ret = fsync(fd_number(fo)); -#endif - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; } -__wasi_errno_t wasmtime_ssp_fd_pread( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_iovec_t *iov, - size_t iovcnt, - __wasi_filesize_t offset, - size_t *nread -) { - if (iovcnt == 0) - return __WASI_EINVAL; - - struct fd_object *fo; - __wasi_errno_t error = fd_object_get(curfds, - &fo, fd, __WASI_RIGHT_FD_READ, 0); - if (error != 0) - return error; - -#if CONFIG_HAS_PREADV - ssize_t len = - preadv(fd_number(fo), (const struct iovec *)iov, (int)iovcnt, (off_t)offset); - fd_object_release(fo); - if (len < 0) - return convert_errno(errno); - *nread = (size_t)len; - return 0; -#else - if (iovcnt == 1) { - ssize_t len = pread(fd_number(fo), iov->buf, iov->buf_len, offset); - fd_object_release(fo); - if (len < 0) - return convert_errno(errno); - *nread = len; - return 0; - } else { - // Allocate a single buffer to fit all data. - size_t totalsize = 0; - for (size_t i = 0; i < iovcnt; ++i) - totalsize += iov[i].buf_len; - char *buf = bh_malloc(totalsize); - if (buf == NULL) { - fd_object_release(fo); - return __WASI_ENOMEM; - } - - // Perform a single read operation. - ssize_t len = pread(fd_number(fo), buf, totalsize, offset); - fd_object_release(fo); - if (len < 0) { - bh_free(buf); - return convert_errno(errno); - } - - // Copy data back to vectors. - size_t bufoff = 0; - for (size_t i = 0; i < iovcnt; ++i) { - if (bufoff + iov[i].buf_len < len) { - bh_memcpy_s(iov[i].buf, iov[i].buf_len, buf + bufoff, iov[i].buf_len); - bufoff += iov[i].buf_len; - } else { - bh_memcpy_s(iov[i].buf, iov[i].buf_len, buf + bufoff, len - bufoff); - break; - } - } - bh_free(buf); - *nread = len; - return 0; - } -#endif -} +__wasi_errno_t +wasmtime_ssp_fd_datasync(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_DATASYNC, 0); + if (error != 0) + return error; -__wasi_errno_t wasmtime_ssp_fd_pwrite( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_ciovec_t *iov, - size_t iovcnt, - __wasi_filesize_t offset, - size_t *nwritten -) { - if (iovcnt == 0) - return __WASI_EINVAL; - - struct fd_object *fo; - __wasi_errno_t error = fd_object_get(curfds, - &fo, fd, __WASI_RIGHT_FD_WRITE, 0); - if (error != 0) - return error; + error = os_fdatasync(fo->file_handle); - ssize_t len; -#if CONFIG_HAS_PWRITEV - len = pwritev(fd_number(fo), (const struct iovec *)iov, (int)iovcnt, (off_t)offset); -#else - if (iovcnt == 1) { - len = pwrite(fd_number(fo), iov->buf, iov->buf_len, offset); - } else { - // Allocate a single buffer to fit all data. - size_t totalsize = 0; - for (size_t i = 0; i < iovcnt; ++i) - totalsize += iov[i].buf_len; - char *buf = bh_malloc(totalsize); - if (buf == NULL) { - fd_object_release(fo); - return __WASI_ENOMEM; - } - size_t bufoff = 0; - for (size_t i = 0; i < iovcnt; ++i) { - bh_memcpy_s(buf + bufoff, totalsize - bufoff, - iov[i].buf, iov[i].buf_len); - bufoff += iov[i].buf_len; - } - - // Perform a single write operation. - len = pwrite(fd_number(fo), buf, totalsize, offset); - bh_free(buf); - } -#endif - fd_object_release(fo); - if (len < 0) - return convert_errno(errno); - *nwritten = (size_t)len; - return 0; -} + fd_object_release(exec_env, fo); -__wasi_errno_t wasmtime_ssp_fd_read( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_iovec_t *iov, - size_t iovcnt, - size_t *nread -) { - struct fd_object *fo; - __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READ, 0); - if (error != 0) return error; - - ssize_t len = readv(fd_number(fo), (const struct iovec *)iov, (int)iovcnt); - fd_object_release(fo); - if (len < 0) - return convert_errno(errno); - *nread = (size_t)len; - return 0; } -__wasi_errno_t wasmtime_ssp_fd_renumber( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - __wasi_fd_t from, - __wasi_fd_t to -) { - // Don't allow renumbering over a pre-opened resource. - // TODO: Eventually, we do want to permit this, once libpreopen in - // userspace is capable of removing entries from its tables as well. - { - rwlock_rdlock(&prestats->lock); - struct fd_prestat *prestat; - __wasi_errno_t error = fd_prestats_get_entry(prestats, to, &prestat); - if (error != 0) { - error = fd_prestats_get_entry(prestats, from, &prestat); - } - rwlock_unlock(&prestats->lock); - if (error == 0) { - return __WASI_ENOTSUP; - } - } +__wasi_errno_t +wasmtime_ssp_fd_pread(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_iovec_t *iov, size_t iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ + if (iovcnt == 0) + return __WASI_EINVAL; - struct fd_table *ft = curfds; - rwlock_wrlock(&ft->lock); - struct fd_entry *fe_from; - __wasi_errno_t error = fd_table_get_entry(ft, from, 0, 0, &fe_from); - if (error != 0) { - rwlock_unlock(&ft->lock); - return error; - } - struct fd_entry *fe_to; - error = fd_table_get_entry(ft, to, 0, 0, &fe_to); - if (error != 0) { - rwlock_unlock(&ft->lock); - return error; - } + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READ, 0); - struct fd_object *fo; - fd_table_detach(ft, to, &fo); - refcount_acquire(&fe_from->object->refcount); - fd_table_attach(ft, to, fe_from->object, fe_from->rights_base, - fe_from->rights_inheriting); - fd_object_release(fo); + if (error != 0) + return error; - // Remove the old fd from the file descriptor table. - fd_table_detach(ft, from, &fo); - fd_object_release(fo); - --ft->used; + error = blocking_op_preadv(exec_env, fo->file_handle, iov, (int)iovcnt, + offset, nread); - rwlock_unlock(&ft->lock); - return 0; -} + fd_object_release(exec_env, fo); -__wasi_errno_t wasmtime_ssp_fd_seek( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filedelta_t offset, - __wasi_whence_t whence, - __wasi_filesize_t *newoffset -) { - int nwhence; - switch (whence) { - case __WASI_WHENCE_CUR: - nwhence = SEEK_CUR; - break; - case __WASI_WHENCE_END: - nwhence = SEEK_END; - break; - case __WASI_WHENCE_SET: - nwhence = SEEK_SET; - break; - default: - return __WASI_EINVAL; - } - - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, - offset == 0 && whence == __WASI_WHENCE_CUR - ? __WASI_RIGHT_FD_TELL - : __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_TELL, - 0); - if (error != 0) return error; - - off_t ret = lseek(fd_number(fo), offset, nwhence); - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - *newoffset = (__wasi_filesize_t)ret; - return 0; } -__wasi_errno_t wasmtime_ssp_fd_tell( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t *newoffset -) { - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_TELL, 0); - if (error != 0) - return error; +__wasi_errno_t +wasmtime_ssp_fd_pwrite(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_ciovec_t *iov, + size_t iovcnt, __wasi_filesize_t offset, + size_t *nwritten) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_WRITE, 0); - off_t ret = lseek(fd_number(fo), 0, SEEK_CUR); - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - *newoffset = (__wasi_filesize_t)ret; - return 0; -} + if (error != 0) + return error; + + error = blocking_op_pwritev(exec_env, fo->file_handle, iov, (int)iovcnt, + offset, nwritten); + fd_object_release(exec_env, fo); -__wasi_errno_t wasmtime_ssp_fd_fdstat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_fdstat_t *buf -) { - struct fd_table *ft = curfds; - rwlock_rdlock(&ft->lock); - struct fd_entry *fe; - __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); - if (error != 0) { - rwlock_unlock(&ft->lock); return error; - } - - // Extract file descriptor type and rights. - struct fd_object *fo = fe->object; - *buf = (__wasi_fdstat_t){ - .fs_filetype = fo->type, - .fs_rights_base = fe->rights_base, - .fs_rights_inheriting = fe->rights_inheriting, - }; - - // Fetch file descriptor flags. - int ret; - switch (fo->type) { - default: - ret = fcntl(fd_number(fo), F_GETFL); - break; - } - rwlock_unlock(&ft->lock); - if (ret < 0) - return convert_errno(errno); - - if ((ret & O_APPEND) != 0) - buf->fs_flags |= __WASI_FDFLAG_APPEND; -#ifdef O_DSYNC - if ((ret & O_DSYNC) != 0) - buf->fs_flags |= __WASI_FDFLAG_DSYNC; -#endif - if ((ret & O_NONBLOCK) != 0) - buf->fs_flags |= __WASI_FDFLAG_NONBLOCK; -#ifdef O_RSYNC - if ((ret & O_RSYNC) != 0) - buf->fs_flags |= __WASI_FDFLAG_RSYNC; -#endif - if ((ret & O_SYNC) != 0) - buf->fs_flags |= __WASI_FDFLAG_SYNC; - return 0; } -__wasi_errno_t wasmtime_ssp_fd_fdstat_set_flags( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_fdflags_t fs_flags -) { - int noflags = 0; - if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) - noflags |= O_APPEND; - if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) -#ifdef O_DSYNC - noflags |= O_DSYNC; -#else - noflags |= O_SYNC; -#endif - if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) - noflags |= O_NONBLOCK; - if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) -#ifdef O_RSYNC - noflags |= O_RSYNC; -#else - noflags |= O_SYNC; -#endif - if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) - noflags |= O_SYNC; +__wasi_errno_t +wasmtime_ssp_fd_read(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_iovec_t *iov, size_t iovcnt, + size_t *nread) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READ, 0); - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS, 0); - if (error != 0) - return error; + if (error != 0) + return error; - int ret = fcntl(fd_number(fo), F_SETFL, noflags); - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; -} + error = + blocking_op_readv(exec_env, fo->file_handle, iov, (int)iovcnt, nread); -__wasi_errno_t wasmtime_ssp_fd_fdstat_set_rights( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_rights_t fs_rights_base, - __wasi_rights_t fs_rights_inheriting -) { - struct fd_table *ft = curfds; - rwlock_wrlock(&ft->lock); - struct fd_entry *fe; - __wasi_errno_t error = - fd_table_get_entry(ft, fd, fs_rights_base, fs_rights_inheriting, &fe); - if (error != 0) { - rwlock_unlock(&ft->lock); - return error; - } + fd_object_release(exec_env, fo); - // Restrict the rights on the file descriptor. - fe->rights_base = fs_rights_base; - fe->rights_inheriting = fs_rights_inheriting; - rwlock_unlock(&ft->lock); - return 0; + return error; } -__wasi_errno_t wasmtime_ssp_fd_sync( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd -) { - struct fd_object *fo; - __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_SYNC, 0); - if (error != 0) +__wasi_errno_t +wasmtime_ssp_fd_renumber(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, __wasi_fd_t from, + __wasi_fd_t to) +{ + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + rwlock_wrlock(&prestats->lock); + + struct fd_entry *fe_from; + __wasi_errno_t error = fd_table_get_entry(ft, from, 0, 0, &fe_from); + if (error != 0) { + rwlock_unlock(&prestats->lock); + rwlock_unlock(&ft->lock); + return error; + } + struct fd_entry *fe_to; + error = fd_table_get_entry(ft, to, 0, 0, &fe_to); + if (error != 0) { + rwlock_unlock(&prestats->lock); + rwlock_unlock(&ft->lock); + return error; + } + + struct fd_object *fo; + fd_table_detach(ft, to, &fo); + refcount_acquire(&fe_from->object->refcount); + fd_table_attach(ft, to, fe_from->object, fe_from->rights_base, + fe_from->rights_inheriting); + fd_object_release(exec_env, fo); + + // Remove the old fd from the file descriptor table. + fd_table_detach(ft, from, &fo); + fd_object_release(exec_env, fo); + --ft->used; + + // Handle renumbering of any preopened resources + struct fd_prestat *prestat_from; + __wasi_errno_t prestat_from_error = + fd_prestats_get_entry(prestats, from, &prestat_from); + + struct fd_prestat *prestat_to; + __wasi_errno_t prestat_to_error = + fd_prestats_get_entry(prestats, to, &prestat_to); + + // Renumbering over two preopened resources. + if (prestat_from_error == __WASI_ESUCCESS + && prestat_to_error == __WASI_ESUCCESS) { + (void)fd_prestats_remove_entry(prestats, to); + + error = fd_prestats_insert_locked(prestats, prestat_from->dir, to); + + if (error == __WASI_ESUCCESS) { + (void)fd_prestats_remove_entry(prestats, from); + } + else { + (void)fd_prestats_remove_entry(prestats, to); + } + } + // Renumbering from a non-preopened fd to a preopened fd. In this case, + // we can't a keep the destination fd entry in the preopened table so + // remove it entirely. + else if (prestat_from_error != __WASI_ESUCCESS + && prestat_to_error == __WASI_ESUCCESS) { + (void)fd_prestats_remove_entry(prestats, to); + } + // Renumbering from a preopened fd to a non-preopened fd + else if (prestat_from_error == __WASI_ESUCCESS + && prestat_to_error != __WASI_ESUCCESS) { + error = fd_prestats_insert_locked(prestats, prestat_from->dir, to); + + if (error == __WASI_ESUCCESS) { + (void)fd_prestats_remove_entry(prestats, from); + } + else { + (void)fd_prestats_remove_entry(prestats, to); + } + } + + rwlock_unlock(&prestats->lock); + rwlock_unlock(&ft->lock); + return error; +} + +__wasi_errno_t +wasmtime_ssp_fd_seek(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *newoffset) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, + offset == 0 && whence == __WASI_WHENCE_CUR + ? __WASI_RIGHT_FD_TELL + : __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_TELL, + 0); + if (error != 0) + return error; + + error = os_lseek(fo->file_handle, offset, whence, newoffset); + + fd_object_release(exec_env, fo); - int ret = fsync(fd_number(fo)); - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; + return error; } -__wasi_errno_t wasmtime_ssp_fd_write( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const __wasi_ciovec_t *iov, - size_t iovcnt, - size_t *nwritten -) { - struct fd_object *fo; - __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_WRITE, 0); - if (error != 0) +__wasi_errno_t +wasmtime_ssp_fd_tell(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filesize_t *newoffset) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_TELL, 0); + if (error != 0) + return error; + + error = os_lseek(fo->file_handle, 0, __WASI_WHENCE_CUR, newoffset); + + fd_object_release(exec_env, fo); + return error; +} + +__wasi_errno_t +wasmtime_ssp_fd_fdstat_get(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_fdstat_t *buf) +{ + struct fd_table *ft = curfds; + struct fd_entry *fe; + __wasi_errno_t error; + + (void)exec_env; + + rwlock_rdlock(&ft->lock); + error = fd_table_get_entry(ft, fd, 0, 0, &fe); + if (error != __WASI_ESUCCESS) { + rwlock_unlock(&ft->lock); + return error; + } + + // Extract file descriptor type and rights. + struct fd_object *fo = fe->object; + + __wasi_fdflags_t flags; + error = os_file_get_fdflags(fo->file_handle, &flags); + + if (error != __WASI_ESUCCESS) { + rwlock_unlock(&ft->lock); + return error; + } - ssize_t len = writev(fd_number(fo), (const struct iovec *)iov, (int)iovcnt); - fd_object_release(fo); - if (len < 0) - return convert_errno(errno); - *nwritten = (size_t)len; - return 0; + *buf = (__wasi_fdstat_t){ .fs_filetype = fo->type, + .fs_rights_base = fe->rights_base, + .fs_rights_inheriting = fe->rights_inheriting, + .fs_flags = flags }; + + rwlock_unlock(&ft->lock); + return error; } -__wasi_errno_t wasmtime_ssp_fd_advise( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t offset, - __wasi_filesize_t len, - __wasi_advice_t advice -) { -#ifdef POSIX_FADV_NORMAL - int nadvice; - switch (advice) { - case __WASI_ADVICE_DONTNEED: - nadvice = POSIX_FADV_DONTNEED; - break; - case __WASI_ADVICE_NOREUSE: - nadvice = POSIX_FADV_NOREUSE; - break; - case __WASI_ADVICE_NORMAL: - nadvice = POSIX_FADV_NORMAL; - break; - case __WASI_ADVICE_RANDOM: - nadvice = POSIX_FADV_RANDOM; - break; - case __WASI_ADVICE_SEQUENTIAL: - nadvice = POSIX_FADV_SEQUENTIAL; - break; - case __WASI_ADVICE_WILLNEED: - nadvice = POSIX_FADV_WILLNEED; - break; - default: - return __WASI_EINVAL; - } - - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ADVISE, 0); - if (error != 0) +__wasi_errno_t +wasmtime_ssp_fd_fdstat_set_flags(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_fdflags_t fs_flags) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS, 0); + + if (error != 0) + return error; + + error = os_file_set_fdflags(fo->file_handle, fs_flags); + + fd_object_release(exec_env, fo); + return error; +} - int ret = posix_fadvise(fd_number(fo), (off_t)offset, (off_t)len, nadvice); - fd_object_release(fo); - if (ret != 0) - return convert_errno(ret); - return 0; -#else - // Advisory information can safely be ignored if unsupported. - switch (advice) { - case __WASI_ADVICE_DONTNEED: - case __WASI_ADVICE_NOREUSE: - case __WASI_ADVICE_NORMAL: - case __WASI_ADVICE_RANDOM: - case __WASI_ADVICE_SEQUENTIAL: - case __WASI_ADVICE_WILLNEED: - break; - default: - return __WASI_EINVAL; - } - - // At least check for file descriptor existence. - struct fd_table *ft = curfds; - rwlock_rdlock(&ft->lock); - struct fd_entry *fe; - __wasi_errno_t error = - fd_table_get_entry(ft, fd, __WASI_RIGHT_FD_ADVISE, 0, &fe); - rwlock_unlock(&ft->lock); - return error; -#endif +__wasi_errno_t +wasmtime_ssp_fd_fdstat_set_rights(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting) +{ + struct fd_table *ft = curfds; + struct fd_entry *fe; + __wasi_errno_t error; + + (void)exec_env; + + rwlock_wrlock(&ft->lock); + error = + fd_table_get_entry(ft, fd, fs_rights_base, fs_rights_inheriting, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Restrict the rights on the file descriptor. + fe->rights_base = fs_rights_base; + fe->rights_inheriting = fs_rights_inheriting; + rwlock_unlock(&ft->lock); + return 0; } -__wasi_errno_t wasmtime_ssp_fd_allocate( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t offset, - __wasi_filesize_t len -) { - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ALLOCATE, 0); - if (error != 0) +__wasi_errno_t +wasmtime_ssp_fd_sync(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_SYNC, 0); + + if (error != 0) + return error; + + error = os_fsync(fo->file_handle); + + fd_object_release(exec_env, fo); + return error; +} -#if CONFIG_HAS_POSIX_FALLOCATE - int ret = posix_fallocate(fd_number(fo), (off_t)offset, (off_t)len); +__wasi_errno_t +wasmtime_ssp_fd_write(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const __wasi_ciovec_t *iov, size_t iovcnt, + size_t *nwritten) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) + return error; + +#ifndef BH_VPRINTF + error = blocking_op_writev(exec_env, fo->file_handle, iov, (int)iovcnt, + nwritten); #else - // At least ensure that the file is grown to the right size. - // TODO(ed): See if this can somehow be implemented without any race - // conditions. We may end up shrinking the file right now. - struct stat sb; - int ret = fstat(fd_number(fo), &sb); - if (ret == 0 && sb.st_size < offset + len) - ret = ftruncate(fd_number(fo), offset + len); -#endif + /* redirect stdout/stderr output to BH_VPRINTF function */ + if (fo->is_stdio) { + int i; + *nwritten = 0; + for (i = 0; i < (int)iovcnt; i++) { + if (iov[i].buf_len > 0 && iov[i].buf != NULL) { + char format[16]; + + /* make up format string "%.ns" */ + snprintf(format, sizeof(format), "%%.%ds", (int)iov[i].buf_len); + *nwritten += (size_t)os_printf(format, iov[i].buf); + } + } + } + else { + error = blocking_op_writev(exec_env, fo->file_handle, iov, (int)iovcnt, + nwritten); + } +#endif /* end of BH_VPRINTF */ + fd_object_release(exec_env, fo); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_fd_advise(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filesize_t offset, + __wasi_filesize_t len, __wasi_advice_t advice) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ADVISE, 0); + if (error != 0) + return error; + + if (fo->type == __WASI_FILETYPE_DIRECTORY) { + fd_object_release(exec_env, fo); + return __WASI_EBADF; + } + + error = os_fadvise(fo->file_handle, offset, len, advice); + + fd_object_release(exec_env, fo); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_fd_allocate(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filesize_t offset, + __wasi_filesize_t len) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ALLOCATE, 0); + if (error != __WASI_ESUCCESS) + return error; + + error = os_fallocate(fo->file_handle, offset, len); + + fd_object_release(exec_env, fo); - fd_object_release(fo); - if (ret != 0) - return convert_errno(ret); - return 0; + return error; } // Reads the entire contents of a symbolic link, returning the contents // in an allocated buffer. The allocated buffer is large enough to fit // at least one extra byte, so the caller may append a trailing slash to // it. This is needed by path_get(). -static char *readlinkat_dup( - int fd, - const char *path, - size_t *p_len -) { - char *buf = NULL; - size_t len = 32; - size_t len_org = len; +__wasi_errno_t +readlinkat_dup(os_file_handle handle, const char *path, size_t *p_len, + char **out_buf) +{ + __wasi_errno_t error; + struct __wasi_filestat_t stat = { 0 }; + size_t buf_len; + + /* + * use fstatat to get a better estimation + * If path is a symbolic link, do not dereference it: + * instead return information about the link itself, + * like lstat(). + */ + error = os_fstatat(handle, path, &stat, 0); + if (error != __WASI_ESUCCESS) { + stat.st_size = 0; + } - for (;;) { - char *newbuf = bh_malloc((uint32)len); + /* + * Some magic symlinks report `st_size` as zero. In that case, take + * 32 as the initial buffer size. Otherwise, use `st_size + 1`. + */ + buf_len = stat.st_size ? stat.st_size + 1 : 32; + for (;;) { + size_t bytes_read = 0; + char *buf; + + buf = wasm_runtime_malloc((uint32)buf_len); + if (buf == NULL) { + *out_buf = NULL; + return __WASI_ENOMEM; + } - if (newbuf == NULL) { - if (buf) - bh_free(buf); - return NULL; - } + error = os_readlinkat(handle, path, buf, buf_len, &bytes_read); + if (error != __WASI_ESUCCESS) { + wasm_runtime_free(buf); + *p_len = 0; + *out_buf = NULL; + return error; + } - if (buf != NULL) { - bh_memcpy_s(newbuf, (uint32)len, buf, (uint32)len_org); - bh_free(buf); - } + /* not truncated */ + if (bytes_read < buf_len) { + buf[bytes_read] = '\0'; + *p_len = bytes_read + 1; + *out_buf = buf; + return __WASI_ESUCCESS; + } - buf = newbuf; - ssize_t ret = readlinkat(fd, path, buf, len); - if (ret < 0) { - bh_free(buf); - return NULL; + /* truncated, try again with a bigger buf */ + wasm_runtime_free(buf); + buf = NULL; + buf_len *= 2; } - if ((size_t)ret + 1 < len) { - buf[ret] = '\0'; - *p_len = len; - return buf; - } - len_org = len; - len *= 2; - } } // Lease to a directory, so a path underneath it can be accessed. @@ -1413,287 +1290,296 @@ static char *readlinkat_dup( // descriptor representing the directory where the lookup needs to start // and the actual pathname string. struct path_access { - int fd; // Directory file descriptor. - const char *path; // Pathname. - bool follow; // Whether symbolic links should be followed. - char *path_start; // Internal: pathname to free. - struct fd_object *fd_object; // Internal: directory file descriptor object. + os_file_handle fd; // Directory file descriptor. + const char *path; // Pathname. + bool follow; // Whether symbolic links should be followed. + char *path_start; // Internal: pathname to free. + struct fd_object *fd_object; // Internal: directory file descriptor object. }; // Creates a lease to a file descriptor and pathname pair. If the // operating system does not implement Capsicum, it also normalizes the // pathname to ensure the target path is placed underneath the // directory. -static __wasi_errno_t path_get( - struct fd_table *curfds, - struct path_access *pa, - __wasi_fd_t fd, - __wasi_lookupflags_t flags, - const char *upath, - size_t upathlen, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting, - bool needs_final_component -) TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) { - char *path = str_nullterminate(upath, upathlen); - if (path == NULL) - return convert_errno(errno); - - // Fetch the directory file descriptor. - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, rights_base, rights_inheriting); - if (error != 0) { - bh_free(path); - return error; - } +static __wasi_errno_t +path_get(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct path_access *pa, __wasi_fd_t fd, __wasi_lookupflags_t flags, + const char *upath, size_t upathlen, __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, bool needs_final_component) + TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) +{ + char *path = str_nullterminate(upath, upathlen); + if (path == NULL) + return convert_errno(errno); + + // Fetch the directory file descriptor. + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, rights_base, rights_inheriting); + if (error != 0) { + wasm_runtime_free(path); + return error; + } #if CONFIG_HAS_CAP_ENTER - // Rely on the kernel to constrain access to automatically constrain - // access to files stored underneath this directory. - pa->fd = fd_number(fo); - pa->path = pa->path_start = path; - pa->follow = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0; - pa->fd_object = fo; - return 0; + // Rely on the kernel to constrain access to automatically constrain + // access to files stored underneath this directory. + pa->fd = fo->file_handle; + pa->path = pa->path_start = path; + pa->follow = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0; + pa->fd_object = fo; + return 0; #else - // The implementation provides no mechanism to constrain lookups to a - // directory automatically. Emulate this logic by resolving the - // pathname manually. - - // Stack of directory file descriptors. Index 0 always corresponds - // with the directory provided to this function. Entering a directory - // causes a file descriptor to be pushed, while handling ".." entries - // causes an entry to be popped. Index 0 cannot be popped, as this - // would imply escaping the base directory. - int fds[128]; - fds[0] = fd_number(fo); - size_t curfd = 0; - - // Stack of pathname strings used for symlink expansion. By using a - // stack, there is no need to concatenate any pathname strings while - // expanding symlinks. - char *paths[32]; - char *paths_start[32]; - paths[0] = paths_start[0] = path; - size_t curpath = 0; - size_t expansions = 0; - char *symlink; - size_t symlink_len; - - for (;;) { - // Extract the next pathname component from 'paths[curpath]', null - // terminate it and store it in 'file'. 'ends_with_slashes' stores - // whether the pathname component is followed by one or more - // trailing slashes, as this requires it to be a directory. - char *file = paths[curpath]; - char *file_end = file + strcspn(file, "/"); - paths[curpath] = file_end + strspn(file_end, "/"); - bool ends_with_slashes = *file_end == '/'; - *file_end = '\0'; - - // Test for empty pathname strings and absolute paths. - if (file == file_end) { - error = ends_with_slashes ? __WASI_ENOTCAPABLE : __WASI_ENOENT; - goto fail; - } - - if (strcmp(file, ".") == 0) { - // Skip component. - } else if (strcmp(file, "..") == 0) { - // Pop a directory off the stack. - if (curfd == 0) { - // Attempted to go to parent directory of the directory file - // descriptor. - error = __WASI_ENOTCAPABLE; - goto fail; - } - close(fds[curfd--]); - } else if (curpath > 0 || *paths[curpath] != '\0' || - (ends_with_slashes && !needs_final_component)) { - // A pathname component whose name we're not interested in that is - // followed by a slash or is followed by other pathname - // components. In other words, a pathname component that must be a - // directory. First attempt to obtain a directory file descriptor - // for it. - int newdir = -#ifdef O_SEARCH - openat(fds[curfd], file, O_SEARCH | O_DIRECTORY | O_NOFOLLOW); + // The implementation provides no mechanism to constrain lookups to a + // directory automatically. Emulate this logic by resolving the + // pathname manually. + + // Stack of directory file descriptors. Index 0 always corresponds + // with the directory provided to this function. Entering a directory + // causes a file descriptor to be pushed, while handling ".." entries + // causes an entry to be popped. Index 0 cannot be popped, as this + // would imply escaping the base directory. + os_file_handle fds[128]; + fds[0] = fo->file_handle; + size_t curfd = 0; + + // Stack of pathname strings used for symlink expansion. By using a + // stack, there is no need to concatenate any pathname strings while + // expanding symlinks. + char *paths[32]; + char *paths_start[32]; + paths[0] = paths_start[0] = path; + size_t curpath = 0; + size_t expansions = 0; + char *symlink = NULL; + size_t symlink_len; +#ifdef BH_PLATFORM_WINDOWS +#define PATH_SEPARATORS "/\\" #else - openat(fds[curfd], file, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); -#endif - if (newdir != -1) { - // Success. Push it onto the directory stack. - if (curfd + 1 == sizeof(fds) / sizeof(fds[0])) { - close(newdir); - error = __WASI_ENAMETOOLONG; - goto fail; +#define PATH_SEPARATORS "/" +#endif + + for (;;) { + // Extract the next pathname component from 'paths[curpath]', null + // terminate it and store it in 'file'. 'ends_with_slashes' stores + // whether the pathname component is followed by one or more + // trailing slashes, as this requires it to be a directory. + char *file = paths[curpath]; + char *file_end = file + strcspn(file, PATH_SEPARATORS); + paths[curpath] = file_end + strspn(file_end, PATH_SEPARATORS); + bool ends_with_slashes = + (*file_end != '\0' && strchr(PATH_SEPARATORS, *file_end)); + *file_end = '\0'; + + // Test for empty pathname strings and absolute paths. + if (file == file_end) { + error = ends_with_slashes ? __WASI_ENOTCAPABLE : __WASI_ENOENT; + goto fail; } - fds[++curfd] = newdir; - } else { - // Failed to open it. Attempt symlink expansion. - if (errno != ELOOP && errno != EMLINK && errno != ENOTDIR) { - error = convert_errno(errno); - goto fail; + + if (strcmp(file, ".") == 0) { + // Skip component. } - symlink = readlinkat_dup(fds[curfd], file, &symlink_len); - if (symlink != NULL) - goto push_symlink; + else if (strcmp(file, "..") == 0) { + // Pop a directory off the stack. + if (curfd == 0) { + // Attempted to go to parent directory of the directory file + // descriptor. + error = __WASI_ENOTCAPABLE; + goto fail; + } + error = os_close(fds[curfd--], false); - // readlink returns EINVAL if the path isn't a symlink. In that case, - // it's more informative to return ENOTDIR. - if (errno == EINVAL) - errno = ENOTDIR; + if (error != __WASI_ESUCCESS) + goto fail; + } + else if (curpath > 0 || *paths[curpath] != '\0' + || (ends_with_slashes && !needs_final_component)) { + // A pathname component whose name we're not interested in that is + // followed by a slash or is followed by other pathname + // components. In other words, a pathname component that must be a + // directory. First attempt to obtain a directory file descriptor + // for it. + os_file_handle newdir; + error = blocking_op_openat( + exec_env, fds[curfd], file, __WASI_O_DIRECTORY, 0, 0, + WASI_LIBC_ACCESS_MODE_READ_ONLY, &newdir); + if (error == __WASI_ESUCCESS) { + // Success. Push it onto the directory stack. + if (curfd + 1 == sizeof(fds) / sizeof(fds[0])) { + os_close(newdir, false); + error = __WASI_ENAMETOOLONG; + goto fail; + } + fds[++curfd] = newdir; + } + else { + // Failed to open it. Attempt symlink expansion. + if (error != __WASI_ELOOP && error != __WASI_EMLINK + && error != __WASI_ENOTDIR) { + goto fail; + } + error = + readlinkat_dup(fds[curfd], file, &symlink_len, &symlink); + + if (error == __WASI_ESUCCESS) { + bh_assert(symlink != NULL); + goto push_symlink; + } + + // readlink returns EINVAL if the path isn't a symlink. In that + // case, it's more informative to return ENOTDIR. + if (error == __WASI_EINVAL) + error = __WASI_ENOTDIR; + + goto fail; + } + } + else { + // The final pathname component. Depending on whether it ends with + // a slash or the symlink-follow flag is set, perform symlink + // expansion. + if (ends_with_slashes + || (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0) { + error = + readlinkat_dup(fds[curfd], file, &symlink_len, &symlink); + if (error == __WASI_ESUCCESS) { + bh_assert(symlink != NULL); + goto push_symlink; + } + if (error != __WASI_EINVAL && error != __WASI_ENOENT) { + goto fail; + } + } - error = convert_errno(errno); - goto fail; - } - } else { - // The final pathname component. Depending on whether it ends with - // a slash or the symlink-follow flag is set, perform symlink - // expansion. - if (ends_with_slashes || - (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0) { - symlink = readlinkat_dup(fds[curfd], file, &symlink_len); - if (symlink != NULL) - goto push_symlink; - if (errno != EINVAL && errno != ENOENT) { - error = convert_errno(errno); - goto fail; + // Not a symlink, meaning we're done. Return the filename, + // together with the directory containing this file. + // + // If the file was followed by a trailing slash, we must retain + // it, to ensure system calls properly return ENOTDIR. + // Unfortunately, this opens up a race condition, because this + // means that users of path_get() will perform symlink expansion a + // second time. There is nothing we can do to mitigate this, as + // far as I know. + if (ends_with_slashes) + *file_end = '/'; + pa->path = file; + pa->path_start = paths_start[0]; + goto success; + } + + if (*paths[curpath] == '\0') { + if (curpath == 0) { + // No further pathname components to process. We may end up here + // when called on paths like ".", "a/..", but also if the path + // had trailing slashes and the caller is not interested in the + // name of the pathname component. + wasm_runtime_free(paths_start[0]); + pa->path = "."; + pa->path_start = NULL; + goto success; + } + + // Finished expanding symlink. Continue processing along the + // original path. + wasm_runtime_free(paths_start[curpath--]); + } + continue; + + push_symlink: + // Prevent infinite loops by placing an upper limit on the number of + // symlink expansions. + if (++expansions == 128) { + wasm_runtime_free(symlink); + error = __WASI_ELOOP; + goto fail; + } + + if (*paths[curpath] == '\0') { + // The original path already finished processing. Replace it by + // this symlink entirely. + wasm_runtime_free(paths_start[curpath]); } - } - - // Not a symlink, meaning we're done. Return the filename, - // together with the directory containing this file. - // - // If the file was followed by a trailing slash, we must retain - // it, to ensure system calls properly return ENOTDIR. - // Unfortunately, this opens up a race condition, because this - // means that users of path_get() will perform symlink expansion a - // second time. There is nothing we can do to mitigate this, as - // far as I know. - if (ends_with_slashes) - *file_end = '/'; - pa->path = file; - pa->path_start = paths_start[0]; - goto success; - } - - if (*paths[curpath] == '\0') { - if (curpath == 0) { - // No further pathname components to process. We may end up here - // when called on paths like ".", "a/..", but also if the path - // had trailing slashes and the caller is not interested in the - // name of the pathname component. - bh_free(paths_start[0]); - pa->path = "."; - pa->path_start = NULL; - goto success; - } - - // Finished expanding symlink. Continue processing along the - // original path. - bh_free(paths_start[curpath--]); - } - continue; - - push_symlink: - // Prevent infinite loops by placing an upper limit on the number of - // symlink expansions. - if (++expansions == 128) { - bh_free(symlink); - error = __WASI_ELOOP; - goto fail; - } - - if (*paths[curpath] == '\0') { - // The original path already finished processing. Replace it by - // this symlink entirely. - bh_free(paths_start[curpath]); - } else if (curpath + 1 == sizeof(paths) / sizeof(paths[0])) { - // Too many nested symlinks. Stop processing. - bh_free(symlink); - error = __WASI_ELOOP; - goto fail; - } else { - // The original path still has components left. Retain the - // components that remain, so we can process them afterwards. - ++curpath; - } - - // Append a trailing slash to the symlink if the path leading up to - // it also contained one. Otherwise we would not throw ENOTDIR if - // the target is not a directory. - if (ends_with_slashes) - bh_strcat_s(symlink, (uint32)symlink_len, "/"); - paths[curpath] = paths_start[curpath] = symlink; - } + else if (curpath + 1 == sizeof(paths) / sizeof(paths[0])) { + // Too many nested symlinks. Stop processing. + wasm_runtime_free(symlink); + error = __WASI_ELOOP; + goto fail; + } + else { + // The original path still has components left. Retain the + // components that remain, so we can process them afterwards. + ++curpath; + } + + // Append a trailing slash to the symlink if the path leading up to + // it also contained one. Otherwise we would not throw ENOTDIR if + // the target is not a directory. + if (ends_with_slashes) + bh_strcat_s(symlink, (uint32)symlink_len, "/"); + paths[curpath] = paths_start[curpath] = symlink; + } success: - // Return the lease. Close all directories, except the one the caller - // needs to use. - for (size_t i = 1; i < curfd; ++i) - close(fds[i]); - pa->fd = fds[curfd]; - pa->follow = false; - pa->fd_object = fo; - return 0; + // Return the lease. Close all directories, except the one the caller + // needs to use. + for (size_t i = 1; i < curfd; ++i) + os_close(fds[i], false); + pa->fd = fds[curfd]; + pa->follow = false; + pa->fd_object = fo; + return 0; fail: - // Failure. Free all resources. - for (size_t i = 1; i <= curfd; ++i) - close(fds[i]); - for (size_t i = 0; i <= curpath; ++i) - bh_free(paths_start[i]); - fd_object_release(fo); - return error; + // Failure. Free all resources. + for (size_t i = 1; i <= curfd; ++i) + os_close(fds[i], false); + for (size_t i = 0; i <= curpath; ++i) + wasm_runtime_free(paths_start[i]); + fd_object_release(NULL, fo); + return error; #endif } -static __wasi_errno_t path_get_nofollow( - struct fd_table *curfds, - struct path_access *pa, - __wasi_fd_t fd, - const char *path, - size_t pathlen, - __wasi_rights_t rights_base, - __wasi_rights_t rights_inheriting, - bool needs_final_component -) TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) { - __wasi_lookupflags_t flags = 0; - return path_get(curfds, pa, fd, flags, path, pathlen, rights_base, rights_inheriting, - needs_final_component); -} - -static void path_put( - struct path_access *pa -) UNLOCKS(pa->fd_object->refcount) { - bh_free(pa->path_start); - if (fd_number(pa->fd_object) != pa->fd) - close(pa->fd); - fd_object_release(pa->fd_object); -} - -__wasi_errno_t wasmtime_ssp_path_create_directory( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t pathlen -) { - struct path_access pa; - __wasi_errno_t error = - path_get_nofollow(curfds, &pa, fd, path, pathlen, - __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0, true); - if (error != 0) - return error; +static __wasi_errno_t +path_get_nofollow(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct path_access *pa, __wasi_fd_t fd, const char *path, + size_t pathlen, __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, bool needs_final_component) + TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) +{ + __wasi_lookupflags_t flags = 0; + return path_get(exec_env, curfds, pa, fd, flags, path, pathlen, rights_base, + rights_inheriting, needs_final_component); +} + +static void +path_put(struct path_access *pa) UNLOCKS(pa->fd_object->refcount) +{ + if (pa->path_start) + wasm_runtime_free(pa->path_start); + /* Can't use `!=` operator when `os_file_handle` is a struct */ + if (!os_compare_file_handle(pa->fd_object->file_handle, pa->fd)) + os_close(pa->fd, false); + fd_object_release(NULL, pa->fd_object); +} + +__wasi_errno_t +wasmtime_ssp_path_create_directory(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + const char *path, size_t pathlen) +{ + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(exec_env, curfds, &pa, fd, path, pathlen, + __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0, true); + if (error != 0) + return error; + + error = os_mkdirat(pa.fd, pa.path); + path_put(&pa); - int ret = mkdirat(pa.fd, pa.path, 0777); - path_put(&pa); - if (ret < 0) - return convert_errno(errno); - return 0; + return error; } static bool @@ -1703,7 +1589,7 @@ validate_path(const char *path, struct fd_prestats *pt) char path_resolved[PATH_MAX], prestat_dir_resolved[PATH_MAX]; char *path_real, *prestat_dir_real; - if (!(path_real = realpath(path, path_resolved))) + if (!(path_real = os_realpath(path, path_resolved))) /* path doesn't exist, creating a link to this file is allowed: if this file is to be created in the future, WASI will strictly check whether it @@ -1712,8 +1598,8 @@ validate_path(const char *path, struct fd_prestats *pt) for (i = 0; i < pt->size; i++) { if (pt->prestats[i].dir) { - if (!(prestat_dir_real = realpath(pt->prestats[i].dir, - prestat_dir_resolved))) + if (!(prestat_dir_real = + os_realpath(pt->prestats[i].dir, prestat_dir_resolved))) return false; if (!strncmp(path_real, prestat_dir_real, strlen(prestat_dir_real))) return true; @@ -1723,1226 +1609,1837 @@ validate_path(const char *path, struct fd_prestats *pt) return false; } -__wasi_errno_t wasmtime_ssp_path_link( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - __wasi_fd_t old_fd, - __wasi_lookupflags_t old_flags, - const char *old_path, - size_t old_path_len, - __wasi_fd_t new_fd, - const char *new_path, - size_t new_path_len -) { - struct path_access old_pa; - __wasi_errno_t error = path_get(curfds, &old_pa, old_fd, old_flags, old_path, old_path_len, - __WASI_RIGHT_PATH_LINK_SOURCE, 0, false); - if (error != 0) - return error; - - struct path_access new_pa; - error = path_get_nofollow(curfds, &new_pa, new_fd, new_path, new_path_len, - __WASI_RIGHT_PATH_LINK_TARGET, 0, true); - if (error != 0) { - path_put(&old_pa); - return error; - } - - rwlock_rdlock(&prestats->lock); - if (!validate_path(old_pa.path, prestats) - || !validate_path(new_pa.path, prestats)) { - rwlock_unlock(&prestats->lock); - return __WASI_EBADF; - } - rwlock_unlock(&prestats->lock); - - int ret = linkat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path, - old_pa.follow ? AT_SYMLINK_FOLLOW : 0); - if (ret < 0 && errno == ENOTSUP && !old_pa.follow) { - // OS X doesn't allow creating hardlinks to symbolic links. - // Duplicate the symbolic link instead. - size_t target_len; - char *target = readlinkat_dup(old_pa.fd, old_pa.path, &target_len); - if (target != NULL) { - bh_assert(target[target_len] == '\0'); - rwlock_rdlock(&prestats->lock); - if (!validate_path(target, prestats)) { - rwlock_unlock(&prestats->lock); - bh_free(target); - return __WASI_EBADF; - } - rwlock_unlock(&prestats->lock); - ret = symlinkat(target, new_pa.fd, new_pa.path); - bh_free(target); - } - } - path_put(&old_pa); - path_put(&new_pa); - if (ret < 0) - return convert_errno(errno); - return 0; -} - -__wasi_errno_t wasmtime_ssp_path_open( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t dirfd, - __wasi_lookupflags_t dirflags, - const char *path, - size_t pathlen, - __wasi_oflags_t oflags, - __wasi_rights_t fs_rights_base, - __wasi_rights_t fs_rights_inheriting, - __wasi_fdflags_t fs_flags, - __wasi_fd_t *fd -) { - // Rights that should be installed on the new file descriptor. - __wasi_rights_t rights_base = fs_rights_base; - __wasi_rights_t rights_inheriting = fs_rights_inheriting; - - // Which open() mode should be used to satisfy the needed rights. - bool read = - (rights_base & (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_READDIR)) != 0; - bool write = - (rights_base & (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_WRITE | - __WASI_RIGHT_FD_ALLOCATE | - __WASI_RIGHT_FD_FILESTAT_SET_SIZE)) != 0; - int noflags = write ? read ? O_RDWR : O_WRONLY : O_RDONLY; - - // Which rights are needed on the directory file descriptor. - __wasi_rights_t needed_base = __WASI_RIGHT_PATH_OPEN; - __wasi_rights_t needed_inheriting = rights_base | rights_inheriting; - - // Convert open flags. - if ((oflags & __WASI_O_CREAT) != 0) { - noflags |= O_CREAT; - needed_base |= __WASI_RIGHT_PATH_CREATE_FILE; - } - if ((oflags & __WASI_O_DIRECTORY) != 0) - noflags |= O_DIRECTORY; - if ((oflags & __WASI_O_EXCL) != 0) - noflags |= O_EXCL; - if ((oflags & __WASI_O_TRUNC) != 0) { - noflags |= O_TRUNC; - needed_base |= __WASI_RIGHT_PATH_FILESTAT_SET_SIZE; - } - - // Convert file descriptor flags. - if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) - noflags |= O_APPEND; - if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { -#ifdef O_DSYNC - noflags |= O_DSYNC; -#else - noflags |= O_SYNC; -#endif - needed_inheriting |= __WASI_RIGHT_FD_DATASYNC; - } - if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) - noflags |= O_NONBLOCK; - if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { -#ifdef O_RSYNC - noflags |= O_RSYNC; -#else - noflags |= O_SYNC; -#endif - needed_inheriting |= __WASI_RIGHT_FD_SYNC; - } - if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { - noflags |= O_SYNC; - needed_inheriting |= __WASI_RIGHT_FD_SYNC; - } - if (write && (noflags & (O_APPEND | O_TRUNC)) == 0) - needed_inheriting |= __WASI_RIGHT_FD_SEEK; - - struct path_access pa; - __wasi_errno_t error = - path_get(curfds, &pa, dirfd, dirflags, path, pathlen, needed_base, needed_inheriting, - (oflags & __WASI_O_CREAT) != 0); - if (error != 0) - return error; - if (!pa.follow) - noflags |= O_NOFOLLOW; - - int nfd = openat(pa.fd, pa.path, noflags, 0666); - if (nfd < 0) { - int openat_errno = errno; - // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket. - if (openat_errno == ENXIO) { - struct stat sb; - int ret = - fstatat(pa.fd, pa.path, &sb, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); - path_put(&pa); - return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP - : __WASI_ENXIO; - } - // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY - // on a symlink. - if (openat_errno == ENOTDIR && (noflags & (O_NOFOLLOW | O_DIRECTORY)) != 0) { - struct stat sb; - int ret = fstatat(pa.fd, pa.path, &sb, AT_SYMLINK_NOFOLLOW); - if (S_ISLNK(sb.st_mode)) { - path_put(&pa); - return __WASI_ELOOP; - } - (void)ret; +__wasi_errno_t +wasmtime_ssp_path_link(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, __wasi_fd_t old_fd, + __wasi_lookupflags_t old_flags, const char *old_path, + size_t old_path_len, __wasi_fd_t new_fd, + const char *new_path, size_t new_path_len) +{ + struct path_access old_pa; + __wasi_errno_t error = + path_get(exec_env, curfds, &old_pa, old_fd, old_flags, old_path, + old_path_len, __WASI_RIGHT_PATH_LINK_SOURCE, 0, false); + if (error != 0) + return error; + + struct path_access new_pa; + error = + path_get_nofollow(exec_env, curfds, &new_pa, new_fd, new_path, + new_path_len, __WASI_RIGHT_PATH_LINK_TARGET, 0, true); + if (error != 0) { + path_put(&old_pa); + return error; } - path_put(&pa); - // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on - // a symlink. - if (!pa.follow && openat_errno == EMLINK) - return __WASI_ELOOP; - return convert_errno(openat_errno); - } - path_put(&pa); - - // Determine the type of the new file descriptor and which rights - // contradict with this type. - __wasi_filetype_t type; - __wasi_rights_t max_base, max_inheriting; - error = fd_determine_type_rights(nfd, &type, &max_base, &max_inheriting); - if (error != 0) { - close(nfd); - return error; - } - return fd_table_insert_fd(curfds, nfd, type, rights_base & max_base, - rights_inheriting & max_inheriting, fd); -} -// Copies out directory entry metadata or filename, potentially -// truncating it in the process. -static void fd_readdir_put( - void *buf, - size_t bufsize, - size_t *bufused, - const void *elem, - size_t elemsize -) { - size_t bufavail = bufsize - *bufused; - if (elemsize > bufavail) - elemsize = bufavail; - bh_memcpy_s((char *)buf + *bufused, (uint32)bufavail, elem, (uint32)elemsize); - *bufused += elemsize; -} - -__wasi_errno_t wasmtime_ssp_fd_readdir( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - void *buf, - size_t nbyte, - __wasi_dircookie_t cookie, - size_t *bufused -) { - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READDIR, 0); - if (error != 0) { - return error; - } - - // Create a directory handle if none has been opened yet. - mutex_lock(&fo->directory.lock); - DIR *dp = fo->directory.handle; - if (dp == NULL) { - dp = fdopendir(fd_number(fo)); - if (dp == NULL) { - mutex_unlock(&fo->directory.lock); - fd_object_release(fo); - return convert_errno(errno); - } - fo->directory.handle = dp; - fo->directory.offset = __WASI_DIRCOOKIE_START; - } - - // Seek to the right position if the requested offset does not match - // the current offset. - if (fo->directory.offset != cookie) { - if (cookie == __WASI_DIRCOOKIE_START) - rewinddir(dp); - else - seekdir(dp, (long)cookie); - fo->directory.offset = cookie; - } - - *bufused = 0; - while (*bufused < nbyte) { - // Read the next directory entry. - errno = 0; - struct dirent *de = readdir(dp); - if (de == NULL) { - mutex_unlock(&fo->directory.lock); - fd_object_release(fo); - return errno == 0 || *bufused > 0 ? 0 : convert_errno(errno); - } - fo->directory.offset = (__wasi_dircookie_t)telldir(dp); - - // Craft a directory entry and copy that back. - size_t namlen = strlen(de->d_name); - __wasi_dirent_t cde = { - .d_next = fo->directory.offset, - .d_ino = de->d_ino, - .d_namlen = (uint32)namlen, - }; - switch (de->d_type) { - case DT_BLK: - cde.d_type = __WASI_FILETYPE_BLOCK_DEVICE; - break; - case DT_CHR: - cde.d_type = __WASI_FILETYPE_CHARACTER_DEVICE; - break; - case DT_DIR: - cde.d_type = __WASI_FILETYPE_DIRECTORY; - break; - case DT_FIFO: - cde.d_type = __WASI_FILETYPE_SOCKET_STREAM; - break; - case DT_LNK: - cde.d_type = __WASI_FILETYPE_SYMBOLIC_LINK; - break; - case DT_REG: - cde.d_type = __WASI_FILETYPE_REGULAR_FILE; - break; -#ifdef DT_SOCK - case DT_SOCK: - // Technically not correct, but good enough. - cde.d_type = __WASI_FILETYPE_SOCKET_STREAM; - break; -#endif - default: - cde.d_type = __WASI_FILETYPE_UNKNOWN; - break; + rwlock_rdlock(&prestats->lock); + if (!validate_path(old_pa.path, prestats) + || !validate_path(new_pa.path, prestats)) { + rwlock_unlock(&prestats->lock); + return __WASI_EBADF; } - fd_readdir_put(buf, nbyte, bufused, &cde, sizeof(cde)); - fd_readdir_put(buf, nbyte, bufused, de->d_name, namlen); - } - mutex_unlock(&fo->directory.lock); - fd_object_release(fo); - return 0; -} - -__wasi_errno_t wasmtime_ssp_path_readlink( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t pathlen, - char *buf, - size_t bufsize, - size_t *bufused -) { - struct path_access pa; - __wasi_errno_t error = path_get_nofollow(curfds, - &pa, fd, path, pathlen, __WASI_RIGHT_PATH_READLINK, 0, false); - if (error != 0) - return error; + rwlock_unlock(&prestats->lock); - // Linux requires that the buffer size is positive. whereas POSIX does - // not. Use a fake buffer to store the results if the size is zero. - char fakebuf[1]; - ssize_t len = readlinkat(pa.fd, pa.path, bufsize == 0 ? fakebuf : buf, - bufsize == 0 ? sizeof(fakebuf) : bufsize); - path_put(&pa); - if (len < 0) - return convert_errno(errno); - *bufused = (size_t)len < bufsize ? (size_t)len : bufsize; - return 0; -} - -__wasi_errno_t wasmtime_ssp_path_rename( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, + error = os_linkat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path, + old_pa.follow ? __WASI_LOOKUP_SYMLINK_FOLLOW : 0); + +#if defined(__APPLE__) + if (error == __WASI_ENOTSUP && !old_pa.follow) { + // OS X doesn't allow creating hardlinks to symbolic links. + // Duplicate the symbolic link instead. + size_t target_len; + char *target = NULL; + error = readlinkat_dup(old_pa.fd, old_pa.path, &target_len, &target); + if (error == __WASI_ESUCCESS) { + bh_assert(target != NULL); + bh_assert(target[target_len] == '\0'); + rwlock_rdlock(&prestats->lock); + if (!validate_path(target, prestats)) { + rwlock_unlock(&prestats->lock); + wasm_runtime_free(target); + return __WASI_EBADF; + } + rwlock_unlock(&prestats->lock); + error = os_symlinkat(target, new_pa.fd, new_pa.path); + wasm_runtime_free(target); + } + } #endif - __wasi_fd_t old_fd, - const char *old_path, - size_t old_path_len, - __wasi_fd_t new_fd, - const char *new_path, - size_t new_path_len -) { - struct path_access old_pa; - __wasi_errno_t error = path_get_nofollow(curfds, &old_pa, old_fd, old_path, old_path_len, - __WASI_RIGHT_PATH_RENAME_SOURCE, 0, true); - if (error != 0) - return error; - struct path_access new_pa; - error = path_get_nofollow(curfds, &new_pa, new_fd, new_path, new_path_len, - __WASI_RIGHT_PATH_RENAME_TARGET, 0, true); - if (error != 0) { path_put(&old_pa); - return error; - } - - int ret = renameat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path); - path_put(&old_pa); - path_put(&new_pa); - if (ret < 0) { - return convert_errno(errno); - } - return 0; -} - -// Converts a POSIX stat structure to a CloudABI filestat structure. -static void convert_stat( - const struct stat *in, - __wasi_filestat_t *out -) { - *out = (__wasi_filestat_t){ - .st_dev = in->st_dev, - .st_ino = in->st_ino, - .st_nlink = (__wasi_linkcount_t)in->st_nlink, - .st_size = (__wasi_filesize_t)in->st_size, - .st_atim = convert_timespec(&in->st_atim), - .st_mtim = convert_timespec(&in->st_mtim), - .st_ctim = convert_timespec(&in->st_ctim), - }; -} - -__wasi_errno_t wasmtime_ssp_fd_filestat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filestat_t *buf -) { - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_GET, 0); - if (error != 0) - return error; + path_put(&new_pa); - int ret; - switch (fo->type) { - default: { - struct stat sb; - ret = fstat(fd_number(fo), &sb); - convert_stat(&sb, buf); - break; - } - } - buf->st_filetype = fo->type; - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; -} - -static void convert_timestamp( - __wasi_timestamp_t in, - struct timespec *out -) { - // Store sub-second remainder. - out->tv_nsec = (__syscall_slong_t)(in % 1000000000); - 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); -} - -// Converts the provided timestamps and flags to a set of arguments for -// futimens() and utimensat(). -static void convert_utimens_arguments( - __wasi_timestamp_t st_atim, - __wasi_timestamp_t st_mtim, - __wasi_fstflags_t fstflags, - struct timespec *ts -) { - if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { - ts[0].tv_nsec = UTIME_NOW; - } else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { - convert_timestamp(st_atim, &ts[0]); - } else { - ts[0].tv_nsec = UTIME_OMIT; - } - - if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { - ts[1].tv_nsec = UTIME_NOW; - } else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { - convert_timestamp(st_mtim, &ts[1]); - } else { - ts[1].tv_nsec = UTIME_OMIT; - } -} - -__wasi_errno_t wasmtime_ssp_fd_filestat_set_size( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_filesize_t st_size -) { - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_SIZE, 0); - if (error != 0) return error; - - int ret = ftruncate(fd_number(fo), (off_t)st_size); - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; } -__wasi_errno_t wasmtime_ssp_fd_filestat_set_times( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_timestamp_t st_atim, - __wasi_timestamp_t st_mtim, - __wasi_fstflags_t fstflags -) { - if ((fstflags & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW | - __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) != 0) - return __WASI_EINVAL; - - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_TIMES, 0); - if (error != 0) - return error; +__wasi_errno_t +wasmtime_ssp_path_open(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t dirfd, __wasi_lookupflags_t dirflags, + const char *path, size_t pathlen, __wasi_oflags_t oflags, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting, + __wasi_fdflags_t fs_flags, __wasi_fd_t *fd) +{ + // Rights that should be installed on the new file descriptor. + __wasi_rights_t rights_base = fs_rights_base; + __wasi_rights_t rights_inheriting = fs_rights_inheriting; + + // Which open() mode should be used to satisfy the needed rights. + bool read = + (rights_base & (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_READDIR)) != 0; + bool write = + (rights_base + & (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_WRITE + | __WASI_RIGHT_FD_ALLOCATE | __WASI_RIGHT_FD_FILESTAT_SET_SIZE)) + != 0; + + wasi_libc_file_access_mode access_mode = + write ? read ? WASI_LIBC_ACCESS_MODE_READ_WRITE + : WASI_LIBC_ACCESS_MODE_WRITE_ONLY + : WASI_LIBC_ACCESS_MODE_READ_ONLY; + + // Which rights are needed on the directory file descriptor. + __wasi_rights_t needed_base = __WASI_RIGHT_PATH_OPEN; + __wasi_rights_t needed_inheriting = rights_base | rights_inheriting; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + needed_base |= __WASI_RIGHT_PATH_CREATE_FILE; + } + if ((oflags & __WASI_O_TRUNC) != 0) { + needed_base |= __WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } - struct timespec ts[2]; - convert_utimens_arguments(st_atim, st_mtim, fstflags, ts); - int ret = futimens(fd_number(fo), ts); + // Convert file descriptor flags. + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { + needed_inheriting |= __WASI_RIGHT_FD_SYNC; + } + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { + needed_inheriting |= __WASI_RIGHT_FD_SYNC; + } + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { + needed_inheriting |= __WASI_RIGHT_FD_DATASYNC; + } - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; -} + if (write + && !((fs_flags & __WASI_FDFLAG_APPEND) || (__WASI_O_TRUNC & oflags))) + needed_inheriting |= __WASI_RIGHT_FD_SEEK; -__wasi_errno_t wasmtime_ssp_path_filestat_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_lookupflags_t flags, - const char *path, - size_t pathlen, - __wasi_filestat_t *buf -) { - struct path_access pa; - __wasi_errno_t error = - path_get(curfds, &pa, fd, flags, path, pathlen, __WASI_RIGHT_PATH_FILESTAT_GET, 0, false); - if (error != 0) - return error; + struct path_access pa; + __wasi_errno_t error = path_get( + exec_env, curfds, &pa, dirfd, dirflags, path, pathlen, needed_base, + needed_inheriting, (oflags & __WASI_O_CREAT) != 0); - struct stat sb; - int ret = fstatat(pa.fd, pa.path, &sb, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); - path_put(&pa); - if (ret < 0) - return convert_errno(errno); - convert_stat(&sb, buf); - - // Convert the file type. In the case of sockets there is no way we - // can easily determine the exact socket type. - if (S_ISBLK(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; - else if (S_ISCHR(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; - else if (S_ISDIR(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_DIRECTORY; - else if (S_ISFIFO(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; - else if (S_ISLNK(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; - else if (S_ISREG(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_REGULAR_FILE; - else if (S_ISSOCK(sb.st_mode)) - buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; - return 0; -} - -__wasi_errno_t wasmtime_ssp_path_filestat_set_times( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - __wasi_lookupflags_t flags, - const char *path, - size_t pathlen, - __wasi_timestamp_t st_atim, - __wasi_timestamp_t st_mtim, - __wasi_fstflags_t fstflags -) { - if ((fstflags & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW | - __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) != 0) - return __WASI_EINVAL; - - struct path_access pa; - __wasi_errno_t error = path_get(curfds, - &pa, fd, flags, path, pathlen, __WASI_RIGHT_PATH_FILESTAT_SET_TIMES, 0, false); - if (error != 0) - return error; + if (error != 0) + return error; - struct timespec ts[2]; - convert_utimens_arguments(st_atim, st_mtim, fstflags, ts); - int ret = utimensat(pa.fd, pa.path, ts, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + os_file_handle handle; + error = blocking_op_openat(exec_env, pa.fd, pa.path, oflags, fs_flags, + dirflags, access_mode, &handle); - path_put(&pa); - if (ret < 0) - return convert_errno(errno); - return 0; -} + path_put(&pa); -__wasi_errno_t wasmtime_ssp_path_symlink( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, - struct fd_prestats *prestats, -#endif - const char *old_path, - size_t old_path_len, - __wasi_fd_t fd, - const char *new_path, - size_t new_path_len -) { - char *target = str_nullterminate(old_path, old_path_len); - if (target == NULL) - return convert_errno(errno); - - struct path_access pa; - __wasi_errno_t error = path_get_nofollow(curfds, - &pa, fd, new_path, new_path_len, __WASI_RIGHT_PATH_SYMLINK, 0, true); - if (error != 0) { - bh_free(target); - return error; - } - - rwlock_rdlock(&prestats->lock); - if (!validate_path(target, prestats)) { - rwlock_unlock(&prestats->lock); - bh_free(target); - return __WASI_EBADF; - } - rwlock_unlock(&prestats->lock); - - int ret = symlinkat(target, pa.fd, pa.path); - path_put(&pa); - bh_free(target); - if (ret < 0) - return convert_errno(errno); - return 0; -} - -__wasi_errno_t wasmtime_ssp_path_unlink_file( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t pathlen -) { - struct path_access pa; - __wasi_errno_t error = path_get_nofollow(curfds, - &pa, fd, path, pathlen, __WASI_RIGHT_PATH_UNLINK_FILE, 0, true); - if (error != 0) - return error; + if (error != __WASI_ESUCCESS) + return error; - int ret = unlinkat(pa.fd, pa.path, 0); -#ifndef __linux__ - // Non-Linux implementations may return EPERM when attempting to remove a - // directory without REMOVEDIR. While that's what POSIX specifies, it's - // less useful. Adjust this to EISDIR. It doesn't matter that this is not - // atomic with the unlinkat, because if the file is removed and a directory - // is created before fstatat sees it, we're racing with that change anyway - // and unlinkat could have legitimately seen the directory if the race had - // turned out differently. - if (ret < 0 && errno == EPERM) { - struct stat statbuf; - if (fstatat(pa.fd, pa.path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0 && - S_ISDIR(statbuf.st_mode)) { - errno = EISDIR; - } - } -#endif - path_put(&pa); - if (ret < 0) { - return convert_errno(errno); - } - return 0; -} + // Determine the type of the new file descriptor and which rights + // contradict with this type. + __wasi_filetype_t type; + __wasi_rights_t max_base, max_inheriting; -__wasi_errno_t wasmtime_ssp_path_remove_directory( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t fd, - const char *path, - size_t pathlen -) { - struct path_access pa; - __wasi_errno_t error = path_get_nofollow(curfds, - &pa, fd, path, pathlen, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0, true); - if (error != 0) - return error; + error = fd_determine_type_rights(handle, &type, &max_base, &max_inheriting); - int ret = unlinkat(pa.fd, pa.path, AT_REMOVEDIR); -#ifndef __linux__ - // POSIX permits either EEXIST or ENOTEMPTY when the directory is not empty. - // Map it to ENOTEMPTY. - if (ret < 0 && errno == EEXIST) { - errno = ENOTEMPTY; - } -#endif - path_put(&pa); - if (ret < 0) { - return convert_errno(errno); - } - return 0; + if (error != __WASI_ESUCCESS) { + os_close(handle, false); + return error; + } + + return fd_table_insert_fd(exec_env, curfds, handle, type, + rights_base & max_base, + rights_inheriting & max_inheriting, fd); } -__wasi_errno_t wasmtime_ssp_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 -) NO_LOCK_ANALYSIS { - // Sleeping. - if (nsubscriptions == 1 && in[0].type == __WASI_EVENTTYPE_CLOCK) { - out[0] = (__wasi_event_t){ - .userdata = in[0].userdata, - .type = in[0].type, - }; -#if CONFIG_HAS_CLOCK_NANOSLEEP - clockid_t clock_id; - if (convert_clockid(in[0].u.clock.clock_id, &clock_id)) { - struct timespec ts; - convert_timestamp(in[0].u.clock.timeout, &ts); - int ret = clock_nanosleep( - clock_id, - (in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 - ? TIMER_ABSTIME - : 0, - &ts, NULL); - if (ret != 0) - out[0].error = convert_errno(ret); - } else { - out[0].error = __WASI_ENOTSUP; +// Copies out directory entry metadata or filename, potentially +// truncating it in the process. +static void +fd_readdir_put(void *buf, size_t bufsize, size_t *bufused, const void *elem, + size_t elemsize) +{ + size_t bufavail = bufsize - *bufused; + if (elemsize > bufavail) + elemsize = bufavail; + bh_memcpy_s((char *)buf + *bufused, (uint32)bufavail, elem, + (uint32)elemsize); + *bufused += elemsize; +} + +__wasi_errno_t +wasmtime_ssp_fd_readdir(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, void *buf, size_t nbyte, + __wasi_dircookie_t cookie, size_t *bufused) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READDIR, 0); + if (error != 0) { + return error; } -#else - switch (in[0].u.clock.clock_id) { - case __WASI_CLOCK_MONOTONIC: - if ((in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0) { - // TODO(ed): Implement. - fputs("Unimplemented absolute sleep on monotonic clock\n", stderr); - out[0].error = __WASI_ENOSYS; - } else { - // Perform relative sleeps on the monotonic clock also using - // nanosleep(). This is incorrect, but good enough for now. - struct timespec ts; - convert_timestamp(in[0].u.clock.timeout, &ts); - nanosleep(&ts, NULL); - } - break; - case __WASI_CLOCK_REALTIME: - if ((in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0) { - // Sleeping to an absolute point in time can only be done - // by waiting on a condition variable. - struct mutex mutex; - mutex_init(&mutex); - struct cond cond; - cond_init_realtime(&cond); - mutex_lock(&mutex); - cond_timedwait(&cond, &mutex, in[0].u.clock.timeout, true); - mutex_unlock(&mutex); - mutex_destroy(&mutex); - cond_destroy(&cond); - } else { - // Relative sleeps can be done using nanosleep(). - struct timespec ts; - convert_timestamp(in[0].u.clock.timeout, &ts); - nanosleep(&ts, NULL); + + // Create a directory handle if none has been opened yet. + mutex_lock(&fo->directory.lock); + if (!os_is_dir_stream_valid(&fo->directory.handle)) { + error = os_fdopendir(fo->file_handle, &fo->directory.handle); + if (error != __WASI_ESUCCESS) { + mutex_unlock(&fo->directory.lock); + fd_object_release(exec_env, fo); + return error; } - break; - default: - out[0].error = __WASI_ENOTSUP; - break; + fo->directory.offset = __WASI_DIRCOOKIE_START; } -#endif - *nevents = 1; - return 0; - } - - // Last option: call into poll(). This can only be done in case all - // subscriptions consist of __WASI_EVENTTYPE_FD_READ and - // __WASI_EVENTTYPE_FD_WRITE entries. There may be up to one - // __WASI_EVENTTYPE_CLOCK entry to act as a timeout. These are also - // the subscriptions generate by cloudlibc's poll() and select(). - struct fd_object **fos = bh_malloc((uint32)(nsubscriptions * sizeof(*fos))); - if (fos == NULL) - return __WASI_ENOMEM; - struct pollfd *pfds = bh_malloc((uint32)(nsubscriptions * sizeof(*pfds))); - if (pfds == NULL) { - bh_free(fos); - return __WASI_ENOMEM; - } - - // Convert subscriptions to pollfd entries. Increase the reference - // count on the file descriptors to ensure they remain valid across - // the call to poll(). - struct fd_table *ft = curfds; - rwlock_rdlock(&ft->lock); - *nevents = 0; - const __wasi_subscription_t *clock_subscription = NULL; - for (size_t i = 0; i < nsubscriptions; ++i) { - const __wasi_subscription_t *s = &in[i]; - switch (s->type) { - case __WASI_EVENTTYPE_FD_READ: - case __WASI_EVENTTYPE_FD_WRITE: { - __wasi_errno_t error = - fd_object_get_locked(&fos[i], ft, s->u.fd_readwrite.fd, - __WASI_RIGHT_POLL_FD_READWRITE, 0); - if (error == 0) { - // Proper file descriptor on which we can poll(). - pfds[i] = (struct pollfd){ - .fd = fd_number(fos[i]), - .events = s->type == __WASI_EVENTTYPE_FD_READ ? POLLRDNORM - : POLLWRNORM, - }; - } else { - // Invalid file descriptor or rights missing. - fos[i] = NULL; - pfds[i] = (struct pollfd){.fd = -1}; - out[(*nevents)++] = (__wasi_event_t){ - .userdata = s->userdata, - .error = error, - .type = s->type, - }; - } - break; - } - case __WASI_EVENTTYPE_CLOCK: - if (clock_subscription == NULL && - (s->u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) == 0) { - // Relative timeout. - fos[i] = NULL; - pfds[i] = (struct pollfd){.fd = -1}; - clock_subscription = s; - break; - } - // Fallthrough. - default: - // Unsupported event. - fos[i] = NULL; - pfds[i] = (struct pollfd){.fd = -1}; - out[(*nevents)++] = (__wasi_event_t){ - .userdata = s->userdata, - .error = __WASI_ENOSYS, - .type = s->type, - }; - break; - } - } - rwlock_unlock(&ft->lock); - - // Use a zero-second timeout in case we've already generated events in - // the loop above. - int timeout; - if (*nevents != 0) { - timeout = 0; - } else if (clock_subscription != NULL) { - __wasi_timestamp_t ts = clock_subscription->u.clock.timeout / 1000000; - timeout = ts > INT_MAX ? -1 : (int)ts; - } else { - timeout = -1; - } - int ret = poll(pfds, nsubscriptions, timeout); - - __wasi_errno_t error = 0; - if (ret == -1) { - error = convert_errno(errno); - } else if (ret == 0 && *nevents == 0 && clock_subscription != NULL) { - // No events triggered. Trigger the clock event. - out[(*nevents)++] = (__wasi_event_t){ - .userdata = clock_subscription->userdata, - .type = __WASI_EVENTTYPE_CLOCK, - }; - } else { - // Events got triggered. Don't trigger the clock event. - for (size_t i = 0; i < nsubscriptions; ++i) { - if (pfds[i].fd >= 0) { - __wasi_filesize_t nbytes = 0; - if (in[i].type == __WASI_EVENTTYPE_FD_READ) { - int l; - if (ioctl(fd_number(fos[i]), FIONREAD, &l) == 0) - nbytes = (__wasi_filesize_t)l; - } - if ((pfds[i].revents & POLLNVAL) != 0) { - // Bad file descriptor. This normally cannot occur, as - // referencing the file descriptor object will always ensure - // the descriptor is valid. Still, macOS may sometimes return - // this on FIFOs when reaching end-of-file. - out[(*nevents)++] = (__wasi_event_t){ - .userdata = in[i].userdata, -#ifdef __APPLE__ - .u.fd_readwrite.nbytes = nbytes, - .u.fd_readwrite.flags = __WASI_EVENT_FD_READWRITE_HANGUP, -#else - .error = __WASI_EBADF, -#endif - .type = in[i].type, - }; - } else if ((pfds[i].revents & POLLERR) != 0) { - // File descriptor is in an error state. - out[(*nevents)++] = (__wasi_event_t){ - .userdata = in[i].userdata, - .error = __WASI_EIO, - .type = in[i].type, - }; - } else if ((pfds[i].revents & POLLHUP) != 0) { - // End-of-file. - out[(*nevents)++] = (__wasi_event_t){ - .userdata = in[i].userdata, - .type = in[i].type, - .u.fd_readwrite.nbytes = nbytes, - .u.fd_readwrite.flags = __WASI_EVENT_FD_READWRITE_HANGUP, - }; - } else if ((pfds[i].revents & (POLLRDNORM | POLLWRNORM)) != 0) { - // Read or write possible. - out[(*nevents)++] = (__wasi_event_t){ - .userdata = in[i].userdata, - .type = in[i].type, - .u.fd_readwrite.nbytes = nbytes, - }; - } - } - } - } - - for (size_t i = 0; i < nsubscriptions; ++i) - if (fos[i] != NULL) - fd_object_release(fos[i]); - bh_free(fos); - bh_free(pfds); - return error; -} - -void wasmtime_ssp_proc_exit( - __wasi_exitcode_t rval -) { - _Exit((int32)rval); -} - -__wasi_errno_t wasmtime_ssp_proc_raise( - __wasi_signal_t sig -) { - static const int signals[] = { -#define X(v) [__WASI_##v] = v - X(SIGABRT), X(SIGALRM), X(SIGBUS), X(SIGCHLD), X(SIGCONT), X(SIGFPE), - X(SIGHUP), X(SIGILL), X(SIGINT), X(SIGKILL), X(SIGPIPE), X(SIGQUIT), - X(SIGSEGV), X(SIGSTOP), X(SIGSYS), X(SIGTERM), X(SIGTRAP), X(SIGTSTP), - X(SIGTTIN), X(SIGTTOU), X(SIGURG), X(SIGUSR1), X(SIGUSR2), X(SIGVTALRM), - X(SIGXCPU), X(SIGXFSZ), -#undef X - }; - if (sig >= sizeof(signals) / sizeof(signals[0]) || signals[sig] == 0) - return __WASI_EINVAL; - -#if CONFIG_TLS_USE_GSBASE - // TLS on OS X depends on installing a SIGSEGV handler. Reset SIGSEGV - // to the default action before raising. - if (sig == __WASI_SIGSEGV) { - struct sigaction sa = { - .sa_handler = SIG_DFL, - }; - sigemptyset(&sa.sa_mask); - sigaction(SIGSEGV, &sa, NULL); - } -#endif - if (raise(signals[sig]) < 0) - return convert_errno(errno); - return 0; -} + // Seek to the right position if the requested offset does not match + // the current offset. + if (fo->directory.offset != cookie) { + if (cookie == __WASI_DIRCOOKIE_START) + os_rewinddir(fo->directory.handle); + else + os_seekdir(fo->directory.handle, cookie); + fo->directory.offset = cookie; + } -__wasi_errno_t wasmtime_ssp_random_get( - void *buf, - size_t nbyte -) { - random_buf(buf, nbyte); - return 0; -} + *bufused = 0; + while (*bufused < nbyte) { + // Read the next directory entry. + __wasi_dirent_t cde; + const char *d_name = NULL; -__wasi_errno_t wasmtime_ssp_sock_recv( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t sock, - const __wasi_iovec_t *ri_data, - size_t ri_data_len, - __wasi_riflags_t ri_flags, - size_t *ro_datalen, - __wasi_roflags_t *ro_flags -) { - // Convert input to msghdr. - struct msghdr hdr = { - .msg_iov = (struct iovec *)ri_data, - .msg_iovlen = ri_data_len, - }; - int nflags = 0; - if ((ri_flags & __WASI_SOCK_RECV_PEEK) != 0) - nflags |= MSG_PEEK; - if ((ri_flags & __WASI_SOCK_RECV_WAITALL) != 0) - nflags |= MSG_WAITALL; - - struct fd_object *fo; - __wasi_errno_t error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0); - if (error != 0) { - return error; - } + error = os_readdir(fo->directory.handle, &cde, &d_name); + if (d_name == NULL) { + mutex_unlock(&fo->directory.lock); + fd_object_release(exec_env, fo); - ssize_t datalen = recvmsg(fd_number(fo), &hdr, nflags); - fd_object_release(fo); - if (datalen < 0) { - return convert_errno(errno); - } + return *bufused > 0 ? __WASI_ESUCCESS : error; + } + fo->directory.offset = cde.d_next; - // Convert msghdr to output. - *ro_datalen = (size_t)datalen; - *ro_flags = 0; - if ((hdr.msg_flags & MSG_TRUNC) != 0) - *ro_flags |= __WASI_SOCK_RECV_DATA_TRUNCATED; - return 0; + fd_readdir_put(buf, nbyte, bufused, &cde, sizeof(cde)); + fd_readdir_put(buf, nbyte, bufused, d_name, cde.d_namlen); + } + mutex_unlock(&fo->directory.lock); + fd_object_release(exec_env, fo); + return __WASI_ESUCCESS; } -__wasi_errno_t wasmtime_ssp_sock_send( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t sock, - const __wasi_ciovec_t *si_data, - size_t si_data_len, - __wasi_siflags_t si_flags, - size_t *so_datalen -) NO_LOCK_ANALYSIS { - // Convert input to msghdr. - struct msghdr hdr = { - .msg_iov = (struct iovec *)si_data, - .msg_iovlen = si_data_len, - }; - - // Attach file descriptors if present. - __wasi_errno_t error; - - // Send message. - struct fd_object *fo; - error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); - if (error != 0) - goto out; - ssize_t len = sendmsg(fd_number(fo), &hdr, 0); - fd_object_release(fo); - if (len < 0) { - error = convert_errno(errno); - } else { - *so_datalen = (size_t)len; - } - -out: - return error; -} - -__wasi_errno_t wasmtime_ssp_sock_shutdown( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct fd_table *curfds, -#endif - __wasi_fd_t sock, - __wasi_sdflags_t how -) { - int nhow; - switch (how) { - case __WASI_SHUT_RD: - nhow = SHUT_RD; - break; - case __WASI_SHUT_WR: - nhow = SHUT_WR; - break; - case __WASI_SHUT_RD | __WASI_SHUT_WR: - nhow = SHUT_RDWR; - break; - default: - return __WASI_EINVAL; - } - - struct fd_object *fo; - __wasi_errno_t error = - fd_object_get(curfds, &fo, sock, __WASI_RIGHT_SOCK_SHUTDOWN, 0); - if (error != 0) - return error; +__wasi_errno_t +wasmtime_ssp_path_readlink(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const char *path, size_t pathlen, + char *buf, size_t bufsize, size_t *bufused) +{ + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(exec_env, curfds, &pa, fd, path, pathlen, + __WASI_RIGHT_PATH_READLINK, 0, false); - int ret = shutdown(fd_number(fo), nhow); - fd_object_release(fo); - if (ret < 0) - return convert_errno(errno); - return 0; -} + if (error != 0) + return error; -__wasi_errno_t wasmtime_ssp_sched_yield(void) { - if (sched_yield() < 0) - return convert_errno(errno); - return 0; -} + error = os_readlinkat(pa.fd, pa.path, buf, bufsize, bufused); -__wasi_errno_t wasmtime_ssp_args_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *argv_environ, -#endif - char **argv, - char *argv_buf -) { - for (size_t i = 0; i < argv_environ->argc; ++i) { - argv[i] = argv_buf + (argv_environ->argv[i] - argv_environ->argv_buf); - } - argv[argv_environ->argc] = NULL; - bh_memcpy_s(argv_buf, (uint32)argv_environ->argv_buf_size, - argv_environ->argv_buf, (uint32)argv_environ->argv_buf_size); - return __WASI_ESUCCESS; -} - -__wasi_errno_t wasmtime_ssp_args_sizes_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *argv_environ, -#endif - size_t *argc, - size_t *argv_buf_size -) { - *argc = argv_environ->argc; - *argv_buf_size = argv_environ->argv_buf_size; - return __WASI_ESUCCESS; -} + path_put(&pa); -__wasi_errno_t wasmtime_ssp_environ_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *argv_environ, -#endif - char **environ, - char *environ_buf -) { - for (size_t i = 0; i < argv_environ->environ_count; ++i) { - environ[i] = environ_buf + (argv_environ->environ[i] - argv_environ->environ_buf); - } - environ[argv_environ->environ_count] = NULL; - bh_memcpy_s(environ_buf, (uint32)argv_environ->environ_buf_size, - argv_environ->environ_buf, (uint32)argv_environ->environ_buf_size); - return __WASI_ESUCCESS; -} - -__wasi_errno_t wasmtime_ssp_environ_sizes_get( -#if !defined(WASMTIME_SSP_STATIC_CURFDS) - struct argv_environ_values *argv_environ, -#endif - size_t *environ_count, - size_t *environ_buf_size -) { - *environ_count = argv_environ->environ_count; - *environ_buf_size = argv_environ->environ_buf_size; - return __WASI_ESUCCESS; + return error; } -bool argv_environ_init(struct argv_environ_values *argv_environ, - const size_t *argv_offsets, size_t argv_offsets_len, - const char *argv_buf, size_t argv_buf_len, - const size_t *environ_offsets, size_t environ_offsets_len, - const char *environ_buf, size_t environ_buf_len) +__wasi_errno_t +wasmtime_ssp_path_rename(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t old_fd, const char *old_path, + size_t old_path_len, __wasi_fd_t new_fd, + const char *new_path, size_t new_path_len) { - uint64 total_size; - size_t i; + struct path_access old_pa; + __wasi_errno_t error = path_get_nofollow( + exec_env, curfds, &old_pa, old_fd, old_path, old_path_len, + __WASI_RIGHT_PATH_RENAME_SOURCE, 0, true); + if (error != 0) + return error; + + struct path_access new_pa; + error = path_get_nofollow(exec_env, curfds, &new_pa, new_fd, new_path, + new_path_len, __WASI_RIGHT_PATH_RENAME_TARGET, 0, + true); + if (error != 0) { + path_put(&old_pa); + return error; + } - memset(argv_environ, 0, sizeof(struct argv_environ_values)); + error = os_renameat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path); - argv_environ->argc = argv_offsets_len; - argv_environ->argv_buf_size = argv_buf_len; + path_put(&old_pa); + path_put(&new_pa); - total_size = sizeof(char *) * (uint64)argv_offsets_len; - if (total_size >= UINT32_MAX - || !(argv_environ->argv = bh_malloc((uint32)total_size))) - return false; + return error; +} + +__wasi_errno_t +wasmtime_ssp_fd_filestat_get(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_filestat_t *buf) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_GET, 0); + + if (error != 0) + return error; + error = os_fstat(fo->file_handle, buf); + + fd_object_release(exec_env, fo); + + return error; +} + +static void +convert_timestamp(__wasi_timestamp_t in, os_timespec *out) +{ + // Store sub-second remainder. +#if defined(__SYSCALL_SLONG_TYPE) + out->tv_nsec = (__SYSCALL_SLONG_TYPE)(in % 1000000000); +#else + out->tv_nsec = (long)(in % 1000000000); +#endif + __wasi_timestamp_t temp = in / 1000000000; + + // Clamp to the maximum in case it would overflow our system's time_t. + out->tv_sec = (time_t)temp < BH_TIME_T_MAX ? (time_t)temp : BH_TIME_T_MAX; +} + +__wasi_errno_t +wasmtime_ssp_fd_filestat_set_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_filesize_t st_size) +{ + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_SIZE, 0); + + if (error != 0) + return error; + + error = os_ftruncate(fo->file_handle, st_size); + fd_object_release(exec_env, fo); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_fd_filestat_set_times(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags) +{ + if ((fstflags + & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW + | __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) + != 0 + || (fstflags + & (__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW)) + == (__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW) + || (fstflags + & (__WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) + == (__WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) + return __WASI_EINVAL; - if (argv_buf_len >= UINT32_MAX - || !(argv_environ->argv_buf = bh_malloc((uint32)argv_buf_len))) - goto fail1; + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_TIMES, 0); + if (error != 0) + return error; - for (i = 0; i < argv_offsets_len; ++i) { - argv_environ->argv[i] = argv_environ->argv_buf + argv_offsets[i]; + error = os_futimens(fo->file_handle, st_atim, st_mtim, fstflags); + + fd_object_release(exec_env, fo); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_path_filestat_get(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_lookupflags_t flags, const char *path, + size_t pathlen, __wasi_filestat_t *buf) +{ + struct path_access pa; + __wasi_errno_t error = + path_get(exec_env, curfds, &pa, fd, flags, path, pathlen, + __WASI_RIGHT_PATH_FILESTAT_GET, 0, false); + if (error != 0) + return error; + + error = os_fstatat(pa.fd, pa.path, buf, + pa.follow ? __WASI_LOOKUP_SYMLINK_FOLLOW : 0); + + path_put(&pa); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_path_filestat_set_times(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, size_t pathlen, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags) +{ + if (((fstflags + & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW + | __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) + != 0) + /* ATIM & ATIM_NOW can't be set at the same time */ + || ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0 + && (fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) + /* MTIM & MTIM_NOW can't be set at the same time */ + || ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0 + && (fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0)) + return __WASI_EINVAL; + + struct path_access pa; + __wasi_errno_t error = + path_get(exec_env, curfds, &pa, fd, flags, path, pathlen, + __WASI_RIGHT_PATH_FILESTAT_SET_TIMES, 0, false); + if (error != 0) + return error; + + error = os_utimensat(pa.fd, pa.path, st_atim, st_mtim, fstflags, + pa.follow ? __WASI_LOOKUP_SYMLINK_FOLLOW : 0); + + path_put(&pa); + return error; +} + +__wasi_errno_t +wasmtime_ssp_path_symlink(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct fd_prestats *prestats, const char *old_path, + size_t old_path_len, __wasi_fd_t fd, + const char *new_path, size_t new_path_len) +{ + char *target = str_nullterminate(old_path, old_path_len); + if (target == NULL) + return convert_errno(errno); + + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(exec_env, curfds, &pa, fd, new_path, new_path_len, + __WASI_RIGHT_PATH_SYMLINK, 0, true); + if (error != 0) { + wasm_runtime_free(target); + return error; } - bh_memcpy_s(argv_environ->argv_buf, (uint32)argv_buf_len, - argv_buf, (uint32)argv_buf_len); - argv_environ->environ_count = environ_offsets_len; - argv_environ->environ_buf_size = environ_buf_len; + rwlock_rdlock(&prestats->lock); + if (!validate_path(target, prestats)) { + rwlock_unlock(&prestats->lock); + wasm_runtime_free(target); + return __WASI_EBADF; + } + rwlock_unlock(&prestats->lock); + + error = os_symlinkat(target, pa.fd, pa.path); + + path_put(&pa); + wasm_runtime_free(target); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_path_unlink_file(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, const char *path, size_t pathlen) +{ + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(exec_env, curfds, &pa, fd, path, pathlen, + __WASI_RIGHT_PATH_UNLINK_FILE, 0, true); + if (error != __WASI_ESUCCESS) + return error; + + error = os_unlinkat(pa.fd, pa.path, false); + + path_put(&pa); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_path_remove_directory(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + const char *path, size_t pathlen) +{ + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(exec_env, curfds, &pa, fd, path, pathlen, + __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0, true); + if (error != 0) + return error; - total_size = sizeof(char *) * (uint64)environ_offsets_len; - if (total_size >= UINT32_MAX - || !(argv_environ->environ = bh_malloc((uint32)total_size))) - goto fail2; + error = os_unlinkat(pa.fd, pa.path, true); - if (environ_buf_len >= UINT32_MAX - || !(argv_environ->environ_buf = bh_malloc((uint32)environ_buf_len))) - goto fail3; + path_put(&pa); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_poll_oneoff(wasm_exec_env_t exec_env, struct fd_table *curfds, + const __wasi_subscription_t *in, __wasi_event_t *out, + size_t nsubscriptions, + size_t *nevents) NO_LOCK_ANALYSIS +{ +#if defined(BH_PLATFORM_WINDOWS) + return __WASI_ENOSYS; +#else + // Sleeping. + if (nsubscriptions == 1 && in[0].u.type == __WASI_EVENTTYPE_CLOCK) { + out[0] = (__wasi_event_t){ + .userdata = in[0].userdata, + .type = in[0].u.type, + }; +#if CONFIG_HAS_CLOCK_NANOSLEEP + clockid_t clock_id; + if (wasi_clockid_to_clockid(in[0].u.u.clock.clock_id, &clock_id)) { + os_timespec ts; + convert_timestamp(in[0].u.u.clock.timeout, &ts); + int ret = clock_nanosleep( + clock_id, + (in[0].u.u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 + ? TIMER_ABSTIME + : 0, + &ts, NULL); + if (ret != 0) + out[0].error = convert_errno(ret); + } + else { + out[0].error = __WASI_ENOTSUP; + } +#else + switch (in[0].u.u.clock.clock_id) { + case __WASI_CLOCK_MONOTONIC: + if ((in[0].u.u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) + != 0) { + // TODO(ed): Implement. + fputs("Unimplemented absolute sleep on monotonic clock\n", + stderr); + out[0].error = __WASI_ENOSYS; + } + else { + // Perform relative sleeps on the monotonic clock also using + // nanosleep(). This is incorrect, but good enough for now. + os_timespec ts; + convert_timestamp(in[0].u.u.clock.timeout, &ts); + nanosleep(&ts, NULL); + } + break; + case __WASI_CLOCK_REALTIME: + if ((in[0].u.u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) + != 0) { + // Sleeping to an absolute point in time can only be done + // by waiting on a condition variable. + struct mutex mutex; + struct cond cond; + + if (!mutex_init(&mutex)) + return -1; + if (!cond_init_realtime(&cond)) { + mutex_destroy(&mutex); + return -1; + } + mutex_lock(&mutex); + cond_timedwait(&cond, &mutex, in[0].u.u.clock.timeout, + true); + mutex_unlock(&mutex); + mutex_destroy(&mutex); + cond_destroy(&cond); + } + else { + // Relative sleeps can be done using nanosleep(). + os_timespec ts; + convert_timestamp(in[0].u.u.clock.timeout, &ts); + nanosleep(&ts, NULL); + } + break; + default: + out[0].error = __WASI_ENOTSUP; + break; + } +#endif + *nevents = 1; + if (out[0].error != 0) + return convert_errno(out[0].error); + return 0; + } - for (i = 0; i < environ_offsets_len; ++i) { - argv_environ->environ[i] = argv_environ->environ_buf + environ_offsets[i]; + // Last option: call into poll(). This can only be done in case all + // subscriptions consist of __WASI_EVENTTYPE_FD_READ and + // __WASI_EVENTTYPE_FD_WRITE entries. There may be up to one + // __WASI_EVENTTYPE_CLOCK entry to act as a timeout. These are also + // the subscriptions generate by cloudlibc's poll() and select(). + struct fd_object **fos = + wasm_runtime_malloc((uint32)(nsubscriptions * sizeof(*fos))); + if (fos == NULL) + return __WASI_ENOMEM; + os_poll_file_handle *pfds = + wasm_runtime_malloc((uint32)(nsubscriptions * sizeof(*pfds))); + if (pfds == NULL) { + wasm_runtime_free(fos); + return __WASI_ENOMEM; } - bh_memcpy_s(argv_environ->environ_buf, (uint32)environ_buf_len, - environ_buf, (uint32)environ_buf_len); - return true; + // Convert subscriptions to pollfd entries. Increase the reference + // count on the file descriptors to ensure they remain valid across + // the call to poll(). + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + *nevents = 0; + const __wasi_subscription_t *clock_subscription = NULL; + for (size_t i = 0; i < nsubscriptions; ++i) { + const __wasi_subscription_t *s = &in[i]; + switch (s->u.type) { + case __WASI_EVENTTYPE_FD_READ: + case __WASI_EVENTTYPE_FD_WRITE: + { + __wasi_errno_t error = + fd_object_get_locked(&fos[i], ft, s->u.u.fd_readwrite.fd, + __WASI_RIGHT_POLL_FD_READWRITE, 0); + if (error == 0) { + +// Temporary workaround (see PR#4377) +#ifdef BH_PLATFORM_ZEPHYR + os_file_handle tfd = fos[i]->file_handle->fd; +#else + os_file_handle tfd = fos[i]->file_handle; +#endif + // Proper file descriptor on which we can poll(). + pfds[i] = (os_poll_file_handle){ + .fd = tfd, + .events = s->u.type == __WASI_EVENTTYPE_FD_READ + ? POLLIN + : POLLOUT, + }; + } + else { + // Invalid file descriptor or rights missing. + fos[i] = NULL; + pfds[i] = (os_poll_file_handle){ .fd = -1 }; + out[(*nevents)++] = (__wasi_event_t){ + .userdata = s->userdata, + .error = error, + .type = s->u.type, + }; + } + break; + } + case __WASI_EVENTTYPE_CLOCK: + if (clock_subscription == NULL + && (s->u.u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) + == 0) { + // Relative timeout. + fos[i] = NULL; + pfds[i] = (os_poll_file_handle){ .fd = -1 }; + clock_subscription = s; + break; + } + // Fallthrough. + default: + // Unsupported event. + fos[i] = NULL; + pfds[i] = (os_poll_file_handle){ .fd = -1 }; + out[(*nevents)++] = (__wasi_event_t){ + .userdata = s->userdata, + .error = __WASI_ENOSYS, + .type = s->u.type, + }; + break; + } + } + rwlock_unlock(&ft->lock); -fail3: - bh_free(argv_environ->environ); -fail2: - bh_free(argv_environ->argv_buf); -fail1: - bh_free(argv_environ->argv); + // Use a zero-second timeout in case we've already generated events in + // the loop above. + int timeout; + if (*nevents != 0) { + timeout = 0; + } + else if (clock_subscription != NULL) { + __wasi_timestamp_t ts = clock_subscription->u.u.clock.timeout / 1000000; + timeout = ts > INT_MAX ? -1 : (int)ts; + } + else { + timeout = -1; + } - memset(argv_environ, 0, sizeof(struct argv_environ_values)); + int ret; + int error = blocking_op_poll(exec_env, pfds, nsubscriptions, timeout, &ret); + if (error != 0) { + /* got an error */ + } + else if (ret == 0 && *nevents == 0 && clock_subscription != NULL) { + // No events triggered. Trigger the clock event. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = clock_subscription->userdata, + .type = __WASI_EVENTTYPE_CLOCK, + }; + } + else { + // Events got triggered. Don't trigger the clock event. + for (size_t i = 0; i < nsubscriptions; ++i) { + if (pfds[i].fd >= 0) { + __wasi_filesize_t nbytes = 0; + if (in[i].u.type == __WASI_EVENTTYPE_FD_READ) { + int l; + if (os_ioctl(fos[i]->file_handle, FIONREAD, &l) == 0) + nbytes = (__wasi_filesize_t)l; + } + if ((pfds[i].revents & POLLNVAL) != 0) { + // Bad file descriptor. This normally cannot occur, as + // referencing the file descriptor object will always ensure + // the descriptor is valid. Still, macOS may sometimes + // return this on FIFOs when reaching end-of-file. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, +#ifdef __APPLE__ + .u.fd_readwrite.nbytes = nbytes, + .u.fd_readwrite.flags = + __WASI_EVENT_FD_READWRITE_HANGUP, +#else + .error = __WASI_EBADF, +#endif + .type = in[i].u.type, + }; + } + else if ((pfds[i].revents & POLLERR) != 0) { + // File descriptor is in an error state. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .error = __WASI_EIO, + .type = in[i].u.type, + }; + } + else if ((pfds[i].revents & POLLHUP) != 0) { + // End-of-file. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .type = in[i].u.type, + .u.fd_readwrite.nbytes = nbytes, + .u.fd_readwrite.flags = + __WASI_EVENT_FD_READWRITE_HANGUP, + }; + } + else if ((pfds[i].revents & (POLLIN | POLLOUT)) != 0) { + // Read or write possible. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .type = in[i].u.type, + .u.fd_readwrite.nbytes = nbytes, + }; + } + } + } + } + + for (size_t i = 0; i < nsubscriptions; ++i) + if (fos[i] != NULL) + fd_object_release(exec_env, fos[i]); + wasm_runtime_free(fos); + wasm_runtime_free(pfds); + return error; +#endif +} + +__wasi_errno_t +wasmtime_ssp_random_get(void *buf, size_t nbyte) +{ + return random_buf(buf, nbyte); +} + +__wasi_errno_t +wasi_ssp_sock_accept(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_fdflags_t flags, + __wasi_fd_t *fd_new) +{ + __wasi_filetype_t wasi_type; + __wasi_rights_t max_base, max_inheriting; + struct fd_object *fo; + bh_socket_t new_sock = os_get_invalid_handle(); + int ret; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_ACCEPT, 0); + if (error != __WASI_ESUCCESS) { + goto fail; + } + + ret = blocking_op_socket_accept(exec_env, fo->file_handle, &new_sock, NULL, + NULL); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + error = convert_errno(errno); + goto fail; + } + + error = fd_determine_type_rights(new_sock, &wasi_type, &max_base, + &max_inheriting); + if (error != __WASI_ESUCCESS) { + goto fail; + } + + error = fd_table_insert_fd(exec_env, curfds, new_sock, wasi_type, max_base, + max_inheriting, fd_new); + if (error != __WASI_ESUCCESS) { + /* released in fd_table_insert_fd() */ + new_sock = os_get_invalid_handle(); + goto fail; + } + + return __WASI_ESUCCESS; + +fail: + if (os_is_handle_valid(&new_sock)) { + os_socket_close(new_sock); + } + return error; +} + +__wasi_errno_t +wasi_ssp_sock_addr_local(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_addr_t *addr) +{ + struct fd_object *fo; + bh_sockaddr_t bh_addr; + int ret; + + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_ADDR_LOCAL, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_addr_local(fo->file_handle, &bh_addr); + fd_object_release(exec_env, fo); + if (ret != BHT_OK) { + return convert_errno(errno); + } + + bh_sockaddr_to_wasi_addr(&bh_addr, addr); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_addr_remote(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_addr_t *addr) +{ + struct fd_object *fo; + bh_sockaddr_t bh_addr; + int ret; + + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_ADDR_LOCAL, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_addr_remote(fo->file_handle, &bh_addr); + fd_object_release(exec_env, fo); + if (ret != BHT_OK) { + return convert_errno(errno); + } + + bh_sockaddr_to_wasi_addr(&bh_addr, addr); + + return __WASI_ESUCCESS; +} + +static bool +wasi_addr_to_string(const __wasi_addr_t *addr, char *buf, size_t buflen) +{ + if (addr->kind == IPv4) { + const char *format = "%u.%u.%u.%u"; + + bh_assert(buflen >= 16); + + snprintf(buf, buflen, format, addr->addr.ip4.addr.n0, + addr->addr.ip4.addr.n1, addr->addr.ip4.addr.n2, + addr->addr.ip4.addr.n3); + + return true; + } + else if (addr->kind == IPv6) { + const char *format = "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"; + __wasi_addr_ip6_t ipv6 = addr->addr.ip6.addr; + + bh_assert(buflen >= 40); + + snprintf(buf, buflen, format, ipv6.n0, ipv6.n1, ipv6.n2, ipv6.n3, + ipv6.h0, ipv6.h1, ipv6.h2, ipv6.h3); + + return true; + } return false; } -void argv_environ_destroy(struct argv_environ_values *argv_environ) +__wasi_errno_t +wasi_ssp_sock_bind(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct addr_pool *addr_pool, __wasi_fd_t fd, + __wasi_addr_t *addr) +{ + char buf[48] = { 0 }; + struct fd_object *fo; + __wasi_errno_t error; + int port = addr->kind == IPv4 ? addr->addr.ip4.port : addr->addr.ip6.port; + int ret; + + if (!wasi_addr_to_string(addr, buf, sizeof(buf))) { + return __WASI_EPROTONOSUPPORT; + } + + if (!addr_pool_search(addr_pool, buf)) { + return __WASI_EACCES; + } + + error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_BIND, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_bind(fo->file_handle, buf, &port); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_addr_resolve(wasm_exec_env_t exec_env, struct fd_table *curfds, + char **ns_lookup_list, const char *host, + const char *service, __wasi_addr_info_hints_t *hints, + __wasi_addr_info_t *addr_info, + __wasi_size_t addr_info_size, + __wasi_size_t *max_info_size) +{ + bh_addr_info_t *wamr_addr_info = + wasm_runtime_malloc(addr_info_size * sizeof(bh_addr_info_t)); + uint8_t hints_is_ipv4 = hints->family == INET4; + uint8_t hints_is_tcp = hints->type == SOCKET_STREAM; + size_t _max_info_size; + size_t actual_info_size; + + if (!wamr_addr_info) { + return __WASI_ENOMEM; + } + + if (!ns_lookup_list_search(ns_lookup_list, host)) { + wasm_runtime_free(wamr_addr_info); + return __WASI_EACCES; + } + + int ret = blocking_op_socket_addr_resolve( + exec_env, host, service, + hints->hints_enabled && hints->type != SOCKET_ANY ? &hints_is_tcp + : NULL, + hints->hints_enabled && hints->family != INET_UNSPEC ? &hints_is_ipv4 + : NULL, + wamr_addr_info, addr_info_size, &_max_info_size); + + if (ret != BHT_OK) { + wasm_runtime_free(wamr_addr_info); + return convert_errno(errno); + } + + *max_info_size = _max_info_size; + actual_info_size = + addr_info_size < *max_info_size ? addr_info_size : *max_info_size; + + for (size_t i = 0; i < actual_info_size; i++) { + addr_info[i].type = + wamr_addr_info[i].is_tcp ? SOCKET_STREAM : SOCKET_DGRAM; + bh_sockaddr_to_wasi_addr(&wamr_addr_info[i].sockaddr, + &addr_info[i].addr); + } + + wasm_runtime_free(wamr_addr_info); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_connect(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct addr_pool *addr_pool, __wasi_fd_t fd, + __wasi_addr_t *addr) +{ + char buf[48] = { 0 }; + struct fd_object *fo; + __wasi_errno_t error; + int ret; + + if (!wasi_addr_to_string(addr, buf, sizeof(buf))) { + return __WASI_EPROTONOSUPPORT; + } + + if (!addr_pool_search(addr_pool, buf)) { + return __WASI_EACCES; + } + + error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_CONNECT, 0); + if (error != __WASI_ESUCCESS) { + return error; + } + + /* Consume __wasi_addr_t */ + ret = blocking_op_socket_connect(exec_env, fo->file_handle, buf, + addr->kind == IPv4 ? addr->addr.ip4.port + : addr->addr.ip6.port); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_get_recv_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t *size) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + size_t bufsize = 0; + int ret = os_socket_get_recv_buf_size(fo->file_handle, &bufsize); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + *size = (__wasi_size_t)bufsize; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_get_reuse_addr(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t *reuse) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + bool enabled = false; + + int ret = os_socket_get_reuse_addr(fo->file_handle, &enabled); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + *reuse = (uint8_t)enabled; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_get_reuse_port(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t *reuse) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + bool enabled = false; + int ret = os_socket_get_reuse_port(fo->file_handle, &enabled); + + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + *reuse = (uint8_t)enabled; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_get_send_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t *size) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + size_t bufsize = 0; + int ret = os_socket_get_send_buf_size(fo->file_handle, &bufsize); + + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + *size = (__wasi_size_t)bufsize; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_listen(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, __wasi_size_t backlog) +{ + struct fd_object *fo; + int ret; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_SOCK_LISTEN, 0); + if (error != __WASI_ESUCCESS) + return error; + + ret = os_socket_listen(fo->file_handle, backlog); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_open(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t poolfd, __wasi_address_family_t af, + __wasi_sock_type_t socktype, __wasi_fd_t *sockfd) +{ + bh_socket_t sock; + bool is_tcp = SOCKET_DGRAM == socktype ? false : true; + bool is_ipv4 = INET6 == af ? false : true; + int ret; + __wasi_filetype_t wasi_type = __WASI_FILETYPE_UNKNOWN; + __wasi_rights_t max_base = 0, max_inheriting = 0; + __wasi_errno_t error; + + (void)poolfd; + + ret = os_socket_create(&sock, is_ipv4, is_tcp); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + error = + fd_determine_type_rights(sock, &wasi_type, &max_base, &max_inheriting); + if (error != __WASI_ESUCCESS) { + os_socket_close(sock); + return error; + } + + if (SOCKET_DGRAM == socktype) { + bh_assert(wasi_type == __WASI_FILETYPE_SOCKET_DGRAM); + } + else { + bh_assert(wasi_type == __WASI_FILETYPE_SOCKET_STREAM); + } + + // TODO: base rights and inheriting rights ? + error = fd_table_insert_fd(exec_env, curfds, sock, wasi_type, max_base, + max_inheriting, sockfd); + if (error != __WASI_ESUCCESS) { + return error; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_set_recv_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t size) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + int ret = os_socket_set_recv_buf_size(fo->file_handle, size); + + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_set_reuse_addr(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t reuse) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + int ret = os_socket_set_reuse_addr(fo->file_handle, (bool)reuse); + + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_set_reuse_port(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t fd, uint8_t reuse) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + int ret = os_socket_set_reuse_port(fo->file_handle, (bool)reuse); + + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasi_ssp_sock_set_send_buf_size(wasm_exec_env_t exec_env, + struct fd_table *curfds, __wasi_fd_t fd, + __wasi_size_t size) +{ + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, 0, 0); + if (error != __WASI_ESUCCESS) + return error; + + int ret = os_socket_set_send_buf_size(fo->file_handle, size); + + fd_object_release(exec_env, fo); + if (BHT_OK != ret) { + return convert_errno(errno); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_recv(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, void *buf, size_t buf_len, + size_t *recv_len) +{ + __wasi_addr_t src_addr; + + return wasmtime_ssp_sock_recv_from(exec_env, curfds, sock, buf, buf_len, 0, + &src_addr, recv_len); +} + +__wasi_errno_t +wasmtime_ssp_sock_recv_from(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, void *buf, size_t buf_len, + __wasi_riflags_t ri_flags, __wasi_addr_t *src_addr, + size_t *recv_len) +{ + struct fd_object *fo; + __wasi_errno_t error; + bh_sockaddr_t sockaddr, *sockaddr_ptr = NULL; + int ret; + + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0); + if (error != 0) { + return error; + } + + // If the source address is not NULL, the caller is requesting the source + // address to be returned if the protocol supports it. If the value is + // NULL, the POSIX standard states that the address is not returned. + if (src_addr != NULL) { + sockaddr_ptr = &sockaddr; + } + + /* Consume bh_sockaddr_t instead of __wasi_addr_t */ + ret = blocking_op_socket_recv_from(exec_env, fo->file_handle, buf, buf_len, + 0, sockaddr_ptr); + fd_object_release(exec_env, fo); + if (-1 == ret) { + return convert_errno(errno); + } + + // If the source address is not NULL, we need to convert the sockaddr + // back to __wasi_addr_t format. + if (src_addr != NULL) { + bh_sockaddr_to_wasi_addr(sockaddr_ptr, src_addr); + } + + *recv_len = (size_t)ret; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_send(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, const void *buf, size_t buf_len, + size_t *sent_len) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) { + return error; + } + + ret = os_socket_send(fo->file_handle, buf, buf_len); + fd_object_release(exec_env, fo); + if (-1 == ret) { + return convert_errno(errno); + } + + *sent_len = (size_t)ret; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_send_to(wasm_exec_env_t exec_env, struct fd_table *curfds, + struct addr_pool *addr_pool, __wasi_fd_t sock, + const void *buf, size_t buf_len, + __wasi_siflags_t si_flags, + const __wasi_addr_t *dest_addr, size_t *sent_len) +{ + char addr_buf[48] = { 0 }; + struct fd_object *fo; + __wasi_errno_t error; + int ret; + bh_sockaddr_t sockaddr; + + if (!wasi_addr_to_string(dest_addr, addr_buf, sizeof(addr_buf))) { + return __WASI_EPROTONOSUPPORT; + } + + if (!addr_pool_search(addr_pool, addr_buf)) { + return __WASI_EACCES; + } + + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) { + return error; + } + + wasi_addr_to_bh_sockaddr(dest_addr, &sockaddr); + + /* Consume bh_sockaddr instead of __wasi_addr_t */ + ret = blocking_op_socket_send_to(exec_env, fo->file_handle, buf, buf_len, 0, + &sockaddr); + fd_object_release(exec_env, fo); + if (-1 == ret) { + return convert_errno(errno); + } + + *sent_len = (size_t)ret; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_shutdown(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock) +{ + struct fd_object *fo; + __wasi_errno_t error; + + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + error = os_socket_shutdown(fo->file_handle); + fd_object_release(exec_env, fo); + + return error; +} + +__wasi_errno_t +wasmtime_ssp_sched_yield(void) +{ +#if defined(BH_PLATFORM_WINDOWS) + SwitchToThread(); +#elif defined(BH_PLATFORM_ZEPHYR) + k_yield(); +#else + if (sched_yield() < 0) + return convert_errno(errno); +#endif + return 0; +} + +__wasi_errno_t +wasmtime_ssp_args_get(struct argv_environ_values *argv_environ, char **argv, + char *argv_buf) +{ + for (size_t i = 0; i < argv_environ->argc; ++i) { + argv[i] = + argv_buf + (argv_environ->argv_list[i] - argv_environ->argv_buf); + } + argv[argv_environ->argc] = NULL; + bh_memcpy_s(argv_buf, (uint32)argv_environ->argv_buf_size, + argv_environ->argv_buf, (uint32)argv_environ->argv_buf_size); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_args_sizes_get(struct argv_environ_values *argv_environ, + size_t *argc, size_t *argv_buf_size) { - if (argv_environ->argv_buf) - bh_free(argv_environ->argv_buf); - if (argv_environ->argv) - bh_free(argv_environ->argv); - if (argv_environ->environ_buf) - bh_free(argv_environ->environ_buf); - if (argv_environ->environ) - bh_free(argv_environ->environ); + *argc = argv_environ->argc; + *argv_buf_size = argv_environ->argv_buf_size; + return __WASI_ESUCCESS; } -void fd_table_destroy(struct fd_table *ft) +__wasi_errno_t +wasmtime_ssp_environ_get(struct argv_environ_values *argv_environ, + char **environs, char *environ_buf) +{ + for (size_t i = 0; i < argv_environ->environ_count; ++i) { + environs[i] = + environ_buf + + (argv_environ->environ_list[i] - argv_environ->environ_buf); + } + environs[argv_environ->environ_count] = NULL; + bh_memcpy_s(environ_buf, (uint32)argv_environ->environ_buf_size, + argv_environ->environ_buf, + (uint32)argv_environ->environ_buf_size); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_environ_sizes_get(struct argv_environ_values *argv_environ, + size_t *environ_count, size_t *environ_buf_size) +{ + *environ_count = argv_environ->environ_count; + *environ_buf_size = argv_environ->environ_buf_size; + return __WASI_ESUCCESS; +} + +bool +argv_environ_init(struct argv_environ_values *argv_environ, char *argv_buf, + size_t argv_buf_size, char **argv_list, size_t argc, + char *environ_buf, size_t environ_buf_size, + char **environ_list, size_t environ_count) +{ + memset(argv_environ, 0, sizeof(struct argv_environ_values)); + + argv_environ->argv_buf = argv_buf; + argv_environ->argv_buf_size = argv_buf_size; + argv_environ->argv_list = argv_list; + argv_environ->argc = argc; + argv_environ->environ_buf = environ_buf; + argv_environ->environ_buf_size = environ_buf_size; + argv_environ->environ_list = environ_list; + argv_environ->environ_count = environ_count; + return true; +} + +void +argv_environ_destroy(struct argv_environ_values *argv_environ) +{ + (void)argv_environ; +} + +void +fd_table_destroy(struct fd_table *ft) { if (ft->entries) { for (uint32 i = 0; i < ft->size; i++) { if (ft->entries[i].object != NULL) { - fd_object_release(ft->entries[i].object); + fd_object_release(NULL, ft->entries[i].object); } } - bh_free(ft->entries); + wasm_runtime_free(ft->entries); } + rwlock_destroy(&ft->lock); } -void fd_prestats_destroy(struct fd_prestats *pt) +void +fd_prestats_destroy(struct fd_prestats *pt) { if (pt->prestats) { for (uint32 i = 0; i < pt->size; i++) { if (pt->prestats[i].dir != NULL) { - bh_free((void*)pt->prestats[i].dir); + wasm_runtime_free((void *)pt->prestats[i].dir); } } - bh_free(pt->prestats); + wasm_runtime_free(pt->prestats); } + rwlock_destroy(&pt->lock); +} + +bool +addr_pool_init(struct addr_pool *addr_pool) +{ + memset(addr_pool, 0, sizeof(*addr_pool)); + + return true; +} + +bool +addr_pool_insert(struct addr_pool *addr_pool, const char *addr, uint8 mask) +{ + struct addr_pool *cur = addr_pool; + struct addr_pool *next; + bh_ip_addr_buffer_t target; + + if (!addr_pool) { + return false; + } + + if (!(next = wasm_runtime_malloc(sizeof(struct addr_pool)))) { + return false; + } + + next->next = NULL; + + if (os_socket_inet_network(true, addr, &target) != BHT_OK) { + // If parsing IPv4 fails, try IPv6 + if (os_socket_inet_network(false, addr, &target) != BHT_OK) { + wasm_runtime_free(next); + return false; + } + next->type = IPv6; + bh_memcpy_s(next->addr.ip6, sizeof(next->addr.ip6), target.ipv6, + sizeof(target.ipv6)); + if (mask > 128) { + wasm_runtime_free(next); + return false; + } + next->mask = mask; + } + else { + next->type = IPv4; + next->addr.ip4 = target.ipv4; + if (mask > 32) { + wasm_runtime_free(next); + return false; + } + next->mask = mask; + } + + /* attach with */ + while (cur->next) { + cur = cur->next; + } + cur->next = next; + return true; +} + +static void +init_address_mask(uint8_t *buf, size_t buflen, size_t mask) +{ + size_t element_size = sizeof(uint8_t) * 8; + + for (size_t i = 0; i < buflen; i++) { + if (mask <= i * element_size) { + buf[i] = 0; + } + else { + size_t offset = min(mask - i * element_size, element_size); + buf[i] = (~0u) << (element_size - offset); + } + } +} + +/* target must be in network byte order */ +static bool +compare_address(const struct addr_pool *addr_pool_entry, + bh_ip_addr_buffer_t *target) +{ + uint8_t maskbuf[16] = { 0 }; + uint8_t basebuf[16] = { 0 }; + size_t addr_size; + uint8_t max_addr_mask; + + if (addr_pool_entry->type == IPv4) { + uint32_t addr_ip4 = htonl(addr_pool_entry->addr.ip4); + bh_memcpy_s(basebuf, sizeof(addr_ip4), &addr_ip4, sizeof(addr_ip4)); + addr_size = 4; + } + else { + uint16_t partial_addr_ip6; + for (int i = 0; i < 8; i++) { + partial_addr_ip6 = htons(addr_pool_entry->addr.ip6[i]); + bh_memcpy_s(&basebuf[i * sizeof(partial_addr_ip6)], + sizeof(partial_addr_ip6), &partial_addr_ip6, + sizeof(partial_addr_ip6)); + } + addr_size = 16; + } + max_addr_mask = (uint8)(addr_size * 8); + + /* IPv4 0.0.0.0 or IPv6 :: means any address */ + if (basebuf[0] == 0 && !memcmp(basebuf, basebuf + 1, addr_size - 1)) { + return true; + } + + /* No support for invalid mask value */ + if (addr_pool_entry->mask > max_addr_mask) { + return false; + } + + init_address_mask(maskbuf, addr_size, addr_pool_entry->mask); + + for (size_t i = 0; i < addr_size; i++) { + uint8_t addr_mask = target->data[i] & maskbuf[i]; + uint8_t range_mask = basebuf[i] & maskbuf[i]; + if (addr_mask != range_mask) { + return false; + } + } + + return true; +} + +bool +addr_pool_search(struct addr_pool *addr_pool, const char *addr) +{ + struct addr_pool *cur = addr_pool->next; + bh_ip_addr_buffer_t target; + __wasi_addr_type_t addr_type; + + if (os_socket_inet_network(true, addr, &target) != BHT_OK) { + size_t i; + + if (os_socket_inet_network(false, addr, &target) != BHT_OK) { + return false; + } + addr_type = IPv6; + for (i = 0; i < sizeof(target.ipv6) / sizeof(target.ipv6[0]); i++) { + target.ipv6[i] = htons(target.ipv6[i]); + } + } + else { + addr_type = IPv4; + target.ipv4 = htonl(target.ipv4); + } + + while (cur) { + if (cur->type == addr_type && compare_address(cur, &target)) { + return true; + } + + cur = cur->next; + } + + return false; +} + +void +addr_pool_destroy(struct addr_pool *addr_pool) +{ + struct addr_pool *cur = addr_pool->next; + + while (cur) { + struct addr_pool *next = cur->next; + wasm_runtime_free(cur); + cur = next; + } +} + +#define WASMTIME_SSP_PASSTHROUGH_FD_TABLE struct fd_table *curfds, + +// Defines a function that passes through the socket option to the OS +// implementation +#define WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(FUNC_NAME, OPTION_TYPE) \ + __wasi_errno_t wasmtime_ssp_sock_##FUNC_NAME( \ + wasm_exec_env_t exec_env, \ + WASMTIME_SSP_PASSTHROUGH_FD_TABLE __wasi_fd_t sock, \ + OPTION_TYPE option) \ + { \ + struct fd_object *fo; \ + __wasi_errno_t error; \ + int ret; \ + error = fd_object_get(curfds, &fo, sock, 0, 0); \ + if (error != 0) \ + return error; \ + ret = os_socket_##FUNC_NAME(fo->file_handle, option); \ + fd_object_release(exec_env, fo); \ + if (BHT_OK != ret) \ + return convert_errno(errno); \ + return __WASI_ESUCCESS; \ + } + +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_send_timeout, uint64) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_send_timeout, uint64 *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_recv_timeout, uint64) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_recv_timeout, uint64 *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_send_buf_size, size_t) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_send_buf_size, size_t *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_recv_buf_size, size_t) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_recv_buf_size, size_t *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_broadcast, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_broadcast, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_keep_alive, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_keep_alive, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_reuse_addr, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_reuse_addr, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_reuse_port, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_reuse_port, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_tcp_no_delay, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_tcp_no_delay, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_tcp_quick_ack, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_tcp_quick_ack, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_tcp_keep_idle, uint32) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_tcp_keep_idle, uint32 *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_tcp_keep_intvl, uint32) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_tcp_keep_intvl, uint32 *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_tcp_fastopen_connect, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_tcp_fastopen_connect, bool *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_ip_ttl, uint8_t) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_ip_ttl, uint8_t *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_ip_multicast_ttl, uint8_t) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_ip_multicast_ttl, uint8_t *) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(set_ipv6_only, bool) +WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION(get_ipv6_only, bool *) + +#undef WASMTIME_SSP_PASSTHROUGH_FD_TABLE +#undef WASMTIME_SSP_PASSTHROUGH_SOCKET_OPTION + +__wasi_errno_t +wasmtime_ssp_sock_set_linger(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, bool is_enabled, int linger_s) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + ret = os_socket_set_linger(fo->file_handle, is_enabled, linger_s); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) + return convert_errno(errno); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_get_linger(wasm_exec_env_t exec_env, struct fd_table *curfds, + __wasi_fd_t sock, bool *is_enabled, int *linger_s) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + ret = os_socket_get_linger(fo->file_handle, is_enabled, linger_s); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_add_membership(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + bh_ip_addr_buffer_t addr_info; + bool is_ipv6; + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + wasi_addr_ip_to_bh_ip_addr_buffer(imr_multiaddr, &addr_info); + is_ipv6 = imr_multiaddr->kind == IPv6; + ret = os_socket_set_ip_add_membership(fo->file_handle, &addr_info, + imr_interface, is_ipv6); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) + return convert_errno(errno); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_drop_membership(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, + __wasi_addr_ip_t *imr_multiaddr, + uint32_t imr_interface) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + bh_ip_addr_buffer_t addr_info; + bool is_ipv6; + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + wasi_addr_ip_to_bh_ip_addr_buffer(imr_multiaddr, &addr_info); + is_ipv6 = imr_multiaddr->kind == IPv6; + ret = os_socket_set_ip_drop_membership(fo->file_handle, &addr_info, + imr_interface, is_ipv6); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) + return convert_errno(errno); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_set_ip_multicast_loop(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, bool ipv6, + bool is_enabled) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + ret = os_socket_set_ip_multicast_loop(fo->file_handle, ipv6, is_enabled); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) + return convert_errno(errno); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +wasmtime_ssp_sock_get_ip_multicast_loop(wasm_exec_env_t exec_env, + struct fd_table *curfds, + __wasi_fd_t sock, bool ipv6, + bool *is_enabled) +{ + struct fd_object *fo; + __wasi_errno_t error; + int ret; + error = fd_object_get(curfds, &fo, sock, 0, 0); + if (error != 0) + return error; + + ret = os_socket_get_ip_multicast_loop(fo->file_handle, ipv6, is_enabled); + fd_object_release(exec_env, fo); + if (BHT_OK != ret) + return convert_errno(errno); + + return __WASI_ESUCCESS; } diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h index f4def4bc4f..75ed597846 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -12,9 +14,7 @@ #ifndef POSIX_H #define POSIX_H -#include -#include - +#include "bh_platform.h" #include "locking.h" struct fd_entry; @@ -22,41 +22,69 @@ struct fd_prestat; struct syscalls; struct fd_table { - struct rwlock lock; - struct fd_entry *entries; - size_t size; - size_t used; + struct rwlock lock; + struct fd_entry *entries; + size_t size; + size_t used; }; struct fd_prestats { - struct rwlock lock; - struct fd_prestat *prestats; - size_t size; - size_t used; + struct rwlock lock; + struct fd_prestat *prestats; + size_t size; + size_t used; }; struct argv_environ_values { - size_t argc; - size_t argv_buf_size; - char **argv; - char *argv_buf; - size_t environ_count; - size_t environ_buf_size; - char **environ; - char *environ_buf; + const char *argv_buf; + size_t argv_buf_size; + char **argv_list; + size_t argc; + char *environ_buf; + size_t environ_buf_size; + char **environ_list; + size_t environ_count; +}; + +struct addr_pool { + /* addr and mask in host order */ + union { + uint32 ip4; + uint16 ip6[8]; + } addr; + struct addr_pool *next; + __wasi_addr_type_t type; + uint8 mask; }; -void fd_table_init(struct fd_table *); -bool fd_table_insert_existing(struct fd_table *, __wasi_fd_t, int); -void fd_prestats_init(struct fd_prestats *); -bool fd_prestats_insert(struct fd_prestats *, const char *, __wasi_fd_t); -bool argv_environ_init(struct argv_environ_values *, - const size_t *argv_offsets, size_t argv_offsets_len, - const char *argv_buf, size_t argv_buf_len, - const size_t *environ_offsets, size_t environ_offsets_len, - const char *environ_buf, size_t environ_buf_len); -void argv_environ_destroy(struct argv_environ_values *argv_environ); -void fd_table_destroy(struct fd_table *ft); -void fd_prestats_destroy(struct fd_prestats *pt); +bool +fd_table_init(struct fd_table *); +bool +fd_table_insert_existing(struct fd_table *, __wasi_fd_t, os_file_handle, + bool is_stdio); +bool +fd_prestats_init(struct fd_prestats *); +bool +fd_prestats_insert(struct fd_prestats *, const char *, __wasi_fd_t); +bool +argv_environ_init(struct argv_environ_values *argv_environ, char *argv_buf, + size_t argv_buf_size, char **argv_list, size_t argc, + char *environ_buf, size_t environ_buf_size, + char **environ_list, size_t environ_count); +void +argv_environ_destroy(struct argv_environ_values *argv_environ); +void +fd_table_destroy(struct fd_table *ft); +void +fd_prestats_destroy(struct fd_prestats *pt); + +bool +addr_pool_init(struct addr_pool *); +bool +addr_pool_insert(struct addr_pool *, const char *, uint8 mask); +bool +addr_pool_search(struct addr_pool *, const char *); +void +addr_pool_destroy(struct addr_pool *); #endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h index c46190a853..2d40bc3aae 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -12,81 +14,85 @@ #ifndef QUEUE_H #define QUEUE_H -#include - // LIST: Double-linked list. #define LIST_HEAD(name, type) \ - struct name { \ - struct type *l_first; \ - } + struct name { \ + struct type *l_first; \ + } + +/* clang-format off */ #define LIST_HEAD_INITIALIZER(head) \ - { NULL } + { NULL } +/* clang-format on */ -#define LIST_ENTRY(type) \ - struct { \ - struct type *l_next; \ - struct type **l_prev; \ - } +#define LIST_ENTRY(type) \ + struct { \ + struct type *l_next; \ + struct type **l_prev; \ + } #define LIST_FOREACH(var, head, field) \ - for ((var) = (head)->l_first; (var) != NULL; (var) = (var)->field.l_next) -#define LIST_INIT(head) \ - do { \ - (head)->l_first = NULL; \ - } while (0) -#define LIST_INSERT_HEAD(head, element, field) \ - do { \ - (element)->field.l_next = (head)->l_first; \ - if ((head)->l_first != NULL) \ - (head)->l_first->field.l_prev = &(element)->field.l_next; \ - (head)->l_first = (element); \ - (element)->field.l_prev = &(head)->l_first; \ - } while (0) -#define LIST_REMOVE(element, field) \ - do { \ - if ((element)->field.l_next != NULL) \ - (element)->field.l_next->field.l_prev = (element)->field.l_prev; \ - *(element)->field.l_prev = (element)->field.l_next; \ - } while (0) + for ((var) = (head)->l_first; (var) != NULL; (var) = (var)->field.l_next) + +#define LIST_INIT(head) \ + do { \ + (head)->l_first = NULL; \ + } while (0) + +#define LIST_INSERT_HEAD(head, element, field) \ + do { \ + (element)->field.l_next = (head)->l_first; \ + if ((head)->l_first != NULL) \ + (head)->l_first->field.l_prev = &(element)->field.l_next; \ + (head)->l_first = (element); \ + (element)->field.l_prev = &(head)->l_first; \ + } while (0) + +#define LIST_REMOVE(element, field) \ + do { \ + if ((element)->field.l_next != NULL) \ + (element)->field.l_next->field.l_prev = (element)->field.l_prev; \ + *(element)->field.l_prev = (element)->field.l_next; \ + } while (0) // TAILQ: Double-linked list with tail pointer. #define TAILQ_HEAD(name, type) \ - struct name { \ - struct type *t_first; \ - struct type **t_last; \ - } + struct name { \ + struct type *t_first; \ + struct type **t_last; \ + } -#define TAILQ_ENTRY(type) \ - struct { \ - struct type *t_next; \ - struct type **t_prev; \ - } +#define TAILQ_ENTRY(type) \ + struct { \ + struct type *t_next; \ + struct type **t_prev; \ + } #define TAILQ_EMPTY(head) ((head)->t_first == NULL) #define TAILQ_FIRST(head) ((head)->t_first) #define TAILQ_FOREACH(var, head, field) \ - for ((var) = (head)->t_first; (var) != NULL; (var) = (var)->field.t_next) -#define TAILQ_INIT(head) \ - do { \ - (head)->t_first = NULL; \ - (head)->t_last = &(head)->t_first; \ - } while (0) -#define TAILQ_INSERT_TAIL(head, elm, field) \ - do { \ - (elm)->field.t_next = NULL; \ - (elm)->field.t_prev = (head)->t_last; \ - *(head)->t_last = (elm); \ - (head)->t_last = &(elm)->field.t_next; \ - } while (0) -#define TAILQ_REMOVE(head, element, field) \ - do { \ - if ((element)->field.t_next != NULL) \ - (element)->field.t_next->field.t_prev = (element)->field.t_prev; \ - else \ - (head)->t_last = (element)->field.t_prev; \ - *(element)->field.t_prev = (element)->field.t_next; \ - } while (0) + for ((var) = (head)->t_first; (var) != NULL; (var) = (var)->field.t_next) +#define TAILQ_INIT(head) \ + do { \ + (head)->t_first = NULL; \ + (head)->t_last = &(head)->t_first; \ + } while (0) +#define TAILQ_INSERT_TAIL(head, elm, field) \ + do { \ + (elm)->field.t_next = NULL; \ + (elm)->field.t_prev = (head)->t_last; \ + *(head)->t_last = (elm); \ + (head)->t_last = &(elm)->field.t_next; \ + } while (0) +#define TAILQ_REMOVE(head, element, field) \ + do { \ + if ((element)->field.t_next != NULL) \ + (element)->field.t_next->field.t_prev = (element)->field.t_prev; \ + else \ + (head)->t_last = (element)->field.t_prev; \ + *(element)->field.t_prev = (element)->field.t_next; \ + } while (0) #endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c index 41618bd58d..665a9de918 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -10,63 +12,112 @@ // Copyright (c) 2016 Nuxi, https://nuxi.nl/ #include "ssp_config.h" - -#include -#include -#include -#include -#include -#include -#include - +#include "bh_platform.h" +#include "libc_errno.h" #include "random.h" #if CONFIG_HAS_ARC4RANDOM_BUF -void random_buf(void *buf, size_t len) { - arc4random_buf(buf, len); +__wasi_errno_t +random_buf(void *buf, size_t len) +{ + arc4random_buf(buf, len); + return __WASI_ESUCCESS; } #elif CONFIG_HAS_GETRANDOM +#ifndef BH_PLATFORM_LINUX_SGX #include +#endif + +__wasi_errno_t +random_buf(void *buf, size_t len) +{ + for (;;) { + ssize_t x = getrandom(buf, len, 0); + if (x < 0) { + if (errno == EINTR) + continue; + return convert_errno(errno); + } + if ((size_t)x == len) + break; + buf = (void *)((unsigned char *)buf + x); + len -= (size_t)x; + } + return __WASI_ESUCCESS; +} -void random_buf(void *buf, size_t len) { - for (;;) { - ssize_t x = getrandom(buf, len, 0); - if (x < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "getrandom failed: %s", strerror(errno)); - abort(); - } - if ((size_t)x == len) - return; - buf = (void *)((unsigned char *)buf + x); - len -= x; - } +#elif defined(BH_PLATFORM_WINDOWS) + +#include +#pragma comment(lib, "Bcrypt.lib") + +__wasi_errno_t +random_buf(void *buf, size_t len) +{ + NTSTATUS ret = + BCryptGenRandom(NULL, buf, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG); + + // Since we pass NULL for the algorithm handle, the only way BCryptGenRandom + // can fail is if one of the parameters is invalid + // (STATUS_INVALID_PARAMETER). + return ret ? __WASI_EINVAL : __WASI_ESUCCESS; +} + +#elif defined(BH_PLATFORM_ZEPHYR) +#include +// Maybe having an OS abstraction api would be a good idea +// because every platform is implementing this function. +// we could have a function like `os_random_buf` +// and call `os_random_buf.` in the SSP wrapper `random_buf`. + +__wasi_errno_t +random_buf(void *buf, size_t len) +{ + sys_rand_get(buf, len); + return __WASI_ESUCCESS; } #else -static int urandom; +static int urandom = -1; +static __wasi_errno_t urandom_error = __WASI_ESUCCESS; -static void open_urandom(void) { - urandom = open("/dev/urandom", O_RDONLY); - if (urandom < 0) { - fputs("Failed to open /dev/urandom\n", stderr); - abort(); - } +static void +open_urandom(void) +{ + urandom = open("/dev/urandom", O_RDONLY); + if (urandom < 0) + urandom_error = convert_errno(errno); } -void random_buf(void *buf, size_t len) { - static pthread_once_t open_once = PTHREAD_ONCE_INIT; - pthread_once(&open_once, open_urandom); +__wasi_errno_t +random_buf(void *buf, size_t len) +{ + static pthread_once_t open_once = PTHREAD_ONCE_INIT; + int pthread_ret = pthread_once(&open_once, open_urandom); + + if (pthread_ret != 0) + return convert_errno(pthread_ret); + + if (urandom < 0) + return urandom_error; + + size_t bytes_read = 0; + + while (bytes_read < len) { + ssize_t bytes_read_now = + read(urandom, buf + bytes_read, len - bytes_read); - if ((size_t)read(urandom, buf, len) != len) { - fputs("Short read on /dev/urandom\n", stderr); - abort(); - } + if (bytes_read_now < 0) + return convert_errno(errno); + + bytes_read += (size_t)bytes_read_now; + } + + return __WASI_ESUCCESS; } #endif @@ -78,15 +129,23 @@ void random_buf(void *buf, size_t len) { // arc4random() until it lies within the range [2^k % upper, 2^k). As // this range has length k * upper, we can safely obtain a number // without any modulo bias. -uintmax_t random_uniform(uintmax_t upper) { - // Compute 2^k % upper - // == (2^k - upper) % upper - // == -upper % upper. - uintmax_t lower = -upper % upper; - for (;;) { - uintmax_t value; - random_buf(&value, sizeof(value)); - if (value >= lower) - return value % upper; - } +__wasi_errno_t +random_uniform(uintmax_t upper, uintmax_t *out) +{ + // Compute 2^k % upper + // == (2^k - upper) % upper + // == -upper % upper. + uintmax_t lower = -upper % upper; + for (;;) { + uintmax_t value; + __wasi_errno_t error = random_buf(&value, sizeof(value)); + + if (error != __WASI_ESUCCESS) + return error; + + if (value >= lower) { + *out = value % upper; + return error; + } + } } diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h index d87a292cf0..7cd94d74db 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -12,9 +14,12 @@ #ifndef RANDOM_H #define RANDOM_H -#include +#include "bh_platform.h" -void random_buf(void *, size_t); -uintmax_t random_uniform(uintmax_t); +__wasi_errno_t +random_buf(void *, size_t); + +__wasi_errno_t +random_uniform(uintmax_t upper, uintmax_t *out); #endif 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 0d516e2d1c..79b16cc175 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 @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -12,36 +14,155 @@ #ifndef REFCOUNT_H #define REFCOUNT_H -#include -#include -#include - +#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 + +#if CONFIG_HAS_STD_ATOMIC != 0 -// Simple reference counter. +#include + +/* Simple reference counter. */ struct LOCKABLE refcount { - atomic_uint count; + atomic_uint count; }; -#define PRODUCES(...) LOCKS_SHARED(__VA_ARGS__) NO_LOCK_ANALYSIS -#define CONSUMES(...) UNLOCKS(__VA_ARGS__) NO_LOCK_ANALYSIS +/* Initialize the reference counter. */ +static inline void +refcount_init(struct refcount *r, unsigned int count) PRODUCES(*r) +{ + atomic_init(&r->count, count); +} -// Initialize the reference counter. -static void refcount_init(struct refcount *r, unsigned int count) PRODUCES(*r) { - atomic_init(&r->count, count); +/* Increment the reference counter. */ +static inline void +refcount_acquire(struct refcount *r) PRODUCES(*r) +{ + atomic_fetch_add_explicit(&r->count, 1, memory_order_acquire); } -// Increment the reference counter. -static inline void refcount_acquire(struct refcount *r) PRODUCES(*r) { - atomic_fetch_add_explicit(&r->count, 1, memory_order_acquire); +/* Decrement the reference counter, returning whether the reference + dropped to zero. */ +static inline bool +refcount_release(struct refcount *r) CONSUMES(*r) +{ + int old = + (int)atomic_fetch_sub_explicit(&r->count, 1, memory_order_release); + bh_assert(old != 0 && "Reference count becoming negative"); + return old == 1; } -// Decrement the reference counter, returning whether the reference -// dropped to zero. -static inline bool refcount_release(struct refcount *r) CONSUMES(*r) { - int old = (int)atomic_fetch_sub_explicit(&r->count, 1, memory_order_release); - assert(old != 0 && "Reference count becoming negative"); - return old == 1; +#elif defined(BH_PLATFORM_LINUX_SGX) + +#include + +/* Simple reference counter. */ +struct refcount { + sgx_spinlock_t lock; + unsigned int count; +}; + +/* Initialize the reference counter. */ +static inline void +refcount_init(struct refcount *r, unsigned int count) +{ + r->lock = SGX_SPINLOCK_INITIALIZER; + r->count = count; +} + +/* Increment the reference counter. */ +static inline void +refcount_acquire(struct refcount *r) +{ + sgx_spin_lock(&r->lock); + r->count++; + sgx_spin_unlock(&r->lock); +} + +/* Decrement the reference counter, returning whether the reference + dropped to zero. */ +static inline bool +refcount_release(struct refcount *r) +{ + int old; + sgx_spin_lock(&r->lock); + old = (int)r->count; + r->count--; + sgx_spin_unlock(&r->lock); + bh_assert(old != 0 && "Reference count becoming negative"); + return old == 1; } -#endif +#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) */ + +#elif defined(_MSC_VER) + +/* Simple reference counter. */ +struct LOCKABLE refcount { + LONG count; +}; + +/* Initialize the reference counter. */ +static inline void +refcount_init(struct refcount *r, unsigned int count) +{ + InterlockedExchange(&r->count, (LONG)count); +} + +/* Increment the reference counter. */ +static inline void +refcount_acquire(struct refcount *r) +{ + InterlockedIncrement(&r->count); +} + +/* Decrement the reference counter, returning whether the reference + dropped to zero. */ +static inline bool +refcount_release(struct refcount *r) +{ + return InterlockedDecrement(&r->count) == 0 ? true : false; +} + +#else /* else of CONFIG_HAS_STD_ATOMIC */ +#error "Reference counter isn't implemented" +#endif /* end of CONFIG_HAS_STD_ATOMIC */ + +#endif /* end of REFCOUNT_H */ diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h index 69349e45ca..41ff56f27a 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -12,6 +14,8 @@ #ifndef RIGHTS_H #define RIGHTS_H +/* clang-format off */ + #define RIGHTS_ALL \ (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_READ | \ __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ @@ -28,7 +32,13 @@ __WASI_RIGHT_FD_FILESTAT_SET_SIZE | \ __WASI_RIGHT_PATH_SYMLINK | __WASI_RIGHT_PATH_UNLINK_FILE | \ __WASI_RIGHT_PATH_REMOVE_DIRECTORY | \ - __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_CONNECT | \ + __WASI_RIGHT_SOCK_LISTEN | __WASI_RIGHT_SOCK_BIND | \ + __WASI_RIGHT_SOCK_ACCEPT | __WASI_RIGHT_SOCK_RECV | \ + __WASI_RIGHT_SOCK_SEND | __WASI_RIGHT_SOCK_ADDR_LOCAL | \ + __WASI_RIGHT_SOCK_ADDR_REMOTE | __WASI_RIGHT_SOCK_RECV_FROM | \ + __WASI_RIGHT_SOCK_SEND_TO) + // Block and character device interaction is outside the scope of // CloudABI. Simply allow everything. @@ -37,6 +47,19 @@ #define RIGHTS_CHARACTER_DEVICE_BASE RIGHTS_ALL #define RIGHTS_CHARACTER_DEVICE_INHERITING RIGHTS_ALL +#define RIGHTS_STDIN \ + (__WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_WRITE | \ + __WASI_RIGHT_POLL_FD_READWRITE) + +#define RIGHTS_STDOUT \ + (__WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_DATASYNC | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_SYNC | \ + __WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_WRITE | \ + __WASI_RIGHT_POLL_FD_READWRITE) + +#define RIGHTS_STDERR RIGHTS_STDOUT + // Only allow directory operations on directories. Directories can only // yield file descriptors to other directories and files. #define RIGHTS_DIRECTORY_BASE \ @@ -67,10 +90,15 @@ #define RIGHTS_REGULAR_FILE_INHERITING 0 // Operations that apply to sockets and socket pairs. -#define RIGHTS_SOCKET_BASE \ - (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ - __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ - __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) +#define RIGHTS_SOCKET_BASE \ + (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_CONNECT | \ + __WASI_RIGHT_SOCK_LISTEN | __WASI_RIGHT_SOCK_BIND | \ + __WASI_RIGHT_SOCK_ACCEPT | __WASI_RIGHT_SOCK_RECV | \ + __WASI_RIGHT_SOCK_SEND | __WASI_RIGHT_SOCK_ADDR_LOCAL | \ + __WASI_RIGHT_SOCK_ADDR_REMOTE | __WASI_RIGHT_SOCK_RECV_FROM | \ + __WASI_RIGHT_SOCK_SEND_TO) #define RIGHTS_SOCKET_INHERITING RIGHTS_ALL // Operations that apply to TTYs. @@ -80,4 +108,6 @@ __WASI_RIGHT_POLL_FD_READWRITE) #define RIGHTS_TTY_INHERITING 0 +/* clang-format on */ + #endif diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h deleted file mode 100644 index 16f5d575ca..0000000000 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h +++ /dev/null @@ -1,17 +0,0 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. -// -// Significant parts of this file are derived from cloudabi-utils. See -// https://github.com/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE -// for license information. -// -// The upstream file contains the following copyright notice: -// -// Copyright (c) 2016 Nuxi, https://nuxi.nl/ - -#ifndef SIGNALS_H -#define SIGNALS_H - -void signals_init(void); - -#endif 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 d4af407749..9eeea99895 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 @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -12,9 +14,11 @@ #ifndef SSP_CONFIG_H #define SSP_CONFIG_H -#include +#include "bh_platform.h" +#include "gnuc.h" -#if defined(__FreeBSD__) || defined(__APPLE__) +#if defined(__FreeBSD__) || defined(__APPLE__) \ + || ((defined(ANDROID) || defined(__ANDROID__)) && (__ANDROID_API__ < 28)) #define CONFIG_HAS_ARC4RANDOM_BUF 1 #else #define CONFIG_HAS_ARC4RANDOM_BUF 0 @@ -22,79 +26,90 @@ // On Linux, prefer to use getrandom, though it isn't available in // GLIBC before 2.25. -#if defined(__linux__) && \ - (!defined(__GLIBC__) || \ - __GLIBC__ > 2 || \ - (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +// +// NuttX has arc4random_buf, getrandom, and /dev/urandom. +// We prefer getrandom here because it has the best chance to be usable. +// - Our /dev/urandom usage (keep the open descriptor in a global variable) +// is not compatible with NuttX flat memory model. +// - arc4random_buf is only available with CONFIG_CRYPTO_RANDOM_POOL=y. +#if defined(__NuttX__) \ + || ((defined(__linux__) || defined(ESP_PLATFORM) \ + || defined(__COSMOPOLITAN__)) \ + && (!defined(__GLIBC__) || __GLIBC__ > 2 \ + || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) #define CONFIG_HAS_GETRANDOM 1 #else #define CONFIG_HAS_GETRANDOM 0 #endif -#if defined(__CloudABI__) +#if defined(__CloudABI__) || defined(BH_PLATFORM_FREERTOS) \ + || defined(BH_PLATFORM_ZEPHYR) #define CONFIG_HAS_CAP_ENTER 1 #else #define CONFIG_HAS_CAP_ENTER 0 #endif -#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__EMSCRIPTEN__) +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__EMSCRIPTEN__) \ + && !defined(ESP_PLATFORM) && !defined(DISABLE_CLOCK_NANOSLEEP) \ + && !defined(BH_PLATFORM_FREERTOS) && !defined(BH_PLATFORM_ZEPHYR) #define CONFIG_HAS_CLOCK_NANOSLEEP 1 #else #define CONFIG_HAS_CLOCK_NANOSLEEP 0 #endif -#if !defined(__APPLE__) && !defined(__FreeBSD__) -#define CONFIG_HAS_FDATASYNC 1 -#else -#define CONFIG_HAS_FDATASYNC 0 -#endif - -#ifndef __CloudABI__ -#define CONFIG_HAS_ISATTY 1 -#else -#define CONFIG_HAS_ISATTY 0 -#endif - -#ifndef __APPLE__ -#define CONFIG_HAS_POSIX_FALLOCATE 1 -#else -#define CONFIG_HAS_POSIX_FALLOCATE 0 -#endif - -#ifndef __APPLE__ -#define CONFIG_HAS_PREADV 1 -#else -#define CONFIG_HAS_PREADV 0 -#endif - #if defined(__APPLE__) || defined(__CloudABI__) #define CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1 #else #define CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 0 #endif -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(BH_PLATFORM_LINUX_SGX) && !defined(_WIN32) \ + && !defined(__COSMOPOLITAN__) && !defined(BH_PLATFORM_FREERTOS) \ + && !defined(BH_PLATFORM_ZEPHYR) #define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 1 #else #define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 0 #endif -#ifndef __APPLE__ -#define CONFIG_HAS_PWRITEV 1 +#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_PWRITEV 0 +#define CONFIG_HAS_STD_ATOMIC 0 #endif -#ifdef __APPLE__ -#define st_atimespec st_atim -#define st_mtimespec st_mtim -#define st_ctimespec st_ctim -#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) */ + +#elif defined(_MSC_VER) + +#define CONFIG_HAS_STD_ATOMIC 0 -#ifdef __APPLE__ -#define CONFIG_TLS_USE_GSBASE 1 #else -#define CONFIG_TLS_USE_GSBASE 0 -#endif -#endif +#define CONFIG_HAS_STD_ATOMIC 1 + +#endif /* end of defined(__clang__) */ + +#else /* else of !defined(BH_PLATFORM_LINUX_SGX) */ + +#define CONFIG_HAS_STD_ATOMIC 0 + +#endif /* end of !defined(BH_PLATFORM_LINUX_SGX) */ + +#endif /* end of SSP_CONFIG_H */ diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c index 87b9ba75f8..858d8d5e40 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -10,24 +12,36 @@ // Copyright (c) 2016 Nuxi, https://nuxi.nl/ #include "ssp_config.h" +#include "bh_platform.h" +#include "str.h" -#include -#include -#include +static char * +bh_strndup(const char *s, size_t n) +{ + size_t l = strnlen(s, n); + char *s1 = wasm_runtime_malloc((uint32)(l + 1)); -#include "str.h" + if (!s1) + return NULL; + bh_memcpy_s(s1, (uint32)(l + 1), s, (uint32)l); + s1[l] = 0; + return s1; +} + +char * +str_nullterminate(const char *s, size_t len) +{ + /* Copy string */ + char *ret = bh_strndup(s, len); -char *str_nullterminate(const char *s, size_t len) { - // Copy string. - char *ret = strndup(s, len); - if (ret == NULL) - return NULL; + if (ret == NULL) + return NULL; - // Ensure that it contains no null bytes within. - if (strlen(ret) != len) { - free(ret); - errno = EILSEQ; - return NULL; - } - return ret; + /* Ensure that it contains no null bytes within */ + if (strlen(ret) != len) { + wasm_runtime_free(ret); + errno = EILSEQ; + return NULL; + } + return ret; } diff --git a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h index 8399415e73..7d633e5c8b 100644 --- a/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h +++ b/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h @@ -1,8 +1,10 @@ -// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://github.com/CraneStation/wasmtime/blob/master/LICENSE for license information. +// 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/CraneStation/wasmtime/blob/master/lib/wasi/sandboxed-system-primitives/src/LICENSE +// 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: @@ -14,6 +16,7 @@ #include "ssp_config.h" -char *str_nullterminate(const char *, size_t); +char * +str_nullterminate(const char *, size_t); #endif diff --git a/core/iwasm/libraries/shared-heap/shared_heap.cmake b/core/iwasm/libraries/shared-heap/shared_heap.cmake new file mode 100644 index 0000000000..ec91dabcd0 --- /dev/null +++ b/core/iwasm/libraries/shared-heap/shared_heap.cmake @@ -0,0 +1,8 @@ +# Copyright (C) 2024 Xiaomi Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_SHARED_HEAP ${CMAKE_CURRENT_LIST_DIR}) +add_definitions (-DWASM_ENABLE_SHARED_HEAP=1) +include_directories(${LIB_SHARED_HEAP_DIR}) +file (GLOB source_all ${LIB_SHARED_HEAP}/*.c) +set (LIB_SHARED_HEAP_SOURCE ${source_all}) \ No newline at end of file diff --git a/core/iwasm/libraries/shared-heap/shared_heap_wrapper.c b/core/iwasm/libraries/shared-heap/shared_heap_wrapper.c new file mode 100644 index 0000000000..b7b78307ae --- /dev/null +++ b/core/iwasm/libraries/shared-heap/shared_heap_wrapper.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" +#include "../common/wasm_runtime_common.h" +/* clang-format off */ +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + +#define module_shared_malloc(size, p_native_addr) \ + wasm_runtime_shared_heap_malloc(module_inst, size, p_native_addr) + +#define module_shared_free(offset) \ + wasm_runtime_shared_heap_free(module_inst, offset) +/* clang-format on */ + +static uint32 +shared_heap_malloc_wrapper(wasm_exec_env_t exec_env, uint32 size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + return (uint32)module_shared_malloc((uint64)size, NULL); +} + +static void +shared_heap_free_wrapper(wasm_exec_env_t exec_env, void *ptr) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_native_addr(ptr, (uint64)sizeof(uintptr_t))) { + LOG_WARNING("Invalid app address"); + return; + } + + module_shared_free(addr_native_to_app(ptr)); +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } +/* clang-format on */ + +static NativeSymbol native_symbols_shared_heap[] = { + REG_NATIVE_FUNC(shared_heap_malloc, "(i)i"), + REG_NATIVE_FUNC(shared_heap_free, "(*)"), +}; + +uint32 +get_lib_shared_heap_export_apis(NativeSymbol **p_shared_heap_apis) +{ + *p_shared_heap_apis = native_symbols_shared_heap; + return sizeof(native_symbols_shared_heap) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/simde/simde.cmake b/core/iwasm/libraries/simde/simde.cmake new file mode 100644 index 0000000000..e9844dd872 --- /dev/null +++ b/core/iwasm/libraries/simde/simde.cmake @@ -0,0 +1,28 @@ +# Copyright (C) 2024 Amazon Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# simde is a header only library + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +set (LIB_SIMDE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_SIMDE=1) + +include_directories(${LIB_SIMDE_DIR} ${LIB_SIMDE_DIR}/simde) + +include(FetchContent) + +FetchContent_Declare( + simde + GIT_REPOSITORY https://github.com/simd-everywhere/simde + GIT_TAG v0.8.2 +) + +message("-- Fetching simde ..") +FetchContent_MakeAvailable(simde) +include_directories(SYSTEM "${simde_SOURCE_DIR}") diff --git a/core/iwasm/libraries/template/lib_export_template.c b/core/iwasm/libraries/template/lib_export_template.c deleted file mode 100644 index 35159728b4..0000000000 --- a/core/iwasm/libraries/template/lib_export_template.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include -#include "lib-export.h" - -/* TODO: use macro EXPORT_WASM_API() or EXPORT_WASM_API2() to add functions to register. */ - -NativeSymbol extended_native_symbol_defs[] = { - -/*EXPORT_WASM_API(publish_event)*/ - -}; diff --git a/core/iwasm/libraries/thread-mgr/SConscript b/core/iwasm/libraries/thread-mgr/SConscript new file mode 100644 index 0000000000..65f561ae28 --- /dev/null +++ b/core/iwasm/libraries/thread-mgr/SConscript @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() + +src = Split(''' +thread_manager.c +''') + +CPPPATH = [cwd] + +group = DefineGroup('iwasm_lib_thread_mgr', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c new file mode 100644 index 0000000000..563a9098e1 --- /dev/null +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -0,0 +1,1559 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "thread_manager.h" +#include "../common/wasm_c_api_internal.h" + +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 +#include "debug_engine.h" +#endif + +typedef struct { + bh_list_link l; + void (*destroy_cb)(WASMCluster *); +} DestroyCallBackNode; + +static bh_list destroy_callback_list_head; +static bh_list *const destroy_callback_list = &destroy_callback_list_head; + +static bh_list cluster_list_head; +static bh_list *const cluster_list = &cluster_list_head; +static korp_mutex cluster_list_lock; + +typedef void (*list_visitor)(void *, void *); + +static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM; + +/* Set the maximum thread number, if this function is not called, + the max thread num is defined by CLUSTER_MAX_THREAD_NUM */ +void +wasm_cluster_set_max_thread_num(uint32 num) +{ + if (num > 0) + cluster_max_thread_num = num; +} + +bool +thread_manager_init() +{ + if (bh_list_init(cluster_list) != 0) + return false; + if (os_mutex_init(&cluster_list_lock) != 0) + return false; + return true; +} + +void +thread_manager_destroy() +{ + WASMCluster *cluster = bh_list_first_elem(cluster_list); + WASMCluster *next; + while (cluster) { + next = bh_list_elem_next(cluster); + wasm_cluster_destroy(cluster); + cluster = next; + } + wasm_cluster_cancel_all_callbacks(); + os_mutex_destroy(&cluster_list_lock); +} + +static void +traverse_list(bh_list *l, list_visitor visitor, void *user_data) +{ + void *next, *node = bh_list_first_elem(l); + while (node) { + next = bh_list_elem_next(node); + visitor(node, user_data); + node = next; + } +} + +/* Assumes cluster->lock is locked */ +static bool +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 not have any locks */ +bool +wasm_cluster_allocate_aux_stack(WASMExecEnv *exec_env, uint64 *p_start, + uint32 *p_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); + uint64 stack_end; + + stack_end = wasm_runtime_module_malloc_internal(module_inst, exec_env, + cluster->stack_size, NULL); + *p_start = stack_end + cluster->stack_size; + *p_size = cluster->stack_size; + + return stack_end != 0; +#else + uint32 i; + + /* If the module doesn't have aux stack info, + it can't create any threads */ + + os_mutex_lock(&cluster->lock); + if (!cluster->stack_segment_occupied) { + os_mutex_unlock(&cluster->lock); + return false; + } + + for (i = 0; i < cluster_max_thread_num; i++) { + if (!cluster->stack_segment_occupied[i]) { + if (p_start) + *p_start = cluster->stack_tops[i]; + if (p_size) + *p_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 not have any locks */ +bool +wasm_cluster_free_aux_stack(WASMExecEnv *exec_env, uint64 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_internal(module_inst, exec_env, + start - cluster->stack_size); + + return true; +#else + uint32 i; + + os_mutex_lock(&cluster->lock); + for (i = 0; i < cluster_max_thread_num; i++) { + if (start == cluster->stack_tops[i]) { + cluster->stack_segment_occupied[i] = false; + os_mutex_unlock(&cluster->lock); + return true; + } + } + os_mutex_unlock(&cluster->lock); + return false; +#endif +} + +WASMCluster * +wasm_cluster_create(WASMExecEnv *exec_env) +{ + WASMCluster *cluster; + uint32 aux_stack_size; + uint64 aux_stack_start; + + bh_assert(exec_env->cluster == NULL); + if (!(cluster = wasm_runtime_malloc(sizeof(WASMCluster)))) { + LOG_ERROR("thread manager error: failed to allocate memory"); + return NULL; + } + memset(cluster, 0, sizeof(WASMCluster)); + + exec_env->cluster = cluster; + + bh_list_init(&cluster->exec_env_list); + bh_list_insert(&cluster->exec_env_list, exec_env); + if (os_mutex_init(&cluster->lock) != 0) { + wasm_runtime_free(cluster); + LOG_ERROR("thread manager error: failed to init mutex"); + return NULL; + } + + /* 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 */ + os_mutex_lock(&cluster_list_lock); + if (bh_list_insert(cluster_list, cluster) != 0) { + os_mutex_unlock(&cluster_list_lock); + goto fail; + } + os_mutex_unlock(&cluster_list_lock); + + 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 */ + if (!wasm_exec_env_set_aux_stack(exec_env, aux_stack_start, + cluster->stack_size)) + goto fail; + +#if WASM_ENABLE_HEAP_AUX_STACK_ALLOCATION == 0 + if (cluster_max_thread_num != 0) { + uint64 total_size = cluster_max_thread_num * sizeof(uint64); + uint32 i; + if (total_size >= UINT32_MAX + || !(cluster->stack_tops = + wasm_runtime_malloc((uint32)total_size))) { + goto fail; + } + memset(cluster->stack_tops, 0, (uint32)total_size); + + if (!(cluster->stack_segment_occupied = + wasm_runtime_malloc(cluster_max_thread_num * sizeof(bool)))) { + goto fail; + } + memset(cluster->stack_segment_occupied, 0, + cluster_max_thread_num * sizeof(bool)); + + /* Reserve space for main instance */ + aux_stack_start -= cluster->stack_size; + + for (i = 0; i < cluster_max_thread_num; i++) { + cluster->stack_tops[i] = + aux_stack_start - (uint64)cluster->stack_size * i; + } + } +#endif + + os_mutex_lock(&cluster_list_lock); + if (bh_list_insert(cluster_list, cluster) != 0) { + os_mutex_unlock(&cluster_list_lock); + goto fail; + } + os_mutex_unlock(&cluster_list_lock); + + return cluster; + +fail: + if (cluster) + wasm_cluster_destroy(cluster); + + return NULL; +} + +static void +destroy_cluster_visitor(void *node, void *user_data) +{ + DestroyCallBackNode *destroy_node = (DestroyCallBackNode *)node; + WASMCluster *cluster = (WASMCluster *)user_data; + + destroy_node->destroy_cb(cluster); +} + +void +wasm_cluster_destroy(WASMCluster *cluster) +{ + traverse_list(destroy_callback_list, destroy_cluster_visitor, + (void *)cluster); + + /* Remove the cluster from the cluster list */ + os_mutex_lock(&cluster_list_lock); + bh_list_remove(cluster_list, cluster); + os_mutex_unlock(&cluster_list_lock); + + 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); +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + bh_vector_destroy(&cluster->exception_frames); +#endif + + wasm_runtime_free(cluster); +} + +static void +free_node_visitor(void *node, void *user_data) +{ + wasm_runtime_free(node); +} + +void +wasm_cluster_cancel_all_callbacks() +{ + traverse_list(destroy_callback_list, free_node_visitor, NULL); + bh_list_init(destroy_callback_list); +} + +WASMCluster * +wasm_exec_env_get_cluster(WASMExecEnv *exec_env) +{ + return exec_env->cluster; +} + +/* 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; + + if (cluster->exec_env_list.len == cluster_max_thread_num + 1) { + LOG_ERROR("thread manager error: " + "maximum number of threads exceeded"); + ret = false; + } + + if (ret && bh_list_insert(&cluster->exec_env_list, exec_env) != 0) + ret = false; + + return ret; +} + +static bool +wasm_cluster_del_exec_env_internal(WASMCluster *cluster, WASMExecEnv *exec_env, + bool can_destroy_cluster) +{ + bool ret = true; + bh_assert(exec_env->cluster == cluster); + +#if WASM_ENABLE_DEBUG_INTERP != 0 + /* Wait for debugger control thread to process the + stop event of this thread */ + if (cluster->debug_inst) { + /* lock the debug_inst->wait_lock so + other threads can't fire stop events */ + os_mutex_lock(&cluster->debug_inst->wait_lock); + while (cluster->debug_inst->stopped_thread == exec_env) { + /* either wakes up by signal or by 1-second timeout */ + os_cond_reltimedwait(&cluster->debug_inst->wait_cond, + &cluster->debug_inst->wait_lock, 1000000); + } + os_mutex_unlock(&cluster->debug_inst->wait_lock); + } +#endif + if (bh_list_remove(&cluster->exec_env_list, exec_env) != 0) + ret = false; + + if (can_destroy_cluster) { + if (cluster->exec_env_list.len == 0) { + /* exec_env_list empty, destroy the cluster */ + wasm_cluster_destroy(cluster); + } + } + else { + /* Don't destroy cluster as cluster->lock is being used */ + } + + return ret; +} + +/* The caller should lock cluster->lock for thread safety */ +bool +wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) +{ + return wasm_cluster_del_exec_env_internal(cluster, exec_env, true); +} + +static WASMExecEnv * +wasm_cluster_search_exec_env(WASMCluster *cluster, + WASMModuleInstanceCommon *module_inst) +{ + WASMExecEnv *node = NULL; + + os_mutex_lock(&cluster->lock); + node = bh_list_first_elem(&cluster->exec_env_list); + while (node) { + if (node->module_inst == module_inst) { + os_mutex_unlock(&cluster->lock); + return node; + } + node = bh_list_elem_next(node); + } + + os_mutex_unlock(&cluster->lock); + return NULL; +} + +/* search the global cluster list to find if the given + module instance have a corresponding exec_env */ +WASMExecEnv * +wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst) +{ + WASMCluster *cluster = NULL; + WASMExecEnv *exec_env = NULL; + + os_mutex_lock(&cluster_list_lock); + cluster = bh_list_first_elem(cluster_list); + while (cluster) { + exec_env = wasm_cluster_search_exec_env(cluster, module_inst); + if (exec_env) { + os_mutex_unlock(&cluster_list_lock); + return exec_env; + } + cluster = bh_list_elem_next(cluster); + } + + os_mutex_unlock(&cluster_list_lock); + return NULL; +} + +WASMExecEnv * +wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_module_t module; + wasm_module_inst_t new_module_inst; + WASMExecEnv *new_exec_env; + uint32 aux_stack_size; + uint64 aux_stack_start; + uint32 stack_size = 8192; + struct InstantiationArgs2 args; + + if (!module_inst || !(module = wasm_exec_env_get_module(exec_env))) { + return NULL; + } + + wasm_runtime_instantiation_args_set_defaults(&args); + wasm_runtime_instantiation_args_set_default_stack_size(&args, stack_size); + if (!(new_module_inst = wasm_runtime_instantiate_internal( + module, module_inst, exec_env, &args, NULL, 0))) { + return NULL; + } + + /* Set custom_data to new module instance */ + wasm_runtime_set_custom_data_internal( + new_module_inst, wasm_runtime_get_custom_data(module_inst)); + + wasm_native_inherit_contexts(new_module_inst, module_inst); + + if (!(wasm_cluster_dup_c_api_imports(new_module_inst, module_inst))) { + goto fail1; + } + + if (!wasm_cluster_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 fail1; + } + + os_mutex_lock(&cluster->lock); + + if (cluster->has_exception || cluster->processing) { + goto fail2; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + stack_size = + ((WASMModuleInstance *)module_inst)->default_wasm_stack_size; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + stack_size = + ((AOTModuleInstance *)module_inst)->default_wasm_stack_size; + } +#endif + + new_exec_env = wasm_exec_env_create_internal(new_module_inst, + exec_env->wasm_stack_size); + if (!new_exec_env) { + 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; + } + new_exec_env->is_aux_stack_allocated = true; + + /* Inherit suspend_flags of parent thread */ + new_exec_env->suspend_flags.flags = + (exec_env->suspend_flags.flags & WASM_SUSPEND_FLAG_INHERIT_MASK); + + if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) { + goto fail3; + } + + os_mutex_unlock(&cluster->lock); + + return new_exec_env; + +fail3: + wasm_exec_env_destroy_internal(new_exec_env); +fail2: + os_mutex_unlock(&cluster->lock); + /* free the allocated aux stack space */ + wasm_cluster_free_aux_stack(exec_env, aux_stack_start); +fail1: + wasm_runtime_deinstantiate_internal(new_module_inst, true); + + return NULL; +} + +void +wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + bh_assert(cluster != NULL); + WASMExecEnv *exec_env_tls = NULL; + +#ifdef OS_ENABLE_HW_BOUND_CHECK + /* Note: free_aux_stack can execute the module's "free" function + * using the specified exec_env. In case of OS_ENABLE_HW_BOUND_CHECK, + * it needs to match the TLS exec_env if available. (Consider a native + * function which calls wasm_cluster_destroy_spawned_exec_env.) + */ + exec_env_tls = wasm_runtime_get_exec_env_tls(); +#endif + if (exec_env_tls == NULL) { + exec_env_tls = exec_env; + } + + /* Free aux stack space which was allocated in + wasm_cluster_spawn_exec_env */ + bh_assert(exec_env_tls->is_aux_stack_allocated); + wasm_cluster_free_aux_stack(exec_env_tls, + (uint64)exec_env->aux_stack_bottom); + + os_mutex_lock(&cluster->lock); + + /* Remove exec_env */ + wasm_cluster_del_exec_env_internal(cluster, exec_env, false); + /* 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 */ +static void * +thread_manager_start_routine(void *arg) +{ + void *ret; + WASMExecEnv *exec_env = (WASMExecEnv *)arg; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + 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 (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) + & WASM_SUSPEND_FLAG_EXIT) + ret = exec_env->thread_ret_value; + os_mutex_unlock(&exec_env->wait_lock); +#endif + + /* Routine exit */ + +#if WASM_ENABLE_DEBUG_INTERP != 0 + wasm_cluster_thread_exited(exec_env); +#endif + + /* Free aux stack space */ + if (exec_env->is_aux_stack_allocated) + wasm_cluster_free_aux_stack(exec_env, + (uint64)exec_env->aux_stack_bottom); + + 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 */ + } + +#if WASM_ENABLE_PERF_PROFILING != 0 + os_printf("============= Spawned thread ===========\n"); + wasm_runtime_dump_perf_profiling(module_inst); + os_printf("========================================\n"); +#endif + + /* Remove exec_env */ + wasm_cluster_del_exec_env_internal(cluster, exec_env, false); + /* 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; +} + +int32 +wasm_cluster_create_thread(WASMExecEnv *exec_env, + wasm_module_inst_t module_inst, + bool is_aux_stack_allocated, uint64 aux_stack_start, + uint32 aux_stack_size, + void *(*thread_routine)(void *), void *arg) +{ + WASMCluster *cluster; + WASMExecEnv *new_exec_env; + korp_tid tid; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + os_mutex_lock(&cluster->lock); + + if (cluster->has_exception || cluster->processing) { + goto fail1; + } + + new_exec_env = + wasm_exec_env_create_internal(module_inst, exec_env->wasm_stack_size); + if (!new_exec_env) + goto fail1; + + if (is_aux_stack_allocated) { + /* 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->is_aux_stack_allocated = true; + } + else { + /* Disable aux stack */ + new_exec_env->aux_stack_boundary = 0; + new_exec_env->aux_stack_bottom = UINTPTR_MAX; + new_exec_env->is_aux_stack_allocated = false; + } + + /* Inherit suspend_flags of parent thread */ + new_exec_env->suspend_flags.flags = + (exec_env->suspend_flags.flags & WASM_SUSPEND_FLAG_INHERIT_MASK); + + if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) + goto fail2; + + 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)) { + os_mutex_unlock(&new_exec_env->wait_lock); + goto fail3; + } + + /* 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: + wasm_cluster_del_exec_env_internal(cluster, new_exec_env, false); +fail2: + wasm_exec_env_destroy_internal(new_exec_env); +fail1: + os_mutex_unlock(&cluster->lock); + + return -1; +} + +bool +wasm_cluster_dup_c_api_imports(WASMModuleInstanceCommon *module_inst_dst, + const WASMModuleInstanceCommon *module_inst_src) +{ + /* workaround about passing instantiate-linking information */ + CApiFuncImport **new_c_api_func_imports = NULL; + CApiFuncImport *c_api_func_imports = NULL; + uint32 import_func_count = 0; + uint32 size_in_bytes = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst_src->module_type == Wasm_Module_Bytecode) { + new_c_api_func_imports = + &(((WASMModuleInstance *)module_inst_dst)->c_api_func_imports); + c_api_func_imports = + ((const WASMModuleInstance *)module_inst_src)->c_api_func_imports; + import_func_count = + ((WASMModule *)(((const WASMModuleInstance *)module_inst_src) + ->module)) + ->import_function_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst_src->module_type == Wasm_Module_AoT) { + new_c_api_func_imports = + &(((AOTModuleInstance *)module_inst_dst)->c_api_func_imports); + c_api_func_imports = + ((const AOTModuleInstance *)module_inst_src)->c_api_func_imports; + import_func_count = + ((AOTModule *)(((AOTModuleInstance *)module_inst_src)->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)) + return false; + + bh_memcpy_s(*new_c_api_func_imports, size_in_bytes, c_api_func_imports, + size_in_bytes); + } + return true; +} + +#if WASM_ENABLE_DEBUG_INTERP != 0 +WASMCurrentEnvStatus * +wasm_cluster_create_exenv_status() +{ + WASMCurrentEnvStatus *status; + + if (!(status = wasm_runtime_malloc(sizeof(WASMCurrentEnvStatus)))) { + return NULL; + } + + status->step_count = 0; + status->signal_flag = 0; + status->running_status = 0; + return status; +} + +void +wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status) +{ + wasm_runtime_free(status); +} + +inline static bool +wasm_cluster_thread_is_running(WASMExecEnv *exec_env) +{ + return exec_env->current_status->running_status == STATUS_RUNNING + || exec_env->current_status->running_status == STATUS_STEP; +} + +void +wasm_cluster_clear_thread_signal(WASMExecEnv *exec_env) +{ + exec_env->current_status->signal_flag = 0; +} + +void +wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo) +{ + exec_env->current_status->signal_flag = signo; +} + +static void +notify_debug_instance(WASMExecEnv *exec_env) +{ + WASMCluster *cluster; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + if (!cluster->debug_inst) { + return; + } + + on_thread_stop_event(cluster->debug_inst, exec_env); +} + +static void +notify_debug_instance_exit(WASMExecEnv *exec_env) +{ + WASMCluster *cluster; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + if (!cluster->debug_inst) { + return; + } + + on_thread_exit_event(cluster->debug_inst, exec_env); +} + +void +wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env) +{ + 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); + } +} + +void +wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo) +{ + WASMExecEnv *exec_env = bh_list_first_elem(&cluster->exec_env_list); + while (exec_env) { + wasm_cluster_thread_send_signal(exec_env, signo); + exec_env = bh_list_elem_next(exec_env); + } +} + +void +wasm_cluster_thread_exited(WASMExecEnv *exec_env) +{ + exec_env->current_status->running_status = STATUS_EXIT; + notify_debug_instance_exit(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 +wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst) +{ + cluster->debug_inst = inst; +} + +#endif /* end of WASM_ENABLE_DEBUG_INTERP */ + +/* Check whether the exec_env is in one of all clusters, the caller + should add lock to the cluster list before calling us */ +static bool +clusters_have_exec_env(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = bh_list_first_elem(cluster_list); + 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); + } + + return false; +} + +int32 +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) + *ret_val = NULL; + 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); +} + +int32 +wasm_cluster_detach_thread(WASMExecEnv *exec_env) +{ + int32 ret = 0; + + 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; + } + 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 */ + ret = os_thread_detach(exec_env->handle); + exec_env->thread_is_detached = true; + } + os_mutex_unlock(&cluster_list_lock); + return ret; +} + +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) { + /* Store the return value in exec_env */ + exec_env->thread_ret_value = retval; + + WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, + WASM_SUSPEND_FLAG_EXIT); + +#ifndef BH_PLATFORM_WINDOWS + /* Pop all jmpbuf_node except the last one */ + while (exec_env->jmpbuf_stack_top->prev) { + wasm_exec_env_pop_jmpbuf(exec_env); + } + os_longjmp(exec_env->jmpbuf_stack_top->jmpbuf, 1); + return; +#endif + } +#endif + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); +#if WASM_ENABLE_DEBUG_INTERP != 0 + wasm_cluster_clear_thread_signal(exec_env); + wasm_cluster_thread_exited(exec_env); +#endif + + /* Free aux stack space */ + if (exec_env->is_aux_stack_allocated) + wasm_cluster_free_aux_stack(exec_env, + (uint64)exec_env->aux_stack_bottom); + + /* App exit the thread, free the resources before exit native thread */ + + 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 */ + } + + module_inst = exec_env->module_inst; + + /* Remove exec_env */ + wasm_cluster_del_exec_env_internal(cluster, exec_env, false); + /* 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); +} + +static void +set_thread_cancel_flags(WASMExecEnv *exec_env) +{ + os_mutex_lock(&exec_env->wait_lock); + +#if WASM_ENABLE_DEBUG_INTERP != 0 + wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM); +#endif + WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, + WASM_SUSPEND_FLAG_TERMINATE); + + os_mutex_unlock(&exec_env->wait_lock); + +#ifdef OS_ENABLE_WAKEUP_BLOCKING_OP + wasm_runtime_interrupt_blocking_op(exec_env); +#endif +} + +static void +clear_thread_cancel_flags(WASMExecEnv *exec_env) +{ + os_mutex_lock(&exec_env->wait_lock); + WASM_SUSPEND_FLAGS_FETCH_AND(exec_env->suspend_flags, + ~WASM_SUSPEND_FLAG_TERMINATE); + 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; +} + +static void +terminate_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + + if (curr_exec_env == exec_env) + return; + + wasm_cluster_cancel_thread(curr_exec_env); + wasm_cluster_join_thread(curr_exec_env, NULL); +} + +void +wasm_cluster_terminate_all(WASMCluster *cluster) +{ + 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) +{ + 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 +wait_for_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + + if (curr_exec_env == exec_env) + return; + + wasm_cluster_join_thread(curr_exec_env, NULL); +} + +void +wasm_cluster_wait_for_all(WASMCluster *cluster) +{ + 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) +{ + 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 +wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *)) +{ + DestroyCallBackNode *node; + + if (!(node = wasm_runtime_malloc(sizeof(DestroyCallBackNode)))) { + LOG_ERROR("thread manager error: failed to allocate memory"); + return false; + } + node->destroy_cb = callback; + bh_list_insert(destroy_callback_list, node); + return true; +} + +void +wasm_cluster_suspend_thread(WASMExecEnv *exec_env) +{ + /* Set the suspend flag */ + WASM_SUSPEND_FLAGS_FETCH_OR(exec_env->suspend_flags, + WASM_SUSPEND_FLAG_SUSPEND); +} + +static void +suspend_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + + if (curr_exec_env == exec_env) + return; + + wasm_cluster_suspend_thread(curr_exec_env); +} + +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 +wasm_cluster_resume_thread(WASMExecEnv *exec_env) +{ + WASM_SUSPEND_FLAGS_FETCH_AND(exec_env->suspend_flags, + ~WASM_SUSPEND_FLAG_SUSPEND); + os_cond_signal(&exec_env->wait_cond); +} + +static void +resume_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + + wasm_cluster_resume_thread(curr_exec_env); +} + +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); +} + +struct spread_exception_data { + WASMExecEnv *skip; + const char *exception; +}; + +static void +set_exception_visitor(void *node, void *user_data) +{ + const struct spread_exception_data *data = user_data; + WASMExecEnv *exec_env = (WASMExecEnv *)node; + + if (exec_env != data->skip) { + WASMModuleInstance *wasm_inst = + (WASMModuleInstance *)get_module_inst(exec_env); + + exception_lock(wasm_inst); + if (data->exception != NULL) { + snprintf(wasm_inst->cur_exception, sizeof(wasm_inst->cur_exception), + "Exception: %s", data->exception); + } + else { + wasm_inst->cur_exception[0] = '\0'; + } + exception_unlock(wasm_inst); + + /* Terminate the thread so it can exit from dead loops */ + if (data->exception != NULL) { + set_thread_cancel_flags(exec_env); + } + else { + clear_thread_cancel_flags(exec_env); + } + } +} + +void +wasm_cluster_set_exception(WASMExecEnv *exec_env, const char *exception) +{ + const bool has_exception = exception != NULL; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + struct spread_exception_data data; + data.skip = NULL; + data.exception = exception; + + os_mutex_lock(&cluster->lock); +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (has_exception) { + /* Save the stack frames of the crashed thread into the cluster */ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)get_module_inst(exec_env); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode + && wasm_interp_create_call_stack(exec_env)) { + wasm_frame_vec_clone_internal(module_inst->frames, + &cluster->exception_frames); + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT + && aot_create_call_stack(exec_env)) { + wasm_frame_vec_clone_internal(module_inst->frames, + &cluster->exception_frames); + } +#endif + } +#endif /* WASM_ENABLE_DUMP_CALL_STACK != 0 */ + cluster->has_exception = has_exception; + traverse_list(&cluster->exec_env_list, set_exception_visitor, &data); + os_mutex_unlock(&cluster->lock); +} + +static void +set_custom_data_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMModuleInstanceCommon *module_inst = get_module_inst(curr_exec_env); + + wasm_runtime_set_custom_data_internal(module_inst, user_data); +} + +void +wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst, + void *custom_data) +{ + WASMExecEnv *exec_env = wasm_clusters_search_exec_env(module_inst); + + if (exec_env == NULL) { + /* Maybe threads have not been started yet. */ + wasm_runtime_set_custom_data_internal(module_inst, custom_data); + } + else { + WASMCluster *cluster; + + 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); + } +} + +#if WASM_ENABLE_SHARED_HEAP != 0 +static void +attach_shared_heap_visitor(void *node, void *heap) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMModuleInstanceCommon *module_inst = get_module_inst(curr_exec_env); + + wasm_runtime_attach_shared_heap_internal(module_inst, heap); +} + +static void +detach_shared_heap_visitor(void *node, void *heap) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMModuleInstanceCommon *module_inst = get_module_inst(curr_exec_env); + + (void)heap; + wasm_runtime_detach_shared_heap_internal(module_inst); +} + +bool +wasm_cluster_attach_shared_heap(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *heap) +{ + WASMExecEnv *exec_env = wasm_clusters_search_exec_env(module_inst); + + if (exec_env == NULL) { + /* Maybe threads have not been started yet. */ + return wasm_runtime_attach_shared_heap_internal(module_inst, heap); + } + else { + WASMCluster *cluster; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + os_mutex_lock(&cluster->lock); + /* Try attaching shared heap to this module instance first + to ensure that we can attach it to all other instances. */ + if (!wasm_runtime_attach_shared_heap_internal(module_inst, heap)) { + os_mutex_unlock(&cluster->lock); + return false; + } + /* Detach the shared heap so it can be attached again. */ + wasm_runtime_detach_shared_heap_internal(module_inst); + traverse_list(&cluster->exec_env_list, attach_shared_heap_visitor, + heap); + os_mutex_unlock(&cluster->lock); + } + + return true; +} + +void +wasm_cluster_detach_shared_heap(WASMModuleInstanceCommon *module_inst) +{ + WASMExecEnv *exec_env = wasm_clusters_search_exec_env(module_inst); + + if (exec_env == NULL) { + /* Maybe threads have not been started yet. */ + wasm_runtime_detach_shared_heap_internal(module_inst); + } + else { + WASMCluster *cluster; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + os_mutex_lock(&cluster->lock); + traverse_list(&cluster->exec_env_list, detach_shared_heap_visitor, + NULL); + os_mutex_unlock(&cluster->lock); + } +} +#endif + +#if WASM_ENABLE_MODULE_INST_CONTEXT != 0 +struct inst_set_context_data { + void *key; + void *ctx; +}; + +static void +set_context_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMModuleInstanceCommon *module_inst = get_module_inst(curr_exec_env); + const struct inst_set_context_data *data = user_data; + + wasm_runtime_set_context(module_inst, data->key, data->ctx); +} + +void +wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key, + void *ctx) +{ + WASMExecEnv *exec_env = wasm_clusters_search_exec_env(module_inst); + + if (exec_env == NULL) { + /* Maybe threads have not been started yet. */ + wasm_runtime_set_context(module_inst, key, ctx); + } + else { + WASMCluster *cluster; + struct inst_set_context_data data; + data.key = key; + data.ctx = ctx; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + os_mutex_lock(&cluster->lock); + traverse_list(&cluster->exec_env_list, set_context_visitor, &data); + os_mutex_unlock(&cluster->lock); + } +} +#endif /* WASM_ENABLE_MODULE_INST_CONTEXT != 0 */ + +bool +wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env) +{ + os_mutex_lock(&exec_env->wait_lock); + bool is_thread_terminated = (WASM_SUSPEND_FLAGS_GET(exec_env->suspend_flags) + & WASM_SUSPEND_FLAG_TERMINATE) + ? true + : false; + os_mutex_unlock(&exec_env->wait_lock); + + return is_thread_terminated; +} + +void +exception_lock(WASMModuleInstance *module_inst) +{ + os_mutex_lock(&GetModuleInstanceExtraCommon(module_inst)->exception_lock); +} + +void +exception_unlock(WASMModuleInstance *module_inst) +{ + os_mutex_unlock(&GetModuleInstanceExtraCommon(module_inst)->exception_lock); +} + +void +wasm_cluster_traverse_lock(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + os_mutex_lock(&cluster->lock); +} + +void +wasm_cluster_traverse_unlock(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + os_mutex_unlock(&cluster->lock); +} diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h new file mode 100644 index 0000000000..90d97b0be7 --- /dev/null +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _THREAD_MANAGER_H +#define _THREAD_MANAGER_H + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" +#include "../common/wasm_runtime_common.h" +#if WASM_ENABLE_SHARED_HEAP != 0 +#include "../common/wasm_memory.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 +typedef struct WASMDebugInstance WASMDebugInstance; +#endif + +struct WASMCluster { + struct WASMCluster *next; + + 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 */ + uint64 *stack_tops; + /* 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 wasm_cluster_wait_for_all and wasm_cluster_terminate_all + */ + bool processing; +#if WASM_ENABLE_DEBUG_INTERP != 0 + WASMDebugInstance *debug_inst; +#endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + /* When an exception occurs in a thread, the stack frames of that thread are + * saved into the cluster + */ + Vector exception_frames; +#endif +}; + +void +wasm_cluster_set_max_thread_num(uint32 num); + +bool +thread_manager_init(void); + +void +thread_manager_destroy(void); + +/* Create cluster */ +WASMCluster * +wasm_cluster_create(WASMExecEnv *exec_env); + +/* Destroy cluster */ +void +wasm_cluster_destroy(WASMCluster *cluster); + +/* Get the cluster of the current exec_env */ +WASMCluster * +wasm_exec_env_get_cluster(WASMExecEnv *exec_env); + +/* Forward registered functions to a new thread */ +bool +wasm_cluster_dup_c_api_imports(WASMModuleInstanceCommon *module_inst_dst, + const WASMModuleInstanceCommon *module_inst_src); + +int32 +wasm_cluster_create_thread(WASMExecEnv *exec_env, + wasm_module_inst_t module_inst, + bool is_aux_stack_allocated, uint64 aux_stack_start, + uint32 aux_stack_size, + void *(*thread_routine)(void *), void *arg); + +int32 +wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val); + +int32 +wasm_cluster_detach_thread(WASMExecEnv *exec_env); + +int32 +wasm_cluster_cancel_thread(WASMExecEnv *exec_env); + +void +wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval); + +bool +wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *)); + +void +wasm_cluster_cancel_all_callbacks(void); + +void +wasm_cluster_suspend_all(WASMCluster *cluster); + +void +wasm_cluster_suspend_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env); + +void +wasm_cluster_suspend_thread(WASMExecEnv *exec_env); + +void +wasm_cluster_resume_thread(WASMExecEnv *exec_env); + +void +wasm_cluster_resume_all(WASMCluster *cluster); + +void +wasm_cluster_terminate_all(WASMCluster *cluster); + +void +wasm_cluster_terminate_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env); + +void +wasm_cluster_wait_for_all(WASMCluster *cluster); + +void +wasm_cluster_wait_for_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env); + +bool +wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env); + +WASMExecEnv * +wasm_clusters_search_exec_env(WASMModuleInstanceCommon *module_inst); + +void +wasm_cluster_set_exception(WASMExecEnv *exec_env, const char *exception); + +WASMExecEnv * +wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env); + +void +wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env); + +void +wasm_cluster_spread_custom_data(WASMModuleInstanceCommon *module_inst, + void *custom_data); + +void +wasm_cluster_set_context(WASMModuleInstanceCommon *module_inst, void *key, + void *ctx); + +bool +wasm_cluster_is_thread_terminated(WASMExecEnv *exec_env); + +#if WASM_ENABLE_SHARED_HEAP != 0 +bool +wasm_cluster_attach_shared_heap(WASMModuleInstanceCommon *module_inst, + WASMSharedHeap *heap); + +void +wasm_cluster_detach_shared_heap(WASMModuleInstanceCommon *module_inst); +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 +#define WAMR_SIG_TRAP (5) +#define WAMR_SIG_STOP (19) +#define WAMR_SIG_TERM (15) +#define WAMR_SIG_SINGSTEP (0x1ff) + +#define STATUS_RUNNING (0) +#define STATUS_STOP (1) +#define STATUS_EXIT (2) +#define STATUS_STEP (3) + +#define IS_WAMR_TERM_SIG(signo) ((signo) == WAMR_SIG_TERM) + +#define IS_WAMR_STOP_SIG(signo) \ + ((signo) == WAMR_SIG_STOP || (signo) == WAMR_SIG_TRAP) + +struct WASMCurrentEnvStatus { + uint32 signal_flag; + uint16 step_count; + uint16 running_status; +}; + +WASMCurrentEnvStatus * +wasm_cluster_create_exenv_status(void); + +void +wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status); + +void +wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo); + +/* This function must be called with exec_env->wait_lock locked, otherwise we + * may miss the signal from debugger thread, see + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/1860 */ +void +wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env); + +void +wasm_cluster_wait_thread_status(WASMExecEnv *exec_env, uint32 *status); + +void +wasm_cluster_thread_exited(WASMExecEnv *exec_env); + +void +wasm_cluster_thread_continue(WASMExecEnv *exec_env); + +void +wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo); + +void +wasm_cluster_thread_step(WASMExecEnv *exec_env); + +void +wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst); + +#endif /* end of WASM_ENABLE_DEBUG_INTERP != 0 */ + +void +wasm_cluster_traverse_lock(WASMExecEnv *exec_env); + +void +wasm_cluster_traverse_unlock(WASMExecEnv *exec_env); + +bool +wasm_cluster_allocate_aux_stack(WASMExecEnv *exec_env, uint64 *p_start, + uint32 *p_size); + +bool +wasm_cluster_free_aux_stack(WASMExecEnv *exec_env, uint64 start); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _THREAD_MANAGER_H */ diff --git a/core/iwasm/libraries/thread-mgr/thread_mgr.cmake b/core/iwasm/libraries/thread-mgr/thread_mgr.cmake new file mode 100644 index 0000000000..c37a336b43 --- /dev/null +++ b/core/iwasm/libraries/thread-mgr/thread_mgr.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (THREAD_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_THREAD_MGR=1) + +include_directories(${THREAD_MGR_DIR}) + +file (GLOB source_all ${THREAD_MGR_DIR}/*.c) + +set (THREAD_MGR_SOURCE ${source_all}) + diff --git a/core/iwasm/libraries/wasi-nn/.gitignore b/core/iwasm/libraries/wasi-nn/.gitignore new file mode 100644 index 0000000000..81c0ff6aaa --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/.gitignore @@ -0,0 +1,2 @@ +**/*.wasm +**/*.tflite diff --git a/core/iwasm/libraries/wasi-nn/README.md b/core/iwasm/libraries/wasi-nn/README.md new file mode 100644 index 0000000000..e16891a1ba --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/README.md @@ -0,0 +1,195 @@ +# WASI-NN + +## How to use + +### Host + +Enable WASI-NN in the WAMR by specifying it in the cmake building configuration as follows, + +```cmake +set (WAMR_BUILD_WASI_NN 1) +``` + +or in command line + +```bash +$ cmake -DWAMR_BUILD_WASI_NN=1 ... +``` + +> ![Caution] +> Enabling WAMR_BUILD_WASI_NN will cause the IWASM to link to a shared WAMR library instead of a static one. The WASI-NN backends will then be loaded dynamically when the program is run. You must ensure that all shared libraries are included in the `LD_LIBRARY_PATH`. + +#### Compilation options + +- `WAMR_BUILD_WASI_NN`. This option enables support for WASI-NN. It cannot function independently and requires specifying a backend. It follows the original WASI-NN specification for naming conventions and uses wasi_nn for import module names. +- `WAMR_BUILD_WASI_EPHEMERAL_NN`. This option adheres to the most recent WASI-NN specification for naming conventions and uses wasi_ephemeral_nn for import module names. +- `WAMR_BUILD_WASI_NN_TFLITE`. This option designates TensorFlow Lite as the backend. +- `WAMR_BUILD_WASI_NN_OPENVINO`. This option designates OpenVINO as the backend. +- `WAMR_BUILD_WASI_NN_LLAMACPP`. This option designates Llama.cpp as the backend. +- `WAMR_BUILD_WASI_NN_ONNX`. This option designates ONNX Runtime as the backend. + +### Wasm + +The definition of functions provided by WASI-NN (Wasm imports) is in the header file [wasi_nn.h](_core/iwasm/libraries/wasi-nn/wasi_nn.h_). By only including this file in a WASM application you will bind WASI-NN into your module. + +For some historical reasons, there are two sets of functions in the header file. The first set is the original one, and the second set is the new one. The new set is recommended to use. In code, `WASM_ENABLE_WASI_EPHEMERAL_NN` is used to control which set of functions to use. If `WASM_ENABLE_WASI_EPHEMERAL_NN` is defined, the new set of functions will be used. Otherwise, the original set of functions will be used. + +There is a big difference between the two sets of functions, `tensor_type`. + +```c +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +typedef enum { fp16 = 0, fp32, fp64, u8, i32, i64 } tensor_type; +#else +typedef enum { fp16 = 0, fp32, up8, ip32 } tensor_type; +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ +``` + +It is required to recompile the Wasm application if you want to switch between the two sets of functions. + +#### Openvino installation + +If you're planning to use OpenVINO backends, the first step is to install OpenVINO on your computer. To do this correctly, please follow the official installation guide which you can find at this link: https://docs.openvino.ai/2024/get-started/install-openvino/install-openvino-archive-linux.html. + +After you've installed OpenVINO, you'll need to let cmake system know where to find it. You can do this by setting an environment variable named `OpenVINO_DIR`. This variable should point to the place on your computer where OpenVINO is installed. By setting this variable, your system will be able to locate and use OpenVINO when needed. You can find installation path by running the following command if using APT `$dpkg -L openvino`. The path should be _/opt/intel/openvino/_ or _/usr/lib/openvino_. + +## Tests + +To run the tests we assume that the current directory is the root of the repository. + +### Build the runtime + +Build the runtime image for your execution target type. + +`EXECUTION_TYPE` can be: + +- `cpu` +- `nvidia-gpu` +- `vx-delegate` +- `tpu` + +```bash +$ pwd +/wasm-micro-runtime + +$ EXECUTION_TYPE=cpu docker build -t wasi-nn-${EXECUTION_TYPE} -f core/iwasm/libraries/wasi-nn/test/Dockerfile.${EXECUTION_TYPE} . +``` + +### Build wasm app + +``` +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! +``` + +> [!TIP] +> Use _libwasi-nn-tflite.so_ as an example. You shall use whatever you have built. + +- CPU + +```bash +docker run \ + -v $PWD/core/iwasm/libraries/wasi-nn/test:/assets \ + -v $PWD/core/iwasm/libraries/wasi-nn/test/models:/models \ + wasi-nn-cpu \ + --dir=/ \ + --env="TARGET=cpu" \ + /assets/test_tensorflow.wasm +``` + +- (NVIDIA) GPU + - Requirements: + - [NVIDIA docker](https://github.com/NVIDIA/nvidia-docker). + +```bash +docker run \ + --runtime=nvidia \ + -v $PWD/core/iwasm/libraries/wasi-nn/test:/assets \ + -v $PWD/core/iwasm/libraries/wasi-nn/test/models:/models \ + wasi-nn-nvidia-gpu \ + --dir=/ \ + --env="TARGET=gpu" \ + /assets/test_tensorflow.wasm +``` + +- vx-delegate for NPU (x86 simulator) + +```bash +docker run \ + -v $PWD/core/iwasm/libraries/wasi-nn/test:/assets \ + wasi-nn-vx-delegate \ + --dir=/ \ + --env="TARGET=gpu" \ + /assets/test_tensorflow_quantized.wasm +``` + +- (Coral) TPU + - Requirements: + - [Coral USB](https://coral.ai/products/accelerator/). + +```bash +docker run \ + --privileged \ + --device=/dev/bus/usb:/dev/bus/usb \ + -v $PWD/core/iwasm/libraries/wasi-nn/test:/assets \ + wasi-nn-tpu \ + --dir=/ \ + --env="TARGET=tpu" \ + /assets/test_tensorflow_quantized.wasm +``` + +## What is missing + +Supported: + +- Graph encoding: `tensorflowlite`, `openvino`, `ggml` and `onnx` +- Execution target: `cpu` for all. `gpu` and `tpu` for `tensorflowlite`. +- Tensor type: `fp32`. + +## Smoke test + +### Testing with WasmEdge-WASINN Examples + +To make sure everything is configured properly, refer to the examples provided at [WasmEdge-WASINN-examples](https://github.com/second-state/WasmEdge-WASINN-examples/tree/master). These examples are useful for confirming that the WASI-NN support in WAMR is working correctly. + +Because each backend has its own set of requirements, we recommend using a Docker container to create a straightforward testing environment without complications. + +#### Prepare the execution environment + +```bash +$ pwd +/workspaces/wasm-micro-runtime/ + +$ docker build -t wasi-nn-smoke:v1.0 -f ./core/iwasm/libraries/wasi-nn/test/Dockerfile.wasi-nn-smoke . +``` + +#### Execute + +```bash +$ pwd +/workspaces/wasm-micro-runtime/ +$ docker run --rm wasi-nn-smoke:v1.0 +``` + +It should be noted that the qwen example is selected as the default one about the Llama.cpp backend because it uses a small model and is easy to run. + +```bash +- openvino_mobile_image. PASS +- openvino_mobile_raw. PASS +- openvino_road_segmentation_adas. PASS +- wasmedge_ggml_qwen. PASS +``` + +### Testing with bytecodealliance WASI-NN + +For another example, check out [classification-example](https://github.com/bytecodealliance/wasi-nn/tree/main/rust/examples/classification-example), which focuses on OpenVINO. You can run it using the same Docker container mentioned above. diff --git a/core/iwasm/libraries/wasi-nn/cmake/Findcjson.cmake b/core/iwasm/libraries/wasi-nn/cmake/Findcjson.cmake new file mode 100644 index 0000000000..c698e8c5f9 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/Findcjson.cmake @@ -0,0 +1,32 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +include(FetchContent) + +set(CJSON_SOURCE_DIR "${WAMR_ROOT_DIR}/core/deps/cjson") +if(EXISTS ${CJSON_SOURCE_DIR}) + message("Use existed source code under ${CJSON_SOURCE_DIR}") + FetchContent_Declare( + cjson + SOURCE_DIR ${CJSON_SOURCE_DIR} + ) +else() + message("download source code and store it at ${CJSON_SOURCE_DIR}") + FetchContent_Declare( + cjson + GIT_REPOSITORY https://github.com/DaveGamble/cJSON.git + GIT_TAG v1.7.18 + SOURCE_DIR ${CJSON_SOURCE_DIR} + ) +endif() + +set(ENABLE_CJSON_TEST OFF CACHE INTERNAL "Turn off tests") +set(ENABLE_CJSON_UNINSTALL OFF CACHE INTERNAL "Turn off uninstall to avoid targets conflict") +FetchContent_MakeAvailable(cjson) diff --git a/core/iwasm/libraries/wasi-nn/cmake/Findllamacpp.cmake b/core/iwasm/libraries/wasi-nn/cmake/Findllamacpp.cmake new file mode 100644 index 0000000000..29dd463901 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/Findllamacpp.cmake @@ -0,0 +1,33 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +include(FetchContent) + +set(LLAMA_SOURCE_DIR "${WAMR_ROOT_DIR}/core/deps/llama.cpp") +if(EXISTS ${LLAMA_SOURCE_DIR}) + message("Use existed source code under ${LLAMA_SOURCE_DIR}") + FetchContent_Declare( + llamacpp + SOURCE_DIR ${LLAMA_SOURCE_DIR} + ) +else() + message("download source code and store it at ${LLAMA_SOURCE_DIR}") + FetchContent_Declare( + llamacpp + GIT_REPOSITORY https://github.com/ggerganov/llama.cpp.git + GIT_TAG b3573 + SOURCE_DIR ${LLAMA_SOURCE_DIR} + ) +endif() + +set(LLAMA_BUILD_TESTS OFF) +set(LLAMA_BUILD_EXAMPLES OFF) +set(LLAMA_BUILD_SERVER OFF) +FetchContent_MakeAvailable(llamacpp) diff --git a/core/iwasm/libraries/wasi-nn/cmake/Findonnxruntime.cmake b/core/iwasm/libraries/wasi-nn/cmake/Findonnxruntime.cmake new file mode 100644 index 0000000000..db8f287e36 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/Findonnxruntime.cmake @@ -0,0 +1,86 @@ +# Copyright 2025 Sony Semiconductor Solutions Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Find ONNX Runtime library +# +# This module defines the following variables: +# +# :: +# +# onnxruntime_FOUND - True if onnxruntime is found +# onnxruntime_INCLUDE_DIRS - Include directories for onnxruntime +# onnxruntime_LIBRARIES - List of libraries for onnxruntime +# onnxruntime_VERSION - Version of onnxruntime +# +# :: +# +# Example usage: +# +# find_package(onnxruntime) +# if(onnxruntime_FOUND) +# target_link_libraries(app onnxruntime) +# endif() + +# First try to find ONNX Runtime using the CMake config file +# FIXME: This is a temporary workaround for ONNX Runtime's broken CMake config on Linux. +# See https://github.com/microsoft/onnxruntime/issues/25279 +# Once the upstream issue is fixed, this conditional can be safely removed. +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_package(onnxruntime CONFIG QUIET) + if(onnxruntime_FOUND) + return() + endif() +endif() + +# If not found via CMake config, try to find manually +find_path(onnxruntime_INCLUDE_DIR + NAMES onnxruntime_c_api.h + PATHS + /usr/include + /usr/local/include + /opt/onnxruntime/include + $ENV{ONNXRUNTIME_ROOT}/include + ${CMAKE_CURRENT_LIST_DIR}/../../../../.. +) + +find_library(onnxruntime_LIBRARY + NAMES onnxruntime + PATHS + /usr/lib + /usr/local/lib + /opt/onnxruntime/lib + $ENV{ONNXRUNTIME_ROOT}/lib + ${CMAKE_CURRENT_LIST_DIR}/../../../../.. +) + +# Try to determine version from header file +if(onnxruntime_INCLUDE_DIR) + file(STRINGS "${onnxruntime_INCLUDE_DIR}/onnxruntime_c_api.h" onnxruntime_version_str + REGEX "^#define[\t ]+ORT_API_VERSION[\t ]+[0-9]+") + + if(onnxruntime_version_str) + string(REGEX REPLACE "^#define[\t ]+ORT_API_VERSION[\t ]+([0-9]+)" "\\1" + onnxruntime_VERSION "${onnxruntime_version_str}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(onnxruntime + REQUIRED_VARS onnxruntime_LIBRARY onnxruntime_INCLUDE_DIR + VERSION_VAR onnxruntime_VERSION +) + +if(onnxruntime_FOUND) + set(onnxruntime_LIBRARIES ${onnxruntime_LIBRARY}) + set(onnxruntime_INCLUDE_DIRS ${onnxruntime_INCLUDE_DIR}) + + if(NOT TARGET onnxruntime::onnxruntime) + add_library(onnxruntime::onnxruntime UNKNOWN IMPORTED) + set_target_properties(onnxruntime::onnxruntime PROPERTIES + IMPORTED_LOCATION "${onnxruntime_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${onnxruntime_INCLUDE_DIRS}" + ) + endif() +endif() + +mark_as_advanced(onnxruntime_INCLUDE_DIR onnxruntime_LIBRARY) diff --git a/core/iwasm/libraries/wasi-nn/cmake/Findtensorflow_lite.cmake b/core/iwasm/libraries/wasi-nn/cmake/Findtensorflow_lite.cmake new file mode 100644 index 0000000000..2561b52436 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/Findtensorflow_lite.cmake @@ -0,0 +1,44 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Yes. To solve the compatibility issue with CMAKE (>= 4.0), we need to update +# our `cmake_minimum_required()` to 3.5. However, there are CMakeLists.txt +# from 3rd parties that we should not alter. Therefore, in addition to +# changing the `cmake_minimum_required()`, we should also add a configuration +# here that is compatible with earlier versions. +set(CMAKE_POLICY_VERSION_MINIMUM 3.5 FORCE) + +include(FetchContent) + +set(TFLITE_SOURCE_DIR "${WAMR_ROOT_DIR}/core/deps/tensorflow-src") +if(EXISTS ${TFLITE_SOURCE_DIR}) + message("Use existed source code under ${TFLITE_SOURCE_DIR}") + FetchContent_Declare( + tensorflow_lite + SOURCE_DIR ${TFLITE_SOURCE_DIR} + SOURCE_SUBDIR tensorflow/lite + ) +else() + message("download source code and store it at ${TFLITE_SOURCE_DIR}") + FetchContent_Declare( + tensorflow_lite + GIT_REPOSITORY https://github.com/tensorflow/tensorflow.git + GIT_TAG v2.12.0 + GIT_SHALLOW ON + GIT_PROGRESS ON + SOURCE_DIR ${TFLITE_SOURCE_DIR} + SOURCE_SUBDIR tensorflow/lite + PATCH_COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/add_telemetry.patch + ) +endif() + + +if(WAMR_BUILD_WASI_NN_ENABLE_GPU EQUAL 1) + set(TFLITE_ENABLE_GPU ON) +endif() + +if (CMAKE_SIZEOF_VOID_P EQUAL 4) + set(TFLITE_ENABLE_XNNPACK OFF) +endif() + +FetchContent_MakeAvailable(tensorflow_lite) diff --git a/core/iwasm/libraries/wasi-nn/cmake/add_telemetry.patch b/core/iwasm/libraries/wasi-nn/cmake/add_telemetry.patch new file mode 100644 index 0000000000..8dbf5c355c --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/add_telemetry.patch @@ -0,0 +1,12 @@ +diff --git a/tensorflow/lite/CMakeLists.txt b/tensorflow/lite/CMakeLists.txt +index c71a3925ac..39591a3bd7 100644 +--- a/tensorflow/lite/CMakeLists.txt ++++ b/tensorflow/lite/CMakeLists.txt +@@ -493,6 +493,7 @@ set(TFLITE_PROFILER_SRCS + ${TFLITE_SOURCE_DIR}/profiling/root_profiler.h + ${TFLITE_SOURCE_DIR}/profiling/root_profiler.cc + ${TFLITE_SOURCE_DIR}/profiling/telemetry/profiler.cc ++ ${TFLITE_SOURCE_DIR}/profiling/telemetry/telemetry.cc + ) + if(CMAKE_SYSTEM_NAME MATCHES "Android") + list(APPEND TFLITE_PROFILER_SRCS diff --git a/core/iwasm/libraries/wasi-nn/cmake/iwasm_helper.cmake b/core/iwasm/libraries/wasi-nn/cmake/iwasm_helper.cmake new file mode 100644 index 0000000000..45518de59c --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/iwasm_helper.cmake @@ -0,0 +1,40 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +if (NOT DEFINED WAMR_BUILD_PLATFORM) + string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +endif () + +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (CMAKE_C_STANDARD 99) + +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) + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security -Wshadow -Wno-unused-parameter") + +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wformat -Wformat-security -Wno-unused") + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_FAST_INTERP 1) + +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") diff --git a/core/iwasm/libraries/wasi-nn/cmake/wasi_nn.cmake b/core/iwasm/libraries/wasi-nn/cmake/wasi_nn.cmake new file mode 100644 index 0000000000..56a7b44e4a --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/cmake/wasi_nn.cmake @@ -0,0 +1,132 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +# +# wasi-nn general +set(WASI_NN_ROOT ${CMAKE_CURRENT_LIST_DIR}/..) +set(WASI_NN_SOURCES + ${WASI_NN_ROOT}/src/wasi_nn.c + ${WASI_NN_ROOT}/src/utils/wasi_nn_app_native.c +) +include_directories(${WASI_NN_ROOT}/include) +add_compile_definitions( + $<$:NN_LOG_LEVEL=0> + $<$:NN_LOG_LEVEL=2> +) + +# +# wasi-nn backends +# +# - tflite +if(WAMR_BUILD_WASI_NN_TFLITE EQUAL 1) + find_package(tensorflow_lite REQUIRED) + enable_language(CXX) + + add_library( + wasi_nn_tflite + SHARED + ${WASI_NN_ROOT}/src/wasi_nn_tensorflowlite.cpp + ) + + target_include_directories( + wasi_nn_tflite + PUBLIC + ${tensorflow_lite_SOURCE_DIR} + ) + + target_link_libraries( + wasi_nn_tflite + PUBLIC + vmlib + tensorflow-lite + ) + + install(TARGETS wasi_nn_tflite DESTINATION lib) +endif() + +# - openvino +if(WAMR_BUILD_WASI_NN_OPENVINO EQUAL 1) + if(NOT DEFINED ENV{OpenVINO_DIR}) + message(FATAL_ERROR + "OpenVINO_DIR is not defined. " + "Please follow https://docs.openvino.ai/2024/get-started/install-openvino.html," + "install openvino, and set environment variable OpenVINO_DIR." + "Like OpenVINO_DIR=/usr/lib/openvino-2023.2/ cmake ..." + "Or OpenVINO_DIR=/opt/intel/openvino/ cmake ..." + ) + endif() + + list(APPEND CMAKE_MODULE_PATH $ENV{OpenVINO_DIR}) + # Find OpenVINO + find_package(OpenVINO REQUIRED COMPONENTS Runtime) + + add_library( + wasi_nn_openvino + SHARED + ${WASI_NN_ROOT}/src/wasi_nn_openvino.c + ) + + target_link_libraries( + wasi_nn_openvino + PUBLIC + vmlib + openvino::runtime + openvino::runtime::c + ) + + install(TARGETS wasi_nn_openvino DESTINATION lib) +endif() + +# - llamacpp + +if(WAMR_BUILD_WASI_NN_LLAMACPP EQUAL 1) + find_package(cjson REQUIRED) + find_package(llamacpp REQUIRED) + + add_library( + wasi_nn_llamacpp + SHARED + ${WASI_NN_ROOT}/src/wasi_nn_llamacpp.c + ) + + target_include_directories( + wasi_nn_llamacpp + PUBLIC + ${cjson_SOURCE_DIR} + ) + + target_link_libraries( + wasi_nn_llamacpp + PUBLIC + vmlib + cjson + common + ggml + llama + ) + + install(TARGETS wasi_nn_llamacpp DESTINATION lib) +endif() + +# - onnx +if(WAMR_BUILD_WASI_NN_ONNX EQUAL 1) + find_package(onnxruntime REQUIRED) + enable_language(CXX) + + add_library( + wasi_nn_onnx + SHARED + ${WASI_NN_ROOT}/src/wasi_nn_onnx.cpp + ) + + target_link_libraries( + wasi_nn_onnx + PUBLIC + vmlib + onnxruntime::onnxruntime + ) + + install(TARGETS wasi_nn_onnx DESTINATION lib) +endif() diff --git a/core/iwasm/libraries/wasi-nn/include/wasi_ephemeral_nn.h b/core/iwasm/libraries/wasi-nn/include/wasi_ephemeral_nn.h new file mode 100644 index 0000000000..f76295a1ee --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/include/wasi_ephemeral_nn.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2025 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#define WASM_ENABLE_WASI_EPHEMERAL_NN 1 +#define WASI_NN_NAME(name) wasi_ephemeral_nn_##name + +#include "wasi_nn.h" + +#undef WASM_ENABLE_WASI_EPHEMERAL_NN +#undef WASI_NN_NAME diff --git a/core/iwasm/libraries/wasi-nn/include/wasi_nn.h b/core/iwasm/libraries/wasi-nn/include/wasi_nn.h new file mode 100644 index 0000000000..cda26324eb --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/include/wasi_nn.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * Following definition from: + * [Oct 25th, 2022] + * https://github.com/WebAssembly/wasi-nn/blob/0f77c48ec195748990ff67928a4b3eef5f16c2de/wasi-nn.wit.md + */ + +#ifndef WASI_NN_H +#define WASI_NN_H + +#include +#include "wasi_nn_types.h" + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +#define WASI_NN_IMPORT(name) \ + __attribute__((import_module("wasi_ephemeral_nn"), import_name(name))) +#else +#define WASI_NN_IMPORT(name) \ + __attribute__((import_module("wasi_nn"), import_name(name))) +#warning You are using "wasi_nn", which is a legacy WAMR-specific ABI. It's deperecated and will likely be removed in future versions of WAMR. Please use "wasi_ephemeral_nn" instead. (For a WASM module, use the wasi_ephemeral_nn.h header instead. For the runtime configurations, enable WASM_ENABLE_WASI_EPHEMERAL_NN/WAMR_BUILD_WASI_EPHEMERAL_NN.) +#endif + +/** + * @brief Load an opaque sequence of bytes to use for inference. + * + * @param builder Model builder. + * @param builder_len The size of model builder. + * @param encoding Model encoding. + * @param target Execution target. + * @param g Graph. + * @return wasi_nn_error Execution status. + */ +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +WASI_NN_ERROR_TYPE +WASI_NN_NAME(load) +(WASI_NN_NAME(graph_builder) * builder, uint32_t builder_len, + WASI_NN_NAME(graph_encoding) encoding, WASI_NN_NAME(execution_target) target, + WASI_NN_NAME(graph) * g) WASI_NN_IMPORT("load"); +#else +WASI_NN_ERROR_TYPE +WASI_NN_NAME(load) +(WASI_NN_NAME(graph_builder_array) * builder, + WASI_NN_NAME(graph_encoding) encoding, WASI_NN_NAME(execution_target) target, + WASI_NN_NAME(graph) * g) WASI_NN_IMPORT("load"); +#endif + +WASI_NN_ERROR_TYPE +WASI_NN_NAME(load_by_name) +(const char *name, uint32_t name_len, WASI_NN_NAME(graph) * g) + WASI_NN_IMPORT("load_by_name"); + +/** + * INFERENCE + * + */ + +/** + * @brief Create an execution instance of a loaded graph. + * + * @param g Graph. + * @param ctx Execution context. + * @return wasi_nn_error Execution status. + */ +WASI_NN_ERROR_TYPE +WASI_NN_NAME(init_execution_context) +(WASI_NN_NAME(graph) g, WASI_NN_NAME(graph_execution_context) * ctx) + WASI_NN_IMPORT("init_execution_context"); + +/** + * @brief Define the inputs to use for inference. + * + * @param ctx Execution context. + * @param index Input tensor index. + * @param tensor Input tensor. + * @return wasi_nn_error Execution status. + */ +WASI_NN_ERROR_TYPE +WASI_NN_NAME(set_input) +(WASI_NN_NAME(graph_execution_context) ctx, uint32_t index, + WASI_NN_NAME(tensor) * tensor) WASI_NN_IMPORT("set_input"); + +/** + * @brief Compute the inference on the given inputs. + * + * @param ctx Execution context. + * @return wasi_nn_error Execution status. + */ +WASI_NN_ERROR_TYPE +WASI_NN_NAME(compute) +(WASI_NN_NAME(graph_execution_context) ctx) WASI_NN_IMPORT("compute"); + +/** + * @brief Extract the outputs after inference. + * + * @param ctx Execution context. + * @param index Output tensor index. + * @param output_tensor Buffer where output tensor with index `index` is + * copied. + * @param output_tensor_size Pointer to `output_tensor` maximum size. + * After the function call it is updated with the + * copied number of bytes. + * @return wasi_nn_error Execution status. + */ +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +WASI_NN_ERROR_TYPE +WASI_NN_NAME(get_output) +(WASI_NN_NAME(graph_execution_context) ctx, uint32_t index, + uint8_t *output_tensor, uint32_t output_tensor_max_size, + uint32_t *output_tensor_size) WASI_NN_IMPORT("get_output"); +#else +WASI_NN_ERROR_TYPE +WASI_NN_NAME(get_output) +(graph_execution_context ctx, uint32_t index, uint8_t *output_tensor, + uint32_t *output_tensor_size) WASI_NN_IMPORT("get_output"); +#endif + +#endif diff --git a/core/iwasm/libraries/wasi-nn/include/wasi_nn_host.h b/core/iwasm/libraries/wasi-nn/include/wasi_nn_host.h new file mode 100644 index 0000000000..cb056915f8 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/include/wasi_nn_host.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_HOST_H +#define WASI_NN_HOST_H + +#include "lib_export.h" + +uint32_t +get_wasi_nn_export_apis(NativeSymbol **p_native_symbols); + +bool +wasi_nn_initialize(); + +void +wasi_nn_destroy(); + +#endif /* WASI_NN_HOST_H */ \ No newline at end of file diff --git a/core/iwasm/libraries/wasi-nn/include/wasi_nn_types.h b/core/iwasm/libraries/wasi-nn/include/wasi_nn_types.h new file mode 100644 index 0000000000..952fb65e28 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/include/wasi_nn_types.h @@ -0,0 +1,180 @@ +/* + * 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 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* our host logic doesn't use any prefix. neither legacy wasi_nn.h does. */ + +#if !defined(__wasm__) || !defined(WASI_NN_NAME) +#define WASI_NN_NAME(name) name +#define WASI_NN_ERROR_NAME(name) name +#define WASI_NN_TYPE_NAME(name) name +#define WASI_NN_ENCODING_NAME(name) name +#define WASI_NN_TARGET_NAME(name) name +#define WASI_NN_ERROR_TYPE wasi_nn_error +#else +#define WASI_NN_ERROR_NAME(name) WASI_NN_NAME(error_##name) +#define WASI_NN_TYPE_NAME(name) WASI_NN_NAME(type_##name) +#define WASI_NN_ENCODING_NAME(name) WASI_NN_NAME(encoding_##name) +#define WASI_NN_TARGET_NAME(name) WASI_NN_NAME(target_##name) +#define WASI_NN_ERROR_TYPE WASI_NN_NAME(error); +#endif + +/** + * ERRORS + * + */ + +// sync up with +// https://github.com/WebAssembly/wasi-nn/blob/71320d95b8c6d43f9af7f44e18b1839db85d89b4/wasi-nn.witx#L5-L17 +// Error codes returned by functions in this API. +typedef enum { + WASI_NN_ERROR_NAME(success) = 0, + WASI_NN_ERROR_NAME(invalid_argument), + WASI_NN_ERROR_NAME(invalid_encoding), + WASI_NN_ERROR_NAME(missing_memory), + WASI_NN_ERROR_NAME(busy), + WASI_NN_ERROR_NAME(runtime_error), + WASI_NN_ERROR_NAME(unsupported_operation), + WASI_NN_ERROR_NAME(too_large), + WASI_NN_ERROR_NAME(not_found), + + // for WasmEdge-wasi-nn + WASI_NN_ERROR_NAME(end_of_sequence) = 100, // End of Sequence Found. + WASI_NN_ERROR_NAME(context_full) = 101, // Context Full. + WASI_NN_ERROR_NAME(prompt_tool_long) = 102, // Prompt Too Long. + WASI_NN_ERROR_NAME(model_not_found) = 103, // Model Not Found. +} WASI_NN_ERROR_TYPE; + +/** + * 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; +} WASI_NN_NAME(tensor_dimensions); + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +// sync up with +// https://github.com/WebAssembly/wasi-nn/blob/71320d95b8c6d43f9af7f44e18b1839db85d89b4/wasi-nn.witx#L19-L28 +// The type of the elements in a tensor. +typedef enum { + WASI_NN_TYPE_NAME(fp16) = 0, + WASI_NN_TYPE_NAME(fp32), + WASI_NN_TYPE_NAME(fp64), + WASI_NN_TYPE_NAME(u8), + WASI_NN_TYPE_NAME(i32), + WASI_NN_TYPE_NAME(i64), +} WASI_NN_NAME(tensor_type); +#else +typedef enum { + WASI_NN_TYPE_NAME(fp16) = 0, + WASI_NN_TYPE_NAME(fp32), + WASI_NN_TYPE_NAME(up8), + WASI_NN_TYPE_NAME(ip32), +} WASI_NN_NAME(tensor_type); +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + +// 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. +#if !defined(__wasm__) || WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +typedef struct { + uint8_t *buf; + uint32_t size; +} WASI_NN_NAME(tensor_data); +#else +typedef uint8_t *WASI_NN_NAME(tensor_data); +#endif + +// 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. +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 && defined(__wasm__) + WASI_NN_NAME(tensor_dimensions) dimensions; +#else + WASI_NN_NAME(tensor_dimensions) * dimensions; +#endif + // Describe the type of element in the tensor (e.g., f32). + uint8_t type; + uint8_t _pad[3]; + // Contains the tensor data. + WASI_NN_NAME(tensor_data) data; +} WASI_NN_NAME(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; +} WASI_NN_NAME(graph_builder); + +typedef struct { + WASI_NN_NAME(graph_builder) * buf; + uint32_t size; +} WASI_NN_NAME(graph_builder_array); + +// An execution graph for performing inference (i.e., a model). +typedef uint32_t WASI_NN_NAME(graph); + +// sync up with +// https://github.com/WebAssembly/wasi-nn/blob/main/wit/wasi-nn.wit#L75 +// 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 { + WASI_NN_ENCODING_NAME(openvino) = 0, + WASI_NN_ENCODING_NAME(onnx), + WASI_NN_ENCODING_NAME(tensorflow), + WASI_NN_ENCODING_NAME(pytorch), + WASI_NN_ENCODING_NAME(tensorflowlite), + WASI_NN_ENCODING_NAME(ggml), + WASI_NN_ENCODING_NAME(autodetect), + WASI_NN_ENCODING_NAME(unknown_backend), +} WASI_NN_NAME(graph_encoding); + +// Define where the graph should be executed. +typedef enum WASI_NN_NAME(execution_target) { + WASI_NN_TARGET_NAME(cpu) = 0, + WASI_NN_TARGET_NAME(gpu), + WASI_NN_TARGET_NAME(tpu), +} WASI_NN_NAME(execution_target); + +// Bind a `graph` to the input and output tensors for an inference. +typedef uint32_t WASI_NN_NAME(graph_execution_context); + +#ifdef __cplusplus +} +#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 0000000000..f85ec40a40 --- /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 2 +#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 0000000000..b44f45aac0 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasi_nn_app_native.h" + +static wasi_nn_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, (uint64)builder_wasm->buf_offset, + (uint64)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, (uint64)builder_wasm->buf_offset); + builder->size = builder_wasm->size; + return success; +} + +/** + * builder_array_wasm is consisted of {builder_wasm, size} + */ +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +wasi_nn_error +graph_builder_array_app_native(wasm_module_inst_t instance, + graph_builder_wasm *builder_wasm, uint32_t size, + graph_builder_array *builder_array) +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ +wasi_nn_error +graph_builder_array_app_native(wasm_module_inst_t instance, + graph_builder_array_wasm *builder_array_wasm, + graph_builder_array *builder_array) +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ +{ +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +#define array_size size +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ +#define array_size builder_array_wasm->size + + if (!wasm_runtime_validate_native_addr( + instance, builder_array_wasm, + (uint64)sizeof(graph_builder_array_wasm))) { + NN_ERR_PRINTF("builder_array_wasm is invalid"); + return invalid_argument; + } +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + + NN_DBG_PRINTF("Graph builder array contains %d elements", array_size); + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + if (!wasm_runtime_validate_native_addr(instance, builder_wasm, + (uint64)array_size + * sizeof(graph_builder_wasm))) { + NN_ERR_PRINTF("builder_wasm is invalid"); + return invalid_argument; + } +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ + if (!wasm_runtime_validate_app_addr( + instance, (uint64)builder_array_wasm->buf_offset, + (uint64)array_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, (uint64)builder_array_wasm->buf_offset); +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + + graph_builder *builder = (graph_builder *)wasm_runtime_malloc( + array_size * sizeof(graph_builder)); + if (builder == NULL) + return too_large; + + for (uint32_t i = 0; i < array_size; ++i) { + wasi_nn_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[i].size); + } + + builder_array->buf = builder; + builder_array->size = array_size; + return success; +#undef array_size +} + +static wasi_nn_error +tensor_data_app_native(wasm_module_inst_t instance, uint32_t total_elements, + tensor_wasm *input_tensor_wasm, void **data, + uint32_t *size) +{ +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +#define data_size input_tensor_wasm->data_size +#else +#define data_size total_elements +#endif + + uint64 data_size_in_bytes = data_size; +#if WASM_ENABLE_WASI_EPHEMERAL_NN == 0 + data_size_in_bytes *= sizeof(float); + if (data_size_in_bytes / sizeof(float) != data_size) { + /* overflow */ + return invalid_argument; + } +#endif + + if (!wasm_runtime_validate_app_addr(instance, + (uint64)input_tensor_wasm->data_offset, + data_size_in_bytes)) { + NN_ERR_PRINTF("input_tensor_wasm->data_offset is invalid"); + return invalid_argument; + } + *data = wasm_runtime_addr_app_to_native( + instance, (uint64)input_tensor_wasm->data_offset); + *size = data_size; + return success; +#undef data_size +} + +static wasi_nn_error +tensor_dimensions_app_native(wasm_module_inst_t instance, + tensor_wasm *input_tensor_wasm, + tensor_dimensions **dimensions) +{ +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + tensor_dimensions_wasm *dimensions_wasm = &input_tensor_wasm->dimensions; +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ + if (!wasm_runtime_validate_app_addr( + instance, (uint64)input_tensor_wasm->dimensions_offset, + (uint64)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, (uint64)input_tensor_wasm->dimensions_offset); +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + + if (!wasm_runtime_validate_app_addr(instance, + (uint64)dimensions_wasm->buf_offset, + (uint64)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 too_large; + + (*dimensions)->size = dimensions_wasm->size; + (*dimensions)->buf = (uint32_t *)wasm_runtime_addr_app_to_native( + instance, (uint64)dimensions_wasm->buf_offset); + + NN_DBG_PRINTF("Number of dimensions: %d", (*dimensions)->size); + return success; +} + +wasi_nn_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, + (uint64)sizeof(tensor_wasm))) { + NN_ERR_PRINTF("input_tensor_wasm is invalid"); + return invalid_argument; + } + + wasi_nn_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); + + void *data = NULL; + uint32_t datasize; + if (success + != (res = + tensor_data_app_native(instance, total_elements, + input_tensor_wasm, &data, &datasize))) { + wasm_runtime_free(dimensions); + return res; + } + + input_tensor->type = input_tensor_wasm->type; + input_tensor->dimensions = dimensions; + input_tensor->data.buf = data; + input_tensor->data.size = datasize; + 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 0000000000..80c22784eb --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/utils/wasi_nn_app_native.h @@ -0,0 +1,65 @@ +/* + * 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_types.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 { +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + tensor_dimensions_wasm dimensions; + tensor_type type; + uint32_t data_offset; + uint32_t data_size; +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ + uint32_t dimensions_offset; + tensor_type type; + uint32_t data_offset; +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ +} tensor_wasm; + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +wasi_nn_error +graph_builder_array_app_native(wasm_module_inst_t instance, + graph_builder_wasm *builder_wasm, uint32_t size, + graph_builder_array *builder_array); +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ +wasi_nn_error +graph_builder_array_app_native(wasm_module_inst_t instance, + graph_builder_array_wasm *builder, + graph_builder_array *builder_native); +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + +wasi_nn_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 0000000000..2282534b0f --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn.c @@ -0,0 +1,848 @@ +/* + * 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 "wasi_nn_private.h" +#include "utils/wasi_nn_app_native.h" +#include "utils/logger.h" + +#include "bh_platform.h" +#include "wasi_nn_types.h" +#include "wasm_export.h" + +#if WASM_ENABLE_WASI_EPHEMERAL_NN == 0 +#warning You are using "wasi_nn", which is a legacy WAMR-specific ABI. It's deperecated and will likely be removed in future versions of WAMR. Please use "wasi_ephemeral_nn" instead. (For a WASM module, use the wasi_ephemeral_nn.h header instead. For the runtime configurations, enable WASM_ENABLE_WASI_EPHEMERAL_NN/WAMR_BUILD_WASI_EPHEMERAL_NN.) +#endif + +#define HASHMAP_INITIAL_SIZE 20 +#if defined(__APPLE__) +#define LIB_EXTENTION ".dylib" +#else +#define LIB_EXTENTION ".so" +#endif +#define TFLITE_BACKEND_LIB "libwasi_nn_tflite" LIB_EXTENTION +#define OPENVINO_BACKEND_LIB "libwasi_nn_openvino" LIB_EXTENTION +#define LLAMACPP_BACKEND_LIB "libwasi_nn_llamacpp" LIB_EXTENTION +#define ONNX_BACKEND_LIB "libwasi_nn_onnx" LIB_EXTENTION + +/* Global variables */ +static korp_mutex wasi_nn_lock; +/* + * the "lookup" table is protected by wasi_nn_lock. + * + * an exception: during wasm_runtime_destroy, wasi_nn_destroy tears down + * the table without acquiring the lock. it's ok because there should be + * no other threads using the runtime at this point. + */ +struct backends_api_functions { + void *backend_handle; + api_function functions; +} lookup[autodetect + 1] = { 0 }; + +#define call_wasi_nn_func(backend_encoding, func, wasi_error, ...) \ + do { \ + wasi_error = lookup[backend_encoding].functions.func(__VA_ARGS__); \ + if (wasi_error != success) \ + NN_ERR_PRINTF("Error %s() -> %d", #func, wasi_error); \ + } while (0) + +static void *wasi_nn_key; + +static void +wasi_nn_ctx_destroy(WASINNContext *wasi_nn_ctx) +{ + if (wasi_nn_ctx == NULL) { + return; + } + NN_DBG_PRINTF("[WASI NN] DEINIT..."); + NN_DBG_PRINTF("Freeing wasi-nn"); + NN_DBG_PRINTF("-> current_encoding: %d", wasi_nn_ctx->backend); + + bh_assert(!wasi_nn_ctx->busy); + + /* deinit() the backend */ + if (wasi_nn_ctx->is_backend_ctx_initialized) { + wasi_nn_error res; + call_wasi_nn_func(wasi_nn_ctx->backend, deinit, res, + wasi_nn_ctx->backend_ctx); + } + + os_mutex_destroy(&wasi_nn_ctx->lock); + wasm_runtime_free(wasi_nn_ctx); +} + +static void +dtor(wasm_module_inst_t inst, void *ctx) +{ + wasi_nn_ctx_destroy(ctx); +} + +bool +wasi_nn_initialize() +{ + NN_DBG_PRINTF("[WASI NN General] Initializing wasi-nn"); + + if (os_mutex_init(&wasi_nn_lock)) { + NN_ERR_PRINTF("Error while initializing global lock"); + return false; + } + + wasi_nn_key = wasm_runtime_create_context_key(dtor); + if (wasi_nn_key == NULL) { + NN_ERR_PRINTF("Failed to create context key"); + os_mutex_destroy(&wasi_nn_lock); + return false; + } + + return true; +} + +static WASINNContext * +wasi_nn_initialize_context() +{ + NN_DBG_PRINTF("[WASI NN] INIT..."); + + 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; + } + + memset(wasi_nn_ctx, 0, sizeof(WASINNContext)); + if (os_mutex_init(&wasi_nn_ctx->lock)) { + NN_ERR_PRINTF("Error when initializing a lock for WASI-NN context"); + wasm_runtime_free(wasi_nn_ctx); + return NULL; + } + return wasi_nn_ctx; +} + +/* Get wasi-nn context from module instance */ +static WASINNContext * +wasm_runtime_get_wasi_nn_ctx(wasm_module_inst_t instance) +{ + WASINNContext *wasi_nn_ctx = + wasm_runtime_get_context(instance, wasi_nn_key); + if (wasi_nn_ctx == NULL) { + WASINNContext *newctx = wasi_nn_initialize_context(); + if (newctx == NULL) + return NULL; + os_mutex_lock(&wasi_nn_lock); + wasi_nn_ctx = wasm_runtime_get_context(instance, wasi_nn_key); + if (wasi_nn_ctx == NULL) { + wasm_runtime_set_context_spread(instance, wasi_nn_key, newctx); + wasi_nn_ctx = newctx; + newctx = NULL; + } + os_mutex_unlock(&wasi_nn_lock); + if (newctx != NULL) { + wasi_nn_ctx_destroy(newctx); + } + } + return wasi_nn_ctx; +} + +static WASINNContext * +lock_ctx(wasm_module_inst_t instance) +{ + WASINNContext *wasi_nn_ctx = wasm_runtime_get_wasi_nn_ctx(instance); + if (wasi_nn_ctx == NULL) { + return NULL; + } + os_mutex_lock(&wasi_nn_ctx->lock); + if (wasi_nn_ctx->busy) { + os_mutex_unlock(&wasi_nn_ctx->lock); + return NULL; + } + wasi_nn_ctx->busy = true; + os_mutex_unlock(&wasi_nn_ctx->lock); + return wasi_nn_ctx; +} + +static void +unlock_ctx(WASINNContext *wasi_nn_ctx) +{ + if (wasi_nn_ctx == NULL) { + return; + } + os_mutex_lock(&wasi_nn_ctx->lock); + bh_assert(wasi_nn_ctx->busy); + wasi_nn_ctx->busy = false; + os_mutex_unlock(&wasi_nn_ctx->lock); +} + +void +wasi_nn_destroy() +{ + wasm_runtime_destroy_context_key(wasi_nn_key); + + // close backends' libraries and registered functions + for (unsigned i = 0; i < sizeof(lookup) / sizeof(lookup[0]); i++) { + if (lookup[i].backend_handle) { + dlclose(lookup[i].backend_handle); + lookup[i].backend_handle = NULL; + } + + memset(&lookup[i].functions, 0, sizeof(api_function)); + } + + os_mutex_destroy(&wasi_nn_lock); +} + +/* Utils */ + +/* + *TODO: choose a proper backend based on + * - hardware + * - model file format + * - on device ML framework + */ +static graph_encoding +choose_a_backend() +{ + void *handle; + + handle = dlopen(LLAMACPP_BACKEND_LIB, RTLD_LAZY); + if (handle) { + NN_INFO_PRINTF("Using llama.cpp backend"); + dlclose(handle); + return ggml; + } + +#ifndef NDEBUG + NN_WARN_PRINTF("%s", dlerror()); +#endif + + handle = dlopen(OPENVINO_BACKEND_LIB, RTLD_LAZY); + if (handle) { + NN_INFO_PRINTF("Using openvino backend"); + dlclose(handle); + return openvino; + } + +#ifndef NDEBUG + NN_WARN_PRINTF("%s", dlerror()); +#endif + + handle = dlopen(ONNX_BACKEND_LIB, RTLD_LAZY); + if (handle) { + NN_INFO_PRINTF("Using onnx backend"); + dlclose(handle); + return onnx; + } + +#ifndef NDEBUG + NN_WARN_PRINTF("%s", dlerror()); +#endif + + handle = dlopen(TFLITE_BACKEND_LIB, RTLD_LAZY); + if (handle) { + NN_INFO_PRINTF("Using tflite backend"); + dlclose(handle); + return tensorflowlite; + } + +#ifndef NDEBUG + NN_WARN_PRINTF("%s", dlerror()); +#endif + + NN_WARN_PRINTF("No backend found"); + return unknown_backend; +} + +static bool +register_backend(void *handle, api_function *functions) +{ + BACKEND_INITIALIZE init = (BACKEND_INITIALIZE)dlsym(handle, "init_backend"); + if (!init) { + NN_WARN_PRINTF("init_backend() not found"); + return false; + } + functions->init = init; + + BACKEND_DEINITIALIZE deinit = + (BACKEND_DEINITIALIZE)dlsym(handle, "deinit_backend"); + if (!deinit) { + NN_WARN_PRINTF("deinit_backend() not found"); + return false; + } + functions->deinit = deinit; + + LOAD load = (LOAD)dlsym(handle, "load"); + if (!load) { + NN_WARN_PRINTF("load() not found"); + return false; + } + functions->load = load; + + LOAD_BY_NAME load_by_name = (LOAD_BY_NAME)dlsym(handle, "load_by_name"); + if (!load_by_name) { + NN_WARN_PRINTF("load_by_name() not found"); + return false; + } + functions->load_by_name = load_by_name; + + LOAD_BY_NAME_WITH_CONFIG load_by_name_with_config = + (LOAD_BY_NAME_WITH_CONFIG)dlsym(handle, "load_by_name_with_config"); + if (!load_by_name_with_config) { + NN_WARN_PRINTF("load_by_name_with_config() not found"); + // since only llama.cpp backend need to support this function + } + functions->load_by_name_with_config = load_by_name_with_config; + + INIT_EXECUTION_CONTEXT init_execution_context = + (INIT_EXECUTION_CONTEXT)dlsym(handle, "init_execution_context"); + if (!init_execution_context) { + NN_WARN_PRINTF("init_execution_context() not found"); + return false; + } + functions->init_execution_context = init_execution_context; + + SET_INPUT set_input = (SET_INPUT)dlsym(handle, "set_input"); + if (!set_input) { + NN_WARN_PRINTF("set_input() not found"); + return false; + } + functions->set_input = set_input; + + COMPUTE compute = (COMPUTE)dlsym(handle, "compute"); + if (!compute) { + NN_WARN_PRINTF("compute() not found"); + return false; + } + functions->compute = compute; + + GET_OUTPUT get_output = (GET_OUTPUT)dlsym(handle, "get_output"); + if (!get_output) { + NN_WARN_PRINTF("get_output() not found"); + return false; + } + functions->get_output = get_output; + + return true; +} + +static bool +prepare_backend(const char *lib_name, struct backends_api_functions *backend) +{ + NN_DBG_PRINTF("[Native Register] prepare_backend %s", lib_name); + + void *handle; + handle = dlopen(lib_name, RTLD_LAZY); + if (!handle) { + NN_ERR_PRINTF("Error loading %s. %s", lib_name, dlerror()); + return false; + } + + if (!register_backend(handle, &(backend->functions))) { + NN_ERR_PRINTF("Error when registering functions of %s", lib_name); + dlclose(handle); + return false; + } + + backend->backend_handle = handle; + return true; +} + +static const char * +graph_encoding_to_backend_lib_name(graph_encoding encoding) +{ + switch (encoding) { + case openvino: + return OPENVINO_BACKEND_LIB; + case tensorflowlite: + return TFLITE_BACKEND_LIB; + case ggml: + return LLAMACPP_BACKEND_LIB; + case onnx: + return ONNX_BACKEND_LIB; + default: + return NULL; + } +} + +static bool +detect_and_load_backend(graph_encoding backend_hint, + graph_encoding *loaded_backend) +{ + bool ret; + + if (backend_hint > autodetect) + return false; + + if (backend_hint == autodetect) + backend_hint = choose_a_backend(); + + if (backend_hint == unknown_backend) + return false; + + *loaded_backend = backend_hint; + + os_mutex_lock(&wasi_nn_lock); + /* if already loaded */ + if (lookup[backend_hint].backend_handle) { + os_mutex_unlock(&wasi_nn_lock); + return true; + } + + const char *backend_lib_name = + graph_encoding_to_backend_lib_name(backend_hint); + if (!backend_lib_name) { + os_mutex_unlock(&wasi_nn_lock); + return false; + } + + ret = prepare_backend(backend_lib_name, lookup + backend_hint); + os_mutex_unlock(&wasi_nn_lock); + return ret; +} + +static wasi_nn_error +ensure_backend(wasm_module_inst_t instance, graph_encoding encoding, + WASINNContext *wasi_nn_ctx) +{ + wasi_nn_error res; + + graph_encoding loaded_backend = autodetect; + if (!detect_and_load_backend(encoding, &loaded_backend)) { + res = invalid_encoding; + NN_ERR_PRINTF("load backend failed"); + goto fail; + } + + if (wasi_nn_ctx->is_backend_ctx_initialized) { + if (wasi_nn_ctx->backend != loaded_backend) { + res = unsupported_operation; + goto fail; + } + } + else { + wasi_nn_ctx->backend = loaded_backend; + + /* init() the backend */ + call_wasi_nn_func(wasi_nn_ctx->backend, init, res, + &wasi_nn_ctx->backend_ctx); + if (res != success) + goto fail; + + wasi_nn_ctx->is_backend_ctx_initialized = true; + } + return success; +fail: + return res; +} + +/* WASI-NN implementation */ + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +wasi_nn_error +wasi_nn_load(wasm_exec_env_t exec_env, graph_builder_wasm *builder, + uint32_t builder_wasm_size, graph_encoding encoding, + execution_target target, graph *g) +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ +wasi_nn_error +wasi_nn_load(wasm_exec_env_t exec_env, graph_builder_array_wasm *builder, + graph_encoding encoding, execution_target target, graph *g) +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ +{ + wasi_nn_error res; + + NN_DBG_PRINTF("[WASI NN] LOAD [encoding=%d, target=%d]...", encoding, + target); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) + return runtime_error; + + WASINNContext *wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + + graph_builder_array builder_native = { 0 }; +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + if (success + != (res = graph_builder_array_app_native( + instance, builder, builder_wasm_size, &builder_native))) + goto fail; +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ + if (success + != (res = graph_builder_array_app_native(instance, builder, + &builder_native))) + goto fail; +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ + + if (!wasm_runtime_validate_native_addr(instance, g, + (uint64)sizeof(graph))) { + NN_ERR_PRINTF("graph is invalid"); + res = invalid_argument; + goto fail; + } + + res = ensure_backend(instance, encoding, wasi_nn_ctx); + if (res != success) + goto fail; + + call_wasi_nn_func(wasi_nn_ctx->backend, load, res, wasi_nn_ctx->backend_ctx, + &builder_native, encoding, target, g); + if (res != success) + goto fail; + +fail: + // XXX: Free intermediate structure pointers + if (builder_native.buf) + wasm_runtime_free(builder_native.buf); + unlock_ctx(wasi_nn_ctx); + + return res; +} + +static wasi_nn_error +copyin_and_nul_terminate(wasm_module_inst_t inst, char *name, uint32_t name_len, + char **resultp) +{ + char *nul_terminated_name; + if (!wasm_runtime_validate_native_addr(inst, name, name_len)) { + return invalid_argument; + } + nul_terminated_name = wasm_runtime_malloc(name_len + 1); + if (nul_terminated_name == NULL) { + return runtime_error; + } + bh_memcpy_s(nul_terminated_name, name_len + 1, name, name_len); + nul_terminated_name[name_len] = '\0'; /* ensure NUL termination */ + if (strlen(nul_terminated_name) != name_len) { + /* reject names containing '\0' for now */ + wasm_runtime_free(nul_terminated_name); + return invalid_argument; + } + *resultp = nul_terminated_name; + return success; +} + +wasi_nn_error +wasi_nn_load_by_name(wasm_exec_env_t exec_env, char *name, uint32_t name_len, + graph *g) +{ + WASINNContext *wasi_nn_ctx = NULL; + char *nul_terminated_name = NULL; + wasi_nn_error res; + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) { + return runtime_error; + } + + if (!wasm_runtime_validate_native_addr(instance, g, + (uint64)sizeof(graph))) { + NN_ERR_PRINTF("graph is invalid"); + return invalid_argument; + } + + res = copyin_and_nul_terminate(instance, name, name_len, + &nul_terminated_name); + if (res != success) { + goto fail; + } + + NN_DBG_PRINTF("[WASI NN] LOAD_BY_NAME %s...", nul_terminated_name); + + wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + + res = ensure_backend(instance, autodetect, wasi_nn_ctx); + if (res != success) + goto fail; + + call_wasi_nn_func(wasi_nn_ctx->backend, load_by_name, res, + wasi_nn_ctx->backend_ctx, nul_terminated_name, name_len, + g); + if (res != success) + goto fail; + + res = success; +fail: + if (nul_terminated_name != NULL) { + wasm_runtime_free(nul_terminated_name); + } + if (wasi_nn_ctx != NULL) { + unlock_ctx(wasi_nn_ctx); + } + return res; +} + +wasi_nn_error +wasi_nn_load_by_name_with_config(wasm_exec_env_t exec_env, char *name, + int32_t name_len, char *config, + int32_t config_len, graph *g) +{ + WASINNContext *wasi_nn_ctx = NULL; + char *nul_terminated_name = NULL; + char *nul_terminated_config = NULL; + wasi_nn_error res; + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) { + return runtime_error; + } + + if (!wasm_runtime_validate_native_addr(instance, g, + (uint64)sizeof(graph))) { + NN_ERR_PRINTF("graph is invalid"); + return invalid_argument; + } + + res = copyin_and_nul_terminate(instance, name, name_len, + &nul_terminated_name); + if (res != success) { + goto fail; + } + res = copyin_and_nul_terminate(instance, config, config_len, + &nul_terminated_config); + if (res != success) { + goto fail; + } + + NN_DBG_PRINTF("[WASI NN] LOAD_BY_NAME_WITH_CONFIG %s %s...", + nul_terminated_name, nul_terminated_config); + + wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + + res = ensure_backend(instance, autodetect, wasi_nn_ctx); + if (res != success) + goto fail; + ; + + call_wasi_nn_func(wasi_nn_ctx->backend, load_by_name_with_config, res, + wasi_nn_ctx->backend_ctx, nul_terminated_name, name_len, + nul_terminated_config, config_len, g); + if (res != success) + goto fail; + + res = success; +fail: + if (nul_terminated_name != NULL) { + wasm_runtime_free(nul_terminated_name); + } + if (nul_terminated_config != NULL) { + wasm_runtime_free(nul_terminated_config); + } + if (wasi_nn_ctx != NULL) { + unlock_ctx(wasi_nn_ctx); + } + return res; +} + +wasi_nn_error +wasi_nn_init_execution_context(wasm_exec_env_t exec_env, graph g, + graph_execution_context *ctx) +{ + NN_DBG_PRINTF("[WASI NN] INIT_EXECUTION_CONTEXT..."); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) { + return runtime_error; + } + + wasi_nn_error res; + WASINNContext *wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + + if (!wasm_runtime_validate_native_addr( + instance, ctx, (uint64)sizeof(graph_execution_context))) { + NN_ERR_PRINTF("ctx is invalid"); + res = invalid_argument; + goto fail; + } + + call_wasi_nn_func(wasi_nn_ctx->backend, init_execution_context, res, + wasi_nn_ctx->backend_ctx, g, ctx); +fail: + unlock_ctx(wasi_nn_ctx); + return res; +} + +wasi_nn_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("[WASI NN] SET_INPUT [ctx=%d, index=%d]...", ctx, index); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) { + return runtime_error; + } + + wasi_nn_error res; + WASINNContext *wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + + tensor input_tensor_native = { 0 }; + if (success + != (res = tensor_app_native(instance, input_tensor, + &input_tensor_native))) + goto fail; + + call_wasi_nn_func(wasi_nn_ctx->backend, set_input, res, + wasi_nn_ctx->backend_ctx, ctx, index, + &input_tensor_native); + // XXX: Free intermediate structure pointers + if (input_tensor_native.dimensions) + wasm_runtime_free(input_tensor_native.dimensions); +fail: + unlock_ctx(wasi_nn_ctx); + return res; +} + +wasi_nn_error +wasi_nn_compute(wasm_exec_env_t exec_env, graph_execution_context ctx) +{ + NN_DBG_PRINTF("[WASI NN] COMPUTE [ctx=%d]...", ctx); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) { + return runtime_error; + } + + wasi_nn_error res; + WASINNContext *wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + + call_wasi_nn_func(wasi_nn_ctx->backend, compute, res, + wasi_nn_ctx->backend_ctx, ctx); +fail: + unlock_ctx(wasi_nn_ctx); + return res; +} + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 +wasi_nn_error +wasi_nn_get_output(wasm_exec_env_t exec_env, graph_execution_context ctx, + uint32_t index, void *output_tensor, + uint32_t output_tensor_len, uint32_t *output_tensor_size) +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ +wasi_nn_error +wasi_nn_get_output(wasm_exec_env_t exec_env, graph_execution_context ctx, + uint32_t index, void *output_tensor, + uint32_t *output_tensor_size) +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ +{ + NN_DBG_PRINTF("[WASI NN] GET_OUTPUT [ctx=%d, index=%d]...", ctx, index); + + wasm_module_inst_t instance = wasm_runtime_get_module_inst(exec_env); + if (!instance) { + return runtime_error; + } + + wasi_nn_error res; + WASINNContext *wasi_nn_ctx = lock_ctx(instance); + if (wasi_nn_ctx == NULL) { + res = busy; + goto fail; + } + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + if (!wasm_runtime_validate_native_addr(instance, output_tensor, + output_tensor_len)) { + NN_ERR_PRINTF("output_tensor is invalid"); + res = invalid_argument; + goto fail; + } +#else + if (!wasm_runtime_validate_native_addr(instance, output_tensor, + *output_tensor_size)) { + NN_ERR_PRINTF("output_tensor is invalid"); + res = invalid_argument; + goto fail; + } +#endif + + if (!wasm_runtime_validate_native_addr(instance, output_tensor_size, + (uint64)sizeof(uint32_t))) { + NN_ERR_PRINTF("output_tensor_size is invalid"); + res = invalid_argument; + goto fail; + } + + tensor_data tensor = { + .buf = output_tensor, +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + .size = output_tensor_len, +#else + .size = *output_tensor_size, +#endif + }; + call_wasi_nn_func(wasi_nn_ctx->backend, get_output, res, + wasi_nn_ctx->backend_ctx, ctx, index, &tensor, + output_tensor_size); +fail: + unlock_ctx(wasi_nn_ctx); + 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[] = { +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + REG_NATIVE_FUNC(load, "(*iii*)i"), + REG_NATIVE_FUNC(load_by_name, "(*i*)i"), + + /* load_by_name_with_config is intented to be compatible with + * a wasmedge extension. + * https://github.com/second-state/wasmedge-wasi-nn/pull/2 + * https://github.com/WasmEdge/WasmEdge/blob/5553924e8cdccdc2cbd2a6a6d0ed9b11250c353e/plugins/wasi_nn/wasinnmodule.cpp#L13-L14 + */ + REG_NATIVE_FUNC(load_by_name_with_config, "(*i*i*)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*)i"), +#else /* WASM_ENABLE_WASI_EPHEMERAL_NN == 0 */ + REG_NATIVE_FUNC(load, "(*ii*)i"), + REG_NATIVE_FUNC(load_by_name, "(*i*)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"), +#endif /* WASM_ENABLE_WASI_EPHEMERAL_NN != 0 */ +}; + +uint32_t +get_wasi_nn_export_apis(NativeSymbol **p_native_symbols) +{ + *p_native_symbols = native_symbols_wasi_nn; + return sizeof(native_symbols_wasi_nn) / sizeof(NativeSymbol); +} diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_backend.h b/core/iwasm/libraries/wasi-nn/src/wasi_nn_backend.h new file mode 100644 index 0000000000..8cd03f1214 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_backend.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_NN_BACKEND_H +#define WASI_NN_BACKEND_H + +#include "wasi_nn_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((visibility("default"))) wasi_nn_error +load(void *ctx, graph_builder_array *builder, graph_encoding encoding, + execution_target target, graph *g); + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name(void *tflite_ctx, const char *name, uint32_t namelen, graph *g); + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name_with_config(void *ctx, const char *name, uint32_t namelen, + const char *config, uint32_t config_len, graph *g); + +__attribute__((visibility("default"))) wasi_nn_error +init_execution_context(void *ctx, graph g, graph_execution_context *exec_ctx); + +__attribute__((visibility("default"))) wasi_nn_error +set_input(void *ctx, graph_execution_context exec_ctx, uint32_t index, + tensor *input_tensor); + +__attribute__((visibility("default"))) wasi_nn_error +compute(void *ctx, graph_execution_context exec_ctx); + +__attribute__((visibility("default"))) wasi_nn_error +get_output(void *ctx, graph_execution_context exec_ctx, uint32_t index, + tensor_data *output_tensor, uint32_t *output_tensor_size); + +__attribute__((visibility("default"))) wasi_nn_error +init_backend(void **ctx); + +__attribute__((visibility("default"))) wasi_nn_error +deinit_backend(void *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* WASI_NN_BACKEND_H */ diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_llamacpp.c b/core/iwasm/libraries/wasi-nn/src/wasi_nn_llamacpp.c new file mode 100644 index 0000000000..d895aba992 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_llamacpp.c @@ -0,0 +1,670 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "wasi_nn_backend.h" +#include "utils/logger.h" +#include "llama.h" +#include "ggml.h" +#include "cJSON.h" + +// build info +extern int LLAMA_BUILD_NUMBER; +extern char const *LLAMA_COMMIT; +extern char const *LLAMA_COMPILER; +extern char const *LLAMA_BUILD_TARGET; + +#if WASM_ENABLE_WASI_EPHEMERAL_NN == 0 +#error This backend doesn't support legacy "wasi_nn" abi. Please enable WASM_ENABLE_WASI_EPHEMERAL_NN. +#endif + +// compatible with WasmEdge +// https://github.com/second-state/WasmEdge-WASINN-examples/blob/master/wasmedge-ggml/README.md#parameters +// https://github.com/WasmEdge/WasmEdge/blob/master/plugins/wasi_nn/ggml.cpp +struct wasi_nn_llama_config { + // Backend(plugin in WasmEdge) parameters: + bool enable_log; + bool enable_debug_log; + bool stream_stdout; + // embedding mode + bool embedding; + // TODO: can it be -1? + // can't bigger than ctx_size + int32_t n_predict; + char *reverse_prompt; + + // Used by LLaVA + // multi-model project file + char *mmproj; + char *image; + + // Model parameters (need to reload the model if updated): + // align to definition of struct llama_model_params + int32_t n_gpu_layers; + int32_t main_gpu; + // limited size: llama_max_devices() + float *tensor_split; + bool use_mmap; + + // Context parameters (used by the llama context): + uint32_t ctx_size; + uint32_t batch_size; + uint32_t ubatch_size; + uint32_t threads; + + // Sampling parameters (used by the llama sampling context). + float temp; + float topP; + float repeat_penalty; + float presence_penalty; + float frequency_penalty; +}; + +struct LlamaContext { + struct llama_context *ctx; + struct llama_model *model; + llama_token *prompt; + size_t prompt_len; + llama_token *generation; + size_t generation_len; + struct wasi_nn_llama_config config; +}; + +static void +wasm_edge_llama_default_configuration(struct wasi_nn_llama_config *output) +{ + output->enable_log = false; + output->enable_debug_log = false; + output->stream_stdout = false; + output->embedding = false; + output->n_predict = 512; + output->reverse_prompt = NULL; + + output->mmproj = NULL; + output->image = NULL; + + output->main_gpu = 0; + output->n_gpu_layers = 0; + output->tensor_split = NULL; + output->use_mmap = true; + + // 0 = from model + output->ctx_size = 0; + output->batch_size = 512; + output->ubatch_size = output->batch_size; + output->threads = 1; + + output->temp = 0.80; + output->topP = 0.95; + output->repeat_penalty = 1.10; + output->presence_penalty = 0.0; + output->frequency_penalty = 0.0; +} + +static void +wasm_edge_llama_apply_configuration(const char *config_json, + struct wasi_nn_llama_config *output) +{ + cJSON *root = cJSON_Parse(config_json); + if (root == NULL) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { + NN_WARN_PRINTF("Error before: %s\n", error_ptr); + } + else { + NN_WARN_PRINTF("Failed to parse JSON"); + } + return; + } + + cJSON *item = NULL; + + item = cJSON_GetObjectItem(root, "enable-log"); + if (item != NULL) { + output->enable_log = cJSON_IsTrue(item); + NN_DBG_PRINTF("apply enable-log %d", output->enable_log); + } + + item = cJSON_GetObjectItem(root, "enable-debug-log"); + if (item != NULL) { + output->enable_debug_log = cJSON_IsTrue(item); + NN_DBG_PRINTF("apply enable-debug-log %d", output->enable_debug_log); + } + + item = cJSON_GetObjectItem(root, "stream-stdout"); + if (item != NULL) { + output->stream_stdout = cJSON_IsTrue(item); + NN_DBG_PRINTF("apply stream-stdout %d", output->stream_stdout); + } + + item = cJSON_GetObjectItem(root, "embedding"); + if (item != NULL) { + output->embedding = cJSON_IsTrue(item); + NN_DBG_PRINTF("apply embedding %d", output->embedding); + } + + item = cJSON_GetObjectItem(root, "n-predict"); + if (item != NULL) { + output->n_predict = (int32_t)cJSON_GetNumberValue(item); + NN_DBG_PRINTF("apply n-predict %d", output->n_predict); + } + + item = cJSON_GetObjectItem(root, "n-gpu-layers"); + if (item != NULL) { + output->n_gpu_layers = (int32_t)cJSON_GetNumberValue(item); + NN_DBG_PRINTF("apply n_gpu_layers %d", output->n_gpu_layers); + } + + item = cJSON_GetObjectItem(root, "ctx-size"); + if (item != NULL) { + output->ctx_size = (uint32_t)cJSON_GetNumberValue(item); + NN_DBG_PRINTF("apply ctx-size %d", output->ctx_size); + } + + // more ... + + cJSON_Delete(root); +} + +static struct llama_model_params +llama_model_params_from_wasi_nn_llama_config( + struct wasi_nn_llama_config *config) +{ + struct llama_model_params result = llama_model_default_params(); + + // TODO: support more + result.main_gpu = config->main_gpu; + result.n_gpu_layers = config->n_gpu_layers; + result.use_mmap = config->use_mmap; + + return result; +} + +static struct llama_context_params +llama_context_params_from_wasi_nn_llama_config( + struct wasi_nn_llama_config *config) +{ + struct llama_context_params result = llama_context_default_params(); + + // TODO: support more + result.n_ctx = config->ctx_size; + // result.embeddings = config->embedding; + + return result; +} + +static void +llama_batch_clear(struct llama_batch *batch) +{ + batch->n_tokens = 0; +} + +static void +llama_batch_add(struct llama_batch *batch, llama_token id, llama_pos pos, + llama_seq_id *seq_ids, size_t seq_ids_len, bool logits) +{ + batch->token[batch->n_tokens] = id; + batch->pos[batch->n_tokens] = pos; + batch->n_seq_id[batch->n_tokens] = seq_ids_len; + for (size_t i = 0; i < seq_ids_len; ++i) { + batch->seq_id[batch->n_tokens][i] = seq_ids[i]; + } + batch->logits[batch->n_tokens] = logits; + + batch->n_tokens++; +} + +// always output ERROR and WARN +// INFO needs enable_log +// DEBUG needs enable_debug_log +static void +llama_log_callback_local(enum ggml_log_level level, const char *text, + void *user_data) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)user_data; + + if (level == GGML_LOG_LEVEL_DEBUG && !backend_ctx->config.enable_debug_log) + return; + + if (level == GGML_LOG_LEVEL_INFO && !backend_ctx->config.enable_log) + return; + + printf("%s", text); +} + +static void +llama_build_output_metadata(const struct LlamaContext *backend_ctx, + char *output_buf, size_t output_buf_size) +{ + snprintf(output_buf, output_buf_size, + "{\"input_tokens\":%ld, \"output_tokens\":%ld, " + "\"llama_build_number\":%d," + "\"llama_commit\":\"%s\"}", + backend_ctx->prompt_len, backend_ctx->generation_len, + LLAMA_BUILD_NUMBER, LLAMA_COMMIT); +} + +__attribute__((visibility("default"))) wasi_nn_error +init_backend(void **ctx) +{ + struct LlamaContext *backend_ctx = calloc(1, sizeof(struct LlamaContext)); + if (!backend_ctx) { + NN_ERR_PRINTF("Allocate for OpenVINOContext failed"); + return runtime_error; + } + + llama_backend_init(); + // llama_numa_init(); + llama_log_set(llama_log_callback_local, backend_ctx); + +#ifndef NDEBUG + NN_INFO_PRINTF("llama_build_number: % d, llama_commit: %s, llama_compiler: " + "%s, llama_build_target: %s", + LLAMA_BUILD_NUMBER, LLAMA_COMMIT, LLAMA_COMPILER, + LLAMA_BUILD_TARGET); +#endif + + *ctx = (void *)backend_ctx; + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +deinit_backend(void *ctx) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + if (!backend_ctx) + return invalid_argument; + + if (backend_ctx->generation) + free(backend_ctx->generation); + + if (backend_ctx->prompt) + free(backend_ctx->prompt); + + if (backend_ctx->ctx) + llama_free(backend_ctx->ctx); + + if (backend_ctx->model) + llama_free_model(backend_ctx->model); + + llama_backend_free(); + + free(backend_ctx); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +load(void *ctx, graph_builder_array *builder, graph_encoding encoding, + execution_target target, graph *g) +{ + return unsupported_operation; +} + +static wasi_nn_error +__load_by_name_with_configuration(void *ctx, const char *filename, graph *g) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + if (backend_ctx->model != NULL) { + // we only implement a single graph + return unsupported_operation; + } + + // make sure backend_ctx->config is initialized + + struct llama_model_params model_params = + llama_model_params_from_wasi_nn_llama_config(&backend_ctx->config); + struct llama_model *model = + llama_load_model_from_file(filename, model_params); + if (model == NULL) { + NN_ERR_PRINTF("Failed to load model from file %s", filename); + return runtime_error; + } + +#ifndef NDEBUG + char buf[128] = { 0 }; + llama_model_desc(model, buf, 127); + NN_INFO_PRINTF("Model desc %s", buf); +#endif + + backend_ctx->model = model; + *g = 0; + + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name(void *ctx, const char *filename, uint32_t filename_len, graph *g) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + // use default params + wasm_edge_llama_default_configuration(&backend_ctx->config); + return __load_by_name_with_configuration(ctx, filename, g); +} + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name_with_config(void *ctx, const char *filename, uint32_t filename_len, + const char *config, uint32_t config_len, graph *g) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + wasm_edge_llama_default_configuration(&backend_ctx->config); + + if (config != NULL) { + // parse wasmedge config + wasm_edge_llama_apply_configuration(config, &backend_ctx->config); + } + else { + NN_INFO_PRINTF("No configuration provided, use default"); + } + + return __load_by_name_with_configuration(ctx, filename, g); +} + +// It is assumed that model params shouldn't be changed in Config stage. +// We only load the model once in the Load stage. +__attribute__((visibility("default"))) wasi_nn_error +init_execution_context(void *ctx, graph g, graph_execution_context *exec_ctx) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + if (g != 0 || backend_ctx->model == NULL) { + // we only implement a single graph + return runtime_error; + } + + if (backend_ctx->ctx != NULL) { + // we only implement a single context + return unsupported_operation; + } + + struct llama_context_params ctx_params = + llama_context_params_from_wasi_nn_llama_config(&backend_ctx->config); + struct llama_context *llama_ctx = + llama_new_context_with_model(backend_ctx->model, ctx_params); + if (llama_ctx == NULL) { + NN_ERR_PRINTF("Failed to create context for model"); + return runtime_error; + } + + backend_ctx->ctx = llama_ctx; + *exec_ctx = 0; + + NN_INFO_PRINTF("n_predict = %d, n_ctx = %d", backend_ctx->config.n_predict, + llama_n_ctx(backend_ctx->ctx)); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +set_input(void *ctx, graph_execution_context exec_ctx, uint32_t index, + tensor *wasi_nn_tensor) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + if (exec_ctx != 0 || backend_ctx->ctx == NULL) { + // we only implement a single context + return runtime_error; + } + + if (index != 0) { + NN_ERR_PRINTF("Invalid input index %d", index); + return invalid_argument; + } + + // tensor->data is the prompt string. + char *prompt_text = (char *)wasi_nn_tensor->data.buf; + uint32_t prompt_text_len = wasi_nn_tensor->data.size; + + // note: buf[0] == 1 is a workaround for + // https://github.com/second-state/WasmEdge-WASINN-examples/issues/196. + // we may remove it in future. + if (wasi_nn_tensor->type != u8 || wasi_nn_tensor->dimensions->size != 1 + || !(wasi_nn_tensor->dimensions->buf[0] == 1 + || wasi_nn_tensor->dimensions->buf[0] == prompt_text_len)) { + return invalid_argument; + } + if (wasi_nn_tensor->dimensions->buf[0] == 1 && prompt_text_len != 1) { + NN_WARN_PRINTF("Ignoring seemingly wrong input tensor dimensions."); + } + +#ifndef NDEBUG + NN_DBG_PRINTF("--------------------------------------------------"); + NN_DBG_PRINTF("prompt_text: %.*s", (int)prompt_text_len, prompt_text); + NN_DBG_PRINTF("--------------------------------------------------"); +#endif + + // tokenize the prompt + uint32_t n_token_max = llama_n_ctx(backend_ctx->ctx); + + if (backend_ctx->prompt == NULL) { + backend_ctx->prompt = calloc(n_token_max, sizeof(llama_token)); + if (backend_ctx->prompt == NULL) { + NN_ERR_PRINTF("Failed to allocate tokens_list"); + return runtime_error; + } + } + + int32_t n_tokens = + llama_tokenize(backend_ctx->model, prompt_text, prompt_text_len, + backend_ctx->prompt, n_token_max, true, false); + if (n_tokens < 0) { + NN_ERR_PRINTF("Failed to tokenize prompt text"); + return runtime_error; + } + + backend_ctx->prompt_len = n_tokens; + + // make sure the KV cache is big enough to hold all the prompt and generated + // tokens + int n_kv_req = n_tokens + (backend_ctx->config.n_predict - n_tokens); + if (n_kv_req < 0 || (uint32_t)n_kv_req > n_token_max) { + NN_ERR_PRINTF("the required KV cache size is not big enough, either " + "reduce n_predict or increase n_ctx"); + return runtime_error; + } + + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +compute(void *ctx, graph_execution_context exec_ctx) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + wasi_nn_error ret = runtime_error; + + if (exec_ctx != 0 || backend_ctx->ctx == NULL) { + // we only implement a single context + return runtime_error; + } + + // reset the generation buffer + if (backend_ctx->generation == NULL) { + backend_ctx->generation = + calloc(backend_ctx->config.n_predict, sizeof(llama_token)); + if (backend_ctx->generation == NULL) { + NN_ERR_PRINTF("Failed to allocate generation"); + return runtime_error; + } + } + + backend_ctx->generation_len = 0; + + // check KV cache + uint32_t n_ctx = llama_n_ctx(backend_ctx->ctx); + if (n_ctx <= backend_ctx->generation_len) { + NN_ERR_PRINTF( + "ctx_size(%u) is not big enough(<%ld), please increase it", n_ctx, + backend_ctx->generation_len); + return context_full; + } + + // prepare the batch + struct llama_batch batch = + llama_batch_init(backend_ctx->config.batch_size, 0, 1); + + // evaluate the initial prompt + llama_seq_id seq_ids[1] = { 0 }; + for (size_t i = 0; i < backend_ctx->prompt_len; i++) { + llama_batch_add(&batch, backend_ctx->prompt[i], i, seq_ids, + sizeof(seq_ids) / sizeof(seq_ids[0]), false); + } + + batch.logits[batch.n_tokens - 1] = true; + + if (batch.n_tokens > backend_ctx->config.n_predict) { + NN_DBG_PRINTF("n_predict(%d) is not big enough(%d), please increase it", + backend_ctx->config.n_predict, batch.n_tokens); + return prompt_tool_long; + } + + if (llama_decode(backend_ctx->ctx, batch) != 0) { + NN_ERR_PRINTF("First decode failed"); + return runtime_error; + } + + // main loop + int32_t n_cur = batch.n_tokens; + int32_t n_vocab = llama_n_vocab(backend_ctx->model); + llama_token_data *candidates = NULL; + + candidates = calloc(n_vocab, sizeof(llama_token_data)); + if (candidates == NULL) { + NN_ERR_PRINTF("Failed to allocate candidates"); + goto fail; + } + + while (n_cur <= backend_ctx->config.n_predict) { + // sample the next token + float *logits = + llama_get_logits_ith(backend_ctx->ctx, batch.n_tokens - 1); + + memset(candidates, 0, sizeof(llama_token_data) * n_vocab); + for (llama_token token_id = 0; token_id < n_vocab; token_id++) { + candidates[token_id].id = token_id; + candidates[token_id].logit = logits[token_id]; + candidates[token_id].p = 0.0f; + } + + llama_token_data_array candidates_p = { candidates, n_vocab, false }; + + // sample the most likely token + llama_token new_token_id = + llama_sample_token_greedy(backend_ctx->ctx, &candidates_p); + + backend_ctx->generation[backend_ctx->generation_len++] = new_token_id; + +#ifndef NDEBUG + { + char buf[128] = { 0 }; + llama_token_to_piece(backend_ctx->model, new_token_id, buf, 120, 0, + true); + printf("%d(%s),", new_token_id, buf); + } +#endif + + // is it an end of generation? + if (llama_token_is_eog(backend_ctx->model, new_token_id)) { + printf("\n"); + NN_INFO_PRINTF("reach the end of generation"); + break; + } + + // prepare the next batch + llama_batch_clear(&batch); + // push this new token for next evaluation + llama_batch_add(&batch, new_token_id, n_cur, seq_ids, + sizeof(seq_ids) / sizeof(seq_ids[0]), true); + n_cur++; + + if (llama_decode(backend_ctx->ctx, batch) != 0) { + NN_ERR_PRINTF("Secondary decode failed"); + goto fail; + } + } + + printf("\n"); + ret = success; +fail: + llama_batch_free(batch); + if (candidates != NULL) { + free(candidates); + } + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +get_output(void *ctx, graph_execution_context exec_ctx, uint32_t index, + tensor_data *output_tensor, uint32_t *output_tensor_size) +{ + struct LlamaContext *backend_ctx = (struct LlamaContext *)ctx; + + if (exec_ctx != 0 || backend_ctx->ctx == NULL) { + // we only implement a single context + return runtime_error; + } + + // Compatibility with WasmEdge + if (index > 1) { + NN_ERR_PRINTF("Invalid output index %d", index); + return invalid_argument; + } + + // Index 1 is for the metadata of the outputs. + if (index == 1) { + char output_metadata[128] = { 0 }; + llama_build_output_metadata(backend_ctx, output_metadata, 127); + + if (backend_ctx->config.stream_stdout) { + printf("%s\n", output_metadata); + } + + size_t metadata_len = strlen(output_metadata); + if (metadata_len > output_tensor->size) { + NN_ERR_PRINTF("Output buffer too small for metadata: " + "need %zu, got %zu", + metadata_len, output_tensor->size); + return too_large; + } + memcpy(output_tensor->buf, output_metadata, metadata_len); + *output_tensor_size = metadata_len; + return success; + } + + // token -> piece -> output_tensor + if (backend_ctx->config.stream_stdout) { + printf("\n"); + } + + size_t end_pos = 0; + for (size_t i = 0; i < backend_ctx->generation_len; i++) { + char buf[128] = { 0 }; + llama_token_to_piece(backend_ctx->model, backend_ctx->generation[i], + buf, 120, 0, true); + + if (backend_ctx->config.stream_stdout) { + printf("%s", buf); + } + + size_t piece_len = strlen(buf); + if (end_pos + piece_len > output_tensor->size) { + NN_ERR_PRINTF("Output buffer too small: need at least %zu," + " got %zu", + end_pos + piece_len, output_tensor->size); + return too_large; + } + memcpy(output_tensor->buf + end_pos, buf, piece_len); + end_pos += piece_len; + } + + if (backend_ctx->config.stream_stdout) { + printf("\n"); + } + + *output_tensor_size = end_pos; + return success; +} diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_onnx.cpp b/core/iwasm/libraries/wasi-nn/src/wasi_nn_onnx.cpp new file mode 100644 index 0000000000..88587f68bc --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_onnx.cpp @@ -0,0 +1,795 @@ +/* + * Copyright 2025 Sony Semiconductor Solutions Corporation. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include "bh_platform.h" +#include "wasi_nn_backend.h" +#include "utils/logger.h" +#include "onnxruntime_c_api.h" + +#if WASM_ENABLE_WASI_EPHEMERAL_NN == 0 +#error This backend doesn't support legacy "wasi_nn" abi. Please enable WASM_ENABLE_WASI_EPHEMERAL_NN. +#endif + +/* Maximum number of graphs and execution contexts */ +#define MAX_GRAPHS 4 +#define MAX_CONTEXTS 4 + +/* Graph structure */ +typedef struct { + OrtSession *session; + bool is_initialized; +} OnnxRuntimeGraph; + +/* Execution context structure */ +typedef struct { + OrtMemoryInfo *memory_info; + std::vector input_names; + std::vector output_names; + std::unordered_map inputs; + std::unordered_map outputs; + OnnxRuntimeGraph *graph; + bool is_initialized; +} OnnxRuntimeExecCtx; + +/* ONNX Runtime context structure */ +typedef struct { + OrtEnv *env; + OrtSessionOptions *session_options; + OrtAllocator *allocator; + const OrtApi *ort_api; + std::mutex mutex; + bool is_initialized; + OnnxRuntimeGraph graphs[MAX_GRAPHS]; + OnnxRuntimeExecCtx exec_ctxs[MAX_CONTEXTS]; +} OnnxRuntimeContext; + +static wasi_nn_error +convert_ort_error_to_wasi_nn_error(const OnnxRuntimeContext *ctx, + OrtStatus *status) +{ + if (status == nullptr) { + return success; + } + + wasi_nn_error err; + OrtErrorCode code = ctx->ort_api->GetErrorCode(status); + const char *msg = ctx->ort_api->GetErrorMessage(status); + + NN_ERR_PRINTF("ONNX Runtime error: %s", msg); + + switch (code) { + case ORT_INVALID_ARGUMENT: + err = invalid_argument; + break; + case ORT_RUNTIME_EXCEPTION: + err = runtime_error; + break; + case ORT_NOT_IMPLEMENTED: + err = unsupported_operation; + break; + case ORT_INVALID_PROTOBUF: + err = invalid_encoding; + break; + case ORT_MODEL_LOADED: + err = too_large; + break; + case ORT_INVALID_GRAPH: + err = invalid_encoding; + break; + default: + err = runtime_error; + break; + } + + ctx->ort_api->ReleaseStatus(status); + return err; +} + +static bool +convert_wasi_nn_type_to_ort_type(tensor_type type, + ONNXTensorElementDataType *ort_type) +{ + switch (type) { + case fp32: + *ort_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT; + break; + case fp16: + *ort_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16; + break; + case fp64: + *ort_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE; + break; + case u8: + *ort_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8; + break; + case i32: + *ort_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32; + break; + case i64: + *ort_type = ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64; + break; + default: + NN_WARN_PRINTF("Unsupported wasi-nn tensor type: %d", type); + return false; + } + return true; +} + +/* Backend API implementation */ + +extern "C" { + +__attribute__((visibility("default"))) wasi_nn_error +init_backend(void **onnx_ctx) +{ + wasi_nn_error err = success; + OrtStatus *status = nullptr; + OnnxRuntimeContext *ctx = nullptr; + ctx = new OnnxRuntimeContext(); + ctx->ort_api = OrtGetApiBase()->GetApi(ORT_API_VERSION); + if (!ctx->ort_api) { + NN_ERR_PRINTF("Failed to get ONNX Runtime API"); + err = runtime_error; + goto fail; + } + + NN_INFO_PRINTF("Creating ONNX Runtime environment..."); + status = ctx->ort_api->CreateEnv(ORT_LOGGING_LEVEL_VERBOSE, "wasi-nn", + &ctx->env); + if (status != nullptr) { + const char *error_message = ctx->ort_api->GetErrorMessage(status); + err = convert_ort_error_to_wasi_nn_error(ctx, status); + NN_ERR_PRINTF("Failed to create ONNX Runtime environment: %s", + error_message); + goto fail; + } + NN_INFO_PRINTF("ONNX Runtime environment created successfully"); + + status = ctx->ort_api->CreateSessionOptions(&ctx->session_options); + if (status != nullptr) { + err = convert_ort_error_to_wasi_nn_error(ctx, status); + ctx->ort_api->ReleaseEnv(ctx->env); + NN_ERR_PRINTF("Failed to create ONNX Runtime session options"); + goto fail; + } + + status = ctx->ort_api->SetSessionGraphOptimizationLevel( + ctx->session_options, ORT_ENABLE_BASIC); + if (status != nullptr) { + err = convert_ort_error_to_wasi_nn_error(ctx, status); + ctx->ort_api->ReleaseSessionOptions(ctx->session_options); + ctx->ort_api->ReleaseEnv(ctx->env); + NN_ERR_PRINTF("Failed to set graph optimization level"); + goto fail; + } + + status = ctx->ort_api->GetAllocatorWithDefaultOptions(&ctx->allocator); + if (status != nullptr) { + err = convert_ort_error_to_wasi_nn_error(ctx, status); + ctx->ort_api->ReleaseSessionOptions(ctx->session_options); + ctx->ort_api->ReleaseEnv(ctx->env); + NN_ERR_PRINTF("Failed to get default allocator"); + goto fail; + } + + for (int i = 0; i < MAX_GRAPHS; i++) { + ctx->graphs[i].is_initialized = false; + ctx->graphs[i].session = nullptr; + } + + for (int i = 0; i < MAX_CONTEXTS; i++) { + ctx->exec_ctxs[i].is_initialized = false; + ctx->exec_ctxs[i].memory_info = nullptr; + ctx->exec_ctxs[i].graph = nullptr; + ctx->exec_ctxs[i].input_names.clear(); + ctx->exec_ctxs[i].output_names.clear(); + ctx->exec_ctxs[i].inputs.clear(); + ctx->exec_ctxs[i].outputs.clear(); + } + + ctx->is_initialized = true; + *onnx_ctx = ctx; + + NN_INFO_PRINTF("ONNX Runtime backend initialized"); + return success; + +fail: + delete (ctx); + return err; +} + +__attribute__((visibility("default"))) wasi_nn_error +deinit_backend(void *onnx_ctx) +{ + OnnxRuntimeContext *ctx = (OnnxRuntimeContext *)onnx_ctx; + std::lock_guard lock(ctx->mutex); + + if (!ctx->is_initialized) { + return success; + } + + for (int i = 0; i < MAX_GRAPHS; i++) { + if (ctx->graphs[i].is_initialized) { + ctx->ort_api->ReleaseSession(ctx->graphs[i].session); + ctx->graphs[i].is_initialized = false; + } + } + + for (int i = 0; i < MAX_CONTEXTS; i++) { + if (ctx->exec_ctxs[i].is_initialized) { + for (auto &input : ctx->exec_ctxs[i].inputs) { + ctx->ort_api->ReleaseValue(input.second); + } + for (auto &output : ctx->exec_ctxs[i].outputs) { + ctx->ort_api->ReleaseValue(output.second); + } + + for (auto name : ctx->exec_ctxs[i].input_names) { + free((void *)name); + } + ctx->exec_ctxs[i].input_names.clear(); + + for (auto name : ctx->exec_ctxs[i].output_names) { + free((void *)name); + } + ctx->exec_ctxs[i].output_names.clear(); + + ctx->ort_api->ReleaseMemoryInfo(ctx->exec_ctxs[i].memory_info); + ctx->exec_ctxs[i].is_initialized = false; + } + } + + ctx->ort_api->ReleaseSessionOptions(ctx->session_options); + ctx->ort_api->ReleaseEnv(ctx->env); + ctx->is_initialized = false; + + delete (ctx); + + NN_INFO_PRINTF("ONNX Runtime backend deinitialized"); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +load(void *onnx_ctx, graph_builder_array *builder, graph_encoding encoding, + execution_target target, graph *g) +{ + if (!onnx_ctx) { + return runtime_error; + } + + if (encoding != onnx) { + NN_ERR_PRINTF("Unsupported encoding: %d", encoding); + return invalid_encoding; + } + + if (target != cpu) { + NN_ERR_PRINTF("Only CPU target is supported"); + return unsupported_operation; + } + + OnnxRuntimeContext *ctx = (OnnxRuntimeContext *)onnx_ctx; + std::lock_guard lock(ctx->mutex); + + int graph_index = -1; + for (int i = 0; i < MAX_GRAPHS; i++) { + if (!ctx->graphs[i].is_initialized) { + graph_index = i; + break; + } + } + + if (graph_index == -1) { + NN_ERR_PRINTF("Maximum number of graphs reached"); + return runtime_error; + } + + if (builder->size == 0 || builder->buf == NULL) { + NN_ERR_PRINTF("No model data provided"); + return invalid_argument; + } + + NN_INFO_PRINTF("[ONNX Runtime] Loading model of size %" PRIu32 " bytes...", + builder->buf[0].size); + + if (builder->buf[0].size > 16) { + NN_INFO_PRINTF( + "Model header bytes: %02x %02x %02x %02x %02x %02x %02x %02x", + ((uint8_t *)builder->buf[0].buf)[0], + ((uint8_t *)builder->buf[0].buf)[1], + ((uint8_t *)builder->buf[0].buf)[2], + ((uint8_t *)builder->buf[0].buf)[3], + ((uint8_t *)builder->buf[0].buf)[4], + ((uint8_t *)builder->buf[0].buf)[5], + ((uint8_t *)builder->buf[0].buf)[6], + ((uint8_t *)builder->buf[0].buf)[7]); + } + + OrtStatus *status = ctx->ort_api->CreateSessionFromArray( + ctx->env, builder->buf[0].buf, builder->buf[0].size, + ctx->session_options, &ctx->graphs[graph_index].session); + + if (status != nullptr) { + const char *error_message = ctx->ort_api->GetErrorMessage(status); + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ctx, status); + NN_ERR_PRINTF("Failed to create ONNX Runtime session: %s", + error_message); + return err; + } + + NN_INFO_PRINTF("ONNX Runtime session created successfully"); + + ctx->graphs[graph_index].is_initialized = true; + *g = graph_index; + + NN_INFO_PRINTF("ONNX model loaded as graph %d", graph_index); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name(void *onnx_ctx, const char *name, uint32_t filename_len, graph *g) +{ + if (!onnx_ctx) { + return runtime_error; + } + + OnnxRuntimeContext *ctx = (OnnxRuntimeContext *)onnx_ctx; + std::lock_guard lock(ctx->mutex); + + int graph_index = -1; + for (int i = 0; i < MAX_GRAPHS; i++) { + if (!ctx->graphs[i].is_initialized) { + graph_index = i; + break; + } + } + + if (graph_index == -1) { + NN_ERR_PRINTF("Maximum number of graphs reached"); + return runtime_error; + } + + OrtStatus *status = + ctx->ort_api->CreateSession(ctx->env, name, ctx->session_options, + &ctx->graphs[graph_index].session); + + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ctx, status); + NN_ERR_PRINTF("Failed to create ONNX Runtime session from file: %s", + name); + return err; + } + + ctx->graphs[graph_index].is_initialized = true; + *g = graph_index; + + NN_INFO_PRINTF("ONNX model loaded from file %s as graph %d", name, + graph_index); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +init_execution_context(void *onnx_ctx, graph g, graph_execution_context *ctx) +{ + OnnxRuntimeContext *ort_ctx = (OnnxRuntimeContext *)onnx_ctx; + if (!onnx_ctx) { + return runtime_error; + } + + std::lock_guard lock(ort_ctx->mutex); + + if (g >= MAX_GRAPHS || !ort_ctx->graphs[g].is_initialized) { + NN_ERR_PRINTF("Invalid graph handle: %d", g); + return invalid_argument; + } + + int ctx_index = -1; + for (int i = 0; i < MAX_CONTEXTS; i++) { + if (!ort_ctx->exec_ctxs[i].is_initialized) { + ctx_index = i; + break; + } + } + + if (ctx_index == -1) { + NN_ERR_PRINTF("Maximum number of execution contexts reached"); + return runtime_error; + } + + OnnxRuntimeExecCtx *exec_ctx = &ort_ctx->exec_ctxs[ctx_index]; + exec_ctx->graph = &ort_ctx->graphs[g]; + + OrtStatus *status = ort_ctx->ort_api->CreateCpuMemoryInfo( + OrtArenaAllocator, OrtMemTypeDefault, &exec_ctx->memory_info); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + NN_ERR_PRINTF("Failed to create CPU memory info"); + return err; + } + + size_t num_input_nodes; + status = ort_ctx->ort_api->SessionGetInputCount(exec_ctx->graph->session, + &num_input_nodes); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + ort_ctx->ort_api->ReleaseMemoryInfo(exec_ctx->memory_info); + NN_ERR_PRINTF("Failed to get input count"); + return err; + } + + for (size_t i = 0; i < num_input_nodes; i++) { + char *input_name; + status = ort_ctx->ort_api->SessionGetInputName( + exec_ctx->graph->session, i, ort_ctx->allocator, &input_name); + if (status != nullptr) { + wasi_nn_error err = + convert_ort_error_to_wasi_nn_error(ort_ctx, status); + ort_ctx->ort_api->ReleaseMemoryInfo(exec_ctx->memory_info); + NN_ERR_PRINTF("Failed to get input name"); + return err; + } + exec_ctx->input_names.push_back(input_name); + } + + size_t num_output_nodes; + status = ort_ctx->ort_api->SessionGetOutputCount(exec_ctx->graph->session, + &num_output_nodes); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + ort_ctx->ort_api->ReleaseMemoryInfo(exec_ctx->memory_info); + for (const char *name : exec_ctx->input_names) { + ort_ctx->allocator->Free(ort_ctx->allocator, (void *)name); + } + NN_ERR_PRINTF("Failed to get output count"); + return err; + } + + for (size_t i = 0; i < num_output_nodes; i++) { + char *output_name; + status = ort_ctx->ort_api->SessionGetOutputName( + exec_ctx->graph->session, i, ort_ctx->allocator, &output_name); + if (status != nullptr) { + wasi_nn_error err = + convert_ort_error_to_wasi_nn_error(ort_ctx, status); + ort_ctx->ort_api->ReleaseMemoryInfo(exec_ctx->memory_info); + for (const char *name : exec_ctx->input_names) { + ort_ctx->allocator->Free(ort_ctx->allocator, (void *)name); + } + NN_ERR_PRINTF("Failed to get output name"); + return err; + } + exec_ctx->output_names.push_back(output_name); + } + + exec_ctx->is_initialized = true; + *ctx = ctx_index; + + NN_INFO_PRINTF("Execution context %d initialized for graph %d", ctx_index, + g); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +set_input(void *onnx_ctx, graph_execution_context ctx, uint32_t index, + tensor *input_tensor) +{ + OnnxRuntimeContext *ort_ctx = (OnnxRuntimeContext *)onnx_ctx; + if (!onnx_ctx) { + return runtime_error; + } + + std::lock_guard lock(ort_ctx->mutex); + + if (ctx >= MAX_CONTEXTS || !ort_ctx->exec_ctxs[ctx].is_initialized) { + NN_ERR_PRINTF("Invalid execution context handle: %d", ctx); + return invalid_argument; + } + + if (index >= ort_ctx->exec_ctxs[ctx].input_names.size()) { + NN_ERR_PRINTF("Invalid input index: %d (max: %zu)", index, + ort_ctx->exec_ctxs[ctx].input_names.size() - 1); + return invalid_argument; + } + + OnnxRuntimeExecCtx *exec_ctx = &ort_ctx->exec_ctxs[ctx]; + + OrtTypeInfo *type_info = nullptr; + OrtStatus *status = ort_ctx->ort_api->SessionGetInputTypeInfo( + exec_ctx->graph->session, index, &type_info); + if (status != nullptr) { + ort_ctx->ort_api->ReleaseTypeInfo(type_info); + return runtime_error; + } + + const OrtTensorTypeAndShapeInfo *tensor_info; + status = + ort_ctx->ort_api->CastTypeInfoToTensorInfo(type_info, &tensor_info); + if (status != nullptr) { + ort_ctx->ort_api->ReleaseTypeInfo(type_info); + return runtime_error; + } + + size_t num_model_dims; + status = ort_ctx->ort_api->GetDimensionsCount(tensor_info, &num_model_dims); + std::vector model_dims(num_model_dims); + status = ort_ctx->ort_api->GetDimensions(tensor_info, model_dims.data(), + num_model_dims); + + void *input_tensor_data = input_tensor->data.buf; + void *input_tensor_scaled_data = NULL; + ort_ctx->ort_api->ReleaseTypeInfo(type_info); + size_t num_dims = input_tensor->dimensions->size; + int64_t *ort_dims = (int64_t *)malloc(num_dims * sizeof(int64_t)); + if (!ort_dims) { + NN_ERR_PRINTF("Failed to allocate memory for tensor dimensions"); + return runtime_error; + } + + for (size_t i = 0; i < num_dims; i++) { + ort_dims[i] = input_tensor->dimensions->buf[i]; + } + + ONNXTensorElementDataType ort_type; + if (!convert_wasi_nn_type_to_ort_type( + static_cast(input_tensor->type), &ort_type)) { + NN_ERR_PRINTF("Failed to convert tensor type"); + return runtime_error; + } + + OrtValue *input_value = nullptr; + size_t total_elements = 1; + for (size_t i = 0; i < num_dims; i++) { + total_elements *= input_tensor->dimensions->buf[i]; + } + + status = ort_ctx->ort_api->CreateTensorWithDataAsOrtValue( + exec_ctx->memory_info, input_tensor->data.buf, input_tensor->data.size, + ort_dims, num_dims, ort_type, &input_value); + + free(ort_dims); + + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + NN_ERR_PRINTF("Failed to create input tensor"); + return err; + } + + if (exec_ctx->inputs.count(index) > 0) { + ort_ctx->ort_api->ReleaseValue(exec_ctx->inputs[index]); + } + exec_ctx->inputs[index] = input_value; + + NN_INFO_PRINTF("Input tensor set for context %d, index %d", ctx, index); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +compute(void *onnx_ctx, graph_execution_context ctx) +{ + OnnxRuntimeContext *ort_ctx = (OnnxRuntimeContext *)onnx_ctx; + if (!onnx_ctx) { + return runtime_error; + } + + std::lock_guard lock(ort_ctx->mutex); + + if (ctx >= MAX_CONTEXTS || !ort_ctx->exec_ctxs[ctx].is_initialized) { + NN_ERR_PRINTF("Invalid execution context handle: %d", ctx); + return invalid_argument; + } + + OnnxRuntimeExecCtx *exec_ctx = &ort_ctx->exec_ctxs[ctx]; + + std::vector input_values; + std::vector input_names; + + for (size_t i = 0; i < exec_ctx->input_names.size(); i++) { + if (exec_ctx->inputs.count(i) == 0) { + NN_ERR_PRINTF("Input tensor not set for index %zu", i); + return invalid_argument; + } + input_values.push_back(exec_ctx->inputs[i]); + input_names.push_back(exec_ctx->input_names[i]); + } + + for (auto &output : exec_ctx->outputs) { + ort_ctx->ort_api->ReleaseValue(output.second); + } + exec_ctx->outputs.clear(); + + std::vector output_values(exec_ctx->output_names.size()); + + OrtStatus *status = ort_ctx->ort_api->Run( + exec_ctx->graph->session, nullptr, input_names.data(), + input_values.data(), input_values.size(), exec_ctx->output_names.data(), + exec_ctx->output_names.size(), output_values.data()); + + for (size_t i = 0; i < output_values.size(); i++) { + exec_ctx->outputs[i] = output_values[i]; + } + + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + NN_ERR_PRINTF("Failed to run inference"); + return err; + } + + NN_INFO_PRINTF("Inference computed for context %d", ctx); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +get_output(void *onnx_ctx, graph_execution_context ctx, uint32_t index, + tensor_data *out_buffer, uint32_t *out_buffer_size) +{ + OnnxRuntimeContext *ort_ctx = (OnnxRuntimeContext *)onnx_ctx; + if (!onnx_ctx) { + return runtime_error; + } + + std::lock_guard lock(ort_ctx->mutex); + + if (ctx >= MAX_CONTEXTS || !ort_ctx->exec_ctxs[ctx].is_initialized) { + NN_ERR_PRINTF("Invalid execution context handle: %d", ctx); + return invalid_argument; + } + + if (index >= ort_ctx->exec_ctxs[ctx].output_names.size()) { + NN_ERR_PRINTF("Invalid output index: %d (max: %zu)", index, + ort_ctx->exec_ctxs[ctx].output_names.size() - 1); + return invalid_argument; + } + + OnnxRuntimeExecCtx *exec_ctx = &ort_ctx->exec_ctxs[ctx]; + + OrtValue *output_value = exec_ctx->outputs[index]; + if (!output_value) { + NN_ERR_PRINTF("Output tensor not available for index %d", index); + return runtime_error; + } + + OrtTensorTypeAndShapeInfo *tensor_info; + OrtStatus *status = + ort_ctx->ort_api->GetTensorTypeAndShape(output_value, &tensor_info); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + NN_ERR_PRINTF("Failed to get tensor type and shape"); + return err; + } + + ONNXTensorElementDataType element_type; + status = ort_ctx->ort_api->GetTensorElementType(tensor_info, &element_type); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + ort_ctx->ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info); + NN_ERR_PRINTF("Failed to get tensor element type"); + return err; + } + + size_t num_dims; + status = ort_ctx->ort_api->GetDimensionsCount(tensor_info, &num_dims); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + ort_ctx->ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info); + NN_ERR_PRINTF("Failed to get tensor dimensions count"); + return err; + } + + int64_t *dims = (int64_t *)malloc(num_dims * sizeof(int64_t)); + if (!dims) { + ort_ctx->ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info); + NN_ERR_PRINTF("Failed to allocate memory for tensor dimensions"); + return runtime_error; + } + + status = ort_ctx->ort_api->GetDimensions(tensor_info, dims, num_dims); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + free(dims); + ort_ctx->ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info); + NN_ERR_PRINTF("Failed to get tensor dimensions"); + return err; + } + + size_t tensor_size; + status = + ort_ctx->ort_api->GetTensorShapeElementCount(tensor_info, &tensor_size); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + free(dims); + ort_ctx->ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info); + NN_ERR_PRINTF("Failed to get tensor element count"); + return err; + } + + NN_INFO_PRINTF("Output tensor dimensions: "); + for (size_t i = 0; i < num_dims; i++) { + NN_INFO_PRINTF(" dim[%zu] = %lld", i, dims[i]); + } + NN_INFO_PRINTF("Total elements: %zu", tensor_size); + + ort_ctx->ort_api->ReleaseTensorTypeAndShapeInfo(tensor_info); + free(dims); + + if (tensor_size == 0) { + NN_ERR_PRINTF("Tensor is empty (zero elements)"); + return runtime_error; + } + + void *tensor_data = nullptr; + status = ort_ctx->ort_api->GetTensorMutableData(output_value, &tensor_data); + if (status != nullptr) { + wasi_nn_error err = convert_ort_error_to_wasi_nn_error(ort_ctx, status); + NN_ERR_PRINTF("Failed to get tensor data"); + return err; + } + + if (tensor_data == nullptr) { + NN_ERR_PRINTF("Tensor data pointer is null"); + return runtime_error; + } + + size_t element_size; + switch (element_type) { + case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT: + element_size = sizeof(float); + break; + case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: + element_size = sizeof(uint16_t); + break; + case ONNX_TENSOR_ELEMENT_DATA_TYPE_DOUBLE: + element_size = sizeof(double); + break; + case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32: + element_size = sizeof(int32_t); + break; + case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64: + element_size = sizeof(int64_t); + break; + case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: + element_size = sizeof(uint8_t); + break; + default: + NN_ERR_PRINTF("Unsupported tensor element type: %d", element_type); + return unsupported_operation; + } + + size_t output_size_bytes = tensor_size * element_size; + if (out_buffer->size < output_size_bytes) { + NN_ERR_PRINTF( + "Output buffer too small: %u bytes provided, %zu bytes needed", + out_buffer->size, output_size_bytes); + *out_buffer_size = output_size_bytes; + return too_large; + } + NN_INFO_PRINTF("Output tensor size: %zu elements, element size: %zu bytes, " + "total: %zu bytes", + tensor_size, element_size, output_size_bytes); + + if (tensor_data == nullptr) { + NN_ERR_PRINTF("Tensor data is null"); + return runtime_error; + } + + if (out_buffer->buf == nullptr) { + NN_ERR_PRINTF("Output buffer is null"); + return invalid_argument; + } + + memcpy(out_buffer->buf, tensor_data, output_size_bytes); + *out_buffer_size = output_size_bytes; + + NN_INFO_PRINTF( + "Output tensor retrieved for context %d, index %d, size %zu bytes", ctx, + index, output_size_bytes); + return success; +} + +} /* End of extern "C" */ diff --git a/core/iwasm/libraries/wasi-nn/src/wasi_nn_openvino.c b/core/iwasm/libraries/wasi-nn/src/wasi_nn_openvino.c new file mode 100644 index 0000000000..899e06ee39 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_openvino.c @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasi_nn_backend.h" +#include "utils/logger.h" +#include "bh_platform.h" + +#include "openvino/c/openvino.h" + +#if WASM_ENABLE_WASI_EPHEMERAL_NN == 0 +#error This backend doesn't support legacy "wasi_nn" abi. Please enable WASM_ENABLE_WASI_EPHEMERAL_NN. +#endif + +/* + * refer to + * https://docs.openvino.ai/2024/openvino-workflow/running-inference/integrate-openvino-with-your-application.html + * + * Steps about integrating OpenVINO are: + * + * 1. Create OpenVINO Runtime Core + * 2. Compile Model + * 3. Create Inference Request + * 4. Set Inputs + * 5. Start Inference + * 6. Process Inference Results + * + * from 4. to 6. is the Inference Loop + */ + +/* these limits are arbitrary. */ +#define MAX_GRAPHS 4 +#define MAX_EXECUTION_CONTEXTS 4 + +typedef struct { + ov_core_t *core; + /* keep input model files */ + struct OpenVINOGraph { + void *weight_data; + ov_tensor_t *weights_tensor; + ov_model_t *model; + ov_compiled_model_t *compiled_model; + } graphs[MAX_GRAPHS]; + struct OpenVINOExecutionContext { + struct OpenVINOGraph *graph; + ov_infer_request_t *infer_request; + } execution_contexts[MAX_EXECUTION_CONTEXTS]; + unsigned int n_graphs; + unsigned int n_execution_contexts; +} OpenVINOContext; + +/* + * BE AWARE OF "goto fail" + */ +#define CHECK_OV_STATUS(status, error_code) \ + do { \ + ov_status_e s = status; \ + if (s != OK) { \ + NN_ERR_PRINTF("return status \"%s\", line %d", \ + ov_get_error_info(s), __LINE__); \ + error_code = runtime_error; \ + goto fail; \ + } \ + } while (0) + +static void +dump_ov_shape_t(const ov_shape_t *shape, int32_t output_len, char *output) +{ + int ret = 0; + + ret = snprintf(output, output_len, "%" PRId64 ",[", shape->rank); + if (!ret) + return; + + output_len -= ret; + output += ret; + + for (unsigned i = 0; i < shape->rank && output_len; i++) { + ret = snprintf(output, output_len, " %" PRId64, shape->dims[i]); + if (!ret) + return; + + output_len -= ret; + output += ret; + } + + snprintf(output, output_len, "]"); + return; +} + +#ifndef NDEBUG +static void +print_model_input_output_info(ov_model_t *model) +{ + wasi_nn_error ov_error = success; + char *friendly_name = NULL; + size_t input_size = 0; + ov_output_const_port_t *input_port = NULL; + ov_shape_t input_shape = { 0 }; + ov_element_type_e input_type; + char shape_info[64] = { 0 }; + ov_output_const_port_t *output_port = NULL; + ov_shape_t output_shape = { 0 }; + ov_element_type_e output_type; + + CHECK_OV_STATUS(ov_model_get_friendly_name(model, &friendly_name), + ov_error); + NN_DBG_PRINTF("model name: %s", friendly_name); + + ov_model_inputs_size(model, &input_size); + for (unsigned i = 0; i < input_size; i++) { + CHECK_OV_STATUS(ov_model_const_input_by_index(model, i, &input_port), + ov_error); + CHECK_OV_STATUS(ov_const_port_get_shape(input_port, &input_shape), + ov_error); + CHECK_OV_STATUS(ov_port_get_element_type(input_port, &input_type), + ov_error); + + dump_ov_shape_t(&input_shape, 60, shape_info); + NN_DBG_PRINTF("model input[%u]. element_type: %d, shape: %s", i, + input_type, shape_info); + + ov_shape_free(&input_shape); + memset(&input_shape, 0, sizeof(input_shape)); + ov_output_const_port_free(input_port); + input_port = NULL; + } + + size_t output_size = 0; + ov_model_outputs_size(model, &output_size); + for (unsigned i = 0; i < output_size; i++) { + CHECK_OV_STATUS(ov_model_const_output_by_index(model, i, &output_port), + ov_error); + CHECK_OV_STATUS(ov_const_port_get_shape(output_port, &output_shape), + ov_error); + CHECK_OV_STATUS(ov_port_get_element_type(output_port, &output_type), + ov_error); + + dump_ov_shape_t(&output_shape, 60, shape_info); + NN_DBG_PRINTF("model output[%u]. element_type: %d, shape: %s", i, + output_type, shape_info); + + ov_shape_free(&output_shape); + memset(&output_shape, 0, sizeof(output_shape)); + ov_output_const_port_free(output_port); + output_port = NULL; + } + + (void)ov_error; +fail: + if (friendly_name) + ov_free(friendly_name); + ov_shape_free(&input_shape); + if (input_port) + ov_output_const_port_free(input_port); + ov_shape_free(&output_shape); + if (output_port) + ov_output_const_port_free(output_port); + return; +} +#endif + +static ov_element_type_e +wasi_nn_tensor_type_to_openvino_element_type(tensor_type wasi_nn_type) +{ + switch (wasi_nn_type) { + case fp16: + return F16; + case fp32: + return F32; +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + case fp64: + return F64; + case i64: + return I64; + case u8: + return U8; + case i32: + return I32; +#else + case up8: + return U8; + case ip32: + return I32; +#endif + default: + break; + } + + NN_ERR_PRINTF("%d is an undefined tensor type", wasi_nn_type); + return UNDEFINED; +} + +static void +free_graph(struct OpenVINOGraph *graph) +{ + if (graph->weight_data) + os_free(graph->weight_data); + + if (graph->weights_tensor) + ov_tensor_free(graph->weights_tensor); + + if (graph->model) + ov_model_free(graph->model); + + if (graph->compiled_model) + ov_compiled_model_free(graph->compiled_model); +} + +static void +free_execution_context(struct OpenVINOExecutionContext *c) +{ + if (c->infer_request) + ov_infer_request_free(c->infer_request); +} + +static wasi_nn_error +uint32_array_to_int64_array(uint32_t array_size, uint32_t *src, int64_t **dst) +{ + *dst = os_malloc(array_size * sizeof(int64_t)); + if (!(*dst)) + return runtime_error; + + for (unsigned i = 0; i < array_size; i++) { + (*dst)[i] = src[i]; + } + + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +load(void *ctx, graph_builder_array *builder, graph_encoding encoding, + execution_target target, graph *g) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + struct OpenVINOGraph *graph; + unsigned int graph_idx; + wasi_nn_error ret = unsupported_operation; + + if (encoding != openvino) { + NN_ERR_PRINTF("Unexpected encoding %d.", encoding); + return invalid_argument; + } + + /*FIXME: unblock non-cpu device after supporting */ + if (target != cpu) { + NN_ERR_PRINTF("Unexpected device %d.", target); + return invalid_argument; + } + + if (builder->size != 2) { + NN_ERR_PRINTF("Unexpected builder format."); + return invalid_argument; + } + + /* + * The first builder is the XML file. + * The second builder is the weight file. + */ + graph_builder xml = builder->buf[0]; + graph_builder weight = builder->buf[1]; + + graph_idx = ov_ctx->n_graphs; + if (graph_idx >= MAX_GRAPHS) { + return runtime_error; + } + graph = &ov_ctx->graphs[graph_idx]; + memset(graph, 0, sizeof(*graph)); + + /* transfer weight to an ov tensor */ + { + graph->weight_data = os_malloc(weight.size); + if (!graph->weight_data) + goto fail; + memcpy(graph->weight_data, weight.buf, weight.size); + + ov_element_type_e type = U8; + int64_t dims[1] = { weight.size }; + ov_shape_t shape = { 1, dims }; + CHECK_OV_STATUS(ov_tensor_create_from_host_ptr(type, shape, + graph->weight_data, + &graph->weights_tensor), + ret); + } + + /* load model from buffer */ + CHECK_OV_STATUS(ov_core_read_model_from_memory_buffer( + ov_ctx->core, (char *)xml.buf, xml.size, + graph->weights_tensor, &graph->model), + ret); +#ifndef NDEBUG + print_model_input_output_info(graph->model); +#endif + + CHECK_OV_STATUS(ov_core_compile_model(ov_ctx->core, graph->model, "CPU", 0, + &graph->compiled_model), + ret); + + *g = graph_idx; + ov_ctx->n_graphs++; + return success; +fail: + free_graph(graph); + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name(void *ctx, const char *filename, uint32_t filename_len, graph *g) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + struct OpenVINOGraph *graph; + unsigned int graph_idx; + wasi_nn_error ret = unsupported_operation; + + graph_idx = ov_ctx->n_graphs; + if (graph_idx >= MAX_GRAPHS) { + return runtime_error; + } + graph = &ov_ctx->graphs[graph_idx]; + + memset(graph, 0, sizeof(*graph)); + CHECK_OV_STATUS( + ov_core_read_model(ov_ctx->core, filename, NULL, &graph->model), ret); + + CHECK_OV_STATUS(ov_core_compile_model(ov_ctx->core, graph->model, "CPU", 0, + &graph->compiled_model), + ret); + + *g = graph_idx; + ov_ctx->n_graphs++; + return success; +fail: + free_graph(graph); + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +init_execution_context(void *ctx, graph g, graph_execution_context *exec_ctx) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + struct OpenVINOGraph *graph; + struct OpenVINOExecutionContext *exec; + unsigned int exec_idx; + wasi_nn_error ret; + + if (g >= ov_ctx->n_graphs) + return runtime_error; + graph = &ov_ctx->graphs[g]; + + exec_idx = ov_ctx->n_execution_contexts; + if (exec_idx >= MAX_EXECUTION_CONTEXTS) + return runtime_error; + exec = &ov_ctx->execution_contexts[exec_idx]; + + memset(exec, 0, sizeof(*exec)); + exec->graph = graph; + + CHECK_OV_STATUS(ov_compiled_model_create_infer_request( + graph->compiled_model, &exec->infer_request), + ret); + + *exec_ctx = exec_idx; + ov_ctx->n_execution_contexts++; + return success; +fail: + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +set_input(void *ctx, graph_execution_context exec_ctx, uint32_t index, + tensor *wasi_nn_tensor) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + struct OpenVINOExecutionContext *exec; + wasi_nn_error ret = unsupported_operation; + ov_shape_t input_shape = { 0 }; + ov_tensor_t *input_tensor = NULL; + int64_t *ov_dims = NULL; + + if (exec_ctx >= ov_ctx->n_execution_contexts) + return runtime_error; + exec = &ov_ctx->execution_contexts[exec_ctx]; + + /* wasi_nn_tensor -> ov_tensor */ + { + ret = uint32_array_to_int64_array(wasi_nn_tensor->dimensions->size, + wasi_nn_tensor->dimensions->buf, + &ov_dims); + if (ret != success) + goto fail; + + CHECK_OV_STATUS(ov_shape_create(wasi_nn_tensor->dimensions->size, + ov_dims, &input_shape), + ret); + + ov_element_type_e input_type = + wasi_nn_tensor_type_to_openvino_element_type(wasi_nn_tensor->type); + if (input_type == UNDEFINED) + goto fail; + + char shape_info[64] = { 0 }; + dump_ov_shape_t(&input_shape, 60, shape_info); + NN_DBG_PRINTF("input tensor. element_type: %d, shape: %s", input_type, + shape_info); + + CHECK_OV_STATUS(ov_tensor_create_from_host_ptr(input_type, input_shape, + wasi_nn_tensor->data.buf, + &input_tensor), + ret); + } + + /* install ov_tensor -> infer_request */ + CHECK_OV_STATUS(ov_infer_request_set_input_tensor_by_index( + exec->infer_request, index, input_tensor), + ret); + ret = success; +fail: + if (ov_dims) + os_free(ov_dims); + if (input_tensor) + ov_tensor_free(input_tensor); + ov_shape_free(&input_shape); + + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +compute(void *ctx, graph_execution_context exec_ctx) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + struct OpenVINOExecutionContext *exec; + wasi_nn_error ret = unsupported_operation; + + if (exec_ctx >= ov_ctx->n_execution_contexts) + return runtime_error; + exec = &ov_ctx->execution_contexts[exec_ctx]; + + CHECK_OV_STATUS(ov_infer_request_infer(exec->infer_request), ret); + ret = success; +fail: + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +get_output(void *ctx, graph_execution_context exec_ctx, uint32_t index, + tensor_data *output_tensor, uint32_t *output_tensor_size) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + struct OpenVINOExecutionContext *exec; + wasi_nn_error ret = unsupported_operation; + ov_tensor_t *ov_tensor = NULL; + void *data = NULL; + size_t byte_size = 0; + + if (exec_ctx >= ov_ctx->n_execution_contexts) + return runtime_error; + exec = &ov_ctx->execution_contexts[exec_ctx]; + + CHECK_OV_STATUS(ov_infer_request_get_output_tensor_by_index( + exec->infer_request, index, &ov_tensor), + ret); + + CHECK_OV_STATUS(ov_tensor_get_byte_size(ov_tensor, &byte_size), ret); + + if (byte_size > output_tensor->size) { + ret = too_large; + goto fail; + } + + CHECK_OV_STATUS(ov_tensor_data(ov_tensor, &data), ret); + + memcpy(output_tensor->buf, data, byte_size); + + *output_tensor_size = (uint32_t)byte_size; + + ret = success; + +fail: + if (ov_tensor) + ov_tensor_free(ov_tensor); + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +init_backend(void **ctx) +{ + ov_version_t version; + OpenVINOContext *ov_ctx = NULL; + wasi_nn_error ret = unsupported_operation; + + if (!ctx) { + ret = invalid_argument; + goto fail; + } + + /* Get OpenVINO runtime version */ + CHECK_OV_STATUS(ov_get_openvino_version(&version), ret); + NN_INFO_PRINTF("OpenVINO INFO:"); + NN_INFO_PRINTF(" Description : %s", version.description); + NN_INFO_PRINTF(" Build Number: %s", version.buildNumber); + ov_version_free(&version); + + ov_ctx = (OpenVINOContext *)os_malloc(sizeof(OpenVINOContext)); + if (!ov_ctx) { + NN_ERR_PRINTF("Allocate for OpenVINOContext failed"); + ret = runtime_error; + goto fail; + } + + memset(ov_ctx, 0, sizeof(OpenVINOContext)); + + /* Initialize OpenVINO Runtime Core */ + CHECK_OV_STATUS(ov_core_create(&ov_ctx->core), ret); + + *ctx = (void *)ov_ctx; + return success; +fail: + os_free(ov_ctx); + return ret; +} + +__attribute__((visibility("default"))) wasi_nn_error +deinit_backend(void *ctx) +{ + OpenVINOContext *ov_ctx = (OpenVINOContext *)ctx; + unsigned int i; + + if (!ov_ctx) + return invalid_argument; + + for (i = 0; i < ov_ctx->n_execution_contexts; i++) + free_execution_context(&ov_ctx->execution_contexts[i]); + + for (i = 0; i < ov_ctx->n_graphs; i++) + free_graph(&ov_ctx->graphs[i]); + + if (ov_ctx->core) + ov_core_free(ov_ctx->core); + + os_free(ov_ctx); + return success; +} 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 0000000000..1bff2c514d --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_private.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_PRIVATE_H +#define WASI_NN_PRIVATE_H + +#include "wasi_nn_types.h" +#include "wasm_export.h" + +#include "bh_platform.h" + +typedef struct { + korp_mutex lock; + bool busy; + bool is_backend_ctx_initialized; + graph_encoding backend; + void *backend_ctx; +} WASINNContext; + +typedef wasi_nn_error (*LOAD)(void *, graph_builder_array *, graph_encoding, + execution_target, graph *); +typedef wasi_nn_error (*LOAD_BY_NAME)(void *, const char *, uint32_t, graph *); +typedef wasi_nn_error (*LOAD_BY_NAME_WITH_CONFIG)(void *, const char *, + uint32_t, void *, uint32_t, + graph *); +typedef wasi_nn_error (*INIT_EXECUTION_CONTEXT)(void *, graph, + graph_execution_context *); +typedef wasi_nn_error (*SET_INPUT)(void *, graph_execution_context, uint32_t, + tensor *); +typedef wasi_nn_error (*COMPUTE)(void *, graph_execution_context); +typedef wasi_nn_error (*GET_OUTPUT)(void *, graph_execution_context, uint32_t, + tensor_data *, uint32_t *); +/* wasi-nn general APIs */ +typedef wasi_nn_error (*BACKEND_INITIALIZE)(void **); +typedef wasi_nn_error (*BACKEND_DEINITIALIZE)(void *); + +typedef struct { + LOAD load; + LOAD_BY_NAME load_by_name; + LOAD_BY_NAME_WITH_CONFIG load_by_name_with_config; + INIT_EXECUTION_CONTEXT init_execution_context; + SET_INPUT set_input; + COMPUTE compute; + GET_OUTPUT get_output; + BACKEND_INITIALIZE init; + BACKEND_DEINITIALIZE deinit; +} api_function; + +#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 0000000000..9ac54e6644 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/src/wasi_nn_tensorflowlite.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "utils/logger.h" + +#include "bh_platform.h" +#include "wasi_nn_backend.h" +#include "wasm_export.h" + +#include +#include +#include +#include +#include +#include + +#if WASM_ENABLE_WASI_NN_GPU != 0 +#include +#endif + +#if WASM_ENABLE_WASI_NN_EXTERNAL_DELEGATE != 0 +#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; + TfLiteDelegate *delegate; +} TFLiteContext; + +/* Utils */ + +static wasi_nn_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("Exceeded max graphs per WASM instance"); + return runtime_error; + } + *g = tfl_ctx->current_models++; + os_mutex_unlock(&tfl_ctx->g_lock); + return success; +} +static wasi_nn_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("Exceeded 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 wasi_nn_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 == NULL) { + NN_ERR_PRINTF("Context (model) non-initialized."); + return runtime_error; + } + return success; +} + +static wasi_nn_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 */ +__attribute__((visibility("default"))) wasi_nn_error +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 && target != tpu) { + NN_ERR_PRINTF("Only CPU, GPU and TPU target is supported."); + return invalid_argument; + } + + wasi_nn_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 too_large; + } + + 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 too_large; + } + + // Save target + tfl_ctx->models[*g].target = target; + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +load_by_name(void *tflite_ctx, const char *filename, uint32_t filename_len, + graph *g) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + wasi_nn_error res = initialize_g(tfl_ctx, g); + if (success != res) + return res; + + // Load model + tfl_ctx->models[*g].model = + std::move(tflite::FlatBufferModel::BuildFromFile(filename, NULL)); + + if (tfl_ctx->models[*g].model == NULL) { + NN_ERR_PRINTF("Loading model error."); + return too_large; + } + + // Use CPU as default + tfl_ctx->models[*g].target = cpu; + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +init_execution_context(void *tflite_ctx, graph g, graph_execution_context *ctx) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + wasi_nn_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 too_large; + } + + bool use_default = false; + switch (tfl_ctx->models[g].target) { + case gpu: + { +#if WASM_ENABLE_WASI_NN_GPU != 0 + NN_WARN_PRINTF("GPU enabled."); + // https://www.tensorflow.org/lite/performance/gpu + TfLiteGpuDelegateOptionsV2 options = + TfLiteGpuDelegateOptionsV2Default(); + options.inference_preference = + TFLITE_GPU_INFERENCE_PREFERENCE_SUSTAINED_SPEED; + options.inference_priority1 = + TFLITE_GPU_INFERENCE_PRIORITY_MIN_LATENCY; + tfl_ctx->delegate = TfLiteGpuDelegateV2Create(&options); + if (tfl_ctx->delegate == NULL) { + NN_ERR_PRINTF("Error when generating GPU delegate."); + use_default = true; + return too_large; + } + if (tfl_ctx->interpreters[*ctx] + .interpreter->ModifyGraphWithDelegate(tfl_ctx->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; + } + case tpu: + { +#if WASM_ENABLE_WASI_NN_EXTERNAL_DELEGATE != 0 + NN_WARN_PRINTF("external delegation enabled."); + TfLiteExternalDelegateOptions options = + TfLiteExternalDelegateOptionsDefault( + WASM_WASI_NN_EXTERNAL_DELEGATE_PATH); + tfl_ctx->delegate = TfLiteExternalDelegateCreate(&options); + if (tfl_ctx->delegate == NULL) { + NN_ERR_PRINTF("Error when generating External delegate."); + use_default = true; + return too_large; + } + if (tfl_ctx->interpreters[*ctx] + .interpreter->ModifyGraphWithDelegate(tfl_ctx->delegate) + != kTfLiteOk) { + NN_ERR_PRINTF("Error when enabling External delegate."); + use_default = true; + } +#else + NN_WARN_PRINTF("External delegate 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; +} + +__attribute__((visibility("default"))) wasi_nn_error +set_input(void *tflite_ctx, graph_execution_context ctx, uint32_t index, + tensor *input_tensor) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + TfLiteType tfl_type; + + switch (input_tensor->type) { + case fp32: + tfl_type = TfLiteType::kTfLiteFloat32; + break; +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + case u8: + tfl_type = TfLiteType::kTfLiteUInt8; + break; +#endif + default: + NN_ERR_PRINTF("unsupported input tensor type %u", + input_tensor->type); + return runtime_error; + } + + wasi_nn_error res; + if (success != (res = is_valid_graph_execution_context(tfl_ctx, ctx))) + return res; + + auto interpreter = tfl_ctx->interpreters[ctx].interpreter.get(); + + 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 too_large; + } + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + if (TfLiteTensorType(tensor) != tfl_type) { + NN_ERR_PRINTF("Type mismatch"); + return runtime_error; + } + + if (TfLiteTensorCopyFromBuffer(tensor, input_tensor->data.buf, + input_tensor->data.size) + != kTfLiteOk) { + return runtime_error; + } +#else + 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; + } + + if (tensor->quantization.type == kTfLiteNoQuantization) { + NN_DBG_PRINTF("No quantization information. Using float as default"); + float *it = + tfl_ctx->interpreters[ctx].interpreter->typed_input_tensor( + index); + + int size = model_tensor_size * sizeof(float); + bh_memcpy_s(it, size, input_tensor->data.buf, size); + } + else { // TODO: Assuming uint8 quantized networks. + TfLiteAffineQuantization *quant_info = + (TfLiteAffineQuantization *)tensor->quantization.params; + if (quant_info->scale->size != 1 || quant_info->zero_point->size != 1) { + NN_ERR_PRINTF("Quantization per channel is not supported"); + return runtime_error; + } + uint8_t *it = + tfl_ctx->interpreters[ctx].interpreter->typed_input_tensor( + index); + + float scale = quant_info->scale->data[0]; + float zero_point = (float)quant_info->zero_point->data[0]; + NN_DBG_PRINTF("input tensor: (scale, offset) = (%f, %f)", scale, + zero_point); + + float *input_tensor_f = (float *)input_tensor->data.buf; + for (uint32_t i = 0; i < model_tensor_size; ++i) { + it[i] = (uint8_t)(input_tensor_f[i] / scale + zero_point); + } + } +#endif + + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +compute(void *tflite_ctx, graph_execution_context ctx) +{ + TFLiteContext *tfl_ctx = (TFLiteContext *)tflite_ctx; + + wasi_nn_error res; + if (success != (res = is_valid_graph_execution_context(tfl_ctx, ctx))) + return res; + + tfl_ctx->interpreters[ctx].interpreter->Invoke(); + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +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; + + wasi_nn_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) { + NN_ERR_PRINTF("Index %d is invalid.", index); + return runtime_error; + } + + auto tensor = tfl_ctx->interpreters[ctx].interpreter->output_tensor(index); + if (tensor == NULL) { + NN_ERR_PRINTF("Missing memory"); + return too_large; + } + +#if WASM_ENABLE_WASI_EPHEMERAL_NN != 0 + size_t sz = TfLiteTensorByteSize(tensor); + if (output_tensor->size < sz) { + NN_ERR_PRINTF("Insufficient memory to copy tensor %d", index); + return too_large; + } + if (TfLiteTensorCopyToBuffer(tensor, output_tensor->buf, sz) != kTfLiteOk) { + return runtime_error; + } + *output_tensor_size = sz; +#else + if (tensor->quantization.type == kTfLiteNoQuantization) { + NN_DBG_PRINTF("No quantization information"); + /* + * for now, maintain the bug-to-bug compatibility with the old abi, + * where the size here is the number of fp32, not bytes. + */ + if (output_tensor->size < tensor->bytes / sizeof(float)) { + NN_ERR_PRINTF("Insufficient memory to copy tensor %d", index); + return too_large; + } + bh_memcpy_s(output_tensor->buf, output_tensor->size, tensor->data.data, + tensor->bytes); + /* + * for now, maintain the bug-to-bug compatibility with the old abi, + * where the size here is the number of fp32, not bytes. + */ + *output_tensor_size = tensor->bytes / sizeof(float); + } + else { // TODO: Assuming uint8 quantized networks. + TfLiteAffineQuantization *quant_info = + (TfLiteAffineQuantization *)tensor->quantization.params; + if (quant_info->scale->size != 1 || quant_info->zero_point->size != 1) { + NN_ERR_PRINTF("Quantization per channel is not supported"); + return runtime_error; + } + + 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]; + + /* + * for now, maintain the bug-to-bug compatibility with the old abi, + * where the size here is the number of fp32, not bytes. + */ + if (output_tensor->size < model_tensor_size) { + NN_ERR_PRINTF("Insufficient memory to copy tensor %d", index); + return too_large; + } + + uint8_t *ot = tfl_ctx->interpreters[ctx] + .interpreter->typed_output_tensor(index); + + float scale = quant_info->scale->data[0]; + float zero_point = (float)quant_info->zero_point->data[0]; + NN_DBG_PRINTF("output tensor: (scale, offset) = (%f, %f)", scale, + zero_point); + + float *output_tensor_f = (float *)output_tensor->buf; + for (uint32_t i = 0; i < model_tensor_size; ++i) { + output_tensor_f[i] = (ot[i] - zero_point) * scale; + } + + /* + * for now, maintain the bug-to-bug compatibility with the old abi, + * where the size here is the number of fp32, not bytes. + */ + *output_tensor_size = model_tensor_size; + } +#endif + + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +init_backend(void **tflite_ctx) +{ + TFLiteContext *tfl_ctx = new TFLiteContext(); + if (tfl_ctx == NULL) { + NN_ERR_PRINTF("Error when allocating memory for tensorflowlite."); + return runtime_error; + } + + 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"); + } + + tfl_ctx->delegate = NULL; + + *tflite_ctx = (void *)tfl_ctx; + return success; +} + +__attribute__((visibility("default"))) wasi_nn_error +deinit_backend(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->delegate) { + switch (tfl_ctx->models[i].target) { + case gpu: + { +#if WASM_ENABLE_WASI_NN_GPU != 0 + TfLiteGpuDelegateV2Delete(tfl_ctx->delegate); +#else + NN_ERR_PRINTF("GPU delegate delete but not enabled."); +#endif + break; + } + case tpu: + { +#if WASM_ENABLE_WASI_NN_EXTERNAL_DELEGATE != 0 + TfLiteExternalDelegateDelete(tfl_ctx->delegate); +#else + NN_ERR_PRINTF("External delegate delete but not enabled."); +#endif + break; + } + default: + break; + } + } + 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."); + return success; +} 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 0000000000..b20d0111af --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.compile @@ -0,0 +1,26 @@ +# 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 + +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y \ + cmake build-essential git wget python3.10 python3-pip --no-install-recommends \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +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 --no-cache-dir -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 0000000000..67bfbfe0e5 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.cpu @@ -0,0 +1,45 @@ +# 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 + +# hadolint ignore=DL3008,DL3009 +RUN apt-get update \ + && apt-get install -y --no-install-recommends\ + ca-certificates cmake build-essential git wget + +WORKDIR /usr/local/share/ca-certificates/cacert.org +RUN wget -qP /usr/local/share/ca-certificates/cacert.org http://www.cacert.org/certs/root.crt http://www.cacert.org/certs/class3.crt \ + && update-ca-certificates + +# need a newer cmake +RUN apt-get purge -y cmake + +ARG CMAKE_VER=3.27.0 +RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-Linux-x86_64.sh \ + -q -O /tmp/cmake-install.sh \ + && chmod u+x /tmp/cmake-install.sh \ + && mkdir /opt/cmake-${CMAKE_VER} \ + && /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-${CMAKE_VER} \ + && rm /tmp/cmake-install.sh \ + && ln -s /opt/cmake-${CMAKE_VER}/bin/* /usr/local/bin + +WORKDIR /home/wamr +COPY . . +RUN git config --global http.sslCAinfo /etc/ssl/certs/ca-certificates.crt + +WORKDIR /home/wamr/product-mini/platforms/linux +RUN rm -rf build \ + && cmake -S . -B build\ + -DWAMR_BUILD_WASI_NN=1 -DWAMR_BUILD_WASI_NN_TFLITE=1\ + && cmake --build build -j "$(grep -c ^processor /proc/cpuinfo)" + +FROM ubuntu:22.04 + +COPY --from=base /home/wamr/product-mini/platforms/linux/build/iwasm /usr/bin +COPY --from=base /home/wamr/product-mini/platforms/linux/build/lib*.so /usr/lib +ENV LD_LIBRARY_PATH=/usr/lib + +ENTRYPOINT [ "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 0000000000..8fe82510a6 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.nvidia-gpu @@ -0,0 +1,59 @@ +# 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 + +# hadolint ignore=DL3008,DL3009 +RUN apt-get update \ + && apt-get install -y --no-install-recommends\ + ca-certificates cmake build-essential git wget + +WORKDIR /usr/local/share/ca-certificates/cacert.org +RUN wget -qP /usr/local/share/ca-certificates/cacert.org http://www.cacert.org/certs/root.crt http://www.cacert.org/certs/class3.crt \ + && update-ca-certificates + +# need a newer cmake +RUN apt-get purge -y cmake + +ARG CMAKE_VER=3.27.0 +RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-Linux-x86_64.sh \ + -q -O /tmp/cmake-install.sh \ + && chmod u+x /tmp/cmake-install.sh \ + && mkdir /opt/cmake-${CMAKE_VER} \ + && /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-${CMAKE_VER} \ + && rm /tmp/cmake-install.sh \ + && ln -s /opt/cmake-${CMAKE_VER}/bin/* /usr/local/bin + +WORKDIR /home/wamr +COPY . . +RUN git config --global http.sslCAinfo /etc/ssl/certs/ca-certificates.crt + +WORKDIR /home/wamr/product-mini/platforms/linux +RUN rm -rf build \ + && cmake -S . -B build \ + -DWAMR_BUILD_WASI_NN=1 -DWAMR_BUILD_WASI_NN_TFLITE=1\ + -DWAMR_BUILD_WASI_NN_ENABLE_GPU=1 \ + && cmake --build build -j "$(grep -c ^processor /proc/cpuinfo)" + +FROM nvidia/cuda:11.3.0-runtime-ubuntu20.04 + +# hadolint ignore=DL3008 +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/product-mini/platforms/linux/build/iwasm /usr/bin +COPY --from=base /home/wamr/product-mini/platforms/linux/build/lib*.so /usr/lib +ENV LD_LIBRARY_PATH=/usr/lib + +ENTRYPOINT [ "iwasm" ] diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile.tpu b/core/iwasm/libraries/wasi-nn/test/Dockerfile.tpu new file mode 100644 index 0000000000..6a7dc15341 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.tpu @@ -0,0 +1,48 @@ +# 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 + +# hadolint ignore=DL3008,DL3009 +RUN apt-get update \ + && apt-get install -y --no-install-recommends\ + ca-certificates cmake build-essential git wget + +WORKDIR /usr/local/share/ca-certificates/cacert.org +RUN wget -qP /usr/local/share/ca-certificates/cacert.org http://www.cacert.org/certs/root.crt http://www.cacert.org/certs/class3.crt \ + && update-ca-certificates + +# need a newer cmake +RUN apt-get purge -y cmake + +ARG CMAKE_VER=3.27.0 +RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-Linux-x86_64.sh \ + -q -O /tmp/cmake-install.sh \ + && chmod u+x /tmp/cmake-install.sh \ + && mkdir /opt/cmake-${CMAKE_VER} \ + && /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-${CMAKE_VER} \ + && rm /tmp/cmake-install.sh \ + && ln -s /opt/cmake-${CMAKE_VER}/bin/* /usr/local/bin + +WORKDIR /home/wamr +COPY . . +RUN git config --global http.sslCAinfo /etc/ssl/certs/ca-certificates.crt + +WORKDIR /home/wamr/product-mini/platforms/linux +RUN rm -rf build \ + && cmake -S . -B build\ + -DWAMR_BUILD_WASI_NN=1\ + -DWAMR_BUILD_WASI_NN_TFLITE=1\ + -DWAMR_BUILD_WASI_NN_ENABLE_EXTERNAL_DELEGATE=1 \ + -DWAMR_BUILD_WASI_NN_EXTERNAL_DELEGATE_PATH="libedgetpu.so.1.0" \ + -DWAMR_BUILD_WASI_NN_ENABLE_GPU=1 \ + && cmake --build build -j "$(grep -c ^processor /proc/cpuinfo)" + +RUN cp /home/wamr/core/iwasm/libraries/wasi-nn/test/build/iwasm /run/iwasm \ + && cp /home/wamr/product-mini/platforms/linux/build/lib*.so /usr/lib +ENV LD_LIBRARY_PATH=/usr/lib + +WORKDIR /assets +ENTRYPOINT [ "iwasm" ] diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile.vx-delegate b/core/iwasm/libraries/wasi-nn/test/Dockerfile.vx-delegate new file mode 100644 index 0000000000..fdeff30224 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.vx-delegate @@ -0,0 +1,125 @@ +# 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 + + +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y \ + cmake build-essential git curl libssl-dev python3 --no-install-recommends \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +# hadolint ignore=DL3008 +RUN apt-get update && apt-get install -y wget ca-certificates --no-install-recommends \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir /usr/local/share/ca-certificates/cacert.org \ + && wget -qP /usr/local/share/ca-certificates/cacert.org http://www.cacert.org/certs/root.crt http://www.cacert.org/certs/class3.crt \ + && update-ca-certificates \ + && git config --global http.sslCAinfo /etc/ssl/certs/ca-certificates.crt + +# need a newer cmake +RUN apt-get purge -y cmake + +ARG CMAKE_VER=3.27.0 +RUN wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/cmake-${CMAKE_VER}-Linux-x86_64.sh \ + -q -O /tmp/cmake-install.sh \ + && chmod u+x /tmp/cmake-install.sh \ + && mkdir /opt/cmake-${CMAKE_VER} \ + && /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-${CMAKE_VER} \ + && rm /tmp/cmake-install.sh \ + && ln -s /opt/cmake-${CMAKE_VER}/bin/* /usr/local/bin + +# Build TensorFlow Lite VX delegate default built for x86-64 simulator +WORKDIR /tmp +RUN git clone https://github.com/VeriSilicon/TIM-VX.git tim-vx \ + && git clone https://github.com/VeriSilicon/tflite-vx-delegate.git \ + && git clone https://github.com/tensorflow/tensorflow.git --branch v2.12.0 + +WORKDIR /tmp/tensorflow +RUN git cherry-pick -n 5115fa96d7c5b41451674892317be43e30b7c389 + + +# Build TIM-VX +WORKDIR /tmp/tim-vx/host_build +RUN cmake -DCMAKE_INSTALL_PREFIX=/usr/local ../ \ + && make -j "$(grep -c ^processor /proc/cpuinfo)" \ + && make install + +WORKDIR /tmp/tim-vx +#RUN mkdir -p prebuilt-sdk/x86_64_linux/lib/include +#RUN cp prebuilt-sdk/x86_64_linux/include/CL prebuilt-sdk/x86_64_linux/lib/include -fr + + +# Build TensorFlow Lite +WORKDIR /tmp/tensorflow/build +RUN cmake \ + -DBUILD_SHARED_LIBS=ON=on \ + -DTFLITE_ENABLE_RUY=on \ + -DTFLITE_ENABLE_NNAPI=off \ + -DTFLITE_ENABLE_XNNPACK=on \ + -DTFLITE_ENABLE_EXTERNAL_DELEGATE=on \ + ../tensorflow/lite/ +RUN make -j "$(grep -c ^processor /proc/cpuinfo)" \ + && make install \ + && cp --no-preserve=ownership -d lib*.so* /usr/local/lib \ + && cp -r --no-preserve=ownership -d flatbuffers/include/flatbuffers /usr/local/include +# install header files +RUN install -d /usr/local/include/tensorflow/lite +WORKDIR /tmp/tensorflow/tensorflow/lite +# hadolint ignore=SC2046 +RUN cp --parents \ + $(find . -name "*.h*") \ + /usr/local/include/tensorflow/lite +# install version.h from core +RUN install -d /usr/local/include/tensorflow/core/public && \ + cp /tmp/tensorflow/tensorflow/core/public/version.h /usr/local/include/tensorflow/core/public + + +# Build Vx Delegate default built for x86-64 simulator +WORKDIR /tmp/tflite-vx-delegate/build +RUN cmake \ + -DBUILD_SHARED_LIBS=ON \ + -DFETCHCONTENT_SOURCE_DIR_TENSORFLOW=/tmp/tensorflow \ + -DTFLITE_LIB_LOC=/usr/local/lib/libtensorflow-lite.so \ + -DTIM_VX_INSTALL=/usr/local \ + -DCMAKE_INSTALL_PREFIX=/usr/ \ + ../ +RUN make vx_delegate -j "$(grep -c ^processor /proc/cpuinfo)" \ + && make install \ + && cp --no-preserve=ownership -d lib*.so* /usr/lib +# install header files +RUN install -d /usr/local/include/tensorflow-lite-vx-delegate +WORKDIR /tmp/tflite-vx-delegate/ +# hadolint ignore=SC2046 +RUN cp --parents \ + $(find . -name "*.h*") \ + /usr/local/include/tensorflow-lite-vx-delegate + +ENV VIVANTE_SDK_DIR=/tmp/tim-vx/prebuilt-sdk/x86_64_linux/ +ENV VSIMULATOR_CONFIG=czl + +# Build WASI-NN +WORKDIR /home/wamr + +COPY . . + +WORKDIR /home/wamr/product-mini/platforms/linux + +RUN rm -rf build \ + && cmake -S . -B build\ + -DCMAKE_LIBRARY_PATH="/usr/local/lib/" \ + -DCMAKE_INCLUDE_PATH="/usr/local/include/" \ + -DWAMR_BUILD_WASI_NN=1 \ + -DWAMR_BUILD_WASI_NN_TFLITE=1\ + -DWAMR_BUILD_WASI_NN_ENABLE_EXT=1 \ + -DWASI_NN_EXT_DELEGATE_PATH="/usr/lib/libvx_delegate.so" \ + && cmake --build build -j "$(grep -c ^processor /proc/cpuinfo)" + +RUN cp /home/wamr/product-mini/platforms/linux/build/iwasm /run/iwasm \ + && cp /home/wamr/product-mini/platforms/linux/build/lib*.so /usr/lib + +ENTRYPOINT [ "/run/iwasm" ] diff --git a/core/iwasm/libraries/wasi-nn/test/Dockerfile.wasi-nn-smoke b/core/iwasm/libraries/wasi-nn/test/Dockerfile.wasi-nn-smoke new file mode 100644 index 0000000000..133b191858 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/Dockerfile.wasi-nn-smoke @@ -0,0 +1,123 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# hadolint global ignore=DL3003,DL3008,DL3009,DL3059 + +FROM mcr.microsoft.com/devcontainers/rust:1-1-bullseye@sha256:ddc1ee022d327f024c07484c9333db3fbbfd504bc096cdb66635653a2bebb33e + +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Asian/Shanghai + +# hadolint ignore=DL3009 +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends cmake + +RUN rustup target add wasm32-wasip1 + +# +# Openvino +# Refer to +# - https://docs.openvino.ai/2022.3/openvino_docs_install_guides_installing_openvino_from_archive_linux.html +# - https://docs.openvino.ai/2023.3/openvino_docs_install_guides_installing_openvino_from_archive_linux.html +# - https://docs.openvino.ai/2024/get-started/install-openvino/install-openvino-archive-linux.html +# +RUN wget -q https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +RUN apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB \ + && echo "deb https://apt.repos.intel.com/openvino/2023 ubuntu20 main" | tee /etc/apt/sources.list.d/intel-openvino-2023.list +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install --no-install-recommends -y openvino-2023.2.0 + +# Activate after upgrading to wasi-nn 0.7.0 +# # +# # wasi-nn +# # compilation requirements +# WORKDIR /workspaces/wasi-nn +# RUN git clone https://github.com/bytecodealliance/wasi-nn.git . \ +# # update new wasmtime's cli (#100). Apr 27, 2024 +# && git checkout 556890b121dd1171665d835aba4d04a7e29e37dc +# +# WORKDIR /workspaces/wasi-nn/rust/examples/classification-example/ +# RUN cargo build --target=wasm32-wasip1 +# +# ARG FIXTURE=https://download.01.org/openvinotoolkit/fixtures/mobilenet +# RUN cp target/wasm32-wasip1/debug/wasi-nn-example.wasm . \ +# && wget -q --no-clobber $FIXTURE/mobilenet.xml \ +# && wget -q --no-clobber $FIXTURE/mobilenet.bin +# # There are model files(mobilenet*) and wasm files(wasi-nn-example.wasm) in the directory, + +# +# wasmedge +WORKDIR /tmp +RUN wget -q https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh \ + && chmod a+x ./install.sh +# RUN ./install.sh -p /opt/wasmedge --plugins wasi_nn-tensorflowlite wasi_nn-openvino +RUN ./install.sh -r yes -D -p /opt/wasmedge --plugins wasi_nn-openvino --dist ubuntu20.04 -v 0.14.0 \ + && /opt/wasmedge/bin/wasmedge --version +ENV PATH=/opt/wasmedge/bin:${PATH} +# ENV WASMEDGE_LIB_DIR=/opt/wasmedge/lib + +# +# wasmedge-wasinn-examples +# based on wasi-nn 0.6.0 +WORKDIR /workspaces/wasmedge-wasinn-examples +RUN git clone --depth 1 https://github.com/second-state/WasmEdge-WASINN-examples.git . + +# recompile with debug info +ARG FIXTURE=https://download.01.org/openvinotoolkit/fixtures/mobilenet +WORKDIR /workspaces/wasmedge-wasinn-examples/openvino-mobilenet-image/ +RUN pushd rust \ + && cargo build --target=wasm32-wasip1 \ + && popd \ + && wget -q --no-clobber $FIXTURE/mobilenet.xml \ + && wget -q --no-clobber $FIXTURE/mobilenet.bin \ + && ls -l mobilenet.xml mobilenet.bin + +WORKDIR /workspaces/wasmedge-wasinn-examples/openvino-mobilenet-raw/ +RUN pushd rust \ + && cargo build --target=wasm32-wasip1 \ + && popd \ + && wget -q --no-clobber $FIXTURE/mobilenet.xml \ + && wget -q --no-clobber $FIXTURE/mobilenet.bin \ + && wget -q --no-clobber $FIXTURE/tensor-1x224x224x3-f32.bgr \ + && ls -l mobilenet.xml mobilenet.bin tensor-1x224x224x3-f32.bgr + +WORKDIR /workspaces/wasmedge-wasinn-examples/openvino-road-segmentation-adas/ +RUN pushd openvino-road-seg-adas \ + && cargo build --target=wasm32-wasip1 + +WORKDIR /workspaces/wasmedge-wasinn-examples/tflite-birds_v1-image/ +RUN pushd rust \ + && cargo build --target=wasm32-wasip1 + +# mount models when running +WORKDIR /workspaces/wasmedge-wasinn-examples/wasmedge-ggml/qwen +RUN wget --progress=dot:giga https://www.modelscope.cn/models/qwen/Qwen1.5-0.5B-Chat-GGUF/resolve/master/qwen1_5-0_5b-chat-q2_k.gguf +RUN cargo build --target=wasm32-wasip1 +# +# +# iwasm. build from source +WORKDIR /workspaces/wamr +COPY . . + +WORKDIR /workspaces/wamr/product-mini/platforms/linux + +RUN OpenVINO_DIR=/usr/lib/openvino-2023.2.0 \ + cmake -S . -B build \ + -DWAMR_BUILD_WASI_NN=1 -DWAMR_BUILD_WASI_EPHEMERAL_NN=1 \ + -DWAMR_BUILD_WASI_NN_OPENVINO=1 \ + -DWAMR_BUILD_WASI_NN_TFLITE=1 \ + -DWAMR_BUILD_WASI_NN_LLAMACPP=1 \ + && cmake --build build \ + && cmake --install build + +ENV LD_LIBRARY_PATH=/usr/local/lib + + +# add smoke test script +COPY core/iwasm/libraries/wasi-nn/test/run_smoke_test.py / + +WORKDIR /workspaces/wasmedge-wasinn-examples +CMD ["python3", "/run_smoke_test.py"] diff --git a/core/iwasm/libraries/wasi-nn/test/build.sh b/core/iwasm/libraries/wasi-nn/test/build.sh new file mode 100755 index 0000000000..79d65d730c --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/build.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# on intel mac, this ends up with a lot of the following error. +# +# AttributeError: 'Sequential' object has no attribute '_get_save_spec'. +# +# * "pip install tensorflow" installs tensorflow 2.16.2 on intel mac. +# (because it's the last version before tf deprecated the target.) +# * keras 3 support in the version seems incomplete (thus the error) +# * a workaround: use keras 2 as mentioned in: +# https://github.com/tensorflow/tensorflow/releases/tag/v2.16.1 +# https://blog.tensorflow.org/2024/03/whats-new-in-tensorflow-216.html + +set -e + +CURR_PATH=$(cd $(dirname $0) && pwd -P) + +# WASM application that uses WASI-NN + +/opt/wasi-sdk/bin/clang \ + --target=wasm32-wasi \ + -DNN_LOG_LEVEL=1 \ + -Wl,--allow-undefined \ + -I../include -I../src/utils \ + -o test_tensorflow.wasm \ + test_tensorflow.c utils.c + +# TFLite models to use in the tests + +cd ${CURR_PATH}/models +python3 average.py +python3 max.py +python3 mult_dimension.py +python3 mult_outputs.py +python3 sum.py + +# Specific tests for TPU + +cd ${CURR_PATH} +/opt/wasi-sdk/bin/clang \ + --target=wasm32-wasi \ + -DNN_LOG_LEVEL=1 \ + -Wl,--allow-undefined \ + -I../include -I../src/utils \ + -o test_tensorflow_quantized.wasm \ + test_tensorflow_quantized.c utils.c + +cd ${CURR_PATH}/models +python3 quantized.py + +cd ${CURR_PATH} diff --git a/core/iwasm/libraries/wasi-nn/test/bump_wasi_nn_to_0_6_0.patch b/core/iwasm/libraries/wasi-nn/test/bump_wasi_nn_to_0_6_0.patch new file mode 100644 index 0000000000..46e152b278 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/bump_wasi_nn_to_0_6_0.patch @@ -0,0 +1,47 @@ +diff --git a/openvino-mobilenet-image/rust/Cargo.toml b/openvino-mobilenet-image/rust/Cargo.toml +index d09e0a4..c7083fb 100644 +--- a/openvino-mobilenet-image/rust/Cargo.toml ++++ b/openvino-mobilenet-image/rust/Cargo.toml +@@ -8,6 +8,6 @@ publish = false + + [dependencies] + image = { version = "0.23.14", default-features = false, features = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld"] } +-wasi-nn = { version = "0.4.0" } ++wasi-nn = { version = "0.6.0" } + + [workspace] +diff --git a/openvino-mobilenet-raw/rust/Cargo.toml b/openvino-mobilenet-raw/rust/Cargo.toml +index 8eab25b..3f00aec 100644 +--- a/openvino-mobilenet-raw/rust/Cargo.toml ++++ b/openvino-mobilenet-raw/rust/Cargo.toml +@@ -7,6 +7,6 @@ edition = "2021" + publish = false + + [dependencies] +-wasi-nn = { version = "0.4.0" } ++wasi-nn = { version = "0.6.0" } + + [workspace] +diff --git a/openvino-road-segmentation-adas/openvino-road-seg-adas/Cargo.toml b/openvino-road-segmentation-adas/openvino-road-seg-adas/Cargo.toml +index 998f391..93f91e0 100644 +--- a/openvino-road-segmentation-adas/openvino-road-seg-adas/Cargo.toml ++++ b/openvino-road-segmentation-adas/openvino-road-seg-adas/Cargo.toml +@@ -5,5 +5,5 @@ name = "openvino-road-seg-adas" + version = "0.2.0" + + [dependencies] +-wasi-nn = "0.4.0" ++wasi-nn = "0.6.0" + image = { version = "0.23.14", default-features = false, features = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld"] } +diff --git a/tflite-birds_v1-image/rust/Cargo.toml b/tflite-birds_v1-image/rust/Cargo.toml +index 572ecb9..9e89e87 100644 +--- a/tflite-birds_v1-image/rust/Cargo.toml ++++ b/tflite-birds_v1-image/rust/Cargo.toml +@@ -8,6 +8,6 @@ publish = false + + [dependencies] + image = { version = "0.23.14", default-features = false, features = ["gif", "jpeg", "ico", "png", "pnm", "tga", "tiff", "webp", "bmp", "hdr", "dxt", "dds", "farbfeld"] } +-wasi-nn = "0.4.0" ++wasi-nn = "0.6.0" + + [workspace] diff --git a/core/iwasm/libraries/wasi-nn/test/models/average.py b/core/iwasm/libraries/wasi-nn/test/models/average.py new file mode 100755 index 0000000000..a21fe75207 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/average.py @@ -0,0 +1,16 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf +from utils import save_model + +model = tf.keras.Sequential([ + tf.keras.layers.InputLayer(input_shape=[5, 5, 1]), + tf.keras.layers.AveragePooling2D( + pool_size=(5, 5), strides=None, padding="valid", data_format=None) + +]) + +# Export model to tflite + +save_model(model, "average.tflite") diff --git a/core/iwasm/libraries/wasi-nn/test/models/max.py b/core/iwasm/libraries/wasi-nn/test/models/max.py new file mode 100755 index 0000000000..a3ec456778 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/max.py @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf + +from utils import save_model + +model = tf.keras.Sequential([ + tf.keras.layers.InputLayer(input_shape=[5, 5, 1]), + tf.keras.layers.MaxPooling2D( + pool_size=(5, 5), strides=None, padding="valid", data_format=None) + +]) + +# Export model to tflite + +save_model(model, "max.tflite") diff --git a/core/iwasm/libraries/wasi-nn/test/models/mult_dimension.py b/core/iwasm/libraries/wasi-nn/test/models/mult_dimension.py new file mode 100644 index 0000000000..f521a93afb --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/mult_dimension.py @@ -0,0 +1,15 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf +from utils import save_model + +model = tf.keras.Sequential([ + tf.keras.layers.InputLayer(input_shape=[3, 3, 1]), + tf.keras.layers.Conv2D(1, (1, 1), kernel_initializer=tf.keras.initializers.Constant( + value=1), bias_initializer='zeros' + ) +]) +# Export model to tflite + +save_model(model, "mult_dim.tflite") diff --git a/core/iwasm/libraries/wasi-nn/test/models/mult_outputs.py b/core/iwasm/libraries/wasi-nn/test/models/mult_outputs.py new file mode 100755 index 0000000000..8506e18062 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/mult_outputs.py @@ -0,0 +1,33 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf +import numpy as np +from tensorflow.keras.layers import AveragePooling2D, Conv2D + +from tensorflow.keras import Input, Model + +from utils import save_model + + +inputs = Input(shape=(4, 4, 1)) + +output1 = Conv2D(1, (4, 1), kernel_initializer=tf.keras.initializers.Constant( + value=1), bias_initializer='zeros' +)(inputs) +output2 = AveragePooling2D(pool_size=( + 4, 1), strides=None, padding="valid", data_format=None)(inputs) + +model = Model(inputs=inputs, outputs=[output1, output2]) + +inp = np.arange(16).reshape((1, 4, 4, 1)) + +print(inp) + +res = model.predict(inp) + +print(res) +print(res[0].shape) +print(res[1].shape) + +save_model(model, "mult_out.tflite") diff --git a/core/iwasm/libraries/wasi-nn/test/models/quantized.py b/core/iwasm/libraries/wasi-nn/test/models/quantized.py new file mode 100644 index 0000000000..b195b319f5 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/quantized.py @@ -0,0 +1,30 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf +import numpy as np +import pathlib + +model = tf.keras.Sequential([ + tf.keras.layers.InputLayer(input_shape=[5, 5, 1]), + tf.keras.layers.AveragePooling2D( + pool_size=(5, 5), strides=None, padding="valid", data_format=None) + +]) + +def representative_dataset(): + for _ in range(1000): + data = np.random.randint(0, 25, (1, 5, 5, 1)) + yield [data.astype(np.float32)] + +converter = tf.lite.TFLiteConverter.from_keras_model(model) +converter.optimizations = [tf.lite.Optimize.DEFAULT] +converter.representative_dataset = representative_dataset +converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8] +converter.inference_input_type = tf.uint8 # or tf.int8 +converter.inference_output_type = tf.uint8 # or tf.int8 +tflite_model = converter.convert() + +tflite_models_dir = pathlib.Path("./") +tflite_model_file = tflite_models_dir / "quantized_model.tflite" +tflite_model_file.write_bytes(tflite_model) diff --git a/core/iwasm/libraries/wasi-nn/test/models/sum.py b/core/iwasm/libraries/wasi-nn/test/models/sum.py new file mode 100755 index 0000000000..503125b340 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/sum.py @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf + +from utils import save_model + +model = tf.keras.Sequential([ + tf.keras.layers.InputLayer(input_shape=[5, 5, 1]), + tf.keras.layers.Conv2D(1, (5, 5), kernel_initializer=tf.keras.initializers.Constant( + value=1), bias_initializer='zeros' + ) +]) + +# Export model to tflite + +save_model(model, "sum.tflite") diff --git a/core/iwasm/libraries/wasi-nn/test/models/utils.py b/core/iwasm/libraries/wasi-nn/test/models/utils.py new file mode 100644 index 0000000000..8335f05dac --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/models/utils.py @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import tensorflow as tf +import pathlib + + +def save_model(model, filename): + converter = tf.lite.TFLiteConverter.from_keras_model(model) + tflite_model = converter.convert() + tflite_models_dir = pathlib.Path("./") + tflite_model_file = tflite_models_dir/filename + tflite_model_file.write_bytes(tflite_model) diff --git a/core/iwasm/libraries/wasi-nn/test/requirements.txt b/core/iwasm/libraries/wasi-nn/test/requirements.txt new file mode 100644 index 0000000000..1643b91b00 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/requirements.txt @@ -0,0 +1,2 @@ +tensorflow==2.12.1 +numpy==1.24.4 diff --git a/core/iwasm/libraries/wasi-nn/test/run_smoke_test.py b/core/iwasm/libraries/wasi-nn/test/run_smoke_test.py new file mode 100644 index 0000000000..489e6e59f0 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/run_smoke_test.py @@ -0,0 +1,347 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from dataclasses import dataclass +from pathlib import Path +from pprint import pprint +import re +import shlex +import shutil +import subprocess +from typing import List + + +@dataclass +class WasmEdgeExampleResult: + class_id: int + possibility: float + + +def execute_once( + runtime_bin: str, + runtime_args: List[str], + wasm_file: str, + wasm_args: List[str], + cwd: Path, +) -> str: + cmd = [runtime_bin] + cmd.extend(runtime_args) + cmd.append(wasm_file) + cmd.extend(wasm_args) + + # print(f'Execute: {" ".join(cmd)}') + + p = subprocess.run( + cmd, + cwd=cwd, + capture_output=True, + check=True, + text=True, + universal_newlines=True, + ) + return p.stdout + + +def execute_openvino_road_segmentation_adas_once( + runtime_bin: str, runtime_args: List[str], cwd: Path +) -> str: + """ + execute openvino-road-segmentation-adas with iwasm and wasmedge + """ + + wasm_file = ( + "./openvino-road-seg-adas/target/wasm32-wasip1/debug/openvino-road-seg-adas.wasm" + ) + wasm_args = [ + "./model/road-segmentation-adas-0001.xml", + "./model/road-segmentation-adas-0001.bin", + "./image/empty_road_mapillary.jpg", + ] + return execute_once(runtime_bin, runtime_args, wasm_file, wasm_args, cwd) + + +def execute_openvino_mobilenet_raw_once( + runtime_bin: str, runtime_args: List[str], cwd: Path +) -> str: + """ + execute openvino-mobilenet-image with iwasm and wasmedge + """ + + wasm_file = "./rust/target/wasm32-wasip1/debug/wasmedge-wasinn-example-mobilenet.wasm" + wasm_args = [ + "mobilenet.xml", + "mobilenet.bin", + "./tensor-1x224x224x3-f32.bgr", + ] + return execute_once(runtime_bin, runtime_args, wasm_file, wasm_args, cwd) + + +def execute_openvino_mobilenet_image_once( + runtime_bin: str, runtime_args: List[str], cwd: Path +) -> str: + """ + execute openvino-mobilenet-image with iwasm and wasmedge + """ + + wasm_file = ( + "./rust/target/wasm32-wasip1/debug/wasmedge-wasinn-example-mobilenet-image.wasm" + ) + wasm_args = [ + "mobilenet.xml", + "mobilenet.bin", + "input.jpg", + ] + return execute_once(runtime_bin, runtime_args, wasm_file, wasm_args, cwd) + + +def execute_tflite_birds_v1_image_once( + runtime_bin: str, runtime_args: List[str], cwd: Path +) -> str: + """ + execute openvino-mobilenet-image with iwasm and wasmedge + """ + + wasm_file = ( + "rust/target/wasm32-wasip1/debug/wasmedge-wasinn-example-tflite-bird-image.wasm" + ) + wasm_args = ["lite-model_aiy_vision_classifier_birds_V1_3.tflite", "bird.jpg"] + return execute_once(runtime_bin, runtime_args, wasm_file, wasm_args, cwd) + + +def filter_output(output: str) -> List[WasmEdgeExampleResult]: + """ + not all output is required for comparison + + pick lines like: " 1.) [166](198)Aix galericulata" + """ + filtered = [] + PATTERN = re.compile(r"^\s+\d\.\)\s+\[(\d+)\]\(([.0-9]+)\)\w+") + for line in output.split("\n"): + m = PATTERN.search(line) + if m: + class_id, possibility = m.groups() + filtered.append(WasmEdgeExampleResult(class_id, possibility)) + + assert len(filtered) + return filtered + + +def compare_output( + iwasm_output: List[WasmEdgeExampleResult], + wasmedge_output: List[WasmEdgeExampleResult], +) -> bool: + """ + only compare top 2 and ignore possibility + """ + return (iwasm_output[0].class_id, iwasm_output[1].class_id) == ( + wasmedge_output[0].class_id, + wasmedge_output[1].class_id, + ) + + +def summarizer_result( + example_name: str, + iwasm_output: List[WasmEdgeExampleResult], + wasmedge_output: List[WasmEdgeExampleResult], +): + if compare_output(iwasm_output, wasmedge_output): + print(f"- {example_name}. PASS") + return + + print(f"- {example_name}. FAILED") + print("------------------------------------------------------------") + pprint(iwasm_output) + print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + pprint(wasmedge_output) + print("------------------------------------------------------------") + + +def execute_tflite_birds_v1_image(iwasm_bin: str, wasmedge_bin: str, cwd: Path): + iwasm_output = execute_tflite_birds_v1_image_once( + iwasm_bin, + [ + "--map-dir=.::.", + ], + cwd, + ) + iwasm_output = filter_output(iwasm_output) + + wasmedge_output = execute_tflite_birds_v1_image_once( + wasmedge_bin, ["--dir=.:."], cwd + ) + wasmedge_output = filter_output(wasmedge_output) + + summarizer_result("tf_lite_birds_v1_image", iwasm_output, wasmedge_output) + + +def execute_openvino_mobilenet_image(iwasm_bin: str, wasmedge_bin: str, cwd: Path): + iwasm_output = execute_openvino_mobilenet_image_once( + iwasm_bin, + [ + "--map-dir=.::.", + ], + cwd, + ) + iwasm_output = filter_output(iwasm_output) + + wasmedge_output = execute_openvino_mobilenet_image_once( + wasmedge_bin, ["--dir=.:."], cwd + ) + wasmedge_output = filter_output(wasmedge_output) + + summarizer_result("openvino_mobile_image", iwasm_output, wasmedge_output) + + +def execute_openvino_mobilenet_raw(iwasm_bin: str, wasmedge_bin: str, cwd: Path): + iwasm_output = execute_openvino_mobilenet_raw_once( + iwasm_bin, + [ + "--map-dir=.::.", + ], + cwd, + ) + iwasm_output = filter_output(iwasm_output) + + wasmedge_output = execute_openvino_mobilenet_raw_once( + wasmedge_bin, ["--dir=.:."], cwd + ) + wasmedge_output = filter_output(wasmedge_output) + + summarizer_result("openvino_mobile_raw", iwasm_output, wasmedge_output) + + +def execute_openvino_road_segmentation_adas( + iwasm_bin: str, wasmedge_bin: str, cwd: Path +): + def filter_output(output: str) -> str: + """ + focus on lines: + The size of the output buffer is 7340032 bytes + dump tensor to "wasinn-openvino-inference-output-1x4x512x896xf32.tensor" + """ + for line in output.split("\n"): + if "The size of the output buffer is" in line: + dump_tensor_size = int(line.split(" ")[-2]) + continue + + if "dump tensor to " in line: + dump_tensor_file = line.split(" ")[-1] + continue + + return (dump_tensor_file, dump_tensor_size) + + iwasm_output = execute_openvino_road_segmentation_adas_once( + iwasm_bin, + [ + "--map-dir=.::.", + ], + cwd, + ) + iwasm_tensor_file, iwasm_tensor_size = filter_output(iwasm_output) + + wasmedge_output = execute_openvino_road_segmentation_adas_once( + wasmedge_bin, ["--dir=.:."], cwd + ) + wasmedge_tensor_file, wasmedge_tensor_size = filter_output(wasmedge_output) + + # TODO: binary compare? + if iwasm_tensor_size == wasmedge_tensor_size: + print(f"- openvino_road_segmentation_adas. PASS") + return + + print(f"- openvino_road_segmentation_adas. FAILED") + print("------------------------------------------------------------") + print(f"FILE:{iwasm_tensor_file}, SIZE:{iwasm_tensor_size}") + print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + print(f"FILE:{wasmedge_tensor_file}, SIZE:{wasmedge_tensor_size}") + print("------------------------------------------------------------") + + +def execute_wasmedge_ggml_qwen(iwasm_bin: str, wasmedge_bin: str, cwd: Path): + iwasm_args = ["--dir=."] + wasm_file = ["./target/wasm32-wasip1/debug/wasmedge-ggml-qwen.wasm"] + wasm_args = ["./qwen1_5-0_5b-chat-q2_k.gguf"] + + cmd = [iwasm_bin] + cmd.extend(iwasm_args) + cmd.extend(wasm_file) + cmd.extend(wasm_args) + + # print(f'Execute: {" ".join(cmd)}') + + prompt = "what is the capital of Pakistan" + + with subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=cwd, + ) as p: + # USER + p.stdout.readline() + + p.stdin.write(b"hi\n") + p.stdin.flush() + # ASSISTANT + p.stdout.readline() + # xxx + p.stdout.readline() + # USER + p.stdout.readline() + + p.stdin.write(prompt.encode()) + p.stdin.write(b"\n") + p.stdin.flush() + # ASSISTANT + p.stdout.readline() + # xxx + answer = p.stdout.readline().decode("utf-8") + # USER + p.stdout.readline() + + p.terminate() + + if "Karachi" in answer: + print(f"- wasmedge_ggml_qwen. PASS") + return + + print(f"- wasmedge_ggml_qwen. FAILED") + print("------------------------------------------------------------") + pprint(answer) + print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<") + pprint("Karachi") + print("------------------------------------------------------------") + + +def execute_wasmedge_wasinn_examples(iwasm_bin: str, wasmedge_bin: str): + assert Path.cwd().name == "wasmedge-wasinn-examples" + assert shutil.which(iwasm_bin) + assert shutil.which(wasmedge_bin) + + # TODO: keep commenting until https://github.com/bytecodealliance/wasm-micro-runtime/pull/3597 is merged + # tflite_birds_v1_image_dir = Path.cwd().joinpath("./tflite-birds_v1-image") + # execute_tflite_birds_v1_image(iwasm_bin, wasmedge_bin, tflite_birds_v1_image_dir) + + openvino_mobile_image_dir = Path.cwd().joinpath("./openvino-mobilenet-image") + execute_openvino_mobilenet_image(iwasm_bin, wasmedge_bin, openvino_mobile_image_dir) + + openvino_mobile_raw_dir = Path.cwd().joinpath("./openvino-mobilenet-raw") + execute_openvino_mobilenet_raw(iwasm_bin, wasmedge_bin, openvino_mobile_raw_dir) + + openvino_road_segmentation_adas_dir = Path.cwd().joinpath( + "./openvino-road-segmentation-adas" + ) + execute_openvino_road_segmentation_adas( + iwasm_bin, wasmedge_bin, openvino_road_segmentation_adas_dir + ) + + wasmedge_ggml_qwem_dir = Path.cwd().joinpath("./wasmedge-ggml/qwen") + execute_wasmedge_ggml_qwen(iwasm_bin, wasmedge_bin, wasmedge_ggml_qwem_dir) + + +if __name__ == "__main__": + execute_wasmedge_wasinn_examples("iwasm", "wasmedge") diff --git a/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c b/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c new file mode 100644 index 0000000000..6a9e20702f --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/test_tensorflow.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "logger.h" + +void +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(target, input.input_tensor, input.dim, + &output_size, "./models/sum.tflite", 1); + + assert(output_size == 1); + assert(fabs(output[0] - 300.0) < EPSILON); + + free(input.dim); + free(input.input_tensor); + free(output); +} + +void +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(target, input.input_tensor, input.dim, + &output_size, "./models/max.tflite", 1); + + assert(output_size == 1); + assert(fabs(output[0] - 24.0) < EPSILON); + NN_INFO_PRINTF("Result: max is %f", output[0]); + + free(input.dim); + free(input.input_tensor); + free(output); +} + +void +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(target, input.input_tensor, input.dim, + &output_size, "./models/average.tflite", 1); + + assert(output_size == 1); + assert(fabs(output[0] - 12.0) < EPSILON); + NN_INFO_PRINTF("Result: average is %f", output[0]); + + free(input.dim); + free(input.input_tensor); + free(output); +} + +void +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(target, input.input_tensor, input.dim, + &output_size, "./models/mult_dim.tflite", 1); + + assert(output_size == 9); + for (int i = 0; i < 9; i++) + assert(fabs(output[i] - i) < EPSILON); + + free(input.dim); + free(input.input_tensor); + free(output); +} + +void +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(target, input.input_tensor, input.dim, + &output_size, "./models/mult_out.tflite", 2); + + assert(output_size == 8); + // first tensor check + for (int i = 0; i < 4; i++) + assert(fabs(output[i] - (i * 4 + 24)) < EPSILON); + // second tensor check + for (int i = 0; i < 4; i++) + assert(fabs(output[i + 4] - (i + 6)) < EPSILON); + + free(input.dim); + free(input.input_tensor); + free(output); +} + +int +main() +{ + 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); + + NN_INFO_PRINTF("Tests: passed!"); + return 0; +} diff --git a/core/iwasm/libraries/wasi-nn/test/test_tensorflow_quantized.c b/core/iwasm/libraries/wasi-nn/test/test_tensorflow_quantized.c new file mode 100644 index 0000000000..3ed7c751e3 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/test_tensorflow_quantized.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "logger.h" + +#undef EPSILON +#define EPSILON 1e-2 + +void +test_average_quantized(execution_target target) +{ + int dims[] = { 1, 5, 5, 1 }; + input_info input = create_input(dims); + + uint32_t output_size = 0; + float *output = + run_inference(target, input.input_tensor, input.dim, &output_size, + "./models/quantized_model.tflite", 1); + + NN_INFO_PRINTF("Output size: %d", output_size); + NN_INFO_PRINTF("Result: average is %f", output[0]); + // NOTE: 11.95 instead of 12 because of errors due quantization + assert(fabs(output[0] - 11.95) < EPSILON); + + free(input.dim); + free(input.input_tensor); + free(output); +} + +int +main() +{ + char *env = getenv("TARGET"); + if (env == NULL) { + NN_INFO_PRINTF("Usage:\n--env=\"TARGET=[cpu|gpu|tpu]\""); + return 1; + } + execution_target target; + if (strcmp(env, "cpu") == 0) + target = cpu; + else if (strcmp(env, "gpu") == 0) + target = gpu; + else if (strcmp(env, "tpu") == 0) + target = tpu; + else { + NN_ERR_PRINTF("Wrong target!"); + return 1; + } + NN_INFO_PRINTF("################### Testing quantized model..."); + test_average_quantized(target); + + 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 0000000000..690c37f0e7 --- /dev/null +++ b/core/iwasm/libraries/wasi-nn/test/utils.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "utils.h" +#include "logger.h" +#include "wasi_nn.h" + +#include +#include + +wasi_nn_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 too_large; + } + + result = fread(buffer, 1, MAX_MODEL_SIZE, pFile); + if (result <= 0) { + fclose(pFile); + free(buffer); + return too_large; + } + + graph_builder_array arr; + + arr.size = 1; + arr.buf = (graph_builder *)malloc(sizeof(graph_builder)); + if (arr.buf == NULL) { + fclose(pFile); + free(buffer); + return too_large; + } + + arr.buf[0].size = result; + arr.buf[0].buf = buffer; + + wasi_nn_error res = load(&arr, tensorflowlite, target, g); + + fclose(pFile); + free(buffer); + free(arr.buf); + return res; +} + +wasi_nn_error +wasm_load_by_name(const char *model_name, graph *g) +{ + wasi_nn_error res = load_by_name(model_name, strlen(model_name), g); + return res; +} + +wasi_nn_error +wasm_init_execution_context(graph g, graph_execution_context *ctx) +{ + return init_execution_context(g, ctx); +} + +wasi_nn_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 too_large; + + 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; + wasi_nn_error err = set_input(ctx, 0, &tensor); + + free(dims.buf); + return err; +} + +wasi_nn_error +wasm_compute(graph_execution_context ctx) +{ + return compute(ctx); +} + +wasi_nn_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_by_name(model_name, &graph) != 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 index %d.", i); + break; + } + + 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 0000000000..e0d2417724 --- /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_types.h" + +#define MAX_MODEL_SIZE 85000000 +#define MAX_OUTPUT_TENSOR_SIZE 1000000 +#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 */ + +wasi_nn_error +wasm_load(char *model_name, graph *g, execution_target target); + +wasi_nn_error +wasm_init_execution_context(graph g, graph_execution_context *ctx); + +wasi_nn_error +wasm_set_input(graph_execution_context ctx, float *input_tensor, uint32_t *dim); + +wasi_nn_error +wasm_compute(graph_execution_context ctx); + +wasi_nn_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/shared/coap/er-coap/coap-constants.h b/core/shared/coap/er-coap/coap-constants.h index f73a5b950b..1de2ed9d8a 100644 --- a/core/shared/coap/er-coap/coap-constants.h +++ b/core/shared/coap/er-coap/coap-constants.h @@ -44,6 +44,7 @@ #ifndef COAP_CONSTANTS_H_ #define COAP_CONSTANTS_H_ +/* clang-format off */ #define COAP_DEFAULT_PORT 5683 #define COAP_DEFAULT_SECURE_PORT 5684 @@ -65,128 +66,129 @@ #define COAP_HEADER_OPTION_DELTA_MASK 0xF0 #define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F +/* clang-format on */ /* CoAP message types */ typedef enum { - COAP_TYPE_CON, /* confirmables */ - COAP_TYPE_NON, /* non-confirmables */ - COAP_TYPE_ACK, /* acknowledgements */ - COAP_TYPE_RST /* reset */ + COAP_TYPE_CON, /* confirmables */ + COAP_TYPE_NON, /* non-confirmables */ + COAP_TYPE_ACK, /* acknowledgements */ + COAP_TYPE_RST /* reset */ } coap_message_type_t; +/* clang-format off */ /* CoAP request method codes */ typedef enum { - COAP_GET = 1, - COAP_POST, - COAP_PUT, - COAP_DELETE + COAP_GET = 1, + COAP_POST, COAP_PUT, + COAP_DELETE } coap_method_t; +/* clang-format on */ /* CoAP response codes */ typedef enum { - NO_ERROR = 0, - - CREATED_2_01 = 65, /* CREATED */ - DELETED_2_02 = 66, /* DELETED */ - VALID_2_03 = 67, /* NOT_MODIFIED */ - CHANGED_2_04 = 68, /* CHANGED */ - CONTENT_2_05 = 69, /* OK */ - CONTINUE_2_31 = 95, /* CONTINUE */ - - BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ - UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ - BAD_OPTION_4_02 = 130, /* BAD_OPTION */ - FORBIDDEN_4_03 = 131, /* FORBIDDEN */ - NOT_FOUND_4_04 = 132, /* NOT_FOUND */ - METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ - NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ - PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ - REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ - UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ - - INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ - NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ - BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ - SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ - GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ - PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ - - /* Erbium errors */ - MEMORY_ALLOCATION_ERROR = 192, - PACKET_SERIALIZATION_ERROR, - - /* Erbium hooks */ - MANUAL_RESPONSE, - PING_RESPONSE + COAP_NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + CONTINUE_2_31 = 95, /* CONTINUE */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, + PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE, + PING_RESPONSE } coap_status_t; /* CoAP header option numbers */ typedef enum { - COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ - COAP_OPTION_URI_HOST = 3, /* 1-255 B */ - COAP_OPTION_ETAG = 4, /* 1-8 B */ - COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ - COAP_OPTION_OBSERVE = 6, /* 0-3 B */ - COAP_OPTION_URI_PORT = 7, /* 0-2 B */ - COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ - COAP_OPTION_URI_PATH = 11, /* 0-255 B */ - COAP_OPTION_CONTENT_FORMAT = 12, /* 0-2 B */ - COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ - COAP_OPTION_URI_QUERY = 15, /* 0-255 B */ - COAP_OPTION_ACCEPT = 17, /* 0-2 B */ - COAP_OPTION_LOCATION_QUERY = 20, /* 0-255 B */ - COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ - COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ - COAP_OPTION_SIZE2 = 28, /* 0-4 B */ - COAP_OPTION_PROXY_URI = 35, /* 1-1034 B */ - COAP_OPTION_PROXY_SCHEME = 39, /* 1-255 B */ - COAP_OPTION_SIZE1 = 60, /* 0-4 B */ + COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ + COAP_OPTION_URI_HOST = 3, /* 1-255 B */ + COAP_OPTION_ETAG = 4, /* 1-8 B */ + COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ + COAP_OPTION_OBSERVE = 6, /* 0-3 B */ + COAP_OPTION_URI_PORT = 7, /* 0-2 B */ + COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ + COAP_OPTION_URI_PATH = 11, /* 0-255 B */ + COAP_OPTION_CONTENT_FORMAT = 12, /* 0-2 B */ + COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ + COAP_OPTION_URI_QUERY = 15, /* 0-255 B */ + COAP_OPTION_ACCEPT = 17, /* 0-2 B */ + COAP_OPTION_LOCATION_QUERY = 20, /* 0-255 B */ + COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ + COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ + COAP_OPTION_SIZE2 = 28, /* 0-4 B */ + COAP_OPTION_PROXY_URI = 35, /* 1-1034 B */ + COAP_OPTION_PROXY_SCHEME = 39, /* 1-255 B */ + COAP_OPTION_SIZE1 = 60, /* 0-4 B */ } coap_option_t; /* CoAP Content-Formats */ typedef enum { - TEXT_PLAIN = 0, - TEXT_XML = 1, - TEXT_CSV = 2, - TEXT_HTML = 3, - IMAGE_GIF = 21, - IMAGE_JPEG = 22, - IMAGE_PNG = 23, - IMAGE_TIFF = 24, - AUDIO_RAW = 25, - VIDEO_RAW = 26, - APPLICATION_LINK_FORMAT = 40, - APPLICATION_XML = 41, - APPLICATION_OCTET_STREAM = 42, - APPLICATION_RDF_XML = 43, - APPLICATION_SOAP_XML = 44, - APPLICATION_ATOM_XML = 45, - APPLICATION_XMPP_XML = 46, - APPLICATION_EXI = 47, - APPLICATION_FASTINFOSET = 48, - APPLICATION_SOAP_FASTINFOSET = 49, - APPLICATION_JSON = 50, - APPLICATION_X_OBIX_BINARY = 51 + TEXT_PLAIN = 0, + TEXT_XML = 1, + TEXT_CSV = 2, + TEXT_HTML = 3, + IMAGE_GIF = 21, + IMAGE_JPEG = 22, + IMAGE_PNG = 23, + IMAGE_TIFF = 24, + AUDIO_RAW = 25, + VIDEO_RAW = 26, + APPLICATION_LINK_FORMAT = 40, + APPLICATION_XML = 41, + APPLICATION_OCTET_STREAM = 42, + APPLICATION_RDF_XML = 43, + APPLICATION_SOAP_XML = 44, + APPLICATION_ATOM_XML = 45, + APPLICATION_XMPP_XML = 46, + APPLICATION_EXI = 47, + APPLICATION_FASTINFOSET = 48, + APPLICATION_SOAP_FASTINFOSET = 49, + APPLICATION_JSON = 50, + APPLICATION_X_OBIX_BINARY = 51 } coap_content_format_t; /** * Resource flags for allowed methods and special functionalities. */ typedef enum { - NO_FLAGS = 0, - - /* methods to handle */ - METHOD_GET = (1 << 0), - METHOD_POST = (1 << 1), - METHOD_PUT = (1 << 2), - METHOD_DELETE = (1 << 3), - - /* special flags */ - HAS_SUB_RESOURCES = (1 << 4), - IS_SEPARATE = (1 << 5), - IS_OBSERVABLE = (1 << 6), - IS_PERIODIC = (1 << 7) + NO_FLAGS = 0, + + /* methods to handle */ + METHOD_GET = (1 << 0), + METHOD_POST = (1 << 1), + METHOD_PUT = (1 << 2), + METHOD_DELETE = (1 << 3), + + /* special flags */ + HAS_SUB_RESOURCES = (1 << 4), + IS_SEPARATE = (1 << 5), + IS_OBSERVABLE = (1 << 6), + IS_PERIODIC = (1 << 7) } coap_resource_flags_t; #endif /* COAP_CONSTANTS_H_ */ -/** @} */ diff --git a/core/shared/include/bh_common.h b/core/shared/include/bh_common.h deleted file mode 100644 index 831ffe8a4b..0000000000 --- a/core/shared/include/bh_common.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_COMMON_H -#define _BH_COMMON_H - -#include "bh_assert.h" -#include "bh_platform.h" -#include "bh_list.h" - -typedef void (*bh_print_function_t)(const char* message); -void bh_set_print_function(bh_print_function_t pf); - -#define bh_memcpy_s(dest, dlen, src, slen) do { \ - int _ret = slen == 0 ? 0 : b_memcpy_s (dest, dlen, src, slen); \ - (void)_ret; \ - bh_assert (_ret == 0); \ - } while (0) - -#define bh_strcat_s(dest, dlen, src) do { \ - int _ret = b_strcat_s (dest, dlen, src); \ - (void)_ret; \ - bh_assert (_ret == 0); \ - } while (0) - -#define bh_strcpy_s(dest, dlen, src) do { \ - int _ret = b_strcpy_s (dest, dlen, src); \ - (void)_ret; \ - bh_assert (_ret == 0); \ - } while (0) - -#endif diff --git a/core/shared/include/bh_hashmap.h b/core/shared/include/bh_hashmap.h deleted file mode 100644 index 7d260ec71d..0000000000 --- a/core/shared/include/bh_hashmap.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef WASM_HASHMAP_H -#define WASM_HASHMAP_H - -#include "bh_platform.h" - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Maximum initial size of hash map */ -#define HASH_MAP_MAX_SIZE 65536 - -struct HashMap; -typedef struct HashMap HashMap; - -/* Hash function: to get the hash value of key. */ -typedef uint32 (*HashFunc)(const void *key); - -/* Key equal function: to check whether two keys are equal. */ -typedef bool (*KeyEqualFunc)(void *key1, void *key2); - -/* Key destroy function: to destroy the key, auto called - when an hash element is removed. */ -typedef void (*KeyDestroyFunc)(void *key); - -/* Value destroy function: to destroy the value, auto called - when an hash element is removed. */ -typedef void (*ValueDestroyFunc)(void *key); - -/** - * Create a hash map. - * - * @param size: the initial size of the hash map - * @param use_lock whether to lock the hash map when operating on it - * @param hash_func hash function of the key, must be specified - * @param key_equal_func key equal function, check whether two keys - * are equal, must be specified - * @param key_destroy_func key destroy function, called when an hash element - * is removed if it is not NULL - * @param value_destroy_func value destroy function, called when an hash - * element is removed if it is not NULL - * - * @return the hash map created, NULL if failed - */ -HashMap* -bh_hash_map_create(uint32 size, bool use_lock, - HashFunc hash_func, - KeyEqualFunc key_equal_func, - KeyDestroyFunc key_destroy_func, - ValueDestroyFunc value_destroy_func); - -/** - * Insert an element to the hash map - * - * @param map the hash map to insert element - * @key the key of the element - * @value the value of the element - * - * @return true if success, false otherwise - * Note: fail if key is NULL or duplicated key exists in the hash map, - */ -bool -bh_hash_map_insert(HashMap *map, void *key, void *value); - -/** - * Find an element in the hash map - * - * @param map the hash map to find element - * @key the key of the element - * - * @return the value of the found element if success, NULL otherwise - */ -void* -bh_hash_map_find(HashMap *map, void *key); - -/** - * Update an element in the hash map with new value - * - * @param map the hash map to update element - * @key the key of the element - * @value the new value of the element - * @p_old_value if not NULL, copies the old value to it - * - * @return true if success, false otherwise - * Note: the old value won't be destroyed by value destroy function, - * it will be copied to p_old_value for user to process. - */ -bool -bh_hash_map_update(HashMap *map, void *key, void *value, - void **p_old_value); - -/** - * Remove an element from the hash map - * - * @param map the hash map to remove element - * @key the key of the element - * @p_old_key if not NULL, copies the old key to it - * @p_old_value if not NULL, copies the old value to it - * - * @return true if success, false otherwise - * Note: the old key and old value won't be destroyed by key destroy - * function and value destroy function, they will be copied to - * p_old_key and p_old_value for user to process. - */ -bool -bh_hash_map_remove(HashMap *map, void *key, - void **p_old_key, void **p_old_value); - -/** - * Destroy the hashmap - * - * @param map the hash map to destroy - * - * @return true if success, false otherwise - * Note: the key destroy function and value destroy function will be - * called to destroy each element's key and value if they are - * not NULL. - */ -bool -bh_hash_map_destroy(HashMap *map); - -#ifdef __cplusplus -} -#endif - -#endif /* endof WASM_HASHMAP_H */ - diff --git a/core/shared/include/bh_list.h b/core/shared/include/bh_list.h deleted file mode 100644 index 2e429c69b0..0000000000 --- a/core/shared/include/bh_list.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_LIST_H -#define _BH_LIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bh_types.h" /*For bool type*/ -#include "bh_platform.h" - -/* List user should embedded bh_list_link into list elem data structure - * definition. And bh_list_link data field should be the first field. - * For example, if we would like to use bh_list for our own data type A, - * A must be defined as a structure like below: - * struct A { - * bh_list_link l; - * ... - * }; - * - * bh_list_link is defined as a structure (not typedef void*). - * It will make extend list into bi-direction easy. - */ -typedef struct _bh_list_link { - struct _bh_list_link *next; -} bh_list_link; - -typedef struct _bh_list { - bh_list_link head; - uint32 len; -} bh_list; - -/* Beihai list operation return value */ -typedef enum _bh_list_status { - BH_LIST_SUCCESS = 0, BH_LIST_ERROR = -1 -} bh_list_status; - -/** - * Initialize a list. - * - * @param list pointer to list. - * @return BH_LIST_ERROR if OK; - * BH_LIST_ERROR if list pointer is NULL. - */ -bh_list_status bh_list_init(bh_list *list); - -/** - * Insert an elem pointer into list. The list node memory is maintained by list while - * elem memory is the responsibility of list user. - * - * @param list pointer to list. - * @param elem pointer to elem that will be inserted into list. - * @return BH_LIST_ERROR if OK; - * BH_LIST_ERROR if input is invalid or no memory available. - */ -extern bh_list_status _bh_list_insert(bh_list *list, void *elem); - -#ifdef _INSTRUMENT_TEST_ENABLED -extern bh_list_status bh_list_insert_instr(bh_list *list, void *elem, const char*func_name); -#define bh_list_insert(list, elem) bh_list_insert_instr(list, elem, __FUNCTION__) -#else -#define bh_list_insert _bh_list_insert -#endif - -/** - * Remove an elem pointer from list. The list node memory is maintained by list while - * elem memory is the responsibility of list user. - * - * @param list pointer to list. - * @param elem pointer to elem that will be inserted into list. - * @return BH_LIST_ERROR if OK; - * BH_LIST_ERROR if element does not exist in given list. - */ -bh_list_status bh_list_remove(bh_list *list, void *elem); - -/** - * Get the list length. - * - * @param list pointer to list. - * @return the length of the list. - */ -uint32 bh_list_length(bh_list *list); - -/** - * Get the first elem in the list. - * - * @param list pointer to list. - * @return pointer to the first node. - */ -void* bh_list_first_elem(bh_list *list); - -/** - * Get the next elem of given list input elem. - * - * @param node pointer to list node. - * @return pointer to next list node. - */ -void* bh_list_elem_next(void *node); - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef _BH_LIST_H */ - diff --git a/core/shared/include/bh_log.h b/core/shared/include/bh_log.h deleted file mode 100644 index 6b806e4643..0000000000 --- a/core/shared/include/bh_log.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -/** - * @file bh_log.h - * @date Tue Nov 8 18:19:10 2011 - * - * @brief This log system supports wrapping multiple outputs into one - * log message. This is useful for outputting variable-length logs - * without additional memory overhead (the buffer for concatenating - * the message), e.g. exception stack trace, which cannot be printed - * by a single log calling without the help of an additional buffer. - * Avoiding additional memory buffer is useful for resource-constraint - * systems. It can minimize the impact of log system on applications - * and logs can be printed even when no enough memory is available. - * Functions with prefix "_" are private functions. Only macros that - * are not start with "_" are exposed and can be used. - */ - -#ifndef _BH_LOG_H -#define _BH_LOG_H - -#include - -#include "bh_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * The following functions are the primitive operations of this log - * system. A normal usage of the log system is to call bh_log_printf - * or bh_log_vprintf one or multiple times and then call bh_log_commit - * to wrap (mark) the previous outputs into one log message. The - * bh_log and macros LOG_ERROR etc. can be used to output log messages - * that can be wrapped into one log calling. - */ -int _bh_log_init(void); -void _bh_log_set_verbose_level(int level); -void _bh_log_printf(const char *fmt, ...); -void _bh_log_vprintf(const char *fmt, va_list ap); -void _bh_log_commit(void); - -#if WASM_ENABLE_LOG != 0 -# define bh_log_init() _bh_log_init () -# define bh_log_set_verbose_level(l) _bh_log_set_verbose_level (l) -# define bh_log_printf(...) _bh_log_printf (__VA_ARGS__) -# define bh_log_vprintf(...) _bh_log_vprintf (__VA_ARGS__) -# define bh_log_commit() _bh_log_commit () -#else /* WASM_ENABLE_LOG != 0 */ -# define bh_log_init() 0 -# define bh_log_set_verbose_level(l) (void)0 -# define bh_log_printf(...) (void)0 -# define bh_log_vprintf(...) (void)0 -# define bh_log_commit() (void)0 -#endif /* WASM_ENABLE_LOG != 0 */ - -void _bh_log(const char *tag, const char *file, int line, const char *fmt, ...); - -/* Always print fatal message */ -# define LOG_FATAL(...) _bh_log ("V0.", NULL, 0, __VA_ARGS__) - -#if WASM_ENABLE_LOG != 0 -# define LOG_ERROR(...) _bh_log ("V1.", NULL, 0, __VA_ARGS__) -# define LOG_WARNING(...) _bh_log ("V2.", NULL, 0, __VA_ARGS__) -# define LOG_INFO_RELEASE(...) _bh_log ("V3.", NULL, 0, __VA_ARGS__) -# define LOG_INFO_APP_DEV(...) _bh_log ("V4.", NULL, 0, __VA_ARGS__) -# define LOG_VERBOSE(...) _bh_log ("V5.", NULL, 0, __VA_ARGS__) -# if BEIHAI_ENABLE_MEMORY_PROFILING != 0 -# define LOG_PROFILE(...) _bh_log ("V3.", NULL, 0, __VA_ARGS__) -# else -# define LOG_PROFILE(...) (void)0 -#endif -# if BEIHAI_ENABLE_QUEUE_PROFILING != 0 -# define LOG_QUEUE_PROFILE(...) _bh_log ("V3.", NULL, 0, __VA_ARGS__) -# else -# define LOG_QUEUE_PROFILE(...) (void)0 -#endif -# if BEIHAI_ENABLE_GC_STAT_PROFILING != 0 -# define LOG_GC_STAT_PROFILE(...) _bh_log ("V3.", NULL, 0, __VA_ARGS__) -# else -# define LOG_GC_STAT_PROFILE(...) (void)0 -#endif -#else /* WASM_ENABLE_LOG != 0 */ -# define LOG_ERROR(...) (void)0 -# define LOG_WARNING(...) (void)0 -# define LOG_INFO_APP_DEV(...) (void)0 -# define LOG_INFO_RELEASE(...) (void)0 -# define LOG_VERBOSE(...) (void)0 -# define LOG_PROFILE(...) (void)0 -# define LOG_QUEUE_PROFILE(...) (void)0 -# define LOG_GC_STAT_PROFILE(...) (void)0 -#endif /* WASM_ENABLE_LOG != 0 */ - -#define LOG_PROFILE_INSTANCE_HEAP_CREATED(heap) \ - LOG_PROFILE ("PROF.INSTANCE.HEAP_CREATED: HEAP=%08X", heap) -#define LOG_PROFILE_INSTANCE_THREAD_STARTED(heap) \ - LOG_PROFILE ("PROF.INSTANCE.THREAD_STARTED: HEAP=%08X", heap) -#define LOG_PROFILE_HEAP_ALLOC(heap, size) \ - LOG_PROFILE ("PROF.HEAP.ALLOC: HEAP=%08X SIZE=%d", heap, size) -#define LOG_PROFILE_HEAP_FREE(heap, size) \ - LOG_PROFILE ("PROF.HEAP.FREE: HEAP=%08X SIZE=%d", heap, size) -#define LOG_PROFILE_HEAP_NEW(heap, size) \ - LOG_PROFILE ("PROF.HEAP.NEW: HEAP=%08X SIZE=%d", heap, size) -#define LOG_PROFILE_HEAP_GC(heap, size) \ - LOG_PROFILE ("PROF.HEAP.GC: HEAP=%08X SIZE=%d", heap, size) -#define LOG_PROFILE_STACK_PUSH(used) \ - LOG_PROFILE ("PROF.STACK.PUSH: USED=%d", used) -#define LOG_PROFILE_STACK_POP() \ - LOG_PROFILE ("PROF.STACK.POP") - -/* Please add your component ahead of LOG_COM_MAX */ -enum { - LOG_COM_APP_MANAGER = 0, - LOG_COM_GC, - LOG_COM_HMC, - LOG_COM_UTILS, - LOG_COM_VERIFIER_JEFF, - LOG_COM_VMCORE_JEFF, - LOG_COM_MAX -}; - -#if defined(BH_DEBUG) -void log_parse_coms(const char *coms); -int bh_log_dcom_is_enabled(int component); - -#define LOG_DEBUG(component, ...) do { \ - if (bh_log_dcom_is_enabled (component)) \ - _bh_log ("V6: ", __FILE__, __LINE__, __VA_ARGS__); \ - } while (0) - -#else /* defined(BH_DEBUG) */ - -#define LOG_DEBUG(component, ...) (void)0 - -#endif /* defined(BH_DEBUG) */ - -#ifdef __cplusplus -} -#endif - -#endif /* _BH_LOG_H */ diff --git a/core/shared/include/bh_memory.h b/core/shared/include/bh_memory.h deleted file mode 100644 index 4b3aa86db5..0000000000 --- a/core/shared/include/bh_memory.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_MEMORY_H -#define _BH_MEMORY_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define BH_KB (1024) -#define BH_MB ((BH_KB)*1024) -#define BH_GB ((BH_MB)*1024) - -/** - * Initialize memory allocator with a pool, the bh_malloc/bh_free function - * will malloc/free memory from the pool - * - * @param mem the pool buffer - * @param bytes the size bytes of the buffer - * - * @return 0 if success, -1 otherwise - */ -int bh_memory_init_with_pool(void *mem, unsigned int bytes); - -/** - * Initialize memory allocator with memory allocator, the bh_malloc/bh_free - * function will malloc/free memory with the allocator passed - * - * @param malloc_func the malloc function - * @param free_func the free function - * - * @return 0 if success, -1 otherwise - */ -int bh_memory_init_with_allocator(void *malloc_func, void *free_func); - -/** - * Destroy memory - */ -void bh_memory_destroy(); - -/** - * Get the pool size of memory, if memory is initialized with allocator, - * return 1GB by default. - */ -unsigned bh_memory_pool_size(); - -#if BEIHAI_ENABLE_MEMORY_PROFILING == 0 - -/** - * This function allocates a memory chunk from system - * - * @param size bytes need allocate - * - * @return the pointer to memory allocated - */ -void* bh_malloc(unsigned int size); - -/** - * This function frees memory chunk - * - * @param ptr the pointer to memory need free - */ -void bh_free(void *ptr); - -#else - -void* bh_malloc_profile(const char *file, int line, const char *func, unsigned int size); -void bh_free_profile(const char *file, int line, const char *func, void *ptr); - -#define bh_malloc(size) bh_malloc_profile(__FILE__, __LINE__, __func__, size) -#define bh_free(ptr) bh_free_profile(__FILE__, __LINE__, __func__, ptr) - -/** - * Print current memory profiling data - * - * @param file file name of the caller - * @param line line of the file of the caller - * @param func function name of the caller - */ -void memory_profile_print(const char *file, int line, const char *func, int alloc); - -/** - * Summarize memory usage and print it out - * Can use awk to analyze the output like below: - * awk -F: '{print $2,$4,$6,$8,$9}' OFS="\t" ./out.txt | sort -n -r -k 1 - */ -void memory_usage_summarize(); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef _BH_MEMORY_H */ - diff --git a/core/shared/include/bh_queue.h b/core/shared/include/bh_queue.h deleted file mode 100644 index 92e9b014e0..0000000000 --- a/core/shared/include/bh_queue.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_QUEUE_H -#define _BH_QUEUE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bh_types.h" /*For bool type*/ -#include "bh_platform.h" - -struct _bh_queue_node; -typedef struct _bh_queue_node * bh_message_t; -struct bh_queue; -typedef struct bh_queue bh_queue; - -typedef void (*bh_queue_handle_msg_callback)(void *message, void *arg); - -#define bh_queue_malloc bh_malloc -#define bh_queue_free bh_free - -#define bh_queue_mutex korp_mutex -#define bh_queue_sem korp_sem -#define bh_queue_cond korp_cond - -#define bh_queue_mutex_init vm_mutex_init -#define bh_queue_mutex_destroy vm_mutex_destroy -#define bh_queue_mutex_lock vm_mutex_lock -#define bh_queue_mutex_unlock vm_mutex_unlock - -#define bh_queue_sem_init vm_sem_init -#define bh_queue_sem_destroy vm_sem_destroy -#define bh_queue_sem_wait vm_sem_wait -#define bh_queue_sem_reltimedwait vm_sem_reltimedwait -#define bh_queue_sem_post vm_sem_post - -#define bh_queue_cond_init vm_cond_init -#define bh_queue_cond_destroy vm_cond_destroy -#define bh_queue_cond_wait vm_cond_wait -#define bh_queue_cond_timedwait vm_cond_reltimedwait -#define bh_queue_cond_signal vm_cond_signal -#define bh_queue_cond_broadcast vm_cond_broadcast - -typedef void (*bh_msg_cleaner)(void *msg); - -bh_queue * -bh_queue_create(); - -void -bh_queue_destroy(bh_queue *queue); - -char * bh_message_payload(bh_message_t message); -uint32 bh_message_payload_len(bh_message_t message); -int bh_message_type(bh_message_t message); - -bh_message_t bh_new_msg(unsigned short tag, void *body, unsigned int len, - void * handler); -void bh_free_msg(bh_message_t msg); -bool bh_post_msg(bh_queue *queue, unsigned short tag, void *body, - unsigned int len); -bool bh_post_msg2(bh_queue *queue, bh_message_t msg); - -bh_message_t bh_get_msg(bh_queue *queue, int timeout); - -unsigned -bh_queue_get_message_count(bh_queue *queue); - -void -bh_queue_enter_loop_run(bh_queue *queue, - bh_queue_handle_msg_callback handle_cb, - void *arg); -void -bh_queue_exit_loop_run(bh_queue *queue); - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef _BH_QUEUE_H */ - diff --git a/core/shared/include/bni.h b/core/shared/include/bni.h deleted file mode 100644 index 437cf41d75..0000000000 --- a/core/shared/include/bni.h +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -/** - * @file bni.h - * @date Mon Jul 2 16:54:58 2012 - * - * @brief Beihai native interface. - */ - -#ifndef BNI_H -#define BNI_H - -#include "bh_types.h" - -/* Primitive types */ -typedef uint8 jboolean; -typedef int8 jbyte; -typedef uint16 jchar; -typedef int16 jshort; -typedef int32 jint; -typedef int64 jlong; -typedef float jfloat; -typedef double jdouble; -typedef jint jsize; - -/* Predefined Java class types. */ -struct _jobject; -typedef struct _jobject *jobject; -struct _jclass; -typedef struct _jclass *jclass; -struct _jstring; -typedef struct _jstring *jstring; - -/* Values of jboolean: */ -#define BNI_FALSE 0 -#define BNI_TRUE 1 - -/** - * Return the length of the array object. - * - * @param array Java array object - * - * @return the length of the Java array - */ -#define bni_array_length(array) ((jsize)((uint32)(array)->__length >> 2)) - -/** - * Return the address of the first element of array object. - * - * @param array Java array object - * - * @return the address of the first element of array object - */ -#define bni_array_elem(array) ((array)->__elem) - -/** - * Find the Java class with given class name. - * - * @param name Java class name - * - * @return class object of the Java class if found, NULL otherwise - * - * @throws OutOfMemoryError if VM runs out of memory. - */ -jclass -bni_find_class(const char *name); - -/** - * Throw an exception of given class with message. - * - * @param clazz class object of a subclass of java.lang.Throwable - * @param msg message for the exception or NULL if no message - * - * @return 0 if succeeds, nonzero otherwise - */ -jint -bni_throw_new(jclass clazz, const char *msg); - -/** - * Throw a NullPointerException. - * - * @throws NullPointerException - */ -void -bni_throw_npe(void); - -/** - * Throw an ArrayIndexOutOfBoundsException - * - * @param index the index used to access the array - * - * @throws ArrayIndexOutOfBoundsException - */ -void -bni_throw_aioobe(int index); - -/** - * Determine whether an exception is being thrown. - * - * @return exception object if exception is thrown, NULL otherwise - */ -jobject -bni_exception_occurred(void); - -/** - * Print the current exception to error-reporting channel. - */ -void -bni_exception_describe(void); - -/** - * Clear the currently thrown exception. - */ -void -bni_exception_clear(void); - -/** - * Return the Unicode character number of a string. - * - * @param str Java string object - * - * @return the Unicode character number of the string - */ -jsize -bni_string_length(jstring str); - -/** - * Return the length in bytes of the modified UTF-8 representation of - * a string. - * - * @param str Java string object - * @param start start offset in the string - * @param len number of Unicode characters - * - * @return the UTF-8 length of the string - * - * @throws StringIndexOutOfBoundsException on index overflow. - */ -jsize -bni_string_utf_length(jstring str, jsize start, jsize len); - -/** - * Copies len number of Unicode characters beginning at offset start - * to the given buffer buf. - * - * @param str Java string object - * @param start start offset in the string - * @param len number of Unicode characters to copy - * @param buf buffer for storing the result - */ -void -bni_string_region(jstring str, jsize start, jsize len, jchar *buf); - -/** - * Translates len number of Unicode characters beginning at offset - * start into modified UTF-8 encoding and place the result in the - * given buffer buf. - * - * @param str Java string object - * @param start start offset in the string - * @param len number of Unicode characters to copy - * @param buf buffer for storing the result - * - * @throws StringIndexOutOfBoundsException on index overflow. - */ -void -bni_string_utf_region(jstring str, jsize start, jsize len, char *buf); - -/** - * Translate Unicode characters into modified UTF-8 encoding and return - * the result. - * - * @param str Java string object - * - * @return the UTF-8 encoding string if succeeds, NULL otherwise - */ -char * -bni_string_get_utf_chars(jstring str); - -/** - * Get the given Java object's class index. - * - * @param obj Java object - * - * @return -1 if obj is an array, class index of the object otherwise - */ -jint -bni_object_class_index(jobject obj); - -/** - * Allocate memory from the current instance's private heap. - * - * @param size bytes to allocate - * - * @return pointer to the allocated memory - * - * @throws OutOfMemoryError if VM runs out of memory. - */ -void* -bni_malloc(unsigned size); - -/** - * Allocate memory from the current instance's private heap and clear - * to zero. - * - * @param size bytes to allocate - * - * @return pointer to the allocated memory - * - * @throws OutOfMemoryError if VM runs out of memory. - */ -void* -bni_calloc(unsigned size); - -/** - * Free the memory allocated from the current instance's private heap. - * - * @param ptr pointer to the memory in current instance's private heap - */ -void -bni_free(void *ptr); - -#endif diff --git a/core/shared/include/config.h b/core/shared/include/config.h deleted file mode 100644 index 6ed9dc27f2..0000000000 --- a/core/shared/include/config.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _CONFIG_H_ -#define _CONFIG_H_ - -#if !defined(BUILD_TARGET_X86_64) \ - && !defined(BUILD_TARGET_AMD_64) \ - && !defined(BUILD_TARGET_X86_32) \ - && !defined(BUILD_TARGET_ARM) \ - && !defined(BUILD_TARGET_ARM_VFP) \ - && !defined(BUILD_TARGET_THUMB) \ - && !defined(BUILD_TARGET_THUMB_VFP) \ - && !defined(BUILD_TARGET_MIPS) \ - && !defined(BUILD_TARGET_XTENSA) -#if defined(__x86_64__) || defined(__x86_64) -#define BUILD_TARGET_X86_64 -#elif defined(__amd64__) || defined(__amd64) -#define BUILD_TARGET_AMD_64 -#elif defined(__i386__) || defined(__i386) || defined(i386) -#define BUILD_TARGET_X86_32 -#elif defined(__thumb__) -#define BUILD_TARGET_THUMB -#define BUILD_TARGET "THUMBV4T" -#elif defined(__arm__) -#define BUILD_TARGET_ARM -#define BUILD_TARGET "ARMV4T" -#elif defined(__mips__) || defined(__mips) || defined(mips) -#define BUILD_TARGET_MIPS -#elif defined(__XTENSA__) -#define BUILD_TARGET_XTENSA -#else -#error "Build target isn't set" -#endif -#endif - -enum { - /* Memory allocator ems */ - MEM_ALLOCATOR_EMS = 0, - /* Memory allocator tlsf */ - MEM_ALLOCATOR_TLSF -}; - -/* Default memory allocator */ -#define DEFAULT_MEM_ALLOCATOR MEM_ALLOCATOR_EMS - -#ifndef WASM_ENABLE_INTERP -#define WASM_ENABLE_INTERP 0 -#endif - -#ifndef WASM_ENABLE_AOT -#define WASM_ENABLE_AOT 0 -#endif - -#ifndef WASM_ENABLE_JIT -#define WASM_ENABLE_JIT 0 -#endif - -#if (WASM_ENABLE_AOT == 0) && (WASM_ENABLE_JIT != 0) -/* JIT can only be enabled when AOT is enabled */ -#undef WASM_ENABLE_JIT -#define WASM_ENABLE_JIT 0 -#endif - -#ifndef WASM_ENABLE_WAMR_COMPILER -#define WASM_ENABLE_WAMR_COMPILER 0 -#endif - -#ifndef WASM_ENABLE_LIBC_BUILTIN -#define WASM_ENABLE_LIBC_BUILTIN 0 -#endif - -#ifndef WASM_ENABLE_LIBC_WASI -#define WASM_ENABLE_LIBC_WASI 0 -#endif - -#ifndef WASM_ENABLE_BASE_LIB -#define WASM_ENABLE_BASE_LIB 0 -#endif - -/* WASM log system */ -#ifndef WASM_ENABLE_LOG -#define WASM_ENABLE_LOG 1 -#endif - -#if defined(BUILD_TARGET_X86_32) || defined(BUILD_TARGET_X86_64) -#define WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS 1 -#else -#define WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS 0 -#endif - -/* WASM Interpreter labels-as-values feature */ -#define WASM_ENABLE_LABELS_AS_VALUES 1 - -/* Heap and stack profiling */ -#define BEIHAI_ENABLE_MEMORY_PROFILING 0 - -/* Max app number of all modules */ -#define MAX_APP_INSTALLATIONS 3 - -/* Default timer number in one app */ -#define DEFAULT_TIMERS_PER_APP 20 - -/* Max timer number in one app */ -#define MAX_TIMERS_PER_APP 30 - -/* Max connection number in one app */ -#define MAX_CONNECTION_PER_APP 20 - -/* Max resource registration number in one app */ -#define RESOURCE_REGISTRATION_NUM_MAX 16 - -/* Max length of resource/event url */ -#define RESOUCE_EVENT_URL_LEN_MAX 256 - -/* Default length of queue */ -#define DEFAULT_QUEUE_LENGTH 50 - -/* Default watchdog interval in ms */ -#define DEFAULT_WATCHDOG_INTERVAL (3 * 60 * 1000) - -/* Support memory.grow opcode and enlargeMemory function */ -#define WASM_ENABLE_MEMORY_GROW 1 - -/* The max percentage of global heap that app memory space can grow */ -#define APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT 1 / 3 - -/* Default base offset of app heap space */ -#define DEFAULT_APP_HEAP_BASE_OFFSET (1 * BH_GB) - -/* Default min/max heap size of each app */ -#define APP_HEAP_SIZE_DEFAULT (8 * 1024) -#define APP_HEAP_SIZE_MIN (2 * 1024) -#define APP_HEAP_SIZE_MAX (1024 * 1024) - -/* Default wasm stack size of each app */ -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) -#define DEFAULT_WASM_STACK_SIZE (16 * 1024) -#else -#define DEFAULT_WASM_STACK_SIZE (12 * 1024) -#endif - -/* Default/min/max stack size of each app thread */ -#if !defined(BH_PLATFORM_ZEPHYR) && !defined(BH_PLATFORM_ALIOS_THINGS) -#define APP_THREAD_STACK_SIZE_DEFAULT (20 * 1024) -#define APP_THREAD_STACK_SIZE_MIN (16 * 1024) -#define APP_THREAD_STACK_SIZE_MAX (256 * 1024) -#else -#define APP_THREAD_STACK_SIZE_DEFAULT (4 * 1024) -#define APP_THREAD_STACK_SIZE_MIN (2 * 1024) -#define APP_THREAD_STACK_SIZE_MAX (256 * 1024) -#endif - -#endif /* end of _CONFIG_H_ */ - diff --git a/core/shared/include/jeff_export.h b/core/shared/include/jeff_export.h deleted file mode 100644 index e9f4128dca..0000000000 --- a/core/shared/include/jeff_export.h +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -/** - * @file jeff-export.h - * @date Wed Aug 3 18:17:30 2011 - * - * @brief Exported interface for operating or executing JEFF files. - * All interface names start with "jeff_", which is the namespace name - * of this module. - */ - -#ifndef JEFF_EXPORT_H -#define JEFF_EXPORT_H - -#include "bni.h" -#include "bh_types.h" - -/******************************************************************** - * Exported internal types - ********************************************************************/ - -/** - * JEFF file handle type - */ -struct JeffFileHeaderLinked; -typedef struct JeffFileHeaderLinked *jeff_file_t; - -/** - * JEFF class type - */ -struct JeffClassHeaderLinked; -typedef struct JeffClassHeaderLinked *jeff_class_t; - -/** - * VM instance handle type - */ -struct JeffInstanceLocalRoot; -typedef struct JeffInstanceLocalRoot *jeff_instance_t; - -/** - * Record of one native method's definition. - */ -struct JeffNativeMethodDef { - /* Mangled name of the native method. NULL for initialization - functions. */ - const char *mangled_name; - - /* Points to the native C function. */ - void (*func_ptr)(uint32 *); - - /* Return size type of the native function. */ - uint32 return_size_type; -}; - -/******************************************************************** - * Interface for operating global environment of the JEFF VM - ********************************************************************/ - -/** - * Load the core library from the given file buffer and initialize the - * runtime environment (global objects etc.) of the VM. The thread - * calls this function becomes the supervisor thread, which belongs to - * a unique supervisor instance. Currently, if this init failed, - * partially initialized states of the VM runtime environment won't be - * cleaned up, so the VM must be shutdown and restarted. method_defs - * points to an array of native method definition records. - * Initialization functions must be in the front of the array and - * following native method definitions must be sorted by their mangled - * names. - * - * @param file the JEFF file of the core library - * @param file_size the size of the JEFF file of the core library - * @param method_defs native method definition records - * @param method_defs_num number of native method records - * @param heap the heap for the current (supervisor) instance - * - * @return true if succeeds, otherwise the error cannot be recovered - */ -bool -jeff_runtime_init(jeff_file_t file, unsigned file_size, - struct JeffNativeMethodDef *method_defs, unsigned method_defs_num, - void *heap); - -/** - * Load a JEFF file into the VM from the given file buffer. It can be - * called from any VM thread. - * - * @param file the JEFF file to be loaded - * @param size the size of the JEFF file - * @param is_library whether the JEFF file is a library - * @param allow_to_load a function that returns true if classes in the - * given package is allowed to be loaded. The NULL function pointer - * allows all packages. - * @param allow_to_link a function that returns true if classes in the - * given package is allowed to be linked to. The NULL function - * pointer allows all packages. - * - * @return true if succeeds, otherwise detailed error information is - * passed to vmci_diagnostic_print. The caller can catch it by - * implementing that function. - */ -bool -jeff_runtime_load(jeff_file_t file, unsigned size, bool is_library, - bool (*allow_to_load)(const uint8 *pname, unsigned len), - bool (*allow_to_link)(const uint8 *pname, unsigned plen, - const uint8 *cname, unsigned clen)); - -/** - * Unload a JEFF file from the VM. All resources related to the JEFF - * file except the JEFF file itself are released. It can be called - * from any VM thread. - * - * @param file the JEFF file to be unloaded - * - * @return true if succeeds, otherwise detailed error information is - * passed to vmci_diagnostic_print. The caller can catch it by - * implementing that function. - */ -bool -jeff_runtime_unload(jeff_file_t file); - -/** - * Return the JEFF file with the given file uid. - * - * @param fuid the unique id of a loaded JEFF file - * - * @return the JEFF file is exists, otherwise NULL - */ -jeff_file_t -jeff_runtime_fuid_to_file(unsigned fuid); - -/** - * Return the file uid of the given JEFF file. - * - * @param file a loaded JEFF file - * - * @return the unique id of the given JEFF file - */ -unsigned -jeff_runtime_file_to_fuid(jeff_file_t file); - -/** - * Create a supervisor thread belonging to the supervisor instance. - * Threads that may interact with VM core must be either the main - * thread of supervisor instance (which calls jeff_runtime_init) or - * created by this function so that VM core required data structures - * can be set up correctly. - * - * @param start_routine the start routine of the new thread - * @param arg argument to the start routine - * - * @return true if succeeds, false otherwise - */ -bool -jeff_runtime_create_supervisor_thread(void* (*start_routine)(void *), - void *arg); - -/** - * Create a supervisor thread belonging to the supervisor instance. - * Threads that may interact with VM core must be either the main - * thread of supervisor instance (which calls jeff_runtime_init) or - * created by this function so that VM core required data structures - * can be set up correctly. - * - * @param start_routine the start routine of the new thread - * @param arg argument to the start routine - * @param prio thread priority - * - * @return true if succeeds, false otherwise - */ -bool -jeff_runtime_create_supervisor_thread_with_prio(void* (*start_routine)(void *), - void *arg, int prio); - -/******************************************************************** - * Interface for operating instance local environment - ********************************************************************/ - -/** - * Create a VM instance with the given JEFF file as its main file, - * (i.e. the file containing the main class of the VM instance). This - * function can be called from any VM thread, but it must be isolated - * from JEFF file's unloading operation so that the main file won't be - * unloaded before it's locked by the new instance. All instance - * local memory except stacks of threads are allocated from the given - * heap. If succeeds, it increases reference count of the main_file - * and returns the handle of the new VM instance. The new instance's - * main thread will run the start_routine with argument arg. If the - * cleanup_routine is not NULL, it will be called after start_routine - * returns and just before the main thread exits. It will also be - * called after the instance is destroied. It is guaranteed to be - * called exactly once no matter how the instance terminates. - * - * @param main_file the main JEFF file of the new instance - * @param heap the private heap of the new instance - * @param stack_depth the maximal nesting levels of Java methods of - * the new instance. It must be <= 16 * 1024. Otherwise the instance - * creation will fail. - * @param start_routine start routine of the main thread. Don't - * destroy the heap or inform other thread to do this at the end of - * this routine since after it returns, VM core will call destroy - * functions on objects allocated in this heap (e.g. locks and - * condition variables). Do the destroying or informing of destroying - * in the cleanup_routine. - * @param arg the instance argument that will be passed to the start - * routine. It can be get or set by jeff_runtime_get_instance_arg and - * jeff_runtime_set_instance arg from threads of the instance. The - * caller can use it to store instance local data. - * @param cleanup_routine the optional cleanup routine for the - * instance, which may be NULL. It may be executed in the end of the - * main thread of the created instance by this function if this - * instance exits normally, or it may be executed in a thread of other - * instance in case this instance is being killed by that instance. - * In both cases, this routine regards it is executed in a thread of - * this instance (the instance created by this function) because - * jeff_runtime_get_instance_arg will always return the argument of - * this instance. - * - * @return the VM instance handle if succeeds, NULL otherwise - */ -jeff_instance_t -jeff_runtime_create_instance(jeff_file_t main_file, void *heap, - unsigned stack_depth, void* (*start_routine)(void *), void *arg, - void (*cleanup_routine)(void)); - -/** - * Destroy the given VM instance and decrease the reference count of - * its main file and all explicitly used JEFF files. It can be called - * from any VM thread. If there are alive threads of the instance, - * they will be terminated mandatorily and then the cleanup routine is - * called if it's not NULL. - * - * @param handle the handle of the instance to be destroyed - */ -void -jeff_runtime_destroy_instance(jeff_instance_t handle); - -/** - * Retrieve the current instance's argument. - * - * @return the current instance's argument - */ -void* -jeff_runtime_get_instance_arg(void); - -/** - * Set the current instance's argument. - * - * @return the new argument for the current instance - */ -void -jeff_runtime_set_instance_arg(void *arg); - -/** - * Retrieve the current instance's heap. - * - * @return the current instance's heap - */ -void* -jeff_runtime_get_instance_heap(void); - -/** - * Suspend all threads of the given VM instance. This function can - * only be called from thread that is not of the given VM instance. - * - * @param handle the handle of the instance to be suspended - */ -void -jeff_runtime_suspend_instance(jeff_instance_t handle); - -/** - * Resume all threads of the given VM instance. This function can - * only be called from thread that is not of the given VM instance. - * - * @param handle the handle of the instance to be resumed - */ -void -jeff_runtime_resume_instance(jeff_instance_t handle); - -/** - * Interrupt all threads of the given VM instance. This function can - * only be called from thread that is not of the given VM instance. - * - * @param handle the handle of the instance to be interrupted - * @param by_force whether the interruption is by force - */ -void -jeff_runtime_interrupt_instance(jeff_instance_t handle, bool by_force); - -/** - * Wait for the given VM instance to terminate. - * - * @param ilr the VM instance to be waited for - * @param mills wait millseconds to return - */ -void -jeff_runtime_wait_for_instance(jeff_instance_t ilr, int mills); - -/******************************************************************** - * Interface for operating thread local environment - ********************************************************************/ - -/** - * Return true if there is an uncaught exception (thrown during - * running an application or applet command). - * - * @return true if there is an uncaught exception - */ -bool -jeff_runtime_check_uncaught_exception(void); - -/** - * Print qualified name of the uncaught exception (and stack trace if - * enabled) by calling vmci_diagnostic_print. - */ -void -jeff_runtime_print_uncaught_exception(void); - -/** - * Clear the uncaught exception. - */ -void -jeff_runtime_reset_uncaught_exception(void); - -/** - * Change current thread to a safe state (VMWAIT). After calling this - * and before calling jeff_runtime_exit_safe_state, all operations - * must be safe, i.e. no GC or system level resource operations are - * allowed because in a safe state, the VM instance is assumed to be - * able to perform GC, JDWP or termination at any time. Usually, this - * function is called just before the native code is going to wait for - * something and the exiting safe state function is called just after - * the waiting returns. - */ -void -jeff_runtime_enter_safe_state(void); - -/** - * Change current thread to an unsafe state (RUNNING) so that unsafe - * operations can also be done. - */ -void -jeff_runtime_exit_safe_state(void); - -/** - * Set thread local error code for the current thread. - * - * @param code the error code to be set - */ -void -jeff_runtime_set_error(unsigned code); - -/** - * Get the last error code of current thread. - * - * @return the last error code of current thread - */ -unsigned -jeff_runtime_get_error(void); - -/******************************************************************** - * Interface for GC support - ********************************************************************/ - -/** - * Traverse all objects of the given heap that are global or locate in - * threads' frames and return them by calling vmci_gc_rootset_elem. - * This function will suspend all threads except the current one of - * the VM instance owning the given heap before traversing. It - * traverses either all or none of the rootset objects, and returns - * true and false respectively. If it returns false, the GC process - * shouldn't proceed and is not necessary to unmark anything because - * no objects are marked. The function jeff_runtime_gc_finished must - * be called if and only if this function returns true so as to resume - * threads that are suspended during GC process. - * - * @param heap the heap for which rootset objects are looked up - * - * @return true if succeeds, false otherwise - */ -bool -jeff_runtime_traverse_gc_rootset(void *heap); - -/** - * Get the reference offset table of the given object. If the - * returned value R >= 0, *ret points to the reference offset table of - * the object and R is the number of offsets in the table. Otherwise, - * if the returned value R < 0, all reference fields of the object - * must be in a continuous region (usually the object is an array), - * then *ret is the offset to the first field in the region and R is - * the number of such fields in the region. - * - * @param obj pointer to the Java object - * @param ret points to a pointer for storing the reference offset - * table if return value >= 0, or for storing the offset to the first - * object reference in the Java object if return value < 0 - * - * @return number of offsets in the reference_offset table if >= 0, or - * number of object references in the object if < 0 - */ -int -jeff_object_get_reference_offsets(const jobject obj, uint16 **ret); - -/** - * Inform the containing VM instance that GC has finished and all - * suspended threads can be resumed. This function must be called if - * and only if jeff_runtime_traverse_gc_rootset returns true. - */ -void -jeff_runtime_gc_finished(void); - -/******************************************************************** - * Interface for tooling support - ********************************************************************/ - -/** - * This function is used to suspend the main thread of VM instance so - * that debugger can have chance to connect to the VM instance, set - * breakpoints and do any other debug settings. It must be called - * from the main thread of VM instance at the point just after VM - * instance initialization finishes and just before application code - * is to be executed. - */ -void -jeff_tool_suspend_self(void); - -/** - * Start up tool agent thread for the given VM instance. It can be - * called from any VM thread. - * - * @param handle the VM instance for which tool agent is started up - * @param queue queue of the tool agent - * @return true if succeeds, false otherwise - */ -bool -jeff_tool_start_agent(jeff_instance_t handle, void *queue); - -/******************************************************************** - * Interface for toolkit support - ********************************************************************/ - -/** - * Return the JEFF class pointer of the given class name. - * - * @param class_name the qualified class name - * - * @return the JEFF class pointer - */ -jeff_class_t -jeff_tool_get_jeff_class(const char *class_name); - -/** - * Get the mangled class name of the given class. - * - * @param clz the JEFF class - * @param buf buffer for returning the mangled name - * @param buf_size size of the buffer - * - * @return actual size of the mangled class name including the - * terminating null byte - */ -unsigned -jeff_tool_get_mangled_class_name(jeff_class_t clz, char *buf, - unsigned buf_size); - -/** - * Get class index of given class in its containing JEFF file. - * - * @param clz the JEFF class - * - * @return class index in the containing JEFF file - */ -int -jeff_tool_get_class_index(jeff_class_t clz); - -/** - * Callback handler prototype for traversing fields of class. - * - * @param arg argument passed to the handler from caller - * @param access_flag access flag of the method - * @param name the field name - * @param descriptor mangled field type descriptor - * @param offset the offset of the field in the class - * @param size size of the field - */ -typedef void -(*JeffToolFieldHandler)(void *arg, unsigned access_flag, const char *name, - const char *descriptor, unsigned offset, unsigned size); - -/** - * Traverse all fields of the given class, including those inherited - * from super classes. The fields are traversed in the same order as - * the field layout of the class. - * - * @param arg argument to be passed to the handler - * @param clz the JEFF class - * @param instance instance fields or static fielts - * @param handler the callback handler for each field - */ -void -jeff_tool_foreach_field(void *arg, jeff_class_t clz, bool instance, - JeffToolFieldHandler handler); - -/** - * Callback handler prototype for traversing methods of class. - * - * @param arg argument passed to the handler from caller - * @param access_flag access flag of the method - * @param name mangled name of the method - * @param descriptor mangled method arguments descriptor - * @param retune_type mangled descriptor of method's return type - */ -typedef void -(*JeffToolMethodHandler)(void *arg, unsigned access_flag, const char *name, - const char *descriptor, const char *return_type); - -/** - * Traverse all methods of the given class. - * - * @param arg argument to be passed to the handler - * @param clz the JEFF class - * @param handler the callback handler for each method - */ -void -jeff_tool_foreach_method(void *arg, jeff_class_t clz, - JeffToolMethodHandler handler); - -/** - * Callback handler prototype for traversing classes of main file. - * - * @param arg argument passed to the handler from caller - * @param clz pointer to one class in the main file - */ -typedef void -(*JeffToolClassHandler)(void *arg, jeff_class_t clz); - -/** - * Traverse all classes of the main file. - * - * @param arg argument to be passed to the handler - * @param handler the callback handler for each class - */ -void -jeff_tool_foreach_class(void *arg, JeffToolClassHandler handler); - -/******************************************************************** - * Interface for executing applications - ********************************************************************/ - -/** - * Initialize global environment for executing Java applications. - * - * @return true if succeeds, false otherwise - */ -bool -jeff_application_env_init(void); - -/** - * Find the unique class containing a public static "main - * ([Ljava.lang.String;)V" method from the main JEFF file of the - * current instance and execute that method. - * - * @param argc the number of arguments - * @param argv the arguments array - * - * @return true if the main method is called, false otherwise (e.g. an - * exception occurs when preparing the arguments Java string array) - */ -bool -jeff_application_execute(int argc, char *argv[]); - -/******************************************************************** - * Interface for executing applets - ********************************************************************/ - -/** - * Initialize global environment for executing applets. - * - * @return true if succeeds, false otherwise - */ -bool -jeff_applet_env_init(void); - -/** - * Start to run from com.intel.runtime.core.RuntimeContext.main with a - * default message queue size and a default service class object. If - * the main JEFF file of the current VM instance contains exactly one - * class that is derived from com.intel.util.IntelApplet, then use it - * as the default service class. - * - * @param queue_size the default main message queue size - * @param default_service_class qualified class name of the default - * service class (entry point class), which must be in the main JEFF - * file. If NULL, find the default main class with rules described - * above. - * - * @return true if succeeds, false otherwise - */ -bool -jeff_applet_start(int queue_size, const char *default_service_class); - -#endif diff --git a/core/shared/include/mem_alloc.h b/core/shared/include/mem_alloc.h deleted file mode 100644 index 3e1cf95777..0000000000 --- a/core/shared/include/mem_alloc.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef __MEM_ALLOC_H -#define __MEM_ALLOC_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void *mem_allocator_t; - -mem_allocator_t -mem_allocator_create(void *mem, uint32_t size); - -void -mem_allocator_destroy(mem_allocator_t allocator); - -void * -mem_allocator_malloc(mem_allocator_t allocator, uint32_t size); - -void -mem_allocator_free(mem_allocator_t allocator, void *ptr); - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef __MEM_ALLOC_H */ - diff --git a/core/shared/mem-alloc/SConscript b/core/shared/mem-alloc/SConscript new file mode 100644 index 0000000000..602d871583 --- /dev/null +++ b/core/shared/mem-alloc/SConscript @@ -0,0 +1,33 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import os + +cwd = GetCurrentDir() + +src = Split(''' +''') + + +def addSrcFiles(arr, path): + for f in os.listdir(path): + fpath = os.path.join(path, f); + if os.path.isfile(fpath): + ext = os.path.splitext(fpath)[-1] + if ext == '.c' or ext == '.cpp': + arr += [fpath] + elif os.path.isdir(fpath): + addSrcFiles(arr, fpath) + + + +addSrcFiles(src, cwd); +CPPPATH = [cwd, cwd+'/../include'] + +group = DefineGroup('iwasm_platform_core', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/shared/mem-alloc/bh_memory.c b/core/shared/mem-alloc/bh_memory.c deleted file mode 100644 index 236bf4faaa..0000000000 --- a/core/shared/mem-alloc/bh_memory.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_config.h" -#include "bh_platform.h" -#include "bh_memory.h" -#include "mem_alloc.h" -#include - -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 -#include "bh_thread.h" - -/* Memory profile data of a function */ -typedef struct memory_profile { - struct memory_profile *next; - const char *function_name; - const char *file_name; - int line_in_file; - int malloc_num; - int free_num; - int total_malloc; - int total_free; -} memory_profile_t; - -/* Memory in use which grows when bh_malloc was called - * and decreases when bh_free was called */ -static unsigned int memory_in_use = 0; - -/* Memory profile data list */ -static memory_profile_t *memory_profiles_list = NULL; - -/* Lock of the memory profile list */ -static korp_mutex profile_lock; -#endif - -#ifndef MALLOC_MEMORY_FROM_SYSTEM - -typedef enum Memory_Mode { - MEMORY_MODE_UNKNOWN = 0, - MEMORY_MODE_POOL, - MEMORY_MODE_ALLOCATOR -} Memory_Mode; - -static Memory_Mode memory_mode = MEMORY_MODE_UNKNOWN; - -static mem_allocator_t pool_allocator = NULL; - -static void *(*malloc_func)(unsigned int size) = NULL; -static void (*free_func)(void *ptr) = NULL; - -static unsigned int global_pool_size; - -int bh_memory_init_with_pool(void *mem, unsigned int bytes) -{ - mem_allocator_t _allocator = mem_allocator_create(mem, bytes); - - if (_allocator) { - memory_mode = MEMORY_MODE_POOL; - pool_allocator = _allocator; -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 - vm_mutex_init(&profile_lock); -#endif - global_pool_size = bytes; - return 0; - } - bh_printf("Init memory with pool (%p, %u) failed.\n", mem, bytes); - return -1; -} - -int bh_memory_init_with_allocator(void *_malloc_func, void *_free_func) -{ - if (_malloc_func && _free_func && _malloc_func != _free_func) { - memory_mode = MEMORY_MODE_ALLOCATOR; - malloc_func = _malloc_func; - free_func = _free_func; -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 - vm_mutex_init(&profile_lock); -#endif - return 0; - } - bh_printf("Init memory with allocator (%p, %p) failed.\n", _malloc_func, - _free_func); - return -1; -} - -void bh_memory_destroy() -{ -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 - vm_mutex_destroy(&profile_lock); -#endif - if (memory_mode == MEMORY_MODE_POOL) - mem_allocator_destroy(pool_allocator); - memory_mode = MEMORY_MODE_UNKNOWN; -} - -unsigned bh_memory_pool_size() -{ - if (memory_mode == MEMORY_MODE_POOL) - return global_pool_size; - else - return 1 * BH_GB; -} - -void* bh_malloc_internal(unsigned int size) -{ - if (memory_mode == MEMORY_MODE_UNKNOWN) { - bh_printf("bh_malloc failed: memory hasn't been initialize.\n"); - return NULL; - } else if (memory_mode == MEMORY_MODE_POOL) { - return mem_allocator_malloc(pool_allocator, size); - } else { - return malloc_func(size); - } -} - -void bh_free_internal(void *ptr) -{ - if (memory_mode == MEMORY_MODE_UNKNOWN) { - bh_printf("bh_free failed: memory hasn't been initialize.\n"); - } else if (memory_mode == MEMORY_MODE_POOL) { - mem_allocator_free(pool_allocator, ptr); - } else { - free_func(ptr); - } -} - -#if BEIHAI_ENABLE_MEMORY_PROFILING != 0 -void* bh_malloc_profile(const char *file, - int line, - const char *func, - unsigned int size) -{ - void *p = bh_malloc_internal(size + 8); - - if (p) { - memory_profile_t *profile; - - vm_mutex_lock(&profile_lock); - - profile = memory_profiles_list; - while (profile) { - if (strcmp(profile->function_name, func) == 0 - && strcmp(profile->file_name, file) == 0) { - break; - } - profile = profile->next; - } - - if (profile) { - profile->total_malloc += size;/* TODO: overflow check */ - profile->malloc_num++; - } else { - profile = bh_malloc_internal(sizeof(memory_profile_t)); - if (!profile) { - vm_mutex_unlock(&profile_lock); - bh_memcpy_s(p, size + 8, &size, sizeof(size)); - return (char *)p + 8; - } - - memset(profile, 0, sizeof(memory_profile_t)); - profile->file_name = file; - profile->line_in_file = line; - profile->function_name = func; - profile->malloc_num = 1; - profile->total_malloc = size; - profile->next = memory_profiles_list; - memory_profiles_list = profile; - } - - vm_mutex_unlock(&profile_lock); - - bh_memcpy_s(p, size + 8, &size, sizeof(size)); - memory_in_use += size; - - memory_profile_print(file, line, func, size); - - return (char *)p + 8; - } - - return NULL; -} - -void bh_free_profile(const char *file, int line, const char *func, void *ptr) -{ - unsigned int size = *(unsigned int *)((char *)ptr - 8); - memory_profile_t *profile; - - bh_free_internal((char *)ptr - 8); - - if (memory_in_use >= size) - memory_in_use -= size; - - vm_mutex_lock(&profile_lock); - - profile = memory_profiles_list; - while (profile) { - if (strcmp(profile->function_name, func) == 0 - && strcmp(profile->file_name, file) == 0) { - break; - } - profile = profile->next; - } - - if (profile) { - profile->total_free += size;/* TODO: overflow check */ - profile->free_num++; - } else { - profile = bh_malloc_internal(sizeof(memory_profile_t)); - if (!profile) { - vm_mutex_unlock(&profile_lock); - return; - } - - memset(profile, 0, sizeof(memory_profile_t)); - profile->file_name = file; - profile->line_in_file = line; - profile->function_name = func; - profile->free_num = 1; - profile->total_free = size; - profile->next = memory_profiles_list; - memory_profiles_list = profile; - } - - vm_mutex_unlock(&profile_lock); -} - -/** - * Summarize memory usage and print it out - * Can use awk to analyze the output like below: - * awk -F: '{print $2,$4,$6,$8,$9}' OFS="\t" ./out.txt | sort -n -r -k 1 - */ -void memory_usage_summarize() -{ - memory_profile_t *profile; - - vm_mutex_lock(&profile_lock); - - profile = memory_profiles_list; - while (profile) { - bh_printf("malloc:%d:malloc_num:%d:free:%d:free_num:%d:%s\n", - profile->total_malloc, - profile->malloc_num, - profile->total_free, - profile->free_num, - profile->function_name); - profile = profile->next; - } - - vm_mutex_unlock(&profile_lock); -} - -void memory_profile_print(const char *file, - int line, - const char *func, - int alloc) -{ - bh_printf("location:%s@%d:used:%d:contribution:%d\n", - func, line, memory_in_use, alloc); -} - -#else - -void* bh_malloc(unsigned int size) -{ - return bh_malloc_internal(size); -} - -void bh_free(void *ptr) -{ - bh_free_internal(ptr); -} -#endif - -#else /* else of MALLOC_MEMORY_FROM_SYSTEM */ - -#if BEIHAI_ENABLE_MEMORY_PROFILING == 0 - -void* bh_malloc(unsigned int size) -{ - return malloc(size); -} - -void bh_free(void *ptr) -{ - if (ptr) - free(ptr); -} - -#else /* else of BEIHAI_ENABLE_MEMORY_PROFILING */ - -void* bh_malloc_profile(const char *file, - int line, - const char *func, - unsigned int size) -{ - (void)file; - (void)line; - (void)func; - - (void)memory_profiles_list; - (void)profile_lock; - (void)memory_in_use; - - return malloc(size); -} - -void bh_free_profile(const char *file, int line, const char *func, void *ptr) -{ - (void)file; - (void)line; - (void)func; - - if (ptr) - free(ptr); -} -#endif /* end of BEIHAI_ENABLE_MEMORY_PROFILING */ -#endif /* end of MALLOC_MEMORY_FROM_SYSTEM*/ diff --git a/core/shared/mem-alloc/ems/ems_alloc.c b/core/shared/mem-alloc/ems/ems_alloc.c index 19e76ade5f..ed9b43526d 100644 --- a/core/shared/mem-alloc/ems/ems_alloc.c +++ b/core/shared/mem-alloc/ems/ems_alloc.c @@ -5,166 +5,300 @@ #include "ems_gc_internal.h" +#if WASM_ENABLE_GC != 0 +#define LOCK_HEAP(heap) \ + do { \ + if (!heap->is_doing_reclaim) \ + /* If the heap is doing reclaim, it must have been locked, \ + we should not lock the heap again. */ \ + os_mutex_lock(&heap->lock); \ + } while (0) +#define UNLOCK_HEAP(heap) \ + do { \ + if (!heap->is_doing_reclaim) \ + /* If the heap is doing reclaim, it must have been locked, \ + and will be unlocked after reclaim, we should not \ + unlock the heap again. */ \ + os_mutex_unlock(&heap->lock); \ + } while (0) +#else +#define LOCK_HEAP(heap) os_mutex_lock(&heap->lock) +#define UNLOCK_HEAP(heap) os_mutex_unlock(&heap->lock) +#endif -static int hmu_is_in_heap(gc_heap_t* heap, hmu_t* hmu) +static inline bool +hmu_is_in_heap(void *hmu, gc_uint8 *heap_base_addr, gc_uint8 *heap_end_addr) { - return heap && hmu && (gc_uint8*) hmu >= heap->base_addr - && (gc_uint8*) hmu < heap->base_addr + heap->current_size; + gc_uint8 *addr = (gc_uint8 *)hmu; + return (addr >= heap_base_addr && addr < heap_end_addr) ? true : false; } -/* Remove a node from the tree it belongs to*/ - -/* @p can not be NULL*/ -/* @p can not be the ROOT node*/ - -/* Node @p will be removed from the tree and left,right,parent pointers of node @p will be*/ -/* set to be NULL. Other fields will not be touched.*/ -/* The tree will be re-organized so that the order conditions are still satisified.*/ -BH_STATIC void remove_tree_node(hmu_tree_node_t *p) +/** + * Remove a node from the tree it belongs to + * + * @param p the node to remove, can not be NULL, can not be the ROOT node + * the node will be removed from the tree, and the left, right and + * parent pointers of the node @p will be set to be NULL. Other fields + * won't be touched. The tree will be re-organized so that the order + * conditions are still satisfied. + */ +static bool +remove_tree_node(gc_heap_t *heap, hmu_tree_node_t *p) { hmu_tree_node_t *q = NULL, **slot = NULL; +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + hmu_tree_node_t *root = heap->kfc_tree_root, *parent; + gc_uint8 *base_addr = heap->base_addr; + gc_uint8 *end_addr = base_addr + heap->current_size; +#endif bh_assert(p); - bh_assert(p->parent); /* @p can not be the ROOT node*/ - /* get the slot which holds pointer to node p*/ +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + parent = p->parent; + if (!parent || p == root /* p can not be the ROOT node */ + || !hmu_is_in_heap(p, base_addr, end_addr) + || (parent != root && !hmu_is_in_heap(parent, base_addr, end_addr))) { + goto fail; + } +#endif + + /* get the slot which holds pointer to node p */ if (p == p->parent->right) { - slot = &p->parent->right; - } else { - bh_assert(p == p->parent->left); /* @p should be a child of its parent*/ - slot = &p->parent->left; + /* Don't use `slot = &p->parent->right` to avoid compiler warning */ + slot = (hmu_tree_node_t **)((uint8 *)p->parent + + offsetof(hmu_tree_node_t, right)); + } + else if (p == p->parent->left) { + /* p should be a child of its parent */ + /* Don't use `slot = &p->parent->left` to avoid compiler warning */ + slot = (hmu_tree_node_t **)((uint8 *)p->parent + + offsetof(hmu_tree_node_t, left)); + } + else { + goto fail; } - /* algorithms used to remove node p*/ - /* case 1: if p has no left child, replace p with its right child*/ - /* case 2: if p has no right child, replace p with its left child*/ - /* case 3: otherwise, find p's predecessor, remove it from the tree and replace p with it.*/ - /* use predecessor can keep the left <= root < right condition.*/ + /** + * algorithms used to remove node p + * case 1: if p has no left child, replace p with its right child + * case 2: if p has no right child, replace p with its left child + * case 3: otherwise, find p's predecessor, remove it from the tree + * and replace p with it. + * use predecessor can keep the left <= root < right condition. + */ if (!p->left) { /* move right child up*/ *slot = p->right; - if (p->right) + if (p->right) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(p->right, base_addr, end_addr)) { + goto fail; + } +#endif p->right->parent = p->parent; + } p->left = p->right = p->parent = NULL; - return; + return true; } if (!p->right) { /* move left child up*/ *slot = p->left; - p->left->parent = p->parent; /* p->left can never be NULL.*/ +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(p->left, base_addr, end_addr)) { + goto fail; + } +#endif + /* p->left can never be NULL unless it is corrupted. */ + p->left->parent = p->parent; p->left = p->right = p->parent = NULL; - return; + return true; } /* both left & right exist, find p's predecessor at first*/ q = p->left; - while (q->right) +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(q, base_addr, end_addr)) { + goto fail; + } +#endif + while (q->right) { q = q->right; - remove_tree_node(q); /* remove from the tree*/ +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(q, base_addr, end_addr)) { + goto fail; + } +#endif + } + + /* remove from the tree*/ + if (!remove_tree_node(heap, q)) + return false; *slot = q; q->parent = p->parent; q->left = p->left; q->right = p->right; - if (q->left) + if (q->left) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(q->left, base_addr, end_addr)) { + goto fail; + } +#endif q->left->parent = q; - if (q->right) + } + if (q->right) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(q->right, base_addr, end_addr)) { + goto fail; + } +#endif q->right->parent = q; + } p->left = p->right = p->parent = NULL; + + return true; +fail: +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + heap->is_heap_corrupted = true; +#endif + return false; } -static void unlink_hmu(gc_heap_t *heap, hmu_t *hmu) +static bool +unlink_hmu(gc_heap_t *heap, hmu_t *hmu) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + gc_uint8 *base_addr, *end_addr; +#endif gc_size_t size; bh_assert(gci_is_heap_valid(heap)); - bh_assert( - hmu && (gc_uint8*) hmu >= heap->base_addr - && (gc_uint8*) hmu < heap->base_addr + heap->current_size); - bh_assert(hmu_get_ut(hmu) == HMU_FC); + bh_assert(hmu && (gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu < heap->base_addr + heap->current_size); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (hmu_get_ut(hmu) != HMU_FC) { + heap->is_heap_corrupted = true; + return false; + } +#endif + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + base_addr = heap->base_addr; + end_addr = base_addr + heap->current_size; +#endif size = hmu_get_size(hmu); if (HMU_IS_FC_NORMAL(size)) { uint32 node_idx = size >> 3; - hmu_normal_node_t* node = heap->kfc_normal_list[node_idx].next; - hmu_normal_node_t** p = &(heap->kfc_normal_list[node_idx].next); + hmu_normal_node_t *node_prev = NULL, *node_next; + hmu_normal_node_t *node = heap->kfc_normal_list[node_idx].next; + while (node) { - if ((hmu_t*) node == hmu) { - *p = node->next; +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(node, base_addr, end_addr)) { + heap->is_heap_corrupted = true; + return false; + } +#endif + node_next = get_hmu_normal_node_next(node); + if ((hmu_t *)node == hmu) { + if (!node_prev) /* list head */ + heap->kfc_normal_list[node_idx].next = node_next; + else + set_hmu_normal_node_next(node_prev, node_next); break; } - p = &(node->next); - node = node->next; + node_prev = node; + node = node_next; } if (!node) { - bh_printf("[GC_ERROR]couldn't find the node in the normal list"); + LOG_ERROR("[GC_ERROR]couldn't find the node in the normal list\n"); } - } else { - remove_tree_node((hmu_tree_node_t *) hmu); } + else { + if (!remove_tree_node(heap, (hmu_tree_node_t *)hmu)) + return false; + } + return true; } -static void hmu_set_free_size(hmu_t *hmu) +static void +hmu_set_free_size(hmu_t *hmu) { gc_size_t size; bh_assert(hmu && hmu_get_ut(hmu) == HMU_FC); size = hmu_get_size(hmu); - *((uint32*) ((char*) hmu + size) - 1) = size; + *((uint32 *)((char *)hmu + size) - 1) = size; } -/* Add free chunk back to KFC*/ - -/* @heap should not be NULL and it should be a valid heap*/ -/* @hmu should not be NULL and it should be a HMU of length @size inside @heap*/ -/* @hmu should be aligned to 8*/ -/* @size should be positive and multiple of 8*/ - -/* @hmu with size @size will be added into KFC as a new FC.*/ -void gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size) +/** + * Add free chunk back to KFC + * + * @param heap should not be NULL and it should be a valid heap + * @param hmu should not be NULL and it should be a HMU of length @size inside + * @heap hmu should be 8-bytes aligned + * @param size should be positive and multiple of 8 + * hmu with size @size will be added into KFC as a new FC. + */ +bool +gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + gc_uint8 *base_addr, *end_addr; +#endif hmu_normal_node_t *np = NULL; hmu_tree_node_t *root = NULL, *tp = NULL, *node = NULL; uint32 node_idx; bh_assert(gci_is_heap_valid(heap)); - bh_assert( - hmu && (gc_uint8*) hmu >= heap->base_addr - && (gc_uint8*) hmu < heap->base_addr + heap->current_size); + bh_assert(hmu && (gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu < heap->base_addr + heap->current_size); bh_assert(((gc_uint32)(uintptr_t)hmu_to_obj(hmu) & 7) == 0); - bh_assert( - size > 0 - && ((gc_uint8*) hmu) + size - <= heap->base_addr + heap->current_size); + bh_assert(size > 0 + && ((gc_uint8 *)hmu) + size + <= heap->base_addr + heap->current_size); bh_assert(!(size & 7)); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + base_addr = heap->base_addr; + end_addr = base_addr + heap->current_size; +#endif + hmu_set_ut(hmu, HMU_FC); hmu_set_size(hmu, size); hmu_set_free_size(hmu); if (HMU_IS_FC_NORMAL(size)) { - np = (hmu_normal_node_t*) hmu; + np = (hmu_normal_node_t *)hmu; +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(np, base_addr, end_addr)) { + heap->is_heap_corrupted = true; + return false; + } +#endif node_idx = size >> 3; - np->next = heap->kfc_normal_list[node_idx].next; + set_hmu_normal_node_next(np, heap->kfc_normal_list[node_idx].next); heap->kfc_normal_list[node_idx].next = np; - return; + return true; } - /* big block*/ - node = (hmu_tree_node_t*) hmu; + /* big block */ + node = (hmu_tree_node_t *)hmu; node->size = size; node->left = node->right = node->parent = NULL; - /* find proper node to link this new node to*/ - root = &heap->kfc_tree_root; + /* find proper node to link this new node to */ + root = heap->kfc_tree_root; tp = root; bh_assert(tp->size < size); while (1) { @@ -175,8 +309,8 @@ void gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size) break; } tp = tp->right; - } else /* tp->size >= size*/ - { + } + else { /* tp->size >= size */ if (!tp->left) { tp->left = node; node->parent = tp; @@ -184,29 +318,49 @@ void gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size) } tp = tp->left; } +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(tp, base_addr, end_addr)) { + heap->is_heap_corrupted = true; + return false; + } +#endif } + return true; } -/* Find a proper hmu for required memory size*/ - -/* @heap should not be NULL and it should be a valid heap*/ -/* @size should cover the header and it should be 8 bytes aligned*/ - -/* GC will not be performed here.*/ -/* Heap extension will not be performed here.*/ - -/* A proper HMU will be returned. This HMU can include the header and given size. The returned HMU will be aligned to 8 bytes.*/ -/* NULL will be returned if there are no proper HMU.*/ -BH_STATIC hmu_t *alloc_hmu(gc_heap_t *heap, gc_size_t size) +/** + * Find a proper hmu for required memory size + * + * @param heap should not be NULL and should be a valid heap + * @param size should cover the header and should be 8 bytes aligned + * GC will not be performed here. + * Heap extension will not be performed here. + * + * @return hmu allocated if success, which will be aligned to 8 bytes, + * NULL otherwise + */ +static hmu_t * +alloc_hmu(gc_heap_t *heap, gc_size_t size) { - hmu_normal_node_t *node = NULL, *p = NULL; + gc_uint8 *base_addr, *end_addr; + hmu_normal_list_t *normal_head = NULL; + hmu_normal_node_t *p = NULL; uint32 node_idx = 0, init_node_idx = 0; hmu_tree_node_t *root = NULL, *tp = NULL, *last_tp = NULL; hmu_t *next, *rest; + uintptr_t tp_ret; bh_assert(gci_is_heap_valid(heap)); bh_assert(size > 0 && !(size & 7)); +#if WASM_ENABLE_GC != 0 + /* In doing reclaim, gc must not alloc memory again. */ + bh_assert(!heap->is_doing_reclaim); +#endif + + base_addr = heap->base_addr; + end_addr = base_addr + heap->current_size; + if (size < GC_SMALLEST_SIZE) size = GC_SMALLEST_SIZE; @@ -215,53 +369,73 @@ BH_STATIC hmu_t *alloc_hmu(gc_heap_t *heap, gc_size_t size) /* find a non-empty slot in normal_node_list with good size*/ init_node_idx = (size >> 3); for (node_idx = init_node_idx; node_idx < HMU_NORMAL_NODE_CNT; - node_idx++) { - node = heap->kfc_normal_list + node_idx; - if (node->next) + node_idx++) { + normal_head = heap->kfc_normal_list + node_idx; + if (normal_head->next) break; - node = NULL; + normal_head = NULL; } - /* not found in normal list*/ - if (node) { + /* found in normal list*/ + if (normal_head) { bh_assert(node_idx >= init_node_idx); - p = node->next; - node->next = p->next; - bh_assert(((gc_int32)(uintptr_t)hmu_to_obj(p) & 7) == 0); + p = normal_head->next; +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(p, base_addr, end_addr)) { + heap->is_heap_corrupted = true; + return NULL; + } +#endif + normal_head->next = get_hmu_normal_node_next(p); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (((gc_int32)(uintptr_t)hmu_to_obj(p) & 7) != 0) { + heap->is_heap_corrupted = true; + return NULL; + } +#endif if ((gc_size_t)node_idx != (uint32)init_node_idx - && ((gc_size_t)node_idx << 3) >= size + GC_SMALLEST_SIZE) { /* with bigger size*/ - rest = (hmu_t*) (((char *) p) + size); - gci_add_fc(heap, rest, (node_idx << 3) - size); + /* with bigger size*/ + && ((gc_size_t)node_idx << 3) >= size + GC_SMALLEST_SIZE) { + rest = (hmu_t *)(((char *)p) + size); + if (!gci_add_fc(heap, rest, (node_idx << 3) - size)) { + return NULL; + } hmu_mark_pinuse(rest); - } else { + } + else { size = node_idx << 3; - next = (hmu_t*) ((char*) p + size); - if (hmu_is_in_heap(heap, next)) + next = (hmu_t *)((char *)p + size); + if (hmu_is_in_heap(next, base_addr, end_addr)) hmu_mark_pinuse(next); } -#if GC_STAT_DATA != 0 heap->total_free_size -= size; if ((heap->current_size - heap->total_free_size) - > heap->highmark_size) - heap->highmark_size = heap->current_size - - heap->total_free_size; -#endif + > heap->highmark_size) + heap->highmark_size = + heap->current_size - heap->total_free_size; - hmu_set_size((hmu_t* ) p, size); - return (hmu_t*) p; + hmu_set_size((hmu_t *)p, size); + return (hmu_t *)p; } } /* need to find a node in tree*/ - root = &heap->kfc_tree_root; + root = heap->kfc_tree_root; /* find the best node*/ bh_assert(root); tp = root->right; while (tp) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (!hmu_is_in_heap(tp, base_addr, end_addr)) { + heap->is_heap_corrupted = true; + return NULL; + } +#endif + if (tp->size < size) { tp = tp->right; continue; @@ -278,173 +452,537 @@ BH_STATIC hmu_t *alloc_hmu(gc_heap_t *heap, gc_size_t size) /* alloc in last_p*/ /* remove node last_p from tree*/ - remove_tree_node(last_tp); + if (!remove_tree_node(heap, last_tp)) + return NULL; if (last_tp->size >= size + GC_SMALLEST_SIZE) { - rest = (hmu_t*) ((char*) last_tp + size); - gci_add_fc(heap, rest, last_tp->size - size); + rest = (hmu_t *)((char *)last_tp + size); + if (!gci_add_fc(heap, rest, last_tp->size - size)) + return NULL; hmu_mark_pinuse(rest); - } else { + } + else { size = last_tp->size; - next = (hmu_t*) ((char*) last_tp + size); - if (hmu_is_in_heap(heap, next)) + next = (hmu_t *)((char *)last_tp + size); + if (hmu_is_in_heap(next, base_addr, end_addr)) hmu_mark_pinuse(next); } -#if GC_STAT_DATA != 0 heap->total_free_size -= size; if ((heap->current_size - heap->total_free_size) > heap->highmark_size) heap->highmark_size = heap->current_size - heap->total_free_size; -#endif - hmu_set_size((hmu_t* ) last_tp, size); - return (hmu_t*) last_tp; + + hmu_set_size((hmu_t *)last_tp, size); + tp_ret = (uintptr_t)last_tp; + return (hmu_t *)tp_ret; } return NULL; } -/* Find a proper HMU for given size*/ - -/* @heap should not be NULL and it should be a valid heap*/ -/* @size should cover the header and it should be 8 bytes aligned*/ +#if WASM_ENABLE_GC != 0 +static int +do_gc_heap(gc_heap_t *heap) +{ + int ret = GC_SUCCESS; +#if WASM_ENABLE_GC_PERF_PROFILING != 0 + uint64 start = 0, end = 0, time = 0; -/* This function will try several ways to satisfy the allocation request.*/ -/* 1. Find a proper on available HMUs.*/ -/* 2. GC will be triggered if 1 failed.*/ -/* 3. Find a proper on available HMUS.*/ -/* 4. Return NULL if 3 failed*/ + start = os_time_get_boot_microsecond(); +#endif + if (heap->is_reclaim_enabled) { + UNLOCK_HEAP(heap); + ret = gci_gc_heap(heap); + LOCK_HEAP(heap); + } +#if WASM_ENABLE_GC_PERF_PROFILING != 0 + end = os_time_get_boot_microsecond(); + time = end - start; + heap->total_gc_time += time; + if (time > heap->max_gc_time) { + heap->max_gc_time = time; + } + heap->total_gc_count += 1; +#endif + return ret; +} +#endif -/* A proper HMU will be returned. This HMU can include the header and given size. The returned HMU will be aligned to 8 bytes.*/ -/* NULL will be returned if there are no proper HMU.*/ -BH_STATIC hmu_t* alloc_hmu_ex(gc_heap_t *heap, gc_size_t size) +/** + * Find a proper HMU with given size + * + * @param heap should not be NULL and should be a valid heap + * @param size should cover the header and should be 8 bytes aligned + * + * Note: This function will try several ways to satisfy the allocation request: + * 1. Find a proper on available HMUs. + * 2. GC will be triggered if 1 failed. + * 3. Find a proper on available HMUS. + * 4. Return NULL if 3 failed + * + * @return hmu allocated if success, which will be aligned to 8 bytes, + * NULL otherwise + */ +static hmu_t * +alloc_hmu_ex(gc_heap_t *heap, gc_size_t size) { - hmu_t *ret = NULL; - bh_assert(gci_is_heap_valid(heap)); bh_assert(size > 0 && !(size & 7)); -#ifdef GC_IN_EVERY_ALLOCATION - gci_gc_heap(heap); - ret = alloc_hmu(heap, size); +#if WASM_ENABLE_GC != 0 +#if GC_IN_EVERY_ALLOCATION != 0 + if (GC_SUCCESS != do_gc_heap(heap)) + return NULL; #else + if (heap->total_free_size < heap->gc_threshold) { + if (GC_SUCCESS != do_gc_heap(heap)) + return NULL; + } + else { + hmu_t *ret = NULL; + if ((ret = alloc_hmu(heap, size))) { + return ret; + } + if (GC_SUCCESS != do_gc_heap(heap)) + return NULL; + } +#endif +#endif + + return alloc_hmu(heap, size); +} -# if GC_STAT_DATA != 0 - if (heap->gc_threshold < heap->total_free_size) - ret = alloc_hmu(heap, size); -# else - ret = alloc_hmu(heap, size); -# endif +/* Convert object pointer to HMU pointer - handles aligned allocations */ +hmu_t * +obj_to_hmu(gc_object_t obj) +{ + /* Check for aligned allocation magic signature */ + if (gc_is_aligned_allocation(obj)) { + /* This is an aligned allocation, read offset */ + uint32_t *offset_ptr = ALIGNED_ALLOC_GET_OFFSET_PTR(obj); + return (hmu_t *)((char *)obj - *offset_ptr); + } - if (ret) - return ret; + /* Normal allocation: standard offset */ + return (hmu_t *)((gc_uint8 *)(obj)-OBJ_PREFIX_SIZE) - 1; +} - /*gci_gc_heap(heap);*//* disable gc claim currently */ - ret = alloc_hmu(heap, size); +#if BH_ENABLE_GC_VERIFY == 0 +gc_object_t +gc_alloc_vo(void *vheap, gc_size_t size) +#else +gc_object_t +gc_alloc_vo_internal(void *vheap, gc_size_t size, const char *file, int line) #endif +{ + gc_heap_t *heap = (gc_heap_t *)vheap; + hmu_t *hmu = NULL; + gc_object_t ret = (gc_object_t)NULL; + gc_size_t tot_size = 0, tot_size_unaligned; + + /* hmu header + prefix + obj + suffix */ + tot_size_unaligned = size + OBJ_EXTRA_SIZE; + /* aligned size*/ + tot_size = GC_ALIGN_8(tot_size_unaligned); + if (tot_size < size) + /* integer overflow */ + return NULL; + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (heap->is_heap_corrupted) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, allocate memory failed.\n"); + return NULL; + } +#endif + + LOCK_HEAP(heap); + + hmu = alloc_hmu_ex(heap, tot_size); + if (!hmu) + goto finish; + + bh_assert(hmu_get_size(hmu) >= tot_size); + /* the total size allocated may be larger than + the required size, reset it here */ + tot_size = hmu_get_size(hmu); + +#if GC_STAT_DATA != 0 + heap->total_size_allocated += tot_size; +#endif + + hmu_set_ut(hmu, HMU_VO); + hmu_unfree_vo(hmu); + +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu, tot_size, file, line); +#endif + + ret = hmu_to_obj(hmu); + if (tot_size > tot_size_unaligned) + /* clear buffer appended by GC_ALIGN_8() */ + memset((uint8 *)ret + size, 0, tot_size - tot_size_unaligned); + +finish: + UNLOCK_HEAP(heap); return ret; } -unsigned long g_total_malloc = 0; -unsigned long g_total_free = 0; - -gc_object_t _gc_alloc_vo_i_heap(void *vheap, - gc_size_t size ALLOC_EXTRA_PARAMETERS) +#if BH_ENABLE_GC_VERIFY == 0 +gc_object_t +gc_alloc_vo_aligned(void *vheap, gc_size_t size, gc_size_t alignment) +#else +gc_object_t +gc_alloc_vo_aligned_internal(void *vheap, gc_size_t size, gc_size_t alignment, + const char *file, int line) +#endif { - gc_heap_t* heap = (gc_heap_t*) vheap; + gc_heap_t *heap = (gc_heap_t *)vheap; hmu_t *hmu = NULL; - gc_object_t ret = (gc_object_t) NULL; - gc_size_t tot_size = 0; + gc_object_t ret = NULL; + gc_size_t tot_size, tot_size_unaligned; + gc_uint8 *base_obj; + uintptr_t aligned_addr; + uint32_t offset, alignment_log2; + uint32_t max_alignment; + + /* Get system page size for maximum alignment check */ + max_alignment = (uint32_t)os_getpagesize(); + + /* Validation */ + if (alignment == 0 || (alignment & (alignment - 1)) != 0) { + /* Zero or not power of 2 */ + return NULL; + } + + if (alignment < GC_MIN_ALIGNMENT) { + alignment = GC_MIN_ALIGNMENT; + } + + if (alignment > max_alignment) { + /* Exceeds page size */ + return NULL; + } + + if (size % alignment != 0) { + /* POSIX requirement: size must be multiple of alignment */ + return NULL; + } + + if (size > SIZE_MAX - GC_ALIGNED_SMALLEST_SIZE(alignment)) { + /* Would overflow */ + return NULL; + } + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (heap->is_heap_corrupted) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, allocate memory failed.\n"); + return NULL; + } +#endif + + /* Calculate total size needed */ + tot_size_unaligned = size + OBJ_EXTRA_SIZE + ALIGNED_ALLOC_EXTRA_OVERHEAD + + (alignment > 8 ? (alignment - 8) : 8); + tot_size = GC_ALIGN_8(tot_size_unaligned); + + if (tot_size < size) { + /* Integer overflow */ + return NULL; + } + + LOCK_HEAP(heap); + + hmu = alloc_hmu_ex(heap, tot_size); + if (!hmu) + goto finish; + + bh_assert(hmu_get_size(hmu) >= tot_size); + tot_size = hmu_get_size(hmu); + +#if GC_STAT_DATA != 0 + heap->total_size_allocated += tot_size; +#endif + + /* Get base object pointer */ + base_obj = (gc_uint8 *)hmu + HMU_SIZE + OBJ_PREFIX_SIZE; + + /* Find next aligned address, reserving space for metadata */ + aligned_addr = + (((uintptr_t)base_obj + ALIGNED_ALLOC_METADATA_SIZE + alignment - 1) + & ~(uintptr_t)(alignment - 1)); + ret = (gc_object_t)aligned_addr; + + /* Verify we have enough space */ + bh_assert((gc_uint8 *)ret + size + OBJ_SUFFIX_SIZE + <= (gc_uint8 *)hmu + tot_size); + + /* Calculate offset from HMU to returned pointer */ + offset = (uint32_t)((char *)ret - (char *)hmu); + + /* Calculate log2 of alignment for magic value */ + alignment_log2 = 0; + while ((1U << alignment_log2) < alignment) { + alignment_log2++; + } + + /* Store offset before returned pointer */ + *ALIGNED_ALLOC_GET_OFFSET_PTR(ret) = offset; + + /* Store magic with encoded alignment */ + *ALIGNED_ALLOC_GET_MAGIC_PTR(ret) = + ALIGNED_ALLOC_MAGIC_VALUE | alignment_log2; + + /* Initialize HMU */ + hmu_set_ut(hmu, HMU_VO); + hmu_unfree_vo(hmu); + +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu, tot_size, file, line); +#endif + +finish: + UNLOCK_HEAP(heap); + return ret; +} + +#if BH_ENABLE_GC_VERIFY == 0 +gc_object_t +gc_realloc_vo(void *vheap, void *ptr, gc_size_t size) +#else +gc_object_t +gc_realloc_vo_internal(void *vheap, void *ptr, gc_size_t size, const char *file, + int line) +#endif +{ + gc_heap_t *heap = (gc_heap_t *)vheap; + hmu_t *hmu = NULL, *hmu_old = NULL, *hmu_next; + gc_object_t ret = (gc_object_t)NULL, obj_old = (gc_object_t)ptr; + gc_size_t tot_size, tot_size_unaligned, tot_size_old = 0, tot_size_next; + gc_size_t obj_size, obj_size_old; + gc_uint8 *base_addr, *end_addr; + hmu_type_t ut; - /* align size*/ - tot_size = GC_ALIGN_8(size + HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE); /* hmu header, prefix, suffix*/ + /* hmu header + prefix + obj + suffix */ + tot_size_unaligned = HMU_SIZE + OBJ_PREFIX_SIZE + size + OBJ_SUFFIX_SIZE; + /* aligned size*/ + tot_size = GC_ALIGN_8(tot_size_unaligned); if (tot_size < size) + /* integer overflow */ return NULL; - gct_vm_mutex_lock(&heap->lock); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (heap->is_heap_corrupted) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, allocate memory failed.\n"); + return NULL; + } +#endif + + /* Check if this is an aligned allocation - not supported */ + if (gc_is_aligned_allocation(obj_old)) { + LOG_ERROR("[GC_ERROR]gc_realloc_vo does not support aligned " + "allocations\n"); + return NULL; + } + + if (obj_old) { + hmu_old = obj_to_hmu(obj_old); + tot_size_old = hmu_get_size(hmu_old); + if (tot_size <= tot_size_old) + /* current node already meets requirement */ + return obj_old; + } + + base_addr = heap->base_addr; + end_addr = base_addr + heap->current_size; + + LOCK_HEAP(heap); + + if (hmu_old) { + hmu_next = (hmu_t *)((char *)hmu_old + tot_size_old); + if (hmu_is_in_heap(hmu_next, base_addr, end_addr)) { + ut = hmu_get_ut(hmu_next); + tot_size_next = hmu_get_size(hmu_next); + if (ut == HMU_FC && tot_size <= tot_size_old + tot_size_next) { + /* current node and next node meets requirement */ + if (!unlink_hmu(heap, hmu_next)) { + UNLOCK_HEAP(heap); + return NULL; + } + hmu_set_size(hmu_old, tot_size); + memset((char *)hmu_old + tot_size_old, 0, + tot_size - tot_size_old); +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu_old, tot_size, file, line); +#endif + if (tot_size < tot_size_old + tot_size_next) { + hmu_next = (hmu_t *)((char *)hmu_old + tot_size); + tot_size_next = tot_size_old + tot_size_next - tot_size; + if (!gci_add_fc(heap, hmu_next, tot_size_next)) { + UNLOCK_HEAP(heap); + return NULL; + } + hmu_mark_pinuse(hmu_next); + } + UNLOCK_HEAP(heap); + return obj_old; + } + } + } hmu = alloc_hmu_ex(heap, tot_size); if (!hmu) - goto FINISH; + goto finish; - g_total_malloc += tot_size; + bh_assert(hmu_get_size(hmu) >= tot_size); + /* the total size allocated may be larger than + the required size, reset it here */ + tot_size = hmu_get_size(hmu); + +#if GC_STAT_DATA != 0 + heap->total_size_allocated += tot_size; +#endif hmu_set_ut(hmu, HMU_VO); hmu_unfree_vo(hmu); -#if defined(GC_VERIFY) - hmu_init_prefix_and_suffix(hmu, tot_size, file_name, line_number); +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu, tot_size, file, line); #endif ret = hmu_to_obj(hmu); -#if BH_ENABLE_MEMORY_PROFILING != 0 - bh_printf("HEAP.ALLOC: heap: %p, size: %u", heap, size); -#endif +finish: + + if (ret) { + obj_size = tot_size - HMU_SIZE - OBJ_PREFIX_SIZE - OBJ_SUFFIX_SIZE; + memset(ret, 0, obj_size); + if (obj_old) { + obj_size_old = + tot_size_old - HMU_SIZE - OBJ_PREFIX_SIZE - OBJ_SUFFIX_SIZE; + bh_memcpy_s(ret, obj_size, obj_old, obj_size_old); + } + } - FINISH: - gct_vm_mutex_unlock(&heap->lock); + UNLOCK_HEAP(heap); + + if (ret && obj_old) + gc_free_vo(vheap, obj_old); return ret; } -/* see ems_gc.h for description*/ -gc_object_t _gc_alloc_jo_i_heap(void *vheap, - gc_size_t size ALLOC_EXTRA_PARAMETERS) +#if GC_MANUALLY != 0 +void +gc_free_wo(void *vheap, void *ptr) { - gc_heap_t* heap = (gc_heap_t*) vheap; - gc_object_t ret = (gc_object_t) NULL; - hmu_t *hmu = NULL; - gc_size_t tot_size = 0; + gc_heap_t *heap = (gc_heap_t *)vheap; + gc_object_t *obj = (gc_object_t *)ptr; + hmu_t *hmu = obj_to_hmu(obj); bh_assert(gci_is_heap_valid(heap)); + bh_assert(obj); + bh_assert((gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu < heap->base_addr + heap->current_size); + bh_assert(hmu_get_ut(hmu) == HMU_WO); + + hmu_unmark_wo(hmu); + (void)heap; +} +#endif + +/* see ems_gc.h for description*/ +#if BH_ENABLE_GC_VERIFY == 0 +gc_object_t +gc_alloc_wo(void *vheap, gc_size_t size) +#else +gc_object_t +gc_alloc_wo_internal(void *vheap, gc_size_t size, const char *file, int line) +#endif +{ + gc_heap_t *heap = (gc_heap_t *)vheap; + hmu_t *hmu = NULL; + gc_object_t ret = (gc_object_t)NULL; + gc_size_t tot_size = 0, tot_size_unaligned; - /* align size*/ - tot_size = GC_ALIGN_8(size + HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE); /* hmu header, prefix, suffix*/ + /* hmu header + prefix + obj + suffix */ + tot_size_unaligned = HMU_SIZE + OBJ_PREFIX_SIZE + size + OBJ_SUFFIX_SIZE; + /* aligned size*/ + tot_size = GC_ALIGN_8(tot_size_unaligned); if (tot_size < size) + /* integer overflow */ + return NULL; + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (heap->is_heap_corrupted) { + os_printf("[GC_ERROR]Heap is corrupted, allocate memory failed.\n"); return NULL; + } +#endif + + LOCK_HEAP(heap); hmu = alloc_hmu_ex(heap, tot_size); if (!hmu) - goto FINISH; + goto finish; - /* reset all fields*/ - memset((char*) hmu + sizeof(*hmu), 0, tot_size - sizeof(*hmu)); + /* Don't memset the memory to improve performance, the caller should + decide whether to memset it or not */ - /* hmu->header = 0; */ - hmu_set_ut(hmu, HMU_JO); - hmu_unmark_jo(hmu); + bh_assert(hmu_get_size(hmu) >= tot_size); + /* the total size allocated may be larger than + the required size, reset it here */ + tot_size = hmu_get_size(hmu); -#if defined(GC_VERIFY) - hmu_init_prefix_and_suffix(hmu, tot_size, file_name, line_number); +#if GC_STAT_DATA != 0 + heap->total_size_allocated += tot_size; #endif - ret = hmu_to_obj(hmu); -#if BH_ENABLE_MEMORY_PROFILING != 0 - bh_printf("HEAP.ALLOC: heap: %p, size: %u", heap, size); + hmu_set_ut(hmu, HMU_WO); +#if GC_MANUALLY != 0 + hmu_mark_wo(hmu); +#else + hmu_unmark_wo(hmu); #endif - FINISH: +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu, tot_size, file, line); +#endif + ret = hmu_to_obj(hmu); + if (tot_size > tot_size_unaligned) + /* clear buffer appended by GC_ALIGN_8() */ + memset((uint8 *)ret + size, 0, tot_size - tot_size_unaligned); + +finish: + UNLOCK_HEAP(heap); return ret; } -/* Do some checking to see if given pointer is a possible valid heap*/ - -/* Return GC_TRUE if all checking passed*/ -/* Return GC_FALSE otherwise*/ -int gci_is_heap_valid(gc_heap_t *heap) +/** + * Do some checking to see if given pointer is a possible valid heap + * @return GC_TRUE if all checking passed, GC_FALSE otherwise + */ +int +gci_is_heap_valid(gc_heap_t *heap) { if (!heap) return GC_FALSE; - if (heap->heap_id != (gc_handle_t) heap) + if (heap->heap_id != (gc_handle_t)heap) return GC_FALSE; return GC_TRUE; } -int gc_free_i_heap(void *vheap, gc_object_t obj ALLOC_EXTRA_PARAMETERS) +#if BH_ENABLE_GC_VERIFY == 0 +int +gc_free_vo(void *vheap, gc_object_t obj) +#else +int +gc_free_vo_internal(void *vheap, gc_object_t obj, const char *file, int line) +#endif { - gc_heap_t* heap = (gc_heap_t*) vheap; + gc_heap_t *heap = (gc_heap_t *)vheap; + gc_uint8 *base_addr, *end_addr; hmu_t *hmu = NULL; hmu_t *prev = NULL; hmu_t *next = NULL; @@ -456,14 +994,23 @@ int gc_free_i_heap(void *vheap, gc_object_t obj ALLOC_EXTRA_PARAMETERS) return GC_SUCCESS; } +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (heap->is_heap_corrupted) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, free memory failed.\n"); + return GC_ERROR; + } +#endif + hmu = obj_to_hmu(obj); - gct_vm_mutex_lock(&heap->lock); + base_addr = heap->base_addr; + end_addr = base_addr + heap->current_size; + + LOCK_HEAP(heap); - if ((gc_uint8 *) hmu >= heap->base_addr - && (gc_uint8 *) hmu < heap->base_addr + heap->current_size) { -#ifdef GC_VERIFY - hmu_verify(hmu); + if (hmu_is_in_heap(hmu, base_addr, end_addr)) { +#if BH_ENABLE_GC_VERIFY != 0 + hmu_verify(heap, hmu); #endif ut = hmu_get_ut(hmu); if (ut == HMU_VO) { @@ -475,41 +1022,48 @@ int gc_free_i_heap(void *vheap, gc_object_t obj ALLOC_EXTRA_PARAMETERS) size = hmu_get_size(hmu); - g_total_free += size; + heap->total_free_size += size; #if GC_STAT_DATA != 0 - heap->total_free_size += size; -#endif -#if BH_ENABLE_MEMORY_PROFILING != 0 - bh_printf("HEAP.FREE, heap: %p, size: %u\n",heap, size); + heap->total_size_freed += size; #endif if (!hmu_get_pinuse(hmu)) { - prev = (hmu_t*) ((char*) hmu - *((int*) hmu - 1)); + prev = (hmu_t *)((char *)hmu - *((int *)hmu - 1)); - if (hmu_is_in_heap(heap, prev) && hmu_get_ut(prev) == HMU_FC) { + if (hmu_is_in_heap(prev, base_addr, end_addr) + && hmu_get_ut(prev) == HMU_FC) { size += hmu_get_size(prev); hmu = prev; - unlink_hmu(heap, prev); + if (!unlink_hmu(heap, prev)) { + ret = GC_ERROR; + goto out; + } } } - next = (hmu_t*) ((char*) hmu + size); - if (hmu_is_in_heap(heap, next)) { + next = (hmu_t *)((char *)hmu + size); + if (hmu_is_in_heap(next, base_addr, end_addr)) { if (hmu_get_ut(next) == HMU_FC) { size += hmu_get_size(next); - unlink_hmu(heap, next); - next = (hmu_t*) ((char*) hmu + size); + if (!unlink_hmu(heap, next)) { + ret = GC_ERROR; + goto out; + } + next = (hmu_t *)((char *)hmu + size); } } - gci_add_fc(heap, hmu, size); + if (!gci_add_fc(heap, hmu, size)) { + ret = GC_ERROR; + goto out; + } - if (hmu_is_in_heap(heap, next)) { + if (hmu_is_in_heap(next, base_addr, end_addr)) { hmu_unmark_pinuse(next); } - - } else { + } + else { ret = GC_ERROR; goto out; } @@ -517,60 +1071,239 @@ int gc_free_i_heap(void *vheap, gc_object_t obj ALLOC_EXTRA_PARAMETERS) goto out; } - out: - gct_vm_mutex_unlock(&heap->lock); +out: + UNLOCK_HEAP(heap); return ret; } -void gc_dump_heap_stats(gc_heap_t *heap) +void +gc_dump_heap_stats(gc_heap_t *heap) { - bh_printf("heap: %p, heap start: %p\n", heap, heap->base_addr); - bh_printf( - "total malloc: totalfree: %u, current: %u, highmark: %u, gc cnt: %u\n", - heap->total_free_size, heap->current_size, heap->highmark_size, - heap->total_gc_count); - bh_printf("g_total_malloc=%lu, g_total_free=%lu, occupied=%lu\n", - g_total_malloc, g_total_free, g_total_malloc - g_total_free); + os_printf("heap: %p, heap start: %p\n", heap, heap->base_addr); + os_printf("total free: %" PRIu32 ", current: %" PRIu32 + ", highmark: %" PRIu32 "\n", + heap->total_free_size, heap->current_size, heap->highmark_size); +#if GC_STAT_DATA != 0 + os_printf("total size allocated: %" PRIu64 ", total size freed: %" PRIu64 + ", total occupied: %" PRIu64 "\n", + heap->total_size_allocated, heap->total_size_freed, + heap->total_size_allocated - heap->total_size_freed); +#endif } -#ifdef GC_TEST +uint32 +gc_get_heap_highmark_size(gc_heap_t *heap) +{ + return heap->highmark_size; +} -void gci_dump(char* buf, gc_heap_t *heap) +void +gci_dump(gc_heap_t *heap) { hmu_t *cur = NULL, *end = NULL; hmu_type_t ut; gc_size_t size; - int i = 0; - int p; - char inuse; - int mark; + int i = 0, p, mark; + char inuse = 'U'; - cur = (hmu_t*)heap->base_addr; - end = (hmu_t*)((char*)heap->base_addr + heap->current_size); + cur = (hmu_t *)heap->base_addr; + end = (hmu_t *)((char *)heap->base_addr + heap->current_size); - while(cur < end) - { + while (cur < end) { ut = hmu_get_ut(cur); size = hmu_get_size(cur); p = hmu_get_pinuse(cur); - mark = hmu_is_jo_marked (cur); - - if(ut == HMU_VO) - inuse = 'V'; - else if(ut == HMU_JO) - inuse = hmu_is_jo_marked(cur) ? 'J' : 'j'; - else if(ut == HMU_FC) - inuse = 'F'; - - bh_assert(size > 0); + mark = hmu_is_wo_marked(cur); + + if (ut == HMU_VO) + inuse = 'V'; + else if (ut == HMU_WO) + inuse = hmu_is_wo_marked(cur) ? 'W' : 'w'; + else if (ut == HMU_FC) + inuse = 'F'; + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (size == 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, heap dump failed.\n"); + heap->is_heap_corrupted = true; + return; + } +#endif - buf += sprintf(buf, "#%d %08x %x %x %d %c %d\n", i, (char*) cur - (char*) heap->base_addr, ut, p, mark, inuse, hmu_obj_size(size)); + os_printf("#%d %08" PRIx32 " %" PRIx32 " %d %d" + " %c %" PRId32 "\n", + i, (uint32)((char *)cur - (char *)heap->base_addr), + (uint32)ut, p, mark, inuse, (int32)hmu_obj_size(size)); +#if BH_ENABLE_GC_VERIFY != 0 + if (inuse == 'V') { + gc_object_prefix_t *prefix = (gc_object_prefix_t *)(cur + 1); + os_printf("#%s:%d\n", prefix->file_name, prefix->line_no); + } +#endif - cur = (hmu_t*)((char *)cur + size); + cur = (hmu_t *)((char *)cur + size); i++; } +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (cur != end) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, heap dump failed.\n"); + heap->is_heap_corrupted = true; + } +#else bh_assert(cur == end); +#endif } +#if WASM_ENABLE_GC != 0 +extra_info_node_t * +gc_search_extra_info_node(gc_handle_t handle, gc_object_t obj, + gc_size_t *p_index) +{ + gc_heap_t *vheap = (gc_heap_t *)handle; + int32 low = 0, high = vheap->extra_info_node_cnt - 1; + int32 mid; + extra_info_node_t *node; + + if (!vheap->extra_info_nodes) + return NULL; + + while (low <= high) { + mid = (low + high) / 2; + node = vheap->extra_info_nodes[mid]; + + if (obj == node->obj) { + if (p_index) { + *p_index = mid; + } + return node; + } + else if (obj < node->obj) { + high = mid - 1; + } + else { + low = mid + 1; + } + } + + if (p_index) { + *p_index = low; + } + return NULL; +} + +static bool +insert_extra_info_node(gc_heap_t *vheap, extra_info_node_t *node) +{ + gc_size_t index; + extra_info_node_t *orig_node; + + if (!vheap->extra_info_nodes) { + vheap->extra_info_nodes = vheap->extra_info_normal_nodes; + vheap->extra_info_node_capacity = sizeof(vheap->extra_info_normal_nodes) + / sizeof(extra_info_node_t *); + vheap->extra_info_nodes[0] = node; + vheap->extra_info_node_cnt = 1; + return true; + } + + /* extend array */ + if (vheap->extra_info_node_cnt == vheap->extra_info_node_capacity) { + extra_info_node_t **new_nodes = NULL; + gc_size_t new_capacity = vheap->extra_info_node_capacity * 3 / 2; + gc_size_t total_size = sizeof(extra_info_node_t *) * new_capacity; + + new_nodes = (extra_info_node_t **)BH_MALLOC(total_size); + if (!new_nodes) { + LOG_ERROR("alloc extra info nodes failed"); + return false; + } + + bh_memcpy_s(new_nodes, total_size, vheap->extra_info_nodes, + sizeof(extra_info_node_t *) * vheap->extra_info_node_cnt); + if (vheap->extra_info_nodes != vheap->extra_info_normal_nodes) { + BH_FREE(vheap->extra_info_nodes); + } + + vheap->extra_info_nodes = new_nodes; + vheap->extra_info_node_capacity = new_capacity; + } + + orig_node = gc_search_extra_info_node(vheap, node->obj, &index); + if (orig_node) { + /* replace the old node */ + vheap->extra_info_nodes[index] = node; + BH_FREE(orig_node); + } + else { + bh_memmove_s(vheap->extra_info_nodes + index + 1, + (vheap->extra_info_node_capacity - index - 1) + * sizeof(extra_info_node_t *), + vheap->extra_info_nodes + index, + (vheap->extra_info_node_cnt - index) + * sizeof(extra_info_node_t *)); + vheap->extra_info_nodes[index] = node; + vheap->extra_info_node_cnt += 1; + } + + return true; +} + +bool +gc_set_finalizer(gc_handle_t handle, gc_object_t obj, gc_finalizer_t cb, + void *data) +{ + extra_info_node_t *node = NULL; + gc_heap_t *vheap = (gc_heap_t *)handle; + + node = (extra_info_node_t *)BH_MALLOC(sizeof(extra_info_node_t)); + + if (!node) { + LOG_ERROR("alloc a new extra info node failed"); + return GC_FALSE; + } + memset(node, 0, sizeof(extra_info_node_t)); + + node->finalizer = cb; + node->obj = obj; + node->data = data; + + LOCK_HEAP(vheap); + if (!insert_extra_info_node(vheap, node)) { + BH_FREE(node); + UNLOCK_HEAP(vheap); + return GC_FALSE; + } + UNLOCK_HEAP(vheap); + + gct_vm_set_extra_info_flag(obj, true); + return GC_TRUE; +} + +void +gc_unset_finalizer(gc_handle_t handle, gc_object_t obj) +{ + gc_size_t index; + gc_heap_t *vheap = (gc_heap_t *)handle; + extra_info_node_t *node; + + LOCK_HEAP(vheap); + node = gc_search_extra_info_node(vheap, obj, &index); + + if (!node) { + UNLOCK_HEAP(vheap); + return; + } + + BH_FREE(node); + bh_memmove_s( + vheap->extra_info_nodes + index, + (vheap->extra_info_node_capacity - index) * sizeof(extra_info_node_t *), + vheap->extra_info_nodes + index + 1, + (vheap->extra_info_node_cnt - index - 1) * sizeof(extra_info_node_t *)); + vheap->extra_info_node_cnt -= 1; + UNLOCK_HEAP(vheap); + + gct_vm_set_extra_info_flag(obj, false); +} #endif diff --git a/core/shared/mem-alloc/ems/ems_gc.c b/core/shared/mem-alloc/ems/ems_gc.c new file mode 100644 index 0000000000..bc0488968d --- /dev/null +++ b/core/shared/mem-alloc/ems/ems_gc.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2022 Tencent Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "ems_gc.h" +#include "ems_gc_internal.h" + +#ifndef GB // Some platforms define already, causing build warnings. +#define GB (1 << 30UL) +#endif + +#define MARK_NODE_OBJ_CNT 256 + +#if WASM_ENABLE_GC != 0 + +/* mark node is used for gc marker*/ +typedef struct mark_node_struct { + /* number of to-expand objects can be saved in this node */ + gc_size_t cnt; + + /* the first unused index */ + uint32 idx; + + /* next node on the node list */ + struct mark_node_struct *next; + + /* the actual to-expand objects list */ + gc_object_t set[MARK_NODE_OBJ_CNT]; +} mark_node_t; + +/** + * Alloc a mark node from the native heap + * + * @return a valid mark node if success, NULL otherwise + */ +static mark_node_t * +alloc_mark_node(void) +{ + mark_node_t *ret = (mark_node_t *)BH_MALLOC(sizeof(mark_node_t)); + + if (!ret) { + LOG_ERROR("alloc a new mark node failed"); + return NULL; + } + ret->cnt = sizeof(ret->set) / sizeof(ret->set[0]); + ret->idx = 0; + ret->next = NULL; + return ret; +} + +/* Free a mark node to the native heap + * + * @param node the mark node to free, should not be NULL + */ +static void +free_mark_node(mark_node_t *node) +{ + bh_assert(node); + BH_FREE((gc_object_t)node); +} + +/** + * Sweep phase of mark_sweep algorithm + * @param heap the heap to sweep, should be a valid instance heap + * which has already been marked + */ +static void +sweep_instance_heap(gc_heap_t *heap) +{ + hmu_t *cur = NULL, *end = NULL, *last = NULL; + hmu_type_t ut; + gc_size_t size; + int i, lsize; + gc_size_t tot_free = 0; + + bh_assert(gci_is_heap_valid(heap)); + + cur = (hmu_t *)heap->base_addr; + last = NULL; + end = (hmu_t *)((char *)heap->base_addr + heap->current_size); + + /* reset KFC */ + lsize = + (int)(sizeof(heap->kfc_normal_list) / sizeof(heap->kfc_normal_list[0])); + for (i = 0; i < lsize; i++) { + heap->kfc_normal_list[i].next = NULL; + } + heap->kfc_tree_root->right = NULL; + heap->root_set = NULL; + + while (cur < end) { + ut = hmu_get_ut(cur); + size = hmu_get_size(cur); + bh_assert(size > 0); + + if (ut == HMU_FC || ut == HMU_FM + || (ut == HMU_VO && hmu_is_vo_freed(cur)) + || (ut == HMU_WO && !hmu_is_wo_marked(cur))) { + /* merge previous free areas with current one */ + if (!last) + last = cur; + + if (ut == HMU_WO) { + /* Invoke registered finalizer */ + gc_object_t cur_obj = hmu_to_obj(cur); + if (gct_vm_get_extra_info_flag(cur_obj)) { + extra_info_node_t *node = gc_search_extra_info_node( + (gc_handle_t)heap, cur_obj, NULL); + bh_assert(node); + node->finalizer(node->obj, node->data); + gc_unset_finalizer((gc_handle_t)heap, cur_obj); + } + } + } + else { + /* current block is still live */ + if (last) { + tot_free += (gc_size_t)((char *)cur - (char *)last); + gci_add_fc(heap, last, (gc_size_t)((char *)cur - (char *)last)); + hmu_mark_pinuse(last); + last = NULL; + } + + if (ut == HMU_WO) { + /* unmark it */ + hmu_unmark_wo(cur); + } + } + + cur = (hmu_t *)((char *)cur + size); + } + + bh_assert(cur == end); + + if (last) { + tot_free += (gc_size_t)((char *)cur - (char *)last); + gci_add_fc(heap, last, (gc_size_t)((char *)cur - (char *)last)); + hmu_mark_pinuse(last); + } + + heap->total_free_size = tot_free; + +#if GC_STAT_DATA != 0 + heap->total_gc_count++; + if ((heap->current_size - tot_free) > heap->highmark_size) + heap->highmark_size = heap->current_size - tot_free; + +#endif + gc_update_threshold(heap); +} + +/** + * Add a to-expand node to the to-expand list + * + * @param heap should be a valid instance heap + * @param obj should be a valid wo inside @heap + * + * @return GC_ERROR if there is no more resource for marking, + * GC_SUCCESS if success + */ +static int +add_wo_to_expand(gc_heap_t *heap, gc_object_t obj) +{ + mark_node_t *mark_node = NULL, *new_node = NULL; + hmu_t *hmu = NULL; + + bh_assert(obj); + + hmu = obj_to_hmu(obj); + + bh_assert(gci_is_heap_valid(heap)); + bh_assert((gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu < heap->base_addr + heap->current_size); + bh_assert(hmu_get_ut(hmu) == HMU_WO); + + if (hmu_is_wo_marked(hmu)) + return GC_SUCCESS; /* already marked*/ + + mark_node = (mark_node_t *)heap->root_set; + if (!mark_node || mark_node->idx == mark_node->cnt) { + new_node = alloc_mark_node(); + if (!new_node) { + LOG_ERROR("can not add obj to mark node because of mark node " + "allocation failed"); + return GC_ERROR; + } + new_node->next = mark_node; + heap->root_set = new_node; + mark_node = new_node; + } + + mark_node->set[mark_node->idx++] = obj; + hmu_mark_wo(hmu); + return GC_SUCCESS; +} + +/* Check ems_gc.h for description*/ +int +gc_add_root(void *heap_p, gc_object_t obj) +{ + gc_heap_t *heap = (gc_heap_t *)heap_p; + hmu_t *hmu = NULL; + + if (!obj) { + LOG_ERROR("gc_add_root with NULL obj"); + return GC_ERROR; + } + + hmu = obj_to_hmu(obj); + + if (!gci_is_heap_valid(heap)) { + LOG_ERROR("vm_get_gc_handle_for_current_instance returns invalid heap"); + return GC_ERROR; + } + + if (!((gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu < heap->base_addr + heap->current_size)) { + LOG_ERROR("Obj is not a object in current instance heap"); + return GC_ERROR; + } + + if (hmu_get_ut(hmu) != HMU_WO) { + LOG_ERROR("Given object is not wo"); + return GC_ERROR; + } + + if (add_wo_to_expand(heap, obj) != GC_SUCCESS) { + heap->is_fast_marking_failed = 1; + return GC_ERROR; + } + + return GC_SUCCESS; +} + +/** + * Unmark all marked objects to do rollback + * + * @param heap the heap to do rollback, should be a valid instance heap + */ +static void +rollback_mark(gc_heap_t *heap) +{ + mark_node_t *mark_node = NULL, *next_mark_node = NULL; + hmu_t *cur = NULL, *end = NULL; + hmu_type_t ut; + gc_size_t size; + + bh_assert(gci_is_heap_valid(heap)); + + /* roll back*/ + mark_node = (mark_node_t *)heap->root_set; + while (mark_node) { + next_mark_node = mark_node->next; + free_mark_node(mark_node); + mark_node = next_mark_node; + } + + heap->root_set = NULL; + + /* then traverse the heap to unmark all marked wos*/ + + cur = (hmu_t *)heap->base_addr; + end = (hmu_t *)((char *)heap->base_addr + heap->current_size); + + while (cur < end) { + ut = hmu_get_ut(cur); + size = hmu_get_size(cur); + + if (ut == HMU_WO && hmu_is_wo_marked(cur)) { + hmu_unmark_wo(cur); + } + + cur = (hmu_t *)((char *)cur + size); + } + + bh_assert(cur == end); +} + +/** + * Reclaim GC instance heap + * + * @param heap the heap to reclaim, should be a valid instance heap + * + * @return GC_SUCCESS if success, GC_ERROR otherwise + */ +static int +reclaim_instance_heap(gc_heap_t *heap) +{ + mark_node_t *mark_node = NULL; + int idx = 0, j = 0; + bool ret, is_compact_mode = false; + gc_object_t obj = NULL, ref = NULL; + hmu_t *hmu = NULL; + gc_uint32 ref_num = 0, ref_start_offset = 0, size = 0, offset = 0; + gc_uint16 *ref_list = NULL; + + bh_assert(gci_is_heap_valid(heap)); + + heap->root_set = NULL; + +#if WASM_ENABLE_THREAD_MGR == 0 + if (!heap->exec_env) + return GC_SUCCESS; + ret = gct_vm_begin_rootset_enumeration(heap->exec_env, heap); +#else + if (!heap->cluster) + return GC_SUCCESS; + ret = gct_vm_begin_rootset_enumeration(heap->cluster, heap); +#endif + if (!ret) { + if (heap->root_set) { + rollback_mark(heap); + } + return GC_ERROR; + } + +#if BH_ENABLE_GC_VERIFY != 0 + /* no matter whether the enumeration is successful or not, the data + collected should be checked at first */ + mark_node = (mark_node_t *)heap->root_set; + while (mark_node) { + /* all nodes except first should be full filled */ + bh_assert(mark_node == (mark_node_t *)heap->root_set + || mark_node->idx == mark_node->cnt); + + /* all nodes should be non-empty */ + bh_assert(mark_node->idx > 0); + + for (idx = 0; idx < (int)mark_node->idx; idx++) { + obj = mark_node->set[idx]; + hmu = obj_to_hmu(obj); + bh_assert(hmu_is_wo_marked(hmu)); + bh_assert((gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu + < heap->base_addr + heap->current_size); + } + + mark_node = mark_node->next; + } +#endif + + /* TODO: when fast marking failed, we can still do slow + marking, currently just simply roll it back. */ + if (heap->is_fast_marking_failed) { + LOG_ERROR("enumerate rootset failed"); + LOG_ERROR("all marked wos will be unmarked to keep heap consistency"); + + rollback_mark(heap); + heap->is_fast_marking_failed = 0; + return GC_ERROR; + } + + /* the algorithm we use to mark all objects */ + /* 1. mark rootset and organize them into a mark_node list (last marked + * roots at list header, i.e. stack top) */ + /* 2. in every iteration, we use the top node to expand*/ + /* 3. execute step 2 till no expanding */ + /* this is a BFS & DFS mixed algorithm, but more like DFS */ + mark_node = (mark_node_t *)heap->root_set; + while (mark_node) { + heap->root_set = mark_node->next; + + /* note that mark_node->idx may change in each loop */ + for (idx = 0; idx < (int)mark_node->idx; idx++) { + obj = mark_node->set[idx]; + hmu = obj_to_hmu(obj); + size = hmu_get_size(hmu); + + if (!gct_vm_get_wasm_object_ref_list(obj, &is_compact_mode, + &ref_num, &ref_list, + &ref_start_offset)) { + LOG_ERROR("mark process failed because failed " + "vm_get_wasm_object_ref_list"); + break; + } + + if (ref_num >= 2U * GB) { + LOG_ERROR("Invalid ref_num returned"); + break; + } + + if (is_compact_mode) { + for (j = 0; j < (int)ref_num; j++) { + offset = ref_start_offset + j * sizeof(void *); + bh_assert(offset + sizeof(void *) < size); + ref = *(gc_object_t *)(((gc_uint8 *)obj) + offset); + if (ref == NULL_REF || ((uintptr_t)ref & 1)) + continue; /* null object or i31 object */ + if (add_wo_to_expand(heap, ref) == GC_ERROR) { + LOG_ERROR("add_wo_to_expand failed"); + break; + } + } + if (j < (int)ref_num) + break; + } + else { + for (j = 0; j < (int)ref_num; j++) { + offset = ref_list[j]; + bh_assert(offset + sizeof(void *) < size); + + ref = *(gc_object_t *)(((gc_uint8 *)obj) + offset); + if (ref == NULL_REF || ((uintptr_t)ref & 1)) + continue; /* null object or i31 object */ + if (add_wo_to_expand(heap, ref) == GC_ERROR) { + LOG_ERROR("mark process failed"); + break; + } + } + if (j < (int)ref_num) + break; + } + } + if (idx < (int)mark_node->idx) + break; /* not yet done */ + + /* obj's in mark_node are all expanded */ + free_mark_node(mark_node); + mark_node = heap->root_set; + } + + if (mark_node) { + LOG_ERROR("mark process is not successfully finished"); + + free_mark_node(mark_node); + /* roll back is required */ + rollback_mark(heap); + + return GC_ERROR; + } + + /* now sweep */ + sweep_instance_heap(heap); + + (void)size; + + return GC_SUCCESS; +} + +/** + * Do GC on given heap + * + * @param the heap to do GC, should be a valid heap + * + * @return GC_SUCCESS if success, GC_ERROR otherwise + */ +int +gci_gc_heap(void *h) +{ + int ret = GC_ERROR; + gc_heap_t *heap = (gc_heap_t *)h; + + bh_assert(gci_is_heap_valid(heap)); + + LOG_VERBOSE("#reclaim instance heap %p", heap); + + /* TODO: get exec_env of current thread when GC multi-threading + is enabled, and pass it to runtime */ + gct_vm_gc_prepare(NULL); + + gct_vm_mutex_lock(&heap->lock); + heap->is_doing_reclaim = 1; + + ret = reclaim_instance_heap(heap); + + heap->is_doing_reclaim = 0; + gct_vm_mutex_unlock(&heap->lock); + + /* TODO: get exec_env of current thread when GC multi-threading + is enabled, and pass it to runtime */ + gct_vm_gc_finished(NULL); + + LOG_VERBOSE("#reclaim instance heap %p done", heap); + +#if BH_ENABLE_GC_VERIFY != 0 + gci_verify_heap(heap); +#endif + +#if GC_STAT_SHOW != 0 + gc_show_stat(heap); + gc_show_fragment(heap); +#endif + + return ret; +} + +int +gc_is_dead_object(void *obj) +{ + return !hmu_is_wo_marked(obj_to_hmu(obj)); +} + +#else + +int +gci_gc_heap(void *h) +{ + (void)h; + return GC_ERROR; +} + +#endif /* end of WASM_ENABLE_GC != 0 */ diff --git a/core/shared/mem-alloc/ems/ems_gc.h b/core/shared/mem-alloc/ems/ems_gc.h index c24fb841eb..f42bdb07a3 100644 --- a/core/shared/mem-alloc/ems/ems_gc.h +++ b/core/shared/mem-alloc/ems/ems_gc.h @@ -8,8 +8,6 @@ * @date Wed Aug 3 10:46:38 2011 * * @brief This file defines GC modules types and interfaces. - * - * */ #ifndef _EMS_GC_H @@ -21,50 +19,27 @@ extern "C" { #endif -/*Pre-compile configuration can be done here or on Makefiles*/ -/*#define GC_EMBEDDED or GC_STANDALONE*/ -/*#define GC_DEBUG*/ -/*#define GC_TEST // TEST mode is a sub-mode of STANDALONE*/ -/* #define GC_ALLOC_TRACE */ -/* #define GC_STAT */ #ifndef GC_STAT_DATA -#define GC_STAT_DATA 1 -#endif - -#define GC_HEAD_PADDING 4 - -/* Standalone GC is used for testing.*/ -#ifndef GC_EMBEDDED -# ifndef GC_STANDALONE -# define GC_STANDALONE -# endif +#define GC_STAT_DATA 0 #endif -#if defined(GC_EMBEDDED) && defined(GC_STANDALONE) -# error "Can not define GC_EMBEDDED and GC_STANDALONE at the same time" +#ifndef GC_STAT_SHOW +#define GC_STAT_SHOW 0 #endif -#ifdef BH_TEST -# ifndef GC_TEST -# define GC_TEST -# endif +#ifndef GC_IN_EVERY_ALLOCATION +#define GC_IN_EVERY_ALLOCATION 0 #endif -#ifdef BH_DEBUG -/*instrument mode ignore GC_DEBUG feature, for instrument testing gc_alloc_vo_i_heap only has func_name parameter*/ -#if !defined INSTRUMENT_TEST_ENABLED && !defined GC_DEBUG -# define GC_DEBUG -#endif +#ifndef GC_MANUALLY +#define GC_MANUALLY 0 #endif -#if defined(GC_EMBEDDED) && defined(GC_TEST) -# error "Can not defined GC_EMBEDDED and GC_TEST at the same time" -#endif - -typedef void *gc_handle_t; -typedef void *gc_object_t; +#define GC_HEAD_PADDING 4 +#ifndef NULL_REF #define NULL_REF ((gc_object_t)NULL) +#endif #define GC_SUCCESS (0) #define GC_ERROR (-1) @@ -74,55 +49,17 @@ typedef void *gc_object_t; #define GC_MAX_HEAP_SIZE (256 * BH_KB) +typedef void *gc_handle_t; +typedef void *gc_object_t; +typedef uint64 gc_uint64; typedef int64 gc_int64; - -typedef unsigned int gc_uint32; -typedef signed int gc_int32; -typedef unsigned short gc_uint16; -typedef signed short gc_int16; -typedef unsigned char gc_uint8; -typedef signed char gc_int8; -typedef gc_uint32 gc_size_t; - -typedef enum { - MMT_SHARED = 0, - MMT_INSTANCE = 1, - MMT_APPMANAGER = MMT_SHARED, - MMT_VERIFIER = MMT_SHARED, - MMT_JHI = MMT_SHARED, - MMT_LOADER = MMT_SHARED, - MMT_APPLET = MMT_INSTANCE, - MMT_INTERPRETER = MMT_INSTANCE -} gc_mm_t; - -#ifdef GC_STAT -#define GC_HEAP_STAT_SIZE (128 / 4) - -typedef struct { - int usage; - int usage_block; - int vo_usage; - int jo_usage; - int free; - int free_block; - int vo_free; - int jo_free; - int usage_sizes[GC_HEAP_STAT_SIZE]; - int free_sizes[GC_HEAP_STAT_SIZE]; -}gc_stat_t; - -extern void gc_heap_stat(void* heap, gc_stat_t* gc_stat); -extern void __gc_print_stat(void *heap, int verbose); - -#define gc_print_stat __gc_print_stat - -#else - -#define gc_print_stat(heap, verbose) - -#endif - -#if GC_STAT_DATA != 0 +typedef uint32 gc_uint32; +typedef int32 gc_int32; +typedef uint16 gc_uint16; +typedef int16 gc_int16; +typedef uint8 gc_uint8; +typedef int8 gc_int8; +typedef uint32 gc_size_t; typedef enum { GC_STAT_TOTAL = 0, @@ -130,196 +67,290 @@ typedef enum { GC_STAT_HIGHMARK, GC_STAT_COUNT, GC_STAT_TIME, - GC_STAT_MAX_1, - GC_STAT_MAX_2, - GC_STAT_MAX_3, GC_STAT_MAX } GC_STAT_INDEX; +#ifndef GC_FINALIZER_T_DEFINED +#define GC_FINALIZER_T_DEFINED +typedef void (*gc_finalizer_t)(void *obj, void *data); #endif -/*////////////// Exported APIs*/ +#ifndef EXTRA_INFO_NORMAL_NODE_CNT +#define EXTRA_INFO_NORMAL_NODE_CNT 32 +#endif + +/* extra information attached to specific object */ +typedef struct extra_info_node { + gc_object_t obj; + gc_finalizer_t finalizer; + void *data; +} extra_info_node_t; /** - * GC initialization from a buffer + * GC initialization from a buffer, which is separated into + * two parts: the beginning of the buffer is used to create + * the heap structure, and the left is used to create the + * actual pool data * * @param buf the buffer to be initialized to a heap * @param buf_size the size of buffer * * @return gc handle if success, NULL otherwise */ -extern gc_handle_t gc_init_with_pool(char *buf, gc_size_t buf_size); +gc_handle_t +gc_init_with_pool(char *buf, gc_size_t buf_size); + +/** + * GC initialization from heap struct buffer and pool buffer + * + * @param struct_buf the struct buffer to create the heap structure + * @param struct_buf_size the size of struct buffer + * @param pool_buf the pool buffer to create pool data + * @param pool_buf_size the size of poll buffer + * + * @return gc handle if success, NULL otherwise + */ +gc_handle_t +gc_init_with_struct_and_pool(char *struct_buf, gc_size_t struct_buf_size, + char *pool_buf, gc_size_t pool_buf_size); /** - * Destroy heap which is initilized from a buffer + * Destroy heap which is initialized from a buffer * * @param handle handle to heap needed destroy * * @return GC_SUCCESS if success * GC_ERROR for bad parameters or failed system resource freeing. */ -extern int gc_destroy_with_pool(gc_handle_t handle); - -#if GC_STAT_DATA != 0 +int +gc_destroy_with_pool(gc_handle_t handle); +#if WASM_ENABLE_GC != 0 /** - * Get Heap Stats + * Enable or disable GC reclaim for a heap * - * @param stats [out] integer array to save heap stats - * @param size [in] the size of stats - * @param mmt [in] type of heap, MMT_SHARED or MMT_INSTANCE + * @param handle handle of the heap + * @param exec_env the exec_env of current module instance */ -extern void* gc_heap_stats(void *heap, uint32* stats, int size, gc_mm_t mmt); - +#if WASM_ENABLE_THREAD_MGR == 0 +void +gc_enable_gc_reclaim(gc_handle_t handle, void *exec_env); +#else /** - * Set GC threshold factor - * - * @param heap [in] the heap to set - * @param factor [in] the threshold size is free_size * factor / 1000 + * Enable or disable GC reclaim for a heap * - * @return GC_SUCCESS if success. + * @param handle handle of the heap + * @param cluster the tread cluster of current module instance */ -extern int gc_set_threshold_factor(void *heap, unsigned int factor); - +void +gc_enable_gc_reclaim(gc_handle_t handle, void *cluster); #endif - -/*////// Allocate heap object*/ - -/* There are two versions of allocate functions. The functions with _i suffix should be only used*/ -/* internally. Functions without _i suffix are just wrappers with the corresponded functions with*/ -/* _i suffix. Allocation operation code position are record under DEBUG model for debugging.*/ -#ifdef GC_DEBUG -# define ALLOC_EXTRA_PARAMETERS ,const char*file_name,int line_number -# define ALLOC_EXTRA_ARGUMENTS , __FILE__, __LINE__ -# define ALLOC_PASSDOWN_EXTRA_ARGUMENTS , file_name, line_number -# define gc_alloc_vo_h(heap, size) gc_alloc_vo_i_heap(heap, size, __FILE__, __LINE__) -# define gc_free_h(heap, obj) gc_free_i_heap(heap, obj, __FILE__, __LINE__) -#else -# define ALLOC_EXTRA_PARAMETERS -# define ALLOC_EXTRA_ARGUMENTS -# define ALLOC_PASSDOWN_EXTRA_ARGUMENTS -# define gc_alloc_vo_h gc_alloc_vo_i_heap -# define gc_free_h gc_free_i_heap #endif /** - * Invoke a GC - * - * @param heap - * - * @return GC_SUCCESS if success + * Return heap struct size */ -extern int gci_gc_heap(void *heap); +uint32 +gc_get_heap_struct_size(void); /** - * Allocate VM Object in specific heap. + * Migrate heap from one pool buf to another pool buf * - * @param heap heap to allocate. - * @param size bytes to allocate. + * @param handle handle of the new heap + * @param pool_buf_new the new pool buffer + * @param pool_buf_size the size of new pool buffer * - * @return pointer to VM object allocated - * NULL if failed. + * @return GC_SUCCESS if success, GC_ERROR otherwise */ -extern gc_object_t _gc_alloc_vo_i_heap(void *heap, - gc_size_t size ALLOC_EXTRA_PARAMETERS); -extern gc_object_t _gc_alloc_jo_i_heap(void *heap, - gc_size_t size ALLOC_EXTRA_PARAMETERS); -#ifdef INSTRUMENT_TEST_ENABLED -extern gc_object_t gc_alloc_vo_i_heap_instr(void *heap, gc_size_t size, const char* func_name ); -extern gc_object_t gc_alloc_jo_i_heap_instr(void *heap, gc_size_t size, const char* func_name); -# define gc_alloc_vo_i_heap(heap, size) gc_alloc_vo_i_heap_instr(heap, size, __FUNCTION__) -# define gc_alloc_jo_i_heap(heap, size) gc_alloc_jo_i_heap_instr(heap, size, __FUNCTION__) -#else -# define gc_alloc_vo_i_heap _gc_alloc_vo_i_heap -# define gc_alloc_jo_i_heap _gc_alloc_jo_i_heap -#endif +int +gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size); /** - * Allocate Java object in specific heap. + * Check whether the heap is corrupted * - * @param heap heap to allocate. - * @param size bytes to allocate. + * @param handle handle of the heap * - * @return pointer to Java object allocated - * NULL if failed. + * @return true if success, false otherwise */ -extern gc_object_t _gc_alloc_jo_i_heap(void *heap, - gc_size_t size ALLOC_EXTRA_PARAMETERS); +bool +gc_is_heap_corrupted(gc_handle_t handle); /** - * Free VM object - * - * @param heap heap to free. - * @param obj pointer to object need free. + * Get Heap Stats * - * @return GC_SUCCESS if success + * @param stats [out] integer array to save heap stats + * @param size [in] the size of stats + * @param mmt [in] type of heap, MMT_SHARED or MMT_INSTANCE */ -extern int gc_free_i_heap(void *heap, gc_object_t obj ALLOC_EXTRA_PARAMETERS); +void * +gc_heap_stats(void *heap, uint32 *stats, int size); -/** - * Add ref to rootset of gc for current instance. - * - * @param obj pointer to real load of a valid Java object managed by gc for current instance. - * - * @return GC_SUCCESS if success. - * GC_ERROR for invalid parameters. - */ -extern int gc_add_root(void* heap, gc_object_t obj); +#if BH_ENABLE_GC_VERIFY == 0 -/*////////////// Imported APIs which should be implemented in other components*/ +gc_object_t +gc_alloc_vo(void *heap, gc_size_t size); -/*////// Java object layout related APIs*/ +gc_object_t +gc_realloc_vo(void *heap, void *ptr, gc_size_t size); -/** - * Get Java object size from corresponding VM module - * - * @param obj pointer to the real load of a Java object. - * - * @return size of java object. - */ -extern gc_size_t vm_get_java_object_size(gc_object_t obj); +gc_object_t +gc_alloc_vo_aligned(void *heap, gc_size_t size, gc_size_t alignment); + +int +gc_free_vo(void *heap, gc_object_t obj); + +#if WASM_ENABLE_GC != 0 +gc_object_t +gc_alloc_wo(void *heap, gc_size_t size); + +void +gc_free_wo(void *vheap, void *ptr); +#endif + +#else /* else of BH_ENABLE_GC_VERIFY */ + +gc_object_t +gc_alloc_vo_internal(void *heap, gc_size_t size, const char *file, int line); + +gc_object_t +gc_realloc_vo_internal(void *heap, void *ptr, gc_size_t size, const char *file, + int line); + +gc_object_t +gc_alloc_vo_aligned_internal(void *heap, gc_size_t size, gc_size_t alignment, + const char *file, int line); + +int +gc_free_vo_internal(void *heap, gc_object_t obj, const char *file, int line); + +#if WASM_ENABLE_GC != 0 +gc_object_t +gc_alloc_wo_internal(void *heap, gc_size_t size, const char *file, int line); + +void +gc_free_wo_internal(void *vheap, void *ptr, const char *file, int line); +#endif + +/* clang-format off */ +#define gc_alloc_vo(heap, size) \ + gc_alloc_vo_internal(heap, size, __FILE__, __LINE__) + +#define gc_realloc_vo(heap, ptr, size) \ + gc_realloc_vo_internal(heap, ptr, size, __FILE__, __LINE__) + +#define gc_alloc_vo_aligned(heap, size, alignment) \ + gc_alloc_vo_aligned_internal(heap, size, alignment, __FILE__, __LINE__) +#define gc_free_vo(heap, obj) \ + gc_free_vo_internal(heap, obj, __FILE__, __LINE__) + +#if WASM_ENABLE_GC != 0 +#define gc_alloc_wo(heap, size) \ + gc_alloc_wo_internal(heap, size, __FILE__, __LINE__) + +#define gc_free_wo(heap, obj) \ + gc_free_wo_internal(heap, obj, __FILE__, __LINE__) +#endif +/* clang-format on */ + +#endif /* end of BH_ENABLE_GC_VERIFY */ + +#if WASM_ENABLE_GC != 0 /** - * Get reference list of this object + * Add gc object ref to the rootset of a gc heap. * - * @param obj [in] pointer to java object. - * @param is_compact_mode [in] indicate the java object mode. GC_TRUE or GC_FALSE. - * @param ref_num [out] the size of ref_list. - * @param ref_list [out] if is_compact_mode is GC_FALSE, this parameter will be set to a list of offset. - * @param ref_start_offset [out] If is_compact_mode is GC_TRUE, this parameter will be set to the start offset of the references in this object. + * @param heap the heap to add the gc object to its rootset + * @param obj pointer to a valid WASM object managed by the gc heap. * - * @return GC_SUCCESS if success. - * GC_ERROR when error occurs. + * @return GC_SUCCESS if success, GC_ERROR otherwise */ -extern int vm_get_java_object_ref_list(gc_object_t obj, int *is_compact_mode, - gc_size_t *ref_num, gc_uint16 **ref_list, gc_uint32 *ref_start_offset); +int +gc_add_root(void *heap, gc_object_t obj); + +int +gci_gc_heap(void *heap); + +extra_info_node_t * +gc_search_extra_info_node(gc_handle_t handle, gc_object_t obj, + gc_size_t *p_index); /** - * Get gc handle for current instance + * Set finalizer to the given object, if another finalizer is set to the same + * object, the previous one will be cancelled * + * @param handle handle of the heap + * @param obj object to set finalizer + * @param cb finalizer function to be called before this object is freed + * @param data custom data to be passed to finalizer function * - * @return instance heap handle. + * @return true if success, false otherwise */ -extern gc_handle_t app_manager_get_cur_applet_heap(void); +bool +gc_set_finalizer(gc_handle_t handle, gc_object_t obj, gc_finalizer_t cb, + void *data); /** - * Begin current instance heap rootset enumeration - * + * Unset finalizer to the given object * - * @return GC_SUCCESS if success. - * GC_ERROR when error occurs. + * @param handle handle of the heap + * @param obj object to unset finalizer */ -extern int vm_begin_rootset_enumeration(void *heap); +void +gc_unset_finalizer(gc_handle_t handle, gc_object_t obj); -#ifdef _INSTRUMENT_TEST_ENABLED -extern int vm_begin_rootset_enumeration_instr(void *heap, const char*func_name); -#define vm_begin_rootset_enumeration(heap) vm_begin_rootset_enumeration_instr(heap, __FUNCTION__) +#if WASM_ENABLE_THREAD_MGR == 0 +bool +wasm_runtime_traverse_gc_rootset(void *exec_env, void *heap); #else -#define vm_begin_rootset_enumeration _vm_begin_rootset_enumeration -#endif /* INSTUMENT_TEST_ENABLED*/ +bool +wasm_runtime_traverse_gc_rootset(void *cluster, void *heap); +#endif + +bool +wasm_runtime_get_wasm_object_ref_list(gc_object_t obj, bool *p_is_compact_mode, + gc_uint32 *p_ref_num, + gc_uint16 **p_ref_list, + gc_uint32 *p_ref_start_offset); + +bool +wasm_runtime_get_wasm_object_extra_info_flag(gc_object_t obj); + +void +wasm_runtime_set_wasm_object_extra_info_flag(gc_object_t obj, bool set); + +void +wasm_runtime_gc_prepare(void *exec_env); + +void +wasm_runtime_gc_finalize(void *exec_env); +#endif /* end of WASM_ENABLE_GC != 0 */ + +#define GC_HEAP_STAT_SIZE (128 / 4) -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) +typedef struct { + int usage; + int usage_block; + int vo_usage; + int wo_usage; + int free; + int free_block; + int vo_free; + int wo_free; + int usage_sizes[GC_HEAP_STAT_SIZE]; + int free_sizes[GC_HEAP_STAT_SIZE]; +} gc_stat_t; + +void +gc_show_stat(gc_handle_t handle); + +#if WASM_ENABLE_GC != 0 +void +gc_show_fragment(gc_handle_t handle); + +#if WASM_ENABLE_GC_PERF_PROFILING != 0 +void +gc_dump_perf_profiling(gc_handle_t *handle); +#endif #endif #ifdef __cplusplus @@ -327,4 +358,3 @@ extern int vm_begin_rootset_enumeration_instr(void *heap, const char*func_name); #endif #endif - diff --git a/core/shared/mem-alloc/ems/ems_gc_internal.h b/core/shared/mem-alloc/ems/ems_gc_internal.h index 94efb60cbf..417364bab4 100644 --- a/core/shared/mem-alloc/ems/ems_gc_internal.h +++ b/core/shared/mem-alloc/ems/ems_gc_internal.h @@ -11,261 +11,569 @@ extern "C" { #endif #include "bh_platform.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "bh_assert.h" #include "ems_gc.h" -/* basic block managed by EMS gc is the so-called HMU (heap memory unit)*/ -typedef enum _hmu_type_enum -{ +/* HMU (heap memory unit) basic block type */ +typedef enum hmu_type_enum { HMU_TYPE_MIN = 0, HMU_TYPE_MAX = 3, - HMU_JO = 3, - HMU_VO = 2, + HMU_WO = 3, /* WASM Object */ + HMU_VO = 2, /* VM Object */ HMU_FC = 1, HMU_FM = 0 -}hmu_type_t; +} hmu_type_t; -typedef struct _hmu_struct -{ +typedef struct hmu_struct { gc_uint32 header; -}hmu_t; +} hmu_t; -#if defined(GC_VERIFY) +#if BH_ENABLE_GC_VERIFY != 0 +#if UINTPTR_MAX > UINT32_MAX +/* 2 prefix paddings for 64-bit pointer */ +#define GC_OBJECT_PREFIX_PADDING_CNT 2 +#else +/* 3 prefix paddings for 32-bit pointer */ #define GC_OBJECT_PREFIX_PADDING_CNT 3 +#endif #define GC_OBJECT_SUFFIX_PADDING_CNT 4 #define GC_OBJECT_PADDING_VALUE (0x12345678) -typedef struct _gc_object_prefix -{ +typedef struct gc_object_prefix { const char *file_name; gc_int32 line_no; gc_int32 size; gc_uint32 padding[GC_OBJECT_PREFIX_PADDING_CNT]; -}gc_object_prefix_t; +} gc_object_prefix_t; -#define OBJ_PREFIX_SIZE (sizeof(gc_object_prefix_t)) - -typedef struct _gc_object_suffix -{ +typedef struct gc_object_suffix { gc_uint32 padding[GC_OBJECT_SUFFIX_PADDING_CNT]; -}gc_object_suffix_t; +} gc_object_suffix_t; +#define OBJ_PREFIX_SIZE (sizeof(gc_object_prefix_t)) #define OBJ_SUFFIX_SIZE (sizeof(gc_object_suffix_t)) -extern void hmu_init_prefix_and_suffix(hmu_t *hmu, gc_size_t tot_size, const char *file_name, int line_no); -extern void hmu_verify(hmu_t *hmu); +void +hmu_init_prefix_and_suffix(hmu_t *hmu, gc_size_t tot_size, + const char *file_name, int line_no); + +void +hmu_verify(void *vheap, hmu_t *hmu); -#define SKIP_OBJ_PREFIX(p) ((void*)((gc_uint8*)(p) + OBJ_PREFIX_SIZE)) -#define SKIP_OBJ_SUFFIX(p) ((void*)((gc_uint8*)(p) + OBJ_SUFFIX_SIZE)) +#define SKIP_OBJ_PREFIX(p) ((void *)((gc_uint8 *)(p) + OBJ_PREFIX_SIZE)) +#define SKIP_OBJ_SUFFIX(p) ((void *)((gc_uint8 *)(p) + OBJ_SUFFIX_SIZE)) #define OBJ_EXTRA_SIZE (HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE) -#else +#else /* else of BH_ENABLE_GC_VERIFY */ #define OBJ_PREFIX_SIZE 0 #define OBJ_SUFFIX_SIZE 0 -#define SKIP_OBJ_PREFIX(p) ((void*)((gc_uint8*)(p) + OBJ_PREFIX_SIZE)) -#define SKIP_OBJ_SUFFIX(p) ((void*)((gc_uint8*)(p) + OBJ_SUFFIX_SIZE)) +#define SKIP_OBJ_PREFIX(p) ((void *)((gc_uint8 *)(p) + OBJ_PREFIX_SIZE)) +#define SKIP_OBJ_SUFFIX(p) ((void *)((gc_uint8 *)(p) + OBJ_SUFFIX_SIZE)) #define OBJ_EXTRA_SIZE (HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE) -#endif /* GC_DEBUG*/ +#endif /* end of BH_ENABLE_GC_VERIFY */ #define hmu_obj_size(s) ((s)-OBJ_EXTRA_SIZE) #define GC_ALIGN_8(s) (((uint32)(s) + 7) & (uint32)~7) -#define GC_SMALLEST_SIZE GC_ALIGN_8(HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE + 8) -#define GC_GET_REAL_SIZE(x) GC_ALIGN_8(HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE + (((x) > 8) ? (x): 8)) +/* Minimum alignment for allocations */ +#ifndef GC_MIN_ALIGNMENT +#define GC_MIN_ALIGNMENT 8 +#endif -/*////// functions for bit operation*/ +/* Smallest allocation size for normal allocations + * The +8 ensures minimum allocation size for tree node structure */ +#define GC_SMALLEST_SIZE GC_ALIGN_8(OBJ_EXTRA_SIZE + 8) -#define SETBIT(v, offset) (v) |= (1 << (offset)) -#define GETBIT(v, offset) ((v) & (1 << (offset)) ? 1 : 0) -#define CLRBIT(v, offset) (v) &= (uint32)(~(1 << (offset))) +#define GC_GET_REAL_SIZE(x) GC_ALIGN_8(OBJ_EXTRA_SIZE + (((x) > 8) ? (x) : 8)) -#define SETBITS(v, offset, size, value) do { \ - (v) &= (uint32)(~(((1 << size) - 1) << offset));\ - (v) |= (uint32)(value << offset); \ - } while(0) -#define CLRBITS(v, offset, size) (v) &= ~(((1 << size) - 1) << offset) -#define GETBITS(v, offset, size) (((v) & ((uint32)(((1 << size) - 1) << offset))) >> offset) +/* + * ============================================================================ + * Aligned Memory Allocation + * ============================================================================ + * + * This module implements aligned memory allocation similar to C11 + * aligned_alloc() and POSIX posix_memalign() for WAMR's garbage collector. + * + * POSIX aligned_alloc() Specification: + * ------------------------------------ + * From C11 §7.22.3.1 and POSIX.1-2017: + * void *aligned_alloc(size_t alignment, size_t size); + * + * Requirements: + * - alignment: Must be a valid alignment supported by the implementation, + * typically a power of 2 + * - size: Must be an integral multiple of alignment + * - Returns: Pointer aligned to the specified alignment boundary, or NULL + * - Memory must be freed with free() (not realloc'd) + * - Behavior: If size is 0, may return NULL or unique pointer (impl-defined) + * + * IMPORTANT: POSIX does not require realloc() to preserve alignment. + * Calling realloc() on aligned_alloc() memory has undefined behavior. + * + * WAMR Implementation Strategy: + * ----------------------------- + * We implement alignment through over-allocation with metadata tracking: + * + * 1. **Validation Phase**: + * - Check alignment is power-of-2, >= 8 bytes, <= system page size + * - Check size is multiple of alignment + * - Return NULL if validation fails + * + * 2. **Over-Allocation**: + * - Allocate (size + alignment + metadata_overhead) bytes + * - Extra space allows us to find an aligned boundary within the block + * - Calculate log2(alignment) for efficient offset storage + * + * 3. **Alignment Adjustment**: + * - Find next aligned address within allocated block + * - Calculate offset from original allocation to aligned address + * - Store offset in metadata for later free() operation + * + * 4. **Magic Marker Storage**: + * - Store magic marker (0xA11C0000 | offset) in 4 bytes before user pointer + * - Upper 16 bits: 0xA11C identifies aligned allocation + * - Lower 16 bits: offset from HMU to aligned pointer (max 65535 bytes) + * - This marker prevents unsafe realloc() operations + * + * 5. **Realloc Prevention**: + * - gc_realloc_vo_internal() checks for magic marker + * - Returns NULL if realloc attempted on aligned allocation + * - User must manually allocate new memory and copy data + * + * Memory Layout Diagram: + * ---------------------- + * + * Low Address High Address + * ┌─────────────┬──────────┬─────────┬─────────┬──────────────┬─────────────┐ + * │ HMU Header │ Padding │ Offset │ Magic │ Aligned Data │ Padding │ + * │ (4 bytes) │(variable)│(4 bytes)│(4 bytes)│ (size) │ (overhead) │ + * └─────────────┴──────────┴─────────┴─────────┴──────────────┴─────────────┘ + * ▲ └────8 bytes────┘ ▲ + * hmu user_ptr (returned, aligned) + * + * Padding is variable-length to satisfy alignment constraint: + * align_up(HMU_SIZE + ALIGNED_ALLOC_METADATA_SIZE, alignment) + * For alignment >= 12: HMU_SIZE + padding + 8 = alignment + * For alignment < 12: HMU_SIZE + padding + 8 = round_up(12, alignment) + * + * Constraints and Limitations: + * ---------------------------- + * - Minimum alignment: 8 bytes (GC_MIN_ALIGNMENT) + * - Maximum alignment: System page size (os_getpagesize(), typically 4KB) + * - Maximum offset: 65535 bytes (16-bit storage limit) + * - Realloc support: None - returns NULL (prevents alignment loss) + * - Free support: Full - use mem_allocator_free() / wasm_runtime_free() + * - Thread safety: Protected by LOCK_HEAP/UNLOCK_HEAP + * + * Usage Example: + * -------------- + * // Allocate 256 bytes aligned to 64-byte boundary (e.g., for SIMD) + * void *ptr = wasm_runtime_aligned_alloc(256, 64); + * assert((uintptr_t)ptr % 64 == 0); // Guaranteed aligned + * + * // Use the memory... + * + * // Free normally (alignment metadata handled automatically) + * wasm_runtime_free(ptr); + * + * // INVALID: Cannot realloc aligned memory + * void *new_ptr = wasm_runtime_realloc(ptr, 512); // Returns NULL! + */ -/*////// gc object layout definition*/ +/* Aligned allocation constants */ +/* Size of offset field before aligned ptr */ +#define ALIGNED_ALLOC_OFFSET_SIZE 4 +/* Size of magic marker before aligned ptr */ +#define ALIGNED_ALLOC_MAGIC_SIZE 4 +/* Total: 8 bytes */ +#define ALIGNED_ALLOC_METADATA_SIZE \ + (ALIGNED_ALLOC_OFFSET_SIZE + ALIGNED_ALLOC_MAGIC_SIZE) + +/* Aligned allocation magic markers */ +#define ALIGNED_ALLOC_MAGIC_MASK 0xFFFF0000 +#define ALIGNED_ALLOC_MAGIC_VALUE 0xA11C0000 + +/* Get magic pointer from aligned object pointer */ +#define ALIGNED_ALLOC_GET_MAGIC_PTR(obj) \ + ((uint32_t *)((char *)(obj)-ALIGNED_ALLOC_MAGIC_SIZE)) + +/* Get offset pointer from aligned object pointer */ +#define ALIGNED_ALLOC_GET_OFFSET_PTR(obj) \ + ((uint32_t *)((char *)(obj)-ALIGNED_ALLOC_METADATA_SIZE)) + +/* Extra overhead for aligned allocations beyond normal OBJ_EXTRA_SIZE */ +#define ALIGNED_ALLOC_EXTRA_OVERHEAD ALIGNED_ALLOC_METADATA_SIZE + +/* Smallest allocation size for aligned allocations */ +#define GC_ALIGNED_SMALLEST_SIZE(alignment) \ + GC_ALIGN_8(OBJ_EXTRA_SIZE + ALIGNED_ALLOC_METADATA_SIZE \ + + ((alignment) > 8 ? (alignment - 8) : 8)) + +/** + * Check if a gc_object was allocated with alignment requirements. + * + * Aligned allocations store a magic marker (0xA11C0000) in the 4 bytes + * immediately before the object pointer. This marker is used to identify + * aligned allocations to prevent unsafe realloc operations. + * + * @param obj the gc_object to check (user-visible pointer) + * @return true if obj is an aligned allocation, false otherwise + */ +static inline bool +gc_is_aligned_allocation(gc_object_t obj) +{ + if (!obj) + return false; + + uint32_t *magic_ptr = ALIGNED_ALLOC_GET_MAGIC_PTR(obj); + return ((*magic_ptr & ALIGNED_ALLOC_MAGIC_MASK) + == ALIGNED_ALLOC_MAGIC_VALUE); +} + +/** + * hmu bit operation + */ + +#define SETBIT(v, offset) (v) |= ((uint32)1 << (offset)) +#define GETBIT(v, offset) ((v) & ((uint32)1 << (offset)) ? 1 : 0) +#define CLRBIT(v, offset) (v) &= (~((uint32)1 << (offset))) + +/* clang-format off */ +#define SETBITS(v, offset, size, value) \ + do { \ + (v) &= ~((((uint32)1 << size) - 1) << offset); \ + (v) |= ((uint32)value << offset); \ + } while (0) +#define CLRBITS(v, offset, size) \ + (v) &= ~((((uint32)1 << size) - 1) << offset) +#define GETBITS(v, offset, size) \ + (((v) & (((((uint32)1 << size) - 1) << offset))) >> offset) + +/** + * gc object layout definition + * + * #### Header Bit Layout + * + * ``` + * 31 30 29 28 27 0 + * ┌──┬──┬──┬──┬───────────────────────────────────────────────────┐ + * │UT│UT│ P│ *│ Size or Type-Specific Data │ + * └──┴──┴──┴──┴───────────────────────────────────────────────────┘ + * ``` + * + * #### Bit Fields Breakdown + * + * | Bits | Field | Description | + * | --------- | ----------------------- | -------------------------------------------- | + * | **31-30** | **UT** (Usage Type) | 2 bits for chunk type | + * | **29** | **P** (Previous In Use) | 1 bit indicating if previous chunk is in use | + * | **28** | **Type-specific** | Meaning depends on UT field | + * | **27-0** | **Type-specific** | Size or other data depending on UT | + * + * #### Memory Layout in Heap + * + * ``` + * ┌─────────────────────────────────────────────────────────────┐ + * │ HMU Header (4 bytes) │ + * ├─────────────────────────────────────────────────────────────┤ + * │ OBJ_PREFIX (if BH_ENABLE_GC_VERIFY) │ + * │ - file_name pointer │ + * │ - line_no │ + * │ - size │ + * │ - padding values (for corruption detection) │ + * ├─────────────────────────────────────────────────────────────┤ + * │ User Data (aligned to 8 bytes) │ + * │ ... │ + * ├─────────────────────────────────────────────────────────────┤ + * │ OBJ_SUFFIX (if BH_ENABLE_GC_VERIFY) │ + * │ - padding values (for corruption detection) │ + * └─────────────────────────────────────────────────────────────┘ + * ``` + */ +/* clang-format on */ #define HMU_SIZE (sizeof(hmu_t)) -#define hmu_to_obj(hmu) (gc_object_t)(SKIP_OBJ_PREFIX((hmu_t*) (hmu) + 1)) -#define obj_to_hmu(obj) ((hmu_t *)((gc_uint8*)(obj) - OBJ_PREFIX_SIZE) - 1) +#define hmu_to_obj(hmu) (gc_object_t)(SKIP_OBJ_PREFIX((hmu_t *)(hmu) + 1)) -#define HMU_UT_SIZE 2 -#define HMU_UT_OFFSET 30 +/* obj_to_hmu function - handles both normal and aligned allocations */ +hmu_t * +obj_to_hmu(gc_object_t obj); -#define hmu_get_ut(hmu) GETBITS ((hmu)->header, HMU_UT_OFFSET, HMU_UT_SIZE) -#define hmu_set_ut(hmu, type) SETBITS ((hmu)->header, HMU_UT_OFFSET, HMU_UT_SIZE, type) -#define hmu_is_ut_valid(tp) (tp >= HMU_TYPE_MIN && tp <= HMU_TYPE_MAX) +#define HMU_UT_SIZE 2 +#define HMU_UT_OFFSET 30 + +/* clang-format off */ +#define hmu_get_ut(hmu) \ + GETBITS((hmu)->header, HMU_UT_OFFSET, HMU_UT_SIZE) +#define hmu_set_ut(hmu, type) \ + SETBITS((hmu)->header, HMU_UT_OFFSET, HMU_UT_SIZE, type) +#define hmu_is_ut_valid(tp) \ + (tp >= HMU_TYPE_MIN && tp <= HMU_TYPE_MAX) +/* clang-format on */ /* P in use bit means the previous chunk is in use */ #define HMU_P_OFFSET 29 -#define hmu_mark_pinuse(hmu) SETBIT ((hmu)->header, HMU_P_OFFSET) -#define hmu_unmark_pinuse(hmu) CLRBIT ((hmu)->header, HMU_P_OFFSET) -#define hmu_get_pinuse(hmu) GETBIT ((hmu)->header, HMU_P_OFFSET) +#define hmu_mark_pinuse(hmu) SETBIT((hmu)->header, HMU_P_OFFSET) +#define hmu_unmark_pinuse(hmu) CLRBIT((hmu)->header, HMU_P_OFFSET) +#define hmu_get_pinuse(hmu) GETBIT((hmu)->header, HMU_P_OFFSET) -#define HMU_JO_VT_SIZE 27 -#define HMU_JO_VT_OFFSET 0 -#define HMU_JO_MB_OFFSET 28 +#define HMU_WO_VT_SIZE 27 +#define HMU_WO_VT_OFFSET 0 +#define HMU_WO_MB_OFFSET 28 -#define hmu_mark_jo(hmu) SETBIT ((hmu)->header, HMU_JO_MB_OFFSET) -#define hmu_unmark_jo(hmu) CLRBIT ((hmu)->header, HMU_JO_MB_OFFSET) -#define hmu_is_jo_marked(hmu) GETBIT ((hmu)->header, HMU_JO_MB_OFFSET) +#define hmu_mark_wo(hmu) SETBIT((hmu)->header, HMU_WO_MB_OFFSET) +#define hmu_unmark_wo(hmu) CLRBIT((hmu)->header, HMU_WO_MB_OFFSET) +#define hmu_is_wo_marked(hmu) GETBIT((hmu)->header, HMU_WO_MB_OFFSET) +/** + * The hmu size is divisible by 8, its lowest 3 bits are 0, so we only + * store its higher bits of bit [29..3], and bit [2..0] are not stored. + * After that, the maximal heap size can be enlarged from (1<<27) = 128MB + * to (1<<27) * 8 = 1GB. + */ #define HMU_SIZE_SIZE 27 #define HMU_SIZE_OFFSET 0 #define HMU_VO_FB_OFFSET 28 -#define hmu_is_vo_freed(hmu) GETBIT ((hmu)->header, HMU_VO_FB_OFFSET) -#define hmu_unfree_vo(hmu) CLRBIT ((hmu)->header, HMU_VO_FB_OFFSET) +#define hmu_is_vo_freed(hmu) GETBIT((hmu)->header, HMU_VO_FB_OFFSET) +#define hmu_unfree_vo(hmu) CLRBIT((hmu)->header, HMU_VO_FB_OFFSET) -#define hmu_get_size(hmu) GETBITS ((hmu)->header, HMU_SIZE_OFFSET, HMU_SIZE_SIZE) -#define hmu_set_size(hmu, size) SETBITS ((hmu)->header, HMU_SIZE_OFFSET, HMU_SIZE_SIZE, size) +#define hmu_get_size(hmu) \ + (GETBITS((hmu)->header, HMU_SIZE_OFFSET, HMU_SIZE_SIZE) << 3) +#define hmu_set_size(hmu, size) \ + SETBITS((hmu)->header, HMU_SIZE_OFFSET, HMU_SIZE_SIZE, ((size) >> 3)) -/*////// HMU free chunk management*/ +/** + * HMU free chunk management + */ +#ifndef HMU_NORMAL_NODE_CNT #define HMU_NORMAL_NODE_CNT 32 +#endif #define HMU_FC_NORMAL_MAX_SIZE ((HMU_NORMAL_NODE_CNT - 1) << 3) #define HMU_IS_FC_NORMAL(size) ((size) < HMU_FC_NORMAL_MAX_SIZE) #if HMU_FC_NORMAL_MAX_SIZE >= GC_MAX_HEAP_SIZE #error "Too small GC_MAX_HEAP_SIZE" #endif -typedef struct _hmu_normal_node -{ +typedef struct hmu_normal_node { hmu_t hmu_header; - struct _hmu_normal_node *next; -}hmu_normal_node_t; + gc_int32 next_offset; +} hmu_normal_node_t; + +typedef struct hmu_normal_list { + hmu_normal_node_t *next; +} hmu_normal_list_t; -typedef struct _hmu_tree_node +static inline hmu_normal_node_t * +get_hmu_normal_node_next(hmu_normal_node_t *node) { + return node->next_offset + ? (hmu_normal_node_t *)((uint8 *)node + node->next_offset) + : NULL; +} + +static inline void +set_hmu_normal_node_next(hmu_normal_node_t *node, hmu_normal_node_t *next) +{ + if (next) { + bh_assert((uint8 *)next - (uint8 *)node < INT32_MAX); + node->next_offset = (gc_int32)(intptr_t)((uint8 *)next - (uint8 *)node); + } + else { + node->next_offset = 0; + } +} + +/** + * Define hmu_tree_node as a packed struct, since it is at the 4-byte + * aligned address and the size of hmu_head is 4, so in 64-bit target, + * the left/right/parent fields will be at 8-byte aligned address, + * we can access them directly. + */ +#if UINTPTR_MAX == UINT64_MAX +#if defined(_MSC_VER) +__pragma(pack(push, 1)); +#define __attr_packed +#define __attr_aligned(a) +#elif defined(__GNUC__) || defined(__clang__) +#define __attr_packed __attribute__((packed)) +#define __attr_aligned(a) __attribute__((aligned(a))) +#else +#error "packed attribute isn't used to define struct hmu_tree_node" +#endif +#else /* else of UINTPTR_MAX == UINT64_MAX */ +#define __attr_packed +#define __attr_aligned(a) +#endif + +typedef struct hmu_tree_node { hmu_t hmu_header; + struct hmu_tree_node *left; + struct hmu_tree_node *right; + struct hmu_tree_node *parent; gc_size_t size; - struct _hmu_tree_node *left; - struct _hmu_tree_node *right; - struct _hmu_tree_node *parent; -}hmu_tree_node_t; +} __attr_packed __attr_aligned(4) hmu_tree_node_t; -typedef struct _gc_heap_struct -{ - gc_handle_t heap_id; /* for double checking*/ +#if UINTPTR_MAX == UINT64_MAX +#if defined(_MSC_VER) +__pragma(pack(pop)); +#endif +#endif + +bh_static_assert(sizeof(hmu_tree_node_t) == 8 + 3 * sizeof(void *)); +bh_static_assert(offsetof(hmu_tree_node_t, left) == 4); + +#define ASSERT_TREE_NODE_ALIGNED_ACCESS(tree_node) \ + do { \ + bh_assert((((uintptr_t)&tree_node->left) & (sizeof(uintptr_t) - 1)) \ + == 0); \ + } while (0) + +typedef struct gc_heap_struct { + /* for double checking*/ + gc_handle_t heap_id; gc_uint8 *base_addr; gc_size_t current_size; - gc_size_t max_size; korp_mutex lock; - hmu_normal_node_t kfc_normal_list[HMU_NORMAL_NODE_CNT]; + hmu_normal_list_t kfc_normal_list[HMU_NORMAL_NODE_CNT]; - /* order in kfc_tree is: size[left] <= size[cur] < size[right]*/ - hmu_tree_node_t kfc_tree_root; +#if UINTPTR_MAX == UINT64_MAX + /* make kfc_tree_root_buf 4-byte aligned and not 8-byte aligned, + so kfc_tree_root's left/right/parent fields are 8-byte aligned + and we can access them directly */ + uint32 __padding; +#endif + uint8 kfc_tree_root_buf[sizeof(hmu_tree_node_t)]; + /* point to kfc_tree_root_buf, the order in kfc_tree is: + size[left] <= size[cur] < size[right] */ + hmu_tree_node_t *kfc_tree_root; +#if WASM_ENABLE_GC != 0 /* for rootset enumeration of private heap*/ void *root_set; +#if WASM_ENABLE_THREAD_MGR == 0 + /* exec_env of current wasm module instance */ + void *exec_env; +#else + /* thread cluster of current module instances */ + void *cluster; +#endif + /* whether the fast mode of marking process that requires - additional memory fails. When the fast mode fails, the - marking process can still be done in the slow mode, which - doesn't need additional memory (by walking through all - blocks and marking sucessors of marked nodes until no new - node is marked). TODO: slow mode is not implemented. */ + additional memory fails. When the fast mode fails, the + marking process can still be done in the slow mode, which + doesn't need additional memory (by walking through all + blocks and marking successors of marked nodes until no new + node is marked). TODO: slow mode is not implemented. */ unsigned is_fast_marking_failed : 1; -#if GC_STAT_DATA != 0 - gc_size_t highmark_size; + /* whether the heap is doing reclaim */ + unsigned is_doing_reclaim : 1; + + /* Whether the heap can do reclaim */ + unsigned is_reclaim_enabled : 1; +#endif + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + /* whether heap is corrupted, e.g. the hmu nodes are modified + by user */ + bool is_heap_corrupted; +#endif + gc_size_t init_size; - gc_size_t total_gc_count; + gc_size_t highmark_size; gc_size_t total_free_size; + +#if WASM_ENABLE_GC != 0 gc_size_t gc_threshold; gc_size_t gc_threshold_factor; - gc_int64 total_gc_time; + gc_size_t total_gc_count; + gc_size_t total_gc_time; + gc_size_t max_gc_time; + /* Usually there won't be too many extra info node, so we try to use a fixed + * array to store them, if the fixed array don't have enough space to store + * the nodes, a new space will be allocated from heap */ + extra_info_node_t *extra_info_normal_nodes[EXTRA_INFO_NORMAL_NODE_CNT]; + /* Used to store extra information such as finalizer for specified nodes, we + * introduce a separate space to store these information so only nodes who + * really require extra information will occupy additional memory spaces. */ + extra_info_node_t **extra_info_nodes; + gc_size_t extra_info_node_cnt; + gc_size_t extra_info_node_capacity; #endif -}gc_heap_t; - -/*////// MISC internal used APIs*/ - -extern void gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size); -extern int gci_is_heap_valid(gc_heap_t *heap); - -#ifdef GC_DEBUG -extern void gci_verify_heap(gc_heap_t *heap); -extern void gci_dump(char* buf, gc_heap_t *heap); +#if GC_STAT_DATA != 0 + gc_uint64 total_size_allocated; + gc_uint64 total_size_freed; #endif +} gc_heap_t; -#if GC_STAT_DATA != 0 +#if WASM_ENABLE_GC != 0 -/* the default GC threshold size is free_size * GC_DEFAULT_THRESHOLD_FACTOR / 1000 */ -#define GC_DEFAULT_THRESHOLD_FACTOR 400 +#define GC_DEFAULT_THRESHOLD_FACTOR 300 -static inline void gc_update_threshold(gc_heap_t *heap) +static inline void +gc_update_threshold(gc_heap_t *heap) { - heap->gc_threshold = heap->total_free_size * heap->gc_threshold_factor / 1000; + uint64_t result = (uint64_t)heap->total_free_size + * (uint64_t)heap->gc_threshold_factor / 1000; + /* heap->total_free_size * heap->gc_threshold_factor won't exceed + * 6^32(GC_HEAP_SIZE_MAX * GC_DEFAULT_THRESHOLD_FACTOR), so casting result + * to uint32_t is safe + */ + heap->gc_threshold = (uint32_t)result; } -#endif -/*////// MISC data structures*/ +#define gct_vm_mutex_init os_mutex_init +#define gct_vm_mutex_destroy os_mutex_destroy +#define gct_vm_mutex_lock os_mutex_lock +#define gct_vm_mutex_unlock os_mutex_unlock +#define gct_vm_gc_prepare wasm_runtime_gc_prepare +#define gct_vm_gc_finished wasm_runtime_gc_finalize +#define gct_vm_begin_rootset_enumeration wasm_runtime_traverse_gc_rootset +#define gct_vm_get_wasm_object_ref_list wasm_runtime_get_wasm_object_ref_list +#define gct_vm_get_extra_info_flag wasm_runtime_get_wasm_object_extra_info_flag +#define gct_vm_set_extra_info_flag wasm_runtime_set_wasm_object_extra_info_flag + +#endif /* end of WAMS_ENABLE_GC != 0 */ + +/** + * MISC internal used APIs + */ -#define MARK_NODE_OBJ_CNT 256 +bool +gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size); -/* mark node is used for gc marker*/ -typedef struct _mark_node_struct -{ - /* number of to-expand objects can be saved in this node*/ - gc_size_t cnt; - - /* the first unused index*/ - int idx; - - /* next node on the node list*/ - struct _mark_node_struct *next; - - /* the actual to-expand objects list*/ - gc_object_t set[MARK_NODE_OBJ_CNT]; -}mark_node_t; - -/*////// Imported APIs wrappers under TEST mode*/ - -#ifdef GC_TEST -extern int (*gct_vm_get_java_object_ref_list)( - gc_object_t obj, - int *is_compact_mode, /* can be set to GC_TRUE, or GC_FALSE */ - gc_size_t *ref_num, - gc_uint16 **ref_list, - gc_uint32 *ref_start_offset); -extern int (*gct_vm_mutex_init)(korp_mutex *mutex); -extern int (*gct_vm_mutex_destroy)(korp_mutex *mutex); -extern int (*gct_vm_mutex_lock)(korp_mutex *mutex); -extern int (*gct_vm_mutex_unlock)(korp_mutex *mutex); -extern gc_handle_t (*gct_vm_get_gc_handle_for_current_instance)(void); -extern int (*gct_vm_begin_rootset_enumeration)(void* heap); -extern int (*gct_vm_gc_finished)(void); -#else -#define gct_vm_get_java_object_ref_list bh_get_java_object_ref_list -#define gct_vm_mutex_init vm_mutex_init -#define gct_vm_mutex_destroy vm_mutex_destroy -#define gct_vm_mutex_lock vm_mutex_lock -#define gct_vm_mutex_unlock vm_mutex_unlock -#define gct_vm_get_gc_handle_for_current_instance app_manager_get_cur_applet_heap -#define gct_vm_begin_rootset_enumeration vm_begin_rootset_enumeration -#define gct_vm_gc_finished jeff_runtime_gc_finished -#endif +int +gci_is_heap_valid(gc_heap_t *heap); + +/** + * Verify heap integrity + */ +void +gci_verify_heap(gc_heap_t *heap); + +/** + * Dump heap nodes + */ +void +gci_dump(gc_heap_t *heap); #ifdef __cplusplus } #endif -#endif +#endif /* end of _EMS_GC_INTERNAL_H */ diff --git a/core/shared/mem-alloc/ems/ems_hmu.c b/core/shared/mem-alloc/ems/ems_hmu.c index 0a6d1abc31..ea84d98cdb 100644 --- a/core/shared/mem-alloc/ems/ems_hmu.c +++ b/core/shared/mem-alloc/ems/ems_hmu.c @@ -5,41 +5,52 @@ #include "ems_gc_internal.h" -#if defined(GC_VERIFY) -/* Set default value to prefix and suffix*/ +#if BH_ENABLE_GC_VERIFY != 0 -/* @hmu should not be NULL and it should have been correctly initilized (except for prefix and suffix part)*/ -/* @tot_size is offered here because hmu_get_size can not be used till now. @tot_size should not be smaller than OBJ_EXTRA_SIZE.*/ -/* For VO, @tot_size should be equal to object total size.*/ -void hmu_init_prefix_and_suffix(hmu_t *hmu, gc_size_t tot_size, const char *file_name, int line_no) +/** + * Set default value to prefix and suffix + * @param hmu should not be NULL and should have been correctly initialized + * (except prefix and suffix part) + * @param tot_size is offered here because hmu_get_size can not be used + * till now. tot_size should not be smaller than OBJ_EXTRA_SIZE. + * For VO, tot_size should be equal to object total size. + */ +void +hmu_init_prefix_and_suffix(hmu_t *hmu, gc_size_t tot_size, + const char *file_name, int line_no) { gc_object_prefix_t *prefix = NULL; gc_object_suffix_t *suffix = NULL; gc_uint32 i = 0; bh_assert(hmu); - bh_assert(hmu_get_ut(hmu) == HMU_JO || hmu_get_ut(hmu) == HMU_VO); + bh_assert(hmu_get_ut(hmu) == HMU_WO || hmu_get_ut(hmu) == HMU_VO); bh_assert(tot_size >= OBJ_EXTRA_SIZE); bh_assert(!(tot_size & 7)); bh_assert(hmu_get_ut(hmu) != HMU_VO || hmu_get_size(hmu) >= tot_size); prefix = (gc_object_prefix_t *)(hmu + 1); - suffix = (gc_object_suffix_t *)((gc_uint8*)hmu + tot_size - OBJ_SUFFIX_SIZE); + suffix = + (gc_object_suffix_t *)((gc_uint8 *)hmu + tot_size - OBJ_SUFFIX_SIZE); prefix->file_name = file_name; prefix->line_no = line_no; prefix->size = tot_size; - for(i = 0;i < GC_OBJECT_PREFIX_PADDING_CNT;i++) - { + + for (i = 0; i < GC_OBJECT_PREFIX_PADDING_CNT; i++) { prefix->padding[i] = GC_OBJECT_PADDING_VALUE; } - for(i = 0;i < GC_OBJECT_SUFFIX_PADDING_CNT;i++) - { + + for (i = 0; i < GC_OBJECT_SUFFIX_PADDING_CNT; i++) { suffix->padding[i] = GC_OBJECT_PADDING_VALUE; } } -void hmu_verify(hmu_t *hmu) +void +hmu_verify(void *vheap, hmu_t *hmu) { +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + gc_heap_t *heap = (gc_heap_t *)vheap; +#endif gc_object_prefix_t *prefix = NULL; gc_object_suffix_t *suffix = NULL; gc_uint32 i = 0; @@ -53,35 +64,32 @@ void hmu_verify(hmu_t *hmu) prefix = (gc_object_prefix_t *)(hmu + 1); size = prefix->size; - suffix = (gc_object_suffix_t *)((gc_uint8*)hmu + size - OBJ_SUFFIX_SIZE); + suffix = (gc_object_suffix_t *)((gc_uint8 *)hmu + size - OBJ_SUFFIX_SIZE); - if(ut == HMU_VO || ut == HMU_JO) - { + if (ut == HMU_VO || ut == HMU_WO) { /* check padding*/ - for(i = 0;i < GC_OBJECT_PREFIX_PADDING_CNT;i++) - { - if(prefix->padding[i] != GC_OBJECT_PADDING_VALUE) - { + for (i = 0; i < GC_OBJECT_PREFIX_PADDING_CNT; i++) { + if (prefix->padding[i] != GC_OBJECT_PADDING_VALUE) { is_padding_ok = 0; break; } } - for(i = 0;i < GC_OBJECT_SUFFIX_PADDING_CNT;i++) - { - if(suffix->padding[i] != GC_OBJECT_PADDING_VALUE) - { + for (i = 0; i < GC_OBJECT_SUFFIX_PADDING_CNT; i++) { + if (suffix->padding[i] != GC_OBJECT_PADDING_VALUE) { is_padding_ok = 0; break; } } - if(!is_padding_ok) - { - printf("Invalid padding for object created at %s:%d", - (prefix->file_name ? prefix->file_name : ""), prefix->line_no); + if (!is_padding_ok) { + LOG_ERROR("Invalid padding for object created at %s:%d\n", + (prefix->file_name ? prefix->file_name : ""), + prefix->line_no); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + heap->is_heap_corrupted = true; +#endif } - bh_assert(is_padding_ok); } } -#endif +#endif /* end of BH_ENABLE_GC_VERIFY */ diff --git a/core/shared/mem-alloc/ems/ems_kfc.c b/core/shared/mem-alloc/ems/ems_kfc.c index 5ea662aece..e761cb642b 100644 --- a/core/shared/mem-alloc/ems/ems_kfc.c +++ b/core/shared/mem-alloc/ems/ems_kfc.c @@ -5,178 +5,541 @@ #include "ems_gc_internal.h" - -#define HEAP_INC_FACTOR 1 - -/* Check if current platform is compatible with current GC design*/ - -/* Return GC_ERROR if not;*/ -/* Return GC_SUCCESS otherwise.*/ -int gci_check_platform() -{ -#define CHECK(x, y) do { \ - if((x) != (y)) { \ - bh_printf("Platform checking failed on LINE %d at FILE %s.",\ - __LINE__, __FILE__); \ - return GC_ERROR; \ - } \ -} while(0) - - CHECK(8, sizeof(gc_int64)); - CHECK(4, sizeof(gc_uint32)); - CHECK(4, sizeof(gc_int32)); - CHECK(2, sizeof(gc_uint16)); - CHECK(2, sizeof(gc_int16)); - CHECK(1, sizeof(gc_int8)); - CHECK(1, sizeof(gc_uint8)); - CHECK(4, sizeof(gc_size_t)); - /*CHECK(4, sizeof(void *));*/ - - return GC_SUCCESS; -} - -gc_handle_t gc_init_with_pool(char *buf, gc_size_t buf_size) +static gc_handle_t +gc_init_internal(gc_heap_t *heap, char *base_addr, gc_size_t heap_max_size) { - char *buf_end = buf + buf_size; - char *buf_aligned = (char*) (((uintptr_t) buf + 7) & (uintptr_t)~7); - char *base_addr = buf_aligned + sizeof(gc_heap_t); - gc_heap_t *heap = (gc_heap_t*) buf_aligned; - gc_size_t heap_max_size; - hmu_normal_node_t *p = NULL; hmu_tree_node_t *root = NULL, *q = NULL; - int i = 0, ret; - - /* check system compatibility*/ - if (gci_check_platform() == GC_ERROR) { - bh_printf("Check platform compatibility failed"); - return NULL; - } - - if (buf_size < 1024) { - bh_printf("[GC_ERROR]heap_init_size(%d) < 1024", buf_size); - return NULL; - } - - base_addr = (char*) (((uintptr_t) base_addr + 7) & (uintptr_t)~7) + GC_HEAD_PADDING; - heap_max_size = (uint32)(buf_end - base_addr) & (uint32)~7; + int ret; memset(heap, 0, sizeof *heap); memset(base_addr, 0, heap_max_size); - ret = gct_vm_mutex_init(&heap->lock); + ret = os_mutex_init(&heap->lock); if (ret != BHT_OK) { - bh_printf("[GC_ERROR]failed to init lock "); + LOG_ERROR("[GC_ERROR]failed to init lock\n"); return NULL; } -#ifdef BH_FOOTPRINT - bh_printf("\nINIT HEAP 0x%08x %d\n", base_addr, heap_max_size); -#endif - /* init all data structures*/ - heap->max_size = heap_max_size; heap->current_size = heap_max_size; - heap->base_addr = (gc_uint8*) base_addr; - heap->heap_id = (gc_handle_t) heap; + heap->base_addr = (gc_uint8 *)base_addr; + heap->heap_id = (gc_handle_t)heap; -#if GC_STAT_DATA != 0 heap->total_free_size = heap->current_size; heap->highmark_size = 0; - heap->total_gc_count = 0; - heap->total_gc_time = 0; +#if WASM_ENABLE_GC != 0 heap->gc_threshold_factor = GC_DEFAULT_THRESHOLD_FACTOR; gc_update_threshold(heap); #endif - for (i = 0; i < HMU_NORMAL_NODE_CNT; i++) { - /* make normal node look like a FC*/ - p = &heap->kfc_normal_list[i]; - memset(p, 0, sizeof *p); - hmu_set_ut(&p->hmu_header, HMU_FC); - hmu_set_size(&p->hmu_header, sizeof *p); - } - - root = &heap->kfc_tree_root; + root = heap->kfc_tree_root = (hmu_tree_node_t *)heap->kfc_tree_root_buf; memset(root, 0, sizeof *root); root->size = sizeof *root; hmu_set_ut(&root->hmu_header, HMU_FC); hmu_set_size(&root->hmu_header, sizeof *root); - q = (hmu_tree_node_t *) heap->base_addr; + q = (hmu_tree_node_t *)heap->base_addr; memset(q, 0, sizeof *q); hmu_set_ut(&q->hmu_header, HMU_FC); hmu_set_size(&q->hmu_header, heap->current_size); + ASSERT_TREE_NODE_ALIGNED_ACCESS(q); + ASSERT_TREE_NODE_ALIGNED_ACCESS(root); + hmu_mark_pinuse(&q->hmu_header); root->right = q; q->parent = root; q->size = heap->current_size; - bh_assert( - root->size <= HMU_FC_NORMAL_MAX_SIZE - && HMU_FC_NORMAL_MAX_SIZE < q->size); /*@NOTIFY*/ + bh_assert(root->size <= HMU_FC_NORMAL_MAX_SIZE); -#if BH_ENABLE_MEMORY_PROFILING != 0 - bh_printf("heap is successfully initialized with max_size=%u.", - heap_max_size); -#endif return heap; } -int gc_destroy_with_pool(gc_handle_t handle) +gc_handle_t +gc_init_with_pool(char *buf, gc_size_t buf_size) +{ + char *buf_end = buf + buf_size; + char *buf_aligned = (char *)(((uintptr_t)buf + 7) & (uintptr_t)~7); + char *base_addr = buf_aligned + sizeof(gc_heap_t); + gc_heap_t *heap = (gc_heap_t *)buf_aligned; + gc_size_t heap_max_size; + + if (buf_size < APP_HEAP_SIZE_MIN) { + LOG_ERROR("[GC_ERROR]heap init buf size (%" PRIu32 ") < %" PRIu32 "\n", + buf_size, (uint32)APP_HEAP_SIZE_MIN); + return NULL; + } + + base_addr = + (char *)(((uintptr_t)base_addr + 7) & (uintptr_t)~7) + GC_HEAD_PADDING; + heap_max_size = (uint32)(buf_end - base_addr) & (uint32)~7; + +#if WASM_ENABLE_MEMORY_TRACING != 0 + LOG_VERBOSE("Heap created, total size: %u", buf_size); + LOG_VERBOSE(" heap struct size: %u", sizeof(gc_heap_t)); + LOG_VERBOSE(" actual heap size: %u", heap_max_size); + LOG_VERBOSE(" padding bytes: %u", + buf_size - sizeof(gc_heap_t) - heap_max_size); +#endif + return gc_init_internal(heap, base_addr, heap_max_size); +} + +gc_handle_t +gc_init_with_struct_and_pool(char *struct_buf, gc_size_t struct_buf_size, + char *pool_buf, gc_size_t pool_buf_size) +{ + gc_heap_t *heap = (gc_heap_t *)struct_buf; + char *base_addr = pool_buf + GC_HEAD_PADDING; + char *pool_buf_end = pool_buf + pool_buf_size; + gc_size_t heap_max_size; + + if ((((uintptr_t)struct_buf) & 7) != 0) { + LOG_ERROR("[GC_ERROR]heap init struct buf not 8-byte aligned\n"); + return NULL; + } + + if (struct_buf_size < sizeof(gc_handle_t)) { + LOG_ERROR("[GC_ERROR]heap init struct buf size (%" PRIu32 ") < %zu\n", + struct_buf_size, sizeof(gc_handle_t)); + return NULL; + } + + if ((((uintptr_t)pool_buf) & 7) != 0) { + LOG_ERROR("[GC_ERROR]heap init pool buf not 8-byte aligned\n"); + return NULL; + } + + if (pool_buf_size < APP_HEAP_SIZE_MIN) { + LOG_ERROR("[GC_ERROR]heap init buf size (%" PRIu32 ") < %u\n", + pool_buf_size, APP_HEAP_SIZE_MIN); + return NULL; + } + + heap_max_size = (uint32)(pool_buf_end - base_addr) & (uint32)~7; + +#if WASM_ENABLE_MEMORY_TRACING != 0 + LOG_VERBOSE("Heap created, total size: %u", + struct_buf_size + pool_buf_size); + LOG_VERBOSE(" heap struct size: %u", sizeof(gc_heap_t)); + LOG_VERBOSE(" actual heap size: %u", heap_max_size); + LOG_VERBOSE(" padding bytes: %u", pool_buf_size - heap_max_size); +#endif + return gc_init_internal(heap, base_addr, heap_max_size); +} + +int +gc_destroy_with_pool(gc_handle_t handle) { - gc_heap_t *heap = (gc_heap_t *) handle; - gct_vm_mutex_destroy(&heap->lock); - memset(heap->base_addr, 0, heap->max_size); + gc_heap_t *heap = (gc_heap_t *)handle; + int ret = GC_SUCCESS; + +#if WASM_ENABLE_GC != 0 + gc_size_t i = 0; + + if (heap->extra_info_node_cnt > 0) { + for (i = 0; i < heap->extra_info_node_cnt; i++) { + extra_info_node_t *node = heap->extra_info_nodes[i]; +#if BH_ENABLE_GC_VERIFY != 0 + os_printf("Memory leak detected: gc object [%p] not claimed\n", + node->obj); +#endif + bh_assert(heap->is_reclaim_enabled); + node->finalizer(node->obj, node->data); + + BH_FREE(heap->extra_info_nodes[i]); + } + + if (heap->extra_info_nodes != heap->extra_info_normal_nodes) { + BH_FREE(heap->extra_info_nodes); + } + } +#endif + +#if BH_ENABLE_GC_VERIFY != 0 + hmu_t *cur = (hmu_t *)heap->base_addr; + hmu_t *end = (hmu_t *)((char *)heap->base_addr + heap->current_size); + + if ( +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + !heap->is_heap_corrupted && +#endif + (hmu_t *)((char *)cur + hmu_get_size(cur)) != end) { + LOG_WARNING("Memory leak detected:\n"); + gci_dump(heap); + ret = GC_ERROR; + } +#endif + + os_mutex_destroy(&heap->lock); + memset(heap->base_addr, 0, heap->current_size); memset(heap, 0, sizeof(gc_heap_t)); - return GC_SUCCESS; + return ret; +} + +#if WASM_ENABLE_GC != 0 +#if WASM_ENABLE_THREAD_MGR == 0 +void +gc_enable_gc_reclaim(gc_handle_t handle, void *exec_env) +{ + gc_heap_t *heap = (gc_heap_t *)handle; + + heap->is_reclaim_enabled = 1; + heap->exec_env = exec_env; +} +#else +void +gc_enable_gc_reclaim(gc_handle_t handle, void *cluster) +{ + gc_heap_t *heap = (gc_heap_t *)handle; + + heap->is_reclaim_enabled = 1; + heap->cluster = cluster; +} +#endif +#endif + +uint32 +gc_get_heap_struct_size() +{ + return sizeof(gc_heap_t); } -#if defined(GC_VERIFY) -/* Verify heap integrity*/ -/* @heap should not be NULL and it should be a valid heap*/ -void gci_verify_heap(gc_heap_t *heap) +static void +adjust_ptr(uint8 **p_ptr, intptr_t offset) +{ + if ((!*p_ptr)) { + return; + } + + /* + * to resolve a possible signed integer overflow issue + * when p_ptr is over 0x8000000000000000 by not using + * `(intptr_t)` + */ + uintptr_t offset_val = 0; +#if UINTPTR_MAX == UINT64_MAX + offset_val = labs(offset); +#else + offset_val = abs(offset); +#endif + + if (offset > 0) { + *p_ptr = (uint8 *)((uintptr_t)(*p_ptr) + offset_val); + } + else { + *p_ptr = (uint8 *)((uintptr_t)(*p_ptr) - offset_val); + } +} + +int +gc_migrate(gc_handle_t handle, char *pool_buf_new, gc_size_t pool_buf_size) +{ + gc_heap_t *heap = (gc_heap_t *)handle; + char *base_addr_new = pool_buf_new + GC_HEAD_PADDING; + char *pool_buf_end = pool_buf_new + pool_buf_size; + intptr_t offset = (uint8 *)base_addr_new - (uint8 *)heap->base_addr; + hmu_t *cur = NULL, *end = NULL; + hmu_tree_node_t *tree_node; + uint8 **p_left, **p_right, **p_parent; + gc_size_t heap_max_size, size; + + if ((((uintptr_t)pool_buf_new) & 7) != 0) { + LOG_ERROR("[GC_ERROR]heap migrate pool buf not 8-byte aligned\n"); + return GC_ERROR; + } + + heap_max_size = (uint32)(pool_buf_end - base_addr_new) & (uint32)~7; + + if (pool_buf_end < base_addr_new || heap_max_size < heap->current_size) { + LOG_ERROR("[GC_ERROR]heap migrate invalid pool buf size\n"); + return GC_ERROR; + } + + if (offset == 0) + return 0; + +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + if (heap->is_heap_corrupted) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, heap migrate failed.\n"); + return GC_ERROR; + } +#endif + + heap->base_addr = (uint8 *)base_addr_new; + + ASSERT_TREE_NODE_ALIGNED_ACCESS(heap->kfc_tree_root); + + p_left = (uint8 **)((uint8 *)heap->kfc_tree_root + + offsetof(hmu_tree_node_t, left)); + p_right = (uint8 **)((uint8 *)heap->kfc_tree_root + + offsetof(hmu_tree_node_t, right)); + p_parent = (uint8 **)((uint8 *)heap->kfc_tree_root + + offsetof(hmu_tree_node_t, parent)); + adjust_ptr(p_left, offset); + adjust_ptr(p_right, offset); + adjust_ptr(p_parent, offset); + + cur = (hmu_t *)heap->base_addr; + end = (hmu_t *)((char *)heap->base_addr + heap->current_size); + + while (cur < end) { + size = hmu_get_size(cur); + + if (size <= 0 || size > (uint32)((uint8 *)end - (uint8 *)cur)) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, heap migrate failed.\n"); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + heap->is_heap_corrupted = true; +#endif + return GC_ERROR; + } + + if (hmu_get_ut(cur) == HMU_FC && !HMU_IS_FC_NORMAL(size)) { + tree_node = (hmu_tree_node_t *)cur; + + ASSERT_TREE_NODE_ALIGNED_ACCESS(tree_node); + + p_left = (uint8 **)((uint8 *)tree_node + + offsetof(hmu_tree_node_t, left)); + p_right = (uint8 **)((uint8 *)tree_node + + offsetof(hmu_tree_node_t, right)); + p_parent = (uint8 **)((uint8 *)tree_node + + offsetof(hmu_tree_node_t, parent)); + adjust_ptr(p_left, offset); + adjust_ptr(p_right, offset); + if (tree_node->parent != heap->kfc_tree_root) + /* The root node belongs to heap structure, + it is fixed part and isn't changed. */ + adjust_ptr(p_parent, offset); + } + cur = (hmu_t *)((char *)cur + size); + } + + if (cur != end) { + LOG_ERROR("[GC_ERROR]Heap is corrupted, heap migrate failed.\n"); +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + heap->is_heap_corrupted = true; +#endif + return GC_ERROR; + } + + return 0; +} + +bool +gc_is_heap_corrupted(gc_handle_t handle) +{ +#if BH_ENABLE_GC_CORRUPTION_CHECK != 0 + gc_heap_t *heap = (gc_heap_t *)handle; + + return heap->is_heap_corrupted ? true : false; +#else + return false; +#endif +} + +#if BH_ENABLE_GC_VERIFY != 0 +void +gci_verify_heap(gc_heap_t *heap) { hmu_t *cur = NULL, *end = NULL; bh_assert(heap && gci_is_heap_valid(heap)); cur = (hmu_t *)heap->base_addr; end = (hmu_t *)(heap->base_addr + heap->current_size); - while(cur < end) - { - hmu_verify(cur); - cur = (hmu_t *)((gc_uint8*)cur + hmu_get_size(cur)); + while (cur < end) { + hmu_verify(heap, cur); + cur = (hmu_t *)((gc_uint8 *)cur + hmu_get_size(cur)); } bh_assert(cur == end); } #endif -void* gc_heap_stats(void *heap_arg, uint32* stats, int size, gc_mm_t mmt) +void +gc_heap_stat(void *heap_ptr, gc_stat_t *stat) +{ + hmu_t *cur = NULL, *end = NULL; + hmu_type_t ut; + gc_size_t size; + gc_heap_t *heap = (gc_heap_t *)heap_ptr; + + memset(stat, 0, sizeof(gc_stat_t)); + cur = (hmu_t *)heap->base_addr; + end = (hmu_t *)((char *)heap->base_addr + heap->current_size); + + while (cur < end) { + ut = hmu_get_ut(cur); + size = hmu_get_size(cur); + bh_assert(size > 0); + + if (ut == HMU_FC || ut == HMU_FM + || (ut == HMU_VO && hmu_is_vo_freed(cur)) + || (ut == HMU_WO && !hmu_is_wo_marked(cur))) { + if (ut == HMU_VO) + stat->vo_free += size; + if (ut == HMU_WO) + stat->wo_free += size; + stat->free += size; + stat->free_block++; + if (size / sizeof(int) < GC_HEAP_STAT_SIZE - 1) + stat->free_sizes[size / sizeof(int)] += 1; + else + stat->free_sizes[GC_HEAP_STAT_SIZE - 1] += 1; + } + else { + if (ut == HMU_VO) + stat->vo_usage += size; + if (ut == HMU_WO) + stat->wo_usage += size; + stat->usage += size; + stat->usage_block++; + if (size / sizeof(int) < GC_HEAP_STAT_SIZE - 1) + stat->usage_sizes[size / sizeof(int)] += 1; + else + stat->usage_sizes[GC_HEAP_STAT_SIZE - 1] += 1; + } + + cur = (hmu_t *)((char *)cur + size); + } +} + +void +gc_print_stat(void *heap_ptr, int verbose) +{ + gc_stat_t stat; + int i; + + bh_assert(heap_ptr != NULL); + gc_heap_t *heap = (gc_heap_t *)(heap_ptr); + + gc_heap_stat(heap, &stat); + + os_printf("# stat %s %p use %d free %d \n", "instance", heap, stat.usage, + stat.free); + os_printf("# stat %s %p wo_usage %d vo_usage %d \n", "instance", heap, + stat.wo_usage, stat.vo_usage); + os_printf("# stat %s %p wo_free %d vo_free %d \n", "instance", heap, + stat.wo_free, stat.vo_free); +#if WASM_ENABLE_GC == 0 + os_printf("# stat free size %" PRIu32 " high %" PRIu32 "\n", + heap->total_free_size, heap->highmark_size); +#else + os_printf("# stat gc %" PRIu32 " free size %" PRIu32 " high %" PRIu32 "\n", + heap->total_gc_count, heap->total_free_size, heap->highmark_size); +#endif + if (verbose) { + os_printf("usage sizes: \n"); + for (i = 0; i < GC_HEAP_STAT_SIZE; i++) + if (stat.usage_sizes[i]) + os_printf(" %d: %d; ", i * 4, stat.usage_sizes[i]); + os_printf(" \n"); + os_printf("free sizes: \n"); + for (i = 0; i < GC_HEAP_STAT_SIZE; i++) + if (stat.free_sizes[i]) + os_printf(" %d: %d; ", i * 4, stat.free_sizes[i]); + } +} + +void * +gc_heap_stats(void *heap_arg, uint32 *stats, int size) { - (void) mmt; int i; - gc_heap_t *heap = (gc_heap_t *) heap_arg; + gc_heap_t *heap = (gc_heap_t *)heap_arg; + + if (!gci_is_heap_valid(heap)) { + for (i = 0; i < size; i++) + stats[i] = 0; + return NULL; + } for (i = 0; i < size; i++) { switch (i) { - case GC_STAT_TOTAL: - stats[i] = heap->current_size; - break; - case GC_STAT_FREE: - stats[i] = heap->total_free_size; - break; - case GC_STAT_HIGHMARK: - stats[i] = heap->highmark_size; - break; - case GC_STAT_COUNT: - stats[i] = heap->total_gc_count; - break; - case GC_STAT_TIME: - stats[i] = (uint32)heap->total_gc_time; - break; - default: - break; + case GC_STAT_TOTAL: + stats[i] = heap->current_size; + break; + case GC_STAT_FREE: + stats[i] = heap->total_free_size; + break; + case GC_STAT_HIGHMARK: + stats[i] = heap->highmark_size; + break; +#if WASM_ENABLE_GC != 0 + case GC_STAT_COUNT: + stats[i] = heap->total_gc_count; + break; + case GC_STAT_TIME: + stats[i] = heap->total_gc_time; + break; +#endif + default: + break; } } + return heap; } + +void +gc_traverse_tree(hmu_tree_node_t *node, gc_size_t *stats, int *n) +{ + if (!node) + return; + + if (*n > 0) + gc_traverse_tree(node->right, stats, n); + + if (*n > 0) { + (*n)--; + stats[*n] = node->size; + } + + if (*n > 0) + gc_traverse_tree(node->left, stats, n); +} + +void +gc_show_stat(void *heap) +{ + + uint32 stats[GC_STAT_MAX]; + + heap = gc_heap_stats(heap, stats, GC_STAT_MAX); + + os_printf("\n[GC stats %p] %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 + " %" PRIu32 "\n", + heap, stats[0], stats[1], stats[2], stats[3], stats[4]); +} + +#if WASM_ENABLE_GC != 0 +void +gc_show_fragment(void *heap_arg) +{ + uint32 stats[3]; + int n = 3; + gc_heap_t *heap = (gc_heap_t *)heap_arg; + + memset(stats, 0, n * sizeof(int)); + gct_vm_mutex_lock(&heap->lock); + gc_traverse_tree(heap->kfc_tree_root, (gc_size_t *)stats, &n); + gct_vm_mutex_unlock(&heap->lock); + os_printf("\n[GC %p top sizes] %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", heap, + stats[0], stats[1], stats[2]); +} + +#if WASM_ENABLE_GC_PERF_PROFILING != 0 +void +gc_dump_perf_profiling(gc_handle_t *handle) +{ + gc_heap_t *gc_heap_handle = (void *)handle; + if (gc_heap_handle) { + os_printf("\nGC performance summary\n"); + os_printf(" Total GC time (ms): %u\n", + gc_heap_handle->total_gc_time); + os_printf(" Max GC time (ms): %u\n", gc_heap_handle->max_gc_time); + } + else { + os_printf("Failed to dump GC performance\n"); + } +} +#endif +#endif diff --git a/core/shared/mem-alloc/mem_alloc.c b/core/shared/mem-alloc/mem_alloc.c index 72670ff5f5..c17a69ee28 100644 --- a/core/shared/mem-alloc/mem_alloc.c +++ b/core/shared/mem-alloc/mem_alloc.c @@ -4,59 +4,182 @@ */ #include "mem_alloc.h" -#include "config.h" +#include #if DEFAULT_MEM_ALLOCATOR == MEM_ALLOCATOR_EMS #include "ems/ems_gc.h" -mem_allocator_t mem_allocator_create(void *mem, uint32_t size) +mem_allocator_t +mem_allocator_create(void *mem, uint32_t size) +{ + return gc_init_with_pool((char *)mem, size); +} + +mem_allocator_t +mem_allocator_create_with_struct_and_pool(void *struct_buf, + uint32_t struct_buf_size, + void *pool_buf, + uint32_t pool_buf_size) +{ + return gc_init_with_struct_and_pool((char *)struct_buf, struct_buf_size, + pool_buf, pool_buf_size); +} + +int +mem_allocator_destroy(mem_allocator_t allocator) { - return gc_init_with_pool((char *) mem, size); + return gc_destroy_with_pool((gc_handle_t)allocator); } -void mem_allocator_destroy(mem_allocator_t allocator) +uint32 +mem_allocator_get_heap_struct_size() { - gc_destroy_with_pool((gc_handle_t) allocator); + return gc_get_heap_struct_size(); } void * mem_allocator_malloc(mem_allocator_t allocator, uint32_t size) { - return gc_alloc_vo_h((gc_handle_t) allocator, size); + return gc_alloc_vo((gc_handle_t)allocator, size); } -void mem_allocator_free(mem_allocator_t allocator, void *ptr) +void * +mem_allocator_realloc(mem_allocator_t allocator, void *ptr, uint32_t size) +{ + return gc_realloc_vo((gc_handle_t)allocator, ptr, size); +} + +void +mem_allocator_free(mem_allocator_t allocator, void *ptr) { if (ptr) - gc_free_h((gc_handle_t) allocator, ptr); + gc_free_vo((gc_handle_t)allocator, ptr); +} + +#if BH_ENABLE_GC_VERIFY == 0 +void * +mem_allocator_malloc_aligned(mem_allocator_t allocator, uint32_t size, + uint32_t alignment) +{ + return gc_alloc_vo_aligned((gc_handle_t)allocator, size, alignment); +} +#else +void * +mem_allocator_malloc_aligned_internal(mem_allocator_t allocator, uint32_t size, + uint32_t alignment, const char *file, + int line) +{ + return gc_alloc_vo_aligned_internal((gc_handle_t)allocator, size, alignment, + file, line); +} +#endif + +#if WASM_ENABLE_GC != 0 +void * +mem_allocator_malloc_with_gc(mem_allocator_t allocator, uint32_t size) +{ + return gc_alloc_wo((gc_handle_t)allocator, size); +} + +#if WASM_GC_MANUALLY != 0 +void +mem_allocator_free_with_gc(mem_allocator_t allocator, void *ptr) +{ + if (ptr) + gc_free_wo((gc_handle_t)allocator, ptr); +} +#endif + +#if WASM_ENABLE_THREAD_MGR == 0 +void +mem_allocator_enable_gc_reclaim(mem_allocator_t allocator, void *exec_env) +{ + gc_enable_gc_reclaim((gc_handle_t)allocator, exec_env); +} +#else +void +mem_allocator_enable_gc_reclaim(mem_allocator_t allocator, void *cluster) +{ + gc_enable_gc_reclaim((gc_handle_t)allocator, cluster); } +#endif + +int +mem_allocator_add_root(mem_allocator_t allocator, WASMObjectRef obj) +{ + return gc_add_root((gc_handle_t)allocator, (gc_object_t)obj); +} +#endif + +int +mem_allocator_migrate(mem_allocator_t allocator, char *pool_buf_new, + uint32 pool_buf_size) +{ + return gc_migrate((gc_handle_t)allocator, pool_buf_new, pool_buf_size); +} + +bool +mem_allocator_is_heap_corrupted(mem_allocator_t allocator) +{ + return gc_is_heap_corrupted((gc_handle_t)allocator); +} + +bool +mem_allocator_get_alloc_info(mem_allocator_t allocator, void *mem_alloc_info) +{ + gc_heap_stats((gc_handle_t)allocator, mem_alloc_info, 3); + return true; +} + +#if WASM_ENABLE_GC != 0 +bool +mem_allocator_set_gc_finalizer(mem_allocator_t allocator, void *obj, + gc_finalizer_t cb, void *data) +{ + return gc_set_finalizer((gc_handle_t)allocator, (gc_object_t)obj, cb, data); +} + +void +mem_allocator_unset_gc_finalizer(mem_allocator_t allocator, void *obj) +{ + gc_unset_finalizer((gc_handle_t)allocator, (gc_object_t)obj); +} + +#if WASM_ENABLE_GC_PERF_PROFILING != 0 +void +mem_allocator_dump_perf_profiling(mem_allocator_t allocator) +{ + gc_dump_perf_profiling((gc_handle_t)allocator); +} +#endif + +#endif #else /* else of DEFAULT_MEM_ALLOCATOR */ #include "tlsf/tlsf.h" -#include "bh_thread.h" typedef struct mem_allocator_tlsf { tlsf_t tlsf; korp_mutex lock; -}mem_allocator_tlsf; +} mem_allocator_tlsf; mem_allocator_t mem_allocator_create(void *mem, uint32_t size) { mem_allocator_tlsf *allocator_tlsf; tlsf_t tlsf; - char *mem_aligned = (char*)(((uintptr_t)mem + 3) & ~3); + char *mem_aligned = (char *)(((uintptr_t)mem + 3) & ~3); if (size < 1024) { printf("Create mem allocator failed: pool size must be " - "at least 1024 bytes.\n"); + "at least 1024 bytes.\n"); return NULL; } - size -= mem_aligned - (char*)mem; - mem = (void*)mem_aligned; + size -= mem_aligned - (char *)mem; + mem = (void *)mem_aligned; tlsf = tlsf_create_with_pool(mem, size); if (!tlsf) { @@ -73,7 +196,7 @@ mem_allocator_create(void *mem, uint32_t size) allocator_tlsf->tlsf = tlsf; - if (vm_mutex_init(&allocator_tlsf->lock)) { + if (os_mutex_init(&allocator_tlsf->lock)) { printf("Create mem allocator failed: tlsf_malloc failed.\n"); tlsf_free(tlsf, allocator_tlsf); tlsf_destroy(tlsf); @@ -89,7 +212,7 @@ mem_allocator_destroy(mem_allocator_t allocator) mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; tlsf_t tlsf = allocator_tlsf->tlsf; - vm_mutex_destroy(&allocator_tlsf->lock); + os_mutex_destroy(&allocator_tlsf->lock); tlsf_free(tlsf, allocator_tlsf); tlsf_destroy(tlsf); } @@ -101,12 +224,28 @@ mem_allocator_malloc(mem_allocator_t allocator, uint32_t size) mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; if (size == 0) - /* tlsf doesn't allow to allocate 0 byte */ - size = 1; + /* tlsf doesn't allow to allocate 0 byte */ + size = 1; - vm_mutex_lock(&allocator_tlsf->lock); + os_mutex_lock(&allocator_tlsf->lock); ret = tlsf_malloc(allocator_tlsf->tlsf, size); - vm_mutex_unlock(&allocator_tlsf->lock); + os_mutex_unlock(&allocator_tlsf->lock); + return ret; +} + +void * +mem_allocator_realloc(mem_allocator_t allocator, void *ptr, uint32_t size) +{ + void *ret; + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + + if (size == 0) + /* tlsf doesn't allow to allocate 0 byte */ + size = 1; + + os_mutex_lock(&allocator_tlsf->lock); + ret = tlsf_realloc(allocator_tlsf->tlsf, ptr, size); + os_mutex_unlock(&allocator_tlsf->lock); return ret; } @@ -115,11 +254,17 @@ mem_allocator_free(mem_allocator_t allocator, void *ptr) { if (ptr) { mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; - vm_mutex_lock(&allocator_tlsf->lock); + os_mutex_lock(&allocator_tlsf->lock); tlsf_free(allocator_tlsf->tlsf, ptr); - vm_mutex_unlock(&allocator_tlsf->lock); + os_mutex_unlock(&allocator_tlsf->lock); } } -#endif /* end of DEFAULT_MEM_ALLOCATOR */ +int +mem_allocator_migrate(mem_allocator_t allocator, mem_allocator_t allocator_old) +{ + return tlsf_migrate((mem_allocator_tlsf *)allocator, + (mem_allocator_tlsf *)allocator_old); +} +#endif /* end of DEFAULT_MEM_ALLOCATOR */ diff --git a/core/shared/mem-alloc/mem_alloc.cmake b/core/shared/mem-alloc/mem_alloc.cmake index 2a224bf52e..76d1706dcf 100644 --- a/core/shared/mem-alloc/mem_alloc.cmake +++ b/core/shared/mem-alloc/mem_alloc.cmake @@ -6,11 +6,32 @@ set (MEM_ALLOC_DIR ${CMAKE_CURRENT_LIST_DIR}) include_directories(${MEM_ALLOC_DIR}) +if (WAMR_BUILD_GC_VERIFY EQUAL 1) + add_definitions (-DBH_ENABLE_GC_VERIFY=1) +endif () + +if (NOT DEFINED WAMR_BUILD_GC_CORRUPTION_CHECK) + # Disable memory allocator heap corruption check + # when GC is enabled + if (WAMR_BUILD_GC EQUAL 1) + set (WAMR_BUILD_GC_CORRUPTION_CHECK 0) + else () + set (WAMR_BUILD_GC_CORRUPTION_CHECK 1) + endif () +endif () + +if (WAMR_BUILD_GC_CORRUPTION_CHECK EQUAL 0) + add_definitions (-DBH_ENABLE_GC_CORRUPTION_CHECK=0) +endif () + +if (DEFINED WAMR_BUILD_GC_HEAP_SIZE_DEFAULT) + add_definitions ("-DGC_HEAP_SIZE_DEFAULT=${WAMR_BUILD_GC_HEAP_SIZE_DEFAULT}") +endif () + file (GLOB_RECURSE source_all ${MEM_ALLOC_DIR}/ems/*.c ${MEM_ALLOC_DIR}/tlsf/*.c - ${MEM_ALLOC_DIR}/mem_alloc.c - ${MEM_ALLOC_DIR}/bh_memory.c) + ${MEM_ALLOC_DIR}/mem_alloc.c) set (MEM_ALLOC_SHARED_SOURCE ${source_all}) diff --git a/core/shared/mem-alloc/mem_alloc.h b/core/shared/mem-alloc/mem_alloc.h new file mode 100644 index 0000000000..3e55d49ed5 --- /dev/null +++ b/core/shared/mem-alloc/mem_alloc.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __MEM_ALLOC_H +#define __MEM_ALLOC_H + +#include "bh_platform.h" +#if WASM_ENABLE_GC != 0 +#include "../../common/gc/gc_object.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *mem_allocator_t; + +#ifndef GC_FINALIZER_T_DEFINED +#define GC_FINALIZER_T_DEFINED +typedef void (*gc_finalizer_t)(void *obj, void *data); +#endif + +mem_allocator_t +mem_allocator_create(void *mem, uint32_t size); + +mem_allocator_t +mem_allocator_create_with_struct_and_pool(void *struct_buf, + uint32_t struct_buf_size, + void *pool_buf, + uint32_t pool_buf_size); + +int +mem_allocator_destroy(mem_allocator_t allocator); + +uint32 +mem_allocator_get_heap_struct_size(void); + +void * +mem_allocator_malloc(mem_allocator_t allocator, uint32_t size); + +void * +mem_allocator_realloc(mem_allocator_t allocator, void *ptr, uint32_t size); + +void +mem_allocator_free(mem_allocator_t allocator, void *ptr); + +/* Aligned allocation support */ +#ifndef GC_MIN_ALIGNMENT +#define GC_MIN_ALIGNMENT 8 +#endif + +#if BH_ENABLE_GC_VERIFY == 0 + +void * +mem_allocator_malloc_aligned(mem_allocator_t allocator, uint32_t size, + uint32_t alignment); + +#define mem_allocator_malloc_aligned_internal(allocator, size, alignment, \ + file, line) \ + mem_allocator_malloc_aligned(allocator, size, alignment) + +#else /* BH_ENABLE_GC_VERIFY != 0 */ + +void * +mem_allocator_malloc_aligned_internal(mem_allocator_t allocator, uint32_t size, + uint32_t alignment, const char *file, + int line); + +#define mem_allocator_malloc_aligned(allocator, size, alignment) \ + mem_allocator_malloc_aligned_internal(allocator, size, alignment, \ + __FILE__, __LINE__) + +#endif /* end of BH_ENABLE_GC_VERIFY */ + +int +mem_allocator_migrate(mem_allocator_t allocator, char *pool_buf_new, + uint32 pool_buf_size); + +bool +mem_allocator_is_heap_corrupted(mem_allocator_t allocator); + +#if WASM_ENABLE_GC != 0 +void * +mem_allocator_malloc_with_gc(mem_allocator_t allocator, uint32_t size); + +#if WASM_GC_MANUALLY != 0 +void +mem_allocator_free_with_gc(mem_allocator_t allocator, void *ptr); +#endif + +#if WASM_ENABLE_THREAD_MGR == 0 +void +mem_allocator_enable_gc_reclaim(mem_allocator_t allocator, void *exec_env); +#else +void +mem_allocator_enable_gc_reclaim(mem_allocator_t allocator, void *cluster); +#endif + +int +mem_allocator_add_root(mem_allocator_t allocator, WASMObjectRef obj); + +bool +mem_allocator_set_gc_finalizer(mem_allocator_t allocator, void *obj, + gc_finalizer_t cb, void *data); + +void +mem_allocator_unset_gc_finalizer(mem_allocator_t allocator, void *obj); + +#if WASM_ENABLE_GC_PERF_PROFILING != 0 +void +mem_allocator_dump_perf_profiling(mem_allocator_t allocator); +#endif +#endif /* end of WASM_ENABLE_GC != 0 */ + +bool +mem_allocator_get_alloc_info(mem_allocator_t allocator, void *mem_alloc_info); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef __MEM_ALLOC_H */ diff --git a/core/shared/platform/CMakeLists.txt b/core/shared/platform/CMakeLists.txt deleted file mode 100755 index 7abefa2b38..0000000000 --- a/core/shared/platform/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -include_directories (./include ../include ./${WAMR_BUILD_PLATFORM}) - -add_definitions (-D__POSIX__ -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200809L -D_BSD_SOURCE) - -file (GLOB_RECURSE source_all ${WAMR_BUILD_PLATFORM}/*.c) -add_library (supportlib ${source_all}) - -target_link_libraries (supportlib -pthread -lrt) - -if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - add_library (supportlib_ut ${source_all}) - - set_target_properties (supportlib_ut PROPERTIES COMPILE_DEFINITIONS BH_TEST=1) - - target_link_libraries (supportlib_ut -pthread -lrt) -endif () - diff --git a/core/shared/platform/Makefile b/core/shared/platform/Makefile deleted file mode 100644 index cd10597a18..0000000000 --- a/core/shared/platform/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -obj-y += zephyr/ diff --git a/core/shared/platform/README.md b/core/shared/platform/README.md new file mode 100644 index 0000000000..de6f1cc688 --- /dev/null +++ b/core/shared/platform/README.md @@ -0,0 +1,10 @@ +This folder contains the platform abstract layer for multiple platforms. To support a new platform, you can simply create a new folder here and implement all the APIs defined in [`include`](./include) folder. + + + +Refer to [port_wamr.md](../../../doc/port_wamr.md) for how to port WAMR to a target platform. + + + + + diff --git a/core/shared/platform/alios/alios_platform.c b/core/shared/platform/alios/alios_platform.c new file mode 100644 index 0000000000..a3752b4395 --- /dev/null +++ b/core/shared/platform/alios/alios_platform.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +int +os_thread_sys_init(); + +void +os_thread_sys_destroy(); + +int +bh_platform_init() +{ + return os_thread_sys_init(); +} + +void +bh_platform_destroy() +{ + os_thread_sys_destroy(); +} + +void * +os_malloc(unsigned size) +{ + return NULL; +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return NULL; +} + +void +os_free(void *ptr) +{} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + void *addr; + + if (size >= UINT32_MAX) + return NULL; + + if ((addr = BH_MALLOC((uint32)size))) + memset(addr, 0, (uint32)size); + + return addr; +} + +void +os_munmap(void *addr, size_t size) +{ + return BH_FREE(addr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush() +{} + +void +os_icache_flush(void *start, size_t len) +{} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return -1; +} diff --git a/core/shared/platform/alios/alios_thread.c b/core/shared/platform/alios/alios_thread.c new file mode 100644 index 0000000000..9fe927db0e --- /dev/null +++ b/core/shared/platform/alios/alios_thread.c @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +/* clang-format off */ +#define bh_assert(v) do { \ + if (!(v)) { \ + printf("\nASSERTION FAILED: %s, at %s, line %d\n", \ + #v, __FILE__, __LINE__); \ + aos_reboot(); \ + while (1); \ + } \ +} while (0) +/* clang-format on */ + +struct os_thread_data; +typedef struct os_thread_wait_node { + aos_sem_t sem; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Thread body */ + aos_task_t thread; + /* Thread start routine */ + thread_start_routine_t start_routine; + /* Thread start routine argument */ + void *arg; + /* Thread local root */ + void *tlr; + /* Wait node of current thread */ + os_thread_wait_node wait_node; + /* Lock for waiting list */ + aos_mutex_t wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; +} os_thread_data; + +static bool is_thread_sys_inited = false; + +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Thread data key */ +static aos_task_key_t thread_data_key; + +/* Thread name index */ +static int thread_name_index; + +int +os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + + if (aos_task_key_create(&thread_data_key) != 0) + return BHT_ERROR; + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); + + if (aos_sem_new(&supervisor_thread_data.wait_node.sem, 1) != 0) { + aos_task_key_delete(thread_data_key); + return BHT_ERROR; + } + + if (aos_task_setspecific(thread_data_key, &supervisor_thread_data)) { + aos_sem_free(&supervisor_thread_data.wait_node.sem); + aos_task_key_delete(thread_data_key); + return BHT_ERROR; + } + + is_thread_sys_inited = true; + return BHT_OK; +} + +void +os_thread_sys_destroy() +{ + if (is_thread_sys_inited) { + aos_task_key_delete(thread_data_key); + aos_sem_free(&supervisor_thread_data.wait_node.sem); + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + return aos_task_getspecific(thread_data_key); +} + +static void +os_thread_cleanup(void) +{ + os_thread_data *thread_data = thread_data_current(); + os_thread_wait_list thread_wait_list; + aos_mutex_t *wait_list_lock; + aos_sem_t *wait_node_sem; + + bh_assert(thread_data != NULL); + wait_list_lock = &thread_data->wait_list_lock; + thread_wait_list = thread_data->thread_wait_list; + wait_node_sem = &thread_data->wait_node.sem; + + /* Free thread data firstly */ + BH_FREE(thread_data); + + aos_mutex_lock(wait_list_lock, AOS_WAIT_FOREVER); + if (thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + aos_sem_signal(&head->sem); + head = next; + } + } + aos_mutex_unlock(wait_list_lock); + + /* Free sem and lock */ + aos_sem_free(wait_node_sem); + aos_mutex_free(wait_list_lock); +} + +static void +os_thread_wrapper(void *arg) +{ + os_thread_data *thread_data = arg; + + /* Set thread custom data */ + if (!aos_task_setspecific(thread_data_key, thread_data)) + thread_data->start_routine(thread_data->arg); + + os_thread_cleanup(); +} + +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + os_thread_data *thread_data; + char thread_name[32]; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Create and initialize thread data */ + if (!(thread_data = BH_MALLOC(sizeof(os_thread_data)))) + return BHT_ERROR; + + memset(thread_data, 0, sizeof(os_thread_data)); + + thread_data->start_routine = start; + thread_data->arg = arg; + + if (aos_sem_new(&thread_data->wait_node.sem, 1) != 0) + goto fail1; + + if (aos_mutex_new(&thread_data->wait_list_lock)) + goto fail2; + + snprintf(thread_name, sizeof(thread_name), "%s%d", "wasm-thread-", + ++thread_name_index); + + /* Create the thread */ + if (aos_task_new_ext((aos_task_t *)thread_data, thread_name, + os_thread_wrapper, thread_data, stack_size, prio)) + goto fail3; + + aos_msleep(10); + *p_tid = (korp_tid)thread_data; + return BHT_OK; + +fail3: + aos_mutex_free(&thread_data->wait_list_lock); +fail2: + aos_sem_free(&thread_data->wait_node.sem); +fail1: + BH_FREE(thread_data); + return BHT_ERROR; +} + +korp_tid +os_self_thread() +{ + return (korp_tid)aos_task_getspecific(thread_data_key); +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ + (void)value_ptr; + os_thread_data *thread_data, *curr_thread_data; + + /* Get thread data of current thread */ + curr_thread_data = thread_data_current(); + curr_thread_data->wait_node.next = NULL; + + /* Get thread data */ + thread_data = (os_thread_data *)thread; + + aos_mutex_lock(&thread_data->wait_list_lock, AOS_WAIT_FOREVER); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = &curr_thread_data->wait_node; + else { + /* 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; + } + aos_mutex_unlock(&thread_data->wait_list_lock); + + /* Wait the sem */ + aos_sem_wait(&curr_thread_data->wait_node.sem, AOS_WAIT_FOREVER); + + return BHT_OK; +} + +int +os_mutex_init(korp_mutex *mutex) +{ + return aos_mutex_new(mutex) == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + aos_mutex_free(mutex); + return BHT_OK; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + return aos_mutex_lock(mutex, AOS_WAIT_FOREVER); +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + return aos_mutex_unlock(mutex); +} + +int +os_cond_init(korp_cond *cond) +{ + if (aos_mutex_new(&cond->wait_list_lock) != 0) + return BHT_ERROR; + + cond->thread_wait_list = NULL; + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + aos_mutex_free(&cond->wait_list_lock); + return BHT_OK; +} + +static int +os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, + uint32 mills) +{ + os_thread_wait_node *node = &thread_data_current()->wait_node; + + node->next = NULL; + + aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); + if (!cond->thread_wait_list) + cond->thread_wait_list = node; + else { + /* Add to end of wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + aos_mutex_unlock(&cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + aos_mutex_unlock(mutex); + aos_sem_wait(&node->sem, timed ? mills : AOS_WAIT_FOREVER); + aos_mutex_lock(mutex, AOS_WAIT_FOREVER); + + /* Remove wait node from wait list */ + aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); + if (cond->thread_wait_list == node) + cond->thread_wait_list = node->next; + 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; + } + aos_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + if (useconds == BHT_WAIT_FOREVER) { + return os_cond_wait_internal(cond, mutex, false, 0); + } + else { + uint64 mills_64 = useconds / 1000; + uint32 mills; + + if (mills_64 < (uint64)(UINT32_MAX - 1)) { + mills = (uint64)mills_64; + } + else { + mills = UINT32_MAX - 1; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + return os_cond_wait_internal(cond, mutex, true, mills); + } +} + +int +os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); + if (cond->thread_wait_list) + aos_sem_signal(&cond->thread_wait_list->sem); + aos_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +uint8 * +os_thread_get_stack_boundary() +{ + /* TODO: get alios stack boundary */ + return NULL; +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} \ No newline at end of file diff --git a/core/shared/platform/alios/alios_time.c b/core/shared/platform/alios/alios_time.c new file mode 100644 index 0000000000..fb09623a65 --- /dev/null +++ b/core/shared/platform/alios/alios_time.c @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_us() +{ + return (uint64)aos_now_ms() * 1000; +} + +uint64 +os_time_thread_cputime_us(void) +{ + /* FIXME if u know the right api */ + return os_time_get_boot_us(); +} \ No newline at end of file diff --git a/core/shared/platform/alios/bh_assert.c b/core/shared/platform/alios/bh_assert.c deleted file mode 100644 index 60f3b2deb7..0000000000 --- a/core/shared/platform/alios/bh_assert.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_assert.h" -#include -#include -#include - -#ifdef BH_TEST -#include -#endif - -#ifdef BH_TEST -/* for exception throwing */ -jmp_buf bh_test_jb; -#endif - -void bh_assert_internal(int v, const char *file_name, int line_number, const char *expr_string) -{ - if(v) return; - - if(!file_name) file_name = "NULL FILENAME"; - if(!expr_string) expr_string = "NULL EXPR_STRING"; - - printf("\nASSERTION FAILED: %s, at FILE=%s, LINE=%d\n", expr_string, file_name, line_number); - -#ifdef BH_TEST - longjmp(bh_test_jb, 1); -#endif - - aos_reboot(); -} - -void bh_debug_internal(const char *file_name, int line_number, const char *fmt, ...) -{ -#ifndef JEFF_TEST_VERIFIER - va_list args; - - va_start(args, fmt); - bh_assert(file_name); - - printf("\nDebug info FILE=%s, LINE=%d: ", file_name, line_number); - vprintf(fmt, args); - - va_end(args); - printf("\n"); -#endif -} - diff --git a/core/shared/platform/alios/bh_definition.c b/core/shared/platform/alios/bh_definition.c deleted file mode 100644 index 1bd85b1259..0000000000 --- a/core/shared/platform/alios/bh_definition.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" - -#define RSIZE_MAX 0x7FFFFFFF - -int b_memcpy_s(void * s1, unsigned int s1max, - const void * s2, unsigned int n) -{ - char *dest = (char*)s1; - char *src = (char*)s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} - -int b_strcat_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s1) + strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcat(s1, s2); - return 0; -} - -int b_strcpy_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcpy(s1, s2); - return 0; -} - diff --git a/core/shared/platform/alios/bh_math.c b/core/shared/platform/alios/bh_math.c deleted file mode 100644 index 913d521675..0000000000 --- a/core/shared/platform/alios/bh_math.c +++ /dev/null @@ -1,861 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2004 David Schultz - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#include "bh_platform.h" - -#define __FDLIBM_STDC__ - -typedef uint32_t u_int32_t; -typedef uint64_t u_int64_t; - -typedef union u32double_tag { - int *pint; - double *pdouble; -} U32DOUBLE; - -static inline int * -pdouble2pint(double *pdouble) -{ - U32DOUBLE u; - u.pdouble = pdouble; - return u.pint; -} - -typedef union -{ - double value; - struct - { - u_int32_t lsw; - u_int32_t msw; - } parts; - struct - { - u_int64_t w; - } xparts; -} ieee_double_shape_type_little; - -typedef union -{ - double value; - struct - { - u_int32_t msw; - u_int32_t lsw; - } parts; - struct - { - u_int64_t w; - } xparts; -} ieee_double_shape_type_big; - -typedef union { - double d; - struct { - unsigned int manl :32; - unsigned int manh :20; - unsigned int exp :11; - unsigned int sign :1; - } bits; -} IEEEd2bits_L; - -typedef union { - double d; - struct { - unsigned int sign :1; - unsigned int exp :11; - unsigned int manh :20; - unsigned int manl :32; - } bits; -} IEEEd2bits_B; - -typedef union { - float f; - struct { - unsigned int man :23; - unsigned int exp :8; - unsigned int sign :1; - } bits; -} IEEEf2bits_L; - -typedef union { - float f; - struct { - unsigned int sign :1; - unsigned int exp :8; - unsigned int man :23; - } bits; -} IEEEf2bits_B; - -static union { - int a; - char b; -} __ue = { .a = 1 }; - -#define is_little_endian() (__ue.b == 1) - -#define __HIL(x) *(1+pdouble2pint(&x)) -#define __LOL(x) *(pdouble2pint(&x)) -#define __HIB(x) *(int*)&x -#define __LOB(x) *(1+(int*)&x) - -/* Get two 32 bit ints from a double. */ - -#define EXTRACT_WORDS_L(ix0,ix1,d) \ - do { \ - ieee_double_shape_type_little ew_u; \ - ew_u.value = (d); \ - (ix0) = ew_u.parts.msw; \ - (ix1) = ew_u.parts.lsw; \ - } while (0) - -/* Set a double from two 32 bit ints. */ - -#define INSERT_WORDS_L(d,ix0,ix1) \ - do { \ - ieee_double_shape_type_little iw_u; \ - iw_u.parts.msw = (ix0); \ - iw_u.parts.lsw = (ix1); \ - (d) = iw_u.value; \ - } while (0) - -/* Get two 32 bit ints from a double. */ - -#define EXTRACT_WORDS_B(ix0,ix1,d) \ - do { \ - ieee_double_shape_type_big ew_u; \ - ew_u.value = (d); \ - (ix0) = ew_u.parts.msw; \ - (ix1) = ew_u.parts.lsw; \ - } while (0) - -/* Set a double from two 32 bit ints. */ - -#define INSERT_WORDS_B(d,ix0,ix1) \ - do { \ - ieee_double_shape_type_big iw_u; \ - iw_u.parts.msw = (ix0); \ - iw_u.parts.lsw = (ix1); \ - (d) = iw_u.value; \ - } while (0) - -/* Get the more significant 32 bit int from a double. */ -#define GET_HIGH_WORD_L(i,d) \ - do { \ - ieee_double_shape_type_little gh_u; \ - gh_u.value = (d); \ - (i) = gh_u.parts.msw; \ - } while (0) - -/* Get the more significant 32 bit int from a double. */ -#define GET_HIGH_WORD_B(i,d) \ - do { \ - ieee_double_shape_type_big gh_u; \ - gh_u.value = (d); \ - (i) = gh_u.parts.msw; \ - } while (0) - -/* Set the more significant 32 bits of a double from an int. */ -#define SET_HIGH_WORD_L(d,v) \ - do { \ - ieee_double_shape_type_little sh_u; \ - sh_u.value = (d); \ - sh_u.parts.msw = (v); \ - (d) = sh_u.value; \ - } while (0) - -/* Set the more significant 32 bits of a double from an int. */ -#define SET_HIGH_WORD_B(d,v) \ - do { \ - ieee_double_shape_type_big sh_u; \ - sh_u.value = (d); \ - sh_u.parts.msw = (v); \ - (d) = sh_u.value; \ - } while (0) - -/* - * A union which permits us to convert between a float and a 32 bit - * int. - */ -typedef union -{ - float value; - /* FIXME: Assumes 32 bit int. */ - unsigned int word; -} ieee_float_shape_type; - -/* Get a 32 bit int from a float. */ -#define GET_FLOAT_WORD(i,d) \ - do { \ - ieee_float_shape_type gf_u; \ - gf_u.value = (d); \ - (i) = gf_u.word; \ - } while (0) - -/* Set a float from a 32 bit int. */ -#define SET_FLOAT_WORD(d,i) \ - do { \ - ieee_float_shape_type sf_u; \ - sf_u.word = (i); \ - (d) = sf_u.value; \ - } while (0) - -/* Macro wrappers. */ -#define EXTRACT_WORDS(ix0,ix1,d) do { \ - if (is_little_endian()) \ - EXTRACT_WORDS_L(ix0,ix1,d); \ - else \ - EXTRACT_WORDS_B(ix0,ix1,d); \ -} while (0) - -#define INSERT_WORDS(d,ix0,ix1) do { \ - if (is_little_endian()) \ - INSERT_WORDS_L(d,ix0,ix1); \ - else \ - INSERT_WORDS_B(d,ix0,ix1); \ -} while (0) - -#define GET_HIGH_WORD(i,d) \ - do { \ - if (is_little_endian()) \ - GET_HIGH_WORD_L(i,d); \ - else \ - GET_HIGH_WORD_B(i,d); \ - } while (0) - -#define SET_HIGH_WORD(d,v) \ - do { \ - if (is_little_endian()) \ - SET_HIGH_WORD_L(d,v); \ - else \ - SET_HIGH_WORD_B(d,v); \ - } while (0) - -#define __HI(x) (is_little_endian() ? __HIL(x) : __HIB(x)) - -#define __LO(x) (is_little_endian() ? __LOL(x) : __LOB(x)) - -/* - * Attempt to get strict C99 semantics for assignment with non-C99 compilers. - */ -#if FLT_EVAL_METHOD == 0 || __GNUC__ == 0 -#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval)) -#else -#define STRICT_ASSIGN(type, lval, rval) do { \ - volatile type __lval; \ - \ - if (sizeof(type) >= sizeof(long double)) \ - (lval) = (rval); \ - else { \ - __lval = (rval); \ - (lval) = __lval; \ - } \ -} while (0) -#endif - -#ifdef __FDLIBM_STDC__ -static const double huge = 1.0e300; -#else -static double huge = 1.0e300; -#endif - -#ifdef __STDC__ -static const double -#else -static double -#endif -tiny = 1.0e-300; - -#ifdef __STDC__ -static const double -#else -static double -#endif -one= 1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */ - -#ifdef __STDC__ -static const double -#else -static double -#endif -TWO52[2]={ - 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ - -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ -}; - -static double freebsd_sqrt(double x); -static double freebsd_floor(double x); -static double freebsd_ceil(double x); -static double freebsd_fabs(double x); -static double freebsd_rint(double x); -static int freebsd_isnan(double x); - -static double freebsd_sqrt(double x) /* wrapper sqrt */ -{ - double z; - int32_t sign = (int)0x80000000; - int32_t ix0,s0,q,m,t,i; - u_int32_t r,t1,s1,ix1,q1; - - EXTRACT_WORDS(ix0,ix1,x); - - /* take care of Inf and NaN */ - if((ix0&0x7ff00000)==0x7ff00000) { - return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf - sqrt(-inf)=sNaN */ - } - /* take care of zero */ - if(ix0<=0) { - if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ - else if(ix0<0) - return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ - } - /* normalize x */ - m = (ix0>>20); - if(m==0) { /* subnormal x */ - while(ix0==0) { - m -= 21; - ix0 |= (ix1>>11); ix1 <<= 21; - } - for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; - m -= i-1; - ix0 |= (ix1>>(32-i)); - ix1 <<= i; - } - m -= 1023; /* unbias exponent */ - ix0 = (ix0&0x000fffff)|0x00100000; - if(m&1){ /* odd m, double x to make it even */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - } - m >>= 1; /* m = [m/2] */ - - /* generate sqrt(x) bit by bit */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ - r = 0x00200000; /* r = moving bit from right to left */ - - while(r!=0) { - t = s0+r; - if(t<=ix0) { - s0 = t+r; - ix0 -= t; - q += r; - } - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - r>>=1; - } - - r = sign; - while(r!=0) { - t1 = s1+r; - t = s0; - if((t>31); - ix1 += ix1; - r>>=1; - } - - /* use floating add to find out rounding direction */ - if((ix0|ix1)!=0) { - z = one-tiny; /* trigger inexact flag */ - if (z>=one) { - z = one+tiny; - if (q1==(u_int32_t)0xffffffff) { q1=0; q += 1;} - else if (z>one) { - if (q1==(u_int32_t)0xfffffffe) q+=1; - q1+=2; - } else - q1 += (q1&1); - } - } - ix0 = (q>>1)+0x3fe00000; - ix1 = q1>>1; - if ((q&1)==1) ix1 |= sign; - ix0 += (m <<20); - - INSERT_WORDS(z,ix0,ix1); - - return z; -} - -static double freebsd_floor(double x) -{ - int32_t i0,i1,j0; - u_int32_t i,j; - - EXTRACT_WORDS(i0,i1,x); - - j0 = ((i0>>20)&0x7ff)-0x3ff; - if(j0<20) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0>=0) {i0=i1=0;} - else if(((i0&0x7fffffff)|i1)!=0) - { i0=0xbff00000;i1=0;} - } - } else { - i = (0x000fffff)>>j0; - if(((i0&i)|i1)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0<0) i0 += (0x00100000)>>j0; - i0 &= (~i); i1=0; - } - } - } else if (j0>51) { - if(j0==0x400) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } else { - i = ((u_int32_t)(0xffffffff))>>(j0-20); - if((i1&i)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0<0) { - if(j0==20) i0+=1; - else { - j = i1+(1<<(52-j0)); - if(j>20)&0x7ff)-0x3ff; - if(j0<20) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0<0) {i0=0x80000000;i1=0;} - else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} - } - } else { - i = (0x000fffff)>>j0; - if(((i0&i)|i1)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0>0) i0 += (0x00100000)>>j0; - i0 &= (~i); i1=0; - } - } - } else if (j0>51) { - if(j0==0x400) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } else { - i = ((u_int32_t)(0xffffffff))>>(j0-20); - if((i1&i)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0>0) { - if(j0==20) i0+=1; - else { - j = i1 + (1<<(52-j0)); - if(j>31)&1; - j0 = ((i0>>20)&0x7ff)-0x3ff; - if(j0<20) { - if(j0<0) { - if(((i0&0x7fffffff)|i1)==0) return x; - i1 |= (i0&0x0fffff); - i0 &= 0xfffe0000; - i0 |= ((i1|-i1)>>12)&0x80000; - SET_HIGH_WORD(x,i0); - STRICT_ASSIGN(double,w,TWO52[sx]+x); - t = w-TWO52[sx]; - GET_HIGH_WORD(i0,t); - SET_HIGH_WORD(t,(i0&0x7fffffff)|(sx<<31)); - return t; - } else { - i = (0x000fffff)>>j0; - if(((i0&i)|i1)==0) return x; /* x is integral */ - i>>=1; - if(((i0&i)|i1)!=0) { - /* - * Some bit is set after the 0.5 bit. To avoid the - * possibility of errors from double rounding in - * w = TWO52[sx]+x, adjust the 0.25 bit to a lower - * guard bit. We do this for all j0<=51. The - * adjustment is trickiest for j0==18 and j0==19 - * since then it spans the word boundary. - */ - if(j0==19) i1 = 0x40000000; else - if(j0==18) i1 = 0x80000000; else - i0 = (i0&(~i))|((0x20000)>>j0); - } - } - } else if (j0>51) { - if(j0==0x400) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } else { - i = ((u_int32_t)(0xffffffff))>>(j0-20); - if((i1&i)==0) return x; /* x is integral */ - i>>=1; - if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); - } - INSERT_WORDS(x,i0,i1); - STRICT_ASSIGN(double,w,TWO52[sx]+x); - return w-TWO52[sx]; -} - -static int freebsd_isnan(double d) -{ - if (is_little_endian()) { - IEEEd2bits_L u; - u.d = d; - return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); - } - else { - IEEEd2bits_B u; - u.d = d; - return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); - } -} - -static double freebsd_fabs(double x) -{ - u_int32_t high; - GET_HIGH_WORD(high,x); - SET_HIGH_WORD(x,high&0x7fffffff); - return x; -} - -static const float huge_f = 1.0e30F; - -static const float -TWO23[2]={ - 8.3886080000e+06, /* 0x4b000000 */ - -8.3886080000e+06, /* 0xcb000000 */ -}; - -static float -freebsd_truncf(float x) -{ - int32_t i0,j0; - u_int32_t i; - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge_f+x>0.0F) /* |x|<1, so return 0*sign(x) */ - i0 &= 0x80000000; - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) return x; /* x is integral */ - if(huge_f+x>0.0F) /* raise inexact flag */ - i0 &= (~i); - } - } else { - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } - SET_FLOAT_WORD(x,i0); - return x; -} - -static float -freebsd_rintf(float x) -{ - int32_t i0,j0,sx; - float w,t; - GET_FLOAT_WORD(i0,x); - sx = (i0>>31)&1; - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { - if((i0&0x7fffffff)==0) return x; - STRICT_ASSIGN(float,w,TWO23[sx]+x); - t = w-TWO23[sx]; - GET_FLOAT_WORD(i0,t); - SET_FLOAT_WORD(t,(i0&0x7fffffff)|(sx<<31)); - return t; - } - STRICT_ASSIGN(float,w,TWO23[sx]+x); - return w-TWO23[sx]; - } - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ -} - -static float -freebsd_ceilf(float x) -{ - int32_t i0,j0; - u_int32_t i; - - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge_f+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0<0) {i0=0x80000000;} - else if(i0!=0) { i0=0x3f800000;} - } - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) return x; /* x is integral */ - if(huge_f+x>(float)0.0) { /* raise inexact flag */ - if(i0>0) i0 += (0x00800000)>>j0; - i0 &= (~i); - } - } - } else { - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } - SET_FLOAT_WORD(x,i0); - return x; -} - -static float -freebsd_floorf(float x) -{ - int32_t i0,j0; - u_int32_t i; - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge_f+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0>=0) {i0=0;} - else if((i0&0x7fffffff)!=0) - { i0=0xbf800000;} - } - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) return x; /* x is integral */ - if(huge_f+x>(float)0.0) { /* raise inexact flag */ - if(i0<0) i0 += (0x00800000)>>j0; - i0 &= (~i); - } - } - } else { - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } - SET_FLOAT_WORD(x,i0); - return x; -} - -static float -freebsd_fminf(float x, float y) -{ - if (is_little_endian()) { - IEEEf2bits_L u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[1].bits.sign].f); - } - else { - IEEEf2bits_B u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[1].bits.sign].f); - } - - return (x < y ? x : y); -} - -static float -freebsd_fmaxf(float x, float y) -{ - if (is_little_endian()) { - IEEEf2bits_L u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[0].bits.sign].f); - } - else { - IEEEf2bits_B u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[0].bits.sign].f); - } - - return (x > y ? x : y); -} - -double sqrt(double x) -{ - return freebsd_sqrt(x); -} - -double floor(double x) -{ - return freebsd_floor(x); -} - -double ceil(double x) -{ - return freebsd_ceil(x); -} - -double fmin(double x, double y) -{ - return x < y ? x : y; -} - -double fmax(double x, double y) -{ - return x > y ? x : y; -} - -double rint(double x) -{ - return freebsd_rint(x); -} - -double fabs(double x) -{ - return freebsd_fabs(x); -} - -int isnan(double x) -{ - return freebsd_isnan(x); -} - -double trunc(double x) -{ - return (x > 0) ? freebsd_floor(x) : freebsd_ceil(x); -} - -int signbit(double x) -{ - return ((__HI(x) & 0x80000000) >> 31); -} - -float -truncf(float x) -{ - return freebsd_truncf(x); -} - -float -rintf(float x) -{ - return freebsd_rintf(x); -} - -float -ceilf(float x) -{ - return freebsd_ceilf(x); -} - -float -floorf(float x) -{ - return freebsd_floorf(x); -} - -float -fminf(float x, float y) -{ - return freebsd_fminf(x, y); -} - -float -fmaxf(float x, float y) -{ - return freebsd_fmaxf(x, y); -} - diff --git a/core/shared/platform/alios/bh_platform.c b/core/shared/platform/alios/bh_platform.c deleted file mode 100644 index bfa8d9e423..0000000000 --- a/core/shared/platform/alios/bh_platform.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_memory.h" -#include "bh_common.h" -#include -#include - -char *bh_strdup(const char *s) -{ - uint32 size; - char *s1 = NULL; - - if (s) { - size = (uint32)(strlen(s) + 1); - if ((s1 = bh_malloc(size))) - bh_memcpy_s(s1, size, s, size); - } - return s1; -} - -int bh_platform_init() -{ - return 0; -} - -void * -bh_mmap(void *hint, unsigned int size, int prot, int flags) -{ - return bh_malloc(size); -} - -void -bh_munmap(void *addr, uint32 size) -{ - return bh_free(addr); -} - -int -bh_mprotect(void *addr, uint32 size, int prot) -{ - return 0; -} - diff --git a/core/shared/platform/alios/bh_platform.h b/core/shared/platform/alios/bh_platform.h deleted file mode 100644 index 5f66a69fdb..0000000000 --- a/core/shared/platform/alios/bh_platform.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_PLATFORM_H -#define _BH_PLATFORM_H - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_memory.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef BH_PLATFORM_ALIOS_THINGS -#define BH_PLATFORM_ALIOS_THINGS -#endif - -#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) - -/* Default thread priority */ -#define BH_THREAD_DEFAULT_PRIORITY 30 - -#define BH_ROUTINE_MODIFIER - -/* Invalid thread tid */ -#define INVALID_THREAD_ID NULL - -#define BH_WAIT_FOREVER AOS_WAIT_FOREVER - -typedef uint64_t uint64; -typedef int64_t int64; - -#define wa_malloc bh_malloc -#define wa_free bh_free -#define wa_strdup bh_strdup - -typedef aos_task_t korp_thread; -typedef korp_thread *korp_tid; -typedef aos_task_t *aos_tid_t; -typedef aos_mutex_t korp_mutex; -typedef aos_sem_t korp_sem; - -struct bh_thread_wait_node; -typedef struct bh_thread_wait_node *bh_thread_wait_list; -typedef struct korp_cond { - aos_mutex_t wait_list_lock; - bh_thread_wait_list thread_wait_list; -} korp_cond; - -typedef void* (*thread_start_routine_t)(void*); - -/* Unit test framework is based on C++, where the declaration of - snprintf is different. */ -#ifndef __cplusplus -int snprintf(char *buffer, size_t count, const char *format, ...); -#endif - -#ifndef NULL -#define NULL ((void*)0) -#endif - -/** - * Return the offset of the given field in the given type. - * - * @param Type the type containing the filed - * @param field the field in the type - * - * @return the offset of field in Type - */ -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) -#endif - -#define bh_printf printf - -extern void bh_assert_internal(int v, const char *file_name, int line_number, const char *expr_string); -#define bh_assert(expr) bh_assert_internal((int)(expr), __FILE__, __LINE__, # expr) - -extern int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n); -extern int b_strcat_s(char * s1, size_t s1max, const char * s2); -extern int b_strcpy_s(char * s1, size_t s1max, const char * s2); - -/* math functions */ -double sqrt(double x); -double floor(double x); -double ceil(double x); -double fmin(double x, double y); -double fmax(double x, double y); -double rint(double x); -double fabs(double x); -double trunc(double x); -float floorf(float x); -float ceilf(float x); -float fminf(float x, float y); -float fmaxf(float x, float y); -float rintf(float x); -float truncf(float x); -int signbit(double x); -int isnan(double x); - -int bh_platform_init(); - -/* MMAP mode */ -enum { - MMAP_PROT_NONE = 0, - MMAP_PROT_READ = 1, - MMAP_PROT_WRITE = 2, - MMAP_PROT_EXEC = 4 -}; - -/* MMAP flags */ -enum { - MMAP_MAP_NONE = 0, - /* Put the mapping into 0 to 2 G, supported only on x86_64 */ - MMAP_MAP_32BIT = 1, - /* Don't interpret addr as a hint: place the mapping at exactly - that address. */ - MMAP_MAP_FIXED = 2 -}; - -void *bh_mmap(void *hint, unsigned int size, int prot, int flags); -void bh_munmap(void *addr, uint32 size); -int bh_mprotect(void *addr, uint32 size, int prot); - -#endif /* end of _BH_PLATFORM_H */ - diff --git a/core/shared/platform/alios/bh_platform_log.c b/core/shared/platform/alios/bh_platform_log.c deleted file mode 100644 index 8c9e114d22..0000000000 --- a/core/shared/platform/alios/bh_platform_log.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include - - -void bh_log_emit(const char *fmt, va_list ap) -{ - vprintf(fmt, ap); -} - -int bh_fprintf(FILE *stream, const char *fmt, ...) -{ - (void)stream; - va_list ap; - int ret; - - va_start(ap, fmt); - ret = vprintf(fmt, ap); - va_end(ap); - - return ret; -} - -int bh_fflush(void *stream) -{ - (void)stream; - return 0; -} - diff --git a/core/shared/platform/alios/bh_thread.c b/core/shared/platform/alios/bh_thread.c deleted file mode 100644 index a177519146..0000000000 --- a/core/shared/platform/alios/bh_thread.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_thread.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include -#include - - -struct bh_thread_data; -typedef struct bh_thread_wait_node { - aos_sem_t sem; - bh_thread_wait_list next; -} bh_thread_wait_node; - -typedef struct bh_thread_data { - /* Thread body */ - aos_task_t thread; - /* Thread start routine */ - thread_start_routine_t start_routine; - /* Thread start routine argument */ - void *arg; - /* Thread local root */ - void *tlr; - /* Wait node of current thread */ - bh_thread_wait_node wait_node; - /* Lock for waiting list */ - aos_mutex_t wait_list_lock; - /* Waiting list of other threads who are joining this thread */ - bh_thread_wait_list thread_wait_list; -} bh_thread_data; - -static bool is_thread_sys_inited = false; - -/* Thread data of supervisor thread */ -static bh_thread_data supervisor_thread_data; - -/* Thread data key */ -static aos_task_key_t thread_data_key; - -/* Thread name index */ -static int thread_name_index; - -int -_vm_thread_sys_init() -{ - if (is_thread_sys_inited) - return BHT_OK; - - if (aos_task_key_create(&thread_data_key) != 0) - return BHT_ERROR; - - /* Initialize supervisor thread data */ - memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); - - if (aos_sem_new(&supervisor_thread_data.wait_node.sem, 1) != 0) { - aos_task_key_delete(thread_data_key); - return BHT_ERROR; - } - - if (aos_task_setspecific(thread_data_key, &supervisor_thread_data)) { - aos_sem_free(&supervisor_thread_data.wait_node.sem); - aos_task_key_delete(thread_data_key); - return BHT_ERROR; - } - - is_thread_sys_inited = true; - return BHT_OK; -} - -void -vm_thread_sys_destroy() -{ - if (is_thread_sys_inited) { - aos_task_key_delete(thread_data_key); - aos_sem_free(&supervisor_thread_data.wait_node.sem); - is_thread_sys_inited = false; - } -} - -static bh_thread_data * -thread_data_current() -{ - return aos_task_getspecific(thread_data_key); -} - -static void -vm_thread_cleanup(void) -{ - bh_thread_data *thread_data = thread_data_current(); - bh_thread_wait_list thread_wait_list; - aos_mutex_t *wait_list_lock; - aos_sem_t *wait_node_sem; - - bh_assert(thread_data != NULL); - wait_list_lock = &thread_data->wait_list_lock; - thread_wait_list = thread_data->thread_wait_list; - wait_node_sem = &thread_data->wait_node.sem; - - /* Free thread data firstly */ - bh_free(thread_data); - - aos_mutex_lock(wait_list_lock, AOS_WAIT_FOREVER); - if (thread_wait_list) { - /* Signal each joining thread */ - bh_thread_wait_list head = thread_wait_list; - while (head) { - bh_thread_wait_list next = head->next; - aos_sem_signal(&head->sem); - head = next; - } - } - aos_mutex_unlock(wait_list_lock); - - /* Free sem and lock */ - aos_sem_free(wait_node_sem); - aos_mutex_free(wait_list_lock); -} - -static void -vm_thread_wrapper(void *arg) -{ - bh_thread_data *thread_data = arg; - - /* Set thread custom data */ - if (!aos_task_setspecific(thread_data_key, thread_data)) - thread_data->start_routine(thread_data->arg); - - vm_thread_cleanup(); -} - -int -_vm_thread_create(korp_tid *p_tid, thread_start_routine_t start, - void *arg, unsigned int stack_size) -{ - return _vm_thread_create_with_prio(p_tid, start, arg, stack_size, - BH_THREAD_DEFAULT_PRIORITY); -} - -int -_vm_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio) -{ - bh_thread_data *thread_data; - char thread_name[32]; - - if (!p_tid || !stack_size) - return BHT_ERROR; - - /* Create and initialize thread data */ - if (!(thread_data = bh_malloc(sizeof(bh_thread_data)))) - return BHT_ERROR; - - memset(thread_data, 0, sizeof(bh_thread_data)); - - thread_data->start_routine = start; - thread_data->arg = arg; - - if (aos_sem_new(&thread_data->wait_node.sem, 1) != 0) - goto fail1; - - if (aos_mutex_new(&thread_data->wait_list_lock)) - goto fail2; - - snprintf(thread_name, sizeof(thread_name), "%s%d", - "wasm-thread-", ++thread_name_index); - - /* Create the thread */ - if (aos_task_new_ext((aos_task_t*)thread_data, thread_name, - vm_thread_wrapper, thread_data, - stack_size, prio)) - goto fail3; - - aos_msleep(10); - *p_tid = (korp_tid)thread_data; - return BHT_OK; - -fail3: - aos_mutex_free(&thread_data->wait_list_lock); -fail2: - aos_sem_free(&thread_data->wait_node.sem); -fail1: - bh_free(thread_data); - return BHT_ERROR; -} - -korp_tid -_vm_self_thread() -{ - return (korp_tid)aos_task_getspecific(thread_data_key); -} - -void -vm_thread_exit(void * code) -{ - vm_thread_cleanup(); - aos_task_exit((int)(intptr_t)code); -} - -int -_vm_thread_cancel (korp_tid thread) -{ - /* TODO */ - return 0; -} - -int -_vm_thread_join (korp_tid thread, void **value_ptr, int mills) -{ - (void)value_ptr; - bh_thread_data *thread_data, *curr_thread_data; - - /* Get thread data of current thread */ - curr_thread_data = thread_data_current(); - curr_thread_data->wait_node.next = NULL; - - /* Get thread data */ - thread_data = (bh_thread_data*)thread; - - aos_mutex_lock(&thread_data->wait_list_lock, AOS_WAIT_FOREVER); - if (!thread_data->thread_wait_list) - thread_data->thread_wait_list = &curr_thread_data->wait_node; - else { - /* Add to end of waiting list */ - bh_thread_wait_node *p = thread_data->thread_wait_list; - while (p->next) - p = p->next; - p->next = &curr_thread_data->wait_node; - } - aos_mutex_unlock(&thread_data->wait_list_lock); - - /* Wait the sem */ - aos_sem_wait(&curr_thread_data->wait_node.sem, mills); - - return BHT_OK; -} - -int -_vm_thread_detach (korp_tid thread) -{ - (void)thread; - return BHT_OK; -} - -void * -_vm_tls_get(unsigned idx) -{ - (void)idx; - bh_thread_data *thread_data; - - bh_assert (idx == 0); - thread_data = thread_data_current(); - - return thread_data ? thread_data->tlr : NULL; -} - -int -_vm_tls_put(unsigned idx, void * tls) -{ - bh_thread_data *thread_data; - - (void)idx; - bh_assert (idx == 0); - thread_data = thread_data_current(); - bh_assert (thread_data != NULL); - - thread_data->tlr = tls; - return BHT_OK; -} - -int -_vm_mutex_init(korp_mutex *mutex) -{ - return aos_mutex_new(mutex) == 0 ? BHT_OK : BHT_ERROR; -} - -int -_vm_recursive_mutex_init(korp_mutex *mutex) -{ - return aos_mutex_new(mutex) == 0 ? BHT_OK : BHT_ERROR; -} - -int -_vm_mutex_destroy(korp_mutex *mutex) -{ - aos_mutex_free(mutex); - return BHT_OK; -} - -void -vm_mutex_lock(korp_mutex *mutex) -{ - aos_mutex_lock(mutex, AOS_WAIT_FOREVER); -} - -int -vm_mutex_trylock(korp_mutex *mutex) -{ - return aos_mutex_lock(mutex, AOS_NO_WAIT); -} - -void vm_mutex_unlock(korp_mutex *mutex) -{ - aos_mutex_unlock(mutex); -} - -int _vm_sem_init(korp_sem* sem, unsigned int c) -{ - return aos_sem_new(sem, c) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_destroy(korp_sem *sem) -{ - aos_sem_free(sem); - return BHT_OK; -} - -int _vm_sem_wait(korp_sem *sem) -{ - return aos_sem_wait(sem, AOS_WAIT_FOREVER); -} - -int _vm_sem_reltimedwait(korp_sem *sem, int mills) -{ - return aos_sem_wait(sem, mills); -} - -int _vm_sem_post(korp_sem *sem) -{ - aos_sem_signal(sem); - return BHT_OK; -} - -int -_vm_cond_init(korp_cond *cond) -{ - if (aos_mutex_new(&cond->wait_list_lock) != 0) - return BHT_ERROR; - - cond->thread_wait_list = NULL; - return BHT_OK; -} - -int -_vm_cond_destroy(korp_cond *cond) -{ - aos_mutex_free(&cond->wait_list_lock); - return BHT_OK; -} - -static int -vm_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, - bool timed, int mills) -{ - bh_thread_wait_node *node = &thread_data_current()->wait_node; - - node->next = NULL; - - aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); - if (!cond->thread_wait_list) - cond->thread_wait_list = node; - else { - /* Add to end of wait list */ - bh_thread_wait_node *p = cond->thread_wait_list; - while (p->next) - p = p->next; - p->next = node; - } - aos_mutex_unlock(&cond->wait_list_lock); - - /* Unlock mutex, wait sem and lock mutex again */ - aos_mutex_unlock(mutex); - aos_sem_wait(&node->sem, timed ? mills : AOS_WAIT_FOREVER); - aos_mutex_lock(mutex, AOS_WAIT_FOREVER); - - /* Remove wait node from wait list */ - aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); - if (cond->thread_wait_list == node) - cond->thread_wait_list = node->next; - else { - /* Remove from the wait list */ - bh_thread_wait_node *p = cond->thread_wait_list; - while (p->next != node) - p = p->next; - p->next = node->next; - } - aos_mutex_unlock(&cond->wait_list_lock); - - return BHT_OK; -} - -int -_vm_cond_wait(korp_cond *cond, korp_mutex *mutex) -{ - return vm_cond_wait_internal(cond, mutex, false, 0); -} - -int -_vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) -{ - return vm_cond_wait_internal(cond, mutex, true, mills); -} - -int -_vm_cond_signal(korp_cond *cond) -{ - /* Signal the head wait node of wait list */ - aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); - if (cond->thread_wait_list) - aos_sem_signal(&cond->thread_wait_list->sem); - aos_mutex_unlock(&cond->wait_list_lock); - - return BHT_OK; -} - -int -_vm_cond_broadcast (korp_cond *cond) -{ - /* Signal each wait node of wait list */ - aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); - if (cond->thread_wait_list) { - bh_thread_wait_node *p = cond->thread_wait_list; - while (p) { - aos_sem_signal(&p->sem); - p = p->next; - } - } - aos_mutex_unlock(&cond->wait_list_lock); - - return BHT_OK; -} - diff --git a/core/shared/platform/alios/bh_time.c b/core/shared/platform/alios/bh_time.c deleted file mode 100644 index f06fe9fca6..0000000000 --- a/core/shared/platform/alios/bh_time.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_time.h" - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -uint64 _bh_time_get_tick_millisecond() -{ - return aos_get_hz() / 1000; -} - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -uint64 _bh_time_get_boot_millisecond() -{ - return (uint64)aos_now_ms(); -} - -uint64 _bh_time_get_millisecond_from_1970() -{ - return (uint64)aos_now_ms(); -} - -size_t -_bh_time_strftime (char *str, size_t max, const char *format, int64 time) -{ - str = aos_now_time_str(str, max); - return str ? strlen(str) + 1 : 0; -} - diff --git a/core/shared/platform/alios/platform_internal.h b/core/shared/platform/alios/platform_internal.h new file mode 100644 index 0000000000..c5032f9b2e --- /dev/null +++ b/core/shared/platform/alios/platform_internal.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BH_PLATFORM_ALIOS_THINGS +#define BH_PLATFORM_ALIOS_THINGS +#endif + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 30 + +typedef aos_task_t korp_thread; +typedef korp_thread *korp_tid; +typedef aos_task_t *aos_tid_t; +typedef aos_mutex_t korp_mutex; +typedef aos_sem_t korp_sem; + +/* korp_rwlock is used in platform_api_extension.h, + we just define the type to make the compiler happy */ +typedef struct { + int dummy; +} korp_rwlock; + +struct os_thread_wait_node; +typedef struct os_thread_wait_node *os_thread_wait_list; +typedef struct korp_cond { + aos_mutex_t wait_list_lock; + os_thread_wait_list thread_wait_list; +} korp_cond; + +#define os_printf printf +#define os_vprintf vprintf + +/* clang-format off */ +/* math functions which are not provided by os*/ +double sqrt(double x); +double floor(double x); +double ceil(double x); +double fmin(double x, double y); +double fmax(double x, double y); +double rint(double x); +double fabs(double x); +double trunc(double x); +float sqrtf(float x); +float floorf(float x); +float ceilf(float x); +float fminf(float x, float y); +float fmaxf(float x, float y); +float rintf(float x); +float fabsf(float x); +float truncf(float x); +int isnan_double(double x); +int isnan_float(float x); +int signbit_double(double x); +int signbit_float(float x); +#define isnan(x) (sizeof(x) == sizeof(double) ? isnan_double((double)x) : isnan_float(x)) +#define signbit(x) (sizeof(x) == sizeof(double) ? signbit_double((double)x) : signbit_float(x)) +/* clang-format on */ + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_file_handle; +typedef void *os_dir_stream; +typedef int os_raw_file_handle; +typedef int os_poll_file_handle; +typedef unsigned int os_nfds_t; +typedef int os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#endif /* end of _BH_PLATFORM_H */ diff --git a/core/shared/platform/alios/shared_platform.cmake b/core/shared/platform/alios/shared_platform.cmake index cce0df72a1..a3aaddd4a6 100644 --- a/core/shared/platform/alios/shared_platform.cmake +++ b/core/shared/platform/alios/shared_platform.cmake @@ -1,13 +1,16 @@ -# 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}) - -include_directories(${PLATFORM_SHARED_DIR}) -include_directories(${PLATFORM_SHARED_DIR}/../include) - - -file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) - -set (PLATFORM_SHARED_SOURCE ${source_all}) - +# 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_ALIOS_THINGS) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) + diff --git a/core/shared/platform/android/platform_init.c b/core/shared/platform/android/platform_init.c new file mode 100644 index 0000000000..ad206af0ee --- /dev/null +++ b/core/shared/platform/android/platform_init.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#define API_NOT_SUPPORT_ERROR(API, VERSION) \ + __android_log_print(ANDROID_LOG_ERROR, "wasm_runtime::", \ + "%s() is only supported when __ANDROID_API__ >= %s.", \ + #API, #VERSION); + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *fmt, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, fmt); +#ifndef BH_VPRINTF + ret += __android_log_vprint(ANDROID_LOG_INFO, "wasm_runtime::", fmt, ap); +#else + ret += BH_VPRINTF(fmt, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *fmt, va_list ap) +{ +#ifndef BH_VPRINTF + return __android_log_vprint(ANDROID_LOG_INFO, "wasm_runtime::", fmt, ap); +#else + return BH_VPRINTF(fmt, ap); +#endif +} + +#if __ANDROID_API__ < 19 + +int +futimens(int __dir_fd, const struct timespec __times[2]) +{ + API_NOT_SUPPORT_ERROR(futimens, 19); + return -1; +} + +#endif + +#if __ANDROID_API__ < 21 + +int +posix_fallocate(int __fd, off_t __offset, off_t __length) +{ + API_NOT_SUPPORT_ERROR(posix_fallocate, 21); + return -1; +} + +int +posix_fadvise(int fd, off_t offset, off_t len, int advice) +{ + API_NOT_SUPPORT_ERROR(posix_fadvise, 21); + return -1; +} + +int +linkat(int __old_dir_fd, const char *__old_path, int __new_dir_fd, + const char *__new_path, int __flags) +{ + API_NOT_SUPPORT_ERROR(linkat, 21); + return -1; +} + +int +symlinkat(const char *__old_path, int __new_dir_fd, const char *__new_path) +{ + API_NOT_SUPPORT_ERROR(symlinkat, 21); + return -1; +} + +ssize_t +readlinkat(int __dir_fd, const char *__path, char *__buf, size_t __buf_size) +{ + API_NOT_SUPPORT_ERROR(readlinkat, 21); + return -1; +} + +int +accept4(int __fd, struct sockaddr *__addr, socklen_t *__addr_length, + int __flags) +{ + API_NOT_SUPPORT_ERROR(accept4, 21); + return -1; +} + +int +dup3(int oldfd, int newfd, int cloexec) +{ + API_NOT_SUPPORT_ERROR(dup3, 21); + return -1; +} + +int +pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) +{ + API_NOT_SUPPORT_ERROR(pthread_condattr_setclock, 21); + return -1; +} + +int +epoll_create1(int flags) +{ + API_NOT_SUPPORT_ERROR(epoll_create1, 21); + return -1; +} + +int +epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, + const sigset_t *sigmask) +{ + API_NOT_SUPPORT_ERROR(epoll_pwait, 21); + return -1; +} + +int +inotify_init1(int flags) +{ + API_NOT_SUPPORT_ERROR(inotify_init1, 21); + return -1; +} + +#endif + +#if __ANDROID_API__ < 23 + +long +telldir(DIR *__dir) +{ + API_NOT_SUPPORT_ERROR(telldir, 23); + return -1; +} + +void +seekdir(DIR *__dir, long __location) +{ + API_NOT_SUPPORT_ERROR(seekdir, 23); +} + +#endif + +#if __ANDROID_API__ < 24 + +ssize_t +preadv(int __fd, const struct iovec *__iov, int __count, off_t __offset) +{ + API_NOT_SUPPORT_ERROR(preadv, 24); + return -1; +} + +ssize_t +pwritev(int __fd, const struct iovec *__iov, int __count, off_t __offset) +{ + API_NOT_SUPPORT_ERROR(pwritev, 24); + return -1; +} + +#endif diff --git a/core/shared/platform/android/platform_internal.h b/core/shared/platform/android/platform_internal.h new file mode 100644 index 0000000000..6f74e3c78a --- /dev/null +++ b/core/shared/platform/android/platform_internal.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_ANDROID +#define BH_PLATFORM_ANDROID +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define os_thread_local_attribute __thread + +#define bh_socket_t int + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +typedef void (*os_signal_handler)(void *sig_addr); + +int +os_thread_signal_init(os_signal_handler handler); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +void +os_signal_unmask(); + +void +os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64/RISCV64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#define os_getpagesize getpagesize + +typedef long int __syscall_slong_t; + +#if __ANDROID_API__ < 19 + +int +futimens(int __dir_fd, const struct timespec __times[2]); + +#endif + +#if __ANDROID_API__ < 21 + +int +posix_fallocate(int __fd, off_t __offset, off_t __length); + +int +posix_fadvise(int fd, off_t offset, off_t len, int advice); + +int +linkat(int __old_dir_fd, const char *__old_path, int __new_dir_fd, + const char *__new_path, int __flags); + +int +symlinkat(const char *__old_path, int __new_dir_fd, const char *__new_path); + +ssize_t +readlinkat(int __dir_fd, const char *__path, char *__buf, size_t __buf_size); + +#endif + +#if __ANDROID_API__ < 23 + +long +telldir(DIR *__dir); + +void +seekdir(DIR *__dir, long __location); + +#endif + +ssize_t +preadv(int __fd, const struct iovec *__iov, int __count, off_t __offset); + +ssize_t +pwritev(int __fd, const struct iovec *__iov, int __count, off_t __offset); + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef struct timespec os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/android/shared_platform.cmake b/core/shared/platform/android/shared_platform.cmake new file mode 100644 index 0000000000..13beb8e774 --- /dev/null +++ b/core/shared/platform/android/shared_platform.cmake @@ -0,0 +1,18 @@ +# 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_ANDROID) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/common/freertos/freertos_malloc.c b/core/shared/platform/common/freertos/freertos_malloc.c new file mode 100644 index 0000000000..e47a8cce16 --- /dev/null +++ b/core/shared/platform/common/freertos/freertos_malloc.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_malloc(unsigned size) +{ + return NULL; +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return NULL; +} + +void +os_free(void *ptr) +{} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} diff --git a/core/shared/platform/common/freertos/freertos_thread.c b/core/shared/platform/common/freertos/freertos_thread.c new file mode 100644 index 0000000000..8d57fda531 --- /dev/null +++ b/core/shared/platform/common/freertos/freertos_thread.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +/* clang-format off */ +#define bh_assert(v) do { \ + if (!(v)) { \ + int _count = 1; \ + os_printf("\nASSERTION FAILED: %s, at %s, line %d\n",\ + #v, __FILE__, __LINE__); \ + /* divived by 0 to make it abort */ \ + os_printf("%d\n", _count / (_count - 1)); \ + while (1); \ + } \ +} while (0) +/* clang-format on */ + +struct os_thread_data; +typedef struct os_thread_wait_node { + /* Binary semaphore */ + SemaphoreHandle_t sem; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Next thread data */ + struct os_thread_data *next; + /* Thread handle */ + TaskHandle_t handle; + /* Thread start routine */ + thread_start_routine_t start_routine; + /* Thread start routine argument */ + void *arg; + /* Thread local root */ + void *tlr; + /* Wait node of current thread */ + os_thread_wait_node wait_node; + /* Lock for waiting list */ + SemaphoreHandle_t wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; +} os_thread_data; + +static bool is_thread_sys_inited = false; + +/* Lock for thread data list */ +static SemaphoreHandle_t thread_data_lock; + +/* Thread data list */ +static os_thread_data *thread_data_list = NULL; +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Thread name index */ +static int thread_name_index; + +static void +thread_data_list_add(os_thread_data *thread_data) +{ + xSemaphoreTake(thread_data_lock, portMAX_DELAY); + if (!thread_data_list) + thread_data_list = thread_data; + else { + /* If already in list, just return */ + os_thread_data *p = thread_data_list; + while (p) { + if (p == thread_data) { + xSemaphoreGive(thread_data_lock); + return; + } + p = p->next; + } + + /* Set as head of list */ + thread_data->next = thread_data_list; + thread_data_list = thread_data; + } + xSemaphoreGive(thread_data_lock); +} + +static void +thread_data_list_remove(os_thread_data *thread_data) +{ + xSemaphoreTake(thread_data_lock, portMAX_DELAY); + if (thread_data_list) { + if (thread_data_list == thread_data) + thread_data_list = thread_data_list->next; + else { + /* Search and remove it from list */ + os_thread_data *p = thread_data_list; + while (p && p->next != thread_data) + p = p->next; + if (p && p->next == thread_data) + p->next = p->next->next; + } + } + xSemaphoreGive(thread_data_lock); +} + +static os_thread_data * +thread_data_list_lookup(TaskHandle_t handle) +{ + xSemaphoreTake(thread_data_lock, portMAX_DELAY); + if (thread_data_list) { + os_thread_data *p = thread_data_list; + while (p) { + if (p->handle == handle) { + /* Found */ + xSemaphoreGive(thread_data_lock); + return p; + } + p = p->next; + } + } + xSemaphoreGive(thread_data_lock); + return NULL; +} + +int +os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + + if (!(thread_data_lock = xSemaphoreCreateMutex())) + return BHT_ERROR; + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); + + if (!(supervisor_thread_data.wait_node.sem = xSemaphoreCreateBinary())) { + vSemaphoreDelete(thread_data_lock); + return BHT_ERROR; + } + + supervisor_thread_data.handle = xTaskGetCurrentTaskHandle(); + /* Set as head of thread data list */ + thread_data_list = &supervisor_thread_data; + + is_thread_sys_inited = true; + return BHT_OK; +} + +void +os_thread_sys_destroy() +{ + if (is_thread_sys_inited) { + vSemaphoreDelete(supervisor_thread_data.wait_node.sem); + vSemaphoreDelete(thread_data_lock); + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + TaskHandle_t handle = xTaskGetCurrentTaskHandle(); + return thread_data_list_lookup(handle); +} + +static void +os_thread_cleanup(void) +{ + os_thread_data *thread_data = thread_data_current(); + os_thread_wait_list thread_wait_list; + SemaphoreHandle_t wait_list_lock; + SemaphoreHandle_t wait_node_sem; + + bh_assert(thread_data != NULL); + wait_list_lock = thread_data->wait_list_lock; + thread_wait_list = thread_data->thread_wait_list; + wait_node_sem = thread_data->wait_node.sem; + + xSemaphoreTake(wait_list_lock, portMAX_DELAY); + if (thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + xSemaphoreGive(head->sem); + head = next; + } + } + xSemaphoreGive(wait_list_lock); + + /* Free sem and lock */ + vSemaphoreDelete(wait_node_sem); + vSemaphoreDelete(wait_list_lock); + + thread_data_list_remove(thread_data); + BH_FREE(thread_data); +} + +static void +os_thread_wrapper(void *arg) +{ + os_thread_data *thread_data = arg; + + thread_data->handle = xTaskGetCurrentTaskHandle(); + thread_data_list_add(thread_data); + + thread_data->start_routine(thread_data->arg); + os_thread_exit(NULL); +} + +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + os_thread_data *thread_data; + char thread_name[32]; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Create and initialize thread data */ + if (!(thread_data = BH_MALLOC(sizeof(os_thread_data)))) + return BHT_ERROR; + + memset(thread_data, 0, sizeof(os_thread_data)); + + thread_data->start_routine = start; + thread_data->arg = arg; + + if (!(thread_data->wait_node.sem = xSemaphoreCreateBinary())) + goto fail1; + + if (!(thread_data->wait_list_lock = xSemaphoreCreateMutex())) + goto fail2; + + snprintf(thread_name, sizeof(thread_name), "%s%d", "wasm-thread-", + ++thread_name_index); + + /* Create the thread */ + if (pdPASS + != xTaskCreate(os_thread_wrapper, thread_name, stack_size / 4, + thread_data, prio, &thread_data->handle)) + goto fail3; + + thread_data_list_add(thread_data); + *p_tid = thread_data->handle; + return BHT_OK; + +fail3: + vSemaphoreDelete(thread_data->wait_list_lock); +fail2: + vSemaphoreDelete(thread_data->wait_node.sem); +fail1: + BH_FREE(thread_data); + return BHT_ERROR; +} + +korp_tid +os_self_thread() +{ + return xTaskGetCurrentTaskHandle(); +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ + os_thread_data *thread_data, *curr_thread_data; + TaskHandle_t handle = thread; + + (void)value_ptr; + + /* Get thread data of current thread */ + curr_thread_data = thread_data_current(); + curr_thread_data->wait_node.next = NULL; + + /* Get thread data */ + thread_data = thread_data_list_lookup(handle); + + xSemaphoreTake(thread_data->wait_list_lock, portMAX_DELAY); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = &curr_thread_data->wait_node; + else { + /* 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; + } + xSemaphoreGive(thread_data->wait_list_lock); + + /* Wait the sem */ + xSemaphoreTake(curr_thread_data->wait_node.sem, portMAX_DELAY); + return BHT_OK; +} + +int +os_thread_detach(korp_tid thread) +{ + /* Do nothing */ + (void)thread; + return BHT_OK; +} + +void +os_thread_exit(void *retval) +{ + (void)retval; + os_thread_cleanup(); + vTaskDelete(NULL); +} + +int +os_mutex_init(korp_mutex *mutex) +{ + SemaphoreHandle_t semaphore; + + if (!(semaphore = xSemaphoreCreateMutex())) + return BHT_ERROR; + mutex->sem = semaphore; + mutex->is_recursive = false; + return BHT_OK; +} + +int +os_recursive_mutex_init(korp_mutex *mutex) +{ + SemaphoreHandle_t semaphore; + + if (!(semaphore = xSemaphoreCreateRecursiveMutex())) + return BHT_ERROR; + mutex->sem = semaphore; + mutex->is_recursive = true; + return BHT_OK; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + vSemaphoreDelete(mutex->sem); + return BHT_OK; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + int ret = -1; + + if (!mutex->is_recursive) + ret = xSemaphoreTake(mutex->sem, portMAX_DELAY); + else + ret = xSemaphoreTakeRecursive(mutex->sem, portMAX_DELAY); + return ret == pdPASS ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + int ret = -1; + + if (!mutex->is_recursive) + ret = xSemaphoreGive(mutex->sem); + else + ret = xSemaphoreGiveRecursive(mutex->sem); + return ret == pdPASS ? BHT_OK : BHT_ERROR; +} + +int +os_cond_init(korp_cond *cond) +{ + if (!(cond->wait_list_lock = xSemaphoreCreateMutex())) + return BHT_ERROR; + + cond->thread_wait_list = NULL; + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + vSemaphoreDelete(cond->wait_list_lock); + return BHT_OK; +} + +static int +os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, int mills) +{ + os_thread_wait_node *node = &thread_data_current()->wait_node; + + node->next = NULL; + + xSemaphoreTake(cond->wait_list_lock, portMAX_DELAY); + if (!cond->thread_wait_list) + cond->thread_wait_list = node; + else { + /* Add to end of wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + xSemaphoreGive(cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + os_mutex_unlock(mutex); + xSemaphoreTake(node->sem, timed ? mills / portTICK_RATE_MS : portMAX_DELAY); + os_mutex_lock(mutex); + + /* Remove wait node from wait list */ + xSemaphoreTake(cond->wait_list_lock, portMAX_DELAY); + if (cond->thread_wait_list == node) + cond->thread_wait_list = node->next; + 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; + } + xSemaphoreGive(cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + if (useconds == BHT_WAIT_FOREVER) { + return os_cond_wait_internal(cond, mutex, false, 0); + } + else { + uint64 mills_64 = useconds / 1000; + int32 mills; + + if (mills_64 < (uint64)INT32_MAX) { + mills = (int32)mills_64; + } + else { + mills = INT32_MAX; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + return os_cond_wait_internal(cond, mutex, true, mills); + } +} + +int +os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + xSemaphoreTake(cond->wait_list_lock, portMAX_DELAY); + if (cond->thread_wait_list) + xSemaphoreGive(cond->thread_wait_list->sem); + xSemaphoreGive(cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_broadcast(korp_cond *cond) +{ + /* Signal all of the wait node of wait list */ + xSemaphoreTake(cond->wait_list_lock, portMAX_DELAY); + if (cond->thread_wait_list) { + os_thread_wait_node *p = cond->thread_wait_list; + while (p) { + xSemaphoreGive(p->sem); + p = p->next; + } + } + xSemaphoreGive(cond->wait_list_lock); + + return BHT_OK; +} + +uint8 * +os_thread_get_stack_boundary() +{ + /* TODO: get freertos stack boundary */ + return NULL; +} + +void +os_thread_jit_write_protect_np(bool enabled) +{ + (void)enabled; +} diff --git a/core/shared/platform/common/freertos/freertos_time.c b/core/shared/platform/common/freertos/freertos_time.c new file mode 100644 index 0000000000..e8249fec19 --- /dev/null +++ b/core/shared/platform/common/freertos/freertos_time.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_us() +{ + TickType_t ticks = xTaskGetTickCount(); + return (uint64)1000 * 1000 / configTICK_RATE_HZ * ticks; +} + +uint64 +os_time_thread_cputime_us(void) +{ + /* FIXME if u know the right api */ + return os_time_get_boot_us(); +} \ No newline at end of file diff --git a/core/shared/platform/common/freertos/platform_api_freertos.cmake b/core/shared/platform/common/freertos/platform_api_freertos.cmake new file mode 100644 index 0000000000..ebfc19d788 --- /dev/null +++ b/core/shared/platform/common/freertos/platform_api_freertos.cmake @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_COMMON_FREERTOS_DIR ${CMAKE_CURRENT_LIST_DIR}) + +file (GLOB_RECURSE source_all ${PLATFORM_COMMON_FREERTOS_DIR}/*.c) + +set (PLATFORM_COMMON_FREERTOS_SOURCE ${source_all} ) diff --git a/core/shared/platform/common/libc-util/SConscript b/core/shared/platform/common/libc-util/SConscript new file mode 100644 index 0000000000..7180b26c40 --- /dev/null +++ b/core/shared/platform/common/libc-util/SConscript @@ -0,0 +1,20 @@ +# +# Copyright 2024 Sony Semiconductor Solutions Corporation. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import re + +Import('rtconfig') + +cwd = GetCurrentDir() +src = Split(''' +libc_errno.c +''') +CPPPATH = [cwd] + +group = DefineGroup('iwasm_libc_util', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/shared/platform/common/libc-util/libc_errno.c b/core/shared/platform/common/libc-util/libc_errno.c new file mode 100644 index 0000000000..e6c26c839a --- /dev/null +++ b/core/shared/platform/common/libc-util/libc_errno.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "errno.h" +#include "libc_errno.h" + +__wasi_errno_t +convert_errno(int error) +{ + // The C standard library only requires EDOM, EILSEQ and ERANGE to be + // defined. Other error codes are POSIX-specific and hence may or may + // not be available on non-POSIX platforms. + __wasi_errno_t code = __WASI_ENOSYS; +#define X(v) \ + case v: \ + code = __WASI_##v; \ + break; + switch (error) { + X(EDOM) + X(EILSEQ) + X(ERANGE) +#ifdef E2BIG + X(E2BIG) +#endif +#ifdef EACCES + X(EACCES) +#endif +#ifdef EADDRINUSE + X(EADDRINUSE) +#endif +#ifdef EADDRNOTAVAIL + X(EADDRNOTAVAIL) +#endif +#ifdef EAFNOSUPPORT + X(EAFNOSUPPORT) +#endif +#ifdef EAGAIN + X(EAGAIN) +#endif +#ifdef EALREADY + X(EALREADY) +#endif +#ifdef EBADF + X(EBADF) +#endif +#ifdef EBADMSG + X(EBADMSG) +#endif +#ifdef EBUSY + X(EBUSY) +#endif +#ifdef ECANCELED + X(ECANCELED) +#endif +#ifdef ECHILD + X(ECHILD) +#endif +#ifdef ECONNABORTED + X(ECONNABORTED) +#endif +#ifdef ECONNREFUSED + X(ECONNREFUSED) +#endif +#ifdef ECONNRESET + X(ECONNRESET) +#endif +#ifdef EDEADLK + X(EDEADLK) +#endif +#ifdef EDESTADDRREQ + X(EDESTADDRREQ) +#endif +#ifdef EDQUOT + X(EDQUOT) +#endif +#ifdef EEXIST + X(EEXIST) +#endif +#ifdef EFAULT + X(EFAULT) +#endif +#ifdef EFBIG + X(EFBIG) +#endif +#ifdef EHOSTUNREACH + X(EHOSTUNREACH) +#endif +#ifdef EIDRM + X(EIDRM) +#endif +#ifdef EINPROGRESS + X(EINPROGRESS) +#endif +#ifdef EINTR + X(EINTR) +#endif +#ifdef EINVAL + X(EINVAL) +#endif +#ifdef EIO + X(EIO) +#endif +#ifdef EISCONN + X(EISCONN) +#endif +#ifdef EISDIR + X(EISDIR) +#endif +#ifdef ELOOP + X(ELOOP) +#endif +#ifdef EMFILE + X(EMFILE) +#endif +#ifdef EMLINK + X(EMLINK) +#endif +#ifdef EMSGSIZE + X(EMSGSIZE) +#endif +#ifdef EMULTIHOP + X(EMULTIHOP) +#endif +#ifdef ENAMETOOLONG + X(ENAMETOOLONG) +#endif +#ifdef ENETDOWN + X(ENETDOWN) +#endif +#ifdef ENETRESET + X(ENETRESET) +#endif +#ifdef ENETUNREACH + X(ENETUNREACH) +#endif +#ifdef ENFILE + X(ENFILE) +#endif +#ifdef ENOBUFS + X(ENOBUFS) +#endif +#ifdef ENODEV + X(ENODEV) +#endif +#ifdef ENOENT + X(ENOENT) +#endif +#ifdef ENOEXEC + X(ENOEXEC) +#endif +#ifdef ENOLCK + X(ENOLCK) +#endif +#ifdef ENOLINK + X(ENOLINK) +#endif +#ifdef ENOMEM + X(ENOMEM) +#endif +#ifdef ENOMSG + X(ENOMSG) +#endif +#ifdef ENOPROTOOPT + X(ENOPROTOOPT) +#endif +#ifdef ENOSPC + X(ENOSPC) +#endif +#ifdef ENOSYS + X(ENOSYS) +#endif +#ifdef ENOTCAPABLE + X(ENOTCAPABLE) +#endif +#ifdef ENOTCONN + X(ENOTCONN) +#endif +#ifdef ENOTDIR + X(ENOTDIR) +#endif +#ifdef ENOTEMPTY + X(ENOTEMPTY) +#endif +#ifdef ENOTRECOVERABLE + X(ENOTRECOVERABLE) +#endif +#ifdef ENOTSOCK + X(ENOTSOCK) +#endif +#ifdef ENOTSUP + X(ENOTSUP) +#endif +#ifdef ENOTTY + X(ENOTTY) +#endif +#ifdef ENXIO + X(ENXIO) +#endif +#ifdef EOVERFLOW + X(EOVERFLOW) +#endif +#ifdef EOWNERDEAD + X(EOWNERDEAD) +#endif +#ifdef EPERM + X(EPERM) +#endif +#ifdef EPIPE + X(EPIPE) +#endif +#ifdef EPROTO + X(EPROTO) +#endif +#ifdef EPROTONOSUPPORT + X(EPROTONOSUPPORT) +#endif +#ifdef EPROTOTYPE + X(EPROTOTYPE) +#endif +#ifdef EROFS + X(EROFS) +#endif +#ifdef ESPIPE + X(ESPIPE) +#endif +#ifdef ESRCH + X(ESRCH) +#endif +#ifdef ESTALE + X(ESTALE) +#endif +#ifdef ETIMEDOUT + X(ETIMEDOUT) +#endif +#ifdef ETXTBSY + X(ETXTBSY) +#endif +#ifdef EXDEV + X(EXDEV) +#endif + default: +#ifdef EOPNOTSUPP + if (error == EOPNOTSUPP) + code = __WASI_ENOTSUP; +#endif +#ifdef EWOULDBLOCK + if (error == EWOULDBLOCK) + code = __WASI_EAGAIN; +#endif + break; + } +#undef X + return code; +} \ No newline at end of file diff --git a/core/shared/platform/common/libc-util/libc_errno.h b/core/shared/platform/common/libc-util/libc_errno.h new file mode 100644 index 0000000000..b0a8b78c6a --- /dev/null +++ b/core/shared/platform/common/libc-util/libc_errno.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASI_ERRNO_H +#define WASI_ERRNO_H + +#include "platform_wasi_types.h" + +// Converts an errno error code to a WASI error code. +__wasi_errno_t +convert_errno(int error); + +#endif /* end of WASI_ERRNO_H */ \ No newline at end of file diff --git a/core/shared/platform/common/libc-util/platform_common_libc_util.cmake b/core/shared/platform/common/libc-util/platform_common_libc_util.cmake new file mode 100644 index 0000000000..a7c7645ce3 --- /dev/null +++ b/core/shared/platform/common/libc-util/platform_common_libc_util.cmake @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_COMMON_LIBC_UTIL_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${PLATFORM_COMMON_LIBC_UTIL_DIR}) + +file (GLOB_RECURSE PLATFORM_COMMON_LIBC_UTIL_SOURCE ${PLATFORM_COMMON_LIBC_UTIL_DIR}/*.c) \ No newline at end of file diff --git a/core/shared/platform/alios/COPYRIGHT b/core/shared/platform/common/math/COPYRIGHT similarity index 100% rename from core/shared/platform/alios/COPYRIGHT rename to core/shared/platform/common/math/COPYRIGHT diff --git a/core/shared/platform/common/math/math.c b/core/shared/platform/common/math/math.c new file mode 100644 index 0000000000..3c0171570f --- /dev/null +++ b/core/shared/platform/common/math/math.c @@ -0,0 +1,1710 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "platform_common.h" + +#define __FDLIBM_STDC__ + +#ifndef FLT_EVAL_METHOD +#define FLT_EVAL_METHOD 0 +#endif + +typedef uint32_t u_int32_t; +typedef uint64_t u_int64_t; + +typedef union u32double_tag { + int *pint; + double *pdouble; +} U32DOUBLE; + +static inline int * +pdouble2pint(double *pdouble) +{ + U32DOUBLE u; + u.pdouble = pdouble; + return u.pint; +} + +typedef union { + double value; + struct { + u_int32_t lsw; + u_int32_t msw; + } parts; + struct { + u_int64_t w; + } xparts; +} ieee_double_shape_type_little; + +typedef union { + double value; + struct { + u_int32_t msw; + u_int32_t lsw; + } parts; + struct { + u_int64_t w; + } xparts; +} ieee_double_shape_type_big; + +typedef union { + double d; + struct { + unsigned int manl : 32; + unsigned int manh : 20; + unsigned int exp : 11; + unsigned int sign : 1; + } bits; +} IEEEd2bits_L; + +typedef union { + double d; + struct { + unsigned int sign : 1; + unsigned int exp : 11; + unsigned int manh : 20; + unsigned int manl : 32; + } bits; +} IEEEd2bits_B; + +typedef union { + float f; + struct { + unsigned int man : 23; + unsigned int exp : 8; + unsigned int sign : 1; + } bits; +} IEEEf2bits_L; + +typedef union { + float f; + struct { + unsigned int sign : 1; + unsigned int exp : 8; + unsigned int man : 23; + } bits; +} IEEEf2bits_B; + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +#define __HIL(x) *(1 + pdouble2pint(&x)) +#define __LOL(x) *(pdouble2pint(&x)) +#define __HIB(x) *(pdouble2pint(&x)) +#define __LOB(x) *(1 + pdouble2pint(&x)) + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS_L(ix0, ix1, d) \ + do { \ + ieee_double_shape_type_little ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS_L(d, ix0, ix1) \ + do { \ + ieee_double_shape_type_little iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS_B(ix0, ix1, d) \ + do { \ + ieee_double_shape_type_big ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS_B(d, ix0, ix1) \ + do { \ + ieee_double_shape_type_big iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +/* Get the more significant 32 bit int from a double. */ +#define GET_HIGH_WORD_L(i, d) \ + do { \ + ieee_double_shape_type_little gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ + } while (0) + +/* Get the more significant 32 bit int from a double. */ +#define GET_HIGH_WORD_B(i, d) \ + do { \ + ieee_double_shape_type_big gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ +#define SET_HIGH_WORD_L(d, v) \ + do { \ + ieee_double_shape_type_little sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ +#define SET_HIGH_WORD_B(d, v) \ + do { \ + ieee_double_shape_type_big sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Set the less significant 32 bits of a double from an int. */ +#define SET_LOW_WORD_L(d, v) \ + do { \ + ieee_double_shape_type_little sh_u; \ + sh_u.value = (d); \ + sh_u.parts.lsw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ +#define SET_LOW_WORD_B(d, v) \ + do { \ + ieee_double_shape_type_big sh_u; \ + sh_u.value = (d); \ + sh_u.parts.lsw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Get the less significant 32 bit int from a double. */ +#define GET_LOW_WORD_L(i, d) \ + do { \ + ieee_double_shape_type_little gl_u; \ + gl_u.value = (d); \ + (i) = gl_u.parts.lsw; \ + } while (0) + +/* Get the less significant 32 bit int from a double. */ +#define GET_LOW_WORD_B(i, d) \ + do { \ + ieee_double_shape_type_big gl_u; \ + gl_u.value = (d); \ + (i) = gl_u.parts.lsw; \ + } while (0) + +/* + * A union which permits us to convert between a float and a 32 bit + * int. + */ +typedef union { + float value; + /* FIXME: Assumes 32 bit int. */ + unsigned int word; +} ieee_float_shape_type; + +/* Get a 32 bit int from a float. */ +#define GET_FLOAT_WORD(i, d) \ + do { \ + ieee_float_shape_type gf_u; \ + gf_u.value = (d); \ + (i) = gf_u.word; \ + } while (0) + +/* Set a float from a 32 bit int. */ +#define SET_FLOAT_WORD(d, i) \ + do { \ + ieee_float_shape_type sf_u; \ + sf_u.word = (i); \ + (d) = sf_u.value; \ + } while (0) + +/* Macro wrappers. */ +#define EXTRACT_WORDS(ix0, ix1, d) \ + do { \ + if (is_little_endian()) \ + EXTRACT_WORDS_L(ix0, ix1, d); \ + else \ + EXTRACT_WORDS_B(ix0, ix1, d); \ + } while (0) + +#define INSERT_WORDS(d, ix0, ix1) \ + do { \ + if (is_little_endian()) \ + INSERT_WORDS_L(d, ix0, ix1); \ + else \ + INSERT_WORDS_B(d, ix0, ix1); \ + } while (0) + +#define GET_HIGH_WORD(i, d) \ + do { \ + if (is_little_endian()) \ + GET_HIGH_WORD_L(i, d); \ + else \ + GET_HIGH_WORD_B(i, d); \ + } while (0) + +#define SET_HIGH_WORD(d, v) \ + do { \ + if (is_little_endian()) \ + SET_HIGH_WORD_L(d, v); \ + else \ + SET_HIGH_WORD_B(d, v); \ + } while (0) + +#define GET_LOW_WORD(d, v) \ + do { \ + if (is_little_endian()) \ + GET_LOW_WORD_L(d, v); \ + else \ + GET_LOW_WORD_B(d, v); \ + } while (0) + +#define SET_LOW_WORD(d, v) \ + do { \ + if (is_little_endian()) \ + SET_LOW_WORD_L(d, v); \ + else \ + SET_LOW_WORD_B(d, v); \ + } while (0) + +#define __HI(x) (is_little_endian() ? __HIL(x) : __HIB(x)) + +#define __LO(x) (is_little_endian() ? __LOL(x) : __LOB(x)) + +/* + * Attempt to get strict C99 semantics for assignment with non-C99 compilers. + */ +#if FLT_EVAL_METHOD == 0 || __GNUC__ == 0 +#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval)) +#else +#define STRICT_ASSIGN(type, lval, rval) \ + do { \ + volatile type __lval; \ + \ + if (sizeof(type) >= sizeof(long double)) \ + (lval) = (rval); \ + else { \ + __lval = (rval); \ + (lval) = __lval; \ + } \ + } while (0) +#endif + +#ifdef __FDLIBM_STDC__ +static const double huge = 1.0e300; +#else +static double huge = 1.0e300; +#endif + +#ifdef __STDC__ +static const double +#else +static double +#endif + tiny = 1.0e-300; + +#ifdef __STDC__ +static const double +#else +static double +#endif + one = 1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */ + +#ifdef __STDC__ +static const double +#else +static double +#endif + TWO52[2] = { + 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ + -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ + }; + +#ifdef __STDC__ +static const double +#else +static double +#endif + atanhi[] = { + 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ + 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ + 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ + 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ + }; + +#ifdef __STDC__ +static const double +#else +static double +#endif + atanlo[] = { + 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ + 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ + 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ + 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ + }; + +#ifdef __STDC__ +static const double +#else +static double +#endif + aT[] = { + 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ + -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ + 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ + -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ + 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ + -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ + 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ + -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ + 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ + -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ + 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ + }; + +#ifdef __STDC__ +static const double +#else +static double +#endif + zero = 0.0, + pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */ + pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */ + pi = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */ + pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ + +#ifdef __STDC__ +static const double +#else +static double +#endif +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ +dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ +two = 2.0, +two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ +L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ +L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ +L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ +L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ +L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ +lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ +lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ +ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ +cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ +cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ +cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ +ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ +ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ +ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +static double +freebsd_floor(double x); +static double +freebsd_ceil(double x); +static double +freebsd_fabs(double x); +static double +freebsd_rint(double x); +static int +freebsd_isnan(double x); +static double +freebsd_atan(double x); +static double +freebsd_atan2(double y, double x); + +static double +freebsd_atan(double x) +{ + double w, s1, s2, z; + int32_t ix, hx, id; + + GET_HIGH_WORD(hx, x); + ix = hx & 0x7fffffff; + if (ix >= 0x44100000) { /* if |x| >= 2^66 */ + u_int32_t low; + GET_LOW_WORD(low, x); + if (ix > 0x7ff00000 || (ix == 0x7ff00000 && (low != 0))) + return x + x; /* NaN */ + if (hx > 0) + return atanhi[3] + *(volatile double *)&atanlo[3]; + else + return -atanhi[3] - *(volatile double *)&atanlo[3]; + } + if (ix < 0x3fdc0000) { /* |x| < 0.4375 */ + if (ix < 0x3e400000) { /* |x| < 2^-27 */ + if (huge + x > one) + return x; /* raise inexact */ + } + id = -1; + } + else { + x = freebsd_fabs(x); + if (ix < 0x3ff30000) { /* |x| < 1.1875 */ + if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */ + id = 0; + x = (2.0 * x - one) / (2.0 + x); + } + else { /* 11/16<=|x|< 19/16 */ + id = 1; + x = (x - one) / (x + one); + } + } + else { + if (ix < 0x40038000) { /* |x| < 2.4375 */ + id = 2; + x = (x - 1.5) / (one + 1.5 * x); + } + else { /* 2.4375 <= |x| < 2^66 */ + id = 3; + x = -1.0 / x; + } + } + } + /* end of argument reduction */ + z = x * x; + w = z * z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z + * (aT[0] + + w + * (aT[2] + + w * (aT[4] + w * (aT[6] + w * (aT[8] + w * aT[10]))))); + s2 = w * (aT[1] + w * (aT[3] + w * (aT[5] + w * (aT[7] + w * aT[9])))); + if (id < 0) + return x - x * (s1 + s2); + else { + z = atanhi[id] - ((x * (s1 + s2) - atanlo[id]) - x); + return (hx < 0) ? -z : z; + } +} + +static double +freebsd_atan2(double y, double x) +{ + double z; + int32_t k, m, hx, hy, ix, iy; + u_int32_t lx, ly; + + EXTRACT_WORDS(hx, lx, x); + ix = hx & 0x7fffffff; + EXTRACT_WORDS(hy, ly, y); + iy = hy & 0x7fffffff; + if (((ix | ((lx | -lx) >> 31)) > 0x7ff00000) + || ((iy | ((ly | -ly) >> 31)) > 0x7ff00000)) /* x or y is NaN */ + return x + y; + if (hx == 0x3ff00000 && lx == 0) + return freebsd_atan(y); /* x=1.0 */ + m = ((hy >> 31) & 1) | ((hx >> 30) & 2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if ((iy | ly) == 0) { + switch (m) { + case 0: + case 1: + return y; /* atan(+-0,+anything)=+-0 */ + case 2: + return pi + tiny; /* atan(+0,-anything) = pi */ + case 3: + default: + return -pi - tiny; /* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if ((ix | lx) == 0) + return (hy < 0) ? -pi_o_2 - tiny : pi_o_2 + tiny; + + /* when x is INF */ + if (ix == 0x7ff00000) { + if (iy == 0x7ff00000) { + switch (m) { + case 0: + return pi_o_4 + tiny; /* atan(+INF,+INF) */ + case 1: + return -pi_o_4 - tiny; /* atan(-INF,+INF) */ + case 2: + return 3.0 * pi_o_4 + tiny; /*atan(+INF,-INF)*/ + case 3: + default: + return -3.0 * pi_o_4 - tiny; /*atan(-INF,-INF)*/ + } + } + else { + switch (m) { + case 0: + return zero; /* atan(+...,+INF) */ + case 1: + return -zero; /* atan(-...,+INF) */ + case 2: + return pi + tiny; /* atan(+...,-INF) */ + case 3: + default: + return -pi - tiny; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if (iy == 0x7ff00000) + return (hy < 0) ? -pi_o_2 - tiny : pi_o_2 + tiny; + + /* compute y/x */ + k = (iy - ix) >> 20; + if (k > 60) { /* |y/x| > 2**60 */ + z = pi_o_2 + 0.5 * pi_lo; + m &= 1; + } + else if (hx < 0 && k < -60) + z = 0.0; /* 0 > |y|/x > -2**-60 */ + else + z = freebsd_atan(fabs(y / x)); /* safe to do y/x */ + switch (m) { + case 0: + return z; /* atan(+,+) */ + case 1: + return -z; /* atan(-,+) */ + case 2: + return pi - (z - pi_lo); /* atan(+,-) */ + default: /* case 3 */ + return (z - pi_lo) - pi; /* atan(-,-) */ + } +} + +#ifndef BH_HAS_SQRTF +static float +freebsd_sqrtf(float x) +{ + float z; + int32_t sign = (int)0x80000000; + int32_t ix, s, q, m, t, i; + u_int32_t r; + + GET_FLOAT_WORD(ix, x); + + /* take care of Inf and NaN */ + if ((ix & 0x7f800000) == 0x7f800000) { + return x * x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if (ix <= 0) { + if ((ix & (~sign)) == 0) + return x; /* sqrt(+-0) = +-0 */ + else if (ix < 0) + return (x - x) / (x - x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix >> 23); + if (m == 0) { /* subnormal x */ + for (i = 0; (ix & 0x00800000) == 0; i++) + ix <<= 1; + m -= i - 1; + } + m -= 127; /* unbias exponent */ + ix = (ix & 0x007fffff) | 0x00800000; + if (m & 1) /* odd m, double x to make it even */ + ix += ix; + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix += ix; + q = s = 0; /* q = sqrt(x) */ + r = 0x01000000; /* r = moving bit from right to left */ + + while (r != 0) { + t = s + r; + if (t <= ix) { + s = t + r; + ix -= t; + q += r; + } + ix += ix; + r >>= 1; + } + + /* use floating add to find out rounding direction */ + if (ix != 0) { + z = one - tiny; /* trigger inexact flag */ + if (z >= one) { + z = one + tiny; + if (z > one) + q += 2; + else + q += (q & 1); + } + } + ix = (q >> 1) + 0x3f000000; + ix += (m << 23); + SET_FLOAT_WORD(z, ix); + return z; +} +#endif /* end of BH_HAS_SQRTF */ + +#ifndef BH_HAS_SQRT +static double +freebsd_sqrt(double x) /* wrapper sqrt */ +{ + double z; + int32_t sign = (int)0x80000000; + int32_t ix0, s0, q, m, t, i; + u_int32_t r, t1, s1, ix1, q1; + + EXTRACT_WORDS(ix0, ix1, x); + + /* take care of Inf and NaN */ + if ((ix0 & 0x7ff00000) == 0x7ff00000) { + return x * x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if (ix0 <= 0) { + if (((ix0 & (~sign)) | ix1) == 0) + return x; /* sqrt(+-0) = +-0 */ + else if (ix0 < 0) + return (x - x) / (x - x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix0 >> 20); + if (m == 0) { /* subnormal x */ + while (ix0 == 0) { + m -= 21; + ix0 |= (ix1 >> 11); + ix1 <<= 21; + } + for (i = 0; (ix0 & 0x00100000) == 0; i++) + ix0 <<= 1; + m -= i - 1; + ix0 |= (ix1 >> (32 - i)); + ix1 <<= i; + } + m -= 1023; /* unbias exponent */ + ix0 = (ix0 & 0x000fffff) | 0x00100000; + if (m & 1) { /* odd m, double x to make it even */ + ix0 += ix0 + ((ix1 & sign) >> 31); + ix1 += ix1; + } + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix0 += ix0 + ((ix1 & sign) >> 31); + ix1 += ix1; + q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ + r = 0x00200000; /* r = moving bit from right to left */ + + while (r != 0) { + t = s0 + r; + if (t <= ix0) { + s0 = t + r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1 & sign) >> 31); + ix1 += ix1; + r >>= 1; + } + + r = sign; + while (r != 0) { + t1 = s1 + r; + t = s0; + if ((t < ix0) || ((t == ix0) && (t1 <= ix1))) { + s1 = t1 + r; + if (((t1 & sign) == sign) && (s1 & sign) == 0) + s0 += 1; + ix0 -= t; + if (ix1 < t1) + ix0 -= 1; + ix1 -= t1; + q1 += r; + } + ix0 += ix0 + ((ix1 & sign) >> 31); + ix1 += ix1; + r >>= 1; + } + + /* use floating add to find out rounding direction */ + if ((ix0 | ix1) != 0) { + z = one - tiny; /* trigger inexact flag */ + if (z >= one) { + z = one + tiny; + if (q1 == (u_int32_t)0xffffffff) { + q1 = 0; + q += 1; + } + else if (z > one) { + if (q1 == (u_int32_t)0xfffffffe) + q += 1; + q1 += 2; + } + else + q1 += (q1 & 1); + } + } + ix0 = (q >> 1) + 0x3fe00000; + ix1 = q1 >> 1; + if ((q & 1) == 1) + ix1 |= sign; + ix0 += (m << 20); + + INSERT_WORDS(z, ix0, ix1); + + return z; +} +#endif /* end of BH_HAS_SQRT */ + +static double +freebsd_floor(double x) +{ + int32_t i0, i1, j0; + u_int32_t i, j; + + EXTRACT_WORDS(i0, i1, x); + + j0 = ((i0 >> 20) & 0x7ff) - 0x3ff; + if (j0 < 20) { + if (j0 < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) { /* return 0*sign(x) if |x|<1 */ + if (i0 >= 0) { + i0 = i1 = 0; + } + else if (((i0 & 0x7fffffff) | i1) != 0) { + i0 = 0xbff00000; + i1 = 0; + } + } + } + else { + i = (0x000fffff) >> j0; + if (((i0 & i) | i1) == 0) + return x; /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + if (i0 < 0) + i0 += (0x00100000) >> j0; + i0 &= (~i); + i1 = 0; + } + } + } + else if (j0 > 51) { + if (j0 == 0x400) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ + } + else { + i = ((u_int32_t)(0xffffffff)) >> (j0 - 20); + if ((i1 & i) == 0) + return x; /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + if (i0 < 0) { + if (j0 == 20) + i0 += 1; + else { + j = i1 + (1 << (52 - j0)); + if (j < (u_int32_t)i1) + i0 += 1; /* got a carry */ + i1 = j; + } + } + i1 &= (~i); + } + } + + INSERT_WORDS(x, i0, i1); + + return x; +} + +static double +freebsd_ceil(double x) +{ + int32_t i0, i1, j0; + u_int32_t i, j; + EXTRACT_WORDS(i0, i1, x); + j0 = ((i0 >> 20) & 0x7ff) - 0x3ff; + if (j0 < 20) { + if (j0 < 0) { /* raise inexact if x != 0 */ + if (huge + x > 0.0) { /* return 0*sign(x) if |x|<1 */ + if (i0 < 0) { + i0 = 0x80000000; + i1 = 0; + } + else if ((i0 | i1) != 0) { + i0 = 0x3ff00000; + i1 = 0; + } + } + } + else { + i = (0x000fffff) >> j0; + if (((i0 & i) | i1) == 0) + return x; /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + if (i0 > 0) + i0 += (0x00100000) >> j0; + i0 &= (~i); + i1 = 0; + } + } + } + else if (j0 > 51) { + if (j0 == 0x400) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ + } + else { + i = ((u_int32_t)(0xffffffff)) >> (j0 - 20); + if ((i1 & i) == 0) + return x; /* x is integral */ + if (huge + x > 0.0) { /* raise inexact flag */ + if (i0 > 0) { + if (j0 == 20) + i0 += 1; + else { + j = i1 + (1 << (52 - j0)); + if (j < (u_int32_t)i1) + i0 += 1; /* got a carry */ + i1 = j; + } + } + i1 &= (~i); + } + } + INSERT_WORDS(x, i0, i1); + return x; +} + +static double +freebsd_rint(double x) +{ + int32_t i0, j0, sx; + u_int32_t i, i1; + double w, t; + EXTRACT_WORDS(i0, i1, x); + sx = (i0 >> 31) & 1; + j0 = ((i0 >> 20) & 0x7ff) - 0x3ff; + if (j0 < 20) { + if (j0 < 0) { + if (((i0 & 0x7fffffff) | i1) == 0) + return x; + i1 |= (i0 & 0x0fffff); + i0 &= 0xfffe0000; + i0 |= ((i1 | -i1) >> 12) & 0x80000; + SET_HIGH_WORD(x, i0); + STRICT_ASSIGN(double, w, TWO52[sx] + x); + t = w - TWO52[sx]; + GET_HIGH_WORD(i0, t); + SET_HIGH_WORD(t, (i0 & 0x7fffffff) | (sx << 31)); + return t; + } + else { + i = (0x000fffff) >> j0; + if (((i0 & i) | i1) == 0) + return x; /* x is integral */ + i >>= 1; + if (((i0 & i) | i1) != 0) { + /* + * Some bit is set after the 0.5 bit. To avoid the + * possibility of errors from double rounding in + * w = TWO52[sx]+x, adjust the 0.25 bit to a lower + * guard bit. We do this for all j0<=51. The + * adjustment is trickiest for j0==18 and j0==19 + * since then it spans the word boundary. + */ + if (j0 == 19) + i1 = 0x40000000; + else if (j0 == 18) + i1 = 0x80000000; + else + i0 = (i0 & (~i)) | ((0x20000) >> j0); + } + } + } + else if (j0 > 51) { + if (j0 == 0x400) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ + } + else { + i = ((u_int32_t)(0xffffffff)) >> (j0 - 20); + if ((i1 & i) == 0) + return x; /* x is integral */ + i >>= 1; + if ((i1 & i) != 0) + i1 = (i1 & (~i)) | ((0x40000000) >> (j0 - 20)); + } + INSERT_WORDS(x, i0, i1); + STRICT_ASSIGN(double, w, TWO52[sx] + x); + return w - TWO52[sx]; +} + +static int +freebsd_isnan(double d) +{ + if (is_little_endian()) { + IEEEd2bits_L u; + u.d = d; + return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); + } + else { + IEEEd2bits_B u; + u.d = d; + return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); + } +} + +static int +freebsd_isnanf(float f) +{ + if (is_little_endian()) { + IEEEf2bits_L u; + u.f = f; + return (u.bits.exp == 0xff && u.bits.man != 0); + } + else { + IEEEf2bits_B u; + u.f = f; + return (u.bits.exp == 0xff && u.bits.man != 0); + } +} + +static float +freebsd_fabsf(float x) +{ + u_int32_t ix; + GET_FLOAT_WORD(ix, x); + SET_FLOAT_WORD(x, ix & 0x7fffffff); + return x; +} + +static double +freebsd_fabs(double x) +{ + u_int32_t high; + GET_HIGH_WORD(high, x); + SET_HIGH_WORD(x, high & 0x7fffffff); + return x; +} + +static const float huge_f = 1.0e30F; + +static const float TWO23[2] = { + 8.3886080000e+06, /* 0x4b000000 */ + -8.3886080000e+06, /* 0xcb000000 */ +}; + +static float +freebsd_truncf(float x) +{ + int32_t i0, j0; + u_int32_t i; + GET_FLOAT_WORD(i0, x); + j0 = ((i0 >> 23) & 0xff) - 0x7f; + if (j0 < 23) { + if (j0 < 0) { /* raise inexact if x != 0 */ + if (huge_f + x > 0.0F) /* |x|<1, so return 0*sign(x) */ + i0 &= 0x80000000; + } + else { + i = (0x007fffff) >> j0; + if ((i0 & i) == 0) + return x; /* x is integral */ + if (huge_f + x > 0.0F) /* raise inexact flag */ + i0 &= (~i); + } + } + else { + if (j0 == 0x80) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ + } + SET_FLOAT_WORD(x, i0); + return x; +} + +static float +freebsd_rintf(float x) +{ + int32_t i0, j0, sx; + float w, t; + GET_FLOAT_WORD(i0, x); + sx = (i0 >> 31) & 1; + j0 = ((i0 >> 23) & 0xff) - 0x7f; + if (j0 < 23) { + if (j0 < 0) { + if ((i0 & 0x7fffffff) == 0) + return x; + STRICT_ASSIGN(float, w, TWO23[sx] + x); + t = w - TWO23[sx]; + GET_FLOAT_WORD(i0, t); + SET_FLOAT_WORD(t, (i0 & 0x7fffffff) | (sx << 31)); + return t; + } + STRICT_ASSIGN(float, w, TWO23[sx] + x); + return w - TWO23[sx]; + } + if (j0 == 0x80) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ +} + +static float +freebsd_ceilf(float x) +{ + int32_t i0, j0; + u_int32_t i; + + GET_FLOAT_WORD(i0, x); + j0 = ((i0 >> 23) & 0xff) - 0x7f; + if (j0 < 23) { + if (j0 < 0) { /* raise inexact if x != 0 */ + if (huge_f + x > (float)0.0) { /* return 0*sign(x) if |x|<1 */ + if (i0 < 0) { + i0 = 0x80000000; + } + else if (i0 != 0) { + i0 = 0x3f800000; + } + } + } + else { + i = (0x007fffff) >> j0; + if ((i0 & i) == 0) + return x; /* x is integral */ + if (huge_f + x > (float)0.0) { /* raise inexact flag */ + if (i0 > 0) + i0 += (0x00800000) >> j0; + i0 &= (~i); + } + } + } + else { + if (j0 == 0x80) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ + } + SET_FLOAT_WORD(x, i0); + return x; +} + +static float +freebsd_floorf(float x) +{ + int32_t i0, j0; + u_int32_t i; + GET_FLOAT_WORD(i0, x); + j0 = ((i0 >> 23) & 0xff) - 0x7f; + if (j0 < 23) { + if (j0 < 0) { /* raise inexact if x != 0 */ + if (huge_f + x > (float)0.0) { /* return 0*sign(x) if |x|<1 */ + if (i0 >= 0) { + i0 = 0; + } + else if ((i0 & 0x7fffffff) != 0) { + i0 = 0xbf800000; + } + } + } + else { + i = (0x007fffff) >> j0; + if ((i0 & i) == 0) + return x; /* x is integral */ + if (huge_f + x > (float)0.0) { /* raise inexact flag */ + if (i0 < 0) + i0 += (0x00800000) >> j0; + i0 &= (~i); + } + } + } + else { + if (j0 == 0x80) + return x + x; /* inf or NaN */ + else + return x; /* x is integral */ + } + SET_FLOAT_WORD(x, i0); + return x; +} + +static float +freebsd_fminf(float x, float y) +{ + if (is_little_endian()) { + IEEEf2bits_L u[2] = { 0 }; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[1].bits.sign].f); + } + else { + IEEEf2bits_B u[2] = { 0 }; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[1].bits.sign].f); + } + + return (x < y ? x : y); +} + +static float +freebsd_fmaxf(float x, float y) +{ + if (is_little_endian()) { + IEEEf2bits_L u[2] = { 0 }; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[0].bits.sign].f); + } + else { + IEEEf2bits_B u[2] = { 0 }; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[0].bits.sign].f); + } + + return (x > y ? x : y); +} + +static double +freebsd_copysign(double x, double y) +{ + u_int32_t hx, hy; + GET_HIGH_WORD(hx, x); + GET_HIGH_WORD(hy, y); + SET_HIGH_WORD(x, (hx & 0x7fffffff) | (hy & 0x80000000)); + return x; +} + +static double +freebsd_scalbn(double x, int n) +{ + int32_t k, hx, lx; + EXTRACT_WORDS(hx, lx, x); + k = (hx & 0x7ff00000) >> 20; /* extract exponent */ + if (k == 0) { /* 0 or subnormal x */ + if ((lx | (hx & 0x7fffffff)) == 0) + return x; /* +-0 */ + x *= two54; + GET_HIGH_WORD(hx, x); + k = ((hx & 0x7ff00000) >> 20) - 54; + if (n < -50000) + return tiny * x; /*underflow*/ + } + if (k == 0x7ff) + return x + x; /* NaN or Inf */ + k = k + n; + if (k > 0x7fe) + return huge * freebsd_copysign(huge, x); /* overflow */ + if (k > 0) /* normal result */ + { + SET_HIGH_WORD(x, (hx & 0x800fffff) | (k << 20)); + return x; + } + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge * freebsd_copysign(huge, x); /*overflow*/ + else + return tiny * freebsd_copysign(tiny, x); /*underflow*/ + } + k += 54; /* subnormal result */ + SET_HIGH_WORD(x, (hx & 0x800fffff) | (k << 20)); + return x * twom54; +} + +static double +freebsd_pow(double x, double y) +{ + double z, ax, z_h, z_l, p_h, p_l; + double y1, t1, t2, r, s, t, u, v, w; + int32_t i, j, k, yisint, n; + int32_t hx, hy, ix, iy; + u_int32_t lx, ly; + + EXTRACT_WORDS(hx, lx, x); + EXTRACT_WORDS(hy, ly, y); + ix = hx & 0x7fffffff; + iy = hy & 0x7fffffff; + + /* y==zero: x**0 = 1 */ + if ((iy | ly) == 0) + return one; + + /* x==1: 1**y = 1, even if y is NaN */ + if (hx == 0x3ff00000 && lx == 0) + return one; + + /* y!=zero: result is NaN if either arg is NaN */ + if (ix > 0x7ff00000 || ((ix == 0x7ff00000) && (lx != 0)) || iy > 0x7ff00000 + || ((iy == 0x7ff00000) && (ly != 0))) + return (x + 0.0) + (y + 0.0); + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if (hx < 0) { + if (iy >= 0x43400000) + yisint = 2; /* even integer y */ + else if (iy >= 0x3ff00000) { + k = (iy >> 20) - 0x3ff; /* exponent */ + if (k > 20) { + j = ly >> (52 - k); + if (((u_int32_t)(j << (52 - k))) == ly) + yisint = 2 - (j & 1); + } + else if (ly == 0) { + j = iy >> (20 - k); + if ((j << (20 - k)) == iy) + yisint = 2 - (j & 1); + } + } + } + + /* special value of y */ + if (ly == 0) { + if (iy == 0x7ff00000) { /* y is +-inf */ + if (((ix - 0x3ff00000) | lx) == 0) + return one; /* (-1)**+-inf is NaN */ + else if (ix >= 0x3ff00000) /* (|x|>1)**+-inf = inf,0 */ + return (hy >= 0) ? y : zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy < 0) ? -y : zero; + } + if (iy == 0x3ff00000) { /* y is +-1 */ + if (hy < 0) + return one / x; + else + return x; + } + if (hy == 0x40000000) + return x * x; /* y is 2 */ + if (hy == 0x40080000) + return x * x * x; /* y is 3 */ + if (hy == 0x40100000) { /* y is 4 */ + u = x * x; + return u * u; + } + if (hy == 0x3fe00000) { /* y is 0.5 */ + if (hx >= 0) /* x >= +0 */ + return sqrt(x); + } + } + + ax = fabs(x); + /* special value of x */ + if (lx == 0) { + if (ix == 0x7ff00000 || ix == 0 || ix == 0x3ff00000) { + z = ax; /*x is +-0,+-inf,+-1*/ + if (hy < 0) + z = one / z; /* z = (1/|x|) */ + if (hx < 0) { + if (((ix - 0x3ff00000) | yisint) == 0) { + z = (z - z) / (z - z); /* (-1)**non-int is NaN */ + } + else if (yisint == 1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + /* CYGNUS LOCAL + fdlibm-5.3 fix: This used to be + n = (hx>>31)+1; + but ANSI C says a right shift of a signed negative quantity is + implementation defined. */ + n = ((u_int32_t)hx >> 31) - 1; + + /* (x<0)**(non-int) is NaN */ + if ((n | yisint) == 0) + return (x - x) / (x - x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if ((n | (yisint - 1)) == 0) + s = -one; /* (-ve)**(odd int) */ + + /* |y| is huge */ + if (iy > 0x41e00000) { /* if |y| > 2**31 */ + if (iy > 0x43f00000) { /* if |y| > 2**64, must o/uflow */ + if (ix <= 0x3fefffff) + return (hy < 0) ? huge * huge : tiny * tiny; + if (ix >= 0x3ff00000) + return (hy > 0) ? huge * huge : tiny * tiny; + } + /* over/underflow if x is not close to one */ + if (ix < 0x3fefffff) + return (hy < 0) ? s * huge * huge : s * tiny * tiny; + if (ix > 0x3ff00000) + return (hy > 0) ? s * huge * huge : s * tiny * tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax - one; /* t has 20 trailing zeros */ + w = (t * t) * (0.5 - t * (0.3333333333333333333333 - t * 0.25)); + u = ivln2_h * t; /* ivln2_h has 21 sig. bits */ + v = t * ivln2_l - w * ivln2; + t1 = u + v; + SET_LOW_WORD(t1, 0); + t2 = v - (t1 - u); + } + else { + double ss, s2, s_h, s_l, t_h, t_l; + n = 0; + /* take care subnormal number */ + if (ix < 0x00100000) { + ax *= two53; + n -= 53; + GET_HIGH_WORD(ix, ax); + } + n += ((ix) >> 20) - 0x3ff; + j = ix & 0x000fffff; + /* determine interval */ + ix = j | 0x3ff00000; /* normalize ix */ + if (j <= 0x3988E) + k = 0; /* |x|> 1) | 0x20000000) + 0x00080000 + (k << 18)); + t_l = ax - (t_h - bp[k]); + s_l = v * ((u - s_h * t_h) - s_h * t_l); + /* compute log(ax) */ + s2 = ss * ss; + r = s2 * s2 + * (L1 + s2 * (L2 + s2 * (L3 + s2 * (L4 + s2 * (L5 + s2 * L6))))); + r += s_l * (s_h + ss); + s2 = s_h * s_h; + t_h = 3.0 + s2 + r; + SET_LOW_WORD(t_h, 0); + t_l = r - ((t_h - 3.0) - s2); + /* u+v = ss*(1+...) */ + u = s_h * t_h; + v = s_l * t_h + t_l * ss; + /* 2/(3log2)*(ss+...) */ + p_h = u + v; + SET_LOW_WORD(p_h, 0); + p_l = v - (p_h - u); + z_h = cp_h * p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l * p_h + p_l * cp + dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h + z_l) + dp_h[k]) + t); + SET_LOW_WORD(t1, 0); + t2 = z_l - (((t1 - t) - dp_h[k]) - z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + SET_LOW_WORD(y1, 0); + p_l = (y - y1) * t1 + y * t2; + p_h = y1 * t1; + z = p_l + p_h; + EXTRACT_WORDS(j, i, z); + if (j >= 0x40900000) { /* z >= 1024 */ + if (((j - 0x40900000) | i) != 0) /* if z > 1024 */ + return s * huge * huge; /* overflow */ + else { + if (p_l + ovt > z - p_h) + return s * huge * huge; /* overflow */ + } + } + else if ((j & 0x7fffffff) >= 0x4090cc00) { /* z <= -1075 */ + if (((j - 0xc090cc00) | i) != 0) /* z < -1075 */ + return s * tiny * tiny; /* underflow */ + else { + if (p_l <= z - p_h) + return s * tiny * tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j & 0x7fffffff; + k = (i >> 20) - 0x3ff; + n = 0; + if (i > 0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j + (0x00100000 >> (k + 1)); + k = ((n & 0x7fffffff) >> 20) - 0x3ff; /* new k for n */ + t = zero; + SET_HIGH_WORD(t, n & ~(0x000fffff >> k)); + n = ((n & 0x000fffff) | 0x00100000) >> (20 - k); + if (j < 0) + n = -n; + p_h -= t; + } + t = p_l + p_h; + SET_LOW_WORD(t, 0); + u = t * lg2_h; + v = (p_l - (t - p_h)) * lg2 + t * lg2_l; + z = u + v; + w = v - (z - u); + t = z * z; + t1 = z - t * (P1 + t * (P2 + t * (P3 + t * (P4 + t * P5)))); + r = (z * t1) / (t1 - two) - (w + z * w); + z = one - (r - z); + GET_HIGH_WORD(j, z); + j += (n << 20); + if ((j >> 20) <= 0) + z = freebsd_scalbn(z, n); /* subnormal output */ + else + SET_HIGH_WORD(z, j); + return s * z; +} + +double +atan(double x) +{ + return freebsd_atan(x); +} + +double +atan2(double y, double x) +{ + return freebsd_atan2(y, x); +} + +#ifndef BH_HAS_SQRT +double +sqrt(double x) +{ + return freebsd_sqrt(x); +} +#endif + +double +floor(double x) +{ + return freebsd_floor(x); +} + +double +ceil(double x) +{ + return freebsd_ceil(x); +} + +double +fmin(double x, double y) +{ + return x < y ? x : y; +} + +double +fmax(double x, double y) +{ + return x > y ? x : y; +} + +double +rint(double x) +{ + return freebsd_rint(x); +} + +double +fabs(double x) +{ + return freebsd_fabs(x); +} + +int +isnan_float(float x) +{ + return freebsd_isnanf(x); +} + +int +isnan_double(double x) +{ + return freebsd_isnan(x); +} + +double +trunc(double x) +{ + return (x > 0) ? freebsd_floor(x) : freebsd_ceil(x); +} + +int +signbit_float(float x) +{ + unsigned int i; + GET_FLOAT_WORD(i, x); + return (int)(i >> 31); +} + +int +signbit_double(double x) +{ + return ((__HI(x) & 0x80000000) >> 31); +} + +float +fabsf(float x) +{ + return freebsd_fabsf(x); +} + +float +truncf(float x) +{ + return freebsd_truncf(x); +} + +float +rintf(float x) +{ + return freebsd_rintf(x); +} + +float +ceilf(float x) +{ + return freebsd_ceilf(x); +} + +float +floorf(float x) +{ + return freebsd_floorf(x); +} + +float +fminf(float x, float y) +{ + return freebsd_fminf(x, y); +} + +float +fmaxf(float x, float y) +{ + return freebsd_fmaxf(x, y); +} + +#ifndef BH_HAS_SQRTF +float +sqrtf(float x) +{ + return freebsd_sqrtf(x); +} +#endif + +double +pow(double x, double y) +{ + return freebsd_pow(x, y); +} + +double +scalbn(double x, int n) +{ + return freebsd_scalbn(x, n); +} diff --git a/core/shared/platform/common/math/platform_api_math.cmake b/core/shared/platform/common/math/platform_api_math.cmake new file mode 100644 index 0000000000..6be55262bf --- /dev/null +++ b/core/shared/platform/common/math/platform_api_math.cmake @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_COMMON_MATH_DIR ${CMAKE_CURRENT_LIST_DIR}) + +file (GLOB_RECURSE math_source_all ${PLATFORM_COMMON_MATH_DIR}/*.c) + +set (PLATFORM_COMMON_MATH_SOURCE ${math_source_all} ) diff --git a/core/shared/platform/common/memory/mremap.c b/core/shared/platform/common/memory/mremap.c new file mode 100644 index 0000000000..dca10a342b --- /dev/null +++ b/core/shared/platform/common/memory/mremap.c @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2024 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size) +{ + return os_mremap_slow(old_addr, old_size, new_size); +} diff --git a/core/shared/platform/common/memory/platform_api_memory.cmake b/core/shared/platform/common/memory/platform_api_memory.cmake new file mode 100644 index 0000000000..9f06c1391a --- /dev/null +++ b/core/shared/platform/common/memory/platform_api_memory.cmake @@ -0,0 +1,4 @@ +# Copyright (C) 2024 Amazon Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +file (GLOB_RECURSE PLATFORM_COMMON_MEMORY_SOURCE ${CMAKE_CURRENT_LIST_DIR}/*.c) diff --git a/core/shared/platform/common/posix/SConscript b/core/shared/platform/common/posix/SConscript new file mode 100644 index 0000000000..48cffda254 --- /dev/null +++ b/core/shared/platform/common/posix/SConscript @@ -0,0 +1,20 @@ +# +# Copyright 2024 Sony Semiconductor Solutions Corporation. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import re + +Import('rtconfig') + +cwd = GetCurrentDir() +src = Split(''' +posix_file.c +''') +CPPPATH = [cwd] + +group = DefineGroup('iwasm_common_posix', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/shared/platform/common/posix/platform_api_posix.cmake b/core/shared/platform/common/posix/platform_api_posix.cmake new file mode 100644 index 0000000000..2553a7d04a --- /dev/null +++ b/core/shared/platform/common/posix/platform_api_posix.cmake @@ -0,0 +1,40 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_COMMON_POSIX_DIR ${CMAKE_CURRENT_LIST_DIR}) + +file (GLOB_RECURSE source_all ${PLATFORM_COMMON_POSIX_DIR}/*.c) + +if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1) + list(REMOVE_ITEM source_all + ${PLATFORM_COMMON_POSIX_DIR}/posix_file.c + ${PLATFORM_COMMON_POSIX_DIR}/posix_clock.c + ) +endif() + +if ((NOT WAMR_BUILD_LIBC_WASI EQUAL 1) AND (NOT WAMR_BUILD_DEBUG_INTERP EQUAL 1)) + list(REMOVE_ITEM source_all + ${PLATFORM_COMMON_POSIX_DIR}/posix_socket.c + ) +else() + include (${CMAKE_CURRENT_LIST_DIR}/../libc-util/platform_common_libc_util.cmake) + set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) +endif() + +# This is to support old CMake version. Newer version of CMake could use +# list APPEND/POP_BACK methods. +include(CheckSymbolExists) +set (CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE ${CMAKE_REQUIRED_DEFINITIONS}) +check_symbol_exists (mremap "sys/mman.h" MREMAP_EXISTS) +list (REMOVE_AT CMAKE_REQUIRED_DEFINITIONS 0) + +if(MREMAP_EXISTS) + add_definitions (-DWASM_HAVE_MREMAP=1) + add_definitions (-D_GNU_SOURCE) +else() + add_definitions (-DWASM_HAVE_MREMAP=0) + include (${CMAKE_CURRENT_LIST_DIR}/../memory/platform_api_memory.cmake) + set (source_all ${source_all} ${PLATFORM_COMMON_MEMORY_SOURCE}) +endif() + +set (PLATFORM_COMMON_POSIX_SOURCE ${source_all} ) diff --git a/core/shared/platform/common/posix/posix_blocking_op.c b/core/shared/platform/common/posix/posix_blocking_op.c new file mode 100644 index 0000000000..e56f84cf1d --- /dev/null +++ b/core/shared/platform/common/posix/posix_blocking_op.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" + +#ifdef OS_ENABLE_WAKEUP_BLOCKING_OP + +static bool g_blocking_op_inited = false; +static int g_blocking_op_signo = SIGUSR1; +static sigset_t g_blocking_op_sigmask; + +static void +blocking_op_sighandler(int signo) +{ + /* nothing */ + (void)signo; +} + +void +os_set_signal_number_for_blocking_op(int signo) +{ + g_blocking_op_signo = signo; +} + +int +os_blocking_op_init() +{ + if (g_blocking_op_inited) { + return BHT_OK; + } + + sigemptyset(&g_blocking_op_sigmask); + sigaddset(&g_blocking_op_sigmask, g_blocking_op_signo); + + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = blocking_op_sighandler; + if (sigaction(g_blocking_op_signo, &sa, NULL)) { + return BHT_ERROR; + } + g_blocking_op_inited = true; + return BHT_OK; +} + +void +os_begin_blocking_op() +{ + pthread_sigmask(SIG_UNBLOCK, &g_blocking_op_sigmask, NULL); +} + +void +os_end_blocking_op() +{ + pthread_sigmask(SIG_BLOCK, &g_blocking_op_sigmask, NULL); +} + +int +os_wakeup_blocking_op(korp_tid tid) +{ + int ret = pthread_kill(tid, g_blocking_op_signo); + if (ret != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +#endif /* OS_ENABLE_WAKEUP_BLOCKING_OP */ diff --git a/core/shared/platform/common/posix/posix_clock.c b/core/shared/platform/common/posix/posix_clock.c new file mode 100644 index 0000000000..41413211cb --- /dev/null +++ b/core/shared/platform/common/posix/posix_clock.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "libc_errno.h" +#include "platform_api_extension.h" + +#define NANOSECONDS_PER_SECOND 1000000000ULL + +static __wasi_errno_t +wasi_clockid_to_clockid(__wasi_clockid_t in, clockid_t *out) +{ + switch (in) { + case __WASI_CLOCK_MONOTONIC: + *out = CLOCK_MONOTONIC; + return __WASI_ESUCCESS; + case __WASI_CLOCK_REALTIME: + *out = CLOCK_REALTIME; + return __WASI_ESUCCESS; + case __WASI_CLOCK_PROCESS_CPUTIME_ID: +#if defined(CLOCK_PROCESS_CPUTIME_ID) + *out = CLOCK_PROCESS_CPUTIME_ID; + return __WASI_ESUCCESS; +#else + return __WASI_ENOTSUP; +#endif + case __WASI_CLOCK_THREAD_CPUTIME_ID: +#if defined(CLOCK_THREAD_CPUTIME_ID) + *out = CLOCK_THREAD_CPUTIME_ID; + return __WASI_ESUCCESS; +#else + return __WASI_ENOTSUP; +#endif + default: + return __WASI_EINVAL; + } +} + +static __wasi_timestamp_t +timespec_to_nanoseconds(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / NANOSECONDS_PER_SECOND) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * NANOSECONDS_PER_SECOND + + (__wasi_timestamp_t)ts->tv_nsec; +} + +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution) +{ + clockid_t nclock_id; + __wasi_errno_t error = wasi_clockid_to_clockid(clock_id, &nclock_id); + + if (error != __WASI_ESUCCESS) + return error; + + struct timespec ts; + if (clock_getres(nclock_id, &ts) < 0) + return convert_errno(errno); + + *resolution = timespec_to_nanoseconds(&ts); + + return error; +} + +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time) +{ + clockid_t nclock_id; + __wasi_errno_t error = wasi_clockid_to_clockid(clock_id, &nclock_id); + + (void)precision; + + if (error != __WASI_ESUCCESS) + return error; + + struct timespec ts; + if (clock_gettime(nclock_id, &ts) < 0) + return convert_errno(errno); + + *time = timespec_to_nanoseconds(&ts); + + return error; +} diff --git a/core/shared/platform/common/posix/posix_file.c b/core/shared/platform/common/posix/posix_file.c new file mode 100644 index 0000000000..a05853f5e4 --- /dev/null +++ b/core/shared/platform/common/posix/posix_file.c @@ -0,0 +1,1069 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "libc_errno.h" +#include + +#if !defined(__APPLE__) && !defined(ESP_PLATFORM) +#define CONFIG_HAS_PWRITEV 1 +#define CONFIG_HAS_PREADV 1 +#else +#define CONFIG_HAS_PWRITEV 0 +#define CONFIG_HAS_PREADV 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(ESP_PLATFORM) +#define CONFIG_HAS_FDATASYNC 1 +#else +#define CONFIG_HAS_FDATASYNC 0 +#endif + +/* + * For NuttX, CONFIG_HAS_ISATTY is provided by its platform header. + * (platform_internal.h) + */ +#if !defined(CONFIG_HAS_D_INO) +#if !defined(__NuttX__) && !defined(__RTTHREAD__) +#define CONFIG_HAS_D_INO 1 +#define CONFIG_HAS_ISATTY 1 +#else +#define CONFIG_HAS_D_INO 0 +#endif +#endif + +#if !defined(__APPLE__) && !defined(ESP_PLATFORM) && !defined(__COSMOPOLITAN__) +#define CONFIG_HAS_POSIX_FALLOCATE 1 +#else +#define CONFIG_HAS_POSIX_FALLOCATE 0 +#endif + +#if defined(O_DSYNC) +#define CONFIG_HAS_O_DSYNC +#endif + +// POSIX requires O_RSYNC to be defined, but Linux explicitly doesn't support +// it. +#if defined(O_RSYNC) && !defined(__linux__) +#define CONFIG_HAS_O_RSYNC +#endif + +#if defined(O_SYNC) +#define CONFIG_HAS_O_SYNC +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +// Converts a POSIX timespec to a WASI timestamp. +static __wasi_timestamp_t +convert_timespec(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + + (__wasi_timestamp_t)ts->tv_nsec; +} + +// Converts a POSIX stat structure to a WASI filestat structure +static void +convert_stat(os_file_handle handle, const struct stat *in, + __wasi_filestat_t *out) +{ + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_nlink = (__wasi_linkcount_t)in->st_nlink; + out->st_size = (__wasi_filesize_t)in->st_size; +#ifdef __APPLE__ + out->st_atim = convert_timespec(&in->st_atimespec); + out->st_mtim = convert_timespec(&in->st_mtimespec); + out->st_ctim = convert_timespec(&in->st_ctimespec); +#else + out->st_atim = convert_timespec(&in->st_atim); + out->st_mtim = convert_timespec(&in->st_mtim); + out->st_ctim = convert_timespec(&in->st_ctim); +#endif + + // Convert the file type. In the case of sockets there is no way we + // can easily determine the exact socket type. + if (S_ISBLK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + } + else if (S_ISCHR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + } + else if (S_ISDIR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_DIRECTORY; + } + else if (S_ISFIFO(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + } + else if (S_ISLNK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; + } + else if (S_ISREG(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_REGULAR_FILE; + } + else if (S_ISSOCK(in->st_mode)) { + int socktype; + socklen_t socktypelen = sizeof(socktype); + + if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) + < 0) { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + + switch (socktype) { + case SOCK_DGRAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + } + else { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + } +} + +static void +convert_timestamp(__wasi_timestamp_t in, struct timespec *out) +{ + // Store sub-second remainder. +#if defined(__SYSCALL_SLONG_TYPE) + out->tv_nsec = (__SYSCALL_SLONG_TYPE)(in % 1000000000); +#else + out->tv_nsec = (long)(in % 1000000000); +#endif + in /= 1000000000; + + // Clamp to the maximum in case it would overflow our system's 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 +// futimens() and utimensat(). +static void +convert_utimens_arguments(__wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags, struct timespec *ts) +{ + if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { + ts[0].tv_nsec = UTIME_NOW; + } + else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { + convert_timestamp(st_atim, &ts[0]); + } + else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { + ts[1].tv_nsec = UTIME_NOW; + } + else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { + convert_timestamp(st_mtim, &ts[1]); + } + else { + ts[1].tv_nsec = UTIME_OMIT; + } +} + +__wasi_errno_t +os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) +{ + struct stat stat_buf; + int ret = fstat(handle, &stat_buf); + + if (ret < 0) + return convert_errno(errno); + + convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fstatat(os_file_handle handle, const char *path, + struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags) +{ + struct stat stat_buf; + int ret = fstatat(handle, path, &stat_buf, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? AT_SYMLINK_FOLLOW + : AT_SYMLINK_NOFOLLOW); + + if (ret < 0) + return convert_errno(errno); + + convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags) +{ + int ret = fcntl(handle, F_GETFL); + + if (ret < 0) + return convert_errno(errno); + + *flags = 0; + + if ((ret & O_APPEND) != 0) + *flags |= __WASI_FDFLAG_APPEND; +#ifdef CONFIG_HAS_O_DSYNC + if ((ret & O_DSYNC) != 0) + *flags |= __WASI_FDFLAG_DSYNC; +#endif + if ((ret & O_NONBLOCK) != 0) + *flags |= __WASI_FDFLAG_NONBLOCK; +#ifdef CONFIG_HAS_O_RSYNC + if ((ret & O_RSYNC) != 0) + *flags |= __WASI_FDFLAG_RSYNC; +#endif +#ifdef CONFIG_HAS_O_SYNC + if ((ret & O_SYNC) != 0) + *flags |= __WASI_FDFLAG_SYNC; +#endif + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) +{ + int fcntl_flags = 0; + + if ((flags & __WASI_FDFLAG_APPEND) != 0) + fcntl_flags |= O_APPEND; + if ((flags & __WASI_FDFLAG_DSYNC) != 0) +#ifdef CONFIG_HAS_O_DSYNC + fcntl_flags |= O_DSYNC; +#else + return __WASI_ENOTSUP; +#endif + if ((flags & __WASI_FDFLAG_NONBLOCK) != 0) + fcntl_flags |= O_NONBLOCK; + if ((flags & __WASI_FDFLAG_RSYNC) != 0) +#ifdef CONFIG_HAS_O_RSYNC + fcntl_flags |= O_RSYNC; +#else + return __WASI_ENOTSUP; +#endif + if ((flags & __WASI_FDFLAG_SYNC) != 0) +#ifdef CONFIG_HAS_O_SYNC + fcntl_flags |= O_SYNC; +#else + return __WASI_ENOTSUP; +#endif + + int ret = fcntl(handle, F_SETFL, fcntl_flags); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fdatasync(os_file_handle handle) +{ +#if CONFIG_HAS_FDATASYNC + int ret = fdatasync(handle); +#else + int ret = fsync(handle); +#endif + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fsync(os_file_handle handle) +{ + int ret = fsync(handle); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_open_preopendir(const char *path, os_file_handle *out) +{ + + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + + if (fd < 0) + return convert_errno(errno); + + *out = fd; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode read_write_mode, os_file_handle *out) +{ + int open_flags = 0; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + open_flags |= O_CREAT; + } + if ((oflags & __WASI_O_DIRECTORY) != 0) + open_flags |= O_DIRECTORY; + if ((oflags & __WASI_O_EXCL) != 0) + open_flags |= O_EXCL; + if ((oflags & __WASI_O_TRUNC) != 0) { + open_flags |= O_TRUNC; + } + + // Convert file descriptor flags. + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + open_flags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { +#ifdef CONFIG_HAS_O_DSYNC + open_flags |= O_DSYNC; +#else + return __WASI_ENOTSUP; +#endif + } + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + open_flags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { +#ifdef CONFIG_HAS_O_RSYNC + open_flags |= O_RSYNC; +#else + return __WASI_ENOTSUP; +#endif + } + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { +#ifdef CONFIG_HAS_O_SYNC + open_flags |= O_SYNC; +#else + return __WASI_ENOTSUP; +#endif + } + + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) { + open_flags |= O_NOFOLLOW; + } + + switch (read_write_mode) { + case WASI_LIBC_ACCESS_MODE_READ_WRITE: + open_flags |= O_RDWR; + break; + case WASI_LIBC_ACCESS_MODE_READ_ONLY: + open_flags |= O_RDONLY; + break; + case WASI_LIBC_ACCESS_MODE_WRITE_ONLY: + open_flags |= O_WRONLY; + break; + default: + return __WASI_EINVAL; + } + + int fd = openat(handle, path, open_flags, 0666); + + if (fd < 0) { + int openat_errno = errno; + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket. + if (openat_errno == ENXIO) { + struct stat sb; + int ret = fstatat(handle, path, &sb, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? 0 + : AT_SYMLINK_NOFOLLOW); + return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP + : __WASI_ENXIO; + } + // Linux returns ENOTDIR instead of ELOOP when using + // O_NOFOLLOW|O_DIRECTORY on a symlink. + if (openat_errno == ENOTDIR + && (open_flags & (O_NOFOLLOW | O_DIRECTORY)) != 0) { + struct stat sb; + int ret = fstatat(handle, path, &sb, AT_SYMLINK_NOFOLLOW); + if (S_ISLNK(sb.st_mode)) { + return __WASI_ELOOP; + } + (void)ret; + } + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0 + && openat_errno == EMLINK) + return __WASI_ELOOP; + + return convert_errno(openat_errno); + } + + *out = fd; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_access_mode(os_file_handle handle, + wasi_libc_file_access_mode *access_mode) +{ + int ret = fcntl(handle, F_GETFL, 0); + + if (ret < 0) + return convert_errno(errno); + + switch (ret & O_ACCMODE) { + case O_RDONLY: + *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY; + break; + case O_WRONLY: + *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY; + break; + case O_RDWR: + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + break; + default: + return __WASI_EINVAL; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_close(os_file_handle handle, bool is_stdio) +{ + if (is_stdio) + return __WASI_ESUCCESS; + + int ret = close(handle); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ +#if CONFIG_HAS_PREADV + ssize_t len = + preadv(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset); + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len; + return __WASI_ESUCCESS; +#else + if (iovcnt == 1) { + ssize_t len = pread(handle, iov->buf, iov->buf_len, offset); + + if (len < 0) + return convert_errno(errno); + + *nread = len; + return __WASI_ESUCCESS; + } + + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (int i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + + char *buf = BH_MALLOC(totalsize); + + if (buf == NULL) { + return __WASI_ENOMEM; + } + + // Perform a single read operation. + ssize_t len = pread(handle, buf, totalsize, offset); + + if (len < 0) { + BH_FREE(buf); + return convert_errno(errno); + } + + // Copy data back to vectors. + size_t bufoff = 0; + for (int i = 0; i < iovcnt; ++i) { + if (bufoff + iov[i].buf_len < (size_t)len) { + memcpy(iov[i].buf, buf + bufoff, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + else { + memcpy(iov[i].buf, buf + bufoff, len - bufoff); + break; + } + } + BH_FREE(buf); + *nread = len; + + return __WASI_ESUCCESS; +#endif +} + +__wasi_errno_t +os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + if (iovcnt == 0) + return __WASI_EINVAL; + + ssize_t len = 0; +#if CONFIG_HAS_PWRITEV + len = + pwritev(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset); +#else + if (iovcnt == 1) { + len = pwrite(handle, iov->buf, iov->buf_len, offset); + } + else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (int i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = BH_MALLOC(totalsize); + if (buf == NULL) { + return __WASI_ENOMEM; + } + size_t bufoff = 0; + for (int i = 0; i < iovcnt; ++i) { + memcpy(buf + bufoff, iov[i].buf, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + + // Perform a single write operation. + len = pwrite(handle, buf, totalsize, offset); + BH_FREE(buf); + } +#endif + if (len < 0) + return convert_errno(errno); + + *nwritten = (size_t)len; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + size_t *nread) +{ + ssize_t len = readv(handle, (const struct iovec *)iov, (int)iovcnt); + + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten) +{ + ssize_t len = writev(handle, (const struct iovec *)iov, (int)iovcnt); + + if (len < 0) + return convert_errno(errno); + + *nwritten = (size_t)len; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fallocate(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length) +{ +#if CONFIG_HAS_POSIX_FALLOCATE + int ret = posix_fallocate(handle, (off_t)offset, (off_t)length); +#else + // At least ensure that the file is grown to the right size. + // TODO(ed): See if this can somehow be implemented without any race + // conditions. We may end up shrinking the file right now. + struct stat sb; + int ret = fstat(handle, &sb); + off_t newsize = (off_t)(offset + length); + + if (ret == 0 && sb.st_size < newsize) + ret = ftruncate(handle, newsize); +#endif + + if (ret != 0) + return convert_errno(ret); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_ftruncate(os_file_handle handle, __wasi_filesize_t size) +{ + int ret = ftruncate(handle, (off_t)size); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_futimens(os_file_handle handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags) +{ + struct timespec ts[2]; + convert_utimens_arguments(access_time, modification_time, fstflags, ts); + + int ret = futimens(handle, ts); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_utimensat(os_file_handle handle, const char *path, + __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags, + __wasi_lookupflags_t lookup_flags) +{ + struct timespec ts[2]; + convert_utimens_arguments(access_time, modification_time, fstflags, ts); + + int ret = utimensat(handle, path, ts, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? 0 + : AT_SYMLINK_NOFOLLOW); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readlinkat(os_file_handle handle, const char *path, char *buf, + size_t bufsize, size_t *nread) +{ + // Linux requires that the buffer size is positive. whereas POSIX does + // not. Use a fake buffer to store the results if the size is zero. + char fakebuf[1]; + ssize_t len = readlinkat(handle, path, bufsize == 0 ? fakebuf : buf, + bufsize == 0 ? sizeof(fakebuf) : bufsize); + + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len < bufsize ? (size_t)len : bufsize; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_linkat(os_file_handle from_handle, const char *from_path, + os_file_handle to_handle, const char *to_path, + __wasi_lookupflags_t lookup_flags) +{ + int ret = linkat( + from_handle, from_path, to_handle, to_path, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) ? AT_SYMLINK_FOLLOW : 0); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path) +{ + int ret = symlinkat(old_path, handle, new_path); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_mkdirat(os_file_handle handle, const char *path) +{ + int ret = mkdirat(handle, path, 0777); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_renameat(os_file_handle old_handle, const char *old_path, + os_file_handle new_handle, const char *new_path) +{ + + int ret = renameat(old_handle, old_path, new_handle, new_path); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_unlinkat(os_file_handle handle, const char *path, bool is_dir) +{ + int ret = unlinkat(handle, path, is_dir ? AT_REMOVEDIR : 0); + +#ifndef __linux__ + if (ret < 0) { + // Non-Linux implementations may return EPERM when attempting to remove + // a directory without REMOVEDIR. While that's what POSIX specifies, + // it's less useful. Adjust this to EISDIR. It doesn't matter that this + // is not atomic with the unlinkat, because if the file is removed and a + // directory is created before fstatat sees it, we're racing with that + // change anyway and unlinkat could have legitimately seen the directory + // if the race had turned out differently. + if (errno == EPERM) { + struct stat statbuf; + if (fstatat(handle, path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0 + && S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; + } + } + // POSIX permits either EEXIST or ENOTEMPTY when the directory is not + // empty. Map it to ENOTEMPTY. + else if (errno == EEXIST) { + errno = ENOTEMPTY; + } + + return convert_errno(errno); + } +#endif + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_lseek(os_file_handle handle, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *new_offset) +{ + int nwhence; + + switch (whence) { + case __WASI_WHENCE_CUR: + nwhence = SEEK_CUR; + break; + case __WASI_WHENCE_END: + nwhence = SEEK_END; + break; + case __WASI_WHENCE_SET: + nwhence = SEEK_SET; + break; + default: + return __WASI_EINVAL; + } + + off_t ret = lseek(handle, offset, nwhence); + + if (ret < 0) + return convert_errno(errno); + + *new_offset = (__wasi_filesize_t)ret; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fadvise(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length, __wasi_advice_t advice) +{ +#ifdef POSIX_FADV_NORMAL + int nadvice; + switch (advice) { + case __WASI_ADVICE_DONTNEED: + nadvice = POSIX_FADV_DONTNEED; + break; + case __WASI_ADVICE_NOREUSE: + nadvice = POSIX_FADV_NOREUSE; + break; + case __WASI_ADVICE_NORMAL: + nadvice = POSIX_FADV_NORMAL; + break; + case __WASI_ADVICE_RANDOM: + nadvice = POSIX_FADV_RANDOM; + break; + case __WASI_ADVICE_SEQUENTIAL: + nadvice = POSIX_FADV_SEQUENTIAL; + break; + case __WASI_ADVICE_WILLNEED: + nadvice = POSIX_FADV_WILLNEED; + break; + default: + return __WASI_EINVAL; + } + + int ret = posix_fadvise(handle, (off_t)offset, (off_t)length, nadvice); + + if (ret != 0) + return convert_errno(ret); + + return __WASI_ESUCCESS; +#else + // Advisory information can be safely ignored if not supported + switch (advice) { + case __WASI_ADVICE_DONTNEED: + case __WASI_ADVICE_NOREUSE: + case __WASI_ADVICE_NORMAL: + case __WASI_ADVICE_RANDOM: + case __WASI_ADVICE_SEQUENTIAL: + case __WASI_ADVICE_WILLNEED: + return __WASI_ESUCCESS; + default: + return __WASI_EINVAL; + } +#endif +} + +__wasi_errno_t +os_isatty(os_file_handle handle) +{ +#if CONFIG_HAS_ISATTY + int ret = isatty(handle); + + if (ret == 1) + return __WASI_ESUCCESS; + + return __WASI_ENOTTY; +#else + return __WASI_ENOTSUP; +#endif +} + +bool +os_is_stdin_handle(os_file_handle fd) +{ + return fd == STDIN_FILENO; +} + +bool +os_is_stdout_handle(os_file_handle fd) +{ + return fd == STDOUT_FILENO; +} + +bool +os_is_stderr_handle(os_file_handle fd) +{ + return fd == STDERR_FILENO; +} + +os_file_handle +os_convert_stdin_handle(os_raw_file_handle raw_stdin) +{ + return raw_stdin >= 0 ? raw_stdin : STDIN_FILENO; +} + +os_file_handle +os_convert_stdout_handle(os_raw_file_handle raw_stdout) +{ + return raw_stdout >= 0 ? raw_stdout : STDOUT_FILENO; +} + +os_file_handle +os_convert_stderr_handle(os_raw_file_handle raw_stderr) +{ + return raw_stderr >= 0 ? raw_stderr : STDERR_FILENO; +} + +__wasi_errno_t +os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream) +{ + *dir_stream = fdopendir(handle); + + if (*dir_stream == NULL) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_rewinddir(os_dir_stream dir_stream) +{ + rewinddir(dir_stream); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position) +{ + seekdir(dir_stream, (long)position); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, + const char **d_name) +{ + errno = 0; + + struct dirent *dent = readdir(dir_stream); + + if (dent == NULL) { + *d_name = NULL; + if (errno != 0) { + return convert_errno(errno); + } + else { + return 0; + } + } + + long offset = (__wasi_dircookie_t)telldir(dir_stream); + + size_t namlen = strlen(dent->d_name); + + *d_name = dent->d_name; + entry->d_next = offset; + entry->d_namlen = (__wasi_dirnamlen_t)namlen; +#if CONFIG_HAS_D_INO + entry->d_ino = dent->d_ino; +#else + entry->d_ino = 0; +#endif + + switch (dent->d_type) { + case DT_BLK: + entry->d_type = __WASI_FILETYPE_BLOCK_DEVICE; + break; + case DT_CHR: + entry->d_type = __WASI_FILETYPE_CHARACTER_DEVICE; + break; + case DT_DIR: + entry->d_type = __WASI_FILETYPE_DIRECTORY; + break; + case DT_FIFO: + entry->d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; + case DT_LNK: + entry->d_type = __WASI_FILETYPE_SYMBOLIC_LINK; + break; + case DT_REG: + entry->d_type = __WASI_FILETYPE_REGULAR_FILE; + break; +#ifdef DT_SOCK + case DT_SOCK: + // Technically not correct, but good enough. + entry->d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; +#endif + default: + entry->d_type = __WASI_FILETYPE_UNKNOWN; + break; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_closedir(os_dir_stream dir_stream) +{ + int ret = closedir(dir_stream); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +os_dir_stream +os_get_invalid_dir_stream() +{ + return NULL; +} + +bool +os_is_dir_stream_valid(os_dir_stream *dir_stream) +{ + assert(dir_stream != NULL); + + return *dir_stream != NULL; +} + +bool +os_is_handle_valid(os_file_handle *handle) +{ + assert(handle != NULL); + + return *handle > -1; +} + +char * +os_realpath(const char *path, char *resolved_path) +{ + return realpath(path, resolved_path); +} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return -1; +} + +// Better to define the function here, as Linux-SGX will +// use this file to implement the `_os` functions. +// So we don't need to define them in the Linux-SGX platform. +int +os_ioctl(os_file_handle handle, int request, ...) +{ + int ret = -1; + va_list args; + + va_start(args, request); + ret = ioctl(handle, request, args); + va_end(args); + + return ret; +} + +int +os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout) +{ + return poll(fds, nfs, timeout); +} + +bool +os_compare_file_handle(os_file_handle handle1, os_file_handle handle2) +{ + return handle1 == handle2; +} diff --git a/core/shared/platform/common/posix/posix_malloc.c b/core/shared/platform/common/posix/posix_malloc.c new file mode 100644 index 0000000000..912998ee07 --- /dev/null +++ b/core/shared/platform/common/posix/posix_malloc.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + int ret = -1; + FILE *f; + char line[128] = { 0 }; + unsigned int out_idx = 0; + + if (!out || !size) + goto quit; + + f = fopen("/proc/self/status", "r"); + if (!f) { + perror("fopen failed: "); + goto quit; + } + + memset(out, 0, size); + + while (fgets(line, sizeof(line), f)) { +#if WASM_ENABLE_MEMORY_PROFILING != 0 + if (strncmp(line, "Vm", 2) == 0 || strncmp(line, "Rss", 3) == 0) { +#else + if (strncmp(line, "VmRSS", 5) == 0 + || strncmp(line, "RssAnon", 7) == 0) { +#endif + size_t line_len = strlen(line); + if (line_len >= size - 1 - out_idx) + goto close_file; + + /* copying without null-terminated byte */ + memcpy(out + out_idx, line, line_len); + out_idx += line_len; + } + } + + if (ferror(f)) { + perror("fgets failed: "); + goto close_file; + } + + ret = 0; +close_file: + fclose(f); +quit: + return ret; +} \ No newline at end of file diff --git a/core/shared/platform/common/posix/posix_memmap.c b/core/shared/platform/common/posix/posix_memmap.c new file mode 100644 index 0000000000..d5cad885ca --- /dev/null +++ b/core/shared/platform/common/posix/posix_memmap.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#if defined(__APPLE__) || defined(__MACH__) +#include +#include +#endif + +#ifndef BH_ENABLE_TRACE_MMAP +#define BH_ENABLE_TRACE_MMAP 0 +#endif + +#if BH_ENABLE_TRACE_MMAP != 0 +static size_t total_size_mmapped = 0; +static size_t total_size_munmapped = 0; +#endif + +#define HUGE_PAGE_SIZE (2 * 1024 * 1024) + +#if !defined(__APPLE__) && !defined(__NuttX__) && defined(MADV_HUGEPAGE) +static inline uintptr_t +round_up(uintptr_t v, uintptr_t b) +{ + uintptr_t m = b - 1; + return (v + m) & ~m; +} + +static inline uintptr_t +round_down(uintptr_t v, uintptr_t b) +{ + uintptr_t m = b - 1; + return v & ~m; +} +#endif + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + int map_prot = PROT_NONE; +#if (defined(__APPLE__) || defined(__MACH__)) && defined(__arm64__) \ + && defined(TARGET_OS_OSX) && TARGET_OS_OSX != 0 + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_JIT; +#else + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; +#endif + uint64 request_size, page_size; + uint8 *addr = MAP_FAILED; + uint32 i; + + page_size = (uint64)getpagesize(); + request_size = (size + page_size - 1) & ~(page_size - 1); + +#if !defined(__APPLE__) && !defined(__NuttX__) && defined(MADV_HUGEPAGE) + /* huge page isn't supported on MacOS and NuttX */ + if (request_size >= HUGE_PAGE_SIZE) + /* apply one extra huge page */ + request_size += HUGE_PAGE_SIZE; +#endif + + if ((size_t)request_size < size) { + os_printf("mmap failed: request size overflow due to paging\n"); + return NULL; + } + +#if WASM_ENABLE_MEMORY64 == 0 + if (request_size > 16 * (uint64)UINT32_MAX) { + os_printf("mmap failed: for memory64 at most 64G is allowed\n"); + return NULL; + } +#endif + + if (prot & MMAP_PROT_READ) + map_prot |= PROT_READ; + + if (prot & MMAP_PROT_WRITE) + map_prot |= PROT_WRITE; + + if (prot & MMAP_PROT_EXEC) + map_prot |= PROT_EXEC; + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#ifndef __APPLE__ + if (flags & MMAP_MAP_32BIT) + map_flags |= MAP_32BIT; +#endif +#endif + + if (flags & MMAP_MAP_FIXED) + map_flags |= MAP_FIXED; + +#if defined(BUILD_TARGET_RISCV64_LP64D) || defined(BUILD_TARGET_RISCV64_LP64) + /* As AOT relocation in RISCV64 may require that the code/data mapped + * is in range 0 to 2GB, we try to map the memory with hint address + * (mmap's first argument) to meet the requirement. + */ + if (!hint && !(flags & MMAP_MAP_FIXED) && (flags & MMAP_MAP_32BIT)) { + uint8 *stack_addr = (uint8 *)&map_prot; + uint8 *text_addr = (uint8 *)os_mmap; + /* hint address begins with 1MB */ + static uint8 *hint_addr = (uint8 *)(uintptr_t)BH_MB; + + if ((hint_addr - text_addr >= 0 && hint_addr - text_addr < 100 * BH_MB) + || (text_addr - hint_addr >= 0 + && text_addr - hint_addr < 100 * BH_MB)) { + /* hint address is possibly in text section, skip it */ + hint_addr += 100 * BH_MB; + } + + if ((hint_addr - stack_addr >= 0 && hint_addr - stack_addr < 8 * BH_MB) + || (stack_addr - hint_addr >= 0 + && stack_addr - hint_addr < 8 * BH_MB)) { + /* hint address is possibly in native stack area, skip it */ + hint_addr += 8 * BH_MB; + } + + /* try 10 times, step with 1MB each time */ + for (i = 0; i < 10 && hint_addr < (uint8 *)(uintptr_t)(2ULL * BH_GB); + i++) { + addr = mmap(hint_addr, request_size, map_prot, map_flags, file, 0); + if (addr != MAP_FAILED) { + if (addr > (uint8 *)(uintptr_t)(2ULL * BH_GB)) { + /* unmap and try again if the mapped address doesn't + * meet the requirement */ + os_munmap(addr, request_size); + } + else { + /* success, reset next hint address */ + hint_addr += request_size; + break; + } + } + hint_addr += BH_MB; + } + } +#endif /* end of BUILD_TARGET_RISCV64_LP64D || BUILD_TARGET_RISCV64_LP64 */ + + /* memory hasn't been mapped or was mapped failed previously */ + if (addr == MAP_FAILED) { + /* try 5 times on EAGAIN or ENOMEM, and keep retrying on EINTR */ + i = 0; + while (i < 5) { + addr = mmap(hint, request_size, map_prot, map_flags, file, 0); + if (addr != MAP_FAILED) + break; + if (errno == EINTR) + continue; + if (errno != EAGAIN && errno != ENOMEM) { + break; + } + i++; + } + } + + if (addr == MAP_FAILED) { + os_printf("mmap failed with errno: %d, hint: %p, size: %" PRIu64 + ", prot: %d, flags: %d\n", + errno, hint, request_size, map_prot, map_flags); + return NULL; + } + +#if BH_ENABLE_TRACE_MMAP != 0 + total_size_mmapped += request_size; + os_printf("mmap return: %p with size: %zu, total_size_mmapped: %zu, " + "total_size_munmapped: %zu\n", + addr, request_size, total_size_mmapped, total_size_munmapped); +#endif + +#if !defined(__APPLE__) && !defined(__NuttX__) && defined(MADV_HUGEPAGE) + /* huge page isn't supported on MacOS and NuttX */ + if (request_size > HUGE_PAGE_SIZE) { + uintptr_t huge_start, huge_end; + size_t prefix_size = 0, suffix_size = HUGE_PAGE_SIZE; + + huge_start = round_up((uintptr_t)addr, HUGE_PAGE_SIZE); + + if (huge_start > (uintptr_t)addr) { + prefix_size += huge_start - (uintptr_t)addr; + suffix_size -= huge_start - (uintptr_t)addr; + } + + /* unmap one extra huge page */ + + if (prefix_size > 0) { + munmap(addr, prefix_size); +#if BH_ENABLE_TRACE_MMAP != 0 + total_size_munmapped += prefix_size; + os_printf("munmap %p with size: %zu, total_size_mmapped: %zu, " + "total_size_munmapped: %zu\n", + addr, prefix_size, total_size_mmapped, + total_size_munmapped); +#endif + } + if (suffix_size > 0) { + munmap(addr + request_size - suffix_size, suffix_size); +#if BH_ENABLE_TRACE_MMAP != 0 + total_size_munmapped += suffix_size; + os_printf("munmap %p with size: %zu, total_size_mmapped: %zu, " + "total_size_munmapped: %zu\n", + addr + request_size - suffix_size, suffix_size, + total_size_mmapped, total_size_munmapped); +#endif + } + + addr = (uint8 *)huge_start; + request_size -= HUGE_PAGE_SIZE; + + huge_end = round_down(huge_start + request_size, HUGE_PAGE_SIZE); + if (huge_end > huge_start) { + int ret = madvise((void *)huge_start, huge_end - huge_start, + MADV_HUGEPAGE); + if (ret) { +#if BH_ENABLE_TRACE_MMAP != 0 + os_printf( + "warning: madvise(%p, %lu) huge page failed, return %d\n", + (void *)huge_start, huge_end - huge_start, ret); +#endif + } + } + } +#endif /* end of __APPLE__ || __NuttX__ || !MADV_HUGEPAGE */ + + return addr; +} + +void +os_munmap(void *addr, size_t size) +{ + uint64 page_size = (uint64)getpagesize(); + uint64 request_size = (size + page_size - 1) & ~(page_size - 1); + + if (addr) { + if (munmap(addr, request_size)) { + os_printf("os_munmap error addr:%p, size:0x%" PRIx64 ", errno:%d\n", + addr, request_size, errno); + return; + } +#if BH_ENABLE_TRACE_MMAP != 0 + total_size_munmapped += request_size; + os_printf("munmap %p with size: %zu, total_size_mmapped: %zu, " + "total_size_munmapped: %zu\n", + addr, request_size, total_size_mmapped, total_size_munmapped); +#endif + } +} + +#if WASM_HAVE_MREMAP != 0 +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size) +{ + void *ptr = mremap(old_addr, old_size, new_size, MREMAP_MAYMOVE); + + if (ptr == MAP_FAILED) { +#if BH_ENABLE_TRACE_MMAP != 0 + os_printf("mremap failed: %d\n", errno); +#endif + return os_mremap_slow(old_addr, old_size, new_size); + } + + return ptr; +} +#endif + +int +os_mprotect(void *addr, size_t size, int prot) +{ + int map_prot = PROT_NONE; + uint64 page_size = (uint64)getpagesize(); + uint64 request_size = (size + page_size - 1) & ~(page_size - 1); + + if (!addr) + return 0; + + if (prot & MMAP_PROT_READ) + map_prot |= PROT_READ; + + if (prot & MMAP_PROT_WRITE) + map_prot |= PROT_WRITE; + + if (prot & MMAP_PROT_EXEC) + map_prot |= PROT_EXEC; + + return mprotect(addr, request_size, map_prot); +} + +void +os_dcache_flush(void) +{} + +void +os_icache_flush(void *start, size_t len) +{ +#if defined(__APPLE__) || defined(__MACH__) + sys_icache_invalidate(start, len); +#else + (void)start; + (void)len; +#endif +} diff --git a/core/shared/platform/common/posix/posix_sleep.c b/core/shared/platform/common/posix/posix_sleep.c new file mode 100644 index 0000000000..fa0645037a --- /dev/null +++ b/core/shared/platform/common/posix/posix_sleep.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "platform_api_extension.h" + +int +os_usleep(uint32 usec) +{ + struct timespec ts; + int ret; + + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + ret = nanosleep(&ts, NULL); + return ret == 0 ? 0 : -1; +} diff --git a/core/shared/platform/common/posix/posix_socket.c b/core/shared/platform/common/posix/posix_socket.c new file mode 100644 index 0000000000..469b2a50c0 --- /dev/null +++ b/core/shared/platform/common/posix/posix_socket.c @@ -0,0 +1,1038 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +#include "libc_errno.h" + +#include +#include +#include +#include + +static bool +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out, + socklen_t *out_len) +{ + struct sockaddr_in *v4; +#ifdef IPPROTO_IPV6 + struct sockaddr_in6 *v6; +#endif + + assert(textual); + + v4 = (struct sockaddr_in *)out; + if (inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) { + v4->sin_family = AF_INET; + v4->sin_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } + +#ifdef IPPROTO_IPV6 + v6 = (struct sockaddr_in6 *)out; + if (inet_pton(AF_INET6, textual, &v6->sin6_addr.s6_addr) == 1) { + v6->sin6_family = AF_INET6; + v6->sin6_port = htons(port); + *out_len = sizeof(struct sockaddr_in6); + return true; + } +#endif + + return false; +} + +static int +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, + bh_sockaddr_t *bh_sockaddr) +{ + switch (sockaddr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + + bh_sockaddr->port = ntohs(addr->sin_port); + bh_sockaddr->addr_buffer.ipv4 = ntohl(addr->sin_addr.s_addr); + bh_sockaddr->is_ipv4 = true; + return BHT_OK; + } +#ifdef IPPROTO_IPV6 + case AF_INET6: + { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + + bh_sockaddr->port = ntohs(addr->sin6_port); + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = addr->sin6_addr.s6_addr[i * 2] + | (addr->sin6_addr.s6_addr[i * 2 + 1] << 8); + bh_sockaddr->addr_buffer.ipv6[i] = ntohs(part_addr); + } + + bh_sockaddr->is_ipv4 = false; + return BHT_OK; + } +#endif + default: + errno = EAFNOSUPPORT; + return BHT_ERROR; + } +} + +static void +bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, + struct sockaddr_storage *sockaddr, socklen_t *socklen) +{ + if (bh_sockaddr->is_ipv4) { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + addr->sin_port = htons(bh_sockaddr->port); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(bh_sockaddr->addr_buffer.ipv4); + *socklen = sizeof(*addr); + } +#ifdef IPPROTO_IPV6 + else { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + addr->sin6_port = htons(bh_sockaddr->port); + addr->sin6_family = AF_INET6; + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = htons(bh_sockaddr->addr_buffer.ipv6[i]); + addr->sin6_addr.s6_addr[i * 2] = 0xff & part_addr; + addr->sin6_addr.s6_addr[i * 2 + 1] = (0xff00 & part_addr) >> 8; + } + + *socklen = sizeof(*addr); + } +#endif +} + +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + int af = is_ipv4 ? AF_INET : AF_INET6; + + if (!sock) { + return BHT_ERROR; + } + + if (is_tcp) { + *sock = socket(af, SOCK_STREAM, IPPROTO_TCP); + } + else { + *sock = socket(af, SOCK_DGRAM, 0); + } + + return (*sock == -1) ? BHT_ERROR : BHT_OK; +} + +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + struct sockaddr_storage addr = { 0 }; + struct linger ling; + socklen_t socklen; + int ret; + + assert(host); + assert(port); + + ling.l_onoff = 1; + ling.l_linger = 0; + + if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr, + &socklen)) { + goto fail; + } + + ret = fcntl(socket, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + goto fail; + } + + ret = setsockopt(socket, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); + if (ret < 0) { + goto fail; + } + + ret = bind(socket, (struct sockaddr *)&addr, socklen); + if (ret < 0) { + goto fail; + } + + socklen = sizeof(addr); + if (getsockname(socket, (void *)&addr, &socklen) == -1) { + 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: + return BHT_ERROR; +} + +int +os_socket_settimeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, + sizeof(tv)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + if (listen(socket, max_client) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) +{ + if (addr == NULL) { + *sock = accept(server_sock, NULL, NULL); + } + else { + socklen_t len = *addrlen; + *sock = accept(server_sock, addr, &len); + *addrlen = len; + } + if (*sock < 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + struct sockaddr_storage addr_in = { 0 }; + socklen_t addr_len; + int ret = 0; + + if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr_in, + &addr_len)) { + return BHT_ERROR; + } + + ret = connect(socket, (struct sockaddr *)&addr_in, addr_len); + if (ret == -1) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + return recv(socket, buf, len, 0); +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = sizeof(sock_addr); + int ret; + + ret = recvfrom(socket, buf, len, flags, (struct sockaddr *)&sock_addr, + &socklen); + + if (ret < 0) { + return ret; + } + + if (src_addr && socklen > 0) { + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, src_addr) + == BHT_ERROR) { + return -1; + } + } + else if (src_addr) { + memset(src_addr, 0, sizeof(*src_addr)); + } + + return ret; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + return send(socket, buf, len, 0); +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = 0; + + bh_sockaddr_to_sockaddr(dest_addr, &sock_addr, &socklen); + + return sendto(socket, buf, len, flags, (const struct sockaddr *)&sock_addr, + socklen); +} + +int +os_socket_close(bh_socket_t socket) +{ + close(socket); + return BHT_OK; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + if (shutdown(socket, O_RDWR) != 0) { + return convert_errno(errno); + } + return __WASI_ESUCCESS; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + if (!cp) + return BHT_ERROR; + + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { +#ifdef IPPROTO_IPV6 + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + + return BHT_OK; +} + +static int +getaddrinfo_error_to_errno(int error) +{ + switch (error) { + case EAI_AGAIN: + return EAGAIN; + case EAI_FAIL: + return EFAULT; + case EAI_MEMORY: + return ENOMEM; + case EAI_SYSTEM: + return errno; + default: + return EINVAL; + } +} + +static int +is_addrinfo_supported(struct addrinfo *info) +{ + return + // Allow only IPv4 and IPv6 + (info->ai_family == AF_INET || info->ai_family == AF_INET6) + // Allow only UDP and TCP + && (info->ai_socktype == SOCK_DGRAM || info->ai_socktype == SOCK_STREAM) + && (info->ai_protocol == IPPROTO_TCP || info->ai_protocol == IPPROTO_UDP + || info->ai_protocol == 0); +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + struct addrinfo hints = { 0 }, *res, *result; + int hints_enabled = hint_is_tcp || hint_is_ipv4; + int ret; + size_t pos = 0; + + if (hints_enabled) { + if (hint_is_ipv4) { + hints.ai_family = *hint_is_ipv4 ? AF_INET : AF_INET6; + } + if (hint_is_tcp) { + hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM; + } + } + + ret = getaddrinfo(host, strlen(service) == 0 ? NULL : service, + hints_enabled ? &hints : NULL, &result); + if (ret != BHT_OK) { + errno = getaddrinfo_error_to_errno(ret); + return BHT_ERROR; + } + + res = result; + while (res) { + if (!is_addrinfo_supported(res)) { + res = res->ai_next; + continue; + } + if (addr_info_size > pos) { + ret = + sockaddr_to_bh_sockaddr(res->ai_addr, &addr_info[pos].sockaddr); + + if (ret == BHT_ERROR) { + freeaddrinfo(result); + return BHT_ERROR; + } + + addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM; + } + + pos++; + res = res->ai_next; + } + + *max_info_size = pos; + freeaddrinfo(result); + + return BHT_OK; +} + +static int +os_socket_setbooloption(bh_socket_t socket, int level, int optname, + bool is_enabled) +{ + int option = (int)is_enabled; + if (setsockopt(socket, level, optname, &option, sizeof(option)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +static int +os_socket_getbooloption(bh_socket_t socket, int level, int optname, + bool *is_enabled) +{ + assert(is_enabled); + + int optval; + socklen_t optval_size = sizeof(optval); + if (getsockopt(socket, level, optname, &optval, &optval_size) != 0) { + return BHT_ERROR; + } + *is_enabled = (bool)optval; + return BHT_OK; +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + if (getsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + if (getsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ +#if defined(SO_REUSEPORT) /* NuttX doesn't have SO_REUSEPORT */ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +#else + errno = ENOTSUP; + return BHT_ERROR; +#endif /* defined(SO_REUSEPORT) */ +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ +#if defined(SO_REUSEPORT) /* NuttX doesn't have SO_REUSEPORT */ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +#else + errno = ENOTSUP; + return BHT_ERROR; +#endif /* defined(SO_REUSEPORT) */ +} + +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + struct linger linger_opts = { .l_onoff = (int)is_enabled, + .l_linger = linger_s }; + if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, + sizeof(linger_opts)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + assert(is_enabled); + assert(linger_s); + + struct linger linger_opts; + socklen_t linger_opts_len = sizeof(linger_opts); + if (getsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, + &linger_opts_len) + != 0) { + return BHT_ERROR; + } + *linger_s = linger_opts.l_linger; + *is_enabled = (bool)linger_opts.l_onoff; + return BHT_OK; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ +#ifdef TCP_QUICKACK + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ +#ifdef TCP_QUICKACK + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) +{ + int time_s_int = (int)time_s; +#ifdef TCP_KEEPIDLE + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) +{ + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); +#ifdef TCP_KEEPIDLE + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) +{ + int time_s_int = (int)time_s; +#ifdef TCP_KEEPINTVL + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) +{ +#ifdef TCP_KEEPINTVL + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ +#ifdef TCP_FASTOPEN_CONNECT + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ +#ifdef TCP_FASTOPEN_CONNECT + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + + return BHT_OK; +} + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + + return BHT_OK; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (setsockopt(socket, IPPROTO_IP, IP_TTL, &ttl_s, sizeof(ttl_s)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + if (getsockopt(socket, IPPROTO_IP, IP_TTL, ttl_s, &opt_len) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl_s, sizeof(ttl_s)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + if (getsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s, &opt_len) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + if (setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + if (getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + return BHT_OK; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + if (getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + return BHT_OK; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = getsockname(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = getpeername(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} \ No newline at end of file diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c new file mode 100644 index 0000000000..80b7d6545e --- /dev/null +++ b/core/shared/platform/common/posix/posix_thread.c @@ -0,0 +1,784 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#if !defined(__RTTHREAD__) +#define _GNU_SOURCE +#endif +#endif +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#if defined(__APPLE__) || defined(__MACH__) +#include +#endif + +typedef struct { + thread_start_routine_t start; + void *arg; +#ifdef OS_ENABLE_HW_BOUND_CHECK + os_signal_handler signal_handler; +#endif +} thread_wrapper_arg; + +#ifdef OS_ENABLE_HW_BOUND_CHECK +/* The signal handler passed to os_thread_signal_init() */ +static os_thread_local_attribute os_signal_handler signal_handler; +#endif + +static void * +os_thread_wrapper(void *arg) +{ + thread_wrapper_arg *targ = arg; + thread_start_routine_t start_func = targ->start; + void *thread_arg = targ->arg; +#ifdef OS_ENABLE_HW_BOUND_CHECK + os_signal_handler handler = targ->signal_handler; +#endif + +#if 0 + os_printf("THREAD CREATED %jx\n", (uintmax_t)(uintptr_t)pthread_self()); +#endif + BH_FREE(targ); +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (os_thread_signal_init(handler) != 0) + return NULL; +#endif +#ifdef OS_ENABLE_WAKEUP_BLOCKING_OP + os_end_blocking_op(); +#endif +#if BH_DEBUG != 0 +#if defined __APPLE__ + pthread_setname_np("wamr"); +#else + pthread_setname_np(pthread_self(), "wamr"); +#endif +#endif + start_func(thread_arg); +#ifdef OS_ENABLE_HW_BOUND_CHECK + os_thread_signal_destroy(); +#endif + return NULL; +} + +int +os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + pthread_attr_t tattr; + thread_wrapper_arg *targ; + + assert(stack_size > 0); + assert(tid); + assert(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\n", + stack_size, (unsigned int)PTHREAD_STACK_MIN); + pthread_attr_destroy(&tattr); + return BHT_ERROR; + } + + targ = (thread_wrapper_arg *)BH_MALLOC(sizeof(*targ)); + if (!targ) { + pthread_attr_destroy(&tattr); + return BHT_ERROR; + } + + targ->start = start; + targ->arg = arg; +#ifdef OS_ENABLE_HW_BOUND_CHECK + targ->signal_handler = signal_handler; +#endif + + if (pthread_create(tid, &tattr, os_thread_wrapper, targ) != 0) { + pthread_attr_destroy(&tattr); + BH_FREE(targ); + return BHT_ERROR; + } + + pthread_attr_destroy(&tattr); + return BHT_OK; +} + +int +os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +korp_tid +os_self_thread() +{ + return (korp_tid)pthread_self(); +} + +int +os_mutex_init(korp_mutex *mutex) +{ + return pthread_mutex_init(mutex, NULL) == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_recursive_mutex_init(korp_mutex *mutex) +{ + int ret; + + pthread_mutexattr_t mattr; + + assert(mutex); + ret = pthread_mutexattr_init(&mattr); + if (ret) + return BHT_ERROR; + + pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); + ret = pthread_mutex_init(mutex, &mattr); + pthread_mutexattr_destroy(&mattr); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_destroy(mutex); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_lock(mutex); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_unlock(mutex); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_cond_init(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_init(cond, NULL) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_destroy(cond) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + assert(cond); + assert(mutex); + + if (pthread_cond_wait(cond, mutex) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +korp_sem * +os_sem_open(const char *name, int oflags, int mode, int val) +{ + return sem_open(name, oflags, mode, val); +} + +int +os_sem_close(korp_sem *sem) +{ + return sem_close(sem); +} + +int +os_sem_wait(korp_sem *sem) +{ + return sem_wait(sem); +} + +int +os_sem_trywait(korp_sem *sem) +{ + return sem_trywait(sem); +} + +int +os_sem_post(korp_sem *sem) +{ + return sem_post(sem); +} + +int +os_sem_getvalue(korp_sem *sem, int *sval) +{ +#if defined(__APPLE__) + /* + * macOS doesn't have working sem_getvalue. + * It's marked as deprecated in the system header. + * Mock it up here to avoid compile-time deprecation warnings. + */ + errno = ENOSYS; + return -1; +#else + return sem_getvalue(sem, sval); +#endif +} + +int +os_sem_unlink(const char *name) +{ + return sem_unlink(name); +} + +static void +msec_nsec_to_abstime(struct timespec *ts, uint64 usec) +{ + struct timeval tv; + time_t tv_sec_new; + long int tv_nsec_new; + + gettimeofday(&tv, NULL); + + tv_sec_new = (time_t)(tv.tv_sec + usec / 1000000); + if (tv_sec_new >= tv.tv_sec) { + ts->tv_sec = tv_sec_new; + } + else { + /* integer overflow */ + ts->tv_sec = BH_TIME_T_MAX; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + + tv_nsec_new = (long int)(tv.tv_usec * 1000 + (usec % 1000000) * 1000); + if (tv.tv_usec * 1000 >= tv.tv_usec && tv_nsec_new >= tv.tv_usec * 1000) { + ts->tv_nsec = tv_nsec_new; + } + else { + /* integer overflow */ + ts->tv_nsec = LONG_MAX; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + + if (ts->tv_nsec >= 1000000000L && ts->tv_sec < BH_TIME_T_MAX) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + int ret; + struct timespec abstime; + + if (useconds == BHT_WAIT_FOREVER) + ret = pthread_cond_wait(cond, mutex); + else { + msec_nsec_to_abstime(&abstime, useconds); + ret = pthread_cond_timedwait(cond, mutex, &abstime); + } + + if (ret != BHT_OK && ret != ETIMEDOUT) + return BHT_ERROR; + + return ret; +} + +int +os_cond_signal(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_signal(cond) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_cond_broadcast(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_broadcast(cond) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_init(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_init(lock, NULL) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_rdlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_wrlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_unlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_destroy(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ + return pthread_join(thread, value_ptr); +} + +int +os_thread_detach(korp_tid thread) +{ + return pthread_detach(thread); +} + +void +os_thread_exit(void *retval) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + os_thread_signal_destroy(); +#endif + return pthread_exit(retval); +} + +#if defined(os_thread_local_attribute) +static os_thread_local_attribute uint8 *thread_stack_boundary = NULL; +#endif + +uint8 * +os_thread_get_stack_boundary() +{ + pthread_t self; +#ifdef __linux__ + pthread_attr_t attr; + size_t guard_size; +#endif + uint8 *addr = NULL; + size_t stack_size, max_stack_size; + int page_size; + +#if defined(os_thread_local_attribute) + if (thread_stack_boundary) + return thread_stack_boundary; +#endif + + page_size = getpagesize(); + self = pthread_self(); + max_stack_size = + (size_t)(APP_THREAD_STACK_SIZE_MAX + page_size - 1) & ~(page_size - 1); + + if (max_stack_size < APP_THREAD_STACK_SIZE_DEFAULT) + max_stack_size = APP_THREAD_STACK_SIZE_DEFAULT; + +#ifdef __linux__ + if (pthread_getattr_np(self, &attr) == 0) { + pthread_attr_getstack(&attr, (void **)&addr, &stack_size); + pthread_attr_getguardsize(&attr, &guard_size); + pthread_attr_destroy(&attr); + if (stack_size > max_stack_size) + addr = addr + stack_size - max_stack_size; + addr += guard_size; + } + (void)stack_size; +#elif defined(__APPLE__) || defined(__NuttX__) || defined(__RTTHREAD__) + if ((addr = (uint8 *)pthread_get_stackaddr_np(self))) { + stack_size = pthread_get_stacksize_np(self); + + /** + * Check whether stack_addr is the base or end of the stack, + * change it to the base if it is the end of stack. + */ + if (addr <= (uint8 *)&stack_size) + addr = addr + stack_size; + + if (stack_size > max_stack_size) + stack_size = max_stack_size; + + addr -= stack_size; + } +#endif + +#if defined(os_thread_local_attribute) + thread_stack_boundary = addr; +#endif + return addr; +} + +void +os_thread_jit_write_protect_np(bool enabled) +{ +#if (defined(__APPLE__) || defined(__MACH__)) && defined(__arm64__) \ + && defined(TARGET_OS_OSX) && TARGET_OS_OSX != 0 + pthread_jit_write_protect_np(enabled); +#endif +} + +#ifdef OS_ENABLE_HW_BOUND_CHECK + +#define SIG_ALT_STACK_SIZE (32 * 1024) + +/** + * Whether thread signal environment is initialized: + * the signal handler is registered, the stack pages are touched, + * the stack guard pages are set and signal alternate stack are set. + */ +static os_thread_local_attribute bool thread_signal_inited = false; + +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 +/* The signal alternate stack base addr */ +static os_thread_local_attribute uint8 *sigalt_stack_base_addr; +/* The previous signal alternate stack */ +static os_thread_local_attribute stack_t prev_sigalt_stack; + +/* + * ASAN is not designed to work with custom stack unwind or other low-level + * things. Ignore a function that does some low-level magic. (e.g. walking + * through the thread's stack bypassing the frame boundaries) + */ +#if defined(__clang__) +#pragma clang optimize off +__attribute__((no_sanitize_address)) +#elif defined(__GNUC__) +#pragma GCC push_options +#pragma GCC optimize("O0") +__attribute__((no_sanitize_address)) +#endif +static uint32 +touch_pages(uint8 *stack_min_addr, uint32 page_size) +{ + uint8 sum = 0; + while (1) { + volatile uint8 *touch_addr = (volatile uint8 *)os_alloca(page_size / 2); + if (touch_addr < stack_min_addr + page_size) { + sum += *(stack_min_addr + page_size - 1); + break; + } + *touch_addr = 0; + sum += *touch_addr; + } + return sum; +} +#if defined(__clang__) +#pragma clang optimize on +#elif defined(__GNUC__) +#pragma GCC pop_options +#endif + +static bool +init_stack_guard_pages() +{ + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + uint8 *stack_min_addr = os_thread_get_stack_boundary(); + + if (stack_min_addr == NULL) + return false; + + /* Touch each stack page to ensure that it has been mapped: the OS + may lazily grow the stack mapping as a guard page is hit. */ + (void)touch_pages(stack_min_addr, page_size); + /* First time to call aot function, protect guard pages */ + if (os_mprotect(stack_min_addr, page_size * guard_page_count, + MMAP_PROT_NONE) + != 0) { + return false; + } + return true; +} + +static void +destroy_stack_guard_pages() +{ + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + uint8 *stack_min_addr = os_thread_get_stack_boundary(); + + os_mprotect(stack_min_addr, page_size * guard_page_count, + MMAP_PROT_READ | MMAP_PROT_WRITE); +} +#endif /* end of WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 */ + +/* + * ASAN is not designed to work with custom stack unwind or other low-level + * things. Ignore a function that does some low-level magic. (e.g. walking + * through the thread's stack bypassing the frame boundaries) + */ +#if defined(__GNUC__) || defined(__clang__) +__attribute__((no_sanitize_address)) +#endif +static void +mask_signals(int how) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGSEGV); + sigaddset(&set, SIGBUS); + pthread_sigmask(how, &set, NULL); +} + +static struct sigaction prev_sig_act_SIGSEGV; +static struct sigaction prev_sig_act_SIGBUS; + +/* + * ASAN is not designed to work with custom stack unwind or other low-level + * things. Ignore a function that does some low-level magic. (e.g. walking + * through the thread's stack bypassing the frame boundaries) + */ +#if defined(__GNUC__) || defined(__clang__) +__attribute__((no_sanitize_address)) +#endif +static void +signal_callback(int sig_num, siginfo_t *sig_info, void *sig_ucontext) +{ + void *sig_addr = sig_info->si_addr; + struct sigaction *prev_sig_act = NULL; + + mask_signals(SIG_BLOCK); + + /* Try to handle signal with the registered signal handler */ + if (signal_handler && (sig_num == SIGSEGV || sig_num == SIGBUS)) { + signal_handler(sig_addr); + } + + if (sig_num == SIGSEGV) + prev_sig_act = &prev_sig_act_SIGSEGV; + else if (sig_num == SIGBUS) + prev_sig_act = &prev_sig_act_SIGBUS; + + /* Forward the signal to next handler if found */ + if (prev_sig_act && (prev_sig_act->sa_flags & SA_SIGINFO)) { + prev_sig_act->sa_sigaction(sig_num, sig_info, sig_ucontext); + } + else if (prev_sig_act + && prev_sig_act->sa_handler + /* Filter out SIG_DFL and SIG_IGN here, they will + run into the else branch below */ + && (void *)prev_sig_act->sa_handler != SIG_DFL + && (void *)prev_sig_act->sa_handler != SIG_IGN) { + prev_sig_act->sa_handler(sig_num); + } + /* Output signal info and then crash if signal is unhandled */ + else { + switch (sig_num) { + case SIGSEGV: + os_printf("unhandled SIGSEGV, si_addr: %p\n", sig_addr); + break; + case SIGBUS: + os_printf("unhandled SIGBUS, si_addr: %p\n", sig_addr); + break; + default: + os_printf("unhandle signal %d, si_addr: %p\n", sig_num, + sig_addr); + break; + } + + abort(); + } +} + +int +os_thread_signal_init(os_signal_handler handler) +{ + struct sigaction sig_act; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + stack_t sigalt_stack_info; + uint32 map_size = SIG_ALT_STACK_SIZE; + uint8 *map_addr; +#endif + + if (thread_signal_inited) + return 0; + +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + if (!init_stack_guard_pages()) { + os_printf("Failed to init stack guard pages\n"); + return -1; + } + + /* Initialize memory for signal alternate stack of current thread */ + if (!(map_addr = os_mmap(NULL, map_size, MMAP_PROT_READ | MMAP_PROT_WRITE, + MMAP_MAP_NONE, os_get_invalid_handle()))) { + os_printf("Failed to mmap memory for alternate stack\n"); + goto fail1; + } + + /* Initialize signal alternate stack */ + memset(map_addr, 0, map_size); + sigalt_stack_info.ss_sp = map_addr; + sigalt_stack_info.ss_size = map_size; + sigalt_stack_info.ss_flags = 0; + memset(&prev_sigalt_stack, 0, sizeof(stack_t)); + /* Set signal alternate stack and save the previous one */ + if (sigaltstack(&sigalt_stack_info, &prev_sigalt_stack) != 0) { + os_printf("Failed to init signal alternate stack\n"); + goto fail2; + } +#endif + + memset(&prev_sig_act_SIGSEGV, 0, sizeof(struct sigaction)); + memset(&prev_sig_act_SIGBUS, 0, sizeof(struct sigaction)); + + /* Install signal handler */ + sig_act.sa_sigaction = signal_callback; + sig_act.sa_flags = SA_SIGINFO | SA_NODEFER; +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + sig_act.sa_flags |= SA_ONSTACK; +#endif + sigemptyset(&sig_act.sa_mask); + if (sigaction(SIGSEGV, &sig_act, &prev_sig_act_SIGSEGV) != 0 + || sigaction(SIGBUS, &sig_act, &prev_sig_act_SIGBUS) != 0) { + os_printf("Failed to register signal handler\n"); + goto fail3; + } + +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + sigalt_stack_base_addr = map_addr; +#endif + +#if defined(os_thread_local_attribute) + // calculate and cache the new stack boundary. + // see https://github.com/bytecodealliance/wasm-micro-runtime/issues/3966 + (void)os_thread_get_stack_boundary(); +#endif + + signal_handler = handler; + thread_signal_inited = true; + return 0; + +fail3: +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + memset(&sigalt_stack_info, 0, sizeof(stack_t)); + sigalt_stack_info.ss_flags = SS_DISABLE; + sigalt_stack_info.ss_size = map_size; + sigaltstack(&sigalt_stack_info, NULL); +fail2: + os_munmap(map_addr, map_size); +fail1: + destroy_stack_guard_pages(); +#endif + return -1; +} + +void +os_thread_signal_destroy() +{ + if (!thread_signal_inited) + return; + +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + /* Restore the previous signal alternate stack */ + sigaltstack(&prev_sigalt_stack, NULL); + + os_munmap(sigalt_stack_base_addr, SIG_ALT_STACK_SIZE); + + destroy_stack_guard_pages(); +#endif + + thread_signal_inited = false; +} + +bool +os_thread_signal_inited() +{ + return thread_signal_inited; +} + +void +os_signal_unmask() +{ + mask_signals(SIG_UNBLOCK); +} + +void +os_sigreturn() +{ +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 +#if defined(__APPLE__) +#define UC_RESET_ALT_STACK 0x80000000 + extern int __sigreturn(void *, int); + + /* It's necessary to call __sigreturn to restore the sigaltstack state + after exiting the signal handler. */ + __sigreturn(NULL, UC_RESET_ALT_STACK); +#endif +#endif +} +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ diff --git a/core/shared/platform/common/posix/posix_time.c b/core/shared/platform/common/posix/posix_time.c new file mode 100644 index 0000000000..8c339aba27 --- /dev/null +++ b/core/shared/platform/common/posix/posix_time.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_us() +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + + return ((uint64)ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +} + +uint64 +os_time_thread_cputime_us() +{ + struct timespec ts; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) { + return 0; + } + + return ((uint64)ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +} \ No newline at end of file diff --git a/core/shared/platform/cosmopolitan/platform_init.c b/core/shared/platform/cosmopolitan/platform_init.c new file mode 100644 index 0000000000..2aae13fa14 --- /dev/null +++ b/core/shared/platform/cosmopolitan/platform_init.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} diff --git a/core/shared/platform/cosmopolitan/platform_internal.h b/core/shared/platform/cosmopolitan/platform_internal.h new file mode 100644 index 0000000000..10de33fe77 --- /dev/null +++ b/core/shared/platform/cosmopolitan/platform_internal.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2023 Dylibso. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_COSMOPOLITAN +#define BH_PLATFORM_COSMOPOLITAN +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define os_thread_local_attribute __thread + +#define bh_socket_t int + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef struct timespec os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#if WASM_DISABLE_WRITE_GS_BASE == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#define os_writegsbase(base_addr) \ + do { \ + uint64 __gs_value = (uint64)(uintptr_t)base_addr; \ + asm volatile("wrgsbase %0" ::"r"(__gs_value) : "memory"); \ + } while (0) +#if 0 +/* _writegsbase_u64 also works, but need to add -mfsgsbase flag for gcc */ +#include +#define os_writegsbase(base_addr) \ + _writegsbase_u64(((uint64)(uintptr_t)base_addr)) +#endif +#endif +#endif + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +typedef void (*os_signal_handler)(void *sig_addr); + +int +os_thread_signal_init(os_signal_handler handler); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +void +os_signal_unmask(); + +void +os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64/RISCV64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#define os_getpagesize getpagesize + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/cosmopolitan/shared_platform.cmake b/core/shared/platform/cosmopolitan/shared_platform.cmake new file mode 100644 index 0000000000..929ecb9e4a --- /dev/null +++ b/core/shared/platform/cosmopolitan/shared_platform.cmake @@ -0,0 +1,19 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# Copyright (C) 2023 Dylibso. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_COSMOPOLITAN) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/darwin/bh_assert.c b/core/shared/platform/darwin/bh_assert.c deleted file mode 100644 index c609fb93d4..0000000000 --- a/core/shared/platform/darwin/bh_assert.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_assert.h" -#include -#include -#include - -#ifdef BH_TEST -#include -#endif - -#ifdef BH_TEST -/* for exception throwing */ -jmp_buf bh_test_jb; -#endif - -void bh_assert_internal(int v, const char *file_name, int line_number, - const char *expr_string) -{ - if (v) - return; - - if (!file_name) - file_name = "NULL FILENAME"; - if (!expr_string) - expr_string = "NULL EXPR_STRING"; - - printf("\nASSERTION FAILED: %s, at FILE=%s, LINE=%d\n", expr_string, - file_name, line_number); - -#ifdef BH_TEST - longjmp(bh_test_jb, 1); -#endif - - abort(); -} - -void bh_debug_internal(const char *file_name, int line_number, const char *fmt, - ...) -{ -#ifndef JEFF_TEST_VERIFIER - va_list args; - - va_start(args, fmt); - bh_assert(file_name); - - printf("\nDebug info FILE=%s, LINE=%d: ", file_name, line_number); - vprintf(fmt, args); - - va_end(args); - printf("\n"); -#endif -} - diff --git a/core/shared/platform/darwin/bh_definition.c b/core/shared/platform/darwin/bh_definition.c deleted file mode 100644 index 8fb58d2f84..0000000000 --- a/core/shared/platform/darwin/bh_definition.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" - -#define RSIZE_MAX 0x7FFFFFFF - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n) -{ - char *dest = (char*) s1; - char *src = (char*) s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} - -int b_strcat_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s1) + strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcat(s1, s2); - - return 0; -} - -int b_strcpy_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcpy(s1, s2); - - return 0; -} - -int fopen_s(FILE ** pFile, const char *filename, const char *mode) -{ - if (NULL == pFile || NULL == filename || NULL == mode) { - return -1; - } - - *pFile = fopen(filename, mode); - - if (NULL == *pFile) - return -1; - - return 0; -} diff --git a/core/shared/platform/darwin/bh_platform.c b/core/shared/platform/darwin/bh_platform.c deleted file mode 100755 index 4eda405b07..0000000000 --- a/core/shared/platform/darwin/bh_platform.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_common.h" -#include "bh_assert.h" - -#include -#include -#include -#include - -char *bh_strdup(const char *s) -{ - uint32 size; - char *s1 = NULL; - - if (s) { - size = (uint32)(strlen(s) + 1); - if ((s1 = bh_malloc(size))) - bh_memcpy_s(s1, size, s, size); - } - return s1; -} - -int bh_platform_init() -{ - return 0; -} - -char* -bh_read_file_to_buffer(const char *filename, uint32 *ret_size) -{ - char *buffer; - int file; - uint32 file_size, read_size; - struct stat stat_buf; - - if (!filename || !ret_size) { - printf("Read file to buffer failed: invalid filename or ret size.\n"); - return NULL; - } - - if ((file = open(filename, O_RDONLY, 0)) == -1) { - printf("Read file to buffer failed: open file %s failed.\n", - filename); - return NULL; - } - - if (fstat(file, &stat_buf) != 0) { - printf("Read file to buffer failed: fstat file %s failed.\n", - filename); - close(file); - return NULL; - } - - file_size = (uint32)stat_buf.st_size; - - if (!(buffer = bh_malloc(file_size))) { - printf("Read file to buffer failed: alloc memory failed.\n"); - close(file); - return NULL; - } - - read_size = (uint32)read(file, buffer, file_size); - close(file); - - if (read_size < file_size) { - printf("Read file to buffer failed: read file content failed.\n"); - bh_free(buffer); - return NULL; - } - - *ret_size = file_size; - return buffer; -} - -void * -bh_mmap(void *hint, uint32 size, int prot, int flags) -{ - int map_prot = PROT_NONE; - int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; - uint64 request_size, page_size; - uint8 *addr, *addr_aligned; - uint32 i; - - /* align to 2M if no less than 2M, else align to 4K */ - page_size = size < 2 * 1024 * 1024 ? 4096 : 2 * 1024 * 1024; - request_size = (size + page_size - 1) & ~(page_size - 1); - request_size += page_size; - - if (request_size >= UINT32_MAX) - return NULL; - - if (prot & MMAP_PROT_READ) - map_prot |= PROT_READ; - - if (prot & MMAP_PROT_WRITE) - map_prot |= PROT_WRITE; - - if (prot & MMAP_PROT_EXEC) - map_prot |= PROT_EXEC; - -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - if (flags & MMAP_MAP_32BIT) - map_flags |= MAP_32BIT; -#endif - - if (flags & MMAP_MAP_FIXED) - map_flags |= MAP_FIXED; - - /* try 5 times */ - for (i = 0; i < 5; i ++) { - addr = mmap(hint, size, map_prot, map_flags, -1, 0); - if (addr != MAP_FAILED) - break; - } - if (addr == MAP_FAILED) - return NULL; - - addr_aligned = (uint8*)(uintptr_t) - (((uint64)(uintptr_t)addr + page_size - 1) & ~(page_size - 1)); - - /* Unmap memory allocated before the aligned base address */ - if (addr != addr_aligned) { - uint32 prefix_size = (uint32)(addr_aligned - addr); - munmap(addr, prefix_size); - request_size -= prefix_size; - } - - /* Unmap memory allocated after the potentially unaligned end */ - if (size != request_size) { - uint32 suffix_size = (uint32)(request_size - size); - munmap(addr_aligned + size, suffix_size); - request_size -= size; - } - - if (size >= 2 * 1024 * 1024) { - /* Try to use huge page to improve performance */ - if (!madvise(addr, size, MADV_HUGEPAGE)) - /* make huge page become effective */ - memset(addr, 0, size); - } - - return addr_aligned; -} - -void -bh_munmap(void *addr, uint32 size) -{ - if (addr) - munmap(addr, size); -} - -int -bh_mprotect(void *addr, uint32 size, int prot) -{ - int map_prot = PROT_NONE; - - if (!addr) - return 0; - - if (prot & MMAP_PROT_READ) - map_prot |= PROT_READ; - - if (prot & MMAP_PROT_WRITE) - map_prot |= PROT_WRITE; - - if (prot & MMAP_PROT_EXEC) - map_prot |= PROT_EXEC; - - return mprotect(addr, size, map_prot); -} - diff --git a/core/shared/platform/darwin/bh_platform.h b/core/shared/platform/darwin/bh_platform.h deleted file mode 100644 index 07ec9b9572..0000000000 --- a/core/shared/platform/darwin/bh_platform.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_PLATFORM_H -#define _BH_PLATFORM_H - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_memory.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef uint64_t uint64; -typedef int64_t int64; - -extern void DEBUGME(void); - -#define DIE do{bh_debug("Die here\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); DEBUGME(void); while(1);}while(0) - -#ifndef BH_PLATFORM_DARWIN -#define BH_PLATFORM_DARWIN -#endif - -/* NEED qsort */ - -#define _STACK_SIZE_ADJUSTMENT (32 * 1024) - -/* Stack size of applet threads's native part. */ -#define BH_APPLET_PRESERVED_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT) - -/* Default thread priority */ -#define BH_THREAD_DEFAULT_PRIORITY 0 - -#define BH_ROUTINE_MODIFIER - -#define BHT_TIMEDOUT ETIMEDOUT - -#define INVALID_THREAD_ID 0xFFffFFff - -typedef pthread_t korp_tid; -typedef pthread_mutex_t korp_mutex; -typedef sem_t korp_sem; -typedef pthread_cond_t korp_cond; -typedef pthread_t korp_thread; -typedef void* (*thread_start_routine_t)(void*); - -#define wa_malloc bh_malloc -#define wa_free bh_free -#define wa_strdup bh_strdup - -#define bh_printf printf - -/* int snprintf(char *buffer, size_t count, const char *format, ...); */ -double fmod(double x, double y); -float fmodf(float x, float y); -double sqrt(double x); - -#define BH_WAIT_FOREVER 0xFFFFFFFF - -#ifndef NULL -# define NULL ((void*) 0) -#endif - -/** - * Return the offset of the given field in the given type. - * - * @param Type the type containing the filed - * @param field the field in the type - * - * @return the offset of field in Type - */ -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) -#endif - -#define bh_assert assert - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, - unsigned int n); -int b_strcat_s(char * s1, size_t s1max, const char * s2); -int b_strcpy_s(char * s1, size_t s1max, const char * s2); - -int fopen_s(FILE ** pFile, const char *filename, const char *mode); - -char *bh_read_file_to_buffer(const char *filename, uint32 *ret_size); - -char *bh_strdup(const char *s); - -int bh_platform_init(); - -/* MMAP mode */ -enum { - MMAP_PROT_NONE = 0, - MMAP_PROT_READ = 1, - MMAP_PROT_WRITE = 2, - MMAP_PROT_EXEC = 4 -}; - -/* MMAP flags */ -enum { - MMAP_MAP_NONE = 0, - /* Put the mapping into 0 to 2 G, supported only on x86_64 */ - MMAP_MAP_32BIT = 1, - /* Don't interpret addr as a hint: place the mapping at exactly - that address. */ - MMAP_MAP_FIXED = 2 -}; - -void *bh_mmap(void *hint, unsigned int size, int prot, int flags); -void bh_munmap(void *addr, uint32 size); -int bh_mprotect(void *addr, uint32 size, int prot); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/shared/platform/darwin/bh_platform_log.c b/core/shared/platform/darwin/bh_platform_log.c deleted file mode 100644 index 902ca7d60b..0000000000 --- a/core/shared/platform/darwin/bh_platform_log.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include - -void bh_log_emit(const char *fmt, va_list ap) -{ - vprintf(fmt, ap); - fflush(stdout); -} - -int bh_fprintf(FILE *stream, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = vfprintf(stream ? stream : stdout, fmt, ap); - va_end(ap); - - return ret; -} - -int bh_fflush(void *stream) -{ - return fflush(stream ? stream : stdout); -} diff --git a/core/shared/platform/darwin/bh_thread.c b/core/shared/platform/darwin/bh_thread.c deleted file mode 100644 index abf7368062..0000000000 --- a/core/shared/platform/darwin/bh_thread.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_thread.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include -#include -#include - -static bool is_thread_sys_inited = false; - -static korp_mutex thread_list_lock; -static pthread_key_t thread_local_storage_key[BH_MAX_TLS_NUM]; - -int _vm_thread_sys_init() -{ - unsigned i, j; - int ret; - - if (is_thread_sys_inited) - return 0; - - for (i = 0; i < BH_MAX_TLS_NUM; i++) { - ret = pthread_key_create(&thread_local_storage_key[i], NULL); - if (ret) - goto fail; - } - - ret = vm_mutex_init(&thread_list_lock); - if (ret) - goto fail; - - is_thread_sys_inited = true; - return 0; - - fail: for (j = 0; j < i; j++) - pthread_key_delete(thread_local_storage_key[j]); - return -1; -} - -void vm_thread_sys_destroy(void) -{ - if (is_thread_sys_inited) { - unsigned i; - for (i = 0; i < BH_MAX_TLS_NUM; i++) - pthread_key_delete(thread_local_storage_key[i]); - vm_mutex_destroy(&thread_list_lock); - is_thread_sys_inited = false; - } -} - -typedef struct { - thread_start_routine_t start; - void* stack; - uint32 stack_size; - void* arg; -} thread_wrapper_arg; - -static void *vm_thread_wrapper(void *arg) -{ - thread_wrapper_arg * targ = arg; - LOG_VERBOSE("THREAD CREATE 0x%08x\n", &targ); - targ->stack = (void *)((uintptr_t)(&arg) & (uintptr_t)~0xfff); - _vm_tls_put(1, targ); - targ->start(targ->arg); - bh_free(targ); - _vm_tls_put(1, NULL); - return NULL; -} - -int _vm_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio) -{ - pthread_attr_t tattr; - thread_wrapper_arg *targ; - - bh_assert(stack_size > 0); - bh_assert(tid); - bh_assert(start); - - *tid = INVALID_THREAD_ID; - - pthread_attr_init(&tattr); - pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); - if (pthread_attr_setstacksize(&tattr, stack_size) != 0) { - bh_debug("Invalid thread stack size %u. Min stack size on Linux = %u", - stack_size, PTHREAD_STACK_MIN); - pthread_attr_destroy(&tattr); - return BHT_ERROR; - } - - targ = (thread_wrapper_arg*) bh_malloc(sizeof(*targ)); - if (!targ) { - pthread_attr_destroy(&tattr); - return BHT_ERROR; - } - - targ->start = start; - targ->arg = arg; - targ->stack_size = stack_size; - - if (pthread_create(tid, &tattr, vm_thread_wrapper, targ) != 0) { - pthread_attr_destroy(&tattr); - bh_free(targ); - return BHT_ERROR; - } - - pthread_attr_destroy(&tattr); - return BHT_OK; -} - -int _vm_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, - unsigned int stack_size) -{ - return _vm_thread_create_with_prio(tid, start, arg, stack_size, - BH_THREAD_DEFAULT_PRIORITY); -} - -korp_tid _vm_self_thread() -{ - return (korp_tid) pthread_self(); -} - -void vm_thread_exit(void * code) -{ - bh_free(_vm_tls_get(1)); - _vm_tls_put(1, NULL); - pthread_exit(code); -} - -void *_vm_tls_get(unsigned idx) -{ - bh_assert(idx < BH_MAX_TLS_NUM); - return pthread_getspecific(thread_local_storage_key[idx]); -} - -int _vm_tls_put(unsigned idx, void * tls) -{ - bh_assert(idx < BH_MAX_TLS_NUM); - pthread_setspecific(thread_local_storage_key[idx], tls); - return BHT_OK; -} - -int _vm_mutex_init(korp_mutex *mutex) -{ - return pthread_mutex_init(mutex, NULL) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_recursive_mutex_init(korp_mutex *mutex) -{ - int ret; - - pthread_mutexattr_t mattr; - - bh_assert(mutex); - ret = pthread_mutexattr_init(&mattr); - if (ret) - return BHT_ERROR; - - pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); - ret = pthread_mutex_init(mutex, &mattr); - pthread_mutexattr_destroy(&mattr); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_mutex_destroy(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_destroy(mutex); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/* Returned error (EINVAL, EAGAIN and EDEADLK) from - locking the mutex indicates some logic error present in - the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_lock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_lock(mutex); - if (0 != ret) { - printf("vm mutex lock failed (ret=%d)!\n", ret); - exit(-1); - } -} - -int vm_mutex_trylock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_trylock(mutex); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/* Returned error (EINVAL, EAGAIN and EPERM) from - unlocking the mutex indicates some logic error present - in the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_unlock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_unlock(mutex); - if (0 != ret) { - printf("vm mutex unlock failed (ret=%d)!\n", ret); - exit(-1); - } -} - -int _vm_sem_init(korp_sem* sem, unsigned int c) -{ - int ret; - - bh_assert(sem); - ret = sem_init(sem, 0, c); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_destroy(korp_sem *sem) -{ - int ret; - - bh_assert(sem); - ret = sem_destroy(sem); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_wait(korp_sem *sem) -{ - int ret; - - bh_assert(sem); - - ret = sem_wait(sem); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/*int _vm_sem_reltimedwait(korp_sem *sem, int mills) -{ - int ret = BHT_OK; - - struct timespec timeout; - const int mills_per_sec = 1000; - const int mills_to_nsec = 1E6; - - bh_assert(sem); - - if (mills == (int)BHT_WAIT_FOREVER) { - ret = sem_wait(sem); - } else { - - timeout.tv_sec = mills / mills_per_sec; - timeout.tv_nsec = (mills % mills_per_sec) * mills_to_nsec; - timeout.tv_sec += time(NULL); - - ret = sem_timedwait(sem, &timeout); - } - - if (ret != BHT_OK) { - if (errno == BHT_TIMEDOUT) { - ret = BHT_TIMEDOUT; - errno = 0; - } else { - bh_debug("Faliure happens when timed wait is called"); - bh_assert(0); - } - } - - return ret; -} -*/ - -int _vm_sem_post(korp_sem *sem) -{ - bh_assert(sem); - - return sem_post(sem) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_cond_init(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_init(cond, NULL) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_destroy(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_destroy(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex) -{ - bh_assert(cond); - bh_assert(mutex); - - if (pthread_cond_wait(cond, mutex) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -static void msec_nsec_to_abstime(struct timespec *ts, int64 msec, int32 nsec) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - ts->tv_sec = (long int)(tv.tv_sec + msec / 1000); - ts->tv_nsec = (long int)(tv.tv_usec * 1000 + (msec % 1000) * 1000000 + nsec); - - if (ts->tv_nsec >= 1000000000L) { - ts->tv_sec++; - ts->tv_nsec -= 1000000000L; - } -} - -int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) -{ - int ret; - struct timespec abstime; - - if (mills == (int)BHT_WAIT_FOREVER) - ret = pthread_cond_wait(cond, mutex); - else { - msec_nsec_to_abstime(&abstime, mills, 0); - ret = pthread_cond_timedwait(cond, mutex, &abstime); - } - - if (ret != BHT_OK && ret != BHT_TIMEDOUT) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_signal(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_signal(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_broadcast(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_broadcast(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_thread_cancel(korp_tid thread) -{ - return pthread_cancel(thread); -} - -int _vm_thread_join(korp_tid thread, void **value_ptr, int mills) -{ - return pthread_join(thread, value_ptr); -} - -int _vm_thread_detach(korp_tid thread) -{ - return pthread_detach(thread); -} - diff --git a/core/shared/platform/darwin/bh_time.c b/core/shared/platform/darwin/bh_time.c deleted file mode 100644 index 698cdec24f..0000000000 --- a/core/shared/platform/darwin/bh_time.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_time.h" - -#include -#include -#include -#include - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -uint64 _bh_time_get_tick_millisecond() -{ - return (uint64)sysconf(_SC_CLK_TCK); -} - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -uint64 _bh_time_get_boot_millisecond() -{ - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - - return ((uint64) ts.tv_sec) * 1000 + ((uint64)ts.tv_nsec) / (1000 * 1000); -} - -uint32 bh_get_tick_sec() -{ - return (uint32)(_bh_time_get_boot_millisecond() / 1000); -} - -/* - * This function returns GMT time milliseconds since from 1970.1.1, AKA UNIX time. - * @return milliseconds since from 1970.1.1. - */ -uint64 _bh_time_get_millisecond_from_1970() -{ - struct timeb tp; - ftime(&tp); - - return ((uint64) tp.time) * 1000 + tp.millitm - - (tp.dstflag == 0 ? 0 : 60 * 60 * 1000) - + ((uint64)tp.timezone) * 60 * 1000; -} - -size_t _bh_time_strftime(char *s, size_t max, const char *format, int64 time) -{ - time_t time_sec = (time_t)(time / 1000); - struct timeb tp; - struct tm *ltp; - - ftime(&tp); - time_sec -= tp.timezone * 60; - - ltp = localtime(&time_sec); - if (ltp == NULL) { - return 0; - } - return strftime(s, max, format, ltp); -} - diff --git a/core/shared/platform/darwin/platform_init.c b/core/shared/platform/darwin/platform_init.c new file mode 100644 index 0000000000..2aae13fa14 --- /dev/null +++ b/core/shared/platform/darwin/platform_init.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} diff --git a/core/shared/platform/darwin/platform_internal.h b/core/shared/platform/darwin/platform_internal.h new file mode 100644 index 0000000000..9446866224 --- /dev/null +++ b/core/shared/platform/darwin/platform_internal.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_DARWIN +#define BH_PLATFORM_DARWIN +#endif + +#define BH_HAS_DLFCN 1 + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define os_thread_local_attribute __thread + +#define bh_socket_t int + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +typedef void (*os_signal_handler)(void *sig_addr); + +int +os_thread_signal_init(os_signal_handler handler); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +void +os_signal_unmask(); + +void +os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64/RISCV64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#define os_getpagesize getpagesize + +#if WASM_DISABLE_WAKEUP_BLOCKING_OP == 0 +#define OS_ENABLE_WAKEUP_BLOCKING_OP +#endif +void +os_set_signal_number_for_blocking_op(int signo); + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef struct timespec os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/darwin/shared_platform.cmake b/core/shared/platform/darwin/shared_platform.cmake index 2fdc1330e3..3680f3d3ad 100644 --- a/core/shared/platform/darwin/shared_platform.cmake +++ b/core/shared/platform/darwin/shared_platform.cmake @@ -3,11 +3,19 @@ set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) +add_definitions(-DBH_PLATFORM_DARWIN) + include_directories(${PLATFORM_SHARED_DIR}) include_directories(${PLATFORM_SHARED_DIR}/../include) +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) -set (PLATFORM_SHARED_SOURCE ${source_all}) +include (${CMAKE_CURRENT_LIST_DIR}/../common/memory/platform_api_memory.cmake) +set (source_all ${source_all} ${PLATFORM_COMMON_MEMORY_SOURCE}) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/ego/platform_init.c b/core/shared/platform/ego/platform_init.c new file mode 100644 index 0000000000..38a0e80492 --- /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 0000000000..1ece346be6 --- /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 0000000000..9b84c58414 --- /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_clock.c b/core/shared/platform/esp-idf/espidf_clock.c new file mode 100644 index 0000000000..41413211cb --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_clock.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "libc_errno.h" +#include "platform_api_extension.h" + +#define NANOSECONDS_PER_SECOND 1000000000ULL + +static __wasi_errno_t +wasi_clockid_to_clockid(__wasi_clockid_t in, clockid_t *out) +{ + switch (in) { + case __WASI_CLOCK_MONOTONIC: + *out = CLOCK_MONOTONIC; + return __WASI_ESUCCESS; + case __WASI_CLOCK_REALTIME: + *out = CLOCK_REALTIME; + return __WASI_ESUCCESS; + case __WASI_CLOCK_PROCESS_CPUTIME_ID: +#if defined(CLOCK_PROCESS_CPUTIME_ID) + *out = CLOCK_PROCESS_CPUTIME_ID; + return __WASI_ESUCCESS; +#else + return __WASI_ENOTSUP; +#endif + case __WASI_CLOCK_THREAD_CPUTIME_ID: +#if defined(CLOCK_THREAD_CPUTIME_ID) + *out = CLOCK_THREAD_CPUTIME_ID; + return __WASI_ESUCCESS; +#else + return __WASI_ENOTSUP; +#endif + default: + return __WASI_EINVAL; + } +} + +static __wasi_timestamp_t +timespec_to_nanoseconds(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / NANOSECONDS_PER_SECOND) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * NANOSECONDS_PER_SECOND + + (__wasi_timestamp_t)ts->tv_nsec; +} + +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution) +{ + clockid_t nclock_id; + __wasi_errno_t error = wasi_clockid_to_clockid(clock_id, &nclock_id); + + if (error != __WASI_ESUCCESS) + return error; + + struct timespec ts; + if (clock_getres(nclock_id, &ts) < 0) + return convert_errno(errno); + + *resolution = timespec_to_nanoseconds(&ts); + + return error; +} + +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time) +{ + clockid_t nclock_id; + __wasi_errno_t error = wasi_clockid_to_clockid(clock_id, &nclock_id); + + (void)precision; + + if (error != __WASI_ESUCCESS) + return error; + + struct timespec ts; + if (clock_gettime(nclock_id, &ts) < 0) + return convert_errno(errno); + + *time = timespec_to_nanoseconds(&ts); + + return error; +} diff --git a/core/shared/platform/esp-idf/espidf_file.c b/core/shared/platform/esp-idf/espidf_file.c new file mode 100644 index 0000000000..04d31cb479 --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_file.c @@ -0,0 +1,1053 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "libc_errno.h" +#include + +#if !defined(__APPLE__) && !defined(ESP_PLATFORM) +#define CONFIG_HAS_PWRITEV 1 +#define CONFIG_HAS_PREADV 1 +#else +#define CONFIG_HAS_PWRITEV 0 +#define CONFIG_HAS_PREADV 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(ESP_PLATFORM) +#define CONFIG_HAS_FDATASYNC 1 +#else +#define CONFIG_HAS_FDATASYNC 0 +#endif + +/* + * For NuttX, CONFIG_HAS_ISATTY is provided by its platform header. + * (platform_internal.h) + */ +#if !defined(CONFIG_HAS_D_INO) +#if !defined(__NuttX__) +#define CONFIG_HAS_D_INO 1 +#define CONFIG_HAS_ISATTY 1 +#else +#define CONFIG_HAS_D_INO 0 +#endif +#endif + +#if !defined(__APPLE__) && !defined(ESP_PLATFORM) && !defined(__COSMOPOLITAN__) +#define CONFIG_HAS_POSIX_FALLOCATE 1 +#else +#define CONFIG_HAS_POSIX_FALLOCATE 0 +#endif + +#if defined(O_DSYNC) +#define CONFIG_HAS_O_DSYNC +#endif + +// POSIX requires O_RSYNC to be defined, but Linux explicitly doesn't support +// it. +#if defined(O_RSYNC) && !defined(__linux__) +#define CONFIG_HAS_O_RSYNC +#endif + +#if defined(O_SYNC) +#define CONFIG_HAS_O_SYNC +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +// Converts a POSIX timespec to a WASI timestamp. +static __wasi_timestamp_t +convert_timespec(const struct timespec *ts) +{ + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + + (__wasi_timestamp_t)ts->tv_nsec; +} + +// Converts a POSIX stat structure to a WASI filestat structure +static void +convert_stat(os_file_handle handle, const struct stat *in, + __wasi_filestat_t *out) +{ + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_nlink = (__wasi_linkcount_t)in->st_nlink; + out->st_size = (__wasi_filesize_t)in->st_size; +#ifdef __APPLE__ + out->st_atim = convert_timespec(&in->st_atimespec); + out->st_mtim = convert_timespec(&in->st_mtimespec); + out->st_ctim = convert_timespec(&in->st_ctimespec); +#else + out->st_atim = convert_timespec(&in->st_atim); + out->st_mtim = convert_timespec(&in->st_mtim); + out->st_ctim = convert_timespec(&in->st_ctim); +#endif + + // Convert the file type. In the case of sockets there is no way we + // can easily determine the exact socket type. + if (S_ISBLK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + } + else if (S_ISCHR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + } + else if (S_ISDIR(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_DIRECTORY; + } + else if (S_ISFIFO(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + } + else if (S_ISLNK(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; + } + else if (S_ISREG(in->st_mode)) { + out->st_filetype = __WASI_FILETYPE_REGULAR_FILE; + } + else if (S_ISSOCK(in->st_mode)) { + int socktype; + socklen_t socktypelen = sizeof(socktype); + + if (getsockopt(handle, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) + < 0) { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + + switch (socktype) { + case SOCK_DGRAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + out->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + return; + } + } + else { + out->st_filetype = __WASI_FILETYPE_UNKNOWN; + } +} + +static void +convert_timestamp(__wasi_timestamp_t in, struct timespec *out) +{ + // Store sub-second remainder. +#if defined(__SYSCALL_SLONG_TYPE) + out->tv_nsec = (__SYSCALL_SLONG_TYPE)(in % 1000000000); +#else + out->tv_nsec = (long)(in % 1000000000); +#endif + in /= 1000000000; + + // Clamp to the maximum in case it would overflow our system's 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 +// futimens() and utimensat(). +static void +convert_utimens_arguments(__wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags, struct timespec *ts) +{ + if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { + ts[0].tv_nsec = UTIME_NOW; + } + else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { + convert_timestamp(st_atim, &ts[0]); + } + else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { + ts[1].tv_nsec = UTIME_NOW; + } + else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { + convert_timestamp(st_mtim, &ts[1]); + } + else { + ts[1].tv_nsec = UTIME_OMIT; + } +} + +__wasi_errno_t +os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) +{ + struct stat stat_buf; + int ret = fstat(handle, &stat_buf); + + if (ret < 0) + return convert_errno(errno); + + convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fstatat(os_file_handle handle, const char *path, + struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags) +{ + struct stat stat_buf; + int ret = fstatat(handle, path, &stat_buf, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? AT_SYMLINK_FOLLOW + : AT_SYMLINK_NOFOLLOW); + + if (ret < 0) + return convert_errno(errno); + + convert_stat(handle, &stat_buf, buf); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags) +{ + int ret = fcntl(handle, F_GETFL); + + if (ret < 0) + return convert_errno(errno); + + *flags = 0; + + if ((ret & O_APPEND) != 0) + *flags |= __WASI_FDFLAG_APPEND; +#ifdef CONFIG_HAS_O_DSYNC + if ((ret & O_DSYNC) != 0) + *flags |= __WASI_FDFLAG_DSYNC; +#endif + if ((ret & O_NONBLOCK) != 0) + *flags |= __WASI_FDFLAG_NONBLOCK; +#ifdef CONFIG_HAS_O_RSYNC + if ((ret & O_RSYNC) != 0) + *flags |= __WASI_FDFLAG_RSYNC; +#endif +#ifdef CONFIG_HAS_O_SYNC + if ((ret & O_SYNC) != 0) + *flags |= __WASI_FDFLAG_SYNC; +#endif + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) +{ + int fcntl_flags = 0; + + if ((flags & __WASI_FDFLAG_APPEND) != 0) + fcntl_flags |= O_APPEND; + if ((flags & __WASI_FDFLAG_DSYNC) != 0) +#ifdef CONFIG_HAS_O_DSYNC + fcntl_flags |= O_DSYNC; +#else + return __WASI_ENOTSUP; +#endif + if ((flags & __WASI_FDFLAG_NONBLOCK) != 0) + fcntl_flags |= O_NONBLOCK; + if ((flags & __WASI_FDFLAG_RSYNC) != 0) +#ifdef CONFIG_HAS_O_RSYNC + fcntl_flags |= O_RSYNC; +#else + return __WASI_ENOTSUP; +#endif + if ((flags & __WASI_FDFLAG_SYNC) != 0) +#ifdef CONFIG_HAS_O_SYNC + fcntl_flags |= O_SYNC; +#else + return __WASI_ENOTSUP; +#endif + + int ret = fcntl(handle, F_SETFL, fcntl_flags); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fdatasync(os_file_handle handle) +{ +#if CONFIG_HAS_FDATASYNC + int ret = fdatasync(handle); +#else + int ret = fsync(handle); +#endif + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fsync(os_file_handle handle) +{ + int ret = fsync(handle); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_open_preopendir(const char *path, os_file_handle *out) +{ + + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + + if (fd < 0) + return convert_errno(errno); + + *out = fd; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode read_write_mode, os_file_handle *out) +{ + int open_flags = 0; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + open_flags |= O_CREAT; + } + if ((oflags & __WASI_O_DIRECTORY) != 0) + open_flags |= O_DIRECTORY; + if ((oflags & __WASI_O_EXCL) != 0) + open_flags |= O_EXCL; + if ((oflags & __WASI_O_TRUNC) != 0) { + open_flags |= O_TRUNC; + } + + // Convert file descriptor flags. + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + open_flags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { +#ifdef CONFIG_HAS_O_DSYNC + open_flags |= O_DSYNC; +#else + return __WASI_ENOTSUP; +#endif + } + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + open_flags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { +#ifdef CONFIG_HAS_O_RSYNC + open_flags |= O_RSYNC; +#else + return __WASI_ENOTSUP; +#endif + } + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { +#ifdef CONFIG_HAS_O_SYNC + open_flags |= O_SYNC; +#else + return __WASI_ENOTSUP; +#endif + } + + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) { + open_flags |= O_NOFOLLOW; + } + + switch (read_write_mode) { + case WASI_LIBC_ACCESS_MODE_READ_WRITE: + open_flags |= O_RDWR; + break; + case WASI_LIBC_ACCESS_MODE_READ_ONLY: + open_flags |= O_RDONLY; + break; + case WASI_LIBC_ACCESS_MODE_WRITE_ONLY: + open_flags |= O_WRONLY; + break; + default: + return __WASI_EINVAL; + } + + int fd = openat(handle, path, open_flags, 0666); + + if (fd < 0) { + int openat_errno = errno; + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket. + if (openat_errno == ENXIO) { + struct stat sb; + int ret = fstatat(handle, path, &sb, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? 0 + : AT_SYMLINK_NOFOLLOW); + return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP + : __WASI_ENXIO; + } + // Linux returns ENOTDIR instead of ELOOP when using + // O_NOFOLLOW|O_DIRECTORY on a symlink. + if (openat_errno == ENOTDIR + && (open_flags & (O_NOFOLLOW | O_DIRECTORY)) != 0) { + struct stat sb; + int ret = fstatat(handle, path, &sb, AT_SYMLINK_NOFOLLOW); + if (S_ISLNK(sb.st_mode)) { + return __WASI_ELOOP; + } + (void)ret; + } + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0 + && openat_errno == EMLINK) + return __WASI_ELOOP; + + return convert_errno(openat_errno); + } + + *out = fd; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_access_mode(os_file_handle handle, + wasi_libc_file_access_mode *access_mode) +{ + int ret = fcntl(handle, F_GETFL, 0); + + if (ret < 0) + return convert_errno(errno); + + switch (ret & O_ACCMODE) { + case O_RDONLY: + *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY; + break; + case O_WRONLY: + *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY; + break; + case O_RDWR: + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + break; + default: + return __WASI_EINVAL; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_close(os_file_handle handle, bool is_stdio) +{ + if (is_stdio) + return __WASI_ESUCCESS; + + int ret = close(handle); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ +#if CONFIG_HAS_PREADV + ssize_t len = + preadv(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset); + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len; + return __WASI_ESUCCESS; +#else + if (iovcnt == 1) { + ssize_t len = pread(handle, iov->buf, iov->buf_len, offset); + + if (len < 0) + return convert_errno(errno); + + *nread = len; + return __WASI_ESUCCESS; + } + + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (int i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + + char *buf = BH_MALLOC(totalsize); + + if (buf == NULL) { + return __WASI_ENOMEM; + } + + // Perform a single read operation. + ssize_t len = pread(handle, buf, totalsize, offset); + + if (len < 0) { + BH_FREE(buf); + return convert_errno(errno); + } + + // Copy data back to vectors. + size_t bufoff = 0; + for (int i = 0; i < iovcnt; ++i) { + if (bufoff + iov[i].buf_len < (size_t)len) { + memcpy(iov[i].buf, buf + bufoff, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + else { + memcpy(iov[i].buf, buf + bufoff, len - bufoff); + break; + } + } + BH_FREE(buf); + *nread = len; + + return __WASI_ESUCCESS; +#endif +} + +__wasi_errno_t +os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + if (iovcnt == 0) + return __WASI_EINVAL; + + ssize_t len = 0; +#if CONFIG_HAS_PWRITEV + len = + pwritev(handle, (const struct iovec *)iov, (int)iovcnt, (off_t)offset); +#else + if (iovcnt == 1) { + len = pwrite(handle, iov->buf, iov->buf_len, offset); + } + else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (int i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = BH_MALLOC(totalsize); + if (buf == NULL) { + return __WASI_ENOMEM; + } + size_t bufoff = 0; + for (int i = 0; i < iovcnt; ++i) { + memcpy(buf + bufoff, iov[i].buf, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + + // Perform a single write operation. + len = pwrite(handle, buf, totalsize, offset); + BH_FREE(buf); + } +#endif + if (len < 0) + return convert_errno(errno); + + *nwritten = (size_t)len; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + size_t *nread) +{ + ssize_t len = readv(handle, (const struct iovec *)iov, (int)iovcnt); + + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten) +{ + ssize_t len = writev(handle, (const struct iovec *)iov, (int)iovcnt); + + if (len < 0) + return convert_errno(errno); + + *nwritten = (size_t)len; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fallocate(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length) +{ +#if CONFIG_HAS_POSIX_FALLOCATE + int ret = posix_fallocate(handle, (off_t)offset, (off_t)length); +#else + // At least ensure that the file is grown to the right size. + // TODO(ed): See if this can somehow be implemented without any race + // conditions. We may end up shrinking the file right now. + struct stat sb; + int ret = fstat(handle, &sb); + off_t newsize = (off_t)(offset + length); + + if (ret == 0 && sb.st_size < newsize) + ret = ftruncate(handle, newsize); +#endif + + if (ret != 0) + return convert_errno(ret); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_ftruncate(os_file_handle handle, __wasi_filesize_t size) +{ + int ret = ftruncate(handle, (off_t)size); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_futimens(os_file_handle handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags) +{ + struct timespec ts[2]; + convert_utimens_arguments(access_time, modification_time, fstflags, ts); + + int ret = futimens(handle, ts); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_utimensat(os_file_handle handle, const char *path, + __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags, + __wasi_lookupflags_t lookup_flags) +{ + struct timespec ts[2]; + convert_utimens_arguments(access_time, modification_time, fstflags, ts); + + int ret = utimensat(handle, path, ts, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) + ? 0 + : AT_SYMLINK_NOFOLLOW); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readlinkat(os_file_handle handle, const char *path, char *buf, + size_t bufsize, size_t *nread) +{ + // Linux requires that the buffer size is positive. whereas POSIX does + // not. Use a fake buffer to store the results if the size is zero. + char fakebuf[1]; + ssize_t len = readlinkat(handle, path, bufsize == 0 ? fakebuf : buf, + bufsize == 0 ? sizeof(fakebuf) : bufsize); + + if (len < 0) + return convert_errno(errno); + + *nread = (size_t)len < bufsize ? (size_t)len : bufsize; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_linkat(os_file_handle from_handle, const char *from_path, + os_file_handle to_handle, const char *to_path, + __wasi_lookupflags_t lookup_flags) +{ + int ret = linkat( + from_handle, from_path, to_handle, to_path, + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) ? AT_SYMLINK_FOLLOW : 0); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path) +{ + int ret = symlinkat(old_path, handle, new_path); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_mkdirat(os_file_handle handle, const char *path) +{ + int ret = mkdirat(handle, path, 0777); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_renameat(os_file_handle old_handle, const char *old_path, + os_file_handle new_handle, const char *new_path) +{ + + int ret = renameat(old_handle, old_path, new_handle, new_path); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_unlinkat(os_file_handle handle, const char *path, bool is_dir) +{ + int ret = unlinkat(handle, path, is_dir ? AT_REMOVEDIR : 0); + +#ifndef __linux__ + if (ret < 0) { + // Non-Linux implementations may return EPERM when attempting to remove + // a directory without REMOVEDIR. While that's what POSIX specifies, + // it's less useful. Adjust this to EISDIR. It doesn't matter that this + // is not atomic with the unlinkat, because if the file is removed and a + // directory is created before fstatat sees it, we're racing with that + // change anyway and unlinkat could have legitimately seen the directory + // if the race had turned out differently. + if (errno == EPERM) { + struct stat statbuf; + if (fstatat(handle, path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0 + && S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; + } + } + // POSIX permits either EEXIST or ENOTEMPTY when the directory is not + // empty. Map it to ENOTEMPTY. + else if (errno == EEXIST) { + errno = ENOTEMPTY; + } + + return convert_errno(errno); + } +#endif + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_lseek(os_file_handle handle, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *new_offset) +{ + int nwhence; + + switch (whence) { + case __WASI_WHENCE_CUR: + nwhence = SEEK_CUR; + break; + case __WASI_WHENCE_END: + nwhence = SEEK_END; + break; + case __WASI_WHENCE_SET: + nwhence = SEEK_SET; + break; + default: + return __WASI_EINVAL; + } + + off_t ret = lseek(handle, offset, nwhence); + + if (ret < 0) + return convert_errno(errno); + + *new_offset = (__wasi_filesize_t)ret; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fadvise(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length, __wasi_advice_t advice) +{ +#ifdef POSIX_FADV_NORMAL + int nadvice; + switch (advice) { + case __WASI_ADVICE_DONTNEED: + nadvice = POSIX_FADV_DONTNEED; + break; + case __WASI_ADVICE_NOREUSE: + nadvice = POSIX_FADV_NOREUSE; + break; + case __WASI_ADVICE_NORMAL: + nadvice = POSIX_FADV_NORMAL; + break; + case __WASI_ADVICE_RANDOM: + nadvice = POSIX_FADV_RANDOM; + break; + case __WASI_ADVICE_SEQUENTIAL: + nadvice = POSIX_FADV_SEQUENTIAL; + break; + case __WASI_ADVICE_WILLNEED: + nadvice = POSIX_FADV_WILLNEED; + break; + default: + return __WASI_EINVAL; + } + + int ret = posix_fadvise(handle, (off_t)offset, (off_t)length, nadvice); + + if (ret != 0) + return convert_errno(ret); + + return __WASI_ESUCCESS; +#else + // Advisory information can be safely ignored if not supported + switch (advice) { + case __WASI_ADVICE_DONTNEED: + case __WASI_ADVICE_NOREUSE: + case __WASI_ADVICE_NORMAL: + case __WASI_ADVICE_RANDOM: + case __WASI_ADVICE_SEQUENTIAL: + case __WASI_ADVICE_WILLNEED: + return __WASI_ESUCCESS; + default: + return __WASI_EINVAL; + } +#endif +} + +__wasi_errno_t +os_isatty(os_file_handle handle) +{ +#if CONFIG_HAS_ISATTY + int ret = isatty(handle); + + if (ret == 1) + return __WASI_ESUCCESS; + + return __WASI_ENOTTY; +#else + return __WASI_ENOTSUP; +#endif +} + +bool +os_is_stdin_handle(os_file_handle fd) +{ + return fd == STDIN_FILENO; +} + +bool +os_is_stdout_handle(os_file_handle fd) +{ + return fd == STDOUT_FILENO; +} + +bool +os_is_stderr_handle(os_file_handle fd) +{ + return fd == STDERR_FILENO; +} + +os_file_handle +os_convert_stdin_handle(os_raw_file_handle raw_stdin) +{ + return raw_stdin >= 0 ? raw_stdin : STDIN_FILENO; +} + +os_file_handle +os_convert_stdout_handle(os_raw_file_handle raw_stdout) +{ + return raw_stdout >= 0 ? raw_stdout : STDOUT_FILENO; +} + +os_file_handle +os_convert_stderr_handle(os_raw_file_handle raw_stderr) +{ + return raw_stderr >= 0 ? raw_stderr : STDERR_FILENO; +} + +__wasi_errno_t +os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream) +{ + *dir_stream = fdopendir(handle); + + if (*dir_stream == NULL) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_rewinddir(os_dir_stream dir_stream) +{ + rewinddir(dir_stream); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position) +{ + seekdir(dir_stream, (long)position); + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, + const char **d_name) +{ + errno = 0; + + struct dirent *dent = readdir(dir_stream); + + if (dent == NULL) { + *d_name = NULL; + if (errno != 0) { + return convert_errno(errno); + } + else { + return 0; + } + } + + long offset = (__wasi_dircookie_t)telldir(dir_stream); + + size_t namlen = strlen(dent->d_name); + + *d_name = dent->d_name; + entry->d_next = offset; + entry->d_namlen = (__wasi_dirnamlen_t)namlen; +#if CONFIG_HAS_D_INO + entry->d_ino = dent->d_ino; +#else + entry->d_ino = 0; +#endif + + switch (dent->d_type) { + case DT_BLK: + entry->d_type = __WASI_FILETYPE_BLOCK_DEVICE; + break; + case DT_CHR: + entry->d_type = __WASI_FILETYPE_CHARACTER_DEVICE; + break; + case DT_DIR: + entry->d_type = __WASI_FILETYPE_DIRECTORY; + break; + case DT_FIFO: + entry->d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; + case DT_LNK: + entry->d_type = __WASI_FILETYPE_SYMBOLIC_LINK; + break; + case DT_REG: + entry->d_type = __WASI_FILETYPE_REGULAR_FILE; + break; +#ifdef DT_SOCK + case DT_SOCK: + // Technically not correct, but good enough. + entry->d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; +#endif + default: + entry->d_type = __WASI_FILETYPE_UNKNOWN; + break; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_closedir(os_dir_stream dir_stream) +{ + int ret = closedir(dir_stream); + + if (ret < 0) + return convert_errno(errno); + + return __WASI_ESUCCESS; +} + +os_dir_stream +os_get_invalid_dir_stream() +{ + return NULL; +} + +bool +os_is_dir_stream_valid(os_dir_stream *dir_stream) +{ + assert(dir_stream != NULL); + + return *dir_stream != NULL; +} + +bool +os_is_handle_valid(os_file_handle *handle) +{ + assert(handle != NULL); + + return *handle > -1; +} + +char * +os_realpath(const char *path, char *resolved_path) +{ + return realpath(path, resolved_path); +} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return -1; +} + +int +os_ioctl(os_file_handle handle, int request, ...) +{ + return BHT_ERROR; +} + +int +os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout) +{ + return BHT_ERROR; +} diff --git a/core/shared/platform/esp-idf/espidf_malloc.c b/core/shared/platform/esp-idf/espidf_malloc.c new file mode 100644 index 0000000000..08ec883058 --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_malloc.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +void * +os_malloc(unsigned size) +{ + void *buf_origin; + void *buf_fixed; + uintptr_t *addr_field; + + buf_origin = malloc(size + 8 + sizeof(uintptr_t)); + if (!buf_origin) { + return NULL; + } + buf_fixed = buf_origin + sizeof(void *); + if ((uintptr_t)buf_fixed & (uintptr_t)0x7) { + buf_fixed = (void *)((uintptr_t)(buf_fixed + 8) & (~(uintptr_t)7)); + } + + addr_field = buf_fixed - sizeof(uintptr_t); + *addr_field = (uintptr_t)buf_origin; + + return buf_fixed; +} + +void * +os_realloc(void *ptr, unsigned size) +{ + void *mem_origin; + void *mem_new; + void *mem_new_fixed; + uintptr_t *addr_field; + + if (!ptr) { + return os_malloc(size); + } + + addr_field = ptr - sizeof(uintptr_t); + mem_origin = (void *)(*addr_field); + mem_new = realloc(mem_origin, size + 8 + sizeof(uintptr_t)); + if (!mem_new) { + return NULL; + } + + if (mem_origin != mem_new) { + mem_new_fixed = mem_new + sizeof(uintptr_t); + if ((uint32)mem_new_fixed & 0x7) { + mem_new_fixed = + (void *)((uintptr_t)(mem_new + 8) & (~(uintptr_t)7)); + } + + addr_field = mem_new_fixed - sizeof(uintptr_t); + *addr_field = (uintptr_t)mem_new; + + return mem_new_fixed; + } + + return ptr; +} + +void +os_free(void *ptr) +{ + void *mem_origin; + uintptr_t *addr_field; + + if (ptr) { + addr_field = ptr - sizeof(uintptr_t); + mem_origin = (void *)(*addr_field); + + free(mem_origin); + } +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} diff --git a/core/shared/platform/esp-idf/espidf_memmap.c b/core/shared/platform/esp-idf/espidf_memmap.c new file mode 100644 index 0000000000..b0c493d2de --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_memmap.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 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_MEM_DUAL_BUS_MIRROR != 0) +#include "soc/mmu.h" +#include "rom/cache.h" + +#define MEM_DUAL_BUS_OFFSET (SOC_IROM_LOW - SOC_IROM_HIGH) + +#define in_ibus_ext(addr) \ + (((uint32)addr >= SOC_IROM_LOW) && ((uint32)addr < SOC_IROM_HIGH)) + +static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; +#endif + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + if (prot & MMAP_PROT_EXEC) { +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + uint32_t mem_caps = MALLOC_CAP_SPIRAM; +#else + uint32_t mem_caps = MALLOC_CAP_EXEC; +#endif + + // Memory allocation with MALLOC_CAP_EXEC will return 4-byte aligned + // Reserve extra 4 byte to fixup alignment and size for the pointer to + // the originally allocated address + void *buf_origin = + heap_caps_malloc(size + 4 + sizeof(uintptr_t), mem_caps); + if (!buf_origin) { + return NULL; + } + void *buf_fixed = buf_origin + sizeof(void *); + if ((uintptr_t)buf_fixed & (uintptr_t)0x7) { + buf_fixed = (void *)((uintptr_t)(buf_fixed + 4) & (~(uintptr_t)7)); + } + + uintptr_t *addr_field = buf_fixed - sizeof(uintptr_t); + *addr_field = (uintptr_t)buf_origin; +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + memset(buf_fixed + MEM_DUAL_BUS_OFFSET, 0, size); + return buf_fixed + MEM_DUAL_BUS_OFFSET; +#else + memset(buf_fixed, 0, size); + return buf_fixed; +#endif + } + else { +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + uint32_t mem_caps = MALLOC_CAP_SPIRAM; +#else + uint32_t mem_caps = MALLOC_CAP_8BIT; +#endif + void *buf_origin = + heap_caps_malloc(size + 4 + sizeof(uintptr_t), mem_caps); + if (!buf_origin) { + return NULL; + } + + // Memory allocation with MALLOC_CAP_SPIRAM or MALLOC_CAP_8BIT will + // return 4-byte aligned Reserve extra 4 byte to fixup alignment and + // size for the pointer to the originally allocated address + void *buf_fixed = buf_origin + sizeof(void *); + if ((uintptr_t)buf_fixed & (uintptr_t)0x7) { + buf_fixed = (void *)((uintptr_t)(buf_fixed + 4) & (~(uintptr_t)7)); + } + + uintptr_t *addr_field = buf_fixed - sizeof(uintptr_t); + *addr_field = (uintptr_t)buf_origin; + + memset(buf_fixed, 0, size); + return buf_fixed; + } +} + +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size) +{ + return os_mremap_slow(old_addr, old_size, new_size); +} + +void +os_munmap(void *addr, size_t size) +{ + char *ptr = (char *)addr; + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + if (in_ibus_ext(ptr)) { + ptr -= MEM_DUAL_BUS_OFFSET; + } +#endif + // We don't need special handling of the executable allocations + // here, free() of esp-idf handles it properly + return os_free(ptr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + IRAM_ATTR +#endif + os_dcache_flush() +{ +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + uint32_t preload; + extern void Cache_WriteBack_All(void); + + portENTER_CRITICAL(&s_spinlock); + + Cache_WriteBack_All(); + preload = Cache_Disable_ICache(); + Cache_Enable_ICache(preload); + + portEXIT_CRITICAL(&s_spinlock); +#endif +} + +void +os_icache_flush(void *start, size_t len) +{} + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) +void * +os_get_dbus_mirror(void *ibus) +{ + if (in_ibus_ext(ibus)) { + return (void *)((char *)ibus - MEM_DUAL_BUS_OFFSET); + } + else { + return ibus; + } +} +#endif diff --git a/core/shared/platform/esp-idf/espidf_platform.c b/core/shared/platform/esp-idf/espidf_platform.c new file mode 100644 index 0000000000..045c3a5f6d --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_platform.c @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "sdkconfig.h" +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) \ + && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 2, 0)) +#define UTIMENSAT_TIMESPEC_POINTER 1 +#define FUTIMENS_TIMESPEC_POINTER 1 +#endif + +#if CONFIG_LITTLEFS_OPEN_DIR && CONFIG_LITTLEFS_FCNTL_GET_PATH +#define OPENAT_SUPPORT 1 + +#undef F_GETPATH +#define F_GETPATH CONFIG_LITTLEFS_FCNTL_F_GETPATH_VALUE + +#define DIR_PATH_LEN (CONFIG_LITTLEFS_OBJ_NAME_LEN + 1) +#endif + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} + +uint64 +os_time_get_boot_us(void) +{ + return (uint64)esp_timer_get_time(); +} + +uint64 +os_time_thread_cputime_us(void) +{ + /* FIXME if u know the right api */ + return os_time_get_boot_us(); +} + +uint8 * +os_thread_get_stack_boundary(void) +{ +#if defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) + TaskStatus_t pxTaskStatus; + vTaskGetInfo(xTaskGetCurrentTaskHandle(), &pxTaskStatus, pdTRUE, eInvalid); + return pxTaskStatus.pxStackBase; +#else // !defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) + return NULL; +#endif +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} + +int +os_usleep(uint32 usec) +{ + return usleep(usec); +} + +/* Below parts of readv & writev are ported from Nuttx, under Apache License + * v2.0 */ + +ssize_t +readv(int fildes, const struct iovec *iov, int iovcnt) +{ + ssize_t ntotal; + ssize_t nread; + size_t remaining; + uint8_t *buffer; + int i; + + /* Process each entry in the struct iovec array */ + + for (i = 0, ntotal = 0; i < iovcnt; i++) { + /* Ignore zero-length reads */ + + if (iov[i].iov_len > 0) { + buffer = iov[i].iov_base; + remaining = iov[i].iov_len; + + /* Read repeatedly as necessary to fill buffer */ + + do { + /* NOTE: read() is a cancellation point */ + + nread = read(fildes, buffer, remaining); + + /* Check for a read error */ + + if (nread < 0) { + return nread; + } + + /* Check for an end-of-file condition */ + + else if (nread == 0) { + return ntotal; + } + + /* Update pointers and counts in order to handle partial + * buffer reads. + */ + + buffer += nread; + remaining -= nread; + ntotal += nread; + } while (remaining > 0); + } + } + + return ntotal; +} + +ssize_t +writev(int fildes, const struct iovec *iov, int iovcnt) +{ + ssize_t ntotal; + ssize_t nwritten; + size_t remaining; + uint8_t *buffer; + int i; + + /* Process each entry in the struct iovec array */ + + for (i = 0, ntotal = 0; i < iovcnt; i++) { + /* Ignore zero-length writes */ + + if (iov[i].iov_len > 0) { + buffer = iov[i].iov_base; + remaining = iov[i].iov_len; + + /* Write repeatedly as necessary to write the entire buffer */ + + do { + /* NOTE: write() is a cancellation point */ + + nwritten = write(fildes, buffer, remaining); + + /* Check for a write error */ + + if (nwritten < 0) { + return ntotal ? ntotal : -1; + } + + /* Update pointers and counts in order to handle partial + * buffer writes. + */ + + buffer += nwritten; + remaining -= nwritten; + ntotal += nwritten; + } while (remaining > 0); + } + } + + return ntotal; +} + +#if OPENAT_SUPPORT +int +openat(int fd, const char *pathname, int flags, ...) +{ + int new_fd; + int ret; + char dir_path[DIR_PATH_LEN]; + char *full_path; + mode_t mode = 0; + bool has_mode = false; + + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = (mode_t)va_arg(ap, int); + va_end(ap); + has_mode = true; + } + + ret = fcntl(fd, F_GETPATH, dir_path); + if (ret != 0) { + errno = EINVAL; + return -1; + } + + ret = asprintf(&full_path, "%s/%s", dir_path, pathname); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + + new_fd = has_mode ? open(full_path, flags, mode) : open(full_path, flags); + free(full_path); + + return new_fd; +} +#else +int +openat(int fd, const char *path, int oflags, ...) +{ + errno = ENOSYS; + return -1; +} +#endif + +int +fstatat(int fd, const char *path, struct stat *buf, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +mkdirat(int fd, const char *path, mode_t mode) +{ + errno = ENOSYS; + return -1; +} + +ssize_t +readlinkat(int fd, const char *path, char *buf, size_t bufsize) +{ + errno = EINVAL; + return -1; +} + +int +linkat(int fd1, const char *path1, int fd2, const char *path2, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +renameat(int fromfd, const char *from, int tofd, const char *to) +{ + errno = ENOSYS; + return -1; +} + +int +symlinkat(const char *target, int fd, const char *path) +{ + errno = ENOSYS; + return -1; +} + +int +unlinkat(int fd, const char *path, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +utimensat(int fd, const char *path, +#if UTIMENSAT_TIMESPEC_POINTER + const struct timespec *ts, +#else + const struct timespec ts[2], +#endif + int flag) +{ + errno = ENOSYS; + return -1; +} + +DIR * +fdopendir(int fd) +{ + errno = ENOSYS; + return NULL; +} + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 2) +int +ftruncate(int fd, off_t length) +{ + errno = ENOSYS; + return -1; +} +#endif + +int +futimens(int fd, +#if FUTIMENS_TIMESPEC_POINTER + const struct timespec *times +#else + const struct timespec times[2] +#endif +) +{ + errno = ENOSYS; + return -1; +} + +int +nanosleep(const struct timespec *req, struct timespec *rem) +{ + errno = ENOSYS; + return -1; +} diff --git a/core/shared/platform/esp-idf/espidf_socket.c b/core/shared/platform/esp-idf/espidf_socket.c new file mode 100644 index 0000000000..8c65094647 --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_socket.c @@ -0,0 +1,1027 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +#include "libc_errno.h" + +#include +#include +#include +#include + +static bool +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out, + socklen_t *out_len) +{ + struct sockaddr_in *v4; +#ifdef IPPROTO_IPV6 + struct sockaddr_in6 *v6; +#endif + + assert(textual); + + v4 = (struct sockaddr_in *)out; + if (inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) { + v4->sin_family = AF_INET; + v4->sin_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } + +#ifdef IPPROTO_IPV6 + v6 = (struct sockaddr_in6 *)out; + if (inet_pton(AF_INET6, textual, &v6->sin6_addr.s6_addr) == 1) { + v6->sin6_family = AF_INET6; + v6->sin6_port = htons(port); + *out_len = sizeof(struct sockaddr_in6); + return true; + } +#endif + + return false; +} + +static int +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, + bh_sockaddr_t *bh_sockaddr) +{ + switch (sockaddr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + + bh_sockaddr->port = ntohs(addr->sin_port); + bh_sockaddr->addr_buffer.ipv4 = ntohl(addr->sin_addr.s_addr); + bh_sockaddr->is_ipv4 = true; + return BHT_OK; + } +#ifdef IPPROTO_IPV6 + case AF_INET6: + { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + + bh_sockaddr->port = ntohs(addr->sin6_port); + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = addr->sin6_addr.s6_addr[i * 2] + | (addr->sin6_addr.s6_addr[i * 2 + 1] << 8); + bh_sockaddr->addr_buffer.ipv6[i] = ntohs(part_addr); + } + + bh_sockaddr->is_ipv4 = false; + return BHT_OK; + } +#endif + default: + errno = EAFNOSUPPORT; + return BHT_ERROR; + } +} + +static void +bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, + struct sockaddr_storage *sockaddr, socklen_t *socklen) +{ + if (bh_sockaddr->is_ipv4) { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + addr->sin_port = htons(bh_sockaddr->port); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(bh_sockaddr->addr_buffer.ipv4); + *socklen = sizeof(*addr); + } +#ifdef IPPROTO_IPV6 + else { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + addr->sin6_port = htons(bh_sockaddr->port); + addr->sin6_family = AF_INET6; + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = htons(bh_sockaddr->addr_buffer.ipv6[i]); + addr->sin6_addr.s6_addr[i * 2] = 0xff & part_addr; + addr->sin6_addr.s6_addr[i * 2 + 1] = (0xff00 & part_addr) >> 8; + } + + *socklen = sizeof(*addr); + } +#endif +} + +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + int af = is_ipv4 ? AF_INET : AF_INET6; + + if (!sock) { + return BHT_ERROR; + } + + if (is_tcp) { + *sock = socket(af, SOCK_STREAM, IPPROTO_TCP); + } + else { + *sock = socket(af, SOCK_DGRAM, 0); + } + + return (*sock == -1) ? BHT_ERROR : BHT_OK; +} + +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + struct sockaddr_storage addr = { 0 }; + struct linger ling; + socklen_t socklen; + int ret; + + assert(host); + assert(port); + + ling.l_onoff = 1; + ling.l_linger = 0; + + if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr, + &socklen)) { + goto fail; + } + + ret = setsockopt(socket, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); + if (ret < 0) { + goto fail; + } + + ret = bind(socket, (struct sockaddr *)&addr, socklen); + if (ret < 0) { + goto fail; + } + + socklen = sizeof(addr); + if (getsockname(socket, (void *)&addr, &socklen) == -1) { + 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: + return BHT_ERROR; +} + +int +os_socket_settimeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, + sizeof(tv)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + if (listen(socket, max_client) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) +{ + *sock = accept(server_sock, addr, (socklen_t *)addrlen); + + if (*sock < 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + struct sockaddr_storage addr_in = { 0 }; + socklen_t addr_len; + int ret = 0; + + if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr_in, + &addr_len)) { + return BHT_ERROR; + } + + ret = connect(socket, (struct sockaddr *)&addr_in, addr_len); + if (ret == -1) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + return recv(socket, buf, len, 0); +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = sizeof(sock_addr); + int ret; + + ret = recvfrom(socket, buf, len, flags, (struct sockaddr *)&sock_addr, + &socklen); + + if (ret < 0) { + return ret; + } + + if (src_addr && socklen > 0) { + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, src_addr) + == BHT_ERROR) { + return -1; + } + } + else if (src_addr) { + memset(src_addr, 0, sizeof(*src_addr)); + } + + return ret; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + return send(socket, buf, len, 0); +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = 0; + + bh_sockaddr_to_sockaddr(dest_addr, &sock_addr, &socklen); + + return sendto(socket, buf, len, flags, (const struct sockaddr *)&sock_addr, + socklen); +} + +int +os_socket_close(bh_socket_t socket) +{ + close(socket); + return BHT_OK; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + if (shutdown(socket, O_RDWR) != 0) { + return convert_errno(errno); + } + return __WASI_ESUCCESS; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + if (!cp) + return BHT_ERROR; + + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { +#ifdef IPPROTO_IPV6 + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + + return BHT_OK; +} + +static int +getaddrinfo_error_to_errno(int error) +{ + switch (error) { + case EAI_AGAIN: + return EAGAIN; + case EAI_FAIL: + return EFAULT; + case EAI_MEMORY: + return ENOMEM; + default: + return EINVAL; + } +} + +static int +is_addrinfo_supported(struct addrinfo *info) +{ + return + // Allow only IPv4 and IPv6 + (info->ai_family == AF_INET || info->ai_family == AF_INET6) + // Allow only UDP and TCP + && (info->ai_socktype == SOCK_DGRAM || info->ai_socktype == SOCK_STREAM) + && (info->ai_protocol == IPPROTO_TCP + || info->ai_protocol == IPPROTO_UDP); +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + struct addrinfo hints = { 0 }, *res, *result; + int hints_enabled = hint_is_tcp || hint_is_ipv4; + int ret; + size_t pos = 0; + + if (hints_enabled) { + if (hint_is_ipv4) { + hints.ai_family = *hint_is_ipv4 ? AF_INET : AF_INET6; + } + if (hint_is_tcp) { + hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM; + } + } + + ret = getaddrinfo(host, strlen(service) == 0 ? NULL : service, + hints_enabled ? &hints : NULL, &result); + if (ret != BHT_OK) { + errno = getaddrinfo_error_to_errno(ret); + return BHT_ERROR; + } + + res = result; + while (res) { + if (addr_info_size > pos) { + if (!is_addrinfo_supported(res)) { + res = res->ai_next; + continue; + } + + ret = + sockaddr_to_bh_sockaddr(res->ai_addr, &addr_info[pos].sockaddr); + + if (ret == BHT_ERROR) { + freeaddrinfo(result); + return BHT_ERROR; + } + + addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM; + } + + pos++; + res = res->ai_next; + } + + *max_info_size = pos; + freeaddrinfo(result); + + return BHT_OK; +} + +static int +os_socket_setbooloption(bh_socket_t socket, int level, int optname, + bool is_enabled) +{ + int option = (int)is_enabled; + if (setsockopt(socket, level, optname, &option, sizeof(option)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +static int +os_socket_getbooloption(bh_socket_t socket, int level, int optname, + bool *is_enabled) +{ + assert(is_enabled); + + int optval; + socklen_t optval_size = sizeof(optval); + if (getsockopt(socket, level, optname, &optval, &optval_size) != 0) { + return BHT_ERROR; + } + *is_enabled = (bool)optval; + return BHT_OK; +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + if (getsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + if (getsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_size_int, &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ +#if defined(SO_REUSEPORT) /* NuttX doesn't have SO_REUSEPORT */ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +#else + errno = ENOTSUP; + return BHT_ERROR; +#endif /* defined(SO_REUSEPORT) */ +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ +#if defined(SO_REUSEPORT) /* NuttX doesn't have SO_REUSEPORT */ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +#else + errno = ENOTSUP; + return BHT_ERROR; +#endif /* defined(SO_REUSEPORT) */ +} + +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + struct linger linger_opts = { .l_onoff = (int)is_enabled, + .l_linger = linger_s }; + if (setsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, + sizeof(linger_opts)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + assert(is_enabled); + assert(linger_s); + + struct linger linger_opts; + socklen_t linger_opts_len = sizeof(linger_opts); + if (getsockopt(socket, SOL_SOCKET, SO_LINGER, &linger_opts, + &linger_opts_len) + != 0) { + return BHT_ERROR; + } + *linger_s = linger_opts.l_linger; + *is_enabled = (bool)linger_opts.l_onoff; + return BHT_OK; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ +#ifdef TCP_QUICKACK + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ +#ifdef TCP_QUICKACK + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) +{ + int time_s_int = (int)time_s; +#ifdef TCP_KEEPIDLE + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) +{ + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); +#ifdef TCP_KEEPIDLE + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) +{ + int time_s_int = (int)time_s; +#ifdef TCP_KEEPINTVL + if (setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) +{ +#ifdef TCP_KEEPINTVL + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + if (getsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ +#ifdef TCP_FASTOPEN_CONNECT + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ +#ifdef TCP_FASTOPEN_CONNECT + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + if (setsockopt(socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + + return BHT_OK; +} + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + if (setsockopt(socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_interface.s_addr = imr_interface; + if (setsockopt(socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + + return BHT_OK; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (setsockopt(socket, IPPROTO_IP, IP_TTL, &ttl_s, sizeof(ttl_s)) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + if (getsockopt(socket, IPPROTO_IP, IP_TTL, ttl_s, &opt_len) != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl_s, sizeof(ttl_s)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + socklen_t opt_len = sizeof(*ttl_s); + if (getsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, ttl_s, &opt_len) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + if (setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + if (getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + return BHT_OK; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + if (getsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len) != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + return BHT_OK; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = getsockname(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = getpeername(socket, (struct sockaddr *)&addr_storage, &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} diff --git a/core/shared/platform/esp-idf/espidf_thread.c b/core/shared/platform/esp-idf/espidf_thread.c new file mode 100644 index 0000000000..cb68df8bdf --- /dev/null +++ b/core/shared/platform/esp-idf/espidf_thread.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +typedef struct { + thread_start_routine_t start; + void *arg; +} thread_wrapper_arg; + +static void * +os_thread_wrapper(void *arg) +{ + thread_wrapper_arg *targ = arg; + thread_start_routine_t start_func = targ->start; + void *thread_arg = targ->arg; + +#if 0 + os_printf("THREAD CREATED %jx\n", (uintmax_t)(uintptr_t)pthread_self()); +#endif + BH_FREE(targ); + start_func(thread_arg); + return NULL; +} + +korp_tid +os_self_thread(void) +{ + /* only allowed if this is a thread, xTaskCreate is not enough look at + * product_mini for how to use this*/ + return pthread_self(); +} + +int +os_mutex_init(korp_mutex *mutex) +{ + return pthread_mutex_init(mutex, NULL); +} + +int +os_recursive_mutex_init(korp_mutex *mutex) +{ + int ret; + + pthread_mutexattr_t mattr; + + assert(mutex); + ret = pthread_mutexattr_init(&mattr); + if (ret) + return BHT_ERROR; + + pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); + ret = pthread_mutex_init(mutex, &mattr); + pthread_mutexattr_destroy(&mattr); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + return pthread_mutex_destroy(mutex); +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + return pthread_mutex_lock(mutex); +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + return pthread_mutex_unlock(mutex); +} + +int +os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + pthread_attr_t tattr; + thread_wrapper_arg *targ; + + assert(stack_size > 0); + assert(tid); + assert(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 = %u", + stack_size, PTHREAD_STACK_MIN); + pthread_attr_destroy(&tattr); + return BHT_ERROR; + } + + targ = (thread_wrapper_arg *)BH_MALLOC(sizeof(*targ)); + if (!targ) { + pthread_attr_destroy(&tattr); + return BHT_ERROR; + } + + targ->start = start; + targ->arg = arg; + +#ifdef CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM + esp_pthread_cfg_t default_config = esp_pthread_get_default_config(); + + default_config.stack_alloc_caps = MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM; + ESP_ERROR_CHECK(esp_pthread_set_cfg(&default_config)); +#endif + + if (pthread_create(tid, &tattr, os_thread_wrapper, targ) != 0) { + pthread_attr_destroy(&tattr); + os_free(targ); + return BHT_ERROR; + } + + pthread_attr_destroy(&tattr); + return BHT_OK; +} + +int +os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_join(korp_tid thread, void **retval) +{ + return pthread_join(thread, retval); +} + +int +os_thread_detach(korp_tid tid) +{ + return pthread_detach(tid); +} + +void +os_thread_exit(void *retval) +{ + pthread_exit(retval); +} + +int +os_cond_init(korp_cond *cond) +{ + return pthread_cond_init(cond, NULL); +} + +int +os_cond_destroy(korp_cond *cond) +{ + return pthread_cond_destroy(cond); +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return pthread_cond_wait(cond, mutex); +} + +static void +msec_nsec_to_abstime(struct timespec *ts, uint64 usec) +{ + struct timeval tv; + time_t tv_sec_new; + long int tv_nsec_new; + + gettimeofday(&tv, NULL); + + tv_sec_new = (time_t)(tv.tv_sec + usec / 1000000); + if (tv_sec_new >= tv.tv_sec) { + ts->tv_sec = tv_sec_new; + } + else { + /* integer overflow */ + ts->tv_sec = BH_TIME_T_MAX; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + + tv_nsec_new = (long int)(tv.tv_usec * 1000 + (usec % 1000000) * 1000); + if (tv.tv_usec * 1000 >= tv.tv_usec && tv_nsec_new >= tv.tv_usec * 1000) { + ts->tv_nsec = tv_nsec_new; + } + else { + /* integer overflow */ + ts->tv_nsec = LONG_MAX; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + + if (ts->tv_nsec >= 1000000000L && ts->tv_sec < BH_TIME_T_MAX) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + int ret; + struct timespec abstime; + + if (useconds == BHT_WAIT_FOREVER) + ret = pthread_cond_wait(cond, mutex); + else { + msec_nsec_to_abstime(&abstime, useconds); + ret = pthread_cond_timedwait(cond, mutex, &abstime); + } + + if (ret != BHT_OK && ret != ETIMEDOUT) + return BHT_ERROR; + + return ret; +} + +int +os_cond_signal(korp_cond *cond) +{ + return pthread_cond_signal(cond); +} + +int +os_cond_broadcast(korp_cond *cond) +{ + return pthread_cond_broadcast(cond); +} + +int +os_rwlock_init(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_init(lock, NULL) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_rdlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_wrlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_unlock(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ + assert(lock); + + if (pthread_rwlock_destroy(lock) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} diff --git a/core/shared/platform/esp-idf/platform_internal.h b/core/shared/platform/esp-idf/platform_internal.h new file mode 100644 index 0000000000..a704bb0fb2 --- /dev/null +++ b/core/shared/platform/esp-idf/platform_internal.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esp_pthread.h" +#include "esp_timer.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_ESP_IDF +#define BH_PLATFORM_ESP_IDF +#endif + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef unsigned int korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 5 + +/* Special value for tv_nsec field of timespec */ + +#define UTIME_NOW ((1l << 30) - 1l) +#ifndef __cplusplus +#define UTIME_OMIT ((1l << 30) - 2l) +#endif + +/* Below parts of d_type define are ported from Nuttx, under Apache License v2.0 + */ + +/* Following macros are defined in espressif GCC of esp-idf v5.3 + */ + +#define DTYPE_UNKNOWN 0 +#define DTYPE_FILE 1 +#define DTYPE_DIRECTORY 2 +#define DTYPE_CHR 4 +#define DTYPE_BLK 5 +#define DTYPE_FIFO 8 +#define DTYPE_LINK 10 +#define DTYPE_SOCK 12 + +/* Following macros are not defined in espressif GCC of esp-idf v5.3 + */ + +#define DTYPE_SEM 100 +#define DTYPE_MQ 101 +#define DTYPE_SHM 102 +#define DTYPE_MTD 103 + +/* The d_type field of the dirent structure is not specified by POSIX. It + * is a non-standard, 4.5BSD extension that is implemented by most OSs. A + * POSIX compliant OS may not implement the d_type field at all. Many OS's + * (including glibc) may use the following alternative naming for the file + * type names: + */ + +#ifndef DT_UNKNOWN +#define DT_UNKNOWN DTYPE_UNKNOWN +#endif + +#ifndef DT_FIFO +#define DT_FIFO DTYPE_FIFO +#endif + +#ifndef DT_CHR +#define DT_CHR DTYPE_CHR +#endif + +#ifndef DT_SEM +#define DT_SEM DTYPE_SEM +#endif + +#ifndef DT_DIR +#define DT_DIR DTYPE_DIRECTORY +#endif + +#ifndef DT_MQ +#define DT_MQ DTYPE_MQ +#endif + +#ifndef DT_BLK +#define DT_BLK DTYPE_BLK +#endif + +#ifndef DT_SHM +#define DT_SHM DTYPE_SHM +#endif + +#ifndef DT_REG +#define DT_REG DTYPE_FILE +#endif + +#ifndef DT_MTD +#define DT_MTD DTYPE_MTD +#endif + +#ifndef DT_LNK +#define DT_LNK DTYPE_LINK +#endif + +#ifndef DT_SOCK +#define DT_SOCK DTYPE_SOCK +#endif + +static inline int +os_getpagesize() +{ + return 4096; +} + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_poll_file_handle; +typedef unsigned int os_nfds_t; +typedef int os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/shared/platform/esp-idf/shared_platform.cmake b/core/shared/platform/esp-idf/shared_platform.cmake new file mode 100644 index 0000000000..d254b087b0 --- /dev/null +++ b/core/shared/platform/esp-idf/shared_platform.cmake @@ -0,0 +1,22 @@ +# 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_ESP_IDF) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) +set (source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) + +# If enable PSRAM of ESP32-S3, it had better to put AOT into PSRAM, so that +# users can use SRAM to for Wi-Fi/BLE and peripheral driver. +if(CONFIG_ESP32S3_SPIRAM_SUPPORT) + add_definitions(-DWASM_MEM_DUAL_BUS_MIRROR=1) +endif() diff --git a/core/shared/platform/freebsd/platform_init.c b/core/shared/platform/freebsd/platform_init.c new file mode 100644 index 0000000000..2aae13fa14 --- /dev/null +++ b/core/shared/platform/freebsd/platform_init.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} diff --git a/core/shared/platform/freebsd/platform_internal.h b/core/shared/platform/freebsd/platform_internal.h new file mode 100644 index 0000000000..8d6160c36e --- /dev/null +++ b/core/shared/platform/freebsd/platform_internal.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_FREEBSD +#define BH_PLATFORM_FREEBSD +#endif + +#define BH_HAS_DLFCN 1 + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define os_thread_local_attribute __thread + +#define bh_socket_t int + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef struct timespec os_timespec; + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +typedef void (*os_signal_handler)(void *sig_addr); + +int +os_thread_signal_init(os_signal_handler handler); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +void +os_signal_unmask(); + +void +os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64/RISCV64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#define os_getpagesize getpagesize + +#if WASM_DISABLE_WAKEUP_BLOCKING_OP == 0 +#define OS_ENABLE_WAKEUP_BLOCKING_OP +#endif +void +os_set_signal_number_for_blocking_op(int signo); + +typedef int os_file_handle; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/freebsd/shared_platform.cmake b/core/shared/platform/freebsd/shared_platform.cmake new file mode 100644 index 0000000000..12583fc63a --- /dev/null +++ b/core/shared/platform/freebsd/shared_platform.cmake @@ -0,0 +1,18 @@ +# 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_FREEBSD) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/include/bh_assert.h b/core/shared/platform/include/bh_assert.h deleted file mode 100644 index 370f6cea87..0000000000 --- a/core/shared/platform/include/bh_assert.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_ASSERT_H -#define _BH_ASSERT_H - -#include "bh_config.h" -#include "bh_platform.h" - -#ifdef BH_TEST -# ifndef BH_DEBUG -# error "BH_TEST should be defined under BH_DEBUG" -# endif -#endif - -#ifdef BH_TEST -# if defined(WIN32) || defined(__linux__) -# else -# error "Test case can not run on the current platform" -# endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef BH_DEBUG - -void bh_assert_internal(int v, const char *file_name, int line_number, - const char *expr_string); -#define bh_assert(expr) bh_assert_internal((int)(expr), __FILE__, __LINE__, #expr) - -void bh_debug_internal(const char *file_name, int line_number, - const char *fmt, ...); -#if defined(WIN32) - #define bh_debug(fmt, ...) bh_debug_internal(__FILE__, __LINE__, fmt, __VA_ARGS__) -#elif defined(__linux__) - #define bh_debug bh_debug_internal(__FILE__, __LINE__, "");printf -#else - #error "Unsupported platform" -#endif - -#else /* else of BH_DEBUG */ - -#define bh_debug if(0)printf - -#endif /* end of BH_DEBUG */ - -#ifdef BH_TEST - #define BH_STATIC -#else - #define BH_STATIC static -#endif /* end of BH_TEST */ - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/core/shared/platform/include/bh_config.h b/core/shared/platform/include/bh_config.h deleted file mode 100644 index 0069194d1c..0000000000 --- a/core/shared/platform/include/bh_config.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/** - * @file bh_config.h - * @date Tue Sep 13 14:53:17 2011 - * - * @brief Configurations for different platforms and targets. Make - * sure all source files in Beihai project include this header file - * directly or indirectly. - */ - -#ifndef BH_CONFIG - -#include "config.h" - -#endif /* end of BH_CONFIG */ - diff --git a/core/shared/platform/include/bh_platform_log.h b/core/shared/platform/include/bh_platform_log.h deleted file mode 100644 index 2513055e7c..0000000000 --- a/core/shared/platform/include/bh_platform_log.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef BH_PLATFORM_LOG -#define BH_PLATFORM_LOG - -#include "bh_platform.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void bh_log_emit(const char *fmt, va_list ap); - -int bh_fprintf(void *stream, const char *fmt, ...); - -int bh_fflush(void *stream); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/shared/platform/include/bh_thread.h b/core/shared/platform/include/bh_thread.h deleted file mode 100644 index 901e615eb5..0000000000 --- a/core/shared/platform/include/bh_thread.h +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_THREAD_H -#define _BH_THREAD_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bh_config.h" -#include "bh_platform.h" - -#define BH_MAX_THREAD 32 -#define BH_MAX_TLS_NUM 2 - -#define BHT_ERROR (-1) -#define BHT_TIMED_OUT (1) -#define BHT_OK (0) - -#define BHT_NO_WAIT 0x00000000 -#define BHT_WAIT_FOREVER 0xFFFFFFFF - -/** - * vm_thread_sys_init - * initiation function for beihai thread system. Invoked at the beginning of beihai intiation. - * - * @return 0 if succuess. - */ -int _vm_thread_sys_init(void); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_thread_sys_init_instr(const char*func_name); -#define vm_thread_sys_init(void) vm_thread_sys_init_instr(__FUNCTION__) -#else -#define vm_thread_sys_init _vm_thread_sys_init -#endif - -void vm_thread_sys_destroy(void); - -/** - * This function creates a thread - * - * @param p_tid [OUTPUT] the pointer of tid - * @param start main routine of the thread - * @param arg argument passed to main routine - * @param stack_size bytes of stack size - * - * @return 0 if success. - */ -int _vm_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, - unsigned int stack_size); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_thread_create_instr(korp_tid *p_tid, thread_start_routine_t start, void *arg, unsigned int stack_size, const char*func_name); -#define vm_thread_create(p_tid, start, arg, stack_size) vm_thread_create_instr(p_tid, start, arg, stack_size, __FUNCTION__) -#else -#define vm_thread_create _vm_thread_create -#endif - -/** - * This function creates a thread - * - * @param p_tid [OUTPUT] the pointer of tid - * @param start main routine of the thread - * @param arg argument passed to main routine - * @param stack_size bytes of stack size - * @param prio the priority - * - * @return 0 if success. - */ -int _vm_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_thread_create_with_prio_instr(korp_tid *p_tid, thread_start_routine_t start, void *arg, unsigned int stack_size, int prio, const char*func_name); -#define vm_thread_create_with_prio(p_tid, start, arg, stack_size) vm_thread_create_instr(p_tid, start, arg, stack_size, prio, __FUNCTION__) -#else -#define vm_thread_create_with_prio _vm_thread_create_with_prio -#endif - -/** - * This function never returns. - * - * @param code not used - */ -void vm_thread_exit(void *code); - -/** - * This function gets current thread id - * - * @return current thread id - */ -korp_tid _vm_self_thread(void); -#ifdef _INSTRUMENT_TEST_ENABLED -korp_tid vm_self_thread_instr(const char*func_name); -#define vm_self_thread(void) vm_self_thread_instr(__FUNCTION__) -#else -#define vm_self_thread _vm_self_thread -#endif - -/** - * This function saves a pointer in thread local storage. One thread can only save one pointer. - * - * @param idx tls array index - * @param ptr pointer need save as TLS - * - * @return 0 if success - */ -int _vm_tls_put(unsigned idx, void *ptr); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_tls_put_instr(unsigned idx, void *ptr, const char*func_name); -#define vm_tls_put(idx, ptr) vm_tls_put_instr(idx, ptr, __FUNCTION__) -#else -#define vm_tls_put _vm_tls_put -#endif - -/** - * This function gets a pointer saved in TLS. - * - * @param idx tls array index - * - * @return the pointer saved in TLS. - */ -void *_vm_tls_get(unsigned idx); -#ifdef _INSTRUMENT_TEST_ENABLED -void *vm_tls_get_instr(unsigned idx, const char*func_name); -#define vm_tls_get(idx) vm_tls_get_instr(idx, __FUNCTION__) -#else -#define vm_tls_get _vm_tls_get -#endif - -#define vm_thread_testcancel(void) - -/** - * This function creates a non-recursive mutex - * - * @param mutex [OUTPUT] pointer to mutex initialized. - * - * @return 0 if success - */ -int _vm_mutex_init(korp_mutex *mutex); -#ifdef INSTRUMENT_TEST_ENABLED -int vm_mutex_init_instr(korp_mutex *mutex, const char*func_name); -#define vm_mutex_init(mutex) vm_mutex_init_instr(mutex, __FUNCTION__) -#else -#define vm_mutex_init _vm_mutex_init -#endif - -/** - * This function creates a recursive mutex - * - * @param mutex [OUTPUT] pointer to mutex initialized. - * - * @return 0 if success - */ -int _vm_recursive_mutex_init(korp_mutex *mutex); -#ifdef INSTRUMENT_TEST_ENABLED -int vm_recursive_mutex_init_instr(korp_mutex *mutex, const char*func_name); -#define vm_recursive_mutex_init(mutex) vm_recursive_mutex_init_instr(mutex, __FUNCTION__) -#else -#define vm_recursive_mutex_init _vm_recursive_mutex_init -#endif - -/** - * This function destroys a mutex - * - * @param mutex pointer to mutex need destroy - * - * @return 0 if success - */ -int _vm_mutex_destroy(korp_mutex *mutex); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_mutex_destroy_instr(korp_mutex *mutex, const char*func_name); -#define vm_mutex_destroy(mutex) vm_mutex_destroy_instr(mutex, __FUNCTION__) -#else -#define vm_mutex_destroy _vm_mutex_destroy -#endif - -/** - * This function locks the mutex - * - * @param mutex pointer to mutex need lock - * - * @return Void - */ -void vm_mutex_lock(korp_mutex *mutex); - -/** - * This function locks the mutex without waiting - * - * @param mutex pointer to mutex need lock - * - * @return 0 if success - */ -int vm_mutex_trylock(korp_mutex *mutex); - -/** - * This function unlocks the mutex - * - * @param mutex pointer to mutex need unlock - * - * @return Void - */ -void vm_mutex_unlock(korp_mutex *mutex); - -/** - * This function creates a semaphone - * - * @param sem [OUTPUT] pointer to semaphone - * @param c counter of semaphone - * - * @return 0 if success - */ -int _vm_sem_init(korp_sem *sem, unsigned int c); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_sem_init_instr(korp_sem *sem, unsigned int c, const char*func_name); -#define vm_sem_init(sem, c) vm_sem_init_instr(sem, c, __FUNCTION__) -#else -#define vm_sem_init _vm_sem_init -#endif - -/** - * This function destroys a semaphone - * - * @param sem pointer to semaphone need destroy - * - * @return 0 if success - */ -int _vm_sem_destroy(korp_sem *sem); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_sem_destroy_instr(korp_sem *sem, const char*func_name); -#define vm_sem_destroy(sem) vm_sem_destroy_instr(sem, __FUNCTION__) -#else -#define vm_sem_destroy _vm_sem_destroy -#endif - -/** - * This function performs wait operation on semaphone - * - * @param sem pointer to semaphone need perform wait operation - * - * @return 0 if success - */ -int _vm_sem_wait(korp_sem *sem); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_sem_wait_instr(korp_sem *sem, const char*func_name); -#define vm_sem_wait(sem) vm_sem_wait_instr(sem, __FUNCTION__) -#else -#define vm_sem_wait _vm_sem_wait -#endif - -/** - * This function performs wait operation on semaphone with a timeout - * - * @param sem pointer to semaphone need perform wait operation - * @param mills wait milliseconds to return - * - * @return 0 if success - * @return BH_TIMEOUT if time out - */ -int _vm_sem_reltimedwait(korp_sem *sem, int mills); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_sem_reltimedwait_instr(korp_sem *sem, int mills, const char*func_name); -#define vm_sem_reltimedwait(sem, mills) vm_sem_reltimedwait_instr(sem, mills, __FUNCTION__) -#else -#define vm_sem_reltimedwait _vm_sem_reltimedwait -#endif - -/** - * This function performs post operation on semaphone - * - * @param sem pointer to semaphone need perform post operation - * - * @return 0 if success - */ -int _vm_sem_post(korp_sem *sem); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_sem_post_instr(korp_sem *sem, const char*func_name); -#define vm_sem_post(sem) vm_sem_post_instr(sem, __FUNCTION__) -#else -#define vm_sem_post _vm_sem_post -#endif - -/** - * This function creates a condition variable - * - * @param cond [OUTPUT] pointer to condition variable - * - * @return 0 if success - */ -int _vm_cond_init(korp_cond *cond); -#ifdef INSTRUMENT_TEST_ENABLED -int vm_cond_init_instr(korp_cond *cond, const char*func_name); -#define vm_cond_init(cond) vm_cond_init_instr(cond, __FUNCTION__) -#else -#define vm_cond_init _vm_cond_init -#endif - -/** - * This function destroys condition variable - * - * @param cond pointer to condition variable - * - * @return 0 if success - */ -int _vm_cond_destroy(korp_cond *cond); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_cond_destroy_instr(korp_cond *cond, const char*func_name); -#define vm_cond_destroy(cond) vm_cond_destroy_instr(cond, __FUNCTION__) -#else -#define vm_cond_destroy _vm_cond_destroy -#endif - -/** - * This function will block on a condition varible. - * - * @param cond pointer to condition variable - * @param mutex pointer to mutex to protect the condition variable - * - * @return 0 if success - */ -int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_cond_wait_instr(korp_cond *cond, korp_mutex *mutex, const char*func_name); -#define vm_cond_wait(cond, mutex) vm_cond_wait_instr(cond, mutex, __FUNCTION__) -#else -#define vm_cond_wait _vm_cond_wait -#endif - -/** - * This function will block on a condition varible or return if time specified passes. - * - * @param cond pointer to condition variable - * @param mutex pointer to mutex to protect the condition variable - * @param mills milliseconds to wait - * - * @return 0 if success - */ -int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_cond_reltimedwait_instr(korp_cond *cond, korp_mutex *mutex, int mills, const char*func_name); -#define vm_cond_reltimedwait(cond, mutex, mills) vm_cond_reltimedwait_instr(cond, mutex, mills, __FUNCTION__) -#else -#define vm_cond_reltimedwait _vm_cond_reltimedwait -#endif - -/** - * This function signals the condition variable - * - * @param cond condition variable - * - * @return 0 if success - */ -int _vm_cond_signal(korp_cond *cond); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_cond_signal_instr(korp_cond *cond, const char*func_name); -#define vm_cond_signal(cond) vm_cond_signal_instr(cond, __FUNCTION__) -#else -#define vm_cond_signal _vm_cond_signal -#endif - -int _vm_cond_broadcast(korp_cond *cond); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_cond_broadcast_instr(korp_cond *cond, const char*func_name); -#define vm_cond_broadcast(cond) vm_cond_broadcast_instr(cond, __FUNCTION__) -#else -#define vm_cond_broadcast _vm_cond_broadcast -#endif - -int _vm_thread_cancel(korp_tid thread); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_thread_cancel_instr(korp_tid thread, const char*func_name); -#define vm_thread_cancel(thread) vm_thread_cancel_instr(thread, __FUNCTION__) -#else -#define vm_thread_cancel _vm_thread_cancel -#endif - -int _vm_thread_join(korp_tid thread, void **value_ptr, int mills); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_thread_join_instr(korp_tid thread, void **value_ptr, int mills, const char*func_name); -#define vm_thread_join(thread, value_ptr, mills) vm_thread_join_instr(thread, value_ptr, mills, __FUNCTION__) -#else -#define vm_thread_join _vm_thread_join -#endif - -int _vm_thread_detach(korp_tid thread); -#ifdef _INSTRUMENT_TEST_ENABLED -int vm_thread_detach_instr(korp_tid thread, const char*func_name); -#define vm_thread_detach(thread) vm_thread_detach_instr(thread, __FUNCTION__) -#else -#define vm_thread_detach _vm_thread_detach -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* #ifndef _BH_THREAD_H */ diff --git a/core/shared/platform/include/bh_time.h b/core/shared/platform/include/bh_time.h deleted file mode 100644 index e2fafbd5e8..0000000000 --- a/core/shared/platform/include/bh_time.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_TIME_H -#define _BH_TIME_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_platform.h" - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -extern uint64 _bh_time_get_tick_millisecond(void); -#ifdef _INSTRUMENT_TEST_ENABLED -extern uint64 bh_time_get_tick_millisecond_instr(const char*func_name); -#define bh_time_get_tick_millisecond() bh_time_get_tick_millisecond_instr(__FUNCTION__) -#else -#define bh_time_get_tick_millisecond _bh_time_get_tick_millisecond -#endif - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -extern uint64 _bh_time_get_boot_millisecond(void); -#ifdef _INSTRUMENT_TEST_ENABLED -extern uint64 bh_time_get_boot_millisecond_instr(const char*func_name); -#define bh_time_get_boot_millisecond() bh_time_get_boot_millisecond_instr(__FUNCTION__) -#else -#define bh_time_get_boot_millisecond _bh_time_get_boot_millisecond -#endif - -extern uint32 bh_get_tick_sec(); -#define bh_get_tick_ms _bh_time_get_boot_millisecond - -/* - * This function returns GMT milliseconds since from 1970.1.1, AKA UNIX time. - * @return milliseconds since from 1970.1.1. - */ -extern uint64 _bh_time_get_millisecond_from_1970(void); -#ifdef _INSTRUMENT_TEST_ENABLED -extern uint64 bh_time_get_millisecond_from_1970_instr(const char*func_name); -#define bh_time_get_millisecond_from_1970() bh_time_get_millisecond_from_1970_instr(__FUNCTION__) -#else -#define bh_time_get_millisecond_from_1970 _bh_time_get_millisecond_from_1970 -#endif - -/** - * This function sets timezone with specific hours. - * - * @param hours represents the deviation (in hours) of the local time from GMT (can be a positive or a negative number) - * @param half_hour if true, adds half an hour to the local time calculation. For example, if hours=(+5) then the time will be GMT +5:30; if hours=(-5) then the time will be GMT -4:30. - * @param daylight_save if true, applies the daylight saving scheme when calculating the local time (adds one hour to the local time calculation) - */ -extern void bh_set_timezone(int hours, int half_hour, int daylight_save); - -/** - * This functions returns the offset in seconds which needs to be added GMT to get the local time. - * - * - * @return offset in secords which needs to be added GMT to get the local time. - */ -extern int bh_get_timezone_offset(void); - -size_t bh_time_strftime(char *s, size_t max, const char *format, int64 time); - -#ifdef _INSTRUMENT_TEST_ENABLED -size_t bh_time_strftime_instr(char *s, size_t max, const char *format, int64 time, const char*func_name); -#define bh_time_strftime(s, max, format, time) bh_time_strftime_instr(s, max, format, time, __FUNCTION__) -#else -#define bh_time_strftime _bh_time_strftime -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/shared/platform/include/bh_types.h b/core/shared/platform/include/bh_types.h deleted file mode 100644 index 4c4214e0fa..0000000000 --- a/core/shared/platform/include/bh_types.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_TYPES_H -#define _BH_TYPES_H - -#include "bh_config.h" - -typedef unsigned char uint8; -typedef char int8; -typedef unsigned short uint16; -typedef short int16; -typedef unsigned int uint32; -typedef int int32; -typedef float float32; -typedef double float64; - -#include "bh_platform.h" - -#ifndef NULL -#define NULL (void*)0 -#endif - -#ifndef __cplusplus -#define true 1 -#define false 0 -#define inline __inline -#endif - -#endif - diff --git a/core/shared/platform/include/platform_api_extension.h b/core/shared/platform/include/platform_api_extension.h new file mode 100644 index 0000000000..41ec5742bc --- /dev/null +++ b/core/shared/platform/include/platform_api_extension.h @@ -0,0 +1,1696 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef PLATFORM_API_EXTENSION_H +#define PLATFORM_API_EXTENSION_H + +#include "platform_common.h" +#include "platform_wasi_types.h" +/** + * The related data structures should be defined + * in platform_internal.h + **/ +#include "platform_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************** + * * + * Extension interface * + * * + ***************************************************/ + +/**************************************************** + * Section 1 * + * Multi thread support * + ****************************************************/ + +/** + * NOTES: + * 1. If you are building VM core only, it must be implemented to + * enable multi-thread support, otherwise no need to implement it + * 2. To build the app-mgr and app-framework, you must implement it + */ + +/** + * Creates a thread + * + * @param p_tid [OUTPUT] the pointer of tid + * @param start main routine of the thread + * @param arg argument passed to main routine + * @param stack_size bytes of stack size + * + * @return 0 if success. + */ +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size); + +/** + * Creates a thread with priority + * + * @param p_tid [OUTPUT] the pointer of tid + * @param start main routine of the thread + * @param arg argument passed to main routine + * @param stack_size bytes of stack size + * @param prio the priority + * + * @return 0 if success. + */ +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio); + +/** + * Waits for the thread specified by thread to terminate + * + * @param thread the thread to wait + * @param retval if not NULL, output the exit status of the terminated thread + * + * @return return 0 if success + */ +int +os_thread_join(korp_tid thread, void **retval); + +/** + * Detach the thread specified by thread + * + * @param thread the thread to detach + * + * @return return 0 if success + */ +int os_thread_detach(korp_tid); + +/** + * Exit current thread + * + * @param retval the return value of the current thread + */ +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(except the CCAC compiler + provided by MetaWare, which doesn't support atomic operations) */ +#if defined(__clang__) && !defined(__CCAC__) +/* 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 +#elif __GNUC_PREREQ(4, 7) +#define os_memory_order_acquire __ATOMIC_ACQUIRE +#define os_memory_order_release __ATOMIC_RELEASE +#define os_memory_order_seq_cst __ATOMIC_SEQ_CST +#define os_atomic_thread_fence __atomic_thread_fence +#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_acquire memory_order_acquire +#define os_memory_order_release memory_order_release +#define os_memory_order_seq_cst memory_order_seq_cst +#define os_atomic_thread_fence atomic_thread_fence +#define os_atomic_cmpxchg atomic_compare_exchange_strong +#endif + +#endif /* end of os_atomic_thread_fence */ + +/** + * Initialize current thread environment if current thread + * is created by developer but not runtime + * + * @return 0 if success, -1 otherwise + */ +int +os_thread_env_init(void); + +/** + * Destroy current thread environment + */ +void +os_thread_env_destroy(void); + +/** + * Whether the thread environment is initialized + */ +bool +os_thread_env_inited(void); + +/** + * Suspend execution of the calling thread for (at least) + * usec microseconds + * + * @return 0 if success, -1 otherwise + */ +int +os_usleep(uint32 usec); + +/** + * Creates a recursive mutex + * + * @param mutex [OUTPUT] pointer to mutex initialized. + * + * @return 0 if success + */ +int +os_recursive_mutex_init(korp_mutex *mutex); + +/** + * This function creates a condition variable + * + * @param cond [OUTPUT] pointer to condition variable + * + * @return 0 if success + */ +int +os_cond_init(korp_cond *cond); + +/** + * This function destroys condition variable + * + * @param cond pointer to condition variable + * + * @return 0 if success + */ +int +os_cond_destroy(korp_cond *cond); + +/** + * Wait a condition variable. + * + * @param cond pointer to condition variable + * @param mutex pointer to mutex to protect the condition variable + * + * @return 0 if success + */ +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex); + +/** + * Wait a condition variable or return if time specified passes. + * + * @param cond pointer to condition variable + * @param mutex pointer to mutex to protect the condition variable + * @param useconds microseconds to wait + * + * @return 0 if success + */ +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds); + +/** + * Signals the condition variable + * + * @param cond condition variable + * + * @return 0 if success + */ +int +os_cond_signal(korp_cond *cond); + +/** + * Broadcast the condition variable + * + * @param cond condition variable + * + * @return 0 if success + */ +int +os_cond_broadcast(korp_cond *cond); + +/** + * Initialize readwrite lock object + * + * @param cond [OUTPUT] pointer to a lock object variable + * + * @return 0 if success + */ +int +os_rwlock_init(korp_rwlock *lock); + +/** + * Acquire the read lock + * + * @param lock lock variable + * + * @return 0 if success + */ +int +os_rwlock_rdlock(korp_rwlock *lock); + +/** + * Acquire the write lock + * + * @param lock lock variable + * + * @return 0 if success + */ +int +os_rwlock_wrlock(korp_rwlock *lock); + +/** + * Unlocks the lock object + * + * @param lock lock variable + * + * @return 0 if success + */ +int +os_rwlock_unlock(korp_rwlock *lock); + +/** + * Destroy a lock object + * + * @param lock lock variable + * + * @return 0 if success + */ +int +os_rwlock_destroy(korp_rwlock *lock); + +/** + * Creates a new POSIX-like semaphore or opens an existing + * semaphore. The semaphore is identified by name. For details of + * the construction of name, please refer to + * https://man7.org/linux/man-pages/man3/sem_open.3.html. + * + * @param name semaphore name + * @param oflasg specifies flags that control the operation of the call + * @param mode permission flags + * @param val initial value of the named semaphore. + * + * @return korp_sem * if success, NULL otherwise + */ +korp_sem * +os_sem_open(const char *name, int oflags, int mode, int val); + +/** + * Closes the named semaphore referred to by sem, + * allowing any resources that the system has allocated to the + * calling process for this semaphore to be freed. + * + * @param sem + * + * @return 0 if success + */ +int +os_sem_close(korp_sem *sem); + +/** + * Decrements (locks) the semaphore pointed to by sem. + * If the semaphore's value is greater than zero, then the decrement + * proceeds, and the function returns, immediately. If the + * semaphore currently has the value zero, then the call blocks + * until either it becomes possible to perform the decrement (i.e., + * the semaphore value rises above zero), or a signal handler + * interrupts the call. + * + * @return 0 if success + */ +int +os_sem_wait(korp_sem *sem); + +/** + * Is the same as sem_wait(), except that if the + * decrement cannot be immediately performed, then call returns an + * error (errno set to EAGAIN) instead of blocking. + * + * @return 0 if success + */ +int +os_sem_trywait(korp_sem *sem); + +/** + * Increments (unlocks) the semaphore pointed to by sem. + * If the semaphore's value consequently becomes greater than zero, + * then another process or thread blocked in a sem_wait(3) call will + * be woken up and proceed to lock the semaphore. + * + * @return 0 if success + */ +int +os_sem_post(korp_sem *sem); + +/** + * Places the current value of the semaphore pointed + * to sem into the integer pointed to by sval. + * + * @return 0 if success + */ +int +os_sem_getvalue(korp_sem *sem, int *sval); + +/** + * Remove the named semaphore referred to by name. + * The semaphore name is removed immediately. The semaphore is + * destroyed once all other processes that have the semaphore open + * close it. + * + * @param name semaphore name + * + * @return 0 if success + */ +int +os_sem_unlink(const char *name); + +/** + * Initialize process-global state for os_wakeup_blocking_op. + */ +int +os_blocking_op_init(void); + +/** + * Start accepting os_wakeup_blocking_op requests for the calling thread. + */ +void +os_begin_blocking_op(void); + +/** + * Stop accepting os_wakeup_blocking_op requests for the calling thread. + */ +void +os_end_blocking_op(void); + +/** + * Wake up the specified thread. + * + * For example, on posix-like platforms, this can be implemented by + * sending a signal (w/o SA_RESTART) which interrupts a blocking + * system call. + */ +int +os_wakeup_blocking_op(korp_tid tid); + +/**************************************************** + * Section 2 * + * Socket support * + ****************************************************/ + +/** + * NOTES: + * Socket APIs are required by source debugging feature. + * If you don't need source debugging feature, then no + * need to implement these APIs + */ + +typedef union { + uint32 ipv4; + uint16 ipv6[8]; + uint8 data[1]; +} bh_ip_addr_buffer_t; + +typedef struct { + bh_ip_addr_buffer_t addr_buffer; + uint16 port; + bool is_ipv4; +} bh_sockaddr_t; + +/** + * Create a socket + * + * @param sock [OUTPUT] the pointer of socket + * @param is_ipv4 true for IPv4, false for IPv6 + * @param is_tcp true for tcp, false for udp + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp); + +/** + * Assign the address and port to the socket + * + * @param socket the socket to bind + * @param addr the ip address, only IPv4 supported currently + * @param port [INPUT/OUTPUT] the port number, if the value is 0, + * it will use a port assigned by OS. On return it will + * contain the actual bound port number + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_bind(bh_socket_t socket, const char *addr, int *port); + +/** + * Set timeout for the given socket + * + * @param socket the socket to set timeout + * @param timeout_us timeout in microseconds + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_settimeout(bh_socket_t socket, uint64 timeout_us); + +/** + * Make the socket as a passive socket to accept incoming connection requests + * + * @param socket the socket to listen + * @param max_client maximum clients + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_listen(bh_socket_t socket, int max_client); + +/** + * Accept an incoming connection + * + * @param server_sock the socket to accept new connections + * @param sock [OUTPUT] the connected socket + * @param addr [OUTPUT] the address of the peer socket. If addr is NULL, + * nothing is filled in, and addrlen will not be used + * @param addrlen [INPUT/OUTPUT] the size (in bytes) of the structure + * pointed to by addr, on return it will contain the actual + * size of the peer address + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen); + +/** + * initiate a connection on a socket + * + * @param socket the socket to connect with + * @param addr the ip address, only IPv4 supported currently + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_connect(bh_socket_t socket, const char *addr, int port); + +/** + * Blocking receive message from a socket. + * + * @param socket the socket to receive message from + * @param buf the buffer to store the data + * @param len length of the buffer, this API does not guarantee that + * [len] bytes are received + * + * @return number of bytes received if success, -1 otherwise + */ +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len); + +/** + * Blocking receive message from a socket. + * + * @param socket the socket to send message + * @param buf the buffer to store the data + * @param len length of the buffer, this API does not guarantee that + * [len] bytes are received + * @param flags control the operation + * @param src_addr source address + * + * @return number of bytes sent if success, -1 otherwise + */ +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr); + +/** + * Blocking send message on a socket + * + * @param socket the socket to send message + * @param buf the buffer of data to be sent + * @param len length of the buffer + * + * @return number of bytes sent if success, -1 otherwise + */ +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len); + +/** + * Blocking send message on a socket to the target address + * + * @param socket the socket to send message + * @param buf the buffer of data to be sent + * @param len length of the buffer + * @param flags control the operation + * @param dest_addr target address + * + * @return number of bytes sent if success, -1 otherwise + */ +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr); + +/** + * Close a socket + * + * @param socket the socket to be closed + * + * @return always return 0 + */ +int +os_socket_close(bh_socket_t socket); + +/** + * Shutdown a socket + * + * @param socket the socket to be shutdown + * + * @return returns corresponding error code + */ +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket); + +/** + * converts cp into a number in host byte order suitable for use as + * an Internet network address + * + * @param is_ipv4 a flag that indicates whether the string is an IPv4 or + * IPv6 address + * + * @param cp a string in IPv4 numbers-and-dots notation or IPv6 + * numbers-and-colons notation + * + * @param out an output buffer to store binary address + * + * @return On success, the function returns 0. + * If the input is invalid, -1 is returned + */ +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out); + +typedef struct { + bh_sockaddr_t sockaddr; + uint8_t is_tcp; +} bh_addr_info_t; + +/** + * Resolve a host a hostname and a service to one or more IP addresses + * + * @param host a host to resolve + * + * @param service a service to find a port for + * + * @param hint_is_tcp an optional flag that determines a preferred socket type + (TCP or UDP). + * + * @param hint_is_ipv4 an optional flag that determines a preferred address + family (IPv4 or IPv6) + * + * @param addr_info a buffer for resolved addresses + * + * @param addr_info_size a size of the buffer for resolved addresses + + * @param max_info_size a maximum number of addresses available (can be bigger + or smaller than buffer size) + + * @return On success, the function returns 0; otherwise, it returns -1 + */ +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size); + +/** + * Returns an binary address and a port of the local socket + * + * @param socket the local socket + * + * @param sockaddr a buffer for storing the address + * + * @return On success, returns 0; otherwise, it returns -1. + */ +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr); + +/** + * Returns an binary address and a port of the remote socket + * + * @param socket the remote socket + * + * @param sockaddr a buffer for storing the address + * + * @return On success, returns 0; otherwise, it returns -1. + */ +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr); + +/** + * Set the maximum send buffer size. + * + * @param socket the socket to set + * @param bufsiz requested kernel buffer size + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz); + +/** + * Get the maximum send buffer size. + * + * @param socket the socket to set + * @param bufsiz the returned kernel buffer size + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz); + +/** + * Set the maximum receive buffer size. + * + * @param socket the socket to set + * @param bufsiz requested kernel buffer size + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz); + +/** + * Get the maximum receive buffer size. + * + * @param socket the socket to set + * @param bufsiz the returned kernel buffer size + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz); + +/** + * Enable sending of keep-alive messages on connection-oriented sockets + * + * @param socket the socket to set the flag + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled); + +/** + * Get if sending of keep-alive messages on connection-oriented sockets is + * enabled + * + * @param socket the socket to check + * @param is_enabled 1 if enabled or 0 if disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled); + +/** + * Set the send timeout until reporting an error + * + * @param socket the socket to set + * @param time_us microseconds until timeout + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us); + +/** + * Get the send timeout until reporting an error + * + * @param socket the socket to set + * @param time_us the returned microseconds until timeout + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us); + +/** + * Set the recv timeout until reporting an error + * + * @param socket the socket to set + * @param time_us microseconds until timeout + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us); + +/** + * Get the recv timeout until reporting an error + * + * @param socket the socket to set + * @param time_us the returned microseconds until timeout + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us); + +/** + * Enable reuse of local addresses + * + * @param socket the socket to set + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled); + +/** + * Get whether reuse of local addresses is enabled + * + * @param socket the socket to set + * @param is_enabled 1 for enabled or 0 for disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled); + +/** + * Enable reuse of local ports + * + * @param socket the socket to set + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled); + +/** + * Get whether reuse of local ports is enabled + * + * @param socket the socket to set + * @param is_enabled 1 for enabled or 0 for disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled); + +/** + * Set the linger options for the given socket + * + * @param socket the socket to set + * @param is_enabled whether linger is enabled + * @param linger_s linger time (seconds) + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s); + +/** + * Get the linger options for the given socket + * + * @param socket the socket to get + * @param is_enabled whether linger is enabled + * @param linger_s linger time (seconds) + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s); + +/** + * Set no delay TCP + * If set, disable the Nagle algorithm. + * This means that segments are always sent as soon as possible, + * even if there is only a small amount of data + * + * @param socket the socket to set the flag + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled); + +/** + * Get no delay TCP + * If set, disable the Nagle algorithm. + * This means that segments are always sent as soon as possible, + * even if there is only a small amount of data + * + * @param socket the socket to check + * @param is_enabled 1 if enabled or 0 if disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled); + +/** + * Enable/Disable tcp quickack mode + * In quickack mode, acks are sent immediately, rather than delayed if needed in + * accordance to normal TCP operation + * + * @param socket the socket to set the flag + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled); + +/** + * Enable/Disable tcp quickack mode + * In quickack mode, acks are sent immediately, rather than delayed if needed in + * accordance to normal TCP operation + * + * @param socket the socket to check + * @param is_enabled 1 if enabled or 0 if disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled); + +/** + * Set the time the connection needs to remain idle before sending keepalive + * probes + * + * @param socket the socket to set + * @param time_s seconds until keepalive probes are sent + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32_t time_s); + +/** + * Gets the time the connection needs to remain idle before sending keepalive + * probes + * + * @param socket the socket to check + * @param time_s seconds until keepalive probes are sent + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32_t *time_s); + +/** + * Set the time between individual keepalive probes + * + * @param socket the socket to set + * @param time_us seconds between individual keepalive probes + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32_t time_s); + +/** + * Get the time between individual keepalive probes + * + * @param socket the socket to get + * @param time_s seconds between individual keepalive probes + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32_t *time_s); + +/** + * Set use of TCP Fast Open + * + * @param socket the socket to set + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled); + +/** + * Get whether use of TCP Fast Open is enabled + * + * @param socket the socket to get + * @param is_enabled 1 to enabled or 0 to disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled); + +/** + * Set enable or disable IPv4 or IPv6 multicast loopback. + * + * @param socket the socket to set + * @param ipv6 true to set ipv6 loopback or false for ipv4 + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled); + +/** + * Get enable or disable IPv4 or IPv6 multicast loopback. + * + * @param socket the socket to check + * @param ipv6 true to set ipv6 loopback or false for ipv4 + * @param is_enabled 1 for enabled or 0 for disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, + bool *is_enabled); + +/** + * Add membership to a group + * + * @param socket the socket to add membership to + * @param imr_multiaddr the group multicast address (IPv4 or IPv6) + * @param imr_interface the interface to join on + * @param is_ipv6 whether the imr_multiaddr is IPv4 or IPv6 (true for IPv6) + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6); + +/** + * Drop membership of a group + * + * @param socket the socket to drop membership to + * @param imr_multiaddr the group multicast address (IPv4 or IPv6) + * @param imr_interface the interface to join on + * @param is_ipv6 whether the imr_multiaddr is IPv4 or IPv6 (true for IPv6) + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6); + +/** + * Set the current time-to-live field that is + * used in every packet sent from this socket. + * @param socket the socket to set the flag + * @param ttl_s time to live (seconds) + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s); + +/** + * Retrieve the current time-to-live field that is + * used in every packet sent from this socket. + * @param socket the socket to set the flag + * @param ttl_s time to live (seconds) + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s); + +/** + * Set the time-to-live value of outgoing multicast + * packets for this socket + * @param socket the socket to set the flag + * @param ttl_s time to live (seconds) + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s); + +/** + * Read the time-to-live value of outgoing multicast + * packets for this socket + * @param socket the socket to set the flag + * @param ttl_s time to live (seconds) + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s); + +/** + * Restrict to sending and receiving IPv6 packets only + * + * @param socket the socket to set + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled); + +/** + * Get whether only sending and receiving IPv6 packets + * + * @param socket the socket to check + * @param is_enabled 1 for enabled or 0 for disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled); + +/** + * Set whether broadcast is enabled + * When enabled, datagram sockets are allowed + * to send packets to a broadcast address. + * + * @param socket the socket to set the flag + * @param is_enabled 1 to enable or 0 to disable + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled); + +/** + * Get whether broadcast is enabled + * When enabled, datagram sockets are allowed + * to send packets to a broadcast address. + * + * @param socket the socket to check + * @param is_enabled 1 if enabled or 0 if disabled + * + * @return 0 if success, -1 otherwise + */ +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled); + +/** + * Dump memory information of the current process + * It may have variant implementations in different platforms + * + * @param out the output buffer. It is for sure the return content + * is a c-string which ends up with '\0' + * @param size the size of the output buffer + * + * @return 0 if success, -1 otherwise + */ +int +os_dumps_proc_mem_info(char *out, unsigned int size); + +/**************************************************** + * Section 3 * + * Filesystem support * + ****************************************************/ + +/** + * NOTES: + * Filesystem APIs are required for WASI libc support. If you don't need to + * support WASI libc, there is no need to implement these APIs. With a + * few exceptions, each filesystem function has been named after the equivalent + * POSIX filesystem function with an os_ prefix. + * + * Filesystem types + * + * os_raw_file_handle: the underlying OS file handle type e.g. int on POSIX + * systems and HANDLE on Windows. This type exists to allow embedders to provide + * custom file handles for stdout/stdin/stderr. + * + * os_file_handle: the file handle type used in the WASI libc fd + * table. Filesystem implementations can use it as a means to store any + * necessary platform-specific information which may not be directly available + * through the raw OS file handle. Similar to POSIX file descriptors, file + * handles may also refer to sockets, directories, symbolic links or character + * devices and any of the filesystem operations which make sense for these + * resource types should be supported as far as possible. + * + * os_dir_stream: a directory stream type in which filesystem implementations + * can store any necessary state to iterate over the entries in a directory. + */ + +/** + * Obtain information about an open file associated with the given handle. + * + * @param handle the handle for which to obtain file information + * @param buf a buffer in which to store the information + */ +__wasi_errno_t +os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf); + +/** + * Obtain information about an open file or directory. + * @param handle the directory handle from which to resolve the file/directory + * path + * @param path the relative path of the file or directory for which to obtain + * information + * @param buf a buffer in which to store the information + * @param follow_symlink whether to follow symlinks when resolving the path + */ +__wasi_errno_t +os_fstatat(os_file_handle handle, const char *path, + struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags); + +/** + * Obtain the file status flags for the provided handle. This is similar to the + * POSIX function fcntl called with the F_GETFL command. + * + * @param handle the handle for which to obtain the file status flags + * @param flags a pointer in which to store the output + */ +__wasi_errno_t +os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags); + +/** + * Set the file status flags for the provided handle. This is similar to the + * POSIX function fcntl called with the F_SETFL command. + * + * @param handle the handle for which to set the file status flags + * @param flags the flags to set + */ +__wasi_errno_t +os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags); + +/** + * Synchronize the data of a file to disk. + * + * @param handle + */ +__wasi_errno_t +os_fdatasync(os_file_handle handle); + +/** + * Synchronize the data and metadata of a file to disk. + * + * @param handle + */ +__wasi_errno_t +os_fsync(os_file_handle handle); + +/** + * Open a preopen directory. The path provided must refer to a directory and the + * returned handle will allow only readonly operations. + * + * @param path the path of the preopen directory to open + * @param out a pointer in which to store the newly opened handle + */ +__wasi_errno_t +os_open_preopendir(const char *path, os_file_handle *out); + +typedef uint8 wasi_libc_file_access_mode; +#define WASI_LIBC_ACCESS_MODE_READ_ONLY 0 +#define WASI_LIBC_ACCESS_MODE_WRITE_ONLY 1 +#define WASI_LIBC_ACCESS_MODE_READ_WRITE 2 + +/** + * Open a file or directory at the given path. + * + * @param handle a handle to the directory in which to open the new file or + * directory + * @param path the relative path of the file or directory to open + * @param oflags the flags to determine how the file or directory is opened + * @param fd_flags the flags to set on the returned handle + * @param lookup_flags whether to follow symlinks when resolving the path + * @param access_mode whether the file is opened as read only, write only or + * both + * @param out a pointer in which to store the newly opened handle + */ +__wasi_errno_t +os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fd_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode access_mode, os_file_handle *out); + +/** + * Obtain the file access mode for the provided handle. This is similar to the + * POSIX function fcntl called with the F_GETFL command combined with the + * O_ACCMODE mask. + * + * @param handle the handle for which to obtain the access mode + * @param access_mode a pointer in which to store the access mode + */ +__wasi_errno_t +os_file_get_access_mode(os_file_handle handle, + wasi_libc_file_access_mode *access_mode); + +/** + * Close the provided handle. If is_stdio is true, the raw file handle + * associated with the given file handle will not be closed. + * + * @param handle the handle to close + * @param is_stdio whether the provided handle refers to a stdio device + */ +__wasi_errno_t +os_close(os_file_handle handle, bool is_stdio); + +/** + * Read data from the provided handle at the given offset into multiple buffers. + * + * @param handle the handle to read from + * @param iov the buffers to read into + * @param iovcnt the number of buffers to read into + * @param offset the offset to read from + * @param nread a pointer in which to store the number of bytes read + */ +__wasi_errno_t +os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread); + +/** + * Write data from multiple buffers at the given offset to the provided handle. + * + * @param handle the handle to write to + * @param iov the buffers to write from + * @param iovcnt the number of buffers to write from + * @param offset the offset to write from + * @param nwritten a pointer in which to store the number of bytes written + */ +__wasi_errno_t +os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten); + +/** + * Read data from the provided handle into multiple buffers. + * + * @param handle the handle to read from + * @param iov the buffers to read into + * @param iovcnt the number of buffers to read into + * @param nread a pointer in which to store the number of bytes read + */ +__wasi_errno_t +os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + size_t *nread); + +/** + * Write data from multiple buffers to the provided handle. + * + * @param handle the handle to write to + * @param iov the buffers to write from + * @param iovcnt the number of buffers to write from + * @param nwritten a pointer in which to store the number of bytes written + */ +__wasi_errno_t +os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten); + +/** + * Allocate storage space for the file associated with the provided handle. This + * is similar to the POSIX function posix_fallocate. + * + * @param handle the handle to allocate space for + * @param offset the offset to allocate space at + * @param length the amount of space to allocate + */ +__wasi_errno_t +os_fallocate(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length); + +/** + * Adjust the size of an open file. + * + * @param handle the associated file handle for which to adjust the size + * @param size the new size of the file + */ +__wasi_errno_t +os_ftruncate(os_file_handle handle, __wasi_filesize_t size); + +/** + * Set file access and modification times on an open file or directory. + * + * @param handle the associated file handle for which to adjust the + * access/modification times + * @param access_time the timestamp for the new access time + * @param modification_time the timestamp for the new modification time + * @param fstflags a bitmask to indicate which timestamps to adjust + */ +__wasi_errno_t +os_futimens(os_file_handle handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags); + +/** + * Set file access and modification times on an open file or directory. + * + * @param handle the directory handle from which to resolve the path + * @param path the relative path of the file or directory for which to adjust + * the access/modification times + * @param access_time the timestamp for the new access time + * @param modification_time the timestamp for the new modification time + * @param fstflags a bitmask to indicate which timestamps to adjust + * @param lookup_flags whether to follow symlinks when resolving the path + */ +__wasi_errno_t +os_utimensat(os_file_handle handle, const char *path, + __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags, + __wasi_lookupflags_t lookup_flags); + +/** + * Read the contents of a symbolic link relative to the provided directory + * handle. + * + * @param handle the directory handle + * @param path the relative path of the symbolic link from which to read + * @param buf the buffer to read the link contents into + * @param bufsize the size of the provided buffer + * @param nread a pointer in which to store the number of bytes read into the + * buffer + */ +__wasi_errno_t +os_readlinkat(os_file_handle handle, const char *path, char *buf, + size_t bufsize, size_t *nread); + +/** + * Create a link from one path to another path. + * + * @param from_handle the directory handle from which to resolve the origin path + * @param from_path the origin path to link from + * @param to_handle the directory handle from which to resolve the destination + * path + * @param to_path the destination path at which to create the link + * @param lookup_flags whether to follow symlinks when resolving the origin path + */ +__wasi_errno_t +os_linkat(os_file_handle from_handle, const char *from_path, + os_file_handle to_handle, const char *to_path, + __wasi_lookupflags_t lookup_flags); + +/** + * Create a symbolic link from one path to another path. + * + * @param old_path the symbolic link contents + * @param handle the directory handle from which to resolve the destination path + * @param new_path the destination path at which to create the symbolic link + */ +__wasi_errno_t +os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path); + +/** + * Create a directory relative to the provided directory handle. + * + * @param handle the directory handle + * @param path the relative path of the directory to create + */ +__wasi_errno_t +os_mkdirat(os_file_handle handle, const char *path); + +/** + * Rename a file or directory. + * + * @param old_handle the directory handle from which to resolve the old path + * @param old_path the source path to rename + * @param new_handle the directory handle from which to resolve the destination + * path + * @param new_path the destination path to which to rename the file or directory + */ +__wasi_errno_t +os_renameat(os_file_handle old_handle, const char *old_path, + os_file_handle new_handle, const char *new_path); + +/** + * Unlink a file or directory. + * + * @param handle the directory handle from which to resolve the path + * @param path the relative path of the file or directory to unlink + * @param is_dir whether the provided handle refers to a directory or file + */ +__wasi_errno_t +os_unlinkat(os_file_handle handle, const char *path, bool is_dir); + +/** + * Move the read/write offset of an open file. + * + * @param handle the associated file handle for which to adjust the offset + * @param offset the number of bytes to adjust the offset by + * @param whence the position whence to adjust the offset + * @param new_offset a pointer in which to store the new offset + */ +__wasi_errno_t +os_lseek(os_file_handle handle, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *new_offset); + +/** + * Provide file advisory information for the given handle. This is similar to + * the POSIX function posix_fadvise. + * + * @param handle the associated file handle for which to provide advisory + * information + * @param offset the offset within the file to which the advisory + * information applies + * @param length the length of the region for which the advisory information + * applies + * @param advice the advice to provide + */ +__wasi_errno_t +os_fadvise(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length, __wasi_advice_t advice); + +/** + * Determine if the given handle refers to a terminal device. __WASI_ESUCCESS + * will be returned if the handle is associated with a terminal device, + * otherwise an appropriate error code will be returned. + * + * @param handle + */ +__wasi_errno_t +os_isatty(os_file_handle handle); + +/** + * Converts a raw file handle to STDIN to a corresponding file handle to STDIN. + * If the provided raw file handle is invalid, the platform-default raw handle + * for STDIN will be used. + * + * @param raw_stdin a raw file handle to STDIN + * + * @return a handle to STDIN + */ +os_file_handle +os_convert_stdin_handle(os_raw_file_handle raw_stdin); + +/** + * Converts a raw file handle to STDOUT to a corresponding file handle to + * STDOUT. If the provided raw file handle is invalid, the platform-default raw + * handle for STDOUT will be used. + * + * @param raw_stdout a raw file handle to STDOUT + * + * @return a handle to STDOUT + */ +os_file_handle +os_convert_stdout_handle(os_raw_file_handle raw_stdout); + +/** + * Converts a raw file handle to STDERR to a corresponding file handle to + * STDERR. If the provided raw file handle is invalid, the platform-default raw + * handle for STDERR will be used. + * + * @param raw_stderr a raw file handle to STDERR + * + * @return a handle to STDERR + */ +os_file_handle +os_convert_stderr_handle(os_raw_file_handle raw_stderr); + +/** + * + * @param fd a file handle + * + * @return true if it is stdin + */ +bool +os_is_stdin_handle(os_file_handle fd); + +/** + * + * @param fd a file handle + * + * @return true if it is stdout + */ +bool +os_is_stdout_handle(os_file_handle fd); + +/** + * + * @param fd a file handle + * + * @return true if it is stderr + */ +bool +os_is_stderr_handle(os_file_handle fd); + +/** + * Open a directory stream for the provided directory handle. The returned + * directory stream will be positioned at the first entry in the directory. + * + * @param handle the directory handle + * @param dir_stream a pointer in which to store the new directory stream + */ +__wasi_errno_t +os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream); + +/** + * Reset the position of a directory stream to the beginning of the directory. + * + * @param dir_stream the directory stream for which to reset the position + */ +__wasi_errno_t +os_rewinddir(os_dir_stream dir_stream); + +/** + * Set the position of the given directory stream. + * + * @param dir_stream the directory stream for which to set the position + * @param position the position to set + */ +__wasi_errno_t +os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position); + +/** + * Read a directory entry from the given directory stream. The directory name + * will be NULL if the end of the directory is reached or an error is + * encountered. + * + * @param dir_stream the directory stream from which to read the entry + * @param entry a pointer in which to store the directory entry + * @param d_name a pointer in which to store the directory entry name + */ +__wasi_errno_t +os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, + const char **d_name); + +/** + * Close the given directory stream. The handle associated with the directory + * stream will also be closed. + * + * @param dir_stream the directory stream to close + */ +__wasi_errno_t +os_closedir(os_dir_stream dir_stream); + +/** + * Returns an invalid directory stream that is guaranteed to cause failure when + * called with any directory filesystem operation. + * + * @return the invalid directory stream + */ +os_dir_stream +os_get_invalid_dir_stream(void); + +/** + * Checks whether the given directory stream is valid. An invalid directory + * stream is guaranteed to cause failure when called with any directory + * filesystem operation. + * + * @param dir_stream a pointer to a directory stream + */ +bool +os_is_dir_stream_valid(os_dir_stream *dir_stream); + +/** + * Returns an invalid handle that is guaranteed to cause failure when + * called with any filesystem operation. + * + * @return the invalid handle + */ +os_file_handle +os_get_invalid_handle(void); + +/** + * Returns an invalid raw file handle that is guaranteed to cause failure when + * called with any filesystem operation. + * + * @return the invalid raw file handle + */ +os_raw_file_handle +os_invalid_raw_handle(void); + +/** + * Checks whether the given file handle is valid. An invalid handle is + * guaranteed to cause failure when called with any filesystem operation. + * + * @param handle a pointer to a file handle + */ +bool +os_is_handle_valid(os_file_handle *handle); + +/** + * Resolve a pathname. The generated pathname will be stored as a + * null-terminated string, with a maximum length of PATH_MAX bytes. + * + * @param path the path to resolve + * @param resolved_path the buffer to store the resolved path in + * + * @return the resolved path if success, NULL otherwise + */ +char * +os_realpath(const char *path, char *resolved_path); + +/**************************************************** + * Section 4 * + * Clock functions * + ****************************************************/ + +/** + * NOTES: + * Clock functions are required for WASI libc support. If you don't need to + * support WASI libc, there is no need to implement these APIs. + */ + +/** + * Get the resolution of the specified clock. + * + * @param clock_id clock identifier + * @param resolution output variable to store the clock resolution + */ +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution); + +/** + * Get the current time of the specified clock. + * + * @param clock_id clock identifier + * @param precision the maximum lag that the returned time value may have, + * compared to its actual value. + * @param time output variable to store the clock time + */ +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time); + +#ifdef __cplusplus +} +#endif + +/* Experimental */ + +/* Used in posix.c around L2259 and expect the return code + * of ioctl() directly. + */ +int +os_ioctl(os_file_handle handle, int request, ...); + +/* Higher level API: + * __wasi_errno_t + * blocking_op_poll(wasm_exec_env_t exec_env, os_poll_file_handle *pfds, + * os_nfds_t nfds, int timeout_ms, int *retp) + * Already format the errno and expect the return code of poll() directly. + */ +int +os_poll(os_poll_file_handle *pfds, os_nfds_t nfs, int timeout); + +bool +os_compare_file_handle(os_file_handle handle1, os_file_handle handle2); + +#endif /* #ifndef PLATFORM_API_EXTENSION_H */ diff --git a/core/shared/platform/include/platform_api_vmcore.h b/core/shared/platform/include/platform_api_vmcore.h new file mode 100644 index 0000000000..1fa524f9e8 --- /dev/null +++ b/core/shared/platform/include/platform_api_vmcore.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_API_VMCORE_H +#define _PLATFORM_API_VMCORE_H + +#include "platform_common.h" +#include "platform_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************** + * Section 1 * + * Interfaces required by the runtime * + ****************************************************/ + +/** + * Initialize the platform internal resources if needed, + * this function is called by wasm_runtime_init() and + * wasm_runtime_full_init() + * + * @return 0 if success + */ +int +bh_platform_init(void); + +/** + * Destroy the platform internal resources if needed, + * this function is called by wasm_runtime_destroy() + */ +void +bh_platform_destroy(void); + +/** + ******** memory allocator APIs ********** + */ + +void * +os_malloc(unsigned size); + +void * +os_realloc(void *ptr, unsigned size); + +void +os_free(void *ptr); + +/** + * Note: the above APIs can simply return NULL if wasm runtime + * isn't initialized with Alloc_With_System_Allocator. + * Refer to wasm_runtime_full_init(). + */ + +int +os_printf(const char *format, ...); + +int +os_vprintf(const char *format, va_list ap); + +/** + * Get microseconds after boot. + */ +uint64 +os_time_get_boot_us(void); + +/** + * Get thread-specific CPU-time clock in microseconds + */ +uint64 +os_time_thread_cputime_us(void); + +/** + * Get current thread id. + * Implementation optional: Used by runtime for logging only. + */ +korp_tid +os_self_thread(void); + +/** + * Get current thread's stack boundary address, used for runtime + * to check the native stack overflow. Return NULL if it is not + * easy to implement, but may have potential issue. + */ +uint8 * +os_thread_get_stack_boundary(void); + +/** + * Set whether the MAP_JIT region write protection is enabled for this thread. + * Pass true to make the region executable, false to make it writable. + */ +void +os_thread_jit_write_protect_np(bool enabled); + +/** + ************** mutext APIs *********** + * vmcore: Not required until pthread is supported by runtime + * app-mgr: Must be implemented + */ + +int +os_mutex_init(korp_mutex *mutex); + +int +os_mutex_destroy(korp_mutex *mutex); + +int +os_mutex_lock(korp_mutex *mutex); + +int +os_mutex_unlock(korp_mutex *mutex); + +/************************************************** + * Section 2 * + * APIs required by WAMR AOT * + **************************************************/ + +/* Memory map modes */ +enum { + MMAP_PROT_NONE = 0, + MMAP_PROT_READ = 1, + MMAP_PROT_WRITE = 2, + MMAP_PROT_EXEC = 4 +}; + +/* Memory map flags */ +enum { + MMAP_MAP_NONE = 0, + /* Put the mapping into 0 to 2 G, supported only on x86_64 */ + MMAP_MAP_32BIT = 1, + /* Don't interpret addr as a hint: place the mapping at exactly + that address. */ + MMAP_MAP_FIXED = 2, +}; + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file); +void +os_munmap(void *addr, size_t size); +int +os_mprotect(void *addr, size_t size, int prot); + +static inline void * +os_mremap_slow(void *old_addr, size_t old_size, size_t new_size) +{ + void *new_memory = os_mmap(NULL, new_size, MMAP_PROT_WRITE | MMAP_PROT_READ, + 0, os_get_invalid_handle()); + if (!new_memory) { + return NULL; + } + /* + * bh_memcpy_s can't be used as it doesn't support values bigger than + * UINT32_MAX + */ + memcpy(new_memory, old_addr, new_size < old_size ? new_size : old_size); + os_munmap(old_addr, old_size); + + return new_memory; +} + +/* Doesn't guarantee that protection flags will be preserved. + os_mprotect() must be called after remapping. */ +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size); + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) +void * +os_get_dbus_mirror(void *ibus); +#endif + +/** + * Flush cpu data cache, in some CPUs, after applying relocation to the + * AOT code, the code may haven't been written back to the cpu data cache, + * which may cause unexpected behaviour when executing the AOT code. + * Implement this function if required, or just leave it empty. + */ +void +os_dcache_flush(void); + +/** + * Flush instruction cache. + */ +void +os_icache_flush(void *start, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _PLATFORM_API_VMCORE_H */ diff --git a/core/shared/platform/include/platform_common.h b/core/shared/platform/include/platform_common.h new file mode 100644 index 0000000000..28001af746 --- /dev/null +++ b/core/shared/platform/include/platform_common.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_COMMON_H +#define _PLATFORM_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform_internal.h" +#include "../../../config.h" + +#define BH_MAX_THREAD 32 + +#define BHT_ERROR (-1) +#define BHT_TIMED_OUT (1) +#define BHT_OK (0) + +#define BHT_WAIT_FOREVER ((uint64)-1LL) + +#define BH_KB (1024) +#define BH_MB ((BH_KB)*1024) +#define BH_GB ((BH_MB)*1024) + +#ifndef BH_MALLOC +#define BH_MALLOC os_malloc +#endif + +#ifndef BH_FREE +#define BH_FREE os_free +#endif + +#ifndef BH_TIME_T_MAX +#define BH_TIME_T_MAX LONG_MAX +#endif + +#if defined(_MSC_BUILD) +#if defined(COMPILING_WASM_RUNTIME_API) +__declspec(dllexport) void *BH_MALLOC(unsigned int size); +__declspec(dllexport) void BH_FREE(void *ptr); +#else +__declspec(dllimport) void *BH_MALLOC(unsigned int size); +__declspec(dllimport) void BH_FREE(void *ptr); +#endif +#else +void * +BH_MALLOC(unsigned int size); +void +BH_FREE(void *ptr); +#endif + +#if defined(BH_VPRINTF) +#if defined(MSVC) +__declspec(dllimport) int BH_VPRINTF(const char *format, va_list ap); +#else +int +BH_VPRINTF(const char *format, va_list ap); +#endif +#endif + +#ifndef NULL +#define NULL (void *)0 +#endif + +#if !defined(BH_HAS_DLFCN) +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) +#define BH_HAS_DLFCN 1 +#else +#define BH_HAS_DLFCN 0 +#endif +#endif + +#ifndef __cplusplus + +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + +#ifndef inline +#define inline __inline +#endif + +#endif + +/* Return the offset of the given field in the given type */ +#ifndef offsetof +/* GCC 4.0 and later has the builtin. */ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define offsetof(Type, field) __builtin_offsetof(Type, field) +#else +#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) +#endif +#endif + +typedef uint8_t uint8; +typedef int8_t int8; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint32_t uint32; +typedef int32_t int32; +typedef float float32; +typedef double float64; +typedef uint64_t uint64; +typedef int64_t int64; + +typedef void *(*thread_start_routine_t)(void *); + +#ifndef bh_socket_t +/* If no socket defined on current platform, + give a fake definition to make the compiler happy */ +#define bh_socket_t int +#endif + +/* Format specifiers macros in case + they are not provided by compiler */ +#ifndef __PRI64_PREFIX +#if UINTPTR_MAX == UINT64_MAX +#define __PRI64_PREFIX "l" +#define __PRIPTR_PREFIX "l" +#else +#define __PRI64_PREFIX "ll" +#define __PRIPTR_PREFIX +#endif +#endif /* #ifndef __PRI64_PREFIX */ + +/* Macros for printing format specifiers */ +#ifndef PRId32 +#define PRId32 "d" +#endif +#ifndef PRIi32 +#define PRIi32 "i" +#endif +#ifndef PRIu32 +#define PRIu32 "u" +#endif +#ifndef PRIx32 +#define PRIx32 "x" +#endif +#ifndef PRIX32 +#define PRIX32 "X" +#endif + +#ifndef PRId64 +#define PRId64 __PRI64_PREFIX "d" +#endif +#ifndef PRIu64 +#define PRIu64 __PRI64_PREFIX "u" +#endif +#ifndef PRIx64 +#define PRIx64 __PRI64_PREFIX "x" +#endif +#ifndef PRIX64 +#define PRIX64 __PRI64_PREFIX "X" +#endif +#ifndef PRIxPTR +#define PRIxPTR __PRIPTR_PREFIX "x" +#endif +#ifndef PRIXPTR +#define PRIXPTR __PRIPTR_PREFIX "X" +#endif + +/* Macros for scanning format specifiers */ +#ifndef SCNd32 +#define SCNd32 "d" +#endif +#ifndef SCNi32 +#define SCNi32 "i" +#endif +#ifndef SCNu32 +#define SCNu32 "u" +#endif +#ifndef SCNx32 +#define SCNx32 "x" +#endif + +#ifndef SCNd64 +#define SCNd64 __PRI64_PREFIX "d" +#endif +#ifndef SCNu64 +#define SCNu64 __PRI64_PREFIX "u" +#endif +#ifndef SCNx64 +#define SCNx64 __PRI64_PREFIX "x" +#endif +#ifndef SCNxPTR +#define SCNxPTR __PRIPTR_PREFIX "x" +#endif + +#ifndef NAN +#define NAN (0.0 / 0.0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _PLATFORM_COMMON_H */ diff --git a/core/shared/platform/include/platform_wasi_types.h b/core/shared/platform/include/platform_wasi_types.h new file mode 100644 index 0000000000..980d3f84ec --- /dev/null +++ b/core/shared/platform/include/platform_wasi_types.h @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * This file declares the WASI interface. The definitions of types, macros and + * structures in this file should be consistent with those in wasi-libc: + * https://github.com/WebAssembly/wasi-libc/blob/main/libc-bottom-half/headers/public/wasi/api.h + */ + +#ifndef _PLATFORM_WASI_TYPES_H +#define _PLATFORM_WASI_TYPES_H + +#include "../../../config.h" + +#include +#include + +/* clang-format off */ + +#ifdef __cplusplus +#ifndef _Static_assert +#define _Static_assert static_assert +#endif /* _Static_assert */ + +#ifndef _Alignof +#define _Alignof alignof +#endif /* _Alignof */ + +extern "C" { +#elif defined(_MSC_VER) && !_CRT_HAS_C11 +#define _Static_assert static_assert +#define _Alignof __alignof +#endif + +/* There is no need to check the WASI layout if we're using uvwasi or libc-wasi + * is not enabled at all. */ +#if WASM_ENABLE_UVWASI != 0 || WASM_ENABLE_LIBC_WASI == 0 +#define assert_wasi_layout(expr, message) /* nothing */ +#else +#define assert_wasi_layout(expr, message) _Static_assert(expr, message) +#endif + +assert_wasi_layout(_Alignof(int8_t) == 1, "non-wasi data layout"); +assert_wasi_layout(_Alignof(uint8_t) == 1, "non-wasi data layout"); +assert_wasi_layout(_Alignof(int16_t) == 2, "non-wasi data layout"); +assert_wasi_layout(_Alignof(uint16_t) == 2, "non-wasi data layout"); +assert_wasi_layout(_Alignof(int32_t) == 4, "non-wasi data layout"); +assert_wasi_layout(_Alignof(uint32_t) == 4, "non-wasi data layout"); +#if 0 +assert_wasi_layout(_Alignof(int64_t) == 8, "non-wasi data layout"); +assert_wasi_layout(_Alignof(uint64_t) == 8, "non-wasi data layout"); +#endif + +typedef uint32_t __wasi_size_t; +assert_wasi_layout(_Alignof(__wasi_size_t) == 4, "non-wasi data layout"); + +typedef uint8_t __wasi_advice_t; +#define __WASI_ADVICE_NORMAL (0) +#define __WASI_ADVICE_SEQUENTIAL (1) +#define __WASI_ADVICE_RANDOM (2) +#define __WASI_ADVICE_WILLNEED (3) +#define __WASI_ADVICE_DONTNEED (4) +#define __WASI_ADVICE_NOREUSE (5) + +typedef uint32_t __wasi_clockid_t; +#define __WASI_CLOCK_REALTIME (0) +#define __WASI_CLOCK_MONOTONIC (1) +#define __WASI_CLOCK_PROCESS_CPUTIME_ID (2) +#define __WASI_CLOCK_THREAD_CPUTIME_ID (3) + +typedef uint64_t __wasi_device_t; + +typedef uint64_t __wasi_dircookie_t; +#define __WASI_DIRCOOKIE_START (0) + +typedef uint32_t __wasi_dirnamlen_t; + +typedef uint16_t __wasi_errno_t; +#define __WASI_ESUCCESS (0) +#define __WASI_E2BIG (1) +#define __WASI_EACCES (2) +#define __WASI_EADDRINUSE (3) +#define __WASI_EADDRNOTAVAIL (4) +#define __WASI_EAFNOSUPPORT (5) +#define __WASI_EAGAIN (6) +#define __WASI_EALREADY (7) +#define __WASI_EBADF (8) +#define __WASI_EBADMSG (9) +#define __WASI_EBUSY (10) +#define __WASI_ECANCELED (11) +#define __WASI_ECHILD (12) +#define __WASI_ECONNABORTED (13) +#define __WASI_ECONNREFUSED (14) +#define __WASI_ECONNRESET (15) +#define __WASI_EDEADLK (16) +#define __WASI_EDESTADDRREQ (17) +#define __WASI_EDOM (18) +#define __WASI_EDQUOT (19) +#define __WASI_EEXIST (20) +#define __WASI_EFAULT (21) +#define __WASI_EFBIG (22) +#define __WASI_EHOSTUNREACH (23) +#define __WASI_EIDRM (24) +#define __WASI_EILSEQ (25) +#define __WASI_EINPROGRESS (26) +#define __WASI_EINTR (27) +#define __WASI_EINVAL (28) +#define __WASI_EIO (29) +#define __WASI_EISCONN (30) +#define __WASI_EISDIR (31) +#define __WASI_ELOOP (32) +#define __WASI_EMFILE (33) +#define __WASI_EMLINK (34) +#define __WASI_EMSGSIZE (35) +#define __WASI_EMULTIHOP (36) +#define __WASI_ENAMETOOLONG (37) +#define __WASI_ENETDOWN (38) +#define __WASI_ENETRESET (39) +#define __WASI_ENETUNREACH (40) +#define __WASI_ENFILE (41) +#define __WASI_ENOBUFS (42) +#define __WASI_ENODEV (43) +#define __WASI_ENOENT (44) +#define __WASI_ENOEXEC (45) +#define __WASI_ENOLCK (46) +#define __WASI_ENOLINK (47) +#define __WASI_ENOMEM (48) +#define __WASI_ENOMSG (49) +#define __WASI_ENOPROTOOPT (50) +#define __WASI_ENOSPC (51) +#define __WASI_ENOSYS (52) +#define __WASI_ENOTCONN (53) +#define __WASI_ENOTDIR (54) +#define __WASI_ENOTEMPTY (55) +#define __WASI_ENOTRECOVERABLE (56) +#define __WASI_ENOTSOCK (57) +#define __WASI_ENOTSUP (58) +#define __WASI_ENOTTY (59) +#define __WASI_ENXIO (60) +#define __WASI_EOVERFLOW (61) +#define __WASI_EOWNERDEAD (62) +#define __WASI_EPERM (63) +#define __WASI_EPIPE (64) +#define __WASI_EPROTO (65) +#define __WASI_EPROTONOSUPPORT (66) +#define __WASI_EPROTOTYPE (67) +#define __WASI_ERANGE (68) +#define __WASI_EROFS (69) +#define __WASI_ESPIPE (70) +#define __WASI_ESRCH (71) +#define __WASI_ESTALE (72) +#define __WASI_ETIMEDOUT (73) +#define __WASI_ETXTBSY (74) +#define __WASI_EXDEV (75) +#define __WASI_ENOTCAPABLE (76) + +#if defined(_MSC_VER) +#define ALIGNED_(x) __declspec(align(x)) +#define WARN_UNUSED _Check_return_ +#elif defined(__GNUC__) +#define ALIGNED_(x) __attribute__ ((aligned(x))) +#define WARN_UNUSED __attribute__((__warn_unused_result__)) +#endif + +#define ALIGNED_TYPE(t,x) typedef t ALIGNED_(x) + +typedef uint16_t __wasi_eventrwflags_t; +#define __WASI_EVENT_FD_READWRITE_HANGUP (0x0001) + +typedef uint8_t __wasi_eventtype_t; +#define __WASI_EVENTTYPE_CLOCK (0) +#define __WASI_EVENTTYPE_FD_READ (1) +#define __WASI_EVENTTYPE_FD_WRITE (2) + +typedef uint32_t __wasi_exitcode_t; + +typedef int32_t __wasi_fd_t; + +typedef uint16_t __wasi_fdflags_t; +#define __WASI_FDFLAG_APPEND (0x0001) +#define __WASI_FDFLAG_DSYNC (0x0002) +#define __WASI_FDFLAG_NONBLOCK (0x0004) +#define __WASI_FDFLAG_RSYNC (0x0008) +#define __WASI_FDFLAG_SYNC (0x0010) + +typedef int64_t __wasi_filedelta_t; + +typedef uint64_t __wasi_filesize_t; + +typedef uint8_t __wasi_filetype_t; +#define __WASI_FILETYPE_UNKNOWN (0) +#define __WASI_FILETYPE_BLOCK_DEVICE (1) +#define __WASI_FILETYPE_CHARACTER_DEVICE (2) +#define __WASI_FILETYPE_DIRECTORY (3) +#define __WASI_FILETYPE_REGULAR_FILE (4) +#define __WASI_FILETYPE_SOCKET_DGRAM (5) +#define __WASI_FILETYPE_SOCKET_STREAM (6) +#define __WASI_FILETYPE_SYMBOLIC_LINK (7) + +typedef uint16_t __wasi_fstflags_t; +#define __WASI_FILESTAT_SET_ATIM (0x0001) +#define __WASI_FILESTAT_SET_ATIM_NOW (0x0002) +#define __WASI_FILESTAT_SET_MTIM (0x0004) +#define __WASI_FILESTAT_SET_MTIM_NOW (0x0008) + +typedef uint64_t __wasi_inode_t; + +ALIGNED_TYPE(uint64_t, 8) __wasi_linkcount_t; + +typedef uint32_t __wasi_lookupflags_t; +#define __WASI_LOOKUP_SYMLINK_FOLLOW (0x00000001) + +typedef uint16_t __wasi_oflags_t; +#define __WASI_O_CREAT (0x0001) +#define __WASI_O_DIRECTORY (0x0002) +#define __WASI_O_EXCL (0x0004) +#define __WASI_O_TRUNC (0x0008) + +typedef uint16_t __wasi_riflags_t; +#define __WASI_SOCK_RECV_PEEK (0x0001) +#define __WASI_SOCK_RECV_WAITALL (0x0002) + +typedef uint64_t __wasi_rights_t; + +/** + * Observe that WASI defines rights in the plural form + * TODO: refactor to use RIGHTS instead of RIGHT + */ +#define __WASI_RIGHT_FD_DATASYNC ((__wasi_rights_t)(UINT64_C(1) << 0)) +#define __WASI_RIGHT_FD_READ ((__wasi_rights_t)(UINT64_C(1) << 1)) +#define __WASI_RIGHT_FD_SEEK ((__wasi_rights_t)(UINT64_C(1) << 2)) +#define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS ((__wasi_rights_t)(UINT64_C(1) << 3)) +#define __WASI_RIGHT_FD_SYNC ((__wasi_rights_t)(UINT64_C(1) << 4)) +#define __WASI_RIGHT_FD_TELL ((__wasi_rights_t)(UINT64_C(1) << 5)) +#define __WASI_RIGHT_FD_WRITE ((__wasi_rights_t)(UINT64_C(1) << 6)) +#define __WASI_RIGHT_FD_ADVISE ((__wasi_rights_t)(UINT64_C(1) << 7)) +#define __WASI_RIGHT_FD_ALLOCATE ((__wasi_rights_t)(UINT64_C(1) << 8)) +#define __WASI_RIGHT_PATH_CREATE_DIRECTORY ((__wasi_rights_t)(UINT64_C(1) << 9)) +#define __WASI_RIGHT_PATH_CREATE_FILE ((__wasi_rights_t)(UINT64_C(1) << 10)) +#define __WASI_RIGHT_PATH_LINK_SOURCE ((__wasi_rights_t)(UINT64_C(1) << 11)) +#define __WASI_RIGHT_PATH_LINK_TARGET ((__wasi_rights_t)(UINT64_C(1) << 12)) +#define __WASI_RIGHT_PATH_OPEN ((__wasi_rights_t)(UINT64_C(1) << 13)) +#define __WASI_RIGHT_FD_READDIR ((__wasi_rights_t)(UINT64_C(1) << 14)) +#define __WASI_RIGHT_PATH_READLINK ((__wasi_rights_t)(UINT64_C(1) << 15)) +#define __WASI_RIGHT_PATH_RENAME_SOURCE ((__wasi_rights_t)(UINT64_C(1) << 16)) +#define __WASI_RIGHT_PATH_RENAME_TARGET ((__wasi_rights_t)(UINT64_C(1) << 17)) +#define __WASI_RIGHT_PATH_FILESTAT_GET ((__wasi_rights_t)(UINT64_C(1) << 18)) +#define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE ((__wasi_rights_t)(UINT64_C(1) << 19)) +#define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES ((__wasi_rights_t)(UINT64_C(1) << 20)) +#define __WASI_RIGHT_FD_FILESTAT_GET ((__wasi_rights_t)(UINT64_C(1) << 21)) +#define __WASI_RIGHT_FD_FILESTAT_SET_SIZE ((__wasi_rights_t)(UINT64_C(1) << 22)) +#define __WASI_RIGHT_FD_FILESTAT_SET_TIMES ((__wasi_rights_t)(UINT64_C(1) << 23)) +#define __WASI_RIGHT_PATH_SYMLINK ((__wasi_rights_t)(UINT64_C(1) << 24)) +#define __WASI_RIGHT_PATH_REMOVE_DIRECTORY ((__wasi_rights_t)(UINT64_C(1) << 25)) +#define __WASI_RIGHT_PATH_UNLINK_FILE ((__wasi_rights_t)(UINT64_C(1) << 26)) +#define __WASI_RIGHT_POLL_FD_READWRITE ((__wasi_rights_t)(UINT64_C(1) << 27)) +#define __WASI_RIGHT_SOCK_CONNECT ((__wasi_rights_t)(UINT64_C(1) << 28)) +#define __WASI_RIGHT_SOCK_LISTEN ((__wasi_rights_t)(UINT64_C(1) << 29)) +#define __WASI_RIGHT_SOCK_BIND ((__wasi_rights_t)(UINT64_C(1) << 30)) +#define __WASI_RIGHT_SOCK_ACCEPT ((__wasi_rights_t)(UINT64_C(1) << 31)) +#define __WASI_RIGHT_SOCK_RECV ((__wasi_rights_t)(UINT64_C(1) << 32)) +#define __WASI_RIGHT_SOCK_SEND ((__wasi_rights_t)(UINT64_C(1) << 33)) +#define __WASI_RIGHT_SOCK_ADDR_LOCAL ((__wasi_rights_t)(UINT64_C(1) << 34)) +#define __WASI_RIGHT_SOCK_ADDR_REMOTE ((__wasi_rights_t)(UINT64_C(1) << 35)) +#define __WASI_RIGHT_SOCK_RECV_FROM ((__wasi_rights_t)(UINT64_C(1) << 36)) +#define __WASI_RIGHT_SOCK_SEND_TO ((__wasi_rights_t)(UINT64_C(1) << 37)) + +typedef uint16_t __wasi_roflags_t; +#define __WASI_SOCK_RECV_DATA_TRUNCATED (0x0001) + +typedef uint8_t __wasi_sdflags_t; +#define __WASI_SHUT_RD (0x01) +#define __WASI_SHUT_WR (0x02) + +typedef uint16_t __wasi_siflags_t; + +typedef uint8_t __wasi_signal_t; + +typedef uint16_t __wasi_subclockflags_t; +#define __WASI_SUBSCRIPTION_CLOCK_ABSTIME (0x0001) + +typedef uint64_t __wasi_timestamp_t; + +typedef uint64_t __wasi_userdata_t; + +typedef uint8_t __wasi_whence_t; +#define __WASI_WHENCE_SET (0) +#define __WASI_WHENCE_CUR (1) +#define __WASI_WHENCE_END (2) + +typedef uint8_t __wasi_preopentype_t; +#define __WASI_PREOPENTYPE_DIR (0) + +struct fd_table; +struct fd_prestats; +struct argv_environ_values; +struct addr_pool; + +typedef struct ALIGNED_(8) __wasi_dirent_t { + __wasi_dircookie_t d_next; + __wasi_inode_t d_ino; + __wasi_dirnamlen_t d_namlen; + __wasi_filetype_t d_type; +} __wasi_dirent_t; +assert_wasi_layout(offsetof(__wasi_dirent_t, d_next) == 0, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_dirent_t, d_ino) == 8, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_dirent_t, d_namlen) == 16, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_dirent_t, d_type) == 20, "non-wasi data layout"); +assert_wasi_layout(sizeof(__wasi_dirent_t) == 24, "non-wasi data layout"); +assert_wasi_layout(_Alignof(__wasi_dirent_t) == 8, "non-wasi data layout"); + +typedef struct ALIGNED_(8) __wasi_event_t { + __wasi_userdata_t userdata; + __wasi_errno_t error; + __wasi_eventtype_t type; + uint8_t __paddings[5]; + union __wasi_event_u { + struct __wasi_event_u_fd_readwrite_t { + __wasi_filesize_t nbytes; + __wasi_eventrwflags_t flags; + uint8_t __paddings[6]; + } fd_readwrite; + } u; +} __wasi_event_t; +assert_wasi_layout(offsetof(__wasi_event_t, userdata) == 0, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_event_t, error) == 8, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_event_t, type) == 10, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_event_t, u.fd_readwrite.nbytes) == 16, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_event_t, u.fd_readwrite.flags) == 24, "non-wasi data layout"); +assert_wasi_layout(sizeof(__wasi_event_t) == 32, "non-wasi data layout"); +assert_wasi_layout(_Alignof(__wasi_event_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_prestat_t { + __wasi_preopentype_t pr_type; + union __wasi_prestat_u { + struct __wasi_prestat_u_dir_t { + size_t pr_name_len; + } dir; + } u; +} __wasi_prestat_t; +assert_wasi_layout(offsetof(__wasi_prestat_t, pr_type) == 0, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 4, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 8, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + sizeof(__wasi_prestat_t) == 8, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + sizeof(__wasi_prestat_t) == 16, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + _Alignof(__wasi_prestat_t) == 4, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + _Alignof(__wasi_prestat_t) == 8, "non-wasi data layout"); + +typedef struct ALIGNED_(8) __wasi_fdstat_t { + __wasi_filetype_t fs_filetype; + __wasi_fdflags_t fs_flags; + uint8_t __paddings[4]; + __wasi_rights_t fs_rights_base; + __wasi_rights_t fs_rights_inheriting; +} __wasi_fdstat_t; +assert_wasi_layout( + offsetof(__wasi_fdstat_t, fs_filetype) == 0, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_fdstat_t, fs_flags) == 2, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_fdstat_t, fs_rights_base) == 8, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, + "non-wasi data layout"); +assert_wasi_layout(sizeof(__wasi_fdstat_t) == 24, "non-wasi data layout"); +assert_wasi_layout(_Alignof(__wasi_fdstat_t) == 8, "non-wasi data layout"); + +typedef struct ALIGNED_(8) __wasi_filestat_t { + __wasi_device_t st_dev; + __wasi_inode_t st_ino; + __wasi_filetype_t st_filetype; + __wasi_linkcount_t st_nlink; + __wasi_filesize_t st_size; + __wasi_timestamp_t st_atim; + __wasi_timestamp_t st_mtim; + __wasi_timestamp_t st_ctim; +} __wasi_filestat_t; +assert_wasi_layout(offsetof(__wasi_filestat_t, st_dev) == 0, "non-wasi data layout"); +assert_wasi_layout(offsetof(__wasi_filestat_t, st_ino) == 8, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_filestat_t, st_filetype) == 16, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_filestat_t, st_nlink) == 24, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_filestat_t, st_size) == 32, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_filestat_t, st_atim) == 40, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_filestat_t, st_mtim) == 48, "non-wasi data layout"); +assert_wasi_layout( + offsetof(__wasi_filestat_t, st_ctim) == 56, "non-wasi data layout"); +assert_wasi_layout(sizeof(__wasi_filestat_t) == 64, "non-wasi data layout"); +assert_wasi_layout(_Alignof(__wasi_filestat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_ciovec_t { + const void *buf; + size_t buf_len; +} __wasi_ciovec_t; +assert_wasi_layout(offsetof(__wasi_ciovec_t, buf) == 0, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + offsetof(__wasi_ciovec_t, buf_len) == 4, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + offsetof(__wasi_ciovec_t, buf_len) == 8, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + sizeof(__wasi_ciovec_t) == 8, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + sizeof(__wasi_ciovec_t) == 16, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + _Alignof(__wasi_ciovec_t) == 4, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + _Alignof(__wasi_ciovec_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_iovec_t { + void *buf; + size_t buf_len; +} __wasi_iovec_t; +assert_wasi_layout(offsetof(__wasi_iovec_t, buf) == 0, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + offsetof(__wasi_iovec_t, buf_len) == 4, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + offsetof(__wasi_iovec_t, buf_len) == 8, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + sizeof(__wasi_iovec_t) == 8, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + sizeof(__wasi_iovec_t) == 16, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 4 || + _Alignof(__wasi_iovec_t) == 4, "non-wasi data layout"); +assert_wasi_layout(sizeof(void *) != 8 || + _Alignof(__wasi_iovec_t) == 8, "non-wasi data layout"); + +/** + * The contents of a `subscription` when type is `eventtype::clock`. + */ +typedef struct ALIGNED_(8) __wasi_subscription_clock_t { + /** + * The clock against which to compare the timestamp. + */ + __wasi_clockid_t clock_id; + + uint8_t __paddings1[4]; + + /** + * The absolute or relative timestamp. + */ + __wasi_timestamp_t timeout; + + /** + * The amount of time that the implementation may wait additionally + * to coalesce with other events. + */ + __wasi_timestamp_t precision; + + /** + * Flags specifying whether the timeout is absolute or relative + */ + __wasi_subclockflags_t flags; + + uint8_t __paddings2[4]; + +} __wasi_subscription_clock_t; + +assert_wasi_layout(sizeof(__wasi_subscription_clock_t) == 32, "witx calculated size"); +assert_wasi_layout(_Alignof(__wasi_subscription_clock_t) == 8, "witx calculated align"); +assert_wasi_layout(offsetof(__wasi_subscription_clock_t, clock_id) == 0, "witx calculated offset"); +assert_wasi_layout(offsetof(__wasi_subscription_clock_t, timeout) == 8, "witx calculated offset"); +assert_wasi_layout(offsetof(__wasi_subscription_clock_t, precision) == 16, "witx calculated offset"); +assert_wasi_layout(offsetof(__wasi_subscription_clock_t, flags) == 24, "witx calculated offset"); + +/** + * The contents of a `subscription` when type is type is + * `eventtype::fd_read` or `eventtype::fd_write`. + */ +typedef struct __wasi_subscription_fd_readwrite_t { + /** + * The file descriptor on which to wait for it to become ready for reading or writing. + */ + __wasi_fd_t fd; + +} __wasi_subscription_fd_readwrite_t; + +assert_wasi_layout(sizeof(__wasi_subscription_fd_readwrite_t) == 4, "witx calculated size"); +assert_wasi_layout(_Alignof(__wasi_subscription_fd_readwrite_t) == 4, "witx calculated align"); +assert_wasi_layout(offsetof(__wasi_subscription_fd_readwrite_t, fd) == 0, "witx calculated offset"); + +/** + * The contents of a `subscription`. + */ +typedef union __wasi_subscription_u_u_t { + __wasi_subscription_clock_t clock; + __wasi_subscription_fd_readwrite_t fd_readwrite; +} __wasi_subscription_u_u_t ; + +typedef struct ALIGNED_(8) __wasi_subscription_u_t { + __wasi_eventtype_t type; + __wasi_subscription_u_u_t u; +} __wasi_subscription_u_t; + +assert_wasi_layout(sizeof(__wasi_subscription_u_t) == 40, "witx calculated size"); +assert_wasi_layout(_Alignof(__wasi_subscription_u_t) == 8, "witx calculated align"); +assert_wasi_layout(offsetof(__wasi_subscription_u_t, u) == 8, "witx calculated union offset"); +assert_wasi_layout(sizeof(__wasi_subscription_u_u_t) == 32, "witx calculated union size"); +assert_wasi_layout(_Alignof(__wasi_subscription_u_u_t) == 8, "witx calculated union align"); + +/** + * Subscription to an event. + */ +typedef struct __wasi_subscription_t { + /** + * User-provided value that is attached to the subscription in the + * implementation and returned through `event::userdata`. + */ + __wasi_userdata_t userdata; + + /** + * The type of the event to which to subscribe, and its contents + */ + __wasi_subscription_u_t u; + +} __wasi_subscription_t; + +assert_wasi_layout(sizeof(__wasi_subscription_t) == 48, "witx calculated size"); +assert_wasi_layout(_Alignof(__wasi_subscription_t) == 8, "witx calculated align"); +assert_wasi_layout(offsetof(__wasi_subscription_t, userdata) == 0, "witx calculated offset"); +assert_wasi_layout(offsetof(__wasi_subscription_t, u) == 8, "witx calculated offset"); + +/* keep syncing with wasi_socket_ext.h */ + +typedef uint16_t __wasi_ip_port_t; + +/* Ensure that __wasi_addr_type_t has a size of 4 byte (I32). + However, it will not have the type safety of enum. */ +typedef uint32_t __wasi_addr_type_t; +#define IPv4 (0) +#define IPv6 (1) + +/* n0.n1.n2.n3 */ +typedef struct __wasi_addr_ip4_t { + uint8_t n0; + uint8_t n1; + uint8_t n2; + uint8_t n3; +} __wasi_addr_ip4_t; + +typedef struct __wasi_addr_ip4_port_t { + __wasi_addr_ip4_t addr; + __wasi_ip_port_t port; +} __wasi_addr_ip4_port_t; + +typedef struct __wasi_addr_ip6_t { + uint16_t n0; + uint16_t n1; + uint16_t n2; + uint16_t n3; + uint16_t h0; + uint16_t h1; + uint16_t h2; + uint16_t h3; +} __wasi_addr_ip6_t; + +typedef struct __wasi_addr_ip6_port_t { + __wasi_addr_ip6_t addr; + __wasi_ip_port_t port; +} __wasi_addr_ip6_port_t; + +typedef struct __wasi_addr_ip_t { + __wasi_addr_type_t kind; + union { + __wasi_addr_ip4_t ip4; + __wasi_addr_ip6_t ip6; + } addr; +} __wasi_addr_ip_t; + +typedef struct __wasi_addr_t { + __wasi_addr_type_t kind; + union { + __wasi_addr_ip4_port_t ip4; + __wasi_addr_ip6_port_t ip6; + } addr; +} __wasi_addr_t; + +/* Force 32-bit wire width for cross-boundary fields */ +typedef int32_t __wasi_sock_type_t; +#define SOCKET_ANY (-1) +#define SOCKET_DGRAM (0) +#define SOCKET_STREAM (1) + +typedef int32_t __wasi_address_family_t; +#define INET4 (0) +#define INET6 (1) +#define INET_UNSPEC (2) + +typedef struct __wasi_addr_info_t { + __wasi_addr_t addr; + __wasi_sock_type_t type; +} __wasi_addr_info_t; + +typedef struct __wasi_addr_info_hints_t { + __wasi_sock_type_t type; // 4 bytes + __wasi_address_family_t family; // 4 bytes + uint8_t hints_enabled; // 1 byte + uint8_t _pad[3]; // enforce layout +} __wasi_addr_info_hints_t; + +assert_wasi_layout(sizeof(__wasi_sock_type_t) == 4, "sock_type must be 4 bytes"); +assert_wasi_layout(sizeof(__wasi_address_family_t) == 4, "addr_family must be 4 bytes"); + +assert_wasi_layout(sizeof(__wasi_addr_info_hints_t) == 12, "hints_t must be 12 bytes"); +assert_wasi_layout(offsetof(__wasi_addr_info_hints_t, type) == 0, "hints.type@0"); +assert_wasi_layout(offsetof(__wasi_addr_info_hints_t, family) == 4, "hints.family@4"); +assert_wasi_layout(offsetof(__wasi_addr_info_hints_t, hints_enabled) == 8, "hints.enabled@8"); + +assert_wasi_layout(offsetof(__wasi_addr_info_t, type) == sizeof(__wasi_addr_t), + "addr_info.type follows addr"); + +#undef assert_wasi_layout + +/* clang-format on */ +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_WASI_TYPES_H */ diff --git a/core/shared/platform/linux-sgx/bh_assert.c b/core/shared/platform/linux-sgx/bh_assert.c deleted file mode 100644 index fa378c5e7f..0000000000 --- a/core/shared/platform/linux-sgx/bh_assert.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_assert.h" -#include -#include -#include - -#ifdef BH_TEST -#include -#endif - -#ifdef BH_TEST -/* for exception throwing */ -jmp_buf bh_test_jb; -#endif -#define FIXED_BUFFER_SIZE (1<<9) - -void bh_assert_internal(int v, const char *file_name, int line_number, - const char *expr_string) -{ - if (v) - return; - - if (!file_name) - file_name = "NULL FILENAME"; - if (!expr_string) - expr_string = "NULL EXPR_STRING"; - - bh_printf_sgx("\nASSERTION FAILED: %s, at FILE=%s, LINE=%d\n", expr_string, - file_name, line_number); - -#ifdef BH_TEST - longjmp(bh_test_jb, 1); -#endif - - abort(); -} - -void bh_debug_internal(const char *file_name, int line_number, const char *fmt, - ...) -{ -#ifndef JEFF_TEST_VERIFIER - va_list args; - char msg[FIXED_BUFFER_SIZE] = { '\0' }; - - va_start(args, fmt); - vsnprintf(msg, FIXED_BUFFER_SIZE, fmt, args); - va_end(args); - bh_printf_sgx("\nDebug info FILE=%s, LINE=%d: ", file_name, line_number); - bh_printf_sgx(msg); - bh_printf_sgx("\n"); -#endif -} - diff --git a/core/shared/platform/linux-sgx/bh_definition.c b/core/shared/platform/linux-sgx/bh_definition.c deleted file mode 100644 index 2b69af0ca6..0000000000 --- a/core/shared/platform/linux-sgx/bh_definition.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" - -#define RSIZE_MAX 0x7FFFFFFF - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n) -{ - char *dest = (char*) s1; - char *src = (char*) s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} - -int b_strcat_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s1) + strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strncat(s1, s2, strlen(s2)); - - return 0; -} - -int b_strcpy_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strncpy(s1, s2, s1max); - - return 0; -} - diff --git a/core/shared/platform/linux-sgx/bh_platform.c b/core/shared/platform/linux-sgx/bh_platform.c deleted file mode 100644 index 631cad7f30..0000000000 --- a/core/shared/platform/linux-sgx/bh_platform.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_common.h" -#include "bh_platform.h" - -#include - -#define FIXED_BUFFER_SIZE (1<<9) -static bh_print_function_t print_function = NULL; - -char *bh_strdup(const char *s) -{ - uint32 size; - char *s1 = NULL; - - if (s) { - size = (uint32)(strlen(s) + 1); - if ((s1 = bh_malloc(size))) - bh_memcpy_s(s1, size, s, size); - } - return s1; -} - -int bh_platform_init() -{ - return 0; -} - -int putchar(int c) -{ - return 0; -} - -int puts(const char *s) -{ - return 0; -} - -void bh_set_print_function(bh_print_function_t pf) -{ - print_function = pf; -} - -int bh_printf_sgx(const char *message, ...) -{ - if (print_function != NULL) { - char msg[FIXED_BUFFER_SIZE] = { '\0' }; - va_list ap; - va_start(ap, message); - vsnprintf(msg, FIXED_BUFFER_SIZE, message, ap); - va_end(ap); - print_function(msg); - } - - return 0; -} - -int bh_vprintf_sgx(const char * format, va_list arg) -{ - if (print_function != NULL) { - char msg[FIXED_BUFFER_SIZE] = { '\0' }; - vsnprintf(msg, FIXED_BUFFER_SIZE, format, arg); - print_function(msg); - } - - return 0; -} - -void * -bh_mmap(void *hint, unsigned int size, int prot, int flags) -{ - /* TODO: implement bh_mmap in Linux SGX */ - return NULL; -} - -void -bh_munmap(void *addr, uint32 size) -{ - /* TODO: implement bh_munmap in Linux SGX */ -} - -int -bh_mprotect(void *addr, uint32 size, int prot) -{ - /* TODO: implement bh_mprotect in Linux SGX */ - return -1; -} - diff --git a/core/shared/platform/linux-sgx/bh_platform.h b/core/shared/platform/linux-sgx/bh_platform.h deleted file mode 100644 index 00fbfbd98f..0000000000 --- a/core/shared/platform/linux-sgx/bh_platform.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_PLATFORM_H -#define _BH_PLATFORM_H - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_memory.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern int bh_printf_sgx(const char *message, ...); -extern int bh_vprintf_sgx(const char * format, va_list arg); - -typedef uint64_t uint64; -typedef int64_t int64; - -#define DIE do{bh_debug("Die here\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); DEBUGME(void); while(1);}while(0) - -#ifndef BH_PLATFORM_LINUX_SGX -#define BH_PLATFORM_LINUX_SGX -#endif - -/* NEED qsort */ - -#define _STACK_SIZE_ADJUSTMENT (32 * 1024) - -/* Stack size of applet threads's native part. */ -#define BH_APPLET_PRESERVED_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT) - -/* Default thread priority */ -#define BH_THREAD_DEFAULT_PRIORITY 0 - -#define BH_ROUTINE_MODIFIER - -#define BHT_TIMEDOUT ETIMEDOUT - -#define INVALID_THREAD_ID 0xFFffFFff - -typedef int korp_sem; -typedef void* (*thread_start_routine_t)(void*); -typedef sgx_thread_mutex_t korp_mutex; -typedef sgx_thread_t korp_tid; -typedef sgx_thread_t korp_thread; -typedef sgx_thread_cond_t korp_cond; - -#define wa_malloc bh_malloc -#define wa_free bh_free -#define wa_strdup bh_strdup - -#define bh_printf bh_printf_sgx - -int snprintf(char *buffer, size_t count, const char *format, ...); -int strncasecmp(const char *s1, const char *s2, size_t n); -double fmod(double x, double y); -float fmodf(float x, float y); -double sqrt(double x); - -#define BH_WAIT_FOREVER 0xFFFFFFFF - -#ifndef NULL -# define NULL ((void*) 0) -#endif - -/** - * Return the offset of the given field in the given type. - * - * @param Type the type containing the filed - * @param field the field in the type - * - * @return the offset of field in Type - */ -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) -#endif - -#define bh_assert assert - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, - unsigned int n); -int b_strcat_s(char * s1, size_t s1max, const char * s2); -int b_strcpy_s(char * s1, size_t s1max, const char * s2); - -char *bh_strdup(const char *s); - -int bh_platform_init(); - -/* MMAP mode */ -enum { - MMAP_PROT_NONE = 0, - MMAP_PROT_READ = 1, - MMAP_PROT_WRITE = 2, - MMAP_PROT_EXEC = 4 -}; - -/* MMAP flags */ -enum { - MMAP_MAP_NONE = 0, - /* Put the mapping into 0 to 2 G, supported only on x86_64 */ - MMAP_MAP_32BIT = 1, - /* Don't interpret addr as a hint: place the mapping at exactly - that address. */ - MMAP_MAP_FIXED = 2 -}; - -void *bh_mmap(void *hint, unsigned int size, int prot, int flags); -void bh_munmap(void *addr, uint32 size); -int bh_mprotect(void *addr, uint32 size, int prot); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/shared/platform/linux-sgx/bh_platform_log.c b/core/shared/platform/linux-sgx/bh_platform_log.c deleted file mode 100644 index 994cedfca0..0000000000 --- a/core/shared/platform/linux-sgx/bh_platform_log.c +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include - -void bh_log_emit(const char *fmt, va_list ap) -{ - bh_vprintf_sgx(fmt, ap); -} - -int bh_fflush(void *stream) -{ - return 0; -} diff --git a/core/shared/platform/linux-sgx/bh_thread.c b/core/shared/platform/linux-sgx/bh_thread.c deleted file mode 100644 index 28667be20e..0000000000 --- a/core/shared/platform/linux-sgx/bh_thread.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_thread.h" -#include "bh_assert.h" -#include "bh_memory.h" -#include -#include -#include - -int _vm_thread_sys_init() -{ - return 0; -} - -void vm_thread_sys_destroy(void) -{ -} - -int _vm_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio) -{ - return BHT_ERROR; - // return BHT_OK; -} - -int _vm_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, - unsigned int stack_size) -{ - return _vm_thread_create_with_prio(tid, start, arg, stack_size, - BH_THREAD_DEFAULT_PRIORITY); -} - -korp_tid _vm_self_thread() -{ - return sgx_thread_self(); -} - -void vm_thread_exit(void * code) -{ -} - -// storage for one thread -static __thread void *_tls_store = NULL; - -void *_vm_tls_get(unsigned idx) -{ - return _tls_store; -} - -int _vm_tls_put(unsigned idx, void * tls) -{ - _tls_store = tls; - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_mutex_init(korp_mutex *mutex) -{ - sgx_thread_mutex_t m = SGX_THREAD_MUTEX_INITIALIZER; - *mutex = m; - return BHT_OK; -} - -int _vm_recursive_mutex_init(korp_mutex *mutex) -{ - sgx_thread_mutex_t m = SGX_THREAD_RECURSIVE_MUTEX_INITIALIZER; - *mutex = m; - return BHT_OK; -} - -int _vm_mutex_destroy(korp_mutex *mutex) -{ - sgx_thread_mutex_destroy(mutex); - return BHT_OK; -} - -/* Returned error (EINVAL, EAGAIN and EDEADLK) from - locking the mutex indicates some logic error present in - the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_lock(korp_mutex *mutex) -{ - sgx_thread_mutex_lock(mutex); -} - -int vm_mutex_trylock(korp_mutex *mutex) -{ - return (sgx_thread_mutex_trylock(mutex) == 0? BHT_OK: BHT_ERROR); -} - -/* Returned error (EINVAL, EAGAIN and EPERM) from - unlocking the mutex indicates some logic error present - in the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_unlock(korp_mutex *mutex) -{ - sgx_thread_mutex_unlock(mutex); -} - -int _vm_sem_init(korp_sem* sem, unsigned int c) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_sem_destroy(korp_sem *sem) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_sem_wait(korp_sem *sem) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_sem_reltimedwait(korp_sem *sem, int mills) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_sem_post(korp_sem *sem) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_cond_init(korp_cond *cond) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_cond_destroy(korp_cond *cond) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_cond_signal(korp_cond *cond) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_cond_broadcast(korp_cond *cond) -{ - return BHT_OK; - //return BHT_ERROR; -} - -int _vm_thread_cancel(korp_tid thread) -{ - return 0; -} - -int _vm_thread_join(korp_tid thread, void **value_ptr, int mills) -{ - return 0; -} - -int _vm_thread_detach(korp_tid thread) -{ - return 0; -} diff --git a/core/shared/platform/linux-sgx/bh_time.c b/core/shared/platform/linux-sgx/bh_time.c deleted file mode 100644 index 189e668d43..0000000000 --- a/core/shared/platform/linux-sgx/bh_time.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_time.h" - -#include -#include -#include - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -uint64 _bh_time_get_tick_millisecond() -{ - //TODO: - return 0; -} - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -uint64 _bh_time_get_boot_millisecond() -{ - //TODO - return 0; -} - -uint32 bh_get_tick_sec() -{ - //TODO - return 0; -} - -/* - * This function returns GMT time milliseconds since from 1970.1.1, AKA UNIX time. - * @return milliseconds since from 1970.1.1. - */ -uint64 _bh_time_get_millisecond_from_1970() -{ - //TODO - return 0; -} - -size_t _bh_time_strftime(char *s, size_t max, const char *format, int64 time) -{ - //TODO - return 0; -} - diff --git a/core/shared/platform/linux-sgx/platform_internal.h b/core/shared/platform/linux-sgx/platform_internal.h new file mode 100644 index 0000000000..62f9a4482f --- /dev/null +++ b/core/shared/platform/linux-sgx/platform_internal.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sgx_error.h" +#include "sgx_file.h" +#include "sgx_pthread.h" +#include "sgx_time.h" +#include "sgx_socket.h" +#include "sgx_signal.h" +#include "sgx_trts.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_LINUX_SGX +#define BH_PLATFORM_LINUX_SGX +#endif + +#define _STACK_SIZE_ADJUSTMENT (32 * 1024) + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_thread; +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_rwlock_t korp_rwlock; +typedef unsigned int korp_sem; + +#ifndef SGX_DISABLE_PTHREAD +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#endif + +typedef int (*os_print_function_t)(const char *message); +void +os_set_print_function(os_print_function_t pf); + +char * +strcpy(char *dest, const char *src); + +#define os_memory_order_acquire __ATOMIC_ACQUIRE +#define os_memory_order_release __ATOMIC_RELEASE +#define os_memory_order_seq_cst __ATOMIC_SEQ_CST +#define os_atomic_thread_fence __atomic_thread_fence + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; + +struct _pollfd { + int fd; + short events; + short revents; +}; + +typedef struct _pollfd os_poll_file_handle; +typedef unsigned long os_nfds_t; +typedef struct timespec os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#define os_getpagesize getpagesize + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/linux-sgx/sgx_file.c b/core/shared/platform/linux-sgx/sgx_file.c new file mode 100644 index 0000000000..a8ae8d2f9a --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_file.c @@ -0,0 +1,1117 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "sgx_error.h" +#include "sgx_file.h" + +#if WASM_ENABLE_SGX_IPFS != 0 +#include "sgx_ipfs.h" +#endif + +#ifndef SGX_DISABLE_WASI + +#define TRACE_FUNC() os_printf("undefined %s\n", __FUNCTION__) +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +/** fd **/ +int +ocall_open(int *p_fd, const char *pathname, int flags, bool has_mode, + unsigned mode); + +int +ocall_openat(int *p_fd, int dirfd, const char *pathname, int flags, + bool has_mode, unsigned mode); + +int +ocall_read(ssize_t *p_ret, int fd, void *buf, size_t read_size); + +int +ocall_close(int *p_ret, int fd); + +int +ocall_lseek(off_t *p_ret, int fd, off_t offset, int whence); + +int +ocall_ftruncate(int *p_ret, int fd, off_t length); + +int +ocall_fsync(int *p_ret, int fd); + +int +ocall_fdatasync(int *p_ret, int fd); + +int +ocall_isatty(int *p_ret, int fd); +/** fd end **/ + +/** DIR **/ +int +ocall_fdopendir(int fd, void **p_dirp); + +int +ocall_readdir(void **p_dirent, void *dirp); + +int +ocall_rewinddir(void *dirp); + +int +ocall_seekdir(void *dirp, long loc); + +int +ocall_telldir(long *p_dir, void *dirp); + +int +ocall_closedir(int *p_ret, void *dirp); +/** DIR end **/ + +/** stat **/ +int +ocall_stat(int *p_ret, const char *pathname, void *buf, unsigned int buf_len); +int +ocall_fstat(int *p_ret, int fd, void *buf, unsigned int buf_len); +int +ocall_fstatat(int *p_ret, int dirfd, const char *pathname, void *buf, + unsigned int buf_len, int flags); +/** stat end **/ + +/** link **/ +int +ocall_mkdirat(int *p_ret, int dirfd, const char *pathname, unsigned mode); +int +ocall_link(int *p_ret, const char *oldpath, const char *newpath); +int +ocall_linkat(int *p_ret, int olddirfd, const char *oldpath, int newdirfd, + const char *newpath, int flags); +int +ocall_unlinkat(int *p_ret, int dirfd, const char *pathname, int flags); +int +ocall_readlink(ssize_t *p_ret, const char *pathname, char *buf, size_t bufsiz); +int +ocall_readlinkat(ssize_t *p_ret, int dirfd, const char *pathname, char *buf, + size_t bufsiz); +int +ocall_renameat(int *p_ret, int olddirfd, const char *oldpath, int newdirfd, + const char *newpath); +int +ocall_symlinkat(int *p_ret, const char *target, int newdirfd, + const char *linkpath); +/** link end **/ + +/** control **/ +int +ocall_ioctl(int *p_ret, int fd, unsigned long request, void *arg, + unsigned int arg_len); +int +ocall_fcntl(int *p_ret, int fd, int cmd); +int +ocall_fcntl_long(int *p_ret, int fd, int cmd, long arg); +/** control end **/ + +/** **/ +int +ocall_realpath(int *p_ret, const char *path, char *buf, unsigned int buf_len); +int +ocall_posix_fallocate(int *p_ret, int fd, off_t offset, off_t len); +int +ocall_poll(int *p_ret, void *fds, unsigned nfds, int timeout, + unsigned int fds_len); +int +ocall_getopt(int *p_ret, int argc, char *argv_buf, unsigned int argv_buf_len, + const char *optstring); +int +ocall_sched_yield(int *p_ret); + +/** struct iovec **/ +ssize_t +ocall_readv(ssize_t *p_ret, int fd, char *iov_buf, unsigned int buf_size, + int iovcnt, bool has_offset, off_t offset); +ssize_t +ocall_writev(ssize_t *p_ret, int fd, char *iov_buf, unsigned int buf_size, + int iovcnt, bool has_offset, off_t offset); +/** iovec end **/ + +int +ocall_get_errno(int *p_ret); + +int +open(const char *pathname, int flags, ...) +{ + int fd; + bool has_mode = false; + mode_t mode = 0; + + if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + has_mode = true; + } + + if (SGX_SUCCESS != ocall_open(&fd, pathname, flags, has_mode, mode)) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (fd >= 0 && (flags & O_CLOEXEC)) + fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (fd == -1) + errno = get_errno(); + return fd; +} + +int +openat(int dirfd, const char *pathname, int flags, ...) +{ + int fd; + bool has_mode = false; + mode_t mode = 0; + + if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + has_mode = true; + } + + if (SGX_SUCCESS + != ocall_openat(&fd, dirfd, pathname, flags, has_mode, mode)) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (fd >= 0 && (flags & O_CLOEXEC)) + fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (fd == -1) + errno = get_errno(); + +#if WASM_ENABLE_SGX_IPFS != 0 + struct stat sb; + int ret = fstatat(dirfd, pathname, &sb, 0); + if (ret < 0) { + if (ocall_close(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return -1; + } + + // Ony files are managed by SGX IPFS + if (S_ISREG(sb.st_mode)) { + // When WAMR uses Intel SGX IPFS to enabled, it opens a second + // file descriptor to interact with the secure file. + // The first file descriptor opened earlier is used to interact + // with the metadata of the file (e.g., time, flags, etc.). + void *file_ptr = ipfs_fopen(fd, flags); + if (file_ptr == NULL) { + if (ocall_close(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return -1; + } + } +#endif + + return fd; +} + +int +close(int fd) +{ + int ret; + +#if WASM_ENABLE_SGX_IPFS != 0 + // Close the IPFS file pointer in addition of the file descriptor + ret = ipfs_close(fd); + if (ret == -1) + errno = get_errno(); +#endif + + if (ocall_close(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t +read(int fd, void *buf, size_t size) +{ + ssize_t ret; + int size_read_max = 2048, size_read, total_size_read = 0, count, i; + char *p = buf; + + if (buf == NULL) { + TRACE_FUNC(); + return -1; + } + + count = (size + size_read_max - 1) / size_read_max; + for (i = 0; i < count; i++) { + size_read = (i < count - 1) ? size_read_max : size - size_read_max * i; + + if (ocall_read(&ret, fd, p, size_read) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) { + /* read failed */ + errno = get_errno(); + return -1; + } + + p += ret; + total_size_read += ret; + + if (ret < size_read) + /* end of file */ + break; + } + return total_size_read; +} + +DIR * +fdopendir(int fd) +{ + DIR *result = NULL; + + result = (DIR *)BH_MALLOC(sizeof(DIR)); + if (!result) + return NULL; + + if (ocall_fdopendir(fd, (void **)result) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(result); + return NULL; + } + + if ((void *)*result == NULL) { /* opendir failed */ + TRACE_FUNC(); + BH_FREE(result); + errno = get_errno(); + return NULL; + } + + return result; +} + +struct dirent * +readdir(DIR *dirp) +{ + struct dirent *result; + + if (dirp == NULL) + return NULL; + + if (ocall_readdir((void **)&result, (void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return NULL; + } + + if (!result) + errno = get_errno(); + return result; +} + +void +rewinddir(DIR *dirp) +{ + if (ocall_rewinddir((void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } +} + +void +seekdir(DIR *dirp, long loc) +{ + if (ocall_seekdir((void *)*dirp, loc) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } +} + +long +telldir(DIR *dirp) +{ + long ret; + + if (ocall_telldir(&ret, (void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +closedir(DIR *dirp) +{ + int ret; + + if (ocall_closedir(&ret, (void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + BH_FREE(dirp); + if (ret == -1) + errno = get_errno(); + return ret; +} + +static ssize_t +readv_internal(int fd, const struct iovec *iov, int iovcnt, bool has_offset, + off_t offset) +{ + ssize_t ret, size_left; + struct iovec *iov1; + int i; + char *p; + uint64 total_size = sizeof(struct iovec) * (uint64)iovcnt; + + if (iov == NULL || iovcnt < 1) + return -1; + + for (i = 0; i < iovcnt; i++) { + total_size += iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + +#if WASM_ENABLE_SGX_IPFS != 0 + if (fd > 2) { + return ipfs_read(fd, iov, iovcnt, has_offset, offset); + } +#endif + + iov1 = BH_MALLOC((uint32)total_size); + + if (iov1 == NULL) + return -1; + + memset(iov1, 0, (uint32)total_size); + + p = (char *)(uintptr_t)(sizeof(struct iovec) * iovcnt); + + for (i = 0; i < iovcnt; i++) { + iov1[i].iov_len = iov[i].iov_len; + iov1[i].iov_base = p; + p += iov[i].iov_len; + } + + if (ocall_readv(&ret, fd, (char *)iov1, (uint32)total_size, iovcnt, + has_offset, offset) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(iov1); + return -1; + } + + p = (char *)(uintptr_t)(sizeof(struct iovec) * iovcnt); + + size_left = ret; + for (i = 0; i < iovcnt; i++) { + if (size_left > iov[i].iov_len) { + memcpy(iov[i].iov_base, (uintptr_t)p + (char *)iov1, + iov[i].iov_len); + p += iov[i].iov_len; + size_left -= iov[i].iov_len; + } + else { + memcpy(iov[i].iov_base, (uintptr_t)p + (char *)iov1, size_left); + break; + } + } + + BH_FREE(iov1); + if (ret == -1) + errno = get_errno(); + return ret; +} + +static ssize_t +writev_internal(int fd, const struct iovec *iov, int iovcnt, bool has_offset, + off_t offset) +{ + ssize_t ret; + struct iovec *iov1; + int i; + char *p; + uint64 total_size = sizeof(struct iovec) * (uint64)iovcnt; + + if (iov == NULL || iovcnt < 1) + return -1; + + for (i = 0; i < iovcnt; i++) { + total_size += iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + +#if WASM_ENABLE_SGX_IPFS != 0 + if (fd > 2) { + return ipfs_write(fd, iov, iovcnt, has_offset, offset); + } +#endif + + iov1 = BH_MALLOC((uint32)total_size); + + if (iov1 == NULL) + return -1; + + memset(iov1, 0, (uint32)total_size); + + p = (char *)(uintptr_t)(sizeof(struct iovec) * iovcnt); + + for (i = 0; i < iovcnt; i++) { + iov1[i].iov_len = iov[i].iov_len; + iov1[i].iov_base = p; + memcpy((uintptr_t)p + (char *)iov1, iov[i].iov_base, iov[i].iov_len); + p += iov[i].iov_len; + } + + if (ocall_writev(&ret, fd, (char *)iov1, (uint32)total_size, iovcnt, + has_offset, offset) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(iov1); + return -1; + } + + BH_FREE(iov1); + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t +readv(int fd, const struct iovec *iov, int iovcnt) +{ + return readv_internal(fd, iov, iovcnt, false, 0); +} + +ssize_t +writev(int fd, const struct iovec *iov, int iovcnt) +{ + return writev_internal(fd, iov, iovcnt, false, 0); +} + +ssize_t +preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + return readv_internal(fd, iov, iovcnt, true, offset); +} + +ssize_t +pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + return writev_internal(fd, iov, iovcnt, true, offset); +} + +off_t +lseek(int fd, off_t offset, int whence) +{ + off_t ret; + +#if WASM_ENABLE_SGX_IPFS != 0 + ret = ipfs_lseek(fd, offset, whence); +#else + if (ocall_lseek(&ret, fd, (long)offset, whence) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); +#endif + + return ret; +} + +int +ftruncate(int fd, off_t length) +{ + int ret; + +#if WASM_ENABLE_SGX_IPFS != 0 + ret = ipfs_ftruncate(fd, length); +#else + if (ocall_ftruncate(&ret, fd, length) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); +#endif + + return ret; +} + +int +stat(const char *pathname, struct stat *statbuf) +{ + int ret; + + if (statbuf == NULL) + return -1; + + if (ocall_stat(&ret, pathname, (void *)statbuf, sizeof(struct stat)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +fstat(int fd, struct stat *statbuf) +{ + int ret; + + if (statbuf == NULL) + return -1; + + if (ocall_fstat(&ret, fd, (void *)statbuf, sizeof(struct stat)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags) +{ + int ret; + + if (statbuf == NULL) + return -1; + + if (ocall_fstatat(&ret, dirfd, pathname, (void *)statbuf, + sizeof(struct stat), flags) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +fsync(int fd) +{ + int ret; + +#if WASM_ENABLE_SGX_IPFS != 0 + ret = ipfs_fflush(fd); +#else + if (ocall_fsync(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); +#endif + + return ret; +} + +int +fdatasync(int fd) +{ + int ret; + +#if WASM_ENABLE_SGX_IPFS != 0 + ret = ipfs_fflush(fd); +#else + if (ocall_fdatasync(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); +#endif + + return ret; +} + +int +mkdirat(int dirfd, const char *pathname, mode_t mode) +{ + int ret; + + if (ocall_mkdirat(&ret, dirfd, pathname, mode) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +link(const char *oldpath, const char *newpath) +{ + int ret; + + if (ocall_link(&ret, oldpath, newpath) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, + int flags) +{ + int ret; + + if (ocall_linkat(&ret, olddirfd, oldpath, newdirfd, newpath, flags) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +unlinkat(int dirfd, const char *pathname, int flags) +{ + int ret; + + if (ocall_unlinkat(&ret, dirfd, pathname, flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t +readlink(const char *pathname, char *buf, size_t bufsiz) +{ + ssize_t ret; + + if (buf == NULL) + return -1; + + if (ocall_readlink(&ret, pathname, buf, bufsiz) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t +readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) +{ + ssize_t ret; + + if (buf == NULL) + return -1; + + if (ocall_readlinkat(&ret, dirfd, pathname, buf, bufsiz) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +symlinkat(const char *target, int newdirfd, const char *linkpath) +{ + int ret; + + if (ocall_symlinkat(&ret, target, newdirfd, linkpath) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) +{ + int ret; + + if (ocall_renameat(&ret, olddirfd, oldpath, newdirfd, newpath) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +ioctl(int fd, unsigned long request, ...) +{ + int ret; + va_list args; + + switch (request) { + case FIONREAD: + va_start(args, request); + int *arg = (int *)va_arg(args, int *); + if (ocall_ioctl(&ret, fd, request, arg, sizeof(*arg)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + va_end(args); + return -1; + } + va_end(args); + break; + + default: + os_printf("ioctl failed: unknown request", request); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +fcntl(int fd, int cmd, ... /* arg */) +{ + int ret; + va_list args; + + switch (cmd) { + case F_GETFD: + case F_GETFL: + if (ocall_fcntl(&ret, fd, cmd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + break; + + case F_DUPFD: + case F_SETFD: + case F_SETFL: + va_start(args, cmd); + long arg_1 = (long)va_arg(args, long); + if (ocall_fcntl_long(&ret, fd, cmd, arg_1) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + va_end(args); + return -1; + } + va_end(args); + break; + + default: + os_printf("fcntl failed: unknown cmd %d.\n", cmd); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +isatty(int fd) +{ + int ret; + + if (ocall_isatty(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == 0) + errno = get_errno(); + return ret; +} + +char * +realpath(const char *path, char *resolved_path) +{ + int ret; + char buf[PATH_MAX] = { 0 }; + + if (ocall_realpath(&ret, path, buf, PATH_MAX) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return (char *)NULL; + } + + if (ret != 0) + return (char *)NULL; + + if (resolved_path) { + strcpy(resolved_path, buf); + } + else { + resolved_path = BH_MALLOC(strlen(buf) + 1); + if (resolved_path == NULL) + return NULL; + strcpy(resolved_path, buf); + } + + return resolved_path; +} + +int +posix_fallocate(int fd, off_t offset, off_t len) +{ + int ret; + +#if WASM_ENABLE_SGX_IPFS != 0 + ret = ipfs_posix_fallocate(fd, offset, len); +#else + if (ocall_posix_fallocate(&ret, fd, offset, len) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } +#endif + + return ret; +} + +int +poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int ret; + + if (fds == NULL) + return -1; + + if (ocall_poll(&ret, fds, nfds, timeout, sizeof(*fds) * nfds) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +getopt(int argc, char *const argv[], const char *optstring) +{ + int ret; + char **argv1; + char *p; + int i; + uint64 total_size = sizeof(char *) * (uint64)argc; + + for (i = 0; i < argc; i++) { + total_size += strlen(argv[i]) + 1; + } + + if (total_size >= UINT32_MAX) + return -1; + + argv1 = BH_MALLOC((uint32)total_size); + + if (argv1 == NULL) + return -1; + + p = (char *)(uintptr_t)(sizeof(char *) * argc); + + for (i = 0; i < argc; i++) { + argv1[i] = p; + strcpy((char *)argv1 + (uintptr_t)p, argv[i]); + p += ((uintptr_t)strlen(argv[i]) + 1); + } + + if (ocall_getopt(&ret, argc, (char *)argv1, total_size, optstring) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(argv1); + return -1; + } + + BH_FREE(argv1); + if (ret == -1) + errno = get_errno(); + return ret; +} + +int +sched_yield(void) +{ + int ret; + + if (ocall_sched_yield(&ret) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t +getrandom(void *buf, size_t buflen, unsigned int flags) +{ + sgx_status_t ret; + + if (!buf || buflen > INT32_MAX || flags != 0) { + errno = EINVAL; + return -1; + } + + ret = sgx_read_rand(buf, buflen); + if (ret != SGX_SUCCESS) { + errno = EFAULT; + return -1; + } + + return (ssize_t)buflen; +} + +#define RDRAND_RETRIES 3 + +static int +rdrand64_step(uint64 *seed) +{ + uint8 ok; + __asm__ volatile("rdseed %0; setc %1" : "=r"(*seed), "=qm"(ok)); + return (int)ok; +} + +static int +rdrand64_retry(uint64 *rand, uint32 retries) +{ + uint32 count = 0; + + while (count++ <= retries) { + if (rdrand64_step(rand)) { + return -1; + } + } + return 0; +} + +static uint32 +rdrand_get_bytes(uint8 *dest, uint32 n) +{ + uint8 *head_start = dest, *tail_start = NULL; + uint64 *block_start; + uint32 count, ltail, lhead, lblock; + uint64 i, temp_rand; + + /* Get the address of the first 64-bit aligned block in the + destination buffer. */ + if (((uintptr_t)head_start & (uintptr_t)7) == 0) { + /* already 8-byte aligned */ + block_start = (uint64 *)head_start; + lhead = 0; + lblock = n & ~7; + } + else { + /* next 8-byte aligned */ + block_start = (uint64 *)(((uintptr_t)head_start + 7) & ~(uintptr_t)7); + lhead = (uint32)((uintptr_t)block_start - (uintptr_t)head_start); + lblock = (n - lhead) & ~7; + } + + /* Compute the number of 64-bit blocks and the remaining number + of bytes (the tail) */ + ltail = n - lblock - lhead; + if (ltail > 0) { + tail_start = (uint8 *)block_start + lblock; + } + + /* Populate the starting, mis-aligned section (the head) */ + if (lhead > 0) { + if (!rdrand64_retry(&temp_rand, RDRAND_RETRIES)) { + return 0; + } + memcpy(head_start, &temp_rand, lhead); + } + + /* Populate the central, aligned blocks */ + count = lblock / 8; + for (i = 0; i < count; i++, block_start++) { + if (!rdrand64_retry(block_start, RDRAND_RETRIES)) { + return i * 8 + lhead; + } + } + + /* Populate the tail */ + if (ltail > 0) { + if (!rdrand64_retry(&temp_rand, RDRAND_RETRIES)) { + return count * 8 + lhead; + } + + memcpy(tail_start, &temp_rand, ltail); + } + + return n; +} + +int +getentropy(void *buffer, size_t length) +{ + uint32 size; + + if (!buffer || length > INT32_MAX) { + errno = EINVAL; + return -1; + } + + if (length == 0) { + return 0; + } + + size = rdrand_get_bytes(buffer, (uint32)length); + if (size != length) { + errno = EFAULT; + return -1; + } + + return 0; +} + +int +get_errno(void) +{ + int ret; + + if (ocall_get_errno(&ret) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + return ret; +} + +#endif diff --git a/core/shared/platform/linux-sgx/sgx_file.h b/core/shared/platform/linux-sgx/sgx_file.h new file mode 100644 index 0000000000..8690e1f69c --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_file.h @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_FILE_H +#define _SGX_FILE_H + +#include "sgx_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#define FD_CLOEXEC 1 + +#define O_PATH 010000000 +#define O_SEARCH O_PATH +#define O_EXEC O_PATH + +#define O_ACCMODE (03 | O_SEARCH) +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 + +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_NOCTTY 0400 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_DSYNC 010000 +#define O_SYNC 04010000 +#define O_RSYNC 04010000 +#define O_DIRECTORY 0200000 +#define O_NOFOLLOW 0400000 +#define O_CLOEXEC 02000000 + +#define O_ASYNC 020000 +#define O_DIRECT 040000 +#define O_LARGEFILE 0 +#define O_NOATIME 01000000 +#define O_PATH 010000000 +#define O_TMPFILE 020200000 +#define O_NDELAY O_NONBLOCK + +#define S_IFMT 0170000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFBLK 0060000 +#define S_IFREG 0100000 +#define S_IFIFO 0010000 +#define S_IFLNK 0120000 +#define S_IFSOCK 0140000 + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR) +#define S_ISCHR(mode) (((mode)&S_IFMT) == S_IFCHR) +#define S_ISBLK(mode) (((mode)&S_IFMT) == S_IFBLK) +#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG) +#define S_ISFIFO(mode) (((mode)&S_IFMT) == S_IFIFO) +#define S_ISLNK(mode) (((mode)&S_IFMT) == S_IFLNK) +#define S_ISSOCK(mode) (((mode)&S_IFMT) == S_IFSOCK) + +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +#define AT_SYMLINK_NOFOLLOW 0x100 +#define AT_REMOVEDIR 0x200 +#define AT_SYMLINK_FOLLOW 0x400 + +#define POLLIN 0x001 +#define POLLPRI 0x002 +#define POLLOUT 0x004 +#define POLLERR 0x008 +#define POLLHUP 0x010 +#define POLLNVAL 0x020 +#define POLLRDNORM 0x040 +#define POLLRDBAND 0x080 +#define POLLWRNORM 0x100 +#define POLLWRBAND 0x200 + +#define FIONREAD 0x541B + +#define PATH_MAX 4096 + +/* Special value used to indicate openat should use the current + working directory. */ +#define AT_FDCWD -100 + +typedef long __syscall_slong_t; + +typedef unsigned long dev_t; +typedef unsigned long ino_t; +typedef unsigned mode_t; +typedef unsigned long nlink_t; +typedef unsigned socklen_t; +typedef long blksize_t; +typedef long blkcnt_t; + +typedef int pid_t; +typedef unsigned gid_t; +typedef unsigned uid_t; + +typedef unsigned long nfds_t; + +typedef uintptr_t DIR; + +struct dirent { + ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +struct stat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + long __unused[3]; +}; + +struct iovec { + void *iov_base; + size_t iov_len; +}; + +struct pollfd { + int fd; + short events; + short revents; +}; + +int +open(const char *pathname, int flags, ...); +int +openat(int dirfd, const char *pathname, int flags, ...); +int +close(int fd); + +DIR * +fdopendir(int fd); +int +closedir(DIR *dirp); +void +rewinddir(DIR *dirp); +void +seekdir(DIR *dirp, long loc); +struct dirent * +readdir(DIR *dirp); +long +telldir(DIR *dirp); + +ssize_t +read(int fd, void *buf, size_t count); +ssize_t +readv(int fd, const struct iovec *iov, int iovcnt); +ssize_t +writev(int fd, const struct iovec *iov, int iovcnt); +ssize_t +preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset); +ssize_t +pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset); + +off_t +lseek(int fd, off_t offset, int whence); +int +ftruncate(int fd, off_t length); + +int +stat(const char *pathname, struct stat *statbuf); +int +fstat(int fd, struct stat *statbuf); +int +fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags); + +int +fsync(int fd); +int +fdatasync(int fd); + +int +mkdirat(int dirfd, const char *pathname, mode_t mode); +int +link(const char *oldpath, const char *newpath); +int +linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, + int flags); +int +unlinkat(int dirfd, const char *pathname, int flags); +ssize_t +readlink(const char *pathname, char *buf, size_t bufsiz); +ssize_t +readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz); +int +symlinkat(const char *target, int newdirfd, const char *linkpath); +int +renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); + +int +ioctl(int fd, unsigned long request, ...); +int +fcntl(int fd, int cmd, ... /* arg */); + +int +isatty(int fd); + +char * +realpath(const char *path, char *resolved_path); + +int +posix_fallocate(int fd, off_t offset, off_t len); + +int +poll(struct pollfd *fds, nfds_t nfds, int timeout); + +int +getopt(int argc, char *const argv[], const char *optstring); + +int +sched_yield(void); + +ssize_t +getrandom(void *buf, size_t buflen, unsigned int flags); + +int +getentropy(void *buffer, size_t length); + +int +get_errno(void); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_FILE_H */ diff --git a/core/shared/platform/linux-sgx/sgx_ipfs.c b/core/shared/platform/linux-sgx/sgx_ipfs.c new file mode 100644 index 0000000000..3a46a41709 --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_ipfs.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2022 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if WASM_ENABLE_SGX_IPFS != 0 + +#include "ssp_config.h" +#include "bh_platform.h" +#include "sgx_ipfs.h" + +#include + +#include "sgx_tprotected_fs.h" + +#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; + +// Converts an SGX error code to a POSIX error code. +static __wasi_errno_t +convert_sgx_errno(int error) +{ + if (error >= SGX_ERROR_FILE_LOWEST_ERROR_ID + && error <= SGX_ERROR_FILE_HIGHEST_ERROR_ID) { + switch (error) { + /* The file is in bad status */ + case SGX_ERROR_FILE_BAD_STATUS: + return ENOTRECOVERABLE; + /* The Key ID field is all zeros, can't re-generate the encryption + * key */ + case SGX_ERROR_FILE_NO_KEY_ID: + return EKEYREJECTED; + /* The current file name is different then the original file name + * (not allowed, substitution attack) */ + case SGX_ERROR_FILE_NAME_MISMATCH: + return EIO; + /* The file is not an SGX file */ + case SGX_ERROR_FILE_NOT_SGX_FILE: + return EEXIST; + /* A recovery file can't be opened, so flush operation can't + * continue (only used when no EXXX is returned) */ + case SGX_ERROR_FILE_CANT_OPEN_RECOVERY_FILE: + return EIO; + /* A recovery file can't be written, so flush operation can't + * continue (only used when no EXXX is returned) */ + case SGX_ERROR_FILE_CANT_WRITE_RECOVERY_FILE: + return EIO; + /* When opening the file, recovery is needed, but the recovery + * process failed */ + case SGX_ERROR_FILE_RECOVERY_NEEDED: + return EIO; + /* fflush operation (to disk) failed (only used when no EXXX is + * returned) */ + case SGX_ERROR_FILE_FLUSH_FAILED: + return EIO; + /* fclose operation (to disk) failed (only used when no EXXX is + * returned) */ + case SGX_ERROR_FILE_CLOSE_FAILED: + return EIO; + } + } + + return error; +} + +static void * +fd2file(int fd) +{ + return bh_hash_map_find(ipfs_file_list, (void *)(intptr_t)fd); +} + +static void +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() +{ + ipfs_file_list = + bh_hash_map_create(32, true, (HashFunc)fd_hash, (KeyEqualFunc)fd_equal, + NULL, (ValueDestroyFunc)ipfs_file_destroy); + + return ipfs_file_list != NULL ? BHT_OK : BHT_ERROR; +} + +void +ipfs_destroy() +{ + bh_hash_map_destroy(ipfs_file_list); +} + +int +ipfs_posix_fallocate(int fd, off_t offset, size_t len) +{ + void *sgx_file = fd2file(fd); + if (!sgx_file) { + return EBADF; + } + + // The wrapper for fseek takes care of extending the file if sought beyond + // the end + if (ipfs_lseek(fd, offset + len, SEEK_SET) == -1) { + return errno; + } + + // Make sure the file is allocated by flushing it + if (sgx_fflush(sgx_file) != 0) { + return errno; + } + + return 0; +} + +size_t +ipfs_read(int fd, const struct iovec *iov, int iovcnt, bool has_offset, + off_t offset) +{ + int i; + off_t original_offset = 0; + void *sgx_file = fd2file(fd); + size_t read_result, number_of_read_bytes = 0; + + if (!sgx_file) { + errno = EBADF; + return -1; + } + + if (has_offset) { + // Save the current offset, to restore it after the read operation + original_offset = (off_t)sgx_ftell(sgx_file); + + if (original_offset == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + // Move to the desired location + if (sgx_fseek(sgx_file, offset, SEEK_SET) == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + } + + // For each element in the vector + for (i = 0; i < iovcnt; i++) { + if (iov[i].iov_len == 0) + continue; + + read_result = sgx_fread(iov[i].iov_base, 1, iov[i].iov_len, sgx_file); + number_of_read_bytes += read_result; + + if (read_result != iov[i].iov_len) { + if (!sgx_feof(sgx_file)) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + } + } + + if (has_offset) { + // Restore the position of the cursor + if (sgx_fseek(sgx_file, original_offset, SEEK_SET) == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + } + + return number_of_read_bytes; +} + +size_t +ipfs_write(int fd, const struct iovec *iov, int iovcnt, bool has_offset, + off_t offset) +{ + int i; + off_t original_offset = 0; + void *sgx_file = fd2file(fd); + size_t write_result, number_of_written_bytes = 0; + + if (!sgx_file) { + errno = EBADF; + return -1; + } + + if (has_offset) { + // Save the current offset, to restore it after the read operation + original_offset = (off_t)sgx_ftell(sgx_file); + + if (original_offset == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + // Move to the desired location + if (sgx_fseek(sgx_file, offset, SEEK_SET) == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + } + + // For each element in the vector + for (i = 0; i < iovcnt; i++) { + if (iov[i].iov_len == 0) + continue; + + write_result = sgx_fwrite(iov[i].iov_base, 1, iov[i].iov_len, sgx_file); + number_of_written_bytes += write_result; + + if (write_result != iov[i].iov_len) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + } + + if (has_offset) { + // Restore the position of the cursor + if (sgx_fseek(sgx_file, original_offset, SEEK_SET) == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + } + + return number_of_written_bytes; +} + +int +ipfs_close(int fd) +{ + void *sgx_file; + + if (!bh_hash_map_remove(ipfs_file_list, (void *)(intptr_t)fd, NULL, + &sgx_file)) { + errno = EBADF; + return -1; + } + + if (sgx_fclose(sgx_file)) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + return 0; +} + +void * +ipfs_fopen(int fd, int flags) +{ + // Mapping back the mode + const char *mode; + + bool must_create = (flags & O_CREAT) != 0; + bool must_truncate = (flags & O_TRUNC) != 0; + bool must_append = (flags & O_APPEND) != 0; + bool read_only = (flags & O_ACCMODE) == O_RDONLY; + bool write_only = (flags & O_ACCMODE) == O_WRONLY; + bool read_write = (flags & O_ACCMODE) == O_RDWR; + + // The mapping of the mode is similar to the table in the official + // specifications: + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html + // Note that POSIX has obtained a file descriptor beforehand. + // If opened with a destructive mode ("w" or "w+"), the truncate operation + // already occurred and must not be repeated because this will invalidate + // the file descriptor obtained by POSIX. Therefore, we do NOT map to the + // modes that truncate the file ("w" and "w+"). Instead, we map to a + // non-destructive mode ("r+"). + + if (read_only) + mode = "r"; + else if (write_only && must_create && must_truncate) + // Rather than "w", we map to a non-destructive mode + mode = "r+"; + else if (write_only && must_create && must_append) + mode = "a"; + else if (read_write && must_create && must_append) + mode = "a+"; + else if (read_write) + // Rather than "w+", we map to a non-destructive mode + mode = "r+"; + else + mode = NULL; + + // Cannot map the requested access to the SGX IPFS + if (mode == NULL) { + errno = __WASI_ENOTCAPABLE; + return NULL; + } + + // Determine the symbolic link of the file descriptor, because IPFS does not + // support opening a relative path to a file descriptor (i.e., openat). + // Using the symbolic link in /proc/self allows to retrieve the same path as + // opened by the initial openat and respects the chroot of WAMR. + size_t ret; + char symbolic_path[32]; + ret = + snprintf(symbolic_path, sizeof(symbolic_path), "/proc/self/fd/%d", fd); + if (ret >= sizeof(symbolic_path)) { + errno = ENAMETOOLONG; + return NULL; + } + + // Resolve the symbolic link to real absolute path, because IPFS can only + // open a file with a same file name it was initially created. Otherwise, + // IPFS throws SGX_ERROR_FILE_NAME_MISMATCH. + char real_path[PATH_MAX] = { 0 }; + ret = readlink(symbolic_path, real_path, PATH_MAX - 1); + if (ret == -1) + return NULL; + + // Opening the file using the real path + void *sgx_file = sgx_fopen_auto_key(real_path, mode); + + if (sgx_file == NULL) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return NULL; + } + + if (!bh_hash_map_insert(ipfs_file_list, (void *)(intptr_t)fd, sgx_file)) { + errno = __WASI_ECANCELED; + sgx_fclose(sgx_file); + os_printf("An error occurred while inserting the IPFS file pointer in " + "the map.\n"); + return NULL; + } + + return sgx_file; +} + +int +ipfs_fflush(int fd) +{ + void *sgx_file = fd2file(fd); + + if (!sgx_file) { + errno = EBADF; + return EOF; + } + + int ret = sgx_fflush(sgx_file); + + if (ret == 1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return EOF; + } + + return ret; +} + +off_t +ipfs_lseek(int fd, off_t offset, int nwhence) +{ + off_t cursor_current_location; + void *sgx_file = fd2file(fd); + if (!sgx_file) { + errno = EBADF; + return -1; + } + + // 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) { + cursor_current_location = (off_t)sgx_ftell(sgx_file); + + if (cursor_current_location == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + return cursor_current_location; + } + + int fseek_result = sgx_fseek(sgx_file, offset, nwhence); + + if (fseek_result == 0) { + off_t new_offset = (off_t)sgx_ftell(sgx_file); + + if (new_offset == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + return new_offset; + } + else { + // In the case fseek returned an error + int sgx_error = sgx_ferror(sgx_file); + if (sgx_error != EINVAL) { + errno = convert_sgx_errno(sgx_error); + return -1; + } + + // We must consider a difference in behavior of sgx_fseek and the POSIX + // fseek. If the cursor is moved beyond the end of the file, sgx_fseek + // returns an error, whereas POSIX fseek accepts the cursor move and + // fill with zeroes the difference for the next write. This + // implementation handle zeroes completion and moving the cursor forward + // the end of the file, but does it now (during the fseek), which is + // different compared to POSIX implementation, that writes zeroes on the + // next write. This avoids the runtime to keep track of the cursor + // manually. + + // Assume the error is raised because the cursor is moved beyond the end + // 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 + if (ipfs_write_zeroes(sgx_file, number_of_zeroes) != 0) { + return -1; + } + + // Move again 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; + } + + return offset; + } +} + +// The official API does not provide a way to truncate files. +// Only files extension is supported. +int +ipfs_ftruncate(int fd, off_t len) +{ + void *sgx_file = fd2file(fd); + if (!sgx_file) { + errno = EBADF; + return -1; + } + + off_t original_offset = sgx_ftell(sgx_file); + + // Optimization path: if the length is smaller than the offset, + // IPFS does not support truncate to a smaller size. + if (len < original_offset) { + os_printf( + "SGX IPFS does not support truncate files to smaller sizes.\n"); + return __WASI_ECANCELED; + } + + // Move to the end of the file to determine whether this is + // a file extension or reduction. + if (sgx_fseek(sgx_file, 0, SEEK_END) == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + off_t file_size = sgx_ftell(sgx_file); + + // Reducing the file space is not supported by IPFS. + if (len < file_size) { + os_printf( + "SGX IPFS does not support truncate files to smaller sizes.\n"); + return __WASI_ECANCELED; + } + + // Increasing the size is equal to writing from the end of the file + // with null bytes. + if (ipfs_write_zeroes(sgx_file, len - file_size) != 0) { + return -1; + } + + // Restore the position of the cursor + if (sgx_fseek(sgx_file, original_offset, SEEK_SET) == -1) { + errno = convert_sgx_errno(sgx_ferror(sgx_file)); + return -1; + } + + return 0; +} + +#endif /* end of WASM_ENABLE_SGX_IPFS */ diff --git a/core/shared/platform/linux-sgx/sgx_ipfs.h b/core/shared/platform/linux-sgx/sgx_ipfs.h new file mode 100644 index 0000000000..3a911d2b6e --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_ipfs.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _LIBC_WASI_SGX_PFS_H +#define _LIBC_WASI_SGX_PFS_H + +#include "bh_hashmap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int +ipfs_init(); +void +ipfs_destroy(); +int +ipfs_posix_fallocate(int fd, off_t offset, size_t len); +size_t +ipfs_read(int fd, const struct iovec *iov, int iovcnt, bool has_offset, + off_t offset); +size_t +ipfs_write(int fd, const struct iovec *iov, int iovcnt, bool has_offset, + off_t offset); +int +ipfs_close(int fd); +void * +ipfs_fopen(int fd, int flags); +int +ipfs_fflush(int fd); +off_t +ipfs_lseek(int fd, off_t offset, int nwhence); +int +ipfs_ftruncate(int fd, off_t len); + +/** + * Whether two file descriptors are equal. + */ +inline static bool +fd_equal(int left, int right) +{ + return left == right ? true : false; +} + +/** + * Returns the file descriptor as a hash value. + */ +inline static uint32 +fd_hash(int fd) +{ + return (uint32)fd; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _LIBC_WASI_SGX_PFS_H */ \ No newline at end of file diff --git a/core/shared/platform/linux-sgx/sgx_platform.c b/core/shared/platform/linux-sgx/sgx_platform.c new file mode 100644 index 0000000000..d259908634 --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_platform.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +#include "sgx_rsrv_mem_mngr.h" + +#if WASM_ENABLE_SGX_IPFS != 0 +#include "sgx_ipfs.h" +#endif + +static os_print_function_t print_function = NULL; + +int +bh_platform_init() +{ + int ret = BHT_OK; + +#if WASM_ENABLE_SGX_IPFS != 0 + ret = ipfs_init(); +#endif + + return ret; +} + +void +bh_platform_destroy() +{ +#if WASM_ENABLE_SGX_IPFS != 0 + ipfs_destroy(); +#endif +} + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} + +int +putchar(int c) +{ + return 0; +} + +int +puts(const char *s) +{ + return 0; +} + +void +os_set_print_function(os_print_function_t pf) +{ + print_function = pf; +} + +#define FIXED_BUFFER_SIZE 4096 + +int +os_printf(const char *message, ...) +{ + int bytes_written = 0; + + if (print_function != NULL) { + char msg[FIXED_BUFFER_SIZE] = { '\0' }; + va_list ap; + va_start(ap, message); + vsnprintf(msg, FIXED_BUFFER_SIZE, message, ap); + va_end(ap); + bytes_written += print_function(msg); + } + + return bytes_written; +} + +int +os_vprintf(const char *format, va_list arg) +{ + int bytes_written = 0; + + if (print_function != NULL) { + char msg[FIXED_BUFFER_SIZE] = { '\0' }; + vsnprintf(msg, FIXED_BUFFER_SIZE, format, arg); + bytes_written += print_function(msg); + } + + return bytes_written; +} + +char * +strcpy(char *dest, const char *src) +{ + const unsigned char *s = src; + unsigned char *d = dest; + + while ((*d++ = *s++)) { + } + return dest; +} + +#if WASM_ENABLE_LIBC_WASI == 0 +bool +os_is_handle_valid(os_file_handle *handle) +{ + assert(handle != NULL); + + return *handle > -1; +} +#else +/* implemented in posix_file.c */ +#endif + +static void * +os_mmap_internal(void *hint, size_t size, int prot, int flags, + os_file_handle file, bool clear) +{ + int mprot = 0; + uint64 aligned_size, page_size; + void *ret = NULL; + sgx_status_t st = 0; + + if (os_is_handle_valid(&file)) { + os_printf("os_mmap(size=%u, prot=0x%x, file=%x) failed: file is not " + "supported.\n", + size, prot, file); + return NULL; + } + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + + if (aligned_size >= UINT32_MAX) { + os_printf("mmap failed: request size overflow due to paging\n"); + return NULL; + } + + ret = sgx_alloc_rsrv_mem(aligned_size); + if (ret == NULL) { + os_printf("os_mmap(size=%u, aligned size=%lu, prot=0x%x) failed.\n", + size, aligned_size, prot); + return NULL; + } + + if (clear) { + memset(ret, 0, aligned_size); + } + + if (prot & MMAP_PROT_READ) + mprot |= SGX_PROT_READ; + if (prot & MMAP_PROT_WRITE) + mprot |= SGX_PROT_WRITE; + if (prot & MMAP_PROT_EXEC) + mprot |= SGX_PROT_EXEC; + + st = sgx_tprotect_rsrv_mem(ret, aligned_size, mprot); + if (st != SGX_SUCCESS) { + os_printf("os_mmap(size=%u, prot=0x%x) failed to set protect.\n", size, + prot); + sgx_free_rsrv_mem(ret, aligned_size); + return NULL; + } + + return ret; +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + return os_mmap_internal(hint, size, prot, flags, file, true); +} + +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size) +{ + void *new_memory = + os_mmap_internal(NULL, new_size, MMAP_PROT_WRITE | MMAP_PROT_READ, 0, + os_get_invalid_handle(), false); + if (!new_memory) { + return NULL; + } + size_t copy_size = new_size < old_size ? new_size : old_size; + memcpy(new_memory, old_addr, copy_size); + if (new_size > copy_size) { + memset((char *)new_memory + copy_size, 0, new_size - copy_size); + } + os_munmap(old_addr, old_size); + return new_memory; +} + +void +os_munmap(void *addr, size_t size) +{ + uint64 aligned_size, page_size; + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + sgx_free_rsrv_mem(addr, aligned_size); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + int mprot = 0; + sgx_status_t st = 0; + uint64 aligned_size, page_size; + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + + if (prot & MMAP_PROT_READ) + mprot |= SGX_PROT_READ; + if (prot & MMAP_PROT_WRITE) + mprot |= SGX_PROT_WRITE; + if (prot & MMAP_PROT_EXEC) + mprot |= SGX_PROT_EXEC; + st = sgx_tprotect_rsrv_mem(addr, aligned_size, mprot); + if (st != SGX_SUCCESS) + os_printf("os_mprotect(addr=0x%" PRIx64 + ", size=%u, prot=0x%x) failed.\n", + (uintptr_t)addr, size, prot); + + return (st == SGX_SUCCESS ? 0 : -1); +} + +void +os_dcache_flush(void) +{ +} + +void +os_icache_flush(void *start, size_t len) +{ +} diff --git a/core/shared/platform/linux-sgx/sgx_pthread.c b/core/shared/platform/linux-sgx/sgx_pthread.c new file mode 100644 index 0000000000..7801e3534f --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_pthread.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "sgx_pthread.h" +#include "sgx_error.h" + +#ifndef SGX_DISABLE_WASI + +#define TRACE_FUNC() os_printf("undefined %s\n", __FUNCTION__) +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +#ifndef SGX_THREAD_LOCK_INITIALIZER /* defined since sgxsdk-2.11 */ +/* sgxsdk doesn't support pthread_rwlock related APIs until + version 2.11, we implement them by ourselves. */ +int +ocall_pthread_rwlock_init(int *p_ret, void **rwlock, void *attr); + +int +ocall_pthread_rwlock_destroy(int *p_ret, void **rwlock); + +int +ocall_pthread_rwlock_rdlock(int *p_ret, void **rwlock); + +int +ocall_pthread_rwlock_wrlock(int *p_ret, void **rwlock); + +int +ocall_pthread_rwlock_unlock(int *p_ret, void **rwlock); + +int +pthread_rwlock_init(pthread_rwlock_t *rwlock, void *attr) +{ + int ret = -1; + + if (ocall_pthread_rwlock_init(&ret, (void **)rwlock, NULL) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + (void)attr; + return ret; +} + +int +pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_destroy(&ret, (void *)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + +int +pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_rdlock(&ret, (void *)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + +int +pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_wrlock(&ret, (void *)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + +int +pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_unlock(&ret, (void *)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} +#endif /* end of SGX_THREAD_LOCK_INITIALIZER */ + +#endif diff --git a/core/shared/platform/linux-sgx/sgx_pthread.h b/core/shared/platform/linux-sgx/sgx_pthread.h new file mode 100644 index 0000000000..01a3ae0443 --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_pthread.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_PTHREAD_H +#define _SGX_PTHREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SGX_THREAD_LOCK_INITIALIZER /* defined since sgxsdk-2.11 */ +/* sgxsdk doesn't support pthread_rwlock related APIs until + version 2.11, we implement them by ourselves. */ +typedef uintptr_t pthread_rwlock_t; + +int +pthread_rwlock_init(pthread_rwlock_t *rwlock, void *attr); +int +pthread_rwlock_destroy(pthread_rwlock_t *rwlock); + +int +pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); +int +pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); +int +pthread_rwlock_unlock(pthread_rwlock_t *rwlock); +#endif /* end of SGX_THREAD_LOCK_INITIALIZER */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_PTHREAD_H */ diff --git a/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h b/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h new file mode 100644 index 0000000000..5555d4d9f9 --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * This file is copied from + * https://github.com/intel/linux-sgx/blob/4589daddd58bec7367a6a9de3fe301e6de17671a/common/inc/internal/sgx_rsrv_mem_mngr.h + * The reason we copied here is that the official SGX SDK release has + * not included this header file yet. + */ + +#pragma once + +#ifndef _SGX_RSRV_MEM_MNGR_H_ +#define _SGX_RSRV_MEM_MNGR_H_ + +#include "stdint.h" +#include "sgx_error.h" + +#define SGX_PROT_READ 0x1 /* page can be read */ +#define SGX_PROT_WRITE 0x2 /* page can be written */ +#define SGX_PROT_EXEC 0x4 /* page can be executed */ +#define SGX_PROT_NONE 0x0 /* page can not be accessed */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Allocate a range of EPC memory from the reserved memory area with RW + * permission + * + * Parameters: + * Inputs: length [in]: Size of region to be allocated in bytes. Page aligned. + * Return: Starting address of the new allocated memory area on success; + * otherwise NULL + */ +void * +sgx_alloc_rsrv_mem(size_t length); + +/* Free a range of EPC memory from the reserved memory area + * + * Parameters: + * Inputs: addr[in]: Starting address of region to be freed. Page aligned. + * length[in]: The length of the memory to be freed in bytes. + * Page aligned. + * Return: 0 on success; otherwise -1 + */ +int +sgx_free_rsrv_mem(void *addr, size_t length); + +/* Modify the access permissions of the pages in the reserved memory area. + * + * Parameters: + * Inputs: addr[in]: Starting address of region which needs to change access + * permission. Page aligned. + * length[in]: The length of the memory to be manipulated in bytes. + * Page aligned. + * prot[in]: The target memory protection. + * Return: sgx_status_t - SGX_SUCCESS or failure as defined in sgx_error.h + */ +sgx_status_t +sgx_tprotect_rsrv_mem(void *addr, size_t len, int prot); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/shared/platform/linux-sgx/sgx_signal.c b/core/shared/platform/linux-sgx/sgx_signal.c new file mode 100644 index 0000000000..b52c18821c --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_signal.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#ifndef SGX_DISABLE_WASI + +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +int +ocall_raise(int *p_ret, int sig); + +int +raise(int sig) +{ + int ret; + + if (ocall_raise(&ret, sig) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +#endif diff --git a/core/shared/platform/linux-sgx/sgx_signal.h b/core/shared/platform/linux-sgx/sgx_signal.h new file mode 100644 index 0000000000..494342be3d --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_signal.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_SIGNAL_H +#define _SGX_SIGNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Signals. */ +#define SIGHUP 1 /* Hangup (POSIX). */ +#define SIGINT 2 /* Interrupt (ANSI). */ +#define SIGQUIT 3 /* Quit (POSIX). */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ +#define SIGTRAP 5 /* Trace trap (POSIX). */ +#define SIGABRT 6 /* Abort (ANSI). */ +#define SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define SIGBUS 7 /* BUS error (4.2 BSD). */ +#define SIGFPE 8 /* Floating-point exception (ANSI). */ +#define SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define SIGPIPE 13 /* Broken pipe (POSIX). */ +#define SIGALRM 14 /* Alarm clock (POSIX). */ +#define SIGTERM 15 /* Termination (ANSI). */ +#define SIGSTKFLT 16 /* Stack fault. */ +#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define SIGCHLD 17 /* Child status has changed (POSIX). */ +#define SIGCONT 18 /* Continue (POSIX). */ +#define SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define SIGTTIN 21 /* Background read from tty (POSIX). */ +#define SIGTTOU 22 /* Background write to tty (POSIX). */ +#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define SIGPWR 30 /* Power failure restart (System V). */ +#define SIGSYS 31 /* Bad system call. */ +#define SIGUNUSED 31 + +int +raise(int sig); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_SIGNAL_H */ diff --git a/core/shared/platform/linux-sgx/sgx_socket.c b/core/shared/platform/linux-sgx/sgx_socket.c new file mode 100644 index 0000000000..458bb1e249 --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_socket.c @@ -0,0 +1,1227 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#ifndef SGX_DISABLE_WASI + +#include "libc_errno.h" + +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +/** OCALLs prototypes **/ +int +ocall_accept(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, + uint32_t addr_size); + +int +ocall_bind(int *p_ret, int sockfd, const void *addr, uint32_t addrlen); + +int +ocall_close(int *p_ret, int fd); + +int +ocall_connect(int *p_ret, int sockfd, void *addr, uint32_t addrlen); + +int +ocall_fcntl_long(int *p_ret, int fd, int cmd, long arg); + +int +ocall_getsockname(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, + uint32_t addr_size); + +int +ocall_getpeername(int *p_ret, int sockfd, void *addr, uint32_t *addrlen, + uint32_t addr_size); + +int +ocall_getsockopt(int *p_ret, int sockfd, int level, int optname, void *val_buf, + unsigned int val_buf_size, void *len_buf); + +int +ocall_listen(int *p_ret, int sockfd, int backlog); + +int +ocall_recv(int *p_ret, int sockfd, void *buf, size_t len, int flags); + +int +ocall_recvfrom(ssize_t *p_ret, int sockfd, void *buf, size_t len, int flags, + void *src_addr, uint32_t *addrlen, uint32_t addr_size); + +int +ocall_recvmsg(ssize_t *p_ret, int sockfd, void *msg_buf, + unsigned int msg_buf_size, int flags); + +int +ocall_send(int *p_ret, int sockfd, const void *buf, size_t len, int flags); + +int +ocall_sendto(ssize_t *p_ret, int sockfd, const void *buf, size_t len, int flags, + void *dest_addr, uint32_t addrlen); + +int +ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf, + unsigned int msg_buf_size, int flags); + +int +ocall_setsockopt(int *p_ret, int sockfd, int level, int optname, void *optval, + unsigned int optlen); + +int +ocall_shutdown(int *p_ret, int sockfd, int how); + +int +ocall_socket(int *p_ret, int domain, int type, int protocol); +/** OCALLs prototypes end **/ + +/** In-enclave implementation of POSIX functions **/ +static bool +is_little_endian() +{ + long i = 0x01020304; + unsigned char *c = (unsigned char *)&i; + return (*c == 0x04) ? true : false; +} + +static void +swap32(uint8 *pData) +{ + uint8 value = *pData; + *pData = *(pData + 3); + *(pData + 3) = value; + + value = *(pData + 1); + *(pData + 1) = *(pData + 2); + *(pData + 2) = value; +} + +static void +swap16(uint8 *pData) +{ + uint8 value = *pData; + *(pData) = *(pData + 1); + *(pData + 1) = value; +} + +uint32 +htonl(uint32 value) +{ + uint32 ret; + if (is_little_endian()) { + ret = value; + swap32((uint8 *)&ret); + return ret; + } + + return value; +} + +uint32 +ntohl(uint32 value) +{ + return htonl(value); +} + +uint16 +htons(uint16 value) +{ + uint16 ret; + if (is_little_endian()) { + ret = value; + swap16((uint8 *)&ret); + return ret; + } + + return value; +} + +static uint16 +ntohs(uint16 value) +{ + return htons(value); +} + +/* Coming from musl, under MIT license */ +static int +hexval(unsigned c) +{ + if (c - '0' < 10) + return c - '0'; + c |= 32; + if (c - 'a' < 6) + return c - 'a' + 10; + return -1; +} + +/* Coming from musl, under MIT license */ +static int +inet_pton(int af, const char *restrict s, void *restrict a0) +{ + uint16_t ip[8]; + unsigned char *a = a0; + int i, j, v, d, brk = -1, need_v4 = 0; + + if (af == AF_INET) { + for (i = 0; i < 4; i++) { + for (v = j = 0; j < 3 && isdigit(s[j]); j++) + v = 10 * v + s[j] - '0'; + if (j == 0 || (j > 1 && s[0] == '0') || v > 255) + return 0; + a[i] = v; + if (s[j] == 0 && i == 3) + return 1; + if (s[j] != '.') + return 0; + s += j + 1; + } + return 0; + } + else if (af != AF_INET6) { + errno = EAFNOSUPPORT; + return -1; + } + + if (*s == ':' && *++s != ':') + return 0; + + for (i = 0;; i++) { + if (s[0] == ':' && brk < 0) { + brk = i; + ip[i & 7] = 0; + if (!*++s) + break; + if (i == 7) + return 0; + continue; + } + for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) + v = 16 * v + d; + if (j == 0) + return 0; + ip[i & 7] = v; + if (!s[j] && (brk >= 0 || i == 7)) + break; + if (i == 7) + return 0; + if (s[j] != ':') { + if (s[j] != '.' || (i < 6 && brk < 0)) + return 0; + need_v4 = 1; + i++; + break; + } + s += j + 1; + } + if (brk >= 0) { + memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); + for (j = 0; j < 7 - i; j++) + ip[brk + j] = 0; + } + for (j = 0; j < 8; j++) { + *a++ = ip[j] >> 8; + *a++ = ip[j]; + } + if (need_v4 && inet_pton(AF_INET, (void *)s, a - 4) <= 0) + return 0; + return 1; +} + +static int +inet_addr(const char *p) +{ + struct in_addr a; + if (!inet_pton(AF_INET, p, &a)) + return -1; + return a.s_addr; +} +/** In-enclave implementation of POSIX functions end **/ + +static int +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr_in *out) +{ + assert(textual); + + out->sin_family = AF_INET; + out->sin_port = htons(port); + out->sin_addr.s_addr = inet_addr(textual); + + return BHT_OK; +} + +static int +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, socklen_t socklen, + bh_sockaddr_t *bh_sockaddr) +{ + switch (sockaddr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + + assert(socklen >= sizeof(struct sockaddr_in)); + + bh_sockaddr->port = ntohs(addr->sin_port); + bh_sockaddr->addr_buffer.ipv4 = ntohl(addr->sin_addr.s_addr); + bh_sockaddr->is_ipv4 = true; + return BHT_OK; + } + default: + errno = EAFNOSUPPORT; + return BHT_ERROR; + } +} + +static int +bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, + struct sockaddr *sockaddr, socklen_t *socklen) +{ + if (bh_sockaddr->is_ipv4) { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + addr->sin_port = htons(bh_sockaddr->port); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(bh_sockaddr->addr_buffer.ipv4); + *socklen = sizeof(*addr); + return BHT_OK; + } + else { + errno = EAFNOSUPPORT; + return BHT_ERROR; + } +} + +static int +os_socket_setbooloption(bh_socket_t socket, int level, int optname, + bool is_enabled) +{ + int option = (int)is_enabled; + int ret; + + if (ocall_setsockopt(&ret, socket, level, optname, &option, sizeof(option)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return BHT_ERROR; + } + + if (ret != 0) { + errno = get_errno(); + return BHT_ERROR; + } + + return BHT_OK; +} + +static int +os_socket_getbooloption(bh_socket_t socket, int level, int optname, + bool *is_enabled) +{ + assert(is_enabled); + + int optval; + socklen_t optval_size = sizeof(optval); + int ret; + if (ocall_getsockopt(&ret, socket, level, optname, &optval, optval_size, + &optval_size) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return BHT_ERROR; + } + + if (ret != 0) { + errno = get_errno(); + return BHT_ERROR; + } + + *is_enabled = (bool)optval; + return BHT_OK; +} + +int +socket(int domain, int type, int protocol) +{ + int ret; + + if (ocall_socket(&ret, domain, type, protocol) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) +{ + int ret; + unsigned int val_buf_size = *optlen; + + if (ocall_getsockopt(&ret, sockfd, level, optname, optval, val_buf_size, + (void *)optlen) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +setsockopt(int sockfd, int level, int optname, const void *optval, + socklen_t optlen) +{ + int ret; + + if (ocall_setsockopt(&ret, sockfd, level, optname, (void *)optval, optlen) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags) +{ + ssize_t ret; + int i; + char *p; + struct msghdr *msg1; + + uint64 total_size = sizeof(struct msghdr) + (uint64)msg->msg_namelen + + (uint64)msg->msg_controllen; + + total_size += sizeof(struct iovec) * (msg->msg_iovlen); + + for (i = 0; i < msg->msg_iovlen; i++) { + total_size += msg->msg_iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + + msg1 = BH_MALLOC((uint32)total_size); + + if (msg1 == NULL) + return -1; + + p = (char *)(uintptr_t)sizeof(struct msghdr); + + if (msg->msg_name != NULL) { + msg1->msg_name = p; + memcpy((uintptr_t)p + (char *)msg1, msg->msg_name, + (size_t)msg->msg_namelen); + p += msg->msg_namelen; + } + + if (msg->msg_control != NULL) { + msg1->msg_control = p; + memcpy((uintptr_t)p + (char *)msg1, msg->msg_control, + (size_t)msg->msg_control); + p += msg->msg_controllen; + } + + if (msg->msg_iov != NULL) { + msg1->msg_iov = (struct iovec *)p; + p += (uintptr_t)(sizeof(struct iovec) * (msg->msg_iovlen)); + + for (i = 0; i < msg->msg_iovlen; i++) { + msg1->msg_iov[i].iov_base = p; + msg1->msg_iov[i].iov_len = msg->msg_iov[i].iov_len; + memcpy((uintptr_t)p + (char *)msg1, msg->msg_iov[i].iov_base, + (size_t)(msg->msg_iov[i].iov_len)); + p += msg->msg_iov[i].iov_len; + } + } + + if (ocall_sendmsg(&ret, sockfd, (void *)msg1, (uint32)total_size, flags) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + ssize_t ret; + int i; + char *p; + struct msghdr *msg1; + + uint64 total_size = sizeof(struct msghdr) + (uint64)msg->msg_namelen + + (uint64)msg->msg_controllen; + + total_size += sizeof(struct iovec) * (msg->msg_iovlen); + + for (i = 0; i < msg->msg_iovlen; i++) { + total_size += msg->msg_iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + + msg1 = BH_MALLOC((uint32)total_size); + + if (msg1 == NULL) + return -1; + + memset(msg1, 0, total_size); + + p = (char *)(uintptr_t)sizeof(struct msghdr); + + if (msg->msg_name != NULL) { + msg1->msg_name = p; + p += msg->msg_namelen; + } + + if (msg->msg_control != NULL) { + msg1->msg_control = p; + p += msg->msg_controllen; + } + + if (msg->msg_iov != NULL) { + msg1->msg_iov = (struct iovec *)p; + p += (uintptr_t)(sizeof(struct iovec) * (msg->msg_iovlen)); + + for (i = 0; i < msg->msg_iovlen; i++) { + msg1->msg_iov[i].iov_base = p; + msg1->msg_iov[i].iov_len = msg->msg_iov[i].iov_len; + p += msg->msg_iov[i].iov_len; + } + } + + if (ocall_recvmsg(&ret, sockfd, (void *)msg1, (uint32)total_size, flags) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + p = (char *)(uintptr_t)(sizeof(struct msghdr)); + + if (msg1->msg_name != NULL) { + memcpy(msg->msg_name, (uintptr_t)p + (char *)msg1, + (size_t)msg1->msg_namelen); + p += msg1->msg_namelen; + } + + if (msg1->msg_control != NULL) { + memcpy(msg->msg_control, (uintptr_t)p + (char *)msg1, + (size_t)msg1->msg_control); + p += msg->msg_controllen; + } + + if (msg1->msg_iov != NULL) { + p += (uintptr_t)(sizeof(struct iovec) * (msg1->msg_iovlen)); + + for (i = 0; i < msg1->msg_iovlen; i++) { + memcpy(msg->msg_iov[i].iov_base, (uintptr_t)p + (char *)msg1, + (size_t)(msg1->msg_iov[i].iov_len)); + p += msg1->msg_iov[i].iov_len; + } + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +shutdown(int sockfd, int how) +{ + int ret; + + if (ocall_shutdown(&ret, sockfd, how) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +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); + + if (ocall_accept(sock, server_sock, &addr_tmp, &len, len) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (*sock < 0) { + errno = get_errno(); + return BHT_ERROR; + } + + return BHT_OK; +} +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + struct sockaddr_in addr; + struct linger ling; + unsigned int socklen; + int ret; + + assert(host); + assert(port); + + ling.l_onoff = 1; + ling.l_linger = 0; + + if (ocall_fcntl_long(&ret, socket, F_SETFD, FD_CLOEXEC) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret < 0) { + goto fail; + } + + if (ocall_setsockopt(&ret, socket, SOL_SOCKET, SO_LINGER, &ling, + sizeof(ling)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret < 0) { + goto fail; + } + + addr.sin_addr.s_addr = inet_addr(host); + addr.sin_port = htons(*port); + addr.sin_family = AF_INET; + + if (ocall_bind(&ret, socket, &addr, sizeof(addr)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret < 0) { + goto fail; + } + + socklen = sizeof(addr); + + if (ocall_getsockname(&ret, socket, (void *)&addr, &socklen, socklen) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) { + goto fail; + } + + *port = ntohs(addr.sin_port); + + return BHT_OK; + +fail: + errno = get_errno(); + return BHT_ERROR; +} + +int +os_socket_close(bh_socket_t socket) +{ + int ret; + + if (ocall_close(&ret, socket) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + struct sockaddr_in addr_in = { 0 }; + socklen_t addr_len = sizeof(struct sockaddr_in); + int ret = 0; + + if ((ret = textual_addr_to_sockaddr(addr, port, &addr_in)) < 0) { + return ret; + } + + if (ocall_connect(&ret, socket, &addr_in, addr_len) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + int af; + + if (!sock) { + return BHT_ERROR; + } + + if (is_ipv4) { + af = AF_INET; + } + else { + errno = ENOSYS; + return BHT_ERROR; + } + + if (is_tcp) { + if (ocall_socket(sock, af, SOCK_STREAM, IPPROTO_TCP) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + } + else { + if (ocall_socket(sock, af, SOCK_DGRAM, 0) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + } + + if (*sock == -1) { + errno = get_errno(); + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + if (!cp) + return BHT_ERROR; + + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } + } + + return BHT_OK; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + int ret; + + if (ocall_listen(&ret, socket, max_client) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + int ret; + + if (ocall_recv(&ret, socket, buf, len, 0) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + errno = ENOSYS; + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + ssize_t ret; + + if (ocall_recvfrom(&ret, socket, buf, len, flags, &addr, &addr_len, + addr_len) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + errno = ENOSYS; + return -1; + } + + if (ret < 0) { + errno = get_errno(); + return ret; + } + + if (src_addr && addr_len > 0) { + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, + src_addr) + == BHT_ERROR) { + return -1; + } + } + + return ret; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + int ret; + + if (ocall_send(&ret, socket, buf, len, 0) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + errno = ENOSYS; + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + struct sockaddr_in addr; + socklen_t addr_len; + ssize_t ret; + + if (bh_sockaddr_to_sockaddr(dest_addr, (struct sockaddr *)&addr, &addr_len) + == BHT_ERROR) { + return -1; + } + + if (ocall_sendto(&ret, socket, buf, len, flags, (struct sockaddr *)&addr, + addr_len) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + errno = ENOSYS; + return -1; + } + + if (ret == -1) { + errno = get_errno(); + } + + return ret; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + if (shutdown(socket, O_RDWR) != 0) { + return convert_errno(errno); + } + return __WASI_ESUCCESS; +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int ret; + + if (ocall_getsockname(&ret, socket, (struct sockaddr *)&addr, &addr_len, + addr_len) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return BHT_ERROR; + } + + if (ret != BHT_OK) { + errno = get_errno(); + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, + sockaddr); +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + int ret; + + if (ocall_getpeername(&ret, socket, (void *)&addr, &addr_len, addr_len) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret != BHT_OK) { + errno = get_errno(); + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr, addr_len, + sockaddr); +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +} + +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_QUICKACK, + is_enabled); +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, + is_enabled); +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + if (ipv6) { + return os_socket_setbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); + } + else { + return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + if (ipv6) { + return os_socket_getbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); + } + else { + return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +#endif diff --git a/core/shared/platform/linux-sgx/sgx_socket.h b/core/shared/platform/linux-sgx/sgx_socket.h new file mode 100644 index 0000000000..b1a91cf1cd --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_socket.h @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_SOCKET_H +#define _SGX_SOCKET_H + +#include "sgx_file.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* For setsockopt(2) */ +#define SOL_SOCKET 1 + +#define SO_DEBUG 1 +#define SO_REUSEADDR 2 +#define SO_TYPE 3 +#define SO_ERROR 4 +#define SO_DONTROUTE 5 +#define SO_BROADCAST 6 +#define SO_SNDBUF 7 +#define SO_RCVBUF 8 +#define SO_SNDBUFFORCE 32 +#define SO_RCVBUFFORCE 33 +#define SO_KEEPALIVE 9 +#define SO_OOBINLINE 10 +#define SO_NO_CHECK 11 +#define SO_PRIORITY 12 +#define SO_LINGER 13 +#define SO_BSDCOMPAT 14 +#define SO_REUSEPORT 15 +#define SO_PASSCRED 16 +#define SO_PEERCRED 17 +#define SO_RCVLOWAT 18 +#define SO_SNDLOWAT 19 +#define SO_RCVTIMEO_OLD 20 +#define SO_SNDTIMEO_OLD 21 + +/* User-settable options (used with setsockopt) */ +#define TCP_NODELAY 1 /* Don't delay send to coalesce packets */ +#define TCP_MAXSEG 2 /* Set maximum segment size */ +#define TCP_CORK 3 /* Control sending of partial frames */ +#define TCP_KEEPIDLE 4 /* Start keeplives after this period */ +#define TCP_KEEPINTVL 5 /* Interval between keepalives */ +#define TCP_KEEPCNT 6 /* Number of keepalives before death */ +#define TCP_SYNCNT 7 /* Number of SYN retransmits */ +#define TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */ +#define TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */ +#define TCP_WINDOW_CLAMP 10 /* Bound advertised window */ +#define TCP_INFO 11 /* Information about this connection. */ +#define TCP_QUICKACK 12 /* Bock/re-enable quick ACKs. */ +#define TCP_CONGESTION 13 /* Congestion control algorithm. */ +#define TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */ +#define TCP_COOKIE_TRANSACTIONS 15 /* TCP Cookie Transactions */ +#define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/ +#define TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */ +#define TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */ +#define TCP_REPAIR 19 /* TCP sock is under repair right now */ +#define TCP_REPAIR_QUEUE 20 /* Set TCP queue to repair */ +#define TCP_QUEUE_SEQ 21 /* Set sequence number of repaired queue. */ +#define TCP_REPAIR_OPTIONS 22 /* Repair TCP connection options */ +#define TCP_FASTOPEN 23 /* Enable FastOpen on listeners */ +#define TCP_TIMESTAMP 24 /* TCP time stamp */ +#define TCP_NOTSENT_LOWAT \ + 25 /* Limit number of unsent bytes in write queue. \ + */ +#define TCP_CC_INFO 26 /* Get Congestion Control (optional) info. */ +#define TCP_SAVE_SYN 27 /* Record SYN headers for new connections. */ +#define TCP_SAVED_SYN 28 /* Get SYN headers recorded for connection. */ +#define TCP_REPAIR_WINDOW 29 /* Get/set window parameters. */ +#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */ +#define TCP_ULP 31 /* Attach a ULP to a TCP connection. */ +#define TCP_MD5SIG_EXT 32 /* TCP MD5 Signature with extensions. */ +#define TCP_FASTOPEN_KEY 33 /* Set the key for Fast Open (cookie). */ +#define TCP_FASTOPEN_NO_COOKIE 34 /* Enable TFO without a TFO cookie. */ +#define TCP_ZEROCOPY_RECEIVE 35 +#define TCP_INQ 36 /* Notify bytes available to read as a cmsg on read. */ +#define TCP_CM_INQ TCP_INQ +#define TCP_TX_DELAY 37 /* Delay outgoing packets by XX usec. */ + +/* Standard well-defined IP protocols. */ +#define IPPROTO_IP 0 /* Dummy protocol for TCP. */ +#define IPPROTO_ICMP 1 /* Internet Control Message Protocol. */ +#define IPPROTO_IGMP 2 /* Internet Group Management Protocol. */ +#define IPPROTO_IPIP 4 /* IPIP tunnels (older KA9Q tunnels use 94). */ +#define IPPROTO_TCP 6 /* Transmission Control Protocol. */ +#define IPPROTO_EGP 8 /* Exterior Gateway Protocol. */ +#define IPPROTO_PUP 12 /* PUP protocol. */ +#define IPPROTO_UDP 17 /* User Datagram Protocol. */ +#define IPPROTO_IDP 22 /* XNS IDP protocol. */ +#define IPPROTO_TP 29 /* SO Transport Protocol Class 4. */ +#define IPPROTO_DCCP 33 /* Datagram Congestion Control Protocol. */ +#define IPPROTO_IPV6 41 /* IPv6 header. */ +#define IPPROTO_RSVP 46 /* Reservation Protocol. */ +#define IPPROTO_GRE 47 /* General Routing Encapsulation. */ +#define IPPROTO_ESP 50 /* encapsulating security payload. */ +#define IPPROTO_AH 51 /* authentication header. */ +#define IPPROTO_MTP 92 /* Multicast Transport Protocol. */ +#define IPPROTO_BEETPH 94 /* IP option pseudo header for BEET. */ +#define IPPROTO_ENCAP 98 /* Encapsulation Header. */ +#define IPPROTO_PIM 103 /* Protocol Independent Multicast. */ +#define IPPROTO_COMP 108 /* Compression Header Protocol. */ +#define IPPROTO_SCTP 132 /* Stream Control Transmission Protocol. */ +#define IPPROTO_UDPLITE 136 /* UDP-Lite protocol. */ +#define IPPROTO_MPLS 137 /* MPLS in IP. */ +#define IPPROTO_RAW 255 /* Raw IP packets. */ + +#define IP_ROUTER_ALERT 5 /* bool */ +#define IP_PKTINFO 8 /* bool */ +#define IP_PKTOPTIONS 9 +#define IP_PMTUDISC 10 /* obsolete name? */ +#define IP_MTU_DISCOVER 10 /* int; see below */ +#define IP_RECVERR 11 /* bool */ +#define IP_RECVTTL 12 /* bool */ +#define IP_RECVTOS 13 /* bool */ +#define IP_MTU 14 /* int */ +#define IP_FREEBIND 15 +#define IP_IPSEC_POLICY 16 +#define IP_XFRM_POLICY 17 +#define IP_PASSSEC 18 +#define IP_TRANSPARENT 19 +#define IP_MULTICAST_ALL 49 /* bool */ + +/* TProxy original addresses */ +#define IP_ORIGDSTADDR 20 +#define IP_RECVORIGDSTADDR IP_ORIGDSTADDR +#define IP_MINTTL 21 +#define IP_NODEFRAG 22 +#define IP_CHECKSUM 23 +#define IP_BIND_ADDRESS_NO_PORT 24 +#define IP_RECVFRAGSIZE 25 +#define IP_PMTUDISC_DONT 0 +#define IP_PMTUDISC_WANT 1 +#define IP_PMTUDISC_DO 2 +#define IP_PMTUDISC_PROBE 3 +#define IP_PMTUDISC_INTERFACE 4 +#define IP_PMTUDISC_OMIT 5 +#define IP_MULTICAST_IF 32 +#define IP_MULTICAST_TTL 33 +#define IP_MULTICAST_LOOP 34 +#define IP_ADD_MEMBERSHIP 35 +#define IP_DROP_MEMBERSHIP 36 +#define IP_UNBLOCK_SOURCE 37 +#define IP_BLOCK_SOURCE 38 +#define IP_ADD_SOURCE_MEMBERSHIP 39 +#define IP_DROP_SOURCE_MEMBERSHIP 40 +#define IP_MSFILTER 41 +#define IP_MULTICAST_ALL 49 +#define IP_UNICAST_IF 50 + +#define IPV6_ADDRFORM 1 +#define IPV6_2292PKTINFO 2 +#define IPV6_2292HOPOPTS 3 +#define IPV6_2292DSTOPTS 4 +#define IPV6_2292RTHDR 5 +#define IPV6_2292PKTOPTIONS 6 +#define IPV6_CHECKSUM 7 +#define IPV6_2292HOPLIMIT 8 + +#define SCM_SRCRT IPV6_RXSRCRT + +#define IPV6_NEXTHOP 9 +#define IPV6_AUTHHDR 10 +#define IPV6_UNICAST_HOPS 16 +#define IPV6_MULTICAST_IF 17 +#define IPV6_MULTICAST_HOPS 18 +#define IPV6_MULTICAST_LOOP 19 +#define IPV6_JOIN_GROUP 20 +#define IPV6_LEAVE_GROUP 21 +#define IPV6_ROUTER_ALERT 22 +#define IPV6_MTU_DISCOVER 23 +#define IPV6_MTU 24 +#define IPV6_RECVERR 25 +#define IPV6_V6ONLY 26 +#define IPV6_JOIN_ANYCAST 27 +#define IPV6_LEAVE_ANYCAST 28 +#define IPV6_MULTICAST_ALL 29 +#define IPV6_ROUTER_ALERT_ISOLATE 30 +#define IPV6_IPSEC_POLICY 34 +#define IPV6_XFRM_POLICY 35 +#define IPV6_HDRINCL 36 + +/* Advanced API (RFC3542) (1). */ +#define IPV6_RECVPKTINFO 49 +#define IPV6_PKTINFO 50 +#define IPV6_RECVHOPLIMIT 51 +#define IPV6_HOPLIMIT 52 +#define IPV6_RECVHOPOPTS 53 +#define IPV6_HOPOPTS 54 +#define IPV6_RTHDRDSTOPTS 55 +#define IPV6_RECVRTHDR 56 +#define IPV6_RTHDR 57 +#define IPV6_RECVDSTOPTS 58 +#define IPV6_DSTOPTS 59 +#define IPV6_RECVPATHMTU 60 +#define IPV6_PATHMTU 61 +#define IPV6_DONTFRAG 62 + +/* Advanced API (RFC3542) (2). */ +#define IPV6_RECVTCLASS 66 +#define IPV6_TCLASS 67 + +#define IPV6_AUTOFLOWLABEL 70 + +/* RFC5014. */ +#define IPV6_ADDR_PREFERENCES 72 + +/* RFC5082. */ +#define IPV6_MINHOPCOUNT 73 + +#define IPV6_ORIGDSTADDR 74 +#define IPV6_RECVORIGDSTADDR IPV6_ORIGDSTADDR +#define IPV6_TRANSPARENT 75 +#define IPV6_UNICAST_IF 76 +#define IPV6_RECVFRAGSIZE 77 +#define IPV6_FREEBIND 78 + +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 + +#define MSG_OOB 0x0001 +#define MSG_PEEK 0x0002 +#define MSG_DONTROUTE 0x0004 +#define MSG_CTRUNC 0x0008 +#define MSG_PROXY 0x0010 +#define MSG_TRUNC 0x0020 +#define MSG_DONTWAIT 0x0040 +#define MSG_EOR 0x0080 +#define MSG_WAITALL 0x0100 +#define MSG_FIN 0x0200 +#define MSG_SYN 0x0400 +#define MSG_CONFIRM 0x0800 +#define MSG_RST 0x1000 +#define MSG_ERRQUEUE 0x2000 +#define MSG_NOSIGNAL 0x4000 +#define MSG_MORE 0x8000 +#define MSG_WAITFORONE 0x10000 +#define MSG_BATCH 0x40000 +#define MSG_FASTOPEN 0x20000000 +#define MSG_CMSG_CLOEXEC 0x40000000 + +#define SHUT_RD 0 +#define SHUT_WR 1 +#define SHUT_RDWR 2 + +/* Address families. */ +#define AF_INET 2 /* IP protocol family. */ +#define AF_INET6 10 /* IP version 6. */ + +/* Standard well-defined IP protocols. */ +#define IPPROTO_TCP 6 /* Transmission Control Protocol. */ + +/* Types of sockets. */ +#define SOCK_DGRAM \ + 2 /* Connectionless, unreliable datagrams of fixed maximum length. */ + +struct msghdr { + void *msg_name; + socklen_t msg_namelen; + struct iovec *msg_iov; + int msg_iovlen; + void *msg_control; + socklen_t msg_controllen; + int msg_flags; +}; + +/* Internet address. */ +struct in_addr { + uint32_t s_addr; +}; +typedef struct in_addr in_addr_t; + +/* Structure describing an Internet socket address. */ +#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ +struct sockaddr_in { + uint16_t sin_family; + uint16_t sin_port; /* Port number. */ + struct in_addr sin_addr; /* Internet address. */ + + /* Pad to size of `struct sockaddr'. */ + unsigned char__pad[__SOCK_SIZE__ - sizeof(uint16_t) - sizeof(uint16_t) + - sizeof(struct in_addr)]; +}; + +/* Structure used to manipulate the SO_LINGER option. */ +struct linger { + int l_onoff; /* Nonzero to linger on close. */ + int l_linger; /* Time to linger. */ +}; + +/* Structure describing a generic socket address. */ +struct sockaddr { + unsigned short int sa_family; /* Common data: address family and length. */ + char sa_data[14]; /* Address data. */ +}; + +uint32_t +ntohl(uint32_t value); + +uint32_t +htonl(uint32_t value); + +uint16_t +htons(uint16_t value); + +int +socket(int domain, int type, int protocol); + +int +getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); + +int +setsockopt(int sockfd, int level, int optname, const void *optval, + socklen_t optlen); + +ssize_t +sendmsg(int sockfd, const struct msghdr *msg, int flags); + +ssize_t +recvmsg(int sockfd, struct msghdr *msg, int flags); + +int +shutdown(int sockfd, int how); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_SOCKET_H */ diff --git a/core/shared/platform/linux-sgx/sgx_thread.c b/core/shared/platform/linux-sgx/sgx_thread.c new file mode 100644 index 0000000000..73f3005fab --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_thread.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#ifndef SGX_DISABLE_PTHREAD +typedef struct { + thread_start_routine_t start; + void *arg; +} thread_wrapper_arg; + +static void * +os_thread_wrapper(void *arg) +{ + thread_wrapper_arg *targ = arg; + thread_start_routine_t start_func = targ->start; + void *thread_arg = targ->arg; + +#if 0 + os_printf("THREAD CREATED %p\n", &targ); +#endif + BH_FREE(targ); + start_func(thread_arg); + return NULL; +} + +int +os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + thread_wrapper_arg *targ; + + assert(tid); + assert(start); + + targ = (thread_wrapper_arg *)BH_MALLOC(sizeof(*targ)); + if (!targ) { + return BHT_ERROR; + } + + targ->start = start; + targ->arg = arg; + + if (pthread_create(tid, NULL, os_thread_wrapper, targ) != 0) { + BH_FREE(targ); + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} +#endif + +korp_tid +os_self_thread() +{ +#ifndef SGX_DISABLE_PTHREAD + return pthread_self(); +#else + return 0; +#endif +} + +int +os_mutex_init(korp_mutex *mutex) +{ +#ifndef SGX_DISABLE_PTHREAD + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + *mutex = m; +#endif + return BHT_OK; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ +#ifndef SGX_DISABLE_PTHREAD + pthread_mutex_destroy(mutex); +#endif + return BHT_OK; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ +#ifndef SGX_DISABLE_PTHREAD + return pthread_mutex_lock(mutex); +#else + return 0; +#endif +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ +#ifndef SGX_DISABLE_PTHREAD + return pthread_mutex_unlock(mutex); +#else + return 0; +#endif +} + +int +os_cond_init(korp_cond *cond) +{ +#ifndef SGX_DISABLE_PTHREAD + pthread_cond_t c = PTHREAD_COND_INITIALIZER; + *cond = c; +#endif + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ +#ifndef SGX_DISABLE_PTHREAD + pthread_cond_destroy(cond); +#endif + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(cond); + assert(mutex); + + if (pthread_cond_wait(cond, mutex) != BHT_OK) + return BHT_ERROR; + +#endif + return BHT_OK; +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + os_printf("warning: SGX pthread_cond_timedwait isn't supported, " + "calling pthread_cond_wait instead!\n"); + return BHT_ERROR; +} + +int +os_cond_signal(korp_cond *cond) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(cond); + + if (pthread_cond_signal(cond) != BHT_OK) + return BHT_ERROR; + +#endif + return BHT_OK; +} + +int +os_cond_broadcast(korp_cond *cond) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(cond); + + if (pthread_cond_broadcast(cond) != BHT_OK) + return BHT_ERROR; + +#endif + return BHT_OK; +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ +#ifndef SGX_DISABLE_PTHREAD + return pthread_join(thread, value_ptr); +#else + return 0; +#endif +} + +int +os_thread_detach(korp_tid thread) +{ + /* SGX pthread_detach isn't provided, return directly. */ + return 0; +} + +void +os_thread_exit(void *retval) +{ +#ifndef SGX_DISABLE_PTHREAD + pthread_exit(retval); +#else + return; +#endif +} + +uint8 * +os_thread_get_stack_boundary() +{ + /* TODO: get sgx stack boundary */ + return NULL; +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} + +int +os_rwlock_init(korp_rwlock *lock) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(lock); + + if (pthread_rwlock_init(lock, NULL) != BHT_OK) + return BHT_ERROR; +#endif + + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(lock); + + if (pthread_rwlock_rdlock(lock) != BHT_OK) + return BHT_ERROR; +#endif + + return BHT_OK; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(lock); + + if (pthread_rwlock_wrlock(lock) != BHT_OK) + return BHT_ERROR; +#endif + + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(lock); + + if (pthread_rwlock_unlock(lock) != BHT_OK) + return BHT_ERROR; +#endif + + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ +#ifndef SGX_DISABLE_PTHREAD + assert(lock); + + if (pthread_rwlock_destroy(lock) != BHT_OK) + return BHT_ERROR; +#endif + + return BHT_OK; +} diff --git a/core/shared/platform/linux-sgx/sgx_time.c b/core/shared/platform/linux-sgx/sgx_time.c new file mode 100644 index 0000000000..d39db22fbc --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_time.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#define TRACE_FUNC() os_printf("undefined %s\n", __FUNCTION__) +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +int +ocall_clock_gettime(int *p_ret, unsigned clock_id, void *tp_buf, + unsigned int tp_buf_size); +int +ocall_clock_getres(int *p_ret, int clock_id, void *res_buf, + unsigned int res_buf_size); +int +ocall_utimensat(int *p_ret, int dirfd, const char *pathname, + const void *times_buf, unsigned int times_buf_size, int flags); +int +ocall_futimens(int *p_ret, int fd, const void *times_buf, + unsigned int times_buf_size); +int +ocall_clock_nanosleep(int *p_ret, unsigned clock_id, int flags, + const void *req_buf, unsigned int req_buf_size, + const void *rem_buf, unsigned int rem_buf_size); + +uint64 +os_time_get_boot_us() +{ +#ifndef SGX_DISABLE_WASI + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + + return ((uint64)ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +#else + return 0; +#endif +} + +uint64 +os_time_thread_cputime_us(void) +{ +#ifndef SGX_DISABLE_WASI + struct timespec ts; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) { + return 0; + } + + return ((uint64)ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +#else + return 0; +#endif +} + +#ifndef SGX_DISABLE_WASI + +int +clock_getres(int clock_id, struct timespec *res) +{ + int ret; + + if (ocall_clock_getres(&ret, clock_id, (void *)res, sizeof(struct timespec)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +clock_gettime(clockid_t clock_id, struct timespec *tp) +{ + int ret; + + if (ocall_clock_gettime(&ret, clock_id, (void *)tp, sizeof(struct timespec)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +utimensat(int dirfd, const char *pathname, const struct timespec times[2], + int flags) +{ + int ret; + + if (ocall_utimensat(&ret, dirfd, pathname, (void *)times, + sizeof(struct timespec) * 2, flags) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +futimens(int fd, const struct timespec times[2]) +{ + int ret; + + if (ocall_futimens(&ret, fd, (void *)times, sizeof(struct timespec) * 2) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int +clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, + struct timespec *remain) +{ + int ret; + + if (ocall_clock_nanosleep(&ret, clock_id, flags, (void *)request, + sizeof(struct timespec), (void *)remain, + sizeof(struct timespec)) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +#endif diff --git a/core/shared/platform/linux-sgx/sgx_time.h b/core/shared/platform/linux-sgx/sgx_time.h new file mode 100644 index 0000000000..8267f1fa50 --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_time.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_TIME_H +#define _SGX_TIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOCK_THREAD_CPUTIME_ID 3 + +#define UTIME_NOW 0x3fffffff +#define UTIME_OMIT 0x3ffffffe +#define TIMER_ABSTIME 1 + +typedef long int time_t; + +typedef int clockid_t; + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +int +clock_getres(int clock_id, struct timespec *res); + +int +clock_gettime(clockid_t clock_id, struct timespec *tp); + +int +utimensat(int dirfd, const char *pathname, const struct timespec times[2], + int flags); +int +futimens(int fd, const struct timespec times[2]); +int +clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, + struct timespec *remain); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_TIME_H */ diff --git a/core/shared/platform/linux-sgx/sgx_wamr.edl b/core/shared/platform/linux-sgx/sgx_wamr.edl new file mode 100644 index 0000000000..7cb4817fda --- /dev/null +++ b/core/shared/platform/linux-sgx/sgx_wamr.edl @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +enclave { + include "stdint.h" + include "stdbool.h" + include "unistd.h" + + untrusted { + int ocall_open([in, string]const char *pathname, int flags, + bool has_mode, unsigned mode); + int ocall_openat(int dirfd, + [in, string]const char *pathname, int flags, + bool has_mode, unsigned mode); + int ocall_close(int fd); + ssize_t ocall_read(int fd, [out, size=read_size]void *buf, + size_t read_size); + off_t ocall_lseek(int fd, off_t offset, int whence); + int ocall_ftruncate(int fd, off_t length); + int ocall_fsync(int fd); + int ocall_fdatasync(int fd); + int ocall_isatty(int fd); + void ocall_fdopendir(int fd, [out]void **p_dirp); + /* implementation related to multiple thread */ + void *ocall_readdir([user_check]void *dirp); + void ocall_rewinddir([user_check]void *dirp); + void ocall_seekdir([user_check]void *dirp, long loc); + long ocall_telldir([user_check]void *dirp); + int ocall_closedir([user_check]void *dirp); + + int ocall_stat([in, string]const char *pathname, + [out, size=buf_len]void *buf, + unsigned int buf_len); + int ocall_fstat(int fd, [out, size=buf_len]void *buf, + unsigned int buf_len); + int ocall_fstatat(int dirfd, [in, string]const char *pathname, + [out, size=buf_len]void *buf, + unsigned int buf_len, int flags); + + int ocall_mkdirat(int dirfd, [in, string]const char *pathname, + unsigned mode); + int ocall_link([in, string] const char *oldpath, + [in, string] const char *newpath); + int ocall_linkat(int olddirfd, [in, string]const char *oldpath, + int newdirfd, [in, string]const char *newpath, + int flags); + int ocall_unlinkat(int dirfd, [in, string]const char *pathname, + int flags); + ssize_t ocall_readlink([in, string]const char *pathname, + [out, size=bufsiz]char *buf, + size_t bufsiz); + ssize_t ocall_readlinkat(int dirfd, + [in, string]const char *pathname, + [out, size=bufsiz]char *buf, + size_t bufsiz); + int ocall_renameat(int olddirfd, + [in, string]const char *oldpath, + int newdirfd, + [in, string]const char *newpath); + int ocall_symlinkat([in ,string]const char *target, + int newdirfd, + [in, string]const char *linkpath); + + int ocall_ioctl(int fd, unsigned long request, + [out, size=arg_len]void *arg, + unsigned int arg_len); + int ocall_fcntl(int fd, int cmd); + int ocall_fcntl_long(int fd, int cmd, long arg); + + int ocall_realpath([in, string]const char *path, + [out, size=buf_len]char *buf, + unsigned int buf_len); + int ocall_posix_fallocate(int fd, off_t offset, off_t len); + int ocall_poll([in, out, size=fds_len]void *fds, unsigned nfds, + int timeout, unsigned int fds_len); + + int ocall_getopt(int argc, + [in, size=argv_buf_len]char *argv_buf, + unsigned int argv_buf_len, + [in, string]const char *optstring); + ssize_t ocall_readv(int fd, + [in, out, size=buf_size]char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset); + ssize_t ocall_writev(int fd, + [in, size=buf_size]char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset); + + /* time clock */ + int ocall_clock_gettime(unsigned clock_id, + [out, size=tp_buf_size]void *tp_buf, + unsigned int tp_buf_size); + int ocall_clock_getres(int clock_id, + [out, size=res_buf_size]void *res_buf, + unsigned int res_buf_size); + int ocall_utimensat(int dirfd, [in, string]const char *pathname, + [in, size=times_buf_size]const void *times_buf, + unsigned int times_buf_size, int flags); + int ocall_futimens(int fd, [in, size=times_buf_size]const void *times_buf, + unsigned int times_buf_size); + int ocall_clock_nanosleep(unsigned clock_id, int flags, + [in, size=req_buf_size]const void *req_buf, + unsigned int req_buf_size, + [out, size=rem_buf_size]void *rem_buf, + unsigned int rem_buf_size); + + int ocall_raise(int sig); + + int ocall_sched_yield(); + + int ocall_pthread_rwlock_init([out]void **rwlock, [user_check]void *attr); + int ocall_pthread_rwlock_destroy([user_check]void *rwlock); + int ocall_pthread_rwlock_rdlock([user_check]void *rwlock); + int ocall_pthread_rwlock_wrlock([user_check]void *rwlock); + int ocall_pthread_rwlock_unlock([user_check]void *rwlock); + + int ocall_get_errno(); + + /* sockets */ + int ocall_accept(int sockfd, [in, size=addr_size]void *addr, + [in, size=4] uint32_t *addrlen, uint32_t addr_size); + int ocall_bind(int sockfd, [in, size=addrlen]const void *addr, + uint32_t addrlen); + int ocall_connect(int sockfd, [in, size=addrlen]void *addr, uint32_t addrlen); + int ocall_getsockname(int sockfd, [out, size=addr_size]void *addr, + [in, out, size=4]uint32_t *addrlen, uint32_t addr_size); + int ocall_getpeername(int sockfd, [out, size=addr_size]void *addr, + [in, out, size=4]uint32_t *addrlen, uint32_t addr_size); + int ocall_getsockopt(int sockfd, int level, int optname, + [out, size=val_buf_size]void *val_buf, + unsigned int val_buf_size, + [in, out, size=4]void *len_buf); + int ocall_listen(int sockfd, int backlog); + int ocall_recv(int sockfd, [out, size=len]void *buf, size_t len, int flags); + ssize_t ocall_recvfrom(int sockfd, [out, size=len]void *buf, size_t len, int flags, + [out, size=addr_size]void *src_addr, + [in, out, size=4]uint32_t *addrlen, uint32_t addr_size); + ssize_t ocall_recvmsg(int sockfd, + [in, out, size=msg_buf_size]void *msg_buf, + unsigned int msg_buf_size, + int flags); + int ocall_send(int sockfd, [in, size=len]const void *buf, size_t len, int flags); + ssize_t ocall_sendto(int sockfd, [in, size=len]const void *buf, size_t len, int flags, + [in, size=addrlen]void *dest_addr, uint32_t addrlen); + ssize_t ocall_sendmsg(int sockfd, + [in, size=msg_buf_size]void *msg_buf, + unsigned int msg_buf_size, + int flags); + int ocall_setsockopt(int sockfd, int level, int optname, + [in, size=optlen]void *optval, + unsigned int optlen); + int ocall_shutdown(int sockfd, int how); + int ocall_socket(int domain, int type, int protocol); + }; +}; diff --git a/core/shared/platform/linux-sgx/shared_platform.cmake b/core/shared/platform/linux-sgx/shared_platform.cmake index 2fdc1330e3..e8e1670058 100644 --- a/core/shared/platform/linux-sgx/shared_platform.cmake +++ b/core/shared/platform/linux-sgx/shared_platform.cmake @@ -3,11 +3,43 @@ set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) +add_definitions(-DBH_PLATFORM_LINUX_SGX) + include_directories(${PLATFORM_SHARED_DIR}) include_directories(${PLATFORM_SHARED_DIR}/../include) +if ("$ENV{SGX_SDK}" STREQUAL "") + set (SGX_SDK_DIR "/opt/intel/sgxsdk") +else() + set (SGX_SDK_DIR $ENV{SGX_SDK}) +endif() + +include_directories (${SGX_SDK_DIR}/include) +if (NOT BUILD_UNTRUST_PART EQUAL 1) + include_directories (${SGX_SDK_DIR}/include/tlibc + ${SGX_SDK_DIR}/include/libcxx) +endif () + +if (NOT WAMR_BUILD_THREAD_MGR EQUAL 1) + add_definitions(-DSGX_DISABLE_PTHREAD) +endif () -file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) +file (GLOB source_all ${PLATFORM_SHARED_DIR}/*.c) + +if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1) + add_definitions(-DSGX_DISABLE_WASI) +else() + list(APPEND source_all + ${PLATFORM_SHARED_DIR}/../common/posix/posix_file.c + ${PLATFORM_SHARED_DIR}/../common/posix/posix_clock.c + ) + include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) + set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) +endif() + +file (GLOB source_all_untrusted ${PLATFORM_SHARED_DIR}/untrusted/*.c) set (PLATFORM_SHARED_SOURCE ${source_all}) +set (PLATFORM_SHARED_SOURCE_UNTRUSTED ${source_all_untrusted}) + diff --git a/core/shared/platform/linux-sgx/untrusted/file.c b/core/shared/platform/linux-sgx/untrusted/file.c new file mode 100644 index 0000000000..cb9bf6a210 --- /dev/null +++ b/core/shared/platform/linux-sgx/untrusted/file.c @@ -0,0 +1,321 @@ +/* + * 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 +#include +#include +#include + +int +ocall_open(const char *pathname, int flags, bool has_mode, unsigned mode) +{ + if (has_mode) { + return open(pathname, flags, (mode_t)mode); + } + else { + return open(pathname, flags); + } +} + +int +ocall_openat(int dirfd, const char *pathname, int flags, bool has_mode, + unsigned mode) +{ + if (has_mode) { + return openat(dirfd, pathname, flags, (mode_t)mode); + } + else { + return openat(dirfd, pathname, flags); + } +} + +int +ocall_close(int fd) +{ + return close(fd); +} + +ssize_t +ocall_read(int fd, void *buf, size_t read_size) +{ + if (buf != NULL) { + return read(fd, buf, read_size); + } + else { + return -1; + } +} + +off_t +ocall_lseek(int fd, off_t offset, int whence) +{ + return lseek(fd, offset, whence); +} + +int +ocall_ftruncate(int fd, off_t length) +{ + return ftruncate(fd, length); +} + +int +ocall_fsync(int fd) +{ + return fsync(fd); +} + +int +ocall_fdatasync(int fd) +{ + return fdatasync(fd); +} + +int +ocall_isatty(int fd) +{ + return isatty(fd); +} + +void +ocall_fdopendir(int fd, void **dirp) +{ + if (dirp) { + *(DIR **)dirp = fdopendir(fd); + } +} + +void * +ocall_readdir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + return readdir(p_dirp); +} + +void +ocall_rewinddir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + if (p_dirp) { + rewinddir(p_dirp); + } +} + +void +ocall_seekdir(void *dirp, long loc) +{ + DIR *p_dirp = (DIR *)dirp; + + if (p_dirp) { + seekdir(p_dirp, loc); + } +} + +long +ocall_telldir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + if (p_dirp) { + return telldir(p_dirp); + } + return -1; +} + +int +ocall_closedir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + if (p_dirp) { + return closedir(p_dirp); + } + return -1; +} + +int +ocall_stat(const char *pathname, void *buf, unsigned int buf_len) +{ + return stat(pathname, (struct stat *)buf); +} + +int +ocall_fstat(int fd, void *buf, unsigned int buf_len) +{ + return fstat(fd, (struct stat *)buf); +} + +int +ocall_fstatat(int dirfd, const char *pathname, void *buf, unsigned int buf_len, + int flags) +{ + return fstatat(dirfd, pathname, (struct stat *)buf, flags); +} + +int +ocall_mkdirat(int dirfd, const char *pathname, unsigned mode) +{ + return mkdirat(dirfd, pathname, (mode_t)mode); +} + +int +ocall_link(const char *oldpath, const char *newpath) +{ + return link(oldpath, newpath); +} + +int +ocall_linkat(int olddirfd, const char *oldpath, int newdirfd, + const char *newpath, int flags) +{ + return linkat(olddirfd, oldpath, newdirfd, newpath, flags); +} + +int +ocall_unlinkat(int dirfd, const char *pathname, int flags) +{ + return unlinkat(dirfd, pathname, flags); +} + +ssize_t +ocall_readlink(const char *pathname, char *buf, size_t bufsiz) +{ + return readlink(pathname, buf, bufsiz); +} + +ssize_t +ocall_readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) +{ + return readlinkat(dirfd, pathname, buf, bufsiz); +} + +int +ocall_renameat(int olddirfd, const char *oldpath, int newdirfd, + const char *newpath) +{ + return renameat(olddirfd, oldpath, newdirfd, newpath); +} + +int +ocall_symlinkat(const char *target, int newdirfd, const char *linkpath) +{ + return symlinkat(target, newdirfd, linkpath); +} + +int +ocall_ioctl(int fd, unsigned long request, void *arg, unsigned int arg_len) +{ + /* support just int *arg temporally */ + return ioctl(fd, request, (int *)arg); +} + +int +ocall_fcntl(int fd, int cmd) +{ + return fcntl(fd, cmd); +} + +int +ocall_fcntl_long(int fd, int cmd, long arg) +{ + return fcntl(fd, cmd, arg); +} + +ssize_t +ocall_readv(int fd, char *iov_buf, unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset) +{ + struct iovec *iov = (struct iovec *)iov_buf; + ssize_t ret; + int i; + + for (i = 0; i < iovcnt; i++) { + iov[i].iov_base = iov_buf + (unsigned)(uintptr_t)iov[i].iov_base; + } + + if (has_offset) + ret = preadv(fd, iov, iovcnt, offset); + else + ret = readv(fd, iov, iovcnt); + + return ret; +} + +ssize_t +ocall_writev(int fd, char *iov_buf, unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset) +{ + struct iovec *iov = (struct iovec *)iov_buf; + int i; + ssize_t ret; + + for (i = 0; i < iovcnt; i++) { + iov[i].iov_base = iov_buf + (unsigned)(uintptr_t)iov[i].iov_base; + } + + if (has_offset) + ret = pwritev(fd, iov, iovcnt, offset); + else + ret = writev(fd, iov, iovcnt); + + return ret; +} + +int +ocall_realpath(const char *path, char *buf, unsigned int buf_len) +{ + char *val = NULL; + val = realpath(path, buf); + if (val != NULL) { + return 0; + } + return -1; +} + +int +ocall_posix_fallocate(int fd, off_t offset, off_t len) +{ + return posix_fallocate(fd, offset, len); +} + +int +ocall_poll(void *fds, unsigned nfds, int timeout, unsigned int fds_len) +{ + return poll((struct pollfd *)fds, (nfds_t)nfds, timeout); +} + +int +ocall_getopt(int argc, char *argv_buf, unsigned int argv_buf_len, + const char *optstring) +{ + int ret; + int i; + char **argv = (char **)argv_buf; + + for (i = 0; i < argc; i++) { + argv[i] = argv_buf + (uintptr_t)argv[i]; + } + + return getopt(argc, argv, optstring); +} + +int +ocall_sched_yield() +{ + return sched_yield(); +} + +int +ocall_get_errno() +{ + return errno; +} diff --git a/core/shared/platform/linux-sgx/untrusted/pthread.c b/core/shared/platform/linux-sgx/untrusted/pthread.c new file mode 100644 index 0000000000..890ef754c5 --- /dev/null +++ b/core/shared/platform/linux-sgx/untrusted/pthread.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +int +ocall_pthread_rwlock_init(void **rwlock, void *attr) +{ + int ret = 0; + + *rwlock = malloc(sizeof(pthread_rwlock_t)); + if (*rwlock == NULL) + return -1; + + ret = pthread_rwlock_init((pthread_rwlock_t *)*rwlock, NULL); + if (ret != 0) { + free(*rwlock); + *rwlock = NULL; + } + (void)attr; + return ret; +} + +int +ocall_pthread_rwlock_destroy(void *rwlock) +{ + pthread_rwlock_t *lock = (pthread_rwlock_t *)rwlock; + int ret; + + ret = pthread_rwlock_destroy(lock); + free(lock); + return ret; +} + +int +ocall_pthread_rwlock_rdlock(void *rwlock) +{ + return pthread_rwlock_rdlock((pthread_rwlock_t *)rwlock); +} + +int +ocall_pthread_rwlock_wrlock(void *rwlock) +{ + return pthread_rwlock_wrlock((pthread_rwlock_t *)rwlock); +} + +int +ocall_pthread_rwlock_unlock(void *rwlock) +{ + return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock); +} diff --git a/core/shared/platform/linux-sgx/untrusted/signal.c b/core/shared/platform/linux-sgx/untrusted/signal.c new file mode 100644 index 0000000000..b2eecfb7ad --- /dev/null +++ b/core/shared/platform/linux-sgx/untrusted/signal.c @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include + +int +ocall_raise(int sig) +{ + return raise(sig); +} \ No newline at end of file diff --git a/core/shared/platform/linux-sgx/untrusted/socket.c b/core/shared/platform/linux-sgx/untrusted/socket.c new file mode 100644 index 0000000000..6f598ab8ff --- /dev/null +++ b/core/shared/platform/linux-sgx/untrusted/socket.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#include +#include +#include + +int +ocall_socket(int domain, int type, int protocol) +{ + return socket(domain, type, protocol); +} + +int +ocall_getsockopt(int sockfd, int level, int optname, void *val_buf, + unsigned int val_buf_size, void *len_buf) +{ + return getsockopt(sockfd, level, optname, val_buf, (socklen_t *)len_buf); +} + +ssize_t +ocall_sendmsg(int sockfd, void *msg_buf, unsigned int msg_buf_size, int flags) +{ + struct msghdr *msg = (struct msghdr *)msg_buf; + int i; + ssize_t ret; + + if (msg->msg_name != NULL) + msg->msg_name = msg_buf + (unsigned)(uintptr_t)msg->msg_name; + + if (msg->msg_control != NULL) + msg->msg_control = msg_buf + (unsigned)(uintptr_t)msg->msg_control; + + if (msg->msg_iov != NULL) { + msg->msg_iov = msg_buf + (unsigned)(uintptr_t)msg->msg_iov; + for (i = 0; i < msg->msg_iovlen; i++) { + msg->msg_iov[i].iov_base = + msg_buf + (unsigned)(uintptr_t)msg->msg_iov[i].iov_base; + } + } + + return sendmsg(sockfd, msg, flags); +} + +ssize_t +ocall_recvmsg(int sockfd, void *msg_buf, unsigned int msg_buf_size, int flags) +{ + struct msghdr *msg = (struct msghdr *)msg_buf; + int i; + ssize_t ret; + + if (msg->msg_name != NULL) + msg->msg_name = msg_buf + (unsigned)(uintptr_t)msg->msg_name; + + if (msg->msg_control != NULL) + msg->msg_control = msg_buf + (unsigned)(uintptr_t)msg->msg_control; + + if (msg->msg_iov != NULL) { + msg->msg_iov = msg_buf + (unsigned)(uintptr_t)msg->msg_iov; + for (i = 0; i < msg->msg_iovlen; i++) { + msg->msg_iov[i].iov_base = + msg_buf + (unsigned)(uintptr_t)msg->msg_iov[i].iov_base; + } + } + + return recvmsg(sockfd, msg, flags); +} + +int +ocall_shutdown(int sockfd, int how) +{ + return shutdown(sockfd, how); +} + +int +ocall_setsockopt(int sockfd, int level, int optname, void *optval, + unsigned int optlen) +{ + return setsockopt(sockfd, level, optname, optval, optlen); +} + +int +ocall_bind(int sockfd, const void *addr, uint32_t addrlen) +{ + return bind(sockfd, (const struct sockaddr *)addr, addrlen); +} + +int +ocall_getsockname(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size) +{ + return getsockname(sockfd, (struct sockaddr *)addr, addrlen); +} + +int +ocall_getpeername(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size) +{ + return getpeername(sockfd, (struct sockaddr *)addr, addrlen); +} + +int +ocall_listen(int sockfd, int backlog) +{ + return listen(sockfd, backlog); +} + +int +ocall_accept(int sockfd, void *addr, uint32_t *addrlen, uint32_t addr_size) +{ + return accept(sockfd, (struct sockaddr *)addr, addrlen); +} + +int +ocall_recv(int sockfd, void *buf, size_t len, int flags) +{ + return recv(sockfd, buf, len, flags); +} + +ssize_t +ocall_recvfrom(int sockfd, void *buf, size_t len, int flags, void *src_addr, + uint32_t *addrlen, uint32_t addr_size) +{ + return recvfrom(sockfd, buf, len, flags, (struct sockaddr *)src_addr, + addrlen); +} + +int +ocall_send(int sockfd, const void *buf, size_t len, int flags) +{ + return send(sockfd, buf, len, flags); +} + +ssize_t +ocall_sendto(int sockfd, const void *buf, size_t len, int flags, + void *dest_addr, uint32_t addrlen) +{ + return sendto(sockfd, buf, len, flags, (struct sockaddr *)dest_addr, + addrlen); +} + +int +ocall_connect(int sockfd, void *addr, uint32_t addrlen) +{ + return connect(sockfd, (const struct sockaddr *)addr, addrlen); +} \ No newline at end of file diff --git a/core/shared/platform/linux-sgx/untrusted/time.c b/core/shared/platform/linux-sgx/untrusted/time.c new file mode 100644 index 0000000000..5fa387b0cd --- /dev/null +++ b/core/shared/platform/linux-sgx/untrusted/time.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#include + +/** time clock **/ +int +ocall_clock_gettime(unsigned clock_id, void *tp_buf, unsigned int tp_buf_size) +{ + return clock_gettime((clockid_t)clock_id, (struct timespec *)tp_buf); +} + +int +ocall_clock_getres(int clock_id, void *res_buf, unsigned int res_buf_size) +{ + return clock_getres(clock_id, (struct timespec *)res_buf); +} + +int +ocall_utimensat(int dirfd, const char *pathname, const void *times_buf, + unsigned int times_buf_size, int flags) +{ + return utimensat(dirfd, pathname, (struct timespec *)times_buf, flags); +} + +int +ocall_futimens(int fd, const void *times_buf, unsigned int times_buf_size) +{ + return futimens(fd, (struct timespec *)times_buf); +} + +int +ocall_clock_nanosleep(unsigned clock_id, int flags, const void *req_buf, + unsigned int req_buf_size, const void *rem_buf, + unsigned int rem_buf_size) +{ + return clock_nanosleep((clockid_t)clock_id, flags, + (struct timespec *)req_buf, + (struct timespec *)rem_buf); +} diff --git a/core/shared/platform/linux/bh_assert.c b/core/shared/platform/linux/bh_assert.c deleted file mode 100644 index c609fb93d4..0000000000 --- a/core/shared/platform/linux/bh_assert.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_assert.h" -#include -#include -#include - -#ifdef BH_TEST -#include -#endif - -#ifdef BH_TEST -/* for exception throwing */ -jmp_buf bh_test_jb; -#endif - -void bh_assert_internal(int v, const char *file_name, int line_number, - const char *expr_string) -{ - if (v) - return; - - if (!file_name) - file_name = "NULL FILENAME"; - if (!expr_string) - expr_string = "NULL EXPR_STRING"; - - printf("\nASSERTION FAILED: %s, at FILE=%s, LINE=%d\n", expr_string, - file_name, line_number); - -#ifdef BH_TEST - longjmp(bh_test_jb, 1); -#endif - - abort(); -} - -void bh_debug_internal(const char *file_name, int line_number, const char *fmt, - ...) -{ -#ifndef JEFF_TEST_VERIFIER - va_list args; - - va_start(args, fmt); - bh_assert(file_name); - - printf("\nDebug info FILE=%s, LINE=%d: ", file_name, line_number); - vprintf(fmt, args); - - va_end(args); - printf("\n"); -#endif -} - diff --git a/core/shared/platform/linux/bh_definition.c b/core/shared/platform/linux/bh_definition.c deleted file mode 100644 index 8fb58d2f84..0000000000 --- a/core/shared/platform/linux/bh_definition.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" - -#define RSIZE_MAX 0x7FFFFFFF - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n) -{ - char *dest = (char*) s1; - char *src = (char*) s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} - -int b_strcat_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s1) + strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcat(s1, s2); - - return 0; -} - -int b_strcpy_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcpy(s1, s2); - - return 0; -} - -int fopen_s(FILE ** pFile, const char *filename, const char *mode) -{ - if (NULL == pFile || NULL == filename || NULL == mode) { - return -1; - } - - *pFile = fopen(filename, mode); - - if (NULL == *pFile) - return -1; - - return 0; -} diff --git a/core/shared/platform/linux/bh_platform.c b/core/shared/platform/linux/bh_platform.c deleted file mode 100755 index 4eda405b07..0000000000 --- a/core/shared/platform/linux/bh_platform.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_common.h" -#include "bh_assert.h" - -#include -#include -#include -#include - -char *bh_strdup(const char *s) -{ - uint32 size; - char *s1 = NULL; - - if (s) { - size = (uint32)(strlen(s) + 1); - if ((s1 = bh_malloc(size))) - bh_memcpy_s(s1, size, s, size); - } - return s1; -} - -int bh_platform_init() -{ - return 0; -} - -char* -bh_read_file_to_buffer(const char *filename, uint32 *ret_size) -{ - char *buffer; - int file; - uint32 file_size, read_size; - struct stat stat_buf; - - if (!filename || !ret_size) { - printf("Read file to buffer failed: invalid filename or ret size.\n"); - return NULL; - } - - if ((file = open(filename, O_RDONLY, 0)) == -1) { - printf("Read file to buffer failed: open file %s failed.\n", - filename); - return NULL; - } - - if (fstat(file, &stat_buf) != 0) { - printf("Read file to buffer failed: fstat file %s failed.\n", - filename); - close(file); - return NULL; - } - - file_size = (uint32)stat_buf.st_size; - - if (!(buffer = bh_malloc(file_size))) { - printf("Read file to buffer failed: alloc memory failed.\n"); - close(file); - return NULL; - } - - read_size = (uint32)read(file, buffer, file_size); - close(file); - - if (read_size < file_size) { - printf("Read file to buffer failed: read file content failed.\n"); - bh_free(buffer); - return NULL; - } - - *ret_size = file_size; - return buffer; -} - -void * -bh_mmap(void *hint, uint32 size, int prot, int flags) -{ - int map_prot = PROT_NONE; - int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; - uint64 request_size, page_size; - uint8 *addr, *addr_aligned; - uint32 i; - - /* align to 2M if no less than 2M, else align to 4K */ - page_size = size < 2 * 1024 * 1024 ? 4096 : 2 * 1024 * 1024; - request_size = (size + page_size - 1) & ~(page_size - 1); - request_size += page_size; - - if (request_size >= UINT32_MAX) - return NULL; - - if (prot & MMAP_PROT_READ) - map_prot |= PROT_READ; - - if (prot & MMAP_PROT_WRITE) - map_prot |= PROT_WRITE; - - if (prot & MMAP_PROT_EXEC) - map_prot |= PROT_EXEC; - -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - if (flags & MMAP_MAP_32BIT) - map_flags |= MAP_32BIT; -#endif - - if (flags & MMAP_MAP_FIXED) - map_flags |= MAP_FIXED; - - /* try 5 times */ - for (i = 0; i < 5; i ++) { - addr = mmap(hint, size, map_prot, map_flags, -1, 0); - if (addr != MAP_FAILED) - break; - } - if (addr == MAP_FAILED) - return NULL; - - addr_aligned = (uint8*)(uintptr_t) - (((uint64)(uintptr_t)addr + page_size - 1) & ~(page_size - 1)); - - /* Unmap memory allocated before the aligned base address */ - if (addr != addr_aligned) { - uint32 prefix_size = (uint32)(addr_aligned - addr); - munmap(addr, prefix_size); - request_size -= prefix_size; - } - - /* Unmap memory allocated after the potentially unaligned end */ - if (size != request_size) { - uint32 suffix_size = (uint32)(request_size - size); - munmap(addr_aligned + size, suffix_size); - request_size -= size; - } - - if (size >= 2 * 1024 * 1024) { - /* Try to use huge page to improve performance */ - if (!madvise(addr, size, MADV_HUGEPAGE)) - /* make huge page become effective */ - memset(addr, 0, size); - } - - return addr_aligned; -} - -void -bh_munmap(void *addr, uint32 size) -{ - if (addr) - munmap(addr, size); -} - -int -bh_mprotect(void *addr, uint32 size, int prot) -{ - int map_prot = PROT_NONE; - - if (!addr) - return 0; - - if (prot & MMAP_PROT_READ) - map_prot |= PROT_READ; - - if (prot & MMAP_PROT_WRITE) - map_prot |= PROT_WRITE; - - if (prot & MMAP_PROT_EXEC) - map_prot |= PROT_EXEC; - - return mprotect(addr, size, map_prot); -} - diff --git a/core/shared/platform/linux/bh_platform.h b/core/shared/platform/linux/bh_platform.h deleted file mode 100644 index a2b040f768..0000000000 --- a/core/shared/platform/linux/bh_platform.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_PLATFORM_H -#define _BH_PLATFORM_H - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_memory.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef uint64_t uint64; -typedef int64_t int64; - -extern void DEBUGME(void); - -#define DIE do{bh_debug("Die here\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); DEBUGME(void); while(1);}while(0) - -#ifndef BH_PLATFORM_LINUX -#define BH_PLATFORM_LINUX -#endif - -/* NEED qsort */ - -#define _STACK_SIZE_ADJUSTMENT (32 * 1024) - -/* Stack size of applet threads's native part. */ -#define BH_APPLET_PRESERVED_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT) - -/* Default thread priority */ -#define BH_THREAD_DEFAULT_PRIORITY 0 - -#define BH_ROUTINE_MODIFIER - -#define BHT_TIMEDOUT ETIMEDOUT - -#define INVALID_THREAD_ID 0xFFffFFff - -typedef pthread_t korp_tid; -typedef pthread_mutex_t korp_mutex; -typedef sem_t korp_sem; -typedef pthread_cond_t korp_cond; -typedef pthread_t korp_thread; -typedef void* (*thread_start_routine_t)(void*); - -#define wa_malloc bh_malloc -#define wa_free bh_free -#define wa_strdup bh_strdup - -#define bh_printf printf - -int snprintf(char *buffer, size_t count, const char *format, ...); -double fmod(double x, double y); -float fmodf(float x, float y); -double sqrt(double x); - -#define BH_WAIT_FOREVER 0xFFFFFFFF - -#ifndef NULL -# define NULL ((void*) 0) -#endif - -/** - * Return the offset of the given field in the given type. - * - * @param Type the type containing the filed - * @param field the field in the type - * - * @return the offset of field in Type - */ -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) -#endif - -#define bh_assert assert - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, - unsigned int n); -int b_strcat_s(char * s1, size_t s1max, const char * s2); -int b_strcpy_s(char * s1, size_t s1max, const char * s2); - -int fopen_s(FILE ** pFile, const char *filename, const char *mode); - -char *bh_read_file_to_buffer(const char *filename, uint32 *ret_size); - -char *bh_strdup(const char *s); - -int bh_platform_init(); - -/* MMAP mode */ -enum { - MMAP_PROT_NONE = 0, - MMAP_PROT_READ = 1, - MMAP_PROT_WRITE = 2, - MMAP_PROT_EXEC = 4 -}; - -/* MMAP flags */ -enum { - MMAP_MAP_NONE = 0, - /* Put the mapping into 0 to 2 G, supported only on x86_64 */ - MMAP_MAP_32BIT = 1, - /* Don't interpret addr as a hint: place the mapping at exactly - that address. */ - MMAP_MAP_FIXED = 2 -}; - -void *bh_mmap(void *hint, unsigned int size, int prot, int flags); -void bh_munmap(void *addr, uint32 size); -int bh_mprotect(void *addr, uint32 size, int prot); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/shared/platform/linux/bh_platform_log.c b/core/shared/platform/linux/bh_platform_log.c deleted file mode 100644 index 902ca7d60b..0000000000 --- a/core/shared/platform/linux/bh_platform_log.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include - -void bh_log_emit(const char *fmt, va_list ap) -{ - vprintf(fmt, ap); - fflush(stdout); -} - -int bh_fprintf(FILE *stream, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = vfprintf(stream ? stream : stdout, fmt, ap); - va_end(ap); - - return ret; -} - -int bh_fflush(void *stream) -{ - return fflush(stream ? stream : stdout); -} diff --git a/core/shared/platform/linux/bh_thread.c b/core/shared/platform/linux/bh_thread.c deleted file mode 100755 index 8160fb0449..0000000000 --- a/core/shared/platform/linux/bh_thread.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_thread.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include -#include -#include - -static bool is_thread_sys_inited = false; - -static korp_mutex thread_list_lock; -static pthread_key_t thread_local_storage_key[BH_MAX_TLS_NUM]; - -int _vm_thread_sys_init() -{ - unsigned i, j; - int ret; - - if (is_thread_sys_inited) - return 0; - - for (i = 0; i < BH_MAX_TLS_NUM; i++) { - ret = pthread_key_create(&thread_local_storage_key[i], NULL); - if (ret) - goto fail; - } - - ret = vm_mutex_init(&thread_list_lock); - if (ret) - goto fail; - - is_thread_sys_inited = true; - return 0; - - fail: for (j = 0; j < i; j++) - pthread_key_delete(thread_local_storage_key[j]); - return -1; -} - -void vm_thread_sys_destroy(void) -{ - if (is_thread_sys_inited) { - unsigned i; - for (i = 0; i < BH_MAX_TLS_NUM; i++) - pthread_key_delete(thread_local_storage_key[i]); - vm_mutex_destroy(&thread_list_lock); - is_thread_sys_inited = false; - } -} - -typedef struct { - thread_start_routine_t start; - void* stack; - uint32 stack_size; - void* arg; -} thread_wrapper_arg; - -static void *vm_thread_wrapper(void *arg) -{ - thread_wrapper_arg * targ = arg; - LOG_VERBOSE("THREAD CREATE 0x%08x\n", &targ); - targ->stack = (void *)((uintptr_t)(&arg) & (uintptr_t)~0xfff); - _vm_tls_put(1, targ); - targ->start(targ->arg); - bh_free(targ); - _vm_tls_put(1, NULL); - return NULL; -} - -int _vm_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio) -{ - pthread_attr_t tattr; - thread_wrapper_arg *targ; - - bh_assert(stack_size > 0); - bh_assert(tid); - bh_assert(start); - - *tid = INVALID_THREAD_ID; - - pthread_attr_init(&tattr); - pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); - if (pthread_attr_setstacksize(&tattr, stack_size) != 0) { - bh_debug("Invalid thread stack size %u. Min stack size on Linux = %u", - stack_size, PTHREAD_STACK_MIN); - pthread_attr_destroy(&tattr); - return BHT_ERROR; - } - - targ = (thread_wrapper_arg*) bh_malloc(sizeof(*targ)); - if (!targ) { - pthread_attr_destroy(&tattr); - return BHT_ERROR; - } - - targ->start = start; - targ->arg = arg; - targ->stack_size = stack_size; - - if (pthread_create(tid, &tattr, vm_thread_wrapper, targ) != 0) { - pthread_attr_destroy(&tattr); - bh_free(targ); - return BHT_ERROR; - } - - pthread_attr_destroy(&tattr); - return BHT_OK; -} - -int _vm_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, - unsigned int stack_size) -{ - return _vm_thread_create_with_prio(tid, start, arg, stack_size, - BH_THREAD_DEFAULT_PRIORITY); -} - -korp_tid _vm_self_thread() -{ - return (korp_tid) pthread_self(); -} - -void vm_thread_exit(void * code) -{ - bh_free(_vm_tls_get(1)); - _vm_tls_put(1, NULL); - pthread_exit(code); -} - -void *_vm_tls_get(unsigned idx) -{ - bh_assert(idx < BH_MAX_TLS_NUM); - return pthread_getspecific(thread_local_storage_key[idx]); -} - -int _vm_tls_put(unsigned idx, void * tls) -{ - bh_assert(idx < BH_MAX_TLS_NUM); - pthread_setspecific(thread_local_storage_key[idx], tls); - return BHT_OK; -} - -int _vm_mutex_init(korp_mutex *mutex) -{ - return pthread_mutex_init(mutex, NULL) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_recursive_mutex_init(korp_mutex *mutex) -{ - int ret; - - pthread_mutexattr_t mattr; - - bh_assert(mutex); - ret = pthread_mutexattr_init(&mattr); - if (ret) - return BHT_ERROR; - - pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE_NP); - ret = pthread_mutex_init(mutex, &mattr); - pthread_mutexattr_destroy(&mattr); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_mutex_destroy(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_destroy(mutex); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/* Returned error (EINVAL, EAGAIN and EDEADLK) from - locking the mutex indicates some logic error present in - the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_lock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_lock(mutex); - if (0 != ret) { - printf("vm mutex lock failed (ret=%d)!\n", ret); - exit(-1); - } -} - -int vm_mutex_trylock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_trylock(mutex); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/* Returned error (EINVAL, EAGAIN and EPERM) from - unlocking the mutex indicates some logic error present - in the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_unlock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_unlock(mutex); - if (0 != ret) { - printf("vm mutex unlock failed (ret=%d)!\n", ret); - exit(-1); - } -} - -int _vm_sem_init(korp_sem* sem, unsigned int c) -{ - int ret; - - bh_assert(sem); - ret = sem_init(sem, 0, c); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_destroy(korp_sem *sem) -{ - int ret; - - bh_assert(sem); - ret = sem_destroy(sem); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_wait(korp_sem *sem) -{ - int ret; - - bh_assert(sem); - - ret = sem_wait(sem); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_reltimedwait(korp_sem *sem, int mills) -{ - int ret = BHT_OK; - - struct timespec timeout; - const int mills_per_sec = 1000; - const int mills_to_nsec = 1E6; - - bh_assert(sem); - - if (mills == (int)BHT_WAIT_FOREVER) { - ret = sem_wait(sem); - } else { - - timeout.tv_sec = mills / mills_per_sec; - timeout.tv_nsec = (mills % mills_per_sec) * mills_to_nsec; - timeout.tv_sec += time(NULL); - - ret = sem_timedwait(sem, &timeout); - } - - if (ret != BHT_OK) { - if (errno == BHT_TIMEDOUT) { - ret = BHT_TIMEDOUT; - errno = 0; - } else { - bh_debug("Faliure happens when timed wait is called"); - bh_assert(0); - } - } - - return ret; -} - -int _vm_sem_post(korp_sem *sem) -{ - bh_assert(sem); - - return sem_post(sem) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_cond_init(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_init(cond, NULL) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_destroy(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_destroy(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex) -{ - bh_assert(cond); - bh_assert(mutex); - - if (pthread_cond_wait(cond, mutex) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -static void msec_nsec_to_abstime(struct timespec *ts, int64 msec, int32 nsec) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - ts->tv_sec = (long int)(tv.tv_sec + msec / 1000); - ts->tv_nsec = (long int)(tv.tv_usec * 1000 + (msec % 1000) * 1000000 + nsec); - - if (ts->tv_nsec >= 1000000000L) { - ts->tv_sec++; - ts->tv_nsec -= 1000000000L; - } -} - -int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) -{ - int ret; - struct timespec abstime; - - if (mills == (int)BHT_WAIT_FOREVER) - ret = pthread_cond_wait(cond, mutex); - else { - msec_nsec_to_abstime(&abstime, mills, 0); - ret = pthread_cond_timedwait(cond, mutex, &abstime); - } - - if (ret != BHT_OK && ret != BHT_TIMEDOUT) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_signal(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_signal(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_broadcast(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_broadcast(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_thread_cancel(korp_tid thread) -{ - return pthread_cancel(thread); -} - -int _vm_thread_join(korp_tid thread, void **value_ptr, int mills) -{ - return pthread_join(thread, value_ptr); -} - -int _vm_thread_detach(korp_tid thread) -{ - return pthread_detach(thread); -} - diff --git a/core/shared/platform/linux/bh_time.c b/core/shared/platform/linux/bh_time.c deleted file mode 100755 index 698cdec24f..0000000000 --- a/core/shared/platform/linux/bh_time.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_time.h" - -#include -#include -#include -#include - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -uint64 _bh_time_get_tick_millisecond() -{ - return (uint64)sysconf(_SC_CLK_TCK); -} - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -uint64 _bh_time_get_boot_millisecond() -{ - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - - return ((uint64) ts.tv_sec) * 1000 + ((uint64)ts.tv_nsec) / (1000 * 1000); -} - -uint32 bh_get_tick_sec() -{ - return (uint32)(_bh_time_get_boot_millisecond() / 1000); -} - -/* - * This function returns GMT time milliseconds since from 1970.1.1, AKA UNIX time. - * @return milliseconds since from 1970.1.1. - */ -uint64 _bh_time_get_millisecond_from_1970() -{ - struct timeb tp; - ftime(&tp); - - return ((uint64) tp.time) * 1000 + tp.millitm - - (tp.dstflag == 0 ? 0 : 60 * 60 * 1000) - + ((uint64)tp.timezone) * 60 * 1000; -} - -size_t _bh_time_strftime(char *s, size_t max, const char *format, int64 time) -{ - time_t time_sec = (time_t)(time / 1000); - struct timeb tp; - struct tm *ltp; - - ftime(&tp); - time_sec -= tp.timezone * 60; - - ltp = localtime(&time_sec); - if (ltp == NULL) { - return 0; - } - return strftime(s, max, format, ltp); -} - diff --git a/core/shared/platform/linux/platform_init.c b/core/shared/platform/linux/platform_init.c new file mode 100644 index 0000000000..2aae13fa14 --- /dev/null +++ b/core/shared/platform/linux/platform_init.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} diff --git a/core/shared/platform/linux/platform_internal.h b/core/shared/platform/linux/platform_internal.h new file mode 100644 index 0000000000..b17abd2e48 --- /dev/null +++ b/core/shared/platform/linux/platform_internal.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_LINUX +#define BH_PLATFORM_LINUX +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define os_thread_local_attribute __thread + +#define bh_socket_t int + +#if WASM_DISABLE_WRITE_GS_BASE == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#define os_writegsbase(base_addr) \ + do { \ + uint64 __gs_value = (uint64)(uintptr_t)base_addr; \ + asm volatile("wrgsbase %0" ::"r"(__gs_value) : "memory"); \ + } while (0) +#if 0 +/* _writegsbase_u64 also works, but need to add -mfsgsbase flag for gcc */ +#include +#define os_writegsbase(base_addr) \ + _writegsbase_u64(((uint64)(uintptr_t)base_addr)) +#endif +#endif +#endif + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) || defined(BUILD_TARGET_RISCV64_LP64D) \ + || defined(BUILD_TARGET_RISCV64_LP64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +typedef void (*os_signal_handler)(void *sig_addr); + +int +os_thread_signal_init(os_signal_handler handler); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +void +os_signal_unmask(); + +void +os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64/RISCV64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#define os_getpagesize getpagesize + +#if WASM_DISABLE_WAKEUP_BLOCKING_OP == 0 +#define OS_ENABLE_WAKEUP_BLOCKING_OP +#endif +void +os_set_signal_number_for_blocking_op(int signo); + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; + +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef struct timespec os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/linux/shared_platform.cmake b/core/shared/platform/linux/shared_platform.cmake index a49c5c6e1d..9a87260160 100644 --- a/core/shared/platform/linux/shared_platform.cmake +++ b/core/shared/platform/linux/shared_platform.cmake @@ -3,15 +3,16 @@ set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) +add_definitions(-DBH_PLATFORM_LINUX) + include_directories(${PLATFORM_SHARED_DIR}) include_directories(${PLATFORM_SHARED_DIR}/../include) +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) -set (PLATFORM_SHARED_SOURCE ${source_all}) - -LIST (APPEND RUNTIME_LIB_HEADER_LIST "${PLATFORM_SHARED_DIR}/bh_platform.h") +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) -LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) \ No newline at end of file +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/nuttx/nuttx_platform.c b/core/shared/platform/nuttx/nuttx_platform.c new file mode 100644 index 0000000000..da5bf86736 --- /dev/null +++ b/core/shared/platform/nuttx/nuttx_platform.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2020 XiaoMi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "platform_api_vmcore.h" + +#if defined(CONFIG_ARCH_USE_TEXT_HEAP) +#include +#endif + +#include + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + void *p; + +#if defined(CONFIG_ARCH_USE_TEXT_HEAP) + if ((prot & MMAP_PROT_EXEC) != 0) { + p = up_textheap_memalign(sizeof(void *), size); + if (p) { +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + void *dp = os_get_dbus_mirror(p); + memset(dp, 0, size); + os_dcache_flush(); +#else + memset(p, 0, size); +#endif + } + return p; + } +#endif + + if ((uint64)size >= UINT32_MAX) + return NULL; + + /* Note: aot_loader.c assumes that os_mmap provides large enough + * alignment for any data sections. Some sections like rodata.cst32 + * actually require alignment larger than the natural alignment + * provided by malloc. + * + * Probably it's cleaner to add an explicit alignment argument to + * os_mmap. However, it only makes sense if we change our aot format + * to keep the necessary alignment. + * + * For now, let's assume 32 byte alignment is enough. + */ + if (posix_memalign(&p, 32, size)) { + return NULL; + } + + /* Zero the memory which is required by os_mmap */ + memset(p, 0, size); + + return p; +} + +void +os_munmap(void *addr, size_t size) +{ +#if defined(CONFIG_ARCH_USE_TEXT_HEAP) + if (up_textheap_heapmember(addr)) { + up_textheap_free(addr); + return; + } +#endif + + free(addr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush() +{ +#if defined(CONFIG_ARCH_USE_TEXT_HEAP) \ + && defined(CONFIG_ARCH_HAVE_TEXT_HEAP_SEPARATE_DATA_ADDRESS) + up_textheap_data_sync(); +#endif +#ifndef CONFIG_BUILD_KERNEL + up_flush_dcache_all(); +#endif +} + +void +os_icache_flush(void *start, size_t len) +{ +#ifndef CONFIG_BUILD_KERNEL + up_invalidate_icache((uintptr_t)start, (uintptr_t)start + len); +#endif +} + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) +void * +os_get_dbus_mirror(void *ibus) +{ +#if defined(CONFIG_ARCH_USE_TEXT_HEAP) \ + && defined(CONFIG_ARCH_HAVE_TEXT_HEAP_SEPARATE_DATA_ADDRESS) + return up_textheap_data_address(ibus); +#else + return ibus; +#endif +} +#endif + +/* If AT_FDCWD is provided, maybe we have openat family */ +#if !defined(AT_FDCWD) + +int +openat(int fd, const char *path, int oflags, ...) +{ + errno = ENOSYS; + return -1; +} + +int +fstatat(int fd, const char *path, struct stat *buf, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +mkdirat(int fd, const char *path, mode_t mode) +{ + errno = ENOSYS; + return -1; +} + +ssize_t +readlinkat(int fd, const char *path, char *buf, size_t bufsize) +{ + errno = ENOSYS; + return -1; +} + +int +linkat(int fd1, const char *path1, int fd2, const char *path2, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +renameat(int fromfd, const char *from, int tofd, const char *to) +{ + errno = ENOSYS; + return -1; +} +int +symlinkat(const char *target, int fd, const char *path) +{ + errno = ENOSYS; + return -1; +} +int +unlinkat(int fd, const char *path, int flag) +{ + errno = ENOSYS; + return -1; +} +int +utimensat(int fd, const char *path, const struct timespec ts[2], int flag) +{ + errno = ENOSYS; + return -1; +} + +#endif /* !defined(AT_FDCWD) */ + +#ifndef CONFIG_NET + +#include + +int +accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) +{ + 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 new file mode 100644 index 0000000000..d746dda004 --- /dev/null +++ b/core/shared/platform/nuttx/platform_internal.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2020 XiaoMi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_NUTTX +#define BH_PLATFORM_NUTTX +#endif + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define os_getpagesize getpagesize + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 100 + +#define os_printf printf +#define os_vprintf vprintf + +#if defined(CONFIG_LIBC_DLFCN) +#define BH_HAS_DLFCN 1 +#else +#define BH_HAS_DLFCN 0 +#endif + +/* On NuttX, time_t is uint32_t */ +#define BH_TIME_T_MAX 0xffffffff + +/* + * NuttX doesn't have O_DIRECTORY or directory open. + * REVISIT: maybe this is safer to be disabled at higher level. + */ +#if !defined(O_DIRECTORY) +#define O_DIRECTORY 0 +#endif + +#if !defined(O_NOFOLLOW) +#define O_NOFOLLOW 0 +#endif + +#undef CONFIG_HAS_ISATTY +#ifdef CONFIG_SERIAL_TERMIOS +#define CONFIG_HAS_ISATTY 1 +#else +#define CONFIG_HAS_ISATTY 0 +#endif + +#define BUILTIN_LIBC_BUFFERED_PRINTF 1 +#define BUILTIN_LIBC_BUFFERED_PRINT_SIZE 128 +#define BUILTIN_LIBC_BUFFERED_PRINT_PREFIX + +/* + * NuttX doesn't have openat family. + */ + +/* If AT_FDCWD is provided, maybe we have openat family */ +#if !defined(AT_FDCWD) + +int +openat(int fd, const char *path, int oflags, ...); +int +fstatat(int fd, const char *path, struct stat *buf, int flag); +int +mkdirat(int fd, const char *path, mode_t mode); +ssize_t +readlinkat(int fd, const char *path, char *buf, size_t bufsize); +int +linkat(int fd1, const char *path1, int fd2, const char *path2, int flag); +int +renameat(int fromfd, const char *from, int tofd, const char *to); +int +symlinkat(const char *target, int fd, const char *path); +int +unlinkat(int fd, const char *path, int flag); +int +utimensat(int fd, const char *path, const struct timespec ts[2], int flag); +#define AT_SYMLINK_NOFOLLOW 0 +#define AT_SYMLINK_FOLLOW 0 +#define AT_REMOVEDIR 0 + +#endif /* !defined(AT_FDCWD) */ + +/* + * NuttX doesn't have fdopendir. + */ + +DIR * +fdopendir(int fd); + +#if WASM_DISABLE_WAKEUP_BLOCKING_OP == 0 +#define OS_ENABLE_WAKEUP_BLOCKING_OP +#endif +void +os_set_signal_number_for_blocking_op(int signo); + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef struct timespec os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _BH_PLATFORM_H */ diff --git a/core/shared/platform/nuttx/shared_platform.cmake b/core/shared/platform/nuttx/shared_platform.cmake new file mode 100644 index 0000000000..d691068f2f --- /dev/null +++ b/core/shared/platform/nuttx/shared_platform.cmake @@ -0,0 +1,26 @@ +# Copyright (C) 2020 XiaoMi 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_NUTTX) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +if (WAMR_BUILD_LIBC_WASI EQUAL 1) + list(APPEND source_all ${PLATFORM_SHARED_DIR}/../common/posix/posix_file.c) + include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) + set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) +endif () + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE} ${PLATFORM_COMMON_POSIX_SOURCE} ${UNCOMMON_SHARED_SOURCE}) +# remove posix_memmap.c for NuttX +list(REMOVE_ITEM ${PLATFORM_SHARED_SOURCE} ${PLATFORM_COMMON_POSIX_DIR}/posix_memmap.c) + diff --git a/core/shared/platform/riot/platform_internal.h b/core/shared/platform/riot/platform_internal.h new file mode 100644 index 0000000000..6eaae2caf0 --- /dev/null +++ b/core/shared/platform/riot/platform_internal.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +/* Riot includes core */ +#include +#include +#include + +/* Riot includes sys */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BH_PLATFORM_RIOT +#define BH_PLATFORM_RIOT +#endif + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 7 + +typedef thread_t korp_thread; +typedef kernel_pid_t korp_tid; +typedef mutex_t korp_mutex; +typedef unsigned int korp_sem; + +/* korp_rwlock is used in platform_api_extension.h, + we just define the type to make the compiler happy */ +typedef struct { + int dummy; +} korp_rwlock; + +/* typedef sema_t korp_sem; */ + +struct os_thread_wait_node; +typedef struct os_thread_wait_node *os_thread_wait_list; +typedef struct korp_cond { + mutex_t wait_list_lock; + os_thread_wait_list thread_wait_list; +} korp_cond; + +#define os_printf printf +#define os_vprintf vprintf + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_file_handle; +typedef void *os_dir_stream; +typedef int os_raw_file_handle; +typedef int os_poll_file_handle; +typedef unsigned int os_nfds_t; +typedef int os_timespec; + +#if WA_MATH +/* clang-format off */ +/* math functions which are not provided by os*/ +double sqrt(double x); +double floor(double x); +double ceil(double x); +double fmin(double x, double y); +double fmax(double x, double y); +double rint(double x); +double fabs(double x); +double trunc(double x); +float sqrtf(float x); +float floorf(float x); +float ceilf(float x); +float fminf(float x, float y); +float fmaxf(float x, float y); +float rintf(float x); +float fabsf(float x); +float truncf(float x); +int isnan_double(double x); +int isnan_float(float x); +int signbit_double(double x); +int signbit_float(float x); +#define isnan(x) (sizeof(x) == sizeof(double) ? isnan_double((double)x) : isnan_float(x)) +#define signbit(x) (sizeof(x) == sizeof(double) ? signbit_double((double)x) : signbit_float(x)) +/* clang-format on */ +#endif + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +/* There is no MMU in RIOT so the function return 1024 to make the compiler + happy */ +static inline int +os_getpagesize() +{ + return 1024; +} + +#endif /* end of _BH_PLATFORM_H */ diff --git a/core/shared/platform/riot/riot_platform.c b/core/shared/platform/riot/riot_platform.c new file mode 100644 index 0000000000..b48033247a --- /dev/null +++ b/core/shared/platform/riot/riot_platform.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +int +os_thread_sys_init(void); + +void +os_thread_sys_destroy(void); + +int +bh_platform_init(void) +{ + return os_thread_sys_init(); +} + +void +bh_platform_destroy(void) +{ + os_thread_sys_destroy(); +} + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + void *addr; + + if (size >= UINT32_MAX) + return NULL; + + if ((addr = BH_MALLOC((uint32)size))) + memset(addr, 0, (uint32)size); + + return addr; +} + +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size) +{ + return os_mremap_slow(old_addr, old_size, new_size); +} + +void +os_munmap(void *addr, size_t size) +{ + return BH_FREE(addr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush(void) +{ +#if defined(CONFIG_CPU_CORTEX_M7) && defined(CONFIG_ARM_MPU) + uint32 key; + key = irq_lock(); + SCB_CleanDCache(); + irq_unlock(key); +#endif +} + +void +os_icache_flush(void *start, size_t len) +{} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return -1; +} diff --git a/core/shared/platform/riot/riot_thread.c b/core/shared/platform/riot/riot_thread.c new file mode 100644 index 0000000000..893ed0b456 --- /dev/null +++ b/core/shared/platform/riot/riot_thread.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#include +#include +#include + +/* clang-format off */ +#define bh_assert(v) do { \ + if (!(v)) { \ + printf("\nASSERTION FAILED: %s, at %s, line %d\n", \ + #v, __FILE__, __LINE__); \ + core_panic(0, 0/*expr_string*/); \ + while (1); \ + } \ +} while (0) +/* clang-format on */ + +struct os_thread_data; +typedef struct os_thread_wait_node { + sema_t sem; + void *ret; + os_thread_wait_list next; +} os_thread_wait_node; + +// all information for thread to cleanup it self +typedef struct os_thread_data { + /* Next thread data */ + struct os_thread_data *next; + /* thread handle */ + kernel_pid_t tid; + /* Thread start routine */ + thread_start_routine_t start_routine; + /* Thread start routine argument */ + void *arg; + /* thread local root */ + void *tlr; + /* Lock for waiting list */ + mutex_t wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; + /* Thread stack size */ + unsigned stack_size; + /* Thread stack */ + char stack[1]; +} os_thread_data; + +typedef struct os_thread_obj { + korp_tid thread; + /* Whether the thread is terminated and this thread object is to + be freed in the future. */ + bool to_be_freed; + struct os_thread_obj *next; +} os_thread_obj; + +static bool is_thread_sys_inited = false; + +/* Lock for thread data list */ +static mutex_t thread_data_lock; + +/* Thread data list */ +static os_thread_data *thread_data_list = NULL; + +static void +thread_data_list_add(os_thread_data *thread_data) +{ + mutex_lock(&thread_data_lock); + if (!thread_data_list) + thread_data_list = thread_data; + else { + /* If already in list, just return */ + os_thread_data *p = thread_data_list; + while (p) { + if (p == thread_data) { + mutex_unlock(&thread_data_lock); + return; + } + p = p->next; + } + + /* Set as head of list */ + thread_data->next = thread_data_list; + thread_data_list = thread_data; + } + mutex_unlock(&thread_data_lock); +} + +static void +thread_data_list_remove(os_thread_data *thread_data) +{ + mutex_lock(&thread_data_lock); + if (thread_data_list) { + if (thread_data_list == thread_data) + thread_data_list = thread_data_list->next; + else { + /* Search and remove it from list */ + os_thread_data *p = thread_data_list; + while (p && p->next != thread_data) + p = p->next; + if (p && p->next == thread_data) + p->next = p->next->next; + } + } + mutex_unlock(&thread_data_lock); +} + +static os_thread_data * +thread_data_list_lookup(korp_tid tid) +{ + mutex_lock(&thread_data_lock); + if (thread_data_list) { + os_thread_data *p = thread_data_list; + while (p) { + if (p->tid == tid) { + /* Found */ + mutex_unlock(&thread_data_lock); + return p; + } + p = p->next; + } + } + mutex_unlock(&thread_data_lock); + return NULL; +} + +int +os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + + mutex_init(&thread_data_lock); + + is_thread_sys_inited = true; + return BHT_OK; +} + +void +os_thread_sys_destroy() +{ + if (is_thread_sys_inited) { + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + kernel_pid_t tid = thread_getpid(); + return thread_data_list_lookup(tid); +} + +static void +os_thread_cleanup(void) +{ + // TODO Check this (Join sema trigger, cleanup of thread_data) + os_thread_data *thread_data = thread_data_current(); + bh_assert(thread_data != NULL); + mutex_lock(&thread_data->wait_list_lock); + if (thread_data->thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_data->thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + head->ret = thread_data->arg; + sema_post(&head->sem); + head = next; + } + thread_data->thread_wait_list = NULL; + } + mutex_unlock(&thread_data->wait_list_lock); + + thread_data_list_remove(thread_data); +} + +static void * +os_thread_wrapper(void *thread_data) +{ + /* Set thread custom data */ + os_thread_data *t = (os_thread_data *)thread_data; + t->tid = thread_getpid(); + thread_data_list_add(t); + + // save the return value to arg since it is not need after the call + t->arg = (t->start_routine)(t->arg); + + os_thread_cleanup(); // internal structures and joiners + + BH_FREE(thread_data); + sched_task_exit(); // stop thread //clean + return NULL; // never reached +} + +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + kernel_pid_t tid; + os_thread_data *thread_data; + unsigned thread_data_size; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Create and initialize thread data */ + thread_data_size = offsetof(os_thread_data, stack) + stack_size; + if (!(thread_data = BH_MALLOC(thread_data_size))) { + return BHT_ERROR; + } + + memset(thread_data, 0, thread_data_size); + mutex_init(&thread_data->wait_list_lock); + thread_data->stack_size = stack_size; + thread_data->start_routine = start; + thread_data->arg = arg; + + /* Create the thread &*/ + if (!((tid = thread_create(thread_data->stack, stack_size, prio, 0, + os_thread_wrapper, thread_data, "WASM")))) { + BH_FREE(thread_data); + return BHT_ERROR; + } + + thread_data->tid = tid; + + /* Set thread custom data */ + thread_data_list_add(thread_data); + *p_tid = tid; + return BHT_OK; +} + +korp_tid +os_self_thread() +{ + return (korp_tid)thread_getpid(); +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ + // will test if thread is still working, + // wait if it is + os_thread_data *thread_data; + os_thread_wait_node node; + + sema_create(&node.sem, 0); + node.next = NULL; + + /* Get thread data */ + thread_data = thread_data_list_lookup(thread); + if (thread_data == NULL) { + // thread not found + sema_destroy(&node.sem); + return BHT_ERROR; + } + bh_assert(thread_data != NULL); + + mutex_lock(&thread_data->wait_list_lock); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = &node; + else { + /* Add to end of waiting list */ + os_thread_wait_node *p = thread_data->thread_wait_list; + while (p->next) + p = p->next; + p->next = &node; + } + mutex_unlock(&thread_data->wait_list_lock); + + sema_wait(&node.sem); + // get the return value pointer conted may not be available after return + if (value_ptr) + (*value_ptr) = node.ret; + /* Wait some time for the thread to be actually terminated */ + // TODO: k_sleep(100); + + // TODO: bump target prio to make it finish and free its resources + thread_yield(); + + // node has done its job + sema_destroy(&node.sem); + + return BHT_OK; +} + +// int vm_mutex_trylock(korp_mutex *mutex) +// { +// return mutex_trylock(mutex); +// } + +int +os_mutex_init(korp_mutex *mutex) +{ + mutex_init(mutex); + return BHT_OK; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + (void)mutex; + return BHT_OK; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + mutex_lock(mutex); + return 0; // Riot mutexes do not return until success +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + mutex_unlock(mutex); + return 0; // Riot mutexes do not return until success +} + +int +os_cond_init(korp_cond *cond) +{ + mutex_init(&cond->wait_list_lock); + cond->thread_wait_list = NULL; + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + (void)cond; + return BHT_OK; +} + +static int +os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, + uint64 useconds) +{ + os_thread_wait_node *node; + + /* Create wait node and append it to wait list */ + if (!(node = BH_MALLOC(sizeof(os_thread_wait_node)))) + return BHT_ERROR; + + sema_create(&node->sem, 0); + node->next = NULL; + + mutex_lock(&cond->wait_list_lock); + if (!cond->thread_wait_list) + cond->thread_wait_list = node; + else { + /* Add to end of wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + mutex_unlock(&cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + mutex_unlock(mutex); + if (timed) + sema_wait(&node->sem); + else + sema_wait_timed_ztimer(&node->sem, ZTIMER_USEC, useconds); + mutex_lock(mutex); + + /* Remove wait node from wait list */ + mutex_lock(&cond->wait_list_lock); + if (cond->thread_wait_list == node) + cond->thread_wait_list = node->next; + 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; + } + BH_FREE(node); + mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + return os_cond_wait_internal(cond, mutex, (useconds != BHT_WAIT_FOREVER), + useconds); +} + +int +os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + mutex_lock(&cond->wait_list_lock); + if (cond->thread_wait_list) + sema_post(&cond->thread_wait_list->sem); + mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +uint8 * +os_thread_get_stack_boundary() +{ +#if defined(DEVELHELP) || defined(SCHED_TEST_STACK) \ + || defined(MODULE_MPU_STACK_GUARD) + return (uint8 *)thread_get_active()->stack_start; +#else + return NULL; +#endif +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} \ No newline at end of file diff --git a/core/shared/platform/riot/riot_time.c b/core/shared/platform/riot/riot_time.c new file mode 100644 index 0000000000..ce73777c92 --- /dev/null +++ b/core/shared/platform/riot/riot_time.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include +#include + +#if IS_USED(MODULE_ZTIMER64_USEC) +uint64 +os_time_get_boot_us() +{ + return ztimer64_now(ZTIMER64_USEC); +} +#elif IS_USED(MODULE_ZTIMER64_MSEC) +uint64 +os_time_get_boot_us() +{ + return ztimer64_now(ZTIMER64_MSEC) * 1000; +} +#else +#ifdef __GNUC__ +__attribute__((weak)) uint64 +os_time_get_boot_us(); +#endif +uint64 +os_time_get_boot_us() +{ + static uint64_t times; + return ++times; +} +#endif + +uint64 +os_time_thread_cputime_us(void) +{ + /* FIXME if u know the right api */ + return os_time_get_boot_us(); +} diff --git a/core/shared/platform/riot/shared_platform.cmake b/core/shared/platform/riot/shared_platform.cmake new file mode 100644 index 0000000000..52cf90463b --- /dev/null +++ b/core/shared/platform/riot/shared_platform.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_RIOT) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +# include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) + diff --git a/core/shared/platform/rt-thread/SConscript b/core/shared/platform/rt-thread/SConscript new file mode 100644 index 0000000000..1e93f47550 --- /dev/null +++ b/core/shared/platform/rt-thread/SConscript @@ -0,0 +1,34 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + + +from building import * +import os + +cwd = GetCurrentDir() + +src = Split(''' +''') + + +def addSrcFiles(arr, path): + for f in os.listdir(path): + fpath = os.path.join(path, f); + if os.path.isfile(fpath): + ext = os.path.splitext(fpath)[-1] + if ext == '.c' or ext == '.cpp': + arr += [fpath] + elif os.path.isdir(fpath): + addSrcFiles(arr, fpath) + + + +addSrcFiles(src, cwd); +CPPPATH = [cwd, cwd+'/../include'] + +group = DefineGroup('iwasm_platform_core', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/shared/platform/rt-thread/platform_internal.h b/core/shared/platform/rt-thread/platform_internal.h new file mode 100644 index 0000000000..b8e64b6c75 --- /dev/null +++ b/core/shared/platform/rt-thread/platform_internal.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef RTTHREAD_PLATFORM_INTERNAL_H +#define RTTHREAD_PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#if defined(RT_USING_PTHREADS) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(WASM_ENABLE_AOT) +#if defined(RTT_WAMR_BUILD_TARGET_THUMB) +#define BUILD_TARGET "thumbv4t" +#elif defined(RTT_WAMR_BUILD_TARGET_ARMV7) +#define BUILD_TARGET "armv7" +#elif defined(RTT_WAMR_BUILD_TARGET_ARMV6) +#define BUILD_TARGET "armv6" +#elif defined(RTT_WAMR_BUILD_TARGET_ARMV4) +#define BUILD_TARGET "armv4" +#elif defined(RTT_WAMR_BUILD_TARGET_X86_32) +#define BUILD_TARGET "X86_32" +#else +#error "unsupported aot platform." +#endif +#endif /* WASM_ENABLE_AOT */ + +/* Use rt-thread's definition as default */ +#if 0 // defined(RT_USING_PTHREADS) +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +#else +typedef rt_thread_t korp_tid; +typedef struct rt_mutex korp_mutex; +typedef struct rt_thread korp_cond; +typedef struct rt_thread korp_thread; +#endif +typedef unsigned int korp_sem; + +#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED) +typedef uint32_t socklen_t; +#endif + +#if !defined(SOL_SOCKET) +#define SOL_SOCKET 1 +#endif + +#if !defined(SO_TYPE) +#define SO_TYPE 3 +#endif + +#if !defined(SOCK_DGRAM) +#define SOCK_DGRAM 2 +#endif + +#if !defined(SOCK_STREAM) +#define SOCK_STREAM 1 +#endif + +#if !defined(UTIME_NOW) +#define UTIME_NOW -2L +#endif + +#if !defined(UTIME_OMIT) +#define UTIME_OMIT -1L +#endif + +#if !defined(AT_SYMLINK_NOFOLLOW) +#define AT_SYMLINK_NOFOLLOW 2 +#endif + +#if !defined(AT_SYMLINK_FOLLOW) +#define AT_SYMLINK_FOLLOW 4 +#endif + +#if !defined(AT_REMOVEDIR) +#define AT_REMOVEDIR 8 +#endif + +#define DT_BLK 0x06 +#define DT_CHR 0x02 +#define DT_LNK 0x0A + +#define PTHREAD_STACK_MIN 1024 +#define BH_THREAD_DEFAULT_PRIORITY 30 + +/* korp_rwlock is used in platform_api_extension.h, + we just define the type to make the compiler happy */ +typedef struct { + int dummy; +} korp_rwlock; + +typedef rt_uint8_t uint8_t; +typedef rt_int8_t int8_t; +typedef rt_uint16_t uint16_t; +typedef rt_int16_t int16_t; +typedef rt_uint64_t uint64_t; +typedef rt_int64_t int64_t; + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_file_handle; +typedef void *os_dir_stream; +typedef int os_raw_file_handle; +typedef int os_poll_file_handle; +typedef unsigned int os_nfds_t; +typedef int os_timespec; + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#endif /* RTTHREAD_PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/rt-thread/rtt_file.c b/core/shared/platform/rt-thread/rtt_file.c new file mode 100644 index 0000000000..f858f7eaed --- /dev/null +++ b/core/shared/platform/rt-thread/rtt_file.c @@ -0,0 +1,200 @@ +/* + * Copyright 2024 Sony Semiconductor Solutions Corporation. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#include +#include +#include +#include +#include + +struct iovec { + void *iov_base; + size_t iov_len; +}; + +ssize_t +readv(int fd, const struct iovec *iov, int iovcnt) +{ + ssize_t ntotal; + ssize_t nread; + size_t remaining; + uint8_t *buffer; + int i; + + /* Process each entry in the struct iovec array */ + + for (i = 0, ntotal = 0; i < iovcnt; i++) { + /* Ignore zero-length reads */ + + if (iov[i].iov_len > 0) { + buffer = iov[i].iov_base; + remaining = iov[i].iov_len; + + /* Read repeatedly as necessary to fill buffer */ + + do { + /* NOTE: read() is a cancellation point */ + + nread = read(fd, buffer, remaining); + + /* Check for a read error */ + + if (nread < 0) { + return nread; + } + + /* Check for an end-of-file condition */ + + else if (nread == 0) { + return ntotal; + } + + /* Update pointers and counts in order to handle partial + * buffer reads. + */ + + buffer += nread; + remaining -= nread; + ntotal += nread; + } while (remaining > 0); + } + } + + return ntotal; +} + +ssize_t +writev(int fd, const struct iovec *iov, int iovcnt) +{ + uint16_t i, num; + int length; + + num = 0; + for (i = 0; i < iovcnt; i++) { + if (iov[i].iov_len > 0) { + length = write(fd, iov[i].iov_base, iov[i].iov_len); + if (length != iov[i].iov_len) + return errno; + + num += iov[i].iov_len; + } + } + return num; +} + +int +fstatat(int fd, const char *path, struct stat *buf, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +mkdirat(int fd, const char *path, mode_t mode) +{ + errno = ENOSYS; + return -1; +} + +ssize_t +readlinkat(int fd, const char *path, char *buf, size_t bufsize) +{ + errno = EINVAL; + return -1; +} + +int +linkat(int fd1, const char *path1, int fd2, const char *path2, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +renameat(int fromfd, const char *from, int tofd, const char *to) +{ + errno = ENOSYS; + return -1; +} + +int +symlinkat(const char *target, int fd, const char *path) +{ + errno = ENOSYS; + return -1; +} + +int +unlinkat(int fd, const char *path, int flag) +{ + errno = ENOSYS; + return -1; +} + +int +utimensat(int fd, const char *path, const struct timespec *ts, int flag) +{ + errno = ENOSYS; + return -1; +} + +DIR * +fdopendir(int fd) +{ + errno = ENOSYS; + return NULL; +} + +int +fdatasync(int fd) +{ + errno = ENOSYS; + return -1; +} + +ssize_t +preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + errno = ENOSYS; + return 0; +} + +ssize_t +pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + errno = ENOSYS; + return 0; +} + +char * +realpath(char *path, char *resolved_path) +{ + errno = ENOSYS; + return NULL; +} + +int +futimens(int fd, const struct timespec *times) +{ + errno = ENOSYS; + return -1; +} + +int +posix_fallocate(int __fd, off_t __offset, off_t __length) +{ + errno = ENOSYS; + return -1; +} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return -1; +} diff --git a/core/shared/platform/rt-thread/rtt_platform.c b/core/shared/platform/rt-thread/rtt_platform.c new file mode 100644 index 0000000000..904bb52ed1 --- /dev/null +++ b/core/shared/platform/rt-thread/rtt_platform.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +typedef struct os_malloc_list { + void *real; + void *used; + rt_list_t node; +} os_malloc_list_t; + +int +bh_platform_init(void) +{ + return 0; +} + +void +bh_platform_destroy(void) +{} + +void * +os_malloc(unsigned size) +{ + void *buf_origin; + void *buf_fixed; + rt_ubase_t *addr_field; + + buf_origin = rt_malloc(size + 8 + sizeof(rt_ubase_t)); + buf_fixed = buf_origin + sizeof(void *); + if ((rt_ubase_t)buf_fixed & 0x7) { + buf_fixed = (void *)((rt_ubase_t)(buf_fixed + 8) & (~7)); + } + + addr_field = buf_fixed - sizeof(rt_ubase_t); + *addr_field = (rt_ubase_t)buf_origin; + + return buf_fixed; +} + +void * +os_realloc(void *ptr, unsigned size) +{ + + void *mem_origin; + void *mem_new; + void *mem_new_fixed; + rt_ubase_t *addr_field; + + if (!ptr) { + return RT_NULL; + } + + addr_field = ptr - sizeof(rt_ubase_t); + mem_origin = (void *)(*addr_field); + mem_new = rt_realloc(mem_origin, size + 8 + sizeof(rt_ubase_t)); + + if (mem_origin != mem_new) { + mem_new_fixed = mem_new + sizeof(rt_ubase_t); + if ((rt_ubase_t)mem_new_fixed & 0x7) { + mem_new_fixed = (void *)((rt_ubase_t)(mem_new_fixed + 8) & (~7)); + } + + addr_field = mem_new_fixed - sizeof(rt_ubase_t); + *addr_field = (rt_ubase_t)mem_new; + + return mem_new_fixed; + } + + return ptr; +} + +void +os_free(void *ptr) +{ + void *mem_origin; + rt_ubase_t *addr_field; + + if (ptr) { + addr_field = ptr - sizeof(rt_ubase_t); + mem_origin = (void *)(*addr_field); + + rt_free(mem_origin); + } +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} + +static char wamr_vprint_buf[RT_CONSOLEBUF_SIZE * 2]; + +int +os_printf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + rt_size_t len = + vsnprintf(wamr_vprint_buf, sizeof(wamr_vprint_buf) - 1, format, ap); + wamr_vprint_buf[len] = 0x00; + rt_kputs(wamr_vprint_buf); + va_end(ap); + return 0; +} + +int +os_vprintf(const char *format, va_list ap) +{ + rt_size_t len = + vsnprintf(wamr_vprint_buf, sizeof(wamr_vprint_buf) - 1, format, ap); + wamr_vprint_buf[len] = 0; + rt_kputs(wamr_vprint_buf); + return 0; +} + +uint64 +os_time_get_boot_us(void) +{ + uint64 ret = rt_tick_get() * 1000; + ret /= RT_TICK_PER_SECOND; + return ret; +} + +uint64 +os_time_thread_cputime_us(void) +{ + /* FIXME if u know the right api */ + return os_time_get_boot_us(); +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + void *buf_origin; + void *buf_fixed; + rt_ubase_t *addr_field; + + buf_origin = rt_malloc(size + 8 + sizeof(rt_ubase_t)); + if (!buf_origin) + return NULL; + + buf_fixed = buf_origin + sizeof(void *); + if ((rt_ubase_t)buf_fixed & 0x7) { + buf_fixed = (void *)((rt_ubase_t)(buf_fixed + 8) & (~7)); + } + + addr_field = buf_fixed - sizeof(rt_ubase_t); + *addr_field = (rt_ubase_t)buf_origin; + + memset(buf_origin, 0, size + 8 + sizeof(rt_ubase_t)); + return buf_fixed; +} + +void +os_munmap(void *addr, size_t size) +{ + void *mem_origin; + rt_ubase_t *addr_field; + + if (addr) { + addr_field = addr - sizeof(rt_ubase_t); + mem_origin = (void *)(*addr_field); + + rt_free(mem_origin); + } +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush(void) +{} + +void +os_icache_flush(void *start, size_t len) +{} + +int +os_getpagesize(void) +{ + return 4096; +} + +void * +os_mremap(void *in, size_t old_size, size_t new_size) +{ + return os_realloc(in, new_size); +} + +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time) +{ + *time = rt_tick_get() * 1000ll * 1000ll; + return 0; +} + +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution) +{ + return 0; +} diff --git a/core/shared/platform/rt-thread/rtt_socket.c b/core/shared/platform/rt-thread/rtt_socket.c new file mode 100644 index 0000000000..ae1d9ed33e --- /dev/null +++ b/core/shared/platform/rt-thread/rtt_socket.c @@ -0,0 +1,385 @@ +/* + * Copyright 2024 Sony Semiconductor Solutions Corporation. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) +{ + return BHT_ERROR; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + return BHT_ERROR; +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + return BHT_ERROR; +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + return BHT_ERROR; +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + return BHT_ERROR; +} + +int +os_socket_close(bh_socket_t socket) +{ + return BHT_ERROR; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + return BHT_ERROR; +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + return BHT_ERROR; +} + +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + return BHT_ERROR; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + return BHT_ERROR; +} + +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + return BHT_ERROR; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + return BHT_ERROR; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + return __WASI_ENOSYS; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + return BHT_ERROR; +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + return BHT_ERROR; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + return BHT_ERROR; +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + return BHT_ERROR; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + return BHT_ERROR; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + return BHT_ERROR; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + return BHT_ERROR; +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + return BHT_ERROR; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + return BHT_ERROR; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + return BHT_ERROR; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + return BHT_ERROR; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) +{ + return BHT_ERROR; +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) +{ + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) +{ + return BHT_ERROR; +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) +{ + return BHT_ERROR; +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + return BHT_ERROR; +} + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + return BHT_ERROR; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + return BHT_ERROR; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + return BHT_ERROR; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + return BHT_ERROR; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ + return BHT_ERROR; +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ + return BHT_ERROR; +} + +static void +swap16(uint8 *pData) +{ + uint8 value = *pData; + *(pData) = *(pData + 1); + *(pData + 1) = value; +} + +static void +swap32(uint8 *pData) +{ + uint8 value = *pData; + *pData = *(pData + 3); + *(pData + 3) = value; + + value = *(pData + 1); + *(pData + 1) = *(pData + 2); + *(pData + 2) = value; +} + +/** In-enclave implementation of POSIX functions **/ +static bool +is_little_endian() +{ + long i = 0x01020304; + unsigned char *c = (unsigned char *)&i; + return (*c == 0x04) ? true : false; +} + +uint16 +htons(uint16 value) +{ + uint16 ret; + if (is_little_endian()) { + ret = value; + swap16((uint8 *)&ret); + return ret; + } + + return value; +} + +uint32 +htonl(uint32 value) +{ + uint32 ret; + if (is_little_endian()) { + ret = value; + swap32((uint8 *)&ret); + return ret; + } + + return value; +} diff --git a/core/shared/platform/rt-thread/rtt_thread.c b/core/shared/platform/rt-thread/rtt_thread.c new file mode 100644 index 0000000000..5f988fad0d --- /dev/null +++ b/core/shared/platform/rt-thread/rtt_thread.c @@ -0,0 +1,427 @@ +/* + * Copyright 2024 Sony Semiconductor Solutions Corporation. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#include +#include +#include +#include +#include + +struct os_thread_data; +typedef struct os_thread_wait_node *os_thread_wait_list; +typedef struct os_thread_wait_node { + /* Binary semaphore */ + rt_sem_t sem; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Next thread data */ + struct os_thread_data *next; + /* Thread handle */ + rt_thread_t handle; + /* Thread start routine */ + thread_start_routine_t start_routine; + /* Thread start routine argument */ + void *arg; + /* Wait node of current thread */ + os_thread_wait_node wait_node; + /* Lock for waiting list */ + rt_mutex_t wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; +} os_thread_data; + +/* Lock for thread data list */ +static rt_mutex_t thread_data_lock; + +static bool is_thread_sys_inited = false; + +/* Thread data list */ +static os_thread_data *thread_data_list = NULL; + +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Thread name index */ +static int thread_name_index = 0; + +static void +thread_data_list_add(os_thread_data *thread_data) +{ + rt_mutex_take(thread_data_lock, RT_WAITING_FOREVER); + if (!thread_data_list) + thread_data_list = thread_data; + else { + /* If already in list, just return */ + os_thread_data *p = thread_data_list; + while (p) { + if (p == thread_data) { + rt_mutex_release(thread_data_lock); + return; + } + p = p->next; + } + + /* Set as head of list */ + thread_data->next = thread_data_list; + thread_data_list = thread_data; + } + rt_mutex_release(thread_data_lock); +} + +static void +os_thread_wrapper(void *arg) +{ + os_thread_data *thread_data = arg; + + thread_data->handle = rt_thread_self(); + thread_data_list_add(thread_data); + + thread_data->start_routine(thread_data->arg); + rt_kprintf("start_routine quit\n"); + os_thread_exit(NULL); +} + +static void +thread_data_list_remove(os_thread_data *thread_data) +{ + rt_mutex_take(thread_data_lock, RT_WAITING_FOREVER); + if (thread_data_list) { + if (thread_data_list == thread_data) + thread_data_list = thread_data_list->next; + else { + /* Search and remove it from list */ + os_thread_data *p = thread_data_list; + while (p && p->next != thread_data) + p = p->next; + if (p && p->next == thread_data) + p->next = p->next->next; + } + } + rt_mutex_release(thread_data_lock); +} + +static os_thread_data * +thread_data_list_lookup(rt_thread_t handle) +{ + rt_mutex_take(thread_data_lock, RT_WAITING_FOREVER); + if (thread_data_list) { + os_thread_data *p = thread_data_list; + while (p) { + if (p->handle == handle) { + /* Found */ + rt_mutex_release(thread_data_lock); + return p; + } + p = p->next; + } + } + rt_mutex_release(thread_data_lock); + return NULL; +} + +static os_thread_data * +thread_data_current() +{ + rt_thread_t handle = rt_thread_self(); + return thread_data_list_lookup(handle); +} + +int +os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + + if (!(thread_data_lock = + rt_mutex_create("thread_data_lock_mutex", RT_IPC_FLAG_FIFO))) + return BHT_ERROR; + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); + + if (!(supervisor_thread_data.wait_node.sem = + rt_sem_create("spvr", 0, RT_IPC_FLAG_PRIO))) { + rt_mutex_delete(thread_data_lock); + return BHT_ERROR; + } + + supervisor_thread_data.handle = rt_thread_self(); + /* Set as head of thread data list */ + thread_data_list = &supervisor_thread_data; + + is_thread_sys_inited = true; + return BHT_OK; +} + +void +os_thread_sys_destroy() +{ + if (is_thread_sys_inited) { + rt_sem_release(supervisor_thread_data.wait_node.sem); + rt_mutex_delete(thread_data_lock); + is_thread_sys_inited = false; + } +} + +korp_tid +os_self_thread(void) +{ + return rt_thread_self(); +} + +uint8 * +os_thread_get_stack_boundary(void) +{ + rt_thread_t tid = rt_thread_self(); + return tid->stack_addr; +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} + +int +os_mutex_init(korp_mutex *mutex) +{ + return rt_mutex_init(mutex, "wamr0", RT_IPC_FLAG_FIFO); +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + return rt_mutex_detach(mutex); +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + return rt_mutex_take(mutex, RT_WAITING_FOREVER); +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + return rt_mutex_release(mutex); +} + +/* + * functions below was not implement + */ + +int +os_cond_init(korp_cond *cond) +{ + return 0; +} + +int +os_cond_destroy(korp_cond *cond) +{ + return 0; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return 0; +} + +int +os_cond_signal(korp_cond *cond) +{ + return 0; +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + return 0; +} + +int +os_rwlock_init(korp_rwlock *lock) +{ + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ + + return BHT_OK; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ + + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ + return BHT_OK; +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + os_thread_data *thread_data; + char thread_name[32]; + void *stack; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Create and initialize thread data */ + if (!(thread_data = rt_malloc(sizeof(os_thread_data)))) + return BHT_ERROR; + + memset(thread_data, 0, sizeof(os_thread_data)); + + thread_data->start_routine = start; + thread_data->arg = arg; + + if (!(thread_data->wait_node.sem = + rt_sem_create("sem", 0, RT_IPC_FLAG_PRIO))) + goto fail1; + + if (!(thread_data->wait_list_lock = + rt_mutex_create("wait_list_lock_mutex", RT_IPC_FLAG_FIFO))) + goto fail2; + + snprintf(thread_name, sizeof(thread_name), "%s%d", "wasm-thread-", + ++thread_name_index); + + thread_data->handle = rt_thread_create(thread_name, os_thread_wrapper, + thread_data, stack_size, 15, 5); + if (thread_data->handle == RT_NULL) { + rt_kprintf("os_thread_create_with_prio failed, tid=%d\n", + thread_data->handle); + goto fail3; + } + + thread_data_list_add(thread_data); + *p_tid = thread_data->handle; + rt_thread_startup(*p_tid); + return BHT_OK; + +fail3: + rt_mutex_delete(thread_data->wait_list_lock); +fail2: + rt_sem_delete(thread_data->wait_node.sem); +fail1: + rt_free(thread_data); + return BHT_ERROR; +} + +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_detach(korp_tid thread) +{ + /* Do nothing */ + (void)thread; + return BHT_OK; +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ + os_thread_data *thread_data, *curr_thread_data; + rt_thread_t handle = thread; + + (void)value_ptr; + + /* Get thread data of current thread */ + curr_thread_data = thread_data_current(); + curr_thread_data->wait_node.next = NULL; + + /* Get thread data */ + thread_data = thread_data_list_lookup(handle); + + rt_mutex_take(thread_data->wait_list_lock, RT_WAITING_FOREVER); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = &curr_thread_data->wait_node; + else { + /* 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; + } + rt_mutex_release(thread_data->wait_list_lock); + + /* Wait the sem */ + rt_sem_take(curr_thread_data->wait_node.sem, RT_WAITING_FOREVER); + return BHT_OK; +} + +static void +os_thread_cleanup(void) +{ + os_thread_data *thread_data = thread_data_current(); + os_thread_wait_list thread_wait_list; + rt_mutex_t wait_list_lock; + rt_sem_t wait_node_sem; + + // bh_assert(thread_data != NULL); + wait_list_lock = thread_data->wait_list_lock; + thread_wait_list = thread_data->thread_wait_list; + wait_node_sem = thread_data->wait_node.sem; + + rt_mutex_take(wait_list_lock, RT_WAITING_FOREVER); + if (thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + rt_sem_release(head->sem); + head = next; + } + } + rt_mutex_release(wait_list_lock); + + /* Free sem and lock */ + rt_sem_delete(wait_node_sem); + rt_mutex_delete(wait_list_lock); + + thread_data_list_remove(thread_data); + rt_free(thread_data); +} + +void +os_thread_exit(void *retval) +{ + (void)retval; + os_thread_cleanup(); + // vTaskDelete(NULL); +} + +int +os_thread_kill(korp_tid tid, int sig) +{ + return rt_thread_kill(tid, sig); +} diff --git a/core/shared/platform/rt-thread/shared_platform.cmake b/core/shared/platform/rt-thread/shared_platform.cmake new file mode 100644 index 0000000000..fce9bff339 --- /dev/null +++ b/core/shared/platform/rt-thread/shared_platform.cmake @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_RTT) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +# include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) + diff --git a/core/shared/platform/vxworks/bh_assert.c b/core/shared/platform/vxworks/bh_assert.c deleted file mode 100644 index c609fb93d4..0000000000 --- a/core/shared/platform/vxworks/bh_assert.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_assert.h" -#include -#include -#include - -#ifdef BH_TEST -#include -#endif - -#ifdef BH_TEST -/* for exception throwing */ -jmp_buf bh_test_jb; -#endif - -void bh_assert_internal(int v, const char *file_name, int line_number, - const char *expr_string) -{ - if (v) - return; - - if (!file_name) - file_name = "NULL FILENAME"; - if (!expr_string) - expr_string = "NULL EXPR_STRING"; - - printf("\nASSERTION FAILED: %s, at FILE=%s, LINE=%d\n", expr_string, - file_name, line_number); - -#ifdef BH_TEST - longjmp(bh_test_jb, 1); -#endif - - abort(); -} - -void bh_debug_internal(const char *file_name, int line_number, const char *fmt, - ...) -{ -#ifndef JEFF_TEST_VERIFIER - va_list args; - - va_start(args, fmt); - bh_assert(file_name); - - printf("\nDebug info FILE=%s, LINE=%d: ", file_name, line_number); - vprintf(fmt, args); - - va_end(args); - printf("\n"); -#endif -} - diff --git a/core/shared/platform/vxworks/bh_definition.c b/core/shared/platform/vxworks/bh_definition.c deleted file mode 100644 index 4cdf982597..0000000000 --- a/core/shared/platform/vxworks/bh_definition.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" - -#define RSIZE_MAX 0x7FFFFFFF - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n) -{ - char *dest = (char*) s1; - char *src = (char*) s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} - -int b_strcat_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s1) + strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcat(s1, s2); - - return 0; -} - -int b_strcpy_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcpy(s1, s2); - - return 0; -} diff --git a/core/shared/platform/vxworks/bh_platform.c b/core/shared/platform/vxworks/bh_platform.c deleted file mode 100644 index ea5666b30a..0000000000 --- a/core/shared/platform/vxworks/bh_platform.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_common.h" -#include "bh_assert.h" - -#include -#include -#include -#include -#include -#include -#include -#include - - -char *bh_strdup(const char *s) -{ - uint32 size; - char *s1 = NULL; - - if (s) { - size = (uint32)(strlen(s) + 1); - if ((s1 = bh_malloc(size))) - bh_memcpy_s(s1, size, s, size); - } - return s1; -} - -int bh_platform_init() -{ - return 0; -} - -char* -bh_read_file_to_buffer(const char *filename, uint32 *ret_size) -{ - char *buffer; - int file; - uint32 file_size, read_size; - struct stat stat_buf; - - if (!filename || !ret_size) { - printf("Read file to buffer failed: invalid filename or ret size.\n"); - return NULL; - } - - if ((file = open(filename, O_RDONLY, 0)) == -1) { - printf("Read file to buffer failed: open file %s failed.\n", - filename); - return NULL; - } - - if (fstat(file, &stat_buf) != 0) { - printf("Read file to buffer failed: fstat file %s failed.\n", - filename); - close(file); - return NULL; - } - - file_size = (uint32)stat_buf.st_size; - - if (!(buffer = bh_malloc(file_size))) { - printf("Read file to buffer failed: alloc memory failed.\n"); - close(file); - return NULL; - } - - read_size = (uint32)read(file, buffer, file_size); - close(file); - - if (read_size < file_size) { - printf("Read file to buffer failed: read file content failed.\n"); - bh_free(buffer); - return NULL; - } - - *ret_size = file_size; - return buffer; -} - -void * -bh_mmap(void *hint, uint32 size, int prot, int flags) -{ - int map_prot = PROT_NONE; - int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; - uint64 request_size, page_size; - uint8 *addr, *addr_aligned; - uint32 i; - - /* align to 2M if no less than 2M, else align to 4K */ - page_size = size < 2 * 1024 * 1024 ? 4096 : 2 * 1024 * 1024; - request_size = (size + page_size - 1) & ~(page_size - 1); - request_size += page_size; - - if (request_size >= UINT32_MAX) - return NULL; - - if (prot & MMAP_PROT_READ) - map_prot |= PROT_READ; - - if (prot & MMAP_PROT_WRITE) - map_prot |= PROT_WRITE; - - if (prot & MMAP_PROT_EXEC) - map_prot |= PROT_EXEC; - -#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) - if (flags & MMAP_MAP_32BIT) - map_flags |= MAP_32BIT; -#endif - - if (flags & MMAP_MAP_FIXED) - map_flags |= MAP_FIXED; - - /* try 5 times */ - for (i = 0; i < 5; i ++) { - addr = mmap(hint, size, map_prot, map_flags, -1, 0); - if (addr != MAP_FAILED) - break; - } - if (addr == MAP_FAILED) - return NULL; - - addr_aligned = (uint8*)(uintptr_t) - (((uint64)(uintptr_t)addr + page_size - 1) & ~(page_size - 1)); - - /* Unmap memory allocated before the aligned base address */ - if (addr != addr_aligned) { - uint32 prefix_size = (uint32)(addr_aligned - addr); - munmap(addr, prefix_size); - request_size -= prefix_size; - } - - /* Unmap memory allocated after the potentially unaligned end */ - if (size != request_size) { - uint32 suffix_size = (uint32)(request_size - size); - munmap(addr_aligned + size, suffix_size); - request_size -= size; - } - - if (size >= 2 * 1024 * 1024) { - /* Try to use huge page to improve performance */ - if (!madvise(addr, size, MADV_HUGEPAGE)) - /* make huge page become effective */ - memset(addr, 0, size); - } - - return addr_aligned; -} - -void -bh_munmap(void *addr, uint32 size) -{ - if (addr) - munmap(addr, size); -} - -int -bh_mprotect(void *addr, uint32 size, int prot) -{ - int map_prot = PROT_NONE; - - if (!addr) - return 0; - - if (prot & MMAP_PROT_READ) - map_prot |= PROT_READ; - - if (prot & MMAP_PROT_WRITE) - map_prot |= PROT_WRITE; - - if (prot & MMAP_PROT_EXEC) - map_prot |= PROT_EXEC; - - return mprotect(addr, size, map_prot); -} - diff --git a/core/shared/platform/vxworks/bh_platform.h b/core/shared/platform/vxworks/bh_platform.h deleted file mode 100644 index 107f6a59f7..0000000000 --- a/core/shared/platform/vxworks/bh_platform.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_PLATFORM_H -#define _BH_PLATFORM_H - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_memory.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef uint64_t uint64; -typedef int64_t int64; - -extern void DEBUGME(void); - -#define DIE do{bh_debug("Die here\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); DEBUGME(void); while(1);}while(0) - -#ifndef BH_PLATFORM_VXWORKS -#define BH_PLATFORM_VXWORKS -#endif - -/* NEED qsort */ - -#define _STACK_SIZE_ADJUSTMENT (32 * 1024) - -/* Stack size of applet threads's native part. */ -#define BH_APPLET_PRESERVED_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT) - -/* Default thread priority */ -#define BH_THREAD_DEFAULT_PRIORITY 0 - -#define BH_ROUTINE_MODIFIER - -#define BHT_TIMEDOUT ETIMEDOUT - -#define INVALID_THREAD_ID 0xFFffFFff - -typedef pthread_t korp_tid; -typedef pthread_mutex_t korp_mutex; -typedef sem_t korp_sem; -typedef pthread_cond_t korp_cond; -typedef pthread_t korp_thread; -typedef void* (*thread_start_routine_t)(void*); - -#define wa_malloc bh_malloc -#define wa_free bh_free -#define wa_strdup bh_strdup - -#define bh_printf printf - -int snprintf(char *buffer, size_t count, const char *format, ...); -double fmod(double x, double y); -float fmodf(float x, float y); -double sqrt(double x); - -#define BH_WAIT_FOREVER 0xFFFFFFFF - -#ifndef NULL -# define NULL ((void*) 0) -#endif - -/** - * Return the offset of the given field in the given type. - * - * @param Type the type containing the filed - * @param field the field in the type - * - * @return the offset of field in Type - */ -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) -#endif - -#define bh_assert assert - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, - unsigned int n); -int b_strcat_s(char * s1, size_t s1max, const char * s2); -int b_strcpy_s(char * s1, size_t s1max, const char * s2); - -char *bh_read_file_to_buffer(const char *filename, uint32 *ret_size); - -char *bh_strdup(const char *s); - -int bh_platform_init(); - -/* MMAP mode */ -enum { - MMAP_PROT_NONE = 0, - MMAP_PROT_READ = 1, - MMAP_PROT_WRITE = 2, - MMAP_PROT_EXEC = 4, -}; - -/* MMAP flags */ -enum { - MMAP_MAP_NONE = 0, - /* Put the mapping into 0 to 2 G, supported only on x86_64 */ - MMAP_MAP_32BIT = 1, - /* Don't interpret addr as a hint: place the mapping at exactly - that address. */ - MMAP_MAP_FIXED = 2 -}; - -void *bh_mmap(void *hint, unsigned int size, int prot, int flags); -void bh_munmap(void *addr, uint32 size); -int bh_mprotect(void *addr, uint32 size, int prot); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/shared/platform/vxworks/bh_platform_log.c b/core/shared/platform/vxworks/bh_platform_log.c deleted file mode 100644 index 902ca7d60b..0000000000 --- a/core/shared/platform/vxworks/bh_platform_log.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include - -void bh_log_emit(const char *fmt, va_list ap) -{ - vprintf(fmt, ap); - fflush(stdout); -} - -int bh_fprintf(FILE *stream, const char *fmt, ...) -{ - va_list ap; - int ret; - - va_start(ap, fmt); - ret = vfprintf(stream ? stream : stdout, fmt, ap); - va_end(ap); - - return ret; -} - -int bh_fflush(void *stream) -{ - return fflush(stream ? stream : stdout); -} diff --git a/core/shared/platform/vxworks/bh_thread.c b/core/shared/platform/vxworks/bh_thread.c deleted file mode 100644 index 3b51596fd2..0000000000 --- a/core/shared/platform/vxworks/bh_thread.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_thread.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include -#include -#include - -static bool is_thread_sys_inited = false; - -static korp_mutex thread_list_lock; -static pthread_key_t thread_local_storage_key[BH_MAX_TLS_NUM]; - -int _vm_thread_sys_init() -{ - unsigned i, j; - int ret; - - if (is_thread_sys_inited) - return 0; - - for (i = 0; i < BH_MAX_TLS_NUM; i++) { - ret = pthread_key_create(&thread_local_storage_key[i], NULL); - if (ret) - goto fail; - } - - ret = vm_mutex_init(&thread_list_lock); - if (ret) - goto fail; - - is_thread_sys_inited = true; - return 0; - - fail: for (j = 0; j < i; j++) - pthread_key_delete(thread_local_storage_key[j]); - return -1; -} - -void vm_thread_sys_destroy(void) -{ - if (is_thread_sys_inited) { - unsigned i; - for (i = 0; i < BH_MAX_TLS_NUM; i++) - pthread_key_delete(thread_local_storage_key[i]); - vm_mutex_destroy(&thread_list_lock); - is_thread_sys_inited = false; - } -} - -typedef struct { - thread_start_routine_t start; - void* stack; - uint32 stack_size; - void* arg; -} thread_wrapper_arg; - -static void *vm_thread_wrapper(void *arg) -{ - thread_wrapper_arg * targ = arg; - LOG_VERBOSE("THREAD CREATE 0x%08x\n", &targ); - targ->stack = (void *)((uintptr_t)(&arg) & (uintptr_t)~0xfff); - _vm_tls_put(1, targ); - targ->start(targ->arg); - bh_free(targ); - _vm_tls_put(1, NULL); - return NULL; -} - -int _vm_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio) -{ - pthread_attr_t tattr; - thread_wrapper_arg *targ; - - bh_assert(stack_size > 0); - bh_assert(tid); - bh_assert(start); - - *tid = INVALID_THREAD_ID; - - pthread_attr_init(&tattr); - pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); - if (pthread_attr_setstacksize(&tattr, stack_size) != 0) { - bh_debug("Invalid thread stack size %u. Min stack size on Linux = %u", - stack_size, PTHREAD_STACK_MIN); - pthread_attr_destroy(&tattr); - return BHT_ERROR; - } - - targ = (thread_wrapper_arg*) bh_malloc(sizeof(*targ)); - if (!targ) { - pthread_attr_destroy(&tattr); - return BHT_ERROR; - } - - targ->start = start; - targ->arg = arg; - targ->stack_size = stack_size; - - if (pthread_create(tid, &tattr, vm_thread_wrapper, targ) != 0) { - pthread_attr_destroy(&tattr); - bh_free(targ); - return BHT_ERROR; - } - - pthread_attr_destroy(&tattr); - return BHT_OK; -} - -int _vm_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, - unsigned int stack_size) -{ - return _vm_thread_create_with_prio(tid, start, arg, stack_size, - BH_THREAD_DEFAULT_PRIORITY); -} - -korp_tid _vm_self_thread() -{ - return (korp_tid) pthread_self(); -} - -void vm_thread_exit(void * code) -{ - bh_free(_vm_tls_get(1)); - _vm_tls_put(1, NULL); - pthread_exit(code); -} - -void *_vm_tls_get(unsigned idx) -{ - bh_assert(idx < BH_MAX_TLS_NUM); - return pthread_getspecific(thread_local_storage_key[idx]); -} - -int _vm_tls_put(unsigned idx, void * tls) -{ - bh_assert(idx < BH_MAX_TLS_NUM); - pthread_setspecific(thread_local_storage_key[idx], tls); - return BHT_OK; -} - -int _vm_mutex_init(korp_mutex *mutex) -{ - return pthread_mutex_init(mutex, NULL) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_recursive_mutex_init(korp_mutex *mutex) -{ - int ret; - - pthread_mutexattr_t mattr; - - bh_assert(mutex); - ret = pthread_mutexattr_init(&mattr); - if (ret) - return BHT_ERROR; - - pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); - ret = pthread_mutex_init(mutex, &mattr); - pthread_mutexattr_destroy(&mattr); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_mutex_destroy(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_destroy(mutex); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/* Returned error (EINVAL, EAGAIN and EDEADLK) from - locking the mutex indicates some logic error present in - the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_lock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_lock(mutex); - if (0 != ret) { - printf("vm mutex lock failed (ret=%d)!\n", ret); - exit(-1); - } -} - -int vm_mutex_trylock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_trylock(mutex); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -/* Returned error (EINVAL, EAGAIN and EPERM) from - unlocking the mutex indicates some logic error present - in the program somewhere. - Don't try to recover error for an existing unknown error.*/ -void vm_mutex_unlock(korp_mutex *mutex) -{ - int ret; - - bh_assert(mutex); - ret = pthread_mutex_unlock(mutex); - if (0 != ret) { - printf("vm mutex unlock failed (ret=%d)!\n", ret); - exit(-1); - } -} - -int _vm_sem_init(korp_sem* sem, unsigned int c) -{ - int ret; - - bh_assert(sem); - ret = sem_init(sem, 0, c); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_destroy(korp_sem *sem) -{ - int ret; - - bh_assert(sem); - ret = sem_destroy(sem); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_wait(korp_sem *sem) -{ - int ret; - - bh_assert(sem); - - ret = sem_wait(sem); - - return ret == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_sem_reltimedwait(korp_sem *sem, int mills) -{ - int ret = BHT_OK; - - struct timespec timeout; - const int mills_per_sec = 1000; - const int mills_to_nsec = 1E6; - - bh_assert(sem); - - if (mills == (int)BHT_WAIT_FOREVER) { - ret = sem_wait(sem); - } else { - - timeout.tv_sec = mills / mills_per_sec; - timeout.tv_nsec = (mills % mills_per_sec) * mills_to_nsec; - timeout.tv_sec += time(NULL); - - ret = sem_timedwait(sem, &timeout); - } - - if (ret != BHT_OK) { - if (errno == BHT_TIMEDOUT) { - ret = BHT_TIMEDOUT; - errno = 0; - } else { - bh_debug("Faliure happens when timed wait is called"); - bh_assert(0); - } - } - - return ret; -} - -int _vm_sem_post(korp_sem *sem) -{ - bh_assert(sem); - - return sem_post(sem) == 0 ? BHT_OK : BHT_ERROR; -} - -int _vm_cond_init(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_init(cond, NULL) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_destroy(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_destroy(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex) -{ - bh_assert(cond); - bh_assert(mutex); - - if (pthread_cond_wait(cond, mutex) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -static void msec_nsec_to_abstime(struct timespec *ts, int64 msec, int32 nsec) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - ts->tv_sec = (long int)(tv.tv_sec + msec / 1000); - ts->tv_nsec = (long int)(tv.tv_usec * 1000 + (msec % 1000) * 1000000 + nsec); - - if (ts->tv_nsec >= 1000000000L) { - ts->tv_sec++; - ts->tv_nsec -= 1000000000L; - } -} - -int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) -{ - int ret; - struct timespec abstime; - - if (mills == (int)BHT_WAIT_FOREVER) - ret = pthread_cond_wait(cond, mutex); - else { - msec_nsec_to_abstime(&abstime, mills, 0); - ret = pthread_cond_timedwait(cond, mutex, &abstime); - } - - if (ret != BHT_OK && ret != BHT_TIMEDOUT) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_signal(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_signal(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_cond_broadcast(korp_cond *cond) -{ - bh_assert(cond); - - if (pthread_cond_broadcast(cond) != BHT_OK) - return BHT_ERROR; - - return BHT_OK; -} - -int _vm_thread_cancel(korp_tid thread) -{ - return pthread_cancel(thread); -} - -int _vm_thread_join(korp_tid thread, void **value_ptr, int mills) -{ - return pthread_join(thread, value_ptr); -} - -int _vm_thread_detach(korp_tid thread) -{ - return pthread_detach(thread); -} - diff --git a/core/shared/platform/vxworks/bh_time.c b/core/shared/platform/vxworks/bh_time.c deleted file mode 100644 index fefa0496ab..0000000000 --- a/core/shared/platform/vxworks/bh_time.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_time.h" - -#include -#include -#include - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -uint64 _bh_time_get_tick_millisecond() -{ - return (uint64)sysconf(_SC_CLK_TCK); -} - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -uint64 _bh_time_get_boot_millisecond() -{ - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { - return 0; - } - - return ((uint64) ts.tv_sec) * 1000 + ((uint64)ts.tv_nsec) / (1000 * 1000); -} - -uint32 bh_get_tick_sec() -{ - return (uint32)(_bh_time_get_boot_millisecond() / 1000); -} - -/* - * This function returns GMT time milliseconds since from 1970.1.1, AKA UNIX time. - * @return milliseconds since from 1970.1.1. - */ -uint64 _bh_time_get_millisecond_from_1970() -{ - struct timespec ts; - - if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { - return 0; - } - - return ((uint64) ts.tv_sec) * 1000 + ((uint64)ts.tv_nsec) / (1000 * 1000); -} - -size_t _bh_time_strftime(char *s, size_t max, const char *format, int64 time) -{ - time_t time_sec = (time_t)(time / 1000); - struct tm *ltp; - - ltp = localtime(&time_sec); - if (ltp == NULL) { - return 0; - } - return strftime(s, max, format, ltp); -} - diff --git a/core/shared/platform/vxworks/platform_init.c b/core/shared/platform/vxworks/platform_init.c new file mode 100644 index 0000000000..2aae13fa14 --- /dev/null +++ b/core/shared/platform/vxworks/platform_init.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} diff --git a/core/shared/platform/vxworks/platform_internal.h b/core/shared/platform/vxworks/platform_internal.h new file mode 100644 index 0000000000..bf9d2885e0 --- /dev/null +++ b/core/shared/platform/vxworks/platform_internal.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_VXWORKS +#define BH_PLATFORM_VXWORKS +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; +typedef pthread_rwlock_t korp_rwlock; +typedef sem_t korp_sem; + +#define OS_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +#define os_thread_local_attribute __thread + +typedef int os_file_handle; +typedef DIR *os_dir_stream; +typedef int os_raw_file_handle; + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef struct pollfd os_poll_file_handle; +typedef nfds_t os_nfds_t; +typedef timespec os_timespec; + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +typedef void (*os_signal_handler)(void *sig_addr); + +int +os_thread_signal_init(os_signal_handler handler); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +void +os_signal_unmask(); + +void +os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#define os_getpagesize getpagesize + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return -1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/vxworks/shared_platform.cmake b/core/shared/platform/vxworks/shared_platform.cmake index 2fdc1330e3..6979ce2357 100644 --- a/core/shared/platform/vxworks/shared_platform.cmake +++ b/core/shared/platform/vxworks/shared_platform.cmake @@ -3,11 +3,16 @@ set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) +add_definitions(-DBH_PLATFORM_VXWORKS) + include_directories(${PLATFORM_SHARED_DIR}) include_directories(${PLATFORM_SHARED_DIR}/../include) +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) -set (PLATFORM_SHARED_SOURCE ${source_all}) +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/windows/platform_init.c b/core/shared/platform/windows/platform_init.c new file mode 100644 index 0000000000..96bcf9ab1a --- /dev/null +++ b/core/shared/platform/windows/platform_init.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +os_thread_sys_init(); + +void +os_thread_sys_destroy(); + +int +init_winsock(); + +void +deinit_winsock(); + +int +bh_platform_init() +{ + if (init_winsock() != 0) { + return -1; + } + + return os_thread_sys_init(); +} + +void +bh_platform_destroy() +{ + deinit_winsock(); + + os_thread_sys_destroy(); +} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} + +unsigned +os_getpagesize() +{ + SYSTEM_INFO sys_info; + GetNativeSystemInfo(&sys_info); + return (unsigned)sys_info.dwPageSize; +} + +void +os_dcache_flush(void) +{} + +void +os_icache_flush(void *start, size_t len) +{} \ No newline at end of file diff --git a/core/shared/platform/windows/platform_internal.h b/core/shared/platform/windows/platform_internal.h new file mode 100644 index 0000000000..f77e1ad5a1 --- /dev/null +++ b/core/shared/platform/windows/platform_internal.h @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +/* + * Suppress the noisy warnings: + * winbase.h: warning C5105: macro expansion producing 'defined' has + * undefined behavior + */ +#pragma warning(disable : 5105) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_wasi_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_WINDOWS +#define BH_PLATFORM_WINDOWS +#endif + +#ifdef _MSC_VER +#ifndef PATH_MAX +#define PATH_MAX MAX_PATH +#endif +#endif /* #ifdef _MSC_VER */ + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef SSIZE_T ssize_t; + +typedef void *korp_thread; +typedef void *korp_tid; +typedef void *korp_mutex; +typedef void *korp_sem; + +typedef struct { + SRWLOCK lock; + bool exclusive; +} korp_rwlock; + +/** + * Create the mutex when os_mutex_lock is called, and no need to + * CloseHandle() for the static lock's lifetime, since + * "The system closes the handle automatically when the process + * terminates. The mutex object is destroyed when its last + * handle has been closed." + * Refer to: + * https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa + */ +#define OS_THREAD_MUTEX_INITIALIZER NULL + +struct os_thread_wait_node; +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; + +unsigned +os_getpagesize(); +void * +os_mem_commit(void *ptr, size_t size, int flags); +void +os_mem_decommit(void *ptr, size_t size); + +#define os_thread_local_attribute __declspec(thread) + +#define strncasecmp _strnicmp +#define strcasecmp _stricmp + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp + +int +os_thread_signal_init(); + +void +os_thread_signal_destroy(); + +bool +os_thread_signal_inited(); + +#define os_signal_unmask() (void)0 +#define os_sigreturn() (void)0 + +#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 + +typedef enum windows_handle_type { + windows_handle_type_socket, + windows_handle_type_file +} windows_handle_type; + +typedef enum windows_access_mode { + windows_access_mode_read = 1 << 0, + windows_access_mode_write = 1 << 1 +} windows_access_mode; + +typedef struct windows_handle { + windows_handle_type type; + __wasi_fdflags_t fdflags; + windows_access_mode access_mode; + union { + HANDLE handle; + SOCKET socket; + } raw; +} windows_handle; + +typedef struct windows_dir_stream { + // Enough space for the wide filename and the info struct itself + char info_buf[PATH_MAX * sizeof(wchar_t) + sizeof(FILE_ID_BOTH_DIR_INFO)]; + char current_entry_name[PATH_MAX]; + // An offset into info_buf to read the next entry from + DWORD cursor; + int cookie; + windows_handle *handle; +} windows_dir_stream; + +typedef windows_dir_stream *os_dir_stream; + +#if WASM_ENABLE_UVWASI == 0 +typedef windows_handle *os_file_handle; +typedef HANDLE os_raw_file_handle; +#else +typedef uint32_t os_file_handle; +typedef uint32_t os_raw_file_handle; +#endif + +#define bh_socket_t windows_handle * + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_poll_file_handle; +typedef unsigned int os_nfds_t; +typedef struct timespec os_timespec; + +// UWP apps do not have stdout/stderr handles so provide a default +// implementation of vprintf on debug builds so output from WASI libc is sent to +// the debugger and not lost completely. +#if !defined(BH_VPRINTF) && !defined(NDEBUG) && WINAPI_PARTITION_DESKTOP == 0 +#define BH_VPRINTF uwp_print_to_debugger +#define UWP_DEFAULT_VPRINTF +#endif + +static inline os_file_handle +os_get_invalid_handle(void) +{ +#if WASM_ENABLE_UVWASI == 0 + return NULL; +#else + return -1; +#endif +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ diff --git a/core/shared/platform/windows/shared_platform.cmake b/core/shared/platform/windows/shared_platform.cmake new file mode 100644 index 0000000000..7a3331eff1 --- /dev/null +++ b/core/shared/platform/windows/shared_platform.cmake @@ -0,0 +1,33 @@ +# 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_WINDOWS) +add_definitions(-DHAVE_STRUCT_TIMESPEC) +add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS) +enable_language(CXX) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c + ${PLATFORM_SHARED_DIR}/*.cpp) + +if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1) + list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/win_file.c) +elseif (WAMR_BUILD_LIBC_UVWASI EQUAL 1) + # uvwasi doesn't need to compile win_file.c + list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/win_file.c) +else() + include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) + set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) +endif() + +include (${CMAKE_CURRENT_LIST_DIR}/../common/memory/platform_api_memory.cmake) +set (source_all ${source_all} ${PLATFORM_COMMON_MEMORY_SOURCE}) + +set (PLATFORM_SHARED_SOURCE ${source_all}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/core/shared/platform/windows/win_atomic.cpp b/core/shared/platform/windows/win_atomic.cpp new file mode 100644 index 0000000000..4e09405bb0 --- /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)((int)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_clock.c b/core/shared/platform/windows/win_clock.c new file mode 100644 index 0000000000..1d618c8b67 --- /dev/null +++ b/core/shared/platform/windows/win_clock.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include +#include "win_util.h" + +#define NANOSECONDS_PER_SECOND 1000000000ULL +#define NANOSECONDS_PER_TICK 100 + +#if WINAPI_PARTITION_DESKTOP +#ifndef __kernel_entry +#define __kernel_entry +#endif +#ifndef NTAPI +#define NTAPI +#endif +#ifndef _Out_ +#define _Out_ +#endif +extern __kernel_entry NTSTATUS NTAPI +NtQueryTimerResolution(_Out_ PULONG MinimumResolution, + _Out_ PULONG MaximumResolution, + _Out_ PULONG CurrentResolution); +#endif + +static __wasi_errno_t +calculate_monotonic_clock_frequency(uint64 *out_frequency) +{ + LARGE_INTEGER frequency; + if (!QueryPerformanceFrequency(&frequency)) + return convert_windows_error_code(GetLastError()); + + *out_frequency = (uint64)frequency.QuadPart; + return __WASI_ESUCCESS; +} + +static __wasi_errno_t +get_performance_counter_value(uint64 *out_counter) +{ + LARGE_INTEGER counter; + if (!QueryPerformanceCounter(&counter)) + return convert_windows_error_code(GetLastError()); + + *out_counter = counter.QuadPart; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + + switch (clock_id) { + case __WASI_CLOCK_MONOTONIC: + { + uint64 frequency; + error = calculate_monotonic_clock_frequency(&frequency); + + if (error != __WASI_ESUCCESS) + return error; + + const uint64 result = (uint64)NANOSECONDS_PER_SECOND / frequency; + *resolution = result; + return error; + } + case __WASI_CLOCK_REALTIME: + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + case __WASI_CLOCK_THREAD_CPUTIME_ID: + { +#if WINAPI_PARTITION_DESKTOP && WASM_ENABLE_WAMR_COMPILER == 0 + ULONG maximum_time; + ULONG minimum_time; + ULONG current_time; + NTSTATUS + status = NtQueryTimerResolution(&maximum_time, &minimum_time, + ¤t_time); + uint64 result = (uint64)current_time * NANOSECONDS_PER_TICK; + *resolution = result / (uint64)NANOSECONDS_PER_SECOND; + return error; +#else + return __WASI_ENOTSUP; +#endif + } + default: + return __WASI_EINVAL; + } +} + +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + + switch (clock_id) { + case __WASI_CLOCK_REALTIME: + { + FILETIME sys_now; +#if NTDDI_VERSION >= NTDDI_WIN8 + GetSystemTimePreciseAsFileTime(&sys_now); +#else + GetSystemTimeAsFileTime(&sys_now); +#endif + *time = convert_filetime_to_wasi_timestamp(&sys_now); + return BHT_OK; + } + case __WASI_CLOCK_MONOTONIC: + { + uint64 frequency; + error = calculate_monotonic_clock_frequency(&frequency); + + if (error != __WASI_ESUCCESS) + return error; + + uint64 counter; + error = get_performance_counter_value(&counter); + + if (error != __WASI_ESUCCESS) + return error; + + if (NANOSECONDS_PER_SECOND % frequency == 0) { + *time = counter * NANOSECONDS_PER_SECOND / frequency; + } + else { + uint64 seconds = counter / frequency; + uint64 fractions = counter % frequency; + *time = seconds * NANOSECONDS_PER_SECOND + + (fractions * NANOSECONDS_PER_SECOND) / frequency; + } + return error; + } + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + case __WASI_CLOCK_THREAD_CPUTIME_ID: + { + FILETIME creation_time; + FILETIME exit_time; + FILETIME kernel_time; + FILETIME user_time; + + HANDLE handle = (clock_id == __WASI_CLOCK_PROCESS_CPUTIME_ID) + ? GetCurrentProcess() + : GetCurrentThread(); + + if (!GetProcessTimes(handle, &creation_time, &exit_time, + &kernel_time, &user_time)) + return convert_windows_error_code(GetLastError()); + + *time = convert_filetime_to_wasi_timestamp(&kernel_time) + + convert_filetime_to_wasi_timestamp(&user_time); + + return error; + } + default: + return __WASI_EINVAL; + } +} diff --git a/core/shared/platform/windows/win_file.c b/core/shared/platform/windows/win_file.c new file mode 100644 index 0000000000..7cfda4cc3a --- /dev/null +++ b/core/shared/platform/windows/win_file.c @@ -0,0 +1,1854 @@ +/* + * Copyright (C) 2023 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "libc_errno.h" +#include "win_util.h" + +#include "PathCch.h" + +#pragma comment(lib, "Pathcch.lib") + +#define CHECK_VALID_HANDLE_WITH_RETURN_VALUE(win_handle, ret) \ + do { \ + if ((win_handle) == NULL \ + || ((win_handle)->type == windows_handle_type_socket \ + && (win_handle)->raw.socket == INVALID_SOCKET) \ + || ((win_handle)->type == windows_handle_type_file \ + && (win_handle)->raw.handle == INVALID_HANDLE_VALUE)) \ + return (ret); \ + \ + } while (0) + +#define CHECK_VALID_HANDLE(win_handle) \ + CHECK_VALID_HANDLE_WITH_RETURN_VALUE(win_handle, __WASI_EBADF) + +#define CHECK_VALID_FILE_HANDLE(win_handle) \ + do { \ + if ((win_handle) == NULL) \ + return __WASI_EBADF; \ + \ + if ((win_handle)->type == windows_handle_type_socket) \ + return __WASI_EINVAL; \ + \ + if (((win_handle)->type == windows_handle_type_file \ + && (win_handle)->raw.handle == INVALID_HANDLE_VALUE)) \ + return __WASI_EBADF; \ + \ + } while (0) + +#define CHECK_VALID_WIN_DIR_STREAM(win_dir_stream) \ + do { \ + if ((win_dir_stream) == NULL) \ + return __WASI_EINVAL; \ + CHECK_VALID_FILE_HANDLE((win_dir_stream)->handle); \ + } while (0) + +static __wasi_filetype_t +get_disk_filetype(DWORD attribute) +{ + if (attribute == INVALID_FILE_ATTRIBUTES) + return __WASI_FILETYPE_UNKNOWN; + if (attribute & FILE_ATTRIBUTE_REPARSE_POINT) + return __WASI_FILETYPE_SYMBOLIC_LINK; + if (attribute & FILE_ATTRIBUTE_DIRECTORY) + return __WASI_FILETYPE_DIRECTORY; + + return __WASI_FILETYPE_REGULAR_FILE; +} + +static __wasi_filetype_t +get_socket_filetype(SOCKET socket) +{ + char socket_type = 0; + int size = sizeof(socket_type); + + if (getsockopt(socket, SOL_SOCKET, SO_TYPE, &socket_type, &size) == 0) { + switch (socket_type) { + case SOCK_STREAM: + return __WASI_FILETYPE_SOCKET_STREAM; + case SOCK_DGRAM: + return __WASI_FILETYPE_SOCKET_DGRAM; + } + } + return __WASI_FILETYPE_UNKNOWN; +} + +static __wasi_errno_t +convert_windows_filetype(os_file_handle handle, DWORD filetype, + __wasi_filetype_t *out_filetype) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + + switch (filetype) { + case FILE_TYPE_DISK: + FILE_ATTRIBUTE_TAG_INFO file_info; + + bool success = GetFileInformationByHandleEx( + handle->raw.handle, FileAttributeTagInfo, &file_info, + sizeof(file_info)); + + if (!success + || file_info.FileAttributes == INVALID_FILE_ATTRIBUTES) { + error = convert_windows_error_code(GetLastError()); + break; + } + + *out_filetype = get_disk_filetype(file_info.FileAttributes); + break; + case FILE_TYPE_CHAR: + *out_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + break; + case FILE_TYPE_PIPE: + if (handle->type == windows_handle_type_socket) + *out_filetype = get_socket_filetype(handle->raw.socket); + else + *out_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + + break; + case FILE_TYPE_REMOTE: + case FILE_TYPE_UNKNOWN: + default: + *out_filetype = __WASI_FILETYPE_UNKNOWN; + } + + return error; +} + +// Converts the input string to a wchar string. +static __wasi_errno_t +convert_to_wchar(const char *str, wchar_t *buf, size_t buf_size) +{ + int converted_chars = + MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, (int)buf_size); + + if (converted_chars == 0) + return convert_windows_error_code(GetLastError()); + + return __WASI_ESUCCESS; +} + +// Get the filepath for a handle. The size of the buffer should be specified in +// terms of wchar. +static __wasi_errno_t +get_handle_filepath(HANDLE handle, wchar_t *buf, DWORD buf_size) +{ + DWORD bufsize_in_chars = buf_size * (sizeof(wchar_t) / sizeof(char)); + DWORD size = GetFinalPathNameByHandleW( + handle, buf, bufsize_in_chars, FILE_NAME_NORMALIZED | VOLUME_NAME_NONE); + + if (size > bufsize_in_chars) + return __WASI_ENAMETOOLONG; + + if (size == 0) + return convert_windows_error_code(GetLastError()); + + return __WASI_ESUCCESS; +} + +static __wasi_errno_t +convert_hresult_error_code(HRESULT error_code) +{ + switch (error_code) { + case E_OUTOFMEMORY: + return __WASI_ENOMEM; + case E_INVALIDARG: + default: + return __WASI_EINVAL; + } +} + +// Returns the absolute filepath from the relative path to the directory +// associated with the provided handle. +static __wasi_errno_t +get_absolute_filepath(HANDLE handle, const char *relative_path, + wchar_t *absolute_path, size_t buf_len) +{ + wchar_t handle_path[PATH_MAX]; + + __wasi_errno_t error = get_handle_filepath(handle, handle_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + wchar_t relative_wpath[PATH_MAX]; + error = convert_to_wchar(relative_path, relative_wpath, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + HRESULT ret = + PathCchCombine(absolute_path, buf_len, handle_path, relative_wpath); + if (ret != S_OK) + error = convert_hresult_error_code(ret); + + return error; +} + +static bool +has_directory_attribute(DWORD attributes) +{ + if (attributes == INVALID_FILE_ATTRIBUTES) + return false; + + return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +static bool +is_directory(const wchar_t *path) +{ + DWORD attributes = GetFileAttributesW(path); + + return has_directory_attribute(attributes); +} + +static bool +has_symlink_attribute(DWORD attributes) +{ + if (attributes == INVALID_FILE_ATTRIBUTES) + return false; + + return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; +} + +static void +init_dir_stream(os_dir_stream dir_stream, os_file_handle handle) +{ + dir_stream->cursor = 0; + dir_stream->handle = handle; + dir_stream->cookie = 0; +} + +static void +reset_dir_stream(os_dir_stream dir_stream) +{ + dir_stream->cursor = 0; + dir_stream->cookie = 0; +} + +// Advances to the next directory entry and optionally reads into to the +// provided buffer if not NULL. +static __wasi_errno_t +read_next_dir_entry(os_dir_stream dir_stream, FILE_ID_BOTH_DIR_INFO **out_entry) +{ + FILE_INFO_BY_HANDLE_CLASS file_info_class; + + if (dir_stream->cookie == 0) + file_info_class = FileIdBothDirectoryRestartInfo; + else + file_info_class = FileIdBothDirectoryInfo; + + if (dir_stream->cursor == 0 + && !GetFileInformationByHandleEx(dir_stream->handle->raw.handle, + file_info_class, dir_stream->info_buf, + sizeof(dir_stream->info_buf))) { + if (out_entry != NULL) + *out_entry = NULL; + DWORD win_error = GetLastError(); + // We've reached the end of the directory - return success + if (win_error == ERROR_NO_MORE_FILES) { + dir_stream->cookie = 0; + dir_stream->cursor = 0; + return __WASI_ESUCCESS; + } + + return convert_windows_error_code(win_error); + } + + FILE_ID_BOTH_DIR_INFO *current_info = + (FILE_ID_BOTH_DIR_INFO *)(dir_stream->info_buf + dir_stream->cursor); + + if (current_info->NextEntryOffset == 0) + dir_stream->cursor = 0; + else + dir_stream->cursor += current_info->NextEntryOffset; + + ++dir_stream->cookie; + + if (out_entry != NULL) + *out_entry = current_info; + else + return __WASI_ESUCCESS; + + // Convert and copy over the wchar filename into the entry_name buf + int ret = WideCharToMultiByte( + CP_UTF8, 0, current_info->FileName, + current_info->FileNameLength / (sizeof(wchar_t) / sizeof(char)), + dir_stream->current_entry_name, sizeof(dir_stream->current_entry_name), + NULL, NULL); + + if (ret == 0) + return convert_windows_error_code(GetLastError()); + + return __WASI_ESUCCESS; +} + +static HANDLE +create_handle(wchar_t *path, bool is_dir, bool follow_symlink, bool readonly) +{ + CREATEFILE2_EXTENDED_PARAMETERS create_params; + + create_params.dwSize = sizeof(create_params); + create_params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + create_params.dwSecurityQosFlags = 0; + create_params.dwFileFlags = 0; + create_params.lpSecurityAttributes = NULL; + create_params.hTemplateFile = NULL; + + if (is_dir) { + create_params.dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + create_params.dwFileFlags |= FILE_FLAG_BACKUP_SEMANTICS; + } + + if (!follow_symlink) + create_params.dwFileFlags |= FILE_FLAG_OPEN_REPARSE_POINT; + + DWORD desired_access = GENERIC_READ; + + if (!readonly) + desired_access |= GENERIC_WRITE; + else + create_params.dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + + return CreateFile2(path, desired_access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + OPEN_EXISTING, &create_params); +} + +#if WINAPI_PARTITION_DESKTOP == 0 +// Modifies the given path in place and replaces it with the filename component +// (including the extension) of the path. +static __wasi_errno_t +extract_filename_from_path(wchar_t *path, size_t buf_size) +{ + wchar_t extension[256]; + wchar_t filename[256]; + __wasi_errno_t error = __WASI_ESUCCESS; + + // Get the filename from the fullpath. + errno_t ret = + _wsplitpath_s(path, NULL, 0, NULL, 0, filename, 256, extension, 256); + if (ret != 0) { + error = convert_errno(ret); + return error; + } + + ret = wcscat_s(filename, 256, extension); + + if (ret != 0) { + error = convert_errno(ret); + return error; + } + + ret = wcscpy_s(path, buf_size, filename); + + if (ret != 0) + error = convert_errno(ret); + + return error; +} + +static __wasi_errno_t +get_handle_to_parent_directory(HANDLE handle, HANDLE *out_dir_handle) +{ + wchar_t path[PATH_MAX]; + __wasi_errno_t error = get_handle_filepath(handle, path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + wchar_t parent_dir_path[PATH_MAX]; + errno_t ret = wcscpy_s(parent_dir_path, PATH_MAX, path); + + if (ret != 0) { + error = convert_errno(ret); + return error; + } + + ret = wcscat_s(parent_dir_path, PATH_MAX, L"/.."); + + if (ret != 0) { + error = convert_errno(ret); + return error; + } + + HANDLE dir_handle = create_handle(parent_dir_path, true, true, true); + + if (dir_handle == INVALID_HANDLE_VALUE) { + error = convert_windows_error_code(GetLastError()); + return error; + } + + *out_dir_handle = dir_handle; + return error; +} + +// The easiest way to get all the necessary file information for files is to +// open a handle to the parent directory and iterate through the entries via +// FileIdBothDirectoryInfo. Other file information classes are only +// available on desktop. +static __wasi_errno_t +get_disk_file_information(HANDLE handle, __wasi_filestat_t *buf) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + HANDLE raw_dir_handle = INVALID_HANDLE_VALUE; + + wchar_t path[PATH_MAX] = L"."; + + if (buf->st_filetype != __WASI_FILETYPE_DIRECTORY) { + error = get_handle_filepath(handle, path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + goto fail; + + error = get_handle_to_parent_directory(handle, &raw_dir_handle); + + if (error != __WASI_ESUCCESS) + goto fail; + + error = extract_filename_from_path(path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + goto fail; + } + else { + raw_dir_handle = handle; + } + + windows_handle dir_handle = { .access_mode = windows_access_mode_read, + .raw = { .handle = raw_dir_handle }, + .fdflags = 0, + .type = windows_handle_type_file }; + windows_dir_stream dir_stream; + init_dir_stream(&dir_stream, &dir_handle); + + do { + FILE_ID_BOTH_DIR_INFO *file_id_both_dir_info = NULL; + __wasi_errno_t error = + read_next_dir_entry(&dir_stream, &file_id_both_dir_info); + + if (error != __WASI_ESUCCESS || file_id_both_dir_info == NULL) + goto fail; + + const DWORD filename_length = file_id_both_dir_info->FileNameLength + / (sizeof(wchar_t) / sizeof(char)); + + if (wcsncmp(file_id_both_dir_info->FileName, path, filename_length) + == 0) { + buf->st_ino = + (__wasi_inode_t)(file_id_both_dir_info->FileId.QuadPart); + buf->st_atim = convert_filetime_to_wasi_timestamp( + (LPFILETIME)&file_id_both_dir_info->LastAccessTime.QuadPart); + buf->st_mtim = convert_filetime_to_wasi_timestamp( + (LPFILETIME)&file_id_both_dir_info->LastWriteTime.QuadPart); + buf->st_ctim = convert_filetime_to_wasi_timestamp( + (LPFILETIME)&file_id_both_dir_info->ChangeTime.QuadPart); + buf->st_size = + (__wasi_filesize_t)(file_id_both_dir_info->EndOfFile.QuadPart); + + break; + } + } while (dir_stream.cookie != 0); + + FILE_STANDARD_INFO file_standard_info; + + bool success = GetFileInformationByHandleEx(handle, FileStandardInfo, + &file_standard_info, + sizeof(file_standard_info)); + + if (!success) { + error = convert_windows_error_code(GetLastError()); + goto fail; + } + + buf->st_nlink = (__wasi_linkcount_t)file_standard_info.NumberOfLinks; +fail: + if (buf->st_filetype != __WASI_FILETYPE_DIRECTORY + && raw_dir_handle != INVALID_HANDLE_VALUE) + CloseHandle(raw_dir_handle); + + return error; +} + +#else + +static __wasi_errno_t +get_disk_file_information(HANDLE handle, __wasi_filestat_t *buf) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + FILE_BASIC_INFO file_basic_info; + + int ret = GetFileInformationByHandleEx( + handle, FileBasicInfo, &file_basic_info, sizeof(file_basic_info)); + + if (ret == 0) { + error = convert_windows_error_code(GetLastError()); + return error; + } + + buf->st_atim = convert_filetime_to_wasi_timestamp( + (LPFILETIME)&file_basic_info.LastAccessTime.QuadPart); + buf->st_mtim = convert_filetime_to_wasi_timestamp( + (LPFILETIME)&file_basic_info.LastWriteTime.QuadPart); + buf->st_ctim = convert_filetime_to_wasi_timestamp( + (LPFILETIME)&file_basic_info.ChangeTime.QuadPart); + + BY_HANDLE_FILE_INFORMATION file_info; + ret = GetFileInformationByHandle(handle, &file_info); + + if (ret == 0) { + error = convert_windows_error_code(GetLastError()); + return error; + } + + ULARGE_INTEGER file_size = { .LowPart = file_info.nFileSizeLow, + .HighPart = file_info.nFileSizeHigh }; + buf->st_size = (__wasi_filesize_t)(file_size.QuadPart); + + ULARGE_INTEGER file_id = { .LowPart = file_info.nFileIndexLow, + .HighPart = file_info.nFileIndexHigh }; + buf->st_ino = (__wasi_inode_t)(file_id.QuadPart); + + buf->st_dev = (__wasi_device_t)file_info.dwVolumeSerialNumber; + buf->st_nlink = (__wasi_linkcount_t)file_info.nNumberOfLinks; + + return error; +} + +#endif /* end of WINAPI_PARTITION_DESKTOP == 0 */ + +static __wasi_errno_t +get_file_information(os_file_handle handle, __wasi_filestat_t *buf) +{ + __wasi_errno_t error = __WASI_ESUCCESS; + + DWORD windows_filetype = GetFileType(handle->raw.handle); + error = + convert_windows_filetype(handle, windows_filetype, &buf->st_filetype); + + if (error != __WASI_ESUCCESS) + return error; + + buf->st_dev = 0; + + if (windows_filetype != FILE_TYPE_DISK) { + buf->st_atim = 0; + buf->st_ctim = 0; + buf->st_mtim = 0; + buf->st_nlink = 0; + buf->st_size = 0; + buf->st_ino = 0; + + return error; + } + + return get_disk_file_information(handle->raw.handle, buf); +} + +__wasi_errno_t +os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) +{ + CHECK_VALID_HANDLE(handle); + + return get_file_information(handle, buf); +} + +__wasi_errno_t +os_fstatat(os_file_handle handle, const char *path, + struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags) +{ + CHECK_VALID_FILE_HANDLE(handle); + + wchar_t absolute_path[PATH_MAX]; + + __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path, + absolute_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + windows_handle resolved_handle = { + .type = windows_handle_type_file, + .fdflags = 0, + .raw = { .handle = create_handle( + absolute_path, is_directory(absolute_path), + ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0), + true) }, + .access_mode = windows_access_mode_read + }; + + if (resolved_handle.raw.handle == INVALID_HANDLE_VALUE) + return convert_windows_error_code(GetLastError()); + + error = get_file_information(&resolved_handle, buf); + + CloseHandle(resolved_handle.raw.handle); + + return error; +} + +__wasi_errno_t +os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags) +{ + CHECK_VALID_HANDLE(handle); + + *flags = handle->fdflags; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) +{ + CHECK_VALID_HANDLE(handle); + + if (handle->type == windows_handle_type_socket + && (((handle->fdflags ^ flags) & __WASI_FDFLAG_NONBLOCK) != 0)) { + u_long non_block = flags & __WASI_FDFLAG_NONBLOCK; + + int ret = ioctlsocket(handle->raw.socket, (long)FIONBIO, &non_block); + + if (ret != 0) + return convert_winsock_error_code(WSAGetLastError()); + + if (non_block) + handle->fdflags |= __WASI_FDFLAG_NONBLOCK; + else + handle->fdflags &= ~__WASI_FDFLAG_NONBLOCK; + return __WASI_ESUCCESS; + } + + // It's not supported setting FILE_FLAG_WRITE_THROUGH or + // FILE_FLAG_NO_BUFFERING via SetFileAttributes so __WASI_FDFLAG_APPEND is + // the only flags we can do anything with. + if (((handle->fdflags ^ flags) & __WASI_FDFLAG_APPEND) != 0) { + if ((flags & __WASI_FDFLAG_APPEND) != 0) + handle->fdflags |= __WASI_FDFLAG_APPEND; + else + handle->fdflags &= ~__WASI_FDFLAG_APPEND; + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_access_mode(os_file_handle handle, + wasi_libc_file_access_mode *access_mode) +{ + CHECK_VALID_HANDLE(handle); + + if ((handle->access_mode & windows_access_mode_read) != 0 + && (handle->access_mode & windows_access_mode_write) != 0) + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + else if ((handle->access_mode & windows_access_mode_write) != 0) + *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY; + else + *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY; + + return __WASI_ESUCCESS; +} + +static __wasi_errno_t +flush_file_buffers_on_handle(HANDLE handle) +{ + bool success = FlushFileBuffers(handle); + + return success ? __WASI_ESUCCESS + : convert_windows_error_code(GetLastError()); +} + +__wasi_errno_t +os_fdatasync(os_file_handle handle) +{ + CHECK_VALID_FILE_HANDLE(handle); + + return flush_file_buffers_on_handle(handle->raw.handle); +} + +__wasi_errno_t +os_fsync(os_file_handle handle) +{ + CHECK_VALID_FILE_HANDLE(handle); + + return flush_file_buffers_on_handle(handle->raw.handle); +} + +__wasi_errno_t +os_open_preopendir(const char *path, os_file_handle *out) +{ + *out = NULL; + + wchar_t wpath[PATH_MAX]; + __wasi_errno_t error = convert_to_wchar(path, wpath, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + HANDLE dir_handle = create_handle(wpath, true, true, true); + + if (dir_handle == INVALID_HANDLE_VALUE) + return convert_windows_error_code(GetLastError()); + + *out = BH_MALLOC(sizeof(windows_handle)); + + if (*out == NULL) { + CloseHandle(dir_handle); + return __WASI_ENOMEM; + } + + (*out)->type = windows_handle_type_file; + (*out)->raw.handle = dir_handle; + (*out)->fdflags = 0; + (*out)->access_mode = windows_access_mode_read; + + return error; +} + +__wasi_errno_t +os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fs_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode access_mode, os_file_handle *out) +{ + CHECK_VALID_FILE_HANDLE(handle); + *out = BH_MALLOC(sizeof(windows_handle)); + + if (*out == NULL) + return __WASI_ENOMEM; + + (*out)->type = windows_handle_type_file; + (*out)->fdflags = fs_flags; + (*out)->raw.handle = INVALID_HANDLE_VALUE; + + DWORD attributes = FILE_FLAG_BACKUP_SEMANTICS; + + if ((fs_flags & (__WASI_FDFLAG_SYNC | __WASI_FDFLAG_RSYNC)) != 0) + attributes |= (FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING); + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) + attributes |= FILE_FLAG_WRITE_THROUGH; + + if ((oflags & __WASI_O_DIRECTORY) != 0) { + attributes |= FILE_ATTRIBUTE_DIRECTORY; + oflags &= ~(__WASI_O_DIRECTORY); + } + // Use async operations on the handle if it's not a directory + else { + attributes |= FILE_FLAG_OVERLAPPED; + } + + __wasi_errno_t error = __WASI_ESUCCESS; + + DWORD access_flags = 0; + + switch (access_mode) { + case WASI_LIBC_ACCESS_MODE_READ_ONLY: + access_flags |= GENERIC_READ; + (*out)->access_mode = windows_access_mode_read; + break; + case WASI_LIBC_ACCESS_MODE_WRITE_ONLY: + access_flags |= GENERIC_WRITE; + (*out)->access_mode = windows_access_mode_write; + break; + case WASI_LIBC_ACCESS_MODE_READ_WRITE: + access_flags |= GENERIC_WRITE | GENERIC_READ; + (*out)->access_mode = + windows_access_mode_read | windows_access_mode_write; + break; + } + + DWORD creation_disposition = 0; + + switch (oflags) { + case __WASI_O_CREAT | __WASI_O_EXCL: + case __WASI_O_CREAT | __WASI_O_EXCL | __WASI_O_TRUNC: + creation_disposition = CREATE_NEW; + break; + case __WASI_O_CREAT | __WASI_O_TRUNC: + creation_disposition = CREATE_ALWAYS; + break; + case __WASI_O_CREAT: + creation_disposition = OPEN_ALWAYS; + break; + case 0: + case __WASI_O_EXCL: + creation_disposition = OPEN_EXISTING; + break; + case __WASI_O_TRUNC: + case __WASI_O_EXCL | __WASI_O_TRUNC: + creation_disposition = TRUNCATE_EXISTING; + // CreateFile2 requires write access if we truncate the file upon + // opening + access_flags |= GENERIC_WRITE; + break; + } + + wchar_t absolute_path[PATH_MAX]; + error = get_absolute_filepath(handle->raw.handle, path, absolute_path, + PATH_MAX); + + if (error != __WASI_ESUCCESS) + goto fail; + + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) + attributes |= FILE_FLAG_OPEN_REPARSE_POINT; + + // Windows doesn't seem to throw an error for the following cases where the + // file/directory already exists so add explicit checks. + if (creation_disposition == OPEN_EXISTING) { + DWORD file_attributes = GetFileAttributesW(absolute_path); + + if (file_attributes != INVALID_FILE_ATTRIBUTES) { + bool is_dir = file_attributes & FILE_ATTRIBUTE_DIRECTORY; + bool is_symlink = file_attributes & FILE_ATTRIBUTE_REPARSE_POINT; + // Check that we're not trying to open an existing file/symlink as a + // directory. + if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0 + && (!is_dir || is_symlink)) { + error = __WASI_ENOTDIR; + goto fail; + } + + // Check that we're not trying to open an existing symlink with + // O_NOFOLLOW. + if ((file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 + && (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) { + error = __WASI_ELOOP; + goto fail; + } + } + } + + CREATEFILE2_EXTENDED_PARAMETERS create_params; + create_params.dwSize = sizeof(create_params); + create_params.dwFileAttributes = attributes & 0xFFF; + create_params.dwFileFlags = attributes & 0xFFF00000; + create_params.dwSecurityQosFlags = 0; + create_params.lpSecurityAttributes = NULL; + create_params.hTemplateFile = NULL; + + (*out)->raw.handle = + CreateFile2(absolute_path, access_flags, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + creation_disposition, &create_params); + + if ((*out)->raw.handle == INVALID_HANDLE_VALUE) { + error = convert_windows_error_code(GetLastError()); + goto fail; + } + + return error; +fail: + if (*out != NULL) { + if ((*out)->raw.handle != INVALID_HANDLE_VALUE) + CloseHandle((*out)->raw.handle); + + BH_FREE(*out); + } + + return error; +} + +__wasi_errno_t +os_close(os_file_handle handle, bool is_stdio) +{ + CHECK_VALID_HANDLE(handle); + + // We don't own the underlying raw handle so just free the handle and return + // success. + if (is_stdio) { + BH_FREE(handle); + return __WASI_ESUCCESS; + } + + switch (handle->type) { + case windows_handle_type_file: + bool success = CloseHandle(handle->raw.handle); + + if (!success) + return convert_windows_error_code(GetLastError()); + + break; + case windows_handle_type_socket: + int ret = closesocket(handle->raw.socket); + + if (ret != 0) + return convert_winsock_error_code(WSAGetLastError()); + + break; + default: + assert(false && "unreachable"); + } + + BH_FREE(handle); + + return __WASI_ESUCCESS; +} + +static __wasi_errno_t +read_data_at_offset(HANDLE handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + OVERLAPPED *read_operations = + BH_MALLOC((uint32_t)(sizeof(OVERLAPPED) * (uint32_t)iovcnt)); + + if (read_operations == NULL) + return __WASI_ENOMEM; + + ULARGE_INTEGER query_offset = { .QuadPart = offset }; + __wasi_errno_t error = __WASI_ESUCCESS; + size_t total_bytes_read = 0; + + const __wasi_iovec_t *current = iov; + int successful_read_count = 0; + + for (int i = 0; i < iovcnt; ++i, ++current) { + read_operations[i].Internal = 0; + read_operations[i].InternalHigh = 0; + read_operations[i].Offset = query_offset.LowPart; + read_operations[i].OffsetHigh = query_offset.HighPart; + read_operations[i].hEvent = NULL; + + if (!ReadFileEx(handle, current->buf, (DWORD)current->buf_len, + &read_operations[i], NULL)) { + DWORD win_error = GetLastError(); + if (win_error != ERROR_IO_PENDING) { + error = convert_windows_error_code(win_error); + break; + } + } + ++successful_read_count; + query_offset.QuadPart += (DWORD)current->buf_len; + } + + // Get the result of all the asynchronous read operations + for (int i = 0; i < successful_read_count; ++i) { + DWORD bytes_transferred = 0; + if (!GetOverlappedResult(handle, &read_operations[i], + &bytes_transferred, true)) { + DWORD win_error = GetLastError(); + + if (win_error != ERROR_HANDLE_EOF) + error = convert_windows_error_code(win_error); + else + total_bytes_read += (size_t)bytes_transferred; + + CancelIo(handle); + + for (int j = i + 1; j < iovcnt; ++j) { + GetOverlappedResult(handle, &read_operations[j], + &bytes_transferred, true); + } + break; + } + + total_bytes_read += (size_t)bytes_transferred; + } + + *nwritten = total_bytes_read; + + BH_FREE(read_operations); + return error; +} + +__wasi_errno_t +os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ + CHECK_VALID_FILE_HANDLE(handle); + + return read_data_at_offset(handle->raw.handle, iov, iovcnt, offset, nread); +} + +__wasi_errno_t +os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + size_t *nread) +{ + CHECK_VALID_HANDLE(handle); + + LARGE_INTEGER current_offset = { .QuadPart = 0 }; + + // Seek to the current offset before reading + int ret = SetFilePointerEx(handle->raw.handle, current_offset, + ¤t_offset, FILE_CURRENT); + if (ret == 0) + return convert_windows_error_code(GetLastError()); + + __wasi_errno_t error = + read_data_at_offset(handle->raw.handle, iov, iovcnt, + (__wasi_filesize_t)current_offset.QuadPart, nread); + + if (error != __WASI_ESUCCESS) + return error; + + current_offset.QuadPart += (LONGLONG)(*nread); + + // Update the current offset to match how many bytes we've read + ret = + SetFilePointerEx(handle->raw.handle, current_offset, NULL, FILE_BEGIN); + + if (ret == 0) + error = convert_windows_error_code(GetLastError()); + + return error; +} + +static __wasi_errno_t +write_data_at_offset(HANDLE handle, const struct __wasi_ciovec_t *iov, + int iovcnt, __wasi_filesize_t offset, size_t *nwritten) +{ + OVERLAPPED *write_operations = + BH_MALLOC((uint32_t)(sizeof(OVERLAPPED) * (uint32_t)iovcnt)); + + if (write_operations == NULL) + return __WASI_ENOMEM; + + ULARGE_INTEGER query_offset = { .QuadPart = offset }; + __wasi_errno_t error = __WASI_ESUCCESS; + size_t total_bytes_written = 0; + + const __wasi_ciovec_t *current = iov; + int successful_write_count = 0; + for (int i = 0; i < iovcnt; ++i, ++current) { + write_operations[i].Internal = 0; + write_operations[i].InternalHigh = 0; + write_operations[i].Offset = query_offset.LowPart; + write_operations[i].OffsetHigh = query_offset.HighPart; + write_operations[i].hEvent = NULL; + + if (!WriteFileEx(handle, current->buf, (DWORD)current->buf_len, + &write_operations[i], NULL)) { + DWORD win_error = GetLastError(); + if (win_error != ERROR_IO_PENDING) { + error = convert_windows_error_code(win_error); + break; + } + } + ++successful_write_count; + query_offset.QuadPart += (DWORD)current->buf_len; + } + + // Get the result of all the asynchronous writes + for (int i = 0; i < successful_write_count; ++i) { + DWORD bytes_transferred = 0; + if (!GetOverlappedResult(handle, &write_operations[i], + &bytes_transferred, true)) { + error = convert_windows_error_code(GetLastError()); + CancelIo(handle); + + for (int j = i + 1; j < iovcnt; ++j) { + GetOverlappedResult(handle, &write_operations[j], + &bytes_transferred, true); + } + break; + } + + total_bytes_written += (size_t)bytes_transferred; + } + + *nwritten = total_bytes_written; + + BH_FREE(write_operations); + return error; +} + +__wasi_errno_t +os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + CHECK_VALID_FILE_HANDLE(handle); + + return write_data_at_offset(handle->raw.handle, iov, iovcnt, offset, + nwritten); +} + +__wasi_errno_t +os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten) +{ + CHECK_VALID_HANDLE(handle); + + bool append = (handle->fdflags & __WASI_FDFLAG_APPEND) != 0; + LARGE_INTEGER write_offset = { .QuadPart = 0 }; + DWORD move_method = append ? FILE_END : FILE_CURRENT; + + int ret = SetFilePointerEx(handle->raw.handle, write_offset, &write_offset, + move_method); + if (ret == 0) + return convert_windows_error_code(GetLastError()); + + __wasi_errno_t error = write_data_at_offset( + handle->raw.handle, iov, iovcnt, + (__wasi_filesize_t)write_offset.QuadPart, nwritten); + + if (error != __WASI_ESUCCESS) + return error; + + write_offset.QuadPart += (LONGLONG)(*nwritten); + + // Update the write offset to match how many bytes we've written + ret = SetFilePointerEx(handle->raw.handle, write_offset, NULL, FILE_BEGIN); + + if (ret == 0) + error = convert_windows_error_code(GetLastError()); + + return error; +} + +__wasi_errno_t +os_fallocate(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length) +{ + CHECK_VALID_FILE_HANDLE(handle); + + LARGE_INTEGER current_file_size; + int ret = GetFileSizeEx(handle->raw.handle, ¤t_file_size); + + if (ret == 0) + return convert_windows_error_code(GetLastError()); + + if (offset > INT64_MAX || length > INT64_MAX || offset + length > INT64_MAX) + return __WASI_EINVAL; + + // The best we can do here is to increase the size of the file if it's less + // than the offset + length. + const LONGLONG requested_size = (LONGLONG)(offset + length); + + FILE_END_OF_FILE_INFO end_of_file_info; + end_of_file_info.EndOfFile.QuadPart = requested_size; + + if (requested_size <= current_file_size.QuadPart) + return __WASI_ESUCCESS; + + bool success = + SetFileInformationByHandle(handle->raw.handle, FileEndOfFileInfo, + &end_of_file_info, sizeof(end_of_file_info)); + + return success ? __WASI_ESUCCESS + : convert_windows_error_code(GetLastError()); +} + +__wasi_errno_t +os_ftruncate(os_file_handle handle, __wasi_filesize_t size) +{ + CHECK_VALID_FILE_HANDLE(handle); + + FILE_END_OF_FILE_INFO end_of_file_info; + end_of_file_info.EndOfFile.QuadPart = (LONGLONG)size; + + bool success = + SetFileInformationByHandle(handle->raw.handle, FileEndOfFileInfo, + &end_of_file_info, sizeof(end_of_file_info)); + + return success ? __WASI_ESUCCESS + : convert_windows_error_code(GetLastError()); +} + +static __wasi_errno_t +set_file_times(HANDLE handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags) +{ + FILETIME atim = { 0, 0 }; + FILETIME mtim = { 0, 0 }; + + if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { + atim = convert_wasi_timestamp_to_filetime(access_time); + } + else if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { + GetSystemTimePreciseAsFileTime(&atim); + } + + if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { + mtim = convert_wasi_timestamp_to_filetime(modification_time); + } + else if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { + GetSystemTimePreciseAsFileTime(&mtim); + } + + bool success = SetFileTime(handle, NULL, &atim, &mtim); + + return success ? __WASI_ESUCCESS + : convert_windows_error_code(GetLastError()); +} + +__wasi_errno_t +os_futimens(os_file_handle handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags) +{ + CHECK_VALID_FILE_HANDLE(handle); + + return set_file_times(handle->raw.handle, access_time, modification_time, + fstflags); +} + +__wasi_errno_t +os_utimensat(os_file_handle handle, const char *path, + __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags, + __wasi_lookupflags_t lookup_flags) +{ + CHECK_VALID_FILE_HANDLE(handle); + + wchar_t absolute_path[PATH_MAX]; + __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path, + absolute_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + HANDLE resolved_handle = create_handle( + absolute_path, is_directory(absolute_path), + (lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0, false); + + if (resolved_handle == INVALID_HANDLE_VALUE) + return convert_windows_error_code(GetLastError()); + + error = set_file_times(resolved_handle, access_time, modification_time, + fstflags); + + CloseHandle(resolved_handle); + + return error; +} + +__wasi_errno_t +os_readlinkat(os_file_handle handle, const char *path, char *buf, + size_t bufsize, size_t *nread) +{ + CHECK_VALID_FILE_HANDLE(handle); + + wchar_t symlink_path[PATH_MAX]; + __wasi_errno_t error = + get_absolute_filepath(handle->raw.handle, path, symlink_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + DWORD symlink_attributes = GetFileAttributesW(symlink_path); + + if (!has_symlink_attribute(symlink_attributes)) + return __WASI_EINVAL; + + HANDLE link_handle = create_handle( + symlink_path, has_directory_attribute(symlink_attributes), false, true); + + if (link_handle == INVALID_HANDLE_VALUE) + return convert_windows_error_code(GetLastError()); + +#if WINAPI_PARTITION_DESKTOP != 0 +// MinGW32 already has a definition for REPARSE_DATA_BUFFER +#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR) + // See + // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_reparse_data_buffer + // for more details. + typedef struct _REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; + } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +#endif + + char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + + REPARSE_DATA_BUFFER *reparse_data = (REPARSE_DATA_BUFFER *)buffer; + + if (!DeviceIoControl(link_handle, FSCTL_GET_REPARSE_POINT, NULL, 0, &buffer, + sizeof(buffer), NULL, NULL)) { + error = convert_windows_error_code(GetLastError()); + goto fail; + } + + int wbufsize = 0; + wchar_t *wbuf = NULL; + + // The following checks are taken from the libuv windows filesystem + // implementation, + // https://github.com/libuv/libuv/blob/v1.x/src/win/fs.c#L181-L244. Real + // symlinks can contain pretty much anything, but the only thing we really + // care about is undoing the implicit conversion to an NT namespaced path + // that CreateSymbolicLink will perform on absolute paths. + if (reparse_data->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + wbuf = reparse_data->SymbolicLinkReparseBuffer.PathBuffer + + (reparse_data->SymbolicLinkReparseBuffer.SubstituteNameOffset + / sizeof(wchar_t)); + wbufsize = reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength + / sizeof(wchar_t); + + if (wbufsize >= 4 && wbuf[0] == L'\\' && wbuf[1] == L'?' + && wbuf[2] == L'?' && wbuf[3] == L'\\') { + // Starts with \??\ + if (wbufsize >= 6 + && ((wbuf[4] >= L'A' && wbuf[4] <= L'Z') + || (wbuf[4] >= L'a' && wbuf[4] <= L'z')) + && wbuf[5] == L':' && (wbufsize == 6 || wbuf[6] == L'\\')) + { + // \??\:\ + wbuf += 4; + wbufsize -= 4; + } + else if (wbufsize >= 8 && (wbuf[4] == L'U' || wbuf[4] == L'u') + && (wbuf[5] == L'N' || wbuf[5] == L'n') + && (wbuf[6] == L'C' || wbuf[6] == L'c') + && wbuf[7] == L'\\') + { + // \??\UNC\\\ - make sure the final path looks like \\\\ + wbuf += 6; + wbuf[0] = L'\\'; + wbufsize -= 6; + } + } + } + else if (reparse_data->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) { + // Junction + wbuf = reparse_data->MountPointReparseBuffer.PathBuffer + + (reparse_data->MountPointReparseBuffer.SubstituteNameOffset + / sizeof(wchar_t)); + wbufsize = reparse_data->MountPointReparseBuffer.SubstituteNameLength + / sizeof(wchar_t); + + // Only treat junctions that look like \??\:\ as a symlink. + if (!(wbufsize >= 6 && wbuf[0] == L'\\' && wbuf[1] == L'?' + && wbuf[2] == L'?' && wbuf[3] == L'\\' + && ((wbuf[4] >= L'A' && wbuf[4] <= L'Z') + || (wbuf[4] >= L'a' && wbuf[4] <= L'z')) + && wbuf[5] == L':' && (wbufsize == 6 || wbuf[6] == L'\\'))) { + error = __WASI_EINVAL; + goto fail; + } + + /* Remove leading \??\ */ + wbuf += 4; + wbufsize -= 4; + } + else { + error = __WASI_EINVAL; + goto fail; + } + + if (wbuf != NULL) + *nread = (size_t)WideCharToMultiByte(CP_UTF8, 0, wbuf, wbufsize, buf, + (int)bufsize, NULL, NULL); + + if (*nread == 0 && wbuf != NULL) { + DWORD win_error = GetLastError(); + if (win_error == ERROR_INSUFFICIENT_BUFFER) + *nread = bufsize; + else + error = convert_windows_error_code(win_error); + } +#else + error = __WASI_ENOTSUP; +#endif /* end of WINAPI_PARTITION_DESKTOP == 0 */ +fail: + CloseHandle(link_handle); + return error; +} + +__wasi_errno_t +os_linkat(os_file_handle from_handle, const char *from_path, + os_file_handle to_handle, const char *to_path, + __wasi_lookupflags_t lookup_flags) +{ +#if WINAPI_PARTITION_DESKTOP == 0 + return __WASI_ENOSYS; +#else + CHECK_VALID_FILE_HANDLE(from_handle); + CHECK_VALID_FILE_HANDLE(to_handle); + + wchar_t absolute_from_path[PATH_MAX]; + __wasi_errno_t error = get_absolute_filepath( + from_handle->raw.handle, from_path, absolute_from_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + wchar_t absolute_to_path[PATH_MAX]; + error = get_absolute_filepath(to_handle->raw.handle, to_path, + absolute_to_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + size_t to_path_len = strlen(to_path); + + // Windows doesn't throw an error in the case that the new path has a + // trailing slash but the target to link to is a file. + if (to_path[to_path_len - 1] == '/' + || to_path[to_path_len - 1] == '\\' + && !is_directory(absolute_from_path)) { + return __WASI_ENOENT; + } + + int ret = CreateHardLinkW(absolute_to_path, absolute_from_path, NULL); + + if (ret == 0) + error = convert_windows_error_code(GetLastError()); + + return error; +#endif /* end of WINAPI_PARTITION_DESKTOP == 0 */ +} + +__wasi_errno_t +os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path) +{ +#if WINAPI_PARTITION_DESKTOP == 0 + return __WASI_ENOSYS; +#else + CHECK_VALID_FILE_HANDLE(handle); + + wchar_t absolute_new_path[PATH_MAX]; + __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, new_path, + absolute_new_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + DWORD target_type = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + + wchar_t old_wpath[PATH_MAX]; + size_t old_path_len = 0; + + error = convert_to_wchar(old_path, old_wpath, PATH_MAX); + + if (error != __WASI_ESUCCESS) + goto fail; + + wchar_t absolute_old_path[PATH_MAX]; + error = get_absolute_filepath(handle->raw.handle, old_path, + absolute_old_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + goto fail; + + if (is_directory(absolute_old_path)) + target_type |= SYMBOLIC_LINK_FLAG_DIRECTORY; + + bool success = + CreateSymbolicLinkW(absolute_new_path, old_wpath, target_type); + + if (!success) { + DWORD win_error = GetLastError(); + + // Return a more useful error code if a file/directory already exists at + // the symlink location. + if (win_error == ERROR_ACCESS_DENIED || win_error == ERROR_INVALID_NAME) + error = __WASI_ENOENT; + else + error = convert_windows_error_code(GetLastError()); + } +fail: + return error; +#endif /* end of WINAPI_PARTITION_DESKTOP == 0 */ +} + +__wasi_errno_t +os_mkdirat(os_file_handle handle, const char *path) +{ + CHECK_VALID_FILE_HANDLE(handle); + + wchar_t absolute_path[PATH_MAX]; + __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path, + absolute_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + bool success = CreateDirectoryW(absolute_path, NULL); + + if (!success) + error = convert_windows_error_code(GetLastError()); + + return error; +} + +__wasi_errno_t +os_renameat(os_file_handle old_handle, const char *old_path, + os_file_handle new_handle, const char *new_path) +{ + CHECK_VALID_FILE_HANDLE(old_handle); + CHECK_VALID_FILE_HANDLE(new_handle); + + wchar_t old_absolute_path[PATH_MAX]; + __wasi_errno_t error = get_absolute_filepath( + old_handle->raw.handle, old_path, old_absolute_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + wchar_t new_absolute_path[PATH_MAX]; + error = get_absolute_filepath(new_handle->raw.handle, new_path, + new_absolute_path, PATH_MAX); + + if (error != __WASI_ESUCCESS) + return error; + + int ret = MoveFileExW(old_absolute_path, new_absolute_path, + MOVEFILE_REPLACE_EXISTING); + if (ret == 0) + error = convert_windows_error_code(GetLastError()); + + return error; +} + +__wasi_errno_t +os_isatty(os_file_handle handle) +{ + CHECK_VALID_HANDLE(handle); + + DWORD console_mode; + return GetConsoleMode(handle->raw.handle, &console_mode) ? __WASI_ESUCCESS + : __WASI_ENOTTY; +} + +static os_file_handle +create_stdio_handle(HANDLE raw_stdio_handle, DWORD stdio) +{ + os_file_handle stdio_handle = BH_MALLOC(sizeof(windows_handle)); + + if (stdio_handle == NULL) + return NULL; + + stdio_handle->type = windows_handle_type_file; + stdio_handle->access_mode = + windows_access_mode_read | windows_access_mode_write; + stdio_handle->fdflags = 0; + + if (raw_stdio_handle == INVALID_HANDLE_VALUE) + raw_stdio_handle = GetStdHandle(stdio); + + stdio_handle->raw.handle = raw_stdio_handle; + + return stdio_handle; +} + +bool +os_is_stdin_handle(os_file_handle fd) +{ + return fd->raw.handle == GetStdHandle(STD_INPUT_HANDLE); +} + +bool +os_is_stdout_handle(os_file_handle fd) +{ + return fd->raw.handle == GetStdHandle(STD_OUTPUT_HANDLE); +} + +bool +os_is_stderr_handle(os_file_handle fd) +{ + return fd->raw.handle == GetStdHandle(STD_ERROR_HANDLE); +} + +os_file_handle +os_convert_stdin_handle(os_raw_file_handle raw_stdin) +{ + return create_stdio_handle(raw_stdin, STD_INPUT_HANDLE); +} + +os_file_handle +os_convert_stdout_handle(os_raw_file_handle raw_stdout) +{ + return create_stdio_handle(raw_stdout, STD_OUTPUT_HANDLE); +} + +os_file_handle +os_convert_stderr_handle(os_raw_file_handle raw_stderr) +{ + return create_stdio_handle(raw_stderr, STD_ERROR_HANDLE); +} + +__wasi_errno_t +os_unlinkat(os_file_handle handle, const char *path, bool is_dir) +{ + CHECK_VALID_FILE_HANDLE(handle); + + wchar_t absolute_path[PATH_MAX]; + __wasi_errno_t error = get_absolute_filepath(handle->raw.handle, path, + absolute_path, PATH_MAX); + + DWORD attributes = GetFileAttributesW(absolute_path); + + if (attributes != INVALID_FILE_ATTRIBUTES + && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { + // Override is_dir for symlinks. A symlink to a directory counts as a + // directory itself in Windows. + is_dir = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + } + + if (error != __WASI_ESUCCESS) + return error; + + int ret = + is_dir ? RemoveDirectoryW(absolute_path) : DeleteFileW(absolute_path); + + if (ret == 0) + error = convert_windows_error_code(GetLastError()); + + return error; +} + +__wasi_errno_t +os_lseek(os_file_handle handle, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *new_offset) +{ + CHECK_VALID_FILE_HANDLE(handle); + DWORD sys_whence = 0; + + switch (whence) { + case __WASI_WHENCE_SET: + sys_whence = FILE_BEGIN; + break; + case __WASI_WHENCE_END: + sys_whence = FILE_END; + break; + case __WASI_WHENCE_CUR: + sys_whence = FILE_CURRENT; + break; + default: + return __WASI_EINVAL; + } + + LARGE_INTEGER distance_to_move = { .QuadPart = offset }; + LARGE_INTEGER updated_offset = { .QuadPart = 0 }; + + int ret = SetFilePointerEx(handle->raw.handle, distance_to_move, + &updated_offset, sys_whence); + + if (ret == 0) + return convert_windows_error_code(GetLastError()); + + *new_offset = (__wasi_filesize_t)updated_offset.QuadPart; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fadvise(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length, __wasi_advice_t advice) +{ + CHECK_VALID_FILE_HANDLE(handle); + // Advisory information can be safely ignored if not supported + switch (advice) { + case __WASI_ADVICE_DONTNEED: + case __WASI_ADVICE_NOREUSE: + case __WASI_ADVICE_NORMAL: + case __WASI_ADVICE_RANDOM: + case __WASI_ADVICE_SEQUENTIAL: + case __WASI_ADVICE_WILLNEED: + return __WASI_ESUCCESS; + default: + return __WASI_EINVAL; + } +} + +__wasi_errno_t +os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream) +{ + CHECK_VALID_FILE_HANDLE(handle); + + // Check the handle is a directory handle first + DWORD windows_filetype = GetFileType(handle->raw.handle); + + __wasi_filetype_t filetype = __WASI_FILETYPE_UNKNOWN; + __wasi_errno_t error = + convert_windows_filetype(handle, windows_filetype, &filetype); + + if (error != __WASI_ESUCCESS) + return error; + + if (filetype != __WASI_FILETYPE_DIRECTORY) + return __WASI_ENOTDIR; + + *dir_stream = BH_MALLOC(sizeof(windows_dir_stream)); + + if (*dir_stream == NULL) + return __WASI_ENOMEM; + + init_dir_stream(*dir_stream, handle); + + return error; +} + +__wasi_errno_t +os_rewinddir(os_dir_stream dir_stream) +{ + CHECK_VALID_WIN_DIR_STREAM(dir_stream); + + reset_dir_stream(dir_stream); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position) +{ + CHECK_VALID_WIN_DIR_STREAM(dir_stream); + + if (dir_stream->cookie == position) + return __WASI_ESUCCESS; + + if (dir_stream->cookie > position) { + reset_dir_stream(dir_stream); + } + + while (dir_stream->cookie < position) { + __wasi_errno_t error = read_next_dir_entry(dir_stream, NULL); + + if (error != __WASI_ESUCCESS) + return error; + + // We've reached the end of the directory. + if (dir_stream->cookie == 0) { + break; + } + } + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, + const char **d_name) +{ + CHECK_VALID_WIN_DIR_STREAM(dir_stream); + + FILE_ID_BOTH_DIR_INFO *file_id_both_dir_info = NULL; + + __wasi_errno_t error = + read_next_dir_entry(dir_stream, &file_id_both_dir_info); + + if (error != __WASI_ESUCCESS || file_id_both_dir_info == NULL) + return error; + + entry->d_ino = (__wasi_inode_t)file_id_both_dir_info->FileId.QuadPart; + entry->d_namlen = (__wasi_dirnamlen_t)(file_id_both_dir_info->FileNameLength + / (sizeof(wchar_t) / sizeof(char))); + entry->d_next = (__wasi_dircookie_t)dir_stream->cookie; + entry->d_type = get_disk_filetype(file_id_both_dir_info->FileAttributes); + + *d_name = dir_stream->current_entry_name; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_closedir(os_dir_stream dir_stream) +{ + CHECK_VALID_WIN_DIR_STREAM(dir_stream); + + bool success = CloseHandle(dir_stream->handle->raw.handle); + + if (!success) { + DWORD win_error = GetLastError(); + + if (win_error == ERROR_INVALID_HANDLE) + BH_FREE(dir_stream); + return convert_windows_error_code(win_error); + } + + BH_FREE(dir_stream); + + return __WASI_ESUCCESS; +} + +os_dir_stream +os_get_invalid_dir_stream() +{ + return NULL; +} + +bool +os_is_dir_stream_valid(os_dir_stream *dir_stream) +{ + assert(dir_stream != NULL); + + if (((*dir_stream) == NULL) || ((*dir_stream)->handle == NULL) + || ((*dir_stream)->handle->type != windows_handle_type_file) + || ((*dir_stream)->handle->raw.handle == INVALID_HANDLE_VALUE)) + return false; + + return true; +} + +bool +os_is_handle_valid(os_file_handle *handle) +{ + assert(handle != NULL); + + CHECK_VALID_HANDLE_WITH_RETURN_VALUE(*handle, false); + + return true; +} + +char * +os_realpath(const char *path, char *resolved_path) +{ + resolved_path = _fullpath(resolved_path, path, PATH_MAX); + + // Check the file/directory actually exists + DWORD attributes = GetFileAttributesA(resolved_path); + + if (attributes == INVALID_FILE_ATTRIBUTES) + return NULL; + + return resolved_path; +} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return INVALID_HANDLE_VALUE; +} + +bool +os_compare_file_handle(os_file_handle handle1, os_file_handle handle2) +{ + if (handle1->type != handle2->type) { + return false; + } + + if (handle1->fdflags != handle2->fdflags + || handle1->access_mode != handle2->access_mode) { + return false; + } + + switch (handle1->type) { + case windows_handle_type_file: + return handle1->raw.handle == handle2->raw.handle; + case windows_handle_type_socket: + return handle1->raw.socket == handle2->raw.socket; + default: + // Unknown handle type + return false; + } +} + +int +os_ioctl(os_file_handle handle, int request, ...) +{ + return BHT_ERROR; +} + +// Should not be called because locked by ifdef. +int +os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout) +{ + return BHT_ERROR; +} diff --git a/core/shared/platform/windows/win_malloc.c b/core/shared/platform/windows/win_malloc.c new file mode 100644 index 0000000000..56aaf9c7b3 --- /dev/null +++ b/core/shared/platform/windows/win_malloc.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} \ No newline at end of file diff --git a/core/shared/platform/windows/win_memmap.c b/core/shared/platform/windows/win_memmap.c new file mode 100644 index 0000000000..994c3e4e2e --- /dev/null +++ b/core/shared/platform/windows/win_memmap.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#define TRACE_MEMMAP 0 + +static DWORD +access_to_win32_flags(int prot) +{ + DWORD protect = PAGE_NOACCESS; + + if (prot & MMAP_PROT_EXEC) { + if (prot & MMAP_PROT_WRITE) + protect = PAGE_EXECUTE_READWRITE; + else + protect = PAGE_EXECUTE_READ; + } + else if (prot & MMAP_PROT_WRITE) { + protect = PAGE_READWRITE; + } + else if (prot & MMAP_PROT_READ) { + protect = PAGE_READONLY; + } + + return protect; +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + DWORD alloc_type = MEM_RESERVE; + DWORD protect; + size_t request_size, page_size; + void *addr; + + page_size = os_getpagesize(); + request_size = (size + page_size - 1) & ~(page_size - 1); + + if (request_size < size) { + printf("mmap failed: request size overflow due to paging\n"); + return NULL; + } + +#if WASM_ENABLE_JIT != 0 + /** + * Allocate memory at the highest possible address if the + * request size is large, or LLVM JIT might report error: + * IMAGE_REL_AMD64_ADDR32NB relocation requires an ordered + * section layout. + */ + if (request_size > 10 * BH_MB) + alloc_type |= MEM_TOP_DOWN; +#endif + + protect = access_to_win32_flags(prot); + if (protect != PAGE_NOACCESS) { + alloc_type |= MEM_COMMIT; + } + + addr = VirtualAlloc((LPVOID)hint, request_size, alloc_type, protect); + +#if TRACE_MEMMAP != 0 + printf("Map memory, request_size: %zu, alloc_type: 0x%x, " + "protect: 0x%x, ret: %p\n", + request_size, alloc_type, protect, addr); +#endif + return addr; +} + +void +os_munmap(void *addr, size_t size) +{ + size_t page_size = os_getpagesize(); + size_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (addr) { + if (!VirtualFree(addr, request_size, MEM_DECOMMIT)) { + printf("warning: os_munmap decommit pages failed, " + "addr: %p, request_size: %zu, errno: %d\n", + addr, request_size, errno); + return; + } + + if (!VirtualFree(addr, 0, MEM_RELEASE)) { + printf("warning: os_munmap release pages failed, " + "addr: %p, size: %zu, errno:%d\n", + addr, request_size, errno); + } + } +#if TRACE_MEMMAP != 0 + printf("Unmap memory, addr: %p, request_size: %zu\n", addr, request_size); +#endif +} + +void * +os_mem_commit(void *addr, size_t size, int flags) +{ + DWORD protect = access_to_win32_flags(flags); + size_t page_size = os_getpagesize(); + size_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (!addr) + return NULL; + +#if TRACE_MEMMAP != 0 + printf("Commit memory, addr: %p, request_size: %zu, protect: 0x%x\n", addr, + request_size, protect); +#endif + return VirtualAlloc((LPVOID)addr, request_size, MEM_COMMIT, protect); +} + +void +os_mem_decommit(void *addr, size_t size) +{ + size_t page_size = os_getpagesize(); + size_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (!addr) + return; + +#if TRACE_MEMMAP != 0 + printf("Decommit memory, addr: %p, request_size: %zu\n", addr, + request_size); +#endif + VirtualFree((LPVOID)addr, request_size, MEM_DECOMMIT); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + DWORD protect; + size_t page_size = os_getpagesize(); + size_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (!addr) + return 0; + + protect = access_to_win32_flags(prot); +#if TRACE_MEMMAP != 0 + printf("Mprotect memory, addr: %p, request_size: %zu, protect: 0x%x\n", + addr, request_size, protect); +#endif + return VirtualProtect((LPVOID)addr, request_size, protect, NULL); +} diff --git a/core/shared/platform/windows/win_socket.c b/core/shared/platform/windows/win_socket.c new file mode 100644 index 0000000000..8d61c45ccc --- /dev/null +++ b/core/shared/platform/windows/win_socket.c @@ -0,0 +1,705 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +#include "platform_wasi_types.h" +#include "win_util.h" + +/* link with Ws2_32.lib */ +#pragma comment(lib, "ws2_32.lib") + +static bool is_winsock_inited = false; + +#define CHECK_VALID_SOCKET_HANDLE(win_handle) \ + do { \ + if ((win_handle) == NULL) { \ + errno = EBADF; \ + return BHT_ERROR; \ + } \ + if ((win_handle)->type != windows_handle_type_socket) { \ + errno = ENOTSOCK; \ + return BHT_ERROR; \ + } \ + if ((win_handle)->raw.socket == INVALID_SOCKET) { \ + errno = EBADF; \ + return BHT_ERROR; \ + } \ + } while (0) + +int +init_winsock() +{ +#if WASM_ENABLE_HOST_SOCKET_INIT == 0 + WSADATA wsaData; + + if (!is_winsock_inited) { + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + os_printf("winsock init failed"); + return BHT_ERROR; + } + + is_winsock_inited = true; + } +#endif + + return BHT_OK; +} + +void +deinit_winsock() +{ +#if WASM_ENABLE_HOST_SOCKET_INIT == 0 + if (is_winsock_inited) { + WSACleanup(); + } +#endif +} + +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + int af; + + if (!sock) { + return BHT_ERROR; + } + + *(sock) = BH_MALLOC(sizeof(windows_handle)); + + if ((*sock) == NULL) { + errno = ENOMEM; + return BHT_ERROR; + } + + (*sock)->type = windows_handle_type_socket; + (*sock)->access_mode = windows_access_mode_read | windows_access_mode_write; + (*sock)->fdflags = 0; + + if (is_ipv4) { + af = AF_INET; + } + else { + errno = ENOSYS; + return BHT_ERROR; + } + + if (is_tcp) { + (*sock)->raw.socket = socket(af, SOCK_STREAM, IPPROTO_TCP); + } + else { + (*sock)->raw.socket = socket(af, SOCK_DGRAM, 0); + } + + if ((*sock)->raw.socket == INVALID_SOCKET) { + BH_FREE(*sock); + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + struct sockaddr_in addr; + int socklen, ret; + + assert(host); + assert(port); + + addr.sin_addr.s_addr = inet_addr(host); + addr.sin_port = htons(*port); + addr.sin_family = AF_INET; + + ret = bind(socket->raw.socket, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + goto fail; + } + + socklen = sizeof(addr); + if (getsockname(socket->raw.socket, (void *)&addr, &socklen) == -1) { + os_printf("getsockname failed with error %d\n", WSAGetLastError()); + goto fail; + } + + *port = ntohs(addr.sin_port); + + return BHT_OK; + +fail: + return BHT_ERROR; +} + +int +os_socket_settimeout(bh_socket_t socket, uint64 timeout_us) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + DWORD tv = (DWORD)(timeout_us / 1000UL); + + if (setsockopt(socket->raw.socket, SOL_SOCKET, SO_RCVTIMEO, + (const char *)&tv, sizeof(tv)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + if (listen(socket->raw.socket, max_client) != 0) { + os_printf("socket listen failed with error %d\n", WSAGetLastError()); + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) +{ + CHECK_VALID_SOCKET_HANDLE(server_sock); + + struct sockaddr addr_tmp; + unsigned int len = sizeof(struct sockaddr); + + *sock = BH_MALLOC(sizeof(windows_handle)); + + if (*sock == NULL) { + errno = ENOMEM; + return BHT_ERROR; + } + + (*sock)->type = windows_handle_type_socket; + (*sock)->access_mode = windows_access_mode_read | windows_access_mode_write; + (*sock)->fdflags = 0; + (*sock)->raw.socket = accept(server_sock->raw.socket, + (struct sockaddr *)&addr_tmp, (int *)&len); + + if ((*sock)->raw.socket == INVALID_SOCKET) { + BH_FREE(*sock); + os_printf("socket accept failed with error %d\n", WSAGetLastError()); + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + return recv(socket->raw.socket, buf, len, 0); +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + return send(socket->raw.socket, buf, len, 0); +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_close(bh_socket_t socket) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + closesocket(socket->raw.socket); + + BH_FREE(socket); + + return BHT_OK; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + if (shutdown(socket->raw.socket, SD_BOTH) != 0) { + return convert_winsock_error_code(WSAGetLastError()); + } + return __WASI_ESUCCESS; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + if (!cp) + return BHT_ERROR; + + if (is_ipv4) { + if (inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { + if (inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } + } + + return BHT_OK; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32 time_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32 *time_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32 time_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32 *time_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool option) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *option) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + CHECK_VALID_SOCKET_HANDLE(socket); + + errno = ENOSYS; + return BHT_ERROR; +} diff --git a/core/shared/platform/windows/win_thread.c b/core/shared/platform/windows/win_thread.c new file mode 100644 index 0000000000..1f6a57ebbf --- /dev/null +++ b/core/shared/platform/windows/win_thread.c @@ -0,0 +1,870 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#define bh_assert(v) assert(v) + +#define BH_SEM_COUNT_MAX 0xFFFF + +struct os_thread_data; + +typedef struct os_thread_wait_node { + korp_sem sem; + void *retval; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Next thread data */ + struct os_thread_data *next; + /* Thread data of parent thread */ + struct os_thread_data *parent; + /* Thread Id */ + DWORD thread_id; + /* Thread start routine */ + thread_start_routine_t start_routine; + /* Thread start routine argument */ + void *arg; + /* Wait node of current thread */ + os_thread_wait_node wait_node; + /* Wait cond */ + korp_cond wait_cond; + /* Wait lock */ + 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 */ + void *thread_retval; +} os_thread_data; + +static bool is_thread_sys_inited = false; + +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Thread data list lock */ +static korp_mutex thread_data_list_lock; + +/* Thread data key */ +static DWORD thread_data_key; + +/* The GetCurrentThreadStackLimits API from "kernel32" */ +static void(WINAPI *GetCurrentThreadStackLimits_Kernel32)(PULONG_PTR, + PULONG_PTR) = NULL; + +int +os_sem_init(korp_sem *sem); +int +os_sem_destroy(korp_sem *sem); +int +os_sem_wait(korp_sem *sem); +int +os_sem_reltimed_wait(korp_sem *sem, uint64 useconds); +int +os_sem_signal(korp_sem *sem); + +static void +thread_data_list_add(os_thread_data *thread_data) +{ + os_mutex_lock(&thread_data_list_lock); + /* If already in list, just return */ + os_thread_data *p = &supervisor_thread_data; + while (p) { + if (p == thread_data) { + os_mutex_unlock(&thread_data_list_lock); + return; + } + p = p->next; + } + thread_data->next = supervisor_thread_data.next; + supervisor_thread_data.next = thread_data; + os_mutex_unlock(&thread_data_list_lock); +} + +static void +thread_data_list_remove(os_thread_data *thread_data) +{ + os_mutex_lock(&thread_data_list_lock); + /* Search and remove it from list */ + os_thread_data *p = &supervisor_thread_data; + while (p && p->next != thread_data) + p = p->next; + + if (p && p->next) { + bh_assert(p->next == thread_data); + p->next = p->next->next; + /* Release the resources in thread_data */ + os_cond_destroy(&thread_data->wait_cond); + os_mutex_destroy(&thread_data->wait_lock); + os_sem_destroy(&thread_data->wait_node.sem); + BH_FREE(thread_data); + } + os_mutex_unlock(&thread_data_list_lock); +} + +static os_thread_data * +thread_data_list_lookup(korp_tid tid) +{ + os_thread_data *thread_data = (os_thread_data *)tid; + os_mutex_lock(&thread_data_list_lock); + os_thread_data *p = supervisor_thread_data.next; + while (p) { + if (p == thread_data) { + /* Found */ + os_mutex_unlock(&thread_data_list_lock); + return p; + } + p = p->next; + } + os_mutex_unlock(&thread_data_list_lock); + return NULL; +} + +int +os_thread_sys_init() +{ + HMODULE module; + + if (is_thread_sys_inited) + return BHT_OK; + + if ((thread_data_key = TlsAlloc()) == TLS_OUT_OF_INDEXES) + return BHT_ERROR; + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(os_thread_data)); + + supervisor_thread_data.thread_id = GetCurrentThreadId(); + + if (os_sem_init(&supervisor_thread_data.wait_node.sem) != BHT_OK) + goto fail1; + + if (os_mutex_init(&supervisor_thread_data.wait_lock) != BHT_OK) + goto fail2; + + if (os_cond_init(&supervisor_thread_data.wait_cond) != BHT_OK) + goto fail3; + + if (!TlsSetValue(thread_data_key, &supervisor_thread_data)) + goto fail4; + + if (os_mutex_init(&thread_data_list_lock) != BHT_OK) + goto fail5; + + if ((module = GetModuleHandle((LPCTSTR) "kernel32"))) { + *(void **)&GetCurrentThreadStackLimits_Kernel32 = + GetProcAddress(module, "GetCurrentThreadStackLimits"); + } + + is_thread_sys_inited = true; + return BHT_OK; + +fail5: + TlsSetValue(thread_data_key, NULL); +fail4: + os_cond_destroy(&supervisor_thread_data.wait_cond); +fail3: + os_mutex_destroy(&supervisor_thread_data.wait_lock); +fail2: + os_sem_destroy(&supervisor_thread_data.wait_node.sem); +fail1: + TlsFree(thread_data_key); + return BHT_ERROR; +} + +void +os_thread_sys_destroy() +{ + if (is_thread_sys_inited) { + os_thread_data *thread_data, *thread_data_next; + + thread_data = supervisor_thread_data.next; + while (thread_data) { + thread_data_next = thread_data->next; + + /* Destroy resources of thread data */ + os_cond_destroy(&thread_data->wait_cond); + os_sem_destroy(&thread_data->wait_node.sem); + os_mutex_destroy(&thread_data->wait_lock); + BH_FREE(thread_data); + + thread_data = thread_data_next; + } + + os_mutex_destroy(&thread_data_list_lock); + os_cond_destroy(&supervisor_thread_data.wait_cond); + os_mutex_destroy(&supervisor_thread_data.wait_lock); + os_sem_destroy(&supervisor_thread_data.wait_node.sem); + memset(&supervisor_thread_data, 0, sizeof(os_thread_data)); + TlsFree(thread_data_key); + thread_data_key = 0; + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + return (os_thread_data *)TlsGetValue(thread_data_key); +} + +static void +os_thread_cleanup(void *retval) +{ + os_thread_data *thread_data = thread_data_current(); + + bh_assert(thread_data != NULL); + + os_mutex_lock(&thread_data->wait_lock); + if (thread_data->thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_data->thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + head->retval = retval; + os_sem_signal(&head->sem); + head = next; + } + thread_data->thread_wait_list = thread_data->thread_wait_list_end = + NULL; + } + /* Set thread status and thread return value */ + thread_data->thread_exited = true; + thread_data->thread_retval = retval; + os_mutex_unlock(&thread_data->wait_lock); +} + +static unsigned __stdcall os_thread_wrapper(void *arg) +{ + os_thread_data *thread_data = arg; + os_thread_data *parent = thread_data->parent; + void *retval; + bool result; + +#if 0 + os_printf("THREAD CREATED %p\n", thread_data); +#endif + + os_mutex_lock(&parent->wait_lock); + thread_data->thread_id = GetCurrentThreadId(); + result = TlsSetValue(thread_data_key, thread_data); +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (result) + result = os_thread_signal_init() == 0 ? true : false; +#endif + /* Notify parent thread */ + os_cond_signal(&parent->wait_cond); + os_mutex_unlock(&parent->wait_lock); + + if (!result) + return -1; + + retval = thread_data->start_routine(thread_data->arg); + + os_thread_cleanup(retval); + return 0; +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + os_thread_data *parent = thread_data_current(); + os_thread_data *thread_data; + + if (!p_tid || !start) + return BHT_ERROR; + + if (stack_size < BH_APPLET_PRESERVED_STACK_SIZE) + stack_size = BH_APPLET_PRESERVED_STACK_SIZE; + + if (!(thread_data = BH_MALLOC(sizeof(os_thread_data)))) + return BHT_ERROR; + + memset(thread_data, 0, sizeof(os_thread_data)); + thread_data->parent = parent; + thread_data->start_routine = start; + thread_data->arg = arg; + + if (os_sem_init(&thread_data->wait_node.sem) != BHT_OK) + goto fail1; + + if (os_mutex_init(&thread_data->wait_lock) != BHT_OK) + goto fail2; + + if (os_cond_init(&thread_data->wait_cond) != BHT_OK) + goto fail3; + + os_mutex_lock(&parent->wait_lock); + if (!_beginthreadex(NULL, stack_size, os_thread_wrapper, thread_data, 0, + NULL)) { + os_mutex_unlock(&parent->wait_lock); + goto fail4; + } + + /* Add thread data into thread data list */ + thread_data_list_add(thread_data); + + /* Wait for the thread routine to set thread_data's tid + and add thread_data to thread data list */ + os_cond_wait(&parent->wait_cond, &parent->wait_lock); + os_mutex_unlock(&parent->wait_lock); + + *p_tid = (korp_tid)thread_data; + return BHT_OK; + +fail4: + os_cond_destroy(&thread_data->wait_cond); +fail3: + os_mutex_destroy(&thread_data->wait_lock); +fail2: + os_sem_destroy(&thread_data->wait_node.sem); +fail1: + BH_FREE(thread_data); + return BHT_ERROR; +} + +int +os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +korp_tid +os_self_thread() +{ + return (korp_tid)TlsGetValue(thread_data_key); +} + +int +os_thread_join(korp_tid thread, void **p_retval) +{ + os_thread_data *thread_data, *curr_thread_data; + + /* Get thread data of current thread */ + curr_thread_data = thread_data_current(); + curr_thread_data->wait_node.next = NULL; + + /* Get thread data of thread to join */ + thread_data = thread_data_list_lookup(thread); + + if (thread_data == NULL) { + os_printf("Can't join thread %p, it does not exist", thread); + return BHT_ERROR; + } + + os_mutex_lock(&thread_data->wait_lock); + + if (thread_data->thread_exited) { + /* Thread has exited */ + if (p_retval) + *p_retval = thread_data->thread_retval; + os_mutex_unlock(&thread_data->wait_lock); + thread_data_list_remove(thread_data); + return BHT_OK; + } + + /* Thread is running */ + 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 */ + 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); + + /* Wait the sem */ + os_sem_wait(&curr_thread_data->wait_node.sem); + if (p_retval) + *p_retval = curr_thread_data->wait_node.retval; + thread_data_list_remove(thread_data); + return BHT_OK; +} + +int +os_thread_detach(korp_tid thread) +{ + /* Do nothing */ + return BHT_OK; + (void)thread; +} + +void +os_thread_exit(void *retval) +{ + os_thread_cleanup(retval); + _endthreadex(0); +} + +int +os_thread_env_init() +{ + os_thread_data *thread_data = TlsGetValue(thread_data_key); + + if (thread_data) + /* Already created */ + return BHT_OK; + + if (!(thread_data = BH_MALLOC(sizeof(os_thread_data)))) + return BHT_ERROR; + + memset(thread_data, 0, sizeof(os_thread_data)); + thread_data->thread_id = GetCurrentThreadId(); + + if (os_sem_init(&thread_data->wait_node.sem) != BHT_OK) + goto fail1; + + if (os_mutex_init(&thread_data->wait_lock) != BHT_OK) + goto fail2; + + if (os_cond_init(&thread_data->wait_cond) != BHT_OK) + goto fail3; + + if (!TlsSetValue(thread_data_key, thread_data)) + goto fail4; + + return BHT_OK; + +fail4: + os_cond_destroy(&thread_data->wait_cond); +fail3: + os_mutex_destroy(&thread_data->wait_lock); +fail2: + os_sem_destroy(&thread_data->wait_node.sem); +fail1: + BH_FREE(thread_data); + return BHT_ERROR; +} + +void +os_thread_env_destroy() +{ + os_thread_data *thread_data = TlsGetValue(thread_data_key); + + /* Note that supervisor_thread_data's resources will be destroyed + by os_thread_sys_destroy() */ + if (thread_data && thread_data != &supervisor_thread_data) { + TlsSetValue(thread_data_key, NULL); + os_cond_destroy(&thread_data->wait_cond); + os_mutex_destroy(&thread_data->wait_lock); + os_sem_destroy(&thread_data->wait_node.sem); + BH_FREE(thread_data); + } +} + +bool +os_thread_env_inited() +{ + os_thread_data *thread_data = TlsGetValue(thread_data_key); + return thread_data ? true : false; +} + +int +os_sem_init(korp_sem *sem) +{ + bh_assert(sem); + *sem = CreateSemaphore(NULL, 0, BH_SEM_COUNT_MAX, NULL); + return (*sem != NULL) ? BHT_OK : BHT_ERROR; +} + +int +os_sem_destroy(korp_sem *sem) +{ + bh_assert(sem); + CloseHandle(*sem); + return BHT_OK; +} + +int +os_sem_wait(korp_sem *sem) +{ + DWORD ret; + + bh_assert(sem); + + ret = WaitForSingleObject(*sem, INFINITE); + + if (ret == WAIT_OBJECT_0) + return BHT_OK; + else if (ret == WAIT_TIMEOUT) + return (int)WAIT_TIMEOUT; + else /* WAIT_FAILED or others */ + return BHT_ERROR; +} + +int +os_sem_reltimed_wait(korp_sem *sem, uint64 useconds) +{ + uint64 mseconds_64; + DWORD ret, mseconds; + + bh_assert(sem); + + if (useconds == BHT_WAIT_FOREVER) + mseconds = INFINITE; + else { + mseconds_64 = useconds / 1000; + + if (mseconds_64 < (uint64)(UINT32_MAX - 1)) { + mseconds = (uint32)mseconds_64; + } + else { + mseconds = UINT32_MAX - 1; + os_printf("Warning: os_sem_reltimed_wait exceeds limit, " + "set to max timeout instead\n"); + } + } + + ret = WaitForSingleObject(*sem, mseconds); + + if (ret == WAIT_OBJECT_0) + return BHT_OK; + else if (ret == WAIT_TIMEOUT) + return (int)WAIT_TIMEOUT; + else /* WAIT_FAILED or others */ + return BHT_ERROR; +} + +int +os_sem_signal(korp_sem *sem) +{ + bh_assert(sem); + return ReleaseSemaphore(*sem, 1, NULL) != FALSE ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_init(korp_mutex *mutex) +{ + bh_assert(mutex); + *mutex = CreateMutex(NULL, FALSE, NULL); + return (*mutex != NULL) ? BHT_OK : BHT_ERROR; +} + +int +os_recursive_mutex_init(korp_mutex *mutex) +{ + bh_assert(mutex); + *mutex = CreateMutex(NULL, FALSE, NULL); + return (*mutex != NULL) ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + assert(mutex); + return CloseHandle(*mutex) ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + + if (*mutex == NULL) { /* static initializer? */ + HANDLE p = CreateMutex(NULL, FALSE, NULL); + + if (!p) { + return BHT_ERROR; + } + + if (InterlockedCompareExchangePointer((PVOID *)mutex, (PVOID)p, NULL) + != NULL) { + /* lock has been created by other threads */ + CloseHandle(p); + } + } + + ret = WaitForSingleObject(*mutex, INFINITE); + return ret != WAIT_FAILED ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + bh_assert(mutex); + return ReleaseMutex(*mutex) ? BHT_OK : BHT_ERROR; +} + +int +os_rwlock_init(korp_rwlock *lock) +{ + bh_assert(lock); + + InitializeSRWLock(&(lock->lock)); + lock->exclusive = false; + + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ + bh_assert(lock); + + AcquireSRWLockShared(&(lock->lock)); + + return BHT_OK; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ + bh_assert(lock); + + AcquireSRWLockExclusive(&(lock->lock)); + lock->exclusive = true; + + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ + bh_assert(lock); + + if (lock->exclusive) { + lock->exclusive = false; + ReleaseSRWLockExclusive(&(lock->lock)); + } + else { + ReleaseSRWLockShared(&(lock->lock)); + } + + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ + (void)lock; + + return BHT_OK; +} + +int +os_cond_init(korp_cond *cond) +{ + bh_assert(cond); + if (os_mutex_init(&cond->wait_list_lock) != BHT_OK) + return BHT_ERROR; + + cond->thread_wait_list = cond->thread_wait_list_end = NULL; + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + bh_assert(cond); + os_mutex_destroy(&cond->wait_list_lock); + return BHT_OK; +} + +static int +os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, + uint64 useconds) +{ + os_thread_wait_node *node = &thread_data_current()->wait_node; + + node->next = NULL; + + bh_assert(cond); + bh_assert(mutex); + os_mutex_lock(&cond->wait_list_lock); + 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 */ + cond->thread_wait_list_end->next = node; + cond->thread_wait_list_end = node; + } + os_mutex_unlock(&cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + os_mutex_unlock(mutex); + int wait_result; + if (timed) + wait_result = os_sem_reltimed_wait(&node->sem, useconds); + else + wait_result = os_sem_wait(&node->sem); + os_mutex_lock(mutex); + + /* Remove wait node from wait list */ + os_mutex_lock(&cond->wait_list_lock); + 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); + + return wait_result; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + if (useconds == BHT_WAIT_FOREVER) { + return os_cond_wait_internal(cond, mutex, false, 0); + } + else { + return os_cond_wait_internal(cond, mutex, true, useconds); + } +} + +int +os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + os_mutex_lock(&cond->wait_list_lock); + if (cond->thread_wait_list) + os_sem_signal(&cond->thread_wait_list->sem); + os_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_broadcast(korp_cond *cond) +{ + /* Signal all of the wait node of wait list */ + os_mutex_lock(&cond->wait_list_lock); + if (cond->thread_wait_list) { + os_thread_wait_node *p = cond->thread_wait_list; + while (p) { + os_sem_signal(&p->sem); + p = p->next; + } + } + + os_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +static os_thread_local_attribute uint8 *thread_stack_boundary = NULL; + +static ULONG +GetCurrentThreadStackLimits_Win7(PULONG_PTR p_low_limit, + PULONG_PTR p_high_limit) +{ + MEMORY_BASIC_INFORMATION mbi; + NT_TIB *tib = (NT_TIB *)NtCurrentTeb(); + + if (!tib) { + os_printf("warning: NtCurrentTeb() failed\n"); + return -1; + } + + *p_high_limit = (ULONG_PTR)tib->StackBase; + + if (VirtualQuery(tib->StackLimit, &mbi, sizeof(mbi))) { + *p_low_limit = (ULONG_PTR)mbi.AllocationBase; + return 0; + } + + os_printf("warning: VirtualQuery() failed\n"); + return GetLastError(); +} + +uint8 * +os_thread_get_stack_boundary() +{ + ULONG_PTR low_limit = 0, high_limit = 0; + uint32 page_size; + + if (thread_stack_boundary) + return thread_stack_boundary; + + page_size = os_getpagesize(); + if (GetCurrentThreadStackLimits_Kernel32) { + GetCurrentThreadStackLimits_Kernel32(&low_limit, &high_limit); + } + else { + if (0 != GetCurrentThreadStackLimits_Win7(&low_limit, &high_limit)) + return NULL; + } + + /* 4 pages are set unaccessible by system, we reserved + one more page at least for safety */ + thread_stack_boundary = (uint8 *)(uintptr_t)low_limit + page_size * 5; + return thread_stack_boundary; +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} + +#ifdef OS_ENABLE_HW_BOUND_CHECK +static os_thread_local_attribute bool thread_signal_inited = false; + +int +os_thread_signal_init() +{ +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + ULONG StackSizeInBytes = 16 * 1024; +#endif + bool ret; + + if (thread_signal_inited) + return 0; + +#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + ret = SetThreadStackGuarantee(&StackSizeInBytes); +#else + ret = true; +#endif + if (ret) + thread_signal_inited = true; + return ret ? 0 : -1; +} + +void +os_thread_signal_destroy() +{ + /* Do nothing */ +} + +bool +os_thread_signal_inited() +{ + return thread_signal_inited; +} +#endif diff --git a/core/shared/platform/windows/win_time.c b/core/shared/platform/windows/win_time.c new file mode 100644 index 0000000000..7b2cd4fffa --- /dev/null +++ b/core/shared/platform/windows/win_time.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_us() +{ + struct timespec ts; +#if defined(__MINGW32__) + // https://www.mail-archive.com/mingw-w64-public@lists.sourceforge.net/msg18361.html + clock_gettime(CLOCK_REALTIME, &ts); +#else + timespec_get(&ts, TIME_UTC); +#endif + + return ((uint64)ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +} + +uint64 +os_time_thread_cputime_us(void) +{ + /* FIXME if u know the right api */ + return os_time_get_boot_us(); +} \ No newline at end of file diff --git a/core/shared/platform/windows/win_util.c b/core/shared/platform/windows/win_util.c new file mode 100644 index 0000000000..58987fa004 --- /dev/null +++ b/core/shared/platform/windows/win_util.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_common.h" +#include "win_util.h" + +// From 1601-01-01 to 1970-01-01 there are 134774 days. +static const uint64_t NT_to_UNIX_epoch_in_ns = + 134774ull * 86400ull * 1000ull * 1000ull * 1000ull; + +__wasi_timestamp_t +convert_filetime_to_wasi_timestamp(LPFILETIME filetime) +{ + ULARGE_INTEGER temp = { .HighPart = filetime->dwHighDateTime, + .LowPart = filetime->dwLowDateTime }; + + // WASI timestamps are measured in nanoseconds whereas FILETIME structs are + // represented in terms 100-nanosecond intervals. + return (temp.QuadPart * 100ull) - NT_to_UNIX_epoch_in_ns; +} + +FILETIME +convert_wasi_timestamp_to_filetime(__wasi_timestamp_t timestamp) +{ + ULARGE_INTEGER temp = { .QuadPart = + (timestamp + NT_to_UNIX_epoch_in_ns) / 100ull }; + + FILETIME ret = { .dwLowDateTime = temp.LowPart, + .dwHighDateTime = temp.HighPart }; + + return ret; +} + +__wasi_errno_t +convert_windows_error_code(DWORD windows_error_code) +{ + switch (windows_error_code) { + case ERROR_INVALID_PARAMETER: + case ERROR_INVALID_HANDLE: + case ERROR_NEGATIVE_SEEK: + return __WASI_EINVAL; + case ERROR_SHARING_VIOLATION: + case ERROR_PIPE_BUSY: + return __WASI_EBUSY; + case ERROR_ACCESS_DENIED: + return __WASI_EACCES; + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + return __WASI_EEXIST; + case ERROR_NO_MORE_FILES: + case ERROR_FILE_NOT_FOUND: + case ERROR_INVALID_NAME: + return __WASI_ENOENT; + case ERROR_PRIVILEGE_NOT_HELD: + return __WASI_EPERM; + case ERROR_NOT_ENOUGH_MEMORY: + return __WASI_ENOMEM; + case ERROR_NOACCESS: + return __WASI_EFAULT; + case ERROR_DIR_NOT_EMPTY: + return __WASI_ENOTEMPTY; + case ERROR_DIRECTORY: + return __WASI_ENOTDIR; + case ERROR_IO_PENDING: + case ERROR_INSUFFICIENT_BUFFER: + case ERROR_INVALID_FLAGS: + case ERROR_NO_UNICODE_TRANSLATION: + default: + return __WASI_EINVAL; + } +} + +#ifdef UWP_DEFAULT_VPRINTF +int +uwp_print_to_debugger(const char *format, va_list ap) +{ + // Provide a stack buffer which should be large enough for any realistic + // string so we avoid making an allocation on every printf call. + char stack_buf[2048]; + char *buf = stack_buf; + int ret = vsnprintf(stack_buf, sizeof(stack_buf), format, ap); + + if ((size_t)ret >= sizeof(stack_buf)) { + // Allocate an extra byte for the null terminator. + char *heap_buf = BH_MALLOC((unsigned int)(ret) + 1); + buf = heap_buf; + + if (heap_buf == NULL) { + // Output as much as we can to the debugger if allocating a buffer + // fails. + OutputDebugStringA(stack_buf); + return ret; + } + + ret = vsnprintf(heap_buf, (size_t)ret + 1, format, ap); + } + + if (ret >= 0) + OutputDebugStringA(buf); + + if (buf != stack_buf) + BH_FREE(buf); + + return ret; +} +#endif + +__wasi_errno_t +convert_winsock_error_code(int error_code) +{ + switch (error_code) { + case WSASYSNOTREADY: + case WSAEWOULDBLOCK: + return __WASI_EAGAIN; + case WSAVERNOTSUPPORTED: + return __WASI_ENOTSUP; + case WSAEINPROGRESS: + return __WASI_EINPROGRESS; + case WSAEPROCLIM: + return __WASI_EBUSY; + case WSAEFAULT: + return __WASI_EFAULT; + case WSAENETDOWN: + return __WASI_ENETDOWN; + case WSAENOTSOCK: + return __WASI_ENOTSOCK; + case WSAEINTR: + return __WASI_EINTR; + case WSAEAFNOSUPPORT: + return __WASI_EAFNOSUPPORT; + case WSAEMFILE: + return __WASI_ENFILE; + case WSAEINVAL: + return __WASI_EINVAL; + case WSAENOBUFS: + return __WASI_ENOBUFS; + case WSAEPROTONOSUPPORT: + return __WASI_EPROTONOSUPPORT; + case WSAEPROTOTYPE: + return __WASI_EPROTOTYPE; + case WSAESOCKTNOSUPPORT: + return __WASI_ENOTSUP; + case WSAECONNABORTED: + return __WASI_ECONNABORTED; + case WSAECONNRESET: + return __WASI_ECONNRESET; + case WSAENOTCONN: + return __WASI_ENOTCONN; + case WSAEINVALIDPROCTABLE: + case WSAEINVALIDPROVIDER: + case WSAEPROVIDERFAILEDINIT: + case WSANOTINITIALISED: + default: + return __WASI_EINVAL; + } +} diff --git a/core/shared/platform/windows/win_util.h b/core/shared/platform/windows/win_util.h new file mode 100644 index 0000000000..7fda508c72 --- /dev/null +++ b/core/shared/platform/windows/win_util.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WIN_UTIL_H +#define _WIN_UTIL_H + +#include "platform_wasi_types.h" +/* + * Suppress the noisy warnings: + * winbase.h: warning C5105: macro expansion producing 'defined' has + * undefined behavior + */ +#pragma warning(disable : 5105) +#include + +__wasi_timestamp_t +convert_filetime_to_wasi_timestamp(LPFILETIME filetime); + +FILETIME +convert_wasi_timestamp_to_filetime(__wasi_timestamp_t timestamp); + +/* Convert a Windows error code to a WASI error code */ +__wasi_errno_t +convert_windows_error_code(DWORD windows_error_code); + +/* Convert a Winsock error code to a WASI error code */ +__wasi_errno_t +convert_winsock_error_code(int error_code); + +#endif /* end of _WIN_UTIL_H */ diff --git a/core/shared/platform/zephyr/COPYRIGHT b/core/shared/platform/zephyr/COPYRIGHT deleted file mode 100644 index a0e1c83a9f..0000000000 --- a/core/shared/platform/zephyr/COPYRIGHT +++ /dev/null @@ -1,126 +0,0 @@ -# $FreeBSD$ -# @(#)COPYRIGHT 8.2 (Berkeley) 3/21/94 - -The compilation of software known as FreeBSD is distributed under the -following terms: - -Copyright (c) 1992-2019 The FreeBSD Project. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -The 4.4BSD and 4.4BSD-Lite software is distributed under the following -terms: - -All of the documentation and software included in the 4.4BSD and 4.4BSD-Lite -Releases is copyrighted by The Regents of the University of California. - -Copyright 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 - The Regents of the University of California. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: -This product includes software developed by the University of -California, Berkeley and its contributors. -4. Neither the name of the University nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -The Institute of Electrical and Electronics Engineers and the American -National Standards Committee X3, on Information Processing Systems have -given us permission to reprint portions of their documentation. - -In the following statement, the phrase ``this text'' refers to portions -of the system documentation. - -Portions of this text are reprinted and reproduced in electronic form in -the second BSD Networking Software Release, from IEEE Std 1003.1-1988, IEEE -Standard Portable Operating System Interface for Computer Environments -(POSIX), copyright C 1988 by the Institute of Electrical and Electronics -Engineers, Inc. In the event of any discrepancy between these versions -and the original IEEE Standard, the original IEEE Standard is the referee -document. - -In the following statement, the phrase ``This material'' refers to portions -of the system documentation. - -This material is reproduced with permission from American National -Standards Committee X3, on Information Processing Systems. Computer and -Business Equipment Manufacturers Association (CBEMA), 311 First St., NW, -Suite 500, Washington, DC 20001-2178. The developmental work of -Programming Language C was completed by the X3J11 Technical Committee. - -The views and conclusions contained in the software and documentation are -those of the authors and should not be interpreted as representing official -policies, either expressed or implied, of the Regents of the University -of California. - - -NOTE: The copyright of UC Berkeley's Berkeley Software Distribution ("BSD") -source has been updated. The copyright addendum may be found at -ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change and is -included below. - -July 22, 1999 - -To All Licensees, Distributors of Any Version of BSD: - -As you know, certain of the Berkeley Software Distribution ("BSD") source -code files require that further distributions of products containing all or -portions of the software, acknowledge within their advertising materials -that such products contain software developed by UC Berkeley and its -contributors. - -Specifically, the provision reads: - -" * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors." - -Effective immediately, licensees and distributors are no longer required to -include the acknowledgement within advertising materials. Accordingly, the -foregoing paragraph of those BSD Unix files containing it is hereby deleted -in its entirety. - -William Hoskins -Director, Office of Technology Licensing -University of California, Berkeley diff --git a/core/shared/platform/zephyr/Makefile b/core/shared/platform/zephyr/Makefile deleted file mode 100644 index c1520f3636..0000000000 --- a/core/shared/platform/zephyr/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -obj-y += bh_assert.o bh_definition.o bh_memory.o bh_platform_log.o bh_thread.o bh_time.o diff --git a/core/shared/platform/zephyr/bh_assert.c b/core/shared/platform/zephyr/bh_assert.c deleted file mode 100644 index ad3205984a..0000000000 --- a/core/shared/platform/zephyr/bh_assert.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_assert.h" -#include -#include -#include - -#ifdef BH_TEST -#include -#endif - -#ifdef BH_TEST -/* for exception throwing */ -jmp_buf bh_test_jb; -#endif -extern void abort(void); -void bh_assert_internal(int v, const char *file_name, int line_number, - const char *expr_string) -{ - if (v) - return; - - if (!file_name) - file_name = "NULL FILENAME"; - if (!expr_string) - expr_string = "NULL EXPR_STRING"; - - printk("\nASSERTION FAILED: %s, at FILE=%s, LINE=%d\n", expr_string, - file_name, line_number); - -#ifdef BH_TEST - longjmp(bh_test_jb, 1); -#endif - - abort(); -} - -void bh_debug_internal(const char *file_name, int line_number, const char *fmt, - ...) -{ -#ifndef JEFF_TEST_VERIFIER - va_list args; - - va_start(args, fmt); - bh_assert(file_name); - - printf("\nDebug info FILE=%s, LINE=%d: ", file_name, line_number); - vprintf(fmt, args); - - va_end(args); - printf("\n"); -#endif -} - diff --git a/core/shared/platform/zephyr/bh_definition.c b/core/shared/platform/zephyr/bh_definition.c deleted file mode 100644 index aae3ed8231..0000000000 --- a/core/shared/platform/zephyr/bh_definition.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" - -#define RSIZE_MAX 0x7FFFFFFF - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n) -{ - char *dest = (char*) s1; - char *src = (char*) s2; - if (n == 0) { - return 0; - } - - if (s1 == NULL || s1max > RSIZE_MAX) { - return -1; - } - if (s2 == NULL || n > s1max) { - memset(dest, 0, s1max); - return -1; - } - memcpy(dest, src, n); - return 0; -} - -int b_strcat_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1 || NULL == s2 - || s1max < (strlen(s1) + strlen(s2) + 1) - || s1max > RSIZE_MAX) { - return -1; - } - - strcat(s1, s2); - - return 0; -} - -int b_strcpy_s(char * s1, size_t s1max, const char * s2) -{ - if (NULL == s1|| NULL == s2 - || s1max < (strlen(s2) + 1) || s1max > RSIZE_MAX) { - return -1; - } - - strcpy(s1, s2); - - return 0; -} - diff --git a/core/shared/platform/zephyr/bh_math.c b/core/shared/platform/zephyr/bh_math.c deleted file mode 100644 index 913d521675..0000000000 --- a/core/shared/platform/zephyr/bh_math.c +++ /dev/null @@ -1,861 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2004 David Schultz - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#include "bh_platform.h" - -#define __FDLIBM_STDC__ - -typedef uint32_t u_int32_t; -typedef uint64_t u_int64_t; - -typedef union u32double_tag { - int *pint; - double *pdouble; -} U32DOUBLE; - -static inline int * -pdouble2pint(double *pdouble) -{ - U32DOUBLE u; - u.pdouble = pdouble; - return u.pint; -} - -typedef union -{ - double value; - struct - { - u_int32_t lsw; - u_int32_t msw; - } parts; - struct - { - u_int64_t w; - } xparts; -} ieee_double_shape_type_little; - -typedef union -{ - double value; - struct - { - u_int32_t msw; - u_int32_t lsw; - } parts; - struct - { - u_int64_t w; - } xparts; -} ieee_double_shape_type_big; - -typedef union { - double d; - struct { - unsigned int manl :32; - unsigned int manh :20; - unsigned int exp :11; - unsigned int sign :1; - } bits; -} IEEEd2bits_L; - -typedef union { - double d; - struct { - unsigned int sign :1; - unsigned int exp :11; - unsigned int manh :20; - unsigned int manl :32; - } bits; -} IEEEd2bits_B; - -typedef union { - float f; - struct { - unsigned int man :23; - unsigned int exp :8; - unsigned int sign :1; - } bits; -} IEEEf2bits_L; - -typedef union { - float f; - struct { - unsigned int sign :1; - unsigned int exp :8; - unsigned int man :23; - } bits; -} IEEEf2bits_B; - -static union { - int a; - char b; -} __ue = { .a = 1 }; - -#define is_little_endian() (__ue.b == 1) - -#define __HIL(x) *(1+pdouble2pint(&x)) -#define __LOL(x) *(pdouble2pint(&x)) -#define __HIB(x) *(int*)&x -#define __LOB(x) *(1+(int*)&x) - -/* Get two 32 bit ints from a double. */ - -#define EXTRACT_WORDS_L(ix0,ix1,d) \ - do { \ - ieee_double_shape_type_little ew_u; \ - ew_u.value = (d); \ - (ix0) = ew_u.parts.msw; \ - (ix1) = ew_u.parts.lsw; \ - } while (0) - -/* Set a double from two 32 bit ints. */ - -#define INSERT_WORDS_L(d,ix0,ix1) \ - do { \ - ieee_double_shape_type_little iw_u; \ - iw_u.parts.msw = (ix0); \ - iw_u.parts.lsw = (ix1); \ - (d) = iw_u.value; \ - } while (0) - -/* Get two 32 bit ints from a double. */ - -#define EXTRACT_WORDS_B(ix0,ix1,d) \ - do { \ - ieee_double_shape_type_big ew_u; \ - ew_u.value = (d); \ - (ix0) = ew_u.parts.msw; \ - (ix1) = ew_u.parts.lsw; \ - } while (0) - -/* Set a double from two 32 bit ints. */ - -#define INSERT_WORDS_B(d,ix0,ix1) \ - do { \ - ieee_double_shape_type_big iw_u; \ - iw_u.parts.msw = (ix0); \ - iw_u.parts.lsw = (ix1); \ - (d) = iw_u.value; \ - } while (0) - -/* Get the more significant 32 bit int from a double. */ -#define GET_HIGH_WORD_L(i,d) \ - do { \ - ieee_double_shape_type_little gh_u; \ - gh_u.value = (d); \ - (i) = gh_u.parts.msw; \ - } while (0) - -/* Get the more significant 32 bit int from a double. */ -#define GET_HIGH_WORD_B(i,d) \ - do { \ - ieee_double_shape_type_big gh_u; \ - gh_u.value = (d); \ - (i) = gh_u.parts.msw; \ - } while (0) - -/* Set the more significant 32 bits of a double from an int. */ -#define SET_HIGH_WORD_L(d,v) \ - do { \ - ieee_double_shape_type_little sh_u; \ - sh_u.value = (d); \ - sh_u.parts.msw = (v); \ - (d) = sh_u.value; \ - } while (0) - -/* Set the more significant 32 bits of a double from an int. */ -#define SET_HIGH_WORD_B(d,v) \ - do { \ - ieee_double_shape_type_big sh_u; \ - sh_u.value = (d); \ - sh_u.parts.msw = (v); \ - (d) = sh_u.value; \ - } while (0) - -/* - * A union which permits us to convert between a float and a 32 bit - * int. - */ -typedef union -{ - float value; - /* FIXME: Assumes 32 bit int. */ - unsigned int word; -} ieee_float_shape_type; - -/* Get a 32 bit int from a float. */ -#define GET_FLOAT_WORD(i,d) \ - do { \ - ieee_float_shape_type gf_u; \ - gf_u.value = (d); \ - (i) = gf_u.word; \ - } while (0) - -/* Set a float from a 32 bit int. */ -#define SET_FLOAT_WORD(d,i) \ - do { \ - ieee_float_shape_type sf_u; \ - sf_u.word = (i); \ - (d) = sf_u.value; \ - } while (0) - -/* Macro wrappers. */ -#define EXTRACT_WORDS(ix0,ix1,d) do { \ - if (is_little_endian()) \ - EXTRACT_WORDS_L(ix0,ix1,d); \ - else \ - EXTRACT_WORDS_B(ix0,ix1,d); \ -} while (0) - -#define INSERT_WORDS(d,ix0,ix1) do { \ - if (is_little_endian()) \ - INSERT_WORDS_L(d,ix0,ix1); \ - else \ - INSERT_WORDS_B(d,ix0,ix1); \ -} while (0) - -#define GET_HIGH_WORD(i,d) \ - do { \ - if (is_little_endian()) \ - GET_HIGH_WORD_L(i,d); \ - else \ - GET_HIGH_WORD_B(i,d); \ - } while (0) - -#define SET_HIGH_WORD(d,v) \ - do { \ - if (is_little_endian()) \ - SET_HIGH_WORD_L(d,v); \ - else \ - SET_HIGH_WORD_B(d,v); \ - } while (0) - -#define __HI(x) (is_little_endian() ? __HIL(x) : __HIB(x)) - -#define __LO(x) (is_little_endian() ? __LOL(x) : __LOB(x)) - -/* - * Attempt to get strict C99 semantics for assignment with non-C99 compilers. - */ -#if FLT_EVAL_METHOD == 0 || __GNUC__ == 0 -#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval)) -#else -#define STRICT_ASSIGN(type, lval, rval) do { \ - volatile type __lval; \ - \ - if (sizeof(type) >= sizeof(long double)) \ - (lval) = (rval); \ - else { \ - __lval = (rval); \ - (lval) = __lval; \ - } \ -} while (0) -#endif - -#ifdef __FDLIBM_STDC__ -static const double huge = 1.0e300; -#else -static double huge = 1.0e300; -#endif - -#ifdef __STDC__ -static const double -#else -static double -#endif -tiny = 1.0e-300; - -#ifdef __STDC__ -static const double -#else -static double -#endif -one= 1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */ - -#ifdef __STDC__ -static const double -#else -static double -#endif -TWO52[2]={ - 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ - -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ -}; - -static double freebsd_sqrt(double x); -static double freebsd_floor(double x); -static double freebsd_ceil(double x); -static double freebsd_fabs(double x); -static double freebsd_rint(double x); -static int freebsd_isnan(double x); - -static double freebsd_sqrt(double x) /* wrapper sqrt */ -{ - double z; - int32_t sign = (int)0x80000000; - int32_t ix0,s0,q,m,t,i; - u_int32_t r,t1,s1,ix1,q1; - - EXTRACT_WORDS(ix0,ix1,x); - - /* take care of Inf and NaN */ - if((ix0&0x7ff00000)==0x7ff00000) { - return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf - sqrt(-inf)=sNaN */ - } - /* take care of zero */ - if(ix0<=0) { - if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ - else if(ix0<0) - return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ - } - /* normalize x */ - m = (ix0>>20); - if(m==0) { /* subnormal x */ - while(ix0==0) { - m -= 21; - ix0 |= (ix1>>11); ix1 <<= 21; - } - for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; - m -= i-1; - ix0 |= (ix1>>(32-i)); - ix1 <<= i; - } - m -= 1023; /* unbias exponent */ - ix0 = (ix0&0x000fffff)|0x00100000; - if(m&1){ /* odd m, double x to make it even */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - } - m >>= 1; /* m = [m/2] */ - - /* generate sqrt(x) bit by bit */ - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ - r = 0x00200000; /* r = moving bit from right to left */ - - while(r!=0) { - t = s0+r; - if(t<=ix0) { - s0 = t+r; - ix0 -= t; - q += r; - } - ix0 += ix0 + ((ix1&sign)>>31); - ix1 += ix1; - r>>=1; - } - - r = sign; - while(r!=0) { - t1 = s1+r; - t = s0; - if((t>31); - ix1 += ix1; - r>>=1; - } - - /* use floating add to find out rounding direction */ - if((ix0|ix1)!=0) { - z = one-tiny; /* trigger inexact flag */ - if (z>=one) { - z = one+tiny; - if (q1==(u_int32_t)0xffffffff) { q1=0; q += 1;} - else if (z>one) { - if (q1==(u_int32_t)0xfffffffe) q+=1; - q1+=2; - } else - q1 += (q1&1); - } - } - ix0 = (q>>1)+0x3fe00000; - ix1 = q1>>1; - if ((q&1)==1) ix1 |= sign; - ix0 += (m <<20); - - INSERT_WORDS(z,ix0,ix1); - - return z; -} - -static double freebsd_floor(double x) -{ - int32_t i0,i1,j0; - u_int32_t i,j; - - EXTRACT_WORDS(i0,i1,x); - - j0 = ((i0>>20)&0x7ff)-0x3ff; - if(j0<20) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0>=0) {i0=i1=0;} - else if(((i0&0x7fffffff)|i1)!=0) - { i0=0xbff00000;i1=0;} - } - } else { - i = (0x000fffff)>>j0; - if(((i0&i)|i1)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0<0) i0 += (0x00100000)>>j0; - i0 &= (~i); i1=0; - } - } - } else if (j0>51) { - if(j0==0x400) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } else { - i = ((u_int32_t)(0xffffffff))>>(j0-20); - if((i1&i)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0<0) { - if(j0==20) i0+=1; - else { - j = i1+(1<<(52-j0)); - if(j>20)&0x7ff)-0x3ff; - if(j0<20) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0<0) {i0=0x80000000;i1=0;} - else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} - } - } else { - i = (0x000fffff)>>j0; - if(((i0&i)|i1)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0>0) i0 += (0x00100000)>>j0; - i0 &= (~i); i1=0; - } - } - } else if (j0>51) { - if(j0==0x400) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } else { - i = ((u_int32_t)(0xffffffff))>>(j0-20); - if((i1&i)==0) return x; /* x is integral */ - if(huge+x>0.0) { /* raise inexact flag */ - if(i0>0) { - if(j0==20) i0+=1; - else { - j = i1 + (1<<(52-j0)); - if(j>31)&1; - j0 = ((i0>>20)&0x7ff)-0x3ff; - if(j0<20) { - if(j0<0) { - if(((i0&0x7fffffff)|i1)==0) return x; - i1 |= (i0&0x0fffff); - i0 &= 0xfffe0000; - i0 |= ((i1|-i1)>>12)&0x80000; - SET_HIGH_WORD(x,i0); - STRICT_ASSIGN(double,w,TWO52[sx]+x); - t = w-TWO52[sx]; - GET_HIGH_WORD(i0,t); - SET_HIGH_WORD(t,(i0&0x7fffffff)|(sx<<31)); - return t; - } else { - i = (0x000fffff)>>j0; - if(((i0&i)|i1)==0) return x; /* x is integral */ - i>>=1; - if(((i0&i)|i1)!=0) { - /* - * Some bit is set after the 0.5 bit. To avoid the - * possibility of errors from double rounding in - * w = TWO52[sx]+x, adjust the 0.25 bit to a lower - * guard bit. We do this for all j0<=51. The - * adjustment is trickiest for j0==18 and j0==19 - * since then it spans the word boundary. - */ - if(j0==19) i1 = 0x40000000; else - if(j0==18) i1 = 0x80000000; else - i0 = (i0&(~i))|((0x20000)>>j0); - } - } - } else if (j0>51) { - if(j0==0x400) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } else { - i = ((u_int32_t)(0xffffffff))>>(j0-20); - if((i1&i)==0) return x; /* x is integral */ - i>>=1; - if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); - } - INSERT_WORDS(x,i0,i1); - STRICT_ASSIGN(double,w,TWO52[sx]+x); - return w-TWO52[sx]; -} - -static int freebsd_isnan(double d) -{ - if (is_little_endian()) { - IEEEd2bits_L u; - u.d = d; - return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); - } - else { - IEEEd2bits_B u; - u.d = d; - return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); - } -} - -static double freebsd_fabs(double x) -{ - u_int32_t high; - GET_HIGH_WORD(high,x); - SET_HIGH_WORD(x,high&0x7fffffff); - return x; -} - -static const float huge_f = 1.0e30F; - -static const float -TWO23[2]={ - 8.3886080000e+06, /* 0x4b000000 */ - -8.3886080000e+06, /* 0xcb000000 */ -}; - -static float -freebsd_truncf(float x) -{ - int32_t i0,j0; - u_int32_t i; - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge_f+x>0.0F) /* |x|<1, so return 0*sign(x) */ - i0 &= 0x80000000; - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) return x; /* x is integral */ - if(huge_f+x>0.0F) /* raise inexact flag */ - i0 &= (~i); - } - } else { - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } - SET_FLOAT_WORD(x,i0); - return x; -} - -static float -freebsd_rintf(float x) -{ - int32_t i0,j0,sx; - float w,t; - GET_FLOAT_WORD(i0,x); - sx = (i0>>31)&1; - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { - if((i0&0x7fffffff)==0) return x; - STRICT_ASSIGN(float,w,TWO23[sx]+x); - t = w-TWO23[sx]; - GET_FLOAT_WORD(i0,t); - SET_FLOAT_WORD(t,(i0&0x7fffffff)|(sx<<31)); - return t; - } - STRICT_ASSIGN(float,w,TWO23[sx]+x); - return w-TWO23[sx]; - } - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ -} - -static float -freebsd_ceilf(float x) -{ - int32_t i0,j0; - u_int32_t i; - - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge_f+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0<0) {i0=0x80000000;} - else if(i0!=0) { i0=0x3f800000;} - } - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) return x; /* x is integral */ - if(huge_f+x>(float)0.0) { /* raise inexact flag */ - if(i0>0) i0 += (0x00800000)>>j0; - i0 &= (~i); - } - } - } else { - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } - SET_FLOAT_WORD(x,i0); - return x; -} - -static float -freebsd_floorf(float x) -{ - int32_t i0,j0; - u_int32_t i; - GET_FLOAT_WORD(i0,x); - j0 = ((i0>>23)&0xff)-0x7f; - if(j0<23) { - if(j0<0) { /* raise inexact if x != 0 */ - if(huge_f+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ - if(i0>=0) {i0=0;} - else if((i0&0x7fffffff)!=0) - { i0=0xbf800000;} - } - } else { - i = (0x007fffff)>>j0; - if((i0&i)==0) return x; /* x is integral */ - if(huge_f+x>(float)0.0) { /* raise inexact flag */ - if(i0<0) i0 += (0x00800000)>>j0; - i0 &= (~i); - } - } - } else { - if(j0==0x80) return x+x; /* inf or NaN */ - else return x; /* x is integral */ - } - SET_FLOAT_WORD(x,i0); - return x; -} - -static float -freebsd_fminf(float x, float y) -{ - if (is_little_endian()) { - IEEEf2bits_L u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[1].bits.sign].f); - } - else { - IEEEf2bits_B u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[1].bits.sign].f); - } - - return (x < y ? x : y); -} - -static float -freebsd_fmaxf(float x, float y) -{ - if (is_little_endian()) { - IEEEf2bits_L u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[0].bits.sign].f); - } - else { - IEEEf2bits_B u[2]; - - u[0].f = x; - u[1].f = y; - - /* Check for NaNs to avoid raising spurious exceptions. */ - if (u[0].bits.exp == 255 && u[0].bits.man != 0) - return (y); - if (u[1].bits.exp == 255 && u[1].bits.man != 0) - return (x); - - /* Handle comparisons of signed zeroes. */ - if (u[0].bits.sign != u[1].bits.sign) - return (u[u[0].bits.sign].f); - } - - return (x > y ? x : y); -} - -double sqrt(double x) -{ - return freebsd_sqrt(x); -} - -double floor(double x) -{ - return freebsd_floor(x); -} - -double ceil(double x) -{ - return freebsd_ceil(x); -} - -double fmin(double x, double y) -{ - return x < y ? x : y; -} - -double fmax(double x, double y) -{ - return x > y ? x : y; -} - -double rint(double x) -{ - return freebsd_rint(x); -} - -double fabs(double x) -{ - return freebsd_fabs(x); -} - -int isnan(double x) -{ - return freebsd_isnan(x); -} - -double trunc(double x) -{ - return (x > 0) ? freebsd_floor(x) : freebsd_ceil(x); -} - -int signbit(double x) -{ - return ((__HI(x) & 0x80000000) >> 31); -} - -float -truncf(float x) -{ - return freebsd_truncf(x); -} - -float -rintf(float x) -{ - return freebsd_rintf(x); -} - -float -ceilf(float x) -{ - return freebsd_ceilf(x); -} - -float -floorf(float x) -{ - return freebsd_floorf(x); -} - -float -fminf(float x, float y) -{ - return freebsd_fminf(x, y); -} - -float -fmaxf(float x, float y) -{ - return freebsd_fmaxf(x, y); -} - diff --git a/core/shared/platform/zephyr/bh_platform.c b/core/shared/platform/zephyr/bh_platform.c deleted file mode 100755 index fd56115bb2..0000000000 --- a/core/shared/platform/zephyr/bh_platform.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include "bh_memory.h" -#include "bh_common.h" -#include -#include - -char *bh_strdup(const char *s) -{ - uint32 size; - char *s1 = NULL; - - if (s) { - size = (uint32)(strlen(s) + 1); - if ((s1 = bh_malloc(size))) - bh_memcpy_s(s1, size, s, size); - } - return s1; -} - -static int -_stdout_hook_iwasm(int c) -{ - printk("%c", (char)c); - return 1; -} - -int bh_platform_init() -{ - extern void __stdout_hook_install(int (*hook)(int)); - /* Enable printf() in Zephyr */ - __stdout_hook_install(_stdout_hook_iwasm); - return 0; -} - -void * -bh_mmap(void *hint, unsigned int size, int prot, int flags) -{ - return bh_malloc(size); -} - -void -bh_munmap(void *addr, uint32 size) -{ - return bh_free(addr); -} - -int -bh_mprotect(void *addr, uint32 size, int prot) -{ - return 0; -} diff --git a/core/shared/platform/zephyr/bh_platform.h b/core/shared/platform/zephyr/bh_platform.h deleted file mode 100644 index 86cfcd4afc..0000000000 --- a/core/shared/platform/zephyr/bh_platform.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _BH_PLATFORM_H -#define _BH_PLATFORM_H - -#include "bh_config.h" -#include "bh_types.h" -#include "bh_memory.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef CONFIG_NET_BUF_USER_DATA_SIZE -#define CONFIG_NET_BUF_USER_DATA_SIZE 0 -#endif -#include -#include -#include -#include -#include - -#ifndef BH_PLATFORM_ZEPHYR -#define BH_PLATFORM_ZEPHYR -#endif - -#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) - -/* Default thread priority */ -#define BH_THREAD_DEFAULT_PRIORITY 7 - -#define BH_ROUTINE_MODIFIER - -/* Invalid thread tid */ -#define INVALID_THREAD_ID NULL - -#define BH_WAIT_FOREVER K_FOREVER - -typedef uint64_t uint64; -typedef int64_t int64; - -typedef struct k_thread korp_thread; -typedef korp_thread *korp_tid; -typedef struct k_mutex korp_mutex; -typedef struct k_sem korp_sem; - -#define wa_malloc bh_malloc -#define wa_free bh_free -#define wa_strdup bh_strdup - -struct bh_thread_wait_node; -typedef struct bh_thread_wait_node *bh_thread_wait_list; -typedef struct korp_cond { - struct k_mutex wait_list_lock; - bh_thread_wait_list thread_wait_list; -} korp_cond; - -typedef void* (*thread_start_routine_t)(void*); - -#define wa_malloc bh_malloc -#define wa_free bh_free - -#define bh_printf printf - -/* Unit test framework is based on C++, where the declaration of - snprintf is different. */ -#ifndef __cplusplus -int snprintf(char *buffer, size_t count, const char *format, ...); -#endif - -#ifndef NULL -#define NULL ((void*)0) -#endif - -/** - * Return the offset of the given field in the given type. - * - * @param Type the type containing the filed - * @param field the field in the type - * - * @return the offset of field in Type - */ -#ifndef offsetof -#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) -#endif - -#define bh_assert(x) \ - do { \ - if (!(x)) { \ - printk("bh_assert(%s, %d)\n", __func__, __LINE__);\ - } \ - } while (0) - -int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, - unsigned int n); -int b_strcat_s(char * s1, size_t s1max, const char * s2); -int b_strcpy_s(char * s1, size_t s1max, const char * s2); - -/* math functions */ -double sqrt(double x); -double floor(double x); -double ceil(double x); -double fmin(double x, double y); -double fmax(double x, double y); -double rint(double x); -double fabs(double x); -double trunc(double x); -float floorf(float x); -float ceilf(float x); -float fminf(float x, float y); -float fmaxf(float x, float y); -float rintf(float x); -float truncf(float x); -int signbit(double x); -int isnan(double x); - -unsigned long long int strtoull(const char *nptr, char **endptr, - int base); -double strtod(const char *nptr, char **endptr); -float strtof(const char *nptr, char **endptr); - -int bh_platform_init(); - -/* MMAP mode */ -enum { - MMAP_PROT_NONE = 0, - MMAP_PROT_READ = 1, - MMAP_PROT_WRITE = 2, - MMAP_PROT_EXEC = 4 -}; - -/* MMAP flags */ -enum { - MMAP_MAP_NONE = 0, - /* Put the mapping into 0 to 2 G, supported only on x86_64 */ - MMAP_MAP_32BIT = 1, - /* Don't interpret addr as a hint: place the mapping at exactly - that address. */ - MMAP_MAP_FIXED = 2 -}; - -void *bh_mmap(void *hint, unsigned int size, int prot, int flags); -void bh_munmap(void *addr, uint32 size); -int bh_mprotect(void *addr, uint32 size, int prot); - -#endif diff --git a/core/shared/platform/zephyr/bh_platform_log.c b/core/shared/platform/zephyr/bh_platform_log.c deleted file mode 100644 index 76e377690a..0000000000 --- a/core/shared/platform/zephyr/bh_platform_log.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_platform.h" -#include - -struct out_context { - int count; -}; - -typedef int (*out_func_t)(int c, void *ctx); - -extern void *__printk_get_hook(void); -static int (*_char_out)(int) = NULL; - -static int char_out(int c, struct out_context *ctx) -{ - ctx->count++; - if (_char_out == NULL) { - _char_out = __printk_get_hook(); - } - return _char_out(c); -} - -static int bh_vprintk(const char *fmt, va_list ap) -{ - struct out_context ctx = { 0 }; - z_vprintk((out_func_t) char_out, &ctx, fmt, ap); - return ctx.count; -} - -void bh_log_emit(const char *fmt, va_list ap) -{ - bh_vprintk(fmt, ap); -} - -int bh_fprintf(FILE *stream, const char *fmt, ...) -{ - (void) stream; - va_list ap; - int ret; - - va_start(ap, fmt); - ret = bh_vprintk(fmt, ap); - va_end(ap); - - return ret; -} - -int bh_fflush(void *stream) -{ - (void) stream; - return 0; -} - diff --git a/core/shared/platform/zephyr/bh_thread.c b/core/shared/platform/zephyr/bh_thread.c deleted file mode 100755 index dba49e6d30..0000000000 --- a/core/shared/platform/zephyr/bh_thread.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_thread.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include -#include - -typedef struct bh_thread_wait_node { - struct k_sem sem; - bh_thread_wait_list next; -} bh_thread_wait_node; - -typedef struct bh_thread_data { - /* Next thread data */ - struct bh_thread_data *next; - /* Zephyr thread handle */ - korp_tid tid; - /* Jeff thread local root */ - void *tlr; - /* Lock for waiting list */ - struct k_mutex wait_list_lock; - /* Waiting list of other threads who are joining this thread */ - bh_thread_wait_list thread_wait_list; - /* Thread stack size */ - unsigned stack_size; - /* Thread stack */ - char stack[1]; -} bh_thread_data; - -typedef struct bh_thread_obj { - struct k_thread thread; - /* Whether the thread is terminated and this thread object is to - be freed in the future. */ - bool to_be_freed; - struct bh_thread_obj *next; -} bh_thread_obj; - -static bool is_thread_sys_inited = false; - -/* Thread data of supervisor thread */ -static bh_thread_data supervisor_thread_data; - -/* Lock for thread data list */ -static struct k_mutex thread_data_lock; - -/* Thread data list */ -static bh_thread_data *thread_data_list = NULL; - -/* Lock for thread object list */ -static struct k_mutex thread_obj_lock; - -/* Thread object list */ -static bh_thread_obj *thread_obj_list = NULL; - -static void thread_data_list_add(bh_thread_data *thread_data) -{ - k_mutex_lock(&thread_data_lock, K_FOREVER); - if (!thread_data_list) - thread_data_list = thread_data; - else { - /* If already in list, just return */ - bh_thread_data *p = thread_data_list; - while (p) { - if (p == thread_data) { - k_mutex_unlock(&thread_data_lock); - return; - } - p = p->next; - } - - /* Set as head of list */ - thread_data->next = thread_data_list; - thread_data_list = thread_data; - } - k_mutex_unlock(&thread_data_lock); -} - -static void thread_data_list_remove(bh_thread_data *thread_data) -{ - k_mutex_lock(&thread_data_lock, K_FOREVER); - if (thread_data_list) { - if (thread_data_list == thread_data) - thread_data_list = thread_data_list->next; - else { - /* Search and remove it from list */ - bh_thread_data *p = thread_data_list; - while (p && p->next != thread_data) - p = p->next; - if (p && p->next == thread_data) - p->next = p->next->next; - } - } - k_mutex_unlock(&thread_data_lock); -} - -static bh_thread_data * -thread_data_list_lookup(k_tid_t tid) -{ - k_mutex_lock(&thread_data_lock, K_FOREVER); - if (thread_data_list) { - bh_thread_data *p = thread_data_list; - while (p) { - if (p->tid == tid) { - /* Found */ - k_mutex_unlock(&thread_data_lock); - return p; - } - p = p->next; - } - } - k_mutex_unlock(&thread_data_lock); - return NULL; -} - -static void thread_obj_list_add(bh_thread_obj *thread_obj) -{ - k_mutex_lock(&thread_obj_lock, K_FOREVER); - if (!thread_obj_list) - thread_obj_list = thread_obj; - else { - /* Set as head of list */ - thread_obj->next = thread_obj_list; - thread_obj_list = thread_obj; - } - k_mutex_unlock(&thread_obj_lock); -} - -static void thread_obj_list_reclaim() -{ - bh_thread_obj *p, *p_prev; - k_mutex_lock(&thread_obj_lock, K_FOREVER); - p_prev = NULL; - p = thread_obj_list; - while (p) { - if (p->to_be_freed) { - if (p_prev == NULL) { /* p is the head of list */ - thread_obj_list = p->next; - bh_free(p); - p = thread_obj_list; - } else { /* p is not the head of list */ - p_prev->next = p->next; - bh_free(p); - p = p_prev->next; - } - } else { - p_prev = p; - p = p->next; - } - } - k_mutex_unlock(&thread_obj_lock); -} - -int _vm_thread_sys_init() -{ - if (is_thread_sys_inited) - return BHT_OK; - - k_mutex_init(&thread_data_lock); - k_mutex_init(&thread_obj_lock); - - /* Initialize supervisor thread data */ - memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); - supervisor_thread_data.tid = k_current_get(); - /* Set as head of thread data list */ - thread_data_list = &supervisor_thread_data; - - is_thread_sys_inited = true; - return BHT_OK; -} - -void vm_thread_sys_destroy(void) -{ - if (is_thread_sys_inited) { - is_thread_sys_inited = false; - } -} - -static bh_thread_data * -thread_data_current() -{ - k_tid_t tid = k_current_get(); - return thread_data_list_lookup(tid); -} - -static void vm_thread_cleanup(void) -{ - bh_thread_data *thread_data = thread_data_current(); - - bh_assert(thread_data != NULL); - k_mutex_lock(&thread_data->wait_list_lock, K_FOREVER); - if (thread_data->thread_wait_list) { - /* Signal each joining thread */ - bh_thread_wait_list head = thread_data->thread_wait_list; - while (head) { - bh_thread_wait_list next = head->next; - k_sem_give(&head->sem); - /* head will be freed by joining thread */ - head = next; - } - thread_data->thread_wait_list = NULL; - } - k_mutex_unlock(&thread_data->wait_list_lock); - - thread_data_list_remove(thread_data); - /* Set flag to true for the next thread creating to - free the thread object */ - ((bh_thread_obj*) thread_data->tid)->to_be_freed = true; - bh_free(thread_data); -} - -static void vm_thread_wrapper(void *start, void *arg, void *thread_data) -{ - /* Set thread custom data */ - ((bh_thread_data*) thread_data)->tid = k_current_get(); - thread_data_list_add(thread_data); - - ((thread_start_routine_t) start)(arg); - vm_thread_cleanup(); -} - -int _vm_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, - unsigned int stack_size) -{ - return _vm_thread_create_with_prio(p_tid, start, arg, stack_size, - BH_THREAD_DEFAULT_PRIORITY); -} - -int _vm_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, - void *arg, unsigned int stack_size, int prio) -{ - korp_tid tid; - bh_thread_data *thread_data; - unsigned thread_data_size; - - if (!p_tid || !stack_size) - return BHT_ERROR; - - /* Free the thread objects of terminated threads */ - thread_obj_list_reclaim(); - - /* Create and initialize thread object */ - if (!(tid = bh_malloc(sizeof(bh_thread_obj)))) - return BHT_ERROR; - - memset(tid, 0, sizeof(bh_thread_obj)); - - /* Create and initialize thread data */ - thread_data_size = offsetof(bh_thread_data, stack) + stack_size; - if (!(thread_data = bh_malloc(thread_data_size))) { - bh_free(tid); - return BHT_ERROR; - } - - memset(thread_data, 0, thread_data_size); - k_mutex_init(&thread_data->wait_list_lock); - thread_data->stack_size = stack_size; - thread_data->tid = tid; - - /* Create the thread */ - if (!((tid = k_thread_create(tid, (k_thread_stack_t *) thread_data->stack, - stack_size, vm_thread_wrapper, start, arg, thread_data, prio, 0, - K_NO_WAIT)))) { - bh_free(tid); - bh_free(thread_data); - return BHT_ERROR; - } - - bh_assert(tid == thread_data->tid); - - /* Set thread custom data */ - thread_data_list_add(thread_data); - thread_obj_list_add((bh_thread_obj*) tid); - *p_tid = tid; - return BHT_OK; -} - -korp_tid _vm_self_thread() -{ - return (korp_tid) k_current_get(); -} - -void vm_thread_exit(void * code) -{ - (void) code; - korp_tid self = vm_self_thread(); - vm_thread_cleanup(); - k_thread_abort((k_tid_t) self); -} - -int _vm_thread_cancel(korp_tid thread) -{ - k_thread_abort((k_tid_t) thread); - return 0; -} - -int _vm_thread_join(korp_tid thread, void **value_ptr, int mills) -{ - (void) value_ptr; - bh_thread_data *thread_data; - bh_thread_wait_node *node; - - /* Create wait node and append it to wait list */ - if (!(node = bh_malloc(sizeof(bh_thread_wait_node)))) - return BHT_ERROR; - - k_sem_init(&node->sem, 0, 1); - node->next = NULL; - - /* Get thread data */ - thread_data = thread_data_list_lookup(thread); - bh_assert(thread_data != NULL); - - k_mutex_lock(&thread_data->wait_list_lock, K_FOREVER); - if (!thread_data->thread_wait_list) - thread_data->thread_wait_list = node; - else { - /* Add to end of waiting list */ - bh_thread_wait_node *p = thread_data->thread_wait_list; - while (p->next) - p = p->next; - p->next = node; - } - k_mutex_unlock(&thread_data->wait_list_lock); - - /* Wait the sem */ - k_sem_take(&node->sem, mills); - - /* Wait some time for the thread to be actually terminated */ - k_sleep(100); - - /* Destroy resource */ - bh_free(node); - return BHT_OK; -} - -int _vm_thread_detach(korp_tid thread) -{ - (void) thread; - return BHT_OK; -} - -void *_vm_tls_get(unsigned idx) -{ - (void) idx; - bh_thread_data *thread_data; - - bh_assert(idx == 0); - thread_data = thread_data_current(); - - return thread_data ? thread_data->tlr : NULL; -} - -int _vm_tls_put(unsigned idx, void * tls) -{ - bh_thread_data *thread_data; - - (void) idx; - bh_assert(idx == 0); - thread_data = thread_data_current(); - bh_assert(thread_data != NULL); - - thread_data->tlr = tls; - return BHT_OK; -} - -int _vm_mutex_init(korp_mutex *mutex) -{ - (void) mutex; - k_mutex_init(mutex); - return BHT_OK; -} - -int _vm_recursive_mutex_init(korp_mutex *mutex) -{ - k_mutex_init(mutex); - return BHT_OK; -} - -int _vm_mutex_destroy(korp_mutex *mutex) -{ - (void) mutex; - return BHT_OK; -} - -void vm_mutex_lock(korp_mutex *mutex) -{ - k_mutex_lock(mutex, K_FOREVER); -} - -int vm_mutex_trylock(korp_mutex *mutex) -{ - return k_mutex_lock(mutex, K_NO_WAIT); -} - -void vm_mutex_unlock(korp_mutex *mutex) -{ - k_mutex_unlock(mutex); -} - -int _vm_sem_init(korp_sem* sem, unsigned int c) -{ - k_sem_init(sem, 0, c); - return BHT_OK; -} - -int _vm_sem_destroy(korp_sem *sem) -{ - (void) sem; - return BHT_OK; -} - -int _vm_sem_wait(korp_sem *sem) -{ - return k_sem_take(sem, K_FOREVER); -} - -int _vm_sem_reltimedwait(korp_sem *sem, int mills) -{ - return k_sem_take(sem, mills); -} - -int _vm_sem_post(korp_sem *sem) -{ - k_sem_give(sem); - return BHT_OK; -} - -int _vm_cond_init(korp_cond *cond) -{ - k_mutex_init(&cond->wait_list_lock); - cond->thread_wait_list = NULL; - return BHT_OK; -} - -int _vm_cond_destroy(korp_cond *cond) -{ - (void) cond; - return BHT_OK; -} - -static int _vm_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, - bool timed, int mills) -{ - bh_thread_wait_node *node; - - /* Create wait node and append it to wait list */ - if (!(node = bh_malloc(sizeof(bh_thread_wait_node)))) - return BHT_ERROR; - - k_sem_init(&node->sem, 0, 1); - node->next = NULL; - - k_mutex_lock(&cond->wait_list_lock, K_FOREVER); - if (!cond->thread_wait_list) - cond->thread_wait_list = node; - else { - /* Add to end of wait list */ - bh_thread_wait_node *p = cond->thread_wait_list; - while (p->next) - p = p->next; - p->next = node; - } - k_mutex_unlock(&cond->wait_list_lock); - - /* Unlock mutex, wait sem and lock mutex again */ - k_mutex_unlock(mutex); - k_sem_take(&node->sem, timed ? mills : K_FOREVER); - k_mutex_lock(mutex, K_FOREVER); - - /* Remove wait node from wait list */ - k_mutex_lock(&cond->wait_list_lock, K_FOREVER); - if (cond->thread_wait_list == node) - cond->thread_wait_list = node->next; - else { - /* Remove from the wait list */ - bh_thread_wait_node *p = cond->thread_wait_list; - while (p->next != node) - p = p->next; - p->next = node->next; - } - bh_free(node); - k_mutex_unlock(&cond->wait_list_lock); - - return BHT_OK; -} - -int _vm_cond_wait(korp_cond *cond, korp_mutex *mutex) -{ - return _vm_cond_wait_internal(cond, mutex, false, 0); -} - -int _vm_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int mills) -{ - return _vm_cond_wait_internal(cond, mutex, true, mills); -} - -int _vm_cond_signal(korp_cond *cond) -{ - /* Signal the head wait node of wait list */ - k_mutex_lock(&cond->wait_list_lock, K_FOREVER); - if (cond->thread_wait_list) - k_sem_give(&cond->thread_wait_list->sem); - k_mutex_unlock(&cond->wait_list_lock); - - return BHT_OK; -} - -int _vm_cond_broadcast(korp_cond *cond) -{ - /* Signal each wait node of wait list */ - k_mutex_lock(&cond->wait_list_lock, K_FOREVER); - if (cond->thread_wait_list) { - bh_thread_wait_node *p = cond->thread_wait_list; - while (p) { - k_sem_give(&p->sem); - p = p->next; - } - } - k_mutex_unlock(&cond->wait_list_lock); - - return BHT_OK; -} - diff --git a/core/shared/platform/zephyr/bh_time.c b/core/shared/platform/zephyr/bh_time.c deleted file mode 100755 index b6db78eade..0000000000 --- a/core/shared/platform/zephyr/bh_time.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "bh_time.h" - -/* - * This function returns milliseconds per tick. - * @return milliseconds per tick. - */ -uint64 _bh_time_get_tick_millisecond() -{ - return k_uptime_get_32(); -} - -/* - * This function returns milliseconds after boot. - * @return milliseconds after boot. - */ -uint64 _bh_time_get_boot_millisecond() -{ - return k_uptime_get_32(); -} - -uint64 _bh_time_get_millisecond_from_1970() -{ - return k_uptime_get(); -} - -size_t _bh_time_strftime(char *str, size_t max, const char *format, int64 time) -{ - (void) format; - (void) time; - uint32 t = k_uptime_get_32(); - int h, m, s; - - t = t % (24 * 60 * 60); - h = t / (60 * 60); - t = t % (60 * 60); - m = t / 60; - s = t % 60; - - return snprintf(str, max, "%02d:%02d:%02d", h, m, s); -} - diff --git a/core/shared/platform/zephyr/platform_internal.h b/core/shared/platform/zephyr/platform_internal.h new file mode 100644 index 0000000000..b3f1218742 --- /dev/null +++ b/core/shared/platform/zephyr/platform_internal.h @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-FileCopyrightText: 2024 Siemens AG (For Zephyr usermode changes) + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +/* + * Modern Zephyr uses zephyr/ namespace. + * + * Note: Cannot use KERNEL_VERSION_NUMBER here as it's defined in version.h + * which we're trying to include. Must use feature detection instead. + */ +#ifdef __has_include +#if __has_include() +#include +#include +#else +#include +#include +#endif +#else +#include +#include +#endif + +#if KERNEL_VERSION_NUMBER < 0x030200 /* version 3.2.0 */ +#include +#include +#if KERNEL_VERSION_NUMBER >= 0x020200 /* version 2.2.0 */ +#include +#else +#include +#endif +#else /* else of KERNEL_VERSION_NUMBER < 0x030200 */ +#include +#endif /* end of KERNEL_VERSION_NUMBER < 0x030200 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_NET_BUF_USER_DATA_SIZE +#define CONFIG_NET_BUF_USER_DATA_SIZE 0 +#endif + +#if KERNEL_VERSION_NUMBER < 0x030200 /* version 3.2.0 */ +#include +#include +#include +#include +#include +#include +#include +#else /* else of KERNEL_VERSION_NUMBER < 0x030200 */ +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* end of KERNEL_VERSION_NUMBER < 0x030200 */ + +#ifdef CONFIG_USERSPACE +#include +#include +#endif /* end of CONFIG_USERSPACE */ + +#if KERNEL_VERSION_NUMBER >= 0x030300 /* version 3.3.0 */ +#include +#endif /* end of KERNEL_VERSION_NUMBER > 0x030300 */ + +#ifdef CONFIG_ARM_MPU +#if KERNEL_VERSION_NUMBER < 0x030200 /* version 3.2.0 */ +#include +#elif KERNEL_VERSION_NUMBER < 0x030400 /* version 3.4.0 */ +#include +#else /* > 3.4.0 */ +#include +#endif +#endif + +#ifdef signbit /* probably since Zephyr v3.5.0 a new picolib is included */ +#define BH_HAS_SIGNBIT 1 +#endif + +#ifndef BH_PLATFORM_ZEPHYR +#define BH_PLATFORM_ZEPHYR +#endif + +#include + +#ifndef PATH_MAX +#define PATH_MAX 256 +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +/* Synchronization primitives for usermode. + * The macros are prefixed with 'z' because when building + * with WAMR_BUILD_LIBC_WASI the same functions are defined, + * and used in the sandboxed-system-primitives (see locking.h) + */ +#ifdef CONFIG_USERSPACE +#define zmutex_t struct sys_mutex +#define zmutex_init(mtx) sys_mutex_init(mtx) +#define zmutex_lock(mtx, timeout) sys_mutex_lock(mtx, timeout) +#define zmutex_unlock(mtx) sys_mutex_unlock(mtx) + +#define zsem_t struct sys_sem +#define zsem_init(sem, init_count, limit) sys_sem_init(sem, init_count, limit) +#define zsem_give(sem) sys_sem_give(sem) +#define zsem_take(sem, timeout) sys_sem_take(sem, timeout) +#define zsem_count_get(sem) sys_sem_count_get(sem) +#else /* else of CONFIG_USERSPACE */ +#define zmutex_t struct k_mutex +#define zmutex_init(mtx) k_mutex_init(mtx) +#define zmutex_lock(mtx, timeout) k_mutex_lock(mtx, timeout) +#define zmutex_unlock(mtx) k_mutex_unlock(mtx) + +#define zsem_t struct k_sem +#define zsem_init(sem, init_count, limit) k_sem_init(sem, init_count, limit) +#define zsem_give(sem) k_sem_give(sem) +#define zsem_take(sem, timeout) k_sem_take(sem, timeout) +#define zsem_count_get(sem) k_sem_count_get(sem) +#endif /* end of CONFIG_USERSPACE */ + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 7 + +typedef struct k_thread korp_thread; +typedef korp_thread *korp_tid; +typedef zmutex_t korp_mutex; +typedef unsigned int korp_sem; + +/* korp_rwlock is used in platform_api_extension.h, + we just define the type to make the compiler happy */ +struct os_thread_wait_node; +typedef struct os_thread_wait_node *os_thread_wait_list; +typedef struct korp_cond { + zmutex_t wait_list_lock; + os_thread_wait_list thread_wait_list; +} korp_cond; + +typedef struct { + struct k_mutex mtx; // Mutex for exclusive access + struct k_sem sem; // Semaphore for shared access + int read_count; // Number of readers +} korp_rwlock; + +// TODO: Conform to Zephyr POSIX definition of rwlock: +// struct posix_rwlock { +// struct k_sem rd_sem; +// struct k_sem wr_sem; +// struct k_sem reader_active; /* blocks WR till reader has acquired lock */ +// k_tid_t wr_owner; +// }; + +#ifndef Z_TIMEOUT_MS +#define Z_TIMEOUT_MS(ms) ms +#endif + +/* clang-format off */ +void abort(void); +size_t strspn(const char *s, const char *accept); +size_t strcspn(const char *s, const char *reject); + +/* math functions which are not provided by os with minimal libc */ +#if defined(CONFIG_MINIMAL_LIBC) +double atan(double x); +double atan2(double y, double x); +double sqrt(double x); +double floor(double x); +double ceil(double x); +double fmin(double x, double y); +double fmax(double x, double y); +double rint(double x); +double fabs(double x); +double trunc(double x); +float sqrtf(float x); +float floorf(float x); +float ceilf(float x); +float fminf(float x, float y); +float fmaxf(float x, float y); +float rintf(float x); +float fabsf(float x); +float truncf(float x); +int isnan_double(double x); +int isnan_float(float x); +#define isnan(x) (sizeof(x) == sizeof(double) ? isnan_double((double)x) : isnan_float(x)) +double pow(double x, double y); +double scalbn(double x, int n); + +#ifndef BH_HAS_SIGNBIT +int signbit_double(double x); +int signbit_float(float x); +#define signbit(x) (sizeof(x) == sizeof(double) ? signbit_double((double)x) : signbit_float(x)) +#endif + +unsigned long long int strtoull(const char *nptr, char **endptr, int base); +double strtod(const char *nptr, char **endptr); +float strtof(const char *nptr, char **endptr); +#else +#include +#endif /* CONFIG_MINIMAL_LIBC */ + +/* clang-format on */ + +#if KERNEL_VERSION_NUMBER >= 0x030100 /* version 3.1.0 */ +#define BH_HAS_SQRT +#define BH_HAS_SQRTF +#endif + +/** + * @brief Allocate executable memory + * + * @param size size of the memory to be allocated + * + * @return the address of the allocated memory if not NULL + */ +typedef void *(*exec_mem_alloc_func_t)(unsigned int size); + +/** + * @brief Release executable memory + * + * @param the address of the executable memory to be released + */ +typedef void (*exec_mem_free_func_t)(void *addr); + +/* Below function are called by external project to set related function + * pointers that will be used to malloc/free executable memory. Otherwise + * default mechanise will be used. + */ +void +set_exec_mem_alloc_func(exec_mem_alloc_func_t alloc_func, + exec_mem_free_func_t free_func); + +/* The below types are used in platform_api_extension.h, + we just define them to make the compiler happy */ +typedef int os_dir_stream; +typedef int os_raw_file_handle; + +#define OS_DIR_STREAM_INVALID 0 + +// handle for file system descriptor +typedef struct zephyr_fs_desc { + char *path; + union { + struct fs_file_t file; + struct fs_dir_t dir; + }; + bool is_dir; + bool used; + uint32_t dir_index; // DSK: supprt for rewind and seek +} zephyr_fs_desc; + +// definition of zephyr_handle +typedef struct zephyr_handle { + int fd; + bool is_sock; +} zephyr_handle; + +typedef struct zephyr_handle *os_file_handle; +#define bh_socket_t zephyr_handle * + +typedef struct zsock_pollfd os_poll_file_handle; +typedef unsigned int os_nfds_t; + +// Some of these definitions will throw warning for macros +// redefinition if CONFIG_POSIX_API=y, but it's fine. +// Warning: the CONFIG_POSIX_API will surely be deprecated and +// split into more macros, so we may use some ifdefs to avoid +// the warning in the future. +#define POLLIN ZSOCK_POLLIN +#define POLLPRI ZSOCK_POLLPRI +#define POLLOUT ZSOCK_POLLOUT +#define POLLERR ZSOCK_POLLERR +#define POLLHUP ZSOCK_POLLHUP +#define POLLNVAL ZSOCK_POLLNVAL + +#define FIONREAD ZFD_IOCTL_FIONREAD + +typedef struct timespec os_timespec; + +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 1 +#endif + +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 4 +#endif + +static inline int +os_sched_yield(void) +{ + k_yield(); + return 0; +} + +static inline os_file_handle +os_get_invalid_handle(void) +{ + return NULL; +} + +static inline int +os_getpagesize() +{ +#ifdef CONFIG_MMU + return CONFIG_MMU_PAGE_SIZE; +#else + /* Return a default page size if the MMU is not enabled */ + return 4096; /* 4KB */ +#endif +} + +#endif diff --git a/core/shared/platform/zephyr/shared_platform.cmake b/core/shared/platform/zephyr/shared_platform.cmake index cce0df72a1..f424b97204 100644 --- a/core/shared/platform/zephyr/shared_platform.cmake +++ b/core/shared/platform/zephyr/shared_platform.cmake @@ -1,13 +1,27 @@ -# 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}) - -include_directories(${PLATFORM_SHARED_DIR}) -include_directories(${PLATFORM_SHARED_DIR}/../include) - - -file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) - -set (PLATFORM_SHARED_SOURCE ${source_all}) - +# 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_ZEPHYR) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +if(${CONFIG_MINIMAL_LIBC}) + include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake) + set (source_all ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) +endif() + +if (NOT WAMR_BUILD_LIBC_WASI EQUAL 1) + list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_socket.c) + list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_file.c) + list(REMOVE_ITEM source_all ${PLATFORM_SHARED_DIR}/zephyr_clock.c) +else() + include (${CMAKE_CURRENT_LIST_DIR}/../common/libc-util/platform_common_libc_util.cmake) + set(source_all ${source_all} ${PLATFORM_COMMON_LIBC_UTIL_SOURCE}) +endif () + +set (PLATFORM_SHARED_SOURCE ${source_all}) diff --git a/core/shared/platform/zephyr/zephyr_clock.c b/core/shared/platform/zephyr/zephyr_clock.c new file mode 100644 index 0000000000..f8a8ab60ad --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_clock.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "platform_api_vmcore.h" +#include "libc_errno.h" + +#include + +/* Notes: + * We are using the same implementation for __WASI_CLOCK_REALTIME and + * __WASI_CLOCK_MONOTONIC, because it is a practical solution when there + * is no RTC or external time source available. + * The implementation is based on the Zephyr `k_cycle_get_32()` function or + * the 64bits variant if available. + * We could have used `k_uptime_get()` instead, but it is not as precise, + * it has a millisecond resolution or depend on CONFIG_SYS_CLOCK_TICKS_PER_SEC. + * Feel free to change the implementation if you have a better solution. + * May look at + * https://github.com/zephyrproject-rtos/zephyr/blob/main/lib/posix/options/clock.c + * for reference. + */ + +#define NANOSECONDS_PER_SECOND 1000000000ULL + +__wasi_errno_t +os_clock_res_get(__wasi_clockid_t clock_id, __wasi_timestamp_t *resolution) +{ + switch (clock_id) { + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + case __WASI_CLOCK_THREAD_CPUTIME_ID: + return __WASI_ENOTSUP; + case __WASI_CLOCK_REALTIME: + case __WASI_CLOCK_MONOTONIC: + *resolution = + NANOSECONDS_PER_SECOND / CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + return __WASI_ESUCCESS; + default: + return __WASI_EINVAL; + } +} + +__wasi_errno_t +os_clock_time_get(__wasi_clockid_t clock_id, __wasi_timestamp_t precision, + __wasi_timestamp_t *time) +{ + (void)precision; + + switch (clock_id) { + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + case __WASI_CLOCK_THREAD_CPUTIME_ID: + return __WASI_ENOTSUP; + case __WASI_CLOCK_REALTIME: + case __WASI_CLOCK_MONOTONIC: +#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER + *time = k_cycle_get_64(); +#else + *time = k_cycle_get_32(); +#endif + return __WASI_ESUCCESS; + default: + return __WASI_EINVAL; + } +} \ No newline at end of file diff --git a/core/shared/platform/zephyr/zephyr_file.c b/core/shared/platform/zephyr/zephyr_file.c new file mode 100644 index 0000000000..54b357e318 --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_file.c @@ -0,0 +1,1211 @@ +/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +#include "libc_errno.h" + +#include +#include + +#include +#include +#include +#include + +/* Notes: + * This is the implementation of a POSIX-like file system interface for Zephyr. + * To manage our file descriptors, we created a struct `zephyr_fs_desc` that + * represent a zephyr file descriptor and hold useful informations. + * We also created a file descriptor table to keep track of all the file + * descriptors. + * + * To pass the file descriptor reference to the higher level abstraction, we + * pass the index of the fd table to an `os_file_handle` struct. + * Then in the WASI implementation layer we can retrieve the file descriptor + * reference. + * + * We also fake the stdin, stdout and stderr file descriptors. + * We do not handle write on stdin and read on stdin, stdout and stderr. + */ + +// No OS API wrapper (Zephyr): +// file: +// off_t fs_tell(struct fs_file_t *zfp) +// file system: +// int fs_mount(struct fs_mount_t *mp) +// int fs_unmount(struct fs_mount_t *mp +// int fs_readmount(int *index, const char **name) +// int fs_statvfs(const char *path, struct fs_statvfs *stat) +// int fs_mkfs(int fs_type, uintptr_t dev_id, void *cfg, int flags) +// int fs_register(int type, const struct fs_file_system_t *fs) +// int fs_unregister(int type, const struct fs_file_system_t *fs) + +// We will take the maximum number of open files +// from the Zephyr POSIX configuration +#define CONFIG_WASI_MAX_OPEN_FILES CONFIG_ZVFS_OPEN_MAX + +static inline bool +os_is_virtual_fd(int fd) +{ + switch (fd) { + case STDIN_FILENO: + case STDOUT_FILENO: + case STDERR_FILENO: + return true; + default: + return false; + }; +} + +// Macro to retrieve a file system descriptor and check it's validity. +// fd's 0-2 are reserved for standard streams, hence the by-3 offsets. +#define GET_FILE_SYSTEM_DESCRIPTOR(fd, ptr) \ + do { \ + if (os_is_virtual_fd(fd)) { \ + ptr = NULL; \ + break; \ + } \ + if (fd < 3 || fd >= CONFIG_WASI_MAX_OPEN_FILES + 3) { \ + return __WASI_EBADF; \ + } \ + k_mutex_lock(&desc_array_mutex, K_FOREVER); \ + ptr = &desc_array[(int)fd - 3]; \ + if (!ptr->used) { \ + k_mutex_unlock(&desc_array_mutex); \ + return __WASI_EBADF; \ + } \ + k_mutex_unlock(&desc_array_mutex); \ + } while (0) + +// Array to keep track of file system descriptors. +static struct zephyr_fs_desc desc_array[CONFIG_WASI_MAX_OPEN_FILES]; + +// mutex to protect the file descriptor array +K_MUTEX_DEFINE(desc_array_mutex); + +static char prestat_dir[MAX_FILE_NAME + 1]; + +bool +build_absolute_path(char *abs_path, size_t abs_path_len, const char *path) +{ + if (!path) { + abs_path[0] = '\0'; + return false; + } + + size_t len1 = strlen(prestat_dir); + size_t len2 = strlen(path); + + if (len1 + 1 + len2 + 1 > abs_path_len) { + abs_path[0] = '\0'; // Empty string on error + return false; // Truncation would occur + } + + snprintf(abs_path, abs_path_len, "%s/%s", prestat_dir, path); + return true; +} + +static struct zephyr_fs_desc * +zephyr_fs_alloc_obj(bool is_dir, const char *path, int *index) +{ + struct zephyr_fs_desc *ptr = NULL; + *index = -1; // give a default value to index in case table is full + + k_mutex_lock(&desc_array_mutex, K_FOREVER); + for (int i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) { + if (desc_array[i].used == false) { + ptr = &desc_array[i]; + ptr->used = true; + ptr->is_dir = is_dir; + ptr->dir_index = 0; + size_t path_len = strlen(path) + 1; + ptr->path = BH_MALLOC(path_len); + if (ptr->path == NULL) { + ptr->used = false; + k_mutex_unlock(&desc_array_mutex); + return NULL; + } + strcpy(ptr->path, path); + *index = i + 3; + break; + } + } + + k_mutex_unlock(&desc_array_mutex); + + if (ptr == NULL) { + printk("Error: all file descriptor slots are in use (max = %d)\n", + CONFIG_WASI_MAX_OPEN_FILES); + } + + return ptr; +} + +static inline void +zephyr_fs_free_obj(struct zephyr_fs_desc *ptr) +{ + BH_FREE(ptr->path); + ptr->path = NULL; + ptr->used = false; +} + +/* /!\ Needed for socket to work */ +__wasi_errno_t +os_fstat(os_file_handle handle, struct __wasi_filestat_t *buf) +{ + struct zephyr_fs_desc *ptr = NULL; + int socktype, rc; + + if (!handle->is_sock) { + + if (os_is_virtual_fd(handle->fd)) { + buf->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + buf->st_size = 0; + buf->st_atim = 0; + buf->st_mtim = 0; + buf->st_ctim = 0; + return __WASI_ESUCCESS; + } + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // Get file information using Zephyr's fs_stat function + struct fs_dirent entry; + rc = fs_stat(ptr->path, &entry); + if (rc < 0) { + return convert_errno(-rc); + } + + // Fill in the __wasi_filestat_t structure + buf->st_dev = 0; // Zephyr's fs_stat doesn't provide a device ID + buf->st_ino = 0; // Zephyr's fs_stat doesn't provide an inode number + buf->st_filetype = entry.type == FS_DIR_ENTRY_DIR + ? __WASI_FILETYPE_DIRECTORY + : __WASI_FILETYPE_REGULAR_FILE; + buf->st_nlink = 1; // Zephyr's fs_stat doesn't provide a link count + buf->st_size = entry.size; + buf->st_atim = 0; // Zephyr's fs_stat doesn't provide timestamps + buf->st_mtim = 0; + buf->st_ctim = 0; + + return __WASI_ESUCCESS; + + // return os_fstatat(handle, ptr->path, buf, 0); + } + else { + // socklen_t socktypelen = sizeof(socktype); + // rc = zsock_getsockopt(handle->fd, SOL_SOCKET, SO_TYPE, &socktype, + // &socktypelen); Using `zsock_getsockopt` will add a dependency on the + // network stack + // TODO: may add a type to the `zephyr_handle`. + rc = 1; + socktype = SOCK_STREAM; + if (rc < 0) { + return convert_errno(-rc); + } + + switch (socktype) { + case SOCK_DGRAM: + buf->st_filetype = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + buf->st_filetype = __WASI_FILETYPE_UNKNOWN; + break; + } + buf->st_size = 0; + buf->st_atim = 0; + buf->st_mtim = 0; + buf->st_ctim = 0; + return __WASI_ESUCCESS; + } +} + +__wasi_errno_t +os_fstatat(os_file_handle handle, const char *path, + struct __wasi_filestat_t *buf, __wasi_lookupflags_t lookup_flags) +{ + struct fs_dirent entry; + int rc; + + if (handle->fd < 0) { + return __WASI_EBADF; + } + + char abs_path[MAX_FILE_NAME + 1]; + + if (handle == NULL) { + return __WASI_EINVAL; // Or another appropriate error code + } + + if (!build_absolute_path(abs_path, sizeof(abs_path), path)) { + return __WASI_ENOMEM; + } + + // Get file information using Zephyr's fs_stat function + rc = fs_stat(abs_path, &entry); + if (rc < 0) { + return convert_errno(-rc); + } + + // Fill in the __wasi_filestat_t structure + buf->st_dev = 0; // Zephyr's fs_stat doesn't provide a device ID + // DSK: setting this to 0, in addition to d_ino = 1 causes failures with + // readdir() So, here's a hack to to avoid this. + buf->st_ino = 1; // Zephyr's fs_stat doesn't provide an inode number. + buf->st_filetype = entry.type == FS_DIR_ENTRY_DIR + ? __WASI_FILETYPE_DIRECTORY + : __WASI_FILETYPE_REGULAR_FILE; + buf->st_nlink = 1; // Zephyr's fs_stat doesn't provide a link count + buf->st_size = entry.size; + buf->st_atim = 0; // Zephyr's fs_stat doesn't provide timestamps + buf->st_mtim = 0; + buf->st_ctim = 0; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_fdflags(os_file_handle handle, __wasi_fdflags_t *flags) +{ + struct zephyr_fs_desc *ptr = NULL; + + if (os_is_virtual_fd(handle->fd)) { + *flags = 0; + return __WASI_ESUCCESS; + } + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + if ((ptr->file.flags & FS_O_APPEND) != 0) { + *flags |= __WASI_FDFLAG_APPEND; + } + /* Others flags: + * - __WASI_FDFLAG_DSYNC + * - __WASI_FDFLAG_RSYNC + * - __WASI_FDFLAG_SYNC + * - __WASI_FDFLAG_NONBLOCK + * Have no equivalent in Zephyr. + */ + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_set_fdflags(os_file_handle handle, __wasi_fdflags_t flags) +{ + if (os_is_virtual_fd(handle->fd)) { + return __WASI_ESUCCESS; + } + + struct zephyr_fs_desc *ptr = NULL; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + if ((flags & __WASI_FDFLAG_APPEND) != 0) { + ptr->file.flags |= FS_O_APPEND; + } + /* Same as above */ + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fdatasync(os_file_handle handle) +{ + return os_fsync(handle); +} + +__wasi_errno_t +os_fsync(os_file_handle handle) +{ + if (os_is_virtual_fd(handle->fd)) { + return __WASI_ESUCCESS; + } + + struct zephyr_fs_desc *ptr = NULL; + int rc = 0; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + if (ptr->is_dir) { + return __WASI_EISDIR; + } + + rc = fs_sync(&ptr->file); + if (rc < 0) { + return convert_errno(-rc); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_open_preopendir(const char *path, os_file_handle *out) +{ + int rc, index; + struct zephyr_fs_desc *ptr; + + *out = BH_MALLOC(sizeof(struct zephyr_handle)); + if (*out == NULL) { + return __WASI_ENOMEM; + } + + ptr = zephyr_fs_alloc_obj(true, path, &index); + if (ptr == NULL) { + BH_FREE(*out); + return __WASI_EMFILE; + } + + fs_dir_t_init(&ptr->dir); + + rc = fs_opendir(&ptr->dir, path); + if (rc < 0) { + zephyr_fs_free_obj(ptr); + BH_FREE(*out); + return convert_errno(-rc); + } + + (*out)->fd = index; + (*out)->is_sock = false; + + strncpy(prestat_dir, path, MAX_FILE_NAME + 1); + prestat_dir[MAX_FILE_NAME] = '\0'; + + return __WASI_ESUCCESS; +} + +static int +wasi_flags_to_zephyr(__wasi_oflags_t oflags, __wasi_fdflags_t fd_flags, + __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode access_mode) +{ + int mode = 0; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + mode |= FS_O_CREATE; + } + if (((oflags & __WASI_O_EXCL) != 0) || ((oflags & __WASI_O_TRUNC) != 0) + || ((oflags & __WASI_O_DIRECTORY) != 0)) { + /* Zephyr is not POSIX no equivalent for these flags */ + /* __WASI_O_DIRECTORY: Open shouldn't handle directories */ + // TODO: log warning + } + + // Convert file descriptor flags. + if ((fd_flags & __WASI_FDFLAG_APPEND) != 0) { + mode |= FS_O_APPEND; + } + if (((fd_flags & __WASI_FDFLAG_DSYNC) != 0) + || ((fd_flags & __WASI_FDFLAG_RSYNC) != 0) + || ((fd_flags & __WASI_FDFLAG_SYNC) != 0) + || ((fd_flags & __WASI_FDFLAG_NONBLOCK) != 0)) { + /* Zephyr is not POSIX no equivalent for these flags */ + // TODO: log warning + } + + // Convert lookup flag. + if ((lookup_flags & __WASI_LOOKUP_SYMLINK_FOLLOW) == 0) { + /* Zephyr is not POSIX no equivalent for these flags */ + // TODO: log warning + return __WASI_ENOTSUP; + } + + // Convert access mode. + switch (access_mode) { + case WASI_LIBC_ACCESS_MODE_READ_WRITE: + mode |= FS_O_RDWR; + break; + case WASI_LIBC_ACCESS_MODE_READ_ONLY: + mode |= FS_O_READ; + break; + case WASI_LIBC_ACCESS_MODE_WRITE_ONLY: + mode |= FS_O_WRITE; + break; + default: + // TODO: log warning + break; + } + return mode; +} + +__wasi_errno_t +os_openat(os_file_handle handle, const char *path, __wasi_oflags_t oflags, + __wasi_fdflags_t fd_flags, __wasi_lookupflags_t lookup_flags, + wasi_libc_file_access_mode access_mode, os_file_handle *out) +{ + /* + * `handle` will be unused because zephyr doesn't expose an openat + * function and don't seem to have the concept of relative path. + * We fill `out` with a new file descriptor. + */ + int rc, index; + struct zephyr_fs_desc *ptr = NULL; + char abs_path[MAX_FILE_NAME + 1]; + + *out = BH_MALLOC(sizeof(struct zephyr_handle)); + if (*out == NULL) { + return __WASI_ENOMEM; + } + + if (!build_absolute_path(abs_path, sizeof(abs_path), path)) { + BH_FREE(*out); + return __WASI_ENOMEM; + } + + // Treat directories as a special case + bool is_dir = oflags & __WASI_O_DIRECTORY; + + ptr = zephyr_fs_alloc_obj(is_dir, abs_path, &index); + if (!ptr && (index < 0)) { + BH_FREE(*out); + return __WASI_EMFILE; + } + + if (is_dir) { + fs_dir_t_init(&ptr->dir); + // fs_opendir() is called in libc later - don't call here + } + else { + // Is a file + int zmode = + wasi_flags_to_zephyr(oflags, fd_flags, lookup_flags, access_mode); + fs_file_t_init(&ptr->file); + rc = fs_open(&ptr->file, abs_path, zmode); + + if (rc < 0) { + zephyr_fs_free_obj(ptr); + BH_FREE(*out); + return convert_errno(-rc); + } + } + + (*out)->fd = index; + (*out)->is_sock = false; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_file_get_access_mode(os_file_handle handle, + wasi_libc_file_access_mode *access_mode) +{ + + if (handle->fd == STDIN_FILENO) { + *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY; + return __WASI_ESUCCESS; + } + else if (handle->fd == STDOUT_FILENO || handle->fd == STDERR_FILENO) { + *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY; + return __WASI_ESUCCESS; + } + + struct zephyr_fs_desc *ptr = NULL; + + if (handle->is_sock) { + // for socket we can use the following code + // TODO: Need to determine better logic + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + return __WASI_ESUCCESS; + } + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + if (ptr->is_dir) { + // DSK: is this actually the correct mode for a dir? + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + return __WASI_ESUCCESS; + } + + if ((ptr->file.flags & FS_O_RDWR) != 0) { + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + } + else if ((ptr->file.flags & FS_O_READ) != 0) { + *access_mode = WASI_LIBC_ACCESS_MODE_READ_ONLY; + } + else if ((ptr->file.flags & FS_O_WRITE) != 0) { + *access_mode = WASI_LIBC_ACCESS_MODE_WRITE_ONLY; + } + else { + // we return read/write by default + *access_mode = WASI_LIBC_ACCESS_MODE_READ_WRITE; + } + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_close(os_file_handle handle, bool is_stdio) +{ + int rc; + struct zephyr_fs_desc *ptr = NULL; + + if (is_stdio) + return __WASI_ESUCCESS; + + if (handle->is_sock) { + rc = zsock_close(handle->fd); + } + // Handle is assumed to be a file descriptor + else { + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + rc = ptr->is_dir ? fs_closedir(&ptr->dir) : fs_close(&ptr->file); + zephyr_fs_free_obj(ptr); // free in any case. + } + + BH_FREE(handle); + if (rc < 0) { + return convert_errno(-rc); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_preadv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nread) +{ + if (handle->fd == STDIN_FILENO) { + return __WASI_ENOSYS; + } + + struct zephyr_fs_desc *ptr = NULL; + int rc; + ssize_t total_read = 0; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // Seek to the offset + rc = fs_seek(&ptr->file, offset, FS_SEEK_SET); + if (rc < 0) { + return convert_errno(-rc); + } + + // Read data into each buffer + for (int i = 0; i < iovcnt; i++) { + ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len); + if (bytes_read < 0) { + return convert_errno(-bytes_read); + } + + total_read += bytes_read; + + /* If we read less than we asked for, stop reading + bytes_read being a non-negative value was already checked */ + if ((size_t)bytes_read < iov[i].buf_len) { + break; + } + } + + *nread = total_read; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_pwritev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + __wasi_filesize_t offset, size_t *nwritten) +{ + struct zephyr_fs_desc *ptr = NULL; + ssize_t total_written = 0; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // Seek to the offset + int rc = fs_seek(&ptr->file, offset, FS_SEEK_SET); + if (rc < 0) { + return convert_errno(-rc); + } + + // Write data from each buffer + for (int i = 0; i < iovcnt; i++) { + ssize_t bytes_written = + fs_write(&ptr->file, iov[i].buf, iov[i].buf_len); + if (bytes_written < 0) { + return convert_errno(-bytes_written); + } + + total_written += bytes_written; + + /* If we wrote less than we asked for, stop writing + bytes_read being a non-negative value was already checked */ + if ((size_t)bytes_written < iov[i].buf_len) { + break; + } + } + + *nwritten = total_written; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readv(os_file_handle handle, const struct __wasi_iovec_t *iov, int iovcnt, + size_t *nread) +{ + struct zephyr_fs_desc *ptr = NULL; + ssize_t total_read = 0; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // Read data into each buffer + for (int i = 0; i < iovcnt; i++) { + ssize_t bytes_read = fs_read(&ptr->file, iov[i].buf, iov[i].buf_len); + if (bytes_read < 0) { + // If an error occurred, return it + return convert_errno(-bytes_read); + } + + total_read += bytes_read; + + /* If we read less than we asked for, stop reading + bytes_read being a non-negative value was already checked */ + if ((size_t)bytes_read < iov[i].buf_len) { + break; + } + } + + *nread = total_read; + + return __WASI_ESUCCESS; +} + +/* With wasi-libc we need to redirect write on stdout/err to printf */ +// TODO: handle write on stdin +__wasi_errno_t +os_writev(os_file_handle handle, const struct __wasi_ciovec_t *iov, int iovcnt, + size_t *nwritten) +{ + ssize_t total_written = 0; + + if (os_is_virtual_fd(handle->fd)) { + FILE *fd = (handle->fd == STDERR_FILENO) ? stderr : stdout; + for (int i = 0; i < iovcnt; i++) { + ssize_t bytes_written = fwrite(iov[i].buf, 1, iov[i].buf_len, fd); + if (bytes_written < 0) + return convert_errno(-bytes_written); + total_written += bytes_written; + } + + *nwritten = total_written; + return __WASI_ESUCCESS; + } + + struct zephyr_fs_desc *ptr = NULL; + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // Write data from each buffer + for (int i = 0; i < iovcnt; i++) { + ssize_t bytes_written = + fs_write(&ptr->file, iov[i].buf, iov[i].buf_len); + if (bytes_written < 0) { + return convert_errno(-bytes_written); + } + + total_written += bytes_written; + + /* If we wrote less than we asked for, stop writing + bytes_read being a non-negative value was already checked */ + if ((size_t)bytes_written < iov[i].buf_len) { + break; + } + } + + *nwritten = total_written; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fallocate(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_ftruncate(os_file_handle handle, __wasi_filesize_t size) +{ + + if (os_is_virtual_fd(handle->fd)) { + return __WASI_EINVAL; + } + + struct zephyr_fs_desc *ptr = NULL; + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + int rc = fs_truncate(&ptr->file, (off_t)size); + if (rc < 0) { + return convert_errno(-rc); + } + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_futimens(os_file_handle handle, __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_utimensat(os_file_handle handle, const char *path, + __wasi_timestamp_t access_time, + __wasi_timestamp_t modification_time, __wasi_fstflags_t fstflags, + __wasi_lookupflags_t lookup_flags) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_readlinkat(os_file_handle handle, const char *path, char *buf, + size_t bufsize, size_t *nread) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_linkat(os_file_handle from_handle, const char *from_path, + os_file_handle to_handle, const char *to_path, + __wasi_lookupflags_t lookup_flags) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_symlinkat(const char *old_path, os_file_handle handle, const char *new_path) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_mkdirat(os_file_handle handle, const char *path) +{ + struct zephyr_fs_desc *ptr = NULL; + int index, rc; + char abs_path[MAX_FILE_NAME + 1]; + + if (handle == NULL) { + return __WASI_EINVAL; // Or another appropriate error code + } + + if (!build_absolute_path(abs_path, sizeof(abs_path), path)) { + return __WASI_ENOMEM; + } + + rc = fs_mkdir(abs_path); + if (rc < 0) { + return convert_errno(-rc); + } + + ptr = zephyr_fs_alloc_obj(true, abs_path, &index); + if (!ptr) { + fs_unlink(abs_path); + return __WASI_EMFILE; + } + fs_dir_t_init(&ptr->dir); + + handle->fd = index; + handle->is_sock = false; + + return __WASI_ESUCCESS; +} + +// DSK: Somewhere along the WASI libc implementation path, the knowledge +// was lost that `old_handle` and `new_handle` refer to directories that +// contain the files to be renamed, rather than the file fds themselves: +// +// __wasilibc_nocwd_renameat(old_dirfd, old_relative_path, +// new_dirfd, new_relative_path); +// +// Therefore we won't mess with the supplied fd's, and work only off +// of the supplied paths. Note: this will change when more than one +// pre-opened dir is supported in the future. +__wasi_errno_t +os_renameat(os_file_handle old_handle, const char *old_path, + os_file_handle new_handle, const char *new_path) +{ + // directories, safe to ignore + (void)old_handle; + (void)new_handle; + + char abs_old_path[MAX_FILE_NAME + 1]; + char abs_new_path[MAX_FILE_NAME + 1]; + + if (!build_absolute_path(abs_old_path, sizeof(abs_old_path), old_path)) { + return __WASI_ENOMEM; + } + + if (!build_absolute_path(abs_new_path, sizeof(abs_new_path), new_path)) { + return __WASI_ENOMEM; + } + + // Attempt to perform the rename + int rc = fs_rename(abs_old_path, abs_new_path); + if (rc < 0) { + return convert_errno(-rc); + } + + // If there is an allocated fd in our table, update the descriptor table + // entry DSK: better approach here? + k_mutex_lock(&desc_array_mutex, K_FOREVER); + for (int i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) { + struct zephyr_fs_desc *ptr = &desc_array[i]; + if (ptr->used && ptr->path && strcmp(ptr->path, abs_old_path) == 0) { + size_t new_path_len = strlen(abs_new_path) + 1; + char *new_path_copy = BH_MALLOC(new_path_len); + if (new_path_copy != NULL) { + strcpy(new_path_copy, abs_new_path); + BH_FREE(ptr->path); + ptr->path = new_path_copy; + } + else { + k_mutex_unlock(&desc_array_mutex); + return __WASI_ENOMEM; + } + break; // Only one descriptor should match + } + } + k_mutex_unlock(&desc_array_mutex); + + return __WASI_ESUCCESS; +} + +// DSK: Same thing as renameat: `handle` refers to the containing directory, +// not the file handle to unlink. We ignore the handle and use the path +// exclusively. +// +// TODO: is there anything we need to do in case is_dir=true? +__wasi_errno_t +os_unlinkat(os_file_handle handle, const char *path, bool is_dir) +{ + (void)handle; + + char abs_path[MAX_FILE_NAME + 1]; + + if (!build_absolute_path(abs_path, sizeof(abs_path), path)) { + return __WASI_ENOMEM; + } + + int rc = fs_unlink(abs_path); + if (rc < 0) { + return convert_errno(-rc); + } + + // Search for any active descriptor referencing this path and free it. + k_mutex_lock(&desc_array_mutex, K_FOREVER); + for (int i = 0; i < CONFIG_WASI_MAX_OPEN_FILES; i++) { + struct zephyr_fs_desc *ptr = &desc_array[i]; + if (ptr->used && ptr->path && strcmp(ptr->path, abs_path) == 0) { + zephyr_fs_free_obj(ptr); + break; + } + } + k_mutex_unlock(&desc_array_mutex); + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_lseek(os_file_handle handle, __wasi_filedelta_t offset, + __wasi_whence_t whence, __wasi_filesize_t *new_offset) +{ + + if (os_is_virtual_fd(handle->fd)) { + return __WASI_ESPIPE; // Seeking not supported on character streams + } + + struct zephyr_fs_desc *ptr = NULL; + int zwhence; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + + // They have the same value but this is more explicit + switch (whence) { + case __WASI_WHENCE_SET: + zwhence = FS_SEEK_SET; + break; + case __WASI_WHENCE_CUR: + zwhence = FS_SEEK_CUR; + break; + case __WASI_WHENCE_END: + zwhence = FS_SEEK_END; + break; + default: + return __WASI_EINVAL; + } + + off_t rc = fs_seek(&ptr->file, (off_t)offset, zwhence); + if (rc < 0) { + return convert_errno(-rc); + } + + *new_offset = (__wasi_filesize_t)rc; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_fadvise(os_file_handle handle, __wasi_filesize_t offset, + __wasi_filesize_t length, __wasi_advice_t advice) +{ + return __WASI_ENOSYS; +} + +__wasi_errno_t +os_isatty(os_file_handle handle) +{ + if (os_is_virtual_fd(handle->fd)) { + return __WASI_ESUCCESS; + } + + return __WASI_ENOTTY; +} + +os_file_handle +os_convert_stdin_handle(os_raw_file_handle raw_stdin) +{ + os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle)); + if (!handle) + return NULL; + + handle->fd = STDIN_FILENO; + handle->is_sock = false; + return handle; +} + +os_file_handle +os_convert_stdout_handle(os_raw_file_handle raw_stdout) +{ + os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle)); + if (!handle) + return NULL; + + handle->fd = STDOUT_FILENO; + handle->is_sock = false; + return handle; +} + +os_file_handle +os_convert_stderr_handle(os_raw_file_handle raw_stderr) +{ + os_file_handle handle = BH_MALLOC(sizeof(struct zephyr_handle)); + if (!handle) + return NULL; + + handle->fd = STDERR_FILENO; + handle->is_sock = false; + return handle; +} + +__wasi_errno_t +os_fdopendir(os_file_handle handle, os_dir_stream *dir_stream) +{ + /* Here we assume that either mdkdir or preopendir was called + * before otherwise function will fail. + */ + struct zephyr_fs_desc *ptr = NULL; + + GET_FILE_SYSTEM_DESCRIPTOR(handle->fd, ptr); + if (!ptr->is_dir) { + return __WASI_ENOTDIR; + } + + int rc = fs_opendir(&ptr->dir, ptr->path); + if (rc < 0) { + return convert_errno(-rc); + } + + /* we store the fd in the `os_dir_stream` to use other function */ + *dir_stream = handle->fd; + + return __WASI_ESUCCESS; +} + +// DSK: simple open and close to rewind index. +__wasi_errno_t +os_rewinddir(os_dir_stream dir_stream) +{ + struct zephyr_fs_desc *ptr = NULL; + GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr); + + if (!ptr->is_dir) + return __WASI_ENOTDIR; + + int rc = fs_closedir(&ptr->dir); // Close current stream + if (rc < 0) + return convert_errno(-rc); + + rc = fs_opendir(&ptr->dir, ptr->path); // Reopen from start + if (rc < 0) + return convert_errno(-rc); + + ptr->dir_index = 0; // Reset virtual position tracker + return __WASI_ESUCCESS; +} + +// DSK: start from 0 and linear seek since there's no cookies in the zephyr fs +// TODO: duplicated code with rewinddir +__wasi_errno_t +os_seekdir(os_dir_stream dir_stream, __wasi_dircookie_t position) +{ + struct zephyr_fs_desc *ptr = NULL; + GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr); + + if (!ptr->is_dir) + return __WASI_ENOTDIR; + + int rc = fs_closedir(&ptr->dir); + if (rc < 0) + return convert_errno(-rc); + + rc = fs_opendir(&ptr->dir, ptr->path); + if (rc < 0) + return convert_errno(-rc); + + // Emulate seek by re-reading entries up to 'position' + struct fs_dirent tmp; + for (__wasi_dircookie_t i = 0; i < position; i++) { + rc = fs_readdir(&ptr->dir, &tmp); + if (rc < 0) + return convert_errno(-rc); + if (tmp.name[0] == '\0') + break; // End of directory + } + + ptr->dir_index = position; + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_readdir(os_dir_stream dir_stream, __wasi_dirent_t *entry, + const char **d_name) +{ + struct fs_dirent fs_entry; + struct zephyr_fs_desc *ptr = NULL; + GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr); + if (!ptr->is_dir) { + return __WASI_ENOTDIR; + } + + int rc = fs_readdir(&ptr->dir, &fs_entry); + if (rc < 0) { + return convert_errno(-rc); + } + + if (fs_entry.name[0] == '\0') { + // DSK: the caller expects the name buffer to be null + // when we've reached the end of the directory. + *d_name = NULL; + return __WASI_ESUCCESS; + } + + // DSK: emulated increasing value for rewinddir and seekdir + entry->d_next = ++ptr->dir_index; + + // DSK: A hack to get readdir working. This needs to be non-zero along with + // st_ino for the libc side of readdir to work correctly. + entry->d_ino = 1 + ptr->dir_index; + + entry->d_namlen = strlen(fs_entry.name); + entry->d_type = fs_entry.type == FS_DIR_ENTRY_DIR + ? __WASI_FILETYPE_DIRECTORY + : __WASI_FILETYPE_REGULAR_FILE; + + // DSK: name exists in fs_entry and we need to return it + static char name_buf[MAX_FILE_NAME + 1]; + strncpy(name_buf, fs_entry.name, MAX_FILE_NAME); + name_buf[MAX_FILE_NAME] = '\0'; + *d_name = name_buf; + + return __WASI_ESUCCESS; +} + +__wasi_errno_t +os_closedir(os_dir_stream dir_stream) +{ + struct zephyr_fs_desc *ptr = NULL; + + GET_FILE_SYSTEM_DESCRIPTOR(dir_stream, ptr); + if (!ptr->is_dir) { + return __WASI_ENOTDIR; + } + + int rc = fs_closedir(&ptr->dir); + zephyr_fs_free_obj(ptr); // free in any case. + if (rc < 0) { + return convert_errno(-rc); + } + + return __WASI_ESUCCESS; +} + +os_dir_stream +os_get_invalid_dir_stream() +{ + return OS_DIR_STREAM_INVALID; +} + +bool +os_is_dir_stream_valid(os_dir_stream *dir_stream) +{ + // DSK: this probably needs a check... + return false; +} + +bool +os_is_handle_valid(os_file_handle *handle) +{ + if (handle == NULL || *handle == NULL) { + return false; + } + return (*handle)->fd > -1; +} + +char * +os_realpath(const char *path, char *resolved_path) +{ + /* In fact we could implement a path resolving method, because every paths + * are at one point put into memory. + * We could then maintain a 'tree' to represent the file system. + * --> The file system root is easily accessable with: + * * (fs_dir_t) dir.mp->mnt_point + * * (fs_file_t) file.mp->mnt_point + * But we will just use absolute path for now. + */ + if ((!path) || (strlen(path) > PATH_MAX)) { + // Invalid input, path has to be valid and less than PATH_MAX + return NULL; + } + + return strncpy(resolved_path, path, PATH_MAX); +} + +bool +os_compare_file_handle(os_file_handle handle1, os_file_handle handle2) +{ + return handle1->fd == handle2->fd && handle1->is_sock == handle2->is_sock; +} + +bool +os_is_stdin_handle(os_file_handle handle) +{ + return (handle == (os_file_handle)stdin); +} + +bool +os_is_stdout_handle(os_file_handle handle) +{ + return (handle == (os_file_handle)stdout); +} + +bool +os_is_stderr_handle(os_file_handle handle) +{ + return (handle == (os_file_handle)stderr); +} diff --git a/core/shared/platform/zephyr/zephyr_platform.c b/core/shared/platform/zephyr/zephyr_platform.c new file mode 100644 index 0000000000..3280e542f6 --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_platform.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-FileCopyrightText: 2024 Siemens AG (For Zephyr usermode changes) + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +/* function pointers for executable memory management */ +static exec_mem_alloc_func_t exec_mem_alloc_func = NULL; +static exec_mem_free_func_t exec_mem_free_func = NULL; + +#if WASM_ENABLE_AOT != 0 +#ifdef CONFIG_ARM_MPU +/** + * This function will allow execute from sram region. + * This is needed for AOT code because by default all soc will + * disable the execute from SRAM. + */ +static void +disable_mpu_rasr_xn(void) +{ + uint32 index; + /* Kept the max index as 8 (irrespective of soc) because the sram + would most likely be set at index 2. */ + for (index = 0U; index < 8; index++) { + MPU->RNR = index; +#ifdef MPU_RASR_XN_Msk + if (MPU->RASR & MPU_RASR_XN_Msk) { + MPU->RASR |= ~MPU_RASR_XN_Msk; + } +#endif + } +} +#endif /* end of CONFIG_ARM_MPU */ +#endif + +#ifndef CONFIG_USERSPACE +static int +_stdout_hook_iwasm(int c) +{ + printk("%c", (char)c); + return 1; +} +#endif + +int +os_thread_sys_init(); + +void +os_thread_sys_destroy(); + +int +bh_platform_init() +{ +#ifndef CONFIG_USERSPACE + extern void __stdout_hook_install(int (*hook)(int)); + /* Enable printf() in Zephyr */ + __stdout_hook_install(_stdout_hook_iwasm); +#endif + +#if WASM_ENABLE_AOT != 0 +#ifdef CONFIG_ARM_MPU + /* Enable executable memory support */ + disable_mpu_rasr_xn(); +#endif +#endif + + return os_thread_sys_init(); +} + +void +bh_platform_destroy() +{ + os_thread_sys_destroy(); +} + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int +os_dumps_proc_mem_info(char *out, unsigned int size) +{ + return -1; +} + +#if 0 +struct out_context { + int count; +}; + +typedef int (*out_func_t)(int c, void *ctx); + +static int +char_out(int c, void *ctx) +{ + struct out_context *out_ctx = (struct out_context*)ctx; + out_ctx->count++; + return _stdout_hook_iwasm(c); +} + +int +os_vprintf(const char *fmt, va_list ap) +{ +#if 0 + struct out_context ctx = { 0 }; + cbvprintf(char_out, &ctx, fmt, ap); + return ctx.count; +#else + vprintk(fmt, ap); + return 0; +#endif +} +#endif + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); +#ifndef BH_VPRINTF + ret += vprintf(format, ap); +#else + ret += BH_VPRINTF(format, ap); +#endif + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ +#ifndef BH_VPRINTF + return vprintf(format, ap); +#else + return BH_VPRINTF(format, ap); +#endif +} + +#if KERNEL_VERSION_NUMBER <= 0x020400 /* version 2.4.0 */ +void +abort(void) +{ + int i = 0; + os_printf("%d\n", 1 / i); +} +#endif + +#if KERNEL_VERSION_NUMBER <= 0x010E01 /* version 1.14.1 */ +size_t +strspn(const char *s, const char *accept) +{ + os_printf("## unimplemented function %s called", __FUNCTION__); + return 0; +} + +size_t +strcspn(const char *s, const char *reject) +{ + os_printf("## unimplemented function %s called", __FUNCTION__); + return 0; +} +#endif + +void * +os_mmap(void *hint, size_t size, int prot, int flags, os_file_handle file) +{ + void *addr; + + if ((uint64)size >= UINT32_MAX) + return NULL; + + if (exec_mem_alloc_func) + addr = exec_mem_alloc_func((uint32)size); + else + addr = BH_MALLOC(size); + + if (addr) + memset(addr, 0, size); + return addr; +} + +void * +os_mremap(void *old_addr, size_t old_size, size_t new_size) +{ + return os_mremap_slow(old_addr, old_size, new_size); +} + +void +os_munmap(void *addr, size_t size) +{ + if (exec_mem_free_func) + exec_mem_free_func(addr); + else + BH_FREE(addr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush() +{ +#if defined(CONFIG_CPU_CORTEX_M7) && defined(CONFIG_ARM_MPU) +#if KERNEL_VERSION_NUMBER < 0x030300 /* version 3.3.0 */ + uint32 key; + key = irq_lock(); + SCB_CleanDCache(); + irq_unlock(key); +#else + sys_cache_data_flush_all(); +#endif +#elif defined(CONFIG_SOC_CVF_EM7D) && defined(CONFIG_ARC_MPU) \ + && defined(CONFIG_CACHE_FLUSHING) + __asm__ __volatile__("sync"); + z_arc_v2_aux_reg_write(_ARC_V2_DC_FLSH, BIT(0)); + __asm__ __volatile__("sync"); +#endif +} + +void +os_icache_flush(void *start, size_t len) +{ +#if KERNEL_VERSION_NUMBER >= 0x030300 /* version 3.3.0 */ + sys_cache_instr_flush_range(start, len); +#endif +} + +void +set_exec_mem_alloc_func(exec_mem_alloc_func_t alloc_func, + exec_mem_free_func_t free_func) +{ + exec_mem_alloc_func = alloc_func; + exec_mem_free_func = free_func; +} + +os_raw_file_handle +os_invalid_raw_handle(void) +{ + return -1; +} diff --git a/core/shared/platform/zephyr/zephyr_socket.c b/core/shared/platform/zephyr/zephyr_socket.c new file mode 100644 index 0000000000..56f37fc8e2 --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_socket.c @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_extension.h" +#include "platform_api_vmcore.h" + +#include +#include +#include + +#include +#include + +#include "libc_errno.h" +#include +#include + +// Static functions +static bool +textual_addr_to_sockaddr(const char *textual, int port, struct sockaddr *out, + socklen_t *out_len) +{ + struct sockaddr_in *v4; +#ifdef IPPROTO_IPV6 + struct sockaddr_in *v6; +#endif + + assert(textual); + + v4 = (struct sockaddr_in *)out; + if (zsock_inet_pton(AF_INET, textual, &v4->sin_addr.s_addr) == 1) { + v4->sin_family = AF_INET; + v4->sin_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } + +#ifdef IPPROTO_IPV6 + v6 = (struct sockaddr_in *)out; + if (zsock_inet_pton(AF_INET6, textual, &v6->sin6_addr.s6_addr) == 1) { + v6->sin6_family = AF_INET6; + v6->sin6_port = htons(port); + *out_len = sizeof(struct sockaddr_in); + return true; + } +#endif + + return false; +} + +static int +sockaddr_to_bh_sockaddr(const struct sockaddr *sockaddr, + bh_sockaddr_t *bh_sockaddr) +{ + switch (sockaddr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + + bh_sockaddr->port = ntohs(addr->sin_port); + bh_sockaddr->addr_buffer.ipv4 = ntohl(addr->sin_addr.s_addr); + bh_sockaddr->is_ipv4 = true; + return BHT_OK; + } +#ifdef IPPROTO_IPV6 + case AF_INET6: + { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + size_t i; + + bh_sockaddr->port = ntohs(addr->sin6_port); + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = addr->sin6_addr.s6_addr[i * 2] + | (addr->sin6_addr.s6_addr[i * 2 + 1] << 8); + bh_sockaddr->addr_buffer.ipv6[i] = ntohs(part_addr); + } + + bh_sockaddr->is_ipv4 = false; + return BHT_OK; + } +#endif + default: + errno = EAFNOSUPPORT; + return BHT_ERROR; + } +} + +static void +bh_sockaddr_to_sockaddr(const bh_sockaddr_t *bh_sockaddr, + struct sockaddr_storage *sockaddr, socklen_t *socklen) +{ + if (bh_sockaddr->is_ipv4) { + struct sockaddr_in *addr = (struct sockaddr_in *)sockaddr; + addr->sin_port = htons(bh_sockaddr->port); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = htonl(bh_sockaddr->addr_buffer.ipv4); + *socklen = sizeof(*addr); + } +#ifdef IPPROTO_IPV6 + else { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)sockaddr; + size_t i; + addr->sin6_port = htons(bh_sockaddr->port); + addr->sin6_family = AF_INET6; + + for (i = 0; i < sizeof(bh_sockaddr->addr_buffer.ipv6) + / sizeof(bh_sockaddr->addr_buffer.ipv6[0]); + i++) { + uint16 part_addr = htons(bh_sockaddr->addr_buffer.ipv6[i]); + addr->sin6_addr.s6_addr[i * 2] = 0xff & part_addr; + addr->sin6_addr.s6_addr[i * 2 + 1] = (0xff00 & part_addr) >> 8; + } + + *socklen = sizeof(*addr); + } +#endif +} + +static int +getaddrinfo_error_to_errno(int error) +{ + switch (error) { + case DNS_EAI_AGAIN: + return EAGAIN; + case DNS_EAI_FAIL: + return EFAULT; + case DNS_EAI_MEMORY: + return ENOMEM; + case DNS_EAI_SYSTEM: + return errno; + default: + return EINVAL; + } +} + +static int +is_addrinfo_supported(struct zsock_addrinfo *info) +{ + return + // Allow only IPv4 and IPv6 + (info->ai_family == AF_INET || info->ai_family == AF_INET6) + // Allow only UDP and TCP + && (info->ai_socktype == SOCK_DGRAM || info->ai_socktype == SOCK_STREAM) + && (info->ai_protocol == IPPROTO_TCP + || info->ai_protocol == IPPROTO_UDP); +} + +static int +os_socket_setbooloption(bh_socket_t socket, int level, int optname, + bool is_enabled) +{ + int option = (int)is_enabled; + + if (zsock_setsockopt(socket->fd, level, optname, &option, sizeof(option)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +static int +os_socket_getbooloption(bh_socket_t socket, int level, int optname, + bool *is_enabled) +{ + assert(is_enabled); + + int optval; + socklen_t optval_size = sizeof(optval); + + if (zsock_getsockopt(socket->fd, level, optname, &optval, &optval_size) + != 0) { + return BHT_ERROR; + } + *is_enabled = (bool)optval; + return BHT_OK; +} + +// Platform API implementation +int +os_socket_create(bh_socket_t *sock, bool is_ipv4, bool is_tcp) +{ + int af = is_ipv4 ? AF_INET : AF_INET6; + // now socket is a struct try *(sock)->fd + + *(sock) = BH_MALLOC(sizeof(zephyr_handle)); + + if (!*sock) { + return BHT_ERROR; + } + + if (is_tcp) { + (*sock)->fd = zsock_socket(af, SOCK_STREAM, IPPROTO_TCP); + } + else { + (*sock)->fd = + zsock_socket(af, SOCK_DGRAM, IPPROTO_UDP); // IPPROTO_UDP or 0 + } + + (*sock)->is_sock = true; + + if ((*sock)->fd == -1) { + BH_FREE(*sock); + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_bind(bh_socket_t socket, const char *host, int *port) +{ + struct sockaddr_storage addr = { 0 }; + socklen_t socklen; + int ret; + + assert(host); + assert(port); + + if (!textual_addr_to_sockaddr(host, *port, (struct sockaddr *)&addr, + &socklen)) { + return BHT_ERROR; + } + + // F_SETF_SETFD and FD_CLOEXEC are not defined in zephyr. + // SO_LINGER: Socket lingers on close (ignored, for compatibility) + + ret = zsock_bind(socket->fd, (struct sockaddr *)&addr, socklen); + if (ret < 0) { + return BHT_ERROR; + } + + socklen = sizeof(addr); + if (zsock_getsockname(socket->fd, (void *)&addr, &socklen) == -1) { + return BHT_ERROR; + } + + if (addr.ss_family == AF_INET) { // addr.sin_family + *port = ntohs(((struct sockaddr_in *)&addr)->sin_port); + } + else { +#ifdef IPPROTO_IPV6 + *port = ntohs(((struct sockaddr_in *)&addr)->sin6_port); +#else + return BHT_ERROR; +#endif + } + + return BHT_OK; +} + +int +os_socket_settimeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval timeout = { 0 }; + + timeout.tv_sec = timeout_us / 1000000; + timeout.tv_usec = timeout_us % 1000000; + + if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, + sizeof(timeout)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_listen(bh_socket_t socket, int max_client) +{ + return zsock_listen(socket->fd, max_client) != 0 ? BHT_ERROR : BHT_OK; +} + +int +os_socket_accept(bh_socket_t server_sock, bh_socket_t *sock, void *addr, + unsigned int *addrlen) +{ + *sock = BH_MALLOC(sizeof(zephyr_handle)); + if (!*sock) { + return BHT_ERROR; + } + + (*sock)->fd = zsock_accept(server_sock->fd, addr, addrlen); + + if ((*sock)->fd < 0) { + BH_FREE(*sock); + return BHT_ERROR; + } + + (*sock)->is_sock = true; + + return BHT_OK; +} + +int +os_socket_connect(bh_socket_t socket, const char *addr, int port) +{ + struct sockaddr_storage addr_in = { 0 }; + socklen_t socklen; + int ret; + + assert(addr); + + if (!textual_addr_to_sockaddr(addr, port, (struct sockaddr *)&addr_in, + &socklen)) { + return BHT_ERROR; + } + + ret = zsock_connect(socket->fd, (struct sockaddr *)&addr_in, socklen); + if (ret < 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_recv(bh_socket_t socket, void *buf, unsigned int len) +{ + return zsock_recv(socket->fd, buf, len, 0); +} + +int +os_socket_recv_from(bh_socket_t socket, void *buf, unsigned int len, int flags, + bh_sockaddr_t *src_addr) +{ + struct sockaddr_storage sock_addr = { 0 }; + socklen_t socklen = sizeof(sock_addr); + int ret; + + ret = zsock_recvfrom(socket->fd, buf, len, flags, + (struct sockaddr *)&sock_addr, &socklen); + + if (ret < 0) { + return BHT_ERROR; + } + + if (src_addr && socklen > 0) { + // zsock_recvfrom doesn't seem to set `addr->sa_family`, + // so we set it manually. + ((struct sockaddr *)&sock_addr)->sa_family = + src_addr->is_ipv4 == true ? AF_INET : AF_INET6; + + if (sockaddr_to_bh_sockaddr((struct sockaddr *)&sock_addr, src_addr) + == BHT_ERROR) { + return -1; + } + } + else if (src_addr) { + memset(src_addr, 0, sizeof(*src_addr)); + } + + return ret; +} + +int +os_socket_send(bh_socket_t socket, const void *buf, unsigned int len) +{ + return zsock_send(socket->fd, buf, len, 0); +} + +int +os_socket_send_to(bh_socket_t socket, const void *buf, unsigned int len, + int flags, const bh_sockaddr_t *dest_addr) +{ + struct sockaddr_storage addr = { 0 }; + socklen_t socklen; + + (void)bh_sockaddr_to_sockaddr(dest_addr, &addr, &socklen); + + return zsock_sendto(socket->fd, buf, len, flags, (struct sockaddr *)&addr, + socklen); +} + +int +os_socket_close(bh_socket_t socket) +{ + zsock_close(socket->fd); + + BH_FREE(socket); + + return BHT_OK; +} + +__wasi_errno_t +os_socket_shutdown(bh_socket_t socket) +{ + if (zsock_shutdown(socket->fd, ZSOCK_SHUT_RDWR) == -1) { + return convert_errno(errno); + } + return __WASI_ESUCCESS; +} + +int +os_socket_inet_network(bool is_ipv4, const char *cp, bh_ip_addr_buffer_t *out) +{ + if (!cp) + return BHT_ERROR; + + if (is_ipv4) { + if (zsock_inet_pton(AF_INET, cp, &out->ipv4) != 1) { + return BHT_ERROR; + } + /* Note: ntohl(INADDR_NONE) == INADDR_NONE */ + out->ipv4 = ntohl(out->ipv4); + } + else { +#ifdef IPPROTO_IPV6 + if (zsock_inet_pton(AF_INET6, cp, out->ipv6) != 1) { + return BHT_ERROR; + } + for (int i = 0; i < 8; i++) { + out->ipv6[i] = ntohs(out->ipv6[i]); + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + + return BHT_OK; +} + +int +os_socket_addr_resolve(const char *host, const char *service, + uint8_t *hint_is_tcp, uint8_t *hint_is_ipv4, + bh_addr_info_t *addr_info, size_t addr_info_size, + size_t *max_info_size) +{ + struct zsock_addrinfo hints = { 0 }, *res, *result; + int hints_enabled = hint_is_tcp || hint_is_ipv4; + int ret; + size_t pos = 0; + + if (hints_enabled) { + if (hint_is_ipv4) { + hints.ai_family = *hint_is_ipv4 ? AF_INET : AF_INET6; + } + if (hint_is_tcp) { + hints.ai_socktype = *hint_is_tcp ? SOCK_STREAM : SOCK_DGRAM; + } + } + + ret = zsock_getaddrinfo(host, strlen(service) == 0 ? NULL : service, + hints_enabled ? &hints : NULL, &result); + if (ret != BHT_OK) { + errno = getaddrinfo_error_to_errno(ret); + return BHT_ERROR; + } + + res = result; + + while (res) { + if (addr_info_size > pos) { + if (!is_addrinfo_supported(res)) { + res = res->ai_next; + continue; + } + + ret = + sockaddr_to_bh_sockaddr(res->ai_addr, &addr_info[pos].sockaddr); + + if (ret == BHT_ERROR) { + zsock_freeaddrinfo(result); + return BHT_ERROR; + } + + addr_info[pos].is_tcp = res->ai_socktype == SOCK_STREAM; + } + + pos++; + res = res->ai_next; + } + + *max_info_size = pos; + zsock_freeaddrinfo(result); + + return BHT_OK; +} + +int +os_socket_addr_local(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = zsock_getsockname(socket->fd, (struct sockaddr *)&addr_storage, + &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_addr_remote(bh_socket_t socket, bh_sockaddr_t *sockaddr) +{ + struct sockaddr_storage addr_storage = { 0 }; + socklen_t addr_len = sizeof(addr_storage); + int ret; + + ret = zsock_getpeername(socket->fd, (struct sockaddr *)&addr_storage, + &addr_len); + + if (ret != BHT_OK) { + return BHT_ERROR; + } + + return sockaddr_to_bh_sockaddr((struct sockaddr *)&addr_storage, sockaddr); +} + +int +os_socket_set_send_buf_size(bh_socket_t socket, size_t bufsiz) +{ + int buf_size_int = (int)bufsiz; + + if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + sizeof(buf_size_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_send_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + + if (zsock_getsockopt(socket->fd, SOL_SOCKET, SO_SNDBUF, &buf_size_int, + &bufsiz_len) + != 0) { + return BHT_ERROR; + } + + *bufsiz = (size_t)buf_size_int; + return BHT_OK; +} + +int +os_socket_set_recv_buf_size(bh_socket_t socket, size_t bufsiz) +{ + if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_RCVBUF, &bufsiz, + sizeof(bufsiz)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_buf_size(bh_socket_t socket, size_t *bufsiz) +{ + assert(bufsiz); + + int buf_size_int; + socklen_t bufsiz_len = sizeof(buf_size_int); + + if (zsock_getsockopt(socket->fd, SOL_SOCKET, SO_RCVBUF, &buf_size_int, + &bufsiz_len) + != 0) { + return BHT_ERROR; + } + *bufsiz = (size_t)buf_size_int; + + return BHT_OK; +} + +int +os_socket_set_keep_alive(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_get_keep_alive(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_KEEPALIVE, + is_enabled); +} + +int +os_socket_set_send_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + + if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +} + +int +os_socket_get_send_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + + if (zsock_getsockopt(socket->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &tv_len) + != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + + return BHT_OK; +} + +int +os_socket_set_recv_timeout(bh_socket_t socket, uint64 timeout_us) +{ + struct timeval tv; + tv.tv_sec = timeout_us / 1000000UL; + tv.tv_usec = timeout_us % 1000000UL; + + if (zsock_setsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_recv_timeout(bh_socket_t socket, uint64 *timeout_us) +{ + struct timeval tv; + socklen_t tv_len = sizeof(tv); + + if (zsock_getsockopt(socket->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &tv_len) + != 0) { + return BHT_ERROR; + } + *timeout_us = (tv.tv_sec * 1000000UL) + tv.tv_usec; + + return BHT_OK; +} + +int +os_socket_set_reuse_addr(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_get_reuse_addr(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEADDR, + is_enabled); +} + +int +os_socket_set_reuse_port(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +} + +int +os_socket_get_reuse_port(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_REUSEPORT, + is_enabled); +} + +// SO_LINGER Socket lingers on close (ignored, for compatibility) +int +os_socket_set_linger(bh_socket_t socket, bool is_enabled, int linger_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_linger(bh_socket_t socket, bool *is_enabled, int *linger_s) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_no_delay(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_get_tcp_no_delay(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, IPPROTO_TCP, TCP_NODELAY, + is_enabled); +} + +int +os_socket_set_tcp_quick_ack(bh_socket_t socket, bool is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_quick_ack(bh_socket_t socket, bool *is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_tcp_keep_idle(bh_socket_t socket, uint32_t time_s) +{ + int time_s_int = (int)time_s; + +#ifdef TCP_KEEPIDLE + if (zsock_setsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (zsock_setsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_idle(bh_socket_t socket, uint32_t *time_s) +{ + assert(time_s); + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + +#ifdef TCP_KEEPIDLE + if (zsock_getsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s_int, + &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + + return BHT_OK; +#elif defined(TCP_KEEPALIVE) + if (zsock_getsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPALIVE, &time_s_int, + &time_s_len) + != 0) { + return BHT_ERROR; + } + *time_s = (uint32)time_s_int; + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_keep_intvl(bh_socket_t socket, uint32_t time_s) +{ + int time_s_int = (int)time_s; + +#ifdef TCP_KEEPINTVL + if (zsock_setsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, + sizeof(time_s_int)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_get_tcp_keep_intvl(bh_socket_t socket, uint32_t *time_s) +{ +#ifdef TCP_KEEPINTVL + if (!socket || !time_s || socket->fd < 0) { + errno = EINVAL; + return BHT_ERROR; + } + + int time_s_int; + socklen_t time_s_len = sizeof(time_s_int); + + if (zsock_getsockopt(socket->fd, IPPROTO_TCP, TCP_KEEPINTVL, &time_s_int, + &time_s_len) + != 0) { + return BHT_ERROR; + } + + if (time_s_int < 0) { + errno = EINVAL; + return BHT_ERROR; + } + + *time_s = (uint32_t)time_s_int; + + return BHT_OK; +#else + errno = ENOSYS; + + return BHT_ERROR; +#endif +} + +int +os_socket_set_tcp_fastopen_connect(bh_socket_t socket, bool is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_get_tcp_fastopen_connect(bh_socket_t socket, bool *is_enabled) +{ + errno = ENOSYS; + + return BHT_ERROR; +} + +int +os_socket_set_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_setbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_get_ip_multicast_loop(bh_socket_t socket, bool ipv6, bool *is_enabled) +{ + if (ipv6) { +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + return os_socket_getbooloption(socket, IPPROTO_IP, IP_MULTICAST_LOOP, + is_enabled); + } +} + +int +os_socket_set_ip_add_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + + if (setsockopt(socket->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreqn mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_address.s_addr = imr_interface; + + if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + return BHT_OK; +} + +int +os_socket_set_ip_drop_membership(bh_socket_t socket, + bh_ip_addr_buffer_t *imr_multiaddr, + uint32_t imr_interface, bool is_ipv6) +{ + assert(imr_multiaddr); + + if (is_ipv6) { +#if defined(IPPROTO_IPV6) && !defined(BH_PLATFORM_COSMOPOLITAN) + struct ipv6_mreq mreq; + + for (int i = 0; i < 8; i++) { + ((uint16_t *)mreq.ipv6mr_multiaddr.s6_addr)[i] = + imr_multiaddr->ipv6[i]; + } + mreq.ipv6mr_interface = imr_interface; + + if (zsock_setsockopt(socket->fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) + != 0) { + return BHT_ERROR; + } +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif + } + else { + struct ip_mreqn mreq; + mreq.imr_multiaddr.s_addr = imr_multiaddr->ipv4; + mreq.imr_address.s_addr = imr_interface; + + if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, + sizeof(mreq)) + != 0) { + return BHT_ERROR; + } + } + return BHT_OK; +} + +int +os_socket_set_ip_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_TTL, &ttl_s, sizeof(ttl_s)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + /* Most socket-level options utilize an int argument for optval */ + int opt; + socklen_t opt_len = sizeof(opt); + if (zsock_getsockopt(socket->fd, IPPROTO_IP, IP_TTL, &opt, &opt_len) != 0) { + return BHT_ERROR; + } + *ttl_s = (uint8_t)opt; + + return BHT_OK; +} + +int +os_socket_set_ip_multicast_ttl(bh_socket_t socket, uint8_t ttl_s) +{ + if (zsock_setsockopt(socket->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl_s, + sizeof(ttl_s)) + != 0) { + return BHT_ERROR; + } + + return BHT_OK; +} + +int +os_socket_get_ip_multicast_ttl(bh_socket_t socket, uint8_t *ttl_s) +{ + /* Most socket-level options utilize an int argument for optval */ + int opt; + socklen_t opt_len = sizeof(opt); + if (zsock_getsockopt(socket->fd, IPPROTO_IP, IP_MULTICAST_TTL, &opt, + &opt_len) + != 0) { + return BHT_ERROR; + } + *ttl_s = (uint8_t)opt; + + return BHT_OK; +} + +int +os_socket_set_ipv6_only(bh_socket_t socket, bool is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_setbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_get_ipv6_only(bh_socket_t socket, bool *is_enabled) +{ +#ifdef IPPROTO_IPV6 + return os_socket_getbooloption(socket, IPPROTO_IPV6, IPV6_V6ONLY, + is_enabled); +#else + errno = EAFNOSUPPORT; + return BHT_ERROR; +#endif +} + +int +os_socket_set_broadcast(bh_socket_t socket, bool is_enabled) +{ + return os_socket_setbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +int +os_socket_get_broadcast(bh_socket_t socket, bool *is_enabled) +{ + return os_socket_getbooloption(socket, SOL_SOCKET, SO_BROADCAST, + is_enabled); +} + +// Experimental : +int +os_ioctl(os_file_handle handle, int request, ...) +{ + return __WASI_ENOSYS; + // int ret = -1; + // va_list args; + + // va_start(args, request); + // ret = zsock_ioctl(handle->fd, request, args); + // va_end(args); + + // return ret; +} + +int +os_poll(os_poll_file_handle *fds, os_nfds_t nfs, int timeout) +{ + return zsock_poll(fds, nfs, timeout); +} diff --git a/core/shared/platform/zephyr/zephyr_thread.c b/core/shared/platform/zephyr/zephyr_thread.c new file mode 100644 index 0000000000..af864e417e --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_thread.c @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-FileCopyrightText: 2024 Siemens AG (For Zephyr usermode changes) + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +/* clang-format off */ +#define bh_assert(v) do { \ + if (!(v)) { \ + printf("\nASSERTION FAILED: %s, at %s, line %d\n", \ + #v, __FILE__, __LINE__); \ + abort(); \ + } \ +} while (0) +/* clang-format on */ + +#if defined(CONFIG_ARM_MPU) || defined(CONFIG_ARC_MPU) \ + || KERNEL_VERSION_NUMBER > 0x020300 /* version 2.3.0 */ +#define BH_ENABLE_ZEPHYR_MPU_STACK 1 +#elif !defined(BH_ENABLE_ZEPHYR_MPU_STACK) +#define BH_ENABLE_ZEPHYR_MPU_STACK 0 +#endif +#if !defined(BH_ZEPHYR_MPU_STACK_SIZE) +#define BH_ZEPHYR_MPU_STACK_SIZE APP_THREAD_STACK_SIZE_MIN +#endif +#if !defined(BH_ZEPHYR_MPU_STACK_COUNT) +#define BH_ZEPHYR_MPU_STACK_COUNT 4 +#endif + +#if BH_ENABLE_ZEPHYR_MPU_STACK != 0 +static K_THREAD_STACK_ARRAY_DEFINE(mpu_stacks, BH_ZEPHYR_MPU_STACK_COUNT, + BH_ZEPHYR_MPU_STACK_SIZE); +static bool mpu_stack_allocated[BH_ZEPHYR_MPU_STACK_COUNT]; +static zmutex_t mpu_stack_lock; + +static char * +mpu_stack_alloc() +{ + int i; + + zmutex_lock(&mpu_stack_lock, K_FOREVER); + for (i = 0; i < BH_ZEPHYR_MPU_STACK_COUNT; i++) { + if (!mpu_stack_allocated[i]) { + mpu_stack_allocated[i] = true; + zmutex_unlock(&mpu_stack_lock); + return (char *)mpu_stacks[i]; + } + } + zmutex_unlock(&mpu_stack_lock); + return NULL; +} + +static void +mpu_stack_free(char *stack) +{ + int i; + + zmutex_lock(&mpu_stack_lock, K_FOREVER); + for (i = 0; i < BH_ZEPHYR_MPU_STACK_COUNT; i++) { + if ((char *)mpu_stacks[i] == stack) + mpu_stack_allocated[i] = false; + } + zmutex_unlock(&mpu_stack_lock); +} +#endif + +typedef struct os_thread_wait_node { + zsem_t sem; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Next thread data */ + struct os_thread_data *next; + /* Zephyr thread handle */ + korp_tid tid; + /* Jeff thread local root */ + void *tlr; + /* Lock for waiting list */ + zmutex_t wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; + /* Thread stack size */ + unsigned stack_size; +#if BH_ENABLE_ZEPHYR_MPU_STACK == 0 + /* Thread stack */ + char stack[1]; +#else + char *stack; +#endif +} os_thread_data; + +typedef struct os_thread_obj { + struct k_thread thread; + /* Whether the thread is terminated and this thread object is to + be freed in the future. */ + bool to_be_freed; + struct os_thread_obj *next; +} os_thread_obj; + +static bool is_thread_sys_inited = false; + +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Lock for thread data list */ +static zmutex_t thread_data_lock; + +/* Thread data list */ +static os_thread_data *thread_data_list = NULL; + +/* Lock for thread object list */ +static zmutex_t thread_obj_lock; + +/* Thread object list */ +static os_thread_obj *thread_obj_list = NULL; + +static void +thread_data_list_add(os_thread_data *thread_data) +{ + zmutex_lock(&thread_data_lock, K_FOREVER); + if (!thread_data_list) + thread_data_list = thread_data; + else { + /* If already in list, just return */ + os_thread_data *p = thread_data_list; + while (p) { + if (p == thread_data) { + zmutex_unlock(&thread_data_lock); + return; + } + p = p->next; + } + + /* Set as head of list */ + thread_data->next = thread_data_list; + thread_data_list = thread_data; + } + zmutex_unlock(&thread_data_lock); +} + +static void +thread_data_list_remove(os_thread_data *thread_data) +{ + zmutex_lock(&thread_data_lock, K_FOREVER); + if (thread_data_list) { + if (thread_data_list == thread_data) + thread_data_list = thread_data_list->next; + else { + /* Search and remove it from list */ + os_thread_data *p = thread_data_list; + while (p && p->next != thread_data) + p = p->next; + if (p && p->next == thread_data) + p->next = p->next->next; + } + } + zmutex_unlock(&thread_data_lock); +} + +static os_thread_data * +thread_data_list_lookup(k_tid_t tid) +{ + zmutex_lock(&thread_data_lock, K_FOREVER); + if (thread_data_list) { + os_thread_data *p = thread_data_list; + while (p) { + if (p->tid == tid) { + /* Found */ + zmutex_unlock(&thread_data_lock); + return p; + } + p = p->next; + } + } + zmutex_unlock(&thread_data_lock); + return NULL; +} + +static void +thread_obj_list_add(os_thread_obj *thread_obj) +{ + zmutex_lock(&thread_obj_lock, K_FOREVER); + if (!thread_obj_list) + thread_obj_list = thread_obj; + else { + /* Set as head of list */ + thread_obj->next = thread_obj_list; + thread_obj_list = thread_obj; + } + zmutex_unlock(&thread_obj_lock); +} + +static void +thread_obj_list_reclaim() +{ + os_thread_obj *p, *p_prev; + zmutex_lock(&thread_obj_lock, K_FOREVER); + p_prev = NULL; + p = thread_obj_list; + while (p) { + if (p->to_be_freed) { + if (p_prev == NULL) { /* p is the head of list */ + thread_obj_list = p->next; + BH_FREE(p); + p = thread_obj_list; + } + else { /* p is not the head of list */ + p_prev->next = p->next; + BH_FREE(p); + p = p_prev->next; + } + } + else { + p_prev = p; + p = p->next; + } + } + zmutex_unlock(&thread_obj_lock); +} + +int +os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + +#if BH_ENABLE_ZEPHYR_MPU_STACK != 0 + zmutex_init(&mpu_stack_lock); +#endif + zmutex_init(&thread_data_lock); + zmutex_init(&thread_obj_lock); + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); + supervisor_thread_data.tid = k_current_get(); + /* Set as head of thread data list */ + thread_data_list = &supervisor_thread_data; + + is_thread_sys_inited = true; + return BHT_OK; +} + +void +os_thread_sys_destroy(void) +{ + if (is_thread_sys_inited) { + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + k_tid_t tid = k_current_get(); + return thread_data_list_lookup(tid); +} + +static void +os_thread_cleanup(void) +{ + os_thread_data *thread_data = thread_data_current(); + + bh_assert(thread_data != NULL); + zmutex_lock(&thread_data->wait_list_lock, K_FOREVER); + if (thread_data->thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_data->thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + zsem_give(&head->sem); + /* head will be freed by joining thread */ + head = next; + } + thread_data->thread_wait_list = NULL; + } + zmutex_unlock(&thread_data->wait_list_lock); + + thread_data_list_remove(thread_data); + /* Set flag to true for the next thread creating to + free the thread object */ + ((os_thread_obj *)thread_data->tid)->to_be_freed = true; +#if BH_ENABLE_ZEPHYR_MPU_STACK != 0 + mpu_stack_free(thread_data->stack); +#endif + BH_FREE(thread_data); +} + +static void +os_thread_wrapper(void *start, void *arg, void *thread_data) +{ + /* Set thread custom data */ + ((os_thread_data *)thread_data)->tid = k_current_get(); + thread_data_list_add(thread_data); + + ((thread_start_routine_t)start)(arg); + os_thread_cleanup(); +} + +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + korp_tid tid; + os_thread_data *thread_data; + unsigned thread_data_size; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Free the thread objects of terminated threads */ + thread_obj_list_reclaim(); + + /* Create and initialize thread object */ + if (!(tid = BH_MALLOC(sizeof(os_thread_obj)))) + return BHT_ERROR; + + memset(tid, 0, sizeof(os_thread_obj)); + + /* Create and initialize thread data */ +#if BH_ENABLE_ZEPHYR_MPU_STACK == 0 + if (stack_size < APP_THREAD_STACK_SIZE_MIN) + stack_size = APP_THREAD_STACK_SIZE_MIN; + thread_data_size = offsetof(os_thread_data, stack) + stack_size; +#else + stack_size = BH_ZEPHYR_MPU_STACK_SIZE; + thread_data_size = sizeof(os_thread_data); +#endif + if (!(thread_data = BH_MALLOC(thread_data_size))) { + goto fail1; + } + + memset(thread_data, 0, thread_data_size); + zmutex_init(&thread_data->wait_list_lock); + thread_data->stack_size = stack_size; + thread_data->tid = tid; + +#if BH_ENABLE_ZEPHYR_MPU_STACK != 0 + if (!(thread_data->stack = mpu_stack_alloc())) { + goto fail2; + } +#endif + + /* Create the thread */ + if (!((tid = k_thread_create(tid, (k_thread_stack_t *)thread_data->stack, + stack_size, os_thread_wrapper, start, arg, + thread_data, prio, 0, K_NO_WAIT)))) { + goto fail3; + } + + bh_assert(tid == thread_data->tid); + + k_thread_name_set(tid, "wasm-zephyr"); + + /* Set thread custom data */ + thread_data_list_add(thread_data); + thread_obj_list_add((os_thread_obj *)tid); + *p_tid = tid; + return BHT_OK; + +fail3: +#if BH_ENABLE_ZEPHYR_MPU_STACK != 0 + mpu_stack_free(thread_data->stack); +fail2: +#endif + BH_FREE(thread_data); +fail1: + BH_FREE(tid); + return BHT_ERROR; +} + +korp_tid +os_self_thread() +{ + return (korp_tid)k_current_get(); +} + +int +os_thread_join(korp_tid thread, void **value_ptr) +{ + (void)value_ptr; + os_thread_data *thread_data; + os_thread_wait_node *node; + + /* Get thread data */ + thread_data = thread_data_list_lookup(thread); + + if (thread_data == NULL) { + os_printf( + "Can't join thread %p, probably already exited or does not exist", + thread); + return BHT_OK; + } + + /* Create wait node and append it to wait list */ + if (!(node = BH_MALLOC(sizeof(os_thread_wait_node)))) + return BHT_ERROR; + + zsem_init(&node->sem, 0, 1); + node->next = NULL; + + zmutex_lock(&thread_data->wait_list_lock, K_FOREVER); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = node; + else { + /* Add to end of waiting list */ + os_thread_wait_node *p = thread_data->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + zmutex_unlock(&thread_data->wait_list_lock); + + /* Wait the sem */ + zsem_take(&node->sem, K_FOREVER); + + /* Wait some time for the thread to be actually terminated */ + k_sleep(Z_TIMEOUT_MS(100)); + + /* Destroy resource */ + BH_FREE(node); + return BHT_OK; +} + +int +os_mutex_init(korp_mutex *mutex) +{ + zmutex_init(mutex); + return BHT_OK; +} + +int +os_recursive_mutex_init(korp_mutex *mutex) +{ + zmutex_init(mutex); + return BHT_OK; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + (void)mutex; + return BHT_OK; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + return zmutex_lock(mutex, K_FOREVER); +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ +#if KERNEL_VERSION_NUMBER >= 0x020200 /* version 2.2.0 */ + return zmutex_unlock(mutex); +#else + zmutex_unlock(mutex); + return 0; +#endif +} + +int +os_cond_init(korp_cond *cond) +{ + zmutex_init(&cond->wait_list_lock); + cond->thread_wait_list = NULL; + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + (void)cond; + return BHT_OK; +} + +static int +os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, bool timed, int mills) +{ + os_thread_wait_node *node; + + /* Create wait node and append it to wait list */ + if (!(node = BH_MALLOC(sizeof(os_thread_wait_node)))) + return BHT_ERROR; + + zsem_init(&node->sem, 0, 1); + node->next = NULL; + + zmutex_lock(&cond->wait_list_lock, K_FOREVER); + if (!cond->thread_wait_list) + cond->thread_wait_list = node; + else { + /* Add to end of wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + zmutex_unlock(&cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + zmutex_unlock(mutex); + zsem_take(&node->sem, timed ? Z_TIMEOUT_MS(mills) : K_FOREVER); + zmutex_lock(mutex, K_FOREVER); + + /* Remove wait node from wait list */ + zmutex_lock(&cond->wait_list_lock, K_FOREVER); + if (cond->thread_wait_list == node) + cond->thread_wait_list = node->next; + 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; + } + BH_FREE(node); + zmutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, uint64 useconds) +{ + + if (useconds == BHT_WAIT_FOREVER) { + return os_cond_wait_internal(cond, mutex, false, 0); + } + else { + uint64 mills_64 = useconds / 1000; + int32 mills; + + if (mills_64 < (uint64)INT32_MAX) { + mills = (int32)mills_64; + } + else { + mills = INT32_MAX; + os_printf("Warning: os_cond_reltimedwait exceeds limit, " + "set to max timeout instead\n"); + } + return os_cond_wait_internal(cond, mutex, true, mills); + } +} + +int +os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + zmutex_lock(&cond->wait_list_lock, K_FOREVER); + if (cond->thread_wait_list) + zsem_give(&cond->thread_wait_list->sem); + zmutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +uint8 * +os_thread_get_stack_boundary() +{ +#if defined(CONFIG_THREAD_STACK_INFO) && !defined(CONFIG_USERSPACE) + korp_tid thread = k_current_get(); + return (uint8 *)thread->stack_info.start; +#else + return NULL; +#endif +} + +void +os_thread_jit_write_protect_np(bool enabled) +{} + +int +os_rwlock_init(korp_rwlock *lock) +{ + if (!lock) { + return BHT_ERROR; + } + + k_mutex_init(&lock->mtx); + k_sem_init(&lock->sem, 0, K_SEM_MAX_LIMIT); + lock->read_count = 0; + + return BHT_OK; +} + +int +os_rwlock_rdlock(korp_rwlock *lock) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_rwlock_wrlock(korp_rwlock *lock) +{ + // Acquire the mutex to ensure exclusive access + if (k_mutex_lock(&lock->mtx, K_FOREVER) != 0) { + return BHT_ERROR; + } + + // Wait until there are no readers + while (lock->read_count > 0) { + // Release the mutex while we're waiting + k_mutex_unlock(&lock->mtx); + + // Wait for a short time + k_sleep(K_MSEC(1)); + + // Re-acquire the mutex + if (k_mutex_lock(&lock->mtx, K_FOREVER) != 0) { + return BHT_ERROR; + } + } + // At this point, we hold the mutex and there are no readers, so we have the + // write lock + return BHT_OK; +} + +int +os_rwlock_unlock(korp_rwlock *lock) +{ + k_mutex_unlock(&lock->mtx); + return BHT_OK; +} + +int +os_rwlock_destroy(korp_rwlock *lock) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_thread_detach(korp_tid thread) +{ + (void)thread; + return BHT_OK; +} + +void +os_thread_exit(void *retval) +{ + (void)retval; + os_thread_cleanup(); + k_thread_abort(k_current_get()); +} + +int +os_cond_broadcast(korp_cond *cond) +{ + os_thread_wait_node *node; + zmutex_lock(&cond->wait_list_lock, K_FOREVER); + node = cond->thread_wait_list; + while (node) { + os_thread_wait_node *next = node->next; + zsem_give(&node->sem); + node = next; + } + zmutex_unlock(&cond->wait_list_lock); + return BHT_OK; +} + +korp_sem * +os_sem_open(const char *name, int oflags, int mode, int val) +{ + /* Not implemented */ + return NULL; +} + +int +os_sem_close(korp_sem *sem) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_sem_wait(korp_sem *sem) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_sem_trywait(korp_sem *sem) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_sem_post(korp_sem *sem) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_sem_getvalue(korp_sem *sem, int *sval) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_sem_unlink(const char *name) +{ + /* Not implemented */ + return BHT_ERROR; +} + +int +os_blocking_op_init() +{ + /* Not implemented */ + return BHT_ERROR; +} + +void +os_begin_blocking_op() +{ + /* Not implemented */ +} + +void +os_end_blocking_op() +{ + /* Not implemented */ +} + +int +os_wakeup_blocking_op(korp_tid tid) +{ + /* Not implemented */ + return BHT_ERROR; +} \ No newline at end of file diff --git a/core/shared/platform/zephyr/zephyr_time.c b/core/shared/platform/zephyr/zephyr_time.c new file mode 100644 index 0000000000..5b84057379 --- /dev/null +++ b/core/shared/platform/zephyr/zephyr_time.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_us() +{ + return k_uptime_get() * 1000; +} + +uint64 +os_time_thread_cputime_us(void) +{ + /* On certain boards, enabling userspace could impact the collection of + * thread runtime statistics */ +#ifdef CONFIG_THREAD_RUNTIME_STATS + k_tid_t tid; + struct k_thread_runtime_stats stats; + uint32 clock_freq; + uint64 cpu_cycles, time_in_us = 0; + + tid = k_current_get(); + if (k_thread_runtime_stats_get(tid, &stats) == 0) { + cpu_cycles = stats.execution_cycles; + clock_freq = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + time_in_us = (cpu_cycles * 1000000) / clock_freq; + } + + return time_in_us; +#else + return os_time_get_boot_us(); +#endif +} diff --git a/core/shared/utils/CMakeLists.txt b/core/shared/utils/CMakeLists.txt deleted file mode 100644 index 0291097897..0000000000 --- a/core/shared/utils/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -include_directories (. ../include ../platform/include ../platform/${WAMR_BUILD_PLATFORM} coap/er-coap coap/extension) - - -file (GLOB_RECURSE source_all *.c ) - -add_library (vmutilslib ${source_all}) - diff --git a/core/shared/utils/Makefile b/core/shared/utils/Makefile deleted file mode 100644 index b5612a07b7..0000000000 --- a/core/shared/utils/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -obj-y += bh_list.o bh_log.o attr-container.o bh_queue.o -obj-y += coap/ diff --git a/core/shared/utils/SConscript b/core/shared/utils/SConscript new file mode 100644 index 0000000000..358f2ffca2 --- /dev/null +++ b/core/shared/utils/SConscript @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import os + +cwd = GetCurrentDir() + +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('iwasm_shared_utils', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/shared/utils/bh_assert.c b/core/shared/utils/bh_assert.c new file mode 100644 index 0000000000..5e62baaf3a --- /dev/null +++ b/core/shared/utils/bh_assert.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_assert.h" + +void +bh_assert_internal(int64 v, const char *file_name, int line_number, + const char *expr_string) +{ + if (v) + return; + + if (!file_name) + file_name = "NULL FILENAME"; + + if (!expr_string) + expr_string = "NULL EXPR_STRING"; + + LOG_ERROR("\nASSERTION FAILED: %s, at file %s, line %d\n", expr_string, + file_name, line_number); + + abort(); +} diff --git a/core/shared/utils/bh_assert.h b/core/shared/utils/bh_assert.h new file mode 100644 index 0000000000..ec9e632af0 --- /dev/null +++ b/core/shared/utils/bh_assert.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_ASSERT_H +#define _BH_ASSERT_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if BH_DEBUG != 0 +void +bh_assert_internal(int64 v, const char *file_name, int line_number, + const char *expr_string); +#define bh_assert(expr) \ + bh_assert_internal((int64)(uintptr_t)(expr), __FILE__, __LINE__, #expr) +#else +#define bh_assert(expr) (void)0 +#endif /* end of BH_DEBUG */ + +#if !defined(__has_extension) +#define __has_extension(a) 0 +#endif + +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) \ + || (defined(__GNUC__) && __GNUC__ * 0x100 + __GNUC_MINOR__ >= 0x406) \ + || __has_extension(c_static_assert) + +#define bh_static_assert(expr) _Static_assert(expr, #expr) +#else +#define bh_static_assert(expr) /* nothing */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _BH_ASSERT_H */ diff --git a/core/shared/utils/bh_atomic.h b/core/shared/utils/bh_atomic.h new file mode 100644 index 0000000000..57cb4d1c91 --- /dev/null +++ b/core/shared/utils/bh_atomic.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2023 Amazon Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_ATOMIC_H +#define _BH_ATOMIC_H + +#include "bh_platform.h" +#include "gnuc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Why don't we use C11 stdatomics here? + * + * Unlike C11 stdatomics, + * + * - bh_atomic_xxx_t is guaranteed to have the same size as the base type. + * Thus more friendly to our AOT conventions. + * + * - It's available for C++. + * Although C++23 will have C-compatible stdatomics.h, it isn't widely + * available yet. + */ + +/* + * Note about BH_ATOMIC_32_IS_ATOMIC + * + * If BH_ATOMIC_32_IS_ATOMIC == 0, BH_ATOMIC_xxx operations defined below + * are not really atomic and require an external lock. + * + * Expected usage is: + * + * bh_atomic_32_t var = 0; + * uint32 old; + * #if BH_ATOMIC_32_IS_ATOMIC == 0 + * lock(&some_lock); + * #endif + * old = BH_ATOMIC_32_FETCH_AND(var, 1); + * #if BH_ATOMIC_32_IS_ATOMIC == 0 + * unlock(&some_lock); + * #endif + */ + +typedef uint64 bh_atomic_64_t; +typedef uint32 bh_atomic_32_t; +typedef uint16 bh_atomic_16_t; + +/* The flag can be defined by the user if the platform + * supports atomic 32-bit operations. + * If left undefined, it will be automatically defined + * according to the platform. + */ +#ifdef WASM_UINT64_IS_ATOMIC +#define BH_ATOMIC_64_IS_ATOMIC WASM_UINT64_IS_ATOMIC +#endif /* WASM_UINT64_IS_ATOMIC */ + +#ifdef WASM_UINT32_IS_ATOMIC +#define BH_ATOMIC_32_IS_ATOMIC WASM_UINT32_IS_ATOMIC +#endif /* WASM_UINT32_IS_ATOMIC */ + +#ifdef WASM_UINT16_IS_ATOMIC +#define BH_ATOMIC_16_IS_ATOMIC WASM_UINT16_IS_ATOMIC +#endif /* WASM_UINT16_IS_ATOMIC */ + +#if defined(__GNUC_PREREQ) +#if __GNUC_PREREQ(4, 7) +#define CLANG_GCC_HAS_ATOMIC_BUILTIN +#endif +#elif defined(__clang__) +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 0) +#define CLANG_GCC_HAS_ATOMIC_BUILTIN +#endif +#endif + +#if defined(CLANG_GCC_HAS_ATOMIC_BUILTIN) +#ifndef BH_ATOMIC_64_IS_ATOMIC +#define BH_ATOMIC_64_IS_ATOMIC 1 +#endif +#ifndef BH_ATOMIC_32_IS_ATOMIC +#define BH_ATOMIC_32_IS_ATOMIC 1 +#endif +#ifndef BH_ATOMIC_16_IS_ATOMIC +#define BH_ATOMIC_16_IS_ATOMIC 1 +#endif +#else +#ifndef BH_ATOMIC_64_IS_ATOMIC +#define BH_ATOMIC_64_IS_ATOMIC 0 +#endif +#ifndef BH_ATOMIC_32_IS_ATOMIC +#define BH_ATOMIC_32_IS_ATOMIC 0 +#endif +#ifndef BH_ATOMIC_16_IS_ATOMIC +#define BH_ATOMIC_16_IS_ATOMIC 0 +#endif +#endif + +/* Force disable atomic 16-bit operations on bare-metal RISC-V + * because the 16-bit atomic operations is emulated by 32-bit + * atomic operations, which has linkage problem on current toolchain: + * in function `shared_memory_inc_reference': + * wasm_shared_memory.c:85:(.text.shared_memory_inc_reference+0x10): undefined + * reference to `__atomic_fetch_add_2' + */ +#ifndef WASM_UINT16_IS_ATOMIC +#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) \ + && !defined(__OpenBSD__) && defined(__riscv) +#undef BH_ATOMIC_16_IS_ATOMIC +#define BH_ATOMIC_16_IS_ATOMIC 0 +#endif +#endif + +/* On some 32-bit platform, disable 64-bit atomic operations, otherwise + * undefined reference to `__atomic_load_8', if on Zephyr, can add board related + * macro in autoconf.h to control */ +#ifndef WASM_UINT64_IS_ATOMIC +#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) \ + && !defined(__OpenBSD__) && (defined(__riscv) || defined(__arm__)) \ + && UINT32_MAX == UINTPTR_MAX +#undef BH_ATOMIC_64_IS_ATOMIC +#define BH_ATOMIC_64_IS_ATOMIC 0 +#endif +#endif + +#if BH_ATOMIC_64_IS_ATOMIC != 0 + +#define BH_ATOMIC_64_LOAD(v) __atomic_load_n(&(v), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_64_STORE(v, val) __atomic_store_n(&(v), val, __ATOMIC_SEQ_CST) +#define BH_ATOMIC_64_FETCH_OR(v, val) \ + __atomic_fetch_or(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_64_FETCH_AND(v, val) \ + __atomic_fetch_and(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_64_FETCH_ADD(v, val) \ + __atomic_fetch_add(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_64_FETCH_SUB(v, val) \ + __atomic_fetch_sub(&(v), (val), __ATOMIC_SEQ_CST) + +#else /* else of BH_ATOMIC_64_IS_ATOMIC != 0 */ + +#define BH_ATOMIC_64_LOAD(v) (v) +#define BH_ATOMIC_64_STORE(v, val) (v) = val +#define BH_ATOMIC_64_FETCH_OR(v, val) nonatomic_64_fetch_or(&(v), val) +#define BH_ATOMIC_64_FETCH_AND(v, val) nonatomic_64_fetch_and(&(v), val) +#define BH_ATOMIC_64_FETCH_ADD(v, val) nonatomic_64_fetch_add(&(v), val) +#define BH_ATOMIC_64_FETCH_SUB(v, val) nonatomic_64_fetch_sub(&(v), val) + +static inline uint64 +nonatomic_64_fetch_or(bh_atomic_64_t *p, uint64 val) +{ + uint64 old = *p; + *p |= val; + return old; +} + +static inline uint64 +nonatomic_64_fetch_and(bh_atomic_64_t *p, uint64 val) +{ + uint64 old = *p; + *p &= val; + return old; +} + +static inline uint64 +nonatomic_64_fetch_add(bh_atomic_64_t *p, uint64 val) +{ + uint64 old = *p; + *p += val; + return old; +} + +static inline uint64 +nonatomic_64_fetch_sub(bh_atomic_64_t *p, uint64 val) +{ + uint64 old = *p; + *p -= val; + return old; +} +#endif + +#if BH_ATOMIC_32_IS_ATOMIC != 0 + +#define BH_ATOMIC_32_LOAD(v) __atomic_load_n(&(v), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_32_STORE(v, val) __atomic_store_n(&(v), val, __ATOMIC_SEQ_CST) +#define BH_ATOMIC_32_FETCH_OR(v, val) \ + __atomic_fetch_or(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_32_FETCH_AND(v, val) \ + __atomic_fetch_and(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_32_FETCH_ADD(v, val) \ + __atomic_fetch_add(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_32_FETCH_SUB(v, val) \ + __atomic_fetch_sub(&(v), (val), __ATOMIC_SEQ_CST) + +#else /* else of BH_ATOMIC_32_IS_ATOMIC != 0 */ + +#define BH_ATOMIC_32_LOAD(v) (v) +#define BH_ATOMIC_32_STORE(v, val) (v) = val +#define BH_ATOMIC_32_FETCH_OR(v, val) nonatomic_32_fetch_or(&(v), val) +#define BH_ATOMIC_32_FETCH_AND(v, val) nonatomic_32_fetch_and(&(v), val) +#define BH_ATOMIC_32_FETCH_ADD(v, val) nonatomic_32_fetch_add(&(v), val) +#define BH_ATOMIC_32_FETCH_SUB(v, val) nonatomic_32_fetch_sub(&(v), val) + +static inline uint32 +nonatomic_32_fetch_or(bh_atomic_32_t *p, uint32 val) +{ + uint32 old = *p; + *p |= val; + return old; +} + +static inline uint32 +nonatomic_32_fetch_and(bh_atomic_32_t *p, uint32 val) +{ + uint32 old = *p; + *p &= val; + return old; +} + +static inline uint32 +nonatomic_32_fetch_add(bh_atomic_32_t *p, uint32 val) +{ + uint32 old = *p; + *p += val; + return old; +} + +static inline uint32 +nonatomic_32_fetch_sub(bh_atomic_32_t *p, uint32 val) +{ + uint32 old = *p; + *p -= val; + return old; +} + +#endif + +#if BH_ATOMIC_16_IS_ATOMIC != 0 + +#define BH_ATOMIC_16_IS_ATOMIC 1 +#define BH_ATOMIC_16_LOAD(v) __atomic_load_n(&(v), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_16_STORE(v, val) __atomic_store_n(&(v), val, __ATOMIC_SEQ_CST) +#define BH_ATOMIC_16_FETCH_OR(v, val) \ + __atomic_fetch_or(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_16_FETCH_AND(v, val) \ + __atomic_fetch_and(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_16_FETCH_ADD(v, val) \ + __atomic_fetch_add(&(v), (val), __ATOMIC_SEQ_CST) +#define BH_ATOMIC_16_FETCH_SUB(v, val) \ + __atomic_fetch_sub(&(v), (val), __ATOMIC_SEQ_CST) + +#else /* else of BH_ATOMIC_16_IS_ATOMIC != 0 */ + +#define BH_ATOMIC_16_LOAD(v) (v) +#define BH_ATOMIC_16_STORE(v) (v) = val +#define BH_ATOMIC_16_FETCH_OR(v, val) nonatomic_16_fetch_or(&(v), val) +#define BH_ATOMIC_16_FETCH_AND(v, val) nonatomic_16_fetch_and(&(v), val) +#define BH_ATOMIC_16_FETCH_ADD(v, val) nonatomic_16_fetch_add(&(v), val) +#define BH_ATOMIC_16_FETCH_SUB(v, val) nonatomic_16_fetch_sub(&(v), val) + +static inline uint16 +nonatomic_16_fetch_or(bh_atomic_16_t *p, uint16 val) +{ + uint16 old = *p; + *p |= val; + return old; +} + +static inline uint16 +nonatomic_16_fetch_and(bh_atomic_16_t *p, uint16 val) +{ + uint16 old = *p; + *p &= val; + return old; +} + +static inline uint16 +nonatomic_16_fetch_add(bh_atomic_16_t *p, uint16 val) +{ + uint16 old = *p; + *p += val; + return old; +} + +static inline uint16 +nonatomic_16_fetch_sub(bh_atomic_16_t *p, uint16 val) +{ + uint16 old = *p; + *p -= val; + return old; +} + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _BH_ATOMIC_H */ diff --git a/core/shared/utils/bh_bitmap.c b/core/shared/utils/bh_bitmap.c new file mode 100644 index 0000000000..2ee918049c --- /dev/null +++ b/core/shared/utils/bh_bitmap.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_bitmap.h" + +bh_bitmap * +bh_bitmap_new(uintptr_t begin_index, unsigned bitnum) +{ + bh_bitmap *bitmap; + uint32 bitmap_size = (bitnum + 7) / 8; + uint32 total_size = offsetof(bh_bitmap, map) + bitmap_size; + + if (bitnum > UINT32_MAX - 7 || total_size < offsetof(bh_bitmap, map) + || (total_size - offsetof(bh_bitmap, map)) != bitmap_size) { + return NULL; /* integer overflow */ + } + + if ((bitmap = BH_MALLOC(total_size)) != NULL) { + memset(bitmap, 0, total_size); + bitmap->begin_index = begin_index; + bitmap->end_index = begin_index + bitnum; + } + + return bitmap; +} diff --git a/core/shared/utils/bh_bitmap.h b/core/shared/utils/bh_bitmap.h new file mode 100644 index 0000000000..c0e56cb995 --- /dev/null +++ b/core/shared/utils/bh_bitmap.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_BITMAP_H +#define _BH_BITMAP_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A simple fixed size bitmap. + */ +typedef struct bh_bitmap { + /* The first valid bit index. */ + uintptr_t begin_index; + + /* The last valid bit index plus one. */ + uintptr_t end_index; + + /* The bitmap. */ + uint8 map[1]; +} bh_bitmap; + +/** + * Create a new bitmap. + * + * @param begin_index the first valid bit index + * @param bitnum maximal bit number of the bitmap. + * + * @return the new bitmap if succeeds, NULL otherwise. + */ +bh_bitmap * +bh_bitmap_new(uintptr_t begin_index, unsigned bitnum); + +/** + * Delete a bitmap. + * + * @param bitmap the bitmap to be deleted + */ +static inline void +bh_bitmap_delete(bh_bitmap *bitmap) +{ + if (bitmap != NULL) + BH_FREE(bitmap); +} + +/** + * Check whether the given index is in the range of the bitmap. + * + * @param bitmap the bitmap + * @param n the bit index + * + * @return true if the index is in range, false otherwise + */ +static inline bool +bh_bitmap_is_in_range(bh_bitmap *bitmap, uintptr_t n) +{ + return n >= bitmap->begin_index && n < bitmap->end_index; +} + +/** + * Get a bit in the bitmap + * + * @param bitmap the bitmap + * @param n the n-th bit to be get + * + * @return value of the bit + */ +static inline int +bh_bitmap_get_bit(bh_bitmap *bitmap, uintptr_t n) +{ + uintptr_t idx = n - bitmap->begin_index; + bh_assert(n >= bitmap->begin_index && n < bitmap->end_index); + return (bitmap->map[idx / 8] >> (idx % 8)) & 1; +} + +/** + * Set a bit in the bitmap. + * + * @param bitmap the bitmap + * @param n the n-th bit to be set + */ +static inline void +bh_bitmap_set_bit(bh_bitmap *bitmap, uintptr_t n) +{ + uintptr_t idx = n - bitmap->begin_index; + bh_assert(n >= bitmap->begin_index && n < bitmap->end_index); + bitmap->map[idx / 8] |= 1 << (idx % 8); +} + +/** + * Clear a bit in the bitmap. + * + * @param bitmap the bitmap + * @param n the n-th bit to be cleared + */ +static inline void +bh_bitmap_clear_bit(bh_bitmap *bitmap, uintptr_t n) +{ + uintptr_t idx = n - bitmap->begin_index; + bh_assert(n >= bitmap->begin_index && n < bitmap->end_index); + bitmap->map[idx / 8] &= ~(1 << (idx % 8)); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/shared/utils/bh_common.c b/core/shared/utils/bh_common.c new file mode 100644 index 0000000000..d86840d7d2 --- /dev/null +++ b/core/shared/utils/bh_common.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" + +static char * +align_ptr(char *src, unsigned int b) +{ + uintptr_t v = (uintptr_t)src; + uintptr_t m = b - 1; + return (char *)((v + m) & ~m); +} + +/* +Memory copy, with word alignment +*/ +int +b_memcpy_wa(void *s1, unsigned int s1max, const void *s2, unsigned int n) +{ + char *dest = (char *)s1; + char *src = (char *)s2; + + char *pa = align_ptr(src, 4); + char *pb = align_ptr((src + n), 4); + + unsigned int buff; + const char *p_byte_read; + + unsigned int *p; + char *ps; + + if (n == 0) { + return 0; + } + + if (pa > src) { + pa -= 4; + } + + for (p = (unsigned int *)pa; p < (unsigned int *)pb; p++) { + buff = *(p); + p_byte_read = ((char *)&buff); + + /* read leading word */ + if ((char *)p <= src) { + for (ps = src; ps < ((char *)p + 4); ps++) { + if (ps >= src + n) { + break; + } + p_byte_read = ((char *)&buff) + (ps - (char *)p); + *dest++ = *p_byte_read; + } + } + /* read trailing word */ + else if ((char *)p >= pb - 4) { + for (ps = (char *)p; ps < src + n; ps++) { + *dest++ = *p_byte_read++; + } + } + /* read remaining word(s) */ + else { + if ((char *)p + 4 >= src + n) { + for (ps = (char *)p; ps < src + n; ps++) { + *dest++ = *p_byte_read++; + } + } + else { + *(unsigned int *)dest = buff; + dest += 4; + } + } + } + + return 0; +} + +int +b_memcpy_s(void *s1, unsigned int s1max, const void *s2, unsigned int n) +{ + char *dest = (char *)s1; + char *src = (char *)s2; + if (n == 0) { + return 0; + } + + if (s1 == NULL) { + return -1; + } + if (s2 == NULL || n > s1max) { + memset(dest, 0, s1max); + return -1; + } + memcpy(dest, src, n); + return 0; +} + +int +b_memmove_s(void *s1, unsigned int s1max, const void *s2, unsigned int n) +{ + char *dest = (char *)s1; + char *src = (char *)s2; + if (n == 0) { + return 0; + } + + if (s1 == NULL) { + return -1; + } + if (s2 == NULL || n > s1max) { + memset(dest, 0, s1max); + return -1; + } + memmove(dest, src, n); + return 0; +} + +int +b_strcat_s(char *s1, unsigned int s1max, const char *s2) +{ + if (NULL == s1 || NULL == s2 || s1max < (strlen(s1) + strlen(s2) + 1)) { + return -1; + } + + memcpy(s1 + strlen(s1), s2, strlen(s2) + 1); + return 0; +} + +int +b_strcpy_s(char *s1, unsigned int s1max, const char *s2) +{ + if (NULL == s1 || NULL == s2 || s1max < (strlen(s2) + 1)) { + return -1; + } + + memcpy(s1, s2, strlen(s2) + 1); + return 0; +} + +char * +bh_strdup(const char *s) +{ + uint32 size; + char *s1 = NULL; + + if (s) { + size = (uint32)(strlen(s) + 1); + if ((s1 = BH_MALLOC(size))) + bh_memcpy_s(s1, size, s, size); + } + return s1; +} + +char * +wa_strdup(const char *s) +{ + uint32 size; + char *s1 = NULL; + + if (s) { + size = (uint32)(strlen(s) + 1); + if ((s1 = WA_MALLOC(size))) + bh_memcpy_s(s1, size, s, size); + } + return s1; +} + +char * +bh_strtok_r(char *str, const char *delim, char **saveptr) +{ +#if !(defined(_WIN32) || defined(_WIN32_)) + return strtok_r(str, delim, saveptr); +#else + return strtok_s(str, delim, saveptr); +#endif +} + +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 +int +bh_system(const char *cmd) +{ + int ret; + +#if !(defined(_WIN32) || defined(_WIN32_)) + ret = system(cmd); +#else + ret = _spawnlp(_P_WAIT, "cmd.exe", "/c", cmd, NULL); +#endif + + return ret; +} + +#if defined(_WIN32) || defined(_WIN32_) +errno_t +_mktemp_s(char *nameTemplate, size_t sizeInChars); +#endif + +bool +bh_mkstemp(char *file_name, size_t name_len) +{ + int fd; + +#if !(defined(_WIN32) || defined(_WIN32_)) + (void)name_len; + /* On Linux, it generates a unique temporary filename from template, creates + * and opens the file, and returns an open file descriptor for the file. */ + if ((fd = mkstemp(file_name)) <= 0) { + goto fail; + } + + /* close and remove temp file */ + close(fd); + unlink(file_name); +#else + /* On Windows, it generates a unique temporary file name but does not create + * or open the file */ + if (_mktemp_s(file_name, name_len) != 0) { + goto fail; + } +#endif + + return true; +fail: + return false; +} +#endif /* End of WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 */ diff --git a/core/shared/utils/bh_common.h b/core/shared/utils/bh_common.h new file mode 100644 index 0000000000..2f31bd5d80 --- /dev/null +++ b/core/shared/utils/bh_common.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_COMMON_H +#define _BH_COMMON_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define bh_memcpy_s(dest, dlen, src, slen) \ + do { \ + int _ret = b_memcpy_s(dest, dlen, src, slen); \ + (void)_ret; \ + bh_assert(_ret == 0); \ + } while (0) + +#define bh_memcpy_wa(dest, dlen, src, slen) \ + do { \ + int _ret = b_memcpy_wa(dest, dlen, src, slen); \ + (void)_ret; \ + bh_assert(_ret == 0); \ + } while (0) + +#define bh_memmove_s(dest, dlen, src, slen) \ + do { \ + int _ret = b_memmove_s(dest, dlen, src, slen); \ + (void)_ret; \ + bh_assert(_ret == 0); \ + } while (0) + +#define bh_strcat_s(dest, dlen, src) \ + do { \ + int _ret = b_strcat_s(dest, dlen, src); \ + (void)_ret; \ + bh_assert(_ret == 0); \ + } while (0) + +#define bh_strcpy_s(dest, dlen, src) \ + do { \ + int _ret = b_strcpy_s(dest, dlen, src); \ + (void)_ret; \ + bh_assert(_ret == 0); \ + } while (0) + +int +b_memcpy_s(void *s1, unsigned int s1max, const void *s2, unsigned int n); +int +b_memcpy_wa(void *s1, unsigned int s1max, const void *s2, unsigned int n); +int +b_memmove_s(void *s1, unsigned int s1max, const void *s2, unsigned int n); +int +b_strcat_s(char *s1, unsigned int s1max, const char *s2); +int +b_strcpy_s(char *s1, unsigned int s1max, const char *s2); + +/* strdup with string allocated by BH_MALLOC */ +char * +bh_strdup(const char *s); + +/* strdup with string allocated by WA_MALLOC */ +char * +wa_strdup(const char *s); + +char * +bh_strtok_r(char *str, const char *delim, char **saveptr); + +#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0 +/* Executes a system command in bash/cmd.exe */ +int +bh_system(const char *cmd); + +/* Tests whether can create a temporary file with the given name */ +bool +bh_mkstemp(char *filename, size_t name_len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/shared/utils/bh_hashmap.c b/core/shared/utils/bh_hashmap.c index 33891a8a0f..794b7a746e 100644 --- a/core/shared/utils/bh_hashmap.c +++ b/core/shared/utils/bh_hashmap.c @@ -4,10 +4,6 @@ */ #include "bh_hashmap.h" -#include "bh_log.h" -#include "bh_thread.h" -#include "bh_memory.h" - typedef struct HashMapElem { void *key; @@ -29,16 +25,17 @@ struct HashMap { HashMapElem *elements[1]; }; -HashMap* -bh_hash_map_create(uint32 size, bool use_lock, - HashFunc hash_func, - KeyEqualFunc key_equal_func, - KeyDestroyFunc key_destroy_func, +HashMap * +bh_hash_map_create(uint32 size, bool use_lock, HashFunc hash_func, + KeyEqualFunc key_equal_func, KeyDestroyFunc key_destroy_func, ValueDestroyFunc value_destroy_func) { HashMap *map; uint64 total_size; + if (size < HASH_MAP_MIN_SIZE) + size = HASH_MAP_MIN_SIZE; + if (size > HASH_MAP_MAX_SIZE) { LOG_ERROR("HashMap create failed: size is too large.\n"); return NULL; @@ -46,16 +43,17 @@ bh_hash_map_create(uint32 size, bool use_lock, if (!hash_func || !key_equal_func) { LOG_ERROR("HashMap create failed: hash function or key equal function " - " is NULL.\n"); + " is NULL.\n"); return NULL; } - total_size = offsetof(HashMap, elements) + - sizeof(HashMapElem) * (uint64)size + - (use_lock ? sizeof(korp_mutex) : 0); + total_size = offsetof(HashMap, elements) + + sizeof(HashMapElem *) * (uint64)size + + (use_lock ? sizeof(korp_mutex) : 0); - if (total_size >= UINT32_MAX - || !(map = bh_malloc((uint32)total_size))) { + /* size <= HASH_MAP_MAX_SIZE, so total_size won't be larger than + UINT32_MAX, no need to check integer overflow */ + if (!(map = BH_MALLOC((uint32)total_size))) { LOG_ERROR("HashMap create failed: alloc memory failed.\n"); return NULL; } @@ -63,12 +61,11 @@ bh_hash_map_create(uint32 size, bool use_lock, memset(map, 0, (uint32)total_size); if (use_lock) { - map->lock = (korp_mutex*) - ((uint8*)map + offsetof(HashMap, elements) - + sizeof(HashMapElem) * size); - if (vm_mutex_init(map->lock)) { + map->lock = (korp_mutex *)((uint8 *)map + offsetof(HashMap, elements) + + sizeof(HashMapElem *) * size); + if (os_mutex_init(map->lock)) { LOG_ERROR("HashMap create failed: init map lock failed.\n"); - bh_free(map); + BH_FREE(map); return NULL; } } @@ -93,7 +90,7 @@ bh_hash_map_insert(HashMap *map, void *key, void *value) } if (map->lock) { - vm_mutex_lock(map->lock); + os_mutex_lock(map->lock); } index = map->hash_func(key) % map->size; @@ -106,7 +103,7 @@ bh_hash_map_insert(HashMap *map, void *key, void *value) elem = elem->next; } - if (!(elem = bh_malloc(sizeof(HashMapElem)))) { + if (!(elem = BH_MALLOC(sizeof(HashMapElem)))) { LOG_ERROR("HashMap insert elem failed: alloc memory failed.\n"); goto fail; } @@ -117,18 +114,18 @@ bh_hash_map_insert(HashMap *map, void *key, void *value) map->elements[index] = elem; if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return true; fail: if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return false; } -void* +void * bh_hash_map_find(HashMap *map, void *key) { uint32 index; @@ -141,7 +138,7 @@ bh_hash_map_find(HashMap *map, void *key) } if (map->lock) { - vm_mutex_lock(map->lock); + os_mutex_lock(map->lock); } index = map->hash_func(key) % map->size; @@ -151,7 +148,7 @@ bh_hash_map_find(HashMap *map, void *key) if (map->key_equal_func(elem->key, key)) { value = elem->value; if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return value; } @@ -159,14 +156,13 @@ bh_hash_map_find(HashMap *map, void *key) } if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return NULL; } bool -bh_hash_map_update(HashMap *map, void *key, void *value, - void **p_old_value) +bh_hash_map_update(HashMap *map, void *key, void *value, void **p_old_value) { uint32 index; HashMapElem *elem; @@ -177,7 +173,7 @@ bh_hash_map_update(HashMap *map, void *key, void *value, } if (map->lock) { - vm_mutex_lock(map->lock); + os_mutex_lock(map->lock); } index = map->hash_func(key) % map->size; @@ -189,22 +185,22 @@ bh_hash_map_update(HashMap *map, void *key, void *value, *p_old_value = elem->value; elem->value = value; if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return true; - } - elem = elem->next; + } + elem = elem->next; } if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return false; } bool -bh_hash_map_remove(HashMap *map, void *key, - void **p_old_key, void **p_old_value) +bh_hash_map_remove(HashMap *map, void *key, void **p_old_key, + void **p_old_value) { uint32 index; HashMapElem *elem, *prev; @@ -215,7 +211,7 @@ bh_hash_map_remove(HashMap *map, void *key, } if (map->lock) { - vm_mutex_lock(map->lock); + os_mutex_lock(map->lock); } index = map->hash_func(key) % map->size; @@ -233,10 +229,10 @@ bh_hash_map_remove(HashMap *map, void *key, else prev->next = elem->next; - bh_free(elem); + BH_FREE(elem); if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return true; } @@ -246,7 +242,7 @@ bh_hash_map_remove(HashMap *map, void *key, } if (map->lock) { - vm_mutex_unlock(map->lock); + os_mutex_unlock(map->lock); } return false; } @@ -263,7 +259,7 @@ bh_hash_map_destroy(HashMap *map) } if (map->lock) { - vm_mutex_lock(map->lock); + os_mutex_lock(map->lock); } for (index = 0; index < map->size; index++) { @@ -277,16 +273,67 @@ bh_hash_map_destroy(HashMap *map) if (map->value_destroy_func) { map->value_destroy_func(elem->value); } - bh_free(elem); + BH_FREE(elem); elem = next; } } if (map->lock) { - vm_mutex_unlock(map->lock); - vm_mutex_destroy(map->lock); + os_mutex_unlock(map->lock); + os_mutex_destroy(map->lock); } - bh_free(map); + BH_FREE(map); + return true; +} + +uint32 +bh_hash_map_get_struct_size(HashMap *hashmap) +{ + uint32 size = (uint32)(uintptr_t)offsetof(HashMap, elements) + + (uint32)sizeof(HashMapElem *) * hashmap->size; + + if (hashmap->lock) { + size += (uint32)sizeof(korp_mutex); + } + + return size; +} + +uint32 +bh_hash_map_get_elem_struct_size() +{ + return (uint32)sizeof(HashMapElem); +} + +bool +bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback, + void *user_data) +{ + uint32 index; + HashMapElem *elem, *next; + + if (!map || !callback) { + LOG_ERROR("HashMap traverse failed: map or callback is NULL.\n"); + return false; + } + + if (map->lock) { + os_mutex_lock(map->lock); + } + + for (index = 0; index < map->size; index++) { + elem = map->elements[index]; + while (elem) { + next = elem->next; + callback(elem->key, elem->value, user_data); + elem = next; + } + } + + if (map->lock) { + os_mutex_unlock(map->lock); + } + return true; } diff --git a/core/shared/utils/bh_hashmap.h b/core/shared/utils/bh_hashmap.h new file mode 100644 index 0000000000..38aa2c6686 --- /dev/null +++ b/core/shared/utils/bh_hashmap.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASM_HASHMAP_H +#define WASM_HASHMAP_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Minimum initial size of hash map */ +#define HASH_MAP_MIN_SIZE 4 + +/* Maximum initial size of hash map */ +#define HASH_MAP_MAX_SIZE 65536 + +struct HashMap; +typedef struct HashMap HashMap; + +/* Hash function: to get the hash value of key. */ +typedef uint32 (*HashFunc)(const void *key); + +/* Key equal function: to check whether two keys are equal. */ +typedef bool (*KeyEqualFunc)(void *key1, void *key2); + +/* Key destroy function: to destroy the key, auto called + for each key when the hash map is destroyed. */ +typedef void (*KeyDestroyFunc)(void *key); + +/* Value destroy function: to destroy the value, auto called + for each value when the hash map is destroyed. */ +typedef void (*ValueDestroyFunc)(void *value); + +/* traverse callback function: + auto called when traverse every hash element */ +typedef void (*TraverseCallbackFunc)(void *key, void *value, void *user_data); + +/** + * Create a hash map. + * + * @param size: the initial size of the hash map + * @param use_lock whether to lock the hash map when operating on it + * @param hash_func hash function of the key, must be specified + * @param key_equal_func key equal function, check whether two keys + * are equal, must be specified + * @param key_destroy_func key destroy function, called for each key if not NULL + * when the hash map is destroyed + * @param value_destroy_func value destroy function, called for each value if + * not NULL when the hash map is destroyed + * + * @return the hash map created, NULL if failed + */ +HashMap * +bh_hash_map_create(uint32 size, bool use_lock, HashFunc hash_func, + KeyEqualFunc key_equal_func, KeyDestroyFunc key_destroy_func, + ValueDestroyFunc value_destroy_func); + +/** + * Insert an element to the hash map + * + * @param map the hash map to insert element + * @key the key of the element + * @value the value of the element + * + * @return true if success, false otherwise + * Note: fail if key is NULL or duplicated key exists in the hash map, + */ +bool +bh_hash_map_insert(HashMap *map, void *key, void *value); + +/** + * Find an element in the hash map + * + * @param map the hash map to find element + * @key the key of the element + * + * @return the value of the found element if success, NULL otherwise + */ +void * +bh_hash_map_find(HashMap *map, void *key); + +/** + * Update an element in the hash map with new value + * + * @param map the hash map to update element + * @key the key of the element + * @value the new value of the element + * @p_old_value if not NULL, copies the old value to it + * + * @return true if success, false otherwise + * Note: the old value won't be destroyed by value destroy function, + * it will be copied to p_old_value for user to process. + */ +bool +bh_hash_map_update(HashMap *map, void *key, void *value, void **p_old_value); + +/** + * Remove an element from the hash map + * + * @param map the hash map to remove element + * @key the key of the element + * @p_old_key if not NULL, copies the old key to it + * @p_old_value if not NULL, copies the old value to it + * + * @return true if success, false otherwise + * Note: the old key and old value won't be destroyed by key destroy + * function and value destroy function, they will be copied to + * p_old_key and p_old_value for user to process. + */ +bool +bh_hash_map_remove(HashMap *map, void *key, void **p_old_key, + void **p_old_value); + +/** + * Destroy the hashmap + * + * @param map the hash map to destroy + * + * @return true if success, false otherwise + * Note: the key destroy function and value destroy function will be + * called to destroy each element's key and value if they are + * not NULL. + */ +bool +bh_hash_map_destroy(HashMap *map); + +/** + * Get the structure size of HashMap + * + * @param map the hash map to calculate + * + * @return the memory space occupied by HashMap structure + */ +uint32 +bh_hash_map_get_struct_size(HashMap *hashmap); + +/** + * Get the structure size of HashMap Element + * + * @return the memory space occupied by HashMapElem structure + */ +uint32 +bh_hash_map_get_elem_struct_size(void); + +/** + * Traverse the hash map and call the callback function + * + * @param map the hash map to traverse + * @param callback the function to be called for every element + * @param user_data the argument to be passed to the callback function + * + * @return true if success, false otherwise + * Note: if the hash map has lock, the map will be locked during traverse, + * keep the callback function as simple as possible. + */ +bool +bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* endof WASM_HASHMAP_H */ diff --git a/core/shared/utils/bh_leb128.c b/core/shared/utils/bh_leb128.c new file mode 100644 index 0000000000..8e4b13dce8 --- /dev/null +++ b/core/shared/utils/bh_leb128.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_leb128.h" + +bh_leb_read_status_t +bh_leb_read(const uint8 *buf, const uint8 *buf_end, uint32 maxbits, bool sign, + uint64 *p_result, size_t *p_offset) +{ + uint64 result = 0; + uint32 shift = 0; + uint32 offset = 0, bcnt = 0; + uint64 byte; + + while (true) { + /* uN or SN must not exceed ceil(N/7) bytes */ + if (bcnt + 1 > (maxbits + 6) / 7) { + return BH_LEB_READ_TOO_LONG; + } + + if ((uintptr_t)buf + offset + 1 < (uintptr_t)buf + || (uintptr_t)buf + offset + 1 > (uintptr_t)buf_end) { + return BH_LEB_READ_UNEXPECTED_END; + } + byte = buf[offset]; + offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + if (!sign && maxbits == 32 && shift >= maxbits) { + /* The top bits set represent values > 32 bits */ + if (((uint8)byte) & 0xf0) + return BH_LEB_READ_OVERFLOW; + } + else if (sign && maxbits == 32) { + if (shift < maxbits) { + /* Sign extend, second-highest bit is the sign bit */ + if ((uint8)byte & 0x40) + result |= (~((uint64)0)) << shift; + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x8; + int top_bits = ((uint8)byte) & 0xf0; + if ((sign_bit_set && top_bits != 0x70) + || (!sign_bit_set && top_bits != 0)) + return BH_LEB_READ_OVERFLOW; + } + } + else if (sign && maxbits == 64) { + if (shift < maxbits) { + /* Sign extend, second-highest bit is the sign bit */ + if ((uint8)byte & 0x40) + result |= (~((uint64)0)) << shift; + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x1; + int top_bits = ((uint8)byte) & 0xfe; + + if ((sign_bit_set && top_bits != 0x7e) + || (!sign_bit_set && top_bits != 0)) + return BH_LEB_READ_OVERFLOW; + } + } + + *p_offset = offset; + *p_result = result; + return BH_LEB_READ_SUCCESS; +} \ No newline at end of file diff --git a/core/shared/utils/bh_leb128.h b/core/shared/utils/bh_leb128.h new file mode 100644 index 0000000000..ce73b4b88d --- /dev/null +++ b/core/shared/utils/bh_leb128.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_LEB128_H +#define _BH_LEB128_H + +#include "bh_platform.h" + +typedef enum { + BH_LEB_READ_SUCCESS, + BH_LEB_READ_TOO_LONG, + BH_LEB_READ_OVERFLOW, + BH_LEB_READ_UNEXPECTED_END, +} bh_leb_read_status_t; + +#ifdef __cplusplus +extern "C" { +#endif + +bh_leb_read_status_t +bh_leb_read(const uint8 *buf, const uint8 *buf_end, uint32 maxbits, bool sign, + uint64 *p_result, size_t *p_offset); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/core/shared/utils/bh_list.c b/core/shared/utils/bh_list.c index 9d6727af06..3265ba6010 100644 --- a/core/shared/utils/bh_list.c +++ b/core/shared/utils/bh_list.c @@ -4,21 +4,22 @@ */ #include "bh_list.h" -#include "bh_assert.h" -#ifdef BH_DEBUG +#if BH_DEBUG != 0 /** - * Test whehter a pointer value has exist in given list. + * Test whether a pointer value has exist in given list. * * @param list pointer to list. * @param elem pointer to elem that will be inserted into list. * @return true if the pointer has been in the list; * false otherwise. */ -BH_STATIC bool bh_list_is_elem_exist(bh_list *list, void *elem); +static bool +bh_list_is_elem_exist(bh_list *list, void *elem); #endif -bh_list_status bh_list_init(bh_list *list) +bh_list_status +bh_list_init(bh_list *list) { if (!list) return BH_LIST_ERROR; @@ -28,23 +29,25 @@ bh_list_status bh_list_init(bh_list *list) return BH_LIST_SUCCESS; } -bh_list_status _bh_list_insert(bh_list *list, void *elem) +bh_list_status +bh_list_insert(bh_list *list, void *elem) { bh_list_link *p = NULL; if (!list || !elem) return BH_LIST_ERROR; -#ifdef BH_DEBUG - bh_assert (!bh_list_is_elem_exist(list, elem)); +#if BH_DEBUG != 0 + bh_assert(!bh_list_is_elem_exist(list, elem)); #endif - p = (bh_list_link *) elem; + p = (bh_list_link *)elem; p->next = (list->head).next; (list->head).next = p; list->len++; return BH_LIST_SUCCESS; } -bh_list_status bh_list_remove(bh_list *list, void *elem) +bh_list_status +bh_list_remove(bh_list *list, void *elem) { bh_list_link *cur = NULL; bh_list_link *prev = NULL; @@ -72,32 +75,37 @@ bh_list_status bh_list_remove(bh_list *list, void *elem) return BH_LIST_ERROR; } -uint32 bh_list_length(bh_list *list) +uint32 +bh_list_length(bh_list *list) { return (list ? list->len : 0); } -void* bh_list_first_elem(bh_list *list) +void * +bh_list_first_elem(bh_list *list) { return (list ? (list->head).next : NULL); } -void* bh_list_elem_next(void *node) +void * +bh_list_elem_next(void *node) { - return (node ? ((bh_list_link *) node)->next : NULL); + return (node ? ((bh_list_link *)node)->next : NULL); } -#ifdef BH_DEBUG -BH_STATIC bool bh_list_is_elem_exist(bh_list *list, void *elem) +#if BH_DEBUG != 0 +static bool +bh_list_is_elem_exist(bh_list *list, void *elem) { bh_list_link *p = NULL; - if (!list || !elem) return false; + if (!list || !elem) + return false; p = (list->head).next; - while (p && p != elem) p = p->next; + while (p && p != elem) + p = p->next; return (p != NULL); } #endif - diff --git a/core/shared/utils/bh_list.h b/core/shared/utils/bh_list.h new file mode 100644 index 0000000000..f102153248 --- /dev/null +++ b/core/shared/utils/bh_list.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_LIST_H +#define _BH_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bh_platform.h" + +/* List user should embedded bh_list_link into list elem data structure + * definition. And bh_list_link data field should be the first field. + * For example, if we would like to use bh_list for our own data type A, + * A must be defined as a structure like below: + * struct A { + * bh_list_link l; + * ... + * }; + * + * bh_list_link is defined as a structure (not typedef void*). + * It will make extend list into bi-direction easy. + */ +typedef struct bh_list_link { + struct bh_list_link *next; +} bh_list_link; + +typedef struct bh_list { + bh_list_link head; + uint32 len; +} bh_list; + +/* list operation return value */ +typedef enum bh_list_status { + BH_LIST_SUCCESS = 0, + BH_LIST_ERROR = -1 +} bh_list_status; + +/** + * Initialize a list. + * + * @param list pointer to list. + * @return BH_LIST_ERROR if OK; + * BH_LIST_ERROR if list pointer is NULL. + */ +bh_list_status +bh_list_init(bh_list *list); + +/** + * Insert an elem pointer into list. The list node memory is maintained by list + * while elem memory is the responsibility of list user. + * + * @param list pointer to list. + * @param elem pointer to elem that will be inserted into list. + * @return BH_LIST_ERROR if OK; + * BH_LIST_ERROR if input is invalid or no memory + * available. + */ +bh_list_status +bh_list_insert(bh_list *list, void *elem); + +/** + * Remove an elem pointer from list. The list node memory is maintained by list + * while elem memory is the responsibility of list user. + * + * @param list pointer to list. + * @param elem pointer to elem that will be inserted into list. + * @return BH_LIST_ERROR if OK; + * BH_LIST_ERROR if element does not exist in given + * list. + */ +bh_list_status +bh_list_remove(bh_list *list, void *elem); + +/** + * Get the list length. + * + * @param list pointer to list. + * @return the length of the list. + */ +uint32 +bh_list_length(bh_list *list); + +/** + * Get the first elem in the list. + * + * @param list pointer to list. + * @return pointer to the first node. + */ +void * +bh_list_first_elem(bh_list *list); + +/** + * Get the next elem of given list input elem. + * + * @param node pointer to list node. + * @return pointer to next list node. + */ +void * +bh_list_elem_next(void *node); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _BH_LIST_H */ diff --git a/core/shared/utils/bh_log.c b/core/shared/utils/bh_log.c index 39d89781d5..69763a2a39 100644 --- a/core/shared/utils/bh_log.c +++ b/core/shared/utils/bh_log.c @@ -2,201 +2,110 @@ * Copyright (C) 2019 Intel Corporation. All rights reserved. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -/** - * @file bh_log.c - * @date Tue Nov 8 18:20:06 2011 - * - * @brief Implementation of Beihai's log system. - */ -#include "bh_assert.h" -#include "bh_time.h" -#include "bh_thread.h" #include "bh_log.h" -#include "bh_platform_log.h" /** * The verbose level of the log system. Only those verbose logs whose - * levels are less than or equal to this value are outputed. - */ -static int log_verbose_level; - -/** - * The lock for protecting the global output stream of logs. - */ -static korp_mutex log_stream_lock; - -/** - * The current logging thread that owns the log_stream_lock. - */ -static korp_tid cur_logging_thread = INVALID_THREAD_ID; - -/** - * Whether the currently being printed log is ennabled. + * levels are less than or equal to this value are output. */ -static bool cur_log_enabled; - -int _bh_log_init() -{ - log_verbose_level = 3; - return vm_mutex_init(&log_stream_lock); -} +static uint32 log_verbose_level = BH_LOG_LEVEL_WARNING; -void _bh_log_set_verbose_level(int level) +void +bh_log_set_verbose_level(uint32 level) { log_verbose_level = level; } -void _bh_log_printf(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - _bh_log_vprintf(fmt, ap); - va_end(ap); -} - -/** - * Return true if the given tag is enabled by the configuration. - * - * @param tag the tag (or prefix) of the log message. - * - * @return true if the log should be enabled. - */ -static bool is_log_enabled(const char *tag) -{ - /* Print all non-verbose or verbose logs whose levels are less than - or equal to the configured verbose level. */ - return tag[0] != 'V' || tag[1] - '0' <= log_verbose_level; -} - -/** - * Helper function for converting "..." to va_list. - */ -static void bh_log_emit_helper(const char *fmt, ...) +#ifndef BH_LOG +void +bh_log(LogLevel log_level, const char *file, int line, const char *fmt, ...) { va_list ap; - va_start(ap, fmt); - bh_log_emit(fmt, ap); - va_end(ap); -} - -extern size_t _bh_time_strftime(char *s, size_t max, const char *format, - int64 time); - -void _bh_log_vprintf(const char *fmt, va_list ap) -{ - korp_tid self = vm_self_thread(); - /* Try to own the log stream and start the log output. */ - if (self != cur_logging_thread) { - vm_mutex_lock(&log_stream_lock); - cur_logging_thread = self; - - if (fmt && (cur_log_enabled = is_log_enabled(fmt))) { - char buf[32]; - bh_time_strftime(buf, 32, "%Y-%m-%d %H:%M:%S", - (int64)bh_time_get_millisecond_from_1970()); - bh_log_emit_helper("\n[%s - %X]: ", buf, (int) self); - - /* Strip the "Vn." prefix. */ - if (fmt && strlen(fmt) >= 3 && fmt[0] == 'V' && fmt[1] && fmt[2]) - fmt += 3; - } - } - - if (cur_log_enabled && fmt) - bh_log_emit(fmt, ap); -} + korp_tid self; + char buf[32] = { 0 }; + uint64 usec; + uint32 t, h, m, s, mills; -void _bh_log_commit() -{ - if (vm_self_thread() != cur_logging_thread) - /* Ignore the single commit without printing anything. */ + if ((uint32)log_level > log_verbose_level) return; - cur_logging_thread = INVALID_THREAD_ID; - vm_mutex_unlock(&log_stream_lock); -} + self = os_self_thread(); -void _bh_log(const char *tag, const char *file, int line, const char *fmt, ...) -{ - va_list ap; + usec = os_time_get_boot_us(); + t = (uint32)(usec / 1000000) % (24 * 60 * 60); + h = t / (60 * 60); + t = t % (60 * 60); + m = t / 60; + s = t % 60; + mills = (uint32)((usec % 1000000) / 1000); - if (tag) - _bh_log_printf(tag); + snprintf(buf, sizeof(buf), + "%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%03" PRIu32, h, m, s, + mills); + +#ifndef BH_VPRINTF + os_printf("[%s - %" PRIXPTR "]: ", buf, (uintptr_t)self); +#endif if (file) - _bh_log_printf("%s:%d", file, line); + os_printf("%s, line %d, ", file, line); va_start(ap, fmt); - _bh_log_vprintf(fmt, ap); + os_vprintf(fmt, ap); va_end(ap); - _bh_log_commit(); + os_printf("\n"); } +#endif -#if defined(BH_DEBUG) - -BH_STATIC char com_switchs[LOG_COM_MAX]; /* 0: off; 1: on */ -BH_STATIC char *com_names[LOG_COM_MAX] = {"app_manager", "gc", "hmc", "utils", - "verifier_jeff", "vmcore_jeff"}; +static uint32 last_time_ms = 0; +static uint32 total_time_ms = 0; -BH_STATIC int com_find_name(const char **com) +void +bh_print_time(const char *prompt) { - int i; - const char *c, *name; - - for (i = 0; i < LOG_COM_MAX; i++) { - c = *com; - name = com_names[i]; - while (*name != '\0') { - if (*c != *name) - break; - c++; - name++; - } - if (*name == '\0') { - *com = c; - return i; - } - } - - return LOG_COM_MAX; -} + uint32 curr_time_ms; -void log_parse_coms(const char *coms) -{ - int i; + if (log_verbose_level < 3) + return; - for (i = LOG_COM_MAX; i--; ) - com_switchs[i] = 0; + curr_time_ms = (uint32)bh_get_tick_ms(); - com_switchs[LOG_COM_UTILS] = 1; /* utils is a common part */ + if (last_time_ms == 0) + last_time_ms = curr_time_ms; - if (coms == NULL) - return; + total_time_ms += curr_time_ms - last_time_ms; - while (*coms != '\0') { - i = com_find_name(&coms); - if (i == LOG_COM_MAX) - break; - com_switchs[i] = 1; + os_printf("%-48s time of last stage: %" PRIu32 " ms, total time: %" PRIu32 + " ms\n", + prompt, curr_time_ms - last_time_ms, total_time_ms); - if (*coms == '\0') - return; - if (*coms != ';') - break; - /* *com == ';' */ - coms++; - } - - /* Log the message without aborting. */ - LOG_DEBUG(LOG_COM_UTILS, "The component names for logging are not right: %s.", coms); + last_time_ms = curr_time_ms; } -int bh_log_dcom_is_enabled(int component) +void +bh_print_proc_mem(const char *prompt) { - return com_switchs[component]; -} + char buf[1024] = { 0 }; -#endif /* defined(BH_DEBUG) */ + if (log_verbose_level < BH_LOG_LEVEL_DEBUG) + return; + if (os_dumps_proc_mem_info(buf, sizeof(buf)) != 0) + return; + + os_printf("%s\n", prompt); + os_printf("===== memory usage =====\n"); + os_printf("%s", buf); + os_printf("==========\n"); + return; +} + +void +bh_log_proc_mem(const char *function, uint32 line) +{ + char prompt[128] = { 0 }; + snprintf(prompt, sizeof(prompt), "[MEM] %s(...) L%" PRIu32, function, line); + bh_print_proc_mem(prompt); +} diff --git a/core/shared/utils/bh_log.h b/core/shared/utils/bh_log.h new file mode 100644 index 0000000000..53921b250e --- /dev/null +++ b/core/shared/utils/bh_log.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +/** + * @file bh_log.h + * @date Tue Nov 8 18:19:10 2011 + * + * @brief This log system supports wrapping multiple outputs into one + * log message. This is useful for outputting variable-length logs + * without additional memory overhead (the buffer for concatenating + * the message), e.g. exception stack trace, which cannot be printed + * by a single log calling without the help of an additional buffer. + * Avoiding additional memory buffer is useful for resource-constraint + * systems. It can minimize the impact of log system on applications + * and logs can be printed even when no enough memory is available. + * Functions with prefix "_" are private functions. Only macros that + * are not start with "_" are exposed and can be used. + */ + +#ifndef _BH_LOG_H +#define _BH_LOG_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + BH_LOG_LEVEL_FATAL = 0, + BH_LOG_LEVEL_ERROR = 1, + BH_LOG_LEVEL_WARNING = 2, + BH_LOG_LEVEL_DEBUG = 3, + BH_LOG_LEVEL_VERBOSE = 4 +} LogLevel; + +void +bh_log_set_verbose_level(uint32 level); + +#ifndef BH_LOG +void +bh_log(LogLevel log_level, const char *file, int line, const char *fmt, ...); +#else +void +BH_LOG(uint32 log_level, const char *file, int line, const char *fmt, ...); +#define bh_log BH_LOG +#endif + +#ifdef BH_PLATFORM_NUTTX + +#undef LOG_FATAL +#undef LOG_ERROR +#undef LOG_WARNING +#undef LOG_VERBOSE +#undef LOG_DEBUG + +#endif + +#if BH_DEBUG != 0 +#define LOG_FATAL(...) \ + bh_log(BH_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG_FATAL(...) \ + bh_log(BH_LOG_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__) +#endif + +#define LOG_ERROR(...) bh_log(BH_LOG_LEVEL_ERROR, NULL, 0, __VA_ARGS__) +#define LOG_WARNING(...) bh_log(BH_LOG_LEVEL_WARNING, NULL, 0, __VA_ARGS__) +#define LOG_VERBOSE(...) bh_log(BH_LOG_LEVEL_VERBOSE, NULL, 0, __VA_ARGS__) + +#if BH_DEBUG != 0 +#define LOG_DEBUG(...) \ + bh_log(BH_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG_DEBUG(...) (void)0 +#endif + +void +bh_print_time(const char *prompt); + +void +bh_print_proc_mem(const char *prompt); + +void +bh_log_proc_mem(const char *function, uint32 line); + +#define LOG_PROC_MEM(...) bh_log_proc_mem(__FUNCTION__, __LINE__) + +#ifdef __cplusplus +} +#endif + +#endif /* _BH_LOG_H */ diff --git a/core/shared/utils/bh_platform.h b/core/shared/utils/bh_platform.h new file mode 100644 index 0000000000..86aef839dd --- /dev/null +++ b/core/shared/utils/bh_platform.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_PLATFORM_H +#define _BH_PLATFORM_H + +#include "../platform/include/platform_common.h" +#include "../platform/include/platform_api_vmcore.h" +#include "../platform/include/platform_api_extension.h" +#include "bh_assert.h" +#include "bh_common.h" +#include "bh_hashmap.h" +#include "bh_list.h" +#include "bh_log.h" +#include "bh_queue.h" +#include "bh_vector.h" +#include "runtime_timer.h" + +/** + * WA_MALLOC/WA_FREE need to be redefined for both + * runtime native and WASM app respectively. + * + * Some source files are shared for building native and WASM, + * and this the mem allocator API for these files. + * + * Here we define it for the native world + */ +#ifndef WA_MALLOC +#define WA_MALLOC wasm_runtime_malloc +#endif + +#ifndef WA_FREE +#define WA_FREE wasm_runtime_free +#endif + +#endif /* #ifndef _BH_PLATFORM_H */ diff --git a/core/shared/utils/bh_queue.c b/core/shared/utils/bh_queue.c index cd6a58166e..4fc756fbe4 100644 --- a/core/shared/utils/bh_queue.c +++ b/core/shared/utils/bh_queue.c @@ -4,17 +4,13 @@ */ #include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "bh_time.h" -#include "bh_common.h" - -typedef struct _bh_queue_node { - struct _bh_queue_node * next; - struct _bh_queue_node * prev; + +typedef struct bh_queue_node { + struct bh_queue_node *next; + struct bh_queue_node *prev; unsigned short tag; unsigned int len; - void * body; + void *body; bh_msg_cleaner msg_cleaner; } bh_queue_node; @@ -24,23 +20,26 @@ struct bh_queue { unsigned int cnt; unsigned int max; unsigned int drops; - bh_queue_node * head; - bh_queue_node * tail; + bh_queue_node *head; + bh_queue_node *tail; bool exit_loop_run; }; -char * bh_message_payload(bh_message_t message) +char * +bh_message_payload(bh_message_t message) { return message->body; } -uint32 bh_message_payload_len(bh_message_t message) +uint32 +bh_message_payload_len(bh_message_t message) { return message->len; } -int bh_message_type(bh_message_t message) +int +bh_message_type(bh_message_t message) { return message->tag; } @@ -72,7 +71,8 @@ bh_queue_create() return queue; } -void bh_queue_destroy(bh_queue *queue) +void +bh_queue_destroy(bh_queue *queue) { bh_queue_node *node; @@ -93,16 +93,18 @@ void bh_queue_destroy(bh_queue *queue) bh_queue_free(queue); } -bool bh_post_msg2(bh_queue *queue, bh_queue_node *msg) +bool +bh_post_msg2(bh_queue *queue, bh_queue_node *msg) { + bh_queue_mutex_lock(&queue->queue_lock); + if (queue->cnt >= queue->max) { queue->drops++; bh_free_msg(msg); + bh_queue_mutex_unlock(&queue->queue_lock); return false; } - bh_queue_mutex_lock(&queue->queue_lock); - if (queue->cnt == 0) { bh_assert(queue->head == NULL); bh_assert(queue->tail == NULL); @@ -111,7 +113,8 @@ bool bh_post_msg2(bh_queue *queue, bh_queue_node *msg) queue->cnt = 1; bh_queue_cond_signal(&queue->queue_wait_cond); - } else { + } + else { msg->next = NULL; msg->prev = queue->tail; queue->tail->next = msg; @@ -124,14 +127,16 @@ bool bh_post_msg2(bh_queue *queue, bh_queue_node *msg) return true; } -bool bh_post_msg(bh_queue *queue, unsigned short tag, void *body, - unsigned int len) +bool +bh_post_msg(bh_queue *queue, unsigned short tag, void *body, unsigned int len) { bh_queue_node *msg = bh_new_msg(tag, body, len, NULL); if (msg == NULL) { + bh_queue_mutex_lock(&queue->queue_lock); queue->drops++; + bh_queue_mutex_unlock(&queue->queue_lock); if (len != 0 && body) - bh_free(body); + BH_FREE(body); return false; } @@ -143,23 +148,24 @@ bool bh_post_msg(bh_queue *queue, unsigned short tag, void *body, return true; } -bh_queue_node * bh_new_msg(unsigned short tag, void *body, unsigned int len, - void * handler) +bh_queue_node * +bh_new_msg(unsigned short tag, void *body, unsigned int len, void *handler) { - bh_queue_node *msg = (bh_queue_node*) bh_queue_malloc( - sizeof(bh_queue_node)); + bh_queue_node *msg = + (bh_queue_node *)bh_queue_malloc(sizeof(bh_queue_node)); if (msg == NULL) return NULL; memset(msg, 0, sizeof(bh_queue_node)); msg->len = len; msg->body = body; msg->tag = tag; - msg->msg_cleaner = (bh_msg_cleaner) handler; + msg->msg_cleaner = (bh_msg_cleaner)handler; return msg; } -void bh_free_msg(bh_queue_node *msg) +void +bh_free_msg(bh_queue_node *msg) { if (msg->msg_cleaner) { msg->msg_cleaner(msg->body); @@ -167,7 +173,7 @@ void bh_free_msg(bh_queue_node *msg) return; } - // note: sometime we just use the payload pointer for a integer value + // note: sometimes we just use the payload pointer for an integer value // len!=0 is the only indicator about the body is an allocated buffer. if (msg->body && msg->len) bh_queue_free(msg->body); @@ -175,7 +181,8 @@ void bh_free_msg(bh_queue_node *msg) bh_queue_free(msg); } -bh_message_t bh_get_msg(bh_queue *queue, int timeout) +bh_message_t +bh_get_msg(bh_queue *queue, uint64 timeout_us) { bh_queue_node *msg = NULL; bh_queue_mutex_lock(&queue->queue_lock); @@ -184,25 +191,27 @@ bh_message_t bh_get_msg(bh_queue *queue, int timeout) bh_assert(queue->head == NULL); bh_assert(queue->tail == NULL); - if (timeout == 0) { + if (timeout_us == 0) { bh_queue_mutex_unlock(&queue->queue_lock); return NULL; } bh_queue_cond_timedwait(&queue->queue_wait_cond, &queue->queue_lock, - timeout); + timeout_us); } if (queue->cnt == 0) { bh_assert(queue->head == NULL); bh_assert(queue->tail == NULL); - } else if (queue->cnt == 1) { + } + else if (queue->cnt == 1) { bh_assert(queue->head == queue->tail); msg = queue->head; queue->head = queue->tail = NULL; queue->cnt = 0; - } else { + } + else { msg = queue->head; queue->head = queue->head->next; queue->head->prev = NULL; @@ -214,7 +223,8 @@ bh_message_t bh_get_msg(bh_queue *queue, int timeout) return msg; } -unsigned bh_queue_get_message_count(bh_queue *queue) +unsigned +bh_queue_get_message_count(bh_queue *queue) { if (!queue) return 0; @@ -222,15 +232,15 @@ unsigned bh_queue_get_message_count(bh_queue *queue) return queue->cnt; } -void bh_queue_enter_loop_run(bh_queue *queue, - bh_queue_handle_msg_callback handle_cb, - void *arg) +void +bh_queue_enter_loop_run(bh_queue *queue, bh_queue_handle_msg_callback handle_cb, + void *arg) { if (!queue) return; while (!queue->exit_loop_run) { - bh_queue_node * message = bh_get_msg(queue, (int)BH_WAIT_FOREVER); + bh_queue_node *message = bh_get_msg(queue, BHT_WAIT_FOREVER); if (message) { handle_cb(message, arg); @@ -239,7 +249,8 @@ void bh_queue_enter_loop_run(bh_queue *queue, } } -void bh_queue_exit_loop_run(bh_queue *queue) +void +bh_queue_exit_loop_run(bh_queue *queue) { if (queue) { queue->exit_loop_run = true; diff --git a/core/shared/utils/bh_queue.h b/core/shared/utils/bh_queue.h new file mode 100644 index 0000000000..c15f435266 --- /dev/null +++ b/core/shared/utils/bh_queue.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_QUEUE_H +#define _BH_QUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bh_platform.h" + +struct bh_queue_node; +typedef struct bh_queue_node *bh_message_t; +struct bh_queue; +typedef struct bh_queue bh_queue; + +typedef void (*bh_queue_handle_msg_callback)(void *message, void *arg); + +#define bh_queue_malloc BH_MALLOC +#define bh_queue_free BH_FREE + +#define bh_queue_mutex korp_mutex +#define bh_queue_cond korp_cond + +#define bh_queue_mutex_init os_mutex_init +#define bh_queue_mutex_destroy os_mutex_destroy +#define bh_queue_mutex_lock os_mutex_lock +#define bh_queue_mutex_unlock os_mutex_unlock + +#define bh_queue_cond_init os_cond_init +#define bh_queue_cond_destroy os_cond_destroy +#define bh_queue_cond_wait os_cond_wait +#define bh_queue_cond_timedwait os_cond_reltimedwait +#define bh_queue_cond_signal os_cond_signal +#define bh_queue_cond_broadcast os_cond_broadcast + +typedef void (*bh_msg_cleaner)(void *msg); + +bh_queue * +bh_queue_create(void); + +void +bh_queue_destroy(bh_queue *queue); + +char * +bh_message_payload(bh_message_t message); +uint32 +bh_message_payload_len(bh_message_t message); +int +bh_message_type(bh_message_t message); + +bh_message_t +bh_new_msg(unsigned short tag, void *body, unsigned int len, void *handler); +void +bh_free_msg(bh_message_t msg); +bool +bh_post_msg(bh_queue *queue, unsigned short tag, void *body, unsigned int len); +bool +bh_post_msg2(bh_queue *queue, bh_message_t msg); + +bh_message_t +bh_get_msg(bh_queue *queue, uint64 timeout_us); + +unsigned +bh_queue_get_message_count(bh_queue *queue); + +void +bh_queue_enter_loop_run(bh_queue *queue, bh_queue_handle_msg_callback handle_cb, + void *arg); +void +bh_queue_exit_loop_run(bh_queue *queue); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _BH_QUEUE_H */ diff --git a/core/shared/utils/bh_vector.c b/core/shared/utils/bh_vector.c index bb7900f447..7f0c74b9ba 100644 --- a/core/shared/utils/bh_vector.c +++ b/core/shared/utils/bh_vector.c @@ -3,52 +3,57 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#include "bh_log.h" #include "bh_vector.h" -#include "bh_memory.h" - -static uint8* -alloc_vector_data(uint32 length, uint32 size_elem) +static uint8 * +alloc_vector_data(size_t length, size_t size_elem) { uint64 total_size = ((uint64)size_elem) * length; uint8 *data; - if (total_size > UINT32_MAX) { + if (length > UINT32_MAX || size_elem > UINT32_MAX + || total_size > UINT32_MAX) { return NULL; } - if ((data = bh_malloc((uint32)total_size))) { + if ((data = BH_MALLOC((uint32)total_size))) { memset(data, 0, (uint32)total_size); } return data; } +/** + * every caller of `extend_vector` must provide + * a thread-safe environment. + */ static bool -extend_vector(Vector *vector, uint32 length) +extend_vector(Vector *vector, size_t length) { uint8 *data; - if (length <= vector->max_elements) + if (length <= vector->max_elems) return true; - if (length < vector->size_elem * 3 / 2) - length = vector->size_elem * 3 / 2; + if (length < vector->max_elems * 3 / 2) + length = vector->max_elems * 3 / 2; if (!(data = alloc_vector_data(length, vector->size_elem))) { return false; } - memcpy(data, vector->data, vector->size_elem * vector->max_elements); - bh_free(vector->data); + bh_memcpy_s(data, (uint32)(vector->size_elem * length), vector->data, + (uint32)(vector->size_elem * vector->max_elems)); + BH_FREE(vector->data); + vector->data = data; - vector->max_elements = length; + vector->max_elems = length; return true; } bool -bh_vector_init(Vector *vector, uint32 init_length, uint32 size_elem) +bh_vector_init(Vector *vector, size_t init_length, size_t size_elem, + bool use_lock) { if (!vector) { LOG_ERROR("Init vector failed: vector is NULL.\n"); @@ -65,8 +70,28 @@ bh_vector_init(Vector *vector, uint32 init_length, uint32 size_elem) } vector->size_elem = size_elem; - vector->max_elements = init_length; - vector->num_elements = 0; + vector->max_elems = init_length; + vector->num_elems = 0; + vector->lock = NULL; + + if (use_lock) { + if (!(vector->lock = BH_MALLOC(sizeof(korp_mutex)))) { + LOG_ERROR("Init vector failed: alloc locker failed.\n"); + bh_vector_destroy(vector); + return false; + } + + if (BHT_OK != os_mutex_init(vector->lock)) { + LOG_ERROR("Init vector failed: init locker failed.\n"); + + BH_FREE(vector->lock); + vector->lock = NULL; + + bh_vector_destroy(vector); + return false; + } + } + return true; } @@ -78,80 +103,116 @@ bh_vector_set(Vector *vector, uint32 index, const void *elem_buf) return false; } - if (index >= vector->num_elements) { + if (index >= vector->num_elems) { LOG_ERROR("Set vector elem failed: invalid elem index.\n"); return false; } - memcpy(vector->data + vector->size_elem * index, - elem_buf, vector->size_elem); + if (vector->lock) + os_mutex_lock(vector->lock); + bh_memcpy_s(vector->data + vector->size_elem * index, + (uint32)vector->size_elem, elem_buf, (uint32)vector->size_elem); + if (vector->lock) + os_mutex_unlock(vector->lock); return true; } -bool bh_vector_get(const Vector *vector, uint32 index, void *elem_buf) +bool +bh_vector_get(Vector *vector, uint32 index, void *elem_buf) { if (!vector || !elem_buf) { LOG_ERROR("Get vector elem failed: vector or elem buf is NULL.\n"); return false; } - if (index >= vector->num_elements) { + if (index >= vector->num_elems) { LOG_ERROR("Get vector elem failed: invalid elem index.\n"); return false; } - memcpy(elem_buf, vector->data + vector->size_elem * index, - vector->size_elem); + if (vector->lock) + os_mutex_lock(vector->lock); + bh_memcpy_s(elem_buf, (uint32)vector->size_elem, + vector->data + vector->size_elem * index, + (uint32)vector->size_elem); + if (vector->lock) + os_mutex_unlock(vector->lock); return true; } -bool bh_vector_insert(Vector *vector, uint32 index, const void *elem_buf) +bool +bh_vector_insert(Vector *vector, uint32 index, const void *elem_buf) { - uint32 i; + size_t i; uint8 *p; + bool ret = false; if (!vector || !elem_buf) { LOG_ERROR("Insert vector elem failed: vector or elem buf is NULL.\n"); - return false; + goto just_return; } - if (index >= vector->num_elements) { + if (index >= vector->num_elems) { LOG_ERROR("Insert vector elem failed: invalid elem index.\n"); - return false; + goto just_return; } - if (!extend_vector(vector, vector->num_elements + 1)) { + if (vector->lock) + os_mutex_lock(vector->lock); + + if (!extend_vector(vector, vector->num_elems + 1)) { LOG_ERROR("Insert vector elem failed: extend vector failed.\n"); - return false; + goto unlock_return; } - p = vector->data + vector->size_elem * vector->num_elements; - for (i = vector->num_elements - 1; i > index; i--) { - memcpy(p, p - vector->size_elem, vector->size_elem); + p = vector->data + vector->size_elem * vector->num_elems; + for (i = vector->num_elems - 1; i > index; i--) { + bh_memcpy_s(p, (uint32)vector->size_elem, p - vector->size_elem, + (uint32)vector->size_elem); p -= vector->size_elem; } - memcpy(p, elem_buf, vector->size_elem); - vector->num_elements++; - return true; + bh_memcpy_s(p, (uint32)vector->size_elem, elem_buf, + (uint32)vector->size_elem); + vector->num_elems++; + ret = true; + +unlock_return: + if (vector->lock) + os_mutex_unlock(vector->lock); +just_return: + return ret; } -bool bh_vector_append(Vector *vector, const void *elem_buf) +bool +bh_vector_append(Vector *vector, const void *elem_buf) { + bool ret = false; + if (!vector || !elem_buf) { LOG_ERROR("Append vector elem failed: vector or elem buf is NULL.\n"); - return false; + goto just_return; } - if (!extend_vector(vector, vector->num_elements + 1)) { - LOG_ERROR("Append ector elem failed: extend vector failed.\n"); - return false; + /* make sure one more slot is used by the thread who allocates it */ + if (vector->lock) + os_mutex_lock(vector->lock); + + if (!extend_vector(vector, vector->num_elems + 1)) { + LOG_ERROR("Append vector elem failed: extend vector failed.\n"); + goto unlock_return; } - memcpy(vector->data + vector->size_elem * vector->num_elements, - elem_buf, vector->size_elem); - vector->num_elements++; - return true; + bh_memcpy_s(vector->data + vector->size_elem * vector->num_elems, + (uint32)vector->size_elem, elem_buf, (uint32)vector->size_elem); + vector->num_elems++; + ret = true; + +unlock_return: + if (vector->lock) + os_mutex_unlock(vector->lock); +just_return: + return ret; } bool @@ -165,30 +226,36 @@ bh_vector_remove(Vector *vector, uint32 index, void *old_elem_buf) return false; } - if (index >= vector->num_elements) { + if (index >= vector->num_elems) { LOG_ERROR("Remove vector elem failed: invalid elem index.\n"); return false; } + if (vector->lock) + os_mutex_lock(vector->lock); p = vector->data + vector->size_elem * index; if (old_elem_buf) { - memcpy(old_elem_buf, p, vector->size_elem); + bh_memcpy_s(old_elem_buf, (uint32)vector->size_elem, p, + (uint32)vector->size_elem); } - for (i = index; i < vector->num_elements - 1; i++) { - memcpy(p, p + vector->size_elem, vector->size_elem); + for (i = index; i < vector->num_elems - 1; i++) { + bh_memcpy_s(p, (uint32)vector->size_elem, p + vector->size_elem, + (uint32)vector->size_elem); p += vector->size_elem; } - vector->num_elements--; + vector->num_elems--; + if (vector->lock) + os_mutex_unlock(vector->lock); return true; } -uint32 +size_t bh_vector_size(const Vector *vector) { - return vector ? vector->num_elements : 0; + return vector ? vector->num_elems : 0; } bool @@ -200,7 +267,13 @@ bh_vector_destroy(Vector *vector) } if (vector->data) - bh_free(vector->data); + BH_FREE(vector->data); + + if (vector->lock) { + os_mutex_destroy(vector->lock); + BH_FREE(vector->lock); + } + memset(vector, 0, sizeof(Vector)); return true; } diff --git a/core/shared/include/bh_vector.h b/core/shared/utils/bh_vector.h similarity index 91% rename from core/shared/include/bh_vector.h rename to core/shared/utils/bh_vector.h index 36f5bf11d6..d0aaaf19b0 100644 --- a/core/shared/include/bh_vector.h +++ b/core/shared/utils/bh_vector.h @@ -8,7 +8,6 @@ #include "bh_platform.h" - #ifdef __cplusplus extern "C" { #endif @@ -16,14 +15,15 @@ extern "C" { #define DEFAULT_VECTOR_INIT_SIZE 8 typedef struct Vector { - /* size of each element */ - uint32 size_elem; /* max element number */ - uint32 max_elements; - /* current element num */ - uint32 num_elements; + size_t max_elems; /* vector data allocated */ uint8 *data; + /* current element num */ + size_t num_elems; + /* size of each element */ + size_t size_elem; + void *lock; } Vector; /** @@ -36,7 +36,8 @@ typedef struct Vector { * @return true if success, false otherwise */ bool -bh_vector_init(Vector *vector, uint32 init_length, uint32 size_elem); +bh_vector_init(Vector *vector, size_t init_length, size_t size_elem, + bool use_lock); /** * Set element of vector @@ -61,7 +62,7 @@ bh_vector_set(Vector *vector, uint32 index, const void *elem_buf); * @return true if success, false otherwise */ bool -bh_vector_get(const Vector *vector, uint32 index, void *elem_buf); +bh_vector_get(Vector *vector, uint32 index, void *elem_buf); /** * Insert element of vector @@ -105,7 +106,7 @@ bh_vector_remove(Vector *vector, uint32 index, void *old_elem_buf); * * @return return the size of the vector */ -uint32 +size_t bh_vector_size(const Vector *vector); /** @@ -123,4 +124,3 @@ bh_vector_destroy(Vector *vector); #endif #endif /* endof _WASM_VECTOR_H */ - diff --git a/core/shared/utils/gnuc.h b/core/shared/utils/gnuc.h new file mode 100644 index 0000000000..70000ae0b9 --- /dev/null +++ b/core/shared/utils/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/shared/utils/runtime_timer.c b/core/shared/utils/runtime_timer.c index 09e3f14119..410f05dacf 100644 --- a/core/shared/utils/runtime_timer.c +++ b/core/shared/utils/runtime_timer.c @@ -4,29 +4,30 @@ */ #include "runtime_timer.h" -#include "bh_thread.h" -#include "bh_time.h" -#define PRINT(...) -//#define PRINT printf +#if 1 +#define PRINT(...) (void)0 +#else +#define PRINT printf +#endif typedef struct _app_timer { - struct _app_timer * next; + struct _app_timer *next; uint32 id; - unsigned int interval; + uint32 interval; uint64 expiry; bool is_periodic; } app_timer_t; struct _timer_ctx { - app_timer_t * g_app_timers; - app_timer_t * idle_timers; - app_timer_t * free_timers; - unsigned int g_max_id; + app_timer_t *app_timers; + app_timer_t *idle_timers; + app_timer_t *free_timers; + uint32 max_timer_id; int pre_allocated; - unsigned int owner; + uint32 owner; - //add mutext and conditions + /* mutex and condition */ korp_cond cond; korp_mutex mutex; @@ -34,69 +35,81 @@ struct _timer_ctx { check_timer_expiry_f refresh_checker; }; -uint32 bh_get_elpased_ms(uint32 * last_system_clock) +uint64 +bh_get_tick_ms() { - uint32 elpased_ms; + return os_time_get_boot_us() / 1000; +} - // attention: the bh_get_tick_ms() return 64 bits integer. - // but the bh_get_elpased_ms() is designed to use 32 bits clock count. - uint32 now = (uint32) bh_get_tick_ms(); +uint32 +bh_get_elpased_ms(uint32 *last_system_clock) +{ + uint32 elapsed_ms; + /* attention: the bh_get_tick_ms() returns a 64-bit integer, but + bh_get_elpased_ms() is designed to use a 32-bit clock count */ + uint32 now = (uint32)bh_get_tick_ms(); - // system clock overrun + /* system clock overrun */ if (now < *last_system_clock) { - elpased_ms = now + (0xFFFFFFFF - *last_system_clock) + 1; - } else { - elpased_ms = now - *last_system_clock; + PRINT("system clock overrun!\n"); + elapsed_ms = now + (UINT32_MAX - *last_system_clock) + 1; + } + else { + elapsed_ms = now - *last_system_clock; } *last_system_clock = now; - - return elpased_ms; + return elapsed_ms; } -static app_timer_t * remove_timer_from(timer_ctx_t ctx, uint32 timer_id, - bool active_list) +static app_timer_t * +remove_timer_from(timer_ctx_t ctx, uint32 timer_id, bool active_list) { - vm_mutex_lock(&ctx->mutex); - app_timer_t ** head; + app_timer_t **head, *prev, *t; + + os_mutex_lock(&ctx->mutex); + if (active_list) - head = &ctx->g_app_timers; + head = &ctx->app_timers; else head = &ctx->idle_timers; - app_timer_t * t = *head; - app_timer_t * prev = NULL; + t = *head; + prev = NULL; while (t) { if (t->id == timer_id) { if (prev == NULL) { *head = t->next; - PRINT("removed timer [%d] at head from list %d\n", t->id, active_list); - } else { + PRINT("removed timer [%d] at head from list %d\n", t->id, + active_list); + } + else { prev->next = t->next; - PRINT("removed timer [%d] after [%d] from list %d\n", t->id, prev->id, active_list); + PRINT("removed timer [%d] after [%d] from list %d\n", t->id, + prev->id, active_list); } - vm_mutex_unlock(&ctx->mutex); + os_mutex_unlock(&ctx->mutex); if (active_list && prev == NULL && ctx->refresh_checker) ctx->refresh_checker(ctx); - return t; - } else { + } + else { prev = t; t = t->next; } } - vm_mutex_unlock(&ctx->mutex); - + os_mutex_unlock(&ctx->mutex); return NULL; } -static app_timer_t * remove_timer(timer_ctx_t ctx, uint32 timer_id, - bool * active) +static app_timer_t * +remove_timer(timer_ctx_t ctx, uint32 timer_id, bool *active) { - app_timer_t* t = remove_timer_from(ctx, timer_id, true); + app_timer_t *t = remove_timer_from(ctx, timer_id, true); + if (t) { if (active) *active = true; @@ -108,81 +121,86 @@ static app_timer_t * remove_timer(timer_ctx_t ctx, uint32 timer_id, return remove_timer_from(ctx, timer_id, false); } -static void reschedule_timer(timer_ctx_t ctx, app_timer_t * timer) +static void +reschedule_timer(timer_ctx_t ctx, app_timer_t *timer) { + app_timer_t *t; + app_timer_t *prev = NULL; - vm_mutex_lock(&ctx->mutex); - app_timer_t * t = ctx->g_app_timers; - app_timer_t * prev = NULL; + os_mutex_lock(&ctx->mutex); + t = ctx->app_timers; timer->next = NULL; timer->expiry = bh_get_tick_ms() + timer->interval; while (t) { if (timer->expiry < t->expiry) { if (prev == NULL) { - timer->next = ctx->g_app_timers; - ctx->g_app_timers = timer; + timer->next = ctx->app_timers; + ctx->app_timers = timer; PRINT("rescheduled timer [%d] at head\n", timer->id); - } else { + } + else { timer->next = t; prev->next = timer; - PRINT("rescheduled timer [%d] after [%d]\n", timer->id, prev->id); + PRINT("rescheduled timer [%d] after [%d]\n", timer->id, + prev->id); } - vm_mutex_unlock(&ctx->mutex); - - // ensure the refresh_checker() is called out of the lock - if (prev == NULL && ctx->refresh_checker) - ctx->refresh_checker(ctx); - - return; - } else { + goto out; + } + else { prev = t; t = t->next; } } if (prev) { - // insert to the list end + /* insert to the list end */ prev->next = timer; - PRINT("rescheduled timer [%d] at end, after [%d]\n", timer->id, prev->id); - } else { - // insert at the begin - bh_assert(ctx->g_app_timers == NULL); - ctx->g_app_timers = timer; + PRINT("rescheduled timer [%d] at end, after [%d]\n", timer->id, + prev->id); + } + else { + /* insert at the beginning */ + bh_assert(ctx->app_timers == NULL); + ctx->app_timers = timer; PRINT("rescheduled timer [%d] as first\n", timer->id); } - vm_mutex_unlock(&ctx->mutex); +out: + os_mutex_unlock(&ctx->mutex); - // ensure the refresh_checker() is called out of the lock + /* ensure the refresh_checker() is called out of the lock */ if (prev == NULL && ctx->refresh_checker) ctx->refresh_checker(ctx); - } -static void release_timer(timer_ctx_t ctx, app_timer_t * t) +static void +release_timer(timer_ctx_t ctx, app_timer_t *t) { if (ctx->pre_allocated) { - vm_mutex_lock(&ctx->mutex); + os_mutex_lock(&ctx->mutex); t->next = ctx->free_timers; ctx->free_timers = t; PRINT("recycle timer :%d\n", t->id); - vm_mutex_unlock(&ctx->mutex); - } else { + os_mutex_unlock(&ctx->mutex); + } + else { PRINT("destroy timer :%d\n", t->id); - bh_free(t); + BH_FREE(t); } } -void release_timer_list(app_timer_t ** p_list) +void +release_timer_list(app_timer_t **p_list) { app_timer_t *t = *p_list; + while (t) { app_timer_t *next = t->next; PRINT("destroy timer list:%d\n", t->id); - bh_free(t); + BH_FREE(t); t = next; } @@ -190,27 +208,29 @@ void release_timer_list(app_timer_t ** p_list) } /* - * - * API exposed - * + * API exposed */ -timer_ctx_t create_timer_ctx(timer_callback_f timer_handler, - check_timer_expiry_f expiery_checker, int prealloc_num, - unsigned int owner) +timer_ctx_t +create_timer_ctx(timer_callback_f timer_handler, + check_timer_expiry_f expiry_checker, int prealloc_num, + unsigned int owner) { - timer_ctx_t ctx = (timer_ctx_t) bh_malloc(sizeof(struct _timer_ctx)); + timer_ctx_t ctx = (timer_ctx_t)BH_MALLOC(sizeof(struct _timer_ctx)); + if (ctx == NULL) return NULL; + memset(ctx, 0, sizeof(struct _timer_ctx)); ctx->timer_callback = timer_handler; ctx->pre_allocated = prealloc_num; - ctx->refresh_checker = expiery_checker; + ctx->refresh_checker = expiry_checker; ctx->owner = owner; while (prealloc_num > 0) { - app_timer_t *timer = (app_timer_t*) bh_malloc(sizeof(app_timer_t)); + app_timer_t *timer = (app_timer_t *)BH_MALLOC(sizeof(app_timer_t)); + if (timer == NULL) goto cleanup; @@ -220,76 +240,83 @@ timer_ctx_t create_timer_ctx(timer_callback_f timer_handler, prealloc_num--; } - vm_cond_init(&ctx->cond); - vm_mutex_init(&ctx->mutex); + if (os_cond_init(&ctx->cond) != 0) + goto cleanup; - PRINT("timer ctx created. pre-alloc: %d\n", ctx->pre_allocated); + if (os_mutex_init(&ctx->mutex) != 0) { + os_cond_destroy(&ctx->cond); + goto cleanup; + } + PRINT("timer ctx created. pre-alloc: %d\n", ctx->pre_allocated); return ctx; - cleanup: - +cleanup: if (ctx) { release_timer_list(&ctx->free_timers); - bh_free(ctx); + BH_FREE(ctx); } PRINT("timer ctx create failed\n"); return NULL; } -void destroy_timer_ctx(timer_ctx_t ctx) +void +destroy_timer_ctx(timer_ctx_t ctx) { while (ctx->free_timers) { - void * tmp = ctx->free_timers; + void *tmp = ctx->free_timers; ctx->free_timers = ctx->free_timers->next; - bh_free(tmp); + BH_FREE(tmp); } cleanup_app_timers(ctx); - vm_cond_destroy(&ctx->cond); - vm_mutex_destroy(&ctx->mutex); - bh_free(ctx); + os_cond_destroy(&ctx->cond); + os_mutex_destroy(&ctx->mutex); + BH_FREE(ctx); } -unsigned int timer_ctx_get_owner(timer_ctx_t ctx) +unsigned int +timer_ctx_get_owner(timer_ctx_t ctx) { return ctx->owner; } -void add_idle_timer(timer_ctx_t ctx, app_timer_t * timer) +void +add_idle_timer(timer_ctx_t ctx, app_timer_t *timer) { - vm_mutex_lock(&ctx->mutex); + os_mutex_lock(&ctx->mutex); timer->next = ctx->idle_timers; ctx->idle_timers = timer; - vm_mutex_unlock(&ctx->mutex); + os_mutex_unlock(&ctx->mutex); } -uint32 sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, - bool auto_start) +uint32 +sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, bool auto_start) { - app_timer_t *timer; if (ctx->pre_allocated) { - if (ctx->free_timers == NULL) + if (ctx->free_timers == NULL) { return (uint32)-1; + } else { timer = ctx->free_timers; ctx->free_timers = timer->next; } - } else { - timer = (app_timer_t*) bh_malloc(sizeof(app_timer_t)); + } + else { + timer = (app_timer_t *)BH_MALLOC(sizeof(app_timer_t)); if (timer == NULL) return (uint32)-1; } memset(timer, 0, sizeof(*timer)); - ctx->g_max_id++; - if (ctx->g_max_id == (uint32)-1) - ctx->g_max_id++; - timer->id = ctx->g_max_id; + ctx->max_timer_id++; + if (ctx->max_timer_id == (uint32)-1) + ctx->max_timer_id++; + timer->id = ctx->max_timer_id; timer->interval = (uint32)interval; timer->is_periodic = is_period; @@ -301,10 +328,12 @@ uint32 sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, return timer->id; } -bool sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id) +bool +sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id) { bool from_active; - app_timer_t * t = remove_timer(ctx, timer_id, &from_active); + app_timer_t *t = remove_timer(ctx, timer_id, &from_active); + if (t == NULL) return false; @@ -314,10 +343,12 @@ bool sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id) return from_active; } -bool sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id) +bool +sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id) { bool from_active; - app_timer_t * t = remove_timer(ctx, timer_id, &from_active); + app_timer_t *t = remove_timer(ctx, timer_id, &from_active); + if (t == NULL) return false; @@ -327,14 +358,15 @@ bool sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id) return true; } -bool sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval) +bool +sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval) { - app_timer_t * t = remove_timer(ctx, timer_id, NULL); + app_timer_t *t = remove_timer(ctx, timer_id, NULL); + if (t == NULL) return false; - if (interval > 0) - t->interval = (uint32)interval; + t->interval = (uint32)interval; reschedule_timer(ctx, t); @@ -343,86 +375,95 @@ bool sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval) } /* - * - * - * API called by the timer manager from another thread or the kernel timer handler - * - * + * API called by the timer manager from another thread or the kernel timer + * handler */ -// lookup the app queue by the module name -//post a timeout message to the app queue -// -static void handle_expired_timers(timer_ctx_t ctx, app_timer_t * expired) +/** + * lookup the app queue by the module name + * post a timeout message to the app queue + */ +static void +handle_expired_timers(timer_ctx_t ctx, app_timer_t *expired) { while (expired) { - app_timer_t * t = expired; + app_timer_t *t = expired; ctx->timer_callback(t->id, ctx->owner); + /* get next expired timer first, since the following + operation may change expired->next */ expired = expired->next; if (t->is_periodic) { - // if it is repeating, then reschedule it; + /* if it is repeating, then reschedule it */ reschedule_timer(ctx, t); - - } else { - // else move it to idle list + } + else { + /* else move it to idle list */ add_idle_timer(ctx, t); } } } -int get_expiry_ms(timer_ctx_t ctx) +uint32 +get_expiry_ms(timer_ctx_t ctx) { - int ms_to_next_expiry; + uint32 ms_to_next_expiry; uint64 now = bh_get_tick_ms(); - vm_mutex_lock(&ctx->mutex); - if (ctx->g_app_timers == NULL) - ms_to_next_expiry = 7 * 24 * 60 * 60 * 1000; // 1 week - else if (ctx->g_app_timers->expiry >= now) - ms_to_next_expiry = (int)(ctx->g_app_timers->expiry - now); + os_mutex_lock(&ctx->mutex); + if (ctx->app_timers == NULL) + ms_to_next_expiry = (uint32)-1; + else if (ctx->app_timers->expiry >= now) + ms_to_next_expiry = (uint32)(ctx->app_timers->expiry - now); else ms_to_next_expiry = 0; - vm_mutex_unlock(&ctx->mutex); + os_mutex_unlock(&ctx->mutex); return ms_to_next_expiry; } -int check_app_timers(timer_ctx_t ctx) +uint32 +check_app_timers(timer_ctx_t ctx) { - vm_mutex_lock(&ctx->mutex); - - app_timer_t * t = ctx->g_app_timers; - app_timer_t * expired = NULL; - + app_timer_t *t, *expired = NULL, *expired_end = NULL; uint64 now = bh_get_tick_ms(); + os_mutex_lock(&ctx->mutex); + + t = ctx->app_timers; while (t) { if (now >= t->expiry) { - ctx->g_app_timers = t->next; + ctx->app_timers = t->next; - t->next = expired; - expired = t; + /* append t to the end of expired list */ + t->next = NULL; + if (!expired_end) { + expired = expired_end = t; + } + else { + expired_end->next = t; + expired_end = t; + } - t = ctx->g_app_timers; - } else { + t = ctx->app_timers; + } + else { break; } } - vm_mutex_unlock(&ctx->mutex); + os_mutex_unlock(&ctx->mutex); handle_expired_timers(ctx, expired); - return get_expiry_ms(ctx); } -void cleanup_app_timers(timer_ctx_t ctx) +void +cleanup_app_timers(timer_ctx_t ctx) { - vm_mutex_lock(&ctx->mutex); + os_mutex_lock(&ctx->mutex); - release_timer_list(&ctx->g_app_timers); + release_timer_list(&ctx->app_timers); release_timer_list(&ctx->idle_timers); - vm_mutex_unlock(&ctx->mutex); + os_mutex_unlock(&ctx->mutex); } - diff --git a/core/shared/utils/runtime_timer.h b/core/shared/utils/runtime_timer.h index b4aa657c32..b8d90c5ffb 100644 --- a/core/shared/utils/runtime_timer.h +++ b/core/shared/utils/runtime_timer.h @@ -12,26 +12,38 @@ extern "C" { #endif -uint32 bh_get_elpased_ms(uint32 * last_system_clock); +uint64 +bh_get_tick_ms(void); +uint32 +bh_get_elpased_ms(uint32 *last_system_clock); struct _timer_ctx; -typedef struct _timer_ctx * timer_ctx_t; -typedef void (*timer_callback_f)(uint32 id, unsigned int owner); +typedef struct _timer_ctx *timer_ctx_t; +typedef void (*timer_callback_f)(unsigned int id, unsigned int owner); typedef void (*check_timer_expiry_f)(timer_ctx_t ctx); -timer_ctx_t create_timer_ctx(timer_callback_f timer_handler, - check_timer_expiry_f, int prealloc_num, unsigned int owner); +timer_ctx_t +create_timer_ctx(timer_callback_f timer_handler, check_timer_expiry_f, + int prealloc_num, unsigned int owner); void destroy_timer_ctx(timer_ctx_t); -unsigned int timer_ctx_get_owner(timer_ctx_t ctx); - -uint32 sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, - bool auto_start); -bool sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id); -bool sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id); -bool sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval); -void cleanup_app_timers(timer_ctx_t ctx); -int check_app_timers(timer_ctx_t ctx); -int get_expiry_ms(timer_ctx_t ctx); +unsigned int +timer_ctx_get_owner(timer_ctx_t ctx); + +uint32 +sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, + bool auto_start); +bool +sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id); +bool +sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id); +bool +sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval); +void +cleanup_app_timers(timer_ctx_t ctx); +uint32 +check_app_timers(timer_ctx_t ctx); +uint32 +get_expiry_ms(timer_ctx_t ctx); #ifdef __cplusplus } diff --git a/core/shared/utils/shared_utils.cmake b/core/shared/utils/shared_utils.cmake index 44a150bf5a..5b7d02dde2 100644 --- a/core/shared/utils/shared_utils.cmake +++ b/core/shared/utils/shared_utils.cmake @@ -4,10 +4,8 @@ set (UTILS_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) include_directories(${UTILS_SHARED_DIR}) -include_directories(${UTILS_SHARED_DIR}/../include) - -file (GLOB_RECURSE source_all ${UTILS_SHARED_DIR}/*.c) +file (GLOB source_all ${UTILS_SHARED_DIR}/*.c) set (UTILS_SHARED_SOURCE ${source_all}) diff --git a/core/shared/utils/uncommon/SConscript b/core/shared/utils/uncommon/SConscript new file mode 100644 index 0000000000..f608645fe2 --- /dev/null +++ b/core/shared/utils/uncommon/SConscript @@ -0,0 +1,32 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * +import os + +cwd = GetCurrentDir() + +# src = Split(''' +# ''') + + +def addSrcFiles(arr, path): + for f in os.listdir(path): + fpath = os.path.join(path, f); + if os.path.isfile(fpath): + ext = os.path.splitext(fpath)[-1] + if ext == '.c' or ext == '.cpp': + arr += [fpath] + #elif os.path.isdir(fpath): + # addSrcFiles(arr, fpath) + +src = Glob('*.c') +src += Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('iwasm_shared_utils_uncommon', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/core/shared/utils/uncommon/bh_getopt.c b/core/shared/utils/uncommon/bh_getopt.c new file mode 100644 index 0000000000..19e23a7b5c --- /dev/null +++ b/core/shared/utils/uncommon/bh_getopt.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 Ant Financial Services Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __GNUC__ + +#include "bh_getopt.h" +#include +#include + +char *optarg = NULL; +int optind = 1; + +int +getopt(int argc, char *const argv[], const char *optstring) +{ + static int sp = 1; + int opt; + char *p; + + if (sp == 1) { + if ((optind >= argc) || (argv[optind][0] != '-') + || (argv[optind][1] == 0)) { + return -1; + } + else if (!strcmp(argv[optind], "--")) { + optind++; + return -1; + } + } + + opt = argv[optind][sp]; + p = strchr(optstring, opt); + if (opt == ':' || p == NULL) { + printf("illegal option : '-%c'\n", opt); + if (argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return ('?'); + } + if (p[1] == ':') { + if (argv[optind][sp + 1] != '\0') + optarg = &argv[optind++][sp + 1]; + else if (++optind >= argc) { + printf("option '-%c' requires an argument :\n", opt); + sp = 1; + return ('?'); + } + else { + optarg = argv[optind++]; + } + sp = 1; + } + else { + if (argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return (opt); +} +#endif diff --git a/core/shared/utils/uncommon/bh_getopt.h b/core/shared/utils/uncommon/bh_getopt.h new file mode 100644 index 0000000000..efd3ab4036 --- /dev/null +++ b/core/shared/utils/uncommon/bh_getopt.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 Ant Financial Services Group. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifdef __GNUC__ +#include +#endif +#ifndef __GNUC__ +#ifndef GETOPT_H__ +#define GETOPT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *optarg; +extern int optind; + +int +getopt(int argc, char *const argv[], const char *optstring); + +#ifdef __cplusplus +} +#endif + +#endif /* end of GETOPT_H__ */ +#endif /* end of __GNUC__ */ diff --git a/core/shared/utils/uncommon/bh_read_file.c b/core/shared/utils/uncommon/bh_read_file.c new file mode 100644 index 0000000000..adfa175785 --- /dev/null +++ b/core/shared/utils/uncommon/bh_read_file.c @@ -0,0 +1,117 @@ +#include "bh_read_file.h" + +#include +#include +#if defined(_WIN32) || defined(_WIN32_) +#include +#else +#include +#endif + +#if defined(_WIN32) || defined(_WIN32_) + +#if defined(__MINGW32__) && !defined(_SH_DENYNO) +#define _SH_DENYNO 0x40 +#endif + +char * +bh_read_file_to_buffer(const char *filename, uint32 *ret_size) +{ + char *buffer; + int file; + uint32 file_size, buf_size, read_size; + struct stat stat_buf; + + if (!filename || !ret_size) { + printf("Read file to buffer failed: invalid filename or ret size.\n"); + return NULL; + } + + if (_sopen_s(&file, filename, _O_RDONLY | _O_BINARY, _SH_DENYNO, 0)) { + printf("Read file to buffer failed: open file %s failed.\n", filename); + return NULL; + } + + if (fstat(file, &stat_buf) != 0) { + printf("Read file to buffer failed: fstat file %s failed.\n", filename); + _close(file); + return NULL; + } + file_size = (uint32)stat_buf.st_size; + + /* At lease alloc 1 byte to avoid malloc failed */ + buf_size = file_size > 0 ? file_size : 1; + + if (!(buffer = (char *)BH_MALLOC(buf_size))) { + printf("Read file to buffer failed: alloc memory failed.\n"); + _close(file); + return NULL; + } +#if WASM_ENABLE_MEMORY_TRACING != 0 + LOG_VERBOSE("Read file, total size: %u", file_size); +#endif + + read_size = _read(file, buffer, file_size); + _close(file); + + if (read_size < file_size) { + printf("Read file to buffer failed: read file content failed.\n"); + BH_FREE(buffer); + return NULL; + } + + *ret_size = file_size; + return buffer; +} +#else /* else of defined(_WIN32) || defined(_WIN32_) */ +char * +bh_read_file_to_buffer(const char *filename, uint32 *ret_size) +{ + char *buffer; + int file; + uint32 file_size, buf_size, read_size; + struct stat stat_buf; + + if (!filename || !ret_size) { + printf("Read file to buffer failed: invalid filename or ret size.\n"); + return NULL; + } + + if ((file = open(filename, O_RDONLY, 0)) == -1) { + printf("Read file to buffer failed: open file %s failed.\n", filename); + return NULL; + } + + if (fstat(file, &stat_buf) != 0) { + printf("Read file to buffer failed: fstat file %s failed.\n", filename); + close(file); + return NULL; + } + + file_size = (uint32)stat_buf.st_size; + + /* At lease alloc 1 byte to avoid malloc failed */ + buf_size = file_size > 0 ? file_size : 1; + + if (!(buffer = BH_MALLOC(buf_size))) { + printf("Read file to buffer failed: alloc memory failed.\n"); + close(file); + return NULL; + } +#if WASM_ENABLE_MEMORY_TRACING != 0 + LOG_VERBOSE("Read file, total size: %u", file_size); +#endif + + read_size = (uint32)read(file, buffer, file_size); + close(file); + + if (read_size < file_size) { + printf("Read file to buffer failed: read file content failed.\n"); + BH_FREE(buffer); + return NULL; + } + + *ret_size = file_size; + return buffer; +} +#endif /* end of defined(_WIN32) || defined(_WIN32_) */ diff --git a/core/shared/utils/uncommon/bh_read_file.h b/core/shared/utils/uncommon/bh_read_file.h new file mode 100644 index 0000000000..bbebf847ff --- /dev/null +++ b/core/shared/utils/uncommon/bh_read_file.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_FILE_H +#define _BH_FILE_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +char * +bh_read_file_to_buffer(const char *filename, uint32 *ret_size); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _BH_FILE_H */ diff --git a/core/shared/utils/uncommon/shared_uncommon.cmake b/core/shared/utils/uncommon/shared_uncommon.cmake new file mode 100644 index 0000000000..0a15b87b84 --- /dev/null +++ b/core/shared/utils/uncommon/shared_uncommon.cmake @@ -0,0 +1,11 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (UNCOMMON_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${UNCOMMON_SHARED_DIR}) + +file (GLOB_RECURSE source_all ${UNCOMMON_SHARED_DIR}/*.c) + +set (UNCOMMON_SHARED_SOURCE ${source_all}) + diff --git a/core/version.h b/core/version.h new file mode 100644 index 0000000000..1baffd2dc7 --- /dev/null +++ b/core/version.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * version.h.in is a template file. version.h is a generated file. + * Please do not edit both files directly. + * + * Any changes to the version should be done in build-scripts/version.cmake. + * + * Continue to maintain the version.h for certain embedded platforms. + */ + +#ifndef _WAMR_VERSION_H_ +#define _WAMR_VERSION_H_ + +/* clang-format off */ +#define WAMR_VERSION_MAJOR 2 +#define WAMR_VERSION_MINOR 4 +#define WAMR_VERSION_PATCH 3 +/* clang-format on */ + +#endif diff --git a/core/version.h.in b/core/version.h.in new file mode 100644 index 0000000000..e28df65ce4 --- /dev/null +++ b/core/version.h.in @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * version.h.in is a template file. version.h is a generated file. + * Please do not edit both files directly. + * + * Any changes to the version should be done in build-scripts/version.cmake. + * + * Continue to maintain the version.h for certain embedded platforms. + */ + +#ifndef _WAMR_VERSION_H_ +#define _WAMR_VERSION_H_ + +/* clang-format off */ +#define WAMR_VERSION_MAJOR @WAMR_VERSION_MAJOR@ +#define WAMR_VERSION_MINOR @WAMR_VERSION_MINOR@ +#define WAMR_VERSION_PATCH @WAMR_VERSION_PATCH@ +/* clang-format on */ + +#endif diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 2e534bd802..cd931a4662 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -1,234 +1,733 @@ +# Build WAMR vmcore -Build WAMR core (iwasm) -========================= -Please follow the instructions below to build the WAMR VM core on different platforms. +WAMR vmcore is the runtime library set that loads and runs Wasm modules. This guide walks you through building the WAMR vmcore. -Linux -------------------------- -First of all please install the dependent packages. -Run command below in Ubuntu-18.04: -``` Bash -sudo apt install build-essential cmake g++-multilib libgcc-8-dev lib32gcc-8-dev -``` -Or in Ubuntu-16.04: -``` Bash -sudo apt install build-essential cmake g++-multilib libgcc-5-dev lib32gcc-5-dev -``` -Or in Fedora: -``` Bash -sudo dnf install glibc-devel.i686 -``` +References: -After installing dependencies, build the source code: -``` Bash -cd product-mini/platforms/linux/ -mkdir build -cd build -cmake .. -make -``` -The binary file iwasm will be generated under build folder. - -Note: -WAMR provides some features which can be easily configured by passing options to cmake: -``` Bash -cmake -DWAMR_BUILD_INTERP=1/0 to enable or disable WASM intepreter -cmake -DWAMR_BUILD_AOT=1/0 to enable or disable WASM AOT -cmake -DWAMR_BUILD_JIT=1/0 to enable or disable WASM JIT -cmake -DWAMR_BUILD_LIBC_BUILTIN=1/0 enable or disable Libc builtin API's -cmake -DWAMR_BUILD_LIBC_WASI=1/0 enable or disable Libc WASI API's -cmake -DWAMR_BUILD_TARGET= to set the building target, including: - X86_64, X86_32, ARM, THUMB, XTENSA and MIPS - for ARM and THUMB, we can specify the info, e.g. ARMV4, ARMV4T, ARMV5, ARMV5T, THUMBV4T, THUMBV5T and so on. -``` +- [how to build iwasm](../product-mini/README.md): build for Linux, Windows, macOS, and more +- [Blog: Introduction to WAMR running modes](https://bytecodealliance.github.io/wamr.dev/blog/introduction-to-wamr-running-modes/) -For example, if we want to disable interpreter, enable AOT and WASI, we can: -``` Bash -cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=0 -``` -Or if we want to enable inerpreter, disable AOT and WASI, and build as X86_32, we can: -``` Bash -cmake .. -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_TARGET=X86_32 -``` +## building configurations -By default in Linux, the interpreter, AOT and WASI are enabled, and JIT is disabled. And the build target is -set to X86_64 or X86_32 depending on the platform's bitwidth. +Include the script `runtime_lib.cmake` from [build-scripts](../build-scripts) into your CMakeLists.txt to pull vmcore into your build. -To enable WASM JIT, firstly we should build LLVM: -``` Bash -cd product-mini/platforms/linux/ -./build_llvm.sh (The llvm source code is cloned under /core/deps/llvm and auto built) -``` -Then pass option -DWAMR_BUILD_JIT=1 to cmake to enable WASM JIT: -``` Bash -mkdir build -cd build -cmake .. -DWAMR_BUILD_JIT=1 -make +```cmake +# add this into your CMakeLists.txt +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) ``` -Linux SGX (Intel Software Guard Extention) -------------------------- -First of all please install the [Intel SGX SDK](https://software.intel.com/en-us/sgx/sdk). - -After installing dependencies, build the source code: -``` Bash -source /environment -cd product-mini/platforms/linux-sgx/ -mkdir build -cd build -cmake .. -make -``` -This builds the libraries used by SGX enclave sample, the generated file libvmlib.a and libextlib.a will be copied to enclave-sample folder. +The `runtime_lib.cmake` script exposes variables that control WAMR runtime features. Set them in CMakeLists.txt or pass them on the cmake command line. -Then build the enclave sample: -``` Bash -source /environment -cd enclave-sample -make +```cmake +# Set flags in CMakeLists.txt +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +# Include the runtime lib script +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) ``` -The binary file app will be generated. -To run the sample: -``` Bash -source /environment -./app -``` +### All compilation flags -MacOS -------------------------- -Make sure to install Xcode from App Store firstly, and install cmake. +| Compilation flags | Description | +| -------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| [WAMR_APP_THREAD_STACK_SIZE_MAX](#set-maximum-app-thread-stack-size) | Maximum stack size for app threads | +| [WAMR_BH_LOG](#host-defined-log) | Host defined logging | +| [WAMR_BH_VPRINTF](#host-defined-vprintf) | Host defined vprintf | +| [WAMR_BUILD_ALLOC_WITH_USAGE](#user-defined-linear-memory-allocator) | Allocation with usage tracking | +| [WAMR_BUILD_ALLOC_WITH_USER_DATA](#user-defined-linear-memory-allocator) | Allocation with user data | +| [WAMR_BUILD_AOT](#configure-aot) | AoT compilation(wamrc) | +| [WAMR_BUILD_AOT](#configure-aot) | AoT runtime | +| [WAMR_BUILD_AOT_INTRINSICS](#aot-intrinsics) | AoT intrinsics | +| [WAMR_BUILD_AOT_STACK_FRAME](#aot-stack-frame-feature) | AoT stack frame | +| [WAMR_BUILD_AOT_VALIDATOR](#aot-validator) | AoT validator | +| [WAMR_BUILD_BULK_MEMORY](#bulk-memory-feature) | bulk memory | +| [WAMR_BUILD_COPY_CALL_STACK](#copy-call-stack) | copy call stack | +| [WAMR_BUILD_CUSTOM_NAME_SECTION](#name-section) | name section | +| [WAMR_BUILD_DEBUG_AOT](#source-debugging-features) | debug AoT | +| [WAMR_BUILD_DEBUG_INTERP](#source-debugging-features) | debug interpreter | +| [WAMR_BUILD_DUMP_CALL_STACK](#dump-call-stack-feature) | dump call stack | +| [WAMR_BUILD_DYNAMIC_AOT_DEBUG](#source-debugging-features) | dynamic AoT debugging | +| [WAMR_BUILD_EXCE_HANDLING](#exception-handling) | exception handling | +| [WAMR_BUILD_EXTENDED_CONST_EXPR](#extended-constant-expression) | extended constant expressions | +| [WAMR_BUILD_FAST_INTERP](#configure-interpreters) | fast interpreter | +| [WAMR_BUILD_FAST_JIT](#configure-fast-jit) | fast JIT | +| [WAMR_BUILD_FAST_JIT_DUMP](#configure-fast-jit) | fast JIT dump | +| [WAMR_BUILD_GC](#garbage-collection) | garbage collection | +| [WAMR_BUILD_GC_HEAP_VERIFY](#garbage-collection) | garbage collection heap verification | +| [WAMR_BUILD_GC_HEAP_SIZE_DEFAULT](garbage-collection) | default garbage collection heap size | +| [WAMR_BUILD_GLOBAL_HEAP_POOL](#a-pre-allocation-for-runtime-and-wasm-apps) | global heap pool | +| [WAMR_BUILD_GLOBAL_HEAP_SIZE](#a-pre-allocation-for-runtime-and-wasm-apps) | global heap size | +| [WAMR_BUILD_INSTRUCTION_METERING](#instruction-metering) | instruction metering | +| [WAMR_BUILD_INTERP](#configure-interpreters) | interpreter | +| [WAMR_BUILD_INVOKE_NATIVE_GENERAL](#invoke-general-ffi) | FFI general | +| [WAMR_BUILD_JIT](#configure-llvm-jit) | JIT compilation | +| [WAMR_BUILD_LAZY_JIT](#configure-llvm-jit) | lazy JIT compilation | +| [WAMR_BUILD_LIBC_BUILTIN](#configure-libc) | libc builtin functions | +| [WAMR_BUILD_LIBC_EMCC](#configure-libc) | libc emcc compatibility | +| [WAMR_BUILD_LIBC_UVWASI](#configure-libc) | libc uvwasi compatibility | +| [WAMR_BUILD_LIBC_WASI](#configure-libc) | wasi libc | +| [WAMR_BUILD_LIB_PTHREAD](#lib-pthread) | pthread library | +| [WAMR_BUILD_LIB_PTHREAD_SEMAPHORE](#lib-pthread-semaphore) | pthread semaphore support | +| [WAMR_BUILD_LIB_RATS](#librats) | RATS library | +| [WAMR_BUILD_LIB_WASI_THREADS](#lib-wasi-threads) | wasi threads | +| [WAMR_BUILD_LINUX_PERF](#linux-perf-support) | Linux performance counters | +| [WAMR_BUILD_LIME1](#lime1-target) | LIME1 runtime | +| [WAMR_BUILD_LOAD_CUSTOM_SECTION](#load-wasm-custom-sections) | loading custom sections | +| [WAMR_BUILD_MEMORY64](#memory64-feature) | memory64 support | +| [WAMR_BUILD_MEMORY_PROFILING](#memory-profiling-experiment) | memory profiling | +| [WAMR_BUILD_MEMORY_TRACING](#memory-tracing) | memory tracing | +| [WAMR_BUILD_MINI_LOADER](#wasm-mini-loader) :warning: :exclamation: | mini loader | +| [WAMR_BUILD_MODULE_INST_CONTEXT](#module-instance-context-apis) | module instance context | +| [WAMR_BUILD_MULTI_MEMORY](#multi-memory) | multi-memory support | +| [WAMR_BUILD_MULTI_MODULE](#multi-module-feature) | multi-module support | +| [WAMR_BUILD_PERF_PROFILING](#performance-profiling-experiment) | performance profiling | +| [WAMR_BUILD_PLATFORM](#configure-platform-and-architecture) | Default platform | +| [WAMR_BUILD_QUICK_AOT_ENTRY](#quick-aotjti-entries) | quick AOT entry | +| [WAMR_BUILD_REF_TYPES](#reference-types-feature) | reference types | +| [WAMR_BUILD_SANITIZER](#sanitizer) | sanitizer | +| [WAMR_BUILD_SGX_IPFS](#intel-protected-file-system) | Intel Protected File System support | +| [WAMR_BUILD_SHARED_HEAP](#shared-heap-among-wasm-apps-and-host-native) | shared heap | +| [WAMR_BUILD_SHARED_MEMORY](shared-memory-feature) | shared memory | +| [WAMR_BUILD_SHRUNK_MEMORY](#shrunk-the-memory-usage) | shrunk memory | +| [WAMR_BUILD_SIMD](#128-bit-simd-feature) | SIMD support | +| [WAMR_BUILD_SIMDE](#128-bit-simd-feature) | SIMD E extensions | +| [WAMR_BUILD_SPEC_TEST](#support-spec-test) | spec test | +| [WAMR_BUILD_STACK_GUARD_SIZE](#stack-guard-size) | Stack guard size | +| [WAMR_BUILD_STATIC_PGO](running-pgoprofile-guided-optimization-instrumented-aot-file) | Static PGO | +| [WAMR_BUILD_STRINGREF](#garbage-collection) | String reference support | +| [WAMR_BUILD_TAIL_CALL](#tail-call-feature) | Tail call optimization | +| [WAMR_BUILD_TARGET](#configure-platform-and-architecture) | Default target architecture | +| [WAMR_BUILD_THREAD_MGR](#thread-manager) | Thread manager | +| [WAMR_BUILD_WAMR_COMPILER](#configure-aot) | WAMR compiler | +| [WAMR_BUILD_WASI_EPHEMERAL_NN](#lib-wasi-nn-with-wasi_ephemeral_nn-module-support) | WASI ephemeral NN | +| [WAMR_BUILD_WASI_NN](#lib-wasi-nn) | WASI NN | +| [WAMR_BUILD_WASI_NN_EXTERNAL_DELEGATE_PATH](#lib-wasi-nn-external-delegate-mode) | External delegate path for WASI NN | +| [WAMR_BUILD_WASI_NN_ENABLE_GPU](#lib-wasi-nn-gpu-mode) | GPU support for WASI NN | +| [WAMR_BUILD_WASI_NN_LLAMACPP](#lib-wasi-nn) | LLAMA CPP for WASI NN | +| [WAMR_BUILD_WASI_NN_ONNX](#lib-wasi-nn) | ONNX for WASI NN | +| [WAMR_BUILD_WASI_NN_OPENVINO](#lib-wasi-nn) | OpenVINO for WASI NN | +| [WAMR_BUILD_WASI_NN_TFLITE](#lib-wasi-nn) | TFLite for WASI NN | +| [WAMR_BUILD_WASM_CACHE](#wasm-cache) | WASM cache | +| [WAMR_CONFIGURABLE_BOUNDS_CHECKS](#configurable-memory-access-boundary-check) :warning: :exclamation: | Configurable bounds checks | +| [WAMR_DISABLE_APP_ENTRY](#exclude-wamr-application-entry-functions) | Disable app entry | +| [WAMR_DISABLE_HW_BOUND_CHECK](#disable-boundary-check-with-hardware-trap) | Disable hardware bound check | +| [WAMR_DISABLE_STACK_HW_BOUND_CHECK](#disable-native-stack-boundary-check-with-hardware-trap) | Disable stack hardware bound check | +| [WAMR_DISABLE_WAKEUP_BLOCKING_OP](#disable-async-wakeup-of-blocking-operation) | Disable wakeup blocking operation | +| [WAMR_DISABLE_WRITE_GS_BASE](#disable-writing-the-linear-memory-base-address-to-x86-gs-segment-register) | Disable write GS base | +| [WAMR_TEST_GC](#test-garbage-collection) | Test garbage collection | -If you use Homebrew, install cmake from the command line: -``` Bash -brew install cmake -``` +### **Privileged Features** :warning: :exclamation: -Then build the source codes: -``` -cd product-mini/platforms/darwin/ -mkdir build -cd build -cmake .. -make -``` -Note: -WAMR provides some features which can be easily configured by passing options to cmake, please see [Linux platform](./build_wamr.md#linux) for details. Currently in MacOS, interpreter, AoT, and builtin libc are enabled by default. +_Privileged Features_ are powerful options that can boost performance or add capabilities but lower security by compromising isolation. Use them with care and test thoroughly. -VxWorks -------------------------- -VxWorks 7 SR0620 release is validated. +### **[config.h](../core/config.h)** -First you need to build a VSB. Make sure *UTILS_UNIX* layer is added in the VSB. -After the VSB is built, export the VxWorks toolchain path by: -``` -export /host/vx-compiler/bin:$PATH +Above compilation flags map to macros in `config.h`. For example, `WAMR_BUILD_AOT` maps to `WAMR_BUILD_AOT` in `config.h`. The build system sets these macros automatically based on your CMake settings. If your build doesn't set those flags, default values in `config.h` apply. + +### **Configure platform and architecture** + +- **WAMR_BUILD_PLATFORM**: set the target platform. Match the platform folder name under [core/shared/platform](../core/shared/platform). + +- **WAMR_BUILD_TARGET**: set the target CPU architecture. Supported targets: X86_64, X86_32, AARCH64, ARM, THUMB, XTENSA, ARC, RISCV32, RISCV64, and MIPS. + - For ARM and THUMB, use `[][_VFP]`. `` is the ARM sub-architecture. `_VFP` means arguments and returns use VFP coprocessor registers s0-s15 (d0-d7). Both are optional, for example ARMV7, ARMV7_VFP, THUMBV7, or THUMBV7_VFP. + - For AARCH64, use `[]`. VFP is on by default. `` is optional, for example AARCH64, AARCH64V8, or AARCH64V8.1. + - For RISCV64, use `[_abi]`. `_abi` is optional. Supported: RISCV64, RISCV64_LP64D, and RISCV64_LP64. RISCV64 and RISCV64_LP64D both use [LP64D](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (LP64 with hardware floating-point for FLEN=64). RISCV64_LP64 uses [LP64](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (integer calling convention only; no hardware floating-point calling convention). + - For RISCV32, use `[_abi]`. `_abi` is optional. Supported: RISCV32, RISCV32_ILP32D, RISCV32_ILP32F, and RISCV32_ILP32. RISCV32 and RISCV32_ILP32D both use [ILP32D](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (ILP32 with hardware floating-point for FLEN=64). RISCV32_ILP32F uses [ILP32F](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (ILP32 with hardware floating-point for FLEN=32). RISCV32_ILP32 uses [ILP32](https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#named-abis) (integer calling convention only). + +```bash +cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM ``` -Now switch to iwasm source tree to build the source code: + +### **Configure interpreters** + +- **WAMR_BUILD_INTERP**=1/0: turn the WASM interpreter on or off. + +- **WAMR_BUILD_FAST_INTERP**=1/0: pick fast (default) or classic interpreter. + +> [!NOTE] +> The fast interpreter runs ~2X faster than classic interpreter, but consumes about 2X memory to hold the pre-compiled code. + +### **Configure AOT** + +- **WAMR_BUILD_AOT**=1/0: turn AOT on or off. Defaults to on. +- **WAMR_BUILD_WAMR_COMPILER**=1/0. It is used to wasm loader and compilation to indictate compiler mode. + +### **Configure LLVM JIT** + +Comparing with fast JIT, LLVM JIT covers more architectures and produces better optimized code, but takes longer on cold start. + +- **WAMR_BUILD_JIT**=1/0: turn LLVM JIT on or off. Defaults to off. +- **WAMR_BUILD_LAZY_JIT**=1/0: turn lazy JIT on or off. Defaults to off. With lazy JIT, functions are compiled in background threads before they are called, which can reduce startup time for large modules. + +### **Configure Fast JIT** + +The fast JIT is a lightweight JIT that emits code quickly and tunes hot functions. + +- **WAMR_BUILD_FAST_JIT**=1/0: turn Fast JIT on or off. Defaults to off. +- **WAMR_BUILD_FAST_JIT_DUMP**=1/0: dump fast JIT compiled code to stdout for debugging. Defaults to off. + +> [!WARNING] +> It currently covers only a few architectures (x86_64). + +### **Configure Multi-tier JIT** + +Use fast jit as the first tier and LLVM JIT as the second tier. + +- With **WAMR_BUILD_FAST_JIT**=1 and **WAMR_BUILD_JIT**=1, you get multi-tier JIT. Defaults to off. + +> [!WARNING] +> It currently covers only a few architectures (x86_64). + +### **Configure LIBC** + +- **WAMR_BUILD_LIBC_BUILTIN**=1/0: build the built-in libc subset for WASM apps. Defaults to on. + +- **WAMR_BUILD_LIBC_WASI**=1/0: build the [WASI](https://github.com/WebAssembly/WASI) libc subset for WASM apps. Defaults to on. + +- **WAMR_BUILD_LIBC_UVWASI**=1/0 (Experiment): build the WASI libc subset for WASM apps using [uvwasi](https://github.com/nodejs/uvwasi). Defaults to off. + +- **WAMR_BUILD_LIBC_EMCC**=1/0: build the emcc-compatible libc subset for WASM apps. Defaults to off. + +> [!WARNING] +> WAMR is not a secure sandbox on every platform. On platforms where **WAMR_BUILD_LIBC_WASI** is unsupported (for example Windows), you can try the uvwasi-based WASI via **WAMR_BUILD_LIBC_UVWASI**, but it is unsafe. + +### **Multi-Module feature** + +- **WAMR_BUILD_MULTI_MODULE**=1/0, default to off. + +> [!NOTE] +> See [Multiple Modules as Dependencies](./multi_module.md) for details. + +> [!WARNING] +> The multi-module feature is not supported in fast-jit or llvm-jit modes. + +### **WASM mini loader** + +- **WAMR_BUILD_MINI_LOADER**=1/0, default to off. + +> [!NOTE] +> The mini loader skips integrity checks on the WASM binary. Make sure the file is valid yourself. + +> [!WARNING] +> This is a [privileged feature](#privileged-features) that compromises security. Use it only when you trust the WASM binary source. + +### **shared memory feature** + +- **WAMR_BUILD_SHARED_MEMORY**=1/0, default to off. + +### **bulk memory feature** + +- **WAMR_BUILD_BULK_MEMORY**=1/0, default to off. + +### **memory64 feature** + +- **WAMR_BUILD_MEMORY64**=1/0, default to off. + +> [!WARNING] +> Supported only in classic interpreter mode and AOT mode. + +### **thread manager** + +- **WAMR_BUILD_THREAD_MGR**=1/0, default to off. + +### **lib-pthread** + +- **WAMR_BUILD_LIB_PTHREAD**=1/0, default to off. + +> [!NOTE] +> When you enable lib pthread, required features such as `shared memory` and `thread manager` are enabled automatically. See [WAMR pthread library](./pthread_library.md) for details. + +### **lib-pthread-semaphore** + +- **WAMR_BUILD_LIB_PTHREAD_SEMAPHORE**=1/0, default to off. + +> [!NOTE] +> This depends on `lib-pthread` and turns it on automatically. + +### **lib wasi-threads** + +- **WAMR_BUILD_LIB_WASI_THREADS**=1/0, default to off. + +> [!NOTE] +> Enabling lib wasi-threads also enables its dependencies `shared memory` and `thread manager`. See [wasi-threads](./pthread_impls.md#wasi-threads-new) and [Introduction to WAMR WASI threads](https://bytecodealliance.github.io/wamr.dev/blog/introduction-to-wamr-wasi-threads) for details. + +### **lib wasi-nn** + +- **WAMR_BUILD_WASI_NN**=1/0, default to off. +- **WAMR_BUILD_WASI_NN_LLAMACPP**=1/0, default to off. +- **WAMR_BUILD_WASI_NN_ONNX**=1/0, default to off. +- **WAMR_BUILD_WASI_NN_OPENVINO**=1/0, default to off. +- **WAMR_BUILD_WASI_NN_TFLITE**=1/0, default to off. + +> [!NOTE] +> Using WAMR_BUILD_WASI_NN without WAMR_BUILD_WASI_EPHEMERAL_NN is deprecated and may be removed later. Please enable WAMR_BUILD_WASI_EPHEMERAL_NN too. See [WASI-NN](../core/iwasm/libraries/wasi-nn) for details. + +### **lib wasi-nn GPU mode** + +- **WAMR_BUILD_WASI_NN_ENABLE_GPU**=1/0, default to off. + +### **lib wasi-nn external delegate mode** + +- **WAMR_BUILD_WASI_NN_ENABLE_EXTERNAL_DELEGATE**=1/0, default to off. + +- **WAMR_BUILD_WASI_NN_EXTERNAL_DELEGATE_PATH**=Path to the external delegate shared library (for example `libedgetpu.so.1.0` for Coral USB). + +### **lib wasi-nn with `wasi_ephemeral_nn` module support** + +- **WAMR_BUILD_WASI_EPHEMERAL_NN**=1/0, default to on. + +### **Disable boundary check with hardware trap** + +- **WAMR_DISABLE_HW_BOUND_CHECK**=1/0, default to on if the platform supports it. Otherwise, software boundary checks are used. + +> [!NOTE] +> By default only [linux/darwin/android/windows/vxworks 64-bit](https://github.com/bytecodealliance/wasm-micro-runtime/blob/5fb5119239220b0803e7045ca49b0a29fe65e70e/core/shared/platform/linux/platform_internal.h#L81) platforms enable this hardware trap boundary check. On 32-bit platforms it is off even if the flag is 0. The wamrc tool omits boundary check instructions in AOT code for all 64-bit targets except SGX to improve speed. The boundary check covers linear memory access and native stack access unless `WAMR_DISABLE_STACK_HW_BOUND_CHECK` is set. + +### **Disable native stack boundary check with hardware trap** + +- **WAMR_DISABLE_STACK_HW_BOUND_CHECK**=1/0, default to on if the platform supports it; same rule as `WAMR_DISABLE_HW_BOUND_CHECK`. Otherwise, software boundary checks are used. + +> [!NOTE] +> If hardware trap boundary checks are off (or `WAMR_DISABLE_HW_BOUND_CHECK` is 1), native stack boundary checks are also off regardless of `WAMR_DISABLE_STACK_HW_BOUND_CHECK`. If hardware trap boundary checks are on, this setting decides whether the native stack check is on. + +### **Disable async wakeup of blocking operation** + +- **WAMR_DISABLE_WAKEUP_BLOCKING_OP**=1/0, default to on when the platform supports it. + +> [!NOTE] +> This feature lets blocking threads terminate asynchronously. If you disable it, blocking threads may never finish when asked to exit. + +### **tail call feature** + +- **WAMR_BUILD_TAIL_CALL**=1/0, default to off. + +### **128-bit SIMD feature** + +- **WAMR_BUILD_SIMD**=1/0, default to on. +- **WAMR_BUILD_SIMDE**=1/0, default to off. + +SIMDE (SIMD Everywhere) implements SIMD operations in fast interpreter mode. + +> [!WARNING] +> Supported in AOT, JIT, and fast-interpreter modes with the SIMDe library. + +### **SIMDe library for SIMD in fast interpreter** + +- **WAMR_BUILD_LIB_SIMDE**=1/0, default to off. + +> [!NOTE] +> When enabled, SIMDe (SIMD Everywhere) implements SIMD operations in fast interpreter mode. + +### **Exception Handling** + +- **WAMR_BUILD_EXCE_HANDLING**=1/0, default to off. + +> [!NOTE] +> Current implementation supports only Legacy Wasm exception handling proposal, not the latest version. + +> [!WARNING] +> Exception handling currently works only in classic interpreter mode. + +### **Garbage Collection** + +- **WAMR_BUILD_GC**=1/0, default to off. +- **WAMR_BUILD_GC_HEAP_VERIFY**=1/0, default to off. When enabled, verifies the heap during free. +- **WAMR_BUILD_STRINGREF**=1/0, default to off. When enabled, need to set WAMR_STRINGREF_IMPL_SOURCE as well + +> [!WARNING] +> Current implentation of Garbage Collection(GC) is not fully compliant with the Wasm GC proposal and Wasm 3.0 specification. There are still few known limitations: +> +> - `exn` and `noexn` types are not supported. +> - nested structs and arrays are not fully supported. +> +> Garbage collection is not supported in fast-jit mode and multi-tier-jit mode. + +### **Set the Garbage Collection heap size** + +- **WAMR_BUILD_GC_HEAP_SIZE_DEFAULT**=n, default to 128 kB (131072). + +### **Multi Memory** + +- **WAMR_BUIL_MULTI_MEMORY**=1/0, default to off. + +> [!WARNING] +> Multi memory is supported only in classic interpreter mode. + +### **Name Section** + +- **WAMR_BUILD_CUSTOM_NAME_SECTION**=1/0: load function names from the custom name section. Default is off. + +### **AOT stack frame feature** + +- **WAMR_BUILD_AOT_STACK_FRAME**=1/0, default to off. + +> [!NOTE] +> When enabled, AOT or JIT stack frames (similar to classic interpreter frames but storing only what is needed) are built during calls. Add `--enable-dump-call-stack` to wamrc when compiling AOT modules. + +### **dump call stack feature** + +- **WAMR_BUILD_DUMP_CALL_STACK**=1/0, default to off. + +> [!NOTE] +> When enabled, the runtime dumps the call stack on exceptions. +> +> - In interpreter mode, names come first from the custom name section. If that section is absent or disabled, names come from import/export sections. +> - In AOT/JIT mode, names come from the import/export section. Export as many functions as possible (for `wasi-sdk` you can use `-Wl,--export-all`) when compiling the wasm module, and add `--enable-dump-call-stack --emit-custom-sections=name` to wamrc when compiling the AOT module. + +### **memory profiling (Experiment)** + +- **WAMR_BUILD_MEMORY_PROFILING**=1/0, default to off. + +> [!NOTE] +> When enabled, call `void wasm_runtime_dump_mem_consumption(wasm_exec_env_t exec_env)` to dump memory usage. Currently only module, module_instance, and exec_env memory are measured; other components such as `wasi-ctx`, `multi-module`, and `thread-manager` are not included. See [Memory usage estimation for a module](./memory_usage.md). + +### **memory tracing** + +- **WAMR_BUILD_MEMORY_TRACING**=1/0, default to off. + +> [!NOTE] +> When enabled, detailed memory allocation and deallocation traces are printed at runtime, which is useful for debugging memory issues. + +### **performance profiling (Experiment)** + +- **WAMR_BUILD_PERF_PROFILING**=1/0, default to off. + +> [!NOTE] +> When enabled, call `void wasm_runtime_dump_perf_profiling(wasm_module_inst_t module_inst)` to dump per-function performance. Function name lookup follows the same order as the dump call stack feature. See [Tune the performance of running wasm/aot file](./perf_tune.md). + +### **A pre-allocation for runtime and wasm apps** + +- **WAMR_BUILD_GLOBAL_HEAP_POOL**=1/0, default to off for _iwasm_ apps except on Alios and Zephyr. +- **WAMR_BUILD_GLOBAL_HEAP_SIZE**=n, default to 10 MB (10485760) for _iwasm_ apps, except Alios (256 kB), Riot (256 kB), and Zephyr (128 kB). + +> [!NOTE] +> When enabled, WAMR uses a big global heap for runtime and wasm apps instead of allocating memory from the system directly. This can reduce memory fragmentation and improve performance when many small allocations happen. The global heap is allocated at startup. **WAMR_BUILD_GLOBAL_HEAP_POOL** applies to _iwasm_ apps in `product-mini`. For your own host app, set `mem_alloc_type` to `Alloc_With_Pool` if you want to use a global heap. The global heap is described in [Memory model and memory usage tunning](memory_tune.md). **WAMR_BUILD_GLOBAL_HEAP_SIZE** applies to _iwasm_ apps in `product-mini`. For your host app, set `mem_alloc_option.pool` with the size you want for the global heap. The global heap is described in [Memory model and memory usage tunning](memory_tune.md). + +### **Set maximum app thread stack size** + +- **WAMR_APP_THREAD_STACK_SIZE_MAX**=n, default to 8 MB (8388608). + +> [!NOTE] +> AOT boundary checks with hardware traps may use large stacks because the OS can grow stacks lazily when a guard page is hit. Use this setting to cap total stack use, for example `-DWAMR_APP_THREAD_STACK_SIZE_MAX=131072` (128 KB). + +### **Host defined vprintf** + +- **WAMR_BH_VPRINTF**=, default to off. + +> [!NOTE] +> If you provide `vprintf_callback`, `os_printf()` and `os_vprintf()` on Linux, Darwin, Windows, VxWorks, Android, and esp-idf, plus WASI libc output, call your callback instead of libc `vprintf()`. Example outside the runtime lib: +> +> ```C +> int my_vprintf(const char *format, va_list ap) +> { +> /* output to pre-opened file stream */ +> FILE *my_file = ...; +> return vfprintf(my_file, format, ap); +> /* or output to pre-opened file descriptor */ +> int my_fd = ...; +> return vdprintf(my_fd, format, ap); +> /* or output to string buffer and print the string */ +> char buf[128]; +> vsnprintf(buf, sizeof(buf), format, ap); +> return my_printf("%s", buf); +> } +> ``` +> +> Then run `cmake -DWAMR_BH_VPRINTF=my_vprintf ..`, or add the compiler macro `BH_VPRINTF=my_vprintf` (for example `add_definitions(-DBH_VPRINTF=my_vprintf)` in CMakeLists.txt). See [basic sample](../samples/basic/src/main.c) for an example. + +### **WAMR_BH_LOG**=, default to off. + +> [!NOTE] +> If you provide `log_callback`, WAMR logs go there. Example: +> +> ```C +> void my_log(uint32 log_level, const char *file, int line, const char *fmt, ...) +> { +> /* Usage of custom logger */ +> } +> ``` +> +> See [basic sample](../samples/basic/src/main.c) for an example. + +### **reference types feature** + +- **WAMR_BUILD_REF_TYPES**=1/0, default to on. + +### **Exclude WAMR application entry functions** + +- **WAMR_DISABLE_APP_ENTRY**=1/0, default to off. + +> [!NOTE] +> The WAMR application entry (`core/iwasm/common/wasm_application.c`) wraps common steps to instantiate and run wasm functions and print results. These use platform APIs. this flag to skip the file if your platform lacks those APIs. _Do not enable this flag when building `product-mini`._ + +### **source debugging features** + +- **WAMR_BUILD_DEBUG_INTERP**=1/0, default to off. +- **WAMR_BUILD_DEBUG_AOT**=1/0, default to off. +- **WAMR_BUILD_DYNAMIC_AOT_DEBUG**=1/0, default to off. + +> [!NOTE] +> Source debugging needs extra setup. See [source_debugging.md](./source_debugging.md) and [WAMR source debugging basic](https://bytecodealliance.github.io/wamr.dev/blog/wamr-source-debugging-basic). + +### **load wasm custom sections** + +- **WAMR_BUILD_LOAD_CUSTOM_SECTION**=1/0, default to off. + +> [!NOTE] +> By default, custom sections are ignored. `WAMR_BUILD_LOAD_CUSTOM_SECTION` so the embedder can read them via `wasm_runtime_get_custom_section`. If `WAMR_BUILD_CUSTOM_NAME_SECTION` is on, the custom name section is consumed by the runtime and unavailable to the embedder. For AoT files, pass `--emit-custom-sections` to wamrc to keep the sections; otherwise they are dropped. + +### **Stack guard size** + +- **WAMR_BUILD_STACK_GUARD_SIZE**=n, default to N/A when not set. + +> [!NOTE] +> By default, stack guard size is 1K (1024) or 24K when uvwasi is enabled. + +### **Disable writing the linear memory base address to x86 GS segment register** + +- **WAMR_DISABLE_WRITE_GS_BASE**=1/0, default to on if the platform supports it. + +> [!NOTE] +> By default only [linux x86-64](https://github.com/bytecodealliance/wasm-micro-runtime/blob/5fb5119239220b0803e7045ca49b0a29fe65e70e/core/shared/platform/linux/platform_internal.h#L67) enables this. On 32-bit platforms it stays off even if set to 0. On linux x86-64, writing the linear memory base to the GS segment can speed up linear memory access for LLVM AOT/JIT when `--enable-segue=[]` is passed to `wamrc` or `iwasm`. +> +> See [segue optimization for wamrc when generating the aot file](./perf_tune.md#3-enable-segue-optimization-for-wamrc-when-generating-the-aot-file) for details. + +### **User defined linear memory allocator** + +- **WAMR_BUILD_ALLOC_WITH_USAGE**=1/0, default to off. +- **WAMR_BUILD_ALLOC_WITH_USER_DATA**=1/0, default to off. + +> [!NOTE] +> By default, the system allocates linear memory. With this on and `Alloc_With_Allocator` selected, you can provide your own allocator. + +### **running PGO(Profile-Guided Optimization) instrumented AOT file** + +- **WAMR_BUILD_STATIC_PGO**=1/0, default to off. + +> [!NOTE] +> See [Use the AOT static PGO method](./perf_tune.md#5-use-the-aot-static-pgo-method). + +### **linux perf support** + +- **WAMR_BUILD_LINUX_PERF**=1/0: enable linux perf support to generate flamegraphs for wasm app performance. Default is off. + +> [!NOTE] +> See [Use linux-perf](./perf_tune.md#7-use-linux-perf). + +### **module instance context APIs** + +- **WAMR_BUILD_MODULE_INST_CONTEXT**=1/0: enable module instance context APIs so the embedder can set one or more contexts for a wasm module instance. Default is on. + +```C + wasm_runtime_create_context_key + wasm_runtime_destroy_context_key + wasm_runtime_set_context + wasm_runtime_set_context_spread + wasm_runtime_get_context ``` -cd product-mini/platforms/vxworks/ -mkdir build -cd build -cmake .. -make + +> [!NOTE] +> See [wasm_export.h](../core/iwasm/include/wasm_export.h) for details. + +### **quick AOT/JTI entries** + +- **WAMR_BUILD_QUICK_AOT_ENTRY**=1/0: register quick call entries to speed up AOT/JIT function calls. Default is on. + +> [!NOTE] +> See [Refine callings to AOT/JIT functions from host native](./perf_tune.md#83-refine-callings-to-aotjit-functions-from-host-native). + +### **AOT intrinsics** + +- **WAMR_BUILD_AOT_INTRINSICS**=1/0: turn on AOT intrinsic functions. Default is on. AOT code can call these when wamrc uses `--disable-llvm-intrinsics` or `--enable-builtin-intrinsics=`. + +> [!NOTE] +> See [Tuning the XIP intrinsic functions](./xip.md#tuning-the-xip-intrinsic-functions). + +### **extended constant expression** + +- **WAMR_BUILD_EXTENDED_CONST_EXPR**=1/0, default to off. + +> [!NOTE] +> See [Extended Constant Expressions](https://github.com/WebAssembly/extended-const/blob/main/proposals/extended-const/Overview.md). + +### **bulk-memory-opt** + +- **WAMR_BUILD_BULK_MEMORY_OPT**=1/0, default to off. + +> [!NOTE] +> See [bulk-memory-opt](https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#bulk-memory-opt). + +### **call-indirect-overlong** + +- **WAMR_BUILD_CALL_INDIRECT_OVERLONG**=1/0, default to off. + +> [!NOTE] +> See [call-indirect-overlong](https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#call-indirect-overlong). + +### **Lime1 target** + +- **WAMR_BUILD_LIME1**=1/0, default to off. + +> [!NOTE] +> See [Lime1](https://github.com/WebAssembly/tool-conventions/blob/main/Lime.md#lime1). + +### **Configurable memory access boundary check** + +- **WAMR_CONFIGURABLE_BOUNDS_CHECKS**=1/0, default to off. + +> [!WARNING] +> When enabled, you can run `iwasm --disable-bounds-checks` to turn off memory access boundary checks in interpreter mode. This is a [privileged feature](#privileged-features); use it carefully. + +### **Shared heap among wasm apps and host native** + +- **WAMR_BUILD_SHARED_HEAP**=1/0, default to off. + +> [!NOTE] +> When enabled, you can create and attach shared heaps, and the following APIs become available: +> +> ```C +> wasm_runtime_create_shared_heap +> wasm_runtime_attach_shared_heap +> wasm_runtime_detach_shared_heap +> wasm_runtime_shared_heap_malloc +> wasm_runtime_shared_heap_free +> ``` +> +> A wasm app can call these to use the shared heap attached to its module instance: +> +> ```C +> void *shared_heap_malloc(); +> void shared_heap_free(void *ptr); +> ``` + +> [!WARNING] +> The shared-heap feature is not supported in fast-jit mode. + +### **Shrunk the memory usage** + +- **WAMR_BUILD_SHRUNK_MEMORY**=1/0, default to on. + +> [!NOTE] +> When enabled, this reduces memory by shrinking linear memory, especially when `memory.grow` is unused and memory needs are predictable. + +### **Instruction metering** + +- **WAMR_BUILD_INSTRUCTION_METERING**=1/0, default to off. + +> [!NOTE] +> This limits the number of instructions a wasm module instance can run. Call `wasm_runtime_set_instruction_count_limit(...)` before `wasm_runtime_call_*(...)` to enforce the cap. + +> [!WARNING] +> This is only supported in classic interpreter mode. + +## **Branch hints** + +- **WAMR_BUILD_BRANCH_HINTS**=1/0, default to disable if not set + +> [!NOTE] +> Enabling this feature allows the runtime to utilize branch hints for better performance during aot/jit execution. + +## **Combination of configurations:** + +### **Invoke general FFI** + +- **WAMR_BUILD_INVOKE_NATIVE_GENERAL**=1/0, default to off. + +By default, WAMR uses architecture-specific calling conventions to call native functions from WASM modules. When this feature is enabled, WAMR uses a general calling convention that works on all architectures but is slower. The details are in [iwasm_common.cmake](../core/iwasm/common/iwasm_common.cmake) + +### **Host defined log** + +- **WAMR_BH_LOG**=, default to off. + +### **AOT Validator** + +- **WAMR_BUILD_AOT_VALIDATOR**=1/0, default to off. + +> [!NOTE] +> By default, WAMR believes AOT files are valid and unforged. + +### **Copy Call Stack** + +- **WAMR_BUILD_COPY_CALL_STACK**=1/0, default to off. + +> [!NOTE] +> Unlike [dump call stack](dump-call-stack-feature), which prints the call stack on exceptions, this feature lets the embedder copy the call stack programmatically via `wasm_runtime_dump_call_stack_to_buf()`. + +### **Librats** + +- **WAMR_BUILD_LIB_RATS**=1/0, default to off. + +> [librats](https://github.com/inclavare-containers/librats) is a C library designed to facilitate remote attestation for secure computing environments. It provides a framework for attesting the integrity of computing environments remotely, enabling trust establishment between different Trusted Execution Environments (TEEs). + +> [!WARNING] +> This is for Intel SGX platforms only. + +### **Sanitizer** + +- **WAMR_BUILD_SANITIZER**=[ubsan|asan|tsan|posan], default is empty + +Use one or more of the following sanitizers when building WAMR with sanitizer support: AddressSanitizer, UndefinedBehaviorSanitizer, ThreadSanitizer, or Pointer-Overflow Sanitizer. + +### **Intel Protected File System** + +- **WAMR_BUILD_SGX_IPFS**=1/0, default to off. + +> [!WARNING] +> This is for Intel SGX platforms only. + +### **Support spec test** + +- **WAMR_BUILD_SPEC_TEST**=1/0, default to off. + +### **WASM Cache** + +- **WAMR_BUILD_WASM_CACHE**=1/0, default to off. + +### **Test Garbage Collection** + +- **WAMR_TEST_GC**=1/0, default to off. + +It is used to test garbage collection related APIs and features. Refer to [iwasm_gc.cmake](../core/iwasm/common/gc/iwasm_gc.cmake) for details. + +It is used to cache loaded wasm modules in memory to speed up module instantiation only in wasm-c-api. + +## **Combination of configurations** + +You can mix settings. For example, to disable the interpreter, enable AOT and WASI, run: + +```Bash +cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=1 -DWAMR_BUILD_PLATFORM=linux ``` -Create a VIP based on the VSB. Make sure the following components are added: -* INCLUDE_POSIX_PTHREADS -* INCLUDE_POSIX_PTHREAD_SCHEDULER -* INCLUDE_SHARED_DATA -* INCLUDE_SHL - -Copy the generated iwasm executable, the test WASM binary as well as the needed -shared libraries (libc.so.1, libllvm.so.1 or libgnu.so.1 depending on the VSB, -libunix.so.1) to a supported file system (eg: romfs). - -Note: -WAMR provides some features which can be easily configured by passing options to cmake, please see [Linux platform](./build_wamr.md#linux) for details. Currently in VxWorks, interpreter and builtin libc are enabled by default. - -Zephyr -------------------------- -You need to download the Zephyr source code first and embed WAMR into it. -``` Bash -git clone https://github.com/zephyrproject-rtos/zephyr.git -cd zephyr/samples/ -cp -a /product-mini/platforms/zephyr/simple . -cd simple -ln -s wamr -mkdir build && cd build -source ../../../zephyr-env.sh -cmake -GNinja -DBOARD=qemu_x86_nommu .. -ninja + +To enable the interpreter, disable AOT and WASI, and target X86_32, run: + +```Bash +cmake .. -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_TARGET=X86_32 ``` -Note: -WAMR provides some features which can be easily configured by passing options to cmake, please see [Linux platform](./build_wamr.md#linux) for details. Currently in Zephyr, interpreter, AoT and builtin libc are enabled by default. - - -AliOS-Things -------------------------- -1. a developerkit board id needed for testing -2. download the AliOS-Things code - ``` Bash - git clone https://github.com/alibaba/AliOS-Things.git - ``` -3. copy /product-mini/platforms/alios-things directory to AliOS-Things/middleware, and rename it as iwasm - ``` Bash - cp -a /product-mini/platforms/alios-things middleware/iwasm - ``` -4. create a link to in middleware/iwasm/ and rename it to wamr - ``` Bash - ln -s middleware/iwasm/wamr - ``` -5. modify file app/example/helloworld/helloworld.c, patch as: - ``` C - #include - #include - extern bool iwasm_init(); - int application_start(int argc, char *argv[]) - { - int count = 0; - iwasm_init(); - ... - } - ``` -6. modify file app/example/helloworld/aos.mk - ``` C - $(NAME)_COMPONENTS := osal_aos iwasm - ``` -7. build source code and run - For linuxhost: - ``` Bash - aos make helloworld@linuxhost -c config - aos make - ./out/helloworld@linuxhost/binary/helloworld@linuxhost.elf - ``` - - For developerkit: - Modify file middleware/iwasm/aos.mk, patch as: - ``` C - WAMR_BUILD_TARGET := THUMBV7M - ``` - - ``` Bash - aos make helloworld@developerkit -c config - aos make - ``` - download the binary to developerkit board, check the output from serial port - -Docker -------------------------- -[Docker](https://www.docker.com/) will download all the dependencies and build WAMR Core on your behalf. - -Make sure you have Docker installed on your machine: [macOS](https://docs.docker.com/docker-for-mac/install/), [Windows](https://docs.docker.com/docker-for-windows/install/) or [Linux](https://docs.docker.com/install/linux/docker-ce/ubuntu/). - -Build the Docker image: - -``` Bash -docker build --rm -f "Dockerfile" -t wamr:latest . + +When enabling SIMD for fast interpreter mode, turn on both SIMD and the SIMDe library: + +```Bash + +cmake .. -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_SIMD=1 -DWAMR_BUILD_LIB_SIMDE=1 ``` -Run the image in interactive mode: -``` Bash -docker run --rm -it wamr:latest + +For Valgrind, start with these and add more as needed: + +```Bash + #... + -DCMAKE_BUILD_TYPE=Debug \ + -DWAMR_DISABLE_HW_BOUND_CHECK=0 \ + -DWAMR_DISABLE_WRITE_GS_BASE=0 + #... ``` -You'll now enter the container at `/root`. +To enable the minimal Lime1 feature set, turn off features that are on by default such as bulk memory and reference types: + +```Bash +cmake .. -DWAMR_BUILD_LIME1=1 -DWAMR_BUILD_BULK_MEMORY=0 -DWAMR_BUILD_REF_TYPES=0 -DDWAMR_BUILD_SIMD=0 +``` diff --git a/doc/build_wasm_app.md b/doc/build_wasm_app.md index 2cf6239cc6..648d21534a 100644 --- a/doc/build_wasm_app.md +++ b/doc/build_wasm_app.md @@ -1,14 +1,44 @@ +# Build WASM applications +Prepare WASM building environments +================================== -# Prepare WASM building environments +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). -WASI-SDK version 7.0+ is the major tool supported by WAMR for building WASM applications. There are some other WASM compilers such as the standard clang compiler and Emscripten might also work [here](./other_wasm_compilers.md). +To install WASI SDK, please download the [wasi-sdk release](https://github.com/WebAssembly/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. -Install WASI SDK: Download the [wasi-sdk](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-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. -Build WASM applications -========================= +For [AssemblyScript](https://github.com/AssemblyScript/assemblyscript), please refer to [AssemblyScript quick start](https://www.assemblyscript.org/quick-start.html) and [AssemblyScript compiler](https://www.assemblyscript.org/compiler.html#command-line-options) for how to install `asc` compiler and build WASM applications. + +For Rust, please refer to [Install Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) to install *cargo*, *rustc* and *rustup*. By default they are under ~/.cargo/bin. + +And then run such a command to install `wasm32-wasip1` target. + +``` bash +$ rustup target add wasm32-wasip1 +``` + +To build WASM applications, run + +``` bash +$ cargo build --target wasm32-wasip1 +``` + +The output files are under `target/wasm32-wasip1`. + +To build a release version + +``` bash +$ cargo build --release --target wasm32-wasip1 +``` + + +Build WASM applications with wasi-sdk +===================================== You can write a simple ```test.c``` as the first sample. @@ -38,47 +68,253 @@ int main(int argc, char **argv) } ``` +To build the source file to WASM bytecode, we can input the following command: + +``` Bash +/opt/wasi-sdk/bin/clang -O3 -o test.wasm test.c +``` + +## 1. wasi-sdk options + +There are some useful options that are used to compile C/C++ to Wasm (for a full introduction, please refer to [clang command line argument reference](https://clang.llvm.org/docs/ClangCommandLineReference.html) and [wasm-ld command line argument manual](https://lld.llvm.org/WebAssembly.html)): + +- **-nostdlib** Do not use the standard system startup files or libraries when linking. In this mode, the **libc-builtin** library of WAMR must be built to run the wasm app, otherwise, the **libc-wasi** library must be built. You can specify **-DWAMR_BUILD_LIBC_BUILTIN=1** or **-DWAMR_BUILD_LIBC_WASI=1** for CMake to build WAMR with libc-builtin support or libc-wasi support. + +- **-Wl,--no-entry** Do not output any entry point + +- **-Wl,--export=\** Force a symbol to be exported, e.g. **-Wl,--export=foo** to export foo function + +- **-Wl,--export-all** Export all symbols (normally combined with --no-gc-sections) + +- **-Wl,--initial-memory=\** Initial size of the linear memory, which must be a multiple of 65536 + +- **-Wl,--max-memory=\** Maximum size of the linear memory, which must be a multiple of 65536 + +- **-z stack-size=\** The auxiliary stack size, which is an area of linear memory, must be smaller than the initial memory size. + +- **-Wl,--strip-all** Strip all symbols + +- **-Wl,--shared-memory** Use shared linear memory + +- **-Wl,--allow-undefined** Allow undefined symbols in linked binary + +- **-Wl,--allow-undefined-file=\** Allow symbols listed in \ to be undefined in linked binary +- **-pthread** Support POSIX threads in generated code -To build the source file to WASM bytecode, input following command: +For example, we can build the wasm app with the command: ``` Bash -/opt/wasi-sdk/bin/clang test.c -o test.wasm +/opt/wasi-sdk/bin/clang -O3 -nostdlib \ + -z stack-size=8192 -Wl,--initial-memory=65536 \ + -o test.wasm test.c \ + -Wl,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--export=__heap_base -Wl,--export=__data_end \ + -Wl,--no-entry -Wl,--strip-all -Wl,--allow-undefined +``` +to generate a wasm binary with nostdlib mode, the auxiliary stack size is 8192 bytes, initial memory size is 64 KB, main function, heap base global and data end global are exported, no entry function is generated (no _start function is exported), and all symbols are stripped. Note that it is nostdlib mode, so libc-builtin should be enabled by runtime embedder or iwasm (with `cmake -DWAMR_BUILD_LIBC_BUILT=1`, enabled by iwasm in Linux by default). + +If we want to build the wasm app with wasi mode, we may build the wasm app with the command: + +```bash +/opt/wasi-sdk/bin/clang -O3 \ + -z stack-size=8192 -Wl,--initial-memory=65536 \ + -o test.wasm test.c \ + -Wl,--export=__heap_base -Wl,--export=__data_end \ + -Wl,--strip-all +``` + +to generate a wasm binary with wasi mode, the auxiliary stack size is 8192 bytes, initial memory size is 64 KB, heap base global and data end global are exported, wasi entry function exported (_start function), and all symbols are stripped. Note that it is wasi mode, so libc-wasi should be enabled by runtime embedder or iwasm (with `cmake -DWAMR_BUILD_LIBC_WASI=1`, enabled by iwasm in Linux by default), and normally no need to export main function, by default _start function is executed by iwasm. + +> Note: for the Rust project, we can set these flags by setting the `rustflags` in the Cargo configuration file, e.g. `/.cargo/config.toml` or `$CARGO_HOME/config.toml`, for example: +> ```toml +> [build] +> rustflags = [ +> "-C", "link-arg=--initial-memory=65536", +> "-C", "link-arg=-zstack-size=8192", +> "-C", "link-arg=--export=__heap_base", +> "-C", "link-arg=--export=__data_end", +> "-C", "link-arg=--strip-all", +> ] +> ``` + +## 2. How to reduce the footprint? + +Firstly if libc-builtin (-nostdlib) mode meets the requirements, e.g. there are no file io operations in wasm app, we should build the wasm app with -nostdlib option as possible as we can, since the compiler doesn't build the libc source code into wasm bytecodes, which greatly reduces the binary size. + +### (1) Methods to reduce the libc-builtin (-nostdlib) mode footprint + +- export \_\_heap_base global and \_\_data_end global + ```bash + -Wl,--export=__heap_base -Wl,--export=__data_end + ``` + If the two globals are exported, and there are no memory.grow and memory.size opcodes (normally nostdlib mode doesn't introduce these opcodes since the libc malloc function isn't built into wasm bytecode), WAMR runtime will truncate the linear memory at the place of \__heap_base and append app heap to the end, so we don't need to allocate the memory specified by `-Wl,--initial-memory=n` which must be at least 64 KB. This is helpful for some embedded devices whose memory resource might be limited. + +> For the Rust project, please set the flags in the Cargo configuration file, for example: +> ```toml +> [build] +> rustflags = [ +> "-C", "link-arg=--export=__heap_base", +> "-C", "link-arg=--export=__data_end", +> "-C", "link-arg=--initial-memory=65536", +> ] +> ``` + +- reduce auxiliary stack size + + The auxiliary stack is an area of linear memory, normally the size is 64 KB by default which might be a little large for embedded devices and partly used, we can use `-z stack-size=n` to set its size. + +> For the Rust project, please set the flag in the Cargo configuration file, for example: +> ```toml +> [build] +> rustflags = [ +> "-C", "link-arg=-zstack-size=8192" +> ] +> ``` + +- use -O3 and -Wl,--strip-all + +> For the Rust project, please set the flag in the Cargo configuration file, for example: +> ```toml +> [build] +> rustflags = [ +> "-C", "link-arg=--strip-all" +> ] +> ``` + +- reduce app heap size when running iwasm + + We can pass `--heap-size=n` option to set the maximum app heap size for iwasm, by default it is 16 KB. For the runtime embedder, we can set the `uint32_t heap_size` argument when calling API ` wasm_runtime_instantiate`. + +- reduce wasm operand stack size when running iwasm + + WebAssembly is a binary instruction format for a stack-based virtual machine, which requires a stack to execute the bytecodes. We can pass `--stack-size=n` option to set the maximum stack size for iwasm, by default it is 16 KB. For the runtime embedder, we can set the `uint32_t stack_size` argument when calling API ` wasm_runtime_instantiate` and `wasm_runtime_create_exec_env`. + +- decrease block_addr_cache size for classic interpreter + + The block_addr_cache is a hash cache to store the else/end addresses for WebAssembly blocks (BLOCK/IF/LOOP) to speed up address lookup. This is only available in the classic interpreter. We can set it by defineing macro `-DBLOCK_ADDR_CACHE_SIZE=n`, e.g. add `add_defintion (-DBLOCK_ADDR_CACHE_SIZE=n)` in CMakeLists.txt, by default it is 64, and the total block_addr_cache size is 3072 bytes in 64-bit platform and 1536 bytes in 32-bit platform. + +### (2) Methods to reduce the libc-wasi (without -nostdlib) mode footprint + +Most of the above methods are also available for libc-wasi mode, besides them, we can export malloc and free functions with `-Wl,--export=malloc -Wl,--export=free` option, so WAMR runtime will disable its app heap and call the malloc/free function exported to allocate/free the memory from/to the heap space managed by libc. + +Note: wasm-ld from LLVM 13 and later automatically inserts ctor/dtor calls +for all exported functions for a command. (vs reactor) +It breaks the malloc/free exports mentioned above. + +## 3. Build wasm app with pthread support + +Please ref to [pthread library](./pthread_library.md) for more details. + +## 4. Build wasm app with SIMD support + +The official *wasi-sdk release* doesn't fully support *latest 128-bit SIMD spec* yet. WARM 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. + +And also you can install emsdk and use its SSE header files, please ref to workload samples, e.g. [bwa CMakeLists.txt](../samples/workload/bwa/CMakeLists.txt) and [wasm-av1 CMakeLists.txt](../samples/workload/wasm-av1/CMakeLists.txt) for more details. + +For both wasi-sdk and emsdk, please add the option `-msimd128` for clang or emcc to generate WASM application with SIMD bytecodes. + +# Build WASM applications with emsdk + +## 1. Install emsdk + +Assuming you are using Linux, you may install emcc and em++ from Emscripten EMSDK following the steps below: + +``` +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install latest +./emsdk activate latest +# And then source the emsdk_env.sh script before build wasm app +source emsdk_env.sh (or add it to ~/.bashrc if you don't want to run it each time) ``` +The Emscripten website provides other installation methods beyond Linux. + +## 2. emsdk options + +To build the wasm C source code into wasm binary, we can use the following command: + +```bash +EMCC_ONLY_FORCED_STDLIBS=1 emcc -O3 -s STANDALONE_WASM=1 \ + -o test.wasm test.c \ + -s TOTAL_STACK=4096 -s TOTAL_MEMORY=65536 \ + -s "EXPORTED_FUNCTIONS=['_main']" \ + -s ERROR_ON_UNDEFINED_SYMBOLS=0 +``` + +There are some useful options: + +- **EMCC_ONLY_FORCED_STDLIBS=1** whether to link libc library into the output binary or not, similar to `-nostdlib` option of wasi-sdk clang. If specified, then no libc library is linked and the **libc-builtin** library of WAMR must be built to run the wasm app, otherwise, the **libc-wasi** library must be built. You can specify **-DWAMR_BUILD_LIBC_BUILTIN=1** or **-DWAMR_BUILD_LIBC_WASI=1** for CMake to build WAMR with libc-builtin support or libc-wasi support. + + The emsdk's wasi implementation is incomplete, e.g. open a file might just return fail, so it is strongly not recommended to use this mode, especially when there are file io operations in wasm app, please use wasi-sdk instead. + +- **-s STANDALONE_WASM=1** build wasm app in standalone mode (non-web mode), if the output file has suffix ".wasm", then only wasm file is generated (without html file and JavaScript file). + +- **-s TOTAL_STACK=\** the auxiliary stack size, same as `-z stack-size=\` of wasi-sdk + +- **-s TOTAL_MEMORY=\** or **-s INITIAL_MEORY=\** the initial linear memory size + +- **-s MAXIMUM_MEMORY=\** the maximum linear memory size, only take effect if **-s ALLOW_MEMORY_GROWTH=1** is set + +- **-s ALLOW_MEMORY_GROWTH=1/0** whether the linear memory is allowed to grow or not + +- **-s "EXPORTED_FUNCTIONS=['func name1', 'func name2']"** to export functions + +- **-s ERROR_ON_UNDEFINED_SYMBOLS=0** disable the errors when there are undefined symbols + +For more options, please ref to /upstream/emscripten/src/settings.js, or [Emscripten document](https://emscripten.org/docs/compiling/Building-Projects.html). # Build a project with cmake -If you have complex WASM application project which contains dozens of source files, you can consider using cmake for project building. +If you have a complex WASM application project which contains dozens of source files, you can consider using cmake for project building. You can cross compile your project by using the toolchain provided by WAMR. -We can generate a `CMakeLists.txt` file for `test.c`: +Assume the original `CMakeLists.txt` for `test.c` likes below: ``` cmake -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required (VERSION 3.14) project(hello_world) - -set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},--export=main") add_executable(hello_world test.c) ``` -It is simple to build this project by cmake: +It is easy to use *wasi-sdk* in CMake by setting *CMAKE_TOOLCHAIN_FILE* without any modification on the original *CMakeLists.txt*. -``` Bash -mkdir build && cd build -cmake .. -DCMAKE_TOOLCHAIN_FILE=$WAMR_ROOT/wamr-sdk/app/wamr_toolchain.cmake -make +``` +$ cmake -DWASI_SDK_PREFIX=${WASI_SDK_INSTALLTION_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_SDK_INSTALLTION_DIR}/share/cmake/wasi-sdk.cmake + -DCMAKE_SYSROOT= + .. ``` -You will get ```hello_world``` which is the WASM app binary. +`WASI_SDK_INSTALLTION_DIR` is the directory in where you install the *wasi-sdk*. like */opt/wasi-sdk* -> Note: If you have already built a SDK profile, then the **DCMAKE_TOOLCHAIN_FILE** should be changed into `$WAMR_ROOT/wamr-sdk/out/${PROFILE}/app-sdk/wamr_toolchain.cmake` +If you prefer WASI, set *CMAKE_SYSROOT* to *wasi-sysroot* +``` +$ cmake + -DCMAKE_SYSROOT=${WASI_SDK_INSTALLTION_DIR}/share/wasi-sysroot + .. +``` -# Compile WASM to AoT module +If you prefer *WAMR builtin libc*, set *CMAKE_SYSROOT* to *libc-builtin-sysroot* -Please ensure the wamrc was already generated and available in your shell PATH. Then we can use wamrc to compile WASM app binary to WAMR AoT binary. +> Note: If you have already built a SDK profile + +``` +$ cmake + -DCMAKE_SYSROOT=${WAMR_SOURCE_ROOT}/wamr-sdk/app/libc-builtin-sysroot + .. +``` + +You will get ```hello_world``` which is the WASM app binary. + + +# Compile WASM to AOT module + +Please ensure the wamrc was already generated and available in your shell PATH. Then we can use wamrc to compile WASM app binary to WAMR AOT binary. ``` Bash wamrc -o test.aot test.wasm @@ -90,11 +326,14 @@ wamrc supports a number of compilation options through the command line argument wamrc --help Usage: wamrc [options] -o output_file wasm_file --target= Set the target arch, which has the general format: - = x86_64, i386, arm, thumb, mips. + = x86_64, i386, aarch64, arm, thumb, xtensa, mips, + riscv64, riscv32. Default is host arch, e.g. x86_64 = for ex. on arm or thumb: v5, v6m, v7a, v7m, etc. Use --target=help to list supported targets - --target-abi= Set the target ABI, e.g. gnu, eabi, gnueabihf, etc. (default: gnu) + --target-abi= Set the target ABI, e.g. gnu, eabi, gnueabihf, msvc, etc. + Default is gnu if target isn't riscv64 or riscv32 + For target riscv64 and riscv32, default is lp64d and ilp32d Use --target-abi=help to list all the ABI supported --cpu= Set the target CPU (default: host CPU, e.g. skylake) Use --cpu=help to list all the CPU supported @@ -102,24 +341,108 @@ Usage: wamrc [options] -o output_file wasm_file Use +feature to enable a feature, or -feature to disable it For example, --cpu-features=+feature1,-feature2 Use --cpu-features=+help to list all the features supported - --opt-level=n Set the optimization level (0 to 3, default: 3, which is fastest) - --size-level=n Set the code size level (0 to 3, default: 3, which is smallest) - -sgx Generate code for SGX platform (Intel Software Guard Extention) + --opt-level=n Set the optimization level (0 to 3, default is 3) + --size-level=n Set the code size level (0 to 3, default is 3) + -sgx Generate code for SGX platform (Intel Software Guard Extension) + --bounds-checks=1/0 Enable or disable the bounds checks for memory access: + by default it is disabled in all 64-bit platforms except SGX and + in these platforms runtime does bounds checks with hardware trap, + and by default it is enabled in all 32-bit platforms --format= Specifies the format of the output file The format supported: aot (default) AoT file object Native object file llvmir-unopt Unoptimized LLVM IR llvmir-opt Optimized LLVM IR + --disable-bulk-memory Disable the MVP bulk memory feature + --enable-bulk-memory-opt Enable bulk memory opt feature + --enable-extended-const Enable extended const expr feature + --enable-multi-thread Enable multi-thread feature, the dependent features bulk-memory and + thread-mgr will be enabled automatically + --enable-tail-call Enable the post-MVP tail call feature + --disable-simd Disable the post-MVP 128-bit SIMD feature: + currently 128-bit SIMD is only supported for x86-64 target, + and by default it is enabled in x86-64 target and disabled + in other targets + --disable-ref-types Disable the MVP reference types feature + --enable-call-indirect-overlong + Enable call indirect overlong feature + --enable-lime1 Enable Lime1 + --disable-aux-stack-check Disable auxiliary stack overflow/underflow check + --enable-dump-call-stack Enable stack trace feature + --enable-perf-profiling Enable function performance profiling + -v=n Set log verbose level (0 to 5, default is 2), larger with more log Examples: wamrc -o test.aot test.wasm wamrc --target=i386 -o test.aot test.wasm wamrc --target=i386 --format=object -o test.o test.wasm +``` + +## AoT-compiled module compatibility among WAMR versions + +When making major ABI changes for AoT-compiled modules, we bump +`AOT_CURRENT_VERSION` constant in `core/config.h` header. +The runtime rejects to load a module AoT-compiled with wamrc with +a non-compatible`AOT_CURRENT_VERSION`. + +We try our best to maintain our runtime ABI for AoT-compiled modules +compatible among WAMR versions with compatible `AOT_CURRENT_VERSION` +so that combinations of older wamrc and newer runtime usually work. + +However, there might be minor incompatibilities from time to time. For +example, we usually avoid bumping the version when making a change which +affects only a small fraction of users. For productions, we recommend +using exactly same versions of wamrc and the runtime. + +| WAMR version | AOT_CURRENT_VERSION | Compatible AOT version | | +| ------------ | ------------------- | ---------------------- | ---------------------- | +| 1.x | 3 | 3 | | +| 2.0.0 | 3 | 3 | | +| 2.1.x | 3 | 3 | | +| 2.2.0 | 3 | 3 | | +| 2.3.0 | 4 | 3,4 | | +| 2.4.0 | 4 | 3,4 | See the following note | +| 2.4.1 | 5 | 5 | | +Note: 2.4.0 had a broken AoT versioning. See [issue 4504] for details. +We recommend all 2.4.0 users to migrate to 2.4.1. + +[issue 4504]: https://github.com/bytecodealliance/wasm-micro-runtime/issues/4504 + +## AoT compilation with 3rd-party toolchains + +`wamrc` uses LLVM to compile wasm bytecode to AoT file, this works for most of the architectures, but there may be circumstances where you want to use 3rd-party toolchains to take over some steps of the compilation pipeline, e.g. + +1. The upstream LLVM doesn't support generating object file for your CPU architecture (such as ARC), then we may need some other assembler to do such things. +2. You may get some other LLVM-based toolchains which may have better optimizations for the specific target, then you may want your toolchain to take over all optimization steps. + +`wamrc` provides two environment variables to achieve these: +- `WAMRC_LLC_COMPILER` + + When specified, `wamrc` will emit the optimized LLVM-IR (.bc) to a file, and invoke `$WAMRC_LLC_COMPILER` with ` -c -O3 ` to generate the object file. + + Optionally, you can use environment variable `WAMRC_LLC_FLAGS` to overwrite the default flags. + +- `WAMRC_ASM_COMPILER` + + When specified, `wamrc` will emit the text based assembly file (.s), and invoke `$WAMRC_ASM_COMPILER` with ` -c -O3 ` to generate the object file. + + Optionally, you can use environment variable `WAMRC_ASM_FLAGS` to overwrite the default flags. + +### Usage example +``` bash +WAMRC_LLC_COMPILER=/usr/local/opt/llvm@14/bin/clang WAMRC_LLC_FLAGS="--target=x86_64-pc-linux-gnu -mcmodel=medium -c -O3" ./wamrc -o test.aot test.wasm ``` +> Note: `wamrc` will verify whether the specified file exists and executable. If verification failed, `wamrc` will report a warning and fallback to normal pipeline. Since the verification is based on file, you **must specify the absolute path to the binary** even if it's in `$PATH` + +> Note: `WAMRC_LLC_COMPILER` has higher priority than `WAMRC_ASM_COMPILER`, if `WAMRC_LLC_COMPILER` is set and verified, then `WAMRC_ASM_COMPILER` will be ignored. + +> Note: the `LLC` and `ASM` in the env name just means this compiler will be used to compile the `LLVM IR file`/`assembly file` to object file, usually passing the compiler driver is the simplest way. (e.g. for LLVM toolchain, you don't need to pass `/usr/bin/llc`, using `/usr/bin/clang` is OK) + +> Note: You might need to set `WAMRC_LLC_FLAGS`/`WAMRC_ASM_FLAGS` to match whatever the `wamrc` command would automatically do. In the above example, `-mcmodel=medium` corresponds to `wamrc --size-level=1`, which is the default of `wamrc` on macOS. Run WASM app in WAMR mini product build -======================== +======================================= Run the test.wasm or test.aot with WAMR mini product build: ``` Bash @@ -129,7 +452,7 @@ Run the test.wasm or test.aot with WAMR mini product build: You will get the following output: ``` Hello world! -buf ptr: 0x400002b0 +buf ptr: 0xffffc2c8 buf: 1234 ``` If you would like to run the test app on Zephyr, we have embedded a test sample into its OS image. You will need to execute: diff --git a/doc/devcontainer.md b/doc/devcontainer.md new file mode 100644 index 0000000000..65458a3d51 --- /dev/null +++ b/doc/devcontainer.md @@ -0,0 +1,25 @@ +# Visual Studio Code development container + +We all know Docker containers and may use them a lot in school or work. It resolves dependency management for our projects/applications, prevents package version confusion and conflict, and contamination of the local environment. + +Now WAMR has a Dockerfile under path `.devcontainer` to create a container image, dev container images that you could easily use in VS Code. In case you prefer other IDE like Clion, you can also build it and use for the IDE you like. + +## How to use it + +It's straightforward to use Docker in VS Code! First, you have VS Code and Docker installed(if not yet, check [next section](#learn-more-about-docker-and-vs-code) for howto). Then you need to download Docker in VS Code extensions marketplace. + +And that's it, and you are good to go! When you open the root folder of WAMR, in the bottom right corner, the Docker extension will pop a notification and ask if you like to reopen the folder in a container. + +If you encounter any problems or get stuck somewhere, may this video [demo](https://youtu.be/Uvf2FVS1F8k) for docker usage in VS Code will help. + +## Learn more about Docker and VS Code + +[Install Docker](https://docs.docker.com/get-docker/) + +[Install VS Code](https://code.visualstudio.com/) + +[Docker extension for VS Code](https://code.visualstudio.com/docs/containers/overview) + +[Remote development with Docker in VS Code](https://code.visualstudio.com/docs/remote/containers#_getting-started) + +[What is dev container image in VS Code](https://code.visualstudio.com/docs/remote/containers#_prebuilding-dev-container-images) diff --git a/doc/embed_wamr.md b/doc/embed_wamr.md index 351dd351cf..7d9c46f9d6 100644 --- a/doc/embed_wamr.md +++ b/doc/embed_wamr.md @@ -1,43 +1,346 @@ -Embed WAMR into software production +Embedding WAMR guideline ===================================== -![WAMR embed diagram](./pics/embed.PNG "WAMR embed architecture diagram") +**Note**: This document is about how to embed WAMR into C/C++ host applications, for other languages, please refer to: [Embed WAMR into Python](../language-bindings/python), [Embed WAMR into Go](../language-bindings/go). + +All the embedding APIs supported by the runtime are defined under folder [core/iwasm/include](../core/iwasm/include). The API details are available in the header files. + +## Embed WAMR into developer's project + +WAMR is designed to be easy embeddable in any project, a typical way of building WAMR is to use cmake, developer can configure features by setting cmake variables and then include the script `runtime_lib.cmake` under folder [build-scripts](../build-scripts) in his CMakeList.txt, for example: +``` cmake +set (WAMR_BUILD_PLATFORM "linux") +set (WAMR_BUILD_TARGET "X86_64") +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_FAST_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_SIMD 1) +set (WAMR_ROOT_DIR path/to/wamr/root) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +# include bh_read_file.h +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) +add_executable (your_project main.c ${UNCOMMON_SHARED_SOURCE}) + +target_link_libraries (your_project vmlib -lm) +``` +Examples can be found in [CMakeLists.txt of linux platform](../product-mini/platforms/linux/CMakeLists.txt) and [other platforms](../product-mini/platforms). The available features to configure can be found in [Build WAMR vmcore](./build_wamr.md#wamr-vmcore-cmake-building-configurations). + +Developer can also use Makefile to embed WAMR, by defining macros and including directories, and adding the source files, examples can be found in [makefile of alios-things platform](../product-mini/platforms/alios-things/aos.mk) and [makefile of nuttx platform](../product-mini/platforms/nuttx/wamr.mk). + +## The runtime initialization -A typical WAMR API usage is shown below (some return value checks are ignored): ``` C - static char global_heap_buf[512 * 1024]; + #include "bh_platform.h" + #include "bh_read_file.h" + #include "wasm_export.h" char *buffer, error_buf[128]; wasm_module_t module; wasm_module_inst_t module_inst; wasm_function_inst_t func; wasm_exec_env_t exec_env; - uint32 argv[2], size, stack_size = 8092, heap_size = 8092; + uint32 size, stack_size = 8092, heap_size = 8092; - bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)); + /* initialize the wasm runtime by default configurations */ wasm_runtime_init(); - buffer = read_wasm_binary_to_buffer(…, &size); + /* read WASM file into a memory buffer */ + buffer = bh_read_file_to_buffer(…, &size); + + /* add line below if we want to export native functions to WASM app */ + wasm_runtime_register_natives(...); + + /* parse the WASM file from buffer and create a WASM module */ module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf)); + + /* create an instance of the WASM module (WASM linear memory is ready) */ module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, error_buf, sizeof(error_buf)); - func = wasm_runtime_lookup_function(module_inst, "fib", "(i32)i32"); +``` + +The `wasm_runtime_init()` uses the default memory allocator os_malloc/os_free function from the [`core/shared/platform`](../core/shared/platform) for the runtime memory management. + +WAMR supports to restrict its all memory allocations in a raw buffer. It ensures the dynamic memories used by the WASM applications won't harm the system availability, which is extremely important for embedded systems. This can be done by using `wasm_runtime_full_init()`. This function also allows you to configure the native API's for exporting to WASM app, and set the maximum thread number when multi-thread feature is enabled. + +Refer to the following sample: + +```c +/* the native functions that will be exported to WASM app */ +static NativeSymbol native_symbols[] = { + EXPORT_WASM_API_WITH_SIG(display_input_read, "(*)i"), + EXPORT_WASM_API_WITH_SIG(display_flush, "(iiii*)") +}; + +/* all the runtime memory allocations are retricted in the global_heap_buf array */ +static char global_heap_buf[512 * 1024]; +RuntimeInitArgs init_args; +memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +/* configure the memory allocator for the runtime */ +init_args.mem_alloc_type = Alloc_With_Pool; +init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; +init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + +/* configure the native functions being exported to WASM app */ +init_args.native_module_name = "env"; +init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); +init_args.native_symbols = native_symbols; + +/* set maximum thread number if needed when multi-thread is enabled, + the default value is 4 */ +init_args.max_thread_num = max_thread_num; + +/* initialize runtime environment with user configurations*/ +if (!wasm_runtime_full_init(&init_args)) { + return -1; +} +``` + +## Native calls WASM functions and passes parameters + +After a module is instantiated, the runtime embedder can lookup the target WASM function by name, and create execution environment to call the function. + +```c + /* lookup a WASM function by its name + The function signature can NULL here */ + func = wasm_runtime_lookup_function(module_inst, "fib"); + + /* creat an execution environment to execute the WASM functions */ exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); +``` + +There are several ways to call WASM function: +1. Function call with parameters in an array of 32 bits elements and size: + +```c + uint32 argv[2]; + + /* arguments are always transferred in 32-bit element */ argv[0] = 8; - if (wasm_runtime_call_wasm(exec_env, func, 1, argv_buf) ) { - /* the return value is stored in argv[0] */ - printf("fib function return: %d\n", argv[0]); + + /* call the WASM function */ + if (wasm_runtime_call_wasm(exec_env, func, 1, argv) ) { + /* the return value is stored in argv[0] */ + printf("fib function return: %d\n", argv[0]); + } + else { + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } +``` + +The parameters are transferred in an array of 32 bits elements. For parameters that occupy 4 or fewer bytes, each parameter can be a single array element. For parameters in types like double or int64, each parameter will take two array elements. The function return value will be sent back in the first one or two elements of the array according to the value type. See the sample code below: + +```c + uint32 argv[6]; + char arg1 = 'a'; + int arg2 = 10; + double arg3 = 1.0; + int64 arg4 = 100; + double ret; + + argv[0] = arg1; + argv[1] = arg2; + + /** + * use memory copy for 8-byte parameters rather than + * *(double*)(&argv[2]) = arg3 here because some archs + * like ARM, MIPS require the address must be 8-byte aligned. + * Or use the aligned malloc or compiler align attribute + * to ensure the array address is 8-byte aligned + */ + memcpy(&argv[2], &arg3, sizeof(arg3)); + memcpy(&argv[4], &arg4, sizeof(arg4)); + + /* attention: the arg number is 6 here since both + arg3 and arg4 each takes 2 elements */ + wasm_runtime_call_wasm(exec_env, func, 6, argv); + + /* if the return value is type of 8 bytes, it takes + the first two array elements */ + memcpy(&ret, &argv[0], sizeof(ret)); +``` + +2. Function call with results and arguments both in `wasm_val_t` struct and size: + +```c + uint32 num_args = 1, num_results = 1; + wasm_val_t args[1], results[1]; + + /* set the argument type and value */ + args[0].kind = WASM_I32; + args[0].of.i32 = 8; + + /* call the WASM function */ + if (wasm_runtime_call_wasm_a(exec_env, func, num_results, results, num_args, args)) { + /* the return value is stored in results */ + printf("fib function return: %d\n", results[0].of.i32); } else { - printf("%s\n", wasm_runtime_get_exception(module_inst)); + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); } +``` + +3. Function call with variant argument support: + +```c + uint32 num_args = 1, num_results = 1; + wasm_val_t results[1]; + + /* call the WASM function */ + if (wasm_runtime_call_wasm_v(exec_env, func, 1, results, 1, 8)) { + /* the return value is stored in results */ + printf("fib function return: %d\n", results[0].of.i32); + } + else { + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } +``` + +## Pass buffer to WASM function + +If we need to transfer a buffer to WASM function, we can pass the buffer address through a parameter. **Attention**: The sandbox will forbid the WASM code to access outside memory, we must **allocate the buffer from WASM instance's own memory space and pass the buffer address in instance's space (not the runtime native address)**. + +There are two runtime APIs available for this purpose. + +```c +/** + * malloc a buffer from instance's private memory space. + * + * return: the buffer address in instance's memory space (pass to the WASM function) + * p_native_addr: return the native address of allocated memory + * size: the buffer size to allocate + */ +uint64_t +wasm_runtime_module_malloc(wasm_module_inst_t module_inst, + uint64_t size, void **p_native_addr); + +/** + * malloc a buffer from instance's private memory space, + * and copy the data from another native buffer to it. + * + * return: the buffer address in instance's memory space (pass to the WASM function) + * src: the native buffer address + * size: the size of buffer to be allocated and copy data + */ +uint64_t +wasm_runtime_module_dup_data(wasm_module_inst_t module_inst, + const char *src, uint64_t size); + +/* free the memory allocated from module memory space */ +void +wasm_runtime_module_free(wasm_module_inst_t module_inst, uint64_t ptr); +``` + +Usage sample: + +```c +char * buffer = NULL; +uint64_t buffer_for_wasm; + +buffer_for_wasm = wasm_runtime_module_malloc(module_inst, 100, &buffer); +if (buffer_for_wasm != 0) { + uint32 argv[3]; + strncpy(buffer, "hello", 100); /* use native address for accessing in runtime */ + argv[0] = buffer_for_wasm; /* pass the buffer address for WASM space */ + argv[2] = 100; /* the size of buffer */ + wasm_runtime_call_wasm(exec_env, func, 3, argv); + + /* it is runtime embedder's responsibility to release the memory, + unless the WASM app will free the passed pointer in its code */ + wasm_runtime_module_free(module_inst, buffer_for_wasm); +} +``` + +## Pass structured data to WASM function + +We can't pass structure data or class objects through the pointer since the memory layout can different in two worlds. The way to do it is serialization. Refer to [export_native_api.md](./export_native_api.md) for the details. + +## Execute wasm functions in multiple threads + +It isn't safe to use an `exec_env` object in multiple threads concurrently. +To run a multi-threaded application, you basically need a separate `exec_env` +for each threads. +### Approaches to manage `exec_env` objects and threads + +WAMR supports two approaches to manage `exec_env` and threads as described +below. While they are not exclusive, you usually only need to use one of +them. + +#### Make your WASM application manage threads + + You can make your WASM application spawn threads by itself, + typically using `pthread` APIs like `pthread_create`. + See [pthread library](./pthread_library.md) and + [pthread implementations](./pthread_impls.md) for more details. + In this case, WAMR manages `exec_env` for the spawned threads. + +#### Make your embedder manage threads + + The `spawn exec_env` and `spawn thread` APIs are available for the embedder. + You can use these APIs to manage the threads. + See [Thread related embedder API](./embed_wamr_spawn_api.md) for details. + +### Other notes about threads + +* You can manage the maximum number of threads + + ```C + init_args.max_thread_num = THREAD_NUM; + /* If this init argument is not set, the default maximum thread number is 4 */ + ``` + +* To share memory among threads, you need to build your WASM application with shared memory + + For example, it can be done with `--shared-memory` and `-pthread`. + + ```bash + /opt/wasi-sdk/bin/clang -o test.wasm test.c -nostdlib -pthread \ + -Wl,--shared-memory,--max-memory=131072 \ + -Wl,--no-entry,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors,--export=${your_func_name} + ``` + +* The corresponding threading feature should be enabled while building the runtime + + - WAMR lib-pthread (legacy) + + ```bash + cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 + ``` + + - wasi-threads + + ```bash + cmake .. -DWAMR_BUILD_LIB_WASI_THREADS=1 + ``` + + - `wasm_runtime_spawn_exec_env` and `wasm_runtime_spawn_thread` + + ```bash + cmake .. -DWAMR_BUILD_THREAD_MGR=1 -DWAMR_BUILD_SHARED_MEMORY=1 + ``` + +## The deinitialization procedure + +``` wasm_runtime_destroy_exec_env(exec_env); wasm_runtime_deinstantiate(module_inst); wasm_runtime_unload(module); wasm_runtime_destroy(); - bh_memory_destroy(); + ``` +## Native calling WASM function working flow + +![WAMR embed diagram](./pics/embed.PNG "WAMR embed architecture diagram") diff --git a/doc/embed_wamr_spawn_api.md b/doc/embed_wamr_spawn_api.md new file mode 100644 index 0000000000..f0e637f305 --- /dev/null +++ b/doc/embed_wamr_spawn_api.md @@ -0,0 +1,38 @@ +# Thread related embedder API + +This document explains `wasm_runtime_spawn_exec_env` and +`wasm_runtime_spawn_thread`. +[Here](../samples/spawn-thread) is a sample to show how to use these APIs. + + * spawn exec_env + + `spawn exec_env` API creates a new `exec_env` based on the original `exec_env`. You can use it in other threads. It's up to the embedder how to manage host threads to run the new `exec_env`. + + ```C + new_exec_env = wasm_runtime_spawn_exec_env(exec_env); + + /* Then you can use new_exec_env in your new thread */ + module_inst = wasm_runtime_get_module_inst(new_exec_env); + func_inst = wasm_runtime_lookup_function(module_inst, ...); + wasm_runtime_call_wasm(new_exec_env, func_inst, ...); + + /* you need to use this API to manually destroy the spawned exec_env */ + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + ``` + + * spawn thread + + Alternatively, you can use `spawn thread` API to avoid managing the extra exec_env and the corresponding host thread manually: + + ```C + wasm_thread_t wasm_tid; + void *wamr_thread_cb(wasm_exec_env_t exec_env, void *arg) + { + module_inst = wasm_runtime_get_module_inst(exec_env); + func_inst = wasm_runtime_lookup_function(module_inst, ...); + wasm_runtime_call_wasm(exec_env, func_inst, ...); + } + wasm_runtime_spawn_thread(exec_env, &wasm_tid, wamr_thread_cb, NULL); + /* Use wasm_runtime_join_thread to join the spawned thread */ + wasm_runtime_join_thread(wasm_tid, NULL); + ``` diff --git a/doc/export_native_api.md b/doc/export_native_api.md index f1bfbabcf9..7f3e92db61 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -2,202 +2,291 @@ Export native API to WASM application ======================================================= -The basic working flow for WASM application calling into the native API is shown in the following diagram: -![WAMR WASM API ext diagram](./pics/extend_library.PNG "WAMR WASM API ext architecture diagram") +Exporting native API steps +-------------------------- -WAMR provides the macro `EXPORT_WASM_API` to enable users to export a native API to a WASM application. WAMR has implemented a base API for the timer and messaging by using `EXPORT_WASM_API`. This can be a point of reference for extending your own library. -``` C -static NativeSymbol extended_native_symbol_defs[] = { - EXPORT_WASM_API(wasm_register_resource), - EXPORT_WASM_API(wasm_response_send), - EXPORT_WASM_API(wasm_post_request), - EXPORT_WASM_API(wasm_sub_event), - EXPORT_WASM_API(wasm_create_timer), - EXPORT_WASM_API(wasm_timer_set_interval), - EXPORT_WASM_API(wasm_timer_cancel), - EXPORT_WASM_API(wasm_timer_restart) -}; -``` +#### Step 1: Declare the function interface in WASM app -**Security attention:** A WebAssembly application should only have access to its own memory space. As a result, the integrator should carefully design the native function to ensure that the memory accesses are safe. The native API to be exported to the WASM application must: +Create a header file in a WASM app and declare the functions that are exported from native. In this example, we declare foo and foo2 as below in the header file `example.h` -- Only use 32 bits number for parameters -- Should not pass data to the structure pointer (do data serialization instead) -- Should do the pointer address conversion in the native API -- Should not pass function pointer as callback +```c +/*** file name: example.h ***/ +int foo(int a, int b); +void foo2(char * msg, char * buffer, int buf_len); +``` -Below is a sample of a library extension. All code invoked across WASM and native world must be serialized and de-serialized, and the native world must do a boundary check for every incoming address from the WASM world. -In wasm world: +#### Step 2: Define the native API + +Then we should define the native functions in runtime source tree for handling the calls from the WASM app. The native function can be any name, for example **foo_native** and **foo2** here: + ``` C -void api_send_request(request_t * request, response_handler_f response_handler, - void * user_data) +int foo_native(wasm_exec_env_t exec_env , int a, int b) { - int size; - char *buffer; - transaction_t *trans; + return a+b; +} - if ((trans = (transaction_t *) malloc(sizeof(transaction_t))) == NULL) { - printf( - "send request: allocate memory for request transaction failed!\n"); - return; - } +void foo2(wasm_exec_env_t exec_env, char * msg, uint8 * buffer, int buf_len) +{ + strncpy(buffer, msg, buf_len); +} +``` + +The first parameter exec_env must be defined using type **wasm_exec_env_t** which is the calling convention by WAMR. + +The rest parameters should be in the same types as the parameters of WASM function foo(), but there are a few special cases that are explained in section "Buffer address conversion and boundary check". Regarding the parameter names, they don't have to be the same, but we would suggest using the same names for easy maintenance. - memset(trans, 0, sizeof(transaction_t)); - trans->handler = response_handler; - trans->mid = request->mid; - trans->time = wasm_get_sys_tick_ms(); - trans->user_data = user_data; - // pack request - if ((buffer = pack_request(request, &size)) == NULL) { - printf("send request: pack request failed!\n"); - free(trans); - return; - } - transaction_add(trans); +#### Step 3: Register the native APIs - /* if the trans is the 1st one, start the timer */ - if (trans == g_transactions) { - /* assert(g_trans_timer == NULL); */ - if (g_trans_timer == NULL) { - g_trans_timer = api_timer_create(TRANSACTION_TIMEOUT_MS, - false, - true, transaction_timeout_handler); - } +Register the native APIs in the runtime, then everything is fine. It is ready to build the runtime software. + +``` C +// Define an array of NativeSymbol for the APIs to be exported. +// Note: the array must be static defined since runtime +// will keep it after registration +static NativeSymbol native_symbols[] = +{ + { + "foo", // the name of WASM function name + foo_native, // the native function pointer + "(ii)i" // the function prototype signature + }, + { + "foo2", // the name of WASM function name + foo2, // the native function pointer + "($*~)" // the function prototype signature } +}; - // call native API - wasm_post_request(buffer, size); +// initialize the runtime before registering the native functions +wasm_runtime_init(); - free_req_resp_packet(buffer); +int n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); +if (!wasm_runtime_register_natives("env", + native_symbols, + n_native_symbols)) { + goto fail1; } + +// natives registration must be done before loading WASM modules +module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf)); + ``` -In native world: +**Function signature**: + +The function signature field in **NativeSymbol** structure is a string for describing the function prototype. It is critical to ensure the function signature is correctly mapping the native function interface. + +Each letter in the "()" represents a parameter type, and the one following after ")" represents the return value type. The meaning of each letter: + +- '**i**': i32 +- '**I**': i64 +- '**f**': f32 +- '**F**': f64 +- '**r**': externref (has to be the value of a `uintptr_t` variable), or all kinds of GC reference types when GC feature is enabled +- '**\***': the parameter is a buffer address in WASM application +- '**~**': the parameter is the byte length of WASM buffer as referred by preceding argument "\*". It must follow after '*', otherwise, registration will fail +- '**$**': the parameter is a string in WASM application + +The signature can defined as NULL, then all function parameters are assumed as i32 data type. + +**Use EXPORT_WASM_API_WITH_SIG** + +The `NativeSymbol` element for `foo2 ` above can be also defined with macro EXPORT_WASM_API_WITH_SIG. This macro can be used when the native function name is the same as the WASM symbol name. + +```c +static NativeSymbol native_symbols[] = +{ + EXPORT_WASM_API_WITH_SIG(foo2, "($*~)") // wasm symbol name will be "foo2" +}; +``` + +​ + +## Call exported API in WASM application + +Now we can call the exported native API in wasm application like this: ``` C -void -wasm_post_request(wasm_exec_env_t exec_env, - int32 buffer_offset, int size) +#include +#include "example.h" // where the APIs are declared + +int main(int argc, char **argv) +{ + int a = 0, b = 1; + char * msg = "hello"; + char buffer[100]; + + int c = foo(a, b); // call into native foo_native() + foo2(msg, buffer, sizeof(buffer)); // call into native foo2() + + return 0; +} +``` + +## Build native lib into shared library and register it with `iwasm` application + +Developer can also build the native library into a shared library and register it with iwasm application: +```bash +iwasm --native-lib= +``` + +Refer to [native lib sample](../samples/native-lib) for more details. + + +## Buffer address conversion and boundary check + +A WebAssembly sandbox ensures applications only access to its own memory with a private address space. When passing a pointer address from WASM to native, the address value must be converted to native address before the native function can access it. It is also the native world's responsibility to check the buffer length is not over its sandbox boundary. + + + +The signature letter '$', '\*' and '\~' help the runtime do automatic address conversion and buffer boundary check, so the native function directly uses the string and buffer address. **Notes**: if '\*' is not followed by '\~', the native function should not assume the length of the buffer is more than 1 byte. + + + +As function parameters are always passed in 32 bits numbers, you can also use 'i' for the pointer type argument, then you must do all the address conversion and boundary checking in your native function. For example, if you change the foo2 signature to "(iii)", then you will implement the native part as the following sample: + +```c +// +// If the function signature used i32 data type ("i") +// for buffer address or string parameters, here +// is how to do address conversion and boundary check manually +// +void foo2(wasm_exec_env_t exec_env, + uint32 msg_offset, + uint32 buffer_offset, + int32 buf_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; + char *buffer; + char * msg ; // do boundary check - if (!validate_app_addr(buffer_offset, size)) + if (!wasm_runtime_validate_app_str_add(msg_offset)) + return 0; + + if (!wasm_runtime_validate_app_addr((uint64)buffer_offset, (uint64)buf_len)) return; // do address conversion - buffer = addr_app_to_native(buffer_offset); + buffer = wasm_runtime_addr_app_to_native((uint64)buffer_offset); + msg = wasm_runtime_addr_app_to_native((uint64)msg_offset); - if (buffer != NULL) { - request_t req[1]; + strncpy(buffer, msg, buf_len); +} +``` - // De-serialize data - if (!unpack_request(buffer, size, req)) - return; - // set sender to help dispatch the response to the sender app later - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(mod_id != ID_NONE); - req->sender = mod_id; - if (req->action == COAP_EVENT) { - am_publish_event(req); - return; - } - am_dispatch_request(req); - } -} -``` +## Sandbox security attention +The runtime builder should ensure not broking the memory sandbox when exporting the native function to WASM. +A ground rule: +- Do the pointer address conversion in the native API if "$\*" is not used for the pointer in the function signature -Steps for exporting native API -========================== +A few recommendations: -WAMR implemented a framework for developers to export API's. Below is the procedure to expose the platform API's in three steps: +- Never pass any structure/class object pointer to native (do data serialization instead) +- Never pass a function pointer to the native +Note: while not recommended here, nothing prevents you from passing +structure/function pointers as far as the native API is aware of +and careful about the ABI used in the wasm module. For example, +C function pointers are usually represented as table indexes which +the native API can call with wasm_runtime_call_indirect() or similar. +However, in this document, we don't recommend to implement your native +API that way unless necessary because it needs extra carefulness. -## Step 1: Define the native API for exporting -Define the function **example_native_func** in your source file, namely `example.c` here: -``` C -int example_native_func(wasm_exec_env_t exec_env, - int arg1, int arg2) +## Pass structured data or class object + +We must do data serialization for passing structured data or class objects between the two worlds of WASM and native. There are two serialization methods available in WASM as below, and yet you can introduce more like json, cbor etc. + +- [attributes container](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/app-native-shared/attr_container.c) +- [restful request/response](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/app-native-shared/restful_utils.c) + +Note the serialization library is separately compiled into WASM and runtime. And the source files are located in the folder "[wamr-app-framework/app-framework/app-native-shared](https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/app-native-shared)“ where all source files will be compiled into both worlds. + + + +The following sample code demonstrates WASM app packs a response structure to buffer, then pass the buffer pointer to the native: + +```c +/*** file name: https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/base/app/request.c ***/ + +void api_response_send(response_t *response) { - // Your implementation here + int size; + char * buffer = pack_response(response, &size); + if (buffer == NULL) + return; + + wasm_response_send(buffer, size); // calling exported native API + free_req_resp_packet(buffer); } ``` -The first function argument must be defined using type **wasm_exec_env_t** which is the WAMR calling convention for native API exporting. -The function prototype should also be declared in a header file so the wasm application can include it. -``` C -#ifndef _EXAMPLE_H_ -#define _EXAMPLE_H_ -#ifdef __cplusplus -extern "C" { -#endif -void example_native_func(int arg1, int arg2); -#ifdef __cplusplus -} -#endif -#endif -``` +The following code demonstrates the native API unpack the WASM buffer to local native data structure: -## Step 2: Declare the native API exporting +```c +/*** file name: https://github.com/bytecodealliance/wamr-app-framework/blob/main/app-framework/base/native/request_response.c ***/ -Declare the function **example_native_func** with macro **EXPORT_WASM_API** in your **.inl** file, namely `example.inl` in this sample. -``` C -EXPORT_WASM_API(example_native_func), -``` +bool +wasm_response_send(wasm_exec_env_t exec_env, char *buffer, int size) +{ + if (buffer != NULL) { + response_t response[1]; -Then include the file **example.inl** in definition of array **extended_native_symbol_defs** in the `ext_lib_export.c`. -``` C -static NativeSymbol extended_native_symbol_defs[] = { - #include "example.inl" -}; + if (NULL == unpack_response(buffer, size, response)) + return false; -#include "ext_lib_export.h" + am_send_response(response); + + return true; + } + + return false; +} ``` -## Step 3: Compile the runtime product -Add the source file **example.c** and **ext_lib_export.c** into the CMakeList.txt for building runtime with the exported API's: -``` cmake -set (EXT_API_SOURCE example.c) +## Native API implementation notes -add_executable (sample - # other source files - # ...... - ${EXT_API_SOURCE} - ext_lib_export.c -) -``` +### Async thread termination -# Use exported API in wasm application +When threading support is enabled, a thread can be requested to terminate +itself either explicitly by the `wasm_runtime_terminate` API or implicitly +by cluster-wide activities like WASI `proc_exit`. +If a call to your native function can take long or even forever, +you should make it to respond to termination requests in a timely manner. +There are a few implementation approaches for that: -We can call the exported native API **example_native_func** in wasm application like this: -``` C -#include -#include "example.h" +* Design your API so that it always returns in a timely manner. + This is the most simple approach when possible. -int main(int argc, char **argv) -{ - int a = 0, b = 1; +* Make your native function check the cluster state by calling + `wasm_cluster_is_thread_terminated` from time to time. + If `wasm_cluster_is_thread_terminated` returns true, make your + native function return. + Note: as of writing this, `wasm_cluster_is_thread_terminated` is not + exported as a runtime API. - example_native_func(a, b); - return 0; -} -``` \ No newline at end of file +* If your native function is a simple wrapper of blocking system call, + you can probably use the `wasm_runtime_begin_blocking_op` and + `wasm_runtime_end_blocking_op` runtime API. + See the comment in `wamr_export.h` for details. + +* If your native function is very complex, it might be simpler to put the + guts of it into a separate worker process so that it can be cleaned up + by simply killing it. diff --git a/doc/linux_sgx.md b/doc/linux_sgx.md new file mode 100644 index 0000000000..e7a32753a1 --- /dev/null +++ b/doc/linux_sgx.md @@ -0,0 +1,271 @@ + +Build and Port WAMR vmcore (iwasm) for Linux SGX +================================================ + +Build WAMR vmcore (iwasm) for Linux SGX +--------------------------------------- + +First of all please install the [Intel SGX SDK](https://software.intel.com/en-us/sgx/sdk), v2.8 or later is required, and it is recommended to install the SDK to /opt/intel/sgxsdk. + +After installing the dependencies, build the source code: +``` Bash +source /environment +cd product-mini/platforms/linux-sgx/ +mkdir build && cd build +cmake .. +make +``` + +By default the `fast interpreter` and `AOT` is enabled. If to enable `Fast JIT`, run: +```Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_FAST_JIT=1 +make +``` + +This builds two libraries required by SGX application: + - libvmlib.a for Enclave part + - libvmlib_untrusted.a for App part + +**Note:** WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](./build_wamr.md#wamr-vmcore-cmake-building-configurations) for the details. Currently in Linux SGX, fast interpreter, AOT, libc-builtin, libc-WASI and lib-pthread are enabled by default. + +Then build the enclave sample: +``` Bash +source /environment +cd enclave-sample +make +``` + +**Note:** By default, the generated SGX application assumes it is signed with production key and running on simulation mode. The user can explicitly specify the relative variables in commandline to overwrite the default settings. For example, to build a debug enclave, please build the enclave with `make SGX_DEBUG=1`. To build the enclave running on a hardware-based SGX platform, execute `make SGX_MODE=HW`. + +The binary file iwasm will be generated. To run the sample: + +``` Bash +source /environment +iwasm [-options] wasm_file [args...] +or: +iwasm [-options] aot_file [args...] +``` + +### Minimal build +The libc-WASI and lib-pthread features require a lot of ocalls, if you don't need so much ocalls in your application, you can use the `minimal` version + +``` Bash +# replace the build files with minimal version +cd product-mini/platforms/linux-sgx/ +cp CMakeLists_minimal.txt CMakeLists.txt +cp enclave-sample/Makefile_minimal enclave-sample/Makefile +cp enclave-sample/Enclave/Enclave_minimal.edl enclave-sample/Enclave/Enclave.edl +# follow the building process above +``` + +Port WAMR vmcore for Linux SGX +------------------------------ + +The enclave-sample creates a sample to embed wamr vmlib of Enclave part and App part to an SGX application. To port WAMR vmcore lib to SGX application, there are some steps to do: + +**Step 1: Add "sgx_wamr.edl" and "sgx_pthread.edl" into EDL file, e.g. Enclave.edl:** +> This step is not required in minimal version + +```bash +from "sgx_pthread.edl" import *; +from "sgx_wamr.edl" import *; +``` + +The sgx_wamr.edl is under ${WAMR_ROOT}/core/shared/platform/linux-sgx, so please **add it to the search path list** when generating Enclave_u.c and Enclave_t.c from Enclave.edl: + +```bash +@cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx +``` + +```bash +@cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx +``` + +**Step 2: Link libvmlib.a to Enclave part and link libvmlib_untrusted.a to App part:** +> libvmlib_untrusted.a is not required in minimal version + +```makefile +Enclave_Link_Flags := ... libvmlib.a ... +``` + +```makefile +App_Link_Flags := ... libvmlib_untrusted.a ... +``` + +**And link SGX pthread lib to Enclave part:** +> SGX pthread lib is not required in minimal version + +```makefile +Enclave_Link_Flags := ... -lsgx_pthread ... +``` + +**Step 3: Add WAMR folders and SGX SDK folders to Enclave include path:** + +```makefile +Enclave_Include_Paths := ... -I$(WAMR_ROOT)/core/iwasm/include \ + -I$(WAMR_ROOT)/core/shared/utils \ + -I$(WAMR_ROOT)/core/shared/platform/linux-sgx \ + -I$(SGX_SDK)/include \ + -I$(SGX_SDK)/include/tlibc \ + -I$(SGX_SDK)/include/stlport +``` + +**Step 4: Configure reserved memory and thread info in file Enclave config file (e.g. Enclave.config.xml) to support WAMR AOT and multi-thread, e.g:** + +```xml +0x400000 +1 +10 +``` + +**Step 5: To support log output and os_printf() function in Enclave, please implement an ocall_print function, e.g. in Enclave.edl, add:** + +```cpp +untrusted { + void ocall_print([in, string]const char* str); +}; +``` + +In App part, add: + +```cpp +void +ocall_print(const char* str) +{ + printf("%s", str); +} +``` + +And in Enclave part, set the print function: + +```cpp +#include "wasm_export.h" +#include "bh_platform.h" + +extern "C" { + typedef void (*os_print_function_t)(const char* message); + extern void os_set_print_function(os_print_function_t pf); + + void + enclave_print(const char *message) + { + ocall_print(message); + } +} + +// In the beginning of Enclave initialization, add: +os_set_print_function(enclave_print); +``` + +Embed WAMR vmcore in Linux SGX +------------------------------ + +Normally we can embed WAMR vmcore in Linux SGX by calling the vmcore exported API's, see [Embed WAMR guide](./embed_wamr.md) for the details. And the the ecall_iwasm_main() function in file Enclave.cpp of enclave-sample also provides sample to invoke wasm app main function with wasm file buffer: + +```cpp +void +ecall_iwasm_main(uint8_t *wasm_file_buf, uint32_t wasm_file_size); +``` + +The enclave-sample also wraps an ecall function to receive commands from App to Enclave, and handle the commands in Enclave by calling the related WAMR vmcore API. The commands and related API's are: + +```cpp +typedef enum EcallCmd { + CMD_INIT_RUNTIME = 0, /* wasm_runtime_init/full_init() */ + CMD_LOAD_MODULE, /* wasm_runtime_load() */ + CMD_INSTANTIATE_MODULE, /* wasm_runtime_instantiate() */ + CMD_LOOKUP_FUNCTION, /* wasm_runtime_lookup_function() */ + CMD_CREATE_EXEC_ENV, /* wasm_runtime_create_exec_env() */ + CMD_CALL_WASM, /* wasm_runtime_call_wasm */ + CMD_EXEC_APP_FUNC, /* wasm_application_execute_func() */ + CMD_EXEC_APP_MAIN, /* wasm_application_execute_main() */ + CMD_GET_EXCEPTION, /* wasm_runtime_get_exception() */ + CMD_DEINSTANTIATE_MODULE, /* wasm_runtime_deinstantiate() */ + CMD_UNLOAD_MODULE, /* wasm_runtime_unload() */ + CMD_DESTROY_RUNTIME, /* wasm_runtime_destroy() */ + CMD_SET_WASI_ARGS, /* wasm_runtime_set_wasi_args() */ + CMD_SET_LOG_LEVEL, /* bh_log_set_verbose_level() */ +}; +``` + +SGX Intel Protected File System +------------------------------- +Intel SGX introduced a feature called [Intel Protection File System Library (IPFS)](https://www.intel.com/content/www/us/en/developer/articles/technical/overview-of-intel-protected-file-system-library-using-software-guard-extensions.html) to create, operate and delete files inside the enclave. +WAMR supports the mapping of IPFS on WASI functions related to file interactions, providing seamless persistence with confidentiality and integrity to the hosted WebAssembly applications in the enclave. + +The usage of SGX IPFS is an optional feature. +To opt-in, the support of IPFS requires the following changes: + - set the flag `WAMR_BUILD_SGX_IPFS=1` when running `cmake`, + - the enclave must be linked with the trusted IPFS library (`-lsgx_tprotected_fs`), + - the application outside of the enclave must be linked with the untrusted IPFS library (`-lsgx_uprotected_fs`), + - the EDL file must include the following import statement: + +```edl +from "sgx_tprotected_fs.edl" import *; +``` + +When using the [enclave-sample](../product-mini/platforms/linux-sgx/enclave-sample/) project, setting the flag `WAMR_BUILD_SGX_IPFS=1` when running `cmake` enables these changes automatically. + + +### Verification of SGX IPFS +One can observe the usage of IPFS by running the [file sample](../samples/file/) WebAssembly application. +Enabling the SGX IPFS on this sample project leads to the generation of an encrypted text file. + + +### Mapping of WASI/POSIX to IPFS +This table summarizes how WASI is mapped to POSIX and IPFS. +Since IPFS is a subset of the WASI/POSIX, emulation is performed to fill the missing implementation. + +| WASI | POSIX | IPFS | +|------------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------| +| `fd_read` | `readv` | `sgx_fread` | +| `fd_write` | `writev` | `sgx_fwrite` | +| `fd_close` | `close` | `sgx_fclose` | +| `path_open` | `openat` | `sgx_fopen` | +| `fd_datasync` | `fsync` | `sgx_fflush` | +| `fd_tell` | `lseek` | `sgx_ftell` | +| `fd_filestat_set_size` | `ftruncate` | Shrinking files is not supported, nor emulated. Extending files is emulated using `sgx_fseek`/`sgx_ftell`/`sgx_fwrite`. | +| `fd_seek` | `lseek` | The POSIX and IPFS behaviors differ. Emulated using `sgx_fseek`/`sgx_ftell`/`sgx_fwrite`. | +| `fd_pwrite` | `pwrite` | Not supported. Emulated using `sgx_fseek`/`sgx_ftell`/`sgx_fwrite`. | +| `fd_pread` | `pread` | Not supported. Emulated using `sgx_fseek`/`sgx_ftell`/`sgx_fread`. | +| `fd_allocate` | `posix_fallocate` | Not supported. Emulated using `sgx_fseek`/`sgx_ftell`/`sgx_fwrite`/`sgx_fflush`. | + + +### Performance overheads +Many benchmarks have assessed the overheads caused by IPFS through WASI functions using Twine, an early and academic adaptation of WAMR in Intel SGX with WASI support. +The results can be found in [this paper](https://arxiv.org/abs/2103.15860). + +### Limitations +The threat model and the limitations of SGX IPFS can be found in [the official documentation](https://www.intel.com/content/dam/develop/external/us/en/documents/overviewofintelprotectedfilesystemlibrary.pdf). + + +Others +------ + +- Please add "-sgx" option when generating AoT file for SGX platform, e.g.: + + ```bash + wamrc -sgx -o test.aot test.wasm + ``` + +- The default max heap size of Enclave is 16 MB, it might be not enough when executing some workloads, please modify it in Enclave/Enclave.config.xml with a larger size when exception was thrown: + + ```bash + Exception: fail to enlarge memory. + or + Exception: allocate memory failed. + ``` + + Enclave/Enclave.config.xml, default max heap size is 16 MB: + + ```xml + 0x1000000 + ``` + diff --git a/doc/memory_tune.md b/doc/memory_tune.md new file mode 100644 index 0000000000..77c7d433f6 --- /dev/null +++ b/doc/memory_tune.md @@ -0,0 +1,35 @@ +# Memory model and memory usage tunning + +References: +- [Blog: Understand WAMR heap](https://bytecodealliance.github.io/wamr.dev/blog/understand-the-wamr-heaps/) +- [Blog: Understand WAMR stacks](https://bytecodealliance.github.io/wamr.dev/blog/understand-the-wamr-stacks/) + +## The memory model + +The memory model of WAMR can be basically described as below: + +
+ +Note: +- **global heap**: the heap to allocate memory for runtime data structures, including wasm module, wasm module instance, exec env, wasm operand stack and so on. It is initialized by `wasm_runtime_init` or `wasm_runtime_full_init`. And for `wasm_runtime_full_init`, developer can specify the memory allocation mode with `RuntimeInitArgs *init_args`: allocate memory from a user defined byte buffer, from user defined allocation function, or from the platform's os_malloc function. Refer to [wasm_export.h](../core/iwasm/include/wasm_export.h#L98-L141) and [Embedding WAMR guideline](./embed_wamr.md#the-runtime-initialization) for more details. And developer can use `wasm_runtime_malloc/wasm_runtime_free` to allocate/free memory from/to the global heap. +- **wasm operand stack**: the stack to store the operands required by wasm bytecodes as WebAssembly is based on a stack machine. If the exec_env is created by developer with `wasm_runtime_create_exec_env`, then its size is specified by `wasm_runtime_create_exec_env`, otherwise if the exec_env is created by runtime internally, e.g. by `wasm_application_execute_main` or `wasm_application_execute_func`, then the size is specified by `wasm_runtime_instantiate`. +- **linear memory**: a contiguous, mutable array of raw bytes. It is created with an initial size but might be grown dynamically. For most compilers, e.g. wasi-sdk, emsdk, rustc or asc, normally it includes three parts, data area, auxiliary stack area and heap area. For wasi-sdk, the initial/max size can be specified with `-Wl,--initial-memory=n1,--max-memory=n2`, for emsdk, the initial/max size can be specified with `-s INITIAL_MEMORY=n1 -s MAXIMUM_MEMORY=n2 -s ALLOW_MEMORY_GROWTH=1` or `-s TOTAL_MEMORY=n`, and for asc, they can be specified with `--initialMemory` and `--maximumMemory` flags. + - If the memory access boundary check with hardware trap feature is enabled, e.g. in Linux/MacOS/Windows x86-64 by default, the linear memory is allocated by `os_mmap` from virtual address space instead of global heap. +- **aux stack**: the auxiliary stack resides in linear memory to store some temporary data when calling wasm functions, for example, calling a wasm function with complex struct arguments. For wasi-sdk, the size can be specified with `-z stack-size=n`, for emsdk, the size can be specified with `-s TOTAL_STACK=n`. +- **app heap and libc heap**: the heap to allocate memory for wasm app, note that app heap is created only when the malloc/free functions (or __new/__release functions for AssemblyScript) are not exported and runtime can not detect the libc heap. To export the malloc/free functions, for wasi-sdk and emsdk, developer can use `-Wl,--export=malloc -Wl,--export=free` options, for asc, developer can use `--exportRuntime` option. For app heap, the size is specified by `wasm_runtime_instantiate`. It is recommended to export the malloc/free functions and disable app heap. However, if you are using [the old pthread implementation](./pthread_impls.md), you might need some workaround to avoid the libc heap as mentioned in [WAMR pthread library](./pthread_library.md). And developer can use `wasm_runtime_module_malloc/wasm_runtime_module_free` to allocate/free memory from/to app heap (or libc heap if malloc/free functions are exported). +- **__data_end global and __heap_base global**: two globals exported by wasm application to indicate the end of data area and the base address of libc heap. For WAMR, it is recommended to export them as when there are no possible memory grow operations, runtime will truncate the linear memory into the size indicated by `__heap_base`, so as to reduce the footprint, or at least one page (64KB) is required by linear memory. + +## Tune the memory usage + +Normally there are some methods to tune the memory usage: +- set the global heap size with `wasm_runtime_full_init` +- set the wasm operand stack size with `wasm_runtime_create_exec_env` or `wasm_runtime_instantiate` +- set the linear memory size +- set the auxiliary stack size +- export `malloc/free` functions to use libc heap and disable app heap +- set the app heap size with `wasm_runtime_instantiate` +- use `nostdlib` mode, add `-Wl,--strip-all`: refer to [How to reduce the footprint](./build_wasm_app.md#2-how-to-reduce-the-footprint) of building wasm app for more details +- use XIP mode, refer to [WAMR XIP (Execution In Place) feature introduction](./xip.md) for more details +- when using the Wasm C API in fast interpreter or AOT mode, set `clone_wasm_binary=false` in `LoadArgs` and free the wasm binary buffer (with `wasm_byte_vec_delete`) after module loading; `wasm_module_is_underlying_binary_freeable` can be queried to check if the wasm binary buffer can be safely freed (see [the example](../samples/basic/src/free_buffer_early.c)); after the buffer is freed, `wasm_runtime_get_custom_section` cannot be called anymore +- when using the wasm/AOT loader in fast interpreter or AOT mode, set `wasm_binary_freeable=true` in `LoadArgs` and free the wasm binary buffer (with `wasm_byte_vec_delete`) after module loading; `wasm_runtime_is_underlying_binary_freeable` can be queried to check if the wasm binary buffer can be safely freed; after the buffer is freed, `wasm_runtime_get_custom_section` cannot be called anymore +- `WAMR_BUILD_SHRUNK_MEMORY` can be used to reduce the memory usage of WAMR, but it might affect the standard expected behavior of WAMR. \ No newline at end of file diff --git a/doc/memory_usage.md b/doc/memory_usage.md new file mode 100644 index 0000000000..cc3ba2ea6f --- /dev/null +++ b/doc/memory_usage.md @@ -0,0 +1,129 @@ +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 effectively +means unlimited. + +The linear memory is allocated via `os_mmap` and `os_mem_commit`/`os_mprotect`. + +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/multi_module.md b/doc/multi_module.md new file mode 100644 index 0000000000..ac9e3bbde4 --- /dev/null +++ b/doc/multi_module.md @@ -0,0 +1,160 @@ +# Multiple Modules as Dependencies + +A WASM module can _import_ _functions_, _globals_, _memories_ and _tables_ from other modules as dependencies. A module can also _export_ those entities for other modules like a library. + +WAMR loads all dependencies recursively according to the _import section_ of a module. + +> WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details. + +WAMR follows [WASI Command/Reactor Model](https://github.com/WebAssembly/WASI/blob/main/legacy/application-abi.md#current-unstable-abi). The WASI model separates modules into commands and reactors. A Command is the main module that requires exports of reactors(submodules). + +if `WASM_ENABLE_LIBC_WASI` is enabled, any module imports a WASI APIs, like `(import "wasi_snapshot_preview1" "XXX")`, should follow restrictions of the _WASI application ABI_: + +- a main module(a command) should include `_start()` +- a submodule(a reactor) should include `_initialize()` +- both a command and a reactor should include an exported `memory` + +## Multi-Module Related APIs + +### Register a module + +```c +bool +wasm_runtime_register_module(const char *module_name, + wasm_module_t module, + char *error_buf, + uint32_t error_buf_size); +``` + +It is used to register a _module_ with a _module_name_ to WASM runtime, especially for the main module, which is loaded by `wasm_runtime_load()` and doesn't have a chance to tell runtime its _module name_. + +WAMR will get submodules' names(according to the _import section_ of the main module) and load .wasm files from the filesystem or stream and then register them internally. + +### Find a registered module + +```c +wasm_module_t +wasm_runtime_find_module_registered( + const char *module_name); +``` + +It is used to check whether a module with a given _module_name_ has been registered before or not. Return the module if yes. + +### Module reader and destroyer + +```c +typedef bool (*module_reader)(const char *module_name, + uint8_t **p_buffer, + uint32_t *p_size); + +typedef void (*module_destroyer)(uint8_t *buffer, + uint32_t size); + +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); +``` + +WAMR hopes that the native host or embedding environment loads/unloads the module WASM files by themselves and only passes runtime the binary content without worrying about filesystem or storage issues. `module_reader` and `module_destroyer` are two callbacks called when dynamic-loading/unloading submodules. Developers must implement the two callbacks by themselves. + +### Call function of a submodule + +```c +wasm_function_inst_t +wasm_runtime_lookup_function(wasm_module_inst_t const module_inst, + const char *name); +``` + +Multi-module allows one to look up an exported function of a submodule. There are two ways to indicate the function _name_: + +- parent function name only by default, used to look up the function of the parent module +- submodule name, function name and two $ symbols, e.g. `$submodule_name$function_name`, used to lookup function of submodule +- `signature` can be NULL + +## Example + +### Attributes in C/C++ + +Suppose there are three C files, _mA.c_, _mB.c_ and _mC.c_. Each of them exports functions and imports from others except mA. + +import/export with two kinds of `__attribute__`: + +- `__attribute__((import_module("MODULE_NAME"))) __attribute__((import_name("FUNCTION_NAME")))`. to indicate dependencies of the current module. + +- `__attribute__((export_name("FUNCTION_NAME")))`. to expose functions. + +```C +// mA.c +__attribute__((export_name("A1"))) int +A1() +{ + return 11; +} +``` + +```C +// mB.c +__attribute__((import_module("mA"))) +__attribute__((import_name("A1"))) extern int +A1(); + +__attribute__((export_name("B1"))) int +B1() +{ + return 21; +} +``` + +### Compile Options + +to generate a wasm module as a command + +``` +$ /path/to/wasi-sdk/bin/clang -o command.wasm main_module.c +``` + +to generate a wasm module as a reactor + +``` +$ /path/to/wasi-sdk/bin/clang -mexec-model=reactor -o reactor.wasm submodule.c +``` + +In the above case, _mA_ and _mB_ are reactors(submodules), _mC_ is the command(main module). Their _import relationships_ will be like: + +![import relationships](./pics/multi_module_pic1.png) + +### libvmlib + +We need to enable _WAMR_BUILD_MULTI_MODULE_ option when building WAMR vmlib. Please ref to [Build WAMR core](./build_wamr.md) for a thoughtful guide. + +### code + +After all the preparation, we can call some functions from native code with APIs + +First, create two callbacks to load WASM module files into memory and unload them later + +```c +static bool +module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size) +{ + // ... + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size); + // ... +} + +static void +module_destroyer_cb(uint8 *buffer, uint32 size) +{ + BH_FREE(buffer); +} +``` + +Second, create a large buffer and tell WAMR malloc any resource only from this buffer later. + +More details + +```c +static char sandbox_memory_space[10 * 1024 * 1024] = { 0 }; +``` + +Third, put all together. Please refer to [main.c](../samples/multi-module/src/main.c) diff --git a/doc/other_wasm_compilers.md b/doc/other_wasm_compilers.md index ac1ac175fc..1ab5901fa6 100644 --- a/doc/other_wasm_compilers.md +++ b/doc/other_wasm_compilers.md @@ -1,5 +1,4 @@ - ## Use clang compiler The recommended method to build a WASM binary is to use clang compiler ```clang-8```. You can refer to [apt.llvm.org](https://apt.llvm.org) for the detailed instructions. Here are referenced steps to install clang-8 in Ubuntu 16.04 and Ubuntu 18.04. @@ -61,45 +60,9 @@ clang-8 --target=wasm32 -O3 \ You will get ```test.wasm``` which is the WASM app binary. - - - - -## Use Emscripten tool - -The last method to build a WASM binary is to use Emscripten tool ```emcc```. -Assuming you are using Linux, you may install emcc from Emscripten EMSDK following the steps below: - -``` -git clone https://github.com/emscripten-core/emsdk.git -cd emsdk -./emsdk install latest-fastcomp -./emsdk activate latest-fastcomp -``` - -The Emscripten website provides other installation methods beyond Linux. - -Use the emcc command below to build the WASM C source code into the WASM binary. - -``` Bash -cd emsdk -source emsdk_env.sh (or add it to ~/.bashrc if you don't want to run it each time) -cd
-EMCC_ONLY_FORCED_STDLIBS=1 emcc -g -O3 -s WASM=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ - -s TOTAL_MEMORY=65536 -s TOTAL_STACK=4096 \ - -s ASSERTIONS=1 -s STACK_OVERFLOW_CHECK=2 \ - -s "EXPORTED_FUNCTIONS=['_main']" -o test.wasm test.c -``` - -You will get ```test.wasm``` which is the WASM app binary. - - - - - ## Using Docker -Another method availble is using [Docker](https://www.docker.com/). We assume you've already configured Docker (see Platform section above) and have a running interactive shell. Currently the Dockerfile only supports compiling apps with clang, with Emscripten planned for the future. +Another method available is using [Docker](https://www.docker.com/). We assume you've already configured Docker (see Platform section above) and have a running interactive shell. Currently the Dockerfile only supports compiling apps with clang, with Emscripten planned for the future. Use the clang-8 command below to build the WASM C source code into the WASM binary. diff --git a/doc/perf_tune.md b/doc/perf_tune.md new file mode 100644 index 0000000000..9876ed3de5 --- /dev/null +++ b/doc/perf_tune.md @@ -0,0 +1,308 @@ +# Tune the performance of running wasm/aot file + +Normally there are some methods to tune the performance: + +## 1. Use `wasm-opt` tool + +Download the [binaryen release](https://github.com/WebAssembly/binaryen/releases), and use the `wasm-opt` tool in it to optimize the wasm file, for example: + +```bash +wasm-opt -O4 -o test_opt.wasm test.wasm +``` + +## 2. Enable `simd128` option when compiling wasm source files + +WebAssembly [128-bit SIMD](https://github.com/WebAssembly/simd) is supported by WAMR on x86-64 and aarch64 targets, enabling it when compiling wasm source files may greatly improve the performance. For [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) and [emsdk](https://github.com/emscripten-core/emsdk), please add `-msimd128` flag for `clang` and `emcc/em++`: + +```bash +/opt/wasi-sdk/bin/clang -msimd128 -O3 -o + +emcc -msimd128 -O3 -o +``` + +## 3. Enable segue optimization for wamrc when generating the aot file + +[Segue](https://plas2022.github.io/files/pdf/SegueColorGuard.pdf) is an optimization technology which uses x86 segment register to store the WebAssembly linear memory base address, so as to remove most of the cost of SFI (Software-based Fault Isolation) base addition and free up a general purpose register, by this way it may: + +- Improve the performance of JIT/AOT +- Reduce the footprint of JIT/AOT, the JIT/AOT code generated is smaller +- Reduce the compilation time of JIT/AOT + +Currently it is only supported on linux x86-64, developer can use `--enable-segue=[]` for wamrc: + +```bash +wamrc --enable-segue -o aot_file wasm_file +# or +wamrc --enable-segue=[] -o aot_file wasm_file +``` + +`flags` can be: i32.load, i64.load, f32.load, f64.load, v128.load, i32.store, i64.store, f32.store, f64.store and v128.store, use comma to separate them, e.g. `--enable-segue=i32.load,i64.store`, and `--enable-segue` means all flags are added. + +> Note: Normally for most cases, using `--enable-segue` is enough, but for some cases, using `--enable-segue=` may be better, for example for CoreMark benchmark, `--enable-segue=i32.store` may lead to better performance than `--enable-segue`. + +## 4. Enable segue optimization for iwasm when running wasm file + +Similar to segue optimization for wamrc, run: + +```bash +iwasm --enable-segue wasm_file (iwasm is built with llvm-jit enabled) +# or +iwasm --enable-segue=[] wasm_file +``` + +> Note: Currently it is only supported on linux x86-64. + +## 5. Use the AOT static PGO method + +LLVM PGO (Profile-Guided Optimization) allows the compiler to better optimize code for how it actually runs. WAMR supports AOT static PGO, currently it is tested on Linux x86-64 and x86-32. The basic steps are: + +1. Use `wamrc --enable-llvm-pgo -o ` to generate an instrumented aot file. + +2. Compile iwasm with `cmake -DWAMR_BUILD_STATIC_PGO=1` and run `iwasm --gen-prof-file= ` to generate the raw profile file. + +> Note: Directly dumping raw profile data to file system may be unsupported in some environments, developer can dump the profile data into memory buffer instead and try outputting it through network (e.g. uart or socket): + +```C +uint32_t +wasm_runtime_get_pgo_prof_data_size(wasm_module_inst_t module_inst); + +uint32_t +wasm_runtime_dump_pgo_prof_data_to_buf(wasm_module_inst_t module_inst, char *buf, uint32_t len); +``` + +3. Install or compile `llvm-profdata` tool,refer to [here](../tests/benchmarks/README.md#install-llvm-profdata) for the details. + +4. Run `llvm-profdata merge -output= ` to merge the raw profile file into the profile file. + +5. Run `wamrc --use-prof-file= -o ` to generate the optimized aot file. + +6. Run the optimized aot_file: `iwasm `. + +Developer can refer to the `test_pgo.sh` files under each benchmark folder for more details, e.g. [test_pgo.sh](../tests/benchmarks/coremark/test_pgo.sh) of CoreMark benchmark. + +## 6. Disable the memory boundary check + +Please notice that this method is not a general solution since it may lead to security issues. And only boost the performance for some platforms in AOT mode and don't support hardware trap for memory boundary check. + +1. Build WAMR with `-DWAMR_CONFIGURABLE_BOUNDS_CHECKS=1` option. + +2. Compile AOT module by wamrc with `--bounds-check=0` option. + +3. Run the AOT module by iwasm with `--disable-bounds-checks` option. + +> Note: The size of AOT file will be much smaller than the default, and some tricks are possible such as let the wasm application access the memory of host os directly. +> Please notice that if this option is enabled, the wasm spec test will fail since it requires the memory boundary check. For example, the runtime will crash when accessing the memory out of the boundary in some cases instead of throwing an exception as the spec requires. + +You should only use this method for well tested wasm applications and make sure the memory access is safe. + +## 7. Use linux-perf + +Linux perf is a powerful tool to analyze the performance of a program, developer can use it to find the hot functions and optimize them. It is one profiler supported by WAMR. In order to use it, you need to add `--perf-profile` while running _iwasm_. By default, it is disabled. + +> [!CAUTION] +> For now, only llvm-jit mode and aot mode supports linux-perf. + +Here is a basic example, if there is a Wasm application _foo.wasm_, you'll execute. + +``` +$ perf record --output=perf.data.raw -- iwasm --enable-linux-perf foo.wasm +``` + +This will create a _perf.data_ and +- a _jit-xxx.dump_ under _~/.debug/jit/_ folder if running llvm-jit mode +- or _/tmp/perf-.map_ if running AOT mode + + +This file is WAMR generated. It contains information which includes jitted(precompiled) code addresses in memory, names of jitted (precompiled) functions which are named as *aot_func#N* and so on. + +If running with llvm-jit mode, the next thing is to merge _jit-xxx.dump_ file into the _perf.data_. + +``` +$ perf inject --jit --input=perf.data.raw --output=perf.data +``` + +This step will create a lot of _jitted-xxxx-N.so_ which are ELF images for all JIT functions created at runtime. + +> [!TIP] +> add `-v` and check if there is output likes _write ELF image ..._. If yes, it means above merge is successful. + +Finally, you can use _perf report_ to analyze the performance. + +``` +$ perf report --input=perf.data +``` + +> [!CAUTION] +> Using release builds of llvm and iwasm will produce "[unknown]" functions in the call graph. It is not only because +> of the missing debug information, but also because of removing frame pointers. To get the complete result, please +> use debug builds of both llvm and iwasm. +> +> Wasm functions names are stored in _the custom name section_. Toolchains always generate the custom name section in both debug and release builds. However, the custom name section is stripped to pursue smallest size in release build. So, if you want to get a understandable result, please search the manual of toolchain to look for a way to keep the custom name section. +> +> For example, with EMCC, you can add `-g2`. +> +> If not able to get the context of the custom name section, WAMR will use `aot_func#N` to represent the function name. `N` is from 0. `aot_func#0` represents the first _not imported wasm function_. + +### 7.1 Flamegraph + +[Flamegraph](https://www.brendangregg.com/flamegraphs.html) is a powerful tool to visualize stack traces of profiled software so that the most frequent code-paths can be identified quickly and accurately. In order to use it, you need to [capture graphs](https://github.com/brendangregg/FlameGraph#1-capture-stacks) when running `perf record` + +``` +$ perf record -k mono --call-graph=fp --output=perf.data.raw -- iwasm --enable-linux-perf foo.wasm +``` + +If running with llvm-jit mode, merge the _jit-xxx.dump_ file into the _perf.data.raw_. + +``` +$ perf inject --jit --input=perf.data.raw --output=perf.data +``` + +Generate the stack trace file. + +``` +$ perf script > out.perf +``` + +[Fold stacks](https://github.com/brendangregg/FlameGraph#2-fold-stacks). + +``` +$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded +``` + +[Render a flamegraph](https://github.com/brendangregg/FlameGraph#3-flamegraphpl) + +``` +$ ./FlameGraph/flamegraph.pl out.folded > perf.foo.wasm.svg +``` + +> [!TIP] +> use `grep` to pick up folded stacks you are interested in or filter out something. +> +> For example, if just want to see stacks of wasm functions, you can use +> +> ```bash +> # only jitted functions +> $ grep "wasm_runtime_invoke_native" out.folded | ./FlameGraph/flamegraph.pl > perf.foo.wasm.only.svg +> ``` + +> [!TIP] +> use [trans_wasm_func_name.py](../test-tools/trans-jitted-func-name/trans_wasm_func_name.py) to translate jitted function +> names to its original wasm function names. It requires _wasm-objdump_ in _wabt_ and _name section_ in the .wasm file +> +> The input file is the output of `./FlameGraph/stackcollapse-perf.pl`. +> +> ```bash +> python trans_wasm_func_name.py --wabt_home --folded out.folded <.wasm> +> ``` +> +> Then you will see a new file named _out.folded.translated_ which contains the translated folded stacks. +> All wasm functions are translated to its original names with a prefix like "[Wasm]" + +## 8. Refine the calling processes between host native and wasm application + +In some scenarios, there may be lots of callings between host native and wasm application, e.g. frequent callings to AOT/JIT functions from host native or frequent callings to host native from AOT/JIT functions. It is important to refine these calling processes to speedup them, WAMR provides several methods: + +### 8.1 Refine callings to native APIs registered by `wasm_runtime_register_natives` from AOT code + +When wamrc compiles the wasm file to AOT code, it may generate LLVM IR to call the native API from an AOT function, and if it doesn't know the native API's signature, the generated LLVM IR has to call the runtime API `aot_invoke_native` to invoke the native API, which is a relatively slow way. If developer registers native APIs during execution by calling `wasm_runtime_register_natives` or by `iwasm --native-lib=`, then developer can also register native APIs with the same signatures to the AOT compiler by `wamrc --native-lib=`, so as to let the AOT compiler pre-know the native API's signature, and generate optimized LLVM IR to quickly call to the native API. + +The below sample registers an API `int test_add(int, int)` to the AOT compiler: + +```C +/* test_add.c */ + +#include "wasm_export.h" + +static int +test_add_wrapper(wasm_exec_env_t exec_env, int x, int y) { + return 0; /* empty function is enough */ +} + +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_add, "(ii)i") +}; + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} +``` +```bash +# build native lib +gcc -O3 -fPIC -shared -I /core/iwasm/include -o libtest_add.so test_add.c +# register native lib to aot compiler +wamrc --native-lib=./libtest_add.so -o +``` + +> Note: no need to do anything for LLVM JIT since the native APIs must have been registered before execution and JIT compiler already knows the native APIs' signatures. + +### 8.2 Refine callings to native APIs registered by wasm-c-api `wasm_instance_new` from AOT code + +In wasm-c-api mode, when the native APIs are registered by `wasm_instance_new(..., imports, ...)`, developer can use `wamrc --invoke-c-api-import` option to generate the AOT file, which treats the unknown import function as wasm-c-api import function and generates optimized LLVM IR to speedup the calling process. + +> Note: no need to do anything for LLVM JIT since the similar flag has been set to JIT compiler in wasm-c-api `wasm_engine_new` when LLVM JIT is enabled. + +### 8.3 Refine callings to AOT/JIT functions from host native + +Currently by default WAMR runtime has registered many quick AOT/JIT entries to speedup the calling processes to call AOT/JIT functions from host native, as long as developer doesn't disable it by using `cmake -DWAMR_BUILD_QUICK_AOT_ENTRY=0` or setting the compiler macro `WASM_ENABLE_QUICK_AOT_ENTRY` to 0 in the makefile. These quick AOT/JIT entries include: + +1. wasm function contains 0 to 4 arguments and 0 to 1 results, with the type of each argument is i32 or i64 and the type of result is i32, i64 or void. These functions are like: + +```C +// no argument +i32 foo(), i64 foo(), void foo() +// one argument, each argument is i32 or i64 +i32 foo(i32/i64), i64 foo(i32/i64), void(i32/i64) +// two arguments, each argument is i32 or i64 +i32 foo(i32/i64, i32/i64), i64 foo(i32/i64, i32/i64), void(i32/i64, i32/i64) +// three arguments, each argument is i32 or i64 +i32 foo(i32/i64, i32/i64, i32/i64), i64 foo(i32/i64, i32/i64, i32/i64), void(i32/i64, i32/i64, i32/i64) +// four arguments, each argument is i32 or i64 +i32 foo(i32/i64, i32/i64, i32/i64, i32/i64) +i64 foo(i32/i64, i32/i64, i32/i64, i32/i64) +void(i32/i64, i32/i64, i32/i64, i32/i64) +``` + +2. wasm function contains 5 arguments and 0 to 1 results, with the type of each argument is i32 and the type of result is i32, i64 or void. These functions are like: + +```C +i32 foo(i32, i32, i32, i32, i32) +i64 foo(i32, i32, i32, i32, i32) +void foo(i32, i32, i32, i32, i32) +``` + +To speedup the calling processes, developer had better ensure that the signatures of the wasm functions to expose are like above, or add some conversions to achieve it. For example, if a wasm function to call is `f32 foo(f32)`, developer can define a new function `i32 foo1(i32)` like below and export it: +```C +int32 foo1(int32 arg_i32) +{ + float arg_f32 = *(float *)&arg_i32; + float res_f32 = foo(f32); + int32 res_i32 = *(int32 *)&res_i32; + return res_i32; +} +``` +And in the host embedder: +``` + uint32 argv[2]; + float arg_f32 = ...; /* argument to foo */ + float res_f32; + bool ret; + + argv[0] = *(uint32 *)&arg_f32; + func = wasm_runtime_lookup_function(module_inst, "foo1"); + ret = wasm_runtime_call_wasm(exec_env, func, 1, argv); + if (!ret) { + /* handle exception */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } + else { + /* the return value is stored in argv[0] */ + res_f32 = *(float *)&argv[0]; + } +``` diff --git a/doc/pics/app_framework.PNG b/doc/pics/app_framework.PNG new file mode 100644 index 0000000000..802c45ba27 Binary files /dev/null and b/doc/pics/app_framework.PNG differ diff --git a/doc/pics/multi_module_pic1.png b/doc/pics/multi_module_pic1.png new file mode 100644 index 0000000000..f0c8e33a37 Binary files /dev/null and b/doc/pics/multi_module_pic1.png differ diff --git a/doc/pics/sensor_callflow.PNG b/doc/pics/sensor_callflow.PNG index ff471da0bf..12839aa5a7 100644 Binary files a/doc/pics/sensor_callflow.PNG and b/doc/pics/sensor_callflow.PNG differ diff --git a/doc/pics/wamr_memory_model.png b/doc/pics/wamr_memory_model.png new file mode 100644 index 0000000000..d01d378202 Binary files /dev/null and b/doc/pics/wamr_memory_model.png differ diff --git a/doc/pics/wamr_menu_config.png b/doc/pics/wamr_menu_config.png index 82762aa3b0..c85409584e 100644 Binary files a/doc/pics/wamr_menu_config.png and b/doc/pics/wamr_menu_config.png differ diff --git a/doc/pics/workflow.PNG b/doc/pics/workflow.PNG deleted file mode 100644 index 2bba240ff5..0000000000 Binary files a/doc/pics/workflow.PNG and /dev/null differ diff --git a/doc/port_wamr.md b/doc/port_wamr.md index ed7d4af004..7899ff3186 100644 --- a/doc/port_wamr.md +++ b/doc/port_wamr.md @@ -3,46 +3,73 @@ WAMR porting guide ========================= -This document describes how to port WAMR to a new platform "**super-os**" +This document describes how to port WAMR to a new platform "**new-os**" -# Step 1: Create folders for the new platform +# Step 1: Implement platform API layer ------------------------- -Create folders: -- **core/shared/platform/super-os**: for platform API layer implementations -- **product-mini/platforms/super-os**: for the platform mini product build +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) + +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: + + - ```cmake + add_definitions(-DBH_PLATFORM_YOUR_NAME) + ``` + +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 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 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`. -# Step 2: Implement platform API layer -------------------------- -Implement folder core/shared/platform/super-os. Normally in this folder you should implement the following files: -- bh_platform.h and bh_platform.c: define the platform related macros, data types and APIs. -- bh_assert.c: implement function bh_assert_internal() and bh_debug_internal(). -- bh_definition.c: implement function b_memcpy_s, b_strcat_s and b_strcpy_s. And implement fopen_s - if we need to read wasm file from file system. -- bh_platform_log.c: implement function bh_log_emit, bh_fprintf and bh_fflush. -- bh_time.c: implement several time related functions. -- bh_thread.c: implement thread, mutex, condition related functions. -- bh_math.c: implement some math functions if the platform doesn't support them, e.g. sqrt, - fabs and isnan. We may use the open source fdlibm implementation, for example, - ref to platform/zephyr/bh_math.c. - -Please ref to implementation of other platform for more details, e.g. platform/zephyr, platform/linux. - -# Step 3: Create the mini product build for the platform + +# Step 2: Create the mini product for the platform ------------------------- -Implement folder product-mini/platforms/super-os. Normally this folder is to implement the C main function, and generate a WAMR VM core binary named iwasm which can load and run wasm apps. We should implement following files: -- main.c: implement the C main function, which reads wasm file to buffer, loads the wasm file to wasm module, instantiate the module, lookup wasm app main function, and then execute the function. -- ext_lib_export.c: implement the native APIs if you want, and if no native API is to be implemented, just keep array extended_native_symbol_defs empty. -- CMakeLists.txt: there are some settings which can be passed from cmake variables: - - set (WAMR_BUILD_PLATFORM "platform_name"): set the name of the platform - - set (WAMR_BUILD_TARGET ): set the build target, currently the value supported: X86_64, X86_32, ARM[sub], THUMB[sub], MIPS and XTENSA. For ARM and THUMB, you can specify the sub version, e.g. ARMV4, ARMV7, THUMBV4T, THUMBV7T. - - set (WAMR_BUILD_INTERP 1 or 0): whether to interpreter or not - - set (WAMR_BUILD_AOT 1 or 0): whether to build AOT or not - - set (WAMR_BUILD_JIT 1 or 0): whether to build JIT or not - - set (WAMR_BUILD_LIBC_BUILTIN 1 or 0): whether to build Libc builtin or not - - set (WAMR_BUILD_LIBC_WASI 1 or 0): whether to build Libc WASI or not +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. + + + +Firstly create folder **product-mini/platforms/new-os** for the platform mini product build, then refer to the linux platform mini-product for creating the CMakeList.txt and the C implementations. + + + +You should set cmake variable `WAMR_BUILD_PLATFORM` to your platform name while building the mini product. It can be done in the mini product CMakeList.txt file, or pass arguments to cmake command line like: + +``` +mkdir build +cd build +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/doc/pthread_impls.md b/doc/pthread_impls.md new file mode 100644 index 0000000000..92b51cb4a7 --- /dev/null +++ b/doc/pthread_impls.md @@ -0,0 +1,59 @@ +# Pthread implementations + +WAMR has two pthread implementations available as of writing this. + +These implementations are not ABI-compatible. You at least need to rebuild +your wasm modules when migrating from one pthread implementation to another. + +For new users, we recommend to use (or at least experiment) +the new wasi-threads based implementation. +In future, we might remove the old implementation. + +## WAMR lib-pthread (old) + + * The pthread API is directly implemented as host functions in WAMR. + (`WAMR_BUILD_LIB_PTHREAD`) + + * Only minimum API is implemented as of writing this. + (eg. no pthread barriers) + + * WAMR-specific ABI + + * [Known limitations](pthread_library.md#known-limits) + +## wasi-threads (new) + + * The pthread API is implemented in wasi-libc, based on + [wasi-threads](https://github.com/WebAssembly/wasi-threads) + and [WASM threads](https://github.com/WebAssembly/threads) proposals. + + * It requires a recent-enough version of wasi-libc. The experimental support + is included in + [wasi-sdk 20.0](https://github.com/WebAssembly/wasi-sdk/releases/tag/wasi-sdk-20) + or later. + To build your application, cmake users can use the + [cmake toolchain file](https://github.com/WebAssembly/wasi-sdk/blob/main/wasi-sdk-pthread.cmake) + provided by wasi-sdk. + + * wasi-threads is implemented as a host function in WAMR. + (`WAMR_BUILD_LIB_WASI_THREADS`) + + * The ABI is specified in wasi-threads proposal. + You can run the same wasm modules on other runtimes which implement + the proposal. (wasmtime, toywasm, ...) + + * Basically more feature-rich and complete than WAMR lib-pthread. + + **EXCEPTION**: `pthread_exit` is not available as of writing this. + If `pthread_exit` is important for your use cases, please speak up in + the [GitHub issue](https://github.com/WebAssembly/wasi-threads/issues/7). + + **EXCEPTION**: For threads created by `pthread_create`, the AUX stack + (aka C shadow stack) overflow detection mechanism is disabled as of + writing this. + If it's important for your use cases, please speak up in the + [GitHub issue](https://github.com/WebAssembly/wasi-threads/issues/12). + +# References + +* https://github.com/bytecodealliance/wasm-micro-runtime/issues/1790 diff --git a/doc/pthread_library.md b/doc/pthread_library.md new file mode 100644 index 0000000000..602f16d53f --- /dev/null +++ b/doc/pthread_library.md @@ -0,0 +1,198 @@ +# WAMR pthread library + +**Note**: This document describes the old pthread implementation. +See [Pthread implementations](pthread_impls.md). + +WAMR provides a built-in library to support pthread APIs. You can call pthread APIs in your application source code. + +## Build and run +Suppose you have written a C program calling pthread_create() to create a thread, and the file name is main.c +``` C +#include +#include + +void *thread_routine(void *arg) +{ + printf("Enter thread\n"); + pthread_exit(NULL); + return NULL; +} + +int main(int argc, char** argv) +{ + pthread_t tid; + + if (0 != pthread_create(&tid, NULL, thread_routine, NULL)) { + printf("Failed to create thread\n"); + } + + if (0 != pthread_join(tid, NULL)) { + printf("Failed to join thread %d.\n", tid); + } + + printf("Exit\n"); + + return 0; +} +``` +**Build with libc-builtin** + +To build this C program into WebAssembly app with libc-builtin, you can use this command: +``` bash +/opt/wasi-sdk/bin/clang --target=wasm32 \ + --sysroot=${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot \ + -O3 -pthread -nostdlib -z stack-size=32768 \ + -Wl,--shared-memory \ + -Wl,--initial-memory=131072,--max-memory=131072 \ + -Wl,--allow-undefined-file=${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--no-entry -Wl,--export=main \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + main.c -o test.wasm +# -pthread: it will enable some dependent WebAssembly features for thread +# -nostdlib: disable the WASI standard library as we are using WAMR builtin-libc +# -z stack-size=: specify the total aux stack size +# -Wl,--export=__heap_base,--export=__data_end: export these globals so the runtime can resolve the total aux stack size and the start offset of the stack top +# -Wl,--export=__wasm_call_ctors: export the init function to initialize the passive data segments +``` + +**Build with libc-WASI** + +You can also build this program with WASI, but we need to make some changes to wasi-sysroot: + +1. disable malloc/free of wasi, as they are not atomic operations: + ``` bash + /opt/wasi-sdk/bin/llvm-ar -d /opt/wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi/libc.a dlmalloc.o + ``` +2. copy the pthread.h to wasi-sysroot so the compiler can find it: + ``` bash + cp ${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h /opt/wasi-sdk/share/wasi-sysroot/include + ``` +> Note:
+>1. Remember to back up the original sysroot files + +Then build the program with this command: +``` bash +/opt/wasi-sdk/bin/clang -pthread -O3 \ + -Wl,--shared-memory,--max-memory=196608 \ + -Wl,--allow-undefined,--no-check-features \ + -Wl,--export=__heap_base,--export=__data_end \ + main.c -o test.wasm +# -Wl,--no-check-features: the errno.o in wasi-sysroot is not compatible with pthread feature, pass this option to avoid errors +``` + +**Build with EMCC** + +> Note: This document is based on `emcc 2.0.26`, other version may not work with these commands + +EMCC's `-pthread` option is not compatible with standalone mode, we need to pass `-mbulk-memory -matomics` to the compiler and `--shared-memory,--no-check-features` to linker manually + +EMCC provides some empty implementation for pthread related APIs, we need to remove them from emcc's libc. +``` bash +cd ${emsdk_dir}/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten +emar d libc.a library_pthread_stub.o +emranlib libc.a +``` + +``` bash +emcc -O3 -mbulk-memory -matomics -s MALLOC="none" \ + -Wl,--export=__data_end,--export=__heap_base \ + -Wl,--shared-memory,--no-check-features \ + -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ + main.c -o test.wasm +``` + +**Build AOT module** + +You can build the wasm module into AOT module with pthread support, please pass option `--enable-multi-thread` to wamrc: +``` bash +wamrc --enable-multi-thread -o test.aot test.wasm +``` + +Currently WAMR disables pthread library by default. To run the module with pthread support, please build the runtime with `-DWAMR_BUILD_LIB_PTHREAD=1` +``` bash +cd ${WAMR_ROOT}/product-mini/platforms/linux +mkdir build && cd build +cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 +make +# Then you can run the wasm module above: +./iwasm test.wasm +# Or the AOT module: +# ./iwasm test.aot +``` + +[Here](../samples/multi-thread) is also a sample to show how wasm-apps use pthread APIs to create threads, and how to build it with cmake. You can build this sample and have a try: +``` bash +cd ${WAMR_ROOT}/samples/multi-thread +mkdir build && cd build +cmake .. +make +# Run wasm application +./iwasm wasm-apps/test.wasm +``` + + +## Aux stack separation +The compiler may use some spaces in the linear memory as an auxiliary stack. When pthread is enabled, every thread should have its own aux stack space, so the total aux stack space reserved by the compiler will be divided into N + 1 parts, where N is the maximum number of threads that can be created by the user code. + +The default value of N is 4, which means you can create 4 threads at most. This value can be changed by an option if you are using product-mini: +``` bash +./iwasm --max-threads=n test.wasm +``` +If you are going to develop your own runtime product, you can use the API `wasm_runtime_set_max_thread_num` or init arg `init_args.max_thread_num` to set the value, or you can change the macro `CLUSTER_MAX_THREAD_NUM` in [config.h](../core/config.h). + +> Note: the total size of aux stack reserved by compiler can be set with `-z stack-size` option during compilation. If you need to create more threads, please set a larger value, otherwise it is easy to cause aux stack overflow. + +## Supported APIs +``` C +/* Thread APIs */ +int pthread_create(pthread_t *thread, const void *attr, + void *(*start_routine) (void *), void *arg); + +int pthread_join(pthread_t thread, void **retval); + +int pthread_detach(pthread_t thread); + +int pthread_cancel(pthread_t thread); + +pthread_t pthread_self(void); + +void pthread_exit(void *retval); + +/* Mutex APIs */ +int pthread_mutex_init(pthread_mutex_t *mutex, const void *attr); + +int pthread_mutex_lock(pthread_mutex_t *mutex); + +int pthread_mutex_unlock(pthread_mutex_t *mutex); + +int pthread_mutex_destroy(pthread_mutex_t *mutex); + +/* Cond APIs */ +int pthread_cond_init(pthread_cond_t *cond, const void *attr); + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + unsigned int useconds); + +int pthread_cond_signal(pthread_cond_t *cond); + +int pthread_cond_broadcast(pthread_cond_t *cond); + +int pthread_cond_destroy(pthread_cond_t *cond); + +/* Pthread key APIs */ +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)); + +int pthread_setspecific(pthread_key_t key, const void *value); + +void *pthread_getspecific(pthread_key_t key); + +int pthread_key_delete(pthread_key_t key); +``` + +## Known limits +- `pthread_attr_t`, `pthread_mutexattr_t` and `pthread_condattr_t` are not supported yet, so please pass `NULL` as the second argument of `pthread_create`, `pthread_mutex_init` and `pthread_cond_init`. +- The `errno.o` in wasi-sysroot is not compatible with this feature, so using errno in multi-thread may cause unexpected behavior. +- Currently `struct timespec` is not supported, so the prototype of `pthread_cond_timedwait` is different from the native one, it takes an unsigned int argument `useconds` to indicate the waiting time. diff --git a/doc/ref_types.md b/doc/ref_types.md new file mode 100644 index 0000000000..2cd300497b --- /dev/null +++ b/doc/ref_types.md @@ -0,0 +1,9 @@ +# WAMR reference-types introduction + +WebAssembly [reference-types](https://github.com/WebAssembly/reference-types) proposal introduces two new types `funcref` and `externref`. With `externref`, It is easier and more efficient to interoperate with host environment. Host references are able to be represented directly by type `externref`. + +WAMR has implemented the reference-types proposal. WAMR allows a native method to pass a host object to a WASM application as an `externref` parameter or receives a host object from a WASM application as an `externref` result. Internally, WAMR won't try to parse or dereference `externref`. It is an opaque type. + +The restriction of using `externref` in a native method is the host object has to be the value of a `uintptr_t` variable. In other words, it takes **8 bytes** on 64-bit machine and **4 bytes** on 32-bit machines. Please keep that in mind especially when calling `wasm_runtime_call_wasm`. + +Please ref to the [sample](../samples/ref-types) for more details. diff --git a/doc/security_issue_runbook.md b/doc/security_issue_runbook.md new file mode 100644 index 0000000000..8b0bc7970b --- /dev/null +++ b/doc/security_issue_runbook.md @@ -0,0 +1,66 @@ +# Security Issue Runbook + +This runbook provides step-by-step guidance on handling a security advisory. Typically, it begins with a draft security advisory when we initiate the process outlined in this runbook. The draft security advisory is created by a contributor or a maintainer. + +For information on what types of issues are considered security vulnerabilities and require a security advisory for resolution, please refer to [identifying a security issue](./security_need_to_know.md#identifying-a-security-issue). + +## Step 1: Initial Response to Security Advisory + +- Receive Security Advisory: When a new security advisory is received, the Incident Manager, typically the maintainer who opened the advisory, becomes the first responder. If the advisory was opened by someone else, a maintainer should take on the role of Incident Manager. The Incident Manager can hand off this role to another maintainer if necessary. +- Acknowledge Receipt: The Incident Manager should promptly acknowledge receipt of the advisory and communicate that the investigation will begin immediately. Security issues are the highest priority. + +## Step 2: Investigating the Vulnerability + +- Identify the Vulnerability: Reproduce the issue to understand the vulnerability. Determine which versions and platforms are affected. Fill out the advisory details with this information. +- Accept the Report: Accept the security report and create a temporary private fork to collaborate on a fix. Invite necessary helpers and stakeholders to this fork, as their input can be valuable. + +## Step 3: Communication and Collaboration + +- Use Non-Public Channels: Communicate through non-public channels, preferably email, during the resolution process. Avoid filing issues or pull requests on third-party repositories if they are involved. +- Workaround for Third-Party Dependencies: If third-party dependencies are involved, consider a workaround to patch the issue quickly unless the third party can release a fix promptly. + +## Step 4: Finalizing and Preparing for Release + +- Finalize Details: Once a fix is developed and the vulnerability is fully understood, finalize the advisory details and prepare for public release. Ensure the security issues are resolved in the private fork. +- Request CVE: Use the Big Green Button on the advisory to request a CVE number from GitHub staff. +- Advanced Disclosure Email: Decide on a disclosure date, typically within a week, and send an email to sec-announce@bytecodealliance.org about the upcoming security release. Other ways are also available to communicate the disclosure date. + +``` markdown +> A template for the advanced disclosure email + +The Wamr project would like to announce a forthcoming security release. + +The release will be made available on approximately YYYY-MM-DD. Additionally, an advisory will be made available on the same date at https://github.com/advisories. + +The highest severity issue fixed in this release is classified as XXX based on the CVSS classification scheme. +``` + +## Step 5: Preparing and Testing Patch Releases + +- Prepare PRs for Patch Releases: Create pull requests in the private fork for each version being patched. Ensure each PR is ready to apply cleanly and includes release notes for each release branch. +- Run Full Test Suite: Run the full test suite locally for the main branch. Attempt to run as much of the CI matrix locally as possible. + +## Step 6: Public Release and Communication + +- Open Version Bump PRs: Open version bump pull requests on the public repository without including patch notes or release notes for the fix. +- Manually Make PRs from Private Fork: Transfer the necessary pull requests from the private fork to the public repository. +- Merge and Trigger Releases: Merge the version bump PRs and trigger the release process. +- Publish GitHub Advisories: Delete the private forks and use the Big Green Button to publish the advisory. +- Send Security Release Email: Send a follow-up email to sec-announce@bytecodealliance.org describing the security release. Other communication channels can also be used to inform users about the security release. + +```markdown +> A template for the security release email + +[Updated YYYY-MM-DD] Security release available. + +WAMR release version X.Y.Z is now available. The binary release can be found on GitHub at https://github.com/bytecodealliance/wasm-micro-runtime/releases/tag/WAMR-Y.Y.Z. This release addresses the following security issues rated XXX: https://the link of the advisory + +We’ll be conducting a full review of our security practices to ensure ample notification is provided for future security releases. +``` + +By following these steps, you can effectively manage and resolve security issues for your open source project, ensuring timely communication and collaboration while maintaining the integrity and security of your software. + +## References + +- [Vulnerability Response Runbook](https://github.com/bytecodealliance/rfcs/blob/main/accepted/vulnerability-response-runbook.md) +- [Wasmtime Security Vulnerability Runbook](https://docs.wasmtime.dev/security-vulnerability-runbook.html) diff --git a/doc/security_need_to_know.md b/doc/security_need_to_know.md new file mode 100644 index 0000000000..e9bc890396 --- /dev/null +++ b/doc/security_need_to_know.md @@ -0,0 +1,69 @@ +# About security issues + +This document aims to explain the process of identifying a security issue and the steps for managing a security issue. + +## identifying a security issue + +It is commonly stated that a security issue is an issue that: +- Exposes sensitive information to unauthorized parties. +- Allows unauthorized modification of data or system state. +- Affects the availability of the system or its services. +- Permits unauthorized access to the system. +- Enables users to perform actions they should not be able to. +- Allows users to deny actions they have performed. + +Given that WASI is a set of Capability-based APIs, all unauthorized actions are not supposed to happen. Most of the above security concerns can be alleviated. What remains for us is to ensure that the execution of Wasm modules is secure. In other words, do not compromise the sandbox. Unless it is explicitly disabled beforehand. + +WebAssembly binaries are considered untrusted. A Wasm binary that causes a breach of the Wasm sandbox or a crash of the runtime is considered to be a potential security issue. On the other hand, Ahead-of-Time (AoT) binaries are assumed to be generated by a trusted source and using the supported toolchain. Therefore, AoT binaries are considered trusted. As such, malformed or manipulated AoT binaries that breach the sandbox or cause crashes of the runtime may be considered as bugs but are not classified as security issues. + +If the AoT compiler and/or related tools emit an AoT binary that breaches the Wasm sandbox or causes the runtime to crash, this indicates a potential security issue in the AoT toolchain. It is assumed that the correct configuration and options are used when generating AoT binaries. Misconfiguration or misuse of the tooling options, therefore, is not considered to be a security issue. + +### Is this bug considered a security vulnerability? + +#### For someone who finds a problem +If a bug **causes a crash or hang**, treat it as a possible security issue and report it to a security advisor. A maintainer will review it and change its category if needed. When in doubt, report it as a security issue. + +If the person reporting the issue can answer "Yes" to any question in the checklist below, report it as a security issue. Otherwise, the issue can be treated as a regular bug. + +Does the issue allow an WebAssembly binary to: +- break out of the Wasm sandbox? +- read or modify host memory, runtime memory, or another module's data when it should not? +- use files, sockets, device access, or other host resources without being the granted capabilities? +- call host functions or native APIs in a way that bypasses intended checks? +- make the runtime unavailable or put it into an unrecoverable state? + +--- + +#### For those maintainers + +please use the following guidelines to determine if a bug or advisory is a security issue: + +Only bugs that affect [tier A platforms or features](./tiered_support.md) should be considered. + +Actions that differ from Wasm rules (like calculating wrong values) are not seen as security issues as long as they stay within the sandbox. + +By default, native APIs and CLIs are following the principle of **caller guarantee**. If the caller provides incorrect parameters or users input malformed options, it is not a security issue. For example, if a user passes an invalid file descriptor to `fd_read`, it is not a security issue. + +WebAssembly binaries are not trusted. Malformed .wasm files should be handled gracefully. If a .wasm file causes a runtime crash or hang, it is a security issue. On the other hand, it's expected that aot runtime alone doesn't provide the same guarantee. So user-crafted .aot can cause anything, including crashes or hangs. They are not considered security issues. + +A denial-of-service (DoS) attack is a cyberattack that aims to make a computer or network resource unavailable to its users. If the service (runtime in this case) can recover and start another module or run another function within the same instance, it is not considered unavailable, and thus not a Denial of Service (DoS). + +Another type of execution problem we usually do not classify as a security one is if it is caused by an infinite loop or incorrect recursive function call chain. + +### When a maintainer identify a problem that should be classified as a security vulnerability + +Once a maintainer realizes an issue or PR describes a real or possible security vulnerability, act quickly to minimize exposure. Do not share technical details publicly on the issue or PR anymore. Maintainers should: + +- Close or edit the public discussion. Thank the person who reported it and explain that security-related issues should go through the Security Advisory process. Close the public issue or pull request as soon as possible to prevent further public sharing. If details have already been shared, consider editing or asking GitHub staff to remove sensitive content. + +- Create a Security Advisory. Invite the reporter to join as a collaborator or reporter. If the reporter is uncomfortable using GitHub Security Advisories, offer another private communication method, such as email. + +- Follow the guidelines in [the security issue runbook](./security_issue_runbook.md) for the next steps. + +## reporting a security issue + +Follow the [same guidelines](https://bytecodealliance.org/security) as other projects within the Bytecode Alliance. + +## managing a security issue + +Once a security issue is confirmed, please refer to [the runbook](./security_issue_runbook.md) for the subsequent steps to take. diff --git a/doc/shared_heap.md b/doc/shared_heap.md new file mode 100644 index 0000000000..4d014b08ba --- /dev/null +++ b/doc/shared_heap.md @@ -0,0 +1,38 @@ +# Introducing the Shared-Heap Feature in WAMR + +In the world of WebAssembly, flexibility and performance are key. The WebAssembly Micro Runtime (WAMR) has introduced a powerful feature known as the shared heap, designed to enhance performance by allowing data sharing between multiple WebAssembly (WASM) instances, or between WASM and its host, without incurring the overhead of data copying. Let's delve into what this feature offers and how it can be effectively utilized. + +## What is the Shared Heap? + +The shared heap is an innovative extension of the WebAssembly linear memory. Unlike traditional memory, which requires data copying between WASM instances or to the host, the shared heap allows direct access to the same memory space. This can significantly improve performance in scenarios where multiple WASM instances need to interact or share data with the host. + +## Key Benefits + +- Expanded Memory Space: The shared-heap feature effectively expands the linear memory space by introducing hosted memory address spaces. This new linear memory space can be seen as a virtual space, encompassing multiple real spaces. +- Toolchain Workaround: The shared heap acts as a workaround for the current lack of toolchain support for the multi-memory proposal. This provides a practical solution for developers needing enhanced memory capabilities. +- Boundary Checks and Sandbox Protection: The shared heap maintains boundary checks and extends sandbox protection to portions of the real memory space, ensuring secure and reliable memory access across shared heaps. +- Ongoing Evaluation: The shared-heap feature is still under evaluation, and the team is actively seeking better solutions to further improve the functionality and performance. + +## How Does It Work? + +While the concept of a shared heap might seem straightforward, implementing it correctly requires careful attention. It is a runtime feature, not part of the standard WebAssembly specification, nor an ongoing proposal. Here’s how you can leverage the shared heap in your applications: + +### Creating a Shared Heap + +1. WAMR Managed Shared Heap: Use the `wasm_runtime_create_shared_heap(SharedHeapInitArgs \*init_args)` API to create a shared heap. If only `init_args.size` is specified with `init_args.pre_allocated_addr` set to NULL, WAMR will allocate and manage the shared heap. This allows dynamic memory management through `wasm_runtime_shared_heap_malloc()` and `wasm_runtime_shared_heap_free()`. Memory allocation from this heap is valid and can be shared, with automatic cleanup when the runtime is destroyed. + +2. Preallocated Shared Heap: Alternatively, you can use pre-allocated memory, either from the system heap or a static global buffer. This requires you to handle its accessibility, size, and management. Specify `init_args.pre_allocated_addr` along with `init_args.size` to create this type of shared heap, which acts as a single large chunk for direct data sharing. + +### Creating and Attaching Shared Heap Chains + +To form a unified memory space, you can chain multiple shared heaps using the `wasm_runtime_chain_shared_heaps(wasm_shared_heap_t head, wasm_shared_heap_t body)` API. This creates a continuous memory region from the perspective of the WASM app, even though it might consist of separate regions in the native environment. + +Once chained, attach the shared heap to WASM apps using `wasm_runtime_attach_shared_heaps(wasm_module_inst_t module_inst, wasm_shared_heap_t shared_heaps)`. This ensures no overlap with the existing linear memory of the WASM app instance, preventing accidental overwrites. + +### Resource Allocation and Data Transfer + +After attaching the shared heap, both host and WASM code can allocate resources directly from it. Host code can use `wasm_runtime_shared_heap_malloc()`, while WASM code can utilize `shared_heap_malloc()`. This allows one side to allocate memory and pass the address or index to the other side, facilitating efficient data transfer. The original boundary checks for loading and storing in linear memory naturally extend to the shared-heap area, as it is part of the linear memory. This integration ensures that memory operations remain secure and consistent. + +## Conclusion + +The shared heap feature is an exciting advancement in WASM performance optimization. By enabling direct memory sharing, it reduces overhead and boosts efficiency in applications requiring high interactivity. While it offers great benefits, remember it heavily relies on correct implementation to manage shared data effectively. As the feature is still under evaluation, let's work together on a better solution. We are collecting every potential usage and unique feature, looking for the shared common ground that will drive future innovations in WebAssembly applications. diff --git a/doc/socket_api.md b/doc/socket_api.md new file mode 100644 index 0000000000..1ecc1213c7 --- /dev/null +++ b/doc/socket_api.md @@ -0,0 +1,125 @@ +# How to use Berkeley/Posix Socket APIs in WebAssembly + +**_Berkeley sockets_** usually means an API for Internet sockets and Unix domain +sockets. A socket is an abstract representation of the local endpoint of a +network communication path. + +Currently, WAMR supports some Socket API features: +- Support TCP and UDP +- Support IPv4 and IPv6 +- Support get/set socket options +- Support access control + +This document introduces a way to support the _Berkeley/POSIX Socket API_ in +WebAssembly code. + +## Patch the native code + +The first step is to include a header file of the WAMR socket extension in the +native source code. + +```c +#ifdef __wasi__ +#include +#endif +``` + +`__wasi__` is a macro defined by WASI. The host compiler will not enable it. + +## CMake files + +It is recommended that the project should use CMake as its build system. Use +[_wasi-sdk_](https://github.com/WebAssembly/wasi-sdk) +as a toolchain to compile C/C++ to WebAssembly + +```bash +$ cmake -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + .. +``` + +In the *CMakeLists.txt*, include an extension of socket support and link with it. + +```cmake +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake) +add_executable(socket_example tcp_server.c) +target_link_libraries(socket_example socket_wasi_ext) +``` + +Now, the native code with socket APIs is ready for compilation. + +## Run with iwasm + +If having the _.wasm_, the last step is to run it with _iwasm_. + +The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1`. By default, it is +enabled. + +_iwasm_ accepts address ranges via an option, `--addr-pool`, to implement +the capability control. All IP address the WebAssembly application may need to `bind()` or `connect()` +should be announced first. Every IP address should be in CIDR notation. If not, _iwasm_ will return +an error. + +```bash +$ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm +``` + +_iwasm_ also accepts list of domain names and domain name patterns for the address resolution via an option, `--allow-resolve`, to implement the capability control. Every domain that will be resolved using `sock_addr_resolve` needs to be added to the allowlist first. + +```bash +$ iwasm --allow-resolve=*.example.com --allow-resolve=domain.com +``` + +The example above shows how to allow for resolving all `example.com`'s subdomains (e.g. `x.example.com`, `a.b.c.example.com`) and `domain.com` domain. + +Refer to [socket api sample](../samples/socket-api) for more details. + +## Intel SGX support + +WAMR also supports the socket API within Intel SGX enclaves. + +The _iwasm_ should be compiled with `WAMR_BUILD_LIBC_WASI=1` and `WAMR_BUILD_LIB_PTHREAD=1`, which are enabled by default. + +Similarly to running _iwasm_ outside of an enclave, the allowed address ranges are given via the option `--addr-pool`. + +```bash +$ iwasm --addr-pool=1.2.3.4/15,2.3.4.6/16 socket_example.wasm +``` + +Refer to [socket api sample](../samples/socket-api) for the compilation of the Wasm applications and [_iwasm_ for Intel SGX](../product-mini/platforms/linux-sgx) for the Wasm runtime. + +## The background and compatibility notes + +### WASIp1 + +The WASIp1 provides a subset of the socket API. +Namely, + +* send() +* recv() +* shutdown() +* accept() + +Functionalities like connect() and listen() are intentionally omitted +there to maintain the capability-based security model, inherited from +cloudabi. The common practice for applications is to make the host code +pass already connected/listening sockets to wasm module. + +### WAMR extensions + +WAMR extends the WASIp1 with the rest of socket API functionalities +for convenience. + +* socket() +* connect() +* bind() +* listen() +* some of getsockopt/setsockopt options +* name resolution (a subset of getaddrinfo) + +### Compatibilities + +Many of runtimes (eg. Wasmer and WasmEdge) provide similar extensions. +Unfortunately, they are all incompatible. Thus, portable applications +should not rely on these extensions. diff --git a/doc/source_debugging.md b/doc/source_debugging.md new file mode 100644 index 0000000000..3e21c401cc --- /dev/null +++ b/doc/source_debugging.md @@ -0,0 +1,20 @@ +# WAMR source debugging + +## Build wasm application with debug information +To debug your application, you need to compile them with debug information. You can use `-g` option when compiling the source code if you are using wasi-sdk (also work for emcc and rustc): +``` bash +/opt/wasi-sdk/bin/clang -g test.c -o test.wasm +``` + +Then you will get `test.wasm` which is a WebAssembly module with embedded DWARF sections. Further, you can use `llvm-dwarfdump` to check if the generated wasm file contains DWARF information: +``` bash +llvm-dwarfdump-12 test.wasm +``` + +## Debugging with interpreter + +See [Debugging with interpreter](source_debugging_interpreter.md). + +## Debugging with AOT + +See [Debugging with AOT](source_debugging_aot.md). diff --git a/doc/source_debugging_aot.md b/doc/source_debugging_aot.md new file mode 100644 index 0000000000..62ed51e0b2 --- /dev/null +++ b/doc/source_debugging_aot.md @@ -0,0 +1,100 @@ +# WAMR source debugging (AOT) + +## Debugging with AOT + +> Note: AOT debugging is experimental and only a few debugging capabilities are supported. + +1. Build lldb (assume you have already built llvm) +``` bash +cd ${WAMR_ROOT}/core/deps/llvm/build +cmake ../llvm -DLLVM_ENABLE_PROJECTS="clang;lldb" -DLLDB_INCLUDE_TESTS=OFF +make -j $(nproc) +``` + +2. Build wamrc with debugging feature +``` bash +cd ${WAMR_ROOT}/wamr-compiler +mkdir build && cd build +cmake .. -DWAMR_BUILD_DEBUG_AOT=1 +make -j $(nproc) +``` + +3. Build iwasm with debugging feature +``` bash +cd ${WAMR_ROOT}/product-mini/platforms/linux +mkdir build && cd build +cmake .. -DWAMR_BUILD_DEBUG_AOT=1 +make +``` + +4. Compile wasm module to AOT module +``` bash +wamrc -o test.aot test.wasm +``` + +5. Execute iwasm using lldb + + Then you can use lldb commands to debug both wamr runtime and your wasm application in ***current terminal***. + + ``` bash + % lldb iwasm -- test.aot + (lldb) target create "iwasm" + Current executable set to 'iwasm' (x86_64). + (lldb) settings set -- target.run-args "test.aot" + (lldb) settings set plugin.jit-loader.gdb.enable on + (lldb) b main + Breakpoint 1: where = iwasm`main + 48 at main.c:294:11, address = 0x0000000100001020 + (lldb) run + Process 27954 launched: '/tmp/bin/iwasm' (x86_64) + Process 27954 stopped + * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 + frame #0: 0x0000000100001020 iwasm`main(argc=2, argv=0x00007ff7bfeff678) at main.c:294:11 + 291 int + 292 main(int argc, char *argv[]) + 293 { + -> 294 int32 ret = -1; + 295 char *wasm_file = NULL; + 296 const char *func_name = NULL; + 297 uint8 *wasm_file_buf = NULL; + Target 0: (iwasm) stopped. + (lldb) c + Process 27954 resuming + 1 location added to breakpoint 1 + error: need to add support for DW_TAG_base_type 'void' encoded with DW_ATE = 0x0, bit_size = 0 + Process 27954 stopped + * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2 + frame #0: 0x00000001002980a0 JIT(0x100298004)`main(exenv=0x0000000301808200) at hello.c:6:9 + 3 int + 4 main(void) + 5 { + -> 6 printf("hello\n"); + 7 + 8 return 0; + 9 } + Target 0: (iwasm) stopped. + (lldb) br l + Current breakpoints: + 1: name = 'main', locations = 2, resolved = 2, hit count = 2 + 1.1: where = iwasm`main + 48 at main.c:294:11, address = 0x0000000100001020, resolved, hit count = 1 + 1.2: where = JIT(0x100298004)`main + 12 at hello.c:6:9, address = 0x00000001002980a0, resolved, hit count = 1 + + (lldb) + ``` + + * In the above example, + + * The first `main` function, which is in `main.c`, is the main + function of the iwasm command. + + * The second `main` function, which is in `hello.c`, is the main + function of the AOT-compiled wasm module. + + * WAMR AOT debugging uses the GDB JIT loader mechanism to load + the debug info of the debuggee module. + On some platforms including macOS, you need to enable it explicitly. + (`settings set plugin.jit-loader.gdb.enable on`) + + References: + + * https://github.com/llvm/llvm-project/blob/main/llvm/docs/DebuggingJITedCode.rst + * https://sourceware.org/gdb/current/onlinedocs/gdb/JIT-Interface.html diff --git a/doc/source_debugging_interpreter.md b/doc/source_debugging_interpreter.md new file mode 100644 index 0000000000..577b9bf4a4 --- /dev/null +++ b/doc/source_debugging_interpreter.md @@ -0,0 +1,115 @@ +# WAMR source debugging (interpreter) + +References: +- [Blog: WAMR source debugging basic](https://bytecodealliance.github.io/wamr.dev/blog/wamr-source-debugging-basic/) +- [Blog: Debugging wasm with VSCode](https://bytecodealliance.github.io/wamr.dev/blog/debugging-wasm-with-vscode/) + +WAMR supports source level debugging based on DWARF (normally used in C/C++/Rust), source map (normally used in AssemblyScript) is not supported. + +**The lldb's ability to debug wasm application is based on the patch [Add class WasmProcess for WebAssembly debugging](https://reviews.llvm.org/D78801). Thanks very much to the author @paolosev for such a great work!** + +## Debugging with interpreter + +1. Install dependent libraries +``` bash +apt update && apt install cmake make g++ libxml2-dev -y +``` + +2. Build iwasm with source debugging feature +``` bash +cd ${WAMR_ROOT}/product-mini/platforms/linux +mkdir build && cd build +cmake .. -DWAMR_BUILD_DEBUG_INTERP=1 +make +``` +> Note: On MacOS M1 environment, pass the additional `-DWAMR_DISABLE_HW_BOUND_CHECK=1` cmake configuration. + +3. Execute iwasm with debug engine enabled +``` bash +iwasm -g=127.0.0.1:1234 test.wasm +# Use port = 0 to allow a random assigned debug port +``` + +4. Build customized lldb +``` bash +git clone --branch release/13.x --depth=1 https://github.com/llvm/llvm-project +cd llvm-project +git apply ${WAMR_ROOT}/build-scripts/lldb_wasm.patch +mkdir build-lldb +cmake -S ./llvm -B build-lldb \ + -G Ninja \ + -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;lldb" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;WebAssembly" \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=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_ENABLE_BINDINGS:BOOL=OFF -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF -DLLVM_ENABLE_LIBXML2:BOOL=ON +cmake --build build-lldb --target lldb --parallel $(nproc) +# The lldb is generated under build-lldb/bin/lldb +``` +> Note: If using `CommandLineTools` on MacOS, make sure only one SDK is present in `/Library/Developer/CommandLineTools/SDKs`. + +> You can download pre-built `wamr-lldb` binaries from [here](https://github.com/bytecodealliance/wasm-micro-runtime/releases). + +5. Launch customized lldb and connect to iwasm +``` bash +lldb +(lldb) process connect -p wasm connect://127.0.0.1:1234 +``` +Then you can use lldb commands to debug your applications. Please refer to [lldb document](https://lldb.llvm.org/use/tutorial.html) for command usage. + +## Enable debugging in embedders (for interpreter) + +There are three steps to enable debugging in embedders + +1. Set the debug parameters when initializing the runtime environment: + ``` c + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + /* ... */ + strcpy(init_args.ip_addr, "127.0.0.1"); + init_args.instance_port = 1234; + /* + * Or set port to 0 to use a port assigned by os + * init_args.instance_port = 0; + */ + + if (!wasm_runtime_full_init(&init_args)) { + return false; + } + ``` + +2. Use `wasm_runtime_start_debug_instance` to create the debug instance: + ``` c + /* + initialization, loading and instantiating + ... + */ + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + uint32_t debug_port = wasm_runtime_start_debug_instance(exec_env); + ``` + +3. Enable source debugging features during building + + You can use `-DWAMR_BUILD_DEBUG_INTERP=1` during cmake configuration + + Or you can set it directly in `cmake` files: + ``` cmake + set (WAMR_BUILD_DEBUG_INTERP 1) + ``` + +### Attentions +- Debugging `multi-thread wasm module` is not supported, if your wasm module use pthread APIs (see [pthread_library.md](./pthread_library.md)), or the embedder use `wasm_runtime_spawn_thread` to create new wasm threads, then there may be **unexpected behaviour** during debugging. + + > Note: This attention is about "wasm thread" rather than native threads. Executing wasm functions in several different native threads will **not** affect the normal behaviour of debugging feature. + +- When using source debugging features, **don't** create multiple `wasm_instance` from the same `wasm_module`, because the debugger may change the bytecode (set/unset breakpoints) of the `wasm_module`. If you do need several instance from the same bytecode, you need to copy the bytecode to a new butter, then load a new `wasm_module`, and then instantiate the new wasm module to get the new instance. + +- If you are running `lldb` on non-linux platforms, please use `platform select remote-linux` command in lldb before connecting to the runtime: + ``` + (lldb) platform select remote-linux + (lldb) process connect -p wasm connect://127.0.0.1:1234 + ``` diff --git a/doc/stability_release.md b/doc/stability_release.md new file mode 100644 index 0000000000..78e034a3df --- /dev/null +++ b/doc/stability_release.md @@ -0,0 +1,33 @@ +# Semantic Versioning + +WAMR has adopted [semantic versioning](https://semver.org/) to replace the former *date versioning system*. The new version string consists of three parts: + +- *major*: Any change that is not compatible with previous versions, affecting either the ABI or APIs, will result in an increase in the major version number. APIs include: wasm_export.h, wasm_c_api.h, sections in AOT files, among others. +- *minor*: This number increases with the addition of new features. This encompasses not only MVP (Minimum Viable Product) or POST-MVP features but also WebAssembly System Interface (WASI) features and WAMR-specific features. +- *patch*: This number is incremented for patches. + +## Legacy releases + +All previous versions (tags) will retain their current status. There will be no changes to existing release names and links. + +# Release Process + +WAMR has been deployed across various devices. A frequent release cycle would strain customers' testing resources and add extra deployment work. Two factors can trigger a new WAMR release: + +- Community requests, particularly following the integration of significant and new features. +- Security vulnerabilities and critical bug fixes that ensure correctness. + +Patch releases will be made only to address security vulnerabilities and critical issues related to default behavior in prior releases. + +Once a release decision has been made: + +- Create a PR that: + 1. Modifies *build-scripts/version.cmake*. + 2. Executes cmake configuration to update the version. + 3. Updates *RELEASE_NOTES.md*. +- A checklist of the PR includes + - [ ] *build-scripts/version.cmake* + - [ ] *core/version.h* + - [ ] *RELEASE_NOTES.md* +- Once the PR is merged, create a new tag. +- Initiate the release process by triggering *the binary release processes* in *Actions*. diff --git a/doc/stability_wasm_proposals.md b/doc/stability_wasm_proposals.md new file mode 100644 index 0000000000..12f9fee663 --- /dev/null +++ b/doc/stability_wasm_proposals.md @@ -0,0 +1,85 @@ +# Wasm Proposals + +This document is intended to describe the current status of WebAssembly proposals and WASI proposals in WAMR. + +Only track proposals that are followed in the [WebAssembly proposals](https://github.com/WebAssembly/proposals) and [WASI proposals](https://github.com/WebAssembly/WASI/blob/main/docs/Proposals.md). + +Normally, the document tracks proposals that are in phase 4. However, if a proposal in an earlier phase receives support, it will be added to the list below. + +The _status_ represents the configuration _product-mini/platforms/linux/CMakeLists.txt_. There may be minor differences between the top-level CMakeLists and platform-specific CMakeLists. + +Users can turn those features on or off by using compilation options. If a relevant compilation option is not available(`N/A`), it indicates that the feature is permanently enabled. + +## On-by-default Wasm Proposals + +| Proposal | >= Phase 4 | Compilation Option | +| ------------------------------------- | ---------- | ------------------------ | +| Bulk Memory Operations | Yes | `WAMR_BUILD_BULK_MEMORY` | +| Fixed-width SIMD[^1] | Yes | `WAMR_BUILD_SIMD` | +| Import/Export of Mutable Globals[^2] | Yes | N/A | +| Multi-value | Yes | N/A | +| Non-trapping float-to-int Conversions | Yes | N/A | +| Reference Types | Yes | `WAMR_BUILD_REF_TYPES` | +| Sign-extension Operators | Yes | N/A | +| WebAssembly C and C++ API | No | N/A | + +[^1]: llvm-jit and aot only. + +[^2]: in WAMR's implementation, if a mutable global shared by several wasm instances, each instance maintains its own copy of the global rather than sharing it. + +## Off-by-default Wasm Proposals + +| Proposal | >= Phase 4 | Compilation Option | +| ----------------------------- | ---------- | -------------------------------- | +| Branch Hinting | Yes | `WASM_ENABLE_BRANCH_HINTS` | +| Extended Constant Expressions | Yes | `WAMR_BUILD_EXTENDED_CONST_EXPR` | +| Garbage Collection | Yes | `WAMR_BUILD_GC` | +| Legacy Exception Handling[^3] | No | `WAMR_BUILD_EXCE_HANDLING` | +| Memory64 | Yes | `WAMR_BUILD_MEMORY64` | +| Multiple Memories[^4] | Yes | `WAMR_BUILD_MULTI_MEMORY` | +| Reference-Typed Strings | No | `WAMR_BUILD_STRINGREF` | +| Tail Call | Yes | `WAMR_BUILD_TAIL_CALL` | +| Threads[^5] | Yes | `WAMR_BUILD_SHARED_MEMORY` | +| Typed Function References | Yes | `WAMR_BUILD_GC` | + +[^3]: + interpreter only. [a legacy version](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md). + This proposal is currently also known as the "legacy proposal" and still + supported in the web, but can be deprecated in future and the use of + this proposal is discouraged. + +[^4]: interpreter only + +[^5]: `WAMR_BUILD_LIB_PTHREAD` can also be used to enable + +## Unimplemented Wasm Proposals + +| Proposal | >= Phase 4 | +| ------------------------------------------- | ---------- | +| Custom Annotation Syntax in the Text Format | Yes | +| Exception Handling[^6] | Yes | +| JS String Builtins | Yes | +| Relaxed SIMD | Yes | + +[^6]: [up-to-date version](https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/Exceptions.md) + +## On-by-default WASI Proposals + +| Proposal | >= Phase 4 | Compilation Option | +| -------- | ---------- | ------------------ | + +## Off-by-default WASI Proposals + +| Proposal | >= Phase 4 | Compilation Option | +| -------------------------- | ---------- | ----------------------------- | +| Machine Learning (wasi-nn) | No | `WAMR_BUILD_WASI_NN` | +| Threads | No | `WAMR_BUILD_LIB_WASI_THREADS` | + +## Unimplemented WASI Proposals + +| Proposal | >= Phase 4 | +| -------- | ---------- | + +## WAMR features + +WAMR offers a variety of customizable features to create a highly efficient runtime. For more details, please refer to [build_wamr](./build_wamr.md). diff --git a/doc/tiered_support.md b/doc/tiered_support.md new file mode 100644 index 0000000000..4cce12c8ce --- /dev/null +++ b/doc/tiered_support.md @@ -0,0 +1,184 @@ +# Tiered Support + +## Tier A + +This tier is the highest level of support. Features and targets in this tier are fully supported, actively maintained, and regularly tested. Users can expect prompt assistance and comprehensive documentation for any issues or questions related to these features. Users can rely on Tier A features for production environments. Targets in this tier usually have been used in products. + +## Tier B + +This tier represents a moderate level of support. Features and targets in this tier are generally supported and maintained, but may not receive the same level of attention as Tier A. While efforts are made to ensure stability, users may encounter occasional issues that are not immediately addressed. Documentation may be less comprehensive compared to Tier A. Users are encouraged to report any issues they encounter, but response times may vary. + +## Tier C + +This tier indicates experimental features with foundational support levels. These implementations are typically optimized for specific platforms, running modes, or use cases, and may not receive active maintenance. Documentation tends to be minimal or require updates. Production deployment requires specialized expertise and careful evaluation, including establishing appropriate threat models and ensuring comprehensive understanding of the implementation scope. Users should be prepared to troubleshoot issues and handle ongoing maintenance independently, accepting full responsibility for any limitations or challenges that may arise. + +> [!NOTE] +> +> - **actively maintained** and **fully supported**. users can expect timely assistance, comprehensive documentation, and regular updates for any issues or questions related to these features. +> - **regularly tested**. means there are automated tests in CI that run on a regular basis to ensure the stability and functionality of these features. + +## Labels + +**Target** refers to the specific hardware architecture or operating system that the runtime can be compiled for and run on. + +**Runtime Extensions** are features that extend the runtime capabilities of the system beyond the core WebAssembly specification. These extensions may include optimizations, additional APIs, or other enhancements that improve performance, usability, or functionality. + +**Portability** indicates the ability of the runtime to operate across different platforms or environments without requiring significant modifications. This includes compatibility with various operating systems, hardware architectures, and development frameworks. + +# TierA + +## Targets + +| Description | +| -------------------------- | +| aarch64-unknown-nuttx-eabi | +| i386-pc-linux-gnu | +| x86_64-pc-linux-gnu | +| x86_64-apple-darwin | +| x86_64-none-linux-gnu | + +## Features + +| Description | Compilation Flags | Labels | +| -------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------ | +| Linux Compatibility | WAMR_BUILD_PLATFORM=linux | Portability | +| AoT runtime | [WAMR_BUILD_AOT](./build_wamr.md#configure-aot) | Running mode | +| Fast Interpreter | [WAMR_BUILD_FAST_INTERP](./build_wamr.md#configure-interpreters) | Running mode | +| Classic Interpreter | [WAMR_BUILD_INTERP](./build_wamr.md#configure-interpreters) | Running mode | +| AoT compilation (wamrc) | [WAMR_BUILD_WAMR_COMPILER](./build_wamr.md#configure-aot) | Compiler | +| Bulk Memory | [WAMR_BUILD_BULK_MEMORY](./build_wamr.md#bulk-memory-feature) | Wasm Proposal | +| Name section | [WAMR_BUILD_CUSTOM_NAME_SECTION](./build_wamr.md#name-section) | Wasm Proposal | +| Extended Constant Expressions | [WAMR_BUILD_EXTENDED_CONST_EXPR](./build_wamr.md#extended-constant-expression) | Wasm Proposal | +| Non-trapping float-to-int | ALWAYS ON. Can not be disabled | Wasm Proposal | +| Import/Export of Mutable Globals | ALWAYS ON. Can not be disabled | Wasm Proposal | +| Multi-value | ALWAYS ON. Can not be disabled | Wasm Proposal | +| WASI LIBC | [WAMR_BUILD_LIBC_WASI](./build_wamr.md#configure-libc) | Wasm Proposal | +| WASI threads | [WAMR_BUILD_LIB_WASI_THREADS](./build_wamr.md#lib-wasi-threads) | Wasm Proposal | +| Custom sections | [WAMR_BUILD_LOAD_CUSTOM_SECTION](./build_wamr.md#load-wasm-custom-sections) | Wasm Proposal | +| Memory64 | [WAMR_BUILD_MEMORY64](./build_wamr.md#memory64-feature) | Wasm Proposal | +| Reference Types | [WAMR_BUILD_REF_TYPES](./build_wamr.md#reference-types-feature) | Wasm Proposal | +| Threads | [WAMR_BUILD_SHARED_MEMORY](./build_wamr.md#shared-memory-feature) | Wasm Proposal | +| SIMD (128-bit) | [WAMR_BUILD_SIMD](./build_wamr.md#128-bit-simd-feature) | Wasm Proposal | +| AOT intrinsics | [WAMR_BUILD_AOT_INTRINSICS](./build_wamr.md#aot-intrinsics) | Runtime Extensions | +| AoT stack frame | [WAMR_BUILD_AOT_STACK_FRAME](./build_wamr.md#aot-stack-frame-feature) | Runtime Extensions | +| Global heap pool | [WAMR_BUILD_GLOBAL_HEAP_POOL](./build_wamr.md#a-pre-allocation-for-runtime-and-wasm-apps) | Runtime Extensions | +| Global heap size | [WAMR_BUILD_GLOBAL_HEAP_SIZE](./build_wamr.md#a-pre-allocation-for-runtime-and-wasm-apps) | Runtime Extensions | +| Libc builtin | [WAMR_BUILD_LIBC_BUILTIN](./build_wamr.md#configure-libc) | Runtime Extensions | +| Module instance context | [WAMR_BUILD_MODULE_INST_CONTEXT](./build_wamr.md#module-instance-context-apis) | Runtime Extensions | +| Quick AOT/JIT entries | [WAMR_BUILD_QUICK_AOT_ENTRY](./build_wamr.md#configure-aot) | Runtime Extensions | +| Shrunk memory | [WAMR_BUILD_SHRUNK_MEMORY](./build_wamr.md#shrunk-the-memory-usage) | Runtime Extensions | +| Thread manager | [WAMR_BUILD_THREAD_MGR](./build_wamr.md#thread-manager) | Runtime Extensions | +| App entry | [WAMR_DISABLE_APP_ENTRY](./build_wamr.md#exclude-wamr-application-entry-functions) | Runtime Extensions | +| hardware bound check | [WAMR_DISABLE_HW_BOUND_CHECK](./build_wamr.md#disable-boundary-check-with-hardware-trap) | Runtime Extensions | +| stack hardware bound check | [WAMR_DISABLE_STACK_HW_BOUND_CHECK](./build_wamr.md#disable-native-stack-boundary-check-with-hardware-trap) | Runtime Extensions | +| Wakeup blocking operation | [WAMR_DISABLE_WAKEUP_BLOCKING_OP](./build_wamr.md#disable-async-wakeup-of-blocking-operation) | Runtime Extensions | + +# TierB + +## Targets + +| Description | +| ---------------------- | +| arc-unknown-none-elf | +| x86_64-pc-windows-msvc | +| mips-unknown-elf | +| mips64-unknown-elf | + +## Features + +| Description | Compilation Flags | Labels | +| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------ | +| Darwin Compatibility | WAMR_BUILD_PLATFORM=darwin | Portability | +| ESP-IDF Compatibility | WAMR_BUILD_PALTFORM=esp-idf | Portability | +| Nuttx Compatibility | WAMR_BUILD_PALTFORM=nuttx | Portability | +| SGX Compatibility | WAMR_BUILD_PALTFORM=linux-sgx | Portability | +| Zephyr Compatibility | WAMR_BUILD_PALTFORM=zephyr | Portability | +| Stringref | [WAMR_BUILD_STRINGREF](./build_wamr.md#garbage-collection) | Wasm Proposal | +| Tail Calls | [WAMR_BUILD_TAIL_CALL](./build_wamr.md#tail-call-feature) | Wasm Proposal | +| LLVM JIT | [WAMR_BUILD_JIT](./build_wamr.md#configure-llvm-jit) | Running mode | +| Per Instance running mode | ALWAYS ON. Can not be disabled | Runtime Extensions | +| Maximum stack size for app threads | [WAMR_APP_THREAD_STACK_SIZE_MAX](./build_wamr.md#set-maximum-app-thread-stack-size) | Runtime Extensions | +| Host defined logging | [WAMR_BH_LOG](./build_wamr.md#host-defined-log) | Runtime Extensions | +| Host defined vprintf | [WAMR_BH_vprintf](./build_wamr.md#host-defined-vprintf) | Runtime Extensions | +| Allocation with usage tracking | [WAMR_BUILD_ALLOC_WITH_USAGE](./build_wamr.md#user-defined-linear-memory-allocator) | Runtime Extensions | +| Allocation with user data | [WAMR_BUILD_ALLOC_WITH_USER_DATA](./build_wamr.md#user-defined-linear-memory-allocator) | Runtime Extensions | +| Bulk-memory-opt | [WAMR_BUILD_BULK_MEMORY_OPT](./build_wamr.md#bulk-memory-opt) | Runtime Extensions | +| Call-indirect-overlong | [WAMR_BUILD_CALL_INDIRECT_OVERLONG](./build_wamr.md#call-indirect-overlong) | Runtime Extensions | +| Copy Call Stack | [WAMR_BUILD_COPY_CALL_STACK](./build_wamr.md#copy-call-stack) | Runtime Extensions | +| Debug Interpreter | [WAMR_BUILD_DEBUG_INTERP](./build_wamr.md#configure-debug) | Runtime Extensions | +| Dump call stack | [WAMR_BUILD_DUMP_CALL_STACK](./build_wamr.md#dump-call-stack-feature) | Runtime Extensions | +| Native General Invocation | [WAMR_BUILD_INVOKE_NATIVE_GENERAL](./build_wamr.md#invoke-general-ffi) | Runtime Extensions | +| Lazy JIT Compilation | [WAMR_BUILD_LAZY_JIT](./build_wamr.md#configure-llvm-jit) | Runtime Extensions | +| Pthread | [WAMR_BUILD_LIB_PTHREAD](./build_wamr.md#lib-pthread) | Runtime Extensions | +| Pthread Semaphore Support | [WAMR_BUILD_LIB_PTHREAD_SEMAPHORE](./build_wamr.md#lib-pthread-semaphore) | Runtime Extensions | +| Lime1 runtime | [WAMR_BUILD_LIME1](./build_wamr.md#lime1-target) | Runtime Extensions | +| Linux Performance Counters | [WAMR_BUILD_LINUX_PERF](./build_wamr.md#linux-perf-support) | Runtime Extensions | +| Memory profiling | [WAMR_BUILD_MEMORY_PROFILING](./build_wamr.md#memory-profiling-experiment) | Runtime Extensions | +| Multi-module | [WAMR_BUILD_MULTI_MODULE](./build_wamr.md#multi-module-feature) | Runtime Extensions | +| Perf profiling | [WAMR_BUILD_PERF_PROFILING](./build_wamr.md#performance-profiling-experiment) | Runtime Extensions | +| Shared heap | [WAMR_BUILD_SHARED_HEAP](./build_wamr.md#shared-heap-among-wasm-apps-and-host-native) | Runtime Extensions | +| Stack Guard Size | [WAMR_BUILD_STACK_GUARD_SIZE](./build_wamr.md#stack-guard-size) | Runtime Extensions | +| WASI Ephemeral NN | [WAMR_BUILD_WASI_EPHEMERAL_NN](./build_wamr.md#lib-wasi-nn-with-wasi_ephemeral_nn-module-support) | Runtime Extensions | +| WASI-NN (neural network APIs) | [WAMR_BUILD_WASI_NN](./build_wamr.md#lib-wasi-nn) | Runtime Extensions | +| External Delegate for WASI NN | [WAMR_BUILD_WASI_NN_ENABLE_EXTERNAL_DELEGATE](./build_wamr.md#lib-wasi-nn-external-delegate-mode) | Runtime Extensions | +| GPU Support for WASI NN | [WAMR_BUILD_WASI_NN_ENABLE_GPU](./build_wamr.md#lib-wasi-nn-gpu-mode) | Runtime Extensions | +| External Delegate Path for WASI NN | [WAMR_BUILD_WASI_NN_EXTERNAL_DELEGATE_PATH](./build_wamr.md#lib-wasi-nn-external-delegate-mode) | Runtime Extensions | +| LLAMA CPP for WASI NN | [WAMR_BUILD_WASI_NN_LLAMACPP](./build_wamr.md#lib-wasi-nn) | Runtime Extensions | +| ONNX for WASI NN | [WAMR_BUILD_WASI_NN_ONNX](./build_wamr.md#lib-wasi-nn) | Runtime Extensions | +| OpenVINO for WASI NN | [WAMR_BUILD_WASI_NN_OPENVINO](./build_wamr.md#lib-wasi-nn) | Runtime Extensions | +| TFLite for WASI NN | [WAMR_BUILD_WASI_NN_TFLITE](./build_wamr.md#lib-wasi-nn) | Runtime Extensions | +| Configurable bounds checks | [WAMR_CONFIGURABLE_BOUNDS_CHECKS](./build_wamr.md#configurable-memory-access-boundary-check) | Runtime Extensions | +| Write GS base | [WAMR_DISABLE_WRITE_GS_BASE](./build_wamr.md#disable-writing-the-linear-memory-base-address-to-x86-gs-segment-register) | Runtime Extensions | + +# TierC + +## Targets + +| Description | +| ---------------------- | +| aarch64-apple-ios | +| arm-none-eabi | +| i386-unknown-elf | +| i386-wrs-vxworks | +| riscv32-esp-elf | +| riscv32-unknown-elf | +| riscv64-unknown-elf | +| x86_64-linux-android | +| x86_64-linux-cosmo | +| x86_64-unknown-freebsd | +| x86_64-wrs-vxworks | +| xtensa-esp32-elf | + +## Features + +| Description | Compilation Flags | Labels | +| ------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------ | +| AliOS compatibility | WAMR_BUILD_PLATFORM=alios-things | Portability | +| Android Compatibility | WAMR_BUILD_PLATFORM=android | Portability | +| Cosmo Compatibility | WAMR_BUILD_PLATFORM=cosmopolitan | Portability | +| FreeBSD Compatibility | WAMR_BUILD_PLATFORM=freebsd | Portability | +| iOS Compatibility | WAMR_BUILD_PLATFORM=darwin | Portability | +| RIOT OS Compatibility | WAMR_BUILD_PLATFORM=riot | Portability | +| RT-Thread Compatibility | WAMR_BUILD_PLATFORM=rt-thread | Portability | +| VxWorks Compatibility | WAMR_BUILD_PLATFORM=vxworks | Portability | +| Windows Compatibility | WAMR_BUILD_PLATFORM=windows | Portability | +| GC (Garbage Collection) | [WAMR_BUILD_GC](./build_wamr.md#garbage-collection) | Wasm Proposal | +| Legacy Exception Handling | [WAMR_BUILD_EXCE_HANDLING](./build_wamr.md#exception-handling) | Wasm Proposal | +| Multi-memory | [WAMR_BUILD_MULTI_MEMORY](./build_wamr.md#multi-memory) | Wasm Proposal | +| Branch Hints | [WAMR_BUILD_BRANCH_HINTS](./build_wamr.md#branch-hints-feature) | Wasm Proposal | +| Fast JIT | [WAMR_BUILD_FAST_JIT](./build_wamr.md#configure-fast-jit) | Running mode | +| Multi-tier JIT | [Combination of flags](./build_wamr.md#configure-multi-tier-jit) | Running mode | +| AoT Validator | [WAMR_BUILD_AOT_VALIDATOR](./build_wamr.md#aot-validator) | Runtime Extensions | +| Debug AOT | [WAMR_BUILD_DEBUG_AOT](./build_wamr.md#configure-debug) | Runtime Extensions | +| Dynamic AoT debugging | [WAMR_BUILD_DYNAMIC_AOT_DEBUG](./build_wamr.md#configure-debug) | Runtime Extensions | +| Fast JIT Dump | [WAMR_BUILD_FAST_JIT_DUMP](./build_wamr.md#configure-fast-jit) | Runtime Extensions | +| Garbage Collection Heap Verify | [WAMR_BUILD_GC_HEAP_VERIFY](./build_wamr.md#garbage-collection) | Runtime Extensions | +| Instruction Metering | [WAMR_BUILD_INSTRUCTION_METERING](./build_wamr.md#instruction-metering) | Runtime Extensions | +| Libc EMCC Compatibility | [WAMR_BUILD_LIBC_EMCC](./build_wamr.md#libc-emcc) | Runtime Extensions | +| Libc UVWASI Compatibility | [WAMR_BUILD_LIBC_UVWASI](./build_wamr.md#libc-uvwasi) | Runtime Extensions | +| RATS Library | [WAMR_BUILD_LIB_RATS](./build_wamr.md#librats) | Runtime Extensions | +| Mini Loader | [WAMR_BUILD_MINI_LOADER](./build_wamr.md#wasm-mini-loader) | Runtime Extensions | +| SGX IPFS Support | [WAMR_BUILD_SGX_IPFS](./build_wamr.md#intel-protected-file-system) | Runtime Extensions | +| Static PGO | [WAMR_BUILD_STATIC_PGO](./build_wamr.md#running-pgoprofile-guided-optimization-instrumented-aot-file) | Runtime Extensions | +| WASM cache | [WAMR_BUILD_WASM_CACHE](./build_wamr.md#wasm-cache) | Runtime Extensions | +| Test garbage collection | [WAMR_TEST_GC](./build_wamr.md#test-garbage-collection) | Runtime Extensions | diff --git a/doc/wamr_api.md b/doc/wamr_api.md deleted file mode 100644 index 177f00861f..0000000000 --- a/doc/wamr_api.md +++ /dev/null @@ -1,351 +0,0 @@ - -WAMR application framework -======================== - -## Application system callbacks -The `on_init` and `on_destroy` functions are wamr application system callbacks which must be implemented in the wasm application if you want to use the APP framework. -``` C -void on_init() -{ - /* - Your init functions here, for example: - * platform initialization - * timer registration - * service / event registration - * ...... - */ -} - -void on_destroy() -{ - /* - your destroy functions here - */ -} -``` - -## Base App library - -The base library of application framework supports the essential API for WASM applications, such as inter-app communication, timers, etc. Other application framework components rely on the base library. - -When building the WAMR SDK, once application framework is enabled, the base library will automatically enabled. - -### Timer -The *timer* API's can be used to create some `soft timers` with single-shot mode or periodic mode. Here is a reference of how to use timer API's to execute a function every one second. -``` C -/* User global variable */ -static int num = 0; - -/* Timer callback */ -void timer1_update(user_timer_t timer) -{ - printf("Timer update %d\n", num++); -} - -void on_init() -{ - user_timer_t timer; - - /* set up a timer */ - timer = api_timer_create(1000, true, false, timer1_update); - api_timer_restart(timer, 1000); -} - -void on_destroy() -{ - -} -``` - -### Micro-service model (request/response) -The microservice model is also known as request and response model. One WASM application acts as the server which provides a specific service. Other WASM applications or host/cloud applications request that service and get the response. - -
- -Below is the reference implementation of the server application. It provides room temperature measurement service. - -``` C -void on_init() -{ - api_register_resource_handler("/room_temp", room_temp_handler); -} - -void on_destroy() -{ -} - -void room_temp_handler(request_t *request) -{ - response_t response[1]; - attr_container_t *payload; - payload = attr_container_create("room_temp payload"); - if (payload == NULL) - return; - - attr_container_set_string(&payload, "temp unit", "centigrade"); - attr_container_set_int(&payload, "value", 26); - - make_response_for_request(request, response); - set_response(response, - CONTENT_2_05, - FMT_ATTR_CONTAINER, - payload, - attr_container_get_serialize_length(payload)); - - api_response_send(response); - attr_container_destroy(payload); -} -``` - - -### Pub/sub model -One WASM application acts as the event publisher. It publishes events to notify WASM applications or host/cloud applications which subscribe to the events. - -
- -Below is the reference implementation of the pub application. It utilizes a timer to repeatedly publish an overheat alert event to the subscriber applications. Then the subscriber applications receive the events immediately. - -``` C -/* Timer callback */ -void timer_update(user_timer_t timer -{ - attr_container_t *event; - - event = attr_container_create("event"); - attr_container_set_string(&event, - "warning", - "temperature is over high"); - - api_publish_event("alert/overheat", - FMT_ATTR_CONTAINER, - event, - attr_container_get_serialize_length(event)); - - attr_container_destroy(event); -} - -void on_init() -{ - user_timer_t timer; - timer = api_timer_create(1000, true, true, timer_update); -} - -void on_destroy() -{ -} -``` - -Below is the reference implementation of the sub application. -``` C -void overheat_handler(request_t *event) -{ - printf("Event: %s\n", event->url); - - if (event->payload != NULL && event->fmt == FMT_ATTR_CONTAINER) - attr_container_dump((attr_container_t *) event->payload); -} - -void on_init( -{ - api_subscribe_event ("alert/overheat", overheat_handler); -} - -void on_destroy() -{ -} -``` -**Note:** You can also subscribe this event from host side by using host tool. Please refer `samples/simple` project for detail usage. - - -## Sensor API - -The API set is defined in the header file ```core/app-framework/sensor/app/wa-inc/sensor.h```. - -Here is a reference of how to use sensor API's: - -``` C -static sensor_t sensor = NULL; - -/* Sensor event callback*/ -void sensor_event_handler(sensor_t sensor, attr_container_t *event, - void *user_data) -{ - printf("### app get sensor event\n"); - attr_container_dump(event); -} - -void on_init() -{ - char *user_data; - attr_container_t *config; - - printf("### app on_init 1\n"); - /* open a sensor */ - user_data = malloc(100); - printf("### app on_init 2\n"); - sensor = sensor_open("sensor_test", 0, sensor_event_handler, user_data); - printf("### app on_init 3\n"); - - /* config the sensor */ - sensor_config(sensor, 1000, 0, 0); - printf("### app on_init 4\n"); -} - -void on_destroy() -{ - if (NULL != sensor) { - sensor_config(sensor, 0, 0, 0); - } -} -``` - -## Connection API: - -The API set is defined in the header file `core/app-framework/connection/app/wa-inc/connection.h` - -Here is a reference of how to use connection API's: -``` C -/* User global variable */ -static int num = 0; -static user_timer_t g_timer; -static connection_t *g_conn = NULL; - -void on_data1(connection_t *conn, - conn_event_type_t type, - const char *data, - uint32 len, - void *user_data) -{ - if (type == CONN_EVENT_TYPE_DATA) { - char message[64] = {0}; - memcpy(message, data, len); - printf("Client got a message from server -> %s\n", message); - } else if (type == CONN_EVENT_TYPE_DISCONNECT) { - printf("connection is close by server!\n"); - } else { - printf("error: got unknown event type!!!\n"); - } -} - -/* Timer callback */ -void timer1_update(user_timer_t timer) -{ - char message[64] = {0}; - /* Reply to server */ - snprintf(message, sizeof(message), "Hello %d", num++); - api_send_on_connection(g_conn, message, strlen(message)); -} - -void my_close_handler(request_t * request) -{ - response_t response[1]; - - if (g_conn != NULL) { - api_timer_cancel(g_timer); - api_close_connection(g_conn); - } - - make_response_for_request(request, response); - set_response(response, DELETED_2_02, 0, NULL, 0); - api_response_send(response); -} - -void on_init() -{ - user_timer_t timer; - attr_container_t *args; - char *str = "this is client!"; - - api_register_resource_handler("/close", my_close_handler); - - args = attr_container_create(""); - attr_container_set_string(&args, "address", "127.0.0.1"); - attr_container_set_uint16(&args, "port", 7777); - - g_conn = api_open_connection("TCP", args, on_data1, NULL); - if (g_conn == NULL) { - printf("connect to server fail!\n"); - return; - } - - printf("connect to server success! handle: %p\n", g_conn); - - /* set up a timer */ - timer = api_timer_create(1000, true, false, timer1_update); - api_timer_restart(timer, 1000); -} - -void on_destroy() -{ - -} -``` - -## GUI API - -The API's is listed in header file ```core/app-framework/wgl/app/wa-inc/wgl.h``` which is implemented based on open soure 2D graphic library [LittlevGL](https://docs.littlevgl.com/en/html/index.html). - -``` C -static void btn_event_cb(wgl_obj_t btn, wgl_event_t event); - -uint32_t count = 0; -char count_str[11] = { 0 }; -wgl_obj_t hello_world_label; -wgl_obj_t count_label; -wgl_obj_t btn1; -wgl_obj_t label_count1; -int label_count1_value = 0; -char label_count1_str[11] = { 0 }; - -void timer1_update(user_timer_t timer1) -{ - if ((count % 100) == 0) { - snprintf(count_str, sizeof(count_str), "%d", count / 100); - wgl_label_set_text(count_label, count_str); - } - ++count; -} - -void on_init() -{ - hello_world_label = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); - wgl_label_set_text(hello_world_label, "Hello world!"); - wgl_obj_align(hello_world_label, (wgl_obj_t)NULL, WGL_ALIGN_IN_TOP_LEFT, 0, 0); - - count_label = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); - wgl_obj_align(count_label, (wgl_obj_t)NULL, WGL_ALIGN_IN_TOP_MID, 0, 0); - - btn1 = wgl_btn_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); /*Create a button on the currently loaded screen*/ - wgl_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ - wgl_obj_align(btn1, (wgl_obj_t)NULL, WGL_ALIGN_CENTER, 0, 0); /*Align below the label*/ - - /*Create a label on the button*/ - wgl_obj_t btn_label = wgl_label_create(btn1, (wgl_obj_t)NULL); - wgl_label_set_text(btn_label, "Click ++"); - - label_count1 = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); - wgl_label_set_text(label_count1, "0"); - wgl_obj_align(label_count1, (wgl_obj_t)NULL, WGL_ALIGN_IN_BOTTOM_MID, 0, 0); - - /* set up a timer */ - user_timer_t timer; - timer = api_timer_create(10, true, false, timer1_update); - if (timer) - api_timer_restart(timer, 10); - else - printf("Fail to create timer.\n"); -} - -static void btn_event_cb(wgl_obj_t btn, wgl_event_t event) -{ - if(event == WGL_EVENT_RELEASED) { - label_count1_value++; - snprintf(label_count1_str, sizeof(label_count1_str), - "%d", label_count1_value); - wgl_label_set_text(label_count1, label_count1_str); - } -} - -``` - -Currently supported widgets include button, label, list and check box and more wigdet would be provided in future. \ No newline at end of file diff --git a/doc/wasm_c_api.md b/doc/wasm_c_api.md new file mode 100644 index 0000000000..03d5cffd4f --- /dev/null +++ b/doc/wasm_c_api.md @@ -0,0 +1,65 @@ +# wasm-c-api introduction + +wasm-c-api is an engine-agnostic API to embed a WASM engine. +In wasm-micro-runtime, it's provided by the header file `wasm_c_api.h`. +Its functionalities are overlapping with `wasm_export.h`, which is +a native API of wasm-micro-runtime. An embedder is supposed to pick +one of these APIs, rather than mixing both of them. + +All samples come from the commit 340fd9528cc3b26d22fe30ee1628c8c3f2b8c53b +of [wasm-c-api](https://github.com/WebAssembly/wasm-c-api). + +Developer can learn these _APIs_ from +[wasm.h](https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.h). + +And here are [examples](https://github.com/WebAssembly/wasm-c-api/tree/master/example) which +are helpful. + +## FYI + +- The thread model of _wasm_c_api_ is + + - An `wasm_engine_t` instance may only be created once per process + - Every `wasm_store_t` and its objects may only be accessed in a single thread + +- `wasm_engine_new`, `wasm_engine_new_with_config`, `wasm_engine_new_with_args`, + `wasm_engine_delete`should be called in a thread-safe environment. Such + behaviors are not recommended, and please make sure an appropriate calling + sequence if it has to be. + + - call `wasm_engine_new` and `wasm_engine_delete` in different threads + - call `wasm_engine_new` or `wasm_engine_delete` multiple times in + different threads + +## unsupported list + +Currently WAMR supports most of the APIs, the unsupported APIs are listed as below: + +- References + +```c +WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); +WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*); +``` + +- Several Module APIs + +```c +WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out); +WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*); +``` + +Currently growing a table or memory by wasm opcode is supported and it is not supported to grow them +by host-side function callings. + +- Table Grow APIs + +```c +WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init); +``` + +- Memory Grow APIs + +```c +WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta); +``` diff --git a/doc/xip.md b/doc/xip.md new file mode 100644 index 0000000000..a76c7615c8 --- /dev/null +++ b/doc/xip.md @@ -0,0 +1,131 @@ +# WAMR XIP (Execution In Place) feature introduction + +Some IoT devices may require to run the AOT file from flash or ROM which is read-only, so as to reduce the memory consumption, or resolve the issue that there is no executable memory available to run AOT code. In such case, the AOT code inside the AOT file shouldn't be duplicated into memory and shouldn't be modified (or patched) by the AOT relocations. To address this, WAMR implements the XIP (Execution In Place) feature, which generates the AOT relocations as few as possible: +- In the AOT code, an AOT function calls other functions with indirect mode: it doesn't call other functions directly, but looks up their pointers from the function pointer table passed by its first argument exec_env, and then calls the function pointer found. By this way the relocations to other functions are eliminated. +- Eliminate the calls to the LLVM intrinsic functions, or, replace calling them with calling runtime self implemented functions instead, e.g. the calling to `llvm.experimental.constrained.fadd.f32` is replaced by the calling to `aot_intrinsic_fadd_f32`. + +The XIP file is an AOT file without (or with few) relocations to patch the AOT code (or text section). Developer can use the option `--enable-indirect-mode --disable-llvm-intrinsics` for wamrc to generate the AOT file, e.g.: +```bash +wamrc --enable-indirect-mode --disable-llvm-intrinsics -o +or +wamrc --xip -o +``` + +Note: --xip is a short option for --enable-indirect-mode --disable-llvm-intrinsics + +## Known issues + +There may be some relocations to the ".rodata" like sections which require to patch the AOT code. More work will be done to resolve it in the future. + +## Tuning the XIP intrinsic functions + +WAMR provides a default mapping table for some targets, but it may not be the best one for your target. And it doesn't cover all the supported targets. + +So, wamrc provides the option `--enable-builtin-intrinsics=` to make it possible to tune the intrinsic functions for your target. + +Firstly, you should understand why we don't use the LLVM intrinsic functions directly. The reason is that the LLVM intrinsic functions can't map to the native instructions directly, e.g. the LLVM intrinsic function `i32.div_s` can't map to the native instruction if the target doesn't support the division instruction, it will be translated to a function call to the runtime function from libgcc/compiler-rt. This will cause the AOT code to have the relocations to the libgcc/compiler-rt, which is not acceptable for the XIP feature. + +So, we need to replace the LLVM intrinsic functions with the runtime self implemented functions, which can be called through the function pointer table (--enable-indirect-mode) and don't have the relocations to the libgcc/compiler-rt (--disable-llvm-intrinsics). + +Available intrinsic functions for tuning: + +| LLVM intrinsic function | Explanation | +| --- | --- | +| llvm.experimental.constrained.fadd.f32 | float32 add | +| llvm.experimental.constrained.fadd.f64 | float64 add | +| llvm.experimental.constrained.fsub.f32 | float32 sub | +| llvm.experimental.constrained.fsub.f64 | float64 sub | +| llvm.experimental.constrained.fmul.f32 | float32 mul | +| llvm.experimental.constrained.fmul.f64 | float64 mul | +| llvm.experimental.constrained.fdiv.f32 | float32 div | +| llvm.experimental.constrained.fdiv.f64 | float64 div | +| llvm.fabs.f32 | float32 abs | +| llvm.fabs.f64 | float64 abs | +| llvm.ceil.f32 | float32 ceil | +| llvm.ceil.f64 | float64 ceil | +| llvm.floor.f32 | float32 floor | +| llvm.floor.f64 | float64 floor | +| llvm.trunc.f32 | float32 trunc | +| llvm.trunc.f64 | float64 trunc | +| llvm.rint.f32 | float32 rint | +| llvm.rint.f64 | float64 rint | +| llvm.sqrt.f32 | float32 sqrt | +| llvm.sqrt.f64 | float64 sqrt | +| llvm.copysign.f32 | float32 copysign | +| llvm.copysign.f64 | float64 copysign | +| llvm.minnum.f32 | float32 minnum | +| llvm.minnum.f64 | float64 minnum | +| llvm.maxnum.f32 | float32 maxnum | +| llvm.maxnum.f64 | float64 maxnum | +| llvm.ctlz.i32 | int32 count leading zeros | +| llvm.ctlz.i64 | int64 count leading zeros | +| llvm.cttz.i32 | int32 count trailing zeros | +| llvm.cttz.i64 | int64 count trailing zeros | +| llvm.ctpop.i32 | int32 count population | +| llvm.ctpop.i64 | int64 count population | +| f64_convert_i32_s | int32 to float64 | +| f64_convert_i32_u | uint32 to float64 | +| f32_convert_i32_s | int32 to float32 | +| f32_convert_i32_u | uint32 to float32 | +| f64_convert_i64_s | int64 to float64 | +| f64_convert_i64_u | uint64 to float64 | +| f32_convert_i64_s | int64 to float32 | +| f32_convert_i64_u | uint64 to float32 | +| i32_trunc_f32_s | float32 to int32 | +| i32_trunc_f32_u | float32 to uint32 | +| i32_trunc_f64_s | float64 to int32 | +| i32_trunc_f64_u | float64 to uint32 | +| i64_trunc_f64_s | float64 to int64 | +| i64_trunc_f64_u | float64 to uint64 | +| i64_trunc_f32_s | float32 to int64 | +| i64_trunc_f32_u | float32 to uint64 | +| f32_demote_f64 | float64 to float32 | +| f64_promote_f32 | float32 to float64 | +| f32_cmp | float32 compare | +| f64_cmp | float64 compare | +| i64.div_s | int64 div | +| i64.div_u | uint64 div | +| i32.div_s | int32 div | +| i32.div_u | uint32 div | +| i64.rem_s | int64 rem | +| i64.rem_u | uint64 rem | +| i32.rem_s | int32 rem | +| i32.rem_u | uint32 rem | +| i64.or | int64 or | +| i64.and | int64 and | +| i32.const | emit i32 const into constant table | +| i64.const | emit i64 const into constant table | +| f32.const | emit f32 const into constant table | +| f64.const | emit f64 const into constant table | + +And also provide combined intrinsic functions to simplify the tuning: + +* all: all the above intrinsic functions +* i32.common: i32.div_s, i32.div_u, i32.rem_s, i32.rem_u +* i64.common: i64.div_s, i64.div_u, i64.rem_s, i64.rem_u, i64.or, i64.and +* f32.common: f32_cmp, llvm.experimental.constrained.fadd.f32, llvm.experimental.constrained.fsub.f32, llvm.experimental.constrained.fmul.f32, llvm.experimental.constrained.fdiv.f32, llvm.fabs.f32, llvm.ceil.f32, llvm.floor.f32, llvm.trunc.f32, llvm.rint.f32, llvm.sqrt.f32, llvm.copysign.f32, llvm.minnum.f32, llvm.maxnum.f32 +* f64.common: f32_demote_f64, f64_promote_f32, f64_cmp, llvm.experimental.constrained.fadd.f64, llvm.experimental.constrained.fsub.f64, llvm.experimental.constrained.fmul.f64, llvm.experimental.constrained.fdiv.f64, llvm.fabs.f64, llvm.ceil.f64, llvm.floor.f64, llvm.trunc.f64, llvm.rint.f64, llvm.sqrt.f64, llvm.copysign.f64, llvm.minnum.f64, llvm.maxnum.f64 +* f32xi32: i32_trunc_f32_s, i32_trunc_f32_u, f32_convert_i32_s, f32_convert_i32_u +* f64xi32: i32_trunc_f64_s, i32_trunc_f64_u, f64_convert_i32_s, f64_convert_i32_u +* f32xi64: i64_trunc_f32_s, i64_trunc_f32_u, f32_convert_i64_s, f32_convert_i64_u +* f64xi64: i64_trunc_f64_s, i64_trunc_f64_u, f64_convert_i64_s, f64_convert_i64_u +* constop: i32.const, i64.const, f32.const, f64.const +* fpxint: f32xi32, f64xi32, f32xi64, f64xi64 +* fp.common: f32.common, f64.common + + +### Example + +For ARM Cortex-M55, since it has double precision floating point unit, so it can support f32/f64 operations. But as a 32-bit MCU, it can only support 32-bit integer operations. So we can use the following command to generate the XIP binary: + +``` +wamrc --target=thumbv8m.main --cpu=cortex-m55 --xip --enable-builtin-intrinsics=i64.common -o hello.aot hello.wasm +``` + +For ARM Cortex-M3, since it has no floating point unit, and it can only support 32-bit integer operations. So we can use the following command to generate the XIP binary: + +``` +wamrc --target=thumbv7m --cpu=cortex-m3 --xip --enable-builtin-intrinsics=i64.common,fp.common,fpxint -o hello.aot hello.wasm +``` + +Other platforms can be tuned in the same way, which intrinsic should be enabled depends on the target platform's hardware capability. diff --git a/gitbook/advance-tutorial/README.md b/gitbook/advance-tutorial/README.md new file mode 100644 index 0000000000..4a02cc026c --- /dev/null +++ b/gitbook/advance-tutorial/README.md @@ -0,0 +1,7 @@ +# Advance tutorial + +Welcome to the chapter of the advanced tutorial. + +If you care about performance(don't we all?), want to know whether WAMR stands out among other wasm runtimes with respect to your demands, or wish to fine-tune your wasm application's memory footprint. You could refer to [this section](performance-benchmark/README.md) + +In later sections, you can find the tutorial on how to use [application framework](../../doc/wamr_api.md) and [dynamic management](remote-applicatoin-management/README.md). Also, there is a tutorial on [how to port WAMR to the platform](../../doc/port_wamr.md) diff --git a/gitbook/advance-tutorial/performance-benchmark/README.md b/gitbook/advance-tutorial/performance-benchmark/README.md new file mode 100644 index 0000000000..84095e5d0f --- /dev/null +++ b/gitbook/advance-tutorial/performance-benchmark/README.md @@ -0,0 +1,18 @@ +# Performance Test + +Like word on the street said(no way it's just us saying!) or you may saw in previous chapters(maybe multiple times), WAMR is a **lightweight** standalone WebAssembly (WASM) runtime with **small footprint**, **high performance** and highly configurable features. + +Well, you don't have to take our word for it. You could run the [Benchmarks in our repo](https://github.com/bytecodealliance/wasm-micro-runtime/tree/main/tests/benchmarks) and decide whether the performance is good enough. + +We provide multiple benchmarks that you could try: + +- [PolyBench](../../../tests/benchmarks/polybench/README.md) +- [CoreMark](../../../tests/benchmarks/coremark/README.md) +- [Sightglass](../../../tests/benchmarks/sightglass/README.md) +- [JetStream2](../../../tests/benchmarks/jetstream/README.md) + +For the memory footprint, you can refer to the links below. + +- [Performance and footprint data](https://github.com/bytecodealliance/wasm-micro-runtime/wiki/Performance): checkout [here](https://github.com/bytecodealliance/wasm-micro-runtime/wiki/Performance) for the performance and footprint data + +And in the next section, we provide tutorials on memory usage tuning. You can [profile memory usage](../../../doc/build_wamr.md#enable-memory-profiling-experiment) and [tunning memory usage](../../../doc/memory_tune.md) diff --git a/gitbook/advance-tutorial/remote-applicatoin-management/README.md b/gitbook/advance-tutorial/remote-applicatoin-management/README.md new file mode 100644 index 0000000000..d1d2568feb --- /dev/null +++ b/gitbook/advance-tutorial/remote-applicatoin-management/README.md @@ -0,0 +1,7 @@ +# Remote application management + +The WAMR application manager supports **remote application management**(check out local directory {WAMR-DIR}/core/app-mgr or [same directory on GitHub](https://github.com/bytecodealliance/wasm-micro-runtime/tree/main/core/app-mgr) for more) from the host environment or the cloud through any physical communications such as TCP, UPD, UART, BLE, etc. Its modular design makes it able to support application management for different managed runtimes. + +The tool **host_tool** (check out local directory {WAMR-DIR}/test-tools/host-tool or [same directory on GitHub](https://github.com/bytecodealliance/wasm-micro-runtime/tree/main/test-tools/host-tool) for more) communicates to the WAMR app manager for installing/uninstalling the wasm applications on the companion chip from the host system. + +We have two example demos of the use of **host_tool**. One is the [simple example](../../../samples/simple/README.md) using the tool "host_tool" to remotely install/uninstall wasm applications from the WAMR runtime over either TCP socket or UART cable; the other is the [IoT App Store Demo](../../../test-tools/IoT-APP-Store-Demo/README.md) showing the concept of remotely managing the device applications from the cloud. diff --git a/gitbook/appendix/background_knowledge.md b/gitbook/appendix/background_knowledge.md new file mode 100644 index 0000000000..61c1719d2f --- /dev/null +++ b/gitbook/appendix/background_knowledge.md @@ -0,0 +1,61 @@ +# Some background knowledge + +In this section, we aggregate some basic background knowledge and jargon in our project field. This section could be served as a refresher for those who have left academia for a while and cannot fully recall all the weary details and exact meaning of jargon in the Compiler course. Also, it would be a great primer for those who did not take such a course and are interested in such a field(and, of course, our fantastic WAMR project). + +We think providing such a section would be nice so that you do not have to Google around. If there is anything you find inaccurate, you think should be included, or even better, you have something for us that would perfect this section, do feel free to reach out to us on [GitHub](https://github.com/bytecodealliance/wasm-micro-runtime)! + +Let's dive right into our exciting recitation/learning journey without further ado! + +## 1. Compiler + +### 1.1 What is a Compiler? + +Strictly speaking(formal definition you usually find in textbooks), the compiler is a special computer program, a system program(serves as a platform for other software), to be more precise. It takes a source program as input and outputs a target program. The source program is written in the source programming language, and usually, it is a high-level programming language such as C/C++, Java, Rust, and so on. The target program is written in a target programming language would be a low-level programming language like assembly. Take C/C++ as an example, the input for the GCC compiler(component) is a C/C++ translation unit(a source file along with any header it used), and the output is platform-dependent assembly code. + +However, in our daily life, what we usually mean when we refer to the word compiler is the compiler toolchain, which comprises a compiler, assembler, and linker. The assembler is in charge of translating the compiled translation unit(object file) from assembly to truly machine-readable machine code. The linker is used to link all the parts of the program(object files) into one executable file. Together, they can translate our human-readable source code(potentially many files) into a program that can run on the machine. + +For now, we will mainly focus on the more strict definition because I think the concept and algorithm compiler use more closely pertain to our WAMR project. + + + +### 1.2 Structure and algorithm involved + + + +Since we alright know what a compiler is, now let's learn more details about compilers. First, let's talk about the structure of the compiler and the algorithm related to each part. Typically, the compiler consists of three parts, Front End, Optimizer and Back End: + +- Front End: in some sense, this part is more "mature." The theory involved and actual implementation is more or less stable nowadays. Its primary purpose is to gather textual information from source-language programs and understand it syntactically and semantically. After that, it encodes the knowledge it has into Intermediate Representation. The theory behind Front End is formal language theory(Scanners & Parsers) and lattice theory(Elaboration for type checking). + +- Optimizer: as the name suggests, the Optimizer's goal is to optimize our program's performance. Clever readers may be conscious of the difficulty when they hear the word "optimize." Indeed, the Optimizer is very challenging to design and implement since it's a vital part of compiler infrastructure and imposes a heavy performance impact. It analyses the input IR and transforms it into definitive IR, usually through multiple passes, gradually accumulating knowledge of the program and applying a better(hopefully) transformation to it. The output(definitive IR) is semantically equivalent to the input IR to preserve the original meaning of the program we are compiling. The theories and algorithms that could be used for Optimizer are too many to list here. Here are examples: Number theory, some graph algorithms for static analysis, and fixed-point algorithms for data-flow analysis. It's still an active field that attracts many people to research. + +- Back End: the Back End is in charge of mapping programs (in IR form) to low-level code forms that can run on the target machine. Usually, there is more than one Back End, so the compiler is portable for different platforms (ISA). Its main functionality includes instruction selection, register allocation, and instruction scheduling, in which many algorithms are applied, like heuristic search, graph coloring, and some dynamic programming. Like Optimizer, the Back End has many open problems to tackle and also is a field many people hold great interest in. + +## 2. Interpreter + +### 2.1 What is an Interpreter? + +The Interpreter is also a system computer program. Like the compiler, it takes a source program as input; but instead of outputting a target program, it directly executes the program line by line and returns the corresponding results. One thing worth noting is that it's not uncommon for an interpreter to adapt widely used techniques in the compilers to improve its performance. Sometimes they are even used together. + +Based on the levels of the source language(high or low) and compilation strategies, the interpreters can be divided into several different categories. Let's look at them in more detail in the following section. + +### 2.2 Technique and jargon in Interpreter + +- Bytecode: + + The bytecode is a kind of low-level programming language in a very highly optimized and compact format. It could be the target language for the compiler. Because the instruction-like bytecode can be executed line by line in an interpreter on any platform, regardless of what hardware-related ISA it uses, it is also called p-code(portable). One example of bytecode you may be familiar with is Java bytecode. + +- Ahead-of-time(AOT) and Just-in-time(JIT) compilation: + + - AOT: as the name suggests, the AOT compilation means that the compilation happens before the program run time. Normally the target language after AOT compilation is some low-level machine code or bytecode. Then the compiled code can be executed by either a process VM or a normal computer. + + - JIT: just in time compilation is a technique widely adopted by the Interpreter to improve its performance. It detects the heavily used code section when interpreting the program and compiles them into more efficient machine code. When that code section is called again, the machine code is executed rather than having the bytecode interpreted. + +## 3. Virtual machines + +When it comes to the word "Virtual Machines," we usually would remember or refer to that System virtual machines managed by hypervisors such as KVM, VirtualBox, or VMware. We often use them as a substitute for real computers to resolve dependency or compatibility issues for courses or daily work. + +But there is also another type of virtual machine you may have heard of(even you may get really confused at first) and related more closely to the field where our project is. Process (application) virtual machines provide an environment independent of hardware, aiming to run computer programs written in a certain language. Take JVM as an example. It provides an environment for Java bytecode to execute across many platforms. + +## 4. Runtime system + +It's a rather vague term that is really difficult to explain or understand. To make things worse, when people sometimes refer to it as runtime, it's easily confused with compilation runtime, runtime library. The runtime system is an infrastructure that participates in the creation and running of our program. Typically, the components are the execution environment(Application VM maybe) to provide a place for the program to run, and the compiler front end or/and compiler back end to do the necessary analysis, transformation(from source code to bytecode), and optimization. diff --git a/gitbook/appendix/webassembly_details.md b/gitbook/appendix/webassembly_details.md new file mode 100644 index 0000000000..ea11a4ffb4 --- /dev/null +++ b/gitbook/appendix/webassembly_details.md @@ -0,0 +1,6 @@ +--- +description: "This page is under construction/refinement. p.s. wanna hear a construction joke? we are still working on it" +--- +# WebAssembly details + +Meanwhile, if you can't wait to learn more about Wasm, check out this book: _WebAssembly in Action_. It's a great book showcasing wasm basics and how to use wasm with JavaScript inside a browser. diff --git a/gitbook/basics/getting-started/README.md b/gitbook/basics/getting-started/README.md new file mode 100644 index 0000000000..4a6ca20e6b --- /dev/null +++ b/gitbook/basics/getting-started/README.md @@ -0,0 +1,13 @@ +# Getting started: a hello world program + +In this chapter, you'll learn how to run a simple hello world wasm program on your host or the Docker environment using WAMR. The docker tutorial is recommended so you don't have to worry about all the platform-related dependencies and compatibility problems. The hello world program will give you a taste of what our WAMR could do as server-side runtime and ready you for a more detailed guide at the end of this chapter. The [latter guide](../../../doc/build_wasm_app.md) covers the meaning of the compile and build option in detail and gives suggestions on fine-tuning your wasm module. More example programs can be found in [chapter 4. features](../../features/README.md) + +Now, here is the last piece of gibberish before you get your hand dirty: + +Clone our source code repo and use + +```sh +git clone https://github.com/bytecodealliance/wasm-micro-runtime.git +``` + +Or download from use any way you like diff --git a/gitbook/basics/getting-started/host_prerequsites.md b/gitbook/basics/getting-started/host_prerequsites.md new file mode 100644 index 0000000000..d6ab33cf82 --- /dev/null +++ b/gitbook/basics/getting-started/host_prerequsites.md @@ -0,0 +1,40 @@ +# Prerequisites for your host environment + +## Ubuntu + +First, install the needed packages and libraries. + +```sh +apt-get update \ + && apt-get install -y apt-transport-https apt-utils build-essential \ + ca-certificates curl g++-multilib git gnupg \ + libgcc-9-dev lib32gcc-9-dev lsb-release \ + ninja-build ocaml ocamlbuild python2.7 \ + software-properties-common tree tzdata \ + unzip valgrind vim wget zip --no-install-recommends +``` + +Then install CMake and wasi-sdk-16.0 + +```sh +wget --progress=dot:giga -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg > /dev/null \ + && echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ bionic main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null \ + && apt-get update \ + && rm /usr/share/keyrings/kitware-archive-keyring.gpg \ + && apt-get install -y kitware-archive-keyring --no-install-recommends \ + && apt-get install -y cmake --no-install-recommends + +wget -c --progress=dot:giga https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-16/wasi-sdk-16.0-linux.tar.gz -P /opt \ + && tar xf /opt/wasi-sdk-16.0-linux.tar.gz -C /opt \ + && ln -fs /opt/wasi-sdk-16.0 /opt/wasi-sdk \ + && rm /opt/wasi-sdk-16.0-linux.tar.gz +``` + +This should be sufficient to build WAMR and run our hello world program. + \ No newline at end of file diff --git a/gitbook/basics/getting-started/on_docker.md b/gitbook/basics/getting-started/on_docker.md new file mode 100644 index 0000000000..2dd7c77a71 --- /dev/null +++ b/gitbook/basics/getting-started/on_docker.md @@ -0,0 +1,26 @@ +# Using docker + +Now that we have set up docker, we could run the following command directly in VS Code terminal(or the bash of your if you prefer ssh docker container directly). + +Similarly, build iwasm vmcore. + +```sh +cd product-mini/platforms/linux +mkdir build && cd build +cmake .. +make +``` + +Then you are ready to go to the directory that contains the hello world program and copy our iwasm vmcore + +```sh +cp iwasm ../../../app-samples/hello-world +cd ../../../app-samples/hello-world +./build.sh +``` + +Now you can execute your first wasm program! + +```sh +./iwasm test.wasm +``` diff --git a/gitbook/basics/getting-started/on_host.md b/gitbook/basics/getting-started/on_host.md new file mode 100644 index 0000000000..df8689ffb7 --- /dev/null +++ b/gitbook/basics/getting-started/on_host.md @@ -0,0 +1,26 @@ +# Compile, build and test hello world on the host + +Now we have our host set up, we can build our hello world program and run it using WAMR. + +First, build iwasm vmcore on your platform. + +```sh +cd ${WAMR-dir}/product-mini/platforms/${your platform} +mkdir build && cd build +cmake .. +make +``` + +Then you are ready to go to the directory that contains the hello world program and copy our iwasm vmcore + +```sh +cp iwasm ../../../app-samples/hello-world +cd ${WAMR-dir}/product-mini/app-samples/hello-world +./build.sh +``` + +Now you could execute your first wasm program! + +```sh +./iwasm test.wasm +``` diff --git a/gitbook/basics/introduction/README.md b/gitbook/basics/introduction/README.md new file mode 100644 index 0000000000..6886d072b8 --- /dev/null +++ b/gitbook/basics/introduction/README.md @@ -0,0 +1,5 @@ +# Introduction + +In this chapter, we will introduce you to some basic knowledge about [WebAssembly](./webassembly.md) and our project [WAMR](./wamr_project.md). And discuss the security feature that Webassembly language itself and WAMR's enhancement can bring in [this section](./security_feature.md) + +We understand because our backgrounds vary, some terms in the following section may sound familiar but vague. We have a primer in [Appendix A](../../appendix/background_knowledge.md) gathering some details about background knowledge(compiler, interpreter, runtime system, all other jargon) that may be helpful and free you from googling around. You are more than welcome to check it out. diff --git a/gitbook/basics/introduction/security_feature.md b/gitbook/basics/introduction/security_feature.md new file mode 100644 index 0000000000..f375b99f1c --- /dev/null +++ b/gitbook/basics/introduction/security_feature.md @@ -0,0 +1,147 @@ +# The Security of WebAssembly and WAMR's implementation + +WebAssembly is a cutting-edge programming language that helps Web applications such as PhotoShop Online run at native speed in the browser and offers a sandbox mechanism to protect the host environment from malicious attacks cross the world. Beyond the browser, the Wasm can be executed in standalone runtime such as WAMR safely without the need of additional security support from OS and HW. + +## WebAssembly Security Overview + +The security features of WebAssembly(More detailed Wasm language-level security features can be found on the official [WebAssembly website](https://webassembly.org/docs/security/)) + +WebAssembly (Wasm) is designed with two key security goals: + +1. protecting users from malicious or faulty modules + +2. providing developers with robust tools for building secure applications. + +### User Protection + +Each WebAssembly module executes within a sandboxed environment separated from the host runtime using fault isolation techniques. This implies: + +- Applications execute independently, and can't escape the sandbox without going through appropriate APIs. +- Applications generally execute deterministically with limited exceptions. + +Modules must also comply with the security policies of the host environment, such as the same-origin policy in browsers or POSIX on other platforms. + +### Developer Safety Tools + +WebAssembly's design emphasizes security by removing unsafe execution features while maintaining compatibility with C/C++ programs. + +Key safety features include: + +- **Control-flow Integrity (CFI):** + - Modules must declare all accessible functions and their types at load time, enforcing structured control flow. + - Immutable, non-observable compiled code prevents control-flow hijacking attacks. + +- **Function Calls:** + - Calls must reference a valid function index. + - Indirect function calls are checked at runtime for type signature compatibility. + - A protected call stack prevents buffer overflows, ensuring safe returns. + - Branches are restricted to valid destinations within the current function. + +- **Variable Handling:** + - Local variables (fixed scope) are initialized to zero and stored in the protected call stack. + - Global variables are stored in the global index space and can be imported from external modules. + - Variables with unclear static scope (e.g., structs or address-of operator) are stored in isolated linear memory, which has bounds checking and zero-initialization by default. + +- **Traps:** + - Used to terminate execution and signal errors (e.g., invalid index, type mismatch, stack overflow, out-of-bounds memory access, or illegal arithmetic). + - In a browser, traps trigger a JavaScript exception. Future updates may support custom module-defined trap handlers. + +Future improvements may introduce multiple memory sections and finer memory controls (e.g., shared memory, page protection). + +### Memory Safety + +WebAssembly improves memory safety by eliminating common bugs found in traditional C/C++ programs: + +- **Buffer Overflows**: Local and global variables are fixed-size and stored by index, preventing buffer overflows from affecting adjacent memory. Linear memory regions, though, can overwrite objects, but bounds checking and control-flow integrity (CFI) prevent direct code injection, so mitigation like DEP or SSP is unnecessary. + +- **Pointer Safety**: Unsafe pointer usage, like dereferencing unallocated memory or accessing freed memory, is minimized. WebAssembly removes pointer semantics for function calls and variables with fixed scope, and invalid index references result in load-time validation errors or runtime traps. Linear memory is bounds-checked at the region level and zero-initialized by default. + +- **Control Flow Protection**: Although WebAssembly prevents direct code injection, code reuse attacks targeting indirect calls are still possible. However, conventional ROP attacks are infeasible due to CFI, which enforces valid call targets declared at load time. + +- **Potential Vulnerabilities**: Race conditions (e.g., TOCTOU) and side-channel attacks (e.g., timing attacks) are possible, as WebAssembly offers no scheduling guarantees. Future enhancements may introduce memory randomization, code diversification, and bounded pointers to strengthen protection. + +### Control-Flow Integrity (CFI) + +Wasm ensures CFI for both direct and indirect function calls as well as function returns. It uses explicit function section indexes and runtime type checks to verify safe transitions. However, as mentioned above, while these mechanisms prevent most code injection, indirect call exploits using code reuse techniques are still possible. + +Developers can enhance security by enabling fine-grained CFI through Clang/LLVM's WebAssembly support, which adds richer type-level checks and mitigates indirect call attacks, albeit with minor performance trade-offs. + +## WAMR Security Features + +The WebAssembly Micro Runtime (WAMR) is designed to provide an efficient, secure, and lightweight runtime for WebAssembly on standalone devices. It offers a full coverage of the WebAssembly specification and added additional security enhancements to ensure safe execution of Wasm modules. + +### Wasm Language Security Features + +WAMR enforces WebAssembly language-level security features rigorously, ensuring that each Wasm module undergoes comprehensive validation at the loading phase and that execution conforms to the WebAssembly specification during runtime. + +#### Module Validation + +Before execution, WAMR validates the Wasm module to ensure it adheres to the WebAssembly specification. This involves several key checks: + +- **Format Validation**: Ensuring the binary is well-formed and compliant with the Wasm format. This checks the structure, ensuring correct definitions for functions, memory segments, tables, and types. + +- **Type Checking**: All function signatures, local variables, and global variables are verified against their declared types. This ensures type safety across calls and memory operations. + +- **Control Flow Integrity**: Verifies the function call graph to ensure that all function indices and signatures are valid and that function calls do not violate control-flow safety rules. + +- **Operand Stack Integrity**: WAMR ensures that operand stack overflows and underflows are checked during validation. For each function, the number of values pushed and popped from the operand stack must match the declared function signature, avoiding stack imbalances. + +- **Memory and Table Boundaries**: Ensures that memory and table sizes do not exceed predefined limits and that access to these regions remains within bounds. + +#### Module Execution + +During runtime, WAMR ensures that execution strictly conforms to the WebAssembly spec and maintains the security guarantees made at load time: + +- **Memory Safety**: Memory access, both direct and indirect, is rigorously checked. WAMR prevents out-of-bounds access, helping mitigate common vulnerabilities like buffer overflows. + + - Implementation of **Boundary Checks**: WAMR can leverage either software boundary checks or hardware boundary checks. For software boundary checks, before each memory access, the address is validated to ensure it falls within the allowable bounds of the allocated memory. For hardware boundary checks, protection mechanisms such as `mmap`-based memory protection, where sections of memory can be made non-writable or non-executable to prevent invalid memory address access. + +- Like previously mentioned, applications generally execute deterministically with **limited exceptions**, which can be handled in the runtime rather than simply crushing or causing undefined behavior. The exceptions that WAMR can handle include but are not limited to: + + - EXCE_UNREACHABLE: Triggered when unreachable code is executed. + - EXCE_OUT_OF_MEMORY: Signaled when the runtime runs out of memory. + - EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS: Raised when memory access goes out of bounds. + - EXCE_INTEGER_OVERFLOW: Detects integer overflow during arithmetic operations. + - EXCE_INTEGER_DIVIDE_BY_ZERO: Handles division by zero in integer operations. + - EXCE_INVALID_CONVERSION_TO_INTEGER: Raised when an invalid conversion to an integer occurs. + - EXCE_INVALID_FUNCTION_TYPE_INDEX: Triggered when an invalid function type index is accessed. + - EXCE_INVALID_FUNCTION_INDEX: Signaled when an invalid function index is used. + - EXCE_UNDEFINED_ELEMENT: Raised when accessing an undefined element. + - EXCE_UNINITIALIZED_ELEMENT: Triggered when an uninitialized element is accessed. + - EXCE_CALL_UNLINKED_IMPORT_FUNC: Handles calls to unlinked imported functions. + - EXCE_NATIVE_STACK_OVERFLOW: Triggered when the native stack exceeds its limit. + - EXCE_UNALIGNED_ATOMIC: Raised when an unaligned atomic operation is attempted. + - EXCE_AUX_STACK_OVERFLOW: Signals that the auxiliary stack has overflowed. + - EXCE_AUX_STACK_UNDERFLOW: Triggered when the auxiliary stack is underflowed. + - EXCE_OUT_OF_BOUNDS_TABLE_ACCESS: Raised when accessing a table out of bounds. + - EXCE_OPERAND_STACK_OVERFLOW: Signaled when the operand stack overflows. + +These features, combined with the robust validation and execution checks, ensure that WAMR achieves comprehensive security for running WebAssembly modules. + +### Extra Enhancements on Security + +WAMR goes beyond the standard WebAssembly security features by offering additional mechanisms to enhance the security of applications, particularly in the areas of native API access control and hardware-based security. + +#### Native API Export Control + +WAMR allows WebAssembly applications to interact with the host environment through **exported native APIs**. However, unrestricted access to these APIs can introduce security risks, such as unauthorized system calls or resource manipulation. To mitigate these risks, WAMR implements a **fine-grained access control** mechanism for native APIs: + +- **Restricted API Access**: Developers can explicitly define which native APIs are exposed to WebAssembly modules, limiting the surface area for potential misuse. This allows for precise control over which system resources (e.g., file system, networking, I/O devices) can be accessed by a module. + +- **Custom API Policies**: WAMR supports customizable policies, enabling developers to set permissions and constraints on how WebAssembly modules can interact with native APIs. This is particularly useful for sandboxing untrusted code while still allowing necessary functionality under controlled conditions. + +- **API Call Validation**: All calls to native APIs are validated at runtime to ensure that they conform to the defined policies, preventing unauthorized or malicious API usage. + +#### Intel SGX Remote Attestation + +WAMR enhances security for trusted execution environments (TEEs) through its support for **Intel Software Guard Extensions (SGX)**, which provides hardware-level security features, including **remote attestation**: + +- **SGX Integration**: WAMR can run WebAssembly modules within an SGX enclave, a protected area of execution that provides isolation from the rest of the system. This ensures that even if the host machine is compromised, the code and data within the enclave remain secure. + +- **Remote Attestation**: WAMR supports SGX remote attestation, allowing remote parties to verify that a WebAssembly module is running inside a genuine SGX enclave. This involves generating and sending an **attestation report**, which proves the authenticity of the enclave and the integrity of the code running inside it. + + - **Endorsement of Trusted Execution**: The attestation process ensures that the WebAssembly module and its execution environment have not been tampered with, providing assurance to remote users that the module is running in a secure, trusted state. + +- **Sealing and Unsealing**: WAMR supports SGX's data sealing features, enabling WebAssembly modules to securely store sensitive data on disk. Sealed data can only be accessed by the same enclave in future executions, protecting it from unauthorized access even if the host system is compromised. + +These features enhance WAMR’s security, making it suitable for use cases that require both flexible native API access and strong hardware-backed guarantees of code integrity and confidentiality. diff --git a/gitbook/basics/introduction/wamr_project.md b/gitbook/basics/introduction/wamr_project.md new file mode 100644 index 0000000000..0cba5b084c --- /dev/null +++ b/gitbook/basics/introduction/wamr_project.md @@ -0,0 +1,65 @@ +# WAMR project + + In this section, we will introduce the basics of project WAMR to you. In each brief introduction section, you are more than welcome to jump to details of that section triggering your interest. + +## What is it? + +WebAssembly Micro Runtime (WAMR) is a [Bytecode Alliance](https://bytecodealliance.org/) project. A lightweight standalone WebAssembly (WASM) runtime with a small footprint, high performance, and highly configurable features for applications across from embedded, IoT, edge to Trusted Execution Environment (TEE), smart contract, cloud-native, and so on. + +## Why you may want to use it + + + +As we explained in the previous section, WebAssembly is great for code reuse on the server side with the help of runtime like our Project WAMR. So the most straightforward way is to use WAMR to run your WASM program. + +It's not limited to simply being a command line application that runs your wasm program. You could also use it as a library, integrated into your application to run any wasm program inside your application. Although most user cases are embedding WAMR in their C/C++ program, we do support other [language-binding](../../tutorial/language-embedding/README.md) so that you could use WAMR in some language you prefer. + +## Component of WAMR + + + +There are four parts of WAMR. Two main parts of WAMR are: + +1. The "iwasm" VM core to run WASM applications. It has many features and achieves several functionalities. The complete list of features and examples demonstrating it can be found in [Features](../../features/README.md). Here are some brief introductions of some features that may interest you: + + - Flexibility: It supports multiple running modes to provide the ideal responsive time and performance on your demand. The running mode includes interpreter mode, AOT mode (Ahead-of-Time compilation), and JIT modes (Just-in-Time compilation, LLVM JIT, and Fast JIT are supported). Details on how to build and use each mode properly and where you may want to use it can be found in [Tutorial](../../tutorial/README.md) + + - High Performance: WAMR achieves nearly native speed by AOT and JIT modes. It also has a small runtime binary size (~85K for interpreter and ~50K for AOT) and low memory usage + + - Portability: It supports many architectures and platforms. + + The architectures it supports: + + - X86-64, X86-32 + - ARM, THUMB (ARMV7 Cortex-M7 and Cortex-A15 are tested) + - AArch64 (Cortex-A57 and Cortex-A53 are tested) + - RISCV64, RISCV32 (RISC-V LP64 and RISC-V LP64D are tested) + - XTENSA, MIPS, ARC + + The platforms it supports: + + - [Linux](../../../doc/build_wamr.md#linux), [Linux SGX (Intel Software Guard Extension)](../../../doc/linux_sgx.md), [MacOS](../../../doc/build_wamr.md#macos), [Android](../../../doc/build_wamr.md#android), [Windows](../../../doc/build_wamr.md#windows), [Windows (MinGW)](../../../doc/build_wamr.md#mingw) + + - [Zephyr](../../../doc/build_wamr.md#zephyr), [AliOS-Things](../../../doc/build_wamr.md#alios-things), [VxWorks](../../../doc/build_wamr.md#vxworks), [NuttX](../../../doc/build_wamr.md#nuttx), [RT-Thread](../../../doc/build_wamr.md#RT-Thread), [ESP-IDF](../../../doc/build_wamr.md#esp-idf) + + It enables true cross-platform development experience. You can even port WAMR to a new platform following [this tutorial](../../../doc/port_wamr.md). Though it's unlikely since we support many platforms, having such features is comforting. + + - Security: It has Linux SGX (Intel Software Guard Extension) support. Through this unique application isolation technology, your application data is as safe as it can be. + +2. The "wamrc" AOT compiler to compile WASM files into AOT files for best performance and smaller runtime footprint, which is run by "iwasm" VM Core + + Both the wasm binary files and AOT files are supported by iwasm. The wamrc AOT compiler compiles a wasm binary file to an AOT file, which can also be run by iwasm. The speed by AOT and JIT are near to native. + +The other 2 parts are: + +1. Application framework: + + The WAMR application manager supports remote application management from the host environment or the cloud through any physical communications such as TCP, UPD, UART, BLE, etc. Its modular design makes it able to support application management for different managed runtimes. + +2. Application manager: + + By using the iwasm VM core, we are flexible to build different application frameworks for specific domains, although it would take quite some effort. + + The WAMR has offered a comprehensive framework for programming WASM applications for device and IoT usages. The framework supports running multiple applications that are based on the event-driven programming model. Here are the supporting API sets by the WAMR application framework library : + + - Timer, Inter-app communication (request/response and pub/sub), Sensor, Connectivity and data transmission, 2D graphic UI diff --git a/gitbook/basics/introduction/webassembly.md b/gitbook/basics/introduction/webassembly.md new file mode 100644 index 0000000000..65e2fe3521 --- /dev/null +++ b/gitbook/basics/introduction/webassembly.md @@ -0,0 +1,52 @@ +# WebAssembly + +In this section, you will learn the basics of WebAssembly. More details about WebAssembly can be found in [Appendix B](../../appendix/webassembly_details.md) + +## Overview + +Like its name suggest, in a sense, it is related to the Web and Assembly. Web means that it, like many other forerunners, like asm.js, trying to improve JavaScript's performance in Browsers. And the Assembly means that the format of WebAssembly is not a human-readable format but a compact binary format that is more efficient for Browsers to use. To conclude, WebAssembly (Wasm) is a compact, binary instruction format tailored for a stack-based virtual machine. It serves as a portable compilation target for various programming languages, enabling smooth deployment across both client and server environments on the web. It aims to provide benefits such as: + +- High Performance + + Wasm is built for fast execution and compact encoding, allowing programs to be efficiently transmitted and quickly loaded. By leveraging the common hardware capabilities across platforms, WebAssembly can run at near-native speeds. + +- Secure Execution Environment + + Wasm operates within a memory-safe, sandboxed environment, which can be implemented even inside existing JavaScript engines. When integrated into web applications, it adheres to the same-origin policy and browser-based permission models, ensuring robust security. + +- Open and Debuggable Format + + Wasm is designed with a textual representation that aids in debugging, testing, and optimization. This human-readable format allows developers to experiment, learn, and even hand-code Wasm modules. When viewed on the web, this text format makes Wasm modules easily accessible for inspection. + +- A Core Part of the Open Web + + Built to uphold the versionless and backward-compatible nature of the web, WebAssembly seamlessly interacts with JavaScript and can access web APIs. Beyond web applications, Wasm is versatile and supports other non-web environments as well. + +In [The State of WebAssembly 2023](https://www.cncf.io/reports/the-state-of-webassembly-2023/) from SlashData, Linux Foundation, and the Cloud Native Computing Foundation, some key insights into the current status and adoption of WebAssembly can be found. Including the top reason why people want to use WebAssembly: + +- Faster loading times 23% +- Exploring new use cases and technologies 22% +- Sharing code between projects 20% +- Improved performance over JavaScript 20% +- Efficient execution of computationally intensive tasks 19% +- Binaries run anywhere 18% +- Sandboxed security 18% +- Language agnostic 18% + +What makes it even better is that, like Javascript, shortly after the appearance of WebAssembly, it is not limited to the browser. It could also be used server-side. Many WebAssembly runtimes are out there, including our project WAMR. + +## How does it work + +### A browser example + +The most straightforward place you could think of when it comes to the use of WebAssembly is in the browser. + +Emscripten is a compiler toolchain for WebAssembly. It took the C/C++(or any other programming language LLVM frontend support) source program as input and translated it into the WebAssembly target program module. + +Optionally, an HTML and a Javascript file are generated alongside a wasm file, so the plumbing JS code is ready for you to call your wasm module. And you could open HTML on your browser to see the result of your wasm program. + +Here is the more detailed [emscripten official tutorial](https://emscripten.org/docs/getting_started/Tutorial.html) you could follow to write your hello world wasm program and run it on the browser. + +### A server-side example + +A hello world example using our WAMR can be found [here](../getting-started/README.md) diff --git a/gitbook/examples/README.md b/gitbook/examples/README.md new file mode 100644 index 0000000000..300a39f926 --- /dev/null +++ b/gitbook/examples/README.md @@ -0,0 +1,11 @@ +# More Examples + +In this chapter, we provide some extra useful examples to demonstrate how you may want to use WAMR: + +- [File Interaction Of WASI](../../samples/file/README.md): Demonstrating the supported file interaction API of WASI. This sample can also demonstrate the SGX IPFS (Intel Protected File System), enabling an enclave to seal and unseal data at rest. + +- [GUI Examples](gui-examples/README.md): We provide two examples that both use [LVGL library](https://github.com/lvgl/lvgl) + +- [Concurrent WASM Application](../../samples/spawn-thread): Demonstrating how to execute wasm functions of the same wasm application concurrently in threads created by host embedder or runtime, but not the wasm application itself. + +- [Workload](../../samples/workload/README.md): Demonstrating how to build and run some complex workloads, e.g., tensorflow-lite, XNNPACK, wasm-av1, meshoptimizer, and bwa. diff --git a/gitbook/features/README.md b/gitbook/features/README.md new file mode 100644 index 0000000000..de538076c8 --- /dev/null +++ b/gitbook/features/README.md @@ -0,0 +1,39 @@ +--- +description: "This page is under construction/refinement. p.s. wanna hear a construction joke? we are still working on it" +--- +# Features And Examples + + + +In this chapter, you can see the complete list of features that WAMR support. And for each feature, we have an example followed demonstrating the usage of such a feature. + +## IWASM features + +### Key features + +- Full compliant to the W3C WASM MVP +- Small runtime binary size (~85K for interpreter and ~50K for AOT) and low memory usage +- Near to native speed by AOT and JIT +- Self-implemented AOT module loader to enable AOT work on Linux, Windows, MacOS, Android, SGX, and MCU systems +- Choices of WASM application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for the standard libc +- [The simple C APIs to embed WAMR into host environment](../../doc/embed_wamr.md), see [how to integrate WAMR](../../doc/embed_wamr.md) and the [API list](../../core/iwasm/include/wasm_export.h) +- [The mechanism to export native APIs to WASM applications](../../doc/export_native_api.md), see [how to register native APIs](../../doc/export_native_api.md) +- [Multiple modules as dependencies](../../doc/multi_module.md), ref to [document](../../doc/multi_module.md) and [sample](../../samples/multi-module) +- [Multi-thread, pthread APIs and thread management](../../doc/pthread_library.md), ref to [document](../../doc/pthread_library.md) and [sample](../../samples/multi-thread) +- [Linux SGX (Intel Software Guard Extension) support](../../doc/linux_sgx.md), ref to [document](../../doc/linux_sgx.md) +- [Source debugging support](../../doc/source_debugging.md), ref to [document](../../doc/source_debugging.md) +- [WAMR-IDE (Experimental)](../../test-tools/wamr-ide) to develop WebAssembly applications with build, run and debug support, ref to [document](../../test-tools/wamr-ide) +- [XIP (Execution In Place) support](../../doc/xip.md), ref to [document](../../doc/xip.md) +- [Berkeley/Posix Socket support](../../doc/socket_api.md), ref to [document](../../doc/socket_api.md) and [sample](../../samples/socket-api) +- Language bindings: [Go](../../language-bindings/go/README.md), [Python](../../language-bindings/python/README.md) + +### WASM post-MVP features + +There are many post-MVP features for WASM. We support some of them. You can see the details in [this section](demo-examples/README.md) + +- [wasm-c-api](https://github.com/WebAssembly/wasm-c-api) +- [128-bit SIMD](https://github.com/WebAssembly/simd) +- [Reference Types](https://github.com/WebAssembly/reference-types) +- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) +- [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops), [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) +- [Multi-value](https://github.com/WebAssembly/multi-value), [Tail-call](https://github.com/WebAssembly/tail-call), [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory) diff --git a/gitbook/features/demo-examples/README.md b/gitbook/features/demo-examples/README.md new file mode 100644 index 0000000000..8f6c89155f --- /dev/null +++ b/gitbook/features/demo-examples/README.md @@ -0,0 +1,15 @@ +# WASM post-MVP features + +The ones we support: + +- [wasm-c-api](https://github.com/WebAssembly/wasm-c-api), ref to [document](../../../doc/wasm_c_api.md) and [sample](../../../samples/wasm-c-api) + +- [128-bit SIMD](https://github.com/WebAssembly/simd), ref to [samples/workload](../../../samples/workload/README.md) + +- [Reference Types](https://github.com/WebAssembly/reference-types), ref to [document](../../../doc/ref_types.md) and [sample](../../../samples/ref-types) + +Other post-MVP features: + +- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) +- [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops), [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) +- [Multi-value](https://github.com/WebAssembly/multi-value), [Tail-call](https://github.com/WebAssembly/tail-call), [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory) diff --git a/gitbook/features/user-case/README.md b/gitbook/features/user-case/README.md new file mode 100644 index 0000000000..dea2acbcc6 --- /dev/null +++ b/gitbook/features/user-case/README.md @@ -0,0 +1,10 @@ +# User case + +WAMR is widely used in a lot of areas. Here are some cases: + +- [Hyperledger Private Data Objects](https://github.com/hyperledger-labs/private-data-objects/blob/main/common/interpreter/wawaka_wasm/README.md) +- [Inclavare Containers](https://github.com/alibaba/inclavare-containers) +- [Fassm](https://github.com/faasm/faasm) +- [Waft](https://developer.aliyun.com/article/787582) +- [Envoy Proxy](https://github.com/envoyproxy/envoy) +- [Apache Teaclave](https://teaclave.apache.org/docs/executing-wasm) diff --git a/gitbook/home_page.md b/gitbook/home_page.md new file mode 100644 index 0000000000..831c167f01 --- /dev/null +++ b/gitbook/home_page.md @@ -0,0 +1,15 @@ +# Welcome + +Welcome to the home page of WAMR documentation, [WebAssembly Micro Runtime](https://github.com/bytecodealliance/wasm-micro-runtime) is an open-source project under [Bytecode Alliance](https://bytecodealliance.org/). As the name suggests, it is a lightweight standalone WebAssembly (WASM) runtime with a small footprint, high performance, and highly configurable features for applications across from embedded, IoT, edge Trusted Execution Environment (TEE), smart contract, cloud-native, and so on. + +## How to navigate the documentation + +If you are a complete beginner who just stepped into the world of WebAssembly or just looking to kill some time and learn something fun, start with [appendix A. background knowledge 101](appendix/background_knowledge.md) and our [chapter 1. introduction](basics/introduction/README.md). Also, you could learn how to run a hello world server-side wasm application using runtime WAMR in [chapter 2. getting started](basics/getting-started/README.md). + +Suppose you are somewhat familiar with WebAssembly and want to explore what WAMR can do for you as a user and developer. You could first check out [chapter 3. tutorial on how to use WAMR](tutorial/README.md), including [introduction to different running modes](tutorial/running-modes/README.md), how to [build different running modes](tutorial/build-tutorial/README.md), how to [embed WAMR into your application](tutorial/language-embedding/README.md) and how to [debug with WAMR](tutorial/debugging%26IDE-support/README.md). Then you could visit the complete list of [chapter 5. features](features/README.md) WAMR supported to see whether specific feature interest you and would serve your demands well. If the time comes when you start to optimize and consider improving performance or want to utilize the advanced feature, including **application framework** and **dynamic management**. In that case, you could find them in [chapter 4. advance tutorial](advance-tutorial/README.md). + +And, of course, you can always utilize the search function in the top right corner to locate whichever topic you are interested in + +## Social + +Feel free to check out our [blog](https://bytecodealliance.github.io/wamr.dev/) from time to time! We have some great blogs that either showcase some WAMR features and use cases or discuss some interesting topics on WAMR/WASM. diff --git a/gitbook/programmer's-manual/C_API_Lists.md b/gitbook/programmer's-manual/C_API_Lists.md new file mode 100644 index 0000000000..abaef36e81 --- /dev/null +++ b/gitbook/programmer's-manual/C_API_Lists.md @@ -0,0 +1,3 @@ +# Complete C/C++ API Lists + +The complete C/C++ lists for embedding the WAMR VM core can be found in the header file [wasm_export.h](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/core/iwasm/include/wasm_export.h). diff --git a/gitbook/programmer's-manual/README.md b/gitbook/programmer's-manual/README.md new file mode 100644 index 0000000000..fa098060ce --- /dev/null +++ b/gitbook/programmer's-manual/README.md @@ -0,0 +1,6 @@ +--- +description: "This page is under construction/refinement. p.s. wanna hear a construction joke? we are still working on it" +--- +# Programmer's Manual + +Complete List of C APIs can be found [here](gitbook/programmer's_manual/C_API_Lists.md) diff --git a/gitbook/tutorial/README.md b/gitbook/tutorial/README.md new file mode 100644 index 0000000000..7d76c83eda --- /dev/null +++ b/gitbook/tutorial/README.md @@ -0,0 +1,9 @@ +# Tutorial + +In this chapter, we want to walk you through the basic development knowledge and skills of WAMR you may need so that you are ready to write your wasm application with WAMR. + +For starters, you could learn how to compile different running mode of WAMR and their usage in [this section](build-tutorial/README.md). + +Then, as we said before, WAMR is not limited to being a command line application that runs wasm code. You could also [embed WAMR](language-embedding/README.md) into your application. + +And don't forget the one important stage of developing, namely debugging. We cover it in [this section](debugging%26IDE-support/README.md). diff --git a/gitbook/tutorial/build-tutorial/README.md b/gitbook/tutorial/build-tutorial/README.md new file mode 100644 index 0000000000..dee388f970 --- /dev/null +++ b/gitbook/tutorial/build-tutorial/README.md @@ -0,0 +1,36 @@ +--- +description: "This page is under construction/refinement. p.s. wanna hear a construction joke? we are still working on it" +--- +# build tutorial + +In this chapter, we provide a detailed tutorial on how to build [iwasm vmcore](../../../doc/build_wamr.md) and [wamrc](build_wamrc.md). + +## Quick build matrix + +Our powerful **iwasm vmcore** provide various running mode you could choose using the compile CMake option. Here is the matrix for different running mode and their attributes: + +| Running mode | CMake build options | Pros and Cons | +| ----------- | ----------- | --------- | +| AOT | none(default) | | +| Classic Interpreter | -DWAMR_BUILD_FAST_INTERP=0 | | +| Fast Interpreter | none(default) | | +| LLVM JIT | -DWAMR_BUILD_JIT=1 | | +| Fast JIT | -DWAMR_BUILD_FAST_JIT=1 | | + +## Supported architectures and platforms + +Here is a list of architectures and platforms WAMR support. You could click on the link for quick reference. + +The iwasm supports the following architectures: + +- X86-64, X86-32 +- ARM, THUMB (ARMV7 Cortex-M7 and Cortex-A15 are tested) +- AArch64 (Cortex-A57 and Cortex-A53 are tested) +- RISCV64, RISCV32 (RISC-V LP64 and RISC-V LP64D are tested) +- XTENSA, MIPS, ARC + +The following platforms are supported. Click each link below for how to build iwasm on that platform. Refer to [WAMR porting guide](../../../doc/port_wamr.md) for how to port WAMR to a new platform. + +- [Linux](../../../doc/build_wamr.md#linux), [Linux SGX (Intel Software Guard Extension)](../../../doc/linux_sgx.md), [MacOS](../../../doc/build_wamr.md#macos), [Android](../../../doc/build_wamr.md#android), [Windows](../../../doc/build_wamr.md#windows), [Windows (MinGW)](../../../doc/build_wamr.md#mingw) + +- [Zephyr](../../../doc/build_wamr.md#zephyr), [AliOS-Things](../../../doc/build_wamr.md#alios-things), [VxWorks](../../../doc/build_wamr.md#vxworks), [NuttX](../../../doc/build_wamr.md#nuttx), [RT-Thread](../../../doc/build_wamr.md#RT-Thread), [ESP-IDF](../../../doc/build_wamr.md#esp-idf) diff --git a/gitbook/tutorial/build-tutorial/build_wamrc.md b/gitbook/tutorial/build-tutorial/build_wamrc.md new file mode 100644 index 0000000000..891d7d7d44 --- /dev/null +++ b/gitbook/tutorial/build-tutorial/build_wamrc.md @@ -0,0 +1,23 @@ +# How to Build wamrc AOT compiler + +Both the WASM binary file and AOT file are supported by iwasm. The wamrc AOT compiler compiles wasm binary file to AOT file, which can also be run by iwasm. Execute the following commands to build **wamrc** compiler for Linux: + +```shell +cd wamr-compiler +./build_llvm.sh (or "./build_llvm_xtensa.sh" to support xtensa target) +mkdir build && cd build +cmake .. (or "cmake .. -DWAMR_BUILD_PLATFORM=darwin" for MacOS) +make +# wamrc is generated under current directory +``` + +For **Windows**: + +```shell +cd wamr-compiler +python build_llvm.py +mkdir build && cd build +cmake .. +cmake --build . --config Release +# wamrc.exe is generated under .\Release directory +``` diff --git a/gitbook/tutorial/debugging&IDE-support/README.md b/gitbook/tutorial/debugging&IDE-support/README.md new file mode 100644 index 0000000000..e03d0e7a92 --- /dev/null +++ b/gitbook/tutorial/debugging&IDE-support/README.md @@ -0,0 +1,3 @@ +# debugging & IDE support + +Has concern about debugging when it comes to a newly emerging technique like wasm? No worries, we got you covered. You could either [debug directly with lldb](../../../doc/source_debugging.md), or you could even [debug using VS Code](../../../test-tools/wamr-ide/README.md) diff --git a/gitbook/tutorial/language-embedding/README.md b/gitbook/tutorial/language-embedding/README.md new file mode 100644 index 0000000000..14c92a22f1 --- /dev/null +++ b/gitbook/tutorial/language-embedding/README.md @@ -0,0 +1,9 @@ +# language embedding + +As we mentioned before, WAMR is not only a server-side runtime for a wasm application but also a library that you could embed in your application. What's even better is that we support several programming languages so that you can choose your favorite language to embed WAMR to run the wasm app. + +The language WAMR support embedding: + +- [C/C++](../../../doc/embed_wamr.md) +- [Python](../../../language-bindings/python/README.md) +- [Go](../../../language-bindings/go/README.md) diff --git a/gitbook/tutorial/running-modes/README.md b/gitbook/tutorial/running-modes/README.md new file mode 100644 index 0000000000..c5f3c909d5 --- /dev/null +++ b/gitbook/tutorial/running-modes/README.md @@ -0,0 +1,29 @@ +--- +description: "This page is under construction/refinement. p.s. wanna hear a construction joke? we are still working on it" +--- + +# WAMR Running Modes + +## Brief Introduction + +In this section, we want to introduce running modes and their difference to you + +### "iwasm" VM core running mode + +It could run an AOT file(compiled by wamrc AOT compiler) in AOT running mode + +- AOT: Ahead-of-Time compilation. As you can guess from the name, we first need to use the *wamrc* compiler to compile wasm file to the AOT file. Then it could be run with our *iwasm* vmcore. In this running mode, we could achieve the nearly native speed(the best of all running modes) with very small footprint and quick startup + +It could run wasm applications in Interpreter/JIT running mode: + +- Interpreter: + Interpreters are very useful when debugging or studying, but their performance is relatively poor compared with other running modes. We support two running modes of the interpreter: + - Classic Interpreter: plain interpreter running mode + - Fast Interpreter: as you can guess from the name, the fast interpreter runs ~2X faster than the classic interpreter but consumes about 2X memory to hold the pre-compiled code. +- JIT: + Using the Just-in-Time compilation technique, we could make iwasm run much faster than Interpreter mode and sometimes very close to the speed of AOT running mode. We support two running modes of JIT: + - LLVM JIT: the JIT engine is implemented based on LLVM codegen. The performance of LLVM JIT is better than Fast JIT, with ~2x of the latter. But the startup time is slower than Fast JIT. + - Fast JIT: the JIT engine is implemented based on self-implemented codegen and asmjit encoder library. It is a lightweight JIT engine with small footprint, quick startup, good portability and relatively good performance. Currently it supports x86-64 target and Linux/Linux-SGX/MacOS platforms. The performance of Fast JIT is ~50% of the performance of LLVM JIT. + + +For more detailed introduction, kindly refer to this article(**incoming**) in our blog. diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 0000000000..db4afc77d7 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,18 @@ +version: "2.2.0~3" +description: WebAssembly Micro Runtime - A lightweight standalone WebAssembly (Wasm) runtime with small footprint, high performance and highly configurable features +url: https://bytecodealliance.org/ +repository: https://github.com/bytecodealliance/wasm-micro-runtime.git +documentation: https://wamr.gitbook.io/ +issues: https://github.com/bytecodealliance/wasm-micro-runtime/issues +dependencies: + idf: ">=4.4" +targets: + - esp32 + - esp32s2 + - esp32s3 + - esp32c3 + - esp32c6 + - esp32p4 + - esp32c5 +examples: + - path: product-mini/platforms/esp-idf diff --git a/language-bindings/go/README.md b/language-bindings/go/README.md new file mode 100644 index 0000000000..34baf21814 --- /dev/null +++ b/language-bindings/go/README.md @@ -0,0 +1,104 @@ +WAMR Go binding: Embedding WAMR in Go guideline +=============================================== + +This Go library uses CGO to consume the runtime APIs of the WAMR project which are defined in [core/iwasm/include/wasm_export.h](../../core/iwasm/include/wasm_export.h). The API details are available in the header files. + +## Installation + +### Installing from the source code + +Installing from local source tree is in _development mode_. + +Run `./build.sh` in this folder to build the package, which builds the WAMR runtime library firstly and then builds the Go binding library. + +Run `./build.sh` under `samples` folder to build and test the sample. + +```bash +cd samples +./build.sh +``` + +## Supported APIs + +All the embedding APIs supported are defined under folder [wamr](./wamr). + +### Runtime APIs + +```Go +func Runtime() *_Runtime +func (self *_Runtime) FullInit(alloc_with_pool bool, heap_buf []byte, + max_thread_num uint) error +func (self *_Runtime) Init() error +func (self *_Runtime) Destroy() +func (self *_Runtime) SetLogLevel(level LogLevel) +func (self *_Runtime) Malloc(size uint32) *uint8 +func (self *_Runtime) Free(ptr *uint8) +``` + +### Module APIs + +```Go +func NewModule(wasmBytes []byte) (*Module, error) +func (self *Module) Destroy() +func (self *Module) SetWasiArgs(dirList [][]byte, mapDirList [][]byte, + env [][]byte, argv[][]byte) +func (self *Module) SetWasiArgsEx(dirList [][]byte, mapDirList [][]byte, + env [][]byte, argv[][]byte, + stdinfd int, stdoutfd int, stderrfd int) +func (self *Module) SetWasiAddrPool(addrPool [][]byte) +``` + +### Instance APIs + +```Go +func NewInstance(module *Module, + stackSize uint, heapSize uint) (*Instance, error) +func (self *Instance) Destroy() +func (self *Instance) CallFunc(funcName string, + argc uint32, args []uint32) error +func (self *Instance) CallFuncV(funcName string, + num_results uint32, results []interface{}, + args ... interface{}) error +func (self *Instance) GetException() string +func (self Instance) ModuleMalloc(size uint32) (uint32, *uint8) +func (self Instance) ModuleFree(offset uint32) +func (self Instance) ValidateAppAddr(app_offset uint32, size uint32) bool +func (self Instance) ValidateNativeAddr(native_ptr *uint8, size uint32) bool +func (self Instance) AddrAppToNative(app_offset uint32) *uint8 +func (self Instance) AddrNativeToApp(native_ptr *uint8) uint32 +func (self Instance) GetAppAddrRange(app_offset uint32) (bool, uint32, uint32) +func (self Instance) GetNativeAddrRange(native_ptr *uint8) (bool, *uint8, *uint8) +func (self Instance) DumpMemoryConsumption() +func (self Instance) DumpCallStack() +``` + +## Sample codes + +```Go + var module *wamr.Module + var instance *wamr.Instance + var results []interface{} + var err error + + /* Runtime initialization */ + err = wamr.Runtime().FullInit(false, nil, 1) + + /* Read WASM/AOT file into a memory buffer */ + wasmBytes := read_wasm_binary_to_buffer(...) + + /* Load WASM/AOT module from the memory buffer */ + module, err = wamr.NewModule(wasmBytes) + + /* Create WASM/AOT instance from the module */ + instance, err = wamr.NewInstance(module, 16384, 16384) + + /* Call the `fib` function */ + results = make([]interface{}, 1, 1) + err = instance.CallFuncV("fib", 1, results, (int32)32) + fmt.Printf("fib(32) return: %d\n", results[0].(int32)); + + /* Destroy runtime */ + wamr.Runtime().Destroy() +``` + +More samples can be found in [test.go](./samples/test.go) diff --git a/language-bindings/go/build.sh b/language-bindings/go/build.sh new file mode 100755 index 0000000000..c6736ed87e --- /dev/null +++ b/language-bindings/go/build.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +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 + +mkdir -p build && cd build +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 libiwasm.a ${WAMR_GO_DIR}/packaged/lib/${PLATFORM}-${ARCH} + +cd ${WAMR_GO_DIR} +go test diff --git a/language-bindings/go/go.mod b/language-bindings/go/go.mod new file mode 100644 index 0000000000..3425bcb270 --- /dev/null +++ b/language-bindings/go/go.mod @@ -0,0 +1,5 @@ +module github.com/bytecodealliance/wasm-micro-runtime/language-bindings/go + +go 1.15 + +require github.com/stretchr/testify v1.11.1 diff --git a/language-bindings/go/go.sum b/language-bindings/go/go.sum new file mode 100644 index 0000000000..1cd876dc18 --- /dev/null +++ b/language-bindings/go/go.sum @@ -0,0 +1,19 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/language-bindings/go/samples/build.sh b/language-bindings/go/samples/build.sh new file mode 100755 index 0000000000..0799fe5a0d --- /dev/null +++ b/language-bindings/go/samples/build.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CUR_DIR=$PWD + +pushd ${CUR_DIR}/.. > /dev/null 2>&1 +./build.sh +popd > /dev/null 2>& 1 + +cd ${CUR_DIR} +rm -f test +go build test.go +./test diff --git a/language-bindings/go/samples/run.sh b/language-bindings/go/samples/run.sh new file mode 100755 index 0000000000..a57da7f5f1 --- /dev/null +++ b/language-bindings/go/samples/run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +go build test.go +./test diff --git a/language-bindings/go/samples/test.go b/language-bindings/go/samples/test.go new file mode 100644 index 0000000000..19b2814a8e --- /dev/null +++ b/language-bindings/go/samples/test.go @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package main + +import ( + "github.com/bytecodealliance/wasm-micro-runtime/language-bindings/go/wamr" + "fmt" +) + +var wasmBytes = []byte { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x29, 0x07, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x60, 0x04, 0x7F, 0x7E, 0x7D, 0x7C, 0x00, 0x60, 0x02, 0x7E, + 0x7E, 0x01, 0x7E, 0x60, 0x02, 0x7C, 0x7F, 0x01, 0x7D, 0x60, 0x02, 0x7D, + 0x7C, 0x01, 0x7C, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, + 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, + 0x70, 0x75, 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, + 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x04, + 0x66, 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x07, 0x06, 0x00, 0x03, 0x04, + 0x06, 0x05, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, 0x7F, + 0x01, 0x41, 0x90, 0x29, 0x0B, 0x7F, 0x00, 0x41, 0x90, 0x09, 0x0B, 0x7F, + 0x00, 0x41, 0x90, 0x29, 0x0B, 0x07, 0x5F, 0x09, 0x06, 0x6D, 0x65, 0x6D, + 0x6F, 0x72, 0x79, 0x02, 0x00, 0x04, 0x66, 0x69, 0x62, 0x32, 0x00, 0x04, + 0x05, 0x74, 0x65, 0x73, 0x74, 0x31, 0x00, 0x05, 0x05, 0x74, 0x65, 0x73, + 0x74, 0x32, 0x00, 0x06, 0x05, 0x74, 0x65, 0x73, 0x74, 0x33, 0x00, 0x07, + 0x05, 0x74, 0x65, 0x73, 0x74, 0x34, 0x00, 0x08, 0x10, 0x5F, 0x5F, 0x6D, + 0x61, 0x69, 0x6E, 0x5F, 0x61, 0x72, 0x67, 0x63, 0x5F, 0x61, 0x72, 0x67, + 0x76, 0x00, 0x09, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65, + 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, 0x61, 0x70, 0x5F, + 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x0A, 0xA5, 0x03, 0x06, 0x37, 0x01, + 0x01, 0x7F, 0x41, 0x01, 0x21, 0x01, 0x20, 0x00, 0x41, 0x02, 0x4F, 0x04, + 0x7F, 0x41, 0x00, 0x21, 0x01, 0x03, 0x40, 0x20, 0x00, 0x41, 0x02, 0x6B, + 0x10, 0x04, 0x20, 0x01, 0x6A, 0x21, 0x01, 0x20, 0x00, 0x41, 0x01, 0x6B, + 0x22, 0x00, 0x41, 0x01, 0x4B, 0x0D, 0x00, 0x0B, 0x20, 0x01, 0x41, 0x01, + 0x6A, 0x05, 0x41, 0x01, 0x0B, 0x0B, 0x3F, 0x01, 0x01, 0x7F, 0x23, 0x00, + 0x41, 0x20, 0x6B, 0x22, 0x04, 0x24, 0x00, 0x20, 0x04, 0x41, 0x18, 0x6A, + 0x20, 0x03, 0x39, 0x03, 0x00, 0x20, 0x04, 0x41, 0x10, 0x6A, 0x20, 0x02, + 0xBB, 0x39, 0x03, 0x00, 0x20, 0x04, 0x20, 0x01, 0x37, 0x03, 0x08, 0x20, + 0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0xD0, 0x08, 0x20, 0x04, 0x10, + 0x00, 0x1A, 0x20, 0x04, 0x41, 0x20, 0x6A, 0x24, 0x00, 0x0B, 0x3B, 0x01, + 0x01, 0x7F, 0x23, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, 0x24, 0x00, 0x20, + 0x02, 0x20, 0x00, 0x37, 0x03, 0x00, 0x20, 0x02, 0x20, 0x01, 0x37, 0x03, + 0x08, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x20, 0x00, 0x20, 0x01, 0x7C, 0x22, + 0x00, 0x37, 0x03, 0x00, 0x41, 0xF6, 0x08, 0x20, 0x02, 0x10, 0x00, 0x1A, + 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x00, 0x20, 0x00, 0x0B, 0x40, 0x02, + 0x01, 0x7F, 0x01, 0x7C, 0x23, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, 0x24, + 0x00, 0x20, 0x02, 0x20, 0x01, 0x39, 0x03, 0x08, 0x20, 0x02, 0x20, 0x00, + 0xBB, 0x22, 0x03, 0x39, 0x03, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x20, + 0x03, 0x20, 0x01, 0xA2, 0x22, 0x01, 0x39, 0x03, 0x00, 0x41, 0xB4, 0x08, + 0x20, 0x02, 0x10, 0x00, 0x1A, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x00, + 0x20, 0x01, 0x0B, 0x3D, 0x01, 0x01, 0x7F, 0x23, 0x00, 0x41, 0x20, 0x6B, + 0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x20, 0x00, 0x39, 0x03, 0x00, 0x20, + 0x02, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x20, + 0x00, 0x20, 0x01, 0xB7, 0xA3, 0x22, 0x00, 0x39, 0x03, 0x00, 0x41, 0xC2, + 0x08, 0x20, 0x02, 0x10, 0x00, 0x1A, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, + 0x00, 0x20, 0x00, 0xB6, 0x0B, 0x70, 0x00, 0x23, 0x00, 0x41, 0x20, 0x6B, + 0x22, 0x00, 0x24, 0x00, 0x41, 0x9A, 0x08, 0x10, 0x01, 0x1A, 0x02, 0x7F, + 0x41, 0x80, 0x08, 0x10, 0x02, 0x22, 0x01, 0x45, 0x04, 0x40, 0x41, 0x88, + 0x08, 0x10, 0x01, 0x1A, 0x41, 0x7F, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x10, 0x41, 0xA7, 0x08, 0x20, 0x00, 0x41, 0x10, 0x6A, + 0x10, 0x00, 0x1A, 0x20, 0x01, 0x41, 0x04, 0x6A, 0x41, 0x8E, 0x09, 0x2F, + 0x00, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x01, 0x41, 0x8A, 0x09, 0x28, 0x00, + 0x00, 0x36, 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x41, + 0x80, 0x08, 0x20, 0x00, 0x10, 0x00, 0x1A, 0x20, 0x01, 0x10, 0x03, 0x41, + 0x00, 0x0B, 0x20, 0x00, 0x41, 0x20, 0x6A, 0x24, 0x00, 0x0B, 0x0B, 0x97, + 0x01, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0B, 0x8F, 0x01, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x20, + 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00, 0x48, + 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, + 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, 0x70, 0x0A, + 0x00, 0x25, 0x66, 0x20, 0x2A, 0x20, 0x25, 0x66, 0x20, 0x3D, 0x20, 0x25, + 0x66, 0x0A, 0x00, 0x25, 0x66, 0x20, 0x2F, 0x20, 0x25, 0x64, 0x20, 0x3D, + 0x20, 0x25, 0x66, 0x0A, 0x00, 0x69, 0x33, 0x32, 0x3A, 0x20, 0x25, 0x64, + 0x2C, 0x20, 0x69, 0x36, 0x34, 0x3A, 0x20, 0x25, 0x6C, 0x6C, 0x64, 0x2C, + 0x20, 0x66, 0x33, 0x32, 0x3A, 0x20, 0x25, 0x66, 0x2C, 0x20, 0x66, 0x36, + 0x34, 0x3A, 0x20, 0x25, 0x66, 0x0A, 0x00, 0x25, 0x6C, 0x6C, 0x64, 0x20, + 0x2B, 0x20, 0x25, 0x6C, 0x6C, 0x64, 0x20, 0x3D, 0x20, 0x25, 0x6C, 0x6C, + 0x64, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A } + +var global_heap []byte = make([]byte, 128 * 1024, 128 * 1024) + +func main() { + var module *wamr.Module + var instance *wamr.Instance + var argv []uint32 + var results []interface{} + var offset uint64 + var native_addr *uint8 + var err error + + fmt.Print("Init wasm runtime with global heap buf\n"); + err = wamr.Runtime().FullInit(true, global_heap, 1) + if err != nil { + return + } + fmt.Print("Destroy runtime\n"); + wamr.Runtime().Destroy() + + fmt.Print("Init wasm runtime without global heap buf\n"); + err = wamr.Runtime().FullInit(false, nil, 1) + if err != nil { + return + } + + wamr.Runtime().SetLogLevel(wamr.LOG_LEVEL_WARNING) + + fmt.Print("Load wasm module\n"); + module, err = wamr.NewModule(wasmBytes) + if err != nil { + fmt.Println(err) + goto fail + } + + fmt.Print("Instantiate wasm module\n"); + instance, err = wamr.NewInstance(module, 16384, 16384) + if err != nil { + fmt.Println(err) + goto fail + } + + results = make([]interface{}, 8, 8) + argv = make([]uint32, 8) + + fmt.Print("\nCall func __main_argc_argv with CallFunc:\n"); + err = instance.CallFunc("__main_argc_argv", 2, argv) + if err != nil { + fmt.Println(err) + goto fail + } + + fmt.Print("\nCall func __main_argc_argv with CallFuncV:\n"); + err = instance.CallFuncV("__main_argc_argv", 2, results, + (int32)(0), (int32)(0)) + if err != nil { + fmt.Println(err) + goto fail + } + + fmt.Print("\nCall func `i32 fib2(i32)` with CallFunc:\n"); + argv[0] = 32 + err = instance.CallFunc("fib2", 1, argv) + if err != nil { + fmt.Println(err) + goto fail + } + fmt.Printf("fib2(32) return: %d\n", argv[0]); + + fmt.Print("\nCall func `void test1(i32, i64, f32, f64)` with CallFuncV:\n"); + err = instance.CallFuncV("test1", 0, nil, + (int32)(12345678), + (int64)(3344556677889900), + (float32)(5678.1234), + (float64)(987654321.5678)) + if err != nil { + fmt.Println(err) + goto fail + } + + fmt.Print("\nCall func `i64 test2(i64, i64)` with CallFuncV:\n"); + err = instance.CallFuncV("test2", 1, results, + (int64)(3344556677889900), + (int64)(1122331122110099)) + if err != nil { + fmt.Println(err) + goto fail + } + fmt.Printf("test2(3344556677889900, 1122331122110099) return: %d\n", + results[0].(int64)) + + fmt.Print("\nCall func `f64 test3(f32, f64)` with CallFuncV:\n"); + err = instance.CallFuncV("test3", 1, results, + (float32)(3456.1234), + (float64)(7890.4567)) + if err != nil { + fmt.Println(err) + goto fail + } + fmt.Printf("test3(3456.1234, 7890.4567) return: %f\n", + results[0].(float64)) + + fmt.Print("\nCall func `f32 test4(f64, i32)` with CallFuncV:\n"); + err = instance.CallFuncV("test4", 1, results, + (float64)(8912.3456), + (int32)(123)) + if err != nil { + fmt.Println(err) + goto fail + } + fmt.Printf("test4(8912.3456, 123) return: %f\n", + results[0].(float32)) + + fmt.Print("\nTest ModuleMalloc") + offset, native_addr = instance.ModuleMalloc(1024) + fmt.Printf("ModuleMalloc(%d) return offset: %d, native addr: %p\n", + 1024, offset, native_addr) + + if (!instance.ValidateAppAddr(offset, 1024)) { + fmt.Print("Validate app addr failed\n") + } + if (!instance.ValidateNativeAddr(native_addr, 1024)) { + fmt.Print("Validate native addr failed\n") + } + if (native_addr != instance.AddrAppToNative(offset)) { + fmt.Print("Convert app addr to native addr failed\n") + } + if (offset != instance.AddrNativeToApp(native_addr)) { + fmt.Print("Convert app addr to native addr failed\n") + } + + instance.ModuleFree(offset) + + /* + instance.DumpMemoryConsumption() + instance.DumpCallStack() + */ + + fmt.Print("\n"); + +fail: + if (instance != nil) { + fmt.Print("Destroy instance\n"); + instance.Destroy() + } + + if (module != nil) { + fmt.Print("Destroy module\n"); + module.Destroy() + } + + fmt.Print("Destroy wasm runtime\n"); + wamr.Runtime().Destroy() +} diff --git a/language-bindings/go/samples/wasm-app/build.sh b/language-bindings/go/samples/wasm-app/build.sh new file mode 100755 index 0000000000..1d0cc833c8 --- /dev/null +++ b/language-bindings/go/samples/wasm-app/build.sh @@ -0,0 +1,32 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +WAMR_DIR=${PWD}/../../.. + +echo "Build wasm app .." +/opt/wasi-sdk/bin/clang -O3 \ + -z stack-size=4096 -Wl,--initial-memory=65536 \ + -o test.wasm main.c \ + -Wl,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--export=fib2 \ + -Wl,--export=test1 \ + -Wl,--export=test2 \ + -Wl,--export=test3 \ + -Wl,--export=test4 \ + -Wl,--export=__data_end -Wl,--export=__heap_base \ + -Wl,--strip-all,--no-entry \ + -Wl,--allow-undefined \ + -nostdlib \ + +echo "Build binarydump tool .." +rm -fr build && mkdir build && cd build +cmake ../../../../../test-tools/binarydump-tool +make +cd .. + +echo "Generate test_wasm.h .." +./build/binarydump -o test_wasm.h -n wasm_test_file test.wasm + +rm -fr build + +echo "Done" diff --git a/language-bindings/go/samples/wasm-app/main.c b/language-bindings/go/samples/wasm-app/main.c new file mode 100644 index 0000000000..8d1f912428 --- /dev/null +++ b/language-bindings/go/samples/wasm-app/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +unsigned +fib2(unsigned n) +{ + if (n < 2) { + return 1; + } + return fib2(n - 2) + fib2(n - 1); +} + +void +test1(int32_t i32, int64_t i64, float f32, double f64) +{ + printf("i32: %d, i64: %lld, f32: %f, f64: %f\n", i32, i64, f32, f64); +} + +int64_t +test2(int64_t x, int64_t y) +{ + printf("%lld + %lld = %lld\n", x, y, x + y); + return x + y; +} + +double +test3(float x, double y) +{ + printf("%f * %f = %f\n", x, y, x * y); + return x * y; +} + +float +test4(double x, int32_t y) +{ + printf("%f / %d = %f\n", x, y, x / y); + return x / y; +} + +int +main(int argc, char **argv) +{ + char *buf; + + printf("Hello world!\n"); + + buf = malloc(1024); + if (!buf) { + printf("malloc buf failed\n"); + return -1; + } + + printf("buf ptr: %p\n", buf); + + snprintf(buf, 1024, "%s", "1234\n"); + printf("buf: %s", buf); + + free(buf); + return 0; +} diff --git a/language-bindings/go/wamr/cgo.go b/language-bindings/go/wamr/cgo.go new file mode 100644 index 0000000000..cac3bcbccc --- /dev/null +++ b/language-bindings/go/wamr/cgo.go @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +// #cgo CFLAGS: -I${SRCDIR}/packaged/include +// #cgo LDFLAGS: -liwasm -lm +// +// #cgo linux,amd64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/linux-amd64 -L${SRCDIR}/packaged/lib/linux-amd64 +// #cgo linux,arm64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/linux-aarch64 -L${SRCDIR}/packaged/lib/linux-aarch64 +// #cgo darwin,amd64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/darwin-amd64 -L${SRCDIR}/packaged/lib/darwin-amd64 +// #cgo darwin,arm64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/darwin-aarch64 -L${SRCDIR}/packaged/lib/darwin-aarch64 +// +// #include +import "C" + +import ( +) diff --git a/language-bindings/go/wamr/instance.go b/language-bindings/go/wamr/instance.go new file mode 100644 index 0000000000..a031c67a6d --- /dev/null +++ b/language-bindings/go/wamr/instance.go @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +/* +#include +#include + +static inline void +PUT_I64_TO_ADDR(uint32_t *addr, int64_t value) +{ + union { + int64_t val; + uint32_t parts[2]; + } u; + u.val = value; + addr[0] = u.parts[0]; + addr[1] = u.parts[1]; +} + +static inline void +PUT_F64_TO_ADDR(uint32_t *addr, double value) +{ + union { + double val; + uint32_t parts[2]; + } u; + u.val = value; + addr[0] = u.parts[0]; + addr[1] = u.parts[1]; +} + +static inline int64_t +GET_I64_FROM_ADDR(uint32_t *addr) +{ + union { + int64_t val; + uint32_t parts[2]; + } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +static inline double +GET_F64_FROM_ADDR(uint32_t *addr) +{ + union { + double val; + uint32_t parts[2]; + } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} +*/ +import "C" + +import ( + "runtime" + "unsafe" + "fmt" +) + +type Instance struct { + _instance C.wasm_module_inst_t + _exec_env C.wasm_exec_env_t + _module *Module + _exportsCache map[string]C.wasm_function_inst_t +} + +/* Create instance from the module */ +func NewInstance(module *Module, + stackSize uint, heapSize uint) (*Instance, error) { + if (module == nil) { + return nil, fmt.Errorf("NewInstance error: invalid input") + } + + errorBytes := make([]byte, 128) + errorPtr := (*C.char)(unsafe.Pointer(&errorBytes[0])) + errorLen := C.uint(len(errorBytes)) + + instance := C.wasm_runtime_instantiate(module.module, C.uint(stackSize), + C.uint(heapSize), errorPtr, errorLen) + if (instance == nil) { + return nil, fmt.Errorf("NewInstance Error: %s", string(errorBytes)) + } + + exec_env := C.wasm_runtime_create_exec_env(instance, C.uint(stackSize)) + if (exec_env == nil) { + C.wasm_runtime_deinstantiate(instance) + return nil, fmt.Errorf("NewInstance Error: create exec_env failed") + } + + self := &Instance{ + _instance: instance, + _exec_env: exec_env, + _module: module, + _exportsCache: make(map[string]C.wasm_function_inst_t), + } + + runtime.SetFinalizer(self, func(self *Instance) { + self.Destroy() + }) + + return self, nil +} + +/* Destroy the instance */ +func (self *Instance) Destroy() { + runtime.SetFinalizer(self, nil) + if (self._instance != nil) { + C.wasm_runtime_deinstantiate(self._instance) + } + if (self._exec_env != nil) { + C.wasm_runtime_destroy_exec_env(self._exec_env) + } +} + +/* Call the wasm function with argument in the uint32 array, and store + the return values back into the array */ +func (self *Instance) CallFunc(funcName string, + argc uint32, args []uint32) error { + _func := self._exportsCache[funcName] + if _func == nil { + cName := C.CString(funcName) + defer C.free(unsafe.Pointer(cName)) + + _func = C.wasm_runtime_lookup_function(self._instance, cName) + if _func == nil { + return fmt.Errorf("CallFunc error: lookup function failed") + } + self._exportsCache[funcName] = _func + } + + thread_env_inited := Runtime().ThreadEnvInited() + if (!thread_env_inited) { + Runtime().InitThreadEnv() + } + + var args_C *C.uint32_t + if (argc > 0) { + args_C = (*C.uint32_t)(unsafe.Pointer(&args[0])) + } + if (!C.wasm_runtime_call_wasm(self._exec_env, _func, + C.uint(argc), args_C)) { + if (!thread_env_inited) { + Runtime().DestroyThreadEnv() + } + return fmt.Errorf("CallFunc error: %s", string(self.GetException())) + } + + if (!thread_env_inited) { + Runtime().DestroyThreadEnv() + } + return nil +} + +/* Call the wasm function with variant arguments, and store the return + values back into the results array */ +func (self *Instance) CallFuncV(funcName string, + num_results uint32, results []interface{}, + args ... interface{}) error { + _func := self._exportsCache[funcName] + if _func == nil { + cName := C.CString(funcName) + defer C.free(unsafe.Pointer(cName)) + + _func = C.wasm_runtime_lookup_function(self._instance, cName) + if _func == nil { + return fmt.Errorf("CallFunc error: lookup function failed") + } + self._exportsCache[funcName] = _func + } + + param_count := uint32(C.wasm_func_get_param_count(_func, self._instance)) + result_count := uint32(C.wasm_func_get_result_count(_func, self._instance)) + + if (num_results < result_count) { + str := "CallFunc error: invalid result count %d, " + + "must be no smaller than %d" + return fmt.Errorf(str, num_results, result_count) + } + + param_types := make([]C.uchar, param_count, param_count) + result_types := make([]C.uchar, result_count, result_count) + if (param_count > 0) { + C.wasm_func_get_param_types(_func, self._instance, + (*C.uchar)(unsafe.Pointer(¶m_types[0]))) + } + if (result_count > 0) { + C.wasm_func_get_result_types(_func, self._instance, + (*C.uchar)(unsafe.Pointer(&result_types[0]))) + } + + argv_size := param_count * 2 + if (result_count > param_count) { + argv_size = result_count * 2 + } + argv := make([]uint32, argv_size, argv_size) + + var i, argc uint32 + for _, arg := range args { + if (i >= param_count) { + break; + } + switch arg.(type) { + case int32: + if (param_types[i] != C.WASM_I32 && + param_types[i] != C.WASM_FUNCREF && + param_types[i] != C.WASM_EXTERNREF) { + str := "CallFunc error: invalid param type %d, " + + "expect i32 but got other" + return fmt.Errorf(str, param_types[i]) + } + argv[argc] = (uint32)(arg.(int32)) + argc++ + break + case int64: + if (param_types[i] != C.WASM_I64) { + str := "CallFunc error: invalid param type %d, " + + "expect i64 but got other" + return fmt.Errorf(str, param_types[i]) + } + addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc])) + C.PUT_I64_TO_ADDR(addr, (C.int64_t)(arg.(int64))) + argc += 2 + break + case float32: + if (param_types[i] != C.WASM_F32) { + str := "CallFunc error: invalid param type %d, " + + "expect f32 but got other" + return fmt.Errorf(str, param_types[i]) + } + *(*C.float)(unsafe.Pointer(&argv[argc])) = (C.float)(arg.(float32)) + argc++ + break + case float64: + if (param_types[i] != C.WASM_F64) { + str := "CallFunc error: invalid param type %d, " + + "expect f64 but got other" + return fmt.Errorf(str, param_types[i]) + } + addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc])) + C.PUT_F64_TO_ADDR(addr, (C.double)(arg.(float64))) + argc += 2 + break + default: + return fmt.Errorf("CallFunc error: unknown param type %d", + param_types[i]) + } + i++ + } + + if (i < param_count) { + str := "CallFunc error: invalid param count, " + + "must be no smaller than %d" + return fmt.Errorf(str, param_count) + } + + err := self.CallFunc(funcName, argc, argv) + if (err != nil) { + return err + } + + argc = 0 + for i = 0; i < result_count; i++ { + switch result_types[i] { + case C.WASM_I32: + fallthrough + case C.WASM_FUNCREF: + fallthrough + case C.WASM_EXTERNREF: + i32 := (int32)(argv[argc]) + results[i] = i32 + argc++ + break + case C.WASM_I64: + addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc])) + results[i] = (int64)(C.GET_I64_FROM_ADDR(addr)) + argc += 2 + break + case C.WASM_F32: + addr := (*C.float)(unsafe.Pointer(&argv[argc])) + results[i] = (float32)(*addr) + argc++ + break + case C.WASM_F64: + addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc])) + results[i] = (float64)(C.GET_F64_FROM_ADDR(addr)) + argc += 2 + break + } + } + + return nil +} + +/* Get exception info of the instance */ +func (self *Instance) GetException() string { + cStr := C.wasm_runtime_get_exception(self._instance) + goStr := C.GoString(cStr) + return goStr +} + +/* Allocate memory from the heap of the instance */ +func (self Instance) ModuleMalloc(size uint64) (uint64, *uint8) { + var offset C.uint64_t + native_addrs := make([]*uint8, 1, 1) + ptr := unsafe.Pointer(&native_addrs[0]) + offset = C.wasm_runtime_module_malloc(self._instance, (C.uint64_t)(size), + (*unsafe.Pointer)(ptr)) + return (uint64)(offset), native_addrs[0] +} + +/* Free memory to the heap of the instance */ +func (self Instance) ModuleFree(offset uint64) { + C.wasm_runtime_module_free(self._instance, (C.uint64_t)(offset)) +} + +func (self Instance) ValidateAppAddr(app_offset uint64, size uint64) bool { + ret := C.wasm_runtime_validate_app_addr(self._instance, + (C.uint64_t)(app_offset), + (C.uint64_t)(size)) + return (bool)(ret) +} + +func (self Instance) ValidateStrAddr(app_str_offset uint64) bool { + ret := C.wasm_runtime_validate_app_str_addr(self._instance, + (C.uint64_t)(app_str_offset)) + return (bool)(ret) +} + +func (self Instance) ValidateNativeAddr(native_ptr *uint8, size uint64) bool { + native_ptr_C := (unsafe.Pointer)(native_ptr) + ret := C.wasm_runtime_validate_native_addr(self._instance, + native_ptr_C, + (C.uint64_t)(size)) + return (bool)(ret) +} + +func (self Instance) AddrAppToNative(app_offset uint64) *uint8 { + native_ptr := C.wasm_runtime_addr_app_to_native(self._instance, + (C.uint64_t)(app_offset)) + return (*uint8)(native_ptr) +} + +func (self Instance) AddrNativeToApp(native_ptr *uint8) uint64 { + native_ptr_C := (unsafe.Pointer)(native_ptr) + offset := C.wasm_runtime_addr_native_to_app(self._instance, + native_ptr_C) + return (uint64)(offset) +} + +func (self Instance) GetAppAddrRange(app_offset uint64) (bool, + uint64, + uint64) { + var start_offset, end_offset C.uint64_t + ret := C.wasm_runtime_get_app_addr_range(self._instance, + (C.uint64_t)(app_offset), + &start_offset, &end_offset) + return (bool)(ret), (uint64)(start_offset), (uint64)(end_offset) +} + +func (self Instance) GetNativeAddrRange(native_ptr *uint8) (bool, + *uint8, + *uint8) { + var start_addr, end_addr *C.uint8_t + native_ptr_C := (*C.uint8_t)((unsafe.Pointer)(native_ptr)) + ret := C.wasm_runtime_get_native_addr_range(self._instance, + native_ptr_C, + &start_addr, &end_addr) + return (bool)(ret), (*uint8)(start_addr), (*uint8)(end_addr) +} + +func (self Instance) DumpMemoryConsumption() { + C.wasm_runtime_dump_mem_consumption(self._exec_env) +} + +func (self Instance) DumpCallStack() { + C.wasm_runtime_dump_call_stack(self._exec_env) +} diff --git a/language-bindings/go/wamr/instance_test.go b/language-bindings/go/wamr/instance_test.go new file mode 100644 index 0000000000..ad679e2c62 --- /dev/null +++ b/language-bindings/go/wamr/instance_test.go @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +import ( + //"github.com/stretchr/testify/assert" + "testing" +) + +func TestInstance(t *testing.T) { + /* TODO */ +} + +func TestCallFunc(t *testing.T) { + /* TODO */ +} diff --git a/language-bindings/go/wamr/module.go b/language-bindings/go/wamr/module.go new file mode 100644 index 0000000000..7fd131ea6c --- /dev/null +++ b/language-bindings/go/wamr/module.go @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +// #include +import "C" +import ( + "unsafe" + "runtime" + "fmt" +) + +type Module struct { + module C.wasm_module_t +} + +/* Create WASM/AOT module from the memory buffer */ +func NewModule(wasmBytes []byte) (*Module, error) { + if (wasmBytes == nil || len(wasmBytes) == 0) { + return nil, fmt.Errorf("NewModule error: invalid input") + } + + wasmPtr := (*C.uint8_t)(unsafe.Pointer(&wasmBytes[0])) + wasmLen := C.uint(len(wasmBytes)) + + errorBytes := make([]byte, 128) + errorPtr := (*C.char)(unsafe.Pointer(&errorBytes[0])) + errorLen := C.uint(len(errorBytes)) + + m := C.wasm_runtime_load(wasmPtr, wasmLen, errorPtr, errorLen) + if (m == nil) { + return nil, fmt.Errorf("NewModule error: %s", string(errorBytes)) + } + + self := &Module{ + module: m, + } + + runtime.SetFinalizer(self, func(self *Module) { + self.Destroy() + }) + + return self, nil +} + +/* Destroy the module */ +func (self *Module) Destroy() { + runtime.SetFinalizer(self, nil) + if (self.module != nil) { + C.wasm_runtime_unload(self.module) + } +} + +/* Set module's wasi arguments */ +func (self *Module) SetWasiArgs(dirList [][]byte, mapDirList [][]byte, + env [][]byte, argv[][]byte) { + var dirPtr, mapDirPtr, envPtr, argvPtr **C.char + var dirCount, mapDirCount, envCount C.uint + var argc C.int + + if (dirList != nil) { + dirPtr = (**C.char)(unsafe.Pointer(&dirList[0])) + dirCount = C.uint(len(dirList)) + } + + if (mapDirList != nil) { + mapDirPtr = (**C.char)(unsafe.Pointer(&mapDirList[0])) + mapDirCount = C.uint(len(mapDirList)) + } + + if (env != nil) { + envPtr = (**C.char)(unsafe.Pointer(&env[0])) + envCount = C.uint(len(env)) + } + + if (argv != nil) { + argvPtr = (**C.char)(unsafe.Pointer(&argv[0])) + argc = C.int(len(argv)) + } + + C.wasm_runtime_set_wasi_args(self.module, dirPtr, dirCount, + mapDirPtr, mapDirCount, + envPtr, envCount, argvPtr, argc) +} + +/* Set module's wasi arguments */ +func (self *Module) SetWasiArgsEx(dirList [][]byte, mapDirList [][]byte, + env [][]byte, argv[][]byte, + stdinfd int, stdoutfd int, stderrfd int) { + var dirPtr, mapDirPtr, envPtr, argvPtr **C.char + var dirCount, mapDirCount, envCount C.uint + var argc C.int + + if (dirList != nil) { + dirPtr = (**C.char)(unsafe.Pointer(&dirList[0])) + dirCount = C.uint(len(dirList)) + } + + if (mapDirList != nil) { + mapDirPtr = (**C.char)(unsafe.Pointer(&mapDirList[0])) + mapDirCount = C.uint(len(mapDirList)) + } + + if (env != nil) { + envPtr = (**C.char)(unsafe.Pointer(&env[0])) + envCount = C.uint(len(env)) + } + + if (argv != nil) { + argvPtr = (**C.char)(unsafe.Pointer(&argv[0])) + argc = C.int(len(argv)) + } + + C.wasm_runtime_set_wasi_args_ex(self.module, dirPtr, dirCount, + mapDirPtr, mapDirCount, + envPtr, envCount, argvPtr, argc, + C.int64_t(stdinfd), C.int64_t(stdoutfd), + C.int64_t(stderrfd)) +} + +/* Set module's wasi network address pool */ +func (self *Module) SetWasiAddrPool(addrPool [][]byte) { + var addrPoolPtr **C.char + var addrPoolSize C.uint + + if (addrPool != nil) { + addrPoolPtr = (**C.char)(unsafe.Pointer(&addrPool[0])) + addrPoolSize = C.uint(len(addrPool)) + } + C.wasm_runtime_set_wasi_addr_pool(self.module, addrPoolPtr, addrPoolSize) +} + +/* Set module's wasi domain lookup pool */ +func(self *Module) SetWasiNsLookupPool(nsLookupPool [][]byte) { + var nsLookupPoolPtr **C.char + var nsLookupPoolSize C.uint + + if (nsLookupPool != nil) { + nsLookupPoolPtr = (**C.char)(unsafe.Pointer(&nsLookupPool[0])) + nsLookupPoolSize = C.uint(len(nsLookupPool)) + } + C.wasm_runtime_set_wasi_ns_lookup_pool(self.module, nsLookupPoolPtr, nsLookupPoolSize) +} diff --git a/language-bindings/go/wamr/module_test.go b/language-bindings/go/wamr/module_test.go new file mode 100644 index 0000000000..8abeccbaa6 --- /dev/null +++ b/language-bindings/go/wamr/module_test.go @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +import ( + //"github.com/stretchr/testify/assert" + "testing" +) + +func TestModule(t *testing.T) { + /* TODO */ +} diff --git a/language-bindings/go/wamr/packaged/include/dummy.go b/language-bindings/go/wamr/packaged/include/dummy.go new file mode 100644 index 0000000000..271e5c1677 --- /dev/null +++ b/language-bindings/go/wamr/packaged/include/dummy.go @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package include 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 0000000000..35f3c705fc --- /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/go/wamr/packaged/lib/darwin-amd64/dummy.go b/language-bindings/go/wamr/packaged/lib/darwin-amd64/dummy.go new file mode 100644 index 0000000000..fa8096ad6a --- /dev/null +++ b/language-bindings/go/wamr/packaged/lib/darwin-amd64/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_amd64 diff --git a/language-bindings/go/wamr/packaged/lib/dummy.go b/language-bindings/go/wamr/packaged/lib/dummy.go new file mode 100644 index 0000000000..20dfca8012 --- /dev/null +++ b/language-bindings/go/wamr/packaged/lib/dummy.go @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package lib diff --git a/language-bindings/go/wamr/packaged/lib/linux-amd64/dummy.go b/language-bindings/go/wamr/packaged/lib/linux-amd64/dummy.go new file mode 100644 index 0000000000..8a7c15a7d6 --- /dev/null +++ b/language-bindings/go/wamr/packaged/lib/linux-amd64/dummy.go @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package linux_amd64 diff --git a/language-bindings/go/wamr/runtime.go b/language-bindings/go/wamr/runtime.go new file mode 100644 index 0000000000..2c48a92ad7 --- /dev/null +++ b/language-bindings/go/wamr/runtime.go @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +/* +#include +#include + +#include + +void +bh_log_set_verbose_level(uint32_t level); + +bool +init_wamr_runtime(bool alloc_with_pool, uint8_t *heap_buf, + uint32_t heap_size, uint32_t max_thread_num) +{ + RuntimeInitArgs init_args; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + if (alloc_with_pool) { + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = heap_buf; + init_args.mem_alloc_option.pool.heap_size = heap_size; + } + else { + init_args.mem_alloc_type = Alloc_With_System_Allocator; + } + + return wasm_runtime_full_init(&init_args); +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +type LogLevel uint32 +const ( + LOG_LEVEL_FATAL LogLevel = 0 + LOG_LEVEL_ERROR LogLevel = 1 + LOG_LEVEL_WARNING LogLevel = 2 + LOG_LEVEL_DEBUG LogLevel = 3 + LOG_LEVEL_VERBOSE LogLevel = 4 +) + +/* +type NativeSymbol struct { + symbol string + func_ptr *uint8 + signature string +} +*/ + +type _Runtime struct { + initialized bool +} + +var _runtime_singleton *_Runtime + +/* Return the runtime singleton */ +func Runtime() *_Runtime { + if (_runtime_singleton == nil) { + self := &_Runtime{} + _runtime_singleton = self + } + return _runtime_singleton; +} + +/* Initialize the WASM runtime environment */ +func (self *_Runtime) FullInit(alloc_with_pool bool, heap_buf []byte, + max_thread_num uint) error { + var heap_buf_C *C.uchar + + if (self.initialized) { + return nil + } + + if (alloc_with_pool) { + if (heap_buf == nil) { + return fmt.Errorf("Failed to init WAMR runtime") + } + heap_buf_C = (*C.uchar)(unsafe.Pointer(&heap_buf[0])) + } + + if (!C.init_wamr_runtime((C.bool)(alloc_with_pool), heap_buf_C, + (C.uint)(len(heap_buf)), + (C.uint)(max_thread_num))) { + return fmt.Errorf("Failed to init WAMR runtime") + } + + self.initialized = true + return nil +} + +/* Initialize the WASM runtime environment */ +func (self *_Runtime) Init() error { + return self.FullInit(false, nil, 1) +} + +/* Destroy the WASM runtime environment */ +func (self *_Runtime) Destroy() { + if (self.initialized) { + C.wasm_runtime_destroy() + self.initialized = false + } +} + +/* Set log verbose level (0 to 5, default is 2), + larger level with more log */ +func (self *_Runtime) SetLogLevel(level LogLevel) { + C.bh_log_set_verbose_level(C.uint32_t(level)) +} + +/* +func (self *_Runtime) RegisterNatives(moduleName string, + nativeSymbols []NativeSymbol) { +} +*/ /* TODO */ + +func (self *_Runtime) InitThreadEnv() bool { + if (!C.wasm_runtime_init_thread_env()) { + return false + } + return true +} + +func (self *_Runtime) DestroyThreadEnv() { + C.wasm_runtime_destroy_thread_env(); +} + +func (self *_Runtime) ThreadEnvInited() bool { + if (!C.wasm_runtime_thread_env_inited()) { + return false + } + return true +} + +/* Allocate memory from runtime memory environment */ +func (self *_Runtime) Malloc(size uint32) *uint8 { + ptr := C.wasm_runtime_malloc((C.uint32_t)(size)) + return (*uint8)(ptr) +} + +/* Free memory to runtime memory environment */ +func (self *_Runtime) Free(ptr *uint8) { + C.wasm_runtime_free((unsafe.Pointer)(ptr)) +} diff --git a/language-bindings/go/wamr/runtime_test.go b/language-bindings/go/wamr/runtime_test.go new file mode 100644 index 0000000000..66fdf65e5d --- /dev/null +++ b/language-bindings/go/wamr/runtime_test.go @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +package wamr + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRuntime(t *testing.T) { + res := false + if (Runtime() != nil) { + res = true; + } + assert.Equal(t, res, true) + + err := Runtime().Init() + assert.NoError(t, err) + Runtime().Destroy() + + err = Runtime().FullInit(false, nil, 6) + assert.NoError(t, err) + Runtime().Destroy() + + err = Runtime().FullInit(false, nil, 0) + assert.NoError(t, err) + Runtime().Destroy() + + heap_buf := make([]byte, 128 * 1024) + err = Runtime().FullInit(true, heap_buf, 4) + assert.NoError(t, err) + Runtime().Destroy() + + Runtime().FullInit(false, nil, 0) + err = Runtime().FullInit(false, nil, 0) + assert.NoError(t, err) + Runtime().Destroy() + Runtime().Destroy() +} diff --git a/language-bindings/python/.gitignore b/language-bindings/python/.gitignore new file mode 100644 index 0000000000..65efaeda8a --- /dev/null +++ b/language-bindings/python/.gitignore @@ -0,0 +1,160 @@ +# Refer to https://github.com/github/gitignore/blob/main/Python.gitignore +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# virtual environment +Pipfile + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# VSCode settings +.vscode/ + diff --git a/language-bindings/python/LICENSE b/language-bindings/python/LICENSE new file mode 120000 index 0000000000..30cff7403d --- /dev/null +++ b/language-bindings/python/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/language-bindings/python/MANIFEST.in b/language-bindings/python/MANIFEST.in new file mode 100644 index 0000000000..9b0c0893f9 --- /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 new file mode 100644 index 0000000000..45604af7f2 --- /dev/null +++ b/language-bindings/python/README.md @@ -0,0 +1,36 @@ +# wamr-python + +The WAMR Python package contains a set of high-level bindings for WAMR API and WASM-C-API. + +## Installation + +* **Notice**: This python package need python >= `3.10`. + +To Install from local source tree in _development mode_ run the following command, + +```bash +python -m pip install -e . +``` + +In this mode the package appears to be installed but still is editable from the source tree. + +## Usage + +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 +``` + +In the order hand, to use the WASM-C-API, + +```py +import wamr.wasmcapi.ffi as ffi +``` + +For more information: + +* [WAMR API](./wamr-api) +* [WASM-C-API](./wasm-c-api) diff --git a/language-bindings/python/pyproject.toml b/language-bindings/python/pyproject.toml new file mode 100644 index 0000000000..a480ac6718 --- /dev/null +++ b/language-bindings/python/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +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 new file mode 100755 index 0000000000..1087c64251 --- /dev/null +++ b/language-bindings/python/setup.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# pylint: disable=missing-class-docstring +# 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: + readme = f.read() + +with open("LICENSE") as f: + license = f.read() + +setup( + name="wamr-python", + 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/wasm-micro-runtime", + license=license, + include_package_data=True, + cmdclass={ + 'develop': PreDevelopCommand, + 'install': PreInstallCommand, + 'egg_info': PreEggInfoCommand, + }, + python_requires='>=3.10' +) diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/__init__.py b/language-bindings/python/src/wamr/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from test-tools/IoT-APP-Store-Demo/wasm_django/devices/__init__.py rename to language-bindings/python/src/wamr/__init__.py diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/migrations/__init__.py b/language-bindings/python/src/wamr/libs/.placeholder old mode 100755 new mode 100644 similarity index 100% rename from test-tools/IoT-APP-Store-Demo/wasm_django/devices/migrations/__init__.py rename to language-bindings/python/src/wamr/libs/.placeholder diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/__init__.py b/language-bindings/python/src/wamr/wamrapi/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from test-tools/IoT-APP-Store-Demo/wasm_django/mysite/__init__.py rename to language-bindings/python/src/wamr/wamrapi/__init__.py 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 0000000000..9727faa37b --- /dev/null +++ b/language-bindings/python/src/wamr/wamrapi/wamr.py @@ -0,0 +1,249 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from ctypes import Array +from ctypes import addressof +from ctypes import c_char +from ctypes import c_uint +from ctypes import c_uint8 +from ctypes import c_uint64 +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 typing import List +from typing import Tuple +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 +from wamr.wamrapi.iwasm import wasm_runtime_start_debug_instance +from wamr.wamrapi.iwasm import wasm_runtime_call_indirect +from wamr.wamrapi.iwasm import wasm_runtime_get_module_inst +from wamr.wamrapi.iwasm import wasm_runtime_addr_app_to_native +from wamr.wamrapi.iwasm import wasm_runtime_addr_native_to_app +from wamr.wamrapi.iwasm import wasm_runtime_set_wasi_args + +ID_TO_EXEC_ENV_MAPPING = {} + + +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 * 1024 * 2, + ip_addr: str = "127.0.0.1", + instance_port: int = 1234, + ) -> 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 + # Debug port setting + init_args.ip_addr = bytes(ip_addr, "utf-8") + init_args.instance_port = instance_port + 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, + dir_list: List[str] | None = None, + preinitialized_module_inst: wasm_module_inst_t | None = None, + ): + # Store module ensures GC does not remove it + self.module = module + if dir_list: + self._set_wasi_args(module, dir_list) + if preinitialized_module_inst is None: + self.module_inst = self._create_module_inst(module, stack_size, heap_size) + else: + self.module_inst = preinitialized_module_inst + + def __del__(self): + print("deleting Instance") + wasm_runtime_deinstantiate(self.module_inst) + + def _set_wasi_args(self, module: Module, dir_list: List[str]) -> None: + LP_c_char = POINTER(c_char) + LP_LP_c_char = POINTER(LP_c_char) + + p = (LP_c_char * len(dir_list))() + for i, dir in enumerate(dir_list): + enc_dir = dir.encode("utf-8") + p[i] = create_string_buffer(enc_dir) + + na = cast(p, LP_LP_c_char) + wasm_runtime_set_wasi_args( + module.module, na, len(dir_list), None, 0, None, 0, None, 0 + ) + + 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 + + def malloc(self, nbytes: int, native_handler) -> c_uint64: + 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) + if not func: + raise Exception("Error while looking-up function") + return func + + def native_addr_to_app_addr(self, native_addr) -> c_void_p: + return wasm_runtime_addr_native_to_app(self.module_inst, native_addr) + + def app_addr_to_native_addr(self, app_addr) -> c_void_p: + return wasm_runtime_addr_app_to_native(self.module_inst, app_addr) + + +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) + self.env = addressof(self.exec_env.contents) + self.own_c = True + + ID_TO_EXEC_ENV_MAPPING[str(self.env)] = self + + def __del__(self): + if self.own_c: + print("deleting ExecEnv") + wasm_runtime_destroy_exec_env(self.exec_env) + del ID_TO_EXEC_ENV_MAPPING[str(self.env)] + + 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 + + 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 get_module_inst(self) -> Instance: + return self.module_inst + + def start_debugging(self) -> int: + return wasm_runtime_start_debug_instance(self.exec_env) + + def call_indirect(self, element_index: int, argc: int, argv: "POINTER[c_uint]"): + if not wasm_runtime_call_indirect(self.exec_env, element_index, argc, argv): + raise Exception("Error while calling function") + + @staticmethod + def wrap(env: int) -> "ExecEnv": + if str(env) in ID_TO_EXEC_ENV_MAPPING: + return ID_TO_EXEC_ENV_MAPPING[str(env)] + return InternalExecEnv(env) + + +class InternalExecEnv(ExecEnv): + """ + Generate Python ExecEnv-like object from a `wasm_exec_env_t` index. + """ + + def __init__(self, env: int): + self.env = env + self.exec_env = cast(env, wasm_exec_env_t) + self.module_inst = Instance( + module=object(), + preinitialized_module_inst=wasm_runtime_get_module_inst(self.exec_env), + ) + ID_TO_EXEC_ENV_MAPPING[str(env)] = self + + def __del__(self): + del ID_TO_EXEC_ENV_MAPPING[str(self.env)] diff --git a/language-bindings/python/src/wamr/wasmcapi/__init__.py b/language-bindings/python/src/wamr/wasmcapi/__init__.py new file mode 100644 index 0000000000..8d7404ad83 --- /dev/null +++ b/language-bindings/python/src/wamr/wasmcapi/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +__all__ = ["ffi"] diff --git a/language-bindings/python/src/wamr/wasmcapi/binding.py b/language-bindings/python/src/wamr/wasmcapi/binding.py new file mode 100644 index 0000000000..e8b0b383bc --- /dev/null +++ b/language-bindings/python/src/wamr/wasmcapi/binding.py @@ -0,0 +1,2029 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#It is a generated file. DO NOT EDIT. +# +from ctypes import * + +from .ffi import dereference, libiwasm, wasm_ref_t, wasm_val_t + + +wasm_byte_t = c_ubyte + +class wasm_byte_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(wasm_byte_t)), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_byte_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(self.data[i]) + ret += " " + return ret + + + +def wasm_byte_vec_new_empty(arg0): + _wasm_byte_vec_new_empty = libiwasm.wasm_byte_vec_new_empty + _wasm_byte_vec_new_empty.restype = None + _wasm_byte_vec_new_empty.argtypes = [POINTER(wasm_byte_vec_t)] + return _wasm_byte_vec_new_empty(arg0) + +def wasm_byte_vec_new_uninitialized(arg0,arg1): + _wasm_byte_vec_new_uninitialized = libiwasm.wasm_byte_vec_new_uninitialized + _wasm_byte_vec_new_uninitialized.restype = None + _wasm_byte_vec_new_uninitialized.argtypes = [POINTER(wasm_byte_vec_t),c_size_t] + return _wasm_byte_vec_new_uninitialized(arg0,arg1) + +def wasm_byte_vec_new(arg0,arg1,arg2): + _wasm_byte_vec_new = libiwasm.wasm_byte_vec_new + _wasm_byte_vec_new.restype = None + _wasm_byte_vec_new.argtypes = [POINTER(wasm_byte_vec_t),c_size_t,POINTER(wasm_byte_t)] + return _wasm_byte_vec_new(arg0,arg1,arg2) + +def wasm_byte_vec_copy(arg0,arg1): + _wasm_byte_vec_copy = libiwasm.wasm_byte_vec_copy + _wasm_byte_vec_copy.restype = None + _wasm_byte_vec_copy.argtypes = [POINTER(wasm_byte_vec_t),POINTER(wasm_byte_vec_t)] + return _wasm_byte_vec_copy(arg0,arg1) + +def wasm_byte_vec_delete(arg0): + _wasm_byte_vec_delete = libiwasm.wasm_byte_vec_delete + _wasm_byte_vec_delete.restype = None + _wasm_byte_vec_delete.argtypes = [POINTER(wasm_byte_vec_t)] + return _wasm_byte_vec_delete(arg0) + +wasm_name_t = wasm_byte_vec_t + +class wasm_config_t(Structure): + pass + +def wasm_config_delete(arg0): + _wasm_config_delete = libiwasm.wasm_config_delete + _wasm_config_delete.restype = None + _wasm_config_delete.argtypes = [POINTER(wasm_config_t)] + return _wasm_config_delete(arg0) + +def wasm_config_new(): + _wasm_config_new = libiwasm.wasm_config_new + _wasm_config_new.restype = POINTER(wasm_config_t) + _wasm_config_new.argtypes = None + return _wasm_config_new() + +class wasm_engine_t(Structure): + pass + +def wasm_engine_delete(arg0): + _wasm_engine_delete = libiwasm.wasm_engine_delete + _wasm_engine_delete.restype = None + _wasm_engine_delete.argtypes = [POINTER(wasm_engine_t)] + return _wasm_engine_delete(arg0) + +def wasm_engine_new(): + _wasm_engine_new = libiwasm.wasm_engine_new + _wasm_engine_new.restype = POINTER(wasm_engine_t) + _wasm_engine_new.argtypes = None + return _wasm_engine_new() + +def wasm_engine_new_with_config(arg0): + _wasm_engine_new_with_config = libiwasm.wasm_engine_new_with_config + _wasm_engine_new_with_config.restype = POINTER(wasm_engine_t) + _wasm_engine_new_with_config.argtypes = [POINTER(wasm_config_t)] + return _wasm_engine_new_with_config(arg0) + +class wasm_store_t(Structure): + pass + +def wasm_store_delete(arg0): + _wasm_store_delete = libiwasm.wasm_store_delete + _wasm_store_delete.restype = None + _wasm_store_delete.argtypes = [POINTER(wasm_store_t)] + return _wasm_store_delete(arg0) + +def wasm_store_new(arg0): + _wasm_store_new = libiwasm.wasm_store_new + _wasm_store_new.restype = POINTER(wasm_store_t) + _wasm_store_new.argtypes = [POINTER(wasm_engine_t)] + return _wasm_store_new(arg0) + +wasm_mutability_t = c_uint8 + +WASM_CONST = 0 +WASM_VAR = 1 + +class wasm_limits_t(Structure): + _fields_ = [ + ("min", c_uint32), + ("max", c_uint32), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_limits_t): + return False + return self.min == other.min and self.max == other.max + + def __repr__(self): + return f"{{min={self.min}, max={self.max}}}" + + +class wasm_valtype_t(Structure): + pass + +def wasm_valtype_delete(arg0): + _wasm_valtype_delete = libiwasm.wasm_valtype_delete + _wasm_valtype_delete.restype = None + _wasm_valtype_delete.argtypes = [POINTER(wasm_valtype_t)] + return _wasm_valtype_delete(arg0) + +class wasm_valtype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_valtype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_valtype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_valtype_vec_new_empty(arg0): + _wasm_valtype_vec_new_empty = libiwasm.wasm_valtype_vec_new_empty + _wasm_valtype_vec_new_empty.restype = None + _wasm_valtype_vec_new_empty.argtypes = [POINTER(wasm_valtype_vec_t)] + return _wasm_valtype_vec_new_empty(arg0) + +def wasm_valtype_vec_new_uninitialized(arg0,arg1): + _wasm_valtype_vec_new_uninitialized = libiwasm.wasm_valtype_vec_new_uninitialized + _wasm_valtype_vec_new_uninitialized.restype = None + _wasm_valtype_vec_new_uninitialized.argtypes = [POINTER(wasm_valtype_vec_t),c_size_t] + return _wasm_valtype_vec_new_uninitialized(arg0,arg1) + +def wasm_valtype_vec_new(arg0,arg1,arg2): + _wasm_valtype_vec_new = libiwasm.wasm_valtype_vec_new + _wasm_valtype_vec_new.restype = None + _wasm_valtype_vec_new.argtypes = [POINTER(wasm_valtype_vec_t),c_size_t,POINTER(POINTER(wasm_valtype_t))] + return _wasm_valtype_vec_new(arg0,arg1,arg2) + +def wasm_valtype_vec_copy(arg0,arg1): + _wasm_valtype_vec_copy = libiwasm.wasm_valtype_vec_copy + _wasm_valtype_vec_copy.restype = None + _wasm_valtype_vec_copy.argtypes = [POINTER(wasm_valtype_vec_t),POINTER(wasm_valtype_vec_t)] + return _wasm_valtype_vec_copy(arg0,arg1) + +def wasm_valtype_vec_delete(arg0): + _wasm_valtype_vec_delete = libiwasm.wasm_valtype_vec_delete + _wasm_valtype_vec_delete.restype = None + _wasm_valtype_vec_delete.argtypes = [POINTER(wasm_valtype_vec_t)] + return _wasm_valtype_vec_delete(arg0) + +def wasm_valtype_copy(arg0): + _wasm_valtype_copy = libiwasm.wasm_valtype_copy + _wasm_valtype_copy.restype = POINTER(wasm_valtype_t) + _wasm_valtype_copy.argtypes = [POINTER(wasm_valtype_t)] + return _wasm_valtype_copy(arg0) + +wasm_valkind_t = c_uint8 + +WASM_I32 = 0 +WASM_I64 = 1 +WASM_F32 = 2 +WASM_F64 = 3 +WASM_EXTERNREF = 128 +WASM_FUNCREF = 129 + +def wasm_valtype_new(arg0): + _wasm_valtype_new = libiwasm.wasm_valtype_new + _wasm_valtype_new.restype = POINTER(wasm_valtype_t) + _wasm_valtype_new.argtypes = [wasm_valkind_t] + return _wasm_valtype_new(arg0) + +def wasm_valtype_kind(arg0): + _wasm_valtype_kind = libiwasm.wasm_valtype_kind + _wasm_valtype_kind.restype = wasm_valkind_t + _wasm_valtype_kind.argtypes = [POINTER(wasm_valtype_t)] + return _wasm_valtype_kind(arg0) + +class wasm_functype_t(Structure): + pass + +def wasm_functype_delete(arg0): + _wasm_functype_delete = libiwasm.wasm_functype_delete + _wasm_functype_delete.restype = None + _wasm_functype_delete.argtypes = [POINTER(wasm_functype_t)] + return _wasm_functype_delete(arg0) + +class wasm_functype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_functype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_functype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_functype_vec_new_empty(arg0): + _wasm_functype_vec_new_empty = libiwasm.wasm_functype_vec_new_empty + _wasm_functype_vec_new_empty.restype = None + _wasm_functype_vec_new_empty.argtypes = [POINTER(wasm_functype_vec_t)] + return _wasm_functype_vec_new_empty(arg0) + +def wasm_functype_vec_new_uninitialized(arg0,arg1): + _wasm_functype_vec_new_uninitialized = libiwasm.wasm_functype_vec_new_uninitialized + _wasm_functype_vec_new_uninitialized.restype = None + _wasm_functype_vec_new_uninitialized.argtypes = [POINTER(wasm_functype_vec_t),c_size_t] + return _wasm_functype_vec_new_uninitialized(arg0,arg1) + +def wasm_functype_vec_new(arg0,arg1,arg2): + _wasm_functype_vec_new = libiwasm.wasm_functype_vec_new + _wasm_functype_vec_new.restype = None + _wasm_functype_vec_new.argtypes = [POINTER(wasm_functype_vec_t),c_size_t,POINTER(POINTER(wasm_functype_t))] + return _wasm_functype_vec_new(arg0,arg1,arg2) + +def wasm_functype_vec_copy(arg0,arg1): + _wasm_functype_vec_copy = libiwasm.wasm_functype_vec_copy + _wasm_functype_vec_copy.restype = None + _wasm_functype_vec_copy.argtypes = [POINTER(wasm_functype_vec_t),POINTER(wasm_functype_vec_t)] + return _wasm_functype_vec_copy(arg0,arg1) + +def wasm_functype_vec_delete(arg0): + _wasm_functype_vec_delete = libiwasm.wasm_functype_vec_delete + _wasm_functype_vec_delete.restype = None + _wasm_functype_vec_delete.argtypes = [POINTER(wasm_functype_vec_t)] + return _wasm_functype_vec_delete(arg0) + +def wasm_functype_copy(arg0): + _wasm_functype_copy = libiwasm.wasm_functype_copy + _wasm_functype_copy.restype = POINTER(wasm_functype_t) + _wasm_functype_copy.argtypes = [POINTER(wasm_functype_t)] + return _wasm_functype_copy(arg0) + +def wasm_functype_new(arg0,arg1): + _wasm_functype_new = libiwasm.wasm_functype_new + _wasm_functype_new.restype = POINTER(wasm_functype_t) + _wasm_functype_new.argtypes = [POINTER(wasm_valtype_vec_t),POINTER(wasm_valtype_vec_t)] + return _wasm_functype_new(arg0,arg1) + +def wasm_functype_params(arg0): + _wasm_functype_params = libiwasm.wasm_functype_params + _wasm_functype_params.restype = POINTER(wasm_valtype_vec_t) + _wasm_functype_params.argtypes = [POINTER(wasm_functype_t)] + return _wasm_functype_params(arg0) + +def wasm_functype_results(arg0): + _wasm_functype_results = libiwasm.wasm_functype_results + _wasm_functype_results.restype = POINTER(wasm_valtype_vec_t) + _wasm_functype_results.argtypes = [POINTER(wasm_functype_t)] + return _wasm_functype_results(arg0) + +class wasm_globaltype_t(Structure): + pass + +def wasm_globaltype_delete(arg0): + _wasm_globaltype_delete = libiwasm.wasm_globaltype_delete + _wasm_globaltype_delete.restype = None + _wasm_globaltype_delete.argtypes = [POINTER(wasm_globaltype_t)] + return _wasm_globaltype_delete(arg0) + +class wasm_globaltype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_globaltype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_globaltype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_globaltype_vec_new_empty(arg0): + _wasm_globaltype_vec_new_empty = libiwasm.wasm_globaltype_vec_new_empty + _wasm_globaltype_vec_new_empty.restype = None + _wasm_globaltype_vec_new_empty.argtypes = [POINTER(wasm_globaltype_vec_t)] + return _wasm_globaltype_vec_new_empty(arg0) + +def wasm_globaltype_vec_new_uninitialized(arg0,arg1): + _wasm_globaltype_vec_new_uninitialized = libiwasm.wasm_globaltype_vec_new_uninitialized + _wasm_globaltype_vec_new_uninitialized.restype = None + _wasm_globaltype_vec_new_uninitialized.argtypes = [POINTER(wasm_globaltype_vec_t),c_size_t] + return _wasm_globaltype_vec_new_uninitialized(arg0,arg1) + +def wasm_globaltype_vec_new(arg0,arg1,arg2): + _wasm_globaltype_vec_new = libiwasm.wasm_globaltype_vec_new + _wasm_globaltype_vec_new.restype = None + _wasm_globaltype_vec_new.argtypes = [POINTER(wasm_globaltype_vec_t),c_size_t,POINTER(POINTER(wasm_globaltype_t))] + return _wasm_globaltype_vec_new(arg0,arg1,arg2) + +def wasm_globaltype_vec_copy(arg0,arg1): + _wasm_globaltype_vec_copy = libiwasm.wasm_globaltype_vec_copy + _wasm_globaltype_vec_copy.restype = None + _wasm_globaltype_vec_copy.argtypes = [POINTER(wasm_globaltype_vec_t),POINTER(wasm_globaltype_vec_t)] + return _wasm_globaltype_vec_copy(arg0,arg1) + +def wasm_globaltype_vec_delete(arg0): + _wasm_globaltype_vec_delete = libiwasm.wasm_globaltype_vec_delete + _wasm_globaltype_vec_delete.restype = None + _wasm_globaltype_vec_delete.argtypes = [POINTER(wasm_globaltype_vec_t)] + return _wasm_globaltype_vec_delete(arg0) + +def wasm_globaltype_copy(arg0): + _wasm_globaltype_copy = libiwasm.wasm_globaltype_copy + _wasm_globaltype_copy.restype = POINTER(wasm_globaltype_t) + _wasm_globaltype_copy.argtypes = [POINTER(wasm_globaltype_t)] + return _wasm_globaltype_copy(arg0) + +def wasm_globaltype_new(arg0,arg1): + _wasm_globaltype_new = libiwasm.wasm_globaltype_new + _wasm_globaltype_new.restype = POINTER(wasm_globaltype_t) + _wasm_globaltype_new.argtypes = [POINTER(wasm_valtype_t),wasm_mutability_t] + return _wasm_globaltype_new(arg0,arg1) + +def wasm_globaltype_content(arg0): + _wasm_globaltype_content = libiwasm.wasm_globaltype_content + _wasm_globaltype_content.restype = POINTER(wasm_valtype_t) + _wasm_globaltype_content.argtypes = [POINTER(wasm_globaltype_t)] + return _wasm_globaltype_content(arg0) + +def wasm_globaltype_mutability(arg0): + _wasm_globaltype_mutability = libiwasm.wasm_globaltype_mutability + _wasm_globaltype_mutability.restype = wasm_mutability_t + _wasm_globaltype_mutability.argtypes = [POINTER(wasm_globaltype_t)] + return _wasm_globaltype_mutability(arg0) + +class wasm_tabletype_t(Structure): + pass + +def wasm_tabletype_delete(arg0): + _wasm_tabletype_delete = libiwasm.wasm_tabletype_delete + _wasm_tabletype_delete.restype = None + _wasm_tabletype_delete.argtypes = [POINTER(wasm_tabletype_t)] + return _wasm_tabletype_delete(arg0) + +class wasm_tabletype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_tabletype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_tabletype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_tabletype_vec_new_empty(arg0): + _wasm_tabletype_vec_new_empty = libiwasm.wasm_tabletype_vec_new_empty + _wasm_tabletype_vec_new_empty.restype = None + _wasm_tabletype_vec_new_empty.argtypes = [POINTER(wasm_tabletype_vec_t)] + return _wasm_tabletype_vec_new_empty(arg0) + +def wasm_tabletype_vec_new_uninitialized(arg0,arg1): + _wasm_tabletype_vec_new_uninitialized = libiwasm.wasm_tabletype_vec_new_uninitialized + _wasm_tabletype_vec_new_uninitialized.restype = None + _wasm_tabletype_vec_new_uninitialized.argtypes = [POINTER(wasm_tabletype_vec_t),c_size_t] + return _wasm_tabletype_vec_new_uninitialized(arg0,arg1) + +def wasm_tabletype_vec_new(arg0,arg1,arg2): + _wasm_tabletype_vec_new = libiwasm.wasm_tabletype_vec_new + _wasm_tabletype_vec_new.restype = None + _wasm_tabletype_vec_new.argtypes = [POINTER(wasm_tabletype_vec_t),c_size_t,POINTER(POINTER(wasm_tabletype_t))] + return _wasm_tabletype_vec_new(arg0,arg1,arg2) + +def wasm_tabletype_vec_copy(arg0,arg1): + _wasm_tabletype_vec_copy = libiwasm.wasm_tabletype_vec_copy + _wasm_tabletype_vec_copy.restype = None + _wasm_tabletype_vec_copy.argtypes = [POINTER(wasm_tabletype_vec_t),POINTER(wasm_tabletype_vec_t)] + return _wasm_tabletype_vec_copy(arg0,arg1) + +def wasm_tabletype_vec_delete(arg0): + _wasm_tabletype_vec_delete = libiwasm.wasm_tabletype_vec_delete + _wasm_tabletype_vec_delete.restype = None + _wasm_tabletype_vec_delete.argtypes = [POINTER(wasm_tabletype_vec_t)] + return _wasm_tabletype_vec_delete(arg0) + +def wasm_tabletype_copy(arg0): + _wasm_tabletype_copy = libiwasm.wasm_tabletype_copy + _wasm_tabletype_copy.restype = POINTER(wasm_tabletype_t) + _wasm_tabletype_copy.argtypes = [POINTER(wasm_tabletype_t)] + return _wasm_tabletype_copy(arg0) + +def wasm_tabletype_new(arg0,arg1): + _wasm_tabletype_new = libiwasm.wasm_tabletype_new + _wasm_tabletype_new.restype = POINTER(wasm_tabletype_t) + _wasm_tabletype_new.argtypes = [POINTER(wasm_valtype_t),POINTER(wasm_limits_t)] + return _wasm_tabletype_new(arg0,arg1) + +def wasm_tabletype_element(arg0): + _wasm_tabletype_element = libiwasm.wasm_tabletype_element + _wasm_tabletype_element.restype = POINTER(wasm_valtype_t) + _wasm_tabletype_element.argtypes = [POINTER(wasm_tabletype_t)] + return _wasm_tabletype_element(arg0) + +def wasm_tabletype_limits(arg0): + _wasm_tabletype_limits = libiwasm.wasm_tabletype_limits + _wasm_tabletype_limits.restype = POINTER(wasm_limits_t) + _wasm_tabletype_limits.argtypes = [POINTER(wasm_tabletype_t)] + return _wasm_tabletype_limits(arg0) + +class wasm_memorytype_t(Structure): + pass + +def wasm_memorytype_delete(arg0): + _wasm_memorytype_delete = libiwasm.wasm_memorytype_delete + _wasm_memorytype_delete.restype = None + _wasm_memorytype_delete.argtypes = [POINTER(wasm_memorytype_t)] + return _wasm_memorytype_delete(arg0) + +class wasm_memorytype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_memorytype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_memorytype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_memorytype_vec_new_empty(arg0): + _wasm_memorytype_vec_new_empty = libiwasm.wasm_memorytype_vec_new_empty + _wasm_memorytype_vec_new_empty.restype = None + _wasm_memorytype_vec_new_empty.argtypes = [POINTER(wasm_memorytype_vec_t)] + return _wasm_memorytype_vec_new_empty(arg0) + +def wasm_memorytype_vec_new_uninitialized(arg0,arg1): + _wasm_memorytype_vec_new_uninitialized = libiwasm.wasm_memorytype_vec_new_uninitialized + _wasm_memorytype_vec_new_uninitialized.restype = None + _wasm_memorytype_vec_new_uninitialized.argtypes = [POINTER(wasm_memorytype_vec_t),c_size_t] + return _wasm_memorytype_vec_new_uninitialized(arg0,arg1) + +def wasm_memorytype_vec_new(arg0,arg1,arg2): + _wasm_memorytype_vec_new = libiwasm.wasm_memorytype_vec_new + _wasm_memorytype_vec_new.restype = None + _wasm_memorytype_vec_new.argtypes = [POINTER(wasm_memorytype_vec_t),c_size_t,POINTER(POINTER(wasm_memorytype_t))] + return _wasm_memorytype_vec_new(arg0,arg1,arg2) + +def wasm_memorytype_vec_copy(arg0,arg1): + _wasm_memorytype_vec_copy = libiwasm.wasm_memorytype_vec_copy + _wasm_memorytype_vec_copy.restype = None + _wasm_memorytype_vec_copy.argtypes = [POINTER(wasm_memorytype_vec_t),POINTER(wasm_memorytype_vec_t)] + return _wasm_memorytype_vec_copy(arg0,arg1) + +def wasm_memorytype_vec_delete(arg0): + _wasm_memorytype_vec_delete = libiwasm.wasm_memorytype_vec_delete + _wasm_memorytype_vec_delete.restype = None + _wasm_memorytype_vec_delete.argtypes = [POINTER(wasm_memorytype_vec_t)] + return _wasm_memorytype_vec_delete(arg0) + +def wasm_memorytype_copy(arg0): + _wasm_memorytype_copy = libiwasm.wasm_memorytype_copy + _wasm_memorytype_copy.restype = POINTER(wasm_memorytype_t) + _wasm_memorytype_copy.argtypes = [POINTER(wasm_memorytype_t)] + return _wasm_memorytype_copy(arg0) + +def wasm_memorytype_new(arg0): + _wasm_memorytype_new = libiwasm.wasm_memorytype_new + _wasm_memorytype_new.restype = POINTER(wasm_memorytype_t) + _wasm_memorytype_new.argtypes = [POINTER(wasm_limits_t)] + return _wasm_memorytype_new(arg0) + +def wasm_memorytype_limits(arg0): + _wasm_memorytype_limits = libiwasm.wasm_memorytype_limits + _wasm_memorytype_limits.restype = POINTER(wasm_limits_t) + _wasm_memorytype_limits.argtypes = [POINTER(wasm_memorytype_t)] + return _wasm_memorytype_limits(arg0) + +class wasm_externtype_t(Structure): + pass + +def wasm_externtype_delete(arg0): + _wasm_externtype_delete = libiwasm.wasm_externtype_delete + _wasm_externtype_delete.restype = None + _wasm_externtype_delete.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_delete(arg0) + +class wasm_externtype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_externtype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_externtype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_externtype_vec_new_empty(arg0): + _wasm_externtype_vec_new_empty = libiwasm.wasm_externtype_vec_new_empty + _wasm_externtype_vec_new_empty.restype = None + _wasm_externtype_vec_new_empty.argtypes = [POINTER(wasm_externtype_vec_t)] + return _wasm_externtype_vec_new_empty(arg0) + +def wasm_externtype_vec_new_uninitialized(arg0,arg1): + _wasm_externtype_vec_new_uninitialized = libiwasm.wasm_externtype_vec_new_uninitialized + _wasm_externtype_vec_new_uninitialized.restype = None + _wasm_externtype_vec_new_uninitialized.argtypes = [POINTER(wasm_externtype_vec_t),c_size_t] + return _wasm_externtype_vec_new_uninitialized(arg0,arg1) + +def wasm_externtype_vec_new(arg0,arg1,arg2): + _wasm_externtype_vec_new = libiwasm.wasm_externtype_vec_new + _wasm_externtype_vec_new.restype = None + _wasm_externtype_vec_new.argtypes = [POINTER(wasm_externtype_vec_t),c_size_t,POINTER(POINTER(wasm_externtype_t))] + return _wasm_externtype_vec_new(arg0,arg1,arg2) + +def wasm_externtype_vec_copy(arg0,arg1): + _wasm_externtype_vec_copy = libiwasm.wasm_externtype_vec_copy + _wasm_externtype_vec_copy.restype = None + _wasm_externtype_vec_copy.argtypes = [POINTER(wasm_externtype_vec_t),POINTER(wasm_externtype_vec_t)] + return _wasm_externtype_vec_copy(arg0,arg1) + +def wasm_externtype_vec_delete(arg0): + _wasm_externtype_vec_delete = libiwasm.wasm_externtype_vec_delete + _wasm_externtype_vec_delete.restype = None + _wasm_externtype_vec_delete.argtypes = [POINTER(wasm_externtype_vec_t)] + return _wasm_externtype_vec_delete(arg0) + +def wasm_externtype_copy(arg0): + _wasm_externtype_copy = libiwasm.wasm_externtype_copy + _wasm_externtype_copy.restype = POINTER(wasm_externtype_t) + _wasm_externtype_copy.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_copy(arg0) + +wasm_externkind_t = c_uint8 + +WASM_EXTERN_FUNC = 0 +WASM_EXTERN_GLOBAL = 1 +WASM_EXTERN_TABLE = 2 +WASM_EXTERN_MEMORY = 3 + +def wasm_externtype_kind(arg0): + _wasm_externtype_kind = libiwasm.wasm_externtype_kind + _wasm_externtype_kind.restype = wasm_externkind_t + _wasm_externtype_kind.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_kind(arg0) + +def wasm_functype_as_externtype(arg0): + _wasm_functype_as_externtype = libiwasm.wasm_functype_as_externtype + _wasm_functype_as_externtype.restype = POINTER(wasm_externtype_t) + _wasm_functype_as_externtype.argtypes = [POINTER(wasm_functype_t)] + return _wasm_functype_as_externtype(arg0) + +def wasm_globaltype_as_externtype(arg0): + _wasm_globaltype_as_externtype = libiwasm.wasm_globaltype_as_externtype + _wasm_globaltype_as_externtype.restype = POINTER(wasm_externtype_t) + _wasm_globaltype_as_externtype.argtypes = [POINTER(wasm_globaltype_t)] + return _wasm_globaltype_as_externtype(arg0) + +def wasm_tabletype_as_externtype(arg0): + _wasm_tabletype_as_externtype = libiwasm.wasm_tabletype_as_externtype + _wasm_tabletype_as_externtype.restype = POINTER(wasm_externtype_t) + _wasm_tabletype_as_externtype.argtypes = [POINTER(wasm_tabletype_t)] + return _wasm_tabletype_as_externtype(arg0) + +def wasm_memorytype_as_externtype(arg0): + _wasm_memorytype_as_externtype = libiwasm.wasm_memorytype_as_externtype + _wasm_memorytype_as_externtype.restype = POINTER(wasm_externtype_t) + _wasm_memorytype_as_externtype.argtypes = [POINTER(wasm_memorytype_t)] + return _wasm_memorytype_as_externtype(arg0) + +def wasm_externtype_as_functype(arg0): + _wasm_externtype_as_functype = libiwasm.wasm_externtype_as_functype + _wasm_externtype_as_functype.restype = POINTER(wasm_functype_t) + _wasm_externtype_as_functype.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_functype(arg0) + +def wasm_externtype_as_globaltype(arg0): + _wasm_externtype_as_globaltype = libiwasm.wasm_externtype_as_globaltype + _wasm_externtype_as_globaltype.restype = POINTER(wasm_globaltype_t) + _wasm_externtype_as_globaltype.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_globaltype(arg0) + +def wasm_externtype_as_tabletype(arg0): + _wasm_externtype_as_tabletype = libiwasm.wasm_externtype_as_tabletype + _wasm_externtype_as_tabletype.restype = POINTER(wasm_tabletype_t) + _wasm_externtype_as_tabletype.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_tabletype(arg0) + +def wasm_externtype_as_memorytype(arg0): + _wasm_externtype_as_memorytype = libiwasm.wasm_externtype_as_memorytype + _wasm_externtype_as_memorytype.restype = POINTER(wasm_memorytype_t) + _wasm_externtype_as_memorytype.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_memorytype(arg0) + +def wasm_functype_as_externtype_const(arg0): + _wasm_functype_as_externtype_const = libiwasm.wasm_functype_as_externtype_const + _wasm_functype_as_externtype_const.restype = POINTER(wasm_externtype_t) + _wasm_functype_as_externtype_const.argtypes = [POINTER(wasm_functype_t)] + return _wasm_functype_as_externtype_const(arg0) + +def wasm_globaltype_as_externtype_const(arg0): + _wasm_globaltype_as_externtype_const = libiwasm.wasm_globaltype_as_externtype_const + _wasm_globaltype_as_externtype_const.restype = POINTER(wasm_externtype_t) + _wasm_globaltype_as_externtype_const.argtypes = [POINTER(wasm_globaltype_t)] + return _wasm_globaltype_as_externtype_const(arg0) + +def wasm_tabletype_as_externtype_const(arg0): + _wasm_tabletype_as_externtype_const = libiwasm.wasm_tabletype_as_externtype_const + _wasm_tabletype_as_externtype_const.restype = POINTER(wasm_externtype_t) + _wasm_tabletype_as_externtype_const.argtypes = [POINTER(wasm_tabletype_t)] + return _wasm_tabletype_as_externtype_const(arg0) + +def wasm_memorytype_as_externtype_const(arg0): + _wasm_memorytype_as_externtype_const = libiwasm.wasm_memorytype_as_externtype_const + _wasm_memorytype_as_externtype_const.restype = POINTER(wasm_externtype_t) + _wasm_memorytype_as_externtype_const.argtypes = [POINTER(wasm_memorytype_t)] + return _wasm_memorytype_as_externtype_const(arg0) + +def wasm_externtype_as_functype_const(arg0): + _wasm_externtype_as_functype_const = libiwasm.wasm_externtype_as_functype_const + _wasm_externtype_as_functype_const.restype = POINTER(wasm_functype_t) + _wasm_externtype_as_functype_const.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_functype_const(arg0) + +def wasm_externtype_as_globaltype_const(arg0): + _wasm_externtype_as_globaltype_const = libiwasm.wasm_externtype_as_globaltype_const + _wasm_externtype_as_globaltype_const.restype = POINTER(wasm_globaltype_t) + _wasm_externtype_as_globaltype_const.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_globaltype_const(arg0) + +def wasm_externtype_as_tabletype_const(arg0): + _wasm_externtype_as_tabletype_const = libiwasm.wasm_externtype_as_tabletype_const + _wasm_externtype_as_tabletype_const.restype = POINTER(wasm_tabletype_t) + _wasm_externtype_as_tabletype_const.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_tabletype_const(arg0) + +def wasm_externtype_as_memorytype_const(arg0): + _wasm_externtype_as_memorytype_const = libiwasm.wasm_externtype_as_memorytype_const + _wasm_externtype_as_memorytype_const.restype = POINTER(wasm_memorytype_t) + _wasm_externtype_as_memorytype_const.argtypes = [POINTER(wasm_externtype_t)] + return _wasm_externtype_as_memorytype_const(arg0) + +class wasm_importtype_t(Structure): + pass + +def wasm_importtype_delete(arg0): + _wasm_importtype_delete = libiwasm.wasm_importtype_delete + _wasm_importtype_delete.restype = None + _wasm_importtype_delete.argtypes = [POINTER(wasm_importtype_t)] + return _wasm_importtype_delete(arg0) + +class wasm_importtype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_importtype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_importtype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_importtype_vec_new_empty(arg0): + _wasm_importtype_vec_new_empty = libiwasm.wasm_importtype_vec_new_empty + _wasm_importtype_vec_new_empty.restype = None + _wasm_importtype_vec_new_empty.argtypes = [POINTER(wasm_importtype_vec_t)] + return _wasm_importtype_vec_new_empty(arg0) + +def wasm_importtype_vec_new_uninitialized(arg0,arg1): + _wasm_importtype_vec_new_uninitialized = libiwasm.wasm_importtype_vec_new_uninitialized + _wasm_importtype_vec_new_uninitialized.restype = None + _wasm_importtype_vec_new_uninitialized.argtypes = [POINTER(wasm_importtype_vec_t),c_size_t] + return _wasm_importtype_vec_new_uninitialized(arg0,arg1) + +def wasm_importtype_vec_new(arg0,arg1,arg2): + _wasm_importtype_vec_new = libiwasm.wasm_importtype_vec_new + _wasm_importtype_vec_new.restype = None + _wasm_importtype_vec_new.argtypes = [POINTER(wasm_importtype_vec_t),c_size_t,POINTER(POINTER(wasm_importtype_t))] + return _wasm_importtype_vec_new(arg0,arg1,arg2) + +def wasm_importtype_vec_copy(arg0,arg1): + _wasm_importtype_vec_copy = libiwasm.wasm_importtype_vec_copy + _wasm_importtype_vec_copy.restype = None + _wasm_importtype_vec_copy.argtypes = [POINTER(wasm_importtype_vec_t),POINTER(wasm_importtype_vec_t)] + return _wasm_importtype_vec_copy(arg0,arg1) + +def wasm_importtype_vec_delete(arg0): + _wasm_importtype_vec_delete = libiwasm.wasm_importtype_vec_delete + _wasm_importtype_vec_delete.restype = None + _wasm_importtype_vec_delete.argtypes = [POINTER(wasm_importtype_vec_t)] + return _wasm_importtype_vec_delete(arg0) + +def wasm_importtype_copy(arg0): + _wasm_importtype_copy = libiwasm.wasm_importtype_copy + _wasm_importtype_copy.restype = POINTER(wasm_importtype_t) + _wasm_importtype_copy.argtypes = [POINTER(wasm_importtype_t)] + return _wasm_importtype_copy(arg0) + +def wasm_importtype_new(arg0,arg1,arg2): + _wasm_importtype_new = libiwasm.wasm_importtype_new + _wasm_importtype_new.restype = POINTER(wasm_importtype_t) + _wasm_importtype_new.argtypes = [POINTER(wasm_name_t),POINTER(wasm_name_t),POINTER(wasm_externtype_t)] + return _wasm_importtype_new(arg0,arg1,arg2) + +def wasm_importtype_module(arg0): + _wasm_importtype_module = libiwasm.wasm_importtype_module + _wasm_importtype_module.restype = POINTER(wasm_name_t) + _wasm_importtype_module.argtypes = [POINTER(wasm_importtype_t)] + return _wasm_importtype_module(arg0) + +def wasm_importtype_name(arg0): + _wasm_importtype_name = libiwasm.wasm_importtype_name + _wasm_importtype_name.restype = POINTER(wasm_name_t) + _wasm_importtype_name.argtypes = [POINTER(wasm_importtype_t)] + return _wasm_importtype_name(arg0) + +def wasm_importtype_type(arg0): + _wasm_importtype_type = libiwasm.wasm_importtype_type + _wasm_importtype_type.restype = POINTER(wasm_externtype_t) + _wasm_importtype_type.argtypes = [POINTER(wasm_importtype_t)] + return _wasm_importtype_type(arg0) + +class wasm_exporttype_t(Structure): + pass + +def wasm_exporttype_delete(arg0): + _wasm_exporttype_delete = libiwasm.wasm_exporttype_delete + _wasm_exporttype_delete.restype = None + _wasm_exporttype_delete.argtypes = [POINTER(wasm_exporttype_t)] + return _wasm_exporttype_delete(arg0) + +class wasm_exporttype_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_exporttype_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_exporttype_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_exporttype_vec_new_empty(arg0): + _wasm_exporttype_vec_new_empty = libiwasm.wasm_exporttype_vec_new_empty + _wasm_exporttype_vec_new_empty.restype = None + _wasm_exporttype_vec_new_empty.argtypes = [POINTER(wasm_exporttype_vec_t)] + return _wasm_exporttype_vec_new_empty(arg0) + +def wasm_exporttype_vec_new_uninitialized(arg0,arg1): + _wasm_exporttype_vec_new_uninitialized = libiwasm.wasm_exporttype_vec_new_uninitialized + _wasm_exporttype_vec_new_uninitialized.restype = None + _wasm_exporttype_vec_new_uninitialized.argtypes = [POINTER(wasm_exporttype_vec_t),c_size_t] + return _wasm_exporttype_vec_new_uninitialized(arg0,arg1) + +def wasm_exporttype_vec_new(arg0,arg1,arg2): + _wasm_exporttype_vec_new = libiwasm.wasm_exporttype_vec_new + _wasm_exporttype_vec_new.restype = None + _wasm_exporttype_vec_new.argtypes = [POINTER(wasm_exporttype_vec_t),c_size_t,POINTER(POINTER(wasm_exporttype_t))] + return _wasm_exporttype_vec_new(arg0,arg1,arg2) + +def wasm_exporttype_vec_copy(arg0,arg1): + _wasm_exporttype_vec_copy = libiwasm.wasm_exporttype_vec_copy + _wasm_exporttype_vec_copy.restype = None + _wasm_exporttype_vec_copy.argtypes = [POINTER(wasm_exporttype_vec_t),POINTER(wasm_exporttype_vec_t)] + return _wasm_exporttype_vec_copy(arg0,arg1) + +def wasm_exporttype_vec_delete(arg0): + _wasm_exporttype_vec_delete = libiwasm.wasm_exporttype_vec_delete + _wasm_exporttype_vec_delete.restype = None + _wasm_exporttype_vec_delete.argtypes = [POINTER(wasm_exporttype_vec_t)] + return _wasm_exporttype_vec_delete(arg0) + +def wasm_exporttype_copy(arg0): + _wasm_exporttype_copy = libiwasm.wasm_exporttype_copy + _wasm_exporttype_copy.restype = POINTER(wasm_exporttype_t) + _wasm_exporttype_copy.argtypes = [POINTER(wasm_exporttype_t)] + return _wasm_exporttype_copy(arg0) + +def wasm_exporttype_new(arg0,arg1): + _wasm_exporttype_new = libiwasm.wasm_exporttype_new + _wasm_exporttype_new.restype = POINTER(wasm_exporttype_t) + _wasm_exporttype_new.argtypes = [POINTER(wasm_name_t),POINTER(wasm_externtype_t)] + return _wasm_exporttype_new(arg0,arg1) + +def wasm_exporttype_name(arg0): + _wasm_exporttype_name = libiwasm.wasm_exporttype_name + _wasm_exporttype_name.restype = POINTER(wasm_name_t) + _wasm_exporttype_name.argtypes = [POINTER(wasm_exporttype_t)] + return _wasm_exporttype_name(arg0) + +def wasm_exporttype_type(arg0): + _wasm_exporttype_type = libiwasm.wasm_exporttype_type + _wasm_exporttype_type.restype = POINTER(wasm_externtype_t) + _wasm_exporttype_type.argtypes = [POINTER(wasm_exporttype_t)] + return _wasm_exporttype_type(arg0) + +def wasm_val_delete(arg0): + _wasm_val_delete = libiwasm.wasm_val_delete + _wasm_val_delete.restype = None + _wasm_val_delete.argtypes = [POINTER(wasm_val_t)] + return _wasm_val_delete(arg0) + +def wasm_val_copy(arg0,arg1): + _wasm_val_copy = libiwasm.wasm_val_copy + _wasm_val_copy.restype = None + _wasm_val_copy.argtypes = [POINTER(wasm_val_t),POINTER(wasm_val_t)] + return _wasm_val_copy(arg0,arg1) + +class wasm_val_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(wasm_val_t)), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_val_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(self.data[i]) + ret += " " + return ret + + + +def wasm_val_vec_new_empty(arg0): + _wasm_val_vec_new_empty = libiwasm.wasm_val_vec_new_empty + _wasm_val_vec_new_empty.restype = None + _wasm_val_vec_new_empty.argtypes = [POINTER(wasm_val_vec_t)] + return _wasm_val_vec_new_empty(arg0) + +def wasm_val_vec_new_uninitialized(arg0,arg1): + _wasm_val_vec_new_uninitialized = libiwasm.wasm_val_vec_new_uninitialized + _wasm_val_vec_new_uninitialized.restype = None + _wasm_val_vec_new_uninitialized.argtypes = [POINTER(wasm_val_vec_t),c_size_t] + return _wasm_val_vec_new_uninitialized(arg0,arg1) + +def wasm_val_vec_new(arg0,arg1,arg2): + _wasm_val_vec_new = libiwasm.wasm_val_vec_new + _wasm_val_vec_new.restype = None + _wasm_val_vec_new.argtypes = [POINTER(wasm_val_vec_t),c_size_t,POINTER(wasm_val_t)] + return _wasm_val_vec_new(arg0,arg1,arg2) + +def wasm_val_vec_copy(arg0,arg1): + _wasm_val_vec_copy = libiwasm.wasm_val_vec_copy + _wasm_val_vec_copy.restype = None + _wasm_val_vec_copy.argtypes = [POINTER(wasm_val_vec_t),POINTER(wasm_val_vec_t)] + return _wasm_val_vec_copy(arg0,arg1) + +def wasm_val_vec_delete(arg0): + _wasm_val_vec_delete = libiwasm.wasm_val_vec_delete + _wasm_val_vec_delete.restype = None + _wasm_val_vec_delete.argtypes = [POINTER(wasm_val_vec_t)] + return _wasm_val_vec_delete(arg0) + +def wasm_ref_delete(arg0): + _wasm_ref_delete = libiwasm.wasm_ref_delete + _wasm_ref_delete.restype = None + _wasm_ref_delete.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_delete(arg0) + +def wasm_ref_copy(arg0): + _wasm_ref_copy = libiwasm.wasm_ref_copy + _wasm_ref_copy.restype = POINTER(wasm_ref_t) + _wasm_ref_copy.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_copy(arg0) + +def wasm_ref_same(arg0,arg1): + _wasm_ref_same = libiwasm.wasm_ref_same + _wasm_ref_same.restype = c_bool + _wasm_ref_same.argtypes = [POINTER(wasm_ref_t),POINTER(wasm_ref_t)] + return _wasm_ref_same(arg0,arg1) + +def wasm_ref_get_host_info(arg0): + _wasm_ref_get_host_info = libiwasm.wasm_ref_get_host_info + _wasm_ref_get_host_info.restype = c_void_p + _wasm_ref_get_host_info.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_get_host_info(arg0) + +def wasm_ref_set_host_info(arg0,arg1): + _wasm_ref_set_host_info = libiwasm.wasm_ref_set_host_info + _wasm_ref_set_host_info.restype = None + _wasm_ref_set_host_info.argtypes = [POINTER(wasm_ref_t),c_void_p] + return _wasm_ref_set_host_info(arg0,arg1) + +def wasm_ref_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_ref_set_host_info_with_finalizer = libiwasm.wasm_ref_set_host_info_with_finalizer + _wasm_ref_set_host_info_with_finalizer.restype = None + _wasm_ref_set_host_info_with_finalizer.argtypes = [POINTER(wasm_ref_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_ref_set_host_info_with_finalizer(arg0,arg1,arg2) + +class wasm_frame_t(Structure): + pass + +def wasm_frame_delete(arg0): + _wasm_frame_delete = libiwasm.wasm_frame_delete + _wasm_frame_delete.restype = None + _wasm_frame_delete.argtypes = [POINTER(wasm_frame_t)] + return _wasm_frame_delete(arg0) + +class wasm_frame_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_frame_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_frame_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_frame_vec_new_empty(arg0): + _wasm_frame_vec_new_empty = libiwasm.wasm_frame_vec_new_empty + _wasm_frame_vec_new_empty.restype = None + _wasm_frame_vec_new_empty.argtypes = [POINTER(wasm_frame_vec_t)] + return _wasm_frame_vec_new_empty(arg0) + +def wasm_frame_vec_new_uninitialized(arg0,arg1): + _wasm_frame_vec_new_uninitialized = libiwasm.wasm_frame_vec_new_uninitialized + _wasm_frame_vec_new_uninitialized.restype = None + _wasm_frame_vec_new_uninitialized.argtypes = [POINTER(wasm_frame_vec_t),c_size_t] + return _wasm_frame_vec_new_uninitialized(arg0,arg1) + +def wasm_frame_vec_new(arg0,arg1,arg2): + _wasm_frame_vec_new = libiwasm.wasm_frame_vec_new + _wasm_frame_vec_new.restype = None + _wasm_frame_vec_new.argtypes = [POINTER(wasm_frame_vec_t),c_size_t,POINTER(POINTER(wasm_frame_t))] + return _wasm_frame_vec_new(arg0,arg1,arg2) + +def wasm_frame_vec_copy(arg0,arg1): + _wasm_frame_vec_copy = libiwasm.wasm_frame_vec_copy + _wasm_frame_vec_copy.restype = None + _wasm_frame_vec_copy.argtypes = [POINTER(wasm_frame_vec_t),POINTER(wasm_frame_vec_t)] + return _wasm_frame_vec_copy(arg0,arg1) + +def wasm_frame_vec_delete(arg0): + _wasm_frame_vec_delete = libiwasm.wasm_frame_vec_delete + _wasm_frame_vec_delete.restype = None + _wasm_frame_vec_delete.argtypes = [POINTER(wasm_frame_vec_t)] + return _wasm_frame_vec_delete(arg0) + +def wasm_frame_copy(arg0): + _wasm_frame_copy = libiwasm.wasm_frame_copy + _wasm_frame_copy.restype = POINTER(wasm_frame_t) + _wasm_frame_copy.argtypes = [POINTER(wasm_frame_t)] + return _wasm_frame_copy(arg0) + +def wasm_frame_instance(arg0): + _wasm_frame_instance = libiwasm.wasm_frame_instance + _wasm_frame_instance.restype = POINTER(wasm_instance_t) + _wasm_frame_instance.argtypes = [POINTER(wasm_frame_t)] + return _wasm_frame_instance(arg0) + +def wasm_frame_func_index(arg0): + _wasm_frame_func_index = libiwasm.wasm_frame_func_index + _wasm_frame_func_index.restype = c_uint32 + _wasm_frame_func_index.argtypes = [POINTER(wasm_frame_t)] + return _wasm_frame_func_index(arg0) + +def wasm_frame_func_offset(arg0): + _wasm_frame_func_offset = libiwasm.wasm_frame_func_offset + _wasm_frame_func_offset.restype = c_size_t + _wasm_frame_func_offset.argtypes = [POINTER(wasm_frame_t)] + return _wasm_frame_func_offset(arg0) + +def wasm_frame_module_offset(arg0): + _wasm_frame_module_offset = libiwasm.wasm_frame_module_offset + _wasm_frame_module_offset.restype = c_size_t + _wasm_frame_module_offset.argtypes = [POINTER(wasm_frame_t)] + return _wasm_frame_module_offset(arg0) + +wasm_message_t = wasm_name_t + +class wasm_trap_t(Structure): + pass + +def wasm_trap_delete(arg0): + _wasm_trap_delete = libiwasm.wasm_trap_delete + _wasm_trap_delete.restype = None + _wasm_trap_delete.argtypes = [POINTER(wasm_trap_t)] + return _wasm_trap_delete(arg0) + +def wasm_trap_copy(arg0): + _wasm_trap_copy = libiwasm.wasm_trap_copy + _wasm_trap_copy.restype = POINTER(wasm_trap_t) + _wasm_trap_copy.argtypes = [POINTER(wasm_trap_t)] + return _wasm_trap_copy(arg0) + +def wasm_trap_same(arg0,arg1): + _wasm_trap_same = libiwasm.wasm_trap_same + _wasm_trap_same.restype = c_bool + _wasm_trap_same.argtypes = [POINTER(wasm_trap_t),POINTER(wasm_trap_t)] + return _wasm_trap_same(arg0,arg1) + +def wasm_trap_get_host_info(arg0): + _wasm_trap_get_host_info = libiwasm.wasm_trap_get_host_info + _wasm_trap_get_host_info.restype = c_void_p + _wasm_trap_get_host_info.argtypes = [POINTER(wasm_trap_t)] + return _wasm_trap_get_host_info(arg0) + +def wasm_trap_set_host_info(arg0,arg1): + _wasm_trap_set_host_info = libiwasm.wasm_trap_set_host_info + _wasm_trap_set_host_info.restype = None + _wasm_trap_set_host_info.argtypes = [POINTER(wasm_trap_t),c_void_p] + return _wasm_trap_set_host_info(arg0,arg1) + +def wasm_trap_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_trap_set_host_info_with_finalizer = libiwasm.wasm_trap_set_host_info_with_finalizer + _wasm_trap_set_host_info_with_finalizer.restype = None + _wasm_trap_set_host_info_with_finalizer.argtypes = [POINTER(wasm_trap_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_trap_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_trap_as_ref(arg0): + _wasm_trap_as_ref = libiwasm.wasm_trap_as_ref + _wasm_trap_as_ref.restype = POINTER(wasm_ref_t) + _wasm_trap_as_ref.argtypes = [POINTER(wasm_trap_t)] + return _wasm_trap_as_ref(arg0) + +def wasm_ref_as_trap(arg0): + _wasm_ref_as_trap = libiwasm.wasm_ref_as_trap + _wasm_ref_as_trap.restype = POINTER(wasm_trap_t) + _wasm_ref_as_trap.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_trap(arg0) + +def wasm_trap_as_ref_const(arg0): + _wasm_trap_as_ref_const = libiwasm.wasm_trap_as_ref_const + _wasm_trap_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_trap_as_ref_const.argtypes = [POINTER(wasm_trap_t)] + return _wasm_trap_as_ref_const(arg0) + +def wasm_ref_as_trap_const(arg0): + _wasm_ref_as_trap_const = libiwasm.wasm_ref_as_trap_const + _wasm_ref_as_trap_const.restype = POINTER(wasm_trap_t) + _wasm_ref_as_trap_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_trap_const(arg0) + +def wasm_trap_new(arg0,arg1): + _wasm_trap_new = libiwasm.wasm_trap_new + _wasm_trap_new.restype = POINTER(wasm_trap_t) + _wasm_trap_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_message_t)] + return _wasm_trap_new(arg0,arg1) + +def wasm_trap_message(arg0,arg1): + _wasm_trap_message = libiwasm.wasm_trap_message + _wasm_trap_message.restype = None + _wasm_trap_message.argtypes = [POINTER(wasm_trap_t),POINTER(wasm_message_t)] + return _wasm_trap_message(arg0,arg1) + +def wasm_trap_origin(arg0): + _wasm_trap_origin = libiwasm.wasm_trap_origin + _wasm_trap_origin.restype = POINTER(wasm_frame_t) + _wasm_trap_origin.argtypes = [POINTER(wasm_trap_t)] + return _wasm_trap_origin(arg0) + +def wasm_trap_trace(arg0,arg1): + _wasm_trap_trace = libiwasm.wasm_trap_trace + _wasm_trap_trace.restype = None + _wasm_trap_trace.argtypes = [POINTER(wasm_trap_t),POINTER(wasm_frame_vec_t)] + return _wasm_trap_trace(arg0,arg1) + +class wasm_foreign_t(Structure): + pass + +def wasm_foreign_delete(arg0): + _wasm_foreign_delete = libiwasm.wasm_foreign_delete + _wasm_foreign_delete.restype = None + _wasm_foreign_delete.argtypes = [POINTER(wasm_foreign_t)] + return _wasm_foreign_delete(arg0) + +def wasm_foreign_copy(arg0): + _wasm_foreign_copy = libiwasm.wasm_foreign_copy + _wasm_foreign_copy.restype = POINTER(wasm_foreign_t) + _wasm_foreign_copy.argtypes = [POINTER(wasm_foreign_t)] + return _wasm_foreign_copy(arg0) + +def wasm_foreign_same(arg0,arg1): + _wasm_foreign_same = libiwasm.wasm_foreign_same + _wasm_foreign_same.restype = c_bool + _wasm_foreign_same.argtypes = [POINTER(wasm_foreign_t),POINTER(wasm_foreign_t)] + return _wasm_foreign_same(arg0,arg1) + +def wasm_foreign_get_host_info(arg0): + _wasm_foreign_get_host_info = libiwasm.wasm_foreign_get_host_info + _wasm_foreign_get_host_info.restype = c_void_p + _wasm_foreign_get_host_info.argtypes = [POINTER(wasm_foreign_t)] + return _wasm_foreign_get_host_info(arg0) + +def wasm_foreign_set_host_info(arg0,arg1): + _wasm_foreign_set_host_info = libiwasm.wasm_foreign_set_host_info + _wasm_foreign_set_host_info.restype = None + _wasm_foreign_set_host_info.argtypes = [POINTER(wasm_foreign_t),c_void_p] + return _wasm_foreign_set_host_info(arg0,arg1) + +def wasm_foreign_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_foreign_set_host_info_with_finalizer = libiwasm.wasm_foreign_set_host_info_with_finalizer + _wasm_foreign_set_host_info_with_finalizer.restype = None + _wasm_foreign_set_host_info_with_finalizer.argtypes = [POINTER(wasm_foreign_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_foreign_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_foreign_as_ref(arg0): + _wasm_foreign_as_ref = libiwasm.wasm_foreign_as_ref + _wasm_foreign_as_ref.restype = POINTER(wasm_ref_t) + _wasm_foreign_as_ref.argtypes = [POINTER(wasm_foreign_t)] + return _wasm_foreign_as_ref(arg0) + +def wasm_ref_as_foreign(arg0): + _wasm_ref_as_foreign = libiwasm.wasm_ref_as_foreign + _wasm_ref_as_foreign.restype = POINTER(wasm_foreign_t) + _wasm_ref_as_foreign.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_foreign(arg0) + +def wasm_foreign_as_ref_const(arg0): + _wasm_foreign_as_ref_const = libiwasm.wasm_foreign_as_ref_const + _wasm_foreign_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_foreign_as_ref_const.argtypes = [POINTER(wasm_foreign_t)] + return _wasm_foreign_as_ref_const(arg0) + +def wasm_ref_as_foreign_const(arg0): + _wasm_ref_as_foreign_const = libiwasm.wasm_ref_as_foreign_const + _wasm_ref_as_foreign_const.restype = POINTER(wasm_foreign_t) + _wasm_ref_as_foreign_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_foreign_const(arg0) + +def wasm_foreign_new(arg0): + _wasm_foreign_new = libiwasm.wasm_foreign_new + _wasm_foreign_new.restype = POINTER(wasm_foreign_t) + _wasm_foreign_new.argtypes = [POINTER(wasm_store_t)] + return _wasm_foreign_new(arg0) + +class WASMModuleCommon(Structure): + pass + +class WASMModuleCommon(Structure): + pass + +wasm_module_t = POINTER(WASMModuleCommon) + +def wasm_module_new(arg0,arg1): + _wasm_module_new = libiwasm.wasm_module_new + _wasm_module_new.restype = POINTER(wasm_module_t) + _wasm_module_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_byte_vec_t)] + return _wasm_module_new(arg0,arg1) + +def wasm_module_delete(arg0): + _wasm_module_delete = libiwasm.wasm_module_delete + _wasm_module_delete.restype = None + _wasm_module_delete.argtypes = [POINTER(wasm_module_t)] + return _wasm_module_delete(arg0) + +def wasm_module_validate(arg0,arg1): + _wasm_module_validate = libiwasm.wasm_module_validate + _wasm_module_validate.restype = c_bool + _wasm_module_validate.argtypes = [POINTER(wasm_store_t),POINTER(wasm_byte_vec_t)] + return _wasm_module_validate(arg0,arg1) + +def wasm_module_imports(arg0,arg1): + _wasm_module_imports = libiwasm.wasm_module_imports + _wasm_module_imports.restype = None + _wasm_module_imports.argtypes = [POINTER(wasm_module_t),POINTER(wasm_importtype_vec_t)] + return _wasm_module_imports(arg0,arg1) + +def wasm_module_exports(arg0,arg1): + _wasm_module_exports = libiwasm.wasm_module_exports + _wasm_module_exports.restype = None + _wasm_module_exports.argtypes = [POINTER(wasm_module_t),POINTER(wasm_exporttype_vec_t)] + return _wasm_module_exports(arg0,arg1) + +def wasm_module_serialize(arg0,arg1): + _wasm_module_serialize = libiwasm.wasm_module_serialize + _wasm_module_serialize.restype = None + _wasm_module_serialize.argtypes = [POINTER(wasm_module_t),POINTER(wasm_byte_vec_t)] + return _wasm_module_serialize(arg0,arg1) + +def wasm_module_deserialize(arg0,arg1): + _wasm_module_deserialize = libiwasm.wasm_module_deserialize + _wasm_module_deserialize.restype = POINTER(wasm_module_t) + _wasm_module_deserialize.argtypes = [POINTER(wasm_store_t),POINTER(wasm_byte_vec_t)] + return _wasm_module_deserialize(arg0,arg1) + +class wasm_func_t(Structure): + pass + +def wasm_func_delete(arg0): + _wasm_func_delete = libiwasm.wasm_func_delete + _wasm_func_delete.restype = None + _wasm_func_delete.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_delete(arg0) + +def wasm_func_copy(arg0): + _wasm_func_copy = libiwasm.wasm_func_copy + _wasm_func_copy.restype = POINTER(wasm_func_t) + _wasm_func_copy.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_copy(arg0) + +def wasm_func_same(arg0,arg1): + _wasm_func_same = libiwasm.wasm_func_same + _wasm_func_same.restype = c_bool + _wasm_func_same.argtypes = [POINTER(wasm_func_t),POINTER(wasm_func_t)] + return _wasm_func_same(arg0,arg1) + +def wasm_func_get_host_info(arg0): + _wasm_func_get_host_info = libiwasm.wasm_func_get_host_info + _wasm_func_get_host_info.restype = c_void_p + _wasm_func_get_host_info.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_get_host_info(arg0) + +def wasm_func_set_host_info(arg0,arg1): + _wasm_func_set_host_info = libiwasm.wasm_func_set_host_info + _wasm_func_set_host_info.restype = None + _wasm_func_set_host_info.argtypes = [POINTER(wasm_func_t),c_void_p] + return _wasm_func_set_host_info(arg0,arg1) + +def wasm_func_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_func_set_host_info_with_finalizer = libiwasm.wasm_func_set_host_info_with_finalizer + _wasm_func_set_host_info_with_finalizer.restype = None + _wasm_func_set_host_info_with_finalizer.argtypes = [POINTER(wasm_func_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_func_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_func_as_ref(arg0): + _wasm_func_as_ref = libiwasm.wasm_func_as_ref + _wasm_func_as_ref.restype = POINTER(wasm_ref_t) + _wasm_func_as_ref.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_as_ref(arg0) + +def wasm_ref_as_func(arg0): + _wasm_ref_as_func = libiwasm.wasm_ref_as_func + _wasm_ref_as_func.restype = POINTER(wasm_func_t) + _wasm_ref_as_func.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_func(arg0) + +def wasm_func_as_ref_const(arg0): + _wasm_func_as_ref_const = libiwasm.wasm_func_as_ref_const + _wasm_func_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_func_as_ref_const.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_as_ref_const(arg0) + +def wasm_ref_as_func_const(arg0): + _wasm_ref_as_func_const = libiwasm.wasm_ref_as_func_const + _wasm_ref_as_func_const.restype = POINTER(wasm_func_t) + _wasm_ref_as_func_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_func_const(arg0) + +wasm_func_callback_t = CFUNCTYPE(c_void_p,POINTER(wasm_val_vec_t),POINTER(wasm_val_vec_t)) + +wasm_func_callback_with_env_t = CFUNCTYPE(c_void_p,c_void_p,POINTER(wasm_val_vec_t),POINTER(wasm_val_vec_t)) + +def wasm_func_new(arg0,arg1,arg2): + _wasm_func_new = libiwasm.wasm_func_new + _wasm_func_new.restype = POINTER(wasm_func_t) + _wasm_func_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_functype_t),wasm_func_callback_t] + return _wasm_func_new(arg0,arg1,arg2) + +def wasm_func_new_with_env(arg0,arg1,arg2,arg3,arg4): + _wasm_func_new_with_env = libiwasm.wasm_func_new_with_env + _wasm_func_new_with_env.restype = POINTER(wasm_func_t) + _wasm_func_new_with_env.argtypes = [POINTER(wasm_store_t),POINTER(wasm_functype_t),wasm_func_callback_with_env_t,c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_func_new_with_env(arg0,arg1,arg2,arg3,arg4) + +def wasm_func_type(arg0): + _wasm_func_type = libiwasm.wasm_func_type + _wasm_func_type.restype = POINTER(wasm_functype_t) + _wasm_func_type.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_type(arg0) + +def wasm_func_param_arity(arg0): + _wasm_func_param_arity = libiwasm.wasm_func_param_arity + _wasm_func_param_arity.restype = c_size_t + _wasm_func_param_arity.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_param_arity(arg0) + +def wasm_func_result_arity(arg0): + _wasm_func_result_arity = libiwasm.wasm_func_result_arity + _wasm_func_result_arity.restype = c_size_t + _wasm_func_result_arity.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_result_arity(arg0) + +def wasm_func_call(arg0,arg1,arg2): + _wasm_func_call = libiwasm.wasm_func_call + _wasm_func_call.restype = POINTER(wasm_trap_t) + _wasm_func_call.argtypes = [POINTER(wasm_func_t),POINTER(wasm_val_vec_t),POINTER(wasm_val_vec_t)] + return _wasm_func_call(arg0,arg1,arg2) + +class wasm_global_t(Structure): + pass + +def wasm_global_delete(arg0): + _wasm_global_delete = libiwasm.wasm_global_delete + _wasm_global_delete.restype = None + _wasm_global_delete.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_delete(arg0) + +def wasm_global_copy(arg0): + _wasm_global_copy = libiwasm.wasm_global_copy + _wasm_global_copy.restype = POINTER(wasm_global_t) + _wasm_global_copy.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_copy(arg0) + +def wasm_global_same(arg0,arg1): + _wasm_global_same = libiwasm.wasm_global_same + _wasm_global_same.restype = c_bool + _wasm_global_same.argtypes = [POINTER(wasm_global_t),POINTER(wasm_global_t)] + return _wasm_global_same(arg0,arg1) + +def wasm_global_get_host_info(arg0): + _wasm_global_get_host_info = libiwasm.wasm_global_get_host_info + _wasm_global_get_host_info.restype = c_void_p + _wasm_global_get_host_info.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_get_host_info(arg0) + +def wasm_global_set_host_info(arg0,arg1): + _wasm_global_set_host_info = libiwasm.wasm_global_set_host_info + _wasm_global_set_host_info.restype = None + _wasm_global_set_host_info.argtypes = [POINTER(wasm_global_t),c_void_p] + return _wasm_global_set_host_info(arg0,arg1) + +def wasm_global_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_global_set_host_info_with_finalizer = libiwasm.wasm_global_set_host_info_with_finalizer + _wasm_global_set_host_info_with_finalizer.restype = None + _wasm_global_set_host_info_with_finalizer.argtypes = [POINTER(wasm_global_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_global_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_global_as_ref(arg0): + _wasm_global_as_ref = libiwasm.wasm_global_as_ref + _wasm_global_as_ref.restype = POINTER(wasm_ref_t) + _wasm_global_as_ref.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_as_ref(arg0) + +def wasm_ref_as_global(arg0): + _wasm_ref_as_global = libiwasm.wasm_ref_as_global + _wasm_ref_as_global.restype = POINTER(wasm_global_t) + _wasm_ref_as_global.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_global(arg0) + +def wasm_global_as_ref_const(arg0): + _wasm_global_as_ref_const = libiwasm.wasm_global_as_ref_const + _wasm_global_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_global_as_ref_const.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_as_ref_const(arg0) + +def wasm_ref_as_global_const(arg0): + _wasm_ref_as_global_const = libiwasm.wasm_ref_as_global_const + _wasm_ref_as_global_const.restype = POINTER(wasm_global_t) + _wasm_ref_as_global_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_global_const(arg0) + +def wasm_global_new(arg0,arg1,arg2): + _wasm_global_new = libiwasm.wasm_global_new + _wasm_global_new.restype = POINTER(wasm_global_t) + _wasm_global_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_globaltype_t),POINTER(wasm_val_t)] + return _wasm_global_new(arg0,arg1,arg2) + +def wasm_global_type(arg0): + _wasm_global_type = libiwasm.wasm_global_type + _wasm_global_type.restype = POINTER(wasm_globaltype_t) + _wasm_global_type.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_type(arg0) + +def wasm_global_get(arg0,arg1): + _wasm_global_get = libiwasm.wasm_global_get + _wasm_global_get.restype = None + _wasm_global_get.argtypes = [POINTER(wasm_global_t),POINTER(wasm_val_t)] + return _wasm_global_get(arg0,arg1) + +def wasm_global_set(arg0,arg1): + _wasm_global_set = libiwasm.wasm_global_set + _wasm_global_set.restype = None + _wasm_global_set.argtypes = [POINTER(wasm_global_t),POINTER(wasm_val_t)] + return _wasm_global_set(arg0,arg1) + +class wasm_table_t(Structure): + pass + +def wasm_table_delete(arg0): + _wasm_table_delete = libiwasm.wasm_table_delete + _wasm_table_delete.restype = None + _wasm_table_delete.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_delete(arg0) + +def wasm_table_copy(arg0): + _wasm_table_copy = libiwasm.wasm_table_copy + _wasm_table_copy.restype = POINTER(wasm_table_t) + _wasm_table_copy.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_copy(arg0) + +def wasm_table_same(arg0,arg1): + _wasm_table_same = libiwasm.wasm_table_same + _wasm_table_same.restype = c_bool + _wasm_table_same.argtypes = [POINTER(wasm_table_t),POINTER(wasm_table_t)] + return _wasm_table_same(arg0,arg1) + +def wasm_table_get_host_info(arg0): + _wasm_table_get_host_info = libiwasm.wasm_table_get_host_info + _wasm_table_get_host_info.restype = c_void_p + _wasm_table_get_host_info.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_get_host_info(arg0) + +def wasm_table_set_host_info(arg0,arg1): + _wasm_table_set_host_info = libiwasm.wasm_table_set_host_info + _wasm_table_set_host_info.restype = None + _wasm_table_set_host_info.argtypes = [POINTER(wasm_table_t),c_void_p] + return _wasm_table_set_host_info(arg0,arg1) + +def wasm_table_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_table_set_host_info_with_finalizer = libiwasm.wasm_table_set_host_info_with_finalizer + _wasm_table_set_host_info_with_finalizer.restype = None + _wasm_table_set_host_info_with_finalizer.argtypes = [POINTER(wasm_table_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_table_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_table_as_ref(arg0): + _wasm_table_as_ref = libiwasm.wasm_table_as_ref + _wasm_table_as_ref.restype = POINTER(wasm_ref_t) + _wasm_table_as_ref.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_as_ref(arg0) + +def wasm_ref_as_table(arg0): + _wasm_ref_as_table = libiwasm.wasm_ref_as_table + _wasm_ref_as_table.restype = POINTER(wasm_table_t) + _wasm_ref_as_table.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_table(arg0) + +def wasm_table_as_ref_const(arg0): + _wasm_table_as_ref_const = libiwasm.wasm_table_as_ref_const + _wasm_table_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_table_as_ref_const.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_as_ref_const(arg0) + +def wasm_ref_as_table_const(arg0): + _wasm_ref_as_table_const = libiwasm.wasm_ref_as_table_const + _wasm_ref_as_table_const.restype = POINTER(wasm_table_t) + _wasm_ref_as_table_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_table_const(arg0) + +wasm_table_size_t = c_uint32 + +def wasm_table_new(arg0,arg1,arg2): + _wasm_table_new = libiwasm.wasm_table_new + _wasm_table_new.restype = POINTER(wasm_table_t) + _wasm_table_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_tabletype_t),POINTER(wasm_ref_t)] + return _wasm_table_new(arg0,arg1,arg2) + +def wasm_table_type(arg0): + _wasm_table_type = libiwasm.wasm_table_type + _wasm_table_type.restype = POINTER(wasm_tabletype_t) + _wasm_table_type.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_type(arg0) + +def wasm_table_get(arg0,arg1): + _wasm_table_get = libiwasm.wasm_table_get + _wasm_table_get.restype = POINTER(wasm_ref_t) + _wasm_table_get.argtypes = [POINTER(wasm_table_t),wasm_table_size_t] + return _wasm_table_get(arg0,arg1) + +def wasm_table_set(arg0,arg1,arg2): + _wasm_table_set = libiwasm.wasm_table_set + _wasm_table_set.restype = c_bool + _wasm_table_set.argtypes = [POINTER(wasm_table_t),wasm_table_size_t,POINTER(wasm_ref_t)] + return _wasm_table_set(arg0,arg1,arg2) + +def wasm_table_size(arg0): + _wasm_table_size = libiwasm.wasm_table_size + _wasm_table_size.restype = wasm_table_size_t + _wasm_table_size.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_size(arg0) + +def wasm_table_grow(arg0,arg1,arg2): + _wasm_table_grow = libiwasm.wasm_table_grow + _wasm_table_grow.restype = c_bool + _wasm_table_grow.argtypes = [POINTER(wasm_table_t),wasm_table_size_t,POINTER(wasm_ref_t)] + return _wasm_table_grow(arg0,arg1,arg2) + +class wasm_memory_t(Structure): + pass + +def wasm_memory_delete(arg0): + _wasm_memory_delete = libiwasm.wasm_memory_delete + _wasm_memory_delete.restype = None + _wasm_memory_delete.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_delete(arg0) + +def wasm_memory_copy(arg0): + _wasm_memory_copy = libiwasm.wasm_memory_copy + _wasm_memory_copy.restype = POINTER(wasm_memory_t) + _wasm_memory_copy.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_copy(arg0) + +def wasm_memory_same(arg0,arg1): + _wasm_memory_same = libiwasm.wasm_memory_same + _wasm_memory_same.restype = c_bool + _wasm_memory_same.argtypes = [POINTER(wasm_memory_t),POINTER(wasm_memory_t)] + return _wasm_memory_same(arg0,arg1) + +def wasm_memory_get_host_info(arg0): + _wasm_memory_get_host_info = libiwasm.wasm_memory_get_host_info + _wasm_memory_get_host_info.restype = c_void_p + _wasm_memory_get_host_info.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_get_host_info(arg0) + +def wasm_memory_set_host_info(arg0,arg1): + _wasm_memory_set_host_info = libiwasm.wasm_memory_set_host_info + _wasm_memory_set_host_info.restype = None + _wasm_memory_set_host_info.argtypes = [POINTER(wasm_memory_t),c_void_p] + return _wasm_memory_set_host_info(arg0,arg1) + +def wasm_memory_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_memory_set_host_info_with_finalizer = libiwasm.wasm_memory_set_host_info_with_finalizer + _wasm_memory_set_host_info_with_finalizer.restype = None + _wasm_memory_set_host_info_with_finalizer.argtypes = [POINTER(wasm_memory_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_memory_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_memory_as_ref(arg0): + _wasm_memory_as_ref = libiwasm.wasm_memory_as_ref + _wasm_memory_as_ref.restype = POINTER(wasm_ref_t) + _wasm_memory_as_ref.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_as_ref(arg0) + +def wasm_ref_as_memory(arg0): + _wasm_ref_as_memory = libiwasm.wasm_ref_as_memory + _wasm_ref_as_memory.restype = POINTER(wasm_memory_t) + _wasm_ref_as_memory.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_memory(arg0) + +def wasm_memory_as_ref_const(arg0): + _wasm_memory_as_ref_const = libiwasm.wasm_memory_as_ref_const + _wasm_memory_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_memory_as_ref_const.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_as_ref_const(arg0) + +def wasm_ref_as_memory_const(arg0): + _wasm_ref_as_memory_const = libiwasm.wasm_ref_as_memory_const + _wasm_ref_as_memory_const.restype = POINTER(wasm_memory_t) + _wasm_ref_as_memory_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_memory_const(arg0) + +wasm_memory_pages_t = c_uint32 + +def wasm_memory_new(arg0,arg1): + _wasm_memory_new = libiwasm.wasm_memory_new + _wasm_memory_new.restype = POINTER(wasm_memory_t) + _wasm_memory_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_memorytype_t)] + return _wasm_memory_new(arg0,arg1) + +def wasm_memory_type(arg0): + _wasm_memory_type = libiwasm.wasm_memory_type + _wasm_memory_type.restype = POINTER(wasm_memorytype_t) + _wasm_memory_type.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_type(arg0) + +def wasm_memory_data(arg0): + _wasm_memory_data = libiwasm.wasm_memory_data + _wasm_memory_data.restype = POINTER(c_ubyte) + _wasm_memory_data.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_data(arg0) + +def wasm_memory_data_size(arg0): + _wasm_memory_data_size = libiwasm.wasm_memory_data_size + _wasm_memory_data_size.restype = c_size_t + _wasm_memory_data_size.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_data_size(arg0) + +def wasm_memory_size(arg0): + _wasm_memory_size = libiwasm.wasm_memory_size + _wasm_memory_size.restype = wasm_memory_pages_t + _wasm_memory_size.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_size(arg0) + +def wasm_memory_grow(arg0,arg1): + _wasm_memory_grow = libiwasm.wasm_memory_grow + _wasm_memory_grow.restype = c_bool + _wasm_memory_grow.argtypes = [POINTER(wasm_memory_t),wasm_memory_pages_t] + return _wasm_memory_grow(arg0,arg1) + +class wasm_extern_t(Structure): + pass + +def wasm_extern_delete(arg0): + _wasm_extern_delete = libiwasm.wasm_extern_delete + _wasm_extern_delete.restype = None + _wasm_extern_delete.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_delete(arg0) + +def wasm_extern_copy(arg0): + _wasm_extern_copy = libiwasm.wasm_extern_copy + _wasm_extern_copy.restype = POINTER(wasm_extern_t) + _wasm_extern_copy.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_copy(arg0) + +def wasm_extern_same(arg0,arg1): + _wasm_extern_same = libiwasm.wasm_extern_same + _wasm_extern_same.restype = c_bool + _wasm_extern_same.argtypes = [POINTER(wasm_extern_t),POINTER(wasm_extern_t)] + return _wasm_extern_same(arg0,arg1) + +def wasm_extern_get_host_info(arg0): + _wasm_extern_get_host_info = libiwasm.wasm_extern_get_host_info + _wasm_extern_get_host_info.restype = c_void_p + _wasm_extern_get_host_info.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_get_host_info(arg0) + +def wasm_extern_set_host_info(arg0,arg1): + _wasm_extern_set_host_info = libiwasm.wasm_extern_set_host_info + _wasm_extern_set_host_info.restype = None + _wasm_extern_set_host_info.argtypes = [POINTER(wasm_extern_t),c_void_p] + return _wasm_extern_set_host_info(arg0,arg1) + +def wasm_extern_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_extern_set_host_info_with_finalizer = libiwasm.wasm_extern_set_host_info_with_finalizer + _wasm_extern_set_host_info_with_finalizer.restype = None + _wasm_extern_set_host_info_with_finalizer.argtypes = [POINTER(wasm_extern_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_extern_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_extern_as_ref(arg0): + _wasm_extern_as_ref = libiwasm.wasm_extern_as_ref + _wasm_extern_as_ref.restype = POINTER(wasm_ref_t) + _wasm_extern_as_ref.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_ref(arg0) + +def wasm_ref_as_extern(arg0): + _wasm_ref_as_extern = libiwasm.wasm_ref_as_extern + _wasm_ref_as_extern.restype = POINTER(wasm_extern_t) + _wasm_ref_as_extern.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_extern(arg0) + +def wasm_extern_as_ref_const(arg0): + _wasm_extern_as_ref_const = libiwasm.wasm_extern_as_ref_const + _wasm_extern_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_extern_as_ref_const.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_ref_const(arg0) + +def wasm_ref_as_extern_const(arg0): + _wasm_ref_as_extern_const = libiwasm.wasm_ref_as_extern_const + _wasm_ref_as_extern_const.restype = POINTER(wasm_extern_t) + _wasm_ref_as_extern_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_extern_const(arg0) + +class wasm_extern_vec_t(Structure): + _fields_ = [ + ("size", c_size_t), + ("data", POINTER(POINTER(wasm_extern_t))), + ("num_elems", c_size_t), + ("size_of_elem", c_size_t), + ("lock", c_void_p), + ] + + def __eq__(self, other): + if not isinstance(other, wasm_extern_vec_t): + return False + return self.size == other.size and self.num_elems == other.num_elems and self.size_of_elem == other.size_of_elem + + def __repr__(self): + ret = "" + for i in range(self.num_elems): + ret += str(dereference(self.data[i])) + ret += " " + return ret + + + +def wasm_extern_vec_new_empty(arg0): + _wasm_extern_vec_new_empty = libiwasm.wasm_extern_vec_new_empty + _wasm_extern_vec_new_empty.restype = None + _wasm_extern_vec_new_empty.argtypes = [POINTER(wasm_extern_vec_t)] + return _wasm_extern_vec_new_empty(arg0) + +def wasm_extern_vec_new_uninitialized(arg0,arg1): + _wasm_extern_vec_new_uninitialized = libiwasm.wasm_extern_vec_new_uninitialized + _wasm_extern_vec_new_uninitialized.restype = None + _wasm_extern_vec_new_uninitialized.argtypes = [POINTER(wasm_extern_vec_t),c_size_t] + return _wasm_extern_vec_new_uninitialized(arg0,arg1) + +def wasm_extern_vec_new(arg0,arg1,arg2): + _wasm_extern_vec_new = libiwasm.wasm_extern_vec_new + _wasm_extern_vec_new.restype = None + _wasm_extern_vec_new.argtypes = [POINTER(wasm_extern_vec_t),c_size_t,POINTER(POINTER(wasm_extern_t))] + return _wasm_extern_vec_new(arg0,arg1,arg2) + +def wasm_extern_vec_copy(arg0,arg1): + _wasm_extern_vec_copy = libiwasm.wasm_extern_vec_copy + _wasm_extern_vec_copy.restype = None + _wasm_extern_vec_copy.argtypes = [POINTER(wasm_extern_vec_t),POINTER(wasm_extern_vec_t)] + return _wasm_extern_vec_copy(arg0,arg1) + +def wasm_extern_vec_delete(arg0): + _wasm_extern_vec_delete = libiwasm.wasm_extern_vec_delete + _wasm_extern_vec_delete.restype = None + _wasm_extern_vec_delete.argtypes = [POINTER(wasm_extern_vec_t)] + return _wasm_extern_vec_delete(arg0) + +def wasm_extern_kind(arg0): + _wasm_extern_kind = libiwasm.wasm_extern_kind + _wasm_extern_kind.restype = wasm_externkind_t + _wasm_extern_kind.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_kind(arg0) + +def wasm_extern_type(arg0): + _wasm_extern_type = libiwasm.wasm_extern_type + _wasm_extern_type.restype = POINTER(wasm_externtype_t) + _wasm_extern_type.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_type(arg0) + +def wasm_func_as_extern(arg0): + _wasm_func_as_extern = libiwasm.wasm_func_as_extern + _wasm_func_as_extern.restype = POINTER(wasm_extern_t) + _wasm_func_as_extern.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_as_extern(arg0) + +def wasm_global_as_extern(arg0): + _wasm_global_as_extern = libiwasm.wasm_global_as_extern + _wasm_global_as_extern.restype = POINTER(wasm_extern_t) + _wasm_global_as_extern.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_as_extern(arg0) + +def wasm_table_as_extern(arg0): + _wasm_table_as_extern = libiwasm.wasm_table_as_extern + _wasm_table_as_extern.restype = POINTER(wasm_extern_t) + _wasm_table_as_extern.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_as_extern(arg0) + +def wasm_memory_as_extern(arg0): + _wasm_memory_as_extern = libiwasm.wasm_memory_as_extern + _wasm_memory_as_extern.restype = POINTER(wasm_extern_t) + _wasm_memory_as_extern.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_as_extern(arg0) + +def wasm_extern_as_func(arg0): + _wasm_extern_as_func = libiwasm.wasm_extern_as_func + _wasm_extern_as_func.restype = POINTER(wasm_func_t) + _wasm_extern_as_func.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_func(arg0) + +def wasm_extern_as_global(arg0): + _wasm_extern_as_global = libiwasm.wasm_extern_as_global + _wasm_extern_as_global.restype = POINTER(wasm_global_t) + _wasm_extern_as_global.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_global(arg0) + +def wasm_extern_as_table(arg0): + _wasm_extern_as_table = libiwasm.wasm_extern_as_table + _wasm_extern_as_table.restype = POINTER(wasm_table_t) + _wasm_extern_as_table.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_table(arg0) + +def wasm_extern_as_memory(arg0): + _wasm_extern_as_memory = libiwasm.wasm_extern_as_memory + _wasm_extern_as_memory.restype = POINTER(wasm_memory_t) + _wasm_extern_as_memory.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_memory(arg0) + +def wasm_func_as_extern_const(arg0): + _wasm_func_as_extern_const = libiwasm.wasm_func_as_extern_const + _wasm_func_as_extern_const.restype = POINTER(wasm_extern_t) + _wasm_func_as_extern_const.argtypes = [POINTER(wasm_func_t)] + return _wasm_func_as_extern_const(arg0) + +def wasm_global_as_extern_const(arg0): + _wasm_global_as_extern_const = libiwasm.wasm_global_as_extern_const + _wasm_global_as_extern_const.restype = POINTER(wasm_extern_t) + _wasm_global_as_extern_const.argtypes = [POINTER(wasm_global_t)] + return _wasm_global_as_extern_const(arg0) + +def wasm_table_as_extern_const(arg0): + _wasm_table_as_extern_const = libiwasm.wasm_table_as_extern_const + _wasm_table_as_extern_const.restype = POINTER(wasm_extern_t) + _wasm_table_as_extern_const.argtypes = [POINTER(wasm_table_t)] + return _wasm_table_as_extern_const(arg0) + +def wasm_memory_as_extern_const(arg0): + _wasm_memory_as_extern_const = libiwasm.wasm_memory_as_extern_const + _wasm_memory_as_extern_const.restype = POINTER(wasm_extern_t) + _wasm_memory_as_extern_const.argtypes = [POINTER(wasm_memory_t)] + return _wasm_memory_as_extern_const(arg0) + +def wasm_extern_as_func_const(arg0): + _wasm_extern_as_func_const = libiwasm.wasm_extern_as_func_const + _wasm_extern_as_func_const.restype = POINTER(wasm_func_t) + _wasm_extern_as_func_const.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_func_const(arg0) + +def wasm_extern_as_global_const(arg0): + _wasm_extern_as_global_const = libiwasm.wasm_extern_as_global_const + _wasm_extern_as_global_const.restype = POINTER(wasm_global_t) + _wasm_extern_as_global_const.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_global_const(arg0) + +def wasm_extern_as_table_const(arg0): + _wasm_extern_as_table_const = libiwasm.wasm_extern_as_table_const + _wasm_extern_as_table_const.restype = POINTER(wasm_table_t) + _wasm_extern_as_table_const.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_table_const(arg0) + +def wasm_extern_as_memory_const(arg0): + _wasm_extern_as_memory_const = libiwasm.wasm_extern_as_memory_const + _wasm_extern_as_memory_const.restype = POINTER(wasm_memory_t) + _wasm_extern_as_memory_const.argtypes = [POINTER(wasm_extern_t)] + return _wasm_extern_as_memory_const(arg0) + +class wasm_instance_t(Structure): + pass + +def wasm_instance_delete(arg0): + _wasm_instance_delete = libiwasm.wasm_instance_delete + _wasm_instance_delete.restype = None + _wasm_instance_delete.argtypes = [POINTER(wasm_instance_t)] + return _wasm_instance_delete(arg0) + +def wasm_instance_copy(arg0): + _wasm_instance_copy = libiwasm.wasm_instance_copy + _wasm_instance_copy.restype = POINTER(wasm_instance_t) + _wasm_instance_copy.argtypes = [POINTER(wasm_instance_t)] + return _wasm_instance_copy(arg0) + +def wasm_instance_same(arg0,arg1): + _wasm_instance_same = libiwasm.wasm_instance_same + _wasm_instance_same.restype = c_bool + _wasm_instance_same.argtypes = [POINTER(wasm_instance_t),POINTER(wasm_instance_t)] + return _wasm_instance_same(arg0,arg1) + +def wasm_instance_get_host_info(arg0): + _wasm_instance_get_host_info = libiwasm.wasm_instance_get_host_info + _wasm_instance_get_host_info.restype = c_void_p + _wasm_instance_get_host_info.argtypes = [POINTER(wasm_instance_t)] + return _wasm_instance_get_host_info(arg0) + +def wasm_instance_set_host_info(arg0,arg1): + _wasm_instance_set_host_info = libiwasm.wasm_instance_set_host_info + _wasm_instance_set_host_info.restype = None + _wasm_instance_set_host_info.argtypes = [POINTER(wasm_instance_t),c_void_p] + return _wasm_instance_set_host_info(arg0,arg1) + +def wasm_instance_set_host_info_with_finalizer(arg0,arg1,arg2): + _wasm_instance_set_host_info_with_finalizer = libiwasm.wasm_instance_set_host_info_with_finalizer + _wasm_instance_set_host_info_with_finalizer.restype = None + _wasm_instance_set_host_info_with_finalizer.argtypes = [POINTER(wasm_instance_t),c_void_p,CFUNCTYPE(None,c_void_p)] + return _wasm_instance_set_host_info_with_finalizer(arg0,arg1,arg2) + +def wasm_instance_as_ref(arg0): + _wasm_instance_as_ref = libiwasm.wasm_instance_as_ref + _wasm_instance_as_ref.restype = POINTER(wasm_ref_t) + _wasm_instance_as_ref.argtypes = [POINTER(wasm_instance_t)] + return _wasm_instance_as_ref(arg0) + +def wasm_ref_as_instance(arg0): + _wasm_ref_as_instance = libiwasm.wasm_ref_as_instance + _wasm_ref_as_instance.restype = POINTER(wasm_instance_t) + _wasm_ref_as_instance.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_instance(arg0) + +def wasm_instance_as_ref_const(arg0): + _wasm_instance_as_ref_const = libiwasm.wasm_instance_as_ref_const + _wasm_instance_as_ref_const.restype = POINTER(wasm_ref_t) + _wasm_instance_as_ref_const.argtypes = [POINTER(wasm_instance_t)] + return _wasm_instance_as_ref_const(arg0) + +def wasm_ref_as_instance_const(arg0): + _wasm_ref_as_instance_const = libiwasm.wasm_ref_as_instance_const + _wasm_ref_as_instance_const.restype = POINTER(wasm_instance_t) + _wasm_ref_as_instance_const.argtypes = [POINTER(wasm_ref_t)] + return _wasm_ref_as_instance_const(arg0) + +def wasm_instance_new(arg0,arg1,arg2,arg3): + _wasm_instance_new = libiwasm.wasm_instance_new + _wasm_instance_new.restype = POINTER(wasm_instance_t) + _wasm_instance_new.argtypes = [POINTER(wasm_store_t),POINTER(wasm_module_t),POINTER(wasm_extern_vec_t),POINTER(POINTER(wasm_trap_t))] + return _wasm_instance_new(arg0,arg1,arg2,arg3) + +def wasm_instance_new_with_args(arg0,arg1,arg2,arg3,arg4,arg5): + _wasm_instance_new_with_args = libiwasm.wasm_instance_new_with_args + _wasm_instance_new_with_args.restype = POINTER(wasm_instance_t) + _wasm_instance_new_with_args.argtypes = [POINTER(wasm_store_t),POINTER(wasm_module_t),POINTER(wasm_extern_vec_t),POINTER(POINTER(wasm_trap_t)),c_uint32,c_uint32] + return _wasm_instance_new_with_args(arg0,arg1,arg2,arg3,arg4,arg5) + +class InstantiationArgs(Structure): + pass + +def wasm_instance_new_with_args_ex(arg0,arg1,arg2,arg3,arg4): + _wasm_instance_new_with_args_ex = libiwasm.wasm_instance_new_with_args_ex + _wasm_instance_new_with_args_ex.restype = POINTER(wasm_instance_t) + _wasm_instance_new_with_args_ex.argtypes = [POINTER(wasm_store_t),POINTER(wasm_module_t),POINTER(wasm_extern_vec_t),POINTER(POINTER(wasm_trap_t)),POINTER(InstantiationArgs)] + return _wasm_instance_new_with_args_ex(arg0,arg1,arg2,arg3,arg4) + +def wasm_instance_exports(arg0,arg1): + _wasm_instance_exports = libiwasm.wasm_instance_exports + _wasm_instance_exports.restype = None + _wasm_instance_exports.argtypes = [POINTER(wasm_instance_t),POINTER(wasm_extern_vec_t)] + return _wasm_instance_exports(arg0,arg1) diff --git a/language-bindings/python/src/wamr/wasmcapi/ffi.py b/language-bindings/python/src/wamr/wasmcapi/ffi.py new file mode 100644 index 0000000000..e8a084c143 --- /dev/null +++ b/language-bindings/python/src/wamr/wasmcapi/ffi.py @@ -0,0 +1,642 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# pylint: disable=missing-class-docstring +# pylint: disable=missing-function-docstring +# pylint: disable=missing-module-docstring + +import ctypes as c +import os +from pathlib import Path +import sys + +# +# Prologue. Dependencies of binding +# + +# how to open the library file of WAMR + +if sys.platform == "linux": + BUILDING_DIR = "product-mini/platforms/linux/build" + LIBRARY_NAME = "libiwasm.so" +elif sys.platform == "win32": + BUILDING_DIR = "product-mini/platforms/windows/build" + LIBRARY_NAME = "iwasm.dll" +elif sys.platform == "darwin": + BUILDING_DIR = "product-mini/platforms/darwin/build" + LIBRARY_NAME = "libiwasm.dylib" +else: + raise RuntimeError(f"unsupported platform `{sys.platform}`") + +# FIXME: should load libiwasm.so from current system library path +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.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}") + +libpath = wamr_dir.joinpath(BUILDING_DIR).joinpath(LIBRARY_NAME).resolve() +if not libpath.exists(): + raise RuntimeError(f"not found precompiled wamr library at {libpath}") + +print(f"loading WAMR library from {libpath} ...") +libiwasm = c.cdll.LoadLibrary(libpath) + + +class wasm_ref_t(c.Structure): + # pylint: disable=invalid-name + pass + + +class wasm_val_union(c.Union): + # pylint: disable=invalid-name + _fields_ = [ + ("i32", c.c_int32), + ("i64", c.c_int64), + ("f32", c.c_float), + ("f64", c.c_double), + ("ref", c.POINTER(wasm_ref_t)), + ] + + +class wasm_val_t(c.Structure): + # pylint: disable=invalid-name + _fields_ = [ + ("kind", c.c_uint8), + ("of", wasm_val_union), + ] + + +def dereference(p): + # pylint: disable=protected-access + if not isinstance(p, c._Pointer): + raise RuntimeError("not a pointer") + return p.contents + + +# HELPERs +def create_null_pointer(struct_type): + return c.POINTER(struct_type)() + + +def is_null_pointer(c_pointer): + # pylint: disable=protected-access + if isinstance(c_pointer, c._Pointer): + return False if c_pointer else True + else: + raise RuntimeError("not a pointer") + + +def wasm_vec_to_list(vec): + """ + Converts a vector or a POINTER(vector) to a list + vector of type pointers -> list of type pointers + """ + known_vec_type = [ + wasm_byte_vec_t, + wasm_valtype_vec_t, + wasm_functype_vec_t, + wasm_globaltype_vec_t, + wasm_tabletype_vec_t, + wasm_memorytype_vec_t, + wasm_externtype_vec_t, + wasm_importtype_vec_t, + wasm_exporttype_vec_t, + wasm_val_vec_t, + wasm_frame_vec_t, + wasm_extern_vec_t, + ] + known_vec_pointer_type = [POINTER(vec_type) for vec_type in known_vec_type] + + if any([isinstance(vec, pointer_type) for pointer_type in known_vec_pointer_type]): + vec = dereference(vec) + return [vec.data[i] for i in range(vec.num_elems)] + elif any([isinstance(vec, vec_type) for vec_type in known_vec_type]): + return [vec.data[i] for i in range(vec.num_elems)] + else: + raise RuntimeError("not a known vector type") + + +def list_to_carray(elem_type, *args): + """ + Converts a python list into a C array + """ + data = (elem_type * len(args))(*args) + return data + + +def load_module_file(wasm_content): + binary = wasm_byte_vec_t() + wasm_byte_vec_new_uninitialized(binary, len(wasm_content)) + # has to use malloced memory. + c.memmove(binary.data, wasm_content, len(wasm_content)) + binary.num_elems = len(wasm_content) + return binary + + +# +# Enhancment of binding +# + +from .binding import * + +# Built-in functions for Structure + + +wasm_finalizer = CFUNCTYPE(None, c_void_p) + + +def __repr_wasm_limits_t(self): + return f"{self.min:#x} {self.max:#x}" + + +# overwrite +wasm_limits_t.__repr__ = __repr_wasm_limits_t + + +def __compare_wasm_valtype_t(self, other): + if not isinstance(other, wasm_valtype_t): + return False + + return wasm_valtype_kind(byref(self)) == wasm_valtype_kind(byref(other)) + + +def __repr_wasm_valtype_t(self): + val_kind = wasm_valtype_kind(byref(self)) + if WASM_I32 == val_kind: + return "i32" + elif WASM_I64 == val_kind: + return "i64" + elif WASM_F32 == val_kind: + return "f32" + elif WASM_F64 == val_kind: + return "f64" + elif WASM_FUNCREF == val_kind: + return "funcref" + else: + return "externref" + + +wasm_valtype_t.__eq__ = __compare_wasm_valtype_t +wasm_valtype_t.__repr__ = __repr_wasm_valtype_t + + +def __compare_wasm_byte_vec_t(self, other): + if not isinstance(other, wasm_byte_vec_t): + return False + + if self.num_elems != other.num_elems: + return False + + self_data = bytes(self.data[: self.num_elems]) + other_data = bytes(other.data[: other.num_elems]) + return self_data.decode() == other_data.decode() + + +def __repr_wasm_byte_vec_t(self): + data = bytes(self.data[: self.num_elems]) + return data.decode() if self.size else "" + + +wasm_byte_vec_t.__eq__ = __compare_wasm_byte_vec_t +wasm_byte_vec_t.__repr__ = __repr_wasm_byte_vec_t + + +def __compare_wasm_functype_t(self, other): + if not isinstance(other, wasm_functype_t): + return False + + params1 = dereference(wasm_functype_params(byref(self))) + params2 = dereference(wasm_functype_params(byref(other))) + results1 = dereference(wasm_functype_results(byref(self))) + results2 = dereference(wasm_functype_results(byref(other))) + return params1 == params2 and results1 == results2 + + +def __repr_wasm_functype_t(self): + params = dereference(wasm_functype_params(byref(self))) + results = dereference(wasm_functype_results(byref(self))) + params = f" (params {params})" if params.size else "" + results = f" (results {results})" if results.size else "" + return f"(func{params}{results})" + + +wasm_functype_t.__eq__ = __compare_wasm_functype_t +wasm_functype_t.__repr__ = __repr_wasm_functype_t + + +def __compare_wasm_globaltype_t(self, other): + if not isinstance(other, wasm_globaltype_t): + return False + + content1 = dereference(wasm_globaltype_content(byref(self))) + content2 = dereference(wasm_globaltype_content(byref(other))) + mutability1 = wasm_globaltype_mutability(byref(self)) + mutability2 = wasm_globaltype_mutability(byref(other)) + return content1 == content2 and mutability1 == mutability2 + + +def __repr_wasm_globaltype_t(self): + mutability = f"{wasm_globaltype_mutability(byref(self))}" + content = f"{dereference(wasm_globaltype_content(byref(self)))}" + return f"(global{' mut ' if mutability else ' '}{content})" + + +wasm_globaltype_t.__eq__ = __compare_wasm_globaltype_t +wasm_globaltype_t.__repr__ = __repr_wasm_globaltype_t + + +def __compare_wasm_tabletype_t(self, other): + if not isinstance(other, wasm_tabletype_t): + return False + + element1 = dereference(wasm_tabletype_element(byref(self))) + element2 = dereference(wasm_tabletype_element(byref(other))) + limits1 = dereference(wasm_tabletype_limits(byref(self))) + limits2 = dereference(wasm_tabletype_limits(byref(other))) + return element1 == element2 and limits1 == limits2 + + +def __repr_wasm_tabletype_t(self): + element = dereference(wasm_tabletype_element(byref(self))) + limit = dereference(wasm_tabletype_limits(byref(self))) + return f"(table {limit} {element})" + + +wasm_tabletype_t.__eq__ = __compare_wasm_tabletype_t +wasm_tabletype_t.__repr__ = __repr_wasm_tabletype_t + + +def __compare_wasm_memorytype_t(self, other): + if not isinstance(other, wasm_memorytype_t): + return False + + limits1 = dereference(wasm_memorytype_limits(byref(self))) + limits2 = dereference(wasm_memorytype_limits(byref(other))) + return limits1 == limits2 + + +def __repr_wasm_memorytype_t(self): + limit = dereference(wasm_memorytype_limits(byref(self))) + return f"(memory {limit})" + + +wasm_memorytype_t.__eq__ = __compare_wasm_memorytype_t +wasm_memorytype_t.__repr__ = __repr_wasm_memorytype_t + + +def __compare_wasm_externtype_t(self, other): + if not isinstance(other, wasm_externtype_t): + return False + + if wasm_externtype_kind(byref(self)) != wasm_externtype_kind(byref(other)): + return False + + extern_kind = wasm_externtype_kind(byref(self)) + if WASM_EXTERN_FUNC == extern_kind: + return dereference(wasm_externtype_as_functype(self)) == dereference( + wasm_externtype_as_functype(other) + ) + elif WASM_EXTERN_GLOBAL == extern_kind: + return dereference(wasm_externtype_as_globaltype(self)) == dereference( + wasm_externtype_as_globaltype(other) + ) + elif WASM_EXTERN_MEMORY == extern_kind: + return dereference(wasm_externtype_as_memorytype(self)) == dereference( + wasm_externtype_as_memorytype(other) + ) + elif WASM_EXTERN_TABLE == extern_kind: + return dereference(wasm_externtype_as_tabletype(self)) == dereference( + wasm_externtype_as_tabletype(other) + ) + else: + raise RuntimeError("not a valid wasm_externtype_t") + + +def __repr_wasm_externtype_t(self): + extern_kind = wasm_externtype_kind(byref(self)) + if WASM_EXTERN_FUNC == extern_kind: + return str(dereference(wasm_externtype_as_functype(byref(self)))) + elif WASM_EXTERN_GLOBAL == extern_kind: + return str(dereference(wasm_externtype_as_globaltype(byref(self)))) + elif WASM_EXTERN_MEMORY == extern_kind: + return str(dereference(wasm_externtype_as_memorytype(byref(self)))) + elif WASM_EXTERN_TABLE == extern_kind: + return str(dereference(wasm_externtype_as_tabletype(byref(self)))) + else: + raise RuntimeError("not a valid wasm_externtype_t") + + +wasm_externtype_t.__eq__ = __compare_wasm_externtype_t +wasm_externtype_t.__repr__ = __repr_wasm_externtype_t + + +def __compare_wasm_importtype_t(self, other): + if not isinstance(other, wasm_importtype_t): + return False + + if dereference(wasm_importtype_module(self)) != dereference( + wasm_importtype_module(other) + ): + return False + + if dereference(wasm_importtype_name(self)) != dereference( + wasm_importtype_name(other) + ): + return False + + self_type = dereference(wasm_importtype_type(byref(self))) + other_type = dereference(wasm_importtype_type(byref(other))) + return self_type == other_type + + +def __repr_wasm_importtype_t(self): + module = wasm_importtype_module(byref(self)) + name = wasm_importtype_name(byref(self)) + extern_type = wasm_importtype_type(byref(self)) + return f'(import "{dereference(module)}" "{dereference(name)}" {dereference(extern_type)})' + + +wasm_importtype_t.__eq__ = __compare_wasm_importtype_t +wasm_importtype_t.__repr__ = __repr_wasm_importtype_t + + +def __compare_wasm_exporttype_t(self, other): + if not isinstance(other, wasm_exporttype_t): + return False + + self_name = dereference(wasm_exporttype_name(byref(self))) + other_name = dereference(wasm_exporttype_name(byref(other))) + if self_name != other_name: + return False + + self_type = dereference(wasm_exporttype_type(byref(self))) + other_type = dereference(wasm_exporttype_type(byref(other))) + return self_type == other_type + + +def __repr_wasm_exporttype_t(self): + name = wasm_exporttype_name(byref(self)) + extern_type = wasm_exporttype_type(byref(self)) + return f'(export "{dereference(name)}" {dereference(extern_type)})' + + +wasm_exporttype_t.__eq__ = __compare_wasm_exporttype_t +wasm_exporttype_t.__repr__ = __repr_wasm_exporttype_t + + +def __compare_wasm_val_t(self, other): + if not isinstance(other, wasm_val_t): + return False + + if self.kind != other.kind: + return False + + if WASM_I32 == self.kind: + return self.of.i32 == other.of.i32 + elif WASM_I64 == self.kind: + return self.of.i64 == other.of.i64 + elif WASM_F32 == self.kind: + return self.of.f32 == other.of.f32 + elif WASM_F64 == self.kind: + return self.of.f64 == other.of.f64 + elif WASM_EXTERNREF == self.kind: + raise RuntimeError("FIXME") + else: + raise RuntimeError("not a valid val kind") + + +def __repr_wasm_val_t(self): + if WASM_I32 == self.kind: + return f"i32 {self.of.i32}" + elif WASM_I64 == self.kind: + return f"i64 {self.of.i64}" + elif WASM_F32 == self.kind: + return f"f32 {self.of.f32}" + elif WASM_F64 == self.kind: + return f"f64 {self.of.f64}" + elif WASM_EXTERNREF == self.kind: + return f"externref {self.of.ref}" + else: + raise RuntimeError("not a valid val kind") + + +wasm_val_t.__repr__ = __repr_wasm_val_t +wasm_val_t.__eq__ = __compare_wasm_val_t + + +def __repr_wasm_trap_t(self): + message = wasm_message_t() + wasm_trap_message(self, message) + return f'(trap "{str(message)}")' + + +wasm_trap_t.__repr__ = __repr_wasm_trap_t + + +def __repr_wasm_frame_t(self): + instance = wasm_frame_instance(self) + module_offset = wasm_frame_module_offset(self) + func_index = wasm_frame_func_index(self) + func_offset = wasm_frame_func_offset(self) + return f"> module:{module_offset:#x} => func#{func_index:#x}.{func_offset:#x}" + + +wasm_frame_t.__repr__ = __repr_wasm_frame_t + + +def __repr_wasm_module_t(self): + imports = wasm_importtype_vec_t() + wasm_module_imports(self, imports) + + exports = wasm_exporttype_vec_t() + wasm_module_exports(self, exports) + + ret = "(module" + ret += str(imports).replace("(import", "\n (import") + ret += str(exports).replace("(export", "\n (export") + ret += "\n)" + return ret + + +wasm_module_t.__repr__ = __repr_wasm_module_t + + +def __repr_wasm_instance_t(self): + exports = wasm_extern_vec_t() + wasm_instance_exports(self, exports) + + ret = "(instance" + ret += str(exports).replace("(export", "\n (export") + ret += "\n)" + return ret + + +wasm_instance_t.__repr__ = __repr_wasm_instance_t + + +def __repr_wasm_func_t(self): + ft = wasm_func_type(self) + return f"{str(dereference(ft))[:-1]} ... )" + + +wasm_func_t.__repr__ = __repr_wasm_func_t + + +def __repr_wasm_global_t(self): + gt = wasm_global_type(self) + return f"{str(dereference(gt))[:-1]} ... )" + + +wasm_global_t.__repr__ = __repr_wasm_global_t + + +def __repr_wasm_table_t(self): + tt = wasm_table_type(self) + return f"{str(dereference(tt))[:-1]} ... )" + + +wasm_table_t.__repr__ = __repr_wasm_table_t + + +def __repr_wasm_memory_t(self): + mt = wasm_memory_type(self) + return f"{str(dereference(mt))[:-1]} ... )" + + +wasm_memory_t.__repr__ = __repr_wasm_memory_t + + +def __repr_wasm_extern_t(self): + ext_type = wasm_extern_type(self) + ext_kind = wasm_extern_kind(self) + + ret = "(export " + if WASM_EXTERN_FUNC == ext_kind: + ft = wasm_externtype_as_functype(ext_type) + ret += str(dereference(ft)) + elif WASM_EXTERN_GLOBAL == ext_kind: + gt = wasm_externtype_as_globaltype(ext_type) + ret += str(dereference(gt)) + elif WASM_EXTERN_MEMORY == ext_kind: + mt = wasm_externtype_as_memorytype(ext_type) + ret += str(dereference(mt)) + elif WASM_EXTERN_TABLE == ext_kind: + tt = wasm_externtype_as_tabletype(ext_type) + ret += str(dereference(tt)) + else: + raise RuntimeError("not a valid extern kind") + ret += ")" + return ret + + +wasm_extern_t.__repr__ = __repr_wasm_extern_t + + +# Function Types construction short-hands +def wasm_name_new_from_string(s): + name = wasm_name_t() + data = ((c.c_ubyte) * len(s)).from_buffer_copy(s.encode()) + wasm_byte_vec_new(byref(name), len(s), data) + return name + + +def __wasm_functype_new(param_list, result_list): + def __list_to_wasm_valtype_vec(l): + vec = wasm_valtype_vec_t() + + if not l: + wasm_valtype_vec_new_empty(byref(vec)) + else: + data_type = POINTER(wasm_valtype_t) * len(l) + data = data_type() + for i in range(len(l)): + data[i] = l[i] + wasm_valtype_vec_new(byref(vec), len(l), data) + + return vec + + params = __list_to_wasm_valtype_vec(param_list) + results = __list_to_wasm_valtype_vec(result_list) + return wasm_functype_new(byref(params), byref(results)) + + +def wasm_functype_new_0_0(): + return __wasm_functype_new([], []) + + +def wasm_functype_new_1_0(p1): + return __wasm_functype_new([p1], []) + + +def wasm_functype_new_2_0(p1, p2): + return __wasm_functype_new([p1, p2], []) + + +def wasm_functype_new_3_0(p1, p2, p3): + return __wasm_functype_new([p1, p2, p3], []) + + +def wasm_functype_new_0_1(r1): + return __wasm_functype_new([], [r1]) + + +def wasm_functype_new_1_1(p1, r1): + return __wasm_functype_new([p1], [r1]) + + +def wasm_functype_new_2_1(p1, p2, r1): + return __wasm_functype_new([p1, p2], [r1]) + + +def wasm_functype_new_3_1(p1, p2, p3, r1): + return __wasm_functype_new([p1, p2, p3], [r1]) + + +def wasm_limits_new(min, max): + limit = wasm_limits_t() + limit.min = min + limit.max = max + return c.pointer(limit) + + +def wasm_i32_val(i): + v = wasm_val_t() + v.kind = WASM_I32 + v.of.i32 = i + return v + + +def wasm_i64_val(i): + v = wasm_val_t() + v.kind = WASM_I64 + v.of.i64 = i + return v + + +def wasm_f32_val(z): + v = wasm_val_t() + v.kind = WASM_F32 + v.of.f32 = z + return v + + +def wasm_f64_val(z): + v = wasm_val_t() + v.kind = WASM_F64 + v.of.f64 = z + return v + + +def wasm_func_cb_decl(func): + return wasm_func_callback_t(func) + + +def wasm_func_with_env_cb_decl(func): + return wasm_func_callback_with_env_t(func) diff --git a/language-bindings/python/utils/create_lib.sh b/language-bindings/python/utils/create_lib.sh new file mode 100755 index 0000000000..1571358802 --- /dev/null +++ b/language-bindings/python/utils/create_lib.sh @@ -0,0 +1,38 @@ +#!/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 \ + -DWAMR_BUILD_DEBUG_INTERP=1 \ + -DWAMR_BUILD_LIB_PTHREAD=1 \ + -DWAMR_BUILD_LIB_WASI_THREADS=1 \ + -DWAMR_BUILD_LIB_WASI=1 \ + -DBUILD_SHARED_LIBS=ON \ + .. +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 0000000000..58229da42c --- /dev/null +++ b/language-bindings/python/wamr-api/README.md @@ -0,0 +1,32 @@ +# WAMR API + +* **Notice**: The python package `wamr.wamrapi.wamr` requires a python version >= `3.10`. + +## Setup + +### Pre-requisites +#### Install requirements +Before proceeding it is necessary to make sure your Python environment is correctly configured. To do this open a terminal session in this directory and perform the following: + + +```shell +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +### Build native lib and update bindings + +The following command builds the iwasm library and generates the Python bindings, + +```sh +# In WAMR root directory +bash language-bindings/python/utils/create_lib.sh +``` + +This will build and copy libiwasm into the package. + +## Samples + +- **[basic](./samples/basic)**: Demonstrating how to use basic python bindings. +- **[native-symbol](./samples/native-symbol)**: Desmostrate how to call WASM from Python and how to export Python functions into WASM. diff --git a/language-bindings/python/wamr-api/requirements.txt b/language-bindings/python/wamr-api/requirements.txt new file mode 100644 index 0000000000..923575a447 --- /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/basic/compile.sh b/language-bindings/python/wamr-api/samples/basic/compile.sh new file mode 100755 index 0000000000..4745ef1e84 --- /dev/null +++ b/language-bindings/python/wamr-api/samples/basic/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/basic/main.py b/language-bindings/python/wamr-api/samples/basic/main.py new file mode 100644 index 0000000000..525aab8106 --- /dev/null +++ b/language-bindings/python/wamr-api/samples/basic/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/basic/sum.c b/language-bindings/python/wamr-api/samples/basic/sum.c new file mode 100644 index 0000000000..586c5bc6a5 --- /dev/null +++ b/language-bindings/python/wamr-api/samples/basic/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/wamr-api/samples/native-symbol/README.md b/language-bindings/python/wamr-api/samples/native-symbol/README.md new file mode 100644 index 0000000000..75d4fab46f --- /dev/null +++ b/language-bindings/python/wamr-api/samples/native-symbol/README.md @@ -0,0 +1,44 @@ +# Native Symbol + +This sample demonstrates how to declare a Python function as `NativeSymbol`. + +Steps of the example: +1. Load WASM from Python +2. Call `c_func` from WASM. +3. `c_func` calls `python_func` from Python. +4. `python_func` calls `add` from WASM. +5. Result shown by Python. + +## Build + +Follow instructions [build wamr Python package](../../README.md). + +Compile WASM app example, + +```sh +./compile.sh +``` + +## Run sample + +```sh +python main.py +``` + +Output: + +``` +python: calling c_func(10) +c: in c_func with input: 10 +c: calling python_func(11) +python: in python_func with input: 11 +python: calling add(11, 1000) +python: result from add: 1011 +c: result from python_func: 1012 +c: returning 1013 +python: result from c_func: 1013 +deleting ExecEnv +deleting Instance +deleting Module +deleting Engine +``` diff --git a/language-bindings/python/wamr-api/samples/native-symbol/compile.sh b/language-bindings/python/wamr-api/samples/native-symbol/compile.sh new file mode 100755 index 0000000000..4e504b018b --- /dev/null +++ b/language-bindings/python/wamr-api/samples/native-symbol/compile.sh @@ -0,0 +1,14 @@ +#!/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,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--export=__data_end -Wl,--export=__heap_base \ + -Wl,--strip-all,--no-entry \ + -Wl,--allow-undefined \ + -Wl,--export=c_func\ + -Wl,--export=add\ + -o func.wasm func.c diff --git a/language-bindings/python/wamr-api/samples/native-symbol/func.c b/language-bindings/python/wamr-api/samples/native-symbol/func.c new file mode 100644 index 0000000000..4411a6b2cf --- /dev/null +++ b/language-bindings/python/wamr-api/samples/native-symbol/func.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +int +python_func(int val); + +int +add(int val1, int val2) +{ + return val1 + val2; +} + +int +c_func(int val) +{ + printf("c: in c_func with input: %d\n", val); + printf("c: calling python_func(%d)\n", val + 1); + int res = python_func(val + 1); + printf("c: result from python_func: %d\n", res); + printf("c: returning %d\n", res + 1); + return res + 1; +} + +int +main() +{} diff --git a/language-bindings/python/wamr-api/samples/native-symbol/main.py b/language-bindings/python/wamr-api/samples/native-symbol/main.py new file mode 100644 index 0000000000..3871ef1398 --- /dev/null +++ b/language-bindings/python/wamr-api/samples/native-symbol/main.py @@ -0,0 +1,59 @@ +# 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 +from ctypes import c_int32 +from ctypes import c_uint +from ctypes import c_void_p +from ctypes import cast +from ctypes import CFUNCTYPE + +from wamr.wamrapi.iwasm import NativeSymbol +from wamr.wamrapi.iwasm import String +from wamr.wamrapi.wamr import ExecEnv + +def python_func(env: int, value: int) -> int: + print("python: in python_func with input:", value) + # Example of generating ExecEnv from `wasm_exec_env_t`` + exec_env = ExecEnv.wrap(env) + add = exec_env.get_module_inst().lookup_function("add") + const = 1000 + argv = (c_uint * 2)(value, const) + print(f"python: calling add({value}, {const})") + exec_env.call(add, 2, argv) + res = argv[0] + print("python: result from add:", res) + return res + 1 + + +native_symbols = (NativeSymbol * 1)( + *[ + NativeSymbol( + symbol=String.from_param("python_func"), + func_ptr=cast( + CFUNCTYPE(c_int32, c_void_p, c_int32)(python_func), c_void_p + ), + signature=String.from_param("(i)i"), + ) + ] +) + +def main(): + engine = Engine() + engine.register_natives("env", native_symbols) + module = Module.from_file(engine, pathlib.Path(__file__).parent / "func.wasm") + module_inst = Instance(module) + exec_env = ExecEnv(module_inst) + + func = module_inst.lookup_function("c_func") + + inp = 10 + print(f"python: calling c_func({inp})") + argv = (c_uint)(inp) + exec_env.call(func, 1, argv) + print("python: result from c_func:", argv.value) + +if __name__ == "__main__": + main() 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 0000000000..1684afa5c1 --- /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/wasm-c-api/docs/design.md b/language-bindings/python/wasm-c-api/docs/design.md new file mode 100644 index 0000000000..3478ad021c --- /dev/null +++ b/language-bindings/python/wasm-c-api/docs/design.md @@ -0,0 +1,709 @@ +# how to implement a python binding of WAMR + +A python language binding of Wasm runtime allows its users to call a set of APIs of +the runtime from the python world. Those APIs maybe implemented in C, C++, or Rust. + +In the WAMR case, a python binding allows APIs in `core/iwasm/include/wasm_c_api.h` +to be used in the python scripts. To achieve that, we will create two kinds +of stuff: wrappers of structured data types and wrappers of functions under the +help of _ctypes_. + +Cyptes is a tool in the standard library for creating Python bindings. It +provides a low-level toolset for loading shared libraries and marshaling +data between Python and C. Other options include _cffi_, _pybind11_, +_cpython_ and so on. Because we tend to make the binding depending on least +items. The built-in module, _ctypes_, is a good choice. + +## General rules to marshal + +The core of the idea of a language binding is how to translate different +representations of types in different language. + +### load libraries + +The `ctypes` supports locating a dynamic link library in a way similar to the +compiler does. + +Currently, `ctypes.LoadLibrary` supports: + +- `CDLL`. Those libraries use the standard C calling conversion. +- `OleDLL` and `WinDLL`. Those libraries use the `stdcall` calling conversion on + Windows only + +### fundamental datatypes + +_ctypes_ provides [primitive C compatiable data types](https://docs.python.org/3/library/ctypes.html#fundamental-data-types). +Like `c_bool`, `c_byte`, `c_int`, `c_long` and so on. + +> `c_int` represents the _C_ `signed int` datatype. On platforms where +> `sizeof(int) == sizeof(long)` it is an alias to `c_long`. + +| c datatypes | ctypes | +| ------------------- | ----------------------- | +| bool | c_bool | +| byte_t | c_ubyte | +| char | c_char | +| float32_t | c_float | +| float64_t | c_double | +| int32_t | c_int32 | +| int64_t | c_int64 | +| intptr_t | c_void_p | +| size_t | c_size_t | +| uint8_t | c_uint8 | +| uint32_t | c_uint32 | +| void | None | +| wasm_byte_t | c_ubyte | +| wasm_externkind_t | c_uint8 | +| wasm_memory_pages_t | c_uint32 | +| wasm_mutability_t | c_bool | +| wasm_table_size_t | c_uint32 | +| wasm_valkind_t | c_uint8 | +| wasm_data_type\* | POINTER(wasm_data_type) | + +- `c_void_p` only represents `void *` only +- `None` represents `void` in function parameter lists and return lists + +### structured datatypes + +Create a corresponding concept for every native structured data type includes +`enum`, `struct` and `union`, in the python world. + +#### Enum types + +For example, if there is a `enum wasm_mutability_enum` in native. + +```c +typedef uint8_t wasm_mutability_t; +enum wasm_mutability_enum { + WASM_CONST, + WASM_VAR +}; +``` + +Use `ctypes.int`(or any integer types in ctypes) to represents its value directly. + +```python +# represents enum wasm_mutability_enum +wasm_mutability_t = c_uint8 + +WASM_CONST = 0 +WASM_VAR = 1 +``` + +> C standard only requires "Each enumerated type shall be compatible with char, +> a signed integer type, or an unsigned integer type. The choice of the integer +> type is implementation-defined, but shall be capable of representing the +> values of all the members of the enumeration. + +#### Struct types + +If there is a `struct wasm_byte_vec_t` in native(in C). + +```c +typedef struct wasm_byte_vec_t { + size_t size; + wasm_byte_t *data; + size_t num_elems; + size_t size_of_elem; +} wasm_byte_vec_t; +``` + +Use `ctypes.Structure` to create its corresponding data type in python. + +```python +class wasm_byte_vec_t(ctypes.Structure): + _fileds_ = [ + ("size", ctypes.c_size_t), + ("data", ctypes.POINTER(c_ubyte)), + ("num_elems", ctypes.c_size_t), + ("size_of_elem", ctypes.c_size_t), + ] +``` + +a list of `Structures` + +| name | +| ----------------- | +| wasm_engine_t | +| wasm_store_t | +| wasm_limits_t | +| wasm_valtype_t | +| wasm_functype_t | +| wasm_globaltype_t | +| wasm_tabletype_t | +| wasm_memorytype_t | +| wasm_externtype_t | +| wasm_importtype_t | +| wasm_exporttype_t | +| wasm_ref_t | +| wasm_ref_t | +| wasm_frame_t | +| wasm_trap_t | +| wasm_foreign_t | +| WASMModuleCommon | +| WASMModuleCommon | +| wasm_func_t | +| wasm_global_t | +| wasm_table_t | +| wasm_memory_t | +| wasm_extern_t | +| wasm_instance_t | + +not supported `struct` + +- wasm_config_t + +If there is an anonymous `union` in native. + +```c +typedef struct wasm_val_t { + wasm_valkind_t kind; + union { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + } of; +} wasm_val_t; +``` + +Use `ctypes.Union` to create its corresponding data type in python. + +```python +class _OF(ctypes.Union): + _fields_ = [ + ("i32", ctypes.c_int32), + ("i64", ctypes.c_int64), + ("f32", ctypes.c_float), + ("f64", ctypes.c_double), + ] + +class wasm_val_t(ctypes.Structure): + _anonymous_ = ("of",) + _fields_ = [ + ("kind", ctypes.c_uint8) + ("of", _OF) + ] +``` + +### wrappers of functions + +Foreign functions (C functions) can be accessed as attributes of loaded shared +libraries or an instance of function prototypes. Callback functions(python +functions) can only be accessed by instantiating function prototypes. + +For example, + +```c +void wasm_name_new(wasm_name_t* out, size_t len, wasm_byte_t [] data); +``` + +Assume there are: + +- `class wasm_name_t` of python represents `wasm_name_t` of C +- `libiwasm` represents loaded _libiwasm.so_ + +If to access a c function like an attribute, + +```python +def wasm_name_new(out, len, data): + _wasm_name_new = libiwasm.wasm_name_new + _wasm_name_new.argtypes = (ctypes.POINTER(wasm_name_t), ctypes.c_size_t, ctypes.POINTER(ctypes.c_ubyte)) + _wasm_name_new.restype = None + return _wasm_name_new(out, len, data) +``` + +Or to instantiate a function prototype, + +```python +def wasm_name_new(out, len, data): + return ctypes.CFUNCTYPE(None, (ctypes.POINTER(wasm_name_t), ctypes.c_size_t, ctypes.POINTER(ctypes.c_ubyte)))( + ("wasm_name_new", libiwasm), out, len, data) +``` + +Now it is able to create a `wasm_name_t` with `wasm_name_new()` in python. + +Sometimes, need to create a python function as a callback of c. + +```c +wasm_trap_t* (*wasm_func_callback_t)(wasm_val_vec_t* args, wasm_val_vec_t *results); +``` + +Use `cyptes.CFUNCTYPE` to create a _pointer of function_ + +```python +def hello(args, results): + print("hello from a callback") + +wasm_func_callback_t = ctypes.CFUNCTYPE(c_size_t, POINTER(wasm_val_vec_t), POINTER(wasm_val_vec_t)) +hello_callback = wasm_func_callback_t(hello) +``` + +or with a decorator + +```python +def wasm_func_cb_decl(func): + return @ctypes.CFUNCTYPE(ctypes.POINTER(wasm_trap_t), (ctypes.POINTER(wasm_val_vec_t), ctypes.POINTER(wasm_val_vec_t)))(func) + +@wasm_func_cb_decl +def hello(args, results): + print("hello from a callback") +``` + +### programming tips + +#### `struct` and `ctypes.Structure` + +There are two kinds of `cytes.Structure` in `binding.py`. + +- has `__field__` definition. like `class wasm_byte_vec_t(Structure)` +- doesn't have `__field__` definition. like `class wasm_config_t(Structure)` + +Since, `ctypes` will create its C world _mirror_ variable according to `__field__` +information, `wasm_config_t()` will only create a python instance without binding +to any C variable. `wasm_byte_vec_t()` will return a python instance with an internal +C variable. + +That is why `pointer(wasm_config_t())` is a NULL pointer which can not be dereferenced. + +#### deal with pointers + +`byref()` and `pointer()` are two functions can return a pointer. + +```python +x = ctypes.c_int(2) + +# use pointer() to creates a new pointer instance which would later be used in Python +x_ptr = ctypes.pointer(x) +... +struct_use_pointer = Mystruct() +struct_use_pointer.ptr = x_ptr + +# use byref() pass a pointer to an object to a foreign function call +func(ctypes.byref(x)) +``` + +The main difference is that `pointer()` does a lot more work since it +constructs a real pointer object. It is faster to use `byref(`) if don't need +the pointer object in Python itself(e.g. only use it as an argument to pass +to a function). + +There is no doubt that `wasm_xxx_new()` which return type is `ctypes.POINTER` +can return a pointer. Plus, the return value of `wasm_xxx_t()` can also be +used as a pointer without casting by `byref` or `pointer`. + +#### array + +In [ctypes document](https://docs.python.org/3/library/ctypes.html#arrays), +it states that "The recommended way to create array types is by multiplying a +data type with a positive integer". So _multiplying a data type_ should be a +better way to create arrays + +```python +from ctypes import * + +class POINT(Structure): + _fields_ = ("x", c_int), ("y", c_int) + +# multiplying a data type +# type(TenPointsArrayType) is +TenPointsArrayType = POINT * 10 + +# Instances are created in the usual way, by calling the class: +arr = TenPointsArrayType() +arr[0] = POINT(3,2) +for pt in arr: + print(pt.x, pt.y) +``` + +On both sides, it is OK to assign an array to a pointer. + +```c +char buf[128] = {0}; +char *ptr = buf; +``` + +```python +binary = wasm_byte_vec_t() +binary.data = (ctypes.c_ubyte * len(wasm)).from_buffer_copy(wasm) +``` + +#### exceptions and traps + +Interfaces of _wasm-c-api_ have their return values to represent failures. +The python binding should just keep and transfer them to callers instead of +raising any additional exception. + +The python binding should raise exceptions when the python partial is failed. + +#### readonly buffer + +```python +with open("hello.wasm", "rb") as f: + wasm = f.read() + binary = wasm_byte_vec_t() + wasm_byte_vec_new_uninitialized(byref(binary), len(wasm)) + # create a ctypes instance (byte[] in c) and copy the content + # from wasm(bytearray in python) + binary.data = (ctypes.c_ubyte * len(wasm)).from_buffer_copy(wasm) +``` + +in the above example, `wasm` is a python-created readable buffer. It is not +writable and needs to be copied into a ctype array. + +#### variable arguments + +A function with _variable arguments_ makes it hard to specify the required +argument types for the function prototype. It leaves us one way to call it +directly without any arguments type checking. + +```python +libc.printf(b"Hello, an int %d, a float %f, a string %s\n", c_int(1), c_double(3.14), "World!") +``` + +#### Use `c_bool` to represent `wasm_mutability_t ` + +- `True` for `WASM_CONST` +- `False` for `WASM_VALUE` + +#### customize class builtins + +- `__eq__` for comparation. +- `__repr__` for printing. + +### bindgen.py + +`bindgen.py` is a tool to create WAMR python binding automatically. `binding.py` +is generated. We should avoid modification on it. Additional helpers should go +to `ffi.py`. + +`bindgen.py` uses _pycparser_. Visit the AST of `core/iwasm/include/wasm_c_api.h` +created by _gcc_ and generate necessary wrappers. + +```python +from pycparser import c_ast + +class Visitor(c_ast.NodeVisitor): + def visit_Struct(self, node): + pass + + def visit_Union(self, node): + pass + + def visit_TypeDef(self, node): + pass + + def visit_FuncDecl(self, node): + pass + +ast = parse_file(...) +v = Visitor() +v.visit(ast) +``` + +Before running _bindgen.py_, the shared library _libiwasm.so_ should be generated. + +```bash +$ cd /path/to/wamr/repo +$ # if it is in linux +$ pushd product-mini/platforms/linux/ +$ cmake -S . -B build .. +$ cmake --build build --target iwasm +$ popd +$ cd binding/python +$ python utils/bindgen.py +``` + +`wasm_frame_xxx` and `wasm_trap_xxx` only work well when enabling `WAMR_BUILD_DUMP_CALL_STACK`. + +```bash +$ cmake -S . -B build -DWAMR_BUILD_DUMP_CALL_STACK=1 .. +``` + +## OOP wrappers + +Based on the above general rules, there will be corresponding python +APIs for every C API in `wasm_c_api.h` with same name. Users can do procedural +programming with those. + +In next phase, we will create OOP APIs. Almost follow the +[C++ version of wasm_c_api](https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.hh) + +## A big list + +| WASM Concept | Procedural APIs | OOP APIs | OOP APIs methods | +| ------------ | -------------------------------- | ---------- | ---------------- | +| XXX_vec | wasm_xxx_vec_new | | list | +| | wasm_xxx_vec_new_uninitialized | | | +| | wasm_xxx_vec_new_empty | | | +| | wasm_xxx_vec_copy | | | +| | wasm_xxx_vec_delete | | | +| valtype | wasm_valtype_new | valtype | \_\_init\_\_ | +| | wasm_valtype_delete | | \_\_del\_\_ | +| | wasm_valtype_kind | | \_\_eq\_\_ | +| | wasm_valtype_copy | | | +| | _vector methods_ | | | +| functype | wasm_functype_new | functype | | +| | wasm_functype_delete | | | +| | wasm_functype_params | | | +| | wasm_functype_results | | | +| | wasm_functype_copy | | | +| | _vector methods_ | | | +| globaltype | wasm_globaltype_new | globaltype | \_\_init\_\_ | +| | wasm_globaltype_delete | | \_\_del\_\_ | +| | wasm_globaltype_content | | \_\_eq\_\_ | +| | wasm_globaltype_mutability | | | +| | wasm_globaltype_copy | | | +| | _vector methods_ | | | +| tabletype | wasm_tabletype_new | tabletype | \_\_init\_\_ | +| | wasm_tabletype_delete | | \_\_del\_\_ | +| | wasm_tabletype_element | | \_\_eq\_\_ | +| | wasm_tabletype_limits | | | +| | wasm_tabletype_copy | | | +| | _vector methods_ | | | +| memorytype | wasm_memorytype_new | memorytype | \_\_init\_\_ | +| | wasm_memorytype_delete | | \_\_del\_\_ | +| | wasm_memorytype_limits | | \_\_eq\_\_ | +| | wasm_memorytype_copy | | | +| | _vector methods_ | | | +| externtype | wasm_externtype_as_XXX | externtype | | +| | wasm_XXX_as_externtype | | | +| | wasm_externtype_copy | | | +| | wasm_externtype_delete | | | +| | wasm_externtype_kind | | | +| | _vector methods_ | | | +| importtype | wasm_importtype_new | importtype | | +| | wasm_importtype_delete | | | +| | wasm_importtype_module | | | +| | wasm_importtype_name | | | +| | wasm_importtype_type | | | +| | wasm_importtype_copy | | | +| | _vector methods_ | | | +| exportype | wasm_exporttype_new | exporttype | | +| | wasm_exporttype_delete | | | +| | wasm_exporttype_name | | | +| | wasm_exporttype_type | | | +| | wasm_exporttype_copy | | | +| | _vector methods_ | | | +| val | wasm_val_delete | val | | +| | wasm_val_copy | | | +| | _vector methods_ | | | +| frame | wasm_frame_delete | frame | | +| | wasm_frame_instance | | | +| | wasm_frame_func_index | | | +| | wasm_frame_func_offset | | | +| | wasm_frame_module_offset | | | +| | wasm_frame_copy | | | +| | _vector methods_ | | | +| trap | wasm_trap_new | trap | | +| | wasm_trap_delete | | | +| | wasm_trap_message | | | +| | wasm_trap_origin | | | +| | wasm_trap_trace | | | +| | _vector methods_ | | | +| foreign | wasm_foreign_new | foreign | | +| | wasm_foreign_delete | | | +| | _vector methods_ | | | +| engine | wasm_engine_new | engine | | +| | wasm_engine_new_with_args\* | | | +| | wasm_engine_new_with_config | | | +| | wasm_engine_delete | | | +| store | wasm_store_new | store | | +| | wasm_store_delete | | | +| | _vector methods_ | | | +| module | wasm_module_new | module | | +| | wasm_module_delete | | | +| | wasm_module_validate | | | +| | wasm_module_imports | | | +| | wasm_module_exports | | | +| instance | wasm_instance_new | instance | | +| | wasm_instance_delete | | | +| | wasm_instance_new_with_args\* | | | +| | wasm_instance_new_with_args_ex\* | | | +| | wasm_instance_exports | | | +| | _vector methods_ | | | +| func | wasm_func_new | func | | +| | wasm_func_new_with_env | | | +| | wasm_func_delete | | | +| | wasm_func_type | | | +| | wasm_func_call | | | +| | wasm_func_param_arity | | | +| | wasm_func_result_arity | | | +| | _vector methods_ | | | +| global | wasm_global_new | global | | +| | wasm_global_delete | | | +| | wasm_global_type | | | +| | wasm_global_get | | | +| | wasm_global_set | | | +| | _vector methods_ | | | +| table | wasm_table_new | table | | +| | wasm_table_delete | | | +| | wasm_table_type | | | +| | wasm_table_get | | | +| | wasm_table_set | | | +| | wasm_table_size | | | +| | _vector methods_ | | | +| memory | wasm_memory_new | memory | | +| | wasm_memory_delete | | | +| | wasm_memory_type | | | +| | wasm_memory_data | | | +| | wasm_memory_data_size | | | +| | wasm_memory_size | | | +| | _vector methods_ | | | +| extern | wasm_extern_delete | extern | | +| | wasm_extern_as_XXX | | | +| | wasm_XXX_as_extern | | | +| | wasm_extern_kind | | | +| | wasm_extern_type | | | +| | _vector methods_ | | | + +not supported _functions_ + +- wasm_config_XXX +- wasm_module_deserialize +- wasm_module_serialize +- wasm_ref_XXX +- wasm_XXX_as_ref +- wasm_XXX_as_ref_const +- wasm_XXX_copy +- wasm_XXX_get_host_info +- wasm_XXX_set_host_info + +## test + +there will be two kinds of tests in the project + +- unit test. located in `./tests`. driven by _unittest_. run by + `$ python -m unittest` or `$ make test`. +- integration test. located in `./samples`. + +The whole project is under test-driven development. Every wrapper function will +have two kinds of test cases. The first kind is a positive case. It checks a +wrapper function with expected and safe arguments combinations. Its goal is the +function should work well with expected inputs. Another kind is a negative +case. It feeds unexpected arguments combinations into a wrapper function. Arguments +should include but not be limited to `None`. It ensures that the function will +gracefully handle invalid input or unexpected behaviors. + +## distribution + +### package + +Create a python package named `wamr`. Users should import it after installation +just like any other python module. + +```python +from wamr import * +``` + +### PyPI + +Refer to [tutorial provided by PyPA](https://packaging.python.org/en/latest/tutorials/packaging-projects/). +Steps to publish WAMR Python library: + +1. Creating `pyproject.toml` tells build tools (like pip and build) what is + required to build a project. An example .toml file uses _setuptools_ + + ```toml + [build-system] + requires = [ + "setuptools>=42", + "wheel" + ] + build-backend = "setuptools.build_meta" + ``` + +2. Configuring metadata tells build tools about a package (such as the name + and the version), as well as which code files to include + + - Static metadata (`setup.cfg`): guaranteed to be the same every time. + It is simpler, easier to read, and avoids many common errors, like + encoding errors. + + - Dynamic metadata (`setup.py`): possibly non-deterministic. Any items that + are dynamic or determined at install-time, as well as extension modules + or extensions to setuptools, need to go into setup.py. + + **_Static metadata should be preferred_**. Dynamic metadata should be used + only as an escape hatch when necessary. setup.py used to be + required, but can be omitted with newer versions of setuptools and pip. + +3. Including other files in the distribution + + - For [source distribution](https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist): + + It's usually generated using `python setup.py sdist`, providing metadata + and the essential source files needed for installing by a tool like pip, + or for generating a Built Distribution. + + It includes our Python modules, pyproject.toml, metadata, README.md, + LICENSE. If you want to control what goes in this explicitly, + see [Including files in source distributions with MANIFEST.in](https://packaging.python.org/en/latest/guides/using-manifest-in/#using-manifest-in). + + - For [final built distribution](https://packaging.python.org/en/latest/glossary/#term-Built-Distribution) + + A Distribution format containing files and metadata that only need to be + moved to the correct location on the target system, to be installed. + e.g. `Wheel` + + It will have the Python files in the discovered or listed Python packages. + If you want to control what goes here, such as to add data files, + see [Including Data Files](https://setuptools.pypa.io/en/latest/userguide/datafiles.html) from the [setuptools docs](https://setuptools.pypa.io/en/latest/index.html). + +4. Generating distribution archives. These are archives that are uploaded to + the Python Package Index and can be installed by pip. + + example using `setuptools` + + ```shell + python3 -m pip install --upgrade build + python3 -m build + ``` + + generated files: + + ```shell + dist/ + WAMR-package-0.0.1-py3-none-any.whl + WAMR-package-0.0.1.tar.gz + ``` + + The `tar.gz` file is a _source archive_ whereas the `.whl file` is a + _built distribution_. Newer pip versions preferentially install built + distributions but will fall back to source archives if needed. You should + always upload a source archive and provide built archives for compatibility + reasons. + +5. Uploading the distribution archives + + - Register an account on https://pypi.org. + - To securely upload your project, you’ll need a + [PyPI API token](https://pypi.org/help/#apitoken). It can create at + [here](https://pypi.org/manage/account/#api-tokens), and the “Scope” + the setting needs to be “Entire account”. + - After registration, now twine can be used to upload the distribution packages. + + ```shell + # install twine + python3 -m pip install --upgrade twine + # --repository is https://pypi.org/ by default. + # You will be prompted for a username and password. For the username, use __token__. For the password, use the token value, including the pypi- prefix. + twine upload dist/* + ``` + +after all, the python binding will be installed with + +```shell +$ pip install wamr +``` + +PS: A example lifecycle of a python package +![python-package-lifecycle](images/python_package_life_cycle.png) + +## CI + +There are several parts: + +- code format check. +- test. include running all unit test cases and examples. +- publish built distribution. diff --git a/language-bindings/python/wasm-c-api/docs/images/python_package_life_cycle.png b/language-bindings/python/wasm-c-api/docs/images/python_package_life_cycle.png new file mode 100644 index 0000000000..90856e1816 Binary files /dev/null and b/language-bindings/python/wasm-c-api/docs/images/python_package_life_cycle.png differ diff --git a/language-bindings/python/wasm-c-api/docs/setup_dev_env.md b/language-bindings/python/wasm-c-api/docs/setup_dev_env.md new file mode 100644 index 0000000000..6612748f6c --- /dev/null +++ b/language-bindings/python/wasm-c-api/docs/setup_dev_env.md @@ -0,0 +1,12 @@ +Use a python virtual environment tool to create an environment for development. All necessary packages are in _../requirements.txt_. + +python code formatter is provided by _black_. + +python code linter is provided by _pylint_ and default configuration. + +Unit tests are driven by _unittest_. + +```bash +$ python -m unittest -v tests/test_basics.py +$ python -m unittest -v tests/test_advanced.py +``` diff --git a/language-bindings/python/wasm-c-api/requirements.txt b/language-bindings/python/wasm-c-api/requirements.txt new file mode 100644 index 0000000000..7bb68ba6ca --- /dev/null +++ b/language-bindings/python/wasm-c-api/requirements.txt @@ -0,0 +1,5 @@ +black +nose +pycparser +pylint + diff --git a/language-bindings/python/wasm-c-api/samples/hello.wat b/language-bindings/python/wasm-c-api/samples/hello.wat new file mode 100644 index 0000000000..1c56c55822 --- /dev/null +++ b/language-bindings/python/wasm-c-api/samples/hello.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/language-bindings/python/wasm-c-api/samples/hello_oop.py b/language-bindings/python/wasm-c-api/samples/hello_oop.py new file mode 100644 index 0000000000..666f63cd88 --- /dev/null +++ b/language-bindings/python/wasm-c-api/samples/hello_oop.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +import ctypes +from wamr import * + + +def hello_callback(): + print("Calling back...") + print("> Hello World!") + + +def main(): + print("Initializing...") + engine = Engine() + store = Store(engine) + + print("Loading binary...") + print("Compiling module...") + module = Module.from_file(engine, "./hello.wasm") + + print("Creating callback...") + hello = Func(store, FuncType([], []), hello_callback) + + print("Instantiating module...") + instance = Instance(store, module, [hello]) + + print("Extracting export...") + run = instance.exports(store)["run"] + + print("Calling export...") + run(store) + + print("Shutting down...") + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/language-bindings/python/wasm-c-api/samples/hello_procedural.py b/language-bindings/python/wasm-c-api/samples/hello_procedural.py new file mode 100644 index 0000000000..5924423bd6 --- /dev/null +++ b/language-bindings/python/wasm-c-api/samples/hello_procedural.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +import ctypes +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" + b"\x80\x80\x00\x01\x00\x05hello\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00" + b"\x07\x87\x80\x80\x80\x00\x01\x03run\x00\x01\n\x8a\x80\x80\x80\x00\x01" + b"\x84\x80\x80\x80\x00\x00\x10\x00\x0b" +) + + +@ffi.wasm_func_cb_decl +def hello_callback(args, results): + print("Calling back...") + print("> Hello World!") + + +def main(): + print("Initializing...") + engine = ffi.wasm_engine_new() + store = ffi.wasm_store_new(engine) + + print("Loading binary...") + + # for convenience, use binary content instead of open file + # with open("./hello.wasm", "rb") as f: + # wasm = f.read() + wasm = WAMS_BINARY_CONTENT + binary = ffi.wasm_byte_vec_t() + ffi.wasm_byte_vec_new_uninitialized(binary, len(wasm)) + # underlying buffer is not writable + binary.data = (ctypes.c_ubyte * len(wasm)).from_buffer_copy(wasm) + + print("Compiling module...") + module = ffi.wasm_module_new(store, binary) + if not module: + raise RuntimeError("Compiling module failed") + + binary.data = None + ffi.wasm_byte_vec_delete(binary) + + print("Creating callback...") + hello_type = ffi.wasm_functype_new_0_0() + hello_func = ffi.wasm_func_new( + store, + hello_type, + hello_callback, + ) + + ffi.wasm_functype_delete(hello_type) + + print("Instantiating module...") + + imports = ffi.wasm_extern_vec_t() + ffi.wasm_extern_vec_new((imports), 1, ffi.wasm_func_as_extern(hello_func)) + instance = ffi.wasm_instance_new(store, module, imports, None) + + ffi.wasm_func_delete(hello_func) + + print("Extracting export...") + exports = ffi.wasm_extern_vec_t() + ffi.wasm_instance_exports(instance, exports) + + run_func = ffi.wasm_extern_as_func(exports.data[0]) + if not run_func: + raise RuntimeError("can not extract exported function") + + ffi.wasm_instance_delete(instance) + ffi.wasm_module_delete(module) + + print("Calling export...") + args = ffi.wasm_val_vec_t() + results = ffi.wasm_val_vec_t() + + ffi.wasm_val_vec_new_empty(args) + ffi.wasm_val_vec_new_empty(results) + ffi.wasm_func_call(run_func, args, results) + + print("Shutting down...") + ffi.wasm_store_delete(store) + ffi.wasm_engine_delete(engine) + + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/language-bindings/python/wasm-c-api/tests/__init__.py b/language-bindings/python/wasm-c-api/tests/__init__.py new file mode 100644 index 0000000000..fd913c63cc --- /dev/null +++ b/language-bindings/python/wasm-c-api/tests/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +__all__ = ["test_basic", "test_advanced"] diff --git a/language-bindings/python/wasm-c-api/tests/context.py b/language-bindings/python/wasm-c-api/tests/context.py new file mode 100644 index 0000000000..15c30087cb --- /dev/null +++ b/language-bindings/python/wasm-c-api/tests/context.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import os + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) + +import wamr diff --git a/language-bindings/python/wasm-c-api/tests/test_advanced.py b/language-bindings/python/wasm-c-api/tests/test_advanced.py new file mode 100644 index 0000000000..706447ab5c --- /dev/null +++ b/language-bindings/python/wasm-c-api/tests/test_advanced.py @@ -0,0 +1,493 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# pylint: disable=missing-class-docstring +# pylint: disable=missing-function-docstring +# pylint: disable=missing-module-docstring + +import ctypes as c +import math +import unittest + +import wamr.wasmcapi.ffi as ffi + + +# It is a module likes: +# (module +# (import "mod" "g0" (global i32)) +# (import "mod" "f0" (func (param f32) (result f64))) +# +# (func (export "f1") (param i32 i64)) +# (global (export "g1") (mut f32) (f32.const 3.14)) +# (memory (export "m1") 1 2) +# (table (export "t1") 1 funcref) +# +# (func (export "f2") (unreachable)) +# ) +MODULE_BINARY = ( + b"\x00asm\x01\x00\x00\x00\x01\x0e\x03`\x01}\x01|`\x02\x7f~\x00`\x00" + b"\x00\x02\x14\x02\x03mod\x02g0\x03\x7f\x00\x03mod\x02f0\x00\x00\x03\x03" + b"\x02\x01\x02\x04\x04\x01p\x00\x01\x05\x04\x01\x01\x01\x02\x06\t\x01}\x01C" + b"\xc3\xf5H@\x0b\x07\x1a\x05\x02f1\x00\x01\x02g1\x03\x01\x02m1\x02\x00\x02t1" + b"\x01\x00\x02f2\x00\x02\n\x08\x02\x02\x00\x0b\x03\x00\x00\x0b" +) + +# False -> True when testing with a library enabling WAMR_BUILD_DUMP_CALL_STACK flag +TEST_WITH_WAMR_BUILD_DUMP_CALL_STACK = False + + +@ffi.wasm_func_cb_decl +def callback(args, results): + args = ffi.dereference(args) + results = ffi.dereference(results) + + arg_v = args.data[0] + + result_v = ffi.wasm_f64_val(arg_v.of.f32 * 2.0) + ffi.wasm_val_copy(results.data[0], result_v) + results.num_elems = 1 + + print(f"\nIn callback: {arg_v} --> {result_v}\n") + + +@ffi.wasm_func_with_env_cb_decl +def callback_with_env(env, args, results): + # pylint: disable=unused-argument + print("summer") + + +class AdvancedTestSuite(unittest.TestCase): + @classmethod + def setUpClass(cls): + print("Initializing...") + cls._wasm_engine = ffi.wasm_engine_new() + cls._wasm_store = ffi.wasm_store_new(cls._wasm_engine) + + def assertIsNullPointer(self, pointer): + # pylint: disable=invalid-name + if not ffi.is_null_pointer(pointer): + self.fail("not a non-null pointer") + + def assertIsNotNullPointer(self, pointer): + # pylint: disable=invalid-name + if ffi.is_null_pointer(pointer): + self.fail("not a non-null pointer") + + def load_binary(self, binary_string): + print("Load binary...") + binary = ffi.load_module_file(binary_string) + binary = c.pointer(binary) + self.assertIsNotNullPointer(binary) + return binary + + def compile(self, binary): + print("Compile...") + module = ffi.wasm_module_new(self._wasm_store, binary) + self.assertIsNotNullPointer(module) + return module + + def prepare_imports_local(self): + print("Prepare imports...") + func_type = ffi.wasm_functype_new_1_1( + ffi.wasm_valtype_new(ffi.WASM_F32), + ffi.wasm_valtype_new(ffi.WASM_F64), + ) + func = ffi.wasm_func_new(self._wasm_store, func_type, callback) + self.assertIsNotNullPointer(func) + ffi.wasm_functype_delete(func_type) + + glbl_type = ffi.wasm_globaltype_new(ffi.wasm_valtype_new(ffi.WASM_I32), True) + init = ffi.wasm_i32_val(1024) + glbl = ffi.wasm_global_new(self._wasm_store, glbl_type, init) + self.assertIsNotNullPointer(glbl) + ffi.wasm_globaltype_delete(glbl_type) + + imports = ffi.wasm_extern_vec_t() + data = ffi.list_to_carray( + c.POINTER(ffi.wasm_extern_t), + ffi.wasm_func_as_extern(func), + ffi.wasm_global_as_extern(glbl), + ) + ffi.wasm_extern_vec_new(imports, 2, data) + imports = c.pointer(imports) + self.assertIsNotNullPointer(imports) + return imports + + def instantiate(self, module, imports): + print("Instantiate module...") + instance = ffi.wasm_instance_new( + self._wasm_store, module, imports, ffi.create_null_pointer(ffi.wasm_trap_t) + ) + self.assertIsNotNone(instance) + self.assertIsNotNullPointer(instance) + return instance + + def extract_exports(self, instance): + print("Extracting exports...") + exports = ffi.wasm_extern_vec_t() + ffi.wasm_instance_exports(instance, exports) + exports = c.pointer(exports) + self.assertIsNotNullPointer(exports) + return exports + + def setUp(self): + binary = self.load_binary(MODULE_BINARY) + self.module = self.compile(binary) + self.imports = self.prepare_imports_local() + self.instance = self.instantiate(self.module, self.imports) + self.exports = self.extract_exports(self.instance) + + ffi.wasm_byte_vec_delete(binary) + + def tearDown(self): + if self.imports: + ffi.wasm_extern_vec_delete(self.imports) + + if self.exports: + ffi.wasm_extern_vec_delete(self.exports) + + ffi.wasm_instance_delete(self.instance) + ffi.wasm_module_delete(self.module) + + def test_wasm_func_call_wasm(self): + export_list = ffi.wasm_vec_to_list(self.exports) + print(export_list) + + func = ffi.wasm_extern_as_func(export_list[0]) + self.assertIsNotNullPointer(func) + + # make a call + params = ffi.wasm_val_vec_t() + data = ffi.list_to_carray( + ffi.wasm_val_t, + ffi.wasm_i32_val(1024), + ffi.wasm_i64_val(1024 * 1024), + ) + ffi.wasm_val_vec_new(params, 2, data) + + results = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new_empty(results) + + ffi.wasm_func_call(func, params, results) + + def test_wasm_func_call_native(self): + import_list = ffi.wasm_vec_to_list(self.imports) + + func = ffi.wasm_extern_as_func(import_list[0]) + self.assertIsNotNullPointer(func) + + params = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new( + params, 1, ffi.list_to_carray(ffi.wasm_val_t, ffi.wasm_f32_val(3.14)) + ) + results = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new_uninitialized(results, 1) + ffi.wasm_func_call(func, params, results) + self.assertEqual(params.data[0].of.f32 * 2, results.data[0].of.f64) + + def test_wasm_func_call_unlinked(self): + ft = ffi.wasm_functype_new_0_0() + func = ffi.wasm_func_new(self._wasm_store, ft, callback) + params = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new_empty(params) + results = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new_empty(results) + trap = ffi.wasm_func_call(func, params, results) + ffi.wasm_func_delete(func) + + def test_wasm_global_get_wasm(self): + export_list = ffi.wasm_vec_to_list(self.exports) + glb = ffi.wasm_extern_as_global(export_list[1]) + self.assertIsNotNullPointer(glb) + + # access the global + val = ffi.wasm_val_t() + ffi.wasm_global_get(glb, val) + self.assertAlmostEqual(val.of.f32, 3.14, places=3) + + def test_wasm_global_get_native(self): + import_list = ffi.wasm_vec_to_list(self.imports) + + glb = ffi.wasm_extern_as_global(import_list[1]) + self.assertIsNotNullPointer(glb) + + val = ffi.wasm_val_t() + ffi.wasm_global_get(glb, val) + self.assertEqual(val.of.i32, 1024) + + def test_wasm_global_get_unlinked(self): + gt = ffi.wasm_globaltype_new(ffi.wasm_valtype_new(ffi.WASM_I32), True) + init = ffi.wasm_i32_val(32) + glbl = ffi.wasm_global_new(self._wasm_store, gt, init) + val_ret = ffi.wasm_f32_val(3.14) + ffi.wasm_global_get(glbl, val_ret) + ffi.wasm_global_delete(glbl) + + # val_ret wasn't touched, keep the original value + self.assertAlmostEqual(val_ret.of.f32, 3.14, 3) + + def test_wasm_global_get_null_val(self): + export_list = ffi.wasm_vec_to_list(self.exports) + glb = ffi.wasm_extern_as_global(export_list[1]) + ffi.wasm_global_get(glb, ffi.create_null_pointer(ffi.wasm_val_t)) + + def test_wasm_global_get_null_global(self): + val = ffi.wasm_val_t() + ffi.wasm_global_get(ffi.create_null_pointer(ffi.wasm_global_t), val) + + def test_wasm_global_set_wasm(self): + export_list = ffi.wasm_vec_to_list(self.exports) + glb = ffi.wasm_extern_as_global(export_list[1]) + self.assertIsNotNullPointer(glb) + + # access the global + new_val = ffi.wasm_f32_val(math.e) + ffi.wasm_global_set(glb, new_val) + + val = ffi.wasm_val_t() + ffi.wasm_global_get(glb, val) + self.assertNotEqual(val.of.f32, 3.14) + + def test_wasm_global_set_native(self): + import_list = ffi.wasm_vec_to_list(self.imports) + + glb = ffi.wasm_extern_as_global(import_list[1]) + self.assertIsNotNullPointer(glb) + + new_val = ffi.wasm_i32_val(2048) + ffi.wasm_global_set(glb, new_val) + + val = ffi.wasm_val_t() + ffi.wasm_global_get(glb, val) + self.assertEqual(val, new_val) + + def test_wasm_global_set_unlinked(self): + gt = ffi.wasm_globaltype_new(ffi.wasm_valtype_new(ffi.WASM_I32), True) + init = ffi.wasm_i32_val(32) + glbl = ffi.wasm_global_new(self._wasm_store, gt, init) + val_ret = ffi.wasm_f32_val(3.14) + ffi.wasm_global_set(glbl, val_ret) + ffi.wasm_global_delete(glbl) + + def test_wasm_global_set_null_v(self): + export_list = ffi.wasm_vec_to_list(self.exports) + glb = ffi.wasm_extern_as_global(export_list[1]) + # access the global + ffi.wasm_global_set(glb, ffi.create_null_pointer(ffi.wasm_val_t)) + + def test_wasm_global_set_null_global(self): + # access the global + new_val = ffi.wasm_f32_val(math.e) + ffi.wasm_global_set(ffi.create_null_pointer(ffi.wasm_global_t), new_val) + + def test_wasm_table_size(self): + export_list = ffi.wasm_vec_to_list(self.exports) + tbl = ffi.wasm_extern_as_table(export_list[3]) + self.assertIsNotNullPointer(tbl) + + tbl_sz = ffi.wasm_table_size(tbl) + self.assertEqual(tbl_sz, 1) + + def test_wasm_table_size_unlink(self): + vt = ffi.wasm_valtype_new(ffi.WASM_FUNCREF) + limits = ffi.wasm_limits_new(10, 15) + tt = ffi.wasm_tabletype_new(vt, limits) + tbl = ffi.wasm_table_new( + self._wasm_store, tt, ffi.create_null_pointer(ffi.wasm_ref_t) + ) + tbl_sz = ffi.wasm_table_size(tbl) + ffi.wasm_table_delete(tbl) + + def test_wasm_table_size_null_table(self): + ffi.wasm_table_size(ffi.create_null_pointer(ffi.wasm_table_t)) + + def test_wasm_table_get(self): + export_list = ffi.wasm_vec_to_list(self.exports) + tbl = ffi.wasm_extern_as_table(export_list[3]) + self.assertIsNotNullPointer(tbl) + + ref = ffi.wasm_table_get(tbl, 0) + self.assertIsNullPointer(ref) + + ref = ffi.wasm_table_get(tbl, 4096) + self.assertIsNullPointer(ref) + + def test_wasm_table_get_unlinked(self): + vt = ffi.wasm_valtype_new(ffi.WASM_FUNCREF) + limits = ffi.wasm_limits_new(10, 15) + tt = ffi.wasm_tabletype_new(vt, limits) + tbl = ffi.wasm_table_new( + self._wasm_store, tt, ffi.create_null_pointer(ffi.wasm_ref_t) + ) + ffi.wasm_table_get(tbl, 0) + ffi.wasm_table_delete(tbl) + + def test_wasm_table_get_null_table(self): + ffi.wasm_table_get(ffi.create_null_pointer(ffi.wasm_table_t), 0) + + def test_wasm_table_get_out_of_bounds(self): + export_list = ffi.wasm_vec_to_list(self.exports) + tbl = ffi.wasm_extern_as_table(export_list[3]) + ffi.wasm_table_get(tbl, 1_000_000_000) + + def test_wasm_ref(self): + export_list = ffi.wasm_vec_to_list(self.exports) + func = ffi.wasm_extern_as_func(export_list[0]) + self.assertIsNotNullPointer(func) + + ref = ffi.wasm_func_as_ref(func) + self.assertIsNotNullPointer(ref) + + func_from_ref = ffi.wasm_ref_as_func(ref) + self.assertEqual( + ffi.dereference(ffi.wasm_func_type(func)), + ffi.dereference(ffi.wasm_func_type(func_from_ref)), + ) + + def test_wasm_table_set(self): + export_list = ffi.wasm_vec_to_list(self.exports) + tbl = ffi.wasm_extern_as_table(export_list[3]) + self.assertIsNotNullPointer(tbl) + + func = ffi.wasm_extern_as_func(export_list[0]) + ref = ffi.wasm_func_as_ref(func) + + ffi.wasm_table_set(tbl, 0, ref) + + ref_ret = ffi.wasm_table_get(tbl, 0) + self.assertIsNotNullPointer(ref_ret) + func_ret = ffi.wasm_ref_as_func(ref_ret) + self.assertEqual( + ffi.dereference(ffi.wasm_func_type(func)), + ffi.dereference(ffi.wasm_func_type(func_ret)), + ) + + def test_wasm_table_set_unlinked(self): + vt = ffi.wasm_valtype_new(ffi.WASM_FUNCREF) + limits = ffi.wasm_limits_new(10, 15) + tt = ffi.wasm_tabletype_new(vt, limits) + tbl = ffi.wasm_table_new( + self._wasm_store, tt, ffi.create_null_pointer(ffi.wasm_ref_t) + ) + export_list = ffi.wasm_vec_to_list(self.exports) + func = ffi.wasm_extern_as_func(export_list[0]) + ref = ffi.wasm_func_as_ref(func) + ffi.wasm_table_set(tbl, 0, ref) + ffi.wasm_table_delete(tbl) + + def test_wasm_table_set_null_table(self): + export_list = ffi.wasm_vec_to_list(self.exports) + func = ffi.wasm_extern_as_func(export_list[0]) + ref = ffi.wasm_func_as_ref(func) + ffi.wasm_table_set(ffi.create_null_pointer(ffi.wasm_table_t), 0, ref) + + def test_wasm_table_set_null_ref(self): + export_list = ffi.wasm_vec_to_list(self.exports) + tbl = ffi.wasm_extern_as_table(export_list[3]) + ffi.wasm_table_set(tbl, 0, ffi.create_null_pointer(ffi.wasm_ref_t)) + + def test_wasm_table_set_out_of_bounds(self): + export_list = ffi.wasm_vec_to_list(self.exports) + tbl = ffi.wasm_extern_as_table(export_list[3]) + func = ffi.wasm_extern_as_func(export_list[0]) + ref = ffi.wasm_func_as_ref(func) + ffi.wasm_table_set(tbl, 1_000_000_000, ref) + + def test_wasm_memory_size(self): + export_list = ffi.wasm_vec_to_list(self.exports) + mem = ffi.wasm_extern_as_memory(export_list[2]) + self.assertIsNotNullPointer(mem) + + pg_sz = ffi.wasm_memory_size(mem) + self.assertEqual(pg_sz, 1) + + def test_wasm_memory_size_unlinked(self): + limits = ffi.wasm_limits_new(10, 12) + mt = ffi.wasm_memorytype_new(limits) + mem = ffi.wasm_memory_new(self._wasm_store, mt) + ffi.wasm_memory_size(mem) + ffi.wasm_memory_delete(mem) + + def test_wasm_memory_data(self): + export_list = ffi.wasm_vec_to_list(self.exports) + mem = ffi.wasm_extern_as_memory(export_list[2]) + self.assertIsNotNullPointer(mem) + + data_base = ffi.wasm_memory_data(mem) + self.assertIsNotNone(data_base) + + def test_wasm_memory_data_unlinked(self): + limits = ffi.wasm_limits_new(10, 12) + mt = ffi.wasm_memorytype_new(limits) + mem = ffi.wasm_memory_new(self._wasm_store, mt) + ffi.wasm_memory_data(mem) + ffi.wasm_memory_delete(mem) + + def test_wasm_memory_data_size(self): + export_list = ffi.wasm_vec_to_list(self.exports) + mem = ffi.wasm_extern_as_memory(export_list[2]) + self.assertIsNotNullPointer(mem) + + mem_sz = ffi.wasm_memory_data_size(mem) + self.assertGreater(mem_sz, 0) + + def test_wasm_memory_data_size_unlinked(self): + limits = ffi.wasm_limits_new(10, 12) + mt = ffi.wasm_memorytype_new(limits) + mem = ffi.wasm_memory_new(self._wasm_store, mt) + ffi.wasm_memory_data_size(mem) + ffi.wasm_memory_delete(mem) + + @unittest.skipUnless( + TEST_WITH_WAMR_BUILD_DUMP_CALL_STACK, + "need to enable WAMR_BUILD_DUMP_CALL_STACK", + ) + # assertions only works if enabling WAMR_BUILD_DUMP_CALL_STACK + def test_wasm_frame(self): + export_list = ffi.wasm_vec_to_list(self.exports) + func = ffi.wasm_extern_as_func(export_list[4]) + # make a call + params = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new_empty(params) + results = ffi.wasm_val_vec_t() + ffi.wasm_val_vec_new_empty(results) + + print("Making a call...") + trap = ffi.wasm_func_call(func, params, results) + + message = ffi.wasm_message_t() + ffi.wasm_trap_message(trap, message) + self.assertIsNotNullPointer(c.pointer(message)) + print(message) + + frame = ffi.wasm_trap_origin(trap) + self.assertIsNotNullPointer(frame) + print(ffi.dereference(frame)) + + traces = ffi.wasm_frame_vec_t() + ffi.wasm_trap_trace(trap, traces) + self.assertIsNotNullPointer(c.pointer(frame)) + + instance = ffi.wasm_frame_instance(frame) + self.assertIsNotNullPointer(instance) + + module_offset = ffi.wasm_frame_module_offset(frame) + + func_index = ffi.wasm_frame_func_index(frame) + self.assertEqual(func_index, 2) + + func_offset = ffi.wasm_frame_func_offset(frame) + self.assertGreater(func_offset, 0) + + @classmethod + def tearDownClass(cls): + print("Shutting down...") + ffi.wasm_store_delete(cls._wasm_store) + ffi.wasm_engine_delete(cls._wasm_engine) + + +if __name__ == "__main__": + unittest.main() diff --git a/language-bindings/python/wasm-c-api/tests/test_basic.py b/language-bindings/python/wasm-c-api/tests/test_basic.py new file mode 100644 index 0000000000..7ddaf5d1c1 --- /dev/null +++ b/language-bindings/python/wasm-c-api/tests/test_basic.py @@ -0,0 +1,1590 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# pylint: disable=missing-class-docstring +# pylint: disable=missing-function-docstring +# pylint: disable=missing-module-docstring + +import ctypes as c +import unittest +from venv import create + +from wamr.wasmcapi.ffi import * + +# It is a module likes: +# (module +# (import "mod" "g0" (global i32)) +# (import "mod" "f0" (func (param f32) (result f64))) +# +# (func (export "f1") (param i32 i64)) +# (global (export "g1") (mut f32) (f32.const 3.14)) +# (memory 1 2) +# (table 1 funcref) +# ) +MODULE_BINARY = ( + b"\x00asm\x01\x00\x00\x00\x01\x0b\x02`\x01}\x01|`\x02\x7f~\x00" + b"\x02\x14\x02\x03mod\x02g0\x03\x7f\x00\x03mod\x02f0\x00\x00\x03" + b"\x02\x01\x01\x04\x04\x01p\x00\x01\x05\x04\x01\x01\x01\x02\x06\t" + b"\x01}\x01C\xc3\xf5H@\x0b\x07\x0b\x02\x02f1\x00\x01\x02g1\x03\x01\n" + b"\x04\x01\x02\x00\x0b" +) + + +@wasm_func_cb_decl +def callback(args, results): + # pylint: disable=unused-argument + print("summer") + + +@wasm_func_with_env_cb_decl +def callback_with_env(env, args, results): + # pylint: disable=unused-argument + print("summer") + + +class BasicTestSuite(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls._wasm_engine = wasm_engine_new() + cls._wasm_store = wasm_store_new(cls._wasm_engine) + + def assertIsNullPointer(self, c_pointer): + if not is_null_pointer(c_pointer): + self.fail("not a null pointer") + + def assertIsNotNullPointer(self, c_pointer): + if is_null_pointer(c_pointer): + self.fail("not a non-null pointer") + + def test_wasm_valkind(self): + self.assertEqual( + [WASM_I32, WASM_I64, WASM_F32, WASM_F64, WASM_EXTERNREF, WASM_FUNCREF], + [0, 1, 2, 3, 128, 129], + ) + + def test_wasm_valtype_new_pos(self): + vt = wasm_valtype_new(WASM_I32) + self.assertIsNotNullPointer(vt) + wasm_valtype_delete(vt) + + def test_wasm_valtype_new_neg(self): + vt = wasm_valtype_new(37) + self.assertIsNullPointer(vt) + wasm_valtype_delete(vt) + + def test_wasm_valtype_kind_pos(self): + vt = wasm_valtype_new(WASM_I64) + self.assertEqual(wasm_valtype_kind(vt), WASM_I64) + wasm_valtype_delete(vt) + + def test_wasm_valtype_kind_neg(self): + wasm_valtype_kind(create_null_pointer(wasm_valtype_t)) + + def test_wasm_valtype_delete_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + wasm_valtype_delete(vt) + + def test_wasm_valtype_delete_neg(self): + wasm_valtype_delete(create_null_pointer(wasm_valtype_t)) + + def test_wasm_valtype_copy_pos(self): + vt1 = wasm_valtype_new(WASM_FUNCREF) + vt2 = wasm_valtype_copy(vt1) + + self.assertIsNotNone(vt1) + self.assertIsNotNullPointer(vt1) + self.assertEqual(dereference(vt1), dereference(vt2)) + + wasm_valtype_delete(vt1) + wasm_valtype_delete(vt2) + + def test_wasm_valtype_copy_neg(self): + vt = wasm_valtype_copy(create_null_pointer(wasm_valtype_t)) + self.assertIsNotNone(vt) + self.assertIsNullPointer(vt) + + def test_list_to_carray(self): + v1 = wasm_valtype_new(WASM_I64) + v2 = wasm_valtype_new(WASM_F32) + v3 = wasm_valtype_new(WASM_FUNCREF) + data = list_to_carray(c.POINTER(wasm_valtype_t), v1, v2, v3) + + self.assertIsNotNone(data) + self.assertTrue(isinstance(data, c.Array)) + self.assertEqual(data._length_, 3) + self.assertEqual(dereference(data[0]), dereference(v1)) + self.assertEqual(dereference(data[1]), dereference(v2)) + self.assertEqual(dereference(data[2]), dereference(v3)) + + wasm_valtype_delete(v1) + wasm_valtype_delete(v2) + wasm_valtype_delete(v3) + + def test_wasm_valtype_vec_new_pos(self): + def_vt_list = [ + wasm_valtype_new(WASM_I32), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_FUNCREF), + ] + data = list_to_carray(c.POINTER(wasm_valtype_t), *def_vt_list) + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new(vt_vec, 3, data) + + self.assertEqual(vt_vec.size, 3) + self.assertEqual(vt_vec.num_elems, 3) + self.assertIsNotNullPointer(vt_vec.data) + + ret_vt_list = wasm_vec_to_list(vt_vec) + ret_vt_list = [dereference(vt) for vt in ret_vt_list] + def_vt_list = [dereference(vt) for vt in def_vt_list] + self.assertEqual(ret_vt_list, def_vt_list) + + wasm_valtype_vec_delete(vt_vec) + + def test_wasm_valtype_vec_new_neg(self): + data = list_to_carray( + c.POINTER(wasm_valtype_t), + wasm_valtype_new(WASM_I32), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_FUNCREF), + ) + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new(vt_vec, 1_000_000_000, data) + + self.assertEqual(vt_vec.size, 0) + self.assertIsNullPointer(vt_vec.data) + + wasm_valtype_vec_delete(vt_vec) + + def test_wasm_valtype_vec_new_null_out(self): + data = list_to_carray( + c.POINTER(wasm_valtype_t), + wasm_valtype_new(WASM_I32), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_FUNCREF), + ) + wasm_valtype_vec_new(create_null_pointer(wasm_valtype_vec_t), 10, data) + + def test_wasm_valtype_vec_new_null_data(self): + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new(vt_vec, 3, create_null_pointer(wasm_valtype_t)) + self.assertIsNotNone(vt_vec) + self.assertIsNotNullPointer(c.pointer(vt_vec)) + + def test_wasm_valtype_vec_new_uninitialized_pos(self): + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new_uninitialized((vt_vec), 2) + self.assertEqual(2, vt_vec.size) + wasm_valtype_vec_delete(vt_vec) + + def test_wasm_valtype_vec_new_uninitialized_neg(self): + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new_uninitialized(vt_vec, 1_000_000_000) + self.assertEqual(vt_vec.size, 0) + self.assertIsNullPointer(vt_vec.data) + wasm_valtype_vec_delete(vt_vec) + + def test_wasm_valtype_vec_new_uninitialized_null_out(self): + wasm_valtype_vec_new_uninitialized(create_null_pointer(wasm_valtype_vec_t), 2) + + def test_wasm_valtype_vec_new_empty_pos(self): + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new_empty(vt_vec) + self.assertEqual(0, vt_vec.size) + self.assertIsNullPointer(vt_vec.data) + wasm_valtype_vec_delete(vt_vec) + + def test_wasm_valtype_vec_new_empty_neg(self): + wasm_valtype_vec_new_empty(create_null_pointer(wasm_valtype_vec_t)) + + def test_wasm_valtype_vec_copy_pos(self): + vt_vec1 = wasm_valtype_vec_t() + vt1 = wasm_valtype_new(WASM_F32) + vt2 = wasm_valtype_new(WASM_I32) + data = list_to_carray(c.POINTER(wasm_valtype_t), vt1, vt2) + wasm_valtype_vec_new(vt_vec1, 2, data) + + vt_vec2 = wasm_valtype_vec_t() + wasm_valtype_vec_copy(vt_vec2, vt_vec1) + + print(f"{vt_vec1} --> {vt_vec2}") + + self.assertEqual(vt_vec2.size, 2) + self.assertEqual(vt_vec2.num_elems, 2) + self.assertEqual(dereference(vt_vec2.data[0]), dereference(vt1)) + self.assertEqual(dereference(vt_vec2.data[1]), dereference(vt2)) + + wasm_valtype_vec_delete(vt_vec1) + wasm_valtype_vec_delete(vt_vec2) + + def test_wasm_valtype_vec_copy_null_src(self): + dst = wasm_valtype_vec_t() + wasm_valtype_vec_copy(dst, create_null_pointer(wasm_valtype_vec_t)) + self.assertIsNotNullPointer(c.pointer(dst)) + self.assertIsNullPointer(dst.data) + + def test_wasm_valtype_vec_copy_null_dst(self): + src = wasm_valtype_vec_t() + wasm_valtype_vec_new_empty(src) + wasm_valtype_vec_copy(create_null_pointer(wasm_valtype_vec_t), src) + wasm_valtype_vec_delete(src) + + def test_wasm_valtype_vec_delete_pos(self): + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new_uninitialized(vt_vec, 10) + wasm_valtype_vec_delete(vt_vec) + + vt_vec = wasm_valtype_vec_t() + wasm_valtype_vec_new_empty(vt_vec) + wasm_valtype_vec_delete(vt_vec) + + def test_wasm_valtype_vec_delete_neg(self): + wasm_valtype_vec_delete(create_null_pointer(wasm_valtype_vec_t)) + + def test_wasm_functype_new_0_0(self): + ft = wasm_functype_new_0_0() + + self.assertIsNotNullPointer(ft) + self.assertEqual(0, dereference(wasm_functype_params(ft)).size) + self.assertEqual(0, dereference(wasm_functype_results(ft)).size) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_1_0(self): + vt = wasm_valtype_new(WASM_I64) + ft = wasm_functype_new_1_0(vt) + + self.assertIsNotNullPointer(ft) + params = wasm_vec_to_list(wasm_functype_params(ft)) + self.assertEqual([dereference(p) for p in params], [dereference(vt)]) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_2_0(self): + vt1 = wasm_valtype_new(WASM_I64) + vt2 = wasm_valtype_new(WASM_F64) + ft = wasm_functype_new_2_0(vt1, vt2) + + self.assertIsNotNullPointer(ft) + self.assertEqual(2, dereference(wasm_functype_params(ft)).size) + self.assertEqual(0, dereference(wasm_functype_results(ft)).size) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_3_0(self): + vt_list = [ + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_I64), + ] + ft = wasm_functype_new_3_0(*vt_list) + + params = wasm_vec_to_list(wasm_functype_params(ft)) + self.assertEqual( + [dereference(p) for p in params], + [dereference(vt) for vt in vt_list], + ) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_0_1(self): + vt1 = wasm_valtype_new(WASM_I64) + ft = wasm_functype_new_0_1(vt1) + + self.assertIsNotNullPointer(ft) + self.assertEqual(0, dereference(wasm_functype_params(ft)).size) + self.assertEqual(1, dereference(wasm_functype_results(ft)).size) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_1_1(self): + vt1 = wasm_valtype_new(WASM_I64) + vt2 = wasm_valtype_new(WASM_F64) + ft = wasm_functype_new_1_1(vt1, vt2) + + params = wasm_vec_to_list(wasm_functype_params(ft)) + self.assertEqual(dereference(params[0]), dereference(vt1)) + + results = wasm_vec_to_list(wasm_functype_results(ft)) + self.assertEqual(dereference(results[0]), dereference(vt2)) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_2_1(self): + vt_list = [ + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_I64), + ] + ft = wasm_functype_new_2_1(*vt_list) + + self.assertIsNotNullPointer(ft) + self.assertEqual(2, dereference(wasm_functype_params(ft)).size) + self.assertEqual(1, dereference(wasm_functype_results(ft)).size) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_3_1(self): + vt_list = [ + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_I32), + ] + ft = wasm_functype_new_3_1(*vt_list) + + params = wasm_vec_to_list(wasm_functype_params(ft)) + self.assertEqual( + [dereference(p) for p in params], [dereference(vt) for vt in vt_list[:3]] + ) + + results = wasm_vec_to_list(wasm_functype_results(ft)) + self.assertEqual(dereference(results[0]), dereference(vt_list[-1])) + + wasm_functype_delete(ft) + + def test_wasm_functype_new_neg(self): + ft = wasm_functype_new( + create_null_pointer(wasm_valtype_vec_t), + create_null_pointer(wasm_valtype_vec_t), + ) + + self.assertIsNotNullPointer(ft) + + wasm_functype_delete(ft) + + def test_wasm_functype_delete_pos(self): + ft = wasm_functype_new_0_0() + wasm_functype_delete(ft) + + def test_wasm_functype_delete_neg(self): + wasm_functype_delete(create_null_pointer(wasm_functype_t)) + + def test_wasm_functype_params_pos(self): + vt_list = [ + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_I64), + ] + ft = wasm_functype_new_3_0(*vt_list) + params = wasm_vec_to_list(wasm_functype_params(ft)) + + self.assertEqual( + [dereference(p) for p in params], + [dereference(vt) for vt in vt_list], + ) + + wasm_functype_delete(ft) + + def test_wasm_functype_params_neg(self): + params = wasm_functype_params(create_null_pointer(wasm_functype_t)) + self.assertIsNullPointer(params) + + def test_wasm_functype_results_pos(self): + vt1 = wasm_valtype_new(WASM_I64) + ft = wasm_functype_new_0_1(vt1) + results = wasm_vec_to_list(wasm_functype_results(ft)) + + self.assertEqual(dereference(results[0]), dereference(vt1)) + + wasm_functype_delete(ft) + + def test_wasm_functype_results_neg(self): + results = wasm_functype_results(create_null_pointer(wasm_functype_t)) + self.assertIsNullPointer(results) + + def test_wasm_functype_copy_pos(self): + ft1 = wasm_functype_new_2_1( + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_F64), + wasm_valtype_new(WASM_I64), + ) + ft2 = wasm_functype_copy(ft1) + + self.assertIsNotNullPointer(ft2) + self.assertEqual(2, dereference(wasm_functype_params(ft1)).size) + self.assertEqual(1, dereference(wasm_functype_results(ft2)).size) + + wasm_functype_delete(ft1) + wasm_functype_delete(ft2) + + def test_wasm_functype_copy_neg(self): + ft2 = wasm_functype_copy(create_null_pointer(wasm_functype_t)) + self.assertIsNullPointer(ft2) + wasm_functype_delete(ft2) + + def test_wasm_globaltype_new_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + gt = wasm_globaltype_new(vt, True) + + self.assertIsNotNullPointer(gt) + + wasm_globaltype_delete(gt) + + def test_wasm_globaltype_new_neg(self): + gt = wasm_globaltype_new(create_null_pointer(wasm_valtype_t), True) + self.assertIsNullPointer(gt) + wasm_globaltype_delete(gt) + + def test_wasm_globaltype_delete_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + gt = wasm_globaltype_new(vt, False) + wasm_globaltype_delete(gt) + + def test_wasm_globaltype_delete_neg(self): + wasm_globaltype_delete(create_null_pointer(wasm_globaltype_t)) + + def test_wasm_globaltype_content_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + gt = wasm_globaltype_new(vt, True) + gt_ret = wasm_globaltype_content(gt) + + self.assertEqual(dereference(vt), dereference(gt_ret)) + + wasm_globaltype_delete(gt) + + def test_wasm_globaltype_content_neg(self): + gt_ret = wasm_globaltype_content(create_null_pointer(wasm_globaltype_t)) + self.assertIsNullPointer(gt_ret) + + def test_wasm_globaltype_mutability_pos(self): + vt1 = wasm_valtype_new(WASM_F32) + gt1 = wasm_globaltype_new(vt1, False) + vt2 = wasm_valtype_new(WASM_F32) + gt2 = wasm_globaltype_new(vt2, True) + + self.assertFalse(wasm_globaltype_mutability(gt1)) + self.assertTrue(wasm_globaltype_mutability(gt2)) + + wasm_globaltype_delete(gt1) + wasm_globaltype_delete(gt2) + + def test_wasm_globaltype_mutability_neg(self): + self.assertFalse( + wasm_globaltype_mutability(create_null_pointer(wasm_globaltype_t)) + ) + + def test_wasm_globaltype_copy_pos(self): + vt = wasm_valtype_new(WASM_I32) + gt1 = wasm_globaltype_new(vt, True) + gt2 = wasm_globaltype_copy(gt1) + + self.assertEqual(dereference(gt1), dereference(gt2)) + + wasm_globaltype_delete(gt1) + wasm_globaltype_delete(gt2) + + def test_wasm_globaltype_copy_neg(self): + gt2 = wasm_globaltype_copy(create_null_pointer(wasm_globaltype_t)) + + self.assertIsNullPointer(gt2) + wasm_globaltype_delete(gt2) + + def test_wasm_limit_new(self): + limit = wasm_limits_new(10, 20) + self.assertIsNotNullPointer(limit) + self.assertEqual(dereference(limit).min, 10) + self.assertEqual(dereference(limit).max, 20) + + def test_wasm_tabletype_new_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limit = wasm_limits_new(0, 0xFF) + tt = wasm_tabletype_new(vt, limit) + + self.assertIsNotNullPointer(tt) + wasm_tabletype_delete(tt) + + def test_wasm_tabletype_new_null_val_type(self): + limit = wasm_limits_new(0, 0xFFFFFFFF) + tt = wasm_tabletype_new(create_null_pointer(wasm_valtype_t), limit) + + self.assertIsNullPointer(tt) + wasm_tabletype_delete(tt) + + def test_wasm_tabletype_new_null_limits(self): + vt = wasm_valtype_new(WASM_FUNCREF) + tt = wasm_tabletype_new(vt, create_null_pointer(wasm_limits_t)) + + self.assertIsNullPointer(tt) + wasm_tabletype_delete(tt) + + def test_wasm_tabletype_delete_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limit = wasm_limits_new(0, 0xFFFFFFFF) + tt = wasm_tabletype_new(vt, limit) + wasm_tabletype_delete(tt) + + def test_wasm_tabletype_delete_neg(self): + wasm_tabletype_delete(create_null_pointer(wasm_tabletype_t)) + + def test_wasm_tabletype_element_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limit = wasm_limits_new(0, 0xFFFFFFFF) + tt = wasm_tabletype_new(vt, limit) + vt_ret = wasm_tabletype_element(tt) + + self.assertEqual(dereference(vt), dereference(vt_ret)) + + wasm_tabletype_delete(tt) + + def test_wasm_tabletype_element_neg(self): + vt_ret = wasm_tabletype_element(create_null_pointer(wasm_tabletype_t)) + self.assertIsNullPointer(vt_ret) + + def test_wasm_tabletype_limits_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limit = wasm_limits_new(100, 256) + tt = wasm_tabletype_new(vt, limit) + limit_ret = wasm_tabletype_limits(tt) + + self.assertEqual(dereference(limit), dereference(limit_ret)) + + wasm_tabletype_delete(tt) + + def test_wasm_tabletype_limits_neg(self): + limit_ret = wasm_tabletype_limits(create_null_pointer(wasm_tabletype_t)) + self.assertIsNullPointer(limit_ret) + + def test_wasm_tabletype_copy_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limit = wasm_limits_new(13, 19) + tt1 = wasm_tabletype_new(vt, limit) + tt2 = wasm_tabletype_copy(tt1) + + self.assertEqual(dereference(tt1), dereference(tt2)) + + wasm_tabletype_delete(tt1) + wasm_tabletype_delete(tt2) + + def test_wasm_tabletype_copy_neg(self): + tt2 = wasm_tabletype_copy(create_null_pointer(wasm_tabletype_t)) + self.assertIsNullPointer(tt2) + wasm_tabletype_delete(tt2) + + def test_wasm_memorytype_new_pos(self): + limit = wasm_limits_new(0, 3) + mt = wasm_memorytype_new(limit) + + self.assertIsNotNullPointer(mt) + + wasm_memorytype_delete(mt) + + def test_wasm_memorytype_new_neg(self): + mt = wasm_memorytype_new(None) + + self.assertIsNullPointer(mt) + + wasm_memorytype_delete(mt) + + def test_wasm_memorytype_delete_pos(self): + limit = wasm_limits_new(1, 2) + mt = wasm_memorytype_new(limit) + wasm_memorytype_delete(mt) + + def test_wasm_memorytype_delete_neg(self): + wasm_memorytype_delete(create_null_pointer(wasm_memorytype_t)) + + def test_wasm_memorytype_limits_pos(self): + limit = wasm_limits_new(3, 8) + mt = wasm_memorytype_new(limit) + limit_ret = wasm_memorytype_limits(mt) + + self.assertEqual(dereference(limit), dereference(limit_ret)) + + wasm_memorytype_delete(mt) + + def test_wasm_memorytype_limits_neg(self): + wasm_memorytype_limits(create_null_pointer(wasm_memorytype_t)) + + def test_wasm_memorytype_copy_pos(self): + limit = wasm_limits_new(7, 13) + mt1 = wasm_memorytype_new(limit) + mt2 = wasm_memorytype_copy(mt1) + + self.assertEqual( + dereference(mt1), + dereference(mt2), + ) + + wasm_memorytype_delete(mt1) + wasm_memorytype_delete(mt2) + + def test_wasm_memorytype_copy_neg(self): + mt2 = wasm_memorytype_copy(create_null_pointer(wasm_memorytype_t)) + + self.assertIsNullPointer(mt2) + + wasm_memorytype_delete(mt2) + + def test_wasm_externtype_kind_pos(self): + ft = wasm_functype_new_0_0() + gt = wasm_globaltype_new(wasm_valtype_new(WASM_FUNCREF), True) + mt = wasm_memorytype_new(wasm_limits_new(1, 2)) + tt = wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), wasm_limits_new(10, 20)) + ets = [ + wasm_functype_as_externtype(ft), + wasm_globaltype_as_externtype(gt), + wasm_memorytype_as_externtype(mt), + wasm_tabletype_as_externtype(tt), + ] + type_kinds = [wasm_externtype_kind(et) for et in ets] + + self.assertEqual( + type_kinds, + [ + WASM_EXTERN_FUNC, + WASM_EXTERN_GLOBAL, + WASM_EXTERN_MEMORY, + WASM_EXTERN_TABLE, + ], + ) + + [wasm_externtype_delete(et) for et in ets] + + def test_wasm_externtype_kind_neg(self): + et = wasm_memorytype_as_externtype(create_null_pointer(wasm_memorytype_t)) + self.assertIsNullPointer(et) + + def test_wasm_externtype_delete_pos(self): + mt = wasm_memorytype_new(wasm_limits_new(10, 20)) + et = wasm_memorytype_as_externtype(mt) + wasm_externtype_delete(et) + + def test_wasm_externtype_delete_neg(self): + et = wasm_globaltype_as_externtype(create_null_pointer(wasm_globaltype_t)) + wasm_externtype_delete(et) + + def test_wasm_externtype_copy_pos(self): + tt1 = wasm_tabletype_new( + wasm_valtype_new(WASM_FUNCREF), wasm_limits_new(10, 20) + ) + et1 = wasm_tabletype_as_externtype(tt1) + et2 = wasm_externtype_copy(et1) + + tt2 = wasm_externtype_as_tabletype(et2) + self.assertEqual(dereference(tt1), dereference(tt2)) + + wasm_externtype_delete(et2) + wasm_externtype_delete(et1) + + def test_wasm_externtype_copy_neg(self): + et1 = create_null_pointer(wasm_externtype_t) + et2 = wasm_externtype_copy(et1) + wasm_externtype_delete(et2) + wasm_externtype_delete(et1) + + def test_wasm_name_new_from_string(self): + s = "let the stars shine upon you" + name = wasm_name_new_from_string(s) + + name_data = c.cast(name.data, c.c_char_p) + name_data = bytes.decode(name_data.value) + self.assertEqual(name_data, s) + + def test_wasm_importtype_new_pos(self): + module_name = "mA" + field_name = "func#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + ft = wasm_functype_new_0_0() + et = wasm_functype_as_externtype(ft) + it = wasm_importtype_new(module_name, field_name, et) + + self.assertIsNotNullPointer(it) + self.assertEqual(dereference(wasm_importtype_module(it)), module_name) + self.assertEqual(dereference(wasm_importtype_name(it)), field_name) + self.assertEqual(dereference(wasm_importtype_type(it)), dereference(et)) + + wasm_importtype_delete(it) + + def test_wasm_importtype_new_null_ext_type(self): + module_name = "mA" + field_name = "func#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + it = wasm_importtype_new( + module_name, + field_name, + create_null_pointer(wasm_externtype_t), + ) + + self.assertIsNullPointer(it) + + wasm_importtype_delete(it) + + def test_wasm_importtype_new_null_module(self): + field_name = "func#1" + field_name = wasm_name_new_from_string(field_name) + ft = wasm_functype_new_0_0() + et = wasm_functype_as_externtype(ft) + it = wasm_importtype_new(create_null_pointer(wasm_name_t), field_name, et) + + self.assertIsNullPointer(it) + + wasm_importtype_delete(it) + + def test_wasm_importtype_new_null_field(self): + module_name = "mA" + module_name = wasm_name_new_from_string(module_name) + ft = wasm_functype_new_0_0() + et = wasm_functype_as_externtype(ft) + it = wasm_importtype_new(module_name, create_null_pointer(wasm_name_t), et) + + self.assertIsNullPointer(it) + + wasm_importtype_delete(it) + + def test_wasm_importtype_copy_pos(self): + module_name = "mA" + field_name = "memory#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + mt = wasm_memorytype_new(wasm_limits_new(10, 20)) + et = wasm_memorytype_as_externtype(mt) + it1 = wasm_importtype_new(module_name, field_name, et) + it2 = wasm_importtype_copy(it1) + + self.assertEqual(dereference(it1), dereference(it2)) + + wasm_importtype_delete(it1) + wasm_importtype_delete(it2) + + def test_wasm_importtype_copy_neg(self): + it1 = create_null_pointer(wasm_importtype_t) + it2 = wasm_importtype_copy(it1) + wasm_importtype_delete(it1) + wasm_importtype_delete(it2) + + def test_wasm_importtype_delete_pos(self): + module_name = "mA" + field_name = "memory#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + tt = wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), wasm_limits_new(10, 20)) + et = wasm_tabletype_as_externtype(tt) + it = wasm_importtype_new(module_name, field_name, et) + wasm_importtype_delete(it) + + def test_wasm_importtype_delete_neg(self): + wasm_importtype_delete(create_null_pointer(wasm_importtype_t)) + + def test_wasm_importtype_module_pos(self): + module_name = "mA" + field_name = "func#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + ft = wasm_functype_new_0_0() + et = wasm_functype_as_externtype(ft) + it = wasm_importtype_new(module_name, field_name, et) + module_name_ret = wasm_importtype_module(it) + + self.assertEqual(dereference(module_name_ret), module_name) + + wasm_importtype_delete(it) + + def test_wasm_importtype_module_neg(self): + it = create_null_pointer(wasm_importtype_t) + wasm_importtype_module(it) + wasm_importtype_delete(it) + + def test_wasm_importtype_name_pos(self): + module_name = "mA" + field_name = "func#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + ft = wasm_functype_new_0_0() + et = wasm_functype_as_externtype(ft) + it = wasm_importtype_new(module_name, field_name, et) + field_name_ret = wasm_importtype_name(it) + + self.assertEqual(dereference(field_name_ret), field_name) + + wasm_importtype_delete(it) + + def test_wasm_importtype_name_neg(self): + it = create_null_pointer(wasm_importtype_t) + wasm_importtype_name(it) + wasm_importtype_delete(it) + + def test_wasm_importtype_type_pos(self): + module_name = "mA" + field_name = "func#1" + module_name = wasm_name_new_from_string(module_name) + field_name = wasm_name_new_from_string(field_name) + ft = wasm_functype_new_0_0() + et = wasm_functype_as_externtype(ft) + it = wasm_importtype_new(module_name, field_name, et) + et_ret = wasm_importtype_type(it) + + self.assertEqual(dereference(et_ret), dereference(et)) + + wasm_importtype_delete(it) + + def test_wasm_importtype_type_neg(self): + it = create_null_pointer(wasm_importtype_t) + wasm_importtype_type(it) + wasm_importtype_delete(it) + + def test_wasm_exporttype_new_pos(self): + name = "hello" + name = wasm_name_new_from_string(name) + ft = wasm_functype_new_0_0() + ft = wasm_functype_as_externtype(ft) + et = wasm_exporttype_new(name, ft) + + self.assertIsNotNullPointer(et) + + wasm_exporttype_delete(et) + + def test_wasm_exporttype_new_null_name(self): + name = create_null_pointer(wasm_name_t) + ft = wasm_functype_new_0_0() + ft = wasm_functype_as_externtype(ft) + et = wasm_exporttype_new(name, ft) + + self.assertIsNullPointer(et) + + wasm_exporttype_delete(et) + + def test_wasm_exporttype_new_null_ext_type(self): + name = "hello" + name = wasm_name_new_from_string(name) + ext_type = create_null_pointer(wasm_externtype_t) + et = wasm_exporttype_new(name, ext_type) + + self.assertIsNullPointer(et) + + wasm_exporttype_delete(et) + + def test_wasm_exporttype_copy_pos(self): + name = "hello" + name = wasm_name_new_from_string(name) + gt = wasm_globaltype_new(wasm_valtype_new(WASM_F32), True) + gt = wasm_globaltype_as_externtype(gt) + et1 = wasm_exporttype_new(name, gt) + et2 = wasm_exporttype_copy(et1) + + self.assertEqual( + dereference(et1), + dereference(et2), + ) + + wasm_exporttype_delete(et1) + wasm_exporttype_delete(et2) + + def test_wasm_exporttype_copy_neg(self): + et1 = create_null_pointer(wasm_exporttype_t) + et2 = wasm_exporttype_copy(et1) + + wasm_exporttype_delete(et1) + wasm_exporttype_delete(et2) + + def test_wasm_exporttype_delete_pos(self): + name = "hello" + name = wasm_name_new_from_string(name) + mt = wasm_memorytype_new(wasm_limits_new(10, 20)) + mt = wasm_memorytype_as_externtype(mt) + et = wasm_exporttype_new(name, mt) + + wasm_exporttype_delete(et) + + def test_wasm_exporttype_delete_neg(self): + et = create_null_pointer(wasm_exporttype_t) + wasm_exporttype_delete(et) + + def test_wasm_exporttype_name_pos(self): + name = "hello" + name = wasm_name_new_from_string(name) + tt = wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), wasm_limits_new(10, 20)) + tt = wasm_tabletype_as_externtype(tt) + et = wasm_exporttype_new(name, tt) + name_ret = wasm_exporttype_name(et) + + self.assertEqual(dereference(name_ret), name) + + wasm_exporttype_delete(et) + + def test_wasm_exporttype_name_neg(self): + et = create_null_pointer(wasm_exporttype_t) + wasm_exporttype_name(et) + wasm_exporttype_delete(et) + + def test_wasm_exporttype_type_pos(self): + name = "hello" + name = wasm_name_new_from_string(name) + tt = wasm_tabletype_new(wasm_valtype_new(WASM_FUNCREF), wasm_limits_new(10, 20)) + tt = wasm_tabletype_as_externtype(tt) + et = wasm_exporttype_new(name, tt) + tt_ret = wasm_exporttype_type(et) + + self.assertEqual(dereference(tt_ret), dereference(tt)) + + wasm_exporttype_delete(et) + + def test_wasm_exporttype_type_neg(self): + et = create_null_pointer(wasm_exporttype_t) + wasm_exporttype_type(et) + wasm_exporttype_delete(et) + + def test_wasm_i32_val(self): + val = wasm_i32_val(100) + + self.assertEqual(val.kind, WASM_I32) + self.assertEqual(val.of.i32, 100) + + # can not use wasm_val_delete() because it is not malloced + + def test_wasm_i64_val(self): + val = wasm_i64_val(-100) + + self.assertEqual(val.kind, WASM_I64) + self.assertEqual(val.of.i64, -100) + + # can not use wasm_val_delete() because it is not malloced + + def test_wasm_f32_val(self): + val = wasm_f32_val(100) + + self.assertEqual(val.kind, WASM_F32) + self.assertEqual(val.of.f32, 100.0) + + # can not use wasm_val_delete() because it is not malloced + + def test_wasm_f64_val(self): + val = wasm_f64_val(-100) + + self.assertEqual(val.kind, WASM_F64) + self.assertEqual(val.of.f64, -100.0) + + # can not use wasm_val_delete() because it is not malloced + + # there is no wasm_val_new() to malloc a wasm_val_t + def test_wasm_val_delete(self): + pass + + def test_wasm_val_copy(self): + v1 = wasm_f32_val(3.14) + v2 = wasm_val_t() + wasm_val_copy(v1, v2) + + self.assertEqual(v1, v2) + # can not use wasm_val_delete() because it is not malloced + + def test_wasm_ref_delete_neg(self): + ref = create_null_pointer(wasm_ref_t) + wasm_ref_delete(ref) + + ref = wasm_ref_t() + wasm_ref_delete(ref) + + def test_wasm_trap_new_pos(self): + # can't create a trap with traces(wasm_frame_vec_t) + msg = wasm_name_new_from_string("a fake trap") + trap = wasm_trap_new(self._wasm_store, msg) + + self.assertIsNotNone(trap) + + wasm_trap_delete(trap) + + def test_wasm_trap_new_null_msg(self): + trap = wasm_trap_new(self._wasm_store, create_null_pointer(wasm_name_t)) + + self.assertIsNotNone(trap) + self.assertIsNotNullPointer(trap) + + wasm_trap_delete(trap) + + def test_wasm_trap_message_pos(self): + msg = wasm_name_new_from_string("a fake trap") + trap = wasm_trap_new(self._wasm_store, msg) + msg_in_trap = wasm_message_t() + wasm_trap_message(trap, msg_in_trap) + + self.assertEqual( + msg, + msg_in_trap, + ) + + wasm_trap_delete(trap) + + def test_wasm_trap_message_null_trap(self): + msg = wasm_name_new_from_string("a fake trap") + wasm_trap_message(create_null_pointer(wasm_trap_t), msg) + + def test_wasm_trap_message_null_out(self): + msg = wasm_name_new_from_string("a fake trap") + trap = wasm_trap_new(self._wasm_store, msg) + wasm_trap_message(trap, create_null_pointer(wasm_message_t)) + wasm_trap_delete(trap) + + # test those APIs in advance: + # wasm_trap_origin + # wasm_trap_trace + # wasm_frame_delete + # wasm_frame_copy + # wasm_frame_module_offset + # wasm_frame_instance + # wasm_frame_func_index + # wasm_frame_func_offset + + def test_wasm_foreign_new_pos(self): + foreign = wasm_foreign_new(self._wasm_store) + + self.assertIsNotNone(foreign) + self.assertIsNotNullPointer(foreign) + + wasm_foreign_delete(foreign) + + def test_wasm_foreign_new_neg(self): + foreign = wasm_foreign_new(create_null_pointer(wasm_store_t)) + + self.assertIsNotNone(foreign) + self.assertIsNullPointer(foreign) + + wasm_foreign_delete(foreign) + + def test_wasm_foreign_delete_pos(self): + foreign = wasm_foreign_new(self._wasm_store) + wasm_foreign_delete(foreign) + + def test_wasm_foreign_delete_neg(self): + wasm_foreign_delete(create_null_pointer(wasm_foreign_t)) + + # wasm_egnine_new()/wasm_engine_delete() + # wasm_store_new()/wasm_store_delete() + # used in setUpClass() and tearDownClass + + def test_wasm_module_new_pos(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + + self.assertIsNotNone(module) + self.assertIsNotNullPointer(module) + + wasm_byte_vec_delete(binary) + wasm_module_delete(module) + + def test_wasm_module_new_neg(self): + module = wasm_module_new(self._wasm_store, create_null_pointer(wasm_byte_vec_t)) + + self.assertIsNotNone(module) + self.assertIsNullPointer(module) + + wasm_module_delete(module) + + def test_wasm_module_delete_pos(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + wasm_byte_vec_delete(binary) + wasm_module_delete(module) + + def test_wasm_module_delete_neg(self): + module = wasm_module_new(self._wasm_store, create_null_pointer(wasm_byte_vec_t)) + wasm_module_delete(module) + + def test_wasm_module_validate_pos(self): + binary = load_module_file(MODULE_BINARY) + validation = wasm_module_validate(self._wasm_store, binary) + + self.assertTrue(validation) + + wasm_byte_vec_delete(binary) + + def test_wasm_module_validate_neg(self): + tmp = (1024).to_bytes(2, byteorder="big") + binary = load_module_file(tmp) + validation = wasm_module_validate(self._wasm_store, binary) + + self.assertFalse(validation) + + wasm_byte_vec_delete(binary) + + def test_wasm_module_imports_pos(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + imports = wasm_importtype_vec_t() + wasm_module_imports(module, imports) + + imports_list = wasm_vec_to_list(imports) + self.assertEqual(len(imports_list), 2) + + func_type = wasm_functype_new_1_1( + wasm_valtype_new(WASM_F32), + wasm_valtype_new(WASM_F64), + ) + ext_type = wasm_functype_as_externtype(func_type) + self.assertEqual( + dereference(wasm_importtype_type(imports_list[0])), dereference(ext_type) + ) + + wasm_externtype_delete(ext_type) + wasm_importtype_vec_delete(imports) + wasm_byte_vec_delete(binary) + wasm_module_delete(module) + + def test_wasm_module_imports_null_module(self): + imports = wasm_importtype_vec_t() + wasm_module_imports(create_null_pointer(wasm_module_t), imports) + + self.assertEqual(imports.size, 0) + + wasm_importtype_vec_delete(imports) + + def test_wasm_module_imports_null_out(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + wasm_module_imports(module, create_null_pointer(wasm_importtype_vec_t)) + wasm_byte_vec_delete(binary) + wasm_module_delete(module) + + def test_wasm_module_exports_pos(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + exports = wasm_exporttype_vec_t() + wasm_module_exports(module, exports) + + exports_list = wasm_vec_to_list(exports) + self.assertEqual(len(exports_list), 2) + + glbl_type = wasm_globaltype_new(wasm_valtype_new(WASM_F32), True) + ext_type = wasm_globaltype_as_externtype(glbl_type) + self.assertEqual( + dereference(wasm_exporttype_type(exports_list[1])), dereference(ext_type) + ) + + wasm_exporttype_vec_delete(exports) + wasm_byte_vec_delete(binary) + wasm_module_delete(module) + + def test_wasm_module_exports_null_module(self): + exports = wasm_exporttype_vec_t() + wasm_module_exports(create_null_pointer(wasm_module_t), exports) + + self.assertEqual(exports.size, 0) + + wasm_exporttype_vec_delete(exports) + + def test_wasm_module_exports_null_out(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + wasm_module_exports(module, create_null_pointer(wasm_exporttype_vec_t)) + wasm_byte_vec_delete(binary) + wasm_module_delete(module) + + def test_wasm_instance_new_pos_empty_imports(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + imports = wasm_extern_vec_t() + wasm_extern_vec_new_empty(imports) + instance = wasm_instance_new( + self._wasm_store, module, imports, create_null_pointer(wasm_trap_t) + ) + + wasm_module_delete(module) + + self.assertIsNullPointer(instance) + + def test_wasm_instance_new_pos(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + + ft = wasm_functype_new_1_1( + wasm_valtype_new(WASM_F32), + wasm_valtype_new(WASM_F64), + ) + func = wasm_func_new(self._wasm_store, ft, callback) + + gt = wasm_globaltype_new(wasm_valtype_new(WASM_I32), True) + init = wasm_i32_val(100) + gb = wasm_global_new(self._wasm_store, gt, init) + + imports = wasm_extern_vec_t() + data = list_to_carray( + c.POINTER(wasm_extern_t), + wasm_func_as_extern(func), + wasm_global_as_extern(gb), + ) + wasm_extern_vec_new(imports, 2, data) + + instance = wasm_instance_new( + self._wasm_store, module, imports, create_null_pointer(wasm_trap_t) + ) + + self.assertIsNotNone(instance) + + wasm_instance_delete(instance) + wasm_module_delete(module) + + def test_wasm_instance_new_neg_null_imports(self): + binary = load_module_file(MODULE_BINARY) + module = wasm_module_new(self._wasm_store, binary) + instance = wasm_instance_new( + self._wasm_store, + module, + create_null_pointer(wasm_extern_vec_t), + create_null_pointer(wasm_trap_t), + ) + + wasm_module_delete(module) + + self.assertIsNullPointer(instance) + + # test those APIs in advanced: + # wasm_instance_delete + # wasm_instance_exports + + def test_wasm_func_new_pos(self): + vt1 = wasm_valtype_new(WASM_F32) + vt2 = wasm_valtype_new(WASM_FUNCREF) + ft = wasm_functype_new_1_1(vt1, vt2) + func = wasm_func_new(self._wasm_store, ft, callback) + + self.assertIsNotNone(func) + self.assertIsNotNullPointer(func) + + wasm_func_delete(func) + + def test_wasm_func_new_null_type(self): + func = wasm_func_new( + self._wasm_store, create_null_pointer(wasm_functype_t), callback + ) + + self.assertIsNotNone(func) + self.assertIsNullPointer(func) + + wasm_func_delete(func) + + def test_wasm_func_new_null_callback(self): + vt1 = wasm_valtype_new(WASM_F32) + vt2 = wasm_valtype_new(WASM_FUNCREF) + ft = wasm_functype_new_1_1(vt1, vt2) + func = wasm_func_new(self._wasm_store, ft, wasm_func_callback_t()) + + self.assertIsNotNone(func) + self.assertIsNullPointer(func) + + wasm_func_delete(func) + + def test_wasm_func_new_with_env_pos(self): + ft = wasm_functype_new_3_1( + wasm_valtype_new(WASM_I32), + wasm_valtype_new(WASM_F32), + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_I64), + ) + func = wasm_func_new_with_env( + self._wasm_store, + ft, + callback_with_env, + c.c_void_p(0), + wasm_finalizer(0), + ) + + self.assertIsNotNone(func) + self.assertIsNotNullPointer(func) + + wasm_func_delete(func) + + def test_wasm_func_new_with_env_null_type(self): + func = wasm_func_new_with_env( + self._wasm_store, + create_null_pointer(wasm_functype_t), + callback_with_env, + c.c_void_p(0), + wasm_finalizer(0), + ) + + self.assertIsNotNone(func) + self.assertIsNullPointer(func) + + wasm_func_delete(func) + + def test_wasm_func_new_with_env_null_callback(self): + ft = wasm_functype_new_3_1( + wasm_valtype_new(WASM_I32), + wasm_valtype_new(WASM_F32), + wasm_valtype_new(WASM_I64), + wasm_valtype_new(WASM_I64), + ) + func = wasm_func_new_with_env( + self._wasm_store, + ft, + wasm_func_callback_with_env_t(), + c.c_void_p(0), + wasm_finalizer(0), + ) + + self.assertIsNotNone(func) + self.assertIsNullPointer(func) + + wasm_func_delete(func) + + def test_wasm_func_delete_pos(self): + ft = wasm_functype_new_0_0() + func = wasm_func_new(self._wasm_store, ft, callback) + wasm_func_delete(func) + + def test_wasm_func_delete_neg(self): + wasm_func_delete(create_null_pointer(wasm_func_t)) + + def test_wasm_func_type_pos(self): + ft = wasm_functype_new_2_0( + wasm_valtype_new(WASM_F32), + wasm_valtype_new(WASM_FUNCREF), + ) + func = wasm_func_new(self._wasm_store, ft, callback) + ft_ret = wasm_func_type(func) + + self.assertEqual( + dereference(ft), + dereference(ft_ret), + ) + + wasm_functype_delete(ft_ret) + wasm_func_delete(func) + + def test_wasm_func_type_neg(self): + ft_ret = wasm_func_type(create_null_pointer(wasm_func_t)) + wasm_functype_delete(ft_ret) + + def test_wasm_func_copy_pos(self): + vt1 = wasm_valtype_new(WASM_F32) + ft = wasm_functype_new_0_1(vt1) + func1 = wasm_func_new(self._wasm_store, ft, callback) + func2 = wasm_func_copy(func1) + + self.assertEqual( + dereference(wasm_func_type(func1)), dereference(wasm_func_type(func2)) + ) + + wasm_func_delete(func2) + wasm_func_delete(func1) + + def test_wasm_func_copy_neg(self): + func1 = wasm_func_new( + self._wasm_store, create_null_pointer(wasm_functype_t), callback + ) + func2 = wasm_func_copy(func1) + + wasm_func_delete(func2) + wasm_func_delete(func1) + + # test wasm_func_call in advanced + + def test_wasm_global_new_pos(self): + vt = wasm_valtype_new(WASM_F32) + gt = wasm_globaltype_new(vt, False) + v = wasm_f32_val(3.14) + g = wasm_global_new(self._wasm_store, gt, v) + + self.assertIsNotNone(g) + self.assertIsNotNullPointer(g) + + wasm_globaltype_delete(gt) + wasm_global_delete(g) + + def test_wasm_global_new_null_type(self): + v = wasm_f32_val(3.14) + g = wasm_global_new(self._wasm_store, create_null_pointer(wasm_globaltype_t), v) + + self.assertIsNotNone(g) + self.assertIsNullPointer(g) + + wasm_global_delete(g) + + def test_wasm_global_new_null_init(self): + vt = wasm_valtype_new(WASM_F32) + gt = wasm_globaltype_new(vt, False) + g = wasm_global_new(self._wasm_store, gt, create_null_pointer(wasm_val_t)) + + self.assertIsNotNone(g) + self.assertIsNullPointer(g) + + wasm_globaltype_delete(gt) + wasm_global_delete(g) + + def test_wasm_global_delete_pos(self): + vt = wasm_valtype_new(WASM_I32) + gt = wasm_globaltype_new(vt, True) + v = wasm_i32_val(3) + g = wasm_global_new(self._wasm_store, gt, v) + wasm_globaltype_delete(gt) + wasm_global_delete(g) + + def test_wasm_global_delete_neg(self): + wasm_global_delete(create_null_pointer(wasm_global_t)) + + def test_wasm_global_type_pos(self): + vt = wasm_valtype_new(WASM_I64) + gt = wasm_globaltype_new(vt, False) + v = wasm_i32_val(3) + g = wasm_global_new(self._wasm_store, gt, v) + gt_ret = wasm_global_type(g) + + self.assertEqual(dereference(gt), dereference(gt_ret)) + + wasm_globaltype_delete(gt) + wasm_globaltype_delete(gt_ret) + wasm_global_delete(g) + + def test_wasm_global_type_neg(self): + gt = wasm_global_type(create_null_pointer(wasm_global_t)) + wasm_globaltype_delete(gt) + + # test wasm_global_get and wasm_global_set in advanced + + def test_wasm_table_new_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limits = wasm_limits_new(10, 15) + tt = wasm_tabletype_new(vt, limits) + t = wasm_table_new(self._wasm_store, tt, create_null_pointer(wasm_ref_t)) + + self.assertIsNotNone(t) + self.assertIsNotNullPointer(t) + + wasm_table_delete(t) + + def test_wasm_table_new_null_type(self): + t = wasm_table_new( + self._wasm_store, + create_null_pointer(wasm_tabletype_t), + create_null_pointer(wasm_ref_t), + ) + + self.assertIsNotNone(t) + self.assertIsNullPointer(t) + + wasm_table_delete(t) + + def test_wasm_table_delete_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limits = wasm_limits_new(10, 15) + tt = wasm_tabletype_new(vt, limits) + t = wasm_table_new(self._wasm_store, tt, create_null_pointer(wasm_ref_t)) + wasm_table_delete(t) + + def test_wasm_table_delete_neg(self): + wasm_table_delete(create_null_pointer(wasm_table_t)) + + def test_wasm_table_type_pos(self): + vt = wasm_valtype_new(WASM_FUNCREF) + limits = wasm_limits_new(1, 2) + tt = wasm_tabletype_new(vt, limits) + t = wasm_table_new(self._wasm_store, tt, create_null_pointer(wasm_ref_t)) + tt_ret = wasm_table_type(t) + + self.assertEqual( + dereference(tt), + dereference(tt_ret), + ) + + wasm_table_delete(t) + + def test_wasm_table_type_neg(self): + t = wasm_table_new( + self._wasm_store, + create_null_pointer(wasm_tabletype_t), + create_null_pointer(wasm_ref_t), + ) + tt_ret = wasm_table_type(t) + wasm_table_delete(t) + + # test wasm_table_size, wasm_table_get, wasm_table_set in advanced + + def test_wasm_memory_new_pos(self): + limits = wasm_limits_new(10, 12) + mt = wasm_memorytype_new(limits) + m = wasm_memory_new(self._wasm_store, mt) + + self.assertIsNotNullPointer(m) + + wasm_memory_delete(m) + + def test_wasm_memory_new_null_type(self): + m = wasm_memory_new(self._wasm_store, create_null_pointer(wasm_memorytype_t)) + + self.assertIsNullPointer(m) + + wasm_memory_delete(m) + + def test_wasm_memory_delete_pos(self): + limits = wasm_limits_new(10, 21) + mt = wasm_memorytype_new(limits) + m = wasm_memory_new(self._wasm_store, mt) + wasm_memory_delete(m) + + def test_wasm_memory_delete_neg(self): + wasm_memory_delete(create_null_pointer(wasm_memory_t)) + + def test_wasm_memory_type_pos(self): + limits = wasm_limits_new(10, 21) + mt = wasm_memorytype_new(limits) + m = wasm_memory_new(self._wasm_store, mt) + mt_ret = wasm_memory_type(m) + + self.assertEqual(dereference(mt), dereference(mt_ret)) + + wasm_memory_delete(m) + + def test_wasm_memory_type_neg(self): + mt = wasm_memory_type(create_null_pointer(wasm_memory_t)) + + self.assertIsNullPointer(mt) + wasm_memorytype_delete(mt) + + # test wasm_memory_size, wasm_memory_data, wasm_memory_data_size in advanced + + def test_wasm_extern_delete_pos(self): + vt = wasm_valtype_new(WASM_I64) + gt = wasm_globaltype_new(vt, False) + v = wasm_i64_val(128) + glb = wasm_global_new(self._wasm_store, gt, v) + etrn = wasm_global_as_extern(glb) + wasm_extern_delete(etrn) + + def test_wasm_extern_delete_neg(self): + etrn = wasm_global_as_extern(create_null_pointer(wasm_global_t)) + wasm_extern_delete(etrn) + + def test_wasm_extern_type_pos(self): + vt = wasm_valtype_new(WASM_I64) + gt = wasm_globaltype_new(vt, False) + v = wasm_i64_val(128) + glb = wasm_global_new(self._wasm_store, gt, v) + etrn = wasm_global_as_extern(glb) + + tp = wasm_extern_type(etrn) + gt_ret = wasm_externtype_as_globaltype(tp) + self.assertEqual( + dereference(gt), + dereference(gt_ret), + ) + wasm_extern_delete(etrn) + + def test_wasm_extern_type_neg(self): + wasm_extern_type(create_null_pointer(wasm_extern_t)) + + def test_wasm_extern_kind_pos(self): + ft = wasm_functype_new_0_0() + func = wasm_func_new(self._wasm_store, ft, callback) + etrn = wasm_func_as_extern(func) + kind = wasm_extern_kind(etrn) + + self.assertEqual(WASM_EXTERN_FUNC, kind) + + wasm_extern_delete(etrn) + + def test_wasm_extern_kind_neg(self): + wasm_extern_kind(create_null_pointer(wasm_extern_t)) + + @classmethod + def tearDownClass(cls): + wasm_store_delete(cls._wasm_store) + wasm_engine_delete(cls._wasm_engine) + + +if __name__ == "__main__": + unittest.main() diff --git a/language-bindings/python/wasm-c-api/utils/bindgen.py b/language-bindings/python/wasm-c-api/utils/bindgen.py new file mode 100644 index 0000000000..a505404d58 --- /dev/null +++ b/language-bindings/python/wasm-c-api/utils/bindgen.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# pylint: disable=missing-class-docstring +# pylint: disable=missing-function-docstring +# pylint: disable=missing-module-docstring + +""" +- Need to run *download_wamr.py* firstly. +- Parse *./wasm-micro-runtime/core/iwasm/include/wasm_c_api.h* and generate + *wamr/binding.py* +""" +import os +import pathlib +import shutil +import sys + +from pycparser import c_ast, parse_file + +WASM_C_API_HEADER = "core/iwasm/include/wasm_c_api.h" +BINDING_PATH = "language-bindings/python/wamr/wasmcapi/binding.py" +# 4 spaces as default indent +INDENT = " " + +IGNORE_SYMOLS = ( + "wasm_engine_new_with_args", + "wasm_valkind_is_num", + "wasm_valkind_is_ref", + "wasm_valtype_is_num", + "wasm_valtype_is_ref", + "wasm_valtype_new_i32", + "wasm_valtype_new_i64", + "wasm_valtype_new_f32", + "wasm_valtype_new_f64", + "wasm_valtype_new_anyref", + "wasm_valtype_new_funcref", + "wasm_functype_new_0_0", + "wasm_functype_new_0_0", + "wasm_functype_new_1_0", + "wasm_functype_new_2_0", + "wasm_functype_new_3_0", + "wasm_functype_new_0_1", + "wasm_functype_new_1_1", + "wasm_functype_new_2_1", + "wasm_functype_new_3_1", + "wasm_functype_new_0_2", + "wasm_functype_new_1_2", + "wasm_functype_new_2_2", + "wasm_functype_new_3_2", + "wasm_val_init_ptr", + "wasm_val_ptr", + "wasm_val_t", + "wasm_ref_t", + "wasm_name_new_from_string", + "wasm_name_new_from_string_nt", +) + + +class Visitor(c_ast.NodeVisitor): + def __init__(self): + self.type_map = { + "_Bool": "c_bool", + "byte_t": "c_ubyte", + "char": "c_char", + "errno_t": "c_int", + "int": "c_int", + "long": "c_long", + "size_t": "c_size_t", + "uint32_t": "c_uint32", + "uint8_t": "c_uint8", + "void": "None", + } + self.ret = ( + "# -*- coding: utf-8 -*-\n" + "#!/usr/bin/env python3\n" + "#\n" + "# Copyright (C) 2019 Intel Corporation. All rights reserved.\n" + "# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" + "#\n" + "#It is a generated file. DO NOT EDIT.\n" + "#\n" + "from ctypes import *\n" + "\n" + "from .ffi import dereference, libiwasm, wasm_ref_t, wasm_val_t\n" + "\n" + "\n" + ) + + def get_type_name(self, c_type): + if isinstance(c_type, c_ast.TypeDecl): + return self.get_type_name(c_type.type) + elif isinstance(c_type, c_ast.PtrDecl): + pointed_type = self.get_type_name(c_type.type) + + if isinstance(c_type.type, c_ast.FuncDecl): + # CFUCNTYPE is a pointer of function + return pointed_type + + if "None" == pointed_type: + return "c_void_p" + + return f"POINTER({pointed_type})" + + elif isinstance(c_type, c_ast.ArrayDecl): + return f"POINTER({self.get_type_name(c_type.type)})" + elif isinstance(c_type, c_ast.IdentifierType): + if len(c_type.names) > 1: + raise RuntimeError(f"unexpected type with a long names: {c_type}") + + type_name = c_type.names[0] + + if type_name.startswith("wasm_"): + return type_name + + if not type_name in self.type_map: + raise RuntimeError(f"a new type should be in type_map: {type_name}") + + return self.type_map.get(type_name) + elif isinstance(c_type, c_ast.Union): + if not c_type.name: + raise RuntimeError(f"found an anonymous union {c_type}") + + return c_type.name + elif isinstance(c_type, c_ast.Struct): + if not c_type.name: + raise RuntimeError(f"found an anonymous union {c_type}") + + return c_type.name + elif isinstance(c_type, c_ast.FuncDecl): + content = "CFUNCTYPE(" + if isinstance(c_type.type, c_ast.PtrDecl): + # there is a bug in CFUNCTYPE if the result type is a pointer + content += "c_void_p" + else: + content += f"{self.get_type_name(c_type.type)}" + content += f",{self.get_type_name(c_type.args)}" if c_type.args else "" + content += ")" + return content + elif isinstance(c_type, c_ast.Decl): + return self.get_type_name(c_type.type) + elif isinstance(c_type, c_ast.ParamList): + content = ",".join( + [self.get_type_name(param.type) for param in c_type.params] + ) + return content + else: + raise RuntimeError(f"unexpected type: {c_type.show()}") + + def visit_Struct(self, node): + # pylint: disable=invalid-name + def gen_fields(info, indent): + content = "" + for k, v in info.items(): + content += f'{indent}("{k}", {v}),\n' + return content[:-1] + + def gen_equal(info, indent): + content = f"{indent}return" + for k, v in info.items(): + # not compare pointer value in __eq__ + if v.startswith("POINTER") or v.startswith("c_void_p"): + continue + + content += f" self.{k} == other.{k} and" + return content[:-4] + + def gen_repr(info, indent): + content = f'{indent}return f"{{{{' + for k, _ in info.items(): + content += f"{k}={{self.{k}}}, " + content = content[:-2] + '}}"' + return content + + def gen_vector_repr(info, indent): + content = f'{indent}ret = ""\n' + content += f"{indent}for i in range(self.num_elems):\n" + + if 1 == info["data"].count("POINTER"): + # pointer + content += f"{2*indent}ret += str(self.data[i])\n" + else: + # pointer of pointer + content += f"{2*indent}ret += str(dereference(self.data[i]))\n" + + content += f'{2*indent}ret += " "\n' + content += f"{indent}return ret\n" + return content + + if not node.name or not node.name.lower().startswith("wasm"): + return + + if node.name in IGNORE_SYMOLS: + return + + name = node.name + + info = {} + if node.decls: + for decl in node.decls: + info[decl.name] = self.get_type_name(decl.type) + + if info: + self.ret += ( + f"class {name}(Structure):\n" + f"{INDENT}_fields_ = [\n" + f"{gen_fields(info, INDENT*2)}\n" + f"{INDENT}]\n" + f"\n" + f"{INDENT}def __eq__(self, other):\n" + f"{INDENT*2}if not isinstance(other, {name}):\n" + f"{INDENT*3}return False\n" + f"{gen_equal(info, INDENT*2)}\n" + f"\n" + f"{INDENT}def __repr__(self):\n" + ) + self.ret += ( + f"{gen_vector_repr(info, INDENT*2)}\n" + if name.endswith("_vec_t") + else f"{gen_repr(info, INDENT*2)}\n" + ) + self.ret += "\n" + + else: + self.ret += f"class {name}(Structure):\n{INDENT}pass\n" + + self.ret += "\n" + + def visit_Union(self, node): + # pylint: disable=invalid-name + print(f"Union: {node.show()}") + + def visit_Typedef(self, node): + # pylint: disable=invalid-name + # system defined + if not node.name: + return + + if not node.name.startswith("wasm_"): + return + + if node.name in IGNORE_SYMOLS: + return + + self.visit(node.type) + + if node.name == self.get_type_name(node.type): + return + else: + self.ret += f"{node.name} = {self.get_type_name(node.type)}\n" + self.ret += "\n" + + def visit_FuncDecl(self, node): + # pylint: disable=invalid-name + restype = self.get_type_name(node.type) + + if isinstance(node.type, c_ast.TypeDecl): + func_name = node.type.declname + elif isinstance(node.type, c_ast.PtrDecl): + func_name = node.type.type.declname + else: + raise RuntimeError(f"unexpected type in FuncDecl: {type}") + + if not func_name.startswith("wasm_") or func_name.endswith("_t"): + return + + if func_name in IGNORE_SYMOLS: + return + + params_len = 0 + for arg in node.args.params: + # ignore void but not void* + if isinstance(arg.type, c_ast.TypeDecl): + type_name = self.get_type_name(arg.type) + if "None" == type_name: + continue + + params_len += 1 + + args = ( + "" if not params_len else ",".join([f"arg{i}" for i in range(params_len)]) + ) + argtypes = f"[{self.get_type_name(node.args)}]" if params_len else "None" + + self.ret += ( + f"def {func_name}({args}):\n" + f"{INDENT}_{func_name} = libiwasm.{func_name}\n" + f"{INDENT}_{func_name}.restype = {restype}\n" + f"{INDENT}_{func_name}.argtypes = {argtypes}\n" + f"{INDENT}return _{func_name}({args})\n" + ) + self.ret += "\n" + + def visit_Enum(self, node): + # pylint: disable=invalid-name + elem_value = 0 + # generate enum elementes directly as consts with values + for i, elem in enumerate(node.values.enumerators): + self.ret += f"{elem.name}" + + if elem.value: + elem_value = int(elem.value.value) + else: + if 0 == i: + elem_value = 0 + else: + elem_value += 1 + + self.ret += f" = {elem_value}\n" + + self.ret += "\n" + + +def preflight_check(workspace): + wamr_repo = workspace + file_check_list = [ + wamr_repo.exists(), + wamr_repo.joinpath(WASM_C_API_HEADER).exists(), + ] + + if not all(file_check_list): + print( + "please run utils/download_wamr.py to download the repo, or re-download the repo" + ) + return False + + if not shutil.which("gcc"): + print("please install gcc") + return False + + return True + + +def do_parse(workspace): + filename = workspace.joinpath(WASM_C_API_HEADER) + filename = str(filename) + + ast = parse_file( + filename, + use_cpp=True, + cpp_path="gcc", + cpp_args=[ + "-E", + "-D__attribute__(x)=", + "-D__asm__(x)=", + "-D__asm(x)=", + "-D__builtin_va_list=int", + "-D__extension__=", + "-D__inline__=", + "-D__restrict=", + "-D__restrict__=", + "-D_Static_assert(x, y)=", + "-D__signed=", + "-D__volatile__(x)=", + "-Dstatic_assert(x, y)=", + ], + ) + + ast_visitor = Visitor() + ast_visitor.visit(ast) + return ast_visitor.ret + + +def main(): + current_file = pathlib.Path(__file__) + if current_file.is_symlink(): + current_file = pathlib.Path(os.readlink(current_file)) + + current_dir = current_file.parent.resolve() + root_dir = current_dir.joinpath("../../../..").resolve() + + if not preflight_check(root_dir): + return False + + 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)) + + return True + + +if __name__ == "__main__": + sys.exit(0 if main() else 1) diff --git a/language-bindings/rust/README.md b/language-bindings/rust/README.md new file mode 100644 index 0000000000..06bdbd2fc7 --- /dev/null +++ b/language-bindings/rust/README.md @@ -0,0 +1 @@ +We use a separate repository to host the WAMR Rust SDK. Please refer to [WAMR Rust SDK](https://github.com/bytecodealliance/wamr-rust-sdk) for more details. diff --git a/product-mini/README.md b/product-mini/README.md new file mode 100644 index 0000000000..65c3dd00de --- /dev/null +++ b/product-mini/README.md @@ -0,0 +1,479 @@ +# Build iwasm + +iwasm is the executable binary built with WAMR VMcore supports WASI and command line interface. Refer to [**how to build wamr vmcore**](../doc/build_wamr.md) for all the supported CMAKE compilation variables. + +If you are building for ARM architecture on a X86 development machine, you can use the `CMAKE_TOOLCHAIN_FILE` to set the toolchain file for cross compiling. + +``` +cmake .. -DCMAKE_TOOLCHAIN_FILE=$TOOL_CHAIN_FILE \ + -DWAMR_BUILD_PLATFORM=linux \ + -DWAMR_BUILD_TARGET=ARM +``` + +Refer to toolchain sample file [`wamr-app-framework/samples/simple/profiles/arm-interp/toolchain.cmake`](https://github.com/bytecodealliance/wamr-app-framework/blob/main/samples/simple/profiles/arm-interp/toolchain.cmake) for how to build mini product for ARM target architecture. + +If you compile for ESP-IDF, make sure to set the right toolchain file for the chip you're using (e.g. `$IDF_PATH/tools/cmake/toolchain-esp32c3.cmake`). +Note that all ESP-IDF toolchain files live under `$IDF_PATH/tools/cmake/`. + +## Linux + +First of all please install the dependent packages. +Run command below in Ubuntu-22.04: +``` Bash +sudo apt install build-essential cmake g++-multilib libgcc-11-dev lib32gcc-11-dev ccache +``` +Or in Ubuntu-20.04 +``` Bash +sudo apt install build-essential cmake g++-multilib libgcc-9-dev lib32gcc-9-dev ccache +``` +Or in Ubuntu-18.04: +``` Bash +sudo apt install build-essential cmake g++-multilib libgcc-8-dev lib32gcc-8-dev ccache +``` +Or in Fedora: +``` Bash +sudo dnf install glibc-devel.i686 +``` + +After installing dependencies, build the source code: +``` Bash +cd product-mini/platforms/linux/ +mkdir build && cd build +cmake .. +make +# iwasm is generated under current directory +``` + +By default in Linux, the `fast interpreter`, `AOT` and `Libc WASI` are enabled, and JIT is disabled. +And the build target is set to X86_64 or X86_32 depending on the platform's bitwidth. + +There are total 6 running modes supported: fast interpreter, classi interpreter, AOT, LLVM JIT, Fast JIT and Multi-tier JIT. + +(1) To run a wasm file with `fast interpreter` mode - build iwasm with default build and then: +```Bash +iwasm +``` +Or +```Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_INTERP=1 +make +``` + +(2) To disable `fast interpreter` and enable `classic interpreter` instead: +``` Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_FAST_INTERP=0 +make +``` + +(3) To run an AOT file, firstly please refer to [Build wamrc AOT compiler](../wamr-compiler/README.md) to build wamrc, and then: +```Bash +wamrc -o +iwasm +``` + +(4) To enable the `LLVM JIT` mode, firstly we should build the LLVM library: +``` Bash +cd product-mini/platforms/linux/ +./build_llvm.sh (The llvm source code is cloned under /core/deps/llvm and auto built) +``` + +Note: By default, ccache is disabled to reduce CI storage consumption. For local development with frequent LLVM rebuilds, you can enable ccache for faster incremental builds by using the `--use-ccache` flag: +``` Bash +cd /build-scripts +python3 build_llvm.py --arch X86 --use-ccache +``` + +Then pass argument `-DWAMR_BUILD_JIT=1` to cmake to enable LLVM JIT: +``` Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_JIT=1 +make +``` + +Note: +By default, the LLVM Orc JIT with Lazy compilation is enabled to speedup the launching process and reduce +the JIT compilation time by creating backend threads to compile the WASM functions parallelly, and for the +main thread, the functions in the module will not be compiled until they are firstly called and haven't been +compiled by the compilation threads. + +If developer wants to disable the Lazy compilation, we can: +``` Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=0 +make +``` +In which all the WASM functions will be previously compiled before main thread starts to run the wasm module. + +(5) To enable the `Fast JIT` mode: +``` Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_FAST_JIT=1 +make +``` +The Fast JIT is a lightweight JIT engine with quick startup, small footprint and good portability, and gains ~50% performance of AOT. + +(6) To enable the `Multi-tier JIT` mode: +``` Bash +mkdir build && cd build +cmake .. -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_JIT=1 +make +``` +The Multi-tier JIT is a two level JIT tier-up engine, which launches Fast JIT to run the wasm module as soon as possible and creates backend threads to compile the LLVM JIT functions at the same time, and when the LLVM JIT functions are compiled, the runtime will switch the extecution from the Fast JIT jitted code to LLVM JIT jitted code gradually, so as to gain the best performance. + +## Linux SGX (Intel Software Guard Extension) + + +Please see [Build and Port WAMR vmcore for Linux SGX](../doc/linux_sgx.md) for the details. + +## MacOS + + +Make sure to install Xcode from App Store firstly, and install cmake. + +If you use Homebrew, install cmake from the command line: +``` Bash +brew install cmake +``` + +Then build the source codes: +``` Bash +cd product-mini/platforms/darwin/ +mkdir build +cd build +cmake .. +make +# iwasm is generated under current directory +``` +By default in MacOS, the `fast interpreter`, `AOT` and `Libc WASI` are enabled, and JIT is disabled. +And the build target is set to X86_64 or X86_32 depending on the platform's bitwidth. + +To run a wasm file with interpreter mode: +```Bash +iwasm +``` +To run an AOT file, firstly please refer to [Build wamrc AOT compiler](../wamr-compiler/README.md) to build wamrc, and then: +```Bash +wamrc -o +iwasm +``` +Note: +For how to build the `JIT` mode and `classic interpreter` mode, please refer to [Build iwasm on Linux](../doc/build_wamr.md#linux). + +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](../doc/build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in MacOS, interpreter, AOT, and builtin libc are enabled by default. + +## Windows + + +Make sure `MSVC` and `cmake` are installed and available in the command line environment + +Then build the source codes: +``` Bash +cd product-mini/platforms/windows/ +mkdir build +cd build +cmake .. +cmake --build . --config Release +# ./Release/iwasm.exe is generated +``` + +By default in Windows, the `fast interpreter`, `AOT` and `Libc WASI` are enabled, and JIT is disabled. + +To run a wasm file with interpreter mode: +```Bash +iwasm.exe +``` +To run an AOT file, firstly please refer to [Build wamrc AOT compiler](../wamr-compiler/README.md) to build wamrc, and then: +```Bash +wamrc.exe -o +iwasm.exe +``` +Note: +For how to build the `JIT` mode and `classic interpreter` mode, please refer to [Build iwasm on Linux](../doc/build_wamr.md#linux). + +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](../doc/build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in Windows, interpreter, AOT, and builtin libc are enabled by default. + +## MinGW + + +First make sure the correct CMake package is installed; the following commands +are valid for the MSYS2 build environment: + +```Bash +pacman -R cmake +pacman -S mingw-w64-x86_64-cmake +pacman -S mingw-w64-x86_64-gcc +pacman -S make git +``` + +Then follow the build instructions for Windows above, and add the following +arguments for cmake: + +```Bash +cmake .. -G"Unix Makefiles" \ + -DWAMR_DISABLE_HW_BOUND_CHECK=1 +```` + +Note that WASI will be disabled until further work is done towards full MinGW support. + +- Since memory access boundary check with hardware trap feature is disabled, when generating the AOT file with `wamrc`, the `--bounds-checks=1` flag should be added to generate the memory access boundary check instructions to ensure the sandbox security: +```bash +wamrc --bounds-checks=1 -o +``` +- Compiler complaining about missing `UnwindInfoAddress` field in `RUNTIME_FUNCTION` +struct (winnt.h). + + +## VxWorks + +VxWorks 7 SR0620 release is validated. + +First you need to build a VSB. Make sure *UTILS_UNIX* layer is added in the VSB. +After the VSB is built, export the VxWorks toolchain path by: +```bash +export /host/vx-compiler/bin:$PATH +``` +Now switch to iwasm source tree to build the source code: +```bash +cd product-mini/platforms/vxworks/ +mkdir build +cd build +cmake .. +make +``` +Create a VIP based on the VSB. Make sure the following components are added: +* INCLUDE_POSIX_PTHREADS +* INCLUDE_POSIX_PTHREAD_SCHEDULER +* INCLUDE_SHARED_DATA +* INCLUDE_SHL + +Copy the generated iwasm executable, the test WASM binary as well as the needed +shared libraries (libc.so.1, libllvm.so.1 or libgnu.so.1 depending on the VSB, +libunix.so.1) to a supported file system (eg: romfs). + +Note: +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](../doc/build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in VxWorks, interpreter and builtin libc are enabled by default. + +## Zephyr +Please refer to this [README](./platforms/zephyr/simple/README.md) under the Zephyr sample directory for details. + +Note: +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](../doc/build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in Zephyr, interpreter, AOT and builtin libc are enabled by default. + + +## RT-Thread + + +1. Get rt-thread [system codes](https://github.com/RT-Thread/rt-thread). + +2. Enable WAMR software package with menuconfig tool which provided by RT-Thread. + + * Environment in Linux, run command below: + + ```bash + scons --menuconfig + ``` + + * Environment in Windows ConEmu, run command below: + + ```bash + menuconfig + ``` + + Select and enable `WAMR` in: + + * RT-Thread online packages + * tools packages + * WebAssembly Micro Runtime (WAMR) + +3. Configure `WAMR` with menuconfig tool. + + you can choice features of iwasm below: + + * Enable testing parameters of iwasm + * Enable interpreter Mode / Fast interpreter Mode + * Use built-libc + * Enable AOT + +4. Exit menuconfig tool and save configure, update and download package. + + ```bash + pkgs --update + ``` + +5. build project and download the binary to boards. + + ```bash + scons + ``` + + or build project with 8-thread by using command below: + + ```bash + scons -j8 + ``` + + after project building, you can got an binary file named `rtthread.bin`, then you can download this file to the MCU board. + +## Android + +Able to generate a shared library support Android platform. +- need an [android SDK](https://developer.android.com/studio). Go and get the "Command line tools only" +- look for a command named *sdkmanager* and download below components. version numbers might need to check and pick others + - "build-tools;29.0.3" + - "cmake;3.10.2.4988404" + - "ndk;latest" + - "patcher;v4" + - "platform-tools" + - "platforms;android-29" +- add bin/ of the downloaded cmake to $PATH +- export ANDROID_HOME=/the/path/of/downloaded/sdk/ +- export ANDROID_NDK_LATEST_HOME=/the/path/of/downloaded/sdk/ndk/2x.xxx/ +- ready to go + +Use such commands, you are able to compile with default configurations. + +``` shell +$ cd product-mini/platforms/android/ +$ mkdir build +$ cd build +$ cmake .. +$ make +$ # check output in distribution/wasm +$ # include/ includes all necessary head files +$ # lib includes libiwasm.so +``` + +To change the target architecture and ABI, you can define `WAMR_BUILD_TARGET` or `ANDROID_ABI` respectively. To build for [supported Android ABIs](https://developer.android.com/ndk/guides/abis#sa): + +```shell +$ cmake .. -DWAMR_BUILD_TARGET=X86_32 -DANDROID_ABI=x86 # 32-bit Intel CPU +$ cmake .. -DWAMR_BUILD_TARGET=X86_64 -DANDROID_ABI=x86_64 # 64-bit Intel CPU +$ cmake .. -DWAMR_BUILD_TARGET=ARMV7A -DANDROID_ABI=armeabi-v7a # 32-bit ARM CPU +$ cmake .. -DWAMR_BUILD_TARGET=AARCH64 -DANDROID_ABI=arm64-v8a # 64-bit ARM CPU +``` + +## NuttX + +WAMR is integrated with NuttX, just enable the WAMR in Kconfig option (Application Configuration/Interpreters). + +## ESP-IDF + +WAMR integrates with ESP-IDF both for the XTENSA and RISC-V chips (esp32x and esp32c3 respectively). + +In order to use this, you need at least version 4.3.1 of ESP-IDF. +If you don't have it installed, follow the instructions [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/#get-started-get-prerequisites). +ESP-IDF also installs the toolchains needed for compiling WAMR and ESP-IDF. +A small demonstration of how to use WAMR and ESP-IDF can be found under [product_mini](./platforms/esp-idf). +The demo builds WAMR for ESP-IDF and runs a small wasm program. +In order to run it for your specific Espressif chip, edit the [build_and_run.sh](./platforms/esp-idf/build_and_run.sh) file and put the correct toolchain file (see #Cross-compilation) and `IDF_TARGET`. +Before compiling it is also necessary to call ESP-IDF's `export.sh` script to bring all compile time relevant information in scope. + +## Docker + +[Docker](https://www.docker.com/) will download all the dependencies and build WAMR Core on your behalf. + +Make sure you have Docker installed on your machine: [macOS](https://docs.docker.com/docker-for-mac/install/), [Windows](https://docs.docker.com/docker-for-windows/install/) or [Linux](https://docs.docker.com/install/linux/docker-ce/ubuntu/). + +Build *iwasm* with the Docker image: + +``` Bash +$ cd ci +$ ./build_wamr.sh +$ ls ../build_out/ +``` + +*build_wamr.sh* will generate *linux* compatible libraries ( libiwasm.so and +libvmlib.a ) and an executable binary (*iwasm*) and copy *iwasm* to +*build_out*. All original generated files are still under +*product-mini/platforms/linux/build*. + +## FreeBSD + +First, install the dependent packages: +```shell +sudo pkg install gcc cmake wget +``` + +Then you can run the following commands to build iwasm with default configurations: +```shell +cd product-mini/platforms/freebsd +mkdir build && cd build +cmake .. +make +``` + + +## AliOS-Things + +1. a developerkit board id needed for testing +2. download the AliOS-Things code + ``` Bash + git clone https://github.com/alibaba/AliOS-Things.git + ``` +3. copy /product-mini/platforms/alios-things directory to AliOS-Things/middleware, and rename it as iwasm + ``` Bash + cp -a /product-mini/platforms/alios-things middleware/iwasm + ``` +4. create a link to in middleware/iwasm/ and rename it to wamr + ``` Bash + ln -s middleware/iwasm/wamr + ``` +5. modify file app/example/helloworld/helloworld.c, patch as: + ``` C + #include + #include + extern bool iwasm_init(); + int application_start(int argc, char *argv[]) + { + int count = 0; + iwasm_init(); + ... + } + ``` +6. modify file app/example/helloworld/aos.mk + ``` C + $(NAME)_COMPONENTS := osal_aos iwasm + ``` +7. build source code and run + For linux host: + + ``` Bash + aos make helloworld@linuxhost -c config + aos make + ./out/helloworld@linuxhost/binary/helloworld@linuxhost.elf + ``` + + For developerkit: + Modify file middleware/iwasm/aos.mk, patch as: + + ``` C + WAMR_BUILD_TARGET := THUMBV7M + ``` + + ``` Bash + aos make helloworld@developerkit -c config + aos make + ``` + download the binary to developerkit board, check the output from serial port + +## Cosmopolitan Libc +Currently, only x86_64 architecture with interpreter modes is supported. + +Setup `cosmocc` as described in [Getting Started](https://github.com/jart/cosmopolitan/#getting-started) being sure to get its `bin` directory into `PATH`. + +Build iwasm +``` Bash +export CC=x86_64-unknown-cosmo-cc +export CXX=x86_64-unknown-cosmo-c++ +rm -rf build +mkdir build +cmake -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=1 -B build +cmake --build build -j +``` + +Run like +``` Bash +./build/iwasm.com +``` diff --git a/product-mini/app-samples/hello-world-cmake/CMakeLists.txt b/product-mini/app-samples/hello-world-cmake/CMakeLists.txt index b41fe0a515..6131b69772 100644 --- a/product-mini/app-samples/hello-world-cmake/CMakeLists.txt +++ b/product-mini/app-samples/hello-world-cmake/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 3.5) +cmake_minimum_required (VERSION 3.14) project(hello_world) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-unused-command-line-argument") diff --git a/product-mini/app-samples/hello-world-cmake/main.c b/product-mini/app-samples/hello-world-cmake/main.c index 1c356c4799..b6dfd2802b 100644 --- a/product-mini/app-samples/hello-world-cmake/main.c +++ b/product-mini/app-samples/hello-world-cmake/main.c @@ -5,9 +5,11 @@ #include "stdio.h" -void print_line(char* str); +void +print_line(char *str); -int main() +int +main() { print_line("Hello World!"); print_line("Wasm Micro Runtime"); diff --git a/product-mini/app-samples/hello-world-cmake/print.c b/product-mini/app-samples/hello-world-cmake/print.c index 8eee6b6b39..d98a82667b 100644 --- a/product-mini/app-samples/hello-world-cmake/print.c +++ b/product-mini/app-samples/hello-world-cmake/print.c @@ -6,7 +6,8 @@ #include "stdio.h" #include "string.h" -void print_line(char* str) +void +print_line(char *str) { printf("%s\n", str); } \ No newline at end of file diff --git a/product-mini/app-samples/hello-world/build.sh b/product-mini/app-samples/hello-world/build.sh index 9407c700e2..5dc612bca7 100755 --- a/product-mini/app-samples/hello-world/build.sh +++ b/product-mini/app-samples/hello-world/build.sh @@ -3,12 +3,23 @@ WAMR_DIR=${PWD}/../../.. -/opt/wasi-sdk/bin/clang \ - --target=wasm32 -O3 \ +echo "Build wasm app .." +/opt/wasi-sdk/bin/clang -O3 \ -z stack-size=4096 -Wl,--initial-memory=65536 \ - --sysroot=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot \ - -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \ - -Wl,--export=main, \ - -Wl,--no-threads,--strip-all,--no-entry \ - -nostdlib -o test.wasm *.c -#./jeffdump -o test_wasm.h -n wasm_test_file test.wasm + -o test.wasm main.c \ + -Wl,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--export=__data_end -Wl,--export=__heap_base \ + -Wl,--strip-all,--no-entry \ + -Wl,--allow-undefined \ + -nostdlib \ + +echo "Build binarydump tool .." +rm -fr build && mkdir build && cd build +cmake ../../../../test-tools/binarydump-tool +make +cd .. + +echo "Generate test_wasm.h .." +./build/binarydump -o test_wasm.h -n wasm_test_file test.wasm + +echo "Done" diff --git a/product-mini/app-samples/hello-world/main.c b/product-mini/app-samples/hello-world/main.c index d392fb1f86..4778a4d9b6 100644 --- a/product-mini/app-samples/hello-world/main.c +++ b/product-mini/app-samples/hello-world/main.c @@ -6,7 +6,8 @@ #include #include -int main(int argc, char **argv) +int +main(int argc, char **argv) { char *buf; diff --git a/product-mini/platforms/alios-things/aos.mk b/product-mini/platforms/alios-things/aos.mk index bf05bff217..7d98ca0b87 100644 --- a/product-mini/platforms/alios-things/aos.mk +++ b/product-mini/platforms/alios-things/aos.mk @@ -2,9 +2,13 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception NAME := iwasm +CORE_ROOT := wamr/core IWASM_ROOT := wamr/core/iwasm SHARED_ROOT := wamr/core/shared +GLOBAL_DEFINES += BH_MALLOC=wasm_runtime_malloc +GLOBAL_DEFINES += BH_FREE=wasm_runtime_free + # Change it to THUMBV7M if you want to build for developerkit WAMR_BUILD_TARGET := X86_32 @@ -13,23 +17,29 @@ WAMR_BUILD_PLATFORM := alios-things ifeq (${WAMR_BUILD_TARGET}, X86_32) GLOBAL_DEFINES += BUILD_TARGET_X86_32 INVOKE_NATIVE := invokeNative_ia32.s + AOT_RELOC := aot_reloc_x86_32.c else ifeq (${WAMR_BUILD_TARGET}, X86_64) GLOBAL_DEFINES += BUILD_TARGET_X86_64 INVOKE_NATIVE := invokeNative_em64.s + AOT_RELOC := aot_reloc_x86_64.c else ifeq ($(findstring ARM,$(WAMR_BUILD_TARGET)), ARM) GLOBAL_DEFINES += BUILD_TARGET_ARM GLOBAL_DEFINES += BUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" INVOKE_NATIVE := invokeNative_arm.s + AOT_RELOC := aot_reloc_arm.c else ifeq ($(findstring THUMB,$(WAMR_BUILD_TARGET)), THUMB) GLOBAL_DEFINES += BUILD_TARGET_THUMB GLOBAL_DEFINES += BUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" INVOKE_NATIVE := invokeNative_thumb.s + AOT_RELOC := aot_reloc_thumb.c else ifeq (${WAMR_BUILD_TARGET}, MIPS) GLOBAL_DEFINES += BUILD_TARGET_MIPS INVOKE_NATIVE := invokeNative_mips.s + AOT_RELOC := aot_reloc_mips.c else ifeq (${WAMR_BUILD_TARGET}, XTENSA) GLOBAL_DEFINES += BUILD_TARGET_XTENSA INVOKE_NATIVE := invokeNative_xtensa.s + AOT_RELOC := aot_reloc_xtensa.c else $(error Build target isn't set) endif @@ -40,6 +50,18 @@ WAMR_BUILD_INTERP = 1 # Enable AOT by default. WAMR_BUILD_AOT = 1 +# Override the global heap usage +ifndef WAMR_BUILD_GLOBAL_HEAP_POOL +WAMR_BUILD_GLOBAL_HEAP_POOL=1 +endif +GLOBAL_DEFINES += WASM_ENABLE_GLOBAL_HEAP_POOL=${WAMR_BUILD_GLOBAL_HEAP_POOL} + +# Override the global heap size for small devices +ifndef WAMR_BUILD_GLOBAL_HEAP_SIZE +WAMR_BUILD_GLOBAL_HEAP_SIZE = 262144 # 256 kB +endif +GLOBAL_DEFINES += WASM_GLOBAL_HEAP_SIZE=${WAMR_BUILD_GLOBAL_HEAP_SIZE} + ifeq (${WAMR_BUILD_INTERP}, 1) GLOBAL_DEFINES += WASM_ENABLE_INTERP=1 endif @@ -50,10 +72,13 @@ endif GLOBAL_DEFINES += WASM_ENABLE_LIBC_BUILTIN=1 -GLOBAL_INCLUDES += ${IWASM_ROOT}/include \ +GLOBAL_INCLUDES += ${CORE_ROOT} \ + ${IWASM_ROOT}/include \ ${IWASM_ROOT}/common \ ${SHARED_ROOT}/include \ ${SHARED_ROOT}/platform/include \ + ${SHARED_ROOT}/utils \ + ${SHARED_ROOT}/mem-alloc \ ${SHARED_ROOT}/platform/alios ifeq (${WAMR_BUILD_INTERP}, 1) @@ -64,38 +89,45 @@ ifeq (${WAMR_BUILD_AOT}, 1) GLOBAL_INCLUDES += ${IWASM_ROOT}/aot endif -$(NAME)_SOURCES := ${SHARED_ROOT}/platform/alios/bh_assert.c \ - ${SHARED_ROOT}/platform/alios/bh_definition.c \ - ${SHARED_ROOT}/platform/alios/bh_math.c \ - ${SHARED_ROOT}/platform/alios/bh_platform.c \ - ${SHARED_ROOT}/platform/alios/bh_platform_log.c \ - ${SHARED_ROOT}/platform/alios/bh_thread.c \ - ${SHARED_ROOT}/platform/alios/bh_time.c \ - ${SHARED_ROOT}/mem-alloc/bh_memory.c \ +$(NAME)_SOURCES := ${SHARED_ROOT}/platform/alios/alios_platform.c \ + ${SHARED_ROOT}/platform/alios/alios_thread.c \ + ${SHARED_ROOT}/platform/alios/alios_time.c \ + ${SHARED_ROOT}/platform/common/math/math.c \ ${SHARED_ROOT}/mem-alloc/mem_alloc.c \ ${SHARED_ROOT}/mem-alloc/ems/ems_kfc.c \ ${SHARED_ROOT}/mem-alloc/ems/ems_alloc.c \ ${SHARED_ROOT}/mem-alloc/ems/ems_hmu.c \ + ${SHARED_ROOT}/utils/bh_assert.c \ + ${SHARED_ROOT}/utils/bh_bitmap.c \ + ${SHARED_ROOT}/utils/bh_common.c \ ${SHARED_ROOT}/utils/bh_hashmap.c \ ${SHARED_ROOT}/utils/bh_list.c \ + ${SHARED_ROOT}/utils/bh_leb128.c \ ${SHARED_ROOT}/utils/bh_log.c \ ${SHARED_ROOT}/utils/bh_queue.c \ ${SHARED_ROOT}/utils/bh_vector.c \ + ${SHARED_ROOT}/utils/runtime_timer.c \ ${IWASM_ROOT}/libraries/libc-builtin/libc_builtin_wrapper.c \ + ${IWASM_ROOT}/common/wasm_application.c \ ${IWASM_ROOT}/common/wasm_runtime_common.c \ ${IWASM_ROOT}/common/wasm_native.c \ ${IWASM_ROOT}/common/wasm_exec_env.c \ + ${IWASM_ROOT}/common/wasm_loader_common.c \ + ${IWASM_ROOT}/common/wasm_memory.c \ + ${IWASM_ROOT}/common/wasm_c_api.c \ ${IWASM_ROOT}/common/arch/${INVOKE_NATIVE} \ - src/main.c src/ext_lib_export.c + src/main.c ifeq (${WAMR_BUILD_INTERP}, 1) -$(NAME)_SOURCES += ${IWASM_ROOT}/interpreter/wasm_interp.c \ +$(NAME)_SOURCES += ${IWASM_ROOT}/interpreter/wasm_interp_classic.c \ ${IWASM_ROOT}/interpreter/wasm_loader.c \ ${IWASM_ROOT}/interpreter/wasm_runtime.c endif ifeq (${WAMR_BUILD_AOT}, 1) $(NAME)_SOURCES += ${IWASM_ROOT}/aot/aot_loader.c \ - ${IWASM_ROOT}/aot/aot_runtime.c + ${IWASM_ROOT}/aot/arch/${AOT_RELOC} \ + ${IWASM_ROOT}/aot/aot_runtime.c \ + ${IWASM_ROOT}/aot/aot_intrinsic.c endif diff --git a/product-mini/platforms/alios-things/src/ext_lib_export.c b/product-mini/platforms/alios-things/src/ext_lib_export.c deleted file mode 100644 index 8813f0dbf3..0000000000 --- a/product-mini/platforms/alios-things/src/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/alios-things/src/main.c b/product-mini/platforms/alios-things/src/main.c index 29bf5f9ba1..f2c6f85c16 100644 --- a/product-mini/platforms/alios-things/src/main.c +++ b/product-mini/platforms/alios-things/src/main.c @@ -7,9 +7,7 @@ #include #include "bh_platform.h" #include "bh_log.h" -#include "bh_platform_log.h" #include "wasm_export.h" -#include "bh_memory.h" #include "test_wasm.h" static int app_argc; @@ -26,65 +24,75 @@ static char **app_argv; * @return true if the main function is called, false otherwise. */ bool -wasm_application_execute_main(wasm_module_inst_t module_inst, - int32_t argc, char *argv[]); +wasm_application_execute_main(wasm_module_inst_t module_inst, int32_t argc, + char *argv[]); -static void* +static void * app_instance_main(wasm_module_inst_t module_inst) { const char *exception; wasm_application_execute_main(module_inst, app_argc, app_argv); if ((exception = wasm_runtime_get_exception(module_inst))) - bh_printf("%s\n", exception); + printf("%s\n", exception); return NULL; } -static char global_heap_buf[256 * 1024] = { 0 }; +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 +static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; +#endif -void iwasm_main(void *arg1) +void +iwasm_main(void *arg1) { uint8 *wasm_file_buf = NULL; uint32 wasm_file_size; wasm_module_t wasm_module = NULL; wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; char error_buf[128]; #if WASM_ENABLE_LOG != 0 int log_verbose_level = 2; #endif - (void) arg1; + (void)arg1; - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - bh_printf("Init global heap failed.\n"); - return; - } + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else +#error Another memory allocation scheme than global heap pool is not implemented yet for Alios. +#endif /* initialize runtime environment */ - if (!wasm_runtime_init()) - goto fail1; + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return; + } #if WASM_ENABLE_LOG != 0 bh_log_set_verbose_level(log_verbose_level); #endif /* load WASM byte buffer from byte buffer of include file */ - wasm_file_buf = (uint8*) wasm_test_file; + wasm_file_buf = (uint8 *)wasm_test_file; wasm_file_size = sizeof(wasm_test_file); /* load WASM module */ if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, - error_buf, sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail2; + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail1; } /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, 8 * 1024, - 8 * 1024, error_buf, sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail3; + if (!(wasm_module_inst = wasm_runtime_instantiate( + wasm_module, 8 * 1024, 8 * 1024, error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; } app_instance_main(wasm_module_inst); @@ -92,24 +100,22 @@ void iwasm_main(void *arg1) /* destroy the module instance */ wasm_runtime_deinstantiate(wasm_module_inst); - fail3: +fail2: /* unload the module */ wasm_runtime_unload(wasm_module); - fail2: +fail1: /* destroy runtime environment */ wasm_runtime_destroy(); - - fail1: bh_memory_destroy(); } #define DEFAULT_THREAD_STACKSIZE (6 * 1024) #define DEFAULT_THREAD_PRIORITY 50 -bool iwasm_init(void) +bool +iwasm_init(void) { - int ret = aos_task_new("wasm-main", iwasm_main, NULL, - DEFAULT_THREAD_STACKSIZE); + int ret = + aos_task_new("wasm-main", iwasm_main, NULL, DEFAULT_THREAD_STACKSIZE); return ret == 0 ? true : false; } - diff --git a/product-mini/platforms/alios-things/src/test_wasm.h b/product-mini/platforms/alios-things/src/test_wasm.h index 65b8347982..0c343024a2 100644 --- a/product-mini/platforms/alios-things/src/test_wasm.h +++ b/product-mini/platforms/alios-things/src/test_wasm.h @@ -5,55 +5,42 @@ /** * The byte array buffer is the file content of a test wasm binary file, - * which is compiled by emcc or clang toolchain from C source file of: - * core/iwasm/app-samples/hello-world/main.c. + * which is compiled by wasi-sdk toolchain from C source file of: + * product-mini/app-samples/hello-world/main.c. */ -unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, - 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, - 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, - 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, - 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, - 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, - 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, - 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, - 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, - 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, - 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, - 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, - 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, - 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, - 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, - 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, - 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, - 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, - 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, - 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, - 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, - 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, - 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, - 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, - 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, - 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, - 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, - 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, - 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, - 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, - 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, - 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, - 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, - 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, - 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, - 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, - 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, - 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, - 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, - 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, - 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, - 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, - 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, - 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, - 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, - 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, - 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; +unsigned char wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x28, 0x0B, 0x7F, 0x00, 0x41, 0xBA, 0x08, 0x0B, + 0x7F, 0x00, 0x41, 0xC0, 0x28, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, + 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x04, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x04, 0x0A, 0xB2, 0x01, 0x01, 0xAF, 0x01, 0x01, 0x03, + 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x88, 0x80, 0x80, 0x00, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, + 0x41, 0xA8, 0x88, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1A, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6A, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, + 0x04, 0x20, 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x88, + 0x80, 0x80, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, + 0x8D, 0x88, 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x41, 0x93, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, + 0x80, 0x00, 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x0B, 0x0B, 0x41, 0x01, 0x00, 0x41, 0x80, 0x08, + 0x0B, 0x3A, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, + 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, + 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, + 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; diff --git a/product-mini/platforms/android/CMakeLists.txt b/product-mini/platforms/android/CMakeLists.txt new file mode 100644 index 0000000000..7c4671331d --- /dev/null +++ b/product-mini/platforms/android/CMakeLists.txt @@ -0,0 +1,135 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +if (NOT DEFINED WAMR_BUILD_TARGET) + message (FATAL_ERROR "WAMR_BUILD_TARGET isn't set") +endif () + +if (NOT (WAMR_BUILD_TARGET STREQUAL "X86_64" + OR WAMR_BUILD_TARGET STREQUAL "X86_32" + OR WAMR_BUILD_TARGET MATCHES "AARCH64.*" + OR WAMR_BUILD_TARGET MATCHES "ARM.*" + OR WAMR_BUILD_TARGET MATCHES "RISCV64.*")) + message (FATAL_ERROR "Unsupported build target platform ${WAMR_BUILD_TARGET}!") +endif () + +if (NOT DEFINED ANDROID_ABI) + if (WAMR_BUILD_TARGET STREQUAL "X86_64") + set (ANDROID_ABI "x86_64") + elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + set (ANDROID_ABI "x86") + elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + set (ANDROID_ABI "arm64-v8a") + elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + set (ANDROID_ABI "armeabi-v7a") + else () + set (ANDROID_ABI "riscv64") + endif () +endif () + +if (NOT DEFINED ANDROID_LD) + set (ANDROID_LD lld) +endif () + +if (NOT DEFINED ANDROID_PLATFORM) + set (ANDROID_PLATFORM 24) +endif () + +# https://android.googlesource.com/platform/ndk/+/master/build/cmake/android.toolchain.cmake +set (CMAKE_TOOLCHAIN_FILE "$ENV{ANDROID_NDK_LATEST_HOME}/build/cmake/android.toolchain.cmake") +set (ANDROID_SDK $ENV{ANDROID_HOME}) +set (ANDROID_NDK $ENV{ANDROID_NDK_LATEST_HOME}) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "android") + +set (CMAKE_VERBOSE_MAKEFILE ON) + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple modules by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + 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() + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (vmlib) + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE") + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#endif () +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") + +add_library (iwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +if (CMAKE_BUILD_TYPE STREQUAL Release) +target_link_libraries (iwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -landroid -llog -s) +else() +target_link_libraries (iwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -landroid -llog) +endif() + +set (distribution_DIR ${CMAKE_BINARY_DIR}/distribution) +set_target_properties (iwasm PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${distribution_DIR}/wasm/lib") +set_version_info (iwasm) + +add_custom_command (TARGET iwasm POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${WAMR_ROOT_DIR}/core/iwasm/include" "${distribution_DIR}/wasm/include/" + COMMENT "Copying iwasm to output directory") diff --git a/product-mini/platforms/android/build_jit.sh b/product-mini/platforms/android/build_jit.sh new file mode 100755 index 0000000000..ffa440e95c --- /dev/null +++ b/product-mini/platforms/android/build_jit.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -fr build && mkdir build +cd build +cmake .. -DWAMR_BUILD_JIT=1 +make -j ${nroc} +cd .. diff --git a/product-mini/platforms/android/build_llvm.sh b/product-mini/platforms/android/build_llvm.sh new file mode 100755 index 0000000000..145e2dbaa0 --- /dev/null +++ b/product-mini/platforms/android/build_llvm.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Copyright (C) 2020 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +/usr/bin/env python3 -m pip install --user -r ../../../build-scripts/requirements.txt +/usr/bin/env python3 ../../../build-scripts/build_llvm.py --platform android "$@" diff --git a/product-mini/platforms/android/wasm-jni.cpp b/product-mini/platforms/android/wasm-jni.cpp new file mode 100644 index 0000000000..c2eff3a8bf --- /dev/null +++ b/product-mini/platforms/android/wasm-jni.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include +#include + +#define LOGI(...) \ + ((void)__android_log_print(ANDROID_LOG_INFO, "wasm_jni::", __VA_ARGS__)) + +static void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, 0, NULL); + if ((exception = wasm_runtime_get_exception(module_inst))) + LOGI("%s\n", exception); + return NULL; +} + +// WARNING! CAN NOT BE READ ONLY!!! +static unsigned char wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x28, 0x0B, 0x7F, 0x00, 0x41, 0xBA, 0x08, 0x0B, + 0x7F, 0x00, 0x41, 0xC0, 0x28, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, + 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x04, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x04, 0x0A, 0xB2, 0x01, 0x01, 0xAF, 0x01, 0x01, 0x03, + 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x88, 0x80, 0x80, 0x00, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, + 0x41, 0xA8, 0x88, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1A, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6A, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, + 0x04, 0x20, 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x88, + 0x80, 0x80, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, + 0x8D, 0x88, 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x41, 0x93, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, + 0x80, 0x00, 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x0B, 0x0B, 0x41, 0x01, 0x00, 0x41, 0x80, 0x08, + 0x0B, 0x3A, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, + 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, + 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, + 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; + +extern "C" JNIEXPORT void JNICALL +Java_com_intel_wasm_api_Runtime_run(JNIEnv *env, jclass thiz) +{ + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + uint wasm_file_size = 0; + uint8_t *wasm_file_buf = NULL; + char error_buf[128] = { 0 }; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if WASM_ENABLE_GLOBAL_HEAP_POOL == 0 + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = (void *)malloc; + init_args.mem_alloc_option.allocator.realloc_func = (void *)realloc; + init_args.mem_alloc_option.allocator.free_func = (void *)free; +#else +#error The usage of a global heap pool is not implemented yet for Android. +#endif + + LOGI("wasm_runtime_full_init"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + LOGI("Init runtime failed.\n"); + return; + } + + /* load WASM byte buffer from a preinstall WASM bin file */ + LOGI("use an internal test file, gona to output Hello World in logcat\n"); + wasm_file_buf = (uint8_t *)wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + + /* load WASM module */ + LOGI("wasm_runtime_load"); + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + LOGI("in wasm_runtime_load %s\n", error_buf); + LOGI("goto fail1\n"); + goto fail1; + } + + /* instantiate the module */ + LOGI("wasm_runtime_instantiate"); + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, 64 * 1024, /* stack size */ + 64 * 1024, /* heap size */ + error_buf, sizeof(error_buf)))) { + LOGI("%s\n", error_buf); + LOGI("goto fail2\n"); + goto fail2; + } + + LOGI("run main() of the application"); + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + LOGI("wasm_runtime_deinstantiate"); + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + LOGI("wasm_runtime_unload"); + wasm_runtime_unload(wasm_module); + +fail1: + // in our case, we don't need a free, but it is not a typical one + /* free the file buffer */ + // bh_free((void *) wasm_file_buf); + + /* destroy runtime environment */ + LOGI("wasm_runtime_destroy"); + wasm_runtime_destroy(); + return; +} diff --git a/product-mini/platforms/common/libc_wasi.c b/product-mini/platforms/common/libc_wasi.c new file mode 100644 index 0000000000..8e45d7329c --- /dev/null +++ b/product-mini/platforms/common/libc_wasi.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2023 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "bh_platform.h" +#include "wasm_export.h" + +typedef struct { + const char *dir_list[8]; + uint32 dir_list_size; + const char *map_dir_list[8]; + uint32 map_dir_list_size; + const char *env_list[8]; + uint32 env_list_size; + const char *addr_pool[8]; + uint32 addr_pool_size; + const char *ns_lookup_pool[8]; + uint32 ns_lookup_pool_size; +} libc_wasi_parse_context_t; + +typedef enum { + LIBC_WASI_PARSE_RESULT_OK = 0, + LIBC_WASI_PARSE_RESULT_NEED_HELP, + LIBC_WASI_PARSE_RESULT_BAD_PARAM +} libc_wasi_parse_result_t; + +static void +libc_wasi_print_help(void) +{ + printf(" --env= Pass wasi environment variables with " + "\"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" " + "--env=\"key2=value2\"\n"); + printf(" --dir=
Grant wasi access to the given host " + "directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); + printf(" --map-dir= Grant wasi access to the given host " + "directories\n"); + printf(" to the program at a specific guest " + "path, for example:\n"); + printf(" --map-dir= " + "--map-dir=\n"); + printf(" --addr-pool= Grant wasi access to the given network " + "addresses in\n"); + printf(" CIDR notation to the program, separated " + "with ',',\n"); + printf(" for example:\n"); + printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n"); + printf(" --allow-resolve= Allow the lookup of the specific domain " + "name or domain\n"); + printf(" name suffixes using a wildcard, for " + "example:\n"); + printf(" --allow-resolve=example.com # allow the " + "lookup of the specific domain\n"); + printf(" --allow-resolve=*.example.com # allow " + "the lookup of all subdomains\n"); + printf(" --allow-resolve=* # allow any lookup\n"); +} + +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} + +libc_wasi_parse_result_t +libc_wasi_parse(char *arg, libc_wasi_parse_context_t *ctx) +{ + if (!strncmp(arg, "--dir=", 6)) { + if (arg[6] == '\0') + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + if (ctx->dir_list_size >= sizeof(ctx->dir_list) / sizeof(char *)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(ctx->dir_list) / sizeof(char *))); + return LIBC_WASI_PARSE_RESULT_BAD_PARAM; + } + ctx->dir_list[ctx->dir_list_size++] = arg + 6; + } + else if (!strncmp(arg, "--map-dir=", 10)) { + if (arg[10] == '\0') + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + if (ctx->map_dir_list_size + >= sizeof(ctx->map_dir_list) / sizeof(char *)) { + printf("Only allow max map dir number %d\n", + (int)(sizeof(ctx->map_dir_list) / sizeof(char *))); + return 1; + } + ctx->map_dir_list[ctx->map_dir_list_size++] = arg + 10; + } + else if (!strncmp(arg, "--env=", 6)) { + char *tmp_env; + + if (arg[6] == '\0') + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + if (ctx->env_list_size >= sizeof(ctx->env_list) / sizeof(char *)) { + printf("Only allow max env number %d\n", + (int)(sizeof(ctx->env_list) / sizeof(char *))); + return LIBC_WASI_PARSE_RESULT_BAD_PARAM; + } + tmp_env = arg + 6; + if (validate_env_str(tmp_env)) + ctx->env_list[ctx->env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", " + "got \"%s\"\n", + tmp_env); + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + } + } + /* TODO: parse the configuration file via --addr-pool-file */ + else if (!strncmp(arg, "--addr-pool=", strlen("--addr-pool="))) { + /* like: --addr-pool=100.200.244.255/30 */ + char *token = NULL; + + if ('\0' == arg[12]) + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + + token = strtok(arg + strlen("--addr-pool="), ","); + while (token) { + if (ctx->addr_pool_size + >= sizeof(ctx->addr_pool) / sizeof(char *)) { + printf("Only allow max address number %d\n", + (int)(sizeof(ctx->addr_pool) / sizeof(char *))); + return LIBC_WASI_PARSE_RESULT_BAD_PARAM; + } + + ctx->addr_pool[ctx->addr_pool_size++] = token; + token = strtok(NULL, ","); + } + } + else if (!strncmp(arg, "--allow-resolve=", 16)) { + if (arg[16] == '\0') + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + if (ctx->ns_lookup_pool_size + >= sizeof(ctx->ns_lookup_pool) / sizeof(ctx->ns_lookup_pool[0])) { + printf("Only allow max ns lookup number %d\n", + (int)(sizeof(ctx->ns_lookup_pool) + / sizeof(ctx->ns_lookup_pool[0]))); + return LIBC_WASI_PARSE_RESULT_BAD_PARAM; + } + ctx->ns_lookup_pool[ctx->ns_lookup_pool_size++] = arg + 16; + } + else { + return LIBC_WASI_PARSE_RESULT_NEED_HELP; + } + return LIBC_WASI_PARSE_RESULT_OK; +} + +static void +libc_wasi_set_init_args(struct InstantiationArgs2 *args, int argc, char **argv, + libc_wasi_parse_context_t *ctx) +{ + wasm_runtime_instantiation_args_set_wasi_arg(args, argv, argc); + wasm_runtime_instantiation_args_set_wasi_env(args, ctx->env_list, + ctx->env_list_size); + wasm_runtime_instantiation_args_set_wasi_dir( + args, ctx->dir_list, ctx->dir_list_size, ctx->map_dir_list, + ctx->map_dir_list_size); + wasm_runtime_instantiation_args_set_wasi_addr_pool(args, ctx->addr_pool, + ctx->addr_pool_size); + wasm_runtime_instantiation_args_set_wasi_ns_lookup_pool( + args, ctx->ns_lookup_pool, ctx->ns_lookup_pool_size); +} diff --git a/product-mini/platforms/common/wasm_proposal.c b/product-mini/platforms/common/wasm_proposal.c new file mode 100644 index 0000000000..8a4c3d44f9 --- /dev/null +++ b/product-mini/platforms/common/wasm_proposal.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 "bh_platform.h" + +void +wasm_proposal_print_status(void) +{ + printf("About Wasm Proposals:\n"); + printf(" Always-on:\n"); + printf(" - Import/Export of Mutable Globals\n"); + printf(" - Multi-value\n"); + printf(" - Non-trapping float-to-int Conversions\n"); + printf(" - Sign-extension Operators\n"); + printf(" - WebAssembly C and C++ API\n"); + printf(" Compilation Configurable. 0 is OFF. 1 is ON:\n"); + printf(" - Bulk Memory Operation via WASM_ENABLE_BULK_MEMORY: %u\n", + WASM_ENABLE_BULK_MEMORY); + printf(" - Fixed-width SIMD via WASM_ENABLE_SIMD: %u\n", + WASM_ENABLE_SIMD); + printf(" - Garbage Collection via WASM_ENABLE_GC: %u\n", WASM_ENABLE_GC); + printf( + " - Legacy Exception Handling via WASM_ENABLE_EXCE_HANDLING: %u\n", + WASM_ENABLE_EXCE_HANDLING); + printf(" - Memory64 via WASM_ENABLE_MEMORY64: %u\n", + WASM_ENABLE_MEMORY64); + printf(" - Multiple Memories via WASM_ENABLE_MULTI_MEMORY: %u\n", + WASM_ENABLE_MULTI_MEMORY); + printf(" - Reference Types via WASM_ENABLE_REF_TYPES: %u\n", + WASM_ENABLE_REF_TYPES); + printf(" - Reference-Typed Strings via WASM_ENABLE_REF_TYPES: %u\n", + WASM_ENABLE_REF_TYPES); + printf(" - Tail Call via WASM_ENABLE_TAIL_CALL: %u\n", + WASM_ENABLE_TAIL_CALL); + printf(" - Threads via WASM_ENABLE_SHARED_MEMORY: %u\n", + WASM_ENABLE_SHARED_MEMORY); + printf(" - Typed Function References via WASM_ENABLE_GC: %u\n", + WASM_ENABLE_GC); + printf(" Unsupported (>= Phase4):\n"); + printf(" - Branch Hinting\n"); + printf(" - Custom Annotation Syntax in the Text Format\n"); + printf(" - Exception Handling\n"); + printf(" - JS String Builtins\n"); + printf(" - Relaxed SIMD\n"); +} diff --git a/product-mini/platforms/cosmopolitan/CMakeLists.txt b/product-mini/platforms/cosmopolitan/CMakeLists.txt new file mode 100644 index 0000000000..6b834369e6 --- /dev/null +++ b/product-mini/platforms/cosmopolitan/CMakeLists.txt @@ -0,0 +1,167 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# Copyright (C) 2023 Dylibso. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +project (iwasm) + +set (CMAKE_VERBOSE_MAKEFILE OFF) + +set (WAMR_BUILD_PLATFORM "cosmopolitan") + +set(CMAKE_EXECUTABLE_SUFFIX ".com") + +# 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 17) + +# 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_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple modules by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + 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) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + # Disable reference types by default + set (WAMR_BUILD_REF_TYPES 0) +endif () + +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +set (WAMR_DISABLE_STACK_HW_BOUND_CHECK 1) +set (WAMR_BUILD_AOT 0) +set (WAMR_DISABLE_WRITE_GS_BASE 1) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +check_pie_supported() +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_target_properties (vmlib PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_version_info (vmlib) + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#endif () +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +set_version_info (iwasm) + +set_target_properties (iwasm PROPERTIES POSITION_INDEPENDENT_CODE ON) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} ${WASI_NN_LIBS} -lm -ldl -lpthread) + +add_library (libiwasm STATIC ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (libiwasm) + +install (TARGETS libiwasm DESTINATION lib) + +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} ${WASI_NN_LIBS} -lm -ldl -lpthread) diff --git a/product-mini/platforms/cosmopolitan/build_cosmocc.sh b/product-mini/platforms/cosmopolitan/build_cosmocc.sh new file mode 100755 index 0000000000..8d96027e1d --- /dev/null +++ b/product-mini/platforms/cosmopolitan/build_cosmocc.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Copyright (C) 2023 Dylibso. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +export CC=x86_64-unknown-cosmo-cc +export CXX=x86_64-unknown-cosmo-c++ +rm -rf build +mkdir build +cmake -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_FAST_INTERP=1 -B build +cmake --build build -j diff --git a/product-mini/platforms/cosmopolitan/main.c b/product-mini/platforms/cosmopolitan/main.c new file mode 100644 index 0000000000..8f0e84a97f --- /dev/null +++ b/product-mini/platforms/cosmopolitan/main.c @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../posix/main.c" diff --git a/product-mini/platforms/darwin/CMakeLists.txt b/product-mini/platforms/darwin/CMakeLists.txt index b2c3ada7a7..8d19942813 100644 --- a/product-mini/platforms/darwin/CMakeLists.txt +++ b/product-mini/platforms/darwin/CMakeLists.txt @@ -1,10 +1,12 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8) +cmake_minimum_required (VERSION 3.14) project (iwasm) +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) + set (WAMR_BUILD_PLATFORM "darwin") # Reset default linker flags @@ -12,14 +14,21 @@ set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") # Set WAMR_BUILD_TARGET, currently values supported: -# "X86_64", "AMD_64", "X86_32", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +# "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_SIZEOF_VOID_P EQUAL 8) + 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") - else () + 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 () @@ -27,6 +36,8 @@ if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif () +set(CMAKE_CXX_STANDARD 17) + if (NOT DEFINED WAMR_BUILD_INTERP) # Enable Interpreter by default set (WAMR_BUILD_INTERP 1) @@ -42,36 +53,111 @@ if (NOT DEFINED WAMR_BUILD_JIT) set (WAMR_BUILD_JIT 0) endif () +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) # Enable libc builtin support by default set (WAMR_BUILD_LIBC_BUILTIN 1) endif () if (NOT DEFINED WAMR_BUILD_LIBC_WASI) - # Disable libc wasi support by default - set (WAMR_BUILD_LIBC_WASI 0) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple module by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) endif () +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + # Enable reference types by default + set (WAMR_BUILD_REF_TYPES 1) +endif () + +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-U,_get_ext_lib_export_apis") set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") set (CMAKE_MACOSX_RPATH True) +# if enable wasi-nn, both wasi-nn-backends and iwasm +# need to use same WAMR (dynamic) libraries +if (WAMR_BUILD_WASI_NN EQUAL 1) + set (BUILD_SHARED_LIBS ON) +endif () + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) -add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) -add_executable (iwasm main.c ext_lib_export.c) +set_version_info (iwasm) install (TARGETS iwasm DESTINATION bin) -target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) +target_link_libraries (iwasm vmlib) + +add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (vmlib) + +target_include_directories(vmlib INTERFACE + $ +) -add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +set (WAMR_PUBLIC_HEADERS + ${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 +) -install (TARGETS libiwasm DESTINATION lib) +set_target_properties (vmlib PROPERTIES + OUTPUT_NAME iwasm + PUBLIC_HEADER "${WAMR_PUBLIC_HEADERS}" +) -set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) -target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) +install (TARGETS vmlib + EXPORT iwasmTargets + DESTINATION lib + PUBLIC_HEADER DESTINATION include +) +install_iwasm_package () diff --git a/product-mini/platforms/darwin/build_jit.sh b/product-mini/platforms/darwin/build_jit.sh new file mode 100755 index 0000000000..a7d5591079 --- /dev/null +++ b/product-mini/platforms/darwin/build_jit.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -fr build && mkdir build +cd build +cmake .. -DWAMR_BUILD_JIT=1 +make -j ${nproc} +cd .. diff --git a/product-mini/platforms/darwin/build_llvm.sh b/product-mini/platforms/darwin/build_llvm.sh new file mode 100755 index 0000000000..b8a9761f85 --- /dev/null +++ b/product-mini/platforms/darwin/build_llvm.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Copyright (C) 2020 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +/usr/bin/env python3 -m pip install --user -r ../../../build-scripts/requirements.txt +/usr/bin/env python3 ../../../build-scripts/build_llvm.py --platform darwin "$@" diff --git a/product-mini/platforms/darwin/ext_lib_export.c b/product-mini/platforms/darwin/ext_lib_export.c deleted file mode 100644 index 8813f0dbf3..0000000000 --- a/product-mini/platforms/darwin/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/darwin/main.c b/product-mini/platforms/darwin/main.c index 5f0ba1525a..8f0e84a97f 100644 --- a/product-mini/platforms/darwin/main.c +++ b/product-mini/platforms/darwin/main.c @@ -3,304 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -#include -#include "bh_platform.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include "wasm_export.h" - -static int app_argc; -static char **app_argv; - -static int print_help() -{ - bh_printf("Usage: iwasm [-options] wasm_file [args...]\n"); - bh_printf("options:\n"); - bh_printf(" -f|--function name Specify function name to run in module\n" - " rather than main\n"); -#if WASM_ENABLE_LOG != 0 - bh_printf(" -v=X Set log verbose level (0 to 5, default is 2),\n" - " larger level with more log\n"); -#endif - bh_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 - bh_printf(" --env= Pass wasi environment variables with \"key=value\"\n"); - bh_printf(" to the program, for example:\n"); - bh_printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); - bh_printf(" --dir= Grant wasi access to the given host directories\n"); - bh_printf(" to the program, for example:\n"); - bh_printf(" --dir= --dir=\n"); -#endif - - return 1; -} - -static void* -app_instance_main(wasm_module_inst_t module_inst) -{ - const char *exception; - - wasm_application_execute_main(module_inst, app_argc, app_argv); - if ((exception = wasm_runtime_get_exception(module_inst))) - bh_printf("%s\n", exception); - return NULL; -} - -static void* -app_instance_func(wasm_module_inst_t module_inst, const char *func_name) -{ - wasm_application_execute_func(module_inst, func_name, app_argc - 1, - app_argv + 1); - /* The result of wasm function or exception info was output inside - wasm_application_execute_func(), here we don't output them again. */ - return NULL; -} - -/** - * Split a space separated strings into an array of strings - * Returns NULL on failure - * Memory must be freed by caller - * Based on: http://stackoverflow.com/a/11198630/471795 - */ -static char ** -split_string(char *str, int *count) -{ - char **res = NULL; - char *p; - int idx = 0; - - /* split string and append tokens to 'res' */ - do { - p = strtok(str, " "); - str = NULL; - res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1)); - if (res == NULL) { - return NULL; - } - res[idx++] = p; - } while (p); - - if (count) { - *count = idx - 1; - } - return res; -} - -static void* -app_instance_repl(wasm_module_inst_t module_inst) -{ - char *cmd = NULL; - size_t len = 0; - ssize_t n; - - while ((bh_printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { - bh_assert(n > 0); - if (cmd[n - 1] == '\n') { - if (n == 1) - continue; - else - cmd[n - 1] = '\0'; - } - app_argv = split_string(cmd, &app_argc); - if (app_argv == NULL) { - LOG_ERROR("Wasm prepare param failed: split string failed.\n"); - break; - } - if (app_argc != 0) { - wasm_application_execute_func(module_inst, app_argv[0], - app_argc - 1, app_argv + 1); - } - free(app_argv); - } - free(cmd); - return NULL; -} - -#if WASM_ENABLE_LIBC_WASI != 0 -static bool -validate_env_str(char *env) -{ - char *p = env; - int key_len = 0; - - while (*p != '\0' && *p != '=') { - key_len++; - p++; - } - - if (*p != '=' || key_len == 0) - return false; - - return true; -} -#endif - -#define USE_GLOBAL_HEAP_BUF 0 - -#if USE_GLOBAL_HEAP_BUF != 0 -static char global_heap_buf[10 * 1024 * 1024] = { 0 }; -#endif - -int main(int argc, char *argv[]) -{ - char *wasm_file = NULL; - const char *func_name = NULL; - uint8 *wasm_file_buf = NULL; - uint32 wasm_file_size; - wasm_module_t wasm_module = NULL; - wasm_module_inst_t wasm_module_inst = NULL; - char error_buf[128] = { 0 }; -#if WASM_ENABLE_LOG != 0 - int log_verbose_level = 2; -#endif - bool is_repl_mode = false; -#if WASM_ENABLE_LIBC_WASI != 0 - const char *dir_list[8] = { NULL }; - uint32 dir_list_size = 0; - const char *env_list[8] = { NULL }; - uint32 env_list_size = 0; -#endif - - /* Process options. */ - for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { - if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { - argc--, argv++; - if (argc < 2) { - print_help(); - return 0; - } - func_name = argv[0]; - } -#if WASM_ENABLE_LOG != 0 - else if (!strncmp(argv[0], "-v=", 3)) { - log_verbose_level = atoi(argv[0] + 3); - if (log_verbose_level < 0 || log_verbose_level > 5) - return print_help(); - } -#endif - else if (!strcmp(argv[0], "--repl")) - is_repl_mode = true; -#if WASM_ENABLE_LIBC_WASI != 0 - else if (!strncmp(argv[0], "--dir=", 6)) { - if (argv[0][6] == '\0') - return print_help(); - if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) { - bh_printf("Only allow max dir number %d\n", - (int)(sizeof(dir_list) / sizeof(char*))); - return -1; - } - dir_list[dir_list_size++] = argv[0] + 6; - } - else if (!strncmp(argv[0], "--env=", 6)) { - char *tmp_env; - - if (argv[0][6] == '\0') - return print_help(); - if (env_list_size >= sizeof(env_list) / sizeof(char*)) { - bh_printf("Only allow max env number %d\n", - (int)(sizeof(env_list) / sizeof(char*))); - return -1; - } - tmp_env = argv[0] + 6; - if (validate_env_str(tmp_env)) - env_list[env_list_size++] = tmp_env; - else { - bh_printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n", - tmp_env); - return print_help(); - } - } -#endif - else - return print_help(); - } - - if (argc == 0) - return print_help(); - - wasm_file = argv[0]; - app_argc = argc; - app_argv = argv; - -#if USE_GLOBAL_HEAP_BUF != 0 - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - bh_printf("Init memory with global heap buffer failed.\n"); - return -1; - } -#else - if (bh_memory_init_with_allocator(malloc, free)) { - bh_printf("Init memory with memory allocator failed.\n"); - return -1; - } -#endif - - /* initialize runtime environment */ - if (!wasm_runtime_init()) - goto fail1; - - bh_log_set_verbose_level(log_verbose_level); - - /* load WASM byte buffer from WASM bin file */ - if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file, - &wasm_file_size))) - goto fail2; - - /* load WASM module */ - if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, - error_buf, sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail3; - } - -#if WASM_ENABLE_LIBC_WASI != 0 - wasm_runtime_set_wasi_args(wasm_module, - dir_list, dir_list_size, - NULL, 0, - env_list, env_list_size, - argv, argc); -#endif - - /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, - 64 * 1024, /* stack size */ - 64 * 1024, /* heap size */ - error_buf, - sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail4; - } - - if (is_repl_mode) - app_instance_repl(wasm_module_inst); - else if (func_name) - app_instance_func(wasm_module_inst, func_name); - else - app_instance_main(wasm_module_inst); - - /* destroy the module instance */ - wasm_runtime_deinstantiate(wasm_module_inst); - -fail4: - /* unload the module */ - wasm_runtime_unload(wasm_module); - -fail3: - /* free the file buffer */ - bh_free(wasm_file_buf); - -fail2: - /* destroy runtime environment */ - wasm_runtime_destroy(); - -fail1: - bh_memory_destroy(); - return 0; -} - +#include "../posix/main.c" diff --git a/product-mini/platforms/esp-idf/.gitignore b/product-mini/platforms/esp-idf/.gitignore new file mode 100644 index 0000000000..c260f41fd2 --- /dev/null +++ b/product-mini/platforms/esp-idf/.gitignore @@ -0,0 +1,2 @@ +sdkconfig +sdkconfig.old \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/CMakeLists.txt b/product-mini/platforms/esp-idf/CMakeLists.txt new file mode 100644 index 0000000000..309949a20b --- /dev/null +++ b/product-mini/platforms/esp-idf/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (C) 2019-21 Intel Corporation and others. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# from ESP-IDF 4.0 examples/build_system/cmake/idf_as_lib +cmake_minimum_required(VERSION 3.14) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(wamr-simple) \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/README.md b/product-mini/platforms/esp-idf/README.md new file mode 100644 index 0000000000..eb71f09cd6 --- /dev/null +++ b/product-mini/platforms/esp-idf/README.md @@ -0,0 +1,91 @@ +# How to Use WAMR with ESP-IDF + +ESP-IDF is the official development framework for Espressif SoCs, supporting Windows, Linux, and macOS. WAMR (WebAssembly Micro Runtime) can be integrated as a standard [ESP-IDF](https://github.com/espressif/esp-idf) component. + +## 1. Setup the ESP-IDF Development Environment + +This example demonstrates how to use WAMR with ESP-IDF. Before proceeding, ensure you have the ESP-IDF development environment installed. For the relevant process, please refer to ESP-IDF [documents](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html). + +### Prerequisites + +#### Software Requirements + +* ESP-IDF v4.4.0 and above. + +#### Hardware Requirements + +* A development board with one of the following SoCs: + + - ESP32 + + - ESP32-C3 + + - ESP32-S3 + + - ESP32-C6 + + - ESP32-P4 + + - ESP32-C5 + +* See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +> Note: Different chips require different ESP-IDF versions, please check [ESP-IDF Release and SoC Compatibility](https://github.com/espressif/esp-idf?tab=readme-ov-file#esp-idf-release-and-soc-compatibility) before proceeding. + +### Installation Steps + +1. Navigate to the ESP-IDF root directory. + +2. Run the installation script based on your OS: + + - Linux/MacOS + + ``` + ./install.sh + ``` + + - Windows + + ``` + ./install.bat + ``` + +3. If successful, you should see: + + ``` + All done! You can now run: + + . ./export.sh + ``` + +## 2. Compiling and Running the Project + +### Set the Target Chip + +Switch to the project directory and specify the target chip: + +```bash +idf.py set-target +``` + +### Configure the project + +Open the configuration menu: + +```bash +idf.py menuconfig +``` + +To modify WAMR settings, navigate to: `Component config -> WASM Micro Runtime` + +### Build and Flash + +Run the following command to compile, flash, and monitor the application: + +```bash +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects. \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/build_and_run.sh b/product-mini/platforms/esp-idf/build_and_run.sh new file mode 100755 index 0000000000..4b8d248cb9 --- /dev/null +++ b/product-mini/platforms/esp-idf/build_and_run.sh @@ -0,0 +1,47 @@ +#!/bin/bash -e + +# Copyright (C) 2019-21 Intel Corporation and others. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +ESP32_TARGET="esp32" +ESP32C3_TARGET="esp32c3" +ESP32S3_TARGET="esp32s3" +ESP32C6_TARGET="esp32c6" +ESP32P4_TARGET="esp32p4" +ESP32C5_TARGET="esp32c5" + +usage () +{ + echo "USAGE:" + echo "$0 $ESP32_TARGET|$ESP32C3_TARGET|$ESP32S3_TARGET|$ESP32C6_TARGET|$ESP32P4_TARGET|$ESP32C5_TARGET" + echo "Example:" + echo " $0 $ESP32_TARGET" + echo " $0 $ESP32C3_TARGET" + echo " $0 $ESP32S3_TARGET" + echo " $0 $ESP32C6_TARGET" + echo " $0 $ESP32P4_TARGET" + echo " $0 $ESP32C5_TARGET" + exit 1 +} + +if [ $# != 1 ] ; then + usage +fi + +TARGET=$1 + +if [ "$TARGET" = "$ESP32C5_TARGET" ]; then + IDF_ST_CMD="idf.py --preview set-target $TARGET" +else + IDF_ST_CMD="idf.py set-target $TARGET" +fi + +if [[ -z "${WAMR_PATH}" ]]; then + export WAMR_PATH=$PWD/../../.. +fi + +rm -rf build +$IDF_ST_CMD +idf.py build +idf.py flash + diff --git a/product-mini/platforms/esp-idf/main/CMakeLists.txt b/product-mini/platforms/esp-idf/main/CMakeLists.txt new file mode 100644 index 0000000000..1bb61bad94 --- /dev/null +++ b/product-mini/platforms/esp-idf/main/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (C) 2021 Intel Corporation and others. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/product-mini/platforms/esp-idf/main/idf_component.yml b/product-mini/platforms/esp-idf/main/idf_component.yml new file mode 100644 index 0000000000..1c05f476e5 --- /dev/null +++ b/product-mini/platforms/esp-idf/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + wasm-micro-runtime: + version: "^2" + override_path: "../../../.." + idf: + version: ">=4.4" \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/main/main.c b/product-mini/platforms/esp-idf/main/main.c new file mode 100644 index 0000000000..1a34096d7a --- /dev/null +++ b/product-mini/platforms/esp-idf/main/main.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2019-21 Intel Corporation and others. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "wasm_export.h" +#include "bh_platform.h" +#include "test_wasm.h" + +#include "esp_log.h" + +#define IWASM_MAIN_STACK_SIZE 5120 + +#define LOG_TAG "wamr" + +static void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, 0, NULL); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +void * +iwasm_main(void *arg) +{ + (void)arg; /* unused */ + /* setup variables for instantiating and running the wasm module */ + uint8_t *wasm_file_buf = NULL; + unsigned wasm_file_buf_size = 0; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + char error_buf[128]; + void *ret; + RuntimeInitArgs init_args; + + /* configure memory allocation */ + memset(&init_args, 0, sizeof(RuntimeInitArgs)); +#if WASM_ENABLE_GLOBAL_HEAP_POOL == 0 + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = (void *)os_malloc; + init_args.mem_alloc_option.allocator.realloc_func = (void *)os_realloc; + init_args.mem_alloc_option.allocator.free_func = (void *)os_free; +#else +#error The usage of a global heap pool is not implemented yet for esp-idf. +#endif + + ESP_LOGI(LOG_TAG, "Initialize WASM runtime"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + ESP_LOGE(LOG_TAG, "Init runtime failed."); + return NULL; + } + +#if WASM_ENABLE_INTERP != 0 + ESP_LOGI(LOG_TAG, "Run wamr with interpreter"); + + wasm_file_buf = (uint8_t *)wasm_test_file_interp; + wasm_file_buf_size = sizeof(wasm_test_file_interp); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_buf_size, + error_buf, sizeof(error_buf)))) { + ESP_LOGE(LOG_TAG, "Error in wasm_runtime_load: %s", error_buf); + goto fail1interp; + } + + ESP_LOGI(LOG_TAG, "Instantiate WASM runtime"); + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, 32 * 1024, // stack size + 32 * 1024, // heap size + error_buf, sizeof(error_buf)))) { + ESP_LOGE(LOG_TAG, "Error while instantiating: %s", error_buf); + goto fail2interp; + } + + ESP_LOGI(LOG_TAG, "run main() of the application"); + ret = app_instance_main(wasm_module_inst); + assert(!ret); + + /* destroy the module instance */ + ESP_LOGI(LOG_TAG, "Deinstantiate WASM runtime"); + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2interp: + /* unload the module */ + ESP_LOGI(LOG_TAG, "Unload WASM module"); + wasm_runtime_unload(wasm_module); + +fail1interp: +#endif +#if WASM_ENABLE_AOT != 0 + ESP_LOGI(LOG_TAG, "Run wamr with AoT"); + + wasm_file_buf = (uint8_t *)wasm_test_file_aot; + wasm_file_buf_size = sizeof(wasm_test_file_aot); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_buf_size, + error_buf, sizeof(error_buf)))) { + ESP_LOGE(LOG_TAG, "Error in wasm_runtime_load: %s", error_buf); + goto fail1aot; + } + + ESP_LOGI(LOG_TAG, "Instantiate WASM runtime"); + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, 32 * 1024, // stack size + 32 * 1024, // heap size + error_buf, sizeof(error_buf)))) { + ESP_LOGE(LOG_TAG, "Error while instantiating: %s", error_buf); + goto fail2aot; + } + + ESP_LOGI(LOG_TAG, "run main() of the application"); + ret = app_instance_main(wasm_module_inst); + assert(!ret); + + /* destroy the module instance */ + ESP_LOGI(LOG_TAG, "Deinstantiate WASM runtime"); + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2aot: + /* unload the module */ + ESP_LOGI(LOG_TAG, "Unload WASM module"); + wasm_runtime_unload(wasm_module); +fail1aot: +#endif + + /* destroy runtime environment */ + ESP_LOGI(LOG_TAG, "Destroy WASM runtime"); + wasm_runtime_destroy(); + + return NULL; +} + +void +app_main(void) +{ + pthread_t t; + int res; + + pthread_attr_t tattr; + pthread_attr_init(&tattr); + pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); + pthread_attr_setstacksize(&tattr, IWASM_MAIN_STACK_SIZE); + + res = pthread_create(&t, &tattr, iwasm_main, (void *)NULL); + assert(res == 0); + + res = pthread_join(t, NULL); + assert(res == 0); + + ESP_LOGI(LOG_TAG, "Exiting..."); +} diff --git a/product-mini/platforms/esp-idf/main/test_wasm.h b/product-mini/platforms/esp-idf/main/test_wasm.h new file mode 100644 index 0000000000..fc2d76fdb7 --- /dev/null +++ b/product-mini/platforms/esp-idf/main/test_wasm.h @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2019-21 Intel Corporation and others. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if WASM_ENABLE_INTERP != 0 +unsigned char __aligned(4) wasm_test_file_interp[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x28, 0x0B, 0x7F, 0x00, 0x41, 0xBA, 0x08, 0x0B, + 0x7F, 0x00, 0x41, 0xC0, 0x28, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, + 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x04, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x04, 0x0A, 0xB2, 0x01, 0x01, 0xAF, 0x01, 0x01, 0x03, + 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x88, 0x80, 0x80, 0x00, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, + 0x41, 0xA8, 0x88, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1A, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6A, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, + 0x04, 0x20, 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x88, + 0x80, 0x80, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, + 0x8D, 0x88, 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x41, 0x93, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, + 0x80, 0x00, 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x0B, 0x0B, 0x41, 0x01, 0x00, 0x41, 0x80, 0x08, + 0x0B, 0x3A, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, + 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, + 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, + 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; +#endif + +#if WASM_ENABLE_AOT != 0 +#if BUILD_TARGET_XTENSA != 0 +// XTENSA +unsigned char __aligned(4) wasm_test_file_aot[] = { + 0x00, 0x61, 0x6F, 0x74, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x74, 0x65, 0x6E, 0x73, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, + 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, + 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, + 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, + 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x41, 0x00, 0x40, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x41, 0x00, 0x3A, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x41, 0x00, 0x40, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x65, 0x6E, 0x76, 0x00, 0x04, 0x00, 0x70, 0x75, 0x74, 0x73, 0x00, 0x00, + 0x03, 0x00, 0x65, 0x6E, 0x76, 0x00, 0x06, 0x00, 0x6D, 0x61, 0x6C, 0x6C, + 0x6F, 0x63, 0x01, 0x00, 0x03, 0x00, 0x65, 0x6E, 0x76, 0x00, 0x06, 0x00, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x02, 0x00, 0x03, 0x00, 0x65, 0x6E, + 0x76, 0x00, 0x04, 0x00, 0x66, 0x72, 0x65, 0x65, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x40, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xC1, 0x00, 0x98, + 0x62, 0x78, 0x22, 0x82, 0x27, 0x6A, 0x32, 0xC8, 0xE0, 0x0C, 0xD4, 0x37, + 0xB9, 0x08, 0xA8, 0x72, 0x0C, 0xEB, 0x37, 0xBA, 0x0F, 0x4D, 0x0B, 0x81, + 0xFF, 0xFF, 0xAD, 0x07, 0xBD, 0x04, 0xE0, 0x08, 0x00, 0x0C, 0x02, 0x1D, + 0xF0, 0xB9, 0x51, 0x49, 0x81, 0xA9, 0x61, 0x99, 0x91, 0x89, 0xC1, 0x68, + 0x32, 0x82, 0x27, 0x64, 0x89, 0xB1, 0x82, 0x27, 0x62, 0x89, 0x71, 0x82, + 0x27, 0x56, 0x89, 0xA1, 0x0C, 0x05, 0x32, 0x67, 0x6A, 0x52, 0x46, 0x03, + 0x52, 0x46, 0x02, 0x0C, 0x48, 0x89, 0xE1, 0x82, 0x46, 0x01, 0x1C, 0xB8, + 0x82, 0x46, 0x00, 0x0C, 0x1C, 0x41, 0xFF, 0xFF, 0xAD, 0x02, 0xBD, 0x05, + 0xDD, 0x06, 0xE0, 0x04, 0x00, 0x92, 0xA0, 0xFF, 0x90, 0x8A, 0x10, 0x16, + 0xA8, 0x1E, 0x0C, 0x05, 0x52, 0x46, 0x03, 0x52, 0x46, 0x02, 0x88, 0xE1, + 0x82, 0x46, 0x01, 0x52, 0x46, 0x00, 0x0C, 0x1B, 0xAD, 0x02, 0xCD, 0x0B, + 0x99, 0xD1, 0xDD, 0x06, 0xE0, 0x04, 0x00, 0x98, 0xD1, 0x90, 0x8A, 0x10, + 0x16, 0x58, 0x1C, 0x49, 0x41, 0xE8, 0x06, 0x16, 0xCE, 0x17, 0x88, 0xC1, + 0x82, 0xC8, 0xF0, 0x0C, 0x24, 0x37, 0xB8, 0x02, 0xC6, 0xDB, 0xFF, 0x98, + 0xB1, 0x87, 0xB9, 0x02, 0xC6, 0xD9, 0xFF, 0x98, 0xA1, 0x8A, 0x99, 0x1C, + 0x8B, 0x00, 0x0B, 0x40, 0xE0, 0xA0, 0x91, 0xA2, 0x49, 0x03, 0x1C, 0x0C, + 0x00, 0x0C, 0x40, 0xE0, 0xA0, 0x91, 0xA2, 0x49, 0x02, 0xE0, 0xA8, 0x41, + 0xA9, 0x01, 0xA2, 0x49, 0x01, 0xE2, 0x49, 0x00, 0xC9, 0x11, 0x00, 0x0C, + 0x40, 0x80, 0x90, 0x91, 0x92, 0x46, 0x06, 0xB9, 0x21, 0x00, 0x0B, 0x40, + 0x80, 0x90, 0x91, 0x92, 0x46, 0x07, 0x82, 0x46, 0x04, 0x80, 0x88, 0x41, + 0x82, 0x46, 0x05, 0x0C, 0x05, 0x52, 0x46, 0x02, 0x52, 0x46, 0x03, 0x52, + 0x46, 0x00, 0x88, 0xE1, 0x82, 0x46, 0x01, 0x0C, 0x24, 0xAD, 0x02, 0xBD, + 0x04, 0xCD, 0x04, 0xDD, 0x06, 0x88, 0x41, 0xE9, 0x31, 0xE0, 0x08, 0x00, + 0x88, 0xD1, 0x80, 0x8A, 0x10, 0x16, 0xC8, 0x13, 0xF8, 0x31, 0x4B, 0x8F, + 0x98, 0x71, 0x87, 0xB9, 0x02, 0x86, 0xBB, 0xFF, 0x92, 0xA4, 0x11, 0xD8, + 0xA1, 0x9A, 0x9D, 0x92, 0x09, 0x00, 0x8A, 0x8D, 0xA2, 0xA4, 0x12, 0xAA, + 0xAD, 0xA2, 0x0A, 0x00, 0xA2, 0x48, 0x01, 0x92, 0x48, 0x00, 0xE8, 0xB1, + 0xF7, 0xBE, 0x02, 0x06, 0xB3, 0xFF, 0x82, 0xA4, 0x0E, 0x8A, 0x8D, 0x82, + 0x08, 0x00, 0x92, 0xA4, 0x0D, 0x9A, 0x9D, 0x92, 0x09, 0x00, 0xA2, 0xA4, + 0x10, 0xAA, 0xAD, 0xA2, 0x0A, 0x00, 0xFA, 0xBD, 0xC2, 0xA4, 0x0F, 0xCA, + 0xCD, 0xC2, 0x0C, 0x00, 0xC2, 0x4B, 0x02, 0xA2, 0x4B, 0x03, 0x92, 0x4B, + 0x00, 0x82, 0x4B, 0x01, 0x37, 0xBE, 0x02, 0x06, 0xA6, 0xFF, 0x88, 0xA1, + 0x3A, 0x88, 0xA8, 0x21, 0x00, 0x0A, 0x40, 0xF0, 0x90, 0x91, 0x92, 0x48, + 0x03, 0x98, 0x11, 0x00, 0x09, 0x40, 0xF0, 0x90, 0x91, 0x92, 0x48, 0x02, + 0x98, 0x01, 0x92, 0x48, 0x01, 0xF2, 0x48, 0x00, 0x30, 0x80, 0x91, 0x82, + 0x46, 0x06, 0x00, 0x0A, 0x40, 0x30, 0x80, 0x91, 0x82, 0x46, 0x07, 0x32, + 0x46, 0x04, 0x30, 0x88, 0x41, 0x82, 0x46, 0x05, 0x0C, 0x05, 0x52, 0x46, + 0x02, 0x52, 0x46, 0x03, 0x1C, 0x38, 0x82, 0x46, 0x00, 0x88, 0xE1, 0x82, + 0x46, 0x01, 0x0C, 0x2B, 0xAD, 0x02, 0xCD, 0x0B, 0xDD, 0x06, 0x48, 0x41, + 0x3D, 0x0F, 0xE0, 0x04, 0x00, 0x98, 0xD1, 0x90, 0x8A, 0x10, 0x16, 0x78, + 0x07, 0x32, 0x46, 0x00, 0x88, 0x21, 0x00, 0x08, 0x40, 0x30, 0x80, 0x91, + 0x82, 0x46, 0x03, 0x88, 0x11, 0x00, 0x08, 0x40, 0x30, 0x80, 0x91, 0x82, + 0x46, 0x02, 0x88, 0x01, 0x82, 0x46, 0x01, 0x0C, 0x3B, 0x0C, 0x1C, 0xAD, + 0x02, 0x5D, 0x09, 0xDD, 0x06, 0xE0, 0x04, 0x00, 0x50, 0x8A, 0x10, 0x0C, + 0x05, 0x56, 0xB8, 0x02, 0x46, 0x10, 0x00, 0x0C, 0x05, 0x52, 0x46, 0x03, + 0x52, 0x46, 0x02, 0x88, 0xE1, 0x82, 0x46, 0x01, 0x2C, 0x88, 0x82, 0x46, + 0x00, 0x0C, 0x1C, 0xAD, 0x02, 0xBD, 0x05, 0x4D, 0x09, 0xDD, 0x06, 0x88, + 0x41, 0xE0, 0x08, 0x00, 0x40, 0x8A, 0x10, 0x16, 0xA8, 0x01, 0x7C, 0xF5, + 0x48, 0x81, 0x88, 0xC1, 0x98, 0x91, 0x87, 0x39, 0x02, 0x86, 0x72, 0xFF, + 0x48, 0x51, 0x98, 0x61, 0x87, 0xB9, 0x02, 0x06, 0x70, 0xFF, 0x82, 0x67, + 0x6A, 0x2D, 0x05, 0x1D, 0xF0, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x6D, 0x65, 0x6D, 0x6F, + 0x72, 0x79, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0A, 0x00, + 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xC8, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x2E, 0x72, + 0x65, 0x6C, 0x61, 0x2E, 0x74, 0x65, 0x78, 0x74, 0x08, 0x00, 0x2E, 0x6C, + 0x69, 0x74, 0x65, 0x72, 0x61, 0x6C, 0x0D, 0x00, 0x2E, 0x72, 0x65, 0x6C, + 0x61, 0x2E, 0x6C, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6C, 0x00, 0x11, 0x00, + 0x61, 0x6F, 0x74, 0x5F, 0x69, 0x6E, 0x76, 0x6F, 0x6B, 0x65, 0x5F, 0x6E, + 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, 0x19, 0x00, 0x61, 0x6F, 0x74, 0x5F, + 0x73, 0x65, 0x74, 0x5F, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, + 0x6E, 0x5F, 0x77, 0x69, 0x74, 0x68, 0x5F, 0x69, 0x64, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00 +}; +#else +// RISC-V +unsigned char __aligned(4) wasm_test_file_aot[] = { + 0x00, 0x61, 0x6F, 0x74, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xF3, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x69, 0x73, 0x63, 0x76, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x14, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, + 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, + 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, + 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, + 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x41, 0x00, 0x40, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x41, 0x00, 0x3A, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x41, 0x00, 0x40, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x65, 0x6E, 0x76, 0x00, 0x04, 0x00, 0x70, 0x75, 0x74, 0x73, 0x00, 0x00, + 0x03, 0x00, 0x65, 0x6E, 0x76, 0x00, 0x06, 0x00, 0x6D, 0x61, 0x6C, 0x6C, + 0x6F, 0x63, 0x01, 0x00, 0x03, 0x00, 0x65, 0x6E, 0x76, 0x00, 0x06, 0x00, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x02, 0x00, 0x03, 0x00, 0x65, 0x6E, + 0x76, 0x00, 0x04, 0x00, 0x66, 0x72, 0x65, 0x65, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x40, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x01, 0x01, 0xFC, 0x23, 0x2E, 0x11, 0x02, 0x23, 0x2C, 0x81, 0x02, + 0x23, 0x2A, 0x91, 0x02, 0x23, 0x28, 0x21, 0x03, 0x23, 0x26, 0x31, 0x03, + 0x23, 0x24, 0x41, 0x03, 0x23, 0x22, 0x51, 0x03, 0x23, 0x20, 0x61, 0x03, + 0x23, 0x2E, 0x71, 0x01, 0x23, 0x2C, 0x81, 0x01, 0x23, 0x2A, 0x91, 0x01, + 0x23, 0x28, 0xA1, 0x01, 0x23, 0x26, 0xB1, 0x01, 0x83, 0x29, 0x85, 0x00, + 0x03, 0xAA, 0x89, 0x1A, 0x83, 0x2A, 0x85, 0x01, 0x93, 0x0B, 0x0A, 0xFE, + 0x13, 0x04, 0xD0, 0x00, 0x63, 0xFA, 0x7A, 0x01, 0x93, 0x04, 0x05, 0x00, + 0x03, 0x2B, 0xC5, 0x01, 0x13, 0x04, 0xE0, 0x00, 0x63, 0x7A, 0x7B, 0x05, + 0x13, 0x85, 0x09, 0x00, 0x93, 0x05, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xE7, 0x80, 0x00, 0x00, 0x13, 0x05, 0x00, 0x00, 0x83, 0x2D, 0xC1, 0x00, + 0x03, 0x2D, 0x01, 0x01, 0x83, 0x2C, 0x41, 0x01, 0x03, 0x2C, 0x81, 0x01, + 0x83, 0x2B, 0xC1, 0x01, 0x03, 0x2B, 0x01, 0x02, 0x83, 0x2A, 0x41, 0x02, + 0x03, 0x2A, 0x81, 0x02, 0x83, 0x29, 0xC1, 0x02, 0x03, 0x29, 0x01, 0x03, + 0x83, 0x24, 0x41, 0x03, 0x03, 0x24, 0x81, 0x03, 0x83, 0x20, 0xC1, 0x03, + 0x13, 0x01, 0x01, 0x04, 0x67, 0x80, 0x00, 0x00, 0x03, 0xA9, 0xC4, 0x00, + 0x03, 0xAC, 0x89, 0x15, 0x83, 0xAD, 0x89, 0x18, 0x83, 0xAC, 0x09, 0x19, + 0x23, 0xA4, 0x79, 0x1B, 0xA3, 0x01, 0x09, 0x00, 0x23, 0x01, 0x09, 0x00, + 0x13, 0x04, 0x40, 0x00, 0xA3, 0x00, 0x89, 0x00, 0x13, 0x05, 0xB0, 0x01, + 0x23, 0x00, 0xA9, 0x00, 0x13, 0x06, 0x10, 0x00, 0x13, 0x85, 0x04, 0x00, + 0x93, 0x05, 0x00, 0x00, 0x93, 0x06, 0x09, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xE7, 0x80, 0x00, 0x00, 0x13, 0x75, 0xF5, 0x0F, 0xE3, 0x0C, 0x05, 0xF6, + 0xA3, 0x01, 0x09, 0x00, 0x23, 0x01, 0x09, 0x00, 0xA3, 0x00, 0x89, 0x00, + 0x23, 0x00, 0x09, 0x00, 0x93, 0x05, 0x10, 0x00, 0x13, 0x06, 0x10, 0x00, + 0x13, 0x85, 0x04, 0x00, 0x93, 0x06, 0x09, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xE7, 0x80, 0x00, 0x00, 0x13, 0x75, 0xF5, 0x0F, 0xE3, 0x04, 0x05, 0xF4, + 0x03, 0x2D, 0x09, 0x00, 0x63, 0x08, 0x0D, 0x18, 0x13, 0x05, 0x0A, 0xFF, + 0xB3, 0x35, 0x75, 0x01, 0x33, 0xB6, 0xAC, 0x00, 0xB3, 0xE5, 0xC5, 0x00, + 0x13, 0x04, 0x20, 0x00, 0xE3, 0x9C, 0x05, 0xF0, 0xB3, 0x05, 0xAC, 0x00, + 0x13, 0x56, 0x8D, 0x01, 0x23, 0x24, 0xC1, 0x00, 0xA3, 0x81, 0xC5, 0x00, + 0x13, 0x56, 0x0D, 0x01, 0x23, 0x22, 0xC1, 0x00, 0x23, 0x81, 0xC5, 0x00, + 0x13, 0x56, 0x8D, 0x00, 0x23, 0x20, 0xC1, 0x00, 0xA3, 0x80, 0xC5, 0x00, + 0x23, 0x80, 0xA5, 0x01, 0x23, 0x01, 0x09, 0x00, 0xA3, 0x01, 0x09, 0x00, + 0x23, 0x00, 0x09, 0x00, 0x93, 0x05, 0x40, 0x00, 0xA3, 0x00, 0xB9, 0x00, + 0x93, 0x55, 0x05, 0x01, 0x23, 0x03, 0xB9, 0x00, 0x93, 0x55, 0x85, 0x01, + 0xA3, 0x03, 0xB9, 0x00, 0x23, 0x02, 0xA9, 0x00, 0x13, 0x55, 0x85, 0x00, + 0xA3, 0x02, 0xA9, 0x00, 0x93, 0x05, 0x20, 0x00, 0x13, 0x06, 0x20, 0x00, + 0x13, 0x04, 0x20, 0x00, 0x13, 0x85, 0x04, 0x00, 0x93, 0x06, 0x09, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xE7, 0x80, 0x00, 0x00, 0x13, 0x75, 0xF5, 0x0F, + 0xE3, 0x04, 0x05, 0xEA, 0x13, 0x05, 0x4D, 0x00, 0xE3, 0xE8, 0xAD, 0xE8, + 0x83, 0x05, 0x2C, 0x41, 0x03, 0x46, 0x1C, 0x41, 0x33, 0x05, 0xAC, 0x00, + 0xA3, 0x00, 0xB5, 0x00, 0x23, 0x00, 0xC5, 0x00, 0xE3, 0xEC, 0xAC, 0xE7, + 0x03, 0x45, 0xEC, 0x40, 0x83, 0x45, 0xFC, 0x40, 0x03, 0x46, 0x0C, 0x41, + 0x83, 0x46, 0xDC, 0x40, 0x33, 0x07, 0xAC, 0x01, 0x23, 0x01, 0xB7, 0x00, + 0xA3, 0x01, 0xC7, 0x00, 0x23, 0x00, 0xD7, 0x00, 0xA3, 0x00, 0xA7, 0x00, + 0xE3, 0xE8, 0x7C, 0xE5, 0x33, 0x05, 0x7C, 0x01, 0x03, 0x24, 0x81, 0x00, + 0xA3, 0x01, 0x85, 0x00, 0x03, 0x2C, 0x41, 0x00, 0x23, 0x01, 0x85, 0x01, + 0x83, 0x2C, 0x01, 0x00, 0xA3, 0x00, 0x95, 0x01, 0x23, 0x00, 0xA5, 0x01, + 0x23, 0x01, 0x09, 0x00, 0xA3, 0x01, 0x09, 0x00, 0x13, 0x05, 0x30, 0x01, + 0x23, 0x00, 0xA9, 0x00, 0x13, 0x05, 0x40, 0x00, 0xA3, 0x00, 0xA9, 0x00, + 0x13, 0xD5, 0x0B, 0x01, 0x23, 0x03, 0xA9, 0x00, 0x13, 0xD5, 0x8B, 0x01, + 0xA3, 0x03, 0xA9, 0x00, 0x23, 0x02, 0x79, 0x01, 0x13, 0xD5, 0x8B, 0x00, + 0xA3, 0x02, 0xA9, 0x00, 0x93, 0x05, 0x20, 0x00, 0x13, 0x06, 0x20, 0x00, + 0x13, 0x85, 0x04, 0x00, 0x93, 0x06, 0x09, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xE7, 0x80, 0x00, 0x00, 0x13, 0x75, 0xF5, 0x0F, 0xE3, 0x06, 0x05, 0xDE, + 0x23, 0x00, 0xA9, 0x01, 0xA3, 0x01, 0x89, 0x00, 0x23, 0x01, 0x89, 0x01, + 0xA3, 0x00, 0x99, 0x01, 0x93, 0x05, 0x30, 0x00, 0x13, 0x06, 0x10, 0x00, + 0x13, 0x85, 0x04, 0x00, 0x93, 0x06, 0x09, 0x00, 0x97, 0x00, 0x00, 0x00, + 0xE7, 0x80, 0x00, 0x00, 0x93, 0x75, 0xF5, 0x0F, 0x13, 0x05, 0x00, 0x00, + 0x63, 0x92, 0x05, 0x04, 0x6F, 0xF0, 0x9F, 0xDB, 0xA3, 0x01, 0x09, 0x00, + 0x23, 0x01, 0x09, 0x00, 0x13, 0x05, 0x40, 0x00, 0xA3, 0x00, 0xA9, 0x00, + 0x13, 0x05, 0x80, 0x02, 0x23, 0x00, 0xA9, 0x00, 0x13, 0x06, 0x10, 0x00, + 0x13, 0x85, 0x04, 0x00, 0x93, 0x05, 0x00, 0x00, 0x93, 0x06, 0x09, 0x00, + 0x97, 0x00, 0x00, 0x00, 0xE7, 0x80, 0x00, 0x00, 0x93, 0x75, 0xF5, 0x0F, + 0x13, 0x05, 0xF0, 0xFF, 0xE3, 0x8C, 0x05, 0xD6, 0x13, 0x04, 0xD0, 0x00, + 0xE3, 0xF0, 0x4A, 0xD7, 0x13, 0x04, 0xE0, 0x00, 0xE3, 0x6C, 0x4B, 0xD5, + 0x23, 0xA4, 0x49, 0x1B, 0x6F, 0xF0, 0x5F, 0xD6, 0x03, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, 0x6D, 0x65, 0x6D, 0x6F, + 0x72, 0x79, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x6D, 0x61, 0x69, 0x6E, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0A, 0x00, + 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0B, 0x00, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xCC, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x2E, 0x72, 0x65, 0x6C, 0x61, 0x2E, 0x74, 0x65, 0x78, 0x74, + 0x19, 0x00, 0x61, 0x6F, 0x74, 0x5F, 0x73, 0x65, 0x74, 0x5F, 0x65, 0x78, + 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x5F, 0x77, 0x69, 0x74, 0x68, + 0x5F, 0x69, 0x64, 0x00, 0x11, 0x00, 0x61, 0x6F, 0x74, 0x5F, 0x69, 0x6E, + 0x76, 0x6F, 0x6B, 0x65, 0x5F, 0x6E, 0x61, 0x74, 0x69, 0x76, 0x65, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xBC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xA8, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xE8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00 +}; +#endif +#endif \ No newline at end of file diff --git a/product-mini/platforms/esp-idf/sdkconfig.defaults b/product-mini/platforms/esp-idf/sdkconfig.defaults new file mode 100644 index 0000000000..bb158eea93 --- /dev/null +++ b/product-mini/platforms/esp-idf/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +# CONFIG_ESP_SYSTEM_MEMPROT_FEATURE is not set diff --git a/product-mini/platforms/esp-idf/sdkconfig.defaults.esp32 b/product-mini/platforms/esp-idf/sdkconfig.defaults.esp32 new file mode 100644 index 0000000000..538ea3da1f --- /dev/null +++ b/product-mini/platforms/esp-idf/sdkconfig.defaults.esp32 @@ -0,0 +1 @@ +# CONFIG_ESP32_MEMPROT_FEATURE is not set diff --git a/product-mini/platforms/esp-idf/sdkconfig.defaults.esp32c3 b/product-mini/platforms/esp-idf/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000000..29b82c4b8f --- /dev/null +++ b/product-mini/platforms/esp-idf/sdkconfig.defaults.esp32c3 @@ -0,0 +1 @@ +# CONFIG_ESP32C3_MEMPROT_FEATURE is not set diff --git a/product-mini/platforms/freebsd/CMakeLists.txt b/product-mini/platforms/freebsd/CMakeLists.txt new file mode 100644 index 0000000000..fccc523236 --- /dev/null +++ b/product-mini/platforms/freebsd/CMakeLists.txt @@ -0,0 +1,137 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "freebsd") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# 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(CMAKE_CXX_STANDARD 17) + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple module by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +set (WAMR_DISABLE_HW_BOUND_CHECK 1) + +set (CMAKE_SHARED_LINKER_FLAGS "-Wl") +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + +set (CMAKE_MACOSX_RPATH True) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_version_info (vmlib) + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +set_version_info (iwasm) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) + +add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (libiwasm) + +install (TARGETS libiwasm DESTINATION lib) + +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) + diff --git a/product-mini/platforms/freebsd/build_jit.sh b/product-mini/platforms/freebsd/build_jit.sh new file mode 100755 index 0000000000..e6bee753c2 --- /dev/null +++ b/product-mini/platforms/freebsd/build_jit.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -fr build && mkdir build +cd build +cmake .. -DWAMR_BUILD_JIT=1 +nproc=$(sysctl -n hw.ncpu) +make -j ${nproc} +cd .. diff --git a/product-mini/platforms/freebsd/build_llvm.sh b/product-mini/platforms/freebsd/build_llvm.sh new file mode 100755 index 0000000000..c5666b7f5d --- /dev/null +++ b/product-mini/platforms/freebsd/build_llvm.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Copyright (C) 2020 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +/usr/bin/env python3 -m pip install --user -r ../../../build-scripts/requirements.txt +/usr/bin/env python3 ../../../build-scripts/build_llvm.py "$@" diff --git a/product-mini/platforms/freebsd/main.c b/product-mini/platforms/freebsd/main.c new file mode 100644 index 0000000000..8f0e84a97f --- /dev/null +++ b/product-mini/platforms/freebsd/main.c @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../posix/main.c" diff --git a/product-mini/platforms/ios/CMakeLists.txt b/product-mini/platforms/ios/CMakeLists.txt new file mode 100644 index 0000000000..80c618e8d2 --- /dev/null +++ b/product-mini/platforms/ios/CMakeLists.txt @@ -0,0 +1,137 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.21) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "darwin") +set (WAMR_BUILD_TYPE Release) +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_LIBC_WASI 1) + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# 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(CMAKE_CXX_STANDARD 17) + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple module by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE") + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#endif () +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") + +add_library (iwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +if (CMAKE_BUILD_TYPE STREQUAL Release) +target_link_libraries (iwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -s) +else() +target_link_libraries (iwasm ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl) +endif() + +set (distribution_DIR ${CMAKE_BINARY_DIR}/distribution) +set_target_properties (iwasm PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${distribution_DIR}/wasm/lib") +set_version_info (iwasm) + +add_custom_command (TARGET iwasm POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${WAMR_ROOT_DIR}/core/iwasm/include" "${distribution_DIR}/wasm/include/" + COMMENT "Copying iwasm to output directory") diff --git a/product-mini/platforms/ios/generate_xcodeproj.sh b/product-mini/platforms/ios/generate_xcodeproj.sh new file mode 100755 index 0000000000..168608168f --- /dev/null +++ b/product-mini/platforms/ios/generate_xcodeproj.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Copyright (C) 2022 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -rf ./iwasm-proj +git clone https://github.com/leetal/ios-cmake.git ios-cmake +cmake -Biwasm-proj -G Xcode -DDEPLOYMENT_TARGET=11.0 -DPLATFORM=OS64 -DENABLE_BITCODE=0 -DCMAKE_TOOLCHAIN_FILE=ios-cmake/ios.toolchain.cmake . diff --git a/product-mini/platforms/linux-sgx/CMakeLists.txt b/product-mini/platforms/linux-sgx/CMakeLists.txt index 9e373a7f91..dd2dc39197 100644 --- a/product-mini/platforms/linux-sgx/CMakeLists.txt +++ b/product-mini/platforms/linux-sgx/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8) +cmake_minimum_required (VERSION 3.14) project (iwasm) @@ -16,9 +16,11 @@ if (NOT DEFINED WAMR_BUILD_TARGET) if (CMAKE_SIZEOF_VOID_P EQUAL 8) # Build as X86_64 by default in 64-bit platform set (WAMR_BUILD_TARGET "X86_64") - else () + 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 () @@ -32,8 +34,9 @@ if (NOT DEFINED WAMR_BUILD_INTERP) endif () if (NOT DEFINED WAMR_BUILD_AOT) - # Disable AOT by default. - set (WAMR_BUILD_AOT 0) + # Enable AOT by default + # Please install Intel SGX SDKv2.8 or later. + set (WAMR_BUILD_AOT 1) endif () if (NOT DEFINED WAMR_BUILD_JIT) @@ -41,75 +44,148 @@ if (NOT DEFINED WAMR_BUILD_JIT) set (WAMR_BUILD_JIT 0) endif () +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) # Enable libc builtin support by default set (WAMR_BUILD_LIBC_BUILTIN 1) endif () if (NOT DEFINED WAMR_BUILD_LIBC_WASI) - # Disable libc wasi support by default - set (WAMR_BUILD_LIBC_WASI 0) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_RATS) + # Disable lib rats support by default + set (WAMR_BUILD_LIB_RATS 0) +endif() + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Enable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Disable SIMD by default + set (WAMR_BUILD_SIMD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SGX_IPFS) + # Disable SGX IPFS by default + set (WAMR_BUILD_SGX_IPFS 0) +endif () + +if (NOT DEFINED WAMR_BUILD_STATIC_PGO) + # Disable static PGO by default + set (WAMR_BUILD_STATIC_PGO 0) +endif () + +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + # Enable reference types by default + set (WAMR_BUILD_REF_TYPES 1) 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 \ +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11 -ffunction-sections -fdata-sections \ -Wall -Wno-unused-parameter -Wno-pedantic \ -nostdinc -fvisibility=hidden -fpie" ) set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) -set (SHARED_DIR ${WAMR_ROOT_DIR}/core/shared) -set (IWASM_DIR ${WAMR_ROOT_DIR}/core/iwasm) -set (APP_FRAMEWORK_DIR ${WAMR_ROOT_DIR}/core/app-framework) -# include the build config template file -include (${WAMR_ROOT_DIR}/build-scripts/config_common.cmake) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_version_info (vmlib) -if ("$ENV{SGX_SDK}" STREQUAL "") - set (SGX_SDK_DIR "/opt/intel/sgxsdk") -else() - set (SGX_SDK_DIR $ENV{SGX_SDK}) -endif() +add_custom_command ( + OUTPUT libvmlib_untrusted.a + COMMAND mkdir -p untrusted && cd untrusted && + ${CMAKE_C_COMPILER} -c ${PLATFORM_SHARED_SOURCE_UNTRUSTED} + COMMAND ${CMAKE_AR} rc libvmlib_untrusted.a untrusted/*.o) -include_directories (${SHARED_DIR}/include - ${IWASM_DIR}/include - ${SGX_SDK_DIR}/include - ${SGX_SDK_DIR}/include/tlibc - ${SGX_SDK_DIR}/include/libcxx) +add_custom_target (vmlib_untrusted ALL DEPENDS libvmlib_untrusted.a) -enable_language (ASM) +if ((WAMR_BUILD_STATIC_PGO EQUAL 1) AND (WAMR_BUILD_AOT EQUAL 1)) + execute_process( + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_STATIC_PGO = 0/WAMR_BUILD_STATIC_PGO = 1/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +else() + execute_process( + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_STATIC_PGO = 1/WAMR_BUILD_STATIC_PGO = 0/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +endif() -include (${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) -include (${SHARED_DIR}/mem-alloc/mem_alloc.cmake) -include (${SHARED_DIR}/utils/shared_utils.cmake) -if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) - include (${IWASM_DIR}/libraries/libc-builtin/libc_builtin.cmake) -endif () -if (WAMR_BUILD_LIBC_WASI EQUAL 1) - include (${IWASM_DIR}/libraries/libc-wasi/libc_wasi.cmake) -endif () +if (DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) + execute_process( + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_GLOBAL_HEAP_POOL = .*/WAMR_BUILD_GLOBAL_HEAP_POOL = ${WAMR_BUILD_GLOBAL_HEAP_POOL}/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) + if (DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + execute_process( + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_GLOBAL_HEAP_SIZE = .*/WAMR_BUILD_GLOBAL_HEAP_SIZE = ${WAMR_BUILD_GLOBAL_HEAP_SIZE}/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) + endif() +endif() -include (${IWASM_DIR}/common/iwasm_common.cmake) +if (WAMR_BUILD_LIB_RATS EQUAL 1) + execute_process( + COMMAND bash -c "sed -i -E 's/^#define WASM_ENABLE_LIB_RATS 0/#define WASM_ENABLE_LIB_RATS 1/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Enclave/Enclave.edl" + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_LIB_RATS = 0/WAMR_BUILD_LIB_RATS = 1/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +else() + execute_process( + COMMAND bash -c "sed -i -E 's/^#define WASM_ENABLE_LIB_RATS 1/#define WASM_ENABLE_LIB_RATS 0/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Enclave/Enclave.edl" + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_LIB_RATS = 1/WAMR_BUILD_LIB_RATS = 0/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +endif() -if (WAMR_BUILD_INTERP EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) - include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) -endif () +if (WAMR_BUILD_SGX_IPFS EQUAL 1) + execute_process( + COMMAND bash -c "sed -i -E 's/^#define WASM_ENABLE_SGX_IPFS 0/#define WASM_ENABLE_SGX_IPFS 1/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Enclave/Enclave.edl" + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_SGX_IPFS = 0/WAMR_BUILD_SGX_IPFS = 1/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +else() + execute_process( + COMMAND bash -c "sed -i -E 's/^#define WASM_ENABLE_SGX_IPFS 1/#define WASM_ENABLE_SGX_IPFS 0/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Enclave/Enclave.edl" + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_SGX_IPFS = 1/WAMR_BUILD_SGX_IPFS = 0/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +endif() -if (WAMR_BUILD_AOT EQUAL 1) - include (${IWASM_DIR}/aot/iwasm_aot.cmake) - if (WAMR_BUILD_JIT EQUAL 1) - include (${IWASM_DIR}/compilation/iwasm_compl.cmake) - endif () -endif () +if (WAMR_BUILD_LIBC_WASI EQUAL 1) + execute_process( + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_LIBC_WASI = 0/WAMR_BUILD_LIBC_WASI = 1/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +else() + execute_process( + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_LIBC_WASI = 1/WAMR_BUILD_LIBC_WASI = 0/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput + ) +endif() -add_library (vmlib - ${PLATFORM_SHARED_SOURCE} - ${MEM_ALLOC_SHARED_SOURCE} - ${UTILS_SHARED_SOURCE} - ${LIBC_BUILTIN_SOURCE} - ${LIBC_WASI_SOURCE} - ${IWASM_COMMON_SOURCE} - ${IWASM_INTERP_SOURCE} - ${IWASM_AOT_SOURCE} - ${IWASM_COMPL_SOURCE}) - -add_library (extlib ext_lib_export.c) +if (WAMR_BUILD_SPEC_TEST EQUAL 1) + execute_process( + COMMAND bash -c "sed -i -E 's/0x1000000<\\/ReservedMemMaxSize>/0x8000000<\\/ReservedMemMaxSize>/g' ${CMAKE_CURRENT_SOURCE_DIR}/enclave-sample/Enclave/Enclave.config.xml" + OUTPUT_VARIABLE cmdOutput + ) +endif() diff --git a/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt b/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt new file mode 100644 index 0000000000..5104769d73 --- /dev/null +++ b/product-mini/platforms/linux-sgx/CMakeLists_minimal.txt @@ -0,0 +1,89 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "linux-sgx") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET +if (NOT DEFINED WAMR_BUILD_TARGET) + if (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_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default + # Please install Intel SGX SDKv2.8 or later. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Enable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Enable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +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 \ + -nostdinc -fvisibility=hidden -fpie" ) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_version_info (vmlib) + +add_custom_command ( + OUTPUT libvmlib_untrusted.a + COMMAND mkdir -p untrusted && cd untrusted && + ${CMAKE_C_COMPILER} -c ${PLATFORM_SHARED_SOURCE_UNTRUSTED} + COMMAND ${CMAKE_AR} rc libvmlib_untrusted.a untrusted/*.o) + +add_custom_target (vmlib_untrusted ALL DEPENDS libvmlib_untrusted.a) diff --git a/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp b/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp index 9c4dcc5c92..fc997868bd 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp +++ b/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp @@ -4,14 +4,19 @@ */ #include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include #include "Enclave_u.h" #include "sgx_urts.h" +#include "pal_api.h" #ifndef TRUE #define TRUE 1 @@ -21,22 +26,44 @@ #define FALSE 0 #endif -#define TOKEN_FILENAME "enclave.token" +#define TOKEN_FILENAME "enclave.token" #define ENCLAVE_FILENAME "enclave.signed.so" -#define MAX_PATH FILENAME_MAX +#define MAX_PATH 1024 + +#define TEST_OCALL_API 0 -sgx_enclave_id_t g_eid = 0; +static sgx_enclave_id_t g_eid = 0; -void -ocall_print(const char* str) +sgx_enclave_id_t +pal_get_enclave_id(void) { - printf("%s", str); + return g_eid; } -static void -print_error_message(sgx_status_t ret) +int +ocall_print(const char *str) { - printf("SGX error code: %d\n", ret); + return printf("%s", str); +} + +static char * +get_exe_path(char *path_buf, unsigned path_buf_size) +{ + ssize_t i; + ssize_t size = readlink("/proc/self/exe", path_buf, path_buf_size - 1); + + if (size < 0 || (size >= path_buf_size - 1)) { + return NULL; + } + + path_buf[size] = '\0'; + for (i = size - 1; i >= 0; i--) { + if (path_buf[i] == '/') { + path_buf[i + 1] = '\0'; + break; + } + } + return path_buf; } /* Initialize the enclave: @@ -48,22 +75,35 @@ static int enclave_init(sgx_enclave_id_t *p_eid) { - char token_path[MAX_PATH] = {'\0'}; - sgx_launch_token_t token = {0}; + char token_path[MAX_PATH] = { '\0' }; + char enclave_path[MAX_PATH] = { '\0' }; + const char *home_dir; + sgx_launch_token_t token = { 0 }; sgx_status_t ret = SGX_ERROR_UNEXPECTED; int updated = 0; + size_t write_num, enc_file_len; + FILE *fp; - /* Step 1: try to retrieve the launch token saved by last transaction + enc_file_len = strlen(ENCLAVE_FILENAME); + if (!get_exe_path(enclave_path, sizeof(enclave_path) - enc_file_len)) { + printf("Failed to get exec path\n"); + return -1; + } + memcpy(enclave_path + strlen(enclave_path), ENCLAVE_FILENAME, enc_file_len); + + /* Step 1: try to retrieve the launch token saved by last transaction * if there is no token, then create a new one. */ /* try to get the token saved in $HOME */ - const char *home_dir = getpwuid(getuid())->pw_dir; + home_dir = getpwuid(getuid())->pw_dir; + size_t home_dir_len = home_dir ? strlen(home_dir) : 0; - if (home_dir != NULL && - (strlen(home_dir) + strlen("/") + sizeof(TOKEN_FILENAME) + 1) <= MAX_PATH) { + if (home_dir != NULL + && home_dir_len + <= MAX_PATH - 1 - sizeof(TOKEN_FILENAME) - strlen("/")) { /* compose the token path */ - strncpy(token_path, home_dir, strlen(home_dir)); - strncat(token_path, "/", strlen("/")); + strncpy(token_path, home_dir, MAX_PATH); + strncat(token_path, "/", strlen("/") + 1); strncat(token_path, TOKEN_FILENAME, sizeof(TOKEN_FILENAME) + 1); } else { @@ -71,9 +111,10 @@ enclave_init(sgx_enclave_id_t *p_eid) strncpy(token_path, TOKEN_FILENAME, sizeof(TOKEN_FILENAME)); } - FILE *fp = fopen(token_path, "rb"); + fp = fopen(token_path, "rb"); if (fp == NULL && (fp = fopen(token_path, "wb")) == NULL) { - printf("Warning: Failed to create/open the launch token file \"%s\".\n", token_path); + printf("Warning: Failed to create/open the launch token file \"%s\".\n", + token_path); } if (fp != NULL) { @@ -82,15 +123,22 @@ enclave_init(sgx_enclave_id_t *p_eid) if (read_num != 0 && read_num != sizeof(sgx_launch_token_t)) { /* if token is invalid, clear the buffer */ memset(&token, 0x0, sizeof(sgx_launch_token_t)); - printf("Warning: Invalid launch token read from \"%s\".\n", token_path); + printf("Warning: Invalid launch token read from \"%s\".\n", + token_path); } } /* Step 2: call sgx_create_enclave to initialize an enclave instance */ /* Debug Support: set 2nd parameter to 1 */ - ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, &token, &updated, p_eid, NULL); + ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, &token, &updated, + p_eid, NULL); + if (ret != SGX_SUCCESS) + /* Try to load enclave.sign.so from the path of exe file */ + ret = sgx_create_enclave(enclave_path, SGX_DEBUG_FLAG, &token, &updated, + p_eid, NULL); if (ret != SGX_SUCCESS) { - print_error_message(ret); + printf("Failed to create enclave from %s, error code: %d\n", + ENCLAVE_FILENAME, ret); if (fp != NULL) fclose(fp); return -1; @@ -98,17 +146,19 @@ enclave_init(sgx_enclave_id_t *p_eid) /* Step 3: save the launch token if it is updated */ if (updated == FALSE || fp == NULL) { - /* if the token is not updated, or file handler is invalid, do not perform saving */ - if (fp != NULL) fclose(fp); + /* if the token is not updated, or file handler is invalid, + do not perform saving */ + if (fp != NULL) + fclose(fp); return 0; } - /* reopen the file with write capablity */ + /* reopen the file with write capability */ fp = freopen(token_path, "wb", fp); if (fp == NULL) return 0; - size_t write_num = fwrite(token, 1, sizeof(sgx_launch_token_t), fp); + write_num = fwrite(token, 1, sizeof(sgx_launch_token_t), fp); if (write_num != sizeof(sgx_launch_token_t)) printf("Warning: Failed to save launch token to \"%s\".\n", token_path); @@ -116,15 +166,894 @@ enclave_init(sgx_enclave_id_t *p_eid) return 0; } +static unsigned char * +read_file_to_buffer(const char *filename, uint32_t *ret_size) +{ + unsigned char *buffer; + FILE *file; + int file_size, read_size; + + if (!filename || !ret_size) { + printf("Read file to buffer failed: invalid filename or ret size.\n"); + return NULL; + } + + if (!(file = fopen(filename, "r"))) { + printf("Read file to buffer failed: open file %s failed.\n", filename); + return NULL; + } + + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (!(buffer = (unsigned char *)malloc(file_size))) { + printf("Read file to buffer failed: alloc memory failed.\n"); + fclose(file); + return NULL; + } + + read_size = fread(buffer, 1, file_size, file); + fclose(file); + + if (read_size < file_size) { + printf("Read file to buffer failed: read file content failed.\n"); + free(buffer); + return NULL; + } + + *ret_size = file_size; + + return buffer; +} + +/* clang-format off */ +static int +print_help() +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); + printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" + " level with more log\n"); + 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"); + printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" + " that runs commands in the form of `FUNC ARG...`\n"); + printf(" --env= Pass wasi environment variables with \"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); + printf(" --dir= Grant wasi access to the given host directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); + printf(" --addr-pool= Grant wasi access to the given network addresses in\n"); + printf(" CIDR notation to the program, separated with ',',\n"); + printf(" for example:\n"); + printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n"); + printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); +#if WASM_ENABLE_STATIC_PGO != 0 + printf(" --gen-prof-file= Generate LLVM PGO (Profile-Guided Optimization) profile file\n"); +#endif + printf(" --version Show version information\n"); + return 1; +} +/* clang-format on */ + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, " "); + str = NULL; + res = (char **)realloc(res, sizeof(char *) * (unsigned)(idx + 1)); + if (res == NULL) { + return NULL; + } + res[idx++] = p; + } while (p); + + /** + * since the function name, + * res[0] might be contains a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + + if (count) { + *count = idx - 1; + } + return res; +} + +typedef enum EcallCmd { + CMD_INIT_RUNTIME = 0, /* wasm_runtime_init/full_init() */ + CMD_LOAD_MODULE, /* wasm_runtime_load() */ + CMD_INSTANTIATE_MODULE, /* wasm_runtime_instantiate() */ + CMD_LOOKUP_FUNCTION, /* wasm_runtime_lookup_function() */ + CMD_CREATE_EXEC_ENV, /* wasm_runtime_create_exec_env() */ + CMD_CALL_WASM, /* wasm_runtime_call_wasm */ + CMD_EXEC_APP_FUNC, /* wasm_application_execute_func() */ + CMD_EXEC_APP_MAIN, /* wasm_application_execute_main() */ + CMD_GET_EXCEPTION, /* wasm_runtime_get_exception() */ + CMD_DEINSTANTIATE_MODULE, /* wasm_runtime_deinstantiate() */ + CMD_UNLOAD_MODULE, /* wasm_runtime_unload() */ + CMD_DESTROY_RUNTIME, /* wasm_runtime_destroy() */ + CMD_SET_WASI_ARGS, /* wasm_runtime_set_wasi_args() */ + CMD_SET_LOG_LEVEL, /* bh_log_set_verbose_level() */ + CMD_GET_VERSION, /* wasm_runtime_get_version() */ +#if WASM_ENABLE_STATIC_PGO != 0 + CMD_GET_PGO_PROF_BUF_SIZE, /* wasm_runtime_get_pro_prof_data_size() */ + CMD_DUMP_PGO_PROF_BUF_DATA, /* wasm_runtime_dump_pgo_prof_data_to_buf() */ +#endif +} EcallCmd; + +static void +app_instance_func(void *wasm_module_inst, const char *func_name, int app_argc, + char **app_argv); + +static void * +app_instance_repl(void *module_inst, int app_argc, char **app_argv) +{ + char *cmd = NULL; + size_t len = 0; + ssize_t n; + + while ((printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { + assert(n > 0); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + if (!strcmp(cmd, "__exit__")) { + printf("exit repl mode\n"); + break; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + printf("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + app_instance_func(module_inst, app_argv[0], app_argc - 1, + app_argv + 1); + } + free(app_argv); + } + free(cmd); + return NULL; +} + +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} + +static bool +set_log_verbose_level(int log_verbose_level) +{ + uint64_t ecall_args[1]; + + /* Set log verbose level */ + if (log_verbose_level != 2) { + ecall_args[0] = log_verbose_level; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_SET_LOG_LEVEL, + (uint8_t *)ecall_args, sizeof(uint64_t))) { + printf("Call ecall_handle_command() failed.\n"); + return false; + } + } + return true; +} + +static bool +init_runtime(uint32_t max_thread_num) +{ + uint64_t ecall_args[1]; + + ecall_args[0] = max_thread_num; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_INIT_RUNTIME, (uint8_t *)ecall_args, + sizeof(ecall_args))) { + printf("Call ecall_handle_command() failed.\n"); + return false; + } + if (!(bool)ecall_args[0]) { + printf("Init runtime environment failed.\n"); + return false; + } + return true; +} + +static void +destroy_runtime() +{ + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_DESTROY_RUNTIME, NULL, 0)) { + printf("Call ecall_handle_command() failed.\n"); + } +} + +static void * +load_module(uint8_t *wasm_file_buf, uint32_t wasm_file_size, char *error_buf, + uint32_t error_buf_size) +{ + uint64_t ecall_args[4]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_file_buf; + ecall_args[1] = wasm_file_size; + ecall_args[2] = (uint64_t)(uintptr_t)error_buf; + ecall_args[3] = error_buf_size; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_LOAD_MODULE, (uint8_t *)ecall_args, + sizeof(uint64_t) * 4)) { + printf("Call ecall_handle_command() failed.\n"); + return NULL; + } + + return (void *)(uintptr_t)ecall_args[0]; +} + +static void +unload_module(void *wasm_module) +{ + uint64_t ecall_args[1]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_UNLOAD_MODULE, (uint8_t *)ecall_args, + sizeof(uint64_t))) { + printf("Call ecall_handle_command() failed.\n"); + } +} + +static void * +instantiate_module(void *wasm_module, uint32_t stack_size, uint32_t heap_size, + char *error_buf, uint32_t error_buf_size) +{ + uint64_t ecall_args[5]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; + ecall_args[1] = stack_size; + ecall_args[2] = heap_size; + ecall_args[3] = (uint64_t)(uintptr_t)error_buf; + ecall_args[4] = error_buf_size; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_INSTANTIATE_MODULE, + (uint8_t *)ecall_args, sizeof(uint64_t) * 5)) { + printf("Call ecall_handle_command() failed.\n"); + return NULL; + } + + return (void *)(uintptr_t)ecall_args[0]; +} + +static void +deinstantiate_module(void *wasm_module_inst) +{ + uint64_t ecall_args[1]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module_inst; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_DEINSTANTIATE_MODULE, + (uint8_t *)ecall_args, sizeof(uint64_t))) { + printf("Call ecall_handle_command() failed.\n"); + } +} + +static bool +get_exception(void *wasm_module_inst, char *exception, uint32_t exception_size) +{ + uint64_t ecall_args[3]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module_inst; + ecall_args[1] = (uint64_t)(uintptr_t)exception; + ecall_args[2] = exception_size; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_GET_EXCEPTION, (uint8_t *)ecall_args, + sizeof(uint64_t) * 3)) { + printf("Call ecall_handle_command() failed.\n"); + } + + return (bool)ecall_args[0]; +} + +static void +app_instance_main(void *wasm_module_inst, int app_argc, char **app_argv) +{ + char exception[128]; + uint64_t ecall_args_buf[16], *ecall_args = ecall_args_buf; + int i, size; + + if (app_argc + 2 > sizeof(ecall_args_buf) / sizeof(uint64_t)) { + if (!(ecall_args = + (uint64_t *)malloc(sizeof(uint64_t) * (app_argc + 2)))) { + printf("Allocate memory failed.\n"); + return; + } + } + + ecall_args[0] = (uintptr_t)wasm_module_inst; + ecall_args[1] = app_argc; + for (i = 0; i < app_argc; i++) { + ecall_args[i + 2] = (uintptr_t)app_argv[i]; + } + + size = (uint32_t)sizeof(uint64_t) * (app_argc + 2); + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_EXEC_APP_MAIN, (uint8_t *)ecall_args, + size)) { + printf("Call ecall_handle_command() failed.\n"); + } + + if (get_exception(wasm_module_inst, exception, sizeof(exception))) { + printf("%s\n", exception); + } + + if (ecall_args != ecall_args_buf) { + free(ecall_args); + } +} + +static void +app_instance_func(void *wasm_module_inst, const char *func_name, int app_argc, + char **app_argv) +{ + uint64_t ecall_args_buf[16], *ecall_args = ecall_args_buf; + int i, size; + + if (app_argc + 3 > sizeof(ecall_args_buf) / sizeof(uint64_t)) { + if (!(ecall_args = + (uint64_t *)malloc(sizeof(uint64_t) * (app_argc + 3)))) { + printf("Allocate memory failed.\n"); + return; + } + } + + ecall_args[0] = (uintptr_t)wasm_module_inst; + ecall_args[1] = (uintptr_t)func_name; + ecall_args[2] = (uintptr_t)app_argc; + for (i = 0; i < app_argc; i++) { + ecall_args[i + 3] = (uintptr_t)app_argv[i]; + } + + size = (uint32_t)sizeof(uint64_t) * (app_argc + 3); + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_EXEC_APP_FUNC, (uint8_t *)ecall_args, + size)) { + printf("Call ecall_handle_command() failed.\n"); + } + + if (ecall_args != ecall_args_buf) { + free(ecall_args); + } +} + +static bool +set_wasi_args(void *wasm_module, const char **dir_list, uint32_t dir_list_size, + const char **env_list, uint32_t env_list_size, int stdinfd, + int stdoutfd, int stderrfd, char **argv, uint32_t argc, + const char **addr_pool, uint32_t addr_pool_size) +{ + uint64_t ecall_args[12]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; + ecall_args[1] = (uint64_t)(uintptr_t)dir_list; + ecall_args[2] = dir_list_size; + ecall_args[3] = (uint64_t)(uintptr_t)env_list; + ecall_args[4] = env_list_size; + ecall_args[5] = stdinfd; + ecall_args[6] = stdoutfd; + ecall_args[7] = stderrfd; + ecall_args[8] = (uint64_t)(uintptr_t)argv; + ecall_args[9] = argc; + ecall_args[10] = (uint64_t)(uintptr_t)addr_pool; + ecall_args[11] = addr_pool_size; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_SET_WASI_ARGS, (uint8_t *)ecall_args, + sizeof(uint64_t) * 12)) { + printf("Call ecall_handle_command() failed.\n"); + } + + return (bool)ecall_args[0]; +} + +static void +get_version(uint64_t *major, uint64_t *minor, uint64_t *patch) +{ + uint64_t ecall_args[3] = { 0 }; + + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_GET_VERSION, (uint8_t *)ecall_args, + sizeof(ecall_args))) { + printf("Call ecall_handle_command() failed.\n"); + return; + } + + *major = ecall_args[0]; + *minor = ecall_args[1]; + *patch = ecall_args[2]; +} + +#if WASM_ENABLE_STATIC_PGO != 0 +static void +dump_pgo_prof_data(void *module_inst, const char *path) +{ + char *buf; + uint32_t len; + FILE *file; + + uint64_t ecall_args[1]; + ecall_args[0] = (uint64_t)(uintptr_t)module_inst; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_GET_PGO_PROF_BUF_SIZE, + (uint8_t *)ecall_args, sizeof(ecall_args))) { + printf("Call ecall_handle_command() failed.\n"); + return; + } + if (!(len = ecall_args[0])) { + printf("failed to get LLVM PGO profile data size\n"); + return; + } + + if (!(buf = (char *)malloc(len))) { + printf("allocate memory failed\n"); + return; + } + + uint64_t ecall_args_2[3]; + ecall_args_2[0] = (uint64_t)(uintptr_t)module_inst; + ecall_args_2[1] = (uint64_t)(uintptr_t)buf; + ecall_args_2[2] = len; + if (SGX_SUCCESS + != ecall_handle_command(g_eid, CMD_DUMP_PGO_PROF_BUF_DATA, + (uint8_t *)ecall_args_2, + sizeof(ecall_args_2))) { + printf("Call ecall_handle_command() failed.\n"); + free(buf); + return; + } + if (!(len = ecall_args_2[0])) { + printf("failed to dump LLVM PGO profile data\n"); + free(buf); + return; + } + + if (!(file = fopen(path, "wb"))) { + printf("failed to create file %s", path); + free(buf); + return; + } + fwrite(buf, len, 1, file); + fclose(file); + + free(buf); + + printf("LLVM raw profile file %s was generated.\n", path); +} +#endif + int -main(int argc, char const *argv[]) +main(int argc, char *argv[]) { + int32_t ret = -1; + char *wasm_file = NULL; + const char *func_name = NULL; + uint8_t *wasm_file_buf = NULL; + uint32_t wasm_file_size; + uint32_t stack_size = 64 * 1024, heap_size = 16 * 1024; + void *wasm_module = NULL; + void *wasm_module_inst = NULL; + char error_buf[128] = { 0 }; + int log_verbose_level = 2; + bool is_repl_mode = false; + const char *dir_list[8] = { NULL }; + uint32_t dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32_t env_list_size = 0; + const char *addr_pool[8] = { NULL }; + uint32_t addr_pool_size = 0; + uint32_t max_thread_num = 4; +#if WASM_ENABLE_STATIC_PGO != 0 + const char *gen_prof_file = NULL; +#endif + if (enclave_init(&g_eid) < 0) { std::cout << "Fail to initialize enclave." << std::endl; return 1; } - ecall_iwasm_main(g_eid); +#if TEST_OCALL_API != 0 + { + if (!init_runtime(max_thread_num)) { + return -1; + } + ecall_iwasm_test(g_eid); + destroy_runtime(); + return 0; + } +#endif + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + return print_help(); + } + func_name = argv[0]; + } + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } + else if (!strcmp(argv[0], "--repl")) { + is_repl_mode = true; + } + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } + else if (!strncmp(argv[0], "--dir=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(dir_list) / sizeof(char *))); + return 1; + } + dir_list[dir_list_size++] = argv[0] + 6; + } + else if (!strncmp(argv[0], "--env=", 6)) { + char *tmp_env; + + if (argv[0][6] == '\0') + return print_help(); + if (env_list_size >= sizeof(env_list) / sizeof(char *)) { + printf("Only allow max env number %d\n", + (int)(sizeof(env_list) / sizeof(char *))); + return 1; + } + tmp_env = argv[0] + 6; + if (validate_env_str(tmp_env)) + env_list[env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", " + "got \"%s\"\n", + tmp_env); + return print_help(); + } + } + /* TODO: parse the configuration file via --addr-pool-file */ + else if (!strncmp(argv[0], "--addr-pool=", strlen("--addr-pool="))) { + /* like: --addr-pool=100.200.244.255/30 */ + char *token = NULL; + + if ('\0' == argv[0][12]) + return print_help(); + + token = strtok(argv[0] + strlen("--addr-pool="), ","); + while (token) { + if (addr_pool_size >= sizeof(addr_pool) / sizeof(char *)) { + printf("Only allow max address number %d\n", + (int)(sizeof(addr_pool) / sizeof(char *))); + return 1; + } + + addr_pool[addr_pool_size++] = token; + token = strtok(NULL, ";"); + } + } + else if (!strncmp(argv[0], "--max-threads=", 14)) { + if (argv[0][14] == '\0') + return print_help(); + max_thread_num = atoi(argv[0] + 14); + } +#if WASM_ENABLE_STATIC_PGO != 0 + else if (!strncmp(argv[0], "--gen-prof-file=", 16)) { + if (argv[0][16] == '\0') + return print_help(); + gen_prof_file = argv[0] + 16; + } +#endif + else if (!strncmp(argv[0], "--version", 9)) { + uint64_t major = 0, minor = 0, patch = 0; + get_version(&major, &minor, &patch); + printf("iwasm %lu.%lu.%lu\n", major, minor, patch); + return 0; + } + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + + /* Init runtime */ + if (!init_runtime(max_thread_num)) { + return -1; + } + + /* Set log verbose level */ + if (!set_log_verbose_level(log_verbose_level)) { + goto fail1; + } + + /* Load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8_t *)read_file_to_buffer(wasm_file, &wasm_file_size))) { + goto fail1; + } + + /* Load module */ + if (!(wasm_module = load_module(wasm_file_buf, wasm_file_size, error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* Set wasi arguments */ + if (!set_wasi_args(wasm_module, dir_list, dir_list_size, env_list, + env_list_size, 0, 1, 2, argv, argc, addr_pool, + addr_pool_size)) { + printf("%s\n", "set wasi arguments failed.\n"); + goto fail3; + } + + /* Instantiate module */ + if (!(wasm_module_inst = + instantiate_module(wasm_module, stack_size, heap_size, error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + if (is_repl_mode) + app_instance_repl(wasm_module_inst, argc, argv); + else if (func_name) + app_instance_func(wasm_module_inst, func_name, argc - 1, argv + 1); + else + app_instance_main(wasm_module_inst, argc, argv); + +#if WASM_ENABLE_STATIC_PGO != 0 + if (gen_prof_file) + dump_pgo_prof_data(wasm_module_inst, gen_prof_file); +#endif + + ret = 0; + + /* Deinstantiate module */ + deinstantiate_module(wasm_module_inst); + +fail3: + /* Unload module */ + unload_module(wasm_module); + +fail2: + /* Free the file buffer */ + free(wasm_file_buf); + +fail1: + /* Destroy runtime environment */ + destroy_runtime(); + + return ret; +} + +int +wamr_pal_get_version(void) +{ + return WAMR_PAL_VERSION; +} + +int +wamr_pal_init(const struct wamr_pal_attr *args) +{ + sgx_enclave_id_t *p_eid = &g_eid; + + if (enclave_init(&g_eid) < 0) { + std::cout << "Fail to initialize enclave." << std::endl; + return 1; + } + return 0; +} + +int +wamr_pal_create_process(struct wamr_pal_create_process_args *args) +{ + uint32_t stack_size = 64 * 1024, heap_size = 16 * 1024; + int log_verbose_level = 2; + bool is_repl_mode = false; + const char *dir_list[8] = { NULL }; + uint32_t dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32_t env_list_size = 0; + const char *addr_pool[8] = { NULL }; + uint32_t addr_pool_size = 0; + uint32_t max_thread_num = 4; + char *wasm_files[16]; + void *wasm_module_inst[16]; + int stdinfd = -1; + int stdoutfd = -1; + int stderrfd = -1; + + const int argc = 2; + char *argv[argc] = { (char *)"./iwasm", (char *)args->argv[0] }; + + uint8_t *wasm_files_buf = NULL; + void *wasm_modules = NULL; + int len = 0, i; + + char *temp = (char *)args->argv[0]; + while (temp) { + len++; + temp = (char *)args->argv[len]; + } + + if (len > sizeof(wasm_files) / sizeof(char *)) { + printf("Number of input files is out of range\n"); + return -1; + } + + for (i = 0; i < len; ++i) { + wasm_files[i] = (char *)args->argv[i]; + } + + if (args->stdio != NULL) { + stdinfd = args->stdio->stdin_fd; + stdoutfd = args->stdio->stdout_fd; + stderrfd = args->stdio->stderr_fd; + } + + /* Init runtime */ + if (!init_runtime(max_thread_num)) { + printf("Failed to init runtime\n"); + return -1; + } + + /* Set log verbose level */ + if (!set_log_verbose_level(log_verbose_level)) { + printf("Failed to set log level\n"); + destroy_runtime(); + return -1; + } + + for (i = 0; i < len; ++i) { + uint8_t *wasm_file_buf = NULL; + uint32_t wasm_file_size; + void *wasm_module = NULL; + char error_buf[128] = { 0 }; + + /* Load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = (uint8_t *)read_file_to_buffer( + wasm_files[i], &wasm_file_size))) { + printf("Failed to read file to buffer\n"); + destroy_runtime(); + return -1; + } + + /* Load module */ + if (!(wasm_module = load_module(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + free(wasm_file_buf); + destroy_runtime(); + return -1; + } + + /* Set wasi arguments */ + if (!set_wasi_args(wasm_module, dir_list, dir_list_size, env_list, + env_list_size, stdinfd, stdoutfd, stderrfd, argv, + argc, addr_pool, addr_pool_size)) { + printf("%s\n", "set wasi arguments failed.\n"); + unload_module(wasm_module); + free(wasm_file_buf); + destroy_runtime(); + return -1; + } + + /* Instantiate module */ + if (!(wasm_module_inst[i] = + instantiate_module(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + unload_module(wasm_module); + free(wasm_file_buf); + destroy_runtime(); + return -1; + } + + app_instance_main(wasm_module_inst[i], argc, argv); + + /* Deinstantiate module */ + deinstantiate_module(wasm_module_inst[i]); + unload_module(wasm_module); + free(wasm_file_buf); + } + + destroy_runtime(); return 0; } +int +wamr_pal_destroy(void) +{ + // sgx_destroy_enclave(g_eid); + return 0; +} + +int +wamr_pal_exec(struct wamr_pal_exec_args *args) +{ + // app_instance_main(wasm_module_inst[i], argc, argv); + return 0; +} + +int +wamr_pal_kill(int pid, int sig) +{ + // deinstantiate_module(wasm_module_inst[i]); + // unload_module(wasm_module); + // free(wasm_file_buf); + return 0; +} + +int +pal_get_version(void) __attribute__((weak, alias("wamr_pal_get_version"))); + +int +pal_init(const struct wamr_pal_attr *attr) + __attribute__((weak, alias("wamr_pal_init"))); + +int +pal_create_process(struct wamr_pal_create_process_args *args) + __attribute__((weak, alias("wamr_pal_create_process"))); + +int +pal_exec(struct wamr_pal_exec_args *args) + __attribute__((weak, alias("wamr_pal_exec"))); + +int +pal_kill(int pid, int sig) __attribute__((weak, alias("wamr_pal_kill"))); + +int +pal_destroy(void) __attribute__((weak, alias("wamr_pal_destroy"))); diff --git a/product-mini/platforms/linux-sgx/enclave-sample/App/README.md b/product-mini/platforms/linux-sgx/enclave-sample/App/README.md new file mode 100644 index 0000000000..78d8b25af0 --- /dev/null +++ b/product-mini/platforms/linux-sgx/enclave-sample/App/README.md @@ -0,0 +1,161 @@ +# Running WAMR as an [Enclave Runtime](https://github.com/alibaba/inclavare-containers/blob/master/docs/design/terminology.md#enclave-runtime) for [Inclavare Containers](https://github.com/alibaba/inclavare-containers) + +In order to establish with `rune`, a novel OCI Runtime for spawning and running enclaves in containers, it is required to implement an [enclave runtime PAL](https://github.com/alibaba/inclavare-containers/blob/master/docs/design/terminology.md#enclave-runtime-pal) to make the communications with WAMR. + +With the assist of `rune`, WAMR is brought to the cloud-native ecosystem beyond the basis. This is the so-called term "WAMR enclave runtime". + +This guide will provide the information about the build, integration and deployment for WAMR enclave runtime. Eventually, the resulting docker image will be launched by `rune`, and the WARM application as the entrypoint of docker image will run in Intel SGX enclave with the hardware-enforced isolation and cryptographically data protection. + +## Build WAMR vmcore (iwasm) and enclave image + +Please follow [this guide](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/linux_sgx.md#build-wamr-vmcore-iwasm-for-linux-sgx) to build iwasm and enclave image as the prerequisite. + +The generated enclave image enclave.signed.so will be consumed by WAMR enclave runtime mentioned below. + +--- + +## Build and install the PAL of WAMR enclave runtime + +```shell +g++ -shared -fPIC -o libwamr-pal.so App/*.o libvmlib_untrusted.a -L/opt/intel/sgxsdk/lib64 -lsgx_urts -lpthread -lssl -lcrypto +cp ./libwamr-pal.so /usr/lib/libwamr-pal.so +``` + +--- + +## Build WAMR application + +As the prerequisite, please +- refer to [this step](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/build_wasm_app.md#prepare-wasm-building-environments) to install wasi-sdk. Note that the binaries of wasi-sdk must be installed at `/opt/wasi-sdk/bin/`. +- refer to [this guide](https://github.com/bytecodealliance/wasm-micro-runtime#build-wamrc-aot-compiler) to generate wamrc AoT compiler. + +The sample WAMR application test.c is provided in [this guide](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/build_wasm_app.md#build-wasm-applications). Don't forget to compile the `.wasm` file to `.aot` file: +```shell +wamrc -sgx -o test.aot test.wasm +``` + +The generated test.aot is the WAMR application launched by WAMR enclave runtime. + +--- + +## Build WAMR docker image + +Under the `enclave-sample` directory, to create the WAMR docker images to load the `enclave.signed.so` and target application wasm files, please type the following commands to create a `Dockerfile`: + +For CentOS: + +```shell +cat >Dockerfile < Dockerfile < 0, then success; otherwise, it is an invalid version. + */ +int +wamr_pal_get_version(void); + +/* + * WAMR PAL attributes + */ +typedef struct wamr_pal_attr { + // WAMR instance directory. + // + // The default value is "."; that is, the current working directory + const char *instance_dir; + // Log level. + // + // Specifies the log verbose level (0 to 5, default is 2) + // large level with more log + // + const char *log_level; +} wamr_pal_attr_t; + +/* clang-format off */ +#define WAMR_PAL_ATTR_INITVAL { \ + .instance_dir = ".", \ + .log_level = 2 \ +} +/* clang-format on */ + +/* + * The struct which consists of file descriptors of standard I/O + */ +typedef struct wamr_stdio_fds { + int stdin_fd; + int stdout_fd; + int stderr_fd; +} wamr_stdio_fds_t; + +/* + * The struct which consists of arguments needed by wamr_pal_create_process + */ +struct wamr_pal_create_process_args { + + // Path to new process. + // + // The path of the command which will be created as a new process. + // + // Mandatory field. Must not be NULL. + const char *path; + + // Arguments array pass to new process. + // + // The arguments to the command. By convention, the argv[0] should be the + // program name. And the last element of the array must be NULL to indicate + // the length of array. + // + // Mandatory field. Must not be NULL. + const char **argv; + + // Untrusted environment variable array pass to new process. + // + // The untrusted env vars to the command. And the last element of the array + // must be NULL to indicate the end of the array. + // + // Optional field. + const char **env; + + // File descriptors of the redirected standard I/O (i.e., stdin, stdout, + // stderr) + // + // If set to NULL, will use the original standard I/O file descriptors. + // + // Optional field. + const struct wamr_stdio_fds *stdio; + + // Output. Pid of new process in libos. + // + // If wamr_pal_create_process returns success, pid of the new process will + // be updated. + // + // Mandatory field. Must not be NULL. + int *pid; +}; + +/* + * The struct which consists of arguments needed by wamr_pal_exec + */ +struct wamr_pal_exec_args { + // Pid of new process created with wamr_pal_create_process. + // + // Mandatory field. + int pid; + + // Output. The exit status of the command. The semantic of + // this value follows the one described in wait(2) man + // page. For example, if the program terminated normally, + // then WEXITSTATUS(exit_status) gives the value returned + // from a main function. + // + // Mandatory field. Must not be NULL. + int *exit_value; +}; + +int +wamr_pal_init(const struct wamr_pal_attr *args); + +int +wamr_pal_create_process(struct wamr_pal_create_process_args *args); + +int +wamr_pal_destroy(void); + +int +wamr_pal_exec(struct wamr_pal_exec_args *args); + +int +wamr_pal_kill(int pid, int sig); + +int +pal_get_version(void); + +int +pal_init(const struct wamr_pal_attr *attr); + +int +pal_create_process(struct wamr_pal_create_process_args *args); + +int +pal_exec(struct wamr_pal_exec_args *args); + +int +pal_kill(int pid, int sig); + +int +pal_destroy(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __WAMR_PAL_API_H__ */ diff --git a/product-mini/platforms/linux-sgx/enclave-sample/App/wamr-bundle.md b/product-mini/platforms/linux-sgx/enclave-sample/App/wamr-bundle.md new file mode 100644 index 0000000000..3e2cdabe7f --- /dev/null +++ b/product-mini/platforms/linux-sgx/enclave-sample/App/wamr-bundle.md @@ -0,0 +1,64 @@ +# Run WAMR enclave runtime with bundle + +## Create WAMR Application bundle + +`rune` can directly launch an OCI bundle converted from docker image. If you have Docker installed you can use its `export` sub-command to acquire a root filesystem from an existing WAMR application docker image. + +```shell +# create the top most bundle directory +mkdir -p "$HOME/rune_workdir" +cd "$HOME/rune_workdir" +mkdir wamr-sgx-bundle +cd warmr-sgx-bundle + +# create the rootfs directory +mkdir rootfs + +# export wamr application image via Docker into the rootfs directory +docker export $(docker create ${wamr_application_image}) | sudo tar -C rootfs -xvf - +``` + +After a root filesystem is populated you just generate a spec in the format of a config.json file inside your bundle. `rune` provides a spec command which is similar to `runc` to generate a template file that you are then able to edit. + +```shell +rune spec +``` + +To find features and documentation for fields in the spec please refer to the [specs](https://github.com/opencontainers/runtime-spec) repository. + +In order to run the target application in WAMR with `rune`, you need to change the entrypoint from `sh` to the target application, and in order to run multi-applications in one runtime with enclave, change it to `/run/rune/${wasm_app1.aot}`, `/run/rune/${wasm_app2.aot}` ... + +```json + "process": { + "args": [ + "/run/rune/${wasm_app}" + ], + } +``` + +and then configure enclave runtime as following: + +```json + "annotations": { + "enclave.type": "intelSgx", + "enclave.runtime.path": "/usr/lib/libwamr-pal.so", + "enclave.runtime.args": "debug" + } +``` + +where: + +- @enclave.type: specify the type of enclave hardware to use, such as `intelSgx`. +- @enclave.runtime.path: specify the path to enclave runtime to launch. For an WAMR application, you need to specify the path to `libwamr-pal.so`. +- @enclave.runtime.args: specify the specific arguments to enclave runtime, separated by the comma. + +--- + +## Run WAMR Application + +Assuming you have an OCI bundle from the previous step you can execute the container in this way. + +```shell +cd "$HOME/rune_workdir/wamr-sgx-bundle" +sudo rune run wamr-sgx-app +``` diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml index a94d12f001..ea3a63f8f5 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml @@ -2,11 +2,14 @@ 0 0 - 0x40000 - 0x100000 + 0x100000 + 0x8000000 + 0x1000000 + 1 10 1 - 0 + + 1 0 0xFFFFFFFF 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 f757887875..dc3b6c3a24 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp @@ -5,101 +5,1088 @@ #include #include +#include +#include + #include "Enclave_t.h" -#include "test_wasm.h" -#include "bh_memory.h" #include "wasm_export.h" +#include "bh_platform.h" -static char global_heap_buf[512 * 1024] = { 0 }; +#if WASM_ENABLE_LIB_RATS != 0 +#include +#endif -static int app_argc; -static char **app_argv; +extern "C" { +typedef int (*os_print_function_t)(const char *message); +extern void +os_set_print_function(os_print_function_t pf); -static void* -app_instance_main(wasm_module_inst_t module_inst) +int +enclave_print(const char *message) { - const char *exception; + int bytes_written = 0; + + if (SGX_SUCCESS != ocall_print(&bytes_written, message)) + return 0; + + return bytes_written; +} +} + +typedef enum EcallCmd { + CMD_INIT_RUNTIME = 0, /* wasm_runtime_init/full_init() */ + CMD_LOAD_MODULE, /* wasm_runtime_load() */ + CMD_INSTANTIATE_MODULE, /* wasm_runtime_instantiate() */ + CMD_LOOKUP_FUNCTION, /* wasm_runtime_lookup_function() */ + CMD_CREATE_EXEC_ENV, /* wasm_runtime_create_exec_env() */ + CMD_CALL_WASM, /* wasm_runtime_call_wasm */ + CMD_EXEC_APP_FUNC, /* wasm_application_execute_func() */ + CMD_EXEC_APP_MAIN, /* wasm_application_execute_main() */ + CMD_GET_EXCEPTION, /* wasm_runtime_get_exception() */ + CMD_DEINSTANTIATE_MODULE, /* wasm_runtime_deinstantiate() */ + CMD_UNLOAD_MODULE, /* wasm_runtime_unload() */ + CMD_DESTROY_RUNTIME, /* wasm_runtime_destroy() */ + CMD_SET_WASI_ARGS, /* wasm_runtime_set_wasi_args() */ + CMD_SET_LOG_LEVEL, /* bh_log_set_verbose_level() */ + CMD_GET_VERSION, /* wasm_runtime_get_version() */ +#if WASM_ENABLE_STATIC_PGO != 0 + CMD_GET_PGO_PROF_BUF_SIZE, /* wasm_runtime_get_pro_prof_data_size() */ + CMD_DUMP_PGO_PROF_BUF_DATA, /* wasm_runtime_dump_pgo_prof_data_to_buf() */ +#endif +} EcallCmd; + +typedef struct EnclaveModule { + wasm_module_t module; + uint8 *wasm_file; + uint32 wasm_file_size; + char *wasi_arg_buf; + char **wasi_dir_list; + uint32 wasi_dir_list_size; + char **wasi_env_list; + uint32 wasi_env_list_size; + char **wasi_addr_pool_list; + uint32 wasi_addr_pool_list_size; + char **wasi_argv; + uint32 wasi_argc; + bool is_xip_file; + uint32 total_size_mapped; +#if WASM_ENABLE_LIB_RATS != 0 + char module_hash[SHA256_DIGEST_LENGTH]; + struct EnclaveModule *next; +#endif +} EnclaveModule; + +#if WASM_ENABLE_LIB_RATS != 0 +static EnclaveModule *enclave_module_list = NULL; +static korp_mutex enclave_module_list_lock = OS_THREAD_MUTEX_INITIALIZER; +#endif - wasm_application_execute_main(module_inst, app_argc, app_argv); - if ((exception = wasm_runtime_get_exception(module_inst))) { - ocall_print(exception); - ocall_print("\n"); +/* Handle table for secure EnclaveModule reference management */ +#define MAX_MODULES 128 +typedef struct HandleTableEntry { + uint32 id; + EnclaveModule *module_ref; + bool in_use; +} HandleTableEntry; + +static HandleTableEntry module_table[MAX_MODULES] = { 0 }; +static uint32 next_module_id = 1; +static korp_mutex module_table_lock = OS_THREAD_MUTEX_INITIALIZER; + +/* : Allocate secure handle for EnclaveModule, preventing pointer + * exposure */ +static uint32 +allocate_module_handle(EnclaveModule *module) +{ + uint32 handle_id = 0; + + os_mutex_lock(&module_table_lock); + + /* Find free slot */ + for (uint32 i = 0; i < MAX_MODULES; i++) { + if (!module_table[i].in_use) { + module_table[i].id = next_module_id++; + module_table[i].module_ref = module; + module_table[i].in_use = true; + handle_id = module_table[i].id; + break; + } } - return NULL; + + os_mutex_unlock(&module_table_lock); + + if (handle_id == 0) { + int bytes_written = 0; + ocall_print(&bytes_written, + "SECURITY WARNING: Module handle table full\n"); + } + + return handle_id; } -extern "C" { +/* Lookup EnclaveModule by handle ID, preventing direct pointer access */ +static EnclaveModule * +lookup_module_by_handle(uint32 handle_id) +{ + EnclaveModule *module = NULL; + + if (handle_id == 0) + return NULL; + + os_mutex_lock(&module_table_lock); + + for (uint32 i = 0; i < MAX_MODULES; i++) { + if (module_table[i].in_use && module_table[i].id == handle_id) { + module = module_table[i].module_ref; + break; + } + } + + os_mutex_unlock(&module_table_lock); + + return module; +} + +static void +release_module_handle(uint32 handle_id) +{ + os_mutex_lock(&module_table_lock); + + for (uint32 i = 0; i < MAX_MODULES; i++) { + if (module_table[i].in_use && module_table[i].id == handle_id) { + module_table[i].id = 0; + module_table[i].module_ref = NULL; + module_table[i].in_use = false; + break; + } + } + + os_mutex_unlock(&module_table_lock); +} + +/* Handle table for secure wasm_module_inst_t reference management */ +#define MAX_INSTANCES 128 +typedef struct InstanceTableEntry { + uint32 id; + wasm_module_inst_t inst_ref; + bool in_use; +} InstanceTableEntry; + +static InstanceTableEntry instance_table[MAX_INSTANCES] = { 0 }; +static uint32 next_instance_id = 1; +static korp_mutex instance_table_lock = OS_THREAD_MUTEX_INITIALIZER; + +/*Allocate secure handle for wasm_module_inst_t, preventing pointer exposure */ +static uint32 +allocate_instance_handle(wasm_module_inst_t inst) +{ + uint32 handle_id = 0; + + os_mutex_lock(&instance_table_lock); + + for (uint32 i = 0; i < MAX_INSTANCES; i++) { + if (!instance_table[i].in_use) { + instance_table[i].id = next_instance_id++; + instance_table[i].inst_ref = inst; + instance_table[i].in_use = true; + handle_id = instance_table[i].id; + break; + } + } + + os_mutex_unlock(&instance_table_lock); + + if (handle_id == 0) { + int bytes_written = 0; + ocall_print(&bytes_written, + "SECURITY WARNING: Instance handle table full\n"); + } + + return handle_id; +} + +/* SECURITY: Lookup wasm_module_inst_t by handle ID, preventing direct pointer + * access */ +static wasm_module_inst_t +lookup_instance_by_handle(uint32 handle_id) +{ + wasm_module_inst_t inst = NULL; + + if (handle_id == 0) + return NULL; + + os_mutex_lock(&instance_table_lock); + + for (uint32 i = 0; i < MAX_INSTANCES; i++) { + if (instance_table[i].in_use && instance_table[i].id == handle_id) { + inst = instance_table[i].inst_ref; + break; + } + } + + os_mutex_unlock(&instance_table_lock); + + return inst; +} + +static void +release_instance_handle(uint32 handle_id) +{ + os_mutex_lock(&instance_table_lock); + + for (uint32 i = 0; i < MAX_INSTANCES; i++) { + if (instance_table[i].in_use && instance_table[i].id == handle_id) { + instance_table[i].id = 0; + instance_table[i].inst_ref = NULL; + instance_table[i].in_use = false; + break; + } + } + + os_mutex_unlock(&instance_table_lock); +} + +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 +static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; +#endif + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +static bool runtime_inited = false; + +static void +handle_cmd_init_runtime(uint64 *args, uint32 argc) +{ + uint32 max_thread_num; + RuntimeInitArgs init_args; + + bh_assert(argc == 1); + + /* avoid duplicated init */ + if (runtime_inited) { + args[0] = false; + return; + } + + os_set_print_function(enclave_print); + + max_thread_num = (uint32)args[0]; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.max_thread_num = max_thread_num; + +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_System_Allocator; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERROR("Init runtime environment failed.\n"); + args[0] = false; + return; + } -int bh_printf(const char *message, ...); + runtime_inited = true; + args[0] = true; -typedef void (*bh_print_function_t)(const char* message); -extern void bh_set_print_function(bh_print_function_t pf); + LOG_VERBOSE("Init runtime environment success.\n"); +} -void enclave_print(const char *message) +static void +handle_cmd_destroy_runtime() { - ocall_print(message); + if (!runtime_inited) + return; + + wasm_runtime_destroy(); + runtime_inited = false; + + LOG_VERBOSE("Destroy runtime success.\n"); } +static uint8 * +align_ptr(const uint8 *p, uint32 b) +{ + uintptr_t v = (uintptr_t)p; + uintptr_t m = b - 1; + return (uint8 *)((v + m) & ~m); } -void ecall_iwasm_main() +#define AOT_SECTION_TYPE_TARGET_INFO 0 +#define AOT_SECTION_TYPE_SIGNATURE 6 +#define E_TYPE_XIP 4 + +#define CHECK_BUF(buf, buf_end, length) \ + do { \ + if ((uintptr_t)buf + length < (uintptr_t)buf \ + || (uintptr_t)buf + length > (uintptr_t)buf_end) \ + return false; \ + } while (0) + +#define read_uint16(p, p_end, res) \ + do { \ + p = (uint8 *)align_ptr(p, sizeof(uint16)); \ + CHECK_BUF(p, p_end, sizeof(uint16)); \ + res = *(uint16 *)p; \ + p += sizeof(uint16); \ + } while (0) + +#define read_uint32(p, p_end, res) \ + do { \ + p = (uint8 *)align_ptr(p, sizeof(uint32)); \ + CHECK_BUF(p, p_end, sizeof(uint32)); \ + res = *(uint32 *)p; \ + p += sizeof(uint32); \ + } while (0) + +static bool +is_xip_file(const uint8 *buf, uint32 size) { - bh_set_print_function(enclave_print); + const uint8 *p = buf, *p_end = buf + size; + uint32 section_type, section_size; + uint16 e_type; + + if (get_package_type(buf, size) != Wasm_Module_AoT) + return false; + + CHECK_BUF(p, p_end, 8); + p += 8; + while (p < p_end) { + read_uint32(p, p_end, section_type); + read_uint32(p, p_end, section_size); + CHECK_BUF(p, p_end, section_size); - uint8_t *wasm_file_buf = NULL; - int wasm_file_size; + if (section_type == AOT_SECTION_TYPE_TARGET_INFO) { + p += 4; + read_uint16(p, p_end, e_type); + return (e_type == E_TYPE_XIP) ? true : false; + } + else if (section_type >= AOT_SECTION_TYPE_SIGNATURE) { + return false; + } + p += section_size; + } + + return false; +} + +static void +handle_cmd_load_module(uint64 *args, uint32 argc) +{ + uint64 *args_org = args; + char *wasm_file = *(char **)args++; + uint32 wasm_file_size = *(uint32 *)args++; + char *error_buf = *(char **)args++; + uint32 error_buf_size = *(uint32 *)args++; + uint64 total_size = sizeof(EnclaveModule) + (uint64)wasm_file_size; + EnclaveModule *enclave_module; + + bh_assert(argc == 4); + + if (!runtime_inited) { + *(void **)args_org = NULL; + return; + } + + if (!is_xip_file((uint8 *)wasm_file, wasm_file_size)) { + if (total_size >= UINT32_MAX + || !(enclave_module = (EnclaveModule *)wasm_runtime_malloc( + (uint32)total_size))) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: " + "allocate memory failed."); + *(void **)args_org = NULL; + return; + } + memset(enclave_module, 0, (uint32)total_size); + } + else { + int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; + int map_flags = MMAP_MAP_NONE; + + if (total_size >= UINT32_MAX + || !(enclave_module = (EnclaveModule *)os_mmap( + NULL, (uint32)total_size, map_prot, map_flags, + os_get_invalid_handle()))) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: mmap memory failed."); + *(void **)args_org = NULL; + return; + } + memset(enclave_module, 0, (uint32)total_size); + enclave_module->is_xip_file = true; + enclave_module->total_size_mapped = (uint32)total_size; + } + + enclave_module->wasm_file = (uint8 *)enclave_module + sizeof(EnclaveModule); + bh_memcpy_s(enclave_module->wasm_file, wasm_file_size, wasm_file, + wasm_file_size); + + if (!(enclave_module->module = + wasm_runtime_load(enclave_module->wasm_file, wasm_file_size, + error_buf, error_buf_size))) { + if (!enclave_module->is_xip_file) + wasm_runtime_free(enclave_module); + else + os_munmap(enclave_module, (uint32)total_size); + *(void **)args_org = NULL; + return; + } + + uint32 enclave_module_id = allocate_module_handle(enclave_module); + if (enclave_module_id == 0) { + /* Handle table full - cleanup and return error */ + if (!enclave_module->is_xip_file) + wasm_runtime_free(enclave_module); + else + os_munmap(enclave_module, (uint32)total_size); + *(void **)args_org = NULL; + return; + } + *(uint32 *)args_org = enclave_module_id; + +#if WASM_ENABLE_LIB_RATS != 0 + /* Calculate the module hash */ + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, wasm_file, wasm_file_size); + SHA256_Final((unsigned char *)enclave_module->module_hash, &sha256); + + /* Insert enclave module to enclave module list */ + os_mutex_lock(&enclave_module_list_lock); + enclave_module->next = enclave_module_list; + enclave_module_list = enclave_module; + os_mutex_unlock(&enclave_module_list_lock); +#endif + + LOG_VERBOSE("Load module success.\n"); +} + +static void +handle_cmd_unload_module(uint64 *args, uint32 argc) +{ + uint32 module_handle_id = *(uint32 *)args++; + EnclaveModule *enclave_module = lookup_module_by_handle(module_handle_id); + + bh_assert(argc == 1); + + if (!runtime_inited) { + return; + } + +#if WASM_ENABLE_LIB_RATS != 0 + /* Remove enclave module from enclave module list */ + os_mutex_lock(&enclave_module_list_lock); + + EnclaveModule *node_prev = NULL; + EnclaveModule *node = enclave_module_list; + + while (node && node != enclave_module) { + node_prev = node; + node = node->next; + } + bh_assert(node == enclave_module); + + if (!node_prev) + enclave_module_list = node->next; + else + node_prev->next = node->next; + + os_mutex_unlock(&enclave_module_list_lock); +#endif + + /* Release module handle */ + release_module_handle(module_handle_id); + + /* Destroy enclave module resources */ + if (enclave_module->wasi_arg_buf) + wasm_runtime_free(enclave_module->wasi_arg_buf); + + wasm_runtime_unload(enclave_module->module); + if (!enclave_module->is_xip_file) + wasm_runtime_free(enclave_module); + else + os_munmap(enclave_module, enclave_module->total_size_mapped); + + LOG_VERBOSE("Unload module success.\n"); +} + +#if WASM_ENABLE_LIB_RATS != 0 +char * +wasm_runtime_get_module_hash(wasm_module_t module) +{ + EnclaveModule *enclave_module; + char *module_hash = NULL; + + os_mutex_lock(&enclave_module_list_lock); + + enclave_module = enclave_module_list; + while (enclave_module) { + if (enclave_module->module == module) { + module_hash = enclave_module->module_hash; + break; + } + enclave_module = enclave_module->next; + } + os_mutex_unlock(&enclave_module_list_lock); + + return module_hash; +} +#endif + +static void +handle_cmd_instantiate_module(uint64 *args, uint32 argc) +{ + uint64 *args_org = args; + uint32 module_handle_id = *(uint32 *)args++; + EnclaveModule *enclave_module = lookup_module_by_handle(module_handle_id); + uint32 stack_size = *(uint32 *)args++; + uint32 heap_size = *(uint32 *)args++; + char *error_buf = *(char **)args++; + uint32 error_buf_size = *(uint32 *)args++; + wasm_module_inst_t module_inst; + + bh_assert(argc == 5); + + if (!runtime_inited || !enclave_module) { + *(void **)args_org = NULL; + return; + } + + if (!(module_inst = + wasm_runtime_instantiate(enclave_module->module, stack_size, + heap_size, error_buf, error_buf_size))) { + *(void **)args_org = NULL; + return; + } + + uint32 instance_id = allocate_instance_handle(module_inst); + if (instance_id == 0) { + wasm_runtime_deinstantiate(module_inst); + *(void **)args_org = NULL; + return; + } + *(uint32 *)args_org = instance_id; + + LOG_VERBOSE("Instantiate module success.\n"); +} + +static void +handle_cmd_deinstantiate_module(uint64 *args, uint32 argc) +{ + uint32 instance_handle_id = *(uint32 *)args++; + wasm_module_inst_t module_inst = + lookup_instance_by_handle(instance_handle_id); + + bh_assert(argc == 1); + + if (!runtime_inited) { + return; + } + + wasm_runtime_deinstantiate(module_inst); + + release_instance_handle(instance_handle_id); + + LOG_VERBOSE("Deinstantiate module success.\n"); +} + +static void +handle_cmd_get_exception(uint64 *args, uint32 argc) +{ + uint64 *args_org = args; + uint32 instance_handle_id = *(uint32 *)args++; + wasm_module_inst_t module_inst = + lookup_instance_by_handle(instance_handle_id); + char *exception = *(char **)args++; + uint32 exception_size = *(uint32 *)args++; + const char *exception1; + + bh_assert(argc == 3); + + if (!runtime_inited) { + args_org[0] = false; + return; + } + + if ((exception1 = wasm_runtime_get_exception(module_inst))) { + snprintf(exception, exception_size, "%s", exception1); + args_org[0] = true; + } + else { + args_org[0] = false; + } +} + +static void +handle_cmd_exec_app_main(uint64 *args, int32 argc) +{ + uint32 instance_handle_id = *(uint32 *)args++; + wasm_module_inst_t module_inst = + lookup_instance_by_handle(instance_handle_id); + uint32 app_argc = *(uint32 *)args++; + char **app_argv = NULL; + uint64 total_size; + int32 i; + + bh_assert(argc >= 3); + bh_assert(app_argc >= 1); + + if (!runtime_inited) { + return; + } + + total_size = sizeof(char *) * (app_argc > 2 ? (uint64)app_argc : 2); + + if (total_size >= UINT32_MAX + || !(app_argv = (char **)wasm_runtime_malloc(total_size))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed."); + return; + } + + for (i = 0; i < app_argc; i++) { + app_argv[i] = (char *)(uintptr_t)args[i]; + } + + wasm_application_execute_main(module_inst, app_argc - 1, app_argv + 1); + + wasm_runtime_free(app_argv); +} + +static void +handle_cmd_exec_app_func(uint64 *args, int32 argc) +{ + uint32 instance_handle_id = *(uint32 *)args++; + wasm_module_inst_t module_inst = + lookup_instance_by_handle(instance_handle_id); + char *func_name = *(char **)args++; + uint32 app_argc = *(uint32 *)args++; + char **app_argv = NULL; + uint64 total_size; + int32 i, func_name_len = strlen(func_name); + + bh_assert(argc == app_argc + 3); + + if (!runtime_inited) { + return; + } + + total_size = sizeof(char *) * (app_argc > 2 ? (uint64)app_argc : 2); + + if (total_size >= UINT32_MAX + || !(app_argv = (char **)wasm_runtime_malloc(total_size))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed."); + return; + } + + for (i = 0; i < app_argc; i++) { + app_argv[i] = (char *)(uintptr_t)args[i]; + } + + wasm_application_execute_func(module_inst, func_name, app_argc, app_argv); + + wasm_runtime_free(app_argv); +} + +static void +handle_cmd_set_log_level(uint64 *args, uint32 argc) +{ +#if WASM_ENABLE_LOG != 0 + LOG_VERBOSE("Set log verbose level to %d.\n", (int)args[0]); + bh_log_set_verbose_level((int)args[0]); +#endif +} + +#if WASM_ENABLE_LIBC_WASI != 0 +static void +handle_cmd_set_wasi_args(uint64 *args, int32 argc) +{ + uint64 *args_org = args; + uint32 module_handle_id = *(uint32 *)args++; + EnclaveModule *enclave_module = lookup_module_by_handle(module_handle_id); + char **dir_list = *(char ***)args++; + uint32 dir_list_size = *(uint32 *)args++; + char **env_list = *(char ***)args++; + uint32 env_list_size = *(uint32 *)args++; + int stdinfd = *(int *)args++; + int stdoutfd = *(int *)args++; + int stderrfd = *(int *)args++; + char **wasi_argv = *(char ***)args++; + char *p, *p1; + uint32 wasi_argc = *(uint32 *)args++; + char **addr_pool_list = *(char ***)args++; + uint32 addr_pool_list_size = *(uint32 *)args++; + uint64 total_size = 0; + int32 i, str_len; + + bh_assert(argc == 10); + + if (!runtime_inited || !enclave_module) { + *args_org = false; + return; + } + + /* Validate all pointer arrays before use */ + if (dir_list_size > 0 + && (!dir_list + || !sgx_is_outside_enclave(dir_list, + sizeof(char *) * dir_list_size))) { + int bytes_written = 0; + ocall_print(&bytes_written, "SECURITY ERROR: Invalid dir_list\n"); + *args_org = false; + return; + } + + if (env_list_size > 0 + && (!env_list + || !sgx_is_outside_enclave(env_list, + sizeof(char *) * env_list_size))) { + int bytes_written = 0; + ocall_print(&bytes_written, "SECURITY ERROR: Invalid env_list\n"); + *args_org = false; + return; + } + + if (wasi_argc > 0 + && (!wasi_argv + || !sgx_is_outside_enclave(wasi_argv, + sizeof(char *) * wasi_argc))) { + int bytes_written = 0; + ocall_print(&bytes_written, "SECURITY ERROR: Invalid wasi_argv\n"); + *args_org = false; + return; + } + + if (addr_pool_list_size > 0 + && (!addr_pool_list + || !sgx_is_outside_enclave(addr_pool_list, + sizeof(char *) * addr_pool_list_size))) { + int bytes_written = 0; + ocall_print(&bytes_written, "SECURITY ERROR: Invalid addr_pool_list\n"); + *args_org = false; + return; + } + + total_size += sizeof(char *) * (uint64)dir_list_size + + sizeof(char *) * (uint64)env_list_size + + sizeof(char *) * (uint64)addr_pool_list_size + + sizeof(char *) * (uint64)wasi_argc; + + for (i = 0; i < dir_list_size; i++) { + total_size += strlen(dir_list[i]) + 1; + } + + for (i = 0; i < env_list_size; i++) { + total_size += strlen(env_list[i]) + 1; + } + + for (i = 0; i < addr_pool_list_size; i++) { + total_size += strlen(addr_pool_list[i]) + 1; + } + + for (i = 0; i < wasi_argc; i++) { + total_size += strlen(wasi_argv[i]) + 1; + } + + if (total_size >= UINT32_MAX + || !(enclave_module->wasi_arg_buf = p = + (char *)wasm_runtime_malloc((uint32)total_size))) { + *args_org = false; + return; + } + + p1 = p + sizeof(char *) * dir_list_size + sizeof(char *) * env_list_size + + sizeof(char *) * addr_pool_list_size + sizeof(char *) * wasi_argc; + + if (dir_list_size > 0) { + enclave_module->wasi_dir_list = (char **)p; + enclave_module->wasi_dir_list_size = dir_list_size; + for (i = 0; i < dir_list_size; i++) { + enclave_module->wasi_dir_list[i] = p1; + str_len = strlen(dir_list[i]); + bh_memcpy_s(p1, str_len + 1, dir_list[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * dir_list_size; + } + + if (env_list_size > 0) { + enclave_module->wasi_env_list = (char **)p; + enclave_module->wasi_env_list_size = env_list_size; + for (i = 0; i < env_list_size; i++) { + enclave_module->wasi_env_list[i] = p1; + str_len = strlen(env_list[i]); + bh_memcpy_s(p1, str_len + 1, env_list[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * env_list_size; + } + + if (addr_pool_list_size > 0) { + enclave_module->wasi_addr_pool_list = (char **)p; + enclave_module->wasi_addr_pool_list_size = addr_pool_list_size; + for (i = 0; i < addr_pool_list_size; i++) { + enclave_module->wasi_addr_pool_list[i] = p1; + str_len = strlen(addr_pool_list[i]); + bh_memcpy_s(p1, str_len + 1, addr_pool_list[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * addr_pool_list_size; + } + + if (wasi_argc > 0) { + enclave_module->wasi_argv = (char **)p; + enclave_module->wasi_argc = wasi_argc; + for (i = 0; i < wasi_argc; i++) { + enclave_module->wasi_argv[i] = p1; + str_len = strlen(wasi_argv[i]); + bh_memcpy_s(p1, str_len + 1, wasi_argv[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * wasi_argc; + } + + wasm_runtime_set_wasi_args_ex( + enclave_module->module, (const char **)enclave_module->wasi_dir_list, + dir_list_size, NULL, 0, (const char **)enclave_module->wasi_env_list, + env_list_size, enclave_module->wasi_argv, enclave_module->wasi_argc, + (stdinfd != -1) ? stdinfd : 0, (stdoutfd != -1) ? stdoutfd : 1, + (stderrfd != -1) ? stderrfd : 2); + + wasm_runtime_set_wasi_addr_pool( + enclave_module->module, + (const char **)enclave_module->wasi_addr_pool_list, + addr_pool_list_size); + + *args_org = true; +} +#else +static void +handle_cmd_set_wasi_args(uint64 *args, int32 argc) +{ + *args = true; +} +#endif /* end of WASM_ENABLE_LIBC_WASI != 0 */ + +static void +handle_cmd_get_version(uint64 *args, uint32 argc) +{ + uint32 major, minor, patch; + bh_assert(argc == 3); + + wasm_runtime_get_version(&major, &minor, &patch); + args[0] = major; + args[1] = minor; + args[2] = patch; +} + +#if WASM_ENABLE_STATIC_PGO != 0 +static void +handle_cmd_get_pgo_prof_buf_size(uint64 *args, int32 argc) +{ + wasm_module_inst_t module_inst = *(wasm_module_inst_t *)args; + uint32 buf_len; + + bh_assert(argc == 1); + + if (!runtime_inited) { + args[0] = 0; + return; + } + + buf_len = wasm_runtime_get_pgo_prof_data_size(module_inst); + args[0] = buf_len; +} + +static void +handle_cmd_get_pro_prof_buf_data(uint64 *args, int32 argc) +{ + uint64 *args_org = args; + wasm_module_inst_t module_inst = *(wasm_module_inst_t *)args++; + char *buf = *(char **)args++; + uint32 len = *(uint32 *)args++; + uint32 bytes_dumped; + + bh_assert(argc == 3); + + if (!runtime_inited) { + args_org[0] = 0; + return; + } + + bytes_dumped = + wasm_runtime_dump_pgo_prof_data_to_buf(module_inst, buf, len); + args_org[0] = bytes_dumped; +} +#endif + +void +ecall_handle_command(unsigned cmd, unsigned char *cmd_buf, + unsigned cmd_buf_size) +{ + /* + * Validate buffer before processing + * cmd_buf can be NULL if cmd_buf_size is 0, but if cmd_buf_size is + * non-zero, cmd_buf must be valid + */ + if ((!cmd_buf && cmd_buf_size > 0) || (cmd_buf && cmd_buf_size == 0)) { + int bytes_written = 0; + ocall_print(&bytes_written, + "SECURITY ERROR: Invalid buffer parameters\n"); + return; + } + + // Because of [in, out] cmd_buf in edl, it is allocated inside enclave. + if (cmd_buf && sgx_is_outside_enclave(cmd_buf, cmd_buf_size)) { + int bytes_written = 0; + ocall_print(&bytes_written, + "SECURITY ERROR: Buffer should be inside enclave\n"); + return; + } + + if (cmd_buf_size % sizeof(uint64) != 0) { + int bytes_written = 0; + ocall_print(&bytes_written, + "SECURITY ERROR: Buffer alignment invalid\n"); + return; + } + + uint64 *args = (uint64 *)cmd_buf; + uint32 argc = cmd_buf_size / sizeof(uint64); + + LOG_VERBOSE("Received command %d with %u arguments.\n", cmd, argc); + + switch (cmd) { + case CMD_INIT_RUNTIME: + handle_cmd_init_runtime(args, argc); + break; + case CMD_LOAD_MODULE: + handle_cmd_load_module(args, argc); + break; + case CMD_SET_WASI_ARGS: + handle_cmd_set_wasi_args(args, argc); + break; + case CMD_INSTANTIATE_MODULE: + handle_cmd_instantiate_module(args, argc); + break; + case CMD_LOOKUP_FUNCTION: + break; + case CMD_CREATE_EXEC_ENV: + break; + case CMD_CALL_WASM: + break; + case CMD_EXEC_APP_FUNC: + handle_cmd_exec_app_func(args, argc); + break; + case CMD_EXEC_APP_MAIN: + handle_cmd_exec_app_main(args, argc); + break; + case CMD_GET_EXCEPTION: + handle_cmd_get_exception(args, argc); + break; + case CMD_DEINSTANTIATE_MODULE: + handle_cmd_deinstantiate_module(args, argc); + break; + case CMD_UNLOAD_MODULE: + handle_cmd_unload_module(args, argc); + break; + case CMD_DESTROY_RUNTIME: + handle_cmd_destroy_runtime(); + break; + case CMD_SET_LOG_LEVEL: + handle_cmd_set_log_level(args, argc); + break; + case CMD_GET_VERSION: + handle_cmd_get_version(args, argc); + break; +#if WASM_ENABLE_STATIC_PGO != 0 + case CMD_GET_PGO_PROF_BUF_SIZE: + handle_cmd_get_pgo_prof_buf_size(args, argc); + break; + case CMD_DUMP_PGO_PROF_BUF_DATA: + handle_cmd_get_pro_prof_buf_data(args, argc); + break; +#endif + default: + LOG_ERROR("Unknown command %d\n", cmd); + break; + } +} + +void +ecall_iwasm_main(uint8_t *wasm_file_buf, uint32_t wasm_file_size) +{ wasm_module_t wasm_module = NULL; wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; char error_buf[128]; + const char *exception; - if (bh_memory_init_with_pool(global_heap_buf, - sizeof(global_heap_buf)) != 0) { - ocall_print("Init global heap failed.\n"); + /* avoid duplicated init */ + if (runtime_inited) { return; } - /* initialize runtime environment */ - if (!wasm_runtime_init()) - goto fail1; + os_set_print_function(enclave_print); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); - /* load WASM byte buffer from byte buffer of include file */ - wasm_file_buf = (uint8_t*) wasm_test_file; - wasm_file_size = sizeof(wasm_test_file); +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_System_Allocator; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + enclave_print("Init runtime environment failed."); + enclave_print("\n"); + return; + } /* load WASM module */ if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, error_buf, sizeof(error_buf)))) { - ocall_print(error_buf); - ocall_print("\n"); - goto fail2; + enclave_print(error_buf); + enclave_print("\n"); + goto fail1; } /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, - 16 * 1024, - 16 * 1024, - error_buf, - sizeof(error_buf)))) { - ocall_print(error_buf); - ocall_print("\n"); - goto fail3; + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, 16 * 1024, 16 * 1024, + error_buf, sizeof(error_buf)))) { + enclave_print(error_buf); + enclave_print("\n"); + goto fail2; } /* execute the main function of wasm app */ - app_instance_main(wasm_module_inst); + wasm_application_execute_main(wasm_module_inst, 0, NULL); + if ((exception = wasm_runtime_get_exception(wasm_module_inst))) { + enclave_print(exception); + enclave_print("\n"); + } /* destroy the module instance */ wasm_runtime_deinstantiate(wasm_module_inst); -fail3: +fail2: /* unload the module */ wasm_runtime_unload(wasm_module); -fail2: +fail1: /* destroy runtime environment */ wasm_runtime_destroy(); - -fail1: - bh_memory_destroy(); } - diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl index fc7ef56ea9..5fe69a8d3f 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl @@ -3,16 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#define WASM_ENABLE_SGX_IPFS 0 +#define WASM_ENABLE_LIB_RATS 0 + enclave { from "sgx_tstdc.edl" import *; + from "sgx_pthread.edl" import *; + from "sgx_wamr.edl" import *; +#if WASM_ENABLE_LIB_RATS != 0 + from "rats.edl" import *; + from "sgx_tsgxssl.edl" import *; +#endif +#if WASM_ENABLE_SGX_IPFS != 0 + from "sgx_tprotected_fs.edl" import *; +#endif + //TODO: replace void with an int as error code trusted { /* define ECALLs here. */ - public void ecall_iwasm_main(void); + public void ecall_handle_command(unsigned cmd, + [in, out, size=cmd_buf_size]uint8_t *cmd_buf, + unsigned cmd_buf_size); + public void ecall_iwasm_main([user_check]uint8_t *wasm_file_buf, + uint32_t wasm_file_size); }; untrusted { /* define OCALLs here. */ - void ocall_print([in, string]const char* str); + int ocall_print([in, string]const char* str); }; }; diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_minimal.edl b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_minimal.edl new file mode 100644 index 0000000000..f7c1ebee98 --- /dev/null +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_minimal.edl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +enclave { + from "sgx_tstdc.edl" import *; + + trusted { + /* define ECALLs here. */ + public void ecall_handle_command(unsigned cmd, + [in, out, size=cmd_buf_size]uint8_t *cmd_buf, + unsigned cmd_buf_size); + public void ecall_iwasm_main([user_check]uint8_t *wasm_file_buf, + uint32_t wasm_file_size); + }; + + untrusted { + /* define OCALLs here. */ + int ocall_print([in, string]const char* str); + }; +}; diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp new file mode 100644 index 0000000000..2ae6454d61 --- /dev/null +++ b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include "Enclave_t.h" +#include "wasm_export.h" +#include "bh_platform.h" + +void +ecall_iwasm_test() +{ + ocall_print(" Prepare to invoke sgx_open ... ==>\n\n"); + + /* creat test.txt firstly */ + char file[] = "test.txt"; + char file_hd_ln[] = "test_hd_ln.txt"; + char file_sf_ln[] = "test_sf_ln.txt"; + char rlt_dir_path[] = "./tmp"; + char rlt_dir_path_new[] = "./tmp_new"; + char *str0 = (char *)"good luck, "; + char *str1 = (char *)"have fun!"; + + char buf0[4], buf1[4]; + struct iovec iov_w[2]; + struct iovec iov_r[2]; + + char buf[2048]; + ssize_t total_size; + ssize_t s_ret; + int fd; + int dird; + int file_buf; + int ret; + long ret_l; + DIR *dirp; + struct dirent *p_dirent; + struct stat *statbuf; + struct pollfd fds[1]; + char *res; + + /** getopt value **/ + int argc = 3; + char n_1[] = "./main"; + char n_2[] = "-a"; + char n_3[] = "test.txt"; + + char *argv[3]; + argv[0] = n_1; + argv[1] = n_2; + argv[2] = n_3; + + /* time clock */ + struct timespec ts; + struct timespec t_res; + struct timespec times[2]; + struct stat statbuf_2; + times[0] = statbuf_2.st_atim; + times[1] = statbuf_2.st_mtim; + struct timespec rqtp; + struct timespec rmtp; + rqtp.tv_sec = 0; + rqtp.tv_nsec = 0; + + /** mkdirat **/ + /* mkdir tmp in current directory for test + * if the ./tmp directory has exits, mkdirat will fail + */ + ret = mkdirat(AT_FDCWD, rlt_dir_path, 0755); + if (ret == 0) { + ocall_print("\tOperation mkdirat success.\n"); + } + + /* flags: + 0100: - O_CREAT + 02 : - O_RDWR */ + /** 1. open **/ + fd = open(file, O_RDWR); + if (fd != -1) { + ocall_print("\tOperation open test_open.txt success.\n"); + } + + /** read **/ + total_size = read(fd, buf, 2048); + if (total_size != -1) { + ocall_print("\tOperation read test_open.txt success.\n"); + ocall_print("\t\tthe details of the file: "); + ocall_print(buf); + ocall_print("\n"); + } + + /** lseek **/ + ret = lseek(fd, 1, SEEK_CUR); + if (ret != -1) { + ocall_print("\tOperation lseek success.\n"); + } + + /** ftruncate **/ + ret = ftruncate(fd, 0); + if (ret == 0) { + ocall_print("\tOperation ftruncate success.\n"); + } + + /** fdatasync **/ + ret = fdatasync(fd); + if (ret == 0) { + ocall_print("\tOperation fdatasync success.\n"); + } + + /** isatty **/ + ret = isatty(fd); + if (ret == 0) { + ocall_print("\tOperation fisatty success.\n"); + } + + /** fsync **/ + ret = fsync(fd); + if (ret == 0) { + ocall_print("\tOperation fsync success.\n"); + } + + /** 1. close **/ + ret = close(fd); + if (ret != -1) { + ocall_print("\tOperation close success.\n"); + } + + /*----------------------------------------------------------------*/ + + /**-- DIR --**/ + /** fdopendir **/ + /* 2. open */ + dird = open(rlt_dir_path, O_RDONLY); + dirp = fdopendir(dird); + if (dirp != NULL) { + ocall_print("\tOperation fdopendir success.\n"); + } + + /** readdir **/ + p_dirent = readdir(dirp); + if (p_dirent != NULL) { + ocall_print("\tOperation readdir success.\t"); + ocall_print(p_dirent->d_name); + ocall_print("\n"); + } + + /** rewinddir **/ + rewinddir(dirp); + + /** seekdir **/ + seekdir(dirp, 1); + + /** telldir **/ + ret_l = telldir(dirp); + if (ret_l != -1) { + ocall_print("\tOperation telldir success. \n"); + } + + /** closedir **/ + ret = closedir(dirp); + if (ret == 0) { + ocall_print("\tOperation closedir success. \n"); + } + /* 2. close */ + close(dird); + + /*----------------------------------------------------------------*/ + + /** fstat **/ + /** 3. open file firstly **/ + fd = open(file, O_RDWR); + statbuf = (stat *)malloc(sizeof(stat)); + ret = fstat(fd, statbuf); + if (ret == 0) { + ocall_print("\tOperation fstat success. \n"); + } + free(statbuf); + /* 3. close */ + close(fd); + + /*----------------------------------------------------------------*/ + + /** fstatat **/ + /* 4. open */ + dird = open(rlt_dir_path, O_RDONLY); + ret = fstatat(AT_FDCWD, rlt_dir_path, statbuf, 0); + if (ret == 0) { + ocall_print("\tOperation fstatat success. \n"); + } + + /** renameat **/ + ret = renameat(AT_FDCWD, rlt_dir_path, AT_FDCWD, rlt_dir_path_new); + if (ret == 0) { + ocall_print("\tOperation renameat ./tmp to " + "./tmp_new success. \n"); + } + renameat(AT_FDCWD, rlt_dir_path_new, AT_FDCWD, rlt_dir_path); + + /** link **/ + ret = link(file, file_hd_ln); + if (ret == 0) { + ocall_print("\tOperation link success. \n"); + } + + /** unlinkat **/ + ret = unlinkat(AT_FDCWD, file_hd_ln, 0); + if (ret == 0) { + ocall_print("\tOperation unlinkat success. \n"); + } + + /** linkat **/ + ret = linkat(AT_FDCWD, file, AT_FDCWD, file_hd_ln, 0); + if (ret == 0) { + ocall_print("\tOperation linkat success. \n"); + } + /* delete hard link file */ + unlinkat(AT_FDCWD, file_hd_ln, 0); + + /** symlinkat **/ + ret = symlinkat(file, AT_FDCWD, file_sf_ln); + if (ret == 0) { + ocall_print("\tOperation symlinkat from test.txt " + "to text_sf_ln.txt success. \n"); + } + /** readlinkat **/ + total_size = readlinkat(AT_FDCWD, file_sf_ln, buf, sizeof(buf)); + if (total_size != -1) { + ocall_print("\tOperation readlinkat success. \n"); + ocall_print("\t\t the link details of the file is: "); + ocall_print(buf); + ocall_print("\n"); + } + /* delete soft link file */ + unlinkat(AT_FDCWD, file_sf_ln, 0); + /* 4. close */ + close(dird); + + /*----------------------------------------------------------------*/ + + /* 5. open */ + fd = open(file, O_RDWR); + /** ioctl **/ + ret = ioctl(fd, FIONREAD, &file_buf); + if (ret == 0) { + ocall_print("\tOperation ioctl success. \n"); + } + /** fcntl(fd, cmd) **/ + ret = fcntl(fd, F_GETFD); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation fcntl_1 success. \n"); + } + /** fcntl(fd, cmd, long) **/ + ret = fcntl(fd, F_SETFD, ret); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation fcntl_2 success. \n"); + } + + /* 5. close */ + close(fd); + + /*----------------------------------------------------------------*/ + + /** posix_fallocate **/ + /* 6. open */ + fd = open(file, O_RDWR); + ret = posix_fallocate(fd, 1, 1); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation posix_fallocate success. \n"); + } + /* 6. close */ + close(fd); + + /** poll **/ + ret = poll(fds, 1, 10); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation poll success. \n"); + } + + /** realpath **/ + res = realpath(file, res); + if (res) { + ocall_print("\tOperation realpath success. \n"); + ocall_print("\t\t the absolute path of the file is: "); + ocall_print(res); + ocall_print("\n"); + } + + /** getrandom **/ + total_size = getrandom(buf, 1024, 0); + if (ret != -1) { + ocall_print("\tOperation getrandom success. \n"); + } + + /** writev **/ + /* 7. open */ + fd = open(file, O_RDWR); + iov_w[0].iov_base = str0; + iov_w[0].iov_len = strlen(str0); + iov_w[1].iov_base = str1; + iov_w[1].iov_len = strlen(str1); + + s_ret = writev(fd, iov_w, 2); + if (s_ret != -1) { + ocall_print("\tOperation writev success. \n"); + } + + /** readv **/ + iov_r[0].iov_base = buf0; + iov_r[0].iov_len = sizeof(buf0) - 1; + iov_r[1].iov_base = buf1; + iov_r[1].iov_len = sizeof(buf1) - 1; + + s_ret = readv(fd, iov_r, 2); + if (s_ret != -1) { + ocall_print("\tOperation readv success. \n"); + ocall_print("\t\t"); + ocall_print(buf0); + ocall_print(buf1); + ocall_print("\n"); + } + + iov_r[0].iov_base = buf0; + iov_r[0].iov_len = sizeof(buf0) - 1; + iov_r[1].iov_base = buf1; + iov_r[1].iov_len = sizeof(buf1) - 1; + + s_ret = preadv(fd, iov_r, 2, 2); + if (s_ret != -1) { + ocall_print("\tOperation readv success. \n"); + ocall_print("\t\t"); + ocall_print(buf0); + ocall_print(buf1); + ocall_print("\n"); + } + /* 7. close */ + close(fd); + + /** getopt **/ + while ((ret = getopt(argc, argv, "f:abc")) + != -1) { // get option from the getopt() method + switch (ret) { + // For option i, r, l, print that these are options + case 'a': + case 'b': + case 'c': + ocall_print("\tGiven Option operation success. \n"); + break; + case 'f': // here f is used for some file name + ocall_print("\tGiven File operation success.\n"); + break; + case '?': // used for some unknown options + ocall_print("\tunknown option trigger success.\n"); + break; + } + } + + /** sched_yield **/ + ret = sched_yield(); + if (ret == 0) { + ocall_print("\tOperation sched_yield success. \n"); + } + + /** clock_gettime **/ + ret = clock_gettime(CLOCK_REALTIME, &ts); + if (ret == 0) { + ocall_print("\tOperation clock_gettime success. \n"); + } + + /** clock_getres **/ + ret = clock_getres(CLOCK_REALTIME, &t_res); + if (ret == 0) { + ocall_print("\tOperation clock_getres success. \n"); + } + + /** futimens **/ + /* 8. open */ + fd = open(file, O_RDWR); + ret = futimens(fd, NULL); + if (ret == 0) { + ocall_print("\tOperation futimens NULL success. \n"); + } + + ret = futimens(fd, times); + if (ret == 0) { + ocall_print("\tOperation futimens times[2] success. \n"); + } + /* 8. close */ + close(fd); + + /** utimensat **/ + /* 9. open */ + dird = open(rlt_dir_path, O_RDONLY); + ret = utimensat(AT_FDCWD, file, NULL, AT_SYMLINK_NOFOLLOW); + if (ret == 0) { + ocall_print("\tOperation utimensat NULL success. \n"); + } + + ret = utimensat(AT_FDCWD, file, times, AT_SYMLINK_NOFOLLOW); + if (ret == 0) { + ocall_print("\tOperation utimensat times[2] success. \n"); + } + /* 9. close */ + close(fd); + + /** clock_nanosleep **/ + ret = clock_nanosleep(CLOCK_REALTIME, 0, &rqtp, NULL); + if (ret == 0) { + ocall_print("\tOperation clock_nanosleep NULL success. \n"); + } + + ret = clock_nanosleep(CLOCK_REALTIME, 0, &rqtp, &rmtp); + if (ret == 0) { + ocall_print("\tOperation clock_nanosleep 2 success. \n"); + } + + ocall_print("\n<== ... End test\n"); +} \ No newline at end of file diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/test_wasm.h b/product-mini/platforms/linux-sgx/enclave-sample/Enclave/test_wasm.h deleted file mode 100644 index 65b8347982..0000000000 --- a/product-mini/platforms/linux-sgx/enclave-sample/Enclave/test_wasm.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/** - * The byte array buffer is the file content of a test wasm binary file, - * which is compiled by emcc or clang toolchain from C source file of: - * core/iwasm/app-samples/hello-world/main.c. - */ -unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, - 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, - 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, - 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, - 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, - 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, - 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, - 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, - 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, - 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, - 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, - 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, - 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, - 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, - 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, - 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, - 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, - 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, - 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, - 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, - 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, - 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, - 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, - 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, - 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, - 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, - 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, - 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, - 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, - 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, - 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, - 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, - 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, - 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, - 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, - 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, - 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, - 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, - 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, - 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, - 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, - 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, - 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, - 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, - 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, - 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, - 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Makefile b/product-mini/platforms/linux-sgx/enclave-sample/Makefile index dac4f78da1..8fd053a5fb 100644 --- a/product-mini/platforms/linux-sgx/enclave-sample/Makefile +++ b/product-mini/platforms/linux-sgx/enclave-sample/Makefile @@ -4,8 +4,24 @@ ######## SGX SDK Settings ######## SGX_SDK ?= /opt/intel/sgxsdk +SGX_SSL ?= /opt/intel/sgxssl SGX_MODE ?= SIM SGX_ARCH ?= x64 +SGX_DEBUG ?= 0 +SPEC_TEST ?= 0 + +# These variables are automatically set by CMakeLists.txt +WAMR_BUILD_SGX_IPFS = 0 +WAMR_BUILD_LIB_RATS = 0 +WAMR_BUILD_GLOBAL_HEAP_POOL = 0 +WAMR_BUILD_GLOBAL_HEAP_SIZE = 10485760 +WAMR_BUILD_STATIC_PGO = 0 +WAMR_BUILD_LIBC_WASI = 1 + +VMLIB_BUILD_DIR ?= $(CURDIR)/../build +LIB_RATS_SRC ?= $(VMLIB_BUILD_DIR)/_deps/librats-build +LIB_RATS_INSTALL_DIR := $(VMLIB_BUILD_DIR)/librats/lib/librats +LIB_RATS_INCLUDE_DIR := $(VMLIB_BUILD_DIR)/librats/include ifeq ($(shell getconf LONG_BIT), 32) SGX_ARCH := x86 @@ -32,9 +48,9 @@ endif endif ifeq ($(SGX_DEBUG), 1) - SGX_COMMON_CFLAGS += -O0 -g + SGX_COMMON_CFLAGS += -O0 -g else - SGX_COMMON_CFLAGS += -O2 + SGX_COMMON_CFLAGS += -O2 endif ######## App Settings ######## @@ -47,23 +63,34 @@ endif App_Cpp_Files := App/App.cpp App_Include_Paths := -IApp -I$(SGX_SDK)/include +ifeq ($(WAMR_BUILD_LIB_RATS), 1) + App_Include_Paths += -I$(LIB_RATS_INCLUDE_DIR) +endif -App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths) +App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths) \ + -DWASM_ENABLE_STATIC_PGO=$(WAMR_BUILD_STATIC_PGO) \ + -DWASM_ENABLE_LIBC_WASI=$(WAMR_BUILD_LIBC_WASI) # Three configuration modes - Debug, prerelease, release # Debug - Macro DEBUG enabled. # Prerelease - Macro NDEBUG and EDEBUG enabled. # Release - Macro NDEBUG enabled. ifeq ($(SGX_DEBUG), 1) - App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG + App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG else ifeq ($(SGX_PRERELEASE), 1) - App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG + App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG +else + App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG +endif + +ifeq ($(SPEC_TEST), 1) + App_C_Flags += -DWASM_ENABLE_SPEC_TEST=1 else - App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG + App_C_Flags += -DWASM_ENABLE_SPEC_TEST=0 endif App_Cpp_Flags := $(App_C_Flags) -std=c++11 -App_Link_Flags := $(SGX_COMMON_CFLAGS) -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) -lpthread +App_Link_Flags := $(SGX_COMMON_CFLAGS) libvmlib_untrusted.a -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) -lpthread ifneq ($(SGX_MODE), HW) App_Link_Flags += -lsgx_uae_service_sim @@ -71,9 +98,13 @@ else App_Link_Flags += -lsgx_uae_service endif +ifeq ($(WAMR_BUILD_LIB_RATS), 1) + App_Link_Flags += -L$(LIB_RATS_INSTALL_DIR) -L$(SGX_SSL)/lib64 -lrats_u -lsgx_dcap_ql -lsgx_dcap_quoteverify -lsgx_ukey_exchange -lsgx_usgxssl +endif + App_Cpp_Objects := $(App_Cpp_Files:.cpp=.o) -App_Name := app +App_Name := iwasm ######## Enclave Settings ######## @@ -84,24 +115,66 @@ else Trts_Library_Name := sgx_trts Service_Library_Name := sgx_tservice endif + +ifeq ($(WAMR_BUILD_SGX_IPFS), 1) + Intel_Ipfs_Trusted_Flag = -lsgx_tprotected_fs + App_Link_Flags += -lsgx_uprotected_fs +endif + Crypto_Library_Name := sgx_tcrypto WAMR_ROOT := $(CURDIR)/../../../../ Enclave_Cpp_Files := Enclave/Enclave.cpp + Enclave_Include_Paths := -IEnclave -I$(WAMR_ROOT)/core/iwasm/include \ - -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport - -Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector $(Enclave_Include_Paths) -Enclave_Cpp_Flags := $(Enclave_C_Flags) -std=c++03 -nostdinc++ -Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ - -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ - libvmlib.a libextlib.a \ - -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \ + -I$(WAMR_ROOT)/core/shared/utils \ + -I$(WAMR_ROOT)/core/shared/platform/linux-sgx \ + -I$(SGX_SDK)/include \ + -I$(SGX_SDK)/include/tlibc \ + -I$(SGX_SDK)/include/stlport + +ifeq ($(WAMR_BUILD_LIB_RATS), 1) + Enclave_Include_Paths += -I$(LIB_RATS_INCLUDE_DIR) -I$(SGX_SSL)/include +endif + +Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden \ + -fpie -fstack-protector $(Enclave_Include_Paths) \ + -DWASM_GLOBAL_HEAP_SIZE=$(WAMR_BUILD_GLOBAL_HEAP_SIZE) \ + -DWASM_ENABLE_GLOBAL_HEAP_POOL=$(WAMR_BUILD_GLOBAL_HEAP_POOL) \ + -DWASM_ENABLE_LIB_RATS=$(WAMR_BUILD_LIB_RATS) \ + -DWASM_ENABLE_STATIC_PGO=$(WAMR_BUILD_STATIC_PGO) \ + -DWASM_ENABLE_LIBC_WASI=$(WAMR_BUILD_LIBC_WASI) +ifeq ($(SPEC_TEST), 1) + Enclave_C_Flags += -DWASM_ENABLE_SPEC_TEST=1 +else + Enclave_C_Flags += -DWASM_ENABLE_SPEC_TEST=0 +endif + +ifeq ($(WAMR_BUILD_LIB_RATS), 1) + Rats_Lib_Link_Dirs := -L$(LIB_RATS_INSTALL_DIR) -L$(LIB_RATS_INSTALL_DIR)/attesters -L$(LIB_RATS_INSTALL_DIR)/verifiers -L$(SGX_SSL)/lib64 -L$(VMLIB_BUILD_DIR)/external/libcbor/src/libcbor/lib -L$(LIB_RATS_INSTALL_DIR)/crypto_wrappers + Rats_Lib_W_Link_libs := -lattester_nullattester -lattester_sgx_ecdsa -lattester_sgx_la \ + -lverifier_nullverifier -lverifier_sgx_la -lverifier_sgx_ecdsa_qve -lcbor \ + -lrats_lib -lsgx_tsgxssl -lcrypto_wrapper_nullcrypto -lcrypto_wrapper_openssl + Rats_Lib_NW_Link_libs := -lsgx_dcap_tvl -lsgx_tsgxssl_crypto +endif + +Enclave_Cpp_Flags := $(Enclave_C_Flags) -std=c++11 -nostdinc++ +Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) ${Rats_Lib_Link_Dirs} \ + -Wl,--whole-archive -l$(Trts_Library_Name) ${Rats_Lib_W_Link_libs} $(Intel_Ipfs_Trusted_Flag) -Wl,--no-whole-archive \ + -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -lsgx_pthread -lsgx_tkey_exchange -l$(Crypto_Library_Name) -l$(Service_Library_Name) $(Rats_Lib_NW_Link_libs) -Wl,--end-group \ -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ -Wl,--defsym,__ImageBase=0 +Enclave_Edl_Search_Path = --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx +ifeq ($(WAMR_BUILD_LIB_RATS), 1) + Enclave_Edl_Search_Path += --search-path $(LIB_RATS_INCLUDE_DIR)/librats/edl --search-path $(SGX_SSL)/include +endif + + Enclave_Cpp_Objects := $(Enclave_Cpp_Files:.cpp=.o) Enclave_Name := enclave.so @@ -138,9 +211,14 @@ ifneq ($(Build_Mode), HW_RELEASE) endif ######## App Objects ######## +librats: +ifeq ($(WAMR_BUILD_LIB_RATS), 1) + @cd $(LIB_RATS_SRC) && make install + @echo "librats build success" +endif -App/Enclave_u.c: $(SGX_EDGER8R) Enclave/Enclave.edl - @cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl --search-path ../Enclave --search-path $(SGX_SDK)/include +App/Enclave_u.c: $(SGX_EDGER8R) Enclave/Enclave.edl librats + @cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl $(Enclave_Edl_Search_Path) @echo "GEN => $@" App/Enclave_u.o: App/Enclave_u.c @@ -151,15 +229,18 @@ App/%.o: App/%.cpp @$(CXX) $(App_Cpp_Flags) -c $< -o $@ @echo "CXX <= $<" -$(App_Name): App/Enclave_u.o $(App_Cpp_Objects) +libvmlib_untrusted.a: $(VMLIB_BUILD_DIR)/libvmlib_untrusted.a + @cp $< $@ + @echo "CP $@ <= $<" + +$(App_Name): App/Enclave_u.o $(App_Cpp_Objects) libvmlib_untrusted.a @$(CXX) $^ -o $@ $(App_Link_Flags) @echo "LINK => $@" ######## Enclave Objects ######## - -Enclave/Enclave_t.c: $(SGX_EDGER8R) Enclave/Enclave.edl - @cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl --search-path ../Enclave --search-path $(SGX_SDK)/include +Enclave/Enclave_t.c: $(SGX_EDGER8R) Enclave/Enclave.edl librats + @cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl $(Enclave_Edl_Search_Path) @echo "GEN => $@" Enclave/Enclave_t.o: Enclave/Enclave_t.c @@ -170,15 +251,11 @@ Enclave/%.o: Enclave/%.cpp @$(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ @echo "CXX <= $<" -libvmlib.a: ../build/libvmlib.a - @cp $< $@ - @echo "CP $@ <= $<" - -libextlib.a: ../build/libextlib.a +libvmlib.a: $(VMLIB_BUILD_DIR)/libvmlib.a @cp $< $@ @echo "CP $@ <= $<" -$(Enclave_Name): Enclave/Enclave_t.o $(Enclave_Cpp_Objects) libvmlib.a libextlib.a +$(Enclave_Name): Enclave/Enclave_t.o $(Enclave_Cpp_Objects) libvmlib.a @$(CXX) $^ -o $@ $(Enclave_Link_Flags) @echo "LINK => $@" @@ -189,4 +266,4 @@ $(Signed_Enclave_Name): $(Enclave_Name) .PHONY: clean clean: - @rm -f $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.* + @rm -f $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.* libvmlib.a libvmlib_untrusted.a diff --git a/product-mini/platforms/linux-sgx/enclave-sample/Makefile_minimal b/product-mini/platforms/linux-sgx/enclave-sample/Makefile_minimal new file mode 100644 index 0000000000..07b640da2c --- /dev/null +++ b/product-mini/platforms/linux-sgx/enclave-sample/Makefile_minimal @@ -0,0 +1,210 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +######## SGX SDK Settings ######## + +SGX_SDK ?= /opt/intel/sgxsdk +SGX_MODE ?= SIM +SGX_ARCH ?= x64 +SGX_DEBUG ?= 0 +SPEC_TEST ?= 0 + +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g +else + SGX_COMMON_CFLAGS += -O2 +endif + +######## App Settings ######## + +ifneq ($(SGX_MODE), HW) + Urts_Library_Name := sgx_urts_sim +else + Urts_Library_Name := sgx_urts +endif + +App_Cpp_Files := App/App.cpp +App_Include_Paths := -IApp -I$(SGX_SDK)/include + +App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths) + +# Three configuration modes - Debug, prerelease, release +# Debug - Macro DEBUG enabled. +# Prerelease - Macro NDEBUG and EDEBUG enabled. +# Release - Macro NDEBUG enabled. +ifeq ($(SGX_DEBUG), 1) + App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG +else ifeq ($(SGX_PRERELEASE), 1) + App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG +else + App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG +endif + +App_Cpp_Flags := $(App_C_Flags) -std=c++11 +App_Link_Flags := $(SGX_COMMON_CFLAGS) -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) -lpthread + +ifneq ($(SGX_MODE), HW) + App_Link_Flags += -lsgx_uae_service_sim +else + App_Link_Flags += -lsgx_uae_service +endif + +App_Cpp_Objects := $(App_Cpp_Files:.cpp=.o) + +App_Name := iwasm + +######## Enclave Settings ######## + +ifneq ($(SGX_MODE), HW) + Trts_Library_Name := sgx_trts_sim + Service_Library_Name := sgx_tservice_sim +else + Trts_Library_Name := sgx_trts + Service_Library_Name := sgx_tservice +endif +Crypto_Library_Name := sgx_tcrypto + +WAMR_ROOT := $(CURDIR)/../../../../ + +Enclave_Cpp_Files := Enclave/Enclave.cpp + +Enclave_Include_Paths := -IEnclave -I$(WAMR_ROOT)/core/iwasm/include \ + -I$(WAMR_ROOT)/core/shared/utils \ + -I$(WAMR_ROOT)/core/shared/platform/linux-sgx \ + -I$(SGX_SDK)/include \ + -I$(SGX_SDK)/include/tlibc \ + -I$(SGX_SDK)/include/stlport + +Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector $(Enclave_Include_Paths) + +# disable wasi +Enclave_C_Flags += -DWASM_ENABLE_LIBC_WASI=0 + +ifeq ($(SPEC_TEST), 1) + Enclave_C_Flags += -DWASM_ENABLE_SPEC_TEST=1 +else + Enclave_C_Flags += -DWASM_ENABLE_SPEC_TEST=0 +endif +Enclave_Cpp_Flags := $(Enclave_C_Flags) -std=c++03 -nostdinc++ +Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ + -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ + libvmlib.a \ + -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \ + -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ + -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ + -Wl,--defsym,__ImageBase=0 + +Enclave_Cpp_Objects := $(Enclave_Cpp_Files:.cpp=.o) + +Enclave_Name := enclave.so +Signed_Enclave_Name := enclave.signed.so +Enclave_Config_File := Enclave/Enclave.config.xml + +ifeq ($(SGX_MODE), HW) +ifneq ($(SGX_DEBUG), 1) +ifneq ($(SGX_PRERELEASE), 1) +Build_Mode = HW_RELEASE +endif +endif +endif + + +.PHONY: all run + +ifeq ($(Build_Mode), HW_RELEASE) +all: $(App_Name) $(Enclave_Name) + @echo "The project has been built in release hardware mode." + @echo "Please sign the $(Enclave_Name) first with your signing key before you run the $(App_Name) to launch and access the enclave." + @echo "To sign the enclave use the command:" + @echo " $(SGX_ENCLAVE_SIGNER) sign -key -enclave $(Enclave_Name) -out <$(Signed_Enclave_Name)> -config $(Enclave_Config_File)" + @echo "You can also sign the enclave using an external signing tool. See User's Guide for more details." + @echo "To build the project in simulation mode set SGX_MODE=SIM. To build the project in prerelease mode set SGX_PRERELEASE=1 and SGX_MODE=HW." +else +all: $(App_Name) $(Signed_Enclave_Name) +endif + +run: all +ifneq ($(Build_Mode), HW_RELEASE) + @$(CURDIR)/$(App_Name) + @echo "RUN => $(App_Name) [$(SGX_MODE)|$(SGX_ARCH), OK]" +endif + +######## App Objects ######## + +App/Enclave_u.c: $(SGX_EDGER8R) Enclave/Enclave.edl + @cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx + @echo "GEN => $@" + +App/Enclave_u.o: App/Enclave_u.c + @$(CC) $(App_C_Flags) -c $< -o $@ + @echo "CC <= $<" + +App/%.o: App/%.cpp + @$(CXX) $(App_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +$(App_Name): App/Enclave_u.o $(App_Cpp_Objects) + @$(CXX) $^ -o $@ $(App_Link_Flags) + @echo "LINK => $@" + + +######## Enclave Objects ######## + +Enclave/Enclave_t.c: $(SGX_EDGER8R) Enclave/Enclave.edl + @cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx + @echo "GEN => $@" + +Enclave/Enclave_t.o: Enclave/Enclave_t.c + @$(CC) $(Enclave_C_Flags) -c $< -o $@ + @echo "CC <= $<" + +Enclave/%.o: Enclave/%.cpp + @$(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +libvmlib.a: ../build/libvmlib.a + @cp $< $@ + @echo "CP $@ <= $<" + +$(Enclave_Name): Enclave/Enclave_t.o $(Enclave_Cpp_Objects) libvmlib.a + @$(CXX) $^ -o $@ $(Enclave_Link_Flags) + @echo "LINK => $@" + +$(Signed_Enclave_Name): $(Enclave_Name) + @$(SGX_ENCLAVE_SIGNER) sign -key Enclave/Enclave_private.pem -enclave $(Enclave_Name) -out $@ -config $(Enclave_Config_File) + @echo "SIGN => $@" + +.PHONY: clean + +clean: + @rm -f $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.* libvmlib.a diff --git a/product-mini/platforms/linux-sgx/ext_lib_export.c b/product-mini/platforms/linux-sgx/ext_lib_export.c deleted file mode 100644 index 8813f0dbf3..0000000000 --- a/product-mini/platforms/linux-sgx/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index 5f1aeb69b5..a0c0675d52 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -1,25 +1,47 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8) +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) project (iwasm) +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) + +set (CMAKE_VERBOSE_MAKEFILE OFF) + set (WAMR_BUILD_PLATFORM "linux") # 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 17) + # Set WAMR_BUILD_TARGET, currently values supported: -# "X86_64", "AMD_64", "X86_32", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +# "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_SIZEOF_VOID_P EQUAL 8) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + 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") - else () + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + 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 () @@ -42,6 +64,11 @@ if (NOT DEFINED WAMR_BUILD_JIT) set (WAMR_BUILD_JIT 0) endif () +if (NOT DEFINED WAMR_BUILD_FAST_JIT) + # Disable Fast JIT by default + set (WAMR_BUILD_FAST_JIT 0) +endif () + if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) # Enable libc builtin support by default set (WAMR_BUILD_LIBC_BUILTIN 1) @@ -52,22 +79,66 @@ if (NOT DEFINED WAMR_BUILD_LIBC_WASI) set (WAMR_BUILD_LIBC_WASI 1) endif () -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () -include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) -add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Disable multiple modules by default + set (WAMR_BUILD_MULTI_MODULE 0) +endif () -set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE") +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () -set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") -# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion -Wsign-conversion") +if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) + # Disable wasi threads library by default + set (WAMR_BUILD_LIB_WASI_THREADS 0) +endif() -if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") - if (NOT (${CMAKE_C_COMPILER} MATCHES ".*clang.*")) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") - endif () +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + # Enable reference types by default + set (WAMR_BUILD_REF_TYPES 1) endif () +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +# if enable wasi-nn, both wasi-nn-backends and iwasm +# need to use same WAMR (dynamic) libraries +if (WAMR_BUILD_WASI_NN EQUAL 1) + set (BUILD_SHARED_LIBS ON) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +check_pie_supported() + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + # The following flags are to enhance security, but it may impact performance, # we disable them by default. #if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") @@ -76,17 +147,44 @@ endif () #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") -add_executable (iwasm main.c ext_lib_export.c) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +set_version_info (iwasm) + +set_target_properties (iwasm PROPERTIES POSITION_INDEPENDENT_CODE ON) + +target_link_libraries(iwasm vmlib) install (TARGETS iwasm DESTINATION bin) -target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) +add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (vmlib) + +target_include_directories(vmlib INTERFACE + $ +) -add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +set (WAMR_PUBLIC_HEADERS + ${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 +) -install (TARGETS libiwasm DESTINATION lib) +set_target_properties (vmlib PROPERTIES + OUTPUT_NAME iwasm + PUBLIC_HEADER "${WAMR_PUBLIC_HEADERS}" + POSITION_INDEPENDENT_CODE ON +) -set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) -target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) +install (TARGETS vmlib + EXPORT iwasmTargets + DESTINATION lib + PUBLIC_HEADER DESTINATION include +) +install_iwasm_package () diff --git a/product-mini/platforms/linux/build_jit.sh b/product-mini/platforms/linux/build_jit.sh index 908d1560c3..f794a37c8a 100755 --- a/product-mini/platforms/linux/build_jit.sh +++ b/product-mini/platforms/linux/build_jit.sh @@ -5,6 +5,8 @@ rm -fr build && mkdir build cd build +# By default LazyJIT is enabled, to disable it: +# cmake .. -DWAMR_BUILD_JIT=1 -DWAMR_BUILD_LAZY_JIT=0 cmake .. -DWAMR_BUILD_JIT=1 -make +make -j ${nproc} cd .. diff --git a/product-mini/platforms/linux/build_llvm.sh b/product-mini/platforms/linux/build_llvm.sh index d585792646..c5666b7f5d 100755 --- a/product-mini/platforms/linux/build_llvm.sh +++ b/product-mini/platforms/linux/build_llvm.sh @@ -1,43 +1,7 @@ #!/bin/sh -# Copyright (C) 2019 Intel Corporation. All rights reserved. +# Copyright (C) 2020 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -DEPS_DIR=${PWD}/../../../core/deps - -cd ${DEPS_DIR} -if [ ! -d "llvm" ]; then - echo "Clone llvm to core/deps/ .." - git clone https://github.com/llvm-mirror/llvm.git -fi - -cd llvm -mkdir -p build -cd build - -if [ ! -f bin/llvm-lto ]; then - - CORE_NUM=$(nproc --all) - if [ -z "${CORE_NUM}" ]; then - CORE_NUM=1 - fi - - echo "Build llvm with" ${CORE_NUM} "cores" - - cmake .. \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ - -DCMAKE_BUILD_TYPE:STRING="Release" \ - -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ - -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON \ - -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ - -DLLVM_INCLUDE_TESTS:BOOL=OFF \ - -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ - -DLLVM_APPEND_VC_REV:BOOL=OFF - make -j ${CORE_NUM} - -else - echo "llvm has already been built" -fi - -cd ${PWD} - +/usr/bin/env python3 -m pip install --user -r ../../../build-scripts/requirements.txt +/usr/bin/env python3 ../../../build-scripts/build_llvm.py "$@" diff --git a/product-mini/platforms/linux/ext_lib_export.c b/product-mini/platforms/linux/ext_lib_export.c deleted file mode 100644 index 8813f0dbf3..0000000000 --- a/product-mini/platforms/linux/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/linux/main.c b/product-mini/platforms/linux/main.c index 5f0ba1525a..8f0e84a97f 100644 --- a/product-mini/platforms/linux/main.c +++ b/product-mini/platforms/linux/main.c @@ -3,304 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -#include -#include "bh_platform.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include "wasm_export.h" - -static int app_argc; -static char **app_argv; - -static int print_help() -{ - bh_printf("Usage: iwasm [-options] wasm_file [args...]\n"); - bh_printf("options:\n"); - bh_printf(" -f|--function name Specify function name to run in module\n" - " rather than main\n"); -#if WASM_ENABLE_LOG != 0 - bh_printf(" -v=X Set log verbose level (0 to 5, default is 2),\n" - " larger level with more log\n"); -#endif - bh_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 - bh_printf(" --env= Pass wasi environment variables with \"key=value\"\n"); - bh_printf(" to the program, for example:\n"); - bh_printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); - bh_printf(" --dir= Grant wasi access to the given host directories\n"); - bh_printf(" to the program, for example:\n"); - bh_printf(" --dir= --dir=\n"); -#endif - - return 1; -} - -static void* -app_instance_main(wasm_module_inst_t module_inst) -{ - const char *exception; - - wasm_application_execute_main(module_inst, app_argc, app_argv); - if ((exception = wasm_runtime_get_exception(module_inst))) - bh_printf("%s\n", exception); - return NULL; -} - -static void* -app_instance_func(wasm_module_inst_t module_inst, const char *func_name) -{ - wasm_application_execute_func(module_inst, func_name, app_argc - 1, - app_argv + 1); - /* The result of wasm function or exception info was output inside - wasm_application_execute_func(), here we don't output them again. */ - return NULL; -} - -/** - * Split a space separated strings into an array of strings - * Returns NULL on failure - * Memory must be freed by caller - * Based on: http://stackoverflow.com/a/11198630/471795 - */ -static char ** -split_string(char *str, int *count) -{ - char **res = NULL; - char *p; - int idx = 0; - - /* split string and append tokens to 'res' */ - do { - p = strtok(str, " "); - str = NULL; - res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1)); - if (res == NULL) { - return NULL; - } - res[idx++] = p; - } while (p); - - if (count) { - *count = idx - 1; - } - return res; -} - -static void* -app_instance_repl(wasm_module_inst_t module_inst) -{ - char *cmd = NULL; - size_t len = 0; - ssize_t n; - - while ((bh_printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { - bh_assert(n > 0); - if (cmd[n - 1] == '\n') { - if (n == 1) - continue; - else - cmd[n - 1] = '\0'; - } - app_argv = split_string(cmd, &app_argc); - if (app_argv == NULL) { - LOG_ERROR("Wasm prepare param failed: split string failed.\n"); - break; - } - if (app_argc != 0) { - wasm_application_execute_func(module_inst, app_argv[0], - app_argc - 1, app_argv + 1); - } - free(app_argv); - } - free(cmd); - return NULL; -} - -#if WASM_ENABLE_LIBC_WASI != 0 -static bool -validate_env_str(char *env) -{ - char *p = env; - int key_len = 0; - - while (*p != '\0' && *p != '=') { - key_len++; - p++; - } - - if (*p != '=' || key_len == 0) - return false; - - return true; -} -#endif - -#define USE_GLOBAL_HEAP_BUF 0 - -#if USE_GLOBAL_HEAP_BUF != 0 -static char global_heap_buf[10 * 1024 * 1024] = { 0 }; -#endif - -int main(int argc, char *argv[]) -{ - char *wasm_file = NULL; - const char *func_name = NULL; - uint8 *wasm_file_buf = NULL; - uint32 wasm_file_size; - wasm_module_t wasm_module = NULL; - wasm_module_inst_t wasm_module_inst = NULL; - char error_buf[128] = { 0 }; -#if WASM_ENABLE_LOG != 0 - int log_verbose_level = 2; -#endif - bool is_repl_mode = false; -#if WASM_ENABLE_LIBC_WASI != 0 - const char *dir_list[8] = { NULL }; - uint32 dir_list_size = 0; - const char *env_list[8] = { NULL }; - uint32 env_list_size = 0; -#endif - - /* Process options. */ - for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { - if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { - argc--, argv++; - if (argc < 2) { - print_help(); - return 0; - } - func_name = argv[0]; - } -#if WASM_ENABLE_LOG != 0 - else if (!strncmp(argv[0], "-v=", 3)) { - log_verbose_level = atoi(argv[0] + 3); - if (log_verbose_level < 0 || log_verbose_level > 5) - return print_help(); - } -#endif - else if (!strcmp(argv[0], "--repl")) - is_repl_mode = true; -#if WASM_ENABLE_LIBC_WASI != 0 - else if (!strncmp(argv[0], "--dir=", 6)) { - if (argv[0][6] == '\0') - return print_help(); - if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) { - bh_printf("Only allow max dir number %d\n", - (int)(sizeof(dir_list) / sizeof(char*))); - return -1; - } - dir_list[dir_list_size++] = argv[0] + 6; - } - else if (!strncmp(argv[0], "--env=", 6)) { - char *tmp_env; - - if (argv[0][6] == '\0') - return print_help(); - if (env_list_size >= sizeof(env_list) / sizeof(char*)) { - bh_printf("Only allow max env number %d\n", - (int)(sizeof(env_list) / sizeof(char*))); - return -1; - } - tmp_env = argv[0] + 6; - if (validate_env_str(tmp_env)) - env_list[env_list_size++] = tmp_env; - else { - bh_printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n", - tmp_env); - return print_help(); - } - } -#endif - else - return print_help(); - } - - if (argc == 0) - return print_help(); - - wasm_file = argv[0]; - app_argc = argc; - app_argv = argv; - -#if USE_GLOBAL_HEAP_BUF != 0 - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - bh_printf("Init memory with global heap buffer failed.\n"); - return -1; - } -#else - if (bh_memory_init_with_allocator(malloc, free)) { - bh_printf("Init memory with memory allocator failed.\n"); - return -1; - } -#endif - - /* initialize runtime environment */ - if (!wasm_runtime_init()) - goto fail1; - - bh_log_set_verbose_level(log_verbose_level); - - /* load WASM byte buffer from WASM bin file */ - if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file, - &wasm_file_size))) - goto fail2; - - /* load WASM module */ - if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, - error_buf, sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail3; - } - -#if WASM_ENABLE_LIBC_WASI != 0 - wasm_runtime_set_wasi_args(wasm_module, - dir_list, dir_list_size, - NULL, 0, - env_list, env_list_size, - argv, argc); -#endif - - /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, - 64 * 1024, /* stack size */ - 64 * 1024, /* heap size */ - error_buf, - sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail4; - } - - if (is_repl_mode) - app_instance_repl(wasm_module_inst); - else if (func_name) - app_instance_func(wasm_module_inst, func_name); - else - app_instance_main(wasm_module_inst); - - /* destroy the module instance */ - wasm_runtime_deinstantiate(wasm_module_inst); - -fail4: - /* unload the module */ - wasm_runtime_unload(wasm_module); - -fail3: - /* free the file buffer */ - bh_free(wasm_file_buf); - -fail2: - /* destroy runtime environment */ - wasm_runtime_destroy(); - -fail1: - bh_memory_destroy(); - return 0; -} - +#include "../posix/main.c" diff --git a/product-mini/platforms/nuttx/CMakeLists.txt b/product-mini/platforms/nuttx/CMakeLists.txt new file mode 100644 index 0000000000..7d6f17f945 --- /dev/null +++ b/product-mini/platforms/nuttx/CMakeLists.txt @@ -0,0 +1,219 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# select platform configuration setting WAMR_BUILD_TARGET +set(WAMR_BUILD_PLATFORM nuttx) + +if(CONFIG_ARCH_ARMV6M) + set(WAMR_BUILD_TARGET THUMBV6M) +elseif(CONFIG_ARCH_ARMV7A) + if(CONFIG_ARM_THUMB) + if(CONFIG_ARCH_FPU) + set(WAMR_BUILD_TARGET THUMBV7_VFP) + else() + set(WAMR_BUILD_TARGET THUMBV7) + endif() + else() + if(CONFIG_ARCH_FPU) + set(WAMR_BUILD_TARGET ARMV7_VFP) + else() + set(WAMR_BUILD_TARGET ARMV7) + endif() + endif() +elseif(CONFIG_ARCH_ARMV7M) + set(WAMR_BUILD_TARGET THUMBV7EM) +elseif(CONFIG_ARCH_ARMV8M) + set(WAMR_BUILD_TARGET THUMBV8M) +elseif(CONFIG_ARCH_X86) + set(WAMR_BUILD_TARGET X86_32) +elseif(CONFIG_ARCH_X86_64) + set(WAMR_BUILD_TARGET X86_64) +elseif(CONFIG_ARCH_XTENSA) + set(WAMR_BUILD_TARGET XTENSA) +elseif(CONFIG_ARCH_RV64) + set(WAMR_BUILD_TARGET RISCV64) +elseif(CONFIG_ARCH_RV32) + set(WAMR_BUILD_TARGET RISCV32) +elseif(CONFIG_ARCH_SIM) + if(CONFIG_SIM_M32 OR CONFIG_HOST_X86) + set(WAMR_BUILD_TARGET X86_32) + elseif(CONFIG_HOST_ARM) + set(WAMR_BUILD_TARGET ARM) + elseif(CONFIG_HOST_ARM64) + set(WAMR_BUILD_TARGET AARCH64) + else() + set(WAMR_BUILD_TARGET X86_64) + endif() + if(CONFIG_HOST_MACOS) + add_definitions(-DBH_PLATFORM_DARWIN) + endif() +endif() + +if(CONFIG_INTERPRETERS_WAMR_LOG) + add_definitions(-DWASM_ENABLE_LOG=1) +else() + add_definitions(-DWASM_ENABLE_LOG=0) +endif() + +if(CONFIG_INTERPRETERS_WAMR_AOT_WORD_ALIGN_READ) + add_definitions(-DWASM_ENABLE_WORD_ALIGN_READ=1) +else() + add_definitions(-DWASM_ENABLE_WORD_ALIGN_READ=0) +endif() + +if(CONFIG_INTERPRETERS_WAMR_DYNAMIC_AOT_DEBUG) + add_definitions(-DWASM_ENABLE_DYNAMIC_AOT_DEBUG=1) +else() + add_definitions(-DWASM_ENABLE_DYNAMIC_AOT_DEBUG=0) +endif() + +if(CONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE) + add_definitions(-DWASM_STACK_GUARD_SIZE=0) +else() + add_definitions( + -DWASM_STACK_GUARD_SIZE=${CONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE}) +endif() + +if(CONFIG_INTERPRETERS_WAMR_MEMORY_TRACING) + set(WAMR_BUILD_MEMORY_TRACING 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_SHARED_MEMORY) + set(WAMR_BUILD_SHARED_MEMORY 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_BULK_MEMORY) + set(WAMR_BUILD_BULK_MEMORY 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_AOT_STACK_FRAME) + set(WAMR_BUILD_AOT_STACK_FRAME 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_PERF_PROFILING) + set(WAMR_BUILD_PERF_PROFILING 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_GC) + set(WAMR_BUILD_GC 1) + set(WAMR_BUILD_STRINGREF 1) + set(WAMR_BUILD_REF_TYPES 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_GC_MANUALLY) + add_definitions(-DWASM_GC_MANUALLY=1) +else() + add_definitions(-DWASM_GC_MANUALLY=0) +endif() + +if(CONFIG_INTERPRETERS_WAMR_GC_PERF_PROFILING) + set(WAMR_BUILD_GC_PERF_PROFILING 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_ENABLE_EXCE_HANDLING) + set(WAMR_BUILD_EXCE_HANDLING 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_TAIL_CALL) + set(WAMR_BUILD_TAIL_CALL 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_MEMORY_PROFILING) + set(WAMR_BUILD_MEMORY_PROFILING 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_MULTI_MODULE) + set(WAMR_BUILD_MULTI_MODULE 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_LIB_PTHREAD_SEMAPHORE) + set(WAMR_BUILD_LIB_PTHREAD_SEMAPHORE 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_DISABLE_HW_BOUND_CHECK) + set(WAMR_DISABLE_HW_BOUND_CHECK 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_CUSTOM_NAME_SECTIONS) + set(WAMR_BUILD_CUSTOM_NAME_SECTION 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL) + set(WAMR_BUILD_GLOBAL_HEAP_POOL 1) + math(EXPR _HEAP_SIZE_ + "${CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL_SIZE} * 1024") + set(WAMR_BUILD_GLOBAL_HEAP_SIZE ${_HEAP_SIZE_}) +endif() + +if (CONFIG_INTERPRETERS_WAMR_MEM_ALLOC_WITH_USAGE) + set(WAMR_BUILD_MEM_ALLOC_WITH_USAGE 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST) + set(WAMR_BUILD_SPEC_TEST 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_REF_TYPES) + set(WAMR_BUILD_REF_TYPES 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_CLASSIC) + # include iwasm_interp.cmake + set(WAMR_BUILD_INTERP 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_FAST) + # enable iwasm_interp.cmake + set(WAMR_BUILD_FAST_INTERP 1) +endif() + +if((CONFIG_INTERPRETERS_WAMR_FAST OR CONFIG_INTERPRETERS_WAMR_CLASSIC) + AND CONFIG_INTERPRETERS_WAMR_MINILOADER) + # enable iwasm_interp.cmake + set(WAMR_BUILD_MINI_LOADER 1) +else() + set(WAMR_BUILD_MINI_LOADER 0) +endif() + +if(CONFIG_INTERPRETERS_WAMR_AOT) + # include iwasm_aot.cmake + set(WAMR_BUILD_AOT 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_DEBUG_INTERP) + # include debug_engine.cmake + set(WAMR_BUILD_DEBUG_INTERP 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN) + # include libc_builtin.cmake + set(WAMR_BUILD_LIBC_BUILTIN 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_LIBC_WASI) + # include libc_wasi.cmake + set(WAMR_BUILD_LIBC_WASI 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_THREAD_MGR) + # include thread_mgr.cmake + set(WAMR_BUILD_THREAD_MGR 1) +endif() + +if(CONFIG_INTERPRETERS_WAMR_LIB_PTHREAD) + # include lib_pthread.cmake + set(WAMR_BUILD_LIB_PTHREAD 1) +endif() + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + +# enable WAMR build system +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +# NuttX wamr lib compile required: `WAMR_SOURCES` `WAMR_CFLAGS` `WAMR_INCDIRS` +# `WAMR_DEFINITIONS` +set(WAMR_SOURCES ${WAMR_RUNTIME_LIB_SOURCE}) +set(WAMR_CFLAGS -Wno-shadow -Wno-unused-variable + -Wno-int-conversion -Wno-implicit-function-declaration) +get_directory_property(WAMR_INCDIRS INCLUDE_DIRECTORIES) +get_directory_property(WAMR_DEFINITIONS COMPILE_DEFINITIONS) diff --git a/product-mini/platforms/nuttx/main.c b/product-mini/platforms/nuttx/main.c new file mode 100644 index 0000000000..8f0e84a97f --- /dev/null +++ b/product-mini/platforms/nuttx/main.c @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../posix/main.c" diff --git a/product-mini/platforms/nuttx/wamr.mk b/product-mini/platforms/nuttx/wamr.mk new file mode 100644 index 0000000000..44d8694e57 --- /dev/null +++ b/product-mini/platforms/nuttx/wamr.mk @@ -0,0 +1,476 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CORE_ROOT := wamr/core +IWASM_ROOT := wamr/core/iwasm +SHARED_ROOT := wamr/core/shared + +ifeq ($(CONFIG_ARCH_ARMV6M),y) +WAMR_BUILD_TARGET := THUMBV6M +else ifeq ($(CONFIG_ARCH_ARMV7A),y) +WAMR_BUILD_TARGET := THUMBV7 +else ifeq ($(CONFIG_ARCH_ARMV7M),y) +WAMR_BUILD_TARGET := THUMBV7EM +else ifeq ($(CONFIG_ARCH_ARMV8M),y) +WAMR_BUILD_TARGET := THUMBV8M +else ifeq ($(CONFIG_ARCH_ARM64),y) +WAMR_BUILD_TARGET := AARCH64 +else ifeq ($(CONFIG_ARCH_X86),y) +WAMR_BUILD_TARGET := X86_32 +else ifeq ($(CONFIG_ARCH_X86_64),y) +WAMR_BUILD_TARGET := X86_64 +else ifeq ($(CONFIG_ARCH_XTENSA),y) +WAMR_BUILD_TARGET := XTENSA +else ifeq ($(CONFIG_ARCH_RV64),y) +WAMR_BUILD_TARGET := RISCV64 +else ifeq ($(CONFIG_ARCH_RV32),y) +WAMR_BUILD_TARGET := RISCV32 +else ifeq ($(CONFIG_ARCH_SIM),y) +ifeq ($(CONFIG_SIM_M32),y) +WAMR_BUILD_TARGET := X86_32 +else ifeq ($(CONFIG_HOST_X86),y) +WAMR_BUILD_TARGET := X86_32 +else ifeq ($(CONFIG_HOST_ARM),y) +WAMR_BUILD_TARGET := ARM +else ifeq ($(CONFIG_HOST_ARM64),y) +WAMR_BUILD_TARGET := AARCH64 +else +WAMR_BUILD_TARGET := X86_64 +endif +ifeq ($(CONFIG_HOST_MACOS),y) +# Note: invokeNative_em64.s needs BH_PLATFORM_DARWIN +AFLAGS += -DBH_PLATFORM_DARWIN +endif +endif + +WAMR_BUILD_PLATFORM := nuttx + +CFLAGS += -DBH_MALLOC=wasm_runtime_malloc +CFLAGS += -DBH_FREE=wasm_runtime_free + +ifeq ($(WAMR_BUILD_TARGET), X86_32) + CFLAGS += -DBUILD_TARGET_X86_32 + INVOKE_NATIVE := invokeNative_ia32.s + AOT_RELOC := aot_reloc_x86_32.c +else ifeq ($(WAMR_BUILD_TARGET), X86_64) + CFLAGS += -DBUILD_TARGET_X86_64 + INVOKE_NATIVE := invokeNative_em64.s + AOT_RELOC := aot_reloc_x86_64.c +else ifeq ($(WAMR_BUILD_TARGET), AARCH64) + CFLAGS += -DBUILD_TARGET_AARCH64 + CFLAGS += -DBUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" + INVOKE_NATIVE := invokeNative_aarch64.s + AOT_RELOC := aot_reloc_aarch64.c +else ifeq ($(findstring ARM,$(WAMR_BUILD_TARGET)), ARM) + CFLAGS += -DBUILD_TARGET_ARM + CFLAGS += -DBUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" + INVOKE_NATIVE := invokeNative_arm.s + AOT_RELOC := aot_reloc_arm.c +else ifeq ($(findstring THUMB,$(WAMR_BUILD_TARGET)), THUMB) + CFLAGS += -DBUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" + ifeq ($(CONFIG_ARCH_FPU),y) + CFLAGS += -DBUILD_TARGET_THUMB_VFP + INVOKE_NATIVE := invokeNative_thumb_vfp.s + else + CFLAGS += -DBUILD_TARGET_THUMB + INVOKE_NATIVE := invokeNative_thumb.s + endif + AOT_RELOC := aot_reloc_thumb.c +else ifeq (${WAMR_BUILD_TARGET}, MIPS) + CFLAGS += -DBUILD_TARGET_MIPS + INVOKE_NATIVE := invokeNative_mips.s + AOT_RELOC := aot_reloc_mips.c +else ifeq (${WAMR_BUILD_TARGET}, XTENSA) + CFLAGS += -DBUILD_TARGET_XTENSA + INVOKE_NATIVE := invokeNative_xtensa.s + AOT_RELOC := aot_reloc_xtensa.c +else ifeq (${WAMR_BUILD_TARGET}, RISCV64) + +ifeq (${CONFIG_ARCH_DPFPU},y) + CFLAGS += -DBUILD_TARGET_RISCV64_LP64D +else ifneq (${CONFIG_ARCH_FPU},y) + CFLAGS += -DBUILD_TARGET_RISCV64_LP64 +else + $(error riscv64 lp64f is unsupported) +endif + INVOKE_NATIVE += invokeNative_riscv.S + + AOT_RELOC := aot_reloc_riscv.c + +else ifeq (${WAMR_BUILD_TARGET}, RISCV32) + +ifeq (${CONFIG_ARCH_DPFPU},y) + CFLAGS += -DBUILD_TARGET_RISCV32_ILP32D +else ifeq (${CONFIG_ARCH_FPU},y) + CFLAGS += -DBUILD_TARGET_RISCV32_ILP32F +else + CFLAGS += -DBUILD_TARGET_RISCV32_ILP32 +endif + + INVOKE_NATIVE += invokeNative_riscv.S + AOT_RELOC := aot_reloc_riscv.c + +else + $(error Build target is unsupported) +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LOG),y) +CFLAGS += -DWASM_ENABLE_LOG=1 +else +CFLAGS += -DWASM_ENABLE_LOG=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_AOT),y) +CFLAGS += -I$(IWASM_ROOT)/aot +CFLAGS += -DWASM_ENABLE_AOT=1 +CSRCS += aot_loader.c \ + $(AOT_RELOC) \ + aot_intrinsic.c \ + aot_runtime.c +ifeq ($(CONFIG_INTERPRETERS_WAMR_DEBUG_AOT),y) +CFLAGS += -DWASM_ENABLE_DEBUG_AOT=1 +CSRCS += elf_parser.c \ + jit_debug.c +endif +else +CFLAGS += -DWASM_ENABLE_AOT=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_AOT_QUICK_ENTRY),y) +CFLAGS += -DWASM_ENABLE_QUICK_AOT_ENTRY=1 +else +CFLAGS += -DWASM_ENABLE_QUICK_AOT_ENTRY=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_AOT_WORD_ALIGN_READ),y) +CFLAGS += -DWASM_ENABLE_WORD_ALIGN_READ=1 +else +CFLAGS += -DWASM_ENABLE_WORD_ALIGN_READ=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_DYNAMIC_AOT_DEBUG),y) +CFLAGS += -DWASM_ENABLE_DYNAMIC_AOT_DEBUG=1 +else +CFLAGS += -DWASM_ENABLE_DYNAMIC_AOT_DEBUG=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_MEM_DUAL_BUS_MIRROR),y) +CFLAGS += -DWASM_MEM_DUAL_BUS_MIRROR=1 +else +CFLAGS += -DWASM_MEM_DUAL_BUS_MIRROR=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_FAST), y) +CFLAGS += -DWASM_ENABLE_FAST_INTERP=1 +CFLAGS += -DWASM_ENABLE_INTERP=1 +CSRCS += wasm_interp_fast.c +CSRCS += wasm_runtime.c +else +CFLAGS += -DWASM_ENABLE_FAST_INTERP=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_CLASSIC), y) +CFLAGS += -DWASM_ENABLE_INTERP=1 +CSRCS += wasm_interp_classic.c +CSRCS += wasm_runtime.c +endif + +ifeq ($(findstring y,$(CONFIG_INTERPRETERS_WAMR_FAST)$(CONFIG_INTERPRETERS_WAMR_CLASSIC)), y) +ifeq ($(CONFIG_INTERPRETERS_WAMR_MINILOADER),y) +CFLAGS += -DWASM_ENABLE_MINI_LOADER=1 +CSRCS += wasm_mini_loader.c +else +CFLAGS += -DWASM_ENABLE_MINI_LOADER=0 +CSRCS += wasm_loader.c +endif +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_DEBUG_INTERP),y) +# Note: INTERPRETERS_WAMR_CLASSIC/INTERPRETERS_WAMR_THREAD_MGR +# dependencies are already handled in NuttX apps Kconfig +CFLAGS += -DWASM_ENABLE_DEBUG_INTERP=1 +CFLAGS += -I$(IWASM_ROOT)/libraries/debug-engine +CSRCS += debug_engine.c +CSRCS += gdbserver.c +CSRCS += handler.c +CSRCS += packets.c +CSRCS += utils.c +VPATH += $(IWASM_ROOT)/libraries/debug-engine +endif + +ifneq ($(CONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE),) +CFLAGS += -DWASM_STACK_GUARD_SIZE=CONFIG_INTERPRETERS_WAMR_STACK_GUARD_SIZE +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_SHARED_MEMORY),y) +CFLAGS += -DWASM_ENABLE_SHARED_MEMORY=1 +CSRCS += wasm_shared_memory.c +else +CFLAGS += -DWASM_ENABLE_SHARED_MEMORY=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_BULK_MEMORY),y) +CFLAGS += -DWASM_ENABLE_BULK_MEMORY=1 +else +CFLAGS += -DWASM_ENABLE_BULK_MEMORY=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_AOT_STACK_FRAME), y) +CFLAGS += -DWASM_ENABLE_AOT_STACK_FRAME=1 +else +CFLAGS += -DWASM_ENABLE_AOT_STACK_FRAME=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_PERF_PROFILING),y) +CFLAGS += -DWASM_ENABLE_PERF_PROFILING=1 +CFLAGS += -DWASM_ENABLE_AOT_STACK_FRAME=1 +else +CFLAGS += -DWASM_ENABLE_PERF_PROFILING=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_GC_PERF_PROFILING),y) +CFLAGS += -DWASM_ENABLE_GC_PERF_PROFILING=1 +else +CFLAGS += -DWASM_ENABLE_GC_PERF_PROFILING=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_MEMORY_PROFILING),y) +CFLAGS += -DWASM_ENABLE_MEMORY_PROFILING=1 +else +CFLAGS += -DWASM_ENABLE_MEMORY_PROFILING=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_MEMORY_TRACING),y) +CFLAGS += -DWASM_ENABLE_MEMORY_TRACING=1 +else +CFLAGS += -DWASM_ENABLE_MEMORY_TRACING=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_DUMP_CALL_STACK),y) +CFLAGS += -DWASM_ENABLE_DUMP_CALL_STACK=1 +CFLAGS += -DWASM_ENABLE_AOT_STACK_FRAME=1 +else +CFLAGS += -DWASM_ENABLE_DUMP_CALL_STACK=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LIBC_BUILTIN),y) +CFLAGS += -DWASM_ENABLE_LIBC_BUILTIN=1 +CSRCS += libc_builtin_wrapper.c +VPATH += $(IWASM_ROOT)/libraries/libc-builtin +else +CFLAGS += -DWASM_ENABLE_LIBC_BUILTIN=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_CONFIGURABLE_BOUNDS_CHECKS),y) +CFLAGS += -DWASM_CONFIGURABLE_BOUNDS_CHECKS=1 +else +CFLAGS += -DWASM_CONFIGURABLE_BOUNDS_CHECKS=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LIBC_WASI),y) +CFLAGS += -DWASM_ENABLE_LIBC_WASI=1 +CFLAGS += -I$(IWASM_ROOT)/libraries/libc-wasi/sandboxed-system-primitives/src +CFLAGS += -I$(IWASM_ROOT)/libraries/libc-wasi/sandboxed-system-primitives/include +CFLAGS += -I${SHARED_ROOT}/platform/common/libc-util +CSRCS += blocking_op.c +CSRCS += posix_socket.c +CSRCS += posix_file.c +CSRCS += posix_clock.c +CSRCS += libc_errno.c +CSRCS += libc_wasi_wrapper.c +VPATH += $(IWASM_ROOT)/libraries/libc-wasi +CSRCS += posix.c +CSRCS += random.c +CSRCS += str.c +VPATH += $(IWASM_ROOT)/libraries/libc-wasi/sandboxed-system-primitives/src +# todo: use Kconfig select instead +CONFIG_INTERPRETERS_WAMR_MODULE_INSTANCE_CONTEXT = y +else +CFLAGS += -DWASM_ENABLE_LIBC_WASI=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_MODULE_INSTANCE_CONTEXT),y) +CFLAGS += -DWASM_ENABLE_MODULE_INST_CONTEXT=1 +else +CFLAGS += -DWASM_ENABLE_MODULE_INST_CONTEXT=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_MULTI_MODULE),y) +CFLAGS += -DWASM_ENABLE_MULTI_MODULE=1 +else +CFLAGS += -DWASM_ENABLE_MULTI_MODULE=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_THREAD_MGR),y) +CFLAGS += -DWASM_ENABLE_THREAD_MGR=1 +CSRCS += thread_manager.c +VPATH += $(IWASM_ROOT)/libraries/thread-mgr +else +CFLAGS += -DWASM_ENABLE_THREAD_MGR=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LIB_WASI_THREADS),y) +CFLAGS += -DWASM_ENABLE_LIB_WASI_THREADS=1 +CSRCS += lib_wasi_threads_wrapper.c +CSRCS += tid_allocator.c +VPATH += $(IWASM_ROOT)/libraries/lib-wasi-threads +else +CFLAGS += -DWASM_ENABLE_LIB_WASI_THREADS=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_GC),y) +CFLAGS += -DWASM_ENABLE_GC=1 +CSRCS += gc_common.c gc_type.c gc_object.c +VPATH += $(IWASM_ROOT)/common/gc +else +CFLAGS += -DWASM_ENABLE_GC=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_GC_MANUALLY),y) +CFLAGS += -DWASM_GC_MANUALLY=1 +else +CFLAGS += -DWASM_GC_MANUALLY=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LIB_PTHREAD),y) +CFLAGS += -DWASM_ENABLE_LIB_PTHREAD=1 +CSRCS += lib_pthread_wrapper.c +else +CFLAGS += -DWASM_ENABLE_LIB_PTHREAD=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LIB_PTHREAD_SEMAPHORE),y) +CFLAGS += -DWASM_ENABLE_LIB_PTHREAD_SEMAPHORE=1 +else +CFLAGS += -DWASM_ENABLE_LIB_PTHREAD_SEMAPHORE=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_DISABLE_HW_BOUND_CHECK),y) +CFLAGS += -DWASM_DISABLE_HW_BOUND_CHECK=1 +CFLAGS += -DWASM_DISABLE_STACK_HW_BOUND_CHECK=1 +else +CFLAGS += -DWASM_DISABLE_HW_BOUND_CHECK=0 +CFLAGS += -DWASM_DISABLE_STACK_HW_BOUND_CHECK=0 +endif + +# REVISIT: is this worth to have a Kconfig? +CFLAGS += -DWASM_DISABLE_WAKEUP_BLOCKING_OP=0 + +ifeq ($(CONFIG_INTERPRETERS_WAMR_LOAD_CUSTOM_SECTIONS),y) +CFLAGS += -DWASM_ENABLE_LOAD_CUSTOM_SECTION=1 +else +CFLAGS += -DWASM_ENABLE_LOAD_CUSTOM_SECTION=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_CUSTOM_NAME_SECTIONS),y) +CFLAGS += -DWASM_ENABLE_CUSTOM_NAME_SECTION=1 +else +CFLAGS += -DWASM_ENABLE_CUSTOM_NAME_SECTION=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL),y) +CFLAGS += -DWASM_ENABLE_GLOBAL_HEAP_POOL=1 +CFLAGS += -DWASM_GLOBAL_HEAP_SIZE="$(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL_SIZE) * 1024" +else +CFLAGS += -DWASM_ENABLE_GLOBAL_HEAP_POOL=0 +ifeq ($(CONFIG_INTERPRETERS_WAMR_MEM_ALLOC_WITH_USAGE),y) +CFLAGS += -DWASM_MEM_ALLOC_WITH_USAGE=1 +else +CFLAGS += -DWASM_MEM_ALLOC_WITH_USAGE=0 +endif +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST),y) +CFLAGS += -DWASM_ENABLE_SPEC_TEST=1 +else +CFLAGS += -DWASM_ENABLE_SPEC_TEST=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_REF_TYPES),y) +CFLAGS += -DWASM_ENABLE_REF_TYPES=1 +else +CFLAGS += -DWASM_ENABLE_REF_TYPES=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_TAIL_CALL),y) +CFLAGS += -DWASM_ENABLE_TAIL_CALL=1 +else +CFLAGS += -DWASM_ENABLE_TAIL_CALL=0 +endif + +ifeq ($(CONFIG_INTERPRETERS_WAMR_ENABLE_EXCE_HANDLING),y) +CFLAGS += -DWASM_ENABLE_EXCE_HANDLING=1 +CFLAGS += -DWASM_ENABLE_TAGS=1 +else +CFLAGS += -DWASM_ENABLE_EXCE_HANDLING=0 +CFLAGS += -DWASM_ENABLE_TAGS=0 +endif + +CFLAGS += -Wno-shadow -Wno-unused-variable +CFLAGS += -Wno-int-conversion -Wno-implicit-function-declaration + +CFLAGS += -I${CORE_ROOT} \ + -I${IWASM_ROOT}/include \ + -I${IWASM_ROOT}/interpreter \ + -I${IWASM_ROOT}/common \ + -I${IWASM_ROOT}/libraries/thread-mgr \ + -I${SHARED_ROOT}/include \ + -I${SHARED_ROOT}/platform/include \ + -I${SHARED_ROOT}/utils \ + -I${SHARED_ROOT}/utils/uncommon \ + -I${SHARED_ROOT}/mem-alloc \ + -I${SHARED_ROOT}/platform/nuttx + +ifeq ($(WAMR_BUILD_INTERP), 1) +CFLAGS += -I$(IWASM_ROOT)/interpreter +endif + +CSRCS += nuttx_platform.c \ + posix_blocking_op.c \ + posix_thread.c \ + posix_time.c \ + posix_sleep.c \ + mremap.c \ + mem_alloc.c \ + ems_kfc.c \ + ems_alloc.c \ + ems_hmu.c \ + ems_gc.c \ + bh_assert.c \ + bh_bitmap.c \ + bh_common.c \ + bh_hashmap.c \ + bh_list.c \ + bh_leb128.c \ + bh_log.c \ + bh_queue.c \ + bh_vector.c \ + bh_read_file.c \ + runtime_timer.c \ + wasm_application.c \ + wasm_blocking_op.c \ + wasm_runtime_common.c \ + wasm_native.c \ + wasm_exec_env.c \ + wasm_loader_common.c \ + wasm_memory.c \ + wasm_c_api.c + +ASRCS += $(INVOKE_NATIVE) + +VPATH += $(SHARED_ROOT)/platform/nuttx +VPATH += $(SHARED_ROOT)/platform/common/memory +VPATH += $(SHARED_ROOT)/platform/common/posix +VPATH += $(SHARED_ROOT)/platform/common/libc-util +VPATH += $(SHARED_ROOT)/mem-alloc +VPATH += $(SHARED_ROOT)/mem-alloc/ems +VPATH += $(SHARED_ROOT)/utils +VPATH += $(SHARED_ROOT)/utils/uncommon +VPATH += $(IWASM_ROOT)/common +VPATH += $(IWASM_ROOT)/interpreter +VPATH += $(IWASM_ROOT)/libraries +VPATH += $(IWASM_ROOT)/libraries/lib-pthread +VPATH += $(IWASM_ROOT)/common/arch +VPATH += $(IWASM_ROOT)/aot +VPATH += $(IWASM_ROOT)/aot/arch +VPATH += $(IWASM_ROOT)/aot/debug diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c new file mode 100644 index 0000000000..2d7d3afeb8 --- /dev/null +++ b/product-mini/platforms/posix/main.c @@ -0,0 +1,1121 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include + +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +#if WASM_ENABLE_LIBC_WASI != 0 +#include "../common/libc_wasi.c" +#endif + +#include "../common/wasm_proposal.c" + +#if BH_HAS_DLFCN +#include +#endif + +static int app_argc; +static char **app_argv; + +/* clang-format off */ +static int +print_help(void) +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); +#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"); +#if WASM_ENABLE_LIBC_WASI !=0 + printf(" --heap-size=n Set maximum heap size in bytes, default is 0 KB when libc wasi is enabled\n"); +#else + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB when libc wasi is diabled\n"); +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + printf(" --shared-heap-size=n Create shared heap of n bytes and attach to the wasm app.\n"); + printf(" The size n will be adjusted to a minumum number aligned to page size\n"); +#endif +#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_GC != 0 + printf(" --gc-heap-size=n Set maximum gc heap size in bytes,\n"); + printf(" default is %u KB\n", GC_HEAP_SIZE_DEFAULT / 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"); +#if defined(os_writegsbase) + printf(" --enable-segue[=] Enable using segment register GS as the base address of\n"); + printf(" linear memory, which may improve performance, flags can be:\n"); + printf(" i32.load, i64.load, f32.load, f64.load, v128.load,\n"); + printf(" i32.store, i64.store, f32.store, f64.store, v128.store\n"); + printf(" Use comma to separate, e.g. --enable-segue=i32.load,i64.store\n"); + printf(" and --enable-segue means all flags are added.\n"); +#endif +#endif /* WASM_ENABLE_JIT != 0 */ +#if WASM_ENABLE_LINUX_PERF != 0 + printf(" --enable-linux-perf Enable linux perf support. It works in aot and llvm-jit.\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_CONFIGURABLE_BOUNDS_CHECKS != 0 + printf(" --disable-bounds-checks Disable bounds checks for memory accesses\n"); +#endif +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_print_help(); +#endif +#if BH_HAS_DLFCN + printf(" --native-lib= Register native libraries to the WASM module, which\n"); + printf(" are shared object (.so) files, for example:\n"); + printf(" --native-lib=test1.so --native-lib=test2.so\n"); +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + printf(" --module-path= Indicate a module search path. default is current\n" + " directory('./')\n"); +#endif +#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_THREAD_MGR != 0 + printf(" --timeout=ms Set the maximum execution time in ms.\n"); + printf(" If it expires, the runtime aborts the execution\n"); + printf(" with a trap.\n"); +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 + printf(" -g=ip:port Set the debug sever address, default is debug disabled\n"); + printf(" if port is 0, then a random port will be used\n"); +#endif +#if WASM_ENABLE_STATIC_PGO != 0 + printf(" --gen-prof-file= Generate LLVM PGO (Profile-Guided Optimization) profile file\n"); +#endif + printf(" --version Show version information\n"); + return 1; +} +/* clang-format on */ + +static const void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + exception = wasm_runtime_get_exception(module_inst); + return exception; +} + +static const void * +app_instance_func(wasm_module_inst_t module_inst, const char *func_name) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + /* The result of wasm function or exception info was output inside + wasm_application_execute_func(), here we don't output them again. */ + return wasm_runtime_get_exception(module_inst); +} + +/** + * Split a string into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count, const char *delimer) +{ + char **res = NULL, **res1; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, delimer); + str = NULL; + res1 = res; + res = (char **)realloc(res1, sizeof(char *) * (uint32)(idx + 1)); + if (res == NULL) { + free(res1); + return NULL; + } + res[idx++] = p; + } while (p); + + /** + * Due to the function name, + * res[0] might contain a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + + if (count) { + *count = idx - 1; + } + return res; +} + +static void * +app_instance_repl(wasm_module_inst_t module_inst) +{ + char *cmd = NULL; + size_t len = 0; + ssize_t n; + + while ((printf("webassembly> "), fflush(stdout), + n = getline(&cmd, &len, stdin)) + != -1) { + bh_assert(n > 0); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + if (!strcmp(cmd, "__exit__")) { + printf("exit repl mode\n"); + break; + } + app_argv = split_string(cmd, &app_argc, " "); + if (app_argv == NULL) { + LOG_ERROR("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + const char *exception; + wasm_application_execute_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + } + free(app_argv); + } + free(cmd); + return NULL; +} + +#if WASM_ENABLE_JIT != 0 +static uint32 +resolve_segue_flags(char *str_flags) +{ + uint32 segue_flags = 0; + int32 flag_count, i; + char **flag_list; + + flag_list = split_string(str_flags, &flag_count, ","); + if (flag_list) { + for (i = 0; i < flag_count; i++) { + if (!strcmp(flag_list[i], "i32.load")) { + segue_flags |= 1 << 0; + } + else if (!strcmp(flag_list[i], "i64.load")) { + segue_flags |= 1 << 1; + } + else if (!strcmp(flag_list[i], "f32.load")) { + segue_flags |= 1 << 2; + } + else if (!strcmp(flag_list[i], "f64.load")) { + segue_flags |= 1 << 3; + } + else if (!strcmp(flag_list[i], "v128.load")) { + segue_flags |= 1 << 4; + } + else if (!strcmp(flag_list[i], "i32.store")) { + segue_flags |= 1 << 8; + } + else if (!strcmp(flag_list[i], "i64.store")) { + segue_flags |= 1 << 9; + } + else if (!strcmp(flag_list[i], "f32.store")) { + segue_flags |= 1 << 10; + } + else if (!strcmp(flag_list[i], "f64.store")) { + segue_flags |= 1 << 11; + } + else if (!strcmp(flag_list[i], "v128.store")) { + segue_flags |= 1 << 12; + } + else { + /* invalid flag */ + segue_flags = (uint32)-1; + break; + } + } + free(flag_list); + } + return segue_flags; +} +#endif /* end of WASM_ENABLE_JIT != 0 */ + +#if BH_HAS_DLFCN +struct native_lib { + void *handle; + + uint32 (*get_native_lib)(char **p_module_name, + NativeSymbol **p_native_symbols); + int (*init_native_lib)(void); + void (*deinit_native_lib)(void); + + char *module_name; + NativeSymbol *native_symbols; + uint32 n_native_symbols; +}; + +struct native_lib * +load_native_lib(const char *name) +{ + struct native_lib *lib = wasm_runtime_malloc(sizeof(*lib)); + if (lib == NULL) { + LOG_WARNING("warning: failed to load native library %s because of " + "allocation failure", + name); + goto fail; + } + memset(lib, 0, sizeof(*lib)); + + /* open the native library */ + if (!(lib->handle = dlopen(name, RTLD_NOW | RTLD_GLOBAL)) + && !(lib->handle = dlopen(name, RTLD_LAZY))) { + LOG_WARNING("warning: failed to load native library %s. %s", name, + dlerror()); + goto fail; + } + + lib->init_native_lib = dlsym(lib->handle, "init_native_lib"); + lib->get_native_lib = dlsym(lib->handle, "get_native_lib"); + lib->deinit_native_lib = dlsym(lib->handle, "deinit_native_lib"); + + if (!lib->get_native_lib) { + LOG_WARNING("warning: failed to lookup `get_native_lib` function " + "from native lib %s", + name); + goto fail; + } + + if (lib->init_native_lib) { + int ret = lib->init_native_lib(); + if (ret != 0) { + LOG_WARNING("warning: `init_native_lib` function from native " + "lib %s failed with %d", + name, ret); + goto fail; + } + } + + lib->n_native_symbols = + lib->get_native_lib(&lib->module_name, &lib->native_symbols); + + /* register native symbols */ + if (!(lib->n_native_symbols > 0 && lib->module_name && lib->native_symbols + && wasm_runtime_register_natives( + lib->module_name, lib->native_symbols, lib->n_native_symbols))) { + LOG_WARNING("warning: failed to register native lib %s", name); + if (lib->deinit_native_lib) { + lib->deinit_native_lib(); + } + goto fail; + } + return lib; +fail: + if (lib != NULL) { + if (lib->handle != NULL) { + dlclose(lib->handle); + } + wasm_runtime_free(lib); + } + return NULL; +} + +static uint32 +load_and_register_native_libs(const char **native_lib_list, + uint32 native_lib_count, + struct native_lib **native_lib_loaded_list) +{ + uint32 i, native_lib_loaded_count = 0; + + for (i = 0; i < native_lib_count; i++) { + struct native_lib *lib = load_native_lib(native_lib_list[i]); + if (lib == NULL) { + continue; + } + native_lib_loaded_list[native_lib_loaded_count++] = lib; + } + + return native_lib_loaded_count; +} + +static void +unregister_and_unload_native_libs(uint32 native_lib_count, + struct native_lib **native_lib_loaded_list) +{ + uint32 i; + + for (i = 0; i < native_lib_count; i++) { + struct native_lib *lib = native_lib_loaded_list[i]; + + /* unregister native symbols */ + if (!wasm_runtime_unregister_natives(lib->module_name, + lib->native_symbols)) { + LOG_WARNING("warning: failed to unregister native lib %p", + lib->handle); + continue; + } + + if (lib->deinit_native_lib) { + lib->deinit_native_lib(); + } + + dlclose(lib->handle); + wasm_runtime_free(lib); + } +} +#endif /* BH_HAS_DLFCN */ + +#if WASM_ENABLE_MULTI_MODULE != 0 +static char * +handle_module_path(const char *module_path) +{ + /* next character after '=' */ + return (strchr(module_path, '=')) + 1; +} + +static char *module_search_path = "."; + +static bool +module_reader_callback(package_type_t module_type, const char *module_name, + uint8 **p_buffer, uint32 *p_size) +{ + char *file_format = NULL; +#if WASM_ENABLE_INTERP != 0 + if (module_type == Wasm_Module_Bytecode) + file_format = ".wasm"; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_type == Wasm_Module_AoT) + file_format = ".aot"; +#endif + bh_assert(file_format); + const char *format = "%s/%s%s"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(file_format) + 1; + char *wasm_file_name = wasm_runtime_malloc(sz); + if (!wasm_file_name) { + return false; + } + snprintf(wasm_file_name, sz, format, module_search_path, module_name, + file_format); + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); + + wasm_runtime_free(wasm_file_name); + return *p_buffer != NULL; +} + +static void +module_destroyer_callback(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + wasm_runtime_free(buffer); + buffer = NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 +static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; +#else +static void * +malloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + unsigned int size) +{ + return malloc(size); +} + +static void * +realloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, bool full_size_mmaped, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr, unsigned int size) +{ + return realloc(ptr, size); +} + +static void +free_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr) +{ + free(ptr); +} +#endif /* end of WASM_ENABLE_GLOBAL_HEAP_POOL */ + +#if WASM_ENABLE_STATIC_PGO != 0 +static void +dump_pgo_prof_data(wasm_module_inst_t module_inst, const char *path) +{ + char *buf; + uint32 len; + FILE *file; + + if (!(len = wasm_runtime_get_pgo_prof_data_size(module_inst))) { + printf("failed to get LLVM PGO profile data size\n"); + return; + } + + if (!(buf = wasm_runtime_malloc(len))) { + printf("allocate memory failed\n"); + return; + } + + if (len != wasm_runtime_dump_pgo_prof_data_to_buf(module_inst, buf, len)) { + printf("failed to dump LLVM PGO profile data\n"); + wasm_runtime_free(buf); + return; + } + + if (!(file = fopen(path, "wb"))) { + printf("failed to create file %s", path); + wasm_runtime_free(buf); + return; + } + fwrite(buf, len, 1, file); + fclose(file); + + wasm_runtime_free(buf); + + printf("LLVM raw profile file %s was generated.\n", path); +} +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +struct timeout_arg { + uint32 timeout_ms; + wasm_module_inst_t inst; +#if defined(BH_HAS_STD_ATOMIC) + _Atomic +#endif + bool cancel; +}; + +void * +timeout_thread(void *vp) +{ + const struct timeout_arg *arg = vp; + const uint64 end_time = + os_time_get_boot_us() + (uint64)arg->timeout_ms * 1000; + while (!arg->cancel) { + const uint64 now = os_time_get_boot_us(); + if ((int64)(now - end_time) > 0) { + wasm_runtime_terminate(arg->inst); + break; + } + const uint64 left_us = end_time - now; + uint32 us; + if (left_us >= 100 * 1000) { + us = 100 * 1000; + } + else { + us = left_us; + } + os_usleep(us); + } + return NULL; +} +#endif + +int +main(int argc, char *argv[]) +{ + int32 ret = -1; + char *wasm_file = NULL; + const char *func_name = NULL; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 64 * 1024; +#if WASM_ENABLE_LIBC_WASI != 0 + uint32 heap_size = 0; +#else + uint32 heap_size = 16 * 1024; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + SharedHeapInitArgs shared_heap_init_args; + uint32 shared_heap_size = 0; + void *shared_heap = NULL; +#endif +#if WASM_ENABLE_FAST_JIT != 0 + uint32 jit_code_cache_size = FAST_JIT_DEFAULT_CODE_CACHE_SIZE; +#endif +#if WASM_ENABLE_GC != 0 + uint32 gc_heap_size = GC_HEAP_SIZE_DEFAULT; +#endif +#if WASM_ENABLE_JIT != 0 + uint32 llvm_jit_size_level = 3; + uint32 llvm_jit_opt_level = 3; + uint32 segue_flags = 0; +#endif +#if WASM_ENABLE_LINUX_PERF != 0 + bool enable_linux_perf = false; +#endif + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RunningMode running_mode = 0; + RuntimeInitArgs init_args; + struct InstantiationArgs2 *inst_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + bool is_repl_mode = false; + bool is_xip_file = false; +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + bool disable_bounds_checks = false; +#endif +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_parse_context_t wasi_parse_ctx; +#endif +#if BH_HAS_DLFCN + const char *native_lib_list[8] = { NULL }; + uint32 native_lib_count = 0; + struct native_lib *native_lib_loaded_list[8]; + uint32 native_lib_loaded_count = 0; +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 + char *ip_addr = NULL; + int instance_port = 0; +#endif +#if WASM_ENABLE_STATIC_PGO != 0 + const char *gen_prof_file = NULL; +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + int timeout_ms = -1; +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + memset(&wasi_parse_ctx, 0, sizeof(wasi_parse_ctx)); +#endif + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + return print_help(); + } + 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); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } +#endif + else if (!strcmp(argv[0], "--repl")) { + is_repl_mode = true; + } +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + else if (!strcmp(argv[0], "--disable-bounds-checks")) { + disable_bounds_checks = true; + } +#endif + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } +#if WASM_ENABLE_SHARED_HEAP != 0 + else if (!strncmp(argv[0], "--shared-heap-size=", 19)) { + if (argv[0][19] == '\0') + return print_help(); + shared_heap_size = atoi(argv[0] + 19); + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 + else if (!strncmp(argv[0], "--jit-codecache-size=", 21)) { + if (argv[0][21] == '\0') + return print_help(); + jit_code_cache_size = atoi(argv[0] + 21); + } +#endif +#if WASM_ENABLE_GC != 0 + else if (!strncmp(argv[0], "--gc-heap-size=", 15)) { + if (argv[0][15] == '\0') + return print_help(); + gc_heap_size = atoi(argv[0] + 15); + } +#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; + } + } + else if (!strcmp(argv[0], "--enable-segue")) { + /* all flags are enabled */ + segue_flags = 0x1F1F; + } + else if (!strncmp(argv[0], "--enable-segue=", 15)) { + segue_flags = resolve_segue_flags(argv[0] + 15); + if (segue_flags == (uint32)-1) + return print_help(); + } +#endif /* end of WASM_ENABLE_JIT != 0 */ +#if BH_HAS_DLFCN + else if (!strncmp(argv[0], "--native-lib=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + if (native_lib_count >= sizeof(native_lib_list) / sizeof(char *)) { + printf("Only allow max native lib number %d\n", + (int)(sizeof(native_lib_list) / sizeof(char *))); + return 1; + } + native_lib_list[native_lib_count++] = argv[0] + 13; + } +#endif +#if WASM_ENABLE_LINUX_PERF != 0 + else if (!strncmp(argv[0], "--enable-linux-perf", 19)) { + enable_linux_perf = true; + } +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + else if (!strncmp(argv[0], + "--module-path=", strlen("--module-path="))) { + module_search_path = handle_module_path(argv[0]); + if (!strlen(module_search_path)) { + return print_help(); + } + } +#endif +#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(); + wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); + } +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + else if (!strncmp(argv[0], "--timeout=", 10)) { + if (argv[0][10] == '\0') + return print_help(); + timeout_ms = atoi(argv[0] + 10); + } +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 + else if (!strncmp(argv[0], "-g=", 3)) { + char *port_str = strchr(argv[0] + 3, ':'); + char *port_end; + if (port_str == NULL) + return print_help(); + *port_str = '\0'; + instance_port = strtoul(port_str + 1, &port_end, 10); + if (port_str[1] == '\0' || *port_end != '\0') + return print_help(); + ip_addr = argv[0] + 3; + } +#endif +#if WASM_ENABLE_STATIC_PGO != 0 + else if (!strncmp(argv[0], "--gen-prof-file=", 16)) { + if (argv[0][16] == '\0') + return print_help(); + gen_prof_file = argv[0] + 16; + } +#endif + else if (!strcmp(argv[0], "--version")) { + uint32 major, minor, patch; + wasm_runtime_get_version(&major, &minor, &patch); + printf("iwasm %" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", major, minor, + patch); + printf("\n"); + wasm_proposal_print_status(); + return 0; + } + else { +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_parse_result_t result = + libc_wasi_parse(argv[0], &wasi_parse_ctx); + switch (result) { + case LIBC_WASI_PARSE_RESULT_OK: + continue; + case LIBC_WASI_PARSE_RESULT_NEED_HELP: + return print_help(); + case LIBC_WASI_PARSE_RESULT_BAD_PARAM: + return 1; + } +#else + return print_help(); +#endif + } + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + app_argc = argc; + app_argv = 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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + /* Set user data for the allocator is needed */ + /* init_args.mem_alloc_option.allocator.user_data = user_data; */ +#endif + init_args.mem_alloc_option.allocator.malloc_func = malloc_func; + init_args.mem_alloc_option.allocator.realloc_func = realloc_func; + init_args.mem_alloc_option.allocator.free_func = free_func; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 + init_args.fast_jit_code_cache_size = jit_code_cache_size; +#endif + +#if WASM_ENABLE_GC != 0 + init_args.gc_heap_size = gc_heap_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; + init_args.segue_flags = segue_flags; +#endif +#if WASM_ENABLE_LINUX_PERF != 0 + init_args.enable_linux_perf = enable_linux_perf; +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + init_args.instance_port = instance_port; + if (ip_addr) + /* ensure that init_args.ip_addr is null terminated */ + strncpy(init_args.ip_addr, ip_addr, sizeof(init_args.ip_addr) - 1); +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + +#if BH_HAS_DLFCN + native_lib_loaded_count = load_and_register_native_libs( + native_lib_list, native_lib_count, native_lib_loaded_list); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + +#if WASM_ENABLE_AOT != 0 + if (wasm_runtime_is_xip_file(wasm_file_buf, wasm_file_size)) { + uint8 *wasm_file_mapped; + uint8 *daddr; + int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; + int map_flags = MMAP_MAP_32BIT; + + if (!(wasm_file_mapped = os_mmap(NULL, (uint32)wasm_file_size, map_prot, + map_flags, os_get_invalid_handle()))) { + printf("mmap memory failed\n"); + wasm_runtime_free(wasm_file_buf); + goto fail1; + } + +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + daddr = os_get_dbus_mirror(wasm_file_mapped); +#else + daddr = wasm_file_mapped; +#endif + bh_memcpy_s(daddr, wasm_file_size, wasm_file_buf, wasm_file_size); +#if (WASM_MEM_DUAL_BUS_MIRROR != 0) + os_dcache_flush(); +#endif + wasm_runtime_free(wasm_file_buf); + wasm_file_buf = wasm_file_mapped; + is_xip_file = true; + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_set_module_reader(module_reader_callback, + module_destroyer_callback); +#endif + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + +#if WASM_ENABLE_DYNAMIC_AOT_DEBUG != 0 + if (!wasm_runtime_set_module_name(wasm_module, wasm_file, error_buf, + sizeof(error_buf))) { + printf("set aot module name failed in dynamic aot debug mode, %s\n", + error_buf); + goto fail3; + } +#endif + + if (!wasm_runtime_instantiation_args_create(&inst_args)) { + printf("failed to create instantiate args\n"); + goto fail3; + } + wasm_runtime_instantiation_args_set_default_stack_size(inst_args, + stack_size); + wasm_runtime_instantiation_args_set_host_managed_heap_size(inst_args, + heap_size); +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_set_init_args(inst_args, argc, argv, &wasi_parse_ctx); +#endif + + /* instantiate the module */ + wasm_module_inst = wasm_runtime_instantiate_ex2( + wasm_module, inst_args, error_buf, sizeof(error_buf)); + wasm_runtime_instantiation_args_destroy(inst_args); + if (!wasm_module_inst) { + printf("%s\n", error_buf); + goto fail3; + } + +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + if (disable_bounds_checks) { + wasm_runtime_set_bounds_checks(wasm_module_inst, false); + } +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (ip_addr != NULL) { + wasm_exec_env_t exec_env = + wasm_runtime_get_exec_env_singleton(wasm_module_inst); + uint32_t debug_port; + if (exec_env == NULL) { + printf("%s\n", wasm_runtime_get_exception(wasm_module_inst)); + goto fail4; + } + debug_port = wasm_runtime_start_debug_instance(exec_env); + if (debug_port == 0) { + printf("Failed to start debug instance\n"); + goto fail4; + } + } +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + struct timeout_arg timeout_arg; + korp_tid timeout_tid; + if (timeout_ms >= 0) { + timeout_arg.timeout_ms = timeout_ms; + timeout_arg.inst = wasm_module_inst; + timeout_arg.cancel = false; + ret = os_thread_create(&timeout_tid, timeout_thread, &timeout_arg, + APP_THREAD_STACK_SIZE_DEFAULT); + if (ret != 0) { + printf("Failed to start timeout\n"); + goto fail5; + } + } +#endif + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (shared_heap_size > 0) { + memset(&shared_heap_init_args, 0, sizeof(shared_heap_init_args)); + shared_heap_init_args.size = shared_heap_size; + shared_heap = wasm_runtime_create_shared_heap(&shared_heap_init_args); + + if (!shared_heap) { + printf("Create preallocated shared heap failed\n"); + goto fail6; + } + + /* attach module instance to the shared heap */ + if (!wasm_runtime_attach_shared_heap(wasm_module_inst, shared_heap)) { + printf("Attach shared heap failed.\n"); + goto fail6; + } + } +#endif + + ret = 0; + const char *exception = NULL; + if (is_repl_mode) { + app_instance_repl(wasm_module_inst); + } + else if (func_name) { + exception = app_instance_func(wasm_module_inst, func_name); + if (exception) { + /* got an exception */ + ret = 1; + } + } + else { + exception = app_instance_main(wasm_module_inst); + if (exception) { + /* got an exception */ + ret = 1; + } + } + +#if WASM_ENABLE_LIBC_WASI != 0 + if (ret == 0) { + /* propagate wasi exit code. */ + ret = wasm_runtime_get_wasi_exit_code(wasm_module_inst); + } +#endif + + if (exception) + printf("%s\n", exception); + +#if WASM_ENABLE_STATIC_PGO != 0 && WASM_ENABLE_AOT != 0 + if (get_package_type(wasm_file_buf, wasm_file_size) == Wasm_Module_AoT + && gen_prof_file) + dump_pgo_prof_data(wasm_module_inst, gen_prof_file); +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + if (timeout_ms >= 0) { + timeout_arg.cancel = true; + os_thread_join(timeout_tid, NULL); + } +#endif + +#if WASM_ENABLE_SHARED_HEAP != 0 +fail6: +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +fail5: +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 +fail4: +#endif + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + if (!is_xip_file) + wasm_runtime_free(wasm_file_buf); + else + os_munmap(wasm_file_buf, wasm_file_size); + +fail1: +#if BH_HAS_DLFCN + /* unload the native libraries */ + unregister_and_unload_native_libs(native_lib_loaded_count, + native_lib_loaded_list); +#endif + + /* destroy runtime environment */ + wasm_runtime_destroy(); + + return ret; +} diff --git a/product-mini/platforms/riot/CMakeLists.txt b/product-mini/platforms/riot/CMakeLists.txt new file mode 100644 index 0000000000..cc98146c57 --- /dev/null +++ b/product-mini/platforms/riot/CMakeLists.txt @@ -0,0 +1,66 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + +project(NONE) + +enable_language (ASM) + +set (WAMR_BUILD_PLATFORM "riot") + +# Build as X86_32 by default, change to "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS" or "XTENSA" +# if we want to support arm, thumb, mips or xtensa + + +if (NOT DEFINED WAMR_BUILD_TARGET) + set (WAMR_BUILD_TARGET "X86_32") +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Disable AOT by default. + set (WAMR_BUILD_AOT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +if (NOT DEFINED WAMR_ROOT_DIR) + # this assumption is true if this file is copied to WAMR_ROOT + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +endif () + +# Override the global heap size for small devices +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + add_definitions (-DWASM_GLOBAL_HEAP_SIZE=262144) # 256 kB +endif () + + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +# need includes from RIOT prepare them as a cmake list +string(REGEX MATCHALL "([^\ ]+\ |[^\ ]+$)" RIOT_INCLUDES_LIST "${RIOT_INCLUDES}") + +include_directories(SYSTEM ${RIOT_INCLUDES_LIST}) + +# target_sources( ${WAMR_RUNTIME_LIB_SOURCE} ) +# executable linking is done by RIOT build system + +add_library( wamr ${WAMR_RUNTIME_LIB_SOURCE}) + +set_version_info (wamr) diff --git a/product-mini/platforms/riot/Makefile b/product-mini/platforms/riot/Makefile new file mode 100644 index 0000000000..d564a85a17 --- /dev/null +++ b/product-mini/platforms/riot/Makefile @@ -0,0 +1,97 @@ +APPLICATION = wamr-mini +# If no BOARD is defined in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../../../../RIOT + +USEMODULE += ztimer64_msec +USEMODULE += ztimer_usec +USEMODULE += sema + +WPEDANTIC := 0 +WERROR := 0 + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +ARCHIVES += $(BINDIR)/libwamr.a + +#Load the usual RIOT make infrastructure + +include $(RIOTBASE)/Makefile.include + + +WAMR_SOURCE = $(CURDIR)/../../.. +WAMR_BUILD_DIR = $(BINDIR)/wamr + +#less Wall TODO: get things fixed +CFLAGS := $(filter-out -pedantic, $(CFLAGS)) +CFLAGS += -Wno-format +CFLAGS += -Wno-strict-prototypes +CFLAGS += -Wno-old-style-definition +CFLAGS += -Wno-cast-function-type + +WAMR_CORE = $(WAMR_SOURCE)/core +IWASM_ROOT = $(WAMR_CORE)/iwasm +SHARED_LIB_ROOT = $(WAMR_CORE)/shared + +IWASM_INCLUDES += ${IWASM_ROOT}/include \ + ${SHARED_LIB_ROOT}/platform/include \ + ${SHARED_LIB_ROOT}/platform/riot \ + + +INCLUDES += $(addprefix -I,${IWASM_INCLUDES}) + + + +RIOT_INCLUDES = $(filter-out -%,$(subst -I,,$(INCLUDES))) + +#WAMR_BUILD_TARGET is "X86_32" "AARCH64[sub]", "ARM[sub]", +# "THUMB[sub]", "MIPS" or "XTENSA" +#no msp430, no AVR support for now + +#translate (CPU_ARCH) to Build Target +ifeq ($(CPU),native) +#$(CPU) is defined for every CPU +#Riot native is x86_32 + WAMR_BUILD_TARGET = X86_32 +else ifeq ($(findstring arm,$(CPU_ARCH)),arm) + WAMR_BUILD_TARGET = THUMB +else ifeq ($(CPU_ARCH),mips32r2) + WAMR_BUILD_TARGET = MIPS +else ifeq ($(CPU_ARCH),xtensa) + WAMR_BUILD_TARGET = XTENSA +endif + +ifeq ($(QUIET), 0) + CMAKEMAKEFLAGS += VERBOSE=1 +endif + + +$(BINDIR)/libwamr.a: $(WAMR_BUILD_DIR)/libwamr.a + cp $< $@ + +$(WAMR_BUILD_DIR)/libwamr.a: $(WAMR_BUILD_DIR)/Makefile + $(MAKE) -C $(WAMR_BUILD_DIR) $(CMAKEMAKEFLAGS) + +$(WAMR_BUILD_DIR)/Makefile: CMakeLists.txt + cmake -B$(WAMR_BUILD_DIR) \ + "-DRIOT_INCLUDES=$(RIOT_INCLUDES)"\ + -DWAMR_ROOT_DIR=$(WAMR_SOURCE)\ + -DWAMR_BUILD_TARGET=$(WAMR_BUILD_TARGET)\ + -DCMAKE_SYSTEM_NAME=Generic \ + -DCMAKE_SYSTEM_PROCESSOR="$(MCPU)" \ + -DCMAKE_C_COMPILER=$(CC) \ + -DCMAKE_C_COMPILER_WORKS=TRUE \ + +print_build_target: + @echo CPU_ARCH: $(CPU_ARCH) + @echo CPU: $(CPU) + @echo CFLAGS: $(CFLAGS) + @echo WAMR_BUILD_TARGET: $(WAMR_BUILD_TARGET) diff --git a/product-mini/platforms/riot/iwasmt.c b/product-mini/platforms/riot/iwasmt.c new file mode 100644 index 0000000000..633b2b405d --- /dev/null +++ b/product-mini/platforms/riot/iwasmt.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * Copyright (C) 2020 TU Bergakademie Freiberg Karl Fessel + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include + +#include + +#include "wasm_export.h" + +#include + +/* provide some test program */ +#include "test_wasm.h" + +#define DEFAULT_THREAD_STACKSIZE (6 * 1024) +#define DEFAULT_THREAD_PRIORITY 50 + +static int app_argc; +static char **app_argv; + +static void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) { + puts(exception); + } + return NULL; +} + +void * +iwasm_t(void *arg1) +{ + wasm_module_t wasm_module = (wasm_module_t)arg1; + wasm_module_inst_t wasm_module_inst = NULL; + char error_buf[128]; + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate( + wasm_module, 8 * 1024, 8 * 1024, error_buf, sizeof(error_buf)))) { + puts(error_buf); + } + else { + app_instance_main(wasm_module_inst); + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + } + return NULL; +} + +/* enable FUNC_ALLOC to use custom memory allocation functions */ +#define FUNC_ALLOC + +void * +iwasm_main(void *arg1) +{ + (void)arg1; /* unused */ + uint8_t *wasm_file_buf = NULL; + unsigned wasm_file_buf_size = 0; + wasm_module_t wasm_module = NULL; + char error_buf[128]; + + RuntimeInitArgs init_args; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); +#if defined(FUNC_ALLOC) && WASM_ENABLE_GLOBAL_HEAP_POOL == 0 + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#elif WASM_ENABLE_GLOBAL_HEAP_POOL != 0 + static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_System_Allocator; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + puts("Init runtime environment failed."); + return NULL; + } + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8_t *)wasm_test_file; + wasm_file_buf_size = sizeof(wasm_test_file); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_buf_size, + error_buf, sizeof(error_buf)))) { + puts(error_buf); + } + else { + iwasm_t(wasm_module); + wasm_runtime_unload(wasm_module); + } + + wasm_runtime_destroy(); + return NULL; +} + +bool +iwasm_init(void) +{ + /* clang-format off */ + struct { + char *stack; + int stacksize; + uint8_t priority; + int flags; + thread_task_func_t task_func; + void *arg; + const char *name; + } b = { + .stacksize = DEFAULT_THREAD_STACKSIZE, + .priority = 8, + .flags = 0, + .task_func = iwasm_main, + .arg = NULL, + .name = "simple_wamr" + }; + /* clang-format on */ + + b.stack = malloc(b.stacksize); + kernel_pid_t tpid = thread_create(b.stack, b.stacksize, b.priority, b.flags, + b.task_func, b.arg, b.name); + + return tpid != 0 ? true : false; + ; +} + +#define telltruth(X) ((X) ? "true" : "false") + +int +main(void) +{ + printf("iwasm_initilised: %s\n", telltruth(iwasm_init())); +} diff --git a/product-mini/platforms/riot/test_wasm.h b/product-mini/platforms/riot/test_wasm.h new file mode 100644 index 0000000000..0c343024a2 --- /dev/null +++ b/product-mini/platforms/riot/test_wasm.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * The byte array buffer is the file content of a test wasm binary file, + * which is compiled by wasi-sdk toolchain from C source file of: + * product-mini/app-samples/hello-world/main.c. + */ +unsigned char wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x28, 0x0B, 0x7F, 0x00, 0x41, 0xBA, 0x08, 0x0B, + 0x7F, 0x00, 0x41, 0xC0, 0x28, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, + 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x04, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x04, 0x0A, 0xB2, 0x01, 0x01, 0xAF, 0x01, 0x01, 0x03, + 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x88, 0x80, 0x80, 0x00, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, + 0x41, 0xA8, 0x88, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1A, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6A, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, + 0x04, 0x20, 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x88, + 0x80, 0x80, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, + 0x8D, 0x88, 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x41, 0x93, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, + 0x80, 0x00, 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x0B, 0x0B, 0x41, 0x01, 0x00, 0x41, 0x80, 0x08, + 0x0B, 0x3A, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, + 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, + 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, + 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; diff --git a/product-mini/platforms/rt-thread/SConscript b/product-mini/platforms/rt-thread/SConscript new file mode 100644 index 0000000000..4f7ffe63ed --- /dev/null +++ b/product-mini/platforms/rt-thread/SConscript @@ -0,0 +1,14 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +group = DefineGroup('iwasm', src, depend = ['PKG_USING_WAMR']) + +Return('group') diff --git a/product-mini/platforms/rt-thread/iwasm.c b/product-mini/platforms/rt-thread/iwasm.c new file mode 100644 index 0000000000..56905fbc60 --- /dev/null +++ b/product-mini/platforms/rt-thread/iwasm.c @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include "wasm_export.h" +#include "platform_api_vmcore.h" +#include +#include +#include + +#if WASM_ENABLE_LIBC_WASI != 0 +#include "../common/libc_wasi.c" +#endif + +#ifdef WAMR_ENABLE_RTT_EXPORT + +#ifdef WAMR_RTT_EXPORT_VPRINTF +static int +wasm_vprintf(wasm_exec_env_t env, const char *fmt, va_list va) +{ + return vprintf(fmt, va); +} + +static int +wasm_vsprintf(wasm_exec_env_t env, char *buf, const char *fmt, va_list va) +{ + return vsprintf(buf, fmt, va); +} + +static int +wasm_vsnprintf(wasm_exec_env_t env, char *buf, int n, const char *fmt, + va_list va) +{ + return vsnprintf(buf, n, fmt, va); +} + +#endif /* WAMR_RTT_EXPORT_VPRINTF */ + +#ifdef WAMR_RTT_EXPORT_DEVICE_OPS +static rt_device_t +wasm_rt_device_find(wasm_exec_env_t env, const char *name) +{ + return rt_device_find(name); +} + +static rt_err_t +wasm_rt_device_open(wasm_exec_env_t env, rt_device_t dev, rt_uint16_t o_flag) +{ + return rt_device_open(dev, o_flag); +} + +static rt_size_t +wasm_rt_device_write(wasm_exec_env_t env, rt_device_t dev, rt_off_t offset, + const void *buf, rt_size_t size) +{ + return rt_device_write(dev, offset, buf, size); +} + +static rt_size_t +wasm_rt_device_read(wasm_exec_env_t env, rt_device_t dev, rt_off_t offset, + void *buf, rt_size_t size) +{ + return rt_device_read(dev, offset, buf, size); +} + +static rt_err_t +wasm_rt_device_close(wasm_exec_env_t env, rt_device_t dev) +{ + return rt_device_close(dev); +} + +static rt_err_t +wasm_rt_device_control(wasm_exec_env_t env, rt_device_t dev, int cmd, void *arg) +{ + return rt_device_control(dev, cmd, arg); +} + +#endif /* WAMR_RTT_EXPORT_DEVICE_OPS */ + +/* clang-format off */ +static NativeSymbol native_export_symbols[] = { +#ifdef WAMR_RTT_EXPORT_VPRINTF + { + "vprintf", + wasm_vprintf, + "($*)i" + }, + { + "vsprintf", + wasm_vsprintf, + "($$*)i" + }, + { + "vsnprintf", + wasm_vsnprintf, + "($i$*)i" + }, +#endif /* WAMR_RTT_EXPORT_VPRINTF */ + +#ifdef WAMR_RTT_EXPORT_DEVICE_OPS + { + "rt_device_find", + wasm_rt_device_find, + "($)i" + }, + { + "rt_device_open", + wasm_rt_device_open, + "(ii)i" + }, + { + "rt_device_write", + wasm_rt_device_write, + "(ii*~)i" + }, + { + "rt_device_read", + wasm_rt_device_read, + "(ii*~)i" + }, + { + "rt_device_close", + wasm_rt_device_close, + "(i)i" + }, + { + "rt_device_control", + wasm_rt_device_control, + "(ii*)i" + }, +#ifdef WAMR_RTT_EXPORT_DEVICE_OPS_CPP + { + "_Z15rt_device_closeP9rt_device", + wasm_rt_device_close, + "(i)i" + }, + { + "_Z14rt_device_readP9rt_devicejPvj", + wasm_rt_device_read, + "(ii*~)i" + }, + { + "_Z15rt_device_writeP9rt_devicejPKvj", + wasm_rt_device_write, + "(ii*~)i" + }, + { + "_Z14rt_device_openP9rt_devicet", + wasm_rt_device_open, + "(ii)i" + }, + { + "_Z14rt_device_findPKc", + wasm_rt_device_find, + "($)i" + }, +#endif /* WAMR_RTT_EXPORT_DEVICE_OPS_CPP */ +#endif /* WAMR_RTT_EXPORT_DEVICE_OPS */ +}; +/* clang-format on */ + +#endif /* WAMR_ENABLE_RTT_EXPORT */ + +static void * +app_instance_func(wasm_module_inst_t module_inst, const char *func_name, + int app_argc, char **app_argv) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + return wasm_runtime_get_exception(module_inst); +} + +/** + * run WASM module instance. + * @param module_inst instance of wasm module + * @param app_argc wasm argument count + * @param app_argv wasm arguments + * @return NULL + */ +static void * +app_instance_main(wasm_module_inst_t module_inst, int app_argc, char **app_argv) +{ + wasm_application_execute_main(module_inst, app_argc, app_argv); + return wasm_runtime_get_exception(module_inst); +} + +rt_uint8_t * +my_read_file_to_buffer(char *filename, rt_uint32_t *size) +{ + struct stat f_stat; + + if (!filename || !size) { + rt_set_errno(-EINVAL); + return RT_NULL; + } + + if (stat(filename, &f_stat) != 0) { + rt_set_errno(errno); + return RT_NULL; + } + + if (f_stat.st_size <= 0) { + rt_set_errno(-EINVAL); + return RT_NULL; + } + + rt_uint8_t *buff = rt_malloc(f_stat.st_size); + *size = 0; + if (!buff) { + rt_set_errno(-ENOMEM); + return RT_NULL; + } + + int fd = open(filename, O_RDONLY); + if (fd < 0) { + rt_free(buff); + rt_set_errno(fd); + return RT_NULL; + } + + *size = read(fd, buff, f_stat.st_size); + + close(fd); + + if (*size != f_stat.st_size) { + rt_free(buff); + rt_set_errno(-EBADF); + return RT_NULL; + } + + return buff; +} + +void +iwasm_help(void) +{ +#ifdef WAMR_ENABLE_IWASM_PARAMS + rt_kputs("Usage: iwasm [-options] wasm_file [args...]\n"); + rt_kputs("options:\n"); + rt_kputs(" -t Show time taking to run this app.\n"); + rt_kputs(" -m Show memory taking to run this app\n"); + rt_kputs(" -f|--function name Specify a function name of the module " + "to run rather than main\n"); + rt_kputs(" --max-threads=n Set maximum thread number per " + "cluster, default is 4\n"); +#else + rt_kputs("Usage: iwasm wasm_file [args...]\n"); +#endif /* WAMR_ENABLE_PARAMS */ +} + +int +iwasm(int argc, char **argv) +{ + const char *exception = NULL; + const char *func_name = NULL; + rt_uint8_t *wasm_file_buf = NULL; + rt_uint32_t wasm_file_size; + rt_uint32_t stack_size = 64 * 1024, heap_size = 256 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + struct InstantiationArgs2 *inst_args; + static char error_buf[128] = { 0 }; + /* avoid stack overflow */ +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_parse_context_t wasi_parse_ctx; + memset(&wasi_parse_ctx, 0, sizeof(wasi_parse_ctx)); +#endif + +#ifdef WAMR_ENABLE_IWASM_PARAMS + int i_arg_begin; + bool show_mem = false; + bool show_stack = false; + bool show_time_exec = false; + for (i_arg_begin = 1; i_arg_begin < argc; i_arg_begin++) { + if (argv[i_arg_begin][0] != '-') { + break; + } + + if (argv[i_arg_begin][1] == 'm') { + show_mem = true; + } + else if (argv[i_arg_begin][1] == 's') { + show_stack = true; + } + else if (argv[i_arg_begin][1] == 't') { + show_time_exec = true; + } + else if (argv[i_arg_begin][1] == 'h') { + iwasm_help(); + return 0; + } + else if (argv[i_arg_begin][1] == 'f') { + func_name = argv[++i_arg_begin]; + } + else if (!strncmp(argv[i_arg_begin], "--max-threads=", 14)) { + if (argv[0][14] != '\0') + wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); + else { + iwasm_help(); + return 0; + } + } + else if (argv[i_arg_begin][1] == 0x00) { + continue; + } + else { + rt_kprintf("[iwasm] unknown param: %s\n", argv[i_arg_begin]); + } + } +#else /* WAMR_ENABLE_PARAMS */ +#define i_arg_begin 1 +#endif /* WAMR_ENABLE_PARAMS */ + + if (argc - i_arg_begin < 1) { + iwasm_help(); + return -1; + } + + rt_memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = os_malloc; + init_args.mem_alloc_option.allocator.realloc_func = os_realloc; + init_args.mem_alloc_option.allocator.free_func = os_free; +#ifdef WAMR_ENABLE_RTT_EXPORT + init_args.native_symbols = native_export_symbols; + init_args.n_native_symbols = + sizeof(native_export_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; +#endif /* WAMR_ENABLE_RTT_EXPORT */ + +#ifdef WAMR_ENABLE_IWASM_PARAMS +#if defined(RT_USING_HEAP) && defined(RT_USING_MEMHEAP_AS_HEAP) + extern long list_memheap(void); + if (show_mem) { + list_memheap(); + } +#else + rt_uint32_t total, max, used; + if (show_mem) { + rt_memory_info(&total, &used, &max); + } +#endif + rt_thread_t tid; + if (show_stack) { + tid = rt_thread_self(); + rt_kprintf("thread stack addr: %p, size: %u, sp: %p\n", tid->stack_addr, + tid->stack_size, tid->sp); + } +#endif /* WAMR_ENABLE_PARAMS */ + + if (wasm_runtime_full_init(&init_args) == false) { + rt_kprintf("Init WASM runtime environment failed.\n"); + return -1; + } + + wasm_file_buf = my_read_file_to_buffer(argv[i_arg_begin], &wasm_file_size); + if (!wasm_file_buf) { + rt_err_t err = rt_get_errno(); + rt_kprintf("WASM load file to RAM failed: %d\n", err); + goto fail1; + } + rt_memset(error_buf, 0x00, sizeof(error_buf)); + wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, error_buf, + sizeof(error_buf)); + if (!wasm_module) { + rt_kprintf("%s\n", error_buf); + goto fail2; + } + + if (!wasm_runtime_instantiation_args_create(&inst_args)) { + rt_kprintf("failed to create instantiate args\n"); + goto fail3; + } + wasm_runtime_instantiation_args_set_default_stack_size(inst_args, + stack_size); + wasm_runtime_instantiation_args_set_host_managed_heap_size(inst_args, + heap_size); +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_set_init_args(wasm_module, argc, argv, &wasi_parse_ctx); +#endif + + rt_memset(error_buf, 0x00, sizeof(error_buf)); + wasm_module_inst = wasm_runtime_instantiate_ex2( + wasm_module, inst_args, error_buf, sizeof(error_buf)); + wasm_runtime_instantiation_args_destroy(inst_args); + if (!wasm_module_inst) { + rt_kprintf("%s\n", error_buf); + goto fail3; + } + +#ifdef WAMR_ENABLE_IWASM_PARAMS + rt_tick_t ticks_exec; + if (show_time_exec) { + ticks_exec = rt_tick_get(); + } +#endif /* WAMR_ENABLE_PARAMS */ + + if (func_name) { + exception = app_instance_func(wasm_module_inst, func_name, + argc - i_arg_begin, &argv[i_arg_begin]); + } + else { + exception = app_instance_main(wasm_module_inst, argc - i_arg_begin, + &argv[i_arg_begin]); + rt_kprintf("finished run app_instance_main\n"); + } + + if (exception) + rt_kprintf("%s\n", exception); + +#if WASM_ENABLE_LIBC_WASI != 0 + if (!exception) { + /* propagate wasi exit code. */ + wasm_runtime_get_wasi_exit_code(wasm_module_inst); + } +#endif + +#ifdef WAMR_ENABLE_IWASM_PARAMS + if (show_time_exec) { + ticks_exec = rt_tick_get() - ticks_exec; + rt_kprintf("[iwasm] execute ticks took: %u [ticks/s = %u]\n", + ticks_exec, RT_TICK_PER_SECOND); + } +#if defined(RT_USING_HEAP) && defined(RT_USING_MEMHEAP_AS_HEAP) + if (show_mem) { + list_memheap(); + } +#else + rt_uint32_t total_after, max_after, used_after; + if (show_mem) { + rt_memory_info(&total_after, &used_after, &max_after); + rt_kprintf("[iwasm] memory took: %u\n", used_after - used); + } +#endif + if (show_stack) { + rt_kprintf("[iwasm] thread stack addr: %p, size: %u, sp: %p\n", + tid->stack_addr, tid->stack_size, tid->sp); + } + +#endif /* WAMR_ENABLE_PARAMS */ + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + rt_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} +MSH_CMD_EXPORT(iwasm, Embedded VM of WebAssembly); diff --git a/product-mini/platforms/rt-thread/iwasm.cmake b/product-mini/platforms/rt-thread/iwasm.cmake new file mode 100644 index 0000000000..0ec54ae6b5 --- /dev/null +++ b/product-mini/platforms/rt-thread/iwasm.cmake @@ -0,0 +1,66 @@ +# +# Copyright (c) 2021, RT-Thread Development Team +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +set(CMAKE_ASM_COMPILER_WORKS 1) + +set(WAMR_BUILD_PLATFORM "rt-thread") +set(WAMR_BUILD_TARGET "ARM") + +#set(WAMR_BUILD_INTERP 1) +#set(WAMR_BUILD_FAST_INTERP 1) +#set(WAMR_BUILD_AOT 0) +#set(WAMR_BUILD_JIT 0) +#set(WAMR_BUILD_LIBC_BUILTIN 1) +#set(WAMR_BUILD_LIBC_WASI 0) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 0) +endif () + +# Disable JIT by default. +set (WAMR_BUILD_JIT 0) + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +set (WAMR_BUILD_LIBC_WASI 0) + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +set (WAMR_BUILD_MULTI_MODULE 0) +set (WAMR_BUILD_LIB_PTHREAD 0) +set (WAMR_BUILD_MINI_LOADER 0) +set (WAMR_BUILD_SIMD 0) + + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + +set(CMAKE_ASM_COMPILER_WORKS 1) + +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +file (GLOB wamr_entry_src + ${WAMR_ROOT_DIR}/product-mini/platforms/rt-thread/rtt_wamr_entry.c + ) + +set(WAMR_SOURCE ${wamr_entry_src} ${WAMR_RUNTIME_LIB_SOURCE}) + + diff --git a/product-mini/platforms/vxworks/CMakeLists.txt b/product-mini/platforms/vxworks/CMakeLists.txt index 88ba8328c3..3f08a72ad0 100644 --- a/product-mini/platforms/vxworks/CMakeLists.txt +++ b/product-mini/platforms/vxworks/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required (VERSION 2.8) +cmake_minimum_required (VERSION 3.14) project (iwasm) @@ -24,9 +24,11 @@ if (NOT DEFINED WAMR_BUILD_TARGET) if (CMAKE_SIZEOF_VOID_P EQUAL 8) # Build as X86_64 by default in 64-bit platform set (WAMR_BUILD_TARGET "X86_64") - else () + 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 () @@ -59,14 +61,30 @@ if (NOT DEFINED WAMR_BUILD_LIBC_WASI) set (WAMR_BUILD_LIBC_WASI 0) endif () +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_version_info (vmlib) + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) -add_executable (iwasm main.c ext_lib_export.c) +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +set_version_info (iwasm) install (TARGETS iwasm DESTINATION bin) @@ -74,6 +92,8 @@ target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lunix) add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +set_version_info (libiwasm) + install (TARGETS libiwasm DESTINATION lib) set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) diff --git a/product-mini/platforms/vxworks/ext_lib_export.c b/product-mini/platforms/vxworks/ext_lib_export.c deleted file mode 100644 index 8813f0dbf3..0000000000 --- a/product-mini/platforms/vxworks/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/vxworks/main.c b/product-mini/platforms/vxworks/main.c index 5f0ba1525a..8f0e84a97f 100644 --- a/product-mini/platforms/vxworks/main.c +++ b/product-mini/platforms/vxworks/main.c @@ -3,304 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include -#include -#include "bh_platform.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_memory.h" -#include "wasm_export.h" - -static int app_argc; -static char **app_argv; - -static int print_help() -{ - bh_printf("Usage: iwasm [-options] wasm_file [args...]\n"); - bh_printf("options:\n"); - bh_printf(" -f|--function name Specify function name to run in module\n" - " rather than main\n"); -#if WASM_ENABLE_LOG != 0 - bh_printf(" -v=X Set log verbose level (0 to 5, default is 2),\n" - " larger level with more log\n"); -#endif - bh_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 - bh_printf(" --env= Pass wasi environment variables with \"key=value\"\n"); - bh_printf(" to the program, for example:\n"); - bh_printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); - bh_printf(" --dir= Grant wasi access to the given host directories\n"); - bh_printf(" to the program, for example:\n"); - bh_printf(" --dir= --dir=\n"); -#endif - - return 1; -} - -static void* -app_instance_main(wasm_module_inst_t module_inst) -{ - const char *exception; - - wasm_application_execute_main(module_inst, app_argc, app_argv); - if ((exception = wasm_runtime_get_exception(module_inst))) - bh_printf("%s\n", exception); - return NULL; -} - -static void* -app_instance_func(wasm_module_inst_t module_inst, const char *func_name) -{ - wasm_application_execute_func(module_inst, func_name, app_argc - 1, - app_argv + 1); - /* The result of wasm function or exception info was output inside - wasm_application_execute_func(), here we don't output them again. */ - return NULL; -} - -/** - * Split a space separated strings into an array of strings - * Returns NULL on failure - * Memory must be freed by caller - * Based on: http://stackoverflow.com/a/11198630/471795 - */ -static char ** -split_string(char *str, int *count) -{ - char **res = NULL; - char *p; - int idx = 0; - - /* split string and append tokens to 'res' */ - do { - p = strtok(str, " "); - str = NULL; - res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1)); - if (res == NULL) { - return NULL; - } - res[idx++] = p; - } while (p); - - if (count) { - *count = idx - 1; - } - return res; -} - -static void* -app_instance_repl(wasm_module_inst_t module_inst) -{ - char *cmd = NULL; - size_t len = 0; - ssize_t n; - - while ((bh_printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { - bh_assert(n > 0); - if (cmd[n - 1] == '\n') { - if (n == 1) - continue; - else - cmd[n - 1] = '\0'; - } - app_argv = split_string(cmd, &app_argc); - if (app_argv == NULL) { - LOG_ERROR("Wasm prepare param failed: split string failed.\n"); - break; - } - if (app_argc != 0) { - wasm_application_execute_func(module_inst, app_argv[0], - app_argc - 1, app_argv + 1); - } - free(app_argv); - } - free(cmd); - return NULL; -} - -#if WASM_ENABLE_LIBC_WASI != 0 -static bool -validate_env_str(char *env) -{ - char *p = env; - int key_len = 0; - - while (*p != '\0' && *p != '=') { - key_len++; - p++; - } - - if (*p != '=' || key_len == 0) - return false; - - return true; -} -#endif - -#define USE_GLOBAL_HEAP_BUF 0 - -#if USE_GLOBAL_HEAP_BUF != 0 -static char global_heap_buf[10 * 1024 * 1024] = { 0 }; -#endif - -int main(int argc, char *argv[]) -{ - char *wasm_file = NULL; - const char *func_name = NULL; - uint8 *wasm_file_buf = NULL; - uint32 wasm_file_size; - wasm_module_t wasm_module = NULL; - wasm_module_inst_t wasm_module_inst = NULL; - char error_buf[128] = { 0 }; -#if WASM_ENABLE_LOG != 0 - int log_verbose_level = 2; -#endif - bool is_repl_mode = false; -#if WASM_ENABLE_LIBC_WASI != 0 - const char *dir_list[8] = { NULL }; - uint32 dir_list_size = 0; - const char *env_list[8] = { NULL }; - uint32 env_list_size = 0; -#endif - - /* Process options. */ - for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { - if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { - argc--, argv++; - if (argc < 2) { - print_help(); - return 0; - } - func_name = argv[0]; - } -#if WASM_ENABLE_LOG != 0 - else if (!strncmp(argv[0], "-v=", 3)) { - log_verbose_level = atoi(argv[0] + 3); - if (log_verbose_level < 0 || log_verbose_level > 5) - return print_help(); - } -#endif - else if (!strcmp(argv[0], "--repl")) - is_repl_mode = true; -#if WASM_ENABLE_LIBC_WASI != 0 - else if (!strncmp(argv[0], "--dir=", 6)) { - if (argv[0][6] == '\0') - return print_help(); - if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) { - bh_printf("Only allow max dir number %d\n", - (int)(sizeof(dir_list) / sizeof(char*))); - return -1; - } - dir_list[dir_list_size++] = argv[0] + 6; - } - else if (!strncmp(argv[0], "--env=", 6)) { - char *tmp_env; - - if (argv[0][6] == '\0') - return print_help(); - if (env_list_size >= sizeof(env_list) / sizeof(char*)) { - bh_printf("Only allow max env number %d\n", - (int)(sizeof(env_list) / sizeof(char*))); - return -1; - } - tmp_env = argv[0] + 6; - if (validate_env_str(tmp_env)) - env_list[env_list_size++] = tmp_env; - else { - bh_printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n", - tmp_env); - return print_help(); - } - } -#endif - else - return print_help(); - } - - if (argc == 0) - return print_help(); - - wasm_file = argv[0]; - app_argc = argc; - app_argv = argv; - -#if USE_GLOBAL_HEAP_BUF != 0 - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - bh_printf("Init memory with global heap buffer failed.\n"); - return -1; - } -#else - if (bh_memory_init_with_allocator(malloc, free)) { - bh_printf("Init memory with memory allocator failed.\n"); - return -1; - } -#endif - - /* initialize runtime environment */ - if (!wasm_runtime_init()) - goto fail1; - - bh_log_set_verbose_level(log_verbose_level); - - /* load WASM byte buffer from WASM bin file */ - if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file, - &wasm_file_size))) - goto fail2; - - /* load WASM module */ - if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, - error_buf, sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail3; - } - -#if WASM_ENABLE_LIBC_WASI != 0 - wasm_runtime_set_wasi_args(wasm_module, - dir_list, dir_list_size, - NULL, 0, - env_list, env_list_size, - argv, argc); -#endif - - /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, - 64 * 1024, /* stack size */ - 64 * 1024, /* heap size */ - error_buf, - sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail4; - } - - if (is_repl_mode) - app_instance_repl(wasm_module_inst); - else if (func_name) - app_instance_func(wasm_module_inst, func_name); - else - app_instance_main(wasm_module_inst); - - /* destroy the module instance */ - wasm_runtime_deinstantiate(wasm_module_inst); - -fail4: - /* unload the module */ - wasm_runtime_unload(wasm_module); - -fail3: - /* free the file buffer */ - bh_free(wasm_file_buf); - -fail2: - /* destroy runtime environment */ - wasm_runtime_destroy(); - -fail1: - bh_memory_destroy(); - return 0; -} - +#include "../posix/main.c" diff --git a/product-mini/platforms/windows/CMakeLists.txt b/product-mini/platforms/windows/CMakeLists.txt new file mode 100644 index 0000000000..326c2cf6cf --- /dev/null +++ b/product-mini/platforms/windows/CMakeLists.txt @@ -0,0 +1,169 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project (iwasm C ASM CXX) +# set (CMAKE_VERBOSE_MAKEFILE 1) + +option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) + +set (WAMR_BUILD_PLATFORM "windows") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set(CMAKE_CXX_STANDARD 17) + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (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(FATAL_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Enable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (NOT DEFINED WAMR_BUILD_SIMD) + # Enable SIMD by default + set (WAMR_BUILD_SIMD 1) +endif () + +if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP) + # Disable Debug feature by default + set (WAMR_BUILD_DEBUG_INTERP 0) +endif () + +if (WAMR_BUILD_DEBUG_INTERP EQUAL 1) + set (WAMR_BUILD_FAST_INTERP 0) + set (WAMR_BUILD_MINI_LOADER 0) + set (WAMR_BUILD_SIMD 0) +endif () + +if (WAMR_BUILD_LIBC_WASI EQUAL 1) + set (CMAKE_C_STANDARD 11) + if (MSVC) + add_compile_options(/experimental:c11atomics) + endif() +else() + set (CMAKE_C_STANDARD 99) +endif() + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32_LEAN_AND_MEAN") +if (NOT MINGW) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO") +endif () + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#endif () +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") +#set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${PLATFORM_SHARED_SOURCE} ${UNCOMMON_SHARED_SOURCE} ${UTILS_SHARED_SOURCE}) + +set_version_info (iwasm) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib) + +add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +set_version_info (vmlib) + +target_include_directories(vmlib INTERFACE + $ +) + +set (WAMR_PUBLIC_HEADERS + ${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 +) + +set_target_properties (vmlib PROPERTIES + OUTPUT_NAME libiwasm + PUBLIC_HEADER "${WAMR_PUBLIC_HEADERS}" + POSITION_INDEPENDENT_CODE ON +) + +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS}) +if (MINGW) + target_link_libraries (vmlib ws2_32) +endif () + +if (WIN32) + target_link_libraries(vmlib ntdll) +endif() + +install (TARGETS vmlib + EXPORT iwasmTargets + DESTINATION lib + PUBLIC_HEADER DESTINATION include +) + +install_iwasm_package () diff --git a/product-mini/platforms/windows/build_llvm.py b/product-mini/platforms/windows/build_llvm.py new file mode 100644 index 0000000000..9325a0209b --- /dev/null +++ b/product-mini/platforms/windows/build_llvm.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import pathlib +import subprocess +import sys + +script = ( + pathlib.Path(__file__) + .parent.joinpath("../../../build-scripts/build_llvm.py") + .resolve() +) +subprocess.check_call([sys.executable, script]) diff --git a/product-mini/platforms/windows/main.c b/product-mini/platforms/windows/main.c new file mode 100644 index 0000000000..a19c50d064 --- /dev/null +++ b/product-mini/platforms/windows/main.c @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +#if WASM_ENABLE_LIBC_WASI != 0 +#include "../common/libc_wasi.c" +#endif + +#include "../common/wasm_proposal.c" + +static int app_argc; +static char **app_argv; + +/* clang-format off */ +static int +print_help(void) +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); +#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"); +#if WASM_ENABLE_LIBC_WASI !=0 + printf(" --heap-size=n Set maximum heap size in bytes, default is 0 KB when libc wasi is enabled\n"); +#else + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB when libc wasi is diabled\n"); +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + printf(" --shared-heap-size=n Create shared heap of n bytes and attach to the wasm app.\n"); + printf(" The size n will be adjusted to a minumum number aligned to page size\n"); +#endif +#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_GC != 0 + printf(" --gc-heap-size=n Set maximum gc heap size in bytes,\n"); + printf(" default is %u KB\n", GC_HEAP_SIZE_DEFAULT / 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 /* WASM_ENABLE_JIT != 0 */ + 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_CONFIGURABLE_BOUNDS_CHECKS != 0 + printf(" --disable-bounds-checks Disable bounds checks for memory accesses\n"); +#endif +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_print_help(); +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + printf(" --module-path= Indicate a module search path. default is current\n" + " directory('./')\n"); +#endif +#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 + printf(" -g=ip:port Set the debug sever address, default is debug disabled\n"); + printf(" if port is 0, then a random port will be used\n"); +#endif + printf(" --version Show version information\n"); + return 1; +} +/* clang-format on */ + +static const void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + exception = wasm_runtime_get_exception(module_inst); + return exception; +} + +static const void * +app_instance_func(wasm_module_inst_t module_inst, const char *func_name) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + /* The result of wasm function or exception info was output inside + wasm_application_execute_func(), here we don't output them again. */ + return wasm_runtime_get_exception(module_inst); +} + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL, **res1; + char *p, *next_token; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok_s(str, " ", &next_token); + str = NULL; + res1 = res; + res = (char **)realloc(res1, sizeof(char *) * (uint32)(idx + 1)); + if (res == NULL) { + free(res1); + return NULL; + } + res[idx++] = p; + } while (p); + + /** + * Due to the function name, + * res[0] might contain a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + + if (count) { + *count = idx - 1; + } + return res; +} + +static void * +app_instance_repl(wasm_module_inst_t module_inst) +{ + char buffer[4096]; + char *cmd; + size_t n; + + while ((printf("webassembly> "), fflush(stdout), + cmd = fgets(buffer, sizeof(buffer), stdin)) + != NULL) { + bh_assert(cmd); + n = strlen(cmd); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + if (!strcmp(cmd, "__exit__")) { + printf("exit repl mode\n"); + break; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + LOG_ERROR("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + const char *exception; + wasm_application_execute_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + } + free(app_argv); + } + + return NULL; +} + +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 +static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 }; +#else +static void * +malloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + unsigned int size) +{ + return malloc(size); +} + +static void * +realloc_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, bool full_size_mmaped, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr, unsigned int size) +{ + return realloc(ptr, size); +} + +static void +free_func( +#if WASM_MEM_ALLOC_WITH_USAGE != 0 + mem_alloc_usage_t usage, +#endif +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + void *user_data, +#endif + void *ptr) +{ + free(ptr); +} +#endif /* end of WASM_ENABLE_GLOBAL_HEAP_POOL */ + +#if WASM_ENABLE_MULTI_MODULE != 0 +static char * +handle_module_path(const char *module_path) +{ + /* next character after '=' */ + return (strchr(module_path, '=')) + 1; +} + +static char *module_search_path = "."; + +static bool +module_reader_callback(package_type_t module_type, const char *module_name, + uint8 **p_buffer, uint32 *p_size) +{ + char *file_format = NULL; +#if WASM_ENABLE_INTERP != 0 + if (module_type == Wasm_Module_Bytecode) + file_format = ".wasm"; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_type == Wasm_Module_AoT) + file_format = ".aot"; +#endif + bh_assert(file_format); + const char *format = "%s/%s%s"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(file_format) + 1; + char *wasm_file_name = wasm_runtime_malloc(sz); + if (!wasm_file_name) { + return false; + } + snprintf(wasm_file_name, sz, format, module_search_path, module_name, + file_format); + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); + + wasm_runtime_free(wasm_file_name); + return *p_buffer != NULL; +} + +static void +module_destroyer_callback(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + wasm_runtime_free(buffer); + buffer = NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +int +main(int argc, char *argv[]) +{ + int32 ret = -1; + char *wasm_file = NULL; + const char *func_name = NULL; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 64 * 1024; +#if WASM_ENABLE_LIBC_WASI != 0 + uint32 heap_size = 0; +#else + uint32 heap_size = 16 * 1024; +#endif +#if WASM_ENABLE_SHARED_HEAP != 0 + SharedHeapInitArgs shared_heap_init_args; + uint32 shared_heap_size = 0; + void *shared_heap = NULL; +#endif +#if WASM_ENABLE_FAST_JIT != 0 + uint32 jit_code_cache_size = FAST_JIT_DEFAULT_CODE_CACHE_SIZE; +#endif +#if WASM_ENABLE_GC != 0 + uint32 gc_heap_size = GC_HEAP_SIZE_DEFAULT; +#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; + struct InstantiationArgs2 *inst_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + bool is_repl_mode = false; + bool is_xip_file = false; +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + bool disable_bounds_checks = false; +#endif +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_parse_context_t wasi_parse_ctx; +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 + char *ip_addr = NULL; + int instance_port = 0; +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + memset(&wasi_parse_ctx, 0, sizeof(wasi_parse_ctx)); +#endif + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + return print_help(); + } + 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); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } +#endif + else if (!strcmp(argv[0], "--repl")) { + is_repl_mode = true; + } +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + else if (!strcmp(argv[0], "--disable-bounds-checks")) { + disable_bounds_checks = true; + } +#endif + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } +#if WASM_ENABLE_SHARED_HEAP != 0 + else if (!strncmp(argv[0], "--shared-heap-size=", 19)) { + if (argv[0][19] == '\0') + return print_help(); + shared_heap_size = atoi(argv[0] + 19); + } +#endif +#if WASM_ENABLE_FAST_JIT != 0 + else if (!strncmp(argv[0], "--jit-codecache-size=", 21)) { + if (argv[0][21] == '\0') + return print_help(); + jit_code_cache_size = atoi(argv[0] + 21); + } +#endif +#if WASM_ENABLE_GC != 0 + else if (!strncmp(argv[0], "--gc-heap-size=", 15)) { + if (argv[0][15] == '\0') + return print_help(); + gc_heap_size = atoi(argv[0] + 15); + } +#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_MULTI_MODULE != 0 + else if (!strncmp(argv[0], + "--module-path=", strlen("--module-path="))) { + module_search_path = handle_module_path(argv[0]); + if (!strlen(module_search_path)) { + return print_help(); + } + } +#endif +#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(); + wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); + } +#endif +#if WASM_ENABLE_DEBUG_INTERP != 0 + else if (!strncmp(argv[0], "-g=", 3)) { + char *port_str = strchr(argv[0] + 3, ':'); + char *port_end; + if (port_str == NULL) + return print_help(); + *port_str = '\0'; + instance_port = strtoul(port_str + 1, &port_end, 10); + if (port_str[1] == '\0' || *port_end != '\0') + return print_help(); + ip_addr = argv[0] + 3; + } +#endif + else if (!strcmp(argv[0], "--version")) { + uint32 major, minor, patch; + wasm_runtime_get_version(&major, &minor, &patch); + printf("iwasm %" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", major, minor, + patch); + printf("\n"); + wasm_proposal_print_status(); + return 0; + } + else { +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_parse_result_t result = + libc_wasi_parse(argv[0], &wasi_parse_ctx); + switch (result) { + case LIBC_WASI_PARSE_RESULT_OK: + continue; + case LIBC_WASI_PARSE_RESULT_NEED_HELP: + return print_help(); + case LIBC_WASI_PARSE_RESULT_BAD_PARAM: + return 1; + } +#else + return print_help(); +#endif + } + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + app_argc = argc; + app_argv = 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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; +#if WASM_MEM_ALLOC_WITH_USER_DATA != 0 + /* Set user data for the allocator is needed */ + /* init_args.mem_alloc_option.allocator.user_data = user_data; */ +#endif + init_args.mem_alloc_option.allocator.malloc_func = malloc_func; + init_args.mem_alloc_option.allocator.realloc_func = realloc_func; + init_args.mem_alloc_option.allocator.free_func = free_func; +#endif + +#if WASM_ENABLE_FAST_JIT != 0 + init_args.fast_jit_code_cache_size = jit_code_cache_size; +#endif + +#if WASM_ENABLE_GC != 0 + init_args.gc_heap_size = gc_heap_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) + /* ensure that init_args.ip_addr is null terminated */ + strncpy_s(init_args.ip_addr, sizeof(init_args.ip_addr) - 1, ip_addr, + strlen(ip_addr)); +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + +#if WASM_ENABLE_AOT != 0 + if (wasm_runtime_is_xip_file(wasm_file_buf, wasm_file_size)) { + uint8 *wasm_file_mapped; + int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; + int map_flags = MMAP_MAP_32BIT; + + if (!(wasm_file_mapped = os_mmap(NULL, (uint32)wasm_file_size, map_prot, + map_flags, os_get_invalid_handle()))) { + printf("mmap memory failed\n"); + wasm_runtime_free(wasm_file_buf); + goto fail1; + } + + bh_memcpy_s(wasm_file_mapped, wasm_file_size, wasm_file_buf, + wasm_file_size); + wasm_runtime_free(wasm_file_buf); + wasm_file_buf = wasm_file_mapped; + is_xip_file = true; + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_set_module_reader(module_reader_callback, + module_destroyer_callback); +#endif + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + if (!wasm_runtime_instantiation_args_create(&inst_args)) { + printf("failed to create instantiate args\n"); + goto fail3; + } + wasm_runtime_instantiation_args_set_default_stack_size(inst_args, + stack_size); + wasm_runtime_instantiation_args_set_host_managed_heap_size(inst_args, + heap_size); +#if WASM_ENABLE_LIBC_WASI != 0 + libc_wasi_set_init_args(inst_args, argc, argv, &wasi_parse_ctx); +#endif + + /* instantiate the module */ + wasm_module_inst = wasm_runtime_instantiate_ex2( + wasm_module, inst_args, error_buf, sizeof(error_buf)); + wasm_runtime_instantiation_args_destroy(inst_args); + if (!wasm_module_inst) { + printf("%s\n", error_buf); + goto fail3; + } + +#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 + if (disable_bounds_checks) { + wasm_runtime_set_bounds_checks(wasm_module_inst, false); + } +#endif + +#if WASM_ENABLE_DEBUG_INTERP != 0 + if (ip_addr != NULL) { + wasm_exec_env_t exec_env = + wasm_runtime_get_exec_env_singleton(wasm_module_inst); + uint32_t debug_port; + if (exec_env == NULL) { + printf("%s\n", wasm_runtime_get_exception(wasm_module_inst)); + goto fail4; + } + debug_port = wasm_runtime_start_debug_instance(exec_env); + if (debug_port == 0) { + printf("Failed to start debug instance\n"); + goto fail4; + } + } +#endif + +#if WASM_ENABLE_SHARED_HEAP != 0 + if (shared_heap_size > 0) { + memset(&shared_heap_init_args, 0, sizeof(shared_heap_init_args)); + shared_heap_init_args.size = shared_heap_size; + shared_heap = wasm_runtime_create_shared_heap(&shared_heap_init_args); + + if (!shared_heap) { + printf("Create preallocated shared heap failed\n"); + goto fail6; + } + + /* attach module instance to the shared heap */ + if (!wasm_runtime_attach_shared_heap(wasm_module_inst, shared_heap)) { + printf("Attach shared heap failed.\n"); + goto fail6; + } + } +#endif + + ret = 0; + const char *exception = NULL; + if (is_repl_mode) { + app_instance_repl(wasm_module_inst); + } + else if (func_name) { + exception = app_instance_func(wasm_module_inst, func_name); + if (exception) { + /* got an exception */ + ret = 1; + } + } + else { + exception = app_instance_main(wasm_module_inst); + if (exception) { + /* got an exception */ + ret = 1; + } + } + +#if WASM_ENABLE_LIBC_WASI != 0 + if (ret == 0) { + /* propagate wasi exit code. */ + ret = wasm_runtime_get_wasi_exit_code(wasm_module_inst); + } +#endif + + if (exception) + printf("%s\n", exception); + +#if WASM_ENABLE_SHARED_HEAP != 0 +fail6: +#endif + + /* fail5: label is used by posix/main.c */ + +#if WASM_ENABLE_DEBUG_INTERP != 0 +fail4: +#endif + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + if (!is_xip_file) + wasm_runtime_free(wasm_file_buf); + else + os_munmap(wasm_file_buf, wasm_file_size); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + + return ret; +} diff --git a/product-mini/platforms/windows/wasi_filtered_tests.json b/product-mini/platforms/windows/wasi_filtered_tests.json new file mode 100644 index 0000000000..63910435c8 --- /dev/null +++ b/product-mini/platforms/windows/wasi_filtered_tests.json @@ -0,0 +1,23 @@ +{ + "WASI Rust tests": { + "poll_oneoff_stdio": "Not implemented" + }, + "WASI threads proposal": { + "wasi_threads_exit_main_wasi_read": "Blocking ops not implemented", + "wasi_threads_exit_nonmain_wasi_read": "Blocking ops not implemented", + "wasi_threads_return_main_wasi_read": "Blocking ops not implemented", + "wasi_threads_return_main_wasi": "Blocking ops not implemented", + "wasi_threads_exit_nonmain_wasi": "Blocking ops not implemented", + "wasi_threads_exit_main_wasi": "Blocking ops not implemented" + }, + "WAMR lib-socket tests": { + "nslookup": "Not implemented", + "tcp_udp": "Not implemented" + }, + "lib-wasi-threads tests": { + "nonmain_proc_exit_sleep": "poll_oneoff not implemented", + "main_proc_exit_sleep": "poll_oneoff not implemented", + "main_trap_sleep": "poll_oneoff not implemented", + "nonmain_trap_sleep": "poll_oneoff not implemented" + } +} \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-file/CMakeLists.txt b/product-mini/platforms/zephyr/simple-file/CMakeLists.txt new file mode 100644 index 0000000000..409bafa4d7 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/CMakeLists.txt @@ -0,0 +1,91 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.8.2) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(wamr) + +enable_language (ASM) + +set (WAMR_BUILD_PLATFORM "zephyr") + +# WAMR Configuration: +set (WAMR_BUILD_TARGET "THUMB") +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_FAST_INTERP 0) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_LIBC_BUILTIN 1) # printf +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_LIB_PTHREAD 0) +set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) +set (WAMR_BUILD_GLOBAL_HEAP_SIZE 131072) # 128 KB +# set (WAMR_BUILD_GLOBAL_HEAP_SIZE 65536) # 64 KB + +# Environment variables: + +# Check if WAMR_ROOT_DIR is set +if(DEFINED ENV{WAMR_ROOT_DIR}) + set(WAMR_ROOT_DIR $ENV{WAMR_ROOT_DIR}) +else() + message(FATAL_ERROR "'WAMR_ROOT_DIR' need to be specified") +endif() +message("wasi-sdk was found at ${WAMR_ROOT_DIR}") + +# Check if WASI_SDK_PATH is set +if(NOT $ENV{WASI_SDK_PATH} STREQUAL "") + set(WASI_SDK_PATH $ENV{WASI_SDK_PATH}) +else() + find_program(WASM_C_COMPILER clang /opt/wasi-sdk/bin NO_DEFAULT_PATH) + if(NOT WASM_C_COMPILER) + message(FATAL_ERROR "'wasi-sdk' not found, please ensure wasi-sdk is installed.\ + You can download and install it from\ + https://github.com/WebAssembly/wasi-sdk/releases") + else() + set(WASI_SDK_PATH /opt/wasi-sdk) + endif() +endif() +message("wasi-sdk was found at ${WASI_SDK_PATH}") + +# Check if WAMR_APP_FRAMEWORK_DIR is set +if (DEFINED ENV{WAMR_APP_FRAMEWORK_DIR}) + set(WAMR_APP_FRAMEWORK_DIR $ENV{WAMR_APP_FRAMEWORK_DIR}) +else() + message(FATAL_ERROR "'wamr-app-framework' not found, please ensure they are installed.\ + You can download and install them from\ + https://github.com/bytecodealliance/wamr-app-framework") +endif() +message("wamr-app-framework was found at ${WAMR_APP_FRAMEWORK_DIR}") + +# set the WAMR_SDK_DIR with the path specified in the environment variable +set(WAMR_SDK_DIR + ${WAMR_APP_FRAMEWORK_DIR}/wamr-sdk +) + +# set the WAMR_LIBC_BUILTIN_DIR +set(WAMR_LIBC_BUILTIN_DIR + ${WAMR_SDK_DIR}/wamr-sdk/app/libc-builtin-sysroot +) + +# set the WAMR_SDK_PACKAGE_OUT_DIR +set(WAMR_SDK_PACKAGE_OUT_DIR + ${CMAKE_CURRENT_BINARY_DIR}/wamr-sdk/app-sdk/wamr-app-framework +) + +# # Reset linker flags +# set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +# set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +# include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) # in socket-api sample + +# Build the WAMR runtime +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + src/main.c) + +# Link libraries like in samples. +set(WASI_LIBM "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libm.a") +set(WASI_LIBDL "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libdl.a") + +target_link_libraries(app PUBLIC ${WASI_LIBM} ${WASI_LIBDL}) \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-file/README.md b/product-mini/platforms/zephyr/simple-file/README.md new file mode 100644 index 0000000000..a00aecb3a1 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/README.md @@ -0,0 +1,91 @@ +# File sample +This sample demonstrates the use of WASI API to interact with the file system. + +> 🛠️ **Work in progress:** The sample is functional but be aware that just a small part of WASI File System API was tested. +> Actual Zephyr APIs: +> * directory creation = `fs_mkdir` +> * file opening/creation = `fs_open` +> * file write = `fs_write` +> * file offset = `fs_seek` +> * file read = `fs_read` +> * file close = `fs_close` +> * directory close = `fs_closedir` + +## Run Command +* **Zephyr Build** + 1. **Build:** Replace `nucleo_h743zi` with your board name and the `WAMR_BUILD_TARGET` in `CMakeList.txt` with your target architecture. + ```bash + ZEPHYR_BASE=~/zephyrproject/zephyr \ + WAMR_ROOT_DIR=~/wasm-micro-runtime \ + WASI_SDK_PATH=~/wasi-sdk-21.0 \ + WAMR_APP_FRAMEWORK_DIR=~/wamr-app-framework \ + west build . -b nucleo_h563zi -p always + ``` + ⚠️ **Warning:** The flags `ZEPHYR_BASE`, `WAMR_ROOT_DIR`, `WASI_SDK_PATH`, and `WAMR_APP_FRAMEWORK_DIR` need to be set otherwise the build will fail. + + 2. **Flash:** + ```bash + ZEPHYR_BASE=~/zephyrproject/zephyr west flash + ``` + + 3. **Monitor:** Use a serial link to monitor the output. Personally, I use minicom. + ```bash + minicom -D /dev/ttyACM0 + ``` + + 4. **Debug:** Curently investigating. + +* **WebAssembly Module** + + ❗ **Important:** I used wasi-sdk 21 to compile the module. I still haven't tried the module with the new wasi-sdk 22. + + 1. **Compile:** in the `wasm-apps` folder. + ```bash + ~/wasi-sdk-21.0/bin/clang --sysroot=/home/user/wasi-sdk-21.0/share/wasi-sysroot -nodefaultlibs -lc -o file.wasm file.c -z stack-size=8192 -Wl,--initial-memory=65536 -Wl,--export=__heap_base -Wl,--export=__data_end + ``` + 2. **generate a C header:** Use `xxd` or other tool, I also put simple python script. At application root `simple-file/`. + ```bash + python3 to_c_header.py + ``` + Be free to modify the script to fit your needs. + +## Output +The output should be similar to the following: +```bash +*** Booting Zephyr OS build v3.6.0-4305-g2ec8f442a505 *** +Area 3 at 0x1f0000 on flash-controller@40022000 for 65536 bytes +[00:00:00.067,000] littlefs: LittleFS version 2.8, disk version 2.1 +[00:00:00.074,000] littlefs: FS at flash-controller@40022000:0x1f0000 is 8 0x2000-byte blocks with 512 cycle +[00:00:00.085,000] littlefs: sizes: rd 16 ; pr 16 ; ca 64 ; la 32 +[00:00:00.092,000] littlefs: WEST_TOPDIR/modules/fs/littlefs/lfs.c:1351: Corrupted dir pair at {0x0, 0x1} +[00:00:00.103,000] littlefs: can't mount (LFS -84); formatting +[00:00:00.114,000] littlefs: /lfs mounted +/lfs mount: 0 +[00:00:00.120,000] main: stdin = 0 +[00:00:00.124,000] main: stdout = 1 +[00:00:00.128,000] main: stderr = 2 +[00:00:00.133,000] main: global heap size: 131072 +[00:00:00.142,000] main: Wasm file size: 34682 +[00:00:00:000 - 2000AFE0]: WASI context initialization: START + +[OS] os_rwlock_init +[OS] os_rwlock_init +[00:00:00:000 - 2000AFE0]: WASI context initialization: END + +[00:00:00.190,000] main: main found +Hello WebAssembly Module ! + +mkdir returned 0 +fopen Succeed +fwrite returned 13 +fseek returned 0 +fread returned 13 +buffer read = Hello, World! + +[00:00:00.225,000] main: main executed +[00:00:00.230,000] main: wasi exit code: 0 +[00:00:00.239,000] main: elapsed: 178ms +[00:00:03.158,000] phy_mii: PHY (0) Link speed 100 Mb, full duplex + +[00:00:00.051,000] phy_mii: PHY (0) ID 7C131 +``` \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-file/prj.conf b/product-mini/platforms/zephyr/simple-file/prj.conf new file mode 100644 index 0000000000..ca9d2fc373 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/prj.conf @@ -0,0 +1,40 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Log config +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_MODE_IMMEDIATE=y +# CONFIG_LOG_MODE_DEFERRED=y + +CONFIG_MAIN_STACK_SIZE=8192 +# CONFIG_HEAP_MEM_POOL_SIZE=32768 +CONFIG_REQUIRES_FULL_LIBC=y + +# Config File System +CONFIG_POSIX_API=n +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_LITTLEFS=y +# CONFIG_FS_LITTLEFS_BLK_DEV=y + +# Temp Build Network stack to compile. +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y + + +# Random generator +CONFIG_TEST_RANDOM_GENERATOR=y + +# Stack conf +CONFIG_STACK_SENTINEL=y +CONFIG_HW_STACK_PROTECTION=y + +# Flash +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# Debug +CONFIG_DEBUG=y \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-file/src/file.h b/product-mini/platforms/zephyr/simple-file/src/file.h new file mode 100644 index 0000000000..eba5266809 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/src/file.h @@ -0,0 +1,2898 @@ +/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +unsigned char __aligned(4) wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0x0f, 0x60, + 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7e, 0x7f, 0x01, + 0x7e, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, + 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7e, + 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x09, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, + 0x7e, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00, + 0x60, 0x05, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x01, + 0x7f, 0x60, 0x08, 0x7f, 0x7f, 0x7f, 0x7f, 0x7e, 0x7e, 0x7f, 0x7f, 0x01, + 0x7f, 0x60, 0x02, 0x7c, 0x7f, 0x01, 0x7c, 0x60, 0x03, 0x7f, 0x7f, 0x7f, + 0x00, 0x60, 0x05, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x02, 0xef, 0x03, + 0x0d, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, + 0x08, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x03, 0x16, + 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x0e, 0x61, + 0x72, 0x67, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x5f, 0x67, 0x65, + 0x74, 0x00, 0x03, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, + 0x77, 0x31, 0x08, 0x66, 0x64, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x00, + 0x02, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, + 0x0d, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x67, + 0x65, 0x74, 0x00, 0x03, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x31, 0x13, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, 0x61, + 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x00, + 0x03, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, + 0x0e, 0x66, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x74, 0x61, 0x74, 0x5f, + 0x67, 0x65, 0x74, 0x00, 0x03, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x13, 0x66, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x00, 0x00, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x31, 0x07, 0x66, 0x64, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x00, 0x04, 0x16, + 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x07, 0x66, + 0x64, 0x5f, 0x73, 0x65, 0x65, 0x6b, 0x00, 0x05, 0x16, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, 0x5f, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x00, 0x04, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x31, 0x15, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x79, 0x00, 0x00, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x09, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x6f, 0x70, + 0x65, 0x6e, 0x00, 0x06, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x31, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69, + 0x74, 0x00, 0x07, 0x03, 0x57, 0x56, 0x08, 0x09, 0x08, 0x02, 0x02, 0x07, + 0x07, 0x03, 0x07, 0x0a, 0x03, 0x03, 0x02, 0x03, 0x03, 0x03, 0x00, 0x04, + 0x05, 0x04, 0x03, 0x0b, 0x07, 0x08, 0x02, 0x08, 0x08, 0x00, 0x00, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01, 0x0a, 0x08, 0x08, 0x02, + 0x00, 0x04, 0x03, 0x03, 0x02, 0x00, 0x03, 0x00, 0x03, 0x0c, 0x03, 0x00, + 0x09, 0x0d, 0x0e, 0x08, 0x03, 0x00, 0x02, 0x08, 0x03, 0x04, 0x00, 0x00, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, + 0x03, 0x00, 0x02, 0x04, 0x02, 0x07, 0x02, 0x03, 0x04, 0x05, 0x01, 0x70, + 0x01, 0x06, 0x06, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x1a, 0x04, 0x7f, + 0x01, 0x41, 0x80, 0xeb, 0x00, 0x0b, 0x7f, 0x00, 0x41, 0x00, 0x0b, 0x7f, + 0x00, 0x41, 0x80, 0xeb, 0x00, 0x0b, 0x7f, 0x00, 0x41, 0xfc, 0x2a, 0x0b, + 0x07, 0x2e, 0x04, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, + 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x0f, 0x0b, 0x5f, 0x5f, + 0x68, 0x65, 0x61, 0x70, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x0a, + 0x5f, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x03, 0x03, + 0x09, 0x0b, 0x01, 0x00, 0x41, 0x01, 0x0b, 0x05, 0x2e, 0x2c, 0x30, 0x32, + 0x58, 0x0a, 0xe0, 0xe2, 0x01, 0x56, 0x02, 0x00, 0x0b, 0x03, 0x00, 0x00, + 0x0b, 0x52, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x23, 0x81, 0x80, + 0x80, 0x80, 0x00, 0x41, 0xb0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, + 0x00, 0x0d, 0x00, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0xb0, 0x9e, + 0x80, 0x80, 0x00, 0x6a, 0x41, 0x01, 0x36, 0x02, 0x00, 0x10, 0x8d, 0x80, + 0x80, 0x80, 0x00, 0x10, 0x96, 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x10, + 0xa7, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, 0x0d, 0x01, 0x0f, 0x0b, 0x00, + 0x00, 0x0b, 0x20, 0x00, 0x10, 0xa3, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, + 0x0a, 0x00, 0x20, 0x00, 0x10, 0x91, 0x80, 0x80, 0x80, 0x00, 0x0b, 0xab, + 0x32, 0x01, 0x0b, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, + 0x6b, 0x22, 0x01, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xd0, + 0x9e, 0x80, 0x80, 0x00, 0x22, 0x02, 0x0d, 0x00, 0x02, 0x40, 0x41, 0x00, + 0x28, 0x02, 0x90, 0xa2, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0d, 0x00, 0x41, + 0x00, 0x42, 0x7f, 0x37, 0x02, 0x9c, 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x42, 0x80, 0x80, 0x84, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x37, 0x02, 0x94, + 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x41, + 0x70, 0x71, 0x41, 0xd8, 0xaa, 0xd5, 0xaa, 0x05, 0x73, 0x22, 0x03, 0x36, + 0x02, 0x90, 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, + 0xa4, 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xf4, + 0xa1, 0x80, 0x80, 0x00, 0x0b, 0x41, 0x80, 0x80, 0x84, 0x80, 0x00, 0x41, + 0x80, 0xeb, 0x80, 0x80, 0x00, 0x49, 0x0d, 0x01, 0x41, 0x00, 0x21, 0x02, + 0x41, 0x80, 0x80, 0x84, 0x80, 0x00, 0x41, 0x80, 0xeb, 0x80, 0x80, 0x00, + 0x6b, 0x41, 0xd9, 0x00, 0x49, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x04, 0x41, + 0x00, 0x41, 0x80, 0xeb, 0x80, 0x80, 0x00, 0x36, 0x02, 0xf8, 0xa1, 0x80, + 0x80, 0x00, 0x41, 0x00, 0x41, 0x80, 0xeb, 0x80, 0x80, 0x00, 0x36, 0x02, + 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xdc, + 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x7f, 0x36, 0x02, 0xd8, 0x9e, + 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x80, 0x80, 0x84, 0x80, 0x00, 0x41, + 0x80, 0xeb, 0x80, 0x80, 0x00, 0x6b, 0x36, 0x02, 0xfc, 0xa1, 0x80, 0x80, + 0x00, 0x03, 0x40, 0x20, 0x04, 0x41, 0xf4, 0x9e, 0x80, 0x80, 0x00, 0x6a, + 0x20, 0x04, 0x41, 0xe8, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x03, 0x36, + 0x02, 0x00, 0x20, 0x03, 0x20, 0x04, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, + 0x6a, 0x22, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0xec, 0x9e, 0x80, + 0x80, 0x00, 0x6a, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0xfc, + 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, 0xf0, 0x9e, 0x80, 0x80, + 0x00, 0x6a, 0x22, 0x05, 0x36, 0x02, 0x00, 0x20, 0x05, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x20, 0x04, 0x41, 0x84, 0x9f, 0x80, 0x80, 0x00, 0x6a, 0x20, + 0x04, 0x41, 0xf8, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x03, 0x36, 0x02, + 0x00, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x80, + 0x9f, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x04, + 0x41, 0x20, 0x6a, 0x22, 0x04, 0x41, 0x80, 0x02, 0x47, 0x0d, 0x00, 0x0b, + 0x41, 0x80, 0xeb, 0x80, 0x80, 0x00, 0x41, 0x78, 0x41, 0x80, 0xeb, 0x80, + 0x80, 0x00, 0x6b, 0x41, 0x0f, 0x71, 0x22, 0x04, 0x6a, 0x22, 0x02, 0x41, + 0x04, 0x6a, 0x41, 0x80, 0x80, 0x84, 0x80, 0x00, 0x41, 0x80, 0xeb, 0x80, + 0x80, 0x00, 0x6b, 0x41, 0x48, 0x6a, 0x22, 0x03, 0x20, 0x04, 0x6b, 0x22, + 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, + 0x02, 0xa0, 0xa2, 0x80, 0x80, 0x00, 0x36, 0x02, 0xd4, 0x9e, 0x80, 0x80, + 0x00, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0xc4, 0x9e, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xd0, 0x9e, 0x80, 0x80, 0x00, 0x20, + 0x03, 0x41, 0x80, 0xeb, 0x80, 0x80, 0x00, 0x6a, 0x41, 0x04, 0x6a, 0x41, + 0x38, 0x36, 0x02, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, + 0xec, 0x01, 0x4b, 0x0d, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xb8, + 0x9e, 0x80, 0x80, 0x00, 0x22, 0x06, 0x41, 0x10, 0x20, 0x00, 0x41, 0x13, + 0x6a, 0x41, 0x70, 0x71, 0x20, 0x00, 0x41, 0x0b, 0x49, 0x1b, 0x22, 0x07, + 0x41, 0x03, 0x76, 0x22, 0x03, 0x76, 0x22, 0x04, 0x41, 0x03, 0x71, 0x45, + 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x01, 0x71, 0x20, + 0x03, 0x72, 0x41, 0x01, 0x73, 0x22, 0x05, 0x41, 0x03, 0x74, 0x22, 0x03, + 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x04, 0x20, 0x03, 0x41, + 0xe8, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, 0x28, + 0x02, 0x08, 0x22, 0x07, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x06, 0x41, + 0x7e, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, + 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, + 0x20, 0x04, 0x36, 0x02, 0x0c, 0x0b, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x21, + 0x04, 0x20, 0x03, 0x20, 0x05, 0x41, 0x03, 0x74, 0x22, 0x05, 0x41, 0x03, + 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x05, 0x6a, 0x22, 0x03, 0x20, + 0x03, 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0c, 0x12, + 0x0b, 0x20, 0x07, 0x41, 0x00, 0x28, 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, + 0x22, 0x08, 0x4d, 0x0d, 0x01, 0x02, 0x40, 0x20, 0x04, 0x45, 0x0d, 0x00, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x20, 0x03, 0x74, 0x41, 0x02, 0x20, + 0x03, 0x74, 0x22, 0x04, 0x41, 0x00, 0x20, 0x04, 0x6b, 0x72, 0x71, 0x68, + 0x22, 0x03, 0x41, 0x03, 0x74, 0x22, 0x04, 0x41, 0xe0, 0x9e, 0x80, 0x80, + 0x00, 0x6a, 0x22, 0x05, 0x20, 0x04, 0x41, 0xe8, 0x9e, 0x80, 0x80, 0x00, + 0x6a, 0x28, 0x02, 0x00, 0x22, 0x04, 0x28, 0x02, 0x08, 0x22, 0x00, 0x47, + 0x0d, 0x00, 0x41, 0x00, 0x20, 0x06, 0x41, 0x7e, 0x20, 0x03, 0x77, 0x71, + 0x22, 0x06, 0x36, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, + 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x05, 0x36, + 0x02, 0x0c, 0x0b, 0x20, 0x04, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, + 0x04, 0x20, 0x04, 0x20, 0x03, 0x41, 0x03, 0x74, 0x22, 0x03, 0x6a, 0x20, + 0x03, 0x20, 0x07, 0x6b, 0x22, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, + 0x07, 0x6a, 0x22, 0x00, 0x20, 0x05, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, + 0x02, 0x40, 0x20, 0x08, 0x45, 0x0d, 0x00, 0x20, 0x08, 0x41, 0x78, 0x71, + 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x07, 0x41, 0x00, 0x28, + 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x06, 0x41, 0x01, 0x20, 0x08, 0x41, 0x03, 0x76, 0x74, 0x22, 0x09, + 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x06, 0x20, 0x09, 0x72, 0x36, 0x02, + 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x07, 0x21, 0x09, 0x0c, 0x01, 0x0b, + 0x20, 0x07, 0x28, 0x02, 0x08, 0x21, 0x09, 0x0b, 0x20, 0x09, 0x20, 0x03, + 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x03, + 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x09, 0x36, 0x02, 0x08, + 0x0b, 0x20, 0x04, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x41, 0x00, 0x20, 0x00, + 0x36, 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, + 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x12, 0x0b, 0x41, 0x00, 0x28, + 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x0a, 0x45, 0x0d, 0x01, 0x20, + 0x0a, 0x68, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x00, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, + 0x07, 0x6b, 0x21, 0x03, 0x20, 0x00, 0x21, 0x05, 0x02, 0x40, 0x03, 0x40, + 0x02, 0x40, 0x20, 0x05, 0x28, 0x02, 0x10, 0x22, 0x04, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x02, + 0x0b, 0x20, 0x04, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x07, 0x6b, + 0x22, 0x05, 0x20, 0x03, 0x20, 0x05, 0x20, 0x03, 0x49, 0x22, 0x05, 0x1b, + 0x21, 0x03, 0x20, 0x04, 0x20, 0x00, 0x20, 0x05, 0x1b, 0x21, 0x00, 0x20, + 0x04, 0x21, 0x05, 0x0c, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x18, + 0x21, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x0c, 0x22, 0x09, 0x20, + 0x00, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x04, 0x41, + 0x00, 0x28, 0x02, 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x49, 0x1a, 0x20, 0x09, + 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x09, 0x36, 0x02, 0x0c, + 0x0c, 0x11, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x22, 0x05, + 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x10, + 0x22, 0x04, 0x45, 0x0d, 0x04, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x21, 0x05, + 0x0b, 0x03, 0x40, 0x20, 0x05, 0x21, 0x02, 0x20, 0x04, 0x22, 0x09, 0x41, + 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x00, 0x20, + 0x09, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x20, 0x09, 0x28, 0x02, 0x10, 0x22, + 0x04, 0x0d, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, + 0x10, 0x0b, 0x41, 0x7f, 0x21, 0x07, 0x20, 0x00, 0x41, 0xbf, 0x7f, 0x4b, + 0x0d, 0x00, 0x20, 0x00, 0x41, 0x13, 0x6a, 0x22, 0x04, 0x41, 0x70, 0x71, + 0x21, 0x07, 0x41, 0x00, 0x28, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x22, + 0x0b, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x08, 0x02, 0x40, 0x20, 0x07, + 0x41, 0x80, 0x02, 0x49, 0x0d, 0x00, 0x41, 0x1f, 0x21, 0x08, 0x20, 0x07, + 0x41, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x07, 0x41, 0x26, + 0x20, 0x04, 0x41, 0x08, 0x76, 0x67, 0x22, 0x04, 0x6b, 0x76, 0x41, 0x01, + 0x71, 0x20, 0x04, 0x41, 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, 0x21, 0x08, + 0x0b, 0x41, 0x00, 0x20, 0x07, 0x6b, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x08, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, + 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x05, 0x0d, 0x00, 0x41, + 0x00, 0x21, 0x04, 0x41, 0x00, 0x21, 0x09, 0x0c, 0x01, 0x0b, 0x41, 0x00, + 0x21, 0x04, 0x20, 0x07, 0x41, 0x00, 0x41, 0x19, 0x20, 0x08, 0x41, 0x01, + 0x76, 0x6b, 0x20, 0x08, 0x41, 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x00, 0x41, + 0x00, 0x21, 0x09, 0x03, 0x40, 0x02, 0x40, 0x20, 0x05, 0x28, 0x02, 0x04, + 0x41, 0x78, 0x71, 0x20, 0x07, 0x6b, 0x22, 0x06, 0x20, 0x03, 0x4f, 0x0d, + 0x00, 0x20, 0x06, 0x21, 0x03, 0x20, 0x05, 0x21, 0x09, 0x20, 0x06, 0x0d, + 0x00, 0x41, 0x00, 0x21, 0x03, 0x20, 0x05, 0x21, 0x09, 0x20, 0x05, 0x21, + 0x04, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x41, 0x14, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x06, 0x20, 0x06, 0x20, 0x05, 0x20, 0x00, 0x41, 0x1d, + 0x76, 0x41, 0x04, 0x71, 0x6a, 0x41, 0x10, 0x6a, 0x28, 0x02, 0x00, 0x22, + 0x05, 0x46, 0x1b, 0x20, 0x04, 0x20, 0x06, 0x1b, 0x21, 0x04, 0x20, 0x00, + 0x41, 0x01, 0x74, 0x21, 0x00, 0x20, 0x05, 0x0d, 0x00, 0x0b, 0x0b, 0x02, + 0x40, 0x20, 0x04, 0x20, 0x09, 0x72, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x09, + 0x41, 0x02, 0x20, 0x08, 0x74, 0x22, 0x04, 0x41, 0x00, 0x20, 0x04, 0x6b, + 0x72, 0x20, 0x0b, 0x71, 0x22, 0x04, 0x45, 0x0d, 0x03, 0x20, 0x04, 0x68, + 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, + 0x00, 0x21, 0x04, 0x0b, 0x20, 0x04, 0x45, 0x0d, 0x01, 0x0b, 0x03, 0x40, + 0x20, 0x04, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x07, 0x6b, 0x22, + 0x06, 0x20, 0x03, 0x49, 0x21, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, + 0x10, 0x22, 0x05, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x14, 0x6a, 0x28, 0x02, + 0x00, 0x21, 0x05, 0x0b, 0x20, 0x06, 0x20, 0x03, 0x20, 0x00, 0x1b, 0x21, + 0x03, 0x20, 0x04, 0x20, 0x09, 0x20, 0x00, 0x1b, 0x21, 0x09, 0x20, 0x05, + 0x21, 0x04, 0x20, 0x05, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x09, 0x45, 0x0d, + 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, + 0x20, 0x07, 0x6b, 0x4f, 0x0d, 0x00, 0x20, 0x09, 0x28, 0x02, 0x18, 0x21, + 0x02, 0x02, 0x40, 0x20, 0x09, 0x28, 0x02, 0x0c, 0x22, 0x00, 0x20, 0x09, + 0x46, 0x0d, 0x00, 0x20, 0x09, 0x28, 0x02, 0x08, 0x22, 0x04, 0x41, 0x00, + 0x28, 0x02, 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x49, 0x1a, 0x20, 0x00, 0x20, + 0x04, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x0c, + 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x09, 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, + 0x02, 0x00, 0x22, 0x04, 0x0d, 0x00, 0x20, 0x09, 0x28, 0x02, 0x10, 0x22, + 0x04, 0x45, 0x0d, 0x04, 0x20, 0x09, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x0b, + 0x03, 0x40, 0x20, 0x05, 0x21, 0x06, 0x20, 0x04, 0x22, 0x00, 0x41, 0x14, + 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x00, 0x20, 0x00, + 0x41, 0x10, 0x6a, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x04, + 0x0d, 0x00, 0x0b, 0x20, 0x06, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x0e, + 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, + 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xcc, + 0x9e, 0x80, 0x80, 0x00, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, + 0x20, 0x07, 0x6b, 0x22, 0x05, 0x41, 0x10, 0x49, 0x0d, 0x00, 0x20, 0x03, + 0x20, 0x07, 0x6a, 0x22, 0x00, 0x20, 0x05, 0x41, 0x01, 0x72, 0x36, 0x02, + 0x04, 0x20, 0x03, 0x20, 0x04, 0x6a, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, + 0x03, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, + 0x20, 0x03, 0x20, 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, + 0x20, 0x04, 0x6a, 0x22, 0x04, 0x20, 0x04, 0x28, 0x02, 0x04, 0x41, 0x01, + 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, 0x21, 0x00, 0x41, 0x00, 0x21, 0x05, + 0x0b, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x20, + 0x03, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0c, 0x10, 0x0b, 0x02, 0x40, 0x41, + 0x00, 0x28, 0x02, 0xc4, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x05, 0x20, 0x07, + 0x4d, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x07, 0x6a, 0x22, 0x04, 0x20, 0x05, + 0x20, 0x07, 0x6b, 0x22, 0x03, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, + 0x00, 0x20, 0x04, 0x36, 0x02, 0xd0, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x20, 0x03, 0x36, 0x02, 0xc4, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x02, 0x20, + 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, 0x08, 0x6a, + 0x21, 0x04, 0x0c, 0x10, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0x90, 0xa2, 0x80, 0x80, 0x00, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x28, + 0x02, 0x98, 0xa2, 0x80, 0x80, 0x00, 0x21, 0x03, 0x0c, 0x01, 0x0b, 0x41, + 0x00, 0x42, 0x7f, 0x37, 0x02, 0x9c, 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x42, 0x80, 0x80, 0x84, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x37, 0x02, 0x94, + 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x01, 0x41, 0x0c, 0x6a, 0x41, + 0x70, 0x71, 0x41, 0xd8, 0xaa, 0xd5, 0xaa, 0x05, 0x73, 0x36, 0x02, 0x90, + 0xa2, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xa4, 0xa2, + 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xf4, 0xa1, 0x80, + 0x80, 0x00, 0x41, 0x80, 0x80, 0x04, 0x21, 0x03, 0x0b, 0x41, 0x00, 0x21, + 0x04, 0x02, 0x40, 0x20, 0x03, 0x20, 0x07, 0x41, 0xc7, 0x00, 0x6a, 0x22, + 0x08, 0x6a, 0x22, 0x00, 0x41, 0x00, 0x20, 0x03, 0x6b, 0x22, 0x06, 0x71, + 0x22, 0x09, 0x20, 0x07, 0x4b, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x30, 0x36, + 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x10, 0x0b, 0x02, 0x40, 0x41, + 0x00, 0x28, 0x02, 0xf0, 0xa1, 0x80, 0x80, 0x00, 0x22, 0x04, 0x45, 0x0d, + 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xe8, 0xa1, 0x80, 0x80, 0x00, + 0x22, 0x03, 0x20, 0x09, 0x6a, 0x22, 0x0b, 0x20, 0x03, 0x4d, 0x0d, 0x00, + 0x20, 0x0b, 0x20, 0x04, 0x4d, 0x0d, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x04, + 0x41, 0x00, 0x41, 0x30, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, + 0x10, 0x0b, 0x41, 0x00, 0x2d, 0x00, 0xf4, 0xa1, 0x80, 0x80, 0x00, 0x41, + 0x04, 0x71, 0x0d, 0x05, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, + 0x45, 0x0d, 0x00, 0x41, 0xf8, 0xa1, 0x80, 0x80, 0x00, 0x21, 0x04, 0x03, + 0x40, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x00, 0x22, 0x03, 0x20, 0x02, + 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x04, 0x28, 0x02, 0x04, 0x6a, 0x20, + 0x02, 0x4b, 0x0d, 0x03, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x22, 0x04, + 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x00, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, + 0x22, 0x00, 0x41, 0x7f, 0x46, 0x0d, 0x06, 0x20, 0x09, 0x21, 0x06, 0x02, + 0x40, 0x41, 0x00, 0x28, 0x02, 0x94, 0xa2, 0x80, 0x80, 0x00, 0x22, 0x04, + 0x41, 0x7f, 0x6a, 0x22, 0x03, 0x20, 0x00, 0x71, 0x45, 0x0d, 0x00, 0x20, + 0x09, 0x20, 0x00, 0x6b, 0x20, 0x03, 0x20, 0x00, 0x6a, 0x41, 0x00, 0x20, + 0x04, 0x6b, 0x71, 0x6a, 0x21, 0x06, 0x0b, 0x20, 0x06, 0x20, 0x07, 0x4d, + 0x0d, 0x06, 0x20, 0x06, 0x41, 0xfe, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, + 0x06, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xf0, 0xa1, 0x80, 0x80, 0x00, + 0x22, 0x04, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xe8, 0xa1, 0x80, + 0x80, 0x00, 0x22, 0x03, 0x20, 0x06, 0x6a, 0x22, 0x05, 0x20, 0x03, 0x4d, + 0x0d, 0x07, 0x20, 0x05, 0x20, 0x04, 0x4b, 0x0d, 0x07, 0x0b, 0x20, 0x06, + 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x22, 0x04, 0x20, 0x00, 0x47, 0x0d, + 0x01, 0x0c, 0x08, 0x0b, 0x20, 0x00, 0x20, 0x05, 0x6b, 0x20, 0x06, 0x71, + 0x22, 0x06, 0x41, 0xfe, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x05, 0x20, + 0x06, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x20, 0x04, 0x28, + 0x02, 0x00, 0x20, 0x04, 0x28, 0x02, 0x04, 0x6a, 0x46, 0x0d, 0x04, 0x20, + 0x00, 0x21, 0x04, 0x0b, 0x02, 0x40, 0x20, 0x06, 0x20, 0x07, 0x41, 0xc8, + 0x00, 0x6a, 0x4f, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x7f, 0x46, 0x0d, 0x00, + 0x02, 0x40, 0x20, 0x08, 0x20, 0x06, 0x6b, 0x41, 0x00, 0x28, 0x02, 0x98, + 0xa2, 0x80, 0x80, 0x00, 0x22, 0x03, 0x6a, 0x41, 0x00, 0x20, 0x03, 0x6b, + 0x71, 0x22, 0x03, 0x41, 0xfe, 0xff, 0xff, 0xff, 0x07, 0x4d, 0x0d, 0x00, + 0x20, 0x04, 0x21, 0x00, 0x0c, 0x08, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x10, + 0xa5, 0x80, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x46, 0x0d, 0x00, 0x20, 0x03, + 0x20, 0x06, 0x6a, 0x21, 0x06, 0x20, 0x04, 0x21, 0x00, 0x0c, 0x08, 0x0b, + 0x41, 0x00, 0x20, 0x06, 0x6b, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0c, 0x05, 0x0b, 0x20, 0x04, 0x21, 0x00, 0x20, 0x04, 0x41, 0x7f, 0x47, + 0x0d, 0x06, 0x0c, 0x04, 0x0b, 0x00, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x09, + 0x0c, 0x0c, 0x0b, 0x41, 0x00, 0x21, 0x00, 0x0c, 0x0a, 0x0b, 0x20, 0x00, + 0x41, 0x7f, 0x47, 0x0d, 0x02, 0x0b, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, + 0xf4, 0xa1, 0x80, 0x80, 0x00, 0x41, 0x04, 0x72, 0x36, 0x02, 0xf4, 0xa1, + 0x80, 0x80, 0x00, 0x0b, 0x20, 0x09, 0x41, 0xfe, 0xff, 0xff, 0xff, 0x07, + 0x4b, 0x0d, 0x01, 0x20, 0x09, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x00, 0x41, 0x00, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x04, 0x20, + 0x00, 0x41, 0x7f, 0x46, 0x0d, 0x01, 0x20, 0x04, 0x41, 0x7f, 0x46, 0x0d, + 0x01, 0x20, 0x00, 0x20, 0x04, 0x4f, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x00, + 0x6b, 0x22, 0x06, 0x20, 0x07, 0x41, 0x38, 0x6a, 0x4d, 0x0d, 0x01, 0x0b, + 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xe8, 0xa1, 0x80, 0x80, 0x00, 0x20, + 0x06, 0x6a, 0x22, 0x04, 0x36, 0x02, 0xe8, 0xa1, 0x80, 0x80, 0x00, 0x02, + 0x40, 0x20, 0x04, 0x41, 0x00, 0x28, 0x02, 0xec, 0xa1, 0x80, 0x80, 0x00, + 0x4d, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0xec, 0xa1, 0x80, + 0x80, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x00, 0x28, 0x02, 0xd0, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x03, 0x45, 0x0d, + 0x00, 0x41, 0xf8, 0xa1, 0x80, 0x80, 0x00, 0x21, 0x04, 0x03, 0x40, 0x20, + 0x00, 0x20, 0x04, 0x28, 0x02, 0x00, 0x22, 0x05, 0x20, 0x04, 0x28, 0x02, + 0x04, 0x22, 0x09, 0x6a, 0x46, 0x0d, 0x02, 0x20, 0x04, 0x28, 0x02, 0x08, + 0x22, 0x04, 0x0d, 0x00, 0x0c, 0x03, 0x0b, 0x0b, 0x02, 0x40, 0x02, 0x40, + 0x41, 0x00, 0x28, 0x02, 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x04, 0x45, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x04, 0x4f, 0x0d, 0x01, 0x0b, 0x41, 0x00, + 0x20, 0x00, 0x36, 0x02, 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x0b, 0x41, 0x00, + 0x21, 0x04, 0x41, 0x00, 0x20, 0x06, 0x36, 0x02, 0xfc, 0xa1, 0x80, 0x80, + 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xf8, 0xa1, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x41, 0x7f, 0x36, 0x02, 0xd8, 0x9e, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x41, 0x00, 0x28, 0x02, 0x90, 0xa2, 0x80, 0x80, 0x00, 0x36, 0x02, + 0xdc, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0x84, + 0xa2, 0x80, 0x80, 0x00, 0x03, 0x40, 0x20, 0x04, 0x41, 0xf4, 0x9e, 0x80, + 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, 0xe8, 0x9e, 0x80, 0x80, 0x00, 0x6a, + 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x04, 0x41, 0xe0, 0x9e, + 0x80, 0x80, 0x00, 0x6a, 0x22, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, + 0xec, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, + 0x04, 0x41, 0xfc, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, 0xf0, + 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x05, 0x36, 0x02, 0x00, 0x20, 0x05, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x84, 0x9f, 0x80, 0x80, + 0x00, 0x6a, 0x20, 0x04, 0x41, 0xf8, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, + 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, + 0x04, 0x41, 0x80, 0x9f, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x20, 0x04, 0x41, 0x20, 0x6a, 0x22, 0x04, 0x41, 0x80, 0x02, 0x47, + 0x0d, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x78, 0x20, 0x00, 0x6b, 0x41, 0x0f, + 0x71, 0x22, 0x04, 0x6a, 0x22, 0x03, 0x20, 0x06, 0x41, 0x48, 0x6a, 0x22, + 0x05, 0x20, 0x04, 0x6b, 0x22, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, + 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xa0, 0xa2, 0x80, 0x80, 0x00, 0x36, + 0x02, 0xd4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, + 0xc4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xd0, + 0x9e, 0x80, 0x80, 0x00, 0x20, 0x00, 0x20, 0x05, 0x6a, 0x41, 0x38, 0x36, + 0x02, 0x04, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x4f, 0x0d, 0x00, + 0x20, 0x03, 0x20, 0x05, 0x49, 0x0d, 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, + 0x41, 0x08, 0x71, 0x0d, 0x00, 0x20, 0x03, 0x41, 0x78, 0x20, 0x03, 0x6b, + 0x41, 0x0f, 0x71, 0x22, 0x05, 0x6a, 0x22, 0x00, 0x41, 0x00, 0x28, 0x02, + 0xc4, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x06, 0x6a, 0x22, 0x02, 0x20, 0x05, + 0x6b, 0x22, 0x05, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x04, 0x20, + 0x09, 0x20, 0x06, 0x6a, 0x36, 0x02, 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, + 0x02, 0xa0, 0xa2, 0x80, 0x80, 0x00, 0x36, 0x02, 0xd4, 0x9e, 0x80, 0x80, + 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xc4, 0x9e, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xd0, 0x9e, 0x80, 0x80, 0x00, 0x20, + 0x03, 0x20, 0x02, 0x6a, 0x41, 0x38, 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, + 0x02, 0x40, 0x20, 0x00, 0x41, 0x00, 0x28, 0x02, 0xc8, 0x9e, 0x80, 0x80, + 0x00, 0x22, 0x09, 0x4f, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, + 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x00, 0x21, 0x09, 0x0b, 0x20, 0x00, + 0x20, 0x06, 0x6a, 0x21, 0x05, 0x41, 0xf8, 0xa1, 0x80, 0x80, 0x00, 0x21, + 0x04, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x03, 0x40, 0x20, + 0x04, 0x28, 0x02, 0x00, 0x20, 0x05, 0x46, 0x0d, 0x01, 0x20, 0x04, 0x28, + 0x02, 0x08, 0x22, 0x04, 0x0d, 0x00, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x04, + 0x2d, 0x00, 0x0c, 0x41, 0x08, 0x71, 0x45, 0x0d, 0x01, 0x0b, 0x41, 0xf8, + 0xa1, 0x80, 0x80, 0x00, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, + 0x28, 0x02, 0x00, 0x22, 0x05, 0x20, 0x03, 0x4b, 0x0d, 0x00, 0x20, 0x05, + 0x20, 0x04, 0x28, 0x02, 0x04, 0x6a, 0x22, 0x05, 0x20, 0x03, 0x4b, 0x0d, + 0x03, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x04, 0x0c, 0x00, 0x0b, + 0x0b, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x04, + 0x28, 0x02, 0x04, 0x20, 0x06, 0x6a, 0x36, 0x02, 0x04, 0x20, 0x00, 0x41, + 0x78, 0x20, 0x00, 0x6b, 0x41, 0x0f, 0x71, 0x6a, 0x22, 0x02, 0x20, 0x07, + 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x05, 0x41, 0x78, 0x20, 0x05, + 0x6b, 0x41, 0x0f, 0x71, 0x6a, 0x22, 0x06, 0x20, 0x02, 0x20, 0x07, 0x6a, + 0x22, 0x07, 0x6b, 0x21, 0x04, 0x02, 0x40, 0x20, 0x06, 0x20, 0x03, 0x47, + 0x0d, 0x00, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xd0, 0x9e, 0x80, 0x80, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xc4, 0x9e, 0x80, 0x80, 0x00, + 0x20, 0x04, 0x6a, 0x22, 0x04, 0x36, 0x02, 0xc4, 0x9e, 0x80, 0x80, 0x00, + 0x20, 0x07, 0x20, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0c, 0x08, + 0x0b, 0x02, 0x40, 0x20, 0x06, 0x41, 0x00, 0x28, 0x02, 0xcc, 0x9e, 0x80, + 0x80, 0x00, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x07, 0x36, 0x02, 0xcc, + 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xc0, 0x9e, + 0x80, 0x80, 0x00, 0x20, 0x04, 0x6a, 0x22, 0x04, 0x36, 0x02, 0xc0, 0x9e, + 0x80, 0x80, 0x00, 0x20, 0x07, 0x20, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, + 0x04, 0x20, 0x07, 0x20, 0x04, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x0c, + 0x08, 0x0b, 0x20, 0x06, 0x28, 0x02, 0x04, 0x22, 0x03, 0x41, 0x03, 0x71, + 0x41, 0x01, 0x47, 0x0d, 0x06, 0x20, 0x03, 0x41, 0x78, 0x71, 0x21, 0x08, + 0x02, 0x40, 0x20, 0x03, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x06, + 0x28, 0x02, 0x08, 0x22, 0x05, 0x20, 0x03, 0x41, 0x03, 0x76, 0x22, 0x09, + 0x41, 0x03, 0x74, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x00, + 0x46, 0x1a, 0x02, 0x40, 0x20, 0x06, 0x28, 0x02, 0x0c, 0x22, 0x03, 0x20, + 0x05, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xb8, 0x9e, + 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, 0x09, 0x77, 0x71, 0x36, 0x02, 0xb8, + 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x07, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x46, + 0x1a, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x08, 0x20, 0x05, 0x20, 0x03, + 0x36, 0x02, 0x0c, 0x0c, 0x06, 0x0b, 0x20, 0x06, 0x28, 0x02, 0x18, 0x21, + 0x0b, 0x02, 0x40, 0x20, 0x06, 0x28, 0x02, 0x0c, 0x22, 0x00, 0x20, 0x06, + 0x46, 0x0d, 0x00, 0x20, 0x06, 0x28, 0x02, 0x08, 0x22, 0x03, 0x20, 0x09, + 0x49, 0x1a, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x03, 0x20, + 0x00, 0x36, 0x02, 0x0c, 0x0c, 0x05, 0x0b, 0x02, 0x40, 0x20, 0x06, 0x41, + 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x03, 0x0d, 0x00, 0x20, + 0x06, 0x28, 0x02, 0x10, 0x22, 0x03, 0x45, 0x0d, 0x04, 0x20, 0x06, 0x41, + 0x10, 0x6a, 0x21, 0x05, 0x0b, 0x03, 0x40, 0x20, 0x05, 0x21, 0x09, 0x20, + 0x03, 0x22, 0x00, 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, + 0x03, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x20, 0x00, + 0x28, 0x02, 0x10, 0x22, 0x03, 0x0d, 0x00, 0x0b, 0x20, 0x09, 0x41, 0x00, + 0x36, 0x02, 0x00, 0x0c, 0x04, 0x0b, 0x20, 0x00, 0x41, 0x78, 0x20, 0x00, + 0x6b, 0x41, 0x0f, 0x71, 0x22, 0x04, 0x6a, 0x22, 0x02, 0x20, 0x06, 0x41, + 0x48, 0x6a, 0x22, 0x09, 0x20, 0x04, 0x6b, 0x22, 0x04, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x09, 0x6a, 0x41, 0x38, 0x36, 0x02, + 0x04, 0x20, 0x03, 0x20, 0x05, 0x41, 0x37, 0x20, 0x05, 0x6b, 0x41, 0x0f, + 0x71, 0x6a, 0x41, 0x41, 0x6a, 0x22, 0x09, 0x20, 0x09, 0x20, 0x03, 0x41, + 0x10, 0x6a, 0x49, 0x1b, 0x22, 0x09, 0x41, 0x23, 0x36, 0x02, 0x04, 0x41, + 0x00, 0x41, 0x00, 0x28, 0x02, 0xa0, 0xa2, 0x80, 0x80, 0x00, 0x36, 0x02, + 0xd4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0xc4, + 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, 0xd0, 0x9e, + 0x80, 0x80, 0x00, 0x20, 0x09, 0x41, 0x10, 0x6a, 0x41, 0x00, 0x29, 0x02, + 0x80, 0xa2, 0x80, 0x80, 0x00, 0x37, 0x02, 0x00, 0x20, 0x09, 0x41, 0x00, + 0x29, 0x02, 0xf8, 0xa1, 0x80, 0x80, 0x00, 0x37, 0x02, 0x08, 0x41, 0x00, + 0x20, 0x09, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x80, 0xa2, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x20, 0x06, 0x36, 0x02, 0xfc, 0xa1, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x20, 0x00, 0x36, 0x02, 0xf8, 0xa1, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x36, 0x02, 0x84, 0xa2, 0x80, 0x80, 0x00, 0x20, 0x09, 0x41, + 0x24, 0x6a, 0x21, 0x04, 0x03, 0x40, 0x20, 0x04, 0x41, 0x07, 0x36, 0x02, + 0x00, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, + 0x00, 0x0b, 0x20, 0x09, 0x20, 0x03, 0x46, 0x0d, 0x00, 0x20, 0x09, 0x20, + 0x09, 0x28, 0x02, 0x04, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x04, 0x20, 0x09, + 0x20, 0x09, 0x20, 0x03, 0x6b, 0x22, 0x00, 0x36, 0x02, 0x00, 0x20, 0x03, + 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, 0x00, + 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x78, 0x71, 0x41, + 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, + 0x41, 0x00, 0x28, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x05, 0x41, + 0x01, 0x20, 0x00, 0x41, 0x03, 0x76, 0x74, 0x22, 0x00, 0x71, 0x0d, 0x00, + 0x41, 0x00, 0x20, 0x05, 0x20, 0x00, 0x72, 0x36, 0x02, 0xb8, 0x9e, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x21, 0x05, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x28, + 0x02, 0x08, 0x21, 0x05, 0x0b, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x0c, + 0x20, 0x04, 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x03, 0x20, 0x04, 0x36, + 0x02, 0x0c, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, + 0x41, 0x1f, 0x21, 0x04, 0x02, 0x40, 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, + 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x26, 0x20, 0x00, 0x41, 0x08, + 0x76, 0x67, 0x22, 0x04, 0x6b, 0x76, 0x41, 0x01, 0x71, 0x20, 0x04, 0x41, + 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, 0x21, 0x04, 0x0b, 0x20, 0x03, 0x20, + 0x04, 0x36, 0x02, 0x1c, 0x20, 0x03, 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, + 0x04, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, 0x21, + 0x05, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, + 0x22, 0x09, 0x41, 0x01, 0x20, 0x04, 0x74, 0x22, 0x06, 0x71, 0x0d, 0x00, + 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x00, 0x41, 0x00, 0x20, 0x09, 0x20, + 0x06, 0x72, 0x36, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, + 0x05, 0x36, 0x02, 0x18, 0x20, 0x03, 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, + 0x03, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, + 0x00, 0x41, 0x19, 0x20, 0x04, 0x41, 0x01, 0x76, 0x6b, 0x20, 0x04, 0x41, + 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, 0x21, + 0x09, 0x02, 0x40, 0x03, 0x40, 0x20, 0x09, 0x22, 0x05, 0x28, 0x02, 0x04, + 0x41, 0x78, 0x71, 0x20, 0x00, 0x46, 0x0d, 0x01, 0x20, 0x04, 0x41, 0x1d, + 0x76, 0x21, 0x09, 0x20, 0x04, 0x41, 0x01, 0x74, 0x21, 0x04, 0x20, 0x05, + 0x20, 0x09, 0x41, 0x04, 0x71, 0x6a, 0x41, 0x10, 0x6a, 0x22, 0x06, 0x28, + 0x02, 0x00, 0x22, 0x09, 0x0d, 0x00, 0x0b, 0x20, 0x06, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, 0x03, 0x20, + 0x03, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x03, 0x36, 0x02, 0x08, 0x0c, + 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x03, 0x36, + 0x02, 0x0c, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, + 0x00, 0x36, 0x02, 0x18, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x0c, 0x20, + 0x03, 0x20, 0x04, 0x36, 0x02, 0x08, 0x0b, 0x41, 0x00, 0x28, 0x02, 0xc4, + 0x9e, 0x80, 0x80, 0x00, 0x22, 0x04, 0x20, 0x07, 0x4d, 0x0d, 0x00, 0x41, + 0x00, 0x28, 0x02, 0xd0, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, 0x07, + 0x6a, 0x22, 0x05, 0x20, 0x04, 0x20, 0x07, 0x6b, 0x22, 0x04, 0x41, 0x01, + 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0xc4, 0x9e, + 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xd0, 0x9e, 0x80, + 0x80, 0x00, 0x20, 0x03, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0c, 0x08, 0x0b, 0x41, 0x00, + 0x21, 0x04, 0x41, 0x00, 0x41, 0x30, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, + 0x00, 0x0c, 0x07, 0x0b, 0x41, 0x00, 0x21, 0x00, 0x0b, 0x20, 0x0b, 0x45, + 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, 0x20, 0x06, 0x28, 0x02, + 0x1c, 0x22, 0x05, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, + 0x6a, 0x22, 0x03, 0x28, 0x02, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x03, 0x20, + 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0d, 0x01, 0x41, 0x00, 0x41, 0x00, + 0x28, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, 0x05, 0x77, + 0x71, 0x36, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, + 0x0b, 0x41, 0x10, 0x41, 0x14, 0x20, 0x0b, 0x28, 0x02, 0x10, 0x20, 0x06, + 0x46, 0x1b, 0x6a, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x45, 0x0d, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x0b, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, + 0x06, 0x28, 0x02, 0x10, 0x22, 0x03, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, + 0x03, 0x36, 0x02, 0x10, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0b, + 0x20, 0x06, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x03, 0x45, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, + 0x03, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x08, 0x20, 0x04, 0x6a, + 0x21, 0x04, 0x20, 0x06, 0x20, 0x08, 0x6a, 0x22, 0x06, 0x28, 0x02, 0x04, + 0x21, 0x03, 0x0b, 0x20, 0x06, 0x20, 0x03, 0x41, 0x7e, 0x71, 0x36, 0x02, + 0x04, 0x20, 0x07, 0x20, 0x04, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, + 0x07, 0x20, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, + 0x04, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x78, 0x71, + 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x03, 0x02, 0x40, 0x02, + 0x40, 0x41, 0x00, 0x28, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x05, + 0x41, 0x01, 0x20, 0x04, 0x41, 0x03, 0x76, 0x74, 0x22, 0x04, 0x71, 0x0d, + 0x00, 0x41, 0x00, 0x20, 0x05, 0x20, 0x04, 0x72, 0x36, 0x02, 0xb8, 0x9e, + 0x80, 0x80, 0x00, 0x20, 0x03, 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x03, + 0x28, 0x02, 0x08, 0x21, 0x04, 0x0b, 0x20, 0x04, 0x20, 0x07, 0x36, 0x02, + 0x0c, 0x20, 0x03, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x20, 0x03, + 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, 0x08, 0x0c, 0x01, + 0x0b, 0x41, 0x1f, 0x21, 0x03, 0x02, 0x40, 0x20, 0x04, 0x41, 0xff, 0xff, + 0xff, 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x26, 0x20, 0x04, 0x41, + 0x08, 0x76, 0x67, 0x22, 0x03, 0x6b, 0x76, 0x41, 0x01, 0x71, 0x20, 0x03, + 0x41, 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, 0x21, 0x03, 0x0b, 0x20, 0x07, + 0x20, 0x03, 0x36, 0x02, 0x1c, 0x20, 0x07, 0x42, 0x00, 0x37, 0x02, 0x10, + 0x20, 0x03, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, + 0x21, 0x05, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xbc, 0x9e, 0x80, 0x80, + 0x00, 0x22, 0x00, 0x41, 0x01, 0x20, 0x03, 0x74, 0x22, 0x09, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x20, 0x07, 0x36, 0x02, 0x00, 0x41, 0x00, 0x20, 0x00, + 0x20, 0x09, 0x72, 0x36, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x07, + 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, 0x08, + 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x04, + 0x41, 0x00, 0x41, 0x19, 0x20, 0x03, 0x41, 0x01, 0x76, 0x6b, 0x20, 0x03, + 0x41, 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x03, 0x20, 0x05, 0x28, 0x02, 0x00, + 0x21, 0x00, 0x02, 0x40, 0x03, 0x40, 0x20, 0x00, 0x22, 0x05, 0x28, 0x02, + 0x04, 0x41, 0x78, 0x71, 0x20, 0x04, 0x46, 0x0d, 0x01, 0x20, 0x03, 0x41, + 0x1d, 0x76, 0x21, 0x00, 0x20, 0x03, 0x41, 0x01, 0x74, 0x21, 0x03, 0x20, + 0x05, 0x20, 0x00, 0x41, 0x04, 0x71, 0x6a, 0x41, 0x10, 0x6a, 0x22, 0x09, + 0x28, 0x02, 0x00, 0x22, 0x00, 0x0d, 0x00, 0x0b, 0x20, 0x09, 0x20, 0x07, + 0x36, 0x02, 0x00, 0x20, 0x07, 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, 0x07, + 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, 0x08, + 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x07, + 0x36, 0x02, 0x0c, 0x20, 0x05, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, + 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x07, 0x20, 0x05, 0x36, 0x02, 0x0c, + 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, 0x08, 0x0b, 0x20, 0x02, 0x41, 0x08, + 0x6a, 0x21, 0x04, 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x45, 0x0d, + 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x09, 0x20, 0x09, 0x28, 0x02, 0x1c, + 0x22, 0x05, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, + 0x22, 0x04, 0x28, 0x02, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x00, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x0d, 0x01, 0x41, 0x00, 0x20, 0x0b, 0x41, + 0x7e, 0x20, 0x05, 0x77, 0x71, 0x22, 0x0b, 0x36, 0x02, 0xbc, 0x9e, 0x80, + 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x41, 0x14, 0x20, + 0x02, 0x28, 0x02, 0x10, 0x20, 0x09, 0x46, 0x1b, 0x6a, 0x20, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x02, + 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x09, 0x28, 0x02, 0x10, 0x22, 0x04, + 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x10, 0x20, 0x04, + 0x20, 0x00, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x09, 0x41, 0x14, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x14, 0x6a, + 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, + 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x0f, 0x4b, 0x0d, 0x00, + 0x20, 0x09, 0x20, 0x03, 0x20, 0x07, 0x6a, 0x22, 0x04, 0x41, 0x03, 0x72, + 0x36, 0x02, 0x04, 0x20, 0x09, 0x20, 0x04, 0x6a, 0x22, 0x04, 0x20, 0x04, + 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, + 0x20, 0x09, 0x20, 0x07, 0x6a, 0x22, 0x00, 0x20, 0x03, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x20, 0x09, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, + 0x04, 0x20, 0x00, 0x20, 0x03, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x02, + 0x40, 0x20, 0x03, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x41, + 0x78, 0x71, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x04, 0x02, + 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, + 0x22, 0x05, 0x41, 0x01, 0x20, 0x03, 0x41, 0x03, 0x76, 0x74, 0x22, 0x03, + 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x05, 0x20, 0x03, 0x72, 0x36, 0x02, + 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x04, 0x21, 0x03, 0x0c, 0x01, 0x0b, + 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x03, 0x0b, 0x20, 0x03, 0x20, 0x00, + 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, + 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x08, + 0x0c, 0x01, 0x0b, 0x41, 0x1f, 0x21, 0x04, 0x02, 0x40, 0x20, 0x03, 0x41, + 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x41, 0x26, 0x20, + 0x03, 0x41, 0x08, 0x76, 0x67, 0x22, 0x04, 0x6b, 0x76, 0x41, 0x01, 0x71, + 0x20, 0x04, 0x41, 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, 0x21, 0x04, 0x0b, + 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x1c, 0x20, 0x00, 0x42, 0x00, 0x37, + 0x02, 0x10, 0x20, 0x04, 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, + 0x00, 0x6a, 0x21, 0x05, 0x02, 0x40, 0x20, 0x0b, 0x41, 0x01, 0x20, 0x04, + 0x74, 0x22, 0x07, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, + 0x00, 0x41, 0x00, 0x20, 0x0b, 0x20, 0x07, 0x72, 0x36, 0x02, 0xbc, 0x9e, + 0x80, 0x80, 0x00, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, 0x00, + 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x00, 0x36, 0x02, 0x0c, + 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x00, 0x41, 0x19, 0x20, 0x04, 0x41, + 0x01, 0x76, 0x6b, 0x20, 0x04, 0x41, 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x04, + 0x20, 0x05, 0x28, 0x02, 0x00, 0x21, 0x07, 0x02, 0x40, 0x03, 0x40, 0x20, + 0x07, 0x22, 0x05, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x03, 0x46, + 0x0d, 0x01, 0x20, 0x04, 0x41, 0x1d, 0x76, 0x21, 0x07, 0x20, 0x04, 0x41, + 0x01, 0x74, 0x21, 0x04, 0x20, 0x05, 0x20, 0x07, 0x41, 0x04, 0x71, 0x6a, + 0x41, 0x10, 0x6a, 0x22, 0x06, 0x28, 0x02, 0x00, 0x22, 0x07, 0x0d, 0x00, + 0x0b, 0x20, 0x06, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x05, + 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x00, + 0x20, 0x00, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, + 0x08, 0x22, 0x04, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x05, 0x20, 0x00, + 0x36, 0x02, 0x08, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, + 0x20, 0x05, 0x36, 0x02, 0x0c, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x08, + 0x0b, 0x20, 0x09, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x02, + 0x40, 0x20, 0x0b, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x1c, 0x22, 0x05, 0x41, 0x02, 0x74, 0x41, 0xe8, + 0xa0, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x04, 0x28, 0x02, 0x00, 0x47, 0x0d, + 0x00, 0x20, 0x04, 0x20, 0x09, 0x36, 0x02, 0x00, 0x20, 0x09, 0x0d, 0x01, + 0x41, 0x00, 0x20, 0x0a, 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, + 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x0b, 0x41, 0x10, + 0x41, 0x14, 0x20, 0x0b, 0x28, 0x02, 0x10, 0x20, 0x00, 0x46, 0x1b, 0x6a, + 0x20, 0x09, 0x36, 0x02, 0x00, 0x20, 0x09, 0x45, 0x0d, 0x01, 0x0b, 0x20, + 0x09, 0x20, 0x0b, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, + 0x10, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x09, 0x20, 0x04, 0x36, 0x02, + 0x10, 0x20, 0x04, 0x20, 0x09, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x00, 0x41, + 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x09, + 0x41, 0x14, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x09, + 0x36, 0x02, 0x18, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x0f, + 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x03, 0x20, 0x07, 0x6a, 0x22, 0x04, + 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x04, 0x6a, 0x22, + 0x04, 0x20, 0x04, 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, + 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x07, 0x6a, 0x22, 0x05, 0x20, 0x03, + 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x07, 0x41, 0x03, + 0x72, 0x36, 0x02, 0x04, 0x20, 0x05, 0x20, 0x03, 0x6a, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x02, 0x40, 0x20, 0x08, 0x45, 0x0d, 0x00, 0x20, 0x08, 0x41, + 0x78, 0x71, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x07, 0x41, + 0x00, 0x28, 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x21, 0x04, 0x02, 0x40, + 0x02, 0x40, 0x41, 0x01, 0x20, 0x08, 0x41, 0x03, 0x76, 0x74, 0x22, 0x09, + 0x20, 0x06, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x09, 0x20, 0x06, 0x72, + 0x36, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x07, 0x21, 0x09, 0x0c, + 0x01, 0x0b, 0x20, 0x07, 0x28, 0x02, 0x08, 0x21, 0x09, 0x0b, 0x20, 0x09, + 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, 0x08, + 0x20, 0x04, 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x09, 0x36, + 0x02, 0x08, 0x0b, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xcc, 0x9e, 0x80, + 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0xc0, 0x9e, 0x80, 0x80, + 0x00, 0x0b, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0b, 0x20, 0x01, + 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x0b, + 0x0a, 0x00, 0x20, 0x00, 0x10, 0x93, 0x80, 0x80, 0x80, 0x00, 0x0b, 0xb0, + 0x0d, 0x01, 0x07, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x00, 0x20, + 0x00, 0x41, 0x78, 0x6a, 0x22, 0x01, 0x20, 0x00, 0x41, 0x7c, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x02, 0x41, 0x78, 0x71, 0x22, 0x00, 0x6a, 0x21, 0x03, + 0x02, 0x40, 0x20, 0x02, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x20, 0x02, 0x41, + 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x22, 0x02, 0x6b, 0x22, 0x01, 0x41, 0x00, 0x28, 0x02, 0xc8, 0x9e, 0x80, + 0x80, 0x00, 0x22, 0x04, 0x49, 0x0d, 0x01, 0x20, 0x02, 0x20, 0x00, 0x6a, + 0x21, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, + 0x28, 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x46, 0x0d, 0x00, 0x02, 0x40, + 0x20, 0x02, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, + 0x08, 0x22, 0x04, 0x20, 0x02, 0x41, 0x03, 0x76, 0x22, 0x05, 0x41, 0x03, + 0x74, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x06, 0x46, 0x1a, + 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x22, 0x02, 0x20, 0x04, 0x47, + 0x0d, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xb8, 0x9e, 0x80, 0x80, + 0x00, 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0xb8, 0x9e, 0x80, + 0x80, 0x00, 0x0c, 0x05, 0x0b, 0x20, 0x02, 0x20, 0x06, 0x46, 0x1a, 0x20, + 0x02, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, + 0x0c, 0x0c, 0x04, 0x0b, 0x20, 0x01, 0x28, 0x02, 0x18, 0x21, 0x07, 0x02, + 0x40, 0x20, 0x01, 0x28, 0x02, 0x0c, 0x22, 0x06, 0x20, 0x01, 0x46, 0x0d, + 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x02, 0x20, 0x04, 0x49, 0x1a, + 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x08, 0x20, 0x02, 0x20, 0x06, 0x36, + 0x02, 0x0c, 0x0c, 0x03, 0x0b, 0x02, 0x40, 0x20, 0x01, 0x41, 0x14, 0x6a, + 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x02, 0x0d, 0x00, 0x20, 0x01, 0x28, + 0x02, 0x10, 0x22, 0x02, 0x45, 0x0d, 0x02, 0x20, 0x01, 0x41, 0x10, 0x6a, + 0x21, 0x04, 0x0b, 0x03, 0x40, 0x20, 0x04, 0x21, 0x05, 0x20, 0x02, 0x22, + 0x06, 0x41, 0x14, 0x6a, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x02, 0x0d, + 0x00, 0x20, 0x06, 0x41, 0x10, 0x6a, 0x21, 0x04, 0x20, 0x06, 0x28, 0x02, + 0x10, 0x22, 0x02, 0x0d, 0x00, 0x0b, 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, + 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x04, 0x22, 0x02, 0x41, + 0x03, 0x71, 0x41, 0x03, 0x47, 0x0d, 0x02, 0x20, 0x03, 0x20, 0x02, 0x41, + 0x7e, 0x71, 0x36, 0x02, 0x04, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xc0, + 0x9e, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, + 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0f, 0x0b, 0x41, + 0x00, 0x21, 0x06, 0x0b, 0x20, 0x07, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x01, 0x20, 0x01, 0x28, 0x02, 0x1c, 0x22, 0x04, 0x41, 0x02, + 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x02, 0x28, 0x02, + 0x00, 0x47, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, + 0x06, 0x0d, 0x01, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xbc, 0x9e, 0x80, + 0x80, 0x00, 0x41, 0x7e, 0x20, 0x04, 0x77, 0x71, 0x36, 0x02, 0xbc, 0x9e, + 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x07, 0x41, 0x10, 0x41, 0x14, + 0x20, 0x07, 0x28, 0x02, 0x10, 0x20, 0x01, 0x46, 0x1b, 0x6a, 0x20, 0x06, + 0x36, 0x02, 0x00, 0x20, 0x06, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x06, 0x20, + 0x07, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x10, 0x22, + 0x02, 0x45, 0x0d, 0x00, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x10, 0x20, + 0x02, 0x20, 0x06, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x01, 0x41, 0x14, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x06, 0x41, 0x14, + 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, + 0x18, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x28, + 0x02, 0x04, 0x22, 0x02, 0x41, 0x01, 0x71, 0x45, 0x0d, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x02, + 0x71, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x03, 0x41, 0x00, 0x28, 0x02, 0xd0, + 0x9e, 0x80, 0x80, 0x00, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x01, 0x36, + 0x02, 0xd0, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, + 0xc4, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x00, 0x6a, 0x22, 0x00, 0x36, 0x02, + 0xc4, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, 0xcc, 0x9e, 0x80, + 0x80, 0x00, 0x47, 0x0d, 0x06, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xc0, + 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xcc, 0x9e, + 0x80, 0x80, 0x00, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x00, 0x28, + 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, + 0x01, 0x36, 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, + 0x28, 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x00, 0x6a, 0x22, 0x00, + 0x36, 0x02, 0xc0, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x01, 0x20, 0x00, 0x41, + 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x00, 0x6a, 0x20, 0x00, + 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x41, 0x78, 0x71, 0x20, 0x00, + 0x6a, 0x21, 0x00, 0x02, 0x40, 0x20, 0x02, 0x41, 0xff, 0x01, 0x4b, 0x0d, + 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x02, 0x41, 0x03, + 0x76, 0x22, 0x05, 0x41, 0x03, 0x74, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, + 0x6a, 0x22, 0x06, 0x46, 0x1a, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x0c, + 0x22, 0x02, 0x20, 0x04, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, + 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, + 0x36, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x05, 0x0b, 0x20, 0x02, + 0x20, 0x06, 0x46, 0x1a, 0x20, 0x02, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, + 0x04, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x0c, 0x04, 0x0b, 0x20, 0x03, 0x28, + 0x02, 0x18, 0x21, 0x07, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x22, + 0x06, 0x20, 0x03, 0x46, 0x0d, 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, + 0x02, 0x41, 0x00, 0x28, 0x02, 0xc8, 0x9e, 0x80, 0x80, 0x00, 0x49, 0x1a, + 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x08, 0x20, 0x02, 0x20, 0x06, 0x36, + 0x02, 0x0c, 0x0c, 0x03, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x14, 0x6a, + 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x02, 0x0d, 0x00, 0x20, 0x03, 0x28, + 0x02, 0x10, 0x22, 0x02, 0x45, 0x0d, 0x02, 0x20, 0x03, 0x41, 0x10, 0x6a, + 0x21, 0x04, 0x0b, 0x03, 0x40, 0x20, 0x04, 0x21, 0x05, 0x20, 0x02, 0x22, + 0x06, 0x41, 0x14, 0x6a, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, 0x02, 0x0d, + 0x00, 0x20, 0x06, 0x41, 0x10, 0x6a, 0x21, 0x04, 0x20, 0x06, 0x28, 0x02, + 0x10, 0x22, 0x02, 0x0d, 0x00, 0x0b, 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, + 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x03, 0x20, 0x02, 0x41, 0x7e, 0x71, 0x36, + 0x02, 0x04, 0x20, 0x01, 0x20, 0x00, 0x6a, 0x20, 0x00, 0x36, 0x02, 0x00, + 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0c, 0x03, + 0x0b, 0x41, 0x00, 0x21, 0x06, 0x0b, 0x20, 0x07, 0x45, 0x0d, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x03, 0x20, 0x03, 0x28, 0x02, 0x1c, 0x22, 0x04, + 0x41, 0x02, 0x74, 0x41, 0xe8, 0xa0, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x02, + 0x28, 0x02, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, + 0x00, 0x20, 0x06, 0x0d, 0x01, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xbc, + 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, 0x04, 0x77, 0x71, 0x36, 0x02, + 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x07, 0x41, 0x10, + 0x41, 0x14, 0x20, 0x07, 0x28, 0x02, 0x10, 0x20, 0x03, 0x46, 0x1b, 0x6a, + 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x06, 0x45, 0x0d, 0x01, 0x0b, 0x20, + 0x06, 0x20, 0x07, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, + 0x10, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, + 0x10, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x03, 0x41, + 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x06, + 0x41, 0x14, 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, 0x20, 0x02, 0x20, 0x06, + 0x36, 0x02, 0x18, 0x0b, 0x20, 0x01, 0x20, 0x00, 0x6a, 0x20, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, + 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, 0xcc, 0x9e, 0x80, 0x80, 0x00, 0x47, + 0x0d, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xc0, 0x9e, 0x80, 0x80, + 0x00, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x41, 0xff, 0x01, 0x4b, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x78, 0x71, 0x41, 0xe0, 0x9e, 0x80, 0x80, 0x00, + 0x6a, 0x21, 0x02, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xb8, + 0x9e, 0x80, 0x80, 0x00, 0x22, 0x04, 0x41, 0x01, 0x20, 0x00, 0x41, 0x03, + 0x76, 0x74, 0x22, 0x00, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x04, 0x20, + 0x00, 0x72, 0x36, 0x02, 0xb8, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x02, 0x21, + 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x28, 0x02, 0x08, 0x21, 0x00, 0x0b, + 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x0c, 0x20, 0x02, 0x20, 0x01, 0x36, + 0x02, 0x08, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, + 0x00, 0x36, 0x02, 0x08, 0x0f, 0x0b, 0x41, 0x1f, 0x21, 0x02, 0x02, 0x40, + 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x00, + 0x41, 0x26, 0x20, 0x00, 0x41, 0x08, 0x76, 0x67, 0x22, 0x02, 0x6b, 0x76, + 0x41, 0x01, 0x71, 0x20, 0x02, 0x41, 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, + 0x21, 0x02, 0x0b, 0x20, 0x01, 0x20, 0x02, 0x36, 0x02, 0x1c, 0x20, 0x01, + 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, 0x02, 0x41, 0x02, 0x74, 0x41, 0xe8, + 0xa0, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x00, 0x28, 0x02, 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x06, 0x41, 0x01, + 0x20, 0x02, 0x74, 0x22, 0x03, 0x71, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x01, + 0x36, 0x02, 0x00, 0x41, 0x00, 0x20, 0x06, 0x20, 0x03, 0x72, 0x36, 0x02, + 0xbc, 0x9e, 0x80, 0x80, 0x00, 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, 0x18, + 0x20, 0x01, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x01, 0x20, 0x01, 0x36, + 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x41, 0x19, 0x20, + 0x02, 0x41, 0x01, 0x76, 0x6b, 0x20, 0x02, 0x41, 0x1f, 0x46, 0x1b, 0x74, + 0x21, 0x02, 0x20, 0x04, 0x28, 0x02, 0x00, 0x21, 0x06, 0x02, 0x40, 0x03, + 0x40, 0x20, 0x06, 0x22, 0x04, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, + 0x00, 0x46, 0x0d, 0x01, 0x20, 0x02, 0x41, 0x1d, 0x76, 0x21, 0x06, 0x20, + 0x02, 0x41, 0x01, 0x74, 0x21, 0x02, 0x20, 0x04, 0x20, 0x06, 0x41, 0x04, + 0x71, 0x6a, 0x41, 0x10, 0x6a, 0x22, 0x03, 0x28, 0x02, 0x00, 0x22, 0x06, + 0x0d, 0x00, 0x0b, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x01, + 0x20, 0x04, 0x36, 0x02, 0x18, 0x20, 0x01, 0x20, 0x01, 0x36, 0x02, 0x0c, + 0x20, 0x01, 0x20, 0x01, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x04, + 0x28, 0x02, 0x08, 0x22, 0x00, 0x20, 0x01, 0x36, 0x02, 0x0c, 0x20, 0x04, + 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x18, + 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, 0x00, 0x36, + 0x02, 0x08, 0x0b, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xd8, 0x9e, 0x80, + 0x80, 0x00, 0x41, 0x7f, 0x6a, 0x22, 0x01, 0x41, 0x7f, 0x20, 0x01, 0x1b, + 0x36, 0x02, 0xd8, 0x9e, 0x80, 0x80, 0x00, 0x0b, 0x0b, 0x6b, 0x02, 0x01, + 0x7f, 0x01, 0x7e, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, + 0x00, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0xad, 0x20, 0x01, 0xad, + 0x7e, 0x22, 0x03, 0xa7, 0x21, 0x02, 0x20, 0x01, 0x20, 0x00, 0x72, 0x41, + 0x80, 0x80, 0x04, 0x49, 0x0d, 0x00, 0x41, 0x7f, 0x20, 0x02, 0x20, 0x03, + 0x42, 0x20, 0x88, 0xa7, 0x41, 0x00, 0x47, 0x1b, 0x21, 0x02, 0x0b, 0x02, + 0x40, 0x20, 0x02, 0x10, 0x91, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, + 0x0d, 0x00, 0x20, 0x00, 0x41, 0x7c, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x03, + 0x71, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x20, 0x02, 0x10, 0xa9, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x0b, 0x0b, 0x00, 0x20, + 0x00, 0x10, 0xa3, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0xd5, 0x01, 0x01, + 0x03, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x00, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x08, 0x6a, 0x20, 0x00, + 0x41, 0x0c, 0x6a, 0x10, 0x98, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x08, 0x41, 0x01, 0x6a, 0x22, 0x01, 0x45, 0x0d, 0x01, + 0x20, 0x00, 0x28, 0x02, 0x0c, 0x10, 0x90, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x02, 0x45, 0x0d, 0x02, 0x20, 0x01, 0x41, 0x04, 0x10, 0x94, 0x80, 0x80, + 0x80, 0x00, 0x22, 0x01, 0x45, 0x0d, 0x03, 0x20, 0x01, 0x20, 0x02, 0x10, + 0x97, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x04, 0x20, 0x00, 0x28, 0x02, 0x08, + 0x20, 0x01, 0x10, 0xe2, 0x80, 0x80, 0x80, 0x00, 0x21, 0x01, 0x20, 0x00, + 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0f, + 0x0b, 0x41, 0xc7, 0x00, 0x10, 0x95, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, + 0x41, 0xc6, 0x00, 0x10, 0x95, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x41, + 0xc6, 0x00, 0x10, 0x95, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x20, 0x02, + 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x41, 0xc6, 0x00, 0x10, 0x95, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x0b, 0x20, 0x02, 0x10, 0x92, 0x80, 0x80, 0x80, + 0x00, 0x20, 0x01, 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x41, 0xc7, 0x00, + 0x10, 0x95, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x11, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, + 0x71, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x81, 0x80, 0x80, + 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x0f, 0x00, 0x20, 0x00, + 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, + 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x83, 0x80, 0x80, 0x80, 0x00, + 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, + 0x10, 0x84, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, + 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x85, 0x80, 0x80, 0x80, 0x00, + 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x13, 0x00, 0x20, 0x00, 0x20, 0x01, + 0x20, 0x02, 0x10, 0x86, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, + 0x71, 0x0b, 0x15, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, + 0x10, 0x87, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, + 0x15, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, 0x88, + 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x15, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, 0x89, 0x80, 0x80, + 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x19, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x01, 0x10, 0xaa, 0x80, 0x80, 0x80, 0x00, 0x10, 0x8a, + 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x25, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x02, 0x10, 0xaa, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x03, 0x20, 0x04, 0x20, 0x05, 0x20, 0x06, 0x20, 0x07, + 0x10, 0x8b, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, + 0x0b, 0x00, 0x20, 0x00, 0x10, 0x8c, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, + 0x04, 0x00, 0x00, 0x00, 0x0b, 0x4e, 0x00, 0x02, 0x40, 0x20, 0x00, 0x0d, + 0x00, 0x3f, 0x00, 0x41, 0x10, 0x74, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x00, + 0x41, 0xff, 0xff, 0x03, 0x71, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x7f, 0x4c, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x41, 0x10, 0x76, 0x40, 0x00, 0x22, + 0x00, 0x41, 0x7f, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x30, 0x36, 0x02, + 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x00, 0x41, + 0x10, 0x74, 0x0f, 0x0b, 0x10, 0xa4, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, + 0x02, 0x00, 0x0b, 0x0e, 0x00, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x10, + 0xb5, 0x80, 0x80, 0x80, 0x00, 0x0b, 0xe6, 0x07, 0x01, 0x04, 0x7f, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x20, 0x4b, 0x0d, 0x00, + 0x20, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x02, 0x45, 0x0d, + 0x01, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, + 0x02, 0x41, 0x7f, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, + 0x04, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, + 0x0d, 0x02, 0x20, 0x03, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, + 0x00, 0x01, 0x3a, 0x00, 0x01, 0x20, 0x02, 0x41, 0x7e, 0x6a, 0x21, 0x03, + 0x20, 0x00, 0x41, 0x02, 0x6a, 0x21, 0x04, 0x20, 0x01, 0x41, 0x02, 0x6a, + 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x02, 0x20, 0x03, 0x45, 0x0d, + 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x02, 0x3a, 0x00, 0x02, 0x20, + 0x02, 0x41, 0x7d, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x03, 0x6a, 0x21, + 0x04, 0x20, 0x01, 0x41, 0x03, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, + 0x0d, 0x02, 0x20, 0x03, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, + 0x00, 0x03, 0x3a, 0x00, 0x03, 0x20, 0x02, 0x41, 0x7c, 0x6a, 0x21, 0x03, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x21, 0x04, 0x20, 0x01, 0x41, 0x04, 0x6a, + 0x21, 0x05, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0xfc, + 0x0a, 0x00, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x21, 0x03, 0x20, + 0x00, 0x21, 0x04, 0x20, 0x01, 0x21, 0x05, 0x0b, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x04, 0x41, 0x03, 0x71, 0x22, 0x02, 0x0d, 0x00, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x03, 0x41, 0x10, 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x02, + 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x70, 0x6a, 0x22, 0x02, + 0x41, 0x10, 0x71, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x00, + 0x37, 0x02, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x08, 0x37, 0x02, + 0x08, 0x20, 0x04, 0x41, 0x10, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x10, + 0x6a, 0x21, 0x05, 0x20, 0x02, 0x21, 0x03, 0x0b, 0x20, 0x02, 0x41, 0x10, + 0x49, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x02, 0x03, 0x40, 0x20, 0x04, 0x20, + 0x05, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, + 0x02, 0x08, 0x37, 0x02, 0x08, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x10, + 0x37, 0x02, 0x10, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x18, 0x37, 0x02, + 0x18, 0x20, 0x04, 0x41, 0x20, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x20, + 0x6a, 0x21, 0x05, 0x20, 0x02, 0x41, 0x60, 0x6a, 0x22, 0x02, 0x41, 0x0f, + 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x41, 0x08, 0x49, + 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, + 0x20, 0x05, 0x41, 0x08, 0x6a, 0x21, 0x05, 0x20, 0x04, 0x41, 0x08, 0x6a, + 0x21, 0x04, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x41, 0x04, 0x71, 0x45, 0x0d, + 0x00, 0x20, 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, + 0x05, 0x41, 0x04, 0x6a, 0x21, 0x05, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x21, + 0x04, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x41, 0x02, 0x71, 0x45, 0x0d, 0x00, + 0x20, 0x04, 0x20, 0x05, 0x2f, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x20, 0x04, + 0x41, 0x02, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x02, 0x6a, 0x21, 0x05, + 0x0b, 0x20, 0x02, 0x41, 0x01, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x20, + 0x05, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, + 0x20, 0x49, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x7f, + 0x6a, 0x0e, 0x03, 0x03, 0x00, 0x01, 0x07, 0x0b, 0x20, 0x04, 0x20, 0x05, + 0x28, 0x02, 0x00, 0x3b, 0x00, 0x00, 0x20, 0x04, 0x20, 0x05, 0x41, 0x02, + 0x6a, 0x28, 0x01, 0x00, 0x36, 0x02, 0x02, 0x20, 0x04, 0x20, 0x05, 0x41, + 0x06, 0x6a, 0x29, 0x01, 0x00, 0x37, 0x02, 0x06, 0x20, 0x04, 0x41, 0x12, + 0x6a, 0x21, 0x02, 0x20, 0x05, 0x41, 0x12, 0x6a, 0x21, 0x01, 0x41, 0x0e, + 0x21, 0x06, 0x20, 0x05, 0x41, 0x0e, 0x6a, 0x28, 0x01, 0x00, 0x21, 0x05, + 0x41, 0x0e, 0x21, 0x03, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x28, + 0x02, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x04, 0x20, 0x05, 0x41, 0x01, 0x6a, + 0x28, 0x00, 0x00, 0x36, 0x02, 0x01, 0x20, 0x04, 0x20, 0x05, 0x41, 0x05, + 0x6a, 0x29, 0x00, 0x00, 0x37, 0x02, 0x05, 0x20, 0x04, 0x41, 0x11, 0x6a, + 0x21, 0x02, 0x20, 0x05, 0x41, 0x11, 0x6a, 0x21, 0x01, 0x41, 0x0d, 0x21, + 0x06, 0x20, 0x05, 0x41, 0x0d, 0x6a, 0x28, 0x00, 0x00, 0x21, 0x05, 0x41, + 0x0f, 0x21, 0x03, 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, + 0x41, 0x10, 0x4f, 0x0d, 0x00, 0x20, 0x04, 0x21, 0x02, 0x20, 0x05, 0x21, + 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, 0x00, 0x3a, + 0x00, 0x00, 0x20, 0x04, 0x20, 0x05, 0x28, 0x00, 0x01, 0x36, 0x00, 0x01, + 0x20, 0x04, 0x20, 0x05, 0x29, 0x00, 0x05, 0x37, 0x00, 0x05, 0x20, 0x04, + 0x20, 0x05, 0x2f, 0x00, 0x0d, 0x3b, 0x00, 0x0d, 0x20, 0x04, 0x20, 0x05, + 0x2d, 0x00, 0x0f, 0x3a, 0x00, 0x0f, 0x20, 0x04, 0x41, 0x10, 0x6a, 0x21, + 0x02, 0x20, 0x05, 0x41, 0x10, 0x6a, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x08, 0x71, 0x0d, 0x02, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x28, + 0x02, 0x00, 0x22, 0x02, 0x3a, 0x00, 0x00, 0x20, 0x04, 0x20, 0x02, 0x41, + 0x10, 0x76, 0x3a, 0x00, 0x02, 0x20, 0x04, 0x20, 0x02, 0x41, 0x08, 0x76, + 0x3a, 0x00, 0x01, 0x20, 0x04, 0x20, 0x05, 0x41, 0x03, 0x6a, 0x28, 0x00, + 0x00, 0x36, 0x02, 0x03, 0x20, 0x04, 0x20, 0x05, 0x41, 0x07, 0x6a, 0x29, + 0x00, 0x00, 0x37, 0x02, 0x07, 0x20, 0x04, 0x41, 0x13, 0x6a, 0x21, 0x02, + 0x20, 0x05, 0x41, 0x13, 0x6a, 0x21, 0x01, 0x41, 0x0f, 0x21, 0x06, 0x20, + 0x05, 0x41, 0x0f, 0x6a, 0x28, 0x00, 0x00, 0x21, 0x05, 0x41, 0x0d, 0x21, + 0x03, 0x0b, 0x20, 0x04, 0x20, 0x06, 0x6a, 0x20, 0x05, 0x36, 0x02, 0x00, + 0x0b, 0x20, 0x02, 0x20, 0x01, 0x29, 0x00, 0x00, 0x37, 0x00, 0x00, 0x20, + 0x02, 0x41, 0x08, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x21, + 0x01, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x04, 0x71, 0x45, 0x0d, 0x00, + 0x20, 0x02, 0x20, 0x01, 0x28, 0x00, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, + 0x41, 0x04, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x21, 0x01, + 0x0b, 0x02, 0x40, 0x20, 0x03, 0x41, 0x02, 0x71, 0x45, 0x0d, 0x00, 0x20, + 0x02, 0x20, 0x01, 0x2f, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x20, 0x02, 0x41, + 0x02, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x21, 0x01, 0x0b, + 0x20, 0x03, 0x41, 0x01, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x01, + 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x0b, 0x88, 0x03, + 0x02, 0x03, 0x7f, 0x01, 0x7e, 0x02, 0x40, 0x20, 0x02, 0x41, 0x21, 0x49, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0xfc, 0x0b, 0x00, 0x20, + 0x00, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x20, 0x00, 0x6a, 0x22, 0x03, + 0x41, 0x7f, 0x6a, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x03, + 0x49, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0x02, 0x20, 0x00, + 0x20, 0x01, 0x3a, 0x00, 0x01, 0x20, 0x03, 0x41, 0x7d, 0x6a, 0x20, 0x01, + 0x3a, 0x00, 0x00, 0x20, 0x03, 0x41, 0x7e, 0x6a, 0x20, 0x01, 0x3a, 0x00, + 0x00, 0x20, 0x02, 0x41, 0x07, 0x49, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, + 0x3a, 0x00, 0x03, 0x20, 0x03, 0x41, 0x7c, 0x6a, 0x20, 0x01, 0x3a, 0x00, + 0x00, 0x20, 0x02, 0x41, 0x09, 0x49, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, + 0x20, 0x00, 0x6b, 0x41, 0x03, 0x71, 0x22, 0x04, 0x6a, 0x22, 0x05, 0x20, + 0x01, 0x41, 0xff, 0x01, 0x71, 0x41, 0x81, 0x82, 0x84, 0x08, 0x6c, 0x22, + 0x03, 0x36, 0x02, 0x00, 0x20, 0x05, 0x20, 0x02, 0x20, 0x04, 0x6b, 0x41, + 0x7c, 0x71, 0x22, 0x01, 0x6a, 0x22, 0x02, 0x41, 0x7c, 0x6a, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x09, 0x49, 0x0d, 0x00, 0x20, 0x05, + 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x04, + 0x20, 0x02, 0x41, 0x78, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, + 0x41, 0x74, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x19, + 0x49, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x18, 0x20, 0x05, + 0x20, 0x03, 0x36, 0x02, 0x14, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x10, + 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x02, 0x41, 0x70, 0x6a, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x6c, 0x6a, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x68, 0x6a, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x20, 0x02, 0x41, 0x64, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, + 0x01, 0x20, 0x05, 0x41, 0x04, 0x71, 0x41, 0x18, 0x72, 0x22, 0x02, 0x6b, + 0x22, 0x01, 0x41, 0x20, 0x49, 0x0d, 0x00, 0x20, 0x03, 0xad, 0x42, 0x81, + 0x80, 0x80, 0x80, 0x10, 0x7e, 0x21, 0x06, 0x20, 0x05, 0x20, 0x02, 0x6a, + 0x21, 0x02, 0x03, 0x40, 0x20, 0x02, 0x20, 0x06, 0x37, 0x03, 0x18, 0x20, + 0x02, 0x20, 0x06, 0x37, 0x03, 0x10, 0x20, 0x02, 0x20, 0x06, 0x37, 0x03, + 0x08, 0x20, 0x02, 0x20, 0x06, 0x37, 0x03, 0x00, 0x20, 0x02, 0x41, 0x20, + 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x60, 0x6a, 0x22, 0x01, 0x41, 0x1f, + 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x0b, 0xcc, 0x01, 0x01, 0x03, + 0x7f, 0x20, 0x00, 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, + 0x03, 0x71, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x00, 0x6b, 0x0f, 0x0b, 0x20, 0x00, 0x41, + 0x01, 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x01, + 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x22, + 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, + 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x03, 0x6a, 0x22, 0x01, 0x41, 0x03, + 0x71, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x01, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, 0x0d, 0x01, + 0x0b, 0x20, 0x01, 0x41, 0x7c, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x7b, + 0x6a, 0x21, 0x01, 0x03, 0x40, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x21, 0x01, + 0x20, 0x02, 0x41, 0x04, 0x6a, 0x22, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, + 0x41, 0x7f, 0x73, 0x20, 0x03, 0x41, 0xff, 0xfd, 0xfb, 0x77, 0x6a, 0x71, + 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, 0x45, 0x0d, 0x00, 0x0b, 0x03, + 0x40, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x02, 0x2d, 0x00, + 0x00, 0x21, 0x03, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, 0x20, 0x03, + 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x01, 0x20, 0x00, 0x6b, 0x0b, 0x27, 0x00, + 0x10, 0xca, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x00, 0x10, 0x99, + 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x0f, 0x0b, + 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, + 0x7f, 0x0b, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x38, 0x10, 0xab, 0x80, + 0x80, 0x80, 0x00, 0x0b, 0x71, 0x01, 0x02, 0x7f, 0x23, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x7f, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, + 0x7f, 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x1c, 0x36, 0x02, 0xb4, 0x9e, + 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, + 0x20, 0x02, 0x20, 0x03, 0x41, 0x0c, 0x6a, 0x10, 0xa0, 0x80, 0x80, 0x80, + 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, + 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, 0x04, 0x0c, 0x01, 0x0b, + 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x0b, 0x20, 0x03, 0x41, 0x10, + 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x0b, 0xbb, 0x02, + 0x01, 0x07, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, + 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, + 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x03, + 0x20, 0x00, 0x28, 0x02, 0x18, 0x22, 0x01, 0x36, 0x02, 0x00, 0x20, 0x03, + 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x01, 0x6b, 0x22, 0x04, 0x36, 0x02, + 0x04, 0x41, 0x02, 0x21, 0x05, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, + 0x02, 0x38, 0x20, 0x03, 0x41, 0x02, 0x10, 0xad, 0x80, 0x80, 0x80, 0x00, + 0x22, 0x01, 0x20, 0x04, 0x20, 0x02, 0x6a, 0x22, 0x06, 0x46, 0x0d, 0x00, + 0x20, 0x03, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x7f, + 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x01, 0x20, 0x00, 0x41, 0x00, 0x36, + 0x02, 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x10, 0x20, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x00, 0x41, 0x20, 0x72, 0x36, 0x02, 0x00, 0x20, 0x05, + 0x41, 0x02, 0x46, 0x0d, 0x03, 0x20, 0x02, 0x20, 0x04, 0x28, 0x02, 0x04, + 0x6b, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x01, 0x20, 0x04, + 0x28, 0x02, 0x04, 0x22, 0x07, 0x4b, 0x22, 0x08, 0x41, 0x03, 0x74, 0x6a, + 0x22, 0x09, 0x20, 0x09, 0x28, 0x02, 0x00, 0x20, 0x01, 0x20, 0x07, 0x41, + 0x00, 0x20, 0x08, 0x1b, 0x6b, 0x22, 0x07, 0x6a, 0x36, 0x02, 0x00, 0x20, + 0x04, 0x41, 0x0c, 0x41, 0x04, 0x20, 0x08, 0x1b, 0x6a, 0x22, 0x04, 0x20, + 0x04, 0x28, 0x02, 0x00, 0x20, 0x07, 0x6b, 0x36, 0x02, 0x00, 0x20, 0x09, + 0x21, 0x04, 0x20, 0x06, 0x20, 0x01, 0x6b, 0x22, 0x06, 0x20, 0x00, 0x28, + 0x02, 0x38, 0x20, 0x09, 0x20, 0x05, 0x20, 0x08, 0x6b, 0x22, 0x05, 0x10, + 0xad, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x47, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x01, 0x36, 0x02, 0x18, + 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x2c, 0x6a, 0x36, 0x02, 0x10, 0x20, 0x02, 0x21, 0x01, + 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x01, 0x0b, 0x66, 0x01, 0x02, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x20, 0x6b, 0x22, 0x01, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x10, + 0x9a, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0d, 0x00, 0x41, 0x3b, 0x21, + 0x00, 0x20, 0x01, 0x2d, 0x00, 0x08, 0x41, 0x02, 0x47, 0x0d, 0x00, 0x20, + 0x01, 0x2d, 0x00, 0x10, 0x41, 0x24, 0x71, 0x0d, 0x00, 0x41, 0x01, 0x21, + 0x02, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x02, 0x41, 0x00, 0x20, 0x00, + 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x20, + 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x0b, 0x3b, 0x00, + 0x20, 0x00, 0x41, 0x81, 0x80, 0x80, 0x80, 0x00, 0x36, 0x02, 0x20, 0x02, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0xc0, 0x00, 0x71, 0x0d, 0x00, + 0x20, 0x00, 0x28, 0x02, 0x38, 0x10, 0xaf, 0x80, 0x80, 0x80, 0x00, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x40, 0x0b, 0x20, 0x00, 0x20, + 0x01, 0x20, 0x02, 0x10, 0xae, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x64, 0x01, + 0x01, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0xff, 0x01, 0x71, 0x20, 0x03, 0x41, + 0x08, 0x6a, 0x10, 0x9f, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, + 0x00, 0x41, 0x00, 0x41, 0xc6, 0x00, 0x20, 0x02, 0x20, 0x02, 0x41, 0xcc, + 0x00, 0x46, 0x1b, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x42, 0x7f, + 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x29, 0x03, 0x08, 0x21, 0x01, + 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x01, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x01, + 0x20, 0x02, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x08, 0x00, 0x41, + 0xb8, 0xaa, 0x80, 0x80, 0x00, 0x0b, 0x02, 0x00, 0x0b, 0x83, 0x03, 0x01, + 0x03, 0x7f, 0x02, 0x40, 0x10, 0xb3, 0x80, 0x80, 0x80, 0x00, 0x28, 0x02, + 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, + 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, + 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x34, 0x22, 0x00, 0x0d, 0x00, 0x0b, + 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xbc, 0xaa, 0x80, 0x80, 0x00, + 0x22, 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, + 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, + 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x41, + 0x00, 0x28, 0x02, 0xb0, 0x9d, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0d, + 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, + 0x18, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, + 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, + 0x22, 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, + 0xac, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xa8, + 0x9e, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, + 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, + 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, + 0x0b, 0x0b, 0x5c, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x3c, 0x22, 0x01, 0x41, 0x7f, 0x6a, 0x20, 0x01, 0x72, 0x36, 0x02, 0x3c, + 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x08, 0x71, + 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x20, 0x72, 0x36, 0x02, + 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, 0x04, + 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x01, 0x36, 0x02, 0x18, + 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x2c, 0x6a, 0x36, 0x02, 0x10, 0x41, 0x00, 0x0b, 0xe8, + 0x01, 0x01, 0x05, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x28, 0x02, + 0x10, 0x22, 0x03, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x04, 0x20, 0x02, 0x10, + 0xb6, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x01, 0x20, 0x02, 0x28, 0x02, 0x10, + 0x21, 0x03, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x20, 0x02, 0x28, 0x02, 0x14, + 0x22, 0x05, 0x6b, 0x20, 0x01, 0x4f, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x02, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x0f, 0x0b, 0x41, 0x00, 0x21, 0x06, 0x02, 0x40, 0x20, 0x02, + 0x28, 0x02, 0x40, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x06, + 0x20, 0x00, 0x21, 0x04, 0x41, 0x00, 0x21, 0x03, 0x03, 0x40, 0x20, 0x01, + 0x20, 0x03, 0x46, 0x0d, 0x01, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x21, 0x03, + 0x20, 0x04, 0x41, 0x7f, 0x6a, 0x22, 0x04, 0x20, 0x01, 0x6a, 0x22, 0x07, + 0x2d, 0x00, 0x00, 0x41, 0x0a, 0x47, 0x0d, 0x00, 0x0b, 0x20, 0x02, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x03, 0x6b, 0x41, 0x01, 0x6a, 0x22, 0x06, 0x20, + 0x02, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x22, + 0x04, 0x20, 0x06, 0x49, 0x0d, 0x01, 0x20, 0x03, 0x41, 0x7f, 0x6a, 0x21, + 0x01, 0x20, 0x07, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, 0x02, 0x28, 0x02, + 0x14, 0x21, 0x05, 0x0b, 0x20, 0x05, 0x20, 0x00, 0x20, 0x01, 0x10, 0xa8, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x14, + 0x20, 0x01, 0x6a, 0x36, 0x02, 0x14, 0x20, 0x06, 0x20, 0x01, 0x6a, 0x21, + 0x04, 0x0b, 0x20, 0x04, 0x0b, 0x95, 0x02, 0x01, 0x06, 0x7f, 0x20, 0x02, + 0x20, 0x01, 0x6c, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x28, + 0x02, 0x10, 0x22, 0x05, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x06, 0x20, 0x03, + 0x10, 0xb6, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x01, 0x20, 0x03, 0x28, 0x02, + 0x10, 0x21, 0x05, 0x0b, 0x02, 0x40, 0x20, 0x05, 0x20, 0x03, 0x28, 0x02, + 0x14, 0x22, 0x07, 0x6b, 0x20, 0x04, 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x20, + 0x00, 0x20, 0x04, 0x20, 0x03, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x21, 0x06, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x08, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x40, 0x41, 0x00, 0x4e, + 0x0d, 0x00, 0x20, 0x04, 0x21, 0x05, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, + 0x04, 0x6a, 0x21, 0x06, 0x41, 0x00, 0x21, 0x08, 0x41, 0x00, 0x21, 0x05, + 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, 0x20, 0x05, 0x6a, 0x0d, 0x00, 0x20, + 0x04, 0x21, 0x05, 0x0c, 0x02, 0x0b, 0x20, 0x05, 0x41, 0x7f, 0x6a, 0x22, + 0x05, 0x20, 0x06, 0x6a, 0x22, 0x09, 0x2d, 0x00, 0x00, 0x41, 0x0a, 0x47, + 0x0d, 0x00, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x20, 0x04, 0x20, 0x05, 0x6a, + 0x41, 0x01, 0x6a, 0x22, 0x08, 0x20, 0x03, 0x28, 0x02, 0x20, 0x11, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x22, 0x06, 0x20, 0x08, 0x49, 0x0d, 0x01, + 0x20, 0x05, 0x41, 0x7f, 0x73, 0x21, 0x05, 0x20, 0x09, 0x41, 0x01, 0x6a, + 0x21, 0x00, 0x20, 0x03, 0x28, 0x02, 0x14, 0x21, 0x07, 0x0b, 0x20, 0x07, + 0x20, 0x00, 0x20, 0x05, 0x10, 0xa8, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, + 0x03, 0x20, 0x03, 0x28, 0x02, 0x14, 0x20, 0x05, 0x6a, 0x36, 0x02, 0x14, + 0x20, 0x08, 0x20, 0x05, 0x6a, 0x21, 0x06, 0x0b, 0x02, 0x40, 0x20, 0x06, + 0x20, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x00, 0x20, 0x01, 0x1b, + 0x0f, 0x0b, 0x20, 0x06, 0x20, 0x01, 0x6e, 0x0b, 0x04, 0x00, 0x20, 0x00, + 0x0b, 0x0c, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0xb9, 0x80, 0x80, 0x80, + 0x00, 0x0b, 0x55, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, + 0xd8, 0xaa, 0x80, 0x80, 0x00, 0x22, 0x01, 0x0d, 0x00, 0x41, 0xc0, 0xaa, + 0x80, 0x80, 0x00, 0x21, 0x01, 0x41, 0x00, 0x41, 0xc0, 0xaa, 0x80, 0x80, + 0x00, 0x36, 0x02, 0xd8, 0xaa, 0x80, 0x80, 0x00, 0x0b, 0x41, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x41, 0xcc, 0x00, 0x4b, 0x1b, 0x41, 0x01, 0x74, 0x41, + 0x90, 0x94, 0x80, 0x80, 0x00, 0x6a, 0x2f, 0x01, 0x00, 0x41, 0x80, 0x88, + 0x80, 0x80, 0x00, 0x6a, 0x20, 0x01, 0x28, 0x02, 0x14, 0x10, 0xba, 0x80, + 0x80, 0x80, 0x00, 0x0b, 0xf2, 0x02, 0x01, 0x03, 0x7f, 0x20, 0x02, 0x41, + 0x00, 0x47, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x45, 0x0d, + 0x00, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x20, 0x01, 0x41, 0xff, + 0x01, 0x71, 0x47, 0x0d, 0x00, 0x20, 0x00, 0x21, 0x04, 0x20, 0x02, 0x21, + 0x05, 0x0c, 0x03, 0x0b, 0x20, 0x02, 0x41, 0x7f, 0x6a, 0x22, 0x05, 0x41, + 0x00, 0x47, 0x21, 0x03, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x04, 0x41, + 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x05, 0x45, 0x0d, 0x01, 0x20, 0x04, + 0x2d, 0x00, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x02, + 0x20, 0x02, 0x41, 0x7e, 0x6a, 0x22, 0x05, 0x41, 0x00, 0x47, 0x21, 0x03, + 0x20, 0x00, 0x41, 0x02, 0x6a, 0x22, 0x04, 0x41, 0x03, 0x71, 0x45, 0x0d, + 0x01, 0x20, 0x05, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x2d, 0x00, 0x00, 0x20, + 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x02, 0x20, 0x02, 0x41, 0x7d, + 0x6a, 0x22, 0x05, 0x41, 0x00, 0x47, 0x21, 0x03, 0x20, 0x00, 0x41, 0x03, + 0x6a, 0x22, 0x04, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x05, 0x45, + 0x0d, 0x01, 0x20, 0x04, 0x2d, 0x00, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, + 0x71, 0x46, 0x0d, 0x02, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x21, 0x04, 0x20, + 0x02, 0x41, 0x7c, 0x6a, 0x22, 0x05, 0x41, 0x00, 0x47, 0x21, 0x03, 0x0c, + 0x01, 0x0b, 0x20, 0x02, 0x21, 0x05, 0x20, 0x00, 0x21, 0x04, 0x0b, 0x20, + 0x03, 0x45, 0x0d, 0x01, 0x02, 0x40, 0x20, 0x04, 0x2d, 0x00, 0x00, 0x20, + 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x00, 0x20, 0x05, 0x41, 0x04, + 0x49, 0x0d, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x41, 0x81, 0x82, + 0x84, 0x08, 0x6c, 0x21, 0x00, 0x03, 0x40, 0x20, 0x04, 0x28, 0x02, 0x00, + 0x20, 0x00, 0x73, 0x22, 0x02, 0x41, 0x7f, 0x73, 0x20, 0x02, 0x41, 0xff, + 0xfd, 0xfb, 0x77, 0x6a, 0x71, 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, + 0x0d, 0x02, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, + 0x7c, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, + 0x05, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x21, + 0x02, 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, 0x2d, 0x00, 0x00, 0x20, 0x02, + 0x47, 0x0d, 0x00, 0x20, 0x04, 0x0f, 0x0b, 0x20, 0x04, 0x41, 0x01, 0x6a, + 0x21, 0x04, 0x20, 0x05, 0x41, 0x7f, 0x6a, 0x22, 0x05, 0x0d, 0x00, 0x0b, + 0x0b, 0x41, 0x00, 0x0b, 0x1a, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x41, 0x00, + 0x20, 0x01, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x20, 0x00, + 0x6b, 0x20, 0x01, 0x20, 0x02, 0x1b, 0x0b, 0xb6, 0x02, 0x01, 0x01, 0x7f, + 0x41, 0x01, 0x21, 0x03, 0x02, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x00, 0x02, + 0x40, 0x20, 0x01, 0x41, 0xff, 0x00, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x3a, 0x00, 0x00, 0x41, 0x01, 0x0f, 0x0b, 0x02, 0x40, 0x02, 0x40, + 0x41, 0x00, 0x28, 0x02, 0xc0, 0xaa, 0x80, 0x80, 0x00, 0x0d, 0x00, 0x02, + 0x40, 0x20, 0x01, 0x41, 0x80, 0x7f, 0x71, 0x41, 0x80, 0xbf, 0x03, 0x46, + 0x0d, 0x00, 0x41, 0x00, 0x41, 0x19, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, + 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x41, + 0x01, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x01, 0x41, 0xff, 0x0f, 0x4b, 0x0d, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, + 0x3a, 0x00, 0x01, 0x20, 0x00, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, 0xc0, + 0x01, 0x72, 0x3a, 0x00, 0x00, 0x41, 0x02, 0x0f, 0x0b, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x01, 0x41, 0x80, 0xb0, 0x03, 0x49, 0x0d, 0x00, 0x20, 0x01, + 0x41, 0x80, 0x40, 0x71, 0x41, 0x80, 0xc0, 0x03, 0x47, 0x0d, 0x01, 0x0b, + 0x20, 0x00, 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, + 0x00, 0x02, 0x20, 0x00, 0x20, 0x01, 0x41, 0x0c, 0x76, 0x41, 0xe0, 0x01, + 0x72, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, + 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x01, 0x41, 0x03, 0x0f, + 0x0b, 0x02, 0x40, 0x20, 0x01, 0x41, 0x80, 0x80, 0x7c, 0x6a, 0x41, 0xff, + 0xff, 0x3f, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x3f, 0x71, + 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x03, 0x20, 0x00, 0x20, 0x01, 0x41, + 0x12, 0x76, 0x41, 0xf0, 0x01, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x41, 0x06, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, + 0x00, 0x02, 0x20, 0x00, 0x20, 0x01, 0x41, 0x0c, 0x76, 0x41, 0x3f, 0x71, + 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x01, 0x41, 0x04, 0x0f, 0x0b, 0x41, + 0x00, 0x41, 0x19, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0b, 0x41, + 0x7f, 0x21, 0x03, 0x0b, 0x20, 0x03, 0x0b, 0x18, 0x00, 0x02, 0x40, 0x20, + 0x00, 0x0d, 0x00, 0x41, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x41, + 0x00, 0x10, 0xbe, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x8f, 0x01, 0x02, 0x01, + 0x7e, 0x01, 0x7f, 0x02, 0x40, 0x20, 0x00, 0xbd, 0x22, 0x02, 0x42, 0x34, + 0x88, 0xa7, 0x41, 0xff, 0x0f, 0x71, 0x22, 0x03, 0x41, 0xff, 0x0f, 0x46, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x03, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, 0x00, + 0x20, 0x01, 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x20, + 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x43, 0xa2, 0x20, + 0x01, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x20, 0x01, 0x20, + 0x01, 0x28, 0x02, 0x00, 0x41, 0x40, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x0f, 0x0b, 0x20, 0x01, 0x20, 0x03, 0x41, 0x82, 0x78, 0x6a, 0x36, 0x02, + 0x00, 0x20, 0x02, 0x42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, + 0x80, 0x7f, 0x83, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf0, + 0x3f, 0x84, 0xbf, 0x21, 0x00, 0x0b, 0x20, 0x00, 0x0b, 0x24, 0x01, 0x01, + 0x7f, 0x20, 0x00, 0x10, 0xaa, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, + 0x7f, 0x41, 0x00, 0x20, 0x02, 0x20, 0x00, 0x41, 0x01, 0x20, 0x02, 0x20, + 0x01, 0x10, 0xb8, 0x80, 0x80, 0x80, 0x00, 0x47, 0x1b, 0x0b, 0x8c, 0x03, + 0x01, 0x03, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xd0, 0x01, + 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, + 0x02, 0x36, 0x02, 0xcc, 0x01, 0x20, 0x03, 0x41, 0xa0, 0x01, 0x6a, 0x41, + 0x20, 0x6a, 0x42, 0x00, 0x37, 0x03, 0x00, 0x20, 0x03, 0x41, 0xb8, 0x01, + 0x6a, 0x42, 0x00, 0x37, 0x03, 0x00, 0x20, 0x03, 0x41, 0xb0, 0x01, 0x6a, + 0x42, 0x00, 0x37, 0x03, 0x00, 0x20, 0x03, 0x42, 0x00, 0x37, 0x03, 0xa8, + 0x01, 0x20, 0x03, 0x42, 0x00, 0x37, 0x03, 0xa0, 0x01, 0x20, 0x03, 0x20, + 0x02, 0x36, 0x02, 0xc8, 0x01, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x20, + 0x01, 0x20, 0x03, 0x41, 0xc8, 0x01, 0x6a, 0x20, 0x03, 0x41, 0xd0, 0x00, + 0x6a, 0x20, 0x03, 0x41, 0xa0, 0x01, 0x6a, 0x10, 0xc3, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x00, 0x4e, 0x0d, 0x00, 0x41, 0x7f, 0x21, 0x00, 0x0c, 0x01, + 0x0b, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x04, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x3c, 0x41, 0x00, 0x4a, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x04, + 0x41, 0x5f, 0x71, 0x36, 0x02, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x0d, 0x00, 0x20, 0x00, + 0x41, 0xd0, 0x00, 0x36, 0x02, 0x2c, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, + 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x10, 0x20, 0x00, 0x28, 0x02, + 0x28, 0x21, 0x05, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x28, 0x0c, 0x01, + 0x0b, 0x41, 0x00, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, 0x10, 0x0d, 0x01, + 0x0b, 0x41, 0x7f, 0x21, 0x02, 0x20, 0x00, 0x10, 0xb6, 0x80, 0x80, 0x80, + 0x00, 0x0d, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x41, 0xc8, + 0x01, 0x6a, 0x20, 0x03, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x03, 0x41, 0xa0, + 0x01, 0x6a, 0x10, 0xc3, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x0b, 0x20, + 0x04, 0x41, 0x20, 0x71, 0x21, 0x01, 0x02, 0x40, 0x20, 0x05, 0x45, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, + 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x20, 0x00, 0x41, 0x00, + 0x36, 0x02, 0x2c, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x28, 0x20, 0x00, + 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x28, 0x02, 0x14, 0x21, 0x05, + 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x10, 0x20, 0x02, 0x41, 0x7f, 0x20, + 0x05, 0x1b, 0x21, 0x02, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, + 0x22, 0x05, 0x20, 0x01, 0x72, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x20, 0x02, + 0x20, 0x05, 0x41, 0x20, 0x71, 0x1b, 0x21, 0x00, 0x0b, 0x20, 0x03, 0x41, + 0xd0, 0x01, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, 0x0b, + 0xf5, 0x46, 0x05, 0x1a, 0x7f, 0x02, 0x7e, 0x01, 0x7c, 0x08, 0x7f, 0x01, + 0x7c, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xf0, 0x06, 0x6b, 0x22, + 0x05, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x05, 0x41, 0xc4, 0x00, + 0x6a, 0x41, 0x0c, 0x6a, 0x21, 0x06, 0x41, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x00, 0x6a, 0x6b, 0x21, 0x07, 0x20, 0x05, 0x41, 0xec, 0x60, 0x6a, 0x21, + 0x08, 0x20, 0x05, 0x41, 0x37, 0x6a, 0x21, 0x09, 0x20, 0x05, 0x41, 0xc4, + 0x00, 0x6a, 0x41, 0x0b, 0x6a, 0x21, 0x0a, 0x20, 0x05, 0x41, 0xd0, 0x00, + 0x6a, 0x41, 0x7f, 0x6a, 0x21, 0x0b, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x41, 0x08, 0x72, 0x21, 0x0c, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x41, + 0x09, 0x72, 0x21, 0x0d, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x41, 0x0a, + 0x6a, 0x21, 0x0e, 0x20, 0x05, 0x41, 0x38, 0x6a, 0x21, 0x0f, 0x41, 0x00, + 0x21, 0x10, 0x41, 0x00, 0x21, 0x11, 0x41, 0x00, 0x21, 0x12, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x03, 0x40, 0x20, 0x01, 0x21, 0x13, 0x20, 0x12, + 0x20, 0x11, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x01, + 0x20, 0x12, 0x20, 0x11, 0x6a, 0x21, 0x11, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x13, 0x2d, 0x00, 0x00, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x20, + 0x13, 0x21, 0x01, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x12, 0x41, 0xff, 0x01, 0x71, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x20, 0x12, + 0x41, 0x25, 0x47, 0x0d, 0x02, 0x20, 0x01, 0x21, 0x14, 0x20, 0x01, 0x21, + 0x12, 0x03, 0x40, 0x02, 0x40, 0x20, 0x12, 0x2d, 0x00, 0x01, 0x41, 0x25, + 0x46, 0x0d, 0x00, 0x20, 0x12, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x14, + 0x41, 0x01, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x2d, 0x00, 0x02, 0x21, 0x15, + 0x20, 0x12, 0x41, 0x02, 0x6a, 0x22, 0x01, 0x21, 0x12, 0x20, 0x15, 0x41, + 0x25, 0x46, 0x0d, 0x00, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x01, 0x21, 0x14, + 0x0b, 0x20, 0x14, 0x20, 0x13, 0x6b, 0x22, 0x12, 0x20, 0x11, 0x41, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x73, 0x22, 0x14, 0x4a, 0x0d, 0x0c, 0x02, 0x40, + 0x20, 0x00, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x13, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x0d, 0x0b, 0x20, 0x01, 0x41, + 0x01, 0x6a, 0x21, 0x12, 0x41, 0x7f, 0x21, 0x16, 0x02, 0x40, 0x20, 0x01, + 0x2c, 0x00, 0x01, 0x22, 0x17, 0x41, 0x50, 0x6a, 0x22, 0x15, 0x41, 0x09, + 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x02, 0x41, 0x24, 0x47, 0x0d, + 0x00, 0x20, 0x01, 0x41, 0x03, 0x6a, 0x21, 0x12, 0x20, 0x01, 0x2c, 0x00, + 0x03, 0x21, 0x17, 0x41, 0x01, 0x21, 0x10, 0x20, 0x15, 0x21, 0x16, 0x0b, + 0x41, 0x00, 0x21, 0x18, 0x02, 0x40, 0x20, 0x17, 0x41, 0x60, 0x6a, 0x22, + 0x01, 0x41, 0x1f, 0x4b, 0x0d, 0x00, 0x41, 0x01, 0x20, 0x01, 0x74, 0x22, + 0x01, 0x41, 0x89, 0xd1, 0x04, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x12, 0x41, + 0x01, 0x6a, 0x21, 0x15, 0x41, 0x00, 0x21, 0x18, 0x03, 0x40, 0x20, 0x01, + 0x20, 0x18, 0x72, 0x21, 0x18, 0x20, 0x15, 0x22, 0x12, 0x2c, 0x00, 0x00, + 0x22, 0x17, 0x41, 0x60, 0x6a, 0x22, 0x01, 0x41, 0x20, 0x4f, 0x0d, 0x01, + 0x20, 0x12, 0x41, 0x01, 0x6a, 0x21, 0x15, 0x41, 0x01, 0x20, 0x01, 0x74, + 0x22, 0x01, 0x41, 0x89, 0xd1, 0x04, 0x71, 0x0d, 0x00, 0x0b, 0x0b, 0x02, + 0x40, 0x20, 0x17, 0x41, 0x2a, 0x47, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x12, 0x2c, 0x00, 0x01, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x09, + 0x4b, 0x0d, 0x00, 0x20, 0x12, 0x2d, 0x00, 0x02, 0x41, 0x24, 0x47, 0x0d, + 0x00, 0x20, 0x04, 0x20, 0x01, 0x41, 0x02, 0x74, 0x6a, 0x41, 0x0a, 0x36, + 0x02, 0x00, 0x20, 0x12, 0x41, 0x03, 0x6a, 0x21, 0x15, 0x20, 0x12, 0x2c, + 0x00, 0x01, 0x41, 0x03, 0x74, 0x20, 0x03, 0x6a, 0x41, 0x80, 0x7d, 0x6a, + 0x28, 0x02, 0x00, 0x21, 0x19, 0x41, 0x01, 0x21, 0x10, 0x0c, 0x01, 0x0b, + 0x20, 0x10, 0x0d, 0x06, 0x20, 0x12, 0x41, 0x01, 0x6a, 0x21, 0x15, 0x02, + 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x10, 0x41, 0x00, 0x21, + 0x19, 0x0c, 0x06, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, + 0x21, 0x19, 0x41, 0x00, 0x21, 0x10, 0x0b, 0x20, 0x19, 0x41, 0x7f, 0x4a, + 0x0d, 0x04, 0x41, 0x00, 0x20, 0x19, 0x6b, 0x21, 0x19, 0x20, 0x18, 0x41, + 0x80, 0xc0, 0x00, 0x72, 0x21, 0x18, 0x0c, 0x04, 0x0b, 0x41, 0x00, 0x21, + 0x19, 0x02, 0x40, 0x20, 0x17, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x09, + 0x4d, 0x0d, 0x00, 0x20, 0x12, 0x21, 0x15, 0x0c, 0x04, 0x0b, 0x41, 0x00, + 0x21, 0x19, 0x03, 0x40, 0x02, 0x40, 0x20, 0x19, 0x41, 0xcc, 0x99, 0xb3, + 0xe6, 0x00, 0x4b, 0x0d, 0x00, 0x41, 0x7f, 0x20, 0x19, 0x41, 0x0a, 0x6c, + 0x22, 0x15, 0x20, 0x01, 0x6a, 0x20, 0x01, 0x20, 0x15, 0x41, 0xff, 0xff, + 0xff, 0xff, 0x07, 0x73, 0x4b, 0x1b, 0x21, 0x19, 0x20, 0x12, 0x2c, 0x00, + 0x01, 0x21, 0x01, 0x20, 0x12, 0x41, 0x01, 0x6a, 0x22, 0x15, 0x21, 0x12, + 0x20, 0x01, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x0a, 0x49, 0x0d, 0x01, + 0x20, 0x19, 0x41, 0x00, 0x48, 0x0d, 0x0e, 0x0c, 0x05, 0x0b, 0x20, 0x12, + 0x2c, 0x00, 0x01, 0x21, 0x01, 0x41, 0x7f, 0x21, 0x19, 0x20, 0x12, 0x41, + 0x01, 0x6a, 0x21, 0x12, 0x20, 0x01, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, + 0x0a, 0x49, 0x0d, 0x00, 0x0c, 0x0d, 0x0b, 0x0b, 0x20, 0x01, 0x2d, 0x00, + 0x01, 0x21, 0x12, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, + 0x0b, 0x0b, 0x20, 0x00, 0x0d, 0x0b, 0x02, 0x40, 0x20, 0x10, 0x0d, 0x00, + 0x41, 0x00, 0x21, 0x11, 0x0c, 0x0c, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x04, 0x28, 0x02, 0x04, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x01, + 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x01, + 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, + 0x28, 0x02, 0x08, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x02, 0x21, 0x01, 0x0c, + 0x01, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, + 0xc4, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x0c, + 0x22, 0x01, 0x0d, 0x00, 0x41, 0x03, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, + 0x03, 0x41, 0x18, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, + 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x10, 0x22, 0x01, 0x0d, + 0x00, 0x41, 0x04, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x20, + 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, 0x80, 0x00, 0x02, + 0x40, 0x20, 0x04, 0x28, 0x02, 0x14, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x05, + 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x28, 0x6a, 0x20, 0x01, + 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, + 0x28, 0x02, 0x18, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x06, 0x21, 0x01, 0x0c, + 0x01, 0x0b, 0x20, 0x03, 0x41, 0x30, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, + 0xc4, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x1c, + 0x22, 0x01, 0x0d, 0x00, 0x41, 0x07, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, + 0x03, 0x41, 0x38, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, + 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x20, 0x22, 0x01, 0x0d, + 0x00, 0x41, 0x08, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0xc0, + 0x00, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x04, 0x28, 0x02, 0x24, 0x22, 0x01, 0x0d, 0x01, 0x41, 0x09, 0x21, + 0x01, 0x0b, 0x20, 0x01, 0x41, 0x02, 0x74, 0x21, 0x01, 0x03, 0x40, 0x20, + 0x04, 0x20, 0x01, 0x6a, 0x28, 0x02, 0x00, 0x0d, 0x03, 0x20, 0x01, 0x41, + 0x04, 0x6a, 0x22, 0x01, 0x41, 0x28, 0x47, 0x0d, 0x00, 0x0b, 0x41, 0x01, + 0x21, 0x11, 0x0c, 0x0c, 0x0b, 0x20, 0x03, 0x41, 0xc8, 0x00, 0x6a, 0x20, + 0x01, 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, 0x80, 0x00, 0x41, 0x01, 0x21, + 0x11, 0x0c, 0x0b, 0x0b, 0x41, 0x00, 0x21, 0x12, 0x41, 0x7f, 0x21, 0x17, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x15, 0x2d, 0x00, 0x00, 0x41, 0x2e, 0x46, + 0x0d, 0x00, 0x20, 0x15, 0x21, 0x01, 0x41, 0x00, 0x21, 0x1a, 0x0c, 0x01, + 0x0b, 0x02, 0x40, 0x20, 0x15, 0x2c, 0x00, 0x01, 0x22, 0x17, 0x41, 0x2a, + 0x47, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x15, 0x2c, 0x00, 0x02, + 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x09, 0x4b, 0x0d, 0x00, 0x20, 0x15, + 0x2d, 0x00, 0x03, 0x41, 0x24, 0x47, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x01, + 0x41, 0x02, 0x74, 0x6a, 0x41, 0x0a, 0x36, 0x02, 0x00, 0x20, 0x15, 0x41, + 0x04, 0x6a, 0x21, 0x01, 0x20, 0x15, 0x2c, 0x00, 0x02, 0x41, 0x03, 0x74, + 0x20, 0x03, 0x6a, 0x41, 0x80, 0x7d, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x17, + 0x0c, 0x01, 0x0b, 0x20, 0x10, 0x0d, 0x03, 0x20, 0x15, 0x41, 0x02, 0x6a, + 0x21, 0x01, 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x17, + 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x15, + 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x15, 0x28, 0x02, 0x00, 0x21, + 0x17, 0x0b, 0x20, 0x17, 0x41, 0x7f, 0x73, 0x41, 0x1f, 0x76, 0x21, 0x1a, + 0x0c, 0x01, 0x0b, 0x20, 0x15, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x02, 0x40, + 0x20, 0x17, 0x41, 0x50, 0x6a, 0x22, 0x1b, 0x41, 0x09, 0x4d, 0x0d, 0x00, + 0x41, 0x01, 0x21, 0x1a, 0x41, 0x00, 0x21, 0x17, 0x0c, 0x01, 0x0b, 0x41, + 0x00, 0x21, 0x1c, 0x20, 0x01, 0x21, 0x15, 0x03, 0x40, 0x41, 0x7f, 0x21, + 0x17, 0x02, 0x40, 0x20, 0x1c, 0x41, 0xcc, 0x99, 0xb3, 0xe6, 0x00, 0x4b, + 0x0d, 0x00, 0x41, 0x7f, 0x20, 0x1c, 0x41, 0x0a, 0x6c, 0x22, 0x01, 0x20, + 0x1b, 0x6a, 0x20, 0x1b, 0x20, 0x01, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x73, 0x4b, 0x1b, 0x21, 0x17, 0x0b, 0x41, 0x01, 0x21, 0x1a, 0x20, 0x15, + 0x2c, 0x00, 0x01, 0x21, 0x1b, 0x20, 0x17, 0x21, 0x1c, 0x20, 0x15, 0x41, + 0x01, 0x6a, 0x22, 0x01, 0x21, 0x15, 0x20, 0x1b, 0x41, 0x50, 0x6a, 0x22, + 0x1b, 0x41, 0x0a, 0x49, 0x0d, 0x00, 0x0b, 0x0b, 0x03, 0x40, 0x20, 0x12, + 0x21, 0x15, 0x20, 0x01, 0x2c, 0x00, 0x00, 0x22, 0x12, 0x41, 0x85, 0x7f, + 0x6a, 0x41, 0x46, 0x49, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, + 0x01, 0x20, 0x12, 0x20, 0x15, 0x41, 0x3a, 0x6c, 0x6a, 0x41, 0x9f, 0x98, + 0x80, 0x80, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x12, 0x41, 0x7f, 0x6a, + 0x41, 0x08, 0x49, 0x0d, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x12, 0x41, 0x1b, 0x46, 0x0d, 0x00, 0x20, 0x12, 0x45, 0x0d, 0x03, + 0x02, 0x40, 0x20, 0x16, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x20, 0x04, 0x20, + 0x16, 0x41, 0x02, 0x74, 0x6a, 0x20, 0x12, 0x36, 0x02, 0x00, 0x20, 0x05, + 0x20, 0x03, 0x20, 0x16, 0x41, 0x03, 0x74, 0x6a, 0x29, 0x03, 0x00, 0x37, + 0x03, 0x38, 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, + 0x00, 0x21, 0x11, 0x0c, 0x0e, 0x0b, 0x20, 0x05, 0x41, 0x38, 0x6a, 0x20, + 0x12, 0x20, 0x02, 0x10, 0xc4, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, + 0x20, 0x16, 0x41, 0x7f, 0x4a, 0x0d, 0x02, 0x0b, 0x41, 0x00, 0x21, 0x12, + 0x20, 0x00, 0x45, 0x0d, 0x08, 0x0b, 0x20, 0x18, 0x41, 0xff, 0xff, 0x7b, + 0x71, 0x22, 0x1c, 0x20, 0x18, 0x20, 0x18, 0x41, 0x80, 0xc0, 0x00, 0x71, + 0x1b, 0x21, 0x16, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x01, 0x41, 0x7f, 0x6a, 0x2c, 0x00, 0x00, 0x22, 0x12, 0x41, + 0x5f, 0x71, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0f, 0x71, 0x41, 0x03, 0x46, + 0x1b, 0x20, 0x12, 0x20, 0x15, 0x1b, 0x22, 0x1d, 0x41, 0xbf, 0x7f, 0x6a, + 0x0e, 0x38, 0x10, 0x12, 0x0d, 0x12, 0x10, 0x10, 0x10, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0c, 0x12, 0x12, 0x12, + 0x12, 0x03, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x10, 0x12, + 0x08, 0x05, 0x10, 0x10, 0x10, 0x12, 0x05, 0x12, 0x12, 0x12, 0x09, 0x01, + 0x04, 0x02, 0x12, 0x12, 0x0a, 0x12, 0x00, 0x12, 0x12, 0x03, 0x12, 0x0b, + 0x41, 0x00, 0x21, 0x1b, 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, + 0x20, 0x05, 0x29, 0x03, 0x38, 0x21, 0x1f, 0x0c, 0x05, 0x0b, 0x41, 0x00, + 0x21, 0x12, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x15, 0x41, 0xff, 0x01, 0x71, 0x0e, 0x08, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x1d, 0x05, 0x06, 0x1d, 0x0b, 0x20, 0x05, + 0x28, 0x02, 0x38, 0x20, 0x11, 0x36, 0x02, 0x00, 0x0c, 0x1c, 0x0b, 0x20, + 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x36, 0x02, 0x00, 0x0c, 0x1b, 0x0b, + 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0xac, 0x37, 0x03, 0x00, 0x0c, + 0x1a, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x3b, 0x01, 0x00, + 0x0c, 0x19, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x3a, 0x00, + 0x00, 0x0c, 0x18, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x36, + 0x02, 0x00, 0x0c, 0x17, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, + 0xac, 0x37, 0x03, 0x00, 0x0c, 0x16, 0x0b, 0x20, 0x17, 0x41, 0x08, 0x20, + 0x17, 0x41, 0x08, 0x4b, 0x1b, 0x21, 0x17, 0x20, 0x16, 0x41, 0x08, 0x72, + 0x21, 0x16, 0x41, 0xf8, 0x00, 0x21, 0x1d, 0x0b, 0x41, 0x00, 0x21, 0x1b, + 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x02, 0x40, 0x20, 0x05, + 0x29, 0x03, 0x38, 0x22, 0x1f, 0x50, 0x45, 0x0d, 0x00, 0x20, 0x0f, 0x21, + 0x13, 0x0c, 0x04, 0x0b, 0x20, 0x1d, 0x41, 0x20, 0x71, 0x21, 0x15, 0x20, + 0x0f, 0x21, 0x13, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, 0x13, + 0x20, 0x1f, 0xa7, 0x41, 0x0f, 0x71, 0x41, 0xb0, 0x9c, 0x80, 0x80, 0x00, + 0x6a, 0x2d, 0x00, 0x00, 0x20, 0x15, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x1f, + 0x42, 0x0f, 0x56, 0x21, 0x12, 0x20, 0x1f, 0x42, 0x04, 0x88, 0x21, 0x1f, + 0x20, 0x12, 0x0d, 0x00, 0x0b, 0x20, 0x16, 0x41, 0x08, 0x71, 0x45, 0x0d, + 0x03, 0x20, 0x1d, 0x41, 0x04, 0x75, 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, + 0x6a, 0x21, 0x1e, 0x41, 0x02, 0x21, 0x1b, 0x0c, 0x03, 0x0b, 0x20, 0x0f, + 0x21, 0x13, 0x02, 0x40, 0x20, 0x05, 0x29, 0x03, 0x38, 0x22, 0x1f, 0x50, + 0x0d, 0x00, 0x20, 0x0f, 0x21, 0x13, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, + 0x6a, 0x22, 0x13, 0x20, 0x1f, 0xa7, 0x41, 0x07, 0x71, 0x41, 0x30, 0x72, + 0x3a, 0x00, 0x00, 0x20, 0x1f, 0x42, 0x07, 0x56, 0x21, 0x12, 0x20, 0x1f, + 0x42, 0x03, 0x88, 0x21, 0x1f, 0x20, 0x12, 0x0d, 0x00, 0x0b, 0x0b, 0x41, + 0x00, 0x21, 0x1b, 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, + 0x16, 0x41, 0x08, 0x71, 0x45, 0x0d, 0x02, 0x20, 0x17, 0x20, 0x0f, 0x20, + 0x13, 0x6b, 0x22, 0x12, 0x41, 0x01, 0x6a, 0x20, 0x17, 0x20, 0x12, 0x4a, + 0x1b, 0x21, 0x17, 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x20, 0x05, 0x29, 0x03, + 0x38, 0x22, 0x1f, 0x42, 0x7f, 0x55, 0x0d, 0x00, 0x20, 0x05, 0x42, 0x00, + 0x20, 0x1f, 0x7d, 0x22, 0x1f, 0x37, 0x03, 0x38, 0x41, 0x01, 0x21, 0x1b, + 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x0c, 0x01, 0x0b, 0x02, + 0x40, 0x20, 0x16, 0x41, 0x80, 0x10, 0x71, 0x45, 0x0d, 0x00, 0x41, 0x01, + 0x21, 0x1b, 0x41, 0xab, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x0c, 0x01, + 0x0b, 0x41, 0xac, 0x95, 0x80, 0x80, 0x00, 0x41, 0xaa, 0x95, 0x80, 0x80, + 0x00, 0x20, 0x16, 0x41, 0x01, 0x71, 0x22, 0x1b, 0x1b, 0x21, 0x1e, 0x0b, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x1f, 0x42, 0x80, 0x80, 0x80, 0x80, 0x10, + 0x5a, 0x0d, 0x00, 0x20, 0x1f, 0x21, 0x20, 0x20, 0x0f, 0x21, 0x13, 0x0c, + 0x01, 0x0b, 0x20, 0x0f, 0x21, 0x13, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, + 0x6a, 0x22, 0x13, 0x20, 0x1f, 0x20, 0x1f, 0x42, 0x0a, 0x80, 0x22, 0x20, + 0x42, 0x0a, 0x7e, 0x7d, 0xa7, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, + 0x1f, 0x42, 0xff, 0xff, 0xff, 0xff, 0x9f, 0x01, 0x56, 0x21, 0x12, 0x20, + 0x20, 0x21, 0x1f, 0x20, 0x12, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x20, 0xa7, + 0x22, 0x12, 0x45, 0x0d, 0x00, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, 0x6a, + 0x22, 0x13, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x15, 0x41, + 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x12, 0x41, + 0x09, 0x4b, 0x21, 0x18, 0x20, 0x15, 0x21, 0x12, 0x20, 0x18, 0x0d, 0x00, + 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x1a, 0x45, 0x0d, 0x00, 0x20, 0x17, 0x41, + 0x00, 0x48, 0x0d, 0x12, 0x0b, 0x20, 0x16, 0x41, 0xff, 0xff, 0x7b, 0x71, + 0x20, 0x16, 0x20, 0x1a, 0x1b, 0x21, 0x1c, 0x02, 0x40, 0x20, 0x05, 0x29, + 0x03, 0x38, 0x22, 0x1f, 0x42, 0x00, 0x52, 0x0d, 0x00, 0x41, 0x00, 0x21, + 0x18, 0x20, 0x17, 0x0d, 0x00, 0x20, 0x0f, 0x21, 0x13, 0x20, 0x0f, 0x21, + 0x12, 0x0c, 0x0c, 0x0b, 0x20, 0x17, 0x20, 0x0f, 0x20, 0x13, 0x6b, 0x20, + 0x1f, 0x50, 0x6a, 0x22, 0x12, 0x20, 0x17, 0x20, 0x12, 0x4a, 0x1b, 0x21, + 0x18, 0x20, 0x0f, 0x21, 0x12, 0x0c, 0x0b, 0x0b, 0x20, 0x05, 0x20, 0x05, + 0x29, 0x03, 0x38, 0x3c, 0x00, 0x37, 0x41, 0x00, 0x21, 0x1b, 0x41, 0xaa, + 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x41, 0x01, 0x21, 0x18, 0x20, 0x09, + 0x21, 0x13, 0x20, 0x0f, 0x21, 0x12, 0x0c, 0x0a, 0x0b, 0x41, 0xb4, 0x9e, + 0x80, 0x80, 0x00, 0x28, 0x02, 0x00, 0x10, 0xbb, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x13, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x22, 0x12, + 0x41, 0x81, 0x96, 0x80, 0x80, 0x00, 0x20, 0x12, 0x1b, 0x21, 0x13, 0x0b, + 0x20, 0x13, 0x20, 0x13, 0x20, 0x17, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x20, 0x17, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x1b, 0x10, 0xbd, + 0x80, 0x80, 0x80, 0x00, 0x22, 0x18, 0x6a, 0x21, 0x12, 0x41, 0x00, 0x21, + 0x1b, 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, 0x17, 0x41, + 0x7f, 0x4a, 0x0d, 0x07, 0x20, 0x12, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x07, + 0x0c, 0x0d, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x21, 0x13, 0x20, 0x17, + 0x0d, 0x01, 0x41, 0x00, 0x21, 0x12, 0x0c, 0x02, 0x0b, 0x20, 0x05, 0x41, + 0x00, 0x36, 0x02, 0x0c, 0x20, 0x05, 0x20, 0x05, 0x29, 0x03, 0x38, 0x3e, + 0x02, 0x08, 0x20, 0x05, 0x20, 0x05, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x38, + 0x20, 0x05, 0x41, 0x08, 0x6a, 0x21, 0x13, 0x41, 0x7f, 0x21, 0x17, 0x0b, + 0x41, 0x00, 0x21, 0x12, 0x20, 0x13, 0x21, 0x14, 0x02, 0x40, 0x03, 0x40, + 0x20, 0x14, 0x28, 0x02, 0x00, 0x22, 0x15, 0x45, 0x0d, 0x01, 0x02, 0x40, + 0x20, 0x05, 0x41, 0x04, 0x6a, 0x20, 0x15, 0x10, 0xbf, 0x80, 0x80, 0x80, + 0x00, 0x22, 0x15, 0x41, 0x00, 0x48, 0x22, 0x18, 0x0d, 0x00, 0x20, 0x15, + 0x20, 0x17, 0x20, 0x12, 0x6b, 0x4b, 0x0d, 0x00, 0x20, 0x14, 0x41, 0x04, + 0x6a, 0x21, 0x14, 0x20, 0x15, 0x20, 0x12, 0x6a, 0x22, 0x12, 0x20, 0x17, + 0x49, 0x0d, 0x01, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x18, 0x0d, 0x0c, 0x0b, + 0x20, 0x12, 0x41, 0x00, 0x48, 0x0d, 0x0a, 0x0b, 0x02, 0x40, 0x20, 0x16, + 0x41, 0x80, 0xc0, 0x04, 0x71, 0x22, 0x18, 0x0d, 0x00, 0x20, 0x19, 0x20, + 0x12, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, + 0x20, 0x19, 0x20, 0x12, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, + 0x41, 0x80, 0x02, 0x49, 0x22, 0x15, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x02, 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, + 0x14, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, + 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x02, 0x40, 0x20, 0x12, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x14, + 0x03, 0x40, 0x20, 0x13, 0x28, 0x02, 0x00, 0x22, 0x15, 0x45, 0x0d, 0x01, + 0x20, 0x05, 0x41, 0x04, 0x6a, 0x20, 0x15, 0x10, 0xbf, 0x80, 0x80, 0x80, + 0x00, 0x22, 0x15, 0x20, 0x14, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x4b, 0x0d, + 0x01, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0x04, 0x6a, 0x20, 0x15, 0x20, 0x00, 0x10, 0xb7, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x13, 0x41, 0x04, 0x6a, 0x21, + 0x13, 0x20, 0x14, 0x20, 0x12, 0x49, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, + 0x20, 0x18, 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, + 0x12, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, + 0x20, 0x19, 0x20, 0x12, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, + 0x41, 0x80, 0x02, 0x49, 0x22, 0x15, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x02, 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, + 0x14, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, + 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x19, 0x20, 0x12, 0x20, 0x19, 0x20, 0x12, 0x4a, 0x1b, 0x21, + 0x12, 0x0c, 0x08, 0x0b, 0x02, 0x40, 0x20, 0x1a, 0x45, 0x0d, 0x00, 0x20, + 0x17, 0x41, 0x00, 0x48, 0x0d, 0x09, 0x0b, 0x20, 0x05, 0x2b, 0x03, 0x38, + 0x21, 0x21, 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x6c, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x21, 0xbd, 0x42, 0x7f, 0x55, 0x0d, 0x00, 0x20, 0x21, 0x9a, + 0x21, 0x21, 0x41, 0x01, 0x21, 0x22, 0x41, 0x00, 0x21, 0x23, 0x41, 0xb4, + 0x95, 0x80, 0x80, 0x00, 0x21, 0x24, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, + 0x16, 0x41, 0x80, 0x10, 0x71, 0x45, 0x0d, 0x00, 0x41, 0x01, 0x21, 0x22, + 0x41, 0x00, 0x21, 0x23, 0x41, 0xb7, 0x95, 0x80, 0x80, 0x00, 0x21, 0x24, + 0x0c, 0x01, 0x0b, 0x41, 0xba, 0x95, 0x80, 0x80, 0x00, 0x41, 0xb5, 0x95, + 0x80, 0x80, 0x00, 0x20, 0x16, 0x41, 0x01, 0x71, 0x22, 0x22, 0x1b, 0x21, + 0x24, 0x20, 0x22, 0x45, 0x21, 0x23, 0x0b, 0x02, 0x40, 0x20, 0x21, 0xbd, + 0x42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x83, + 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf8, 0xff, 0x00, 0x53, + 0x0d, 0x00, 0x20, 0x22, 0x41, 0x03, 0x6a, 0x21, 0x14, 0x02, 0x40, 0x20, + 0x16, 0x41, 0x80, 0xc0, 0x00, 0x71, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x14, + 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, + 0x19, 0x20, 0x14, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, + 0x80, 0x02, 0x49, 0x22, 0x15, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x02, 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, + 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, + 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, + 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x12, 0x41, 0x20, 0x71, + 0x0d, 0x00, 0x20, 0x24, 0x20, 0x22, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x12, 0x0b, 0x02, + 0x40, 0x20, 0x12, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x41, 0xe8, 0x95, 0x80, + 0x80, 0x00, 0x41, 0xf4, 0x95, 0x80, 0x80, 0x00, 0x20, 0x1d, 0x41, 0x20, + 0x71, 0x22, 0x12, 0x1b, 0x41, 0xec, 0x95, 0x80, 0x80, 0x00, 0x41, 0xf8, + 0x95, 0x80, 0x80, 0x00, 0x20, 0x12, 0x1b, 0x20, 0x21, 0x20, 0x21, 0x62, + 0x1b, 0x41, 0x03, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x02, 0x40, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x41, 0x80, + 0xc0, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x14, 0x4c, 0x0d, 0x00, + 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x14, + 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, + 0x22, 0x15, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, + 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, + 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, + 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, + 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x20, + 0x19, 0x20, 0x14, 0x20, 0x19, 0x4a, 0x1b, 0x21, 0x12, 0x0c, 0x08, 0x0b, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x21, 0x20, 0x05, 0x41, 0xec, + 0x00, 0x6a, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x22, 0x21, 0x20, 0x21, + 0xa0, 0x22, 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x05, 0x28, 0x02, 0x6c, 0x22, 0x12, + 0x41, 0x7f, 0x6a, 0x36, 0x02, 0x6c, 0x20, 0x1d, 0x41, 0x20, 0x72, 0x22, + 0x25, 0x41, 0xe1, 0x00, 0x47, 0x0d, 0x01, 0x0c, 0x08, 0x0b, 0x20, 0x1d, + 0x41, 0x20, 0x72, 0x22, 0x25, 0x41, 0xe1, 0x00, 0x46, 0x0d, 0x07, 0x41, + 0x06, 0x20, 0x17, 0x20, 0x17, 0x41, 0x00, 0x48, 0x1b, 0x21, 0x1a, 0x20, + 0x05, 0x28, 0x02, 0x6c, 0x21, 0x13, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x20, + 0x12, 0x41, 0x63, 0x6a, 0x22, 0x13, 0x36, 0x02, 0x6c, 0x41, 0x06, 0x20, + 0x17, 0x20, 0x17, 0x41, 0x00, 0x48, 0x1b, 0x21, 0x1a, 0x20, 0x21, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x41, 0xa2, 0x21, 0x21, 0x0b, + 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x00, 0x41, 0xc8, 0x00, 0x20, + 0x13, 0x41, 0x00, 0x48, 0x22, 0x26, 0x1b, 0x41, 0x02, 0x74, 0x22, 0x27, + 0x6a, 0x22, 0x1e, 0x21, 0x14, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x41, 0x63, 0x20, + 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x71, + 0x45, 0x0d, 0x00, 0x20, 0x21, 0xab, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x41, + 0x00, 0x21, 0x12, 0x0b, 0x20, 0x14, 0x20, 0x12, 0x36, 0x02, 0x00, 0x20, + 0x14, 0x41, 0x04, 0x6a, 0x21, 0x14, 0x20, 0x21, 0x20, 0x12, 0xb8, 0xa1, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x65, 0xcd, 0xcd, 0x41, 0xa2, 0x22, 0x21, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, 0x00, + 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x13, 0x41, 0x01, 0x4e, 0x0d, 0x00, + 0x20, 0x14, 0x21, 0x12, 0x20, 0x1e, 0x21, 0x15, 0x0c, 0x01, 0x0b, 0x20, + 0x1e, 0x21, 0x15, 0x03, 0x40, 0x20, 0x13, 0x41, 0x1d, 0x20, 0x13, 0x41, + 0x1d, 0x48, 0x1b, 0x21, 0x13, 0x02, 0x40, 0x20, 0x14, 0x41, 0x7c, 0x6a, + 0x22, 0x12, 0x20, 0x15, 0x49, 0x0d, 0x00, 0x20, 0x13, 0xad, 0x21, 0x20, + 0x42, 0x00, 0x21, 0x1f, 0x03, 0x40, 0x20, 0x12, 0x20, 0x12, 0x35, 0x02, + 0x00, 0x20, 0x20, 0x86, 0x20, 0x1f, 0x42, 0xff, 0xff, 0xff, 0xff, 0x0f, + 0x83, 0x7c, 0x22, 0x1f, 0x20, 0x1f, 0x42, 0x80, 0x94, 0xeb, 0xdc, 0x03, + 0x80, 0x22, 0x1f, 0x42, 0x80, 0x94, 0xeb, 0xdc, 0x03, 0x7e, 0x7d, 0x3e, + 0x02, 0x00, 0x20, 0x12, 0x41, 0x7c, 0x6a, 0x22, 0x12, 0x20, 0x15, 0x4f, + 0x0d, 0x00, 0x0b, 0x20, 0x1f, 0xa7, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x20, + 0x15, 0x41, 0x7c, 0x6a, 0x22, 0x15, 0x20, 0x12, 0x36, 0x02, 0x00, 0x0b, + 0x02, 0x40, 0x03, 0x40, 0x20, 0x14, 0x22, 0x12, 0x20, 0x15, 0x4d, 0x0d, + 0x01, 0x20, 0x12, 0x41, 0x7c, 0x6a, 0x22, 0x14, 0x28, 0x02, 0x00, 0x45, + 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x28, 0x02, 0x6c, 0x20, + 0x13, 0x6b, 0x22, 0x13, 0x36, 0x02, 0x6c, 0x20, 0x12, 0x21, 0x14, 0x20, + 0x13, 0x41, 0x00, 0x4a, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x13, + 0x41, 0x7f, 0x4a, 0x0d, 0x00, 0x20, 0x1a, 0x41, 0x19, 0x6a, 0x41, 0x09, + 0x6e, 0x41, 0x01, 0x6a, 0x21, 0x28, 0x03, 0x40, 0x41, 0x00, 0x20, 0x13, + 0x6b, 0x22, 0x14, 0x41, 0x09, 0x20, 0x14, 0x41, 0x09, 0x48, 0x1b, 0x21, + 0x17, 0x02, 0x40, 0x02, 0x40, 0x20, 0x15, 0x20, 0x12, 0x49, 0x0d, 0x00, + 0x20, 0x15, 0x28, 0x02, 0x00, 0x21, 0x14, 0x0c, 0x01, 0x0b, 0x41, 0x80, + 0x94, 0xeb, 0xdc, 0x03, 0x20, 0x17, 0x76, 0x21, 0x1c, 0x41, 0x7f, 0x20, + 0x17, 0x74, 0x41, 0x7f, 0x73, 0x21, 0x1b, 0x41, 0x00, 0x21, 0x13, 0x20, + 0x15, 0x21, 0x14, 0x03, 0x40, 0x20, 0x14, 0x20, 0x14, 0x28, 0x02, 0x00, + 0x22, 0x18, 0x20, 0x17, 0x76, 0x20, 0x13, 0x6a, 0x36, 0x02, 0x00, 0x20, + 0x18, 0x20, 0x1b, 0x71, 0x20, 0x1c, 0x6c, 0x21, 0x13, 0x20, 0x14, 0x41, + 0x04, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x49, 0x0d, 0x00, 0x0b, 0x20, 0x15, + 0x28, 0x02, 0x00, 0x21, 0x14, 0x20, 0x13, 0x45, 0x0d, 0x00, 0x20, 0x12, + 0x20, 0x13, 0x36, 0x02, 0x00, 0x20, 0x12, 0x41, 0x04, 0x6a, 0x21, 0x12, + 0x0b, 0x20, 0x05, 0x20, 0x05, 0x28, 0x02, 0x6c, 0x20, 0x17, 0x6a, 0x22, + 0x13, 0x36, 0x02, 0x6c, 0x20, 0x1e, 0x20, 0x15, 0x20, 0x14, 0x45, 0x41, + 0x02, 0x74, 0x6a, 0x22, 0x15, 0x20, 0x25, 0x41, 0xe6, 0x00, 0x46, 0x1b, + 0x22, 0x14, 0x20, 0x28, 0x41, 0x02, 0x74, 0x6a, 0x20, 0x12, 0x20, 0x12, + 0x20, 0x14, 0x6b, 0x41, 0x02, 0x75, 0x20, 0x28, 0x4a, 0x1b, 0x21, 0x12, + 0x20, 0x13, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x00, 0x21, + 0x18, 0x02, 0x40, 0x20, 0x15, 0x20, 0x12, 0x4f, 0x0d, 0x00, 0x20, 0x1e, + 0x20, 0x15, 0x6b, 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x21, 0x18, 0x20, + 0x15, 0x28, 0x02, 0x00, 0x22, 0x13, 0x41, 0x0a, 0x49, 0x0d, 0x00, 0x41, + 0x0a, 0x21, 0x14, 0x03, 0x40, 0x20, 0x18, 0x41, 0x01, 0x6a, 0x21, 0x18, + 0x20, 0x13, 0x20, 0x14, 0x41, 0x0a, 0x6c, 0x22, 0x14, 0x4f, 0x0d, 0x00, + 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x1a, 0x41, 0x00, 0x20, 0x18, 0x20, 0x25, + 0x41, 0xe6, 0x00, 0x46, 0x1b, 0x6b, 0x20, 0x1a, 0x41, 0x00, 0x47, 0x20, + 0x25, 0x41, 0xe7, 0x00, 0x46, 0x22, 0x1b, 0x71, 0x6b, 0x22, 0x14, 0x20, + 0x12, 0x20, 0x1e, 0x6b, 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x41, 0x77, + 0x6a, 0x4e, 0x0d, 0x00, 0x20, 0x14, 0x41, 0x80, 0xc8, 0x00, 0x6a, 0x22, + 0x13, 0x41, 0x09, 0x6d, 0x22, 0x17, 0x41, 0x02, 0x74, 0x22, 0x29, 0x20, + 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x01, 0x41, 0xc9, 0x00, 0x20, 0x26, + 0x1b, 0x41, 0x02, 0x74, 0x22, 0x26, 0x6a, 0x6a, 0x41, 0x80, 0x60, 0x6a, + 0x21, 0x1c, 0x41, 0x0a, 0x21, 0x14, 0x02, 0x40, 0x20, 0x13, 0x20, 0x17, + 0x41, 0x09, 0x6c, 0x6b, 0x22, 0x17, 0x41, 0x07, 0x4a, 0x0d, 0x00, 0x41, + 0x08, 0x20, 0x17, 0x6b, 0x22, 0x28, 0x41, 0x07, 0x71, 0x21, 0x13, 0x41, + 0x0a, 0x21, 0x14, 0x02, 0x40, 0x20, 0x17, 0x41, 0x7f, 0x6a, 0x41, 0x07, + 0x49, 0x0d, 0x00, 0x20, 0x28, 0x41, 0x78, 0x71, 0x21, 0x17, 0x41, 0x0a, + 0x21, 0x14, 0x03, 0x40, 0x20, 0x14, 0x41, 0x80, 0xc2, 0xd7, 0x2f, 0x6c, + 0x21, 0x14, 0x20, 0x17, 0x41, 0x78, 0x6a, 0x22, 0x17, 0x0d, 0x00, 0x0b, + 0x0b, 0x20, 0x13, 0x45, 0x0d, 0x00, 0x03, 0x40, 0x20, 0x14, 0x41, 0x0a, + 0x6c, 0x21, 0x14, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, 0x13, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x1c, 0x41, 0x04, 0x6a, 0x21, 0x28, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x1c, 0x28, 0x02, 0x00, 0x22, 0x13, 0x20, 0x13, 0x20, 0x14, + 0x6e, 0x22, 0x25, 0x20, 0x14, 0x6c, 0x6b, 0x22, 0x17, 0x0d, 0x00, 0x20, + 0x28, 0x20, 0x12, 0x46, 0x0d, 0x01, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x25, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x43, 0x21, 0x21, 0x20, 0x14, 0x41, 0x80, 0x94, 0xeb, 0xdc, + 0x03, 0x47, 0x0d, 0x01, 0x20, 0x1c, 0x20, 0x15, 0x4d, 0x0d, 0x01, 0x20, + 0x1c, 0x41, 0x7c, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x45, 0x0d, + 0x01, 0x0b, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x21, + 0x21, 0x0b, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x20, 0x28, 0x20, 0x12, 0x46, 0x1b, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x20, 0x17, 0x20, 0x14, + 0x41, 0x01, 0x76, 0x22, 0x28, 0x46, 0x1b, 0x20, 0x17, 0x20, 0x28, 0x49, + 0x1b, 0x21, 0x2a, 0x02, 0x40, 0x20, 0x23, 0x0d, 0x00, 0x20, 0x24, 0x2d, + 0x00, 0x00, 0x41, 0x2d, 0x47, 0x0d, 0x00, 0x20, 0x2a, 0x9a, 0x21, 0x2a, + 0x20, 0x21, 0x9a, 0x21, 0x21, 0x0b, 0x20, 0x1c, 0x20, 0x13, 0x20, 0x17, + 0x6b, 0x22, 0x13, 0x36, 0x02, 0x00, 0x20, 0x21, 0x20, 0x2a, 0xa0, 0x20, + 0x21, 0x61, 0x0d, 0x00, 0x20, 0x1c, 0x20, 0x13, 0x20, 0x14, 0x6a, 0x22, + 0x14, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x14, 0x41, 0x80, 0x94, 0xeb, + 0xdc, 0x03, 0x49, 0x0d, 0x00, 0x20, 0x08, 0x20, 0x26, 0x20, 0x29, 0x6a, + 0x6a, 0x21, 0x14, 0x03, 0x40, 0x20, 0x14, 0x41, 0x04, 0x6a, 0x41, 0x00, + 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x14, 0x20, 0x15, 0x4f, 0x0d, 0x00, + 0x20, 0x15, 0x41, 0x7c, 0x6a, 0x22, 0x15, 0x41, 0x00, 0x36, 0x02, 0x00, + 0x0b, 0x20, 0x14, 0x20, 0x14, 0x28, 0x02, 0x00, 0x41, 0x01, 0x6a, 0x22, + 0x13, 0x36, 0x02, 0x00, 0x20, 0x14, 0x41, 0x7c, 0x6a, 0x21, 0x14, 0x20, + 0x13, 0x41, 0xff, 0x93, 0xeb, 0xdc, 0x03, 0x4b, 0x0d, 0x00, 0x0b, 0x20, + 0x14, 0x41, 0x04, 0x6a, 0x21, 0x1c, 0x0b, 0x20, 0x1e, 0x20, 0x15, 0x6b, + 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x21, 0x18, 0x20, 0x15, 0x28, 0x02, + 0x00, 0x22, 0x13, 0x41, 0x0a, 0x49, 0x0d, 0x00, 0x41, 0x0a, 0x21, 0x14, + 0x03, 0x40, 0x20, 0x18, 0x41, 0x01, 0x6a, 0x21, 0x18, 0x20, 0x13, 0x20, + 0x14, 0x41, 0x0a, 0x6c, 0x22, 0x14, 0x4f, 0x0d, 0x00, 0x0b, 0x0b, 0x20, + 0x1c, 0x41, 0x04, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x20, 0x12, 0x20, 0x14, + 0x4b, 0x1b, 0x21, 0x12, 0x0b, 0x20, 0x07, 0x20, 0x12, 0x6a, 0x20, 0x27, + 0x6b, 0x21, 0x14, 0x02, 0x40, 0x03, 0x40, 0x20, 0x14, 0x21, 0x13, 0x20, + 0x12, 0x22, 0x1c, 0x20, 0x15, 0x4d, 0x22, 0x17, 0x0d, 0x01, 0x20, 0x13, + 0x41, 0x7c, 0x6a, 0x21, 0x14, 0x20, 0x1c, 0x41, 0x7c, 0x6a, 0x22, 0x12, + 0x28, 0x02, 0x00, 0x45, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x1b, 0x0d, 0x00, 0x20, 0x16, 0x41, 0x08, 0x71, 0x21, 0x28, 0x0c, + 0x01, 0x0b, 0x20, 0x18, 0x41, 0x7f, 0x73, 0x41, 0x7f, 0x20, 0x1a, 0x41, + 0x01, 0x20, 0x1a, 0x1b, 0x22, 0x12, 0x20, 0x18, 0x4a, 0x20, 0x18, 0x41, + 0x7b, 0x4a, 0x71, 0x22, 0x14, 0x1b, 0x20, 0x12, 0x6a, 0x21, 0x1a, 0x41, + 0x7f, 0x41, 0x7e, 0x20, 0x14, 0x1b, 0x20, 0x1d, 0x6a, 0x21, 0x1d, 0x20, + 0x16, 0x41, 0x08, 0x71, 0x22, 0x28, 0x0d, 0x00, 0x41, 0x77, 0x21, 0x12, + 0x02, 0x40, 0x20, 0x17, 0x0d, 0x00, 0x20, 0x1c, 0x41, 0x7c, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x17, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x12, 0x20, + 0x17, 0x41, 0x0a, 0x70, 0x0d, 0x00, 0x41, 0x0a, 0x21, 0x14, 0x41, 0x00, + 0x21, 0x12, 0x03, 0x40, 0x20, 0x12, 0x41, 0x7f, 0x6a, 0x21, 0x12, 0x20, + 0x17, 0x20, 0x14, 0x41, 0x0a, 0x6c, 0x22, 0x14, 0x70, 0x45, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x13, 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x21, 0x14, + 0x02, 0x40, 0x20, 0x1d, 0x41, 0x5f, 0x71, 0x41, 0xc6, 0x00, 0x47, 0x0d, + 0x00, 0x41, 0x00, 0x21, 0x28, 0x20, 0x1a, 0x20, 0x14, 0x20, 0x12, 0x6a, + 0x41, 0x77, 0x6a, 0x22, 0x12, 0x41, 0x00, 0x20, 0x12, 0x41, 0x00, 0x4a, + 0x1b, 0x22, 0x12, 0x20, 0x1a, 0x20, 0x12, 0x48, 0x1b, 0x21, 0x1a, 0x0c, + 0x01, 0x0b, 0x41, 0x00, 0x21, 0x28, 0x20, 0x1a, 0x20, 0x18, 0x20, 0x14, + 0x6a, 0x20, 0x12, 0x6a, 0x41, 0x77, 0x6a, 0x22, 0x12, 0x41, 0x00, 0x20, + 0x12, 0x41, 0x00, 0x4a, 0x1b, 0x22, 0x12, 0x20, 0x1a, 0x20, 0x12, 0x48, + 0x1b, 0x21, 0x1a, 0x0b, 0x20, 0x1a, 0x41, 0xfd, 0xff, 0xff, 0xff, 0x07, + 0x41, 0xfe, 0xff, 0xff, 0xff, 0x07, 0x20, 0x1a, 0x20, 0x28, 0x72, 0x22, + 0x23, 0x1b, 0x4a, 0x0d, 0x08, 0x20, 0x1a, 0x20, 0x23, 0x41, 0x00, 0x47, + 0x6a, 0x41, 0x01, 0x6a, 0x21, 0x25, 0x02, 0x40, 0x02, 0x40, 0x20, 0x1d, + 0x41, 0x5f, 0x71, 0x41, 0xc6, 0x00, 0x47, 0x22, 0x26, 0x0d, 0x00, 0x20, + 0x18, 0x20, 0x25, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, + 0x0a, 0x20, 0x18, 0x41, 0x00, 0x20, 0x18, 0x41, 0x00, 0x4a, 0x1b, 0x21, + 0x12, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x18, 0x0d, 0x00, + 0x20, 0x06, 0x21, 0x13, 0x20, 0x06, 0x21, 0x14, 0x0c, 0x01, 0x0b, 0x20, + 0x18, 0x20, 0x18, 0x41, 0x1f, 0x75, 0x22, 0x12, 0x73, 0x20, 0x12, 0x6b, + 0x21, 0x12, 0x20, 0x06, 0x21, 0x13, 0x20, 0x06, 0x21, 0x14, 0x03, 0x40, + 0x20, 0x14, 0x41, 0x7f, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x20, 0x12, 0x41, + 0x0a, 0x6e, 0x22, 0x17, 0x41, 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, + 0x00, 0x00, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x21, 0x13, 0x20, 0x12, 0x41, + 0x09, 0x4b, 0x21, 0x1b, 0x20, 0x17, 0x21, 0x12, 0x20, 0x1b, 0x0d, 0x00, + 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x06, 0x20, 0x13, 0x6b, 0x41, 0x01, 0x4a, + 0x0d, 0x00, 0x20, 0x14, 0x20, 0x0e, 0x20, 0x13, 0x6b, 0x6a, 0x22, 0x14, + 0x41, 0x30, 0x20, 0x13, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x6b, 0x41, + 0x76, 0x6a, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, + 0x41, 0x7e, 0x6a, 0x22, 0x27, 0x20, 0x1d, 0x3a, 0x00, 0x00, 0x20, 0x14, + 0x41, 0x7f, 0x6a, 0x41, 0x2d, 0x41, 0x2b, 0x20, 0x18, 0x41, 0x00, 0x48, + 0x1b, 0x3a, 0x00, 0x00, 0x20, 0x06, 0x20, 0x27, 0x6b, 0x22, 0x12, 0x20, + 0x25, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x09, 0x0b, + 0x20, 0x12, 0x20, 0x25, 0x6a, 0x22, 0x12, 0x20, 0x22, 0x41, 0xff, 0xff, + 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x08, 0x20, 0x12, 0x20, 0x22, 0x6a, + 0x21, 0x1b, 0x02, 0x40, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x22, + 0x16, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, + 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, + 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, + 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, + 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, + 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, + 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x24, 0x20, 0x22, 0x20, + 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, + 0x16, 0x41, 0x80, 0x80, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x1b, + 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x30, 0x20, + 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, + 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, + 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, + 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, + 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x20, 0x26, 0x0d, 0x03, 0x20, 0x1e, 0x20, 0x15, 0x20, 0x15, 0x20, 0x1e, + 0x4b, 0x1b, 0x22, 0x18, 0x21, 0x17, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x17, 0x28, 0x02, 0x00, 0x22, 0x12, 0x45, + 0x0d, 0x00, 0x41, 0x08, 0x21, 0x14, 0x03, 0x40, 0x20, 0x05, 0x41, 0xd0, + 0x00, 0x6a, 0x20, 0x14, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, + 0x22, 0x15, 0x41, 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, + 0x20, 0x14, 0x41, 0x7f, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x41, 0x09, 0x4b, + 0x21, 0x13, 0x20, 0x15, 0x21, 0x12, 0x20, 0x13, 0x0d, 0x00, 0x0b, 0x20, + 0x14, 0x41, 0x01, 0x6a, 0x22, 0x15, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x6a, 0x21, 0x12, 0x02, 0x40, 0x20, 0x17, 0x20, 0x18, 0x46, 0x0d, 0x00, + 0x20, 0x14, 0x41, 0x02, 0x6a, 0x41, 0x02, 0x48, 0x0d, 0x04, 0x0c, 0x03, + 0x0b, 0x20, 0x14, 0x41, 0x08, 0x47, 0x0d, 0x03, 0x0c, 0x01, 0x0b, 0x41, + 0x09, 0x21, 0x15, 0x20, 0x17, 0x20, 0x18, 0x47, 0x0d, 0x01, 0x0b, 0x20, + 0x05, 0x41, 0x30, 0x3a, 0x00, 0x58, 0x20, 0x0c, 0x21, 0x12, 0x0c, 0x01, + 0x0b, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x0b, 0x20, 0x15, 0x6a, + 0x22, 0x12, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x12, 0x49, 0x1b, + 0x22, 0x12, 0x41, 0x30, 0x20, 0x15, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x6a, 0x20, 0x12, 0x6b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, + 0x20, 0x12, 0x20, 0x0d, 0x20, 0x12, 0x6b, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x17, 0x41, 0x04, 0x6a, 0x22, 0x17, + 0x20, 0x1e, 0x4d, 0x0d, 0x00, 0x0b, 0x02, 0x40, 0x20, 0x23, 0x45, 0x0d, + 0x00, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x41, + 0xfc, 0x95, 0x80, 0x80, 0x00, 0x41, 0x01, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x1a, 0x41, + 0x01, 0x4e, 0x0d, 0x00, 0x20, 0x1a, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x02, + 0x40, 0x20, 0x17, 0x20, 0x1c, 0x49, 0x0d, 0x00, 0x20, 0x1a, 0x21, 0x12, + 0x0c, 0x01, 0x0b, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x17, 0x28, 0x02, 0x00, 0x22, 0x12, 0x0d, 0x00, 0x20, 0x0d, 0x21, 0x14, + 0x20, 0x0d, 0x21, 0x15, 0x0c, 0x01, 0x0b, 0x20, 0x0d, 0x21, 0x15, 0x20, + 0x0d, 0x21, 0x14, 0x03, 0x40, 0x20, 0x14, 0x41, 0x7f, 0x6a, 0x22, 0x14, + 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x13, 0x41, 0x0a, 0x6c, + 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x15, 0x41, 0x7f, 0x6a, + 0x21, 0x15, 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, 0x18, 0x20, 0x13, 0x21, + 0x12, 0x20, 0x18, 0x0d, 0x00, 0x0b, 0x20, 0x14, 0x20, 0x05, 0x41, 0xd0, + 0x00, 0x6a, 0x4d, 0x0d, 0x01, 0x0b, 0x20, 0x14, 0x20, 0x05, 0x41, 0xd0, + 0x00, 0x6a, 0x6a, 0x20, 0x15, 0x6b, 0x22, 0x14, 0x41, 0x30, 0x20, 0x15, + 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6b, 0x10, 0xa9, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x14, 0x20, 0x1a, 0x41, 0x09, 0x20, 0x1a, 0x41, + 0x09, 0x48, 0x1b, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x1a, 0x41, 0x77, 0x6a, 0x21, 0x12, 0x20, 0x17, 0x41, 0x04, + 0x6a, 0x22, 0x17, 0x20, 0x1c, 0x4f, 0x0d, 0x01, 0x20, 0x1a, 0x41, 0x09, + 0x4a, 0x21, 0x14, 0x20, 0x12, 0x21, 0x1a, 0x20, 0x14, 0x0d, 0x00, 0x0b, + 0x0b, 0x20, 0x00, 0x41, 0x30, 0x20, 0x12, 0x41, 0x09, 0x6a, 0x41, 0x09, + 0x41, 0x00, 0x10, 0xc5, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x04, 0x0b, 0x41, + 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x1c, 0x36, 0x02, 0x00, 0x0c, 0x08, + 0x0b, 0x41, 0x00, 0x21, 0x1b, 0x41, 0xaa, 0x95, 0x80, 0x80, 0x00, 0x21, + 0x1e, 0x20, 0x0f, 0x21, 0x12, 0x20, 0x16, 0x21, 0x1c, 0x20, 0x17, 0x21, + 0x18, 0x0b, 0x20, 0x18, 0x20, 0x12, 0x20, 0x13, 0x6b, 0x22, 0x17, 0x20, + 0x18, 0x20, 0x17, 0x4a, 0x1b, 0x22, 0x1a, 0x20, 0x1b, 0x41, 0xff, 0xff, + 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x05, 0x20, 0x19, 0x20, 0x1b, 0x20, + 0x1a, 0x6a, 0x22, 0x15, 0x20, 0x19, 0x20, 0x15, 0x4a, 0x1b, 0x22, 0x12, + 0x20, 0x14, 0x4a, 0x0d, 0x05, 0x02, 0x40, 0x20, 0x1c, 0x41, 0x80, 0xc0, + 0x04, 0x71, 0x22, 0x1c, 0x0d, 0x00, 0x20, 0x15, 0x20, 0x19, 0x4e, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, 0x20, 0x12, 0x20, + 0x15, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, 0x02, + 0x49, 0x22, 0x16, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, + 0x40, 0x20, 0x16, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, + 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, 0xff, + 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x14, + 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x1e, + 0x20, 0x1b, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x02, 0x40, 0x20, 0x1c, 0x41, 0x80, 0x80, 0x04, 0x47, 0x0d, 0x00, 0x20, + 0x15, 0x20, 0x19, 0x4e, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, + 0x41, 0x30, 0x20, 0x12, 0x20, 0x15, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, + 0x20, 0x14, 0x41, 0x80, 0x02, 0x49, 0x22, 0x1b, 0x1b, 0x10, 0xa9, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x1b, 0x0d, 0x00, 0x03, 0x40, + 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, + 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, + 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, + 0x6a, 0x22, 0x14, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, + 0xf0, 0x00, 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x17, 0x20, 0x18, 0x4e, 0x0d, 0x00, + 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x30, 0x20, 0x1a, 0x20, 0x17, + 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, 0x02, 0x49, + 0x22, 0x18, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, + 0x20, 0x18, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, + 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, 0xff, 0x01, + 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x14, 0x20, + 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x13, 0x20, + 0x17, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x1c, 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x04, 0x20, 0x15, 0x20, 0x19, + 0x4e, 0x0d, 0x04, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, 0x20, + 0x12, 0x20, 0x15, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, + 0x80, 0x02, 0x49, 0x22, 0x15, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x02, 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, + 0xf0, 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, + 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x41, 0x20, 0x71, 0x0d, 0x04, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, + 0x20, 0x14, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0c, + 0x04, 0x0b, 0x02, 0x40, 0x20, 0x1a, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x20, + 0x1c, 0x20, 0x15, 0x41, 0x04, 0x6a, 0x20, 0x1c, 0x20, 0x15, 0x4b, 0x1b, + 0x21, 0x1c, 0x20, 0x15, 0x21, 0x17, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x17, 0x28, 0x02, 0x00, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x41, 0x00, + 0x21, 0x14, 0x03, 0x40, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x14, + 0x6a, 0x41, 0x08, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, + 0x13, 0x41, 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, + 0x14, 0x41, 0x7f, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, + 0x18, 0x20, 0x13, 0x21, 0x12, 0x20, 0x18, 0x0d, 0x00, 0x0b, 0x20, 0x14, + 0x45, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x14, 0x6a, + 0x41, 0x09, 0x6a, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x41, 0x30, + 0x3a, 0x00, 0x58, 0x20, 0x0c, 0x21, 0x12, 0x0b, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x17, 0x20, 0x15, 0x46, 0x0d, 0x00, 0x20, 0x12, 0x20, 0x05, 0x41, + 0xd0, 0x00, 0x6a, 0x4d, 0x0d, 0x01, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x41, 0x30, 0x20, 0x12, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6b, 0x10, + 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x12, 0x41, 0x01, 0x20, 0x00, 0x10, + 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x01, 0x6a, + 0x21, 0x12, 0x02, 0x40, 0x20, 0x28, 0x0d, 0x00, 0x20, 0x1a, 0x41, 0x01, + 0x48, 0x0d, 0x01, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, + 0x0d, 0x00, 0x41, 0xfc, 0x95, 0x80, 0x80, 0x00, 0x41, 0x01, 0x20, 0x00, + 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x0d, 0x20, 0x12, + 0x6b, 0x21, 0x14, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x12, 0x20, 0x14, 0x20, 0x1a, 0x20, 0x14, 0x20, + 0x1a, 0x48, 0x1b, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x1a, 0x20, 0x14, 0x6b, 0x21, 0x1a, 0x20, 0x17, 0x41, 0x04, + 0x6a, 0x22, 0x17, 0x20, 0x1c, 0x4f, 0x0d, 0x01, 0x20, 0x1a, 0x41, 0x7f, + 0x4a, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x41, 0x30, 0x20, 0x1a, 0x41, + 0x12, 0x6a, 0x41, 0x12, 0x41, 0x00, 0x10, 0xc5, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x27, + 0x20, 0x06, 0x20, 0x27, 0x6b, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x01, + 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, 0x01, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, + 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xa9, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, + 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, + 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x01, 0x20, 0x05, + 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0c, 0x01, 0x0b, 0x20, 0x24, 0x20, 0x1d, 0x41, 0x1a, + 0x74, 0x41, 0x1f, 0x75, 0x41, 0x09, 0x71, 0x6a, 0x21, 0x1e, 0x02, 0x40, + 0x20, 0x17, 0x41, 0x0b, 0x4b, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x0c, 0x20, 0x17, 0x6b, 0x22, 0x12, 0x41, 0x07, 0x71, 0x22, 0x14, 0x0d, + 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0x21, 0x2a, + 0x0c, 0x01, 0x0b, 0x20, 0x17, 0x41, 0x74, 0x6a, 0x21, 0x12, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0x21, 0x2a, 0x03, 0x40, 0x20, + 0x12, 0x41, 0x01, 0x6a, 0x21, 0x12, 0x20, 0x2a, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x21, 0x2a, 0x20, 0x14, 0x41, 0x7f, + 0x6a, 0x22, 0x14, 0x0d, 0x00, 0x0b, 0x41, 0x00, 0x20, 0x12, 0x6b, 0x21, + 0x12, 0x0b, 0x02, 0x40, 0x20, 0x17, 0x41, 0x7b, 0x6a, 0x41, 0x07, 0x49, + 0x0d, 0x00, 0x03, 0x40, 0x20, 0x2a, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x40, 0xa2, 0x21, 0x2a, 0x20, 0x12, 0x41, 0x78, 0x6a, 0x22, 0x12, 0x0d, + 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x1e, 0x2d, 0x00, 0x00, 0x41, 0x2d, + 0x47, 0x0d, 0x00, 0x20, 0x2a, 0x20, 0x21, 0x9a, 0x20, 0x2a, 0xa1, 0xa0, + 0x9a, 0x21, 0x21, 0x0c, 0x01, 0x0b, 0x20, 0x21, 0x20, 0x2a, 0xa0, 0x20, + 0x2a, 0xa1, 0x21, 0x21, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x05, 0x28, + 0x02, 0x6c, 0x22, 0x18, 0x45, 0x0d, 0x00, 0x20, 0x18, 0x20, 0x18, 0x41, + 0x1f, 0x75, 0x22, 0x12, 0x73, 0x20, 0x12, 0x6b, 0x21, 0x12, 0x41, 0x00, + 0x21, 0x14, 0x03, 0x40, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x20, 0x14, + 0x6a, 0x41, 0x0b, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, + 0x15, 0x41, 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, + 0x14, 0x41, 0x7f, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, + 0x13, 0x20, 0x15, 0x21, 0x12, 0x20, 0x13, 0x0d, 0x00, 0x0b, 0x20, 0x14, + 0x45, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x20, 0x14, 0x6a, + 0x41, 0x0c, 0x6a, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x41, 0x30, + 0x3a, 0x00, 0x4f, 0x20, 0x0a, 0x21, 0x12, 0x0b, 0x20, 0x22, 0x41, 0x02, + 0x72, 0x21, 0x1c, 0x20, 0x1d, 0x41, 0x20, 0x71, 0x21, 0x15, 0x20, 0x12, + 0x41, 0x7e, 0x6a, 0x22, 0x1a, 0x20, 0x1d, 0x41, 0x0f, 0x6a, 0x3a, 0x00, + 0x00, 0x20, 0x12, 0x41, 0x7f, 0x6a, 0x41, 0x2d, 0x41, 0x2b, 0x20, 0x18, + 0x41, 0x00, 0x48, 0x1b, 0x3a, 0x00, 0x00, 0x20, 0x16, 0x41, 0x08, 0x71, + 0x21, 0x13, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x21, 0x14, 0x03, 0x40, + 0x20, 0x14, 0x21, 0x12, 0x02, 0x40, 0x02, 0x40, 0x20, 0x21, 0x99, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x41, 0x63, 0x45, 0x0d, 0x00, + 0x20, 0x21, 0xaa, 0x21, 0x14, 0x0c, 0x01, 0x0b, 0x41, 0x80, 0x80, 0x80, + 0x80, 0x78, 0x21, 0x14, 0x0b, 0x20, 0x12, 0x20, 0x14, 0x41, 0xb0, 0x9c, + 0x80, 0x80, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x20, 0x15, 0x72, 0x3a, 0x00, + 0x00, 0x20, 0x21, 0x20, 0x14, 0xb7, 0xa1, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x40, 0xa2, 0x21, 0x21, 0x02, 0x40, 0x20, 0x12, 0x41, + 0x01, 0x6a, 0x22, 0x14, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6b, 0x41, + 0x01, 0x47, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x13, 0x0d, 0x00, 0x20, 0x17, + 0x41, 0x00, 0x4a, 0x0d, 0x00, 0x20, 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x0d, 0x01, 0x0b, 0x20, 0x12, 0x41, 0x2e, + 0x3a, 0x00, 0x01, 0x20, 0x12, 0x41, 0x02, 0x6a, 0x21, 0x14, 0x0b, 0x20, + 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, + 0x00, 0x0b, 0x41, 0xfd, 0xff, 0xff, 0xff, 0x07, 0x20, 0x06, 0x20, 0x1a, + 0x6b, 0x22, 0x18, 0x20, 0x1c, 0x6a, 0x22, 0x12, 0x6b, 0x20, 0x17, 0x48, + 0x0d, 0x02, 0x20, 0x17, 0x41, 0x02, 0x6a, 0x20, 0x14, 0x20, 0x05, 0x41, + 0xd0, 0x00, 0x6a, 0x6b, 0x22, 0x14, 0x20, 0x14, 0x41, 0x7e, 0x6a, 0x20, + 0x17, 0x48, 0x1b, 0x20, 0x14, 0x20, 0x17, 0x1b, 0x22, 0x13, 0x20, 0x12, + 0x6a, 0x21, 0x1b, 0x02, 0x40, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x04, 0x71, + 0x22, 0x15, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, + 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, + 0x17, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, + 0x17, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, + 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, + 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, + 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, + 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x1e, 0x20, 0x1c, + 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, + 0x20, 0x15, 0x41, 0x80, 0x80, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, + 0x1b, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x30, + 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, + 0x41, 0x80, 0x02, 0x49, 0x22, 0x17, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x02, 0x40, 0x20, 0x17, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, + 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, + 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x13, 0x20, + 0x14, 0x6b, 0x22, 0x12, 0x41, 0x01, 0x48, 0x0d, 0x00, 0x20, 0x05, 0x41, + 0xf0, 0x04, 0x6a, 0x41, 0x30, 0x20, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, + 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xa9, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, + 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x1a, 0x20, 0x18, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x15, 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x00, + 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, + 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xa9, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, + 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, + 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x1b, 0x20, 0x19, 0x20, 0x1b, 0x20, 0x19, + 0x4a, 0x1b, 0x22, 0x12, 0x41, 0x00, 0x4e, 0x0d, 0x00, 0x0b, 0x0b, 0x41, + 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x3d, 0x36, 0x02, 0x00, 0x0b, 0x41, + 0x7f, 0x21, 0x11, 0x0b, 0x20, 0x05, 0x41, 0xf0, 0x06, 0x6a, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x11, 0x0b, 0xb3, 0x04, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x01, 0x41, 0x77, 0x6a, 0x0e, 0x12, 0x11, 0x00, 0x01, 0x04, 0x02, + 0x03, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x12, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, + 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x34, 0x02, + 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x34, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, + 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, + 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, + 0x6a, 0x41, 0x78, 0x71, 0x22, 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, + 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, + 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x32, 0x01, 0x00, 0x37, 0x03, + 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, + 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x33, 0x01, + 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x30, 0x00, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x31, 0x00, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, + 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, + 0x71, 0x22, 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, + 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, + 0x71, 0x22, 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, 0x71, 0x22, 0x01, + 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, 0x03, + 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, + 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x34, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, + 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, + 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, + 0x71, 0x22, 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x2b, 0x03, 0x00, 0x39, 0x03, 0x00, 0x0f, 0x0b, 0x10, 0xc6, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, + 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, + 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x0b, 0x9e, 0x01, 0x01, 0x01, + 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x02, 0x6b, 0x22, + 0x05, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x02, 0x20, + 0x03, 0x4c, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x22, 0x03, + 0x41, 0x80, 0x02, 0x20, 0x03, 0x41, 0x80, 0x02, 0x49, 0x22, 0x04, 0x1b, + 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x02, 0x40, 0x20, 0x04, + 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, + 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x03, 0x41, 0x80, 0x7e, + 0x6a, 0x22, 0x03, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x02, 0x20, + 0x03, 0x20, 0x00, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x05, 0x41, 0x80, 0x02, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x0b, + 0x1c, 0x00, 0x41, 0xb6, 0x97, 0x80, 0x80, 0x00, 0x41, 0xb8, 0x9d, 0x80, + 0x80, 0x00, 0x10, 0xc1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x10, 0xa4, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x0b, 0x3b, 0x01, 0x01, 0x7f, 0x23, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x0c, 0x41, 0xc0, 0x9c, + 0x80, 0x80, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0xc2, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x01, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x01, 0x0b, 0x49, 0x01, 0x03, 0x7f, 0x41, 0x00, 0x21, + 0x03, 0x02, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x03, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x22, 0x04, 0x20, 0x01, 0x2d, 0x00, 0x00, + 0x22, 0x05, 0x47, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, + 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, 0x02, 0x41, 0x7f, 0x6a, + 0x22, 0x02, 0x0d, 0x00, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x04, 0x20, 0x05, + 0x6b, 0x21, 0x03, 0x0b, 0x20, 0x03, 0x0b, 0x2e, 0x01, 0x02, 0x7f, 0x02, + 0x40, 0x20, 0x00, 0x10, 0xaa, 0x80, 0x80, 0x80, 0x00, 0x41, 0x01, 0x6a, + 0x22, 0x01, 0x10, 0x90, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, + 0x00, 0x20, 0x02, 0x20, 0x00, 0x20, 0x01, 0x10, 0xa8, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x02, 0x0b, 0xe0, 0x01, 0x01, 0x04, 0x7f, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x00, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x41, 0x00, 0x2d, 0x00, 0xec, 0xaa, + 0x80, 0x80, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x2d, 0x00, + 0xec, 0xaa, 0x80, 0x80, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x41, 0x03, + 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x20, 0x01, + 0x20, 0x00, 0x41, 0x08, 0x6a, 0x10, 0x9c, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x02, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x08, 0x47, 0x0d, 0x02, 0x41, + 0x00, 0x41, 0x01, 0x3a, 0x00, 0xec, 0xaa, 0x80, 0x80, 0x00, 0x0c, 0x04, + 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x08, 0x0d, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x0c, 0x22, 0x03, 0x41, 0x01, 0x6a, 0x10, 0x90, 0x80, 0x80, + 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x03, 0x20, 0x01, 0x20, 0x02, 0x20, + 0x03, 0x10, 0x9d, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x02, 0x20, 0x02, 0x20, + 0x00, 0x28, 0x02, 0x0c, 0x6a, 0x41, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x01, + 0x20, 0x02, 0x10, 0xcb, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x03, 0x20, 0x02, + 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, + 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, 0x41, 0xc7, 0x00, 0x10, 0x95, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x0b, 0x41, 0xc6, 0x00, 0x10, 0x95, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x0b, 0x97, 0x02, 0x01, 0x04, 0x7f, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x0e, 0x02, 0x01, 0x01, 0x00, 0x0b, + 0x20, 0x01, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xe4, + 0xaa, 0x80, 0x80, 0x00, 0x22, 0x02, 0x41, 0x00, 0x28, 0x02, 0xf0, 0xaa, + 0x80, 0x80, 0x00, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xe8, 0xaa, + 0x80, 0x80, 0x00, 0x21, 0x03, 0x02, 0x40, 0x41, 0x08, 0x20, 0x02, 0x41, + 0x01, 0x74, 0x41, 0x04, 0x20, 0x02, 0x1b, 0x22, 0x04, 0x10, 0x94, 0x80, + 0x80, 0x80, 0x00, 0x22, 0x05, 0x0d, 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, + 0x05, 0x20, 0x03, 0x20, 0x02, 0x41, 0x03, 0x74, 0x10, 0xa8, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x05, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0xf0, 0xaa, + 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0xe8, 0xaa, 0x80, + 0x80, 0x00, 0x20, 0x03, 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x02, + 0x40, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x22, 0x03, 0x2d, + 0x00, 0x00, 0x41, 0x52, 0x6a, 0x0e, 0x02, 0x01, 0x00, 0x03, 0x0b, 0x20, + 0x03, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x01, 0x6a, 0x21, 0x01, 0x20, 0x03, 0x2d, 0x00, 0x01, 0x22, 0x04, 0x45, + 0x0d, 0x00, 0x20, 0x04, 0x41, 0x2f, 0x47, 0x0d, 0x01, 0x20, 0x03, 0x41, + 0x02, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x03, + 0x10, 0xc9, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0d, 0x00, 0x41, 0x7f, + 0x0f, 0x0b, 0x41, 0x00, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x36, 0x02, 0xe4, + 0xaa, 0x80, 0x80, 0x00, 0x41, 0x00, 0x28, 0x02, 0xe8, 0xaa, 0x80, 0x80, + 0x00, 0x20, 0x02, 0x41, 0x03, 0x74, 0x6a, 0x22, 0x01, 0x20, 0x00, 0x36, + 0x02, 0x04, 0x20, 0x01, 0x20, 0x03, 0x36, 0x02, 0x00, 0x41, 0x00, 0x0f, + 0x0b, 0x10, 0xa4, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x5d, 0x01, 0x01, + 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x04, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x20, 0x03, 0x36, 0x02, + 0x0c, 0x02, 0x40, 0x02, 0x40, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x45, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x04, 0x41, 0x0c, + 0x6a, 0x41, 0x00, 0x10, 0x8e, 0x80, 0x80, 0x80, 0x00, 0x21, 0x03, 0x0c, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0xcd, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x03, 0x0b, 0x20, 0x04, 0x41, 0x10, 0x6a, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x0b, 0xbd, 0x02, 0x01, 0x0a, 0x7f, + 0x20, 0x00, 0x41, 0x7f, 0x6a, 0x21, 0x03, 0x10, 0xca, 0x80, 0x80, 0x80, + 0x00, 0x03, 0x40, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x2d, 0x00, + 0x00, 0x41, 0x2f, 0x46, 0x0d, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x02, + 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xe4, 0xaa, 0x80, 0x80, 0x00, + 0x22, 0x05, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xe8, 0xaa, 0x80, + 0x80, 0x00, 0x21, 0x06, 0x41, 0x7f, 0x21, 0x07, 0x03, 0x40, 0x20, 0x06, + 0x20, 0x05, 0x41, 0x7f, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x74, 0x6a, 0x22, + 0x08, 0x28, 0x02, 0x00, 0x22, 0x09, 0x10, 0xaa, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x0a, 0x02, 0x40, 0x02, 0x40, 0x20, 0x07, 0x41, 0x7f, 0x46, 0x0d, + 0x00, 0x20, 0x0a, 0x20, 0x04, 0x4d, 0x0d, 0x01, 0x0b, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x0a, 0x0d, 0x00, 0x20, 0x03, 0x2d, 0x00, 0x00, 0x41, 0xff, + 0x01, 0x71, 0x41, 0x2f, 0x47, 0x0d, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x09, + 0x20, 0x0a, 0x10, 0xc8, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x01, 0x20, 0x09, + 0x41, 0x7f, 0x6a, 0x21, 0x0b, 0x20, 0x0a, 0x21, 0x0c, 0x02, 0x40, 0x03, + 0x40, 0x20, 0x0c, 0x22, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x7f, + 0x6a, 0x21, 0x0c, 0x20, 0x0b, 0x20, 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x41, + 0x2f, 0x46, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x6a, 0x2d, + 0x00, 0x00, 0x22, 0x00, 0x41, 0x2f, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x0d, + 0x01, 0x0b, 0x20, 0x01, 0x20, 0x09, 0x36, 0x02, 0x00, 0x20, 0x08, 0x28, + 0x02, 0x04, 0x21, 0x07, 0x20, 0x0a, 0x21, 0x04, 0x0b, 0x20, 0x05, 0x0d, + 0x00, 0x0b, 0x20, 0x07, 0x41, 0x7f, 0x47, 0x0d, 0x01, 0x0b, 0x41, 0x00, + 0x41, 0x2c, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x0f, + 0x0b, 0x20, 0x03, 0x20, 0x04, 0x6a, 0x41, 0x7f, 0x6a, 0x21, 0x00, 0x03, + 0x40, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x00, 0x2d, 0x00, 0x00, 0x22, + 0x03, 0x41, 0x2f, 0x46, 0x0d, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x00, 0x41, + 0xfc, 0x95, 0x80, 0x80, 0x00, 0x20, 0x03, 0x1b, 0x36, 0x02, 0x00, 0x20, + 0x07, 0x0b, 0x99, 0x02, 0x02, 0x02, 0x7f, 0x02, 0x7e, 0x23, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, + 0x41, 0x80, 0x80, 0x80, 0xf0, 0x01, 0x71, 0x41, 0x80, 0x80, 0x80, 0x70, + 0x6a, 0x41, 0x19, 0x76, 0x22, 0x04, 0x41, 0x09, 0x4b, 0x0d, 0x00, 0x41, + 0x01, 0x20, 0x04, 0x74, 0x22, 0x04, 0x41, 0x82, 0x05, 0x71, 0x0d, 0x01, + 0x42, 0xbc, 0xfd, 0xfe, 0x7d, 0x21, 0x05, 0x20, 0x04, 0x41, 0x09, 0x71, + 0x0d, 0x02, 0x0b, 0x41, 0x00, 0x41, 0x1c, 0x36, 0x02, 0xb4, 0x9e, 0x80, + 0x80, 0x00, 0x41, 0x7f, 0x21, 0x04, 0x0c, 0x02, 0x0b, 0x42, 0xbe, 0xfd, + 0xff, 0x7d, 0x42, 0xbc, 0xfd, 0xfe, 0x7d, 0x20, 0x02, 0x41, 0x80, 0x80, + 0x80, 0x20, 0x71, 0x1b, 0x22, 0x05, 0x42, 0xc1, 0x82, 0x80, 0x02, 0x84, + 0x20, 0x05, 0x20, 0x02, 0x41, 0x80, 0x80, 0x80, 0x80, 0x01, 0x71, 0x1b, + 0x21, 0x05, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, + 0x10, 0x9a, 0x80, 0x80, 0x80, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x41, + 0x00, 0x20, 0x04, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, + 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x41, 0x7f, 0x21, 0x04, 0x02, 0x40, 0x20, + 0x00, 0x20, 0x02, 0x41, 0x7f, 0x73, 0x41, 0x18, 0x76, 0x41, 0x01, 0x71, + 0x20, 0x01, 0x20, 0x02, 0x41, 0x0c, 0x76, 0x41, 0xff, 0x1f, 0x71, 0x20, + 0x03, 0x29, 0x03, 0x18, 0x22, 0x06, 0x20, 0x05, 0x83, 0x20, 0x06, 0x20, + 0x02, 0x41, 0xff, 0x1f, 0x71, 0x20, 0x03, 0x41, 0x04, 0x6a, 0x10, 0xa2, + 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, + 0x02, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x20, + 0x03, 0x28, 0x02, 0x04, 0x21, 0x04, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x0b, 0x23, 0x00, 0x02, + 0x40, 0x20, 0x00, 0x20, 0x01, 0x10, 0xa1, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x01, 0x0d, 0x00, 0x41, 0x00, 0x0f, 0x0b, 0x41, 0x00, 0x20, 0x01, 0x36, + 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x0b, 0xa1, 0x01, 0x01, + 0x02, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x02, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x02, + 0x41, 0x0c, 0x6a, 0x41, 0xf4, 0xaa, 0x80, 0x80, 0x00, 0x41, 0xf8, 0xaa, + 0x80, 0x80, 0x00, 0x41, 0x01, 0x10, 0x8e, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x03, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x41, + 0xf4, 0xaa, 0x80, 0x80, 0x00, 0x41, 0xf8, 0xaa, 0x80, 0x80, 0x00, 0x28, + 0x02, 0x00, 0x10, 0xcc, 0x80, 0x80, 0x80, 0x00, 0x21, 0x03, 0x0b, 0x41, + 0x7f, 0x21, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x7f, 0x47, + 0x0d, 0x00, 0x41, 0x00, 0x41, 0x2c, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, + 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0xf4, 0xaa, 0x80, 0x80, 0x00, + 0x28, 0x02, 0x00, 0x20, 0x01, 0x10, 0xce, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x00, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x20, 0x00, 0x0b, 0x9f, 0x01, 0x01, 0x02, 0x7f, 0x23, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x41, 0xf4, + 0xaa, 0x80, 0x80, 0x00, 0x41, 0xf8, 0xaa, 0x80, 0x80, 0x00, 0x41, 0x01, + 0x10, 0x8e, 0x80, 0x80, 0x80, 0x00, 0x21, 0x03, 0x0c, 0x01, 0x0b, 0x20, + 0x00, 0x20, 0x02, 0x41, 0x0c, 0x6a, 0x41, 0xf4, 0xaa, 0x80, 0x80, 0x00, + 0x41, 0xf8, 0xaa, 0x80, 0x80, 0x00, 0x28, 0x02, 0x00, 0x10, 0xcc, 0x80, + 0x80, 0x80, 0x00, 0x21, 0x03, 0x0b, 0x41, 0x7f, 0x21, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x03, 0x41, 0x7f, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x41, + 0x2c, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x20, + 0x03, 0x41, 0xf4, 0xaa, 0x80, 0x80, 0x00, 0x28, 0x02, 0x00, 0x10, 0xcf, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x6a, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, 0x0b, 0xe1, 0x02, 0x01, + 0x03, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, + 0x41, 0xff, 0x01, 0x71, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, + 0x03, 0x71, 0x45, 0x0d, 0x02, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x22, 0x03, 0x0d, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x03, 0x20, 0x01, + 0x41, 0xff, 0x01, 0x71, 0x47, 0x0d, 0x01, 0x20, 0x00, 0x0f, 0x0b, 0x20, + 0x00, 0x20, 0x00, 0x10, 0xaa, 0x80, 0x80, 0x80, 0x00, 0x6a, 0x0f, 0x0b, + 0x02, 0x40, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x03, 0x41, 0x03, 0x71, + 0x0d, 0x00, 0x20, 0x03, 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x2d, + 0x00, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x01, 0x41, + 0xff, 0x01, 0x71, 0x46, 0x0d, 0x01, 0x02, 0x40, 0x20, 0x00, 0x41, 0x02, + 0x6a, 0x22, 0x03, 0x41, 0x03, 0x71, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x00, + 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x2d, 0x00, 0x00, 0x22, 0x04, 0x45, 0x0d, + 0x01, 0x20, 0x04, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x01, + 0x02, 0x40, 0x20, 0x00, 0x41, 0x03, 0x6a, 0x22, 0x03, 0x41, 0x03, 0x71, + 0x0d, 0x00, 0x20, 0x03, 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x2d, + 0x00, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x01, 0x41, + 0xff, 0x01, 0x71, 0x46, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x04, 0x6a, 0x21, + 0x00, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, + 0x7f, 0x73, 0x20, 0x03, 0x41, 0xff, 0xfd, 0xfb, 0x77, 0x6a, 0x71, 0x41, + 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x81, + 0x82, 0x84, 0x08, 0x6c, 0x21, 0x02, 0x03, 0x40, 0x20, 0x03, 0x20, 0x02, + 0x73, 0x22, 0x03, 0x41, 0x7f, 0x73, 0x20, 0x03, 0x41, 0xff, 0xfd, 0xfb, + 0x77, 0x6a, 0x71, 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, 0x0d, 0x01, + 0x20, 0x00, 0x41, 0x04, 0x6a, 0x22, 0x00, 0x28, 0x02, 0x00, 0x22, 0x03, + 0x41, 0x7f, 0x73, 0x20, 0x03, 0x41, 0xff, 0xfd, 0xfb, 0x77, 0x6a, 0x71, + 0x41, 0x80, 0x81, 0x82, 0x84, 0x78, 0x71, 0x45, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x00, 0x41, 0x7f, 0x6a, 0x21, 0x03, 0x03, 0x40, 0x20, 0x03, 0x41, + 0x01, 0x6a, 0x22, 0x03, 0x2d, 0x00, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x01, + 0x20, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x47, 0x0d, 0x00, 0x0b, + 0x0b, 0x20, 0x03, 0x0b, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0xd2, + 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x41, 0x00, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x1b, 0x0b, 0x65, 0x01, + 0x03, 0x7f, 0x41, 0x80, 0x80, 0x80, 0xa0, 0x01, 0x41, 0x80, 0x80, 0x80, + 0x20, 0x41, 0x80, 0x80, 0x80, 0x80, 0x01, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x22, 0x01, 0x41, 0xf2, 0x00, 0x46, 0x22, 0x02, 0x1b, 0x20, 0x00, 0x41, + 0x2b, 0x10, 0xd3, 0x80, 0x80, 0x80, 0x00, 0x1b, 0x22, 0x03, 0x41, 0x80, + 0x80, 0x01, 0x72, 0x20, 0x03, 0x20, 0x00, 0x41, 0xf8, 0x00, 0x10, 0xd3, + 0x80, 0x80, 0x80, 0x00, 0x1b, 0x22, 0x00, 0x20, 0x00, 0x41, 0x80, 0x20, + 0x72, 0x20, 0x02, 0x1b, 0x22, 0x00, 0x41, 0x80, 0x80, 0x02, 0x72, 0x20, + 0x00, 0x20, 0x01, 0x41, 0xf7, 0x00, 0x46, 0x1b, 0x20, 0x01, 0x41, 0xe1, + 0x00, 0x46, 0x72, 0x0b, 0x92, 0x02, 0x02, 0x01, 0x7f, 0x02, 0x7e, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x03, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x41, 0x7f, 0x6a, 0x0e, 0x04, 0x05, + 0x00, 0x01, 0x02, 0x03, 0x0b, 0x41, 0x00, 0x21, 0x01, 0x0c, 0x04, 0x0b, + 0x02, 0x40, 0x20, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x10, 0x9a, 0x80, + 0x80, 0x80, 0x00, 0x22, 0x01, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x01, + 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x03, 0x0b, 0x20, 0x03, + 0x29, 0x03, 0x10, 0x22, 0x04, 0x42, 0xc0, 0x00, 0x83, 0x21, 0x05, 0x20, + 0x03, 0x2f, 0x01, 0x0a, 0x21, 0x01, 0x02, 0x40, 0x20, 0x04, 0x42, 0x82, + 0x80, 0x01, 0x83, 0x50, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x05, 0x50, 0x0d, + 0x00, 0x20, 0x01, 0x41, 0x80, 0x80, 0x80, 0xa0, 0x01, 0x72, 0x21, 0x01, + 0x0c, 0x05, 0x0b, 0x20, 0x01, 0x41, 0x80, 0x80, 0x80, 0x20, 0x72, 0x21, + 0x01, 0x0c, 0x04, 0x0b, 0x02, 0x40, 0x20, 0x05, 0x50, 0x0d, 0x00, 0x20, + 0x01, 0x41, 0x80, 0x80, 0x80, 0x80, 0x01, 0x72, 0x21, 0x01, 0x0c, 0x04, + 0x0b, 0x20, 0x01, 0x41, 0x80, 0x80, 0x80, 0xc0, 0x00, 0x72, 0x21, 0x01, + 0x0c, 0x03, 0x0b, 0x20, 0x03, 0x20, 0x02, 0x41, 0x04, 0x6a, 0x36, 0x02, + 0x08, 0x02, 0x40, 0x20, 0x00, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0xff, + 0x1f, 0x71, 0x10, 0x9b, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x0d, 0x00, + 0x41, 0x00, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x41, 0x00, 0x20, 0x01, 0x36, + 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x41, + 0x1c, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0b, 0x41, 0x7f, 0x21, + 0x01, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x20, 0x01, 0x0b, 0x71, 0x01, 0x02, 0x7f, 0x23, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x7f, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, + 0x7f, 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x1c, 0x36, 0x02, 0xb4, 0x9e, + 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, + 0x20, 0x02, 0x20, 0x03, 0x41, 0x0c, 0x6a, 0x10, 0x9e, 0x80, 0x80, 0x80, + 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, + 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, 0x04, 0x0c, 0x01, 0x0b, + 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x0b, 0x20, 0x03, 0x41, 0x10, + 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x0b, 0x70, 0x01, + 0x01, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, 0x36, + 0x02, 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x08, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x41, 0x01, 0x20, 0x03, + 0x41, 0x04, 0x6a, 0x10, 0x9e, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, + 0x0d, 0x00, 0x41, 0x00, 0x41, 0x08, 0x20, 0x02, 0x20, 0x02, 0x41, 0xcc, + 0x00, 0x46, 0x1b, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x7f, + 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x04, 0x21, 0x02, + 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x02, 0x0b, 0xfc, 0x01, 0x01, 0x05, 0x7f, 0x23, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x20, 0x03, 0x20, 0x01, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, + 0x28, 0x02, 0x2c, 0x22, 0x04, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x00, + 0x28, 0x02, 0x28, 0x22, 0x05, 0x36, 0x02, 0x08, 0x20, 0x03, 0x20, 0x02, + 0x20, 0x04, 0x41, 0x00, 0x47, 0x6b, 0x22, 0x06, 0x36, 0x02, 0x04, 0x20, + 0x00, 0x28, 0x02, 0x38, 0x21, 0x07, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, + 0x45, 0x0d, 0x00, 0x20, 0x07, 0x20, 0x03, 0x41, 0x02, 0x10, 0xd6, 0x80, + 0x80, 0x80, 0x00, 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x07, 0x20, 0x05, + 0x20, 0x04, 0x10, 0xd7, 0x80, 0x80, 0x80, 0x00, 0x21, 0x04, 0x0b, 0x41, + 0x00, 0x21, 0x06, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, 0x00, 0x4a, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x20, 0x41, + 0x10, 0x20, 0x04, 0x1b, 0x72, 0x36, 0x02, 0x00, 0x0c, 0x01, 0x0b, 0x02, + 0x40, 0x20, 0x04, 0x20, 0x03, 0x28, 0x02, 0x04, 0x22, 0x07, 0x4b, 0x0d, + 0x00, 0x20, 0x04, 0x21, 0x06, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x28, 0x22, 0x06, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x06, + 0x20, 0x04, 0x20, 0x07, 0x6b, 0x6a, 0x36, 0x02, 0x08, 0x02, 0x40, 0x20, + 0x00, 0x28, 0x02, 0x2c, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x06, 0x41, + 0x01, 0x6a, 0x36, 0x02, 0x04, 0x20, 0x02, 0x20, 0x01, 0x6a, 0x41, 0x7f, + 0x6a, 0x20, 0x06, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0b, 0x20, 0x02, + 0x21, 0x06, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x06, 0x0b, 0x37, 0x01, 0x02, 0x7f, 0x20, 0x00, 0x10, + 0xb3, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x28, 0x02, 0x00, 0x36, 0x02, + 0x34, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x45, 0x0d, + 0x00, 0x20, 0x02, 0x20, 0x00, 0x36, 0x02, 0x30, 0x0b, 0x20, 0x01, 0x20, + 0x00, 0x36, 0x02, 0x00, 0x10, 0xb4, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, + 0x0b, 0x85, 0x03, 0x01, 0x04, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x41, 0x20, 0x6b, 0x22, 0x02, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x41, 0xf0, 0x95, 0x80, 0x80, 0x00, 0x20, 0x01, 0x2c, + 0x00, 0x00, 0x22, 0x03, 0x41, 0x04, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, + 0x0d, 0x00, 0x41, 0x00, 0x21, 0x04, 0x41, 0x00, 0x41, 0x1c, 0x36, 0x02, + 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x41, 0xf8, + 0x08, 0x10, 0x90, 0x80, 0x80, 0x80, 0x00, 0x22, 0x04, 0x0d, 0x00, 0x41, + 0x00, 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x05, 0x20, 0x04, + 0x41, 0x00, 0x41, 0xf0, 0x00, 0x10, 0xa9, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x04, 0x02, 0x40, 0x20, 0x01, 0x41, 0x2b, 0x10, 0xd3, 0x80, 0x80, 0x80, + 0x00, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x08, 0x41, 0x04, 0x20, 0x03, 0x41, + 0xf2, 0x00, 0x46, 0x1b, 0x22, 0x05, 0x36, 0x02, 0x00, 0x0b, 0x02, 0x40, + 0x20, 0x01, 0x41, 0xe5, 0x00, 0x10, 0xd3, 0x80, 0x80, 0x80, 0x00, 0x45, + 0x0d, 0x00, 0x20, 0x02, 0x41, 0x01, 0x36, 0x02, 0x10, 0x20, 0x00, 0x41, + 0x02, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x10, 0xd5, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x21, 0x03, 0x0b, 0x02, 0x40, 0x20, + 0x03, 0x41, 0xff, 0x01, 0x71, 0x41, 0xe1, 0x00, 0x47, 0x0d, 0x00, 0x02, + 0x40, 0x20, 0x00, 0x41, 0x03, 0x41, 0x00, 0x10, 0xd5, 0x80, 0x80, 0x80, + 0x00, 0x22, 0x01, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x01, + 0x41, 0x01, 0x72, 0x36, 0x02, 0x00, 0x20, 0x00, 0x41, 0x04, 0x20, 0x02, + 0x10, 0xd5, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x04, 0x20, 0x05, + 0x41, 0x80, 0x01, 0x72, 0x22, 0x05, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x04, + 0x41, 0x7f, 0x36, 0x02, 0x40, 0x20, 0x04, 0x41, 0x80, 0x08, 0x36, 0x02, + 0x2c, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x38, 0x20, 0x04, 0x20, 0x04, + 0x41, 0xf8, 0x00, 0x6a, 0x36, 0x02, 0x28, 0x02, 0x40, 0x20, 0x05, 0x41, + 0x08, 0x71, 0x0d, 0x00, 0x20, 0x00, 0x10, 0xaf, 0x80, 0x80, 0x80, 0x00, + 0x45, 0x0d, 0x00, 0x20, 0x04, 0x41, 0x0a, 0x36, 0x02, 0x40, 0x0b, 0x20, + 0x04, 0x41, 0x84, 0x80, 0x80, 0x80, 0x00, 0x36, 0x02, 0x24, 0x20, 0x04, + 0x41, 0x81, 0x80, 0x80, 0x80, 0x00, 0x36, 0x02, 0x20, 0x20, 0x04, 0x41, + 0x85, 0x80, 0x80, 0x80, 0x00, 0x36, 0x02, 0x1c, 0x20, 0x04, 0x41, 0x82, + 0x80, 0x80, 0x80, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x10, 0xd9, 0x80, + 0x80, 0x80, 0x00, 0x21, 0x04, 0x0b, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x0b, 0x65, 0x01, 0x01, 0x7f, + 0x02, 0x40, 0x41, 0xf0, 0x95, 0x80, 0x80, 0x00, 0x20, 0x01, 0x2c, 0x00, + 0x00, 0x41, 0x04, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x00, 0x41, + 0x00, 0x41, 0x1c, 0x36, 0x02, 0xb4, 0x9e, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x0f, 0x0b, 0x41, 0x00, 0x21, 0x02, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, + 0x10, 0xd4, 0x80, 0x80, 0x80, 0x00, 0x10, 0xd0, 0x80, 0x80, 0x80, 0x00, + 0x22, 0x00, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, + 0xda, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x0d, 0x00, 0x20, 0x00, 0x10, + 0xab, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, 0x02, 0x0b, 0x20, + 0x02, 0x0b, 0xb1, 0x01, 0x01, 0x01, 0x7e, 0x02, 0x40, 0x20, 0x02, 0x41, + 0x03, 0x49, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x1c, 0x36, 0x02, 0xb4, 0x9e, + 0x80, 0x80, 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x01, 0xac, 0x21, 0x03, + 0x02, 0x40, 0x20, 0x02, 0x41, 0x01, 0x47, 0x0d, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x08, 0x22, 0x01, 0x45, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x04, 0x6b, 0xac, 0x7d, 0x21, 0x03, 0x0b, 0x02, 0x40, + 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, + 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x20, 0x00, 0x28, 0x02, + 0x14, 0x0d, 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x36, + 0x02, 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x10, 0x02, 0x40, 0x20, + 0x00, 0x20, 0x03, 0x20, 0x02, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x42, 0x00, 0x59, 0x0d, 0x00, 0x41, 0x7f, + 0x0f, 0x0b, 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x00, 0x41, 0x6f, 0x71, 0x36, 0x02, 0x00, 0x41, 0x00, + 0x0b, 0x85, 0x01, 0x01, 0x02, 0x7f, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x3c, 0x22, 0x01, 0x41, 0x7f, 0x6a, 0x20, 0x01, 0x72, 0x36, 0x02, 0x3c, + 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, + 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x20, + 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, + 0x10, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, + 0x71, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x20, 0x72, 0x36, + 0x02, 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x28, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x6a, 0x22, 0x02, 0x36, 0x02, 0x08, + 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, 0x01, 0x41, 0x1b, 0x74, + 0x41, 0x1f, 0x75, 0x0b, 0xc4, 0x01, 0x01, 0x03, 0x7f, 0x20, 0x03, 0x20, + 0x03, 0x28, 0x02, 0x3c, 0x22, 0x04, 0x41, 0x7f, 0x6a, 0x20, 0x04, 0x72, + 0x36, 0x02, 0x3c, 0x20, 0x02, 0x20, 0x01, 0x6c, 0x21, 0x05, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x04, 0x22, 0x04, 0x20, 0x03, 0x28, + 0x02, 0x08, 0x22, 0x06, 0x47, 0x0d, 0x00, 0x20, 0x05, 0x21, 0x04, 0x0c, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x04, 0x20, 0x06, 0x20, 0x04, 0x6b, 0x22, + 0x06, 0x20, 0x05, 0x20, 0x06, 0x20, 0x05, 0x49, 0x1b, 0x22, 0x06, 0x10, + 0xa8, 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x20, 0x03, 0x20, 0x04, 0x20, + 0x06, 0x6a, 0x36, 0x02, 0x04, 0x20, 0x05, 0x20, 0x06, 0x6b, 0x21, 0x04, + 0x20, 0x00, 0x20, 0x06, 0x6a, 0x21, 0x00, 0x0b, 0x20, 0x02, 0x41, 0x00, + 0x20, 0x01, 0x1b, 0x21, 0x06, 0x02, 0x40, 0x20, 0x04, 0x45, 0x0d, 0x00, + 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x10, 0xdd, 0x80, 0x80, + 0x80, 0x00, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x00, 0x20, 0x04, 0x20, 0x03, + 0x28, 0x02, 0x1c, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x22, 0x02, + 0x0d, 0x01, 0x0b, 0x20, 0x05, 0x20, 0x04, 0x6b, 0x20, 0x01, 0x6e, 0x0f, + 0x0b, 0x20, 0x00, 0x20, 0x02, 0x6a, 0x21, 0x00, 0x20, 0x04, 0x20, 0x02, + 0x6b, 0x22, 0x04, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x06, 0x0b, 0xe8, 0x02, + 0x01, 0x03, 0x7f, 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, + 0x01, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xb0, 0x9d, 0x80, 0x80, 0x00, + 0x45, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xb0, 0x9d, 0x80, 0x80, 0x00, + 0x10, 0xdf, 0x80, 0x80, 0x80, 0x00, 0x21, 0x01, 0x0b, 0x02, 0x40, 0x41, + 0x00, 0x28, 0x02, 0xa8, 0x9e, 0x80, 0x80, 0x00, 0x45, 0x0d, 0x00, 0x41, + 0x00, 0x28, 0x02, 0xa8, 0x9e, 0x80, 0x80, 0x00, 0x10, 0xdf, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x01, 0x72, 0x21, 0x01, 0x0b, 0x02, 0x40, 0x10, 0xb3, + 0x80, 0x80, 0x80, 0x00, 0x28, 0x02, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, + 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, + 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x0d, 0x00, 0x41, + 0x7f, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, + 0x04, 0x22, 0x02, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x03, 0x46, 0x0d, + 0x00, 0x20, 0x00, 0x20, 0x02, 0x20, 0x03, 0x6b, 0xac, 0x41, 0x01, 0x20, + 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, + 0x0b, 0x41, 0x00, 0x21, 0x02, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, + 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, 0x10, 0x20, 0x00, 0x42, 0x00, 0x37, + 0x02, 0x04, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x72, 0x21, 0x01, 0x0b, 0x20, + 0x00, 0x28, 0x02, 0x34, 0x22, 0x00, 0x0d, 0x00, 0x0b, 0x0b, 0x10, 0xb4, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x20, 0x00, 0x28, 0x02, 0x14, 0x0d, + 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x04, + 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, + 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, + 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, + 0x03, 0x10, 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, 0x04, 0x41, 0x00, 0x0b, + 0x02, 0x00, 0x0b, 0x98, 0x01, 0x01, 0x05, 0x7f, 0x20, 0x00, 0x10, 0xdf, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x01, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x0c, 0x11, 0x82, 0x80, 0x80, 0x80, 0x00, 0x00, 0x21, 0x02, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x0d, 0x00, 0x20, 0x00, + 0x10, 0xe0, 0x80, 0x80, 0x80, 0x00, 0x10, 0xb3, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x03, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x30, 0x22, 0x04, 0x45, + 0x0d, 0x00, 0x20, 0x04, 0x20, 0x00, 0x28, 0x02, 0x34, 0x36, 0x02, 0x34, + 0x0b, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x34, 0x22, 0x05, 0x45, 0x0d, + 0x00, 0x20, 0x05, 0x20, 0x04, 0x36, 0x02, 0x30, 0x0b, 0x02, 0x40, 0x20, + 0x03, 0x28, 0x02, 0x00, 0x20, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x03, 0x20, + 0x05, 0x36, 0x02, 0x00, 0x0b, 0x10, 0xb4, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x50, 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, + 0x10, 0x92, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x01, 0x72, + 0x0b, 0xd2, 0x06, 0x01, 0x51, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x02, 0x41, 0xb0, 0x01, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, + 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x21, 0x05, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0xac, 0x01, 0x20, 0x04, + 0x20, 0x00, 0x36, 0x02, 0xa8, 0x01, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, + 0xa4, 0x01, 0x41, 0x00, 0x21, 0x06, 0x20, 0x04, 0x20, 0x06, 0x36, 0x02, + 0x9c, 0x01, 0x41, 0xb9, 0x98, 0x80, 0x80, 0x00, 0x21, 0x07, 0x41, 0x00, + 0x21, 0x08, 0x20, 0x07, 0x20, 0x08, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x41, 0xdc, 0x95, 0x80, 0x80, 0x00, 0x21, 0x09, 0x41, 0xff, 0x03, + 0x21, 0x0a, 0x20, 0x09, 0x20, 0x0a, 0x10, 0xd1, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x0b, 0x20, 0x04, 0x20, 0x0b, 0x36, 0x02, 0xa0, 0x01, 0x20, 0x04, + 0x28, 0x02, 0xa0, 0x01, 0x21, 0x0c, 0x41, 0x00, 0x21, 0x0d, 0x20, 0x0c, + 0x21, 0x0e, 0x20, 0x0d, 0x21, 0x0f, 0x20, 0x0e, 0x20, 0x0f, 0x48, 0x21, + 0x10, 0x41, 0x01, 0x21, 0x11, 0x20, 0x10, 0x20, 0x11, 0x71, 0x21, 0x12, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x12, 0x45, 0x0d, 0x00, 0x41, 0xb4, 0x9e, + 0x80, 0x80, 0x00, 0x21, 0x13, 0x20, 0x13, 0x28, 0x02, 0x00, 0x21, 0x14, + 0x20, 0x04, 0x20, 0x14, 0x36, 0x02, 0xa0, 0x01, 0x20, 0x04, 0x28, 0x02, + 0xa0, 0x01, 0x21, 0x15, 0x20, 0x04, 0x20, 0x15, 0x36, 0x02, 0x00, 0x41, + 0xcd, 0x96, 0x80, 0x80, 0x00, 0x21, 0x16, 0x20, 0x16, 0x20, 0x04, 0x10, + 0xc7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x7f, 0x21, 0x17, 0x20, 0x04, + 0x20, 0x17, 0x36, 0x02, 0xac, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x28, + 0x02, 0xa0, 0x01, 0x21, 0x18, 0x20, 0x04, 0x20, 0x18, 0x36, 0x02, 0x50, + 0x41, 0xe9, 0x96, 0x80, 0x80, 0x00, 0x21, 0x19, 0x41, 0xd0, 0x00, 0x21, + 0x1a, 0x20, 0x04, 0x20, 0x1a, 0x6a, 0x21, 0x1b, 0x20, 0x19, 0x20, 0x1b, + 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0xc7, 0x95, 0x80, 0x80, + 0x00, 0x21, 0x1c, 0x41, 0xfe, 0x95, 0x80, 0x80, 0x00, 0x21, 0x1d, 0x20, + 0x1c, 0x20, 0x1d, 0x10, 0xdb, 0x80, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, + 0x04, 0x20, 0x1e, 0x36, 0x02, 0x98, 0x01, 0x20, 0x04, 0x28, 0x02, 0x98, + 0x01, 0x21, 0x1f, 0x41, 0x00, 0x21, 0x20, 0x20, 0x1f, 0x21, 0x21, 0x20, + 0x20, 0x21, 0x22, 0x20, 0x21, 0x20, 0x22, 0x47, 0x21, 0x23, 0x41, 0x01, + 0x21, 0x24, 0x20, 0x23, 0x20, 0x24, 0x71, 0x21, 0x25, 0x02, 0x40, 0x20, + 0x25, 0x0d, 0x00, 0x41, 0xa8, 0x96, 0x80, 0x80, 0x00, 0x21, 0x26, 0x41, + 0x00, 0x21, 0x27, 0x20, 0x26, 0x20, 0x27, 0x10, 0xc7, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x41, 0x7f, 0x21, 0x28, 0x20, 0x04, 0x20, 0x28, 0x36, 0x02, + 0xac, 0x01, 0x0c, 0x01, 0x0b, 0x41, 0xbe, 0x96, 0x80, 0x80, 0x00, 0x21, + 0x29, 0x41, 0x00, 0x21, 0x2a, 0x20, 0x29, 0x20, 0x2a, 0x10, 0xc7, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x41, 0x88, 0x96, 0x80, 0x80, 0x00, 0x21, 0x2b, + 0x20, 0x04, 0x20, 0x2b, 0x36, 0x02, 0x94, 0x01, 0x41, 0x0d, 0x21, 0x2c, + 0x20, 0x04, 0x20, 0x2c, 0x36, 0x02, 0x90, 0x01, 0x20, 0x04, 0x28, 0x02, + 0x94, 0x01, 0x21, 0x2d, 0x20, 0x04, 0x28, 0x02, 0x98, 0x01, 0x21, 0x2e, + 0x41, 0x01, 0x21, 0x2f, 0x41, 0x0d, 0x21, 0x30, 0x20, 0x2d, 0x20, 0x2f, + 0x20, 0x30, 0x20, 0x2e, 0x10, 0xb8, 0x80, 0x80, 0x80, 0x00, 0x21, 0x31, + 0x20, 0x04, 0x20, 0x31, 0x36, 0x02, 0x8c, 0x01, 0x20, 0x04, 0x28, 0x02, + 0x8c, 0x01, 0x21, 0x32, 0x20, 0x04, 0x20, 0x32, 0x36, 0x02, 0x10, 0x41, + 0x8f, 0x97, 0x80, 0x80, 0x00, 0x21, 0x33, 0x41, 0x10, 0x21, 0x34, 0x20, + 0x04, 0x20, 0x34, 0x6a, 0x21, 0x35, 0x20, 0x33, 0x20, 0x35, 0x10, 0xc7, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x04, 0x28, 0x02, 0x98, 0x01, 0x21, + 0x36, 0x41, 0x00, 0x21, 0x37, 0x20, 0x36, 0x20, 0x37, 0x20, 0x37, 0x10, + 0xdc, 0x80, 0x80, 0x80, 0x00, 0x21, 0x38, 0x20, 0x04, 0x20, 0x38, 0x36, + 0x02, 0xa0, 0x01, 0x20, 0x04, 0x28, 0x02, 0xa0, 0x01, 0x21, 0x39, 0x20, + 0x04, 0x20, 0x39, 0x36, 0x02, 0x20, 0x41, 0xfc, 0x96, 0x80, 0x80, 0x00, + 0x21, 0x3a, 0x41, 0x20, 0x21, 0x3b, 0x20, 0x04, 0x20, 0x3b, 0x6a, 0x21, + 0x3c, 0x20, 0x3a, 0x20, 0x3c, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x41, 0xe0, 0x00, 0x21, 0x3d, 0x20, 0x04, 0x20, 0x3d, 0x6a, 0x21, 0x3e, + 0x20, 0x3e, 0x21, 0x3f, 0x20, 0x04, 0x28, 0x02, 0x98, 0x01, 0x21, 0x40, + 0x41, 0x01, 0x21, 0x41, 0x41, 0x20, 0x21, 0x42, 0x20, 0x3f, 0x20, 0x41, + 0x20, 0x42, 0x20, 0x40, 0x10, 0xde, 0x80, 0x80, 0x80, 0x00, 0x21, 0x43, + 0x20, 0x04, 0x20, 0x43, 0x36, 0x02, 0x8c, 0x01, 0x20, 0x04, 0x28, 0x02, + 0x8c, 0x01, 0x21, 0x44, 0x20, 0x04, 0x20, 0x44, 0x36, 0x02, 0x30, 0x41, + 0xa3, 0x97, 0x80, 0x80, 0x00, 0x21, 0x45, 0x41, 0x30, 0x21, 0x46, 0x20, + 0x04, 0x20, 0x46, 0x6a, 0x21, 0x47, 0x20, 0x45, 0x20, 0x47, 0x10, 0xc7, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0xe0, 0x00, 0x21, 0x48, 0x20, 0x04, + 0x20, 0x48, 0x6a, 0x21, 0x49, 0x20, 0x49, 0x21, 0x4a, 0x20, 0x04, 0x20, + 0x4a, 0x36, 0x02, 0x40, 0x41, 0x96, 0x96, 0x80, 0x80, 0x00, 0x21, 0x4b, + 0x41, 0xc0, 0x00, 0x21, 0x4c, 0x20, 0x04, 0x20, 0x4c, 0x6a, 0x21, 0x4d, + 0x20, 0x4b, 0x20, 0x4d, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, + 0x04, 0x28, 0x02, 0x98, 0x01, 0x21, 0x4e, 0x20, 0x4e, 0x10, 0xe1, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, 0x4f, 0x20, 0x04, 0x20, 0x4f, + 0x36, 0x02, 0xac, 0x01, 0x0b, 0x20, 0x04, 0x28, 0x02, 0xac, 0x01, 0x21, + 0x50, 0x41, 0xb0, 0x01, 0x21, 0x51, 0x20, 0x04, 0x20, 0x51, 0x6a, 0x21, + 0x52, 0x20, 0x52, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x50, 0x0f, + 0x0b, 0x0b, 0xbb, 0x16, 0x02, 0x00, 0x41, 0x80, 0x08, 0x0b, 0xc0, 0x14, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x00, 0x49, 0x6c, 0x6c, 0x65, + 0x67, 0x61, 0x6c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x00, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, + 0x65, 0x6e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x4e, 0x6f, 0x74, 0x20, + 0x61, 0x20, 0x74, 0x74, 0x79, 0x00, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x00, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x00, + 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x20, 0x6f, 0x72, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x00, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x00, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x65, + 0x78, 0x69, 0x73, 0x74, 0x73, 0x00, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, + 0x74, 0x6f, 0x6f, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x00, + 0x4e, 0x6f, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x6c, 0x65, 0x66, + 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x00, + 0x4f, 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, + 0x79, 0x00, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x62, + 0x75, 0x73, 0x79, 0x00, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, + 0x74, 0x65, 0x64, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x63, + 0x61, 0x6c, 0x6c, 0x00, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x69, 0x6c, 0x79, + 0x20, 0x75, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x00, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x73, 0x65, 0x65, + 0x6b, 0x00, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x2d, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x52, 0x65, 0x61, 0x64, + 0x2d, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x79, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x6d, 0x70, 0x74, + 0x79, 0x00, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x72, 0x65, 0x73, 0x65, 0x74, 0x20, 0x62, 0x79, 0x20, 0x70, 0x65, + 0x65, 0x72, 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x74, 0x69, 0x6d, 0x65, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x00, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, + 0x66, 0x75, 0x73, 0x65, 0x64, 0x00, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x69, + 0x73, 0x20, 0x75, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, + 0x65, 0x00, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x75, 0x73, 0x65, 0x00, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x20, + 0x70, 0x69, 0x70, 0x65, 0x00, 0x49, 0x2f, 0x4f, 0x20, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x00, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x00, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, + 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x00, 0x4e, 0x6f, 0x74, 0x20, + 0x61, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x00, + 0x49, 0x73, 0x20, 0x61, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x00, 0x54, 0x65, 0x78, 0x74, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x20, 0x62, 0x75, 0x73, 0x79, 0x00, 0x45, 0x78, 0x65, 0x63, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, + 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x72, 0x67, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x6c, + 0x6f, 0x6e, 0x67, 0x00, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x63, + 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x46, + 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x6f, 0x6f, 0x20, + 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x54, 0x6f, 0x6f, 0x20, 0x6d, 0x61, 0x6e, + 0x79, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x4e, + 0x6f, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x00, 0x42, 0x61, 0x64, 0x20, 0x66, 0x69, 0x6c, + 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, + 0x00, 0x4e, 0x6f, 0x20, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x20, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x00, 0x42, 0x61, 0x64, 0x20, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x00, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x74, + 0x6f, 0x6f, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x00, 0x54, 0x6f, 0x6f, + 0x20, 0x6d, 0x61, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x00, + 0x4e, 0x6f, 0x20, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x61, 0x76, 0x61, + 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x6f, 0x63, 0x6b, + 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, + 0x00, 0x53, 0x74, 0x61, 0x74, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, + 0x65, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x50, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x20, 0x64, 0x69, 0x65, 0x64, 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, + 0x64, 0x00, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x65, 0x64, 0x00, 0x4e, 0x6f, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, + 0x20, 0x74, 0x79, 0x70, 0x65, 0x00, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, + 0x00, 0x4c, 0x69, 0x6e, 0x6b, 0x20, 0x68, 0x61, 0x73, 0x20, 0x62, 0x65, + 0x65, 0x6e, 0x20, 0x73, 0x65, 0x76, 0x65, 0x72, 0x65, 0x64, 0x00, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x00, 0x42, 0x61, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x73, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x00, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x72, + 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x00, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x6c, 0x61, 0x72, 0x67, + 0x65, 0x00, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x77, + 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x00, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x73, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x20, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x20, 0x62, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x00, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x73, 0x20, 0x64, + 0x6f, 0x77, 0x6e, 0x00, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, + 0x75, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x00, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, + 0x65, 0x73, 0x65, 0x74, 0x20, 0x62, 0x79, 0x20, 0x6e, 0x65, 0x74, 0x77, + 0x6f, 0x72, 0x6b, 0x00, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x4e, + 0x6f, 0x20, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x00, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x53, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, + 0x69, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x00, + 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, + 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x74, + 0x61, 0x6c, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x68, 0x61, 0x6e, + 0x64, 0x6c, 0x65, 0x00, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x20, 0x65, 0x78, + 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x00, 0x4d, 0x75, 0x6c, 0x74, 0x69, + 0x68, 0x6f, 0x70, 0x20, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, + 0x64, 0x00, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x75, 0x66, 0x66, 0x69, 0x63, 0x69, + 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x75, 0x02, 0x4e, 0x00, 0xd6, 0x01, + 0xe2, 0x04, 0xb9, 0x04, 0x18, 0x01, 0x8e, 0x05, 0xed, 0x02, 0x16, 0x04, + 0xf2, 0x00, 0x97, 0x03, 0x01, 0x03, 0x38, 0x05, 0xaf, 0x01, 0x82, 0x01, + 0x4f, 0x03, 0x2f, 0x04, 0x1e, 0x00, 0xd4, 0x05, 0xa2, 0x00, 0x12, 0x03, + 0x1e, 0x03, 0xc2, 0x01, 0xde, 0x03, 0x08, 0x00, 0xac, 0x05, 0x00, 0x01, + 0x64, 0x02, 0xf1, 0x01, 0x65, 0x05, 0x34, 0x02, 0x8c, 0x02, 0xcf, 0x02, + 0x2d, 0x03, 0x4c, 0x04, 0xe3, 0x05, 0x9f, 0x02, 0xf8, 0x04, 0x1c, 0x05, + 0x08, 0x05, 0xb1, 0x02, 0x4b, 0x05, 0x15, 0x02, 0x78, 0x00, 0x52, 0x02, + 0x3c, 0x03, 0xf1, 0x03, 0xe4, 0x00, 0xc3, 0x03, 0x7d, 0x04, 0xcc, 0x00, + 0xaa, 0x03, 0x79, 0x05, 0x24, 0x02, 0x6e, 0x01, 0x6d, 0x03, 0x22, 0x04, + 0xab, 0x04, 0x44, 0x00, 0xfb, 0x01, 0xae, 0x00, 0x83, 0x03, 0x60, 0x00, + 0xe5, 0x01, 0x07, 0x04, 0x94, 0x04, 0x5e, 0x04, 0x2b, 0x00, 0x58, 0x01, + 0x39, 0x01, 0x92, 0x00, 0xc2, 0x05, 0x9b, 0x01, 0x43, 0x02, 0x46, 0x01, + 0xf6, 0x05, 0x2d, 0x2b, 0x20, 0x20, 0x20, 0x30, 0x58, 0x30, 0x78, 0x00, + 0x2d, 0x30, 0x58, 0x2b, 0x30, 0x58, 0x20, 0x30, 0x58, 0x2d, 0x30, 0x78, + 0x2b, 0x30, 0x78, 0x20, 0x30, 0x78, 0x00, 0x2f, 0x6c, 0x66, 0x73, 0x2f, + 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0x2f, 0x6c, 0x66, 0x73, 0x2f, 0x66, 0x6f, 0x6c, + 0x64, 0x65, 0x72, 0x00, 0x6e, 0x61, 0x6e, 0x00, 0x69, 0x6e, 0x66, 0x00, + 0x72, 0x77, 0x61, 0x00, 0x4e, 0x41, 0x4e, 0x00, 0x49, 0x4e, 0x46, 0x00, + 0x2e, 0x00, 0x77, 0x2b, 0x00, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x00, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, + 0x21, 0x00, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x72, 0x65, 0x61, + 0x64, 0x20, 0x3d, 0x20, 0x25, 0x73, 0x0a, 0x00, 0x66, 0x6f, 0x70, 0x65, + 0x6e, 0x20, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x00, 0x66, 0x6f, 0x70, 0x65, 0x6e, 0x20, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x0a, 0x00, 0x6d, 0x6b, 0x64, + 0x69, 0x72, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x25, 0x64, 0x0a, + 0x00, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x65, 0x64, 0x20, 0x25, 0x64, 0x0a, 0x00, 0x66, 0x73, 0x65, 0x65, + 0x6b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x25, + 0x64, 0x0a, 0x00, 0x66, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x25, 0x64, 0x0a, 0x00, 0x66, + 0x72, 0x65, 0x61, 0x64, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, + 0x64, 0x20, 0x25, 0x64, 0x0a, 0x00, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x64, 0x6f, + 0x75, 0x62, 0x6c, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, + 0x69, 0x73, 0x20, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x6c, 0x79, + 0x20, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x2e, 0x0a, 0x54, + 0x6f, 0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x69, 0x74, 0x2c, + 0x20, 0x61, 0x64, 0x64, 0x20, 0x2d, 0x6c, 0x63, 0x2d, 0x70, 0x72, 0x69, + 0x6e, 0x74, 0x73, 0x63, 0x61, 0x6e, 0x2d, 0x6c, 0x6f, 0x6e, 0x67, 0x2d, + 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x2e, 0x0a, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, + 0x65, 0x62, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, 0x20, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x21, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0a, 0x00, + 0x19, 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x11, 0x0a, 0x19, 0x19, 0x19, 0x03, + 0x0a, 0x07, 0x00, 0x01, 0x1b, 0x09, 0x0b, 0x18, 0x00, 0x00, 0x09, 0x06, + 0x0b, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x19, 0x00, 0x00, 0x00, 0x19, 0x19, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x19, 0x00, 0x0a, 0x0d, 0x19, 0x19, 0x19, 0x00, 0x0d, 0x00, + 0x00, 0x02, 0x00, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x1a, 0x00, + 0x00, 0x00, 0x1a, 0x1a, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x1a, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x09, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, + 0x00, 0x16, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x00, 0x41, 0xc0, 0x1c, + 0x0b, 0xec, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x38, 0x11, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x64, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8, 0x0e, 0x00, 0x00, 0x00, + 0xcd, 0x0e, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0xfd, 0x0d, 0x63, 0x00, + 0x2a, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, + 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x61, + 0x72, 0x67, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x01, 0x30, 0x5f, 0x5f, 0x69, + 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x02, 0x2a, 0x5f, + 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, + 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, + 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x03, 0x2f, 0x5f, 0x5f, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, + 0x61, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x04, 0x35, 0x5f, 0x5f, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, + 0x74, 0x61, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x61, 0x67, + 0x73, 0x05, 0x30, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, + 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, + 0x5f, 0x66, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x74, 0x61, 0x74, 0x5f, + 0x67, 0x65, 0x74, 0x06, 0x35, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, + 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x07, 0x29, + 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, + 0x5f, 0x72, 0x65, 0x61, 0x64, 0x08, 0x29, 0x5f, 0x5f, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x6b, + 0x09, 0x2a, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, + 0x66, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x0a, 0x37, 0x5f, 0x5f, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x70, 0x61, 0x74, 0x68, + 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x0b, 0x2b, 0x5f, 0x5f, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x6f, 0x70, + 0x65, 0x6e, 0x0c, 0x2b, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x31, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x0d, + 0x11, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x6d, 0x5f, 0x63, 0x61, 0x6c, 0x6c, + 0x5f, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x0e, 0x2c, 0x75, 0x6e, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x5f, 0x77, 0x65, 0x61, 0x6b, 0x3a, 0x5f, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x66, 0x69, + 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x0f, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x10, 0x06, 0x6d, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x11, 0x08, 0x64, 0x6c, + 0x6d, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x12, 0x04, 0x66, 0x72, 0x65, 0x65, + 0x13, 0x06, 0x64, 0x6c, 0x66, 0x72, 0x65, 0x65, 0x14, 0x06, 0x63, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x15, 0x05, 0x5f, 0x45, 0x78, 0x69, 0x74, 0x16, + 0x0b, 0x5f, 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x76, 0x6f, 0x69, 0x64, + 0x17, 0x0f, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x61, 0x72, 0x67, + 0x73, 0x5f, 0x67, 0x65, 0x74, 0x18, 0x15, 0x5f, 0x5f, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, + 0x5f, 0x67, 0x65, 0x74, 0x19, 0x0f, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x66, 0x64, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x1a, 0x14, 0x5f, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, + 0x74, 0x61, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x1b, 0x1a, 0x5f, 0x5f, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, 0x61, + 0x74, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x1c, + 0x15, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x70, + 0x72, 0x65, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x1d, 0x1a, + 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x70, 0x72, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x1e, 0x0e, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, + 0x64, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x1f, 0x0e, 0x5f, 0x5f, 0x77, 0x61, + 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x6b, 0x20, 0x0f, + 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x21, 0x1c, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x22, 0x10, 0x5f, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x6f, + 0x70, 0x65, 0x6e, 0x23, 0x10, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x24, 0x05, 0x61, + 0x62, 0x6f, 0x72, 0x74, 0x25, 0x04, 0x73, 0x62, 0x72, 0x6b, 0x26, 0x05, + 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x27, 0x11, 0x5f, 0x5f, 0x77, 0x61, 0x73, + 0x6d, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x5f, 0x64, 0x74, 0x6f, 0x72, 0x73, + 0x28, 0x06, 0x6d, 0x65, 0x6d, 0x63, 0x70, 0x79, 0x29, 0x06, 0x6d, 0x65, + 0x6d, 0x73, 0x65, 0x74, 0x2a, 0x06, 0x73, 0x74, 0x72, 0x6c, 0x65, 0x6e, + 0x2b, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2c, 0x0d, 0x5f, 0x5f, 0x73, + 0x74, 0x64, 0x69, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x2d, 0x06, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x76, 0x2e, 0x0d, 0x5f, 0x5f, 0x73, 0x74, + 0x64, 0x69, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x2f, 0x08, 0x5f, + 0x5f, 0x69, 0x73, 0x61, 0x74, 0x74, 0x79, 0x30, 0x0e, 0x5f, 0x5f, 0x73, + 0x74, 0x64, 0x6f, 0x75, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x31, + 0x07, 0x5f, 0x5f, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x32, 0x0c, 0x5f, 0x5f, + 0x73, 0x74, 0x64, 0x69, 0x6f, 0x5f, 0x73, 0x65, 0x65, 0x6b, 0x33, 0x0a, + 0x5f, 0x5f, 0x6f, 0x66, 0x6c, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x34, 0x0c, + 0x5f, 0x5f, 0x6f, 0x66, 0x6c, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, + 0x35, 0x0c, 0x5f, 0x5f, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x5f, 0x65, 0x78, + 0x69, 0x74, 0x36, 0x09, 0x5f, 0x5f, 0x74, 0x6f, 0x77, 0x72, 0x69, 0x74, + 0x65, 0x37, 0x09, 0x5f, 0x5f, 0x66, 0x77, 0x72, 0x69, 0x74, 0x65, 0x78, + 0x38, 0x06, 0x66, 0x77, 0x72, 0x69, 0x74, 0x65, 0x39, 0x05, 0x64, 0x75, + 0x6d, 0x6d, 0x79, 0x3a, 0x09, 0x5f, 0x5f, 0x6c, 0x63, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x3b, 0x08, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x3c, 0x06, 0x6d, 0x65, 0x6d, 0x63, 0x68, 0x72, 0x3d, 0x07, 0x73, 0x74, + 0x72, 0x6e, 0x6c, 0x65, 0x6e, 0x3e, 0x07, 0x77, 0x63, 0x72, 0x74, 0x6f, + 0x6d, 0x62, 0x3f, 0x06, 0x77, 0x63, 0x74, 0x6f, 0x6d, 0x62, 0x40, 0x05, + 0x66, 0x72, 0x65, 0x78, 0x70, 0x41, 0x05, 0x66, 0x70, 0x75, 0x74, 0x73, + 0x42, 0x08, 0x76, 0x66, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x43, 0x0b, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x5f, 0x63, 0x6f, 0x72, 0x65, 0x44, + 0x07, 0x70, 0x6f, 0x70, 0x5f, 0x61, 0x72, 0x67, 0x45, 0x03, 0x70, 0x61, + 0x64, 0x46, 0x19, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x64, 0x6f, 0x75, 0x62, + 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x47, 0x06, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, + 0x48, 0x06, 0x6d, 0x65, 0x6d, 0x63, 0x6d, 0x70, 0x49, 0x06, 0x73, 0x74, + 0x72, 0x64, 0x75, 0x70, 0x4a, 0x1c, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x70, 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, + 0x65, 0x5f, 0x70, 0x72, 0x65, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x4b, 0x27, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x6f, 0x70, 0x65, + 0x6e, 0x65, 0x64, 0x5f, 0x66, 0x64, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, + 0x6b, 0x65, 0x64, 0x4c, 0x17, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x6c, + 0x69, 0x62, 0x63, 0x5f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x72, 0x65, 0x6c, + 0x70, 0x61, 0x74, 0x68, 0x4d, 0x17, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x61, 0x62, + 0x73, 0x70, 0x61, 0x74, 0x68, 0x4e, 0x1e, 0x5f, 0x5f, 0x77, 0x61, 0x73, + 0x69, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x6e, 0x6f, 0x63, 0x77, 0x64, 0x5f, + 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x74, 0x5f, 0x6e, 0x6f, 0x6d, 0x6f, 0x64, + 0x65, 0x4f, 0x1f, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x6c, 0x69, 0x62, + 0x63, 0x5f, 0x6e, 0x6f, 0x63, 0x77, 0x64, 0x5f, 0x6d, 0x6b, 0x64, 0x69, + 0x72, 0x61, 0x74, 0x5f, 0x6e, 0x6f, 0x6d, 0x6f, 0x64, 0x65, 0x50, 0x16, + 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x6f, + 0x70, 0x65, 0x6e, 0x5f, 0x6e, 0x6f, 0x6d, 0x6f, 0x64, 0x65, 0x51, 0x05, + 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x0b, 0x5f, 0x5f, 0x73, 0x74, 0x72, + 0x63, 0x68, 0x72, 0x6e, 0x75, 0x6c, 0x53, 0x06, 0x73, 0x74, 0x72, 0x63, + 0x68, 0x72, 0x54, 0x0c, 0x5f, 0x5f, 0x66, 0x6d, 0x6f, 0x64, 0x65, 0x66, + 0x6c, 0x61, 0x67, 0x73, 0x55, 0x05, 0x66, 0x63, 0x6e, 0x74, 0x6c, 0x56, + 0x05, 0x72, 0x65, 0x61, 0x64, 0x76, 0x57, 0x04, 0x72, 0x65, 0x61, 0x64, + 0x58, 0x0c, 0x5f, 0x5f, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x5f, 0x72, 0x65, + 0x61, 0x64, 0x59, 0x09, 0x5f, 0x5f, 0x6f, 0x66, 0x6c, 0x5f, 0x61, 0x64, + 0x64, 0x5a, 0x08, 0x5f, 0x5f, 0x66, 0x64, 0x6f, 0x70, 0x65, 0x6e, 0x5b, + 0x05, 0x66, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x05, 0x66, 0x73, 0x65, 0x65, + 0x6b, 0x5d, 0x08, 0x5f, 0x5f, 0x74, 0x6f, 0x72, 0x65, 0x61, 0x64, 0x5e, + 0x05, 0x66, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x06, 0x66, 0x66, 0x6c, 0x75, + 0x73, 0x68, 0x60, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x61, 0x06, 0x66, + 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x62, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x07, + 0x33, 0x02, 0x00, 0x0f, 0x5f, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x01, 0x1f, 0x47, 0x4f, 0x54, + 0x2e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2e, 0x5f, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, + 0x62, 0x61, 0x73, 0x65, 0x09, 0x11, 0x02, 0x00, 0x07, 0x2e, 0x72, 0x6f, + 0x64, 0x61, 0x74, 0x61, 0x01, 0x05, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x00, + 0x26, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, 0x01, + 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x2d, 0x62, + 0x79, 0x01, 0x05, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x06, 0x31, 0x37, 0x2e, + 0x30, 0x2e, 0x36, 0x00, 0x39, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x03, 0x2b, 0x0b, + 0x62, 0x75, 0x6c, 0x6b, 0x2d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x2b, + 0x0f, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x67, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x73, 0x2b, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x65, + 0x78, 0x74 +}; \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-file/src/main.c b/product-mini/platforms/zephyr/simple-file/src/main.c new file mode 100644 index 0000000000..405a97f17b --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/src/main.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// #include + +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "file.h" + +#include +#include +#include +#include +#include +#include +#include + +#define CONFIG_HEAP_MEM_POOL_SIZE WASM_GLOBAL_HEAP_SIZE +#define CONFIG_APP_STACK_SIZE 16384 +#define CONFIG_APP_HEAP_SIZE 16384 + +LOG_MODULE_REGISTER(main); + +static char global_heap_buf[CONFIG_HEAP_MEM_POOL_SIZE] = { 0 }; + +static int app_argc; +static char **app_argv; + +//-------------------------------------------------------------------------------------------// +static int +littlefs_flash_erase(unsigned int id) +{ + const struct flash_area *pfa; + int rc; + + rc = flash_area_open(id, &pfa); + if (rc < 0) { + LOG_ERR("FAIL: unable to find flash area %u: %d\n", id, rc); + return rc; + } + + LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n", id, + (unsigned int)pfa->fa_off, pfa->fa_dev->name, + (unsigned int)pfa->fa_size); + + /* Optional wipe flash contents */ + if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) { + rc = flash_area_erase(pfa, 0, pfa->fa_size); + LOG_ERR("Erasing flash area ... %d", rc); + } + + flash_area_close(pfa); + return rc; +} +#define PARTITION_NODE DT_NODELABEL(lfs1) + +#if DT_NODE_EXISTS(PARTITION_NODE) +FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE); +#else /* PARTITION_NODE */ +FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage); +static struct fs_mount_t lfs_storage_mnt = { + .type = FS_LITTLEFS, + .fs_data = &storage, + .storage_dev = (void *)FIXED_PARTITION_ID(storage_partition), + .mnt_point = "/lfs", +}; +#endif /* PARTITION_NODE */ + +struct fs_mount_t *mountpoint = +#if DT_NODE_EXISTS(PARTITION_NODE) + &FS_FSTAB_ENTRY(PARTITION_NODE) +#else + &lfs_storage_mnt +#endif + ; + +static int +littlefs_mount(struct fs_mount_t *mp) +{ + int rc; + + rc = littlefs_flash_erase((uintptr_t)mp->storage_dev); + if (rc < 0) { + return rc; + } + + /* Do not mount if auto-mount has been enabled */ +#if !DT_NODE_EXISTS(PARTITION_NODE) \ + || !(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT) + rc = fs_mount(mp); + if (rc < 0) { + LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n", + (uintptr_t)mp->storage_dev, mp->mnt_point, rc); + return rc; + } + LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc); +#else + LOG_PRINTK("%s automounted\n", mp->mnt_point); +#endif + + return 0; +} + +//-------------------------------------------------------------------------------------------// +int +main(void) +{ + int start, end; + start = k_uptime_get_32(); + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; + const char *exception; + int rc; + + int log_verbose_level = 2; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + rc = littlefs_mount(mountpoint); + if (rc < 0) { + LOG_ERR("FAIL: mounting %s: %d\n", mountpoint->mnt_point, rc); + return 0; + } + +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + LOG_INF("global heap size: %d", sizeof(global_heap_buf)); +#else +#error "memory allocation scheme is not defined." +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERR("Init runtime environment failed."); + return; + } + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8 *)wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + LOG_INF("Wasm file size: %d", wasm_file_size); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + LOG_ERR("Failed to load module: %s", error_buf); + goto fail1; + } + + /* Set the WASI context */ +#if WASM_ENABLE_LIBC_WASI != 0 +#define DIR_LIST_SIZE 1 + const char *dir_list[DIR_LIST_SIZE] = { + "/lfs", + }; + /* No dir list => No file system + * dir_cont = 0 + * No mapped dir list => No file system + * map_dir_cont = 0 + * No environment variables + * env_count = 0 + * No command line arguments + * argv 0 + */ + wasm_runtime_set_wasi_args(wasm_module, dir_list, DIR_LIST_SIZE, NULL, 0, + NULL, 0, NULL, 0); +#endif + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate( + wasm_module, CONFIG_APP_STACK_SIZE, CONFIG_APP_HEAP_SIZE, + error_buf, sizeof(error_buf)))) { + LOG_ERR("Failed to instantiate module: %s", error_buf); + goto fail2; + } + + /* invoke the main function */ + if (wasm_runtime_lookup_function(wasm_module_inst, "_start") + || wasm_runtime_lookup_function(wasm_module_inst, "__main_argc_argv") + || wasm_runtime_lookup_function(wasm_module_inst, "main")) { + + LOG_INF("main found"); + wasm_application_execute_main(wasm_module_inst, 0, NULL); + LOG_INF("main executed"); + } + else { + LOG_ERR("Failed to lookup function main"); + return -1; + } + + if ((exception = wasm_runtime_get_exception(wasm_module_inst))) + LOG_ERR("get exception: %s", exception); + + rc = wasm_runtime_get_wasi_exit_code(wasm_module_inst); + LOG_INF("wasi exit code: %d", rc); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + + end = k_uptime_get_32(); + + LOG_INF("elapsed: %dms", (end - start)); + + return 0; +} \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-file/to_c_header.py b/product-mini/platforms/zephyr/simple-file/to_c_header.py new file mode 100644 index 0000000000..7712c0b5ab --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/to_c_header.py @@ -0,0 +1,32 @@ +# Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Python script to convert wasm file to byte array in a .h file +import os + +CWD = os.getcwd() +CMAKE_CURRENT_BINARY_DIR = os.getenv('CMAKE_CURRENT_BINARY_DIR', CWD) +CMAKE_CURRENT_SOURCE_DIR = os.getenv('CMAKE_CURRENT_SOURCE_DIR', f'{CWD}/../src') + +LICENCE_HEADER = """/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +""" + +print('CMAKE_CURRENT_BINARY_DIR:', CMAKE_CURRENT_BINARY_DIR) +print('CMAKE_CURRENT_SOURCE_DIR:', CMAKE_CURRENT_SOURCE_DIR) + +# Open the wasm file in binary mode and read the data +with open(f'{CWD}/wasm-apps/file.wasm', 'rb') as f: + wasm_bytes = f.read() + +# Convert the bytes to a comma-separated string of hex values +byte_array = ', '.join(f'0x{byte:02x}' for byte in wasm_bytes) + +# Create the output string +output = f'{LICENCE_HEADER}\nunsigned char __aligned(4) wasm_test_file[] = {{ {byte_array} }};' + +# Write the output string to the .h file +with open(f'{CWD}/src/file.h', 'w') as f: + f.write(output) diff --git a/product-mini/platforms/zephyr/simple-file/wasm-apps/file.c b/product-mini/platforms/zephyr/simple-file/wasm-apps/file.c new file mode 100644 index 0000000000..be25e55166 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-file/wasm-apps/file.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +// Zephyr +#define CWD "/lfs" +#define FOLDER_PATH CWD "/folder" +#define FILE_PATH CWD "folder/test.txt" + +int +main(int argc, char **argv) +{ + int rc; + const int zero = 0; + printf("Hello WebAssembly Module !\n"); + + rc = mkdir(FOLDER_PATH, 0777); + if (rc < 0) { + rc = errno; + printf("mkdir failed with error %d\n", rc); + return -1; + } + printf("mkdir returned %d\n", rc); + + FILE *file = fopen(FILE_PATH, "w+"); + if (!file) { + printf("fopen Failed to open\n"); + return -1; + } + printf("fopen Succeed\n"); + + const char *data = "Hello, World!"; + size_t len = 13; + size_t nitems = fwrite(data, sizeof(char), 13, file); + printf("fwrite returned %d\n", (int)nitems); + + rc = fseek(file, 0, SEEK_SET); + printf("fseek returned %d\n", rc); + + char buffer[32]; + nitems = fread(buffer, sizeof(char), 32, file); + printf("fread returned %d\n", (int)nitems); + printf("buffer read = %s\n", buffer); + + fclose(file); + + return 0; +} diff --git a/product-mini/platforms/zephyr/simple-http/CMakeLists.txt b/product-mini/platforms/zephyr/simple-http/CMakeLists.txt new file mode 100644 index 0000000000..e0236c04c1 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/CMakeLists.txt @@ -0,0 +1,89 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.8.2) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(wamr) + +enable_language (ASM) + +set (WAMR_BUILD_PLATFORM "zephyr") + +# WAMR Configuration: +set (WAMR_BUILD_TARGET "THUMB") +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) # printf +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_LIB_PTHREAD 0) +set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) +set (WAMR_BUILD_GLOBAL_HEAP_SIZE 98304) # 96 KB + +# Environment variables: + +# Check if WAMR_ROOT_DIR is set +if(DEFINED ENV{WAMR_ROOT_DIR}) + set(WAMR_ROOT_DIR $ENV{WAMR_ROOT_DIR}) +else() + message(FATAL_ERROR "'WAMR_ROOT_DIR' need to be specified") +endif() +message("wasi-sdk was found at ${WAMR_ROOT_DIR}") + +# Check if WASI_SDK_PATH is set +if(NOT $ENV{WASI_SDK_PATH} STREQUAL "") + set(WASI_SDK_PATH $ENV{WASI_SDK_PATH}) +else() + find_program(WASM_C_COMPILER clang /opt/wasi-sdk/bin NO_DEFAULT_PATH) + if(NOT WASM_C_COMPILER) + message(FATAL_ERROR "'wasi-sdk' not found, please ensure wasi-sdk is installed.\ + You can download and install it from\ + https://github.com/WebAssembly/wasi-sdk/releases") + else() + set(WASI_SDK_PATH /opt/wasi-sdk) + endif() +endif() +message("wasi-sdk was found at ${WASI_SDK_PATH}") + +# Check if WAMR_APP_FRAMEWORK_DIR is set +if (DEFINED ENV{WAMR_APP_FRAMEWORK_DIR}) + set(WAMR_APP_FRAMEWORK_DIR $ENV{WAMR_APP_FRAMEWORK_DIR}) +else() + message(FATAL_ERROR "'wamr-app-framework' not found, please ensure they are installed.\ + You can download and install them from\ + https://github.com/bytecodealliance/wamr-app-framework") +endif() +message("wamr-app-framework was found at ${WAMR_APP_FRAMEWORK_DIR}") + +# set the WAMR_SDK_DIR with the path specified in the environment variable +set(WAMR_SDK_DIR + ${WAMR_APP_FRAMEWORK_DIR}/wamr-sdk +) + +# set the WAMR_LIBC_BUILTIN_DIR +set(WAMR_LIBC_BUILTIN_DIR + ${WAMR_SDK_DIR}/wamr-sdk/app/libc-builtin-sysroot +) + +# set the WAMR_SDK_PACKAGE_OUT_DIR +set(WAMR_SDK_PACKAGE_OUT_DIR + ${CMAKE_CURRENT_BINARY_DIR}/wamr-sdk/app-sdk/wamr-app-framework +) + +# # Reset linker flags +# set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +# set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +# include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) # in socket-api sample + +# Build the WAMR runtime +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + src/main.c) + +# Link libraries like in samples. +set(WASI_LIBM "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libm.a") +set(WASI_LIBDL "${WASI_SDK_PATH}/share/wasi-sysroot/lib/wasm32-wasi/libdl.a") + +target_link_libraries(app PUBLIC ${WASI_LIBM} ${WASI_LIBDL}) \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-http/README.md b/product-mini/platforms/zephyr/simple-http/README.md new file mode 100644 index 0000000000..040c959a7c --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/README.md @@ -0,0 +1,143 @@ +# Socket sample +this sample demonstrates the use of WASI API to interact with sockets. + +> ❗ **Important:** This sample was ported/adapted from the http_get zephyr sample. The original sample can be found [here]( https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/net/sockets/http_get/src/http_get.c). + +> 🛠️ **Work in progress:** The sample is functional but be aware that just a small part of WASI socket API was tested. +> Actual Zephyr APIs: +> * socket creation = `zsock_socket` +> * socket connection = `zsock_connect` +> * socket emission = `zsock_sendto` +> * socket reception = `zsock_recvfrom` +> * socket destruction = `zsock_close` +> +> With the sockets most API are in fact provided by the runtime instead of WASI because of the lack of socket support in WASI preview1. + +## Setup +1. Connect a network cable to the board ethernet port. +2. Configure the network interface on the host machine + ``` + Internet Protocol Version 4 (TCP/IPv4) Properties: + IP Address: 192.0.2.10 + Subnet Mask: 255.255.255.0 + Default Gateway: 192.0.2.2 + ``` +3. Start a simple HTTP server on the host machine. + ```bash + python3 -m http.server --bind 0.0.0.0 + ``` +4. Disable any firewall that may block the connection. + +## Configuration +To configure the server side IP address and port modify the following lines in the `http_get.c` file. + +1. The `HTTP_HOST` and `HTTP_PORT` macros define the server IP address and port. + ```c + /* HTTP server to connect to */ + #define HTTP_HOST "192.0.2.10" + /* Port to connect to, as string */ + #define HTTP_PORT "8000" + /* HTTP path to request */ + #define HTTP_PATH "/" + + // ... + + #define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n" + ``` + > 📄 **Notes:** These macros are used to build the request string, but they are not used to instantiate the address structure. Because at one point we didn't want to use `inet_pton` to convert the string to an address and it remained like this. + +2. The `addr` structure is used to store the server address. + ```c + addr.sin_port = htons(8000); + addr.sin_addr.s_addr = + htonl(0xC000020A); // hard coded IP address for 192.0.2.10 + ``` + +To configure the authorized IP address(es) modify the following lines in the `main.c` file. WAMR will only allow the IP addresses in the pool to connect to the server. +```c +#define ADDRESS_POOL_SIZE 1 + const char *addr_pool[ADDRESS_POOL_SIZE] = { + "192.0.2.10/24", + }; +``` +## Run Command +* **Zephyr Build** + 1. **Build:** Replace `nucleo_h743zi` with your board name and the `WAMR_BUILD_TARGET` in `CMakeList.txt` with your target architecture. + ```bash + ZEPHYR_BASE=~/zephyrproject/zephyr \ + WAMR_ROOT_DIR=~/wasm-micro-runtime \ + WASI_SDK_PATH=~/wasi-sdk-21.0 \ + WAMR_APP_FRAMEWORK_DIR=~/wamr-app-framework \ + west build . -b nucleo_h563zi -p always + ``` + ⚠️ **Warning:** The flags `ZEPHYR_BASE`, `WAMR_ROOT_DIR`, `WASI_SDK_PATH`, and `WAMR_APP_FRAMEWORK_DIR` need to be set otherwise the build will fail. + + 2. **Flash:** + ```bash + ZEPHYR_BASE=~/zephyrproject/zephyr west flash + ``` + + 3. **Monitor:** Use a serial link to monitor the output. Personally, I use minicom. + ```bash + minicom -D /dev/ttyACM0 + ``` + + 4. **Debug:** Curently investigating. + +* **WebAssembly Module** + + ❗ **Important:** I used wasi-sdk 21 to compile the module. I still haven't tried the module with the new wasi-sdk 22. + + 0. **Compile a static lib:** in the `wasm-apps` folder. + * **Compile the an object:** + ```bash + ~/wasi-sdk-21.0/bin/clang --sysroot=/home/user/wasi-sdk-21.0/share/wasi-sysroot -Iinc/ -c inc/wasi_socket_ext.c -o inc/wasi_socket_ext.o + ``` + * **Create a static lib:** + ```bash + ~/wasi-sdk-21.0/bin/llvm-ar rcs inc/libwasi_socket_ext.a inc/wasi_socket_ext.o + ``` + 1. **Compile:** in the `wasm-apps` folder. + ```bash + ~/wasi-sdk-21.0/bin/clang --sysroot=/home/user/wasi-sdk-21.0/share/wasi-sysroot -Iinc/ -nodefaultlibs -o http_get.wasm http_get.c -lc -Linc/ -lwasi_socket_ext -z stack-size=8192 -Wl,--initial-memory=65536 -Wl,--export=__heap_base -Wl,--export=__data_end -Wl,--allow-undefined + ``` + 2. **generate a C header:** Use `xxd` or other tool, I also put simple python script. At application root `simple-http/`. + ```bash + python3 to_c_header.py + ``` + Be free to modify the script to fit your needs. + +## Output +The output should be similar to the following: +```bash +*** Booting Zephyr OS build v3.6.0-4305-g2ec8f442a505 *** +[00:00:00.061,000] net_config: Initializing network +[00:00:00.067,000] net_config: Waiting interface 1 (0x2000a910) to be up... +[00:00:03.158,000] phy_mii: PHY (0) Link speed 100 Mb, full duplex + +[00:00:03.288,000] net_config: Interface 1 (0x2000a910) coming up +[00:00:03.295,000] net_config: IPv4 address: 192.0.2.1 +global heap size: 131072 +Wasm file size: 36351 +main found +[wasm-mod] Preparing HTTP GET request for http://192.0.2.10:8000/ +[wasm-mod] sock = 3 +[wasm-mod] connect rc = 0 +[wasm-mod] send rc = 36 +[wasm-mod] Response: + +HTTP/1.0 200 OK +Server: SimpleHTTP/0.6 Python/3.10.10 +Date: Fri, 14 Jun 2024 07:26:56 GMT +Content-type: text/html; charset=utf-8 +Content-Length: 2821 + +# Skip the HTML content + +[wasm-mod] len = 0 break + +[wasm-mod] Connection closed +main executed +wasi exit code: 0 +elapsed: 405ms +``` \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-http/prj.conf b/product-mini/platforms/zephyr/simple-http/prj.conf new file mode 100644 index 0000000000..c6f6ff48d3 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/prj.conf @@ -0,0 +1,61 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Log config +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_MODE_IMMEDIATE=y +CONFIG_NET_LOG=y + +CONFIG_MAIN_STACK_SIZE=8192 +# CONFIG_HEAP_MEM_POOL_SIZE=32768 +CONFIG_REQUIRES_FULL_LIBC=y + +# Networking config +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_POSIX_API=n + +# Stack conf +# CONFIG_NO_OPTIMIZATIONS=y +CONFIG_STACK_SENTINEL=y +CONFIG_HW_STACK_PROTECTION=y +# CONFIG_STACK_CANARIES=y +# CONFIG_ISR_STACK_SIZE=4096 + +# Network driver config +CONFIG_TEST_RANDOM_GENERATOR=y + +# Network address config +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2" +CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2" + +# Config File System +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_LITTLEFS=y +# Flash +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# CONFIG_DNS_RESOLVER=y +# CONFIG_DNS_SERVER_IP_ADDRESSES=y +# CONFIG_DNS_SERVER1="192.0.2.2" + +# Config init stack +# CONFIG_INIT_STACKS=y +# CONFIG_NET_PKT_RX_COUNT=100 +# CONFIG_NET_PKT_TX_COUNT=100 +# CONFIG_NET_BUF_RX_COUNT=100 +# CONFIG_NET_BUF_TX_COUNT=100 + +# Flash +CONFIG_FLASH=y + +# Debug +CONFIG_DEBUG=y \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-http/src/http_get.h b/product-mini/platforms/zephyr/simple-http/src/http_get.h new file mode 100644 index 0000000000..0b7dcc8704 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/src/http_get.h @@ -0,0 +1,3039 @@ +/* + * Copyright (c) 2017 Linaro Limited + * Copyright (C) 2024 Grenoble INP - ESISAR Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +unsigned char __aligned(4) wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x68, 0x10, 0x60, + 0x03, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7e, 0x7f, 0x01, + 0x7e, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x01, 0x7f, + 0x60, 0x04, 0x7f, 0x7e, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x04, 0x7f, 0x7f, + 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x06, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, + 0x7f, 0x60, 0x02, 0x7c, 0x7f, 0x01, 0x7c, 0x60, 0x05, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x05, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x04, 0x7f, 0x7f, 0x7f, 0x7f, + 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x02, 0xb4, 0x03, 0x0c, 0x16, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x61, 0x72, + 0x67, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x02, 0x16, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x0e, 0x61, 0x72, 0x67, 0x73, + 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x00, 0x02, + 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, + 0x66, 0x64, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x00, 0x03, 0x16, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x0d, 0x66, 0x64, + 0x5f, 0x66, 0x64, 0x73, 0x74, 0x61, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x00, + 0x02, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, + 0x07, 0x66, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x6b, 0x00, 0x04, 0x16, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x08, 0x66, 0x64, + 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x00, 0x05, 0x16, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x09, 0x70, 0x72, 0x6f, 0x63, + 0x5f, 0x65, 0x78, 0x69, 0x74, 0x00, 0x06, 0x16, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x09, 0x73, 0x6f, 0x63, 0x6b, 0x5f, + 0x72, 0x65, 0x63, 0x76, 0x00, 0x07, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x31, 0x0c, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x02, 0x16, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x0c, 0x73, 0x6f, 0x63, 0x6b, + 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, 0x74, 0x6f, 0x00, 0x07, 0x16, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x0e, 0x73, 0x6f, + 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x5f, 0x66, 0x72, 0x6f, 0x6d, + 0x00, 0x07, 0x16, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x31, 0x09, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x00, + 0x05, 0x03, 0x4c, 0x4b, 0x08, 0x08, 0x02, 0x03, 0x03, 0x06, 0x06, 0x02, + 0x06, 0x09, 0x08, 0x03, 0x02, 0x02, 0x03, 0x02, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x03, 0x08, 0x08, 0x03, 0x03, 0x02, 0x03, 0x00, 0x00, 0x03, 0x00, + 0x01, 0x01, 0x09, 0x08, 0x03, 0x00, 0x05, 0x02, 0x02, 0x03, 0x00, 0x02, + 0x0a, 0x02, 0x00, 0x0b, 0x0c, 0x0d, 0x08, 0x00, 0x00, 0x03, 0x00, 0x02, + 0x00, 0x0e, 0x05, 0x03, 0x03, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x02, 0x07, + 0x07, 0x07, 0x07, 0x00, 0x05, 0x0f, 0x0f, 0x04, 0x05, 0x01, 0x70, 0x01, + 0x05, 0x05, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x1a, 0x04, 0x7f, 0x01, + 0x41, 0xc0, 0xf4, 0x00, 0x0b, 0x7f, 0x00, 0x41, 0x00, 0x0b, 0x7f, 0x00, + 0x41, 0xc0, 0xf4, 0x00, 0x0b, 0x7f, 0x00, 0x41, 0xb4, 0x34, 0x0b, 0x07, + 0x2e, 0x04, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x06, + 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x0d, 0x0b, 0x5f, 0x5f, 0x68, + 0x65, 0x61, 0x70, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x0a, 0x5f, + 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x03, 0x03, 0x09, + 0x0a, 0x01, 0x00, 0x41, 0x01, 0x0b, 0x04, 0x29, 0x27, 0x2b, 0x2d, 0x0a, + 0x81, 0xf1, 0x01, 0x4b, 0x02, 0x00, 0x0b, 0x52, 0x01, 0x01, 0x7f, 0x02, + 0x40, 0x02, 0x40, 0x23, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0xf0, 0x9f, + 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x0d, 0x00, 0x23, 0x81, 0x80, + 0x80, 0x80, 0x00, 0x41, 0xf0, 0x9f, 0x80, 0x80, 0x00, 0x6a, 0x41, 0x01, + 0x36, 0x02, 0x00, 0x10, 0x8c, 0x80, 0x80, 0x80, 0x00, 0x10, 0x95, 0x80, + 0x80, 0x80, 0x00, 0x21, 0x00, 0x10, 0xa3, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x00, 0x0d, 0x01, 0x0f, 0x0b, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x10, 0x9e, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0xfa, 0x07, 0x03, 0x07, 0x7f, 0x01, + 0x7e, 0x5d, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, + 0x90, 0x01, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, + 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x00, 0x21, 0x05, 0x20, + 0x04, 0x20, 0x05, 0x36, 0x02, 0x8c, 0x01, 0x20, 0x04, 0x20, 0x00, 0x36, + 0x02, 0x88, 0x01, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x84, 0x01, 0x41, + 0x00, 0x21, 0x06, 0x20, 0x04, 0x20, 0x06, 0x36, 0x02, 0x5c, 0x41, 0xca, + 0x8a, 0x80, 0x80, 0x00, 0x21, 0x07, 0x41, 0x00, 0x21, 0x08, 0x20, 0x07, + 0x20, 0x08, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x42, 0x00, 0x21, + 0x09, 0x20, 0x04, 0x20, 0x09, 0x37, 0x03, 0x68, 0x20, 0x04, 0x20, 0x09, + 0x37, 0x03, 0x60, 0x41, 0x01, 0x21, 0x0a, 0x20, 0x04, 0x20, 0x0a, 0x3b, + 0x01, 0x60, 0x41, 0xc0, 0x3e, 0x21, 0x0b, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x0c, 0x20, 0x0b, 0x20, 0x0c, 0x71, 0x21, 0x0d, 0x20, 0x0d, 0x10, 0xa5, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x0e, 0x20, 0x04, 0x20, 0x0e, 0x3b, 0x01, + 0x62, 0x41, 0x8a, 0x84, 0x80, 0x80, 0x7c, 0x21, 0x0f, 0x20, 0x0f, 0x10, + 0xa4, 0x80, 0x80, 0x80, 0x00, 0x21, 0x10, 0x20, 0x04, 0x20, 0x10, 0x36, + 0x02, 0x64, 0x41, 0x01, 0x21, 0x11, 0x41, 0x06, 0x21, 0x12, 0x20, 0x11, + 0x20, 0x12, 0x20, 0x12, 0x10, 0xd3, 0x80, 0x80, 0x80, 0x00, 0x21, 0x13, + 0x20, 0x04, 0x20, 0x13, 0x36, 0x02, 0x7c, 0x20, 0x04, 0x28, 0x02, 0x7c, + 0x21, 0x14, 0x20, 0x04, 0x20, 0x14, 0x36, 0x02, 0x30, 0x41, 0xff, 0x89, + 0x80, 0x80, 0x00, 0x21, 0x15, 0x41, 0x30, 0x21, 0x16, 0x20, 0x04, 0x20, + 0x16, 0x6a, 0x21, 0x17, 0x20, 0x15, 0x20, 0x17, 0x10, 0xa6, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x20, 0x04, 0x28, 0x02, 0x7c, 0x21, 0x18, 0x41, 0xe0, + 0x00, 0x21, 0x19, 0x20, 0x04, 0x20, 0x19, 0x6a, 0x21, 0x1a, 0x20, 0x1a, + 0x21, 0x1b, 0x41, 0x10, 0x21, 0x1c, 0x20, 0x18, 0x20, 0x1b, 0x20, 0x1c, + 0x10, 0xcd, 0x80, 0x80, 0x80, 0x00, 0x21, 0x1d, 0x20, 0x04, 0x20, 0x1d, + 0x36, 0x02, 0x5c, 0x20, 0x04, 0x28, 0x02, 0x5c, 0x21, 0x1e, 0x20, 0x04, + 0x20, 0x1e, 0x36, 0x02, 0x40, 0x41, 0x95, 0x8a, 0x80, 0x80, 0x00, 0x21, + 0x1f, 0x41, 0xc0, 0x00, 0x21, 0x20, 0x20, 0x04, 0x20, 0x20, 0x6a, 0x21, + 0x21, 0x20, 0x1f, 0x20, 0x21, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x20, 0x04, 0x28, 0x02, 0x7c, 0x21, 0x22, 0x41, 0xb3, 0x8c, 0x80, 0x80, + 0x00, 0x21, 0x23, 0x41, 0x24, 0x21, 0x24, 0x41, 0x00, 0x21, 0x25, 0x41, + 0xe0, 0x00, 0x21, 0x26, 0x20, 0x04, 0x20, 0x26, 0x6a, 0x21, 0x27, 0x20, + 0x27, 0x21, 0x28, 0x41, 0x10, 0x21, 0x29, 0x20, 0x22, 0x20, 0x23, 0x20, + 0x24, 0x20, 0x25, 0x20, 0x28, 0x20, 0x29, 0x10, 0xcf, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x2a, 0x20, 0x04, 0x20, 0x2a, 0x36, 0x02, 0x5c, 0x20, 0x04, + 0x28, 0x02, 0x5c, 0x21, 0x2b, 0x20, 0x04, 0x20, 0x2b, 0x36, 0x02, 0x50, + 0x41, 0xb1, 0x8a, 0x80, 0x80, 0x00, 0x21, 0x2c, 0x41, 0xd0, 0x00, 0x21, + 0x2d, 0x20, 0x04, 0x20, 0x2d, 0x6a, 0x21, 0x2e, 0x20, 0x2c, 0x20, 0x2e, + 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x04, 0x28, 0x02, 0x5c, + 0x21, 0x2f, 0x41, 0x00, 0x21, 0x30, 0x20, 0x2f, 0x21, 0x31, 0x20, 0x30, + 0x21, 0x32, 0x20, 0x31, 0x20, 0x32, 0x48, 0x21, 0x33, 0x41, 0x01, 0x21, + 0x34, 0x20, 0x33, 0x20, 0x34, 0x71, 0x21, 0x35, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x35, 0x45, 0x0d, 0x00, 0x41, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x21, + 0x36, 0x20, 0x36, 0x28, 0x02, 0x00, 0x21, 0x37, 0x20, 0x04, 0x20, 0x37, + 0x36, 0x02, 0x00, 0x41, 0xea, 0x89, 0x80, 0x80, 0x00, 0x21, 0x38, 0x20, + 0x38, 0x20, 0x04, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, + 0x21, 0x39, 0x20, 0x04, 0x20, 0x39, 0x36, 0x02, 0x8c, 0x01, 0x0c, 0x01, + 0x0b, 0x41, 0xd8, 0x8c, 0x80, 0x80, 0x00, 0x21, 0x3a, 0x41, 0x00, 0x21, + 0x3b, 0x20, 0x3a, 0x20, 0x3b, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x03, 0x40, 0x41, 0x10, 0x21, 0x3c, 0x20, 0x04, 0x20, 0x3c, 0x36, 0x02, + 0x58, 0x20, 0x04, 0x28, 0x02, 0x7c, 0x21, 0x3d, 0x41, 0x80, 0xa0, 0x80, + 0x80, 0x00, 0x21, 0x3e, 0x41, 0xff, 0x07, 0x21, 0x3f, 0x41, 0x00, 0x21, + 0x40, 0x41, 0xe0, 0x00, 0x21, 0x41, 0x20, 0x04, 0x20, 0x41, 0x6a, 0x21, + 0x42, 0x20, 0x42, 0x21, 0x43, 0x41, 0xd8, 0x00, 0x21, 0x44, 0x20, 0x04, + 0x20, 0x44, 0x6a, 0x21, 0x45, 0x20, 0x45, 0x21, 0x46, 0x20, 0x3d, 0x20, + 0x3e, 0x20, 0x3f, 0x20, 0x40, 0x20, 0x43, 0x20, 0x46, 0x10, 0xd1, 0x80, + 0x80, 0x80, 0x00, 0x21, 0x47, 0x20, 0x04, 0x20, 0x47, 0x36, 0x02, 0x54, + 0x20, 0x04, 0x28, 0x02, 0x54, 0x21, 0x48, 0x41, 0x00, 0x21, 0x49, 0x20, + 0x48, 0x21, 0x4a, 0x20, 0x49, 0x21, 0x4b, 0x20, 0x4a, 0x20, 0x4b, 0x48, + 0x21, 0x4c, 0x41, 0x01, 0x21, 0x4d, 0x20, 0x4c, 0x20, 0x4d, 0x71, 0x21, + 0x4e, 0x02, 0x40, 0x20, 0x4e, 0x45, 0x0d, 0x00, 0x41, 0x80, 0xa8, 0x80, + 0x80, 0x00, 0x21, 0x4f, 0x20, 0x4f, 0x28, 0x02, 0x00, 0x21, 0x50, 0x20, + 0x04, 0x20, 0x50, 0x36, 0x02, 0x10, 0x41, 0xea, 0x89, 0x80, 0x80, 0x00, + 0x21, 0x51, 0x41, 0x10, 0x21, 0x52, 0x20, 0x04, 0x20, 0x52, 0x6a, 0x21, + 0x53, 0x20, 0x51, 0x20, 0x53, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x41, 0x00, 0x21, 0x54, 0x20, 0x04, 0x20, 0x54, 0x36, 0x02, 0x8c, 0x01, + 0x0c, 0x02, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x54, 0x21, 0x55, 0x41, 0x00, + 0x21, 0x56, 0x20, 0x55, 0x20, 0x56, 0x3a, 0x00, 0x80, 0xa0, 0x80, 0x80, + 0x00, 0x41, 0x80, 0xa0, 0x80, 0x80, 0x00, 0x21, 0x57, 0x20, 0x04, 0x20, + 0x57, 0x36, 0x02, 0x20, 0x41, 0x9d, 0x88, 0x80, 0x80, 0x00, 0x21, 0x58, + 0x41, 0x20, 0x21, 0x59, 0x20, 0x04, 0x20, 0x59, 0x6a, 0x21, 0x5a, 0x20, + 0x58, 0x20, 0x5a, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x04, + 0x28, 0x02, 0x54, 0x21, 0x5b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x5b, 0x0d, + 0x00, 0x41, 0xb2, 0x89, 0x80, 0x80, 0x00, 0x21, 0x5c, 0x41, 0x00, 0x21, + 0x5d, 0x20, 0x5c, 0x20, 0x5d, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0c, 0x01, 0x0b, 0x0c, 0x01, 0x0b, 0x0b, 0x41, 0xed, 0x8c, 0x80, 0x80, + 0x00, 0x21, 0x5e, 0x41, 0x00, 0x21, 0x5f, 0x20, 0x5e, 0x20, 0x5f, 0x10, + 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x04, 0x28, 0x02, 0x7c, 0x21, + 0x60, 0x20, 0x60, 0x10, 0x97, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0xcc, + 0x89, 0x80, 0x80, 0x00, 0x21, 0x61, 0x41, 0x00, 0x21, 0x62, 0x20, 0x61, + 0x20, 0x62, 0x10, 0xa6, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, + 0x63, 0x20, 0x04, 0x20, 0x63, 0x36, 0x02, 0x8c, 0x01, 0x0b, 0x20, 0x04, + 0x28, 0x02, 0x8c, 0x01, 0x21, 0x64, 0x41, 0x90, 0x01, 0x21, 0x65, 0x20, + 0x04, 0x20, 0x65, 0x6a, 0x21, 0x66, 0x20, 0x66, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x64, 0x0f, 0x0b, 0x0a, 0x00, 0x20, 0x00, 0x10, 0x90, + 0x80, 0x80, 0x80, 0x00, 0x0b, 0xab, 0x32, 0x01, 0x0b, 0x7f, 0x23, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x01, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x41, 0x00, 0x28, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x02, + 0x0d, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xdc, 0xab, 0x80, 0x80, + 0x00, 0x22, 0x03, 0x0d, 0x00, 0x41, 0x00, 0x42, 0x7f, 0x37, 0x02, 0xe8, + 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x80, 0x80, 0x84, 0x80, 0x80, + 0x80, 0xc0, 0x00, 0x37, 0x02, 0xe0, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x20, 0x01, 0x41, 0x08, 0x6a, 0x41, 0x70, 0x71, 0x41, 0xd8, 0xaa, 0xd5, + 0xaa, 0x05, 0x73, 0x22, 0x03, 0x36, 0x02, 0xdc, 0xab, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xf0, 0xab, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x41, 0x00, 0x36, 0x02, 0xc0, 0xab, 0x80, 0x80, 0x00, 0x0b, 0x41, + 0x80, 0x80, 0x84, 0x80, 0x00, 0x41, 0xc0, 0xf4, 0x80, 0x80, 0x00, 0x49, + 0x0d, 0x01, 0x41, 0x00, 0x21, 0x02, 0x41, 0x80, 0x80, 0x84, 0x80, 0x00, + 0x41, 0xc0, 0xf4, 0x80, 0x80, 0x00, 0x6b, 0x41, 0xd9, 0x00, 0x49, 0x0d, + 0x00, 0x41, 0x00, 0x21, 0x04, 0x41, 0x00, 0x41, 0xc0, 0xf4, 0x80, 0x80, + 0x00, 0x36, 0x02, 0xc4, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0xc0, + 0xf4, 0x80, 0x80, 0x00, 0x36, 0x02, 0x94, 0xa8, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x20, 0x03, 0x36, 0x02, 0xa8, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x41, 0x7f, 0x36, 0x02, 0xa4, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, + 0x80, 0x80, 0x84, 0x80, 0x00, 0x41, 0xc0, 0xf4, 0x80, 0x80, 0x00, 0x6b, + 0x36, 0x02, 0xc8, 0xab, 0x80, 0x80, 0x00, 0x03, 0x40, 0x20, 0x04, 0x41, + 0xc0, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, 0xb4, 0xa8, 0x80, + 0x80, 0x00, 0x6a, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x04, + 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x05, 0x36, 0x02, 0x00, + 0x20, 0x04, 0x41, 0xb8, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x05, 0x36, + 0x02, 0x00, 0x20, 0x04, 0x41, 0xc8, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, + 0x04, 0x41, 0xbc, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x05, 0x36, 0x02, + 0x00, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0xd0, + 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, 0xc4, 0xa8, 0x80, 0x80, + 0x00, 0x6a, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x05, 0x36, + 0x02, 0x00, 0x20, 0x04, 0x41, 0xcc, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, + 0x03, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x20, 0x6a, 0x22, 0x04, 0x41, + 0x80, 0x02, 0x47, 0x0d, 0x00, 0x0b, 0x41, 0xc0, 0xf4, 0x80, 0x80, 0x00, + 0x41, 0x78, 0x41, 0xc0, 0xf4, 0x80, 0x80, 0x00, 0x6b, 0x41, 0x0f, 0x71, + 0x22, 0x04, 0x6a, 0x22, 0x02, 0x41, 0x04, 0x6a, 0x41, 0x80, 0x80, 0x84, + 0x80, 0x00, 0x41, 0xc0, 0xf4, 0x80, 0x80, 0x00, 0x6b, 0x41, 0x48, 0x6a, + 0x22, 0x03, 0x20, 0x04, 0x6b, 0x22, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xec, 0xab, 0x80, 0x80, 0x00, + 0x36, 0x02, 0xa0, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x04, 0x36, + 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x02, 0x36, 0x02, + 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x03, 0x41, 0xc0, 0xf4, 0x80, 0x80, + 0x00, 0x6a, 0x41, 0x04, 0x6a, 0x41, 0x38, 0x36, 0x02, 0x00, 0x0b, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0xec, 0x01, 0x4b, 0x0d, 0x00, 0x02, + 0x40, 0x41, 0x00, 0x28, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x06, + 0x41, 0x10, 0x20, 0x00, 0x41, 0x13, 0x6a, 0x41, 0x70, 0x71, 0x20, 0x00, + 0x41, 0x0b, 0x49, 0x1b, 0x22, 0x07, 0x41, 0x03, 0x76, 0x22, 0x03, 0x76, + 0x22, 0x04, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x04, 0x41, 0x01, 0x71, 0x20, 0x03, 0x72, 0x41, 0x01, 0x73, 0x22, + 0x05, 0x41, 0x03, 0x74, 0x22, 0x03, 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, + 0x6a, 0x22, 0x04, 0x20, 0x03, 0x41, 0xb4, 0xa8, 0x80, 0x80, 0x00, 0x6a, + 0x28, 0x02, 0x00, 0x22, 0x03, 0x28, 0x02, 0x08, 0x22, 0x07, 0x47, 0x0d, + 0x00, 0x41, 0x00, 0x20, 0x06, 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, 0x36, + 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x20, + 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, 0x0c, 0x0b, + 0x20, 0x03, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x20, 0x03, 0x20, 0x05, 0x41, + 0x03, 0x74, 0x22, 0x05, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, + 0x20, 0x05, 0x6a, 0x22, 0x03, 0x20, 0x03, 0x28, 0x02, 0x04, 0x41, 0x01, + 0x72, 0x36, 0x02, 0x04, 0x0c, 0x12, 0x0b, 0x20, 0x07, 0x41, 0x00, 0x28, + 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x08, 0x4d, 0x0d, 0x01, 0x02, + 0x40, 0x20, 0x04, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, + 0x20, 0x03, 0x74, 0x41, 0x02, 0x20, 0x03, 0x74, 0x22, 0x04, 0x41, 0x00, + 0x20, 0x04, 0x6b, 0x72, 0x71, 0x68, 0x22, 0x03, 0x41, 0x03, 0x74, 0x22, + 0x04, 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x05, 0x20, 0x04, + 0x41, 0xb4, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x04, + 0x28, 0x02, 0x08, 0x22, 0x00, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x06, + 0x41, 0x7e, 0x20, 0x03, 0x77, 0x71, 0x22, 0x06, 0x36, 0x02, 0x84, 0xa8, + 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, + 0x08, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x0c, 0x0b, 0x20, 0x04, 0x20, + 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x04, 0x20, 0x03, 0x41, + 0x03, 0x74, 0x22, 0x03, 0x6a, 0x20, 0x03, 0x20, 0x07, 0x6b, 0x22, 0x05, + 0x36, 0x02, 0x00, 0x20, 0x04, 0x20, 0x07, 0x6a, 0x22, 0x00, 0x20, 0x05, + 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, 0x08, 0x45, 0x0d, + 0x00, 0x20, 0x08, 0x41, 0x78, 0x71, 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, + 0x6a, 0x21, 0x07, 0x41, 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, + 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x20, 0x06, 0x41, 0x01, 0x20, 0x08, + 0x41, 0x03, 0x76, 0x74, 0x22, 0x09, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, + 0x06, 0x20, 0x09, 0x72, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x07, 0x21, 0x09, 0x0c, 0x01, 0x0b, 0x20, 0x07, 0x28, 0x02, 0x08, 0x21, + 0x09, 0x0b, 0x20, 0x09, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, + 0x03, 0x36, 0x02, 0x08, 0x20, 0x03, 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, + 0x03, 0x20, 0x09, 0x36, 0x02, 0x08, 0x0b, 0x20, 0x04, 0x41, 0x08, 0x6a, + 0x21, 0x04, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0x98, 0xa8, 0x80, 0x80, + 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, + 0x0c, 0x12, 0x0b, 0x41, 0x00, 0x28, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, + 0x22, 0x0a, 0x45, 0x0d, 0x01, 0x20, 0x0a, 0x68, 0x41, 0x02, 0x74, 0x41, + 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x00, 0x28, + 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x07, 0x6b, 0x21, 0x03, 0x20, 0x00, + 0x21, 0x05, 0x02, 0x40, 0x03, 0x40, 0x02, 0x40, 0x20, 0x05, 0x28, 0x02, + 0x10, 0x22, 0x04, 0x0d, 0x00, 0x20, 0x05, 0x41, 0x14, 0x6a, 0x28, 0x02, + 0x00, 0x22, 0x04, 0x45, 0x0d, 0x02, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x04, + 0x41, 0x78, 0x71, 0x20, 0x07, 0x6b, 0x22, 0x05, 0x20, 0x03, 0x20, 0x05, + 0x20, 0x03, 0x49, 0x22, 0x05, 0x1b, 0x21, 0x03, 0x20, 0x04, 0x20, 0x00, + 0x20, 0x05, 0x1b, 0x21, 0x00, 0x20, 0x04, 0x21, 0x05, 0x0c, 0x00, 0x0b, + 0x0b, 0x20, 0x00, 0x28, 0x02, 0x18, 0x21, 0x0b, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x0c, 0x22, 0x09, 0x20, 0x00, 0x46, 0x0d, 0x00, 0x20, 0x00, + 0x28, 0x02, 0x08, 0x22, 0x04, 0x41, 0x00, 0x28, 0x02, 0x94, 0xa8, 0x80, + 0x80, 0x00, 0x49, 0x1a, 0x20, 0x09, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, + 0x04, 0x20, 0x09, 0x36, 0x02, 0x0c, 0x0c, 0x11, 0x0b, 0x02, 0x40, 0x20, + 0x00, 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, + 0x00, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0d, 0x04, 0x20, + 0x00, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x0b, 0x03, 0x40, 0x20, 0x05, 0x21, + 0x02, 0x20, 0x04, 0x22, 0x09, 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, + 0x00, 0x22, 0x04, 0x0d, 0x00, 0x20, 0x09, 0x41, 0x10, 0x6a, 0x21, 0x05, + 0x20, 0x09, 0x28, 0x02, 0x10, 0x22, 0x04, 0x0d, 0x00, 0x0b, 0x20, 0x02, + 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x10, 0x0b, 0x41, 0x7f, 0x21, 0x07, + 0x20, 0x00, 0x41, 0xbf, 0x7f, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x13, + 0x6a, 0x22, 0x04, 0x41, 0x70, 0x71, 0x21, 0x07, 0x41, 0x00, 0x28, 0x02, + 0x88, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x0b, 0x45, 0x0d, 0x00, 0x41, 0x00, + 0x21, 0x08, 0x02, 0x40, 0x20, 0x07, 0x41, 0x80, 0x02, 0x49, 0x0d, 0x00, + 0x41, 0x1f, 0x21, 0x08, 0x20, 0x07, 0x41, 0xff, 0xff, 0xff, 0x07, 0x4b, + 0x0d, 0x00, 0x20, 0x07, 0x41, 0x26, 0x20, 0x04, 0x41, 0x08, 0x76, 0x67, + 0x22, 0x04, 0x6b, 0x76, 0x41, 0x01, 0x71, 0x20, 0x04, 0x41, 0x01, 0x74, + 0x6b, 0x41, 0x3e, 0x6a, 0x21, 0x08, 0x0b, 0x41, 0x00, 0x20, 0x07, 0x6b, + 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x08, + 0x41, 0x02, 0x74, 0x41, 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, + 0x00, 0x22, 0x05, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x04, 0x41, 0x00, 0x21, + 0x09, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x20, 0x07, 0x41, 0x00, + 0x41, 0x19, 0x20, 0x08, 0x41, 0x01, 0x76, 0x6b, 0x20, 0x08, 0x41, 0x1f, + 0x46, 0x1b, 0x74, 0x21, 0x00, 0x41, 0x00, 0x21, 0x09, 0x03, 0x40, 0x02, + 0x40, 0x20, 0x05, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x07, 0x6b, + 0x22, 0x06, 0x20, 0x03, 0x4f, 0x0d, 0x00, 0x20, 0x06, 0x21, 0x03, 0x20, + 0x05, 0x21, 0x09, 0x20, 0x06, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x03, 0x20, + 0x05, 0x21, 0x09, 0x20, 0x05, 0x21, 0x04, 0x0c, 0x03, 0x0b, 0x20, 0x04, + 0x20, 0x05, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x06, 0x20, 0x06, + 0x20, 0x05, 0x20, 0x00, 0x41, 0x1d, 0x76, 0x41, 0x04, 0x71, 0x6a, 0x41, + 0x10, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x05, 0x46, 0x1b, 0x20, 0x04, 0x20, + 0x06, 0x1b, 0x21, 0x04, 0x20, 0x00, 0x41, 0x01, 0x74, 0x21, 0x00, 0x20, + 0x05, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x04, 0x20, 0x09, 0x72, + 0x0d, 0x00, 0x41, 0x00, 0x21, 0x09, 0x41, 0x02, 0x20, 0x08, 0x74, 0x22, + 0x04, 0x41, 0x00, 0x20, 0x04, 0x6b, 0x72, 0x20, 0x0b, 0x71, 0x22, 0x04, + 0x45, 0x0d, 0x03, 0x20, 0x04, 0x68, 0x41, 0x02, 0x74, 0x41, 0xb4, 0xaa, + 0x80, 0x80, 0x00, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x04, 0x0b, 0x20, 0x04, + 0x45, 0x0d, 0x01, 0x0b, 0x03, 0x40, 0x20, 0x04, 0x28, 0x02, 0x04, 0x41, + 0x78, 0x71, 0x20, 0x07, 0x6b, 0x22, 0x06, 0x20, 0x03, 0x49, 0x21, 0x00, + 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x10, 0x22, 0x05, 0x0d, 0x00, 0x20, + 0x04, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x05, 0x0b, 0x20, 0x06, + 0x20, 0x03, 0x20, 0x00, 0x1b, 0x21, 0x03, 0x20, 0x04, 0x20, 0x09, 0x20, + 0x00, 0x1b, 0x21, 0x09, 0x20, 0x05, 0x21, 0x04, 0x20, 0x05, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x09, 0x45, 0x0d, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, + 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x07, 0x6b, 0x4f, 0x0d, 0x00, + 0x20, 0x09, 0x28, 0x02, 0x18, 0x21, 0x02, 0x02, 0x40, 0x20, 0x09, 0x28, + 0x02, 0x0c, 0x22, 0x00, 0x20, 0x09, 0x46, 0x0d, 0x00, 0x20, 0x09, 0x28, + 0x02, 0x08, 0x22, 0x04, 0x41, 0x00, 0x28, 0x02, 0x94, 0xa8, 0x80, 0x80, + 0x00, 0x49, 0x1a, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, 0x04, + 0x20, 0x00, 0x36, 0x02, 0x0c, 0x0c, 0x0f, 0x0b, 0x02, 0x40, 0x20, 0x09, + 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x04, 0x0d, 0x00, + 0x20, 0x09, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0d, 0x04, 0x20, 0x09, + 0x41, 0x10, 0x6a, 0x21, 0x05, 0x0b, 0x03, 0x40, 0x20, 0x05, 0x21, 0x06, + 0x20, 0x04, 0x22, 0x00, 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, 0x00, + 0x22, 0x04, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x20, + 0x00, 0x28, 0x02, 0x10, 0x22, 0x04, 0x0d, 0x00, 0x0b, 0x20, 0x06, 0x41, + 0x00, 0x36, 0x02, 0x00, 0x0c, 0x0e, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x04, 0x20, 0x07, 0x49, 0x0d, + 0x00, 0x41, 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, 0x21, 0x03, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x20, 0x07, 0x6b, 0x22, 0x05, 0x41, + 0x10, 0x49, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x07, 0x6a, 0x22, 0x00, 0x20, + 0x05, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6a, + 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x07, 0x41, 0x03, 0x72, + 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x20, 0x04, 0x41, 0x03, + 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6a, 0x22, 0x04, 0x20, + 0x04, 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, + 0x21, 0x00, 0x41, 0x00, 0x21, 0x05, 0x0b, 0x41, 0x00, 0x20, 0x05, 0x36, + 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, + 0x98, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x21, 0x04, + 0x0c, 0x10, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x90, 0xa8, 0x80, + 0x80, 0x00, 0x22, 0x05, 0x20, 0x07, 0x4d, 0x0d, 0x00, 0x20, 0x02, 0x20, + 0x07, 0x6a, 0x22, 0x04, 0x20, 0x05, 0x20, 0x07, 0x6b, 0x22, 0x03, 0x41, + 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0x9c, + 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, 0x36, 0x02, 0x90, 0xa8, + 0x80, 0x80, 0x00, 0x20, 0x02, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, + 0x04, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0c, 0x10, 0x0b, 0x02, + 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xdc, 0xab, 0x80, 0x80, 0x00, + 0x45, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0xe4, 0xab, 0x80, 0x80, 0x00, + 0x21, 0x03, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x42, 0x7f, 0x37, 0x02, 0xe8, + 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x42, 0x80, 0x80, 0x84, 0x80, 0x80, + 0x80, 0xc0, 0x00, 0x37, 0x02, 0xe0, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x20, 0x01, 0x41, 0x0c, 0x6a, 0x41, 0x70, 0x71, 0x41, 0xd8, 0xaa, 0xd5, + 0xaa, 0x05, 0x73, 0x36, 0x02, 0xdc, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x36, 0x02, 0xf0, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, + 0x00, 0x36, 0x02, 0xc0, 0xab, 0x80, 0x80, 0x00, 0x41, 0x80, 0x80, 0x04, + 0x21, 0x03, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x02, 0x40, 0x20, 0x03, 0x20, + 0x07, 0x41, 0xc7, 0x00, 0x6a, 0x22, 0x08, 0x6a, 0x22, 0x00, 0x41, 0x00, + 0x20, 0x03, 0x6b, 0x22, 0x06, 0x71, 0x22, 0x09, 0x20, 0x07, 0x4b, 0x0d, + 0x00, 0x41, 0x00, 0x41, 0x30, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, + 0x0c, 0x10, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xbc, 0xab, 0x80, + 0x80, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0xb4, 0xab, 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, 0x09, 0x6a, 0x22, + 0x0b, 0x20, 0x03, 0x4d, 0x0d, 0x00, 0x20, 0x0b, 0x20, 0x04, 0x4d, 0x0d, + 0x01, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x41, 0x00, 0x41, 0x30, 0x36, 0x02, + 0x80, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x10, 0x0b, 0x41, 0x00, 0x2d, 0x00, + 0xc0, 0xab, 0x80, 0x80, 0x00, 0x41, 0x04, 0x71, 0x0d, 0x05, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x00, 0x41, 0xc4, 0xab, + 0x80, 0x80, 0x00, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, 0x28, + 0x02, 0x00, 0x22, 0x03, 0x20, 0x02, 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x20, + 0x04, 0x28, 0x02, 0x04, 0x6a, 0x20, 0x02, 0x4b, 0x0d, 0x03, 0x0b, 0x20, + 0x04, 0x28, 0x02, 0x08, 0x22, 0x04, 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x00, + 0x10, 0xa1, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x41, 0x7f, 0x46, 0x0d, + 0x06, 0x20, 0x09, 0x21, 0x06, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0xe0, + 0xab, 0x80, 0x80, 0x00, 0x22, 0x04, 0x41, 0x7f, 0x6a, 0x22, 0x03, 0x20, + 0x00, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x09, 0x20, 0x00, 0x6b, 0x20, 0x03, + 0x20, 0x00, 0x6a, 0x41, 0x00, 0x20, 0x04, 0x6b, 0x71, 0x6a, 0x21, 0x06, + 0x0b, 0x20, 0x06, 0x20, 0x07, 0x4d, 0x0d, 0x06, 0x20, 0x06, 0x41, 0xfe, + 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x06, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0xbc, 0xab, 0x80, 0x80, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x41, + 0x00, 0x28, 0x02, 0xb4, 0xab, 0x80, 0x80, 0x00, 0x22, 0x03, 0x20, 0x06, + 0x6a, 0x22, 0x05, 0x20, 0x03, 0x4d, 0x0d, 0x07, 0x20, 0x05, 0x20, 0x04, + 0x4b, 0x0d, 0x07, 0x0b, 0x20, 0x06, 0x10, 0xa1, 0x80, 0x80, 0x80, 0x00, + 0x22, 0x04, 0x20, 0x00, 0x47, 0x0d, 0x01, 0x0c, 0x08, 0x0b, 0x20, 0x00, + 0x20, 0x05, 0x6b, 0x20, 0x06, 0x71, 0x22, 0x06, 0x41, 0xfe, 0xff, 0xff, + 0xff, 0x07, 0x4b, 0x0d, 0x05, 0x20, 0x06, 0x10, 0xa1, 0x80, 0x80, 0x80, + 0x00, 0x22, 0x00, 0x20, 0x04, 0x28, 0x02, 0x00, 0x20, 0x04, 0x28, 0x02, + 0x04, 0x6a, 0x46, 0x0d, 0x04, 0x20, 0x00, 0x21, 0x04, 0x0b, 0x02, 0x40, + 0x20, 0x06, 0x20, 0x07, 0x41, 0xc8, 0x00, 0x6a, 0x4f, 0x0d, 0x00, 0x20, + 0x04, 0x41, 0x7f, 0x46, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x08, 0x20, 0x06, + 0x6b, 0x41, 0x00, 0x28, 0x02, 0xe4, 0xab, 0x80, 0x80, 0x00, 0x22, 0x03, + 0x6a, 0x41, 0x00, 0x20, 0x03, 0x6b, 0x71, 0x22, 0x03, 0x41, 0xfe, 0xff, + 0xff, 0xff, 0x07, 0x4d, 0x0d, 0x00, 0x20, 0x04, 0x21, 0x00, 0x0c, 0x08, + 0x0b, 0x02, 0x40, 0x20, 0x03, 0x10, 0xa1, 0x80, 0x80, 0x80, 0x00, 0x41, + 0x7f, 0x46, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x06, 0x6a, 0x21, 0x06, 0x20, + 0x04, 0x21, 0x00, 0x0c, 0x08, 0x0b, 0x41, 0x00, 0x20, 0x06, 0x6b, 0x10, + 0xa1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0c, 0x05, 0x0b, 0x20, 0x04, 0x21, + 0x00, 0x20, 0x04, 0x41, 0x7f, 0x47, 0x0d, 0x06, 0x0c, 0x04, 0x0b, 0x00, + 0x00, 0x0b, 0x41, 0x00, 0x21, 0x09, 0x0c, 0x0c, 0x0b, 0x41, 0x00, 0x21, + 0x00, 0x0c, 0x0a, 0x0b, 0x20, 0x00, 0x41, 0x7f, 0x47, 0x0d, 0x02, 0x0b, + 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xc0, 0xab, 0x80, 0x80, 0x00, 0x41, + 0x04, 0x72, 0x36, 0x02, 0xc0, 0xab, 0x80, 0x80, 0x00, 0x0b, 0x20, 0x09, + 0x41, 0xfe, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x01, 0x20, 0x09, 0x10, + 0xa1, 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x41, 0x00, 0x10, 0xa1, 0x80, + 0x80, 0x80, 0x00, 0x21, 0x04, 0x20, 0x00, 0x41, 0x7f, 0x46, 0x0d, 0x01, + 0x20, 0x04, 0x41, 0x7f, 0x46, 0x0d, 0x01, 0x20, 0x00, 0x20, 0x04, 0x4f, + 0x0d, 0x01, 0x20, 0x04, 0x20, 0x00, 0x6b, 0x22, 0x06, 0x20, 0x07, 0x41, + 0x38, 0x6a, 0x4d, 0x0d, 0x01, 0x0b, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, + 0xb4, 0xab, 0x80, 0x80, 0x00, 0x20, 0x06, 0x6a, 0x22, 0x04, 0x36, 0x02, + 0xb4, 0xab, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x41, 0x00, 0x28, + 0x02, 0xb8, 0xab, 0x80, 0x80, 0x00, 0x4d, 0x0d, 0x00, 0x41, 0x00, 0x20, + 0x04, 0x36, 0x02, 0xb8, 0xab, 0x80, 0x80, 0x00, 0x0b, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x9c, 0xa8, 0x80, + 0x80, 0x00, 0x22, 0x03, 0x45, 0x0d, 0x00, 0x41, 0xc4, 0xab, 0x80, 0x80, + 0x00, 0x21, 0x04, 0x03, 0x40, 0x20, 0x00, 0x20, 0x04, 0x28, 0x02, 0x00, + 0x22, 0x05, 0x20, 0x04, 0x28, 0x02, 0x04, 0x22, 0x09, 0x6a, 0x46, 0x0d, + 0x02, 0x20, 0x04, 0x28, 0x02, 0x08, 0x22, 0x04, 0x0d, 0x00, 0x0c, 0x03, + 0x0b, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x94, 0xa8, + 0x80, 0x80, 0x00, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x04, + 0x4f, 0x0d, 0x01, 0x0b, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0x94, 0xa8, + 0x80, 0x80, 0x00, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x41, 0x00, 0x20, 0x06, + 0x36, 0x02, 0xc8, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, + 0x02, 0xc4, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x7f, 0x36, 0x02, + 0xa4, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xdc, + 0xab, 0x80, 0x80, 0x00, 0x36, 0x02, 0xa8, 0xa8, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x41, 0x00, 0x36, 0x02, 0xd0, 0xab, 0x80, 0x80, 0x00, 0x03, 0x40, + 0x20, 0x04, 0x41, 0xc0, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, + 0xb4, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, + 0x03, 0x20, 0x04, 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x05, + 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0xb8, 0xa8, 0x80, 0x80, 0x00, 0x6a, + 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0xc8, 0xa8, 0x80, 0x80, + 0x00, 0x6a, 0x20, 0x04, 0x41, 0xbc, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, + 0x05, 0x36, 0x02, 0x00, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, + 0x04, 0x41, 0xd0, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x04, 0x41, 0xc4, + 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, + 0x20, 0x05, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0xcc, 0xa8, 0x80, 0x80, + 0x00, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x20, 0x6a, + 0x22, 0x04, 0x41, 0x80, 0x02, 0x47, 0x0d, 0x00, 0x0b, 0x20, 0x00, 0x41, + 0x78, 0x20, 0x00, 0x6b, 0x41, 0x0f, 0x71, 0x22, 0x04, 0x6a, 0x22, 0x03, + 0x20, 0x06, 0x41, 0x48, 0x6a, 0x22, 0x05, 0x20, 0x04, 0x6b, 0x22, 0x04, + 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, + 0xec, 0xab, 0x80, 0x80, 0x00, 0x36, 0x02, 0xa0, 0xa8, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x20, 0x04, 0x36, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x20, 0x03, 0x36, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x00, + 0x20, 0x05, 0x6a, 0x41, 0x38, 0x36, 0x02, 0x04, 0x0c, 0x02, 0x0b, 0x20, + 0x03, 0x20, 0x00, 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x05, 0x49, 0x0d, + 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x41, 0x08, 0x71, 0x0d, 0x00, 0x20, + 0x03, 0x41, 0x78, 0x20, 0x03, 0x6b, 0x41, 0x0f, 0x71, 0x22, 0x05, 0x6a, + 0x22, 0x00, 0x41, 0x00, 0x28, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x06, 0x6a, 0x22, 0x02, 0x20, 0x05, 0x6b, 0x22, 0x05, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x20, 0x04, 0x20, 0x09, 0x20, 0x06, 0x6a, 0x36, 0x02, + 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xec, 0xab, 0x80, 0x80, 0x00, + 0x36, 0x02, 0xa0, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x05, 0x36, + 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, + 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, 0x6a, 0x41, 0x38, + 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x41, 0x00, + 0x28, 0x02, 0x94, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x09, 0x4f, 0x0d, 0x00, + 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0x94, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x00, 0x21, 0x09, 0x0b, 0x20, 0x00, 0x20, 0x06, 0x6a, 0x21, 0x05, 0x41, + 0xc4, 0xab, 0x80, 0x80, 0x00, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x03, 0x40, 0x20, 0x04, 0x28, 0x02, 0x00, 0x20, 0x05, + 0x46, 0x0d, 0x01, 0x20, 0x04, 0x28, 0x02, 0x08, 0x22, 0x04, 0x0d, 0x00, + 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x04, 0x2d, 0x00, 0x0c, 0x41, 0x08, 0x71, + 0x45, 0x0d, 0x01, 0x0b, 0x41, 0xc4, 0xab, 0x80, 0x80, 0x00, 0x21, 0x04, + 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x00, 0x22, 0x05, 0x20, + 0x03, 0x4b, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x04, 0x28, 0x02, 0x04, 0x6a, + 0x22, 0x05, 0x20, 0x03, 0x4b, 0x0d, 0x03, 0x0b, 0x20, 0x04, 0x28, 0x02, + 0x08, 0x21, 0x04, 0x0c, 0x00, 0x0b, 0x0b, 0x20, 0x04, 0x20, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x04, 0x20, 0x04, 0x28, 0x02, 0x04, 0x20, 0x06, 0x6a, + 0x36, 0x02, 0x04, 0x20, 0x00, 0x41, 0x78, 0x20, 0x00, 0x6b, 0x41, 0x0f, + 0x71, 0x6a, 0x22, 0x02, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, + 0x20, 0x05, 0x41, 0x78, 0x20, 0x05, 0x6b, 0x41, 0x0f, 0x71, 0x6a, 0x22, + 0x06, 0x20, 0x02, 0x20, 0x07, 0x6a, 0x22, 0x07, 0x6b, 0x21, 0x04, 0x02, + 0x40, 0x20, 0x06, 0x20, 0x03, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x07, + 0x36, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, + 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x04, 0x6a, 0x22, 0x04, 0x36, + 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x07, 0x20, 0x04, 0x41, 0x01, + 0x72, 0x36, 0x02, 0x04, 0x0c, 0x08, 0x0b, 0x02, 0x40, 0x20, 0x06, 0x41, + 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, 0x47, 0x0d, 0x00, 0x41, + 0x00, 0x20, 0x07, 0x36, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x28, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x04, 0x6a, + 0x22, 0x04, 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x07, 0x20, + 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x07, 0x20, 0x04, 0x6a, + 0x20, 0x04, 0x36, 0x02, 0x00, 0x0c, 0x08, 0x0b, 0x20, 0x06, 0x28, 0x02, + 0x04, 0x22, 0x03, 0x41, 0x03, 0x71, 0x41, 0x01, 0x47, 0x0d, 0x06, 0x20, + 0x03, 0x41, 0x78, 0x71, 0x21, 0x08, 0x02, 0x40, 0x20, 0x03, 0x41, 0xff, + 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x06, 0x28, 0x02, 0x08, 0x22, 0x05, 0x20, + 0x03, 0x41, 0x03, 0x76, 0x22, 0x09, 0x41, 0x03, 0x74, 0x41, 0xac, 0xa8, + 0x80, 0x80, 0x00, 0x6a, 0x22, 0x00, 0x46, 0x1a, 0x02, 0x40, 0x20, 0x06, + 0x28, 0x02, 0x0c, 0x22, 0x03, 0x20, 0x05, 0x47, 0x0d, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x28, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, + 0x09, 0x77, 0x71, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x07, + 0x0b, 0x20, 0x03, 0x20, 0x00, 0x46, 0x1a, 0x20, 0x03, 0x20, 0x05, 0x36, + 0x02, 0x08, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x0c, 0x06, 0x0b, + 0x20, 0x06, 0x28, 0x02, 0x18, 0x21, 0x0b, 0x02, 0x40, 0x20, 0x06, 0x28, + 0x02, 0x0c, 0x22, 0x00, 0x20, 0x06, 0x46, 0x0d, 0x00, 0x20, 0x06, 0x28, + 0x02, 0x08, 0x22, 0x03, 0x20, 0x09, 0x49, 0x1a, 0x20, 0x00, 0x20, 0x03, + 0x36, 0x02, 0x08, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x0c, 0x05, + 0x0b, 0x02, 0x40, 0x20, 0x06, 0x41, 0x14, 0x6a, 0x22, 0x05, 0x28, 0x02, + 0x00, 0x22, 0x03, 0x0d, 0x00, 0x20, 0x06, 0x28, 0x02, 0x10, 0x22, 0x03, + 0x45, 0x0d, 0x04, 0x20, 0x06, 0x41, 0x10, 0x6a, 0x21, 0x05, 0x0b, 0x03, + 0x40, 0x20, 0x05, 0x21, 0x09, 0x20, 0x03, 0x22, 0x00, 0x41, 0x14, 0x6a, + 0x22, 0x05, 0x28, 0x02, 0x00, 0x22, 0x03, 0x0d, 0x00, 0x20, 0x00, 0x41, + 0x10, 0x6a, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x03, 0x0d, + 0x00, 0x0b, 0x20, 0x09, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x04, 0x0b, + 0x20, 0x00, 0x41, 0x78, 0x20, 0x00, 0x6b, 0x41, 0x0f, 0x71, 0x22, 0x04, + 0x6a, 0x22, 0x02, 0x20, 0x06, 0x41, 0x48, 0x6a, 0x22, 0x09, 0x20, 0x04, + 0x6b, 0x22, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, + 0x09, 0x6a, 0x41, 0x38, 0x36, 0x02, 0x04, 0x20, 0x03, 0x20, 0x05, 0x41, + 0x37, 0x20, 0x05, 0x6b, 0x41, 0x0f, 0x71, 0x6a, 0x41, 0x41, 0x6a, 0x22, + 0x09, 0x20, 0x09, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x49, 0x1b, 0x22, 0x09, + 0x41, 0x23, 0x36, 0x02, 0x04, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0xec, + 0xab, 0x80, 0x80, 0x00, 0x36, 0x02, 0xa0, 0xa8, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x20, 0x04, 0x36, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x20, 0x02, 0x36, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x09, 0x41, + 0x10, 0x6a, 0x41, 0x00, 0x29, 0x02, 0xcc, 0xab, 0x80, 0x80, 0x00, 0x37, + 0x02, 0x00, 0x20, 0x09, 0x41, 0x00, 0x29, 0x02, 0xc4, 0xab, 0x80, 0x80, + 0x00, 0x37, 0x02, 0x08, 0x41, 0x00, 0x20, 0x09, 0x41, 0x08, 0x6a, 0x36, + 0x02, 0xcc, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x06, 0x36, 0x02, + 0xc8, 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0xc4, + 0xab, 0x80, 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x36, 0x02, 0xd0, 0xab, + 0x80, 0x80, 0x00, 0x20, 0x09, 0x41, 0x24, 0x6a, 0x21, 0x04, 0x03, 0x40, + 0x20, 0x04, 0x41, 0x07, 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x04, 0x6a, + 0x22, 0x04, 0x20, 0x05, 0x49, 0x0d, 0x00, 0x0b, 0x20, 0x09, 0x20, 0x03, + 0x46, 0x0d, 0x00, 0x20, 0x09, 0x20, 0x09, 0x28, 0x02, 0x04, 0x41, 0x7e, + 0x71, 0x36, 0x02, 0x04, 0x20, 0x09, 0x20, 0x09, 0x20, 0x03, 0x6b, 0x22, + 0x00, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, + 0x02, 0x04, 0x02, 0x40, 0x20, 0x00, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, + 0x20, 0x00, 0x41, 0x78, 0x71, 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, 0x6a, + 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x84, 0xa8, + 0x80, 0x80, 0x00, 0x22, 0x05, 0x41, 0x01, 0x20, 0x00, 0x41, 0x03, 0x76, + 0x74, 0x22, 0x00, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x05, 0x20, 0x00, + 0x72, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x04, 0x21, 0x05, + 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x05, 0x0b, 0x20, + 0x05, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x03, 0x36, 0x02, + 0x08, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x05, + 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x41, 0x1f, 0x21, 0x04, 0x02, 0x40, + 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x00, + 0x41, 0x26, 0x20, 0x00, 0x41, 0x08, 0x76, 0x67, 0x22, 0x04, 0x6b, 0x76, + 0x41, 0x01, 0x71, 0x20, 0x04, 0x41, 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, + 0x21, 0x04, 0x0b, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x1c, 0x20, 0x03, + 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, 0x04, 0x41, 0x02, 0x74, 0x41, 0xb4, + 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x05, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x09, 0x41, 0x01, 0x20, 0x04, + 0x74, 0x22, 0x06, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x41, 0x00, 0x20, 0x09, 0x20, 0x06, 0x72, 0x36, 0x02, 0x88, 0xa8, + 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, 0x03, + 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x03, 0x20, 0x03, 0x36, 0x02, 0x0c, + 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x41, 0x00, 0x41, 0x19, 0x20, 0x04, 0x41, + 0x01, 0x76, 0x6b, 0x20, 0x04, 0x41, 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x04, + 0x20, 0x05, 0x28, 0x02, 0x00, 0x21, 0x09, 0x02, 0x40, 0x03, 0x40, 0x20, + 0x09, 0x22, 0x05, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x00, 0x46, + 0x0d, 0x01, 0x20, 0x04, 0x41, 0x1d, 0x76, 0x21, 0x09, 0x20, 0x04, 0x41, + 0x01, 0x74, 0x21, 0x04, 0x20, 0x05, 0x20, 0x09, 0x41, 0x04, 0x71, 0x6a, + 0x41, 0x10, 0x6a, 0x22, 0x06, 0x28, 0x02, 0x00, 0x22, 0x09, 0x0d, 0x00, + 0x0b, 0x20, 0x06, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x05, + 0x36, 0x02, 0x18, 0x20, 0x03, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x03, + 0x20, 0x03, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, + 0x08, 0x22, 0x04, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x05, 0x20, 0x03, + 0x36, 0x02, 0x08, 0x20, 0x03, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x03, + 0x20, 0x05, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x04, 0x36, 0x02, 0x08, + 0x0b, 0x41, 0x00, 0x28, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x04, + 0x20, 0x07, 0x4d, 0x0d, 0x00, 0x41, 0x00, 0x28, 0x02, 0x9c, 0xa8, 0x80, + 0x80, 0x00, 0x22, 0x03, 0x20, 0x07, 0x6a, 0x22, 0x05, 0x20, 0x04, 0x20, + 0x07, 0x6b, 0x22, 0x04, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x41, 0x00, + 0x20, 0x04, 0x36, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, + 0x05, 0x36, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x07, + 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x21, + 0x04, 0x0c, 0x08, 0x0b, 0x41, 0x00, 0x21, 0x04, 0x41, 0x00, 0x41, 0x30, + 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x07, 0x0b, 0x41, 0x00, + 0x21, 0x00, 0x0b, 0x20, 0x0b, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x20, 0x06, 0x20, 0x06, 0x28, 0x02, 0x1c, 0x22, 0x05, 0x41, 0x02, 0x74, + 0x41, 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x03, 0x28, 0x02, 0x00, + 0x47, 0x0d, 0x00, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x0d, 0x01, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0x88, 0xa8, 0x80, 0x80, + 0x00, 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0x88, 0xa8, 0x80, + 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x0b, 0x41, 0x10, 0x41, 0x14, 0x20, + 0x0b, 0x28, 0x02, 0x10, 0x20, 0x06, 0x46, 0x1b, 0x6a, 0x20, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x00, 0x20, 0x0b, + 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, 0x06, 0x28, 0x02, 0x10, 0x22, 0x03, + 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x10, 0x20, 0x03, + 0x20, 0x00, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x06, 0x41, 0x14, 0x6a, 0x28, + 0x02, 0x00, 0x22, 0x03, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x14, 0x6a, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x18, + 0x0b, 0x20, 0x08, 0x20, 0x04, 0x6a, 0x21, 0x04, 0x20, 0x06, 0x20, 0x08, + 0x6a, 0x22, 0x06, 0x28, 0x02, 0x04, 0x21, 0x03, 0x0b, 0x20, 0x06, 0x20, + 0x03, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x04, 0x20, 0x07, 0x20, 0x04, 0x6a, + 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, 0x07, 0x20, 0x04, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x02, 0x40, 0x20, 0x04, 0x41, 0xff, 0x01, 0x4b, 0x0d, + 0x00, 0x20, 0x04, 0x41, 0x78, 0x71, 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, + 0x6a, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x84, + 0xa8, 0x80, 0x80, 0x00, 0x22, 0x05, 0x41, 0x01, 0x20, 0x04, 0x41, 0x03, + 0x76, 0x74, 0x22, 0x04, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x05, 0x20, + 0x04, 0x72, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x03, 0x21, + 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x08, 0x21, 0x04, 0x0b, + 0x20, 0x04, 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x07, 0x36, + 0x02, 0x08, 0x20, 0x07, 0x20, 0x03, 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, + 0x04, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x41, 0x1f, 0x21, 0x03, 0x02, + 0x40, 0x20, 0x04, 0x41, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, 0x00, 0x20, + 0x04, 0x41, 0x26, 0x20, 0x04, 0x41, 0x08, 0x76, 0x67, 0x22, 0x03, 0x6b, + 0x76, 0x41, 0x01, 0x71, 0x20, 0x03, 0x41, 0x01, 0x74, 0x6b, 0x41, 0x3e, + 0x6a, 0x21, 0x03, 0x0b, 0x20, 0x07, 0x20, 0x03, 0x36, 0x02, 0x1c, 0x20, + 0x07, 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, 0x03, 0x41, 0x02, 0x74, 0x41, + 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x05, 0x02, 0x40, 0x41, 0x00, + 0x28, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x00, 0x41, 0x01, 0x20, + 0x03, 0x74, 0x22, 0x09, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x07, 0x36, + 0x02, 0x00, 0x41, 0x00, 0x20, 0x00, 0x20, 0x09, 0x72, 0x36, 0x02, 0x88, + 0xa8, 0x80, 0x80, 0x00, 0x20, 0x07, 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, + 0x07, 0x20, 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, + 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x41, 0x00, 0x41, 0x19, 0x20, 0x03, + 0x41, 0x01, 0x76, 0x6b, 0x20, 0x03, 0x41, 0x1f, 0x46, 0x1b, 0x74, 0x21, + 0x03, 0x20, 0x05, 0x28, 0x02, 0x00, 0x21, 0x00, 0x02, 0x40, 0x03, 0x40, + 0x20, 0x00, 0x22, 0x05, 0x28, 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x04, + 0x46, 0x0d, 0x01, 0x20, 0x03, 0x41, 0x1d, 0x76, 0x21, 0x00, 0x20, 0x03, + 0x41, 0x01, 0x74, 0x21, 0x03, 0x20, 0x05, 0x20, 0x00, 0x41, 0x04, 0x71, + 0x6a, 0x41, 0x10, 0x6a, 0x22, 0x09, 0x28, 0x02, 0x00, 0x22, 0x00, 0x0d, + 0x00, 0x0b, 0x20, 0x09, 0x20, 0x07, 0x36, 0x02, 0x00, 0x20, 0x07, 0x20, + 0x05, 0x36, 0x02, 0x18, 0x20, 0x07, 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, + 0x07, 0x20, 0x07, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, + 0x02, 0x08, 0x22, 0x04, 0x20, 0x07, 0x36, 0x02, 0x0c, 0x20, 0x05, 0x20, + 0x07, 0x36, 0x02, 0x08, 0x20, 0x07, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, + 0x07, 0x20, 0x05, 0x36, 0x02, 0x0c, 0x20, 0x07, 0x20, 0x04, 0x36, 0x02, + 0x08, 0x0b, 0x20, 0x02, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0c, 0x02, 0x0b, + 0x02, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x09, 0x20, 0x09, 0x28, 0x02, 0x1c, 0x22, 0x05, 0x41, 0x02, 0x74, 0x41, + 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x04, 0x28, 0x02, 0x00, 0x47, + 0x0d, 0x00, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0d, + 0x01, 0x41, 0x00, 0x20, 0x0b, 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, 0x22, + 0x0b, 0x36, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, + 0x02, 0x41, 0x10, 0x41, 0x14, 0x20, 0x02, 0x28, 0x02, 0x10, 0x20, 0x09, + 0x46, 0x1b, 0x6a, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x45, 0x0d, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x02, 0x36, 0x02, 0x18, 0x02, 0x40, 0x20, + 0x09, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, + 0x04, 0x36, 0x02, 0x10, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0b, + 0x20, 0x09, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x04, 0x45, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x20, 0x04, 0x36, 0x02, 0x00, 0x20, + 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x03, 0x41, 0x0f, 0x4b, 0x0d, 0x00, 0x20, 0x09, 0x20, 0x03, 0x20, 0x07, + 0x6a, 0x22, 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x09, 0x20, + 0x04, 0x6a, 0x22, 0x04, 0x20, 0x04, 0x28, 0x02, 0x04, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x09, 0x20, 0x07, 0x6a, 0x22, + 0x00, 0x20, 0x03, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x09, 0x20, + 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x00, 0x20, 0x03, 0x6a, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x03, 0x41, 0xff, 0x01, + 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x41, 0x78, 0x71, 0x41, 0xac, 0xa8, 0x80, + 0x80, 0x00, 0x6a, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x05, 0x41, 0x01, 0x20, 0x03, + 0x41, 0x03, 0x76, 0x74, 0x22, 0x03, 0x71, 0x0d, 0x00, 0x41, 0x00, 0x20, + 0x05, 0x20, 0x03, 0x72, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x04, 0x21, 0x03, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, + 0x03, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, + 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, + 0x00, 0x20, 0x03, 0x36, 0x02, 0x08, 0x0c, 0x01, 0x0b, 0x41, 0x1f, 0x21, + 0x04, 0x02, 0x40, 0x20, 0x03, 0x41, 0xff, 0xff, 0xff, 0x07, 0x4b, 0x0d, + 0x00, 0x20, 0x03, 0x41, 0x26, 0x20, 0x03, 0x41, 0x08, 0x76, 0x67, 0x22, + 0x04, 0x6b, 0x76, 0x41, 0x01, 0x71, 0x20, 0x04, 0x41, 0x01, 0x74, 0x6b, + 0x41, 0x3e, 0x6a, 0x21, 0x04, 0x0b, 0x20, 0x00, 0x20, 0x04, 0x36, 0x02, + 0x1c, 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, 0x04, 0x41, 0x02, + 0x74, 0x41, 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x05, 0x02, 0x40, + 0x20, 0x0b, 0x41, 0x01, 0x20, 0x04, 0x74, 0x22, 0x07, 0x71, 0x0d, 0x00, + 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0x00, 0x20, 0x0b, 0x20, + 0x07, 0x72, 0x36, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x00, 0x20, + 0x05, 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, + 0x00, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x00, 0x41, 0x19, 0x20, 0x04, 0x41, 0x01, 0x76, 0x6b, 0x20, 0x04, 0x41, + 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, 0x21, + 0x07, 0x02, 0x40, 0x03, 0x40, 0x20, 0x07, 0x22, 0x05, 0x28, 0x02, 0x04, + 0x41, 0x78, 0x71, 0x20, 0x03, 0x46, 0x0d, 0x01, 0x20, 0x04, 0x41, 0x1d, + 0x76, 0x21, 0x07, 0x20, 0x04, 0x41, 0x01, 0x74, 0x21, 0x04, 0x20, 0x05, + 0x20, 0x07, 0x41, 0x04, 0x71, 0x6a, 0x41, 0x10, 0x6a, 0x22, 0x06, 0x28, + 0x02, 0x00, 0x22, 0x07, 0x0d, 0x00, 0x0b, 0x20, 0x06, 0x20, 0x00, 0x36, + 0x02, 0x00, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, + 0x00, 0x36, 0x02, 0x0c, 0x20, 0x00, 0x20, 0x00, 0x36, 0x02, 0x08, 0x0c, + 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x00, 0x36, + 0x02, 0x0c, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x00, 0x41, + 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x0c, 0x20, + 0x00, 0x20, 0x04, 0x36, 0x02, 0x08, 0x0b, 0x20, 0x09, 0x41, 0x08, 0x6a, + 0x21, 0x04, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x0b, 0x45, 0x0d, 0x00, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x1c, 0x22, + 0x05, 0x41, 0x02, 0x74, 0x41, 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x22, + 0x04, 0x28, 0x02, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x09, 0x36, + 0x02, 0x00, 0x20, 0x09, 0x0d, 0x01, 0x41, 0x00, 0x20, 0x0a, 0x41, 0x7e, + 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x0c, + 0x02, 0x0b, 0x20, 0x0b, 0x41, 0x10, 0x41, 0x14, 0x20, 0x0b, 0x28, 0x02, + 0x10, 0x20, 0x00, 0x46, 0x1b, 0x6a, 0x20, 0x09, 0x36, 0x02, 0x00, 0x20, + 0x09, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x09, 0x20, 0x0b, 0x36, 0x02, 0x18, + 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x10, 0x22, 0x04, 0x45, 0x0d, 0x00, + 0x20, 0x09, 0x20, 0x04, 0x36, 0x02, 0x10, 0x20, 0x04, 0x20, 0x09, 0x36, + 0x02, 0x18, 0x0b, 0x20, 0x00, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, + 0x04, 0x45, 0x0d, 0x00, 0x20, 0x09, 0x41, 0x14, 0x6a, 0x20, 0x04, 0x36, + 0x02, 0x00, 0x20, 0x04, 0x20, 0x09, 0x36, 0x02, 0x18, 0x0b, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x03, 0x41, 0x0f, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x20, + 0x03, 0x20, 0x07, 0x6a, 0x22, 0x04, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, + 0x20, 0x00, 0x20, 0x04, 0x6a, 0x22, 0x04, 0x20, 0x04, 0x28, 0x02, 0x04, + 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x00, 0x20, + 0x07, 0x6a, 0x22, 0x05, 0x20, 0x03, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, + 0x20, 0x00, 0x20, 0x07, 0x41, 0x03, 0x72, 0x36, 0x02, 0x04, 0x20, 0x05, + 0x20, 0x03, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x02, 0x40, 0x20, 0x08, + 0x45, 0x0d, 0x00, 0x20, 0x08, 0x41, 0x78, 0x71, 0x41, 0xac, 0xa8, 0x80, + 0x80, 0x00, 0x6a, 0x21, 0x07, 0x41, 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, + 0x80, 0x00, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x41, 0x01, 0x20, 0x08, + 0x41, 0x03, 0x76, 0x74, 0x22, 0x09, 0x20, 0x06, 0x71, 0x0d, 0x00, 0x41, + 0x00, 0x20, 0x09, 0x20, 0x06, 0x72, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, + 0x00, 0x20, 0x07, 0x21, 0x09, 0x0c, 0x01, 0x0b, 0x20, 0x07, 0x28, 0x02, + 0x08, 0x21, 0x09, 0x0b, 0x20, 0x09, 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, + 0x07, 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x07, 0x36, 0x02, + 0x0c, 0x20, 0x04, 0x20, 0x09, 0x36, 0x02, 0x08, 0x0b, 0x41, 0x00, 0x20, + 0x05, 0x36, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, 0x20, 0x03, + 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x0b, 0x20, 0x00, 0x41, 0x08, + 0x6a, 0x21, 0x04, 0x0b, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x04, 0x0b, 0x0a, 0x00, 0x20, 0x00, 0x10, 0x92, + 0x80, 0x80, 0x80, 0x00, 0x0b, 0xb0, 0x0d, 0x01, 0x07, 0x7f, 0x02, 0x40, + 0x20, 0x00, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x78, 0x6a, 0x22, 0x01, + 0x20, 0x00, 0x41, 0x7c, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x02, 0x41, 0x78, + 0x71, 0x22, 0x00, 0x6a, 0x21, 0x03, 0x02, 0x40, 0x20, 0x02, 0x41, 0x01, + 0x71, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, + 0x01, 0x20, 0x01, 0x28, 0x02, 0x00, 0x22, 0x02, 0x6b, 0x22, 0x01, 0x41, + 0x00, 0x28, 0x02, 0x94, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x04, 0x49, 0x0d, + 0x01, 0x20, 0x02, 0x20, 0x00, 0x6a, 0x21, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, 0x80, + 0x00, 0x46, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x02, 0x41, 0xff, 0x01, 0x4b, + 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, 0x22, 0x04, 0x20, 0x02, 0x41, + 0x03, 0x76, 0x22, 0x05, 0x41, 0x03, 0x74, 0x41, 0xac, 0xa8, 0x80, 0x80, + 0x00, 0x6a, 0x22, 0x06, 0x46, 0x1a, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, + 0x0c, 0x22, 0x02, 0x20, 0x04, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x00, + 0x28, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, 0x05, 0x77, + 0x71, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x05, 0x0b, 0x20, + 0x02, 0x20, 0x06, 0x46, 0x1a, 0x20, 0x02, 0x20, 0x04, 0x36, 0x02, 0x08, + 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x0c, 0x04, 0x0b, 0x20, 0x01, + 0x28, 0x02, 0x18, 0x21, 0x07, 0x02, 0x40, 0x20, 0x01, 0x28, 0x02, 0x0c, + 0x22, 0x06, 0x20, 0x01, 0x46, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x08, + 0x22, 0x02, 0x20, 0x04, 0x49, 0x1a, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, + 0x08, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x0c, 0x0c, 0x03, 0x0b, 0x02, + 0x40, 0x20, 0x01, 0x41, 0x14, 0x6a, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, + 0x02, 0x0d, 0x00, 0x20, 0x01, 0x28, 0x02, 0x10, 0x22, 0x02, 0x45, 0x0d, + 0x02, 0x20, 0x01, 0x41, 0x10, 0x6a, 0x21, 0x04, 0x0b, 0x03, 0x40, 0x20, + 0x04, 0x21, 0x05, 0x20, 0x02, 0x22, 0x06, 0x41, 0x14, 0x6a, 0x22, 0x04, + 0x28, 0x02, 0x00, 0x22, 0x02, 0x0d, 0x00, 0x20, 0x06, 0x41, 0x10, 0x6a, + 0x21, 0x04, 0x20, 0x06, 0x28, 0x02, 0x10, 0x22, 0x02, 0x0d, 0x00, 0x0b, + 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x03, + 0x28, 0x02, 0x04, 0x22, 0x02, 0x41, 0x03, 0x71, 0x41, 0x03, 0x47, 0x0d, + 0x02, 0x20, 0x03, 0x20, 0x02, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x04, 0x41, + 0x00, 0x20, 0x00, 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x20, 0x03, + 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, + 0x36, 0x02, 0x04, 0x0f, 0x0b, 0x41, 0x00, 0x21, 0x06, 0x0b, 0x20, 0x07, + 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, 0x20, 0x01, 0x28, + 0x02, 0x1c, 0x22, 0x04, 0x41, 0x02, 0x74, 0x41, 0xb4, 0xaa, 0x80, 0x80, + 0x00, 0x6a, 0x22, 0x02, 0x28, 0x02, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x02, + 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x06, 0x0d, 0x01, 0x41, 0x00, 0x41, + 0x00, 0x28, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7e, 0x20, 0x04, + 0x77, 0x71, 0x36, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, + 0x20, 0x07, 0x41, 0x10, 0x41, 0x14, 0x20, 0x07, 0x28, 0x02, 0x10, 0x20, + 0x01, 0x46, 0x1b, 0x6a, 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x06, 0x45, + 0x0d, 0x01, 0x0b, 0x20, 0x06, 0x20, 0x07, 0x36, 0x02, 0x18, 0x02, 0x40, + 0x20, 0x01, 0x28, 0x02, 0x10, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x06, + 0x20, 0x02, 0x36, 0x02, 0x10, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x18, + 0x0b, 0x20, 0x01, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, 0x02, 0x45, + 0x0d, 0x00, 0x20, 0x06, 0x41, 0x14, 0x6a, 0x20, 0x02, 0x36, 0x02, 0x00, + 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x01, 0x20, 0x03, + 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x28, 0x02, 0x04, 0x22, 0x02, 0x41, 0x01, + 0x71, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x02, 0x41, 0x02, 0x71, 0x0d, 0x00, 0x02, 0x40, 0x20, + 0x03, 0x41, 0x00, 0x28, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, 0x47, 0x0d, + 0x00, 0x41, 0x00, 0x20, 0x01, 0x36, 0x02, 0x9c, 0xa8, 0x80, 0x80, 0x00, + 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x00, 0x6a, 0x22, 0x00, 0x36, 0x02, 0x90, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x01, 0x41, + 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, 0x47, 0x0d, 0x06, 0x41, + 0x00, 0x41, 0x00, 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x00, + 0x41, 0x00, 0x36, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, 0x0f, 0x0b, 0x02, + 0x40, 0x20, 0x03, 0x41, 0x00, 0x28, 0x02, 0x98, 0xa8, 0x80, 0x80, 0x00, + 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x01, 0x36, 0x02, 0x98, 0xa8, 0x80, + 0x80, 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0x8c, 0xa8, 0x80, 0x80, + 0x00, 0x20, 0x00, 0x6a, 0x22, 0x00, 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, + 0x00, 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, + 0x01, 0x20, 0x00, 0x6a, 0x20, 0x00, 0x36, 0x02, 0x00, 0x0f, 0x0b, 0x20, + 0x02, 0x41, 0x78, 0x71, 0x20, 0x00, 0x6a, 0x21, 0x00, 0x02, 0x40, 0x20, + 0x02, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x03, 0x28, 0x02, 0x08, + 0x22, 0x04, 0x20, 0x02, 0x41, 0x03, 0x76, 0x22, 0x05, 0x41, 0x03, 0x74, + 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x22, 0x06, 0x46, 0x1a, 0x02, + 0x40, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x22, 0x02, 0x20, 0x04, 0x47, 0x0d, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x28, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, + 0x41, 0x7e, 0x20, 0x05, 0x77, 0x71, 0x36, 0x02, 0x84, 0xa8, 0x80, 0x80, + 0x00, 0x0c, 0x05, 0x0b, 0x20, 0x02, 0x20, 0x06, 0x46, 0x1a, 0x20, 0x02, + 0x20, 0x04, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, 0x0c, + 0x0c, 0x04, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x18, 0x21, 0x07, 0x02, 0x40, + 0x20, 0x03, 0x28, 0x02, 0x0c, 0x22, 0x06, 0x20, 0x03, 0x46, 0x0d, 0x00, + 0x20, 0x03, 0x28, 0x02, 0x08, 0x22, 0x02, 0x41, 0x00, 0x28, 0x02, 0x94, + 0xa8, 0x80, 0x80, 0x00, 0x49, 0x1a, 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, + 0x08, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x0c, 0x0c, 0x03, 0x0b, 0x02, + 0x40, 0x20, 0x03, 0x41, 0x14, 0x6a, 0x22, 0x04, 0x28, 0x02, 0x00, 0x22, + 0x02, 0x0d, 0x00, 0x20, 0x03, 0x28, 0x02, 0x10, 0x22, 0x02, 0x45, 0x0d, + 0x02, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x21, 0x04, 0x0b, 0x03, 0x40, 0x20, + 0x04, 0x21, 0x05, 0x20, 0x02, 0x22, 0x06, 0x41, 0x14, 0x6a, 0x22, 0x04, + 0x28, 0x02, 0x00, 0x22, 0x02, 0x0d, 0x00, 0x20, 0x06, 0x41, 0x10, 0x6a, + 0x21, 0x04, 0x20, 0x06, 0x28, 0x02, 0x10, 0x22, 0x02, 0x0d, 0x00, 0x0b, + 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x03, + 0x20, 0x02, 0x41, 0x7e, 0x71, 0x36, 0x02, 0x04, 0x20, 0x01, 0x20, 0x00, + 0x6a, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, + 0x72, 0x36, 0x02, 0x04, 0x0c, 0x03, 0x0b, 0x41, 0x00, 0x21, 0x06, 0x0b, + 0x20, 0x07, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x20, + 0x03, 0x28, 0x02, 0x1c, 0x22, 0x04, 0x41, 0x02, 0x74, 0x41, 0xb4, 0xaa, + 0x80, 0x80, 0x00, 0x6a, 0x22, 0x02, 0x28, 0x02, 0x00, 0x47, 0x0d, 0x00, + 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, 0x06, 0x0d, 0x01, 0x41, + 0x00, 0x41, 0x00, 0x28, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7e, + 0x20, 0x04, 0x77, 0x71, 0x36, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x0c, + 0x02, 0x0b, 0x20, 0x07, 0x41, 0x10, 0x41, 0x14, 0x20, 0x07, 0x28, 0x02, + 0x10, 0x20, 0x03, 0x46, 0x1b, 0x6a, 0x20, 0x06, 0x36, 0x02, 0x00, 0x20, + 0x06, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x06, 0x20, 0x07, 0x36, 0x02, 0x18, + 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x10, 0x22, 0x02, 0x45, 0x0d, 0x00, + 0x20, 0x06, 0x20, 0x02, 0x36, 0x02, 0x10, 0x20, 0x02, 0x20, 0x06, 0x36, + 0x02, 0x18, 0x0b, 0x20, 0x03, 0x41, 0x14, 0x6a, 0x28, 0x02, 0x00, 0x22, + 0x02, 0x45, 0x0d, 0x00, 0x20, 0x06, 0x41, 0x14, 0x6a, 0x20, 0x02, 0x36, + 0x02, 0x00, 0x20, 0x02, 0x20, 0x06, 0x36, 0x02, 0x18, 0x0b, 0x20, 0x01, + 0x20, 0x00, 0x6a, 0x20, 0x00, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x00, + 0x41, 0x01, 0x72, 0x36, 0x02, 0x04, 0x20, 0x01, 0x41, 0x00, 0x28, 0x02, + 0x98, 0xa8, 0x80, 0x80, 0x00, 0x47, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x00, + 0x36, 0x02, 0x8c, 0xa8, 0x80, 0x80, 0x00, 0x0f, 0x0b, 0x02, 0x40, 0x20, + 0x00, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x78, 0x71, + 0x41, 0xac, 0xa8, 0x80, 0x80, 0x00, 0x6a, 0x21, 0x02, 0x02, 0x40, 0x02, + 0x40, 0x41, 0x00, 0x28, 0x02, 0x84, 0xa8, 0x80, 0x80, 0x00, 0x22, 0x04, + 0x41, 0x01, 0x20, 0x00, 0x41, 0x03, 0x76, 0x74, 0x22, 0x00, 0x71, 0x0d, + 0x00, 0x41, 0x00, 0x20, 0x04, 0x20, 0x00, 0x72, 0x36, 0x02, 0x84, 0xa8, + 0x80, 0x80, 0x00, 0x20, 0x02, 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x02, + 0x28, 0x02, 0x08, 0x21, 0x00, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, + 0x0c, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x01, 0x20, 0x02, + 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x08, 0x0f, 0x0b, + 0x41, 0x1f, 0x21, 0x02, 0x02, 0x40, 0x20, 0x00, 0x41, 0xff, 0xff, 0xff, + 0x07, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x26, 0x20, 0x00, 0x41, 0x08, + 0x76, 0x67, 0x22, 0x02, 0x6b, 0x76, 0x41, 0x01, 0x71, 0x20, 0x02, 0x41, + 0x01, 0x74, 0x6b, 0x41, 0x3e, 0x6a, 0x21, 0x02, 0x0b, 0x20, 0x01, 0x20, + 0x02, 0x36, 0x02, 0x1c, 0x20, 0x01, 0x42, 0x00, 0x37, 0x02, 0x10, 0x20, + 0x02, 0x41, 0x02, 0x74, 0x41, 0xb4, 0xaa, 0x80, 0x80, 0x00, 0x6a, 0x21, + 0x04, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x88, 0xa8, 0x80, + 0x80, 0x00, 0x22, 0x06, 0x41, 0x01, 0x20, 0x02, 0x74, 0x22, 0x03, 0x71, + 0x0d, 0x00, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x00, 0x41, 0x00, 0x20, + 0x06, 0x20, 0x03, 0x72, 0x36, 0x02, 0x88, 0xa8, 0x80, 0x80, 0x00, 0x20, + 0x01, 0x20, 0x04, 0x36, 0x02, 0x18, 0x20, 0x01, 0x20, 0x01, 0x36, 0x02, + 0x08, 0x20, 0x01, 0x20, 0x01, 0x36, 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x20, + 0x00, 0x41, 0x00, 0x41, 0x19, 0x20, 0x02, 0x41, 0x01, 0x76, 0x6b, 0x20, + 0x02, 0x41, 0x1f, 0x46, 0x1b, 0x74, 0x21, 0x02, 0x20, 0x04, 0x28, 0x02, + 0x00, 0x21, 0x06, 0x02, 0x40, 0x03, 0x40, 0x20, 0x06, 0x22, 0x04, 0x28, + 0x02, 0x04, 0x41, 0x78, 0x71, 0x20, 0x00, 0x46, 0x0d, 0x01, 0x20, 0x02, + 0x41, 0x1d, 0x76, 0x21, 0x06, 0x20, 0x02, 0x41, 0x01, 0x74, 0x21, 0x02, + 0x20, 0x04, 0x20, 0x06, 0x41, 0x04, 0x71, 0x6a, 0x41, 0x10, 0x6a, 0x22, + 0x03, 0x28, 0x02, 0x00, 0x22, 0x06, 0x0d, 0x00, 0x0b, 0x20, 0x03, 0x20, + 0x01, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, 0x18, 0x20, + 0x01, 0x20, 0x01, 0x36, 0x02, 0x0c, 0x20, 0x01, 0x20, 0x01, 0x36, 0x02, + 0x08, 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x22, 0x00, 0x20, + 0x01, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, + 0x01, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x01, 0x20, 0x04, 0x36, 0x02, + 0x0c, 0x20, 0x01, 0x20, 0x00, 0x36, 0x02, 0x08, 0x0b, 0x41, 0x00, 0x41, + 0x00, 0x28, 0x02, 0xa4, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x6a, 0x22, + 0x01, 0x41, 0x7f, 0x20, 0x01, 0x1b, 0x36, 0x02, 0xa4, 0xa8, 0x80, 0x80, + 0x00, 0x0b, 0x0b, 0x6b, 0x02, 0x01, 0x7f, 0x01, 0x7e, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x02, 0x0c, 0x01, 0x0b, + 0x20, 0x00, 0xad, 0x20, 0x01, 0xad, 0x7e, 0x22, 0x03, 0xa7, 0x21, 0x02, + 0x20, 0x01, 0x20, 0x00, 0x72, 0x41, 0x80, 0x80, 0x04, 0x49, 0x0d, 0x00, + 0x41, 0x7f, 0x20, 0x02, 0x20, 0x03, 0x42, 0x20, 0x88, 0xa7, 0x41, 0x00, + 0x47, 0x1b, 0x21, 0x02, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x10, 0x90, 0x80, + 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x7c, + 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x00, + 0x41, 0x00, 0x20, 0x02, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x20, 0x00, 0x0b, 0x0b, 0x00, 0x20, 0x00, 0x10, 0x9e, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x0b, 0xd5, 0x01, 0x01, 0x03, 0x7f, 0x23, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x00, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x00, 0x41, 0x08, 0x6a, 0x20, 0x00, 0x41, 0x0c, 0x6a, 0x10, 0x99, 0x80, + 0x80, 0x80, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x08, 0x41, 0x01, + 0x6a, 0x22, 0x01, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x28, 0x02, 0x0c, 0x10, + 0x8f, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x02, 0x20, 0x01, + 0x41, 0x04, 0x10, 0x93, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x45, 0x0d, + 0x03, 0x20, 0x01, 0x20, 0x02, 0x10, 0x98, 0x80, 0x80, 0x80, 0x00, 0x0d, + 0x04, 0x20, 0x00, 0x28, 0x02, 0x08, 0x20, 0x01, 0x10, 0x8e, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x01, 0x20, 0x00, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x01, 0x0f, 0x0b, 0x41, 0xc7, 0x00, 0x10, 0x94, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x41, 0xc6, 0x00, 0x10, 0x94, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x0b, 0x41, 0xc6, 0x00, 0x10, 0x94, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x0b, 0x20, 0x02, 0x10, 0x91, 0x80, 0x80, 0x80, 0x00, + 0x41, 0xc6, 0x00, 0x10, 0x94, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, 0x20, + 0x02, 0x10, 0x91, 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x10, 0x91, 0x80, + 0x80, 0x80, 0x00, 0x41, 0xc7, 0x00, 0x10, 0x94, 0x80, 0x80, 0x80, 0x00, + 0x00, 0x0b, 0x02, 0x00, 0x0b, 0x27, 0x00, 0x10, 0x96, 0x80, 0x80, 0x80, + 0x00, 0x02, 0x40, 0x20, 0x00, 0x10, 0x9a, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x00, 0x0d, 0x00, 0x41, 0x00, 0x0f, 0x0b, 0x41, 0x00, 0x20, 0x00, 0x36, + 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x0b, 0x11, 0x00, 0x20, + 0x00, 0x20, 0x01, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, + 0x03, 0x71, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x81, 0x80, + 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x0f, 0x00, 0x20, + 0x00, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, + 0x0b, 0x11, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x83, 0x80, 0x80, 0x80, + 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x15, 0x00, 0x20, 0x00, 0x20, + 0x01, 0x20, 0x02, 0x20, 0x03, 0x10, 0x84, 0x80, 0x80, 0x80, 0x00, 0x41, + 0xff, 0xff, 0x03, 0x71, 0x0b, 0x15, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, + 0x02, 0x20, 0x03, 0x10, 0x85, 0x80, 0x80, 0x80, 0x00, 0x41, 0xff, 0xff, + 0x03, 0x71, 0x0b, 0x0b, 0x00, 0x20, 0x00, 0x10, 0x86, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x0b, 0x19, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, + 0x03, 0x20, 0x04, 0x20, 0x05, 0x10, 0x87, 0x80, 0x80, 0x80, 0x00, 0x41, + 0xff, 0xff, 0x03, 0x71, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x4e, 0x00, + 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x3f, 0x00, 0x41, 0x10, 0x74, 0x0f, + 0x0b, 0x02, 0x40, 0x20, 0x00, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0d, 0x00, + 0x20, 0x00, 0x41, 0x7f, 0x4c, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x41, + 0x10, 0x76, 0x40, 0x00, 0x22, 0x00, 0x41, 0x7f, 0x47, 0x0d, 0x00, 0x41, + 0x00, 0x41, 0x30, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7f, + 0x0f, 0x0b, 0x20, 0x00, 0x41, 0x10, 0x74, 0x0f, 0x0b, 0x10, 0xa0, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x0b, 0x02, 0x00, 0x0b, 0x0e, 0x00, 0x10, 0xa2, + 0x80, 0x80, 0x80, 0x00, 0x10, 0xaf, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x23, + 0x00, 0x20, 0x00, 0x41, 0x18, 0x74, 0x20, 0x00, 0x41, 0x80, 0xfe, 0x03, + 0x71, 0x41, 0x08, 0x74, 0x72, 0x20, 0x00, 0x41, 0x08, 0x76, 0x41, 0x80, + 0xfe, 0x03, 0x71, 0x20, 0x00, 0x41, 0x18, 0x76, 0x72, 0x72, 0x0b, 0x12, + 0x00, 0x20, 0x00, 0x41, 0x08, 0x74, 0x20, 0x00, 0x41, 0x08, 0x76, 0x72, + 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x3b, 0x01, 0x01, 0x7f, 0x23, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x02, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x0c, 0x41, 0x80, + 0x9e, 0x80, 0x80, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0xba, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x01, 0x20, 0x02, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x01, 0x0b, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, + 0x38, 0x10, 0x97, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x71, 0x01, 0x02, 0x7f, + 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, 0x04, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x02, 0x41, 0x7f, 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x41, 0x1c, + 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x02, 0x40, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x41, 0x0c, 0x6a, 0x10, + 0x9d, 0x80, 0x80, 0x80, 0x00, 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, + 0x20, 0x02, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, + 0x04, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x28, 0x02, 0x0c, 0x21, 0x04, 0x0b, + 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x04, 0x0b, 0xbb, 0x02, 0x01, 0x07, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x03, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x20, 0x03, 0x20, 0x01, 0x36, + 0x02, 0x08, 0x20, 0x03, 0x20, 0x00, 0x28, 0x02, 0x18, 0x22, 0x01, 0x36, + 0x02, 0x00, 0x20, 0x03, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x01, 0x6b, + 0x22, 0x04, 0x36, 0x02, 0x04, 0x41, 0x02, 0x21, 0x05, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x03, 0x41, 0x02, 0x10, 0xa8, + 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x20, 0x04, 0x20, 0x02, 0x6a, 0x22, + 0x06, 0x46, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x04, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x01, 0x41, 0x7f, 0x4a, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x01, 0x20, + 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, 0x00, 0x42, 0x00, 0x37, 0x03, + 0x10, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x41, 0x20, 0x72, 0x36, + 0x02, 0x00, 0x20, 0x05, 0x41, 0x02, 0x46, 0x0d, 0x03, 0x20, 0x02, 0x20, + 0x04, 0x28, 0x02, 0x04, 0x6b, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x04, + 0x20, 0x01, 0x20, 0x04, 0x28, 0x02, 0x04, 0x22, 0x07, 0x4b, 0x22, 0x08, + 0x41, 0x03, 0x74, 0x6a, 0x22, 0x09, 0x20, 0x09, 0x28, 0x02, 0x00, 0x20, + 0x01, 0x20, 0x07, 0x41, 0x00, 0x20, 0x08, 0x1b, 0x6b, 0x22, 0x07, 0x6a, + 0x36, 0x02, 0x00, 0x20, 0x04, 0x41, 0x0c, 0x41, 0x04, 0x20, 0x08, 0x1b, + 0x6a, 0x22, 0x04, 0x20, 0x04, 0x28, 0x02, 0x00, 0x20, 0x07, 0x6b, 0x36, + 0x02, 0x00, 0x20, 0x09, 0x21, 0x04, 0x20, 0x06, 0x20, 0x01, 0x6b, 0x22, + 0x06, 0x20, 0x00, 0x28, 0x02, 0x38, 0x20, 0x09, 0x20, 0x05, 0x20, 0x08, + 0x6b, 0x22, 0x05, 0x10, 0xa8, 0x80, 0x80, 0x80, 0x00, 0x22, 0x01, 0x47, + 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, + 0x01, 0x36, 0x02, 0x18, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x6a, 0x36, 0x02, 0x10, + 0x20, 0x02, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0b, 0x66, 0x01, 0x02, 0x7f, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6b, 0x22, 0x01, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, + 0x41, 0x08, 0x6a, 0x10, 0x9b, 0x80, 0x80, 0x80, 0x00, 0x22, 0x00, 0x0d, + 0x00, 0x41, 0x3b, 0x21, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x08, 0x41, 0x02, + 0x47, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x10, 0x41, 0x24, 0x71, 0x0d, + 0x00, 0x41, 0x01, 0x21, 0x02, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x02, + 0x41, 0x00, 0x20, 0x00, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x0b, + 0x20, 0x01, 0x41, 0x20, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x02, 0x0b, 0x3b, 0x00, 0x20, 0x00, 0x41, 0x81, 0x80, 0x80, 0x80, 0x00, + 0x36, 0x02, 0x20, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0xc0, + 0x00, 0x71, 0x0d, 0x00, 0x20, 0x00, 0x28, 0x02, 0x38, 0x10, 0xaa, 0x80, + 0x80, 0x80, 0x00, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x7f, 0x36, 0x02, 0x40, + 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0xa9, 0x80, 0x80, 0x80, + 0x00, 0x0b, 0x64, 0x01, 0x01, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x41, 0x10, 0x6b, 0x22, 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x41, 0xff, 0x01, + 0x71, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x10, 0x9c, 0x80, 0x80, 0x80, 0x00, + 0x22, 0x02, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x41, 0xc6, 0x00, 0x20, 0x02, + 0x20, 0x02, 0x41, 0xcc, 0x00, 0x46, 0x1b, 0x36, 0x02, 0x80, 0xa8, 0x80, + 0x80, 0x00, 0x42, 0x7f, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x29, + 0x03, 0x08, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x01, 0x0b, 0x11, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x38, 0x20, 0x01, 0x20, 0x02, 0x10, 0xac, 0x80, 0x80, 0x80, 0x00, + 0x0b, 0x08, 0x00, 0x41, 0x88, 0xb4, 0x80, 0x80, 0x00, 0x0b, 0x83, 0x03, + 0x01, 0x03, 0x7f, 0x02, 0x40, 0x10, 0xae, 0x80, 0x80, 0x80, 0x00, 0x28, + 0x02, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, + 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, + 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, + 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, + 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, + 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, + 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x34, 0x22, 0x00, 0x0d, 0x00, + 0x0b, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, 0x8c, 0xb4, 0x80, 0x80, + 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, + 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, + 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, + 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, + 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x02, 0x40, + 0x41, 0x00, 0x28, 0x02, 0xf0, 0x9e, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, + 0x02, 0x18, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, + 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, + 0x0b, 0x20, 0x00, 0x28, 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, + 0x08, 0x22, 0x02, 0x46, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, + 0x6b, 0xac, 0x41, 0x01, 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x41, 0x00, 0x28, 0x02, + 0xe8, 0x9f, 0x80, 0x80, 0x00, 0x22, 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, + 0x20, 0x00, 0x28, 0x02, 0x14, 0x20, 0x00, 0x28, 0x02, 0x18, 0x46, 0x0d, + 0x00, 0x20, 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, + 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x0b, 0x20, 0x00, 0x28, + 0x02, 0x04, 0x22, 0x01, 0x20, 0x00, 0x28, 0x02, 0x08, 0x22, 0x02, 0x46, + 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x6b, 0xac, 0x41, 0x01, + 0x20, 0x00, 0x28, 0x02, 0x24, 0x11, 0x81, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x1a, 0x0b, 0x0b, 0x5c, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x20, 0x00, 0x28, + 0x02, 0x3c, 0x22, 0x01, 0x41, 0x7f, 0x6a, 0x20, 0x01, 0x72, 0x36, 0x02, + 0x3c, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x08, + 0x71, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x20, 0x72, 0x36, + 0x02, 0x00, 0x41, 0x7f, 0x0f, 0x0b, 0x20, 0x00, 0x42, 0x00, 0x37, 0x02, + 0x04, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x28, 0x22, 0x01, 0x36, 0x02, + 0x18, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x14, 0x20, 0x00, 0x20, 0x01, + 0x20, 0x00, 0x28, 0x02, 0x2c, 0x6a, 0x36, 0x02, 0x10, 0x41, 0x00, 0x0b, + 0xe8, 0x01, 0x01, 0x05, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x28, + 0x02, 0x10, 0x22, 0x03, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x04, 0x20, 0x02, + 0x10, 0xb0, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x01, 0x20, 0x02, 0x28, 0x02, + 0x10, 0x21, 0x03, 0x0b, 0x02, 0x40, 0x20, 0x03, 0x20, 0x02, 0x28, 0x02, + 0x14, 0x22, 0x05, 0x6b, 0x20, 0x01, 0x4f, 0x0d, 0x00, 0x20, 0x02, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x02, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x0f, 0x0b, 0x41, 0x00, 0x21, 0x06, 0x02, 0x40, 0x20, + 0x02, 0x28, 0x02, 0x40, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x41, 0x00, 0x21, + 0x06, 0x20, 0x00, 0x21, 0x04, 0x41, 0x00, 0x21, 0x03, 0x03, 0x40, 0x20, + 0x01, 0x20, 0x03, 0x46, 0x0d, 0x01, 0x20, 0x03, 0x41, 0x01, 0x6a, 0x21, + 0x03, 0x20, 0x04, 0x41, 0x7f, 0x6a, 0x22, 0x04, 0x20, 0x01, 0x6a, 0x22, + 0x07, 0x2d, 0x00, 0x00, 0x41, 0x0a, 0x47, 0x0d, 0x00, 0x0b, 0x20, 0x02, + 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x6b, 0x41, 0x01, 0x6a, 0x22, 0x06, + 0x20, 0x02, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, + 0x22, 0x04, 0x20, 0x06, 0x49, 0x0d, 0x01, 0x20, 0x03, 0x41, 0x7f, 0x6a, + 0x21, 0x01, 0x20, 0x07, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x20, 0x02, 0x28, + 0x02, 0x14, 0x21, 0x05, 0x0b, 0x20, 0x05, 0x20, 0x00, 0x20, 0x01, 0x10, + 0xbf, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, + 0x14, 0x20, 0x01, 0x6a, 0x36, 0x02, 0x14, 0x20, 0x06, 0x20, 0x01, 0x6a, + 0x21, 0x04, 0x0b, 0x20, 0x04, 0x0b, 0x95, 0x02, 0x01, 0x06, 0x7f, 0x20, + 0x02, 0x20, 0x01, 0x6c, 0x21, 0x04, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, + 0x28, 0x02, 0x10, 0x22, 0x05, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x06, 0x20, + 0x03, 0x10, 0xb0, 0x80, 0x80, 0x80, 0x00, 0x0d, 0x01, 0x20, 0x03, 0x28, + 0x02, 0x10, 0x21, 0x05, 0x0b, 0x02, 0x40, 0x20, 0x05, 0x20, 0x03, 0x28, + 0x02, 0x14, 0x22, 0x07, 0x6b, 0x20, 0x04, 0x4f, 0x0d, 0x00, 0x20, 0x03, + 0x20, 0x00, 0x20, 0x04, 0x20, 0x03, 0x28, 0x02, 0x20, 0x11, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x00, 0x21, 0x06, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, + 0x08, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x28, 0x02, 0x40, 0x41, 0x00, + 0x4e, 0x0d, 0x00, 0x20, 0x04, 0x21, 0x05, 0x0c, 0x01, 0x0b, 0x20, 0x00, + 0x20, 0x04, 0x6a, 0x21, 0x06, 0x41, 0x00, 0x21, 0x08, 0x41, 0x00, 0x21, + 0x05, 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, 0x20, 0x05, 0x6a, 0x0d, 0x00, + 0x20, 0x04, 0x21, 0x05, 0x0c, 0x02, 0x0b, 0x20, 0x05, 0x41, 0x7f, 0x6a, + 0x22, 0x05, 0x20, 0x06, 0x6a, 0x22, 0x09, 0x2d, 0x00, 0x00, 0x41, 0x0a, + 0x47, 0x0d, 0x00, 0x0b, 0x20, 0x03, 0x20, 0x00, 0x20, 0x04, 0x20, 0x05, + 0x6a, 0x41, 0x01, 0x6a, 0x22, 0x08, 0x20, 0x03, 0x28, 0x02, 0x20, 0x11, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x22, 0x06, 0x20, 0x08, 0x49, 0x0d, + 0x01, 0x20, 0x05, 0x41, 0x7f, 0x73, 0x21, 0x05, 0x20, 0x09, 0x41, 0x01, + 0x6a, 0x21, 0x00, 0x20, 0x03, 0x28, 0x02, 0x14, 0x21, 0x07, 0x0b, 0x20, + 0x07, 0x20, 0x00, 0x20, 0x05, 0x10, 0xbf, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x20, 0x03, 0x20, 0x03, 0x28, 0x02, 0x14, 0x20, 0x05, 0x6a, 0x36, 0x02, + 0x14, 0x20, 0x08, 0x20, 0x05, 0x6a, 0x21, 0x06, 0x0b, 0x02, 0x40, 0x20, + 0x06, 0x20, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x02, 0x41, 0x00, 0x20, 0x01, + 0x1b, 0x0f, 0x0b, 0x20, 0x06, 0x20, 0x01, 0x6e, 0x0b, 0x04, 0x00, 0x20, + 0x00, 0x0b, 0x0c, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0xb3, 0x80, 0x80, + 0x80, 0x00, 0x0b, 0x55, 0x01, 0x01, 0x7f, 0x02, 0x40, 0x41, 0x00, 0x28, + 0x02, 0xa8, 0xb4, 0x80, 0x80, 0x00, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x90, + 0xb4, 0x80, 0x80, 0x00, 0x21, 0x01, 0x41, 0x00, 0x41, 0x90, 0xb4, 0x80, + 0x80, 0x00, 0x36, 0x02, 0xa8, 0xb4, 0x80, 0x80, 0x00, 0x0b, 0x41, 0x00, + 0x20, 0x00, 0x20, 0x00, 0x41, 0xcc, 0x00, 0x4b, 0x1b, 0x41, 0x01, 0x74, + 0x41, 0x80, 0x99, 0x80, 0x80, 0x00, 0x6a, 0x2f, 0x01, 0x00, 0x41, 0xef, + 0x8c, 0x80, 0x80, 0x00, 0x6a, 0x20, 0x01, 0x28, 0x02, 0x14, 0x10, 0xb4, + 0x80, 0x80, 0x80, 0x00, 0x0b, 0xb6, 0x02, 0x01, 0x01, 0x7f, 0x41, 0x01, + 0x21, 0x03, 0x02, 0x40, 0x20, 0x00, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, + 0x01, 0x41, 0xff, 0x00, 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x3a, + 0x00, 0x00, 0x41, 0x01, 0x0f, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, + 0x28, 0x02, 0x90, 0xb4, 0x80, 0x80, 0x00, 0x0d, 0x00, 0x02, 0x40, 0x20, + 0x01, 0x41, 0x80, 0x7f, 0x71, 0x41, 0x80, 0xbf, 0x03, 0x46, 0x0d, 0x00, + 0x41, 0x00, 0x41, 0x19, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x0c, + 0x02, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x41, 0x01, 0x0f, + 0x0b, 0x02, 0x40, 0x20, 0x01, 0x41, 0xff, 0x0f, 0x4b, 0x0d, 0x00, 0x20, + 0x00, 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, + 0x01, 0x20, 0x00, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, 0xc0, 0x01, 0x72, + 0x3a, 0x00, 0x00, 0x41, 0x02, 0x0f, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x01, 0x41, 0x80, 0xb0, 0x03, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x41, 0x80, + 0x40, 0x71, 0x41, 0x80, 0xc0, 0x03, 0x47, 0x0d, 0x01, 0x0b, 0x20, 0x00, + 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x02, + 0x20, 0x00, 0x20, 0x01, 0x41, 0x0c, 0x76, 0x41, 0xe0, 0x01, 0x72, 0x3a, + 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x06, 0x76, 0x41, 0x3f, 0x71, + 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x01, 0x41, 0x03, 0x0f, 0x0b, 0x02, + 0x40, 0x20, 0x01, 0x41, 0x80, 0x80, 0x7c, 0x6a, 0x41, 0xff, 0xff, 0x3f, + 0x4b, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, 0x3f, 0x71, 0x41, 0x80, + 0x01, 0x72, 0x3a, 0x00, 0x03, 0x20, 0x00, 0x20, 0x01, 0x41, 0x12, 0x76, + 0x41, 0xf0, 0x01, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x41, + 0x06, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, 0x01, 0x72, 0x3a, 0x00, 0x02, + 0x20, 0x00, 0x20, 0x01, 0x41, 0x0c, 0x76, 0x41, 0x3f, 0x71, 0x41, 0x80, + 0x01, 0x72, 0x3a, 0x00, 0x01, 0x41, 0x04, 0x0f, 0x0b, 0x41, 0x00, 0x41, + 0x19, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x0b, 0x41, 0x7f, 0x21, + 0x03, 0x0b, 0x20, 0x03, 0x0b, 0x18, 0x00, 0x02, 0x40, 0x20, 0x00, 0x0d, + 0x00, 0x41, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x41, 0x00, 0x10, + 0xb6, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x8f, 0x01, 0x02, 0x01, 0x7e, 0x01, + 0x7f, 0x02, 0x40, 0x20, 0x00, 0xbd, 0x22, 0x02, 0x42, 0x34, 0x88, 0xa7, + 0x41, 0xff, 0x0f, 0x71, 0x22, 0x03, 0x41, 0xff, 0x0f, 0x46, 0x0d, 0x00, + 0x02, 0x40, 0x20, 0x03, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, 0x00, 0x20, 0x01, + 0x41, 0x00, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x43, 0xa2, 0x20, 0x01, 0x10, + 0xb8, 0x80, 0x80, 0x80, 0x00, 0x21, 0x00, 0x20, 0x01, 0x20, 0x01, 0x28, + 0x02, 0x00, 0x41, 0x40, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x0f, 0x0b, + 0x20, 0x01, 0x20, 0x03, 0x41, 0x82, 0x78, 0x6a, 0x36, 0x02, 0x00, 0x20, + 0x02, 0x42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0x80, 0x7f, + 0x83, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf0, 0x3f, 0x84, + 0xbf, 0x21, 0x00, 0x0b, 0x20, 0x00, 0x0b, 0x24, 0x01, 0x01, 0x7f, 0x20, + 0x00, 0x10, 0xc1, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0x7f, 0x41, + 0x00, 0x20, 0x02, 0x20, 0x00, 0x41, 0x01, 0x20, 0x02, 0x20, 0x01, 0x10, + 0xb2, 0x80, 0x80, 0x80, 0x00, 0x47, 0x1b, 0x0b, 0x8c, 0x03, 0x01, 0x03, + 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xd0, 0x01, 0x6b, 0x22, + 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, 0x36, + 0x02, 0xcc, 0x01, 0x20, 0x03, 0x41, 0xa0, 0x01, 0x6a, 0x41, 0x20, 0x6a, + 0x42, 0x00, 0x37, 0x03, 0x00, 0x20, 0x03, 0x41, 0xb8, 0x01, 0x6a, 0x42, + 0x00, 0x37, 0x03, 0x00, 0x20, 0x03, 0x41, 0xb0, 0x01, 0x6a, 0x42, 0x00, + 0x37, 0x03, 0x00, 0x20, 0x03, 0x42, 0x00, 0x37, 0x03, 0xa8, 0x01, 0x20, + 0x03, 0x42, 0x00, 0x37, 0x03, 0xa0, 0x01, 0x20, 0x03, 0x20, 0x02, 0x36, + 0x02, 0xc8, 0x01, 0x02, 0x40, 0x02, 0x40, 0x41, 0x00, 0x20, 0x01, 0x20, + 0x03, 0x41, 0xc8, 0x01, 0x6a, 0x20, 0x03, 0x41, 0xd0, 0x00, 0x6a, 0x20, + 0x03, 0x41, 0xa0, 0x01, 0x6a, 0x10, 0xbb, 0x80, 0x80, 0x80, 0x00, 0x41, + 0x00, 0x4e, 0x0d, 0x00, 0x41, 0x7f, 0x21, 0x00, 0x0c, 0x01, 0x0b, 0x20, + 0x00, 0x28, 0x02, 0x00, 0x21, 0x04, 0x02, 0x40, 0x20, 0x00, 0x28, 0x02, + 0x3c, 0x41, 0x00, 0x4a, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x04, 0x41, 0x5f, + 0x71, 0x36, 0x02, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x28, 0x02, 0x2c, 0x0d, 0x00, 0x20, 0x00, 0x41, 0xd0, + 0x00, 0x36, 0x02, 0x2c, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, 0x18, 0x20, + 0x00, 0x42, 0x00, 0x37, 0x03, 0x10, 0x20, 0x00, 0x28, 0x02, 0x28, 0x21, + 0x05, 0x20, 0x00, 0x20, 0x03, 0x36, 0x02, 0x28, 0x0c, 0x01, 0x0b, 0x41, + 0x00, 0x21, 0x05, 0x20, 0x00, 0x28, 0x02, 0x10, 0x0d, 0x01, 0x0b, 0x41, + 0x7f, 0x21, 0x02, 0x20, 0x00, 0x10, 0xb0, 0x80, 0x80, 0x80, 0x00, 0x0d, + 0x01, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x03, 0x41, 0xc8, 0x01, 0x6a, + 0x20, 0x03, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x03, 0x41, 0xa0, 0x01, 0x6a, + 0x10, 0xbb, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x0b, 0x20, 0x04, 0x41, + 0x20, 0x71, 0x21, 0x01, 0x02, 0x40, 0x20, 0x05, 0x45, 0x0d, 0x00, 0x20, + 0x00, 0x41, 0x00, 0x41, 0x00, 0x20, 0x00, 0x28, 0x02, 0x20, 0x11, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x00, 0x1a, 0x20, 0x00, 0x41, 0x00, 0x36, 0x02, + 0x2c, 0x20, 0x00, 0x20, 0x05, 0x36, 0x02, 0x28, 0x20, 0x00, 0x41, 0x00, + 0x36, 0x02, 0x18, 0x20, 0x00, 0x28, 0x02, 0x14, 0x21, 0x05, 0x20, 0x00, + 0x42, 0x00, 0x37, 0x03, 0x10, 0x20, 0x02, 0x41, 0x7f, 0x20, 0x05, 0x1b, + 0x21, 0x02, 0x0b, 0x20, 0x00, 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x05, + 0x20, 0x01, 0x72, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x20, 0x02, 0x20, 0x05, + 0x41, 0x20, 0x71, 0x1b, 0x21, 0x00, 0x0b, 0x20, 0x03, 0x41, 0xd0, 0x01, + 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, 0x0b, 0xf5, 0x46, + 0x05, 0x1a, 0x7f, 0x02, 0x7e, 0x01, 0x7c, 0x08, 0x7f, 0x01, 0x7c, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0xf0, 0x06, 0x6b, 0x22, 0x05, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x41, + 0x0c, 0x6a, 0x21, 0x06, 0x41, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, + 0x6b, 0x21, 0x07, 0x20, 0x05, 0x41, 0xec, 0x60, 0x6a, 0x21, 0x08, 0x20, + 0x05, 0x41, 0x37, 0x6a, 0x21, 0x09, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, + 0x41, 0x0b, 0x6a, 0x21, 0x0a, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x41, + 0x7f, 0x6a, 0x21, 0x0b, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x41, 0x08, + 0x72, 0x21, 0x0c, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x41, 0x09, 0x72, + 0x21, 0x0d, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x41, 0x0a, 0x6a, 0x21, + 0x0e, 0x20, 0x05, 0x41, 0x38, 0x6a, 0x21, 0x0f, 0x41, 0x00, 0x21, 0x10, + 0x41, 0x00, 0x21, 0x11, 0x41, 0x00, 0x21, 0x12, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x03, 0x40, 0x20, 0x01, 0x21, 0x13, 0x20, 0x12, 0x20, 0x11, + 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x01, 0x20, 0x12, + 0x20, 0x11, 0x6a, 0x21, 0x11, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x13, 0x2d, 0x00, 0x00, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x20, 0x13, 0x21, + 0x01, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x12, 0x41, + 0xff, 0x01, 0x71, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x20, 0x12, 0x41, 0x25, + 0x47, 0x0d, 0x02, 0x20, 0x01, 0x21, 0x14, 0x20, 0x01, 0x21, 0x12, 0x03, + 0x40, 0x02, 0x40, 0x20, 0x12, 0x2d, 0x00, 0x01, 0x41, 0x25, 0x46, 0x0d, + 0x00, 0x20, 0x12, 0x21, 0x01, 0x0c, 0x03, 0x0b, 0x20, 0x14, 0x41, 0x01, + 0x6a, 0x21, 0x14, 0x20, 0x12, 0x2d, 0x00, 0x02, 0x21, 0x15, 0x20, 0x12, + 0x41, 0x02, 0x6a, 0x22, 0x01, 0x21, 0x12, 0x20, 0x15, 0x41, 0x25, 0x46, + 0x0d, 0x00, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x01, 0x21, 0x14, 0x0b, 0x20, + 0x14, 0x20, 0x13, 0x6b, 0x22, 0x12, 0x20, 0x11, 0x41, 0xff, 0xff, 0xff, + 0xff, 0x07, 0x73, 0x22, 0x14, 0x4a, 0x0d, 0x0c, 0x02, 0x40, 0x20, 0x00, + 0x45, 0x0d, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x13, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x0d, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, + 0x21, 0x12, 0x41, 0x7f, 0x21, 0x16, 0x02, 0x40, 0x20, 0x01, 0x2c, 0x00, + 0x01, 0x22, 0x17, 0x41, 0x50, 0x6a, 0x22, 0x15, 0x41, 0x09, 0x4b, 0x0d, + 0x00, 0x20, 0x01, 0x2d, 0x00, 0x02, 0x41, 0x24, 0x47, 0x0d, 0x00, 0x20, + 0x01, 0x41, 0x03, 0x6a, 0x21, 0x12, 0x20, 0x01, 0x2c, 0x00, 0x03, 0x21, + 0x17, 0x41, 0x01, 0x21, 0x10, 0x20, 0x15, 0x21, 0x16, 0x0b, 0x41, 0x00, + 0x21, 0x18, 0x02, 0x40, 0x20, 0x17, 0x41, 0x60, 0x6a, 0x22, 0x01, 0x41, + 0x1f, 0x4b, 0x0d, 0x00, 0x41, 0x01, 0x20, 0x01, 0x74, 0x22, 0x01, 0x41, + 0x89, 0xd1, 0x04, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x12, 0x41, 0x01, 0x6a, + 0x21, 0x15, 0x41, 0x00, 0x21, 0x18, 0x03, 0x40, 0x20, 0x01, 0x20, 0x18, + 0x72, 0x21, 0x18, 0x20, 0x15, 0x22, 0x12, 0x2c, 0x00, 0x00, 0x22, 0x17, + 0x41, 0x60, 0x6a, 0x22, 0x01, 0x41, 0x20, 0x4f, 0x0d, 0x01, 0x20, 0x12, + 0x41, 0x01, 0x6a, 0x21, 0x15, 0x41, 0x01, 0x20, 0x01, 0x74, 0x22, 0x01, + 0x41, 0x89, 0xd1, 0x04, 0x71, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, + 0x17, 0x41, 0x2a, 0x47, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x12, + 0x2c, 0x00, 0x01, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x09, 0x4b, 0x0d, + 0x00, 0x20, 0x12, 0x2d, 0x00, 0x02, 0x41, 0x24, 0x47, 0x0d, 0x00, 0x20, + 0x04, 0x20, 0x01, 0x41, 0x02, 0x74, 0x6a, 0x41, 0x0a, 0x36, 0x02, 0x00, + 0x20, 0x12, 0x41, 0x03, 0x6a, 0x21, 0x15, 0x20, 0x12, 0x2c, 0x00, 0x01, + 0x41, 0x03, 0x74, 0x20, 0x03, 0x6a, 0x41, 0x80, 0x7d, 0x6a, 0x28, 0x02, + 0x00, 0x21, 0x19, 0x41, 0x01, 0x21, 0x10, 0x0c, 0x01, 0x0b, 0x20, 0x10, + 0x0d, 0x06, 0x20, 0x12, 0x41, 0x01, 0x6a, 0x21, 0x15, 0x02, 0x40, 0x20, + 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x10, 0x41, 0x00, 0x21, 0x19, 0x0c, + 0x06, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, + 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x01, 0x28, 0x02, 0x00, 0x21, 0x19, + 0x41, 0x00, 0x21, 0x10, 0x0b, 0x20, 0x19, 0x41, 0x7f, 0x4a, 0x0d, 0x04, + 0x41, 0x00, 0x20, 0x19, 0x6b, 0x21, 0x19, 0x20, 0x18, 0x41, 0x80, 0xc0, + 0x00, 0x72, 0x21, 0x18, 0x0c, 0x04, 0x0b, 0x41, 0x00, 0x21, 0x19, 0x02, + 0x40, 0x20, 0x17, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x09, 0x4d, 0x0d, + 0x00, 0x20, 0x12, 0x21, 0x15, 0x0c, 0x04, 0x0b, 0x41, 0x00, 0x21, 0x19, + 0x03, 0x40, 0x02, 0x40, 0x20, 0x19, 0x41, 0xcc, 0x99, 0xb3, 0xe6, 0x00, + 0x4b, 0x0d, 0x00, 0x41, 0x7f, 0x20, 0x19, 0x41, 0x0a, 0x6c, 0x22, 0x15, + 0x20, 0x01, 0x6a, 0x20, 0x01, 0x20, 0x15, 0x41, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x73, 0x4b, 0x1b, 0x21, 0x19, 0x20, 0x12, 0x2c, 0x00, 0x01, 0x21, + 0x01, 0x20, 0x12, 0x41, 0x01, 0x6a, 0x22, 0x15, 0x21, 0x12, 0x20, 0x01, + 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x0a, 0x49, 0x0d, 0x01, 0x20, 0x19, + 0x41, 0x00, 0x48, 0x0d, 0x0e, 0x0c, 0x05, 0x0b, 0x20, 0x12, 0x2c, 0x00, + 0x01, 0x21, 0x01, 0x41, 0x7f, 0x21, 0x19, 0x20, 0x12, 0x41, 0x01, 0x6a, + 0x21, 0x12, 0x20, 0x01, 0x41, 0x50, 0x6a, 0x22, 0x01, 0x41, 0x0a, 0x49, + 0x0d, 0x00, 0x0c, 0x0d, 0x0b, 0x0b, 0x20, 0x01, 0x2d, 0x00, 0x01, 0x21, + 0x12, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, + 0x20, 0x00, 0x0d, 0x0b, 0x02, 0x40, 0x20, 0x10, 0x0d, 0x00, 0x41, 0x00, + 0x21, 0x11, 0x0c, 0x0c, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x04, 0x28, 0x02, 0x04, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x01, 0x21, 0x01, + 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x08, 0x6a, 0x20, 0x01, 0x20, 0x02, + 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, + 0x08, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x02, 0x21, 0x01, 0x0c, 0x01, 0x0b, + 0x20, 0x03, 0x41, 0x10, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xbc, 0x80, + 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x22, 0x01, + 0x0d, 0x00, 0x41, 0x03, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x18, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, + 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x10, 0x22, 0x01, 0x0d, 0x00, 0x41, + 0x04, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x20, 0x6a, 0x20, + 0x01, 0x20, 0x02, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, + 0x04, 0x28, 0x02, 0x14, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x05, 0x21, 0x01, + 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x28, 0x6a, 0x20, 0x01, 0x20, 0x02, + 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, + 0x18, 0x22, 0x01, 0x0d, 0x00, 0x41, 0x06, 0x21, 0x01, 0x0c, 0x01, 0x0b, + 0x20, 0x03, 0x41, 0x30, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xbc, 0x80, + 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x1c, 0x22, 0x01, + 0x0d, 0x00, 0x41, 0x07, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x38, 0x6a, 0x20, 0x01, 0x20, 0x02, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, + 0x02, 0x40, 0x20, 0x04, 0x28, 0x02, 0x20, 0x22, 0x01, 0x0d, 0x00, 0x41, + 0x08, 0x21, 0x01, 0x0c, 0x01, 0x0b, 0x20, 0x03, 0x41, 0xc0, 0x00, 0x6a, + 0x20, 0x01, 0x20, 0x02, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, + 0x28, 0x02, 0x24, 0x22, 0x01, 0x0d, 0x01, 0x41, 0x09, 0x21, 0x01, 0x0b, + 0x20, 0x01, 0x41, 0x02, 0x74, 0x21, 0x01, 0x03, 0x40, 0x20, 0x04, 0x20, + 0x01, 0x6a, 0x28, 0x02, 0x00, 0x0d, 0x03, 0x20, 0x01, 0x41, 0x04, 0x6a, + 0x22, 0x01, 0x41, 0x28, 0x47, 0x0d, 0x00, 0x0b, 0x41, 0x01, 0x21, 0x11, + 0x0c, 0x0c, 0x0b, 0x20, 0x03, 0x41, 0xc8, 0x00, 0x6a, 0x20, 0x01, 0x20, + 0x02, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x41, 0x01, 0x21, 0x11, 0x0c, + 0x0b, 0x0b, 0x41, 0x00, 0x21, 0x12, 0x41, 0x7f, 0x21, 0x17, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x15, 0x2d, 0x00, 0x00, 0x41, 0x2e, 0x46, 0x0d, 0x00, + 0x20, 0x15, 0x21, 0x01, 0x41, 0x00, 0x21, 0x1a, 0x0c, 0x01, 0x0b, 0x02, + 0x40, 0x20, 0x15, 0x2c, 0x00, 0x01, 0x22, 0x17, 0x41, 0x2a, 0x47, 0x0d, + 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x15, 0x2c, 0x00, 0x02, 0x41, 0x50, + 0x6a, 0x22, 0x01, 0x41, 0x09, 0x4b, 0x0d, 0x00, 0x20, 0x15, 0x2d, 0x00, + 0x03, 0x41, 0x24, 0x47, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x01, 0x41, 0x02, + 0x74, 0x6a, 0x41, 0x0a, 0x36, 0x02, 0x00, 0x20, 0x15, 0x41, 0x04, 0x6a, + 0x21, 0x01, 0x20, 0x15, 0x2c, 0x00, 0x02, 0x41, 0x03, 0x74, 0x20, 0x03, + 0x6a, 0x41, 0x80, 0x7d, 0x6a, 0x28, 0x02, 0x00, 0x21, 0x17, 0x0c, 0x01, + 0x0b, 0x20, 0x10, 0x0d, 0x03, 0x20, 0x15, 0x41, 0x02, 0x6a, 0x21, 0x01, + 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x17, 0x0c, 0x01, + 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x15, 0x41, 0x04, + 0x6a, 0x36, 0x02, 0x00, 0x20, 0x15, 0x28, 0x02, 0x00, 0x21, 0x17, 0x0b, + 0x20, 0x17, 0x41, 0x7f, 0x73, 0x41, 0x1f, 0x76, 0x21, 0x1a, 0x0c, 0x01, + 0x0b, 0x20, 0x15, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x02, 0x40, 0x20, 0x17, + 0x41, 0x50, 0x6a, 0x22, 0x1b, 0x41, 0x09, 0x4d, 0x0d, 0x00, 0x41, 0x01, + 0x21, 0x1a, 0x41, 0x00, 0x21, 0x17, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, + 0x1c, 0x20, 0x01, 0x21, 0x15, 0x03, 0x40, 0x41, 0x7f, 0x21, 0x17, 0x02, + 0x40, 0x20, 0x1c, 0x41, 0xcc, 0x99, 0xb3, 0xe6, 0x00, 0x4b, 0x0d, 0x00, + 0x41, 0x7f, 0x20, 0x1c, 0x41, 0x0a, 0x6c, 0x22, 0x01, 0x20, 0x1b, 0x6a, + 0x20, 0x1b, 0x20, 0x01, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4b, + 0x1b, 0x21, 0x17, 0x0b, 0x41, 0x01, 0x21, 0x1a, 0x20, 0x15, 0x2c, 0x00, + 0x01, 0x21, 0x1b, 0x20, 0x17, 0x21, 0x1c, 0x20, 0x15, 0x41, 0x01, 0x6a, + 0x22, 0x01, 0x21, 0x15, 0x20, 0x1b, 0x41, 0x50, 0x6a, 0x22, 0x1b, 0x41, + 0x0a, 0x49, 0x0d, 0x00, 0x0b, 0x0b, 0x03, 0x40, 0x20, 0x12, 0x21, 0x15, + 0x20, 0x01, 0x2c, 0x00, 0x00, 0x22, 0x12, 0x41, 0x85, 0x7f, 0x6a, 0x41, + 0x46, 0x49, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, + 0x12, 0x20, 0x15, 0x41, 0x3a, 0x6c, 0x6a, 0x41, 0xdf, 0x99, 0x80, 0x80, + 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x22, 0x12, 0x41, 0x7f, 0x6a, 0x41, 0x08, + 0x49, 0x0d, 0x00, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x12, + 0x41, 0x1b, 0x46, 0x0d, 0x00, 0x20, 0x12, 0x45, 0x0d, 0x03, 0x02, 0x40, + 0x20, 0x16, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x20, 0x04, 0x20, 0x16, 0x41, + 0x02, 0x74, 0x6a, 0x20, 0x12, 0x36, 0x02, 0x00, 0x20, 0x05, 0x20, 0x03, + 0x20, 0x16, 0x41, 0x03, 0x74, 0x6a, 0x29, 0x03, 0x00, 0x37, 0x03, 0x38, + 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x0d, 0x00, 0x41, 0x00, 0x21, + 0x11, 0x0c, 0x0e, 0x0b, 0x20, 0x05, 0x41, 0x38, 0x6a, 0x20, 0x12, 0x20, + 0x02, 0x10, 0xbc, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x02, 0x0b, 0x20, 0x16, + 0x41, 0x7f, 0x4a, 0x0d, 0x02, 0x0b, 0x41, 0x00, 0x21, 0x12, 0x20, 0x00, + 0x45, 0x0d, 0x08, 0x0b, 0x20, 0x18, 0x41, 0xff, 0xff, 0x7b, 0x71, 0x22, + 0x1c, 0x20, 0x18, 0x20, 0x18, 0x41, 0x80, 0xc0, 0x00, 0x71, 0x1b, 0x21, + 0x16, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x01, 0x41, 0x7f, 0x6a, 0x2c, 0x00, 0x00, 0x22, 0x12, 0x41, 0x5f, 0x71, + 0x20, 0x12, 0x20, 0x12, 0x41, 0x0f, 0x71, 0x41, 0x03, 0x46, 0x1b, 0x20, + 0x12, 0x20, 0x15, 0x1b, 0x22, 0x1d, 0x41, 0xbf, 0x7f, 0x6a, 0x0e, 0x38, + 0x10, 0x12, 0x0d, 0x12, 0x10, 0x10, 0x10, 0x12, 0x12, 0x12, 0x12, 0x12, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0c, 0x12, 0x12, 0x12, 0x12, 0x03, + 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x10, 0x12, 0x08, 0x05, + 0x10, 0x10, 0x10, 0x12, 0x05, 0x12, 0x12, 0x12, 0x09, 0x01, 0x04, 0x02, + 0x12, 0x12, 0x0a, 0x12, 0x00, 0x12, 0x12, 0x03, 0x12, 0x0b, 0x41, 0x00, + 0x21, 0x1b, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, 0x05, + 0x29, 0x03, 0x38, 0x21, 0x1f, 0x0c, 0x05, 0x0b, 0x41, 0x00, 0x21, 0x12, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x15, 0x41, 0xff, 0x01, 0x71, 0x0e, 0x08, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x1d, 0x05, 0x06, 0x1d, 0x0b, 0x20, 0x05, 0x28, 0x02, + 0x38, 0x20, 0x11, 0x36, 0x02, 0x00, 0x0c, 0x1c, 0x0b, 0x20, 0x05, 0x28, + 0x02, 0x38, 0x20, 0x11, 0x36, 0x02, 0x00, 0x0c, 0x1b, 0x0b, 0x20, 0x05, + 0x28, 0x02, 0x38, 0x20, 0x11, 0xac, 0x37, 0x03, 0x00, 0x0c, 0x1a, 0x0b, + 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x3b, 0x01, 0x00, 0x0c, 0x19, + 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x3a, 0x00, 0x00, 0x0c, + 0x18, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0x36, 0x02, 0x00, + 0x0c, 0x17, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x20, 0x11, 0xac, 0x37, + 0x03, 0x00, 0x0c, 0x16, 0x0b, 0x20, 0x17, 0x41, 0x08, 0x20, 0x17, 0x41, + 0x08, 0x4b, 0x1b, 0x21, 0x17, 0x20, 0x16, 0x41, 0x08, 0x72, 0x21, 0x16, + 0x41, 0xf8, 0x00, 0x21, 0x1d, 0x0b, 0x41, 0x00, 0x21, 0x1b, 0x41, 0x80, + 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x02, 0x40, 0x20, 0x05, 0x29, 0x03, + 0x38, 0x22, 0x1f, 0x50, 0x45, 0x0d, 0x00, 0x20, 0x0f, 0x21, 0x13, 0x0c, + 0x04, 0x0b, 0x20, 0x1d, 0x41, 0x20, 0x71, 0x21, 0x15, 0x20, 0x0f, 0x21, + 0x13, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, 0x13, 0x20, 0x1f, + 0xa7, 0x41, 0x0f, 0x71, 0x41, 0xf0, 0x9d, 0x80, 0x80, 0x00, 0x6a, 0x2d, + 0x00, 0x00, 0x20, 0x15, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x1f, 0x42, 0x0f, + 0x56, 0x21, 0x12, 0x20, 0x1f, 0x42, 0x04, 0x88, 0x21, 0x1f, 0x20, 0x12, + 0x0d, 0x00, 0x0b, 0x20, 0x16, 0x41, 0x08, 0x71, 0x45, 0x0d, 0x03, 0x20, + 0x1d, 0x41, 0x04, 0x75, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x6a, 0x21, + 0x1e, 0x41, 0x02, 0x21, 0x1b, 0x0c, 0x03, 0x0b, 0x20, 0x0f, 0x21, 0x13, + 0x02, 0x40, 0x20, 0x05, 0x29, 0x03, 0x38, 0x22, 0x1f, 0x50, 0x0d, 0x00, + 0x20, 0x0f, 0x21, 0x13, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, + 0x13, 0x20, 0x1f, 0xa7, 0x41, 0x07, 0x71, 0x41, 0x30, 0x72, 0x3a, 0x00, + 0x00, 0x20, 0x1f, 0x42, 0x07, 0x56, 0x21, 0x12, 0x20, 0x1f, 0x42, 0x03, + 0x88, 0x21, 0x1f, 0x20, 0x12, 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x00, 0x21, + 0x1b, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, 0x16, 0x41, + 0x08, 0x71, 0x45, 0x0d, 0x02, 0x20, 0x17, 0x20, 0x0f, 0x20, 0x13, 0x6b, + 0x22, 0x12, 0x41, 0x01, 0x6a, 0x20, 0x17, 0x20, 0x12, 0x4a, 0x1b, 0x21, + 0x17, 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x20, 0x05, 0x29, 0x03, 0x38, 0x22, + 0x1f, 0x42, 0x7f, 0x55, 0x0d, 0x00, 0x20, 0x05, 0x42, 0x00, 0x20, 0x1f, + 0x7d, 0x22, 0x1f, 0x37, 0x03, 0x38, 0x41, 0x01, 0x21, 0x1b, 0x41, 0x80, + 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, + 0x16, 0x41, 0x80, 0x10, 0x71, 0x45, 0x0d, 0x00, 0x41, 0x01, 0x21, 0x1b, + 0x41, 0x81, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x0c, 0x01, 0x0b, 0x41, + 0x82, 0x88, 0x80, 0x80, 0x00, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, + 0x16, 0x41, 0x01, 0x71, 0x22, 0x1b, 0x1b, 0x21, 0x1e, 0x0b, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x1f, 0x42, 0x80, 0x80, 0x80, 0x80, 0x10, 0x5a, 0x0d, + 0x00, 0x20, 0x1f, 0x21, 0x20, 0x20, 0x0f, 0x21, 0x13, 0x0c, 0x01, 0x0b, + 0x20, 0x0f, 0x21, 0x13, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, + 0x13, 0x20, 0x1f, 0x20, 0x1f, 0x42, 0x0a, 0x80, 0x22, 0x20, 0x42, 0x0a, + 0x7e, 0x7d, 0xa7, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x1f, 0x42, + 0xff, 0xff, 0xff, 0xff, 0x9f, 0x01, 0x56, 0x21, 0x12, 0x20, 0x20, 0x21, + 0x1f, 0x20, 0x12, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x20, 0xa7, 0x22, 0x12, + 0x45, 0x0d, 0x00, 0x03, 0x40, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, 0x13, + 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x15, 0x41, 0x0a, 0x6c, + 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x12, 0x41, 0x09, 0x4b, + 0x21, 0x18, 0x20, 0x15, 0x21, 0x12, 0x20, 0x18, 0x0d, 0x00, 0x0b, 0x0b, + 0x02, 0x40, 0x20, 0x1a, 0x45, 0x0d, 0x00, 0x20, 0x17, 0x41, 0x00, 0x48, + 0x0d, 0x12, 0x0b, 0x20, 0x16, 0x41, 0xff, 0xff, 0x7b, 0x71, 0x20, 0x16, + 0x20, 0x1a, 0x1b, 0x21, 0x1c, 0x02, 0x40, 0x20, 0x05, 0x29, 0x03, 0x38, + 0x22, 0x1f, 0x42, 0x00, 0x52, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x18, 0x20, + 0x17, 0x0d, 0x00, 0x20, 0x0f, 0x21, 0x13, 0x20, 0x0f, 0x21, 0x12, 0x0c, + 0x0c, 0x0b, 0x20, 0x17, 0x20, 0x0f, 0x20, 0x13, 0x6b, 0x20, 0x1f, 0x50, + 0x6a, 0x22, 0x12, 0x20, 0x17, 0x20, 0x12, 0x4a, 0x1b, 0x21, 0x18, 0x20, + 0x0f, 0x21, 0x12, 0x0c, 0x0b, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x29, 0x03, + 0x38, 0x3c, 0x00, 0x37, 0x41, 0x00, 0x21, 0x1b, 0x41, 0x80, 0x88, 0x80, + 0x80, 0x00, 0x21, 0x1e, 0x41, 0x01, 0x21, 0x18, 0x20, 0x09, 0x21, 0x13, + 0x20, 0x0f, 0x21, 0x12, 0x0c, 0x0a, 0x0b, 0x41, 0x80, 0xa8, 0x80, 0x80, + 0x00, 0x28, 0x02, 0x00, 0x10, 0xb5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x13, + 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x22, 0x12, 0x41, 0xab, + 0x89, 0x80, 0x80, 0x00, 0x20, 0x12, 0x1b, 0x21, 0x13, 0x0b, 0x20, 0x13, + 0x20, 0x13, 0x20, 0x17, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x20, 0x17, + 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x49, 0x1b, 0x10, 0xc3, 0x80, 0x80, + 0x80, 0x00, 0x22, 0x18, 0x6a, 0x21, 0x12, 0x41, 0x00, 0x21, 0x1b, 0x41, + 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, 0x17, 0x41, 0x7f, 0x4a, + 0x0d, 0x07, 0x20, 0x12, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x07, 0x0c, 0x0d, + 0x0b, 0x20, 0x05, 0x28, 0x02, 0x38, 0x21, 0x13, 0x20, 0x17, 0x0d, 0x01, + 0x41, 0x00, 0x21, 0x12, 0x0c, 0x02, 0x0b, 0x20, 0x05, 0x41, 0x00, 0x36, + 0x02, 0x0c, 0x20, 0x05, 0x20, 0x05, 0x29, 0x03, 0x38, 0x3e, 0x02, 0x08, + 0x20, 0x05, 0x20, 0x05, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x38, 0x20, 0x05, + 0x41, 0x08, 0x6a, 0x21, 0x13, 0x41, 0x7f, 0x21, 0x17, 0x0b, 0x41, 0x00, + 0x21, 0x12, 0x20, 0x13, 0x21, 0x14, 0x02, 0x40, 0x03, 0x40, 0x20, 0x14, + 0x28, 0x02, 0x00, 0x22, 0x15, 0x45, 0x0d, 0x01, 0x02, 0x40, 0x20, 0x05, + 0x41, 0x04, 0x6a, 0x20, 0x15, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x15, 0x41, 0x00, 0x48, 0x22, 0x18, 0x0d, 0x00, 0x20, 0x15, 0x20, 0x17, + 0x20, 0x12, 0x6b, 0x4b, 0x0d, 0x00, 0x20, 0x14, 0x41, 0x04, 0x6a, 0x21, + 0x14, 0x20, 0x15, 0x20, 0x12, 0x6a, 0x22, 0x12, 0x20, 0x17, 0x49, 0x0d, + 0x01, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x18, 0x0d, 0x0c, 0x0b, 0x20, 0x12, + 0x41, 0x00, 0x48, 0x0d, 0x0a, 0x0b, 0x02, 0x40, 0x20, 0x16, 0x41, 0x80, + 0xc0, 0x04, 0x71, 0x22, 0x18, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x12, 0x4c, + 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, 0x20, 0x19, + 0x20, 0x12, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, + 0x02, 0x49, 0x22, 0x15, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x02, 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, + 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, + 0x14, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, + 0x40, 0x20, 0x12, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x14, 0x03, 0x40, + 0x20, 0x13, 0x28, 0x02, 0x00, 0x22, 0x15, 0x45, 0x0d, 0x01, 0x20, 0x05, + 0x41, 0x04, 0x6a, 0x20, 0x15, 0x10, 0xb7, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x15, 0x20, 0x14, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x4b, 0x0d, 0x01, 0x02, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0x04, 0x6a, 0x20, 0x15, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x13, 0x41, 0x04, 0x6a, 0x21, 0x13, 0x20, + 0x14, 0x20, 0x12, 0x49, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x18, + 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x12, 0x4c, + 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, 0x20, 0x19, + 0x20, 0x12, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, + 0x02, 0x49, 0x22, 0x15, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x02, 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, + 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, + 0x14, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x19, 0x20, 0x12, 0x20, 0x19, 0x20, 0x12, 0x4a, 0x1b, 0x21, 0x12, 0x0c, + 0x08, 0x0b, 0x02, 0x40, 0x20, 0x1a, 0x45, 0x0d, 0x00, 0x20, 0x17, 0x41, + 0x00, 0x48, 0x0d, 0x09, 0x0b, 0x20, 0x05, 0x2b, 0x03, 0x38, 0x21, 0x21, + 0x20, 0x05, 0x41, 0x00, 0x36, 0x02, 0x6c, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x21, 0xbd, 0x42, 0x7f, 0x55, 0x0d, 0x00, 0x20, 0x21, 0x9a, 0x21, 0x21, + 0x41, 0x01, 0x21, 0x22, 0x41, 0x00, 0x21, 0x23, 0x41, 0x8a, 0x88, 0x80, + 0x80, 0x00, 0x21, 0x24, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x16, 0x41, + 0x80, 0x10, 0x71, 0x45, 0x0d, 0x00, 0x41, 0x01, 0x21, 0x22, 0x41, 0x00, + 0x21, 0x23, 0x41, 0x8d, 0x88, 0x80, 0x80, 0x00, 0x21, 0x24, 0x0c, 0x01, + 0x0b, 0x41, 0x90, 0x88, 0x80, 0x80, 0x00, 0x41, 0x8b, 0x88, 0x80, 0x80, + 0x00, 0x20, 0x16, 0x41, 0x01, 0x71, 0x22, 0x22, 0x1b, 0x21, 0x24, 0x20, + 0x22, 0x45, 0x21, 0x23, 0x0b, 0x02, 0x40, 0x20, 0x21, 0xbd, 0x42, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x83, 0x42, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf8, 0xff, 0x00, 0x53, 0x0d, 0x00, + 0x20, 0x22, 0x41, 0x03, 0x6a, 0x21, 0x14, 0x02, 0x40, 0x20, 0x16, 0x41, + 0x80, 0xc0, 0x00, 0x71, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x14, 0x4c, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, + 0x14, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, + 0x49, 0x22, 0x15, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, + 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, + 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, + 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, + 0x20, 0x00, 0x28, 0x02, 0x00, 0x22, 0x12, 0x41, 0x20, 0x71, 0x0d, 0x00, + 0x20, 0x24, 0x20, 0x22, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x20, 0x00, 0x28, 0x02, 0x00, 0x21, 0x12, 0x0b, 0x02, 0x40, 0x20, + 0x12, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x41, 0x83, 0x89, 0x80, 0x80, 0x00, + 0x41, 0xa1, 0x89, 0x80, 0x80, 0x00, 0x20, 0x1d, 0x41, 0x20, 0x71, 0x22, + 0x12, 0x1b, 0x41, 0x87, 0x89, 0x80, 0x80, 0x00, 0x41, 0xa5, 0x89, 0x80, + 0x80, 0x00, 0x20, 0x12, 0x1b, 0x20, 0x21, 0x20, 0x21, 0x62, 0x1b, 0x41, + 0x03, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, + 0x40, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x41, 0x80, 0xc0, 0x00, + 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x14, 0x4c, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x14, 0x6b, 0x22, + 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x15, + 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x15, + 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, + 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, + 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, + 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x20, 0x19, 0x20, + 0x14, 0x20, 0x19, 0x4a, 0x1b, 0x21, 0x12, 0x0c, 0x08, 0x0b, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x20, 0x21, 0x20, 0x05, 0x41, 0xec, 0x00, 0x6a, + 0x10, 0xb8, 0x80, 0x80, 0x80, 0x00, 0x22, 0x21, 0x20, 0x21, 0xa0, 0x22, + 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x0d, + 0x00, 0x20, 0x05, 0x20, 0x05, 0x28, 0x02, 0x6c, 0x22, 0x12, 0x41, 0x7f, + 0x6a, 0x36, 0x02, 0x6c, 0x20, 0x1d, 0x41, 0x20, 0x72, 0x22, 0x25, 0x41, + 0xe1, 0x00, 0x47, 0x0d, 0x01, 0x0c, 0x08, 0x0b, 0x20, 0x1d, 0x41, 0x20, + 0x72, 0x22, 0x25, 0x41, 0xe1, 0x00, 0x46, 0x0d, 0x07, 0x41, 0x06, 0x20, + 0x17, 0x20, 0x17, 0x41, 0x00, 0x48, 0x1b, 0x21, 0x1a, 0x20, 0x05, 0x28, + 0x02, 0x6c, 0x21, 0x13, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x20, 0x12, 0x41, + 0x63, 0x6a, 0x22, 0x13, 0x36, 0x02, 0x6c, 0x41, 0x06, 0x20, 0x17, 0x20, + 0x17, 0x41, 0x00, 0x48, 0x1b, 0x21, 0x1a, 0x20, 0x21, 0x44, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb0, 0x41, 0xa2, 0x21, 0x21, 0x0b, 0x20, 0x05, + 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x00, 0x41, 0xc8, 0x00, 0x20, 0x13, 0x41, + 0x00, 0x48, 0x22, 0x26, 0x1b, 0x41, 0x02, 0x74, 0x22, 0x27, 0x6a, 0x22, + 0x1e, 0x21, 0x14, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x21, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x41, 0x63, 0x20, 0x21, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x71, 0x45, 0x0d, + 0x00, 0x20, 0x21, 0xab, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, + 0x12, 0x0b, 0x20, 0x14, 0x20, 0x12, 0x36, 0x02, 0x00, 0x20, 0x14, 0x41, + 0x04, 0x6a, 0x21, 0x14, 0x20, 0x21, 0x20, 0x12, 0xb8, 0xa1, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x65, 0xcd, 0xcd, 0x41, 0xa2, 0x22, 0x21, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, 0x00, 0x0b, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x13, 0x41, 0x01, 0x4e, 0x0d, 0x00, 0x20, 0x14, + 0x21, 0x12, 0x20, 0x1e, 0x21, 0x15, 0x0c, 0x01, 0x0b, 0x20, 0x1e, 0x21, + 0x15, 0x03, 0x40, 0x20, 0x13, 0x41, 0x1d, 0x20, 0x13, 0x41, 0x1d, 0x48, + 0x1b, 0x21, 0x13, 0x02, 0x40, 0x20, 0x14, 0x41, 0x7c, 0x6a, 0x22, 0x12, + 0x20, 0x15, 0x49, 0x0d, 0x00, 0x20, 0x13, 0xad, 0x21, 0x20, 0x42, 0x00, + 0x21, 0x1f, 0x03, 0x40, 0x20, 0x12, 0x20, 0x12, 0x35, 0x02, 0x00, 0x20, + 0x20, 0x86, 0x20, 0x1f, 0x42, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x83, 0x7c, + 0x22, 0x1f, 0x20, 0x1f, 0x42, 0x80, 0x94, 0xeb, 0xdc, 0x03, 0x80, 0x22, + 0x1f, 0x42, 0x80, 0x94, 0xeb, 0xdc, 0x03, 0x7e, 0x7d, 0x3e, 0x02, 0x00, + 0x20, 0x12, 0x41, 0x7c, 0x6a, 0x22, 0x12, 0x20, 0x15, 0x4f, 0x0d, 0x00, + 0x0b, 0x20, 0x1f, 0xa7, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x20, 0x15, 0x41, + 0x7c, 0x6a, 0x22, 0x15, 0x20, 0x12, 0x36, 0x02, 0x00, 0x0b, 0x02, 0x40, + 0x03, 0x40, 0x20, 0x14, 0x22, 0x12, 0x20, 0x15, 0x4d, 0x0d, 0x01, 0x20, + 0x12, 0x41, 0x7c, 0x6a, 0x22, 0x14, 0x28, 0x02, 0x00, 0x45, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x05, 0x20, 0x05, 0x28, 0x02, 0x6c, 0x20, 0x13, 0x6b, + 0x22, 0x13, 0x36, 0x02, 0x6c, 0x20, 0x12, 0x21, 0x14, 0x20, 0x13, 0x41, + 0x00, 0x4a, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x13, 0x41, 0x7f, + 0x4a, 0x0d, 0x00, 0x20, 0x1a, 0x41, 0x19, 0x6a, 0x41, 0x09, 0x6e, 0x41, + 0x01, 0x6a, 0x21, 0x28, 0x03, 0x40, 0x41, 0x00, 0x20, 0x13, 0x6b, 0x22, + 0x14, 0x41, 0x09, 0x20, 0x14, 0x41, 0x09, 0x48, 0x1b, 0x21, 0x17, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x15, 0x20, 0x12, 0x49, 0x0d, 0x00, 0x20, 0x15, + 0x28, 0x02, 0x00, 0x21, 0x14, 0x0c, 0x01, 0x0b, 0x41, 0x80, 0x94, 0xeb, + 0xdc, 0x03, 0x20, 0x17, 0x76, 0x21, 0x1c, 0x41, 0x7f, 0x20, 0x17, 0x74, + 0x41, 0x7f, 0x73, 0x21, 0x1b, 0x41, 0x00, 0x21, 0x13, 0x20, 0x15, 0x21, + 0x14, 0x03, 0x40, 0x20, 0x14, 0x20, 0x14, 0x28, 0x02, 0x00, 0x22, 0x18, + 0x20, 0x17, 0x76, 0x20, 0x13, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x18, 0x20, + 0x1b, 0x71, 0x20, 0x1c, 0x6c, 0x21, 0x13, 0x20, 0x14, 0x41, 0x04, 0x6a, + 0x22, 0x14, 0x20, 0x12, 0x49, 0x0d, 0x00, 0x0b, 0x20, 0x15, 0x28, 0x02, + 0x00, 0x21, 0x14, 0x20, 0x13, 0x45, 0x0d, 0x00, 0x20, 0x12, 0x20, 0x13, + 0x36, 0x02, 0x00, 0x20, 0x12, 0x41, 0x04, 0x6a, 0x21, 0x12, 0x0b, 0x20, + 0x05, 0x20, 0x05, 0x28, 0x02, 0x6c, 0x20, 0x17, 0x6a, 0x22, 0x13, 0x36, + 0x02, 0x6c, 0x20, 0x1e, 0x20, 0x15, 0x20, 0x14, 0x45, 0x41, 0x02, 0x74, + 0x6a, 0x22, 0x15, 0x20, 0x25, 0x41, 0xe6, 0x00, 0x46, 0x1b, 0x22, 0x14, + 0x20, 0x28, 0x41, 0x02, 0x74, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x20, 0x14, + 0x6b, 0x41, 0x02, 0x75, 0x20, 0x28, 0x4a, 0x1b, 0x21, 0x12, 0x20, 0x13, + 0x41, 0x00, 0x48, 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x00, 0x21, 0x18, 0x02, + 0x40, 0x20, 0x15, 0x20, 0x12, 0x4f, 0x0d, 0x00, 0x20, 0x1e, 0x20, 0x15, + 0x6b, 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x21, 0x18, 0x20, 0x15, 0x28, + 0x02, 0x00, 0x22, 0x13, 0x41, 0x0a, 0x49, 0x0d, 0x00, 0x41, 0x0a, 0x21, + 0x14, 0x03, 0x40, 0x20, 0x18, 0x41, 0x01, 0x6a, 0x21, 0x18, 0x20, 0x13, + 0x20, 0x14, 0x41, 0x0a, 0x6c, 0x22, 0x14, 0x4f, 0x0d, 0x00, 0x0b, 0x0b, + 0x02, 0x40, 0x20, 0x1a, 0x41, 0x00, 0x20, 0x18, 0x20, 0x25, 0x41, 0xe6, + 0x00, 0x46, 0x1b, 0x6b, 0x20, 0x1a, 0x41, 0x00, 0x47, 0x20, 0x25, 0x41, + 0xe7, 0x00, 0x46, 0x22, 0x1b, 0x71, 0x6b, 0x22, 0x14, 0x20, 0x12, 0x20, + 0x1e, 0x6b, 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x41, 0x77, 0x6a, 0x4e, + 0x0d, 0x00, 0x20, 0x14, 0x41, 0x80, 0xc8, 0x00, 0x6a, 0x22, 0x13, 0x41, + 0x09, 0x6d, 0x22, 0x17, 0x41, 0x02, 0x74, 0x22, 0x29, 0x20, 0x05, 0x41, + 0xf0, 0x00, 0x6a, 0x41, 0x01, 0x41, 0xc9, 0x00, 0x20, 0x26, 0x1b, 0x41, + 0x02, 0x74, 0x22, 0x26, 0x6a, 0x6a, 0x41, 0x80, 0x60, 0x6a, 0x21, 0x1c, + 0x41, 0x0a, 0x21, 0x14, 0x02, 0x40, 0x20, 0x13, 0x20, 0x17, 0x41, 0x09, + 0x6c, 0x6b, 0x22, 0x17, 0x41, 0x07, 0x4a, 0x0d, 0x00, 0x41, 0x08, 0x20, + 0x17, 0x6b, 0x22, 0x28, 0x41, 0x07, 0x71, 0x21, 0x13, 0x41, 0x0a, 0x21, + 0x14, 0x02, 0x40, 0x20, 0x17, 0x41, 0x7f, 0x6a, 0x41, 0x07, 0x49, 0x0d, + 0x00, 0x20, 0x28, 0x41, 0x78, 0x71, 0x21, 0x17, 0x41, 0x0a, 0x21, 0x14, + 0x03, 0x40, 0x20, 0x14, 0x41, 0x80, 0xc2, 0xd7, 0x2f, 0x6c, 0x21, 0x14, + 0x20, 0x17, 0x41, 0x78, 0x6a, 0x22, 0x17, 0x0d, 0x00, 0x0b, 0x0b, 0x20, + 0x13, 0x45, 0x0d, 0x00, 0x03, 0x40, 0x20, 0x14, 0x41, 0x0a, 0x6c, 0x21, + 0x14, 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x22, 0x13, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x1c, 0x41, 0x04, 0x6a, 0x21, 0x28, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x1c, 0x28, 0x02, 0x00, 0x22, 0x13, 0x20, 0x13, 0x20, 0x14, 0x6e, 0x22, + 0x25, 0x20, 0x14, 0x6c, 0x6b, 0x22, 0x17, 0x0d, 0x00, 0x20, 0x28, 0x20, + 0x12, 0x46, 0x0d, 0x01, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x25, 0x41, + 0x01, 0x71, 0x0d, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x43, 0x21, 0x21, 0x20, 0x14, 0x41, 0x80, 0x94, 0xeb, 0xdc, 0x03, 0x47, + 0x0d, 0x01, 0x20, 0x1c, 0x20, 0x15, 0x4d, 0x0d, 0x01, 0x20, 0x1c, 0x41, + 0x7c, 0x6a, 0x2d, 0x00, 0x00, 0x41, 0x01, 0x71, 0x45, 0x0d, 0x01, 0x0b, + 0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x43, 0x21, 0x21, 0x0b, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x3f, 0x44, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0x3f, 0x20, 0x28, 0x20, 0x12, 0x46, 0x1b, 0x44, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0x20, 0x17, 0x20, 0x14, 0x41, 0x01, + 0x76, 0x22, 0x28, 0x46, 0x1b, 0x20, 0x17, 0x20, 0x28, 0x49, 0x1b, 0x21, + 0x2a, 0x02, 0x40, 0x20, 0x23, 0x0d, 0x00, 0x20, 0x24, 0x2d, 0x00, 0x00, + 0x41, 0x2d, 0x47, 0x0d, 0x00, 0x20, 0x2a, 0x9a, 0x21, 0x2a, 0x20, 0x21, + 0x9a, 0x21, 0x21, 0x0b, 0x20, 0x1c, 0x20, 0x13, 0x20, 0x17, 0x6b, 0x22, + 0x13, 0x36, 0x02, 0x00, 0x20, 0x21, 0x20, 0x2a, 0xa0, 0x20, 0x21, 0x61, + 0x0d, 0x00, 0x20, 0x1c, 0x20, 0x13, 0x20, 0x14, 0x6a, 0x22, 0x14, 0x36, + 0x02, 0x00, 0x02, 0x40, 0x20, 0x14, 0x41, 0x80, 0x94, 0xeb, 0xdc, 0x03, + 0x49, 0x0d, 0x00, 0x20, 0x08, 0x20, 0x26, 0x20, 0x29, 0x6a, 0x6a, 0x21, + 0x14, 0x03, 0x40, 0x20, 0x14, 0x41, 0x04, 0x6a, 0x41, 0x00, 0x36, 0x02, + 0x00, 0x02, 0x40, 0x20, 0x14, 0x20, 0x15, 0x4f, 0x0d, 0x00, 0x20, 0x15, + 0x41, 0x7c, 0x6a, 0x22, 0x15, 0x41, 0x00, 0x36, 0x02, 0x00, 0x0b, 0x20, + 0x14, 0x20, 0x14, 0x28, 0x02, 0x00, 0x41, 0x01, 0x6a, 0x22, 0x13, 0x36, + 0x02, 0x00, 0x20, 0x14, 0x41, 0x7c, 0x6a, 0x21, 0x14, 0x20, 0x13, 0x41, + 0xff, 0x93, 0xeb, 0xdc, 0x03, 0x4b, 0x0d, 0x00, 0x0b, 0x20, 0x14, 0x41, + 0x04, 0x6a, 0x21, 0x1c, 0x0b, 0x20, 0x1e, 0x20, 0x15, 0x6b, 0x41, 0x02, + 0x75, 0x41, 0x09, 0x6c, 0x21, 0x18, 0x20, 0x15, 0x28, 0x02, 0x00, 0x22, + 0x13, 0x41, 0x0a, 0x49, 0x0d, 0x00, 0x41, 0x0a, 0x21, 0x14, 0x03, 0x40, + 0x20, 0x18, 0x41, 0x01, 0x6a, 0x21, 0x18, 0x20, 0x13, 0x20, 0x14, 0x41, + 0x0a, 0x6c, 0x22, 0x14, 0x4f, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x1c, 0x41, + 0x04, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x20, 0x12, 0x20, 0x14, 0x4b, 0x1b, + 0x21, 0x12, 0x0b, 0x20, 0x07, 0x20, 0x12, 0x6a, 0x20, 0x27, 0x6b, 0x21, + 0x14, 0x02, 0x40, 0x03, 0x40, 0x20, 0x14, 0x21, 0x13, 0x20, 0x12, 0x22, + 0x1c, 0x20, 0x15, 0x4d, 0x22, 0x17, 0x0d, 0x01, 0x20, 0x13, 0x41, 0x7c, + 0x6a, 0x21, 0x14, 0x20, 0x1c, 0x41, 0x7c, 0x6a, 0x22, 0x12, 0x28, 0x02, + 0x00, 0x45, 0x0d, 0x00, 0x0b, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x1b, + 0x0d, 0x00, 0x20, 0x16, 0x41, 0x08, 0x71, 0x21, 0x28, 0x0c, 0x01, 0x0b, + 0x20, 0x18, 0x41, 0x7f, 0x73, 0x41, 0x7f, 0x20, 0x1a, 0x41, 0x01, 0x20, + 0x1a, 0x1b, 0x22, 0x12, 0x20, 0x18, 0x4a, 0x20, 0x18, 0x41, 0x7b, 0x4a, + 0x71, 0x22, 0x14, 0x1b, 0x20, 0x12, 0x6a, 0x21, 0x1a, 0x41, 0x7f, 0x41, + 0x7e, 0x20, 0x14, 0x1b, 0x20, 0x1d, 0x6a, 0x21, 0x1d, 0x20, 0x16, 0x41, + 0x08, 0x71, 0x22, 0x28, 0x0d, 0x00, 0x41, 0x77, 0x21, 0x12, 0x02, 0x40, + 0x20, 0x17, 0x0d, 0x00, 0x20, 0x1c, 0x41, 0x7c, 0x6a, 0x28, 0x02, 0x00, + 0x22, 0x17, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x12, 0x20, 0x17, 0x41, + 0x0a, 0x70, 0x0d, 0x00, 0x41, 0x0a, 0x21, 0x14, 0x41, 0x00, 0x21, 0x12, + 0x03, 0x40, 0x20, 0x12, 0x41, 0x7f, 0x6a, 0x21, 0x12, 0x20, 0x17, 0x20, + 0x14, 0x41, 0x0a, 0x6c, 0x22, 0x14, 0x70, 0x45, 0x0d, 0x00, 0x0b, 0x0b, + 0x20, 0x13, 0x41, 0x02, 0x75, 0x41, 0x09, 0x6c, 0x21, 0x14, 0x02, 0x40, + 0x20, 0x1d, 0x41, 0x5f, 0x71, 0x41, 0xc6, 0x00, 0x47, 0x0d, 0x00, 0x41, + 0x00, 0x21, 0x28, 0x20, 0x1a, 0x20, 0x14, 0x20, 0x12, 0x6a, 0x41, 0x77, + 0x6a, 0x22, 0x12, 0x41, 0x00, 0x20, 0x12, 0x41, 0x00, 0x4a, 0x1b, 0x22, + 0x12, 0x20, 0x1a, 0x20, 0x12, 0x48, 0x1b, 0x21, 0x1a, 0x0c, 0x01, 0x0b, + 0x41, 0x00, 0x21, 0x28, 0x20, 0x1a, 0x20, 0x18, 0x20, 0x14, 0x6a, 0x20, + 0x12, 0x6a, 0x41, 0x77, 0x6a, 0x22, 0x12, 0x41, 0x00, 0x20, 0x12, 0x41, + 0x00, 0x4a, 0x1b, 0x22, 0x12, 0x20, 0x1a, 0x20, 0x12, 0x48, 0x1b, 0x21, + 0x1a, 0x0b, 0x20, 0x1a, 0x41, 0xfd, 0xff, 0xff, 0xff, 0x07, 0x41, 0xfe, + 0xff, 0xff, 0xff, 0x07, 0x20, 0x1a, 0x20, 0x28, 0x72, 0x22, 0x23, 0x1b, + 0x4a, 0x0d, 0x08, 0x20, 0x1a, 0x20, 0x23, 0x41, 0x00, 0x47, 0x6a, 0x41, + 0x01, 0x6a, 0x21, 0x25, 0x02, 0x40, 0x02, 0x40, 0x20, 0x1d, 0x41, 0x5f, + 0x71, 0x41, 0xc6, 0x00, 0x47, 0x22, 0x26, 0x0d, 0x00, 0x20, 0x18, 0x20, + 0x25, 0x41, 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x0a, 0x20, + 0x18, 0x41, 0x00, 0x20, 0x18, 0x41, 0x00, 0x4a, 0x1b, 0x21, 0x12, 0x0c, + 0x01, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x18, 0x0d, 0x00, 0x20, 0x06, + 0x21, 0x13, 0x20, 0x06, 0x21, 0x14, 0x0c, 0x01, 0x0b, 0x20, 0x18, 0x20, + 0x18, 0x41, 0x1f, 0x75, 0x22, 0x12, 0x73, 0x20, 0x12, 0x6b, 0x21, 0x12, + 0x20, 0x06, 0x21, 0x13, 0x20, 0x06, 0x21, 0x14, 0x03, 0x40, 0x20, 0x14, + 0x41, 0x7f, 0x6a, 0x22, 0x14, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, + 0x22, 0x17, 0x41, 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, + 0x20, 0x13, 0x41, 0x7f, 0x6a, 0x21, 0x13, 0x20, 0x12, 0x41, 0x09, 0x4b, + 0x21, 0x1b, 0x20, 0x17, 0x21, 0x12, 0x20, 0x1b, 0x0d, 0x00, 0x0b, 0x0b, + 0x02, 0x40, 0x20, 0x06, 0x20, 0x13, 0x6b, 0x41, 0x01, 0x4a, 0x0d, 0x00, + 0x20, 0x14, 0x20, 0x0e, 0x20, 0x13, 0x6b, 0x6a, 0x22, 0x14, 0x41, 0x30, + 0x20, 0x13, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x6b, 0x41, 0x76, 0x6a, + 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x7e, + 0x6a, 0x22, 0x27, 0x20, 0x1d, 0x3a, 0x00, 0x00, 0x20, 0x14, 0x41, 0x7f, + 0x6a, 0x41, 0x2d, 0x41, 0x2b, 0x20, 0x18, 0x41, 0x00, 0x48, 0x1b, 0x3a, + 0x00, 0x00, 0x20, 0x06, 0x20, 0x27, 0x6b, 0x22, 0x12, 0x20, 0x25, 0x41, + 0xff, 0xff, 0xff, 0xff, 0x07, 0x73, 0x4a, 0x0d, 0x09, 0x0b, 0x20, 0x12, + 0x20, 0x25, 0x6a, 0x22, 0x12, 0x20, 0x22, 0x41, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x73, 0x4a, 0x0d, 0x08, 0x20, 0x12, 0x20, 0x22, 0x6a, 0x21, 0x1b, + 0x02, 0x40, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x22, 0x16, 0x0d, + 0x00, 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, + 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, + 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, + 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, + 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, + 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, + 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, + 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb1, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x24, 0x20, 0x22, 0x20, 0x00, 0x10, + 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x16, 0x41, + 0x80, 0x80, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x30, 0x20, 0x19, 0x20, + 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, + 0x49, 0x22, 0x14, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, + 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, + 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, + 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x26, + 0x0d, 0x03, 0x20, 0x1e, 0x20, 0x15, 0x20, 0x15, 0x20, 0x1e, 0x4b, 0x1b, + 0x22, 0x18, 0x21, 0x17, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x17, 0x28, 0x02, 0x00, 0x22, 0x12, 0x45, 0x0d, 0x00, + 0x41, 0x08, 0x21, 0x14, 0x03, 0x40, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x20, 0x14, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x15, + 0x41, 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x14, + 0x41, 0x7f, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, 0x13, + 0x20, 0x15, 0x21, 0x12, 0x20, 0x13, 0x0d, 0x00, 0x0b, 0x20, 0x14, 0x41, + 0x01, 0x6a, 0x22, 0x15, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6a, 0x21, + 0x12, 0x02, 0x40, 0x20, 0x17, 0x20, 0x18, 0x46, 0x0d, 0x00, 0x20, 0x14, + 0x41, 0x02, 0x6a, 0x41, 0x02, 0x48, 0x0d, 0x04, 0x0c, 0x03, 0x0b, 0x20, + 0x14, 0x41, 0x08, 0x47, 0x0d, 0x03, 0x0c, 0x01, 0x0b, 0x41, 0x09, 0x21, + 0x15, 0x20, 0x17, 0x20, 0x18, 0x47, 0x0d, 0x01, 0x0b, 0x20, 0x05, 0x41, + 0x30, 0x3a, 0x00, 0x58, 0x20, 0x0c, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x20, + 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x0b, 0x20, 0x15, 0x6a, 0x22, 0x12, + 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x12, 0x49, 0x1b, 0x22, 0x12, + 0x41, 0x30, 0x20, 0x15, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6a, 0x20, + 0x12, 0x6b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x12, + 0x20, 0x0d, 0x20, 0x12, 0x6b, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x17, 0x41, 0x04, 0x6a, 0x22, 0x17, 0x20, 0x1e, + 0x4d, 0x0d, 0x00, 0x0b, 0x02, 0x40, 0x20, 0x23, 0x45, 0x0d, 0x00, 0x20, + 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x41, 0xa9, 0x89, + 0x80, 0x80, 0x00, 0x41, 0x01, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x1a, 0x41, 0x01, 0x4e, + 0x0d, 0x00, 0x20, 0x1a, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, + 0x17, 0x20, 0x1c, 0x49, 0x0d, 0x00, 0x20, 0x1a, 0x21, 0x12, 0x0c, 0x01, + 0x0b, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x17, 0x28, + 0x02, 0x00, 0x22, 0x12, 0x0d, 0x00, 0x20, 0x0d, 0x21, 0x14, 0x20, 0x0d, + 0x21, 0x15, 0x0c, 0x01, 0x0b, 0x20, 0x0d, 0x21, 0x15, 0x20, 0x0d, 0x21, + 0x14, 0x03, 0x40, 0x20, 0x14, 0x41, 0x7f, 0x6a, 0x22, 0x14, 0x20, 0x12, + 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x13, 0x41, 0x0a, 0x6c, 0x6b, 0x41, + 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x15, 0x41, 0x7f, 0x6a, 0x21, 0x15, + 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, 0x18, 0x20, 0x13, 0x21, 0x12, 0x20, + 0x18, 0x0d, 0x00, 0x0b, 0x20, 0x14, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x4d, 0x0d, 0x01, 0x0b, 0x20, 0x14, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, + 0x6a, 0x20, 0x15, 0x6b, 0x22, 0x14, 0x41, 0x30, 0x20, 0x15, 0x20, 0x05, + 0x41, 0xd0, 0x00, 0x6a, 0x6b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x14, 0x20, 0x1a, 0x41, 0x09, 0x20, 0x1a, 0x41, 0x09, 0x48, + 0x1b, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x1a, 0x41, 0x77, 0x6a, 0x21, 0x12, 0x20, 0x17, 0x41, 0x04, 0x6a, 0x22, + 0x17, 0x20, 0x1c, 0x4f, 0x0d, 0x01, 0x20, 0x1a, 0x41, 0x09, 0x4a, 0x21, + 0x14, 0x20, 0x12, 0x21, 0x1a, 0x20, 0x14, 0x0d, 0x00, 0x0b, 0x0b, 0x20, + 0x00, 0x41, 0x30, 0x20, 0x12, 0x41, 0x09, 0x6a, 0x41, 0x09, 0x41, 0x00, + 0x10, 0xbd, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x04, 0x0b, 0x41, 0x80, 0xa8, + 0x80, 0x80, 0x00, 0x41, 0x1c, 0x36, 0x02, 0x00, 0x0c, 0x08, 0x0b, 0x41, + 0x00, 0x21, 0x1b, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1e, 0x20, + 0x0f, 0x21, 0x12, 0x20, 0x16, 0x21, 0x1c, 0x20, 0x17, 0x21, 0x18, 0x0b, + 0x20, 0x18, 0x20, 0x12, 0x20, 0x13, 0x6b, 0x22, 0x17, 0x20, 0x18, 0x20, + 0x17, 0x4a, 0x1b, 0x22, 0x1a, 0x20, 0x1b, 0x41, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x73, 0x4a, 0x0d, 0x05, 0x20, 0x19, 0x20, 0x1b, 0x20, 0x1a, 0x6a, + 0x22, 0x15, 0x20, 0x19, 0x20, 0x15, 0x4a, 0x1b, 0x22, 0x12, 0x20, 0x14, + 0x4a, 0x0d, 0x05, 0x02, 0x40, 0x20, 0x1c, 0x41, 0x80, 0xc0, 0x04, 0x71, + 0x22, 0x1c, 0x0d, 0x00, 0x20, 0x15, 0x20, 0x19, 0x4e, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, 0x20, 0x12, 0x20, 0x15, 0x6b, + 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, 0x02, 0x49, 0x22, + 0x16, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, + 0x16, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, + 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, + 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, 0xff, 0x01, 0x4b, + 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, + 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x14, 0x20, 0x00, + 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x1e, 0x20, 0x1b, + 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, + 0x20, 0x1c, 0x41, 0x80, 0x80, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x15, 0x20, + 0x19, 0x4e, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x30, + 0x20, 0x12, 0x20, 0x15, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, + 0x41, 0x80, 0x02, 0x49, 0x22, 0x1b, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x02, 0x40, 0x20, 0x1b, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, + 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, + 0x14, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, + 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x02, 0x40, 0x20, 0x17, 0x20, 0x18, 0x4e, 0x0d, 0x00, 0x20, 0x05, + 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x30, 0x20, 0x1a, 0x20, 0x17, 0x6b, 0x22, + 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, 0x02, 0x49, 0x22, 0x18, + 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x18, + 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x80, + 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, 0xff, 0x01, 0x4b, 0x0d, + 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, + 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x13, 0x20, 0x17, 0x20, + 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x1c, 0x41, + 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x04, 0x20, 0x15, 0x20, 0x19, 0x4e, 0x0d, + 0x04, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x41, 0x20, 0x20, 0x12, 0x20, + 0x15, 0x6b, 0x22, 0x14, 0x41, 0x80, 0x02, 0x20, 0x14, 0x41, 0x80, 0x02, + 0x49, 0x22, 0x15, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, + 0x40, 0x20, 0x15, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x00, + 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0b, 0x20, 0x14, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x14, 0x41, 0xff, + 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, + 0x20, 0x71, 0x0d, 0x04, 0x20, 0x05, 0x41, 0xf0, 0x00, 0x6a, 0x20, 0x14, + 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0c, 0x04, 0x0b, + 0x02, 0x40, 0x20, 0x1a, 0x41, 0x00, 0x48, 0x0d, 0x00, 0x20, 0x1c, 0x20, + 0x15, 0x41, 0x04, 0x6a, 0x20, 0x1c, 0x20, 0x15, 0x4b, 0x1b, 0x21, 0x1c, + 0x20, 0x15, 0x21, 0x17, 0x03, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x17, + 0x28, 0x02, 0x00, 0x22, 0x12, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x14, + 0x03, 0x40, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x14, 0x6a, 0x41, + 0x08, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x13, 0x41, + 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x14, 0x41, + 0x7f, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, 0x18, 0x20, + 0x13, 0x21, 0x12, 0x20, 0x18, 0x0d, 0x00, 0x0b, 0x20, 0x14, 0x45, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x14, 0x6a, 0x41, 0x09, + 0x6a, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x41, 0x30, 0x3a, 0x00, + 0x58, 0x20, 0x0c, 0x21, 0x12, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x17, + 0x20, 0x15, 0x46, 0x0d, 0x00, 0x20, 0x12, 0x20, 0x05, 0x41, 0xd0, 0x00, + 0x6a, 0x4d, 0x0d, 0x01, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x41, 0x30, + 0x20, 0x12, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6b, 0x10, 0xc0, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x21, 0x12, + 0x0c, 0x01, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x12, 0x41, 0x01, 0x20, 0x00, 0x10, 0xb1, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x01, 0x6a, 0x21, 0x12, + 0x02, 0x40, 0x20, 0x28, 0x0d, 0x00, 0x20, 0x1a, 0x41, 0x01, 0x48, 0x0d, + 0x01, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, + 0x41, 0xa9, 0x89, 0x80, 0x80, 0x00, 0x41, 0x01, 0x20, 0x00, 0x10, 0xb1, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x0d, 0x20, 0x12, 0x6b, 0x21, + 0x14, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, + 0x00, 0x20, 0x12, 0x20, 0x14, 0x20, 0x1a, 0x20, 0x14, 0x20, 0x1a, 0x48, + 0x1b, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, + 0x1a, 0x20, 0x14, 0x6b, 0x21, 0x1a, 0x20, 0x17, 0x41, 0x04, 0x6a, 0x22, + 0x17, 0x20, 0x1c, 0x4f, 0x0d, 0x01, 0x20, 0x1a, 0x41, 0x7f, 0x4a, 0x0d, + 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x41, 0x30, 0x20, 0x1a, 0x41, 0x12, 0x6a, + 0x41, 0x12, 0x41, 0x00, 0x10, 0xbd, 0x80, 0x80, 0x80, 0x00, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x27, 0x20, 0x06, + 0x20, 0x27, 0x6b, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x01, 0x20, 0x19, + 0x20, 0x1b, 0x4c, 0x0d, 0x01, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, + 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, + 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xc0, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, + 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x01, 0x20, 0x05, 0x41, 0xf0, + 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0c, 0x01, 0x0b, 0x20, 0x24, 0x20, 0x1d, 0x41, 0x1a, 0x74, 0x41, + 0x1f, 0x75, 0x41, 0x09, 0x71, 0x6a, 0x21, 0x1e, 0x02, 0x40, 0x20, 0x17, + 0x41, 0x0b, 0x4b, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x41, 0x0c, 0x20, + 0x17, 0x6b, 0x22, 0x12, 0x41, 0x07, 0x71, 0x22, 0x14, 0x0d, 0x00, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0x21, 0x2a, 0x0c, 0x01, + 0x0b, 0x20, 0x17, 0x41, 0x74, 0x6a, 0x21, 0x12, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x40, 0x21, 0x2a, 0x03, 0x40, 0x20, 0x12, 0x41, + 0x01, 0x6a, 0x21, 0x12, 0x20, 0x2a, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x40, 0xa2, 0x21, 0x2a, 0x20, 0x14, 0x41, 0x7f, 0x6a, 0x22, + 0x14, 0x0d, 0x00, 0x0b, 0x41, 0x00, 0x20, 0x12, 0x6b, 0x21, 0x12, 0x0b, + 0x02, 0x40, 0x20, 0x17, 0x41, 0x7b, 0x6a, 0x41, 0x07, 0x49, 0x0d, 0x00, + 0x03, 0x40, 0x20, 0x2a, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x40, 0xa2, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40, 0xa2, + 0x21, 0x2a, 0x20, 0x12, 0x41, 0x78, 0x6a, 0x22, 0x12, 0x0d, 0x00, 0x0b, + 0x0b, 0x02, 0x40, 0x20, 0x1e, 0x2d, 0x00, 0x00, 0x41, 0x2d, 0x47, 0x0d, + 0x00, 0x20, 0x2a, 0x20, 0x21, 0x9a, 0x20, 0x2a, 0xa1, 0xa0, 0x9a, 0x21, + 0x21, 0x0c, 0x01, 0x0b, 0x20, 0x21, 0x20, 0x2a, 0xa0, 0x20, 0x2a, 0xa1, + 0x21, 0x21, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x05, 0x28, 0x02, 0x6c, + 0x22, 0x18, 0x45, 0x0d, 0x00, 0x20, 0x18, 0x20, 0x18, 0x41, 0x1f, 0x75, + 0x22, 0x12, 0x73, 0x20, 0x12, 0x6b, 0x21, 0x12, 0x41, 0x00, 0x21, 0x14, + 0x03, 0x40, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x20, 0x14, 0x6a, 0x41, + 0x0b, 0x6a, 0x20, 0x12, 0x20, 0x12, 0x41, 0x0a, 0x6e, 0x22, 0x15, 0x41, + 0x0a, 0x6c, 0x6b, 0x41, 0x30, 0x72, 0x3a, 0x00, 0x00, 0x20, 0x14, 0x41, + 0x7f, 0x6a, 0x21, 0x14, 0x20, 0x12, 0x41, 0x09, 0x4b, 0x21, 0x13, 0x20, + 0x15, 0x21, 0x12, 0x20, 0x13, 0x0d, 0x00, 0x0b, 0x20, 0x14, 0x45, 0x0d, + 0x00, 0x20, 0x05, 0x41, 0xc4, 0x00, 0x6a, 0x20, 0x14, 0x6a, 0x41, 0x0c, + 0x6a, 0x21, 0x12, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x41, 0x30, 0x3a, 0x00, + 0x4f, 0x20, 0x0a, 0x21, 0x12, 0x0b, 0x20, 0x22, 0x41, 0x02, 0x72, 0x21, + 0x1c, 0x20, 0x1d, 0x41, 0x20, 0x71, 0x21, 0x15, 0x20, 0x12, 0x41, 0x7e, + 0x6a, 0x22, 0x1a, 0x20, 0x1d, 0x41, 0x0f, 0x6a, 0x3a, 0x00, 0x00, 0x20, + 0x12, 0x41, 0x7f, 0x6a, 0x41, 0x2d, 0x41, 0x2b, 0x20, 0x18, 0x41, 0x00, + 0x48, 0x1b, 0x3a, 0x00, 0x00, 0x20, 0x16, 0x41, 0x08, 0x71, 0x21, 0x13, + 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x21, 0x14, 0x03, 0x40, 0x20, 0x14, + 0x21, 0x12, 0x02, 0x40, 0x02, 0x40, 0x20, 0x21, 0x99, 0x44, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x41, 0x63, 0x45, 0x0d, 0x00, 0x20, 0x21, + 0xaa, 0x21, 0x14, 0x0c, 0x01, 0x0b, 0x41, 0x80, 0x80, 0x80, 0x80, 0x78, + 0x21, 0x14, 0x0b, 0x20, 0x12, 0x20, 0x14, 0x41, 0xf0, 0x9d, 0x80, 0x80, + 0x00, 0x6a, 0x2d, 0x00, 0x00, 0x20, 0x15, 0x72, 0x3a, 0x00, 0x00, 0x20, + 0x21, 0x20, 0x14, 0xb7, 0xa1, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x40, 0xa2, 0x21, 0x21, 0x02, 0x40, 0x20, 0x12, 0x41, 0x01, 0x6a, + 0x22, 0x14, 0x20, 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x6b, 0x41, 0x01, 0x47, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x13, 0x0d, 0x00, 0x20, 0x17, 0x41, 0x00, + 0x4a, 0x0d, 0x00, 0x20, 0x21, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x61, 0x0d, 0x01, 0x0b, 0x20, 0x12, 0x41, 0x2e, 0x3a, 0x00, + 0x01, 0x20, 0x12, 0x41, 0x02, 0x6a, 0x21, 0x14, 0x0b, 0x20, 0x21, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, 0x00, 0x0b, + 0x41, 0xfd, 0xff, 0xff, 0xff, 0x07, 0x20, 0x06, 0x20, 0x1a, 0x6b, 0x22, + 0x18, 0x20, 0x1c, 0x6a, 0x22, 0x12, 0x6b, 0x20, 0x17, 0x48, 0x0d, 0x02, + 0x20, 0x17, 0x41, 0x02, 0x6a, 0x20, 0x14, 0x20, 0x05, 0x41, 0xd0, 0x00, + 0x6a, 0x6b, 0x22, 0x14, 0x20, 0x14, 0x41, 0x7e, 0x6a, 0x20, 0x17, 0x48, + 0x1b, 0x20, 0x14, 0x20, 0x17, 0x1b, 0x22, 0x13, 0x20, 0x12, 0x6a, 0x21, + 0x1b, 0x02, 0x40, 0x20, 0x16, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x22, 0x15, + 0x0d, 0x00, 0x20, 0x19, 0x20, 0x1b, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, + 0xf0, 0x04, 0x6a, 0x41, 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, + 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x17, 0x1b, + 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x17, 0x0d, + 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, + 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, + 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, + 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, + 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb1, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, + 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x1e, 0x20, 0x1c, 0x20, 0x00, + 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x15, + 0x41, 0x80, 0x80, 0x04, 0x47, 0x0d, 0x00, 0x20, 0x19, 0x20, 0x1b, 0x4c, + 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x30, 0x20, 0x19, + 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, + 0x02, 0x49, 0x22, 0x17, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x02, 0x40, 0x20, 0x17, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, + 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, + 0x12, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0xd0, 0x00, 0x6a, 0x20, 0x14, 0x20, 0x00, 0x10, 0xb1, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, 0x40, 0x20, 0x13, 0x20, 0x14, 0x6b, + 0x22, 0x12, 0x41, 0x01, 0x48, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, + 0x6a, 0x41, 0x30, 0x20, 0x12, 0x41, 0x80, 0x02, 0x20, 0x12, 0x41, 0x80, + 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, + 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, 0x22, 0x12, 0x41, + 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, 0x00, 0x00, + 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x20, + 0x12, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x02, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, + 0x1a, 0x20, 0x18, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, + 0x0b, 0x20, 0x15, 0x41, 0x80, 0xc0, 0x00, 0x47, 0x0d, 0x00, 0x20, 0x19, + 0x20, 0x1b, 0x4c, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, + 0x20, 0x20, 0x19, 0x20, 0x1b, 0x6b, 0x22, 0x12, 0x41, 0x80, 0x02, 0x20, + 0x12, 0x41, 0x80, 0x02, 0x49, 0x22, 0x14, 0x1b, 0x10, 0xc0, 0x80, 0x80, + 0x80, 0x00, 0x1a, 0x02, 0x40, 0x20, 0x14, 0x0d, 0x00, 0x03, 0x40, 0x02, + 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0xf0, 0x04, 0x6a, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, + 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x12, 0x41, 0x80, 0x7e, 0x6a, + 0x22, 0x12, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, + 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x05, 0x41, 0xf0, + 0x04, 0x6a, 0x20, 0x12, 0x20, 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, + 0x1a, 0x0b, 0x20, 0x1b, 0x20, 0x19, 0x20, 0x1b, 0x20, 0x19, 0x4a, 0x1b, + 0x22, 0x12, 0x41, 0x00, 0x4e, 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x80, 0xa8, + 0x80, 0x80, 0x00, 0x41, 0x3d, 0x36, 0x02, 0x00, 0x0b, 0x41, 0x7f, 0x21, + 0x11, 0x0b, 0x20, 0x05, 0x41, 0xf0, 0x06, 0x6a, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x11, 0x0b, 0xb3, 0x04, 0x00, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x01, + 0x41, 0x77, 0x6a, 0x0e, 0x12, 0x11, 0x00, 0x01, 0x04, 0x02, 0x03, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x12, + 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, + 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x34, 0x02, 0x00, 0x37, + 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x35, + 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x34, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, + 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, + 0x78, 0x71, 0x22, 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, + 0x00, 0x20, 0x00, 0x20, 0x01, 0x32, 0x01, 0x00, 0x37, 0x03, 0x00, 0x0f, + 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, + 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x33, 0x01, 0x00, 0x37, + 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x30, + 0x00, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x31, 0x00, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, 0x71, 0x22, + 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, + 0x03, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, 0x71, 0x22, + 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, + 0x03, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, 0x71, 0x22, 0x01, 0x41, 0x08, + 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x29, 0x03, 0x00, 0x37, + 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, + 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x34, + 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, + 0x02, 0x00, 0x22, 0x01, 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, + 0x20, 0x01, 0x35, 0x02, 0x00, 0x37, 0x03, 0x00, 0x0f, 0x0b, 0x20, 0x02, + 0x20, 0x02, 0x28, 0x02, 0x00, 0x41, 0x07, 0x6a, 0x41, 0x78, 0x71, 0x22, + 0x01, 0x41, 0x08, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x2b, + 0x03, 0x00, 0x39, 0x03, 0x00, 0x0f, 0x0b, 0x10, 0xbe, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x0b, 0x20, 0x02, 0x20, 0x02, 0x28, 0x02, 0x00, 0x22, 0x01, + 0x41, 0x04, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x20, 0x01, 0x28, 0x02, + 0x00, 0x36, 0x02, 0x00, 0x0b, 0x0b, 0x9e, 0x01, 0x01, 0x01, 0x7f, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x80, 0x02, 0x6b, 0x22, 0x05, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, 0x20, 0x02, 0x20, 0x03, 0x4c, + 0x0d, 0x00, 0x20, 0x04, 0x41, 0x80, 0xc0, 0x04, 0x71, 0x0d, 0x00, 0x20, + 0x05, 0x20, 0x01, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x22, 0x03, 0x41, 0x80, + 0x02, 0x20, 0x03, 0x41, 0x80, 0x02, 0x49, 0x22, 0x04, 0x1b, 0x10, 0xc0, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x02, 0x40, 0x20, 0x04, 0x0d, 0x00, + 0x03, 0x40, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x41, 0x20, 0x71, + 0x0d, 0x00, 0x20, 0x02, 0x41, 0x80, 0x02, 0x20, 0x00, 0x10, 0xb1, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x03, 0x41, 0x80, 0x7e, 0x6a, 0x22, + 0x03, 0x41, 0xff, 0x01, 0x4b, 0x0d, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x2d, + 0x00, 0x00, 0x41, 0x20, 0x71, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x03, 0x20, + 0x00, 0x10, 0xb1, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x0b, 0x20, 0x05, 0x41, + 0x80, 0x02, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x0b, 0x1c, 0x00, + 0x41, 0x8d, 0x8b, 0x80, 0x80, 0x00, 0x41, 0xf8, 0x9e, 0x80, 0x80, 0x00, + 0x10, 0xb9, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x10, 0xa0, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x0b, 0xe6, 0x07, 0x01, 0x04, 0x7f, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x02, 0x41, 0x20, 0x4b, 0x0d, 0x00, 0x20, 0x01, 0x41, + 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x02, 0x45, 0x0d, 0x01, 0x20, 0x00, + 0x20, 0x01, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x7f, + 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x04, 0x20, 0x01, + 0x41, 0x01, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x02, 0x20, + 0x03, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x01, 0x3a, + 0x00, 0x01, 0x20, 0x02, 0x41, 0x7e, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, + 0x02, 0x6a, 0x21, 0x04, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x22, 0x05, 0x41, + 0x03, 0x71, 0x45, 0x0d, 0x02, 0x20, 0x03, 0x45, 0x0d, 0x02, 0x20, 0x00, + 0x20, 0x01, 0x2d, 0x00, 0x02, 0x3a, 0x00, 0x02, 0x20, 0x02, 0x41, 0x7d, + 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, 0x03, 0x6a, 0x21, 0x04, 0x20, 0x01, + 0x41, 0x03, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x02, 0x20, + 0x03, 0x45, 0x0d, 0x02, 0x20, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x03, 0x3a, + 0x00, 0x03, 0x20, 0x02, 0x41, 0x7c, 0x6a, 0x21, 0x03, 0x20, 0x00, 0x41, + 0x04, 0x6a, 0x21, 0x04, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x21, 0x05, 0x0c, + 0x02, 0x0b, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0xfc, 0x0a, 0x00, 0x00, + 0x20, 0x00, 0x0f, 0x0b, 0x20, 0x02, 0x21, 0x03, 0x20, 0x00, 0x21, 0x04, + 0x20, 0x01, 0x21, 0x05, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x04, 0x41, + 0x03, 0x71, 0x22, 0x02, 0x0d, 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, + 0x41, 0x10, 0x4f, 0x0d, 0x00, 0x20, 0x03, 0x21, 0x02, 0x0c, 0x01, 0x0b, + 0x02, 0x40, 0x20, 0x03, 0x41, 0x70, 0x6a, 0x22, 0x02, 0x41, 0x10, 0x71, + 0x0d, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, + 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x08, 0x37, 0x02, 0x08, 0x20, 0x04, + 0x41, 0x10, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x10, 0x6a, 0x21, 0x05, + 0x20, 0x02, 0x21, 0x03, 0x0b, 0x20, 0x02, 0x41, 0x10, 0x49, 0x0d, 0x00, + 0x20, 0x03, 0x21, 0x02, 0x03, 0x40, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, + 0x00, 0x37, 0x02, 0x00, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x08, 0x37, + 0x02, 0x08, 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x10, 0x37, 0x02, 0x10, + 0x20, 0x04, 0x20, 0x05, 0x29, 0x02, 0x18, 0x37, 0x02, 0x18, 0x20, 0x04, + 0x41, 0x20, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x20, 0x6a, 0x21, 0x05, + 0x20, 0x02, 0x41, 0x60, 0x6a, 0x22, 0x02, 0x41, 0x0f, 0x4b, 0x0d, 0x00, + 0x0b, 0x0b, 0x02, 0x40, 0x20, 0x02, 0x41, 0x08, 0x49, 0x0d, 0x00, 0x20, + 0x04, 0x20, 0x05, 0x29, 0x02, 0x00, 0x37, 0x02, 0x00, 0x20, 0x05, 0x41, + 0x08, 0x6a, 0x21, 0x05, 0x20, 0x04, 0x41, 0x08, 0x6a, 0x21, 0x04, 0x0b, + 0x02, 0x40, 0x20, 0x02, 0x41, 0x04, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x04, + 0x20, 0x05, 0x28, 0x02, 0x00, 0x36, 0x02, 0x00, 0x20, 0x05, 0x41, 0x04, + 0x6a, 0x21, 0x05, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x21, 0x04, 0x0b, 0x02, + 0x40, 0x20, 0x02, 0x41, 0x02, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x04, 0x20, + 0x05, 0x2f, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x20, 0x04, 0x41, 0x02, 0x6a, + 0x21, 0x04, 0x20, 0x05, 0x41, 0x02, 0x6a, 0x21, 0x05, 0x0b, 0x20, 0x02, + 0x41, 0x01, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, + 0x00, 0x3a, 0x00, 0x00, 0x20, 0x00, 0x0f, 0x0b, 0x02, 0x40, 0x02, 0x40, + 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x20, 0x49, 0x0d, + 0x00, 0x02, 0x40, 0x02, 0x40, 0x20, 0x02, 0x41, 0x7f, 0x6a, 0x0e, 0x03, + 0x03, 0x00, 0x01, 0x07, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, + 0x3b, 0x00, 0x00, 0x20, 0x04, 0x20, 0x05, 0x41, 0x02, 0x6a, 0x28, 0x01, + 0x00, 0x36, 0x02, 0x02, 0x20, 0x04, 0x20, 0x05, 0x41, 0x06, 0x6a, 0x29, + 0x01, 0x00, 0x37, 0x02, 0x06, 0x20, 0x04, 0x41, 0x12, 0x6a, 0x21, 0x02, + 0x20, 0x05, 0x41, 0x12, 0x6a, 0x21, 0x01, 0x41, 0x0e, 0x21, 0x06, 0x20, + 0x05, 0x41, 0x0e, 0x6a, 0x28, 0x01, 0x00, 0x21, 0x05, 0x41, 0x0e, 0x21, + 0x03, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, 0x3a, + 0x00, 0x00, 0x20, 0x04, 0x20, 0x05, 0x41, 0x01, 0x6a, 0x28, 0x00, 0x00, + 0x36, 0x02, 0x01, 0x20, 0x04, 0x20, 0x05, 0x41, 0x05, 0x6a, 0x29, 0x00, + 0x00, 0x37, 0x02, 0x05, 0x20, 0x04, 0x41, 0x11, 0x6a, 0x21, 0x02, 0x20, + 0x05, 0x41, 0x11, 0x6a, 0x21, 0x01, 0x41, 0x0d, 0x21, 0x06, 0x20, 0x05, + 0x41, 0x0d, 0x6a, 0x28, 0x00, 0x00, 0x21, 0x05, 0x41, 0x0f, 0x21, 0x03, + 0x0c, 0x02, 0x0b, 0x02, 0x40, 0x02, 0x40, 0x20, 0x03, 0x41, 0x10, 0x4f, + 0x0d, 0x00, 0x20, 0x04, 0x21, 0x02, 0x20, 0x05, 0x21, 0x01, 0x0c, 0x01, + 0x0b, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x20, + 0x04, 0x20, 0x05, 0x28, 0x00, 0x01, 0x36, 0x00, 0x01, 0x20, 0x04, 0x20, + 0x05, 0x29, 0x00, 0x05, 0x37, 0x00, 0x05, 0x20, 0x04, 0x20, 0x05, 0x2f, + 0x00, 0x0d, 0x3b, 0x00, 0x0d, 0x20, 0x04, 0x20, 0x05, 0x2d, 0x00, 0x0f, + 0x3a, 0x00, 0x0f, 0x20, 0x04, 0x41, 0x10, 0x6a, 0x21, 0x02, 0x20, 0x05, + 0x41, 0x10, 0x6a, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, 0x08, 0x71, 0x0d, + 0x02, 0x0c, 0x03, 0x0b, 0x20, 0x04, 0x20, 0x05, 0x28, 0x02, 0x00, 0x22, + 0x02, 0x3a, 0x00, 0x00, 0x20, 0x04, 0x20, 0x02, 0x41, 0x10, 0x76, 0x3a, + 0x00, 0x02, 0x20, 0x04, 0x20, 0x02, 0x41, 0x08, 0x76, 0x3a, 0x00, 0x01, + 0x20, 0x04, 0x20, 0x05, 0x41, 0x03, 0x6a, 0x28, 0x00, 0x00, 0x36, 0x02, + 0x03, 0x20, 0x04, 0x20, 0x05, 0x41, 0x07, 0x6a, 0x29, 0x00, 0x00, 0x37, + 0x02, 0x07, 0x20, 0x04, 0x41, 0x13, 0x6a, 0x21, 0x02, 0x20, 0x05, 0x41, + 0x13, 0x6a, 0x21, 0x01, 0x41, 0x0f, 0x21, 0x06, 0x20, 0x05, 0x41, 0x0f, + 0x6a, 0x28, 0x00, 0x00, 0x21, 0x05, 0x41, 0x0d, 0x21, 0x03, 0x0b, 0x20, + 0x04, 0x20, 0x06, 0x6a, 0x20, 0x05, 0x36, 0x02, 0x00, 0x0b, 0x20, 0x02, + 0x20, 0x01, 0x29, 0x00, 0x00, 0x37, 0x00, 0x00, 0x20, 0x02, 0x41, 0x08, + 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x08, 0x6a, 0x21, 0x01, 0x0b, 0x02, + 0x40, 0x20, 0x03, 0x41, 0x04, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x20, + 0x01, 0x28, 0x00, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x41, 0x04, 0x6a, + 0x21, 0x02, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x21, 0x01, 0x0b, 0x02, 0x40, + 0x20, 0x03, 0x41, 0x02, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x01, + 0x2f, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x20, 0x02, 0x41, 0x02, 0x6a, 0x21, + 0x02, 0x20, 0x01, 0x41, 0x02, 0x6a, 0x21, 0x01, 0x0b, 0x20, 0x03, 0x41, + 0x01, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x02, 0x20, 0x01, 0x2d, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x0b, 0x20, 0x00, 0x0b, 0x88, 0x03, 0x02, 0x03, 0x7f, + 0x01, 0x7e, 0x02, 0x40, 0x20, 0x02, 0x41, 0x21, 0x49, 0x0d, 0x00, 0x20, + 0x00, 0x20, 0x01, 0x20, 0x02, 0xfc, 0x0b, 0x00, 0x20, 0x00, 0x0f, 0x0b, + 0x02, 0x40, 0x20, 0x02, 0x45, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x3a, + 0x00, 0x00, 0x20, 0x02, 0x20, 0x00, 0x6a, 0x22, 0x03, 0x41, 0x7f, 0x6a, + 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x03, 0x49, 0x0d, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0x02, 0x20, 0x00, 0x20, 0x01, 0x3a, + 0x00, 0x01, 0x20, 0x03, 0x41, 0x7d, 0x6a, 0x20, 0x01, 0x3a, 0x00, 0x00, + 0x20, 0x03, 0x41, 0x7e, 0x6a, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, 0x02, + 0x41, 0x07, 0x49, 0x0d, 0x00, 0x20, 0x00, 0x20, 0x01, 0x3a, 0x00, 0x03, + 0x20, 0x03, 0x41, 0x7c, 0x6a, 0x20, 0x01, 0x3a, 0x00, 0x00, 0x20, 0x02, + 0x41, 0x09, 0x49, 0x0d, 0x00, 0x20, 0x00, 0x41, 0x00, 0x20, 0x00, 0x6b, + 0x41, 0x03, 0x71, 0x22, 0x04, 0x6a, 0x22, 0x05, 0x20, 0x01, 0x41, 0xff, + 0x01, 0x71, 0x41, 0x81, 0x82, 0x84, 0x08, 0x6c, 0x22, 0x03, 0x36, 0x02, + 0x00, 0x20, 0x05, 0x20, 0x02, 0x20, 0x04, 0x6b, 0x41, 0x7c, 0x71, 0x22, + 0x01, 0x6a, 0x22, 0x02, 0x41, 0x7c, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, + 0x20, 0x01, 0x41, 0x09, 0x49, 0x0d, 0x00, 0x20, 0x05, 0x20, 0x03, 0x36, + 0x02, 0x08, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x04, 0x20, 0x02, 0x41, + 0x78, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, 0x41, 0x74, 0x6a, + 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x01, 0x41, 0x19, 0x49, 0x0d, 0x00, + 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x18, 0x20, 0x05, 0x20, 0x03, 0x36, + 0x02, 0x14, 0x20, 0x05, 0x20, 0x03, 0x36, 0x02, 0x10, 0x20, 0x05, 0x20, + 0x03, 0x36, 0x02, 0x0c, 0x20, 0x02, 0x41, 0x70, 0x6a, 0x20, 0x03, 0x36, + 0x02, 0x00, 0x20, 0x02, 0x41, 0x6c, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, + 0x20, 0x02, 0x41, 0x68, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x02, + 0x41, 0x64, 0x6a, 0x20, 0x03, 0x36, 0x02, 0x00, 0x20, 0x01, 0x20, 0x05, + 0x41, 0x04, 0x71, 0x41, 0x18, 0x72, 0x22, 0x02, 0x6b, 0x22, 0x01, 0x41, + 0x20, 0x49, 0x0d, 0x00, 0x20, 0x03, 0xad, 0x42, 0x81, 0x80, 0x80, 0x80, + 0x10, 0x7e, 0x21, 0x06, 0x20, 0x05, 0x20, 0x02, 0x6a, 0x21, 0x02, 0x03, + 0x40, 0x20, 0x02, 0x20, 0x06, 0x37, 0x03, 0x18, 0x20, 0x02, 0x20, 0x06, + 0x37, 0x03, 0x10, 0x20, 0x02, 0x20, 0x06, 0x37, 0x03, 0x08, 0x20, 0x02, + 0x20, 0x06, 0x37, 0x03, 0x00, 0x20, 0x02, 0x41, 0x20, 0x6a, 0x21, 0x02, + 0x20, 0x01, 0x41, 0x60, 0x6a, 0x22, 0x01, 0x41, 0x1f, 0x4b, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x00, 0x0b, 0xcc, 0x01, 0x01, 0x03, 0x7f, 0x20, 0x00, + 0x21, 0x01, 0x02, 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x03, 0x71, 0x45, + 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x0d, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x6b, 0x0f, 0x0b, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x22, + 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, + 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x22, 0x01, 0x41, 0x03, + 0x71, 0x45, 0x0d, 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x01, + 0x20, 0x00, 0x41, 0x03, 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, 0x45, 0x0d, + 0x00, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x41, + 0x04, 0x6a, 0x22, 0x01, 0x41, 0x03, 0x71, 0x0d, 0x01, 0x0b, 0x20, 0x01, + 0x41, 0x7c, 0x6a, 0x21, 0x02, 0x20, 0x01, 0x41, 0x7b, 0x6a, 0x21, 0x01, + 0x03, 0x40, 0x20, 0x01, 0x41, 0x04, 0x6a, 0x21, 0x01, 0x20, 0x02, 0x41, + 0x04, 0x6a, 0x22, 0x02, 0x28, 0x02, 0x00, 0x22, 0x03, 0x41, 0x7f, 0x73, + 0x20, 0x03, 0x41, 0xff, 0xfd, 0xfb, 0x77, 0x6a, 0x71, 0x41, 0x80, 0x81, + 0x82, 0x84, 0x78, 0x71, 0x45, 0x0d, 0x00, 0x0b, 0x03, 0x40, 0x20, 0x01, + 0x41, 0x01, 0x6a, 0x21, 0x01, 0x20, 0x02, 0x2d, 0x00, 0x00, 0x21, 0x03, + 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, 0x20, 0x03, 0x0d, 0x00, 0x0b, + 0x0b, 0x20, 0x01, 0x20, 0x00, 0x6b, 0x0b, 0xf2, 0x02, 0x01, 0x03, 0x7f, + 0x20, 0x02, 0x41, 0x00, 0x47, 0x21, 0x03, 0x02, 0x40, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x20, 0x00, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x00, 0x20, + 0x02, 0x45, 0x0d, 0x00, 0x02, 0x40, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x20, + 0x01, 0x41, 0xff, 0x01, 0x71, 0x47, 0x0d, 0x00, 0x20, 0x00, 0x21, 0x04, + 0x20, 0x02, 0x21, 0x05, 0x0c, 0x03, 0x0b, 0x20, 0x02, 0x41, 0x7f, 0x6a, + 0x22, 0x05, 0x41, 0x00, 0x47, 0x21, 0x03, 0x20, 0x00, 0x41, 0x01, 0x6a, + 0x22, 0x04, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x01, 0x20, 0x05, 0x45, 0x0d, + 0x01, 0x20, 0x04, 0x2d, 0x00, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, + 0x46, 0x0d, 0x02, 0x20, 0x02, 0x41, 0x7e, 0x6a, 0x22, 0x05, 0x41, 0x00, + 0x47, 0x21, 0x03, 0x20, 0x00, 0x41, 0x02, 0x6a, 0x22, 0x04, 0x41, 0x03, + 0x71, 0x45, 0x0d, 0x01, 0x20, 0x05, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x2d, + 0x00, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x02, 0x20, + 0x02, 0x41, 0x7d, 0x6a, 0x22, 0x05, 0x41, 0x00, 0x47, 0x21, 0x03, 0x20, + 0x00, 0x41, 0x03, 0x6a, 0x22, 0x04, 0x41, 0x03, 0x71, 0x45, 0x0d, 0x01, + 0x20, 0x05, 0x45, 0x0d, 0x01, 0x20, 0x04, 0x2d, 0x00, 0x00, 0x20, 0x01, + 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x02, 0x20, 0x00, 0x41, 0x04, 0x6a, + 0x21, 0x04, 0x20, 0x02, 0x41, 0x7c, 0x6a, 0x22, 0x05, 0x41, 0x00, 0x47, + 0x21, 0x03, 0x0c, 0x01, 0x0b, 0x20, 0x02, 0x21, 0x05, 0x20, 0x00, 0x21, + 0x04, 0x0b, 0x20, 0x03, 0x45, 0x0d, 0x01, 0x02, 0x40, 0x20, 0x04, 0x2d, + 0x00, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, 0x46, 0x0d, 0x00, 0x20, + 0x05, 0x41, 0x04, 0x49, 0x0d, 0x00, 0x20, 0x01, 0x41, 0xff, 0x01, 0x71, + 0x41, 0x81, 0x82, 0x84, 0x08, 0x6c, 0x21, 0x00, 0x03, 0x40, 0x20, 0x04, + 0x28, 0x02, 0x00, 0x20, 0x00, 0x73, 0x22, 0x02, 0x41, 0x7f, 0x73, 0x20, + 0x02, 0x41, 0xff, 0xfd, 0xfb, 0x77, 0x6a, 0x71, 0x41, 0x80, 0x81, 0x82, + 0x84, 0x78, 0x71, 0x0d, 0x02, 0x20, 0x04, 0x41, 0x04, 0x6a, 0x21, 0x04, + 0x20, 0x05, 0x41, 0x7c, 0x6a, 0x22, 0x05, 0x41, 0x03, 0x4b, 0x0d, 0x00, + 0x0b, 0x0b, 0x20, 0x05, 0x45, 0x0d, 0x01, 0x0b, 0x20, 0x01, 0x41, 0xff, + 0x01, 0x71, 0x21, 0x02, 0x03, 0x40, 0x02, 0x40, 0x20, 0x04, 0x2d, 0x00, + 0x00, 0x20, 0x02, 0x47, 0x0d, 0x00, 0x20, 0x04, 0x0f, 0x0b, 0x20, 0x04, + 0x41, 0x01, 0x6a, 0x21, 0x04, 0x20, 0x05, 0x41, 0x7f, 0x6a, 0x22, 0x05, + 0x0d, 0x00, 0x0b, 0x0b, 0x41, 0x00, 0x0b, 0x1a, 0x01, 0x01, 0x7f, 0x20, + 0x00, 0x41, 0x00, 0x20, 0x01, 0x10, 0xc2, 0x80, 0x80, 0x80, 0x00, 0x22, + 0x02, 0x20, 0x00, 0x6b, 0x20, 0x01, 0x20, 0x02, 0x1b, 0x0b, 0x37, 0x01, + 0x01, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, + 0x03, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x20, 0x02, 0x36, + 0x02, 0x0c, 0x20, 0x00, 0x20, 0x01, 0x20, 0x02, 0x10, 0xba, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x02, 0x20, 0x03, 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x02, 0x0b, 0x4d, 0x01, 0x01, 0x7f, 0x23, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x6b, 0x22, 0x04, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x20, 0x04, + 0x20, 0x03, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x04, + 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0xf8, 0x9e, 0x80, 0x80, + 0x00, 0x41, 0x90, 0x8c, 0x80, 0x80, 0x00, 0x20, 0x04, 0x10, 0xc4, 0x80, + 0x80, 0x80, 0x00, 0x1a, 0x10, 0xa0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x0b, + 0x8e, 0x01, 0x01, 0x01, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, + 0x10, 0x6b, 0x22, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x03, 0x41, 0x04, 0x49, 0x0d, 0x00, 0x41, 0x00, 0x41, + 0x3a, 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, 0x03, + 0x0c, 0x01, 0x0b, 0x20, 0x04, 0x20, 0x02, 0x36, 0x02, 0x0c, 0x20, 0x04, + 0x20, 0x01, 0x36, 0x02, 0x08, 0x02, 0x40, 0x20, 0x00, 0x20, 0x04, 0x41, + 0x08, 0x6a, 0x41, 0x01, 0x20, 0x03, 0x41, 0xff, 0xff, 0x03, 0x71, 0x20, + 0x04, 0x41, 0x04, 0x6a, 0x20, 0x04, 0x41, 0x02, 0x6a, 0x10, 0x9f, 0x80, + 0x80, 0x80, 0x00, 0x22, 0x03, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x20, 0x03, + 0x36, 0x02, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x41, 0x7f, 0x21, 0x03, 0x0c, + 0x01, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x04, 0x21, 0x03, 0x0b, 0x20, 0x04, + 0x41, 0x10, 0x6a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x03, 0x0b, + 0x12, 0x00, 0x20, 0x00, 0x41, 0x08, 0x74, 0x20, 0x00, 0x41, 0x08, 0x76, + 0x72, 0x41, 0xff, 0xff, 0x03, 0x71, 0x0b, 0x23, 0x00, 0x20, 0x00, 0x41, + 0x18, 0x74, 0x20, 0x00, 0x41, 0x80, 0xfe, 0x03, 0x71, 0x41, 0x08, 0x74, + 0x72, 0x20, 0x00, 0x41, 0x08, 0x76, 0x41, 0x80, 0xfe, 0x03, 0x71, 0x20, + 0x00, 0x41, 0x18, 0x76, 0x72, 0x72, 0x0b, 0xe4, 0x09, 0x11, 0x06, 0x7f, + 0x01, 0x7e, 0x22, 0x7f, 0x01, 0x7e, 0x02, 0x7f, 0x01, 0x7e, 0x04, 0x7f, + 0x01, 0x7e, 0x3e, 0x7f, 0x01, 0x7e, 0x02, 0x7f, 0x01, 0x7e, 0x05, 0x7f, + 0x01, 0x7e, 0x05, 0x7f, 0x01, 0x7e, 0x09, 0x7f, 0x23, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x03, 0x41, 0xe0, 0x00, 0x21, 0x04, 0x20, 0x03, 0x20, + 0x04, 0x6b, 0x21, 0x05, 0x20, 0x05, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x58, 0x20, 0x05, 0x20, 0x01, 0x36, + 0x02, 0x54, 0x20, 0x05, 0x20, 0x02, 0x36, 0x02, 0x50, 0x20, 0x05, 0x28, + 0x02, 0x58, 0x21, 0x06, 0x20, 0x06, 0x28, 0x02, 0x00, 0x21, 0x07, 0x41, + 0x01, 0x21, 0x08, 0x20, 0x07, 0x20, 0x08, 0x4b, 0x1a, 0x02, 0x40, 0x02, + 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, 0x07, 0x0e, 0x02, 0x00, + 0x01, 0x02, 0x0b, 0x42, 0x00, 0x21, 0x09, 0x20, 0x05, 0x20, 0x09, 0x37, + 0x03, 0x48, 0x20, 0x05, 0x20, 0x09, 0x37, 0x03, 0x40, 0x20, 0x05, 0x28, + 0x02, 0x58, 0x21, 0x0a, 0x20, 0x0a, 0x2d, 0x00, 0x04, 0x21, 0x0b, 0x41, + 0xff, 0x01, 0x21, 0x0c, 0x20, 0x0b, 0x20, 0x0c, 0x71, 0x21, 0x0d, 0x41, + 0x18, 0x21, 0x0e, 0x20, 0x0d, 0x20, 0x0e, 0x74, 0x21, 0x0f, 0x20, 0x05, + 0x28, 0x02, 0x58, 0x21, 0x10, 0x20, 0x10, 0x2d, 0x00, 0x05, 0x21, 0x11, + 0x41, 0xff, 0x01, 0x21, 0x12, 0x20, 0x11, 0x20, 0x12, 0x71, 0x21, 0x13, + 0x41, 0x10, 0x21, 0x14, 0x20, 0x13, 0x20, 0x14, 0x74, 0x21, 0x15, 0x20, + 0x0f, 0x20, 0x15, 0x72, 0x21, 0x16, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, + 0x17, 0x20, 0x17, 0x2d, 0x00, 0x06, 0x21, 0x18, 0x41, 0xff, 0x01, 0x21, + 0x19, 0x20, 0x18, 0x20, 0x19, 0x71, 0x21, 0x1a, 0x41, 0x08, 0x21, 0x1b, + 0x20, 0x1a, 0x20, 0x1b, 0x74, 0x21, 0x1c, 0x20, 0x16, 0x20, 0x1c, 0x72, + 0x21, 0x1d, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, 0x1e, 0x20, 0x1e, 0x2d, + 0x00, 0x07, 0x21, 0x1f, 0x41, 0xff, 0x01, 0x21, 0x20, 0x20, 0x1f, 0x20, + 0x20, 0x71, 0x21, 0x21, 0x20, 0x1d, 0x20, 0x21, 0x72, 0x21, 0x22, 0x20, + 0x05, 0x20, 0x22, 0x36, 0x02, 0x3c, 0x41, 0x01, 0x21, 0x23, 0x20, 0x05, + 0x20, 0x23, 0x3b, 0x01, 0x40, 0x20, 0x05, 0x28, 0x02, 0x3c, 0x21, 0x24, + 0x20, 0x24, 0x10, 0xa4, 0x80, 0x80, 0x80, 0x00, 0x21, 0x25, 0x20, 0x05, + 0x20, 0x25, 0x36, 0x02, 0x44, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, 0x26, + 0x20, 0x26, 0x2f, 0x01, 0x08, 0x21, 0x27, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x28, 0x20, 0x27, 0x20, 0x28, 0x71, 0x21, 0x29, 0x20, 0x29, 0x10, 0xa5, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x2a, 0x20, 0x05, 0x20, 0x2a, 0x3b, 0x01, + 0x42, 0x20, 0x05, 0x28, 0x02, 0x54, 0x21, 0x2b, 0x20, 0x05, 0x29, 0x03, + 0x40, 0x21, 0x2c, 0x20, 0x2b, 0x20, 0x2c, 0x37, 0x03, 0x00, 0x41, 0x08, + 0x21, 0x2d, 0x20, 0x2b, 0x20, 0x2d, 0x6a, 0x21, 0x2e, 0x20, 0x05, 0x29, + 0x03, 0x48, 0x21, 0x2f, 0x20, 0x2e, 0x20, 0x2f, 0x37, 0x03, 0x00, 0x20, + 0x05, 0x28, 0x02, 0x50, 0x21, 0x30, 0x41, 0x10, 0x21, 0x31, 0x20, 0x30, + 0x20, 0x31, 0x36, 0x02, 0x00, 0x0c, 0x02, 0x0b, 0x41, 0x28, 0x21, 0x32, + 0x20, 0x05, 0x20, 0x32, 0x6a, 0x21, 0x33, 0x42, 0x00, 0x21, 0x34, 0x20, + 0x33, 0x20, 0x34, 0x37, 0x03, 0x00, 0x41, 0x20, 0x21, 0x35, 0x20, 0x05, + 0x20, 0x35, 0x6a, 0x21, 0x36, 0x20, 0x36, 0x20, 0x34, 0x37, 0x03, 0x00, + 0x20, 0x05, 0x20, 0x34, 0x37, 0x03, 0x18, 0x20, 0x05, 0x20, 0x34, 0x37, + 0x03, 0x10, 0x41, 0x10, 0x21, 0x37, 0x20, 0x05, 0x20, 0x37, 0x6a, 0x21, + 0x38, 0x20, 0x38, 0x21, 0x39, 0x41, 0x08, 0x21, 0x3a, 0x20, 0x39, 0x20, + 0x3a, 0x6a, 0x21, 0x3b, 0x20, 0x05, 0x20, 0x3b, 0x36, 0x02, 0x0c, 0x20, + 0x05, 0x28, 0x02, 0x58, 0x21, 0x3c, 0x20, 0x3c, 0x2f, 0x01, 0x04, 0x21, + 0x3d, 0x41, 0xff, 0xff, 0x03, 0x21, 0x3e, 0x20, 0x3d, 0x20, 0x3e, 0x71, + 0x21, 0x3f, 0x20, 0x3f, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x40, + 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x41, 0x20, 0x41, 0x20, 0x40, 0x3b, + 0x01, 0x00, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, 0x42, 0x20, 0x42, 0x2f, + 0x01, 0x06, 0x21, 0x43, 0x41, 0xff, 0xff, 0x03, 0x21, 0x44, 0x20, 0x43, + 0x20, 0x44, 0x71, 0x21, 0x45, 0x20, 0x45, 0x10, 0xa5, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x46, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x47, 0x20, 0x47, + 0x20, 0x46, 0x3b, 0x01, 0x02, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, 0x48, + 0x20, 0x48, 0x2f, 0x01, 0x08, 0x21, 0x49, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x4a, 0x20, 0x49, 0x20, 0x4a, 0x71, 0x21, 0x4b, 0x20, 0x4b, 0x10, 0xa5, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x4c, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, + 0x4d, 0x20, 0x4d, 0x20, 0x4c, 0x3b, 0x01, 0x04, 0x20, 0x05, 0x28, 0x02, + 0x58, 0x21, 0x4e, 0x20, 0x4e, 0x2f, 0x01, 0x0a, 0x21, 0x4f, 0x41, 0xff, + 0xff, 0x03, 0x21, 0x50, 0x20, 0x4f, 0x20, 0x50, 0x71, 0x21, 0x51, 0x20, + 0x51, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x52, 0x20, 0x05, 0x28, + 0x02, 0x0c, 0x21, 0x53, 0x20, 0x53, 0x20, 0x52, 0x3b, 0x01, 0x06, 0x20, + 0x05, 0x28, 0x02, 0x58, 0x21, 0x54, 0x20, 0x54, 0x2f, 0x01, 0x0c, 0x21, + 0x55, 0x41, 0xff, 0xff, 0x03, 0x21, 0x56, 0x20, 0x55, 0x20, 0x56, 0x71, + 0x21, 0x57, 0x20, 0x57, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x58, + 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x59, 0x20, 0x59, 0x20, 0x58, 0x3b, + 0x01, 0x08, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, 0x5a, 0x20, 0x5a, 0x2f, + 0x01, 0x0e, 0x21, 0x5b, 0x41, 0xff, 0xff, 0x03, 0x21, 0x5c, 0x20, 0x5b, + 0x20, 0x5c, 0x71, 0x21, 0x5d, 0x20, 0x5d, 0x10, 0xa5, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x5e, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x5f, 0x20, 0x5f, + 0x20, 0x5e, 0x3b, 0x01, 0x0a, 0x20, 0x05, 0x28, 0x02, 0x58, 0x21, 0x60, + 0x20, 0x60, 0x2f, 0x01, 0x10, 0x21, 0x61, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x62, 0x20, 0x61, 0x20, 0x62, 0x71, 0x21, 0x63, 0x20, 0x63, 0x10, 0xa5, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x64, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, + 0x65, 0x20, 0x65, 0x20, 0x64, 0x3b, 0x01, 0x0c, 0x20, 0x05, 0x28, 0x02, + 0x58, 0x21, 0x66, 0x20, 0x66, 0x2f, 0x01, 0x12, 0x21, 0x67, 0x41, 0xff, + 0xff, 0x03, 0x21, 0x68, 0x20, 0x67, 0x20, 0x68, 0x71, 0x21, 0x69, 0x20, + 0x69, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x6a, 0x20, 0x05, 0x28, + 0x02, 0x0c, 0x21, 0x6b, 0x20, 0x6b, 0x20, 0x6a, 0x3b, 0x01, 0x0e, 0x41, + 0x02, 0x21, 0x6c, 0x20, 0x05, 0x20, 0x6c, 0x3b, 0x01, 0x10, 0x20, 0x05, + 0x28, 0x02, 0x58, 0x21, 0x6d, 0x20, 0x6d, 0x2f, 0x01, 0x14, 0x21, 0x6e, + 0x41, 0xff, 0xff, 0x03, 0x21, 0x6f, 0x20, 0x6e, 0x20, 0x6f, 0x71, 0x21, + 0x70, 0x20, 0x70, 0x10, 0xa5, 0x80, 0x80, 0x80, 0x00, 0x21, 0x71, 0x20, + 0x05, 0x20, 0x71, 0x3b, 0x01, 0x12, 0x20, 0x05, 0x28, 0x02, 0x54, 0x21, + 0x72, 0x20, 0x05, 0x29, 0x03, 0x10, 0x21, 0x73, 0x20, 0x72, 0x20, 0x73, + 0x37, 0x03, 0x00, 0x41, 0x08, 0x21, 0x74, 0x20, 0x72, 0x20, 0x74, 0x6a, + 0x21, 0x75, 0x20, 0x05, 0x29, 0x03, 0x18, 0x21, 0x76, 0x20, 0x75, 0x20, + 0x76, 0x37, 0x03, 0x00, 0x41, 0x18, 0x21, 0x77, 0x20, 0x72, 0x20, 0x77, + 0x6a, 0x21, 0x78, 0x41, 0x10, 0x21, 0x79, 0x20, 0x05, 0x20, 0x79, 0x6a, + 0x21, 0x7a, 0x20, 0x7a, 0x20, 0x77, 0x6a, 0x21, 0x7b, 0x20, 0x7b, 0x29, + 0x03, 0x00, 0x21, 0x7c, 0x20, 0x78, 0x20, 0x7c, 0x37, 0x03, 0x00, 0x41, + 0x10, 0x21, 0x7d, 0x20, 0x72, 0x20, 0x7d, 0x6a, 0x21, 0x7e, 0x41, 0x10, + 0x21, 0x7f, 0x20, 0x05, 0x20, 0x7f, 0x6a, 0x21, 0x80, 0x01, 0x20, 0x80, + 0x01, 0x20, 0x7d, 0x6a, 0x21, 0x81, 0x01, 0x20, 0x81, 0x01, 0x29, 0x03, + 0x00, 0x21, 0x82, 0x01, 0x20, 0x7e, 0x20, 0x82, 0x01, 0x37, 0x03, 0x00, + 0x20, 0x05, 0x28, 0x02, 0x50, 0x21, 0x83, 0x01, 0x41, 0x20, 0x21, 0x84, + 0x01, 0x20, 0x83, 0x01, 0x20, 0x84, 0x01, 0x36, 0x02, 0x00, 0x0c, 0x01, + 0x0b, 0x41, 0x05, 0x21, 0x85, 0x01, 0x20, 0x05, 0x20, 0x85, 0x01, 0x3b, + 0x01, 0x5e, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x86, 0x01, 0x20, 0x05, + 0x20, 0x86, 0x01, 0x3b, 0x01, 0x5e, 0x0b, 0x20, 0x05, 0x2f, 0x01, 0x5e, + 0x21, 0x87, 0x01, 0x41, 0xff, 0xff, 0x03, 0x21, 0x88, 0x01, 0x20, 0x87, + 0x01, 0x20, 0x88, 0x01, 0x71, 0x21, 0x89, 0x01, 0x41, 0xe0, 0x00, 0x21, + 0x8a, 0x01, 0x20, 0x05, 0x20, 0x8a, 0x01, 0x6a, 0x21, 0x8b, 0x01, 0x20, + 0x8b, 0x01, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x89, 0x01, 0x0f, + 0x0b, 0xac, 0x04, 0x01, 0x43, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x03, 0x41, 0x10, 0x21, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6b, 0x21, + 0x05, 0x20, 0x05, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, + 0x00, 0x36, 0x02, 0x0c, 0x20, 0x05, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, + 0x05, 0x20, 0x02, 0x36, 0x02, 0x04, 0x41, 0x00, 0x21, 0x06, 0x20, 0x05, + 0x20, 0x06, 0x3b, 0x01, 0x02, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x07, + 0x20, 0x07, 0x2f, 0x01, 0x00, 0x21, 0x08, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x09, 0x20, 0x08, 0x20, 0x09, 0x71, 0x21, 0x0a, 0x41, 0x01, 0x21, 0x0b, + 0x20, 0x0b, 0x21, 0x0c, 0x20, 0x0a, 0x21, 0x0d, 0x20, 0x0c, 0x20, 0x0d, + 0x46, 0x21, 0x0e, 0x41, 0x01, 0x21, 0x0f, 0x20, 0x0e, 0x20, 0x0f, 0x71, + 0x21, 0x10, 0x02, 0x40, 0x02, 0x40, 0x20, 0x10, 0x45, 0x0d, 0x00, 0x20, + 0x05, 0x28, 0x02, 0x08, 0x21, 0x11, 0x41, 0x10, 0x21, 0x12, 0x20, 0x12, + 0x21, 0x13, 0x20, 0x11, 0x21, 0x14, 0x20, 0x13, 0x20, 0x14, 0x4d, 0x21, + 0x15, 0x41, 0x01, 0x21, 0x16, 0x20, 0x15, 0x20, 0x16, 0x71, 0x21, 0x17, + 0x02, 0x40, 0x20, 0x17, 0x0d, 0x00, 0x41, 0xb6, 0x88, 0x80, 0x80, 0x00, + 0x21, 0x18, 0x41, 0x8b, 0x89, 0x80, 0x80, 0x00, 0x21, 0x19, 0x41, 0xc4, + 0x00, 0x21, 0x1a, 0x41, 0xa0, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1b, 0x20, + 0x18, 0x20, 0x19, 0x20, 0x1a, 0x20, 0x1b, 0x10, 0xc5, 0x80, 0x80, 0x80, + 0x00, 0x00, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x1c, 0x20, 0x1c, + 0x28, 0x02, 0x04, 0x21, 0x1d, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x1e, + 0x20, 0x1e, 0x2f, 0x01, 0x02, 0x21, 0x1f, 0x20, 0x05, 0x28, 0x02, 0x04, + 0x21, 0x20, 0x41, 0xff, 0xff, 0x03, 0x21, 0x21, 0x20, 0x1f, 0x20, 0x21, + 0x71, 0x21, 0x22, 0x20, 0x1d, 0x20, 0x22, 0x20, 0x20, 0x10, 0xcb, 0x80, + 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, + 0x23, 0x20, 0x23, 0x2f, 0x01, 0x00, 0x21, 0x24, 0x41, 0xff, 0xff, 0x03, + 0x21, 0x25, 0x20, 0x24, 0x20, 0x25, 0x71, 0x21, 0x26, 0x41, 0x02, 0x21, + 0x27, 0x20, 0x27, 0x21, 0x28, 0x20, 0x26, 0x21, 0x29, 0x20, 0x28, 0x20, + 0x29, 0x46, 0x21, 0x2a, 0x41, 0x01, 0x21, 0x2b, 0x20, 0x2a, 0x20, 0x2b, + 0x71, 0x21, 0x2c, 0x02, 0x40, 0x02, 0x40, 0x20, 0x2c, 0x45, 0x0d, 0x00, + 0x20, 0x05, 0x28, 0x02, 0x08, 0x21, 0x2d, 0x41, 0x20, 0x21, 0x2e, 0x20, + 0x2e, 0x21, 0x2f, 0x20, 0x2d, 0x21, 0x30, 0x20, 0x2f, 0x20, 0x30, 0x4d, + 0x21, 0x31, 0x41, 0x01, 0x21, 0x32, 0x20, 0x31, 0x20, 0x32, 0x71, 0x21, + 0x33, 0x02, 0x40, 0x20, 0x33, 0x0d, 0x00, 0x41, 0xdc, 0x88, 0x80, 0x80, + 0x00, 0x21, 0x34, 0x41, 0x8b, 0x89, 0x80, 0x80, 0x00, 0x21, 0x35, 0x41, + 0xcb, 0x00, 0x21, 0x36, 0x41, 0xa0, 0x88, 0x80, 0x80, 0x00, 0x21, 0x37, + 0x20, 0x34, 0x20, 0x35, 0x20, 0x36, 0x20, 0x37, 0x10, 0xc5, 0x80, 0x80, + 0x80, 0x00, 0x00, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x38, 0x41, + 0x08, 0x21, 0x39, 0x20, 0x38, 0x20, 0x39, 0x6a, 0x21, 0x3a, 0x20, 0x05, + 0x28, 0x02, 0x0c, 0x21, 0x3b, 0x20, 0x3b, 0x2f, 0x01, 0x02, 0x21, 0x3c, + 0x20, 0x05, 0x28, 0x02, 0x04, 0x21, 0x3d, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x3e, 0x20, 0x3c, 0x20, 0x3e, 0x71, 0x21, 0x3f, 0x20, 0x3a, 0x20, 0x3f, + 0x20, 0x3d, 0x10, 0xcc, 0x80, 0x80, 0x80, 0x00, 0x0c, 0x01, 0x0b, 0x41, + 0x05, 0x21, 0x40, 0x20, 0x05, 0x20, 0x40, 0x3b, 0x01, 0x02, 0x0b, 0x0b, + 0x20, 0x05, 0x2f, 0x01, 0x02, 0x21, 0x41, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x42, 0x20, 0x41, 0x20, 0x42, 0x71, 0x21, 0x43, 0x41, 0x10, 0x21, 0x44, + 0x20, 0x05, 0x20, 0x44, 0x6a, 0x21, 0x45, 0x20, 0x45, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x43, 0x0f, 0x0b, 0xa9, 0x01, 0x01, 0x10, 0x7f, + 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x03, 0x41, 0x10, 0x21, 0x04, + 0x20, 0x03, 0x20, 0x04, 0x6b, 0x21, 0x05, 0x20, 0x05, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x05, + 0x20, 0x01, 0x3b, 0x01, 0x0a, 0x20, 0x05, 0x20, 0x02, 0x36, 0x02, 0x04, + 0x20, 0x05, 0x28, 0x02, 0x04, 0x21, 0x06, 0x41, 0x00, 0x21, 0x07, 0x20, + 0x06, 0x20, 0x07, 0x36, 0x02, 0x00, 0x20, 0x05, 0x2f, 0x01, 0x0a, 0x21, + 0x08, 0x41, 0xff, 0xff, 0x03, 0x21, 0x09, 0x20, 0x08, 0x20, 0x09, 0x71, + 0x21, 0x0a, 0x20, 0x0a, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, 0x0b, + 0x20, 0x05, 0x28, 0x02, 0x04, 0x21, 0x0c, 0x20, 0x0c, 0x20, 0x0b, 0x3b, + 0x01, 0x08, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x0d, 0x20, 0x05, 0x28, + 0x02, 0x04, 0x21, 0x0e, 0x41, 0x04, 0x21, 0x0f, 0x20, 0x0e, 0x20, 0x0f, + 0x6a, 0x21, 0x10, 0x20, 0x0d, 0x20, 0x10, 0x10, 0xd5, 0x80, 0x80, 0x80, + 0x00, 0x41, 0x10, 0x21, 0x11, 0x20, 0x05, 0x20, 0x11, 0x6a, 0x21, 0x12, + 0x20, 0x12, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x0f, 0x0b, 0xa9, 0x01, + 0x01, 0x10, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x03, 0x41, + 0x10, 0x21, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6b, 0x21, 0x05, 0x20, 0x05, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, + 0x0c, 0x20, 0x05, 0x20, 0x01, 0x3b, 0x01, 0x0a, 0x20, 0x05, 0x20, 0x02, + 0x36, 0x02, 0x04, 0x20, 0x05, 0x28, 0x02, 0x04, 0x21, 0x06, 0x41, 0x01, + 0x21, 0x07, 0x20, 0x06, 0x20, 0x07, 0x36, 0x02, 0x00, 0x20, 0x05, 0x2f, + 0x01, 0x0a, 0x21, 0x08, 0x41, 0xff, 0xff, 0x03, 0x21, 0x09, 0x20, 0x08, + 0x20, 0x09, 0x71, 0x21, 0x0a, 0x20, 0x0a, 0x10, 0xc7, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x04, 0x21, 0x0c, 0x20, 0x0c, + 0x20, 0x0b, 0x3b, 0x01, 0x14, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x0d, + 0x20, 0x05, 0x28, 0x02, 0x04, 0x21, 0x0e, 0x41, 0x04, 0x21, 0x0f, 0x20, + 0x0e, 0x20, 0x0f, 0x6a, 0x21, 0x10, 0x20, 0x0d, 0x20, 0x10, 0x10, 0xd6, + 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x21, 0x11, 0x20, 0x05, 0x20, 0x11, + 0x6a, 0x21, 0x12, 0x20, 0x12, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x0f, + 0x0b, 0xb4, 0x04, 0x05, 0x05, 0x7f, 0x01, 0x7e, 0x1c, 0x7f, 0x01, 0x7e, + 0x17, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x03, 0x41, 0xe0, + 0x00, 0x21, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6b, 0x21, 0x05, 0x20, 0x05, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, 0x00, 0x36, 0x02, + 0x58, 0x20, 0x05, 0x20, 0x01, 0x36, 0x02, 0x54, 0x20, 0x05, 0x20, 0x02, + 0x36, 0x02, 0x50, 0x41, 0xc8, 0x00, 0x21, 0x06, 0x20, 0x05, 0x20, 0x06, + 0x6a, 0x21, 0x07, 0x42, 0x00, 0x21, 0x08, 0x20, 0x07, 0x20, 0x08, 0x37, + 0x03, 0x00, 0x41, 0xc0, 0x00, 0x21, 0x09, 0x20, 0x05, 0x20, 0x09, 0x6a, + 0x21, 0x0a, 0x20, 0x0a, 0x20, 0x08, 0x37, 0x03, 0x00, 0x20, 0x05, 0x20, + 0x08, 0x37, 0x03, 0x38, 0x20, 0x05, 0x28, 0x02, 0x54, 0x21, 0x0b, 0x41, + 0x00, 0x21, 0x0c, 0x20, 0x0c, 0x21, 0x0d, 0x20, 0x0b, 0x21, 0x0e, 0x20, + 0x0d, 0x20, 0x0e, 0x46, 0x21, 0x0f, 0x41, 0x01, 0x21, 0x10, 0x20, 0x0f, + 0x20, 0x10, 0x71, 0x21, 0x11, 0x02, 0x40, 0x02, 0x40, 0x20, 0x11, 0x45, + 0x0d, 0x00, 0x41, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x21, 0x12, 0x41, 0x1c, + 0x21, 0x13, 0x20, 0x12, 0x20, 0x13, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x21, + 0x14, 0x20, 0x05, 0x20, 0x14, 0x36, 0x02, 0x5c, 0x0c, 0x01, 0x0b, 0x20, + 0x05, 0x28, 0x02, 0x54, 0x21, 0x15, 0x20, 0x05, 0x28, 0x02, 0x50, 0x21, + 0x16, 0x41, 0x38, 0x21, 0x17, 0x20, 0x05, 0x20, 0x17, 0x6a, 0x21, 0x18, + 0x20, 0x18, 0x21, 0x19, 0x20, 0x15, 0x20, 0x16, 0x20, 0x19, 0x10, 0xca, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x1a, 0x20, 0x05, 0x20, 0x1a, 0x3b, 0x01, + 0x36, 0x20, 0x05, 0x2f, 0x01, 0x36, 0x21, 0x1b, 0x41, 0xff, 0xff, 0x03, + 0x21, 0x1c, 0x20, 0x1b, 0x20, 0x1c, 0x71, 0x21, 0x1d, 0x02, 0x40, 0x20, + 0x1d, 0x45, 0x0d, 0x00, 0x20, 0x05, 0x2f, 0x01, 0x36, 0x21, 0x1e, 0x41, + 0xff, 0xff, 0x03, 0x21, 0x1f, 0x20, 0x1e, 0x20, 0x1f, 0x71, 0x21, 0x20, + 0x41, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x21, 0x21, 0x20, 0x21, 0x20, 0x20, + 0x36, 0x02, 0x00, 0x41, 0x7f, 0x21, 0x22, 0x20, 0x05, 0x20, 0x22, 0x36, + 0x02, 0x5c, 0x0c, 0x01, 0x0b, 0x41, 0x28, 0x21, 0x23, 0x20, 0x05, 0x20, + 0x23, 0x6a, 0x21, 0x24, 0x42, 0x00, 0x21, 0x25, 0x20, 0x24, 0x20, 0x25, + 0x37, 0x03, 0x00, 0x41, 0x20, 0x21, 0x26, 0x20, 0x05, 0x20, 0x26, 0x6a, + 0x21, 0x27, 0x20, 0x27, 0x20, 0x25, 0x37, 0x03, 0x00, 0x41, 0x18, 0x21, + 0x28, 0x20, 0x05, 0x20, 0x28, 0x6a, 0x21, 0x29, 0x20, 0x29, 0x20, 0x25, + 0x37, 0x03, 0x00, 0x41, 0x10, 0x21, 0x2a, 0x20, 0x05, 0x20, 0x2a, 0x6a, + 0x21, 0x2b, 0x20, 0x2b, 0x20, 0x25, 0x37, 0x03, 0x00, 0x20, 0x05, 0x20, + 0x25, 0x37, 0x03, 0x08, 0x20, 0x05, 0x20, 0x25, 0x37, 0x03, 0x00, 0x20, + 0x05, 0x28, 0x02, 0x58, 0x21, 0x2c, 0x41, 0x38, 0x21, 0x2d, 0x20, 0x05, + 0x20, 0x2d, 0x6a, 0x21, 0x2e, 0x20, 0x2e, 0x21, 0x2f, 0x20, 0x2c, 0x20, + 0x2f, 0x10, 0xce, 0x80, 0x80, 0x80, 0x00, 0x21, 0x30, 0x20, 0x05, 0x20, + 0x30, 0x3b, 0x01, 0x36, 0x20, 0x05, 0x2f, 0x01, 0x36, 0x21, 0x31, 0x41, + 0xff, 0xff, 0x03, 0x21, 0x32, 0x20, 0x31, 0x20, 0x32, 0x71, 0x21, 0x33, + 0x02, 0x40, 0x20, 0x33, 0x45, 0x0d, 0x00, 0x20, 0x05, 0x2f, 0x01, 0x36, + 0x21, 0x34, 0x41, 0xff, 0xff, 0x03, 0x21, 0x35, 0x20, 0x34, 0x20, 0x35, + 0x71, 0x21, 0x36, 0x41, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x21, 0x37, 0x20, + 0x37, 0x20, 0x36, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x21, 0x38, 0x20, 0x05, + 0x20, 0x38, 0x36, 0x02, 0x5c, 0x0c, 0x01, 0x0b, 0x41, 0x00, 0x21, 0x39, + 0x20, 0x05, 0x20, 0x39, 0x36, 0x02, 0x5c, 0x0b, 0x20, 0x05, 0x28, 0x02, + 0x5c, 0x21, 0x3a, 0x41, 0xe0, 0x00, 0x21, 0x3b, 0x20, 0x05, 0x20, 0x3b, + 0x6a, 0x21, 0x3c, 0x20, 0x3c, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x3a, 0x0f, 0x0b, 0x6a, 0x01, 0x0a, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x02, 0x41, 0x10, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, + 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, + 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x08, + 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x05, 0x20, 0x04, 0x28, 0x02, 0x08, + 0x21, 0x06, 0x20, 0x05, 0x20, 0x06, 0x10, 0x88, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x07, 0x41, 0xff, 0xff, 0x03, 0x21, 0x08, 0x20, 0x07, 0x20, 0x08, + 0x71, 0x21, 0x09, 0x41, 0x10, 0x21, 0x0a, 0x20, 0x04, 0x20, 0x0a, 0x6a, + 0x21, 0x0b, 0x20, 0x0b, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x09, + 0x0f, 0x0b, 0x99, 0x04, 0x01, 0x35, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x21, 0x06, 0x41, 0xd0, 0x00, 0x21, 0x07, 0x20, 0x06, 0x20, 0x07, + 0x6b, 0x21, 0x08, 0x20, 0x08, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, + 0x08, 0x20, 0x00, 0x36, 0x02, 0x48, 0x20, 0x08, 0x20, 0x01, 0x36, 0x02, + 0x44, 0x20, 0x08, 0x20, 0x02, 0x36, 0x02, 0x40, 0x20, 0x08, 0x20, 0x03, + 0x36, 0x02, 0x3c, 0x20, 0x08, 0x20, 0x04, 0x36, 0x02, 0x38, 0x20, 0x08, + 0x20, 0x05, 0x36, 0x02, 0x34, 0x20, 0x08, 0x28, 0x02, 0x44, 0x21, 0x09, + 0x20, 0x08, 0x20, 0x09, 0x36, 0x02, 0x2c, 0x20, 0x08, 0x28, 0x02, 0x40, + 0x21, 0x0a, 0x20, 0x08, 0x20, 0x0a, 0x36, 0x02, 0x30, 0x41, 0x00, 0x21, + 0x0b, 0x20, 0x08, 0x20, 0x0b, 0x36, 0x02, 0x28, 0x41, 0x01, 0x21, 0x0c, + 0x20, 0x08, 0x20, 0x0c, 0x36, 0x02, 0x08, 0x41, 0x00, 0x21, 0x0d, 0x20, + 0x08, 0x20, 0x0d, 0x3b, 0x01, 0x06, 0x20, 0x08, 0x28, 0x02, 0x3c, 0x21, + 0x0e, 0x02, 0x40, 0x02, 0x40, 0x20, 0x0e, 0x45, 0x0d, 0x00, 0x41, 0x80, + 0xa8, 0x80, 0x80, 0x00, 0x21, 0x0f, 0x41, 0x32, 0x21, 0x10, 0x20, 0x0f, + 0x20, 0x10, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x21, 0x11, 0x20, 0x08, 0x20, + 0x11, 0x36, 0x02, 0x4c, 0x0c, 0x01, 0x0b, 0x20, 0x08, 0x28, 0x02, 0x38, + 0x21, 0x12, 0x20, 0x08, 0x28, 0x02, 0x34, 0x21, 0x13, 0x41, 0x10, 0x21, + 0x14, 0x20, 0x08, 0x20, 0x14, 0x6a, 0x21, 0x15, 0x20, 0x15, 0x21, 0x16, + 0x20, 0x12, 0x20, 0x13, 0x20, 0x16, 0x10, 0xca, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x17, 0x20, 0x08, 0x20, 0x17, 0x3b, 0x01, 0x0e, 0x20, 0x08, 0x2f, + 0x01, 0x0e, 0x21, 0x18, 0x41, 0xff, 0xff, 0x03, 0x21, 0x19, 0x20, 0x18, + 0x20, 0x19, 0x71, 0x21, 0x1a, 0x02, 0x40, 0x20, 0x1a, 0x45, 0x0d, 0x00, + 0x20, 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x1b, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x1c, 0x20, 0x1b, 0x20, 0x1c, 0x71, 0x21, 0x1d, 0x41, 0x80, 0xa8, 0x80, + 0x80, 0x00, 0x21, 0x1e, 0x20, 0x1e, 0x20, 0x1d, 0x36, 0x02, 0x00, 0x41, + 0x7f, 0x21, 0x1f, 0x20, 0x08, 0x20, 0x1f, 0x36, 0x02, 0x4c, 0x0c, 0x01, + 0x0b, 0x20, 0x08, 0x28, 0x02, 0x48, 0x21, 0x20, 0x20, 0x08, 0x28, 0x02, + 0x08, 0x21, 0x21, 0x20, 0x08, 0x2f, 0x01, 0x06, 0x21, 0x22, 0x41, 0x2c, + 0x21, 0x23, 0x20, 0x08, 0x20, 0x23, 0x6a, 0x21, 0x24, 0x20, 0x24, 0x21, + 0x25, 0x41, 0x10, 0x21, 0x26, 0x20, 0x08, 0x20, 0x26, 0x6a, 0x21, 0x27, + 0x20, 0x27, 0x21, 0x28, 0x41, 0x28, 0x21, 0x29, 0x20, 0x08, 0x20, 0x29, + 0x6a, 0x21, 0x2a, 0x20, 0x2a, 0x21, 0x2b, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x2c, 0x20, 0x22, 0x20, 0x2c, 0x71, 0x21, 0x2d, 0x20, 0x20, 0x20, 0x25, + 0x20, 0x21, 0x20, 0x2d, 0x20, 0x28, 0x20, 0x2b, 0x10, 0xd0, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x2e, 0x20, 0x08, 0x20, 0x2e, 0x3b, 0x01, 0x0e, 0x20, + 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x2f, 0x41, 0xff, 0xff, 0x03, 0x21, 0x30, + 0x20, 0x2f, 0x20, 0x30, 0x71, 0x21, 0x31, 0x02, 0x40, 0x20, 0x31, 0x45, + 0x0d, 0x00, 0x20, 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x32, 0x41, 0xff, 0xff, + 0x03, 0x21, 0x33, 0x20, 0x32, 0x20, 0x33, 0x71, 0x21, 0x34, 0x41, 0x80, + 0xa8, 0x80, 0x80, 0x00, 0x21, 0x35, 0x20, 0x35, 0x20, 0x34, 0x36, 0x02, + 0x00, 0x41, 0x7f, 0x21, 0x36, 0x20, 0x08, 0x20, 0x36, 0x36, 0x02, 0x4c, + 0x0c, 0x01, 0x0b, 0x20, 0x08, 0x28, 0x02, 0x28, 0x21, 0x37, 0x20, 0x08, + 0x20, 0x37, 0x36, 0x02, 0x4c, 0x0b, 0x20, 0x08, 0x28, 0x02, 0x4c, 0x21, + 0x38, 0x41, 0xd0, 0x00, 0x21, 0x39, 0x20, 0x08, 0x20, 0x39, 0x6a, 0x21, + 0x3a, 0x20, 0x3a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x38, 0x0f, + 0x0b, 0xb7, 0x01, 0x01, 0x10, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x06, 0x41, 0x20, 0x21, 0x07, 0x20, 0x06, 0x20, 0x07, 0x6b, 0x21, + 0x08, 0x20, 0x08, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x08, 0x20, + 0x00, 0x36, 0x02, 0x1c, 0x20, 0x08, 0x20, 0x01, 0x36, 0x02, 0x18, 0x20, + 0x08, 0x20, 0x02, 0x36, 0x02, 0x14, 0x20, 0x08, 0x20, 0x03, 0x3b, 0x01, + 0x12, 0x20, 0x08, 0x20, 0x04, 0x36, 0x02, 0x0c, 0x20, 0x08, 0x20, 0x05, + 0x36, 0x02, 0x08, 0x20, 0x08, 0x28, 0x02, 0x1c, 0x21, 0x09, 0x20, 0x08, + 0x28, 0x02, 0x18, 0x21, 0x0a, 0x20, 0x08, 0x28, 0x02, 0x14, 0x21, 0x0b, + 0x20, 0x08, 0x2f, 0x01, 0x12, 0x21, 0x0c, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x0d, 0x20, 0x0c, 0x20, 0x0d, 0x71, 0x21, 0x0e, 0x20, 0x08, 0x28, 0x02, + 0x0c, 0x21, 0x0f, 0x20, 0x08, 0x28, 0x02, 0x08, 0x21, 0x10, 0x20, 0x09, + 0x20, 0x0a, 0x20, 0x0b, 0x20, 0x0e, 0x20, 0x0f, 0x20, 0x10, 0x10, 0x89, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x11, 0x41, 0xff, 0xff, 0x03, 0x21, 0x12, + 0x20, 0x11, 0x20, 0x12, 0x71, 0x21, 0x13, 0x41, 0x20, 0x21, 0x14, 0x20, + 0x08, 0x20, 0x14, 0x6a, 0x21, 0x15, 0x20, 0x15, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x13, 0x0f, 0x0b, 0xff, 0x05, 0x01, 0x50, 0x7f, 0x23, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x06, 0x41, 0xd0, 0x00, 0x21, 0x07, + 0x20, 0x06, 0x20, 0x07, 0x6b, 0x21, 0x08, 0x20, 0x08, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x08, 0x20, 0x00, 0x36, 0x02, 0x48, 0x20, 0x08, + 0x20, 0x01, 0x36, 0x02, 0x44, 0x20, 0x08, 0x20, 0x02, 0x36, 0x02, 0x40, + 0x20, 0x08, 0x20, 0x03, 0x36, 0x02, 0x3c, 0x20, 0x08, 0x20, 0x04, 0x36, + 0x02, 0x38, 0x20, 0x08, 0x20, 0x05, 0x36, 0x02, 0x34, 0x20, 0x08, 0x28, + 0x02, 0x44, 0x21, 0x09, 0x20, 0x08, 0x20, 0x09, 0x36, 0x02, 0x2c, 0x20, + 0x08, 0x28, 0x02, 0x40, 0x21, 0x0a, 0x20, 0x08, 0x20, 0x0a, 0x36, 0x02, + 0x30, 0x41, 0x00, 0x21, 0x0b, 0x20, 0x08, 0x20, 0x0b, 0x36, 0x02, 0x28, + 0x41, 0x01, 0x21, 0x0c, 0x20, 0x08, 0x20, 0x0c, 0x36, 0x02, 0x08, 0x41, + 0x00, 0x21, 0x0d, 0x20, 0x08, 0x20, 0x0d, 0x3b, 0x01, 0x06, 0x20, 0x08, + 0x28, 0x02, 0x3c, 0x21, 0x0e, 0x02, 0x40, 0x02, 0x40, 0x20, 0x0e, 0x45, + 0x0d, 0x00, 0x41, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x21, 0x0f, 0x41, 0x32, + 0x21, 0x10, 0x20, 0x0f, 0x20, 0x10, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x21, + 0x11, 0x20, 0x08, 0x20, 0x11, 0x36, 0x02, 0x4c, 0x0c, 0x01, 0x0b, 0x20, + 0x08, 0x28, 0x02, 0x38, 0x21, 0x12, 0x41, 0x00, 0x21, 0x13, 0x20, 0x12, + 0x21, 0x14, 0x20, 0x13, 0x21, 0x15, 0x20, 0x14, 0x20, 0x15, 0x47, 0x21, + 0x16, 0x41, 0x01, 0x21, 0x17, 0x20, 0x16, 0x20, 0x17, 0x71, 0x21, 0x18, + 0x02, 0x40, 0x20, 0x18, 0x0d, 0x00, 0x20, 0x08, 0x28, 0x02, 0x48, 0x21, + 0x19, 0x20, 0x08, 0x28, 0x02, 0x44, 0x21, 0x1a, 0x20, 0x08, 0x28, 0x02, + 0x40, 0x21, 0x1b, 0x20, 0x08, 0x28, 0x02, 0x3c, 0x21, 0x1c, 0x20, 0x19, + 0x20, 0x1a, 0x20, 0x1b, 0x20, 0x1c, 0x10, 0xc6, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x1d, 0x20, 0x08, 0x20, 0x1d, 0x36, 0x02, 0x4c, 0x0c, 0x01, 0x0b, + 0x20, 0x08, 0x28, 0x02, 0x38, 0x21, 0x1e, 0x20, 0x08, 0x28, 0x02, 0x34, + 0x21, 0x1f, 0x20, 0x1f, 0x28, 0x02, 0x00, 0x21, 0x20, 0x41, 0x10, 0x21, + 0x21, 0x20, 0x08, 0x20, 0x21, 0x6a, 0x21, 0x22, 0x20, 0x22, 0x21, 0x23, + 0x20, 0x1e, 0x20, 0x20, 0x20, 0x23, 0x10, 0xca, 0x80, 0x80, 0x80, 0x00, + 0x21, 0x24, 0x20, 0x08, 0x20, 0x24, 0x3b, 0x01, 0x0e, 0x20, 0x08, 0x2f, + 0x01, 0x0e, 0x21, 0x25, 0x41, 0xff, 0xff, 0x03, 0x21, 0x26, 0x20, 0x25, + 0x20, 0x26, 0x71, 0x21, 0x27, 0x02, 0x40, 0x20, 0x27, 0x45, 0x0d, 0x00, + 0x20, 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x28, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x29, 0x20, 0x28, 0x20, 0x29, 0x71, 0x21, 0x2a, 0x41, 0x80, 0xa8, 0x80, + 0x80, 0x00, 0x21, 0x2b, 0x20, 0x2b, 0x20, 0x2a, 0x36, 0x02, 0x00, 0x41, + 0x7f, 0x21, 0x2c, 0x20, 0x08, 0x20, 0x2c, 0x36, 0x02, 0x4c, 0x0c, 0x01, + 0x0b, 0x20, 0x08, 0x28, 0x02, 0x48, 0x21, 0x2d, 0x20, 0x08, 0x28, 0x02, + 0x08, 0x21, 0x2e, 0x20, 0x08, 0x2f, 0x01, 0x06, 0x21, 0x2f, 0x41, 0x2c, + 0x21, 0x30, 0x20, 0x08, 0x20, 0x30, 0x6a, 0x21, 0x31, 0x20, 0x31, 0x21, + 0x32, 0x41, 0x10, 0x21, 0x33, 0x20, 0x08, 0x20, 0x33, 0x6a, 0x21, 0x34, + 0x20, 0x34, 0x21, 0x35, 0x41, 0x28, 0x21, 0x36, 0x20, 0x08, 0x20, 0x36, + 0x6a, 0x21, 0x37, 0x20, 0x37, 0x21, 0x38, 0x41, 0xff, 0xff, 0x03, 0x21, + 0x39, 0x20, 0x2f, 0x20, 0x39, 0x71, 0x21, 0x3a, 0x20, 0x2d, 0x20, 0x32, + 0x20, 0x2e, 0x20, 0x3a, 0x20, 0x35, 0x20, 0x38, 0x10, 0xd2, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x3b, 0x20, 0x08, 0x20, 0x3b, 0x3b, 0x01, 0x0e, 0x20, + 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x3c, 0x41, 0xff, 0xff, 0x03, 0x21, 0x3d, + 0x20, 0x3c, 0x20, 0x3d, 0x71, 0x21, 0x3e, 0x02, 0x40, 0x20, 0x3e, 0x45, + 0x0d, 0x00, 0x20, 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x3f, 0x41, 0xff, 0xff, + 0x03, 0x21, 0x40, 0x20, 0x3f, 0x20, 0x40, 0x71, 0x21, 0x41, 0x41, 0x80, + 0xa8, 0x80, 0x80, 0x00, 0x21, 0x42, 0x20, 0x42, 0x20, 0x41, 0x36, 0x02, + 0x00, 0x41, 0x7f, 0x21, 0x43, 0x20, 0x08, 0x20, 0x43, 0x36, 0x02, 0x4c, + 0x0c, 0x01, 0x0b, 0x20, 0x08, 0x28, 0x02, 0x38, 0x21, 0x44, 0x20, 0x08, + 0x28, 0x02, 0x34, 0x21, 0x45, 0x41, 0x10, 0x21, 0x46, 0x20, 0x08, 0x20, + 0x46, 0x6a, 0x21, 0x47, 0x20, 0x47, 0x21, 0x48, 0x20, 0x48, 0x20, 0x44, + 0x20, 0x45, 0x10, 0xc9, 0x80, 0x80, 0x80, 0x00, 0x21, 0x49, 0x20, 0x08, + 0x20, 0x49, 0x3b, 0x01, 0x0e, 0x20, 0x08, 0x2f, 0x01, 0x0e, 0x21, 0x4a, + 0x41, 0xff, 0xff, 0x03, 0x21, 0x4b, 0x20, 0x4a, 0x20, 0x4b, 0x71, 0x21, + 0x4c, 0x02, 0x40, 0x20, 0x4c, 0x45, 0x0d, 0x00, 0x20, 0x08, 0x2f, 0x01, + 0x0e, 0x21, 0x4d, 0x41, 0xff, 0xff, 0x03, 0x21, 0x4e, 0x20, 0x4d, 0x20, + 0x4e, 0x71, 0x21, 0x4f, 0x41, 0x80, 0xa8, 0x80, 0x80, 0x00, 0x21, 0x50, + 0x20, 0x50, 0x20, 0x4f, 0x36, 0x02, 0x00, 0x41, 0x7f, 0x21, 0x51, 0x20, + 0x08, 0x20, 0x51, 0x36, 0x02, 0x4c, 0x0c, 0x01, 0x0b, 0x20, 0x08, 0x28, + 0x02, 0x28, 0x21, 0x52, 0x20, 0x08, 0x20, 0x52, 0x36, 0x02, 0x4c, 0x0b, + 0x20, 0x08, 0x28, 0x02, 0x4c, 0x21, 0x53, 0x41, 0xd0, 0x00, 0x21, 0x54, + 0x20, 0x08, 0x20, 0x54, 0x6a, 0x21, 0x55, 0x20, 0x55, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x53, 0x0f, 0x0b, 0xb7, 0x01, 0x01, 0x10, 0x7f, + 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x06, 0x41, 0x20, 0x21, 0x07, + 0x20, 0x06, 0x20, 0x07, 0x6b, 0x21, 0x08, 0x20, 0x08, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x20, 0x08, 0x20, 0x00, 0x36, 0x02, 0x1c, 0x20, 0x08, + 0x20, 0x01, 0x36, 0x02, 0x18, 0x20, 0x08, 0x20, 0x02, 0x36, 0x02, 0x14, + 0x20, 0x08, 0x20, 0x03, 0x3b, 0x01, 0x12, 0x20, 0x08, 0x20, 0x04, 0x36, + 0x02, 0x0c, 0x20, 0x08, 0x20, 0x05, 0x36, 0x02, 0x08, 0x20, 0x08, 0x28, + 0x02, 0x1c, 0x21, 0x09, 0x20, 0x08, 0x28, 0x02, 0x18, 0x21, 0x0a, 0x20, + 0x08, 0x28, 0x02, 0x14, 0x21, 0x0b, 0x20, 0x08, 0x2f, 0x01, 0x12, 0x21, + 0x0c, 0x41, 0xff, 0xff, 0x03, 0x21, 0x0d, 0x20, 0x0c, 0x20, 0x0d, 0x71, + 0x21, 0x0e, 0x20, 0x08, 0x28, 0x02, 0x0c, 0x21, 0x0f, 0x20, 0x08, 0x28, + 0x02, 0x08, 0x21, 0x10, 0x20, 0x09, 0x20, 0x0a, 0x20, 0x0b, 0x20, 0x0e, + 0x20, 0x0f, 0x20, 0x10, 0x10, 0x8a, 0x80, 0x80, 0x80, 0x00, 0x21, 0x11, + 0x41, 0xff, 0xff, 0x03, 0x21, 0x12, 0x20, 0x11, 0x20, 0x12, 0x71, 0x21, + 0x13, 0x41, 0x20, 0x21, 0x14, 0x20, 0x08, 0x20, 0x14, 0x6a, 0x21, 0x15, + 0x20, 0x15, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x13, 0x0f, 0x0b, + 0x82, 0x04, 0x01, 0x39, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x03, 0x41, 0x30, 0x21, 0x04, 0x20, 0x03, 0x20, 0x04, 0x6b, 0x21, 0x05, + 0x20, 0x05, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x05, 0x20, 0x00, + 0x36, 0x02, 0x28, 0x20, 0x05, 0x20, 0x01, 0x36, 0x02, 0x24, 0x20, 0x05, + 0x20, 0x02, 0x36, 0x02, 0x20, 0x41, 0x7f, 0x21, 0x06, 0x20, 0x05, 0x20, + 0x06, 0x36, 0x02, 0x1c, 0x20, 0x05, 0x28, 0x02, 0x28, 0x21, 0x07, 0x41, + 0x01, 0x21, 0x08, 0x20, 0x08, 0x21, 0x09, 0x20, 0x07, 0x21, 0x0a, 0x20, + 0x09, 0x20, 0x0a, 0x46, 0x21, 0x0b, 0x41, 0x01, 0x21, 0x0c, 0x20, 0x0b, + 0x20, 0x0c, 0x71, 0x21, 0x0d, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x0d, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x0e, 0x20, 0x05, 0x20, 0x0e, + 0x36, 0x02, 0x10, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x28, 0x21, + 0x0f, 0x41, 0x02, 0x21, 0x10, 0x20, 0x10, 0x21, 0x11, 0x20, 0x0f, 0x21, + 0x12, 0x20, 0x11, 0x20, 0x12, 0x46, 0x21, 0x13, 0x41, 0x01, 0x21, 0x14, + 0x20, 0x13, 0x20, 0x14, 0x71, 0x21, 0x15, 0x02, 0x40, 0x02, 0x40, 0x20, + 0x15, 0x45, 0x0d, 0x00, 0x41, 0x01, 0x21, 0x16, 0x20, 0x05, 0x20, 0x16, + 0x36, 0x02, 0x10, 0x0c, 0x01, 0x0b, 0x41, 0x32, 0x21, 0x17, 0x20, 0x05, + 0x20, 0x17, 0x36, 0x02, 0x2c, 0x0c, 0x02, 0x0b, 0x0b, 0x20, 0x05, 0x28, + 0x02, 0x24, 0x21, 0x18, 0x41, 0x05, 0x21, 0x19, 0x20, 0x19, 0x21, 0x1a, + 0x20, 0x18, 0x21, 0x1b, 0x20, 0x1a, 0x20, 0x1b, 0x46, 0x21, 0x1c, 0x41, + 0x01, 0x21, 0x1d, 0x20, 0x1c, 0x20, 0x1d, 0x71, 0x21, 0x1e, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x1e, 0x45, 0x0d, 0x00, 0x41, 0x00, 0x21, 0x1f, 0x20, + 0x05, 0x20, 0x1f, 0x36, 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, + 0x02, 0x24, 0x21, 0x20, 0x41, 0x06, 0x21, 0x21, 0x20, 0x21, 0x21, 0x22, + 0x20, 0x20, 0x21, 0x23, 0x20, 0x22, 0x20, 0x23, 0x46, 0x21, 0x24, 0x41, + 0x01, 0x21, 0x25, 0x20, 0x24, 0x20, 0x25, 0x71, 0x21, 0x26, 0x02, 0x40, + 0x02, 0x40, 0x20, 0x26, 0x45, 0x0d, 0x00, 0x41, 0x01, 0x21, 0x27, 0x20, + 0x05, 0x20, 0x27, 0x36, 0x02, 0x0c, 0x0c, 0x01, 0x0b, 0x41, 0x32, 0x21, + 0x28, 0x20, 0x05, 0x20, 0x28, 0x36, 0x02, 0x2c, 0x0c, 0x02, 0x0b, 0x0b, + 0x20, 0x05, 0x28, 0x02, 0x1c, 0x21, 0x29, 0x20, 0x05, 0x28, 0x02, 0x10, + 0x21, 0x2a, 0x20, 0x05, 0x28, 0x02, 0x0c, 0x21, 0x2b, 0x41, 0x18, 0x21, + 0x2c, 0x20, 0x05, 0x20, 0x2c, 0x6a, 0x21, 0x2d, 0x20, 0x2d, 0x21, 0x2e, + 0x20, 0x29, 0x20, 0x2a, 0x20, 0x2b, 0x20, 0x2e, 0x10, 0xd4, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x2f, 0x20, 0x05, 0x20, 0x2f, 0x3b, 0x01, 0x16, 0x20, + 0x05, 0x2f, 0x01, 0x16, 0x21, 0x30, 0x41, 0xff, 0xff, 0x03, 0x21, 0x31, + 0x20, 0x30, 0x20, 0x31, 0x71, 0x21, 0x32, 0x02, 0x40, 0x20, 0x32, 0x45, + 0x0d, 0x00, 0x20, 0x05, 0x2f, 0x01, 0x16, 0x21, 0x33, 0x41, 0xff, 0xff, + 0x03, 0x21, 0x34, 0x20, 0x33, 0x20, 0x34, 0x71, 0x21, 0x35, 0x41, 0x80, + 0xa8, 0x80, 0x80, 0x00, 0x21, 0x36, 0x20, 0x36, 0x20, 0x35, 0x36, 0x02, + 0x00, 0x41, 0x7f, 0x21, 0x37, 0x20, 0x05, 0x20, 0x37, 0x36, 0x02, 0x2c, + 0x0c, 0x01, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x18, 0x21, 0x38, 0x20, 0x05, + 0x20, 0x38, 0x36, 0x02, 0x2c, 0x0b, 0x20, 0x05, 0x28, 0x02, 0x2c, 0x21, + 0x39, 0x41, 0x30, 0x21, 0x3a, 0x20, 0x05, 0x20, 0x3a, 0x6a, 0x21, 0x3b, + 0x20, 0x3b, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x39, 0x0f, 0x0b, + 0x8a, 0x01, 0x01, 0x0c, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x04, 0x41, 0x10, 0x21, 0x05, 0x20, 0x04, 0x20, 0x05, 0x6b, 0x21, 0x06, + 0x20, 0x06, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x06, 0x20, 0x00, + 0x36, 0x02, 0x0c, 0x20, 0x06, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x06, + 0x20, 0x02, 0x36, 0x02, 0x04, 0x20, 0x06, 0x20, 0x03, 0x36, 0x02, 0x00, + 0x20, 0x06, 0x28, 0x02, 0x0c, 0x21, 0x07, 0x20, 0x06, 0x28, 0x02, 0x08, + 0x21, 0x08, 0x20, 0x06, 0x28, 0x02, 0x04, 0x21, 0x09, 0x20, 0x06, 0x28, + 0x02, 0x00, 0x21, 0x0a, 0x20, 0x07, 0x20, 0x08, 0x20, 0x09, 0x20, 0x0a, + 0x10, 0x8b, 0x80, 0x80, 0x80, 0x00, 0x21, 0x0b, 0x41, 0xff, 0xff, 0x03, + 0x21, 0x0c, 0x20, 0x0b, 0x20, 0x0c, 0x71, 0x21, 0x0d, 0x41, 0x10, 0x21, + 0x0e, 0x20, 0x06, 0x20, 0x0e, 0x6a, 0x21, 0x0f, 0x20, 0x0f, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x0d, 0x0f, 0x0b, 0x83, 0x02, 0x01, 0x1d, + 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0x10, 0x21, + 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, + 0x04, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, + 0x05, 0x20, 0x05, 0x10, 0xc8, 0x80, 0x80, 0x80, 0x00, 0x21, 0x06, 0x20, + 0x04, 0x20, 0x06, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, + 0x07, 0x41, 0x80, 0x80, 0x80, 0x78, 0x21, 0x08, 0x20, 0x07, 0x20, 0x08, + 0x71, 0x21, 0x09, 0x41, 0x18, 0x21, 0x0a, 0x20, 0x09, 0x20, 0x0a, 0x76, + 0x21, 0x0b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x0c, 0x20, 0x0c, 0x20, + 0x0b, 0x3a, 0x00, 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x0d, 0x41, + 0x80, 0x80, 0xfc, 0x07, 0x21, 0x0e, 0x20, 0x0d, 0x20, 0x0e, 0x71, 0x21, + 0x0f, 0x41, 0x10, 0x21, 0x10, 0x20, 0x0f, 0x20, 0x10, 0x76, 0x21, 0x11, + 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x12, 0x20, 0x12, 0x20, 0x11, 0x3a, + 0x00, 0x01, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x13, 0x41, 0x80, 0xfe, + 0x03, 0x21, 0x14, 0x20, 0x13, 0x20, 0x14, 0x71, 0x21, 0x15, 0x41, 0x08, + 0x21, 0x16, 0x20, 0x15, 0x20, 0x16, 0x76, 0x21, 0x17, 0x20, 0x04, 0x28, + 0x02, 0x08, 0x21, 0x18, 0x20, 0x18, 0x20, 0x17, 0x3a, 0x00, 0x02, 0x20, + 0x04, 0x28, 0x02, 0x0c, 0x21, 0x19, 0x41, 0xff, 0x01, 0x21, 0x1a, 0x20, + 0x19, 0x20, 0x1a, 0x71, 0x21, 0x1b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, + 0x1c, 0x20, 0x1c, 0x20, 0x1b, 0x3a, 0x00, 0x03, 0x41, 0x10, 0x21, 0x1d, + 0x20, 0x04, 0x20, 0x1d, 0x6a, 0x21, 0x1e, 0x20, 0x1e, 0x24, 0x80, 0x80, + 0x80, 0x80, 0x00, 0x0f, 0x0b, 0xd9, 0x03, 0x01, 0x35, 0x7f, 0x23, 0x80, + 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0x10, 0x21, 0x03, 0x20, 0x02, + 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, + 0x00, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x01, + 0x36, 0x02, 0x08, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x05, 0x20, 0x05, + 0x2f, 0x01, 0x00, 0x21, 0x06, 0x41, 0xff, 0xff, 0x03, 0x21, 0x07, 0x20, + 0x06, 0x20, 0x07, 0x71, 0x21, 0x08, 0x20, 0x08, 0x10, 0xc7, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x09, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x0a, 0x20, + 0x0a, 0x20, 0x09, 0x3b, 0x01, 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, + 0x0b, 0x20, 0x0b, 0x2f, 0x01, 0x02, 0x21, 0x0c, 0x41, 0xff, 0xff, 0x03, + 0x21, 0x0d, 0x20, 0x0c, 0x20, 0x0d, 0x71, 0x21, 0x0e, 0x20, 0x0e, 0x10, + 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, 0x0f, 0x20, 0x04, 0x28, 0x02, 0x08, + 0x21, 0x10, 0x20, 0x10, 0x20, 0x0f, 0x3b, 0x01, 0x02, 0x20, 0x04, 0x28, + 0x02, 0x0c, 0x21, 0x11, 0x20, 0x11, 0x2f, 0x01, 0x04, 0x21, 0x12, 0x41, + 0xff, 0xff, 0x03, 0x21, 0x13, 0x20, 0x12, 0x20, 0x13, 0x71, 0x21, 0x14, + 0x20, 0x14, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, 0x15, 0x20, 0x04, + 0x28, 0x02, 0x08, 0x21, 0x16, 0x20, 0x16, 0x20, 0x15, 0x3b, 0x01, 0x04, + 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x17, 0x20, 0x17, 0x2f, 0x01, 0x06, + 0x21, 0x18, 0x41, 0xff, 0xff, 0x03, 0x21, 0x19, 0x20, 0x18, 0x20, 0x19, + 0x71, 0x21, 0x1a, 0x20, 0x1a, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x1b, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x1c, 0x20, 0x1c, 0x20, 0x1b, + 0x3b, 0x01, 0x06, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x1d, 0x20, 0x1d, + 0x2f, 0x01, 0x08, 0x21, 0x1e, 0x41, 0xff, 0xff, 0x03, 0x21, 0x1f, 0x20, + 0x1e, 0x20, 0x1f, 0x71, 0x21, 0x20, 0x20, 0x20, 0x10, 0xc7, 0x80, 0x80, + 0x80, 0x00, 0x21, 0x21, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x22, 0x20, + 0x22, 0x20, 0x21, 0x3b, 0x01, 0x08, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, + 0x23, 0x20, 0x23, 0x2f, 0x01, 0x0a, 0x21, 0x24, 0x41, 0xff, 0xff, 0x03, + 0x21, 0x25, 0x20, 0x24, 0x20, 0x25, 0x71, 0x21, 0x26, 0x20, 0x26, 0x10, + 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, 0x27, 0x20, 0x04, 0x28, 0x02, 0x08, + 0x21, 0x28, 0x20, 0x28, 0x20, 0x27, 0x3b, 0x01, 0x0a, 0x20, 0x04, 0x28, + 0x02, 0x0c, 0x21, 0x29, 0x20, 0x29, 0x2f, 0x01, 0x0c, 0x21, 0x2a, 0x41, + 0xff, 0xff, 0x03, 0x21, 0x2b, 0x20, 0x2a, 0x20, 0x2b, 0x71, 0x21, 0x2c, + 0x20, 0x2c, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, 0x2d, 0x20, 0x04, + 0x28, 0x02, 0x08, 0x21, 0x2e, 0x20, 0x2e, 0x20, 0x2d, 0x3b, 0x01, 0x0c, + 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x2f, 0x20, 0x2f, 0x2f, 0x01, 0x0e, + 0x21, 0x30, 0x41, 0xff, 0xff, 0x03, 0x21, 0x31, 0x20, 0x30, 0x20, 0x31, + 0x71, 0x21, 0x32, 0x20, 0x32, 0x10, 0xc7, 0x80, 0x80, 0x80, 0x00, 0x21, + 0x33, 0x20, 0x04, 0x28, 0x02, 0x08, 0x21, 0x34, 0x20, 0x34, 0x20, 0x33, + 0x3b, 0x01, 0x0e, 0x41, 0x10, 0x21, 0x35, 0x20, 0x04, 0x20, 0x35, 0x6a, + 0x21, 0x36, 0x20, 0x36, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x0f, 0x0b, + 0x0b, 0xfb, 0x17, 0x02, 0x00, 0x41, 0x80, 0x08, 0x0b, 0x80, 0x16, 0x2d, + 0x2b, 0x20, 0x20, 0x20, 0x30, 0x58, 0x30, 0x78, 0x00, 0x2d, 0x30, 0x58, + 0x2b, 0x30, 0x58, 0x20, 0x30, 0x58, 0x2d, 0x30, 0x78, 0x2b, 0x30, 0x78, + 0x20, 0x30, 0x78, 0x00, 0x25, 0x73, 0x00, 0x73, 0x6f, 0x63, 0x6b, 0x61, + 0x64, 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x61, + 0x64, 0x64, 0x72, 0x5f, 0x69, 0x6e, 0x29, 0x20, 0x3c, 0x3d, 0x20, 0x61, + 0x64, 0x64, 0x72, 0x6c, 0x65, 0x6e, 0x00, 0x73, 0x69, 0x7a, 0x65, 0x6f, + 0x66, 0x28, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x6f, 0x63, + 0x6b, 0x61, 0x64, 0x64, 0x72, 0x5f, 0x69, 0x6e, 0x36, 0x29, 0x20, 0x3c, + 0x3d, 0x20, 0x61, 0x64, 0x64, 0x72, 0x6c, 0x65, 0x6e, 0x00, 0x6e, 0x61, + 0x6e, 0x00, 0x69, 0x6e, 0x66, 0x00, 0x69, 0x6e, 0x63, 0x2f, 0x77, 0x61, + 0x73, 0x69, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x65, 0x78, + 0x74, 0x2e, 0x63, 0x00, 0x4e, 0x41, 0x4e, 0x00, 0x49, 0x4e, 0x46, 0x00, + 0x2e, 0x00, 0x28, 0x6e, 0x75, 0x6c, 0x6c, 0x29, 0x00, 0x5b, 0x77, 0x61, + 0x73, 0x6d, 0x2d, 0x6d, 0x6f, 0x64, 0x5d, 0x20, 0x6c, 0x65, 0x6e, 0x20, + 0x3d, 0x20, 0x30, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x0a, 0x00, 0x5b, + 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, 0x6f, 0x64, 0x5d, 0x20, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6c, 0x6f, + 0x73, 0x65, 0x64, 0x0a, 0x00, 0x5b, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, + 0x6f, 0x64, 0x5d, 0x20, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x25, 0x64, + 0x0a, 0x00, 0x5b, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, 0x6f, 0x64, 0x5d, + 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x20, 0x3d, 0x20, 0x25, 0x64, 0x0a, 0x00, + 0x5b, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, 0x6f, 0x64, 0x5d, 0x20, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x20, 0x72, 0x63, 0x20, 0x3d, 0x20, + 0x25, 0x64, 0x0a, 0x00, 0x5b, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, 0x6f, + 0x64, 0x5d, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x72, 0x63, 0x20, 0x3d, + 0x20, 0x25, 0x64, 0x0a, 0x00, 0x5b, 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, + 0x6f, 0x64, 0x5d, 0x20, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6e, + 0x67, 0x20, 0x48, 0x54, 0x54, 0x50, 0x20, 0x47, 0x45, 0x54, 0x20, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x30, 0x2e, + 0x32, 0x2e, 0x31, 0x30, 0x3a, 0x38, 0x30, 0x30, 0x30, 0x2f, 0x0a, 0x00, + 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6c, + 0x6f, 0x6e, 0x67, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x69, 0x73, 0x20, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x20, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x2e, 0x0a, 0x54, 0x6f, 0x20, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x69, 0x74, 0x2c, 0x20, 0x61, 0x64, 0x64, 0x20, 0x2d, + 0x6c, 0x63, 0x2d, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x63, 0x61, 0x6e, + 0x2d, 0x6c, 0x6f, 0x6e, 0x67, 0x2d, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, + 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x6b, + 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x0a, 0x00, 0x41, + 0x73, 0x73, 0x65, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x61, 0x69, + 0x6c, 0x65, 0x64, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x28, 0x25, 0x73, 0x3a, + 0x20, 0x25, 0x73, 0x3a, 0x20, 0x25, 0x64, 0x29, 0x0a, 0x00, 0x47, 0x45, + 0x54, 0x20, 0x2f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, 0x30, + 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x3a, 0x20, 0x31, 0x39, 0x32, 0x2e, + 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x5b, + 0x77, 0x61, 0x73, 0x6d, 0x2d, 0x6d, 0x6f, 0x64, 0x5d, 0x20, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x0a, 0x0a, 0x00, 0x53, 0x75, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x00, 0x49, 0x6c, 0x6c, 0x65, 0x67, 0x61, + 0x6c, 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x73, 0x65, 0x71, 0x75, 0x65, + 0x6e, 0x63, 0x65, 0x00, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x00, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x61, 0x20, + 0x74, 0x74, 0x79, 0x00, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x20, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x00, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x64, 0x00, 0x4e, 0x6f, + 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6f, + 0x72, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x00, + 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x00, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x65, 0x78, 0x69, + 0x73, 0x74, 0x73, 0x00, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x74, 0x6f, + 0x6f, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x00, 0x4e, 0x6f, + 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x6c, 0x65, 0x66, 0x74, 0x20, + 0x6f, 0x6e, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x00, 0x4f, 0x75, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x00, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x62, 0x75, 0x73, + 0x79, 0x00, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x65, + 0x64, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x63, 0x61, 0x6c, + 0x6c, 0x00, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0x69, 0x6c, 0x79, 0x20, 0x75, + 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x73, 0x65, 0x65, 0x6b, 0x00, + 0x43, 0x72, 0x6f, 0x73, 0x73, 0x2d, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x00, 0x52, 0x65, 0x61, 0x64, 0x2d, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x00, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x00, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, + 0x65, 0x73, 0x65, 0x74, 0x20, 0x62, 0x79, 0x20, 0x70, 0x65, 0x65, 0x72, + 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x69, 0x6d, 0x65, 0x64, 0x20, 0x6f, 0x75, 0x74, 0x00, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x66, 0x75, + 0x73, 0x65, 0x64, 0x00, 0x48, 0x6f, 0x73, 0x74, 0x20, 0x69, 0x73, 0x20, + 0x75, 0x6e, 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x00, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x75, + 0x73, 0x65, 0x00, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x6e, 0x20, 0x70, 0x69, + 0x70, 0x65, 0x00, 0x49, 0x2f, 0x4f, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x00, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x20, 0x6f, 0x72, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x00, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x61, 0x20, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x00, 0x49, 0x73, + 0x20, 0x61, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, + 0x00, 0x54, 0x65, 0x78, 0x74, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x62, + 0x75, 0x73, 0x79, 0x00, 0x45, 0x78, 0x65, 0x63, 0x20, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, 0x49, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x00, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, + 0x6c, 0x69, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x6c, 0x6f, 0x6e, + 0x67, 0x00, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x69, 0x63, 0x20, 0x6c, + 0x69, 0x6e, 0x6b, 0x20, 0x6c, 0x6f, 0x6f, 0x70, 0x00, 0x46, 0x69, 0x6c, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x6c, 0x6f, + 0x6e, 0x67, 0x00, 0x54, 0x6f, 0x6f, 0x20, 0x6d, 0x61, 0x6e, 0x79, 0x20, + 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x20, 0x69, + 0x6e, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x00, 0x4e, 0x6f, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x00, 0x42, 0x61, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x4e, + 0x6f, 0x20, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x00, 0x42, 0x61, 0x64, 0x20, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x00, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x6f, + 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x00, 0x54, 0x6f, 0x6f, 0x20, 0x6d, + 0x61, 0x6e, 0x79, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x00, 0x4e, 0x6f, + 0x20, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x6c, 0x65, 0x00, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x20, 0x64, 0x65, 0x61, 0x64, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x77, + 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x63, 0x63, 0x75, 0x72, 0x00, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x72, 0x65, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x50, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x20, + 0x64, 0x69, 0x65, 0x64, 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x00, + 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x65, 0x64, + 0x00, 0x4e, 0x6f, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, + 0x6f, 0x66, 0x20, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x20, 0x74, + 0x79, 0x70, 0x65, 0x00, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x00, 0x4c, + 0x69, 0x6e, 0x6b, 0x20, 0x68, 0x61, 0x73, 0x20, 0x62, 0x65, 0x65, 0x6e, + 0x20, 0x73, 0x65, 0x76, 0x65, 0x72, 0x65, 0x64, 0x00, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x00, + 0x42, 0x61, 0x64, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, + 0x4e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x00, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x00, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x6c, 0x61, 0x72, 0x67, 0x65, 0x00, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x77, 0x72, 0x6f, + 0x6e, 0x67, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x00, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x00, 0x4e, 0x6f, 0x74, 0x20, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x20, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x20, 0x6e, 0x6f, + 0x74, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, + 0x62, 0x79, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x00, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x4e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x73, 0x20, 0x64, 0x6f, 0x77, + 0x6e, 0x00, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x75, 0x6e, + 0x72, 0x65, 0x61, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x73, + 0x65, 0x74, 0x20, 0x62, 0x79, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x00, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x61, 0x62, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x00, 0x4e, 0x6f, 0x20, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x53, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x00, 0x53, 0x6f, 0x63, 0x6b, 0x65, + 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x00, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x69, 0x6e, + 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x00, 0x4f, 0x70, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x70, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x00, 0x53, 0x74, 0x61, 0x6c, + 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x00, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x20, 0x65, 0x78, 0x63, 0x65, + 0x65, 0x64, 0x65, 0x64, 0x00, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x68, 0x6f, + 0x70, 0x20, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x65, 0x64, 0x00, + 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, + 0x20, 0x69, 0x6e, 0x73, 0x75, 0x66, 0x66, 0x69, 0x63, 0x69, 0x65, 0x6e, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x75, 0x02, 0x4e, 0x00, 0xd6, 0x01, 0xe2, + 0x04, 0xb9, 0x04, 0x18, 0x01, 0x8e, 0x05, 0xed, 0x02, 0x16, 0x04, 0xf2, + 0x00, 0x97, 0x03, 0x01, 0x03, 0x38, 0x05, 0xaf, 0x01, 0x82, 0x01, 0x4f, + 0x03, 0x2f, 0x04, 0x1e, 0x00, 0xd4, 0x05, 0xa2, 0x00, 0x12, 0x03, 0x1e, + 0x03, 0xc2, 0x01, 0xde, 0x03, 0x08, 0x00, 0xac, 0x05, 0x00, 0x01, 0x64, + 0x02, 0xf1, 0x01, 0x65, 0x05, 0x34, 0x02, 0x8c, 0x02, 0xcf, 0x02, 0x2d, + 0x03, 0x4c, 0x04, 0xe3, 0x05, 0x9f, 0x02, 0xf8, 0x04, 0x1c, 0x05, 0x08, + 0x05, 0xb1, 0x02, 0x4b, 0x05, 0x15, 0x02, 0x78, 0x00, 0x52, 0x02, 0x3c, + 0x03, 0xf1, 0x03, 0xe4, 0x00, 0xc3, 0x03, 0x7d, 0x04, 0xcc, 0x00, 0xaa, + 0x03, 0x79, 0x05, 0x24, 0x02, 0x6e, 0x01, 0x6d, 0x03, 0x22, 0x04, 0xab, + 0x04, 0x44, 0x00, 0xfb, 0x01, 0xae, 0x00, 0x83, 0x03, 0x60, 0x00, 0xe5, + 0x01, 0x07, 0x04, 0x94, 0x04, 0x5e, 0x04, 0x2b, 0x00, 0x58, 0x01, 0x39, + 0x01, 0x92, 0x00, 0xc2, 0x05, 0x9b, 0x01, 0x43, 0x02, 0x46, 0x01, 0xf6, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0a, 0x00, 0x19, + 0x19, 0x19, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x11, 0x0a, 0x19, 0x19, 0x19, 0x03, 0x0a, + 0x07, 0x00, 0x01, 0x1b, 0x09, 0x0b, 0x18, 0x00, 0x00, 0x09, 0x06, 0x0b, + 0x00, 0x00, 0x0b, 0x00, 0x06, 0x19, 0x00, 0x00, 0x00, 0x19, 0x19, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x0a, 0x0d, 0x19, 0x19, 0x19, 0x00, 0x0d, 0x00, 0x00, + 0x02, 0x00, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x04, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x09, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x09, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x12, 0x00, 0x00, 0x1a, 0x00, 0x00, + 0x00, 0x1a, 0x1a, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x1a, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, + 0x00, 0x00, 0x09, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x00, 0x41, 0x80, 0x1e, 0x0b, + 0xec, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x16, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x34, 0x1a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0f, 0x00, 0x00, 0x00, 0xbb, + 0x0c, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0xeb, 0x0b, 0x57, 0x00, 0x2a, + 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x61, 0x72, + 0x67, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x01, 0x30, 0x5f, 0x5f, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x73, + 0x69, 0x7a, 0x65, 0x73, 0x5f, 0x67, 0x65, 0x74, 0x02, 0x2a, 0x5f, 0x5f, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x63, + 0x6c, 0x6f, 0x73, 0x65, 0x03, 0x2f, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, 0x61, + 0x74, 0x5f, 0x67, 0x65, 0x74, 0x04, 0x29, 0x5f, 0x5f, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x5f, 0x66, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x6b, + 0x05, 0x2a, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, + 0x66, 0x64, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x06, 0x2b, 0x5f, 0x5f, + 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x31, 0x5f, 0x70, 0x72, 0x6f, 0x63, + 0x5f, 0x65, 0x78, 0x69, 0x74, 0x07, 0x2b, 0x5f, 0x5f, 0x69, 0x6d, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x31, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, + 0x63, 0x76, 0x08, 0x2e, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x31, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x09, 0x2e, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x31, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x6e, 0x64, 0x5f, + 0x74, 0x6f, 0x0a, 0x30, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, + 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x31, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x5f, + 0x66, 0x72, 0x6f, 0x6d, 0x0b, 0x2b, 0x5f, 0x5f, 0x69, 0x6d, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6e, + 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x31, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x70, 0x65, + 0x6e, 0x0c, 0x11, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x6d, 0x5f, 0x63, 0x61, + 0x6c, 0x6c, 0x5f, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x0d, 0x06, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x0e, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x0f, 0x06, + 0x6d, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x10, 0x08, 0x64, 0x6c, 0x6d, 0x61, + 0x6c, 0x6c, 0x6f, 0x63, 0x11, 0x04, 0x66, 0x72, 0x65, 0x65, 0x12, 0x06, + 0x64, 0x6c, 0x66, 0x72, 0x65, 0x65, 0x13, 0x06, 0x63, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x14, 0x05, 0x5f, 0x45, 0x78, 0x69, 0x74, 0x15, 0x0b, 0x5f, + 0x5f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x76, 0x6f, 0x69, 0x64, 0x16, 0x1c, + 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x6c, 0x69, 0x62, 0x63, 0x5f, 0x70, + 0x6f, 0x70, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x17, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x18, + 0x0f, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x61, 0x72, 0x67, 0x73, + 0x5f, 0x67, 0x65, 0x74, 0x19, 0x15, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x61, 0x72, 0x67, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x73, 0x5f, + 0x67, 0x65, 0x74, 0x1a, 0x0f, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x66, 0x64, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x1b, 0x14, 0x5f, 0x5f, + 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x66, 0x64, 0x73, 0x74, + 0x61, 0x74, 0x5f, 0x67, 0x65, 0x74, 0x1c, 0x0e, 0x5f, 0x5f, 0x77, 0x61, + 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x73, 0x65, 0x65, 0x6b, 0x1d, 0x0f, + 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x66, 0x64, 0x5f, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x1e, 0x10, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, + 0x70, 0x72, 0x6f, 0x63, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x1f, 0x10, 0x5f, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x72, + 0x65, 0x63, 0x76, 0x20, 0x05, 0x61, 0x62, 0x6f, 0x72, 0x74, 0x21, 0x04, + 0x73, 0x62, 0x72, 0x6b, 0x22, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x23, + 0x11, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x6d, 0x5f, 0x63, 0x61, 0x6c, 0x6c, + 0x5f, 0x64, 0x74, 0x6f, 0x72, 0x73, 0x24, 0x05, 0x68, 0x74, 0x6f, 0x6e, + 0x6c, 0x25, 0x05, 0x68, 0x74, 0x6f, 0x6e, 0x73, 0x26, 0x06, 0x70, 0x72, + 0x69, 0x6e, 0x74, 0x66, 0x27, 0x0d, 0x5f, 0x5f, 0x73, 0x74, 0x64, 0x69, + 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x06, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x76, 0x29, 0x0d, 0x5f, 0x5f, 0x73, 0x74, 0x64, 0x69, 0x6f, + 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x2a, 0x08, 0x5f, 0x5f, 0x69, 0x73, + 0x61, 0x74, 0x74, 0x79, 0x2b, 0x0e, 0x5f, 0x5f, 0x73, 0x74, 0x64, 0x6f, + 0x75, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x2c, 0x07, 0x5f, 0x5f, + 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x2d, 0x0c, 0x5f, 0x5f, 0x73, 0x74, 0x64, + 0x69, 0x6f, 0x5f, 0x73, 0x65, 0x65, 0x6b, 0x2e, 0x0a, 0x5f, 0x5f, 0x6f, + 0x66, 0x6c, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x0c, 0x5f, 0x5f, 0x73, + 0x74, 0x64, 0x69, 0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x30, 0x09, 0x5f, + 0x5f, 0x74, 0x6f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x31, 0x09, 0x5f, 0x5f, + 0x66, 0x77, 0x72, 0x69, 0x74, 0x65, 0x78, 0x32, 0x06, 0x66, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x33, 0x05, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x34, 0x09, + 0x5f, 0x5f, 0x6c, 0x63, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x35, 0x08, 0x73, + 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x36, 0x07, 0x77, 0x63, 0x72, + 0x74, 0x6f, 0x6d, 0x62, 0x37, 0x06, 0x77, 0x63, 0x74, 0x6f, 0x6d, 0x62, + 0x38, 0x05, 0x66, 0x72, 0x65, 0x78, 0x70, 0x39, 0x05, 0x66, 0x70, 0x75, + 0x74, 0x73, 0x3a, 0x08, 0x76, 0x66, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, + 0x3b, 0x0b, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x5f, 0x63, 0x6f, 0x72, + 0x65, 0x3c, 0x07, 0x70, 0x6f, 0x70, 0x5f, 0x61, 0x72, 0x67, 0x3d, 0x03, + 0x70, 0x61, 0x64, 0x3e, 0x19, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x64, 0x6f, + 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x73, 0x75, 0x70, + 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x3f, 0x06, 0x6d, 0x65, 0x6d, 0x63, + 0x70, 0x79, 0x40, 0x06, 0x6d, 0x65, 0x6d, 0x73, 0x65, 0x74, 0x41, 0x06, + 0x73, 0x74, 0x72, 0x6c, 0x65, 0x6e, 0x42, 0x06, 0x6d, 0x65, 0x6d, 0x63, + 0x68, 0x72, 0x43, 0x07, 0x73, 0x74, 0x72, 0x6e, 0x6c, 0x65, 0x6e, 0x44, + 0x07, 0x66, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x45, 0x0d, 0x5f, 0x5f, + 0x61, 0x73, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x46, + 0x04, 0x72, 0x65, 0x63, 0x76, 0x47, 0x05, 0x6e, 0x74, 0x6f, 0x68, 0x73, + 0x48, 0x05, 0x6e, 0x74, 0x6f, 0x68, 0x6c, 0x49, 0x15, 0x77, 0x61, 0x73, + 0x69, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x6f, + 0x63, 0x6b, 0x61, 0x64, 0x64, 0x72, 0x4a, 0x15, 0x73, 0x6f, 0x63, 0x6b, + 0x61, 0x64, 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x4b, 0x16, 0x69, 0x70, 0x76, 0x34, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x4c, 0x16, 0x69, 0x70, 0x76, 0x36, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x73, 0x69, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x4d, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x4e, 0x13, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6f, 0x63, 0x6b, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x4f, + 0x06, 0x73, 0x65, 0x6e, 0x64, 0x74, 0x6f, 0x50, 0x13, 0x5f, 0x5f, 0x77, + 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x73, 0x65, 0x6e, + 0x64, 0x5f, 0x74, 0x6f, 0x51, 0x08, 0x72, 0x65, 0x63, 0x76, 0x66, 0x72, + 0x6f, 0x6d, 0x52, 0x15, 0x5f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, + 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x63, 0x76, 0x5f, 0x66, 0x72, 0x6f, + 0x6d, 0x53, 0x06, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x54, 0x10, 0x5f, + 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x6f, + 0x70, 0x65, 0x6e, 0x55, 0x1a, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, 0x73, 0x69, 0x5f, 0x69, + 0x70, 0x34, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x56, 0x1b, 0x69, 0x70, 0x76, + 0x36, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x5f, 0x74, 0x6f, 0x5f, 0x77, 0x61, + 0x73, 0x69, 0x5f, 0x69, 0x70, 0x76, 0x36, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x07, 0x33, 0x02, 0x00, 0x0f, 0x5f, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x01, 0x1f, 0x47, 0x4f, + 0x54, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2e, 0x5f, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x5f, 0x62, 0x61, 0x73, 0x65, 0x09, 0x11, 0x02, 0x00, 0x07, 0x2e, 0x72, + 0x6f, 0x64, 0x61, 0x74, 0x61, 0x01, 0x05, 0x2e, 0x64, 0x61, 0x74, 0x61, + 0x00, 0x26, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x72, 0x73, + 0x01, 0x0c, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x2d, + 0x62, 0x79, 0x01, 0x05, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x06, 0x31, 0x37, + 0x2e, 0x30, 0x2e, 0x36, 0x00, 0x39, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x03, 0x2b, + 0x0b, 0x62, 0x75, 0x6c, 0x6b, 0x2d, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x2b, 0x0f, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2d, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x2b, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x2d, + 0x65, 0x78, 0x74 +}; \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-http/src/main.c b/product-mini/platforms/zephyr/simple-http/src/main.c new file mode 100644 index 0000000000..a9caf5d0c4 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/src/main.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// #include + +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "http_get.h" + +#include +#include +#include +#include +#include +#include + +#define CONFIG_HEAP_MEM_POOL_SIZE WASM_GLOBAL_HEAP_SIZE +#define CONFIG_APP_STACK_SIZE 8192 +#define CONFIG_APP_HEAP_SIZE 8192 + +static char global_heap_buf[CONFIG_HEAP_MEM_POOL_SIZE] = { 0 }; + +static int app_argc; +static char **app_argv; + +int +main(void) +{ + int start, end; + start = k_uptime_get_32(); + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; + const char *exception; + + int log_verbose_level = 2; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + printf("global heap size: %d\n", sizeof(global_heap_buf)); +#else +#error "memory allocation scheme is not defined." +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return; + } + + bh_log_set_verbose_level(log_verbose_level); + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8 *)wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + printf("Wasm file size: %d\n", wasm_file_size); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("Failed to load module: %s\n", error_buf); + goto fail1; + } + + /* Set the WASI context */ +#if WASM_ENABLE_LIBC_WASI != 0 +#define ADDRESS_POOL_SIZE 1 + const char *addr_pool[ADDRESS_POOL_SIZE] = { + "192.0.2.10/24", + }; + /* No dir list => No file system + * dir_cont = 0 + * No mapped dir list => No file system + * map_dir_cont = 0 + * No environment variables + * env_count = 0 + * No command line arguments + * argv 0 + */ + wasm_runtime_set_wasi_args(wasm_module, NULL, 0, NULL, 0, NULL, 0, NULL, 0); + wasm_runtime_set_wasi_addr_pool(wasm_module, addr_pool, ADDRESS_POOL_SIZE); + wasm_runtime_set_wasi_ns_lookup_pool(wasm_module, NULL, 0); +#endif + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate( + wasm_module, CONFIG_APP_STACK_SIZE, CONFIG_APP_HEAP_SIZE, + error_buf, sizeof(error_buf)))) { + printf("Failed to instantiate module: %s\n", error_buf); + goto fail2; + } + + /* invoke the main function */ + if (wasm_runtime_lookup_function(wasm_module_inst, "_start") + || wasm_runtime_lookup_function(wasm_module_inst, "__main_argc_argv")) { + + printf("main found\n"); + wasm_application_execute_main(wasm_module_inst, 0, NULL); + printf("main executed\n"); + } + else { + printf("Failed to lookup function main\n"); + return -1; + } + + if ((exception = wasm_runtime_get_exception(wasm_module_inst))) + printf("%s\n", exception); + + int rc = wasm_runtime_get_wasi_exit_code(wasm_module_inst); + printf("wasi exit code: %d\n", rc); // 1 = _WASI_E2BIG + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + + end = k_uptime_get_32(); + + printf("elapsed: %dms\n", (end - start)); + + return 0; +} \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-http/to_c_header.py b/product-mini/platforms/zephyr/simple-http/to_c_header.py new file mode 100644 index 0000000000..c9adf57dd8 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/to_c_header.py @@ -0,0 +1,34 @@ +# Copyright (C) 2024 Grenoble INP - ESISAR. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Python script to convert wasm file to byte array in a .h file +import os + +CWD = os.getcwd() +CMAKE_CURRENT_BINARY_DIR = os.getenv('CMAKE_CURRENT_BINARY_DIR', CWD) +CMAKE_CURRENT_SOURCE_DIR = os.getenv('CMAKE_CURRENT_SOURCE_DIR', f'{CWD}/../src') + +LICENCE_HEADER = """/* + * Copyright (c) 2017 Linaro Limited + * Copyright (C) 2024 Grenoble INP - ESISAR Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ +""" + +print('CMAKE_CURRENT_BINARY_DIR:', CMAKE_CURRENT_BINARY_DIR) +print('CMAKE_CURRENT_SOURCE_DIR:', CMAKE_CURRENT_SOURCE_DIR) + +# Open the wasm file in binary mode and read the data +with open(f'{CWD}/wasm-apps/http_get.wasm', 'rb') as f: + wasm_bytes = f.read() + +# Convert the bytes to a comma-separated string of hex values +byte_array = ', '.join(f'0x{byte:02x}' for byte in wasm_bytes) + +# Create the output string +output = f'unsigned char __aligned(4) wasm_test_file[] = {{ {byte_array} }};' + +# Write the output string to the .h file +with open(f'{CWD}/src/http_get.h', 'w') as f: + f.write(output) \ No newline at end of file diff --git a/product-mini/platforms/zephyr/simple-http/wasm-apps/http_get.c b/product-mini/platforms/zephyr/simple-http/wasm-apps/http_get.c new file mode 100644 index 0000000000..581a4aef46 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/wasm-apps/http_get.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Linaro Limited + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __wasi__ +#include +#endif + +/* HTTP server to connect to */ +#define HTTP_HOST "192.0.2.10" +/* Port to connect to, as string */ +#define HTTP_PORT "8000" +/* HTTP path to request */ +#define HTTP_PATH "/" + +#define SSTRLEN(s) (sizeof(s) - 1) +// #define CHECK(r) { if (r == -1) { printf("Error %d: " #r "\n", errno); +// exit(1); } } + +#define REQUEST "GET " HTTP_PATH " HTTP/1.0\r\nHost: " HTTP_HOST "\r\n\r\n" + +static char response[1024]; + +int +main(int argc, char **argv) +{ + int st, sock; + struct sockaddr_in addr; + int rc = 0; + + printf("[wasm-mod] Preparing HTTP GET request for http://" HTTP_HOST + ":" HTTP_PORT HTTP_PATH "\n"); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(8000); + addr.sin_addr.s_addr = + htonl(0xC000020A); // hard coded IP address for 192.0.2.10 + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + printf("[wasm-mod] sock = %d\n", sock); + + rc = connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + printf("[wasm-mod] connect rc = %d\n", rc); + + rc = sendto(sock, (const void *)REQUEST, SSTRLEN(REQUEST), 0, + (struct sockaddr *)&addr, sizeof(addr)); + printf("[wasm-mod] send rc = %d\n", rc); + if (rc < 0) { + printf("[wasm-mod] Error %d\n", errno); + return 0; + } + + printf("[wasm-mod] Response:\n\n"); + + while (1) { + socklen_t socklen = sizeof(struct sockaddr_in); + int len = recvfrom(sock, response, sizeof(response) - 1, 0, + (struct sockaddr *)&addr, &socklen); + + if (len < 0) { + printf("[wasm-mod] Error %d\n", errno); + return 0; + } + + response[len] = 0; + printf("%s", response); + + if (len == 0) { + printf("[wasm-mod] len = 0 break\n"); + break; + } + } + + printf("\n"); + + (void)close(sock); + printf("[wasm-mod] Connection closed\n"); + return 0; +} diff --git a/product-mini/platforms/zephyr/simple-http/wasm-apps/inc/location.md b/product-mini/platforms/zephyr/simple-http/wasm-apps/inc/location.md new file mode 100644 index 0000000000..51a3ae6f00 --- /dev/null +++ b/product-mini/platforms/zephyr/simple-http/wasm-apps/inc/location.md @@ -0,0 +1,5 @@ +The lib source code are located there: +* [wasi_socket_ext.h](../../../../../../core/iwasm/libraries/lib-socket/inc/wasi_socket_ext.h) +* [wasi_socket_ext.c](../../../../../../core/iwasm/libraries/lib-socket/src/wasi/wasi_socket_ext.c) + + diff --git a/product-mini/platforms/zephyr/simple/CMakeLists.txt b/product-mini/platforms/zephyr/simple/CMakeLists.txt index cafc7002fa..46dc3ea0c5 100644 --- a/product-mini/platforms/zephyr/simple/CMakeLists.txt +++ b/product-mini/platforms/zephyr/simple/CMakeLists.txt @@ -1,16 +1,16 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required(VERSION 3.8.2) +cmake_minimum_required(VERSION 3.14) -include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) -project(NONE) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(wamr) enable_language (ASM) set (WAMR_BUILD_PLATFORM "zephyr") -# Build as X86_32 by default, change to "ARM[sub]", "THUMB[sub]", "MIPS" or "XTENSA" +# Build as X86_32 by default, change to "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS" or "XTENSA" # if we want to support arm, thumb, mips or xtensa if (NOT DEFINED WAMR_BUILD_TARGET) set (WAMR_BUILD_TARGET "X86_32") @@ -32,54 +32,30 @@ if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) endif () if (NOT DEFINED WAMR_BUILD_LIBC_WASI) - # Disable libc wasi support by default + # Disable libc wasi support by default, in the future, + # it can be enabled if libc wasi file/socket/lock support is ready on Zephyr platform set (WAMR_BUILD_LIBC_WASI 0) endif () -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) -set (SHARED_DIR ${WAMR_ROOT_DIR}/core/shared) -set (IWASM_DIR ${WAMR_ROOT_DIR}/core/iwasm) -set (APP_FRAMEWORK_DIR ${WAMR_ROOT_DIR}/core/app-framework) - -# include the build config template makefile -include (${WAMR_ROOT_DIR}/build-scripts/config_common.cmake) - -include_directories (${SHARED_DIR}/include - ${IWASM_DIR}/include) - -include (${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) -include (${SHARED_DIR}/mem-alloc/mem_alloc.cmake) -include (${SHARED_DIR}/utils/shared_utils.cmake) -if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) - include (${IWASM_DIR}/libraries/libc-builtin/libc_builtin.cmake) -endif () -if (WAMR_BUILD_LIBC_WASI EQUAL 1) - include (${IWASM_DIR}/libraries/libc-wasi/libc_wasi.cmake) +if (WAMR_BUILD_TARGET STREQUAL "RISCV64_LP64" OR WAMR_BUILD_TARGET STREQUAL "RISCV32_ILP32") + set (WAMR_BUILD_FAST_INTERP 1) endif () -include (${IWASM_DIR}/common/iwasm_common.cmake) - -if (WAMR_BUILD_INTERP EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) - include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) -endif() +# Override the global heap usage +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_POOL) + set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) +endif () -if (WAMR_BUILD_AOT EQUAL 1) - include (${IWASM_DIR}/aot/iwasm_aot.cmake) +# Override the global heap size for small devices +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + set (WAMR_BUILD_GLOBAL_HEAP_SIZE 131072) # 128 KB endif () -set (VM_LIB_SRCS - ${PLATFORM_SHARED_SOURCE} - ${MEM_ALLOC_SHARED_SOURCE} - ${UTILS_SHARED_SOURCE} - ${LIBC_BUILTIN_SOURCE} - ${LIBC_WASI_SOURCE} - ${IWASM_COMMON_SOURCE} - ${IWASM_INTERP_SOURCE} - ${IWASM_AOT_SOURCE} - ${IWASM_COMPL_SOURCE}) +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) target_sources(app PRIVATE - ${VM_LIB_SRCS} - src/main.c - src/ext_lib_export.c) + ${WAMR_RUNTIME_LIB_SOURCE} + src/main.c) diff --git a/product-mini/platforms/zephyr/simple/Dockerfile b/product-mini/platforms/zephyr/simple/Dockerfile new file mode 100644 index 0000000000..73171cfea7 --- /dev/null +++ b/product-mini/platforms/zephyr/simple/Dockerfile @@ -0,0 +1,58 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# Refer to https://docs.zephyrproject.org/3.7.0/develop/getting_started/index.html +# for more information on how to set up the Zephyr development environment. + +# https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-workspace-application +# zephyrproject/ --> CI ROOT +# ├─── .west/ +# │ └─── config +# ├─── bootloader/ +# ├─── zephyr/ --> Zephyr source code +# ├─── zephyr-sdk/ +# ├─── modules/ +# │ |─── wasm-micro-runtime --> WAMR source code +# ├─── tools/ +# ├─── vendor/ +# └─── application/ --> DUMMY. keep west_lite.yml here + +# If you modify this file, you may need to sync the modifications to the +# .github/actions/setup-zephyr/action.yml +FROM ghcr.io/zephyrproject-rtos/ci-base:v0.26-branch + +ARG DEBIAN_FRONTEND=noninteractive +ENV TZ=Asian/Shanghai +ARG ZEPHYR_SDK_VERSION=0.16.9 +# In west_lite.yml, the Zephyr version is set to v3.7.0 +#ARG ZEPHYR_VERSION=3.7.0 + +# Install the Zephyr Software Development Kit (SDK) minimal version +WORKDIR /root/zephyrproject/zephyr-sdk +# hadolint ignore=DL4006 +RUN wget --progress=dot:giga https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64_minimal.tar.xz \ + && wget --progress=dot:giga -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/sha256.sum | shasum --check --ignore-missing \ + && tar --strip-components=1 -xf zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64_minimal.tar.xz && rm zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64_minimal.tar.xz +# hadolint ignore=DL4006 +# Install arc tools, host tools and Register Zephyr SDK CMake package +# If you want to use other toolchains, please change the -t option +RUN ./setup.sh -t arc-zephyr-elf -h -c + +# Install west +# hadolint ignore=DL3013,DL3059 +RUN pip3 install --no-cache-dir west + +# Setup a T2(Star topology) workspace +WORKDIR /root/zephyrproject/application +COPY ./west_lite.yml ./west_lite.yml + +# init the west workspace with a minimal manifest +RUN west init -l --mf west_lite.yml . + +WORKDIR /root/zephyrproject +RUN west update --stats + +# Git clone wamr +WORKDIR /root/zephyrproject/modules/ +RUN git clone https://github.com/bytecodealliance/wasm-micro-runtime.git wasm-micro-runtime +WORKDIR /root/zephyrproject/modules/wasm-micro-runtime/product-mini/platforms/zephyr diff --git a/product-mini/platforms/zephyr/simple/README.md b/product-mini/platforms/zephyr/simple/README.md new file mode 100644 index 0000000000..3f1a74c6b9 --- /dev/null +++ b/product-mini/platforms/zephyr/simple/README.md @@ -0,0 +1,114 @@ +# How to use WAMR with Zephyr + +[Zephyr](https://www.zephyrproject.org/) is an open source real-time operating +system (RTOS) with a focus on security and broad hardware support. WAMR is +compatible with Zephyr via the [Zephyr WAMR +port](../../../../core/shared/platform/zephyr). + +## Setup + +Using WAMR with Zephyr can be accomplished by either using the provided Docker +image, or by installing Zephyr locally. Both approaches are described below. + +### Docker + +The provided Docker image sets up Zephyr and its dependencies for all +architectures, meaning that you are ready to build for any [supported +board](https://docs.zephyrproject.org/latest/boards/index.html). This comes at +the expense of building a rather large image, which both can take a long time to +build and uses up a large amount of storage (~15 GB). + +Execute the following command to build the Docker image. This may take an +extended period of time to complete. + +```shell +docker build -t wamr-zephyr . +``` + +Execute the following command to run the image built in the previous step. If +you are planning to flash a device after building, make sure to specify it with +[`--device`](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities). + +```shell +docker run -it --rm --device=/dev/ttyUSB0 wamr-zephyr +``` + +### Local Environment + +Zephyr can also be setup locally to enable building this sample application. +This allows you have have more control over what modules and tools are +installed, which can drastically reduce the storage required compared to the +Docker image. + +Follow the steps provided in the [Zephyr Getting Started +guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html) +to setup for local development. + +## Building for a Specific Board + +With an environment setup either locally or in a Docker container, you can build +for a Zephyr supported board using +[`west`](https://docs.zephyrproject.org/latest/develop/west/index.html). There +are already [configuration files](./boards) for a few boards in this sample. +However, if you are using a new board, you will need to add your own file for +the board, or define configuration in the [`prj.conf](./prj.conf). After doing +so, use the following command with your board identifier to build the sample +application. + +```shell +west build . -b --pristine -- -DWAMR_BUILD_TARGET= +``` + +The `` can be found in the Zephyr supported boards +documentation. It must also be used as the name of the board configuration file. +You must define the architecture for WAMR to target with `WAMR_BUILD_TARGET`. +The list of supported architectures can be found in the main project +[README.md](../../../../README.md#supported-architectures-and-platforms). + +It may be necessary to define additional symbols for some boards. For example, +WAMR AOT execution may not be supported on all architectures. It and other +options can be disabled by modifying the [CMakeLists.txt](./CMakeLists.txt) +file, or by passing additional arguments at build (e.g. `-DWAMR_BUILD_AOT=0`). + +### Example Targets + +[ESP32-C3](https://docs.zephyrproject.org/latest/boards/riscv/esp32c3_devkitm/doc/index.html) +is a 32-bit RISC-V target that does not currently support AOT. + +```shell +west build . -b esp32c3_devkitm -p always -- -DWAMR_BUILD_TARGET=RISCV32_ILP32 -DWAMR_BUILD_AOT=0 +``` + +[ARM Cortex-A53 QEMU +(ARM)](https://docs.zephyrproject.org/latest/boards/arm64/qemu_cortex_a53/doc/index.html) +is a 64-bit ARM target for emulating the Cortex-A53 platform. + +```shell +west build . -b qemu_cortex_a53 -p always -- -DWAMR_BUILD_TARGET=AARCH64 +``` + +[ARC QEMU](https://docs.zephyrproject.org/latest/boards/qemu/arc/doc/index.html) +is a 32-bit ARC target for emulating the ARC platform. + +```shell +west build . -b qemu_arc/qemu_arc_em -p always -- -DWAMR_BUILD_TARGET=ARC +``` + +## Flashing or Running Image + +The board can be flashed with the built image with the following command. + +```shell +west flash +``` + +`west` will automatically identify the board if it is connected to the host +machine. + +When using emulated targets, such as those that utilize QEMU, there is no +physical device to flash, but `west` can be used to run the image under +emulation. + +```shell +west build -t run +``` diff --git a/product-mini/platforms/zephyr/simple/boards/nucleo_f767zi.conf b/product-mini/platforms/zephyr/simple/boards/nucleo_f767zi.conf new file mode 100644 index 0000000000..c920e6fd0c --- /dev/null +++ b/product-mini/platforms/zephyr/simple/boards/nucleo_f767zi.conf @@ -0,0 +1,4 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_ARM_MPU=y diff --git a/product-mini/platforms/zephyr/simple/boards/qemu_cortex_a53.conf b/product-mini/platforms/zephyr/simple/boards/qemu_cortex_a53.conf new file mode 100644 index 0000000000..2cc2dd4b47 --- /dev/null +++ b/product-mini/platforms/zephyr/simple/boards/qemu_cortex_a53.conf @@ -0,0 +1,4 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_ARM_MMU=n diff --git a/product-mini/platforms/zephyr/simple/build_and_run.sh b/product-mini/platforms/zephyr/simple/build_and_run.sh new file mode 100755 index 0000000000..bd89906d4a --- /dev/null +++ b/product-mini/platforms/zephyr/simple/build_and_run.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +X86_TARGET="x86" +STM32_TARGET="stm32" +ESP32C3_TARGET="esp32c3" +PARTICLE_ARGON_TARGET="particle_argon" +QEMU_CORTEX_A53="qemu_cortex_a53" +QEMU_XTENSA_TARGET="qemu_xtensa" +QEMU_RISCV64_TARGET="qemu_riscv64" +QEMU_RISCV32_TARGET="qemu_riscv32" +QEMU_ARC_TARGET="qemu_arc" + +usage () +{ + echo "USAGE:" + echo "$0 $X86_TARGET|$STM32_TARGET|$ESP32C3_TARGET|$PARTICLE_ARGON_TARGET|$QEMU_CORTEX_A53|$QEMU_XTENSA_TARGET|$QEMU_RISCV64_TARGET|$QEMU_RISCV32_TARGET|$QEMU_ARC_TARGET" + echo "Example:" + echo " $0 $X86_TARGET" + echo " $0 $STM32_TARGET" + echo " $0 $ESP32C3_TARGET" + echo " $0 $PARTICLE_ARGON_TARGET" + echo " $0 $QEMU_CORTEX_A53" + echo " $0 $QEMU_XTENSA_TARGET" + echo " $0 $QEMU_RISCV64_TARGET" + echo " $0 $QEMU_RISCV32_TARGET" + echo " $0 $QEMU_ARC_TARGET" + exit 1 +} + +if [ $# != 1 ] ; then + usage +fi + +TARGET=$1 + +case $TARGET in + $X86_TARGET) + west build -b qemu_x86_tiny \ + . -p always -- \ + -DWAMR_BUILD_TARGET=X86_32 + west build -t run + ;; + $STM32_TARGET) + west build -b nucleo_f767zi \ + . -p always -- \ + -DWAMR_BUILD_TARGET=THUMBV7 + west flash + ;; + $ESP32C3_TARGET) + west build -b esp32c3_devkitm \ + . -p always -- \ + -DWAMR_BUILD_TARGET=RISCV32_ILP32 \ + -DWAMR_BUILD_AOT=0 + # west flash will discover the device + west flash + ;; + $PARTICLE_ARGON_TARGET) + west build -b particle_argon \ + . -p always -- \ + -DWAMR_BUILD_TARGET=THUMBV7 + # west flash will discover the device + west flash + ;; + $QEMU_XTENSA_TARGET) + west build -b qemu_xtensa \ + . -p always -- \ + -DWAMR_BUILD_TARGET=XTENSA + west build -t run + ;; + $QEMU_CORTEX_A53) + west build -b qemu_cortex_a53 \ + . -p always -- \ + -DWAMR_BUILD_TARGET=AARCH64 + west build -t run + ;; + $QEMU_RISCV64_TARGET) + west build -b qemu_riscv64 \ + . -p always -- \ + -DWAMR_BUILD_TARGET=RISCV64_LP64 \ + -DWAMR_BUILD_AOT=0 + west build -t run + ;; + $QEMU_RISCV32_TARGET) + west build -b qemu_riscv32 \ + . -p always -- \ + -DWAMR_BUILD_TARGET=RISCV32_ILP32 \ + -DWAMR_BUILD_AOT=0 + west build -t run + ;; + $QEMU_ARC_TARGET) + west build -b qemu_arc_em \ + . -p always -- \ + -DWAMR_BUILD_TARGET=ARC \ + -DWAMR_BUILD_AOT=0 + west build -t run + ;; + *) + echo "unsupported target: $TARGET" + usage + exit 1 + ;; +esac + diff --git a/product-mini/platforms/zephyr/simple/prj.conf b/product-mini/platforms/zephyr/simple/prj.conf index e69de29bb2..8ab3c52f8c 100644 --- a/product-mini/platforms/zephyr/simple/prj.conf +++ b/product-mini/platforms/zephyr/simple/prj.conf @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=4096 +CONFIG_THREAD_RUNTIME_STATS=y diff --git a/product-mini/platforms/zephyr/simple/src/ext_lib_export.c b/product-mini/platforms/zephyr/simple/src/ext_lib_export.c deleted file mode 100644 index 8813f0dbf3..0000000000 --- a/product-mini/platforms/zephyr/simple/src/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/zephyr/simple/src/main.c b/product-mini/platforms/zephyr/simple/src/main.c index 621cc7b7ba..f6941d7f04 100644 --- a/product-mini/platforms/zephyr/simple/src/main.c +++ b/product-mini/platforms/zephyr/simple/src/main.c @@ -8,14 +8,24 @@ #include "bh_platform.h" #include "bh_assert.h" #include "bh_log.h" -#include "bh_memory.h" #include "wasm_export.h" +#if defined(BUILD_TARGET_RISCV64_LP64) || defined(BUILD_TARGET_RISCV32_ILP32) +#include "test_wasm_riscv64.h" +#else #include "test_wasm.h" - -#define CONFIG_GLOBAL_HEAP_BUF_SIZE 131072 +#endif /* end of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ + +#if defined(BUILD_TARGET_RISCV64_LP64) || defined(BUILD_TARGET_RISCV32_ILP32) +#define CONFIG_GLOBAL_HEAP_BUF_SIZE 5120 +#define CONFIG_APP_STACK_SIZE 512 +#define CONFIG_APP_HEAP_SIZE 512 +#else /* else of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ +#define CONFIG_GLOBAL_HEAP_BUF_SIZE WASM_GLOBAL_HEAP_SIZE #define CONFIG_APP_STACK_SIZE 8192 #define CONFIG_APP_HEAP_SIZE 8192 -#define CONFIG_MAIN_THREAD_STACK_SIZE 4096 +#endif /* end of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ + +#define CONFIG_MAIN_THREAD_STACK_SIZE 8192 static int app_argc; static char **app_argv; @@ -31,86 +41,133 @@ static char **app_argv; * @return true if the main function is called, false otherwise. */ bool -wasm_application_execute_main(wasm_module_inst_t module_inst, - int argc, char *argv[]); +wasm_application_execute_main(wasm_module_inst_t module_inst, int argc, + char *argv[]); -static void* +static void * app_instance_main(wasm_module_inst_t module_inst) { const char *exception; + wasm_function_inst_t func; + wasm_exec_env_t exec_env; + unsigned argv[2] = { 0 }; + + if (wasm_runtime_lookup_function(module_inst, "main") + || wasm_runtime_lookup_function(module_inst, "__main_argc_argv")) { + LOG_VERBOSE("Calling main function\n"); + wasm_application_execute_main(module_inst, app_argc, app_argv); + } + else if ((func = wasm_runtime_lookup_function(module_inst, "app_main"))) { + exec_env = + wasm_runtime_create_exec_env(module_inst, CONFIG_APP_HEAP_SIZE); + if (!exec_env) { + os_printf("Create exec env failed\n"); + return NULL; + } + + LOG_VERBOSE("Calling app_main function\n"); + wasm_runtime_call_wasm(exec_env, func, 0, argv); + + if (!wasm_runtime_get_exception(module_inst)) { + os_printf("result: 0x%x\n", argv[0]); + } + + wasm_runtime_destroy_exec_env(exec_env); + } + else { + os_printf("Failed to lookup function main or app_main to call\n"); + return NULL; + } - wasm_application_execute_main(module_inst, app_argc, app_argv); if ((exception = wasm_runtime_get_exception(module_inst))) - bh_printf("%s\n", exception); + os_printf("%s\n", exception); + return NULL; } +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 static char global_heap_buf[CONFIG_GLOBAL_HEAP_BUF_SIZE] = { 0 }; +#endif -void iwasm_main(void *arg1, void *arg2, void *arg3) +void +iwasm_main(void *arg1, void *arg2, void *arg3) { + int start, end; + start = k_uptime_get_32(); uint8 *wasm_file_buf = NULL; uint32 wasm_file_size; wasm_module_t wasm_module = NULL; wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; char error_buf[128]; #if WASM_ENABLE_LOG != 0 int log_verbose_level = 2; #endif - (void) arg1; - (void) arg2; - (void) arg3; + (void)arg1; + (void)arg2; + (void)arg3; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#elif (defined(CONFIG_COMMON_LIBC_MALLOC) \ + && CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE != 0) \ + || defined(CONFIG_NEWLIB_LIBC) + init_args.mem_alloc_type = Alloc_With_System_Allocator; +#else +#error "memory allocation scheme is not defined." +#endif - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - bh_printf("Init global heap failed.\n"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); return; } - /* initialize runtime environment */ - if (!wasm_runtime_init()) - goto fail1; - #if WASM_ENABLE_LOG != 0 bh_log_set_verbose_level(log_verbose_level); #endif /* load WASM byte buffer from byte buffer of include file */ - wasm_file_buf = (uint8*) wasm_test_file; + wasm_file_buf = (uint8 *)wasm_test_file; wasm_file_size = sizeof(wasm_test_file); /* load WASM module */ if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, error_buf, sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail2; + printf("%s\n", error_buf); + goto fail1; } /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, - CONFIG_APP_STACK_SIZE, - CONFIG_APP_HEAP_SIZE, - error_buf, - sizeof(error_buf)))) { - bh_printf("%s\n", error_buf); - goto fail3; + if (!(wasm_module_inst = wasm_runtime_instantiate( + wasm_module, CONFIG_APP_STACK_SIZE, CONFIG_APP_HEAP_SIZE, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; } + /* invoke the main function */ app_instance_main(wasm_module_inst); /* destroy the module instance */ wasm_runtime_deinstantiate(wasm_module_inst); - fail3: +fail2: /* unload the module */ wasm_runtime_unload(wasm_module); - fail2: +fail1: /* destroy runtime environment */ wasm_runtime_destroy(); - fail1: bh_memory_destroy(); + end = k_uptime_get_32(); + + printf("elapsed: %d\n", (end - start)); } #define MAIN_THREAD_STACK_SIZE (CONFIG_MAIN_THREAD_STACK_SIZE) @@ -119,17 +176,26 @@ void iwasm_main(void *arg1, void *arg2, void *arg3) K_THREAD_STACK_DEFINE(iwasm_main_thread_stack, MAIN_THREAD_STACK_SIZE); static struct k_thread iwasm_main_thread; -bool iwasm_init(void) +bool +iwasm_init(void) { - k_tid_t tid = k_thread_create(&iwasm_main_thread, iwasm_main_thread_stack, - MAIN_THREAD_STACK_SIZE, - iwasm_main, NULL, NULL, NULL, - MAIN_THREAD_PRIORITY, 0, K_NO_WAIT); + k_tid_t tid = k_thread_create( + &iwasm_main_thread, iwasm_main_thread_stack, MAIN_THREAD_STACK_SIZE, + iwasm_main, NULL, NULL, NULL, MAIN_THREAD_PRIORITY, 0, K_NO_WAIT); return tid ? true : false; } -void main(void) +#if KERNEL_VERSION_NUMBER < 0x030400 /* version 3.4.0 */ +void +main(void) { iwasm_init(); } - +#else +int +main(void) +{ + iwasm_init(); + return 0; +} +#endif diff --git a/product-mini/platforms/zephyr/simple/src/test_wasm.h b/product-mini/platforms/zephyr/simple/src/test_wasm.h index 65b8347982..a729cadef3 100644 --- a/product-mini/platforms/zephyr/simple/src/test_wasm.h +++ b/product-mini/platforms/zephyr/simple/src/test_wasm.h @@ -5,55 +5,42 @@ /** * The byte array buffer is the file content of a test wasm binary file, - * which is compiled by emcc or clang toolchain from C source file of: - * core/iwasm/app-samples/hello-world/main.c. + * which is compiled by wasi-sdk toolchain from C source file of: + * product-mini/app-samples/hello-world/main.c. */ -unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, - 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, - 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, - 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, - 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, - 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, - 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, - 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, - 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, - 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, - 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, - 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, - 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, - 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, - 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, - 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, - 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, - 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, - 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, - 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, - 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, - 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, - 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, - 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, - 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, - 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, - 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, - 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, - 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, - 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, - 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, - 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, - 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, - 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, - 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, - 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, - 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, - 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, - 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, - 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, - 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, - 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, - 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, - 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, - 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, - 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, - 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, - 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; +unsigned char __aligned(4) wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x28, 0x0B, 0x7F, 0x00, 0x41, 0xBA, 0x08, 0x0B, + 0x7F, 0x00, 0x41, 0xC0, 0x28, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, + 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x04, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x04, 0x0A, 0xB2, 0x01, 0x01, 0xAF, 0x01, 0x01, 0x03, + 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x88, 0x80, 0x80, 0x00, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, + 0x41, 0xA8, 0x88, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1A, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6A, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, + 0x04, 0x20, 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x88, + 0x80, 0x80, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, + 0x8D, 0x88, 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x41, 0x93, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, + 0x80, 0x00, 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x0B, 0x0B, 0x41, 0x01, 0x00, 0x41, 0x80, 0x08, + 0x0B, 0x3A, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, + 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, + 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, + 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; diff --git a/product-mini/platforms/zephyr/simple/src/test_wasm_riscv64.h b/product-mini/platforms/zephyr/simple/src/test_wasm_riscv64.h new file mode 100644 index 0000000000..1b45211d7a --- /dev/null +++ b/product-mini/platforms/zephyr/simple/src/test_wasm_riscv64.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +unsigned char __aligned(4) wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x12, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x01, 0x0B, 0x7F, 0x00, 0x41, 0x3A, 0x0B, 0x7F, + 0x00, 0x41, 0xC0, 0x01, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, 0x6D, + 0x6F, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x04, + 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, + 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, + 0x65, 0x03, 0x02, 0x0A, 0xB1, 0x01, 0x01, 0xAE, 0x01, 0x01, 0x03, 0x7F, + 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x80, 0x80, 0x80, 0x00, 0x10, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, 0x10, + 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, 0x41, 0xA8, + 0x80, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, + 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, + 0x10, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6A, + 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, 0x04, 0x20, + 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x80, 0x80, 0x80, + 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, 0x8D, 0x80, + 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x41, 0x93, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, 0x82, 0x80, + 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, 0x80, 0x00, + 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x04, 0x0B, 0x0B, 0x40, 0x01, 0x00, 0x41, 0x00, 0x0B, 0x3A, 0x62, + 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, + 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, + 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, + 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, + 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; diff --git a/product-mini/platforms/zephyr/simple/src/wasm-app-riscv64/build.sh b/product-mini/platforms/zephyr/simple/src/wasm-app-riscv64/build.sh new file mode 100755 index 0000000000..73e7349356 --- /dev/null +++ b/product-mini/platforms/zephyr/simple/src/wasm-app-riscv64/build.sh @@ -0,0 +1,27 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +WAMR_DIR=${PWD}/../../.. + +echo "Build wasm app .." +/opt/wasi-sdk/bin/clang -O3 \ + -z stack-size=128 -Wl,--initial-memory=65536 \ + -Wl,--global-base=0 \ + -o test.wasm main.c \ + -Wl,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--export=__data_end -Wl,--export=__heap_base \ + -Wl,--strip-all,--no-entry \ + -Wl,--allow-undefined \ + -nostdlib \ + +echo "Build binarydump tool .." +rm -fr build && mkdir build && cd build +cmake ../../../../../../../test-tools/binarydump-tool +make +cd .. + +echo "Generate test_wasm.h .." +./build/binarydump -o test_wasm.h -n wasm_test_file test.wasm +cp -a test_wasm.h ../test_wasm_riscv64.h + +echo "Done" diff --git a/product-mini/platforms/zephyr/simple/src/wasm-app-riscv64/main.c b/product-mini/platforms/zephyr/simple/src/wasm-app-riscv64/main.c new file mode 100644 index 0000000000..026cb8fd11 --- /dev/null +++ b/product-mini/platforms/zephyr/simple/src/wasm-app-riscv64/main.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +int +main(int argc, char **argv) +{ + char *buf; + + printf("Hello world!\n"); + + buf = malloc(16); + if (!buf) { + printf("malloc buf failed\n"); + return -1; + } + + printf("buf ptr: %p\n", buf); + + snprintf(buf, 1024, "%s", "1234\n"); + printf("buf: %s", buf); + + free(buf); + return 0; +} diff --git a/product-mini/platforms/zephyr/simple/west_lite.yml b/product-mini/platforms/zephyr/simple/west_lite.yml new file mode 100644 index 0000000000..7c05d73678 --- /dev/null +++ b/product-mini/platforms/zephyr/simple/west_lite.yml @@ -0,0 +1,15 @@ +# The west manifest file for WAMR on Zephyr smoke test. +# +manifest: + # + # Please add items below based on alphabetical order + projects: + - name: zephyr + url: https://github.com/zephyrproject-rtos/zephyr + revision: v3.7.0 + clone-depth: 1 + path: zephyr + west-commands: scripts/west-commands.yml + + self: + path: application diff --git a/product-mini/platforms/zephyr/user-mode/CMakeLists.txt b/product-mini/platforms/zephyr/user-mode/CMakeLists.txt new file mode 100644 index 0000000000..a41560b77d --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(wamr_user_mode LANGUAGES C) + +# Option: use pre-built library integration path. +# The library is still compiled from source under lib-wamr-zephyr/, but instead +# of relying on zephyr_library_app_memory() to register the partition, the +# resulting .a is imported as a pre-built library and the partition is registered +# manually via set_property(). This demonstrates how to integrate a WAMR library +# that was built externally (e.g. copied from another build). +# +# Usage: west build ... -- -DWAMR_USE_PREBUILT_LIB=1 +option(WAMR_USE_PREBUILT_LIB + "Use pre-built library approach for app_smem partition registration" OFF) + +# Always build the library from source, but conditionally skip +# zephyr_library_app_memory() inside the subdirectory when using pre-built mode. +add_subdirectory(lib-wamr-zephyr) + +if(WAMR_USE_PREBUILT_LIB) + # Manually replicate what zephyr_library_app_memory(wamr_partition) does: + # tell gen_app_partitions.py to place this library's globals into wamr_partition. + set_property(TARGET zephyr_property_target + APPEND PROPERTY COMPILE_OPTIONS + "-l" "libwamr_lib.a" "wamr_partition") + + message(STATUS "WAMR: using pre-built library approach for partition registration") +endif() + +# Link the wamr library to the app target +target_link_libraries(app PRIVATE wamr_lib) + +target_sources(app PRIVATE src/main.c) diff --git a/product-mini/platforms/zephyr/user-mode/README.md b/product-mini/platforms/zephyr/user-mode/README.md new file mode 100644 index 0000000000..bacc84973d --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/README.md @@ -0,0 +1,192 @@ +# How to use WAMR with Zephyr in user mode + +This example demonstrates how to build and run a WebAssembly application in user mode on Zephyr. + +> Note: The user mode is not supported on all Zephyr boards. Please refer to the Zephyr documentation for more information. + +## Setup + +Please refer to the [previous WAMR Zephyr README.md](../simple/README.md) for general Zephyr setup instructions. + +And refer to [official documentation of Zephyr user mode](https://docs.zephyrproject.org/latest/kernel/usermode/index.html) for more information about Zephyr user mode. + +### Enable user mode + +To enable Zephyr user mode, set the `CONFIG_USERSPACE` option to yes in the Zephyr configuration. + +```conf +CONFIG_USERSPACE=y +``` + +And link the WAMR runtime as a separate library in CMakelists.txt. + +```cmake +...WAMR CMake set up... + +zephyr_library_named (wamr_lib) + +zephyr_library_sources ( + ${WAMR_RUNTIME_LIB_SOURCE} + wamr_lib.c +) + +zephyr_library_app_memory (wamr_partition) +``` + +The `wamr_partition` is a memory partition that will be granted to the WAMR runtime. It is defined in the Zephyr application code. + +```C +K_APPMEM_PARTITION_DEFINE(wamr_partition); +``` + +When creating a Zephyr thread, set the thread option to `K_USER` and the timeout to `K_FOREVER`. This can ensure that the `wamr_partition` is granted access to the thread before starting it with `k_thread_start`. + +### Advantage of using WAMR runtime in Zephyr user mode thread + +In a user-mode Zephyr thread, the application can only access a restricted partition of memory it granted to. It creates a sandbox for the WAMR runtime to run in, and the WAMR runtime can only access that memory space, meaning that all global variables in the WAMR runtime and both runtime and wasm app heap memory will be allocated from it. In this way, an extra layer of security is added to the wasm application on top of the wasm sandbox provided by WAMR. + +### Using a pre-built WAMR library in user mode + +If the WAMR library is pre-built as a static archive (`.a` file) rather than +compiled inline via `add_subdirectory`, the library's global variables still +need to be placed into the `app_smem` section so they are accessible from the +user-mode thread. This is useful when you want to treat the WAMR runtime as a +binary dependency copied from an external build. + +#### How `zephyr_library_app_memory` works internally + +`zephyr_library_app_memory(partition)` is a thin wrapper that appends metadata +to a CMake target property: + +```cmake +# zephyr/cmake/modules/extensions.cmake +set_property(TARGET zephyr_property_target + APPEND PROPERTY COMPILE_OPTIONS + "-l" "") +``` + +Zephyr's build system passes this metadata as `-l libname.a partition` arguments +to `gen_app_partitions.py`, which generates a linker script fragment with +wildcard patterns that collect the library's `.data` and `.bss` sections into +the named partition: + +```ld +"*libwamr_lib.a:*"(.data .data.* .sdata .sdata.*) +"*libwamr_lib.a:*"(.bss .bss.* .sbss .sbss.* COMMON COMMON.*) +``` + +#### Using the built-in `WAMR_USE_PREBUILT_LIB` option + +This sample's CMakeLists.txt supports a `WAMR_USE_PREBUILT_LIB` option. When +enabled, the library is still compiled from source under `lib-wamr-zephyr/`, +but partition registration bypasses `zephyr_library_app_memory()` and uses the +manual `set_property()` approach instead. This demonstrates the same integration +path you would use with an externally built `.a` file. + +Build from source with `zephyr_library_app_memory` (default): + +```shell +west build -b qemu_x86 . -p always +``` + +Build from source with pre-built library partition registration: + +```shell +west build -b qemu_x86 . -p always -- -DWAMR_USE_PREBUILT_LIB=1 +``` + +The application code (`main.c`) is unchanged in both cases — define the +partition with `K_APPMEM_PARTITION_DEFINE(wamr_partition)`, set up the memory +domain, and create a user-mode thread as usual. + +#### Applying this to your own project + +To use a pre-built WAMR library in a standalone Zephyr application, add the +following to your CMakeLists.txt: + +```cmake +# Import the pre-built library +add_library(wamr_lib STATIC IMPORTED GLOBAL) +set_target_properties(wamr_lib PROPERTIES + IMPORTED_LOCATION /path/to/libwamr_lib.a +) + +# Tell gen_app_partitions.py to place this library's globals into wamr_partition. +# This replicates what zephyr_library_app_memory(wamr_partition) does for +# libraries built through zephyr_library_named(). +set_property(TARGET zephyr_property_target + APPEND PROPERTY COMPILE_OPTIONS + "-l" "libwamr_lib.a" "wamr_partition") + +# Link it to the app +target_link_libraries(app PRIVATE wamr_lib) +``` + +#### Notes + +- The library filename in the `-l` argument must match the archive filename + that the linker sees (e.g. `libwamr_lib.a`). +- The pre-built library must be compiled with the same Zephyr toolchain and + flags (architecture, sysroot, etc.) as the application. +- For Zephyr 4.x, if building the library inline via `add_subdirectory`, add + `add_dependencies(wamr_lib zephyr_generated_headers)` to avoid build race + conditions with generated headers like `heap_constants.h`. + +### Example Targets + +#### qemu_x86 (Zephyr 4.x with Zephyr SDK 1.0+) + +Build for the `qemu_x86` board (32-bit x86, the default `WAMR_BUILD_TARGET`): + +```shell +west build -b qemu_x86 . -p always +``` + +To use the pre-built library approach instead: + +```shell +west build -b qemu_x86 . -p always -- -DWAMR_USE_PREBUILT_LIB=1 +``` + +Run on QEMU using `west`: + +```shell +west build -t run +``` + +> Press `CTRL+a, x` to exit QEMU. + +Expected output: + +``` +*** Booting Zephyr OS build v4.4.0-rc2 *** +wamr_partition start addr: 1257472, size: 45056 +User mode thread: start +Hello world! +buf ptr: 0x1458 +buf: 1234 +User mode thread: elapsed 10 +``` + +> Note: The boot message order may vary. `wamr_partition` size should be around +> 45056 bytes (40 KB global heap + other library globals). + +#### qemu_x86_tiny (older Zephyr / manual QEMU) + +Build for the `qemu_x86_tiny` board: + +```shell +west build -b qemu_x86_tiny . -p always -- -DWAMR_BUILD_TARGET=X86_32 +``` + +Run QEMU manually: + +```shell +qemu-system-i386 -m 32 -cpu qemu32,+nx,+pae -machine pc \ + -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ + -no-reboot -nographic -net none -pidfile qemu.pid \ + -chardev stdio,id=con,mux=on -serial chardev:con \ + -mon chardev=con,mode=readline \ + -icount shift=5,align=off,sleep=off -rtc clock=vm \ + -kernel ./build/zephyr/zephyr.elf +``` diff --git a/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/CMakeLists.txt b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/CMakeLists.txt new file mode 100644 index 0000000000..9fb31fb7ff --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/CMakeLists.txt @@ -0,0 +1,74 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +set (WAMR_BUILD_PLATFORM "zephyr") + +# Build as X86_32 by default, change to "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS" or "XTENSA" +# if we want to support arm, thumb, mips or xtensa +if (NOT DEFINED WAMR_BUILD_TARGET) + set (WAMR_BUILD_TARGET "X86_32") +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default, in the future, + # it can be enabled if libc wasi file/socket/lock support is ready on Zephyr platform + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +if (WAMR_BUILD_TARGET STREQUAL "RISCV64_LP64" OR WAMR_BUILD_TARGET STREQUAL "RISCV32_ILP32") + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +# Limited to global heap usage in Zephyr user mode +set (WAMR_BUILD_GLOBAL_HEAP_POOL 1) + +# Override the global heap size for small devices +if (NOT DEFINED WAMR_BUILD_GLOBAL_HEAP_SIZE) + set (WAMR_BUILD_GLOBAL_HEAP_SIZE 40960) # 40 KB +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +# Embed WAMR as a Zephyr library +zephyr_library_named (wamr_lib) + +# Add source files to the library +zephyr_library_sources ( + ${WAMR_RUNTIME_LIB_SOURCE} + wamr_lib.c +) + +# Ensure generated headers (e.g. heap_constants.h) are ready before compiling +# the library. Zephyr adds this dependency for its own libraries automatically, +# but parallel builds can race when the library is added via add_subdirectory. +if(TARGET zephyr_generated_headers) + add_dependencies(wamr_lib zephyr_generated_headers) +endif() + +# Specify the memory partition where all globals(including the WAMR global heap buffer) +# in the library should be placed. This partition will be defined in the app source code +# and added to the use-mode thread that uses the WAMR library. +# When WAMR_USE_PREBUILT_LIB is set, the parent CMakeLists.txt handles partition +# registration via set_property() instead, demonstrating the pre-built library approach. +if (NOT WAMR_USE_PREBUILT_LIB) + zephyr_library_app_memory (wamr_partition) +endif () diff --git a/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/test_wasm.h b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/test_wasm.h new file mode 100644 index 0000000000..a729cadef3 --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/test_wasm.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * The byte array buffer is the file content of a test wasm binary file, + * which is compiled by wasi-sdk toolchain from C source file of: + * product-mini/app-samples/hello-world/main.c. + */ +unsigned char __aligned(4) wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x28, 0x0B, 0x7F, 0x00, 0x41, 0xBA, 0x08, 0x0B, + 0x7F, 0x00, 0x41, 0xC0, 0x28, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, + 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, + 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x04, 0x6D, 0x61, + 0x69, 0x6E, 0x00, 0x04, 0x0A, 0xB2, 0x01, 0x01, 0xAF, 0x01, 0x01, 0x03, + 0x7F, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, + 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x88, 0x80, 0x80, 0x00, + 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, + 0x80, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, + 0x41, 0xA8, 0x88, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x1A, 0x41, 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x10, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, + 0x10, 0x6A, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, + 0x04, 0x20, 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x88, + 0x80, 0x80, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, + 0x8D, 0x88, 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, + 0x36, 0x02, 0x00, 0x41, 0x93, 0x88, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, + 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, + 0x80, 0x00, 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, + 0x80, 0x00, 0x20, 0x04, 0x0B, 0x0B, 0x41, 0x01, 0x00, 0x41, 0x80, 0x08, + 0x0B, 0x3A, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, + 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, + 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, + 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, + 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; diff --git a/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/test_wasm_riscv64.h b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/test_wasm_riscv64.h new file mode 100644 index 0000000000..1b45211d7a --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/test_wasm_riscv64.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +unsigned char __aligned(4) wasm_test_file[] = { + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01, + 0x7F, 0x00, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x70, 0x75, + 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70, 0x72, + 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04, 0x66, + 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x01, 0x04, 0x05, 0x01, + 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x12, 0x03, + 0x7F, 0x01, 0x41, 0xC0, 0x01, 0x0B, 0x7F, 0x00, 0x41, 0x3A, 0x0B, 0x7F, + 0x00, 0x41, 0xC0, 0x01, 0x0B, 0x07, 0x2C, 0x04, 0x06, 0x6D, 0x65, 0x6D, + 0x6F, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x04, + 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, + 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, + 0x65, 0x03, 0x02, 0x0A, 0xB1, 0x01, 0x01, 0xAE, 0x01, 0x01, 0x03, 0x7F, + 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, 0x24, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x9B, 0x80, 0x80, 0x80, 0x00, 0x10, + 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x02, 0x40, 0x02, 0x40, 0x41, 0x10, + 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x22, 0x03, 0x0D, 0x00, 0x41, 0xA8, + 0x80, 0x80, 0x80, 0x00, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, + 0x7F, 0x21, 0x04, 0x0C, 0x01, 0x0B, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, + 0x10, 0x41, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6A, + 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1A, 0x41, 0x00, 0x21, 0x04, 0x20, + 0x03, 0x41, 0x04, 0x6A, 0x41, 0x00, 0x2F, 0x00, 0x91, 0x80, 0x80, 0x80, + 0x00, 0x3B, 0x00, 0x00, 0x20, 0x03, 0x41, 0x00, 0x28, 0x00, 0x8D, 0x80, + 0x80, 0x80, 0x00, 0x36, 0x00, 0x00, 0x20, 0x02, 0x20, 0x03, 0x36, 0x02, + 0x00, 0x41, 0x93, 0x80, 0x80, 0x80, 0x00, 0x20, 0x02, 0x10, 0x82, 0x80, + 0x80, 0x80, 0x00, 0x1A, 0x20, 0x03, 0x10, 0x83, 0x80, 0x80, 0x80, 0x00, + 0x0B, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, + 0x20, 0x04, 0x0B, 0x0B, 0x40, 0x01, 0x00, 0x41, 0x00, 0x0B, 0x3A, 0x62, + 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, + 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, + 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, + 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, + 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00 +}; diff --git a/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/wamr_lib.c b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/wamr_lib.c new file mode 100644 index 0000000000..a2c639e802 --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/lib-wamr-zephyr/wamr_lib.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "bh_queue.h" +#include "wasm_export.h" +#if defined(BUILD_TARGET_RISCV64_LP64) || defined(BUILD_TARGET_RISCV32_ILP32) +#include "test_wasm_riscv64.h" +#else +#include "test_wasm.h" +#endif /* end of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ + +#if defined(BUILD_TARGET_RISCV64_LP64) || defined(BUILD_TARGET_RISCV32_ILP32) +#define CONFIG_GLOBAL_HEAP_BUF_SIZE 5120 +#define CONFIG_APP_STACK_SIZE 512 +#define CONFIG_APP_HEAP_SIZE 512 +#else /* else of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ +#define CONFIG_GLOBAL_HEAP_BUF_SIZE WASM_GLOBAL_HEAP_SIZE +#define CONFIG_APP_STACK_SIZE 8192 +#define CONFIG_APP_HEAP_SIZE 8192 +#endif /* end of BUILD_TARGET_RISCV64_LP64 || BUILD_TARGET_RISCV32_ILP32 */ + +static int app_argc; +static char **app_argv; + +/** + * Find the unique main function from a WASM module instance + * and execute that function. + * + * @param module_inst the WASM module instance + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the main function is called, false otherwise. + */ +bool +wasm_application_execute_main(wasm_module_inst_t module_inst, int argc, + char *argv[]); + +static void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + wasm_function_inst_t func; + wasm_exec_env_t exec_env; + unsigned argv[2] = { 0 }; + + if (wasm_runtime_lookup_function(module_inst, "main") + || wasm_runtime_lookup_function(module_inst, "__main_argc_argv")) { + LOG_VERBOSE("Calling main function\n"); + wasm_application_execute_main(module_inst, app_argc, app_argv); + } + else if ((func = wasm_runtime_lookup_function(module_inst, "app_main"))) { + exec_env = + wasm_runtime_create_exec_env(module_inst, CONFIG_APP_HEAP_SIZE); + if (!exec_env) { + os_printf("Create exec env failed\n"); + return NULL; + } + + LOG_VERBOSE("Calling app_main function\n"); + wasm_runtime_call_wasm(exec_env, func, 0, argv); + + if (!wasm_runtime_get_exception(module_inst)) { + os_printf("result: 0x%x\n", argv[0]); + } + + wasm_runtime_destroy_exec_env(exec_env); + } + else { + os_printf("Failed to lookup function main or app_main to call\n"); + return NULL; + } + + if ((exception = wasm_runtime_get_exception(module_inst))) + os_printf("%s\n", exception); + + return NULL; +} + +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 +static char global_heap_buf[CONFIG_GLOBAL_HEAP_BUF_SIZE] = { 0 }; +#endif + +void +iwasm_main(void *arg1, void *arg2, void *arg3) +{ + int start, end; + start = k_uptime_get_32(); + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + + (void)arg1; + (void)arg2; + (void)arg3; + + os_printf("User mode thread: start\n"); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#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; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#elif (defined(CONFIG_COMMON_LIBC_MALLOC) \ + && CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE != 0) \ + || defined(CONFIG_NEWLIB_LIBC) + init_args.mem_alloc_type = Alloc_With_System_Allocator; +#else +#error "memory allocation scheme is not defined." +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8 *)wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail1; + } + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate( + wasm_module, CONFIG_APP_STACK_SIZE, CONFIG_APP_HEAP_SIZE, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* invoke the main function */ + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + + end = k_uptime_get_32(); + + os_printf("User mode thread: elapsed %d\n", (end - start)); +} diff --git a/product-mini/platforms/zephyr/user-mode/prj.conf b/product-mini/platforms/zephyr/user-mode/prj.conf new file mode 100644 index 0000000000..023a3caa20 --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/prj.conf @@ -0,0 +1,9 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_USERSPACE=y +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_LOG_BUFFER_SIZE=8096 +CONFIG_THREAD_RUNTIME_STATS=y diff --git a/product-mini/platforms/zephyr/user-mode/src/main.c b/product-mini/platforms/zephyr/user-mode/src/main.c new file mode 100644 index 0000000000..4f51e10d3f --- /dev/null +++ b/product-mini/platforms/zephyr/user-mode/src/main.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include +#include + +#define MAIN_THREAD_STACK_SIZE 2048 +#define MAIN_THREAD_PRIORITY 5 + +static struct k_thread iwasm_user_mode_thread; +K_THREAD_STACK_DEFINE(iwasm_user_mode_thread_stack, MAIN_THREAD_STACK_SIZE); + +extern struct k_mem_partition z_libc_partition; +K_APPMEM_PARTITION_DEFINE(wamr_partition); + +/* WAMR memory domain */ +struct k_mem_domain wamr_domain; + +extern void +iwasm_main(void *arg1, void *arg2, void *arg3); + +bool +iwasm_user_mode(void) +{ + struct k_mem_partition *wamr_domain_parts[] = { &wamr_partition, + &z_libc_partition }; + + printk("wamr_partition start addr: %ld, size: %zu\n", wamr_partition.start, + wamr_partition.size); + + /* Initialize the memory domain with single WAMR partition */ + if (k_mem_domain_init(&wamr_domain, 2, wamr_domain_parts) != 0) { + printk("Failed to initialize memory domain.\n"); + return false; + } + + k_tid_t tid = + k_thread_create(&iwasm_user_mode_thread, iwasm_user_mode_thread_stack, + MAIN_THREAD_STACK_SIZE, iwasm_main, NULL, NULL, NULL, + MAIN_THREAD_PRIORITY, K_USER, K_FOREVER); + + /* Grant WAMR memory domain access to user mode thread */ + if (k_mem_domain_add_thread(&wamr_domain, tid) != 0) { + printk("Failed to add memory domain to thread.\n"); + return false; + } + +#if KERNEL_VERSION_NUMBER < 0x040000 /* version 4.0.0 */ + /* k_thread_start is a legacy API for compatibility. Modern Zephyr threads + * are initialized in the "sleeping" state and do not need special handling + * for "start".*/ + k_thread_start(tid); +#else + /* wakes up thread from sleeping */ + k_wakeup(tid); +#endif + + return tid ? true : false; +} + +#if KERNEL_VERSION_NUMBER < 0x030400 /* version 3.4.0 */ +void +main(void) +{ + iwasm_user_mode(); +} +#else +int +main(void) +{ + iwasm_user_mode(); + return 0; +} +#endif diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000000..a139dcbf27 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,16 @@ +# Samples + +- [**basic**](./basic): Demonstrating how to use runtime exposed API's to call WASM functions, how to register native functions and call them, and how to call WASM function from native function. +- [**custom-section**](./custom-section): Demonstrating how to embed a binary payload into a Wasm custom section, resolve it from native code with `wasm_runtime_get_custom_section`, and print it later through a handle-based native API. +- **[file](./file/README.md)**: Demonstrating the supported file interaction API of WASI. This sample can also demonstrate the SGX IPFS (Intel Protected File System), enabling an enclave to seal and unseal data at rest. +- **[multi-thread](./multi-thread/)**: Demonstrating how to run wasm application which creates multiple threads to execute wasm functions concurrently, and uses mutex/cond by calling pthread related API's. +- **[spawn-thread](./spawn-thread)**: Demonstrating how to execute wasm functions of the same wasm application concurrently, in threads created by host embedder or runtime, but not the wasm application itself. +- **[wasi-threads](./wasi-threads/README.md)**: Demonstrating how to run wasm application which creates multiple threads to execute wasm functions concurrently based on lib wasi-threads. +- **[multi-module](./multi-module)**: Demonstrating the [multiple modules as dependencies](./doc/multi_module.md) feature which implements the [load-time dynamic linking](https://webassembly.org/docs/dynamic-linking/). +- **[ref-types](./ref-types)**: Demonstrating how to call wasm functions with argument of externref type introduced by [reference types proposal](https://github.com/WebAssembly/reference-types). +- **[wasm-c-api](./wasm-c-api/README.md)**: Demonstrating how to run some samples from [wasm-c-api proposal](https://github.com/WebAssembly/wasm-c-api) and showing the supported API's. +- **[socket-api](./socket-api/README.md)**: Demonstrating how to run wasm tcp server and tcp client applications, and how they communicate with each other. +- **[native-lib](./native-lib/README.md)**: Demonstrating how to write required interfaces in native library, build it into a shared library and register the shared library to iwasm. +- **[sgx-ra](./sgx-ra/README.md)**: Demonstrating how to execute Remote Attestation on SGX with [librats](https://github.com/inclavare-containers/librats), which enables mutual attestation with other runtimes or other entities that support librats to ensure that each is running within the TEE. +- **[workload](./workload/README.md)**: Demonstrating how to build and run some complex workloads, e.g. tensorflow-lite, XNNPACK, wasm-av1, meshoptimizer and bwa. +- **[debug-tools](./debug-tools/README.md)**: Demonstrating how to symbolicate a stack trace. diff --git a/samples/basic/.gitignore b/samples/basic/.gitignore new file mode 100644 index 0000000000..0fa8a76bda --- /dev/null +++ b/samples/basic/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/basic/CMakeLists.txt b/samples/basic/CMakeLists.txt new file mode 100644 index 0000000000..244ed81c11 --- /dev/null +++ b/samples/basic/CMakeLists.txt @@ -0,0 +1,88 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (basic) +else() + project (basic C ASM) +endif() + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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) + +if (NOT MSVC) + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT MSVC) + # linker 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 () +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (basic src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) +add_executable (free_buffer_early src/free_buffer_early.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (basic PROPERTIES POSITION_INDEPENDENT_CODE ON) +set_target_properties (free_buffer_early PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (basic vmlib -lm -ldl -lpthread) + target_link_libraries (free_buffer_early vmlib -lm -ldl -lpthread) +else () + target_link_libraries (basic vmlib -lm -ldl -lpthread -lrt) + target_link_libraries (free_buffer_early vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/samples/basic/README.md b/samples/basic/README.md new file mode 100644 index 0000000000..45530ff7f1 --- /dev/null +++ b/samples/basic/README.md @@ -0,0 +1,53 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/basic" +--- + +The "basic" sample project +============== + +This sample demonstrates a few basic usages of embedding WAMR: +- initialize runtime +- load wasm app and instantiate the module +- call wasm function and pass arguments +- export native functions to the WASM apps +- wasm function calls native function and pass arguments +- deinitialize runtime + +Build this sample +============== +Execute the ```build.sh``` script then all binaries including wasm application files would be generated in 'out' directory. + +``` +$ ./build.sh +``` + +Run the sample +========================== +Enter the out directory. +``` +$ cd ./out/ +$ +$ ./basic -f wasm-apps/testapp.wasm +calling into WASM function: generate_float +Native finished calling wasm function generate_float(), returned a float value: 102009.921875f +calling into WASM function: float_to_string +calling into native function: intToStr +calling into native function: get_pow +calling into native function: intToStr +Native finished calling wasm function: float_to_string, returned a formatted string: 102009.921 +``` +Or execute the ```run.sh``` script in ```samples/basic``` folder. +``` +$ ./run.sh +calling into WASM function: generate_float +Native finished calling wasm function generate_float(), returned a float value: 102009.921875f +calling into WASM function: float_to_string +calling into native function: intToStr +calling into native function: get_pow +calling into native function: intToStr +Native finished calling wasm function: float_to_string, returned a formatted string: 102009.921 +``` + + + + diff --git a/samples/basic/build.sh b/samples/basic/build.sh new file mode 100755 index 0000000000..4ea54c679b --- /dev/null +++ b/samples/basic/build.sh @@ -0,0 +1,64 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "#####################build basic project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. -DCMAKE_BUILD_TYPE=Debug -DWAMR_BH_VPRINTF=my_vprintf -DWAMR_BH_LOG=my_log +make -j ${nproc} +if [ $? != 0 ];then + echo "BUILD_FAIL basic exit as $?\n" + exit 2 +fi + +cp -a basic ${OUT_DIR} + +echo -e "\n" + +echo "#####################build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# use WAMR SDK to build out the .wasm binary +/opt/wasi-sdk/bin/clang \ + --target=wasm32 -O0 -z stack-size=4096 -Wl,--initial-memory=65536 \ + --sysroot=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot \ + -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--strip-all,--no-entry -nostdlib \ + -Wl,--export=generate_float \ + -Wl,--export=float_to_string \ + -Wl,--export=calculate\ + -Wl,--export=mul7\ + -Wl,--allow-undefined \ + -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "####################build wasm apps done" diff --git a/samples/basic/run.sh b/samples/basic/run.sh new file mode 100755 index 0000000000..a5fb291667 --- /dev/null +++ b/samples/basic/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/basic -f out/wasm-apps/testapp.wasm \ No newline at end of file diff --git a/samples/basic/src/free_buffer_early.c b/samples/basic/src/free_buffer_early.c new file mode 100644 index 0000000000..b55b6eb1d7 --- /dev/null +++ b/samples/basic/src/free_buffer_early.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" + +void +my_log(uint32 log_level, const char *file, int line, const char *fmt, ...) +{ + char buf[200]; + snprintf(buf, sizeof(buf), "[WamrLogger] %s\n", fmt); + + va_list ap; + va_start(ap, fmt); + vprintf(buf, ap); + va_end(ap); +} + +int +my_vprintf(const char *format, va_list ap) +{ + return vprintf(format, ap); +} + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer = NULL, error_buf[128]; + int opt; + char *wasm_path = NULL; + bool success; + + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + LoadArgs load_args = { 0 }; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + load_args.wasm_binary_freeable = true; + module = wasm_runtime_load_ex((uint8 *)buffer, buf_size, &load_args, + error_buf, sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + if (wasm_runtime_is_underlying_binary_freeable(module)) { + printf("Able to free wasm binary buffer.\n"); + wasm_runtime_free(buffer); + buffer = NULL; + } + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s.\n", error_buf); + goto fail; + } + + char *args[1] = { "3" }; + success = wasm_application_execute_func(module_inst, "mul7", 1, args); + if (!success) { + printf("Unable to execute function.\n"); + goto fail; + } + +fail: + if (module_inst) + wasm_runtime_deinstantiate(module_inst); + if (module) + wasm_runtime_unload(module); + if (buffer) + wasm_runtime_free(buffer); + wasm_runtime_destroy(); + return 0; +} \ No newline at end of file diff --git a/samples/basic/src/main.c b/samples/basic/src/main.c new file mode 100644 index 0000000000..386785a546 --- /dev/null +++ b/samples/basic/src/main.c @@ -0,0 +1,242 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" + +int +intToStr(int x, char *str, int str_len, int digit); +int +get_pow(int x, int y); +int32_t +calculate_native(int32_t n, int32_t func1, int32_t func2); + +void +my_log(uint32 log_level, const char *file, int line, const char *fmt, ...) +{ + char buf[200]; + snprintf(buf, 200, + log_level == WASM_LOG_LEVEL_VERBOSE ? "[WamrLogger - VERBOSE] %s" + : "[WamrLogger] %s", + fmt); + + va_list ap; + va_start(ap, fmt); + vprintf(buf, ap); + va_end(ap); +} + +int +my_vprintf(const char *format, va_list ap) +{ + /* Print in blue */ + char buf[200]; + snprintf(buf, 200, "\x1b[34m%s\x1b[0m", format); + return vprintf(buf, ap); +} + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer, error_buf[128]; + int opt; + char *wasm_path = NULL; + + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + wasm_function_inst_t func = NULL; + wasm_function_inst_t func2 = NULL; + char *native_buffer = NULL; + uint64_t wasm_buffer = 0; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + // Define an array of NativeSymbol for the APIs to be exported. + // Note: the array must be static defined since runtime + // will keep it after registration + // For the function signature specifications, goto the link: + // https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md + + static NativeSymbol native_symbols[] = { + { + "intToStr", // the name of WASM function name + intToStr, // the native function pointer + "(i*~i)i", // the function prototype signature, avoid to use i32 + NULL // attachment is NULL + }, + { + "get_pow", // the name of WASM function name + get_pow, // the native function pointer + "(ii)i", // the function prototype signature, avoid to use i32 + NULL // attachment is NULL + }, + { "calculate_native", calculate_native, "(iii)i", NULL } + }; + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + // Native symbols need below registration phase + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE); + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if (!exec_env) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + if (!(func = wasm_runtime_lookup_function(module_inst, "generate_float"))) { + printf("The generate_float wasm function is not found.\n"); + goto fail; + } + + wasm_val_t results[1] = { { .kind = WASM_F32, .of.f32 = 0 } }; + wasm_val_t arguments[3] = { + { .kind = WASM_I32, .of.i32 = 10 }, + { .kind = WASM_F64, .of.f64 = 0.000101 }, + { .kind = WASM_F32, .of.f32 = 300.002 }, + }; + + // pass 4 elements for function arguments + if (!wasm_runtime_call_wasm_a(exec_env, func, 1, results, 3, arguments)) { + printf("call wasm function generate_float failed. %s\n", + wasm_runtime_get_exception(module_inst)); + goto fail; + } + + float ret_val; + ret_val = results[0].of.f32; + printf("Native finished calling wasm function generate_float(), returned a " + "float value: %ff\n", + ret_val); + + // Next we will pass a buffer to the WASM function + uint32 argv2[4]; + + // must allocate buffer from wasm instance memory space (never use pointer + // from host runtime) + wasm_buffer = + wasm_runtime_module_malloc(module_inst, 100, (void **)&native_buffer); + + memcpy(argv2, &ret_val, sizeof(float)); // the first argument + argv2[1] = wasm_buffer; // the second argument is the wasm buffer address + argv2[2] = 100; // the third argument is the wasm buffer size + argv2[3] = 3; // the last argument is the digits after decimal point for + // converting float to string + + if (!(func2 = + wasm_runtime_lookup_function(module_inst, "float_to_string"))) { + printf( + "The wasm function float_to_string wasm function is not found.\n"); + goto fail; + } + + if (wasm_runtime_call_wasm(exec_env, func2, 4, argv2)) { + printf("Native finished calling wasm function: float_to_string, " + "returned a formatted string: %s\n", + native_buffer); + } + else { + printf("call wasm function float_to_string failed. error: %s\n", + wasm_runtime_get_exception(module_inst)); + goto fail; + } + + wasm_function_inst_t func3 = + wasm_runtime_lookup_function(module_inst, "calculate"); + if (!func3) { + printf("The wasm function calculate is not found.\n"); + goto fail; + } + + uint32_t argv3[1] = { 3 }; + if (wasm_runtime_call_wasm(exec_env, func3, 1, argv3)) { + uint32_t result = *(uint32_t *)argv3; + printf("Native finished calling wasm function: calculate, return: %d\n", + result); + } + else { + printf("call wasm function calculate failed. error: %s\n", + wasm_runtime_get_exception(module_inst)); + goto fail; + } + +fail: + if (exec_env) + wasm_runtime_destroy_exec_env(exec_env); + if (module_inst) { + if (wasm_buffer) + wasm_runtime_module_free(module_inst, (uint64)wasm_buffer); + wasm_runtime_deinstantiate(module_inst); + } + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/basic/src/native_impl.c b/samples/basic/src/native_impl.c new file mode 100644 index 0000000000..0d0c45ae54 --- /dev/null +++ b/samples/basic/src/native_impl.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "wasm_export.h" +#include "math.h" + +// The first parameter is not exec_env because it is invoked by native functions +void +reverse(char *str, int len) +{ + int i = 0, j = len - 1, temp; + while (i < j) { + temp = str[i]; + str[i] = str[j]; + str[j] = temp; + i++; + j--; + } +} + +// The first parameter exec_env must be defined using type wasm_exec_env_t +// which is the calling convention for exporting native API by WAMR. +// +// Converts a given integer x to string str[]. +// digit is the number of digits required in the output. +// If digit is more than the number of digits in x, +// then 0s are added at the beginning. +int +intToStr(wasm_exec_env_t exec_env, int x, char *str, int str_len, int digit) +{ + int i = 0; + + printf("calling into native function: %s\n", __FUNCTION__); + + while (x) { + // native is responsible for checking the str_len overflow + if (i >= str_len) { + return -1; + } + str[i++] = (x % 10) + '0'; + x = x / 10; + } + + // If number of digits required is more, then + // add 0s at the beginning + while (i < digit) { + if (i >= str_len) { + return -1; + } + str[i++] = '0'; + } + + reverse(str, i); + + if (i >= str_len) + return -1; + str[i] = '\0'; + return i; +} + +int +get_pow(wasm_exec_env_t exec_env, int x, int y) +{ + printf("calling into native function: %s\n", __FUNCTION__); + return (int)pow(x, y); +} + +int32_t +calculate_native(wasm_exec_env_t exec_env, int32_t n, int32_t func1, + int32_t func2) +{ + printf("calling into native function: %s, n=%d, func1=%d, func2=%d\n", + __FUNCTION__, n, func1, func2); + + uint32_t argv[] = { n }; + if (!wasm_runtime_call_indirect(exec_env, func1, 1, argv)) { + printf("call func1 failed\n"); + return 0xDEAD; + } + + uint32_t n1 = argv[0]; + printf("call func1 and return n1=%d\n", n1); + + if (!wasm_runtime_call_indirect(exec_env, func2, 1, argv)) { + printf("call func2 failed\n"); + return 0xDEAD; + } + + uint32_t n2 = argv[0]; + printf("call func2 and return n2=%d\n", n2); + return n1 + n2; +} diff --git a/samples/basic/wasm-apps/testapp.c b/samples/basic/wasm-apps/testapp.c new file mode 100644 index 0000000000..8db293071c --- /dev/null +++ b/samples/basic/wasm-apps/testapp.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +int +intToStr(int x, char *str, int str_len, int digit); +int +get_pow(int x, int y); +int32_t +calculate_native(int32_t n, int32_t func1, int32_t func2); + +// +// Primitive parameters functions +// +float +generate_float(int iteration, double seed1, float seed2) +{ + float ret; + + printf("calling into WASM function: %s\n", __FUNCTION__); + + for (int i = 0; i < iteration; i++) { + ret += 1.0f / seed1 + seed2; + } + + return ret; +} + +// Converts a floating-point/double number to a string. +// intToStr() is implemented outside wasm app +void +float_to_string(float n, char *res, int res_size, int afterpoint) +{ + + printf("calling into WASM function: %s\n", __FUNCTION__); + + // Extract integer part + int ipart = (int)n; + + // Extract floating part + float fpart = n - (float)ipart; + + // convert integer part to string + int i = intToStr(ipart, res, res_size, 0); + + // check for display option after point + if (afterpoint != 0) { + res[i] = '.'; // add dot + + // Get the value of fraction part upto given no. + // of points after dot. The third parameter + // is needed to handle cases like 233.007 + fpart = fpart * get_pow(10, afterpoint); + + intToStr((int)fpart, res + i + 1, res_size - i - 1, afterpoint); + } +} + +int32_t +mul7(int32_t n) +{ + printf("calling into WASM function: %s,", __FUNCTION__); + n = n * 7; + printf(" %s return %d \n", __FUNCTION__, n); + return n; +} + +int32_t +mul5(int32_t n) +{ + printf("calling into WASM function: %s,", __FUNCTION__); + n = n * 5; + printf(" %s return %d \n", __FUNCTION__, n); + return n; +} + +int32_t +calculate(int32_t n) +{ + printf("calling into WASM function: %s\n", __FUNCTION__); + int32_t (*f1)(int32_t) = &mul5; + int32_t (*f2)(int32_t) = &mul7; + return calculate_native(n, (uint32_t)f1, (uint32_t)f2); +} diff --git a/samples/bh-atomic/CMakeLists.txt b/samples/bh-atomic/CMakeLists.txt new file mode 100644 index 0000000000..177cf2c5eb --- /dev/null +++ b/samples/bh-atomic/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2023 Midokura Japan KK. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(bh_atomic) + +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if(APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif() + +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_LIBC_BUILTIN 0) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_executable(bh_atomic main.c) + +target_link_libraries(bh_atomic) diff --git a/samples/bh-atomic/main.c b/samples/bh-atomic/main.c new file mode 100644 index 0000000000..61c52800bd --- /dev/null +++ b/samples/bh-atomic/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "bh_platform.h" +#include "bh_atomic.h" + +int +main(int argc, char **argv) +{ + bh_atomic_32_t v; + uint32 o; + + v = 0x00ff00ff; + o = BH_ATOMIC_32_LOAD(v); + assert(o == 0x00ff00ff); + + v = 0x00ff00ff; + o = BH_ATOMIC_32_FETCH_OR(v, 0xffff0000); + assert(o == 0x00ff00ff); + assert(v == 0xffff00ff); + + v = 0x00ff00ff; + o = BH_ATOMIC_32_FETCH_AND(v, 0xffff0000); + assert(o == 0x00ff00ff); + assert(v == 0x00ff0000); + + v = 0x00ff00ff; + o = BH_ATOMIC_32_FETCH_ADD(v, 0x10101); + assert(o == 0x00ff00ff); + assert(v == 0x00ff00ff + 0x10101); + + v = 0x00ff00ff; + o = BH_ATOMIC_32_FETCH_SUB(v, 0x10101); + assert(o == 0x00ff00ff); + assert(v == 0x00ff00ff - 0x10101); + + return 0; +} diff --git a/samples/cmake/FindEMSCRIPTEN.cmake b/samples/cmake/FindEMSCRIPTEN.cmake new file mode 100644 index 0000000000..8f63ec5453 --- /dev/null +++ b/samples/cmake/FindEMSCRIPTEN.cmake @@ -0,0 +1,45 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_path(EMSCRIPTEN_HOME + NAMES upstream/emscripten + PATHS /opt/emsdk + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_file(EMSCRIPTEN_VERSION_FILE + NAMES emscripten-version.txt + PATHS ${EMSCRIPTEN_HOME}/upstream/emscripten + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +file(READ ${EMSCRIPTEN_VERSION_FILE} EMSCRIPTEN_VERSION_FILE_CONTENT) + +string(REGEX + MATCH + "[0-9]+\.[0-9]+(\.[0-9]+)*" + EMSCRIPTEN_VERSION + ${EMSCRIPTEN_VERSION_FILE_CONTENT} +) + +find_package_handle_standard_args(EMSCRIPTEN + REQUIRED_VARS EMSCRIPTEN_HOME + VERSION_VAR EMSCRIPTEN_VERSION + HANDLE_VERSION_RANGE +) + +if(EMSCRIPTEN_FOUND) + set(EMSCRIPTEN_TOOLCHAIN ${EMSCRIPTEN_HOME}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake) + set(EMCC ${EMSCRIPTEN_HOME}/upstream/emscripten/emcc) +endif() +mark_as_advanced(EMSCRIPTEN_TOOLCHAIN EMCC) diff --git a/samples/cmake/FindWAMRC.cmake b/samples/cmake/FindWAMRC.cmake new file mode 100644 index 0000000000..20f9416f74 --- /dev/null +++ b/samples/cmake/FindWAMRC.cmake @@ -0,0 +1,27 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_path(WAMRC_HOME + wamr-compiler + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_file(WAMRC_BIN + wamrc + HINTS ${WAMRC_HOME}/wamr-compiler/build + NO_DEFAULT_PATH + NO_CMAKE_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + REQUIRED +) + +find_package_handle_standard_args(WAMRC REQUIRED_VARS WAMRC_BIN) +mark_as_advanced(WAMRC_BIN) diff --git a/samples/cmake/FindWASISDK.cmake b/samples/cmake/FindWASISDK.cmake new file mode 100644 index 0000000000..65b9647d90 --- /dev/null +++ b/samples/cmake/FindWASISDK.cmake @@ -0,0 +1,25 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +file(GLOB WASISDK_SEARCH_PATH "/opt/wasi-sdk-*") +find_path(WASISDK_HOME + NAMES share/wasi-sysroot + PATHS ${WASISDK_SEARCH_PATH} + NO_DEFAULT_PATH + REQUIRED +) + +string(REGEX MATCH [0-9]+\.[0-9]+\.*[0-9]* WASISDK_VERSION ${WASISDK_HOME}) + +find_package_handle_standard_args(WASISDK REQUIRED_VARS WASISDK_HOME VERSION_VAR WASISDK_VERSION) + +if(WASISDK_FOUND) + set(WASISDK_CC_COMMAND ${WASISDK_HOME}/bin/clang) + set(WASISDK_CXX_COMMAND ${WASISDK_HOME}/bin/clang++) + set(WASISDK_TOOLCHAIN ${WASISDK_HOME}/share/cmake/wasi-sdk.cmake) + set(WASISDK_PTHREAD_TOOLCHAIN ${WASISDK_HOME}/share/cmake/wasi-sdk-pthread.cmake) + set(WASISDK_SYSROOT ${WASISDK_HOME}/share/wasi-sysroot) +endif() +mark_as_advanced(WASISDK_CC_COMMAND WASISDK_CXX_COMMAND WASISDK_TOOLCHAIN WASISDK_PTHREAD_TOOLCHAIN WASISDK_SYSROOT WASISDK_HOME) diff --git a/samples/custom-section/.gitignore b/samples/custom-section/.gitignore new file mode 100644 index 0000000000..6a3417b8d9 --- /dev/null +++ b/samples/custom-section/.gitignore @@ -0,0 +1 @@ +/out/ diff --git a/samples/custom-section/CMakeLists.txt b/samples/custom-section/CMakeLists.txt new file mode 100644 index 0000000000..1a0a94d444 --- /dev/null +++ b/samples/custom-section/CMakeLists.txt @@ -0,0 +1,80 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +if(NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project(custom_section) +else() + project(custom_section C ASM) +endif() + +# ############### runtime settings ################ +string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) + +if(APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif() + +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +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) + set(WAMR_BUILD_TARGET "X86_64") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + 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 Debug) +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_LOAD_CUSTOM_SECTION 1) + +if(NOT MSVC) + set(WAMR_BUILD_LIBC_WASI 1) +endif() + +if(NOT MSVC) + 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() +endif() + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +# ############### application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include(${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable(custom_section + src/main.c + src/native_impl.c + ${UNCOMMON_SHARED_SOURCE} +) + +check_pie_supported() +set_target_properties(custom_section PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if(APPLE) + target_link_libraries(custom_section vmlib -lm -ldl -lpthread) +else() + target_link_libraries(custom_section vmlib -lm -ldl -lpthread -lrt) +endif() diff --git a/samples/custom-section/README.md b/samples/custom-section/README.md new file mode 100644 index 0000000000..63dff44bf5 --- /dev/null +++ b/samples/custom-section/README.md @@ -0,0 +1,69 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/custom-section" +--- + +# The "custom-section" sample project + +This sample demonstrates how to: + +- embed a separate binary payload into a Wasm custom section through a `.s` file +- load that Wasm module with `WAMR_BUILD_LOAD_CUSTOM_SECTION=1` +- export native APIs that resolve a custom section name to a host-side handle +- print the custom section bytes later through a second native API +- optionally compile the Wasm module to AoT while preserving the `demo` custom section + +The Wasm application is built from: + +- `wasm-apps/custom_section.c` +- `wasm-apps/custom_section_payload.s` +- `wasm-apps/custom_section_payload.bin` + +The assembler file emits a section named `.custom_section.demo`, which becomes a Wasm custom section named `demo` in the final `.wasm` file. + +## Why use a custom section for this payload + +The payload in this sample is treated as read-only metadata. Putting it in a custom section lets the embedder access the bytes directly from the loaded module through `wasm_runtime_get_custom_section`, instead of copying the data into Wasm linear memory per instance. + +That matters when the data is large or rarely changed: + +- the bytes stay in the module image as immutable data +- the host can look them up by section name and use them in place +- the Wasm app only needs to pass a small section name and receive a small handle +- no extra application-level serialization or buffer duplication is needed for the read-only payload + +This pattern is useful for embedded assets, lookup tables, model metadata, certificates, and other static blobs that the host wants to consume without treating them as mutable Wasm heap data. + +## Build this sample + +Execute the `build.sh` script. The host executable and the Wasm app are generated in `out`. + +```sh +./build.sh +``` + +Build the AoT variant only when needed by passing `--aot`. This preserves the `demo` custom section in the generated AoT file by calling `wamrc --emit-custom-sections=demo`. + +```sh +./build.sh --aot +``` + +## Run the sample + +Enter the output directory and run the Wasm sample directly: + +```sh +cd ./out/ +./custom_section -f wasm-apps/custom_section.wasm +``` + +Or run the helper script from `samples/custom_section`: + +```sh +./run.sh +``` + +To run the AoT artifact instead, pass `--aot` to the helper script: + +```sh +./run.sh --aot +``` diff --git a/samples/custom-section/build.sh b/samples/custom-section/build.sh new file mode 100755 index 0000000000..4b4835b1d2 --- /dev/null +++ b/samples/custom-section/build.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +set -e + +CURR_DIR=$PWD +OUT_DIR=${PWD}/out +WASM_APPS=${PWD}/wasm-apps +WAMR_ROOT_DIR=${PWD}/../.. +WAMRC_CMD=${WAMR_ROOT_DIR}/wamr-compiler/build/wamrc +BUILD_AOT=0 + +if [ $# -gt 1 ]; then + echo "Usage: $0 [--aot]" + exit 1 +fi + +if [ $# -eq 1 ]; then + if [ "$1" = "--aot" ]; then + BUILD_AOT=1 + else + echo "Usage: $0 [--aot]" + exit 1 + fi +fi + +rm -rf ${OUT_DIR} +mkdir -p ${OUT_DIR}/wasm-apps + +printf '##################### build custom-section project\n' +mkdir -p build +cd build +cmake .. -DCMAKE_BUILD_TYPE=Debug +make -j 4 +cp -a custom_section ${OUT_DIR} + +printf '\n##################### build wasm app\n' +cd ${WASM_APPS} +/opt/wasi-sdk/bin/clang \ + --target=wasm32 \ + -O0 \ + -nostdlib \ + -Wl,--strip-all,--no-entry \ + -Wl,--allow-undefined \ + -Wl,--export=run_demo \ + -o ${OUT_DIR}/wasm-apps/custom_section.wasm \ + custom_section.c \ + custom_section_payload.s + +printf '\nbuild custom_section.wasm success\n' + +if [ ${BUILD_AOT} -eq 1 ]; then + if [ ! -x ${WAMRC_CMD} ]; then + echo "Error: wamrc not found at ${WAMRC_CMD}" + echo "Please build wamrc first under ${WAMR_ROOT_DIR}/wamr-compiler" + exit 1 + fi + + printf '\n##################### build aot app\n' + ${WAMRC_CMD} --emit-custom-sections=demo -o ${OUT_DIR}/wasm-apps/custom_section.aot ${OUT_DIR}/wasm-apps/custom_section.wasm + printf '\nbuild custom_section.aot success\n' +fi + +cd ${CURR_DIR} diff --git a/samples/custom-section/run.sh b/samples/custom-section/run.sh new file mode 100755 index 0000000000..caaa7cb810 --- /dev/null +++ b/samples/custom-section/run.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e + +if [ $# -gt 1 ]; then + echo "Usage: $0 [--aot]" + exit 1 +fi + +APP=out/wasm-apps/custom_section.wasm + +if [ $# -eq 1 ]; then + if [ "$1" = "--aot" ]; then + APP=out/wasm-apps/custom_section.aot + else + echo "Usage: $0 [--aot]" + exit 1 + fi +fi + +if [ ! -f ${APP} ]; then + echo "Error: ${APP} not found" + if [ "$APP" = "out/wasm-apps/custom_section.aot" ]; then + echo "Run ./build.sh --aot first" + else + echo "Run ./build.sh first" + fi + exit 1 +fi + +out/custom_section -f ${APP} diff --git a/samples/custom-section/src/main.c b/samples/custom-section/src/main.c new file mode 100644 index 0000000000..cc76cd3f44 --- /dev/null +++ b/samples/custom-section/src/main.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_getopt.h" +#include "bh_read_file.h" +#include "bh_log.h" +#include "wasm_export.h" + +int32_t +get_custom_section_handle(wasm_exec_env_t exec_env, const char *section_name); +void +print_custom_section(wasm_exec_env_t exec_env, int32_t handle); +void +reset_custom_section_handles(void); + +static void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file]\n"); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + RuntimeInitArgs init_args; + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + wasm_function_inst_t func = NULL; + wasm_val_t results[1] = { { .kind = WASM_I32, .of.i32 = -1 } }; + char *buffer = NULL; + char *wasm_path = NULL; + char error_buf[128]; + int exit_code = 1; + int opt; + uint32_t buf_size; + uint32_t stack_size = 8092; + uint32_t heap_size = 8092; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + + if (optind == 1) { + print_usage(); + return 0; + } + + static NativeSymbol native_symbols[] = { + { "get_custom_section_handle", get_custom_section_handle, "($)i", + NULL }, + { "print_custom_section", print_custom_section, "(i)", NULL }, + }; + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + bh_log_set_verbose_level(BH_LOG_LEVEL_VERBOSE); + + reset_custom_section_handles(); + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if (!exec_env) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + func = wasm_runtime_lookup_function(module_inst, "run_demo"); + if (!func) { + printf("The wasm function run_demo is not found.\n"); + goto fail; + } + + if (!wasm_runtime_call_wasm_a(exec_env, func, 1, results, 0, NULL)) { + printf("call wasm function run_demo failed. error: %s\n", + wasm_runtime_get_exception(module_inst)); + goto fail; + } + + printf("Wasm returned custom section handle: %d\n", results[0].of.i32); + exit_code = results[0].of.i32 >= 0 ? 0 : 1; + +fail: + if (exec_env) { + wasm_runtime_destroy_exec_env(exec_env); + } + if (module_inst) { + wasm_runtime_deinstantiate(module_inst); + } + if (module) { + wasm_runtime_unload(module); + } + if (buffer) { + BH_FREE(buffer); + } + reset_custom_section_handles(); + wasm_runtime_destroy(); + return exit_code; +} diff --git a/samples/custom-section/src/native_impl.c b/samples/custom-section/src/native_impl.c new file mode 100644 index 0000000000..45f9ca9fdb --- /dev/null +++ b/samples/custom-section/src/native_impl.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +#define MAX_CUSTOM_SECTION_HANDLES 8 + +typedef struct CustomSectionHandle { + wasm_module_t module; + const uint8_t *content; + uint32_t length; +} CustomSectionHandle; + +static CustomSectionHandle custom_section_handles[MAX_CUSTOM_SECTION_HANDLES]; + +void +reset_custom_section_handles(void) +{ + memset(custom_section_handles, 0, sizeof(custom_section_handles)); +} + +int32_t +get_custom_section_handle(wasm_exec_env_t exec_env, const char *section_name) +{ + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + wasm_module_t module = wasm_runtime_get_module(module_inst); + const uint8_t *content = NULL; + uint32_t length = 0; + uint32_t i; + + if (!section_name || section_name[0] == '\0') { + printf("custom section name is empty\n"); + return -1; + } + + content = wasm_runtime_get_custom_section(module, section_name, &length); + if (!content) { + printf("custom section [%s] not found\n", section_name); + return -1; + } + + for (i = 0; i < MAX_CUSTOM_SECTION_HANDLES; i++) { + if (custom_section_handles[i].content == content + && custom_section_handles[i].module == module + && custom_section_handles[i].length == length) { + return (int32_t)i; + } + } + + for (i = 0; i < MAX_CUSTOM_SECTION_HANDLES; i++) { + if (!custom_section_handles[i].content) { + custom_section_handles[i].module = module; + custom_section_handles[i].content = content; + custom_section_handles[i].length = length; + printf("resolved custom section [%s] to handle %u (%u bytes)\n", + section_name, i, length); + return (int32_t)i; + } + } + + printf("no free custom section handle slots remain\n"); + return -1; +} + +void +print_custom_section(wasm_exec_env_t exec_env, int32_t handle) +{ + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + wasm_module_t module = wasm_runtime_get_module(module_inst); + CustomSectionHandle *section_handle = NULL; + + if (handle < 0 || handle >= MAX_CUSTOM_SECTION_HANDLES) { + printf("invalid custom section handle %d\n", handle); + return; + } + + section_handle = &custom_section_handles[handle]; + if (!section_handle->content || section_handle->module != module) { + printf("custom section handle %d is not valid for this module\n", + handle); + return; + } + + printf("custom section payload (%u bytes):\n", section_handle->length); + fwrite(section_handle->content, 1, section_handle->length, stdout); + if (section_handle->length == 0 + || section_handle->content[section_handle->length - 1] != '\n') { + putchar('\n'); + } +} diff --git a/samples/custom-section/wasm-apps/custom_section.c b/samples/custom-section/wasm-apps/custom_section.c new file mode 100644 index 0000000000..89bcde957e --- /dev/null +++ b/samples/custom-section/wasm-apps/custom_section.c @@ -0,0 +1,19 @@ +__attribute__((import_module("env"), + import_name("get_custom_section_handle"))) int +get_custom_section_handle(const char *section_name); + +__attribute__((import_module("env"), import_name("print_custom_section"))) void +print_custom_section(int handle); + +__attribute__((export_name("run_demo"))) int +run_demo(void) +{ + static const char section_name[] = "demo"; + int handle = get_custom_section_handle(section_name); + + if (handle >= 0) { + print_custom_section(handle); + } + + return handle; +} diff --git a/samples/custom-section/wasm-apps/custom_section_payload.bin b/samples/custom-section/wasm-apps/custom_section_payload.bin new file mode 100644 index 0000000000..4693af5125 --- /dev/null +++ b/samples/custom-section/wasm-apps/custom_section_payload.bin @@ -0,0 +1,5 @@ +This payload lives in a Wasm custom section. +It is linked by custom_section_payload.s with .incbin. + +The payload is read by custom_section.c, which is compiled to a Wasm module and executed by the host embedder. +It can be arbitrary data, and the host can resolve it with `wasm_runtime_get_custom_section` and print/use it later through a handle-based native API. diff --git a/samples/custom-section/wasm-apps/custom_section_payload.s b/samples/custom-section/wasm-apps/custom_section_payload.s new file mode 100644 index 0000000000..8269ff4e20 --- /dev/null +++ b/samples/custom-section/wasm-apps/custom_section_payload.s @@ -0,0 +1,2 @@ +.section .custom_section.demo,"",@ +.incbin "custom_section_payload.bin" diff --git a/samples/debug-tools/CMakeLists.txt b/samples/debug-tools/CMakeLists.txt new file mode 100644 index 0000000000..32d219faa5 --- /dev/null +++ b/samples/debug-tools/CMakeLists.txt @@ -0,0 +1,106 @@ +# Copyright (C) 2024 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(debug_tools_sample) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake) +find_package(WASISDK REQUIRED) + +option(SOURCE_MAP_DEMO "Enable source map demo" OFF) +if (SOURCE_MAP_DEMO) + find_package(EMSCRIPTEN 3.1.50 REQUIRED) +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_LIBC_WASI 1) +set(WAMR_BUILD_FAST_INTERP 0) # Otherwise addresses don't match llvm-dwarfdump (addr2line) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_DUMP_CALL_STACK 1) # Otherwise stack trace is not printed (addr2line) + +# 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 () + +# 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 ################ +include(ExternalProject) + +# wasm32-wasi +ExternalProject_Add(wasm32-wasi + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) + +if (EMSCRIPTEN_FOUND) + # wasm32-emscripten + ExternalProject_Add(wasm32-emscripten + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_TOOLCHAIN} + -DCMAKE_VERBOSE_MAKEFILE=On + -DSOURCE_MAP_DEMO=On + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR}/emscripten + ) +endif () + +################ 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 -lm -ldl) diff --git a/samples/debug-tools/README.md b/samples/debug-tools/README.md new file mode 100644 index 0000000000..b0358b9e4f --- /dev/null +++ b/samples/debug-tools/README.md @@ -0,0 +1,133 @@ +# "debug-tools" sample introduction + +Tool to symoblicate stack traces. When using wasm in production, debug info are usually stripped using tools like `wasm-opt`, to decrease the binary size. If a corresponding unstripped wasm file is kept, location information (function, file, line, column) can be retrieved from the stripped stack trace. + +## Build and run the sample + +### Generate the stack trace + +Build `iwasm` with `WAMR_BUILD_DUMP_CALL_STACK=1` and `WAMR_BUILD_FAST_INTERP=0` and the wasm file with debug info (e.g. `clang -g`). As it is done in [CMakeLists.txt](./CMakeLists.txt) and [wasm-apps/CMakeLists.txt](./wasm-apps/CMakeLists.txt) (look for `addr2line`): + +```bash +$ mkdir build && cd build +$ cmake .. +$ make +$ ./iwasm wasm-apps/trap.wasm +``` + +The output should be something like + +```text +#00: 0x0159 - $f5 +#01: 0x01b2 - $f6 +#02: 0x0200 - $f7 +#03: 0x026b - $f8 +#04: 0x236b - $f15 +#05: 0x011f - _start + +Exception: unreachable +``` + +Copy the stack trace printed to stdout into a separate file (`call_stack.txt`): + +```bash +$ ./iwasm wasm-apps/trap.wasm | grep "#" > call_stack.txt +``` + +Same for AOT. The AOT binary has to be generated using the `--enable-dump-call-stack` option of `wamrc`, as in [CMakeLists.txt](./wasm-apps/CMakeLists.txt). Then run: + +```bash +$ ./iwasm wasm-apps/trap.aot | grep "#" > call_stack.txt +``` + +### Symbolicate the stack trace + +Run the [addr2line](../../test-tools/addr2line/addr2line.py) script to symbolicate the stack trace: + +```bash +$ python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file wasm-apps/trap.wasm \ + call_stack.txt +``` + +The output should be something like: + +```text +0: c + at wasm-micro-runtime/samples/debug-tools/wasm-apps/trap.c:5:1 +1: b + at wasm-micro-runtime/samples/debug-tools/wasm-apps/trap.c:11:12 +2: a + at wasm-micro-runtime/samples/debug-tools/wasm-apps/trap.c:17:12 +3: main + at wasm-micro-runtime/samples/debug-tools/wasm-apps/trap.c:24:5 +4: __main_void + at unknown:?:? +5: _start +``` + +If WAMR is run in fast interpreter mode (`WAMR_BUILD_FAST_INTERP=1`), addresses in the stack trace cannot be tracked back to location info. +If WAMR <= `1.3.2` is used, the stack trace does not contain addresses. +In those two cases, run the script with `--no-addr`: the line info returned refers to the start of the function + +```bash +$ python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file wasm-apps/trap.wasm \ + call_stack.txt --no-addr +``` + +#### sourcemap + +This script also supports _sourcemap_ which is produced by [_emscripten_](https://emscripten.org/docs/tools_reference/emcc.html). The _sourcemap_ is used to map the wasm function to the original source file. To use it, add `-gsource-map` option to _emcc_ command line. The output should be a section named "sourceMappingURL" and a separated file named "_.map_. + +If the wasm file is with _sourcemap_, the script will use it to get the source file and line info. It needs an extra command line option `--emsdk` to specify the path of _emsdk_. The script will use _emsymbolizer_ to query the source file and line info. + +````bash +$ python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file emscripten/wasm-apps/trap.wasm \ + --emsdk /opt/emsdk \ + call_stack.from_wasm_w_sourcemap.txt + +The output should be something like: + +```text +1: c + at ../../../../../wasm-apps/trap.c:5:1 +2: b + at ../../../../../wasm-apps/trap.c:11:12 +3: a + at ../../../../../wasm-apps/trap.c:17:12 +4: main + at ../../../../../wasm-apps/trap.c:24:5 +5: __main_void + at ../../../../../../../../../emsdk/emscripten/system/lib/standalone/__main_void.c:53:10 +6: _start + at ../../../../../../../../../emsdk/emscripten/system/lib/libc/crt1.c:27:3 +```` + +> The script assume the separated map file _.map_ is in the same directory as the wasm file. + +### Another approach + +If the wasm file is with "name" section, it is able to output function name in the stack trace. To achieve that, need to enable `WAMR_BUILD_LOAD_CUSTOM_SECTION` and `WAMR_BUILD_CUSTOM_NAME_SECTION`. If using .aot file, need to add `--emit-custom-sections=name` into wamrc command line options. + +Then the output should be something like + +```text +#00: 0x0159 - c +#01: 0x01b2 - b +#02: 0x0200 - a +#03: 0x026b - main +#04: 0x236b - __main_void +#05: 0x011f - _start + +Exception: unreachable +``` + +Also, it is able to use _addr2line.py_ to add file and line info to the stack trace. diff --git a/samples/debug-tools/symbolicate.sh b/samples/debug-tools/symbolicate.sh new file mode 100644 index 0000000000..709622f03d --- /dev/null +++ b/samples/debug-tools/symbolicate.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -euox pipefail + +# Symbolicate .wasm +python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file wasm-apps/trap.wasm \ + call_stack.txt + +# Symbolicate .wasm with `--no-addr` +python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file wasm-apps/trap.wasm \ + call_stack.txt --no-addr + +# Symbolicate .aot +python3 ../../../test-tools/addr2line/addr2line.py \ + --wasi-sdk /opt/wasi-sdk \ + --wabt /opt/wabt \ + --wasm-file wasm-apps/trap.wasm \ + call_stack_aot.txt \ No newline at end of file diff --git a/samples/debug-tools/wasm-apps/CMakeLists.txt b/samples/debug-tools/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000000..9858b98dab --- /dev/null +++ b/samples/debug-tools/wasm-apps/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (C) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project (debut_tools_wasm) + +set (CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) + +list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../cmake) +find_package (WAMRC REQUIRED) + +option(SOURCE_MAP_DEMO "Enable source map demo" OFF) +if (SOURCE_MAP_DEMO) + find_package(EMSCRIPTEN 3.1.50 REQUIRED) +endif () + +################ wasm and aot compilation ################ +function (compile_sample SOURCE_FILE) + get_filename_component (FILE_NAME ${SOURCE_FILE} NAME_WLE) + + ## wasm + set (WASM_FILE ${FILE_NAME}.wasm) + add_executable (${FILE_NAME} ${SOURCE_FILE}) + set_target_properties (${FILE_NAME} PROPERTIES SUFFIX .wasm) + + ## aot + set (AOT_FILE ${FILE_NAME}.aot) + add_custom_target ( + ${FILE_NAME}_aot + ALL + DEPENDS ${WAMRC_BIN} ${WASM_FILE} + # Use --enable-dump-call-stack to generate stack trace (addr2line) + COMMAND ${WAMRC_BIN} --size-level=0 --enable-dump-call-stack -o ${AOT_FILE} ${WASM_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + + ## wasm + sourcemap + if (DEFINED EMSCRIPTEN) + add_custom_target( + ${FILE_NAME}_w_sourcemap + ALL + DEPENDS ${SOURCE_FILE} + COMMAND ${EMCC} -O0 -gsource-map -o ${FILE_NAME}.sourcemap.wasm ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + endif () + + ## install both + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${WASM_FILE} DESTINATION wasm-apps) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${AOT_FILE} DESTINATION wasm-apps) + if (DEFINED EMSCRIPTEN) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.sourcemap.wasm DESTINATION wasm-apps) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}.sourcemap.wasm.map DESTINATION wasm-apps) + endif () +endfunction () + +compile_sample(trap.c) diff --git a/samples/debug-tools/wasm-apps/trap.c b/samples/debug-tools/wasm-apps/trap.c new file mode 100644 index 0000000000..364c430d19 --- /dev/null +++ b/samples/debug-tools/wasm-apps/trap.c @@ -0,0 +1,27 @@ +int +c(int n) +{ + __builtin_trap(); +} + +int +b(int n) +{ + n += 3; + return c(n); +} + +int +a(int n) +{ + return b(n); +} + +int +main(int argc, char **argv) +{ + int i = 5; + a(i); + + return 0; +} diff --git a/samples/file/CMakeLists.txt b/samples/file/CMakeLists.txt new file mode 100644 index 0000000000..a5184718bf --- /dev/null +++ b/samples/file/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2022 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(file) + +################ wasm application ############### +add_subdirectory(src) + +# Use ExternalProject to avoid incorporating WAMR library compilation flags into the +# compilation of the wasm application, which could lead to compatibility +# issues due to different targets. +# Like: clang: error: unsupported option '-arch' for target 'wasm32-wasi' +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake) +find_package(WASISDK REQUIRED) + +include(ExternalProject) +ExternalProject_Add(wasm-app + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-app" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-app -B build + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/samples/file/README.md b/samples/file/README.md new file mode 100644 index 0000000000..a90892ead0 --- /dev/null +++ b/samples/file/README.md @@ -0,0 +1,115 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/file" +--- +# "file" sample introduction + +This sample demonstrates the supported file interaction API of WASI. +This sample can also demonstrate the SGX IPFS (Intel Protected File System), enabling an enclave to seal and unseal data at rest. + +## Preparation + +Please install WASI SDK, download the [wasi-sdk release](https://github.com/WebAssembly/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. +For testing with SGX IPFS, follow the instructions in [the documentation of SGX for WAMR](../../doc/linux_sgx.md#sgx-intel-protected-file-system). + +## Build the sample + +```bash +mkdir build +cd build +cmake .. +make +``` + +The WebAssembly application is the file located at `wasm-app/file.wasm`. + +## Run workload + +Either use [iwasm-sample](../../product-mini/platforms/linux/) for Linux, or [enclave-sample](../../product-mini/platforms/linux-sgx/enclave-sample/) for Intel SGX to run the sample, with the argument to allow the file system interaction with the current folder (`--dir=.`). + +The output with Linux and POSIX is like: + +```bash +Opening a file.. +[Test] File opening passed. +Writing to the file.. +[Test] File writing passed. +Moving the cursor to the start of the file.. +Reading from the file, up to 1000 characters.. +Text read: Hello, world! +[Test] File reading passed. +Determine whether we reach the end of the file.. +Is the end of file? 1 +[Test] End of file detection passed. +Getting the plaintext size.. +The plaintext size is 13. +[Test] Retrieving file offset passed. +Force actual write of all the cached data to the disk.. +[Test] Retrieving file offset passed. +Writing 5 characters at offset 7.. +File current offset: 13 +[Test] Writing at specified offset passed. +Reading 5 characters at offset 7.. +Text read: James +File current offset: 13 +[Test] Reading at specified offset passed. +Allocate more space to the file.. +File current offset: 13 +Moving to the end.. +File current offset: 23 +[Test] Allocation or more space passed. +Extend the file size of 10 bytes using ftruncate.. +File current offset: 23 +Moving to the end.. +File current offset: 33 +[Test] Extension of the file size passed. +Closing from the file.. +[Test] Closing file passed. +Getting the size of the file on disk.. +The file size is 33. +All the tests passed! +``` + +The output with SGX and IPFS is like: + +```bash +Opening a file.. +[Test] File opening passed. +Writing to the file.. +[Test] File writing passed. +Moving the cursor to the start of the file.. +Reading from the file, up to 1000 characters.. +Text read: Hello, world! +[Test] File reading passed. +Determine whether we reach the end of the file.. +Is the end of file? 1 +[Test] End of file detection passed. +Getting the plaintext size.. +The plaintext size is 13. +[Test] Retrieving file offset passed. +Force actual write of all the cached data to the disk.. +[Test] Retrieving file offset passed. +Writing 5 characters at offset 7.. +File current offset: 13 +[Test] Writing at specified offset passed. +Reading 5 characters at offset 7.. +Text read: James +File current offset: 13 +[Test] Reading at specified offset passed. +Allocate more space to the file.. +File current offset: 23 +Moving to the end.. +File current offset: 23 +[Test] Allocation or more space passed. +Extend the file size of 10 bytes using ftruncate.. +File current offset: 23 +Moving to the end.. +File current offset: 33 +[Test] Extension of the file size passed. +Closing from the file.. +[Test] Closing file passed. +Getting the size of the file on disk.. +The file size is 4096. +All the tests passed! +``` + +For SGX IPFS, refer to [SGX Intel Protected File System](../../doc/linux_sgx.md#sgx-intel-protected-file-system) for more details. diff --git a/samples/file/src/CMakeLists.txt b/samples/file/src/CMakeLists.txt new file mode 100644 index 0000000000..11cbd5ac1e --- /dev/null +++ b/samples/file/src/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright (C) 2022 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (iwasm) +else() + project (iwasm C ASM) +endif() + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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_REF_TYPES 1) + +if (NOT MSVC) + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT MSVC) + # linker 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 () +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (iwasm PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (iwasm vmlib -lm -ldl -lpthread) +else () + target_link_libraries (iwasm vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/samples/file/src/main.c b/samples/file/src/main.c new file mode 100644 index 0000000000..3675ee0574 --- /dev/null +++ b/samples/file/src/main.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" + +void +print_usage(void) +{ + fprintf(stdout, "Required arguments:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); + fprintf(stdout, " -d [path of host directory] \n"); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer, error_buf[128]; + const char *wasm_path = NULL, *wasi_dir = NULL; + int opt, main_result = 1; + + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:d:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'd': + wasi_dir = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (wasm_path == NULL || wasi_dir == NULL) { + print_usage(); + return 0; + } + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load(buffer, buf_size, error_buf, sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + wasm_runtime_set_wasi_args_ex(module, &wasi_dir, 1, NULL, 0, NULL, 0, NULL, + 0, 0, 1, 2); + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if (!exec_env) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + if (wasm_application_execute_main(module_inst, 0, NULL)) { + main_result = wasm_runtime_get_wasi_exit_code(module_inst); + } + else { + printf("call wasm function main failed. error: %s\n", + wasm_runtime_get_exception(module_inst)); + goto fail; + } + +fail: + if (exec_env) + wasm_runtime_destroy_exec_env(exec_env); + if (module_inst) + wasm_runtime_deinstantiate(module_inst); + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + wasm_runtime_destroy(); + return main_result; +} diff --git a/samples/file/wasm-app/CMakeLists.txt b/samples/file/wasm-app/CMakeLists.txt new file mode 100644 index 0000000000..a80f0c8f03 --- /dev/null +++ b/samples/file/wasm-app/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (C) 2022 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(file_wasm) + +set (CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) + +add_executable(file main.c) +set_target_properties (file PROPERTIES SUFFIX .wasm) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/file.wasm DESTINATION wasm-app) diff --git a/samples/file/wasm-app/main.c b/samples/file/wasm-app/main.c new file mode 100644 index 0000000000..e794a273f3 --- /dev/null +++ b/samples/file/wasm-app/main.c @@ -0,0 +1,187 @@ +/* + * 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 + +#define PATH_TEST_FOLDER "./test" +#define PATH_TEST_FILE (PATH_TEST_FOLDER "/test.txt") +#define FILE_TEXT "Hello, world!" +#define WORLD_OFFSET 7 +#define NAME_REPLACMENT "James" +#define NAME_REPLACMENT_LEN (sizeof(NAME_REPLACMENT) - 1) +#define ADDITIONAL_SPACE 16 * 1024 + +int +main(int argc, char **argv) +{ + FILE *file; + const char *text = FILE_TEXT; + char buffer[1000]; + int ret; + long long stat_size; + + // Test: Create a folder to store the file, if it does not exist yet + ret = mkdir(PATH_TEST_FOLDER, 777); + assert(ret == 0 || (ret == -1 && errno == EEXIST)); + + // Test: File opening (fopen) + printf("Opening a file..\n"); + file = fopen(PATH_TEST_FILE, "w+"); + if (file == NULL) { + printf("Error! errno: %d\n", errno); + } + assert(file != NULL); + printf("[Test] File opening passed.\n"); + + // Test: Writing to a file (fprintf) + printf("Writing to the file..\n"); + ret = fprintf(file, "%s", text); + assert(ret == strlen(text)); + printf("[Test] File writing passed.\n"); + + // Test: Reading from a file (fseek) + printf("Moving the cursor to the start of the file..\n"); + ret = fseek(file, 0, SEEK_SET); + assert(ret == 0); + + printf("Reading from the file, up to 1000 characters..\n"); + fread(buffer, 1, sizeof(buffer), file); + printf("Text read: %s\n", buffer); + assert(strncmp(text, buffer, strlen(text)) == 0); + printf("[Test] File reading passed.\n"); + + // Test: end of file detection (feof) + printf("Determine whether we reach the end of the file..\n"); + int is_end_of_file = feof(file); + printf("Is the end of file? %d\n", is_end_of_file); + assert(is_end_of_file == 1); + printf("[Test] End of file detection passed.\n"); + + // Test: retrieving file offset (ftell) + printf("Getting the plaintext size..\n"); + long plaintext_size = ftell(file); + printf("The plaintext size is %ld.\n", plaintext_size); + assert(plaintext_size == 13); + printf("[Test] Retrieving file offset passed.\n"); + + // Test: persist changes on disk (fflush) + printf("Force actual write of all the cached data to the disk..\n"); + ret = fflush(file); + assert(ret == 0); + printf("[Test] Retrieving file offset passed.\n"); + + // Test: writing at specified offset (pwrite) + printf("Writing 5 characters at offset %d..\n", WORLD_OFFSET); + ret = pwrite(fileno(file), NAME_REPLACMENT, NAME_REPLACMENT_LEN, + WORLD_OFFSET); + printf("File current offset: %ld\n", ftell(file)); + assert(ret == NAME_REPLACMENT_LEN); + assert(ftell(file) == strlen(FILE_TEXT)); + printf("[Test] Writing at specified offset passed.\n"); + + // Test: reading at specified offset (pread) + printf("Reading %ld characters at offset %d..\n", NAME_REPLACMENT_LEN, + WORLD_OFFSET); + buffer[NAME_REPLACMENT_LEN] = '\0'; + pread(fileno(file), buffer, NAME_REPLACMENT_LEN, WORLD_OFFSET); + printf("Text read: %s\n", buffer); + printf("File current offset: %ld\n", ftell(file)); + assert(strcmp(NAME_REPLACMENT, buffer) == 0); + assert(ftell(file) == strlen(FILE_TEXT)); + printf("[Test] Reading at specified offset passed.\n"); + + // Test: moving at the start of the file (fseek) + printf("Move at the start of the file (fseek)..\n"); + printf("File current offset: %ld\n", ftell(file)); + fseek(file, 0, SEEK_SET); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == 0); + + // Test: moving at the end of the file (fseek) + printf("Move at the end of the file (fseek)..\n"); + printf("File current offset: %ld\n", ftell(file)); + fseek(file, 0, SEEK_END); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == strlen(FILE_TEXT)); + int end_position = ftell(file) / 2; + + // Test: moving at the middle of the file (fseek) + printf("Move at the middle of the file (fseek)..\n"); + printf("File current offset: %ld\n", ftell(file)); + fseek(file, 0, SEEK_SET); + fseek(file, end_position, SEEK_CUR); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == end_position); + + // Test: allocate more space to the file (posix_fallocate) + printf("Allocate more space to the file (posix_fallocate)..\n"); + fseek(file, 0, SEEK_END); + posix_fallocate(fileno(file), ftell(file), ADDITIONAL_SPACE); + printf("File current offset: %ld\n", ftell(file)); + printf("Moving to the end..\n"); + fseek(file, 0, SEEK_END); + printf("File current offset: %ld\n", ftell(file)); + assert(ftell(file) == strlen(text) + ADDITIONAL_SPACE); + printf("[Test] Allocation or more space passed.\n"); + + // Test: allocate more space to the file (ftruncate) + 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"); + fseek(file, 0, SEEK_END); + printf("File current offset: %ld\n", ftell(file)); + 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, from the start) + 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, from the middle) + 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; + stat(PATH_TEST_FILE, &st); + stat_size = st.st_size; + assert(stat_size != 0); + + // Compare with the size from fstat + fstat(fileno(file), &st); + printf("The file size is: %lld (stat), %lld (fstat).\n", stat_size, + st.st_size); + assert(stat_size != 0); + assert(stat_size == st.st_size); + + // Test: closing the file (fclose) + printf("Closing from the file..\n"); + ret = fclose(file); + assert(ret == 0); + printf("[Test] Closing file passed.\n"); + + printf("All the tests passed!\n"); + + return 0; +} diff --git a/samples/gui/README.md b/samples/gui/README.md deleted file mode 100644 index 6caf48bc7d..0000000000 --- a/samples/gui/README.md +++ /dev/null @@ -1,114 +0,0 @@ -"gui" sample introduction -============== -This sample demonstrates that a graphic user interface application in WebAssembly programming with WAMR graphic library(WGL) which is part of WAMR app-framework. - -Compared with the [littlevgl](../littlevgl) sample, WGL compiles LittlevGL source code into the WAMR runtime and defines a set of wrapper API's for exporting to Webassembly application. - - -Below picture shows the WASM application is running on an STM board with an LCD touch panel. - - -![WAMR UI SAMPLE](../../doc/pics/vgl_demo2.png "WAMR UI DEMO") - - When users click the blue button, the WASM application increases the counter, and the latest counter value is displayed on the top banner of the touch panel. The number on top will plus one each second, and the number on the bottom will plus one when clicked. - -# Test on Linux - -Install required SDK and libraries --------------- -- 32 bit SDL(simple directmedia layer) (Note: only necessary when `WAMR_BUILD_TARGET` is set to `X86_32` when building WAMR runtime) -Use apt-get: - `sudo apt-get install libsdl2-dev:i386` -Or download source from www.libsdl.org: -``` -./configure C_FLAGS=-m32 CXX_FLAGS=-m32 LD_FLAGS=-m32 -make -sudo make install -``` -- 64 bit SDL(simple directmedia layer) (Note: only necessary when `WAMR_BUILD_TARGET` is set to `X86_64` when building WAMR runtime) -Use apt-get: - `sudo apt-get install libsdl2-dev` -Or download source from www.libsdl.org: -``` -./configure -make -sudo make install -``` - -Build and Run --------------- - -- Build
-`./build.sh`
- All binaries are in "out", which contains "host_tool", "lvgl_native_ui_app", "ui_app.wasm", "ui_app_lvgl_compatible.wasm" and "wasm_runtime_wgl". -- Run native Linux application
-`./lvgl_native_ui_app`
- -- Run WASM VM Linux applicaton & install WASM APP
- First start wasm_runtime_wgl in server mode.
-`./wasm_runtime_wgl -s`
- Then install wasm APP use host tool.
-`./host_tool -i ui_app -f ui_app.wasm`
-`./host_tool -i ui_app -f ui_app_lvgl_compatible.wasm`
- - - -Test on Zephyr -================================ - -We can use a STM32 NUCLEO_F767ZI board with ILI9341 display and XPT2046 touch screen to run the test. Then use host_tool to remotely install wasm app into STM32. -- Build WASM VM into Zephyr system
- a. clone zephyr source code
-Refer to Zephyr getting started.
-https://docs.zephyrproject.org/latest/getting_started/index.html
-`west init zephyrproject`
-`cd zephyrproject`
-`west update`
- b. copy samples
- `cd zephyr/samples/`
- `cp -a samples/gui/wasm-runtime-wgl wasm-runtime-wgl`
- `cd wasm-runtime-wgl/zephyr_build`
- c. create a link to wamr root dir
- ` ln -s wamr`
- d. build source code
- `mkdir build && cd build`
- `source ../../../../zephyr-env.sh`
- `cmake -GNinja -DBOARD=nucleo_f746zg ..`
- ` ninja flash`
- -- Hardware Connections - -``` -+-------------------+-+------------------+ -|NUCLEO-F767ZI | ILI9341 Display | -+-------------------+-+------------------+ -| CN7.10 | CLK | -+-------------------+-+------------------+ -| CN7.12 | MISO | -+-------------------+-+------------------+ -| CN7.14 | MOSI | -+-------------------+-+------------------+ -| CN11.1 | CS1 for ILI9341 | -+-------------------+-+------------------+ -| CN11.2 | D/C | -+-------------------+-+------------------+ -| CN11.3 | RESET | -+-------------------+-+------------------+ -| CN9.25 | PEN interrupt | -+-------------------+-+------------------+ -| CN9.27 | CS2 for XPT2046 | -+-------------------+-+------------------+ -| CN10.14 | PC UART RX | -+-------------------+-+------------------+ -| CN11.16 | PC UART RX | -+-------------------+-+------------------+ -``` - - -- Install WASM application to Zephyr using host_tool
-First, connect PC and STM32 with UART. Then install to use host_tool.
-`./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app.wasm` - -- Install AOT version WASM application -`wamrc --target=thumbv7 --target-abi=eabi --cpu=cortex-m7 -o ui_app.aot ui_app.wasm` -`./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app.aot` diff --git a/samples/gui/build.sh b/samples/gui/build.sh deleted file mode 100755 index 15cd578c97..0000000000 --- a/samples/gui/build.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash - -PROJECT_DIR=$PWD -WAMR_DIR=${PWD}/../.. -OUT_DIR=${PWD}/out -BUILD_DIR=${PWD}/build -WAMR_RUNTIME_CFG=${PROJECT_DIR}/wamr_config_gui.cmake -LV_CFG_PATH=${PROJECT_DIR}/lv_config - -if [ -z $KW_BUILD ] || [ -z $KW_OUT_FILE ];then - echo "Local Build Env" - cmakewrap="cmake" - makewrap="make" -else - echo "Klocwork Build Env" - cmakewrap="cmake -DCMAKE_BUILD_TYPE=Debug" - makewrap="kwinject -o $KW_OUT_FILE make" -fi - -if [ ! -d $BUILD_DIR ]; then - mkdir ${BUILD_DIR} -fi - -rm -rf ${OUT_DIR} -mkdir ${OUT_DIR} - - -cd ${WAMR_DIR}/core/shared/mem-alloc -if [ ! -d "tlsf" ]; then - git clone https://github.com/mattconte/tlsf -fi - -cd ${WAMR_DIR}/core/deps -if [ ! -d "lvgl" ]; then - git clone https://github.com/littlevgl/lvgl.git --branch v6.0.1 -fi -if [ ! -d "lv_drivers" ]; then - git clone https://github.com/littlevgl/lv_drivers.git -fi - -echo -e "\n\n" -echo "##################### 0. build wamr-sdk gui start#####################" -cd ${WAMR_DIR}/wamr-sdk -./build_sdk.sh -n gui -x ${WAMR_RUNTIME_CFG} -e ${LV_CFG_PATH} -[ $? -eq 0 ] || exit $? - -echo "#####################build wamr-sdk success" - - -echo -e "\n\n" -echo "##################### 1. build native-ui-app start#####################" -cd $BUILD_DIR -mkdir -p lvgl-native-ui-app -cd lvgl-native-ui-app -$cmakewrap ${PROJECT_DIR}/lvgl-native-ui-app -[ $? -eq 0 ] || exit $? -$makewrap -if [ $? != 0 ];then - echo "BUILD_FAIL native-ui-app $?\n" - exit 2 -fi -echo $PWD -cp lvgl_native_ui_app ${OUT_DIR} -echo "#####################build native-ui-app success" -echo -e "\n\n" - - -echo "##################### 2. build wasm runtime start#####################" -cd $BUILD_DIR -mkdir -p wasm-runtime-wgl -cd wasm-runtime-wgl -$cmakewrap ${PROJECT_DIR}/wasm-runtime-wgl/linux-build -DWAMR_BUILD_SDK_PROFILE=gui -[ $? -eq 0 ] || exit $? -$makewrap -[ $? -eq 0 ] || exit $? -cp wasm_runtime_wgl ${OUT_DIR}/ - -echo "##################### build littlevgl wasm runtime end#####################" -echo -e "\n\n" - - -echo "#####################build host-tool" -cd $BUILD_DIR -mkdir -p host-tool -cd host-tool -$cmakewrap ${WAMR_DIR}/test-tools/host-tool -$makewrap -if [ $? != 0 ];then - echo "BUILD_FAIL host tool exit as $?\n" - exit 2 -fi -cp host_tool ${OUT_DIR} -echo "#####################build host-tool success" -echo -e "\n\n" - -echo "##################### 3. build wasm ui app start#####################" -cd ${PROJECT_DIR}/wasm-apps/wgl - -rm -rf build -mkdir build && cd build -$cmakewrap .. -DCMAKE_TOOLCHAIN_FILE=${WAMR_DIR}/wamr-sdk/out/gui/app-sdk/wamr_toolchain.cmake -$makewrap -[ $? -eq 0 ] || exit $? -mv ui_app.wasm ${OUT_DIR}/ - -# $makewrap -# mv ui_app.wasm ${OUT_DIR}/ - -cd ${PROJECT_DIR}/wasm-apps/lvgl-compatible -$makewrap -[ $? -eq 0 ] || exit $? -mv ui_app_lvgl_compatible.wasm ${OUT_DIR}/ -echo "##################### build wasm ui app end#####################" diff --git a/samples/gui/lv_config/lv_conf.h b/samples/gui/lv_config/lv_conf.h deleted file mode 100644 index e0caf37abe..0000000000 --- a/samples/gui/lv_config/lv_conf.h +++ /dev/null @@ -1,498 +0,0 @@ -/** - * @file lv_conf.h - * - */ - -/* - * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER - */ - -#if 1 /*Set it to "1" to enable content*/ - -#ifndef LV_CONF_H -#define LV_CONF_H -/* clang-format off */ - -#include - -/*==================== - Graphical settings - *====================*/ - -/* Maximal horizontal and vertical resolution to support by the library.*/ -#define LV_HOR_RES_MAX (320) -#define LV_VER_RES_MAX (240) - -/* Color depth: - * - 1: 1 byte per pixel - * - 8: RGB233 - * - 16: RGB565 - * - 32: ARGB8888 - */ -#define LV_COLOR_DEPTH 32 - -/* Swap the 2 bytes of RGB565 color. - * Useful if the display has a 8 bit interface (e.g. SPI)*/ -#define LV_COLOR_16_SWAP 0 - -/* 1: Enable screen transparency. - * Useful for OSD or other overlapping GUIs. - * Requires `LV_COLOR_DEPTH = 32` colors and the screen's style should be modified: `style.body.opa = ...`*/ -#define LV_COLOR_SCREEN_TRANSP 0 - -/*Images pixels with this color will not be drawn (with chroma keying)*/ -#define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/ - -/* Enable anti-aliasing (lines, and radiuses will be smoothed) */ -#define LV_ANTIALIAS 1 - -/* Default display refresh period. - * Can be changed in the display driver (`lv_disp_drv_t`).*/ -#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ - -/* Dot Per Inch: used to initialize default sizes. - * E.g. a button with width = LV_DPI / 2 -> half inch wide - * (Not so important, you can adjust it to modify default sizes and spaces)*/ -#define LV_DPI 100 /*[px]*/ - -/* Type of coordinates. Should be `int16_t` (or `int32_t` for extreme cases) */ -typedef int16_t lv_coord_t; - -/*========================= - Memory manager settings - *=========================*/ - -/* LittelvGL's internal memory manager's settings. - * The graphical objects and other related data are stored here. */ - -/* 1: use custom malloc/free, 0: use the built-in `lv_mem_alloc` and `lv_mem_free` */ -#ifndef LV_MEM_CUSTOM -#define LV_MEM_CUSTOM 0 -#endif - -#if LV_MEM_CUSTOM == 0 -/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ -# define LV_MEM_SIZE (128U * 1024U) - -/* Complier prefix for a big array declaration */ -# define LV_MEM_ATTR - -/* Set an address for the memory pool instead of allocating it as an array. - * Can be in external SRAM too. */ -# define LV_MEM_ADR 0 - -/* Automatically defrag. on free. Defrag. means joining the adjacent free cells. */ -# define LV_MEM_AUTO_DEFRAG 1 -#else /*LV_MEM_CUSTOM*/ -# define LV_MEM_CUSTOM_INCLUDE "bh_memory.h" /*Header for the dynamic memory function*/ -# define LV_MEM_CUSTOM_ALLOC bh_malloc /*Wrapper to malloc*/ -# define LV_MEM_CUSTOM_FREE bh_free /*Wrapper to free*/ -#endif /*LV_MEM_CUSTOM*/ - -/* Garbage Collector settings - * Used if lvgl is binded to higher level language and the memory is managed by that language */ -#define LV_ENABLE_GC 0 -#if LV_ENABLE_GC != 0 -# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ -# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/ -# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/ -#endif /* LV_ENABLE_GC */ - -/*======================= - Input device settings - *=======================*/ - -/* Input device default settings. - * Can be changed in the Input device driver (`lv_indev_drv_t`)*/ - -/* Input device read period in milliseconds */ -#define LV_INDEV_DEF_READ_PERIOD 30 - -/* Drag threshold in pixels */ -#define LV_INDEV_DEF_DRAG_LIMIT 10 - -/* Drag throw slow-down in [%]. Greater value -> faster slow-down */ -#define LV_INDEV_DEF_DRAG_THROW 20 - -/* Long press time in milliseconds. - * Time to send `LV_EVENT_LONG_PRESSSED`) */ -#define LV_INDEV_DEF_LONG_PRESS_TIME 400 - -/* Repeated trigger period in long press [ms] - * Time between `LV_EVENT_LONG_PRESSED_REPEAT */ -#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100 - -/*================== - * Feature usage - *==================*/ - -/*1: Enable the Animations */ -#define LV_USE_ANIMATION 1 -#if LV_USE_ANIMATION - -/*Declare the type of the user data of animations (can be e.g. `void *`, `int`, `struct`)*/ -typedef void * lv_anim_user_data_t; - -#endif - -/* 1: Enable shadow drawing*/ -#define LV_USE_SHADOW 1 - -/* 1: Enable object groups (for keyboard/encoder navigation) */ -#define LV_USE_GROUP 1 -#if LV_USE_GROUP -typedef void * lv_group_user_data_t; -#endif /*LV_USE_GROUP*/ - -/* 1: Enable GPU interface*/ -#define LV_USE_GPU 1 - -/* 1: Enable file system (might be required for images */ -#define LV_USE_FILESYSTEM 1 -#if LV_USE_FILESYSTEM -/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/ -typedef void * lv_fs_drv_user_data_t; -#endif - -/*1: Add a `user_data` to drivers and objects*/ -#define LV_USE_USER_DATA 1 - -/*======================== - * Image decoder and cache - *========================*/ - -/* 1: Enable indexed (palette) images */ -#define LV_IMG_CF_INDEXED 1 - -/* 1: Enable alpha indexed images */ -#define LV_IMG_CF_ALPHA 1 - -/* Default image cache size. Image caching keeps the images opened. - * If only the built-in image formats are used there is no real advantage of caching. - * (I.e. no new image decoder is added) - * With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. - * However the opened images might consume additional RAM. - * LV_IMG_CACHE_DEF_SIZE must be >= 1 */ -#define LV_IMG_CACHE_DEF_SIZE 1 - -/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/ -typedef void * lv_img_decoder_user_data_t; - -/*===================== - * Compiler settings - *====================*/ -/* Define a custom attribute to `lv_tick_inc` function */ -#define LV_ATTRIBUTE_TICK_INC - -/* Define a custom attribute to `lv_task_handler` function */ -#define LV_ATTRIBUTE_TASK_HANDLER - -/* With size optimization (-Os) the compiler might not align data to - * 4 or 8 byte boundary. This alignment will be explicitly applied where needed. - * E.g. __attribute__((aligned(4))) */ -#define LV_ATTRIBUTE_MEM_ALIGN - -/* Attribute to mark large constant arrays for example - * font's bitmaps */ -#define LV_ATTRIBUTE_LARGE_CONST - -/*=================== - * HAL settings - *==================*/ - -/* 1: use a custom tick source. - * It removes the need to manually update the tick with `lv_tick_inc`) */ -#define LV_TICK_CUSTOM 1 -#if LV_TICK_CUSTOM == 1 -#define LV_TICK_CUSTOM_INCLUDE "system_header.h" /*Header for the sys time function*/ -#define LV_TICK_CUSTOM_SYS_TIME_EXPR (time_get_ms()) /*Expression evaluating to current systime in ms*/ -#endif /*LV_TICK_CUSTOM*/ - -typedef void * lv_disp_drv_user_data_t; /*Type of user data in the display driver*/ -typedef void * lv_indev_drv_user_data_t; /*Type of user data in the input device driver*/ - -/*================ - * Log settings - *===============*/ - -/*1: Enable the log module*/ -#define LV_USE_LOG 1 -#if LV_USE_LOG -/* How important log should be added: - * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information - * LV_LOG_LEVEL_INFO Log important events - * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem - * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail - * LV_LOG_LEVEL_NONE Do not log anything - */ -# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN - -/* 1: Print the log with 'printf'; - * 0: user need to register a callback with `lv_log_register_print`*/ -# define LV_LOG_PRINTF 1 -#endif /*LV_USE_LOG*/ - -/*================ - * THEME USAGE - *================*/ -#define LV_THEME_LIVE_UPDATE 1 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/ - -#define LV_USE_THEME_TEMPL 1 /*Just for test*/ -#define LV_USE_THEME_DEFAULT 1 /*Built mainly from the built-in styles. Consumes very few RAM*/ -#define LV_USE_THEME_ALIEN 1 /*Dark futuristic theme*/ -#define LV_USE_THEME_NIGHT 1 /*Dark elegant theme*/ -#define LV_USE_THEME_MONO 1 /*Mono color theme for monochrome displays*/ -#define LV_USE_THEME_MATERIAL 1 /*Flat theme with bold colors and light shadows*/ -#define LV_USE_THEME_ZEN 1 /*Peaceful, mainly light theme */ -#define LV_USE_THEME_NEMO 1 /*Water-like theme based on the movie "Finding Nemo"*/ - -/*================== - * FONT USAGE - *===================*/ - -/* The built-in fonts contains the ASCII range and some Symbols with 4 bit-per-pixel. - * The symbols are available via `LV_SYMBOL_...` defines - * More info about fonts: https://docs.littlevgl.com/#Fonts - * To create a new font go to: https://littlevgl.com/ttf-font-to-c-array - */ - -/* Robot fonts with bpp = 4 - * https://fonts.google.com/specimen/Roboto */ -#define LV_FONT_ROBOTO_12 1 -#define LV_FONT_ROBOTO_16 1 -#define LV_FONT_ROBOTO_22 1 -#define LV_FONT_ROBOTO_28 1 - -/*Pixel perfect monospace font - * http://pelulamu.net/unscii/ */ -#define LV_FONT_UNSCII_8 1 - -/* Optionally declare your custom fonts here. - * You can use these fonts as default font too - * and they will be available globally. E.g. - * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \ - * LV_FONT_DECLARE(my_font_2) - */ -#define LV_FONT_CUSTOM_DECLARE - -/*Always set a default font from the built-in fonts*/ -#define LV_FONT_DEFAULT &lv_font_roboto_16 - -/* Enable it if you have fonts with a lot of characters. - * The limit depends on the font size, font face and bpp - * but with > 10,000 characters if you see issues probably you need to enable it.*/ -#define LV_FONT_FMT_TXT_LARGE 1 - -/*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/ -typedef void * lv_font_user_data_t; - -/*================= - * Text settings - *=================*/ - -/* Select a character encoding for strings. - * Your IDE or editor should have the same character encoding - * - LV_TXT_ENC_UTF8 - * - LV_TXT_ENC_ASCII - * */ -#define LV_TXT_ENC LV_TXT_ENC_UTF8 - - /*Can break (wrap) texts on these chars*/ -#define LV_TXT_BREAK_CHARS " ,.;:-_" - -/*=================== - * LV_OBJ SETTINGS - *==================*/ - -/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/ -typedef void * lv_obj_user_data_t; - -/*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/ -#define LV_USE_OBJ_REALIGN 1 - -/* Enable to make the object clickable on a larger area. - * LV_EXT_CLICK_AREA_OFF or 0: Disable this feature - * LV_EXT_CLICK_AREA_TINY: The extra area can be adjusted horizontally and vertically (0..255 px) - * LV_EXT_CLICK_AREA_FULL: The extra area can be adjusted in all 4 directions (-32k..+32k px) - */ -#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_FULL - -/*================== - * LV OBJ X USAGE - *================*/ -/* - * Documentation of the object types: https://docs.littlevgl.com/#Object-types - */ - -/*Arc (dependencies: -)*/ -#define LV_USE_ARC 1 - -/*Bar (dependencies: -)*/ -#define LV_USE_BAR 1 - -/*Button (dependencies: lv_cont*/ -#define LV_USE_BTN 1 -#if LV_USE_BTN != 0 -/*Enable button-state animations - draw a circle on click (dependencies: LV_USE_ANIMATION)*/ -# define LV_BTN_INK_EFFECT 1 -#endif - -/*Button matrix (dependencies: -)*/ -#define LV_USE_BTNM 1 - -/*Calendar (dependencies: -)*/ -#define LV_USE_CALENDAR 1 - -/*Canvas (dependencies: lv_img)*/ -#define LV_USE_CANVAS 1 - -/*Check box (dependencies: lv_btn, lv_label)*/ -#define LV_USE_CB 1 - -/*Chart (dependencies: -)*/ -#define LV_USE_CHART 1 -#if LV_USE_CHART -# define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 20 -#endif - -/*Container (dependencies: -*/ -#define LV_USE_CONT 1 - -/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ -#define LV_USE_DDLIST 1 -#if LV_USE_DDLIST != 0 -/*Open and close default animation time [ms] (0: no animation)*/ -# define LV_DDLIST_DEF_ANIM_TIME 200 -#endif - -/*Gauge (dependencies:lv_bar, lv_lmeter)*/ -#define LV_USE_GAUGE 1 - -/*Image (dependencies: lv_label*/ -#define LV_USE_IMG 1 - -/*Image Button (dependencies: lv_btn*/ -#define LV_USE_IMGBTN 1 -#if LV_USE_IMGBTN -/*1: The imgbtn requires left, mid and right parts and the width can be set freely*/ -# define LV_IMGBTN_TILED 0 -#endif - -/*Keyboard (dependencies: lv_btnm)*/ -#define LV_USE_KB 1 - -/*Label (dependencies: -*/ -#define LV_USE_LABEL 1 -#if LV_USE_LABEL != 0 -/*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_ROLL/ROLL_CIRC' mode*/ -# define LV_LABEL_DEF_SCROLL_SPEED 25 - -/* Waiting period at beginning/end of animation cycle */ -# define LV_LABEL_WAIT_CHAR_COUNT 3 - -/*Enable selecting text of the label */ -# define LV_LABEL_TEXT_SEL 1 - -/*Store extra some info in labels (12 bytes) to speed up drawing of very long texts*/ -# define LV_LABEL_LONG_TXT_HINT 0 -#endif - -/*LED (dependencies: -)*/ -#define LV_USE_LED 1 - -/*Line (dependencies: -*/ -#define LV_USE_LINE 1 - -/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/ -#define LV_USE_LIST 1 -#if LV_USE_LIST != 0 -/*Default animation time of focusing to a list element [ms] (0: no animation) */ -# define LV_LIST_DEF_ANIM_TIME 100 -#endif - -/*Line meter (dependencies: *;)*/ -#define LV_USE_LMETER 1 - -/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/ -#define LV_USE_MBOX 1 - -/*Page (dependencies: lv_cont)*/ -#define LV_USE_PAGE 1 -#if LV_USE_PAGE != 0 -/*Focus default animation time [ms] (0: no animation)*/ -# define LV_PAGE_DEF_ANIM_TIME 400 -#endif - -/*Preload (dependencies: lv_arc, lv_anim)*/ -#define LV_USE_PRELOAD 1 -#if LV_USE_PRELOAD != 0 -# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/ -# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/ -# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC -#endif - -/*Roller (dependencies: lv_ddlist)*/ -#define LV_USE_ROLLER 1 -#if LV_USE_ROLLER != 0 -/*Focus animation time [ms] (0: no animation)*/ -# define LV_ROLLER_DEF_ANIM_TIME 200 - -/*Number of extra "pages" when the roller is infinite*/ -# define LV_ROLLER_INF_PAGES 7 -#endif - -/*Slider (dependencies: lv_bar)*/ -#define LV_USE_SLIDER 1 - -/*Spinbox (dependencies: lv_ta)*/ -#define LV_USE_SPINBOX 1 - -/*Switch (dependencies: lv_slider)*/ -#define LV_USE_SW 1 - -/*Text area (dependencies: lv_label, lv_page)*/ -#define LV_USE_TA 1 -#if LV_USE_TA != 0 -# define LV_TA_DEF_CURSOR_BLINK_TIME 400 /*ms*/ -# define LV_TA_DEF_PWD_SHOW_TIME 1500 /*ms*/ -#endif - -/*Table (dependencies: lv_label)*/ -#define LV_USE_TABLE 1 -#if LV_USE_TABLE -# define LV_TABLE_COL_MAX 12 -#endif - -/*Tab (dependencies: lv_page, lv_btnm)*/ -#define LV_USE_TABVIEW 1 -# if LV_USE_TABVIEW != 0 -/*Time of slide animation [ms] (0: no animation)*/ -# define LV_TABVIEW_DEF_ANIM_TIME 300 -#endif - -/*Tileview (dependencies: lv_page) */ -#define LV_USE_TILEVIEW 1 -#if LV_USE_TILEVIEW -/*Time of slide animation [ms] (0: no animation)*/ -# define LV_TILEVIEW_DEF_ANIM_TIME 300 -#endif - -/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/ -#define LV_USE_WIN 1 - -/*================== - * Non-user section - *==================*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/ -# define _CRT_SECURE_NO_WARNINGS -#endif - -/*--END OF LV_CONF_H--*/ - -/*Be sure every define has a default value*/ -#include "lvgl/src/lv_conf_checker.h" - -#endif /*LV_CONF_H*/ - -#endif /*End of "Content enable"*/ diff --git a/samples/gui/lv_config/lv_drv_conf.h b/samples/gui/lv_config/lv_drv_conf.h deleted file mode 100644 index d216a3e907..0000000000 --- a/samples/gui/lv_config/lv_drv_conf.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * @file lv_drv_conf.h - * - */ - -/* - * COPY THIS FILE AS lv_drv_conf.h - */ - -#if 1 /*Set it to "1" to enable the content*/ - -#ifndef LV_DRV_CONF_H -#define LV_DRV_CONF_H - -#include "lv_conf.h" - -/********************* - * DELAY INTERFACE - *********************/ -#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ -#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ -#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ - -/********************* - * DISPLAY INTERFACE - *********************/ - -/*------------ - * Common - *------------*/ -#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ -#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ -#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ - -/*--------- - * SPI - *---------*/ -#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ -#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ -#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ - -/*------------------ - * Parallel port - *-----------------*/ -#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ -#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ -#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ -#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ -#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ - -/*************************** - * INPUT DEVICE INTERFACE - ***************************/ - -/*---------- - * Common - *----------*/ -#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ -#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ -#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ - -/*--------- - * SPI - *---------*/ -#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ -#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ - -/*--------- - * I2C - *---------*/ -#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ -#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ -#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ -#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ -#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ - - -/********************* - * DISPLAY DRIVERS - *********************/ - -/*------------------- - * Monitor of PC - *-------------------*/ -#ifndef USE_MONITOR -# define USE_MONITOR 1 -#endif - -#if USE_MONITOR -# define MONITOR_HOR_RES LV_HOR_RES_MAX -# define MONITOR_VER_RES LV_VER_RES_MAX - -/* Scale window by this factor (useful when simulating small screens) */ -# define MONITOR_ZOOM 1 - -/* Used to test true double buffering with only address changing. - * Set LV_VDB_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_VDB_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */ -# define MONITOR_DOUBLE_BUFFERED 0 - -/*Eclipse: Visual Studio: */ -# define MONITOR_SDL_INCLUDE_PATH - -/*Different rendering might be used if running in a Virtual machine*/ -# define MONITOR_VIRTUAL_MACHINE 0 - -/*Open two windows to test multi display support*/ -# define MONITOR_DUAL 0 -#endif - -/*----------------------------------- - * Native Windows (including mouse) - *----------------------------------*/ -#ifndef USE_WINDOWS -# define USE_WINDOWS 0 -#endif - -#define USE_WINDOWS 0 -#if USE_WINDOWS -# define WINDOW_HOR_RES 480 -# define WINDOW_VER_RES 320 -#endif - -/*---------------- - * SSD1963 - *--------------*/ -#ifndef USE_SSD1963 -# define USE_SSD1963 0 -#endif - -#if USE_SSD1963 -# define SSD1963_HOR_RES LV_HOR_RES -# define SSD1963_VER_RES LV_VER_RES -# define SSD1963_HT 531 -# define SSD1963_HPS 43 -# define SSD1963_LPS 8 -# define SSD1963_HPW 10 -# define SSD1963_VT 288 -# define SSD1963_VPS 12 -# define SSD1963_FPS 4 -# define SSD1963_VPW 10 -# define SSD1963_HS_NEG 0 /*Negative hsync*/ -# define SSD1963_VS_NEG 0 /*Negative vsync*/ -# define SSD1963_ORI 0 /*0, 90, 180, 270*/ -# define SSD1963_COLOR_DEPTH 16 -#endif - -/*---------------- - * R61581 - *--------------*/ -#ifndef USE_R61581 -# define USE_R61581 0 -#endif - -#if USE_R61581 -# define R61581_HOR_RES LV_HOR_RES -# define R61581_VER_RES LV_VER_RES -# define R61581_HSPL 0 /*HSYNC signal polarity*/ -# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ -# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ -# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ -# define R61581_VSPL 0 /*VSYNC signal polarity*/ -# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ -# define R61581_VFP 8 /*Vertical Front poarch*/ -# define R61581_VBP 8 /*Vertical Back poarch */ -# define R61581_DPL 0 /*DCLK signal polarity*/ -# define R61581_EPL 1 /*ENABLE signal polarity*/ -# define R61581_ORI 0 /*0, 180*/ -# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ -#endif - -/*------------------------------ - * ST7565 (Monochrome, low res.) - *-----------------------------*/ -#ifndef USE_ST7565 -# define USE_ST7565 0 -#endif - -#if USE_ST7565 -/*No settings*/ -#endif /*USE_ST7565*/ - -/*----------------------------------------- - * Linux frame buffer device (/dev/fbx) - *-----------------------------------------*/ -#ifndef USE_FBDEV -# define USE_FBDEV 1 -#endif - -#if USE_FBDEV -# define FBDEV_PATH "/dev/fb0" -#endif - -/********************* - * INPUT DEVICES - *********************/ - -/*-------------- - * XPT2046 - *--------------*/ -#ifndef USE_XPT2046 -# define USE_XPT2046 0 -#endif - -#if USE_XPT2046 -# define XPT2046_HOR_RES 480 -# define XPT2046_VER_RES 320 -# define XPT2046_X_MIN 200 -# define XPT2046_Y_MIN 200 -# define XPT2046_X_MAX 3800 -# define XPT2046_Y_MAX 3800 -# define XPT2046_AVG 4 -# define XPT2046_INV 0 -#endif - -/*----------------- - * FT5406EE8 - *-----------------*/ -#ifndef USE_FT5406EE8 -# define USE_FT5406EE8 0 -#endif - -#if USE_FT5406EE8 -# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ -#endif - -/*--------------- - * AD TOUCH - *--------------*/ -#ifndef USE_AD_TOUCH -# define USE_AD_TOUCH 0 -#endif - -#if USE_AD_TOUCH -/*No settings*/ -#endif - - -/*--------------------------------------- - * Mouse or touchpad on PC (using SDL) - *-------------------------------------*/ -#ifndef USE_MOUSE -# define USE_MOUSE 1 -#endif - -#if USE_MOUSE -/*No settings*/ -#endif - -/*------------------------------------------- - * Mousewheel as encoder on PC (using SDL) - *------------------------------------------*/ -#ifndef USE_MOUSEWHEEL -# define USE_MOUSEWHEEL 1 -#endif - -#if USE_MOUSEWHEEL -/*No settings*/ -#endif - -/*------------------------------------------------- - * Touchscreen as libinput interface (for Linux based systems) - *------------------------------------------------*/ -#ifndef USE_LIBINPUT -# define USE_LIBINPUT 0 -#endif - -#if USE_LIBINPUT -# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ -#endif /*USE_LIBINPUT*/ - -/*------------------------------------------------- - * Mouse or touchpad as evdev interface (for Linux based systems) - *------------------------------------------------*/ -#ifndef USE_EVDEV -# define USE_EVDEV 0 -#endif - -#if USE_EVDEV -# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ -# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ - -# define EVDEV_SCALE 0 /* Scale input, e.g. if touchscreen resolution does not match display resolution */ -# if EVDEV_SCALE -# define EVDEV_SCALE_HOR_RES (4096) /* Horizontal resolution of touchscreen */ -# define EVDEV_SCALE_VER_RES (4096) /* Vertical resolution of touchscreen */ -# endif /*EVDEV_SCALE*/ - -# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ -# if EVDEV_CALIBRATE -# define EVDEV_HOR_MIN 3800 /*If EVDEV_XXX_MIN > EVDEV_XXX_MAX the XXX axis is automatically inverted*/ -# define EVDEV_HOR_MAX 200 -# define EVDEV_VER_MIN 200 -# define EVDEV_VER_MAX 3800 -# endif /*EVDEV_SCALE*/ -#endif /*USE_EVDEV*/ - -/*------------------------------- - * Keyboard of a PC (using SDL) - *------------------------------*/ -#ifndef USE_KEYBOARD -# define USE_KEYBOARD 1 -#endif - -#if USE_KEYBOARD -/*No settings*/ -#endif - -#endif /*LV_DRV_CONF_H*/ - -#endif /*End of "Content enable"*/ diff --git a/samples/gui/lv_config/system_header.h b/samples/gui/lv_config/system_header.h deleted file mode 100644 index 41d9238643..0000000000 --- a/samples/gui/lv_config/system_header.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include - -int time_get_ms(); diff --git a/samples/gui/lvgl-native-ui-app/CMakeLists.txt b/samples/gui/lvgl-native-ui-app/CMakeLists.txt deleted file mode 100644 index 3602e5c784..0000000000 --- a/samples/gui/lvgl-native-ui-app/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required (VERSION 2.8.2) -message ("lvgl_native_ui_app...") -project (lvgl_native_ui_app) - -################################################################# - -# Currently build as 64-bit by default. Set to "NO" to build 32-bit binaries. -set (BUILD_AS_64BIT_SUPPORT "YES") - -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - if (${BUILD_AS_64BIT_SUPPORT} STREQUAL "YES") - # Add -fPIC flag if build as 64-bit - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") - else () - add_definitions (-m32) - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") - endif () -endif () - -set(WAMR_DEPS_DIR ../../../core/deps) -set(LVGL_SOURCE_DIR ${WAMR_DEPS_DIR}/lvgl) -set(LVGL_DRIVER_DIR ${WAMR_DEPS_DIR}/lv_drivers) - -################################# - -add_definitions(-DLV_CONF_INCLUDE_SIMPLE) - -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}) -INCLUDE_DIRECTORIES(${WAMR_DEPS_DIR}) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../lv_config) - - -file(GLOB_RECURSE INCLUDES "${LVGL_DRIVER_DIR}/*.h" "${LVGL_SOURCE_DIR}/*.h" "./*.h" ) -file(GLOB_RECURSE SOURCES "${LVGL_DRIVER_DIR}/*.c" "${LVGL_SOURCE_DIR}/*.c" ) - -add_executable(lvgl_native_ui_app main.c get_time.c ${SOURCES} ${INCLUDES}) -target_link_libraries(lvgl_native_ui_app PRIVATE SDL2 ) - diff --git a/samples/gui/lvgl-native-ui-app/LICENCE.txt b/samples/gui/lvgl-native-ui-app/LICENCE.txt deleted file mode 100644 index beaef1d26e..0000000000 --- a/samples/gui/lvgl-native-ui-app/LICENCE.txt +++ /dev/null @@ -1,8 +0,0 @@ -MIT licence -Copyright (c) 2016 Gábor Kiss-Vámosi - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/gui/lvgl-native-ui-app/get_time.c b/samples/gui/lvgl-native-ui-app/get_time.c deleted file mode 100644 index 756191a699..0000000000 --- a/samples/gui/lvgl-native-ui-app/get_time.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include "system_header.h" - -int time_get_ms() -{ - static struct timeval tv; - gettimeofday(&tv, NULL); - long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; - - return (int) time_in_mill; -} diff --git a/samples/gui/lvgl-native-ui-app/main.c b/samples/gui/lvgl-native-ui-app/main.c deleted file mode 100644 index 54600f332d..0000000000 --- a/samples/gui/lvgl-native-ui-app/main.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/** - * @file main - * - */ - -/********************* - * INCLUDES - *********************/ -#define _DEFAULT_SOURCE /* needed for usleep() */ -#include -#include -#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" issue*/ -#include -#include "lvgl/lvgl.h" -#include "lv_drivers/display/monitor.h" -#include "lv_drivers/indev/mouse.h" -#include "lv_drivers/indev/mousewheel.h" -#include "lv_drivers/indev/keyboard.h" - - -/********************* - * DEFINES - *********************/ - -/*On OSX SDL needs different handling*/ -#if defined(__APPLE__) && defined(TARGET_OS_MAC) -# if __APPLE__ && TARGET_OS_MAC -#define SDL_APPLE -# endif -#endif - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void hal_init(void); -static void btn_event_cb(lv_obj_t * btn, lv_event_t event); - -/********************** - * STATIC VARIABLES - **********************/ -uint32_t count = 0; -char count_str[11] = { 0 }; -lv_obj_t *hello_world_label; -lv_obj_t *count_label; -lv_obj_t * btn1; -lv_obj_t * label_count1; -int label_count1_value = 0; -char label_count1_str[11] = { 0 }; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -int main(int argc, char ** argv) -{ - (void) argc; /*Unused*/ - (void) argv; /*Unused*/ - - /*Initialize LittlevGL*/ - lv_init(); - - /*Initialize the HAL (display, input devices, tick) for LittlevGL*/ - hal_init(); - - hello_world_label = lv_label_create(lv_disp_get_scr_act(NULL), NULL); - lv_label_set_text(hello_world_label, "Hello world!"); - lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); - - count_label = lv_label_create(lv_disp_get_scr_act(NULL), NULL); - lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); - btn1 = lv_btn_create(lv_disp_get_scr_act(NULL), NULL); /*Create a button on the currently loaded screen*/ - lv_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ - lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 20); /*Align below the label*/ - - /*Create a label on the button*/ - lv_obj_t * btn_label = lv_label_create(btn1, NULL); - lv_label_set_text(btn_label, "Click ++"); - - label_count1 = lv_label_create(lv_disp_get_scr_act(NULL), NULL); - lv_label_set_text(label_count1, "0"); - lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - while(1) { - /* Periodically call the lv_task handler. - * It could be done in a timer interrupt or an OS task too.*/ - if ((count % 100) == 0) { - snprintf(count_str, sizeof(count_str), "%d", count/ 100); - lv_label_set_text(count_label, count_str); - } - lv_task_handler(); - ++count; - usleep(10 * 1000); /*Just to let the system breath*/ - } - return 0; -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -/** - * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library - */ -static void hal_init(void) -{ - /* Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/ - monitor_init(); - - /*Create a display buffer*/ - static lv_disp_buf_t disp_buf1; - static lv_color_t buf1_1[320*10]; - lv_disp_buf_init(&disp_buf1, buf1_1, NULL, 320*10); - - /*Create a display*/ - lv_disp_drv_t disp_drv; - lv_disp_drv_init(&disp_drv); /*Basic initialization*/ - disp_drv.buffer = &disp_buf1; - disp_drv.flush_cb = monitor_flush; /*Used when `LV_VDB_SIZE != 0` in lv_conf.h (buffered drawing)*/ - // disp_drv.hor_res = 200; - // disp_drv.ver_res = 100; - lv_disp_drv_register(&disp_drv); - - /* Add the mouse as input device - * Use the 'mouse' driver which reads the PC's mouse*/ - mouse_init(); - lv_indev_drv_t indev_drv; - lv_indev_drv_init(&indev_drv); /*Basic initialization*/ - indev_drv.type = LV_INDEV_TYPE_POINTER; - indev_drv.read_cb = mouse_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ - lv_indev_drv_register(&indev_drv); -} - -static void btn_event_cb(lv_obj_t * btn, lv_event_t event) -{ - if(event == LV_EVENT_RELEASED) { - label_count1_value++; - snprintf(label_count1_str, sizeof(label_count1_str), - "%d", label_count1_value); - lv_label_set_text(label_count1, label_count1_str); - } -} diff --git a/samples/gui/wamr_config_gui.cmake b/samples/gui/wamr_config_gui.cmake deleted file mode 100644 index 3b33d33b29..0000000000 --- a/samples/gui/wamr_config_gui.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set (WAMR_BUILD_PLATFORM "linux") -set (WAMR_BUILD_TARGET "X86_64") -set (WAMR_BUILD_INTERP 1) -set (WAMR_BUILD_AOT 1) -set (WAMR_BUILD_JIT 0) -set (WAMR_BUILD_LIBC_BUILTIN 1) -set (WAMR_BUILD_LIBC_WASI 0) -set (WAMR_BUILD_APP_FRAMEWORK 1) -set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_ALL) diff --git a/samples/gui/wasm-apps/lvgl-compatible/Makefile b/samples/gui/wasm-apps/lvgl-compatible/Makefile deleted file mode 100644 index 285e1d1f13..0000000000 --- a/samples/gui/wasm-apps/lvgl-compatible/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -CC = /opt/wasi-sdk/bin/clang -APP_DIR = ${shell pwd} -IWASM_DIR = $(APP_DIR)/../../../../core/iwasm -SDK_DIR = $(APP_DIR)/../../../../wamr-sdk/out/gui/app-sdk -APP_FRAMEWORK_DIR = $(APP_DIR)/../../../../wamr-sdk/out/gui/app-sdk/wamr-app-framework -DEPS_DIR = $(APP_DIR)/../../../../core/deps - -CFLAGS += -O3 \ - -Wno-int-conversion \ - -I$(APP_DIR)/src \ - -I$(APP_FRAMEWORK_DIR)/include \ - -I${DEPS_DIR} - -SRCS += $(APP_DIR)/src/main.c - -all: - @$(CC) $(CFLAGS) $(SRCS) \ - --target=wasm32 -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ - --sysroot=$(SDK_DIR)/libc-builtin-sysroot \ - -L$(APP_FRAMEWORK_DIR)/lib -lapp_framework \ - -Wl,--allow-undefined-file=$(SDK_DIR)/libc-builtin-sysroot/share/defined-symbols.txt \ - -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ - -Wl,--export=on_init -Wl,--export=on_timer_callback \ - -Wl,--export=on_widget_event \ - -Wl,--export=__heap_base,--export=__data_end \ - -o ui_app_lvgl_compatible.wasm diff --git a/samples/gui/wasm-apps/lvgl-compatible/src/main.c b/samples/gui/wasm-apps/lvgl-compatible/src/main.c deleted file mode 100644 index 45c16f2234..0000000000 --- a/samples/gui/wasm-apps/lvgl-compatible/src/main.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include "wasm_app.h" -#include "wa-inc/lvgl.h" -#include "wa-inc/timer_wasm_app.h" - -extern char g_widget_text[]; - -static void btn_event_cb(lv_obj_t *btn, lv_event_t event); - -uint32_t count = 0; -char count_str[11] = { 0 }; -lv_obj_t *hello_world_label; -lv_obj_t *count_label; -lv_obj_t *btn1; -lv_obj_t *label_count1; -int label_count1_value = 100; -char label_count1_str[11] = { 0 }; - -void timer1_update(user_timer_t timer1) -{ - if ((count % 100) == 0) { - snprintf(count_str, sizeof(count_str), "%d", count / 100); - lv_label_set_text(count_label, count_str); - } - ++count; -} - -void on_init() -{ - char *text; - - hello_world_label = lv_label_create(NULL, NULL); - lv_label_set_text(hello_world_label, "Hello world!"); - text = lv_label_get_text(hello_world_label); - printf("Label text %lu %s \n", strlen(text), text); - lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); - - count_label = lv_label_create(NULL, NULL); - lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); - - btn1 = lv_btn_create(NULL, NULL); /*Create a button on the currently loaded screen*/ - lv_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ - lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 0); /*Align below the label*/ - - /*Create a label on the button*/ - lv_obj_t *btn_label = lv_label_create(btn1, NULL); - lv_label_set_text(btn_label, "Click --"); - - label_count1 = lv_label_create(NULL, NULL); - lv_label_set_text(label_count1, "100"); - lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - - /* set up a timer */ - user_timer_t timer; - timer = api_timer_create(10, true, false, timer1_update); - if (timer) - api_timer_restart(timer, 10); - else - printf("Fail to create timer.\n"); -} - -static void btn_event_cb(lv_obj_t *btn, lv_event_t event) -{ - if(event == LV_EVENT_RELEASED) { - label_count1_value--; - snprintf(label_count1_str, sizeof(label_count1_str), - "%d", label_count1_value); - lv_label_set_text(label_count1, label_count1_str); - if (label_count1_value == 0) - label_count1_value = 100; - } -} diff --git a/samples/gui/wasm-apps/wgl/CMakeLists.txt b/samples/gui/wasm-apps/wgl/CMakeLists.txt deleted file mode 100644 index 15ef74f714..0000000000 --- a/samples/gui/wasm-apps/wgl/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -cmake_minimum_required(VERSION 2.8) - -project(wgl) - -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../) - -include_directories( - ${WAMR_ROOT_DIR}/wamr-sdk/out/gui/app-sdk/wamr-app-framework/include - ${WAMR_ROOT_DIR}/core/deps -) - -set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},-L${WAMR_ROOT_DIR}/wamr-sdk/out/gui/app-sdk/wamr-app-framework/lib") -set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},--export=on_init,--export=on_timer_callback,--export=on_widget_event,--export=__heap_base,--export=__data_end") -set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-unused-command-line-argument") - -add_executable(ui_app.wasm - ${CMAKE_CURRENT_LIST_DIR}/src/main.c -) - -target_link_libraries(ui_app.wasm app_framework) diff --git a/samples/gui/wasm-apps/wgl/Makefile b/samples/gui/wasm-apps/wgl/Makefile deleted file mode 100644 index 3e49913e06..0000000000 --- a/samples/gui/wasm-apps/wgl/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -CC = /opt/wasi-sdk/bin/clang -APP_DIR = ${shell pwd} -IWASM_DIR = ../../../../core/iwasm -APP_FRAMEWORK_DIR = ../../../../core/app-framework -DEPS_DIR = ../../../../core/deps - -CFLAGS += -O3 \ - -Wno-int-conversion \ - -I$(APP_DIR)/src \ - -I$(APP_FRAMEWORK_DIR)/base/app \ - -I$(APP_FRAMEWORK_DIR)/app-native-shared \ - -I$(APP_FRAMEWORK_DIR)/sensor/app \ - -I$(APP_FRAMEWORK_DIR)/wgl/app \ - -I$(APP_FRAMEWORK_DIR)/connection/app \ - -I${DEPS_DIR} - -SRCS += $(APP_DIR)/src/main.c - -# For app size consideration, not all but necessary app libs are included -SRCS += $(APP_FRAMEWORK_DIR)/base/app/timer.c -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,--no-threads,--strip-all,--no-entry -nostdlib \ - -Wl,--export=on_init -Wl,--export=on_timer_callback \ - -Wl,--export=on_widget_event \ - -Wl,--export=__heap_base,--export=__data_end \ - -o ui_app.wasm diff --git a/samples/gui/wasm-apps/wgl/src/main.c b/samples/gui/wasm-apps/wgl/src/main.c deleted file mode 100644 index 33520b2032..0000000000 --- a/samples/gui/wasm-apps/wgl/src/main.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include "wasm_app.h" -#include "wa-inc/wgl.h" -#include "wa-inc/timer_wasm_app.h" - -static void btn_event_cb(wgl_obj_t btn, wgl_event_t event); - -uint32_t count = 0; -char count_str[11] = { 0 }; -wgl_obj_t hello_world_label; -wgl_obj_t count_label; -wgl_obj_t btn1; -wgl_obj_t label_count1; -int label_count1_value = 0; -char label_count1_str[11] = { 0 }; - -void timer1_update(user_timer_t timer1) -{ - if ((count % 100) == 0) { - snprintf(count_str, sizeof(count_str), "%d", count / 100); - wgl_label_set_text(count_label, count_str); - } - ++count; -} - -void on_init() -{ - hello_world_label = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); - wgl_label_set_text(hello_world_label, "Hello world!"); - wgl_obj_align(hello_world_label, (wgl_obj_t)NULL, WGL_ALIGN_IN_TOP_LEFT, 0, 0); - - count_label = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); - wgl_obj_align(count_label, (wgl_obj_t)NULL, WGL_ALIGN_IN_TOP_MID, 0, 0); - - btn1 = wgl_btn_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); /*Create a button on the currently loaded screen*/ - wgl_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ - wgl_obj_align(btn1, (wgl_obj_t)NULL, WGL_ALIGN_CENTER, 0, 0); /*Align below the label*/ - - /*Create a label on the button*/ - wgl_obj_t btn_label = wgl_label_create(btn1, (wgl_obj_t)NULL); - wgl_label_set_text(btn_label, "Click ++"); - - label_count1 = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); - wgl_label_set_text(label_count1, "0"); - wgl_obj_align(label_count1, (wgl_obj_t)NULL, WGL_ALIGN_IN_BOTTOM_MID, 0, 0); - - /* set up a timer */ - user_timer_t timer; - timer = api_timer_create(10, true, false, timer1_update); - if (timer) - api_timer_restart(timer, 10); - else - printf("Fail to create timer.\n"); -} - -static void btn_event_cb(wgl_obj_t btn, wgl_event_t event) -{ - if(event == WGL_EVENT_RELEASED) { - label_count1_value++; - snprintf(label_count1_str, sizeof(label_count1_str), - "%d", label_count1_value); - wgl_label_set_text(label_count1, label_count1_str); - - //wgl_cont_set_fit4(btn, WGL_FIT_FLOOD, WGL_FIT_FLOOD, WGL_FIT_FLOOD, WGL_FIT_FLOOD); - //wgl_obj_clean(btn); - } -} diff --git a/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt b/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt deleted file mode 100644 index c1aa7b3c74..0000000000 --- a/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required (VERSION 2.8) - -project (wasm_runtime_wgl) - -set (WAMR_BUILD_PLATFORM "linux") - -# Reset default linker flags -set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") -set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") - -################ wamr runtime settings ################ - -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../..) -set (DEPS_DIR ${WAMR_ROOT_DIR}/core/deps) - - -add_definitions(-DLV_CONF_INCLUDE_SIMPLE) - -## use library and headers in the SDK -link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/gui/runtime-sdk/lib) -include_directories( - ${WAMR_ROOT_DIR}/wamr-sdk/out/gui/runtime-sdk/include - ${WAMR_ROOT_DIR}/wamr-sdk/out/gui/runtime-sdk/include/bi-inc/deps -) - -################ application related ################ - -set (LV_DRIVERS_DIR ${WAMR_ROOT_DIR}/core/deps/lv_drivers) -file (GLOB_RECURSE LV_DRIVERS_SOURCES "${LV_DRIVERS_DIR}/*.c") - -set (PROJECT_SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/../src/platform/${WAMR_BUILD_PLATFORM}) -include_directories( - ${PROJECT_SRC_DIR} - ${DEPS_DIR} - ${DEPS_DIR}/lvgl - ${DEPS_DIR}/lvgl/src -) - -set (SOURCES - ${PROJECT_SRC_DIR}/main.c - ${PROJECT_SRC_DIR}/iwasm_main.c - ${PROJECT_SRC_DIR}/../../ext_lib_export.c - ${LV_DRIVERS_SOURCES} - ) - -add_executable (wasm_runtime_wgl ${SOURCES}) - -target_link_libraries (wasm_runtime_wgl vmlib -lm -ldl -lpthread -lSDL2) -#target_link_libraries(wasm_runtime_wgl PRIVATE SDL2 ) - diff --git a/samples/gui/wasm-runtime-wgl/src/ext_lib_export.c b/samples/gui/wasm-runtime-wgl/src/ext_lib_export.c deleted file mode 100644 index 4f696f0ad9..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/ext_lib_export.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "lib_export.h" -#include "sensor_api.h" -#include "connection_api.h" -#include "gui_api.h" - -static NativeSymbol extended_native_symbol_defs[] = { -#include "runtime_sensor.inl" -#include "connection.inl" -#include "wamr_gui.inl" -}; - -#include "ext_lib_export.h" diff --git a/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c b/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c deleted file mode 100644 index b3b324d5d7..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c +++ /dev/null @@ -1,519 +0,0 @@ - -#ifndef CONNECTION_UART -#include -#include -#include -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "runtime_lib.h" -#include "runtime_timer.h" -#include "native_interface.h" -#include "app_manager_export.h" -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "runtime_sensor.h" -#include "bi-inc/attr_container.h" -#include "module_wasm_app.h" -#include "wasm_export.h" -#include "wgl.h" - -#include "lv_drivers/display/monitor.h" -#include "lv_drivers/indev/mouse.h" - -#define MAX 2048 - -#ifndef CONNECTION_UART -#define SA struct sockaddr -static char *host_address = "127.0.0.1"; -static int port = 8888; -#else -static char *uart_device = "/dev/ttyS2"; -static int baudrate = B115200; -#endif - -extern void init_sensor_framework(); -extern void exit_sensor_framework(); -extern void exit_connection_framework(); -extern int aee_host_msg_callback(void *msg, uint16_t msg_len); -extern bool init_connection_framework(); - -#ifndef CONNECTION_UART -int listenfd = -1; -int sockfd = -1; -static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; -#else -int uartfd = -1; -#endif - -#ifndef CONNECTION_UART -static bool server_mode = false; - -// Function designed for chat between client and server. -void* func(void* arg) -{ - char buff[MAX]; - int n; - struct sockaddr_in servaddr; - - while (1) { - if (sockfd != -1) - close(sockfd); - // socket create and verification - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd == -1) { - printf("socket creation failed...\n"); - return NULL; - } else - printf("Socket successfully created..\n"); - bzero(&servaddr, sizeof(servaddr)); - // assign IP, PORT - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(host_address); - servaddr.sin_port = htons(port); - - // connect the client socket to server socket - if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) != 0) { - printf("connection with the server failed...\n"); - sleep(10); - continue; - } else { - printf("connected to the server..\n"); - } - - // infinite loop for chat - for (;;) { - bzero(buff, MAX); - - // read the message from client and copy it in buffer - n = read(sockfd, buff, sizeof(buff)); - // print buffer which contains the client contents - //fprintf(stderr, "recieved %d bytes from host: %s", n, buff); - - // socket disconnected - if (n <= 0) - break; - - aee_host_msg_callback(buff, n); - } - } - - // After chatting close the socket - close(sockfd); -} - -static bool host_init() -{ - return true; -} - -int host_send(void * ctx, const char *buf, int size) -{ - int ret; - - if (pthread_mutex_trylock(&sock_lock) == 0) { - if (sockfd == -1) { - pthread_mutex_unlock(&sock_lock); - return 0; - } - - ret = write(sockfd, buf, size); - - pthread_mutex_unlock(&sock_lock); - return ret; - } - - return -1; -} - -void host_destroy() -{ - if (server_mode) - close(listenfd); - - pthread_mutex_lock(&sock_lock); - close(sockfd); - pthread_mutex_unlock(&sock_lock); -} - -host_interface interface = { - .init = host_init, - .send = host_send, - .destroy = host_destroy - }; - -void* func_server_mode(void* arg) -{ - int clilent; - struct sockaddr_in serv_addr, cli_addr; - int n; - char buff[MAX]; - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, 0); - - /* First call to socket() function */ - listenfd = socket(AF_INET, SOCK_STREAM, 0); - - if (listenfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - /* Initialize socket structure */ - bzero((char *) &serv_addr, sizeof(serv_addr)); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - /* Now bind the host address using bind() call.*/ - if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("ERROR on binding"); - exit(1); - } - - listen(listenfd, 5); - clilent = sizeof(cli_addr); - - while (1) { - pthread_mutex_lock(&sock_lock); - - sockfd = accept(listenfd, (struct sockaddr *) &cli_addr, &clilent); - - pthread_mutex_unlock(&sock_lock); - - if (sockfd < 0) { - perror("ERROR on accept"); - exit(1); - } - - printf("connection established!\n"); - - for (;;) { - bzero(buff, MAX); - - // read the message from client and copy it in buffer - n = read(sockfd, buff, sizeof(buff)); - - // socket disconnected - if (n <= 0) { - pthread_mutex_lock(&sock_lock); - close(sockfd); - sockfd = -1; - pthread_mutex_unlock(&sock_lock); - - sleep(2); - break; - } - - aee_host_msg_callback(buff, n); - } - } -} - -#else -static int parse_baudrate(int baud) -{ - switch (baud) { - case 9600: - return B9600; - case 19200: - return B19200; - case 38400: - return B38400; - case 57600: - return B57600; - case 115200: - return B115200; - case 230400: - return B230400; - case 460800: - return B460800; - case 500000: - return B500000; - case 576000: - return B576000; - case 921600: - return B921600; - case 1000000: - return B1000000; - case 1152000: - return B1152000; - case 1500000: - return B1500000; - case 2000000: - return B2000000; - case 2500000: - return B2500000; - case 3000000: - return B3000000; - case 3500000: - return B3500000; - case 4000000: - return B4000000; - default: - return -1; - } -} -static bool uart_init(const char *device, int baudrate, int *fd) -{ - int uart_fd; - struct termios uart_term; - - uart_fd = open(device, O_RDWR | O_NOCTTY); - - if (uart_fd <= 0) - return false; - - memset(&uart_term, 0, sizeof(uart_term)); - uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; - uart_term.c_iflag = IGNPAR; - uart_term.c_oflag = 0; - - /* set noncanonical mode */ - uart_term.c_lflag = 0; - uart_term.c_cc[VTIME] = 30; - uart_term.c_cc[VMIN] = 1; - tcflush(uart_fd, TCIFLUSH); - - if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { - close(uart_fd); - return false; - } - - *fd = uart_fd; - - return true; -} - -static void *func_uart_mode(void *arg) -{ - int n; - char buff[MAX]; - - if (!uart_init(uart_device, baudrate, &uartfd)) { - printf("open uart fail! %s\n", uart_device); - return NULL; - } - - for (;;) { - bzero(buff, MAX); - - n = read(uartfd, buff, sizeof(buff)); - - if (n <= 0) { - close(uartfd); - uartfd = -1; - break; - } - - aee_host_msg_callback(buff, n); - } - - return NULL; -} - -static int uart_send(void * ctx, const char *buf, int size) -{ - int ret; - - ret = write(uartfd, buf, size); - - return ret; -} - -static void uart_destroy() -{ - close(uartfd); -} - -static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; - -#endif - -static char global_heap_buf[270 * 1024] = { 0 }; - -static void showUsage() -{ -#ifndef CONNECTION_UART - printf("Usage:\n"); - printf("\nWork as TCP server mode:\n"); - printf("\tvgl_wasm_runtime -s|--server_mode -p|--port \n"); - printf("where\n"); - printf("\t represents the port that would be listened on and the default is 8888\n"); - printf("\nWork as TCP client mode:\n"); - printf("\tvgl_wasm_runtime -a|--host_address -p|--port \n"); - printf("where\n"); - printf("\t represents the network address of host and the default is 127.0.0.1\n"); - printf("\t represents the listen port of host and the default is 8888\n"); -#else - printf("Usage:\n"); - printf("\tvgl_wasm_runtime -u -b \n\n"); - printf("where\n"); - printf("\t represents the UART device name and the default is /dev/ttyS2\n"); - printf("\t represents the UART device baudrate and the default is 115200\n"); -#endif -} - -static bool parse_args(int argc, char *argv[]) -{ - int c; - - while (1) { - int optIndex = 0; - static struct option longOpts[] = { -#ifndef CONNECTION_UART - { "server_mode", no_argument, NULL, 's' }, - { "host_address", required_argument, NULL, 'a' }, - { "port", required_argument, NULL, 'p' }, -#else - { "uart", required_argument, NULL, 'u' }, - { "baudrate", required_argument, NULL, 'b' }, -#endif - { "help", required_argument, NULL, 'h' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "sa:p:u:b:h", longOpts, &optIndex); - if (c == -1) - break; - - switch (c) { -#ifndef CONNECTION_UART - case 's': - server_mode = true; - break; - case 'a': - host_address = optarg; - printf("host address: %s\n", host_address); - break; - case 'p': - port = atoi(optarg); - printf("port: %d\n", port); - break; -#else - case 'u': - uart_device = optarg; - printf("uart device: %s\n", uart_device); - break; - case 'b': - baudrate = parse_baudrate(atoi(optarg)); - printf("uart baudrate: %s\n", optarg); - break; -#endif - case 'h': - showUsage(); - return false; - default: - showUsage(); - return false; - } - } - - return true; -} - -/** - * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library - */ -static void hal_init(void) -{ - /* Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/ - monitor_init(); - - /*Create a display buffer*/ - static lv_disp_buf_t disp_buf1; - static lv_color_t buf1_1[480*10]; - lv_disp_buf_init(&disp_buf1, buf1_1, NULL, 480*10); - - /*Create a display*/ - lv_disp_drv_t disp_drv; - lv_disp_drv_init(&disp_drv); /*Basic initialization*/ - disp_drv.buffer = &disp_buf1; - disp_drv.flush_cb = monitor_flush; - // disp_drv.hor_res = 200; - // disp_drv.ver_res = 100; - lv_disp_drv_register(&disp_drv); - - /* Add the mouse as input device - * Use the 'mouse' driver which reads the PC's mouse*/ - mouse_init(); - lv_indev_drv_t indev_drv; - lv_indev_drv_init(&indev_drv); /*Basic initialization*/ - indev_drv.type = LV_INDEV_TYPE_POINTER; - indev_drv.read_cb = mouse_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ - lv_indev_drv_register(&indev_drv); -} - -// Driver function -int iwasm_main(int argc, char *argv[]) -{ - korp_thread tid; - - if (!parse_args(argc, argv)) - return -1; - - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - printf("Init global heap failed.\n"); - return -1; - } - - if (vm_thread_sys_init() != 0) { - goto fail1; - } - - if (!init_connection_framework()) { - vm_thread_sys_destroy(); - goto fail1; - } - - wgl_init(); - - hal_init(); - - init_sensor_framework(); - - // timer manager - init_wasm_timer(); - -#ifndef CONNECTION_UART - if (server_mode) - vm_thread_create(&tid, func_server_mode, NULL, - BH_APPLET_PRESERVED_STACK_SIZE); - else - vm_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); -#else - vm_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); -#endif - - app_manager_startup(&interface); - - exit_wasm_timer(); - exit_sensor_framework(); - wgl_exit(); - exit_connection_framework(); - -fail1: - bh_memory_destroy(); - - return -1; -} diff --git a/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h b/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h deleted file mode 100644 index d216a3e907..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * @file lv_drv_conf.h - * - */ - -/* - * COPY THIS FILE AS lv_drv_conf.h - */ - -#if 1 /*Set it to "1" to enable the content*/ - -#ifndef LV_DRV_CONF_H -#define LV_DRV_CONF_H - -#include "lv_conf.h" - -/********************* - * DELAY INTERFACE - *********************/ -#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ -#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ -#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ - -/********************* - * DISPLAY INTERFACE - *********************/ - -/*------------ - * Common - *------------*/ -#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ -#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ -#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ - -/*--------- - * SPI - *---------*/ -#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ -#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ -#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ - -/*------------------ - * Parallel port - *-----------------*/ -#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ -#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ -#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ -#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ -#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ - -/*************************** - * INPUT DEVICE INTERFACE - ***************************/ - -/*---------- - * Common - *----------*/ -#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ -#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ -#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ - -/*--------- - * SPI - *---------*/ -#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ -#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ - -/*--------- - * I2C - *---------*/ -#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ -#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ -#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ -#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ -#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ - - -/********************* - * DISPLAY DRIVERS - *********************/ - -/*------------------- - * Monitor of PC - *-------------------*/ -#ifndef USE_MONITOR -# define USE_MONITOR 1 -#endif - -#if USE_MONITOR -# define MONITOR_HOR_RES LV_HOR_RES_MAX -# define MONITOR_VER_RES LV_VER_RES_MAX - -/* Scale window by this factor (useful when simulating small screens) */ -# define MONITOR_ZOOM 1 - -/* Used to test true double buffering with only address changing. - * Set LV_VDB_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_VDB_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */ -# define MONITOR_DOUBLE_BUFFERED 0 - -/*Eclipse: Visual Studio: */ -# define MONITOR_SDL_INCLUDE_PATH - -/*Different rendering might be used if running in a Virtual machine*/ -# define MONITOR_VIRTUAL_MACHINE 0 - -/*Open two windows to test multi display support*/ -# define MONITOR_DUAL 0 -#endif - -/*----------------------------------- - * Native Windows (including mouse) - *----------------------------------*/ -#ifndef USE_WINDOWS -# define USE_WINDOWS 0 -#endif - -#define USE_WINDOWS 0 -#if USE_WINDOWS -# define WINDOW_HOR_RES 480 -# define WINDOW_VER_RES 320 -#endif - -/*---------------- - * SSD1963 - *--------------*/ -#ifndef USE_SSD1963 -# define USE_SSD1963 0 -#endif - -#if USE_SSD1963 -# define SSD1963_HOR_RES LV_HOR_RES -# define SSD1963_VER_RES LV_VER_RES -# define SSD1963_HT 531 -# define SSD1963_HPS 43 -# define SSD1963_LPS 8 -# define SSD1963_HPW 10 -# define SSD1963_VT 288 -# define SSD1963_VPS 12 -# define SSD1963_FPS 4 -# define SSD1963_VPW 10 -# define SSD1963_HS_NEG 0 /*Negative hsync*/ -# define SSD1963_VS_NEG 0 /*Negative vsync*/ -# define SSD1963_ORI 0 /*0, 90, 180, 270*/ -# define SSD1963_COLOR_DEPTH 16 -#endif - -/*---------------- - * R61581 - *--------------*/ -#ifndef USE_R61581 -# define USE_R61581 0 -#endif - -#if USE_R61581 -# define R61581_HOR_RES LV_HOR_RES -# define R61581_VER_RES LV_VER_RES -# define R61581_HSPL 0 /*HSYNC signal polarity*/ -# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ -# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ -# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ -# define R61581_VSPL 0 /*VSYNC signal polarity*/ -# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ -# define R61581_VFP 8 /*Vertical Front poarch*/ -# define R61581_VBP 8 /*Vertical Back poarch */ -# define R61581_DPL 0 /*DCLK signal polarity*/ -# define R61581_EPL 1 /*ENABLE signal polarity*/ -# define R61581_ORI 0 /*0, 180*/ -# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ -#endif - -/*------------------------------ - * ST7565 (Monochrome, low res.) - *-----------------------------*/ -#ifndef USE_ST7565 -# define USE_ST7565 0 -#endif - -#if USE_ST7565 -/*No settings*/ -#endif /*USE_ST7565*/ - -/*----------------------------------------- - * Linux frame buffer device (/dev/fbx) - *-----------------------------------------*/ -#ifndef USE_FBDEV -# define USE_FBDEV 1 -#endif - -#if USE_FBDEV -# define FBDEV_PATH "/dev/fb0" -#endif - -/********************* - * INPUT DEVICES - *********************/ - -/*-------------- - * XPT2046 - *--------------*/ -#ifndef USE_XPT2046 -# define USE_XPT2046 0 -#endif - -#if USE_XPT2046 -# define XPT2046_HOR_RES 480 -# define XPT2046_VER_RES 320 -# define XPT2046_X_MIN 200 -# define XPT2046_Y_MIN 200 -# define XPT2046_X_MAX 3800 -# define XPT2046_Y_MAX 3800 -# define XPT2046_AVG 4 -# define XPT2046_INV 0 -#endif - -/*----------------- - * FT5406EE8 - *-----------------*/ -#ifndef USE_FT5406EE8 -# define USE_FT5406EE8 0 -#endif - -#if USE_FT5406EE8 -# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ -#endif - -/*--------------- - * AD TOUCH - *--------------*/ -#ifndef USE_AD_TOUCH -# define USE_AD_TOUCH 0 -#endif - -#if USE_AD_TOUCH -/*No settings*/ -#endif - - -/*--------------------------------------- - * Mouse or touchpad on PC (using SDL) - *-------------------------------------*/ -#ifndef USE_MOUSE -# define USE_MOUSE 1 -#endif - -#if USE_MOUSE -/*No settings*/ -#endif - -/*------------------------------------------- - * Mousewheel as encoder on PC (using SDL) - *------------------------------------------*/ -#ifndef USE_MOUSEWHEEL -# define USE_MOUSEWHEEL 1 -#endif - -#if USE_MOUSEWHEEL -/*No settings*/ -#endif - -/*------------------------------------------------- - * Touchscreen as libinput interface (for Linux based systems) - *------------------------------------------------*/ -#ifndef USE_LIBINPUT -# define USE_LIBINPUT 0 -#endif - -#if USE_LIBINPUT -# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ -#endif /*USE_LIBINPUT*/ - -/*------------------------------------------------- - * Mouse or touchpad as evdev interface (for Linux based systems) - *------------------------------------------------*/ -#ifndef USE_EVDEV -# define USE_EVDEV 0 -#endif - -#if USE_EVDEV -# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ -# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ - -# define EVDEV_SCALE 0 /* Scale input, e.g. if touchscreen resolution does not match display resolution */ -# if EVDEV_SCALE -# define EVDEV_SCALE_HOR_RES (4096) /* Horizontal resolution of touchscreen */ -# define EVDEV_SCALE_VER_RES (4096) /* Vertical resolution of touchscreen */ -# endif /*EVDEV_SCALE*/ - -# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ -# if EVDEV_CALIBRATE -# define EVDEV_HOR_MIN 3800 /*If EVDEV_XXX_MIN > EVDEV_XXX_MAX the XXX axis is automatically inverted*/ -# define EVDEV_HOR_MAX 200 -# define EVDEV_VER_MIN 200 -# define EVDEV_VER_MAX 3800 -# endif /*EVDEV_SCALE*/ -#endif /*USE_EVDEV*/ - -/*------------------------------- - * Keyboard of a PC (using SDL) - *------------------------------*/ -#ifndef USE_KEYBOARD -# define USE_KEYBOARD 1 -#endif - -#if USE_KEYBOARD -/*No settings*/ -#endif - -#endif /*LV_DRV_CONF_H*/ - -#endif /*End of "Content enable"*/ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c b/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c deleted file mode 100644 index 607e775964..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#include -#include - - -extern int iwasm_main(int argc, char *argv[]); -int main(int argc, char *argv[]) -{ - return iwasm_main(argc,argv); -} - -int time_get_ms() -{ - static struct timeval tv; - gettimeofday(&tv, NULL); - long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; - - return (int) time_in_mill; -} diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c deleted file mode 100644 index 6d9048d4df..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c +++ /dev/null @@ -1,337 +0,0 @@ -/** - * @file XPT2046.c -*/ -/********************* - * INCLUDES - *********************/ -#include "XPT2046.h" -#include "board_config.h" -#include "stdio.h" -#include -#include "spi.h" - -#include "zephyr.h" -#include "kernel.h" - -#if USE_XPT2046 - -#include - -#define abs(x) ((x) < 0 ? -(x) : (x)) - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void xpt2046_corr(int16_t * x, int16_t * y); -static void xpt2046_avg(int16_t * x, int16_t * y); - -/********************** - * STATIC VARIABLES - **********************/ -int16_t avg_buf_x[XPT2046_AVG]; -int16_t avg_buf_y[XPT2046_AVG]; -uint8_t avg_last; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Initialize the XPT2046 - */ -struct device *input_dev; - -struct spi_config spi_conf_xpt2046; -struct spi_cs_control xpt2046_cs_ctrl; -struct device *xpt2046_pen_gpio_dev; -static struct gpio_callback gpio_cb; -lv_indev_data_t touch_point; -lv_indev_data_t last_touch_point; - -#define TOUCH_READ_THREAD_STACK_SIZE 4096 -static K_THREAD_STACK_DEFINE(touch_read_thread_stack, TOUCH_READ_THREAD_STACK_SIZE); -static struct k_thread touch_thread_data; -static struct k_sem sem_touch_read; - -K_MUTEX_DEFINE( spi_display_touch_mutex); - -int cnt = 0; -int touch_read_times = 0; -int last_pen_interrupt_time = 0; -void xpt2046_pen_gpio_callback(struct device *port, struct gpio_callback *cb, - u32_t pins) -{ - int i; - cnt++; - if ((k_uptime_get_32() - last_pen_interrupt_time) > 500) { - k_sem_give(&sem_touch_read); - touch_read_times++; - last_pen_interrupt_time = k_uptime_get_32(); - } - -} - -void disable_pen_interrupt() -{ - int ret = 0; - ret = gpio_pin_disable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); - if (ret != 0) { - printf("gpio_pin_configure GPIO_DIR_IN failed\n"); - } -} -void enable_pen_interrupt() -{ - int ret = 0; - ret = gpio_pin_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); - if (ret != 0) { - printf("gpio_pin_configure failed\n"); - } -} - -void touch_screen_read_thread() -{ - int i; - bool ret = false; - - for (;;) { - k_sem_take(&sem_touch_read, K_FOREVER); - memset(&last_touch_point, 0, sizeof(lv_indev_data_t)); - memset(&touch_point, 0, sizeof(lv_indev_data_t)); - memset(avg_buf_x, 0, sizeof(avg_buf_x)); - memset(avg_buf_y, 0, sizeof(avg_buf_y)); - k_mutex_lock(&spi_display_touch_mutex, K_FOREVER); - disable_pen_interrupt(); - for (i = 0; i < 100; i++) { - ret = xpt2046_read(&touch_point); - if (ret) { - if ((abs(last_touch_point.point.x - touch_point.point.x) < 4) - && (abs(last_touch_point.point.y - touch_point.point.y) - < 4)) { - break; - } - last_touch_point = touch_point; - - } - } - enable_pen_interrupt(); - k_mutex_unlock(&spi_display_touch_mutex); - } -} - -void xpt2046_init(void) -{ - int ret; - input_dev = device_get_binding(XPT2046_SPI_DEVICE_NAME); - - if (input_dev == NULL) { - printf("device not found. Aborting test."); - return; - } - memset((void *) &touch_point, 0, sizeof(lv_indev_data_t)); - - spi_conf_xpt2046.frequency = XPT2046_SPI_MAX_FREQUENCY; - spi_conf_xpt2046.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); - spi_conf_xpt2046.slave = 0; - spi_conf_xpt2046.cs = NULL; -#ifdef XPT2046_CS_GPIO_CONTROLLER - xpt2046_cs_ctrl.gpio_dev = device_get_binding(XPT2046_CS_GPIO_CONTROLLER); - if (xpt2046_cs_ctrl.gpio_dev == NULL) { - printk("Cannot find %s!\n", XPT2046_CS_GPIO_CONTROLLER); - return; - } - gpio_pin_configure(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, - GPIO_DIR_OUT); - gpio_pin_write(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, 1); - xpt2046_cs_ctrl.gpio_pin = XPT2046_CS_GPIO_PIN; - xpt2046_cs_ctrl.delay = 0; - spi_conf_xpt2046.cs = &xpt2046_cs_ctrl; - -#endif - -#ifdef XPT2046_PEN_GPIO_CONTROLLER - - xpt2046_pen_gpio_dev = device_get_binding(XPT2046_PEN_GPIO_CONTROLLER); - if (!xpt2046_pen_gpio_dev) { - printk("Cannot find %s!\n", XPT2046_PEN_GPIO_CONTROLLER); - return; - } - /* Setup GPIO input */ - ret = gpio_pin_configure(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN, - (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE - | GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE) - ); - if (ret) { - printk("Error configuring pin %d!\n", XPT2046_PEN_GPIO_PIN); - } - - gpio_init_callback(&gpio_cb, xpt2046_pen_gpio_callback, - BIT(XPT2046_PEN_GPIO_PIN)); - - ret = gpio_add_callback(xpt2046_pen_gpio_dev, &gpio_cb); - if (ret) { - printk("gpio_add_callback error\n"); - } - ret = gpio_pin_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); - if (ret) { - printk("gpio_pin_enable_callback error\n"); - } -#endif - - k_sem_init(&sem_touch_read, 0, 1); - - k_thread_create(&touch_thread_data, touch_read_thread_stack, - TOUCH_READ_THREAD_STACK_SIZE, touch_screen_read_thread, - NULL, NULL, NULL, 5, - 0, K_NO_WAIT); - printf("xpt2046_init ok \n"); -} - -/** - * Get the current position and state of the touchpad - * @param data store the read data here - * @return false: because no ore data to be read - */ -bool xpt2046_read(lv_indev_data_t * data) -{ - static int16_t last_x = 0; - static int16_t last_y = 0; - bool valid = true; - int s32_ret = 0; - uint8_t buf; - - int16_t x = 0; - int16_t y = 0; - - char tx1[16] = { 0 }; - char rx1[16] = { 0 }; - - struct spi_buf tx_buf = { .buf = &tx1, .len = 3 }; - struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; - struct spi_buf rx_buf = { .buf = &rx1, .len = 3 }; - struct spi_buf_set rx_bufs = { .buffers = &rx_buf, .count = 1 }; - - tx1[0] = CMD_X_READ; - s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); - if (s32_ret != 0) { - printf("spi_transceive return failed:%d\n", s32_ret); - } - x = rx1[1] << 8; - x += rx1[2]; - - tx1[0] = CMD_Y_READ; - s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); - if (s32_ret != 0) { - printf("spi_transceive return failed:%d\n", s32_ret); - } - y = rx1[1] << 8; - y += rx1[2]; - x = x >> 3; - y = y >> 3; - - xpt2046_corr(&x, &y); - if (y <= 0 || (x > 320)) { - valid = false; - } - - last_x = x; - last_y = y; - - data->point.x = x; - data->point.y = y; - data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; - - return valid; -} - -/********************** - * STATIC FUNCTIONS - **********************/ -static void xpt2046_corr(int16_t * x, int16_t * y) -{ -#if XPT2046_XY_SWAP != 0 - int16_t swap_tmp; - swap_tmp = *x; - *x = *y; - *y = swap_tmp; -#endif - - if ((*x) > XPT2046_X_MIN) - (*x) -= XPT2046_X_MIN; - else - (*x) = 0; - - if ((*y) > XPT2046_Y_MIN) - (*y) -= XPT2046_Y_MIN; - else - (*y) = 0; - - (*x) = (uint32_t)((uint32_t)(*x) * XPT2046_HOR_RES) - / (XPT2046_X_MAX - XPT2046_X_MIN); - - (*y) = (uint32_t)((uint32_t)(*y) * XPT2046_VER_RES) - / (XPT2046_Y_MAX - XPT2046_Y_MIN); - -#if XPT2046_X_INV != 0 - (*x) = XPT2046_HOR_RES - (*x); -#endif - -#if XPT2046_Y_INV != 0 - (*y) = XPT2046_VER_RES - (*y); -#endif - -} - -static void xpt2046_avg(int16_t * x, int16_t * y) -{ - /*Shift out the oldest data*/ - uint8_t i; - for (i = XPT2046_AVG - 1; i > 0; i--) { - avg_buf_x[i] = avg_buf_x[i - 1]; - avg_buf_y[i] = avg_buf_y[i - 1]; - } - - /*Insert the new point*/ - avg_buf_x[0] = *x; - avg_buf_y[0] = *y; - if (avg_last < XPT2046_AVG) - avg_last++; - - /*Sum the x and y coordinates*/ - int32_t x_sum = 0; - int32_t y_sum = 0; - for (i = 0; i < avg_last; i++) { - x_sum += avg_buf_x[i]; - y_sum += avg_buf_y[i]; - } - - /*Normalize the sums*/ - (*x) = (int32_t) x_sum / avg_last; - (*y) = (int32_t) y_sum / avg_last; -} - -bool touchscreen_read(lv_indev_data_t * data) -{ - /*Store the collected data*/ - data->point.x = last_touch_point.point.x; - data->point.y = last_touch_point.point.y; - data->state = last_touch_point.state; - - if (last_touch_point.state == LV_INDEV_STATE_PR) { - last_touch_point.state = LV_INDEV_STATE_REL; - } - return false; -} - -#endif diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h deleted file mode 100644 index 788059d996..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * @file XPT2046.h - * - */ - -#ifndef XPT2046_H -#define XPT2046_H - -#define USE_XPT2046 1 - -# define XPT2046_HOR_RES 320 -# define XPT2046_VER_RES 240 -# define XPT2046_X_MIN 200 -# define XPT2046_Y_MIN 200 -# define XPT2046_X_MAX 3800 -# define XPT2046_Y_MAX 3800 -# define XPT2046_AVG 4 -# define XPT2046_INV 0 - -#define CMD_X_READ 0b10010000 -#define CMD_Y_READ 0b11010000 - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - - -#if USE_XPT2046 -#include -#include -#include -#include "lv_hal/lv_hal_indev.h" -#include "device.h" -#include "gpio.h" - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * GLOBAL PROTOTYPES - **********************/ -void xpt2046_init(void); -bool xpt2046_read(lv_indev_data_t * data); - -/********************** - * MACROS - **********************/ - -#endif /* USE_XPT2046 */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* XPT2046_H */ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h deleted file mode 100644 index d7ea279a92..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#ifndef __BOARD_CONFIG_H__ -#define __BOARD_CONFIG_H__ -#include "pin_config_stm32.h" - -#endif /* __BOARD_CONFIG_H__ */ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h deleted file mode 100644 index b820b9ce96..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * @brief Public API for display drivers and applications - */ - -#ifndef ZEPHYR_INCLUDE_DISPLAY_H_ -#define ZEPHYR_INCLUDE_DISPLAY_H_ - -/** - * @brief Display Interface - * @defgroup display_interface Display Interface - * @ingroup display_interfaces - * @{ - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum display_pixel_format { - PIXEL_FORMAT_RGB_888 = BIT(0), PIXEL_FORMAT_MONO01 = BIT(1), /* 0=Black 1=White */ - PIXEL_FORMAT_MONO10 = BIT(2), /* 1=Black 0=White */ - PIXEL_FORMAT_ARGB_8888 = BIT(3), PIXEL_FORMAT_RGB_565 = BIT(4), -}; - -enum display_screen_info { - /** - * If selected, one octet represents 8 pixels ordered vertically, - * otherwise ordered horizontally. - */ - SCREEN_INFO_MONO_VTILED = BIT(0), - /** - * If selected, the MSB represents the first pixel, - * otherwise MSB represents the last pixel. - */ - SCREEN_INFO_MONO_MSB_FIRST = BIT(1), - /** - * Electrophoretic Display. - */ - SCREEN_INFO_EPD = BIT(2), - /** - * Screen has two alternating ram buffers - */ - SCREEN_INFO_DOUBLE_BUFFER = BIT(3), -}; - -/** - * @enum display_orientation - * @brief Enumeration with possible display orientation - * - */ -enum display_orientation { - DISPLAY_ORIENTATION_NORMAL, - DISPLAY_ORIENTATION_ROTATED_90, - DISPLAY_ORIENTATION_ROTATED_180, - DISPLAY_ORIENTATION_ROTATED_270, -}; - -/** - * @struct display_capabilities - * @brief Structure holding display capabilities - * - * @var u16_t display_capabilities::x_resolution - * Display resolution in the X direction - * - * @var u16_t display_capabilities::y_resolution - * Display resolution in the Y direction - * - * @var u32_t display_capabilities::supported_pixel_formats - * Bitwise or of pixel formats supported by the display - * - * @var u32_t display_capabilities::screen_info - * Information about display panel - * - * @var enum display_pixel_format display_capabilities::current_pixel_format - * Currently active pixel format for the display - * - * @var enum display_orientation display_capabilities::current_orientation - * Current display orientation - * - */ -struct display_capabilities { - u16_t x_resolution; - u16_t y_resolution; - u32_t supported_pixel_formats; - u32_t screen_info; - enum display_pixel_format current_pixel_format; - enum display_orientation current_orientation; -}; - -/** - * @struct display_buffer_descriptor - * @brief Structure to describe display data buffer layout - * - * @var u32_t display_buffer_descriptor::buf_size - * Data buffer size in bytes - * - * @var u16_t display_buffer_descriptor::width - * Data buffer row width in pixels - * - * @var u16_t display_buffer_descriptor::height - * Data buffer column height in pixels - * - * @var u16_t display_buffer_descriptor::pitch - * Number of pixels between consecutive rows in the data buffer - * - */ -struct display_buffer_descriptor { - u32_t buf_size; - u16_t width; - u16_t height; - u16_t pitch; -}; - -/** - * @typedef display_blanking_on_api - * @brief Callback API to turn on display blanking - * See display_blanking_on() for argument description - */ -typedef int (*display_blanking_on_api)(const struct device *dev); - -/** - * @typedef display_blanking_off_api - * @brief Callback API to turn off display blanking - * See display_blanking_off() for argument description - */ -typedef int (*display_blanking_off_api)(const struct device *dev); - -/** - * @typedef display_write_api - * @brief Callback API for writing data to the display - * See display_write() for argument description - */ -typedef int (*display_write_api)(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, - const void *buf); - -/** - * @typedef display_read_api - * @brief Callback API for reading data from the display - * See display_read() for argument description - */ -typedef int (*display_read_api)(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, void *buf); - -/** - * @typedef display_get_framebuffer_api - * @brief Callback API to get framebuffer pointer - * See display_get_framebuffer() for argument description - */ -typedef void *(*display_get_framebuffer_api)(const struct device *dev); - -/** - * @typedef display_set_brightness_api - * @brief Callback API to set display brightness - * See display_set_brightness() for argument description - */ -typedef int (*display_set_brightness_api)(const struct device *dev, - const u8_t brightness); - -/** - * @typedef display_set_contrast_api - * @brief Callback API to set display contrast - * See display_set_contrast() for argument description - */ -typedef int (*display_set_contrast_api)(const struct device *dev, - const u8_t contrast); - -/** - * @typedef display_get_capabilities_api - * @brief Callback API to get display capabilities - * See display_get_capabilities() for argument description - */ -typedef void (*display_get_capabilities_api)(const struct device *dev, - struct display_capabilities * capabilities); - -/** - * @typedef display_set_pixel_format_api - * @brief Callback API to set pixel format used by the display - * See display_set_pixel_format() for argument description - */ -typedef int (*display_set_pixel_format_api)(const struct device *dev, - const enum display_pixel_format pixel_format); - -/** - * @typedef display_set_orientation_api - * @brief Callback API to set orientation used by the display - * See display_set_orientation() for argument description - */ -typedef int (*display_set_orientation_api)(const struct device *dev, - const enum display_orientation orientation); - -/** - * @brief Display driver API - * API which a display driver should expose - */ -struct display_driver_api { - display_blanking_on_api blanking_on; - display_blanking_off_api blanking_off; - display_write_api write; - display_read_api read; - display_get_framebuffer_api get_framebuffer; - display_set_brightness_api set_brightness; - display_set_contrast_api set_contrast; - display_get_capabilities_api get_capabilities; - display_set_pixel_format_api set_pixel_format; - display_set_orientation_api set_orientation; -}; -extern struct ili9340_data ili9340_data1; -extern struct display_driver_api ili9340_api1; -/** - * @brief Write data to display - * - * @param dev Pointer to device structure - * @param x x Coordinate of the upper left corner where to write the buffer - * @param y y Coordinate of the upper left corner where to write the buffer - * @param desc Pointer to a structure describing the buffer layout - * @param buf Pointer to buffer array - * - * @retval 0 on success else negative errno code. - */ -static inline int display_write(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, - const void *buf) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->write(dev, x, y, desc, buf); -} - -/** - * @brief Read data from display - * - * @param dev Pointer to device structure - * @param x x Coordinate of the upper left corner where to read from - * @param y y Coordinate of the upper left corner where to read from - * @param desc Pointer to a structure describing the buffer layout - * @param buf Pointer to buffer array - * - * @retval 0 on success else negative errno code. - */ -static inline int display_read(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, void *buf) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->read(dev, x, y, desc, buf); -} - -/** - * @brief Get pointer to framebuffer for direct access - * - * @param dev Pointer to device structure - * - * @retval Pointer to frame buffer or NULL if direct framebuffer access - * is not supported - * - */ -static inline void *display_get_framebuffer(const struct device *dev) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->get_framebuffer(dev); -} - -/** - * @brief Turn display blanking on - * - * @param dev Pointer to device structure - * - * @retval 0 on success else negative errno code. - */ -static inline int display_blanking_on(const struct device *dev) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->blanking_on(dev); -} - -/** - * @brief Turn display blanking off - * - * @param dev Pointer to device structure - * - * @retval 0 on success else negative errno code. - */ -static inline int display_blanking_off(const struct device *dev) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->blanking_off(dev); -} - -/** - * @brief Set the brightness of the display - * - * Set the brightness of the display in steps of 1/256, where 255 is full - * brightness and 0 is minimal. - * - * @param dev Pointer to device structure - * @param brightness Brightness in steps of 1/256 - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_brightness(const struct device *dev, - u8_t brightness) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_brightness(dev, brightness); -} - -/** - * @brief Set the contrast of the display - * - * Set the contrast of the display in steps of 1/256, where 255 is maximum - * difference and 0 is minimal. - * - * @param dev Pointer to device structure - * @param contrast Contrast in steps of 1/256 - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_contrast(const struct device *dev, u8_t contrast) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_contrast(dev, contrast); -} - -/** - * @brief Get display capabilities - * - * @param dev Pointer to device structure - * @param capabilities Pointer to capabilities structure to populate - */ -static inline void display_get_capabilities(const struct device *dev, - struct display_capabilities * capabilities) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - api->get_capabilities(dev, capabilities); -} - -/** - * @brief Set pixel format used by the display - * - * @param dev Pointer to device structure - * @param pixel_format Pixel format to be used by display - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_pixel_format(const struct device *dev, - const enum display_pixel_format pixel_format) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_pixel_format(dev, pixel_format); -} - -/** - * @brief Set display orientation - * - * @param dev Pointer to device structure - * @param orientation Orientation to be used by display - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_orientation(const struct device *dev, - const enum display_orientation orientation) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_orientation(dev, orientation); -} - -#ifdef __cplusplus -} -#endif - -/** - * @} - */ - -#endif /* ZEPHYR_INCLUDE_DISPLAY_H_*/ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c deleted file mode 100644 index 6326e0ae85..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "display_ili9340.h" -#include - -//#define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL -//#include -//LOG_MODULE_REGISTER(display_ili9340); -#define LOG_ERR printf -#define LOG_DBG printf -#define LOG_WRN printf - -#include -#include -#include -#include - -struct ili9340_data { - struct device *reset_gpio; - struct device *command_data_gpio; - struct device *spi_dev; - struct spi_config spi_config; -#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER -struct spi_cs_control cs_ctrl; -#endif -}; - -struct ili9340_data ili9340_data1; - -#define ILI9340_CMD_DATA_PIN_COMMAND 0 -#define ILI9340_CMD_DATA_PIN_DATA 1 - -static void ili9340_exit_sleep(struct ili9340_data *data) -{ - ili9340_transmit(data, ILI9340_CMD_EXIT_SLEEP, NULL, 0); - //k_sleep(120); -} - -int ili9340_init() -{ - struct ili9340_data *data = &ili9340_data1; - printf("Initializing display driver\n"); - data->spi_dev = device_get_binding(DT_ILITEK_ILI9340_0_BUS_NAME); - if (data->spi_dev == NULL) { - return -EPERM; - } - data->spi_config.frequency = DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY; - data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); //SPI_OP_MODE_MASTER | SPI_WORD_SET(8); - data->spi_config.slave = DT_ILITEK_ILI9340_0_BASE_ADDRESS; - -#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER - data->cs_ctrl.gpio_dev = - device_get_binding(DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER); - data->cs_ctrl.gpio_pin = DT_ILITEK_ILI9340_0_CS_GPIO_PIN; - data->cs_ctrl.delay = 0; - data->spi_config.cs = &(data->cs_ctrl); -#else - data->spi_config.cs = NULL; -#endif - data->reset_gpio = device_get_binding( - DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER); - if (data->reset_gpio == NULL) { - return -EPERM; - } - - gpio_pin_configure(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, - GPIO_DIR_OUT); - - data->command_data_gpio = device_get_binding( - DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER); - if (data->command_data_gpio == NULL) { - return -EPERM; - } - - gpio_pin_configure(data->command_data_gpio, - DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, GPIO_DIR_OUT); - - LOG_DBG("Resetting display driver"); - gpio_pin_write(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); - k_sleep(1); - gpio_pin_write(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 0); - k_sleep(1); - gpio_pin_write(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); - k_sleep(5); - - LOG_DBG("Initializing LCD\n"); - ili9340_lcd_init(data); - - LOG_DBG("Exiting sleep mode\n"); - ili9340_exit_sleep(data); - - return 0; -} - -static void ili9340_set_mem_area(struct ili9340_data *data, const u16_t x, - const u16_t y, const u16_t w, const u16_t h) -{ - u16_t spi_data[2]; - - spi_data[0] = sys_cpu_to_be16(x); - spi_data[1] = sys_cpu_to_be16(x + w - 1); - ili9340_transmit(data, ILI9340_CMD_COLUMN_ADDR, &spi_data[0], 4); - - spi_data[0] = sys_cpu_to_be16(y); - spi_data[1] = sys_cpu_to_be16(y + h - 1); - ili9340_transmit(data, ILI9340_CMD_PAGE_ADDR, &spi_data[0], 4); -} - -static int ili9340_write(const struct device *dev, const u16_t x, const u16_t y, - const struct display_buffer_descriptor *desc, const void *buf) -{ - struct ili9340_data *data = (struct ili9340_data *) &ili9340_data1; - const u8_t *write_data_start = (u8_t *) buf; - struct spi_buf tx_buf; - struct spi_buf_set tx_bufs; - u16_t write_cnt; - u16_t nbr_of_writes; - u16_t write_h; - - __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width"); - __ASSERT((3 * desc->pitch * desc->height) <= desc->buf_size, - "Input buffer to small"); - ili9340_set_mem_area(data, x, y, desc->width, desc->height); - - if (desc->pitch > desc->width) { - write_h = 1U; - nbr_of_writes = desc->height; - } else { - write_h = desc->height; - nbr_of_writes = 1U; - } - ili9340_transmit(data, ILI9340_CMD_MEM_WRITE, (void *) write_data_start, - 3 * desc->width * write_h); - - tx_bufs.buffers = &tx_buf; - tx_bufs.count = 1; - - write_data_start += (3 * desc->pitch); - for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) { - tx_buf.buf = (void *) write_data_start; - tx_buf.len = 3 * desc->width * write_h; - spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); - write_data_start += (3 * desc->pitch); - } - - return 0; -} - -static int ili9340_read(const struct device *dev, const u16_t x, const u16_t y, - const struct display_buffer_descriptor *desc, void *buf) -{ - LOG_ERR("Reading not supported"); - return -ENOTSUP; -} - -static void *ili9340_get_framebuffer(const struct device *dev) -{ - LOG_ERR("Direct framebuffer access not supported"); - return NULL; -} - -static int ili9340_display_blanking_off(const struct device *dev) -{ - struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; - - LOG_DBG("Turning display blanking off"); - ili9340_transmit(data, ILI9340_CMD_DISPLAY_ON, NULL, 0); - return 0; -} - -static int ili9340_display_blanking_on(const struct device *dev) -{ - struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; - - LOG_DBG("Turning display blanking on"); - ili9340_transmit(data, ILI9340_CMD_DISPLAY_OFF, NULL, 0); - return 0; -} - -static int ili9340_set_brightness(const struct device *dev, - const u8_t brightness) -{ - LOG_WRN("Set brightness not implemented"); - return -ENOTSUP; -} - -static int ili9340_set_contrast(const struct device *dev, const u8_t contrast) -{ - LOG_ERR("Set contrast not supported"); - return -ENOTSUP; -} - -static int ili9340_set_pixel_format(const struct device *dev, - const enum display_pixel_format pixel_format) -{ - if (pixel_format == PIXEL_FORMAT_RGB_888) { - return 0; -} - LOG_ERR("Pixel format change not implemented"); - return -ENOTSUP; -} - -static int ili9340_set_orientation(const struct device *dev, - const enum display_orientation orientation) -{ -if (orientation == DISPLAY_ORIENTATION_NORMAL) { - return 0; -} -LOG_ERR("Changing display orientation not implemented"); - return -ENOTSUP; -} - -static void ili9340_get_capabilities(const struct device *dev, - struct display_capabilities *capabilities) -{ - memset(capabilities, 0, sizeof(struct display_capabilities)); - capabilities->x_resolution = 320; - capabilities->y_resolution = 240; - capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_888; - capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888; - capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; -} - -void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, - size_t tx_len) -{ - int i; - char * buf1 = tx_data; - data = (struct ili9340_data *) &ili9340_data1; - struct spi_buf tx_buf = { .buf = &cmd, .len = 1 }; - struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; - - gpio_pin_write(data->command_data_gpio, DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, - ILI9340_CMD_DATA_PIN_COMMAND); - spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); - if (tx_data != NULL) { - tx_buf.buf = tx_data; - tx_buf.len = tx_len; - gpio_pin_write(data->command_data_gpio, - DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, - ILI9340_CMD_DATA_PIN_DATA); - spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); - } -} - -struct display_driver_api ili9340_api1 = - { .blanking_on = ili9340_display_blanking_on, .blanking_off = - ili9340_display_blanking_off, .write = ili9340_write, .read = - ili9340_read, .get_framebuffer = ili9340_get_framebuffer, - .set_brightness = ili9340_set_brightness, .set_contrast = - ili9340_set_contrast, .get_capabilities = - ili9340_get_capabilities, .set_pixel_format = - ili9340_set_pixel_format, .set_orientation = - ili9340_set_orientation, }; - -/* - DEVICE_AND_API_INIT(ili9340, DT_ILITEK_ILI9340_0_LABEL, &ili9340_init, - &ili9340_data, NULL, APPLICATION, - CONFIG_APPLICATION_INIT_PRIORITY, &ili9340_api); - */ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h deleted file mode 100644 index 8aab97a651..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ -#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ -#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ -#include "board_config.h" -#include -#include - -#define ILI9340_CMD_ENTER_SLEEP 0x10 -#define ILI9340_CMD_EXIT_SLEEP 0x11 -#define ILI9340_CMD_GAMMA_SET 0x26 -#define ILI9340_CMD_DISPLAY_OFF 0x28 -#define ILI9340_CMD_DISPLAY_ON 0x29 -#define ILI9340_CMD_COLUMN_ADDR 0x2a -#define ILI9340_CMD_PAGE_ADDR 0x2b -#define ILI9340_CMD_MEM_WRITE 0x2c -#define ILI9340_CMD_MEM_ACCESS_CTRL 0x36 -#define ILI9340_CMD_PIXEL_FORMAT_SET 0x3A -#define ILI9340_CMD_FRAME_CTRL_NORMAL_MODE 0xB1 -#define ILI9340_CMD_DISPLAY_FUNCTION_CTRL 0xB6 -#define ILI9340_CMD_POWER_CTRL_1 0xC0 -#define ILI9340_CMD_POWER_CTRL_2 0xC1 -#define ILI9340_CMD_VCOM_CTRL_1 0xC5 -#define ILI9340_CMD_VCOM_CTRL_2 0xC7 -#define ILI9340_CMD_POSITVE_GAMMA_CORRECTION 0xE0 -#define ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION 0xE1 - -#define ILI9340_DATA_MEM_ACCESS_CTRL_MY 0x80 -#define ILI9340_DATA_MEM_ACCESS_CTRL_MX 0x40 -#define ILI9340_DATA_MEM_ACCESS_CTRL_MV 0x20 -#define ILI9340_DATA_MEM_ACCESS_CTRL_ML 0x10 -#define ILI9340_DATA_MEM_ACCESS_CTRL_BGR 0x08 -#define ILI9340_DATA_MEM_ACCESS_CTRL_MH 0x04 - -#define ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT 0x60 -#define ILI9340_DATA_PIXEL_FORMAT_RGB_16_BIT 0x50 -#define ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT 0x06 -#define ILI9340_DATA_PIXEL_FORMAT_MCU_16_BIT 0x05 - -struct ili9340_data; - -/** - * Send data to ILI9340 display controller - * - * @param data Device data structure - * @param cmd Command to send to display controller - * @param tx_data Data to transmit to the display controller - * In case no data should be transmitted pass a NULL pointer - * @param tx_len Number of bytes in tx_data buffer - * - */ -void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, - size_t tx_len); - -/** - * Perform LCD specific initialization - * - * @param data Device data structure - */ -void ili9340_lcd_init(struct ili9340_data *data); - -#define DT_ILITEK_ILI9340_0_LABEL "DISPLAY" -#define CONFIG_DISPLAY_LOG_LEVEL 0 - -#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ */ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c deleted file mode 100644 index a8581a72d3..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "display_ili9340.h" - -void ili9340_lcd_init(struct ili9340_data *data) -{ - u8_t tx_data[15]; - - tx_data[0] = 0x23; - ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_1, tx_data, 1); - - tx_data[0] = 0x10; - ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_2, tx_data, 1); - - tx_data[0] = 0x3e; - tx_data[1] = 0x28; - ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_1, tx_data, 2); - - tx_data[0] = 0x86; - ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_2, tx_data, 1); - - tx_data[0] = - ILI9340_DATA_MEM_ACCESS_CTRL_MV | ILI9340_DATA_MEM_ACCESS_CTRL_BGR; - ili9340_transmit(data, ILI9340_CMD_MEM_ACCESS_CTRL, tx_data, 1); - - tx_data[0] = ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT | - ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT; - ili9340_transmit(data, ILI9340_CMD_PIXEL_FORMAT_SET, tx_data, 1); - - tx_data[0] = 0x00; - tx_data[1] = 0x18; - ili9340_transmit(data, ILI9340_CMD_FRAME_CTRL_NORMAL_MODE, tx_data, 2); - - tx_data[0] = 0x08; - tx_data[1] = 0x82; - tx_data[2] = 0x27; - ili9340_transmit(data, ILI9340_CMD_DISPLAY_FUNCTION_CTRL, tx_data, 3); - - tx_data[0] = 0x01; - ili9340_transmit(data, ILI9340_CMD_GAMMA_SET, tx_data, 1); - - tx_data[0] = 0x0F; - tx_data[1] = 0x31; - tx_data[2] = 0x2B; - tx_data[3] = 0x0C; - tx_data[4] = 0x0E; - tx_data[5] = 0x08; - tx_data[6] = 0x4E; - tx_data[7] = 0xF1; - tx_data[8] = 0x37; - tx_data[9] = 0x07; - tx_data[10] = 0x10; - tx_data[11] = 0x03; - tx_data[12] = 0x0E; - tx_data[13] = 0x09; - tx_data[14] = 0x00; - ili9340_transmit(data, ILI9340_CMD_POSITVE_GAMMA_CORRECTION, tx_data, 15); - - tx_data[0] = 0x00; - tx_data[1] = 0x0E; - tx_data[2] = 0x14; - tx_data[3] = 0x03; - tx_data[4] = 0x11; - tx_data[5] = 0x07; - tx_data[6] = 0x31; - tx_data[7] = 0xC1; - tx_data[8] = 0x48; - tx_data[9] = 0x08; - tx_data[10] = 0x0F; - tx_data[11] = 0x0C; - tx_data[12] = 0x31; - tx_data[13] = 0x36; - tx_data[14] = 0x0F; - ili9340_transmit(data, ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION, tx_data, 15); -} diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c deleted file mode 100644 index 5aee8b5766..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#include "bh_platform.h" -#include "runtime_lib.h" -#include "native_interface.h" -#include "app_manager_export.h" -#include "board_config.h" -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "runtime_sensor.h" -#include "bi-inc/attr_container.h" -#include "module_wasm_app.h" -#include "wasm_export.h" -#include "display.h" -#include "lvgl.h" - -extern void init_sensor_framework(); -extern void exit_sensor_framework(); -extern int aee_host_msg_callback(void *msg, uint16_t msg_len); -extern bool touchscreen_read(lv_indev_data_t * data); -extern int ili9340_init(); -extern void xpt2046_init(void); -extern void wgl_init(); - -#include -#include -#include - -int uart_char_cnt = 0; - -static void uart_irq_callback(struct device *dev) -{ - unsigned char ch; - - while (uart_poll_in(dev, &ch) == 0) { - uart_char_cnt++; - aee_host_msg_callback(&ch, 1); - } -} - -struct device *uart_dev = NULL; - -static bool host_init() -{ - uart_dev = device_get_binding(HOST_DEVICE_COMM_UART_NAME); - if (!uart_dev) { - printf("UART: Device driver not found.\n"); - return false; - } - uart_irq_rx_enable(uart_dev); - uart_irq_callback_set(uart_dev, uart_irq_callback); - return true; -} - -int host_send(void * ctx, const char *buf, int size) -{ - if (!uart_dev) - return 0; - - for (int i = 0; i < size; i++) - uart_poll_out(uart_dev, buf[i]); - - return size; -} - -void host_destroy() -{ -} - -host_interface interface = { - .init = host_init, - .send = host_send, - .destroy = host_destroy -}; - -timer_ctx_t timer_ctx; - -static char global_heap_buf[270 * 1024] = { 0 }; - -static uint8_t color_copy[320 * 10 * 3]; - -static void display_flush(lv_disp_drv_t *disp_drv, - const lv_area_t *area, - lv_color_t *color) -{ - u16_t w = area->x2 - area->x1 + 1; - u16_t h = area->y2 - area->y1 + 1; - struct display_buffer_descriptor desc; - int i; - uint8_t *color_p = color_copy; - - desc.buf_size = 3 * w * h; - desc.width = w; - desc.pitch = w; - desc.height = h; - - for (i = 0; i < w * h; i++, color++) { - color_p[i * 3] = color->ch.red; - color_p[i * 3 + 1] = color->ch.green; - color_p[i * 3 + 2] = color->ch.blue; - } - - display_write(NULL, area->x1, area->y1, &desc, (void *) color_p); - - lv_disp_flush_ready(disp_drv); /* in v5.3 is lv_flush_ready */ -} - -static bool display_input_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) -{ - return touchscreen_read(data); -} - -/** - * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library - */ -static void hal_init(void) -{ - xpt2046_init(); - ili9340_init(); - display_blanking_off(NULL); - - /*Create a display buffer*/ - static lv_disp_buf_t disp_buf1; - static lv_color_t buf1_1[320*10]; - lv_disp_buf_init(&disp_buf1, buf1_1, NULL, 320*10); - - /*Create a display*/ - lv_disp_drv_t disp_drv; - lv_disp_drv_init(&disp_drv); /*Basic initialization*/ - disp_drv.buffer = &disp_buf1; - disp_drv.flush_cb = display_flush; - // disp_drv.hor_res = 200; - // disp_drv.ver_res = 100; - lv_disp_drv_register(&disp_drv); - - lv_indev_drv_t indev_drv; - lv_indev_drv_init(&indev_drv); /*Basic initialization*/ - indev_drv.type = LV_INDEV_TYPE_POINTER; - indev_drv.read_cb = display_input_read; - lv_indev_drv_register(&indev_drv); -} - -int iwasm_main() -{ - host_init(); - - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - printf("Init global heap failed.\n"); - return -1; - } - - if (vm_thread_sys_init() != 0) { - goto fail1; - } - - wgl_init(); - hal_init(); - - // timer manager - init_wasm_timer(); - - // TODO: - app_manager_startup(&interface); - -fail1: - bh_memory_destroy(); - return -1; -} diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c deleted file mode 100644 index 74e007e97c..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include "bh_platform.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_platform_log.h" -#include "wasm_export.h" -#include "bh_memory.h" - -extern int iwasm_main(); - -void main(void) -{ - iwasm_main(); - for(;;){ - k_sleep(1000); - } -} - -int time_get_ms() -{ - return k_uptime_get_32(); -} diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h deleted file mode 100644 index 3cde4ec140..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#ifndef __PIN_CONFIG_JLF_H__ -#define __PIN_CONFIG_JLF_H__ - -#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_2" -#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 10*1000 - -#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIO_0" -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 5 -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIO_0" -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 4 - -#define XPT2046_SPI_DEVICE_NAME "SPI_2" -#define XPT2046_SPI_MAX_FREQUENCY 10*1000 -#define XPT2046_CS_GPIO_CONTROLLER "GPIO_0" -#define XPT2046_CS_GPIO_PIN 6 - -#define XPT2046_PEN_GPIO_CONTROLLER "GPIO_0" -#define XPT2046_PEN_GPIO_PIN 7 - -#define HOST_DEVICE_COMM_UART_NAME "UART_1" -#endif /* __PIN_CONFIG_JLF_H__ */ diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h b/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h deleted file mode 100644 index fba36fab47..0000000000 --- a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#ifndef __PIN_CONFIG_STM32_H__ -#define __PIN_CONFIG_STM32_H__ - -#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_1" -#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 24*1000*1000 - -#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIOC" -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 12 -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIOC" -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 11 - -#define DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER "GPIOC" -#define DT_ILITEK_ILI9340_0_CS_GPIO_PIN 10 - -#define XPT2046_SPI_DEVICE_NAME "SPI_1" -#define XPT2046_SPI_MAX_FREQUENCY 12*1000*1000 -#define XPT2046_CS_GPIO_CONTROLLER "GPIOD" -#define XPT2046_CS_GPIO_PIN 0 - -#define XPT2046_PEN_GPIO_CONTROLLER "GPIOD" -#define XPT2046_PEN_GPIO_PIN 1 - -#define HOST_DEVICE_COMM_UART_NAME "UART_6" - -#endif /* __PIN_CONFIG_STM32_H__ */ diff --git a/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt b/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt deleted file mode 100644 index ef9dc919c9..0000000000 --- a/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required(VERSION 3.8.2) - -include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) -project(NONE) - -set (WAMR_BUILD_PLATFORM "zephyr") - -enable_language (ASM) - -# Build as THUMB by default -# change to "ARM[sub]", "THUMB[sub]", "X86_32", "MIPS" or "XTENSA" -# if we want to support arm_32, x86, mips or xtensa -if (NOT DEFINED WAMR_BUILD_TARGET) - set (WAMR_BUILD_TARGET "THUMBV7") -endif () - - -if (NOT DEFINED WAMR_BUILD_INTERP) - # Enable Interpreter by default - set (WAMR_BUILD_INTERP 1) -endif () - -if (NOT DEFINED WAMR_BUILD_AOT) - set (WAMR_BUILD_AOT 1) -endif () - -if (NOT DEFINED WAMR_BUILD_JIT) - # Disable JIT by default. - set (WAMR_BUILD_JIT 0) -endif () - -if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) - # Enable libc builtin support by default - set (WAMR_BUILD_LIBC_BUILTIN 1) -endif () - -if (NOT DEFINED WAMR_BUILD_LIBC_WASI) - # Disable libc wasi support by default - set (WAMR_BUILD_LIBC_WASI 0) -endif () - -if (NOT DEFINED WAMR_BUILD_APP_FRAMEWORK) - set (WAMR_BUILD_APP_FRAMEWORK 1) -endif () - -if (NOT DEFINED WAMR_BUILD_APP_LIST) - set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_ALL) -endif () - -################ wamr runtime settings ################ -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) -include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) - -################ sample project related ################ -add_definitions(-DLV_CONF_INCLUDE_SIMPLE) -add_definitions(-DLV_MEM_CUSTOM=1) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr) -include_directories(${WAMR_ROOT_DIR}/samples/gui/lv_config) - -set (LVGL_DRV_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340_adafruit_1480.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/XPT2046.c - ) - -target_sources(app PRIVATE - ${WAMR_RUNTIME_LIB_SOURCE} - ${LVGL_DRV_SRCS} - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/iwasm_main.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/ext_lib_export.c - ) diff --git a/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf b/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf deleted file mode 100644 index 4117f0fb5e..0000000000 --- a/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG_SPI=y -CONFIG_SPI_STM32=y -CONFIG_SPI_1=y -CONFIG_PRINTK=y -CONFIG_LOG=y -#CONFIG_UART_2=y -CONFIG_UART_INTERRUPT_DRIVEN=y -CONFIG_STACK_SENTINEL=y -CONFIG_MAIN_STACK_SIZE=2048 -CONFIG_ARM_MPU=n diff --git a/samples/import-func-callback/CMakeLists.txt b/samples/import-func-callback/CMakeLists.txt new file mode 100644 index 0000000000..64a4ddc7b0 --- /dev/null +++ b/samples/import-func-callback/CMakeLists.txt @@ -0,0 +1,101 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) +include(ExternalProject) + +project (import-func-callback) + +set (CMAKE_CXX_STANDARD 17) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 Debug) +endif () + +set (WAMR_BUILD_INTERP 1) + +if (NOT MSVC) + # linker 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") +endif () + +# 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 ################ +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +ExternalProject_Add(wasm_app + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps + BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/wasm-apps + CONFIGURE_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E env + ${WASI_SDK_DIR}/bin/clang + -nostdlib + --target=wasm32 + -Wl,--no-entry + -Wl,--export=test + -Wl,--allow-undefined + -o ${CMAKE_CURRENT_BINARY_DIR}/wasm-apps/test.wasm + ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps/test.c + BUILD_ALWAYS TRUE + INSTALL_COMMAND "" +) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (import-func-callback src/main.c ${UNCOMMON_SHARED_SOURCE}) + +add_dependencies(import-func-callback wasm_app) + +check_pie_supported() +set_target_properties (import-func-callback PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (import-func-callback vmlib -lm -ldl -lpthread ${LLVM_AVAILABLE_LIBS}) +else () + target_link_libraries (import-func-callback vmlib -lm -ldl -lpthread -lrt ${LLVM_AVAILABLE_LIBS}) +endif () diff --git a/samples/import-func-callback/README.md b/samples/import-func-callback/README.md new file mode 100644 index 0000000000..ee822388b5 --- /dev/null +++ b/samples/import-func-callback/README.md @@ -0,0 +1,14 @@ +# "import function callback" sample introduction + +This sample demonstrates how to use import function callbacks to handle WebAssembly modules that import external functions. The sample shows how to register callback functions for imported functions and execute them when the WASM module loads these imported functions. + +The sample includes a WASM module that imports external functions and a host application that provides callback `import_func_type_callback` for these imported functions. + +## Build and run the sample + +```bash +mkdir build && cd build +cmake .. +cmake --build . --config Release +./import-func-callback +``` diff --git a/samples/import-func-callback/src/main.c b/samples/import-func-callback/src/main.c new file mode 100644 index 0000000000..a5f9a9acfe --- /dev/null +++ b/samples/import-func-callback/src/main.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" +#include "assert.h" + +typedef void (*wasm_func_type_callback_t)(const wasm_import_t *import_type); + +const char *import_func_names[] = { "import_func1", "import_func2" }; + +void +import_func_type_callback(const wasm_import_t *import_type) +{ + int ret = 0; + for (uint32_t i = 0; + i < sizeof(import_func_names) / sizeof(import_func_names[0]); i++) { + if (strcmp(import_type->name, import_func_names[i]) == 0) { + ret = 1; + break; + } + } + assert(ret == 1); + return; +} + +/* Iterate over all import functions in the module */ +void +wasm_runtime_for_each_import_func(const wasm_module_t module, + wasm_func_type_callback_t callback) +{ + int32_t import_count = wasm_runtime_get_import_count(module); + if (import_count <= 0) + return; + if (callback == NULL) + return; + + for (int32_t i = 0; i < import_count; ++i) { + wasm_import_t import_type; + wasm_runtime_get_import_type(module, i, &import_type); + + if (import_type.kind != WASM_IMPORT_EXPORT_KIND_FUNC) { + continue; + } + + callback(&import_type); + } +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + wasm_module_t module = NULL; + uint32 buf_size; + char *buffer = NULL; + const char *wasm_path = "wasm-apps/test.wasm"; + char error_buf[128]; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + wasm_runtime_for_each_import_func(module, import_func_type_callback); + +fail: + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/import-func-callback/wasm-apps/test.c b/samples/import-func-callback/wasm-apps/test.c new file mode 100644 index 0000000000..5e6f30b5c6 --- /dev/null +++ b/samples/import-func-callback/wasm-apps/test.c @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +extern int +import_func1(int a, int b); +extern int +import_func2(int a); + +int +test() +{ + int a = import_func1(1, 2); + int b = import_func2(3); + return a + b; +} diff --git a/samples/inst-context-threads/.gitignore b/samples/inst-context-threads/.gitignore new file mode 100644 index 0000000000..0fa8a76bda --- /dev/null +++ b/samples/inst-context-threads/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/inst-context-threads/CMakeLists.txt b/samples/inst-context-threads/CMakeLists.txt new file mode 100644 index 0000000000..6d4b1e8565 --- /dev/null +++ b/samples/inst-context-threads/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (inst-context) +else() + project (inst-context C ASM) +endif() + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 Debug) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 0) +set (WAMR_BUILD_LIB_WASI_THREADS 1) + +if (NOT MSVC) + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT MSVC) + # linker 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 () +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (inst-context src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (inst-context PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (inst-context vmlib -lm -ldl -lpthread) +else () + target_link_libraries (inst-context vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/samples/inst-context-threads/README.md b/samples/inst-context-threads/README.md new file mode 100644 index 0000000000..d64cbff52b --- /dev/null +++ b/samples/inst-context-threads/README.md @@ -0,0 +1,5 @@ +The "inst-context-threads" sample project +========================================= + +This sample demonstrates some interactions between +module instance context API and wasi-threads. diff --git a/samples/inst-context-threads/build.sh b/samples/inst-context-threads/build.sh new file mode 100755 index 0000000000..35f76eccf8 --- /dev/null +++ b/samples/inst-context-threads/build.sh @@ -0,0 +1,61 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "#####################build inst-context project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. +make -j ${nproc} +if [ $? != 0 ];then + echo "BUILD_FAIL inst-context exit as $?\n" + exit 2 +fi + +cp -a inst-context ${OUT_DIR} + +echo -e "\n" + +echo "#####################build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# use WAMR SDK to build out the .wasm binary +# require wasi-sdk with wasi-threads support. (wasi-sdk-20.0 or later) +/opt/wasi-sdk/bin/clang \ + --target=wasm32-wasi-threads \ + -pthread \ + -Wl,--import-memory \ + -Wl,--export-memory \ + -Wl,--max-memory=655360 \ + -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "####################build wasm apps done" diff --git a/samples/inst-context-threads/run.sh b/samples/inst-context-threads/run.sh new file mode 100755 index 0000000000..919ed01666 --- /dev/null +++ b/samples/inst-context-threads/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/inst-context -f out/wasm-apps/testapp.wasm diff --git a/samples/inst-context-threads/src/main.c b/samples/inst-context-threads/src/main.c new file mode 100644 index 0000000000..2a20363c55 --- /dev/null +++ b/samples/inst-context-threads/src/main.c @@ -0,0 +1,151 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" +#include "my_context.h" + +void +set_context(wasm_exec_env_t exec_env, int32_t n); +int32_t +get_context(wasm_exec_env_t exec_env); + +void *my_context_key; +struct my_context my_context; +int my_dtor_called; + +wasm_module_inst_t module_inst = NULL; + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +void +my_context_dtor(wasm_module_inst_t inst, void *ctx) +{ + printf("%s called\n", __func__); + my_dtor_called++; + bh_assert(ctx == &my_context); + bh_assert(inst == module_inst); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer; + char error_buf[128]; + int opt; + char *wasm_path = NULL; + int exit_code = 1; + + wasm_module_t module = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + // Define an array of NativeSymbol for the APIs to be exported. + // Note: the array must be static defined since runtime + // will keep it after registration + // For the function signature specifications, goto the link: + // https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md + + static NativeSymbol native_symbols[] = { + { "set_context", set_context, "(i)", NULL }, + { "get_context", get_context, "()i", NULL }, + }; + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + // Native symbols need below registration phase + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + my_context_key = wasm_runtime_create_context_key(my_context_dtor); + if (!my_context_key) { + printf("wasm_runtime_create_context_key failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + char *args[] = { + "testapp", + }; + wasm_application_execute_main(module_inst, 1, args); + const char *exc = wasm_runtime_get_exception(module_inst); + if (exc != NULL) { + printf("call wasm function calculate failed. error: %s\n", exc); + goto fail; + } + + exit_code = 0; +fail: + if (module_inst) { + bh_assert(my_dtor_called == 0); + wasm_runtime_deinstantiate(module_inst); + bh_assert(my_dtor_called == 1); + } + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + if (my_context_key) + wasm_runtime_destroy_context_key(my_context_key); + wasm_runtime_destroy(); + return exit_code; +} diff --git a/samples/inst-context-threads/src/my_context.h b/samples/inst-context-threads/src/my_context.h new file mode 100644 index 0000000000..008be71ff1 --- /dev/null +++ b/samples/inst-context-threads/src/my_context.h @@ -0,0 +1,11 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +struct my_context { + int x; +}; + +extern void *my_context_key; +extern struct my_context my_context; diff --git a/samples/inst-context-threads/src/native_impl.c b/samples/inst-context-threads/src/native_impl.c new file mode 100644 index 0000000000..0733e1976d --- /dev/null +++ b/samples/inst-context-threads/src/native_impl.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" +#include "my_context.h" + +void +set_context(wasm_exec_env_t exec_env, int32_t n) +{ + wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env); + printf("%s called on module inst %p\n", __func__, inst); + struct my_context *ctx = &my_context; + ctx->x = n; + wasm_runtime_set_context_spread(inst, my_context_key, ctx); +} + +int32_t +get_context(wasm_exec_env_t exec_env) +{ + wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env); + printf("%s called on module inst %p\n", __func__, inst); + struct my_context *ctx = wasm_runtime_get_context(inst, my_context_key); + if (ctx == NULL) { + return -1; + } + return ctx->x; +} diff --git a/samples/inst-context-threads/wasm-apps/testapp.c b/samples/inst-context-threads/wasm-apps/testapp.c new file mode 100644 index 0000000000..429b0875fa --- /dev/null +++ b/samples/inst-context-threads/wasm-apps/testapp.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +void +set_context(int32_t n) __attribute__((import_module("env"))) +__attribute__((import_name("set_context"))); + +int32_t +get_context() __attribute__((import_module("env"))) +__attribute__((import_name("get_context"))); + +void * +start(void *vp) +{ + int32_t v; + + printf("thread started\n"); + + printf("confirming the initial state on thread\n"); + v = get_context(); + assert(v == -1); + + printf("setting the context on thread\n"); + set_context(1234); + + printf("confirming the context on thread\n"); + v = get_context(); + assert(v == 1234); + return NULL; +} + +int +main() +{ + pthread_t t1; + int32_t v; + int ret; + + printf("confirming the initial state on main\n"); + v = get_context(); + assert(v == -1); + + printf("creating a thread\n"); + ret = pthread_create(&t1, NULL, start, NULL); + assert(ret == 0); + void *val; + ret = pthread_join(t1, &val); + assert(ret == 0); + printf("joined the thread\n"); + + printf("confirming the context propagated from the thread on main\n"); + v = get_context(); + assert(v == 1234); + + printf("success\n"); + return 0; +} diff --git a/samples/inst-context/.gitignore b/samples/inst-context/.gitignore new file mode 100644 index 0000000000..0fa8a76bda --- /dev/null +++ b/samples/inst-context/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/inst-context/CMakeLists.txt b/samples/inst-context/CMakeLists.txt new file mode 100644 index 0000000000..91584ad9c3 --- /dev/null +++ b/samples/inst-context/CMakeLists.txt @@ -0,0 +1,84 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (inst-context) +else() + project (inst-context C ASM) +endif() + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 Debug) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) + +if (NOT MSVC) + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT MSVC) + # linker 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 () +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (inst-context src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (inst-context PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (inst-context vmlib -lm -ldl -lpthread) +else () + target_link_libraries (inst-context vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/samples/inst-context/README.md b/samples/inst-context/README.md new file mode 100644 index 0000000000..43b13c66bb --- /dev/null +++ b/samples/inst-context/README.md @@ -0,0 +1,4 @@ +The "inst-context" sample project +================================= + +This sample demonstrates module instance context API. diff --git a/samples/inst-context/build.sh b/samples/inst-context/build.sh new file mode 100755 index 0000000000..816e1cc08e --- /dev/null +++ b/samples/inst-context/build.sh @@ -0,0 +1,63 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "#####################build inst-context project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. +make -j ${nproc} +if [ $? != 0 ];then + echo "BUILD_FAIL inst-context exit as $?\n" + exit 2 +fi + +cp -a inst-context ${OUT_DIR} + +echo -e "\n" + +echo "#####################build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# use WAMR SDK to build out the .wasm binary +/opt/wasi-sdk/bin/clang \ + --target=wasm32 -O0 -z stack-size=4096 -Wl,--initial-memory=65536 \ + --sysroot=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot \ + -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--strip-all,--no-entry -nostdlib \ + -Wl,--export=generate_float \ + -Wl,--export=float_to_string \ + -Wl,--export=calculate\ + -Wl,--allow-undefined \ + -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "####################build wasm apps done" diff --git a/samples/inst-context/run.sh b/samples/inst-context/run.sh new file mode 100755 index 0000000000..919ed01666 --- /dev/null +++ b/samples/inst-context/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/inst-context -f out/wasm-apps/testapp.wasm diff --git a/samples/inst-context/src/main.c b/samples/inst-context/src/main.c new file mode 100644 index 0000000000..f5068deffc --- /dev/null +++ b/samples/inst-context/src/main.c @@ -0,0 +1,166 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" +#include "my_context.h" + +int32_t +add_native(int32_t n); +void *my_context_key; +struct my_context my_context; +int my_dtor_called; + +wasm_module_inst_t module_inst = NULL; + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +void +my_context_dtor(wasm_module_inst_t inst, void *ctx) +{ + printf("%s called\n", __func__); + my_dtor_called++; + bh_assert(ctx == &my_context); + bh_assert(inst == module_inst); +} + +int +main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer; + char error_buf[128]; + int opt; + char *wasm_path = NULL; + + wasm_module_t module = NULL; + wasm_exec_env_t exec_env = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + // Define an array of NativeSymbol for the APIs to be exported. + // Note: the array must be static defined since runtime + // will keep it after registration + // For the function signature specifications, goto the link: + // https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md + + static NativeSymbol native_symbols[] = { { "add_native", add_native, "(i)i", + NULL } }; + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + // Native symbols need below registration phase + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + my_context_key = wasm_runtime_create_context_key(my_context_dtor); + if (!my_context_key) { + printf("wasm_runtime_create_context_key failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + my_context.x = 100; + wasm_runtime_set_context(module_inst, my_context_key, &my_context); + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if (!exec_env) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + wasm_function_inst_t func3 = + wasm_runtime_lookup_function(module_inst, "calculate"); + if (!func3) { + printf("The wasm function calculate is not found.\n"); + goto fail; + } + + uint32_t argv3[1] = { 3 }; + if (wasm_runtime_call_wasm(exec_env, func3, 1, argv3)) { + uint32_t result = *(uint32_t *)argv3; + printf("Native finished calling wasm function: calculate, return: %d\n", + result); + bh_assert(result == 103); /* argv3[0] + my_context.x */ + } + else { + printf("call wasm function calculate failed. error: %s\n", + wasm_runtime_get_exception(module_inst)); + goto fail; + } + +fail: + if (exec_env) + wasm_runtime_destroy_exec_env(exec_env); + if (module_inst) { + bh_assert(my_dtor_called == 0); + wasm_runtime_deinstantiate(module_inst); + bh_assert(my_dtor_called == 1); + } + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + if (my_context_key) + wasm_runtime_destroy_context_key(my_context_key); + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/inst-context/src/my_context.h b/samples/inst-context/src/my_context.h new file mode 100644 index 0000000000..db49c1e3b5 --- /dev/null +++ b/samples/inst-context/src/my_context.h @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +struct my_context { + int x; +}; + +extern void *my_context_key; diff --git a/samples/inst-context/src/native_impl.c b/samples/inst-context/src/native_impl.c new file mode 100644 index 0000000000..1254b4a229 --- /dev/null +++ b/samples/inst-context/src/native_impl.c @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "my_context.h" + +int32_t +add_native(wasm_exec_env_t exec_env, int32_t n) +{ + wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env); + struct my_context *ctx = wasm_runtime_get_context(inst, my_context_key); + return n + ctx->x; +} diff --git a/samples/inst-context/wasm-apps/testapp.c b/samples/inst-context/wasm-apps/testapp.c new file mode 100644 index 0000000000..1774dcd070 --- /dev/null +++ b/samples/inst-context/wasm-apps/testapp.c @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +int32_t +add_native(int32_t n); + +int32_t +calculate(int32_t n) +{ + printf("calling into WASM function: %s\n", __FUNCTION__); + return add_native(n); +} diff --git a/samples/linux-perf/CMakeLists.txt b/samples/linux-perf/CMakeLists.txt new file mode 100644 index 0000000000..efcc8c07e8 --- /dev/null +++ b/samples/linux-perf/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(linux_perf_sample) + +if(NOT CMAKE_HOST_LINUX) + message(FATAL_ERROR "This sample only works on linux") +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_STANDARD 17) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake) +find_package(WASISDK REQUIRED) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +include(CheckPIESupported) + +# AOT and JIT byd default +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_INTERP 0) +set(WAMR_BUILD_JIT 1) +# wasm32-wasi +set(WAMR_BUILD_LIBC_BUILTIN 0) +set(WAMR_BUILD_LIBC_WASI 1) +# mvp +set(WAMR_BUILD_BULK_MEMORY 1) +set(WAMR_BUILD_REF_TYPES 1) +set(WAMR_BUILD_SIMD 1) +set(WAMR_BUILD_TAIL_CALL 1) +# trap information +set(WAMR_BUILD_DUMP_CALL_STACK 1) +# linux perf +set(WAMR_BUILD_LINUX_PERF 1) +# +#set(WAMR_BUILD_THREAD_MGR 0) + +# vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +target_include_directories(vmlib INTERFACE ${WAMR_ROOT_DIR}/core/iwasm/include) +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl) + +################ host ################ +add_executable(${PROJECT_NAME} host/demo.c) +target_link_libraries(${PROJECT_NAME} vmlib) + +################ aot + wasm ################ +include(ExternalProject) +ExternalProject_Add(wasm + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm -B build + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) \ No newline at end of file diff --git a/samples/linux-perf/README.md b/samples/linux-perf/README.md new file mode 100644 index 0000000000..0dd25a99e2 --- /dev/null +++ b/samples/linux-perf/README.md @@ -0,0 +1,90 @@ +# linux perf sample introduction + +This is a sample to show how to use the Linux perf tool to profile the execution of a WebAssembly application. And how to use the [Flamegraph](https://www.brendangregg.com/flamegraphs.html) tool to visualize the profiling result. + +## Build and run the sample + +There are two Wasm modules and their instance will be created and run in the sample. [The first module](./wasm/fib.c) is a simple Wasm module that calculates the Fibonacci number. [The second module](./wasm/ackermann.c) is a simple Wasm module that execute the Ackermann function. The target is enable to profile the execution of both two modules separately. + +```bash +$ cmake -S . -B build +$ cmake --build build +``` + +### Profile the execution + +```bash +$ cd build +$ perf record -k mono -g --output=perf.data -- ./linux_perf_sample +``` + +Enable to use `perf report --stdio` to do a quick analysis of the profiling result. + +### Visualize the profiling result + +Need to download Flamegraph tool from [Flamegraph](https://github.com/brendangregg/FlameGraph/releases/tag/v1.0) firstly. + +```bash +$ perf script > out.perf +$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded +$ ./FlameGraph/flamegraph.pl out.folded > perf.svg +``` + +In this result, you'll see two modules' profiling result and all wasm functions are named as "aot_func#N" which is a little hard to distinguish. + +![perf.png](./pics/perf.png) + +### Separate profiling result + +[process_folded_data.py](../../test-tools/flame-graph-helper/process_folded_data.py) is a script can a) translate "aot_func#N" into its original function name in name sections, b) separate the profiling result of different modules. + +In this sample, we want to separate `fib` and `ackermann` profiling data from _out.folded_. In [demo](host/demo.c), we decide to name the module of `fib1.wasm` as `fib2` and the module of `ackermann1.wasm` as `ackermann2`. + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names fib2=./fib1.wasm,ackermann2=./ackermann1.wasm out.folded +-> write into out.fib2.translated +-> write into out.ackermann2.translated +-> write into out.translated +``` + +More scenarios: + +if only using one wasm during profiling, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm --folded +``` + +if only using one wasm during profiling and specify the module name via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names = --folded +``` + +if only using one wasm during profiling and specify the module name, which is same with the basename of wasm file, via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm --folded +``` + +if using multiple wasm during profiling and specify module names, which are same with basename of wasm files, via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm --wasm --wasm --folded +``` + +if using multiple wasm during profiling and specify module names via APIs, the script can be used like this: + +```bash +$ python process_folded_data.py --wabt_home /opt/wabt --wasm_names =,=,= --folded +``` + +Now we have two flame-graphs for two wasm modules: + +![fib.svg](./pics/perf.fib.svg) + +![ackermann.svg](./pics/perf.ackermann.svg) + +## Reference + +- [perf_tune](../../doc/perf_tune.md) diff --git a/samples/linux-perf/cmake/FindWAMRC.cmake b/samples/linux-perf/cmake/FindWAMRC.cmake new file mode 100644 index 0000000000..586263eddd --- /dev/null +++ b/samples/linux-perf/cmake/FindWAMRC.cmake @@ -0,0 +1,14 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(FindPackageHandleStandardArgs) + +find_file(WAMRC_BIN + NAMES wamrc + DOC "search wamrc" + HINTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../wamr-compiler/build + REQUIRED +) + +find_package_handle_standard_args(WAMRC REQUIRED_VARS WAMRC_BIN) +mark_as_advanced(WAMRC_BIN) \ No newline at end of file diff --git a/samples/linux-perf/host/demo.c b/samples/linux-perf/host/demo.c new file mode 100644 index 0000000000..8ea446ed4c --- /dev/null +++ b/samples/linux-perf/host/demo.c @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +/* return a copy of the file stem of a file path */ +static own char * +stem(const char *file_path) +{ + char *base_name = basename(file_path); + char *s = strdup(base_name); + char *dot = strchr(s, '.'); + assert(dot); + *dot = '\0'; + return s; +} + +static void +guest_i32_to_wasm_i32_array(int *args, unsigned argc, wasm_val_t *data, + unsigned datac) +{ + for (unsigned i = 0; i < argc && i < datac; i++) { + memset(&data[i], 0, sizeof(wasm_val_t)); + data[i].kind = WASM_I32; + data[i].of.i32 = args[i]; + } +} + +int +load_run_wasm_file(wasm_engine_t *engine, const char *file_path, int *args, + unsigned argc) +{ + wasm_store_t *store = wasm_store_new(engine); + // Load binary. + printf("Loading binary...\n"); + FILE *file = fopen(file_path, "rb"); + assert(file); + + int ret = fseek(file, 0L, SEEK_END); + assert(ret == 0); + + long file_size = ftell(file); + assert(file_size != -1); + + ret = fseek(file, 0L, SEEK_SET); + assert(ret == 0); + + wasm_byte_vec_t binary = { 0 }; + wasm_byte_vec_new_uninitialized(&binary, file_size); + + size_t nread = fread(binary.data, file_size, 1, file); + fclose(file); + + // Compile. + printf("Compiling module...\n"); + + // Use its file name as the module name + char *file_name = stem(file_path); + assert(file_name); + + LoadArgs load_args = { 0 }; + load_args.name = file_name; + own wasm_module_t *module = wasm_module_new_ex(store, &binary, &load_args); + wasm_byte_vec_delete(&binary); + assert(module); + + // Use export type to find the function index to call later + wasm_exporttype_vec_t export_types = { 0 }; + wasm_module_exports(module, &export_types); + int func_to_call = -1; + for (unsigned i = 0; i < export_types.num_elems; i++) { + const wasm_name_t *name = wasm_exporttype_name(export_types.data[i]); + if (strncmp(name->data, "run", 3) == 0) { + func_to_call = i; + break; + } + } + assert(func_to_call != -1); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t *instance = wasm_instance_new_with_args( + store, module, &imports, NULL, 16 * 1024 * 1024, 1 * 1024 * 1024); + assert(instance); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + assert(exports.size); + + assert(wasm_extern_kind(exports.data[func_to_call]) == WASM_EXTERN_FUNC); + const wasm_func_t *run_func = + wasm_extern_as_func(exports.data[func_to_call]); + assert(run_func); + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t as[4] = { 0 }; + guest_i32_to_wasm_i32_array(args, argc, as, 4); + + wasm_val_vec_t params = WASM_ARRAY_VEC(as); + wasm_val_t rs[1] = { WASM_I32_VAL(0) }; + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(run_func, ¶ms, &results); + assert(!trap); + + wasm_extern_vec_delete(&exports); + free(file_name); + wasm_store_delete(store); + + { + nread = nread; + ret = ret; + trap = trap; + } + return 0; +} + +void * +load_run_fib_wasm(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 40 }; + load_run_wasm_file(engine, "./fib1.wasm", args, 1); + return NULL; +} + +void * +load_run_fib_aot(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 40 }; + load_run_wasm_file(engine, "./fib2.aot", args, 1); + return NULL; +} + +void * +load_run_ackermann_wasm(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 3, 12 }; + load_run_wasm_file(engine, "./ackermann1.wasm", args, 2); + return NULL; +} + +void * +load_run_ackermann_aot(void *arg) +{ + wasm_engine_t *engine = (wasm_engine_t *)arg; + int args[] = { 3, 12 }; + load_run_wasm_file(engine, "./ackermann2.aot", args, 2); + return NULL; +} + +int +main(int argc, const char *argv[]) +{ + // Initialize. + printf("Initializing...\n"); + wasm_config_t *config = wasm_config_new(); + wasm_config_set_linux_perf_opt(config, true); + wasm_engine_t *engine = wasm_engine_new_with_config(config); + + pthread_t tid[4] = { 0 }; + /* FIXME: uncomment when it is able to run two modules with llvm-jit */ + // pthread_create(&tid[0], NULL, load_run_fib_wasm, (void *)engine); + // pthread_create(&tid[2], NULL, load_run_ackermann_wasm, (void *)engine); + + pthread_create(&tid[1], NULL, load_run_fib_aot, (void *)engine); + pthread_create(&tid[3], NULL, load_run_ackermann_aot, (void *)engine); + + for (unsigned i = 0; i < sizeof(tid) / sizeof(tid[0]); i++) + pthread_join(tid[i], NULL); + + // Shut down. + printf("Shutting down...\n"); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/linux-perf/pics/perf.ackermann.svg b/samples/linux-perf/pics/perf.ackermann.svg new file mode 100644 index 0000000000..c2e1d87472 --- /dev/null +++ b/samples/linux-perf/pics/perf.ackermann.svg @@ -0,0 +1,1349 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +load_run_wasm_file (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +invoke_ii_i (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +load_run_ackermann_aot (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] run (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +wasm_runtime_call_wasm (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +all (11,485,868,643 samples, 100%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +aot_call_function (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +linux_perf_samp (11,485,868,643 samples, 100.00%) +linux_perf_samp + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +wasm_func_call (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +invoke_native_with_hw_bound_check (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +start_thread (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (11,484,299,081 samples, 99.99%) +[Wasm] [ackermann2] ackermann + + +[Wasm] [ackermann2] ackermann (1,569,562 samples, 0.01%) + + + + diff --git a/samples/linux-perf/pics/perf.fib.svg b/samples/linux-perf/pics/perf.fib.svg new file mode 100644 index 0000000000..a1db059a7a --- /dev/null +++ b/samples/linux-perf/pics/perf.fib.svg @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + +Flame Graph + +Reset Zoom +Search +ic + + + +[Wasm] [fib2] fibonacci (1,321,095,222 samples, 93.80%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (382,407,564 samples, 27.15%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (15,340,273 samples, 1.09%) + + + +[Wasm] [fib2] fibonacci (1,359,552,763 samples, 96.53%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (27,274,310 samples, 1.94%) +[.. + + +[Wasm] [fib2] fibonacci (62,450,767 samples, 4.43%) +[Wasm.. + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,388,674,508 samples, 98.59%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,170,751,868 samples, 83.12%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (120,820,158 samples, 8.58%) +[Wasm] [fib2.. + + +invoke_i_i (1,408,481,525 samples, 100.00%) +invoke_i_i + + +[Wasm] [fib2] fibonacci (1,375,872,224 samples, 97.68%) +[Wasm] [fib2] fibonacci + + +load_run_wasm_file (1,408,481,525 samples, 100.00%) +load_run_wasm_file + + +load_run_fib_aot (1,408,481,525 samples, 100.00%) +load_run_fib_aot + + +[Wasm] [fib2] run (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] run + + +[Wasm] [fib2] fibonacci (42,420,273 samples, 3.01%) +[Wa.. + + +[Wasm] [fib2] fibonacci (1,266,323,684 samples, 89.91%) +[Wasm] [fib2] fibonacci + + +linux_perf_samp (1,408,481,525 samples, 100.00%) +linux_perf_samp + + +[Wasm] [fib2] fibonacci (280,259,464 samples, 19.90%) +[Wasm] [fib2] fibonacci + + +start_thread (1,408,481,525 samples, 100.00%) +start_thread + + +[Wasm] [fib2] fibonacci (2,334,521 samples, 0.17%) + + + +[Wasm] [fib2] fibonacci (666,394,609 samples, 47.31%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (943,121,736 samples, 66.96%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,169,581 samples, 0.08%) + + + +[Wasm] [fib2] fibonacci (1,169,581 samples, 0.08%) + + + +[Wasm] [fib2] fibonacci (194,755,877 samples, 13.83%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,406,148,966 samples, 99.83%) +[Wasm] [fib2] fibonacci + + +all (1,408,481,525 samples, 100%) + + + +wasm_func_call (1,408,481,525 samples, 100.00%) +wasm_func_call + + +[Wasm] [fib2] fibonacci (5,943,602 samples, 0.42%) + + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +wasm_runtime_call_wasm (1,408,481,525 samples, 100.00%) +wasm_runtime_call_wasm + + +[Wasm] [fib2] fibonacci (1,401,486,191 samples, 99.50%) +[Wasm] [fib2] fibonacci + + +aot_call_function (1,408,481,525 samples, 100.00%) +aot_call_function + + +[Wasm] [fib2] fibonacci (531,941,563 samples, 37.77%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,406,148,966 samples, 99.83%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,061,055,435 samples, 75.33%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,408,481,525 samples, 100.00%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (1,403,816,880 samples, 99.67%) +[Wasm] [fib2] fibonacci + + +[Wasm] [fib2] fibonacci (800,646,766 samples, 56.84%) +[Wasm] [fib2] fibonacci + + +invoke_native_with_hw_bound_check (1,408,481,525 samples, 100.00%) +invoke_native_with_hw_bound_check + + + diff --git a/samples/linux-perf/pics/perf.png b/samples/linux-perf/pics/perf.png new file mode 100755 index 0000000000..fc4c0930fc Binary files /dev/null and b/samples/linux-perf/pics/perf.png differ diff --git a/samples/linux-perf/wasm/CMakeLists.txt b/samples/linux-perf/wasm/CMakeLists.txt new file mode 100644 index 0000000000..8d36e28dfb --- /dev/null +++ b/samples/linux-perf/wasm/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(linux_perf_sample_wasm) + +include(CMakePrintHelpers) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake) +find_package(WAMRC REQUIRED) + +################ wasm ################ +add_executable(fib_wasm fib.c) +set_target_properties(fib_wasm PROPERTIES SUFFIX .wasm) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fib_wasm.wasm DESTINATION . RENAME fib1.wasm) + +add_executable(ackermann_wasm ackermann.c) +set_target_properties(ackermann_wasm PROPERTIES SUFFIX .wasm) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ackermann_wasm.wasm DESTINATION . RENAME ackermann1.wasm) + + +################ aot ################ +add_custom_target(fib_aot + ALL + COMMAND ${WAMRC_BIN} --enable-linux-perf -o fib2.aot fib_wasm.wasm + DEPENDS fib_wasm + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fib2.aot DESTINATION .) + +add_custom_target(ackermann_aot + ALL + COMMAND ${WAMRC_BIN} --enable-linux-perf -o ackermann2.aot ackermann_wasm.wasm + DEPENDS ackermann_wasm + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ackermann2.aot DESTINATION .) diff --git a/samples/linux-perf/wasm/ackermann.c b/samples/linux-perf/wasm/ackermann.c new file mode 100644 index 0000000000..d6eb4346ff --- /dev/null +++ b/samples/linux-perf/wasm/ackermann.c @@ -0,0 +1,38 @@ +#include + +// Ackermann function +unsigned long +ackermann(unsigned long m, unsigned long n) +{ + if (m == 0) { + return n + 1; + } + else if (n == 0) { + return ackermann(m - 1, 1); + } + else { + return ackermann(m - 1, ackermann(m, n - 1)); + } +} + +__attribute__((export_name("run"))) int +run(int m, int n) +{ + int result = ackermann(m, n); + printf("ackermann(%d, %d)=%d\n", m, n, result); + return result; +} + +int +main() +{ + unsigned long m, n, result; + + // Example usage: + m = 3; + n = 2; + result = ackermann(m, n); + printf("Ackermann(%lu, %lu) = %lu\n", m, n, result); + + return 0; +} diff --git a/samples/linux-perf/wasm/fib.c b/samples/linux-perf/wasm/fib.c new file mode 100644 index 0000000000..cb928f65d0 --- /dev/null +++ b/samples/linux-perf/wasm/fib.c @@ -0,0 +1,32 @@ +#include +#include + +int +fibonacci(int n) +{ + if (n <= 0) + return 0; + + if (n == 1) + return 1; + + return fibonacci(n - 1) + fibonacci(n - 2); +} + +__attribute__((export_name("run"))) int +run(int n) +{ + int result = fibonacci(n); + printf("fibonacci(%d)=%d\n", n, result); + return result; +} + +int +main(int argc, char **argv) +{ + int n = atoi(argv[1]); + + printf("fibonacci(%d)=%d\n", n, fibonacci(n)); + + return 0; +} diff --git a/samples/littlevgl/LICENCE.txt b/samples/littlevgl/LICENCE.txt deleted file mode 100644 index beaef1d26e..0000000000 --- a/samples/littlevgl/LICENCE.txt +++ /dev/null @@ -1,8 +0,0 @@ -MIT licence -Copyright (c) 2016 Gábor Kiss-Vámosi - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/samples/littlevgl/README.md b/samples/littlevgl/README.md deleted file mode 100644 index 2859e556a3..0000000000 --- a/samples/littlevgl/README.md +++ /dev/null @@ -1,134 +0,0 @@ -"littlevgl" sample introduction -============== -This sample demonstrates that a graphic user interface application in WebAssembly by compiling the LittlevGL, an open-source embedded 2d graphic library into the WASM bytecode. - -In this sample, the whole LittlevGL source code is built into the WebAssembly code with the user application. The platform interfaces defined by LittlevGL is implemented in the runtime and exported to the application through the declarations from source "ext_lib_export.c" as below: - - EXPORT_WASM_API(display_init), - EXPORT_WASM_API(display_input_read), - EXPORT_WASM_API(display_flush), - EXPORT_WASM_API(display_fill), - EXPORT_WASM_API(display_vdb_write), - EXPORT_WASM_API(display_map), - EXPORT_WASM_API(time_get_ms), - -The runtime component supports building target for Linux and Zephyr/STM Nucleo board. The beauty of this sample is the WebAssembly application can have identical display and behavior when running from both runtime environments. That implies we can do majority of application validation from desktop environment as long as two runtime distributions support the same set of application interface. - - -Below pictures show the WASM application is running on an STM board with an LCD touch panel. - -![WAMR UI SAMPLE](../../doc/pics/vgl_demo2.png "WAMR UI DEMO STM32") - -![WAMR UI SAMPLE](../../doc/pics/vgl_demo_linux.png "WAMR UI DEMO LINUX") - - -The number on top will plus one each second, and the number on the bottom will plus one when clicked. When users click the blue button, the WASM application increases the counter, and the latest counter value is displayed on the top banner of the touch panel. - -The sample also provides the native Linux version of application without the runtime under folder "vgl-native-ui-app". It can help to check differences between the implementations in native and WebAssembly. - -Test on Linux -================================ - -Install required SDK and libraries --------------- -- 32 bit SDL(simple directmedia layer) (Note: only necessary when `WAMR_BUILD_TARGET` is set to `X86_32` when building WAMR runtime) -Use apt-get: - `sudo apt-get install libsdl2-dev:i386` -Or download source from www.libsdl.org: -``` -./configure C_FLAGS=-m32 CXX_FLAGS=-m32 LD_FLAGS=-m32 -make -sudo make install -``` -- 64 bit SDL(simple directmedia layer) (Note: only necessary when `WAMR_BUILD_TARGET` is set to `X86_64` when building WAMR runtime) -Use apt-get: - `sudo apt-get install libsdl2-dev` -Or download source from www.libsdl.org: -``` -./configure -make -sudo make install -``` - - -Build and Run --------------- - -- Build
-`./build.sh`
- All binaries are in "out", which contains "host_tool", "vgl_native_ui_app", "ui_app.wasm" "ui_app_no_wasi.wasm "and "vgl_wasm_runtime". -- Run native Linux application
-`./vgl_native_ui_app`
- -- Run WASM VM Linux applicaton & install WASM APP
- First start vgl_wasm_runtime in server mode.
-`./vgl_wasm_runtime -s`
- Then install wasm APP use host tool.
-`./host_tool -i ui_app -f ui_app.wasm`
- -Test on Zephyr -================================ -We can use a STM32 NUCLEO_F767ZI board with ILI9341 display and XPT2046 touch screen to run the test. Then use host_tool to remotely install wasm app into STM32. -- Build WASM VM into Zephyr system
- a. clone zephyr source code
-Refer to Zephyr getting started.
-https://docs.zephyrproject.org/latest/getting_started/index.html
-`west init zephyrproject`
-`cd zephyrproject`
-`west update`
- b. copy samples
- `cd zephyr/samples/`
- `cp -a samples/littlevgl/vgl-wasm-runtime vgl-wasm-runtime`
- `cd vgl-wasm-runtime/zephyr_build`
- c. create a link to wamr root dir
- ` ln -s wamr`
- d. build source code
- Since ui_app incorporated LittlevGL source code, so it needs more RAM on the device to install the application. - It is recommended that RAM SIZE not less than 420KB. - In our test use nucleo_f767zi, which is not supported by Zephyr. - However, nucleo_f767zi is almost the same as nucleo_f746zg, except FLASH and SRAM size. - So we changed the DTS setting of nucleo_f746zg boards for a workaround.
- - `Modify zephyr/dts/arm/st/f7/stm32f746.dtsi, change DT_SIZE_K(256) to DT_SIZE_K(512) in 'sram0' definition.`
- `mkdir build && cd build`
- `source ../../../../zephyr-env.sh`
- `cmake -GNinja -DBOARD=nucleo_f746zg ..`
- ` ninja flash`
- -- Hardware Connections - -``` -+-------------------+-+------------------+ -|NUCLEO-F767ZI | ILI9341 Display | -+-------------------+-+------------------+ -| CN7.10 | CLK | -+-------------------+-+------------------+ -| CN7.12 | MISO | -+-------------------+-+------------------+ -| CN7.14 | MOSI | -+-------------------+-+------------------+ -| CN11.1 | CS1 for ILI9341 | -+-------------------+-+------------------+ -| CN11.2 | D/C | -+-------------------+-+------------------+ -| CN11.3 | RESET | -+-------------------+-+------------------+ -| CN9.25 | PEN interrupt | -+-------------------+-+------------------+ -| CN9.27 | CS2 for XPT2046 | -+-------------------+-+------------------+ -| CN10.14 | PC UART RX | -+-------------------+-+------------------+ -| CN11.16 | PC UART RX | -+-------------------+-+------------------+ -``` - - -- Install WASM application to Zephyr using host_tool
-First, connect PC and STM32 with UART. Then install to use host_tool.
-`./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app_no_wasi.wasm` -**Note**: WASI is unavailable on zephyr currently, so you have to use the ui_app_no_wasi.wasm which doesn't depend on WASI. - -- Install AOT version WASM application -`wamrc --target=thumbv7 --target-abi=eabi --cpu=cortex-m7 -o ui_app_no_wasi.aot ui_app_no_wasi.wasm` -`./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app_no_wasi.aot` diff --git a/samples/littlevgl/build.sh b/samples/littlevgl/build.sh deleted file mode 100755 index d1f596da56..0000000000 --- a/samples/littlevgl/build.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -PROJECT_DIR=$PWD -WAMR_DIR=${PWD}/../.. -OUT_DIR=${PWD}/out -BUILD_DIR=${PWD}/build - -if [ -z $KW_BUILD ] || [ -z $KW_OUT_FILE ];then - echo "Local Build Env" - cmakewrap="cmake" - makewrap="make" -else - echo "Klocwork Build Env" - cmakewrap="cmake -DCMAKE_BUILD_TYPE=Debug" - makewrap="kwinject -o $KW_OUT_FILE make" -fi - -if [ ! -d $BUILD_DIR ]; then - mkdir ${BUILD_DIR} -fi - -rm -rf ${OUT_DIR} -mkdir ${OUT_DIR} - - -cd ${WAMR_DIR}/core/shared/mem-alloc -if [ ! -d "tlsf" ]; then - git clone https://github.com/mattconte/tlsf -fi - -cd ${WAMR_DIR}/core/deps -if [ ! -d "lvgl" ]; then - git clone https://github.com/littlevgl/lvgl.git --branch v6.0.1 -fi - -echo "##################### 0. build wamr-sdk littlevgl start#####################" -cd ${WAMR_DIR}/wamr-sdk -./build_sdk.sh -n littlevgl -x ${PROJECT_DIR}/wamr_config_littlevgl.cmake -echo "#####################build wamr-sdk littlevgl success" - -echo -e "\n\n" -echo "##################### 1. build native-ui-app start#####################" -cd $BUILD_DIR -mkdir -p vgl-native-ui-app -cd vgl-native-ui-app -$cmakewrap ${PROJECT_DIR}/vgl-native-ui-app -$makewrap -if [ $? != 0 ];then - echo "BUILD_FAIL native-ui-app $?\n" - exit 2 -fi -echo $PWD -cp vgl_native_ui_app ${OUT_DIR} -echo "#####################build native-ui-app success" - -echo -e "\n\n" -echo "##################### 2. build littlevgl wasm runtime start#####################" -cd $BUILD_DIR -mkdir -p vgl-wasm-runtime -cd vgl-wasm-runtime -$cmakewrap ${PROJECT_DIR}/vgl-wasm-runtime -$makewrap -cp vgl_wasm_runtime ${OUT_DIR}/ - -echo "##################### build littlevgl wasm runtime end#####################" - -echo -e "\n\n" -echo "#####################build host-tool" -cd $BUILD_DIR -mkdir -p host-tool -cd host-tool -$cmakewrap ${WAMR_DIR}/test-tools/host-tool -$makewrap -if [ $? != 0 ];then - echo "BUILD_FAIL host tool exit as $?\n" - exit 2 -fi -cp host_tool ${OUT_DIR} -echo "#####################build host-tool success" - -echo -e "\n\n" -echo "##################### 3. build wasm ui app start#####################" -cd ${PROJECT_DIR}/wasm-apps -if [ ! -d "${PROJECT_DIR}/wasm-apps/lvgl" ]; then - if [ -d "$BUILD_DIR/vgl-native-ui-app/lvgl" ]; then - cp -fr $BUILD_DIR/vgl-native-ui-app/lvgl ${PROJECT_DIR}/wasm-apps - fi -fi -./build_wasm_app.sh -mv ui_app.wasm ${OUT_DIR}/ -mv ui_app_no_wasi.wasm ${OUT_DIR}/ -rm -fr ${PROJECT_DIR}/wasm-apps/lvgl -echo "##################### build wasm ui app end#####################" diff --git a/samples/littlevgl/lv_config/lv_conf.h b/samples/littlevgl/lv_config/lv_conf.h deleted file mode 100644 index 76533a8e1b..0000000000 --- a/samples/littlevgl/lv_config/lv_conf.h +++ /dev/null @@ -1,389 +0,0 @@ -/** - * @file lv_conf.h - * - */ - -#if 1 /*Set it to "1" to enable content*/ - -#ifndef LV_CONF_H -#define LV_CONF_H -/*=================== - Dynamic memory - *===================*/ - -/* Memory size which will be used by the library - * to store the graphical objects and other data */ -#define LV_MEM_CUSTOM 1 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/ -#if LV_MEM_CUSTOM == 0 -# define LV_MEM_SIZE (64U * 1024U) /*Size memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ -# define LV_MEM_ATTR /*Complier prefix for big array declaration*/ -# define LV_MEM_ADR 0 /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/ -# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/ -#else /*LV_MEM_CUSTOM*/ -# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ -# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/ -# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/ -#endif /*LV_MEM_CUSTOM*/ - -/* Garbage Collector settings - * Used if lvgl is binded to higher language and the memory is managed by that language */ -#define LV_ENABLE_GC 0 -#if LV_ENABLE_GC != 0 -# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/ -# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/ -# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ -#endif /* LV_ENABLE_GC */ - -/*=================== - Graphical settings - *===================*/ - -/* Horizontal and vertical resolution of the library.*/ -#define LV_HOR_RES (320) -#define LV_VER_RES (240) - -/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide - * (Not so important, you can adjust it to modify default sizes and spaces)*/ -#define LV_DPI 100 - -/* Enable anti-aliasing (lines, and radiuses will be smoothed) */ -#define LV_ANTIALIAS 0 /*1: Enable anti-aliasing*/ - -/*Screen refresh period in milliseconds*/ -#define LV_REFR_PERIOD 30 - -/*----------------- - * VDB settings - *----------------*/ - -/* VDB (Virtual Display Buffer) is an internal graphics buffer. - * The GUI will be drawn into this buffer first and then - * the buffer will be passed to your `disp_drv.disp_flush` function to - * copy it to your frame buffer. - * VDB is required for: buffered drawing, opacity, anti-aliasing and shadows - * Learn more: https://docs.littlevgl.com/#Drawing*/ - -/* Size of the VDB in pixels. Typical size: ~1/10 screen. Must be >= LV_HOR_RES - * Setting it to 0 will disable VDB and `disp_drv.disp_fill` and `disp_drv.disp_map` functions - * will be called to draw to the frame buffer directly*/ -#define LV_VDB_SIZE ((LV_VER_RES * LV_HOR_RES) / 10) - -/* Bit-per-pixel of VDB. Useful for monochrome or non-standard color format displays. - * Special formats are handled with `disp_drv.vdb_wr`)*/ -#define LV_VDB_PX_BPP LV_COLOR_SIZE /*LV_COLOR_SIZE comes from LV_COLOR_DEPTH below to set 8, 16 or 32 bit pixel size automatically */ - -/* Place VDB to a specific address (e.g. in external RAM) - * 0: allocate automatically into RAM - * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/ -#define LV_VDB_ADR 0 - -/* Use two Virtual Display buffers (VDB) to parallelize rendering and flushing - * The flushing should use DMA to write the frame buffer in the background */ -#define LV_VDB_DOUBLE 0 - -/* Place VDB2 to a specific address (e.g. in external RAM) - * 0: allocate automatically into RAM - * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/ -#define LV_VDB2_ADR 0 - -/* Using true double buffering in `disp_drv.disp_flush` you will always get the image of the whole screen. - * Your only task is to set the rendered image (`color_p` parameter) as frame buffer address or send it to your display. - * The best if you do in the blank period of you display to avoid tearing effect. - * Requires: - * - LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES - * - LV_VDB_DOUBLE = 1 - */ -#define LV_VDB_TRUE_DOUBLE_BUFFERED 0 - -/*================= - Misc. setting - *=================*/ - -/*Input device settings*/ -#define LV_INDEV_READ_PERIOD 50 /*Input device read period in milliseconds*/ -#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: USE_LV_REAL_DRAW = 1)*/ -#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */ -#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */ -#define LV_INDEV_LONG_PRESS_TIME 400 /*Long press time in milliseconds*/ -#define LV_INDEV_LONG_PRESS_REP_TIME 100 /*Repeated trigger period in long press [ms] */ - -/*Color settings*/ -#define LV_COLOR_DEPTH 32 /*Color depth: 1/8/16/32*/ -#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/ -#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/ -#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/ - -/*Text settings*/ -#define LV_TXT_UTF8 1 /*Enable UTF-8 coded Unicode character usage */ -#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/ -#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */ -#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */ -#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */ - -/*Feature usage*/ -#define USE_LV_ANIMATION 1 /*1: Enable all animations*/ -#define USE_LV_SHADOW 1 /*1: Enable shadows*/ -#define USE_LV_GROUP 1 /*1: Enable object groups (for keyboards)*/ -#define USE_LV_GPU 0 /*1: Enable GPU interface*/ -#define USE_LV_REAL_DRAW 1 /*1: Enable function which draw directly to the frame buffer instead of VDB (required if LV_VDB_SIZE = 0)*/ -#define USE_LV_FILESYSTEM 0 /*1: Enable file system (might be required for images*/ -#define USE_LV_MULTI_LANG 0 /* Number of languages for labels to store (0: to disable this feature)*/ - -/*Compiler settings*/ -#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */ -#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */ -#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/ -#define LV_COMPILER_NON_CONST_INIT_SUPPORTED 1 /* 1: Initialization with non constant values are supported */ - -/*HAL settings*/ -#define LV_TICK_CUSTOM 1 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */ -#if LV_TICK_CUSTOM == 1 -#define LV_TICK_CUSTOM_INCLUDE "system_header.h" /*Header for the sys time function*/ -#define LV_TICK_CUSTOM_SYS_TIME_EXPR (time_get_ms()) /*Expression evaluating to current systime in ms*/ -#endif /*LV_TICK_CUSTOM*/ - -/*Log settings*/ -#define USE_LV_LOG 1 /*Enable/disable the log module*/ -#if USE_LV_LOG -/* How important log should be added: - * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information - * LV_LOG_LEVEL_INFO Log important events - * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't caused problem - * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail - */ -# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN -/* 1: Print the log with 'printf'; 0: user need to register a callback*/ - -# define LV_LOG_PRINTF 0 -#endif /*USE_LV_LOG*/ - -/*================ - * THEME USAGE - *================*/ -#define LV_THEME_LIVE_UPDATE 1 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/ - -#define USE_LV_THEME_TEMPL 0 /*Just for test*/ -#define USE_LV_THEME_DEFAULT 1 /*Built mainly from the built-in styles. Consumes very few RAM*/ -#define USE_LV_THEME_ALIEN 0 /*Dark futuristic theme*/ -#define USE_LV_THEME_NIGHT 0 /*Dark elegant theme*/ -#define USE_LV_THEME_MONO 0 /*Mono color theme for monochrome displays*/ -#define USE_LV_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/ -#define USE_LV_THEME_ZEN 0 /*Peaceful, mainly light theme */ -#define USE_LV_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/ - -/*================== - * FONT USAGE - *===================*/ - -/* More info about fonts: https://docs.littlevgl.com/#Fonts - * To enable a built-in font use 1,2,4 or 8 values - * which will determine the bit-per-pixel. Higher value means smoother fonts */ -#define USE_LV_FONT_DEJAVU_10 0 -#define USE_LV_FONT_DEJAVU_10_LATIN_SUP 0 -#define USE_LV_FONT_DEJAVU_10_CYRILLIC 0 -#define USE_LV_FONT_SYMBOL_10 0 - -#define USE_LV_FONT_DEJAVU_20 4 -#define USE_LV_FONT_DEJAVU_20_LATIN_SUP 0 -#define USE_LV_FONT_DEJAVU_20_CYRILLIC 0 -#define USE_LV_FONT_SYMBOL_20 0 - -#define USE_LV_FONT_DEJAVU_30 0 -#define USE_LV_FONT_DEJAVU_30_LATIN_SUP 0 -#define USE_LV_FONT_DEJAVU_30_CYRILLIC 0 -#define USE_LV_FONT_SYMBOL_30 0 - -#define USE_LV_FONT_DEJAVU_40 0 -#define USE_LV_FONT_DEJAVU_40_LATIN_SUP 0 -#define USE_LV_FONT_DEJAVU_40_CYRILLIC 0 -#define USE_LV_FONT_SYMBOL_40 0 - -#define USE_LV_FONT_MONOSPACE_8 1 - -/* Optionally declare your custom fonts here. - * You can use these fonts as default font too - * and they will be available globally. E.g. - * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \ - * LV_FONT_DECLARE(my_font_2) \ - */ -#define LV_FONT_CUSTOM_DECLARE - -#define LV_FONT_DEFAULT &lv_font_dejavu_20 /*Always set a default font from the built-in fonts*/ - -/*=================== - * LV_OBJ SETTINGS - *==================*/ -#define LV_OBJ_FREE_NUM_TYPE uint32_t /*Type of free number attribute (comment out disable free number)*/ -#define LV_OBJ_FREE_PTR 1 /*Enable the free pointer attribute*/ -#define LV_OBJ_REALIGN 1 /*Enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/ - -/*================== - * LV OBJ X USAGE - *================*/ -/* - * Documentation of the object types: https://docs.littlevgl.com/#Object-types - */ - -/***************** - * Simple object - *****************/ - -/*Label (dependencies: -*/ -#define USE_LV_LABEL 1 -#if USE_LV_LABEL != 0 -# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/ -#endif - -/*Image (dependencies: lv_label*/ -#define USE_LV_IMG 1 -#if USE_LV_IMG != 0 -# define LV_IMG_CF_INDEXED 1 /*Enable indexed (palette) images*/ -# define LV_IMG_CF_ALPHA 1 /*Enable alpha indexed images*/ -#endif - -/*Line (dependencies: -*/ -#define USE_LV_LINE 1 - -/*Arc (dependencies: -)*/ -#define USE_LV_ARC 1 - -/******************* - * Container objects - *******************/ - -/*Container (dependencies: -*/ -#define USE_LV_CONT 1 - -/*Page (dependencies: lv_cont)*/ -#define USE_LV_PAGE 1 - -/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/ -#define USE_LV_WIN 1 - -/*Tab (dependencies: lv_page, lv_btnm)*/ -#define USE_LV_TABVIEW 1 -# if USE_LV_TABVIEW != 0 -# define LV_TABVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/ -#endif - -/*Tileview (dependencies: lv_page) */ -#define USE_LV_TILEVIEW 1 -#if USE_LV_TILEVIEW -# define LV_TILEVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/ -#endif - -/************************* - * Data visualizer objects - *************************/ - -/*Bar (dependencies: -)*/ -#define USE_LV_BAR 1 - -/*Line meter (dependencies: *;)*/ -#define USE_LV_LMETER 1 - -/*Gauge (dependencies:lv_bar, lv_lmeter)*/ -#define USE_LV_GAUGE 1 - -/*Chart (dependencies: -)*/ -#define USE_LV_CHART 1 - -/*Table (dependencies: lv_label)*/ -#define USE_LV_TABLE 1 -#if USE_LV_TABLE -# define LV_TABLE_COL_MAX 12 -#endif - -/*LED (dependencies: -)*/ -#define USE_LV_LED 1 - -/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/ -#define USE_LV_MBOX 1 - -/*Text area (dependencies: lv_label, lv_page)*/ -#define USE_LV_TA 1 -#if USE_LV_TA != 0 -# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/ -# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/ -#endif - -/*Spinbox (dependencies: lv_ta)*/ -#define USE_LV_SPINBOX 1 - -/*Calendar (dependencies: -)*/ -#define USE_LV_CALENDAR 1 - -/*Preload (dependencies: lv_arc)*/ -#define USE_LV_PRELOAD 1 -#if USE_LV_PRELOAD != 0 -# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/ -# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/ -# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC -#endif - -/*Canvas (dependencies: lv_img)*/ -#define USE_LV_CANVAS 1 -/************************* - * User input objects - *************************/ - -/*Button (dependencies: lv_cont*/ -#define USE_LV_BTN 1 -#if USE_LV_BTN != 0 -# define LV_BTN_INK_EFFECT 1 /*Enable button-state animations - draw a circle on click (dependencies: USE_LV_ANIMATION)*/ -#endif - -/*Image Button (dependencies: lv_btn*/ -#define USE_LV_IMGBTN 1 -#if USE_LV_IMGBTN -# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/ -#endif - -/*Button matrix (dependencies: -)*/ -#define USE_LV_BTNM 1 - -/*Keyboard (dependencies: lv_btnm)*/ -#define USE_LV_KB 1 - -/*Check box (dependencies: lv_btn, lv_label)*/ -#define USE_LV_CB 1 - -/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/ -#define USE_LV_LIST 1 -#if USE_LV_LIST != 0 -# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */ -#endif - -/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ -#define USE_LV_DDLIST 1 -#if USE_LV_DDLIST != 0 -# define LV_DDLIST_ANIM_TIME 200 /*Open and close default animation time [ms] (0: no animation)*/ -#endif - -/*Roller (dependencies: lv_ddlist)*/ -#define USE_LV_ROLLER 1 -#if USE_LV_ROLLER != 0 -# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/ -#endif - -/*Slider (dependencies: lv_bar)*/ -#define USE_LV_SLIDER 1 - -/*Switch (dependencies: lv_slider)*/ -#define USE_LV_SW 1 - -/************************* - * Non-user section - *************************/ -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/ -# define _CRT_SECURE_NO_WARNINGS -#endif - -/*--END OF LV_CONF_H--*/ - -/*Be sure every define has a default value*/ -#include "lvgl/lv_conf_checker.h" - -#endif /*LV_CONF_H*/ - -#endif /*End of "Content enable"*/ diff --git a/samples/littlevgl/lv_config/lv_drv_conf.h b/samples/littlevgl/lv_config/lv_drv_conf.h deleted file mode 100644 index d216a3e907..0000000000 --- a/samples/littlevgl/lv_config/lv_drv_conf.h +++ /dev/null @@ -1,310 +0,0 @@ -/** - * @file lv_drv_conf.h - * - */ - -/* - * COPY THIS FILE AS lv_drv_conf.h - */ - -#if 1 /*Set it to "1" to enable the content*/ - -#ifndef LV_DRV_CONF_H -#define LV_DRV_CONF_H - -#include "lv_conf.h" - -/********************* - * DELAY INTERFACE - *********************/ -#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ -#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ -#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ - -/********************* - * DISPLAY INTERFACE - *********************/ - -/*------------ - * Common - *------------*/ -#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ -#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ -#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ - -/*--------- - * SPI - *---------*/ -#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ -#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ -#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ - -/*------------------ - * Parallel port - *-----------------*/ -#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ -#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ -#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ -#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ -#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ - -/*************************** - * INPUT DEVICE INTERFACE - ***************************/ - -/*---------- - * Common - *----------*/ -#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ -#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ -#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ - -/*--------- - * SPI - *---------*/ -#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ -#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ - -/*--------- - * I2C - *---------*/ -#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ -#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ -#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ -#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ -#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ - - -/********************* - * DISPLAY DRIVERS - *********************/ - -/*------------------- - * Monitor of PC - *-------------------*/ -#ifndef USE_MONITOR -# define USE_MONITOR 1 -#endif - -#if USE_MONITOR -# define MONITOR_HOR_RES LV_HOR_RES_MAX -# define MONITOR_VER_RES LV_VER_RES_MAX - -/* Scale window by this factor (useful when simulating small screens) */ -# define MONITOR_ZOOM 1 - -/* Used to test true double buffering with only address changing. - * Set LV_VDB_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_VDB_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */ -# define MONITOR_DOUBLE_BUFFERED 0 - -/*Eclipse: Visual Studio: */ -# define MONITOR_SDL_INCLUDE_PATH - -/*Different rendering might be used if running in a Virtual machine*/ -# define MONITOR_VIRTUAL_MACHINE 0 - -/*Open two windows to test multi display support*/ -# define MONITOR_DUAL 0 -#endif - -/*----------------------------------- - * Native Windows (including mouse) - *----------------------------------*/ -#ifndef USE_WINDOWS -# define USE_WINDOWS 0 -#endif - -#define USE_WINDOWS 0 -#if USE_WINDOWS -# define WINDOW_HOR_RES 480 -# define WINDOW_VER_RES 320 -#endif - -/*---------------- - * SSD1963 - *--------------*/ -#ifndef USE_SSD1963 -# define USE_SSD1963 0 -#endif - -#if USE_SSD1963 -# define SSD1963_HOR_RES LV_HOR_RES -# define SSD1963_VER_RES LV_VER_RES -# define SSD1963_HT 531 -# define SSD1963_HPS 43 -# define SSD1963_LPS 8 -# define SSD1963_HPW 10 -# define SSD1963_VT 288 -# define SSD1963_VPS 12 -# define SSD1963_FPS 4 -# define SSD1963_VPW 10 -# define SSD1963_HS_NEG 0 /*Negative hsync*/ -# define SSD1963_VS_NEG 0 /*Negative vsync*/ -# define SSD1963_ORI 0 /*0, 90, 180, 270*/ -# define SSD1963_COLOR_DEPTH 16 -#endif - -/*---------------- - * R61581 - *--------------*/ -#ifndef USE_R61581 -# define USE_R61581 0 -#endif - -#if USE_R61581 -# define R61581_HOR_RES LV_HOR_RES -# define R61581_VER_RES LV_VER_RES -# define R61581_HSPL 0 /*HSYNC signal polarity*/ -# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ -# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ -# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ -# define R61581_VSPL 0 /*VSYNC signal polarity*/ -# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ -# define R61581_VFP 8 /*Vertical Front poarch*/ -# define R61581_VBP 8 /*Vertical Back poarch */ -# define R61581_DPL 0 /*DCLK signal polarity*/ -# define R61581_EPL 1 /*ENABLE signal polarity*/ -# define R61581_ORI 0 /*0, 180*/ -# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ -#endif - -/*------------------------------ - * ST7565 (Monochrome, low res.) - *-----------------------------*/ -#ifndef USE_ST7565 -# define USE_ST7565 0 -#endif - -#if USE_ST7565 -/*No settings*/ -#endif /*USE_ST7565*/ - -/*----------------------------------------- - * Linux frame buffer device (/dev/fbx) - *-----------------------------------------*/ -#ifndef USE_FBDEV -# define USE_FBDEV 1 -#endif - -#if USE_FBDEV -# define FBDEV_PATH "/dev/fb0" -#endif - -/********************* - * INPUT DEVICES - *********************/ - -/*-------------- - * XPT2046 - *--------------*/ -#ifndef USE_XPT2046 -# define USE_XPT2046 0 -#endif - -#if USE_XPT2046 -# define XPT2046_HOR_RES 480 -# define XPT2046_VER_RES 320 -# define XPT2046_X_MIN 200 -# define XPT2046_Y_MIN 200 -# define XPT2046_X_MAX 3800 -# define XPT2046_Y_MAX 3800 -# define XPT2046_AVG 4 -# define XPT2046_INV 0 -#endif - -/*----------------- - * FT5406EE8 - *-----------------*/ -#ifndef USE_FT5406EE8 -# define USE_FT5406EE8 0 -#endif - -#if USE_FT5406EE8 -# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ -#endif - -/*--------------- - * AD TOUCH - *--------------*/ -#ifndef USE_AD_TOUCH -# define USE_AD_TOUCH 0 -#endif - -#if USE_AD_TOUCH -/*No settings*/ -#endif - - -/*--------------------------------------- - * Mouse or touchpad on PC (using SDL) - *-------------------------------------*/ -#ifndef USE_MOUSE -# define USE_MOUSE 1 -#endif - -#if USE_MOUSE -/*No settings*/ -#endif - -/*------------------------------------------- - * Mousewheel as encoder on PC (using SDL) - *------------------------------------------*/ -#ifndef USE_MOUSEWHEEL -# define USE_MOUSEWHEEL 1 -#endif - -#if USE_MOUSEWHEEL -/*No settings*/ -#endif - -/*------------------------------------------------- - * Touchscreen as libinput interface (for Linux based systems) - *------------------------------------------------*/ -#ifndef USE_LIBINPUT -# define USE_LIBINPUT 0 -#endif - -#if USE_LIBINPUT -# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ -#endif /*USE_LIBINPUT*/ - -/*------------------------------------------------- - * Mouse or touchpad as evdev interface (for Linux based systems) - *------------------------------------------------*/ -#ifndef USE_EVDEV -# define USE_EVDEV 0 -#endif - -#if USE_EVDEV -# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ -# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ - -# define EVDEV_SCALE 0 /* Scale input, e.g. if touchscreen resolution does not match display resolution */ -# if EVDEV_SCALE -# define EVDEV_SCALE_HOR_RES (4096) /* Horizontal resolution of touchscreen */ -# define EVDEV_SCALE_VER_RES (4096) /* Vertical resolution of touchscreen */ -# endif /*EVDEV_SCALE*/ - -# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ -# if EVDEV_CALIBRATE -# define EVDEV_HOR_MIN 3800 /*If EVDEV_XXX_MIN > EVDEV_XXX_MAX the XXX axis is automatically inverted*/ -# define EVDEV_HOR_MAX 200 -# define EVDEV_VER_MIN 200 -# define EVDEV_VER_MAX 3800 -# endif /*EVDEV_SCALE*/ -#endif /*USE_EVDEV*/ - -/*------------------------------- - * Keyboard of a PC (using SDL) - *------------------------------*/ -#ifndef USE_KEYBOARD -# define USE_KEYBOARD 1 -#endif - -#if USE_KEYBOARD -/*No settings*/ -#endif - -#endif /*LV_DRV_CONF_H*/ - -#endif /*End of "Content enable"*/ diff --git a/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt b/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt deleted file mode 100644 index 4a49c0b17c..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required (VERSION 2.8.2) -message ("vgl_native_ui_app...") -project (vgl_native_ui_app) - - -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPLATFORM_NATIVE_LINUX -DUSE_MONITOR -DUSE_MOUSE=1") - -# Currently build as 64-bit by default. Set to "NO" to build 32-bit binaries. -set (BUILD_AS_64BIT_SUPPORT "YES") - -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - if (${BUILD_AS_64BIT_SUPPORT} STREQUAL "YES") - # Add -fPIC flag if build as 64-bit - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") - set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") - else () - add_definitions (-m32) - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") - endif () -endif () - -set(lv_name lvgl) -set(LVGL_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${lv_name}) -set(LVGL_DRIVER_DIR ${CMAKE_CURRENT_LIST_DIR}/lv-drivers) - -message(${LVGL_SOURCE_DIR}) -include( ExternalProject ) - -add_definitions(-DLV_CONF_INCLUDE_SIMPLE) - - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt.in ${CMAKE_CURRENT_BINARY_DIR}/download_lvgl/CMakeLists.txt) - -execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/download_lvgl ) -if(result) - message(FATAL_ERROR "CMake step for lvgl failed: ${result}") -endif() -execute_process(COMMAND ${CMAKE_COMMAND} --build . - RESULT_VARIABLE result - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/download_lvgl ) -if(result) - message(FATAL_ERROR "Build step for lvgl failed: ${result}") -endif() -SET (LVGL_SOURCES - ${LVGL_SOURCE_DIR}/lv_core/lv_group.c - ${LVGL_SOURCE_DIR}/lv_core/lv_indev.c - ${LVGL_SOURCE_DIR}/lv_core/lv_lang.c - ${LVGL_SOURCE_DIR}/lv_core/lv_obj.c - ${LVGL_SOURCE_DIR}/lv_core/lv_refr.c - ${LVGL_SOURCE_DIR}/lv_core/lv_style.c - ${LVGL_SOURCE_DIR}/lv_core/lv_vdb.c - - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_arc.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_img.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_label.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_line.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_rbasic.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_rect.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_triangle.c - ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_vbasic.c - - ${LVGL_SOURCE_DIR}/lv_hal/lv_hal_disp.c - ${LVGL_SOURCE_DIR}/lv_hal/lv_hal_indev.c - ${LVGL_SOURCE_DIR}/lv_hal/lv_hal_tick.c - - ${LVGL_SOURCE_DIR}/lv_misc/lv_anim.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_area.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_circ.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_color.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_font.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_fs.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_gc.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_ll.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_log.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_math.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_mem.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_task.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_templ.c - ${LVGL_SOURCE_DIR}/lv_misc/lv_txt.c - - ${LVGL_SOURCE_DIR}/lv_objx/lv_arc.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_bar.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_btn.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_btnm.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_calendar.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_canvas.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_cb.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_chart.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_cont.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_ddlist.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_gauge.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_img.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_imgbtn.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_kb.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_label.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_led.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_line.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_list.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_lmeter.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_mbox.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_objx_templ.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_page.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_preload.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_roller.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_slider.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_spinbox.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_sw.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_ta.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_table.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_tabview.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_tileview.c - ${LVGL_SOURCE_DIR}/lv_objx/lv_win.c - - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_alien.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_default.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_material.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_mono.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_nemo.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_night.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_templ.c - ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_zen.c - - ${LVGL_SOURCE_DIR}/lv_fonts/lv_font_builtin.c - ${LVGL_SOURCE_DIR}/lv_fonts/lv_font_dejavu_20.c - ${LVGL_DRIVER_DIR}/linux_display_indev.c - ${LVGL_DRIVER_DIR}/indev/mouse.c - -) -SET(SOURCES - ${LVGL_SOURCES} - ${CMAKE_CURRENT_LIST_DIR}/main.c - ) -include_directories( - ${LVGL_DRIVER_DIR} - ${LVGL_DRIVER_DIR}/display - ${LVGL_DRIVER_DIR}/indev - ${LVGL_SOURCE_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_LIST_DIR} - ${CMAKE_CURRENT_LIST_DIR}/../lv_config -) -add_executable(vgl_native_ui_app ${SOURCES} ) -target_link_libraries( vgl_native_ui_app -lSDL2) diff --git a/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in b/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in deleted file mode 100644 index bce7b16b70..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required(VERSION 2.8.2) - -project(lvgl_download NONE) - -include(ExternalProject) -ExternalProject_Add(${lv_name} - GIT_REPOSITORY https://github.com/littlevgl/lvgl.git - GIT_TAG v5.3 - BINARY_DIR "" - SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/lvgl" - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - TEST_COMMAND "" - ) diff --git a/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore b/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore deleted file mode 100644 index 2372cca067..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**/*.o \ No newline at end of file diff --git a/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h b/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h deleted file mode 100644 index 3aa6e16133..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef DISPLAY_INDEV_H_ -#define DISPLAY_INDEV_H_ -#include -#include -#include "mouse.h" -#include "lvgl/lv_misc/lv_color.h" -#include "lvgl/lv_hal/lv_hal_indev.h" -extern void display_init(void); -extern void display_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); -extern bool display_input_read(lv_indev_data_t * data); -extern void display_deinit(void); -extern void display_vdb_write(void *buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, lv_color_t *color, lv_opa_t opa); -extern int time_get_ms(); - -#endif diff --git a/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c b/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c deleted file mode 100644 index 58f7be0ca5..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @file mouse.c - * - */ - -/********************* - * INCLUDES - *********************/ -#include "mouse.h" -#if USE_MOUSE != 0 - -/********************* - * DEFINES - *********************/ -#ifndef MONITOR_ZOOM -#define MONITOR_ZOOM 1 -#endif - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ - -/********************** - * STATIC VARIABLES - **********************/ -static bool left_button_down = false; -static int16_t last_x = 0; -static int16_t last_y = 0; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Initialize the mouse - */ -void mouse_init(void) -{ - -} - -/** - * Get the current position and state of the mouse - * @param data store the mouse data here - * @return false: because the points are not buffered, so no more data to be read - */ -bool mouse_read(lv_indev_data_t * data) -{ - /*Store the collected data*/ - data->point.x = last_x; - data->point.y = last_y; - data->state = left_button_down ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; - - return false; -} - -/** - * It will be called from the main SDL thread - */ -void mouse_handler(SDL_Event * event) -{ - switch (event->type) { - case SDL_MOUSEBUTTONUP: - if (event->button.button == SDL_BUTTON_LEFT) - left_button_down = false; - break; - case SDL_MOUSEBUTTONDOWN: - if (event->button.button == SDL_BUTTON_LEFT) { - left_button_down = true; - last_x = event->motion.x / MONITOR_ZOOM; - last_y = event->motion.y / MONITOR_ZOOM; - } - break; - case SDL_MOUSEMOTION: - last_x = event->motion.x / MONITOR_ZOOM; - last_y = event->motion.y / MONITOR_ZOOM; - - break; - } - -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -#endif diff --git a/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h b/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h deleted file mode 100644 index 2aedd0ff6e..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @file mouse.h - * - */ - -#ifndef MOUSE_H -#define MOUSE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - -#include "lv_drv_conf.h" - -#if USE_MOUSE - -#include -#include -#include "lvgl/lv_hal/lv_hal_indev.h" - -#ifndef MONITOR_SDL_INCLUDE_PATH -#define MONITOR_SDL_INCLUDE_PATH -#endif - -#include MONITOR_SDL_INCLUDE_PATH - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * GLOBAL PROTOTYPES - **********************/ - -/** - * Initialize the mouse - */ -void mouse_init(void); -/** - * Get the current position and state of the mouse - * @param data store the mouse data here - * @return false: because the points are not buffered, so no more data to be read - */ -bool mouse_read(lv_indev_data_t * data); - -/** - * It will be called from the main SDL thread - */ -void mouse_handler(SDL_Event *event); - -/********************** - * MACROS - **********************/ - -#endif /* USE_MOUSE */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* MOUSE_H */ diff --git a/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c b/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c deleted file mode 100644 index cf6ff82a13..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include "display_indev.h" -#include "sys/time.h" -#include "SDL2/SDL.h" -#define MONITOR_HOR_RES 320 -#define MONITOR_VER_RES 240 -#ifndef MONITOR_ZOOM -#define MONITOR_ZOOM 1 -#endif -#define SDL_REFR_PERIOD 50 -void monitor_sdl_init(void); -void monitor_sdl_refr_core(void); -void monitor_sdl_clean_up(void); -static uint32_t tft_fb[MONITOR_HOR_RES * MONITOR_VER_RES]; - -void display_vdb_write(void *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, - lv_color_t *color, lv_opa_t opa) -{ - unsigned char *buf_xy = buf + 4 * x + 4 * y * buf_w; - lv_color_t * temp = (lv_color_t *) buf_xy; - *temp = *color; - /* - if (opa != LV_OPA_COVER) { - lv_color_t mix_color; - - mix_color.red = *buf_xy; - mix_color.green = *(buf_xy+1); - mix_color.blue = *(buf_xy+2); - color = lv_color_mix(color, mix_color, opa); - } - */ -} -int time_get_ms() -{ - static struct timeval tv; - gettimeofday(&tv, NULL); - long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; - - return (int) time_in_mill; -} - -SDL_Window * window; -SDL_Renderer * renderer; -SDL_Texture * texture; -static volatile bool sdl_inited = false; -static volatile bool sdl_refr_qry = false; -static volatile bool sdl_quit_qry = false; - -void monitor_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - /*Return if the area is out the screen*/ - if (x2 < 0 || y2 < 0 || x1 > MONITOR_HOR_RES - 1 - || y1 > MONITOR_VER_RES - 1) { - return; - } - - int32_t y; - uint32_t w = x2 - x1 + 1; - for (y = y1; y <= y2; y++) { - memcpy(&tft_fb[y * MONITOR_HOR_RES + x1], color_p, - w * sizeof(lv_color_t)); - - color_p += w; - } - sdl_refr_qry = true; - - /*IMPORTANT! It must be called to tell the system the flush is ready*/ - -} - -/** - * Fill out the marked area with a color - * @param x1 left coordinate - * @param y1 top coordinate - * @param x2 right coordinate - * @param y2 bottom coordinate - * @param color fill color - */ -void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color) -{ - /*Return if the area is out the screen*/ - if (x2 < 0) - return; - if (y2 < 0) - return; - if (x1 > MONITOR_HOR_RES - 1) - return; - if (y1 > MONITOR_VER_RES - 1) - return; - - /*Truncate the area to the screen*/ - int32_t act_x1 = x1 < 0 ? 0 : x1; - int32_t act_y1 = y1 < 0 ? 0 : y1; - int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; - int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; - - int32_t x; - int32_t y; - uint32_t color32 = color.full; //lv_color_to32(color); - - for (x = act_x1; x <= act_x2; x++) { - for (y = act_y1; y <= act_y2; y++) { - tft_fb[y * MONITOR_HOR_RES + x] = color32; - } - } - - sdl_refr_qry = true; -} - -/** - * Put a color map to the marked area - * @param x1 left coordinate - * @param y1 top coordinate - * @param x2 right coordinate - * @param y2 bottom coordinate - * @param color_p an array of colors - */ -void monitor_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - /*Return if the area is out the screen*/ - if (x2 < 0) - return; - if (y2 < 0) - return; - if (x1 > MONITOR_HOR_RES - 1) - return; - if (y1 > MONITOR_VER_RES - 1) - return; - - /*Truncate the area to the screen*/ - int32_t act_x1 = x1 < 0 ? 0 : x1; - int32_t act_y1 = y1 < 0 ? 0 : y1; - int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; - int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; - - int32_t x; - int32_t y; - - for (y = act_y1; y <= act_y2; y++) { - for (x = act_x1; x <= act_x2; x++) { - tft_fb[y * MONITOR_HOR_RES + x] = color_p->full; //lv_color_to32(*color_p); - color_p++; - } - - color_p += x2 - act_x2; - } - - sdl_refr_qry = true; -} - -void display_init(void) -{ -} - -void display_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - monitor_flush(x1, y1, x2, y2, color_p); -} -void display_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p) -{ - monitor_fill(x1, y1, x2, y2, color_p); -} -void display_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - monitor_map(x1, y1, x2, y2, color_p); -} - -bool display_input_read(lv_indev_data_t * data) -{ - return mouse_read(data); -} - -void display_deinit(void) -{ - -} - -int monitor_sdl_refr_thread(void * param) -{ - (void) param; - - /*If not OSX initialize SDL in the Thread*/ - monitor_sdl_init(); - /*Run until quit event not arrives*/ - while (sdl_quit_qry == false) { - /*Refresh handling*/ - monitor_sdl_refr_core(); - } - - monitor_sdl_clean_up(); - exit(0); - - return 0; -} - -void monitor_sdl_refr_core(void) -{ - if (sdl_refr_qry != false) { - sdl_refr_qry = false; - - SDL_UpdateTexture(texture, NULL, tft_fb, - MONITOR_HOR_RES * sizeof(uint32_t)); - SDL_RenderClear(renderer); - /*Test: Draw a background to test transparent screens (LV_COLOR_SCREEN_TRANSP)*/ -// SDL_SetRenderDrawColor(renderer, 0xff, 0, 0, 0xff); -// SDL_Rect r; -// r.x = 0; r.y = 0; r.w = MONITOR_HOR_RES; r.w = MONITOR_VER_RES; -// SDL_RenderDrawRect(renderer, &r); - /*Update the renderer with the texture containing the rendered image*/ - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - } - - SDL_Event event; - while (SDL_PollEvent(&event)) { -#if USE_MOUSE != 0 - mouse_handler(&event); -#endif - if ((&event)->type == SDL_WINDOWEVENT) { - switch ((&event)->window.event) { -#if SDL_VERSION_ATLEAST(2, 0, 5) - case SDL_WINDOWEVENT_TAKE_FOCUS: -#endif - case SDL_WINDOWEVENT_EXPOSED: - - SDL_UpdateTexture(texture, NULL, tft_fb, - MONITOR_HOR_RES * sizeof(uint32_t)); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - break; - default: - break; - } - } - } - - /*Sleep some time*/ - SDL_Delay(SDL_REFR_PERIOD); - -} -int quit_filter(void * userdata, SDL_Event * event) -{ - (void) userdata; - - if (event->type == SDL_QUIT) { - sdl_quit_qry = true; - } - - return 1; -} - -void monitor_sdl_clean_up(void) -{ - SDL_DestroyTexture(texture); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); -} - -void monitor_sdl_init(void) -{ - /*Initialize the SDL*/ - SDL_Init(SDL_INIT_VIDEO); - - SDL_SetEventFilter(quit_filter, NULL); - - window = SDL_CreateWindow("TFT Simulator", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - MONITOR_HOR_RES * MONITOR_ZOOM, MONITOR_VER_RES * MONITOR_ZOOM, 0); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ - - renderer = SDL_CreateRenderer(window, -1, 0); - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STATIC, MONITOR_HOR_RES, MONITOR_VER_RES); - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); - - /*Initialize the frame buffer to gray (77 is an empirical value) */ - memset(tft_fb, 0x44, MONITOR_HOR_RES * MONITOR_VER_RES * sizeof(uint32_t)); - SDL_UpdateTexture(texture, NULL, tft_fb, - MONITOR_HOR_RES * sizeof(uint32_t)); - sdl_refr_qry = true; - sdl_inited = true; -} - -void display_SDL_init() -{ - SDL_CreateThread(monitor_sdl_refr_thread, "sdl_refr", NULL); - while (sdl_inited == false) - ; /*Wait until 'sdl_refr' initializes the SDL*/ -} - diff --git a/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h b/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h deleted file mode 100644 index 41d9238643..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include - -int time_get_ms(); diff --git a/samples/littlevgl/vgl-native-ui-app/main.c b/samples/littlevgl/vgl-native-ui-app/main.c deleted file mode 100644 index 4d403c2bae..0000000000 --- a/samples/littlevgl/vgl-native-ui-app/main.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/** - * @file main - * - */ - -/********************* - * INCLUDES - *********************/ -#include -#include -#include "lvgl/lvgl.h" -#include "display_indev.h" -#include - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void hal_init(void); -//static int tick_thread(void * data); -//static void memory_monitor(void * param); - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ -uint32_t count = 0; -char count_str[11] = { 0 }; -lv_obj_t *hello_world_label; -lv_obj_t *count_label; -lv_obj_t * btn1; - -lv_obj_t * label_count1; -int label_count1_value = 0; -char label_count1_str[11] = { 0 }; -static lv_res_t btn_rel_action(lv_obj_t * btn) -{ - label_count1_value++; - snprintf(label_count1_str, sizeof(label_count1_str), - "%d", label_count1_value); - lv_label_set_text(label_count1, label_count1_str); - return LV_RES_OK; -} - - -int main() -{ - void display_SDL_init(); - display_SDL_init(); - - /*Initialize LittlevGL*/ - lv_init(); - - /*Initialize the HAL (display, input devices, tick) for LittlevGL*/ - hal_init(); - - hello_world_label = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text(hello_world_label, "Hello world!"); - lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); - - count_label = lv_label_create(lv_scr_act(), NULL); - lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); - - btn1 = lv_btn_create(lv_scr_act(), NULL); /*Create a button on the currently loaded screen*/ - lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, btn_rel_action); /*Set function to be called when the button is released*/ - lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 20); /*Align below the label*/ - - /*Create a label on the button*/ - lv_obj_t * btn_label = lv_label_create(btn1, NULL); - lv_label_set_text(btn_label, "Click ++"); - - label_count1 = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text(label_count1, "0"); - lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - - while(1) { - /* Periodically call the lv_task handler. - * It could be done in a timer interrupt or an OS task too.*/ - if ((count % 100) == 0) { - snprintf(count_str, sizeof(count_str), "%d", count/ 100); - lv_label_set_text(count_label, count_str); - } - lv_task_handler(); - ++count; - usleep(10 * 1000); /*Just to let the system breath*/ - } - - return 0; -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -/** - * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library - */ -void display_flush_wrapper(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - display_flush(x1, y1, x2, y2, color_p); - lv_flush_ready(); -} -void display_vdb_write_wrapper(uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, lv_color_t color, lv_opa_t opa) -{ - display_vdb_write(buf, buf_w, x, y, &color, opa); -} -extern void display_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p); -extern void display_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); -static void hal_init(void) -{ - /* Add a display*/ - lv_disp_drv_t disp_drv; - lv_disp_drv_init(&disp_drv); /*Basic initialization*/ - disp_drv.disp_flush = display_flush_wrapper; /*Used when `LV_VDB_SIZE != 0` in lv_conf.h (buffered drawing)*/ - disp_drv.disp_fill = display_fill; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ - disp_drv.disp_map = display_map; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ -#if LV_VDB_SIZE != 0 - disp_drv.vdb_wr = display_vdb_write_wrapper; -#endif - lv_disp_drv_register(&disp_drv); - - /* Add the mouse as input device - * Use the 'mouse' driver which reads the PC's mouse*/ -// mouse_init(); - lv_indev_drv_t indev_drv; - lv_indev_drv_init(&indev_drv); /*Basic initialization*/ - indev_drv.type = LV_INDEV_TYPE_POINTER; - indev_drv.read = display_input_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ - lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv); - -} - diff --git a/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt b/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt deleted file mode 100644 index aa91933613..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required (VERSION 2.8) - -project (vgl_wasm_runtime) - -set (WAMR_BUILD_PLATFORM "linux") - -# Reset default linker flags -set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") -set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") - -################ wamr runtime settings ################ - -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) - -## use library and headers in the SDK -link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/littlevgl/runtime-sdk/lib) -include_directories( - ${WAMR_ROOT_DIR}/wamr-sdk/out/littlevgl/runtime-sdk/include - ${WAMR_ROOT_DIR}/wamr-sdk/out/littlevgl/runtime-sdk/include/bi-inc/deps -) - -############### application related ############### -include_directories(${CMAKE_CURRENT_LIST_DIR}/src) - -add_executable (vgl_wasm_runtime src/platform/${WAMR_BUILD_PLATFORM}/main.c - src/platform/${WAMR_BUILD_PLATFORM}/iwasm_main.c - src/ext_lib_export.c src/platform/${WAMR_BUILD_PLATFORM}/display_indev.c - src/platform/${WAMR_BUILD_PLATFORM}/mouse.c) - -target_link_libraries (vgl_wasm_runtime vmlib -lm -ldl -lpthread -lSDL2) - diff --git a/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h b/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h deleted file mode 100644 index 78daefe338..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef DISPLAY_INDEV_H_ -#define DISPLAY_INDEV_H_ -#include -#include -#include -#include "bh_platform.h" -#include "wasm_export.h" - -#define USE_MOUSE 1 -typedef union { - struct { - uint8_t blue; - uint8_t green; - uint8_t red; - uint8_t alpha; - }; - uint32_t full; -} lv_color32_t; - -typedef lv_color32_t lv_color_t; -typedef uint8_t lv_indev_state_t; -typedef int16_t lv_coord_t; -typedef uint8_t lv_opa_t; -typedef struct { - lv_coord_t x; - lv_coord_t y; -} lv_point_t; - -typedef struct { - union { - lv_point_t point; /*For LV_INDEV_TYPE_POINTER the currently pressed point*/ - uint32_t key; /*For LV_INDEV_TYPE_KEYPAD the currently pressed key*/ - uint32_t btn; /*For LV_INDEV_TYPE_BUTTON the currently pressed button*/ - int16_t enc_diff; /*For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/ - }; - void *user_data; /*'lv_indev_drv_t.priv' for this driver*/ - lv_indev_state_t state; /*LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/ -} lv_indev_data_t; - -enum { - LV_INDEV_STATE_REL = 0, LV_INDEV_STATE_PR -}; -enum { - LV_OPA_TRANSP = 0, - LV_OPA_0 = 0, - LV_OPA_10 = 25, - LV_OPA_20 = 51, - LV_OPA_30 = 76, - LV_OPA_40 = 102, - LV_OPA_50 = 127, - LV_OPA_60 = 153, - LV_OPA_70 = 178, - LV_OPA_80 = 204, - LV_OPA_90 = 229, - LV_OPA_100 = 255, - LV_OPA_COVER = 255, -}; - -extern void xpt2046_init(void); - -extern bool touchscreen_read(lv_indev_data_t * data); - -extern bool mouse_read(lv_indev_data_t * data); - -extern void display_init(wasm_exec_env_t exec_env); - -extern void display_deinit(wasm_exec_env_t exec_env); - -extern int time_get_ms(wasm_exec_env_t exec_env); - -extern void display_flush(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - int32 color_p_offset); - -extern void display_fill(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p); - -extern void display_map(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); - -extern bool display_input_read(wasm_exec_env_t exec_env, - int32 data_offset); - -void display_vdb_write(wasm_exec_env_t exec_env, - int32 buf_offset, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, int32 color_p_offset, lv_opa_t opa); - -#endif - diff --git a/samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c b/samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c deleted file mode 100644 index 0f68dd8602..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "lib_export.h" -#include "sensor_api.h" -#include "connection_api.h" -#include "display_indev.h" - -static NativeSymbol extended_native_symbol_defs[] = { -#include "runtime_sensor.inl" -#include "connection.inl" - EXPORT_WASM_API(display_init), - EXPORT_WASM_API(display_input_read), - EXPORT_WASM_API(display_flush), - EXPORT_WASM_API(display_fill), - EXPORT_WASM_API(display_vdb_write), - EXPORT_WASM_API(display_map), - EXPORT_WASM_API(time_get_ms) -}; - -#include "ext_lib_export.h" diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c deleted file mode 100644 index 52d57d5067..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include "display_indev.h" -#include "SDL2/SDL.h" -#include "sys/time.h" -#include "wasm_export.h" -#include "app_manager_export.h" - -#define MONITOR_HOR_RES 320 -#define MONITOR_VER_RES 240 -#ifndef MONITOR_ZOOM -#define MONITOR_ZOOM 1 -#endif -#define SDL_REFR_PERIOD 50 -void monitor_sdl_init(void); -void monitor_sdl_refr_core(void); -void monitor_sdl_clean_up(void); - -static uint32_t tft_fb[MONITOR_HOR_RES * MONITOR_VER_RES]; - -int -time_get_ms(wasm_exec_env_t exec_env) -{ - static struct timeval tv; - gettimeofday(&tv, NULL); - long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; - - return (int) time_in_mill; -} - -SDL_Window * window; -SDL_Renderer * renderer; -SDL_Texture * texture; -static volatile bool sdl_inited = false; -static volatile bool sdl_refr_qry = false; -static volatile bool sdl_quit_qry = false; - -void monitor_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - /*Return if the area is out the screen*/ - if (x2 < 0 || y2 < 0 || x1 > MONITOR_HOR_RES - 1 - || y1 > MONITOR_VER_RES - 1) { - return; - } - - int32_t y; - uint32_t w = x2 - x1 + 1; - - for (y = y1; y <= y2; y++) { - memcpy(&tft_fb[y * MONITOR_HOR_RES + x1], color_p, - w * sizeof(lv_color_t)); - - color_p += w; - } - sdl_refr_qry = true; - - /*IMPORTANT! It must be called to tell the system the flush is ready*/ -} - -/** - * Fill out the marked area with a color - * @param x1 left coordinate - * @param y1 top coordinate - * @param x2 right coordinate - * @param y2 bottom coordinate - * @param color fill color - */ -void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color) -{ - /*Return if the area is out the screen*/ - if (x2 < 0) - return; - if (y2 < 0) - return; - if (x1 > MONITOR_HOR_RES - 1) - return; - if (y1 > MONITOR_VER_RES - 1) - return; - - /*Truncate the area to the screen*/ - int32_t act_x1 = x1 < 0 ? 0 : x1; - int32_t act_y1 = y1 < 0 ? 0 : y1; - int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; - int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; - - int32_t x; - int32_t y; - uint32_t color32 = color.full; //lv_color_to32(color); - - for (x = act_x1; x <= act_x2; x++) { - for (y = act_y1; y <= act_y2; y++) { - tft_fb[y * MONITOR_HOR_RES + x] = color32; - } - } - - sdl_refr_qry = true; -} - -/** - * Put a color map to the marked area - * @param x1 left coordinate - * @param y1 top coordinate - * @param x2 right coordinate - * @param y2 bottom coordinate - * @param color_p an array of colors - */ -void monitor_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - /*Return if the area is out the screen*/ - if (x2 < 0) - return; - if (y2 < 0) - return; - if (x1 > MONITOR_HOR_RES - 1) - return; - if (y1 > MONITOR_VER_RES - 1) - return; - - /*Truncate the area to the screen*/ - int32_t act_x1 = x1 < 0 ? 0 : x1; - int32_t act_y1 = y1 < 0 ? 0 : y1; - int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; - int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; - - int32_t x; - int32_t y; - - for (y = act_y1; y <= act_y2; y++) { - for (x = act_x1; x <= act_x2; x++) { - tft_fb[y * MONITOR_HOR_RES + x] = color_p->full; //lv_color_to32(*color_p); - color_p++; - } - - color_p += x2 - act_x2; - } - - sdl_refr_qry = true; -} - - -void -display_init(wasm_exec_env_t exec_env) -{ -} - -void -display_flush(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - int32 color_p_offset) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) - return; - lv_color_t * color_p = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - - monitor_flush(x1, y1, x2, y2, color_p); -} - -void -display_fill(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p) -{ - monitor_fill(x1, y1, x2, y2, color_p); -} - -void -display_map(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - monitor_map(x1, y1, x2, y2, color_p); -} - -bool -display_input_read(wasm_exec_env_t exec_env, - int32 data_p_offset) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - bool ret; - if (!wasm_runtime_validate_app_addr(module_inst, data_p_offset, 1)) - return false; - - struct { - lv_point_t point; - int32 user_data_offset; - uint8 state; - } *data_app; - - lv_indev_data_t data = {0}; - - ret = mouse_read(&data); - - data_app = wasm_runtime_addr_app_to_native(module_inst, - data_p_offset); - - data_app->point = data.point; - data_app->user_data_offset = - wasm_runtime_addr_native_to_app(module_inst, data.user_data); - data_app->state = data.state; - - return ret; -} - -void -display_deinit(wasm_exec_env_t exec_env) -{ -} - -void -display_vdb_write(wasm_exec_env_t exec_env, - int32 buf_offset, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, int32 color_p_offset, lv_opa_t opa) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) - return; - lv_color_t *color = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - - void *buf = wasm_runtime_addr_app_to_native(module_inst, buf_offset); - - unsigned char *buf_xy = buf + 4 * x + 4 * y * buf_w; - lv_color_t * temp = (lv_color_t *) buf_xy; - *temp = *color; - /* - if (opa != LV_OPA_COVER) { - lv_color_t mix_color; - - mix_color.red = *buf_xy; - mix_color.green = *(buf_xy+1); - mix_color.blue = *(buf_xy+2); - color = lv_color_mix(color, mix_color, opa); - } - */ - /* - *buf_xy = color->red; - *(buf_xy + 1) = color->green; - *(buf_xy + 2) = color->blue; - */ -} - -int monitor_sdl_refr_thread(void * param) -{ - (void) param; - - /*If not OSX initialize SDL in the Thread*/ - monitor_sdl_init(); - /*Run until quit event not arrives*/ - while (sdl_quit_qry == false) { - /*Refresh handling*/ - monitor_sdl_refr_core(); - } - - monitor_sdl_clean_up(); - exit(0); - - return 0; -} -extern void mouse_handler(SDL_Event *event); -void monitor_sdl_refr_core(void) -{ - if (sdl_refr_qry != false) { - sdl_refr_qry = false; - - SDL_UpdateTexture(texture, NULL, tft_fb, - MONITOR_HOR_RES * sizeof(uint32_t)); - SDL_RenderClear(renderer); - /*Update the renderer with the texture containing the rendered image*/ - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - } - - SDL_Event event; - while (SDL_PollEvent(&event)) { - - mouse_handler(&event); - - if ((&event)->type == SDL_WINDOWEVENT) { - switch ((&event)->window.event) { -#if SDL_VERSION_ATLEAST(2, 0, 5) - case SDL_WINDOWEVENT_TAKE_FOCUS: -#endif - case SDL_WINDOWEVENT_EXPOSED: - - SDL_UpdateTexture(texture, NULL, tft_fb, - MONITOR_HOR_RES * sizeof(uint32_t)); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - break; - default: - break; - } - } - } - - /*Sleep some time*/ - SDL_Delay(SDL_REFR_PERIOD); - -} -int quit_filter(void * userdata, SDL_Event * event) -{ - (void) userdata; - - if (event->type == SDL_QUIT) { - sdl_quit_qry = true; - } - - return 1; -} - -void monitor_sdl_clean_up(void) -{ - SDL_DestroyTexture(texture); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); -} - -void monitor_sdl_init(void) -{ - /*Initialize the SDL*/ - SDL_Init(SDL_INIT_VIDEO); - - SDL_SetEventFilter(quit_filter, NULL); - - window = SDL_CreateWindow("TFT Simulator", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - MONITOR_HOR_RES * MONITOR_ZOOM, MONITOR_VER_RES * MONITOR_ZOOM, 0); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ - - renderer = SDL_CreateRenderer(window, -1, 0); - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STATIC, MONITOR_HOR_RES, MONITOR_VER_RES); - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); - - /*Initialize the frame buffer to gray (77 is an empirical value) */ - memset(tft_fb, 0x44, MONITOR_HOR_RES * MONITOR_VER_RES * sizeof(uint32_t)); - SDL_UpdateTexture(texture, NULL, tft_fb, - MONITOR_HOR_RES * sizeof(uint32_t)); - sdl_refr_qry = true; - sdl_inited = true; -} - -void display_SDL_init() -{ - SDL_CreateThread(monitor_sdl_refr_thread, "sdl_refr", NULL); - while (sdl_inited == false) - ; /*Wait until 'sdl_refr' initializes the SDL*/ -} - diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c deleted file mode 100644 index 1081cd7273..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c +++ /dev/null @@ -1,493 +0,0 @@ - -#ifndef CONNECTION_UART -#include -#include -#include -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "runtime_lib.h" -#include "runtime_timer.h" -#include "native_interface.h" -#include "app_manager_export.h" -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "runtime_sensor.h" -#include "bi-inc/attr_container.h" -#include "module_wasm_app.h" -#include "wasm_export.h" -#define MAX 2048 - -#ifndef CONNECTION_UART -#define SA struct sockaddr -static char *host_address = "127.0.0.1"; -static int port = 8888; -#else -static char *uart_device = "/dev/ttyS2"; -static int baudrate = B115200; -#endif - -extern void init_sensor_framework(); -extern void exit_sensor_framework(); -extern void exit_connection_framework(); -extern int aee_host_msg_callback(void *msg, uint16_t msg_len); -extern bool init_connection_framework(); - -#ifndef CONNECTION_UART -int listenfd = -1; -int sockfd = -1; -static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; -#else -int uartfd = -1; -#endif - -#ifndef CONNECTION_UART -static bool server_mode = false; - -// Function designed for chat between client and server. -void* func(void* arg) -{ - char buff[MAX]; - int n; - struct sockaddr_in servaddr; - - while (1) { - if (sockfd != -1) - close(sockfd); - // socket create and verification - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd == -1) { - printf("socket creation failed...\n"); - return NULL; - } else - printf("Socket successfully created..\n"); - bzero(&servaddr, sizeof(servaddr)); - // assign IP, PORT - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(host_address); - servaddr.sin_port = htons(port); - - // connect the client socket to server socket - if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) != 0) { - printf("connection with the server failed...\n"); - sleep(10); - continue; - } else { - printf("connected to the server..\n"); - } - - // infinite loop for chat - for (;;) { - bzero(buff, MAX); - - // read the message from client and copy it in buffer - n = read(sockfd, buff, sizeof(buff)); - // print buffer which contains the client contents - //fprintf(stderr, "recieved %d bytes from host: %s", n, buff); - - // socket disconnected - if (n <= 0) - break; - - aee_host_msg_callback(buff, n); - } - } - - // After chatting close the socket - close(sockfd); -} - -static bool host_init() -{ - return true; -} - -int host_send(void * ctx, const char *buf, int size) -{ - int ret; - - if (pthread_mutex_trylock(&sock_lock) == 0) { - if (sockfd == -1) { - pthread_mutex_unlock(&sock_lock); - return 0; - } - - ret = write(sockfd, buf, size); - - pthread_mutex_unlock(&sock_lock); - return ret; - } - - return -1; -} - -void host_destroy() -{ - if (server_mode) - close(listenfd); - - pthread_mutex_lock(&sock_lock); - close(sockfd); - pthread_mutex_unlock(&sock_lock); -} - -host_interface interface = { - .init = host_init, - .send = host_send, - .destroy = host_destroy - }; - -void* func_server_mode(void* arg) -{ - int clilent; - struct sockaddr_in serv_addr, cli_addr; - int n; - char buff[MAX]; - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, 0); - - /* First call to socket() function */ - listenfd = socket(AF_INET, SOCK_STREAM, 0); - - if (listenfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - /* Initialize socket structure */ - bzero((char *) &serv_addr, sizeof(serv_addr)); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - /* Now bind the host address using bind() call.*/ - if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("ERROR on binding"); - exit(1); - } - - listen(listenfd, 5); - clilent = sizeof(cli_addr); - - while (1) { - pthread_mutex_lock(&sock_lock); - - sockfd = accept(listenfd, (struct sockaddr *) &cli_addr, &clilent); - - pthread_mutex_unlock(&sock_lock); - - if (sockfd < 0) { - perror("ERROR on accept"); - exit(1); - } - - printf("connection established!\n"); - - for (;;) { - bzero(buff, MAX); - - // read the message from client and copy it in buffer - n = read(sockfd, buff, sizeof(buff)); - - // socket disconnected - if (n <= 0) { - pthread_mutex_lock(&sock_lock); - close(sockfd); - sockfd = -1; - pthread_mutex_unlock(&sock_lock); - - sleep(2); - break; - } - - aee_host_msg_callback(buff, n); - } - } -} - -#else -static int parse_baudrate(int baud) -{ - switch (baud) { - case 9600: - return B9600; - case 19200: - return B19200; - case 38400: - return B38400; - case 57600: - return B57600; - case 115200: - return B115200; - case 230400: - return B230400; - case 460800: - return B460800; - case 500000: - return B500000; - case 576000: - return B576000; - case 921600: - return B921600; - case 1000000: - return B1000000; - case 1152000: - return B1152000; - case 1500000: - return B1500000; - case 2000000: - return B2000000; - case 2500000: - return B2500000; - case 3000000: - return B3000000; - case 3500000: - return B3500000; - case 4000000: - return B4000000; - default: - return -1; - } -} -static bool uart_init(const char *device, int baudrate, int *fd) -{ - int uart_fd; - struct termios uart_term; - - uart_fd = open(device, O_RDWR | O_NOCTTY); - - if (uart_fd <= 0) - return false; - - memset(&uart_term, 0, sizeof(uart_term)); - uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; - uart_term.c_iflag = IGNPAR; - uart_term.c_oflag = 0; - - /* set noncanonical mode */ - uart_term.c_lflag = 0; - uart_term.c_cc[VTIME] = 30; - uart_term.c_cc[VMIN] = 1; - tcflush(uart_fd, TCIFLUSH); - - if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { - close(uart_fd); - return false; - } - - *fd = uart_fd; - - return true; -} - -static void *func_uart_mode(void *arg) -{ - int n; - char buff[MAX]; - - if (!uart_init(uart_device, baudrate, &uartfd)) { - printf("open uart fail! %s\n", uart_device); - return NULL; - } - - for (;;) { - bzero(buff, MAX); - - n = read(uartfd, buff, sizeof(buff)); - - if (n <= 0) { - close(uartfd); - uartfd = -1; - break; - } - - aee_host_msg_callback(buff, n); - } - - return NULL; -} - -static int uart_send(void * ctx, const char *buf, int size) -{ - int ret; - - ret = write(uartfd, buf, size); - - return ret; -} - -static void uart_destroy() -{ - close(uartfd); -} - -static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; - -#endif - -#ifdef __x86_64__ -static char global_heap_buf[400 * 1024] = { 0 }; -#else -static char global_heap_buf[270 * 1024] = { 0 }; -#endif - -static void showUsage() -{ -#ifndef CONNECTION_UART - printf("Usage:\n"); - printf("\nWork as TCP server mode:\n"); - printf("\tvgl_wasm_runtime -s|--server_mode -p|--port \n"); - printf("where\n"); - printf("\t represents the port that would be listened on and the default is 8888\n"); - printf("\nWork as TCP client mode:\n"); - printf("\tvgl_wasm_runtime -a|--host_address -p|--port \n"); - printf("where\n"); - printf("\t represents the network address of host and the default is 127.0.0.1\n"); - printf("\t represents the listen port of host and the default is 8888\n"); -#else - printf("Usage:\n"); - printf("\tvgl_wasm_runtime -u -b \n\n"); - printf("where\n"); - printf("\t represents the UART device name and the default is /dev/ttyS2\n"); - printf("\t represents the UART device baudrate and the default is 115200\n"); -#endif - printf("\nNote:\n"); - printf("\tUse -w|--wasi_root to specify the root dir (default to '.') of WASI wasm modules. \n"); -} - -static bool parse_args(int argc, char *argv[]) -{ - int c; - - while (1) { - int optIndex = 0; - static struct option longOpts[] = { -#ifndef CONNECTION_UART - { "server_mode", no_argument, NULL, 's' }, - { "host_address", required_argument, NULL, 'a' }, - { "port", required_argument, NULL, 'p' }, -#else - { "uart", required_argument, NULL, 'u' }, - { "baudrate", required_argument, NULL, 'b' }, -#endif - { "wasi_root", required_argument, NULL, 'w' }, - { "help", required_argument, NULL, 'h' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "sa:p:u:b:w:h", longOpts, &optIndex); - if (c == -1) - break; - - switch (c) { -#ifndef CONNECTION_UART - case 's': - server_mode = true; - break; - case 'a': - host_address = optarg; - printf("host address: %s\n", host_address); - break; - case 'p': - port = atoi(optarg); - printf("port: %d\n", port); - break; -#else - case 'u': - uart_device = optarg; - printf("uart device: %s\n", uart_device); - break; - case 'b': - baudrate = parse_baudrate(atoi(optarg)); - printf("uart baudrate: %s\n", optarg); - break; -#endif - case 'w': - if (!wasm_set_wasi_root_dir(optarg)) { - printf("Fail to set wasi root dir: %s\n", optarg); - return false; - } - break; - case 'h': - showUsage(); - return false; - default: - showUsage(); - return false; - } - } - - return true; -} - -// Driver function -int iwasm_main(int argc, char *argv[]) -{ - korp_thread tid; - - if (!parse_args(argc, argv)) - return -1; - - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - printf("Init global heap failed.\n"); - return -1; - } - - if (vm_thread_sys_init() != 0) { - goto fail1; - } - - if (!init_connection_framework()) { - vm_thread_sys_destroy(); - goto fail1; - } - - extern void display_SDL_init(); - display_SDL_init(); - - init_sensor_framework(); - - // timer manager - init_wasm_timer(); - -#ifndef CONNECTION_UART - if (server_mode) - vm_thread_create(&tid, func_server_mode, NULL, - BH_APPLET_PRESERVED_STACK_SIZE); - else - vm_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); -#else - vm_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); -#endif - - app_manager_startup(&interface); - - exit_wasm_timer(); - exit_sensor_framework(); - exit_connection_framework(); - -fail1: - bh_memory_destroy(); - - return -1; -} diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c deleted file mode 100644 index 8c94d0eabd..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -extern int iwasm_main(int argc, char *argv[]); -int main(int argc, char *argv[]) -{ - return iwasm_main(argc,argv); -} diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c deleted file mode 100644 index f5d071d3c1..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @file mouse.c - * - */ - -/********************* - * INCLUDES - *********************/ -#include "display_indev.h" -#include "SDL2/SDL.h" -#if USE_MOUSE != 0 - -/********************* - * DEFINES - *********************/ -#ifndef MONITOR_ZOOM -#define MONITOR_ZOOM 1 -#endif - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ - -/********************** - * STATIC VARIABLES - **********************/ -static bool left_button_down = false; -static int16_t last_x = 0; -static int16_t last_y = 0; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Initialize the mouse - */ -void mouse_init(void) -{ - -} - -/** - * Get the current position and state of the mouse - * @param data store the mouse data here - * @return false: because the points are not buffered, so no more data to be read - */ -bool mouse_read(lv_indev_data_t * data) -{ - /*Store the collected data*/ - data->point.x = last_x; - data->point.y = last_y; - data->state = left_button_down ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; - - return false; -} - -/** - * It will be called from the main SDL thread - */ -void mouse_handler(SDL_Event * event) -{ - switch (event->type) { - case SDL_MOUSEBUTTONUP: - if (event->button.button == SDL_BUTTON_LEFT) - left_button_down = false; - break; - case SDL_MOUSEBUTTONDOWN: - if (event->button.button == SDL_BUTTON_LEFT) { - left_button_down = true; - last_x = event->motion.x / MONITOR_ZOOM; - last_y = event->motion.y / MONITOR_ZOOM; - } - break; - case SDL_MOUSEMOTION: - last_x = event->motion.x / MONITOR_ZOOM; - last_y = event->motion.y / MONITOR_ZOOM; - - break; - } - -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -#endif diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE deleted file mode 100644 index 8f71f43fee..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c deleted file mode 100644 index 6d9048d4df..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c +++ /dev/null @@ -1,337 +0,0 @@ -/** - * @file XPT2046.c -*/ -/********************* - * INCLUDES - *********************/ -#include "XPT2046.h" -#include "board_config.h" -#include "stdio.h" -#include -#include "spi.h" - -#include "zephyr.h" -#include "kernel.h" - -#if USE_XPT2046 - -#include - -#define abs(x) ((x) < 0 ? -(x) : (x)) - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void xpt2046_corr(int16_t * x, int16_t * y); -static void xpt2046_avg(int16_t * x, int16_t * y); - -/********************** - * STATIC VARIABLES - **********************/ -int16_t avg_buf_x[XPT2046_AVG]; -int16_t avg_buf_y[XPT2046_AVG]; -uint8_t avg_last; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Initialize the XPT2046 - */ -struct device *input_dev; - -struct spi_config spi_conf_xpt2046; -struct spi_cs_control xpt2046_cs_ctrl; -struct device *xpt2046_pen_gpio_dev; -static struct gpio_callback gpio_cb; -lv_indev_data_t touch_point; -lv_indev_data_t last_touch_point; - -#define TOUCH_READ_THREAD_STACK_SIZE 4096 -static K_THREAD_STACK_DEFINE(touch_read_thread_stack, TOUCH_READ_THREAD_STACK_SIZE); -static struct k_thread touch_thread_data; -static struct k_sem sem_touch_read; - -K_MUTEX_DEFINE( spi_display_touch_mutex); - -int cnt = 0; -int touch_read_times = 0; -int last_pen_interrupt_time = 0; -void xpt2046_pen_gpio_callback(struct device *port, struct gpio_callback *cb, - u32_t pins) -{ - int i; - cnt++; - if ((k_uptime_get_32() - last_pen_interrupt_time) > 500) { - k_sem_give(&sem_touch_read); - touch_read_times++; - last_pen_interrupt_time = k_uptime_get_32(); - } - -} - -void disable_pen_interrupt() -{ - int ret = 0; - ret = gpio_pin_disable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); - if (ret != 0) { - printf("gpio_pin_configure GPIO_DIR_IN failed\n"); - } -} -void enable_pen_interrupt() -{ - int ret = 0; - ret = gpio_pin_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); - if (ret != 0) { - printf("gpio_pin_configure failed\n"); - } -} - -void touch_screen_read_thread() -{ - int i; - bool ret = false; - - for (;;) { - k_sem_take(&sem_touch_read, K_FOREVER); - memset(&last_touch_point, 0, sizeof(lv_indev_data_t)); - memset(&touch_point, 0, sizeof(lv_indev_data_t)); - memset(avg_buf_x, 0, sizeof(avg_buf_x)); - memset(avg_buf_y, 0, sizeof(avg_buf_y)); - k_mutex_lock(&spi_display_touch_mutex, K_FOREVER); - disable_pen_interrupt(); - for (i = 0; i < 100; i++) { - ret = xpt2046_read(&touch_point); - if (ret) { - if ((abs(last_touch_point.point.x - touch_point.point.x) < 4) - && (abs(last_touch_point.point.y - touch_point.point.y) - < 4)) { - break; - } - last_touch_point = touch_point; - - } - } - enable_pen_interrupt(); - k_mutex_unlock(&spi_display_touch_mutex); - } -} - -void xpt2046_init(void) -{ - int ret; - input_dev = device_get_binding(XPT2046_SPI_DEVICE_NAME); - - if (input_dev == NULL) { - printf("device not found. Aborting test."); - return; - } - memset((void *) &touch_point, 0, sizeof(lv_indev_data_t)); - - spi_conf_xpt2046.frequency = XPT2046_SPI_MAX_FREQUENCY; - spi_conf_xpt2046.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); - spi_conf_xpt2046.slave = 0; - spi_conf_xpt2046.cs = NULL; -#ifdef XPT2046_CS_GPIO_CONTROLLER - xpt2046_cs_ctrl.gpio_dev = device_get_binding(XPT2046_CS_GPIO_CONTROLLER); - if (xpt2046_cs_ctrl.gpio_dev == NULL) { - printk("Cannot find %s!\n", XPT2046_CS_GPIO_CONTROLLER); - return; - } - gpio_pin_configure(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, - GPIO_DIR_OUT); - gpio_pin_write(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, 1); - xpt2046_cs_ctrl.gpio_pin = XPT2046_CS_GPIO_PIN; - xpt2046_cs_ctrl.delay = 0; - spi_conf_xpt2046.cs = &xpt2046_cs_ctrl; - -#endif - -#ifdef XPT2046_PEN_GPIO_CONTROLLER - - xpt2046_pen_gpio_dev = device_get_binding(XPT2046_PEN_GPIO_CONTROLLER); - if (!xpt2046_pen_gpio_dev) { - printk("Cannot find %s!\n", XPT2046_PEN_GPIO_CONTROLLER); - return; - } - /* Setup GPIO input */ - ret = gpio_pin_configure(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN, - (GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE - | GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE) - ); - if (ret) { - printk("Error configuring pin %d!\n", XPT2046_PEN_GPIO_PIN); - } - - gpio_init_callback(&gpio_cb, xpt2046_pen_gpio_callback, - BIT(XPT2046_PEN_GPIO_PIN)); - - ret = gpio_add_callback(xpt2046_pen_gpio_dev, &gpio_cb); - if (ret) { - printk("gpio_add_callback error\n"); - } - ret = gpio_pin_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); - if (ret) { - printk("gpio_pin_enable_callback error\n"); - } -#endif - - k_sem_init(&sem_touch_read, 0, 1); - - k_thread_create(&touch_thread_data, touch_read_thread_stack, - TOUCH_READ_THREAD_STACK_SIZE, touch_screen_read_thread, - NULL, NULL, NULL, 5, - 0, K_NO_WAIT); - printf("xpt2046_init ok \n"); -} - -/** - * Get the current position and state of the touchpad - * @param data store the read data here - * @return false: because no ore data to be read - */ -bool xpt2046_read(lv_indev_data_t * data) -{ - static int16_t last_x = 0; - static int16_t last_y = 0; - bool valid = true; - int s32_ret = 0; - uint8_t buf; - - int16_t x = 0; - int16_t y = 0; - - char tx1[16] = { 0 }; - char rx1[16] = { 0 }; - - struct spi_buf tx_buf = { .buf = &tx1, .len = 3 }; - struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; - struct spi_buf rx_buf = { .buf = &rx1, .len = 3 }; - struct spi_buf_set rx_bufs = { .buffers = &rx_buf, .count = 1 }; - - tx1[0] = CMD_X_READ; - s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); - if (s32_ret != 0) { - printf("spi_transceive return failed:%d\n", s32_ret); - } - x = rx1[1] << 8; - x += rx1[2]; - - tx1[0] = CMD_Y_READ; - s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); - if (s32_ret != 0) { - printf("spi_transceive return failed:%d\n", s32_ret); - } - y = rx1[1] << 8; - y += rx1[2]; - x = x >> 3; - y = y >> 3; - - xpt2046_corr(&x, &y); - if (y <= 0 || (x > 320)) { - valid = false; - } - - last_x = x; - last_y = y; - - data->point.x = x; - data->point.y = y; - data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; - - return valid; -} - -/********************** - * STATIC FUNCTIONS - **********************/ -static void xpt2046_corr(int16_t * x, int16_t * y) -{ -#if XPT2046_XY_SWAP != 0 - int16_t swap_tmp; - swap_tmp = *x; - *x = *y; - *y = swap_tmp; -#endif - - if ((*x) > XPT2046_X_MIN) - (*x) -= XPT2046_X_MIN; - else - (*x) = 0; - - if ((*y) > XPT2046_Y_MIN) - (*y) -= XPT2046_Y_MIN; - else - (*y) = 0; - - (*x) = (uint32_t)((uint32_t)(*x) * XPT2046_HOR_RES) - / (XPT2046_X_MAX - XPT2046_X_MIN); - - (*y) = (uint32_t)((uint32_t)(*y) * XPT2046_VER_RES) - / (XPT2046_Y_MAX - XPT2046_Y_MIN); - -#if XPT2046_X_INV != 0 - (*x) = XPT2046_HOR_RES - (*x); -#endif - -#if XPT2046_Y_INV != 0 - (*y) = XPT2046_VER_RES - (*y); -#endif - -} - -static void xpt2046_avg(int16_t * x, int16_t * y) -{ - /*Shift out the oldest data*/ - uint8_t i; - for (i = XPT2046_AVG - 1; i > 0; i--) { - avg_buf_x[i] = avg_buf_x[i - 1]; - avg_buf_y[i] = avg_buf_y[i - 1]; - } - - /*Insert the new point*/ - avg_buf_x[0] = *x; - avg_buf_y[0] = *y; - if (avg_last < XPT2046_AVG) - avg_last++; - - /*Sum the x and y coordinates*/ - int32_t x_sum = 0; - int32_t y_sum = 0; - for (i = 0; i < avg_last; i++) { - x_sum += avg_buf_x[i]; - y_sum += avg_buf_y[i]; - } - - /*Normalize the sums*/ - (*x) = (int32_t) x_sum / avg_last; - (*y) = (int32_t) y_sum / avg_last; -} - -bool touchscreen_read(lv_indev_data_t * data) -{ - /*Store the collected data*/ - data->point.x = last_touch_point.point.x; - data->point.y = last_touch_point.point.y; - data->state = last_touch_point.state; - - if (last_touch_point.state == LV_INDEV_STATE_PR) { - last_touch_point.state = LV_INDEV_STATE_REL; - } - return false; -} - -#endif diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h deleted file mode 100644 index 6b3347e2c7..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * @file XPT2046.h - * - */ - -#ifndef XPT2046_H -#define XPT2046_H - -#define USE_XPT2046 1 - - -# define XPT2046_HOR_RES 320 -# define XPT2046_VER_RES 240 -# define XPT2046_X_MIN 200 -# define XPT2046_Y_MIN 200 -# define XPT2046_X_MAX 3800 -# define XPT2046_Y_MAX 3800 -# define XPT2046_AVG 4 -# define XPT2046_INV 0 - -#define CMD_X_READ 0b10010000 -#define CMD_Y_READ 0b11010000 - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - -#if USE_XPT2046 -#include -#include -#include -//#include "lvgl/lv_hal/lv_hal_indev.h" -#include "device.h" -#include "gpio.h" -#if 1 -enum { - LV_INDEV_STATE_REL = 0, LV_INDEV_STATE_PR -}; -typedef uint8_t lv_indev_state_t; -typedef int16_t lv_coord_t; -typedef struct { - lv_coord_t x; - lv_coord_t y; -} lv_point_t; - -typedef struct { - union { - lv_point_t point; /*For LV_INDEV_TYPE_POINTER the currently pressed point*/ - uint32_t key; /*For LV_INDEV_TYPE_KEYPAD the currently pressed key*/ - uint32_t btn; /*For LV_INDEV_TYPE_BUTTON the currently pressed button*/ - int16_t enc_diff; /*For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/ - }; - void *user_data; /*'lv_indev_drv_t.priv' for this driver*/ - lv_indev_state_t state; /*LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/ -} lv_indev_data_t; -#endif - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * GLOBAL PROTOTYPES - **********************/ -void xpt2046_init(void); -bool xpt2046_read(lv_indev_data_t * data); - -/********************** - * MACROS - **********************/ - -#endif /* USE_XPT2046 */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* XPT2046_H */ diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h deleted file mode 100644 index d7ea279a92..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#ifndef __BOARD_CONFIG_H__ -#define __BOARD_CONFIG_H__ -#include "pin_config_stm32.h" - -#endif /* __BOARD_CONFIG_H__ */ diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h deleted file mode 100644 index b820b9ce96..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * @brief Public API for display drivers and applications - */ - -#ifndef ZEPHYR_INCLUDE_DISPLAY_H_ -#define ZEPHYR_INCLUDE_DISPLAY_H_ - -/** - * @brief Display Interface - * @defgroup display_interface Display Interface - * @ingroup display_interfaces - * @{ - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum display_pixel_format { - PIXEL_FORMAT_RGB_888 = BIT(0), PIXEL_FORMAT_MONO01 = BIT(1), /* 0=Black 1=White */ - PIXEL_FORMAT_MONO10 = BIT(2), /* 1=Black 0=White */ - PIXEL_FORMAT_ARGB_8888 = BIT(3), PIXEL_FORMAT_RGB_565 = BIT(4), -}; - -enum display_screen_info { - /** - * If selected, one octet represents 8 pixels ordered vertically, - * otherwise ordered horizontally. - */ - SCREEN_INFO_MONO_VTILED = BIT(0), - /** - * If selected, the MSB represents the first pixel, - * otherwise MSB represents the last pixel. - */ - SCREEN_INFO_MONO_MSB_FIRST = BIT(1), - /** - * Electrophoretic Display. - */ - SCREEN_INFO_EPD = BIT(2), - /** - * Screen has two alternating ram buffers - */ - SCREEN_INFO_DOUBLE_BUFFER = BIT(3), -}; - -/** - * @enum display_orientation - * @brief Enumeration with possible display orientation - * - */ -enum display_orientation { - DISPLAY_ORIENTATION_NORMAL, - DISPLAY_ORIENTATION_ROTATED_90, - DISPLAY_ORIENTATION_ROTATED_180, - DISPLAY_ORIENTATION_ROTATED_270, -}; - -/** - * @struct display_capabilities - * @brief Structure holding display capabilities - * - * @var u16_t display_capabilities::x_resolution - * Display resolution in the X direction - * - * @var u16_t display_capabilities::y_resolution - * Display resolution in the Y direction - * - * @var u32_t display_capabilities::supported_pixel_formats - * Bitwise or of pixel formats supported by the display - * - * @var u32_t display_capabilities::screen_info - * Information about display panel - * - * @var enum display_pixel_format display_capabilities::current_pixel_format - * Currently active pixel format for the display - * - * @var enum display_orientation display_capabilities::current_orientation - * Current display orientation - * - */ -struct display_capabilities { - u16_t x_resolution; - u16_t y_resolution; - u32_t supported_pixel_formats; - u32_t screen_info; - enum display_pixel_format current_pixel_format; - enum display_orientation current_orientation; -}; - -/** - * @struct display_buffer_descriptor - * @brief Structure to describe display data buffer layout - * - * @var u32_t display_buffer_descriptor::buf_size - * Data buffer size in bytes - * - * @var u16_t display_buffer_descriptor::width - * Data buffer row width in pixels - * - * @var u16_t display_buffer_descriptor::height - * Data buffer column height in pixels - * - * @var u16_t display_buffer_descriptor::pitch - * Number of pixels between consecutive rows in the data buffer - * - */ -struct display_buffer_descriptor { - u32_t buf_size; - u16_t width; - u16_t height; - u16_t pitch; -}; - -/** - * @typedef display_blanking_on_api - * @brief Callback API to turn on display blanking - * See display_blanking_on() for argument description - */ -typedef int (*display_blanking_on_api)(const struct device *dev); - -/** - * @typedef display_blanking_off_api - * @brief Callback API to turn off display blanking - * See display_blanking_off() for argument description - */ -typedef int (*display_blanking_off_api)(const struct device *dev); - -/** - * @typedef display_write_api - * @brief Callback API for writing data to the display - * See display_write() for argument description - */ -typedef int (*display_write_api)(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, - const void *buf); - -/** - * @typedef display_read_api - * @brief Callback API for reading data from the display - * See display_read() for argument description - */ -typedef int (*display_read_api)(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, void *buf); - -/** - * @typedef display_get_framebuffer_api - * @brief Callback API to get framebuffer pointer - * See display_get_framebuffer() for argument description - */ -typedef void *(*display_get_framebuffer_api)(const struct device *dev); - -/** - * @typedef display_set_brightness_api - * @brief Callback API to set display brightness - * See display_set_brightness() for argument description - */ -typedef int (*display_set_brightness_api)(const struct device *dev, - const u8_t brightness); - -/** - * @typedef display_set_contrast_api - * @brief Callback API to set display contrast - * See display_set_contrast() for argument description - */ -typedef int (*display_set_contrast_api)(const struct device *dev, - const u8_t contrast); - -/** - * @typedef display_get_capabilities_api - * @brief Callback API to get display capabilities - * See display_get_capabilities() for argument description - */ -typedef void (*display_get_capabilities_api)(const struct device *dev, - struct display_capabilities * capabilities); - -/** - * @typedef display_set_pixel_format_api - * @brief Callback API to set pixel format used by the display - * See display_set_pixel_format() for argument description - */ -typedef int (*display_set_pixel_format_api)(const struct device *dev, - const enum display_pixel_format pixel_format); - -/** - * @typedef display_set_orientation_api - * @brief Callback API to set orientation used by the display - * See display_set_orientation() for argument description - */ -typedef int (*display_set_orientation_api)(const struct device *dev, - const enum display_orientation orientation); - -/** - * @brief Display driver API - * API which a display driver should expose - */ -struct display_driver_api { - display_blanking_on_api blanking_on; - display_blanking_off_api blanking_off; - display_write_api write; - display_read_api read; - display_get_framebuffer_api get_framebuffer; - display_set_brightness_api set_brightness; - display_set_contrast_api set_contrast; - display_get_capabilities_api get_capabilities; - display_set_pixel_format_api set_pixel_format; - display_set_orientation_api set_orientation; -}; -extern struct ili9340_data ili9340_data1; -extern struct display_driver_api ili9340_api1; -/** - * @brief Write data to display - * - * @param dev Pointer to device structure - * @param x x Coordinate of the upper left corner where to write the buffer - * @param y y Coordinate of the upper left corner where to write the buffer - * @param desc Pointer to a structure describing the buffer layout - * @param buf Pointer to buffer array - * - * @retval 0 on success else negative errno code. - */ -static inline int display_write(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, - const void *buf) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->write(dev, x, y, desc, buf); -} - -/** - * @brief Read data from display - * - * @param dev Pointer to device structure - * @param x x Coordinate of the upper left corner where to read from - * @param y y Coordinate of the upper left corner where to read from - * @param desc Pointer to a structure describing the buffer layout - * @param buf Pointer to buffer array - * - * @retval 0 on success else negative errno code. - */ -static inline int display_read(const struct device *dev, const u16_t x, - const u16_t y, const struct display_buffer_descriptor *desc, void *buf) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->read(dev, x, y, desc, buf); -} - -/** - * @brief Get pointer to framebuffer for direct access - * - * @param dev Pointer to device structure - * - * @retval Pointer to frame buffer or NULL if direct framebuffer access - * is not supported - * - */ -static inline void *display_get_framebuffer(const struct device *dev) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->get_framebuffer(dev); -} - -/** - * @brief Turn display blanking on - * - * @param dev Pointer to device structure - * - * @retval 0 on success else negative errno code. - */ -static inline int display_blanking_on(const struct device *dev) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->blanking_on(dev); -} - -/** - * @brief Turn display blanking off - * - * @param dev Pointer to device structure - * - * @retval 0 on success else negative errno code. - */ -static inline int display_blanking_off(const struct device *dev) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->blanking_off(dev); -} - -/** - * @brief Set the brightness of the display - * - * Set the brightness of the display in steps of 1/256, where 255 is full - * brightness and 0 is minimal. - * - * @param dev Pointer to device structure - * @param brightness Brightness in steps of 1/256 - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_brightness(const struct device *dev, - u8_t brightness) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_brightness(dev, brightness); -} - -/** - * @brief Set the contrast of the display - * - * Set the contrast of the display in steps of 1/256, where 255 is maximum - * difference and 0 is minimal. - * - * @param dev Pointer to device structure - * @param contrast Contrast in steps of 1/256 - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_contrast(const struct device *dev, u8_t contrast) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_contrast(dev, contrast); -} - -/** - * @brief Get display capabilities - * - * @param dev Pointer to device structure - * @param capabilities Pointer to capabilities structure to populate - */ -static inline void display_get_capabilities(const struct device *dev, - struct display_capabilities * capabilities) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - api->get_capabilities(dev, capabilities); -} - -/** - * @brief Set pixel format used by the display - * - * @param dev Pointer to device structure - * @param pixel_format Pixel format to be used by display - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_pixel_format(const struct device *dev, - const enum display_pixel_format pixel_format) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_pixel_format(dev, pixel_format); -} - -/** - * @brief Set display orientation - * - * @param dev Pointer to device structure - * @param orientation Orientation to be used by display - * - * @retval 0 on success else negative errno code. - */ -static inline int display_set_orientation(const struct device *dev, - const enum display_orientation orientation) -{ - struct display_driver_api *api = &ili9340_api1; - //(struct display_driver_api *)dev->driver_api; - - return api->set_orientation(dev, orientation); -} - -#ifdef __cplusplus -} -#endif - -/** - * @} - */ - -#endif /* ZEPHYR_INCLUDE_DISPLAY_H_*/ diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c deleted file mode 100644 index 6326e0ae85..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "display_ili9340.h" -#include - -//#define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL -//#include -//LOG_MODULE_REGISTER(display_ili9340); -#define LOG_ERR printf -#define LOG_DBG printf -#define LOG_WRN printf - -#include -#include -#include -#include - -struct ili9340_data { - struct device *reset_gpio; - struct device *command_data_gpio; - struct device *spi_dev; - struct spi_config spi_config; -#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER -struct spi_cs_control cs_ctrl; -#endif -}; - -struct ili9340_data ili9340_data1; - -#define ILI9340_CMD_DATA_PIN_COMMAND 0 -#define ILI9340_CMD_DATA_PIN_DATA 1 - -static void ili9340_exit_sleep(struct ili9340_data *data) -{ - ili9340_transmit(data, ILI9340_CMD_EXIT_SLEEP, NULL, 0); - //k_sleep(120); -} - -int ili9340_init() -{ - struct ili9340_data *data = &ili9340_data1; - printf("Initializing display driver\n"); - data->spi_dev = device_get_binding(DT_ILITEK_ILI9340_0_BUS_NAME); - if (data->spi_dev == NULL) { - return -EPERM; - } - data->spi_config.frequency = DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY; - data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); //SPI_OP_MODE_MASTER | SPI_WORD_SET(8); - data->spi_config.slave = DT_ILITEK_ILI9340_0_BASE_ADDRESS; - -#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER - data->cs_ctrl.gpio_dev = - device_get_binding(DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER); - data->cs_ctrl.gpio_pin = DT_ILITEK_ILI9340_0_CS_GPIO_PIN; - data->cs_ctrl.delay = 0; - data->spi_config.cs = &(data->cs_ctrl); -#else - data->spi_config.cs = NULL; -#endif - data->reset_gpio = device_get_binding( - DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER); - if (data->reset_gpio == NULL) { - return -EPERM; - } - - gpio_pin_configure(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, - GPIO_DIR_OUT); - - data->command_data_gpio = device_get_binding( - DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER); - if (data->command_data_gpio == NULL) { - return -EPERM; - } - - gpio_pin_configure(data->command_data_gpio, - DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, GPIO_DIR_OUT); - - LOG_DBG("Resetting display driver"); - gpio_pin_write(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); - k_sleep(1); - gpio_pin_write(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 0); - k_sleep(1); - gpio_pin_write(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); - k_sleep(5); - - LOG_DBG("Initializing LCD\n"); - ili9340_lcd_init(data); - - LOG_DBG("Exiting sleep mode\n"); - ili9340_exit_sleep(data); - - return 0; -} - -static void ili9340_set_mem_area(struct ili9340_data *data, const u16_t x, - const u16_t y, const u16_t w, const u16_t h) -{ - u16_t spi_data[2]; - - spi_data[0] = sys_cpu_to_be16(x); - spi_data[1] = sys_cpu_to_be16(x + w - 1); - ili9340_transmit(data, ILI9340_CMD_COLUMN_ADDR, &spi_data[0], 4); - - spi_data[0] = sys_cpu_to_be16(y); - spi_data[1] = sys_cpu_to_be16(y + h - 1); - ili9340_transmit(data, ILI9340_CMD_PAGE_ADDR, &spi_data[0], 4); -} - -static int ili9340_write(const struct device *dev, const u16_t x, const u16_t y, - const struct display_buffer_descriptor *desc, const void *buf) -{ - struct ili9340_data *data = (struct ili9340_data *) &ili9340_data1; - const u8_t *write_data_start = (u8_t *) buf; - struct spi_buf tx_buf; - struct spi_buf_set tx_bufs; - u16_t write_cnt; - u16_t nbr_of_writes; - u16_t write_h; - - __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width"); - __ASSERT((3 * desc->pitch * desc->height) <= desc->buf_size, - "Input buffer to small"); - ili9340_set_mem_area(data, x, y, desc->width, desc->height); - - if (desc->pitch > desc->width) { - write_h = 1U; - nbr_of_writes = desc->height; - } else { - write_h = desc->height; - nbr_of_writes = 1U; - } - ili9340_transmit(data, ILI9340_CMD_MEM_WRITE, (void *) write_data_start, - 3 * desc->width * write_h); - - tx_bufs.buffers = &tx_buf; - tx_bufs.count = 1; - - write_data_start += (3 * desc->pitch); - for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) { - tx_buf.buf = (void *) write_data_start; - tx_buf.len = 3 * desc->width * write_h; - spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); - write_data_start += (3 * desc->pitch); - } - - return 0; -} - -static int ili9340_read(const struct device *dev, const u16_t x, const u16_t y, - const struct display_buffer_descriptor *desc, void *buf) -{ - LOG_ERR("Reading not supported"); - return -ENOTSUP; -} - -static void *ili9340_get_framebuffer(const struct device *dev) -{ - LOG_ERR("Direct framebuffer access not supported"); - return NULL; -} - -static int ili9340_display_blanking_off(const struct device *dev) -{ - struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; - - LOG_DBG("Turning display blanking off"); - ili9340_transmit(data, ILI9340_CMD_DISPLAY_ON, NULL, 0); - return 0; -} - -static int ili9340_display_blanking_on(const struct device *dev) -{ - struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; - - LOG_DBG("Turning display blanking on"); - ili9340_transmit(data, ILI9340_CMD_DISPLAY_OFF, NULL, 0); - return 0; -} - -static int ili9340_set_brightness(const struct device *dev, - const u8_t brightness) -{ - LOG_WRN("Set brightness not implemented"); - return -ENOTSUP; -} - -static int ili9340_set_contrast(const struct device *dev, const u8_t contrast) -{ - LOG_ERR("Set contrast not supported"); - return -ENOTSUP; -} - -static int ili9340_set_pixel_format(const struct device *dev, - const enum display_pixel_format pixel_format) -{ - if (pixel_format == PIXEL_FORMAT_RGB_888) { - return 0; -} - LOG_ERR("Pixel format change not implemented"); - return -ENOTSUP; -} - -static int ili9340_set_orientation(const struct device *dev, - const enum display_orientation orientation) -{ -if (orientation == DISPLAY_ORIENTATION_NORMAL) { - return 0; -} -LOG_ERR("Changing display orientation not implemented"); - return -ENOTSUP; -} - -static void ili9340_get_capabilities(const struct device *dev, - struct display_capabilities *capabilities) -{ - memset(capabilities, 0, sizeof(struct display_capabilities)); - capabilities->x_resolution = 320; - capabilities->y_resolution = 240; - capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_888; - capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888; - capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; -} - -void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, - size_t tx_len) -{ - int i; - char * buf1 = tx_data; - data = (struct ili9340_data *) &ili9340_data1; - struct spi_buf tx_buf = { .buf = &cmd, .len = 1 }; - struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; - - gpio_pin_write(data->command_data_gpio, DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, - ILI9340_CMD_DATA_PIN_COMMAND); - spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); - if (tx_data != NULL) { - tx_buf.buf = tx_data; - tx_buf.len = tx_len; - gpio_pin_write(data->command_data_gpio, - DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, - ILI9340_CMD_DATA_PIN_DATA); - spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); - } -} - -struct display_driver_api ili9340_api1 = - { .blanking_on = ili9340_display_blanking_on, .blanking_off = - ili9340_display_blanking_off, .write = ili9340_write, .read = - ili9340_read, .get_framebuffer = ili9340_get_framebuffer, - .set_brightness = ili9340_set_brightness, .set_contrast = - ili9340_set_contrast, .get_capabilities = - ili9340_get_capabilities, .set_pixel_format = - ili9340_set_pixel_format, .set_orientation = - ili9340_set_orientation, }; - -/* - DEVICE_AND_API_INIT(ili9340, DT_ILITEK_ILI9340_0_LABEL, &ili9340_init, - &ili9340_data, NULL, APPLICATION, - CONFIG_APPLICATION_INIT_PRIORITY, &ili9340_api); - */ diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h deleted file mode 100644 index 8aab97a651..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ -#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ -#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ -#include "board_config.h" -#include -#include - -#define ILI9340_CMD_ENTER_SLEEP 0x10 -#define ILI9340_CMD_EXIT_SLEEP 0x11 -#define ILI9340_CMD_GAMMA_SET 0x26 -#define ILI9340_CMD_DISPLAY_OFF 0x28 -#define ILI9340_CMD_DISPLAY_ON 0x29 -#define ILI9340_CMD_COLUMN_ADDR 0x2a -#define ILI9340_CMD_PAGE_ADDR 0x2b -#define ILI9340_CMD_MEM_WRITE 0x2c -#define ILI9340_CMD_MEM_ACCESS_CTRL 0x36 -#define ILI9340_CMD_PIXEL_FORMAT_SET 0x3A -#define ILI9340_CMD_FRAME_CTRL_NORMAL_MODE 0xB1 -#define ILI9340_CMD_DISPLAY_FUNCTION_CTRL 0xB6 -#define ILI9340_CMD_POWER_CTRL_1 0xC0 -#define ILI9340_CMD_POWER_CTRL_2 0xC1 -#define ILI9340_CMD_VCOM_CTRL_1 0xC5 -#define ILI9340_CMD_VCOM_CTRL_2 0xC7 -#define ILI9340_CMD_POSITVE_GAMMA_CORRECTION 0xE0 -#define ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION 0xE1 - -#define ILI9340_DATA_MEM_ACCESS_CTRL_MY 0x80 -#define ILI9340_DATA_MEM_ACCESS_CTRL_MX 0x40 -#define ILI9340_DATA_MEM_ACCESS_CTRL_MV 0x20 -#define ILI9340_DATA_MEM_ACCESS_CTRL_ML 0x10 -#define ILI9340_DATA_MEM_ACCESS_CTRL_BGR 0x08 -#define ILI9340_DATA_MEM_ACCESS_CTRL_MH 0x04 - -#define ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT 0x60 -#define ILI9340_DATA_PIXEL_FORMAT_RGB_16_BIT 0x50 -#define ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT 0x06 -#define ILI9340_DATA_PIXEL_FORMAT_MCU_16_BIT 0x05 - -struct ili9340_data; - -/** - * Send data to ILI9340 display controller - * - * @param data Device data structure - * @param cmd Command to send to display controller - * @param tx_data Data to transmit to the display controller - * In case no data should be transmitted pass a NULL pointer - * @param tx_len Number of bytes in tx_data buffer - * - */ -void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, - size_t tx_len); - -/** - * Perform LCD specific initialization - * - * @param data Device data structure - */ -void ili9340_lcd_init(struct ili9340_data *data); - -#define DT_ILITEK_ILI9340_0_LABEL "DISPLAY" -#define CONFIG_DISPLAY_LOG_LEVEL 0 - -#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ */ diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c deleted file mode 100644 index a8581a72d3..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2017 Jan Van Winkel - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "display_ili9340.h" - -void ili9340_lcd_init(struct ili9340_data *data) -{ - u8_t tx_data[15]; - - tx_data[0] = 0x23; - ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_1, tx_data, 1); - - tx_data[0] = 0x10; - ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_2, tx_data, 1); - - tx_data[0] = 0x3e; - tx_data[1] = 0x28; - ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_1, tx_data, 2); - - tx_data[0] = 0x86; - ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_2, tx_data, 1); - - tx_data[0] = - ILI9340_DATA_MEM_ACCESS_CTRL_MV | ILI9340_DATA_MEM_ACCESS_CTRL_BGR; - ili9340_transmit(data, ILI9340_CMD_MEM_ACCESS_CTRL, tx_data, 1); - - tx_data[0] = ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT | - ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT; - ili9340_transmit(data, ILI9340_CMD_PIXEL_FORMAT_SET, tx_data, 1); - - tx_data[0] = 0x00; - tx_data[1] = 0x18; - ili9340_transmit(data, ILI9340_CMD_FRAME_CTRL_NORMAL_MODE, tx_data, 2); - - tx_data[0] = 0x08; - tx_data[1] = 0x82; - tx_data[2] = 0x27; - ili9340_transmit(data, ILI9340_CMD_DISPLAY_FUNCTION_CTRL, tx_data, 3); - - tx_data[0] = 0x01; - ili9340_transmit(data, ILI9340_CMD_GAMMA_SET, tx_data, 1); - - tx_data[0] = 0x0F; - tx_data[1] = 0x31; - tx_data[2] = 0x2B; - tx_data[3] = 0x0C; - tx_data[4] = 0x0E; - tx_data[5] = 0x08; - tx_data[6] = 0x4E; - tx_data[7] = 0xF1; - tx_data[8] = 0x37; - tx_data[9] = 0x07; - tx_data[10] = 0x10; - tx_data[11] = 0x03; - tx_data[12] = 0x0E; - tx_data[13] = 0x09; - tx_data[14] = 0x00; - ili9340_transmit(data, ILI9340_CMD_POSITVE_GAMMA_CORRECTION, tx_data, 15); - - tx_data[0] = 0x00; - tx_data[1] = 0x0E; - tx_data[2] = 0x14; - tx_data[3] = 0x03; - tx_data[4] = 0x11; - tx_data[5] = 0x07; - tx_data[6] = 0x31; - tx_data[7] = 0xC1; - tx_data[8] = 0x48; - tx_data[9] = 0x08; - tx_data[10] = 0x0F; - tx_data[11] = 0x0C; - tx_data[12] = 0x31; - tx_data[13] = 0x36; - tx_data[14] = 0x0F; - ili9340_transmit(data, ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION, tx_data, 15); -} diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c deleted file mode 100644 index d537db6d39..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#include -#include -#include "display_indev.h" -#include "display.h" -#include "wasm_export.h" -#include "app_manager_export.h" - -#define MONITOR_HOR_RES 320 -#define MONITOR_VER_RES 240 -#ifndef MONITOR_ZOOM -#define MONITOR_ZOOM 1 -#endif - -static int lcd_initialized = 0; - -void -display_init(wasm_exec_env_t exec_env) -{ - if (lcd_initialized != 0) { - return; - } - lcd_initialized = 1; - xpt2046_init(); - ili9340_init(); - display_blanking_off(NULL); -} - -void -display_flush(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - int32 color_p_offset) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) - return; - lv_color_t * color_p = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - - u16_t w = x2 - x1 + 1; - u16_t h = y2 - y1 + 1; - struct display_buffer_descriptor desc; - - desc.buf_size = 3 * w * h; - desc.width = w; - desc.pitch = w; - desc.height = h; - display_write(NULL, x1, y1, &desc, (void *) color_p); - - /*lv_flush_ready();*/ -} - -void -display_fill(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p) -{ -} - -void -display_map(wasm_exec_env_t exec_env, - int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ -} - -bool -display_input_read(wasm_exec_env_t exec_env, int32 data_p_offset) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, data_p_offset, 1)) - return false; - lv_indev_data_t * data = wasm_runtime_addr_app_to_native(module_inst, - data_p_offset); - - return touchscreen_read(data); -} - -void -display_deinit(wasm_exec_env_t exec_env) -{ -} - -void -display_vdb_write(wasm_exec_env_t exec_env, - int32 buf_offset, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, int32 color_p_offset, lv_opa_t opa) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) - return; - lv_color_t *color = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - - void *buf = wasm_runtime_addr_app_to_native(module_inst, buf_offset); - - u8_t *buf_xy = buf + 3 * x + 3 * y * buf_w; - /* - if (opa != LV_OPA_COVER) { - lv_color_t mix_color; - - mix_color.red = *buf_xy; - mix_color.green = *(buf_xy+1); - mix_color.blue = *(buf_xy+2); - color = lv_color_mix(color, mix_color, opa); - } - */ - *buf_xy = color->red; - *(buf_xy + 1) = color->green; - *(buf_xy + 2) = color->blue; -} - -int -time_get_ms(wasm_exec_env_t exec_env) -{ - return k_uptime_get_32(); -} - diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c deleted file mode 100644 index fd991a3c4e..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#include "bh_platform.h" -#include "runtime_lib.h" -#include "native_interface.h" -#include "app_manager_export.h" -#include "board_config.h" -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "runtime_sensor.h" -#include "bi-inc/attr_container.h" -#include "module_wasm_app.h" -#include "wasm_export.h" - -extern void init_sensor_framework(); -extern void exit_sensor_framework(); -extern int aee_host_msg_callback(void *msg, uint16_t msg_len); - -#include -#include -#include - -int uart_char_cnt = 0; - -static void uart_irq_callback(struct device *dev) -{ - unsigned char ch; - int size = 0; - - while (uart_poll_in(dev, &ch) == 0) { - uart_char_cnt++; - aee_host_msg_callback(&ch, 1); - } -} - -struct device *uart_dev = NULL; - -static bool host_init() -{ - uart_dev = device_get_binding(HOST_DEVICE_COMM_UART_NAME); - if (!uart_dev) { - printf("UART: Device driver not found.\n"); - return false; - } - uart_irq_rx_enable(uart_dev); - uart_irq_callback_set(uart_dev, uart_irq_callback); - return true; -} - -int host_send(void * ctx, const char *buf, int size) -{ - if (!uart_dev) - return 0; - - for (int i = 0; i < size; i++) - uart_poll_out(uart_dev, buf[i]); - - return size; -} - -void host_destroy() -{ -} - -host_interface interface = { - .init = host_init, - .send = host_send, - .destroy = host_destroy -}; - -timer_ctx_t timer_ctx; - -static char global_heap_buf[370 * 1024] = { 0 }; - -extern void display_init(void); - -int iwasm_main() -{ - korp_thread tid, tm_tid; - - host_init(); - - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) - != 0) { - printf("Init global heap failed.\n"); - return -1; - } - - if (vm_thread_sys_init() != 0) { - goto fail1; - } - - display_init(); - - // timer manager - init_wasm_timer(); - - // TODO: - app_manager_startup(&interface); - -fail1: - bh_memory_destroy(); - return -1; -} diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c deleted file mode 100644 index 231073fa57..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include -#include -#include "bh_platform.h" -#include "bh_assert.h" -#include "bh_log.h" -#include "bh_platform_log.h" -#include "wasm_export.h" -#include "bh_memory.h" -extern void display_init(void); -extern int iwasm_main(); -void main(void) -{ - display_init(); - iwasm_main(); - for(;;){ - k_sleep(1000); - } -} - diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h deleted file mode 100644 index 3cde4ec140..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#ifndef __PIN_CONFIG_JLF_H__ -#define __PIN_CONFIG_JLF_H__ - -#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_2" -#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 10*1000 - -#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIO_0" -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 5 -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIO_0" -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 4 - -#define XPT2046_SPI_DEVICE_NAME "SPI_2" -#define XPT2046_SPI_MAX_FREQUENCY 10*1000 -#define XPT2046_CS_GPIO_CONTROLLER "GPIO_0" -#define XPT2046_CS_GPIO_PIN 6 - -#define XPT2046_PEN_GPIO_CONTROLLER "GPIO_0" -#define XPT2046_PEN_GPIO_PIN 7 - -#define HOST_DEVICE_COMM_UART_NAME "UART_1" -#endif /* __PIN_CONFIG_JLF_H__ */ diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h deleted file mode 100644 index fba36fab47..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -#ifndef __PIN_CONFIG_STM32_H__ -#define __PIN_CONFIG_STM32_H__ - -#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_1" -#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 24*1000*1000 - -#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIOC" -#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 12 -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIOC" -#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 11 - -#define DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER "GPIOC" -#define DT_ILITEK_ILI9340_0_CS_GPIO_PIN 10 - -#define XPT2046_SPI_DEVICE_NAME "SPI_1" -#define XPT2046_SPI_MAX_FREQUENCY 12*1000*1000 -#define XPT2046_CS_GPIO_CONTROLLER "GPIOD" -#define XPT2046_CS_GPIO_PIN 0 - -#define XPT2046_PEN_GPIO_CONTROLLER "GPIOD" -#define XPT2046_PEN_GPIO_PIN 1 - -#define HOST_DEVICE_COMM_UART_NAME "UART_6" - -#endif /* __PIN_CONFIG_STM32_H__ */ diff --git a/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt b/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt deleted file mode 100644 index 9da3340378..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required(VERSION 3.8.2) - -include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) -project(NONE) - -set (WAMR_BUILD_PLATFORM "zephyr") - -enable_language (ASM) - -# Build as THUMB by default -# change to "ARM[sub]", "THUMB[sub]", "X86_32", "MIPS_32" or "XTENSA_32" -# if we want to support arm_32, x86, mips or xtensa -if (NOT DEFINED WAMR_BUILD_TARGET) - set (WAMR_BUILD_TARGET "THUMBV7") -endif () - -if (NOT DEFINED WAMR_BUILD_INTERP) - # Enable Interpreter by default - set (WAMR_BUILD_INTERP 1) -endif () - -if (NOT DEFINED WAMR_BUILD_AOT) - set (WAMR_BUILD_AOT 1) -endif () - -if (NOT DEFINED WAMR_BUILD_JIT) - # Disable JIT by default. - set (WAMR_BUILD_JIT 0) -endif () - -if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) - set (WAMR_BUILD_LIBC_BUILTIN 1) -endif () - -if (NOT DEFINED WAMR_BUILD_LIBC_WASI) - set (WAMR_BUILD_LIBC_WASI 0) -endif () - -if (NOT DEFINED WAMR_BUILD_APP_FRAMEWORK) - set (WAMR_BUILD_APP_FRAMEWORK 1) -endif () - -if (NOT DEFINED WAMR_BUILD_APP_LIST) - set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_SENSOR WAMR_APP_BUILD_CONNECTION) -endif () - -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) -include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) - -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr) - -set (LVGL_DRV_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340_adafruit_1480.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_indev.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/XPT2046.c - ) - -target_sources(app PRIVATE - ${WAMR_RUNTIME_LIB_SOURCE} - ${LVGL_DRV_SRCS} - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/main.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/iwasm_main.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/ext_lib_export.c - ) diff --git a/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf b/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf deleted file mode 100644 index 4117f0fb5e..0000000000 --- a/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG_SPI=y -CONFIG_SPI_STM32=y -CONFIG_SPI_1=y -CONFIG_PRINTK=y -CONFIG_LOG=y -#CONFIG_UART_2=y -CONFIG_UART_INTERRUPT_DRIVEN=y -CONFIG_STACK_SENTINEL=y -CONFIG_MAIN_STACK_SIZE=2048 -CONFIG_ARM_MPU=n diff --git a/samples/littlevgl/wamr_config_littlevgl.cmake b/samples/littlevgl/wamr_config_littlevgl.cmake deleted file mode 100644 index ae0ed78ecc..0000000000 --- a/samples/littlevgl/wamr_config_littlevgl.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set (WAMR_BUILD_PLATFORM "linux") -set (WAMR_BUILD_TARGET X86_64) -set (WAMR_BUILD_INTERP 1) -set (WAMR_BUILD_AOT 1) -set (WAMR_BUILD_JIT 0) -set (WAMR_BUILD_LIBC_BUILTIN 0) -set (WAMR_BUILD_LIBC_WASI 1) -set (WAMR_BUILD_APP_FRAMEWORK 1) -set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_SENSOR WAMR_APP_BUILD_CONNECTION) diff --git a/samples/littlevgl/wasm-apps/Makefile_wasm_app b/samples/littlevgl/wasm-apps/Makefile_wasm_app deleted file mode 100644 index 3ec33520a7..0000000000 --- a/samples/littlevgl/wasm-apps/Makefile_wasm_app +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -CC = /opt/wasi-sdk/bin/clang -LVGL_DIR = ${shell pwd} -SDK_DIR = $(LVGL_DIR)/../../../wamr-sdk/out/littlevgl/app-sdk -APP_FRAMEWORK_DIR = $(SDK_DIR)/wamr-app-framework - -CFLAGS += -O3 \ - -I$(LVGL_DIR) \ - -I$(LVGL_DIR)/lvgl \ - -I$(LVGL_DIR)/lv_drivers \ - -I$(LVGL_DIR)/src \ - -I$(LVGL_DIR)/../lv_config \ - -I$(APP_FRAMEWORK_DIR)/include - -SRCS += lvgl/lv_draw/lv_draw_line.c lvgl/lv_draw/lv_draw_rbasic.c -SRCS += lvgl/lv_draw/lv_draw_img.c lvgl/lv_draw/lv_draw_arc.c -SRCS += lvgl/lv_draw/lv_draw_rect.c lvgl/lv_draw/lv_draw_triangle.c -SRCS += lvgl/lv_draw/lv_draw.c lvgl/lv_draw/lv_draw_label.c -SRCS += lvgl/lv_draw/lv_draw_vbasic.c lvgl/lv_fonts/lv_font_builtin.c -SRCS += lvgl/lv_fonts/lv_font_dejavu_20.c -SRCS += lvgl/lv_objx/lv_img.c -SRCS += lvgl/lv_objx/lv_roller.c lvgl/lv_objx/lv_cb.c lvgl/lv_objx/lv_led.c lvgl/lv_objx/lv_cont.c -SRCS += lvgl/lv_objx/lv_calendar.c lvgl/lv_objx/lv_gauge.c lvgl/lv_objx/lv_page.c -SRCS += lvgl/lv_objx/lv_list.c lvgl/lv_objx/lv_bar.c lvgl/lv_objx/lv_tabview.c -SRCS += lvgl/lv_objx/lv_mbox.c lvgl/lv_objx/lv_objx_templ.c lvgl/lv_objx/lv_sw.c -SRCS += lvgl/lv_objx/lv_label.c lvgl/lv_objx/lv_slider.c lvgl/lv_objx/lv_ddlist.c -SRCS += lvgl/lv_objx/lv_imgbtn.c lvgl/lv_objx/lv_line.c lvgl/lv_objx/lv_chart.c -SRCS += lvgl/lv_objx/lv_btnm.c lvgl/lv_objx/lv_arc.c lvgl/lv_objx/lv_preload.c -SRCS += lvgl/lv_objx/lv_win.c lvgl/lv_objx/lv_lmeter.c lvgl/lv_objx/lv_btn.c -SRCS += lvgl/lv_objx/lv_ta.c lvgl/lv_misc/lv_log.c lvgl/lv_misc/lv_fs.c -SRCS += lvgl/lv_misc/lv_task.c lvgl/lv_misc/lv_circ.c lvgl/lv_misc/lv_anim.c -SRCS += lvgl/lv_misc/lv_color.c lvgl/lv_misc/lv_txt.c lvgl/lv_misc/lv_math.c -SRCS += lvgl/lv_misc/lv_mem.c lvgl/lv_misc/lv_font.c lvgl/lv_misc/lv_ll.c -SRCS += lvgl/lv_misc/lv_area.c lvgl/lv_misc/lv_templ.c lvgl/lv_misc/lv_ufs.c -SRCS += lvgl/lv_misc/lv_gc.c -SRCS += lvgl/lv_hal/lv_hal_tick.c lvgl/lv_hal/lv_hal_indev.c lvgl/lv_hal/lv_hal_disp.c -SRCS += lvgl/lv_themes/lv_theme_mono.c lvgl/lv_themes/lv_theme_templ.c -SRCS += lvgl/lv_themes/lv_theme_material.c lvgl/lv_themes/lv_theme.c -SRCS += lvgl/lv_themes/lv_theme_night.c lvgl/lv_themes/lv_theme_zen.c lvgl/lv_themes/lv_theme_nemo.c -SRCS += lvgl/lv_themes/lv_theme_alien.c lvgl/lv_themes/lv_theme_default.c -SRCS += lvgl/lv_core/lv_group.c lvgl/lv_core/lv_style.c lvgl/lv_core/lv_indev.c -SRCS += lvgl/lv_core/lv_vdb.c lvgl/lv_core/lv_obj.c lvgl/lv_core/lv_refr.c -SRCS += $(LVGL_DIR)/src/main.c - -all: - @$(CC) $(CFLAGS) $(SRCS) \ - -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ - -DLV_CONF_INCLUDE_SIMPLE \ - -L$(APP_FRAMEWORK_DIR)/lib -lapp_framework \ - -Wl,--allow-undefined \ - -Wl,--no-threads,--strip-all,--no-entry \ - -Wl,--export=on_init -Wl,--export=on_timer_callback \ - -Wl,--export=__heap_base,--export=__data_end \ - -o ui_app.wasm diff --git a/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi b/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi deleted file mode 100644 index 05a1762d5b..0000000000 --- a/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -CC = /opt/wasi-sdk/bin/clang -LVGL_DIR = ${shell pwd} -WAMR_DIR = ${LVGL_DIR}/../../.. -SDK_DIR = $(LVGL_DIR)/../../../wamr-sdk/out/littlevgl/app-sdk -APP_FRAMEWORK_DIR = $(SDK_DIR)/wamr-app-framework - -CFLAGS += -O3 \ - -I$(LVGL_DIR) \ - -I$(LVGL_DIR)/lvgl \ - -I$(LVGL_DIR)/lv_drivers \ - -I$(LVGL_DIR)/src \ - -I$(LVGL_DIR)/../lv_config \ - -I$(APP_FRAMEWORK_DIR)/include - -SRCS += lvgl/lv_draw/lv_draw_line.c lvgl/lv_draw/lv_draw_rbasic.c -SRCS += lvgl/lv_draw/lv_draw_img.c lvgl/lv_draw/lv_draw_arc.c -SRCS += lvgl/lv_draw/lv_draw_rect.c lvgl/lv_draw/lv_draw_triangle.c -SRCS += lvgl/lv_draw/lv_draw.c lvgl/lv_draw/lv_draw_label.c -SRCS += lvgl/lv_draw/lv_draw_vbasic.c lvgl/lv_fonts/lv_font_builtin.c -SRCS += lvgl/lv_fonts/lv_font_dejavu_20.c -SRCS += lvgl/lv_objx/lv_img.c -SRCS += lvgl/lv_objx/lv_roller.c lvgl/lv_objx/lv_cb.c lvgl/lv_objx/lv_led.c lvgl/lv_objx/lv_cont.c -SRCS += lvgl/lv_objx/lv_calendar.c lvgl/lv_objx/lv_gauge.c lvgl/lv_objx/lv_page.c -SRCS += lvgl/lv_objx/lv_list.c lvgl/lv_objx/lv_bar.c lvgl/lv_objx/lv_tabview.c -SRCS += lvgl/lv_objx/lv_mbox.c lvgl/lv_objx/lv_objx_templ.c lvgl/lv_objx/lv_sw.c -SRCS += lvgl/lv_objx/lv_label.c lvgl/lv_objx/lv_slider.c lvgl/lv_objx/lv_ddlist.c -SRCS += lvgl/lv_objx/lv_imgbtn.c lvgl/lv_objx/lv_line.c lvgl/lv_objx/lv_chart.c -SRCS += lvgl/lv_objx/lv_btnm.c lvgl/lv_objx/lv_arc.c lvgl/lv_objx/lv_preload.c -SRCS += lvgl/lv_objx/lv_win.c lvgl/lv_objx/lv_lmeter.c lvgl/lv_objx/lv_btn.c -SRCS += lvgl/lv_objx/lv_ta.c lvgl/lv_misc/lv_log.c lvgl/lv_misc/lv_fs.c -SRCS += lvgl/lv_misc/lv_task.c lvgl/lv_misc/lv_circ.c lvgl/lv_misc/lv_anim.c -SRCS += lvgl/lv_misc/lv_color.c lvgl/lv_misc/lv_txt.c lvgl/lv_misc/lv_math.c -SRCS += lvgl/lv_misc/lv_mem.c lvgl/lv_misc/lv_font.c lvgl/lv_misc/lv_ll.c -SRCS += lvgl/lv_misc/lv_area.c lvgl/lv_misc/lv_templ.c lvgl/lv_misc/lv_ufs.c -SRCS += lvgl/lv_misc/lv_gc.c -SRCS += lvgl/lv_hal/lv_hal_tick.c lvgl/lv_hal/lv_hal_indev.c lvgl/lv_hal/lv_hal_disp.c -SRCS += lvgl/lv_themes/lv_theme_mono.c lvgl/lv_themes/lv_theme_templ.c -SRCS += lvgl/lv_themes/lv_theme_material.c lvgl/lv_themes/lv_theme.c -SRCS += lvgl/lv_themes/lv_theme_night.c lvgl/lv_themes/lv_theme_zen.c lvgl/lv_themes/lv_theme_nemo.c -SRCS += lvgl/lv_themes/lv_theme_alien.c lvgl/lv_themes/lv_theme_default.c -SRCS += lvgl/lv_core/lv_group.c lvgl/lv_core/lv_style.c lvgl/lv_core/lv_indev.c -SRCS += lvgl/lv_core/lv_vdb.c lvgl/lv_core/lv_obj.c lvgl/lv_core/lv_refr.c -SRCS += $(LVGL_DIR)/src/main.c - -all: - @$(CC) $(CFLAGS) $(SRCS) \ - --target=wasm32 -Wl,--allow-undefined \ - --sysroot=$(WAMR_DIR)/wamr-sdk/app/libc-builtin-sysroot \ - -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ - -DLV_CONF_INCLUDE_SIMPLE \ - -L$(APP_FRAMEWORK_DIR)/lib -lapp_framework \ - -Wl,--allow-undefined \ - -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ - -Wl,--export=on_init -Wl,--export=on_timer_callback \ - -o ui_app_no_wasi.wasm diff --git a/samples/littlevgl/wasm-apps/build_wasm_app.sh b/samples/littlevgl/wasm-apps/build_wasm_app.sh deleted file mode 100755 index 07bfa964e1..0000000000 --- a/samples/littlevgl/wasm-apps/build_wasm_app.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -WAMR_DIR=${PWD}/../../.. - -if [ -z $KW_BUILD ] || [ -z $KW_OUT_FILE ];then - echo "Local Build Env" - makewrap="make" -else - echo "Klocwork Build Env" - makewrap="kwinject -o $KW_OUT_FILE make" -fi - -if [ ! -d "lvgl" ]; then - git clone https://github.com/littlevgl/lvgl.git --branch v5.3 -fi -$makewrap -f Makefile_wasm_app -$makewrap -f Makefile_wasm_app_no_wasi - diff --git a/samples/littlevgl/wasm-apps/src/display_indev.h b/samples/littlevgl/wasm-apps/src/display_indev.h deleted file mode 100644 index fe07d9ab69..0000000000 --- a/samples/littlevgl/wasm-apps/src/display_indev.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef DISPLAY_INDEV_H_ -#define DISPLAY_INDEV_H_ -#include -#include - -#include "lvgl/lv_misc/lv_color.h" -#include "lvgl/lv_hal/lv_hal_indev.h" -extern void display_init(void); -extern void display_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); -extern bool display_input_read(lv_indev_data_t * data); -extern void display_deinit(void); -extern void display_vdb_write(void *buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, lv_color_t *color, lv_opa_t opa); -extern uint32_t time_get_ms(void); - -#endif diff --git a/samples/littlevgl/wasm-apps/src/main.c b/samples/littlevgl/wasm-apps/src/main.c deleted file mode 100644 index f13e76f8cd..0000000000 --- a/samples/littlevgl/wasm-apps/src/main.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -/** - * @file main - * - */ - -/********************* - * INCLUDES - *********************/ -#include -//#include -#include -#include "lvgl/lvgl.h" -#include "display_indev.h" -#include "wasm_app.h" -#include "wa-inc/timer_wasm_app.h" -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void hal_init(void); -//static int tick_thread(void * data); -//static void memory_monitor(void * param); - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ -uint32_t count = 0; -char count_str[11] = { 0 }; -lv_obj_t *hello_world_label; -lv_obj_t *count_label; -lv_obj_t * btn1; - -lv_obj_t * label_count1; -int label_count1_value = 0; -char label_count1_str[11] = { 0 }; - -void timer1_update(user_timer_t timer1) -{ - if ((count % 100) == 0) { - snprintf(count_str, sizeof(count_str), "%d", count / 100); - lv_label_set_text(count_label, count_str); - } - lv_task_handler(); - ++count; -} - - -static lv_res_t btn_rel_action(lv_obj_t * btn) -{ - label_count1_value++; - snprintf(label_count1_str, sizeof(label_count1_str), - "%d", label_count1_value); - lv_label_set_text(label_count1, label_count1_str); - return LV_RES_OK; -} - - -void on_init() -{ - /*Initialize LittlevGL*/ - lv_init(); - - /*Initialize the HAL (display, input devices, tick) for LittlevGL*/ - hal_init(); - - hello_world_label = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text(hello_world_label, "Hello world!"); - lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); - - count_label = lv_label_create(lv_scr_act(), NULL); - lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); - - btn1 = lv_btn_create(lv_scr_act(), NULL); /*Create a button on the currently loaded screen*/ - lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, btn_rel_action); /*Set function to be called when the button is released*/ - lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 20); /*Align below the label*/ - - /*Create a label on the button*/ - lv_obj_t * btn_label = lv_label_create(btn1, NULL); - lv_label_set_text(btn_label, "Click ++"); - - label_count1 = lv_label_create(lv_scr_act(), NULL); - lv_label_set_text(label_count1, "0"); - lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); - - /* set up a timer */ - user_timer_t timer; - timer = api_timer_create(10, true, false, timer1_update); - if (timer) - api_timer_restart(timer, 10); - else - printf("Fail to create timer.\n"); -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -/** - * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library - */ -void display_flush_wrapper(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) -{ - display_flush(x1, y1, x2, y2, color_p); - lv_flush_ready(); -} -void display_vdb_write_wrapper(uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, lv_color_t color, lv_opa_t opa) -{ - display_vdb_write(buf, buf_w, x, y, &color, opa); -} -extern void display_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p); -extern void display_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); -static void hal_init(void) -{ - /* Add a display*/ - lv_disp_drv_t disp_drv; - lv_disp_drv_init(&disp_drv); /*Basic initialization*/ - disp_drv.disp_flush = display_flush_wrapper; /*Used when `LV_VDB_SIZE != 0` in lv_conf.h (buffered drawing)*/ - disp_drv.disp_fill = display_fill; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ - disp_drv.disp_map = display_map; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ -#if LV_VDB_SIZE != 0 - disp_drv.vdb_wr = display_vdb_write_wrapper; -#endif - lv_disp_drv_register(&disp_drv); - - /* Add the mouse as input device - * Use the 'mouse' driver which reads the PC's mouse*/ -// mouse_init(); - lv_indev_drv_t indev_drv; - lv_indev_drv_init(&indev_drv); /*Basic initialization*/ - indev_drv.type = LV_INDEV_TYPE_POINTER; - indev_drv.read = display_input_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ - lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv); - -} - diff --git a/samples/littlevgl/wasm-apps/src/system_header.h b/samples/littlevgl/wasm-apps/src/system_header.h deleted file mode 100644 index 8d943c8713..0000000000 --- a/samples/littlevgl/wasm-apps/src/system_header.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include - -uint32_t time_get_ms(void); diff --git a/samples/mem-allocator/CMakeLists.txt b/samples/mem-allocator/CMakeLists.txt new file mode 100644 index 0000000000..9f3e49db9c --- /dev/null +++ b/samples/mem-allocator/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2023 Midokura Japan KK. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(mem_allocator_create) + +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if(APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif() + +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_LIBC_BUILTIN 0) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +add_executable(mem_alloc_test main.c) + +target_link_libraries(mem_alloc_test vmlib -lm -lpthread) diff --git a/samples/mem-allocator/main.c b/samples/mem-allocator/main.c new file mode 100644 index 0000000000..a309d2e62e --- /dev/null +++ b/samples/mem-allocator/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include + +#include "mem_alloc.h" + +char store[1000]; + +int +main(int argc, char **argv) +{ + mem_allocator_t a = mem_allocator_create(store, sizeof(store)); + uint8_t *p; + uint8_t *p2; + + p = mem_allocator_malloc(a, 256); + printf("%p\n", p); + if (p == NULL) { + exit(1); + } + p = mem_allocator_realloc(a, p, 256 + 12); + printf("%p\n", p); + if (p == NULL) { + exit(1); + } + + /* + * write some values to confuse the ems allocator. + * + * hmu = p + 256 + * hmu_set_ut(hmu, HMU_FC) + * hmu_set_size(hmu, 256) + * hmu_set_free_size(hmu) + */ + *(uint32_t *)(p + 256) = (1 << 30) | 0x20; + *(uint32_t *)(p + 256 + 12 - 4) = 12; + + p2 = mem_allocator_malloc(a, 256); + printf("%p\n", p2); + if (p2 == NULL) { + exit(1); + } + mem_allocator_free(a, p2); + + p2 = mem_allocator_malloc(a, 256); + printf("%p\n", p2); + if (p2 == NULL) { + exit(1); + } + mem_allocator_free(a, p2); + + mem_allocator_free(a, p); +} diff --git a/samples/multi-module/CMakeLists.txt b/samples/multi-module/CMakeLists.txt new file mode 100644 index 0000000000..c823e517bc --- /dev/null +++ b/samples/multi-module/CMakeLists.txt @@ -0,0 +1,211 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +project(multi_module) + +################ 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) +if (NOT DEFINED WAMR_BUILD_AOT) + set(WAMR_BUILD_AOT 0) +endif () +if (NOT DEFINED WAMR_BUILD_JIT) + set(WAMR_BUILD_JIT 0) +endif () +if (NOT DEFINED WAMR_BUILD_DUMP_CALL_STACK) + set(WAMR_BUILD_DUMP_CALL_STACK 0) +endif () +if (NOT DEFINED WAMR_BUILD_GC) + set(WAMR_BUILD_GC 0) +endif () +set(WAMR_BUILD_SIMD 1) +set(WAMR_BUILD_REF_TYPES 1) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_MULTI_MODULE 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 () + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + +################ application related ################ + +################ WASM MODULES +include(ExternalProject) + +message(CHECK_START "Detecting WASI-SDK") +if(NOT (DEFINED WASI_SDK_DIR OR DEFINED CACHE{WASI_SDK_DIR})) + find_path(WASI_SDK_PARENT + wasi-sdk + PATHS /opt + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(WASI_SDK_PARENT) + set(WASI_SDK_DIR ${WASI_SDK_PARENT}/wasi-sdk) + endif() +endif() +if(WASI_SDK_DIR) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +message(CHECK_START "Detecting WASI_TOOLCHAIN_FILE at ${WASI_SDK_DIR}") +find_file(WASI_TOOLCHAIN_FILE + wasi-sdk.cmake + PATHS "${WASI_SDK_DIR}/share/cmake" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASI_TOOLCHAIN_FILE) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +message(CHECK_START "Detecting WASI_SYS_ROOT at ${WASI_SDK_DIR}") +find_path(WASI_SYS_ROOT + wasi-sysroot + PATHS "${WASI_SDK_DIR}/share" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASI_SYS_ROOT) + message(CHECK_PASS "found") + set(WASI_SYS_ROOT ${WASI_SYS_ROOT}/wasi-sysroot) +else() + message(CHECK_FAIL "not found") +endif() + +if((NOT EXISTS ${WASI_SDK_DIR}) OR (NOT EXISTS ${WASI_TOOLCHAIN_FILE}) OR (NOT EXISTS ${WASI_SYS_ROOT})) + message(FATAL_ERROR "Please set the absolute path of wasi-sdk with \'cmake -DWASI_SDK_DIR=XXX\'") +else() + message(STATUS "WASI_SDK_DIR is ${WASI_SDK_DIR}") + message(STATUS "WASI_TOOLCHAIN_FILE is ${WASI_TOOLCHAIN_FILE}") + message(STATUS "WASI_SYS_ROOT is ${WASI_SYS_ROOT}") +endif() + +# .c -> .wasm +ExternalProject_Add(WASM_MODULE + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps + BUILD_ALWAYS TRUE + UPDATE_COMMAND "" + PATCH_COMMAND "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps + BUILD_COMMAND ${CMAKE_COMMAND} --build . + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy + ./mA.wasm ${CMAKE_BINARY_DIR} + ./mB.wasm ${CMAKE_BINARY_DIR} + ./mC.wasm ${CMAKE_BINARY_DIR} + ./mD.wasm ${CMAKE_BINARY_DIR} + ./mE.wasm ${CMAKE_BINARY_DIR} +) + +################ WASM MODULES TO AOT +if (WAMR_BUILD_AOT EQUAL 1) + set(WAMR_COMPILER_DIR ${CMAKE_CURRENT_LIST_DIR}/../../wamr-compiler/build) + message(CHECK_START "Detecting WAMR_COMPILER at ${WAMR_COMPILER_DIR}") + find_file(WAMR_COMPILER + wamrc + PATHS "${CMAKE_CURRENT_LIST_DIR}/../../wamr-compiler/build" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(WAMR_COMPILER) + message(CHECK_PASS "found") + else() + message(CHECK_FAIL "not found") + endif() + if((NOT EXISTS ${WAMR_COMPILER}) ) + message(FATAL_ERROR "Please build wamrc under the path=${WAMR_ROOT_DIR}/wamr-compiler/ ") + else() + message(STATUS "WAMR_COMPILER is ${WAMR_COMPILER}") + endif() + + if (WAMR_BUILD_DUMP_CALL_STACK EQUAL 1) + list(APPEND WAMR_AOT_COMPILE_OPTIONS "--enable-dump-call-stack") + endif () + if (WAMR_BUILD_GC EQUAL 1) + list(APPEND WAMR_AOT_COMPILE_OPTIONS "--enable-gc") + endif () + + add_custom_target( + wasm_to_aot + ALL + DEPENDS + WASM_MODULE ${WAMR_COMPILER} + COMMAND + ${WAMR_COMPILER} ${WAMR_AOT_COMPILE_OPTIONS} -o mA.aot ./mA.wasm + COMMAND + ${WAMR_COMPILER} ${WAMR_AOT_COMPILE_OPTIONS} -o mB.aot ./mB.wasm + COMMAND + ${WAMR_COMPILER} ${WAMR_AOT_COMPILE_OPTIONS} -o mC.aot ./mC.wasm + WORKING_DIRECTORY + ${CMAKE_BINARY_DIR} + ) +endif() + +################ NATIVE +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable(multi_module src/main.c ${UNCOMMON_SHARED_SOURCE}) + +add_dependencies(multi_module vmlib WASM_MODULE) + +check_pie_supported() +set_target_properties (multi_module PROPERTIES POSITION_INDEPENDENT_CODE ON) + +# libraries +target_link_libraries(multi_module PRIVATE vmlib -lpthread -lm) diff --git a/samples/multi-module/README.md b/samples/multi-module/README.md new file mode 100644 index 0000000000..8e5d8fe8fc --- /dev/null +++ b/samples/multi-module/README.md @@ -0,0 +1,23 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/multi-module" +--- +# WAMR MULTI-MODUEL SAMPLE +**WAMR supports *multi-module* in both *interpreter* mode and *aot* mode.** + +Multi-modules will determine the running mode based on the type of the main module. + + +``` shell +$ mkdir build +$ cd build +$ cmake .. +$ make +$ # It will build multi_module runtime and +$ # wasm file under the ./build . +$ # If you have built wamrc, +$ # aot file will also generate. +$ ./multi_module mC.wasm +$ ... +$ ./multi_module mC.aot +$ ... + diff --git a/samples/multi-module/src/main.c b/samples/multi-module/src/main.c new file mode 100644 index 0000000000..29b623daaa --- /dev/null +++ b/samples/multi-module/src/main.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include "bh_read_file.h" +#include "platform_common.h" +#include "wasm_export.h" + +#if WASM_ENABLE_MULTI_MODULE != 0 +static char *module_search_path = "."; +static bool +module_reader_callback(package_type_t module_type, const char *module_name, + uint8 **p_buffer, uint32 *p_size) +{ + char *file_format = NULL; +#if WASM_ENABLE_INTERP != 0 + if (module_type == Wasm_Module_Bytecode) + file_format = ".wasm"; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_type == Wasm_Module_AoT) + file_format = ".aot"; + +#endif + bh_assert(file_format != NULL); + const char *format = "%s/%s%s"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(file_format) + 1; + char *wasm_file_name = wasm_runtime_malloc(sz); + if (!wasm_file_name) { + return false; + } + snprintf(wasm_file_name, sz, format, module_search_path, module_name, + file_format); + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); + + wasm_runtime_free(wasm_file_name); + return *p_buffer != NULL; +} + +static void +module_destroyer_callback(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + wasm_runtime_free(buffer); + buffer = NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +/* 10M */ +static char sandbox_memory_space[10 * 1024 * 1024] = { 0 }; +int +main(int argc, char *argv[]) +{ + bool ret = false; + if (argc != 2) { + return -1; + } + char *wasm_file = argv[1]; + /* 16K */ + const uint32 stack_size = 16 * 1024; + const uint32 heap_size = 16 * 1024; + + RuntimeInitArgs init_args = { 0 }; + char error_buf[128] = { 0 }; + /* parameters and return values */ + char *args[1] = { 0 }; + + uint8 *file_buf = NULL; + uint32 file_buf_size = 0; + wasm_module_t module = NULL; + wasm_module_t module1; + wasm_module_inst_t module_inst = NULL; + + /* all malloc() only from the given buffer */ + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space; + init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space); + + printf("- wasm_runtime_full_init\n"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + goto EXIT; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + printf("- wasm_runtime_set_module_reader\n"); + /* set module reader and destroyer */ + wasm_runtime_set_module_reader(module_reader_callback, + module_destroyer_callback); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &file_buf_size))) + goto RELEASE_RUNTIME; + /* load mC and let WAMR load mA and mB */ + printf("- wasm_runtime_load\n"); + + if (!(module = wasm_runtime_load(file_buf, file_buf_size, error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto RELEASE_BINARY; + } + + /* instantiate the module */ + printf("- wasm_runtime_instantiate\n"); + if (!(module_inst = wasm_runtime_instantiate( + module, stack_size, heap_size, error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto UNLOAD_MODULE; + } + + /* call functions of mC */ + printf("\n----------------------------------------\n"); + printf("call \"C1\", it will return 0x1f:i32, ===> "); + wasm_application_execute_func(module_inst, "C1", 0, args); + printf("call \"C2\", it will call B1() of mB and return 0x15:i32, ===> "); + wasm_application_execute_func(module_inst, "C2", 0, args); + printf("call \"C3\", it will call A1() of mA and return 0xb:i32, ===> "); + wasm_application_execute_func(module_inst, "C3", 0, args); + printf("call \"C4\", it will call B2() of mB and call A1() of mA and " + "return 0xb:i32, ===> "); + wasm_application_execute_func(module_inst, "C4", 0, args); + printf( + "call \"C5\", it will be failed since it is a export function, ===> "); + wasm_application_execute_func(module_inst, "C5", 0, args); + + /* examine module registration a bit */ + module1 = wasm_runtime_find_module_registered("mC"); + if (module1 != NULL) { + printf("unexpected module mC %p != NULL\n", module1); + goto UNLOAD_MODULE; + } + module1 = wasm_runtime_find_module_registered("mA"); + if (module1 == NULL) { + printf("unexpected module mA\n"); + goto UNLOAD_MODULE; + } + module1 = wasm_runtime_find_module_registered("mB"); + if (module1 == NULL) { + printf("unexpected module mB\n"); + goto UNLOAD_MODULE; + } + if (!wasm_runtime_register_module("mC", module, error_buf, + sizeof(error_buf))) { + printf("%s\n", error_buf); + goto UNLOAD_MODULE; + } + module1 = wasm_runtime_find_module_registered("mC"); + if (module1 != module) { + printf("unexpected module mC %p != %p\n", module1, module); + goto UNLOAD_MODULE; + } + + ret = true; + + printf("- wasm_runtime_deinstantiate\n"); + wasm_runtime_deinstantiate(module_inst); +UNLOAD_MODULE: + printf("- wasm_runtime_unload\n"); + wasm_runtime_unload(module); +RELEASE_BINARY: + module_destroyer_callback(file_buf, file_buf_size); +RELEASE_RUNTIME: + printf("- wasm_runtime_destroy\n"); + wasm_runtime_destroy(); +EXIT: + return ret ? 0 : 1; +} diff --git a/samples/multi-module/wasm-apps/CMakeLists.txt b/samples/multi-module/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000000..6460a99e67 --- /dev/null +++ b/samples/multi-module/wasm-apps/CMakeLists.txt @@ -0,0 +1,90 @@ +# 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-apps) + +message(CHECK_START "Detecting WABT") +if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR})) + find_path(WABT_DIR + wabt + PATHS /opt + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(DEFINED WABT_DIR) + set(WABT_DIR ${WABT_DIR}/wabt) + endif() +endif() +if(WABT_DIR) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}") +find_program(WASM_OBJDUMP + wasm-objdump + PATHS "${WABT_DIR}/bin" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASM_OBJDUMP) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +message(CHECK_START "Detecting WASM2WAT at ${WABT_DIR}") +find_program(WASM2WAT + wasm2wat + PATHS "${WABT_DIR}/bin" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASM2WAT) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +function(COMPILE_WITH_CLANG SOURCE_FILE COMMAND) + get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WLE) + + set(WASM_MODULE ${FILE_NAME}.wasm) + + set(MAIN_TARGET_NAME MODULE_${FILE_NAME}) + + add_executable(${MAIN_TARGET_NAME} ${SOURCE_FILE}) + set_target_properties(${MAIN_TARGET_NAME} PROPERTIES OUTPUT_NAME ${WASM_MODULE}) + target_compile_options (${MAIN_TARGET_NAME} PRIVATE -fno-exceptions) + + if(${COMMAND}) + message(STATUS "Generating ${WASM_MODULE} as COMMAND...") + else() + message(STATUS "Generating ${WASM_MODULE} as REACTOR...") + target_link_options(${MAIN_TARGET_NAME} PRIVATE -mexec-model=reactor) + endif() + + if(EXISTS ${WASM2WAT}) + message(STATUS "Dumping ${WASM_MODULE}...") + set(WASM_WAT ${FILE_NAME}.wat) + set(DUMP_TARGET_NAME DUMP_${FILE_NAME}) + add_custom_command(OUTPUT ${WASM_WAT} + COMMAND ${WASM2WAT} --enable-all -o ${WASM_WAT} ${WASM_MODULE} + COMMENT "Dumping ${WASM_MODULE}..." + DEPENDS ${MAIN_TARGET_NAME} + ) + + add_custom_target(${DUMP_TARGET_NAME} ALL + DEPENDS ${WASM_WAT} + ) + endif() +endfunction() + +compile_with_clang(mA.c OFF) +compile_with_clang(mB.c OFF) +compile_with_clang(mC.c ON) +compile_with_clang(mD.cpp ON) +compile_with_clang(mE.cpp OFF) + diff --git a/samples/multi-module/wasm-apps/mA.c b/samples/multi-module/wasm-apps/mA.c new file mode 100644 index 0000000000..663ec81697 --- /dev/null +++ b/samples/multi-module/wasm-apps/mA.c @@ -0,0 +1,13 @@ +__attribute__((export_name("A1"))) int +A1() +{ + return 11; +} + +int +A2() +{ + return 12; +} + +/* mA is a reactor. it doesn't need a main() */ \ No newline at end of file diff --git a/samples/multi-module/wasm-apps/mB.c b/samples/multi-module/wasm-apps/mB.c new file mode 100644 index 0000000000..a912d1d77d --- /dev/null +++ b/samples/multi-module/wasm-apps/mB.c @@ -0,0 +1,23 @@ +__attribute__((import_module("mA"))) +__attribute__((import_name("A1"))) extern int +A1(); + +__attribute__((export_name("B1"))) int +B1() +{ + return 21; +} + +__attribute__((export_name("B2"))) int +B2() +{ + return A1(); +} + +int +B3() +{ + return 23; +} + +/* mA is a reactor. it doesn't need a main() */ \ No newline at end of file diff --git a/samples/multi-module/wasm-apps/mC.c b/samples/multi-module/wasm-apps/mC.c new file mode 100644 index 0000000000..8b19a5b669 --- /dev/null +++ b/samples/multi-module/wasm-apps/mC.c @@ -0,0 +1,51 @@ +#include +#include + +__attribute__((import_module("mA"))) +__attribute__((import_name("A1"))) extern int +A1(); + +__attribute__((import_module("mB"))) +__attribute__((import_name("B1"))) extern int +B1(); + +__attribute__((import_module("mB"))) +__attribute__((import_name("B2"))) extern int +B2(); + +__attribute__((export_name("C1"))) int +C1() +{ + return 31; +} + +__attribute__((export_name("C2"))) int +C2() +{ + return B1(); +} + +__attribute__((export_name("C3"))) int +C3() +{ + return A1(); +} + +__attribute__((export_name("C4"))) int +C4() +{ + return B2(); +} + +int +C5() +{ + return C1() + C2() + C3() + 35; +} + +int +main() +{ + printf("%u\n", C5()); + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/samples/multi-module/wasm-apps/mD.cpp b/samples/multi-module/wasm-apps/mD.cpp new file mode 100644 index 0000000000..d70998c089 --- /dev/null +++ b/samples/multi-module/wasm-apps/mD.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +static void +bye_main() +{ + std::cout << "mD " << __FUNCTION__ << std::endl; +} + +static void +bye_setup() +{ + std::cout << "mD " << __FUNCTION__ << std::endl; +} + +static void +bye_func() +{ + std::cout << "mD " << __FUNCTION__ << std::endl; +} + +void +func3() __attribute__((__import_module__("mE"), __import_name__("func1"))); + +void +func4() __attribute__((__import_module__("mE"), __import_name__("func2"))); + +void +func1() +{ + std::printf("mD %s\n", __FUNCTION__); + if (std::atexit(bye_func) != 0) { + std::perror("register an atexit handler failed"); + } + func3(); +} + +void +func2() +{ + std::printf("mD %s\n", __FUNCTION__); + func4(); +} + +__attribute__((constructor)) void +setup() +{ + std::cout << "mD " << __FUNCTION__ << std::endl; + if (std::atexit(bye_setup) != 0) { + std::perror("register an atexit handler failed"); + } +} + +__attribute__((destructor)) void +teardown() +{ + std::cout << "mD " << __FUNCTION__ << std::endl; +} + +int +main() +{ + std::printf("mD %s\n", __FUNCTION__); + + if (std::atexit(bye_main) != 0) { + std::perror("register an atexit handler failed"); + return EXIT_FAILURE; + } + + func1(); + func2(); + return EXIT_SUCCESS; +} diff --git a/samples/multi-module/wasm-apps/mE.cpp b/samples/multi-module/wasm-apps/mE.cpp new file mode 100644 index 0000000000..11e70af1fe --- /dev/null +++ b/samples/multi-module/wasm-apps/mE.cpp @@ -0,0 +1,45 @@ +#include +#include +#include + +static void +bye_setup() +{ + std::cout << "mE " << __FUNCTION__ << std::endl; +} + +static void +bye_func() +{ + std::cout << "mE " << __FUNCTION__ << std::endl; +} + +__attribute__((constructor)) void +setup() +{ + std::cout << "mE " << __FUNCTION__ << std::endl; + if (std::atexit(bye_setup) != 0) { + std::perror("register an atexit handler failed"); + } +} + +__attribute__((destructor)) void +teardown() +{ + std::cout << "mE " << __FUNCTION__ << std::endl; +} + +__attribute__((export_name("func1"))) void +func1() +{ + std::cout << "mE " << __FUNCTION__ << std::endl; + if (std::atexit(bye_func) != 0) { + std::perror("register an atexit handler failed"); + } +} + +__attribute__((export_name("func2"))) void +func2() +{ + std::cout << "mE " << __FUNCTION__ << std::endl; +} diff --git a/samples/multi-thread/CMakeLists.txt b/samples/multi-thread/CMakeLists.txt new file mode 100644 index 0000000000..71a1cc5b44 --- /dev/null +++ b/samples/multi-thread/CMakeLists.txt @@ -0,0 +1,80 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(pthread) + +################ 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_LIB_PTHREAD 1) +set(WAMR_BUILD_LIB_PTHREAD_SEMAPHORE 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 () + +# 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/multi-thread/README.md b/samples/multi-thread/README.md new file mode 100644 index 0000000000..ccd23730ee --- /dev/null +++ b/samples/multi-thread/README.md @@ -0,0 +1,3 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/multi-thread" +--- \ No newline at end of file diff --git a/samples/multi-thread/wasm-apps/CMakeLists.txt b/samples/multi-thread/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000000..5442398f94 --- /dev/null +++ b/samples/multi-thread/wasm-apps/CMakeLists.txt @@ -0,0 +1,46 @@ +# 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-apps) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib -pthread -Qunused-arguments") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=32768") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (DEFINED_SYMBOLS +"${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--shared-memory,--max-memory=131072, \ + -Wl,--no-entry,--strip-all, \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + -Wl,--export=main -Wl,--export=__main_argc_argv \ + -Wl,--allow-undefined" + #-Wl,--allow-undefined-file=${DEFINED_SYMBOLS}" +) + +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) diff --git a/samples/multi-thread/wasm-apps/main.c b/samples/multi-thread/wasm-apps/main.c new file mode 100644 index 0000000000..2b9581f1c1 --- /dev/null +++ b/samples/multi-thread/wasm-apps/main.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include + +static pthread_mutex_t mutex; +static pthread_cond_t cond; +static sem_t *sem; + +static void * +thread(void *arg) +{ + int *num = (int *)arg; + + pthread_mutex_lock(&mutex); + printf("thread start \n"); + + for (int i = 0; i < 10; i++) { + *num = *num + 1; + printf("num: %d\n", *num); + } + + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + sem_post(sem); + + printf("thread exit \n"); + + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t tid; + int num = 0, ret = -1; + + if (pthread_mutex_init(&mutex, NULL) != 0) { + printf("Failed to init mutex.\n"); + return -1; + } + if (pthread_cond_init(&cond, NULL) != 0) { + printf("Failed to init cond.\n"); + goto fail1; + } + + // O_CREAT and S_IRGRPS_IRGRP | S_IWGRP on linux (glibc), initial value is 0 + + if (!(sem = sem_open("tessstsem", 0100, 0x10 | 0x20, 0))) { + printf("Failed to open sem. %p\n", sem); + goto fail2; + } + + pthread_mutex_lock(&mutex); + if (pthread_create(&tid, NULL, thread, &num) != 0) { + printf("Failed to create thread.\n"); + pthread_mutex_unlock(&mutex); + goto fail3; + } + + printf("cond wait start\n"); + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + printf("cond wait success.\n"); + + if (sem_wait(sem) != 0) { + printf("Failed to wait sem.\n"); + } + else { + printf("sem wait success.\n"); + } + + if (pthread_join(tid, NULL) != 0) { + printf("Failed to join thread.\n"); + } + + ret = 0; + +fail3: + sem_close(sem); + sem_unlink("tessstsem"); +fail2: + pthread_cond_destroy(&cond); +fail1: + pthread_mutex_destroy(&mutex); + + return ret; +} \ 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 0000000000..dafbea886d --- /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 0000000000..80f170d40d --- /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/native-lib/CMakeLists.txt b/samples/native-lib/CMakeLists.txt new file mode 100644 index 0000000000..497727cb09 --- /dev/null +++ b/samples/native-lib/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(native_lib) + +################ runtime settings ############## +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 are: +# "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) + +# 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 () + +# build out libiwasm +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +# Note: we build libiwasm as a shared library here so that it can be +# shared between iwasm and native libraries. +add_library(libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +################ wamr runtime ################### +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${WAMR_ROOT_DIR}/product-mini/platforms/posix/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 libiwasm -lpthread -lm -ldl) + +################ native libraries ############### +add_library (test_add SHARED test_add.c) +add_library (test_sqrt SHARED test_sqrt.c) +add_library (test_hello SHARED test_hello.c) +# Note: Unlike simpler examples above, test_hello2 directly uses +# the API provided by the libiwasm library. +add_library (test_hello2 SHARED test_hello2.c) +target_link_libraries(test_hello2 libiwasm) + +################ wasm application ############### +add_subdirectory(wasm-app) diff --git a/samples/native-lib/README.md b/samples/native-lib/README.md new file mode 100644 index 0000000000..2ce5e8ff62 --- /dev/null +++ b/samples/native-lib/README.md @@ -0,0 +1,81 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/native-lib" +--- +# "native-lib" sample introduction + +This sample demonstrates how to write required interfaces in native library, build it into a shared library and register the shared library to iwasm. + +The native library should provide `get_native_lib` API for iwasm to return the native library info, including the module name, the native symbol list and the native symbol count, so that iwasm can use them to register the native library, for example: + +```C +static int +foo_wrapper(wasm_exec_env_t exec_env, int x, int y) +{ + return x + y; +} + +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(foo, "(ii)i") +}; + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} +``` + +## Preparation + +Please install WASI SDK, download the [wasi-sdk release](https://github.com/WebAssembly/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. + +## Build the sample + +```bash +mkdir build +cd build +cmake .. +make +``` + +`iwasm`, one wasm module `test.wasm` and two shared libraries `libtest_add.so`, `libtest_sqrt.so` +will be generated. + +## Run workload + +### Linux + +```bash +cd build +./iwasm --native-lib=./libtest_add.so --native-lib=./libtest_sqrt.so --native-lib=./libtest_hello.so --native-lib=./libtest_hello2.so wasm-app/test.wasm +``` + +### macOS + +```bash +cd build +./iwasm --native-lib=libtest_add.dylib --native-lib=libtest_sqrt.dylib --native-lib=libtest_hello.dylib --native-lib=libtest_hello2.dylib wasm-app/test.wasm +``` + +The output is: + +```bash +init_native_lib in test_hello2.c called +Hello World! +10 + 20 = 30 +sqrt(10, 20) = 500 +test_hello("main", 0x0, 0) = 41 +malloc(42) = 0x24e8 +test_hello("main", 0x24e8, 42) = 41 +Message from test_hello: Hello, main. This is test_hello_wrapper! +test_hello2("main", 0x0, 0) = 85 +malloc(86) = 0x24e8 +test_hello2("main", 0x24e8, 86) = 85 +Message from test_hello2: Hello, main. This is test_hello2_wrapper! Your wasm_module_inst_t is 0x7fe0e6804280. +deinit_native_lib in test_hello2.c called +``` diff --git a/samples/native-lib/test_add.c b/samples/native-lib/test_add.c new file mode 100644 index 0000000000..ac44c61928 --- /dev/null +++ b/samples/native-lib/test_add.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +static int +test_add_wrapper(wasm_exec_env_t exec_env, int x, int y) +{ + return x + y; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_add, "(ii)i") +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} diff --git a/samples/native-lib/test_hello.c b/samples/native-lib/test_hello.c new file mode 100644 index 0000000000..c322ec24e8 --- /dev/null +++ b/samples/native-lib/test_hello.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +static int +test_hello_wrapper(wasm_exec_env_t exec_env, const char *name, char *result, + size_t resultlen) +{ + return snprintf(result, resultlen, "Hello, %s. This is %s!\n", name, + __func__); +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_hello, "($*~)i") +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} diff --git a/samples/native-lib/test_hello2.c b/samples/native-lib/test_hello2.c new file mode 100644 index 0000000000..53ea663a72 --- /dev/null +++ b/samples/native-lib/test_hello2.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * This example basically does the same thing as test_hello.c, + * using wasm_export.h API. + */ + +#include +#include + +#include "wasm_export.h" + +static int +test_hello2_wrapper(wasm_exec_env_t exec_env, uint32_t nameaddr, + uint32_t resultaddr, uint32_t resultlen) +{ + /* + * Perform wasm_runtime_malloc to check if the runtime has been + * initialized as expected. + * This would fail with "memory hasn't been initialize" error + * unless we are not sharing a runtime with the loader app. (iwasm) + */ + void *p = wasm_runtime_malloc(1); + if (p == NULL) { + return -1; + } + wasm_runtime_free(p); + + wasm_module_inst_t inst = wasm_runtime_get_module_inst(exec_env); + if (!wasm_runtime_validate_app_str_addr(inst, (uint64_t)nameaddr) + || !wasm_runtime_validate_app_addr(inst, (uint64_t)resultaddr, + (uint64_t)resultlen)) { + return -1; + } + const char *name = + wasm_runtime_addr_app_to_native(inst, (uint64_t)nameaddr); + char *result = wasm_runtime_addr_app_to_native(inst, (uint64_t)resultaddr); + return snprintf(result, resultlen, + "Hello, %s. This is %s! Your wasm_module_inst_t is %p.\n", + name, __func__, inst); +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_hello2, "(iii)i") +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} + +int +init_native_lib() +{ + printf("%s in test_hello2.c called\n", __func__); + return 0; +} + +void +deinit_native_lib() +{ + printf("%s in test_hello2.c called\n", __func__); +} diff --git a/samples/native-lib/test_sqrt.c b/samples/native-lib/test_sqrt.c new file mode 100644 index 0000000000..ecf7c989c0 --- /dev/null +++ b/samples/native-lib/test_sqrt.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +static int +test_sqrt_wrapper(wasm_exec_env_t exec_env, int x, int y) +{ + return x * x + y * y; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_sqrt, "(ii)i") +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} diff --git a/samples/native-lib/wasm-app/CMakeLists.txt b/samples/native-lib/wasm-app/CMakeLists.txt new file mode 100644 index 0000000000..e5bea6010b --- /dev/null +++ b/samples/native-lib/wasm-app/CMakeLists.txt @@ -0,0 +1,35 @@ +# 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-app) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () + +set (CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--max-memory=131072 -z stack-size=8192 \ + -Wl,--no-entry,--strip-all \ + -Wl,--export=__main_argc_argv \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--allow-undefined" +) + +add_executable(test.wasm main.c) +target_link_libraries(test.wasm) diff --git a/samples/native-lib/wasm-app/main.c b/samples/native-lib/wasm-app/main.c new file mode 100644 index 0000000000..dba652daf0 --- /dev/null +++ b/samples/native-lib/wasm-app/main.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +int +test_add(int x, int y); + +int +test_sqrt(int x, int y); + +int +test_hello(const char *name, char *buf, size_t buflen); + +int +test_hello2(const char *name, char *buf, size_t buflen); + +int +main(int argc, char **argv) +{ + const char *name = __func__; + char *buf; + size_t buflen; + int x = 10, y = 20, res; + + printf("Hello World!\n"); + + res = test_add(x, y); + printf("%d + %d = %d\n", x, y, res); + + res = test_sqrt(x, y); + printf("sqrt(%d, %d) = %d\n", x, y, res); + + res = test_hello(name, NULL, 0); + printf("test_hello(\"%s\", %p, %zu) = %d\n", name, NULL, (size_t)0, res); + if (res == -1) { + return -1; + } + buflen = res + 1; + buf = malloc(buflen); + printf("malloc(%zu) = %p\n", buflen, buf); + res = test_hello(__func__, buf, buflen); + if (res == -1) { + return -1; + } + printf("test_hello(\"%s\", %p, %zu) = %d\n", name, buf, buflen, res); + printf("Message from test_hello: %s", buf); + free(buf); + + res = test_hello2(name, NULL, 0); + printf("test_hello2(\"%s\", %p, %zu) = %d\n", name, NULL, (size_t)0, res); + if (res == -1) { + return -1; + } + buflen = res + 1; + buf = malloc(buflen); + printf("malloc(%zu) = %p\n", buflen, buf); + res = test_hello2(__func__, buf, buflen); + if (res == -1) { + return -1; + } + printf("test_hello2(\"%s\", %p, %zu) = %d\n", name, buf, buflen, res); + printf("Message from test_hello2: %s", buf); + free(buf); + + return 0; +} diff --git a/samples/native-stack-overflow/.gitignore b/samples/native-stack-overflow/.gitignore new file mode 100644 index 0000000000..0fa8a76bda --- /dev/null +++ b/samples/native-stack-overflow/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/native-stack-overflow/CMakeLists.txt b/samples/native-stack-overflow/CMakeLists.txt new file mode 100644 index 0000000000..48ffd4f5b8 --- /dev/null +++ b/samples/native-stack-overflow/CMakeLists.txt @@ -0,0 +1,93 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (native-stack-overflow) +else() + project (native-stack-overflow C ASM) +endif() + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_EXTENSIONS YES) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 0) +set (WAMR_BUILD_LIBC_WASI 1) + +if (NOT MSVC) + # linker 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 () +endif () + +if (CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 13.0.0) +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-usage") +endif () + +# GCC doesn't have disable_tail_calls attribute +if (CMAKE_C_COMPILER_ID MATCHES "GNU") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-optimize-sibling-calls") +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (native-stack-overflow src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (native-stack-overflow PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (native-stack-overflow vmlib -lm -ldl -lpthread) +else () + target_link_libraries (native-stack-overflow vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/samples/native-stack-overflow/README.md b/samples/native-stack-overflow/README.md new file mode 100644 index 0000000000..431c5876e5 --- /dev/null +++ b/samples/native-stack-overflow/README.md @@ -0,0 +1,4 @@ +The "native-stack-overflow" sample project +========================================== + +This sample examines native stack overflow detection mechanisms. diff --git a/samples/native-stack-overflow/build.sh b/samples/native-stack-overflow/build.sh new file mode 100755 index 0000000000..de91f26954 --- /dev/null +++ b/samples/native-stack-overflow/build.sh @@ -0,0 +1,103 @@ +#! /bin/sh + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "##################### build (default)" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. +make -j 4 +if [ $? != 0 ];then + echo "BUILD_FAIL native-stack-overflow exit as $?\n" + exit 2 +fi +cp -a native-stack-overflow ${OUT_DIR} + +echo "##################### build (WAMR_DISABLE_HW_BOUND_CHECK=1)" +cd ${CURR_DIR} +mkdir -p cmake_build_disable_hw_bound +cd cmake_build_disable_hw_bound +cmake -D WAMR_DISABLE_HW_BOUND_CHECK=1 .. +make -j 4 +if [ $? != 0 ];then + echo "BUILD_FAIL native-stack-overflow exit as $?\n" + exit 2 +fi +cp -a native-stack-overflow ${OUT_DIR}/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK + +echo "##################### signature shared lib" +cd ${CURR_DIR} +cc -I ../../core/iwasm/include -shared -o ${OUT_DIR}/signature.so \ +src/signature.c + +echo + +echo "##################### build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# use WAMR SDK to build out the .wasm binary +/opt/wasi-sdk/bin/clang \ + -mexec-model=reactor \ + -Os -z stack-size=4096 -Wl,--initial-memory=65536 \ + -Wl,--allow-undefined \ + -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "#################### build wasm apps done" + +echo "#################### aot-compile" +WAMRC=${WAMR_DIR}/wamr-compiler/build/wamrc +${WAMRC} \ +-o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot \ +--size-level=0 \ +${OUT_DIR}/wasm-apps/${OUT_FILE} + +echo "#################### aot-compile w/ signature" +WAMRC=${WAMR_DIR}/wamr-compiler/build/wamrc +${WAMRC} \ +-o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.signature \ +--size-level=0 \ +--native-lib=${OUT_DIR}/signature.so \ +${OUT_DIR}/wasm-apps/${OUT_FILE} + +echo "#################### aot-compile (--bounds-checks=1)" +${WAMRC} \ +-o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.bounds-checks \ +--size-level=0 \ +--bounds-checks=1 \ +${OUT_DIR}/wasm-apps/${OUT_FILE} + +echo "#################### aot-compile (--bounds-checks=1) w/ signature" +${WAMRC} \ +-o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.signature.bounds-checks \ +--size-level=0 \ +--native-lib=${OUT_DIR}/signature.so \ +--bounds-checks=1 \ +${OUT_DIR}/wasm-apps/${OUT_FILE} diff --git a/samples/native-stack-overflow/clean.sh b/samples/native-stack-overflow/clean.sh new file mode 100755 index 0000000000..54c0325bb3 --- /dev/null +++ b/samples/native-stack-overflow/clean.sh @@ -0,0 +1 @@ +rm -rf cmake_build cmake_build_disable_hw_bound out diff --git a/samples/native-stack-overflow/run.sh b/samples/native-stack-overflow/run.sh new file mode 100755 index 0000000000..3ba48980af --- /dev/null +++ b/samples/native-stack-overflow/run.sh @@ -0,0 +1,28 @@ +#! /bin/sh + +set -e + +NAME=${1:-test1} + +echo "====== Interpreter ${NAME}" +out/native-stack-overflow out/wasm-apps/testapp.wasm ${NAME} + +echo +echo "====== Interpreter WAMR_DISABLE_HW_BOUND_CHECK=1 ${NAME}" +out/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK out/wasm-apps/testapp.wasm ${NAME} + +echo +echo "====== AOT ${NAME}" +out/native-stack-overflow out/wasm-apps/testapp.wasm.aot ${NAME} + +echo +echo "====== AOT w/ signature ${NAME}" +out/native-stack-overflow out/wasm-apps/testapp.wasm.aot.signature ${NAME} + +echo +echo "====== AOT WAMR_DISABLE_HW_BOUND_CHECK=1 ${NAME}" +out/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK out/wasm-apps/testapp.wasm.aot.bounds-checks ${NAME} + +echo +echo "====== AOT w/ signature WAMR_DISABLE_HW_BOUND_CHECK=1 ${NAME}" +out/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK out/wasm-apps/testapp.wasm.aot.signature.bounds-checks ${NAME} diff --git a/samples/native-stack-overflow/src/main.c b/samples/native-stack-overflow/src/main.c new file mode 100644 index 0000000000..8d42cc0aa1 --- /dev/null +++ b/samples/native-stack-overflow/src/main.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" + +uint32_t +host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, + uint32_t x, uint32_t stack); +uint32_t +host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack); + +extern unsigned int nest; + +static NativeSymbol native_symbols[] = { + { "host_consume_stack_and_call_indirect", + host_consume_stack_and_call_indirect, "(iii)i", NULL }, + { "host_consume_stack", host_consume_stack, "(i)i", NULL }, +}; + +void * +canary_addr() +{ + uint8_t *p = os_thread_get_stack_boundary(); +#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint32_t page_size = os_getpagesize(); + uint32_t guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + return p + page_size * guard_page_count; +#else + return p; +#endif +} + +void +canary_init(void) +{ + uint32_t *canary = canary_addr(); + *canary = 0xaabbccdd; +} + +bool +canary_check(void) +{ + /* assume an overflow if the first uint32_t on the stack was modified */ + const uint32_t *canary = (void *)canary_addr(); + return *canary == 0xaabbccdd; +} + +struct record { + bool failed; + bool leaked; + char exception[128]; /* EXCEPTION_BUF_LEN */ +}; + +void +print_record(unsigned int start, unsigned int end, const struct record *rec) +{ + printf("%5u - %5u | %6s | %6s | %s\n", start, end, + rec->failed ? "failed" : "ok", rec->leaked ? "leaked" : "ok", + rec->exception); +} + +int +main(int argc, char **argv) +{ + char *buffer; + char error_buf[128]; + + if (argc != 3) { + return 2; + } + const char *module_path = argv[1]; + const char *funcname = argv[2]; + + wasm_module_t module = NULL; + uint32 buf_size; + uint32 stack_size = 4096; + /* + * disable app heap. + * - we use wasi + * - https://github.com/bytecodealliance/wasm-micro-runtime/issues/2275 + */ + uint32 heap_size = 0; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_System_Allocator; + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + if (!wasm_runtime_full_init(&init_args)) { + printf("wasm_runtime_full_init failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(module_path, &buf_size); + if (!buffer) { + printf("bh_read_file_to_buffer failed\n"); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("wasm_runtime_load failed: %s\n", error_buf); + goto fail; + } + + /* header */ + printf(" stack size | fail? | leak? | exception\n"); + printf("-------------------------------------------------------------------" + "--------\n"); + + uint32_t page_size = os_getpagesize(); + unsigned int stack; + unsigned int prevstack = 0; /* appease GCC -Wmaybe-uninitialized */ + unsigned int stack_range_start = 0; + unsigned int stack_range_end = page_size * 6; + unsigned int step = 16; + struct record rec0; + struct record rec1; + struct record *rec = &rec0; + struct record *prevrec = &rec1; + bool have_prevrec = false; + for (stack = stack_range_start; stack < stack_range_end; stack += step) { + wasm_module_inst_t module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + bool failed = true; + const char *exception = NULL; + nest = 0; + + canary_init(); + module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + if (!module_inst) { + printf("wasm_runtime_instantiate failed: %s\n", error_buf); + goto fail2; + } + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if (!exec_env) { + printf("wasm_runtime_create_exec_env failed\n"); + goto fail2; + } + + wasm_function_inst_t func = + wasm_runtime_lookup_function(module_inst, funcname); + if (!func) { + printf("wasm_runtime_lookup_function failed for %s\n", funcname); + goto fail2; + } + + /* note: the function type is (ii)i */ + uint32_t wasm_argv[] = { + stack, /* native_stack */ + 30, /* recurse_count */ + }; + uint32_t wasm_argc = 2; + if (!wasm_runtime_call_wasm(exec_env, func, wasm_argc, wasm_argv)) { + exception = wasm_runtime_get_exception(module_inst); + goto fail2; + } + failed = false; + fail2: + if (!canary_check()) { + printf("stack overurn detected for stack=%u\n", stack); + abort(); + } + + /* + * note: non-zero "nest" here demonstrates resource leak on longjmp + * from signal handler. + * cf. + * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3320 + */ + memset(rec, 0, sizeof(*rec)); + rec->failed = failed; + rec->leaked = nest != 0; + strncpy(rec->exception, exception ? exception : "", + sizeof(rec->exception)); + if (have_prevrec && memcmp(prevrec, rec, sizeof(*rec))) { + print_record(prevstack, stack, prevrec); + have_prevrec = false; + } + if (!have_prevrec) { + prevstack = stack; + struct record *tmp = prevrec; + prevrec = rec; + rec = tmp; + have_prevrec = true; + } + if (exec_env) { + wasm_runtime_destroy_exec_env(exec_env); + } + if (module_inst) { + wasm_runtime_deinstantiate(module_inst); + } + } + if (have_prevrec) { + print_record(prevstack, stack, prevrec); + } + +fail: + if (module) { + wasm_runtime_unload(module); + } + if (buffer) { + BH_FREE(buffer); + } + wasm_runtime_destroy(); +} diff --git a/samples/native-stack-overflow/src/native_impl.c b/samples/native-stack-overflow/src/native_impl.c new file mode 100644 index 0000000000..7037e1029b --- /dev/null +++ b/samples/native-stack-overflow/src/native_impl.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#define __STDC_WANT_LIB_EXT1__ 1 + +#include +#include +#include + +#include "wasm_export.h" +#include "bh_platform.h" + +/* + * this "nest" var has two purposes: + * - prevent tail-call optimization + * - detect possible resource leak + */ +unsigned int nest = 0; +ptrdiff_t prev_diff = 0; + +uint32_t +call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, uint32_t x) +{ + uint32_t argv[1] = { + x, + }; + uint32_t argc = 1; + if (!wasm_runtime_call_indirect(exec_env, funcidx, argc, argv)) { + /* failed */ + return 0; + } + return argv[0]; +} + +uint32_t +host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, + uint32_t x, uint32_t stack) +{ + void *boundary = os_thread_get_stack_boundary(); + void *fp = __builtin_frame_address(0); + ptrdiff_t diff = fp - boundary; + /* + * because this function performs recursive calls depending on + * the user input, we don't have an apriori knowledge how much stack + * we need. perform the overflow check on each iteration. + */ + if (!wasm_runtime_detect_native_stack_overflow(exec_env)) { + return 0; + } + if (diff > stack) { + prev_diff = diff; + nest++; + uint32_t ret = + host_consume_stack_and_call_indirect(exec_env, funcidx, x, stack); + nest--; + return ret; + } + return call_indirect(exec_env, funcidx, x); +} + +__attribute__((noinline)) static uint32_t +consume_stack1(wasm_exec_env_t exec_env, void *base, uint32_t stack) +#if defined(__clang__) + __attribute__((disable_tail_calls)) +#endif +{ + void *fp = __builtin_frame_address(0); + ptrdiff_t diff = (unsigned char *)base - (unsigned char *)fp; + assert(diff > 0); + if (diff > stack) { + return diff; + } + return consume_stack1(exec_env, base, stack); +} + +uint32_t +host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack) +{ + /* + * this function consumes a bit more than "stack" bytes. + */ + if (!wasm_runtime_detect_native_stack_overflow_size(exec_env, 64 + stack)) { + return 0; + } + void *base = __builtin_frame_address(0); + return consume_stack1(exec_env, base, stack); +} diff --git a/samples/native-stack-overflow/src/signature.c b/samples/native-stack-overflow/src/signature.c new file mode 100644 index 0000000000..65dc2043fc --- /dev/null +++ b/samples/native-stack-overflow/src/signature.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +static int +dummy(wasm_exec_env_t exec_env) +{ + return 0; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, dummy, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(host_consume_stack_and_call_indirect, "(iii)i"), + REG_NATIVE_FUNC(host_consume_stack, "(i)i"), +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} diff --git a/samples/native-stack-overflow/wasm-apps/testapp.c b/samples/native-stack-overflow/wasm-apps/testapp.c new file mode 100644 index 0000000000..23ea32efc9 --- /dev/null +++ b/samples/native-stack-overflow/wasm-apps/testapp.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +uint32_t +host_consume_stack_and_call_indirect(int (*)(int), uint32_t, uint32_t); +uint32_t host_consume_stack(uint32_t); + +int +cb(int x) +{ + return x * x; +} + +int +consume_stack_cb(int x) __attribute__((disable_tail_calls)) +{ + /* + * intentions: + * + * - consume native stack by making recursive calls + * + * - avoid tail-call optimization (either by the C compiler or + * aot-compiler) + */ + if (x == 0) { + return 0; + } + return consume_stack_cb(x - 1) + 1; +} + +int +host_consume_stack_cb(int x) +{ + return host_consume_stack(x); +} + +__attribute__((export_name("test1"))) uint32_t +test1(uint32_t native_stack, uint32_t recurse_count) +{ + /* + * ------ os_thread_get_stack_boundary + * ^ + * | + * | "native_stack" bytes of stack left by + * | host_consume_stack_and_call_indirect + * | + * | ^ + * | | + * | | consume_stack_cb (interpreter or aot) + * v | + * ^ + * | + * | host_consume_stack_and_call_indirect + * | + * + * + */ + uint32_t ret = host_consume_stack_and_call_indirect( + consume_stack_cb, recurse_count, native_stack); + return 42; +} + +__attribute__((export_name("test2"))) uint32_t +test2(uint32_t native_stack, uint32_t recurse_count) +{ + uint32_t ret = host_consume_stack_and_call_indirect(host_consume_stack_cb, + 6000, native_stack); + return 42; +} diff --git a/samples/printversion/CMakeLists.txt b/samples/printversion/CMakeLists.txt new file mode 100644 index 0000000000..6e97cb72c4 --- /dev/null +++ b/samples/printversion/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2025 Midokura Japan KK. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.16) + +project(printversion LANGUAGES C) + +add_executable(printversion printversion.c) +find_package(iwasm REQUIRED) +target_link_libraries(printversion iwasm::vmlib) diff --git a/samples/printversion/printversion.c b/samples/printversion/printversion.c new file mode 100644 index 0000000000..91b244fe20 --- /dev/null +++ b/samples/printversion/printversion.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2025 Midokura Japan KK. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include + +#include + +int +main(int argc, char **argv) +{ + uint32_t major; + uint32_t minor; + uint32_t patch; + wasm_runtime_get_version(&major, &minor, &patch); + printf("wasm-micro-runtime %" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", major, + minor, patch); +} diff --git a/samples/printversion/test.sh b/samples/printversion/test.sh new file mode 100755 index 0000000000..a51c1849cd --- /dev/null +++ b/samples/printversion/test.sh @@ -0,0 +1,24 @@ +#! /bin/sh + +# Copyright (C) 2025 Midokura Japan KK. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set -e + +DIST=$(mktemp -d) + +# WAMR_BUILD_SIMD=0 to avoid fetching simde, which is +# not relevant to this particular test. +cmake -B build-wamr \ +-D CMAKE_INSTALL_PREFIX=${DIST} \ +-D WAMR_BUILD_SIMD=0 \ +../.. +cmake --build build-wamr -t install + +cmake -B build-app \ +-D CMAKE_PREFIX_PATH=${DIST} \ +-D CMAKE_INSTALL_PREFIX=${DIST} \ +. +cmake --build build-app + +./build-app/printversion diff --git a/samples/ref-types/CMakeLists.txt b/samples/ref-types/CMakeLists.txt new file mode 100644 index 0000000000..13807444d6 --- /dev/null +++ b/samples/ref-types/CMakeLists.txt @@ -0,0 +1,132 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project(ref-types) +else() + project (ref-types C ASM) +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 () + +if(NOT DEFINED WAMR_BUILD_INTERP) + set(WAMR_BUILD_INTERP 1) +endif() + +if(NOT DEFINED WAMR_BUILD_AOT) + set(WAMR_BUILD_AOT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_JOT) + set(WAMR_BUILD_JIT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_FAST_INTERP) + set(WAMR_BUILD_FAST_INTERP 1) +endif() + +if(NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + set(WAMR_BUILD_LIBC_BUILTIN 1) +endif() + +# Enable reference-types feature +set(WAMR_BUILD_REF_TYPES 1) + +if (NOT MSVC) + # 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 () +endif() +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set(WAMRC ${WAMR_ROOT_DIR}/wamr-compiler/build/wamrc) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +if (MSVC) + target_compile_definitions(vmlib PRIVATE WASM_API_EXTERN=) +endif() + +################ application related ################ +## locate wat2wasm +find_program(WAT2WASM + wat2wasm + PATHS /opt/wabt/bin + REQUIRED +) + +if(NOT WAT2WASM) + message(SEND_ERROR "can not find wat2wasm") +else () + execute_process(COMMAND ${WAT2WASM} --version + OUTPUT_VARIABLE WAT2WASM_VERSION_OUTPUT) + string(STRIP ${WAT2WASM_VERSION_OUTPUT} WAT2WASM_VERSION) + message("-- Found wat2wasm ${WAT2WASM_VERSION}") +endif() + +if (${WAT2WASM_VERSION} VERSION_LESS 1.0.26) + set(WAT2WASM_FLAGS "--enable-reference-types") +endif () + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable(hello src/hello.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (hello PROPERTIES POSITION_INDEPENDENT_CODE ON) + +target_include_directories(hello PRIVATE ${UNCOMMON_SHARED_DIR}) + +target_link_libraries(hello vmlib -lpthread -lm) + +if (MSVC) + target_compile_definitions(hello PRIVATE WASM_API_EXTERN=) +endif() + +# wat to wasm +file(GLOB WAT_FILE src/hello.wat) +add_custom_target(hello_wasm ALL + COMMAND ${WAT2WASM} ${WAT_FILE} ${WAT2WASM_FLAGS} -o ${PROJECT_BINARY_DIR}/hello.wasm + DEPENDS ${WAT_FILE} + BYPRODUCTS ${PROJECT_BINARY_DIR}/hello.wasm + VERBATIM + SOURCES ${WAT_FILE} +) diff --git a/samples/ref-types/README.md b/samples/ref-types/README.md new file mode 100644 index 0000000000..d8eb205cca --- /dev/null +++ b/samples/ref-types/README.md @@ -0,0 +1,3 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/ref-types" +--- \ No newline at end of file diff --git a/samples/ref-types/src/hello.c b/samples/ref-types/src/hello.c new file mode 100644 index 0000000000..aff47ff76e --- /dev/null +++ b/samples/ref-types/src/hello.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +#define USE_GLOBAL_HEAP_BUF 0 + +#if USE_GLOBAL_HEAP_BUF != 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif + +static uintptr_t global_objects[10] = { 0 }; + +int32 +local_cmp_externref(wasm_exec_env_t exec_env, uintptr_t externref_a, + uintptr_t externref_b) +{ + return externref_a == externref_b; +} + +int32 +local_chk_externref(wasm_exec_env_t exec_env, int32 index, uintptr_t externref) +{ + return externref == global_objects[index]; +} + +/* clang-format off */ +static NativeSymbol native_symbols[] = { + { "native-cmp-externref", local_cmp_externref, "(rr)i", NULL }, + { "native-chk-externref", local_chk_externref, "(ir)i", NULL }, +}; +/* clang-format on */ + +static inline void +local_set_externref(int32 index, uintptr_t externref) +{ + global_objects[index] = externref; +} + +static WASMFunctionInstanceCommon *wasm_set_externref_ptr; +static WASMFunctionInstanceCommon *wasm_get_externref_ptr; +static WASMFunctionInstanceCommon *wasm_cmp_externref_ptr; + +static bool +wasm_set_externref(wasm_exec_env_t exec_env, wasm_module_inst_t inst, + int32 index, uintptr_t externref) +{ + union { + uintptr_t val; + uint32 parts[2]; + } u; + uint32 argv[3] = { 0 }; + + if (!exec_env || !wasm_set_externref_ptr) { + return false; + } + + u.val = externref; + argv[0] = index; + argv[1] = u.parts[0]; + argv[2] = u.parts[1]; + if (!wasm_runtime_call_wasm(exec_env, wasm_set_externref_ptr, 2, argv)) { + const char *exception; + if ((exception = wasm_runtime_get_exception(inst))) { + printf("Exception: %s\n", exception); + } + return false; + } + + return true; +} + +static bool +wasm_get_externref(wasm_exec_env_t exec_env, wasm_module_inst_t inst, + int32 index, uintptr_t *ret_externref) +{ + wasm_val_t results[1] = { 0 }; + + if (!exec_env || !wasm_get_externref_ptr || !ret_externref) { + return false; + } + + if (!wasm_runtime_call_wasm_v(exec_env, wasm_get_externref_ptr, 1, results, + 1, index)) { + const char *exception; + if ((exception = wasm_runtime_get_exception(inst))) { + printf("Exception: %s\n", exception); + } + return false; + } + + if (WASM_EXTERNREF != results[0].kind) { + return false; + } + + *ret_externref = results[0].of.foreign; + return true; +} + +static bool +wasm_cmp_externref(wasm_exec_env_t exec_env, wasm_module_inst_t inst, + int32 index, uintptr_t externref, int32 *ret_result) +{ + wasm_val_t results[1] = { 0 }; + wasm_val_t arguments[2] = { + { .kind = WASM_I32, .of.i32 = index }, + { .kind = WASM_EXTERNREF, .of.foreign = externref }, + }; + + if (!exec_env || !wasm_cmp_externref_ptr || !ret_result) { + return false; + } + + if (!wasm_runtime_call_wasm_a(exec_env, wasm_cmp_externref_ptr, 1, results, + 2, arguments)) { + const char *exception; + if ((exception = wasm_runtime_get_exception(inst))) { + printf("Exception: %s\n", exception); + } + return false; + } + + if (results[0].kind != WASM_I32) { + return false; + } + + *ret_result = results[0].of.i32; + return true; +} + +static bool +set_and_cmp(wasm_exec_env_t exec_env, wasm_module_inst_t inst, int32 i, + uintptr_t externref) +{ + int32 cmp_result = 0; + uintptr_t wasm_externref = 0; + + wasm_set_externref(exec_env, inst, i, externref); + local_set_externref(i, externref); + + wasm_get_externref(exec_env, inst, i, &wasm_externref); + if (!local_chk_externref(exec_env, i, wasm_externref)) { + printf("#%d, In host language world Wasm Externref 0x%lx Vs. Native " + "Externref 0x%lx FAILED\n", + i, wasm_externref, externref); + return false; + } + + if (!wasm_cmp_externref(exec_env, inst, i, global_objects[i], &cmp_result) + || !cmp_result) { + printf("#%d, In Wasm world Native Externref 0x%lx Vs, Wasm Externref " + "FAILED\n", + i, global_objects[i]); + return false; + } + + return true; +} + +int +main(int argc, char *argv[]) +{ + char *wasm_file = "hello.wasm"; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + RuntimeInitArgs init_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + const uint64 big_number = 0x123456789abc; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail; + } + + /* instantiate the module */ + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail; + } + + /* create an execution env */ + if (!(exec_env = + wasm_runtime_create_exec_env(wasm_module_inst, stack_size))) { + printf("%s\n", "create exec env failed"); + goto fail; + } + + /* lookup function instance */ + if (!(wasm_cmp_externref_ptr = wasm_runtime_lookup_function( + wasm_module_inst, "cmp-externref"))) { + printf("%s\n", "lookup function cmp-externref failed"); + goto fail; + } + + if (!(wasm_get_externref_ptr = wasm_runtime_lookup_function( + wasm_module_inst, "get-externref"))) { + printf("%s\n", "lookup function get-externref failed"); + goto fail; + } + + if (!(wasm_set_externref_ptr = wasm_runtime_lookup_function( + wasm_module_inst, "set-externref"))) { + printf("%s\n", "lookup function set-externref failed"); + goto fail; + } + + /* test with NULL */ + if (!set_and_cmp(exec_env, wasm_module_inst, 0, 0) + || !set_and_cmp(exec_env, wasm_module_inst, 1, big_number + 1) + || !set_and_cmp(exec_env, wasm_module_inst, 2, big_number + 2) + || !set_and_cmp(exec_env, wasm_module_inst, 3, big_number + 3)) { + goto fail; + } + + printf("GREAT! PASS ALL CHKs\n"); + +fail: + /* destroy exec env */ + if (exec_env) { + wasm_runtime_destroy_exec_env(exec_env); + } + + /* destroy the module instance */ + if (wasm_module_inst) { + wasm_runtime_deinstantiate(wasm_module_inst); + } + + /* unload the module */ + if (wasm_module) { + wasm_runtime_unload(wasm_module); + } + + /* free the file buffer */ + if (wasm_file_buf) { + wasm_runtime_free(wasm_file_buf); + } + + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/ref-types/src/hello.wat b/samples/ref-types/src/hello.wat new file mode 100644 index 0000000000..3c2d2f96de --- /dev/null +++ b/samples/ref-types/src/hello.wat @@ -0,0 +1,44 @@ +;; Copyright (C) 2019 Intel Corporation. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (type $t0 (func (param i32 externref) (result i32))) + + (import "env" "native-cmp-externref" + (func $native-cmp-externref (param externref externref) (result i32)) + ) + + (import "env" "native-chk-externref" + (func $native-chk-externref (param i32 externref) (result i32)) + ) + + (table $t1 8 8 externref) + (table $t2 funcref + (elem + $native-cmp-externref + $native-chk-externref + ) + ) + + (func (export "set-externref") (param $i i32) (param $r externref) + (table.set $t1 (local.get $i) (local.get $r)) + ) + + (func (export "get-externref") (param $i i32) (result externref) + (table.get $t1 (local.get $i)) + ) + + (func (export "cmp-externref") (param $i i32) (param $r externref) (result i32) + (table.get $t1 (local.get $i)) + (local.get $r) + (call $native-cmp-externref) + ) + + (func (export "chk-externref") (param $i i32) (param $r externref) (result i32) + (call_indirect $t2 (type $t0) + (local.get $i) + (local.get $r) + (i32.const 1) + ) + ) +) diff --git a/samples/sgx-ra/CMakeLists.txt b/samples/sgx-ra/CMakeLists.txt new file mode 100644 index 0000000000..48ff9ee569 --- /dev/null +++ b/samples/sgx-ra/CMakeLists.txt @@ -0,0 +1,79 @@ +# Copyright (c) 2022 Intel Corporation +# Copyright (c) 2020-2021 Alibaba Cloud +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(sgx-ra) + +################ runtime settings ############## +set (WAMR_BUILD_PLATFORM "linux-sgx") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET +if (NOT DEFINED WAMR_BUILD_TARGET) + if (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_LIBC_WASI 1) +set (WAMR_BUILD_LIB_PTHREAD 1) +set (WAMR_BUILD_FAST_INTERP 1) +set (WAMR_BUILD_LIB_RATS 1) + +# compiling and linking flags +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 \ + -nostdinc -fvisibility=hidden -fpie" ) + +# build out vmlib +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set (SGX_PLATFORM_DIR ${WAMR_ROOT_DIR}/product-mini/platforms/linux-sgx) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +add_custom_command ( + OUTPUT libvmlib_untrusted.a + COMMAND mkdir -p untrusted && cd untrusted && + ${CMAKE_C_COMPILER} -c ${PLATFORM_SHARED_SOURCE_UNTRUSTED} + COMMAND ${CMAKE_AR} rc libvmlib_untrusted.a untrusted/*.o) + +add_custom_target (vmlib_untrusted ALL DEPENDS libvmlib_untrusted.a) + +execute_process ( + COMMAND bash -c "sed -i -E 's/^#define WASM_ENABLE_LIB_RATS 0/#define WASM_ENABLE_LIB_RATS 1/g' ${SGX_PLATFORM_DIR}/enclave-sample/Enclave/Enclave.edl" + COMMAND bash -c "sed -i -E 's/^WAMR_BUILD_LIB_RATS = 0/WAMR_BUILD_LIB_RATS = 1/g' ${SGX_PLATFORM_DIR}/enclave-sample/Makefile" + OUTPUT_VARIABLE cmdOutput +) + +################ wamr runtime ################### +add_custom_target ( + iwasm ALL + DEPENDS vmlib_untrusted vmlib_untrusted vmlib + COMMAND make -C ${SGX_PLATFORM_DIR}/enclave-sample clean + COMMAND make -C ${SGX_PLATFORM_DIR}/enclave-sample SGX_MODE=HW SGX_DEBUG=1 VMLIB_BUILD_DIR=${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${SGX_PLATFORM_DIR}/enclave-sample/enclave.signed.so ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${SGX_PLATFORM_DIR}/enclave-sample/iwasm ${CMAKE_BINARY_DIR} + COMMAND make -C ${SGX_PLATFORM_DIR}/enclave-sample clean) + +################ wasm application ############### +add_subdirectory(wasm-app) diff --git a/samples/sgx-ra/README.md b/samples/sgx-ra/README.md new file mode 100644 index 0000000000..42240b303f --- /dev/null +++ b/samples/sgx-ra/README.md @@ -0,0 +1,273 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/sgx-ra" +--- +"sgx-ra" sample introduction +============== + +This sample demonstrates how to execute Remote Attestation on SGX with [librats](https://github.com/inclavare-containers/librats) and run it with iwasm. It can only build on [SGX supported processors](https://www.intel.com/content/www/us/en/support/articles/000028173/processors.html), please check it. + +## Preparation + +SGX-RA requires to have installed: + - the WASI-SDK, located in `/opt/wasi-sdk` + +### Intel SGX dependencies + +Before starting, we need to download and install [SGX SDK](https://download.01.org/intel-sgx/latest/linux-latest/distro) and [SGX DCAP Library](https://download.01.org/intel-sgx/latest/dcap-latest) referring to this [guide](https://download.01.org/intel-sgx/sgx-dcap/1.8/linux/docs/Intel_SGX_DCAP_Linux_SW_Installation_Guide.pdf). + +The following commands are an example of the SGX environment installation on Ubuntu 20.04. +``` shell +# Set your platform, you can get the platforms list on +# https://download.01.org/intel-sgx/latest/linux-latest/distro +$ cd $HOME +$ OS_PLATFORM=ubuntu20.04 +$ OS_CODE_NAME=`lsb_release -sc` +$ SGX_PLATFORM=$OS_PLATFORM-server +$ SGX_RELEASE_VERSION=1.17 +$ SGX_DRIVER_VERSION=1.41 +$ SGX_SDK_VERSION=2.20.100.4 + +# install the dependencies +$ sudo apt-get update +$ sudo apt-get install -y build-essential ocaml automake autoconf libtool wget python3 libssl-dev dkms zip cmake +$ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 1 + +# install SGX Driver +$ wget https://download.01.org/intel-sgx/sgx-dcap/$SGX_RELEASE_VERSION/linux/distro/$SGX_PLATFORM/sgx_linux_x64_driver_$SGX_DRIVER_VERSION.bin +$ chmod +x sgx_linux_x64_driver_$SGX_DRIVER_VERSION.bin +$ sudo ./sgx_linux_x64_driver_$SGX_DRIVER_VERSION.bin + +# install SGX SDK +$ wget https://download.01.org/intel-sgx/sgx-dcap/$SGX_RELEASE_VERSION/linux/distro/$SGX_PLATFORM/sgx_linux_x64_sdk_$SGX_SDK_VERSION.bin +$ chmod +x sgx_linux_x64_sdk_$SGX_SDK_VERSION.bin +$ sudo ./sgx_linux_x64_sdk_$SGX_SDK_VERSION.bin --prefix /opt/intel + +# install SGX DCAP Library +$ echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu $OS_CODE_NAME main" | sudo tee /etc/apt/sources.list.d/intel-sgx.list +$ wget -O - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add +$ sudo apt-get update +$ sudo apt-get install -y libsgx-epid libsgx-quote-ex libsgx-dcap-ql libsgx-enclave-common-dev libsgx-dcap-ql-dev libsgx-dcap-default-qpl-dev libsgx-dcap-quote-verify-dev + +# install SGX SSL Library +$ git clone https://github.com/intel/linux-sgx.git +$ cd linux-sgx && make preparation +$ sudo cp external/toolset/$OS_PLATFORM/* /usr/local/bin +$ # Verify that the paths are correctly set +$ which ar as ld objcopy objdump ranlib +$ cd ../ +$ git clone https://github.com/intel/intel-sgx-ssl.git +$ wget https://www.openssl.org/source/openssl-1.1.1v.tar.gz -O intel-sgx-ssl/openssl_source/openssl-1.1.1v.tar.gz +$ cd intel-sgx-ssl/Linux +$ source /opt/intel/sgxsdk/environment +$ make all +$ sudo make install +``` + +You can optionally grant users to communicate with the SDK platform using the following command. +Otherwise, enclaves must be launched with root privileges. + +```shell +sudo usermod -a -G sgx_prv +``` + +### Intel Provisioning Certification Service (Intel PCS) + +Intel DCAP connects to Intel PCS to download the attestation collateral for SGX-enabled machines. +Intel provides a [quick install guide](https://www.intel.com/content/www/us/en/developer/articles/guide/intel-software-guard-extensions-data-center-attestation-primitives-quick-install-guide.html) to set up a simplified environment. +This section summarizes the commands to issue for setting up a working environment on Ubuntu 20.04. + +### Subscribe to Intel PCS Web services + +Intel SGX DCAP requires a complimentary subscription to the Intel PCS. +To subscribe to the service, browse the [Intel SGX Software Services](https://api.portal.trustedservices.intel.com/) page. +A the end of the subscription process, save the primary and the secondary keys. + +### Set up the Intel Provisioning Certification Caching Service (Intel PCCS) + +Intel PCCS is a caching mechanism for attestation collateral, preventing continuously communicating with Intel PCS during attestation. +Intel provides an implementation of the cache mechanism. + +The following commands set up Intel PCCS. +```shell +# install Node.js +$ sudo apt install -y curl cracklib-runtime +$ curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs +# install PCCS software +$ sudo apt-get install -y sgx-dcap-pccs +``` + +The installation will run the PCCS setup script, asking you several questions. + +``` +Do you want to configure PCCS now? (Y/N) +``` + +Answer "Y" to this question. + +``` +Set HTTPS listening port [8081] (1024-65535) +``` + +Accept the default listening port of 8081. + +``` +Set the PCCS service to accept local connections only? [Y] (Y/N) +``` + +Answer "N" to this question. We want the PCCS service to accept connections from other systems. + +``` +Set your Intel PCS API key (Press ENTER to skip) +``` + +Enter either your primary or secondary key retrieved from the previous subsection. +If you already subscribed, you can retrieve them [here](https://api.portal.trustedservices.intel.com/developer). + +``` +Choose caching fill method : [LAZY] (LAZY/OFFLINE/REQ) +``` + +Answer "REQ" to this question. This places the caching service in the "on request" mode, which means it will fetch the attestation collateral for hosts as provisioning requests are received. + +``` +Set PCCS server administrator password: +Re-enter administrator password: +Set PCCS server user password: +Re-enter user password: +``` + +Enter two passwords for the PCCS server. + +``` +Do you want to generate insecure HTTPS key and cert for PCCS service? [Y] (Y/N) +``` + +Answer "Y" to this question. + +### Provisioning the current system's Intel SGX collateral into the PCCS + +Now that the PCCS is up and running, it's time to provision an Intel SGX-enabled platform. +We use the tool `PCKIDRetrievalTool` to get the attestation collateral of the current machine. + +``` shell +$ sudo apt-get install -y sgx-pck-id-retrieval-tool +``` + +Adapt the configuration file of `PCKIDRetrievalTool` located in `/opt/intel/sgx-pck-id-retrieval-tool/network_setting.conf` and make the following changes: +- Change the **PCCS_URL** to match your caching service's location. +- Uncomment the **user_token** parameter, and set it to the user password you created when configuring the PCCS. +- Set the **proxy_type** to fit your environment (most likely, this will be `direct`) +- Ensure **USE_SECURE_CERT** is set to `FALSE` since we're using a self-signed certificate for testing purposes. + +Save your changes and run the provisioning tool. + +```shell +$ sudo PCKIDRetrievalTool +Intel(R) Software Guard Extensions PCK Cert ID Retrieval Tool Version 1.17.100.4 + +Registration status has been set to completed status. +the data has been sent to cache server successfully and pckid_retrieval.csv has been generated successfully! +``` + +You may get some warnings during this execution of the tool. +A correct insertion into the cache server usually means the retrieval of the attestation collateral worked. +Execute the following command to verify the collateral could be stored in your instance of Intel PCCS: + +``` +curl -k https://localhost:8081/sgx/certification/v3/qe/identity +``` + +This should print a JSON value with the attestation collateral. + +### Runtime configuration + +Edit the configuration file, `/etc/sgx_default_qcnl.conf`, and make the following changes: +- Set the **PCCS_URL** parameter to the location of our PCCS server. +- Set **USE_SECURE_CERT** to `FALSE` since we're using a self-signed certificate for testing purposes. + +This system is now ready to run Intel SGX workloads with generate evidence for remote attestation. + +## Build and executing the sample + +``` shell +$ mkdir build && cd build +$ cmake .. +$ make +$ # run the sample +$ ./iwasm wasm-app/test.wasm +``` + +The sample will print the evidence in JSON and the message: *Evidence is trusted.* + +In case of validation issues expressed as a value of `0xeXXX`, the corresponding error reason is explained in [this header file](https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/quote_wrapper/common/inc/sgx_ql_lib_common.h). + +## Validate quotes on non-SGX platforms +Quotes created on an Intel SGX platform can also be verified on systems that do not support SGX (e.g., a different CPU architecture). +This scenario typically arises when deploying trusted applications in a cloud environment, which provides confidential computing. + +For that purpose, we are required to install a subset of Intel SGX libraries to support quote validation. +The steps below highlight how to set up such an environment. + + +### Intel SGX dependencies +```shell +$ OS_CODE_NAME=`lsb_release -sc` +# install SGX DCAP Library +$ echo "deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu $OS_CODE_NAME main" | sudo tee /etc/apt/sources.list.d/intel-sgx.list +$ wget -O - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add +$ sudo apt-get update +$ sudo apt-get install -y libsgx-quote-ex libsgx-dcap-ql libsgx-dcap-quote-verify libsgx-dcap-default-qpl +``` + +### Set up the Intel Provisioning Certification Caching Service (Intel PCCS) +Follow the steps described in the section _Set up the Intel Provisioning Certification Caching Service (Intel PCCS)_. + +### Runtime configuration +Follow the steps described in the section _Runtime configuration_. + +### Provisioning all the Intel SGX collateral into the PCCS +We must finally fetch and configure the SGX collaterals into the PCCS for all the SGX-enabled CPUs. + +```shell +# Set up the Intel PCCS administration tool +$ git clone https://github.com/intel/SGXDataCenterAttestationPrimitives.git +$ cd SGXDataCenterAttestationPrimitives/tools/PccsAdminTool +$ sudo apt-get install -y python3 python3-pip +$ pip3 install -r requirements.txt + +# Configuring the Intel PCCS. Input the PCS/PCCS password as requested. +# 1. Get registration data from PCCS service +./pccsadmin.py get +# 2. Fetch platform collateral data from Intel PCS based on the registration data +./pccsadmin.py fetch +# 3. Put platform collateral data or appraisal policy files to PCCS cache db +./pccsadmin.py put +# 4. Request PCCS to refresh certificates or collateral in cache database +./pccsadmin.py refresh +``` + +### Validation of the quotes +The Wasm application can then be modified to validate precomputed quotes using the exposed function `librats_verify`. + +Alternatively, the underlying library `librats` may be directly used if the non-SGX platforms do not execute WebAssembly code (without WAMR). +Examples are provided in the directory [non-sgx-verify/](non-sgx-verify/). + +### Claims validation +Once the runtime has validated the signature of the quote, the application must also check the other claims embedded in the quote to ensure they match their expected value. + +The documentation _Data Center Attestation Primitives: Library API_ describes in Section _3.8 Enclave Identity Checking_ defines the claims for the user to check. +Here is a summary of them: + +- **Enclave Identity Checking**: either check the hash _MRENCLAVE_ (the enclave identity) or _MRSIGNER_ and the _product id_ (the software provider identity). +- **Verify Attributes**: production enclaves should not have the _Debug_ flag set to 1. +- **Verify SSA Frame extended feature set** +- **Verify the ISV_SVN level of the enclave**: whenever there is a security update to an enclave, the ISV_SVN value should be increased to reflect the higher security level. +- **Verify that the ReportData contains the expected value**: This can be used to provide specific data from the enclave or it can be used to hold a hash of a larger block of data which is provided with the quote. Note that the verification of the quote signature confirms the integrity of the report data (and the rest of the REPORT body). + + +## Further readings + +- [Intel SGX Software Installation Guide For Linux OS](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_SW_Installation_Guide_for_Linux.pdf) +- [Intel Software Guard Extensions (Intel® SGX) Data Center Attestation Primitives: Library API](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf) +- [Remote Attestation for Multi-Package Platforms using Intel SGX Datacenter Attestation Primitives (DCAP)](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_DCAP_Multipackage_SW.pdf) +- [Documentation of the PCCS administration tool](https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/tools/PccsAdminTool/README.txt) diff --git a/samples/sgx-ra/non-sgx-verify/README.md b/samples/sgx-ra/non-sgx-verify/README.md new file mode 100644 index 0000000000..5b9b35430e --- /dev/null +++ b/samples/sgx-ra/non-sgx-verify/README.md @@ -0,0 +1,5 @@ +# Examples of evidence verification without Intel SGX +Intel SGX evidence generated using WAMR can be validated on trusted platforms without Intel SGX, or an Intel processors. + +## Using C# +The sample [csharp/](csharp/) demonstrates such validation using C# as a managed language. diff --git a/samples/sgx-ra/non-sgx-verify/csharp/.gitignore b/samples/sgx-ra/non-sgx-verify/csharp/.gitignore new file mode 100644 index 0000000000..27e48df5ab --- /dev/null +++ b/samples/sgx-ra/non-sgx-verify/csharp/.gitignore @@ -0,0 +1,3 @@ +.idea/ +bin/ +obj/ diff --git a/samples/sgx-ra/non-sgx-verify/csharp/Program.cs b/samples/sgx-ra/non-sgx-verify/csharp/Program.cs new file mode 100644 index 0000000000..6d7753a1be --- /dev/null +++ b/samples/sgx-ra/non-sgx-verify/csharp/Program.cs @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 Intel Corporation. + * Copyright (C) 2024 University of Neuchatel, Switzerland. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; + +// Set the reference values below +byte[] mrEnclaveReference = +{ + 0xDA, 0xE0, 0xDA, 0x2F, 0x8A, 0x53, 0xA0, 0xB4, 0x8F, 0x92, 0x6A, 0x3B, 0xC0, 0x48, 0xD6, 0xA9, + 0x67, 0xD4, 0x7C, 0x86, 0x19, 0x86, 0x76, 0x6F, 0x8F, 0x5A, 0xB1, 0xC0, 0xA8, 0xD8, 0x8E, 0x44 +}; +byte[] mrSignerReference = +{ + 0x83, 0xD7, 0x19, 0xE7, 0x7D, 0xEA, 0xCA, 0x14, 0x70, 0xF6, 0xBA, 0xF6, 0x2A, 0x4D, 0x77, 0x43, + 0x03, 0xC8, 0x99, 0xDB, 0x69, 0x02, 0x0F, 0x9C, 0x70, 0xEE, 0x1D, 0xFC, 0x08, 0xC7, 0xCE, 0x9E +}; +const ushort securityVersionReference = 0; +const ushort productIdReference = 0; +string nonce = "This is a sample.\0"; // Notice the \0 at the end, which is mandatory as C-strings are terminated with this char +string evidenceAsString = """{"type":"sgx_ecdsa","report_base64":"[..]","report_len":[..]}"""; +string wasmFilePath = "../build/wasm-app/test.wasm"; + +// Parse and compute the claims +EvidenceJson? evidenceAsJson = JsonSerializer.Deserialize(evidenceAsString, new JsonSerializerOptions +{ + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower +}); +Debug.Assert(evidenceAsJson != null, "The evidence cannot be parsed."); + +byte[] wasmFileContent = await File.ReadAllBytesAsync(wasmFilePath); +byte[] nonceAsBytes = Encoding.UTF8.GetBytes(nonce); +byte[] computedUserData = await ComputeUserData(wasmFileContent, nonceAsBytes); +byte[] evidenceAsBytes = Convert.FromBase64String(evidenceAsJson.ReportBase64); +Evidence evidence = new(evidenceAsBytes); +int libRatsReturnValue = LibRats.VerifyEvidenceFromJson(evidenceAsString, await ComputeUserData(wasmFileContent, nonceAsBytes)); + +// Compare and display the results +Console.WriteLine($"User data, evidence: {BitConverter.ToString(evidence.UserData)}"); +Console.WriteLine($"User Data, computed: {BitConverter.ToString(computedUserData)}"); +Console.WriteLine($"Do the two user data match? {evidence.UserData.SequenceEqual(computedUserData)}"); +Console.WriteLine($"MrEnclave: {BitConverter.ToString(evidence.MrEnclave)}"); +Console.WriteLine($"Do the MrEnclave match? {mrEnclaveReference.SequenceEqual(evidence.MrEnclave)}"); +Console.WriteLine($"MrSigner: {BitConverter.ToString(evidence.MrSigner)}"); +Console.WriteLine($"Do the MrSigner match? {mrSignerReference.SequenceEqual(evidence.MrSigner)}"); +Console.WriteLine($"Security Version: {evidence.SecurityVersion}, expected: {securityVersionReference}"); +Console.WriteLine($"Product ID: {evidence.ProductId}, expected: {productIdReference}"); +Console.WriteLine($"VerifyJsonUsingLibrats returned: {libRatsReturnValue:X}"); + +// Compute the user data as computed by WAMR +static async ValueTask ComputeUserData(byte[] wasmFileContent, byte[] nonce) +{ + using var sha256 = SHA256.Create(); + var wasmFileContentHash = sha256.ComputeHash(wasmFileContent); + + using MemoryStream stream = new(); + await stream.WriteAsync(wasmFileContentHash); + await stream.WriteAsync(nonce); + stream.Position = 0; + + byte[] computedUserData = await sha256.ComputeHashAsync(stream); + return computedUserData; +} + +/// +/// The layout of the JSON is given by librats. +/// +class EvidenceJson +{ + public required string Type { get; init; } + public required string ReportBase64 { get; init; } + public required int ReportLen { get; init; } +} + +/// +/// The start of the _report_body_t struct from Intel SGX is at offset 0x30. +/// +/// +/// _report_body_t struct: https://github.com/intel/linux-sgx/blob/a1eeccba5a72b3b9b342569d2cc469ece106d3e9/common/inc/sgx_report.h#L93-L111 +/// Attestation flow: https://www.intel.com/content/www/us/en/developer/articles/code-sample/software-guard-extensions-remote-attestation-end-to-end-example.html +/// +class Evidence(byte[] evidenceAsBytes) +{ + public byte[] MrEnclave => evidenceAsBytes[0x70..0x90]; + public byte[] MrSigner => evidenceAsBytes[0xB0..0xD0]; + public ushort ProductId => BitConverter.ToUInt16(evidenceAsBytes.AsSpan(0x130, 2)); + public ushort SecurityVersion => BitConverter.ToUInt16(evidenceAsBytes.AsSpan(0x132, 2)); + public byte[] UserData => evidenceAsBytes[0x170..0x190]; +} + +static class LibRats +{ + /// + /// Verifies the evidence using librats native function. + /// + /// + /// Original signature: int librats_verify_evidence_from_json(const char *json_string, const uint8_t *hash); + /// + [DllImport("/usr/local/lib/librats/librats_lib.so", EntryPoint = "librats_verify_evidence_from_json")] + public static extern int VerifyEvidenceFromJson(string json, byte[] hash); +} diff --git a/samples/sgx-ra/non-sgx-verify/csharp/README.md b/samples/sgx-ra/non-sgx-verify/csharp/README.md new file mode 100644 index 0000000000..689c8da083 --- /dev/null +++ b/samples/sgx-ra/non-sgx-verify/csharp/README.md @@ -0,0 +1,18 @@ +# Examples of evidence verification without Intel SGX using C# +This sample demonstrates how to validate WAMR-generated evidence without using an Intel SGX-enabled platform. +A typical use case is a Web service hosted on trusted premises. + +## Prerequisites + - [dotnet-sdk](https://learn.microsoft.com/en-us/dotnet/core/install/linux) (8+) + - [librats](https://github.com/inclavare-containers/librats) + - Intel infrastructure for validating evidence, [see here](../../README.md#validate-quotes-on-non-sgx-platforms) + +This sample has been tested on Linux Ubuntu 20.04+. +Any other Linux platforms should be supported. +This sample should also work on other OS, provided librats can be compiled on those other OS. + +## How to use + - Supply the reference values to consider trustworthy in [Program.cs](Program.cs#L15-L27). + - Generate a valid JSON evidence using WAMR on an Intel SGX-enabled platform. + - Fill in the JSON evidence in [Program.cs](Program.cs#L28). + - Run the command `dotnet run` in this directory. diff --git a/samples/sgx-ra/non-sgx-verify/csharp/VerifyEvidence.csproj b/samples/sgx-ra/non-sgx-verify/csharp/VerifyEvidence.csproj new file mode 100644 index 0000000000..206b89a9a8 --- /dev/null +++ b/samples/sgx-ra/non-sgx-verify/csharp/VerifyEvidence.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/samples/sgx-ra/wasm-app/CMakeLists.txt b/samples/sgx-ra/wasm-app/CMakeLists.txt new file mode 100644 index 0000000000..93907b7f0d --- /dev/null +++ b/samples/sgx-ra/wasm-app/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2022 Intel Corporation +# Copyright (c) 2020-2021 Alibaba Cloud +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(wasm-app) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) +set (LIB_RATS_DIR ${WAMR_ROOT_DIR}/core/iwasm/libraries/lib-rats) + +set (CMAKE_C_LINK_FLAGS "") +set (CMAKE_CXX_LINK_FLAGS "") +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) +endif () + +set (CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--max-memory=131072 -z stack-size=8192 \ + -Wl,--no-entry,--strip-all \ + -Wl,--export=__main_argc_argv \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--allow-undefined" +) + +add_executable(test.wasm main.c) +set_target_properties(test.wasm PROPERTIES INCLUDE_DIRECTORIES ${LIB_RATS_DIR}) +target_link_libraries(test.wasm) diff --git a/samples/sgx-ra/wasm-app/main.c b/samples/sgx-ra/wasm-app/main.c new file mode 100644 index 0000000000..ab615056eb --- /dev/null +++ b/samples/sgx-ra/wasm-app/main.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2022 Intel Corporation + * Copyright (c) 2020-2021 Alibaba Cloud + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "lib_rats_wrapper.h" + +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') + +/** + * hex_dump + * + * @brief dump data in hex format + * + * @param title: Title + * @param buf: User buffer + * @param size: Dump data size + * @param number: The number of outputs per line + * + * @return void + */ +void +hex_dump(const char *title, const uint8_t *buf, uint32_t size, uint32_t number) +{ + int i, j; + if (title) { + printf("\n\t%s:\n\n", title); + } + + for (i = 0; i < size; i += number) { + printf("%08X: ", i); + + for (j = 0; j < number; j++) { + if (j % 8 == 0) { + printf(" "); + } + if (i + j < size) + printf("%02X ", buf[i + j]); + else + printf(" "); + } + printf(" "); + + for (j = 0; j < number; j++) { + if (i + j < size) { + printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); + } + } + printf("\n"); + } +} + +int +main(int argc, char **argv) +{ + int ret_code = -1; + char *evidence_json = NULL; + + // Generate user_data by SHA256 buffer and the wasm module. + // user_data = SHA256(sha256_wasm_module || buffer) + const char *buffer = "This is a sample."; + + // If you want to declare the evidence of type rats_sgx_evidence_t on the + // stack, you should modify the stack size of the CMAKE_EXE_LINKER_FLAGS in + // CMakeLists.txt to 51200 at least. + rats_sgx_evidence_t *evidence = + (rats_sgx_evidence_t *)malloc(sizeof(rats_sgx_evidence_t)); + if (!evidence) { + printf("ERROR: No memory to allocate.\n"); + goto err; + } + + int rats_err = librats_collect(&evidence_json, buffer); + if (rats_err != 0) { + printf("ERROR: Collect evidence failed, error code: %#x\n", rats_err); + goto err; + } + + if (librats_parse_evidence(evidence_json, evidence) != 0) { + printf("ERROR: Parse evidence failed.\n"); + goto err; + } + + // You could use these parameters for further verification. + hex_dump("Quote", evidence->quote, evidence->quote_size, 32); + hex_dump("User Data", evidence->user_data, SGX_USER_DATA_SIZE, 32); + hex_dump("MRENCLAVE", evidence->mr_enclave, SGX_MEASUREMENT_SIZE, 32); + hex_dump("MRSIGNER", evidence->mr_signer, SGX_MEASUREMENT_SIZE, 32); + printf("\n\tProduct ID:\t\t\t\t%u\n", evidence->product_id); + printf("\tSecurity Version:\t\t\t%u\n", evidence->security_version); + printf("\tAttributes.flags:\t\t\t%llu\n", evidence->att_flags); + printf("\tAttributes.flags[INITTED]:\t\t%d\n", + (evidence->att_flags & SGX_FLAGS_INITTED) != 0); + printf("\tAttributes.flags[DEBUG]:\t\t%d\n", + (evidence->att_flags & SGX_FLAGS_DEBUG) != 0); + printf("\tAttributes.flags[MODE64BIT]:\t\t%d\n", + (evidence->att_flags & SGX_FLAGS_MODE64BIT) != 0); + printf("\tAttributes.flags[PROVISION_KEY]:\t%d\n", + (evidence->att_flags & SGX_FLAGS_PROVISION_KEY) != 0); + printf("\tAttributes.flags[EINITTOKEN_KEY]:\t%d\n", + (evidence->att_flags & SGX_FLAGS_EINITTOKEN_KEY) != 0); + printf("\tAttributes.flags[KSS]:\t\t\t%d\n", + (evidence->att_flags & SGX_FLAGS_KSS) != 0); + printf("\tAttributes.flags[AEX_NOTIFY]:\t\t%d\n", + (evidence->att_flags & SGX_FLAGS_AEX_NOTIFY) != 0); + printf("\tAttribute.xfrm:\t\t\t\t%llu\n", evidence->att_xfrm); + + rats_err = librats_verify((const char *)evidence_json, evidence->user_data); + if (rats_err != 0) { + printf("ERROR: Evidence is not trusted, error code: %#x.\n", rats_err); + goto err; + } + + ret_code = 0; + printf("Evidence is trusted.\n"); + +err: + if (evidence_json) { + librats_dispose_evidence_json(evidence_json); + } + + if (evidence) { + free(evidence); + } + + return ret_code; +} diff --git a/samples/shared-heap/CMakeLists.txt b/samples/shared-heap/CMakeLists.txt new file mode 100644 index 0000000000..89e79a5b3f --- /dev/null +++ b/samples/shared-heap/CMakeLists.txt @@ -0,0 +1,135 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (shared_heap_test) +else() + project (shared_heap_test C ASM) +endif() + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 Debug) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_FAST_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_LIBC_WASI 0) +set (WAMR_BUILD_SHARED_HEAP 1) +set (WAMR_BUILD_GC_HEAP_VERIFY 1) + +if (NOT MSVC) + # linker 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 () +endif () + +# build out vmlib +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +if (MSVC) + target_compile_definitions(vmlib PRIVATE WASM_API_EXTERN=) +endif() +target_link_libraries(vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (shared_heap_chain_test src/shared_heap_chain.c ${UNCOMMON_SHARED_SOURCE}) +add_executable (shared_heap_test src/main.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (shared_heap_test PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + set (LIBS vmlib -lm -ldl -lpthread) +else () + set (LIBS vmlib -lm -ldl -lpthread -lrt) +endif () + +target_link_libraries (shared_heap_chain_test ${LIBS}) +target_link_libraries (shared_heap_test ${LIBS}) + +add_subdirectory(wasm-apps) + +if (WAMR_BUILD_AOT EQUAL 1) + set (WAMR_COMPILER_DIR ${CMAKE_CURRENT_LIST_DIR}/../../wamr-compiler/build) + message (CHECK_START "Detecting WAMR_COMPILER at ${WAMR_COMPILER_DIR}") + find_file (WAMR_COMPILER + wamrc + PATHS "${CMAKE_CURRENT_LIST_DIR}/../../wamr-compiler/build" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if (WAMR_COMPILER) + message (CHECK_PASS "found") + else () + message (CHECK_FAIL "not found") + endif () + if (NOT EXISTS ${WAMR_COMPILER}) + message (FATAL_ERROR "Please build wamrc under ${WAMR_ROOT_DIR}/wamr-compiler") + else () + message (STATUS "WAMR_COMPILER is ${WAMR_COMPILER}") + endif () + + if (WAMR_BUILD_TARGET STREQUAL "X86_32") + set (WAMR_COMPILER_FLAGS --enable-shared-heap --target=i386) + set (WAMR_COMPILER_CHAIN_FLAGS --enable-shared-chain --target=i386) + else () + set (WAMR_COMPILER_FLAGS --enable-shared-heap) + set (WAMR_COMPILER_CHAIN_FLAGS --enable-shared-chain) + endif () + + add_custom_target( + wasm_to_aot + ALL + DEPENDS wasm-apps/test1.wasm wasm-apps/test2.wasm ${WAMR_COMPILER} + COMMAND ${WAMR_COMPILER} ${WAMR_COMPILER_FLAGS} -o wasm-apps/test1.aot wasm-apps/test1.wasm + COMMAND ${WAMR_COMPILER} ${WAMR_COMPILER_FLAGS} -o wasm-apps/test2.aot wasm-apps/test2.wasm + COMMAND ${WAMR_COMPILER} ${WAMR_COMPILER_CHAIN_FLAGS} -o wasm-apps/test1_chain.aot wasm-apps/test1.wasm + COMMAND ${WAMR_COMPILER} ${WAMR_COMPILER_CHAIN_FLAGS} -o wasm-apps/test2_chain.aot wasm-apps/test2.wasm + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endif() diff --git a/samples/shared-heap/README.md b/samples/shared-heap/README.md new file mode 100644 index 0000000000..6e43ceab91 --- /dev/null +++ b/samples/shared-heap/README.md @@ -0,0 +1,50 @@ +# Shared heap Sample introduction + +This is a sample to show how to use the shared heap feature in WAMR. The shared heap feature allows multiple WASM instances to share the same memory space. This feature is useful when you want to run multiple WASM instances in the same process and share data between them. The sandbox nature of WASM is still maintained in the shared heap by WAMR. But the data management and correct data synchronization in shared heap is relied on the user's implementation. + +> Note: The shared heap feature is experimental feature, it should be used with caution. It's optional and only available when building WAMR with the CMake cache variable `WAMR_BUILD_SHARED_HEAP` set to 1. + +## Build and run the sample + +To build the shared heap used in multi thread sample and the shared heap chain sample with following commands: + +```bash +cmake -S . -B build +cmake --build build +``` + +For the shared heap sample, it demonstrates how to create a shared heap and use it shares data between two WASM instances, which would satisfy most of the use cases. Use the following commands to run the sample: + +```bash +cd build +./shared_heap_test +``` + +For the shared heap chain sample. It chains a pre-allocated heap and a normal shared heap to one chain(linked list) as a whole and attaches/detaches all together, and pass the WASM address directly between two WASM instances. Use the following commands to run the sample: + +```bash +cd build +./shared_heap_chain_test +``` + +## How to use shared heap + +The shared heap is an advanced feature in WAMR that gives the user flexibility to share data between multiple WASM instances(it will be the same address mapping for different WASM instance) or between WebAssembly and the host without incurring any copy overhead. The shared heap can be regarded as an extension of the WebAssembly linear memory. But it also heavily relies on the user's implementation to manage the shared data correctly. The following are some takeaway points to help the user use the shared heap correctly. + +### Create and manage shared heap + +You can create a shared heap by calling the `wasm_runtime_create_shared_heap(SharedHeapInitArgs *init_args)` API. And based on the `init_args`, you can create a shared heap in two ways: + +1. WAMR managed shared heap: when only `init_args.size` is given and `init_args.pre_allocated_addr` stays as NULL, WAMR will allocate a shared heap(not from the linear memory) with the given size. The shared heap will be managed by WAMR, the wasm app or host(WAMR users) can dynamically manage memory from it by calling `wasm_runtime_shared_heap_malloc()` and `wasm_runtime_shared_heap_free()` on demand. Only the memory allocated from the shared heap is valid and can be shared, not the unallocated part of shared heap memory. And it will be automatically freed when runtime is destroyed(when `wasm_runtime_destroy()` is called). + +2. Preallocated shared heap: the user can also use a pre-allocated memory(it can be allocated from the system heap, or is a static global buffer, the correctness of its accessibility and size needs to be ensured by the user) as a shared heap by giving `init_args.pre_allocated_addr` and `init_args.size`. This kind of shared heap serves as an area for data exchange, primarily between the host and WebAssembly. Any data within this area can be directly accessed by both sides (assuming the layout of the data structure is known). For instance, the host can store large structured variables in this space, allowing the WebAssembly application to operate on them without the need for copying. And the pre-allocated memory will relies on user to manage its life cycle. + +After creation, the shared heap can be attached to a WASM instance(an additional segment appended to the end of the linear memory) by calling `wasm_runtime_attach_shared_heap(wasm_module_inst_t module_inst, wasm_shared_heap_t shared_heap)`. And it can be detached by calling `wasm_runtime_detach_shared_heap(wasm_module_inst_t module_inst)`. So that the data sharing can only happen between the WASM instances that have the same shared heap attached, complete by user's choice. + +#### Shared heap chain + +Sometimes you may want to use multiple shared heaps to attach together as a chain(linked list) and to share data more flexibly. You can call `wasm_runtime_chain_shared_heaps(wasm_shared_heap_t head, wasm_shared_heap_t body)` to chain two shared heaps together. The shared heap list remains one continuous shared heap in wasm app's point of view. To create a shared heap chain, the shared heaps can't be currently attached to any WASM instance. + +> PS: At most one shared heap in shared heap list can be WAMR managed shared heap, the rest have to be the pre-allocated shared heap. + +![shared-heap-chain](./images/shared_heap_chain.png) diff --git a/samples/shared-heap/images/shared_heap_chain.png b/samples/shared-heap/images/shared_heap_chain.png new file mode 100644 index 0000000000..740a709d49 Binary files /dev/null and b/samples/shared-heap/images/shared_heap_chain.png differ diff --git a/samples/shared-heap/src/main.c b/samples/shared-heap/src/main.c new file mode 100644 index 0000000000..111da33999 --- /dev/null +++ b/samples/shared-heap/src/main.c @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_platform.h" +#include "bh_read_file.h" + +typedef struct thread_arg { + bh_queue *queue; + wasm_module_inst_t module_inst; +} thread_arg; + +static void * +thread1_callback(void *arg) +{ + thread_arg *targ = arg; + wasm_module_inst_t module_inst = targ->module_inst; + bh_queue *queue = targ->queue; + wasm_exec_env_t exec_env; + wasm_function_inst_t my_shared_heap_malloc_func; + wasm_function_inst_t my_shared_heap_free_func; + uint32 i, argv[2]; + + /* lookup wasm functions */ + if (!(my_shared_heap_malloc_func = wasm_runtime_lookup_function( + module_inst, "my_shared_heap_malloc")) + || !(my_shared_heap_free_func = wasm_runtime_lookup_function( + module_inst, "my_shared_heap_free"))) { + printf("Failed to lookup function.\n"); + } + + /* create exec env */ + if (!(exec_env = wasm_runtime_create_exec_env(module_inst, 32768))) { + printf("Failed to create exec env.\n"); + return NULL; + } + + /* allocate memory with wasm_runtime_shared_heap_malloc and send it + to wasm app2 */ + for (i = 0; i < 5; i++) { + uint8 *buf; + uint64 offset; + + offset = wasm_runtime_shared_heap_malloc(module_inst, 1024 * (i + 1), + (void **)&buf); + + if (offset == 0) { + printf("Failed to allocate memory from shared heap\n"); + break; + } + + snprintf(buf, 1024, "Hello, this is buf %u allocated from shared heap", + i + 1); + + printf("wasm app1 send buf: %s\n\n", buf); + if (!bh_post_msg(queue, 1, buf, 1024 * (i + 1))) { + printf("Failed to post message to queue\n"); + wasm_runtime_shared_heap_free(module_inst, offset); + break; + } + } + + /* allocate memory by calling my_shared_heap_malloc function and send it + to wasm app2 */ + for (i = 5; i < 10; i++) { + uint8 *buf; + + argv[0] = 1024 * (i + 1); + argv[1] = i + 1; + wasm_runtime_call_wasm(exec_env, my_shared_heap_malloc_func, 2, argv); + + if (wasm_runtime_get_exception(module_inst)) { + printf("Failed to call 'my_shared_heap_malloc' function: %s\n", + wasm_runtime_get_exception(module_inst)); + break; + } + if (argv[0] == 0) { + printf("Failed to allocate memory from shared heap\n"); + break; + } + + buf = wasm_runtime_addr_app_to_native(module_inst, argv[0]); + + printf("wasm app1 send buf: %s\n\n", buf); + if (!bh_post_msg(queue, 1, buf, 1024 * (i + 1))) { + printf("Failed to post message to queue\n"); + wasm_runtime_shared_heap_free(module_inst, argv[0]); + break; + } + } + + wasm_runtime_destroy_exec_env(exec_env); + + return NULL; +} + +static void +queue_callback(void *message, void *arg) +{ + bh_message_t msg = (bh_message_t)message; + wasm_exec_env_t exec_env = arg; + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + wasm_function_inst_t print_buf_func; + uint32 argv[2]; + + /* lookup wasm function */ + if (!(print_buf_func = + wasm_runtime_lookup_function(module_inst, "print_buf"))) { + printf("Failed to lookup function.\n"); + return; + } + + char *buf = bh_message_payload(msg); + printf("wasm app's native queue received buf: %s\n\n", buf); + + /* call wasm function */ + argv[0] = wasm_runtime_addr_native_to_app(module_inst, buf); + wasm_runtime_call_wasm(exec_env, print_buf_func, 1, argv); + if (wasm_runtime_get_exception(module_inst)) { + printf("Failed to call 'print_buf' function: %s\n", + wasm_runtime_get_exception(module_inst)); + } +} + +static void * +thread2_callback(void *arg) +{ + thread_arg *targ = arg; + bh_queue *queue = targ->queue; + wasm_module_inst_t module_inst = targ->module_inst; + wasm_exec_env_t exec_env; + + /* create exec env */ + if (!(exec_env = wasm_runtime_create_exec_env(module_inst, 32768))) { + printf("Failed to create exec env.\n"); + return NULL; + } + + /* enter queue's message loop until bh_queue_exit_loop_run + is called */ + bh_queue_enter_loop_run(queue, queue_callback, exec_env); + + wasm_runtime_destroy_exec_env(exec_env); + + return NULL; +} + +static char global_heap_buf[512 * 1024]; + +int +main(int argc, char **argv) +{ + char *wasm_file1 = NULL, *wasm_file2 = NULL; + uint8 *wasm_file1_buf = NULL, *wasm_file2_buf = NULL; + uint32 wasm_file1_size, wasm_file2_size; + wasm_module_t wasm_module1 = NULL, wasm_module2 = NULL; + wasm_module_inst_t module_inst1 = NULL; + wasm_module_inst_t module_inst2 = NULL; + wasm_shared_heap_t shared_heap = NULL; + bh_queue *queue = NULL; + RuntimeInitArgs init_args; + SharedHeapInitArgs heap_init_args; + char error_buf[128] = { 0 }; + bool aot_mode = false; + int ret = -1; + + if (argc > 1 && !strcmp(argv[1], "--aot")) + aot_mode = true; + + if (!aot_mode) + printf("Test shared heap in interpreter mode\n\n"); + else + printf("Test shared heap in AOT mode\n\n"); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + /* init wasm runtime */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + /* create queue */ + if (!(queue = bh_queue_create())) { + printf("Create queue failed.\n"); + goto fail; + } + + /* read wasm file */ + if (!aot_mode) + wasm_file1 = "./wasm-apps/test1.wasm"; + else + wasm_file1 = "./wasm-apps/test1.aot"; + if (!(wasm_file1_buf = + bh_read_file_to_buffer(wasm_file1, &wasm_file1_size))) { + printf("Open wasm file %s failed.\n", wasm_file1); + goto fail; + } + + /* load wasm file */ + wasm_module1 = wasm_runtime_load((uint8 *)wasm_file1_buf, wasm_file1_size, + error_buf, sizeof(error_buf)); + if (!wasm_module1) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* instantiate module */ + module_inst1 = wasm_runtime_instantiate(wasm_module1, 65536, 0, error_buf, + sizeof(error_buf)); + if (!module_inst1) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* read wasm file */ + if (!aot_mode) + wasm_file2 = "./wasm-apps/test2.wasm"; + else + wasm_file2 = "./wasm-apps/test2.aot"; + if (!(wasm_file2_buf = + bh_read_file_to_buffer(wasm_file2, &wasm_file2_size))) { + printf("Open wasm file %s failed.\n", wasm_file1); + goto fail; + } + + /* load wasm file */ + wasm_module2 = wasm_runtime_load((uint8 *)wasm_file2_buf, wasm_file2_size, + error_buf, sizeof(error_buf)); + if (!wasm_module2) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* instantiate module */ + module_inst2 = wasm_runtime_instantiate(wasm_module2, 65536, 0, error_buf, + sizeof(error_buf)); + if (!module_inst2) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* create shared heap */ + memset(&heap_init_args, 0, sizeof(heap_init_args)); + heap_init_args.size = 65536; + shared_heap = wasm_runtime_create_shared_heap(&heap_init_args); + if (!shared_heap) { + printf("Create shared heap failed.\n"); + goto fail; + } + + /* attach module instance 1 to the shared heap */ + if (!wasm_runtime_attach_shared_heap(module_inst1, shared_heap)) { + printf("Attach shared heap failed.\n"); + goto fail; + } + + /* attach module instance 2 to the shared heap */ + if (!wasm_runtime_attach_shared_heap(module_inst2, shared_heap)) { + printf("Attach shared heap failed.\n"); + goto fail; + } + + /* create thread 1 */ + thread_arg targ1 = { 0 }; + korp_tid tid1; + targ1.queue = queue; + targ1.module_inst = module_inst1; + if (os_thread_create(&tid1, thread1_callback, &targ1, + APP_THREAD_STACK_SIZE_DEFAULT)) { + printf("Failed to create thread 1\n"); + goto fail; + } + + /* create thread 2 */ + thread_arg targ2 = { 0 }; + korp_tid tid2; + targ2.queue = queue; + targ2.module_inst = module_inst2; + if (os_thread_create(&tid2, thread2_callback, &targ2, + APP_THREAD_STACK_SIZE_DEFAULT)) { + printf("Failed to create thread 2\n"); + os_thread_join(tid1, NULL); + goto fail; + } + + /* wait until all messages are post to wasm app2 and wasm app2 + handles all of them, then exit the queue message loop */ + usleep(10000); + bh_queue_exit_loop_run(queue); + + os_thread_join(tid1, NULL); + os_thread_join(tid2, NULL); + + ret = 0; + +fail: + if (module_inst2) + wasm_runtime_deinstantiate(module_inst2); + + if (module_inst1) + wasm_runtime_deinstantiate(module_inst1); + + if (wasm_module2) + wasm_runtime_unload(wasm_module2); + + if (wasm_module1) + wasm_runtime_unload(wasm_module1); + + if (wasm_file2_buf) + wasm_runtime_free(wasm_file2_buf); + + if (wasm_file1_buf) + wasm_runtime_free(wasm_file1_buf); + + if (queue) + bh_queue_destroy(queue); + + wasm_runtime_destroy(); + + return ret; +} diff --git a/samples/shared-heap/src/shared_heap_chain.c b/samples/shared-heap/src/shared_heap_chain.c new file mode 100644 index 0000000000..f355b5dae2 --- /dev/null +++ b/samples/shared-heap/src/shared_heap_chain.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_platform.h" +#include "bh_read_file.h" + +#define BUF_SIZE 4096 +static char preallocated_buf[BUF_SIZE]; + +static bool +produce_data(wasm_module_inst_t module_inst, wasm_exec_env_t exec_env, + bh_queue *queue, wasm_function_inst_t func, uint32 *argv, + uint32 buf_size, bool free_on_fail) +{ + uint8 *buf; + + wasm_runtime_call_wasm(exec_env, func, 2, argv); + + if (wasm_runtime_get_exception(module_inst)) { + printf("Failed to call function: %s\n", + wasm_runtime_get_exception(module_inst)); + return false; + } + if (argv[0] == 0) { + printf("Failed to allocate memory from shared heap\n"); + return false; + } + + buf = wasm_runtime_addr_app_to_native(module_inst, argv[0]); + printf("wasm app1 send buf: %s\n\n", buf); + + /* Passes wasm address directly between wasm apps since memory in shared + * heap chain is viewed as single address space in wasm's perspective */ + buf = (uint8 *)(uintptr_t)argv[0]; + if (!bh_post_msg(queue, 1, buf, buf_size)) { + printf("Failed to post message to queue\n"); + if (free_on_fail) + wasm_runtime_shared_heap_free(module_inst, argv[0]); + return false; + } + + return true; +} + +static void * +wasm_producer(wasm_module_inst_t module_inst, bh_queue *queue) +{ + wasm_exec_env_t exec_env; + wasm_function_inst_t my_shared_heap_malloc_func, my_shared_heap_free_func, + produce_str_func; + uint32 i, argv[2]; + + /* lookup wasm functions */ + if (!(my_shared_heap_malloc_func = wasm_runtime_lookup_function( + module_inst, "my_shared_heap_malloc")) + || !(my_shared_heap_free_func = wasm_runtime_lookup_function( + module_inst, "my_shared_heap_free")) + || !(produce_str_func = + wasm_runtime_lookup_function(module_inst, "produce_str"))) { + printf("Failed to lookup function.\n"); + } + + /* create exec env */ + if (!(exec_env = wasm_runtime_create_exec_env(module_inst, 32768))) { + printf("Failed to create exec env.\n"); + return NULL; + } + + /* allocate memory by calling my_shared_heap_malloc function and send it + to wasm app2 */ + for (i = 0; i < 8; i++) { + argv[0] = 1024 * (i + 1); + argv[1] = i + 1; + if (!produce_data(module_inst, exec_env, queue, + my_shared_heap_malloc_func, argv, 1024 * (i + 1), + true)) { + break; + } + } + + /* use pre-allocated shared heap memory by calling produce_str function and + send it to wasm app2, the pre-allocated shared heap is the last one in + chain, so its end address is calculated from UIN32_MAX */ + uint32 wasm_start_addr = UINT32_MAX - BUF_SIZE + 1; + for (i = 8; i < 16; i++) { + argv[0] = wasm_start_addr + 512 * (i - 8); + argv[1] = i + 1; + if (!produce_data(module_inst, exec_env, queue, produce_str_func, argv, + 512, false)) { + break; + } + } + + wasm_runtime_destroy_exec_env(exec_env); + + return NULL; +} + +static void +wasm_consumer(wasm_module_inst_t module_inst, bh_queue *queue) +{ + wasm_function_inst_t print_buf_func, consume_str_func; + wasm_exec_env_t exec_env; + uint32 argv[2], i; + bh_message_t msg; + char *buf; + + /* lookup wasm function */ + if (!(print_buf_func = + wasm_runtime_lookup_function(module_inst, "print_buf")) + || !(consume_str_func = + wasm_runtime_lookup_function(module_inst, "consume_str"))) { + printf("Failed to lookup function.\n"); + return; + } + + /* create exec env */ + if (!(exec_env = wasm_runtime_create_exec_env(module_inst, 32768))) { + printf("Failed to create exec env.\n"); + return; + } + + for (i = 0; i < 16; i++) { + msg = bh_get_msg(queue, BHT_WAIT_FOREVER); + if (!msg) + return; + buf = bh_message_payload(msg); + + /* call wasm function */ + argv[0] = (uint32)(uintptr_t)buf; + if (i < 8) + wasm_runtime_call_wasm(exec_env, print_buf_func, 1, argv); + else + wasm_runtime_call_wasm(exec_env, consume_str_func, 1, argv); + + if (wasm_runtime_get_exception(module_inst)) { + printf( + "Failed to call 'print_buf' or 'consumer_str' function: %s\n", + wasm_runtime_get_exception(module_inst)); + } + + bh_free_msg(msg); + } + + wasm_runtime_destroy_exec_env(exec_env); +} + +static char global_heap_buf[512 * 1024]; + +int +main(int argc, char **argv) +{ + char *wasm_file1 = NULL, *wasm_file2 = NULL; + uint8 *wasm_file1_buf = NULL, *wasm_file2_buf = NULL; + uint32 wasm_file1_size, wasm_file2_size; + wasm_module_t wasm_module1 = NULL, wasm_module2 = NULL; + wasm_module_inst_t module_inst1 = NULL; + wasm_module_inst_t module_inst2 = NULL; + wasm_shared_heap_t shared_heap = NULL, shared_heap2 = NULL, + shared_heap_chain = NULL; + bh_queue *queue = NULL; + RuntimeInitArgs init_args; + SharedHeapInitArgs heap_init_args; + char error_buf[128] = { 0 }; + bool aot_mode = false; + int ret = -1; + + if (argc > 1 && !strcmp(argv[1], "--aot")) + aot_mode = true; + + if (!aot_mode) + printf("Test shared heap in interpreter mode\n\n"); + else + printf("Test shared heap in AOT mode\n\n"); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + /* init wasm runtime */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + /* create queue */ + if (!(queue = bh_queue_create())) { + printf("Create queue failed.\n"); + goto fail; + } + + /* read wasm file */ + if (!aot_mode) + wasm_file1 = "./wasm-apps/test1.wasm"; + else + wasm_file1 = "./wasm-apps/test1_chain.aot"; + if (!(wasm_file1_buf = + bh_read_file_to_buffer(wasm_file1, &wasm_file1_size))) { + printf("Open wasm file %s failed.\n", wasm_file1); + goto fail; + } + + /* load wasm file */ + wasm_module1 = wasm_runtime_load((uint8 *)wasm_file1_buf, wasm_file1_size, + error_buf, sizeof(error_buf)); + if (!wasm_module1) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* instantiate module */ + module_inst1 = wasm_runtime_instantiate(wasm_module1, 65536, 0, error_buf, + sizeof(error_buf)); + if (!module_inst1) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* read wasm file */ + if (!aot_mode) + wasm_file2 = "./wasm-apps/test2.wasm"; + else + wasm_file2 = "./wasm-apps/test2_chain.aot"; + if (!(wasm_file2_buf = + bh_read_file_to_buffer(wasm_file2, &wasm_file2_size))) { + printf("Open wasm file %s failed.\n", wasm_file1); + goto fail; + } + + /* load wasm file */ + wasm_module2 = wasm_runtime_load((uint8 *)wasm_file2_buf, wasm_file2_size, + error_buf, sizeof(error_buf)); + if (!wasm_module2) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* instantiate module */ + module_inst2 = wasm_runtime_instantiate(wasm_module2, 65536, 0, error_buf, + sizeof(error_buf)); + if (!module_inst2) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* create shared heap */ + memset(&heap_init_args, 0, sizeof(heap_init_args)); + heap_init_args.size = 65536; + shared_heap = wasm_runtime_create_shared_heap(&heap_init_args); + if (!shared_heap) { + printf("Create shared heap failed.\n"); + goto fail; + } + + /* create a preallocated shared heap */ + memset(&heap_init_args, 0, sizeof(heap_init_args)); + heap_init_args.pre_allocated_addr = preallocated_buf; + heap_init_args.size = BUF_SIZE; + shared_heap2 = wasm_runtime_create_shared_heap(&heap_init_args); + if (!shared_heap2) { + printf("Create preallocated shared heap failed\n"); + goto fail; + } + + shared_heap_chain = + wasm_runtime_chain_shared_heaps(shared_heap, shared_heap2); + if (!shared_heap_chain) { + printf("Create shared heap chain failed\n"); + goto fail; + } + + /* attach module instance 1 to the shared heap */ + if (!wasm_runtime_attach_shared_heap(module_inst1, shared_heap_chain)) { + printf("Attach shared heap failed.\n"); + goto fail; + } + + /* attach module instance 2 to the shared heap */ + if (!wasm_runtime_attach_shared_heap(module_inst2, shared_heap_chain)) { + printf("Attach shared heap failed.\n"); + goto fail; + } + + /* wasm 1 produce shared data */ + wasm_producer(module_inst1, queue); + + /* wasm 2 consume shared data */ + wasm_consumer(module_inst2, queue); + ret = 0; + +fail: + if (module_inst2) + wasm_runtime_deinstantiate(module_inst2); + + if (module_inst1) + wasm_runtime_deinstantiate(module_inst1); + + if (wasm_module2) + wasm_runtime_unload(wasm_module2); + + if (wasm_module1) + wasm_runtime_unload(wasm_module1); + + if (wasm_file2_buf) + wasm_runtime_free(wasm_file2_buf); + + if (wasm_file1_buf) + wasm_runtime_free(wasm_file1_buf); + + if (queue) + bh_queue_destroy(queue); + + wasm_runtime_destroy(); + + return ret; +} diff --git a/samples/shared-heap/wasm-apps/CMakeLists.txt b/samples/shared-heap/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000000..7bfa8cd48f --- /dev/null +++ b/samples/shared-heap/wasm-apps/CMakeLists.txt @@ -0,0 +1,41 @@ +# 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-apps) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () + +set (CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib -Qunused-arguments -z stack-size=32768") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (DEFINED_SYMBOLS "${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set (CMAKE_EXE_LINKER_FLAGS + "-O0 -Wl,--initial-memory=65536, \ + -Wl,--no-entry,--strip-all, \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + -Wl,--export-all \ + -Wl,--allow-undefined" +) + +add_executable(test1.wasm test1.c) +target_link_libraries(test1.wasm) + +add_executable(test2.wasm test2.c) +target_link_libraries(test2.wasm) diff --git a/samples/shared-heap/wasm-apps/test1.c b/samples/shared-heap/wasm-apps/test1.c new file mode 100644 index 0000000000..321f102185 --- /dev/null +++ b/samples/shared-heap/wasm-apps/test1.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include + +extern void * +shared_heap_malloc(uint32_t size); +extern void +shared_heap_free(void *ptr); + +void * +my_shared_heap_malloc(uint32_t size, uint32_t index) +{ + char *buf1 = NULL, *buf2 = NULL, *buf; + + buf1 = shared_heap_malloc(128); + if (!buf1) + return NULL; + + buf1[0] = 'H'; + buf1[1] = 'e'; + buf1[2] = 'l'; + buf1[3] = 'l'; + buf1[4] = 'o'; + buf1[5] = ','; + buf1[6] = ' '; + + buf2 = shared_heap_malloc(128); + if (!buf2) { + shared_heap_free(buf1); + return NULL; + } + + snprintf(buf2, 128, "this is buf %u allocated from shared heap", index); + + buf = shared_heap_malloc(size); + if (!buf) { + shared_heap_free(buf1); + shared_heap_free(buf2); + return NULL; + } + + memset(buf, 0, size); + memcpy(buf, buf1, strlen(buf1)); + memcpy(buf + strlen(buf1), buf2, strlen(buf2)); + + shared_heap_free(buf1); + shared_heap_free(buf2); + return buf; +} + +void +my_shared_heap_free(void *ptr) +{ + shared_heap_free(ptr); +} + +void * +produce_str(char *addr, uint32_t index) +{ + char c; + snprintf(addr, 512, "Data: %u stores to pre-allocated shared heap", index); + /* Actually access it in wasm */ + c = addr[0]; + printf("In WASM: the first char is %c\n", c); + return addr; +} diff --git a/samples/shared-heap/wasm-apps/test2.c b/samples/shared-heap/wasm-apps/test2.c new file mode 100644 index 0000000000..44d573164e --- /dev/null +++ b/samples/shared-heap/wasm-apps/test2.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +extern void +shared_heap_free(void *ptr); + +void +print_buf(char *buf) +{ + printf("wasm app2's wasm func received buf: %s\n\n", buf); + shared_heap_free(buf); +} + +void +consume_str(char *buf) +{ + /* Actually access it in wasm */ + char c = buf[0]; + printf("In WASM: wasm app2's wasm func received buf in pre-allocated " + "shared buf: " + "%s with its first char is %c\n\n", + buf, c); +} diff --git a/samples/shared-module/.gitignore b/samples/shared-module/.gitignore new file mode 100644 index 0000000000..0fa8a76bda --- /dev/null +++ b/samples/shared-module/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/shared-module/CMakeLists.txt b/samples/shared-module/CMakeLists.txt new file mode 100644 index 0000000000..552aa93156 --- /dev/null +++ b/samples/shared-module/CMakeLists.txt @@ -0,0 +1,89 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +project (shared-module) + +set (CMAKE_CXX_STANDARD 17) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 Debug) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) + +# fast interpreter +# set (WAMR_BUILD_FAST_INTERP 1) + +# fast-jit +# set (WAMR_BUILD_FAST_JIT 1) + +# llvm jit +# set (WAMR_BUILD_JIT 1) +# set (LLVM_DIR /usr/local/opt/llvm@14/lib/cmake/llvm) + +set (WAMR_BUILD_REF_TYPES 1) + +if (NOT MSVC) + # linker 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 () +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (shared-module src/main.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (shared-module PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (shared-module vmlib -lm -ldl -lpthread ${LLVM_AVAILABLE_LIBS}) +else () + target_link_libraries (shared-module vmlib -lm -ldl -lpthread -lrt ${LLVM_AVAILABLE_LIBS}) +endif () diff --git a/samples/shared-module/README.md b/samples/shared-module/README.md new file mode 100644 index 0000000000..14baa8cbf7 --- /dev/null +++ b/samples/shared-module/README.md @@ -0,0 +1,5 @@ +The "shared-module" sample project +================================== + +This sample demonstrates a bug described in: +https://github.com/bytecodealliance/wasm-micro-runtime/issues/2735. diff --git a/samples/shared-module/build.sh b/samples/shared-module/build.sh new file mode 100755 index 0000000000..e6c1047453 --- /dev/null +++ b/samples/shared-module/build.sh @@ -0,0 +1,63 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "##################### build shared-module project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. -DCMAKE_BUILD_TYPE=Debug +make -j ${nproc} +if [ $? != 0 ];then + echo "BUILD_FAIL shared-module exit as $?\n" + exit 2 +fi + +cp -a shared-module ${OUT_DIR} + +printf "\n" + +echo "##################### build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.wat` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# Note: the CI installs wabt in /opt/wabt +if type wat2wasm; then + WAT2WASM=${WAT2WASM:-wat2wasm} +elif [ -x /opt/wabt/bin/wat2wasm ]; then + WAT2WASM=${WAT2WASM:-/opt/wabt/bin/wat2wasm} +fi + +${WAT2WASM} -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + +# aot +# wamrc -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE} +# mv ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE} + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "##################### build wasm apps done" diff --git a/samples/shared-module/run.sh b/samples/shared-module/run.sh new file mode 100755 index 0000000000..3cb2e623e7 --- /dev/null +++ b/samples/shared-module/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/shared-module -f out/wasm-apps/testapp.wasm diff --git a/samples/shared-module/src/main.c b/samples/shared-module/src/main.c new file mode 100644 index 0000000000..7efbc4d0b9 --- /dev/null +++ b/samples/shared-module/src/main.c @@ -0,0 +1,206 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +int +main(int argc, char *argv_main[]) +{ + int exit_code = 1; + static char global_heap_buf[512 * 1024]; + char *buffer; + char error_buf[128]; + int opt; + char *wasm_path = NULL; + + const unsigned int N = 4; + wasm_module_t module = NULL; + wasm_module_inst_t module_inst[N]; + wasm_exec_env_t exec_env[N]; + const char *name_test_data_drop = "test_data_drop"; + const char *name_test_elem_drop = "test_elem_drop"; + wasm_function_inst_t func_test_data_drop[N]; + wasm_function_inst_t func_test_elem_drop[N]; + unsigned int i; + unsigned int iter; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + + for (i = 0; i < N; i++) { + module_inst[i] = NULL; + exec_env[i] = NULL; + func_test_data_drop[i] = NULL; + func_test_elem_drop[i] = NULL; + } + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + memset(&init_args, 0, sizeof(init_args)); + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + for (i = 0; i < N; i++) { + module_inst[i] = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst[i]) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + exec_env[i] = wasm_runtime_create_exec_env(module_inst[i], stack_size); + if (!exec_env[i]) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + func_test_data_drop[i] = + wasm_runtime_lookup_function(module_inst[i], name_test_data_drop); + if (!func_test_data_drop[i]) { + printf("The wasm function %s is not found.\n", name_test_data_drop); + goto fail; + } + + func_test_elem_drop[i] = + wasm_runtime_lookup_function(module_inst[i], name_test_elem_drop); + if (!func_test_elem_drop[i]) { + printf("The wasm function %s is not found.\n", name_test_elem_drop); + goto fail; + } + } + + for (iter = 0; iter < 2; iter++) { + /* + * as we drop data/table in the first iteration, + * the later iterations should trap. + */ + const bool should_trap = iter > 0; + + for (i = 0; i < N; i++) { + uint32_t argv[1] = {}; + if (wasm_runtime_call_wasm(exec_env[i], func_test_data_drop[i], 0, + argv)) { + uint32_t result = argv[0]; + printf( + "Native finished calling wasm function: %s, return: %x\n", + name_test_data_drop, result); + if (result != 0x64636261) { /* "abcd" */ + printf("unexpected return value\n"); + goto fail; + } + if (should_trap) { + printf("a trap is expected\n"); + goto fail; + } + } + else if (should_trap) { + printf("call wasm function %s failed as expected. error: %s\n", + name_test_data_drop, + wasm_runtime_get_exception(module_inst[i])); + } + else { + printf("call wasm function %s failed. error: %s\n", + name_test_data_drop, + wasm_runtime_get_exception(module_inst[i])); + goto fail; + } + } + + for (i = 0; i < N; i++) { + wasm_runtime_clear_exception(module_inst[i]); + + uint32_t argv[1] = {}; + if (wasm_runtime_call_wasm(exec_env[i], func_test_elem_drop[i], 0, + argv)) { + uint32_t result = argv[0]; + printf( + "Native finished calling wasm function: %s, return: %x\n", + name_test_elem_drop, result); + if (result != 0) { + printf("unexpected return value\n"); + goto fail; + } + if (should_trap) { + printf("a trap is expected\n"); + goto fail; + } + } + else if (should_trap) { + printf("call wasm function %s failed as expected. error: %s\n", + name_test_elem_drop, + wasm_runtime_get_exception(module_inst[i])); + } + else { + printf("call wasm function %s failed. error: %s\n", + name_test_elem_drop, + wasm_runtime_get_exception(module_inst[i])); + goto fail; + } + } + } + + exit_code = 0; +fail: + for (i = 0; i < N; i++) { + if (exec_env[i]) + wasm_runtime_destroy_exec_env(exec_env[i]); + if (module_inst[i]) + wasm_runtime_deinstantiate(module_inst[i]); + } + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + wasm_runtime_destroy(); + return exit_code; +} diff --git a/samples/shared-module/wasm-apps/testapp.wat b/samples/shared-module/wasm-apps/testapp.wat new file mode 100644 index 0000000000..263a870368 --- /dev/null +++ b/samples/shared-module/wasm-apps/testapp.wat @@ -0,0 +1,22 @@ +;; Copyright (C) 2023 Midokura Japan KK. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (func (export "test_data_drop") (result i32) + (memory.init 0 (i32.const 0) (i32.const 0) (i32.const 4)) + data.drop 0 + (i32.load (i32.const 0)) + ) + (func (export "test_elem_drop") (result i32) + (table.init 0 (i32.const 0) (i32.const 0) (i32.const 4)) + elem.drop 0 + i32.const 3 + table.get 0 + ref.is_null + ) + (func $f) + (memory 1 1) + (table 4 4 funcref) + (data "abcd") + (elem func $f $f $f $f) +) diff --git a/samples/simple/CMakeLists.txt b/samples/simple/CMakeLists.txt deleted file mode 100644 index bb3224745d..0000000000 --- a/samples/simple/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2019 Intel Corporation. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -cmake_minimum_required (VERSION 2.8) - -project (simple) - -################ wamr runtime settings ################ - -# Reset default linker flags -set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") -set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") - -set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) - -## use library and headers in the SDK -link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/simple/runtime-sdk/lib) -include_directories( - ${WAMR_ROOT_DIR}/wamr-sdk/out/simple/runtime-sdk/include -) - -################ application related ################ - -include_directories(${CMAKE_CURRENT_LIST_DIR}/src) - -#Note: uncomment below line to use UART mode -#add_definitions (-DCONNECTION_UART) - -add_executable (simple src/main.c src/iwasm_main.c src/ext_lib_export.c) -target_link_libraries (simple vmlib -lm -ldl -lpthread) - - diff --git a/samples/simple/README.md b/samples/simple/README.md deleted file mode 100644 index 4e00ec5f2f..0000000000 --- a/samples/simple/README.md +++ /dev/null @@ -1,311 +0,0 @@ - - -"simple" sample introduction -============== - -This sample demonstrates following scenarios: - -- Use tool "host_tool" to remotely install/uninstall wasm applications from the WAMR runtime over either TCP socket or UART cable -- Inter-app communication programming models -- Communication between WASM applications and the remote app host_tool -- A number of WASM applications built on top of WAMR application framework API sets - - - -Directory structure ------------------------------- -``` -simple/ -├── build.sh -├── CMakeLists.txt -├── README.md -├── src -│   ├── ext_lib_export.c -│   ├── iwasm_main.c -│   └── main.c -└── wasm-apps - ├── connection.c - ├── event_publisher.c - ├── event_subscriber.c - ├── request_handler.c - ├── request_sender.c - ├── sensor.c - └── timer.c -``` - -- src/ext_lib_export.c
- This file is used to export native APIs. See the `The mechanism of exporting Native API to WASM application` section in WAMR README.md for detail. -- src/iwam_main.c
- This file is the implementation by platform integrator. It implements the interfaces that enable the application manager communicating with the host side. See `{WAMR_ROOT}/core/app-mgr/app-mgr-shared/app_manager_export.h` for the definition of the host interface. -## Set physical communication between device and remote - - - -``` -/* Interfaces of host communication */ -typedef struct host_interface { - host_init_func init; - host_send_fun send; - host_destroy_fun destroy; -} host_interface; - -``` -The `host_init_func` is called when the application manager starts up. And `host_send_fun` is called by the application manager to send data to the host. - -Define a global variable "interface" of the data structure: - -``` - -host_interface interface = { - .init = host_init, - .send = host_send, - .destroy = host_destroy -}; -``` -This interface is passed to application manager during the runtime startup: -``` -app_manager_startup(&interface); -``` - -> - -**Note:** The connection between simple and host_tool is TCP by default. The simple application works as a server and the host_tool works as a client. You can also use UART connection. To achieve this you have to uncomment the below line in CMakeLists.txt and rebuild. - -``` -#add_definitions (-DCONNECTION_UART)` -``` - -To run the UART based test, you have to set up a UART hardware connection between host_tool and the simple application. See the help of host_tool for how to specify UART device parameters. - - -Build the sample -============== -Execute the build.sh script then all binaries including wasm application files would be generated in 'out' directory. -`./build.sh` - - - -**Out directory structure** - -``` -out/ -├── host_tool -├── simple -└── wasm-apps - ├── connection.wasm - ├── event_publisher.wasm - ├── event_subscriber.wasm - ├── request_handler.wasm - ├── request_sender.wasm - ├── sensor.wasm - └── timer.wasm -``` - -- host_tool: - A small testing tool to interact with WAMR. See the usage of this tool by executing "./host_tool -h". - `./host_tool -h` - -- simple: - A simple testing tool running on the host side that interact with WAMR. It is used to install, uninstall and query WASM applications in WAMR, and send request or subscribe event, etc. See the usage of this application by executing "./simple -h". - `./simple -h` -> - -Run the sample -========================== -- Enter the out directory -``` -$ cd ./out/ -``` - -- Startup the 'simple' process works in TCP server mode and you would see "App Manager started." is printed. -``` -$ ./simple -s -App Manager started. -``` - -- Query all installed applications -``` -$ ./host_tool -q - -response status 69 -{ - "num": 0 -} -``` - -The `69` stands for response code SUCCESS. The payload is printed with JSON format where the `num` stands for application installations number and value `0` means currently no application is installed yet. - -- Install the request handler wasm application
-``` -$ ./host_tool -i request_handler -f ./wasm-apps/request_handler.wasm - -response status 65 -``` -Now the request handler application is running and waiting for host or other wasm application to send a request. - -- Query again -``` -$ ./host_tool -q - -response status 69 -{ - "num": 1, - "applet1": "request_handler", - "heap1": 49152 -} -``` -In the payload, we can see `num` is 1 which means 1 application is installed. `applet1`stands for the name of the 1st application. `heap1` stands for the heap size of the 1st application. - -- Send request from host to specific wasm application -``` -$ ./host_tool -r /app/request_handler/url1 -A GET - -response status 69 -{ - "key1": "value1", - "key2": "value2" -} -``` - -We can see a response with status `69` and a payload is received. - -Output of simple application: -``` -connection established! -Send request to applet: request_handler -Send request to app request_handler success. -App request_handler got request, url url1, action 1 -[resp] ### user resource 1 handler called -sent 150 bytes to host -Wasm app process request success. -``` - -- Send a general request from host (not specify target application name)
-``` -$ ./host_tool -r /url1 -A GET - -response status 69 -{ - "key1": "value1", - "key2": "value2" -} -``` - -Output of simple application: -``` -connection established! -Send request to app request_handler success. -App request_handler got request, url /url1, action 1 -[resp] ### user resource 1 handler called -sent 150 bytes to host -Wasm app process request success. -``` - -- Install the event publisher wasm application -``` -$ ./host_tool -i pub -f ./wasm-apps/event_publisher.wasm - -response status 65 -``` - -- Subscribe event by host_tool
-``` -$ ./host_tool -s /alert/overheat -a 3000 - -response status 69 - -received an event alert/overheat -{ - "warning": "temperature is over high" -} -received an event alert/overheat -{ - "warning": "temperature is over high" -} -received an event alert/overheat -{ - "warning": "temperature is over high" -} -received an event alert/overheat -{ - "warning": "temperature is over high" -} -``` -We can see 4 `alert/overheat` events are received in 3 seconds which is published by the `pub` application. - -Output of simple -``` -connection established! -am_register_event adding url:(alert/overheat) -client: -3 registered event (alert/overheat) -sent 16 bytes to host -sent 142 bytes to host -sent 142 bytes to host -sent 142 bytes to host -sent 142 bytes to host -``` -- Install the event subscriber wasm application
-``` -$ ./host_tool -i sub -f ./wasm-apps/event_subscriber.wasm - -response status 65 -``` -The `sub` application is installed. - -Output of simple -``` -connection established! -Install WASM app success! -WASM app 'sub' started -am_register_event adding url:(alert/overheat) -client: 3 registered event (alert/overheat) -sent 16 bytes to host -Send request to app sub success. -App sub got request, url alert/overheat, action 6 -### user over heat event handler called -Attribute container dump: -Tag: -Attribute list: - key: warning, type: string, value: temperature is over high - -Wasm app process request success. -``` - -We can see the `sub` application receives the `alert/overheat` event and dumps it out.
-At device side, the event is represented by an attribute container which contains key-value pairs like below: -``` -Attribute container dump: -Tag: -Attribute list: - key: warning, type: string, value: temperature is over high -``` -`warning` is the key's name. `string` means this is a string value and `temperature is over high` is the value. - -- Uninstall the wasm application
-``` -$ ./host_tool -u request_handler - -response status 66 - -$ ./host_tool -u pub - -response status 66 - -$ ./host_tool -u sub - -response status 66 -``` - -- Query again
-``` -$ ./host_tool -q - -response status 69 -{ - "num": 0 -} -``` - - >**Note:** Here we only installed part of the sample WASM applications. You can try others by yourself. - - >**Note:** You have to manually kill the simple process by Ctrl+C after use. diff --git a/samples/simple/build.sh b/samples/simple/build.sh deleted file mode 100755 index 53f679a18a..0000000000 --- a/samples/simple/build.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash - -CURR_DIR=$PWD -WAMR_DIR=${PWD}/../.. -OUT_DIR=${PWD}/out -BUILD_DIR=${PWD}/build - -IWASM_ROOT=${PWD}/../../core/iwasm -APP_FRAMEWORK_DIR=${PWD}/../../core/app-framework -NATIVE_LIBS=${APP_FRAMEWORK_DIR}/app-native-shared -APP_LIB_SRC="${APP_FRAMEWORK_DIR}/base/app/*.c ${APP_FRAMEWORK_DIR}/sensor/app/*.c \ - ${APP_FRAMEWORK_DIR}/connection/app/*.c ${NATIVE_LIBS}/*.c" -WASM_APPS=${PWD}/wasm-apps -CLEAN= - -usage () -{ - echo "build.sh [options]" - echo " -p [platform]" - echo " -t [target]" - echo " -c, rebuild SDK" - exit 1 -} - - -while getopts "p:t:ch" opt -do - case $opt in - p) - PLATFORM=$OPTARG - ;; - t) - TARGET=$OPTARG - ;; - c) - CLEAN="TRUE" - ;; - h) - usage - exit 1; - ;; - ?) - echo "Unknown arg: $arg" - usage - exit 1 - ;; - esac -done - - -rm -rf ${OUT_DIR} -mkdir ${OUT_DIR} -mkdir ${OUT_DIR}/wasm-apps - -cd ${WAMR_DIR}/core/shared/mem-alloc -if [ ! -d "tlsf" ]; then - git clone https://github.com/mattconte/tlsf -fi - -echo "#####################build wamr sdk" -cd ${WAMR_DIR}/wamr-sdk -./build_sdk.sh -n simple -x ${CURR_DIR}/wamr_config_simple.cmake $* - -echo "#####################build simple project" -cd ${CURR_DIR} -mkdir -p cmake_build -cd cmake_build -cmake .. -DWAMR_BUILD_SDK_PROFILE=simple -make -if [ $? != 0 ];then - echo "BUILD_FAIL simple exit as $?\n" - exit 2 -fi -cp -a simple ${OUT_DIR} -echo "#####################build simple project success" - -echo -e "\n\n" -echo "#####################build host-tool" -cd ${WAMR_DIR}/test-tools/host-tool -mkdir -p bin -cd bin -cmake .. -make -if [ $? != 0 ];then - echo "BUILD_FAIL host tool exit as $?\n" - exit 2 -fi -cp host_tool ${OUT_DIR} -echo "#####################build host-tool success" - -echo -e "\n\n" -echo "#####################build wasm apps" - -cd ${WASM_APPS} - -for i in `ls *.c` -do -APP_SRC="$i" -OUT_FILE=${i%.*}.wasm - -/opt/wasi-sdk/bin/clang \ - -I${WAMR_DIR}/wamr-sdk/out/simple/app-sdk/wamr-app-framework/include \ - -L${WAMR_DIR}/wamr-sdk/out/simple/app-sdk/wamr-app-framework/lib \ - -lapp_framework \ - --target=wasm32 -O3 -z stack-size=4096 -Wl,--initial-memory=65536 \ - --sysroot=${WAMR_DIR}/wamr-sdk/out/simple/app-sdk/libc-builtin-sysroot \ - -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/out/simple/app-sdk/libc-builtin-sysroot/share/defined-symbols.txt \ - -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ - -Wl,--export=on_init -Wl,--export=on_destroy \ - -Wl,--export=on_request -Wl,--export=on_response \ - -Wl,--export=on_sensor_event -Wl,--export=on_timer_callback \ - -Wl,--export=on_connection_data \ - -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} -if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then - echo "build ${OUT_FILE} success" -else - echo "build ${OUT_FILE} fail" -fi -done - -echo "#####################build wasm apps done" diff --git a/samples/simple/src/ext_lib_export.c b/samples/simple/src/ext_lib_export.c deleted file mode 100644 index 4a5750b267..0000000000 --- a/samples/simple/src/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -#include "lib_export.h" -#include "sensor_api.h" -#include "connection_api.h" - -static NativeSymbol extended_native_symbol_defs[] = { -#include "runtime_sensor.inl" -#include "connection.inl" -}; - -#include "ext_lib_export.h" diff --git a/samples/simple/src/iwasm_main.c b/samples/simple/src/iwasm_main.c deleted file mode 100644 index ee808e4ccd..0000000000 --- a/samples/simple/src/iwasm_main.c +++ /dev/null @@ -1,491 +0,0 @@ - -#ifndef CONNECTION_UART -#include -#include -#include -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "runtime_lib.h" -#include "runtime_timer.h" -#include "native_interface.h" -#include "app_manager_export.h" -#include "bh_common.h" -#include "bh_queue.h" -#include "bh_thread.h" -#include "bh_memory.h" -#include "runtime_sensor.h" -#include "bi-inc/attr_container.h" -#include "module_wasm_app.h" -#include "wasm_export.h" - -#define MAX 2048 - -#ifndef CONNECTION_UART -#define SA struct sockaddr -static char *host_address = "127.0.0.1"; -static int port = 8888; -#else -static char *uart_device = "/dev/ttyS2"; -static int baudrate = B115200; -#endif - -extern void init_sensor_framework(); -extern void exit_sensor_framework(); -extern void exit_connection_framework(); -extern int aee_host_msg_callback(void *msg, uint16_t msg_len); -extern bool init_connection_framework(); - -#ifndef CONNECTION_UART -int listenfd = -1; -int sockfd = -1; -static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; -#else -int uartfd = -1; -#endif - -#ifndef CONNECTION_UART -static bool server_mode = false; - -// Function designed for chat between client and server. -void* func(void* arg) -{ - char buff[MAX]; - int n; - struct sockaddr_in servaddr; - - while (1) { - if (sockfd != -1) - close(sockfd); - // socket create and verification - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd == -1) { - printf("socket creation failed...\n"); - return NULL; - } else - printf("Socket successfully created..\n"); - bzero(&servaddr, sizeof(servaddr)); - // assign IP, PORT - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(host_address); - servaddr.sin_port = htons(port); - - // connect the client socket to server socket - if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) != 0) { - printf("connection with the server failed...\n"); - sleep(10); - continue; - } else { - printf("connected to the server..\n"); - } - - // infinite loop for chat - for (;;) { - bzero(buff, MAX); - - // read the message from client and copy it in buffer - n = read(sockfd, buff, sizeof(buff)); - // print buffer which contains the client contents - //fprintf(stderr, "recieved %d bytes from host: %s", n, buff); - - // socket disconnected - if (n <= 0) - break; - - aee_host_msg_callback(buff, n); - } - } - - // After chatting close the socket - close(sockfd); -} - -static bool host_init() -{ - return true; -} - -int host_send(void * ctx, const char *buf, int size) -{ - int ret; - - if (pthread_mutex_trylock(&sock_lock) == 0) { - if (sockfd == -1) { - pthread_mutex_unlock(&sock_lock); - return 0; - } - - ret = write(sockfd, buf, size); - - pthread_mutex_unlock(&sock_lock); - return ret; - } - - return -1; -} - -void host_destroy() -{ - if (server_mode) - close(listenfd); - - pthread_mutex_lock(&sock_lock); - close(sockfd); - pthread_mutex_unlock(&sock_lock); -} - -host_interface interface = { - .init = host_init, - .send = host_send, - .destroy = host_destroy -}; - -/* Change it to 1 when fuzzing test */ -#define WASM_ENABLE_FUZZ_TEST 0 - -void* func_server_mode(void* arg) -{ - int clilent; - struct sockaddr_in serv_addr, cli_addr; - int n; - char buff[MAX]; - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &sa, 0); - - /* First call to socket() function */ - listenfd = socket(AF_INET, SOCK_STREAM, 0); - - if (listenfd < 0) { - perror("ERROR opening socket"); - exit(1); - } - - /* Initialize socket structure */ - bzero((char *) &serv_addr, sizeof(serv_addr)); - - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(port); - - /* Now bind the host address using bind() call.*/ - if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { - perror("ERROR on binding"); - exit(1); - } - - listen(listenfd, 5); - clilent = sizeof(cli_addr); - - while (1) { - pthread_mutex_lock(&sock_lock); - - sockfd = accept(listenfd, (struct sockaddr *) &cli_addr, &clilent); - - pthread_mutex_unlock(&sock_lock); - - if (sockfd < 0) { - perror("ERROR on accept"); - exit(1); - } - - printf("connection established!\n"); - - for (;;) { - bzero(buff, MAX); - - // read the message from client and copy it in buffer - n = read(sockfd, buff, sizeof(buff)); - - // socket disconnected - if (n <= 0) { - pthread_mutex_lock(&sock_lock); - close(sockfd); - sockfd = -1; - pthread_mutex_unlock(&sock_lock); - - sleep(1); - break; - } - - aee_host_msg_callback(buff, n); - } -#if WASM_ENABLE_FUZZ_TEST != 0 - /* Exit the process when host disconnect. - * This is helpful for reproducing failure case. */ - close(sockfd); - exit(1); -#endif - } -} - -#else -static int parse_baudrate(int baud) -{ - switch (baud) { - case 9600: - return B9600; - case 19200: - return B19200; - case 38400: - return B38400; - case 57600: - return B57600; - case 115200: - return B115200; - case 230400: - return B230400; - case 460800: - return B460800; - case 500000: - return B500000; - case 576000: - return B576000; - case 921600: - return B921600; - case 1000000: - return B1000000; - case 1152000: - return B1152000; - case 1500000: - return B1500000; - case 2000000: - return B2000000; - case 2500000: - return B2500000; - case 3000000: - return B3000000; - case 3500000: - return B3500000; - case 4000000: - return B4000000; - default: - return -1; - } -} -static bool uart_init(const char *device, int baudrate, int *fd) -{ - int uart_fd; - struct termios uart_term; - - uart_fd = open(device, O_RDWR | O_NOCTTY); - - if (uart_fd <= 0) - return false; - - memset(&uart_term, 0, sizeof(uart_term)); - uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; - uart_term.c_iflag = IGNPAR; - uart_term.c_oflag = 0; - - /* set noncanonical mode */ - uart_term.c_lflag = 0; - uart_term.c_cc[VTIME] = 30; - uart_term.c_cc[VMIN] = 1; - tcflush(uart_fd, TCIFLUSH); - - if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { - close(uart_fd); - return false; - } - - *fd = uart_fd; - - return true; -} - -static void *func_uart_mode(void *arg) -{ - int n; - char buff[MAX]; - - if (!uart_init(uart_device, baudrate, &uartfd)) { - printf("open uart fail! %s\n", uart_device); - return NULL; - } - - for (;;) { - bzero(buff, MAX); - - n = read(uartfd, buff, sizeof(buff)); - - if (n <= 0) { - close(uartfd); - uartfd = -1; - break; - } - - aee_host_msg_callback(buff, n); - } - - return NULL; -} - -static int uart_send(void * ctx, const char *buf, int size) -{ - int ret; - - ret = write(uartfd, buf, size); - - return ret; -} - -static void uart_destroy() -{ - close(uartfd); -} - -static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; - -#endif - -static char global_heap_buf[1024 * 1024] = { 0 }; - -static void showUsage() -{ -#ifndef CONNECTION_UART - printf("Usage:\n"); - printf("\nWork as TCP server mode:\n"); - printf("\tsimple -s|--server_mode -p|--port \n"); - printf("where\n"); - printf("\t represents the port that would be listened on and the default is 8888\n"); - printf("\nWork as TCP client mode:\n"); - printf("\tsimple -a|--host_address -p|--port \n"); - printf("where\n"); - printf("\t represents the network address of host and the default is 127.0.0.1\n"); - printf("\t represents the listen port of host and the default is 8888\n"); -#else - printf("Usage:\n"); - printf("\tsimple -u -b \n\n"); - printf("where\n"); - printf("\t represents the UART device name and the default is /dev/ttyS2\n"); - printf("\t represents the UART device baudrate and the default is 115200\n"); -#endif -} - -static bool parse_args(int argc, char *argv[]) -{ - int c; - - while (1) { - int optIndex = 0; - static struct option longOpts[] = { -#ifndef CONNECTION_UART - { "server_mode", no_argument, NULL, 's' }, - { "host_address", required_argument, NULL, 'a' }, - { "port", required_argument, NULL, 'p' }, -#else - { "uart", required_argument, NULL, 'u' }, - { "baudrate", required_argument, NULL, 'b' }, -#endif - { "help", required_argument, NULL, 'h' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "sa:p:u:b:w:h", longOpts, &optIndex); - if (c == -1) - break; - - switch (c) { -#ifndef CONNECTION_UART - case 's': - server_mode = true; - break; - case 'a': - host_address = optarg; - printf("host address: %s\n", host_address); - break; - case 'p': - port = atoi(optarg); - printf("port: %d\n", port); - break; -#else - case 'u': - uart_device = optarg; - printf("uart device: %s\n", uart_device); - break; - case 'b': - baudrate = parse_baudrate(atoi(optarg)); - printf("uart baudrate: %s\n", optarg); - break; -#endif - case 'h': - showUsage(); - return false; - default: - showUsage(); - return false; - } - } - - return true; -} - -// Driver function -int iwasm_main(int argc, char *argv[]) -{ - korp_thread tid; - - if (!parse_args(argc, argv)) - return -1; - -#if 1 - if (bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)) -#else - if (bh_memory_init_with_allocator(malloc, free) -#endif - != 0) { - printf("Init global heap failed.\n"); - return -1; - } - - if (vm_thread_sys_init() != 0) { - goto fail1; - } - - if (!init_connection_framework()) { - vm_thread_sys_destroy(); - goto fail1; - } - - init_sensor_framework(); - - // timer manager - init_wasm_timer(); - -#ifndef CONNECTION_UART - if (server_mode) - vm_thread_create(&tid, func_server_mode, NULL, - BH_APPLET_PRESERVED_STACK_SIZE); - else - vm_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); -#else - vm_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); -#endif - - app_manager_startup(&interface); - - exit_wasm_timer(); - exit_sensor_framework(); - exit_connection_framework(); - -fail1: - bh_memory_destroy(); - - return -1; -} diff --git a/samples/simple/src/main.c b/samples/simple/src/main.c deleted file mode 100644 index 5bf24ce67d..0000000000 --- a/samples/simple/src/main.c +++ /dev/null @@ -1,6 +0,0 @@ -extern void iwasm_main(); -int main(int argc, char *argv[]) -{ - iwasm_main(argc, argv); - return 0; -} diff --git a/samples/simple/wamr_config_simple.cmake b/samples/simple/wamr_config_simple.cmake deleted file mode 100644 index a3317f0372..0000000000 --- a/samples/simple/wamr_config_simple.cmake +++ /dev/null @@ -1,9 +0,0 @@ -set (WAMR_BUILD_PLATFORM "linux") -set (WAMR_BUILD_TARGET X86_64) -set (WAMR_BUILD_INTERP 1) -set (WAMR_BUILD_AOT 1) -set (WAMR_BUILD_JIT 0) -set (WAMR_BUILD_LIBC_BUILTIN 1) -set (WAMR_BUILD_LIBC_WASI 0) -set (WAMR_BUILD_APP_FRAMEWORK 1) -set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_CONNECTION WAMR_APP_BUILD_SENSOR) diff --git a/samples/simple/wasm-apps/connection.c b/samples/simple/wasm-apps/connection.c deleted file mode 100644 index ee43a33443..0000000000 --- a/samples/simple/wasm-apps/connection.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/connection.h" -#include "wa-inc/timer_wasm_app.h" -#include "wa-inc/request.h" - -/* User global variable */ -static int num = 0; -static user_timer_t g_timer; -static connection_t *g_conn = NULL; - -void on_data1(connection_t *conn, - conn_event_type_t type, - const char *data, - uint32 len, - void *user_data) -{ - if (type == CONN_EVENT_TYPE_DATA) { - char message[64] = {0}; - memcpy(message, data, len); - printf("Client got a message from server -> %s\n", message); - } else if (type == CONN_EVENT_TYPE_DISCONNECT) { - printf("connection is close by server!\n"); - } else { - printf("error: got unknown event type!!!\n"); - } -} - -/* Timer callback */ -void timer1_update(user_timer_t timer) -{ - char message[64] = {0}; - /* Reply to server */ - snprintf(message, sizeof(message), "Hello %d", num++); - api_send_on_connection(g_conn, message, strlen(message)); -} - -void my_close_handler(request_t * request) -{ - response_t response[1]; - - if (g_conn != NULL) { - api_timer_cancel(g_timer); - api_close_connection(g_conn); - } - - make_response_for_request(request, response); - set_response(response, DELETED_2_02, 0, NULL, 0); - api_response_send(response); -} - -void on_init() -{ - user_timer_t timer; - attr_container_t *args; - char *str = "this is client!"; - - api_register_resource_handler("/close", my_close_handler); - - args = attr_container_create(""); - attr_container_set_string(&args, "address", "127.0.0.1"); - attr_container_set_uint16(&args, "port", 7777); - - g_conn = api_open_connection("TCP", args, on_data1, NULL); - if (g_conn == NULL) { - printf("connect to server fail!\n"); - return; - } - - printf("connect to server success! handle: %p\n", g_conn); - - /* set up a timer */ - timer = api_timer_create(1000, true, false, timer1_update); - api_timer_restart(timer, 1000); -} - -void on_destroy() -{ - /* real destroy work including killing timer and closing sensor is - accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/simple/wasm-apps/event_publisher.c b/samples/simple/wasm-apps/event_publisher.c deleted file mode 100644 index 457f428d31..0000000000 --- a/samples/simple/wasm-apps/event_publisher.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/request.h" -#include "wa-inc/timer_wasm_app.h" - -int num = 0; - -void publish_overheat_event() -{ - attr_container_t *event; - - event = attr_container_create("event"); - attr_container_set_string(&event, "warning", "temperature is over high"); - - api_publish_event("alert/overheat", FMT_ATTR_CONTAINER, event, - attr_container_get_serialize_length(event)); - - attr_container_destroy(event); -} - -/* Timer callback */ -void timer1_update(user_timer_t timer) -{ - publish_overheat_event(); -} - -void start_timer() -{ - user_timer_t timer; - - /* set up a timer */ - timer = api_timer_create(1000, true, false, timer1_update); - api_timer_restart(timer, 1000); -} - -void on_init() -{ - start_timer(); -} - -void on_destroy() -{ - /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/simple/wasm-apps/event_subscriber.c b/samples/simple/wasm-apps/event_subscriber.c deleted file mode 100644 index a061eff89a..0000000000 --- a/samples/simple/wasm-apps/event_subscriber.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/request.h" - -void over_heat_event_handler(request_t *request) -{ - printf("### user over heat event handler called\n"); - - if (request->payload != NULL && request->fmt == FMT_ATTR_CONTAINER) - attr_container_dump((attr_container_t *) request->payload); -} - -void on_init() -{ - api_subscribe_event("alert/overheat", over_heat_event_handler); -} - -void on_destroy() -{ - /* real destroy work including killing timer and closing sensor is - accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/simple/wasm-apps/request_handler.c b/samples/simple/wasm-apps/request_handler.c deleted file mode 100644 index e944daa21b..0000000000 --- a/samples/simple/wasm-apps/request_handler.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/request.h" - -static void url1_request_handler(request_t *request) -{ - response_t response[1]; - attr_container_t *payload; - - printf("[resp] ### user resource 1 handler called\n"); - - if (request->payload != NULL && request->fmt == FMT_ATTR_CONTAINER) - attr_container_dump((attr_container_t *) request->payload); - - payload = attr_container_create("wasm app response payload"); - if (payload == NULL) - return; - - attr_container_set_string(&payload, "key1", "value1"); - attr_container_set_string(&payload, "key2", "value2"); - - make_response_for_request(request, response); - set_response(response, CONTENT_2_05, - FMT_ATTR_CONTAINER, - (void *)payload, - attr_container_get_serialize_length(payload)); - api_response_send(response); - - attr_container_destroy(payload); -} - -static void url2_request_handler(request_t *request) -{ - response_t response[1]; - make_response_for_request(request, response); - set_response(response, DELETED_2_02, 0, NULL, 0); - api_response_send(response); - - printf("### user resource 2 handler called\n"); -} - -void on_init() -{ - /* register resource uri */ - api_register_resource_handler("/url1", url1_request_handler); - api_register_resource_handler("/url2", url2_request_handler); -} - -void on_destroy() -{ - /* real destroy work including killing timer and closing sensor is - accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/simple/wasm-apps/request_sender.c b/samples/simple/wasm-apps/request_sender.c deleted file mode 100644 index 97dba4076f..0000000000 --- a/samples/simple/wasm-apps/request_sender.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/request.h" - -static void my_response_handler(response_t *response, void *user_data) -{ - char *tag = (char *) user_data; - - if (response == NULL) { - printf("[req] request timeout!\n"); - return; - } - - printf("[req] response handler called mid:%d, status:%d, fmt:%d, payload:%p, len:%d, tag:%s\n", - response->mid, response->status, response->fmt, response->payload, - response->payload_len, tag); - - if (response->payload != NULL - && response->payload_len > 0 - && response->fmt == FMT_ATTR_CONTAINER) { - printf("[req] dump the response payload:\n"); - attr_container_dump((attr_container_t *) response->payload); - } -} - -static void test_send_request(char *url, char *tag) -{ - request_t request[1]; - - init_request(request, url, COAP_PUT, 0, NULL, 0); - api_send_request(request, my_response_handler, tag); -} - -void on_init() -{ - test_send_request("/app/request_handler/url1", "a request to target app"); - test_send_request("url1", "a general request"); -} - -void on_destroy() -{ - /* real destroy work including killing timer and closing sensor is - accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/simple/wasm-apps/sensor.c b/samples/simple/wasm-apps/sensor.c deleted file mode 100644 index 717adc5b6f..0000000000 --- a/samples/simple/wasm-apps/sensor.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/sensor.h" - -static sensor_t sensor = NULL; - -/* Sensor event callback*/ -void sensor_event_handler(sensor_t sensor, attr_container_t *event, - void *user_data) -{ - printf("### app get sensor event\n"); - attr_container_dump(event); -} - -void on_init() -{ - char *user_data; - attr_container_t *config; - - printf("### app on_init 1\n"); - /* open a sensor */ - user_data = malloc(100); - printf("### app on_init 2\n"); - sensor = sensor_open("sensor_test", 0, sensor_event_handler, user_data); - printf("### app on_init 3\n"); - - /* config the sensor */ - sensor_config(sensor, 1000, 0, 0); - printf("### app on_init 4\n"); - - /* - config = attr_container_create("sensor config"); - sensor_config(sensor, config); - attr_container_destroy(config); - */ -} - -void on_destroy() -{ - if (NULL != sensor) { - sensor_config(sensor, 0, 0, 0); - } - /* real destroy work including killing timer and closing sensor is - accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/simple/wasm-apps/timer.c b/samples/simple/wasm-apps/timer.c deleted file mode 100644 index 51438c8f34..0000000000 --- a/samples/simple/wasm-apps/timer.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "wasm_app.h" -#include "wa-inc/timer_wasm_app.h" - -/* User global variable */ -static int num = 0; - -/* Timer callback */ -void timer1_update(user_timer_t timer) -{ - printf("Timer update %d\n", num++); -} - -void on_init() -{ - user_timer_t timer; - - /* set up a timer */ - timer = api_timer_create(1000, true, false, timer1_update); - api_timer_restart(timer, 1000); -} - -void on_destroy() -{ - /* real destroy work including killing timer and closing sensor is - accomplished in wasm app library version of on_destroy() */ -} diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt new file mode 100644 index 0000000000..ca3484668d --- /dev/null +++ b/samples/socket-api/CMakeLists.txt @@ -0,0 +1,192 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(socket_api_sample) + +####################################### +## Detect toolchain +####################################### +message(CHECK_START "Detecting WASI-SDK at /opt/wasi-sdk") +if(NOT (DEFINED WASI_SDK_DIR OR DEFINED CACHE{WASI_SDK_DIR})) + find_path(WASI_SDK_PARENT + wasi-sdk + PATHS /opt + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(WASI_SDK_PARENT) + set(WASI_SDK_DIR ${WASI_SDK_PARENT}/wasi-sdk) + endif() +endif() +if(WASI_SDK_DIR) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +if(NOT EXISTS ${WASI_SDK_DIR}) + message(FATAL_ERROR "Please install WASI-SDK under /opt/wasi-sdk") +endif() + +message(CHECK_START "Detecting WASI_TOOLCHAIN_FILE at ${WASI_SDK_DIR}") +find_file(WASI_TOOLCHAIN_FILE + wasi-sdk-pthread.cmake + PATHS "${WASI_SDK_DIR}/share/cmake" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASI_TOOLCHAIN_FILE) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +if(WASI_TOOLCHAIN_FILE-NOTFOUND) + message(FATAL_ERROR "Can not find wasi-sdk-pthread.cmake under ${WASI_SDK_DIR}") +endif() + +message(CHECK_START "Detecting WASI_SYS_ROOT at ${WASI_SDK_DIR}") +find_path(WASI_SYS_ROOT + wasi-sysroot + PATHS "${WASI_SDK_DIR}/share" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASI_SYS_ROOT) + message(CHECK_PASS "found") + set(WASI_SYS_ROOT ${WASI_SYS_ROOT}/wasi-sysroot) +else() + message(CHECK_FAIL "not found") +endif() + +if(WASI_SYS_ROOT-NOTFOUND) + message(FATAL_ERROR "Can not find wasi-sysroot/ under ${WASI_SDK_DIR}") +endif() + +message(STATUS "WASI_SDK_DIR is ${WASI_SDK_DIR}") +message(STATUS "WASI_TOOLCHAIN_FILE is ${WASI_TOOLCHAIN_FILE}") +message(STATUS "WASI_SYS_ROOT is ${WASI_SYS_ROOT}") + +############################################################### +## Build socket applications of wasm version and native version +############################################################### +include(ExternalProject) + +ExternalProject_Add(wasm-app + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src + UPDATE_COMMAND "" + PATCH_COMMAND "" + CONFIGURE_COMMAND ${CMAKE_COMMAND} + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src + BUILD_COMMAND ${CMAKE_COMMAND} --build . + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy + addr_resolve.wasm ${CMAKE_BINARY_DIR} + tcp_client.wasm ${CMAKE_BINARY_DIR} + tcp_server.wasm ${CMAKE_BINARY_DIR} + send_recv.wasm ${CMAKE_BINARY_DIR} + socket_opts.wasm ${CMAKE_BINARY_DIR} + udp_client.wasm ${CMAKE_BINARY_DIR} + udp_server.wasm ${CMAKE_BINARY_DIR} + multicast_client.wasm ${CMAKE_BINARY_DIR} + multicast_server.wasm ${CMAKE_BINARY_DIR} + timeout_client.wasm ${CMAKE_BINARY_DIR} + timeout_server.wasm ${CMAKE_BINARY_DIR} +) + +add_executable(tcp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_server.c) +target_link_libraries(tcp_server pthread) + +add_executable(tcp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/tcp_client.c) + +add_executable(send_recv ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/send_recv.c) +target_link_libraries(send_recv pthread) + +add_executable(addr_resolve ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/addr_resolve.c) + +add_executable(socket_opts ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/socket_opts.c) + +add_executable(udp_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/udp_client.c) + +add_executable(udp_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/udp_server.c) + +add_executable(multicast_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/multicast_client.c) + +add_executable(multicast_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/multicast_server.c) + +add_executable(timeout_client ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/timeout_client.c) + +add_executable(timeout_server ${CMAKE_CURRENT_SOURCE_DIR}/wasm-src/timeout_server.c) + +############################################ +## Build iwasm with wasi and pthread support +############################################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR features + +# 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_FAST_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_REF_TYPES 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 () + +# build vmlib static lib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +# build iwasm +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/socket-api/README.md b/samples/socket-api/README.md new file mode 100644 index 0000000000..1122967c09 --- /dev/null +++ b/samples/socket-api/README.md @@ -0,0 +1,215 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/socket-api" +--- +# "socket-api" sample introduction + +This sample demonstrates how to use WAMR socket-api to develop wasm network applications. +Two wasm applications are provided: tcp-server and tcp-client, and this sample demonstrates +how they communicate with each other. + +## Preparation + +Please install WASI SDK, download the [wasi-sdk release](https://github.com/WebAssembly/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. +And install wabt, download the [wabt release](https://github.com/WebAssembly/wabt/releases) and extract the archive to default path `/opt/wabt` + +## Build the sample + +```bash +mkdir build +cd build +cmake .. +make +``` + +`iwasm` and the following Wasm modules (along with their corresponding native version) will be generated: + * `addr_resolve.wasm`, `addr_resolve` + * `send_recv.wasm`, `send_recv` + * `socket_opts.wasm`, `socket_opts` + * `tcp_client.wasm`, `tcp_client` + * `tcp_server.wasm`, `tcp_server` + * `udp_client.wasm`, `udp_client` + * `udp_server.wasm`, `udp_server` + +> Note that iwasm is built with libc-wasi and lib-pthread enabled. + +## Run workload + +### TCP client/server + +Start the tcp server, which opens port 1234 and waits for clients to connect. + +```bash +cd build +./iwasm --addr-pool=0.0.0.0/15 tcp_server.wasm +``` + +Start the tcp client, which connects the server and receives message. + +```bash +cd build +./iwasm --addr-pool=127.0.0.1/15 tcp_client.wasm +``` + +The output of client is like: + +```bash +[Client] Create socket +[Client] Connect socket +[Client] Client receive +[Client] 115 bytes received: +Buffer received: +Say Hi from the Server +Say Hi from the Server +Say Hi from the Server +Say Hi from the Server +Say Hi from the Server + +[Client] BYE +``` + +`send_recv.wasm` contains a thread as a server and a thread as a client. They +send and receive data via 127.0.0.1:1234. + +```bash +$ ./iwasm --addr-pool=127.0.0.1/0 ./send_recv.wasm +``` + +The output is: + +```bash +Server is online ... +Client is running... +Start receiving. +Start sending. +Send 106 bytes successfully! +Receive 106 bytes successfully! +Data: + The stars shine down + It brings us light + Light comes down + To make us paths + It watches us + And mourns for us +``` + +### Socket options + +`socket_opts.wasm` shows an example of getting and setting various supported socket options +```bash +$ ./iwasm socket_opts.wasm +``` +The output is: +```bash +[Client] Create TCP socket +[Client] Create UDP socket +[Client] Create UDP IPv6 socket +setsockopt SO_RCVTIMEO result is expected +getsockopt SO_RCVTIMEO result is expected +... +[Client] Close sockets +``` + +The `timeout_client.wasm` and `timeout_server.wasm` examples demonstrate socket send and receive timeouts using the socket options. Start the server, then start the client. + +```bash +$ ./iwasm --addr-pool=0.0.0.0/15 timeout_server.wasm +``` + +The output is: + +```bash +Wait for client to connect +Client connected, sleeping for 10s +Shutting down +``` + +```bash +$ ./iwasm --addr-pool=127.0.0.1/15 timeout_client.wasm +``` + +The output is: + +```bash +Waiting on recv, which should timeout +Waiting on send, which should timeout +Success. Closing socket +``` + +The `multicast_client` and `multicast_server` examples demonstrate receiving multicast packets in WASM. Start the client and then the server with a multicast IP address and port. + +```bash +$ ./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm +$ ./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm 224.0.0.1 +$ ./iwasm --addr-pool=0.0.0.0/0,::/0 multicast_client.wasm FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2 +``` + +The output should be + +```bash +Joined multicast group. Waiting for datagram... +Reading datagram message...OK. +The message from multicast server is: "Test message" +``` + +```bash +$ ./multicast_server +$ ./multicast_server 224.0.0.1 +$ ./multicast_server FF02:113D:6FDD:2C17:A643:FFE2:1BD1:3CD2 +``` + +The output should be + +```bash +Datagram sent +``` + +### Domain name server resolution + +`addr_resolve.wasm` demonstrates the usage of resolving a domain name +``` +$ ./iwasm --allow-resolve=*.com addr_resolve.wasm github.com +``` + +The command displays the host name and its corresponding IP address: +``` +Host: github.com +IPv4 address: 140.82.121.4 (TCP) +``` + +### UDP client/server + +Start the UDP server, which opens port 1234 and waits for clients to send a message. + +```bash +cd build +./iwasm --addr-pool=0.0.0.0/15 udp_server.wasm +``` + +Start the tcp client, which sends a message to the server and waits for the response. + +```bash +cd build +./iwasm --addr-pool=127.0.0.1/15 udp_client.wasm +``` + +The output of client is like: + +```bash +[Client] Create socket +[Client] Client send +[Client] Client receive +[Client] Buffer received: Hello from server +[Client] BYE +``` + +The output of the server is like: +``` +[Server] Create socket +[Server] Bind socket +[Server] Wait for clients to connect .. +[Server] received 17 bytes from 127.0.0.1:60927: Hello from client +``` + +## Documentation + +Refer to [socket api document](../../doc/socket_api.md) for more details. diff --git a/samples/socket-api/sample_test_run.py b/samples/socket-api/sample_test_run.py new file mode 100755 index 0000000000..9951a3766f --- /dev/null +++ b/samples/socket-api/sample_test_run.py @@ -0,0 +1,140 @@ +#!/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" + 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 new file mode 100644 index 0000000000..2de2b7688d --- /dev/null +++ b/samples/socket-api/wasm-src/CMakeLists.txt @@ -0,0 +1,91 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(socket_api_sample_wasm_app) + +message(CHECK_START "Detecting WABT") +if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR})) + find_path(WABT_DIR + wabt + PATHS /opt + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH + ) + if(DEFINED WABT_DIR) + set(WABT_DIR ${WABT_DIR}/wabt) + endif() +endif() +if(WABT_DIR) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}") +find_program(WASM_OBJDUMP + wasm-objdump + PATHS "${WABT_DIR}/bin" + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH +) +if(WASM_OBJDUMP) + message(CHECK_PASS "found") +else() + message(CHECK_FAIL "not found") +endif() + +set(SRC ${CMAKE_CURRENT_SOURCE_DIR}) + +include(${CMAKE_CURRENT_SOURCE_DIR}/../../../core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake) + +function(COMPILE_WITH_CLANG SOURCE_FILE) + get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WLE) + + set(WASM_MODULE ${FILE_NAME}.wasm) + + set(MAIN_TARGET_NAME MODULE_${FILE_NAME}) + + add_executable(${MAIN_TARGET_NAME} ${SOURCE_FILE}) + set_target_properties(${MAIN_TARGET_NAME} PROPERTIES OUTPUT_NAME ${WASM_MODULE}) + target_include_directories(${MAIN_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc) + target_compile_options(${MAIN_TARGET_NAME} INTERFACE -pthread) + target_link_libraries(${MAIN_TARGET_NAME} socket_wasi_ext) + target_link_options(${MAIN_TARGET_NAME} PRIVATE + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--export=malloc + LINKER:--export=free + LINKER:--shared-memory,--max-memory=10485760 + LINKER:--no-check-features + LINKER:--allow-undefined + ) + + if(EXISTS ${WASM_OBJDUMP}) + message(STATUS "Dumping ${WASM_MODULE}...") + set(WASM_DUMP ${WASM_MODULE}.dump) + set(DUMP_TARGET_NAME DUMP_${FILE_NAME}) + + add_custom_command(OUTPUT ${WASM_DUMP} + COMMAND ${WASM_OBJDUMP} -dx ${WASM_MODULE} > ${WASM_DUMP} + COMMENT "Dumping ${WASM_MODULE}..." + DEPENDS ${MAIN_TARGET_NAME} + ) + + add_custom_target(${DUMP_TARGET_NAME} ALL + DEPENDS ${WASM_DUMP} + ) + endif() +endfunction() + +compile_with_clang(tcp_server.c) +compile_with_clang(tcp_client.c) +compile_with_clang(send_recv.c) +compile_with_clang(addr_resolve.c) +compile_with_clang(socket_opts.c) +compile_with_clang(udp_client.c) +compile_with_clang(udp_server.c) +compile_with_clang(multicast_client.c) +compile_with_clang(multicast_server.c) +compile_with_clang(timeout_client.c) +compile_with_clang(timeout_server.c) \ No newline at end of file diff --git a/samples/socket-api/wasm-src/addr_resolve.c b/samples/socket-api/wasm-src/addr_resolve.c new file mode 100644 index 0000000000..87734dea3d --- /dev/null +++ b/samples/socket-api/wasm-src/addr_resolve.c @@ -0,0 +1,69 @@ +/* + * 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 +#ifdef __wasi__ +#include +#else +#include +#endif + +int +lookup_host(const char *host) +{ + struct addrinfo hints, *res, *result; + int errcode; + char addrstr[100]; + void *ptr; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + errcode = getaddrinfo(host, NULL, &hints, &result); + if (errcode != 0) { + perror("getaddrinfo"); + return -1; + } + + res = result; + + printf("Host: %s\n", host); + while (res) { + switch (res->ai_family) { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + default: + printf("Unsupported address family: %d\n", res->ai_family); + continue; + } + inet_ntop(res->ai_family, ptr, addrstr, 100); + printf("IPv%d address: %s (%s)\n", res->ai_family == AF_INET6 ? 6 : 4, + addrstr, res->ai_socktype == SOCK_STREAM ? "TCP" : "UDP"); + res = res->ai_next; + } + + freeaddrinfo(result); + + return EXIT_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("Usage: %s DOMAIN\n", argv[0]); + return EXIT_FAILURE; + } + + return lookup_host(argv[1]); +} diff --git a/samples/socket-api/wasm-src/inc/.gitkeep b/samples/socket-api/wasm-src/inc/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/samples/socket-api/wasm-src/multicast_client.c b/samples/socket-api/wasm-src/multicast_client.c new file mode 100644 index 0000000000..43201dcbdc --- /dev/null +++ b/samples/socket-api/wasm-src/multicast_client.c @@ -0,0 +1,135 @@ +/* + * 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 +#ifdef __wasi__ +#include +#endif + +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); +} + +static int +get_ip_addr_type(char *addr, char *buf) +{ + if (inet_pton(AF_INET6, addr, buf)) { + return AF_INET6; + } + if (inet_pton(AF_INET, addr, buf)) { + return AF_INET; + } + return -1; +} + +static int +is_valid_addr_type(int addr_type) +{ + return !(addr_type == -1 + || (addr_type != AF_INET && addr_type != AF_INET6)); +} + +int +main(int argc, char *argv[]) +{ + struct ipv6_mreq ipv6_group; + struct ip_mreq ipv4_group; + int sd; + int datalen; + char databuf[1024] = { 0 }; + char multicast_addr_buffer[16]; + struct sockaddr_storage local_address = { 0 }; + int addr_type = -1; + int read_result; + int bool_opt = 1; + + if (argc < 2) { + printf("Usage is \n"); + return EXIT_FAILURE; + } + + addr_type = get_ip_addr_type(argv[1], multicast_addr_buffer); + + if (!is_valid_addr_type(addr_type)) { + printf("Not a valid ipv4 or ipv6 address\n"); + return EXIT_FAILURE; + } + + if ((sd = socket(addr_type, SOCK_DGRAM, 0)) == -1) { + perror("Failed opening socket"); + return EXIT_FAILURE; + } + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail; + } + + if (addr_type == AF_INET) { + init_sockaddr_inet((struct sockaddr_in *)&local_address); + memcpy(&(ipv4_group.imr_multiaddr), multicast_addr_buffer, 4); + ipv4_group.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &ipv4_group, + sizeof(ipv4_group)) + == -1) { + perror("Failed joining IPv4 multicast group"); + goto fail; + } + } + else { + init_sockaddr_inet6((struct sockaddr_in6 *)&local_address); + memcpy(&(ipv6_group.ipv6mr_multiaddr), multicast_addr_buffer, 16); + ipv6_group.ipv6mr_interface = 0; + + if (setsockopt(sd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &ipv6_group, + sizeof(ipv6_group)) + == -1) { + perror("Failed joining IPv6 multicast group"); + goto fail; + } + } + + if (bind(sd, (struct sockaddr *)&local_address, sizeof(local_address)) + == -1) { + perror("Failed binding socket"); + goto fail; + } + + printf("Joined multicast group. Waiting for datagram...\n"); + + datalen = sizeof(databuf) - 1; + read_result = read(sd, databuf, datalen); + + if (read_result < 0) { + perror("Failed binding socket"); + goto fail; + } + + printf("Reading datagram message...OK.\n"); + printf("The message from multicast server is: \"%s\"\n", databuf); + close(sd); + return EXIT_SUCCESS; + +fail: + close(sd); + return EXIT_FAILURE; +} diff --git a/samples/socket-api/wasm-src/multicast_server.c b/samples/socket-api/wasm-src/multicast_server.c new file mode 100644 index 0000000000..cd33557b2c --- /dev/null +++ b/samples/socket-api/wasm-src/multicast_server.c @@ -0,0 +1,120 @@ +/* + * 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 +#ifdef __wasi__ +#include +#endif + +static int +get_ip_addr_type(char *addr, char *buf) +{ + if (inet_pton(AF_INET6, addr, buf)) { + return AF_INET6; + } + if (inet_pton(AF_INET, addr, buf)) { + return AF_INET; + } + return -1; +} + +static int +is_valid_addr_type(int addr_type) +{ + return !(addr_type == -1 + || (addr_type != AF_INET && addr_type != AF_INET6)); +} + +static void +init_sockaddr_inet(struct sockaddr_in *addr, char *addr_buffer) +{ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + memcpy(&(addr->sin_addr), addr_buffer, 4); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr, char *addr_buffer) +{ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + memcpy(&(addr->sin6_addr), addr_buffer, 16); +} + +int +main(int argc, char *argv[]) +{ + struct sockaddr_storage addr = { 0 }; + int sd; + char *databuf = "Test message"; + int datalen = strlen(databuf) + 1; + char multicast_addr_buffer[16]; + int addr_type = -1; + int multicast_interface; + int bool_opt = 1; + + if (argc < 2) { + printf("Usage is \n"); + return EXIT_FAILURE; + } + + addr_type = get_ip_addr_type(argv[1], multicast_addr_buffer); + + if (!is_valid_addr_type(addr_type)) { + printf("Not a valid ipv4 or ipv6 address\n"); + return EXIT_FAILURE; + } + + if ((sd = socket(addr_type, SOCK_DGRAM, 0)) == -1) { + return EXIT_FAILURE; + } + + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail; + } + + if (addr_type == AF_INET) { + multicast_interface = htonl(INADDR_ANY); + if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, + (char *)&multicast_interface, + sizeof(multicast_interface))) { + perror("Failed setting local interface"); + goto fail; + } + init_sockaddr_inet((struct sockaddr_in *)&addr, multicast_addr_buffer); + } + else { + multicast_interface = 0; + if (setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, + (char *)&multicast_interface, + sizeof(multicast_interface))) { + perror("Failed setting local interface"); + goto fail; + } + init_sockaddr_inet6((struct sockaddr_in6 *)&addr, + multicast_addr_buffer); + } + + if (sendto(sd, databuf, datalen, 0, (struct sockaddr *)&addr, sizeof(addr)) + == -1) { + perror("Failed sending datagram"); + goto fail; + } + + printf("Datagram sent\n"); + close(sd); + return EXIT_SUCCESS; + +fail: + close(sd); + return EXIT_FAILURE; +} \ No newline at end of file diff --git a/samples/socket-api/wasm-src/send_recv.c b/samples/socket-api/wasm-src/send_recv.c new file mode 100644 index 0000000000..1760616984 --- /dev/null +++ b/samples/socket-api/wasm-src/send_recv.c @@ -0,0 +1,223 @@ +/* + * 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 +#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; + +void * +run_as_server(void *arg) +{ + (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); + + printf("Server is online ... \n"); + + new_sock = accept(sock, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (new_sock < 0) { + perror("Accept failed"); + goto fail1; + } + + printf("Start sending. \n"); + send_len = sendmsg(new_sock, &msg, 0); + if (send_len < 0) { + perror("Sendmsg failed"); + goto fail2; + } + printf("Send %ld bytes successfully!\n", send_len); + +fail2: + close(new_sock); +fail1: + shutdown(sock, SHUT_RDWR); + close(sock); + return NULL; +} + +void * +run_as_client(void *arg) +{ + (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; + } + + 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; + } + + printf("Start receiving. \n"); + recv_len = recvmsg(sock, &msg, 0); + if (recv_len < 0) { + perror("Recvmsg failed"); + goto fail; + } + + printf("Receive %ld bytes successfully!\n", recv_len); + assert(recv_len == 106); + + printf("Data:\n"); + char *s = msg.msg_iov->iov_base; + while (strlen(s) > 0) { + printf(" %s\n", s); + s += strlen(s) + 1; + } + +fail: + shutdown(sock, SHUT_RDWR); + close(sock); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + (void)argc; + (void)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/socket-api/wasm-src/socket_opts.c b/samples/socket-api/wasm-src/socket_opts.c new file mode 100644 index 0000000000..890cc0cc5c --- /dev/null +++ b/samples/socket-api/wasm-src/socket_opts.c @@ -0,0 +1,299 @@ +/* + * 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 +#include +#include +#ifdef __wasi__ +#include +#endif + +#define MULTICAST_ADDR 16777440 +#define OPTION_ASSERT(A, B, OPTION) \ + if (A == B) { \ + printf("%s is expected\n", OPTION); \ + } \ + else { \ + printf("%s is unexpected\n", OPTION); \ + perror("assertion failed"); \ + return EXIT_FAILURE; \ + } + +static struct timeval +to_timeval(time_t tv_sec, suseconds_t tv_usec) +{ + struct timeval tv = { tv_sec, tv_usec }; + return tv; +} + +static int +set_and_get_bool_opt(int socket_fd, int level, int optname, int val) +{ + int bool_opt = val; + int ret = -1; + socklen_t opt_len = sizeof(bool_opt); + + ret = setsockopt(socket_fd, level, optname, &bool_opt, sizeof(bool_opt)); + if (ret != 0) + return !val; + + bool_opt = !bool_opt; + ret = getsockopt(socket_fd, level, optname, &bool_opt, &opt_len); + if (ret != 0) + return !val; + + return bool_opt; +} + +int +main(int argc, char *argv[]) +{ + int tcp_socket_fd = 0; + int udp_socket_fd = 0; + int udp_ipv6_socket_fd = 0; + struct timeval tv; + socklen_t opt_len; + int buf_len; + int result; + struct linger linger_opt; + uint32_t time_s; + int ttl; + + printf("[Client] Create TCP socket\n"); + tcp_socket_fd = socket(AF_INET, SOCK_STREAM, 0); + if (tcp_socket_fd == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + printf("[Client] Create UDP socket\n"); + udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (udp_socket_fd == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + printf("[Client] Create UDP IPv6 socket\n"); + udp_ipv6_socket_fd = socket(AF_INET6, SOCK_DGRAM, 0); + if (udp_ipv6_socket_fd == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + // SO_RCVTIMEO + tv = to_timeval(123, 1000); + result = + setsockopt(tcp_socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + OPTION_ASSERT(result, 0, "setsockopt SO_RCVTIMEO result") + + tv = to_timeval(0, 0); + opt_len = sizeof(tv); + result = getsockopt(tcp_socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &opt_len); + OPTION_ASSERT(result, 0, "getsockopt SO_RCVTIMEO result") + OPTION_ASSERT(tv.tv_sec, 123, "SO_RCVTIMEO tv_sec"); + // OPTION_ASSERT(tv.tv_usec, 1000, "SO_RCVTIMEO tv_usec"); + + // SO_SNDTIMEO + tv = to_timeval(456, 2000); + result = + setsockopt(tcp_socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + OPTION_ASSERT(result, 0, "setsockopt SO_SNDTIMEO result") + + tv = to_timeval(0, 0); + opt_len = sizeof(tv); + result = getsockopt(tcp_socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, &opt_len); + OPTION_ASSERT(result, 0, "getsockopt SO_SNDTIMEO result") + OPTION_ASSERT(tv.tv_sec, 456, "SO_SNDTIMEO tv_sec"); + // OPTION_ASSERT(tv.tv_usec, 2000, "SO_SNDTIMEO tv_usec"); + + // SO_SNDBUF + buf_len = 8192; + result = setsockopt(tcp_socket_fd, SOL_SOCKET, SO_SNDBUF, &buf_len, + sizeof(buf_len)); + OPTION_ASSERT(result, 0, "setsockopt SO_SNDBUF result") + + buf_len = 0; + opt_len = sizeof(buf_len); + result = + getsockopt(tcp_socket_fd, SOL_SOCKET, SO_SNDBUF, &buf_len, &opt_len); + OPTION_ASSERT(result, 0, "getsockopt SO_SNDBUF result") + OPTION_ASSERT((buf_len == 16384 || buf_len == 8192), 1, + "SO_SNDBUF buf_len"); + + // SO_RCVBUF + buf_len = 4096; + result = setsockopt(tcp_socket_fd, SOL_SOCKET, SO_RCVBUF, &buf_len, + sizeof(buf_len)); + OPTION_ASSERT(result, 0, "setsockopt SO_RCVBUF result") + + buf_len = 0; + opt_len = sizeof(buf_len); + result = + getsockopt(tcp_socket_fd, SOL_SOCKET, SO_RCVBUF, &buf_len, &opt_len); + OPTION_ASSERT(result, 0, "getsockopt SO_RCVBUF result") + OPTION_ASSERT((buf_len == 8192 || buf_len == 4096), 1, "SO_SNDBUF buf_len"); + + // SO_KEEPALIVE + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, SOL_SOCKET, SO_KEEPALIVE, 1), 1, + "SO_KEEPALIVE enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, SOL_SOCKET, SO_KEEPALIVE, 0), 0, + "SO_KEEPALIVE disabled"); + + // SO_REUSEADDR + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, SOL_SOCKET, SO_REUSEADDR, 1), 1, + "SO_REUSEADDR enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, SOL_SOCKET, SO_REUSEADDR, 0), 0, + "SO_REUSEADDR disabled"); + + // SO_REUSEPORT + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, SOL_SOCKET, SO_REUSEPORT, 1), 1, + "SO_REUSEPORT enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, SOL_SOCKET, SO_REUSEPORT, 0), 0, + "SO_REUSEPORT disabled"); + + // SO_LINGER + linger_opt.l_onoff = 1; + linger_opt.l_linger = 10; + result = setsockopt(tcp_socket_fd, SOL_SOCKET, SO_LINGER, &linger_opt, + sizeof(linger_opt)); + OPTION_ASSERT(result, 0, "setsockopt SO_LINGER result") + + linger_opt.l_onoff = 0; + linger_opt.l_linger = 0; + opt_len = sizeof(linger_opt); + result = + getsockopt(tcp_socket_fd, SOL_SOCKET, SO_LINGER, &linger_opt, &opt_len); + OPTION_ASSERT(result, 0, "getsockopt SO_LINGER result") + OPTION_ASSERT(linger_opt.l_onoff, 1, "SO_LINGER l_onoff"); + OPTION_ASSERT(linger_opt.l_linger, 10, "SO_LINGER l_linger"); + + // SO_BROADCAST + OPTION_ASSERT( + set_and_get_bool_opt(udp_socket_fd, SOL_SOCKET, SO_BROADCAST, 1), 1, + "SO_BROADCAST enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(udp_socket_fd, SOL_SOCKET, SO_BROADCAST, 0), 0, + "SO_BROADCAST disabled"); + + // TCP_KEEPIDLE +#ifdef TCP_KEEPIDLE + time_s = 16; + result = setsockopt(tcp_socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s, + sizeof(time_s)); + OPTION_ASSERT(result, 0, "setsockopt TCP_KEEPIDLE result") + + time_s = 0; + opt_len = sizeof(time_s); + result = + getsockopt(tcp_socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &time_s, &opt_len); + OPTION_ASSERT(result, 0, "getsockopt TCP_KEEPIDLE result") + OPTION_ASSERT(time_s, 16, "TCP_KEEPIDLE"); +#endif + + // TCP_KEEPINTVL + time_s = 8; + result = setsockopt(tcp_socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &time_s, + sizeof(time_s)); + OPTION_ASSERT(result, 0, "setsockopt TCP_KEEPINTVL result") + + time_s = 0; + opt_len = sizeof(time_s); + result = getsockopt(tcp_socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &time_s, + &opt_len); + OPTION_ASSERT(result, 0, "getsockopt TCP_KEEPINTVL result") + OPTION_ASSERT(time_s, 8, "TCP_KEEPINTVL"); + + // TCP_FASTOPEN_CONNECT +#ifdef TCP_FASTOPEN_CONNECT + OPTION_ASSERT(set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, + TCP_FASTOPEN_CONNECT, 1), + 1, "TCP_FASTOPEN_CONNECT enabled"); + OPTION_ASSERT(set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, + TCP_FASTOPEN_CONNECT, 0), + 0, "TCP_FASTOPEN_CONNECT disabled"); +#endif + + // TCP_NODELAY + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_NODELAY, 1), 1, + "TCP_NODELAY enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_NODELAY, 0), 0, + "TCP_NODELAY disabled"); + + // TCP_QUICKACK +#ifdef TCP_QUICKACK + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_QUICKACK, 1), 1, + "TCP_QUICKACK enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(tcp_socket_fd, IPPROTO_TCP, TCP_QUICKACK, 0), 0, + "TCP_QUICKACK disabled"); +#endif + + // IP_TTL + ttl = 8; + result = setsockopt(tcp_socket_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + OPTION_ASSERT(result, 0, "IP_TIL"); + ttl = 0; + opt_len = sizeof(ttl); + result = getsockopt(tcp_socket_fd, IPPROTO_IP, IP_TTL, &ttl, &opt_len); + OPTION_ASSERT(ttl, 8, "IP_TTL"); + OPTION_ASSERT(result, 0, "IP_TIL"); + + // IPV6_V6ONLY + OPTION_ASSERT( + set_and_get_bool_opt(udp_ipv6_socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, 1), + 1, "IPV6_V6ONLY enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(udp_ipv6_socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, 0), + 0, "IPV6_V6ONLY disabled"); + + // IP_MULTICAST_LOOP + OPTION_ASSERT( + set_and_get_bool_opt(udp_socket_fd, IPPROTO_IP, IP_MULTICAST_LOOP, 1), + 1, "IP_MULTICAST_LOOP enabled"); + OPTION_ASSERT( + set_and_get_bool_opt(udp_socket_fd, IPPROTO_IP, IP_MULTICAST_LOOP, 0), + 0, "IP_MULTICAST_LOOP disabled"); + + // IP_MULTICAST_TTL + ttl = 8; + result = setsockopt(udp_socket_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, + sizeof(ttl)); + OPTION_ASSERT(result, 0, "IP_MULTICAST_TTL"); + ttl = 0; + opt_len = sizeof(ttl); + result = + getsockopt(udp_socket_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, &opt_len); + OPTION_ASSERT(ttl, 8, "IP_MULTICAST_TTL"); + OPTION_ASSERT(result, 0, "IP_MULTICAST_TTL"); + + // IPV6_MULTICAST_LOOP + OPTION_ASSERT(set_and_get_bool_opt(udp_ipv6_socket_fd, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, 1), + 1, "IPV6_MULTICAST_LOOP enabled"); + OPTION_ASSERT(set_and_get_bool_opt(udp_ipv6_socket_fd, IPPROTO_IPV6, + IPV6_MULTICAST_LOOP, 0), + 0, "IPV6_MULTICAST_LOOP disabled"); + + printf("[Client] Close sockets\n"); + close(tcp_socket_fd); + close(udp_socket_fd); + close(udp_ipv6_socket_fd); + return EXIT_SUCCESS; +} diff --git a/samples/socket-api/wasm-src/socket_utils.h b/samples/socket-api/wasm-src/socket_utils.h new file mode 100644 index 0000000000..b69135b795 --- /dev/null +++ b/samples/socket-api/wasm-src/socket_utils.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2022 Amazon.com Inc. or its affiliates. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef TCP_UTILS_H +#define TCP_UTILS_H + +#include +#include +#include + +int +sockaddr_to_string(struct sockaddr *addr, char *str, size_t len) +{ + uint16_t port; + char ip_string[64]; + void *addr_buf; + int ret; + + switch (addr->sa_family) { + case AF_INET: + { + struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; + port = addr_in->sin_port; + addr_buf = &addr_in->sin_addr; + break; + } + case AF_INET6: + { + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; + port = addr_in6->sin6_port; + addr_buf = &addr_in6->sin6_addr; + break; + } + default: + return -1; + } + + inet_ntop(addr->sa_family, addr_buf, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])); + + ret = snprintf(str, len, "%s:%d", ip_string, ntohs(port)); + + return ret > 0 && (size_t)ret < len ? 0 : -1; +} + +#endif /* TCP_UTILS_H */ \ No newline at end of file diff --git a/samples/socket-api/wasm-src/tcp_client.c b/samples/socket-api/wasm-src/tcp_client.c new file mode 100644 index 0000000000..ec5009b13f --- /dev/null +++ b/samples/socket-api/wasm-src/tcp_client.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "socket_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + /* 127.0.0.1:1234 */ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + /* [::1]:1234 */ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + addr->sin6_addr = in6addr_loopback; +} + +int +main(int argc, char *argv[]) +{ + int socket_fd, ret, total_size = 0, af; + char buffer[1024] = { 0 }; + char ip_string[64] = { 0 }; + socklen_t len; + struct sockaddr_storage server_address = { 0 }; + struct sockaddr_storage local_address = { 0 }; + + if (argc > 1 && strcmp(argv[1], "inet6") == 0) { + af = AF_INET6; + len = sizeof(struct sockaddr_in6); + init_sockaddr_inet6((struct sockaddr_in6 *)&server_address); + } + else { + af = AF_INET; + len = sizeof(struct sockaddr_in); + init_sockaddr_inet((struct sockaddr_in *)&server_address); + } + + printf("[Client] Create socket\n"); + socket_fd = socket(af, SOCK_STREAM, 0); + if (socket_fd == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + printf("[Client] Connect socket\n"); + if (connect(socket_fd, (struct sockaddr *)&server_address, len) == -1) { + perror("Connect failed"); + close(socket_fd); + return EXIT_FAILURE; + } + + len = sizeof(local_address); + ret = getsockname(socket_fd, (struct sockaddr *)&local_address, &len); + if (ret == -1) { + perror("Failed to retrieve socket address"); + close(socket_fd); + return EXIT_FAILURE; + } + + if (sockaddr_to_string((struct sockaddr *)&local_address, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])) + != 0) { + printf("[Client] failed to parse local address\n"); + close(socket_fd); + return EXIT_FAILURE; + } + + printf("[Client] Local address is: %s\n", ip_string); + + printf("[Client] Client receive\n"); + while (1) { + ret = recv(socket_fd, buffer + total_size, sizeof(buffer) - total_size, + 0); + if (ret <= 0) + break; + total_size += ret; + } + + printf("[Client] %d bytes received:\n", total_size); + if (total_size > 0) { + printf("Buffer received:\n%s\n", buffer); + } + + close(socket_fd); + printf("[Client] BYE \n"); + return EXIT_SUCCESS; +} diff --git a/samples/socket-api/wasm-src/tcp_server.c b/samples/socket-api/wasm-src/tcp_server.c new file mode 100644 index 0000000000..2cc03e2b8d --- /dev/null +++ b/samples/socket-api/wasm-src/tcp_server.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "socket_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +#define WORKER_NUM 5 + +void * +run(void *arg) +{ + const char *message = "Say Hi from the Server\n"; + int new_socket = *(int *)arg; + int i; + + printf("[Server] Communicate with the new connection #%u @ %p ..\n", + new_socket, (void *)(uintptr_t)pthread_self()); + + for (i = 0; i < 5; i++) { + if (send(new_socket, message, strlen(message), 0) < 0) { + perror("Send failed"); + break; + } + } + + printf("[Server] Shutting down the new connection #%u ..\n", new_socket); + shutdown(new_socket, SHUT_RDWR); + + return NULL; +} + +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + /* 0.0.0.0:1234 */ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + addr->sin_addr.s_addr = htonl(INADDR_ANY); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + /* [::]:1234 */ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + addr->sin6_addr = in6addr_any; +} + +int +main(int argc, char *argv[]) +{ + int socket_fd = -1, addrlen = 0, af; + struct sockaddr_storage addr = { 0 }; + unsigned connections = 0; + pthread_t workers[WORKER_NUM] = { 0 }; + int client_sock_fds[WORKER_NUM] = { 0 }; + char ip_string[64]; + + if (argc > 1 && strcmp(argv[1], "inet6") == 0) { + af = AF_INET6; + addrlen = sizeof(struct sockaddr_in6); + init_sockaddr_inet6((struct sockaddr_in6 *)&addr); + } + else { + af = AF_INET; + addrlen = sizeof(struct sockaddr_in6); + init_sockaddr_inet((struct sockaddr_in *)&addr); + } + + printf("[Server] Create socket\n"); + socket_fd = socket(af, SOCK_STREAM, 0); + if (socket_fd < 0) { + perror("Create socket failed"); + goto fail; + } + + printf("[Server] Bind socket\n"); + if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) { + perror("Bind failed"); + goto fail; + } + + printf("[Server] Listening on socket\n"); + if (listen(socket_fd, 3) < 0) { + perror("Listen failed"); + goto fail; + } + + printf("[Server] Wait for clients to connect ..\n"); + while (connections < WORKER_NUM) { + addrlen = sizeof(struct sockaddr); + client_sock_fds[connections] = + accept(socket_fd, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (client_sock_fds[connections] < 0) { + perror("Accept failed"); + break; + } + + if (sockaddr_to_string((struct sockaddr *)&addr, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])) + != 0) { + printf("[Server] failed to parse client address\n"); + goto fail; + } + + printf("[Server] Client connected (%s)\n", ip_string); + if (pthread_create(&workers[connections], NULL, run, + &client_sock_fds[connections])) { + perror("Create a worker thread failed"); + shutdown(client_sock_fds[connections], SHUT_RDWR); + break; + } + + connections++; + } + + if (connections == WORKER_NUM) { + printf("[Server] Achieve maximum amount of connections\n"); + } + + for (int i = 0; i < WORKER_NUM; i++) { + pthread_join(workers[i], NULL); + } + + printf("[Server] Shutting down ..\n"); + shutdown(socket_fd, SHUT_RDWR); + sleep(3); + printf("[Server] BYE \n"); + return EXIT_SUCCESS; + +fail: + printf("[Server] Shutting down ..\n"); + if (socket_fd >= 0) + close(socket_fd); + sleep(3); + return EXIT_FAILURE; +} diff --git a/samples/socket-api/wasm-src/timeout_client.c b/samples/socket-api/wasm-src/timeout_client.c new file mode 100644 index 0000000000..652021b5d1 --- /dev/null +++ b/samples/socket-api/wasm-src/timeout_client.c @@ -0,0 +1,111 @@ +/* + * 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 +#ifdef __wasi__ +#include +#endif + +int +main(int argc, char *argv[]) +{ + int socket_fd; + struct sockaddr_in addr; + struct timeval tv = { 0, 1 }; + const int snd_buf_len = 8; + const int data_buf_len = 1000000; + char *buffer = (char *)malloc(sizeof(char) * data_buf_len); + int result; + socklen_t opt_len = sizeof(snd_buf_len); + struct timeval snd_start_time, snd_end_time; + int bool_opt = 1; + + if (buffer == NULL) { + perror("Allocation failed, please re-run with larger heap size"); + return EXIT_FAILURE; + } + + /* 127.0.0.1:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("Create socket failed"); + goto fail1; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, + sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail2; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + perror("Failed setting SO_RCVTIMEO"); + goto fail2; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) { + perror("Failed setting SO_SNDTIMEO"); + goto fail2; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, &data_buf_len, + sizeof(data_buf_len)) + == -1) { + perror("Failed setting SO_SNDBUF"); + goto fail2; + } + + if (connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("Connect failed"); + goto fail2; + } + + if (getsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, (void *)&data_buf_len, + &opt_len) + == -1) { + perror("Failed getting SO_SNDBUF"); + goto fail2; + } + + printf("Waiting on recv, which should timeout\n"); + result = recv(socket_fd, buffer, 1, 0); + + if (result != -1 || errno != EAGAIN) { + perror("Recv did not timeout as expected"); + goto fail2; + } + + printf("Waiting on send, which should timeout\n"); + gettimeofday(&snd_start_time, NULL); + result = send(socket_fd, buffer, data_buf_len, 0); + gettimeofday(&snd_end_time, NULL); + + if (result >= data_buf_len + || snd_start_time.tv_sec != snd_end_time.tv_sec) { + perror("Send did not timeout as expected"); + goto fail2; + } + + printf("Success. Closing socket \n"); + close(socket_fd); + free(buffer); + return EXIT_SUCCESS; + +fail2: + close(socket_fd); +fail1: + free(buffer); + return EXIT_FAILURE; +} diff --git a/samples/socket-api/wasm-src/timeout_server.c b/samples/socket-api/wasm-src/timeout_server.c new file mode 100644 index 0000000000..2aaf0b5efd --- /dev/null +++ b/samples/socket-api/wasm-src/timeout_server.c @@ -0,0 +1,69 @@ +/* + * 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 +#ifdef __wasi__ +#include +#endif + +int +main(int argc, char *argv[]) +{ + int socket_fd; + int client_socket_fd; + struct sockaddr_in addr = { 0 }; + int addrlen = sizeof(addr); + int bool_opt = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &bool_opt, + sizeof(bool_opt)) + == -1) { + perror("Failed setting SO_REUSEADDR"); + goto fail; + } + + if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) == -1) { + perror("Bind socket failed"); + goto fail; + } + + if (listen(socket_fd, 1) == -1) { + perror("Listen failed"); + goto fail; + } + + if ((client_socket_fd = + accept(socket_fd, (struct sockaddr *)&addr, (socklen_t *)&addrlen)) + == -1) { + perror("Accept failed"); + goto fail; + } + + printf("Client connected, sleeping for 10s\n"); + sleep(10); + + printf("Shutting down\n"); + shutdown(client_socket_fd, SHUT_RDWR); + close(client_socket_fd); + shutdown(socket_fd, SHUT_RDWR); + close(socket_fd); + return EXIT_SUCCESS; + +fail: + close(socket_fd); + return EXIT_FAILURE; +} diff --git a/samples/socket-api/wasm-src/udp_client.c b/samples/socket-api/wasm-src/udp_client.c new file mode 100644 index 0000000000..c800764a9e --- /dev/null +++ b/samples/socket-api/wasm-src/udp_client.c @@ -0,0 +1,84 @@ +/* + * 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 +#ifdef __wasi__ +#include +#endif + +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + /* 127.0.0.1:1234 */ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + /* [::1]:1234 */ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + addr->sin6_addr = in6addr_loopback; +} + +int +main(int argc, char *argv[]) +{ + int socket_fd, ret, af; + char buffer[1024] = { 0 }; + socklen_t serverlen; + struct sockaddr_storage server_address = { 0 }; + const char *message = "Hello from client"; + + if (argc > 1 && strcmp(argv[1], "inet6") == 0) { + af = AF_INET6; + init_sockaddr_inet6((struct sockaddr_in6 *)&server_address); + serverlen = sizeof(struct sockaddr_in6); + } + else { + af = AF_INET; + init_sockaddr_inet((struct sockaddr_in *)&server_address); + serverlen = sizeof(struct sockaddr_in); + } + + printf("[Client] Create socket\n"); + socket_fd = socket(af, SOCK_DGRAM, 0); + if (socket_fd == -1) { + perror("Create socket failed"); + return EXIT_FAILURE; + } + + printf("[Client] Client send\n"); + ret = sendto(socket_fd, message, strlen(message), 0, + (struct sockaddr *)&server_address, serverlen); + if (ret < 0) { + close(socket_fd); + perror("Send failed"); + return EXIT_FAILURE; + } + + printf("[Client] Client receive\n"); + serverlen = sizeof(server_address); + /* make sure there is space for the string terminator */ + ret = recvfrom(socket_fd, buffer, sizeof(buffer) - 1, 0, + (struct sockaddr *)&server_address, &serverlen); + + if (ret > 0) { + buffer[ret] = '\0'; + printf("[Client] Buffer received: %s\n", buffer); + } + + close(socket_fd); + printf("[Client] BYE \n"); + return EXIT_SUCCESS; +} diff --git a/samples/socket-api/wasm-src/udp_server.c b/samples/socket-api/wasm-src/udp_server.c new file mode 100644 index 0000000000..2feac57203 --- /dev/null +++ b/samples/socket-api/wasm-src/udp_server.c @@ -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 "socket_utils.h" + +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#endif + +#define MAX_CONNECTIONS_COUNT 5 + +static void +init_sockaddr_inet(struct sockaddr_in *addr) +{ + /* 0.0.0.0:1234 */ + addr->sin_family = AF_INET; + addr->sin_port = htons(1234); + addr->sin_addr.s_addr = htonl(INADDR_ANY); +} + +static void +init_sockaddr_inet6(struct sockaddr_in6 *addr) +{ + /* [::]:1234 */ + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(1234); + addr->sin6_addr = in6addr_any; +} + +int +main(int argc, char *argv[]) +{ + int socket_fd = -1, af; + socklen_t addrlen = 0; + struct sockaddr_storage addr = { 0 }; + char *reply_message = "Hello from server"; + unsigned connections = 0; + char ip_string[64] = { 0 }; + char buffer[1024] = { 0 }; + + if (argc > 1 && strcmp(argv[1], "inet6") == 0) { + af = AF_INET6; + addrlen = sizeof(struct sockaddr_in6); + init_sockaddr_inet6((struct sockaddr_in6 *)&addr); + } + else { + af = AF_INET; + addrlen = sizeof(struct sockaddr_in); + init_sockaddr_inet((struct sockaddr_in *)&addr); + } + + printf("[Server] Create socket\n"); + socket_fd = socket(af, SOCK_DGRAM, 0); + if (socket_fd < 0) { + perror("Create socket failed"); + goto fail; + } + + printf("[Server] Bind socket\n"); + if (bind(socket_fd, (struct sockaddr *)&addr, addrlen) < 0) { + perror("Bind failed"); + goto fail; + } + + printf("[Server] Wait for clients to connect ..\n"); + while (connections < MAX_CONNECTIONS_COUNT) { + addrlen = sizeof(addr); + /* make sure there is space for the string terminator */ + int ret = recvfrom(socket_fd, buffer, sizeof(buffer) - 1, 0, + (struct sockaddr *)&addr, &addrlen); + if (ret < 0) { + perror("Read failed"); + goto fail; + } + buffer[ret] = '\0'; + + if (sockaddr_to_string((struct sockaddr *)&addr, ip_string, + sizeof(ip_string) / sizeof(ip_string[0])) + != 0) { + printf("[Server] failed to parse client address\n"); + goto fail; + } + + printf("[Server] received %d bytes from %s: %s\n", ret, ip_string, + buffer); + + if (sendto(socket_fd, reply_message, strlen(reply_message), 0, + (struct sockaddr *)&addr, addrlen) + < 0) { + perror("Send failed"); + break; + } + + connections++; + } + + if (connections == MAX_CONNECTIONS_COUNT) { + printf("[Server] Achieve maximum amount of connections\n"); + } + + printf("[Server] Shutting down ..\n"); + shutdown(socket_fd, SHUT_RDWR); + close(socket_fd); + sleep(3); + printf("[Server] BYE \n"); + return EXIT_SUCCESS; + +fail: + printf("[Server] Shutting down ..\n"); + if (socket_fd >= 0) + close(socket_fd); + sleep(3); + return EXIT_FAILURE; +} diff --git a/samples/spawn-thread/CMakeLists.txt b/samples/spawn-thread/CMakeLists.txt new file mode 100644 index 0000000000..6733ca13fe --- /dev/null +++ b/samples/spawn-thread/CMakeLists.txt @@ -0,0 +1,78 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) + +include(CheckPIESupported) + +project(spawn_thread) + +################ 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_FAST_INTERP 1) +set(WAMR_BUILD_THREAD_MGR 1) +set(WAMR_BUILD_SHARED_MEMORY 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 () + +# 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}/src/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (spawn_thread ${RUNTIME_SOURCE_ALL}) +check_pie_supported() +set_target_properties (spawn_thread PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_link_libraries(spawn_thread vmlib -lpthread -lm) diff --git a/samples/spawn-thread/README.md b/samples/spawn-thread/README.md new file mode 100644 index 0000000000..ec32364710 --- /dev/null +++ b/samples/spawn-thread/README.md @@ -0,0 +1,3 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/spawn-thread" +--- \ No newline at end of file diff --git a/samples/spawn-thread/src/main.c b/samples/spawn-thread/src/main.c new file mode 100644 index 0000000000..ecdf679a2b --- /dev/null +++ b/samples/spawn-thread/src/main.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "pthread.h" + +#define THREAD_NUM 10 + +typedef struct ThreadArgs { + wasm_exec_env_t exec_env; + int start; + int length; +} ThreadArgs; + +void * +thread(void *arg) +{ + ThreadArgs *thread_arg = (ThreadArgs *)arg; + wasm_exec_env_t exec_env = thread_arg->exec_env; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_function_inst_t func; + uint32 argv[2]; + + if (!wasm_runtime_init_thread_env()) { + printf("failed to initialize thread environment"); + return NULL; + } + + func = wasm_runtime_lookup_function(module_inst, "sum"); + if (!func) { + printf("failed to lookup function sum"); + wasm_runtime_destroy_thread_env(); + return NULL; + } + argv[0] = thread_arg->start; + argv[1] = thread_arg->length; + + /* call the WASM function */ + if (!wasm_runtime_call_wasm(exec_env, func, 2, argv)) { + printf("%s\n", wasm_runtime_get_exception(module_inst)); + wasm_runtime_destroy_thread_env(); + return NULL; + } + + wasm_runtime_destroy_thread_env(); + return (void *)(uintptr_t)argv[0]; +} + +void * +wamr_thread_cb(wasm_exec_env_t exec_env, void *arg) +{ + ThreadArgs *thread_arg = (ThreadArgs *)arg; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_function_inst_t func; + uint32 argv[2]; + + func = wasm_runtime_lookup_function(module_inst, "sum"); + if (!func) { + printf("failed to lookup function sum"); + return NULL; + } + argv[0] = thread_arg->start; + argv[1] = thread_arg->length; + + /* call the WASM function */ + if (!wasm_runtime_call_wasm(exec_env, func, 2, argv)) { + printf("%s\n", wasm_runtime_get_exception(module_inst)); + return NULL; + } + + return (void *)(uintptr_t)argv[0]; +} + +int +main(int argc, char *argv[]) +{ + char *wasm_file = "wasm-apps/test.wasm"; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size, wasm_argv[2], i, threads_created; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + RuntimeInitArgs init_args; + ThreadArgs thread_arg[THREAD_NUM]; + pthread_t tid[THREAD_NUM]; + wasm_thread_t wasm_tid[THREAD_NUM]; + uint32 result[THREAD_NUM], sum; + wasm_function_inst_t func; + char error_buf[128] = { 0 }; + + memset(thread_arg, 0, sizeof(ThreadArgs) * THREAD_NUM); + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; + init_args.max_thread_num = THREAD_NUM; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* instantiate the module */ + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + /* Create the first exec_env */ + if (!(exec_env = + wasm_runtime_create_exec_env(wasm_module_inst, stack_size))) { + printf("failed to create exec_env\n"); + goto fail4; + } + + func = wasm_runtime_lookup_function(wasm_module_inst, "sum"); + if (!func) { + printf("failed to lookup function sum"); + goto fail5; + } + wasm_argv[0] = 0; + wasm_argv[1] = THREAD_NUM * 10; + + /* + * Execute the wasm function in current thread, get the expect result + */ + if (!wasm_runtime_call_wasm(exec_env, func, 2, wasm_argv)) { + printf("%s\n", wasm_runtime_get_exception(wasm_module_inst)); + } + printf("expect result: %d\n", wasm_argv[0]); + + /* + * Run wasm function in multiple thread created by pthread_create + */ + memset(thread_arg, 0, sizeof(ThreadArgs) * THREAD_NUM); + for (i = 0; i < THREAD_NUM; i++) { + wasm_exec_env_t new_exec_env; + thread_arg[i].start = 10 * i; + thread_arg[i].length = 10; + + /* spawn a new exec_env to be executed in other threads */ + new_exec_env = wasm_runtime_spawn_exec_env(exec_env); + if (new_exec_env) + thread_arg[i].exec_env = new_exec_env; + else { + printf("failed to spawn exec_env\n"); + break; + } + + /* If we use: + thread_arg[i].exec_env = exec_env, + we may get wrong result */ + + if (0 != pthread_create(&tid[i], NULL, thread, &thread_arg[i])) { + printf("failed to create thread.\n"); + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + break; + } + } + + threads_created = i; + + sum = 0; + memset(result, 0, sizeof(uint32) * THREAD_NUM); + for (i = 0; i < threads_created; i++) { + pthread_join(tid[i], (void **)&result[i]); + sum += result[i]; + /* destroy the spawned exec_env */ + if (thread_arg[i].exec_env) + wasm_runtime_destroy_spawned_exec_env(thread_arg[i].exec_env); + } + + printf("[pthread]sum result: %d\n", sum); + + /* + * Run wasm function in multiple thread created by wamr spawn API + */ + memset(thread_arg, 0, sizeof(ThreadArgs) * THREAD_NUM); + for (i = 0; i < THREAD_NUM; i++) { + thread_arg[i].start = 10 * i; + thread_arg[i].length = 10; + + /* No need to spawn exec_env manually */ + if (0 + != wasm_runtime_spawn_thread(exec_env, &wasm_tid[i], wamr_thread_cb, + &thread_arg[i])) { + printf("failed to spawn thread.\n"); + break; + } + } + + threads_created = i; + + sum = 0; + memset(result, 0, sizeof(uint32) * THREAD_NUM); + for (i = 0; i < threads_created; i++) { + wasm_runtime_join_thread(wasm_tid[i], (void **)&result[i]); + sum += result[i]; + /* No need to destroy the spawned exec_env */ + } + printf("[spwan_thread]sum result: %d\n", sum); + +fail5: + wasm_runtime_destroy_exec_env(exec_env); + +fail4: + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/spawn-thread/wasm-apps/CMakeLists.txt b/samples/spawn-thread/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000000..df21b4485c --- /dev/null +++ b/samples/spawn-thread/wasm-apps/CMakeLists.txt @@ -0,0 +1,39 @@ +# 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-apps) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib -pthread -Qunused-arguments") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=32768") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (DEFINED_SYMBOLS +"${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--shared-memory,--max-memory=131072, \ + -Wl,--no-entry,--strip-all,--export=sum, \ + -Wl,--export=return_bss, \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + -Wl,--allow-undefined-file=${DEFINED_SYMBOLS}" +) + +add_executable(test.wasm sum.c) +target_link_libraries(test.wasm) diff --git a/samples/spawn-thread/wasm-apps/sum.c b/samples/spawn-thread/wasm-apps/sum.c new file mode 100644 index 0000000000..6f8b8f2189 --- /dev/null +++ b/samples/spawn-thread/wasm-apps/sum.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * have something in bss so that llvm synthesizes + * wasm start function for this module. + */ +char * +return_bss() +{ + static char bss[4096]; + return bss; +} + +int +sum(int start, int length) +{ + int sum = 0, i; + + for (i = start; i < start + length; i++) { + sum += i; + } + + return sum; +} diff --git a/samples/terminate/.gitignore b/samples/terminate/.gitignore new file mode 100644 index 0000000000..0fa8a76bda --- /dev/null +++ b/samples/terminate/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/samples/terminate/CMakeLists.txt b/samples/terminate/CMakeLists.txt new file mode 100644 index 0000000000..80b8fb688c --- /dev/null +++ b/samples/terminate/CMakeLists.txt @@ -0,0 +1,92 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +project (terminate) + +set (CMAKE_CXX_STANDARD 17) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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 Debug) +endif () + +set (WAMR_BUILD_LIBC_WASI 1) +set (WAMR_BUILD_LIB_WASI_THREADS 1) +set (WAMR_BUILD_THREAD_MGR 1) +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) + +# fast interpreter +# set (WAMR_BUILD_FAST_INTERP 1) + +# fast-jit +# set (WAMR_BUILD_FAST_JIT 1) + +# llvm jit +# set (WAMR_BUILD_JIT 1) +# set (LLVM_DIR /usr/local/opt/llvm@14/lib/cmake/llvm) + +set (WAMR_BUILD_REF_TYPES 1) + +if (NOT MSVC) + # linker 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 () +endif () + +# 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}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (terminate src/main.c ${UNCOMMON_SHARED_SOURCE}) + +check_pie_supported() +set_target_properties (terminate PROPERTIES POSITION_INDEPENDENT_CODE ON) + +if (APPLE) + target_link_libraries (terminate vmlib -lm -ldl -lpthread ${LLVM_AVAILABLE_LIBS}) +else () + target_link_libraries (terminate vmlib -lm -ldl -lpthread -lrt ${LLVM_AVAILABLE_LIBS}) +endif () diff --git a/samples/terminate/README.md b/samples/terminate/README.md new file mode 100644 index 0000000000..89a9c16bc3 --- /dev/null +++ b/samples/terminate/README.md @@ -0,0 +1,4 @@ +The "terminate" sample project +============================== + +This sample demonstrates wasm_runtime_terminate API. diff --git a/samples/terminate/build.sh b/samples/terminate/build.sh new file mode 100755 index 0000000000..4c882e981b --- /dev/null +++ b/samples/terminate/build.sh @@ -0,0 +1,63 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "##################### build terminate project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. -DCMAKE_BUILD_TYPE=Debug +make -j ${nproc} +if [ $? != 0 ];then + echo "BUILD_FAIL terminate exit as $?\n" + exit 2 +fi + +cp -a terminate ${OUT_DIR} + +printf "\n" + +echo "##################### build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.wat` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# Note: the CI installs wabt in /opt/wabt +if type wat2wasm; then + WAT2WASM=${WAT2WASM:-wat2wasm} +elif [ -x /opt/wabt/bin/wat2wasm ]; then + WAT2WASM=${WAT2WASM:-/opt/wabt/bin/wat2wasm} +fi + +${WAT2WASM} -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + +# aot +# wamrc -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE} +# mv ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE} + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "##################### build wasm apps done" diff --git a/samples/terminate/run.sh b/samples/terminate/run.sh new file mode 100755 index 0000000000..0a75fea2a1 --- /dev/null +++ b/samples/terminate/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/terminate -f out/wasm-apps/testapp.wasm diff --git a/samples/terminate/src/main.c b/samples/terminate/src/main.c new file mode 100644 index 0000000000..fb0842a2b5 --- /dev/null +++ b/samples/terminate/src/main.c @@ -0,0 +1,219 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +#include "wasm_export.h" +#include "bh_read_file.h" +#include "bh_getopt.h" + +void +print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + +static void * +runner_with_sigleton_exec_env(void *vp) +{ + wasm_module_inst_t inst = vp; + bool ok = wasm_runtime_init_thread_env(); + assert(ok); + wasm_application_execute_main(inst, 0, NULL); + wasm_runtime_destroy_thread_env(); + return inst; +} + +static void * +runner_with_spawn_exec_env(void *vp) +{ + wasm_exec_env_t env = vp; + wasm_module_inst_t inst = wasm_runtime_get_module_inst(env); + wasm_function_inst_t func; + bool ok = wasm_runtime_init_thread_env(); + assert(ok); + func = wasm_runtime_lookup_function(inst, "block_forever"); + assert(func != NULL); + wasm_runtime_call_wasm(env, func, 0, NULL); + wasm_runtime_destroy_spawned_exec_env(env); + wasm_runtime_destroy_thread_env(); + return inst; +} + +int +main(int argc, char *argv_main[]) +{ + int exit_code = 1; + static char global_heap_buf[512 * 1024]; + char *buffer; + char error_buf[128]; + int opt; + char *wasm_path = NULL; + int ret; + int pipe_fds[2]; + + const unsigned int N = 4; + wasm_module_t module = NULL; + wasm_module_inst_t module_inst[N]; + pthread_t th[N]; + unsigned int i; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + + for (i = 0; i < N; i++) { + module_inst[i] = NULL; + } + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) { + switch (opt) { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + memset(&init_args, 0, sizeof(init_args)); + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if (!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf, + sizeof(error_buf)); + if (!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* Ensure that fd_read on FD 0 blocks. */ + ret = pipe(pipe_fds); + if (ret != 0) { + goto fail; + } + wasm_runtime_set_wasi_args_ex(module, NULL, 0, NULL, 0, NULL, 0, NULL, 0, + pipe_fds[0], -1, -1); + + for (i = 0; i < N; i++) { + bool use_wasm_runtime_spawn_exec_env = i / 2 == 0; + wasm_exec_env_t env; + + module_inst[i] = wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)); + + if (!module_inst[i]) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + /* Note: ensure that module inst has an exec env so that + * it can receive the termination request. + */ + env = wasm_runtime_get_exec_env_singleton(module_inst[i]); + assert(env != NULL); + if (use_wasm_runtime_spawn_exec_env) { + env = wasm_runtime_spawn_exec_env(env); + assert(env != NULL); + } + + if ((i % 2) == 0) { + printf("terminating thread %u before starting\n", i); + wasm_runtime_terminate(module_inst[i]); + } + + if (use_wasm_runtime_spawn_exec_env) { + printf("starting thread %u (spawn_exec_env)\n", i); + ret = pthread_create(&th[i], NULL, runner_with_spawn_exec_env, env); + if (ret != 0) { + wasm_runtime_destroy_spawned_exec_env(env); + goto fail; + } + } + else { + printf("starting thread %u (singleton exec_env)\n", i); + ret = pthread_create(&th[i], NULL, runner_with_sigleton_exec_env, + module_inst[i]); + if (ret != 0) { + goto fail; + } + } + } + + printf("sleeping a bit to ensure that the threads actually started\n"); + sleep(1); + + for (i = 0; i < N; i++) { + if ((i % 2) != 0) { + printf("terminating thread %u\n", i); + wasm_runtime_terminate(module_inst[i]); + } + } + + for (i = 0; i < N; i++) { + printf("joining thread %u\n", i); + void *status; + ret = pthread_join(th[i], &status); + if (ret != 0) { + printf("pthread_join failed for thread %u\n", i); + goto fail; + } + } + + for (i = 0; i < N; i++) { + const char *exception = wasm_runtime_get_exception(module_inst[i]); + if (exception != NULL) { + if (!strstr(exception, "terminated by user")) { + printf("thread %u got an exception: %s (unexpected)\n", i, + exception); + goto fail; + } + printf("thread %u got an exception: %s (expected)\n", i, exception); + } + else { + printf("thread %u got no exception (unexpected)\n", i); + goto fail; + } + } + + exit_code = 0; +fail: + for (i = 0; i < N; i++) { + if (module_inst[i]) + wasm_runtime_deinstantiate(module_inst[i]); + } + if (module) + wasm_runtime_unload(module); + if (buffer) + BH_FREE(buffer); + wasm_runtime_destroy(); + return exit_code; +} diff --git a/samples/terminate/wasm-apps/testapp.wat b/samples/terminate/wasm-apps/testapp.wat new file mode 100644 index 0000000000..349535da73 --- /dev/null +++ b/samples/terminate/wasm-apps/testapp.wat @@ -0,0 +1,53 @@ +;; Copyright (C) 2024 YAMAMOTO Takashi +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (func $fd_read (import "wasi_snapshot_preview1" "fd_read") (param i32 i32 i32 i32) (result i32)) + (func $block_forever (export "block_forever") + ;; read from FD 0 + i32.const 100 ;; iov_base + i32.const 200 ;; buffer + i32.store + i32.const 104 ;; iov_len + i32.const 1 + i32.store + i32.const 0 ;; fd 0 + i32.const 100 ;; iov_base + i32.const 1 ;; iov count + i32.const 300 ;; retp (out) + call $fd_read + unreachable + ) + (func (export "_start") + call $block_forever + ) + + ;; a dumb malloc/free implementation + (func (export "malloc") (param i32) (result i32) + local.get 0 + i32.const 65535 + i32.add + i32.const 65536 + i32.div_u + memory.grow + local.set 0 + local.get 0 + i32.const -1 + i32.eq + if + i32.const 0 + return + end + local.get 0 + i32.const 65536 + i32.mul + ) + (func (export "free") (param i32)) + + (memory (export "memory") 1) + + ;; fake globals to make wasm_set_aux_stack happy + (global (export "__heap_base") i32 (i32.const 0x10000)) + (global (export "__data_end") i32 (i32.const 0x10000)) + (global (mut i32) (i32.const 0x10000)) +) diff --git a/samples/wasi-threads/CMakeLists.txt b/samples/wasi-threads/CMakeLists.txt new file mode 100644 index 0000000000..d532139665 --- /dev/null +++ b/samples/wasi-threads/CMakeLists.txt @@ -0,0 +1,97 @@ +# 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) + +################ 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) +set(WAMR_BUILD_REF_TYPES 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 () + +# 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 ################ +# Use ExternalProject to avoid incorporating WAMR library compilation flags into the +# compilation of the wasm application, which could lead to compatibility +# issues due to different targets. +# Like: clang: error: unsupported option '-arch' for target 'wasm32-wasi' + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../cmake) +find_package(WASISDK REQUIRED) + +include(ExternalProject) +ExternalProject_Add(wasm-apps + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps" + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_PTHREAD_TOOLCHAIN} + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} +) + + +################ 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 0000000000..a79a3cd6a6 --- /dev/null +++ b/samples/wasi-threads/README.md @@ -0,0 +1,22 @@ +# "WASI threads" sample introduction + +To run the sample, `wasi-sdk` >= 20 is required. + +## Build and run the samples + +```shell +$ mkdir build +$ cd build +$ cmake .. +$ make +... +$ ./iwasm wasm-apps/no_pthread.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 0000000000..27e06b6349 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/CMakeLists.txt @@ -0,0 +1,30 @@ +# 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) +project (wasi_threads_wasm) + +set (CMAKE_BUILD_TYPE Debug) # Otherwise no debug symbols (addr2line) + +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 + ) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${WASM_MODULE} DESTINATION wasm-apps) +endfunction () + +compile_sample(no_pthread.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 0000000000..16661bd830 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/no_pthread.c @@ -0,0 +1,68 @@ +/* + * 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); + ASSERT_VALID_TID(thread_id); + + if (__builtin_wasm_memory_atomic_wait32(&data.th_ready, 0, SECOND) == 2) { + printf("Timeout\n"); + return EXIT_FAILURE; + } + + printf("Thread completed, new value: %d, thread id: %d\n", data.value, + data.thread_id); + + assert(thread_id == data.thread_id); + + start_args_deinit(&data.base); + + return ret; +} 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 0000000000..ea8fd14006 --- /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 0000000000..5eae2938d5 --- /dev/null +++ b/samples/wasi-threads/wasm-apps/wasi_thread_start.h @@ -0,0 +1,37 @@ +/* + * 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 + +/* See https://github.com/WebAssembly/wasi-threads#design-choice-thread-ids */ +#define ASSERT_VALID_TID(TID) \ + (void)TID; \ + assert(TID >= 1 && TID <= 0x1FFFFFFF && "Invalid thread ID") + +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 0000000000..ab998a6eb6 --- /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 0000000000..bf318a2989 --- /dev/null +++ b/samples/wasm-c-api-imports/CMakeLists.txt @@ -0,0 +1,173 @@ +# 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 () + +# 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 0000000000..44ac35fc1f --- /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 registration](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. functional 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 0000000000..e2636f09e1 --- /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 0000000000..ccf574d794 --- /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 0000000000..bf2358444c --- /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} --invoke-c-api-import --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 0000000000..e69de29bb2 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 0000000000..821914f4e9 --- /dev/null +++ b/samples/wasm-c-api-imports/wasm/send_recv.c @@ -0,0 +1,248 @@ +/* + * 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) +{ + (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_RDWR); + close(sock); + return NULL; +} + +void * +run_as_client(void *arg) +{ + (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 successfully!\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_RDWR); + close(sock); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + (void)argc; + (void)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 new file mode 100644 index 0000000000..fb95a06287 --- /dev/null +++ b/samples/wasm-c-api/CMakeLists.txt @@ -0,0 +1,204 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +include(CheckPIESupported) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project(c-api) +else() + project (c-api C ASM) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_STANDARD 17) +################ runtime settings ################ + +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default 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") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + 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") + if (NOT DEFINED WAMR_BUILD_SIMD) + set (WAMR_BUILD_SIMD 1) + endif () + 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 DEFINED WAMR_BUILD_INTERP) + set(WAMR_BUILD_INTERP 1) +endif() + +if(NOT DEFINED WAMR_BUILD_AOT) + set(WAMR_BUILD_AOT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_JIT) + set(WAMR_BUILD_JIT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_MULTI_MODULE) + set(WAMR_BUILD_MULTI_MODULE 1) +endif() + +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 0) +set(WAMR_BUILD_DUMP_CALL_STACK 1) +set(WAMR_BUILD_REF_TYPES 1) + +# If not defined WAMR_BUILD_GC, set it to 0 +if(NOT DEFINED WAMRC_BUILD_WITH_GC) + set(WAMRC_BUILD_WITH_GC 0) +endif() + +if(NOT DEFINED WAMR_BUILD_FAST_INTERP) + set(WAMR_BUILD_FAST_INTERP 1) +endif() + +if (NOT MSVC) + # 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 () +endif() +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +if (MSVC) + target_compile_definitions(vmlib PRIVATE WASM_API_EXTERN=) +endif() +target_link_libraries (vmlib ${LLVM_AVAILABLE_LIBS} ${UV_A_LIBS} -lm -ldl -lpthread) + +if (WAMR_BUILD_WASM_CACHE EQUAL 1) + target_link_libraries(vmlib boringssl_crypto) +endif () +################################################ + +################ application related ################ +## locate wat2wasm +find_program(WAT2WASM + wat2wasm + PATHS /opt/wabt/bin + REQUIRED +) + +if(NOT WAT2WASM) + message(SEND_ERROR "can not find wat2wasm") +else () + execute_process(COMMAND ${WAT2WASM} --version + OUTPUT_VARIABLE WAT2WASM_VERSION_OUTPUT) + string(STRIP ${WAT2WASM_VERSION_OUTPUT} WAT2WASM_VERSION) + message("-- Found wat2wasm ${WAT2WASM_VERSION}") +endif() + +if (${WAT2WASM_VERSION} VERSION_LESS 1.0.26) + set(WAT2WASM_FLAGS "--enable-reference-types") +endif () + +if(${WAMR_BUILD_AOT} EQUAL 1 AND ${WAMR_BUILD_INTERP} EQUAL 0) + ## locate wamrc + find_program(WAMRC + wamrc + PATHS ${WAMR_ROOT_DIR}/wamr-compiler/build/ + ) + + if(NOT WAMRC) + message(SEND_ERROR "can not find wamrc. refer to \ + https://github.com/bytecodealliance/wasm-micro-runtime#build-wamrc-aot-compiler" + ) + endif() +endif() +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set(MM_UTIL src/utils/multi_module_utils.c) +# build executable for each .c +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() + +include(CTest) +enable_testing() + +foreach(EX ${EXAMPLES}) + set(SRC ${CMAKE_CURRENT_LIST_DIR}/src/${EX}.c) + + add_executable(${EX} ${SRC} ${UNCOMMON_SHARED_SOURCE} ${MM_UTIL}) + set_target_properties (${EX} PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(${EX} PRIVATE ${UNCOMMON_SHARED_DIR}) + target_link_libraries(${EX} vmlib) + if (MSVC) + target_compile_definitions(${EX} PRIVATE WASM_API_EXTERN=) + endif() + + # wat to wasm + set(WAT ${CMAKE_CURRENT_LIST_DIR}/src/${EX}.wat) + + add_custom_target(${EX}_WASM + COMMAND ${WAT2WASM} ${WAT} ${WAT2WASM_FLAGS} -o ${PROJECT_BINARY_DIR}/${EX}.wasm + DEPENDS ${WAT} + BYPRODUCTS ${PROJECT_BINARY_DIR}/${EX}.wasm + VERBATIM + ) + add_dependencies(${EX} ${EX}_WASM) + + # generate .aot file + if(${WAMR_BUILD_AOT} EQUAL 1) + if(${WAMRC_BUILD_WITH_GC} EQUAL 1) + set(WAMRC_GC_FLAGS "--enable-gc") + else() + set(WAMRC_GC_FLAGS "") + endif() + add_custom_target(${EX}_AOT + COMMAND ${WAMRC} ${WAMRC_GC_FLAGS} -o ${PROJECT_BINARY_DIR}/${EX}.aot + ${PROJECT_BINARY_DIR}/${EX}.wasm + DEPENDS ${EX}_WASM + BYPRODUCTS ${PROJECT_BINARY_DIR}/${EX}.aot + VERBATIM + COMMENT "generate a aot file ${PROJECT_BINARY_DIR}/${EX}.aot" + ) + add_dependencies(${EX} ${EX}_AOT) + endif() + + # run `ctest --test-dir build` + add_test(NAME Test_${EX} + COMMAND ./${EX} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endforeach() diff --git a/samples/wasm-c-api/README.md b/samples/wasm-c-api/README.md new file mode 100644 index 0000000000..205c57b1a0 --- /dev/null +++ b/samples/wasm-c-api/README.md @@ -0,0 +1,53 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/wasm-c-api" +--- +WAMR supports *wasm-c-api* in both *interpreter* mode and *aot* mode. + +Before staring, we need to download and install [WABT](https://github.com/WebAssembly/wabt/releases/latest). + +``` shell +$ cd /opt +$ 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. + + +``` shell +$ mkdir build +$ cd build +$ cmake .. +$ make +$ # it will build a library with c-api supporting. +$ # Also copy *.wasm from ../src/ +$ # and generate executable files +$ # now, it is ok to run samples +$ ./hello +$ ... +$ ./global +$ ... +$ ./callback +$ ... +``` + +They can be compiled and run in *aot* mode when some compiling flags are given. + +``` shell +$ mkdir build +$ cd build +$ cmake -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 .. +$ make +$ # it will build a library with c-api supporting. +$ # Also copy *.wasm from ../src/ +$ # and transform *.wasm to *.aot +$ # and generate executable files +$ # now, it is ok to run samples +$ ./hello +$ ... +$ ./global +$ ... +$ ./callback +$ ... +``` diff --git a/samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE b/samples/wasm-c-api/src/LICENSE similarity index 100% rename from samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE rename to samples/wasm-c-api/src/LICENSE diff --git a/samples/wasm-c-api/src/callback.c b/samples/wasm-c-api/src/callback.c new file mode 100644 index 0000000000..56e6730106 --- /dev/null +++ b/samples/wasm-c-api/src/callback.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// Print a Wasm value +void wasm_val_print(wasm_val_t val) { + switch (val.kind) { + case WASM_I32: { + printf("%" PRIu32, val.of.i32); + } break; + case WASM_I64: { + printf("%" PRIu64, val.of.i64); + } break; + case WASM_F32: { + printf("%f", val.of.f32); + } break; + case WASM_F64: { + printf("%g", val.of.f64); + } break; + case WASM_EXTERNREF: + case WASM_FUNCREF: { + if (val.of.ref == NULL) { + printf("null"); + } else { + printf("ref(%p)", val.of.ref); + } + } break; + } +} + +// A function to be called from Wasm code. +own wasm_trap_t* print_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n> "); + wasm_val_print(args->data[0]); + printf("\n"); + + wasm_val_copy(&results->data[0], &args->data[0]); + return NULL; +} + + +// A function closure. +own wasm_trap_t* closure_callback( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + int i = *(int*)env; + printf("Calling back closure...\n"); + printf("> %d\n", i); + + results->data[0].kind = WASM_I32; + results->data[0].of.i32 = (int32_t)i; + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("callback.aot", "rb"); +#else + FILE* file = fopen("callback.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* print_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32()); + own wasm_func_t* print_func = wasm_func_new(store, print_type, print_callback); + + int i = 42; + own wasm_functype_t* closure_type = wasm_functype_new_0_1(wasm_valtype_new_i32()); + own wasm_func_t* closure_func = wasm_func_new_with_env(store, closure_type, closure_callback, &i, NULL); + + wasm_functype_delete(print_type); + wasm_functype_delete(closure_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { + wasm_func_as_extern(print_func), wasm_func_as_extern(closure_func) + }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(print_func); + wasm_func_delete(closure_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t as[2] = { WASM_I32_VAL(3), WASM_I32_VAL(4) }; + wasm_val_t rs[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(as); + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(run_func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Print result. + printf("Printing result...\n"); + printf("> %u\n", rs[0].of.i32); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/callback.wat b/samples/wasm-c-api/src/callback.wat new file mode 100644 index 0000000000..d86195f51d --- /dev/null +++ b/samples/wasm-c-api/src/callback.wat @@ -0,0 +1,10 @@ +(module + (func $print (import "" "print") (param i32) (result i32)) + (func $closure (import "" "closure") (result i32)) + (func (export "run") (param $x i32) (param $y i32) (result i32) + (i32.add + (call $print (i32.add (local.get $x) (local.get $y))) + (call $closure) + ) + ) +) diff --git a/samples/wasm-c-api/src/callback_chain.c b/samples/wasm-c-api/src/callback_chain.c new file mode 100644 index 0000000000..440cf2a8b1 --- /dev/null +++ b/samples/wasm-c-api/src/callback_chain.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +static const byte_t * +get_memory_data(uint32_t offset, uint32_t length); + +static bool +call_wasm_function(uint32_t export_id, const wasm_val_vec_t *args, + wasm_val_vec_t *results, const char *name); + +/************************ IMPORTED FUNCTIONS **************************/ + +// (nil) -> i32 +#define FUNCTION_TYPE_NIL_I32 wasm_functype_new_0_1(wasm_valtype_new_i32()) +// (i32, i32) -> nil +#define FUNCTION_TYPE_I32X2_NIL \ + wasm_functype_new_2_0(wasm_valtype_new_i32(), wasm_valtype_new_i32()) + +/* IMPORT FUNCTION LIST */ +#define IMPORT_FUNCTION_LIST(V) \ + V(get_pairs, 0, FUNCTION_TYPE_NIL_I32) \ + V(log, 1, FUNCTION_TYPE_I32X2_NIL) + +/* EXPORT FUNCTION LIST */ +#define EXPORT_FUNCTION_LIST(V) \ + V(on_start) \ + V(on_stop) \ + V(malloc) \ + V(free) + +enum EXPORT_ITEM_NAME { +#define DEFINE_ENUM(name) e_##name, + EXPORT_FUNCTION_LIST(DEFINE_ENUM) +#undef DEFINE_ENUM + e_MEMORY, +}; + +#define DEFINE_FUNCTION(name) \ + wasm_trap_t *STUB_##name(const wasm_val_vec_t *args, \ + wasm_val_vec_t *results) + +#define DEFINE_EMPTY_FUNCTION(name) \ + DEFINE_FUNCTION(name) \ + { \ + printf("[WASM -> NATIVE] calling back %s\n", __FUNCTION__); \ + return NULL; \ + } +#undef DEFINE_EMPTY_FUNCTION + +DEFINE_FUNCTION(get_pairs) +{ + wasm_val_vec_t as = { 0 }; + wasm_val_t data[1] = { WASM_I32_VAL(10) }; + wasm_val_vec_new(&as, 1, data); + if (as.data == NULL) { + printf("ERROR: create parameters failed\n"); + return NULL; + } + + call_wasm_function(e_malloc, &as, results, "malloc"); + + wasm_val_vec_delete(&as); + return NULL; +} + +DEFINE_FUNCTION(log) +{ + wasm_val_t offset = args->data[0]; + wasm_val_t length = args->data[1]; + const byte_t *data = NULL; + + printf("[WASM -> NATIVE] calling back %s\n", __FUNCTION__); + + if (offset.kind != WASM_I32 || length.kind != WASM_I32) { + printf("> Error value type!\n"); + } + + if (!(data = get_memory_data(offset.of.i32, length.of.i32))) { + return NULL; + } + + if (data[length.of.i32 - 1]) { + printf("> Error terminated character\n"); + return NULL; + } + + printf("[WASM_LOG] %s\n", data); + return NULL; +} + +/**********************************************************************/ +// all exportted wasm functions. check with "/opt/wabt/bin/wasm-objdump -x -j +// Export X.wasm" -1: memory 0-32: functions +static own wasm_extern_vec_t exports = { 0 }; + +static const byte_t * +get_memory_data(uint32_t offset, uint32_t length) +{ + wasm_memory_t *memory; + + if (!(memory = wasm_extern_as_memory(exports.data[e_MEMORY]))) { + return NULL; + } + + byte_t *base = wasm_memory_data(memory); + size_t size = wasm_memory_data_size(memory); + if (!base || offset + length > size) { + return NULL; + } + + printf("[NATIVE -> WASM] accessing the memory...\n"); + + return base + offset; +} + +static bool +call_wasm_function(uint32_t export_id, const wasm_val_vec_t *args, + wasm_val_vec_t *results, const char *name) +{ + const wasm_func_t *function; + wasm_trap_t *trap; + + printf("[NATIVE -> WASM] calling func %s...\n", name); + + if (!(function = wasm_extern_as_func(exports.data[export_id]))) { + printf("> Error get export function %u\n", export_id); + return false; + } + + if ((trap = wasm_func_call(function, args, results))) { + own wasm_message_t message = { 0 }; + wasm_trap_message(trap, &message); + + if (message.data) { + printf("> Error calling function %s\n", message.data); + } + else { + printf("> Error calling function"); + } + + wasm_name_delete(&message); + wasm_trap_delete(trap); + return false; + } + return true; +} + +int +main(int argc, const char *argv[]) +{ + // Initialize. + printf("Initializing...\n"); + wasm_engine_t *engine = wasm_engine_new(); + wasm_store_t *store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE *file = fopen("callback_chain.aot", "rb"); +#else + FILE *file = fopen("callback_chain.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t *module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + + // Create external functions. + printf("Creating callback...\n"); +#define IMPORT_FUNCTION_VARIABLE_NAME(name, ...) \ + own wasm_func_t *function_##name = NULL; + IMPORT_FUNCTION_LIST(IMPORT_FUNCTION_VARIABLE_NAME) +#undef IMPORT_FUNCTION_VARIABLE_NAME + +#define CREATE_WASM_FUNCTION(name, index, CREATE_FUNC_TYPE) \ + { \ + own wasm_functype_t *type = CREATE_FUNC_TYPE; \ + if (!(function_##name = wasm_func_new(store, type, STUB_##name))) { \ + printf("> Error creating new function\n"); \ + return 1; \ + } \ + wasm_functype_delete(type); \ + } + IMPORT_FUNCTION_LIST(CREATE_WASM_FUNCTION) +#undef CREATE_WASM_FUNCTION + + wasm_extern_t *fs[2] = { 0 }; +#define ADD_TO_FUNCTION_LIST(name, index, ...) \ + fs[index] = wasm_func_as_extern(function_##name); + IMPORT_FUNCTION_LIST(ADD_TO_FUNCTION_LIST) +#undef ADD_TO_FUNCTION_LIST + + wasm_extern_vec_t imports = WASM_ARRAY_VEC(fs); + own wasm_instance_t *instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + +#define DESTROY_WASM_FUNCTION(name, index, ...) \ + wasm_func_delete(function_##name); + IMPORT_FUNCTION_LIST(DESTROY_WASM_FUNCTION) +#undef DESTROY_WASM_FUNCTION + + // Extract export. + printf("Extracting export...\n"); + wasm_instance_exports(instance, &exports); + if (!exports.size) { + printf("> Error accessing exports!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + + if (!call_wasm_function(e_on_start, NULL, NULL, "on_start")) { + printf("> Error calling on_start\n"); + return 1; + } + + if (!call_wasm_function(e_on_stop, NULL, NULL, "on_stop")) { + printf("> Error calling on_stop\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/callback_chain.wat b/samples/wasm-c-api/src/callback_chain.wat new file mode 100644 index 0000000000..80bbf4f74b --- /dev/null +++ b/samples/wasm-c-api/src/callback_chain.wat @@ -0,0 +1,32 @@ +;; Copyright (C) 2019 Intel Corporation. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (func $get_pairs (import "" "get_pairs") (result i32)) + (func $log (import "" "log") (param i32 i32)) + + (func $on_start (export "on_start") + (call $log (i32.const 0) (i32.const 9)) + (call $get_pairs) + (drop) + ) + + (func $on_stop (export "on_stop") + (call $log (i32.const 9) (i32.const 8)) + ) + + (func $malloc (export "malloc") (param i32) (result i32) + (call $log (i32.const 17) (i32.const 7)) + (i32.const 64) + ) + + (func $free(export "free") (param i32) + (call $log (i32.const 24) (i32.const 5)) + ) + + (memory (export "memory") 1) + (data (i32.const 0) "on_start") + (data (i32.const 9) "on_stop") + (data (i32.const 17) "malloc") + (data (i32.const 24) "free") +) diff --git a/samples/wasm-c-api/src/clone.c b/samples/wasm-c-api/src/clone.c new file mode 100644 index 0000000000..7759afee42 --- /dev/null +++ b/samples/wasm-c-api/src/clone.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define WORKER_NUMBER 10 + +/******************************* VM *******************************/ +/* Use wasm_vm_t and vm_xxx to simulate a minimal Wasm VM in Envoy */ + +typedef struct _vm { + wasm_engine_t *engine; + wasm_store_t *store; + wasm_module_t *module; + wasm_shared_module_t *shared_module; + wasm_instance_t *instance; + wasm_func_t **function_list; + wasm_memory_t *memory; + wasm_table_t *table; + wasm_extern_vec_t *exports; +} wasm_vm_t; + +typedef enum _clone_level { + not_cloneable = 0, + compiled_bytecode, + instantiated_module +} clone_level; + +typedef struct _thread_arg_t { + char name[32]; + bool *ready_go_flag; + pthread_mutex_t *ready_go_lock; + pthread_cond_t *ready_go_cond; + const wasm_vm_t *base_vm; +} thread_arg_t; + +wasm_vm_t * +vm_new() +{ + wasm_vm_t *vm = NULL; + + vm = malloc(sizeof(struct _vm)); + if (!vm) + goto fail; + + memset(vm, 0, sizeof(wasm_vm_t)); + + vm->engine = wasm_engine_new(); + if (!vm->engine) + goto fail; + + vm->store = wasm_store_new(vm->engine); + if (!vm->store) + goto fail; + + return vm; + +fail: + if (vm) { + if (vm->engine) + wasm_engine_delete(vm->engine); + + free(vm); + } + return NULL; +} + +wasm_vm_t * +vm_release(wasm_vm_t *vm) +{ + if (!vm) + return NULL; + + if (vm->function_list) { + free(vm->function_list); + vm->function_list = NULL; + } + + vm->memory = NULL; + + if (vm->exports) { + wasm_extern_vec_delete(vm->exports); + free(vm->exports); + vm->exports = NULL; + } + + wasm_instance_delete(vm->instance); + vm->instance = NULL; + + wasm_shared_module_delete(vm->shared_module); + vm->shared_module = NULL; + + wasm_module_delete(vm->module); + vm->module = NULL; + + wasm_store_delete(vm->store); + vm->store = NULL; + + wasm_engine_delete(vm->engine); + vm->engine = NULL; + + free(vm); + return NULL; +} + +bool +vm_load(wasm_vm_t *vm, const wasm_byte_vec_t *binary) +{ + vm->module = wasm_module_new(vm->store, binary); + vm->shared_module = wasm_module_share(vm->module); + return vm->module != NULL; +} + +bool +vm_link(wasm_vm_t *vm, wasm_extern_vec_t *imports) +{ + vm->instance = wasm_instance_new(vm->store, vm->module, imports, NULL); + if (!vm->instance) + goto fail; + + vm->exports = malloc(sizeof(wasm_extern_vec_t)); + if (!vm->exports) + goto fail; + + memset(vm->exports, 0, sizeof(wasm_extern_vec_t)); + wasm_instance_exports(vm->instance, vm->exports); + /* an exported memory, and two exported functions */ + assert(vm->exports->size == 3); + + /* bind memory */ + assert(wasm_extern_kind(vm->exports->data[0]) == WASM_EXTERN_MEMORY); + vm->memory = wasm_extern_as_memory(vm->exports->data[0]); + + vm->function_list = malloc(2 * sizeof(wasm_func_t *)); + if (!vm->function_list) + goto fail; + + memset(vm->function_list, 0, 2 * sizeof(wasm_func_t *)); + + /* bind wasm_set_byte(...) */ + assert(wasm_extern_kind(vm->exports->data[1]) == WASM_EXTERN_FUNC); + vm->function_list[0] = wasm_extern_as_func(vm->exports->data[1]); + + /* bind wasm_get_byte(...) */ + assert(wasm_extern_kind(vm->exports->data[2]) == WASM_EXTERN_FUNC); + vm->function_list[1] = wasm_extern_as_func(vm->exports->data[2]); + + return true; +fail: + return false; +} + +wasm_vm_t * +vm_clone_from_module(const wasm_vm_t *base) +{ + printf("Initializing...\n"); + wasm_vm_t *secondary = NULL; + + secondary = vm_new(); + if (secondary) { + printf("Reuse module and bypass vm_load()..."); + secondary->module = + wasm_module_obtain(base->store, base->shared_module); + if (!secondary->module) + secondary = vm_release(secondary); + } + + return secondary; +} + +wasm_vm_t * +vm_clone_from_instance(const wasm_vm_t *base) +{ + /** + * if do a clone of the level instantiated_module, need to malloc and + * initialize + * - global. WASMGlobalInstance and global data + * - memory. WASMMemoryInstance, memory_data and heap + * - table. WASMTableInstance, table_data + * - exports. all global, memory and table + * + * it is almost everything in wasm_instantiate() except function. + */ + (void)base; + printf("Unsupported\n"); + return NULL; +} + +wasm_vm_t * +vm_clone(const wasm_vm_t *base, clone_level level) +{ + if (level == not_cloneable) + return NULL; + + if (level == compiled_bytecode) + return vm_clone_from_module(base); + else + return vm_clone_from_instance(base); +} + +bool +vm_memory_set_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t byte) +{ + byte_t *data = wasm_memory_data(vm->memory); + assert(data); + *(data + offset) = byte; + return true; +} + +bool +vm_memory_get_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t *byte) +{ + byte_t *data = wasm_memory_data(vm->memory); + assert(data); + *byte = *(data + offset); + return true; +} + +bool +vm_function_set_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t byte) +{ + wasm_val_t a_v[2] = { WASM_I32_VAL(offset), WASM_I32_VAL(byte) }; + wasm_val_vec_t args = WASM_ARRAY_VEC(a_v); + wasm_val_vec_t results = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(vm->function_list[0], &args, &results); + if (trap) { + printf("call wasm_set_byte failed"); + wasm_trap_delete(trap); + return false; + } + + return true; +} + +bool +vm_function_get_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t *byte) +{ + wasm_val_t a_v[1] = { WASM_I32_VAL(offset) }; + wasm_val_vec_t args = WASM_ARRAY_VEC(a_v); + wasm_val_t r_v[1] = { WASM_INIT_VAL }; + wasm_val_vec_t results = WASM_ARRAY_VEC(r_v); + wasm_trap_t *trap = wasm_func_call(vm->function_list[1], &args, &results); + if (trap) { + printf("call wasm_get_byte failed"); + wasm_trap_delete(trap); + return false; + } + + assert(results.data->kind == WASM_I32); + *byte = results.data->of.i32; + return true; +} + +static bool +load_wasm_file_content(const char *file_name, wasm_byte_vec_t *out) +{ + bool ret = false; +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE *file = fopen(file_name, "rb"); +#else + FILE *file = fopen(file_name, "rb"); +#endif + if (!file) { + printf("> Error loading .wasm!\n"); + goto quit; + } + + int offset = fseek(file, 0L, SEEK_END); + if (offset == -1) { + printf("> Error loading .wasm!\n"); + goto close_file; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading .wasm!\n"); + goto close_file; + } + + offset = fseek(file, 0L, SEEK_SET); + if (offset == -1) { + printf("> Error loading .wasm!\n"); + goto close_file; + } + + wasm_byte_vec_new_uninitialized(out, file_size); + if (fread(out->data, file_size, 1, file) != 1) { + printf("> Error loading content!\n"); + goto close_file; + } + + ret = true; +close_file: + fclose(file); +quit: + return ret; +} + +static pthread_key_t name_key; + +wasm_trap_t * +report_cb(const wasm_val_vec_t *args, wasm_val_vec_t *results) +{ + (void)results; + + assert(args->data[0].kind == WASM_I32); + uint32_t chk_pnt_no = args->data[0].of.i32; + + char *name = pthread_getspecific(name_key); + printf("[%s] Pass CHK POINT #%u\n", name, chk_pnt_no); + + return NULL; +} + +bool +run_code_start(wasm_vm_t **out) +{ + bool ret = false; + + printf("Initializing...\n"); + wasm_vm_t *vm = vm_new(); + if (!vm) + goto fail; + + printf("Loading binary...\n"); + wasm_byte_vec_t binary = { 0 }; +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + const char *file_name = "clone.aot"; +#else + const char *file_name = "clone.wasm"; +#endif + if (!load_wasm_file_content(file_name, &binary)) + goto release_vm; + + printf("Compiling module...\n"); + ret = vm_load(vm, &binary); + wasm_byte_vec_delete(&binary); + if (!ret) + goto release_vm; + + printf("Creating callback...\n"); + wasm_functype_t *callback_type = + wasm_functype_new_1_0(wasm_valtype_new_i32()); + if (!callback_type) + goto release_vm; + + wasm_func_t *callback = wasm_func_new(vm->store, callback_type, report_cb); + wasm_functype_delete(callback_type); + if (!callback) + goto release_vm; + + printf("Instantiating module...\n"); + wasm_extern_t *externs[] = { wasm_func_as_extern(callback) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + ret = vm_link(vm, &imports); + wasm_func_delete(callback); + if (!ret) + goto release_vm; + + *out = vm; + return true; + +release_vm: + vm_release(vm); +fail: + return false; +} + +bool +run_warm_start_w_compiled_bytecode(const wasm_vm_t *first, wasm_vm_t **out) +{ + bool ret; + wasm_vm_t *secondary = vm_clone(first, compiled_bytecode); + if (!secondary) + goto fail; + + printf("Creating callback...\n"); + wasm_functype_t *callback_type = + wasm_functype_new_1_0(wasm_valtype_new_i32()); + if (!callback_type) + goto release_vm; + + wasm_func_t *callback = + wasm_func_new(secondary->store, callback_type, report_cb); + wasm_functype_delete(callback_type); + if (!callback) + goto release_vm; + + printf("Instantiating module...\n"); + wasm_extern_t *externs[] = { wasm_func_as_extern(callback) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + ret = vm_link(secondary, &imports); + wasm_func_delete(callback); + if (!ret) + goto release_vm; + + *out = secondary; + return true; + +release_vm: + vm_release(secondary); +fail: + return false; +} + +bool +run_warm_start_w_instantiated_module(const wasm_vm_t *first, wasm_vm_t **out) +{ + wasm_vm_t *secondary = vm_clone(first, instantiated_module); + if (!secondary) + return false; + + *out = secondary; + return true; +} + +void +run_test(const wasm_vm_t *vm) +{ + uint8_t byte = 0xFF; + + /* read initialization */ + vm_function_get_byte(vm, 10, &byte); + assert(byte == 0x0); + vm_memory_get_byte(vm, 10, &byte); + assert(byte == 0x0); + + /* read after writing */ + vm_function_set_byte(vm, 16, 0xab); + vm_function_get_byte(vm, 16, &byte); + assert(byte == 0xab); + + vm_memory_set_byte(vm, 16, 0xcd); + vm_memory_get_byte(vm, 16, &byte); + assert(byte == 0xcd); + + /* reading and writing across */ + vm_function_set_byte(vm, 16, 0xef); + vm_memory_get_byte(vm, 16, &byte); + assert(byte == 0xef); + + vm_memory_set_byte(vm, 16, 0x67); + vm_function_get_byte(vm, 16, &byte); + assert(byte == 0x67); + + printf("All Passed ...\n"); +} + +static void * +thrd_func(void *arg) +{ + thread_arg_t *thrd_arg = (thread_arg_t *)arg; + + sleep(rand() % 5); + printf("Running warm start at %s...\n", thrd_arg->name); + + pthread_setspecific(name_key, thrd_arg->name); + + wasm_vm_t *vm; + if (!run_warm_start_w_compiled_bytecode(thrd_arg->base_vm, &vm)) + return NULL; + + pthread_mutex_trylock(thrd_arg->ready_go_lock); + while (!(*thrd_arg->ready_go_flag)) { + pthread_cond_wait(thrd_arg->ready_go_cond, thrd_arg->ready_go_lock); + } + pthread_mutex_unlock(thrd_arg->ready_go_lock); + + printf("Running test at %s...\n", thrd_arg->name); + run_test(vm); + + vm_release(vm); + pthread_exit(NULL); + return NULL; +} + +int +main() +{ + int ret = EXIT_FAILURE; + bool ready_go_flag = false; + pthread_mutex_t ready_go_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t ready_go_cond = PTHREAD_COND_INITIALIZER; + pthread_key_create(&name_key, NULL); + pthread_setspecific(name_key, "Execution Thread"); + + printf("Running cold start at the execution thread...\n"); + wasm_vm_t *base_vm; + if (!run_code_start(&base_vm)) + goto quit; + run_test(base_vm); + + printf("Running warm start at other threads...\n"); + + pthread_t tids[WORKER_NUMBER] = { 0 }; + thread_arg_t thrd_args[WORKER_NUMBER] = { 0 }; + for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) { + thread_arg_t *thrd_arg = thrd_args + i; + + snprintf(thrd_arg->name, 32, "Worker#%lu", i); + thrd_arg->ready_go_cond = &ready_go_cond; + thrd_arg->ready_go_lock = &ready_go_lock; + thrd_arg->ready_go_flag = &ready_go_flag; + thrd_arg->base_vm = base_vm; + + int ret = pthread_create(&tids[i], NULL, thrd_func, thrd_arg); + if (ret != 0) + break; + } + + sleep(1); + pthread_mutex_trylock(&ready_go_lock); + ready_go_flag = true; + pthread_mutex_unlock(&ready_go_lock); + pthread_cond_broadcast(&ready_go_cond); + + sleep(3); + for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) { + if (tids[i] != 0) + pthread_join(tids[i], NULL); + } + + vm_release(base_vm); + ret = EXIT_SUCCESS; +quit: + return ret; +} diff --git a/samples/wasm-c-api/src/clone.wat b/samples/wasm-c-api/src/clone.wat new file mode 100644 index 0000000000..e9934cc0d9 --- /dev/null +++ b/samples/wasm-c-api/src/clone.wat @@ -0,0 +1,15 @@ +(module + (func $report (import "" "report") (param i32)) + + (memory (export "mem") 1 1) + + (func $wasm_set_byte (export "set_byte") (param i32 i32) + (call $report (i32.const 1)) + (i32.store8 (local.get 0) (local.get 1)) + ) + + (func $wasm_get_byte (export "get_byte") (param i32) (result i32) + (call $report (i32.const 2)) + (i32.load(local.get 0)) + ) +) \ No newline at end of file diff --git a/samples/wasm-c-api/src/empty_imports.c b/samples/wasm-c-api/src/empty_imports.c new file mode 100644 index 0000000000..c8788b51ac --- /dev/null +++ b/samples/wasm-c-api/src/empty_imports.c @@ -0,0 +1,122 @@ +#include + +#include "wasm_c_api.h" + +#define own + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("empty_imports.aot", "rb"); +#else + FILE* file = fopen("empty_imports.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate with non-null but empty imports array. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Run an exported function to verify that the instance was created correctly. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + + const wasm_func_t* add_func = wasm_extern_as_func(exports.data[0]); + if (add_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + printf("Calling export...\n"); + wasm_val_t args[2] = { WASM_I32_VAL(3), WASM_I32_VAL(4) }; + wasm_val_vec_t args_vec = WASM_ARRAY_VEC(args); + + wasm_val_t results[1] = { WASM_INIT_VAL }; + wasm_val_vec_t results_vec = WASM_ARRAY_VEC(results); + + wasm_trap_t *trap = wasm_func_call(add_func, &args_vec, &results_vec); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + return 1; + } + + if (results_vec.data[0].of.i32 != 7) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/empty_imports.wat b/samples/wasm-c-api/src/empty_imports.wat new file mode 100644 index 0000000000..38639790b3 --- /dev/null +++ b/samples/wasm-c-api/src/empty_imports.wat @@ -0,0 +1,5 @@ +(module + (func (export "add") (param i32 i32) (result i32) + (i32.add (local.get 0) (local.get 1)) + ) +) diff --git a/samples/wasm-c-api/src/global.c b/samples/wasm-c-api/src/global.c new file mode 100644 index 0000000000..91c8cb654d --- /dev/null +++ b/samples/wasm-c-api/src/global.c @@ -0,0 +1,278 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) { + printf("> Error accessing global export %zu!\n", i); + exit(1); + } + return wasm_extern_as_global(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +#define check(val, type, expected) \ + if (val.of.type != expected) { \ + printf("> Error reading value\n"); \ + exit(1); \ + } + +#define check_global(global, type, expected) \ + { \ + wasm_val_t val; \ + wasm_global_get(global, &val); \ + check(val, type, expected); \ + } + +#define check_trap(trap) \ + if (trap) { \ + printf("> Error calling function\n"); \ + wasm_trap_delete(trap); \ + exit(1); \ + } + +#define check_call(func, type, expected) \ + { \ + wasm_val_t vs[1]; \ + wasm_val_vec_t args = WASM_EMPTY_VEC; \ + wasm_val_vec_t results = WASM_ARRAY_VEC(vs); \ + wasm_trap_t *trap = wasm_func_call(func, &args, &results); \ + check_trap(trap); \ + check(vs[0], type, expected); \ + } + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("global.aot", "rb"); +#else + FILE* file = fopen("global.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external globals. + printf("Creating globals...\n"); + own wasm_globaltype_t* const_f32_type = wasm_globaltype_new( + wasm_valtype_new(WASM_F32), WASM_CONST); + own wasm_globaltype_t* const_i64_type = wasm_globaltype_new( + wasm_valtype_new(WASM_I64), WASM_CONST); + own wasm_globaltype_t* var_f32_type = wasm_globaltype_new( + wasm_valtype_new(WASM_F32), WASM_VAR); + own wasm_globaltype_t* var_i64_type = wasm_globaltype_new( + wasm_valtype_new(WASM_I64), WASM_VAR); + + wasm_val_t val_f32_1 = WASM_F32_VAL(1); + own wasm_global_t* const_f32_import = + wasm_global_new(store, const_f32_type, &val_f32_1); + wasm_val_t val_i64_2 = WASM_I64_VAL(2); + own wasm_global_t* const_i64_import = + wasm_global_new(store, const_i64_type, &val_i64_2); + wasm_val_t val_f32_3 = WASM_F32_VAL(3); + own wasm_global_t* var_f32_import = + wasm_global_new(store, var_f32_type, &val_f32_3); + wasm_val_t val_i64_4 = WASM_I64_VAL(4); + own wasm_global_t* var_i64_import = + wasm_global_new(store, var_i64_type, &val_i64_4); + + wasm_globaltype_delete(const_f32_type); + wasm_globaltype_delete(const_i64_type); + wasm_globaltype_delete(var_f32_type); + wasm_globaltype_delete(var_i64_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { + wasm_global_as_extern(const_f32_import), + wasm_global_as_extern(const_i64_import), + wasm_global_as_extern(var_f32_import), + wasm_global_as_extern(var_i64_import) + }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_module_delete(module); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_global_t* const_f32_export = get_export_global(&exports, i++); + wasm_global_t* const_i64_export = get_export_global(&exports, i++); + wasm_global_t* var_f32_export = get_export_global(&exports, i++); + wasm_global_t* var_i64_export = get_export_global(&exports, i++); + wasm_func_t* get_const_f32_import = get_export_func(&exports, i++); + wasm_func_t* get_const_i64_import = get_export_func(&exports, i++); + wasm_func_t* get_var_f32_import = get_export_func(&exports, i++); + wasm_func_t* get_var_i64_import = get_export_func(&exports, i++); + wasm_func_t* get_const_f32_export = get_export_func(&exports, i++); + wasm_func_t* get_const_i64_export = get_export_func(&exports, i++); + wasm_func_t* get_var_f32_export = get_export_func(&exports, i++); + wasm_func_t* get_var_i64_export = get_export_func(&exports, i++); + wasm_func_t* set_var_f32_import = get_export_func(&exports, i++); + wasm_func_t* set_var_i64_import = get_export_func(&exports, i++); + wasm_func_t* set_var_f32_export = get_export_func(&exports, i++); + wasm_func_t* set_var_i64_export = get_export_func(&exports, i++); + + // Try cloning. + own wasm_global_t* copy = wasm_global_copy(var_f32_import); + assert(wasm_global_same(var_f32_import, copy)); + wasm_global_delete(copy); + + // Interact. + printf("Accessing globals...\n"); + + // Check initial values. + check_global(const_f32_import, f32, 1); + check_global(const_i64_import, i64, 2); + check_global(var_f32_import, f32, 3); + check_global(var_i64_import, i64, 4); + check_global(const_f32_export, f32, 5); + check_global(const_i64_export, i64, 6); + check_global(var_f32_export, f32, 7); + check_global(var_i64_export, i64, 8); + + check_call(get_const_f32_import, f32, 1); + check_call(get_const_i64_import, i64, 2); + check_call(get_var_f32_import, f32, 3); + check_call(get_var_i64_import, i64, 4); + check_call(get_const_f32_export, f32, 5); + check_call(get_const_i64_export, i64, 6); + check_call(get_var_f32_export, f32, 7); + check_call(get_var_i64_export, i64, 8); + + // Modify variables through API and check again. + wasm_val_t val33 = WASM_F32_VAL(33); + wasm_global_set(var_f32_import, &val33); + wasm_val_t val34 = WASM_I64_VAL(34); + wasm_global_set(var_i64_import, &val34); + wasm_val_t val37 = WASM_F32_VAL(37); + wasm_global_set(var_f32_export, &val37); + wasm_val_t val38 = WASM_I64_VAL(38); + wasm_global_set(var_i64_export, &val38); + + check_global(var_f32_import, f32, 33); + check_global(var_i64_import, i64, 34); + check_global(var_f32_export, f32, 37); + check_global(var_i64_export, i64, 38); + + check_call(get_var_f32_import, f32, 33); + check_call(get_var_i64_import, i64, 34); + check_call(get_var_f32_export, f32, 37); + check_call(get_var_i64_export, i64, 38); + + // Modify variables through calls and check again. + wasm_val_vec_t res = WASM_EMPTY_VEC; + wasm_val_t vs73[] = { WASM_F32_VAL(73) }; + wasm_val_vec_t args73 = WASM_ARRAY_VEC(vs73); + wasm_trap_t *trap = wasm_func_call(set_var_f32_import, &args73, &res); + check_trap(trap); + + wasm_val_t vs74[] = { WASM_I64_VAL(74) }; + wasm_val_vec_t args74 = WASM_ARRAY_VEC(vs74); + trap = wasm_func_call(set_var_i64_import, &args74, &res); + check_trap(trap); + + wasm_val_t vs77[] = { WASM_F32_VAL(77) }; + wasm_val_vec_t args77 = WASM_ARRAY_VEC(vs77); + trap = wasm_func_call(set_var_f32_export, &args77, &res); + check_trap(trap); + + wasm_val_t vs78[] = { WASM_I64_VAL(78) }; + wasm_val_vec_t args78 = WASM_ARRAY_VEC(vs78); + trap = wasm_func_call(set_var_i64_export, &args78, &res); + check_trap(trap); + + check_global(var_f32_import, f32, 73); + check_global(var_i64_import, i64, 74); + check_global(var_f32_export, f32, 77); + check_global(var_i64_export, i64, 78); + + check_call(get_var_f32_import, f32, 73); + check_call(get_var_i64_import, i64, 74); + check_call(get_var_f32_export, f32, 77); + check_call(get_var_i64_export, i64, 78); + + wasm_global_delete(const_f32_import); + wasm_global_delete(const_i64_import); + wasm_global_delete(var_f32_import); + wasm_global_delete(var_i64_import); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/global.wat b/samples/wasm-c-api/src/global.wat new file mode 100644 index 0000000000..dea085772b --- /dev/null +++ b/samples/wasm-c-api/src/global.wat @@ -0,0 +1,27 @@ +(module + (global $f32_import (import "" "const f32") f32) + (global $i64_import (import "" "const i64") i64) + (global $mut_f32_import (import "" "var f32") (mut f32)) + (global $mut_i64_import (import "" "var i64") (mut i64)) + + (global $f32_export (export "const f32") f32 (f32.const 5)) + (global $i64_export (export "const i64") i64 (i64.const 6)) + (global $mut_f32_export (export "var f32") (mut f32) (f32.const 7)) + (global $mut_i64_export (export "var i64") (mut i64) (i64.const 8)) + + (func (export "get const f32 import") (result f32) (global.get $f32_import)) + (func (export "get const i64 import") (result i64) (global.get $i64_import)) + (func (export "get var f32 import") (result f32) (global.get $mut_f32_import)) + (func (export "get var i64 import") (result i64) (global.get $mut_i64_import)) + + (func (export "get const f32 export") (result f32) (global.get $f32_export)) + (func (export "get const i64 export") (result i64) (global.get $i64_export)) + (func (export "get var f32 export") (result f32) (global.get $mut_f32_export)) + (func (export "get var i64 export") (result i64) (global.get $mut_i64_export)) + + (func (export "set var f32 import") (param f32) (global.set $mut_f32_import (local.get 0))) + (func (export "set var i64 import") (param i64) (global.set $mut_i64_import (local.get 0))) + + (func (export "set var f32 export") (param f32) (global.set $mut_f32_export (local.get 0))) + (func (export "set var f64 export") (param i64) (global.set $mut_i64_export (local.get 0))) +) diff --git a/samples/wasm-c-api/src/globalexportimport-0.wat b/samples/wasm-c-api/src/globalexportimport-0.wat new file mode 100644 index 0000000000..7ab897c732 --- /dev/null +++ b/samples/wasm-c-api/src/globalexportimport-0.wat @@ -0,0 +1,8 @@ +;; Copyright (C) 2019 Intel Corporation. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (global $mut_f32_export (export "var f32") (mut f32) (f32.const 7)) + (func (export "get var f32 export") (result f32) (global.get $mut_f32_export)) + (func (export "set var f32 export") (param f32) (global.set $mut_f32_export (local.get 0))) +) diff --git a/samples/wasm-c-api/src/globalexportimport-1.wat b/samples/wasm-c-api/src/globalexportimport-1.wat new file mode 100644 index 0000000000..927e420d78 --- /dev/null +++ b/samples/wasm-c-api/src/globalexportimport-1.wat @@ -0,0 +1,10 @@ +;; Copyright (C) 2019 Intel Corporation. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + (global $mut_f32_import (export "var f32") (import "globalexportimport-0" "var f32") (mut f32)) + (func (export "get var f32 export") (import "globalexportimport-0" "get var f32 export") (result f32)) + (func (export "set var f32 export") (import "globalexportimport-0" "set var f32 export") (param f32)) + (func (export "get var f32 import") (result f32) (global.get $mut_f32_import)) + (func (export "set var f32 import") (param f32) (global.set $mut_f32_import (local.get 0))) +) \ No newline at end of file diff --git a/samples/wasm-c-api/src/globalexportimport.c b/samples/wasm-c-api/src/globalexportimport.c new file mode 100644 index 0000000000..1c1715f2e5 --- /dev/null +++ b/samples/wasm-c-api/src/globalexportimport.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +#include "wasm_c_api.h" +#include "wasm_export.h" +#include "bh_platform.h" + +extern bool +reader(const char *module_name, uint8 **p_buffer, uint32 *p_size); + +extern void +destroyer(uint8 *buffer, uint32 size); + +#define own + +wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) { + printf("> Error accessing global export %zu!\n", i); + exit(1); + } + return wasm_extern_as_global(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +#define check(val, type, expected) \ + if (val.of.type != expected) { \ + printf("> Expected reading value %f or %f \n", expected, expected); \ + printf("> Error reading value %f or %f\n", val.of.type, val.of.type); \ + } + +#define check_global(global, type, expected) \ + { \ + wasm_val_t val; \ + wasm_global_get(global, &val); \ + check(val, type, expected); \ + } + +#define check_trap(trap) \ + if (trap) { \ + printf("> Error calling function\n"); \ + wasm_trap_delete(trap); \ + exit(1); \ + } + +#define check_call(func, type, expected) \ + { \ + wasm_val_vec_t results; \ + wasm_val_vec_new_uninitialized(&results, 1); \ + wasm_trap_t *trap = wasm_func_call(func, NULL, &results); \ + check_trap(trap); \ + check(results.data[0], type, expected); \ + } + +wasm_module_t * create_module_from_file(wasm_store_t* store, const char * filename) +{ + FILE* file = fopen(filename, "rb"); + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + 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"); + fclose(file); + return NULL; + } + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return NULL; + } + wasm_byte_vec_delete(&binary); + fclose(file); + return module; +} + + +int main(int argc, const char* argv[]) { + wasm_runtime_set_module_reader(reader, destroyer); + + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + wasm_module_t* moduleimport = + create_module_from_file(store, "globalimport.aot"); +#else + wasm_module_t* moduleimport = + create_module_from_file(store, "globalexportimport-1.wasm"); +#endif + + if (!moduleimport) { + return 1; + } + + // Instantiate. + printf("Instantiating Import module...\n"); + own wasm_instance_t* instance_import = + wasm_instance_new(store, moduleimport, NULL, NULL); //after this var_f32_export->inst_comm_rt is module_import + if (!instance_import) { + printf("> Error instantiating Import module!\n"); + return 1; + } + wasm_module_delete(moduleimport); + + // Extract export. + printf("Extracting exports from Import module...\n"); + own wasm_extern_vec_t exports_of_import; + wasm_instance_exports(instance_import, &exports_of_import); + int i = 0; + wasm_global_t *var_f32_export = get_export_global(&exports_of_import, i++); + wasm_func_t *get_var_f32_export = get_export_func(&exports_of_import, i++); + wasm_func_t* set_var_f32_export = get_export_func(&exports_of_import, i++); + wasm_func_t* get_var_f32_import = get_export_func(&exports_of_import, i++); + wasm_func_t* set_var_f32_import = get_export_func(&exports_of_import, i++); + + // Interact. + + // Check initial values. + printf("Check initial values...\n"); + check_global(var_f32_export, f32, 7.0); + check_call(get_var_f32_export, f32, 7.0); //Call to module export + check_call(get_var_f32_import, f32, 7.0); //Call to module import + + + // Modify variables through API and check again. + printf("Modify the variable to 37.0...\n"); + wasm_val_t val37 = {.kind = WASM_F32, .of = {.f32 = 37.0}}; + wasm_global_set(var_f32_export, &val37); // var_f32_export->inst_comm_rt is module_import now + + check_global(var_f32_export, f32, 37.0); + check_call(get_var_f32_export, f32, 37.0); //Call to module export Failed here, still 7 + check_call(get_var_f32_import, f32, 37.0); //Call to module import + + // Modify variables through calls and check again. + printf("Modify the variable to 77.0...\n"); + wasm_val_vec_t args77; + wasm_val_vec_new(&args77, 1, (wasm_val_t []){ {.kind = WASM_F32, .of = {.f32 = 77.0}} }); + wasm_trap_t *trap = wasm_func_call(set_var_f32_export, &args77, + NULL); // Call to module export + check_trap(trap); + check_call(get_var_f32_export, f32, 77.0); //Call to module export + check_global(var_f32_export, f32, 77.0); //Failed here, still 37 + check_call(get_var_f32_import, f32, 77.0); //Call to module import Failed here, still 37 + + + printf("Modify the variable to 78.0...\n"); + wasm_val_vec_t args78; + wasm_val_vec_new(&args78, 1, (wasm_val_t []){ {.kind = WASM_F32, .of = {.f32 = 78.0}} }); + trap = wasm_func_call(set_var_f32_import, &args78, NULL); + check_trap(trap); + check_global(var_f32_export, f32, 78.0); + check_call(get_var_f32_export, f32, 78.0); //Call to module export Failed here, still 77 + check_call(get_var_f32_import, f32, 78.0); //Call to module import + + + // wasm_extern_vec_delete(&exports_of_export); + //wasm_instance_delete(instance_export); + wasm_extern_vec_delete(&exports_of_import); + //wasm_instance_delete(instance_import); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; + +} diff --git a/samples/wasm-c-api/src/hello.c b/samples/wasm-c-api/src/hello.c new file mode 100644 index 0000000000..6650f3485d --- /dev/null +++ b/samples/wasm-c-api/src/hello.c @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* hello_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + printf("> Hello World!\n"); + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("hello.aot", "rb"); +#else + FILE* file = fopen("hello.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + if (!wasm_module_validate(store, &binary)) { + printf("> Error validate module!\n"); + wasm_byte_vec_delete(&binary); + return 1; + } + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + assert(wasm_module_set_name(module, "hello")); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* hello_type = wasm_functype_new_0_0(); + own wasm_func_t* hello_func = + wasm_func_new(store, hello_type, hello_callback); + + wasm_functype_delete(hello_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(hello_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(hello_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + { + const char* name = wasm_module_get_name(module); + assert(strncmp(name, "hello", 5) == 0); + printf("> removing module %s \n", name); + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(run_func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/hello.wat b/samples/wasm-c-api/src/hello.wat new file mode 100644 index 0000000000..1c56c55822 --- /dev/null +++ b/samples/wasm-c-api/src/hello.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/samples/wasm-c-api/src/hostref.c b/samples/wasm-c-api/src/hostref.c new file mode 100644 index 0000000000..76f53a956c --- /dev/null +++ b/samples/wasm-c-api/src/hostref.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + + +// A function to be called from Wasm code. +own wasm_trap_t* callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n> "); + printf("> %p\n", + args->data[0].of.ref ? wasm_ref_get_host_info(args->data[0].of.ref) : NULL); + wasm_val_copy(&results->data[0], &args->data[0]); + return NULL; +} + + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + +wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) { + printf("> Error accessing global export %zu!\n", i); + exit(1); + } + return wasm_extern_as_global(exports->data[i]); +} + +wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_table(exports->data[i])) { + printf("> Error accessing table export %zu!\n", i); + exit(1); + } + return wasm_extern_as_table(exports->data[i]); +} + + +own wasm_ref_t* call_v_r(const wasm_func_t* func) { + printf("call_v_r... "); fflush(stdout); + wasm_val_t rs[] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + exit(1); + } + printf("okay\n"); + return rs[0].of.ref; +} + +void call_r_v(const wasm_func_t* func, wasm_ref_t* ref) { + printf("call_r_v... "); fflush(stdout); + wasm_val_t vs[1] = { WASM_REF_VAL(ref) }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + exit(1); + } + printf("okay\n"); +} + +own wasm_ref_t* call_r_r(const wasm_func_t* func, wasm_ref_t* ref) { + printf("call_r_r... "); fflush(stdout); + wasm_val_t vs[1] = { WASM_REF_VAL(ref) }; + wasm_val_t rs[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + exit(1); + } + printf("okay\n"); + return rs[0].of.ref; +} + +void call_ir_v(const wasm_func_t* func, int32_t i, wasm_ref_t* ref) { + printf("call_ir_v... "); fflush(stdout); + wasm_val_t vs[2] = { WASM_I32_VAL(i), WASM_REF_VAL(ref) }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + exit(1); + } + printf("okay\n"); +} + +own wasm_ref_t* call_i_r(const wasm_func_t* func, int32_t i) { + printf("call_i_r... "); fflush(stdout); + wasm_val_t vs[1] = { WASM_I32_VAL(i) }; + wasm_val_t rs[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + exit(1); + } + printf("okay\n"); + return rs[0].of.ref; +} + +void check(own wasm_ref_t* actual, const wasm_ref_t* expected) { + if (actual != expected && + !(actual && expected && wasm_ref_same(actual, expected))) { + printf("> Error reading reference, expected %p, got %p\n", + expected ? wasm_ref_get_host_info(expected) : NULL, + actual ? wasm_ref_get_host_info(actual) : NULL); + exit(1); + } + // if (actual) wasm_ref_delete(actual); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("hostref.aot", "rb"); +#else + FILE* file = fopen("hostref.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external callback function. + printf("Creating callback...\n"); + own wasm_functype_t* callback_type = wasm_functype_new_1_1( + wasm_valtype_new(WASM_EXTERNREF), wasm_valtype_new(WASM_EXTERNREF)); + own wasm_func_t* callback_func = + wasm_func_new(store, callback_type, callback); + + wasm_functype_delete(callback_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(callback_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(callback_func); + wasm_module_delete(module); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_global_t* global = get_export_global(&exports, i++); + wasm_table_t* table = get_export_table(&exports, i++); + wasm_func_t* global_set = get_export_func(&exports, i++); + wasm_func_t* global_get = get_export_func(&exports, i++); + wasm_func_t* table_set = get_export_func(&exports, i++); + wasm_func_t* table_get = get_export_func(&exports, i++); + wasm_func_t* func_call = get_export_func(&exports, i++); + + wasm_instance_delete(instance); + + // Create host references. + printf("Creating host references...\n"); + own wasm_ref_t* host1 = wasm_foreign_as_ref(wasm_foreign_new(store)); + own wasm_ref_t* host2 = wasm_foreign_as_ref(wasm_foreign_new(store)); + wasm_ref_set_host_info(host1, (void*)1); + wasm_ref_set_host_info(host2, (void*)2); + + // Some sanity checks. + check(NULL, NULL); + wasm_ref_t *host1_cp = wasm_ref_copy(host1); + wasm_ref_t *host2_cp = wasm_ref_copy(host2); + check(host1_cp, host1); + check(host2_cp, host2); + wasm_ref_delete(host1_cp); + wasm_ref_delete(host2_cp); + + own wasm_val_t val; + val.kind = WASM_EXTERNREF; + val.of.ref = wasm_ref_copy(host1); + wasm_ref_t *ref_cp = wasm_ref_copy(val.of.ref); + check(ref_cp, host1); + check(val.of.ref, host1); + wasm_ref_delete(val.of.ref); + wasm_ref_delete(ref_cp); + + // Interact. + printf("Accessing global...\n"); + check(call_v_r(global_get), NULL); + call_r_v(global_set, host1); + check(call_v_r(global_get), host1); + call_r_v(global_set, host2); + check(call_v_r(global_get), host2); + call_r_v(global_set, NULL); + check(call_v_r(global_get), NULL); + + wasm_global_get(global, &val); + assert(val.kind == WASM_EXTERNREF); + assert(val.of.ref == NULL); + val.of.ref = host2; + wasm_global_set(global, &val); + wasm_global_get(global, &val); + assert(val.kind == WASM_EXTERNREF); + assert(val.of.ref == host2); + + printf("Accessing table...\n"); + check(call_i_r(table_get, 0), NULL); + check(call_i_r(table_get, 1), NULL); + call_ir_v(table_set, 0, host1); + call_ir_v(table_set, 1, host2); + check(call_i_r(table_get, 0), host1); + check(call_i_r(table_get, 1), host2); + call_ir_v(table_set, 0, NULL); + check(call_i_r(table_get, 0), NULL); + + check(wasm_table_get(table, 2), NULL); + wasm_table_set(table, 2, host1); + check(call_i_r(table_get, 2), host1); + check(wasm_table_get(table, 2), host1); + + printf("Accessing function...\n"); + check(call_r_r(func_call, NULL), NULL); + check(call_r_r(func_call, host1), host1); + check(call_r_r(func_call, host2), host2); + + wasm_ref_delete(host1); + wasm_ref_delete(host2); + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/hostref.wat b/samples/wasm-c-api/src/hostref.wat new file mode 100644 index 0000000000..9ed43dbcb2 --- /dev/null +++ b/samples/wasm-c-api/src/hostref.wat @@ -0,0 +1,24 @@ +(module + (import "" "f" (func $fun (param externref) (result externref))) + + (global $glob (export "global") (mut externref) (ref.null extern)) + (table $tab (export "table") 10 externref) + + (func (export "global.set") (param $r externref) + (global.set $glob (local.get $r)) + ) + (func (export "global.get") (result externref) + (global.get $glob) + ) + + (func (export "table.set") (param $i i32) (param $r externref) + (table.set $tab (local.get $i) (local.get $r)) + ) + (func (export "table.get") (param $i i32) (result externref) + (table.get $tab (local.get $i)) + ) + + (func (export "func.call") (param $r externref) (result externref) + (call $fun (local.get $r)) + ) +) diff --git a/samples/wasm-c-api/src/memory.c b/samples/wasm-c-api/src/memory.c new file mode 100644 index 0000000000..737dd8501d --- /dev/null +++ b/samples/wasm-c-api/src/memory.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + + +wasm_memory_t* get_export_memory(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_memory(exports->data[i])) { + printf("> Error accessing memory export %zu!\n", i); + exit(1); + } + return wasm_extern_as_memory(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +void check(bool success) { + if (!success) { + printf("> Error, expected success\n"); + exit(1); + } +} + +void check_call(wasm_func_t* func, int i, wasm_val_t args[], int32_t expected) { + wasm_val_t r[] = {WASM_INIT_VAL}; + wasm_val_vec_t args_ = {i, args, i, sizeof(wasm_val_t), NULL}; + wasm_val_vec_t results = WASM_ARRAY_VEC(r); + wasm_trap_t *trap = wasm_func_call(func, &args_, &results); + if (trap) { + printf("> Error on result\n"); + wasm_trap_delete(trap); + exit(1); + } + + if (r[0].of.i32 != expected) { + printf("> Error on result\n"); + exit(1); + } +} + +void check_call0(wasm_func_t* func, int32_t expected) { + check_call(func, 0, NULL, expected); +} + +void check_call1(wasm_func_t* func, int32_t arg, int32_t expected) { + wasm_val_t args[] = { WASM_I32_VAL(arg) }; + check_call(func, 1, args, expected); +} + +void check_call2(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) { + wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + check_call(func, 2, args, expected); +} + +void check_ok(wasm_func_t* func, int i, wasm_val_t args[]) { + wasm_val_vec_t args_ = {i, args, i, sizeof(wasm_val_t), NULL}; + wasm_val_vec_t results = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(func, &args_, &results); + if (trap) { + printf("> Error on result, expected empty\n"); + wasm_trap_delete(trap); + exit(1); + } +} + +void check_ok2(wasm_func_t* func, int32_t arg1, int32_t arg2) { + wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + check_ok(func, 2, args); +} + +void check_trap(wasm_func_t* func, int i, wasm_val_t args[]) { + wasm_val_t r[] = {WASM_INIT_VAL}; + wasm_val_vec_t args_ = {i, args, i, sizeof(wasm_val_t), NULL}; + wasm_val_vec_t results = WASM_ARRAY_VEC(r); + own wasm_trap_t* trap = wasm_func_call(func, &args_, &results); + if (! trap) { + printf("> Error on result, expected trap\n"); + exit(1); + } + wasm_trap_delete(trap); +} + +void check_trap1(wasm_func_t* func, int32_t arg) { + wasm_val_t args[] = { WASM_I32_VAL(arg) }; + check_trap(func, 1, args); +} + +void check_trap2(wasm_func_t* func, int32_t arg1, int32_t arg2) { + wasm_val_t args[] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + check_trap(func, 2, args); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("memory.aot", "rb"); +#else + FILE* file = fopen("memory.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t *instance = wasm_instance_new_with_args( + store, module, &imports, NULL, KILOBYTE(32), 0); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_memory_t* memory = get_export_memory(&exports, i++); + wasm_func_t* size_func = get_export_func(&exports, i++); + wasm_func_t* load_func = get_export_func(&exports, i++); + wasm_func_t* store_func = get_export_func(&exports, i++); + + wasm_module_delete(module); + + // Try cloning. + own wasm_memory_t* copy = wasm_memory_copy(memory); + assert(wasm_memory_same(memory, copy)); + wasm_memory_delete(copy); + + // Check initial memory. + printf("Checking memory...\n"); + check(wasm_memory_size(memory) == 2); + check(wasm_memory_data_size(memory) == 0x20000); + check(wasm_memory_data(memory)[0] == 0); + check(wasm_memory_data(memory)[0x1000] == 1); + check(wasm_memory_data(memory)[0x1003] == 4); + + check_call0(size_func, 2); + check_call1(load_func, 0, 0); + check_call1(load_func, 0x1000, 1); + check_call1(load_func, 0x1003, 4); + check_call1(load_func, 0x1ffff, 0); + check_trap1(load_func, 0x20000); + + // Mutate memory. + printf("Mutating memory...\n"); + wasm_memory_data(memory)[0x1003] = 5; + check_ok2(store_func, 0x1002, 6); + check_trap2(store_func, 0x20000, 0); + + check(wasm_memory_data(memory)[0x1002] == 6); + check(wasm_memory_data(memory)[0x1003] == 5); + check_call1(load_func, 0x1002, 6); + check_call1(load_func, 0x1003, 5); + + // Grow memory. + // DO NOT SUPPORT + printf("Bypass Growing memory...\n"); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Create stand-alone memory. + // DO NOT SUPPORT + // TODO(wasm+): Once Wasm allows multiple memories, turn this into import. + printf("Bypass Creating stand-alone memory...\n"); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/memory.wat b/samples/wasm-c-api/src/memory.wat new file mode 100644 index 0000000000..4cf43e2c7d --- /dev/null +++ b/samples/wasm-c-api/src/memory.wat @@ -0,0 +1,11 @@ +(module + (memory (export "memory") 2 3) + + (func (export "size") (result i32) (memory.size)) + (func (export "load") (param i32) (result i32) (i32.load8_s (local.get 0))) + (func (export "store") (param i32 i32) + (i32.store8 (local.get 0) (local.get 1)) + ) + + (data (i32.const 0x1000) "\01\02\03\04") +) diff --git a/samples/wasm-c-api/src/multi.c b/samples/wasm-c-api/src/multi.c new file mode 100644 index 0000000000..a1c8fa9fc9 --- /dev/null +++ b/samples/wasm-c-api/src/multi.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n> "); + printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n", + args->data[0].of.i32, args->data[1].of.i64, + args->data[2].of.i64, args->data[3].of.i32); + printf("\n"); + + wasm_val_copy(&results->data[0], &args->data[3]); + wasm_val_copy(&results->data[1], &args->data[1]); + wasm_val_copy(&results->data[2], &args->data[2]); + wasm_val_copy(&results->data[3], &args->data[0]); + return NULL; +} + + +// A function closure. +own wasm_trap_t* closure_callback( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + int i = *(int*)env; + printf("Calling back closure...\n"); + printf("> %d\n", i); + + results->data[0].kind = WASM_I32; + results->data[0].of.i32 = (int32_t)i; + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("multi.aot", "rb"); +#else + FILE* file = fopen("multi.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + wasm_valtype_t* types[4] = { + wasm_valtype_new_i32(), wasm_valtype_new_i64(), + wasm_valtype_new_i64(), wasm_valtype_new_i32() + }; + own wasm_valtype_vec_t tuple1, tuple2; + wasm_valtype_vec_new(&tuple1, 4, types); + wasm_valtype_vec_copy(&tuple2, &tuple1); + own wasm_functype_t* callback_type = wasm_functype_new(&tuple1, &tuple2); + own wasm_func_t* callback_func = + wasm_func_new(store, callback_type, callback); + + wasm_functype_delete(callback_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(callback_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(callback_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t vals[4] = { + WASM_I32_VAL(1), WASM_I64_VAL(2), WASM_I64_VAL(3), WASM_I32_VAL(4) + }; + wasm_val_t res[4] = { + WASM_INIT_VAL, WASM_INIT_VAL, WASM_INIT_VAL, WASM_INIT_VAL + }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vals); + wasm_val_vec_t results = WASM_ARRAY_VEC(res); + wasm_trap_t *trap = wasm_func_call(run_func, &args, &results); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Print result. + printf("Printing result...\n"); + printf("> %"PRIu32" %"PRIu64" %"PRIu64" %"PRIu32"\n", + res[0].of.i32, res[1].of.i64, res[2].of.i64, res[3].of.i32); + + assert(res[0].of.i32 == 4); + assert(res[1].of.i64 == 3); + assert(res[2].of.i64 == 2); + assert(res[3].of.i32 == 1); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/multi.wat b/samples/wasm-c-api/src/multi.wat new file mode 100644 index 0000000000..e7fb331125 --- /dev/null +++ b/samples/wasm-c-api/src/multi.wat @@ -0,0 +1,7 @@ +(module + (func $f (import "" "f") (param i32 i64 i64 i32) (result i32 i64 i64 i32)) + + (func $g (export "g") (param i32 i64 i64 i32) (result i32 i64 i64 i32) + (call $f (local.get 0) (local.get 2) (local.get 1) (local.get 3)) + ) +) diff --git a/samples/wasm-c-api/src/reflect.c b/samples/wasm-c-api/src/reflect.c new file mode 100644 index 0000000000..e5a9b3fd9e --- /dev/null +++ b/samples/wasm-c-api/src/reflect.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +void print_mutability(wasm_mutability_t mut) { + switch (mut) { + case WASM_VAR: printf("var"); break; + case WASM_CONST: printf("const"); break; + } +} + +void print_limits(const wasm_limits_t* limits) { + if (!limits) { + printf("unknown limits"); + return; + } + printf("%ud", limits->min); + if (limits->max < wasm_limits_max_default) printf(" %ud", limits->max); +} + +void print_valtype(const wasm_valtype_t* type) { + switch (wasm_valtype_kind(type)) { + case WASM_I32: printf("i32"); break; + case WASM_I64: printf("i64"); break; + case WASM_F32: printf("f32"); break; + case WASM_F64: printf("f64"); break; + case WASM_V128: printf("v128"); break; + case WASM_EXTERNREF: printf("externref"); break; + case WASM_FUNCREF: printf("funcref"); break; + } +} + +void print_valtypes(const wasm_valtype_vec_t* types) { + bool first = true; + for (size_t i = 0; i < types->size; ++i) { + if (first) { + first = false; + } else { + printf(" "); + } + print_valtype(types->data[i]); + } +} + +void print_externtype(const wasm_externtype_t* type) { + if (!type) { + printf("unknown extern type"); + return; + } + switch (wasm_externtype_kind(type)) { + case WASM_EXTERN_FUNC: { + const wasm_functype_t* functype = + wasm_externtype_as_functype_const(type); + printf("func "); + print_valtypes(wasm_functype_params(functype)); + printf(" -> "); + print_valtypes(wasm_functype_results(functype)); + } break; + case WASM_EXTERN_GLOBAL: { + const wasm_globaltype_t* globaltype = + wasm_externtype_as_globaltype_const(type); + printf("global "); + print_mutability(wasm_globaltype_mutability(globaltype)); + printf(" "); + print_valtype(wasm_globaltype_content(globaltype)); + } break; + case WASM_EXTERN_TABLE: { + const wasm_tabletype_t* tabletype = + wasm_externtype_as_tabletype_const(type); + printf("table "); + print_limits(wasm_tabletype_limits(tabletype)); + printf(" "); + print_valtype(wasm_tabletype_element(tabletype)); + } break; + case WASM_EXTERN_MEMORY: { + const wasm_memorytype_t* memorytype = + wasm_externtype_as_memorytype_const(type); + printf("memory "); + print_limits(wasm_memorytype_limits(memorytype)); + } break; + } +} + +void print_name(const wasm_name_t* name) { + if (!name) { + printf("unknown name"); + return; + } + printf("\"%.*s\"", (int)name->size, name->data); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("reflect.aot", "rb"); +#else + FILE* file = fopen("reflect.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Extract export. + printf("Extracting export...\n"); + own wasm_exporttype_vec_t export_types; + own wasm_extern_vec_t exports; + wasm_module_exports(module, &export_types); + wasm_instance_exports(instance, &exports); + assert(exports.size == export_types.size); + + for (size_t i = 0; i < exports.size; ++i) { + assert(wasm_extern_kind(exports.data[i]) == + wasm_externtype_kind(wasm_exporttype_type(export_types.data[i]))); + printf("> export %zu ", i); + print_name(wasm_exporttype_name(export_types.data[i])); + printf("\n"); + printf(">> initial: "); + print_externtype(wasm_exporttype_type(export_types.data[i])); + printf("\n"); + printf(">> current: "); + own wasm_externtype_t* current = wasm_extern_type(exports.data[i]); + print_externtype(current); + wasm_externtype_delete(current); + printf("\n"); + if (wasm_extern_kind(exports.data[i]) == WASM_EXTERN_FUNC) { + wasm_func_t* func = wasm_extern_as_func(exports.data[i]); + printf(">> in-arity: %zu", wasm_func_param_arity(func)); + printf(", out-arity: %zu\n", wasm_func_result_arity(func)); + } + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + wasm_extern_vec_delete(&exports); + wasm_exporttype_vec_delete(&export_types); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/reflect.wat b/samples/wasm-c-api/src/reflect.wat new file mode 100644 index 0000000000..7a80e86dde --- /dev/null +++ b/samples/wasm-c-api/src/reflect.wat @@ -0,0 +1,6 @@ +(module + (func (export "func") (param i32 f64 f32) (result i32) (unreachable)) + (global (export "global") f64 (f64.const 0)) + (table (export "table") 0 50 funcref) + (memory (export "memory") 1) +) diff --git a/samples/wasm-c-api/src/serialize.c b/samples/wasm-c-api/src/serialize.c new file mode 100644 index 0000000000..83436817f2 --- /dev/null +++ b/samples/wasm-c-api/src/serialize.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t * +hello_callback(const wasm_val_vec_t *args, wasm_val_vec_t *results) +{ + printf("Calling back...\n"); + printf("> Hello World!\n"); + return NULL; +} + +int +main(int argc, const char *argv[]) +{ + // Initialize. + printf("Initializing...\n"); + wasm_engine_t *engine = wasm_engine_new(); + wasm_store_t *store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); + FILE *file = fopen("serialize.wasm", "rb"); + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + 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"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t *module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Serialize module. + printf("Serializing module...\n"); + own wasm_byte_vec_t serialized; + wasm_module_serialize(module, &serialized); + + wasm_module_delete(module); + + // Deserialize module. + printf("Deserializing module...\n"); + own wasm_module_t *deserialized = + wasm_module_deserialize(store, &serialized); + if (!deserialized) { + printf("> Error deserializing module!\n"); + return 1; + } + + wasm_byte_vec_delete(&serialized); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t *hello_type = wasm_functype_new_0_0(); + own wasm_func_t *hello_func = + wasm_func_new(store, hello_type, hello_callback); + + wasm_functype_delete(hello_type); + + // Instantiate. + printf("Instantiating deserialized module...\n"); + wasm_extern_t *externs[] = { wasm_func_as_extern(hello_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t *instance = + wasm_instance_new(store, deserialized, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(hello_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(deserialized); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_vec_t empty = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(run_func, &empty, &empty); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/serialize.wat b/samples/wasm-c-api/src/serialize.wat new file mode 100644 index 0000000000..1c56c55822 --- /dev/null +++ b/samples/wasm-c-api/src/serialize.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/samples/wasm-c-api/src/table.c b/samples/wasm-c-api/src/table.c new file mode 100644 index 0000000000..c4a7da8bcf --- /dev/null +++ b/samples/wasm-c-api/src/table.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* neg_callback( + const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + results->data[0].kind = WASM_I32; + results->data[0].of.i32 = -args->data[0].of.i32; + return NULL; +} + + +wasm_table_t* get_export_table(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_table(exports->data[i])) { + printf("> Error accessing table export %zu!\n", i); + exit(1); + } + return wasm_extern_as_table(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +void check(bool success) { + if (!success) { + printf("> Error, expected success\n"); + exit(1); + } +} + +void check_table(wasm_table_t* table, int32_t i, bool expect_set) { + own wasm_ref_t* ref = wasm_table_get(table, i); + check((ref != NULL) == expect_set); + if (ref) wasm_ref_delete(ref); +} + +void check_call(wasm_func_t* func, int32_t arg1, int32_t arg2, int32_t expected) { + wasm_val_t vs[2] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + wasm_val_t r[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(r); + wasm_trap_t *trap = wasm_func_call(func, &args, &results); + if (trap) { + printf("> Error on calling\n"); + wasm_trap_delete(trap); + exit(1); + } + + if (r[0].of.i32 != expected) { + printf("> Error on result\n"); + exit(1); + } +} + +void check_trap(wasm_func_t* func, int32_t arg1, int32_t arg2) { + wasm_val_t vs[2] = { WASM_I32_VAL(arg1), WASM_I32_VAL(arg2) }; + wasm_val_t r[1] = { WASM_INIT_VAL }; + wasm_val_vec_t args = WASM_ARRAY_VEC(vs); + wasm_val_vec_t results = WASM_ARRAY_VEC(r); + own wasm_trap_t* trap = wasm_func_call(func, &args, &results); + if (! trap) { + printf("> Error on result, expected trap\n"); + exit(1); + } + wasm_trap_delete(trap); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("table.aot", "rb"); +#else + FILE* file = fopen("table.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = WASM_EMPTY_VEC; + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_table_t* table = get_export_table(&exports, i++); + wasm_func_t* call_indirect = get_export_func(&exports, i++); + wasm_func_t* f = get_export_func(&exports, i++); + wasm_func_t* g = get_export_func(&exports, i++); + + wasm_module_delete(module); + + // Create external function. + printf("Creating callback...\n"); + own wasm_functype_t* neg_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32()); + own wasm_func_t* h = wasm_func_new(store, neg_type, neg_callback); + + wasm_functype_delete(neg_type); + + // Try cloning. + own wasm_table_t* copy = wasm_table_copy(table); + assert(wasm_table_same(table, copy)); + wasm_table_delete(copy); + + // Check initial table. + printf("Checking table...\n"); + check(wasm_table_size(table) == 2); + check_table(table, 0, false); + check_table(table, 1, true); + check_trap(call_indirect, 0, 0); + check_call(call_indirect, 7, 1, 7); + check_trap(call_indirect, 0, 2); + + // Mutate table. + printf("Mutating table...\n"); + check(wasm_table_set(table, 0, wasm_func_as_ref(g))); + check(wasm_table_set(table, 1, NULL)); + wasm_ref_t *ref_f = wasm_func_as_ref(f); + check(! wasm_table_set(table, 2, ref_f)); + wasm_ref_delete(ref_f); + check_table(table, 0, true); + check_table(table, 1, false); + check_call(call_indirect, 7, 0, 666); + check_trap(call_indirect, 0, 1); + check_trap(call_indirect, 0, 2); + + // Grow table. + // DO NOT SUPPORT + printf("Bypass Growing table...\n"); + + wasm_func_delete(h); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Create stand-alone table. + // DO NOT SUPPORT + // TODO(wasm+): Once Wasm allows multiple tables, turn this into import. + printf("Bypass Creating stand-alone table...\n"); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/table.wat b/samples/wasm-c-api/src/table.wat new file mode 100644 index 0000000000..d3e3a945aa --- /dev/null +++ b/samples/wasm-c-api/src/table.wat @@ -0,0 +1,12 @@ +(module + (table (export "table") 2 10 funcref) + + (func (export "call_indirect") (param i32 i32) (result i32) + (call_indirect (param i32) (result i32) (local.get 0) (local.get 1)) + ) + + (func $f (export "f") (param i32) (result i32) (local.get 0)) + (func (export "g") (param i32) (result i32) (i32.const 666)) + + (elem (i32.const 1) $f) +) diff --git a/samples/wasm-c-api/src/threads.c b/samples/wasm-c-api/src/threads.c new file mode 100644 index 0000000000..cbe4aaf444 --- /dev/null +++ b/samples/wasm-c-api/src/threads.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +const int N_THREADS = 10; +const int N_REPS = 3; + +// A function to be called from Wasm code. +own wasm_trap_t * +callback(const wasm_val_vec_t *args, wasm_val_vec_t *results) +{ + assert(args->data[0].kind == WASM_I32); + printf("> Thread %d running\n", args->data[0].of.i32); + return NULL; +} + +typedef struct { + wasm_engine_t *engine; + wasm_shared_module_t *module; + int id; +} thread_args; + +void * +run(void *args_abs) +{ + thread_args *args = (thread_args *)args_abs; + + // Rereate store and module. + own wasm_store_t *store = wasm_store_new(args->engine); + own wasm_module_t *module = wasm_module_obtain(store, args->module); + + // Run the example N times. + for (int i = 0; i < N_REPS; ++i) { + usleep(100000); + + // Create imports. + own wasm_functype_t *func_type = + wasm_functype_new_1_0(wasm_valtype_new_i32()); + own wasm_func_t *func = wasm_func_new(store, func_type, callback); + wasm_functype_delete(func_type); + + wasm_val_t val = WASM_I32_VAL((int32_t)args->id); + own wasm_globaltype_t *global_type = + wasm_globaltype_new(wasm_valtype_new_i32(), WASM_CONST); + own wasm_global_t *global = wasm_global_new(store, global_type, &val); + wasm_globaltype_delete(global_type); + + // Instantiate. + wasm_extern_t *externs[] = { + wasm_func_as_extern(func), + wasm_global_as_extern(global), + }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t *instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return NULL; + } + + wasm_func_delete(func); + wasm_global_delete(global); + + // Extract export. + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return NULL; + } + const wasm_func_t *run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return NULL; + } + + wasm_instance_delete(instance); + + // Call. + wasm_val_vec_t empty = WASM_EMPTY_VEC; + wasm_trap_t *trap = wasm_func_call(run_func, &empty, &empty); + if (trap) { + printf("> Error calling function!\n"); + wasm_trap_delete(trap); + return NULL; + } + + wasm_extern_vec_delete(&exports); + } + + wasm_module_delete(module); + wasm_store_delete(store); + + free(args_abs); + + return NULL; +} + +int +main(int argc, const char *argv[]) +{ + // Initialize. + wasm_engine_t *engine = wasm_engine_new(); + + // Load binary. +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE *file = fopen("threads.aot", "rb"); +#else + FILE *file = fopen("threads.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + return 1; + } + fclose(file); + + // Compile and share. + own wasm_store_t *store = wasm_store_new(engine); + own wasm_module_t *module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + own wasm_shared_module_t *shared = wasm_module_share(module); + + wasm_module_delete(module); + wasm_store_delete(store); + + // Spawn threads. + pthread_t threads[N_THREADS]; + for (int i = 0; i < N_THREADS; i++) { + thread_args *args = malloc(sizeof(thread_args)); + args->id = i; + args->engine = engine; + args->module = shared; + printf("Initializing thread %d...\n", i); + pthread_create(&threads[i], NULL, &run, args); + } + + for (int i = 0; i < N_THREADS; i++) { + printf("Waiting for thread: %d\n", i); + pthread_join(threads[i], NULL); + } + + wasm_shared_module_delete(shared); + wasm_engine_delete(engine); + + return 0; +} diff --git a/samples/wasm-c-api/src/threads.wat b/samples/wasm-c-api/src/threads.wat new file mode 100644 index 0000000000..29a3bbcc1a --- /dev/null +++ b/samples/wasm-c-api/src/threads.wat @@ -0,0 +1,5 @@ +(module + (func $message (import "" "hello") (param i32)) + (global $id (import "" "id") i32) + (func (export "run") (call $message (global.get $id))) +) diff --git a/samples/wasm-c-api/src/trap.c b/samples/wasm-c-api/src/trap.c new file mode 100644 index 0000000000..2637b73469 --- /dev/null +++ b/samples/wasm-c-api/src/trap.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* fail_callback( + void* env, const wasm_val_vec_t* args, wasm_val_vec_t* results +) { + printf("Calling back...\n"); + own wasm_name_t message; + wasm_name_new_from_string_nt(&message, "callback abort"); + own wasm_trap_t* trap = wasm_trap_new((wasm_store_t*)env, &message); + wasm_name_delete(&message); + return trap; +} + + +void print_frame(wasm_frame_t* frame) { + printf("> %p @ 0x%zx = %"PRIu32".0x%zx\n", + wasm_frame_instance(frame), + wasm_frame_module_offset(frame), + wasm_frame_func_index(frame), + wasm_frame_func_offset(frame) + ); +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("trap.aot", "rb"); +#else + FILE* file = fopen("trap.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + fclose(file); + return 1; + } + + 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"); + fclose(file); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* fail_type = + wasm_functype_new_0_1(wasm_valtype_new_i32()); + own wasm_func_t* fail_func = + wasm_func_new_with_env(store, fail_type, fail_callback, store, NULL); + + wasm_functype_delete(fail_type); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_t* externs[] = { wasm_func_as_extern(fail_func) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + own wasm_instance_t* instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(fail_func); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size < 2) { + printf("> Error accessing exports!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + for (int i = 0; i < 2; ++i) { + const wasm_func_t* func = wasm_extern_as_func(exports.data[i]); + if (func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + printf("Calling export %d...\n", i); + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_EMPTY_VEC; + own wasm_trap_t* trap = wasm_func_call(func, &args, &results); + if (!trap) { + printf("> Error calling function, expected trap!\n"); + return 1; + } + + printf("Printing message...\n"); + own wasm_name_t message; + wasm_trap_message(trap, &message); + printf("> %s\n", message.data); + assert(message.num_elems > 0); + assert(strncmp(message.data, "Exception: ", strlen("Exception: ")) == 0); + + printf("Printing origin...\n"); + own wasm_frame_t* frame = wasm_trap_origin(trap); + if (frame) { + print_frame(frame); + wasm_frame_delete(frame); + } else { + printf("> Empty origin.\n"); + } + + printf("Printing trace...\n"); + own wasm_frame_vec_t trace; + wasm_trap_trace(trap, &trace); + if (trace.size > 0) { + for (size_t i = 0; i < trace.size; ++i) { + print_frame(trace.data[i]); + } + } else { + printf("> Empty trace.\n"); + } + + wasm_frame_vec_delete(&trace); + wasm_trap_delete(trap); + wasm_name_delete(&message); + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/samples/wasm-c-api/src/trap.wat b/samples/wasm-c-api/src/trap.wat new file mode 100644 index 0000000000..dfd20fb12a --- /dev/null +++ b/samples/wasm-c-api/src/trap.wat @@ -0,0 +1,5 @@ +(module + (func $callback (import "" "callback") (result i32)) + (func (export "callback") (result i32) (call $callback)) + (func (export "unreachable") (result i32) (unreachable) (i32.const 1)) +) diff --git a/samples/wasm-c-api/src/utils/multi_module_utils.c b/samples/wasm-c-api/src/utils/multi_module_utils.c new file mode 100644 index 0000000000..67010914b0 --- /dev/null +++ b/samples/wasm-c-api/src/utils/multi_module_utils.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_read_file.h" +#include "wasm_export.h" + +static char * +build_module_path(const char *module_name) +{ + const char *module_search_path = "."; + const char *format = "%s/%s.wasm"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(".wasm") + 1; + char *wasm_file_name = BH_MALLOC(sz); + if (!wasm_file_name) { + return NULL; + } + + snprintf(wasm_file_name, sz, format, module_search_path, module_name); + return wasm_file_name; +} + +bool +reader(const char *module_name, uint8 **p_buffer, uint32 *p_size) +{ + char *wasm_file_path = build_module_path(module_name); + if (!wasm_file_path) { + return false; + } + + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size); + BH_FREE(wasm_file_path); + return *p_buffer != NULL; +} + +void +destroyer(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + BH_FREE(buffer); + buffer = NULL; +} diff --git a/samples/workload/CMakeLists.txt b/samples/workload/CMakeLists.txt new file mode 100644 index 0000000000..667f0b4e84 --- /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 new file mode 100644 index 0000000000..8f9371723e --- /dev/null +++ b/samples/workload/README.md @@ -0,0 +1,37 @@ +--- +description: "The related code/working directory of this example resides in directory {WAMR_DIR}/samples/workload" +--- +All workloads have similar requirement of software dependencies, including **emsdk** and **binaryen** + +> 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. Or use [*vscode DevContainer*](../../.devcontainer/) + +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 3.0.0 + +``` bash +$ cd /opt +$ git clone https://github.com/emscripten-core/emsdk.git +$ cd emsdk +$ git pull +$ ./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_111/binaryen-version_111-x86_64-linux.tar.gz) + to */opt/binaryen* + +``` bash +$ wget https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VER}/${BINARYEN_FILE} +$ tar zxf ${BINARYEN_FILE} -C /opt +$ ln -sf /opt/binaryen-${BINARYEN_VER} /opt/binaryen +``` diff --git a/samples/workload/XNNPACK/.gitignore b/samples/workload/XNNPACK/.gitignore new file mode 100644 index 0000000000..09f3fe927b --- /dev/null +++ b/samples/workload/XNNPACK/.gitignore @@ -0,0 +1,2 @@ +xnnpack +build diff --git a/samples/workload/XNNPACK/CMakeLists.txt b/samples/workload/XNNPACK/CMakeLists.txt new file mode 100644 index 0000000000..41cdd583fb --- /dev/null +++ b/samples/workload/XNNPACK/CMakeLists.txt @@ -0,0 +1,198 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(xnnpack_wasm) + +################ EMCC ################ +include(ExternalProject) + +# grep xnnpack_benchmark -A 1 BUILD.bazel \ +# | grep "name =" \ +# | awk '{print $3}' \ +# | sed -e 's/\"//g; s/,//g; s/^/\"/g; s/$/\"/g' +list(APPEND NATIVE_BENCHMARKS + "qs8_dwconv_bench" + "qs8_f32_vcvt_bench" + "qs8_gemm_bench" + "qs8_requantization_bench" + "qs8_vadd_bench" + "qs8_vaddc_bench" + "qs8_vcvt_bench" + "qs16_qs8_vcvt_bench" + "qs8_vlrelu_bench" + "qs8_vmul_bench" + "qs8_vmulc_bench" + "qu8_f32_vcvt_bench" + "qu8_gemm_bench" + "qu8_requantization_bench" + "qu8_vadd_bench" + "qu8_vaddc_bench" + "qu8_vcvt_bench" + "qu8_vlrelu_bench" + "qu8_vmul_bench" + "qu8_vmulc_bench" + "bf16_gemm_bench" + "f16_f32acc_igemm_bench" + "f16_igemm_bench" + "f16_f32acc_gemm_bench" + "f16_gemm_bench" + "f16_raddstoreexpminusmax_bench" + "f16_spmm_bench" + "f16_vsigmoid_bench" + "f16_vtanh_bench" + "f16_f32_vcvt_bench" + "f32_igemm_bench" + "f32_conv_hwc_bench" + "f16_conv_hwc2chw_bench" + "f16_gavgpool_cw_bench" + "f32_gavgpool_cw_bench" + "f32_conv_hwc2chw_bench" + "f16_dwconv_bench" + "f32_dwconv_bench" + "f32_dwconv2d_chw_bench" + "f16_dwconv2d_chw_bench" + "f32_f16_vcvt_bench" + "xx_transpose_bench" + "x8_transpose_bench" + "x16_transpose_bench" + "x24_transpose_bench" + "x32_transpose_bench" + "x64_transpose_bench" + "f32_bgemm_bench" + "f32_gemm_bench" + "f32_qs8_vcvt_bench" + "f32_qu8_vcvt_bench" + "f32_raddexpminusmax_bench" + "f32_raddextexp_bench" + "f32_raddstoreexpminusmax_bench" + "f32_rmax_bench" + "f32_spmm_bench" + "f32_softmax_bench" + "f16_velu_bench" + "f32_velu_bench" + "f32_vhswish_bench" + "f32_vlrelu_bench" + "f32_vrelu_bench" + "f32_vscaleexpminusmax_bench" + "f32_vscaleextexp_bench" + "f32_vsigmoid_bench" + "f16_vsqrt_bench" + "f32_vsqrt_bench" + "f32_vtanh_bench" + "f32_im2col_gemm_bench" + "rounding_bench" + "s16_rmaxabs_bench" + "s16_window_bench" + "u32_filterbank_accumulate_bench" + "u32_filterbank_subtract_bench" + "u32_vlog_bench" + "u64_u32_vsqrtshift_bench" + "i16_vlshift_bench" + "cs16_vsquareabs_bench" + "cs16_bfly4_bench" + "cs16_fftr_bench" + "x8_lut_bench" + "x32_packw_bench" + "x16_packw_bench" + "abs_bench" + "average_pooling_bench" + "bankers_rounding_bench" + "ceiling_bench" + "channel_shuffle_bench" + "convert_bench" + "convolution_bench" + "deconvolution_bench" + "elu_bench" + "floor_bench" + "global_average_pooling_bench" + "hardswish_bench" + "leaky_relu_bench" + "max_pooling_bench" + "negate_bench" + "prelu_bench" + "sigmoid_bench" + "softmax_bench" + "square_bench" + "square_root_bench" + "tanh_bench" + "truncation_bench" + "f16_dwconv_e2e_bench" + "f16_gemm_e2e_bench" + "f32_dwconv_e2e_bench" + "f32_gemm_e2e_bench" + "qs8_dwconv_e2e_bench" + "qs8_gemm_e2e_bench" + "qu8_gemm_e2e_bench" + "qu8_dwconv_e2e_bench" + "end2end_bench" + "f16_exp_ulp_eval" + "f16_expminus_ulp_eval" + "f16_expm1minus_ulp_eval" + "f16_sigmoid_ulp_eval" + "f16_sqrt_ulp_eval" + "f16_tanh_ulp_eval" + "f32_exp_ulp_eval" + "f32_expminus_ulp_eval" + "f32_expm1minus_ulp_eval" + "f32_extexp_ulp_eval" + "f32_sigmoid_ulp_eval" + "f32_sqrt_ulp_eval" + "f32_tanh_ulp_eval" +) + +# Only Download +ExternalProject_Add(xnnpack-download + PREFIX xnnpack + GIT_REPOSITORY https://github.com/google/XNNPACK.git + GIT_TAG b9d4073a6913891ce9cbd8965c8d506075d2a45a + GIT_PROGRESS ON + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack + UPDATE_COMMAND "" + PATCH_COMMAND git apply ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack.patch + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) + +set(WAMRC "${CMAKE_CURRENT_SOURCE_DIR}/../../../wamr-compiler/build/wamrc") +if(EXISTS ${WAMRC}) + message("-- Will generate .aot") +else() + message("Will generate .wasm") +endif() + +foreach(BENCHMARK IN LISTS NATIVE_BENCHMARKS) + string(CONCAT WASM_BENCHMARK "//:" ${BENCHMARK} "-wasm") + string(CONCAT WASM_OUTPUT ${BENCHMARK} ".wasm") + + add_custom_command( + OUTPUT ${WASM_OUTPUT} + COMMAND bazel --output_user_root=build-user-output build -c opt --config=wasm ${WASM_BENCHMARK} + && ${CMAKE_COMMAND} -E copy_if_different ./bazel-bin/${WASM_OUTPUT} ${CMAKE_CURRENT_BINARY_DIR}/${WASM_OUTPUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/xnnpack + DEPENDS xnnpack-download + COMMENT "Generating ${WASM_OUTPUT} ..." + ) + + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_CLEAN_FILES ${CMAKE_CURRENT_BINARY_DIR}/${WASM_OUTPUT}) + + if(EXISTS ${WAMRC}) + string(CONCAT AOT_OUTPUT ${BENCHMARK} ".aot") + + add_custom_command( + OUTPUT ${AOT_OUTPUT} + COMMAND ${WAMRC} -o ${AOT_OUTPUT} ${WASM_OUTPUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${WASM_OUTPUT} + COMMENT "Generating ${AOT_OUTPUT} ..." + ) + + add_custom_target(${BENCHMARK} ALL DEPENDS ${AOT_OUTPUT}) + else() + add_custom_target(${BENCHMARK} ALL DEPENDS ${WASM_OUTPUT}) + endif() +endforeach() + diff --git a/samples/workload/XNNPACK/README.md b/samples/workload/XNNPACK/README.md new file mode 100644 index 0000000000..625ef7f79d --- /dev/null +++ b/samples/workload/XNNPACK/README.md @@ -0,0 +1,49 @@ +"XNNPACK" sample introduction +============== + +This sample demonstrates how to build [XNNPACK](https://github.com/google/XNNPACK) benchmarks into WebAssembly with emsdk toolchain and run them with iwasm. + +## Installation toolchains + +please refer to [installation instructions](../README.md). + +## Build XNNPACK + +please build wamrc: + +``` bash +cd /wamr-compiler +./build_llvm.sh +mkdir build && cd build +cmake .. +make +``` + +And then build xnnpack standalone wasm files + +```bash +$ cd /samples/workload/XNNPACK +$ cmake -S . -B build +$ cmake --build build +``` + +Generated .wasm(and .aot) files are under *samples/workload/XNNPACK/build*. + +## Run benchmarks + +Firstly please build iwasm with simd, libc-emcc and lib-pthread supporting: + +``` bash +$ cd /product-mini/platforms/linux/ +$ mkdir build && cd build +$ cmake .. -DWAMR_BUILD_LIBC_EMCC=1 -DWAMR_BUILD_LIB_PTHREAD=1 +$ make +``` + +Then run: + +``` shell +$ cd /samples/workload/XNNPACK/build +$ iwasm average_pooling_bench.aot # (or other aot files) +``` + diff --git a/samples/workload/XNNPACK/benchmark.patch b/samples/workload/XNNPACK/benchmark.patch new file mode 100644 index 0000000000..713b476d26 --- /dev/null +++ b/samples/workload/XNNPACK/benchmark.patch @@ -0,0 +1,14 @@ +diff --git include/benchmark/benchmark.h include/benchmark/benchmark.h +index 9b54802..baa5938 100755 +--- include/benchmark/benchmark.h ++++ include/benchmark/benchmark.h +@@ -364,7 +364,9 @@ template + inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) { + internal::UseCharPointer(&reinterpret_cast(value)); + } ++ + // FIXME Add ClobberMemory() for non-gnu and non-msvc compilers ++inline BENCHMARK_ALWAYS_INLINE void ClobberMemory() { } + #endif + + // This class is used for user-defined counters. diff --git a/samples/workload/XNNPACK/xnnpack.patch b/samples/workload/XNNPACK/xnnpack.patch new file mode 100644 index 0000000000..d7680d3479 --- /dev/null +++ b/samples/workload/XNNPACK/xnnpack.patch @@ -0,0 +1,138 @@ +diff --git a/.bazelrc b/.bazelrc +index fcaff1063..e61d53337 100644 +--- a/.bazelrc ++++ b/.bazelrc +@@ -1,6 +1,7 @@ + # Basic build settings + build --jobs 128 + build --cxxopt='-std=gnu++14' ++build --incompatible_enable_cc_toolchain_resolution + + # Sets the default Apple platform to macOS. + build --apple_platform_type=macos +@@ -55,3 +56,10 @@ build:macos --apple_platform_type=macos + + build:macos_arm64 --config=macos + build:macos_arm64 --cpu=darwin_arm64 ++ ++# Emscripten configs ++build:wasm --copt="-Wno-unused" ++build:wasm --copt="-Wno-unused-function" ++build:wasm --copt="-Wno-unused-but-set-variable" ++build:wasm --cpu=wasm ++build:wasm --features=wasm_simd +diff --git a/WORKSPACE b/WORKSPACE +index 2e568088b..3961371ca 100644 +--- a/WORKSPACE ++++ b/WORKSPACE +@@ -83,7 +83,23 @@ http_archive( + ) + + # 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 SDK location and API is auto-detected from $ANDROID_HOME environment variable +-android_sdk_repository(name = "androidsdk") ++# android_sdk_repository(name = "androidsdk") ++ ++http_archive( ++ name = "emsdk", ++ sha256 = "5fa6f5eb45a4d50264610c4c9e1c155535359b63bfaad69b4e5101d16c1e7e32", ++ strip_prefix = "emsdk-a896e3d066448b3530dbcaa48869fafefd738f57/bazel", ++ url = "https://github.com/emscripten-core/emsdk/archive/a896e3d066448b3530dbcaa48869fafefd738f57.tar.gz", ++) ++ ++load("@emsdk//:deps.bzl", emsdk_deps = "deps") ++emsdk_deps() ++ ++load("@emsdk//:emscripten_deps.bzl", emsdk_emscripten_deps = "emscripten_deps") ++emsdk_emscripten_deps(emscripten_version = "3.1.44") ++ ++load("@emsdk//:toolchains.bzl", "register_emscripten_toolchains") ++register_emscripten_toolchains() +diff --git a/bench/utils.cc b/bench/utils.cc +index 3b32503a7..656845336 100644 +--- a/bench/utils.cc ++++ b/bench/utils.cc +@@ -456,3 +456,13 @@ CodeMemoryHelper::~CodeMemoryHelper() { + + } // namespace utils + } // namespace benchmark ++ ++ ++extern "C" ++__attribute__((import_module("env"), import_name("getentropy"))) int import_getentropy(void* buffer, size_t length); ++ ++extern "C" ++int getentropy(void* buffer, size_t length) ++{ ++ return import_getentropy(buffer, length); ++} +diff --git a/build_defs.bzl b/build_defs.bzl +index 01b436eb7..2738fd50a 100644 +--- a/build_defs.bzl ++++ b/build_defs.bzl +@@ -1,6 +1,7 @@ + """Build definitions and rules for XNNPACK.""" + +-load(":emscripten.bzl", "xnnpack_emscripten_benchmark_linkopts", "xnnpack_emscripten_deps", "xnnpack_emscripten_minimal_linkopts", "xnnpack_emscripten_test_linkopts") ++load(":emscripten.bzl", "xnnpack_emscripten_benchmark_linkopts", "xnnpack_emscripten_deps", "xnnpack_emscripten_minimal_linkopts", "xnnpack_emscripten_test_linkopts", "xnnpack_emscripten_standalone_benchmark_linkopts") ++load("@emsdk//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary") + + def xnnpack_visibility(): + """Visibility of :XNNPACK target. +@@ -393,7 +394,8 @@ def xnnpack_benchmark(name, srcs, copts = [], deps = [], tags = []): + "//conditions:default": ["-Wno-unused-function"], + }) + copts, + linkopts = select({ +- ":emscripten": xnnpack_emscripten_benchmark_linkopts(), ++ ":emscripten": xnnpack_emscripten_standalone_benchmark_linkopts(), ++ ":emscripten_wasmsimd": xnnpack_emscripten_standalone_benchmark_linkopts(), + ":windows_x86_64_mingw": ["-lshlwapi"], + ":windows_x86_64_msys": ["-lshlwapi"], + "//conditions:default": [], +@@ -405,5 +407,16 @@ def xnnpack_benchmark(name, srcs, copts = [], deps = [], tags = []): + ":emscripten": xnnpack_emscripten_deps(), + "//conditions:default": [], + }), +- tags = tags, ++ tags = tags, ++ ) ++ ++ wasm_cc_binary( ++ name = name + "-wasm", ++ cc_target = ":" + name, ++ threads = "off", ++ simd = True, ++ standalone= True, ++ outputs = [ ++ name + ".wasm", ++ ] + ) +diff --git a/emscripten.bzl b/emscripten.bzl +index f1557a7b1..a3c4f93b9 100644 +--- a/emscripten.bzl ++++ b/emscripten.bzl +@@ -33,6 +33,21 @@ def xnnpack_emscripten_benchmark_linkopts(): + "--pre-js $(location :preamble.js.lds)", + ] + ++def xnnpack_emscripten_standalone_benchmark_linkopts(): ++ return [ ++ "-s ASSERTIONS=1", ++ "-s ERROR_ON_UNDEFINED_SYMBOLS=0", ++ "-s ALLOW_MEMORY_GROWTH=1", ++ "-s TOTAL_MEMORY=536870912", # 512M ++ "-s USE_PTHREADS=0", ++ "-s STANDALONE_WASM=1", ++ "-Wl,--export=__heap_base", ++ "-Wl,--export=__data_end", ++ "-Wl,--export=malloc", ++ "-Wl,--export=free", ++ ] ++ ++ + def xnnpack_emscripten_deps(): + """Emscripten-specific dependencies for unit tests and benchmarks.""" + return [ diff --git a/samples/workload/bwa/.gitignore b/samples/workload/bwa/.gitignore new file mode 100644 index 0000000000..cd72095908 --- /dev/null +++ b/samples/workload/bwa/.gitignore @@ -0,0 +1,4 @@ +build +libz +bwa +include diff --git a/samples/workload/bwa/CMakeLists.bwa_wasm.txt b/samples/workload/bwa/CMakeLists.bwa_wasm.txt new file mode 100644 index 0000000000..a8d1d8821d --- /dev/null +++ b/samples/workload/bwa/CMakeLists.bwa_wasm.txt @@ -0,0 +1,124 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(bwa_wasm C) + +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) +add_library(z_wasm STATIC + ${LIBZ_SRC_DIR}/adler32.c + ${LIBZ_SRC_DIR}/compress.c + ${LIBZ_SRC_DIR}/crc32.c + ${LIBZ_SRC_DIR}/deflate.c + ${LIBZ_SRC_DIR}/gzclose.c + ${LIBZ_SRC_DIR}/gzlib.c + ${LIBZ_SRC_DIR}/gzread.c + ${LIBZ_SRC_DIR}/gzwrite.c + ${LIBZ_SRC_DIR}/infback.c + ${LIBZ_SRC_DIR}/inffast.c + ${LIBZ_SRC_DIR}/inflate.c + ${LIBZ_SRC_DIR}/inftrees.c + ${LIBZ_SRC_DIR}/trees.c + ${LIBZ_SRC_DIR}/uncompr.c + ${LIBZ_SRC_DIR}/zutil.c +) + +set_target_properties(z_wasm PROPERTIES LINKER_LANGUAGE C) + +target_compile_definitions(z_wasm PRIVATE Z_HAVE_UNISTD_H _LARGEFILE64_SOURCE=1) + +target_compile_options(z_wasm + PRIVATE + -Wno-unused-function + -Wno-unused-variable +) + +target_include_directories(z_wasm + PUBLIC + ${LIBZ_SRC_DIR} +) + +################ BWA_WASM ################ +set(BWA_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(BWA_SOURCE + ${BWA_SRC_DIR}/utils.c + ${BWA_SRC_DIR}/kthread.c + ${BWA_SRC_DIR}/kstring.c + ${BWA_SRC_DIR}/ksw.c + ${BWA_SRC_DIR}/bwt.c + ${BWA_SRC_DIR}/bntseq.c + ${BWA_SRC_DIR}/bwa.c + ${BWA_SRC_DIR}/bwamem.c + ${BWA_SRC_DIR}/bwamem_pair.c + ${BWA_SRC_DIR}/bwamem_extra.c + ${BWA_SRC_DIR}/malloc_wrap.c + ${BWA_SRC_DIR}/QSufSort.c + ${BWA_SRC_DIR}/bwt_gen.c + ${BWA_SRC_DIR}/rope.c + ${BWA_SRC_DIR}/rle.c + ${BWA_SRC_DIR}/is.c + ${BWA_SRC_DIR}/bwtindex.c + ${BWA_SRC_DIR}/bwashm.c + ${BWA_SRC_DIR}/bwase.c + ${BWA_SRC_DIR}/bwaseqio.c + ${BWA_SRC_DIR}/bwtgap.c + ${BWA_SRC_DIR}/bwtaln.c + ${BWA_SRC_DIR}/bamlite.c + ${BWA_SRC_DIR}/bwape.c + ${BWA_SRC_DIR}/kopen.c + ${BWA_SRC_DIR}/pemerge.c + ${BWA_SRC_DIR}/maxk.c + ${BWA_SRC_DIR}/bwtsw2_core.c + ${BWA_SRC_DIR}/bwtsw2_main.c + ${BWA_SRC_DIR}/bwtsw2_aux.c + ${BWA_SRC_DIR}/bwt_lite.c + ${BWA_SRC_DIR}/bwtsw2_chain.c + ${BWA_SRC_DIR}/fastmap.c + ${BWA_SRC_DIR}/bwtsw2_pair.c + ${BWA_SRC_DIR}/main.c +) + +add_executable(${PROJECT_NAME} ${BWA_SOURCE}) + +set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME bwa.wasm) + +target_compile_definitions(${PROJECT_NAME} + PRIVATE + USE_MALLOC_WRAPPERS + __SSE__ __SSE2__ __SSE4_1__ + _WASI_EMULATED_MMAN _WASI_EMULATED_SIGNAL _WASI_EMULATED_PROCESS_CLOCKS +) + +target_compile_options(${PROJECT_NAME} + PRIVATE + -Wno-unused-function + -Wno-unused-variable + -msimd128 +) + +target_link_options(${PROJECT_NAME} + PRIVATE + -Wno-unused-command-line-argument + LINKER:--allow-undefined,--export=__heap_base,--export=__data_end + LINKER:-z,stack-size=1048576 +) + +target_link_libraries(${PROJECT_NAME} z_wasm wasi-emulated-process-clocks) + +add_custom_target(bwa_wasm_opt ALL + COMMAND + ${Binaryen_WASM_OPT} -Oz --enable-simd -o bwa.opt.wasm bwa.wasm + BYPRODUCTS + ${CMAKE_CURRENT_BINARY_DIR}/bwa.opt.wasm + WORKING_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_dependencies(bwa_wasm_opt ${PROJECT_NAME}) diff --git a/samples/workload/bwa/CMakeLists.txt b/samples/workload/bwa/CMakeLists.txt new file mode 100644 index 0000000000..45097ab26c --- /dev/null +++ b/samples/workload/bwa/CMakeLists.txt @@ -0,0 +1,72 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(bwa_wasm) + +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) + +################ libz ################ +ExternalProject_Add(libz_src + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG 04f42ceca40f73e2978b50e93806c2a18c1281fc + GIT_PROGRESS ON + GIT_SHALLOW ON + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libz + UPDATE_COMMAND "" + PATCH_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" +) + +################ bwa ################ +ExternalProject_Add(bwa + GIT_REPOSITORY https://github.com/lh3/bwa.git + GIT_TAG v0.7.18 + GIT_PROGRESS ON + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/bwa + DEPENDS libz_src + UPDATE_COMMAND git clean -ffdx && git checkout -- * + && ${CMAKE_COMMAND} -E echo "Copying pre-installed CMakeLists.txt" + && ${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=${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 -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 new file mode 100644 index 0000000000..e665db8959 --- /dev/null +++ b/samples/workload/bwa/README.md @@ -0,0 +1,46 @@ +"bwa" sample introduction +============== + +This sample demonstrates how to build [bwa](https://github.com/lh3/bwa) into +WebAssembly with simd support and run it with iwasm. + +## Preparation + +please refer to [installation instructions](../README.md). + +## Build + +``` shell +$ mkdir build && cd build +$ cmake .. +$ make +# to verify +$ ls bwa.wasm +``` + +## Download sample data + +Download the bwa-0.7.15 binary package from +[such an address](https://sourceforge.net/projects/bio-bwa/files/bwakit/bwakit-0.7.15_x64-linux.tar.bz2/download), +a sample data file named **hs38DH.fa** will be used later. + +If want more data, please refer to http://hgdownload.cse.ucsc.edu/goldenpath/hg19/bigZips/ + +## Run workload + +Firstly please build iwasm with simd support: + +``` shell +$ cd /product-mini/platforms/linux/ +$ mkdir build && cd build +$ cmake .. +$ make +``` + +Then compile wasm file to aot file and run: + +``` shell +$ cd /samples/workload/bwa/build +$ /wamr-compiler/build/wamrc -o bwa.aot bwa.wasm +$ /product-mini/platforms/linux/iwasm --dir=. bwa.aot index hs38DH-extra.fa +``` diff --git a/samples/workload/bwa/bwa.patch b/samples/workload/bwa/bwa.patch new file mode 100644 index 0000000000..5599ca3198 --- /dev/null +++ b/samples/workload/bwa/bwa.patch @@ -0,0 +1,13 @@ +diff --git a/utils.c b/utils.c +index 9ceb1be..323299f 100644 +--- a/utils.c ++++ b/utils.c +@@ -301,6 +301,7 @@ long peakrss(void) + #ifdef __linux__ + return r.ru_maxrss * 1024; + #else +- return r.ru_maxrss; ++ /*return r.ru_maxrss;*/ ++ return 0; + #endif + } diff --git a/samples/workload/cmake/FindBinaryen.cmake b/samples/workload/cmake/FindBinaryen.cmake new file mode 100644 index 0000000000..b4a647861f --- /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 0000000000..fff8aea4a5 --- /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/include/.gitkeep b/samples/workload/include/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/samples/workload/meshoptimizer/.gitignore b/samples/workload/meshoptimizer/.gitignore new file mode 100644 index 0000000000..dd97754d45 --- /dev/null +++ b/samples/workload/meshoptimizer/.gitignore @@ -0,0 +1,2 @@ +build +meshoptimizer \ No newline at end of file diff --git a/samples/workload/meshoptimizer/CMakeLists.txt b/samples/workload/meshoptimizer/CMakeLists.txt new file mode 100644 index 0000000000..d6a1c358a6 --- /dev/null +++ b/samples/workload/meshoptimizer/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(bench-meshoptimizer) + +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) + +ExternalProject_Add(codecbench + PREFIX codecbench + GIT_REPOSITORY https://github.com/zeux/meshoptimizer.git + GIT_TAG f734fd572aed5bf76e84d9ed62ca6f4f6c47d84e + GIT_SHALLOW OFF + GIT_PROGRESS ON + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/meshoptimizer + UPDATE_COMMAND git clean -fd && git checkout -- * + && ${CMAKE_COMMAND} -E echo "Applying patch" + && git apply ${CMAKE_CURRENT_SOURCE_DIR}/codecbench.patch + CONFIGURE_COMMAND ${CMAKE_COMMAND} + -DWASI_SDK_PREFIX=${WASISDK_HOME} + -DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN} + -DCMAKE_SYSROOT=${WASISDK_SYSROOT} + ${CMAKE_CURRENT_SOURCE_DIR}/meshoptimizer + 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 new file mode 100644 index 0000000000..466cd8759c --- /dev/null +++ b/samples/workload/meshoptimizer/README.md @@ -0,0 +1,57 @@ +"codecbench of meshoptimizer" sample introduction +============== + +This sample demonstrates how to build [codecbench of messoptimizer](https://github.com/zeux/meshoptimizer) into +WebAssembly with simd support and run it with iwasm. + +## Preparation + +please refer to [installation instructions](../README.md). + +## Build with wasi-sdk + +``` shell +$ mkdir build && cd build +$ cmake .. +$ make +# to verify +$ ls codecbench.wasm +``` + +## Or build with EMSDK + +EMSDK is another toolchain to compile C/C++ code to WASM. In this case, the output wasm file +might have a higher performance than the file generated by wasi-sdk. + +``` shell +$ git clone https://github.com/zeux/meshoptimizer.git +$ cd messoptimizer +$ em++ tools/codecbench.cpp src/vertexcodec.cpp src/vertexfilter.cpp \ + src/overdrawanalyzer.cpp src/indexgenerator.cpp src/vcacheoptimizer.cpp \ + src/clusterizer.cpp src/indexcodec.cpp src/vfetchanalyzer.cpp \ + src/spatialorder.cpp src/allocator.cpp src/vcacheanalyzer.cpp \ + src/vfetchoptimizer.cpp src/overdrawoptimizer.cpp src/simplifier.cpp \ + src/stripifier.cpp -O3 -msimd128 \ + -s TOTAL_MEMORY=268435456 \ + -o codecbench.wasm +$ ls -l codecbench.wasm +``` + +## Run workload + +Firstly please build iwasm with simd support: + +``` shell +$ cd /product-mini/platforms/linux/ +$ mkdir build && cd build +$ cmake .. +$ make +``` + +Then compile wasm file to aot file and run: + +``` shell +$ /wamr-compiler/build/wamrc -o codecbench.aot codecbench.wasm +$ /product-mini/platforms/linux/build/iwasm codecbench.aot +``` + diff --git a/samples/workload/meshoptimizer/codecbench.patch b/samples/workload/meshoptimizer/codecbench.patch new file mode 100644 index 0000000000..19db792bfb --- /dev/null +++ b/samples/workload/meshoptimizer/codecbench.patch @@ -0,0 +1,119 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 612cf3b..22a365a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -158,3 +158,43 @@ install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/meshoptimizerConfigVersion.cmake + COMPONENT meshoptimizer + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/meshoptimizer) ++ ++################################################## ++# codecbench ++################################################## ++add_executable(codecbench tools/codecbench.cpp ${SOURCES}) ++ ++set_target_properties(codecbench PROPERTIES OUTPUT_NAME codecbench.wasm) ++ ++target_compile_options(codecbench ++ PUBLIC ++ -O3 -msimd128 ++ -std=c++11 ++ -Wno-unused-function ++ -Wno-unused-variable ++) ++ ++target_link_options(codecbench ++ PUBLIC ++ LINKER:-allow-undefined,--demangle,--export=malloc,--export=free ++) ++ ++find_program(WASM_OPT ++ NAMES wasm-opt ++ PATHS /opt/binaryen-version_97/bin /opt/binaryen/bin ++) ++ ++if (NOT WASM_OPT) ++ message(WARNING "can not find wasm-opt and will not optimize any wasm module") ++endif() ++ ++add_custom_target(codecbench.opt ALL ++ COMMAND ++ ${WASM_OPT} -Oz --enable-simd -o codecbench.opt.wasm codecbench.wasm ++ BYPRODUCTS ++ ${CMAKE_CURRENT_BINARY_DIR}/codecbench.opt.wasm ++ WORKING_DIRECTORY ++ ${CMAKE_CURRENT_BINARY_DIR} ++) ++ ++add_dependencies(codecbench.opt codecbench) +diff --git a/src/vertexcodec.cpp b/src/vertexcodec.cpp +index 4bd1112..257c258 100644 +--- a/src/vertexcodec.cpp ++++ b/src/vertexcodec.cpp +@@ -89,13 +89,13 @@ + #endif + + #ifdef SIMD_WASM +-#define wasmx_splat_v32x4(v, i) wasm_v32x4_shuffle(v, v, i, i, i, i) +-#define wasmx_unpacklo_v8x16(a, b) wasm_v8x16_shuffle(a, b, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23) +-#define wasmx_unpackhi_v8x16(a, b) wasm_v8x16_shuffle(a, b, 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31) +-#define wasmx_unpacklo_v16x8(a, b) wasm_v16x8_shuffle(a, b, 0, 8, 1, 9, 2, 10, 3, 11) +-#define wasmx_unpackhi_v16x8(a, b) wasm_v16x8_shuffle(a, b, 4, 12, 5, 13, 6, 14, 7, 15) +-#define wasmx_unpacklo_v64x2(a, b) wasm_v64x2_shuffle(a, b, 0, 2) +-#define wasmx_unpackhi_v64x2(a, b) wasm_v64x2_shuffle(a, b, 1, 3) ++#define wasmx_splat_v32x4(v, i) wasm_i32x4_shuffle(v, v, i, i, i, i) ++#define wasmx_unpacklo_v8x16(a, b) wasm_i8x16_shuffle(a, b, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23) ++#define wasmx_unpackhi_v8x16(a, b) wasm_i8x16_shuffle(a, b, 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31) ++#define wasmx_unpacklo_v16x8(a, b) wasm_i16x8_shuffle(a, b, 0, 8, 1, 9, 2, 10, 3, 11) ++#define wasmx_unpackhi_v16x8(a, b) wasm_i16x8_shuffle(a, b, 4, 12, 5, 13, 6, 14, 7, 15) ++#define wasmx_unpacklo_v64x2(a, b) wasm_i64x2_shuffle(a, b, 0, 2) ++#define wasmx_unpackhi_v64x2(a, b) wasm_i64x2_shuffle(a, b, 1, 3) + #endif + + namespace meshopt +@@ -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]); +- sm1off = wasm_v8x16_shuffle(sm1off, sm1off, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); ++ sm1off = wasm_i8x16_shuffle(sm1off, sm1off, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + v128_t sm1r = wasm_i8x16_add(sm1, sm1off); + +@@ -807,7 +807,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi + + v128_t shuf = decodeShuffleMask(mask0, mask1); + +- v128_t result = wasm_v128_bitselect(wasm_v8x16_swizzle(rest, shuf), sel, mask); ++ v128_t result = wasm_v128_bitselect(wasm_i8x16_swizzle(rest, shuf), sel, mask); + + wasm_v128_store(buffer, result); + +@@ -829,7 +829,7 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi + + v128_t shuf = decodeShuffleMask(mask0, mask1); + +- v128_t result = wasm_v128_bitselect(wasm_v8x16_swizzle(rest, shuf), sel, mask); ++ v128_t result = wasm_v128_bitselect(wasm_i8x16_swizzle(rest, shuf), sel, mask); + + wasm_v128_store(buffer, result); + +diff --git a/src/vertexfilter.cpp b/src/vertexfilter.cpp +index 5c7589c..c79cad4 100644 +--- a/src/vertexfilter.cpp ++++ b/src/vertexfilter.cpp +@@ -57,10 +57,10 @@ + #endif + + #ifdef SIMD_WASM +-#define wasmx_unpacklo_v16x8(a, b) wasm_v16x8_shuffle(a, b, 0, 8, 1, 9, 2, 10, 3, 11) +-#define wasmx_unpackhi_v16x8(a, b) wasm_v16x8_shuffle(a, b, 4, 12, 5, 13, 6, 14, 7, 15) +-#define wasmx_unziplo_v32x4(a, b) wasm_v32x4_shuffle(a, b, 0, 2, 4, 6) +-#define wasmx_unziphi_v32x4(a, b) wasm_v32x4_shuffle(a, b, 1, 3, 5, 7) ++#define wasmx_unpacklo_v16x8(a, b) wasm_i16x8_shuffle(a, b, 0, 8, 1, 9, 2, 10, 3, 11) ++#define wasmx_unpackhi_v16x8(a, b) wasm_i16x8_shuffle(a, b, 4, 12, 5, 13, 6, 14, 7, 15) ++#define wasmx_unziplo_v32x4(a, b) wasm_i32x4_shuffle(a, b, 0, 2, 4, 6) ++#define wasmx_unziphi_v32x4(a, b) wasm_i32x4_shuffle(a, b, 1, 3, 5, 7) + #endif + + #ifndef __has_builtin diff --git a/samples/workload/preparation.sh b/samples/workload/preparation.sh new file mode 100755 index 0000000000..f5d8d8e461 --- /dev/null +++ b/samples/workload/preparation.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +readonly BUILD_CONTENT="/tmp/build_content" +readonly WABT_VER=1.0.31 +readonly WABT_FILE="wabt-${WABT_VER}-ubuntu.tar.gz" +readonly CMAKE_VER=3.25.1 +readonly CMAKE_FILE="cmake-${CMAKE_VER}-Linux-x86_64.sh" +readonly BINARYEN_VER=version_111 +readonly BINARYEN_FILE="binaryen-${BINARYEN_VER}-x86_64-linux.tar.gz" +readonly BAZEL_VER=6.0.0 +readonly BAZEL_FILE=bazel-${BAZEL_VER}-installer-linux-x86_64.sh + +function DEBUG() { + env | grep -q "\" +} + +# +# install dependency +function install_deps() { + apt update + apt install -y lsb-release wget software-properties-common \ + build-essential git tree zip unzip +} + +# +# install wabt +function install_wabt() { + if [[ ! -f ${WABT_FILE} ]]; then + wget https://github.com/WebAssembly/wabt/releases/download/${WABT_VER}/${WABT_FILE} + fi + + tar zxf ${WABT_FILE} -C /opt + ln -sf /opt/wabt-${WABT_VER} /opt/wabt +} + +# +# install cmake +function install_cmake() { + if [[ ! -f cmake-${CMAKE_VER}-Linux-x86_64.sh ]]; then + wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VER}/${CMAKE_FILE} + fi + + chmod a+x ${CMAKE_FILE} + mkdir /opt/cmake + ./${CMAKE_FILE} --prefix=/opt/cmake --skip-license + ln -sf /opt/cmake/bin/cmake /usr/local/bin/cmake +} + +# +# install emsdk +function install_emsdk() { + cd /opt + git clone https://github.com/emscripten-core/emsdk.git + cd emsdk + git pull + ./emsdk install 3.1.28 + ./emsdk activate 3.1.28 + echo "source /opt/emsdk/emsdk_env.sh" >> "${HOME}"/.bashrc +} + +# +# install binaryen +function install_binaryen() { + if [[ ! -f ${BINARYEN_FILE} ]]; then + wget https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VER}/${BINARYEN_FILE} + fi + + tar zxf ${BINARYEN_FILE} -C /opt + ln -sf /opt/binaryen-${BINARYEN_VER} /opt/binaryen +} + +# +# install bazel +function install_bazel() { + if [[ ! -f ${BAZEL_FILE} ]]; then + wget https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VER}/${BAZEL_FILE} + fi + + chmod a+x ${BAZEL_FILE} + ./${BAZEL_FILE} +} + +# +# MAIN +DEBUG && set -xevu +if [[ ! -d ${BUILD_CONTENT} ]]; then + mkdir ${BUILD_CONTENT} +fi + +cd ${BUILD_CONTENT} || exit +if DEBUG; then + "$@" +else + install_deps \ + && install_bazel \ + && install_binaryen \ + && install_cmake \ + && install_emsdk \ + && install_wabt +fi +cd - > /dev/null || exit +DEBUG && set +xevu diff --git a/samples/workload/tensorflow/README.md b/samples/workload/tensorflow/README.md new file mode 100644 index 0000000000..7bc7dd2597 --- /dev/null +++ b/samples/workload/tensorflow/README.md @@ -0,0 +1,19 @@ +"tensorflow" sample introduction +============== + +This sample demonstrates how to build [tensorflow](https://github.com/tensorflow/tensorflow) into WebAssembly with emsdk toolchain and run it with iwasm.: +```bash +./build.sh +# for linux platform, or +./build.sh --threads +# for multi-threading on linux platform +./build.sh --sgx +# for linux-sgx platform +``` +to build tensorflow and run it with iwasm, which basically contains the following steps: +- clone emsdk under `/core/deps`, install and activate 2.0.26 +- hack emcc to delete some objects in libc.a +- build tf-lite with emcc compiler +- build iwasm with lib-pthread and libc-emcc enabled +- run benchmark model with iwasm: + --max-secs 300: means the max training time cost is 5 minutes, you can adjust it by yourself diff --git a/samples/workload/tensorflow/build.sh b/samples/workload/tensorflow/build.sh new file mode 100755 index 0000000000..6df8db423b --- /dev/null +++ b/samples/workload/tensorflow/build.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#################################### +# build tensorflow-lite sample # +#################################### +BUILD_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +WAMR_DIR="${BUILD_SCRIPT_DIR}/../../.." +WAMR_PLATFORM_DIR="${WAMR_DIR}/product-mini/platforms" +WAMRC_DIR="${WAMR_DIR}/wamr-compiler" +CORE_DEPS_DIR="${WAMR_DIR}/core/deps" +EMSDK_DIR="${CORE_DEPS_DIR}/emsdk" + +EMSDK_WASM_DIR="${EMSDK_DIR}/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten" +OUT_DIR="${BUILD_SCRIPT_DIR}/out" +TENSORFLOW_DIR="${BUILD_SCRIPT_DIR}/tensorflow" +TF_LITE_BUILD_DIR="${TENSORFLOW_DIR}/tensorflow/lite/tools/make" + +function Clear_Before_Exit() +{ + [[ -f ${TENSORFLOW_DIR}/tf_lite.patch ]] && + rm -f ${TENSORFLOW_DIR}/tf_lite.patch + # resume the libc.a under EMSDK_WASM_DIR + cd ${EMSDK_WASM_DIR} + mv libc.a.bak libc.a +} + +set -xe + +# 1.clone emsdk +cd ${CORE_DEPS_DIR} +rm -fr emsdk +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install 2.0.26 +./emsdk activate 2.0.26 +source emsdk_env.sh + +# 2.hack emcc +cd ${EMSDK_WASM_DIR} +# back up libc.a +cp libc.a libc.a.bak +# delete some objects in libc.a +emar d libc.a open.o +emar d libc.a mmap.o +emar d libc.a munmap.o +emar d libc.a library_pthread_stub.o +emar d libc.a pthread_self.o +emranlib libc.a + +# 3. build tf-lite +cd ${BUILD_SCRIPT_DIR} +# 3.1 clone tf repo from Github and checkout to 2303ed commit +if [ ! -d "tensorflow" ]; then + git clone https://github.com/tensorflow/tensorflow.git +fi + +cd ${TENSORFLOW_DIR} +git checkout 2303ed4bdb344a1fc4545658d1df6d9ce20331dd + +# 3.2 copy the tf-lite.patch to tensorflow_root_dir and apply it +cd ${TENSORFLOW_DIR} +cp ${BUILD_SCRIPT_DIR}/tf_lite.patch . +git checkout tensorflow/lite/tools/make/Makefile +git checkout tensorflow/lite/tools/make/targets/linux_makefile.inc + +if [[ $(git apply tf_lite.patch 2>&1) =~ "error" ]]; then + echo "git apply patch failed, please check tf-lite related changes..." + Clear_Before_Exit + exit 0 +fi + +cd ${TF_LITE_BUILD_DIR} +# 3.3 download dependencies +if [ ! -d "${TF_LITE_BUILD_DIR}/downloads" ]; then + source download_dependencies.sh +fi + +# 3.4 build tf-lite target +if [ -d "${TF_LITE_BUILD_DIR}/gen" ]; then + rm -fr ${TF_LITE_BUILD_DIR}/gen +fi + +make -j 4 -C "${TENSORFLOW_DIR}" -f ${TF_LITE_BUILD_DIR}/Makefile + +# remove patch file and recover emcc libc.a after building +Clear_Before_Exit + +# 3.5 copy /make/gen target files to out/ +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +cp -r ${TF_LITE_BUILD_DIR}/gen/linux_x86_64/bin/. ${OUT_DIR}/ + +# 4. compile tf-model.wasm to tf-model.aot with wamrc +# 4.1 build wamr-compiler +cd ${WAMRC_DIR} +./build_llvm.sh +rm -fr build && mkdir build +cd build && cmake .. +make +# 4.2 compile tf-mode.wasm to tf-model.aot +WAMRC_CMD="$(pwd)/wamrc" +cd ${OUT_DIR} +if [[ $1 == '--sgx' ]]; then + ${WAMRC_CMD} -sgx -o benchmark_model.aot benchmark_model.wasm +elif [[ $1 == '--threads' ]]; then + ${WAMRC_CMD} --enable-multi-thread -o benchmark_model.aot benchmark_model.wasm +else + ${WAMRC_CMD} -o benchmark_model.aot benchmark_model.wasm +fi + +# 5. build iwasm with pthread and libc_emcc enable +# platform: +# linux by default +# linux-sgx if $1 equals '--sgx' +if [[ $1 == '--sgx' ]]; then + cd ${WAMR_PLATFORM_DIR}/linux-sgx + rm -fr build && mkdir build + cd build && cmake .. -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_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 + make +fi + +# 6. run tensorflow with iwasm +cd ${OUT_DIR} +# 6.1 download tf-lite model +wget "https://storage.googleapis.com/download.tensorflow.org/models/tflite/mobilenet_v1_224_android_quant_2017_11_08.zip" +unzip mobilenet_v1_224_android_quant_2017_11_08.zip + +# 6.2 run tf-lite model with iwasm +echo "---> run tensorflow benchmark model with iwasm" +if [[ $1 == '--sgx' ]]; then + IWASM_CMD="${WAMR_PLATFORM_DIR}/linux-sgx/enclave-sample/iwasm" +else + IWASM_CMD="${WAMR_PLATFORM_DIR}/linux/build/iwasm" +fi + +if [[ $1 == '--threads' ]]; then + ${IWASM_CMD} --heap-size=10475860 \ + benchmark_model.aot --num_threads=4 \ + --graph=mobilenet_quant_v1_224.tflite --max_secs=300 +else + ${IWASM_CMD} --heap-size=10475860 \ + benchmark_model.aot \ + --graph=mobilenet_quant_v1_224.tflite --max_secs=300 +fi diff --git a/samples/workload/tensorflow/tf_lite.patch b/samples/workload/tensorflow/tf_lite.patch new file mode 100644 index 0000000000..b6224d581f --- /dev/null +++ b/samples/workload/tensorflow/tf_lite.patch @@ -0,0 +1,84 @@ +diff --git a/tensorflow/lite/tools/make/Makefile b/tensorflow/lite/tools/make/Makefile +index c7ddff58440..ebfebaead35 100644 +--- a/tensorflow/lite/tools/make/Makefile ++++ b/tensorflow/lite/tools/make/Makefile +@@ -48,11 +48,7 @@ INCLUDES += -I/usr/local/include + + # These are the default libraries needed, but they can be added to or + # overridden by the platform-specific settings in target makefiles. +-LIBS := \ +--lstdc++ \ +--lpthread \ +--lm \ +--lz \ ++LIBS := -lm \ + -ldl + + # There are no rules for compiling objects for the host system (since we don't +@@ -84,14 +80,24 @@ endif # ifeq ($(HOST_ARCH),$(TARGET_ARCH)) + endif # ifeq ($(HOST_OS),$(TARGET)) + endif + ++CFLAGS+=-msimd128 -mbulk-memory -matomics ++CXXFLAGS+=-msimd128 -mbulk-memory -matomics ++ ++LIBFLAGS += -s TOTAL_STACK=1048576 -s MALLOC="none" \ ++ -s INITIAL_MEMORY=16777216 \ ++ -s MAXIMUM_MEMORY=167772160 \ ++ -s ALLOW_MEMORY_GROWTH=1 \ ++ -Wl,--export=__data_end -Wl,--export=__heap_base,--shared-memory,--no-check-features \ ++ -s ERROR_ON_UNDEFINED_SYMBOLS=0 ++ + # This library is the main target for this makefile. It will contain a minimal + # runtime that can be linked in to other programs. + LIB_NAME := libtensorflow-lite.a + + # Benchmark static library and binary + BENCHMARK_LIB_NAME := benchmark-lib.a +-BENCHMARK_BINARY_NAME := benchmark_model +-BENCHMARK_PERF_OPTIONS_BINARY_NAME := benchmark_model_performance_options ++BENCHMARK_BINARY_NAME := benchmark_model.wasm ++BENCHMARK_PERF_OPTIONS_BINARY_NAME := benchmark_model_performance_options.wasm + + # A small example program that shows how to link against the library. + MINIMAL_SRCS := \ +@@ -277,12 +283,16 @@ LIB_PATH := $(LIBDIR)$(LIB_NAME) + BENCHMARK_LIB := $(LIBDIR)$(BENCHMARK_LIB_NAME) + BENCHMARK_BINARY := $(BINDIR)$(BENCHMARK_BINARY_NAME) + BENCHMARK_PERF_OPTIONS_BINARY := $(BINDIR)$(BENCHMARK_PERF_OPTIONS_BINARY_NAME) +-MINIMAL_BINARY := $(BINDIR)minimal ++MINIMAL_BINARY := $(BINDIR)minimal.wasm + LABEL_IMAGE_BINARY := $(BINDIR)label_image + +-CXX := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}g++ +-CC := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}gcc +-AR := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}ar ++# CXX := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}g++ ++# CC := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}gcc ++# AR := $(CC_PREFIX)${TARGET_TOOLCHAIN_PREFIX}ar ++ ++CXX := em++ ++CC := emcc ++AR := emar + + MINIMAL_OBJS := $(addprefix $(OBJDIR), \ + $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(MINIMAL_SRCS)))) +diff --git a/tensorflow/lite/tools/make/targets/linux_makefile.inc b/tensorflow/lite/tools/make/targets/linux_makefile.inc +index 222cef9e5ff..eea89a38f01 100644 +--- a/tensorflow/lite/tools/make/targets/linux_makefile.inc ++++ b/tensorflow/lite/tools/make/targets/linux_makefile.inc +@@ -2,12 +2,10 @@ + ifeq ($(TARGET), linux) + CXXFLAGS += \ + -fPIC \ +- -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK \ +- -pthread ++ -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK + CFLAGS += \ + -fPIC \ +- -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK \ +- -pthread ++ -DGEMMLOWP_ALLOW_SLOW_SCALAR_FALLBACK + # TODO(petewarden): In the future we may want to add architecture-specific + # flags like -msse4.2 + LIBS += -ldl diff --git a/samples/workload/wasm-av1/.gitignore b/samples/workload/wasm-av1/.gitignore new file mode 100644 index 0000000000..e8bec70e48 --- /dev/null +++ b/samples/workload/wasm-av1/.gitignore @@ -0,0 +1,8 @@ +# from CMakeLists +av1 +build +include + +# from build.sh +wasm-av1 +out diff --git a/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt b/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt new file mode 100644 index 0000000000..409665b3c6 --- /dev/null +++ b/samples/workload/wasm-av1/CMakeLists.avx_wasm.txt @@ -0,0 +1,72 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(testavx) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../cmake) + +################ dependencies ################ +find_package(Binaryen 111 REQUIRED) + +################ AOM ################ +set(ENABLE_CCACHE ON) +set(ENABLE_DOCS OFF CACHE BOOL "ENABLE_DOCS" FORCE) +set(ENABLE_EXAMPLES OFF CACHE BOOL "ENABLE_EXAMPLES" FORCE) +set(ENABLE_NEON OFF CACHE BOOL "ENABLE_EXAMPLES" FORCE) +set(ENABLE_NEON_ASM OFF CACHE BOOL "ENABLE_EXAMPLES" FORCE) +set(ENABLE_VSX OFF CACHE BOOL "ENABLE_EXAMPLES" FORCE) +set(ENABLE_MMX OFF CACHE BOOL "ENABLE_EXAMPLES" FORCE) +set(AOM_TARGET_CPU generic) +set(CONFIG_ACCOUNTING 1 CACHE NUMBER "" FORCE) +set(CONFIG_INSPECTION 1 CACHE NUMBER "" FORCE) +set(CONFIG_MULTITHREAD 0 CACHE NUMBER "" FORCE) +set(CONFIG_RUNTIME_CPU_DETECT 0 CACHE NUMBER "" FORCE) +set(CONFIG_UNIT_TESTS 0 CACHE NUMBER "" FORCE) +set(CONFIG_WEBM_IO 0 CACHE NUMBER "" FORCE) +add_subdirectory(third_party/aom third_party/aom/bin EXCLUDE_FROM_ALL) + +################ AV ################ +add_executable(${PROJECT_NAME} + test.c + decode-av1.c +) + +target_include_directories(${PROJECT_NAME} + PRIVATE + third_party/aom/ + ${CMAKE_CURRENT_BINARY_DIR}/third_party/aom/bin +) + +set_target_properties(${PROJECT_NAME} + PROPERTIES + OUTPUT_NAME ${PROJECT_NAME}.wasm +) + +target_link_options(${PROJECT_NAME} + PRIVATE + LINKER:--allow-undefined + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--initial-memory=33554432 + LINKER:-z,stack-size=25165824 +) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + aom +) + +add_dependencies(${PROJECT_NAME} aom) + +add_custom_target(${PROJECT_NAME}_opt ALL + COMMAND + ${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 + ${CMAKE_CURRENT_BINARY_DIR} +) + +add_dependencies(${PROJECT_NAME}_opt ${PROJECT_NAME}) diff --git a/samples/workload/wasm-av1/CMakeLists.txt b/samples/workload/wasm-av1/CMakeLists.txt new file mode 100644 index 0000000000..3d263bfbe4 --- /dev/null +++ b/samples/workload/wasm-av1/CMakeLists.txt @@ -0,0 +1,44 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project(av1_wasm) + +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) + +################ av1 ################ +ExternalProject_Add(av1 + PREFIX av1 + GIT_REPOSITORY https://github.com/GoogleChromeLabs/wasm-av1.git + GIT_TAG master + GIT_PROGRESS ON + GIT_SHALLOW ON + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/av1 + UPDATE_COMMAND git clean -fd && git checkout -- * + && ${CMAKE_COMMAND} -E echo "Copying pre-installed CMakeLists.txt" + && ${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=${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 -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 new file mode 100644 index 0000000000..2166fe6aec --- /dev/null +++ b/samples/workload/wasm-av1/README.md @@ -0,0 +1,54 @@ +"wasm-av1" sample introduction +============== + +This sample demonstrates how to build [wasm-av1](https://github.com/GoogleChromeLabs/wasm-av1) into +WebAssembly with simd support and run it with iwasm. + +## Preparation + +please refer to [installation instructions](../README.md). + +## Build with wasi-sdk + +``` shell +$ mkdir build && cd build +$ cmake .. +$ make +# to verify +$ ls testavx.wasm +``` + +## Or build with EMSDK + +just run the convenience script: + +```bash +./build.sh +``` + +the script builds wasm-av1 and runs it with iwasm, which basically contains the following steps: +- hack emcc to delete some objects in libc.a +- patch wasm-av1 and build it with emcc compiler +- build iwasm with simd and libc-emcc support +- run testav1.aot with iwasm + +### Run workload + +Firstly please build iwasm with simd support: + +``` shell +$ cd /product-mini/platforms/linux/ +$ mkdir build && cd build +$ cmake .. -DWAMR_BUILD_LIBC_EMCC=1 +$ make +``` + +Then compile wasm file to aot file and run: + +``` shell +$ cd
+$ /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/av1-clang.patch b/samples/workload/wasm-av1/av1-clang.patch new file mode 100644 index 0000000000..97e7954825 --- /dev/null +++ b/samples/workload/wasm-av1/av1-clang.patch @@ -0,0 +1,19 @@ +diff --git a/test.c b/test.c +index df2d44b..520bf13 100644 +--- a/test.c ++++ b/test.c +@@ -63,9 +63,14 @@ main(int argc, char *argv[]) { + static int i = 0; + + ++i; ++ printf("Decoding frame #%d\n", i); + if (30 <= i && i < 40) { ++ printf("Dumping frame #%d\n", i); + dump_raw_frame(af, i); + } ++ if (i >= 1000) { ++ break; ++ } + } + /* + * Run the decoder every time, so that we keep diff --git a/samples/workload/wasm-av1/build.sh b/samples/workload/wasm-av1/build.sh new file mode 100755 index 0000000000..efa17eca6d --- /dev/null +++ b/samples/workload/wasm-av1/build.sh @@ -0,0 +1,100 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +#################################### +# build wasm-av1 sample # +#################################### +if [ ! -d "${EMSDK}" ]; then + echo "can not find emsdk. " + echo "please refer to https://emscripten.org/docs/getting_started/downloads.html " + echo "to install it, or active it by 'source emsdk_env.sh'" + exit +fi + +set -xe + +EMSDK_WASM_DIR="${EMSDK}/upstream/emscripten/cache/sysroot/lib/wasm32-emscripten" +BUILD_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OUT_DIR="${BUILD_SCRIPT_DIR}/out" +WASM_AV1_DIR="${BUILD_SCRIPT_DIR}/wasm-av1" + +WAMR_PLATFORM_DIR="${BUILD_SCRIPT_DIR}/../../../product-mini/platforms" +IWASM_CMD="${WAMR_PLATFORM_DIR}/linux/build/iwasm" + +WAMRC_DIR="${BUILD_SCRIPT_DIR}/../../../wamr-compiler" +WAMRC_CMD="${WAMRC_DIR}/build/wamrc" + +function Clear_Before_Exit +{ + [[ -f ${WASM_AV1_DIR}/wasm-av1.patch ]] && + rm -f ${WASM_AV1_DIR}/wasm-av1.patch + # resume the libc.a under EMSDK_WASM_DIR + cd ${EMSDK_WASM_DIR} + mv libc.a.bak libc.a +} + +# 1.hack emcc +cd ${EMSDK_WASM_DIR} +# back up libc.a +cp libc.a libc.a.bak +# delete some objects in libc.a +emar d libc.a fopen.o +emar d libc.a fread.o +emar d libc.a feof.o +emar d libc.a fclose.o + +# 2. build wasm-av1 +cd ${BUILD_SCRIPT_DIR} +# 2.1 clone wasm-av1 repo from Github +if [ ! -d "wasm-av1" ]; then + git clone https://github.com/GoogleChromeLabs/wasm-av1.git +fi + +# 2.2 copy the wasm-av1.patch to wasm-av1 and apply the patch +cd ${WASM_AV1_DIR} +cp -a ${BUILD_SCRIPT_DIR}/wasm-av1.patch . +git checkout Makefile +git checkout test.c +git checkout third_party/aom + +if [[ $(git apply wasm-av1.patch 2>&1) =~ "error" ]]; then + echo "git apply patch failed, please check wasm-av1 related changes..." + Clear_Before_Exit + exit 0 +fi + +make testavx -j 4 + +# remove patch file and recover emcc libc.a after building +Clear_Before_Exit + +# 2.3 copy /make/gen target files to out/ +rm -rf ${OUT_DIR} && mkdir ${OUT_DIR} +cp -a ${WASM_AV1_DIR}/testavx.wasm ${OUT_DIR}/ + +# 3. compile wasm-av1.wasm to wasm-av1.aot with wamrc +# 3.1 build wamr-compiler +cd ${WAMRC_DIR} +./build_llvm.sh +rm -fr build && mkdir build +cd build && cmake .. +make +# 3.2 compile wasm-av1.wasm to wasm-av1.aot +cd ${OUT_DIR} +${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_LIB_PTHREAD=1 -DWAMR_BUILD_LIBC_EMCC=1 +make + +# 5. run wasm-av1 with iwasm +echo "---> run testav1.aot with iwasm" +cd ${OUT_DIR} +${IWASM_CMD} testavx.aot ../wasm-av1/third_party/samples/elephants_dream_480p24.ivf + diff --git a/samples/workload/wasm-av1/wasm-av1.patch b/samples/workload/wasm-av1/wasm-av1.patch new file mode 100644 index 0000000000..98f2625628 --- /dev/null +++ b/samples/workload/wasm-av1/wasm-av1.patch @@ -0,0 +1,701 @@ +diff --git a/Makefile b/Makefile +index c39fff6..4682d43 100644 +--- a/Makefile ++++ b/Makefile +@@ -59,11 +59,13 @@ $(TARGET): $(DEPS) blob-api.c yuv-to-rgb.c $(EMLIBAV1) + ]" \ + blob-api.c yuv-to-rgb.c $(SRCS) $(INC) -L $(LIBDIR) -l$(LIB) + +-$(TESTTARGET): test.c $(DEPS) $(X86LIBAV1) +- cc -o $@ -O3 test.c $(SRCS) $(INC) -L $(X86LIBDIR) -l$(LIB) ++$(TESTTARGET): test.c $(DEPS) $(EMLIBAV1) ++ emcc -o $@.wasm -O3 test.c $(SRCS) $(INC) -L $(LIBDIR) -l$(LIB) \ ++ -s TOTAL_MEMORY=104857600 -s ERROR_ON_UNDEFINED_SYMBOLS=0 + +-$(TESTTARGET)g: test.c $(DEPS) $(X86LIBAV1) +- cc -o $@ -g test.c $(SRCS) $(INC) -L $(X86LIBDIR) -l$(LIB) ++$(TESTTARGET)g: test.c $(DEPS) $(EMLIBAV1) ++ emcc -o $@.wasm -g test.c $(SRCS) $(INC) -L $(LIBDIR) -l$(LIB) \ ++ -s TOTAL_MEMORY=104857600 -s ERROR_ON_UNDEFINED_SYMBOLS=0 + + clean: + -rm $(TARGET) $(TESTTARGET) $(TESTTARGET)g +@@ -80,7 +82,7 @@ $(EMLIBAV1): $(LIBDIR) + -DCONFIG_RUNTIME_CPU_DETECT=0 \ + -DCONFIG_UNIT_TESTS=0 \ + -DCONFIG_WEBM_IO=0 \ +- -DCMAKE_TOOLCHAIN_FILE=`../../get-emcmake.sh`; \ ++ -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake; \ + make \ + ) + +diff --git a/test.c b/test.c +index df2d44b..cb270de 100644 +--- a/test.c ++++ b/test.c +@@ -18,6 +18,9 @@ + + #include "decode-av1-priv.h" + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + static void + dump_raw_frame(AVX_Video_Frame *avf, int id) { + FILE *f; +@@ -26,12 +29,13 @@ dump_raw_frame(AVX_Video_Frame *avf, int id) { + void *buf; + + sprintf(name, "frame%04d.yuv", id); ++ printf("writing %s ..\n", name); + if ((f = fopen(name, "wb")) == NULL) { + return; + } + buf = AVX_Video_Frame_get_buffer(avf); + size = AVX_Video_Frame_get_size(avf); +- fwrite(buf, size, 1, f); ++ emcc_fwrite(buf, size, 1, f); + fclose(f); + } + +@@ -63,9 +67,12 @@ main(int argc, char *argv[]) { + static int i = 0; + + ++i; ++ printf("##decode raw frame %d\n", i); + if (30 <= i && i < 40) { + dump_raw_frame(af, i); + } ++ if (i >= 1000) ++ break; + } + /* + * Run the decoder every time, so that we keep +diff --git a/third_party/aom/CMakeLists.txt b/third_party/aom/CMakeLists.txt +index 9dbe301..20c7be4 100644 +--- a/third_party/aom/CMakeLists.txt ++++ b/third_party/aom/CMakeLists.txt +@@ -56,6 +56,10 @@ option(BUILD_SHARED_LIBS "CMake should generate a shared library build." OFF) + + project(AOM C CXX) + ++set(CMAKE_C_FLAGS "-msimd128 -msse2 -msse3 -msse4.1 -msse4.2 ${CMAKE_C_FLAGS}") ++set(CMAKE_CXX_FLAGS "-msimd128 -msse2 -msse3 -msse4.1 -msse4.2 ${CMAKE_CXX_FLAGS}") ++set(CMAKE_VERBOSE_MAKEFILE on) ++ + set(AOM_ROOT "${CMAKE_CURRENT_SOURCE_DIR}") + set(AOM_CONFIG_DIR "${CMAKE_CURRENT_BINARY_DIR}") + set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" +@@ -347,7 +351,7 @@ if(CONFIG_AV1_DECODER AND ENABLE_EXAMPLES) + em_link_post_js(inspect "${AOM_ROOT}/tools/inspect-post.js") + # Force generation of Wasm instead of asm.js + append_link_flag_to_target("inspect" "-s WASM=1") +- append_compiler_flag("-s WASM=1") ++ append_compiler_flag("-O3 -s WASM=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0") + endif() + endif() + +diff --git a/third_party/aom/aom/src/aom_codec.c b/third_party/aom/aom/src/aom_codec.c +index dbd6fa5..a8d2a49 100644 +--- a/third_party/aom/aom/src/aom_codec.c ++++ b/third_party/aom/aom/src/aom_codec.c +@@ -132,6 +132,7 @@ void aom_internal_error(struct aom_internal_error_info *info, + info->detail[sz - 1] = '\0'; + } + ++ printf("##aom internal error: %s\n", info->detail); + if (info->setjmp) longjmp(info->jmp, info->error_code); + } + +diff --git a/third_party/aom/aom_dsp/grain_table.c b/third_party/aom/aom_dsp/grain_table.c +index 0d6a73f..4b05833 100644 +--- a/third_party/aom/aom_dsp/grain_table.c ++++ b/third_party/aom/aom_dsp/grain_table.c +@@ -293,6 +293,9 @@ aom_codec_err_t aom_film_grain_table_read( + return error_info->error_code; + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + aom_codec_err_t aom_film_grain_table_write( + const aom_film_grain_table_t *t, const char *filename, + struct aom_internal_error_info *error_info) { +@@ -305,7 +308,7 @@ aom_codec_err_t aom_film_grain_table_write( + return error_info->error_code; + } + +- if (!fwrite(kFileMagic, 8, 1, file)) { ++ if (!emcc_fwrite(kFileMagic, 8, 1, file)) { + aom_internal_error(error_info, AOM_CODEC_ERROR, + "Unable to write file magic"); + fclose(file); +diff --git a/third_party/aom/aomdec.c b/third_party/aom/aomdec.c +index 4addee8..f850147 100644 +--- a/third_party/aom/aomdec.c ++++ b/third_party/aom/aomdec.c +@@ -274,6 +274,9 @@ static void update_image_md5(const aom_image_t *img, const int planes[3], + } + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + static void write_image_file(const aom_image_t *img, const int *planes, + const int num_planes, FILE *file) { + int i, y; +@@ -287,7 +290,7 @@ static void write_image_file(const aom_image_t *img, const int *planes, + const int h = aom_img_plane_height(img, plane); + + for (y = 0; y < h; ++y) { +- fwrite(buf, bytes_per_sample, w, file); ++ emcc_fwrite(buf, bytes_per_sample, w, file); + buf += stride; + } + } +diff --git a/third_party/aom/aomenc.c b/third_party/aom/aomenc.c +index 64155b0..3ed5080 100644 +--- a/third_party/aom/aomenc.c ++++ b/third_party/aom/aomenc.c +@@ -59,9 +59,12 @@ static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { + } + #define fread wrap_fread + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + static size_t wrap_fwrite(const void *ptr, size_t size, size_t nmemb, + FILE *stream) { +- return fwrite(ptr, size, nmemb, stream); ++ return emcc_fwrite(ptr, size, nmemb, stream); + } + #define fwrite wrap_fwrite + +diff --git a/third_party/aom/aomstats.c b/third_party/aom/aomstats.c +index 0cfeea2..6833776 100644 +--- a/third_party/aom/aomstats.c ++++ b/third_party/aom/aomstats.c +@@ -80,9 +80,12 @@ void stats_close(stats_io_t *stats, int last_pass) { + } + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + void stats_write(stats_io_t *stats, const void *pkt, size_t len) { + if (stats->file) { +- (void)fwrite(pkt, 1, len, stats->file); ++ (void)emcc_fwrite(pkt, 1, len, stats->file); + } else { + if (stats->buf.sz + len > stats->buf_alloc_sz) { + size_t new_sz = stats->buf_alloc_sz + 64 * 1024; +diff --git a/third_party/aom/av1/common/debugmodes.c b/third_party/aom/av1/common/debugmodes.c +index 868f341..c44258c 100644 +--- a/third_party/aom/av1/common/debugmodes.c ++++ b/third_party/aom/av1/common/debugmodes.c +@@ -89,10 +89,13 @@ void av1_print_modes_and_motion_vectors(AV1_COMMON *cm, const char *file) { + fclose(mvs); + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + void av1_print_uncompressed_frame_header(const uint8_t *data, int size, + const char *filename) { + FILE *hdrFile = fopen(filename, "w"); +- fwrite(data, size, sizeof(uint8_t), hdrFile); ++ emcc_fwrite(data, size, sizeof(uint8_t), hdrFile); + fclose(hdrFile); + } + +diff --git a/third_party/aom/av1/encoder/encoder.c b/third_party/aom/av1/encoder/encoder.c +index a557380..d709d26 100644 +--- a/third_party/aom/av1/encoder/encoder.c ++++ b/third_party/aom/av1/encoder/encoder.c +@@ -2799,6 +2799,9 @@ AV1_COMP *av1_create_compressor(AV1EncoderConfig *oxcf, + snprintf((H) + strlen(H), sizeof(H) - strlen(H), (T), (V)) + #endif // CONFIG_INTERNAL_STATS + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + void av1_remove_compressor(AV1_COMP *cpi) { + AV1_COMMON *cm; + unsigned int i; +@@ -2814,7 +2817,7 @@ void av1_remove_compressor(AV1_COMP *cpi) { + if (cpi->oxcf.pass != 1) { + fprintf(stderr, "Writing counts.stt\n"); + FILE *f = fopen("counts.stt", "wb"); +- fwrite(&aggregate_fc, sizeof(aggregate_fc), 1, f); ++ emcc_fwrite(&aggregate_fc, sizeof(aggregate_fc), 1, f); + fclose(f); + } + #endif // CONFIG_ENTROPY_STATS +@@ -3013,7 +3016,7 @@ void aom_write_yuv_frame_420(YV12_BUFFER_CONFIG *s, FILE *f) { + int h = s->y_height; + + do { +- fwrite(src, s->y_width, 1, f); ++ emcc_fwrite(src, s->y_width, 1, f); + src += s->y_stride; + } while (--h); + +@@ -3021,7 +3024,7 @@ void aom_write_yuv_frame_420(YV12_BUFFER_CONFIG *s, FILE *f) { + h = s->uv_height; + + do { +- fwrite(src, s->uv_width, 1, f); ++ emcc_fwrite(src, s->uv_width, 1, f); + src += s->uv_stride; + } while (--h); + +@@ -3029,7 +3032,7 @@ void aom_write_yuv_frame_420(YV12_BUFFER_CONFIG *s, FILE *f) { + h = s->uv_height; + + do { +- fwrite(src, s->uv_width, 1, f); ++ emcc_fwrite(src, s->uv_width, 1, f); + src += s->uv_stride; + } while (--h); + } +@@ -3121,7 +3124,7 @@ void aom_write_one_yuv_frame(AV1_COMMON *cm, YV12_BUFFER_CONFIG *s) { + uint16_t *src16 = CONVERT_TO_SHORTPTR(s->y_buffer); + + do { +- fwrite(src16, s->y_width, 2, yuv_rec_file); ++ emcc_fwrite(src16, s->y_width, 2, yuv_rec_file); + src16 += s->y_stride; + } while (--h); + +@@ -3129,7 +3132,7 @@ void aom_write_one_yuv_frame(AV1_COMMON *cm, YV12_BUFFER_CONFIG *s) { + h = s->uv_height; + + do { +- fwrite(src16, s->uv_width, 2, yuv_rec_file); ++ emcc_fwrite(src16, s->uv_width, 2, yuv_rec_file); + src16 += s->uv_stride; + } while (--h); + +@@ -3137,7 +3140,7 @@ void aom_write_one_yuv_frame(AV1_COMMON *cm, YV12_BUFFER_CONFIG *s) { + h = s->uv_height; + + do { +- fwrite(src16, s->uv_width, 2, yuv_rec_file); ++ emcc_fwrite(src16, s->uv_width, 2, yuv_rec_file); + src16 += s->uv_stride; + } while (--h); + +@@ -3146,7 +3149,7 @@ void aom_write_one_yuv_frame(AV1_COMMON *cm, YV12_BUFFER_CONFIG *s) { + } + + do { +- fwrite(src, s->y_width, 1, yuv_rec_file); ++ emcc_fwrite(src, s->y_width, 1, yuv_rec_file); + src += s->y_stride; + } while (--h); + +@@ -3154,7 +3157,7 @@ void aom_write_one_yuv_frame(AV1_COMMON *cm, YV12_BUFFER_CONFIG *s) { + h = s->uv_height; + + do { +- fwrite(src, s->uv_width, 1, yuv_rec_file); ++ emcc_fwrite(src, s->uv_width, 1, yuv_rec_file); + src += s->uv_stride; + } while (--h); + +@@ -3162,7 +3165,7 @@ void aom_write_one_yuv_frame(AV1_COMMON *cm, YV12_BUFFER_CONFIG *s) { + h = s->uv_height; + + do { +- fwrite(src, s->uv_width, 1, yuv_rec_file); ++ emcc_fwrite(src, s->uv_width, 1, yuv_rec_file); + src += s->uv_stride; + } while (--h); + +@@ -3241,16 +3244,16 @@ static int dump_one_image(AV1_COMMON *cm, + + // --- Y --- + for (h = 0; h < cm->height; ++h) { +- fwrite(&ref_buf->y_buffer[h * ref_buf->y_stride], 1, cm->width, f_ref); ++ emcc_fwrite(&ref_buf->y_buffer[h * ref_buf->y_stride], 1, cm->width, f_ref); + } + // --- U --- + for (h = 0; h < (cm->height >> 1); ++h) { +- fwrite(&ref_buf->u_buffer[h * ref_buf->uv_stride], 1, (cm->width >> 1), ++ emcc_fwrite(&ref_buf->u_buffer[h * ref_buf->uv_stride], 1, (cm->width >> 1), + f_ref); + } + // --- V --- + for (h = 0; h < (cm->height >> 1); ++h) { +- fwrite(&ref_buf->v_buffer[h * ref_buf->uv_stride], 1, (cm->width >> 1), ++ emcc_fwrite(&ref_buf->v_buffer[h * ref_buf->uv_stride], 1, (cm->width >> 1), + f_ref); + } + +@@ -4692,17 +4695,17 @@ static void dump_filtered_recon_frames(AV1_COMP *cpi) { + + // --- Y --- + for (h = 0; h < cm->height; ++h) { +- fwrite(&recon_buf->y_buffer[h * recon_buf->y_stride], 1, cm->width, ++ emcc_fwrite(&recon_buf->y_buffer[h * recon_buf->y_stride], 1, cm->width, + f_recon); + } + // --- U --- + for (h = 0; h < (cm->height >> 1); ++h) { +- fwrite(&recon_buf->u_buffer[h * recon_buf->uv_stride], 1, (cm->width >> 1), ++ emcc_fwrite(&recon_buf->u_buffer[h * recon_buf->uv_stride], 1, (cm->width >> 1), + f_recon); + } + // --- V --- + for (h = 0; h < (cm->height >> 1); ++h) { +- fwrite(&recon_buf->v_buffer[h * recon_buf->uv_stride], 1, (cm->width >> 1), ++ emcc_fwrite(&recon_buf->v_buffer[h * recon_buf->uv_stride], 1, (cm->width >> 1), + f_recon); + } + +diff --git a/third_party/aom/av1/encoder/firstpass.c b/third_party/aom/av1/encoder/firstpass.c +index bb73fde..b963043 100644 +--- a/third_party/aom/av1/encoder/firstpass.c ++++ b/third_party/aom/av1/encoder/firstpass.c +@@ -476,6 +476,9 @@ static double raw_motion_error_stdev(int *raw_motion_err_list, + return raw_err_stdev; + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + #define UL_INTRA_THRESH 50 + #define INVALID_ROW -1 + void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) { +@@ -1077,7 +1080,7 @@ void av1_first_pass(AV1_COMP *cpi, const struct lookahead_entry *source) { + else + recon_file = fopen(filename, "ab"); + +- (void)fwrite(lst_yv12->buffer_alloc, lst_yv12->frame_size, 1, recon_file); ++ (void)emcc_fwrite(lst_yv12->buffer_alloc, lst_yv12->frame_size, 1, recon_file); + fclose(recon_file); + } + +diff --git a/third_party/aom/build/cmake/aom_configure.cmake b/third_party/aom/build/cmake/aom_configure.cmake +index 9220a32..fb8bf9f 100644 +--- a/third_party/aom/build/cmake/aom_configure.cmake ++++ b/third_party/aom/build/cmake/aom_configure.cmake +@@ -260,7 +260,7 @@ if(MSVC) + add_compiler_flag_if_supported("/WX") + endif() + else() +- require_c_flag("-std=c99" YES) ++ #require_c_flag("-std=c99" YES) + add_compiler_flag_if_supported("-Wall") + add_compiler_flag_if_supported("-Wdisabled-optimization") + add_compiler_flag_if_supported("-Wextra") +diff --git a/third_party/aom/examples/resize_util.c b/third_party/aom/examples/resize_util.c +index 5485691..e60ed86 100644 +--- a/third_party/aom/examples/resize_util.c ++++ b/third_party/aom/examples/resize_util.c +@@ -45,6 +45,9 @@ static int parse_dim(char *v, int *width, int *height) { + return 1; + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + int main(int argc, char *argv[]) { + char *fin, *fout; + FILE *fpin, *fpout; +@@ -111,7 +114,7 @@ int main(int argc, char *argv[]) { + av1_resize_frame420(inbuf, width, inbuf_u, inbuf_v, width / 2, height, + width, outbuf, target_width, outbuf_u, outbuf_v, + target_width / 2, target_height, target_width); +- fwrite(outbuf, target_width * target_height * 3 / 2, 1, fpout); ++ emcc_fwrite(outbuf, target_width * target_height * 3 / 2, 1, fpout); + f++; + } + printf("%d frames processed\n", f); +diff --git a/third_party/aom/examples/scalable_encoder.c b/third_party/aom/examples/scalable_encoder.c +index 10d647e..fcf31e1 100644 +--- a/third_party/aom/examples/scalable_encoder.c ++++ b/third_party/aom/examples/scalable_encoder.c +@@ -91,6 +91,9 @@ void usage_exit(void) { + exit(EXIT_FAILURE); + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img, + int frame_index, int flags, FILE *outfile) { + int got_pkts = 0; +@@ -105,7 +108,7 @@ static int encode_frame(aom_codec_ctx_t *codec, aom_image_t *img, + + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + const int keyframe = (pkt->data.frame.flags & AOM_FRAME_IS_KEY) != 0; +- if (fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile) != ++ if (emcc_fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile) != + pkt->data.frame.sz) { + die_codec(codec, "Failed to write compressed frame"); + } +diff --git a/third_party/aom/ivfenc.c b/third_party/aom/ivfenc.c +index 80f4d14..d0e4e34 100644 +--- a/third_party/aom/ivfenc.c ++++ b/third_party/aom/ivfenc.c +@@ -14,6 +14,9 @@ + #include "aom/aom_encoder.h" + #include "aom_ports/mem_ops.h" + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + void ivf_write_file_header(FILE *outfile, const struct aom_codec_enc_cfg *cfg, + unsigned int fourcc, int frame_cnt) { + char header[32]; +@@ -32,7 +35,7 @@ void ivf_write_file_header(FILE *outfile, const struct aom_codec_enc_cfg *cfg, + mem_put_le32(header + 24, frame_cnt); // length + mem_put_le32(header + 28, 0); // unused + +- fwrite(header, 1, 32, outfile); ++ emcc_fwrite(header, 1, 32, outfile); + } + + void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size) { +@@ -41,12 +44,12 @@ void ivf_write_frame_header(FILE *outfile, int64_t pts, size_t frame_size) { + mem_put_le32(header, (int)frame_size); + mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF)); + mem_put_le32(header + 8, (int)(pts >> 32)); +- fwrite(header, 1, 12, outfile); ++ emcc_fwrite(header, 1, 12, outfile); + } + + void ivf_write_frame_size(FILE *outfile, size_t frame_size) { + char header[4]; + + mem_put_le32(header, (int)frame_size); +- fwrite(header, 1, 4, outfile); ++ emcc_fwrite(header, 1, 4, outfile); + } +diff --git a/third_party/aom/test/decode_perf_test.cc b/third_party/aom/test/decode_perf_test.cc +index 3c93e7d..2d364ae 100644 +--- a/third_party/aom/test/decode_perf_test.cc ++++ b/third_party/aom/test/decode_perf_test.cc +@@ -24,6 +24,11 @@ + + using ::testing::make_tuple; + ++extern "C" { ++ size_t ++ emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++} ++ + namespace { + + #define VIDEO_NAME 0 +@@ -153,7 +158,7 @@ class AV1NewEncodeDecodePerfTest + + // Write frame header and data. + ivf_write_frame_header(outfile_, out_frames_, pkt->data.frame.sz); +- ASSERT_EQ(fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_), ++ ASSERT_EQ(emcc_fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_), + pkt->data.frame.sz); + } + +diff --git a/third_party/aom/test/film_grain_table_test.cc b/third_party/aom/test/film_grain_table_test.cc +index 0688146..dbb8e6b 100644 +--- a/third_party/aom/test/film_grain_table_test.cc ++++ b/third_party/aom/test/film_grain_table_test.cc +@@ -5,6 +5,11 @@ + #include "av1/encoder/grain_test_vectors.h" + #include "test/video_source.h" + ++extern "C" { ++ size_t ++ emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++} ++ + void grain_equal(const aom_film_grain_t *expected, + const aom_film_grain_t *actual) { + EXPECT_EQ(expected->apply_grain, actual->apply_grain); +@@ -168,7 +173,7 @@ TEST_F(FilmGrainTableIOTest, ReadTruncatedFile) { + + std::string grain_file; + FILE *file = libaom_test::GetTempOutFile(&grain_file); +- fwrite("deadbeef", 8, 1, file); ++ emcc_fwrite("deadbeef", 8, 1, file); + fclose(file); + ASSERT_EQ(AOM_CODEC_ERROR, + aom_film_grain_table_read(&table, grain_file.c_str(), &error_)); +diff --git a/third_party/aom/test/resize_test.cc b/third_party/aom/test/resize_test.cc +index e1c4e9f..9c2bce8 100644 +--- a/third_party/aom/test/resize_test.cc ++++ b/third_party/aom/test/resize_test.cc +@@ -22,6 +22,11 @@ + // Enable(1) or Disable(0) writing of the compressed bitstream. + #define WRITE_COMPRESSED_STREAM 0 + ++extern "C" { ++ size_t ++ emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++} ++ + namespace { + + #if WRITE_COMPRESSED_STREAM +@@ -55,13 +60,13 @@ static void write_ivf_file_header(const aom_codec_enc_cfg_t *const cfg, + mem_put_le32(header + 24, frame_cnt); /* length */ + mem_put_le32(header + 28, 0); /* unused */ + +- (void)fwrite(header, 1, 32, outfile); ++ (void)emcc_fwrite(header, 1, 32, outfile); + } + + static void write_ivf_frame_size(FILE *const outfile, const size_t size) { + char header[4]; + mem_put_le32(header, static_cast(size)); +- (void)fwrite(header, 1, 4, outfile); ++ (void)emcc_fwrite(header, 1, 4, outfile); + } + + static void write_ivf_frame_header(const aom_codec_cx_pkt_t *const pkt, +@@ -76,7 +81,7 @@ static void write_ivf_frame_header(const aom_codec_cx_pkt_t *const pkt, + mem_put_le32(header + 4, pts & 0xFFFFFFFF); + mem_put_le32(header + 8, pts >> 32); + +- (void)fwrite(header, 1, 12, outfile); ++ (void)emcc_fwrite(header, 1, 12, outfile); + } + #endif // WRITE_COMPRESSED_STREAM + +@@ -309,7 +314,7 @@ class ResizeInternalTestLarge : public ResizeTest { + + // Write frame header and data. + write_ivf_frame_header(pkt, outfile_); +- (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_); ++ (void)emcc_fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_); + } + #endif + +@@ -608,7 +613,7 @@ class ResizeCspTest : public ResizeTest { + + // Write frame header and data. + write_ivf_frame_header(pkt, outfile_); +- (void)fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_); ++ (void)emcc_fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile_); + } + #endif + +diff --git a/third_party/aom/test/y4m_test.cc b/third_party/aom/test/y4m_test.cc +index ad901d9..f24093f 100644 +--- a/third_party/aom/test/y4m_test.cc ++++ b/third_party/aom/test/y4m_test.cc +@@ -19,6 +19,11 @@ + #include "test/util.h" + #include "test/y4m_video_source.h" + ++extern "C" { ++ size_t ++ emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++} ++ + namespace { + + using std::string; +@@ -68,7 +73,7 @@ static void write_image_file(const aom_image_t *img, FILE *file) { + (plane ? (img->d_w + img->x_chroma_shift) >> img->x_chroma_shift + : img->d_w); + for (y = 0; y < h; ++y) { +- fwrite(buf, bytes_per_sample, w, file); ++ emcc_fwrite(buf, bytes_per_sample, w, file); + buf += stride; + } + } +diff --git a/third_party/aom/third_party/googletest/src/googletest/src/gtest.cc b/third_party/aom/third_party/googletest/src/googletest/src/gtest.cc +index 5a8932c..ac2c435 100644 +--- a/third_party/aom/third_party/googletest/src/googletest/src/gtest.cc ++++ b/third_party/aom/third_party/googletest/src/googletest/src/gtest.cc +@@ -146,6 +146,11 @@ + # define vsnprintf _vsnprintf + #endif // GTEST_OS_WINDOWS + ++extern "C" { ++ size_t ++ emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++} ++ + namespace testing { + + using internal::CountIf; +@@ -3867,7 +3872,7 @@ class ScopedPrematureExitFile { + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); +- fwrite("0", 1, 1, pfile); ++ emcc_fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } +diff --git a/third_party/aom/third_party/libwebm/mkvmuxer/mkvwriter.cc b/third_party/aom/third_party/libwebm/mkvmuxer/mkvwriter.cc +index 84655d8..0004093 100644 +--- a/third_party/aom/third_party/libwebm/mkvmuxer/mkvwriter.cc ++++ b/third_party/aom/third_party/libwebm/mkvmuxer/mkvwriter.cc +@@ -14,6 +14,11 @@ + #include // for _SH_DENYWR + #endif + ++extern "C" { ++ size_t ++ emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++} ++ + namespace mkvmuxer { + + MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {} +@@ -32,7 +37,7 @@ int32 MkvWriter::Write(const void* buffer, uint32 length) { + if (buffer == NULL) + return -1; + +- const size_t bytes_written = fwrite(buffer, 1, length, file_); ++ const size_t bytes_written = emcc_fwrite(buffer, 1, length, file_); + + return (bytes_written == length) ? 0 : -1; + } +diff --git a/third_party/aom/tools_common.c b/third_party/aom/tools_common.c +index 7abc20c..fbc30bc 100644 +--- a/third_party/aom/tools_common.c ++++ b/third_party/aom/tools_common.c +@@ -185,6 +185,9 @@ const AvxInterface *get_aom_decoder_by_fourcc(uint32_t fourcc) { + } + #endif // CONFIG_AV1_DECODER + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + void aom_img_write(const aom_image_t *img, FILE *file) { + int plane; + +@@ -197,7 +200,7 @@ void aom_img_write(const aom_image_t *img, FILE *file) { + int y; + + for (y = 0; y < h; ++y) { +- fwrite(buf, 1, w, file); ++ emcc_fwrite(buf, 1, w, file); + buf += stride; + } + } +diff --git a/third_party/aom/video_writer.c b/third_party/aom/video_writer.c +index 4e072c7..6b1ca54 100644 +--- a/third_party/aom/video_writer.c ++++ b/third_party/aom/video_writer.c +@@ -66,10 +66,13 @@ void aom_video_writer_close(AvxVideoWriter *writer) { + } + } + ++size_t ++emcc_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ++ + int aom_video_writer_write_frame(AvxVideoWriter *writer, const uint8_t *buffer, + size_t size, int64_t pts) { + ivf_write_frame_header(writer->file, pts, size); +- if (fwrite(buffer, 1, size, writer->file) != size) return 0; ++ if (emcc_fwrite(buffer, 1, size, writer->file) != size) return 0; + + ++writer->frame_count; + diff --git a/test-tools/.gitignore b/test-tools/.gitignore new file mode 100644 index 0000000000..6aa8dc0ee2 --- /dev/null +++ b/test-tools/.gitignore @@ -0,0 +1 @@ +/wasi-sdk diff --git a/test-tools/IoT-APP-Store-Demo/README.md b/test-tools/IoT-APP-Store-Demo/README.md deleted file mode 100644 index 06d17a6592..0000000000 --- a/test-tools/IoT-APP-Store-Demo/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# IoT Application Store -Wasm application management portal for WAMR - -# Requirement -Install django with pip3 -``` -pip3 install django -``` - -# Run -1. Start wasm server - ``` - cd wasm_django/server - python3 wasm_server.py - ``` - -2. Start IoT application management web portal - ``` - cd wasm_django - python3 manage.py runserver 0.0.0.0:80 - ``` - -3. Download WAMR runtime from [help](http://localhost/help/) page - > NOTE: You need to start web server according to *step 2* before accessing this link! - -4. Start a WAMR runtime from localhost - ``` - ./simple - ``` - or from other computers - ``` - ./simple -a [your.server.ip.address] - ``` - -# Online demo - http://39.106.110.7/ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/db.sqlite3 b/test-tools/IoT-APP-Store-Demo/wasm_django/db.sqlite3 deleted file mode 100755 index 211576ca3e..0000000000 Binary files a/test-tools/IoT-APP-Store-Demo/wasm_django/db.sqlite3 and /dev/null differ diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/admin.py b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/admin.py deleted file mode 100755 index 8c38f3f3da..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/apps.py b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/apps.py deleted file mode 100755 index d43cc4b66c..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class DevicesConfig(AppConfig): - name = 'devices' diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/models.py b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/models.py deleted file mode 100755 index 71a8362390..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/application.html b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/application.html deleted file mode 100644 index 0b2ea7faf4..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/application.html +++ /dev/null @@ -1,141 +0,0 @@ - - -{% load static %} - - - - - - - Wasm-Micro-Runtime - - - - - - - - - - -
- -
-

WebAssembly Micro Runtime - APP Store Demo

-
-
- - - -
-
-
-
-

- -

-
-
IP :
-
Port :
-
Installed apps :
-
-
-
- - - - - -
-

app is downloading now

-
-
-
-
- - -
-
-
×
-
HOT Applications
- -
-
- -

Product Name:

-

Current Version:

- -
-
-
-
- - -
-

List of Installed Apps:

- -
- - -
-
- -
Product Name:
-
Staus:
-
Current Version:
-
-
-
- -
Copyright© intel.com
- - - - - - - - - - - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html deleted file mode 100644 index 46ecedf15c..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html +++ /dev/null @@ -1,98 +0,0 @@ - - -{% load static %} - - - - - - - - Wasm-Micro-Runtime - - - - - - - - - - -
-
- -
-
-

WebAssembly Micro Runtime - APP Store Demo

-
-
- - - - - -
-
-
-
The products
-
Application List
-
-
- {%csrf_token%} -
- - Choose File -
-
- -
-
-
-
-
-
-
-

Product Name:

-

Product Version:

-

Preloaded Apps

- -
-
-
-
-
- - -
- Copyright© intel.com -
- - - - - - - - - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html deleted file mode 100644 index 5610a2d846..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - -wasm-micro-runtime - - - - - - - - - - -
-

404

-

Server Not Found

-

Github

-
- - - - - - - - - - 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 deleted file mode 100755 index bc834634ce..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html +++ /dev/null @@ -1,102 +0,0 @@ - - -{% load static %} - - - - - - - Wasm-Micro-Runtime - - - - - - - - - - - - -
-
-
-
-

- How to use? -

-

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

-

- 2. In the terminal: cd ~/Download && ./simple -a 39.106.110.7 -

-
-
- -
- Notes: -
We also have a UI-enabled runtime, please 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

-

For more details please refer to this guide -

-

cd ~/Download && ./wasm_runtime_wgl -a 39.106.110.7

-
-
-

- 3. Return to device page, find your device according to the IP address and click it, you - will enter application installation page -

-

- 4. In the application installation page, click the Install Application button, and chose an - app to install. (The "ui_app" is only for UI_enabled_runtimes, simple runtime can't install - this app) -

-

- 5. If you want to upload a new application, go to App Store page, choose a file and click - upload -

-

- Go Back - Download - simple_runtime - Download - UI_enabled_runtime -

-
-
-
-
-

Like this project?

-

Join us and build a powerful and interesting world for embedded - devices!

- - -

- View - on GitHub -

-
-
-
-
-
-
- - - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html deleted file mode 100644 index 3832791d14..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html +++ /dev/null @@ -1,91 +0,0 @@ - - -{% load static %} - - - - - - - Wasm-Micro-Runtime - - - - - - - - - - -
-
- -
-
-

WebAssembly Micro Runtime - APP Store Demo

-
-
- -
-
-
-
-

- -

-
-

The devices

-
-
- -
- - -
-
-
-
-

- -

-
-
IP :
-
Port :
-
Installed apps :
-
-
-

- -

-
-
-
- - - - -
- Copyright© intel.com -
- - - - - - - - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py deleted file mode 100755 index 7ce503c2dd..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py b/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py deleted file mode 100755 index cc11a27a00..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py +++ /dev/null @@ -1,273 +0,0 @@ -''' - /* Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -''' - -# _*_ -from django.shortcuts import render, render_to_response -from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound -import json -import socket -import os - -# Create your views here. - - -avaliable_list = [ - {'ID': 'timer', 'Version': '1.0'}, - {'ID': 'connection', 'Version': '1.0'}, - {'ID': 'event_publisher', 'Version': '3.0'}, - {'ID': 'event_subscriber', 'Version': '1.0'}, - {'ID': 'request_handler', 'Version': '1.0'}, - {'ID': 'sensor', 'Version': '1.0'}, - {'ID': 'ui_app', 'Version': '1.0'} -] - -# Help -def help(req): -# return "Help" page - return render(req, "help.html") - -# View -def index(req): - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - host = '127.0.0.1' - port = 8889 - msg = "" - err = "" - - try: - s.connect((host, port)) - s.send(bytes("query:all", encoding='utf8')) - s.settimeout(10) - msg = s.recv(1024) - except socket.timeout as e: - err = "empty" - print("no client connected") - except socket.error as e: - err = "refused" - print("server not started") - - s.close() - - device_list = [] - if msg != "": - devices = msg.decode('utf-8').split("*") - for dev in devices: - dev_info = eval(dev) - addr = dev_info['addr'] - port = dev_info['port'] - apps = dev_info['num'] - device_list.append({'IP': addr, 'Port': port, 'apps': apps}) - else: - if err == "refused": - return render(req, "empty.html") - - dlist = device_list - - return render(req, 'mysite.html', {'dlist': json.dumps(dlist)}) - - -def apps(req): - open_status = '' - search_node = [] - if req.method == "POST": - dev_search = req.POST['mykey'] - dev_addr = req.POST['voip'] - dev_port = req.POST['voport'] - open_status = 'open' - for i in avaliable_list: - if i['ID'] == dev_search: - search_node = [{'ID':dev_search, 'Version': '1.0'}] - print("search_node:",search_node) - break - else: - search_node = ["Nothing find"] - print( "final:",search_node) - else: - dev_addr = req.GET['ip'] - dev_port = req.GET['port'] - open_status = 'close' - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - host = '127.0.0.1' - port = 8889 - msg = "" - err = "" - - try: - s.connect((host, port)) - s.send(bytes("query:"+dev_addr+":"+str(dev_port), encoding='utf8')) - msg = s.recv(1024) - except socket.error as e: - print("unable to connect to server") - msg = b"fail" - s.close() - - app_list = [] - - if msg != "": - if msg.decode() == "fail": - return render(req, "empty.html") - else: - dic = eval(msg.decode(encoding='utf8')) - app_num = dic["num"] - for i in range(app_num): - app_list.append( - {'pname': dic["applet"+str(i+1)], 'status': 'Installed', 'current_version': '1.0'}) - - alist = app_list - device_info = [] - device_info.append( - {'IP': dev_addr, 'Port': str(dev_port), 'apps': app_num}) - - print(device_info) - return render(req, 'application.html', {'alist': json.dumps(alist), 'dlist': json.dumps(device_info), 'llist': json.dumps(avaliable_list), - "open_status":json.dumps(open_status),"search_node": json.dumps(search_node),}) - - -def appDownload(req): - dev_addr = req.GET['ip'] - dev_port = req.GET['port'] - app_name = req.GET['name'] - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - host = '127.0.0.1' - port = 8889 - msg = "" - - app_path = os.path.abspath(os.path.join(os.getcwd(), "static", "upload")) - if app_path[-1] != '/': - app_path += '/' - - try: - s.connect((host, port)) - s.send(bytes("install:"+dev_addr+":"+str(dev_port)+":"+app_name + - ":"+app_path + app_name + ".wasm", encoding='utf8')) - msg = s.recv(1024) - except socket.error as e: - print("unable to connect to server") - s.close() - - success = "ok" - fail = "Fail!" - status = [success, fail] - print(msg) - if msg == b"fail": - return HttpResponse(json.dumps({ - "status": fail - })) - elif msg == b"success": - return HttpResponse(json.dumps({ - "status": success - })) - else: - return HttpResponse(json.dumps({ - "status": eval(msg.decode())["error message"].split(':')[1] - })) - - -def appDelete(req): - dev_addr = req.GET['ip'] - dev_port = req.GET['port'] - app_name = req.GET['name'] - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - host = '127.0.0.1' - port = 8889 - s.connect((host, port)) - s.send(bytes("uninstall:"+dev_addr+":" + - str(dev_port)+":"+app_name, encoding='utf8')) - msg = s.recv(1024) - s.close() - r = HttpResponse("ok") - return r - -static_list = [{'ID': 'timer', 'Version': '1.0'}, {'ID': 'connection', 'Version': '1.0'}, {'ID': 'event_publisher', 'Version': '3.0'}, { - 'ID': 'event_subscriber', 'Version': '1.0'}, {'ID': 'reuqest_handler', 'Version': '1.0'}, {'ID': 'sensor', 'Version': '1.0'}, {'ID': 'ui_app', 'Version': '1.0'}] - -def store(req): - - store_path = os.path.join('static', 'upload') - status = [] - - print(user_file_list) - return render(req, 'appstore.html', {'staticlist': json.dumps(static_list), 'flist': json.dumps(user_file_list),'ulist':json.dumps(status)}) - -user_file_list = [] -files_list = [] -def uploadapps(req): - status = [] - local_list = ['timer','connection','event_publisher','event_subscriber','reuqest_handler','sensor'] - req.encoding = 'utf-8' - if req.method == 'POST': - myfile = req.FILES.get("myfile", None) - obj = req.FILES.get('myfile') - store_path = os.path.join('static', 'upload') - file_path = os.path.join('static', 'upload', obj.name) - - if not os.path.exists(store_path): - os.makedirs(store_path) - - file_name = obj.name.split(".")[0] - file_prefix = obj.name.split(".")[-1] - - - if file_prefix != "wasm": - status = ["Not a wasm file"] - elif file_name in local_list: - status = ["This App is preloaded"] - elif file_name in files_list: - status = ["This App is already uploaded"] - else: - status = [] - avaliable_list.append({'ID': file_name, 'Version': '1.0'}) - user_file_list.append({'ID': file_name, 'Version': '1.0'}) - files_list.append(file_name) - - print(user_file_list) - f = open(file_path, 'wb') - for chunk in obj.chunks(): - f.write(chunk) - f.close() - return render(req, 'appstore.html', {'staticlist': json.dumps(static_list), 'flist': json.dumps(user_file_list),'ulist':json.dumps(status)}) - -appname_list = [] - -def addapps(request): - types = '' - print("enter addapps") - request.encoding = 'utf-8' - app_dic = {'ID': '', 'Version': ''} - - # if request.method == 'get': - if "NAME" in request.GET: - a_name = request.GET['NAME'] - if a_name != "" and a_name not in appname_list: - appname_list.append(a_name) - message = request.GET['NAME'] + request.GET['Version'] - app_dic['ID'] = request.GET['NAME'] - app_dic['Version'] = request.GET['Version'] - avaliable_list.append(app_dic) - else: - types = "Exist" - print(avaliable_list) - return render(request, 'appstore.html', {'alist': json.dumps(avaliable_list)}) - -def removeapps(req): - app_name = req.GET['name'] - app_version = req.GET['version'] - remove_app = {'ID': app_name, 'Version': app_version} - avaliable_list.remove(remove_app) - user_file_list.remove(remove_app) - files_list.remove(app_name) - return render(req, 'appstore.html', {'alist': json.dumps(avaliable_list),'flist': json.dumps(user_file_list)}) - -# Test -if __name__ == "__main__": - print(device_list[0]['IP']) - print(device['IP']) diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py b/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py deleted file mode 100755 index 341863cf62..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -"""Django's command-line utility for administrative tasks.""" -import os -import sys - - -def main(): - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') - try: - from django.core.management import execute_from_command_line - except ImportError as exc: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) from exc - execute_from_command_line(sys.argv) - - -if __name__ == '__main__': - main() diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py b/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py deleted file mode 100755 index 7eb3685c4c..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py +++ /dev/null @@ -1,136 +0,0 @@ -""" -Django settings for mysite project. - -Generated by 'django-admin startproject' using Django 2.2.2. - -For more information on this file, see -https://docs.djangoproject.com/en/2.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/2.2/ref/settings/ -""" - -import os -from django.conf.global_settings import STATIC_ROOT - -# Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '8m05#6yx5wcygj*a+v6+=-y(#o+(z58-3!epq$u@5)64!mmu8q' - -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True - -ALLOWED_HOSTS = ['*'] - - -# Application definition - -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - - 'devices', -] - -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] - -ROOT_URLCONF = 'mysite.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] - -WSGI_APPLICATION = 'mysite.wsgi.application' - - -# Database -# https://docs.djangoproject.com/en/2.2/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } -} - - -# Password validation -# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/2.2/topics/i18n/ - -LANGUAGE_CODE = 'en-us' - -TIME_ZONE = 'UTC' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = True - -APPEND_SLASH = False - - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/2.2/howto/static-files/ - -STATIC_URL = '/static/' -HERE = os.path.dirname(os.path.abspath(__file__)) -HERE = os.path.join(HERE,'../') -STATICFILES_DIRS = (os.path.join(HERE,'static/'),) -#STATICFILES_DIRS = (os.path.join(BASE_DIR,'static'),) -#STATIC_ROOT = (os.path.join(os.path.dirname(_file_),'static') -#templates -TEMPLATE_DIRS=[ - '/home/xujun/mysite/templates', -] - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py b/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py deleted file mode 100755 index 8a74b5509e..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py +++ /dev/null @@ -1,41 +0,0 @@ -#config:utf-8 - -"""mysite URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/2.2/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" -from django.contrib import admin -#from django.conf.urls import include,url -from django.urls import path,include -from devices import views as devices_views -#from login import views as login_views - - -urlpatterns = [ - - path('admin/', admin.site.urls), - path('',devices_views.index), - path('apps/',devices_views.apps), - path('appDownload/', devices_views.appDownload), - path('appDelete/', devices_views.appDelete), - path('appstore/',devices_views.store), -## path('apps/appstore/',devices_views.storeofdevic), -## path('search/',devices_views.search), - path('upload',devices_views.uploadapps), - path('removeapps/',devices_views.removeapps), - path('help/',devices_views.help), - -] - - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py b/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py deleted file mode 100755 index 45e28c9a19..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for mysite project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') - -application = get_wsgi_application() 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 deleted file mode 100755 index 5edeb90aa0..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py +++ /dev/null @@ -1,605 +0,0 @@ -''' - /* Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ -''' -import select -import socket -import queue -from time import sleep -import struct -import threading -import time -from ctypes import * -import json -import logging -import os - -attr_type_list = [ - "ATTR_NONE", - "ATTR_TYPE_SHORT", - "ATTR_TYPE_INT", - "ATTR_TYPE_INT64", - "ATTR_TYPE_BYTE", - "ATTR_TYPE_UINT16", - "ATTR_TYPE_FLOAT", - "ATTR_TYPE_DOUBLE", - "ATTR_TYPE_BOOLEAN", - "ATTR_TYPE_STRING", - "ATTR_TYPE_BYTEARRAY" -] - - -Phase_Non_Start = 0 -Phase_Leading = 1 -Phase_Type = 2 -Phase_Size = 3 -Phase_Payload = 4 - - - -class imrt_link_message(object): - def __init__(self): - self.leading = bytes([0x12, 0x34]) - self.phase = Phase_Non_Start - self.size_in_phase = 0 - self.message_type = bytes() - self.message_size = bytes() - self.payload = bytes() - self.msg = bytes() - - def set_recv_phase(self, phase): - self.phase = phase - - def on_imrt_link_byte_arrive(self, ch): - self.msg += ch - if self.phase == Phase_Non_Start: - if ch == b'\x12': - self.set_recv_phase(Phase_Leading) - else: - return -1 - elif self.phase == Phase_Leading: - if ch == b'\x34': - self.set_recv_phase(Phase_Type) - else: - self.set_recv_phase(Phase_Non_Start) - return -1 - elif self.phase == Phase_Type: - self.message_type += ch - self.size_in_phase += 1 - - if self.size_in_phase == 2: - (self.message_type, ) = struct.unpack('!H', self.message_type) - self.size_in_phase = 0 - self.set_recv_phase(Phase_Size) - elif self.phase == Phase_Size: - self.message_size += ch - self.size_in_phase += 1 - - if self.size_in_phase == 4: - (self.message_size, ) = struct.unpack('!I', self.message_size) - self.size_in_phase = 0 - self.set_recv_phase(Phase_Payload) - - if self.message_size == b'\x00': - self.set_recv_phase(Phase_Non_Start) - return 0 - - self.set_recv_phase(Phase_Payload) - - elif self.phase == Phase_Payload: - self.payload += ch - self.size_in_phase += 1 - - if self.size_in_phase == self.message_size: - self.set_recv_phase(Phase_Non_Start) - return 0 - - return 2 - - return 1 - - - -def read_file_to_buffer(file_name): - file_object = open(file_name, 'rb') - buffer = None - - if not os.path.exists(file_name): - logging.error("file {} not found.".format(file_name)) - return "file not found" - - try: - buffer = file_object.read() - finally: - file_object.close() - - return buffer - -def decode_attr_container(msg): - - attr_dict = {} - - buf = msg[26 : ] - (total_len, tag_len) = struct.unpack('@IH', buf[0 : 6]) - tag_name = buf[6 : 6 + tag_len].decode() - buf = buf[6 + tag_len : ] - (attr_num, ) = struct.unpack('@H', buf[0 : 2]) - buf = buf[2 : ] - - logging.info("parsed attr:") - logging.info("total_len:{}, tag_len:{}, tag_name:{}, attr_num:{}" - .format(str(total_len), str(tag_len), str(tag_name), str(attr_num))) - - for i in range(attr_num): - (key_len, ) = struct.unpack('@H', buf[0 : 2]) - key_name = buf[2 : 2 + key_len - 1].decode() - buf = buf[2 + key_len : ] - (type_index, ) = struct.unpack('@c', buf[0 : 1]) - - attr_type = attr_type_list[int(type_index[0])] - buf = buf[1 : ] - - if attr_type == "ATTR_TYPE_SHORT": - (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]) - 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]) - 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_FLOAT": - (attr_value, ) = struct.unpack('@f', buf[0 : 4]) - buf = buf[4 : ] - # continue - elif attr_type == "ATTR_TYPE_DOUBLE": - (attr_value, ) = struct.unpack('@d', buf[0 : 8]) - buf = buf[8 : ] - # continue - elif attr_type == "ATTR_TYPE_BOOLEAN": - (attr_value, ) = struct.unpack('@?', buf[0 : 1]) - buf = buf[1 : ] - # continue - elif attr_type == "ATTR_TYPE_STRING": - (str_len, ) = struct.unpack('@H', buf[0 : 2]) - attr_value = buf[2 : 2 + str_len - 1].decode() - buf = buf[2 + str_len : ] - # continue - elif attr_type == "ATTR_TYPE_BYTEARRAY": - (byte_len, ) = struct.unpack('@I', buf[0 : 4]) - attr_value = buf[4 : 4 + byte_len] - buf = buf[4 + byte_len : ] - # continue - - attr_dict[key_name] = attr_value - - logging.info(str(attr_dict)) - return attr_dict - -class Request(): - mid = 0 - url = "" - action = 0 - fmt = 0 - payload = "" - payload_len = 0 - sender = 0 - - def __init__(self, url, action, fmt, payload, payload_len): - self.url = url - self.action = action - self.fmt = fmt - # if type(payload) == bytes: - # self.payload = bytes(payload, encoding = "utf8") - # else: - self.payload_len = payload_len - if self.payload_len > 0: - self.payload = payload - - - def pack_request(self): - url_len = len(self.url) + 1 - buffer_len = url_len + self.payload_len - - req_buffer = struct.pack('!2BH2IHI',1, self.action, self.fmt, self.mid, self.sender, url_len, self.payload_len) - for i in range(url_len - 1): - req_buffer += struct.pack('!c', bytes(self.url[i], encoding = "utf8")) - req_buffer += bytes([0]) - for i in range(self.payload_len): - req_buffer += struct.pack('!B', self.payload[i]) - - return req_buffer, len(req_buffer) - - - def send(self, conn, is_install): - leading = struct.pack('!2B', 0x12, 0x34) - - if not is_install: - msg_type = struct.pack('!H', 0x0002) - else: - msg_type = struct.pack('!H', 0x0004) - buff, buff_len = self.pack_request() - lenth = struct.pack('!I', buff_len) - - try: - conn.send(leading) - conn.send(msg_type) - conn.send(lenth) - conn.send(buff) - except socket.error as e: - logging.error("device closed") - for dev in tcpserver.devices: - if dev.conn == conn: - tcpserver.devices.remove(dev) - return -1 - - -def query(conn): - req = Request("/applet", 1, 0, "", 0) - if req.send(conn, False) == -1: - return "fail" - time.sleep(0.05) - try: - receive_context = imrt_link_message() - start = time.time() - while True: - if receive_context.on_imrt_link_byte_arrive(conn.recv(1)) == 0: - break - elif time.time() - start >= 5.0: - return "fail" - query_resp = receive_context.msg - print(query_resp) - except OSError as e: - logging.error("OSError exception occur") - return "fail" - - res = decode_attr_container(query_resp) - - logging.info('Query device infomation success') - return res - -def install(conn, app_name, wasm_file): - wasm = read_file_to_buffer(wasm_file) - if wasm == "file not found": - return "failed to install: file not found" - - print("wasm file len:") - print(len(wasm)) - req = Request("/applet?name=" + app_name, 3, 98, wasm, len(wasm)) - if req.send(conn, True) == -1: - return "fail" - time.sleep(0.05) - try: - receive_context = imrt_link_message() - start = time.time() - while True: - if receive_context.on_imrt_link_byte_arrive(conn.recv(1)) == 0: - break - elif time.time() - start >= 5.0: - return "fail" - msg = receive_context.msg - except OSError as e: - logging.error("OSError exception occur") - # TODO: check return message - - if len(msg) == 24 and msg[8 + 1] == 65: - logging.info('Install application success') - return "success" - else: - res = decode_attr_container(msg) - logging.warning('Install application failed: %s' % (str(res))) - print(str(res)) - - return str(res) - - -def uninstall(conn, app_name): - req = Request("/applet?name=" + app_name, 4, 99, "", 0) - if req.send(conn, False) == -1: - return "fail" - time.sleep(0.05) - try: - receive_context = imrt_link_message() - start = time.time() - while True: - if receive_context.on_imrt_link_byte_arrive(conn.recv(1)) == 0: - break - elif time.time() - start >= 5.0: - return "fail" - msg = receive_context.msg - except OSError as e: - logging.error("OSError exception occur") - # TODO: check return message - - if len(msg) == 24 and msg[8 + 1] == 66: - logging.info('Uninstall application success') - return "success" - else: - res = decode_attr_container(msg) - logging.warning('Uninstall application failed: %s' % (str(res))) - print(str(res)) - - return str(res) - -class Device: - def __init__(self, conn, addr, port): - self.conn = conn - self.addr = addr - self.port = port - self.app_num = 0 - self.apps = [] - -cmd = [] - -class TCPServer: - def __init__(self, server, server_address, inputs, outputs, message_queues): - # Create a TCP/IP - self.server = server - self.server.setblocking(False) - - # Bind the socket to the port - self.server_address = server_address - print('starting up on %s port %s' % self.server_address) - self.server.bind(self.server_address) - - # Listen for incoming connections - self.server.listen(10) - - self.cmd_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.cmd_sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) - - self.cmd_sock.bind(('127.0.0.1', 8889)) - self.cmd_sock.listen(5) - - - # Sockets from which we expect to read - self.inputs = inputs - self.inputs.append(self.cmd_sock) - - # Sockets to which we expect to write - # 处理要发送的消息 - self.outputs = outputs - # Outgoing message queues (socket: Queue) - self.message_queues = message_queues - - self.devices = [] - self.conn_dict = {} - - def handler_recever(self, readable): - # Handle inputs - for s in readable: - if s is self.server: - # A "readable" socket is ready to accept a connection - connection, client_address = s.accept() - self.client_address = client_address - print('connection from', client_address) - # this is connection not server - # connection.setblocking(0) - self.inputs.append(connection) - - # Give the connection a queue for data we want to send - # self.message_queues[connection] = queue.Queue() - - res = query(connection) - - if res != "fail": - dev = Device(connection, client_address[0], client_address[1]) - self.devices.append(dev) - self.conn_dict[client_address] = connection - - dev_info = {} - dev_info['addr'] = dev.addr - dev_info['port'] = dev.port - dev_info['apps'] = 0 - - logging.info('A new client connected from ("%s":"%s")' % (dev.conn, dev.port)) - - elif s is self.cmd_sock: - connection, client_address = s.accept() - print("web server socket connected") - logging.info("Django server connected") - self.inputs.append(connection) - self.message_queues[connection] = queue.Queue() - - else: - data = s.recv(1024) - if data != b'': - # A readable client socket has data - logging.info('received "%s" from %s' % (data, s.getpeername())) - - # self.message_queues[s].put(data) - # # Add output channel for response - - # if s not in self.outputs: - # self.outputs.append(s) - - if(data.decode().split(':')[0] == "query"): - if data.decode().split(':')[1] == "all": - resp = [] - print('start query all devices') - for dev in self.devices: - dev_info = query(dev.conn) - if dev_info == "fail": - continue - dev_info["addr"] = dev.addr - dev_info["port"] = dev.port - resp.append(str(dev_info)) - - print(resp) - - if self.message_queues[s] is not None: - # '*' is used in web server to sperate the string - self.message_queues[s].put(bytes("*".join(resp), encoding = 'utf8')) - if s not in self.outputs: - self.outputs.append(s) - else: - client_addr = (data.decode().split(':')[1],int(data.decode().split(':')[2])) - - if client_addr in self.conn_dict.keys(): - print('start query device from (%s:%s)' % (client_addr[0], client_addr[1])) - resp = query(self.conn_dict[client_addr]) - print(resp) - - if self.message_queues[s] is not None: - self.message_queues[s].put(bytes(str(resp), encoding = 'utf8')) - if s not in self.outputs: - self.outputs.append(s) - else: # no connection - if self.message_queues[s] is not None: - self.message_queues[s].put(bytes(str("fail"), encoding = 'utf8')) - if s not in self.outputs: - self.outputs.append(s) - elif(data.decode().split(':')[0] == "install"): - client_addr = (data.decode().split(':')[1],int(data.decode().split(':')[2])) - app_name = data.decode().split(':')[3] - app_file = data.decode().split(':')[4] - - if client_addr in self.conn_dict.keys(): - print('start install application %s to ("%s":"%s")' % (app_name, client_addr[0], client_addr[1])) - res = install(self.conn_dict[client_addr], app_name, app_file) - if self.message_queues[s] is not None: - logging.info("response {} to cmd server".format(res)) - self.message_queues[s].put(bytes(res, encoding = 'utf8')) - if s not in self.outputs: - self.outputs.append(s) - elif(data.decode().split(':')[0] == "uninstall"): - client_addr = (data.decode().split(':')[1],int(data.decode().split(':')[2])) - app_name = data.decode().split(':')[3] - - if client_addr in self.conn_dict.keys(): - print("start uninstall") - res = uninstall(self.conn_dict[client_addr], app_name) - if self.message_queues[s] is not None: - logging.info("response {} to cmd server".format(res)) - self.message_queues[s].put(bytes(res, encoding = 'utf8')) - if s not in self.outputs: - self.outputs.append(s) - - - # if self.message_queues[s] is not None: - # self.message_queues[s].put(data) - # if s not in self.outputs: - # self.outputs.append(s) - else: - logging.warning(data) - - # Interpret empty result as closed connection - try: - for dev in self.devices: - if s == dev.conn: - self.devices.remove(dev) - # Stop listening for input on the connection - if s in self.outputs: - self.outputs.remove(s) - self.inputs.remove(s) - - # Remove message queue - if s in self.message_queues.keys(): - del self.message_queues[s] - s.close() - except OSError as e: - logging.error("OSError raised, unknown connection") - return "got it" - - def handler_send(self, writable): - # Handle outputs - for s in writable: - try: - message_queue = self.message_queues.get(s) - send_data = '' - if message_queue is not None: - send_data = message_queue.get_nowait() - except queue.Empty: - self.outputs.remove(s) - else: - # print "sending %s to %s " % (send_data, s.getpeername) - # print "send something" - if message_queue is not None: - s.send(send_data) - else: - print("client has closed") - # del message_queues[s] - # writable.remove(s) - # print "Client %s disconnected" % (client_address) - return "got it" - - def handler_exception(self, exceptional): - # # Handle "exceptional conditions" - for s in exceptional: - print('exception condition on', s.getpeername()) - # Stop listening for input on the connection - self.inputs.remove(s) - if s in self.outputs: - self.outputs.remove(s) - s.close() - - # Remove message queue - del self.message_queues[s] - return "got it" - - -def event_loop(tcpserver, inputs, outputs): - while inputs: - # Wait for at least one of the sockets to be ready for processing - print('waiting for the next event') - readable, writable, exceptional = select.select(inputs, outputs, inputs) - if readable is not None: - tcp_recever = tcpserver.handler_recever(readable) - if tcp_recever == 'got it': - print("server have received") - if writable is not None: - tcp_send = tcpserver.handler_send(writable) - if tcp_send == 'got it': - print("server have send") - if exceptional is not None: - tcp_exception = tcpserver.handler_exception(exceptional) - if tcp_exception == 'got it': - print("server have exception") - - - sleep(0.1) - -def run_wasm_server(): - server_address = ('localhost', 8888) - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) - inputs = [server] - outputs = [] - message_queues = {} - tcpserver = TCPServer(server, server_address, inputs, outputs, message_queues) - - task = threading.Thread(target=event_loop,args=(tcpserver,inputs,outputs)) - task.start() - -if __name__ == '__main__': - logging.basicConfig(level=logging.DEBUG, - filename='wasm_server.log', - filemode='a', - format= - '%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s' - ) - server_address = ('0.0.0.0', 8888) - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) - inputs = [server] - outputs = [] - message_queues = {} - tcpserver = TCPServer(server, server_address, inputs, outputs, message_queues) - logging.info("TCP Server start at {}:{}".format(server_address[0], "8888")) - - task = threading.Thread(target=event_loop,args=(tcpserver,inputs,outputs)) - task.start() - - # event_loop(tcpserver, inputs, outputs) \ No newline at end of file diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css b/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css deleted file mode 100644 index 220d4b6187..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css +++ /dev/null @@ -1,400 +0,0 @@ -/* Copyright (C) 2019 Intel Corporation. All rights reserved. -* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -*/ - -{% load static %} - diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js b/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js deleted file mode 100644 index 0510fb9014..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js +++ /dev/null @@ -1,217 +0,0 @@ -/* Copyright (C) 2019 Intel Corporation. All rights reserved. -* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -*/ - -/* - * Dom Location - * - */ - - function setDivCenter(divname) -// make qn element center aligned - { - var Top =($(window).height()-$(divname).height())/2; - var Left = ($(window).width()-$(divname).width())/2; - var scrollTop = $(document).scrollTop(); - var scrollLeft = $(document).scrollLeft(); - $(divname).css({posisiton:'absolute','top':Top+scrollTop,'left':Left+scrollLeft}); - -}; - -setDivCenter(".middlebox"); -setDivCenter(".deletebox"); - -function setmain(divname){ -// Set the pop-up window of apps for download at the right place - var x = $('#btn').offset().top; - var Top = x + $('#btn').height()+15; - var y = $('#btn').offset().left; - var Left = y + ($('#btn').width()/2)-($(divname).width()/2); - console.log(Top,Left) - $(divname).css({'top':Top,'left':Left}); -} -setmain(".main") - -/* - * download apps - * - */ - -function getthis(val) -//Telling background which app to be loaded from appstore_list and to be installed in the current device. -{ - - /* Get the ip adress and the port of a device, as well as the application ID to be downloaded on this device*/ - var ip,port,name,version; - var ipArr=$("#IPs").text().split(":"); - ip=ipArr[1]; - var portArr=$("#ports").text().split(":"); - port=portArr[1]; - name = $(val).parent().find("#appsinfo1").text().split(":")[1]; - version = $(val).parent().find("#appsinfo2").text().split(":")[1]; - $(".main").fadeOut(); - - for (num in alist){ - if (alist[num]['pname'].trim() == name.trim()) - {alert("This app has been downloaded."); - return;}}; - $("#loading").fadeIn(); - var sNode = document.getElementById("APPS"); - var tempNode= sNode.cloneNode(true); - sNode.parentNode.appendChild(tempNode); - $("#appinfo1").html("Product Name : "+ name); - $("#appinfo2").html("Status : "+"Installing"); - $("#appinfo3").html("Current_Version : "+ version); - - $.get("/appDownload/",{'ip':ip.trim(),'port':port.trim(),'name':name.trim(),},function (ret) { - var status = $.trim(ret.split(":")[1].split("}")[0]); - $(".loadapp").html(name+" is downloading now"); - var msg = JSON.parse(status) - console.log(msg) - if (JSON.parse(status)=="ok"){ - $(".middlebox").fadeIn(); - $(".sourceapp").fadeOut(); - $("#loading").fadeOut(); - $(".findapp").html("Download "+name +" successfully"); - $(".surebtn").click(function (){ - $(".middlebox").fadeOut(); - window.location.reload(); - })} - else if (JSON.parse(status)=="Fail!"){ - alert("Download failed!"); - $("#loading").fadeOut(); - sNode.remove(); - } - else { - alert("Install app failed:" + msg) - $("#loading").fadeOut(); - sNode.remove(); - } - }) -}; - -window.onload = function clone() -//Add & Delete apps to the device. -{ - /*Install Apps*/ - var sourceNode = document.getElementById("APPS"); - if (alist.length != 0) - { - $("#appinfo1").html("Product Name : "+ alist[0]['pname']); - $("#appinfo2").html("Status : "+ alist[0]['status']); - $("#appinfo3").html("Current_Version : "+ alist[0]['current_version']); - $("#delete").attr('class','delet0'); - $("#APPS").attr('class','app0'); - - for (var i=1; i=3){ - alert("Install app failed: exceed max app installations.") - } - $(".main").fadeOut(); - getthis(".mybtn2"); - var newurl = "?"+"ip="+ip+"&port="+port; - window.location.href= newurl; - }); - - } -} -givevalue(); - -function popbox(){ -/*Open and close the "install apps" window*/ - $(".btn").click(function(){ - $(".main").fadeIn(); - }); - $(".close").click(function(){ - $(".main").fadeOut(); - }); -}; -popbox(); diff --git a/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js b/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js deleted file mode 100644 index 71d029efa8..0000000000 --- a/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright (C) 2019 Intel Corporation. All rights reserved. -* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -*/ - -function setDivCenter(divname) -//Center a dom -{ - var Top =($(window).height()-$(divname).height())/2; - var Left = ($(window).width()-$(divname).width())/2; - var scrollTop = $(document).scrollTop(); - var scrollLeft = $(document).scrollLeft(); - $(divname).css({posisiton:'absolute','top':Top+scrollTop,'left':Left+scrollLeft}); - -}; -setDivCenter(".deletebox"); - -function setDivheight(divname) -//set the height of "appbook" to contain all its child elements. -{ - var leng = elist.length + flist.length; - var heig = 51 * leng; - $(divname).css({height:'heig'}); -}; -setDivheight(".appbook"); - -function setfooterposition(divname) -//Locate footer on the right place -{ - var Top = flist.length* $("#devices").height()+300; - var scrollTop = $(document).scrollTop(); - if (flist.length >=4){ - $(divname).css({posisiton:'absolute','top':Top+scrollTop}); - } -} -setfooterposition(".footer"); - -function deleteClick (obj) -//Remove an app from apppstore if clicks the "OK" button -{ - var indexapp = $(obj).attr('class').match(/\d+\b/); - var removeitem = $(".applic"+indexapp); - var name=removeitem.find('#appinfo1').text().split(":")[1].trim(); - var version=removeitem.find('#appinfo2').text().split(":")[1].trim(); - - if (flist.length >= 1){ - $(".deletebox").fadeIn(); - $(".findapp").html("Are you sure to delete "+name); - $(".suresbtn").click(function (){ - removeitem.remove(); - $.get("/removeapps/",{'name':name,'version':version},function (ret) { - console.log(ret);}); - $(".deletebox").fadeOut(); - window.location.href="/appstore/"; - }) - $(".delsbtn").click(function (){ - $(".deletebox").fadeOut(); })} -}; - -function upload_file() -//Make sure the uploading file is eligible -{ - var type = ulist[0]; - console.log(type); - if (type == "Not a wasm file"){ - alert(type); - window.location.href="/appstore/"; - } - if (type == "This App is preloaded"){ - alert(type); - window.location.href="/appstore/"; - } - if (type == "This App is already uploaded"){ - alert(type); - window.location.href="/appstore/"; - } -}; -upload_file(); - - -function clone() -//Render a interface that shows all the apps for installing in appstore, -//including preloaded ones and locally uploaded ones. -{ - - var sourceNode = document.getElementById("applications"); - $("#appinfo1").html("product name : "+ elist[0]['ID']); - $("#appinfo2").html("product Version : "+ elist[0]['Version']); - $("#delbutton").attr('class','del0'); - $("#applications").attr('class','applic0'); - - - for (var i=1; i=4){ - $(divname).css({posisiton:'absolute','top':Top+scrollTop}); - } -} -setfooterposition(".footer"); - -window.onload = function clone() -//Show the list of connected devices -{ - var sourceNode = document.getElementById("devices"); - $("#IPs").html("IP : "+ dlist[0]['IP']); - $("#ports").html("Port : "+ dlist[0]['Port']); - $("#installs").html("Installed Apps : "+ dlist[0]['apps']); - $("#devices").attr('class','devic0'); - $("#dbutton").attr('class','bt0'); - $("#choose").attr('class','chos0'); - - for (var i=1; i --wabt --wasm-file call_stack.txt + ``` + The script will use *wasm-objdump* in wabt to transform address, then use *llvm-dwarfdump* to lookup the line info for each address + in the call-stack dump. +- if addresses are not available in the stack trace (i.e. iwasm <= 1.3.2) or iwasm is used in fast interpreter mode, + run the following command to convert the function index into line info (passing the `--no-addr` option): + ``` + $ python3 addr2line.py --wasi-sdk --wabt --wasm-file call_stack.txt --no-addr + ``` + The script will use *wasm-objdump* in wabt to get the function names corresponding to function indexes, then use *llvm-dwarfdump* to lookup the line info for each + function index in the call-stack dump. +""" + + +def locate_sourceMappingURL_section(wasm_objdump: Path, wasm_file: Path) -> bool: + """ + Figure out if the wasm file has a sourceMappingURL section. + """ + cmd = f"{wasm_objdump} -h {wasm_file}" + p = subprocess.run( + shlex.split(cmd), + check=True, + capture_output=True, + text=True, + universal_newlines=True, + ) + outputs = p.stdout.split(os.linesep) + + for line in outputs: + line = line.strip() + if "sourceMappingURL" in line: + return True + + return False + + +def get_code_section_start(wasm_objdump: Path, wasm_file: Path) -> int: + """ + Find the start offset of Code section in a wasm file. + + if the code section header likes: + Code start=0x0000017c end=0x00004382 (size=0x00004206) count: 47 + + the start offset is 0x0000017c + """ + cmd = f"{wasm_objdump} -h {wasm_file}" + p = subprocess.run( + shlex.split(cmd), + check=True, + capture_output=True, + text=True, + universal_newlines=True, + ) + outputs = p.stdout.split(os.linesep) + + for line in outputs: + line = line.strip() + if "Code" in line: + return int(line.split()[1].split("=")[1], 16) + + return -1 + + +def get_line_info_from_function_addr_dwarf( + dwarf_dump: Path, wasm_file: Path, offset: int +) -> tuple[str, str, str, str]: + """ + Find the location info of a given offset in a wasm file. + """ + cmd = f"{dwarf_dump} --lookup={offset} {wasm_file}" + p = subprocess.run( + shlex.split(cmd), + check=False, + capture_output=True, + text=True, + universal_newlines=True, + ) + outputs = p.stdout.split(os.linesep) + + function_name, function_file = "", "unknown" + function_line, function_column = "?", "?" + + for line in outputs: + line = line.strip() + + if "DW_AT_name" in line: + function_name = get_dwarf_tag_value("DW_AT_name", line) + + if "DW_AT_decl_file" in line: + function_file = get_dwarf_tag_value("DW_AT_decl_file", line) + + if "Line info" in line: + _, function_line, function_column = parse_line_info(line) + + return (function_name, function_file, function_line, function_column) + + +def get_dwarf_tag_value(tag: str, line: str) -> str: + # Try extracting value as string + STR_PATTERN = rf"{tag}\s+\(\"(.*)\"\)" + m = re.match(STR_PATTERN, line) + if m: + return m.groups()[0] + + # Try extracting value as integer + INT_PATTERN = rf"{tag}\s+\((\d+)\)" + m = re.match(INT_PATTERN, line) + return m.groups()[0] + + +def get_line_info_from_function_name_dwarf( + dwarf_dump: Path, wasm_file: Path, function_name: str +) -> tuple[str, str, str]: + """ + Find the location info of a given function in a wasm file. + """ + cmd = f"{dwarf_dump} --name={function_name} {wasm_file}" + p = subprocess.run( + shlex.split(cmd), + check=False, + capture_output=True, + text=True, + universal_newlines=True, + ) + outputs = p.stdout.split(os.linesep) + + function_name, function_file = "", "unknown" + function_line = "?" + + for line in outputs: + line = line.strip() + + if "DW_AT_name" in line: + function_name = get_dwarf_tag_value("DW_AT_name", line) + + if "DW_AT_decl_file" in line: + function_file = get_dwarf_tag_value("DW_AT_decl_file", line) + + if "DW_AT_decl_line" in line: + function_line = get_dwarf_tag_value("DW_AT_decl_line", line) + + return (function_name, function_file, function_line) + + +def get_line_info_from_function_addr_sourcemapping( + emsymbolizer: Path, wasm_file: Path, offset: int +) -> tuple[str, str, str, str]: + """ + Find the location info of a given offset in a wasm file which is compiled with emcc. + + {emsymbolizer} {wasm_file} {offset of file} + + there usually are two lines: + ?? + relative path to source file:line:column + """ + debug_info_source = wasm_file.with_name(f"{wasm_file.name}.map") + cmd = f"{emsymbolizer} -t code -f {debug_info_source} {wasm_file} {offset}" + p = subprocess.run( + shlex.split(cmd), + check=False, + capture_output=True, + text=True, + universal_newlines=True, + cwd=Path.cwd(), + ) + outputs = p.stdout.split(os.linesep) + + function_name, function_file = "", "unknown" + function_line, function_column = "?", "?" + + for line in outputs: + line = line.strip() + + if not line: + continue + + m = re.match(r"(.*):(\d+):(\d+)", line) + if m: + function_file, function_line, function_column = m.groups() + continue + else: + # it's always ??, not sure about that + if "??" != line: + function_name = line + + return (function_name, function_file, function_line, function_column) + + +def parse_line_info(line_info: str) -> tuple[str, str, str]: + """ + line_info -> [file, line, column] + """ + PATTERN = r"Line info: file \'(.+)\', line ([0-9]+), column ([0-9]+)" + m = re.search(PATTERN, line_info) + assert m is not None + + file, line, column = m.groups() + return (file, int(line), int(column)) + + +def parse_call_stack_line(line: str) -> tuple[str, str, str]: + """ + New format (WAMR > 1.3.2): + #00: 0x0a04 - $f18 => (00, 0x0a04, $f18) + Old format: + #00 $f18 => (00, _, $f18) + Text format (-DWAMR_BUILD_LOAD_CUSTOM_SECTION=1 -DWAMR_BUILD_CUSTOM_NAME_SECTION=1): + #02: 0x0200 - a => (02, 0x0200, a) + _start (always): + #05: 0x011f - _start => (05, 0x011f, _start) + """ + + # New format and Text format and _start + PATTERN = r"#([0-9]+): 0x([0-9a-f]+) - (\S+)" + m = re.match(PATTERN, line) + if m is not None: + return m.groups() + + # Old format + PATTERN = r"#([0-9]+) (\S+)" + m = re.match(PATTERN, line) + if m is not None: + return (m.groups()[0], None, m.groups()[1]) + + return None + + +def parse_module_functions(wasm_objdump: Path, wasm_file: Path) -> dict[str, str]: + function_index_to_name = {} + + cmd = f"{wasm_objdump} -x {wasm_file} --section=function" + p = subprocess.run( + shlex.split(cmd), + check=True, + capture_output=True, + text=True, + universal_newlines=True, + ) + outputs = p.stdout.split(os.linesep) + + for line in outputs: + if not f"func[" in line: + continue + + PATTERN = r".*func\[([0-9]+)\].*<(.*)>" + m = re.match(PATTERN, line) + assert m is not None + + index = m.groups()[0] + name = m.groups()[1] + function_index_to_name[index] = name + + return function_index_to_name + + +def demangle(cxxfilt: Path, function_name: str) -> str: + cmd = f"{cxxfilt} -n {function_name}" + p = subprocess.run( + shlex.split(cmd), + check=True, + capture_output=True, + text=True, + universal_newlines=True, + ) + return p.stdout.strip() + + +def main(): + parser = argparse.ArgumentParser(description="addr2line for wasm") + parser.add_argument("--wasi-sdk", type=Path, help="path to wasi-sdk") + parser.add_argument("--wabt", type=Path, help="path to wabt") + parser.add_argument("--wasm-file", type=Path, help="path to wasm file") + parser.add_argument("call_stack_file", type=Path, help="path to a call stack file") + parser.add_argument( + "--no-addr", + action="store_true", + help="use call stack without addresses or from fast interpreter mode", + ) + parser.add_argument("--emsdk", type=Path, help="path to emsdk") + args = parser.parse_args() + + wasm_objdump = args.wabt.joinpath("bin/wasm-objdump") + assert wasm_objdump.exists() + + llvm_dwarf_dump = args.wasi_sdk.joinpath("bin/llvm-dwarfdump") + assert llvm_dwarf_dump.exists() + + llvm_cxxfilt = args.wasi_sdk.joinpath("bin/llvm-cxxfilt") + assert llvm_cxxfilt.exists() + + emcc_production = locate_sourceMappingURL_section(wasm_objdump, args.wasm_file) + if emcc_production: + if args.emsdk is None: + print("Please provide the path to emsdk via --emsdk") + return -1 + + emsymbolizer = args.emsdk.joinpath("upstream/emscripten/emsymbolizer") + assert emsymbolizer.exists() + + code_section_start = get_code_section_start(wasm_objdump, args.wasm_file) + if code_section_start == -1: + return -1 + + function_index_to_name = parse_module_functions(wasm_objdump, args.wasm_file) + + assert args.call_stack_file.exists() + with open(args.call_stack_file, "rt", encoding="ascii") as f: + for i, line in enumerate(f): + line = line.strip() + if not line: + continue + + splitted = parse_call_stack_line(line) + if splitted is None: + print(f"{line}") + continue + + _, offset, index = splitted + if args.no_addr: + # FIXME: w/ emcc production + if not index.startswith("$f"): # E.g. _start or Text format + print(f"{i}: {index}") + continue + index = index[2:] + + if index not in function_index_to_name: + print(f"{i}: {line}") + continue + + if not emcc_production: + _, function_file, function_line = ( + get_line_info_from_function_name_dwarf( + llvm_dwarf_dump, + args.wasm_file, + function_index_to_name[index], + ) + ) + else: + _, function_file, function_line = _, "unknown", "?" + + function_name = demangle(llvm_cxxfilt, function_index_to_name[index]) + print(f"{i}: {function_name}") + print(f"\tat {function_file}:{function_line}") + else: + offset = int(offset, 16) + # match the algorithm in wasm_interp_create_call_stack() + # either a *offset* to *code* section start + # or a *offset* in a file + assert offset > code_section_start + offset = offset - code_section_start + + if emcc_production: + function_name, function_file, function_line, function_column = ( + get_line_info_from_function_addr_sourcemapping( + emsymbolizer, args.wasm_file, offset + ) + ) + else: + function_name, function_file, function_line, function_column = ( + get_line_info_from_function_addr_dwarf( + llvm_dwarf_dump, args.wasm_file, offset + ) + ) + + # if can't parse function_name, use name section or + if function_name == "": + if index.startswith("$f"): + function_name = function_index_to_name.get(index[2:], index) + else: + function_name = index + + function_name = demangle(llvm_cxxfilt, function_name) + + print(f"{i}: {function_name}") + print(f"\tat {function_file}:{function_line}:{function_column}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/test-tools/aot-analyzer/CMakeLists.txt b/test-tools/aot-analyzer/CMakeLists.txt new file mode 100644 index 0000000000..9c317a6e6b --- /dev/null +++ b/test-tools/aot-analyzer/CMakeLists.txt @@ -0,0 +1,77 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) + +project (aot-analyzer) + +set (CMAKE_CXX_STANDARD 17) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (WAMR_STRINGREF_IMPL_SOURCE "STUB") + +# 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_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MEMORY_PROFILING) + # Enable reference types by default + set (WAMR_BUILD_MEMORY_PROFILING 1) +endif () + +if (NOT DEFINED WAMR_BIG_ENDIAN) + set (WAMR_BIG_ENDIAN 0) +endif () + +# 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}) + +################ application related ################ +include_directories(./include) +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (aot-analyzer src/main.cc src/aot_file.cc src/binary_file.cc src/option_parser.cc src/wasm_file.cc ${UNCOMMON_SHARED_SOURCE}) + +target_link_libraries (aot-analyzer vmlib -lm -ldl -lpthread) diff --git a/test-tools/aot-analyzer/README.md b/test-tools/aot-analyzer/README.md new file mode 100644 index 0000000000..1ae3ca7b5b --- /dev/null +++ b/test-tools/aot-analyzer/README.md @@ -0,0 +1,55 @@ +# AoT-Analyzer: The AoT Binary analysis tool + + +## Cloning + +Clone as normal: + +```console +$ git clone +$ cd aot-analyzer +``` + +## Building using CMake directly + +You'll need [CMake](https://cmake.org). You can then run CMake, the normal way: + +```console +$ mkdir build +$ cd build +$ cmake .. +$ cmake --build . +``` + +To analyze AoT files with GC feature enabled, you need to enable GC feature when compiling this tool: + +```console +$ mkdir build +$ cd build +$ cmake -DWAMR_BUILD_GC=1 .. +$ cmake --build . +``` + +## Running aot-analyzer + +Some examples: + +```sh +# parse example.aot, and print basic information about AoT file +$ ./aot-analyzer -i example.aot + +# parse example.aot, and print the size of text section of the AoT file +$ ./aot-analyzer -t example.aot + +# compare these two files, and show the difference in function size between them +$ ./aot-analyzer -c example.aot example.wasm +``` + +**NOTE**: Using `-c` for file comparison, must ensure that the AoT file is generated based on this Wasm file. + + +You can use `--help` to get additional help: + +```console +$ ./aot-analyzer --help +``` \ No newline at end of file diff --git a/test-tools/aot-analyzer/include/analyzer_error.h b/test-tools/aot-analyzer/include/analyzer_error.h new file mode 100644 index 0000000000..62126fdf7b --- /dev/null +++ b/test-tools/aot-analyzer/include/analyzer_error.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef ANALYZER_ERROR_H_ +#define ANALYZER_ERROR_H_ + +#include +#include +#include + +#include "config.h" + +namespace analyzer { + +enum class ErrorLevel { + Warning, + Error, +}; + +static inline const char * +GetErrorLevelName(ErrorLevel error_level) +{ + switch (error_level) { + case ErrorLevel::Warning: + return "warning"; + case ErrorLevel::Error: + return "error"; + } + ANALYZER_UNREACHABLE; +} + +class Error +{ + public: + Error() + : error_level_(ErrorLevel::Error) + {} + Error(ErrorLevel error_level, std::string_view message) + : error_level_(error_level) + , message_(message) + {} + + ErrorLevel error_level_; + std::string message_; +}; + +using Errors = std::vector; + +} // namespace analyzer +#endif \ No newline at end of file diff --git a/test-tools/aot-analyzer/include/aot_file.h b/test-tools/aot-analyzer/include/aot_file.h new file mode 100644 index 0000000000..fdb9c87f13 --- /dev/null +++ b/test-tools/aot-analyzer/include/aot_file.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef AOT_FILE_H_ +#define AOT_FILE_H_ + +#include "binary_file.h" + +namespace analyzer { + +class AoTFile : public BinaryFile +{ + public: + AoTFile(const char *file_name); + + Result Scan(); + + Result ParseTargetInfo(); + AOTTargetInfo GetTargetInfo(); + + std::string GetBinTypeName(uint16_t bin_type); + std::string GetExectuionTypeName(uint16_t e_type); + std::string GetExectuionMachineName(uint16_t e_machine); + + private: + AOTTargetInfo target_info_; +}; + +} // namespace analyzer +#endif diff --git a/test-tools/aot-analyzer/include/binary_file.h b/test-tools/aot-analyzer/include/binary_file.h new file mode 100644 index 0000000000..d0cf00347a --- /dev/null +++ b/test-tools/aot-analyzer/include/binary_file.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef BINARY_FILE_H_ +#define BINARY_FILE_H_ + +#include "aot_runtime.h" +#include "bh_read_file.h" +#include "common.h" +#include "config.h" +#include "wasm_export.h" + +namespace analyzer { + +class BinaryFile +{ + public: + BinaryFile(const char *file_name); + ~BinaryFile(); + + Result ReadModule(); + + virtual Result Scan(); + + void ANALYZER_PRINTF_FORMAT(2, 3) PrintError(const char *format, ...); + Result UpdateCurrentPos(uint32_t steps); + + const char *GetFileName() { return file_name_; } + uint8_t *GetFileData() { return file_data_; } + uint32_t GetFileSize() { return file_size_; } + size_t GetCurrentPos() { return current_pos_; } + wasm_module_t GetModule() { return module_; } + WASMModuleMemConsumption GetMemConsumption() { return mem_conspn_; } + + private: + const char *file_name_; + uint8_t *file_data_; + uint32_t file_size_; + size_t current_pos_; + wasm_module_t module_; + WASMModuleMemConsumption mem_conspn_; +}; + +template +Result +ReadT(T *out_value, BinaryFile *file, const char *type_name) +{ + if (file == NULL + || file->GetCurrentPos() + sizeof(T) > file->GetFileSize()) { + return Result::Error; + } +#if WAMR_BIG_ENDIAN + uint8_t tmp[sizeof(T)]; + memcpy(tmp, file->GetFileData() + file->GetCurrentPos(), sizeof(tmp)); + SwapBytesSized(tmp, sizeof(tmp)); + memcpy(out_value, tmp, sizeof(T)); +#else + memcpy(out_value, file->GetFileData() + file->GetCurrentPos(), sizeof(T)); +#endif + file->UpdateCurrentPos(sizeof(T)); + return Result::Ok; +} + +} // namespace analyzer +#endif diff --git a/test-tools/aot-analyzer/include/common.h b/test-tools/aot-analyzer/include/common.h new file mode 100644 index 0000000000..c4d25bc766 --- /dev/null +++ b/test-tools/aot-analyzer/include/common.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef COMMON_H_ +#define COMMON_H_ + +#include "string_format.h" + +#define ANALYZER_FATAL(...) fprintf(stderr, __VA_ARGS__), exit(1) + +#if WITH_EXCEPTIONS +#define ANALYZER_TRY try { +#define ANALYZER_CATCH_BAD_ALLOC \ + } \ + catch (std::bad_alloc &) {} +#define ANALYZER_CATCH_BAD_ALLOC_AND_EXIT \ + } \ + catch (std::bad_alloc &) { ANALYZER_FATAL("Memory allocation failure.\n"); } +#else +#define ANALYZER_TRY +#define ANALYZER_CATCH_BAD_ALLOC +#define ANALYZER_CATCH_BAD_ALLOC_AND_EXIT +#endif + +namespace analyzer { + +struct ObjdumpOptions { + bool info; + bool text_size; + bool details; + bool compare; + const char *file_name; +}; + +struct Result { + enum Enum { + Ok, + Error, + }; + + Result() + : Result(Ok) + {} + Result(Enum e) + : enum_(e) + {} + operator Enum() const { return enum_; } + Result &operator|=(Result rhs); + + private: + Enum enum_; +}; + +inline Result +operator|(Result lhs, Result rhs) +{ + return (lhs == Result::Error || rhs == Result::Error) ? Result::Error + : Result::Ok; +} + +inline Result & +Result::operator|=(Result rhs) +{ + enum_ = *this | rhs; + return *this; +} + +inline bool +Succeeded(Result result) +{ + return result == Result::Ok; +} + +inline bool +Failed(Result result) +{ + return result == Result::Error; +} + +#define CHECK_RESULT(expr) \ + do { \ + if (Failed(expr)) { \ + return Result::Error; \ + } \ + } while (0) + +#define ERROR_IF(expr, ...) \ + do { \ + if (expr) { \ + PrintError(__VA_ARGS__); \ + return Result::Error; \ + } \ + } while (0) + +#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__) + +} // namespace analyzer +#endif diff --git a/test-tools/aot-analyzer/include/config.h b/test-tools/aot-analyzer/include/config.h new file mode 100644 index 0000000000..9f7fa49eaa --- /dev/null +++ b/test-tools/aot-analyzer/include/config.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#include +#include + +#define ANALYZER_VERSION_STRING "1.0.0" + +#define WASM_MAGIC_NUMBER 0x6d736100 +#define WASM_CURRENT_VERSION 1 + +#define AOT_MAGIC_NUMBER 0x746f6100 +#define AOT_CURRENT_VERSION 5 + +/* Legal values for bin_type */ +#define BIN_TYPE_ELF32L 0 /* 32-bit little endian */ +#define BIN_TYPE_ELF32B 1 /* 32-bit big endian */ +#define BIN_TYPE_ELF64L 2 /* 64-bit little endian */ +#define BIN_TYPE_ELF64B 3 /* 64-bit big endian */ +#define BIN_TYPE_COFF32 4 /* 32-bit little endian */ +#define BIN_TYPE_COFF64 6 /* 64-bit little endian */ + +/* Legal values for e_type (object file type). */ +#define E_TYPE_NONE 0 /* No file type */ +#define E_TYPE_REL 1 /* Relocatable file */ +#define E_TYPE_EXEC 2 /* Executable file */ +#define E_TYPE_DYN 3 /* Shared object file */ +#define E_TYPE_XIP 4 /* eXecute In Place file */ + +/* Legal values for e_machine (architecture). */ +#define E_MACHINE_386 3 /* Intel 80386 */ +#define E_MACHINE_MIPS 8 /* MIPS R3000 big-endian */ +#define E_MACHINE_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ +#define E_MACHINE_ARM 40 /* ARM/Thumb */ +#define E_MACHINE_AARCH64 183 /* AArch64 */ +#define E_MACHINE_ARC 45 /* Argonaut RISC Core */ +#define E_MACHINE_IA_64 50 /* Intel Merced */ +#define E_MACHINE_MIPS_X 51 /* Stanford MIPS-X */ +#define E_MACHINE_X86_64 62 /* AMD x86-64 architecture */ +#define E_MACHINE_ARC_COMPACT 93 /* ARC International ARCompact */ +#define E_MACHINE_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */ +#define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define E_MACHINE_RISCV 243 /* RISC-V 32/64 */ +#define E_MACHINE_WIN_I386 0x14c /* Windows i386 architecture */ +#define E_MACHINE_WIN_X86_64 0x8664 /* Windows x86-64 architecture */ + +/* Whether is available */ +#define HAVE_ALLOCA_H 1 + +/* Whether snprintf is defined by stdio.h */ +#define HAVE_SNPRINTF 1 + +/* Whether ssize_t is defined by stddef.h */ +#define HAVE_SSIZE_T 1 + +/* Whether strcasecmp is defined by strings.h */ +#define HAVE_STRCASECMP 1 + +#define COMPILER_IS_CLANG 0 +#define COMPILER_IS_GNU 1 +#define COMPILER_IS_MSVC 0 + +#define WITH_EXCEPTIONS 0 + +#define SIZEOF_SIZE_T 8 + +#if HAVE_ALLOCA_H +#include +#elif COMPILER_IS_MSVC +#include +#define alloca _alloca +#elif defined(__MINGW32__) +#include +#endif + +#if COMPILER_IS_CLANG || COMPILER_IS_GNU + +#if __MINGW32__ +#define ANALYZER_PRINTF_FORMAT(format_arg, first_arg) \ + __attribute__((format(gnu_printf, (format_arg), (first_arg)))) +#else +#define ANALYZER_PRINTF_FORMAT(format_arg, first_arg) \ + __attribute__((format(printf, (format_arg), (first_arg)))) +#endif + +#ifdef __cplusplus +#define ANALYZER_STATIC_ASSERT(x) static_assert((x), #x) +#else +#define ANALYZER_STATIC_ASSERT(x) _Static_assert((x), #x) +#endif + +#elif COMPILER_IS_MSVC + +#include +#include + +#define ANALYZER_STATIC_ASSERT(x) _STATIC_ASSERT(x) +#define ANALYZER_PRINTF_FORMAT(format_arg, first_arg) + +#else + +#error unknown compiler + +#endif + +#define ANALYZER_UNREACHABLE abort() + +#ifdef __cplusplus + +#if COMPILER_IS_MSVC + +#elif COMPILER_IS_CLANG || COMPILER_IS_GNU + +/* print format specifier for size_t */ +#define PRIzd "zd" +#define PRIzx "zx" + +#else + +#error unknown compiler + +#endif + +#if HAVE_SNPRINTF +#define analyzer_snprintf snprintf +#elif COMPILER_IS_MSVC +#include +int +analyzer_snprintf(char *str, size_t size, const char *format, ...); +#else +#error no snprintf +#endif + +#if COMPILER_IS_MSVC +int +analyzer_vsnprintf(char *str, size_t size, const char *format, va_list ap); +#else +#define analyzer_vsnprintf vsnprintf +#endif + +#if !HAVE_SSIZE_T +#if COMPILER_IS_MSVC +#if defined(_WIN64) +typedef signed __int64 ssize_t; +#else +typedef signed int ssize_t; +#endif +#else +typedef long ssize_t; +#endif +#endif + +#if !HAVE_STRCASECMP +#if COMPILER_IS_MSVC +#define strcasecmp _stricmp +#else +#error no strcasecmp +#endif +#endif + +#endif + +#endif diff --git a/test-tools/aot-analyzer/include/option_parser.h b/test-tools/aot-analyzer/include/option_parser.h new file mode 100644 index 0000000000..a3e0f7d557 --- /dev/null +++ b/test-tools/aot-analyzer/include/option_parser.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2024 Xiaomi Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef OPTION_PARSER_H_ +#define OPTION_PARSER_H_ + +#include +#include +#include + +#include "config.h" + +namespace analyzer { + +class OptionParser +{ + public: + enum class HasArgument { No, Yes }; + enum class ArgumentCount { One, OneOrMore, ZeroOrMore }; + + struct Option; + using Callback = std::function; + using NullCallback = std::function; + + struct Option { + Option(char short_name, const std::string &long_name, + const std::string &metavar, HasArgument has_argument, + const std::string &help, const Callback &); + + char short_name; + std::string long_name; + std::string metavar; + bool has_argument; + std::string help; + Callback callback; + }; + + struct Argument { + Argument(const std::string &name, ArgumentCount, const Callback &); + + std::string name; + ArgumentCount count; + Callback callback; + int handled_count = 0; + }; + + explicit OptionParser(const char *program_name, const char *description); + + void AddOption(const Option &); + void AddOption(char short_name, const char *long_name, const char *help, + const NullCallback &); + void AddOption(const char *long_name, const char *help, + const NullCallback &); + void AddOption(char short_name, const char *long_name, const char *metavar, + const char *help, const Callback &); + + void AddArgument(const std::string &name, ArgumentCount, const Callback &); + void SetErrorCallback(const Callback &); + void Parse(int argc, char *argv[]); + void PrintHelp(); + + private: + static int Match(const char *s, const std::string &full, bool has_argument); + void ANALYZER_PRINTF_FORMAT(2, 3) Errorf(const char *format, ...); + void HandleArgument(size_t *arg_index, const char *arg_value); + void DefaultError(const std::string &); + + std::string program_name_; + std::string description_; + std::vector